[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.yml",
    "content": "name: 🐞 问题反馈 / Bug Report\ndescription: 反馈你遇到的问题 / Report an issue\ntitle: \"[Bug]: \"\nlabels: [\"bug\"]\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        感谢你提交问题！在提交之前，请确保已搜索过 [现有 Issue](https://github.com/appshubcc/Bettbox/issues)。\n        Thanks for reporting! Please search [existing issues](https://github.com/appshubcc/Bettbox/issues) before submitting.\n\n  - type: textarea\n    id: bug-description\n    attributes:\n      label: 问题描述 / Description\n      placeholder: 请清晰地描述你遇到的问题，必要时附上截图。 / Describe the issue in detail, with screenshots if possible.\n    validations:\n      required: true\n\n  - type: input\n    id: version\n    attributes:\n      label: 软件版本 / Version\n      placeholder: 例如 1.15.6\n    validations:\n      required: true\n\n  - type: dropdown\n    id: os\n    attributes:\n      label: 操作系统 / OS\n      options:\n        - Android\n        - Windows\n        - macOS\n        - Linux\n    validations:\n      required: true\n\n  - type: textarea\n    id: steps\n    attributes:\n      label: 复现步骤 / Reproduction Steps\n      placeholder: |\n        1. 打开...\n        2. 点击...\n        3. 出现...\n    validations:\n      required: false\n\n  - type: textarea\n    id: logs\n    attributes:\n      label: 相关日志 / Logs\n      description: 请在设置中开启 Debug 等级日志，并在此粘贴相关内容。尽量不要上传庞大的日志文件。 / Paste relevant debug logs here.\n      render: shell\n    validations:\n      required: true"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/config.yml",
    "content": "contact_links:\n  - name: 💬 交流与讨论 / Channel & Chat\n    url: https://t.me/appshub_chat\n    about: 访问我们的 Telegram 讨论组 / Join the Telegram group"
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.yml",
    "content": "name: ✨ 功能请求 / Feature Request\ndescription: 提交一个新的功能建议 / Propose a new feature\ntitle: \"[Feature]: \"\nlabels: [\"enhancement\"]\nbody:\n  - type: markdown\n    attributes:\n      value: |\n        感谢你为提高项目质量做出贡献！在提交建议前，请检查 [已有 Issue](https://github.com/appshubcc/Bettbox/issues)。\n        Thanks for your contribution! Please check [existing issues](https://github.com/appshubcc/Bettbox/issues) before proposing.\n\n  - type: textarea\n    id: feature-description\n    attributes:\n      label: 功能描述 / Description\n      placeholder: 请清晰简洁地描述该功能。 / A concise description of the feature request.\n    validations:\n      required: true\n\n  - type: textarea\n    id: use-case\n    attributes:\n      label: 使用场景 / Use Case\n      placeholder: 描述该功能的使用场景及它可以解决的问题。 / Describe the usage scenario and what problem it solves.\n    validations:\n      required: false\n\n  - type: checkboxes\n    id: os-labels\n    attributes:\n      label: 适用系统 / Target OS\n      options:\n        - label: Android\n        - label: Windows\n        - label: macOS\n        - label: Linux\n    validations:\n      required: true"
  },
  {
    "path": ".github/release_template.md",
    "content": "# Bettbox 正式版本发布\n<div align=\"left\">\n\n### ✈️ Telegram 社区交流\n\n</div>\n\n<div align=\"left\">\n\n[![Telegram Group](https://img.shields.io/badge/Appshub-Chat-2CA5E0?style=for-the-badge&logo=telegram&logoColor=white)](https://t.me/appshub_chat) [![Telegram Channel](https://img.shields.io/badge/Appshub-Channel-2CA5E0?style=for-the-badge&logo=telegram&logoColor=white)](https://t.me/appshub_channel)\n\n---\n</div>\n\n\n### ⬇️ Download / 下载链接\n\n**Note: For desktop CPUs from 2012 or earlier, please download the Compatible version**\n<br>**注意：桌面端2012年同期及之前的CPU，需要使用Compatible兼容版本**\n\n---\n\n<div align=\"left\">\n\n| **OS/ 系统** | **Requirements / 版本要求** | **Direct Links / 点击直链下载** |\n|:---:|:---|:---|\n| <img src=\"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/android/android-original.svg\" alt=\"Android\" width=\"28\"/> | Android 8.0+<br>*(Compatible with Android TV)* |<a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-VERSION-android-arm64-v8a.apk\"><img src=\"https://img.shields.io/badge/APK-ARMv8-32AF6A?logo=android&logoColor=white&style=flat-square&labelColor=222222\"></a> <a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-VERSION-android-x86_64.apk\"><img src=\"https://img.shields.io/badge/APK-x64-32AF6A?logo=android&logoColor=white&style=flat-square&labelColor=222222\"></a><br><a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-VERSION-android-armeabi-v7a.apk\"><img src=\"https://img.shields.io/badge/APK-ARMv7-32AF6A?logo=android&logoColor=white&style=flat-square&labelColor=222222\"></a> <a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-VERSION-android-universal.apk\"><img src=\"https://img.shields.io/badge/APK-Universal-32AF6A?logo=android&logoColor=white&style=flat-square&labelColor=222222\"></a> |\n| <img src=\"https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/windows11/windows11-original.svg\" alt=\"Windows\" width=\"28\"/> | Windows 10+<br>*(Compatible for Older CPU)* | <a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-VERSION-windows-amd64-setup.exe\"><img src=\"https://img.shields.io/badge/Setup-x64-0078D7?logo=windows&logoColor=white&style=flat-square&labelColor=222222\"></a> <a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-VERSION-windows-arm64-setup.exe\"><img src=\"https://img.shields.io/badge/Setup-ARM64-0078D7?logo=windows&logoColor=white&style=flat-square&labelColor=222222\"></a><br><a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-VERSION-windows-amd64-compatible-setup.exe\"><img src=\"https://img.shields.io/badge/Setup-Compatible%20x64-555555?logo=windows&logoColor=white&style=flat-square&labelColor=222222\"></a> |\n| <img src=\"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/apple/apple-original.svg\" alt=\"macOS\" width=\"28\"/> | macOS 12.0+<br>*(Compatible for 10.15-11.7)* | <a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-VERSION-macos-arm64.dmg\"><img src=\"https://img.shields.io/badge/DMG-Apple%20Silicon-000000?logo=apple&logoColor=white&style=flat-square&labelColor=222222\"></a> <a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-VERSION-macos-amd64.dmg\"><img src=\"https://img.shields.io/badge/DMG-Intel%20x64-00A9E0?logo=apple&logoColor=white&style=flat-square&labelColor=222222\"></a><br><a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-VERSION-macos-amd64-compatible.dmg\"><img src=\"https://img.shields.io/badge/DMG-Compatible%20Intel-555555?logo=apple&logoColor=white&style=flat-square&labelColor=222222\"></a> |\n| <img src=\"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/linux/linux-original.svg\" alt=\"Linux\" width=\"28\"/> | Linux Kernel 5.4+<br>*(Compatible for Older CPU)* | <a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-VERSION-linux-amd64.AppImage\"><img src=\"https://img.shields.io/badge/AppImage-x64-f84e29?logo=linux&logoColor=white&style=flat-square&labelColor=222222\"></a> <a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-VERSION-linux-amd64.deb\"><img src=\"https://img.shields.io/badge/DEB-x64-A81D33?logo=debian&logoColor=white&style=flat-square&labelColor=222222\"></a><br><a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-VERSION-linux-arm64.deb\"><img src=\"https://img.shields.io/badge/DEB-ARM64-A81D33?logo=debian&logoColor=white&style=flat-square&labelColor=222222\"></a> <a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-VERSION-linux-amd64.rpm\"><img src=\"https://img.shields.io/badge/RPM-x64-CC0000?logo=redhat&logoColor=white&style=flat-square&labelColor=222222\"></a><br><a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-VERSION-linux-amd64-compatible.deb\"><img src=\"https://img.shields.io/badge/DEB-Compatible%20x64-555555?logo=debian&logoColor=white&style=flat-square&labelColor=222222\"></a> |\n---\n</div>\n\n### 🐛 Feedback / 问题反馈\n\n> **Note / 提示：**\n> Detailed and well-structured issues will be prioritized(logs / reproduction steps)<br>\n> 书写认真、信息完整（包含必要的复现步骤和日志）的 issues 会被优先处理和对待\n\n**Bug Report / 提交故障**: [Click Here / 点击这里](https://github.com/appshubcc/Bettbox/issues/new?template=bug_report.yml)\n**Feature Request / 需求建议**: [Click Here / 点击这里](https://github.com/appshubcc/Bettbox/issues/new?template=feature_request.yml)\n\n<br>\n"
  },
  {
    "path": ".github/release_template_pre.md",
    "content": "# Bettbox 预览版本发布\n**注意：预览版本通常包含最新的功能以及不稳定性，如遇问题请提交issue[[反馈]](https://github.com/appshubcc/Bettbox/issues/new?template=bug_report.yml)联系社区交流。\n<br>当前版本最新更新日志以及变更，请参考最新提交[[Commits]](https://github.com/appshubcc/Bettbox/commits/vVERSION/)**\n\n<div align=\"left\">\n\n### ✈️ Telegram 社区交流\n\n</div>\n\n<div align=\"left\">\n\n[![Telegram Group](https://img.shields.io/badge/Appshub-Chat-2CA5E0?style=for-the-badge&logo=telegram&logoColor=white)](https://t.me/appshub_chat) [![Telegram Channel](https://img.shields.io/badge/Appshub-Channel-2CA5E0?style=for-the-badge&logo=telegram&logoColor=white)](https://t.me/appshub_channel)\n\n---\n</div>\n\n\n### ⬇️ Download / 下载链接\n\n**Note: For desktop CPUs from 2012 or earlier, please download the Compatible version**\n<br>**注意：桌面端2012年同期及之前的CPU，需要使用Compatible兼容版本**\n\n---\n\n<div align=\"left\">\n\n| **OS/ 系统** | **Requirements / 版本要求** | **Direct Links / 点击直链下载** |\n|:---:|:---|:---|\n| <img src=\"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/android/android-original.svg\" alt=\"Android\" width=\"28\"/> | Android 8.0+<br>*(Compatible with Android TV)* |<a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-BASE_VERSION-android-arm64-v8a.apk\"><img src=\"https://img.shields.io/badge/APK-ARMv8-32AF6A?logo=android&logoColor=white&style=flat-square&labelColor=222222\"></a> <a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-BASE_VERSION-android-x86_64.apk\"><img src=\"https://img.shields.io/badge/APK-x64-32AF6A?logo=android&logoColor=white&style=flat-square&labelColor=222222\"></a><br><a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-BASE_VERSION-android-armeabi-v7a.apk\"><img src=\"https://img.shields.io/badge/APK-ARMv7-32AF6A?logo=android&logoColor=white&style=flat-square&labelColor=222222\"></a> <a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-BASE_VERSION-android-universal.apk\"><img src=\"https://img.shields.io/badge/APK-Universal-32AF6A?logo=android&logoColor=white&style=flat-square&labelColor=222222\"></a> |\n| <img src=\"https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/windows11/windows11-original.svg\" alt=\"Windows\" width=\"28\"/> | Windows 10+<br>*(Compatible for Older CPU)* | <a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-BASE_VERSION-windows-amd64-setup.exe\"><img src=\"https://img.shields.io/badge/Setup-x64-0078D7?logo=windows&logoColor=white&style=flat-square&labelColor=222222\"></a> <a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-BASE_VERSION-windows-arm64-setup.exe\"><img src=\"https://img.shields.io/badge/Setup-ARM64-0078D7?logo=windows&logoColor=white&style=flat-square&labelColor=222222\"></a><br><a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-BASE_VERSION-windows-amd64-compatible-setup.exe\"><img src=\"https://img.shields.io/badge/Setup-Compatible%20x64-555555?logo=windows&logoColor=white&style=flat-square&labelColor=222222\"></a> |\n| <img src=\"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/apple/apple-original.svg\" alt=\"macOS\" width=\"28\"/> | macOS 12.0+<br>*(Compatible for 10.15-11.7)* | <a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-BASE_VERSION-macos-arm64.dmg\"><img src=\"https://img.shields.io/badge/DMG-Apple%20Silicon-000000?logo=apple&logoColor=white&style=flat-square&labelColor=222222\"></a> <a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-BASE_VERSION-macos-amd64.dmg\"><img src=\"https://img.shields.io/badge/DMG-Intel%20x64-00A9E0?logo=apple&logoColor=white&style=flat-square&labelColor=222222\"></a><br><a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-BASE_VERSION-macos-amd64-compatible.dmg\"><img src=\"https://img.shields.io/badge/DMG-Compatible%20Intel-555555?logo=apple&logoColor=white&style=flat-square&labelColor=222222\"></a> |\n| <img src=\"https://cdn.jsdelivr.net/gh/devicons/devicon/icons/linux/linux-original.svg\" alt=\"Linux\" width=\"28\"/> | Linux Kernel 5.4+<br>*(Compatible for Older CPU)* | <a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-BASE_VERSION-linux-amd64.AppImage\"><img src=\"https://img.shields.io/badge/AppImage-x64-f84e29?logo=linux&logoColor=white&style=flat-square&labelColor=222222\"></a> <a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-BASE_VERSION-linux-amd64.deb\"><img src=\"https://img.shields.io/badge/DEB-x64-A81D33?logo=debian&logoColor=white&style=flat-square&labelColor=222222\"></a><br><a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-BASE_VERSION-linux-arm64.deb\"><img src=\"https://img.shields.io/badge/DEB-ARM64-A81D33?logo=debian&logoColor=white&style=flat-square&labelColor=222222\"></a> <a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-BASE_VERSION-linux-amd64.rpm\"><img src=\"https://img.shields.io/badge/RPM-x64-CC0000?logo=redhat&logoColor=white&style=flat-square&labelColor=222222\"></a><br><a href=\"https://github.com/appshubcc/Bettbox/releases/download/vVERSION/Bettbox-BASE_VERSION-linux-amd64-compatible.deb\"><img src=\"https://img.shields.io/badge/DEB-Compatible%20x64-555555?logo=debian&logoColor=white&style=flat-square&labelColor=222222\"></a> |\n---\n</div>\n\n### 🐛 Feedback / 问题反馈\n\n> **Note / 提示：**\n> Detailed and well-structured issues will be prioritized(logs / reproduction steps)<br>\n> 书写认真、信息完整（包含必要的复现步骤和日志）的 issues 会被优先处理和对待\n\n**Bug Report / 提交故障**: [Click Here / 点击这里](https://github.com/appshubcc/Bettbox/issues/new?template=bug_report.yml)\n**Feature Request / 需求建议**: [Click Here / 点击这里](https://github.com/appshubcc/Bettbox/issues/new?template=feature_request.yml)\n\n<br>\n"
  },
  {
    "path": ".github/workflows/build.yaml",
    "content": "name: build\n\non:\n  push:\n    tags:\n      - 'v*'\n\nenv:\n  IS_STABLE: ${{ !contains(github.ref, '-') }}\n  IS_PRERELEASE: ${{ contains(github.ref_name, 'pre') }}\n  SHOULD_RELEASE: ${{ !contains(github.ref, '-') || contains(github.ref_name, 'pre') }}\n  GRADLE_OPTS: -Dorg.gradle.vfs.watch=false\n  FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true\n  SENTRY_BACKEND: none\n  \njobs:\n  build:\n    runs-on: ${{ matrix.os }}\n    strategy:\n      fail-fast: false\n      matrix:\n        include:\n          - platform: android\n            os: ubuntu-24.04\n            arch: arm64\n          - platform: android\n            os: ubuntu-24.04\n            arch: amd64\n          - platform: android\n            os: ubuntu-24.04\n            arch: arm\n          - platform: android\n            os: ubuntu-24.04\n            arch: universal\n          - platform: windows\n            os: windows-2022\n            arch: amd64\n          - platform: windows\n            os: windows-11-arm\n            arch: arm64\n          - platform: macos\n            os: macos-15\n            arch: arm64\n          - platform: macos\n            os: macos-15\n            arch: amd64\n          - platform: linux\n            os: ubuntu-22.04\n            arch: amd64\n          - platform: linux\n            os: ubuntu-24.04-arm\n            arch: arm64\n          - platform: linux\n            os: ubuntu-22.04\n            arch: amd64\n            compatible: true\n          - platform: windows\n            os: windows-2022\n            arch: amd64\n            compatible: true\n          - platform: macos\n            os: macos-15\n            arch: amd64\n            compatible: true\n\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n        with:\n          submodules: recursive\n          fetch-depth: 1\n\n      - name: Setup Rust (Windows)\n        if: matrix.platform == 'windows'\n        uses: dtolnay/rust-toolchain@stable\n        with:\n          targets: ${{ matrix.arch == 'arm64' && 'aarch64-pc-windows-msvc' || 'x86_64-pc-windows-msvc' }}\n\n      - name: Enable Git long paths (Windows)\n        if: matrix.platform == 'windows'\n        run: git config --global core.longpaths true\n\n      - name: Setup Keystore (Android)\n        if: matrix.platform == 'android'\n        run: |\n          echo \"${{ secrets.KEYSTORE }}\" | base64 --decode > android/app/keystore.jks\n          echo \"keyAlias=${{ secrets.KEY_ALIAS }}\" >> android/local.properties\n          echo \"storePassword=${{ secrets.STORE_PASSWORD }}\" >> android/local.properties\n          echo \"keyPassword=${{ secrets.KEY_PASSWORD }}\" >> android/local.properties\n          \n      - name: Setup Java\n        if: matrix.platform == 'android'\n        uses: actions/setup-java@v5\n        with:\n          distribution: temurin\n          java-version: '17'\n          cache: 'gradle'\n\n      - name: Install Dependencies (Linux)\n        if: matrix.platform == 'linux'\n        run: |\n          sudo apt-get update\n          sudo apt-get install -y libcurl4-openssl-dev ninja-build libgtk-3-dev rpm\n\n      - name: Setup Golang\n        uses: actions/setup-go@v6\n        with:\n          go-version: ${{ matrix.compatible == true && '1.20.x' || '1.24.x' }}\n          cache-dependency-path: core/go.sum\n\n      - name: Setup Flutter\n        uses: subosito/flutter-action@v2\n        with:\n          channel: ${{ contains(matrix.os, 'arm') && 'master' || 'stable' }}\n          cache: true\n          cache-key: flutter-${{ matrix.platform }}-${{ matrix.arch }}-${{ hashFiles('**/pubspec.lock') }}\n          flutter-version: ${{ contains(matrix.os, 'arm') && '1c6c6f4' || '3.35.7' }}\n\n      - name: Use prebuilt QuickJS bridge (Windows arm64)\n        if: matrix.platform == 'windows' && matrix.arch == 'arm64'\n        shell: pwsh\n        run: |\n          $dll = \"windows\\overrides\\flutter_js\\arm64\\quickjs_c_bridge.dll\"\n          if (-not (Test-Path $dll)) { throw \"Missing prebuilt QuickJS bridge: $dll\" }\n          Get-Item $dll | Select-Object FullName, Length, LastWriteTime\n\n      - name: Clean Gradle (Android)\n        if: matrix.platform == 'android'\n        run: |\n          cd android\n          flutter clean\n\n      - name: Build Bettbox\n        env:\n          SENTRY_DSN: ${{ secrets.SENTRY_DSN }}\n        run: |\n          flutter pub get\n          dart run build_runner build -d\n          dart setup.dart ${{ matrix.platform }} --arch ${{ matrix.arch }} ${{ env.IS_STABLE == 'true' && '--env stable' || '' }} ${{ matrix.compatible == true && '--compatible' || '' }}\n      \n      - name: Upload Artifacts\n        uses: actions/upload-artifact@v6\n        with:\n          name: Bettbox-${{ matrix.platform }}-${{ matrix.arch }}${{ matrix.compatible == true && '-compatible' || '' }}\n          path: ./dist\n          compression-level: 9\n          overwrite: true\n\n  upload:\n    permissions: write-all\n    needs: [ build ]\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n\n      - name: Download\n        uses: actions/download-artifact@v7\n        with:\n          path: ./dist/\n          pattern: Bettbox-*\n          merge-multiple: true\n\n      - name: Generate release.md\n        run: |\n          echo -e \"- Fixes and improvements\\n- 具体变更参考最新Commits\\n- 注意: 部分小米&Android16+机型在更新APP前,需提前关闭VPN以防止系统问题造成的网络锁死\" > release.md\n\n      - name: Patch release.md\n        run: |\n          version=$(echo \"${{ github.ref_name }}\" | sed 's/^v//')\n          base_version=$(echo \"$version\" | sed 's/-pre[0-9]*//')\n          if [ \"${{ env.IS_PRERELEASE }}\" == \"true\" ]; then\n            sed -e \"s|BASE_VERSION|$base_version|g\" -e \"s|VERSION|$version|g\" ./.github/release_template_pre.md >> release.md\n          else\n            sed -e \"s|BASE_VERSION|$base_version|g\" -e \"s|VERSION|$version|g\" ./.github/release_template.md >> release.md\n          fi\n\n      - name: Release\n        if: ${{ env.SHOULD_RELEASE == 'true' }}\n        uses: softprops/action-gh-release@v3.0.0\n        with:\n          files: ./dist/*\n          body_path: './release.md'\n          prerelease: ${{ env.IS_PRERELEASE == 'true' }}"
  },
  {
    "path": ".gitignore",
    "content": "# Miscellaneous\n*.md\n!.github/release_template.md\n!.github/release_template_pre.md\n*.class\n*.log\n*.pyc\n*.swp\n.DS_Store\n.atom/\n.build/\n.buildlog/\n.history\n.svn/\n.swiftpm/\nmigrate_working_dir/\n\n# IntelliJ related\n*.iml\n*.ipr\n*.iws\n.idea/\n\n# The .vscode folder contains launch configuration and tasks you configure in\n# VS Code which you may wish to be included in version control, so this line\n# is commented out by default.\n#.vscode/\n\n# Flutter/Dart/Pub related\n**/doc/api/\n**/ios/Flutter/.last_build_id\n.dart_tool/\n.flutter-plugins\n.flutter-plugins-dependencies\n.packages\n.pub-cache/\n.pub/\n/build/\n/specs/\n/dist/\n.test/\n.vscode/\n/services/helper/target/\n\n# Symbolication related\napp.*.symbols\n\n# Obfuscation related\napp.*.map.json\n\n# Android Studio will place build artifacts here\n/android/app/debug\n/android/app/profile\n/android/app/release\n\n/android/core/.cxx\n\n#libclash\n/libclash/\n\n#jniLibs\n/android/app/src/main/jniLibs/\n\n# FVM Version Cache\n.fvm/\n.fvmrc"
  },
  {
    "path": ".metadata",
    "content": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrades etc.\n#\n# This file should be version controlled.\n\nversion:\n  revision: 796c8ef79279f9c774545b3771238c3098dbefab\n  channel: stable\n\nproject_type: app\n\n# Tracks metadata for the flutter migrate command\nmigration:\n  platforms:\n    - platform: root\n      create_revision: 796c8ef79279f9c774545b3771238c3098dbefab\n      base_revision: 796c8ef79279f9c774545b3771238c3098dbefab\n    - platform: android\n      create_revision: 796c8ef79279f9c774545b3771238c3098dbefab\n      base_revision: 796c8ef79279f9c774545b3771238c3098dbefab\n    - platform: ios\n      create_revision: 796c8ef79279f9c774545b3771238c3098dbefab\n      base_revision: 796c8ef79279f9c774545b3771238c3098dbefab\n    - platform: linux\n      create_revision: 796c8ef79279f9c774545b3771238c3098dbefab\n      base_revision: 796c8ef79279f9c774545b3771238c3098dbefab\n    - platform: macos\n      create_revision: 796c8ef79279f9c774545b3771238c3098dbefab\n      base_revision: 796c8ef79279f9c774545b3771238c3098dbefab\n    - platform: web\n      create_revision: 796c8ef79279f9c774545b3771238c3098dbefab\n      base_revision: 796c8ef79279f9c774545b3771238c3098dbefab\n    - platform: windows\n      create_revision: 796c8ef79279f9c774545b3771238c3098dbefab\n      base_revision: 796c8ef79279f9c774545b3771238c3098dbefab\n\n  # User provided section\n\n  # List of Local paths (relative to this file) that should be\n  # ignored by the migrate tool.\n  #\n  # Files that are not part of the templates will be ignored by default.\n  unmanaged_files:\n    - 'lib/main.dart'\n    - 'ios/Runner.xcodeproj/project.pbxproj'\n"
  },
  {
    "path": "LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\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 GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  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\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions 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 convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU 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\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\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\nstate 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 3 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\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program 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, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\n"
  },
  {
    "path": "Makefile",
    "content": "android_arm64:\n\tdart ./setup.dart android --arch arm64\nmacos_arm64:\n\tdart ./setup.dart macos --arch arm64\nandroid_app:\n\tdart ./setup.dart android\nandroid_arm64_core:\n\tdart ./setup.dart android --arch arm64 --out core\nmacos_arm64_core:\n\tdart ./setup.dart macos --arch arm64  --out core"
  },
  {
    "path": "analysis_options.yaml",
    "content": "include: package:flutter_lints/flutter.yaml\nanalyzer:\n  exclude:\n    - lib/l10n/intl/**\n    - lib/clash/generated/**\n    - lib/**/generated/**\n    - \"**/*.g.dart\"\n    - \"**/*.freezed.dart\"\n    - \"plugins/**\"\n\nlinter:\n  rules:\n    prefer_single_quotes: true"
  },
  {
    "path": "android/.gitignore",
    "content": "gradle-wrapper.jar\n/.gradle\n/gradle/\n/captures/\n/gradlew\n/gradlew.bat\n/local.properties\nGeneratedPluginRegistrant.java\n\n# Remember to never publicly share your keystore.\n# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app\nkey.properties\n**/*.keystore\n**/*.jks\n"
  },
  {
    "path": "android/app/build.gradle.kts",
    "content": "import java.util.Properties\n\nplugins {\n    id(\"com.android.application\")\n    id(\"kotlin-android\")\n    id(\"dev.flutter.flutter-gradle-plugin\")\n}\n\nval localProperties = Properties().apply {\n    rootProject.file(\"local.properties\").takeIf { it.exists() }?.inputStream()?.use { load(it) }\n}\n\nval mStoreFile = file(\"keystore.jks\")\nval mStorePassword: String? = localProperties.getProperty(\"storePassword\")\nval mKeyAlias: String? = localProperties.getProperty(\"keyAlias\")\nval mKeyPassword: String? = localProperties.getProperty(\"keyPassword\")\nval isRelease = mStoreFile.exists() && mStorePassword != null && mKeyAlias != null && mKeyPassword != null\n\nandroid {\n    namespace = \"com.appshub.bettbox\"\n    compileSdk = 36\n    ndkVersion = \"28.2.13676358\"\n\n    compileOptions {\n        sourceCompatibility = JavaVersion.VERSION_17\n        targetCompatibility = JavaVersion.VERSION_17\n    }\n\n    kotlinOptions {\n        jvmTarget = JavaVersion.VERSION_17.toString()\n    }\n\n    defaultConfig {\n        applicationId = \"com.appshub.bettbox\"\n        minSdk = 26\n        targetSdk = 36\n        versionCode = flutter.versionCode\n        versionName = flutter.versionName\n    }\n\n    signingConfigs {\n        if (isRelease) {\n            create(\"release\") {\n                storeFile = mStoreFile\n                storePassword = mStorePassword\n                keyAlias = mKeyAlias\n                keyPassword = mKeyPassword\n            }\n        }\n    }\n\n    buildTypes {\n        debug {\n            isMinifyEnabled = false\n            applicationIdSuffix = \".debug\"\n        }\n        release {\n            isMinifyEnabled = true\n            isDebuggable = false\n            signingConfig = signingConfigs.getByName(if (isRelease) \"release\" else \"debug\")\n            proguardFiles(getDefaultProguardFile(\"proguard-android-optimize.txt\"), \"proguard-rules.pro\")\n        }\n    }\n\n    packaging {\n        jniLibs {\n            useLegacyPackaging = true\n        }\n    }\n}\n\nflutter {\n    source = \"../..\"\n}\n\ndependencies {\n    implementation(project(\":core\"))\n    implementation(\"com.google.code.gson:gson:2.10.1\")\n    implementation(\"com.android.tools.smali:smali-dexlib2:3.0.9\") {\n        exclude(group = \"com.google.guava\", module = \"guava\")\n    }\n}\n\nconfigurations.all {\n    resolutionStrategy {\n        eachDependency {\n            if (requested.group == \"androidx.datastore\") useVersion(\"1.1.2\")\n        }\n    }\n}\n"
  },
  {
    "path": "android/app/proguard-rules.pro",
    "content": "\n-keep class com.appshub.bettbox.models.**{ *; }"
  },
  {
    "path": "android/app/src/debug/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\">\n    <!-- The INTERNET permission is required for development. Specifically,\n         the Flutter tool needs it to communicate with the running application\n         to allow setting breakpoints, to provide hot reload, etc.\n    -->\n    <application\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"Bettbox Debug\"\n        tools:replace=\"android:label\">\n        <service\n            android:name=\".services.BettboxTileService\"\n            android:label=\"Bettbox Debug\"\n            tools:replace=\"android:label\"\n            tools:targetApi=\"24\" />\n    </application>\n</manifest>\n"
  },
  {
    "path": "android/app/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:tools=\"http://schemas.android.com/tools\">\n\n    <uses-feature\n        android:name=\"android.hardware.touchscreen\"\n        android:required=\"false\" />\n    <uses-feature\n        android:name=\"android.hardware.camera\"\n        android:required=\"false\" />\n    <uses-feature\n        android:name=\"android.software.leanback\"\n        android:required=\"false\" />\n\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n    <uses-permission android:name=\"android.permission.FOREGROUND_SERVICE\" />\n    <uses-permission android:name=\"android.permission.FOREGROUND_SERVICE_SPECIAL_USE\" />\n    <uses-permission android:name=\"android.permission.POST_NOTIFICATIONS\" />\n    <uses-permission android:name=\"android.permission.RECEIVE_BOOT_COMPLETED\" />\n    <uses-permission android:name=\"android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS\" />\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n\n    <uses-permission\n        android:name=\"android.permission.QUERY_ALL_PACKAGES\"\n        tools:ignore=\"QueryAllPackagesPermission\" />\n\n    <application\n        android:name=\".BettboxApplication\"\n        android:hardwareAccelerated=\"true\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"Bettbox\"\n        android:banner=\"@drawable/tv_banner\"\n        android:enableOnBackInvokedCallback=\"true\">\n        <activity\n            android:name=\"com.appshub.bettbox.MainActivity\"\n            android:configChanges=\"orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode\"\n            android:exported=\"true\"\n            android:hardwareAccelerated=\"true\"\n            android:launchMode=\"singleTop\"\n            android:theme=\"@style/LaunchTheme\"\n            android:windowSoftInputMode=\"adjustResize\">\n            <!-- Specifies an Android theme to apply to this Activity as soon as\n                 the Android process has started. This theme is visible to the user\n                 while the Flutter UI initializes. After that, this theme continues\n                 to determine the Window background behind the Flutter UI. -->\n            <meta-data\n                android:name=\"io.flutter.embedding.android.NormalTheme\"\n                android:resource=\"@style/NormalTheme\" />\n\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n                <category android:name=\"android.intent.category.LEANBACK_LAUNCHER\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.service.quicksettings.action.QS_TILE_PREFERENCES\" />\n            </intent-filter>\n            <intent-filter>\n                <action android:name=\"android.intent.action.VIEW\" />\n\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <category android:name=\"android.intent.category.BROWSABLE\" />\n\n                <data android:scheme=\"clash\" android:host=\"install-config\" />\n                <data android:scheme=\"clashmeta\" android:host=\"install-config\" />\n                <data android:scheme=\"bettbox\" android:host=\"install-config\" />\n            </intent-filter>\n        </activity>\n\n        <!-- Light icon alias -->\n        <activity-alias\n            android:name=\"com.appshub.bettbox.MainActivityLight\"\n            android:enabled=\"false\"\n            android:exported=\"true\"\n            android:icon=\"@mipmap/ic_launcher_light\"\n            android:roundIcon=\"@mipmap/ic_launcher_round_light\"\n            android:targetActivity=\"com.appshub.bettbox.MainActivity\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n                <category android:name=\"android.intent.category.LEANBACK_LAUNCHER\" />\n            </intent-filter>\n            <!-- Tile long-press intent -->\n            <intent-filter>\n                <action android:name=\"android.service.quicksettings.action.QS_TILE_PREFERENCES\" />\n            </intent-filter>\n        </activity-alias>\n\n        <activity\n            android:name=\".TempActivity\"\n            android:exported=\"true\"\n            android:theme=\"@style/TransparentTheme\">\n            <intent-filter>\n                <category android:name=\"android.intent.category.DEFAULT\" />\n                <action android:name=\"${applicationId}.action.START\" />\n                <action android:name=\"${applicationId}.action.STOP\" />\n                <action android:name=\"${applicationId}.action.CHANGE\" />\n            </intent-filter>\n        </activity>\n\n        <service\n            android:name=\".services.BettboxTileService\"\n            android:exported=\"true\"\n            android:icon=\"@drawable/ic_tile\"\n            android:label=\"Bettbox\"\n            android:permission=\"android.permission.BIND_QUICK_SETTINGS_TILE\"\n            tools:targetApi=\"n\">\n            <intent-filter>\n                <action android:name=\"android.service.quicksettings.action.QS_TILE\" />\n            </intent-filter>\n            <meta-data\n                android:name=\"android.service.quicksettings.TOGGLEABLE_TILE\"\n                android:value=\"true\" />\n        </service>\n\n        <provider\n            android:name=\".FilesProvider\"\n            android:authorities=\"${applicationId}.files\"\n            android:exported=\"true\"\n            android:grantUriPermissions=\"true\"\n            android:permission=\"android.permission.MANAGE_DOCUMENTS\"\n            android:process=\":background\">\n            <intent-filter>\n                <action android:name=\"android.content.action.DOCUMENTS_PROVIDER\" />\n            </intent-filter>\n        </provider>\n\n        <provider\n            android:name=\"androidx.core.content.FileProvider\"\n            android:authorities=\"${applicationId}.fileProvider\"\n            android:exported=\"false\"\n            android:grantUriPermissions=\"true\">\n            <meta-data\n                android:name=\"android.support.FILE_PROVIDER_PATHS\"\n                android:resource=\"@xml/file_paths\" />\n        </provider>\n\n        <service\n            android:name=\".services.BettboxVpnService\"\n            android:exported=\"false\"\n            android:foregroundServiceType=\"specialUse\"\n            android:permission=\"android.permission.BIND_VPN_SERVICE\">\n            <intent-filter>\n                <action android:name=\"android.net.VpnService\" />\n            </intent-filter>\n            <meta-data android:name=\"android.net.VpnService.SUPPORTS_ALWAYS_ON\" android:value=\"true\" />\n            <property android:name=\"android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE\" android:value=\"VPN\" />\n        </service>\n\n        <service\n            android:name=\".services.BettboxService\"\n            android:exported=\"false\"\n            android:foregroundServiceType=\"specialUse\">\n            <property android:name=\"android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE\" android:value=\"Proxy\" />\n        </service>\n\n        <receiver\n            android:name=\".receivers.BootReceiver\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.BOOT_COMPLETED\" />\n            </intent-filter>\n        </receiver>\n        \n        <receiver\n            android:name=\".receivers.PackageReplacedReceiver\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MY_PACKAGE_REPLACED\" />\n            </intent-filter>\n        </receiver>\n\n        <meta-data\n            android:name=\"flutterEmbedding\"\n            android:value=\"2\" />\n    </application>\n</manifest>\n"
  },
  {
    "path": "android/app/src/main/kotlin/com/appshub/bettbox/BettboxApplication.kt",
    "content": "package com.appshub.bettbox\n\nimport android.app.Application\nimport android.content.Context\n\nclass BettboxApplication : Application() {\n    companion object {\n        private lateinit var instance: BettboxApplication\n        fun getAppContext(): Context = instance.applicationContext\n    }\n\n    override fun onCreate() {\n        super.onCreate()\n        instance = this\n    }\n}\n"
  },
  {
    "path": "android/app/src/main/kotlin/com/appshub/bettbox/FilesProvider.kt",
    "content": "package com.appshub.bettbox\n\nimport android.database.Cursor\nimport android.database.MatrixCursor\nimport android.os.CancellationSignal\nimport android.os.ParcelFileDescriptor\nimport android.provider.DocumentsContract.Document\nimport android.provider.DocumentsContract.Root\nimport android.provider.DocumentsProvider\nimport java.io.File\nimport java.io.FileNotFoundException\n\nclass FilesProvider : DocumentsProvider() {\n\n    companion object {\n        private const val DEFAULT_ROOT_ID = \"0\"\n\n        private val DEFAULT_DOCUMENT_COLUMNS = arrayOf(\n            Document.COLUMN_DOCUMENT_ID,\n            Document.COLUMN_DISPLAY_NAME,\n            Document.COLUMN_MIME_TYPE,\n            Document.COLUMN_FLAGS,\n            Document.COLUMN_SIZE,\n        )\n        private val DEFAULT_ROOT_COLUMNS = arrayOf(\n            Root.COLUMN_ROOT_ID,\n            Root.COLUMN_FLAGS,\n            Root.COLUMN_ICON,\n            Root.COLUMN_TITLE,\n            Root.COLUMN_SUMMARY,\n            Root.COLUMN_DOCUMENT_ID\n        )\n    }\n\n    override fun onCreate(): Boolean = true\n\n    override fun queryRoots(projection: Array<String>?): Cursor = MatrixCursor(projection ?: DEFAULT_ROOT_COLUMNS).apply {\n        newRow().apply {\n            add(Root.COLUMN_ROOT_ID, DEFAULT_ROOT_ID)\n            add(Root.COLUMN_FLAGS, Root.FLAG_LOCAL_ONLY)\n            add(Root.COLUMN_ICON, R.mipmap.ic_launcher)\n            add(Root.COLUMN_TITLE, context!!.getString(R.string.bett_box))\n            add(Root.COLUMN_SUMMARY, \"Data\")\n            add(Root.COLUMN_DOCUMENT_ID, \"/\")\n        }\n    }\n\n    override fun queryChildDocuments(\n        parentDocumentId: String,\n        projection: Array<String>?,\n        sortOrder: String?\n    ): Cursor {\n        val result = MatrixCursor(resolveDocumentProjection(projection))\n        val parentFile = (if (parentDocumentId == \"/\") context?.filesDir else File(parentDocumentId))\n            ?: throw FileNotFoundException(\"Parent directory not found\")\n        parentFile.listFiles()?.forEach { includeFile(result, it) }\n        return result\n    }\n\n    override fun queryDocument(documentId: String, projection: Array<String>?): Cursor =\n        MatrixCursor(resolveDocumentProjection(projection)).apply { includeFile(this, File(documentId)) }\n\n    override fun openDocument(\n        documentId: String,\n        mode: String,\n        signal: CancellationSignal?\n    ): ParcelFileDescriptor = ParcelFileDescriptor.open(File(documentId), ParcelFileDescriptor.parseMode(mode))\n\n    private fun includeFile(result: MatrixCursor, file: File) {\n        result.newRow().apply {\n            add(Document.COLUMN_DOCUMENT_ID, file.absolutePath)\n            add(Document.COLUMN_DISPLAY_NAME, file.name)\n            add(Document.COLUMN_SIZE, file.length())\n            add(Document.COLUMN_FLAGS, Document.FLAG_SUPPORTS_WRITE or Document.FLAG_SUPPORTS_DELETE)\n            add(Document.COLUMN_MIME_TYPE, getDocumentType(file))\n        }\n    }\n\n    private fun getDocumentType(file: File): String =\n        if (file.isDirectory) Document.MIME_TYPE_DIR else \"application/octet-stream\"\n\n    private fun resolveDocumentProjection(projection: Array<String>?): Array<String> = projection ?: DEFAULT_DOCUMENT_COLUMNS\n}\n"
  },
  {
    "path": "android/app/src/main/kotlin/com/appshub/bettbox/GlobalState.kt",
    "content": "package com.appshub.bettbox\n\nimport android.os.SystemClock\nimport com.appshub.bettbox.plugins.AppPlugin\nimport com.appshub.bettbox.plugins.ServicePlugin\nimport com.appshub.bettbox.plugins.TilePlugin\nimport com.appshub.bettbox.plugins.VpnPlugin\nimport io.flutter.FlutterInjector\nimport io.flutter.embedding.engine.FlutterEngine\nimport io.flutter.embedding.engine.dart.DartExecutor\nimport io.flutter.plugins.GeneratedPluginRegistrant\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.Job\nimport kotlinx.coroutines.SupervisorJob\nimport kotlinx.coroutines.cancel\nimport kotlinx.coroutines.delay\nimport kotlinx.coroutines.flow.MutableStateFlow\nimport kotlinx.coroutines.flow.asStateFlow\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\nimport java.util.concurrent.locks.ReentrantLock\nimport kotlin.concurrent.withLock\n\nenum class RunState {\n    START,\n    PENDING,\n    STOP\n}\n\nobject GlobalState {\n    val runLock = ReentrantLock()\n    private val scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)\n\n    const val NOTIFICATION_CHANNEL = \"Bettbox\"\n    const val NOTIFICATION_ID = 1\n\n    private const val TOGGLE_DEBOUNCE_MS = 1000L\n    private const val PENDING_TIMEOUT_MS = 5000L\n    private const val STOP_LOCK_TIMEOUT_MS = 5000L\n\n    @Volatile\n    private var lastToggleAt = 0L\n\n    @Volatile\n    var currentRunState: RunState = RunState.STOP\n        private set\n\n    private val _runState = MutableStateFlow(RunState.STOP)\n    val runState = _runState.asStateFlow()\n\n    private var pendingTimeoutJob: Job? = null\n\n    var flutterEngine: FlutterEngine? = null\n    private var serviceEngine: FlutterEngine? = null\n\n    @Volatile\n    var isSmartStopped = false\n\n    @Volatile\n    var isStopping = false\n\n    fun updateRunState(newState: RunState) {\n        if (newState != RunState.PENDING) {\n            pendingTimeoutJob?.cancel()\n            pendingTimeoutJob = null\n        }\n        currentRunState = newState\n        _runState.value = newState\n    }\n\n    private fun startPendingTimeout() {\n        pendingTimeoutJob?.cancel()\n        pendingTimeoutJob = scope.launch {\n            delay(PENDING_TIMEOUT_MS)\n            if (currentRunState == RunState.PENDING) {\n                android.util.Log.w(\"GlobalState\", \"PENDING state timeout, resetting to STOP\")\n                updateRunState(RunState.STOP)\n            }\n        }\n    }\n\n    fun updateIsStopping(value: Boolean) {\n        isStopping = value\n        runCatching {\n            val ts = if (value) System.currentTimeMillis() else 0L\n            BettboxApplication.getAppContext()\n                .getSharedPreferences(\"vpn_state\", android.content.Context.MODE_PRIVATE)\n                .edit()\n                .putLong(\"stop_lock_ts\", ts)\n                .apply()\n        }\n    }\n\n    fun isCurrentlyStopping(): Boolean {\n        if (isStopping) return true\n        return runCatching {\n            val sp = BettboxApplication.getAppContext()\n                .getSharedPreferences(\"vpn_state\", android.content.Context.MODE_PRIVATE)\n            val ts = sp.getLong(\"stop_lock_ts\", 0L)\n            if (ts == 0L) return false\n\n            val now = System.currentTimeMillis()\n            if (now - ts > STOP_LOCK_TIMEOUT_MS) {\n                sp.edit().remove(\"stop_lock_ts\").apply()\n                false\n            } else {\n                true\n            }\n        }.getOrDefault(false)\n    }\n\n    fun getCurrentAppPlugin(): AppPlugin? {\n        val currentEngine = flutterEngine ?: serviceEngine\n        return currentEngine?.plugins?.get(AppPlugin::class.java) as? AppPlugin\n    }\n\n    fun syncStatus() {\n        val status = VpnPlugin.getStatus()\n        updateRunState(if (status) RunState.START else RunState.STOP)\n    }\n\n    suspend fun getText(text: String): String = getCurrentAppPlugin()?.getText(text) ?: \"\"\n\n    fun getCurrentTilePlugin(): TilePlugin? {\n        val currentEngine = flutterEngine ?: serviceEngine\n        return currentEngine?.plugins?.get(TilePlugin::class.java) as? TilePlugin\n    }\n\n    fun getCurrentVPNPlugin(): VpnPlugin? {\n        return serviceEngine?.plugins?.get(VpnPlugin::class.java) as? VpnPlugin\n    }\n\n    fun handleToggle() {\n        if (!acquireToggleSlot()) return\n        if (!handleStart(skipDebounce = true)) {\n            handleStop(skipDebounce = true)\n        }\n    }\n\n    fun handleStart(skipDebounce: Boolean = false): Boolean {\n        if (!skipDebounce && !acquireToggleSlot()) return false\n        if (currentRunState != RunState.STOP) return false\n\n        updateRunState(RunState.PENDING)\n        startPendingTimeout()\n        runLock.withLock {\n            getCurrentTilePlugin()?.handleStart() ?: initServiceEngine()\n        }\n        return true\n    }\n\n    fun handleStop(skipDebounce: Boolean = false) {\n        if (!skipDebounce && !acquireToggleSlot()) return\n        if (currentRunState != RunState.START) return\n\n        updateRunState(RunState.PENDING)\n        startPendingTimeout()\n        runLock.withLock {\n            getCurrentTilePlugin()?.handleStop()\n        }\n    }\n\n    private fun acquireToggleSlot(): Boolean {\n        val now = SystemClock.elapsedRealtime()\n        synchronized(this) {\n            if (now - lastToggleAt < TOGGLE_DEBOUNCE_MS) return false\n            lastToggleAt = now\n            return true\n        }\n    }\n\n    fun handleTryDestroy() {\n        if (flutterEngine == null) destroyServiceEngine()\n    }\n\n    fun destroyServiceEngine() {\n        runLock.withLock {\n            serviceEngine?.destroy()\n            serviceEngine = null\n        }\n    }\n\n    fun initServiceEngine(flags: List<String>? = null) {\n        runLock.withLock {\n            if (serviceEngine != null) return\n            serviceEngine = FlutterEngine(BettboxApplication.getAppContext()).apply {\n                plugins.add(VpnPlugin)\n                plugins.add(AppPlugin())\n                plugins.add(TilePlugin())\n                plugins.add(ServicePlugin())\n                GeneratedPluginRegistrant.registerWith(this)\n            }\n            val vpnService = DartExecutor.DartEntrypoint(\n                FlutterInjector.instance().flutterLoader().findAppBundlePath(),\n                \"_service\"\n            )\n            val defaultArgs = if (flutterEngine == null && !isCurrentlyStopping()) listOf(\"quick\") else null\n            val args = flags ?: defaultArgs\n            serviceEngine?.dartExecutor?.executeDartEntrypoint(vpnService, args)\n        }\n    }\n\n    fun isServiceEngineRunning(): Boolean = serviceEngine != null\n\n    fun reconnectIpc() {\n        (serviceEngine?.plugins?.get(TilePlugin::class.java) as? TilePlugin)?.handleReconnectIpc()\n    }\n}\n"
  },
  {
    "path": "android/app/src/main/kotlin/com/appshub/bettbox/MainActivity.kt",
    "content": "package com.appshub.bettbox\n\nimport android.content.Context\nimport com.appshub.bettbox.plugins.AppPlugin\nimport com.appshub.bettbox.plugins.ServicePlugin\nimport com.appshub.bettbox.plugins.TilePlugin\nimport com.appshub.bettbox.plugins.VpnPlugin\nimport io.flutter.embedding.android.FlutterActivity\nimport io.flutter.embedding.engine.FlutterEngine\nimport io.flutter.embedding.engine.FlutterEngineCache\nimport io.flutter.embedding.engine.dart.DartExecutor\nimport io.flutter.plugins.GeneratedPluginRegistrant\n\nclass MainActivity : FlutterActivity() {\n    companion object {\n        private const val MAIN_ENGINE_ID = \"bettbox_main_engine\"\n    }\n\n    override fun provideFlutterEngine(context: Context): FlutterEngine {\n        val engineCache = FlutterEngineCache.getInstance()\n        return engineCache.get(MAIN_ENGINE_ID) ?: createAndCacheEngine(context, engineCache)\n    }\n\n    private fun createAndCacheEngine(context: Context, cache: FlutterEngineCache) =\n        FlutterEngine(context.applicationContext).apply {\n            GeneratedPluginRegistrant.registerWith(this)\n            dartExecutor.executeDartEntrypoint(DartExecutor.DartEntrypoint.createDefault())\n            cache.put(MAIN_ENGINE_ID, this)\n            GlobalState.flutterEngine = this\n        }\n\n    override fun configureFlutterEngine(flutterEngine: FlutterEngine) {\n        super.configureFlutterEngine(flutterEngine)\n\n        listOf(VpnPlugin, AppPlugin(), ServicePlugin(), TilePlugin()).forEach { plugin ->\n            if (flutterEngine.plugins.get(plugin.javaClass) == null) {\n                flutterEngine.plugins.add(plugin)\n            }\n        }\n\n        GlobalState.flutterEngine = flutterEngine\n    }\n\n    override fun shouldDestroyEngineWithHost(): Boolean = false\n}\n"
  },
  {
    "path": "android/app/src/main/kotlin/com/appshub/bettbox/TempActivity.kt",
    "content": "package com.appshub.bettbox\n\nimport android.app.Activity\nimport android.os.Bundle\nimport com.appshub.bettbox.extensions.wrapAction\n\nclass TempActivity : Activity() {\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        when (intent.action) {\n            wrapAction(\"START\") -> GlobalState.handleStart()\n            wrapAction(\"STOP\") -> GlobalState.handleStop()\n            wrapAction(\"CHANGE\") -> GlobalState.handleToggle()\n        }\n        finishAndRemoveTask()\n    }\n}\n"
  },
  {
    "path": "android/app/src/main/kotlin/com/appshub/bettbox/extensions/Ext.kt",
    "content": "package com.appshub.bettbox.extensions\n\nimport android.app.PendingIntent\nimport android.content.Context\nimport android.content.Intent\nimport android.graphics.Bitmap\nimport android.graphics.drawable.Drawable\nimport android.net.ConnectivityManager\nimport android.net.Network\nimport android.os.Build\nimport android.system.OsConstants.IPPROTO_TCP\nimport android.system.OsConstants.IPPROTO_UDP\nimport android.util.Base64\nimport androidx.core.graphics.drawable.toBitmap\nimport com.appshub.bettbox.TempActivity\nimport com.appshub.bettbox.models.CIDR\nimport com.appshub.bettbox.models.Metadata\nimport com.appshub.bettbox.models.VpnOptions\nimport io.flutter.plugin.common.MethodChannel\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.suspendCancellableCoroutine\nimport kotlinx.coroutines.withContext\nimport java.io.ByteArrayOutputStream\nimport java.net.Inet4Address\nimport java.net.Inet6Address\nimport java.net.InetAddress\nimport java.util.concurrent.locks.ReentrantLock\nimport kotlin.coroutines.resume\nimport kotlin.coroutines.suspendCoroutine\n\nsuspend fun Drawable.getBase64(maxSizePx: Int = 128): String = withContext(Dispatchers.IO) {\n    val defaultSize = 96\n    val intrinsicWidth = if (intrinsicWidth > 0) intrinsicWidth else defaultSize\n    val intrinsicHeight = if (intrinsicHeight > 0) intrinsicHeight else defaultSize\n    val maxDim = maxOf(intrinsicWidth, intrinsicHeight)\n    val targetSize = minOf(maxDim, maxSizePx)\n    val scale = targetSize.toFloat() / maxDim.toFloat()\n    val targetWidth = (intrinsicWidth * scale).toInt().coerceAtLeast(1)\n    val targetHeight = (intrinsicHeight * scale).toInt().coerceAtLeast(1)\n    val bitmap = toBitmap(targetWidth, targetHeight, Bitmap.Config.ARGB_8888)\n    ByteArrayOutputStream().use { byteArrayOutputStream ->\n        bitmap.compress(Bitmap.CompressFormat.PNG, 80, byteArrayOutputStream)\n        Base64.encodeToString(byteArrayOutputStream.toByteArray(), Base64.NO_WRAP)\n    }\n}\n\nfun Metadata.getProtocol(): Int? = when {\n    network.startsWith(\"tcp\") -> IPPROTO_TCP\n    network.startsWith(\"udp\") -> IPPROTO_UDP\n    else -> null\n}\n\nfun VpnOptions.getIpv4RouteAddress(): List<CIDR> = routeAddress.filter { it.isIpv4() }.map { it.toCIDR() }\n\nfun VpnOptions.getIpv6RouteAddress(): List<CIDR> = routeAddress.filter { it.isIpv6() }.map { it.toCIDR() }\n\nfun String.isIpv4(): Boolean {\n    val parts = split(\"/\")\n    require(parts.size == 2) { \"Invalid CIDR format\" }\n    val ip = parts[0]\n    return ip.contains('.') && !ip.contains(':')\n}\n\nfun String.isIpv6(): Boolean {\n    val parts = split(\"/\")\n    require(parts.size == 2) { \"Invalid CIDR format\" }\n    val ip = parts[0]\n    return ip.contains(':') && !ip.contains('.')\n}\n\nfun String.toCIDR(): CIDR {\n    val parts = split(\"/\")\n    require(parts.size == 2) { \"Invalid CIDR format\" }\n    val ipAddress = parts[0]\n    val prefixLength = parts[1].toIntOrNull() ?: throw IllegalArgumentException(\"Invalid prefix length\")\n    val address = InetAddress.getByName(ipAddress)\n    val maxPrefix = if (address.address.size == 4) 32 else 128\n    require(prefixLength in 0..maxPrefix) { \"Invalid prefix length for IP version\" }\n    return CIDR(address, prefixLength)\n}\n\nfun ConnectivityManager.resolveDns(network: Network?): List<String> =\n    getLinkProperties(network)?.dnsServers?.map { it.asSocketAddressText(53) } ?: emptyList()\n\nfun InetAddress.asSocketAddressText(port: Int): String = when (this) {\n    is Inet6Address -> \"[${numericToTextFormat(this)}]:$port\"\n    is Inet4Address -> \"$hostAddress:$port\"\n    else -> throw IllegalArgumentException(\"Unsupported Inet type ${javaClass}\")\n}\n\nfun Context.wrapAction(action: String): String = \"${packageName}.action.$action\"\n\nfun Context.getActionIntent(action: String): Intent =\n    Intent(this, TempActivity::class.java).apply {\n        this.action = wrapAction(action)\n        addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_MULTIPLE_TASK)\n    }\n\nfun Context.getActionPendingIntent(action: String): PendingIntent {\n    val flags = if (Build.VERSION.SDK_INT >= 31) {\n        PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT\n    } else {\n        PendingIntent.FLAG_UPDATE_CURRENT\n    }\n    return PendingIntent.getActivity(this, 0, getActionIntent(action), flags)\n}\n\nprivate fun numericToTextFormat(address: Inet6Address): String = buildString(39) {\n    val src = address.address\n    for (i in 0 until 8) {\n        append(Integer.toHexString(src[i shl 1].toInt() shl 8 and 0xff00 or (src[(i shl 1) + 1].toInt() and 0xff)))\n        if (i < 7) append(\":\")\n    }\n    if (address.scopeId > 0) {\n        append(\"%\")\n        append(address.scopeId)\n    }\n}\n\nsuspend fun <T> MethodChannel.awaitResult(method: String, arguments: Any? = null): T? =\n    withContext(Dispatchers.Main) {\n        suspendCancellableCoroutine { continuation ->\n            invokeMethod(method, arguments, object : MethodChannel.Result {\n                @Suppress(\"UNCHECKED_CAST\")\n                override fun success(result: Any?) {\n                    if (continuation.isActive) continuation.resume(result as? T)\n                }\n                override fun error(code: String, message: String?, details: Any?) {\n                    if (continuation.isActive) continuation.resume(null)\n                }\n                override fun notImplemented() {\n                    if (continuation.isActive) continuation.resume(null)\n                }\n            })\n        }\n    }\n\nfun ReentrantLock.safeLock() {\n    if (!isHeldByCurrentThread) lock()\n}\n\nfun ReentrantLock.safeUnlock() {\n    if (isHeldByCurrentThread) unlock()\n}\n"
  },
  {
    "path": "android/app/src/main/kotlin/com/appshub/bettbox/models/Package.kt",
    "content": "package com.appshub.bettbox.models\n\ndata class Package(\n    val packageName: String,\n    val label: String,\n    val system: Boolean,\n    val internet: Boolean,\n    val lastUpdateTime: Long,\n)\n"
  },
  {
    "path": "android/app/src/main/kotlin/com/appshub/bettbox/models/Process.kt",
    "content": "package com.appshub.bettbox.models\n\ndata class Process(\n    val id: String,\n    val metadata: Metadata,\n)\n\ndata class Metadata(\n    val network: String,\n    val sourceIP: String,\n    val sourcePort: Int,\n    val destinationIP: String,\n    val destinationPort: Int,\n    val host: String\n)\n"
  },
  {
    "path": "android/app/src/main/kotlin/com/appshub/bettbox/models/Props.kt",
    "content": "package com.appshub.bettbox.models\n\nimport java.net.InetAddress\n\nenum class AccessControlMode {\n    acceptSelected, rejectSelected,\n}\n\ndata class AccessControl(\n    val enable: Boolean,\n    val mode: AccessControlMode,\n    val acceptList: List<String>,\n    val rejectList: List<String>,\n)\n\ndata class CIDR(val address: InetAddress, val prefixLength: Int)\n\ndata class VpnOptions(\n    val enable: Boolean,\n    val port: Int,\n    val accessControl: AccessControl,\n    val allowBypass: Boolean,\n    val systemProxy: Boolean,\n    val bypassDomain: List<String>,\n    val routeAddress: List<String>,\n    val routeMode: String = \"config\",\n    val ipv4Address: String,\n    val ipv6Address: String,\n    val dnsServerAddress: String,\n    val dozeSuspend: Boolean = false,\n    val mtu: Int = 4064,\n)\n\ndata class StartForegroundParams(\n    val title: String,\n    val content: String,\n)"
  },
  {
    "path": "android/app/src/main/kotlin/com/appshub/bettbox/modules/SuspendModule.kt",
    "content": "package com.appshub.bettbox.modules\n\nimport android.content.BroadcastReceiver\nimport android.content.Context\nimport android.content.Intent\nimport android.content.IntentFilter\nimport android.os.Build\nimport android.os.PowerManager\nimport android.util.Log\nimport androidx.core.content.getSystemService\nimport com.appshub.bettbox.core.Core\n\nclass SuspendModule(private val context: Context) {\n    companion object {\n        private const val TAG = \"SuspendModule\"\n    }\n\n    private var isInstalled = false\n    private var isSuspended = false\n\n    private val powerManager: PowerManager? by lazy { context.getSystemService<PowerManager>() }\n\n    private val isScreenOn: Boolean get() = powerManager?.isInteractive ?: true\n\n    private val isDeviceIdleMode: Boolean\n        get() = Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && powerManager?.isDeviceIdleMode == true\n\n    private val shouldSuspend: Boolean get() = !isScreenOn && isDeviceIdleMode\n\n    private fun updateSuspendState() {\n        val shouldSuspendNow = shouldSuspend\n\n        Log.d(TAG, \"updateSuspendState - shouldSuspend: $shouldSuspendNow, isSuspended: $isSuspended\")\n\n        when {\n            shouldSuspendNow && !isSuspended -> {\n                Log.i(TAG, \"Entering Doze - Suspending core\")\n                Core.suspended(true)\n                isSuspended = true\n            }\n            !shouldSuspendNow && isSuspended -> {\n                Log.i(TAG, \"Exiting Doze - Resuming core\")\n                Core.suspended(false)\n                isSuspended = false\n            }\n        }\n    }\n\n    private val receiver = object : BroadcastReceiver() {\n        override fun onReceive(context: Context?, intent: Intent?) {\n            intent?.action?.let {\n                Log.d(TAG, \"Received $it\")\n                updateSuspendState()\n            }\n        }\n    }\n\n    fun install() {\n        if (isInstalled) return\n        isInstalled = true\n        isSuspended = false\n\n        val filter = IntentFilter().apply {\n            addAction(Intent.ACTION_SCREEN_ON)\n            addAction(Intent.ACTION_SCREEN_OFF)\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n                addAction(PowerManager.ACTION_DEVICE_IDLE_MODE_CHANGED)\n            }\n        }\n        context.registerReceiver(receiver, filter)\n        updateSuspendState()\n        Log.i(TAG, \"SuspendModule installed - SDK: ${Build.VERSION.SDK_INT}\")\n    }\n\n    fun uninstall() {\n        if (!isInstalled) return\n        isInstalled = false\n\n        runCatching {\n            context.unregisterReceiver(receiver)\n            if (isSuspended) {\n                Log.i(TAG, \"Uninstalling - Resume from suspend\")\n                Core.suspended(false)\n                isSuspended = false\n            }\n        }.onFailure { Log.w(TAG, \"Failed to unregister receiver: ${it.message}\") }\n        Log.i(TAG, \"SuspendModule uninstalled\")\n    }\n}\n"
  },
  {
    "path": "android/app/src/main/kotlin/com/appshub/bettbox/plugins/AppPlugin.kt",
    "content": "package com.appshub.bettbox.plugins\n\nimport android.Manifest\nimport android.app.Activity\nimport android.app.ActivityManager\nimport android.content.Context\nimport android.content.Intent\nimport android.content.pm.ApplicationInfo\nimport android.content.pm.ComponentInfo\nimport android.content.pm.PackageManager\nimport android.net.VpnService\nimport android.os.Build\nimport android.provider.Settings\nimport android.util.Base64\nimport android.widget.Toast\nimport androidx.core.app.ActivityCompat\nimport androidx.core.content.ContextCompat\nimport androidx.core.content.FileProvider\nimport androidx.core.content.getSystemService\nimport androidx.core.content.pm.ShortcutInfoCompat\nimport androidx.core.content.pm.ShortcutManagerCompat\nimport androidx.core.graphics.drawable.IconCompat\nimport com.appshub.bettbox.BettboxApplication\nimport com.appshub.bettbox.GlobalState\nimport com.appshub.bettbox.R\nimport com.appshub.bettbox.extensions.awaitResult\nimport com.appshub.bettbox.extensions.getActionIntent\nimport com.appshub.bettbox.models.Package\nimport io.flutter.embedding.android.FlutterActivity\nimport io.flutter.embedding.engine.plugins.FlutterPlugin\nimport io.flutter.embedding.engine.plugins.activity.ActivityAware\nimport io.flutter.embedding.engine.plugins.activity.ActivityPluginBinding\nimport io.flutter.plugin.common.MethodCall\nimport io.flutter.plugin.common.MethodChannel\nimport io.flutter.plugin.common.MethodChannel.Result\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.SupervisorJob\nimport kotlinx.coroutines.cancel\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\nimport java.io.File\nimport java.lang.ref.WeakReference\nimport java.security.cert.CertificateFactory\nimport java.security.cert.X509Certificate\nimport java.util.concurrent.ConcurrentHashMap\nimport android.content.res.Configuration\nimport android.net.Uri\nimport android.graphics.Bitmap\nimport android.graphics.Canvas\nimport android.graphics.drawable.Drawable\n\nclass AppPlugin : FlutterPlugin, MethodChannel.MethodCallHandler, ActivityAware {\n\n    private var activityRef: WeakReference<Activity>? = null\n    private var cachedTaskId: Int? = null\n    private lateinit var channel: MethodChannel\n    private lateinit var scope: CoroutineScope\n    private var vpnCallBack: (() -> Unit)? = null\n    private val packages = mutableListOf<Package>()\n    private val chinaPackageCache = ConcurrentHashMap<String, Boolean>()\n\n    private val iconCacheDir by lazy {\n        File(BettboxApplication.getAppContext().cacheDir, \"app_icons\").apply {\n            if (!exists()) mkdirs()\n        }\n    }\n\n    companion object {\n        private const val ICON_SIZE_DP = 48\n        private const val VPN_PERMISSION_REQUEST_CODE = 1001\n        private const val NOTIFICATION_PERMISSION_REQUEST_CODE = 1002\n        private const val CACHE_MAX_FILES = 500\n        private const val PNG_MAGIC_SIZE = 8\n\n        private val SKIP_PREFIX_LIST = listOf(\n            \"com.google\", \"com.android.chrome\", \"com.android.vending\", \"com.facebook\",\n            \"com.instagram\", \"com.whatsapp\", \"com.twitter\", \"com.linkedin\", \"com.snapchat\",\n            \"com.amazon\", \"com.microsoft\", \"com.apple\", \"com.dropbox\", \"com.mozilla\",\n            \"com.brave\", \"com.duckduckgo\", \"com.vivaldi\", \"com.kiwibrowser\",\n            \"org.torproject.torbrowser\", \"com.opera.browser\", \"com.lemon.browser\",\n            \"net.waterfox\", \"ch.protonmail\", \"org.thoughtcrime.securesms\", \"org.telegram\",\n            \"com.surfshark\", \"com.netflix\", \"com.spotify\", \"tv.twitch\", \"com.hulu\",\n            \"com.disney\", \"com.hbo\", \"com.primevideo\", \"com.zhiliaoapp.musically\",\n            \"com.nytimes\", \"bbc.mobile\", \"com.wsj\", \"com.bloomberg\", \"com.medium\",\n            \"com.quora\", \"com.github\", \"io.github\", \"com.slack\", \"com.notion\", \"us.zoom\",\n            \"com.discord\", \"com.reddit\", \"com.pinterest\", \"com.tumblr\", \"jp.naver.line\",\n            \"com.skype\", \"com.box\", \"org.wikipedia\", \"com.gitlab\", \"com.openai\",\n            \"com.valvesoftware\", \"com.roblox\", \"com.ea.gp\", \"com.ubisoft\",\n            \"com.sogou.activity.src\", \"com.qihoo.browser\", \"com.qihoo.haosou\",\n            \"com.liebao\", \"com.mx.browser\", \"com.browser2345\", \"com.ijinshan.browser\",\n            \"com.quark.browser\", \"com.ylmf.androidclient\", \"mark.via\", \"com.xbrowser.play\",\n            \"com.mycompany.app.soulbrowser\", \"com.hshentong.alook\", \"info.bmmk.mbrowser\",\n            \"com.rainsee.browser\", \"com.liuzh.browser\", \"com.yuzhe.browser\",\n            \"org.easyweb.browser\", \"any.browser\", \"us.spotco.fennec_dos\",\n            \"app.grapheneos.vanadium\", \"org.ironfoxoss\", \"com.samsung.android.app.sbrowser\",\n            \"com.mi.global.browser\", \"com.android.browser\", \"com.huawei.browser\",\n            \"com.hihonor.browser\", \"com.heytap.browser\", \"com.coloros.browser\",\n            \"com.oppo.browser\", \"com.vivo.browser\", \"com.bbk.browser\", \"com.meizu.browser\",\n            \"com.meizu.mbrowser\", \"com.lenovo.browser\", \"com.zte.browser\", \"com.gionee.browser\"\n        )\n\n        private val CHINA_APP_PREFIX_LIST = listOf(\n            \"com.tencent\", \"com.alibaba\", \"com.ali\", \"com.alipay\", \"com.taobao\", \"com.baidu\",\n            \"com.iqiyi\", \"com.bytedance\", \"com.ss.android\", \"com.kuaishou\", \"com.smile.gifmaker\",\n            \"com.xunmeng\", \"com.pinduoduo\", \"com.sankuai\", \"com.meituan\", \"com.jingdong\",\n            \"com.jd\", \"tv.danmaku\", \"com.sina\", \"com.weibo\", \"com.sohu\", \"com.netease\",\n            \"com.zhihu\", \"com.xingin\", \"com.huawei\", \"com.xiaomi\", \"com.miui\", \"com.oppo\",\n            \"com.coloros\", \"com.oplus\", \"andes.oplus\", \"com.vivo\", \"com.bbk\", \"com.iqoo\",\n            \"com.meizu\", \"com.flyme\", \"com.gionee\", \"cn.nubia\", \"com.zte\", \"com.lenovo\",\n            \"com.oneplus\", \"com.qihoo\", \"com.360\", \"com.ijiami\", \"com.bangcle\", \"com.secneo\",\n            \"com.kiwisec\", \"com.stub\", \"com.wrapper\", \"cn.securitystack\", \"com.mogosec\",\n            \"com.secoen\", \"com.secshell\", \"com.umeng\", \"com.igexin\", \"cn.jpush\", \"cn.jiguang\",\n            \"com.bugly\", \"com.mob\", \"cn.wps\", \"com.kingsoft\", \"com.xunlei\", \"com.unionpay\",\n            \"com.cainiao\", \"com.sf\", \"com.sdu\", \"com.xiaojukeji\", \"com.autonavi\", \"com.amap\",\n            \"com.chinamobile\", \"com.chinaunicom\", \"com.chinatelecom\", \"com.icbc\", \"com.ccb\",\n            \"com.cmbchina\", \"com.mx\", \"com.qq\", \"app.eleven.com.fastfiletransfer\",\n            \"org.localsend.localsend_app\"\n        )\n\n        private val CHINA_APP_REGEX by lazy {\n            (\"(\" + CHINA_APP_PREFIX_LIST.joinToString(\"|\").replace(\".\", \"\\\\.\") + \").*\").toRegex()\n        }\n    }\n\n    private var isBlockNotification = false\n    private var isActivityAttached = false\n\n    override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {\n        scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)\n        channel = MethodChannel(flutterPluginBinding.binaryMessenger, \"app\")\n        channel.setMethodCallHandler(this)\n        scope.launch(Dispatchers.IO) { cleanIconCache() }\n    }\n\n    private fun initShortcuts(label: String) {\n        val iconRes = if (isSystemInDarkMode()) R.mipmap.ic_launcher_round else R.mipmap.ic_launcher_round_light\n        val shortcut = ShortcutInfoCompat.Builder(BettboxApplication.getAppContext(), \"toggle\")\n            .setShortLabel(label)\n            .setIcon(IconCompat.createWithResource(BettboxApplication.getAppContext(), iconRes))\n            .setIntent(BettboxApplication.getAppContext().getActionIntent(\"CHANGE\"))\n            .build()\n        ShortcutManagerCompat.setDynamicShortcuts(BettboxApplication.getAppContext(), listOf(shortcut))\n    }\n\n    private fun isSystemInDarkMode(): Boolean {\n        val nightModeFlags = BettboxApplication.getAppContext().resources.configuration.uiMode and\n            Configuration.UI_MODE_NIGHT_MASK\n        return nightModeFlags == Configuration.UI_MODE_NIGHT_YES\n    }\n\n    private fun isAndroidTV(): Boolean {\n        val uiMode = BettboxApplication.getAppContext().resources.configuration.uiMode\n        return (uiMode and Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION\n    }\n\n    override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {\n        channel.setMethodCallHandler(null)\n        scope.cancel()\n    }\n\n    private fun tip(message: String?) {\n        if (GlobalState.flutterEngine == null) {\n            Toast.makeText(BettboxApplication.getAppContext(), message, Toast.LENGTH_LONG).show()\n        }\n    }\n\n    override fun onMethodCall(call: MethodCall, result: Result) {\n        when (call.method) {\n            \"moveTaskToBack\" -> {\n                activityRef?.get()?.moveTaskToBack(true)\n                result.success(true)\n            }\n            \"updateExcludeFromRecents\" -> {\n                updateExcludeFromRecents(call.argument<Boolean>(\"value\"))\n                result.success(true)\n            }\n            \"initShortcuts\" -> {\n                initShortcuts(call.arguments as String)\n                result.success(true)\n            }\n            \"getPackages\" -> scope.launch {\n                val forceRefresh = call.argument<Boolean>(\"forceRefresh\") ?: false\n                runCatching { result.success(getPackagesToList(forceRefresh)) }\n                    .onFailure { result.error(\"GET_PACKAGES_FAILED\", it.message, null) }\n            }\n            \"getChinaPackageNames\" -> scope.launch {\n                runCatching { result.success(getChinaPackageNamesList()) }\n                    .onFailure { result.error(\"GET_CHINA_PACKAGES_FAILED\", it.message, null) }\n            }\n            \"getPackageIcon\" -> scope.launch {\n                val packageName = call.argument<String>(\"packageName\")\n                val forceRefresh = call.argument<Boolean>(\"forceRefresh\") ?: false\n                val icon = runCatching {\n                    packageName?.let { \n                        getPackageIconBytes(it, forceRefresh) \n                    } ?: getDefaultIconBytes()\n                }.getOrNull() ?: getDefaultIconBytes()\n                result.success(icon)\n            }\n            \"tip\" -> {\n                tip(call.argument<String>(\"message\"))\n                result.success(true)\n            }\n            \"openFile\" -> {\n                openFile(call.argument<String>(\"path\")!!)\n                result.success(true)\n            }\n            \"getSelfLastUpdateTime\" -> {\n                result.success(getSelfLastUpdateTime())\n            }\n            \"isIgnoringBatteryOptimizations\" -> {\n                result.success(isIgnoringBatteryOptimizations())\n            }\n            \"requestIgnoreBatteryOptimizations\" -> {\n                requestIgnoreBatteryOptimizations()\n                result.success(true)\n            }\n            \"setLauncherIcon\" -> {\n                setLauncherIcon(call.argument<Boolean>(\"useLightIcon\") ?: false)\n                result.success(true)\n            }\n            \"hasPackageListPermission\" -> {\n                result.success(hasPackageListPermission())\n            }\n            \"requestPackageListPermission\" -> {\n                requestPackageListPermission()\n                result.success(true)\n            }\n            \"hasCameraPermission\" -> {\n                result.success(hasCameraPermission())\n            }\n            \"openAppSettings\" -> {\n                openAppSettings()\n                result.success(true)\n            }\n            \"isAndroidTV\" -> {\n                result.success(isAndroidTV())\n            }\n            else -> result.notImplemented()\n        }\n    }\n\n    private fun openFile(path: String) {\n        val context = BettboxApplication.getAppContext()\n        val file = File(path)\n        val uri = FileProvider.getUriForFile(\n            context,\n            \"${context.packageName}.fileProvider\",\n            file\n        )\n        val intent = Intent(Intent.ACTION_VIEW).apply {\n            setDataAndType(uri, \"text/plain\")\n            addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)\n            addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)\n        }\n        runCatching { context.startActivity(intent) }\n    }\n\n    private fun updateExcludeFromRecents(value: Boolean?) {\n        val am = BettboxApplication.getAppContext().getSystemService(Context.ACTIVITY_SERVICE) as? ActivityManager\n        val task = am?.appTasks?.firstOrNull { task ->\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n                task.taskInfo.taskId == activityRef?.get()?.taskId\n            } else {\n                @Suppress(\"DEPRECATION\")\n                task.taskInfo.id == activityRef?.get()?.taskId\n            }\n        }\n\n        when (value) {\n            true -> task?.setExcludeFromRecents(value)\n            false -> task?.setExcludeFromRecents(value)\n            null -> task?.setExcludeFromRecents(false)\n        }\n    }\n\n    private fun getIconSizePx(): Int {\n        val density = BettboxApplication.getAppContext().resources.displayMetrics.density\n        return (ICON_SIZE_DP * density).toInt().coerceAtLeast(1)\n    }\n\n    private fun drawableToPngBytes(drawable: Drawable, sizePx: Int): ByteArray {\n        val bitmap = Bitmap.createBitmap(sizePx, sizePx, Bitmap.Config.ARGB_8888)\n        val canvas = Canvas(bitmap)\n        drawable.setBounds(0, 0, sizePx, sizePx)\n        drawable.draw(canvas)\n        return java.io.ByteArrayOutputStream().use { outputStream ->\n            bitmap.compress(Bitmap.CompressFormat.PNG, 80, outputStream)\n            bitmap.recycle()\n            outputStream.toByteArray()\n        }\n    }\n\n    private fun getDefaultIconBytes(): ByteArray? = runCatching {\n        drawableToPngBytes(BettboxApplication.getAppContext().packageManager.defaultActivityIcon, getIconSizePx())\n    }.getOrNull()\n\n    private fun isPngBytes(bytes: ByteArray): Boolean {\n        if (bytes.size < PNG_MAGIC_SIZE) return false\n        val magic = byteArrayOf(0x89.toByte(), 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A)\n        return bytes.take(PNG_MAGIC_SIZE).toByteArray().contentEquals(magic)\n    }\n\n    private suspend fun getPackageIconBytes(packageName: String, forceRefresh: Boolean = false): ByteArray? =\n        withContext(Dispatchers.IO) {\n            val pm = BettboxApplication.getAppContext().packageManager ?: return@withContext null\n            runCatching {\n                val packageInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {\n                    pm.getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(0))\n                } else {\n                    pm.getPackageInfo(packageName, 0)\n                }\n                val lastUpdateTime = packageInfo?.lastUpdateTime ?: 0L\n                val cacheKey = \"${packageName}_${lastUpdateTime}\"\n                val cacheFile = File(iconCacheDir, cacheKey)\n\n                if (forceRefresh && cacheFile.exists() && (System.currentTimeMillis() - lastUpdateTime) < 24 * 60 * 60 * 1000) {\n                    cacheFile.delete()\n                }\n\n                if (cacheFile.exists() && cacheFile.length() > 0) {\n                    val cachedBytes = cacheFile.readBytes()\n                    if (isPngBytes(cachedBytes)) return@withContext cachedBytes\n                    cacheFile.delete()\n                }\n\n                iconCacheDir.listFiles()?.forEach { if (it.name.startsWith(\"${packageName}_\") && it.name != cacheKey) it.delete() }\n\n                pm.getApplicationIcon(packageName)?.let { drawable ->\n                    val bytes = drawableToPngBytes(drawable, getIconSizePx())\n                    runCatching { cacheFile.writeBytes(bytes) }\n                    return@withContext bytes\n                }\n            }\n            null\n        }\n\n    private suspend fun getPackages(forceRefresh: Boolean = false): List<Package> = withContext(Dispatchers.IO) {\n        if (forceRefresh) packages.clear()\n        if (packages.isNotEmpty()) return@withContext packages\n        val pm = BettboxApplication.getAppContext().packageManager ?: return@withContext emptyList()\n        val selfPackageName = BettboxApplication.getAppContext().packageName\n\n        packages.addAll(pm.getInstalledApplications(PackageManager.GET_META_DATA).mapNotNull { appInfo ->\n            val packageName = appInfo.packageName ?: return@mapNotNull null\n            if (packageName == selfPackageName) return@mapNotNull null\n\n            val label = runCatching { appInfo.loadLabel(pm).toString() }.getOrDefault(packageName)\n            val system = (appInfo.flags and ApplicationInfo.FLAG_SYSTEM) != 0\n            val internet = runCatching {\n                pm.checkPermission(Manifest.permission.INTERNET, packageName) == PackageManager.PERMISSION_GRANTED\n            }.getOrDefault(false)\n            val lastUpdateTime = appInfo.sourceDir?.let { File(it).lastModified() } ?: 0L\n\n            Package(packageName, label, system, internet, lastUpdateTime)\n        })\n        packages\n    }\n\n    private suspend fun getPackagesToList(forceRefresh: Boolean = false): List<Map<String, Any>> =\n        getPackages(forceRefresh).map { mapOf(\"packageName\" to it.packageName, \"label\" to it.label, \"system\" to it.system, \"internet\" to it.internet, \"lastUpdateTime\" to it.lastUpdateTime) }\n\n    private suspend fun getChinaPackageNamesList(): List<String> =\n        getPackages().map { it.packageName }.filter { isChinaPackage(it) }\n\n    private fun cleanIconCache() {\n        runCatching {\n            iconCacheDir.listFiles()?.takeIf { it.size > CACHE_MAX_FILES }?.let { files ->\n                files.sortedBy { it.lastModified() }.take(files.size - CACHE_MAX_FILES).forEach { it.delete() }\n            }\n        }\n    }\n\n    fun requestVpnPermission(callBack: () -> Unit) {\n        vpnCallBack = callBack\n        val intent = VpnService.prepare(BettboxApplication.getAppContext())\n        if (intent != null) {\n            activityRef?.get()?.startActivityForResult(intent, VPN_PERMISSION_REQUEST_CODE)\n            return\n        }\n        vpnCallBack?.invoke()\n    }\n\n    fun requestNotificationsPermission() {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) return\n        if (isBlockNotification || activityRef?.get() == null) return\n        if (ContextCompat.checkSelfPermission(BettboxApplication.getAppContext(), Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED) return\n        activityRef?.get()?.let {\n            ActivityCompat.requestPermissions(it, arrayOf(Manifest.permission.POST_NOTIFICATIONS), NOTIFICATION_PERMISSION_REQUEST_CODE)\n        }\n    }\n\n    suspend fun getText(text: String): String? = withContext(Dispatchers.Default) {\n        channel.awaitResult<String>(\"getText\", text)\n    }\n\n    private fun isChinaPackage(packageName: String): Boolean =\n        chinaPackageCache.getOrPut(packageName) { isChinaPackageInternal(packageName) }\n\n    private fun isChinaPackageInternal(packageName: String): Boolean {\n        val context = BettboxApplication.getAppContext()\n        val pm = context.packageManager ?: return false\n        if (SKIP_PREFIX_LIST.any { packageName == it || packageName.startsWith(\"$it.\") }) return false\n        if (packageName.matches(CHINA_APP_REGEX)) return true\n        if (isChinaCertificate(packageName, pm)) return true\n\n        val flags = PackageManager.GET_ACTIVITIES or PackageManager.GET_SERVICES or PackageManager.GET_RECEIVERS or PackageManager.GET_PROVIDERS\n        runCatching {\n            val packageInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {\n                pm.getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(flags.toLong()))\n            } else {\n                pm.getPackageInfo(packageName, flags)\n            }\n            val components = mutableListOf<ComponentInfo>().apply {\n                packageInfo.services?.let { addAll(it) }\n                packageInfo.activities?.let { addAll(it) }\n                packageInfo.receivers?.let { addAll(it) }\n                packageInfo.providers?.let { addAll(it) }\n            }\n            if (components.any { it.name.matches(CHINA_APP_REGEX) }) return true\n        }\n        return false\n    }\n\n    private fun isChinaCertificate(packageName: String, pm: PackageManager): Boolean = runCatching {\n        val packageInfo = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n            pm.getPackageInfo(packageName, PackageManager.GET_SIGNING_CERTIFICATES)\n        } else {\n            @Suppress(\"DEPRECATION\")\n            pm.getPackageInfo(packageName, PackageManager.GET_SIGNATURES)\n        }\n        val signatures = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {\n            packageInfo.signingInfo?.apkContentsSigners\n        } else {\n            @Suppress(\"DEPRECATION\")\n            packageInfo.signatures\n        }\n        signatures?.any { signature ->\n            val cert = CertificateFactory.getInstance(\"X.509\")\n                .generateCertificate(java.io.ByteArrayInputStream(signature.toByteArray()))\n            cert is X509Certificate && (cert.subjectDN.name.contains(\"C=CN\", ignoreCase = true) ||\n                cert.subjectDN.name.contains(\"C=86\", ignoreCase = true))\n        } == true\n    }.getOrDefault(false)\n\n    override fun onAttachedToActivity(binding: ActivityPluginBinding) {\n        activityRef = WeakReference(binding.activity)\n        if (!isActivityAttached) {\n            isActivityAttached = true\n            binding.addActivityResultListener(::onActivityResult)\n            binding.addRequestPermissionsResultListener(::onRequestPermissionsResultListener)\n        }\n    }\n\n    override fun onDetachedFromActivityForConfigChanges() {\n        activityRef = null\n    }\n    override fun onReattachedToActivityForConfigChanges(binding: ActivityPluginBinding) {\n        activityRef = WeakReference(binding.activity)\n    }\n    override fun onDetachedFromActivity() {\n        channel.invokeMethod(\"exit\", null)\n        activityRef = null\n        cachedTaskId = null\n        isActivityAttached = false\n    }\n\n    private fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?): Boolean {\n        if (!isActivityAttached) return false\n        if (requestCode == VPN_PERMISSION_REQUEST_CODE && resultCode == FlutterActivity.RESULT_OK) {\n            GlobalState.initServiceEngine()\n            vpnCallBack?.invoke()\n        }\n        return true\n    }\n\n    private fun onRequestPermissionsResultListener(requestCode: Int, permissions: Array<String>, grantResults: IntArray): Boolean {\n        if (!isActivityAttached) return false\n        if (requestCode == NOTIFICATION_PERMISSION_REQUEST_CODE) isBlockNotification = true\n        return true\n    }\n\n    private fun isIgnoringBatteryOptimizations(): Boolean =\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {\n            val powerManager = BettboxApplication.getAppContext().getSystemService(android.content.Context.POWER_SERVICE) as? android.os.PowerManager\n            powerManager?.isIgnoringBatteryOptimizations(BettboxApplication.getAppContext().packageName) ?: false\n        } else true\n\n    private fun requestIgnoreBatteryOptimizations() {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.M) return\n        val intent = Intent(Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS).apply {\n            data = Uri.parse(\"package:${BettboxApplication.getAppContext().packageName}\")\n            addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)\n        }\n        runCatching { BettboxApplication.getAppContext().startActivity(intent) }\n            .onFailure {\n                runCatching {\n                    BettboxApplication.getAppContext().startActivity(Intent(Settings.ACTION_IGNORE_BATTERY_OPTIMIZATION_SETTINGS).apply {\n                        addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)\n                    })\n                }\n            }\n    }\n\n    private fun setLauncherIcon(useLightIcon: Boolean) {\n        val context = BettboxApplication.getAppContext()\n        val pm = context.packageManager\n        val packageName = context.packageName\n        val defaultComponent = android.content.ComponentName(packageName, \"com.appshub.bettbox.MainActivity\")\n        val lightComponent = android.content.ComponentName(packageName, \"com.appshub.bettbox.MainActivityLight\")\n\n        if (useLightIcon) {\n            pm.setComponentEnabledSetting(lightComponent, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP)\n            pm.setComponentEnabledSetting(defaultComponent, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP)\n        } else {\n            pm.setComponentEnabledSetting(defaultComponent, PackageManager.COMPONENT_ENABLED_STATE_ENABLED, PackageManager.DONT_KILL_APP)\n            pm.setComponentEnabledSetting(lightComponent, PackageManager.COMPONENT_ENABLED_STATE_DISABLED, PackageManager.DONT_KILL_APP)\n        }\n        VpnPlugin.updateNotificationIcon()\n    }\n\n    private fun hasPackageListPermission(): Boolean {\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) return true\n        val context = BettboxApplication.getAppContext()\n        val pm = context.packageManager\n        return arrayOf(\"com.android.settings\", \"com.android.systemui\").any { pkg ->\n            runCatching {\n                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {\n                    pm.getPackageInfo(pkg, PackageManager.PackageInfoFlags.of(0))\n                } else {\n                    pm.getPackageInfo(pkg, 0)\n                }\n                true\n            }.getOrDefault(false)\n        }\n    }\n\n    private fun requestPackageListPermission() {\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) openAppSettings()\n    }\n\n    private fun hasCameraPermission(): Boolean =\n        ContextCompat.checkSelfPermission(BettboxApplication.getAppContext(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED\n\n    private fun openAppSettings() {\n        val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS).apply {\n            data = Uri.parse(\"package:${BettboxApplication.getAppContext().packageName}\")\n        }\n        activityRef?.get()?.startActivity(intent) ?: run {\n            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)\n            BettboxApplication.getAppContext().startActivity(intent)\n        }\n    }\n\n    private fun getSelfLastUpdateTime(): Long {\n        val context = BettboxApplication.getAppContext()\n        val pm = context.packageManager\n        return runCatching {\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {\n                pm?.getPackageInfo(context.packageName, PackageManager.PackageInfoFlags.of(0))\n            } else {\n                pm?.getPackageInfo(context.packageName, 0)\n            }?.lastUpdateTime\n        }.getOrDefault(0L) ?: 0L\n    }\n}\n"
  },
  {
    "path": "android/app/src/main/kotlin/com/appshub/bettbox/plugins/ServicePlugin.kt",
    "content": "package com.appshub.bettbox.plugins\n\nimport android.os.Handler\nimport android.os.Looper\nimport android.util.Log\nimport com.appshub.bettbox.GlobalState\nimport com.appshub.bettbox.RunState\nimport com.appshub.bettbox.models.VpnOptions\nimport com.google.gson.Gson\nimport io.flutter.embedding.engine.plugins.FlutterPlugin\nimport io.flutter.plugin.common.MethodCall\nimport io.flutter.plugin.common.MethodChannel\nimport java.util.concurrent.CopyOnWriteArrayList\n\nclass ServicePlugin : FlutterPlugin, MethodChannel.MethodCallHandler {\n\n    private lateinit var channel: MethodChannel\n\n    companion object {\n        private val activeChannels = CopyOnWriteArrayList<MethodChannel>()\n        private val mainHandler = Handler(Looper.getMainLooper())\n        private val gson = Gson()\n        private const val TAG = \"ServicePlugin\"\n\n        private fun notify(method: String) {\n            mainHandler.post {\n                activeChannels.forEach { ch ->\n                    runCatching { ch.invokeMethod(method, null) }\n                        .onFailure { Log.e(TAG, \"$method notify error: ${it.message}\") }\n                }\n            }\n        }\n\n        fun notifyNetworkChanged() = notify(\"networkChanged\")\n        fun notifyQuickResponse() = notify(\"quickResponse\")\n        fun notifyVpnStartFailed() = notify(\"vpnStartFailed\")\n        fun notifyRunStateChanged(state: RunState) {\n            mainHandler.post {\n                activeChannels.forEach { ch ->\n                    runCatching { ch.invokeMethod(\"runStateChanged\", state.name) }\n                        .onFailure { Log.e(TAG, \"runStateChanged notify error: ${it.message}\") }\n                }\n            }\n        }\n    }\n\n    override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {\n        channel = MethodChannel(binding.binaryMessenger, \"service\").apply {\n            setMethodCallHandler(this@ServicePlugin)\n        }\n        activeChannels.add(channel)\n    }\n\n    override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {\n        channel.setMethodCallHandler(null)\n        activeChannels.remove(channel)\n    }\n\n    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {\n        when (call.method) {\n            \"startVpn\" -> handleStartVpn(call, result)\n            \"stopVpn\" -> {\n                VpnPlugin.handleStop(force = true)\n                result.success(true)\n            }\n            \"smartStop\" -> {\n                VpnPlugin.handleSmartStop()\n                result.success(true)\n            }\n            \"smartResume\" -> {\n                val data = call.argument<String>(\"data\")\n                val options = gson.fromJson(data, VpnOptions::class.java)\n                VpnPlugin.handleSmartResume(options)\n                result.success(true)\n            }\n            \"setSmartStopped\" -> {\n                GlobalState.isSmartStopped = call.argument<Boolean>(\"value\") ?: false\n                result.success(true)\n            }\n            \"isSmartStopped\" -> result.success(GlobalState.isSmartStopped)\n            \"getLocalIpAddresses\" -> result.success(VpnPlugin.getLocalIpAddresses())\n            \"setQuickResponse\" -> {\n                VpnPlugin.setQuickResponse(call.argument<Boolean>(\"enabled\") ?: false)\n                result.success(true)\n            }\n            \"init\" -> {\n                GlobalState.getCurrentAppPlugin()?.requestNotificationsPermission()\n                GlobalState.initServiceEngine()\n                result.success(true)\n            }\n            \"isServiceEngineRunning\" -> result.success(GlobalState.isServiceEngineRunning())\n            \"status\" -> result.success(GlobalState.currentRunState == RunState.START)\n            \"reconnectIpc\" -> {\n                GlobalState.reconnectIpc()\n                result.success(true)\n            }\n            \"destroy\" -> {\n                GlobalState.destroyServiceEngine()\n                result.success(true)\n            }\n            else -> result.notImplemented()\n        }\n    }\n\n    private fun handleStartVpn(call: MethodCall, result: MethodChannel.Result) {\n        val data = call.argument<String>(\"data\")\n        if (data.isNullOrBlank() || data == \"null\") {\n            result.error(\"INVALID_ARGUMENT\", \"options data is null\", null)\n            return\n        }\n        runCatching { gson.fromJson(data, VpnOptions::class.java) }\n            .onSuccess { options ->\n                VpnPlugin.handleStart(options)\n                result.success(true)\n            }\n            .onFailure { result.error(\"PARSE_ERROR\", it.message, null) }\n    }\n}\n"
  },
  {
    "path": "android/app/src/main/kotlin/com/appshub/bettbox/plugins/TilePlugin.kt",
    "content": "package com.appshub.bettbox.plugins\n\nimport android.os.Handler\nimport android.os.Looper\nimport android.util.Log\nimport io.flutter.embedding.engine.plugins.FlutterPlugin\nimport io.flutter.plugin.common.MethodCall\nimport io.flutter.plugin.common.MethodChannel\n\nclass TilePlugin : FlutterPlugin, MethodChannel.MethodCallHandler {\n\n    private var channel: MethodChannel? = null\n    private val mainHandler = Handler(Looper.getMainLooper())\n\n    companion object {\n        private const val TAG = \"TilePlugin\"\n    }\n\n    override fun onAttachedToEngine(binding: FlutterPlugin.FlutterPluginBinding) {\n        channel = MethodChannel(binding.binaryMessenger, \"tile\").apply {\n            setMethodCallHandler(this@TilePlugin)\n        }\n    }\n\n    override fun onDetachedFromEngine(binding: FlutterPlugin.FlutterPluginBinding) {\n        runCatching { channel?.invokeMethod(\"detached\", null) }\n            .onFailure { Log.e(TAG, \"Failed to invoke detached: ${it.message}\") }\n        channel?.setMethodCallHandler(null)\n        channel = null\n    }\n\n    fun handleStart() = safeInvokeMethod(\"start\")\n    fun handleStop() = safeInvokeMethod(\"stop\")\n    fun handleReconnectIpc() = safeInvokeMethod(\"reconnectIpc\")\n\n    private fun safeInvokeMethod(method: String) {\n        val ch = channel ?: return\n        if (Looper.myLooper() == Looper.getMainLooper()) {\n            runCatching { ch.invokeMethod(method, null) }\n                .onFailure { Log.e(TAG, \"Failed to invoke $method: ${it.message}\") }\n        } else {\n            mainHandler.post {\n                runCatching { ch.invokeMethod(method, null) }\n                    .onFailure { Log.e(TAG, \"Failed to invoke $method: ${it.message}\") }\n            }\n        }\n    }\n\n    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) = result.notImplemented()\n}\n"
  },
  {
    "path": "android/app/src/main/kotlin/com/appshub/bettbox/plugins/VpnPlugin.kt",
    "content": "package com.appshub.bettbox.plugins\n\nimport android.content.ComponentName\nimport android.content.Context\nimport android.content.Intent\nimport android.content.ServiceConnection\nimport android.net.ConnectivityManager\nimport android.net.Network\nimport android.net.NetworkCapabilities\nimport android.net.NetworkRequest\nimport android.os.Build\nimport android.os.IBinder\nimport androidx.core.content.getSystemService\nimport com.appshub.bettbox.BettboxApplication\nimport com.appshub.bettbox.GlobalState\nimport com.appshub.bettbox.RunState\nimport com.appshub.bettbox.core.Core\nimport com.appshub.bettbox.extensions.awaitResult\nimport com.appshub.bettbox.extensions.resolveDns\nimport com.appshub.bettbox.models.StartForegroundParams\nimport com.appshub.bettbox.models.VpnOptions\nimport com.appshub.bettbox.modules.SuspendModule\nimport com.appshub.bettbox.services.BaseServiceInterface\nimport com.appshub.bettbox.services.BettboxService\nimport com.appshub.bettbox.services.BettboxVpnService\nimport com.google.gson.Gson\nimport io.flutter.embedding.engine.plugins.FlutterPlugin\nimport io.flutter.plugin.common.BinaryMessenger\nimport io.flutter.plugin.common.MethodCall\nimport io.flutter.plugin.common.MethodChannel\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.SupervisorJob\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.Job\nimport java.util.Collections\nimport kotlinx.coroutines.delay\nimport kotlinx.coroutines.isActive\nimport kotlinx.coroutines.launch\nimport kotlinx.coroutines.withContext\nimport kotlinx.coroutines.withTimeoutOrNull\nimport java.net.InetSocketAddress\nimport java.util.concurrent.CopyOnWriteArrayList\nimport java.util.concurrent.ConcurrentHashMap\nimport java.util.concurrent.atomic.AtomicBoolean\nimport kotlin.concurrent.withLock\n\ndata object VpnPlugin : FlutterPlugin, MethodChannel.MethodCallHandler {\n    private var bettBoxService: BaseServiceInterface? = null\n    private var options: VpnOptions? = null\n\n    private var isBind = false\n    private val isBinding = AtomicBoolean(false)\n\n    private var job = SupervisorJob()\n    private var scope = CoroutineScope(Dispatchers.Default + job as kotlin.coroutines.CoroutineContext)\n    private var lastStartForegroundParams: StartForegroundParams? = null\n    private val uidPageNameMap = ConcurrentHashMap<Int, String>()\n    private var suspendModule: SuspendModule? = null\n\n    private var quickResponseEnabled = false\n    private var disconnectCount = 0\n    private var disconnectWindowStart = 0L\n    private val disconnectWindowMs = 5000L\n    private val maxDisconnectsInWindow = 3\n    private var lastNetworkType: Int? = null\n    private var lastDns = \"\"\n\n    val networks: MutableSet<Network> = Collections.newSetFromMap(ConcurrentHashMap())\n\n    private val connectivity by lazy {\n        BettboxApplication.getAppContext().getSystemService<ConnectivityManager>()\n    }\n\n    private var bindTimeoutJob: Job? = null\n    private val attachedMessengers = mutableSetOf<BinaryMessenger>()\n    private val channelMap = ConcurrentHashMap<BinaryMessenger, MethodChannel>()\n    private val activeChannels = CopyOnWriteArrayList<MethodChannel>()\n    private val networkCallbackRegistered = AtomicBoolean(false)\n\n    private val connection = object : ServiceConnection {\n        override fun onServiceConnected(className: ComponentName, service: IBinder) {\n            bindTimeoutJob?.cancel()\n            bindTimeoutJob = null\n            isBind = true\n            isBinding.set(false)\n            bettBoxService = when (service) {\n                is BettboxVpnService.LocalBinder -> service.getService()\n                is BettboxService.LocalBinder -> service.getService()\n                else -> throw Exception(\"invalid binder\")\n            }\n            handleStartService()\n        }\n\n        override fun onServiceDisconnected(arg: ComponentName) {\n            isBind = false\n            isBinding.set(false)\n            bettBoxService = null\n            if (GlobalState.currentRunState == RunState.START) {\n                android.util.Log.w(\"VpnPlugin\", \"Service unexpectedly disconnected while running, syncing state\")\n                GlobalState.updateRunState(RunState.STOP)\n                ServicePlugin.notifyVpnStartFailed()\n                ServicePlugin.notifyRunStateChanged(RunState.STOP)\n            }\n        }\n    }\n\n    override fun onAttachedToEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {\n        val isFirstAttach = attachedMessengers.isEmpty()\n        attachedMessengers.add(flutterPluginBinding.binaryMessenger)\n\n        if (job.isCancelled) {\n            job = SupervisorJob()\n            scope = CoroutineScope(Dispatchers.Default + job as kotlin.coroutines.CoroutineContext)\n        }\n\n        val channel = MethodChannel(flutterPluginBinding.binaryMessenger, \"vpn\")\n        channel.setMethodCallHandler(this)\n        channelMap[flutterPluginBinding.binaryMessenger] = channel\n        activeChannels.add(channel)\n\n        if (isFirstAttach) {\n            scope.launch { registerNetworkCallback() }\n        }\n\n        if (GlobalState.currentRunState == RunState.START && bettBoxService == null) {\n            android.util.Log.d(\"VpnPlugin\", \"VPN is running but service connection lost, rebinding...\")\n            options?.let { bindService() }\n        }\n    }\n\n    override fun onDetachedFromEngine(flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {\n        attachedMessengers.remove(flutterPluginBinding.binaryMessenger)\n        channelMap.remove(flutterPluginBinding.binaryMessenger)?.let { channel ->\n            channel.setMethodCallHandler(null)\n            activeChannels.remove(channel)\n        }\n\n        if (attachedMessengers.isEmpty()) {\n            unRegisterNetworkCallback()\n            job.cancel()\n        }\n    }\n\n    override fun onMethodCall(call: MethodCall, result: MethodChannel.Result) {\n        when (call.method) {\n            \"start\" -> {\n                try {\n                    val data = call.argument<String>(\"data\")\n                    if (data == null) {\n                        result.error(\"INVALID_ARGUMENT\", \"data parameter is required\", null)\n                        return\n                    }\n                    val vpnOptions = Gson().fromJson(data, VpnOptions::class.java)\n                    result.success(handleStart(vpnOptions))\n                } catch (e: Exception) {\n                    android.util.Log.e(\"VpnPlugin\", \"Failed to start VPN: ${e.message}\")\n                    result.error(\"PARSE_ERROR\", \"Failed to parse VpnOptions: ${e.message}\", null)\n                }\n            }\n\n            \"stop\" -> {\n                handleStop()\n                result.success(true)\n            }\n\n            \"getLocalIpAddresses\" -> {\n                result.success(getLocalIpAddresses())\n            }\n\n            \"setSmartStopped\" -> {\n                val value = call.argument<Boolean>(\"value\") ?: false\n                GlobalState.isSmartStopped = value\n                result.success(true)\n            }\n\n            \"isSmartStopped\" -> {\n                result.success(GlobalState.isSmartStopped)\n            }\n\n            \"smartStop\" -> {\n                handleSmartStop()\n                result.success(true)\n            }\n\n            \"smartResume\" -> {\n                val data = call.argument<String>(\"data\")\n                result.success(handleSmartResume(Gson().fromJson(data, VpnOptions::class.java)))\n            }\n            \n            \"setQuickResponse\" -> {\n                quickResponseEnabled = call.argument<Boolean>(\"enabled\") ?: false\n                result.success(true)\n            }\n\n            \"status\" -> {\n                result.success(GlobalState.currentRunState == RunState.START)\n            }\n\n            else -> {\n                result.notImplemented()\n            }\n        }\n    }\n    \n    fun setQuickResponse(enabled: Boolean) {\n        quickResponseEnabled = enabled\n    }\n\n    fun getLocalIpAddresses(): List<String> = runCatching {\n        networks.flatMap { network ->\n            connectivity?.getLinkProperties(network)\n                ?.linkAddresses\n                ?.mapNotNull { it.address }\n                ?.filter { !it.isLoopbackAddress && it.hostAddress?.contains(\":\") == false }\n                ?.mapNotNull { it.hostAddress }\n                ?: emptyList()\n        }\n    }.getOrElse {\n        android.util.Log.e(\"VpnPlugin\", \"getLocalIpAddresses error: ${it.message}\")\n        emptyList()\n    }\n\n    fun handleStart(options: VpnOptions): Boolean {\n        onUpdateNetwork()\n        if (options.enable != this.options?.enable) {\n            this.bettBoxService = null\n        }\n        this.options = options\n        when (options.enable) {\n            true -> handleStartVpn()\n            false -> handleStartService()\n        }\n        return true\n    }\n\n    private fun handleStartVpn() {\n        GlobalState.getCurrentAppPlugin()?.requestVpnPermission {\n            handleStartService()\n        }\n    }\n\n    fun requestGc() {\n        invokeDart(\"gc\")\n    }\n\n    fun onUpdateNetwork() {\n        val dns = when {\n            networks.isNotEmpty() -> {\n                networks.flatMap { network ->\n                    connectivity?.resolveDns(network) ?: emptyList()\n                }.toSet()\n            }\n            else -> {\n                val cm = connectivity\n                val activeNetwork = cm?.activeNetwork\n                if (activeNetwork != null && cm != null) {\n                    cm.resolveDns(activeNetwork).toSet()\n                } else {\n                    emptySet()\n                }\n            }\n        }.let { dnsSet ->\n            when {\n                dnsSet.isNotEmpty() -> dnsSet.joinToString(\",\")\n                else -> getAllNetworksDns()\n            }\n        }\n        if (dns == lastDns) return\n        lastDns = dns\n        invokeDart(\"dnsChanged\", dns)\n    }\n    private fun getAllNetworksDns(): String {\n        return runCatching {\n            connectivity?.allNetworks?.flatMap { network ->\n                connectivity?.resolveDns(network) ?: emptyList()\n            }?.filter { it.isNotBlank() }?.toSet()?.joinToString(\",\") ?: \"\"\n        }.getOrElse { \"\" }\n    }\n\n    private val callback = object : ConnectivityManager.NetworkCallback() {\n        override fun onAvailable(network: Network) {\n            networks.add(network)\n            onUpdateNetwork()\n            handleNetworkChange()\n        }\n\n        override fun onLost(network: Network) {\n            networks.remove(network)\n            onUpdateNetwork()\n            handleNetworkChange()\n        }\n    }\n\n    private val request = NetworkRequest.Builder().apply {\n        addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)\n        addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)\n        addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED)\n    }.build()\n\n    private fun registerNetworkCallback() {\n        if (!networkCallbackRegistered.compareAndSet(false, true)) return\n        runCatching {\n            networks.clear()\n            connectivity?.registerNetworkCallback(request, callback)\n        }.onFailure {\n            networkCallbackRegistered.set(false)\n            android.util.Log.e(\"VpnPlugin\", \"Failed to register network callback: ${it.message}\")\n        }\n    }\n\n    private fun unRegisterNetworkCallback() {\n        if (!networkCallbackRegistered.compareAndSet(true, false)) return\n        runCatching {\n            connectivity?.unregisterNetworkCallback(callback)\n        }.onFailure {\n            android.util.Log.e(\"VpnPlugin\", \"Failed to unregister network callback: ${it.message}\")\n        }.also {\n            networks.clear()\n            onUpdateNetwork()\n        }\n    }\n    \n    private fun handleNetworkChange() {\n        val currentNetworkType = getCurrentNetworkType()\n        if (lastNetworkType == null) {\n            lastNetworkType = currentNetworkType\n            return\n        }\n        \n        if (currentNetworkType != lastNetworkType) {\n            lastNetworkType = currentNetworkType\n            \n            ServicePlugin.notifyNetworkChanged()\n            \n            if (!quickResponseEnabled) return\n            if (GlobalState.currentRunState != RunState.START) return\n\n            val now = System.currentTimeMillis()\n            \n            if (now - disconnectWindowStart > disconnectWindowMs) {\n                disconnectWindowStart = now\n                disconnectCount = 0\n            }\n            \n            if (disconnectCount < maxDisconnectsInWindow) {\n                disconnectCount++\n                android.util.Log.d(\"VpnPlugin\", \"Quick Response: Network changed, closing connections ($disconnectCount/$maxDisconnectsInWindow)\")\n                invokeDart(\"closeConnections\")\n            } else {\n                android.util.Log.d(\"VpnPlugin\", \"Quick Response: Disconnect limit reached, ignoring\")\n            }\n        }\n    }\n\n    private fun invokeDart(method: String, arguments: Any? = null) {\n        if (activeChannels.isEmpty()) return\n        scope.launch {\n            withContext(Dispatchers.Main) {\n                activeChannels.forEach { channel ->\n                    runCatching { channel.invokeMethod(method, arguments) }\n                        .onFailure {\n                            android.util.Log.w(\"VpnPlugin\", \"invokeDart($method) failed: ${it.message}\")\n                        }\n                }\n            }\n        }\n    }\n    \n    private fun getCurrentNetworkType(): Int {\n        val activeNetwork = connectivity?.activeNetwork ?: return -1\n        val caps = connectivity?.getNetworkCapabilities(activeNetwork) ?: return -1\n        return when {\n            caps.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> 1\n            caps.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> 2\n            else -> 0\n        }\n    }\n\n    private suspend fun startForeground() {\n        val shouldUpdate = GlobalState.runLock.withLock {\n            GlobalState.currentRunState == RunState.START || GlobalState.isSmartStopped\n        }\n        if (!shouldUpdate) return\n\n        try {\n            bettBoxService?.startForeground()\n        } catch (e: Exception) {\n            android.util.Log.e(\"VpnPlugin\", \"startForeground error: ${e.message}\")\n        }\n    }\n\n    fun updateNotificationIcon() {\n        scope.launch {\n            runCatching {\n                (bettBoxService as? BettboxService)?.resetNotificationBuilder()\n                (bettBoxService as? BettboxVpnService)?.resetNotificationBuilder()\n                bettBoxService?.startForeground()\n            }.onFailure {\n                android.util.Log.e(\"VpnPlugin\", \"updateNotificationIcon error: ${it.message}\")\n            }\n        }\n    }\n\n    fun getStatus(): Boolean {\n        return GlobalState.runLock.withLock {\n            GlobalState.currentRunState == RunState.START && bettBoxService != null\n        }\n    }\n\n    private fun handleStartService() {\n        if (GlobalState.isCurrentlyStopping()) {\n            android.util.Log.w(\"VpnPlugin\", \"VPN is in stopping state, ignore start request\")\n            return\n        }\n        if (bettBoxService == null) {\n            bindService()\n            return\n        }\n        \n        scope.launch {\n            try {\n                val prepareIntent = try {\n                    android.net.VpnService.prepare(BettboxApplication.getAppContext())\n                } catch (e: Exception) {\n                    null\n                }\n\n                if (prepareIntent != null) {\n                    android.util.Log.w(\"VpnPlugin\", \"VPN permission required before start\")\n                    GlobalState.updateRunState(RunState.STOP)\n                    withContext(Dispatchers.Main) {\n                        GlobalState.getCurrentAppPlugin()?.requestVpnPermission {\n                            handleStartService()\n                        }\n                    }\n                    return@launch\n                }\n\n                val currentOptions = options\n                val startAllowed = GlobalState.runLock.withLock {\n                    if (GlobalState.currentRunState == RunState.START) {\n                        android.util.Log.d(\"VpnPlugin\", \"Service already running, refreshing notification\")\n                        scope.launch { startForeground() }\n                        return@withLock false\n                    }\n                    if (currentOptions == null) {\n                        android.util.Log.e(\"VpnPlugin\", \"Start failed: options is null\")\n                        GlobalState.updateRunState(RunState.STOP)\n                        return@withLock false\n                    }\n                    GlobalState.updateRunState(RunState.START)\n                    lastStartForegroundParams = null\n                    true\n                }\n\n                if (!startAllowed || currentOptions == null) return@launch\n\n                performStartCore(currentOptions, retry = true, notifyOnFailure = true)\n            } catch (e: Exception) {\n                android.util.Log.e(\"VpnPlugin\", \"Fatal error in start flow: ${e.message}\")\n                GlobalState.updateRunState(RunState.STOP)\n            }\n        }\n    }\n\n    private suspend fun performStartCore(\n        currentOptions: VpnOptions,\n        retry: Boolean,\n        notifyOnFailure: Boolean\n    ) {\n        var fd: Int? = 0\n        try {\n            fd = bettBoxService?.start(currentOptions)\n        } catch (e: Exception) {\n            android.util.Log.e(\"VpnPlugin\", \"First start attempt failed: ${e.message}\")\n        }\n\n        if (fd == null || (currentOptions.enable && fd <= 0)) {\n            if (retry) {\n                android.util.Log.w(\"VpnPlugin\", \"VPN establish failed, retrying...\")\n                delay(300)\n                try {\n                    fd = bettBoxService?.start(currentOptions)\n                } catch (e: Exception) {\n                    android.util.Log.e(\"VpnPlugin\", \"Retry start failed: ${e.message}\")\n                }\n            }\n        }\n\n        if (fd == null || (currentOptions.enable && fd <= 0)) {\n            android.util.Log.e(\"VpnPlugin\", \"VPN start failed after all attempts\")\n            GlobalState.runLock.withLock { GlobalState.updateRunState(RunState.STOP) }\n            if (notifyOnFailure) {\n                ServicePlugin.notifyVpnStartFailed()\n            }\n            return\n        }\n\n        val canStart = GlobalState.runLock.withLock {\n            if (GlobalState.currentRunState != RunState.START) {\n                bettBoxService?.stop()\n                false\n            } else true\n        }\n        if (!canStart) return\n\n        com.appshub.bettbox.core.Core.startTun(\n            fd = fd ?: 0,\n            protect = this@VpnPlugin::protect,\n            resolverProcess = this@VpnPlugin::resolverProcess,\n        )\n\n        GlobalState.runLock.withLock {\n            if (GlobalState.currentRunState != RunState.START) {\n                Core.stopTun()\n                return@withLock\n            }\n            scope.launch { startForeground() }\n            if (currentOptions.dozeSuspend) {\n                suspendModule?.uninstall()\n                suspendModule = SuspendModule(BettboxApplication.getAppContext())\n                suspendModule?.install()\n            }\n        }\n        onUpdateNetwork()\n    }\n\n    private fun protect(fd: Int): Boolean = runCatching {\n        (bettBoxService as? BettboxVpnService)?.protect(fd) == true\n    }.getOrElse {\n        android.util.Log.e(\"VpnPlugin\", \"protect error: ${it.message}\")\n        false\n    }\n\n    private fun resolverProcess(\n        protocol: Int,\n        source: InetSocketAddress,\n        target: InetSocketAddress,\n        uid: Int,\n    ): String = runCatching {\n        val nextUid = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n            connectivity?.getConnectionOwnerUid(protocol, source, target) ?: -1\n        } else {\n            uid\n        }\n        if (nextUid == -1) {\n            return@runCatching \"\"\n        }\n        uidPageNameMap.getOrPut(nextUid) {\n            BettboxApplication.getAppContext().packageManager?.getPackagesForUid(nextUid)\n                ?.firstOrNull() ?: \"\"\n        }\n    }.getOrElse {\n        android.util.Log.e(\"VpnPlugin\", \"resolverProcess error: ${it.message}\")\n        \"\"\n    }\n\n    fun handleStop(force: Boolean = false) {\n        val serviceRef: BaseServiceInterface?\n        val wasBound: Boolean\n        val shouldForceStop: Boolean\n        GlobalState.runLock.withLock {\n            if (!force && GlobalState.currentRunState == RunState.STOP) return\n            GlobalState.updateIsStopping(true)\n            GlobalState.updateRunState(RunState.STOP)\n            serviceRef = bettBoxService\n            wasBound = isBind\n            shouldForceStop = force || bettBoxService == null\n        }\n\n        suspendModule?.uninstall()\n        suspendModule = null\n        Core.stopTun()\n        serviceRef?.stop()\n\n        runCatching {\n            if (wasBound) {\n                BettboxApplication.getAppContext().unbindService(connection)\n                isBind = false\n            }\n            bettBoxService = null\n        }.onFailure {\n            android.util.Log.e(\"VpnPlugin\", \"unbindService error: ${it.message}\")\n        }\n\n        val context = BettboxApplication.getAppContext()\n        if (shouldForceStop) {\n            context.stopService(Intent(context, BettboxVpnService::class.java))\n            context.stopService(Intent(context, BettboxService::class.java))\n        }\n\n        runCatching {\n            context.getSystemService<android.app.NotificationManager>()\n                ?.cancel(GlobalState.NOTIFICATION_ID)\n        }.onFailure {\n            android.util.Log.e(\"VpnPlugin\", \"cancel notification error: ${it.message}\")\n        }\n\n        scope.launch {\n            delay(300)\n            GlobalState.updateIsStopping(false)\n            delay(200)\n            withContext(Dispatchers.Main) {\n                GlobalState.handleTryDestroy()\n            }\n        }\n    }\n\n    fun handleSmartStop() {\n        GlobalState.runLock.withLock {\n            if (GlobalState.currentRunState == RunState.STOP) return\n            GlobalState.updateRunState(RunState.STOP)\n            GlobalState.isSmartStopped = true\n        }\n        suspendModule?.uninstall()\n        suspendModule = null\n        Core.stopTun()\n        scope.launch {\n            startForeground()\n        }\n    }\n\n    fun handleSmartResume(options: VpnOptions): Boolean {\n        scope.launch {\n            val startAllowed = GlobalState.runLock.withLock {\n                if (GlobalState.currentRunState == RunState.START) return@withLock false\n                GlobalState.isSmartStopped = false\n                this@VpnPlugin.options = options\n\n                if (bettBoxService == null) {\n                    bindService()\n                    return@withLock false\n                }\n\n                GlobalState.updateRunState(RunState.START)\n                lastStartForegroundParams = null\n                true\n            }\n            if (!startAllowed) return@launch\n\n            performStartCore(options, retry = false, notifyOnFailure = false)\n        }\n        return true\n    }\n\n    private fun bindService() {\n        if (!isBinding.compareAndSet(false, true)) return\n\n        bindTimeoutJob?.cancel()\n        bindTimeoutJob = scope.launch {\n            delay(10_000L)\n            if (isBinding.compareAndSet(true, false)) {\n                android.util.Log.w(\"VpnPlugin\", \"bindService timeout (10s), resetting bind state\")\n                GlobalState.runLock.withLock {\n                    if (GlobalState.currentRunState == RunState.PENDING) {\n                        GlobalState.updateRunState(RunState.STOP)\n                    }\n                }\n            }\n        }\n\n        try {\n            if (isBind) {\n                BettboxApplication.getAppContext().unbindService(connection)\n                isBind = false\n            }\n            val intent = Intent(\n                BettboxApplication.getAppContext(),\n                if (options?.enable == true) BettboxVpnService::class.java else BettboxService::class.java\n            )\n            val res = BettboxApplication.getAppContext().bindService(intent, connection, Context.BIND_AUTO_CREATE)\n            if (!res) {\n                isBinding.set(false)\n                bindTimeoutJob?.cancel()\n                bindTimeoutJob = null\n                android.util.Log.e(\"VpnPlugin\", \"bindService returned false (rejected by system)\")\n            }\n        } catch (e: Exception) {\n            isBinding.set(false)\n            bindTimeoutJob?.cancel()\n            bindTimeoutJob = null\n            android.util.Log.e(\"VpnPlugin\", \"bindService error: ${e.message}\")\n        }\n    }\n}"
  },
  {
    "path": "android/app/src/main/kotlin/com/appshub/bettbox/receivers/BootReceiver.kt",
    "content": "package com.appshub.bettbox.receivers\n\nimport android.content.BroadcastReceiver\nimport android.content.Context\nimport android.content.Intent\nimport android.util.Log\nimport com.appshub.bettbox.GlobalState\n\nclass BootReceiver : BroadcastReceiver() {\n    companion object {\n        private const val TAG = \"BootReceiver\"\n        private const val PREFS_NAME = \"FlutterSharedPreferences\"\n        private const val AUTO_LAUNCH_KEY = \"flutter.autoLaunch\"\n    }\n\n    override fun onReceive(context: Context, intent: Intent) {\n        if (intent.action != Intent.ACTION_BOOT_COMPLETED) return\n\n        Log.d(TAG, \"Device boot completed, checking autoLaunch setting\")\n\n        runCatching {\n            val prefs = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE)\n            val autoLaunch = prefs.getBoolean(AUTO_LAUNCH_KEY, false)\n\n            if (autoLaunch) {\n                Log.d(TAG, \"AutoLaunch enabled, triggering silent background boot\")\n                GlobalState.initServiceEngine(listOf(\"boot\"))\n            } else {\n                Log.d(TAG, \"AutoLaunch disabled, skipping background boot\")\n            }\n        }.onFailure { Log.e(TAG, \"Error in BootReceiver\", it) }\n    }\n}\n"
  },
  {
    "path": "android/app/src/main/kotlin/com/appshub/bettbox/receivers/PackageReplacedReceiver.kt",
    "content": "package com.appshub.bettbox.receivers\n\nimport android.content.BroadcastReceiver\nimport android.content.Context\nimport android.content.Intent\nimport android.os.Build\nimport android.util.Log\nimport com.appshub.bettbox.GlobalState\nimport com.appshub.bettbox.RunState\n\nclass PackageReplacedReceiver : BroadcastReceiver() {\n    companion object {\n        private const val TAG = \"PackageReplacedReceiver\"\n    }\n\n    override fun onReceive(context: Context, intent: Intent) {\n        if (intent.action != Intent.ACTION_MY_PACKAGE_REPLACED) return\n        val pending = goAsync()\n        if (Build.VERSION.SDK_INT >= 36) {\n            GlobalState.handleStart(skipDebounce = true)\n        } else {\n            runCatching {\n                android.net.VpnService.prepare(context)\n            }.onFailure { Log.e(TAG, \"Prepare failed\", it) }\n        }\n        \n        pending.finish()\n    }\n}\n"
  },
  {
    "path": "android/app/src/main/kotlin/com/appshub/bettbox/services/BaseServiceInterface.kt",
    "content": "package com.appshub.bettbox.services\n\nimport android.annotation.SuppressLint\nimport android.app.Notification\nimport android.app.Notification.FOREGROUND_SERVICE_IMMEDIATE\nimport android.app.NotificationChannel\nimport android.app.NotificationManager\nimport android.app.PendingIntent\nimport android.app.Service\nimport android.content.pm.PackageManager\nimport android.os.Build\nimport androidx.core.app.NotificationCompat\nimport com.appshub.bettbox.GlobalState\nimport com.appshub.bettbox.R\nimport com.appshub.bettbox.models.VpnOptions\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.Deferred\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.async\nimport android.content.ComponentName\nimport android.content.Intent\n\ninterface BaseServiceInterface {\n    suspend fun start(options: VpnOptions): Int\n    fun stop()\n    suspend fun startForeground()\n}\n\nfun Service.createBettboxNotificationBuilder(): Deferred<NotificationCompat.Builder> =\n    CoroutineScope(Dispatchers.Main).async {\n        val defaultComponent = ComponentName(packageName, \"com.appshub.bettbox.MainActivity\")\n        val lightComponent = ComponentName(packageName, \"com.appshub.bettbox.MainActivityLight\")\n\n        val defaultState = runCatching { packageManager.getComponentEnabledSetting(defaultComponent) }\n            .getOrDefault(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)\n        val lightState = runCatching { packageManager.getComponentEnabledSetting(lightComponent) }\n            .getOrDefault(PackageManager.COMPONENT_ENABLED_STATE_DEFAULT)\n\n        val targetComponent = when {\n            lightState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED -> lightComponent\n            lightState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED -> defaultComponent\n            defaultState == PackageManager.COMPONENT_ENABLED_STATE_ENABLED -> defaultComponent\n            defaultState == PackageManager.COMPONENT_ENABLED_STATE_DISABLED -> lightComponent\n            else -> runCatching {\n                packageManager.getActivityInfo(lightComponent, 0)\n                    .takeIf { it.enabled }?.let { lightComponent }\n            }.getOrNull() ?: defaultComponent\n        }\n\n        android.util.Log.d(\"Notification\", \"Using ${targetComponent.className}\")\n\n        val intent = Intent().apply {\n            component = targetComponent\n            action = Intent.ACTION_MAIN\n            addCategory(Intent.CATEGORY_LAUNCHER)\n            flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP\n        }\n\n        val flags = if (Build.VERSION.SDK_INT >= 31) {\n            PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT\n        } else {\n            PendingIntent.FLAG_UPDATE_CURRENT\n        }\n        val pendingIntent = PendingIntent.getActivity(this@createBettboxNotificationBuilder, 0, intent, flags)\n\n        NotificationCompat.Builder(this@createBettboxNotificationBuilder, GlobalState.NOTIFICATION_CHANNEL).apply {\n            setSmallIcon(R.drawable.ic)\n            setContentTitle(\"Bettbox\")\n            setContentIntent(pendingIntent)\n            setCategory(NotificationCompat.CATEGORY_SERVICE)\n            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {\n                foregroundServiceBehavior = FOREGROUND_SERVICE_IMMEDIATE\n            }\n            setOngoing(true)\n            setShowWhen(false)\n            setOnlyAlertOnce(true)\n        }\n    }\n\nfun Service.ensureNotificationChannel() {\n    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return\n    val manager = getSystemService(NotificationManager::class.java)\n    val channel = manager?.getNotificationChannel(GlobalState.NOTIFICATION_CHANNEL)\n    if (channel == null || channel.importance != NotificationManager.IMPORTANCE_HIGH) {\n        manager?.createNotificationChannel(\n            NotificationChannel(GlobalState.NOTIFICATION_CHANNEL, \"Bettbox Service\", NotificationManager.IMPORTANCE_HIGH)\n        )\n    }\n}\n\n@SuppressLint(\"ForegroundServiceType\")\nfun Service.startForeground(notification: Notification, useSpecialType: Boolean = true) {\n    ensureNotificationChannel()\n\n    val type = if (Build.VERSION.SDK_INT >= 34 && useSpecialType && !GlobalState.isSmartStopped) {\n        android.content.pm.ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE\n    } else {\n        0\n    }\n\n    runCatching {\n        if (type != 0) {\n            startForeground(GlobalState.NOTIFICATION_ID, notification, type)\n        } else {\n            startForeground(GlobalState.NOTIFICATION_ID, notification)\n        }\n    }.onFailure {\n        android.util.Log.e(\"BaseServiceInterface\", \"startForeground failed: ${it.message}\")\n        startForeground(GlobalState.NOTIFICATION_ID, notification)\n    }\n}\n"
  },
  {
    "path": "android/app/src/main/kotlin/com/appshub/bettbox/services/BettboxService.kt",
    "content": "package com.appshub.bettbox.services\n\nimport android.annotation.SuppressLint\nimport android.app.Service\nimport android.content.Intent\nimport android.os.Binder\nimport android.os.Build\nimport android.os.IBinder\nimport android.text.SpannableString\nimport android.text.Spanned\nimport android.text.style.RelativeSizeSpan\nimport androidx.core.app.NotificationCompat\nimport com.appshub.bettbox.GlobalState\nimport com.appshub.bettbox.R\nimport com.appshub.bettbox.models.VpnOptions\n\nclass BettboxService : Service(), BaseServiceInterface {\n\n    @Volatile\n    private var cachedBuilder: NotificationCompat.Builder? = null\n    private val binder = LocalBinder()\n    @Volatile\n    private var hasStartedForeground = false\n\n    inner class LocalBinder : Binder() {\n        fun getService() = this@BettboxService\n    }\n\n    override suspend fun start(options: VpnOptions) = 0\n\n    override fun stop() {\n        hasStartedForeground = false\n        stopSelf()\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n            stopForeground(STOP_FOREGROUND_REMOVE)\n        }\n    }\n\n    fun resetNotificationBuilder() {\n        cachedBuilder = null\n    }\n\n    private suspend fun notificationBuilder() =\n        cachedBuilder ?: createBettboxNotificationBuilder().await().also { cachedBuilder = it }\n\n    @SuppressLint(\"ForegroundServiceType\")\n    override suspend fun startForeground() {\n        ensureNotificationChannel()\n        val title: String\n        val content: String\n        if (GlobalState.isSmartStopped) {\n            title = getString(R.string.core_suspended)\n            content = getString(R.string.smart_auto_stop_service_running)\n        } else {\n            title = getString(R.string.core_connected)\n            content = getString(R.string.service_running)\n        }\n\n        val builder = notificationBuilder()\n\n        val separator = \" ‹ \"\n        val combinedText = \"$title$separator$content\"\n        val spannable = SpannableString(combinedText).apply {\n            val startIndex = title.length + separator.length\n            if (startIndex in 1..combinedText.length) {\n                setSpan(\n                    RelativeSizeSpan(0.80f),\n                    startIndex,\n                    combinedText.length,\n                    Spanned.SPAN_EXCLUSIVE_EXCLUSIVE\n                )\n            }\n        }\n        val notification = builder.setContentTitle(spannable)\n            .setContentText(null)\n            .setStyle(null)\n            .setTicker(combinedText)\n            .build()\n\n        if (!hasStartedForeground) {\n            this.startForeground(notification, useSpecialType = !GlobalState.isSmartStopped)\n            hasStartedForeground = true\n        } else {\n            getSystemService(android.app.NotificationManager::class.java)?.notify(GlobalState.NOTIFICATION_ID, notification)\n        }\n    }\n\n    override fun onTrimMemory(level: Int) {\n        super.onTrimMemory(level)\n        GlobalState.getCurrentVPNPlugin()?.requestGc()\n    }\n\n    override fun onBind(intent: Intent): IBinder = binder\n\n    override fun onDestroy() {\n        stop()\n        super.onDestroy()\n    }\n}\n"
  },
  {
    "path": "android/app/src/main/kotlin/com/appshub/bettbox/services/BettboxTileService.kt",
    "content": "package com.appshub.bettbox.services\n\nimport android.os.Build\nimport android.service.quicksettings.Tile\nimport android.service.quicksettings.TileService\nimport androidx.annotation.RequiresApi\nimport com.appshub.bettbox.GlobalState\nimport com.appshub.bettbox.RunState\nimport kotlinx.coroutines.CoroutineScope\nimport kotlinx.coroutines.Dispatchers\nimport kotlinx.coroutines.SupervisorJob\nimport kotlinx.coroutines.cancel\nimport kotlinx.coroutines.flow.launchIn\nimport kotlinx.coroutines.flow.onEach\nimport kotlinx.coroutines.launch\n\n@RequiresApi(Build.VERSION_CODES.N)\nclass BettboxTileService : TileService() {\n\n    private var scope: CoroutineScope? = null\n\n    private fun updateTile(runState: RunState) {\n        qsTile?.apply {\n            state = when (runState) {\n                RunState.START -> Tile.STATE_ACTIVE\n                RunState.PENDING -> Tile.STATE_UNAVAILABLE\n                RunState.STOP -> Tile.STATE_INACTIVE\n            }\n            updateTile()\n        }\n    }\n\n    override fun onStartListening() {\n        super.onStartListening()\n        scope = CoroutineScope(SupervisorJob() + Dispatchers.Main.immediate)\n        GlobalState.syncStatus()\n        updateTile(GlobalState.currentRunState)\n        scope?.launch {\n            GlobalState.runState.onEach { updateTile(it) }.launchIn(this)\n        }\n    }\n\n    override fun onStopListening() {\n        if (GlobalState.currentRunState == RunState.PENDING) {\n            GlobalState.syncStatus()\n        }\n        scope?.cancel()\n        scope = null\n        super.onStopListening()\n    }\n\n    override fun onClick() {\n        super.onClick()\n        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.UPSIDE_DOWN_CAKE && isLocked) {\n            unlockAndRun { GlobalState.handleToggle() }\n        } else {\n            GlobalState.handleToggle()\n        }\n    }\n\n    override fun onDestroy() {\n        scope?.cancel()\n        scope = null\n        super.onDestroy()\n    }\n}"
  },
  {
    "path": "android/app/src/main/kotlin/com/appshub/bettbox/services/BettboxVpnService.kt",
    "content": "package com.appshub.bettbox.services\n\nimport android.annotation.SuppressLint\nimport android.content.Intent\nimport android.net.ProxyInfo\nimport android.net.VpnService\nimport android.os.Binder\nimport android.os.Build\nimport android.os.IBinder\nimport android.os.Parcel\nimport android.util.Log\nimport androidx.core.app.NotificationCompat\nimport com.appshub.bettbox.GlobalState\nimport com.appshub.bettbox.extensions.getIpv4RouteAddress\nimport com.appshub.bettbox.extensions.getIpv6RouteAddress\nimport com.appshub.bettbox.extensions.toCIDR\nimport com.appshub.bettbox.models.AccessControlMode\nimport com.appshub.bettbox.models.VpnOptions\nimport com.appshub.bettbox.plugins.VpnPlugin\nimport com.appshub.bettbox.R\n\nclass BettboxVpnService : VpnService(), BaseServiceInterface {\n    companion object {\n        private const val TAG = \"BettboxVpnService\"\n    }\n\n    @Volatile\n    private var isStopped = false\n\n    @Volatile\n    private var hasStartedForeground = false\n\n    override fun onCreate() {\n        super.onCreate()\n        GlobalState.initServiceEngine()\n    }\n\n    override suspend fun start(options: VpnOptions): Int = with(Builder()) {\n        options.ipv4Address.takeIf { it.isNotEmpty() }?.let { ipv4 ->\n            val cidr = ipv4.toCIDR()\n            addAddress(cidr.address, cidr.prefixLength)\n            Log.d(\"addAddress\", \"address: ${cidr.address} prefixLength:${cidr.prefixLength}\")\n            val routes = options.getIpv4RouteAddress()\n            if (routes.isNotEmpty()) {\n                runCatching { routes.forEach { addRoute(it.address, it.prefixLength) } }\n                    .onFailure { addRoute(\"0.0.0.0\", 0) }\n            } else {\n                addRoute(\"0.0.0.0\", 0)\n            }\n        } ?: addRoute(\"0.0.0.0\", 0)\n\n        if (options.ipv6Address.isNotEmpty()) {\n            runCatching {\n                val cidr = options.ipv6Address.toCIDR()\n                Log.d(\"addAddress6\", \"address: ${cidr.address} prefixLength:${cidr.prefixLength}\")\n                addAddress(cidr.address, cidr.prefixLength)\n                val routes = options.getIpv6RouteAddress()\n                if (routes.isNotEmpty()) {\n                    runCatching { routes.forEach { addRoute(it.address, it.prefixLength) } }\n                        .onFailure { addRoute(\"::\", 0) }\n                } else {\n                    addRoute(\"::\", 0)\n                }\n            }.onFailure { Log.d(\"addAddress6\", \"IPv6 is not supported.\") }\n        }\n\n        if (options.dnsServerAddress.isNotBlank()) {\n            runCatching { addDnsServer(options.dnsServerAddress) }\n                .onFailure { Log.e(TAG, \"Invalid DNS: ${options.dnsServerAddress}\") }\n        }\n\n        setMtu(options.mtu.coerceIn(1280..65535).takeIf { it > 0 } ?: 1480)\n\n        options.accessControl.takeIf { it.enable }?.let { ac ->\n            when (ac.mode) {\n                AccessControlMode.acceptSelected -> (ac.acceptList + packageName).forEach { addAllowedApplication(it) }\n                AccessControlMode.rejectSelected -> (ac.rejectList - packageName).forEach { addDisallowedApplication(it) }\n            }\n        }\n\n        setSession(\"Bettbox\")\n        setBlocking(false)\n        if (Build.VERSION.SDK_INT >= 29) setMetered(false)\n        if (options.allowBypass) allowBypass()\n\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q && options.systemProxy) {\n            setHttpProxy(ProxyInfo.buildDirectProxy(\"127.0.0.1\", options.port, options.bypassDomain))\n        }\n\n        establish()?.detachFd()?.also { return it }\n        Log.e(TAG, \"Establish VPN rejected by system\")\n        -1\n    }\n\n    override fun onStartCommand(intent: Intent?, flags: Int, startId: Int) = START_STICKY\n\n    override fun stop() {\n        if (isStopped) return\n        isStopped = true\n        hasStartedForeground = false\n\n        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {\n            runCatching { stopForeground(STOP_FOREGROUND_REMOVE) }\n                .onFailure { Log.e(TAG, \"Failed to stop foreground: ${it.message}\") }\n        }\n        stopSelf()\n    }\n\n    @Volatile\n    private var cachedBuilder: NotificationCompat.Builder? = null\n\n    fun resetNotificationBuilder() {\n        cachedBuilder = null\n    }\n\n    private suspend fun notificationBuilder(): NotificationCompat.Builder {\n        if (cachedBuilder == null) {\n            cachedBuilder = createBettboxNotificationBuilder().await()\n        }\n        return cachedBuilder!!\n    }\n\n    @SuppressLint(\"ForegroundServiceType\")\n    override suspend fun startForeground() {\n        ensureNotificationChannel()\n        val title:\n        String\n        val content: String\n        if (GlobalState.isSmartStopped) {\n            title = getString(R.string.core_suspended)\n            content = getString(R.string.smart_auto_stop_service_running)\n        } else {\n            title = getString(R.string.core_connected)\n            content = getString(R.string.service_running)\n        }\n\n        val builder = notificationBuilder()\n\n        val separator = \" ︙ \"\n        val combinedText = \"$title$separator$content\"\n        val spannable = android.text.SpannableString(combinedText)\n        val startIndex = title.length + separator.length\n        if (startIndex in 1..combinedText.length) {\n            spannable.setSpan(\n                android.text.style.RelativeSizeSpan(0.80f),\n                startIndex,\n                combinedText.length,\n                android.text.Spanned.SPAN_EXCLUSIVE_EXCLUSIVE\n            )\n        }\n        val notification = builder.setContentTitle(spannable)\n            .setContentText(null)\n            .setStyle(null)\n            .setTicker(combinedText)\n            .build()\n\n        if (!hasStartedForeground) {\n            this.startForeground(notification, useSpecialType = !GlobalState.isSmartStopped)\n            hasStartedForeground = true\n        } else {\n            getSystemService(android.app.NotificationManager::class.java)?.notify(GlobalState.NOTIFICATION_ID, notification)\n        }\n    }\n\n    override fun onTrimMemory(level: Int) {\n        super.onTrimMemory(level)\n        GlobalState.getCurrentVPNPlugin()?.requestGc()\n    }\n\n    private val binder = LocalBinder()\n\n    inner class LocalBinder : Binder() {\n        fun getService(): BettboxVpnService = this@BettboxVpnService\n\n        override fun onTransact(code: Int, data: Parcel, reply: Parcel?, flags: Int): Boolean =\n            runCatching {\n                super.onTransact(code, data, reply, flags).also { success ->\n                    if (!success) GlobalState.getCurrentTilePlugin()?.handleStop()\n                }\n            }.getOrElse { Log.e(TAG, \"onTransact failed: ${it.message}\"); false }\n    }\n\n    override fun onBind(intent: Intent?): IBinder? {\n        if (intent?.action == VpnService.SERVICE_INTERFACE) {\n            return super.onBind(intent)\n        }\n        return binder\n    }\n\n    override fun onUnbind(intent: Intent?): Boolean {\n        super.onUnbind(intent)\n        return true\n    }\n\n    override fun onRevoke() {\n        runCatching { VpnPlugin.handleStop() }\n        super.onRevoke()\n    }\n\n    override fun onDestroy() {\n        stop()\n        super.onDestroy()\n    }\n}\n"
  },
  {
    "path": "android/app/src/main/res/drawable/ic_launcher_background_dark.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path\n        android:fillColor=\"#292C41\"\n        android:pathData=\"M0,0h108v108h-108z\"/>\n</vector>"
  },
  {
    "path": "android/app/src/main/res/drawable/ic_launcher_background_light.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:pathData=\"M0,0h108v108h-108z\"/>\n</vector>"
  },
  {
    "path": "android/app/src/main/res/drawable/ic_launcher_foreground_dark.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <group\n        android:translateX=\"15.2\"\n        android:translateY=\"15.2\"\n        android:scaleX=\"0.1\"\n        android:scaleY=\"0.1\">\n        <path\n            android:fillColor=\"#FFFFFF\"\n            android:pathData=\"M514.78,480.2h-25.35c-5.34,0-9.18-5.13-7.68-10.25l45.93-156.5c3-10.22,12.38-17.24,23.03-17.24h25.34c5.34,0,9.18,5.13,7.68,10.25l-45.93,156.51c-3,10.22-12.38,17.24-23.03,17.24Z\"/>\n        <path\n            android:fillColor=\"#FFFFFF\"\n            android:pathData=\"M403.29,526.2h-41.01c-8.01,0-13.77-7.7-11.52-15.38l69.74-237.63c4-13.63,16.5-22.99,30.7-22.99h41.02c8.01,0,13.77,7.69,11.51,15.38l-69.74,237.64c-4,13.63-16.5,22.99-30.7,22.99Z\"/>\n        <path\n            android:fillColor=\"#FFFFFF\"\n            android:pathData=\"M267.79,572.2h-59.68c-10.68,0-18.36-10.26-15.35-20.5l94.39-321.63c4.5-15.33,18.57-25.86,34.54-25.86h59.67c10.68,0,18.36,10.26,15.36,20.51l-94.39,321.63c-4.5,15.33-18.56,25.86-34.54,25.86Z\"/>\n    </group>\n</vector>"
  },
  {
    "path": "android/app/src/main/res/drawable/ic_launcher_foreground_light.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportWidth=\"108\"\n    android:viewportHeight=\"108\">\n    <group\n        android:translateX=\"15.2\"\n        android:translateY=\"15.2\"\n        android:scaleX=\"0.1\"\n        android:scaleY=\"0.1\">\n        <path\n            android:fillColor=\"#6890BE\"\n            android:pathData=\"M514.78,480.2h-25.35c-5.34,0-9.18-5.13-7.68-10.25l45.93-156.5c3-10.22,12.38-17.24,23.03-17.24h25.34c5.34,0,9.18,5.13,7.68,10.25l-45.93,156.51c-3,10.22-12.38,17.24-23.03,17.24Z\"/>\n        <path\n            android:fillColor=\"#334663\"\n            android:pathData=\"M403.29,526.2h-41.01c-8.01,0-13.77-7.7-11.52-15.38l69.74-237.63c4-13.63,16.5-22.99,30.7-22.99h41.02c8.01,0,13.77,7.69,11.51,15.38l-69.74,237.64c-4,13.63-16.5,22.99-30.7,22.99Z\"/>\n        <path\n            android:fillColor=\"#292C41\"\n            android:pathData=\"M267.79,572.2h-59.68c-10.68,0-18.36-10.26-15.35-20.5l94.39-321.63c4.5-15.33,18.57-25.86,34.54-25.86h59.67c10.68,0,18.36,10.26,15.36,20.51l-94.39,321.63c-4.5,15.33-18.56,25.86-34.54,25.86Z\"/>\n    </group>\n</vector>"
  },
  {
    "path": "android/app/src/main/res/drawable/ic_notification_dark.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Dark icon notification -->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:pathData=\"M12.14,11.28h-0.59c-0.13,0-0.22-0.12-0.18-0.24l1.08-3.67c0.07-0.24,0.29-0.4,0.54-0.4h0.59c0.13,0,0.22,0.12,0.18,0.24l-1.08,3.67c-0.07,0.24-0.29,0.4-0.54,0.4Z\"/>\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:pathData=\"M9.46,12.35h-0.96c-0.19,0-0.32-0.18-0.27-0.36l1.64-5.58c0.09-0.32,0.39-0.54,0.72-0.54h0.96c0.19,0,0.32,0.18,0.27,0.36l-1.64,5.58c-0.09,0.32-0.39,0.54-0.72,0.54Z\"/>\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:pathData=\"M6.29,13.43h-1.4c-0.25,0-0.43-0.24-0.36-0.48l2.22-7.55c0.11-0.36,0.44-0.61,0.81-0.61h1.4c0.25,0,0.43,0.24,0.36,0.48l-2.22,7.55c-0.11,0.36-0.44,0.61-0.81,0.61Z\"/>\n</vector>\n"
  },
  {
    "path": "android/app/src/main/res/drawable/ic_notification_light.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Light icon notification -->\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"24dp\"\n    android:height=\"24dp\"\n    android:viewportWidth=\"24\"\n    android:viewportHeight=\"24\">\n    <path\n        android:fillColor=\"#6890BE\"\n        android:pathData=\"M12.14,11.28h-0.59c-0.13,0-0.22-0.12-0.18-0.24l1.08-3.67c0.07-0.24,0.29-0.4,0.54-0.4h0.59c0.13,0,0.22,0.12,0.18,0.24l-1.08,3.67c-0.07,0.24-0.29,0.4-0.54,0.4Z\"/>\n    <path\n        android:fillColor=\"#334663\"\n        android:pathData=\"M9.46,12.35h-0.96c-0.19,0-0.32-0.18-0.27-0.36l1.64-5.58c0.09-0.32,0.39-0.54,0.72-0.54h0.96c0.19,0,0.32,0.18,0.27,0.36l-1.64,5.58c-0.09,0.32-0.39,0.54-0.72,0.54Z\"/>\n    <path\n        android:fillColor=\"#292C41\"\n        android:pathData=\"M6.29,13.43h-1.4c-0.25,0-0.43-0.24-0.36-0.48l2.22-7.55c0.11-0.36,0.44-0.61,0.81-0.61h1.4c0.25,0,0.43,0.24,0.36,0.48l-2.22,7.55c-0.11,0.36-0.44,0.61-0.81,0.61Z\"/>\n</vector>\n"
  },
  {
    "path": "android/app/src/main/res/drawable/ic_tile.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Light mode icon -->\n<bitmap xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:src=\"@drawable/ic_shortcut_black\" />\n\n"
  },
  {
    "path": "android/app/src/main/res/drawable/launch_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item>\n        <shape android:shape=\"rectangle\">\n            <solid android:color=\"#FFFFFF\" />\n        </shape>\n    </item>\n    <item\n        android:drawable=\"@drawable/ic_launcher_foreground_light\"\n        android:gravity=\"center\" />\n</layer-list>\n"
  },
  {
    "path": "android/app/src/main/res/drawable/tv_banner.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"320dp\"\n    android:height=\"180dp\"\n    android:viewportWidth=\"320\"\n    android:viewportHeight=\"180\">\n    <path\n        android:fillColor=\"#292C41\"\n        android:pathData=\"M0,0h320v180h-320z\"/>\n    <group\n        android:translateX=\"106\"\n        android:translateY=\"36\">\n        <group\n            android:translateX=\"15.2\"\n            android:translateY=\"15.2\"\n            android:scaleX=\"0.1\"\n            android:scaleY=\"0.1\">\n            <path\n                android:fillColor=\"#FFFFFF\"\n                android:pathData=\"M514.78,480.2h-25.35c-5.34,0-9.18-5.13-7.68-10.25l45.93-156.5c3-10.22,12.38-17.24,23.03-17.24h25.34c5.34,0,9.18,5.13,7.68,10.25l-45.93,156.51c-3,10.22-12.38,17.24-23.03,17.24Z\"/>\n            <path\n                android:fillColor=\"#FFFFFF\"\n                android:pathData=\"M403.29,526.2h-41.01c-8.01,0-13.77-7.7-11.52-15.38l69.74-237.63c4-13.63,16.5-22.99,30.7-22.99h41.02c8.01,0,13.77,7.69,11.51,15.38l-69.74,237.64c-4,13.63-16.5,22.99-30.7,22.99Z\"/>\n            <path\n                android:fillColor=\"#FFFFFF\"\n                android:pathData=\"M267.79,572.2h-59.68c-10.68,0-18.36-10.26-15.35-20.5l94.39-321.63c4.5-15.33,18.57-25.86,34.54-25.86h59.67c10.68,0,18.36,10.26,15.36,20.51l-94.39,321.63c-4.5,15.33-18.56,25.86-34.54,25.86Z\"/>\n        </group>\n    </group>\n</vector>\n"
  },
  {
    "path": "android/app/src/main/res/drawable-night/ic_tile.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Dark mode icon -->\n<bitmap xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:src=\"@drawable/ic_shortcut_white\" />\n\n"
  },
  {
    "path": "android/app/src/main/res/drawable-night/launch_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item>\n        <shape android:shape=\"rectangle\">\n            <solid android:color=\"#292C41\" />\n        </shape>\n    </item>\n    <item\n        android:drawable=\"@drawable/ic_launcher_foreground_dark\"\n        android:gravity=\"center\" />\n</layer-list>\n"
  },
  {
    "path": "android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background_dark\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground_dark\" />\n    <monochrome android:drawable=\"@drawable/ic_launcher_foreground_dark\" />\n</adaptive-icon>"
  },
  {
    "path": "android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_light.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background_light\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground_light\" />\n    <monochrome android:drawable=\"@drawable/ic_launcher_foreground_light\" />\n</adaptive-icon>\n"
  },
  {
    "path": "android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background_dark\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground_dark\" />\n    <monochrome android:drawable=\"@drawable/ic_launcher_foreground_dark\" />\n</adaptive-icon>\n"
  },
  {
    "path": "android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round_light.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background_light\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground_light\" />\n    <monochrome android:drawable=\"@drawable/ic_launcher_foreground_light\" />\n</adaptive-icon>\n"
  },
  {
    "path": "android/app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"bett_box\">Bettbox</string>\n    <string name=\"core_connected\">Connected</string>\n    <string name=\"service_running\">Service is running</string>\n    <string name=\"core_suspended\">Suspended</string>\n    <string name=\"smart_auto_stop_service_running\">Auto-stop is running</string>\n</resources>\n"
  },
  {
    "path": "android/app/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <style name=\"LaunchTheme\" parent=\"@android:style/Theme.Light.NoTitleBar\">\n        <!-- Show a splash screen on the activity. Automatically removed when\n             the Flutter engine draws its first frame -->\n        <item name=\"android:windowBackground\">@drawable/launch_background</item>\n    </style>\n    <!-- Theme applied to the Android Window as soon as the process has started.\n         This theme determines the color of the Android Window while your\n         Flutter UI initializes, as well as behind your Flutter UI while its\n         running.\n\n         This Theme is only used starting with V2 of Flutter's Android embedding. -->\n    <style name=\"NormalTheme\" parent=\"@android:style/Theme.Light.NoTitleBar\">\n        <item name=\"android:windowBackground\">?android:colorBackground</item>\n    </style>\n    <style name=\"TransparentTheme\" parent=\"Theme.AppCompat.NoActionBar\">\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n        <item name=\"android:windowNoTitle\">true</item>\n        <item name=\"android:windowIsTranslucent\">true</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "android/app/src/main/res/values-night/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources xmlns:tools=\"http://schemas.android.com/tools\">\n    <style name=\"LaunchTheme\" parent=\"@android:style/Theme.Translucent.NoTitleBar\">\n        <item name=\"android:windowBackground\">@drawable/launch_background</item>\n        <item name=\"android:windowIsTranslucent\">true</item>\n    </style>\n    <!-- Theme applied to the Android Window as soon as the process has started.\n         This theme determines the color of the Android Window while your\n         Flutter UI initializes, as well as behind your Flutter UI while its\n         running.\n\n         This Theme is only used starting with V2 of Flutter's Android embedding. -->\n    <style name=\"NormalTheme\" parent=\"@android:style/Theme.Translucent.NoTitleBar\">\n        <item name=\"android:windowBackground\">@android:color/transparent</item>\n        <item name=\"android:windowIsTranslucent\">true</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "android/app/src/main/res/values-night-v27/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources xmlns:tools=\"http://schemas.android.com/tools\">\n    <!-- LaunchTheme removed - using NormalTheme directly -->\n</resources>"
  },
  {
    "path": "android/app/src/main/res/values-ru/strings.xml",
    "content": "<resources>\n    <string name=\"bett_box\">Bettbox</string>\n    <string name=\"core_connected\">Подключено</string>\n    <string name=\"service_running\">Сервис запущен</string>\n    <string name=\"core_suspended\">Приостановлено</string>\n    <string name=\"smart_auto_stop_service_running\">Автостоп запущен</string>\n</resources>\n"
  },
  {
    "path": "android/app/src/main/res/values-v27/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources xmlns:tools=\"http://schemas.android.com/tools\">\n    <!-- LaunchTheme removed - using NormalTheme directly -->\n</resources>"
  },
  {
    "path": "android/app/src/main/res/values-zh-rCN/strings.xml",
    "content": "<resources>\n    <string name=\"bett_box\">Bettbox</string>\n    <string name=\"core_connected\">已连接</string>\n    <string name=\"service_running\">服务正在运行中</string>\n    <string name=\"core_suspended\">已挂起</string>\n    <string name=\"smart_auto_stop_service_running\">智能启停运行中</string>\n</resources>\n"
  },
  {
    "path": "android/app/src/main/res/values-zh-rTW/strings.xml",
    "content": "<resources>\n    <string name=\"bett_box\">Bettbox</string>\n    <string name=\"core_connected\">已連線</string>\n    <string name=\"service_running\">服務正在運行中</string>\n    <string name=\"core_suspended\">已暫停</string>\n    <string name=\"smart_auto_stop_service_running\">智能啟停運行中</string>\n</resources>\n"
  },
  {
    "path": "android/app/src/main/res/xml/file_paths.xml",
    "content": "<paths>\n    <files-path\n        name=\"files\"\n        path=\".\"/>\n</paths>\n\n"
  },
  {
    "path": "android/app/src/main/res/xml/network_security_config.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<network-security-config xmlns:tools=\"http://schemas.android.com/tools\"\n    tools:ignore=\"AcceptsUserCertificates\">\n    <base-config>\n        <trust-anchors>\n            <certificates src=\"system\" />\n            <certificates src=\"user\" />\n        </trust-anchors>\n    </base-config>\n    <domain-config cleartextTrafficPermitted=\"true\">\n        <domain includeSubdomains=\"true\">localhost</domain>\n        <domain includeSubdomains=\"true\">127.0.0.1</domain>\n    </domain-config>\n</network-security-config>"
  },
  {
    "path": "android/app/src/profile/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <!-- The INTERNET permission is required for development. Specifically,\n         the Flutter tool needs it to communicate with the running application\n         to allow setting breakpoints, to provide hot reload, etc.\n    -->\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n</manifest>\n"
  },
  {
    "path": "android/build.gradle.kts",
    "content": "\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\n\nval newBuildDir: Directory = rootProject.layout.buildDirectory.dir(\"../../build\").get()\nrootProject.layout.buildDirectory.value(newBuildDir)\n\nsubprojects {\n    val newSubprojectBuildDir: Directory = newBuildDir.dir(project.name)\n    project.layout.buildDirectory.value(newSubprojectBuildDir)\n}\nsubprojects {\n    project.evaluationDependsOn(\":app\")\n}\n\ntasks.register<Delete>(\"clean\") {\n    delete(rootProject.layout.buildDirectory)\n}"
  },
  {
    "path": "android/core/.gitignore",
    "content": "/build"
  },
  {
    "path": "android/core/build.gradle.kts",
    "content": "plugins {\n    id(\"com.android.library\")\n    id(\"org.jetbrains.kotlin.android\")\n}\n\nandroid {\n    namespace = \"com.appshub.bettbox.core\"\n    compileSdk = 36\n    ndkVersion = \"28.2.13676358\"\n\n    defaultConfig {\n        minSdk = 26\n    }\n\n    buildTypes {\n        release {\n            isJniDebuggable = false\n            proguardFiles(\n                getDefaultProguardFile(\"proguard-android-optimize.txt\"),\n                \"proguard-rules.pro\"\n            )\n        }\n    }\n\n    sourceSets {\n        getByName(\"main\") {\n            jniLibs.srcDirs(\"src/main/jniLibs\")\n        }\n    }\n\n    externalNativeBuild {\n        cmake {\n            path(\"src/main/cpp/CMakeLists.txt\")\n            version = \"3.22.1\"\n        }\n    }\n\n    kotlinOptions {\n        jvmTarget = \"17\"\n    }\n\n    compileOptions {\n        sourceCompatibility = JavaVersion.VERSION_17\n        targetCompatibility = JavaVersion.VERSION_17\n    }\n}\ndependencies {\n    implementation(\"androidx.annotation:annotation-jvm:1.9.1\")\n}\n\nval copyNativeLibs by tasks.register<Copy>(\"copyNativeLibs\") {\n    doFirst {\n        delete(\"src/main/jniLibs\")\n    }\n    from(\"../../libclash/android\")\n    into(\"src/main/jniLibs\")\n}\n\nafterEvaluate {\n    tasks.named(\"preBuild\") {\n        dependsOn(copyNativeLibs)\n    }\n}"
  },
  {
    "path": "android/core/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile"
  },
  {
    "path": "android/core/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n</manifest>"
  },
  {
    "path": "android/core/src/main/cpp/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.22.1)\n\nproject(\"core\")\n\nmessage(\"CMAKE_SOURCE_DIR ${CMAKE_SOURCE_DIR}\")\n\nmessage(\"CMAKE_BUILD_TYPE ${CMAKE_BUILD_TYPE}\")\n\nif (NOT \"${CMAKE_BUILD_TYPE}\" STREQUAL \"Debug\")\n    add_compile_options(-O3)\n\n    add_compile_options(-flto)\n\n    add_compile_options(-g0)\n\n    add_compile_options(-ffunction-sections -fdata-sections)\n\n    add_compile_options(-fno-exceptions -fno-rtti)\n\n    add_link_options(\n            -flto\n            -Wl,--gc-sections\n            -Wl,--strip-all\n            -Wl,--exclude-libs=ALL\n    )\n\n    if (${ANDROID_ABI} STREQUAL \"arm64-v8a\" OR ${ANDROID_ABI} STREQUAL \"x86_64\")\n        add_link_options(-Wl,-z,max-page-size=16384)\n    endif()\n\n    add_compile_options(-fvisibility=hidden -fvisibility-inlines-hidden)\nendif ()\n\nset(LIB_CLASH_PATH \"${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI}/libclash.so\")\n\nmessage(\"LIB_CLASH_PATH ${LIB_CLASH_PATH}\")\nif (EXISTS ${LIB_CLASH_PATH})\n    message(\"Found libclash.so for ABI ${ANDROID_ABI}\")\n    add_compile_definitions(LIBCLASH)\n    include_directories(${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})\n    link_directories(${CMAKE_SOURCE_DIR}/../jniLibs/${ANDROID_ABI})\n    add_library(${CMAKE_PROJECT_NAME} SHARED\n            jni_helper.cpp\n            core.cpp)\n    target_link_libraries(${CMAKE_PROJECT_NAME}\n            clash)\nelse ()\n    message(\"Not found libclash.so for ABI ${ANDROID_ABI}\")\n    add_library(${CMAKE_PROJECT_NAME} SHARED\n            jni_helper.cpp\n            core.cpp)\n    target_link_libraries(${CMAKE_PROJECT_NAME})\nendif ()"
  },
  {
    "path": "android/core/src/main/cpp/core.cpp",
    "content": "#ifdef LIBCLASH\n#include <jni.h>\n#include <cstring>\n#include \"jni_helper.h\"\n#include \"libclash.h\"\n\nextern \"C\"\nJNIEXPORT void JNICALL\nJava_com_appshub_bettbox_core_Core_startTun(JNIEnv *env, jobject, const jint fd, jobject cb) {\n    const auto interface = new_global(cb);\n    startTUN(fd, interface);\n}\n\nextern \"C\"\nJNIEXPORT void JNICALL\nJava_com_appshub_bettbox_core_Core_stopTun(JNIEnv *) {\n    stopTun();\n}\n\nextern \"C\"\nJNIEXPORT void JNICALL\nJava_com_appshub_bettbox_core_Core_suspend(JNIEnv *, jobject, jint suspended) {\n    suspend(suspended);\n}\n\n\nstatic jmethodID m_tun_interface_protect;\nstatic jmethodID m_tun_interface_resolve_process;\n\n\nstatic void release_jni_object_impl(void *obj) {\n    ATTACH_JNI();\n    del_global(static_cast<jobject>(obj));\n}\n\nstatic void call_tun_interface_protect_impl(void *tun_interface, const int fd) {\n    ATTACH_JNI();\n    env->CallVoidMethod(static_cast<jobject>(tun_interface),\n                        m_tun_interface_protect,\n                        fd);\n}\n\nstatic const char *\ncall_tun_interface_resolve_process_impl(void *tun_interface, int protocol,\n                                        const char *source,\n                                        const char *target,\n                                        const int uid) {\n    ATTACH_JNI();\n    if (env->PushLocalFrame(8) < 0) {\n        return strdup(\"\");\n    }\n    const auto packageName = reinterpret_cast<jstring>(env->CallObjectMethod(static_cast<jobject>(tun_interface),\n                                                                       m_tun_interface_resolve_process,\n                                                                       protocol,\n                                                                       new_string(source),\n                                                                       new_string(target),\n                                                                       uid));\n    const auto result = get_string(packageName);\n    env->PopLocalFrame(nullptr);\n    return result;\n}\n\nextern \"C\"\nJNIEXPORT jint JNICALL\nJNI_OnLoad(JavaVM *vm, void *) {\n    JNIEnv *env = nullptr;\n    if (vm->GetEnv(reinterpret_cast<void **>(&env), JNI_VERSION_1_6) != JNI_OK) {\n        return JNI_ERR;\n    }\n\n    initialize_jni(vm, env);\n\n    const auto c_tun_interface = find_class(\"com/appshub/bettbox/core/TunInterface\");\n\n    m_tun_interface_protect = find_method(c_tun_interface, \"protect\", \"(I)V\");\n    m_tun_interface_resolve_process = find_method(c_tun_interface, \"resolverProcess\",\n                                                  \"(ILjava/lang/String;Ljava/lang/String;I)Ljava/lang/String;\");\n\n    registerCallbacks(&call_tun_interface_protect_impl,\n                      &call_tun_interface_resolve_process_impl,\n                      &release_jni_object_impl);\n    return JNI_VERSION_1_6;\n}\n#endif\n"
  },
  {
    "path": "android/core/src/main/cpp/jni_helper.cpp",
    "content": "#include \"jni_helper.h\"\n\n#include <cstdlib>\n#include <malloc.h>\n#include <cstring>\n\nstatic JavaVM *global_vm;\n\nstatic jclass c_string;\nstatic jmethodID m_new_string;\nstatic jmethodID m_get_bytes;\n\nvoid initialize_jni(JavaVM *vm, JNIEnv *env) {\n    global_vm = vm;\n\n    c_string = reinterpret_cast<jclass>(new_global(find_class(\"java/lang/String\")));\n    m_new_string = find_method(c_string, \"<init>\", \"([B)V\");\n    m_get_bytes = find_method(c_string, \"getBytes\", \"()[B\");\n}\n\nJavaVM *global_java_vm() {\n    return global_vm;\n}\n\nchar *jni_get_string(JNIEnv *env, jstring str) {\n    const auto array = reinterpret_cast<jbyteArray>(env->CallObjectMethod(str, m_get_bytes));\n    const int length = env->GetArrayLength(array);\n    const auto content = static_cast<char *>(malloc(length + 1));\n    env->GetByteArrayRegion(array, 0, length, reinterpret_cast<jbyte *>(content));\n    content[length] = 0;\n    return content;\n}\n\njstring jni_new_string(JNIEnv *env, const char *str) {\n    const auto length = static_cast<int>(strlen(str));\n    const auto array = env->NewByteArray(length);\n    env->SetByteArrayRegion(array, 0, length, reinterpret_cast<const jbyte *>(str));\n    return reinterpret_cast<jstring>(env->NewObject(c_string, m_new_string, array));\n}\n\nint jni_catch_exception(JNIEnv *env) {\n    const int result = env->ExceptionCheck();\n    if (result) {\n        env->ExceptionDescribe();\n        env->ExceptionClear();\n    }\n    return result;\n}\n\nvoid jni_attach_thread(scoped_jni *jni) {\n    JavaVM *vm = global_java_vm();\n    if (vm->GetEnv(reinterpret_cast<void **>(&jni->env), JNI_VERSION_1_6) == JNI_OK) {\n        jni->require_release = 0;\n        return;\n    }\n    if (vm->AttachCurrentThread(&jni->env, nullptr) != JNI_OK) {\n        abort();\n    }\n    jni->require_release = 1;\n}\n\nvoid jni_detach_thread(const scoped_jni *env) {\n    JavaVM *vm = global_java_vm();\n    if (env->require_release) {\n        vm->DetachCurrentThread();\n    }\n}\n\nvoid release_string(char **str) {\n    free(*str);\n}"
  },
  {
    "path": "android/core/src/main/cpp/jni_helper.h",
    "content": "#pragma once\n\n#include <jni.h>\n\nstruct scoped_jni {\n    JNIEnv *env;\n    int require_release;\n};\n\nextern void initialize_jni(JavaVM *vm, JNIEnv *env);\n\nextern jstring jni_new_string(JNIEnv *env, const char *str);\n\nextern char *jni_get_string(JNIEnv *env, jstring str);\n\nextern int jni_catch_exception(JNIEnv *env);\n\nextern void jni_attach_thread(scoped_jni *jni);\n\nextern void jni_detach_thread(const scoped_jni *env);\n\nextern void release_string(char **str);\n\n#define ATTACH_JNI() __attribute__((unused, cleanup(jni_detach_thread))) \\\n                    scoped_jni _jni{}; \\\n                    jni_attach_thread(&_jni); \\\n                    JNIEnv *env = _jni.env\n\n#define scoped_string __attribute__((cleanup(release_string))) char*\n\n#define find_class(name) env->FindClass(name)\n#define find_method(cls, name, signature) env->GetMethodID(cls, name, signature)\n#define new_global(obj) env->NewGlobalRef(obj)\n#define del_global(obj) env->DeleteGlobalRef(obj)\n#define get_string(jstr) jni_get_string(env, jstr)\n#define new_string(cstr) jni_new_string(env, cstr)\n"
  },
  {
    "path": "android/core/src/main/java/com/appshub/bettbox/core/Core.kt",
    "content": "package com.appshub.bettbox.core\n\nimport android.util.Log\nimport java.net.InetSocketAddress\n\nobject Core {\n\n    private external fun startTun(fd: Int, cb: TunInterface)\n    private external fun suspend(suspended: Int)\n    external fun stopTun()\n\n    init {\n        System.loadLibrary(\"core\")\n    }\n\n    private fun parseInetSocketAddress(address: String): InetSocketAddress {\n        val lastColonIndex = address.lastIndexOf(':')\n        if (lastColonIndex == -1) {\n            return InetSocketAddress(address, 0)\n        }\n\n        val host = address.substring(0, lastColonIndex).removeSurrounding(\"[\", \"]\")\n        val port = address.substring(lastColonIndex + 1).toIntOrNull() ?: 0\n\n        return InetSocketAddress(host, port)\n    }\n\n    fun startTun(\n        fd: Int,\n        protect: (Int) -> Boolean,\n        resolverProcess: (protocol: Int, source: InetSocketAddress, target: InetSocketAddress, uid: Int) -> String\n    ) {\n        startTun(fd, object : TunInterface {\n            override fun protect(fd: Int) {\n                runCatching { protect(fd) }\n                    .onFailure { Log.e(\"Core\", \"protect JNI callback error: ${it.message}\") }\n            }\n\n            override fun resolverProcess(\n                protocol: Int,\n                source: String,\n                target: String,\n                uid: Int\n            ): String = runCatching {\n                resolverProcess(\n                    protocol,\n                    parseInetSocketAddress(source),\n                    parseInetSocketAddress(target),\n                    uid\n                )\n            }.onFailure {\n                Log.e(\"Core\", \"resolverProcess JNI callback error: ${it.message}\")\n            }.getOrDefault(\"\")\n        })\n    }\n\n    fun suspended(value: Boolean) {\n        runCatching {\n            Log.d(\"Core\", \"suspended called with value: $value\")\n            suspend(if (value) 1 else 0)\n            Log.d(\"Core\", \"suspend JNI call completed\")\n        }.onFailure {\n            Log.e(\"Core\", \"Error calling suspend: ${it.message}\", it)\n        }\n    }\n}\n"
  },
  {
    "path": "android/core/src/main/java/com/appshub/bettbox/core/TunInterface.kt",
    "content": "package com.appshub.bettbox.core\n\nimport androidx.annotation.Keep\n\n@Keep\ninterface TunInterface {\n    fun protect(fd: Int)\n    fun resolverProcess(protocol: Int, source: String, target: String, uid: Int): String\n}"
  },
  {
    "path": "android/gradle.properties",
    "content": "org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=1g -XX:+HeapDumpOnOutOfMemoryError\norg.gradle.daemon=true\norg.gradle.parallel=true\norg.gradle.caching=true\norg.gradle.configureondemand=true\nandroid.useAndroidX=true\nandroid.enableJetifier=true\nkotlin_version=2.2.10\nagp_version=8.12.2\nandroid.enableCcache=true\n"
  },
  {
    "path": "android/settings.gradle.kts",
    "content": "pluginManagement {\n    val flutterSdkPath = run {\n        val properties = java.util.Properties()\n        file(\"local.properties\").inputStream().use { properties.load(it) }\n        val flutterSdkPath = properties.getProperty(\"flutter.sdk\")\n        require(flutterSdkPath != null) { \"flutter.sdk not set in local.properties\" }\n        flutterSdkPath\n    }\n\n    includeBuild(\"$flutterSdkPath/packages/flutter_tools/gradle\")\n\n    repositories {\n        google()\n        mavenCentral()\n        gradlePluginPortal()\n    }\n}\n\nplugins {\n    id(\"dev.flutter.flutter-plugin-loader\") version \"1.0.0\"\n    id(\"com.android.application\") version \"8.12.2\" apply false\n    id(\"org.jetbrains.kotlin.android\") version \"2.1.0\" apply false\n}\n\ninclude(\":app\")\ninclude(\":core\")"
  },
  {
    "path": "arb/intl_en.arb",
    "content": "{\n  \"rule\": \"Rule\",\n  \"global\": \"Global\",\n  \"direct\": \"Direct\",\n  \"dashboard\": \"Dashboard\",\n  \"proxies\": \"Proxies\",\n  \"profile\": \"Profile\",\n  \"profiles\": \"Profiles\",\n  \"tools\": \"Tools\",\n  \"logs\": \"Logs\",\n  \"logsDesc\": \"View captured logs\",\n  \"resources\": \"Resources\",\n  \"syncAll\": \"Sync All\",\n  \"syncFailed\": \"Sync Failed\",\n  \"resourcesDesc\": \"External resource info\",\n  \"scriptDesc\": \"Global override script config\",\n  \"trafficUsage\": \"Traffic Usage\",\n  \"coreInfo\": \"Core Info\",\n  \"networkSpeed\": \"Network Speed\",\n  \"outboundMode\": \"Outbound Mode\",\n  \"networkDetection\": \"Network Detection\",\n  \"upload\": \"Upload\",\n  \"download\": \"Download\",\n  \"noProxy\": \"No Proxy\",\n  \"noProxyDesc\": \"Please create or add a valid profile\",\n  \"nullProfileDesc\": \"No profile. Please add one.\",\n  \"settings\": \"Settings\",\n  \"language\": \"Language\",\n  \"defaultText\": \"Default\",\n  \"more\": \"More\",\n  \"other\": \"Other\",\n  \"otherSettings\": \"Enhanced Tools\",\n  \"otherSettingsDesc\": \"Modify enhanced tool settings\",\n  \"smartAutoStop\": \"Smart Auto-Stop\",\n  \"smartAutoStopDesc\": \"Stop VPN on specific networks\",\n  \"networkMatch\": \"Network Match\",\n  \"networkMatchHint\": \"Max 2 IPs/CIDRs, comma-separated\",\n  \"smartAutoStopServiceRunning\": \"Smart Auto-Stop running\",\n  \"serviceRunning\": \"Service Running\",\n  \"coreConnected\": \"Connected\",\n  \"coreSuspended\": \"Suspended\",\n  \"invalidIpFormat\": \"Invalid IP or CIDR format\",\n  \"tooManyRules\": \"Max 2 rules allowed\",\n  \"dozeSuspend\": \"Doze Support\",\n  \"dozeSuspendDesc\": \"Sync with system Doze mode\",\n  \"storeFix\": \"Store Fix\",\n  \"storeFixDesc\": \"Fix Play Store download issues\",\n  \"disableQuic\": \"Disable QUIC\",\n  \"disableQuicDesc\": \"Disable QUIC to resolve specific network issues\",\n  \"excludeChina\": \"Exclude China\",\n  \"excludeChinaDesc\": \"Allow China QUIC traffic instead of blocking all\",\n  \"fcmOptimization\": \"FCM Optimization\",\n  \"fcmOptimizationDesc\": \"Enhance FCM connection stability\",\n  \"quickResponse\": \"Quick Response\",\n  \"quickResponseDesc\": \"Disconnect on network change (WiFi/Mobile)\",\n  \"networkFix\": \"Network Fix\",\n  \"networkFixDesc\": \"Fix Windows network globe icon issue\",\n  \"batteryOptimization\": \"Battery Optimization\",\n  \"batteryOptimizationDesc\": \"Request battery optimization whitelist\",\n  \"alreadyInWhitelist\": \"Already in whitelist\",\n  \"about\": \"About\",\n  \"en\": \"English\",\n  \"ja\": \"Japanese\",\n  \"ru\": \"Russian\",\n  \"zh_CN\": \"Simplified Chinese\",\n  \"zh_TC\": \"Traditional Chinese\",\n  \"theme\": \"Theme\",\n  \"themeDesc\": \"Set theme color and icon\",\n  \"override\": \"Override\",\n  \"overrideDesc\": \"Override proxy configurations\",\n  \"allowLan\": \"Allow LAN\",\n  \"allowLanDesc\": \"Allow LAN access to proxy\",\n  \"tun\": \"TUN\",\n  \"tunDesc\": \"Take over global device traffic\",\n  \"minimizeOnExit\": \"Minimize on Exit\",\n  \"minimizeOnExitDesc\": \"Override default exit behavior\",\n  \"autoLaunch\": \"Auto Launch\",\n  \"autoLaunchDesc\": \"Launch on system startup\",\n  \"smartDelayLaunch\": \"Smart Delay\",\n  \"smartDelayLaunchDesc\": \"Launch after network connected\",\n  \"silentLaunch\": \"Silent Launch\",\n  \"silentLaunchDesc\": \"Start in the background\",\n  \"autoRun\": \"Auto Run\",\n  \"autoRunDesc\": \"Connect on app launch\",\n  \"logcat\": \"Log Capture\",\n  \"logcatDesc\": \"Show log capture entry\",\n  \"enableCrashReport\": \"Crash Analytics\",\n  \"enableCrashReportDesc\": \"Upload crash logs when needed\",\n  \"autoCheckUpdate\": \"Auto Check Updates\",\n  \"autoCheckUpdateDesc\": \"Check updates on app launch\",\n  \"accessControl\": \"Access Control\",\n  \"accessControlDesc\": \"Configure per-app proxy access\",\n  \"clearCacheTitle\": \"Clear Cache\",\n  \"clearCacheDesc\": \"Clear FakeIP and DNS cache?\",\n  \"forceGCTitle\": \"Force Garbage Collection\",\n  \"forceGCDesc\": \"Force kernel garbage collection? Experimental, use with caution.\",\n  \"fcmTip\": \"FCM support depends on your device; results are for reference. Disable 'Allow Bypass VPN' in network settings for accurate results.\",\n  \"application\": \"Application\",\n  \"applicationDesc\": \"Modify application settings\",\n  \"edit\": \"Edit\",\n  \"confirm\": \"Confirm\",\n  \"update\": \"Update\",\n  \"add\": \"Add\",\n  \"save\": \"Save\",\n  \"delete\": \"Delete\",\n  \"years\": \"Years\",\n  \"months\": \"Months\",\n  \"hours\": \"Hours\",\n  \"days\": \"Days\",\n  \"minutes\": \"Minutes\",\n  \"seconds\": \"Seconds\",\n  \"ago\": \" Ago\",\n  \"pleaseCloseTunFirst\": \"Please close TUN first\",\n  \"pleaseCloseSystemProxyFirst\": \"Please close System Proxy first\",\n  \"just\": \"Just now\",\n  \"qrcode\": \"QR Code\",\n  \"qrcodeDesc\": \"Scan QR code to get profile\",\n  \"clipboard\": \"Clipboard\",\n  \"clipboardDesc\": \"Get profile link from clipboard\",\n  \"url\": \"URL\",\n  \"urlDesc\": \"Get profile via URL\",\n  \"file\": \"File\",\n  \"fileDesc\": \"Upload profile file\",\n  \"name\": \"Name\",\n  \"profileNameNullValidationDesc\": \"Please enter a profile name\",\n  \"profileUrlNullValidationDesc\": \"Please enter a profile URL\",\n  \"profileUrlInvalidValidationDesc\": \"Please enter a valid URL\",\n  \"autoUpdate\": \"Auto Update\",\n  \"autoUpdateInterval\": \"Auto update interval (min)\",\n  \"profileAutoUpdateIntervalNullValidationDesc\": \"Please enter update interval\",\n  \"profileAutoUpdateIntervalInvalidValidationDesc\": \"Please enter a valid interval\",\n  \"themeMode\": \"Theme Mode\",\n  \"themeColor\": \"Theme Color\",\n  \"preview\": \"Preview\",\n  \"runtimeConfig\": \"Runtime Config\",\n  \"cameraPermissionDenied\": \"Camera Permission Denied\",\n  \"cameraPermissionDesc\": \"Camera permission is required to scan QR codes. Please grant it in settings.\",\n  \"openSettings\": \"Open Settings\",\n  \"retry\": \"Retry\",\n  \"packageListPermissionRequired\": \"Permission to access installed apps is required. Grant now?\",\n  \"packageListPermissionDenied\": \"Permission denied. Cannot access app list.\",\n  \"auto\": \"Auto\",\n  \"light\": \"Light\",\n  \"dark\": \"Dark\",\n  \"importFromURL\": \"Import from URL\",\n  \"submit\": \"Submit\",\n  \"doYouWantToPass\": \"Do you want to pass\",\n  \"create\": \"Create\",\n  \"defaultSort\": \"Default Sort\",\n  \"delaySort\": \"Sort by Delay\",\n  \"nameSort\": \"Sort by Name\",\n  \"pleaseUploadFile\": \"Please upload a file\",\n  \"pleaseUploadValidQrcode\": \"Please upload a valid QR code\",\n  \"blacklistMode\": \"Blacklist Mode\",\n  \"whitelistMode\": \"Whitelist Mode\",\n  \"filterSystemApp\": \"Filter System Apps\",\n  \"cancelFilterSystemApp\": \"Show System Apps\",\n  \"selectAll\": \"Select All\",\n  \"cancelSelectAll\": \"Deselect All\",\n  \"appAccessControl\": \"App Access Control\",\n  \"accessControlAllowDesc\": \"Only route selected apps through VPN\",\n  \"accessControlNotAllowDesc\": \"Exclude selected apps from VPN\",\n  \"selected\": \"Selected\",\n  \"unableToUpdateCurrentProfileDesc\": \"Unable to update current profile\",\n  \"noMoreInfoDesc\": \"No more info\",\n  \"profileParseErrorDesc\": \"Profile parse error\",\n  \"proxyPort\": \"Proxy Port\",\n  \"proxyPortDesc\": \"Set the Clash listening port\",\n  \"port\": \"Port\",\n  \"logLevel\": \"Log Level\",\n  \"show\": \"Show\",\n  \"exit\": \"Exit\",\n  \"systemProxy\": \"System Proxy\",\n  \"project\": \"Project\",\n  \"core\": \"Core\",\n  \"tabAnimation\": \"Tab Animation\",\n  \"desc\": \"Bettbox is based on the powerful and flexible Mihomo (Clash.Meta) proxy kernel, dedicated to a superior user experience. Forked from FlClash: Better Experience, Out of the box\",\n  \"startVpn\": \"Starting...\",\n  \"stopVpn\": \"Stopping...\",\n  \"discovery\": \"New Version Found\",\n  \"compatible\": \"Compatible Mode\",\n  \"compatibleDesc\": \"Reduces some features for full Clash compatibility\",\n  \"notSelectedTip\": \"Current proxy group cannot be selected.\",\n  \"tip\": \"Tip\",\n  \"backupAndRecovery\": \"Backup & Restore\",\n  \"backupAndRecoveryDesc\": \"Sync data via WebDAV or local files\",\n  \"account\": \"Account\",\n  \"backup\": \"Backup\",\n  \"recovery\": \"Restore\",\n  \"recoveryProfiles\": \"Restore Profiles Only\",\n  \"recoveryAll\": \"Restore All Data\",\n  \"recoverySuccess\": \"Restore Successful\",\n  \"backupSuccess\": \"Backup Successful\",\n  \"noInfo\": \"No Info\",\n  \"pleaseBindWebDAV\": \"Please bind WebDAV\",\n  \"bind\": \"Bind\",\n  \"connectivity\": \"Connectivity:\",\n  \"webDAVConfiguration\": \"WebDAV Configuration\",\n  \"address\": \"Address\",\n  \"addressHelp\": \"WebDAV server address\",\n  \"addressTip\": \"Please enter a valid WebDAV address\",\n  \"password\": \"Password\",\n  \"checkUpdate\": \"Check for Updates\",\n  \"discoverNewVersion\": \"New Version Available\",\n  \"checkUpdateError\": \"Already on the latest version\",\n  \"goDownload\": \"Download Now\",\n  \"unknown\": \"Unknown\",\n  \"geoData\": \"GeoData\",\n  \"externalResources\": \"External Resources\",\n  \"checking\": \"Checking...\",\n  \"country\": \"Country\",\n  \"checkError\": \"Check Failed\",\n  \"search\": \"Search\",\n  \"allowBypass\": \"Allow Bypassing VPN\",\n  \"allowBypassDesc\": \"Allow specific apps to bypass VPN\",\n  \"externalController\": \"External Controller\",\n  \"externalControllerDesc\": \"Control core via online port\",\n  \"controlSecret\": \"Control Secret\",\n  \"controlSecretDesc\": \"RESTful API access password\",\n  \"generateSecret\": \"Generate\",\n  \"secretCopied\": \"Secret copied to clipboard\",\n  \"ipv6Desc\": \"Enable IPv6 traffic routing\",\n  \"app\": \"App\",\n  \"general\": \"General\",\n  \"vpnSystemProxyDesc\": \"Attach HTTP proxy to VpnService\",\n  \"systemProxyDesc\": \"Set system proxy\",\n  \"unifiedDelay\": \"Unified Delay\",\n  \"unifiedDelayDesc\": \"Exclude handshake delays from testing\",\n  \"tcpConcurrent\": \"TCP Concurrent\",\n  \"tcpConcurrentDesc\": \"Allow concurrent TCP connections\",\n  \"geodataLoader\": \"GEO Low Memory\",\n  \"geodataLoaderDesc\": \"Use GEO low memory loader\",\n  \"requests\": \"Requests\",\n  \"requestsDesc\": \"View recent request logs\",\n  \"findProcessMode\": \"Find Process\",\n  \"init\": \"Init\",\n  \"infiniteTime\": \"Never Expires\",\n  \"expirationTime\": \"Expiration Time\",\n  \"connections\": \"Connections\",\n  \"connectionsDesc\": \"View active connections\",\n  \"intranetIP\": \"Intranet IP\",\n  \"view\": \"View\",\n  \"cut\": \"Cut\",\n  \"copy\": \"Copy\",\n  \"paste\": \"Paste\",\n  \"testUrl\": \"Test URL\",\n  \"startTest\": \"Start Test\",\n  \"addProfile\": \"Add Profile\",\n  \"customUrl\": \"Custom URL\",\n  \"sync\": \"Sync\",\n  \"exclude\": \"Hide from Recents\",\n  \"excludeDesc\": \"Hide app from recent tasks list\",\n  \"oneColumn\": \"1 Column\",\n  \"twoColumns\": \"2 Columns\",\n  \"threeColumns\": \"3 Columns\",\n  \"fourColumns\": \"4 Columns\",\n  \"expand\": \"Standard\",\n  \"shrink\": \"Compact\",\n  \"min\": \"Min\",\n  \"tab\": \"Tab\",\n  \"list\": \"List\",\n  \"delay\": \"Delay\",\n  \"style\": \"Style\",\n  \"size\": \"Size\",\n  \"delayAnimation\": \"Delay Animation\",\n  \"delayAnimationDesc\": \"Customize animation during delay testing\",\n  \"noAnimation\": \"Default\",\n  \"rotatingCircle\": \"Rotating Circle\",\n  \"pulse\": \"Pulse\",\n  \"spinningLines\": \"Spinning Lines\",\n  \"threeInOut\": \"Three In Out\",\n  \"threeBounce\": \"Three Bounce\",\n  \"circle\": \"Circle\",\n  \"fadingCircle\": \"Fading Circle\",\n  \"fadingFour\": \"Fading Four\",\n  \"wave\": \"Wave\",\n  \"doubleBounce\": \"Double Bounce\",\n  \"sort\": \"Sort\",\n  \"columns\": \"Columns\",\n  \"proxiesSetting\": \"Proxy Settings\",\n  \"proxyGroup\": \"Proxy Group\",\n  \"go\": \"Go\",\n  \"externalLink\": \"External Link\",\n  \"otherContributors\": \"Other Contributors\",\n  \"autoCloseConnections\": \"Auto-Close Connections\",\n  \"autoCloseConnectionsDesc\": \"Close connections when switching nodes\",\n  \"onlyStatisticsProxy\": \"Proxy Traffic Only\",\n  \"onlyStatisticsProxyDesc\": \"Only record proxy traffic\",\n  \"pureBlackMode\": \"Pure Black Mode\",\n  \"keepAliveIntervalDesc\": \"TCP keep-alive interval\",\n  \"entries\": \" entries\",\n  \"local\": \"Local\",\n  \"remote\": \"Remote\",\n  \"remoteBackupDesc\": \"Backup data to WebDAV\",\n  \"remoteRecoveryDesc\": \"Restore data from WebDAV\",\n  \"localBackupDesc\": \"Backup data locally\",\n  \"localRecoveryDesc\": \"Restore data from file\",\n  \"mode\": \"Mode\",\n  \"time\": \"Time\",\n  \"source\": \"Source\",\n  \"allApps\": \"All Apps\",\n  \"onlyOtherApps\": \"Third-Party Apps Only\",\n  \"action\": \"Action\",\n  \"intelligentSelected\": \"Smart Select\",\n  \"clipboardImport\": \"Import from Clipboard\",\n  \"clipboardExport\": \"Export to Clipboard\",\n  \"layout\": \"Layout\",\n  \"tight\": \"Compact\",\n  \"standard\": \"Standard\",\n  \"loose\": \"Loose\",\n  \"profilesSort\": \"Profile Sorting\",\n  \"start\": \"Start\",\n  \"stop\": \"Stop\",\n  \"powerSwitch\": \"Power Switch\",\n  \"runTime\": \"Uptime\",\n  \"checkOrAddProfile\": \"Please add a profile first\",\n  \"serviceReady\": \"Service Ready\",\n  \"appDesc\": \"App-related settings\",\n  \"vpnDesc\": \"VPN-related settings\",\n  \"dnsDesc\": \"DNS-related settings\",\n  \"key\": \"Key\",\n  \"value\": \"Value\",\n  \"hostsDesc\": \"Append hosts to current config\",\n  \"vpnTip\": \"Restart VPN to apply changes\",\n  \"vpnEnableDesc\": \"Route all system traffic via VpnService\",\n  \"options\": \"Options\",\n  \"loopback\": \"Loopback Unlock\",\n  \"loopbackDesc\": \"UWP loopback unlocking tool\",\n  \"providers\": \"Providers\",\n  \"proxyProviders\": \"Proxy Providers\",\n  \"ruleProviders\": \"Rule Providers\",\n  \"advancedSettings\": \"Advanced Settings\",\n  \"nodeExclusion\": \"Node Exclusion\",\n  \"nodeExclusionDesc\": \"Exclude all matched nodes\",\n  \"nodeExclusionPlaceholder\": \"HK|Hong Kong|🇭🇰\",\n  \"formatError\": \"Please check the format\",\n  \"healthCheckTimeout\": \"Timeout\",\n  \"healthCheckTimeoutDesc\": \"Node health check timeout\",\n  \"concurrencyLimit\": \"Concurrency Limit\",\n  \"concurrencyLimitDesc\": \"Maximum concurrent delay tests\",\n  \"notRecommended\": \"Not Recommended\",\n  \"overrideDns\": \"Override DNS\",\n  \"overrideDnsDesc\": \"Override profile's DNS settings\",\n  \"overrideTestUrl\": \"Override Config\",\n  \"ntp\": \"NTP\",\n  \"ntpDesc\": \"Use NTP time service\",\n  \"overrideNtp\": \"Override NTP\",\n  \"overrideNtpDesc\": \"Override profile's NTP settings\",\n  \"ntpStatus\": \"Status\",\n  \"ntpStatusDesc\": \"Enable NTP time service\",\n  \"writeToSystem\": \"Write to System\",\n  \"writeToSystemDesc\": \"Requires administrator privileges\",\n  \"ntpServer\": \"Server\",\n  \"ntpPort\": \"Port\",\n  \"ntpInterval\": \"Update Interval\",\n  \"sniffer\": \"Sniffer\",\n  \"snifferDesc\": \"Modify domain sniffer config\",\n  \"overrideSniffer\": \"Override Sniffer\",\n  \"overrideSnifferDesc\": \"Override profile's Sniffer settings\",\n  \"snifferStatus\": \"Status\",\n  \"snifferStatusDesc\": \"Enable Sniffer service\",\n  \"forceDnsMapping\": \"Force DNS Mapping\",\n  \"forceDnsMappingDesc\": \"Force mapping DNS results to connections\",\n  \"parsePureIp\": \"Parse Pure IP\",\n  \"parsePureIpDesc\": \"Parse pure IP connections\",\n  \"overrideDestination\": \"Override Destination\",\n  \"overrideDestinationDesc\": \"Override destination with sniffed result\",\n  \"httpPortSniffer\": \"HTTP Port Sniffing\",\n  \"tlsPortSniffer\": \"TLS Port Sniffing\",\n  \"quicPortSniffer\": \"QUIC Port Sniffing\",\n  \"forceDomain\": \"Force Sniff Domain\",\n  \"skipDomain\": \"Skip Domain\",\n  \"skipSrcAddress\": \"Skip Source IP\",\n  \"skipDstAddress\": \"Skip Destination IP\",\n  \"snifferPorts\": \"Ports\",\n  \"snifferPortsHint\": \"e.g.: 80, 8080-8880\",\n  \"snifferDomainHint\": \"One domain per line\",\n  \"snifferAddressHint\": \"One address per line\",\n  \"tunnel\": \"Tunnel\",\n  \"tunnelDesc\": \"Use traffic forwarding tunnel\",\n  \"overrideTunnel\": \"Override Tunnel\",\n  \"overrideTunnelDesc\": \"Override profile's Tunnel settings\",\n  \"tunnelList\": \"Forwarding List\",\n  \"addTunnel\": \"Add Forwarding\",\n  \"editTunnel\": \"Edit Forwarding\",\n  \"deleteTunnel\": \"Delete Forwarding\",\n  \"tunnelNetwork\": \"Network Protocol\",\n  \"tunnelNetworkHint\": \"e.g.: tcp, udp\",\n  \"tunnelAddress\": \"Listen Address\",\n  \"tunnelAddressHint\": \"e.g.: 127.0.0.1:6553\",\n  \"tunnelTarget\": \"Target Address\",\n  \"tunnelTargetHint\": \"e.g.: 114.114.114.114:53\",\n  \"tunnelProxy\": \"Proxy Name\",\n  \"tunnelProxyHint\": \"e.g.: proxy (optional)\",\n  \"experimental\": \"Experimental\",\n  \"experimentalDesc\": \"Use with caution\",\n  \"overrideExperimental\": \"Override Experimental\",\n  \"overrideExperimentalDesc\": \"Override profile's Experimental settings\",\n  \"quicGoDisableGso\": \"Disable QUIC GSO\",\n  \"quicGoDisableGsoDesc\": \"Disable QUIC Generic Segmentation Offload\",\n  \"quicGoDisableEcn\": \"Disable QUIC ECN\",\n  \"quicGoDisableEcnDesc\": \"Disable QUIC Explicit Congestion Notification\",\n  \"dialerIp4pConvert\": \"Enable Dialer IP4P Conversion\",\n  \"dialerIp4pConvertDesc\": \"Enable dialer IP4P address conversion feature\",\n  \"status\": \"Status\",\n  \"statusDesc\": \"Uses system DNS when disabled\",\n  \"preferH3Desc\": \"Prioritize DoH HTTP/3\",\n  \"cacheAlgorithm\": \"Cache Algorithm\",\n  \"respectRules\": \"Respect Rules\",\n  \"respectRulesDesc\": \"DNS connections follow Rules\",\n  \"dnsMode\": \"DNS Mode\",\n  \"fakeipRange\": \"FakeIP Range\",\n  \"fakeipRangeV6\": \"FakeIPv6 Range\",\n  \"fakeIpFilterMode\": \"FakeIP Filter Mode\",\n  \"fakeIpFilterModeDesc\": \"Specify FakeIP filter mode\",\n  \"blacklist\": \"Blacklist\",\n  \"whitelist\": \"Whitelist\",\n  \"fakeipFilter\": \"FakeIP Filter\",\n  \"fakeipTtl\": \"FakeIP TTL\",\n  \"defaultNameserver\": \"Default Nameserver\",\n  \"defaultNameserverDesc\": \"Used to resolve DNS servers\",\n  \"nameserver\": \"Nameserver\",\n  \"nameserverDesc\": \"Used to resolve domains\",\n  \"useHosts\": \"Use Hosts\",\n  \"useSystemHosts\": \"Use System Hosts\",\n  \"nameserverPolicy\": \"Nameserver Policy\",\n  \"nameserverPolicyDesc\": \"Specify domain-specific nameservers\",\n  \"proxyNameserver\": \"Proxy Nameserver\",\n  \"proxyNameserverDesc\": \"Used to resolve proxy nodes\",\n  \"directNameserver\": \"Direct Nameserver\",\n  \"directNameserverDesc\": \"Used to resolve direct domains\",\n  \"directNameserverFollowPolicy\": \"Direct DNS Follows Policy\",\n  \"fallback\": \"Fallback\",\n  \"fallbackDesc\": \"Usually offshore DNS\",\n  \"fallbackFilter\": \"Fallback Filter\",\n  \"geoipCode\": \"GeoIP Code\",\n  \"ipcidr\": \"IP/CIDR\",\n  \"domain\": \"Domain\",\n  \"reset\": \"Reset\",\n  \"action_view\": \"Show/Hide\",\n  \"action_start\": \"Start/Stop\",\n  \"action_mode\": \"Switch Mode\",\n  \"action_proxy\": \"System Proxy\",\n  \"action_tun\": \"TUN\",\n  \"disclaimer\": \"Disclaimer\",\n  \"disclaimerDesc\": \"This free open-source software is for non-commercial learning and personal use only. Proxy services are independent of this software. By agreeing, you acknowledge this; otherwise, please exit.\",\n  \"agree\": \"Agree\",\n  \"hotkeyManagement\": \"Hotkey Management\",\n  \"hotkeyManagementDesc\": \"Control app via keyboard\",\n  \"pressKeyboard\": \"Press a key\",\n  \"inputCorrectHotkey\": \"Enter a valid hotkey\",\n  \"hotkeyConflict\": \"Hotkey Conflict\",\n  \"remove\": \"Remove\",\n  \"noHotKey\": \"No Hotkeys\",\n  \"noNetwork\": \"No Network\",\n  \"ipv6InboundDesc\": \"Allow IPv6 inbound\",\n  \"exportLogs\": \"Export Logs\",\n  \"exportSuccess\": \"Export Successful\",\n  \"iconStyle\": \"Icon Style\",\n  \"onlyIcon\": \"Icon Only\",\n  \"noIcon\": \"No Icon\",\n  \"stackMode\": \"Stack Mode\",\n  \"strictRoute\": \"Strict Route\",\n  \"strictRouteDesc\": \"Use TUN strict routing mode\",\n  \"icmpForwarding\": \"ICMP Forwarding\",\n  \"icmpForwardingDesc\": \"Enable ICMP Ping\",\n  \"dnsHijack\": \"DNS Hijack\",\n  \"dnsHijackDesc\": \"Redirect DNS queries to internal DNS module\",\n  \"endpointIndependentNat\": \"NAT Enhancement\",\n  \"endpointIndependentNatDesc\": \"Enable endpoint-independent NAT\",\n  \"network\": \"Network\",\n  \"networkDesc\": \"Modify network-related settings\",\n  \"bypassDomain\": \"Bypass Domain\",\n  \"bypassDomainDesc\": \"Active only when System Proxy is on\",\n  \"resetTip\": \"Are you sure you want to reset?\",\n  \"regExp\": \"RegExp\",\n  \"icon\": \"Icon\",\n  \"iconConfiguration\": \"Icon Configuration\",\n  \"noData\": \"No Data\",\n  \"adminAutoLaunch\": \"Admin Auto-Launch\",\n  \"adminAutoLaunchDesc\": \"Auto-start with admin privileges\",\n  \"fontFamily\": \"Font\",\n  \"systemFont\": \"System Font\",\n  \"toggle\": \"Toggle\",\n  \"system\": \"System\",\n  \"bypassPrivateRoute\": \"Bypass Private Network\",\n  \"bypassPrivateRouteDesc\": \"Automatically bypass private network IP addresses\",\n  \"pleaseInputAdminPassword\": \"Please enter the admin password\",\n  \"copyEnvVar\": \"Copy Environment Variable\",\n  \"memoryInfo\": \"Memory Info\",\n  \"cancel\": \"Cancel\",\n  \"fileIsUpdate\": \"File modified. Save changes?\",\n  \"profileHasUpdate\": \"Profile modified. Disable auto-update?\",\n  \"hasCacheChange\": \"Cache modifications?\",\n  \"copySuccess\": \"Copy Successful\",\n  \"success\": \"Success\",\n  \"copyLink\": \"Copy Link\",\n  \"exportFile\": \"Export File\",\n  \"cacheCorrupt\": \"Cache corrupted. Clear it?\",\n  \"detectionTip\": \"Third-party API result (for reference only)\",\n  \"ipClickBehavior\": \"Toggle Display\",\n  \"ipPrivacyProtection\": \"Hide IP Display\",\n  \"manualRefreshIp\": \"Refresh IP\",\n  \"tryManualRefresh\": \"Please try manual refresh\",\n  \"refreshAppList\": \"Refresh App List\",\n  \"refreshAppListConfirm\": \"Refresh the app list?\",\n  \"switchToDomesticIp\": \"Get Domestic IP\",\n  \"listen\": \"Listen\",\n  \"undo\": \"Undo\",\n  \"redo\": \"Redo\",\n  \"none\": \"None\",\n  \"basicConfig\": \"Core Configuration\",\n  \"basicConfigDesc\": \"Global core settings\",\n  \"selectedCountTitle\": \"{count} items selected\",\n  \"addRule\": \"Add Rule\",\n  \"ruleName\": \"Rule Name\",\n  \"content\": \"Content\",\n  \"subRule\": \"Sub Rule\",\n  \"ruleTarget\": \"Rule Target\",\n  \"sourceIp\": \"Source IP\",\n  \"noResolve\": \"No Resolve\",\n  \"getOriginRules\": \"Original Rules\",\n  \"overrideOriginRules\": \"Override Original Rules\",\n  \"addedOriginRules\": \"Append to Original Rules\",\n  \"enableOverride\": \"Enable Override\",\n  \"saveChanges\": \"Save changes?\",\n  \"generalDesc\": \"Modify general settings\",\n  \"findProcessModeDesc\": \"Enable process matching\",\n  \"tabAnimationDesc\": \"Effective only in mobile view\",\n  \"navBarHapticFeedback\": \"Haptic Feedback\",\n  \"navBarHapticFeedbackDesc\": \"Vibrate on navigation tab switch\",\n  \"saveTip\": \"Are you sure you want to save?\",\n  \"colorSchemes\": \"Color Schemes\",\n  \"palette\": \"Palette\",\n  \"tonalSpotScheme\": \"Tonal Spot\",\n  \"fidelityScheme\": \"Fidelity\",\n  \"monochromeScheme\": \"Monochrome\",\n  \"neutralScheme\": \"Neutral\",\n  \"vibrantScheme\": \"Vibrant\",\n  \"expressiveScheme\": \"Expressive\",\n  \"contentScheme\": \"Content\",\n  \"rainbowScheme\": \"Rainbow\",\n  \"fruitSaladScheme\": \"Fruit Salad\",\n  \"developerMode\": \"Developer Mode\",\n  \"developerModeEnableTip\": \"Developer mode is enabled.\",\n  \"messageTest\": \"Message Test\",\n  \"messageTestTip\": \"This is a message.\",\n  \"crashTest\": \"Crash Test\",\n  \"clearData\": \"Clear Data\",\n  \"textScale\": \"Text Scaling\",\n  \"lightIcon\": \"Light Icon\",\n  \"lightIconDesc\": \"Manually switch light desktop app icon\",\n  \"harmonyFont\": \"HarmonyOS Font\",\n  \"harmonyFontDesc\": \"Use optimized HarmonyOS Sans font\",\n  \"internet\": \"Internet\",\n  \"systemApp\": \"System App\",\n  \"noNetworkApp\": \"No Network App\",\n  \"contactMe\": \"Contact Me\",\n  \"recoveryStrategy\": \"Recovery Strategy\",\n  \"recoveryStrategy_override\": \"Override\",\n  \"recoveryStrategy_compatible\": \"Compatible\",\n  \"logsTest\": \"Logs Test\",\n  \"emptyTip\": \"{label} cannot be empty\",\n  \"urlTip\": \"{label} must be a URL\",\n  \"numberTip\": \"{label} must be a number\",\n  \"interval\": \"Interval\",\n  \"existsTip\": \"{label} already exists\",\n  \"deleteTip\": \"Delete current {label}?\",\n  \"deleteMultipTip\": \"Delete selected {label}?\",\n  \"nullTip\": \"No {label}\",\n  \"script\": \"Script\",\n  \"color\": \"Color\",\n  \"rename\": \"Rename\",\n  \"unnamed\": \"Unnamed\",\n  \"pleaseEnterScriptName\": \"Please enter a script name\",\n  \"overrideInvalidTip\": \"Inactive in script mode\",\n  \"mixedPort\": \"Mixed Port\",\n  \"socksPort\": \"Socks Port\",\n  \"redirPort\": \"Redir Port\",\n  \"tproxyPort\": \"Tproxy Port\",\n  \"portTip\": \"{label} must be between 1024 and 49151\",\n  \"portConflictTip\": \"Please enter a different port\",\n  \"import\": \"Import\",\n  \"importFromCode\": \"Import from Code\",\n  \"importFailed\": \"Import failed\",\n  \"importFile\": \"Import from File\",\n  \"importUrl\": \"Import from URL\",\n  \"autoSetSystemDns\": \"Auto Set System DNS\",\n  \"details\": \"{label} Details\",\n  \"creationTime\": \"Creation Time\",\n  \"progress\": \"Progress\",\n  \"host\": \"Host\",\n  \"destination\": \"Destination\",\n  \"destinationGeoIP\": \"Destination GeoIP\",\n  \"destinationIPASN\": \"Destination IP ASN\",\n  \"specialProxy\": \"Special Proxy\",\n  \"specialRules\": \"Special Rules\",\n  \"remoteDestination\": \"Remote Destination\",\n  \"networkType\": \"Network Type\",\n  \"proxyChains\": \"Proxy Chains\",\n  \"log\": \"Log\",\n  \"connection\": \"Active Connections\",\n  \"request\": \"Request\",\n  \"switchLabel\": \"Switch\",\n  \"noStatusAvailable\": \"No Status\",\n  \"onlinePanel\": \"Online Panel\",\n  \"openDashboard\": \"Open Zashboard\",\n  \"custom\": \"Custom\",\n  \"wakelock\": \"Wakelock\",\n  \"wakelockDescription\": \"Keeps the screen on and app active in the background without requiring special CPU wakelock permissions.\",\n  \"tunEnableRequireAdmin\": \"TUN requires admin privileges. Please run as Administrator.\",\n  \"restartTip\": \"Restart TUN for changes to take effect\",\n  \"restart\": \"Restart\",\n  \"restartCoreTitle\": \"Restart Core\",\n  \"restartCoreDesc\": \"Manually restart the core?\",\n  \"highRefreshRate\": \"High Refresh Rate\",\n  \"highRefreshRateDesc\": \"Enable highest refresh rate support\"\n}\n"
  },
  {
    "path": "arb/intl_ru.arb",
    "content": "{\n  \"rule\": \"Правила\",\n  \"global\": \"Глобально\",\n  \"direct\": \"Напрямую\",\n  \"dashboard\": \"Главная\",\n  \"proxies\": \"Прокси\",\n  \"profile\": \"Профиль\",\n  \"profiles\": \"Профили\",\n  \"tools\": \"Настройки\",\n  \"logs\": \"Логи\",\n  \"logsDesc\": \"Просмотр журналов\",\n  \"resources\": \"Ресурсы\",\n  \"syncAll\": \"Синхронизировать всё\",\n  \"syncFailed\": \"Ошибка синхронизации\",\n  \"resourcesDesc\": \"Управление внешними ресурсами\",\n  \"scriptDesc\": \"Настройка глобального скрипта переопределения\",\n  \"trafficUsage\": \"Трафик\",\n  \"coreInfo\": \"Информация о ядре\",\n  \"networkSpeed\": \"Скорость сети\",\n  \"outboundMode\": \"Режим выхода\",\n  \"networkDetection\": \"Проверка сети\",\n  \"upload\": \"Отправка\",\n  \"download\": \"Загрузка\",\n  \"noProxy\": \"Нет прокси\",\n  \"noProxyDesc\": \"Создайте или добавьте профиль\",\n  \"nullProfileDesc\": \"Нет профиля, добавьте его\",\n  \"settings\": \"Настройки\",\n  \"language\": \"Язык\",\n  \"defaultText\": \"По умолчанию\",\n  \"more\": \"Подробнее\",\n  \"other\": \"Другое\",\n  \"otherSettings\": \"Расширенные инструменты\",\n  \"otherSettingsDesc\": \"Настройка расширенных функций\",\n  \"smartAutoStop\": \"Умная остановка\",\n  \"smartAutoStopDesc\": \"Останавливать прокси при подключении к заданной сети\",\n  \"networkMatch\": \"Сопоставление сети\",\n  \"networkMatchHint\": \"Введите IP или CIDR, максимум 2, через запятую\",\n  \"smartAutoStopServiceRunning\": \"Служба умной остановки работает\",\n  \"serviceRunning\": \"Служба запущена\",\n  \"coreConnected\": \"Подключено\",\n  \"coreSuspended\": \"Приостановлено\",\n  \"invalidIpFormat\": \"Неверный формат IP или CIDR\",\n  \"tooManyRules\": \"Максимум 2 правила\",\n  \"dozeSuspend\": \"Поддержка Doze\",\n  \"dozeSuspendDesc\": \"Синхронизация с режимом сна Android\",\n  \"storeFix\": \"Исправление магазина\",\n  \"storeFixDesc\": \"Исправляет проблемы загрузки Google Play\",\n  \"disableQuic\": \"Отключить QUIC\",\n  \"disableQuicDesc\": \"Отключить QUIC для решения сетевых проблем\",\n  \"excludeChina\": \"Исключить Китай\",\n  \"excludeChinaDesc\": \"Разрешить QUIC-трафик Китая вместо полной блокировки\",\n  \"fcmOptimization\": \"Оптимизация FCM\",\n  \"fcmOptimizationDesc\": \"Повышает стабильность FCM при прямом подключении\",\n  \"quickResponse\": \"Быстрый отклик\",\n  \"quickResponseDesc\": \"Активно отключать соединения при изменении сети\",\n  \"networkFix\": \"Исправление сети\",\n  \"networkFixDesc\": \"Исправляет значок сети Windows\",\n  \"batteryOptimization\": \"Оптимизация батареи\",\n  \"batteryOptimizationDesc\": \"Запросить добавление в белый список энергосбережения\",\n  \"alreadyInWhitelist\": \"Уже в белом списке\",\n  \"about\": \"О программе\",\n  \"en\": \"Английский\",\n  \"ja\": \"Японский\",\n  \"ru\": \"Русский\",\n  \"zh_CN\": \"Китайский (упрощённый)\",\n  \"zh_TC\": \"Китайский (традиционный)\",\n  \"theme\": \"Тема\",\n  \"themeDesc\": \"Настройка темы и иконок\",\n  \"override\": \"Переопределение\",\n  \"overrideDesc\": \"Переопределение конфигурации прокси\",\n  \"allowLan\": \"LAN доступ\",\n  \"allowLanDesc\": \"Разрешить доступ из локальной сети\",\n  \"tun\": \"Виртуальный адаптер\",\n  \"tunDesc\": \"Перехват всего трафика устройства\",\n  \"minimizeOnExit\": \"Сворачивать при выходе\",\n  \"minimizeOnExitDesc\": \"Изменить поведение при выходе\",\n  \"autoLaunch\": \"Автозапуск\",\n  \"autoLaunchDesc\": \"Запуск при старте системы\",\n  \"smartDelayLaunch\": \"Умная задержка\",\n  \"smartDelayLaunchDesc\": \"Запуск после успешного подключения к сети\",\n  \"silentLaunch\": \"Тихий запуск\",\n  \"silentLaunchDesc\": \"Запуск в фоне без открытия окна\",\n  \"autoRun\": \"Автоподключение\",\n  \"autoRunDesc\": \"Подключаться при запуске приложения\",\n  \"logcat\": \"Сбор логов\",\n  \"logcatDesc\": \"Показать раздел логов\",\n  \"enableCrashReport\": \"Анализ сбоев\",\n  \"enableCrashReportDesc\": \"Отправка отчётов о сбоях при необходимости\",\n  \"autoCheckUpdate\": \"Автообновление\",\n  \"autoCheckUpdateDesc\": \"Проверка обновлений при запуске\",\n  \"accessControl\": \"Контроль доступа\",\n  \"accessControlDesc\": \"Настройка доступа приложений к прокси\",\n  \"clearCacheTitle\": \"Очистить кэш\",\n  \"clearCacheDesc\": \"Очистить кэш FakeIP и DNS?\",\n  \"forceGCTitle\": \"Принудительный GC\",\n  \"forceGCDesc\": \"Выполнить сборку мусора ядра? Экспериментально, используйте с осторожностью\",\n  \"fcmTip\": \"FCM зависит от устройства. Для точных результатов отключите 'Разрешить обход VPN'\",\n  \"application\": \"Приложение\",\n  \"applicationDesc\": \"Настройки приложения\",\n  \"edit\": \"Редактировать\",\n  \"confirm\": \"Подтвердить\",\n  \"update\": \"Обновить\",\n  \"add\": \"Добавить\",\n  \"save\": \"Сохранить\",\n  \"delete\": \"Удалить\",\n  \"years\": \"лет\",\n  \"months\": \"месяцев\",\n  \"hours\": \"часов\",\n  \"days\": \"дней\",\n  \"minutes\": \"минут\",\n  \"seconds\": \"секунд\",\n  \"ago\": \" назад\",\n  \"pleaseCloseTunFirst\": \"Сначала отключите виртуальный адаптер\",\n  \"pleaseCloseSystemProxyFirst\": \"Сначала отключите системный прокси\",\n  \"just\": \"только что\",\n  \"qrcode\": \"QR-код\",\n  \"qrcodeDesc\": \"Сканировать QR для получения профиля\",\n  \"clipboard\": \"Буфер обмена\",\n  \"clipboardDesc\": \"Автоматически получать ссылки из буфера обмена\",\n  \"url\": \"URL\",\n  \"urlDesc\": \"Получить профиль по URL\",\n  \"file\": \"Файл\",\n  \"fileDesc\": \"Загрузить файл конфигурации\",\n  \"name\": \"Имя\",\n  \"profileNameNullValidationDesc\": \"Введите имя профиля\",\n  \"profileUrlNullValidationDesc\": \"Введите URL профиля\",\n  \"profileUrlInvalidValidationDesc\": \"Введите корректный URL профиля\",\n  \"autoUpdate\": \"Автообновление\",\n  \"autoUpdateInterval\": \"Интервал автообновления (минуты)\",\n  \"profileAutoUpdateIntervalNullValidationDesc\": \"Введите интервал автообновления\",\n  \"profileAutoUpdateIntervalInvalidValidationDesc\": \"Введите корректный формат интервала\",\n  \"themeMode\": \"Режим темы\",\n  \"themeColor\": \"Цвет темы\",\n  \"preview\": \"Предпросмотр\",\n  \"runtimeConfig\": \"Конфигурация\",\n  \"cameraPermissionDenied\": \"Доступ к камере запрещён\",\n  \"cameraPermissionDesc\": \"Для сканирования QR-кода требуется доступ к камере. Пожалуйста, предоставьте разрешение в настройках.\",\n  \"openSettings\": \"Открыть настройки\",\n  \"retry\": \"Повторить\",\n  \"packageListPermissionRequired\": \"Эта функция требует доступа к списку установленных приложений. Предоставить разрешение?\",\n  \"packageListPermissionDenied\": \"Разрешение отклонено. Без доступа невозможно получить список приложений.\",\n  \"auto\": \"Авто\",\n  \"light\": \"Светлая\",\n  \"dark\": \"Тёмная\",\n  \"importFromURL\": \"Импорт из URL\",\n  \"submit\": \"Отправить\",\n  \"doYouWantToPass\": \"Пропустить\",\n  \"create\": \"Создать\",\n  \"defaultSort\": \"По умолчанию\",\n  \"delaySort\": \"По задержке\",\n  \"nameSort\": \"По имени\",\n  \"pleaseUploadFile\": \"Загрузите файл\",\n  \"pleaseUploadValidQrcode\": \"Загрузите корректный QR-код\",\n  \"blacklistMode\": \"Режим чёрного списка\",\n  \"whitelistMode\": \"Режим белого списка\",\n  \"filterSystemApp\": \"Скрыть системные приложения\",\n  \"cancelFilterSystemApp\": \"Показать системные приложения\",\n  \"selectAll\": \"Выбрать все\",\n  \"cancelSelectAll\": \"Отменить выбор\",\n  \"appAccessControl\": \"Контроль доступа приложений\",\n  \"accessControlAllowDesc\": \"Только выбранные приложения используют VPN\",\n  \"accessControlNotAllowDesc\": \"Выбранные приложения исключены из VPN\",\n  \"selected\": \"Выбрано\",\n  \"unableToUpdateCurrentProfileDesc\": \"Невозможно обновить текущий профиль\",\n  \"noMoreInfoDesc\": \"Нет дополнительной информации\",\n  \"profileParseErrorDesc\": \"Ошибка разбора профиля\",\n  \"proxyPort\": \"Порт прокси\",\n  \"proxyPortDesc\": \"Установить порт прослушивания Clash\",\n  \"port\": \"Порт\",\n  \"logLevel\": \"Уровень логов\",\n  \"show\": \"Показать\",\n  \"exit\": \"Выход\",\n  \"systemProxy\": \"Системный прокси\",\n  \"project\": \"Проект\",\n  \"core\": \"Ядро\",\n  \"tabAnimation\": \"Анимация вкладок\",\n  \"desc\": \"Bettbox основан на мощном и гибком прокси-ядре Mihomo (Clash.Meta) и стремится к созданию лучшего пользовательского опыта. Форк от FlClash: Улучшенный опыт, готов к работе «из коробки»\",\n  \"startVpn\": \"Запуск VPN\",\n  \"stopVpn\": \"Остановка VPN\",\n  \"discovery\": \"Доступно обновление\",\n  \"compatible\": \"Режим совместимости\",\n  \"compatibleDesc\": \"Включает полную поддержку Clash с потерей некоторых функций\",\n  \"notSelectedTip\": \"Невозможно выбрать эту группу прокси\",\n  \"tip\": \"Подсказка\",\n  \"backupAndRecovery\": \"Резервное копирование\",\n  \"backupAndRecoveryDesc\": \"Синхронизация данных через WebDAV или файл\",\n  \"account\": \"Аккаунт\",\n  \"backup\": \"Создать копию\",\n  \"recovery\": \"Восстановить\",\n  \"recoveryProfiles\": \"Только профили\",\n  \"recoveryAll\": \"Все данные\",\n  \"recoverySuccess\": \"Восстановление успешно\",\n  \"backupSuccess\": \"Резервное копирование успешно\",\n  \"noInfo\": \"Нет информации\",\n  \"pleaseBindWebDAV\": \"Привяжите WebDAV\",\n  \"bind\": \"Привязать\",\n  \"connectivity\": \"Подключение:\",\n  \"webDAVConfiguration\": \"Настройки WebDAV\",\n  \"address\": \"Адрес\",\n  \"addressHelp\": \"Адрес сервера WebDAV\",\n  \"addressTip\": \"Введите корректный адрес WebDAV\",\n  \"password\": \"Пароль\",\n  \"checkUpdate\": \"Проверить обновление\",\n  \"discoverNewVersion\": \"Доступна новая версия\",\n  \"checkUpdateError\": \"Установлена последняя версия\",\n  \"goDownload\": \"Перейти к загрузке\",\n  \"unknown\": \"Неизвестно\",\n  \"geoData\": \"Геоданные\",\n  \"externalResources\": \"Внешние ресурсы\",\n  \"checking\": \"Проверка...\",\n  \"country\": \"Регион\",\n  \"checkError\": \"Ошибка проверки\",\n  \"search\": \"Поиск\",\n  \"allowBypass\": \"Разрешить обход VPN\",\n  \"allowBypassDesc\": \"Некоторые приложения смогут обходить VPN\",\n  \"externalController\": \"Внешнее управление\",\n  \"externalControllerDesc\": \"Управление ядром через REST API\",\n  \"controlSecret\": \"Пароль управления\",\n  \"controlSecretDesc\": \"Пароль для доступа к RESTful API\",\n  \"generateSecret\": \"Сгенерировать\",\n  \"secretCopied\": \"Пароль скопирован в буфер обмена\",\n  \"ipv6Desc\": \"Включить поддержку IPv6\",\n  \"app\": \"Приложение\",\n  \"general\": \"Общие\",\n  \"vpnSystemProxyDesc\": \"Добавить HTTP-прокси к VPN\",\n  \"systemProxyDesc\": \"Настроить системный прокси\",\n  \"unifiedDelay\": \"Унифицированная задержка\",\n  \"unifiedDelayDesc\": \"Убрать задержку рукопожатия и разбора\",\n  \"tcpConcurrent\": \"TCP параллельно\",\n  \"tcpConcurrentDesc\": \"Разрешить параллельные TCP-соединения\",\n  \"geodataLoader\": \"Экономия памяти GEO\",\n  \"geodataLoaderDesc\": \"Использовать загрузчик GEO с низким потреблением памяти\",\n  \"requests\": \"Запросы\",\n  \"requestsDesc\": \"Просмотр недавних запросов\",\n  \"findProcessMode\": \"Поиск процесса\",\n  \"init\": \"Инициализация\",\n  \"infiniteTime\": \"Бессрочно\",\n  \"expirationTime\": \"Срок действия\",\n  \"connections\": \"Соединения\",\n  \"connectionsDesc\": \"Просмотр текущих соединений\",\n  \"intranetIP\": \"Локальный IP\",\n  \"view\": \"Просмотр\",\n  \"cut\": \"Вырезать\",\n  \"copy\": \"Копировать\",\n  \"paste\": \"Вставить\",\n  \"testUrl\": \"URL теста\",\n  \"startTest\": \"Тест задержки\",\n  \"addProfile\": \"Добавить профиль\",\n  \"customUrl\": \"Пользовательский URL\",\n  \"sync\": \"Синхронизировать\",\n  \"exclude\": \"Скрыть из недавних\",\n  \"excludeDesc\": \"Скрыть приложение из недавних задач\",\n  \"oneColumn\": \"1 колонка\",\n  \"twoColumns\": \"2 колонки\",\n  \"threeColumns\": \"3 колонки\",\n  \"fourColumns\": \"4 колонки\",\n  \"expand\": \"Стандартный\",\n  \"shrink\": \"Компактный\",\n  \"min\": \"Минимальный\",\n  \"tab\": \"Вкладки\",\n  \"list\": \"Список\",\n  \"delay\": \"Задержка\",\n  \"style\": \"Стиль\",\n  \"size\": \"Размер\",\n  \"delayAnimation\": \"Анимация задержки\",\n  \"delayAnimationDesc\": \"Настройка анимации при тестировании\",\n  \"noAnimation\": \"По умолчанию\",\n  \"rotatingCircle\": \"Вращающийся круг\",\n  \"pulse\": \"Пульсация\",\n  \"spinningLines\": \"Вращающиеся линии\",\n  \"threeInOut\": \"Три точки\",\n  \"threeBounce\": \"Прыгающие точки\",\n  \"circle\": \"Круг\",\n  \"fadingCircle\": \"Затухающий круг\",\n  \"fadingFour\": \"Затухающие точки\",\n  \"wave\": \"Волна\",\n  \"doubleBounce\": \"Двойной отскок\",\n  \"sort\": \"Сортировка\",\n  \"columns\": \"Колонки\",\n  \"proxiesSetting\": \"Настройки прокси\",\n  \"proxyGroup\": \"Группа прокси\",\n  \"go\": \"Перейти\",\n  \"externalLink\": \"Внешняя ссылка\",\n  \"otherContributors\": \"Другие участники\",\n  \"autoCloseConnections\": \"Автозакрытие соединений\",\n  \"autoCloseConnectionsDesc\": \"Закрывать соединения при смене узла\",\n  \"onlyStatisticsProxy\": \"Только прокси-трафик\",\n  \"onlyStatisticsProxyDesc\": \"Считать только трафик через прокси\",\n  \"pureBlackMode\": \"Чистый чёрный\",\n  \"keepAliveIntervalDesc\": \"Интервал TCP keep-alive\",\n  \"entries\": \" записей\",\n  \"local\": \"Локально\",\n  \"remote\": \"Удалённо\",\n  \"remoteBackupDesc\": \"Резервное копирование на WebDAV\",\n  \"remoteRecoveryDesc\": \"Восстановление с WebDAV\",\n  \"localBackupDesc\": \"Локальное резервное копирование\",\n  \"localRecoveryDesc\": \"Восстановление из файла\",\n  \"mode\": \"Режим\",\n  \"time\": \"Время\",\n  \"source\": \"Источник\",\n  \"allApps\": \"Все приложения\",\n  \"onlyOtherApps\": \"Только сторонние\",\n  \"action\": \"Действие\",\n  \"intelligentSelected\": \"Умный выбор\",\n  \"clipboardImport\": \"Импорт из буфера\",\n  \"clipboardExport\": \"Экспорт в буфер\",\n  \"layout\": \"Макет\",\n  \"tight\": \"Компактный\",\n  \"standard\": \"Стандартный\",\n  \"loose\": \"Свободный\",\n  \"profilesSort\": \"Сортировка профилей\",\n  \"start\": \"Запуск\",\n  \"stop\": \"Остановка\",\n  \"powerSwitch\": \"Переключатель\",\n  \"runTime\": \"Время работы\",\n  \"checkOrAddProfile\": \"Добавьте профиль\",\n  \"serviceReady\": \"Служба готова\",\n  \"appDesc\": \"Настройки приложения\",\n  \"vpnDesc\": \"Настройки VPN\",\n  \"dnsDesc\": \"Настройки DNS\",\n  \"key\": \"Ключ\",\n  \"value\": \"Значение\",\n  \"hostsDesc\": \"Добавить hosts к текущей конфигурации\",\n  \"vpnTip\": \"Перезапустите VPN для применения изменений\",\n  \"vpnEnableDesc\": \"Автоматическая маршрутизация всего трафика через VpnService\",\n  \"options\": \"Опции\",\n  \"loopback\": \"Разблокировка UWP\",\n  \"loopbackDesc\": \"Инструмент для разблокировки UWP loopback\",\n  \"providers\": \"Провайдеры\",\n  \"proxyProviders\": \"Провайдеры прокси\",\n  \"ruleProviders\": \"Провайдеры правил\",\n  \"advancedSettings\": \"Расширенные настройки\",\n  \"nodeExclusion\": \"Исключение узлов\",\n  \"nodeExclusionDesc\": \"Исключить все узлы, соответствующие шаблону\",\n  \"nodeExclusionPlaceholder\": \"HK|Гонконг|🇭🇰\",\n  \"formatError\": \"Проверьте формат\",\n  \"healthCheckTimeout\": \"Таймаут проверки\",\n  \"healthCheckTimeoutDesc\": \"Таймаут проверки работоспособности узлов\",\n  \"concurrencyLimit\": \"Лимит параллелизма\",\n  \"concurrencyLimitDesc\": \"Максимальное количество параллельных тестов задержки\",\n  \"notRecommended\": \"Не рекомендуется\",\n  \"overrideDns\": \"Переопределить DNS\",\n  \"overrideDnsDesc\": \"Включить переопределение настроек DNS в конфигурации\",\n  \"overrideTestUrl\": \"Переопределить URL теста\",\n  \"ntp\": \"NTP\",\n  \"ntpDesc\": \"Использовать службу времени NTP\",\n  \"overrideNtp\": \"Переопределить NTP\",\n  \"overrideNtpDesc\": \"Включить переопределение настроек NTP в конфигурации\",\n  \"ntpStatus\": \"Статус NTP\",\n  \"ntpStatusDesc\": \"Включить службу времени NTP\",\n  \"writeToSystem\": \"Записать в систему\",\n  \"writeToSystemDesc\": \"Требуются права администратора\",\n  \"ntpServer\": \"Сервер NTP\",\n  \"ntpPort\": \"Порт NTP\",\n  \"ntpInterval\": \"Интервал обновления\",\n  \"sniffer\": \"Sniffer\",\n  \"snifferDesc\": \"Настройка сниффинга доменов\",\n  \"overrideSniffer\": \"Переопределить Sniffer\",\n  \"overrideSnifferDesc\": \"Включить переопределение настроек Sniffer в конфигурации\",\n  \"snifferStatus\": \"Статус сниффера\",\n  \"snifferStatusDesc\": \"Включить службу сниффинга\",\n  \"forceDnsMapping\": \"Принудительное DNS-отображение\",\n  \"forceDnsMappingDesc\": \"Принудительно отображать результаты DNS на соединение\",\n  \"parsePureIp\": \"Разбор чистых IP\",\n  \"parsePureIpDesc\": \"Разбирать соединения по чистому IP\",\n  \"overrideDestination\": \"Переопределить назначение\",\n  \"overrideDestinationDesc\": \"Использовать результаты сниффинга для переопределения целевого адреса\",\n  \"httpPortSniffer\": \"HTTP порты сниффера\",\n  \"tlsPortSniffer\": \"TLS порты сниффера\",\n  \"quicPortSniffer\": \"QUIC порты сниффера\",\n  \"forceDomain\": \"Принудительный сниффинг доменов\",\n  \"skipDomain\": \"Пропустить домены\",\n  \"skipSrcAddress\": \"Пропустить IP источника\",\n  \"skipDstAddress\": \"Пропустить IP назначения\",\n  \"snifferPorts\": \"Порты\",\n  \"snifferPortsHint\": \"Например: 80, 8080-8880\",\n  \"snifferDomainHint\": \"Один домен на строку\",\n  \"snifferAddressHint\": \"Один адрес на строку\",\n  \"tunnel\": \"Туннель\",\n  \"tunnelDesc\": \"Использовать туннель перенаправления трафика\",\n  \"overrideTunnel\": \"Переопределить туннель\",\n  \"overrideTunnelDesc\": \"Включить переопределение настроек туннеля в конфигурации\",\n  \"tunnelList\": \"Список перенаправлений\",\n  \"addTunnel\": \"Добавить перенаправление\",\n  \"editTunnel\": \"Изменить перенаправление\",\n  \"deleteTunnel\": \"Удалить перенаправление\",\n  \"tunnelNetwork\": \"Сетевой протокол\",\n  \"tunnelNetworkHint\": \"Например: tcp, udp\",\n  \"tunnelAddress\": \"Адрес прослушивания\",\n  \"tunnelAddressHint\": \"Например: 127.0.0.1:6553\",\n  \"tunnelTarget\": \"Целевой адрес\",\n  \"tunnelTargetHint\": \"Например: 114.114.114.114:53\",\n  \"tunnelProxy\": \"Имя прокси\",\n  \"tunnelProxyHint\": \"Например: proxy (опционально)\",\n  \"experimental\": \"Экспериментальное\",\n  \"experimentalDesc\": \"Экспериментальные настройки, используйте с осторожностью\",\n  \"overrideExperimental\": \"Переопределить экспериментальное\",\n  \"overrideExperimentalDesc\": \"Включить переопределение экспериментальных настроек в конфигурации\",\n  \"quicGoDisableGso\": \"Отключить GSO QUIC\",\n  \"quicGoDisableGsoDesc\": \"Отключить Generic Segmentation Offload для QUIC\",\n  \"quicGoDisableEcn\": \"Отключить ECN QUIC\",\n  \"quicGoDisableEcnDesc\": \"Отключить Explicit Congestion Notification для QUIC\",\n  \"dialerIp4pConvert\": \"Включить преобразование IP4P\",\n  \"dialerIp4pConvertDesc\": \"Включить преобразование IP4P в диалере\",\n  \"status\": \"Статус\",\n  \"statusDesc\": \"Использовать системный DNS при выключении\",\n  \"preferH3Desc\": \"Приоритет HTTP/3 для DoH\",\n  \"cacheAlgorithm\": \"Алгоритм кэша\",\n  \"respectRules\": \"Следовать правилам\",\n  \"respectRulesDesc\": \"DNS-соединения следуют правилам\",\n  \"dnsMode\": \"Режим DNS\",\n  \"fakeipRange\": \"Диапазон FakeIP\",\n  \"fakeipRangeV6\": \"Диапазон FakeIPv6\",\n  \"fakeIpFilterMode\": \"Режим фильтрации FakeIP\",\n  \"fakeIpFilterModeDesc\": \"Указать режим фильтрации FakeIP\",\n  \"blacklist\": \"Чёрный список\",\n  \"whitelist\": \"Белый список\",\n  \"fakeipFilter\": \"Фильтр FakeIP\",\n  \"fakeipTtl\": \"Время жизни FakeIP\",\n  \"defaultNameserver\": \"DNS по умолчанию\",\n  \"defaultNameserverDesc\": \"Используется для разрешения DNS-серверов\",\n  \"nameserver\": \"Серверы имён\",\n  \"nameserverDesc\": \"Используется для разрешения доменов\",\n  \"useHosts\": \"Использовать hosts\",\n  \"useSystemHosts\": \"Использовать системные hosts\",\n  \"nameserverPolicy\": \"Политика DNS\",\n  \"nameserverPolicyDesc\": \"Указать политику DNS для конкретных доменов\",\n  \"proxyNameserver\": \"DNS для прокси\",\n  \"proxyNameserverDesc\": \"Используется для разрешения доменов прокси\",\n  \"directNameserver\": \"DNS для прямых\",\n  \"directNameserverDesc\": \"Используется для разрешения прямых доменов\",\n  \"directNameserverFollowPolicy\": \"Прямой DNS следует правилам\",\n  \"fallback\": \"Резервный DNS\",\n  \"fallbackDesc\": \"Обычно используются зарубежные DNS\",\n  \"fallbackFilter\": \"Фильтр резервного DNS\",\n  \"geoipCode\": \"Код GeoIP\",\n  \"ipcidr\": \"IP/CIDR\",\n  \"domain\": \"Домен\",\n  \"reset\": \"Сброс\",\n  \"action_view\": \"Показать/Скрыть\",\n  \"action_start\": \"Запуск/Остановка\",\n  \"action_mode\": \"Сменить режим\",\n  \"action_proxy\": \"Системный прокси\",\n  \"action_tun\": \"Виртуальный адаптер\",\n  \"disclaimer\": \"Отказ от ответственности\",\n  \"disclaimerDesc\": \"Это бесплатное ПО с открытым исходным кодом, предназначенное только для обучения и личного тестирования. Действия прокси-провайдеров не связаны с этим ПО. Соглашаясь, вы подтверждаете, что полностью осведомлены об этом. Если не согласны, пожалуйста, выйдите!\",\n  \"agree\": \"Согласен\",\n  \"hotkeyManagement\": \"Управление горячими клавишами\",\n  \"hotkeyManagementDesc\": \"Управление приложением с клавиатуры\",\n  \"pressKeyboard\": \"Нажмите клавиши\",\n  \"inputCorrectHotkey\": \"Введите корректное сочетание клавиш\",\n  \"hotkeyConflict\": \"Конфликт горячих клавиш\",\n  \"remove\": \"Удалить\",\n  \"noHotKey\": \"Нет горячих клавиш\",\n  \"noNetwork\": \"Нет сети\",\n  \"ipv6InboundDesc\": \"Разрешить входящие IPv6\",\n  \"exportLogs\": \"Экспорт логов\",\n  \"exportSuccess\": \"Экспорт успешен\",\n  \"iconStyle\": \"Стиль иконок\",\n  \"onlyIcon\": \"Только иконки\",\n  \"noIcon\": \"Без иконок\",\n  \"stackMode\": \"Режим стека\",\n  \"strictRoute\": \"Строгая маршрутизация\",\n  \"strictRouteDesc\": \"Использовать строгий режим маршрутизации TUN\",\n  \"icmpForwarding\": \"Пересылка ICMP\",\n  \"icmpForwardingDesc\": \"Включить поддержку ICMP Ping\",\n  \"dnsHijack\": \"Перехват DNS\",\n  \"dnsHijackDesc\": \"Перенаправить разбор в модуль DNS\",\n  \"endpointIndependentNat\": \"Улучшенный NAT\",\n  \"endpointIndependentNatDesc\": \"Включить NAT независимый от конечной точки\",\n  \"network\": \"Сеть\",\n  \"networkDesc\": \"Настройки сети\",\n  \"bypassDomain\": \"Исключить домены\",\n  \"bypassDomainDesc\": \"Работает только при включённом системном прокси\",\n  \"resetTip\": \"Сбросить настройки?\",\n  \"regExp\": \"Регулярное выражение\",\n  \"icon\": \"Иконка\",\n  \"iconConfiguration\": \"Настройка иконки\",\n  \"noData\": \"Нет данных\",\n  \"adminAutoLaunch\": \"Автозапуск от администратора\",\n  \"adminAutoLaunchDesc\": \"Автозапуск с правами администратора\",\n  \"fontFamily\": \"Шрифт\",\n  \"systemFont\": \"Системный шрифт\",\n  \"toggle\": \"Переключить\",\n  \"system\": \"Система\",\n  \"bypassPrivateRoute\": \"Обход частной сети\",\n  \"bypassPrivateRouteDesc\": \"Автоматически обходить IP-адреса частной сети\",\n  \"pleaseInputAdminPassword\": \"Введите пароль администратора\",\n  \"copyEnvVar\": \"Копировать переменные окружения\",\n  \"memoryInfo\": \"Информация о памяти\",\n  \"cancel\": \"Отмена\",\n  \"fileIsUpdate\": \"Файл изменён. Сохранить изменения?\",\n  \"profileHasUpdate\": \"Конфигурация изменена. Отключить автообновление?\",\n  \"hasCacheChange\": \"Сохранить изменения кэша?\",\n  \"copySuccess\": \"Скопировано\",\n  \"success\": \"Успех\",\n  \"copyLink\": \"Копировать ссылку\",\n  \"exportFile\": \"Экспорт файла\",\n  \"cacheCorrupt\": \"Кэш повреждён. Очистить?\",\n  \"detectionTip\": \"Зависит от сторонних API, только для справки\",\n  \"ipClickBehavior\": \"Переключение отображения\",\n  \"ipPrivacyProtection\": \"Скрыть IP\",\n  \"manualRefreshIp\": \"Обновить IP\",\n  \"tryManualRefresh\": \"Попробуйте обновить вручную\",\n  \"refreshAppList\": \"Обновить список приложений\",\n  \"refreshAppListConfirm\": \"Обновить список приложений?\",\n  \"switchToDomesticIp\": \"Получить локальный IP\",\n  \"listen\": \"Прослушивание\",\n  \"undo\": \"Отменить\",\n  \"redo\": \"Повторить\",\n  \"none\": \"Нет\",\n  \"basicConfig\": \"Конфигурация ядра\",\n  \"basicConfigDesc\": \"Глобальное изменение конфигурации ядра\",\n  \"selectedCountTitle\": \"Выбрано: {count}\",\n  \"addRule\": \"Добавить правило\",\n  \"ruleName\": \"Имя правила\",\n  \"content\": \"Содержимое\",\n  \"subRule\": \"Подправило\",\n  \"ruleTarget\": \"Цель правила\",\n  \"sourceIp\": \"IP источника\",\n  \"noResolve\": \"Не разрешать IP\",\n  \"getOriginRules\": \"Получить исходные правила\",\n  \"overrideOriginRules\": \"Переопределить исходные\",\n  \"addedOriginRules\": \"Добавить к исходным\",\n  \"enableOverride\": \"Включить переопределение\",\n  \"saveChanges\": \"Сохранить изменения?\",\n  \"generalDesc\": \"Изменить общие настройки\",\n  \"findProcessModeDesc\": \"Включить поиск процесса\",\n  \"tabAnimationDesc\": \"Работает только в мобильном режиме\",\n  \"ua\": \"User-Agent\",\n  \"navBarHapticFeedback\": \"Тактильная отдача\",\n  \"navBarHapticFeedbackDesc\": \"Вибрация при переключении нижней панели навигации\",\n  \"saveTip\": \"Сохранить изменения?\",\n  \"colorSchemes\": \"Цветовые схемы\",\n  \"palette\": \"Палитра\",\n  \"tonalSpotScheme\": \"Тональный акцент\",\n  \"fidelityScheme\": \"Высокая точность\",\n  \"monochromeScheme\": \"Монохром\",\n  \"neutralScheme\": \"Нейтральный\",\n  \"vibrantScheme\": \"Яркий\",\n  \"expressiveScheme\": \"Экспрессивный\",\n  \"contentScheme\": \"Контентная тема\",\n  \"rainbowScheme\": \"Радуга\",\n  \"fruitSaladScheme\": \"Фруктовый салат\",\n  \"developerMode\": \"Режим разработчика\",\n  \"developerModeEnableTip\": \"Режим разработчика включён.\",\n  \"messageTest\": \"Тест сообщения\",\n  \"messageTestTip\": \"Это тестовое сообщение.\",\n  \"crashTest\": \"Тест сбоя\",\n  \"clearData\": \"Очистить данные\",\n  \"zoom\": \"Масштаб\",\n  \"textScale\": \"Масштаб текста\",\n  \"lightIcon\": \"Светлая иконка\",\n  \"lightIconDesc\": \"Переключить на светлый стиль рабочего стола вручную\",\n  \"harmonyFont\": \"Шрифт Harmony\",\n  \"harmonyFontDesc\": \"Использовать оптимизированный HarmonyOS Sans\",\n  \"internet\": \"Интернет\",\n  \"systemApp\": \"Системные приложения\",\n  \"noNetworkApp\": \"Приложения без сети\",\n  \"contactMe\": \"Связаться со мной\",\n  \"recoveryStrategy\": \"Стратегия восстановления\",\n  \"recoveryStrategy_override\": \"Перезаписать\",\n  \"recoveryStrategy_compatible\": \"Совместимость\",\n  \"logsTest\": \"Тест логов\",\n  \"emptyTip\": \"{label} не может быть пустым\",\n  \"urlTip\": \"{label} должен быть URL\",\n  \"numberTip\": \"{label} должен быть числом\",\n  \"interval\": \"Интервал\",\n  \"existsTip\": \"{label} уже существует\",\n  \"deleteTip\": \"Удалить текущий {label}?\",\n  \"deleteMultipTip\": \"Удалить выбранные {label}?\",\n  \"nullTip\": \"{label} отсутствует\",\n  \"script\": \"Скрипт\",\n  \"color\": \"Цвет\",\n  \"rename\": \"Переименовать\",\n  \"unnamed\": \"Без имени\",\n  \"pleaseEnterScriptName\": \"Введите название скрипта\",\n  \"overrideInvalidTip\": \"Не действует в режиме скрипта\",\n  \"mixedPort\": \"Смешанный порт\",\n  \"socksPort\": \"Порт Socks\",\n  \"redirPort\": \"Порт перенаправления\",\n  \"tproxyPort\": \"Порт Tproxy\",\n  \"portTip\": \"{label} должен быть от 1024 до 49151\",\n  \"portConflictTip\": \"Введите разные порты\",\n  \"import\": \"Импорт\",\n  \"importFromCode\": \"Импорт из кода\",\n  \"importFailed\": \"Ошибка импорта\",\n  \"importFile\": \"Импорт из файла\",\n  \"importUrl\": \"Импорт по URL\",\n  \"autoSetSystemDns\": \"Автоматически настроить системный DNS\",\n  \"details\": \"Подробности {label}\",\n  \"creationTime\": \"Время создания\",\n  \"progress\": \"Прогресс\",\n  \"host\": \"Хост\",\n  \"destination\": \"Адрес назначения\",\n  \"destinationGeoIP\": \"Геолокация назначения\",\n  \"destinationIPASN\": \"IP ASN назначения\",\n  \"specialProxy\": \"Специальный прокси\",\n  \"specialRules\": \"Специальные правила\",\n  \"remoteDestination\": \"Удалённое назначение\",\n  \"networkType\": \"Тип сети\",\n  \"proxyChains\": \"Цепочка прокси\",\n  \"log\": \"Лог\",\n  \"connection\": \"Активные соединения\",\n  \"request\": \"Запрос\",\n  \"switchLabel\": \"Переключатель\",\n  \"noStatusAvailable\": \"Статус недоступен\",\n  \"custom\": \"Пользовательский\",\n  \"wakelock\": \"Блокировка сна\",\n  \"wakelockDescription\": \"Эта функция не требует специальных разрешений, так как использует только блокировку пробуждения экрана, а не CPU. Приложение остаётся активным в фоне, экран не гаснет автоматически, что полезно в некоторых сценариях.\",\n  \"tunEnableRequireAdmin\": \"Для включения виртуального адаптера требуются права администратора. Запустите программу от имени администратора.\",\n  \"restartTip\": \"Изменения вступят в силу после перезапуска TUN\",\n  \"restart\": \"Перезапуск\",\n  \"restartCoreTitle\": \"Перезапуск ядра\",\n  \"restartCoreDesc\": \"Перезапустить ядро вручную?\",\n  \"highRefreshRate\": \"Высокая частота обновления\",\n  \"highRefreshRateDesc\": \"Включить поддержку максимальной частоты обновления устройства\",\n  \"onlinePanel\": \"Онлайн-панель\",\n  \"openDashboard\": \"Открыть Zashboard\",\n  \"tolerance\": \"Допуск\"\n}\n"
  },
  {
    "path": "arb/intl_zh_CN.arb",
    "content": "{\n  \"rule\": \"规则\",\n  \"global\": \"全局\",\n  \"direct\": \"直连\",\n  \"dashboard\": \"首页\",\n  \"proxies\": \"代理\",\n  \"profile\": \"配置\",\n  \"profiles\": \"配置\",\n  \"tools\": \"更多\",\n  \"logs\": \"日志\",\n  \"logsDesc\": \"查看日志捕获记录\",\n  \"resources\": \"资源\",\n  \"syncAll\": \"全部同步\",\n  \"syncFailed\": \"同步失败\",\n  \"resourcesDesc\": \"外部资源相关信息\",\n  \"scriptDesc\": \"配置全局覆写脚本\",\n  \"trafficUsage\": \"流量统计\",\n  \"coreInfo\": \"内核信息\",\n  \"networkSpeed\": \"网络速度\",\n  \"outboundMode\": \"出站模式\",\n  \"networkDetection\": \"网络检测\",\n  \"upload\": \"上传\",\n  \"download\": \"下载\",\n  \"noProxy\": \"暂无代理\",\n  \"noProxyDesc\": \"请创建配置或者添加有效配置文件\",\n  \"nullProfileDesc\": \"没有配置文件,请先添加配置文件\",\n  \"settings\": \"设置\",\n  \"language\": \"语言\",\n  \"defaultText\": \"默认\",\n  \"more\": \"查看\",\n  \"other\": \"其他\",\n  \"otherSettings\": \"增强工具\",\n  \"otherSettingsDesc\": \"修改增强工具设置\",\n  \"smartAutoStop\": \"智能启停\",\n  \"smartAutoStopDesc\": \"连接指定网络后停止代理服务\",\n  \"networkMatch\": \"网络匹配\",\n  \"networkMatchHint\": \"输入IP或CIDR，最多2个，以逗号分隔\",\n  \"smartAutoStopServiceRunning\": \"智能启停服务运行中\",\n  \"serviceRunning\": \"服务正在运行中\",\n  \"coreConnected\": \"已连接\",\n  \"coreSuspended\": \"已挂起\",\n  \"invalidIpFormat\": \"无效的IP或CIDR格式\",\n  \"tooManyRules\": \"最多允许2个规则\",\n  \"dozeSuspend\": \"休眠支持\",\n  \"dozeSuspendDesc\": \"开启后同步系统Doze休眠模式\",\n  \"storeFix\": \"商店修复\",\n  \"storeFixDesc\": \"修复Google Play商店下载异常\",\n  \"disableQuic\": \"禁用QUIC\",\n  \"disableQuicDesc\": \"禁用QUIC以解决特定网络问题\",\n  \"excludeChina\": \"排除国内\",\n  \"excludeChinaDesc\": \"放行中国QUIC流量而非全部禁用\",\n  \"fcmOptimization\": \"FCM优化\",\n  \"fcmOptimizationDesc\": \"增强FCM直连时的网络稳定性\",\n  \"quickResponse\": \"快速响应\",\n  \"quickResponseDesc\": \"网络发生变化时主动断开连接\",\n  \"networkFix\": \"网络修复\",\n  \"networkFixDesc\": \"修复Windows网络检测地球图标问题\",\n  \"batteryOptimization\": \"电池优化\",\n  \"batteryOptimizationDesc\": \"请求安卓电池优化白名单权限\",\n  \"alreadyInWhitelist\": \"当前应用已在白名单内\",\n  \"about\": \"关于\",\n  \"en\": \"英语\",\n  \"ja\": \"日语\",\n  \"ru\": \"俄语\",\n  \"zh_CN\": \"简体中文\",\n  \"zh_TC\": \"繁体中文\",\n  \"theme\": \"主题\",\n  \"themeDesc\": \"设置主题色彩及图标\",\n  \"override\": \"覆写\",\n  \"overrideDesc\": \"覆写代理相关配置\",\n  \"allowLan\": \"局域网代理\",\n  \"allowLanDesc\": \"允许通过局域网访问代理\",\n  \"tun\": \"虚拟网卡\",\n  \"tunDesc\": \"接管当前设备全局流量\",\n  \"minimizeOnExit\": \"退出最小化\",\n  \"minimizeOnExitDesc\": \"修改系统默认退出事件\",\n  \"autoLaunch\": \"开机启动\",\n  \"autoLaunchDesc\": \"跟随系统自启动\",\n  \"smartDelayLaunch\": \"智能延迟\",\n  \"smartDelayLaunchDesc\": \"在网络成功连接以后启动\",\n  \"silentLaunch\": \"静默启动\",\n  \"silentLaunchDesc\": \"不打开软件直接在后台启动\",\n  \"autoRun\": \"自动连接\",\n  \"autoRunDesc\": \"应用打开后自动连接\",\n  \"logcat\": \"日志捕获\",\n  \"logcatDesc\": \"开启后将会显示日志入口\",\n  \"enableCrashReport\": \"应用崩溃分析\",\n  \"enableCrashReportDesc\": \"必要时上传应用崩溃日志\",\n  \"autoCheckUpdate\": \"自动检查更新\",\n  \"autoCheckUpdateDesc\": \"应用启动时自动检查更新\",\n  \"accessControl\": \"访问控制\",\n  \"accessControlDesc\": \"配置应用访问代理\",\n  \"clearCacheTitle\": \"清理缓存\",\n  \"clearCacheDesc\": \"是否需要清理FakeIP&DNS缓存？\",\n  \"forceGCTitle\": \"强制垃圾回收\",\n  \"forceGCDesc\": \"是否进行强制内核垃圾回收？实验性功能，请谨慎使用\",\n  \"fcmTip\": \"FCM连接和支持取决于设备本身，显示结果仅供参考。因系统权限原因，您需要关闭网络中的\\\"允许绕过VPN\\\"选项，以获得更加准确的结果\",\n  \"application\": \"应用程序\",\n  \"applicationDesc\": \"修改应用程序设置\",\n  \"edit\": \"编辑\",\n  \"confirm\": \"确定\",\n  \"update\": \"更新\",\n  \"add\": \"添加\",\n  \"save\": \"保存\",\n  \"delete\": \"删除\",\n  \"years\": \"年\",\n  \"months\": \"月\",\n  \"hours\": \"小时\",\n  \"days\": \"天\",\n  \"minutes\": \"分钟\",\n  \"seconds\": \"秒\",\n  \"ago\": \"前\",\n  \"pleaseCloseTunFirst\": \"请先关闭虚拟网卡\",\n  \"pleaseCloseSystemProxyFirst\": \"请先关闭系统代理\",\n  \"just\": \"刚刚\",\n  \"qrcode\": \"二维码\",\n  \"qrcodeDesc\": \"扫描二维码获取配置文件\",\n  \"clipboard\": \"剪切板\",\n  \"clipboardDesc\": \"自动获取剪切板订阅链接\",\n  \"url\": \"URL\",\n  \"urlDesc\": \"通过URL获取配置文件\",\n  \"file\": \"文件\",\n  \"fileDesc\": \"直接上传配置文件\",\n  \"name\": \"名称\",\n  \"profileNameNullValidationDesc\": \"请输入配置名称\",\n  \"profileUrlNullValidationDesc\": \"请输入配置URL\",\n  \"profileUrlInvalidValidationDesc\": \"请输入有效配置URL\",\n  \"autoUpdate\": \"自动更新\",\n  \"autoUpdateInterval\": \"自动更新间隔（分钟）\",\n  \"profileAutoUpdateIntervalNullValidationDesc\": \"请输入自动更新间隔时间\",\n  \"profileAutoUpdateIntervalInvalidValidationDesc\": \"请输入有效间隔时间格式\",\n  \"themeMode\": \"主题模式\",\n  \"themeColor\": \"主题色彩\",\n  \"preview\": \"预览\",\n  \"runtimeConfig\": \"运行时配置\",\n  \"cameraPermissionDenied\": \"相机权限被拒绝\",\n  \"cameraPermissionDesc\": \"扫描二维码需要相机权限，请在设置中授予相机权限。\",\n  \"openSettings\": \"打开设置\",\n  \"retry\": \"重试\",\n  \"packageListPermissionRequired\": \"此功能需要访问已安装应用列表的权限。是否授予此权限？\",\n  \"packageListPermissionDenied\": \"权限被拒绝。没有权限无法访问应用列表。\",\n  \"auto\": \"自动\",\n  \"light\": \"浅色\",\n  \"dark\": \"深色\",\n  \"importFromURL\": \"从URL导入\",\n  \"submit\": \"提交\",\n  \"doYouWantToPass\": \"是否要通过\",\n  \"create\": \"创建\",\n  \"defaultSort\": \"按默认排序\",\n  \"delaySort\": \"按延迟排序\",\n  \"nameSort\": \"按名称排序\",\n  \"pleaseUploadFile\": \"请上传文件\",\n  \"pleaseUploadValidQrcode\": \"请上传有效的二维码\",\n  \"blacklistMode\": \"黑名单模式\",\n  \"whitelistMode\": \"白名单模式\",\n  \"filterSystemApp\": \"过滤系统应用\",\n  \"cancelFilterSystemApp\": \"取消过滤系统应用\",\n  \"selectAll\": \"全选\",\n  \"cancelSelectAll\": \"取消全选\",\n  \"appAccessControl\": \"应用访问控制\",\n  \"accessControlAllowDesc\": \"只允许选中的应用进入VPN\",\n  \"accessControlNotAllowDesc\": \"选中的应用将被排除在VPN之外\",\n  \"selected\": \"已选择\",\n  \"unableToUpdateCurrentProfileDesc\": \"无法更新当前配置文件\",\n  \"noMoreInfoDesc\": \"暂无更多信息\",\n  \"profileParseErrorDesc\": \"配置文件解析错误\",\n  \"proxyPort\": \"代理端口\",\n  \"proxyPortDesc\": \"设置Clash监听端口\",\n  \"port\": \"端口\",\n  \"logLevel\": \"日志等级\",\n  \"show\": \"显示\",\n  \"exit\": \"退出\",\n  \"systemProxy\": \"系统代理\",\n  \"project\": \"项目\",\n  \"core\": \"内核\",\n  \"tabAnimation\": \"切换动画\",\n  \"desc\": \"Bettbox基于强大灵活的Mihomo(Clash.Meta)代理内核，致力于更好的体验，Forked form FlClash，Better Experience, Out of the box\",\n  \"startVpn\": \"正在启动\",\n  \"stopVpn\": \"正在停止\",\n  \"discovery\": \"发现新版本\",\n  \"compatible\": \"兼容模式\",\n  \"compatibleDesc\": \"开启将失去部分应用能力，获得全量的Clash的支持\",\n  \"notSelectedTip\": \"当前代理组无法选中\",\n  \"tip\": \"提示\",\n  \"backupAndRecovery\": \"备份与恢复\",\n  \"backupAndRecoveryDesc\": \"通过在线或本地文件同步数据\",\n  \"account\": \"账号\",\n  \"backup\": \"备份\",\n  \"recovery\": \"恢复\",\n  \"recoveryProfiles\": \"仅恢复配置文件\",\n  \"recoveryAll\": \"恢复所有数据\",\n  \"recoverySuccess\": \"恢复成功\",\n  \"backupSuccess\": \"备份成功\",\n  \"noInfo\": \"暂无信息\",\n  \"pleaseBindWebDAV\": \"请绑定WebDAV\",\n  \"bind\": \"绑定\",\n  \"connectivity\": \"连通性：\",\n  \"webDAVConfiguration\": \"WebDAV配置\",\n  \"address\": \"地址\",\n  \"addressHelp\": \"WebDAV服务器地址\",\n  \"addressTip\": \"请输入有效的WebDAV地址\",\n  \"password\": \"密码\",\n  \"checkUpdate\": \"检查更新\",\n  \"discoverNewVersion\": \"发现新版本\",\n  \"checkUpdateError\": \"当前应用已经是最新版了\",\n  \"goDownload\": \"前往下载\",\n  \"unknown\": \"未知\",\n  \"geoData\": \"地理数据\",\n  \"externalResources\": \"外部资源\",\n  \"checking\": \"检测中...\",\n  \"country\": \"区域\",\n  \"checkError\": \"检测失败\",\n  \"search\": \"搜索\",\n  \"allowBypass\": \"允许绕过VPN\",\n  \"allowBypassDesc\": \"开启后部分应用可绕过VPN\",\n  \"externalController\": \"外部控制\",\n  \"externalControllerDesc\": \"通过在线端口控制内核\",\n  \"controlSecret\": \"控制密码\",\n  \"controlSecretDesc\": \"RESTful API的访问密码\",\n  \"generateSecret\": \"生成\",\n  \"secretCopied\": \"密码已复制到剪贴板\",\n  \"ipv6Desc\": \"开启后将可以接收IPv6流量\",\n  \"app\": \"应用\",\n  \"general\": \"常规\",\n  \"vpnSystemProxyDesc\": \"为VpnService附加HTTP代理\",\n  \"systemProxyDesc\": \"设置系统代理\",\n  \"unifiedDelay\": \"统一延迟\",\n  \"unifiedDelayDesc\": \"去除握手解析等额外延迟\",\n  \"tcpConcurrent\": \"TCP并发\",\n  \"tcpConcurrentDesc\": \"开启后允许TCP并发连接\",\n  \"geodataLoader\": \"GEO节能\",\n  \"geodataLoaderDesc\": \"开启后使用GEO低内存加载器\",\n  \"requests\": \"请求\",\n  \"requestsDesc\": \"查看最近请求记录\",\n  \"findProcessMode\": \"查找进程\",\n  \"init\": \"初始化\",\n  \"infiniteTime\": \"长期有效\",\n  \"expirationTime\": \"到期时间\",\n  \"connections\": \"连接\",\n  \"connectionsDesc\": \"查看当前连接数据\",\n  \"intranetIP\": \"内网 IP\",\n  \"view\": \"查看\",\n  \"cut\": \"剪切\",\n  \"copy\": \"复制\",\n  \"paste\": \"粘贴\",\n  \"testUrl\": \"测试链接\",\n  \"startTest\": \"延迟测试\",\n  \"addProfile\": \"添加配置\",\n  \"customUrl\": \"自定义URL\",\n  \"sync\": \"同步\",\n  \"exclude\": \"后台隐藏\",\n  \"excludeDesc\": \"从最近任务中隐藏应用\",\n  \"oneColumn\": \"一列\",\n  \"twoColumns\": \"两列\",\n  \"threeColumns\": \"三列\",\n  \"fourColumns\": \"四列\",\n  \"expand\": \"标准\",\n  \"shrink\": \"紧凑\",\n  \"min\": \"最小\",\n  \"tab\": \"标签页\",\n  \"list\": \"列表\",\n  \"delay\": \"延迟\",\n  \"style\": \"风格\",\n  \"size\": \"尺寸\",\n  \"delayAnimation\": \"延迟动画\",\n  \"delayAnimationDesc\": \"自定义测试过程中的动画显示\",\n  \"noAnimation\": \"默认\",\n  \"rotatingCircle\": \"单圆自转\",\n  \"pulse\": \"脉冲律动\",\n  \"spinningLines\": \"流光旋绕\",\n  \"threeInOut\": \"三星舒合\",\n  \"threeBounce\": \"灵珠跃动\",\n  \"circle\": \"圆环流转\",\n  \"fadingCircle\": \"环影隐渐\",\n  \"fadingFour\": \"四方烁动\",\n  \"wave\": \"波浪起伏\",\n  \"doubleBounce\": \"双重弹奏\",\n  \"sort\": \"排序\",\n  \"columns\": \"列数\",\n  \"proxiesSetting\": \"代理设置\",\n  \"proxyGroup\": \"代理组\",\n  \"go\": \"前往\",\n  \"externalLink\": \"外部链接\",\n  \"otherContributors\": \"其他贡献者\",\n  \"autoCloseConnections\": \"自动关闭连接\",\n  \"autoCloseConnectionsDesc\": \"切换节点后自动关闭连接\",\n  \"onlyStatisticsProxy\": \"代理流量统计\",\n  \"onlyStatisticsProxyDesc\": \"开启后将只统计代理流量\",\n  \"pureBlackMode\": \"纯黑模式\",\n  \"keepAliveIntervalDesc\": \"TCP保持活动间隔\",\n  \"entries\": \"个条目\",\n  \"local\": \"本地\",\n  \"remote\": \"远程\",\n  \"remoteBackupDesc\": \"备份数据到WebDAV\",\n  \"remoteRecoveryDesc\": \"通过WebDAV恢复数据\",\n  \"localBackupDesc\": \"备份数据到本地\",\n  \"localRecoveryDesc\": \"通过文件恢复数据\",\n  \"mode\": \"模式\",\n  \"time\": \"时间\",\n  \"source\": \"来源\",\n  \"allApps\": \"所有应用\",\n  \"onlyOtherApps\": \"仅第三方应用\",\n  \"action\": \"操作\",\n  \"intelligentSelected\": \"智能选择\",\n  \"clipboardImport\": \"剪贴板导入\",\n  \"clipboardExport\": \"导出剪贴板\",\n  \"layout\": \"布局\",\n  \"tight\": \"紧凑\",\n  \"standard\": \"标准\",\n  \"loose\": \"宽松\",\n  \"profilesSort\": \"配置排序\",\n  \"start\": \"启动\",\n  \"stop\": \"停止\",\n  \"powerSwitch\": \"启动开关\",\n  \"runTime\": \"启动时间\",\n  \"checkOrAddProfile\": \"请先添加配置\",\n  \"serviceReady\": \"服务已就绪\",\n  \"appDesc\": \"处理应用相关设置\",\n  \"vpnDesc\": \"修改VPN相关设置\",\n  \"dnsDesc\": \"更新DNS相关设置\",\n  \"key\": \"键\",\n  \"value\": \"值\",\n  \"hostsDesc\": \"追加当前配置Hosts\",\n  \"vpnTip\": \"重启VPN后改变生效\",\n  \"vpnEnableDesc\": \"通过VpnService自动路由系统所有流量\",\n  \"options\": \"选项\",\n  \"loopback\": \"回环解锁工具\",\n  \"loopbackDesc\": \"用于UWP回环解锁\",\n  \"providers\": \"提供者\",\n  \"proxyProviders\": \"代理提供者\",\n  \"ruleProviders\": \"规则提供者\",\n  \"advancedSettings\": \"进阶设置\",\n  \"nodeExclusion\": \"节点排除\",\n  \"nodeExclusionDesc\": \"排除所有匹配到的节点\",\n  \"nodeExclusionPlaceholder\": \"HK|香港|🇭🇰\",\n  \"formatError\": \"请检查格式是否正确\",\n  \"healthCheckTimeout\": \"超时时间\",\n  \"healthCheckTimeoutDesc\": \"节点健康检查超时时间\",\n  \"concurrencyLimit\": \"并发限制\",\n  \"concurrencyLimitDesc\": \"延迟测试的最大并发数量\",\n  \"notRecommended\": \"不推荐\",\n  \"overrideDns\": \"覆写DNS\",\n  \"overrideDnsDesc\": \"开启后将覆盖配置中的DNS选项\",\n  \"overrideTestUrl\": \"覆盖配置\",\n  \"ntp\": \"NTP\",\n  \"ntpDesc\": \"使用NTP时间服务\",\n  \"overrideNtp\": \"覆写NTP\",\n  \"overrideNtpDesc\": \"开启后将覆盖配置中的NTP选项\",\n  \"ntpStatus\": \"状态\",\n  \"ntpStatusDesc\": \"开启NTP时间服务\",\n  \"writeToSystem\": \"写入系统\",\n  \"writeToSystemDesc\": \"需要管理员权限\",\n  \"ntpServer\": \"服务器\",\n  \"ntpPort\": \"端口\",\n  \"ntpInterval\": \"更新时间\",\n  \"sniffer\": \"Sniffer\",\n  \"snifferDesc\": \"修改域名嗅探配置\",\n  \"overrideSniffer\": \"覆写Sniffer\",\n  \"overrideSnifferDesc\": \"开启后将覆盖配置中的Sniffer选项\",\n  \"snifferStatus\": \"状态\",\n  \"snifferStatusDesc\": \"开启嗅探服务设置\",\n  \"forceDnsMapping\": \"强制 DNS 映射\",\n  \"forceDnsMappingDesc\": \"强制将 DNS 查询结果映射到连接\",\n  \"parsePureIp\": \"解析纯 IP 连接\",\n  \"parsePureIpDesc\": \"解析纯 IP 连接\",\n  \"overrideDestination\": \"覆盖目标地址\",\n  \"overrideDestinationDesc\": \"使用嗅探结果覆盖连接目标地址\",\n  \"httpPortSniffer\": \"HTTP 端口嗅探\",\n  \"tlsPortSniffer\": \"TLS 端口嗅探\",\n  \"quicPortSniffer\": \"QUIC 端口嗅探\",\n  \"forceDomain\": \"强制嗅探域名\",\n  \"skipDomain\": \"跳过域名\",\n  \"skipSrcAddress\": \"跳过来源 IP\",\n  \"skipDstAddress\": \"跳过目标 IP\",\n  \"snifferPorts\": \"端口\",\n  \"snifferPortsHint\": \"例如: 80, 8080-8880\",\n  \"snifferDomainHint\": \"每行一个域名\",\n  \"snifferAddressHint\": \"每行一个地址\",\n  \"tunnel\": \"Tunnel\",\n  \"tunnelDesc\": \"使用流量转发隧道\",\n  \"overrideTunnel\": \"覆写Tunnel\",\n  \"overrideTunnelDesc\": \"开启后将覆盖配置中的Tunnel选项\",\n  \"tunnelList\": \"转发列表\",\n  \"addTunnel\": \"添加转发\",\n  \"editTunnel\": \"编辑转发\",\n  \"deleteTunnel\": \"删除转发\",\n  \"tunnelNetwork\": \"网络协议\",\n  \"tunnelNetworkHint\": \"例如: tcp, udp\",\n  \"tunnelAddress\": \"监听地址\",\n  \"tunnelAddressHint\": \"例如: 127.0.0.1:6553\",\n  \"tunnelTarget\": \"目标地址\",\n  \"tunnelTargetHint\": \"例如: 114.114.114.114:53\",\n  \"tunnelProxy\": \"代理名称\",\n  \"tunnelProxyHint\": \"例如: proxy (可选)\",\n  \"experimental\": \"Experimental\",\n  \"experimentalDesc\": \"实验性配置请谨慎使用\",\n  \"overrideExperimental\": \"覆写Experimental\",\n  \"overrideExperimentalDesc\": \"开启后将覆盖配置中的实验性配置\",\n  \"quicGoDisableGso\": \"禁用QUIC通用分段卸载\",\n  \"quicGoDisableGsoDesc\": \"禁用 QUIC 的通用分段卸载功能\",\n  \"quicGoDisableEcn\": \"禁用QUIC显式拥塞通知\",\n  \"quicGoDisableEcnDesc\": \"禁用 QUIC 的显式拥塞通知功能\",\n  \"dialerIp4pConvert\": \"启用拨号IP4P地址转换\",\n  \"dialerIp4pConvertDesc\": \"启用拨号器的 IP4P 地址转换功能\",\n  \"status\": \"状态\",\n  \"statusDesc\": \"关闭后将使用系统DNS\",\n  \"preferH3Desc\": \"优先使用DOH的http/3\",\n  \"cacheAlgorithm\": \"缓存算法\",\n  \"respectRules\": \"遵守规则\",\n  \"respectRulesDesc\": \"DNS连接跟随Rules\",\n  \"dnsMode\": \"DNS模式\",\n  \"fakeipRange\": \"FakeIP范围\",\n  \"fakeipRangeV6\": \"FakeIPv6范围\",\n  \"fakeIpFilterMode\": \"FakeIP过滤模式\",\n  \"fakeIpFilterModeDesc\": \"指定FakeIP过滤模式\",\n  \"blacklist\": \"黑名单\",\n  \"whitelist\": \"白名单\",\n  \"fakeipFilter\": \"FakeIP过滤列表\",\n  \"fakeipTtl\": \"FakeIP有效时间\",\n  \"defaultNameserver\": \"默认域名服务器\",\n  \"defaultNameserverDesc\": \"用于解析DNS服务器\",\n  \"nameserver\": \"域名服务器\",\n  \"nameserverDesc\": \"用于解析域名\",\n  \"useHosts\": \"使用Hosts\",\n  \"useSystemHosts\": \"使用系统Hosts\",\n  \"nameserverPolicy\": \"域名服务器策略\",\n  \"nameserverPolicyDesc\": \"指定对应域名服务器策略\",\n  \"proxyNameserver\": \"代理域名服务器\",\n  \"proxyNameserverDesc\": \"用于解析代理节点的域名\",\n  \"directNameserver\": \"直连域名服务器\",\n  \"directNameserverDesc\": \"用于解析直连出口的域名\",\n  \"directNameserverFollowPolicy\": \"直连DNS遵循规则\",\n  \"fallback\": \"Fallback\",\n  \"fallbackDesc\": \"一般情况下使用境外DNS\",\n  \"fallbackFilter\": \"Fallback过滤\",\n  \"geoipCode\": \"Geoip代码\",\n  \"ipcidr\": \"IP/掩码\",\n  \"domain\": \"域名\",\n  \"reset\": \"重置\",\n  \"action_view\": \"显示/隐藏\",\n  \"action_start\": \"启动/停止\",\n  \"action_mode\": \"切换模式\",\n  \"action_proxy\": \"系统代理\",\n  \"action_tun\": \"虚拟网卡\",\n  \"disclaimer\": \"免责声明\",\n  \"disclaimerDesc\": \"本软件为开源免费软件，仅供学习交流等非商业性质的个人测试使用，代理服务商的行为均与本软件无关，同意声明代表您已完全知晓并确认了这一点，如不同意，请选择退出！\",\n  \"agree\": \"同意\",\n  \"hotkeyManagement\": \"快捷键管理\",\n  \"hotkeyManagementDesc\": \"使用键盘控制应用程序\",\n  \"pressKeyboard\": \"请按下按键\",\n  \"inputCorrectHotkey\": \"请输入正确的快捷键\",\n  \"hotkeyConflict\": \"快捷键冲突\",\n  \"remove\": \"移除\",\n  \"noHotKey\": \"暂无快捷键\",\n  \"noNetwork\": \"无网络\",\n  \"ipv6InboundDesc\": \"允许IPv6入站\",\n  \"exportLogs\": \"导出日志\",\n  \"exportSuccess\": \"导出成功\",\n  \"iconStyle\": \"图标样式\",\n  \"onlyIcon\": \"仅图标\",\n  \"noIcon\": \"无图标\",\n  \"stackMode\": \"栈模式\",\n  \"strictRoute\": \"严格路由\",\n  \"strictRouteDesc\": \"使用TUN严格路由模式\",\n  \"icmpForwarding\": \"ICMP转发\",\n  \"icmpForwardingDesc\": \"开启后将支持ICMP Ping\",\n  \"dnsHijack\": \"DNS劫持\",\n  \"dnsHijackDesc\": \"将解析导入内部DNS模块\",\n  \"endpointIndependentNat\": \"NAT增强\",\n  \"endpointIndependentNatDesc\": \"启用独立于端点的NAT\",\n  \"network\": \"网络\",\n  \"networkDesc\": \"修改网络相关设置\",\n  \"bypassDomain\": \"排除域名\",\n  \"bypassDomainDesc\": \"仅在系统代理启用时生效\",\n  \"resetTip\": \"确定要重置吗?\",\n  \"regExp\": \"正则\",\n  \"icon\": \"图片\",\n  \"iconConfiguration\": \"图片配置\",\n  \"noData\": \"暂无数据\",\n  \"adminAutoLaunch\": \"管理员自启动\",\n  \"adminAutoLaunchDesc\": \"使用管理员模式开机自启动\",\n  \"fontFamily\": \"字体\",\n  \"systemFont\": \"系统字体\",\n  \"toggle\": \"切换\",\n  \"system\": \"系统\",\n  \"bypassPrivateRoute\": \"绕过私有网络\",\n  \"bypassPrivateRouteDesc\": \"自动绕过私有网络IP地址\",\n  \"pleaseInputAdminPassword\": \"请输入管理员密码\",\n  \"copyEnvVar\": \"复制环境变量\",\n  \"memoryInfo\": \"内存信息\",\n  \"cancel\": \"取消\",\n  \"fileIsUpdate\": \"文件有修改，是否保存修改\",\n  \"profileHasUpdate\": \"配置文件已经修改,是否关闭自动更新 \",\n  \"hasCacheChange\": \"是否缓存修改\",\n  \"copySuccess\": \"复制成功\",\n  \"success\": \"Success\",\n  \"copyLink\": \"复制链接\",\n  \"exportFile\": \"导出文件\",\n  \"cacheCorrupt\": \"缓存已损坏，是否清空？\",\n  \"detectionTip\": \"依赖第三方api，仅供参考\",\n  \"ipClickBehavior\": \"显示切换\",\n  \"ipPrivacyProtection\": \"隐藏IP显示\",\n  \"manualRefreshIp\": \"重新获取IP\",\n  \"tryManualRefresh\": \"请尝试手动刷新\",\n  \"refreshAppList\": \"刷新应用列表\",\n  \"refreshAppListConfirm\": \"是否刷新应用列表？\",\n  \"switchToDomesticIp\": \"获取国内IP\",\n  \"listen\": \"监听\",\n  \"undo\": \"撤销\",\n  \"redo\": \"重做\",\n  \"none\": \"无\",\n  \"basicConfig\": \"内核配置\",\n  \"basicConfigDesc\": \"全局修改内核配置\",\n  \"selectedCountTitle\": \"已选择 {count} 项\",\n  \"addRule\": \"添加规则\",\n  \"ruleName\": \"规则名称\",\n  \"content\": \"内容\",\n  \"subRule\": \"子规则\",\n  \"ruleTarget\": \"规则目标\",\n  \"sourceIp\": \"源IP\",\n  \"noResolve\": \"不解析IP\",\n  \"getOriginRules\": \"获取原始规则\",\n  \"overrideOriginRules\": \"覆盖原始规则\",\n  \"addedOriginRules\": \"附加到原始规则\",\n  \"enableOverride\": \"启用覆写\",\n  \"saveChanges\": \"是否保存更改？\",\n  \"generalDesc\": \"修改通用设置\",\n  \"findProcessModeDesc\": \"开启后会将可以查找进程\",\n  \"tabAnimationDesc\": \"仅在部分移动视图中有效\",\n  \"navBarHapticFeedback\": \"触感反馈\",\n  \"navBarHapticFeedbackDesc\": \"底部导航栏切换震动反馈\",\n  \"saveTip\": \"确定要保存吗？\",\n  \"colorSchemes\": \"配色方案\",\n  \"palette\": \"调色板\",\n  \"tonalSpotScheme\": \"调性点缀\",\n  \"fidelityScheme\": \"高保真\",\n  \"monochromeScheme\": \"单色\",\n  \"neutralScheme\": \"中性\",\n  \"vibrantScheme\": \"活力\",\n  \"expressiveScheme\": \"表现力\",\n  \"contentScheme\": \"内容主题\",\n  \"rainbowScheme\": \"彩虹\",\n  \"fruitSaladScheme\": \"果缤纷\",\n  \"developerMode\": \"开发者模式\",\n  \"developerModeEnableTip\": \"开发者模式已启用。\",\n  \"messageTest\": \"消息测试\",\n  \"messageTestTip\": \"这是一条消息。\",\n  \"crashTest\": \"崩溃测试\",\n  \"clearData\": \"清除数据\",\n  \"zoom\": \"缩放\",\n  \"textScale\": \"文本缩放\",\n  \"lightIcon\": \"丹青留白\",\n  \"lightIconDesc\": \"手动切换浅色系桌面应用图标\",\n  \"harmonyFont\": \"鸿蒙字体\",\n  \"harmonyFontDesc\": \"使用优化的HarmonyOS Sans\",\n  \"internet\": \"互联网\",\n  \"systemApp\": \"系统应用\",\n  \"noNetworkApp\": \"无网络应用\",\n  \"contactMe\": \"联系我\",\n  \"recoveryStrategy\": \"恢复策略\",\n  \"recoveryStrategy_override\": \"覆盖\",\n  \"recoveryStrategy_compatible\": \"兼容\",\n  \"logsTest\": \"日志测试\",\n  \"emptyTip\": \"{label}不能为空\",\n  \"urlTip\": \"{label}必须为URL\",\n  \"numberTip\": \"{label}必须为数字\",\n  \"interval\": \"间隔\",\n  \"existsTip\": \"{label}当前已存在\",\n  \"deleteTip\": \"确定删除当前{label}吗？\",\n  \"deleteMultipTip\": \"确定删除选中的{label}吗？\",\n  \"nullTip\": \"暂无{label}\",\n  \"script\": \"脚本\",\n  \"color\": \"颜色\",\n  \"rename\": \"重命名\",\n  \"unnamed\": \"未命名\",\n  \"pleaseEnterScriptName\": \"请输入脚本名称\",\n  \"overrideInvalidTip\": \"在脚本模式下不生效\",\n  \"mixedPort\": \"混合端口\",\n  \"socksPort\": \"Socks端口\",\n  \"redirPort\": \"Redir端口\",\n  \"tproxyPort\": \"Tproxy端口\",\n  \"portTip\": \"{label} 必须在 1024 到 49151 之间\",\n  \"portConflictTip\": \"请输入不同的端口\",\n  \"import\": \"导入\",\n  \"importFromCode\": \"通过代码导入\",\n  \"importFailed\": \"导入失败\",\n  \"importFile\": \"通过文件导入\",\n  \"importUrl\": \"通过URL导入\",\n  \"autoSetSystemDns\": \"自动设置系统DNS\",\n  \"details\": \"{label}详情\",\n  \"creationTime\": \"创建时间\",\n  \"progress\": \"进程\",\n  \"host\": \"主机\",\n  \"destination\": \"目标地址\",\n  \"destinationGeoIP\": \"目标地理定位\",\n  \"destinationIPASN\": \"目标IP ASN\",\n  \"specialProxy\": \"特殊代理\",\n  \"specialRules\": \"特殊规则\",\n  \"remoteDestination\": \"远程目标\",\n  \"networkType\": \"网络类型\",\n  \"proxyChains\": \"代理链\",\n  \"log\": \"日志\",\n  \"connection\": \"活跃连接\",\n  \"request\": \"请求\",\n  \"switchLabel\": \"开关\",\n  \"noStatusAvailable\": \"未获取到状态\",\n  \"onlinePanel\": \"在线面板\",\n  \"openDashboard\": \"打开 Zashboard\",\n  \"custom\": \"自定义\",\n  \"wakelock\": \"亮屏锁\",\n  \"wakelockDescription\": \"本功能不需要任何特殊权限，因为它仅启用屏幕唤醒锁，而不是任何CPU唤醒锁，应用会在后台保持必要的活跃，且屏幕不会自动熄灭，这在一些场景下会很有用\",\n  \"tunEnableRequireAdmin\": \"启用虚拟网卡需要管理员权限，请以管理员身份运行程序\",\n  \"restartTip\": \"重启TUN后改变生效\",\n  \"restart\": \"重启\",\n  \"restartCoreTitle\": \"重启内核\",\n  \"restartCoreDesc\": \"是否手动重启内核？\",\n  \"highRefreshRate\": \"高刷新率\",\n  \"highRefreshRateDesc\": \"启用设备最高刷新率支持\"\n}\n"
  },
  {
    "path": "arb/intl_zh_TC.arb",
    "content": "{\n  \"rule\": \"規則\",\n  \"global\": \"全域\",\n  \"direct\": \"直連\",\n  \"dashboard\": \"首頁\",\n  \"proxies\": \"代理\",\n  \"profile\": \"配置\",\n  \"profiles\": \"配置\",\n  \"tools\": \"更多\",\n  \"logs\": \"日誌\",\n  \"logsDesc\": \"查看日誌擷取記錄\",\n  \"resources\": \"資源\",\n  \"syncAll\": \"全部同步\",\n  \"syncFailed\": \"同步失敗\",\n  \"resourcesDesc\": \"外部資源相關資訊\",\n  \"scriptDesc\": \"配置全局覆寫腳本\",\n  \"trafficUsage\": \"流量統計\",\n  \"coreInfo\": \"內核資訊\",\n  \"networkSpeed\": \"網路速度\",\n  \"outboundMode\": \"出站模式\",\n  \"networkDetection\": \"網路檢測\",\n  \"upload\": \"上傳\",\n  \"download\": \"下載\",\n  \"noProxy\": \"暫無代理\",\n  \"noProxyDesc\": \"請建立配置或新增有效的設定檔\",\n  \"nullProfileDesc\": \"沒有設定檔，請先新增設定檔\",\n  \"settings\": \"設定\",\n  \"language\": \"語言\",\n  \"defaultText\": \"預設\",\n  \"more\": \"查看\",\n  \"other\": \"其他\",\n  \"otherSettings\": \"增強工具\",\n  \"otherSettingsDesc\": \"修改增強工具設定\",\n  \"smartAutoStop\": \"智慧啟停\",\n  \"smartAutoStopDesc\": \"連線指定網路後停止代理服務\",\n  \"networkMatch\": \"網路匹配\",\n  \"networkMatchHint\": \"輸入 IP 或 CIDR，最多 2 個，以逗號分隔\",\n  \"smartAutoStopServiceRunning\": \"智慧啟停服務執行中\",\n  \"serviceRunning\": \"服務正在執行中\",\n  \"coreConnected\": \"已連線\",\n  \"coreSuspended\": \"已掛起\",\n  \"invalidIpFormat\": \"無效的 IP 或 CIDR 格式\",\n  \"tooManyRules\": \"最多允許 2 個規則\",\n  \"dozeSuspend\": \"休眠支援\",\n  \"dozeSuspendDesc\": \"開啟後同步系統 Doze 休眠模式\",\n  \"storeFix\": \"商店修復\",\n  \"storeFixDesc\": \"修復 Google Play 商店下載異常\",\n  \"disableQuic\": \"禁用QUIC\",\n  \"disableQuicDesc\": \"禁用QUIC以解決特定網路問題\",\n  \"excludeChina\": \"排除國內\",\n  \"excludeChinaDesc\": \"放行中國QUIC流量而非全部禁用\",\n  \"fcmOptimization\": \"FCM 優化\",\n  \"fcmOptimizationDesc\": \"增強 FCM 直連時的網路穩定性\",\n  \"quickResponse\": \"快速響應\",\n  \"quickResponseDesc\": \"網路發生變化時主動斷開連接\",\n  \"networkFix\": \"網路修復\",\n  \"networkFixDesc\": \"修復 Windows 網路檢測地球圖示問題\",\n  \"batteryOptimization\": \"電池最佳化\",\n  \"batteryOptimizationDesc\": \"請求 Android 電池最佳化白名單權限\",\n  \"alreadyInWhitelist\": \"目前應用程式已在白名單內\",\n  \"about\": \"關於\",\n  \"en\": \"英語\",\n  \"ja\": \"日語\",\n  \"ru\": \"俄語\",\n  \"zh_CN\": \"簡體中文\",\n  \"zh_TC\": \"繁體中文\",\n  \"theme\": \"主題\",\n  \"themeDesc\": \"設定主題色彩及圖示\",\n  \"override\": \"覆寫\",\n  \"overrideDesc\": \"覆寫代理相關配置\",\n  \"allowLan\": \"區域網路代理\",\n  \"allowLanDesc\": \"允許透過區域網路存取代理\",\n  \"tun\": \"虛擬網卡\",\n  \"tunDesc\": \"接管目前裝置全域流量\",\n  \"minimizeOnExit\": \"退出最小化\",\n  \"minimizeOnExitDesc\": \"修改系統預設退出事件\",\n  \"autoLaunch\": \"開機啟動\",\n  \"autoLaunchDesc\": \"跟隨系統自動啟動\",\n  \"smartDelayLaunch\": \"智慧延遲\",\n  \"smartDelayLaunchDesc\": \"在網路成功連線以後啟動\",\n  \"silentLaunch\": \"靜默啟動\",\n  \"silentLaunchDesc\": \"不打開軟體直接在背景啟動\",\n  \"autoRun\": \"自動連線\",\n  \"autoRunDesc\": \"應用打開後自動連線\",\n  \"logcat\": \"日誌捕獲\",\n  \"logcatDesc\": \"開啟後將會顯示日誌入口\",\n  \"enableCrashReport\": \"應用崩潰分析\",\n  \"enableCrashReportDesc\": \"必要時上傳應用崩潰日誌\",\n  \"autoCheckUpdate\": \"自動檢查更新\",\n  \"autoCheckUpdateDesc\": \"應用啟動時自動檢查更新\",\n  \"accessControl\": \"存取控制\",\n  \"accessControlDesc\": \"設定應用存取代理\",\n  \"clearCacheTitle\": \"清理快取\",\n  \"clearCacheDesc\": \"是否需要清理 FakeIP & DNS 快取？\",\n  \"forceGCTitle\": \"強制垃圾回收\",\n  \"forceGCDesc\": \"是否進行強制內核垃圾回收？實驗性功能，請謹慎使用\",\n  \"fcmTip\": \"FCM 連線和支援取決於裝置本身，顯示結果僅供參考。因系統權限原因，您需要關閉網路中的 \\\"允許繞過 VPN\\\" 選項，以獲得更加準確的結果\",\n  \"application\": \"應用程式\",\n  \"applicationDesc\": \"修改應用程式設定\",\n  \"edit\": \"編輯\",\n  \"confirm\": \"確定\",\n  \"update\": \"更新\",\n  \"add\": \"新增\",\n  \"save\": \"儲存\",\n  \"delete\": \"刪除\",\n  \"years\": \"年\",\n  \"months\": \"月\",\n  \"hours\": \"小時\",\n  \"days\": \"天\",\n  \"minutes\": \"分鐘\",\n  \"seconds\": \"秒\",\n  \"ago\": \"前\",\n  \"pleaseCloseTunFirst\": \"請先關閉虛擬網卡\",\n  \"pleaseCloseSystemProxyFirst\": \"請先關閉系統代理\",\n  \"just\": \"剛剛\",\n  \"qrcode\": \"二維碼\",\n  \"qrcodeDesc\": \"掃描二維碼獲取設定檔\",\n  \"clipboard\": \"剪貼簿\",\n  \"clipboardDesc\": \"自動獲取剪貼簿訂閱連結\",\n  \"url\": \"URL\",\n  \"urlDesc\": \"透過 URL 獲取設定檔\",\n  \"file\": \"檔案\",\n  \"fileDesc\": \"直接上傳設定檔\",\n  \"name\": \"名稱\",\n  \"profileNameNullValidationDesc\": \"請輸入配置名稱\",\n  \"profileUrlNullValidationDesc\": \"請輸入配置 URL\",\n  \"profileUrlInvalidValidationDesc\": \"請輸入有效配置 URL\",\n  \"autoUpdate\": \"自動更新\",\n  \"autoUpdateInterval\": \"自動更新間隔（分鐘）\",\n  \"profileAutoUpdateIntervalNullValidationDesc\": \"請輸入自動更新間隔時間\",\n  \"profileAutoUpdateIntervalInvalidValidationDesc\": \"請輸入有效間隔時間格式\",\n  \"themeMode\": \"主題模式\",\n  \"themeColor\": \"主題色彩\",\n  \"preview\": \"預覽\",\n  \"runtimeConfig\": \"執行時配置\",\n  \"cameraPermissionDenied\": \"相機權限被拒絕\",\n  \"cameraPermissionDesc\": \"掃描二維碼需要相機權限，請在設定中授予相機權限。\",\n  \"openSettings\": \"打開設定\",\n  \"retry\": \"重試\",\n  \"packageListPermissionRequired\": \"此功能需要存取已安裝應用程式清單的權限。是否授予此權限？\",\n  \"packageListPermissionDenied\": \"權限被拒絕。沒有權限無法存取應用程式清單。\",\n  \"auto\": \"自動\",\n  \"light\": \"淺色\",\n  \"dark\": \"深色\",\n  \"importFromURL\": \"從 URL 匯入\",\n  \"submit\": \"提交\",\n  \"doYouWantToPass\": \"是否要通過\",\n  \"create\": \"建立\",\n  \"defaultSort\": \"按預設排序\",\n  \"delaySort\": \"按延遲排序\",\n  \"nameSort\": \"按名稱排序\",\n  \"pleaseUploadFile\": \"請上傳檔案\",\n  \"pleaseUploadValidQrcode\": \"請上傳有效的二維碼\",\n  \"blacklistMode\": \"黑名單模式\",\n  \"whitelistMode\": \"白名單模式\",\n  \"filterSystemApp\": \"過濾系統應用程式\",\n  \"cancelFilterSystemApp\": \"取消過濾系統應用程式\",\n  \"selectAll\": \"全選\",\n  \"cancelSelectAll\": \"取消全選\",\n  \"appAccessControl\": \"應用存取控制\",\n  \"accessControlAllowDesc\": \"只允許選取的應用程式進入 VPN\",\n  \"accessControlNotAllowDesc\": \"選取的應用程式將被排除在VPN之外\",\n  \"selected\": \"已選擇\",\n  \"unableToUpdateCurrentProfileDesc\": \"無法更新目前的設定檔\",\n  \"noMoreInfoDesc\": \"暫無更多資訊\",\n  \"profileParseErrorDesc\": \"設定檔解析錯誤\",\n  \"proxyPort\": \"代理連接埠\",\n  \"proxyPortDesc\": \"設定 Clash 監聽連接埠\",\n  \"port\": \"連接埠\",\n  \"logLevel\": \"日誌等級\",\n  \"show\": \"顯示\",\n  \"exit\": \"退出\",\n  \"systemProxy\": \"系統代理\",\n  \"project\": \"專案\",\n  \"core\": \"內核\",\n  \"tabAnimation\": \"切換動畫\",\n  \"desc\": \"Bettbox 基於強大靈活的 Mihomo (Clash.Meta) 代理內核，致力於提供更好的體驗，Forked from FlClash，Better Experience, Out of the box\",\n  \"startVpn\": \"正在啟動\",\n  \"stopVpn\": \"正在停止\",\n  \"discovery\": \"發現新版本\",\n  \"compatible\": \"相容模式\",\n  \"compatibleDesc\": \"開啟將失去部分應用能力，獲得全量的 Clash 支援\",\n  \"notSelectedTip\": \"目前的代理群組無法選取\",\n  \"tip\": \"提示\",\n  \"backupAndRecovery\": \"備份與還原\",\n  \"backupAndRecoveryDesc\": \"透過線上或本機檔案同步資料\",\n  \"account\": \"帳號\",\n  \"backup\": \"備份\",\n  \"recovery\": \"還原\",\n  \"recoveryProfiles\": \"僅還原設定檔\",\n  \"recoveryAll\": \"還原所有資料\",\n  \"recoverySuccess\": \"還原成功\",\n  \"backupSuccess\": \"備份成功\",\n  \"noInfo\": \"暫無資訊\",\n  \"pleaseBindWebDAV\": \"請綁定 WebDAV\",\n  \"bind\": \"綁定\",\n  \"connectivity\": \"連通性：\",\n  \"webDAVConfiguration\": \"WebDAV 設定\",\n  \"address\": \"地址\",\n  \"addressHelp\": \"WebDAV 伺服器地址\",\n  \"addressTip\": \"請輸入有效的 WebDAV 地址\",\n  \"password\": \"密碼\",\n  \"checkUpdate\": \"檢查更新\",\n  \"discoverNewVersion\": \"發現新版本\",\n  \"checkUpdateError\": \"目前的應用程式已經是最新版了\",\n  \"goDownload\": \"前往下載\",\n  \"unknown\": \"未知\",\n  \"geoData\": \"地理資料\",\n  \"externalResources\": \"外部資源\",\n  \"checking\": \"檢測中...\",\n  \"country\": \"區域\",\n  \"checkError\": \"檢測失敗\",\n  \"search\": \"搜尋\",\n  \"allowBypass\": \"允許繞過 VPN\",\n  \"allowBypassDesc\": \"開啟後部分應用程式可繞過 VPN\",\n  \"externalController\": \"外部控制\",\n  \"externalControllerDesc\": \"透過線上連接埠控制內核\",\n  \"controlSecret\": \"控制密碼\",\n  \"controlSecretDesc\": \"RESTful API 的存取密碼\",\n  \"generateSecret\": \"產生\",\n  \"secretCopied\": \"密碼已複製到剪貼簿\",\n  \"ipv6Desc\": \"開啟後將可以接收 IPv6 流量\",\n  \"app\": \"應用\",\n  \"general\": \"一般\",\n  \"vpnSystemProxyDesc\": \"為 VpnService 附加 HTTP 代理\",\n  \"systemProxyDesc\": \"設定系統代理\",\n  \"unifiedDelay\": \"統一延遲\",\n  \"unifiedDelayDesc\": \"去除交握解析等額外延遲\",\n  \"tcpConcurrent\": \"TCP 並發\",\n  \"tcpConcurrentDesc\": \"開啟後允許 TCP 並發連線\",\n  \"geodataLoader\": \"GEO 節能\",\n  \"geodataLoaderDesc\": \"開啟後使用 GEO 低記憶體載入器\",\n  \"requests\": \"請求\",\n  \"requestsDesc\": \"查看最近請求記錄\",\n  \"findProcessMode\": \"尋找處理程序\",\n  \"init\": \"初始化\",\n  \"infiniteTime\": \"長期有效\",\n  \"expirationTime\": \"到期時間\",\n  \"connections\": \"連線\",\n  \"connectionsDesc\": \"查看目前連線資料\",\n  \"intranetIP\": \"內網 IP\",\n  \"view\": \"查看\",\n  \"cut\": \"剪下\",\n  \"copy\": \"複製\",\n  \"paste\": \"貼上\",\n  \"testUrl\": \"測試連結\",\n  \"startTest\": \"延遲測試\",\n  \"addProfile\": \"新增配置\",\n  \"customUrl\": \"自訂 URL\",\n  \"sync\": \"同步\",\n  \"exclude\": \"背景隱藏\",\n  \"excludeDesc\": \"從最近任務中隱藏應用程式\",\n  \"oneColumn\": \"一列\",\n  \"twoColumns\": \"兩列\",\n  \"threeColumns\": \"三列\",\n  \"fourColumns\": \"四列\",\n  \"expand\": \"標準\",\n  \"shrink\": \"緊湊\",\n  \"min\": \"最小\",\n  \"tab\": \"分頁\",\n  \"list\": \"清單\",\n  \"delay\": \"延遲\",\n  \"style\": \"風格\",\n  \"size\": \"尺寸\",\n  \"delayAnimation\": \"延遲動畫\",\n  \"delayAnimationDesc\": \"自訂測試過程中的動畫顯示\",\n  \"noAnimation\": \"預設\",\n  \"rotatingCircle\": \"單圓自轉\",\n  \"pulse\": \"脈衝律動\",\n  \"spinningLines\": \"流光旋繞\",\n  \"threeInOut\": \"三星舒合\",\n  \"threeBounce\": \"靈珠躍動\",\n  \"circle\": \"圓環流轉\",\n  \"fadingCircle\": \"環影隱漸\",\n  \"fadingFour\": \"四方爍動\",\n  \"wave\": \"波浪起伏\",\n  \"doubleBounce\": \"雙重彈奏\",\n  \"sort\": \"排序\",\n  \"columns\": \"列數\",\n  \"proxiesSetting\": \"代理設定\",\n  \"proxyGroup\": \"代理群組\",\n  \"go\": \"前往\",\n  \"externalLink\": \"外部連結\",\n  \"otherContributors\": \"其他貢獻者\",\n  \"autoCloseConnections\": \"自動關閉連線\",\n  \"autoCloseConnectionsDesc\": \"切換節點後自動關閉連線\",\n  \"onlyStatisticsProxy\": \"代理流量統計\",\n  \"onlyStatisticsProxyDesc\": \"開啟後將只統計代理流量\",\n  \"pureBlackMode\": \"純黑模式\",\n  \"keepAliveIntervalDesc\": \"TCP 保持活動間隔\",\n  \"entries\": \"個項目\",\n  \"local\": \"本機\",\n  \"remote\": \"遠端\",\n  \"remoteBackupDesc\": \"備份資料到 WebDAV\",\n  \"remoteRecoveryDesc\": \"透過 WebDAV 還原資料\",\n  \"localBackupDesc\": \"備份資料到本機\",\n  \"localRecoveryDesc\": \"透過檔案還原資料\",\n  \"mode\": \"模式\",\n  \"time\": \"時間\",\n  \"source\": \"來源\",\n  \"allApps\": \"所有應用程式\",\n  \"onlyOtherApps\": \"僅第三方應用程式\",\n  \"action\": \"操作\",\n  \"intelligentSelected\": \"智慧選擇\",\n  \"clipboardImport\": \"剪貼簿匯入\",\n  \"clipboardExport\": \"匯出剪貼簿\",\n  \"layout\": \"版面配置\",\n  \"tight\": \"緊湊\",\n  \"standard\": \"標準\",\n  \"loose\": \"寬鬆\",\n  \"profilesSort\": \"配置排序\",\n  \"start\": \"啟動\",\n  \"stop\": \"停止\",\n  \"powerSwitch\": \"啟動開關\",\n  \"runTime\": \"啟動時間\",\n  \"checkOrAddProfile\": \"請先新增配置\",\n  \"serviceReady\": \"服務已就緒\",\n  \"appDesc\": \"處理應用相關設定\",\n  \"vpnDesc\": \"修改VPN相關設定\",\n  \"dnsDesc\": \"更新 DNS 相關設定\",\n  \"key\": \"鍵\",\n  \"value\": \"值\",\n  \"hostsDesc\": \"附加目前配置 Hosts\",\n  \"vpnTip\": \"重啟VPN後改變生效\",\n  \"vpnEnableDesc\": \"透過 VpnService 自動路由系統所有流量\",\n  \"options\": \"選項\",\n  \"loopback\": \"迴環解鎖工具\",\n  \"loopbackDesc\": \"用於 UWP 迴環解鎖\",\n  \"providers\": \"提供者\",\n  \"proxyProviders\": \"代理提供者\",\n  \"ruleProviders\": \"規則提供者\",\n  \"advancedSettings\": \"進階設定\",\n  \"nodeExclusion\": \"節點排除\",\n  \"nodeExclusionDesc\": \"排除所有匹配到的節點\",\n  \"nodeExclusionPlaceholder\": \"HK|香港|🇭🇰\",\n  \"formatError\": \"請檢查格式是否正確\",\n  \"healthCheckTimeout\": \"超時時間\",\n  \"healthCheckTimeoutDesc\": \"節點健康檢查超時時間\",\n  \"concurrencyLimit\": \"並發限制\",\n  \"concurrencyLimitDesc\": \"延遲測試的最大並發數量\",\n  \"notRecommended\": \"不推薦\",\n  \"overrideDns\": \"覆寫 DNS\",\n  \"overrideDnsDesc\": \"開啟後將覆蓋配置中的 DNS 選項\",\n  \"overrideTestUrl\": \"覆蓋配置\",\n  \"ntp\": \"NTP\",\n  \"ntpDesc\": \"使用 NTP 時間服務\",\n  \"overrideNtp\": \"覆寫 NTP\",\n  \"overrideNtpDesc\": \"開啟後將覆蓋配置中的 NTP 選項\",\n  \"ntpStatus\": \"狀態\",\n  \"ntpStatusDesc\": \"開啟 NTP 時間服務\",\n  \"writeToSystem\": \"寫入系統\",\n  \"writeToSystemDesc\": \"需要管理員權限\",\n  \"ntpServer\": \"伺服器\",\n  \"ntpPort\": \"連接埠\",\n  \"ntpInterval\": \"更新時間\",\n  \"sniffer\": \"Sniffer\",\n  \"snifferDesc\": \"修改網域嗅探配置\",\n  \"overrideSniffer\": \"覆寫 Sniffer\",\n  \"overrideSnifferDesc\": \"開啟後將覆蓋配置中的 Sniffer 選項\",\n  \"snifferStatus\": \"狀態\",\n  \"snifferStatusDesc\": \"開啟嗅探服務設定\",\n  \"forceDnsMapping\": \"強制 DNS 映射\",\n  \"forceDnsMappingDesc\": \"強制將 DNS 查詢結果映射到連線\",\n  \"parsePureIp\": \"解析純 IP 連線\",\n  \"parsePureIpDesc\": \"解析純 IP 連線\",\n  \"overrideDestination\": \"覆蓋目標地址\",\n  \"overrideDestinationDesc\": \"使用嗅探結果覆蓋連線目標地址\",\n  \"httpPortSniffer\": \"HTTP 連接埠嗅探\",\n  \"tlsPortSniffer\": \"TLS 連接埠嗅探\",\n  \"quicPortSniffer\": \"QUIC 連接埠嗅探\",\n  \"forceDomain\": \"強制嗅探網域\",\n  \"skipDomain\": \"跳過網域\",\n  \"skipSrcAddress\": \"跳過來源 IP\",\n  \"skipDstAddress\": \"跳過目標 IP\",\n  \"snifferPorts\": \"連接埠\",\n  \"snifferPortsHint\": \"例如: 80, 8080-8880\",\n  \"snifferDomainHint\": \"每行一個網域\",\n  \"snifferAddressHint\": \"每行一個地址\",\n  \"tunnel\": \"Tunnel\",\n  \"tunnelDesc\": \"使用流量轉發隧道\",\n  \"overrideTunnel\": \"覆寫 Tunnel\",\n  \"overrideTunnelDesc\": \"開啟後將覆蓋配置中的 Tunnel 選項\",\n  \"tunnelList\": \"轉發清單\",\n  \"addTunnel\": \"新增轉發\",\n  \"editTunnel\": \"編輯轉發\",\n  \"deleteTunnel\": \"刪除轉發\",\n  \"tunnelNetwork\": \"網路協定\",\n  \"tunnelNetworkHint\": \"例如: tcp, udp\",\n  \"tunnelAddress\": \"監聽地址\",\n  \"tunnelAddressHint\": \"例如: 127.0.0.1:6553\",\n  \"tunnelTarget\": \"目標地址\",\n  \"tunnelTargetHint\": \"例如: 114.114.114.114:53\",\n  \"tunnelProxy\": \"代理名稱\",\n  \"tunnelProxyHint\": \"例如: proxy (可選)\",\n  \"experimental\": \"Experimental\",\n  \"experimentalDesc\": \"實驗性配置請謹慎使用\",\n  \"overrideExperimental\": \"覆寫 Experimental\",\n  \"overrideExperimentalDesc\": \"開啟後將覆蓋配置中的實驗性配置\",\n  \"quicGoDisableGso\": \"停用 QUIC 通用分段卸載\",\n  \"quicGoDisableGsoDesc\": \"停用 QUIC 的通用分段卸載功能\",\n  \"quicGoDisableEcn\": \"停用 QUIC 顯式擁塞通知\",\n  \"quicGoDisableEcnDesc\": \"停用 QUIC 的顯式擁塞通知功能\",\n  \"dialerIp4pConvert\": \"啟用撥號 IP4P 地址轉換\",\n  \"dialerIp4pConvertDesc\": \"啟用撥號器的 IP4P 地址轉換功能\",\n  \"status\": \"狀態\",\n  \"statusDesc\": \"關閉後將使用系統 DNS\",\n  \"preferH3Desc\": \"優先使用 DOH 的 http/3\",\n  \"cacheAlgorithm\": \"快取演算法\",\n  \"respectRules\": \"遵守規則\",\n  \"respectRulesDesc\": \"DNS 連線跟隨 Rules\",\n  \"dnsMode\": \"DNS 模式\",\n  \"fakeipRange\": \"FakeIP 範圍\",\n  \"fakeipRangeV6\": \"FakeIPv6 範圍\",\n  \"fakeIpFilterMode\": \"FakeIP 過濾模式\",\n  \"fakeIpFilterModeDesc\": \"指定 FakeIP 過濾模式\",\n  \"blacklist\": \"黑名單\",\n  \"whitelist\": \"白名單\",\n  \"fakeipFilter\": \"FakeIP 過濾清單\",\n  \"fakeipTtl\": \"FakeIP 有效時間\",\n  \"defaultNameserver\": \"預設網域名稱伺服器\",\n  \"defaultNameserverDesc\": \"用於解析 DNS 伺服器\",\n  \"nameserver\": \"網域名稱伺服器\",\n  \"nameserverDesc\": \"用於解析網域\",\n  \"useHosts\": \"使用 Hosts\",\n  \"useSystemHosts\": \"使用系統 Hosts\",\n  \"nameserverPolicy\": \"網域名稱伺服器策略\",\n  \"nameserverPolicyDesc\": \"指定對應網域名稱伺服器策略\",\n  \"proxyNameserver\": \"代理網域名稱伺服器\",\n  \"proxyNameserverDesc\": \"用於解析代理節點的網域\",\n  \"directNameserver\": \"直連網域名稱伺服器\",\n  \"directNameserverDesc\": \"用於解析直連出口的網域\",\n  \"directNameserverFollowPolicy\": \"直連 DNS 遵循規則\",\n  \"fallback\": \"Fallback\",\n  \"fallbackDesc\": \"一般情況下使用境外 DNS\",\n  \"fallbackFilter\": \"Fallback 過濾\",\n  \"geoipCode\": \"GeoIP 代碼\",\n  \"ipcidr\": \"IP / 遮罩\",\n  \"domain\": \"網域\",\n  \"reset\": \"重設\",\n  \"action_view\": \"顯示 / 隱藏\",\n  \"action_start\": \"啟動 / 停止\",\n  \"action_mode\": \"切換模式\",\n  \"action_proxy\": \"系統代理\",\n  \"action_tun\": \"虛擬網卡\",\n  \"disclaimer\": \"免責聲明\",\n  \"disclaimerDesc\": \"本軟體為開源免費軟體，僅供學習交流等非商業性質的個人測試使用，代理服務商的行為均與本軟體無關，同意聲明代表您已完全知曉並確認了這一點，如不同意，請選擇退出！\",\n  \"agree\": \"同意\",\n  \"hotkeyManagement\": \"快捷鍵管理\",\n  \"hotkeyManagementDesc\": \"使用鍵盤控制應用程式\",\n  \"pressKeyboard\": \"請按下按鍵\",\n  \"inputCorrectHotkey\": \"請輸入正確的快捷鍵\",\n  \"hotkeyConflict\": \"快捷鍵衝突\",\n  \"remove\": \"移除\",\n  \"noHotKey\": \"暫無快捷鍵\",\n  \"noNetwork\": \"無網路\",\n  \"ipv6InboundDesc\": \"允許 IPv6 入站\",\n  \"exportLogs\": \"匯出日誌\",\n  \"exportSuccess\": \"匯出成功\",\n  \"iconStyle\": \"圖示樣式\",\n  \"onlyIcon\": \"僅圖示\",\n  \"noIcon\": \"無圖示\",\n  \"stackMode\": \"堆疊模式\",\n  \"strictRoute\": \"嚴格路由\",\n  \"strictRouteDesc\": \"使用 TUN 嚴格路由模式\",\n  \"icmpForwarding\": \"ICMP 轉發\",\n  \"icmpForwardingDesc\": \"開啟後將支援 ICMP Ping\",\n  \"dnsHijack\": \"DNS 劫持\",\n  \"dnsHijackDesc\": \"將解析匯入內部 DNS 模組\",\n  \"endpointIndependentNat\": \"NAT 增強\",\n  \"endpointIndependentNatDesc\": \"啟用獨立於端點的 NAT\",\n  \"network\": \"網路\",\n  \"networkDesc\": \"修改網路相關設定\",\n  \"bypassDomain\": \"排除網域\",\n  \"bypassDomainDesc\": \"僅在系統代理啟用時生效\",\n  \"resetTip\": \"確定要重設嗎？\",\n  \"regExp\": \"正規表示式\",\n  \"icon\": \"圖片\",\n  \"iconConfiguration\": \"圖片設定\",\n  \"noData\": \"暫無資料\",\n  \"adminAutoLaunch\": \"管理員自啟動\",\n  \"adminAutoLaunchDesc\": \"使用管理員模式開機自動啟動\",\n  \"fontFamily\": \"字體\",\n  \"systemFont\": \"系統字體\",\n  \"toggle\": \"切換\",\n  \"system\": \"系統\",\n  \"bypassPrivateRoute\": \"繞過私有網路\",\n  \"bypassPrivateRouteDesc\": \"自動繞過私有網路IP位址\",\n  \"pleaseInputAdminPassword\": \"請輸入管理員密碼\",\n  \"copyEnvVar\": \"複製環境變數\",\n  \"memoryInfo\": \"記憶體資訊\",\n  \"cancel\": \"取消\",\n  \"fileIsUpdate\": \"檔案有修改，是否儲存修改\",\n  \"profileHasUpdate\": \"設定檔已經修改，是否關閉自動更新 \",\n  \"hasCacheChange\": \"是否快取修改\",\n  \"copySuccess\": \"複製成功\",\n  \"success\": \"Success\",\n  \"copyLink\": \"複製連結\",\n  \"exportFile\": \"匯出檔案\",\n  \"cacheCorrupt\": \"快取已損壞，是否清空？\",\n  \"detectionTip\": \"依賴第三方 API，僅供參考\",\n  \"ipClickBehavior\": \"顯示切換\",\n  \"ipPrivacyProtection\": \"隱藏 IP 顯示\",\n  \"manualRefreshIp\": \"重新取得 IP\",\n  \"tryManualRefresh\": \"請嘗試手動重新整理\",\n  \"refreshAppList\": \"重新整理應用程式清單\",\n  \"refreshAppListConfirm\": \"是否重新整理應用程式清單？\",\n  \"switchToDomesticIp\": \"取得國內 IP\",\n  \"listen\": \"監聽\",\n  \"undo\": \"復原\",\n  \"redo\": \"重做\",\n  \"none\": \"無\",\n  \"basicConfig\": \"內核配置\",\n  \"basicConfigDesc\": \"全域修改內核配置\",\n  \"selectedCountTitle\": \"已選擇 {count} 項\",\n  \"addRule\": \"新增規則\",\n  \"ruleName\": \"規則名稱\",\n  \"content\": \"內容\",\n  \"subRule\": \"子規則\",\n  \"ruleTarget\": \"規則目標\",\n  \"sourceIp\": \"來源 IP\",\n  \"noResolve\": \"不解析 IP\",\n  \"getOriginRules\": \"獲取原始規則\",\n  \"overrideOriginRules\": \"覆蓋原始規則\",\n  \"addedOriginRules\": \"附加到原始規則\",\n  \"enableOverride\": \"啟用覆寫\",\n  \"saveChanges\": \"是否儲存更改？\",\n  \"generalDesc\": \"修改一般設定\",\n  \"findProcessModeDesc\": \"開啟後將可以尋找處理程序\",\n  \"tabAnimationDesc\": \"僅在部分行動檢視中有效\",\n  \"navBarHapticFeedback\": \"觸覺回饋\",\n  \"navBarHapticFeedbackDesc\": \"底部導覽列切換震動回饋\",\n  \"saveTip\": \"確定要儲存嗎？\",\n  \"colorSchemes\": \"配色方案\",\n  \"palette\": \"調色盤\",\n  \"tonalSpotScheme\": \"調性點綴\",\n  \"fidelityScheme\": \"高保真\",\n  \"monochromeScheme\": \"單色\",\n  \"neutralScheme\": \"中性\",\n  \"vibrantScheme\": \"活力\",\n  \"expressiveScheme\": \"表現力\",\n  \"contentScheme\": \"內容主題\",\n  \"rainbowScheme\": \"彩虹\",\n  \"fruitSaladScheme\": \"果繽紛\",\n  \"developerMode\": \"開發者模式\",\n  \"developerModeEnableTip\": \"開發者模式已啟用。\",\n  \"messageTest\": \"訊息測試\",\n  \"messageTestTip\": \"這是一條訊息。\",\n  \"crashTest\": \"崩潰測試\",\n  \"clearData\": \"清除資料\",\n  \"zoom\": \"縮放\",\n  \"textScale\": \"文字縮放\",\n  \"lightIcon\": \"丹青留白\",\n  \"lightIconDesc\": \"手動切換淺色系桌面應用程式圖示\",\n  \"harmonyFont\": \"鴻蒙字體\",\n  \"harmonyFontDesc\": \"使用最佳化的 HarmonyOS Sans\",\n  \"internet\": \"網際網路\",\n  \"systemApp\": \"系統應用程式\",\n  \"noNetworkApp\": \"無網路應用程式\",\n  \"contactMe\": \"聯絡我\",\n  \"recoveryStrategy\": \"還原策略\",\n  \"recoveryStrategy_override\": \"覆蓋\",\n  \"recoveryStrategy_compatible\": \"相容\",\n  \"logsTest\": \"日誌測試\",\n  \"emptyTip\": \"{label}不能為空\",\n  \"urlTip\": \"{label}必須為 URL\",\n  \"numberTip\": \"{label}必須為數字\",\n  \"interval\": \"間隔\",\n  \"existsTip\": \"{label}目前已存在\",\n  \"deleteTip\": \"確定刪除目前的 {label} 嗎？\",\n  \"deleteMultipTip\": \"確定刪除選取的 {label} 嗎？\",\n  \"nullTip\": \"暫無 {label}\",\n  \"script\": \"腳本\",\n  \"color\": \"顏色\",\n  \"rename\": \"重新命名\",\n  \"unnamed\": \"未命名\",\n  \"pleaseEnterScriptName\": \"請輸入腳本名稱\",\n  \"overrideInvalidTip\": \"在腳本模式下不生效\",\n  \"mixedPort\": \"混合連接埠\",\n  \"socksPort\": \"Socks 連接埠\",\n  \"redirPort\": \"Redir 連接埠\",\n  \"tproxyPort\": \"Tproxy 連接埠\",\n  \"portTip\": \"{label} 必須在 1024 到 49151 之間\",\n  \"portConflictTip\": \"請輸入不同的連接埠\",\n  \"import\": \"匯入\",\n  \"importFromCode\": \"透過程式碼匯入\",\n  \"importFailed\": \"匯入失敗\",\n  \"importFile\": \"透過檔案匯入\",\n  \"importUrl\": \"透過 URL 匯入\",\n  \"autoSetSystemDns\": \"自動設定系統 DNS\",\n  \"details\": \"{label}詳情\",\n  \"creationTime\": \"建立時間\",\n  \"progress\": \"處理程序\",\n  \"host\": \"主機\",\n  \"destination\": \"目標地址\",\n  \"destinationGeoIP\": \"目標地理定位\",\n  \"destinationIPASN\": \"目標 IP ASN\",\n  \"specialProxy\": \"特殊代理\",\n  \"specialRules\": \"特殊規則\",\n  \"remoteDestination\": \"遠端目標\",\n  \"networkType\": \"網路類型\",\n  \"proxyChains\": \"代理鏈\",\n  \"log\": \"日誌\",\n  \"connection\": \"活躍連線\",\n  \"request\": \"請求\",\n  \"switchLabel\": \"開關\",\n  \"noStatusAvailable\": \"未獲取到狀態\",\n  \"onlinePanel\": \"線上面板\",\n  \"openDashboard\": \"打開 Zashboard\",\n  \"custom\": \"自訂\",\n  \"wakelock\": \"亮螢幕鎖\",\n  \"wakelockDescription\": \"本功能不需要任何特殊權限，因為它僅啟用螢幕喚醒鎖，而不是任何 CPU 喚醒鎖，應用程式會在背景保持必要的活躍，且螢幕不會自動熄滅，這在一些場景下會很有用\",\n  \"tunEnableRequireAdmin\": \"啟用虛擬網卡需要管理員權限，請以管理員身分執行程式\",\n  \"restartTip\": \"重啟TUN後改變生效\",\n  \"restart\": \"重啟\",\n  \"restartCoreTitle\": \"重啟內核\",\n  \"restartCoreDesc\": \"是否手動重啟內核？\",\n  \"highRefreshRate\": \"高重新整理率\",\n  \"highRefreshRateDesc\": \"啟用裝置最高重新整理率支援\"\n}\n"
  },
  {
    "path": "build.yaml",
    "content": "targets:\n  $default:\n    builders:\n      source_gen:combining_builder:\n        options:\n          build_extensions:\n            '^lib/models/{{}}.dart': 'lib/models/generated/{{}}.g.dart'\n            '^lib/providers/{{}}.dart': 'lib/providers/generated/{{}}.g.dart'\n      freezed:\n        options:\n          build_extensions:\n            '^lib/models/{{}}.dart': 'lib/models/generated/{{}}.freezed.dart'"
  },
  {
    "path": "core/Clash.Meta/.github/ISSUE_TEMPLATE/bug_report.yml",
    "content": "name: Bug report\ndescription: Create a report to help us improve\ntitle: \"[Bug] \"\nlabels: [\"bug\"]\nbody:\n  - type: checkboxes\n    id: ensure\n    attributes:\n      label: Verify steps\n      description: \"\n在提交之前，请确认\nPlease verify that you've followed these steps\n\"\n      options:\n        - label: \"\n确保你使用的是**本仓库**最新的的 mihomo 或 mihomo Alpha 版本\nEnsure you are using the latest version of Mihomo or Mihomo Alpha from **this repository**.\n\"\n          required: true\n        - label: \"\n如果你可以自己 debug 并解决的话，提交 PR 吧\nIs this something you can **debug and fix**? Send a pull request! Bug fixes and documentation fixes are welcome.\n\"\n          required: false\n        - label: \"\n我已经在 [Issue Tracker](……/) 中找过我要提出的问题\nI have searched on the [issue tracker](……/) for a related issue.\n\"\n          required: true\n        - label: \"\n我已经使用 Alpha 分支版本测试过，问题依旧存在\nI have tested using the dev branch, and the issue still exists.\n\"\n          required: true\n        - label: \"\n我已经仔细看过 [Documentation](https://wiki.metacubex.one/) 并无法自行解决问题\nI have read the [documentation](https://wiki.metacubex.one/) and was unable to solve the issue.\n\"\n          required: true\n        - label: \"\n这是 Mihomo 核心的问题，并非我所使用的 Mihomo 衍生版本（如 OpenMihomo、KoolMihomo 等）的特定问题\nThis is an issue of the Mihomo core *per se*, not to the derivatives of Mihomo, like OpenMihomo or KoolMihomo.\n\"\n          required: true\n  - type: input\n    attributes:\n      label: Mihomo version\n      description: \"use `mihomo -v`\"\n    validations:\n      required: true\n  - type: dropdown\n    id: os\n    attributes:\n      label: What OS are you seeing the problem on?\n      multiple: true\n      options:\n        - macOS\n        - Windows\n        - Linux\n        - OpenBSD/FreeBSD\n  - type: textarea\n    attributes:\n      render: yaml\n      label: \"Mihomo config\"\n      description: \"\n在下方附上 Mihomo core 配置文件，请确保配置文件中没有敏感信息（比如：服务器地址，密码，端口等）\nPaste the Mihomo core configuration file below, please make sure that there is no sensitive information in the configuration file (e.g., server address/url, password, port)\n\"\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      render: shell\n      label: Mihomo log\n      description: \"\n在下方附上 Mihomo Core 的日志，log level 使用 DEBUG\nPaste the Mihomo core log below with the log level set to `DEBUG`.\n\"\n  - type: textarea\n    attributes:\n      label: Description\n    validations:\n      required: true"
  },
  {
    "path": "core/Clash.Meta/.github/ISSUE_TEMPLATE/config.yml",
    "content": "blank_issues_enabled: false\ncontact_links:\n  - name: mihomo Community Support\n    url: https://github.com/MetaCubeX/mihomo/discussions\n    about: Please ask and answer questions about mihomo here.\n"
  },
  {
    "path": "core/Clash.Meta/.github/ISSUE_TEMPLATE/feature_request.yml",
    "content": "name: Feature request\ndescription: Suggest an idea for this project\ntitle: \"[Feature] \"\nlabels: [\"enhancement\"]\nbody:\n  - type: checkboxes\n    id: ensure\n    attributes:\n      label: Verify steps\n      description: \"\n在提交之前，请确认\nPlease verify that you've followed these steps\n\"\n      options:\n        - label: \"\n我已经在 [Issue Tracker](……/) 中找过我要提出的请求\nI have searched on the [issue tracker](……/) for a related feature request.\n\"\n          required: true\n        - label: \"\n我已经仔细看过 [Documentation](https://wiki.metacubex.one/) 并无法找到这个功能\nI have read the [documentation](https://wiki.metacubex.one/) and was unable to solve the issue.\n\"\n          required: true\n  - type: textarea\n    attributes:\n      label: Description\n      description: 请详细、清晰地表达你要提出的论述，例如这个问题如何影响到你？你想实现什么功能？目前 Mihomo Core 的行为是什麽？\n    validations:\n      required: true\n  - type: textarea\n    attributes:\n      label: Possible Solution\n      description: \"\n此项非必须，但是如果你有想法的话欢迎提出。\nNot obligatory, but suggest a fix/reason for the bug, or ideas how to implement the addition or change\n\""
  },
  {
    "path": "core/Clash.Meta/.github/genReleaseNote.sh",
    "content": "#!/bin/bash\n\nwhile getopts \"v:\" opt; do\n  case $opt in\n    v)\n      version_range=$OPTARG\n      ;;\n    \\?)\n      echo \"Invalid option: -$OPTARG\" >&2\n      exit 1\n      ;;\n  esac\ndone\n\nif [ -z \"$version_range\" ]; then\n  echo \"Please provide the version range using -v option. Example: ./genReleashNote.sh -v v1.14.1...v1.14.2\"\n  exit 1\nfi\n\necho \"## What's Changed\" > release.md\ngit log --pretty=format:\"* %h %s by @%an\" --grep=\"^feat\" -i $version_range | sort -f | uniq >> release.md\necho \"\" >> release.md\n\necho \"## BUG & Fix\" >> release.md\ngit log --pretty=format:\"* %h %s by @%an\" --grep=\"^fix\" -i $version_range | sort -f | uniq >> release.md\necho \"\" >> release.md\n\necho \"## Maintenance\" >> release.md\ngit log --pretty=format:\"* %h %s by @%an\" --grep=\"^chore\\|^docs\\|^refactor\" -i $version_range | sort -f | uniq >> release.md\necho \"\" >> release.md\n\necho \"**Full Changelog**: https://github.com/MetaCubeX/mihomo/compare/$version_range\" >> release.md\n"
  },
  {
    "path": "core/Clash.Meta/.github/patch/go1.21.patch",
    "content": "Subject: [PATCH] Revert \"[release-branch.go1.21] crypto/rand,runtime: switch RtlGenRandom for ProcessPrng\"\n---\nIndex: src/crypto/rand/rand.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go\n--- a/src/crypto/rand/rand.go\t(revision 8bba868de983dd7bf55fcd121495ba8d6e2734e7)\n+++ b/src/crypto/rand/rand.go\t(revision 7e6c963d81e14ee394402671d4044b2940c8d2c1)\n@@ -15,7 +15,7 @@\n // available, /dev/urandom otherwise.\n // On OpenBSD and macOS, Reader uses getentropy(2).\n // On other Unix-like systems, Reader reads from /dev/urandom.\n-// On Windows systems, Reader uses the ProcessPrng API.\n+// On Windows systems, Reader uses the RtlGenRandom API.\n // On JS/Wasm, Reader uses the Web Crypto API.\n // On WASIP1/Wasm, Reader uses random_get from wasi_snapshot_preview1.\n var Reader io.Reader\nIndex: src/crypto/rand/rand_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/crypto/rand/rand_windows.go b/src/crypto/rand/rand_windows.go\n--- a/src/crypto/rand/rand_windows.go\t(revision 8bba868de983dd7bf55fcd121495ba8d6e2734e7)\n+++ b/src/crypto/rand/rand_windows.go\t(revision 7e6c963d81e14ee394402671d4044b2940c8d2c1)\n@@ -15,8 +15,11 @@\n \n type rngReader struct{}\n \n-func (r *rngReader) Read(b []byte) (int, error) {\n-\tif err := windows.ProcessPrng(b); err != nil {\n+func (r *rngReader) Read(b []byte) (n int, err error) {\n+\t// RtlGenRandom only returns 1<<32-1 bytes at a time. We only read at\n+\t// most 1<<31-1 bytes at a time so that  this works the same on 32-bit\n+\t// and 64-bit systems.\n+\tif err := batched(windows.RtlGenRandom, 1<<31-1)(b); err != nil {\n \t\treturn 0, err\n \t}\n \treturn len(b), nil\nIndex: src/internal/syscall/windows/syscall_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go\n--- a/src/internal/syscall/windows/syscall_windows.go\t(revision 8bba868de983dd7bf55fcd121495ba8d6e2734e7)\n+++ b/src/internal/syscall/windows/syscall_windows.go\t(revision 7e6c963d81e14ee394402671d4044b2940c8d2c1)\n@@ -384,7 +384,7 @@\n //sys\tDestroyEnvironmentBlock(block *uint16) (err error) = userenv.DestroyEnvironmentBlock\n //sys\tCreateEvent(eventAttrs *SecurityAttributes, manualReset uint32, initialState uint32, name *uint16) (handle syscall.Handle, err error) = kernel32.CreateEventW\n \n-//sys\tProcessPrng(buf []byte) (err error) = bcryptprimitives.ProcessPrng\n+//sys\tRtlGenRandom(buf []byte) (err error) = advapi32.SystemFunction036\n \n //sys\tRtlLookupFunctionEntry(pc uintptr, baseAddress *uintptr, table *byte) (ret uintptr) = kernel32.RtlLookupFunctionEntry\n //sys\tRtlVirtualUnwind(handlerType uint32, baseAddress uintptr, pc uintptr, entry uintptr, ctxt uintptr, data *uintptr, frame *uintptr, ctxptrs *byte) (ret uintptr) = kernel32.RtlVirtualUnwind\nIndex: src/internal/syscall/windows/zsyscall_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go\n--- a/src/internal/syscall/windows/zsyscall_windows.go\t(revision 8bba868de983dd7bf55fcd121495ba8d6e2734e7)\n+++ b/src/internal/syscall/windows/zsyscall_windows.go\t(revision 7e6c963d81e14ee394402671d4044b2940c8d2c1)\n@@ -37,14 +37,13 @@\n }\n \n var (\n-\tmodadvapi32         = syscall.NewLazyDLL(sysdll.Add(\"advapi32.dll\"))\n-\tmodbcryptprimitives = syscall.NewLazyDLL(sysdll.Add(\"bcryptprimitives.dll\"))\n-\tmodiphlpapi         = syscall.NewLazyDLL(sysdll.Add(\"iphlpapi.dll\"))\n-\tmodkernel32         = syscall.NewLazyDLL(sysdll.Add(\"kernel32.dll\"))\n-\tmodnetapi32         = syscall.NewLazyDLL(sysdll.Add(\"netapi32.dll\"))\n-\tmodpsapi            = syscall.NewLazyDLL(sysdll.Add(\"psapi.dll\"))\n-\tmoduserenv          = syscall.NewLazyDLL(sysdll.Add(\"userenv.dll\"))\n-\tmodws2_32           = syscall.NewLazyDLL(sysdll.Add(\"ws2_32.dll\"))\n+\tmodadvapi32 = syscall.NewLazyDLL(sysdll.Add(\"advapi32.dll\"))\n+\tmodiphlpapi = syscall.NewLazyDLL(sysdll.Add(\"iphlpapi.dll\"))\n+\tmodkernel32 = syscall.NewLazyDLL(sysdll.Add(\"kernel32.dll\"))\n+\tmodnetapi32 = syscall.NewLazyDLL(sysdll.Add(\"netapi32.dll\"))\n+\tmodpsapi    = syscall.NewLazyDLL(sysdll.Add(\"psapi.dll\"))\n+\tmoduserenv  = syscall.NewLazyDLL(sysdll.Add(\"userenv.dll\"))\n+\tmodws2_32   = syscall.NewLazyDLL(sysdll.Add(\"ws2_32.dll\"))\n \n \tprocAdjustTokenPrivileges        = modadvapi32.NewProc(\"AdjustTokenPrivileges\")\n \tprocDuplicateTokenEx             = modadvapi32.NewProc(\"DuplicateTokenEx\")\n@@ -53,7 +52,7 @@\n \tprocOpenThreadToken              = modadvapi32.NewProc(\"OpenThreadToken\")\n \tprocRevertToSelf                 = modadvapi32.NewProc(\"RevertToSelf\")\n \tprocSetTokenInformation          = modadvapi32.NewProc(\"SetTokenInformation\")\n-\tprocProcessPrng                  = modbcryptprimitives.NewProc(\"ProcessPrng\")\n+\tprocSystemFunction036            = modadvapi32.NewProc(\"SystemFunction036\")\n \tprocGetAdaptersAddresses         = modiphlpapi.NewProc(\"GetAdaptersAddresses\")\n \tprocCreateEventW                 = modkernel32.NewProc(\"CreateEventW\")\n \tprocGetACP                       = modkernel32.NewProc(\"GetACP\")\n@@ -149,12 +148,12 @@\n \treturn\n }\n \n-func ProcessPrng(buf []byte) (err error) {\n+func RtlGenRandom(buf []byte) (err error) {\n \tvar _p0 *byte\n \tif len(buf) > 0 {\n \t\t_p0 = &buf[0]\n \t}\n-\tr1, _, e1 := syscall.Syscall(procProcessPrng.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0)\n+\tr1, _, e1 := syscall.Syscall(procSystemFunction036.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0)\n \tif r1 == 0 {\n \t\terr = errnoErr(e1)\n \t}\nIndex: src/runtime/os_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go\n--- a/src/runtime/os_windows.go\t(revision 8bba868de983dd7bf55fcd121495ba8d6e2734e7)\n+++ b/src/runtime/os_windows.go\t(revision 7e6c963d81e14ee394402671d4044b2940c8d2c1)\n@@ -127,8 +127,15 @@\n \t_AddVectoredContinueHandler,\n \t_ stdFunction\n \n-\t// Use ProcessPrng to generate cryptographically random data.\n-\t_ProcessPrng stdFunction\n+\t// Use RtlGenRandom to generate cryptographically random data.\n+\t// This approach has been recommended by Microsoft (see issue\n+\t// 15589 for details).\n+\t// The RtlGenRandom is not listed in advapi32.dll, instead\n+\t// RtlGenRandom function can be found by searching for SystemFunction036.\n+\t// Also some versions of Mingw cannot link to SystemFunction036\n+\t// when building executable as Cgo. So load SystemFunction036\n+\t// manually during runtime startup.\n+\t_RtlGenRandom stdFunction\n \n \t// Load ntdll.dll manually during startup, otherwise Mingw\n \t// links wrong printf function to cgo executable (see issue\n@@ -145,12 +152,12 @@\n )\n \n var (\n-\tbcryptprimitivesdll = [...]uint16{'b', 'c', 'r', 'y', 'p', 't', 'p', 'r', 'i', 'm', 'i', 't', 'i', 'v', 'e', 's', '.', 'd', 'l', 'l', 0}\n-\tkernel32dll         = [...]uint16{'k', 'e', 'r', 'n', 'e', 'l', '3', '2', '.', 'd', 'l', 'l', 0}\n-\tntdlldll            = [...]uint16{'n', 't', 'd', 'l', 'l', '.', 'd', 'l', 'l', 0}\n-\tpowrprofdll         = [...]uint16{'p', 'o', 'w', 'r', 'p', 'r', 'o', 'f', '.', 'd', 'l', 'l', 0}\n-\twinmmdll            = [...]uint16{'w', 'i', 'n', 'm', 'm', '.', 'd', 'l', 'l', 0}\n-\tws2_32dll           = [...]uint16{'w', 's', '2', '_', '3', '2', '.', 'd', 'l', 'l', 0}\n+\tadvapi32dll = [...]uint16{'a', 'd', 'v', 'a', 'p', 'i', '3', '2', '.', 'd', 'l', 'l', 0}\n+\tkernel32dll = [...]uint16{'k', 'e', 'r', 'n', 'e', 'l', '3', '2', '.', 'd', 'l', 'l', 0}\n+\tntdlldll    = [...]uint16{'n', 't', 'd', 'l', 'l', '.', 'd', 'l', 'l', 0}\n+\tpowrprofdll = [...]uint16{'p', 'o', 'w', 'r', 'p', 'r', 'o', 'f', '.', 'd', 'l', 'l', 0}\n+\twinmmdll    = [...]uint16{'w', 'i', 'n', 'm', 'm', '.', 'd', 'l', 'l', 0}\n+\tws2_32dll   = [...]uint16{'w', 's', '2', '_', '3', '2', '.', 'd', 'l', 'l', 0}\n )\n \n // Function to be called by windows CreateThread\n@@ -249,11 +256,11 @@\n \t}\n \t_AddVectoredContinueHandler = windowsFindfunc(k32, []byte(\"AddVectoredContinueHandler\\000\"))\n \n-\tbcryptPrimitives := windowsLoadSystemLib(bcryptprimitivesdll[:])\n-\tif bcryptPrimitives == 0 {\n-\t\tthrow(\"bcryptprimitives.dll not found\")\n+\ta32 := windowsLoadSystemLib(advapi32dll[:])\n+\tif a32 == 0 {\n+\t\tthrow(\"advapi32.dll not found\")\n \t}\n-\t_ProcessPrng = windowsFindfunc(bcryptPrimitives, []byte(\"ProcessPrng\\000\"))\n+\t_RtlGenRandom = windowsFindfunc(a32, []byte(\"SystemFunction036\\000\"))\n \n \tn32 := windowsLoadSystemLib(ntdlldll[:])\n \tif n32 == 0 {\n@@ -610,7 +617,7 @@\n //go:nosplit\n func getRandomData(r []byte) {\n \tn := 0\n-\tif stdcall2(_ProcessPrng, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 {\n+\tif stdcall2(_RtlGenRandom, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 {\n \t\tn = len(r)\n \t}\n \textendRandom(r, n)\n"
  },
  {
    "path": "core/Clash.Meta/.github/patch/go1.22.patch",
    "content": "Subject: [PATCH] Revert \"runtime: always use LoadLibraryEx to load system libraries\"\nRevert \"syscall: remove Windows 7 console handle workaround\"\nRevert \"net: remove sysSocket fallback for Windows 7\"\nRevert \"crypto/rand,runtime: switch RtlGenRandom for ProcessPrng\"\n---\nIndex: src/crypto/rand/rand.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go\n--- a/src/crypto/rand/rand.go\t(revision cb4eee693c382bea4222f20837e26501d40ed892)\n+++ b/src/crypto/rand/rand.go\t(revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9)\n@@ -15,7 +15,7 @@\n // available, /dev/urandom otherwise.\n // On OpenBSD and macOS, Reader uses getentropy(2).\n // On other Unix-like systems, Reader reads from /dev/urandom.\n-// On Windows systems, Reader uses the ProcessPrng API.\n+// On Windows systems, Reader uses the RtlGenRandom API.\n // On JS/Wasm, Reader uses the Web Crypto API.\n // On WASIP1/Wasm, Reader uses random_get from wasi_snapshot_preview1.\n var Reader io.Reader\nIndex: src/crypto/rand/rand_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/crypto/rand/rand_windows.go b/src/crypto/rand/rand_windows.go\n--- a/src/crypto/rand/rand_windows.go\t(revision cb4eee693c382bea4222f20837e26501d40ed892)\n+++ b/src/crypto/rand/rand_windows.go\t(revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9)\n@@ -15,8 +15,11 @@\n \n type rngReader struct{}\n \n-func (r *rngReader) Read(b []byte) (int, error) {\n-\tif err := windows.ProcessPrng(b); err != nil {\n+func (r *rngReader) Read(b []byte) (n int, err error) {\n+\t// RtlGenRandom only returns 1<<32-1 bytes at a time. We only read at\n+\t// most 1<<31-1 bytes at a time so that  this works the same on 32-bit\n+\t// and 64-bit systems.\n+\tif err := batched(windows.RtlGenRandom, 1<<31-1)(b); err != nil {\n \t\treturn 0, err\n \t}\n \treturn len(b), nil\nIndex: src/internal/syscall/windows/syscall_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go\n--- a/src/internal/syscall/windows/syscall_windows.go\t(revision cb4eee693c382bea4222f20837e26501d40ed892)\n+++ b/src/internal/syscall/windows/syscall_windows.go\t(revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9)\n@@ -384,7 +384,7 @@\n //sys\tDestroyEnvironmentBlock(block *uint16) (err error) = userenv.DestroyEnvironmentBlock\n //sys\tCreateEvent(eventAttrs *SecurityAttributes, manualReset uint32, initialState uint32, name *uint16) (handle syscall.Handle, err error) = kernel32.CreateEventW\n \n-//sys\tProcessPrng(buf []byte) (err error) = bcryptprimitives.ProcessPrng\n+//sys\tRtlGenRandom(buf []byte) (err error) = advapi32.SystemFunction036\n \n type FILE_ID_BOTH_DIR_INFO struct {\n \tNextEntryOffset uint32\nIndex: src/internal/syscall/windows/zsyscall_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go\n--- a/src/internal/syscall/windows/zsyscall_windows.go\t(revision cb4eee693c382bea4222f20837e26501d40ed892)\n+++ b/src/internal/syscall/windows/zsyscall_windows.go\t(revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9)\n@@ -37,14 +37,13 @@\n }\n \n var (\n-\tmodadvapi32         = syscall.NewLazyDLL(sysdll.Add(\"advapi32.dll\"))\n-\tmodbcryptprimitives = syscall.NewLazyDLL(sysdll.Add(\"bcryptprimitives.dll\"))\n-\tmodiphlpapi         = syscall.NewLazyDLL(sysdll.Add(\"iphlpapi.dll\"))\n-\tmodkernel32         = syscall.NewLazyDLL(sysdll.Add(\"kernel32.dll\"))\n-\tmodnetapi32         = syscall.NewLazyDLL(sysdll.Add(\"netapi32.dll\"))\n-\tmodpsapi            = syscall.NewLazyDLL(sysdll.Add(\"psapi.dll\"))\n-\tmoduserenv          = syscall.NewLazyDLL(sysdll.Add(\"userenv.dll\"))\n-\tmodws2_32           = syscall.NewLazyDLL(sysdll.Add(\"ws2_32.dll\"))\n+\tmodadvapi32 = syscall.NewLazyDLL(sysdll.Add(\"advapi32.dll\"))\n+\tmodiphlpapi = syscall.NewLazyDLL(sysdll.Add(\"iphlpapi.dll\"))\n+\tmodkernel32 = syscall.NewLazyDLL(sysdll.Add(\"kernel32.dll\"))\n+\tmodnetapi32 = syscall.NewLazyDLL(sysdll.Add(\"netapi32.dll\"))\n+\tmodpsapi    = syscall.NewLazyDLL(sysdll.Add(\"psapi.dll\"))\n+\tmoduserenv  = syscall.NewLazyDLL(sysdll.Add(\"userenv.dll\"))\n+\tmodws2_32   = syscall.NewLazyDLL(sysdll.Add(\"ws2_32.dll\"))\n \n \tprocAdjustTokenPrivileges             = modadvapi32.NewProc(\"AdjustTokenPrivileges\")\n \tprocDuplicateTokenEx                  = modadvapi32.NewProc(\"DuplicateTokenEx\")\n@@ -56,7 +55,7 @@\n \tprocQueryServiceStatus                = modadvapi32.NewProc(\"QueryServiceStatus\")\n \tprocRevertToSelf                      = modadvapi32.NewProc(\"RevertToSelf\")\n \tprocSetTokenInformation               = modadvapi32.NewProc(\"SetTokenInformation\")\n-\tprocProcessPrng                       = modbcryptprimitives.NewProc(\"ProcessPrng\")\n+\tprocSystemFunction036                 = modadvapi32.NewProc(\"SystemFunction036\")\n \tprocGetAdaptersAddresses              = modiphlpapi.NewProc(\"GetAdaptersAddresses\")\n \tprocCreateEventW                      = modkernel32.NewProc(\"CreateEventW\")\n \tprocGetACP                            = modkernel32.NewProc(\"GetACP\")\n@@ -180,12 +179,12 @@\n \treturn\n }\n \n-func ProcessPrng(buf []byte) (err error) {\n+func RtlGenRandom(buf []byte) (err error) {\n \tvar _p0 *byte\n \tif len(buf) > 0 {\n \t\t_p0 = &buf[0]\n \t}\n-\tr1, _, e1 := syscall.Syscall(procProcessPrng.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0)\n+\tr1, _, e1 := syscall.Syscall(procSystemFunction036.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0)\n \tif r1 == 0 {\n \t\terr = errnoErr(e1)\n \t}\nIndex: src/runtime/os_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go\n--- a/src/runtime/os_windows.go\t(revision cb4eee693c382bea4222f20837e26501d40ed892)\n+++ b/src/runtime/os_windows.go\t(revision 83ff9782e024cb328b690cbf0da4e7848a327f4f)\n@@ -40,8 +40,8 @@\n //go:cgo_import_dynamic runtime._GetSystemInfo GetSystemInfo%1 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._GetThreadContext GetThreadContext%2 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._SetThreadContext SetThreadContext%2 \"kernel32.dll\"\n-//go:cgo_import_dynamic runtime._LoadLibraryExW LoadLibraryExW%3 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 \"kernel32.dll\"\n+//go:cgo_import_dynamic runtime._LoadLibraryA LoadLibraryA%1 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._PostQueuedCompletionStatus PostQueuedCompletionStatus%4 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._QueryPerformanceCounter QueryPerformanceCounter%1 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._RaiseFailFastException RaiseFailFastException%3 \"kernel32.dll\"\n@@ -74,7 +74,6 @@\n \t// Following syscalls are available on every Windows PC.\n \t// All these variables are set by the Windows executable\n \t// loader before the Go program starts.\n-\t_AddVectoredContinueHandler,\n \t_AddVectoredExceptionHandler,\n \t_CloseHandle,\n \t_CreateEventA,\n@@ -98,8 +97,8 @@\n \t_GetSystemInfo,\n \t_GetThreadContext,\n \t_SetThreadContext,\n-\t_LoadLibraryExW,\n \t_LoadLibraryW,\n+\t_LoadLibraryA,\n \t_PostQueuedCompletionStatus,\n \t_QueryPerformanceCounter,\n \t_RaiseFailFastException,\n@@ -127,8 +126,23 @@\n \t_WriteFile,\n \t_ stdFunction\n \n-\t// Use ProcessPrng to generate cryptographically random data.\n-\t_ProcessPrng stdFunction\n+\t// Following syscalls are only available on some Windows PCs.\n+\t// We will load syscalls, if available, before using them.\n+\t_AddDllDirectory,\n+\t_AddVectoredContinueHandler,\n+\t_LoadLibraryExA,\n+\t_LoadLibraryExW,\n+\t_ stdFunction\n+\n+\t// Use RtlGenRandom to generate cryptographically random data.\n+\t// This approach has been recommended by Microsoft (see issue\n+\t// 15589 for details).\n+\t// The RtlGenRandom is not listed in advapi32.dll, instead\n+\t// RtlGenRandom function can be found by searching for SystemFunction036.\n+\t// Also some versions of Mingw cannot link to SystemFunction036\n+\t// when building executable as Cgo. So load SystemFunction036\n+\t// manually during runtime startup.\n+\t_RtlGenRandom stdFunction\n \n \t// Load ntdll.dll manually during startup, otherwise Mingw\n \t// links wrong printf function to cgo executable (see issue\n@@ -143,14 +157,6 @@\n \t_ stdFunction\n )\n \n-var (\n-\tbcryptprimitivesdll = [...]uint16{'b', 'c', 'r', 'y', 'p', 't', 'p', 'r', 'i', 'm', 'i', 't', 'i', 'v', 'e', 's', '.', 'd', 'l', 'l', 0}\n-\tntdlldll            = [...]uint16{'n', 't', 'd', 'l', 'l', '.', 'd', 'l', 'l', 0}\n-\tpowrprofdll         = [...]uint16{'p', 'o', 'w', 'r', 'p', 'r', 'o', 'f', '.', 'd', 'l', 'l', 0}\n-\twinmmdll            = [...]uint16{'w', 'i', 'n', 'm', 'm', '.', 'd', 'l', 'l', 0}\n-\tws2_32dll           = [...]uint16{'w', 's', '2', '_', '3', '2', '.', 'd', 'l', 'l', 0}\n-)\n-\n // Function to be called by windows CreateThread\n // to start new os thread.\n func tstart_stdcall(newm *m)\n@@ -239,25 +245,51 @@\n \treturn unsafe.String(&sysDirectory[0], sysDirectoryLen)\n }\n \n-func windowsLoadSystemLib(name []uint16) uintptr {\n-\treturn stdcall3(_LoadLibraryExW, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32)\n+//go:linkname syscall_getSystemDirectory syscall.getSystemDirectory\n+func syscall_getSystemDirectory() string {\n+\treturn unsafe.String(&sysDirectory[0], sysDirectoryLen)\n+}\n+\n+func windowsLoadSystemLib(name []byte) uintptr {\n+\tif useLoadLibraryEx {\n+\t\treturn stdcall3(_LoadLibraryExA, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32)\n+\t} else {\n+\t\tabsName := append(sysDirectory[:sysDirectoryLen], name...)\n+\t\treturn stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&absName[0])))\n+\t}\n }\n \n func loadOptionalSyscalls() {\n-\tbcryptPrimitives := windowsLoadSystemLib(bcryptprimitivesdll[:])\n-\tif bcryptPrimitives == 0 {\n-\t\tthrow(\"bcryptprimitives.dll not found\")\n+\tvar kernel32dll = []byte(\"kernel32.dll\\000\")\n+\tk32 := stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&kernel32dll[0])))\n+\tif k32 == 0 {\n+\t\tthrow(\"kernel32.dll not found\")\n \t}\n-\t_ProcessPrng = windowsFindfunc(bcryptPrimitives, []byte(\"ProcessPrng\\000\"))\n+\t_AddDllDirectory = windowsFindfunc(k32, []byte(\"AddDllDirectory\\000\"))\n+\t_AddVectoredContinueHandler = windowsFindfunc(k32, []byte(\"AddVectoredContinueHandler\\000\"))\n+\t_LoadLibraryExA = windowsFindfunc(k32, []byte(\"LoadLibraryExA\\000\"))\n+\t_LoadLibraryExW = windowsFindfunc(k32, []byte(\"LoadLibraryExW\\000\"))\n+\tuseLoadLibraryEx = (_LoadLibraryExW != nil && _LoadLibraryExA != nil && _AddDllDirectory != nil)\n+\n+\tinitSysDirectory()\n \n-\tn32 := windowsLoadSystemLib(ntdlldll[:])\n+\tvar advapi32dll = []byte(\"advapi32.dll\\000\")\n+\ta32 := windowsLoadSystemLib(advapi32dll)\n+\tif a32 == 0 {\n+\t\tthrow(\"advapi32.dll not found\")\n+\t}\n+\t_RtlGenRandom = windowsFindfunc(a32, []byte(\"SystemFunction036\\000\"))\n+\n+\tvar ntdll = []byte(\"ntdll.dll\\000\")\n+\tn32 := windowsLoadSystemLib(ntdll)\n \tif n32 == 0 {\n \t\tthrow(\"ntdll.dll not found\")\n \t}\n \t_RtlGetCurrentPeb = windowsFindfunc(n32, []byte(\"RtlGetCurrentPeb\\000\"))\n \t_RtlGetNtVersionNumbers = windowsFindfunc(n32, []byte(\"RtlGetNtVersionNumbers\\000\"))\n \n-\tm32 := windowsLoadSystemLib(winmmdll[:])\n+\tvar winmmdll = []byte(\"winmm.dll\\000\")\n+\tm32 := windowsLoadSystemLib(winmmdll)\n \tif m32 == 0 {\n \t\tthrow(\"winmm.dll not found\")\n \t}\n@@ -267,7 +299,8 @@\n \t\tthrow(\"timeBegin/EndPeriod not found\")\n \t}\n \n-\tws232 := windowsLoadSystemLib(ws2_32dll[:])\n+\tvar ws232dll = []byte(\"ws2_32.dll\\000\")\n+\tws232 := windowsLoadSystemLib(ws232dll)\n \tif ws232 == 0 {\n \t\tthrow(\"ws2_32.dll not found\")\n \t}\n@@ -286,7 +319,7 @@\n \t\tcontext  uintptr\n \t}\n \n-\tpowrprof := windowsLoadSystemLib(powrprofdll[:])\n+\tpowrprof := windowsLoadSystemLib([]byte(\"powrprof.dll\\000\"))\n \tif powrprof == 0 {\n \t\treturn // Running on Windows 7, where we don't need it anyway.\n \t}\n@@ -360,6 +393,22 @@\n // in sys_windows_386.s and sys_windows_amd64.s:\n func getlasterror() uint32\n \n+// When loading DLLs, we prefer to use LoadLibraryEx with\n+// LOAD_LIBRARY_SEARCH_* flags, if available. LoadLibraryEx is not\n+// available on old Windows, though, and the LOAD_LIBRARY_SEARCH_*\n+// flags are not available on some versions of Windows without a\n+// security patch.\n+//\n+// https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says:\n+// \"Windows 7, Windows Server 2008 R2, Windows Vista, and Windows\n+// Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on\n+// systems that have KB2533623 installed. To determine whether the\n+// flags are available, use GetProcAddress to get the address of the\n+// AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories\n+// function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_*\n+// flags can be used with LoadLibraryEx.\"\n+var useLoadLibraryEx bool\n+\n var timeBeginPeriodRetValue uint32\n \n // osRelaxMinNS indicates that sysmon shouldn't osRelax if the next\n@@ -507,7 +556,6 @@\n \tinitHighResTimer()\n \ttimeBeginPeriodRetValue = osRelax(false)\n \n-\tinitSysDirectory()\n \tinitLongPathSupport()\n \n \tncpu = getproccount()\n@@ -524,7 +572,7 @@\n //go:nosplit\n func readRandom(r []byte) int {\n \tn := 0\n-\tif stdcall2(_ProcessPrng, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 {\n+\tif stdcall2(_RtlGenRandom, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 {\n \t\tn = len(r)\n \t}\n \treturn n\nIndex: src/net/hook_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/hook_windows.go b/src/net/hook_windows.go\n--- a/src/net/hook_windows.go\t(revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9)\n+++ b/src/net/hook_windows.go\t(revision ef0606261340e608017860b423ffae5c1ce78239)\n@@ -13,6 +13,7 @@\n \thostsFilePath = windows.GetSystemDirectory() + \"/Drivers/etc/hosts\"\n \n \t// Placeholders for socket system calls.\n+\tsocketFunc    func(int, int, int) (syscall.Handle, error)                                                 = syscall.Socket\n \twsaSocketFunc func(int32, int32, int32, *syscall.WSAProtocolInfo, uint32, uint32) (syscall.Handle, error) = windows.WSASocket\n \tconnectFunc   func(syscall.Handle, syscall.Sockaddr) error                                                = syscall.Connect\n \tlistenFunc    func(syscall.Handle, int) error                                                             = syscall.Listen\nIndex: src/net/internal/socktest/main_test.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/internal/socktest/main_test.go b/src/net/internal/socktest/main_test.go\n--- a/src/net/internal/socktest/main_test.go\t(revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9)\n+++ b/src/net/internal/socktest/main_test.go\t(revision ef0606261340e608017860b423ffae5c1ce78239)\n@@ -2,7 +2,7 @@\n // Use of this source code is governed by a BSD-style\n // license that can be found in the LICENSE file.\n \n-//go:build !js && !plan9 && !wasip1 && !windows\n+//go:build !js && !plan9 && !wasip1\n \n package socktest_test\n \nIndex: src/net/internal/socktest/main_windows_test.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/internal/socktest/main_windows_test.go b/src/net/internal/socktest/main_windows_test.go\nnew file mode 100644\n--- /dev/null\t(revision ef0606261340e608017860b423ffae5c1ce78239)\n+++ b/src/net/internal/socktest/main_windows_test.go\t(revision ef0606261340e608017860b423ffae5c1ce78239)\n@@ -0,0 +1,22 @@\n+// Copyright 2015 The Go Authors. All rights reserved.\n+// Use of this source code is governed by a BSD-style\n+// license that can be found in the LICENSE file.\n+\n+package socktest_test\n+\n+import \"syscall\"\n+\n+var (\n+\tsocketFunc func(int, int, int) (syscall.Handle, error)\n+\tcloseFunc  func(syscall.Handle) error\n+)\n+\n+func installTestHooks() {\n+\tsocketFunc = sw.Socket\n+\tcloseFunc = sw.Closesocket\n+}\n+\n+func uninstallTestHooks() {\n+\tsocketFunc = syscall.Socket\n+\tcloseFunc = syscall.Closesocket\n+}\nIndex: src/net/internal/socktest/sys_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/internal/socktest/sys_windows.go b/src/net/internal/socktest/sys_windows.go\n--- a/src/net/internal/socktest/sys_windows.go\t(revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9)\n+++ b/src/net/internal/socktest/sys_windows.go\t(revision ef0606261340e608017860b423ffae5c1ce78239)\n@@ -9,6 +9,38 @@\n \t\"syscall\"\n )\n \n+// Socket wraps [syscall.Socket].\n+func (sw *Switch) Socket(family, sotype, proto int) (s syscall.Handle, err error) {\n+\tsw.once.Do(sw.init)\n+\n+\tso := &Status{Cookie: cookie(family, sotype, proto)}\n+\tsw.fmu.RLock()\n+\tf, _ := sw.fltab[FilterSocket]\n+\tsw.fmu.RUnlock()\n+\n+\taf, err := f.apply(so)\n+\tif err != nil {\n+\t\treturn syscall.InvalidHandle, err\n+\t}\n+\ts, so.Err = syscall.Socket(family, sotype, proto)\n+\tif err = af.apply(so); err != nil {\n+\t\tif so.Err == nil {\n+\t\t\tsyscall.Closesocket(s)\n+\t\t}\n+\t\treturn syscall.InvalidHandle, err\n+\t}\n+\n+\tsw.smu.Lock()\n+\tdefer sw.smu.Unlock()\n+\tif so.Err != nil {\n+\t\tsw.stats.getLocked(so.Cookie).OpenFailed++\n+\t\treturn syscall.InvalidHandle, so.Err\n+\t}\n+\tnso := sw.addLocked(s, family, sotype, proto)\n+\tsw.stats.getLocked(nso.Cookie).Opened++\n+\treturn s, nil\n+}\n+\n // WSASocket wraps [syscall.WSASocket].\n func (sw *Switch) WSASocket(family, sotype, proto int32, protinfo *syscall.WSAProtocolInfo, group uint32, flags uint32) (s syscall.Handle, err error) {\n \tsw.once.Do(sw.init)\nIndex: src/net/main_windows_test.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/main_windows_test.go b/src/net/main_windows_test.go\n--- a/src/net/main_windows_test.go\t(revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9)\n+++ b/src/net/main_windows_test.go\t(revision ef0606261340e608017860b423ffae5c1ce78239)\n@@ -8,6 +8,7 @@\n \n var (\n \t// Placeholders for saving original socket system calls.\n+\torigSocket      = socketFunc\n \torigWSASocket   = wsaSocketFunc\n \torigClosesocket = poll.CloseFunc\n \torigConnect     = connectFunc\n@@ -17,6 +18,7 @@\n )\n \n func installTestHooks() {\n+\tsocketFunc = sw.Socket\n \twsaSocketFunc = sw.WSASocket\n \tpoll.CloseFunc = sw.Closesocket\n \tconnectFunc = sw.Connect\n@@ -26,6 +28,7 @@\n }\n \n func uninstallTestHooks() {\n+\tsocketFunc = origSocket\n \twsaSocketFunc = origWSASocket\n \tpoll.CloseFunc = origClosesocket\n \tconnectFunc = origConnect\nIndex: src/net/sock_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/sock_windows.go b/src/net/sock_windows.go\n--- a/src/net/sock_windows.go\t(revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9)\n+++ b/src/net/sock_windows.go\t(revision ef0606261340e608017860b423ffae5c1ce78239)\n@@ -20,6 +20,21 @@\n func sysSocket(family, sotype, proto int) (syscall.Handle, error) {\n \ts, err := wsaSocketFunc(int32(family), int32(sotype), int32(proto),\n \t\tnil, 0, windows.WSA_FLAG_OVERLAPPED|windows.WSA_FLAG_NO_HANDLE_INHERIT)\n+\tif err == nil {\n+\t\treturn s, nil\n+\t}\n+\t// WSA_FLAG_NO_HANDLE_INHERIT flag is not supported on some\n+\t// old versions of Windows, see\n+\t// https://msdn.microsoft.com/en-us/library/windows/desktop/ms742212(v=vs.85).aspx\n+\t// for details. Just use syscall.Socket, if windows.WSASocket failed.\n+\n+\t// See ../syscall/exec_unix.go for description of ForkLock.\n+\tsyscall.ForkLock.RLock()\n+\ts, err = socketFunc(family, sotype, proto)\n+\tif err == nil {\n+\t\tsyscall.CloseOnExec(s)\n+\t}\n+\tsyscall.ForkLock.RUnlock()\n \tif err != nil {\n \t\treturn syscall.InvalidHandle, os.NewSyscallError(\"socket\", err)\n \t}\nIndex: src/syscall/exec_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go\n--- a/src/syscall/exec_windows.go\t(revision 9779155f18b6556a034f7bb79fb7fb2aad1e26a9)\n+++ b/src/syscall/exec_windows.go\t(revision 7f83badcb925a7e743188041cb6e561fc9b5b642)\n@@ -14,7 +14,6 @@\n \t\"unsafe\"\n )\n \n-// ForkLock is not used on Windows.\n var ForkLock sync.RWMutex\n \n // EscapeArg rewrites command line argument s as prescribed\n@@ -317,6 +316,17 @@\n \t\t}\n \t}\n \n+\tvar maj, min, build uint32\n+\trtlGetNtVersionNumbers(&maj, &min, &build)\n+\tisWin7 := maj < 6 || (maj == 6 && min <= 1)\n+\t// NT kernel handles are divisible by 4, with the bottom 3 bits left as\n+\t// a tag. The fully set tag correlates with the types of handles we're\n+\t// concerned about here.  Except, the kernel will interpret some\n+\t// special handle values, like -1, -2, and so forth, so kernelbase.dll\n+\t// checks to see that those bottom three bits are checked, but that top\n+\t// bit is not checked.\n+\tisLegacyWin7ConsoleHandle := func(handle Handle) bool { return isWin7 && handle&0x10000003 == 3 }\n+\n \tp, _ := GetCurrentProcess()\n \tparentProcess := p\n \tif sys.ParentProcess != 0 {\n@@ -325,7 +335,15 @@\n \tfd := make([]Handle, len(attr.Files))\n \tfor i := range attr.Files {\n \t\tif attr.Files[i] > 0 {\n-\t\t\terr := DuplicateHandle(p, Handle(attr.Files[i]), parentProcess, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)\n+\t\t\tdestinationProcessHandle := parentProcess\n+\n+\t\t\t// On Windows 7, console handles aren't real handles, and can only be duplicated\n+\t\t\t// into the current process, not a parent one, which amounts to the same thing.\n+\t\t\tif parentProcess != p && isLegacyWin7ConsoleHandle(Handle(attr.Files[i])) {\n+\t\t\t\tdestinationProcessHandle = p\n+\t\t\t}\n+\n+\t\t\terr := DuplicateHandle(p, Handle(attr.Files[i]), destinationProcessHandle, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)\n \t\t\tif err != nil {\n \t\t\t\treturn 0, 0, err\n \t\t\t}\n@@ -356,6 +374,14 @@\n \n \tfd = append(fd, sys.AdditionalInheritedHandles...)\n \n+\t// On Windows 7, console handles aren't real handles, so don't pass them\n+\t// through to PROC_THREAD_ATTRIBUTE_HANDLE_LIST.\n+\tfor i := range fd {\n+\t\tif isLegacyWin7ConsoleHandle(fd[i]) {\n+\t\t\tfd[i] = 0\n+\t\t}\n+\t}\n+\n \t// The presence of a NULL handle in the list is enough to cause PROC_THREAD_ATTRIBUTE_HANDLE_LIST\n \t// to treat the entire list as empty, so remove NULL handles.\n \tj := 0\nIndex: src/runtime/syscall_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/runtime/syscall_windows.go b/src/runtime/syscall_windows.go\n--- a/src/runtime/syscall_windows.go\t(revision 7f83badcb925a7e743188041cb6e561fc9b5b642)\n+++ b/src/runtime/syscall_windows.go\t(revision 83ff9782e024cb328b690cbf0da4e7848a327f4f)\n@@ -413,23 +413,36 @@\n \n const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800\n \n+// When available, this function will use LoadLibraryEx with the filename\n+// parameter and the important SEARCH_SYSTEM32 argument. But on systems that\n+// do not have that option, absoluteFilepath should contain a fallback\n+// to the full path inside of system32 for use with vanilla LoadLibrary.\n+//\n //go:linkname syscall_loadsystemlibrary syscall.loadsystemlibrary\n //go:nosplit\n //go:cgo_unsafe_args\n-func syscall_loadsystemlibrary(filename *uint16) (handle, err uintptr) {\n+func syscall_loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle, err uintptr) {\n \tlockOSThread()\n \tc := &getg().m.syscall\n-\tc.fn = getLoadLibraryEx()\n-\tc.n = 3\n-\targs := struct {\n-\t\tlpFileName *uint16\n-\t\thFile      uintptr // always 0\n-\t\tflags      uint32\n-\t}{filename, 0, _LOAD_LIBRARY_SEARCH_SYSTEM32}\n-\tc.args = uintptr(noescape(unsafe.Pointer(&args)))\n+\n+\tif useLoadLibraryEx {\n+\t\tc.fn = getLoadLibraryEx()\n+\t\tc.n = 3\n+\t\targs := struct {\n+\t\t\tlpFileName *uint16\n+\t\t\thFile      uintptr // always 0\n+\t\t\tflags      uint32\n+\t\t}{filename, 0, _LOAD_LIBRARY_SEARCH_SYSTEM32}\n+\t\tc.args = uintptr(noescape(unsafe.Pointer(&args)))\n+\t} else {\n+\t\tc.fn = getLoadLibrary()\n+\t\tc.n = 1\n+\t\tc.args = uintptr(noescape(unsafe.Pointer(&absoluteFilepath)))\n+\t}\n \n \tcgocall(asmstdcallAddr, unsafe.Pointer(c))\n \tKeepAlive(filename)\n+\tKeepAlive(absoluteFilepath)\n \thandle = c.r1\n \tif handle == 0 {\n \t\terr = c.err\nIndex: src/syscall/dll_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/syscall/dll_windows.go b/src/syscall/dll_windows.go\n--- a/src/syscall/dll_windows.go\t(revision 7f83badcb925a7e743188041cb6e561fc9b5b642)\n+++ b/src/syscall/dll_windows.go\t(revision 83ff9782e024cb328b690cbf0da4e7848a327f4f)\n@@ -44,7 +44,7 @@\n \n func SyscallN(trap uintptr, args ...uintptr) (r1, r2 uintptr, err Errno)\n func loadlibrary(filename *uint16) (handle uintptr, err Errno)\n-func loadsystemlibrary(filename *uint16) (handle uintptr, err Errno)\n+func loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle uintptr, err Errno)\n func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno)\n \n // A DLL implements access to a single DLL.\n@@ -53,6 +53,9 @@\n \tHandle Handle\n }\n \n+//go:linkname getSystemDirectory\n+func getSystemDirectory() string // Implemented in runtime package.\n+\n // LoadDLL loads the named DLL file into memory.\n //\n // If name is not an absolute path and is not a known system DLL used by\n@@ -69,7 +72,11 @@\n \tvar h uintptr\n \tvar e Errno\n \tif sysdll.IsSystemDLL[name] {\n-\t\th, e = loadsystemlibrary(namep)\n+\t\tabsoluteFilepathp, err := UTF16PtrFromString(getSystemDirectory() + name)\n+\t\tif err != nil {\n+\t\t\treturn nil, err\n+\t\t}\n+\t\th, e = loadsystemlibrary(namep, absoluteFilepathp)\n \t} else {\n \t\th, e = loadlibrary(namep)\n \t}\n"
  },
  {
    "path": "core/Clash.Meta/.github/patch/go1.23.patch",
    "content": "Subject: [PATCH] Revert \"runtime: always use LoadLibraryEx to load system libraries\"\nRevert \"syscall: remove Windows 7 console handle workaround\"\nRevert \"net: remove sysSocket fallback for Windows 7\"\nRevert \"crypto/rand,runtime: switch RtlGenRandom for ProcessPrng\"\n---\nIndex: src/crypto/rand/rand.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go\n--- a/src/crypto/rand/rand.go\t(revision 6885bad7dd86880be6929c02085e5c7a67ff2887)\n+++ b/src/crypto/rand/rand.go\t(revision 9ac42137ef6730e8b7daca016ece831297a1d75b)\n@@ -16,7 +16,7 @@\n //   - On macOS and iOS, Reader uses arc4random_buf(3).\n //   - On OpenBSD and NetBSD, Reader uses getentropy(2).\n //   - On other Unix-like systems, Reader reads from /dev/urandom.\n-//   - On Windows, Reader uses the ProcessPrng API.\n+//   - On Windows systems, Reader uses the RtlGenRandom API.\n //   - On js/wasm, Reader uses the Web Crypto API.\n //   - On wasip1/wasm, Reader uses random_get from wasi_snapshot_preview1.\n var Reader io.Reader\nIndex: src/crypto/rand/rand_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/crypto/rand/rand_windows.go b/src/crypto/rand/rand_windows.go\n--- a/src/crypto/rand/rand_windows.go\t(revision 6885bad7dd86880be6929c02085e5c7a67ff2887)\n+++ b/src/crypto/rand/rand_windows.go\t(revision 9ac42137ef6730e8b7daca016ece831297a1d75b)\n@@ -15,8 +15,11 @@\n \n type rngReader struct{}\n \n-func (r *rngReader) Read(b []byte) (int, error) {\n-\tif err := windows.ProcessPrng(b); err != nil {\n+func (r *rngReader) Read(b []byte) (n int, err error) {\n+\t// RtlGenRandom only returns 1<<32-1 bytes at a time. We only read at\n+\t// most 1<<31-1 bytes at a time so that  this works the same on 32-bit\n+\t// and 64-bit systems.\n+\tif err := batched(windows.RtlGenRandom, 1<<31-1)(b); err != nil {\n \t\treturn 0, err\n \t}\n \treturn len(b), nil\nIndex: src/internal/syscall/windows/syscall_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go\n--- a/src/internal/syscall/windows/syscall_windows.go\t(revision 6885bad7dd86880be6929c02085e5c7a67ff2887)\n+++ b/src/internal/syscall/windows/syscall_windows.go\t(revision 9ac42137ef6730e8b7daca016ece831297a1d75b)\n@@ -414,7 +414,7 @@\n //sys\tDestroyEnvironmentBlock(block *uint16) (err error) = userenv.DestroyEnvironmentBlock\n //sys\tCreateEvent(eventAttrs *SecurityAttributes, manualReset uint32, initialState uint32, name *uint16) (handle syscall.Handle, err error) = kernel32.CreateEventW\n \n-//sys\tProcessPrng(buf []byte) (err error) = bcryptprimitives.ProcessPrng\n+//sys\tRtlGenRandom(buf []byte) (err error) = advapi32.SystemFunction036\n \n type FILE_ID_BOTH_DIR_INFO struct {\n \tNextEntryOffset uint32\nIndex: src/internal/syscall/windows/zsyscall_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go\n--- a/src/internal/syscall/windows/zsyscall_windows.go\t(revision 6885bad7dd86880be6929c02085e5c7a67ff2887)\n+++ b/src/internal/syscall/windows/zsyscall_windows.go\t(revision 9ac42137ef6730e8b7daca016ece831297a1d75b)\n@@ -38,7 +38,6 @@\n \n var (\n \tmodadvapi32         = syscall.NewLazyDLL(sysdll.Add(\"advapi32.dll\"))\n-\tmodbcryptprimitives = syscall.NewLazyDLL(sysdll.Add(\"bcryptprimitives.dll\"))\n \tmodiphlpapi         = syscall.NewLazyDLL(sysdll.Add(\"iphlpapi.dll\"))\n \tmodkernel32         = syscall.NewLazyDLL(sysdll.Add(\"kernel32.dll\"))\n \tmodnetapi32         = syscall.NewLazyDLL(sysdll.Add(\"netapi32.dll\"))\n@@ -57,7 +56,7 @@\n \tprocQueryServiceStatus                = modadvapi32.NewProc(\"QueryServiceStatus\")\n \tprocRevertToSelf                      = modadvapi32.NewProc(\"RevertToSelf\")\n \tprocSetTokenInformation               = modadvapi32.NewProc(\"SetTokenInformation\")\n-\tprocProcessPrng                       = modbcryptprimitives.NewProc(\"ProcessPrng\")\n+\tprocSystemFunction036                 = modadvapi32.NewProc(\"SystemFunction036\")\n \tprocGetAdaptersAddresses              = modiphlpapi.NewProc(\"GetAdaptersAddresses\")\n \tprocCreateEventW                      = modkernel32.NewProc(\"CreateEventW\")\n \tprocGetACP                            = modkernel32.NewProc(\"GetACP\")\n@@ -183,12 +182,12 @@\n \treturn\n }\n \n-func ProcessPrng(buf []byte) (err error) {\n+func RtlGenRandom(buf []byte) (err error) {\n \tvar _p0 *byte\n \tif len(buf) > 0 {\n \t\t_p0 = &buf[0]\n \t}\n-\tr1, _, e1 := syscall.Syscall(procProcessPrng.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0)\n+\tr1, _, e1 := syscall.Syscall(procSystemFunction036.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0)\n \tif r1 == 0 {\n \t\terr = errnoErr(e1)\n \t}\nIndex: src/runtime/os_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go\n--- a/src/runtime/os_windows.go\t(revision 6885bad7dd86880be6929c02085e5c7a67ff2887)\n+++ b/src/runtime/os_windows.go\t(revision 69e2eed6dd0f6d815ebf15797761c13f31213dd6)\n@@ -39,8 +39,8 @@\n //go:cgo_import_dynamic runtime._GetSystemInfo GetSystemInfo%1 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._GetThreadContext GetThreadContext%2 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._SetThreadContext SetThreadContext%2 \"kernel32.dll\"\n-//go:cgo_import_dynamic runtime._LoadLibraryExW LoadLibraryExW%3 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 \"kernel32.dll\"\n+//go:cgo_import_dynamic runtime._LoadLibraryA LoadLibraryA%1 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._PostQueuedCompletionStatus PostQueuedCompletionStatus%4 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._QueryPerformanceCounter QueryPerformanceCounter%1 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._QueryPerformanceFrequency QueryPerformanceFrequency%1 \"kernel32.dll\"\n@@ -74,7 +74,6 @@\n \t// Following syscalls are available on every Windows PC.\n \t// All these variables are set by the Windows executable\n \t// loader before the Go program starts.\n-\t_AddVectoredContinueHandler,\n \t_AddVectoredExceptionHandler,\n \t_CloseHandle,\n \t_CreateEventA,\n@@ -97,8 +96,8 @@\n \t_GetSystemInfo,\n \t_GetThreadContext,\n \t_SetThreadContext,\n-\t_LoadLibraryExW,\n \t_LoadLibraryW,\n+\t_LoadLibraryA,\n \t_PostQueuedCompletionStatus,\n \t_QueryPerformanceCounter,\n \t_QueryPerformanceFrequency,\n@@ -127,8 +126,23 @@\n \t_WriteFile,\n \t_ stdFunction\n \n-\t// Use ProcessPrng to generate cryptographically random data.\n-\t_ProcessPrng stdFunction\n+\t// Following syscalls are only available on some Windows PCs.\n+\t// We will load syscalls, if available, before using them.\n+\t_AddDllDirectory,\n+\t_AddVectoredContinueHandler,\n+\t_LoadLibraryExA,\n+\t_LoadLibraryExW,\n+\t_ stdFunction\n+\n+\t// Use RtlGenRandom to generate cryptographically random data.\n+\t// This approach has been recommended by Microsoft (see issue\n+\t// 15589 for details).\n+\t// The RtlGenRandom is not listed in advapi32.dll, instead\n+\t// RtlGenRandom function can be found by searching for SystemFunction036.\n+\t// Also some versions of Mingw cannot link to SystemFunction036\n+\t// when building executable as Cgo. So load SystemFunction036\n+\t// manually during runtime startup.\n+\t_RtlGenRandom stdFunction\n \n \t// Load ntdll.dll manually during startup, otherwise Mingw\n \t// links wrong printf function to cgo executable (see issue\n@@ -145,13 +159,6 @@\n \t_ stdFunction\n )\n \n-var (\n-\tbcryptprimitivesdll = [...]uint16{'b', 'c', 'r', 'y', 'p', 't', 'p', 'r', 'i', 'm', 'i', 't', 'i', 'v', 'e', 's', '.', 'd', 'l', 'l', 0}\n-\tntdlldll            = [...]uint16{'n', 't', 'd', 'l', 'l', '.', 'd', 'l', 'l', 0}\n-\tpowrprofdll         = [...]uint16{'p', 'o', 'w', 'r', 'p', 'r', 'o', 'f', '.', 'd', 'l', 'l', 0}\n-\twinmmdll            = [...]uint16{'w', 'i', 'n', 'm', 'm', '.', 'd', 'l', 'l', 0}\n-)\n-\n // Function to be called by windows CreateThread\n // to start new os thread.\n func tstart_stdcall(newm *m)\n@@ -244,8 +251,18 @@\n \treturn unsafe.String(&sysDirectory[0], sysDirectoryLen)\n }\n \n-func windowsLoadSystemLib(name []uint16) uintptr {\n-\treturn stdcall3(_LoadLibraryExW, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32)\n+//go:linkname syscall_getSystemDirectory syscall.getSystemDirectory\n+func syscall_getSystemDirectory() string {\n+\treturn unsafe.String(&sysDirectory[0], sysDirectoryLen)\n+}\n+\n+func windowsLoadSystemLib(name []byte) uintptr {\n+\tif useLoadLibraryEx {\n+\t\treturn stdcall3(_LoadLibraryExA, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32)\n+\t} else {\n+\t\tabsName := append(sysDirectory[:sysDirectoryLen], name...)\n+\t\treturn stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&absName[0])))\n+\t}\n }\n \n //go:linkname windows_QueryPerformanceCounter internal/syscall/windows.QueryPerformanceCounter\n@@ -263,13 +280,28 @@\n }\n \n func loadOptionalSyscalls() {\n-\tbcryptPrimitives := windowsLoadSystemLib(bcryptprimitivesdll[:])\n-\tif bcryptPrimitives == 0 {\n-\t\tthrow(\"bcryptprimitives.dll not found\")\n+\tvar kernel32dll = []byte(\"kernel32.dll\\000\")\n+\tk32 := stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&kernel32dll[0])))\n+\tif k32 == 0 {\n+\t\tthrow(\"kernel32.dll not found\")\n \t}\n-\t_ProcessPrng = windowsFindfunc(bcryptPrimitives, []byte(\"ProcessPrng\\000\"))\n+\t_AddDllDirectory = windowsFindfunc(k32, []byte(\"AddDllDirectory\\000\"))\n+\t_AddVectoredContinueHandler = windowsFindfunc(k32, []byte(\"AddVectoredContinueHandler\\000\"))\n+\t_LoadLibraryExA = windowsFindfunc(k32, []byte(\"LoadLibraryExA\\000\"))\n+\t_LoadLibraryExW = windowsFindfunc(k32, []byte(\"LoadLibraryExW\\000\"))\n+\tuseLoadLibraryEx = (_LoadLibraryExW != nil && _LoadLibraryExA != nil && _AddDllDirectory != nil)\n+\n+\tinitSysDirectory()\n \n-\tn32 := windowsLoadSystemLib(ntdlldll[:])\n+\tvar advapi32dll = []byte(\"advapi32.dll\\000\")\n+\ta32 := windowsLoadSystemLib(advapi32dll)\n+\tif a32 == 0 {\n+\t\tthrow(\"advapi32.dll not found\")\n+\t}\n+\t_RtlGenRandom = windowsFindfunc(a32, []byte(\"SystemFunction036\\000\"))\n+\n+\tvar ntdll = []byte(\"ntdll.dll\\000\")\n+\tn32 := windowsLoadSystemLib(ntdll)\n \tif n32 == 0 {\n \t\tthrow(\"ntdll.dll not found\")\n \t}\n@@ -298,7 +330,7 @@\n \t\tcontext  uintptr\n \t}\n \n-\tpowrprof := windowsLoadSystemLib(powrprofdll[:])\n+\tpowrprof := windowsLoadSystemLib([]byte(\"powrprof.dll\\000\"))\n \tif powrprof == 0 {\n \t\treturn // Running on Windows 7, where we don't need it anyway.\n \t}\n@@ -357,6 +389,22 @@\n // in sys_windows_386.s and sys_windows_amd64.s:\n func getlasterror() uint32\n \n+// When loading DLLs, we prefer to use LoadLibraryEx with\n+// LOAD_LIBRARY_SEARCH_* flags, if available. LoadLibraryEx is not\n+// available on old Windows, though, and the LOAD_LIBRARY_SEARCH_*\n+// flags are not available on some versions of Windows without a\n+// security patch.\n+//\n+// https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says:\n+// \"Windows 7, Windows Server 2008 R2, Windows Vista, and Windows\n+// Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on\n+// systems that have KB2533623 installed. To determine whether the\n+// flags are available, use GetProcAddress to get the address of the\n+// AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories\n+// function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_*\n+// flags can be used with LoadLibraryEx.\"\n+var useLoadLibraryEx bool\n+\n var timeBeginPeriodRetValue uint32\n \n // osRelaxMinNS indicates that sysmon shouldn't osRelax if the next\n@@ -430,7 +478,8 @@\n \t\t// Only load winmm.dll if we need it.\n \t\t// This avoids a dependency on winmm.dll for Go programs\n \t\t// that run on new Windows versions.\n-\t\tm32 := windowsLoadSystemLib(winmmdll[:])\n+\t\tvar winmmdll = []byte(\"winmm.dll\\000\")\n+\t\tm32 := windowsLoadSystemLib(winmmdll)\n \t\tif m32 == 0 {\n \t\t\tprint(\"runtime: LoadLibraryExW failed; errno=\", getlasterror(), \"\\n\")\n \t\t\tthrow(\"winmm.dll not found\")\n@@ -471,6 +520,28 @@\n \tcanUseLongPaths = true\n }\n \n+var osVersionInfo struct {\n+\tmajorVersion uint32\n+\tminorVersion uint32\n+\tbuildNumber  uint32\n+}\n+\n+func initOsVersionInfo() {\n+\tinfo := _OSVERSIONINFOW{}\n+\tinfo.osVersionInfoSize = uint32(unsafe.Sizeof(info))\n+\tstdcall1(_RtlGetVersion, uintptr(unsafe.Pointer(&info)))\n+\tosVersionInfo.majorVersion = info.majorVersion\n+\tosVersionInfo.minorVersion = info.minorVersion\n+\tosVersionInfo.buildNumber = info.buildNumber\n+}\n+\n+//go:linkname rtlGetNtVersionNumbers syscall.rtlGetNtVersionNumbers\n+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) {\n+\t*majorVersion = osVersionInfo.majorVersion\n+\t*minorVersion = osVersionInfo.minorVersion\n+\t*buildNumber = osVersionInfo.buildNumber\n+}\n+\n func osinit() {\n \tasmstdcallAddr = unsafe.Pointer(abi.FuncPCABI0(asmstdcall))\n \n@@ -483,8 +554,8 @@\n \tinitHighResTimer()\n \ttimeBeginPeriodRetValue = osRelax(false)\n \n-\tinitSysDirectory()\n \tinitLongPathSupport()\n+\tinitOsVersionInfo()\n \n \tncpu = getproccount()\n \n@@ -500,7 +571,7 @@\n //go:nosplit\n func readRandom(r []byte) int {\n \tn := 0\n-\tif stdcall2(_ProcessPrng, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 {\n+\tif stdcall2(_RtlGenRandom, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 {\n \t\tn = len(r)\n \t}\n \treturn n\nIndex: src/net/hook_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/hook_windows.go b/src/net/hook_windows.go\n--- a/src/net/hook_windows.go\t(revision 9ac42137ef6730e8b7daca016ece831297a1d75b)\n+++ b/src/net/hook_windows.go\t(revision 21290de8a4c91408de7c2b5b68757b1e90af49dd)\n@@ -13,6 +13,7 @@\n \thostsFilePath = windows.GetSystemDirectory() + \"/Drivers/etc/hosts\"\n \n \t// Placeholders for socket system calls.\n+\tsocketFunc    func(int, int, int) (syscall.Handle, error)                                                 = syscall.Socket\n \twsaSocketFunc func(int32, int32, int32, *syscall.WSAProtocolInfo, uint32, uint32) (syscall.Handle, error) = windows.WSASocket\n \tconnectFunc   func(syscall.Handle, syscall.Sockaddr) error                                                = syscall.Connect\n \tlistenFunc    func(syscall.Handle, int) error                                                             = syscall.Listen\nIndex: src/net/internal/socktest/main_test.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/internal/socktest/main_test.go b/src/net/internal/socktest/main_test.go\n--- a/src/net/internal/socktest/main_test.go\t(revision 9ac42137ef6730e8b7daca016ece831297a1d75b)\n+++ b/src/net/internal/socktest/main_test.go\t(revision 21290de8a4c91408de7c2b5b68757b1e90af49dd)\n@@ -2,7 +2,7 @@\n // Use of this source code is governed by a BSD-style\n // license that can be found in the LICENSE file.\n \n-//go:build !js && !plan9 && !wasip1 && !windows\n+//go:build !js && !plan9 && !wasip1\n \n package socktest_test\n \nIndex: src/net/internal/socktest/main_windows_test.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/internal/socktest/main_windows_test.go b/src/net/internal/socktest/main_windows_test.go\nnew file mode 100644\n--- /dev/null\t(revision 21290de8a4c91408de7c2b5b68757b1e90af49dd)\n+++ b/src/net/internal/socktest/main_windows_test.go\t(revision 21290de8a4c91408de7c2b5b68757b1e90af49dd)\n@@ -0,0 +1,22 @@\n+// Copyright 2015 The Go Authors. All rights reserved.\n+// Use of this source code is governed by a BSD-style\n+// license that can be found in the LICENSE file.\n+\n+package socktest_test\n+\n+import \"syscall\"\n+\n+var (\n+\tsocketFunc func(int, int, int) (syscall.Handle, error)\n+\tcloseFunc  func(syscall.Handle) error\n+)\n+\n+func installTestHooks() {\n+\tsocketFunc = sw.Socket\n+\tcloseFunc = sw.Closesocket\n+}\n+\n+func uninstallTestHooks() {\n+\tsocketFunc = syscall.Socket\n+\tcloseFunc = syscall.Closesocket\n+}\nIndex: src/net/internal/socktest/sys_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/internal/socktest/sys_windows.go b/src/net/internal/socktest/sys_windows.go\n--- a/src/net/internal/socktest/sys_windows.go\t(revision 9ac42137ef6730e8b7daca016ece831297a1d75b)\n+++ b/src/net/internal/socktest/sys_windows.go\t(revision 21290de8a4c91408de7c2b5b68757b1e90af49dd)\n@@ -9,6 +9,38 @@\n \t\"syscall\"\n )\n \n+// Socket wraps [syscall.Socket].\n+func (sw *Switch) Socket(family, sotype, proto int) (s syscall.Handle, err error) {\n+\tsw.once.Do(sw.init)\n+\n+\tso := &Status{Cookie: cookie(family, sotype, proto)}\n+\tsw.fmu.RLock()\n+\tf, _ := sw.fltab[FilterSocket]\n+\tsw.fmu.RUnlock()\n+\n+\taf, err := f.apply(so)\n+\tif err != nil {\n+\t\treturn syscall.InvalidHandle, err\n+\t}\n+\ts, so.Err = syscall.Socket(family, sotype, proto)\n+\tif err = af.apply(so); err != nil {\n+\t\tif so.Err == nil {\n+\t\t\tsyscall.Closesocket(s)\n+\t\t}\n+\t\treturn syscall.InvalidHandle, err\n+\t}\n+\n+\tsw.smu.Lock()\n+\tdefer sw.smu.Unlock()\n+\tif so.Err != nil {\n+\t\tsw.stats.getLocked(so.Cookie).OpenFailed++\n+\t\treturn syscall.InvalidHandle, so.Err\n+\t}\n+\tnso := sw.addLocked(s, family, sotype, proto)\n+\tsw.stats.getLocked(nso.Cookie).Opened++\n+\treturn s, nil\n+}\n+\n // WSASocket wraps [syscall.WSASocket].\n func (sw *Switch) WSASocket(family, sotype, proto int32, protinfo *syscall.WSAProtocolInfo, group uint32, flags uint32) (s syscall.Handle, err error) {\n \tsw.once.Do(sw.init)\nIndex: src/net/main_windows_test.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/main_windows_test.go b/src/net/main_windows_test.go\n--- a/src/net/main_windows_test.go\t(revision 9ac42137ef6730e8b7daca016ece831297a1d75b)\n+++ b/src/net/main_windows_test.go\t(revision 21290de8a4c91408de7c2b5b68757b1e90af49dd)\n@@ -8,6 +8,7 @@\n \n var (\n \t// Placeholders for saving original socket system calls.\n+\torigSocket      = socketFunc\n \torigWSASocket   = wsaSocketFunc\n \torigClosesocket = poll.CloseFunc\n \torigConnect     = connectFunc\n@@ -17,6 +18,7 @@\n )\n \n func installTestHooks() {\n+\tsocketFunc = sw.Socket\n \twsaSocketFunc = sw.WSASocket\n \tpoll.CloseFunc = sw.Closesocket\n \tconnectFunc = sw.Connect\n@@ -26,6 +28,7 @@\n }\n \n func uninstallTestHooks() {\n+\tsocketFunc = origSocket\n \twsaSocketFunc = origWSASocket\n \tpoll.CloseFunc = origClosesocket\n \tconnectFunc = origConnect\nIndex: src/net/sock_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/sock_windows.go b/src/net/sock_windows.go\n--- a/src/net/sock_windows.go\t(revision 9ac42137ef6730e8b7daca016ece831297a1d75b)\n+++ b/src/net/sock_windows.go\t(revision 21290de8a4c91408de7c2b5b68757b1e90af49dd)\n@@ -20,6 +20,21 @@\n func sysSocket(family, sotype, proto int) (syscall.Handle, error) {\n \ts, err := wsaSocketFunc(int32(family), int32(sotype), int32(proto),\n \t\tnil, 0, windows.WSA_FLAG_OVERLAPPED|windows.WSA_FLAG_NO_HANDLE_INHERIT)\n+\tif err == nil {\n+\t\treturn s, nil\n+\t}\n+\t// WSA_FLAG_NO_HANDLE_INHERIT flag is not supported on some\n+\t// old versions of Windows, see\n+\t// https://msdn.microsoft.com/en-us/library/windows/desktop/ms742212(v=vs.85).aspx\n+\t// for details. Just use syscall.Socket, if windows.WSASocket failed.\n+\n+\t// See ../syscall/exec_unix.go for description of ForkLock.\n+\tsyscall.ForkLock.RLock()\n+\ts, err = socketFunc(family, sotype, proto)\n+\tif err == nil {\n+\t\tsyscall.CloseOnExec(s)\n+\t}\n+\tsyscall.ForkLock.RUnlock()\n \tif err != nil {\n \t\treturn syscall.InvalidHandle, os.NewSyscallError(\"socket\", err)\n \t}\nIndex: src/syscall/exec_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go\n--- a/src/syscall/exec_windows.go\t(revision 9ac42137ef6730e8b7daca016ece831297a1d75b)\n+++ b/src/syscall/exec_windows.go\t(revision 6a31d3fa8e47ddabc10bd97bff10d9a85f4cfb76)\n@@ -14,7 +14,6 @@\n \t\"unsafe\"\n )\n \n-// ForkLock is not used on Windows.\n var ForkLock sync.RWMutex\n \n // EscapeArg rewrites command line argument s as prescribed\n@@ -254,6 +253,9 @@\n var zeroProcAttr ProcAttr\n var zeroSysProcAttr SysProcAttr\n \n+//go:linkname rtlGetNtVersionNumbers\n+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32)\n+\n func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) {\n \tif len(argv0) == 0 {\n \t\treturn 0, 0, EWINDOWS\n@@ -317,6 +319,17 @@\n \t\t}\n \t}\n \n+\tvar maj, min, build uint32\n+\trtlGetNtVersionNumbers(&maj, &min, &build)\n+\tisWin7 := maj < 6 || (maj == 6 && min <= 1)\n+\t// NT kernel handles are divisible by 4, with the bottom 3 bits left as\n+\t// a tag. The fully set tag correlates with the types of handles we're\n+\t// concerned about here.  Except, the kernel will interpret some\n+\t// special handle values, like -1, -2, and so forth, so kernelbase.dll\n+\t// checks to see that those bottom three bits are checked, but that top\n+\t// bit is not checked.\n+\tisLegacyWin7ConsoleHandle := func(handle Handle) bool { return isWin7 && handle&0x10000003 == 3 }\n+\n \tp, _ := GetCurrentProcess()\n \tparentProcess := p\n \tif sys.ParentProcess != 0 {\n@@ -325,7 +338,15 @@\n \tfd := make([]Handle, len(attr.Files))\n \tfor i := range attr.Files {\n \t\tif attr.Files[i] > 0 {\n-\t\t\terr := DuplicateHandle(p, Handle(attr.Files[i]), parentProcess, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)\n+\t\t\tdestinationProcessHandle := parentProcess\n+\n+\t\t\t// On Windows 7, console handles aren't real handles, and can only be duplicated\n+\t\t\t// into the current process, not a parent one, which amounts to the same thing.\n+\t\t\tif parentProcess != p && isLegacyWin7ConsoleHandle(Handle(attr.Files[i])) {\n+\t\t\t\tdestinationProcessHandle = p\n+\t\t\t}\n+\n+\t\t\terr := DuplicateHandle(p, Handle(attr.Files[i]), destinationProcessHandle, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)\n \t\t\tif err != nil {\n \t\t\t\treturn 0, 0, err\n \t\t\t}\n@@ -356,6 +377,14 @@\n \n \tfd = append(fd, sys.AdditionalInheritedHandles...)\n \n+\t// On Windows 7, console handles aren't real handles, so don't pass them\n+\t// through to PROC_THREAD_ATTRIBUTE_HANDLE_LIST.\n+\tfor i := range fd {\n+\t\tif isLegacyWin7ConsoleHandle(fd[i]) {\n+\t\t\tfd[i] = 0\n+\t\t}\n+\t}\n+\n \t// The presence of a NULL handle in the list is enough to cause PROC_THREAD_ATTRIBUTE_HANDLE_LIST\n \t// to treat the entire list as empty, so remove NULL handles.\n \tj := 0\nIndex: src/runtime/syscall_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/runtime/syscall_windows.go b/src/runtime/syscall_windows.go\n--- a/src/runtime/syscall_windows.go\t(revision 6a31d3fa8e47ddabc10bd97bff10d9a85f4cfb76)\n+++ b/src/runtime/syscall_windows.go\t(revision 69e2eed6dd0f6d815ebf15797761c13f31213dd6)\n@@ -413,10 +413,20 @@\n \n const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800\n \n+// When available, this function will use LoadLibraryEx with the filename\n+// parameter and the important SEARCH_SYSTEM32 argument. But on systems that\n+// do not have that option, absoluteFilepath should contain a fallback\n+// to the full path inside of system32 for use with vanilla LoadLibrary.\n+//\n //go:linkname syscall_loadsystemlibrary syscall.loadsystemlibrary\n-func syscall_loadsystemlibrary(filename *uint16) (handle, err uintptr) {\n-\thandle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryExW)), uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32)\n+func syscall_loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle, err uintptr) {\n+\tif useLoadLibraryEx {\n+\t\thandle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryExW)), uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32)\n+\t} else {\n+\t\thandle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryW)), uintptr(unsafe.Pointer(absoluteFilepath)))\n+\t}\n \tKeepAlive(filename)\n+\tKeepAlive(absoluteFilepath)\n \tif handle != 0 {\n \t\terr = 0\n \t}\nIndex: src/syscall/dll_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/syscall/dll_windows.go b/src/syscall/dll_windows.go\n--- a/src/syscall/dll_windows.go\t(revision 6a31d3fa8e47ddabc10bd97bff10d9a85f4cfb76)\n+++ b/src/syscall/dll_windows.go\t(revision 69e2eed6dd0f6d815ebf15797761c13f31213dd6)\n@@ -44,7 +44,7 @@\n \n func SyscallN(trap uintptr, args ...uintptr) (r1, r2 uintptr, err Errno)\n func loadlibrary(filename *uint16) (handle uintptr, err Errno)\n-func loadsystemlibrary(filename *uint16) (handle uintptr, err Errno)\n+func loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle uintptr, err Errno)\n func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno)\n \n // A DLL implements access to a single DLL.\n@@ -53,6 +53,9 @@\n \tHandle Handle\n }\n \n+//go:linkname getSystemDirectory\n+func getSystemDirectory() string // Implemented in runtime package.\n+\n // LoadDLL loads the named DLL file into memory.\n //\n // If name is not an absolute path and is not a known system DLL used by\n@@ -69,7 +72,11 @@\n \tvar h uintptr\n \tvar e Errno\n \tif sysdll.IsSystemDLL[name] {\n-\t\th, e = loadsystemlibrary(namep)\n+\t\tabsoluteFilepathp, err := UTF16PtrFromString(getSystemDirectory() + name)\n+\t\tif err != nil {\n+\t\t\treturn nil, err\n+\t\t}\n+\t\th, e = loadsystemlibrary(namep, absoluteFilepathp)\n \t} else {\n \t\th, e = loadlibrary(namep)\n \t}\n"
  },
  {
    "path": "core/Clash.Meta/.github/patch/go1.24.patch",
    "content": "Subject: [PATCH] Revert \"runtime: always use LoadLibraryEx to load system libraries\"\nRevert \"syscall: remove Windows 7 console handle workaround\"\nRevert \"net: remove sysSocket fallback for Windows 7\"\nRevert \"crypto/rand,runtime: switch RtlGenRandom for ProcessPrng\"\n---\nIndex: src/crypto/internal/sysrand/rand_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/crypto/internal/sysrand/rand_windows.go b/src/crypto/internal/sysrand/rand_windows.go\n--- a/src/crypto/internal/sysrand/rand_windows.go\t(revision 3901409b5d0fb7c85a3e6730a59943cc93b2835c)\n+++ b/src/crypto/internal/sysrand/rand_windows.go\t(revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8)\n@@ -7,5 +7,26 @@\n import \"internal/syscall/windows\"\n \n func read(b []byte) error {\n-\treturn windows.ProcessPrng(b)\n+\t// RtlGenRandom only returns 1<<32-1 bytes at a time. We only read at\n+\t// most 1<<31-1 bytes at a time so that  this works the same on 32-bit\n+\t// and 64-bit systems.\n+\treturn batched(windows.RtlGenRandom, 1<<31-1)(b)\n+}\n+\n+// batched returns a function that calls f to populate a []byte by chunking it\n+// into subslices of, at most, readMax bytes.\n+func batched(f func([]byte) error, readMax int) func([]byte) error {\n+\treturn func(out []byte) error {\n+\t\tfor len(out) > 0 {\n+\t\t\tread := len(out)\n+\t\t\tif read > readMax {\n+\t\t\t\tread = readMax\n+\t\t\t}\n+\t\t\tif err := f(out[:read]); err != nil {\n+\t\t\t\treturn err\n+\t\t\t}\n+\t\t\tout = out[read:]\n+\t\t}\n+\t\treturn nil\n+\t}\n }\nIndex: src/crypto/rand/rand.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go\n--- a/src/crypto/rand/rand.go\t(revision 3901409b5d0fb7c85a3e6730a59943cc93b2835c)\n+++ b/src/crypto/rand/rand.go\t(revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8)\n@@ -22,7 +22,7 @@\n //   - On legacy Linux (< 3.17), Reader opens /dev/urandom on first use.\n //   - On macOS, iOS, and OpenBSD Reader, uses arc4random_buf(3).\n //   - On NetBSD, Reader uses the kern.arandom sysctl.\n-//   - On Windows, Reader uses the ProcessPrng API.\n+//   - On Windows systems, Reader uses the RtlGenRandom API.\n //   - On js/wasm, Reader uses the Web Crypto API.\n //   - On wasip1/wasm, Reader uses random_get.\n //\nIndex: src/internal/syscall/windows/syscall_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go\n--- a/src/internal/syscall/windows/syscall_windows.go\t(revision 3901409b5d0fb7c85a3e6730a59943cc93b2835c)\n+++ b/src/internal/syscall/windows/syscall_windows.go\t(revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8)\n@@ -416,7 +416,7 @@\n //sys\tDestroyEnvironmentBlock(block *uint16) (err error) = userenv.DestroyEnvironmentBlock\n //sys\tCreateEvent(eventAttrs *SecurityAttributes, manualReset uint32, initialState uint32, name *uint16) (handle syscall.Handle, err error) = kernel32.CreateEventW\n \n-//sys\tProcessPrng(buf []byte) (err error) = bcryptprimitives.ProcessPrng\n+//sys\tRtlGenRandom(buf []byte) (err error) = advapi32.SystemFunction036\n \n type FILE_ID_BOTH_DIR_INFO struct {\n \tNextEntryOffset uint32\nIndex: src/internal/syscall/windows/zsyscall_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go\n--- a/src/internal/syscall/windows/zsyscall_windows.go\t(revision 3901409b5d0fb7c85a3e6730a59943cc93b2835c)\n+++ b/src/internal/syscall/windows/zsyscall_windows.go\t(revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8)\n@@ -38,7 +38,6 @@\n \n var (\n \tmodadvapi32         = syscall.NewLazyDLL(sysdll.Add(\"advapi32.dll\"))\n-\tmodbcryptprimitives = syscall.NewLazyDLL(sysdll.Add(\"bcryptprimitives.dll\"))\n \tmodiphlpapi         = syscall.NewLazyDLL(sysdll.Add(\"iphlpapi.dll\"))\n \tmodkernel32         = syscall.NewLazyDLL(sysdll.Add(\"kernel32.dll\"))\n \tmodnetapi32         = syscall.NewLazyDLL(sysdll.Add(\"netapi32.dll\"))\n@@ -63,7 +62,7 @@\n \tprocQueryServiceStatus                = modadvapi32.NewProc(\"QueryServiceStatus\")\n \tprocRevertToSelf                      = modadvapi32.NewProc(\"RevertToSelf\")\n \tprocSetTokenInformation               = modadvapi32.NewProc(\"SetTokenInformation\")\n-\tprocProcessPrng                       = modbcryptprimitives.NewProc(\"ProcessPrng\")\n+\tprocSystemFunction036                 = modadvapi32.NewProc(\"SystemFunction036\")\n \tprocGetAdaptersAddresses              = modiphlpapi.NewProc(\"GetAdaptersAddresses\")\n \tprocCreateEventW                      = modkernel32.NewProc(\"CreateEventW\")\n \tprocGetACP                            = modkernel32.NewProc(\"GetACP\")\n@@ -236,12 +235,12 @@\n \treturn\n }\n \n-func ProcessPrng(buf []byte) (err error) {\n+func RtlGenRandom(buf []byte) (err error) {\n \tvar _p0 *byte\n \tif len(buf) > 0 {\n \t\t_p0 = &buf[0]\n \t}\n-\tr1, _, e1 := syscall.Syscall(procProcessPrng.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0)\n+\tr1, _, e1 := syscall.Syscall(procSystemFunction036.Addr(), 2, uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)), 0)\n \tif r1 == 0 {\n \t\terr = errnoErr(e1)\n \t}\nIndex: src/runtime/os_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go\n--- a/src/runtime/os_windows.go\t(revision 3901409b5d0fb7c85a3e6730a59943cc93b2835c)\n+++ b/src/runtime/os_windows.go\t(revision ac3e93c061779dfefc0dd13a5b6e6f764a25621e)\n@@ -40,8 +40,8 @@\n //go:cgo_import_dynamic runtime._GetSystemInfo GetSystemInfo%1 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._GetThreadContext GetThreadContext%2 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._SetThreadContext SetThreadContext%2 \"kernel32.dll\"\n-//go:cgo_import_dynamic runtime._LoadLibraryExW LoadLibraryExW%3 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 \"kernel32.dll\"\n+//go:cgo_import_dynamic runtime._LoadLibraryA LoadLibraryA%1 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._PostQueuedCompletionStatus PostQueuedCompletionStatus%4 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._QueryPerformanceCounter QueryPerformanceCounter%1 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._QueryPerformanceFrequency QueryPerformanceFrequency%1 \"kernel32.dll\"\n@@ -75,7 +75,6 @@\n \t// Following syscalls are available on every Windows PC.\n \t// All these variables are set by the Windows executable\n \t// loader before the Go program starts.\n-\t_AddVectoredContinueHandler,\n \t_AddVectoredExceptionHandler,\n \t_CloseHandle,\n \t_CreateEventA,\n@@ -98,8 +97,8 @@\n \t_GetSystemInfo,\n \t_GetThreadContext,\n \t_SetThreadContext,\n-\t_LoadLibraryExW,\n \t_LoadLibraryW,\n+\t_LoadLibraryA,\n \t_PostQueuedCompletionStatus,\n \t_QueryPerformanceCounter,\n \t_QueryPerformanceFrequency,\n@@ -128,8 +127,23 @@\n \t_WriteFile,\n \t_ stdFunction\n \n-\t// Use ProcessPrng to generate cryptographically random data.\n-\t_ProcessPrng stdFunction\n+\t// Following syscalls are only available on some Windows PCs.\n+\t// We will load syscalls, if available, before using them.\n+\t_AddDllDirectory,\n+\t_AddVectoredContinueHandler,\n+\t_LoadLibraryExA,\n+\t_LoadLibraryExW,\n+\t_ stdFunction\n+\n+\t// Use RtlGenRandom to generate cryptographically random data.\n+\t// This approach has been recommended by Microsoft (see issue\n+\t// 15589 for details).\n+\t// The RtlGenRandom is not listed in advapi32.dll, instead\n+\t// RtlGenRandom function can be found by searching for SystemFunction036.\n+\t// Also some versions of Mingw cannot link to SystemFunction036\n+\t// when building executable as Cgo. So load SystemFunction036\n+\t// manually during runtime startup.\n+\t_RtlGenRandom stdFunction\n \n \t// Load ntdll.dll manually during startup, otherwise Mingw\n \t// links wrong printf function to cgo executable (see issue\n@@ -146,13 +160,6 @@\n \t_ stdFunction\n )\n \n-var (\n-\tbcryptprimitivesdll = [...]uint16{'b', 'c', 'r', 'y', 'p', 't', 'p', 'r', 'i', 'm', 'i', 't', 'i', 'v', 'e', 's', '.', 'd', 'l', 'l', 0}\n-\tntdlldll            = [...]uint16{'n', 't', 'd', 'l', 'l', '.', 'd', 'l', 'l', 0}\n-\tpowrprofdll         = [...]uint16{'p', 'o', 'w', 'r', 'p', 'r', 'o', 'f', '.', 'd', 'l', 'l', 0}\n-\twinmmdll            = [...]uint16{'w', 'i', 'n', 'm', 'm', '.', 'd', 'l', 'l', 0}\n-)\n-\n // Function to be called by windows CreateThread\n // to start new os thread.\n func tstart_stdcall(newm *m)\n@@ -245,8 +252,18 @@\n \treturn unsafe.String(&sysDirectory[0], sysDirectoryLen)\n }\n \n-func windowsLoadSystemLib(name []uint16) uintptr {\n-\treturn stdcall3(_LoadLibraryExW, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32)\n+//go:linkname syscall_getSystemDirectory syscall.getSystemDirectory\n+func syscall_getSystemDirectory() string {\n+\treturn unsafe.String(&sysDirectory[0], sysDirectoryLen)\n+}\n+\n+func windowsLoadSystemLib(name []byte) uintptr {\n+\tif useLoadLibraryEx {\n+\t\treturn stdcall3(_LoadLibraryExA, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32)\n+\t} else {\n+\t\tabsName := append(sysDirectory[:sysDirectoryLen], name...)\n+\t\treturn stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&absName[0])))\n+\t}\n }\n \n //go:linkname windows_QueryPerformanceCounter internal/syscall/windows.QueryPerformanceCounter\n@@ -264,13 +281,28 @@\n }\n \n func loadOptionalSyscalls() {\n-\tbcryptPrimitives := windowsLoadSystemLib(bcryptprimitivesdll[:])\n-\tif bcryptPrimitives == 0 {\n-\t\tthrow(\"bcryptprimitives.dll not found\")\n+\tvar kernel32dll = []byte(\"kernel32.dll\\000\")\n+\tk32 := stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&kernel32dll[0])))\n+\tif k32 == 0 {\n+\t\tthrow(\"kernel32.dll not found\")\n \t}\n-\t_ProcessPrng = windowsFindfunc(bcryptPrimitives, []byte(\"ProcessPrng\\000\"))\n+\t_AddDllDirectory = windowsFindfunc(k32, []byte(\"AddDllDirectory\\000\"))\n+\t_AddVectoredContinueHandler = windowsFindfunc(k32, []byte(\"AddVectoredContinueHandler\\000\"))\n+\t_LoadLibraryExA = windowsFindfunc(k32, []byte(\"LoadLibraryExA\\000\"))\n+\t_LoadLibraryExW = windowsFindfunc(k32, []byte(\"LoadLibraryExW\\000\"))\n+\tuseLoadLibraryEx = (_LoadLibraryExW != nil && _LoadLibraryExA != nil && _AddDllDirectory != nil)\n+\n+\tinitSysDirectory()\n \n-\tn32 := windowsLoadSystemLib(ntdlldll[:])\n+\tvar advapi32dll = []byte(\"advapi32.dll\\000\")\n+\ta32 := windowsLoadSystemLib(advapi32dll)\n+\tif a32 == 0 {\n+\t\tthrow(\"advapi32.dll not found\")\n+\t}\n+\t_RtlGenRandom = windowsFindfunc(a32, []byte(\"SystemFunction036\\000\"))\n+\n+\tvar ntdll = []byte(\"ntdll.dll\\000\")\n+\tn32 := windowsLoadSystemLib(ntdll)\n \tif n32 == 0 {\n \t\tthrow(\"ntdll.dll not found\")\n \t}\n@@ -299,7 +331,7 @@\n \t\tcontext  uintptr\n \t}\n \n-\tpowrprof := windowsLoadSystemLib(powrprofdll[:])\n+\tpowrprof := windowsLoadSystemLib([]byte(\"powrprof.dll\\000\"))\n \tif powrprof == 0 {\n \t\treturn // Running on Windows 7, where we don't need it anyway.\n \t}\n@@ -358,6 +390,22 @@\n // in sys_windows_386.s and sys_windows_amd64.s:\n func getlasterror() uint32\n \n+// When loading DLLs, we prefer to use LoadLibraryEx with\n+// LOAD_LIBRARY_SEARCH_* flags, if available. LoadLibraryEx is not\n+// available on old Windows, though, and the LOAD_LIBRARY_SEARCH_*\n+// flags are not available on some versions of Windows without a\n+// security patch.\n+//\n+// https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says:\n+// \"Windows 7, Windows Server 2008 R2, Windows Vista, and Windows\n+// Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on\n+// systems that have KB2533623 installed. To determine whether the\n+// flags are available, use GetProcAddress to get the address of the\n+// AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories\n+// function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_*\n+// flags can be used with LoadLibraryEx.\"\n+var useLoadLibraryEx bool\n+\n var timeBeginPeriodRetValue uint32\n \n // osRelaxMinNS indicates that sysmon shouldn't osRelax if the next\n@@ -431,7 +479,8 @@\n \t\t// Only load winmm.dll if we need it.\n \t\t// This avoids a dependency on winmm.dll for Go programs\n \t\t// that run on new Windows versions.\n-\t\tm32 := windowsLoadSystemLib(winmmdll[:])\n+\t\tvar winmmdll = []byte(\"winmm.dll\\000\")\n+\t\tm32 := windowsLoadSystemLib(winmmdll)\n \t\tif m32 == 0 {\n \t\t\tprint(\"runtime: LoadLibraryExW failed; errno=\", getlasterror(), \"\\n\")\n \t\t\tthrow(\"winmm.dll not found\")\n@@ -472,6 +521,28 @@\n \tcanUseLongPaths = true\n }\n \n+var osVersionInfo struct {\n+\tmajorVersion uint32\n+\tminorVersion uint32\n+\tbuildNumber  uint32\n+}\n+\n+func initOsVersionInfo() {\n+\tinfo := _OSVERSIONINFOW{}\n+\tinfo.osVersionInfoSize = uint32(unsafe.Sizeof(info))\n+\tstdcall1(_RtlGetVersion, uintptr(unsafe.Pointer(&info)))\n+\tosVersionInfo.majorVersion = info.majorVersion\n+\tosVersionInfo.minorVersion = info.minorVersion\n+\tosVersionInfo.buildNumber = info.buildNumber\n+}\n+\n+//go:linkname rtlGetNtVersionNumbers syscall.rtlGetNtVersionNumbers\n+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) {\n+\t*majorVersion = osVersionInfo.majorVersion\n+\t*minorVersion = osVersionInfo.minorVersion\n+\t*buildNumber = osVersionInfo.buildNumber\n+}\n+\n func osinit() {\n \tasmstdcallAddr = unsafe.Pointer(abi.FuncPCABI0(asmstdcall))\n \n@@ -484,8 +555,8 @@\n \tinitHighResTimer()\n \ttimeBeginPeriodRetValue = osRelax(false)\n \n-\tinitSysDirectory()\n \tinitLongPathSupport()\n+\tinitOsVersionInfo()\n \n \tncpu = getproccount()\n \n@@ -501,7 +572,7 @@\n //go:nosplit\n func readRandom(r []byte) int {\n \tn := 0\n-\tif stdcall2(_ProcessPrng, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 {\n+\tif stdcall2(_RtlGenRandom, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 {\n \t\tn = len(r)\n \t}\n \treturn n\nIndex: src/net/hook_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/hook_windows.go b/src/net/hook_windows.go\n--- a/src/net/hook_windows.go\t(revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8)\n+++ b/src/net/hook_windows.go\t(revision 7b1fd7d39c6be0185fbe1d929578ab372ac5c632)\n@@ -13,6 +13,7 @@\n \thostsFilePath = windows.GetSystemDirectory() + \"/Drivers/etc/hosts\"\n \n \t// Placeholders for socket system calls.\n+\tsocketFunc    func(int, int, int) (syscall.Handle, error)                                                 = syscall.Socket\n \twsaSocketFunc func(int32, int32, int32, *syscall.WSAProtocolInfo, uint32, uint32) (syscall.Handle, error) = windows.WSASocket\n \tconnectFunc   func(syscall.Handle, syscall.Sockaddr) error                                                = syscall.Connect\n \tlistenFunc    func(syscall.Handle, int) error                                                             = syscall.Listen\nIndex: src/net/internal/socktest/main_test.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/internal/socktest/main_test.go b/src/net/internal/socktest/main_test.go\n--- a/src/net/internal/socktest/main_test.go\t(revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8)\n+++ b/src/net/internal/socktest/main_test.go\t(revision 7b1fd7d39c6be0185fbe1d929578ab372ac5c632)\n@@ -2,7 +2,7 @@\n // Use of this source code is governed by a BSD-style\n // license that can be found in the LICENSE file.\n \n-//go:build !js && !plan9 && !wasip1 && !windows\n+//go:build !js && !plan9 && !wasip1\n \n package socktest_test\n \nIndex: src/net/internal/socktest/main_windows_test.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/internal/socktest/main_windows_test.go b/src/net/internal/socktest/main_windows_test.go\nnew file mode 100644\n--- /dev/null\t(revision 7b1fd7d39c6be0185fbe1d929578ab372ac5c632)\n+++ b/src/net/internal/socktest/main_windows_test.go\t(revision 7b1fd7d39c6be0185fbe1d929578ab372ac5c632)\n@@ -0,0 +1,22 @@\n+// Copyright 2015 The Go Authors. All rights reserved.\n+// Use of this source code is governed by a BSD-style\n+// license that can be found in the LICENSE file.\n+\n+package socktest_test\n+\n+import \"syscall\"\n+\n+var (\n+\tsocketFunc func(int, int, int) (syscall.Handle, error)\n+\tcloseFunc  func(syscall.Handle) error\n+)\n+\n+func installTestHooks() {\n+\tsocketFunc = sw.Socket\n+\tcloseFunc = sw.Closesocket\n+}\n+\n+func uninstallTestHooks() {\n+\tsocketFunc = syscall.Socket\n+\tcloseFunc = syscall.Closesocket\n+}\nIndex: src/net/internal/socktest/sys_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/internal/socktest/sys_windows.go b/src/net/internal/socktest/sys_windows.go\n--- a/src/net/internal/socktest/sys_windows.go\t(revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8)\n+++ b/src/net/internal/socktest/sys_windows.go\t(revision 7b1fd7d39c6be0185fbe1d929578ab372ac5c632)\n@@ -9,6 +9,38 @@\n \t\"syscall\"\n )\n \n+// Socket wraps [syscall.Socket].\n+func (sw *Switch) Socket(family, sotype, proto int) (s syscall.Handle, err error) {\n+\tsw.once.Do(sw.init)\n+\n+\tso := &Status{Cookie: cookie(family, sotype, proto)}\n+\tsw.fmu.RLock()\n+\tf, _ := sw.fltab[FilterSocket]\n+\tsw.fmu.RUnlock()\n+\n+\taf, err := f.apply(so)\n+\tif err != nil {\n+\t\treturn syscall.InvalidHandle, err\n+\t}\n+\ts, so.Err = syscall.Socket(family, sotype, proto)\n+\tif err = af.apply(so); err != nil {\n+\t\tif so.Err == nil {\n+\t\t\tsyscall.Closesocket(s)\n+\t\t}\n+\t\treturn syscall.InvalidHandle, err\n+\t}\n+\n+\tsw.smu.Lock()\n+\tdefer sw.smu.Unlock()\n+\tif so.Err != nil {\n+\t\tsw.stats.getLocked(so.Cookie).OpenFailed++\n+\t\treturn syscall.InvalidHandle, so.Err\n+\t}\n+\tnso := sw.addLocked(s, family, sotype, proto)\n+\tsw.stats.getLocked(nso.Cookie).Opened++\n+\treturn s, nil\n+}\n+\n // WSASocket wraps [syscall.WSASocket].\n func (sw *Switch) WSASocket(family, sotype, proto int32, protinfo *syscall.WSAProtocolInfo, group uint32, flags uint32) (s syscall.Handle, err error) {\n \tsw.once.Do(sw.init)\nIndex: src/net/main_windows_test.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/main_windows_test.go b/src/net/main_windows_test.go\n--- a/src/net/main_windows_test.go\t(revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8)\n+++ b/src/net/main_windows_test.go\t(revision 7b1fd7d39c6be0185fbe1d929578ab372ac5c632)\n@@ -8,6 +8,7 @@\n \n var (\n \t// Placeholders for saving original socket system calls.\n+\torigSocket      = socketFunc\n \torigWSASocket   = wsaSocketFunc\n \torigClosesocket = poll.CloseFunc\n \torigConnect     = connectFunc\n@@ -17,6 +18,7 @@\n )\n \n func installTestHooks() {\n+\tsocketFunc = sw.Socket\n \twsaSocketFunc = sw.WSASocket\n \tpoll.CloseFunc = sw.Closesocket\n \tconnectFunc = sw.Connect\n@@ -26,6 +28,7 @@\n }\n \n func uninstallTestHooks() {\n+\tsocketFunc = origSocket\n \twsaSocketFunc = origWSASocket\n \tpoll.CloseFunc = origClosesocket\n \tconnectFunc = origConnect\nIndex: src/net/sock_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/sock_windows.go b/src/net/sock_windows.go\n--- a/src/net/sock_windows.go\t(revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8)\n+++ b/src/net/sock_windows.go\t(revision 7b1fd7d39c6be0185fbe1d929578ab372ac5c632)\n@@ -20,6 +20,21 @@\n func sysSocket(family, sotype, proto int) (syscall.Handle, error) {\n \ts, err := wsaSocketFunc(int32(family), int32(sotype), int32(proto),\n \t\tnil, 0, windows.WSA_FLAG_OVERLAPPED|windows.WSA_FLAG_NO_HANDLE_INHERIT)\n+\tif err == nil {\n+\t\treturn s, nil\n+\t}\n+\t// WSA_FLAG_NO_HANDLE_INHERIT flag is not supported on some\n+\t// old versions of Windows, see\n+\t// https://msdn.microsoft.com/en-us/library/windows/desktop/ms742212(v=vs.85).aspx\n+\t// for details. Just use syscall.Socket, if windows.WSASocket failed.\n+\n+\t// See ../syscall/exec_unix.go for description of ForkLock.\n+\tsyscall.ForkLock.RLock()\n+\ts, err = socketFunc(family, sotype, proto)\n+\tif err == nil {\n+\t\tsyscall.CloseOnExec(s)\n+\t}\n+\tsyscall.ForkLock.RUnlock()\n \tif err != nil {\n \t\treturn syscall.InvalidHandle, os.NewSyscallError(\"socket\", err)\n \t}\nIndex: src/syscall/exec_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go\n--- a/src/syscall/exec_windows.go\t(revision 2a406dc9f1ea7323d6ca9fccb2fe9ddebb6b1cc8)\n+++ b/src/syscall/exec_windows.go\t(revision 979d6d8bab3823ff572ace26767fd2ce3cf351ae)\n@@ -14,7 +14,6 @@\n \t\"unsafe\"\n )\n \n-// ForkLock is not used on Windows.\n var ForkLock sync.RWMutex\n \n // EscapeArg rewrites command line argument s as prescribed\n@@ -254,6 +253,9 @@\n var zeroProcAttr ProcAttr\n var zeroSysProcAttr SysProcAttr\n \n+//go:linkname rtlGetNtVersionNumbers\n+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32)\n+\n func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) {\n \tif len(argv0) == 0 {\n \t\treturn 0, 0, EWINDOWS\n@@ -317,6 +319,17 @@\n \t\t}\n \t}\n \n+\tvar maj, min, build uint32\n+\trtlGetNtVersionNumbers(&maj, &min, &build)\n+\tisWin7 := maj < 6 || (maj == 6 && min <= 1)\n+\t// NT kernel handles are divisible by 4, with the bottom 3 bits left as\n+\t// a tag. The fully set tag correlates with the types of handles we're\n+\t// concerned about here.  Except, the kernel will interpret some\n+\t// special handle values, like -1, -2, and so forth, so kernelbase.dll\n+\t// checks to see that those bottom three bits are checked, but that top\n+\t// bit is not checked.\n+\tisLegacyWin7ConsoleHandle := func(handle Handle) bool { return isWin7 && handle&0x10000003 == 3 }\n+\n \tp, _ := GetCurrentProcess()\n \tparentProcess := p\n \tif sys.ParentProcess != 0 {\n@@ -325,7 +338,15 @@\n \tfd := make([]Handle, len(attr.Files))\n \tfor i := range attr.Files {\n \t\tif attr.Files[i] > 0 {\n-\t\t\terr := DuplicateHandle(p, Handle(attr.Files[i]), parentProcess, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)\n+\t\t\tdestinationProcessHandle := parentProcess\n+\n+\t\t\t// On Windows 7, console handles aren't real handles, and can only be duplicated\n+\t\t\t// into the current process, not a parent one, which amounts to the same thing.\n+\t\t\tif parentProcess != p && isLegacyWin7ConsoleHandle(Handle(attr.Files[i])) {\n+\t\t\t\tdestinationProcessHandle = p\n+\t\t\t}\n+\n+\t\t\terr := DuplicateHandle(p, Handle(attr.Files[i]), destinationProcessHandle, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)\n \t\t\tif err != nil {\n \t\t\t\treturn 0, 0, err\n \t\t\t}\n@@ -356,6 +377,14 @@\n \n \tfd = append(fd, sys.AdditionalInheritedHandles...)\n \n+\t// On Windows 7, console handles aren't real handles, so don't pass them\n+\t// through to PROC_THREAD_ATTRIBUTE_HANDLE_LIST.\n+\tfor i := range fd {\n+\t\tif isLegacyWin7ConsoleHandle(fd[i]) {\n+\t\t\tfd[i] = 0\n+\t\t}\n+\t}\n+\n \t// The presence of a NULL handle in the list is enough to cause PROC_THREAD_ATTRIBUTE_HANDLE_LIST\n \t// to treat the entire list as empty, so remove NULL handles.\n \tj := 0\nIndex: src/runtime/syscall_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/runtime/syscall_windows.go b/src/runtime/syscall_windows.go\n--- a/src/runtime/syscall_windows.go\t(revision 979d6d8bab3823ff572ace26767fd2ce3cf351ae)\n+++ b/src/runtime/syscall_windows.go\t(revision ac3e93c061779dfefc0dd13a5b6e6f764a25621e)\n@@ -413,10 +413,20 @@\n \n const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800\n \n+// When available, this function will use LoadLibraryEx with the filename\n+// parameter and the important SEARCH_SYSTEM32 argument. But on systems that\n+// do not have that option, absoluteFilepath should contain a fallback\n+// to the full path inside of system32 for use with vanilla LoadLibrary.\n+//\n //go:linkname syscall_loadsystemlibrary syscall.loadsystemlibrary\n-func syscall_loadsystemlibrary(filename *uint16) (handle, err uintptr) {\n-\thandle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryExW)), uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32)\n+func syscall_loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle, err uintptr) {\n+\tif useLoadLibraryEx {\n+\t\thandle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryExW)), uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32)\n+\t} else {\n+\t\thandle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryW)), uintptr(unsafe.Pointer(absoluteFilepath)))\n+\t}\n \tKeepAlive(filename)\n+\tKeepAlive(absoluteFilepath)\n \tif handle != 0 {\n \t\terr = 0\n \t}\nIndex: src/syscall/dll_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/syscall/dll_windows.go b/src/syscall/dll_windows.go\n--- a/src/syscall/dll_windows.go\t(revision 979d6d8bab3823ff572ace26767fd2ce3cf351ae)\n+++ b/src/syscall/dll_windows.go\t(revision ac3e93c061779dfefc0dd13a5b6e6f764a25621e)\n@@ -45,7 +45,7 @@\n //go:noescape\n func SyscallN(trap uintptr, args ...uintptr) (r1, r2 uintptr, err Errno)\n func loadlibrary(filename *uint16) (handle uintptr, err Errno)\n-func loadsystemlibrary(filename *uint16) (handle uintptr, err Errno)\n+func loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle uintptr, err Errno)\n func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno)\n \n // A DLL implements access to a single DLL.\n@@ -54,6 +54,9 @@\n \tHandle Handle\n }\n \n+//go:linkname getSystemDirectory\n+func getSystemDirectory() string // Implemented in runtime package.\n+\n // LoadDLL loads the named DLL file into memory.\n //\n // If name is not an absolute path and is not a known system DLL used by\n@@ -70,7 +73,11 @@\n \tvar h uintptr\n \tvar e Errno\n \tif sysdll.IsSystemDLL[name] {\n-\t\th, e = loadsystemlibrary(namep)\n+\t\tabsoluteFilepathp, err := UTF16PtrFromString(getSystemDirectory() + name)\n+\t\tif err != nil {\n+\t\t\treturn nil, err\n+\t\t}\n+\t\th, e = loadsystemlibrary(namep, absoluteFilepathp)\n \t} else {\n \t\th, e = loadlibrary(namep)\n \t}\n"
  },
  {
    "path": "core/Clash.Meta/.github/patch/go1.25.patch",
    "content": "Subject: [PATCH] Revert \"os: remove 5ms sleep on Windows in (*Process).Wait\"\nFix os.RemoveAll not working on Windows7\nRevert \"runtime: always use LoadLibraryEx to load system libraries\"\nRevert \"syscall: remove Windows 7 console handle workaround\"\nRevert \"net: remove sysSocket fallback for Windows 7\"\nRevert \"crypto/rand,runtime: switch RtlGenRandom for ProcessPrng\"\n---\nIndex: src/crypto/internal/sysrand/rand_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/crypto/internal/sysrand/rand_windows.go b/src/crypto/internal/sysrand/rand_windows.go\n--- a/src/crypto/internal/sysrand/rand_windows.go\t(revision 439ff996f0ee506fc2eb84b7f11ffc360a6299f2)\n+++ b/src/crypto/internal/sysrand/rand_windows.go\t(revision 466f6c7a29bc098b0d4c987b803c779222894a11)\n@@ -7,5 +7,26 @@\n import \"internal/syscall/windows\"\n \n func read(b []byte) error {\n-\treturn windows.ProcessPrng(b)\n+\t// RtlGenRandom only returns 1<<32-1 bytes at a time. We only read at\n+\t// most 1<<31-1 bytes at a time so that  this works the same on 32-bit\n+\t// and 64-bit systems.\n+\treturn batched(windows.RtlGenRandom, 1<<31-1)(b)\n+}\n+\n+// batched returns a function that calls f to populate a []byte by chunking it\n+// into subslices of, at most, readMax bytes.\n+func batched(f func([]byte) error, readMax int) func([]byte) error {\n+\treturn func(out []byte) error {\n+\t\tfor len(out) > 0 {\n+\t\t\tread := len(out)\n+\t\t\tif read > readMax {\n+\t\t\t\tread = readMax\n+\t\t\t}\n+\t\t\tif err := f(out[:read]); err != nil {\n+\t\t\t\treturn err\n+\t\t\t}\n+\t\t\tout = out[read:]\n+\t\t}\n+\t\treturn nil\n+\t}\n }\nIndex: src/crypto/rand/rand.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go\n--- a/src/crypto/rand/rand.go\t(revision 439ff996f0ee506fc2eb84b7f11ffc360a6299f2)\n+++ b/src/crypto/rand/rand.go\t(revision 466f6c7a29bc098b0d4c987b803c779222894a11)\n@@ -22,7 +22,7 @@\n //   - On legacy Linux (< 3.17), Reader opens /dev/urandom on first use.\n //   - On macOS, iOS, and OpenBSD Reader, uses arc4random_buf(3).\n //   - On NetBSD, Reader uses the kern.arandom sysctl.\n-//   - On Windows, Reader uses the ProcessPrng API.\n+//   - On Windows systems, Reader uses the RtlGenRandom API.\n //   - On js/wasm, Reader uses the Web Crypto API.\n //   - On wasip1/wasm, Reader uses random_get.\n //\nIndex: src/internal/syscall/windows/syscall_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go\n--- a/src/internal/syscall/windows/syscall_windows.go\t(revision 439ff996f0ee506fc2eb84b7f11ffc360a6299f2)\n+++ b/src/internal/syscall/windows/syscall_windows.go\t(revision 466f6c7a29bc098b0d4c987b803c779222894a11)\n@@ -419,7 +419,7 @@\n //sys\tDestroyEnvironmentBlock(block *uint16) (err error) = userenv.DestroyEnvironmentBlock\n //sys\tCreateEvent(eventAttrs *SecurityAttributes, manualReset uint32, initialState uint32, name *uint16) (handle syscall.Handle, err error) = kernel32.CreateEventW\n \n-//sys\tProcessPrng(buf []byte) (err error) = bcryptprimitives.ProcessPrng\n+//sys\tRtlGenRandom(buf []byte) (err error) = advapi32.SystemFunction036\n \n type FILE_ID_BOTH_DIR_INFO struct {\n \tNextEntryOffset uint32\nIndex: src/internal/syscall/windows/zsyscall_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go\n--- a/src/internal/syscall/windows/zsyscall_windows.go\t(revision 439ff996f0ee506fc2eb84b7f11ffc360a6299f2)\n+++ b/src/internal/syscall/windows/zsyscall_windows.go\t(revision 466f6c7a29bc098b0d4c987b803c779222894a11)\n@@ -38,7 +38,6 @@\n \n var (\n \tmodadvapi32         = syscall.NewLazyDLL(sysdll.Add(\"advapi32.dll\"))\n-\tmodbcryptprimitives = syscall.NewLazyDLL(sysdll.Add(\"bcryptprimitives.dll\"))\n \tmodiphlpapi         = syscall.NewLazyDLL(sysdll.Add(\"iphlpapi.dll\"))\n \tmodkernel32         = syscall.NewLazyDLL(sysdll.Add(\"kernel32.dll\"))\n \tmodnetapi32         = syscall.NewLazyDLL(sysdll.Add(\"netapi32.dll\"))\n@@ -65,7 +64,7 @@\n \tprocSetEntriesInAclW                  = modadvapi32.NewProc(\"SetEntriesInAclW\")\n \tprocSetNamedSecurityInfoW             = modadvapi32.NewProc(\"SetNamedSecurityInfoW\")\n \tprocSetTokenInformation               = modadvapi32.NewProc(\"SetTokenInformation\")\n-\tprocProcessPrng                       = modbcryptprimitives.NewProc(\"ProcessPrng\")\n+\tprocSystemFunction036                 = modadvapi32.NewProc(\"SystemFunction036\")\n \tprocGetAdaptersAddresses              = modiphlpapi.NewProc(\"GetAdaptersAddresses\")\n \tprocCreateEventW                      = modkernel32.NewProc(\"CreateEventW\")\n \tprocCreateIoCompletionPort            = modkernel32.NewProc(\"CreateIoCompletionPort\")\n@@ -270,12 +269,12 @@\n \treturn\n }\n \n-func ProcessPrng(buf []byte) (err error) {\n+func RtlGenRandom(buf []byte) (err error) {\n \tvar _p0 *byte\n \tif len(buf) > 0 {\n \t\t_p0 = &buf[0]\n \t}\n-\tr1, _, e1 := syscall.SyscallN(procProcessPrng.Addr(), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)))\n+\tr1, _, e1 := syscall.SyscallN(procSystemFunction036.Addr(), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)))\n \tif r1 == 0 {\n \t\terr = errnoErr(e1)\n \t}\nIndex: src/runtime/os_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go\n--- a/src/runtime/os_windows.go\t(revision 439ff996f0ee506fc2eb84b7f11ffc360a6299f2)\n+++ b/src/runtime/os_windows.go\t(revision f6bddda4e8ff58a957462a1a09562924d5f3d05c)\n@@ -39,8 +39,8 @@\n //go:cgo_import_dynamic runtime._GetSystemInfo GetSystemInfo%1 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._GetThreadContext GetThreadContext%2 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._SetThreadContext SetThreadContext%2 \"kernel32.dll\"\n-//go:cgo_import_dynamic runtime._LoadLibraryExW LoadLibraryExW%3 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 \"kernel32.dll\"\n+//go:cgo_import_dynamic runtime._LoadLibraryA LoadLibraryA%1 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._PostQueuedCompletionStatus PostQueuedCompletionStatus%4 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._QueryPerformanceCounter QueryPerformanceCounter%1 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._QueryPerformanceFrequency QueryPerformanceFrequency%1 \"kernel32.dll\"\n@@ -74,7 +74,6 @@\n \t// Following syscalls are available on every Windows PC.\n \t// All these variables are set by the Windows executable\n \t// loader before the Go program starts.\n-\t_AddVectoredContinueHandler,\n \t_AddVectoredExceptionHandler,\n \t_CloseHandle,\n \t_CreateEventA,\n@@ -97,8 +96,8 @@\n \t_GetSystemInfo,\n \t_GetThreadContext,\n \t_SetThreadContext,\n-\t_LoadLibraryExW,\n \t_LoadLibraryW,\n+\t_LoadLibraryA,\n \t_PostQueuedCompletionStatus,\n \t_QueryPerformanceCounter,\n \t_QueryPerformanceFrequency,\n@@ -127,8 +126,23 @@\n \t_WriteFile,\n \t_ stdFunction\n \n-\t// Use ProcessPrng to generate cryptographically random data.\n-\t_ProcessPrng stdFunction\n+\t// Following syscalls are only available on some Windows PCs.\n+\t// We will load syscalls, if available, before using them.\n+\t_AddDllDirectory,\n+\t_AddVectoredContinueHandler,\n+\t_LoadLibraryExA,\n+\t_LoadLibraryExW,\n+\t_ stdFunction\n+\n+\t// Use RtlGenRandom to generate cryptographically random data.\n+\t// This approach has been recommended by Microsoft (see issue\n+\t// 15589 for details).\n+\t// The RtlGenRandom is not listed in advapi32.dll, instead\n+\t// RtlGenRandom function can be found by searching for SystemFunction036.\n+\t// Also some versions of Mingw cannot link to SystemFunction036\n+\t// when building executable as Cgo. So load SystemFunction036\n+\t// manually during runtime startup.\n+\t_RtlGenRandom stdFunction\n \n \t// Load ntdll.dll manually during startup, otherwise Mingw\n \t// links wrong printf function to cgo executable (see issue\n@@ -145,13 +159,6 @@\n \t_ stdFunction\n )\n \n-var (\n-\tbcryptprimitivesdll = [...]uint16{'b', 'c', 'r', 'y', 'p', 't', 'p', 'r', 'i', 'm', 'i', 't', 'i', 'v', 'e', 's', '.', 'd', 'l', 'l', 0}\n-\tntdlldll            = [...]uint16{'n', 't', 'd', 'l', 'l', '.', 'd', 'l', 'l', 0}\n-\tpowrprofdll         = [...]uint16{'p', 'o', 'w', 'r', 'p', 'r', 'o', 'f', '.', 'd', 'l', 'l', 0}\n-\twinmmdll            = [...]uint16{'w', 'i', 'n', 'm', 'm', '.', 'd', 'l', 'l', 0}\n-)\n-\n // Function to be called by windows CreateThread\n // to start new os thread.\n func tstart_stdcall(newm *m)\n@@ -244,8 +251,18 @@\n \treturn unsafe.String(&sysDirectory[0], sysDirectoryLen)\n }\n \n-func windowsLoadSystemLib(name []uint16) uintptr {\n-\treturn stdcall3(_LoadLibraryExW, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32)\n+//go:linkname syscall_getSystemDirectory syscall.getSystemDirectory\n+func syscall_getSystemDirectory() string {\n+\treturn unsafe.String(&sysDirectory[0], sysDirectoryLen)\n+}\n+\n+func windowsLoadSystemLib(name []byte) uintptr {\n+\tif useLoadLibraryEx {\n+\t\treturn stdcall3(_LoadLibraryExA, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32)\n+\t} else {\n+\t\tabsName := append(sysDirectory[:sysDirectoryLen], name...)\n+\t\treturn stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&absName[0])))\n+\t}\n }\n \n //go:linkname windows_QueryPerformanceCounter internal/syscall/windows.QueryPerformanceCounter\n@@ -263,13 +280,28 @@\n }\n \n func loadOptionalSyscalls() {\n-\tbcryptPrimitives := windowsLoadSystemLib(bcryptprimitivesdll[:])\n-\tif bcryptPrimitives == 0 {\n-\t\tthrow(\"bcryptprimitives.dll not found\")\n+\tvar kernel32dll = []byte(\"kernel32.dll\\000\")\n+\tk32 := stdcall1(_LoadLibraryA, uintptr(unsafe.Pointer(&kernel32dll[0])))\n+\tif k32 == 0 {\n+\t\tthrow(\"kernel32.dll not found\")\n \t}\n-\t_ProcessPrng = windowsFindfunc(bcryptPrimitives, []byte(\"ProcessPrng\\000\"))\n+\t_AddDllDirectory = windowsFindfunc(k32, []byte(\"AddDllDirectory\\000\"))\n+\t_AddVectoredContinueHandler = windowsFindfunc(k32, []byte(\"AddVectoredContinueHandler\\000\"))\n+\t_LoadLibraryExA = windowsFindfunc(k32, []byte(\"LoadLibraryExA\\000\"))\n+\t_LoadLibraryExW = windowsFindfunc(k32, []byte(\"LoadLibraryExW\\000\"))\n+\tuseLoadLibraryEx = (_LoadLibraryExW != nil && _LoadLibraryExA != nil && _AddDllDirectory != nil)\n+\n+\tinitSysDirectory()\n \n-\tn32 := windowsLoadSystemLib(ntdlldll[:])\n+\tvar advapi32dll = []byte(\"advapi32.dll\\000\")\n+\ta32 := windowsLoadSystemLib(advapi32dll)\n+\tif a32 == 0 {\n+\t\tthrow(\"advapi32.dll not found\")\n+\t}\n+\t_RtlGenRandom = windowsFindfunc(a32, []byte(\"SystemFunction036\\000\"))\n+\n+\tvar ntdll = []byte(\"ntdll.dll\\000\")\n+\tn32 := windowsLoadSystemLib(ntdll)\n \tif n32 == 0 {\n \t\tthrow(\"ntdll.dll not found\")\n \t}\n@@ -298,7 +330,7 @@\n \t\tcontext  uintptr\n \t}\n \n-\tpowrprof := windowsLoadSystemLib(powrprofdll[:])\n+\tpowrprof := windowsLoadSystemLib([]byte(\"powrprof.dll\\000\"))\n \tif powrprof == 0 {\n \t\treturn // Running on Windows 7, where we don't need it anyway.\n \t}\n@@ -357,6 +389,22 @@\n // in sys_windows_386.s and sys_windows_amd64.s:\n func getlasterror() uint32\n \n+// When loading DLLs, we prefer to use LoadLibraryEx with\n+// LOAD_LIBRARY_SEARCH_* flags, if available. LoadLibraryEx is not\n+// available on old Windows, though, and the LOAD_LIBRARY_SEARCH_*\n+// flags are not available on some versions of Windows without a\n+// security patch.\n+//\n+// https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says:\n+// \"Windows 7, Windows Server 2008 R2, Windows Vista, and Windows\n+// Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on\n+// systems that have KB2533623 installed. To determine whether the\n+// flags are available, use GetProcAddress to get the address of the\n+// AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories\n+// function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_*\n+// flags can be used with LoadLibraryEx.\"\n+var useLoadLibraryEx bool\n+\n var timeBeginPeriodRetValue uint32\n \n // osRelaxMinNS indicates that sysmon shouldn't osRelax if the next\n@@ -430,7 +478,8 @@\n \t\t// Only load winmm.dll if we need it.\n \t\t// This avoids a dependency on winmm.dll for Go programs\n \t\t// that run on new Windows versions.\n-\t\tm32 := windowsLoadSystemLib(winmmdll[:])\n+\t\tvar winmmdll = []byte(\"winmm.dll\\000\")\n+\t\tm32 := windowsLoadSystemLib(winmmdll)\n \t\tif m32 == 0 {\n \t\t\tprint(\"runtime: LoadLibraryExW failed; errno=\", getlasterror(), \"\\n\")\n \t\t\tthrow(\"winmm.dll not found\")\n@@ -471,6 +520,28 @@\n \tcanUseLongPaths = true\n }\n \n+var osVersionInfo struct {\n+\tmajorVersion uint32\n+\tminorVersion uint32\n+\tbuildNumber  uint32\n+}\n+\n+func initOsVersionInfo() {\n+\tinfo := _OSVERSIONINFOW{}\n+\tinfo.osVersionInfoSize = uint32(unsafe.Sizeof(info))\n+\tstdcall1(_RtlGetVersion, uintptr(unsafe.Pointer(&info)))\n+\tosVersionInfo.majorVersion = info.majorVersion\n+\tosVersionInfo.minorVersion = info.minorVersion\n+\tosVersionInfo.buildNumber = info.buildNumber\n+}\n+\n+//go:linkname rtlGetNtVersionNumbers syscall.rtlGetNtVersionNumbers\n+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) {\n+\t*majorVersion = osVersionInfo.majorVersion\n+\t*minorVersion = osVersionInfo.minorVersion\n+\t*buildNumber = osVersionInfo.buildNumber\n+}\n+\n func osinit() {\n \tasmstdcallAddr = unsafe.Pointer(abi.FuncPCABI0(asmstdcall))\n \n@@ -483,8 +554,8 @@\n \tinitHighResTimer()\n \ttimeBeginPeriodRetValue = osRelax(false)\n \n-\tinitSysDirectory()\n \tinitLongPathSupport()\n+\tinitOsVersionInfo()\n \n \tnumCPUStartup = getCPUCount()\n \n@@ -500,7 +571,7 @@\n //go:nosplit\n func readRandom(r []byte) int {\n \tn := 0\n-\tif stdcall2(_ProcessPrng, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 {\n+\tif stdcall2(_RtlGenRandom, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 {\n \t\tn = len(r)\n \t}\n \treturn n\nIndex: src/net/hook_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/hook_windows.go b/src/net/hook_windows.go\n--- a/src/net/hook_windows.go\t(revision 466f6c7a29bc098b0d4c987b803c779222894a11)\n+++ b/src/net/hook_windows.go\t(revision 1bdabae205052afe1dadb2ad6f1ba612cdbc532a)\n@@ -13,6 +13,7 @@\n \thostsFilePath = windows.GetSystemDirectory() + \"/Drivers/etc/hosts\"\n \n \t// Placeholders for socket system calls.\n+\tsocketFunc    func(int, int, int) (syscall.Handle, error)                                                 = syscall.Socket\n \twsaSocketFunc func(int32, int32, int32, *syscall.WSAProtocolInfo, uint32, uint32) (syscall.Handle, error) = windows.WSASocket\n \tconnectFunc   func(syscall.Handle, syscall.Sockaddr) error                                                = syscall.Connect\n \tlistenFunc    func(syscall.Handle, int) error                                                             = syscall.Listen\nIndex: src/net/internal/socktest/main_test.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/internal/socktest/main_test.go b/src/net/internal/socktest/main_test.go\n--- a/src/net/internal/socktest/main_test.go\t(revision 466f6c7a29bc098b0d4c987b803c779222894a11)\n+++ b/src/net/internal/socktest/main_test.go\t(revision 1bdabae205052afe1dadb2ad6f1ba612cdbc532a)\n@@ -2,7 +2,7 @@\n // Use of this source code is governed by a BSD-style\n // license that can be found in the LICENSE file.\n \n-//go:build !js && !plan9 && !wasip1 && !windows\n+//go:build !js && !plan9 && !wasip1\n \n package socktest_test\n \nIndex: src/net/internal/socktest/main_windows_test.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/internal/socktest/main_windows_test.go b/src/net/internal/socktest/main_windows_test.go\nnew file mode 100644\n--- /dev/null\t(revision 1bdabae205052afe1dadb2ad6f1ba612cdbc532a)\n+++ b/src/net/internal/socktest/main_windows_test.go\t(revision 1bdabae205052afe1dadb2ad6f1ba612cdbc532a)\n@@ -0,0 +1,22 @@\n+// Copyright 2015 The Go Authors. All rights reserved.\n+// Use of this source code is governed by a BSD-style\n+// license that can be found in the LICENSE file.\n+\n+package socktest_test\n+\n+import \"syscall\"\n+\n+var (\n+\tsocketFunc func(int, int, int) (syscall.Handle, error)\n+\tcloseFunc  func(syscall.Handle) error\n+)\n+\n+func installTestHooks() {\n+\tsocketFunc = sw.Socket\n+\tcloseFunc = sw.Closesocket\n+}\n+\n+func uninstallTestHooks() {\n+\tsocketFunc = syscall.Socket\n+\tcloseFunc = syscall.Closesocket\n+}\nIndex: src/net/internal/socktest/sys_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/internal/socktest/sys_windows.go b/src/net/internal/socktest/sys_windows.go\n--- a/src/net/internal/socktest/sys_windows.go\t(revision 466f6c7a29bc098b0d4c987b803c779222894a11)\n+++ b/src/net/internal/socktest/sys_windows.go\t(revision 1bdabae205052afe1dadb2ad6f1ba612cdbc532a)\n@@ -9,6 +9,38 @@\n \t\"syscall\"\n )\n \n+// Socket wraps [syscall.Socket].\n+func (sw *Switch) Socket(family, sotype, proto int) (s syscall.Handle, err error) {\n+\tsw.once.Do(sw.init)\n+\n+\tso := &Status{Cookie: cookie(family, sotype, proto)}\n+\tsw.fmu.RLock()\n+\tf, _ := sw.fltab[FilterSocket]\n+\tsw.fmu.RUnlock()\n+\n+\taf, err := f.apply(so)\n+\tif err != nil {\n+\t\treturn syscall.InvalidHandle, err\n+\t}\n+\ts, so.Err = syscall.Socket(family, sotype, proto)\n+\tif err = af.apply(so); err != nil {\n+\t\tif so.Err == nil {\n+\t\t\tsyscall.Closesocket(s)\n+\t\t}\n+\t\treturn syscall.InvalidHandle, err\n+\t}\n+\n+\tsw.smu.Lock()\n+\tdefer sw.smu.Unlock()\n+\tif so.Err != nil {\n+\t\tsw.stats.getLocked(so.Cookie).OpenFailed++\n+\t\treturn syscall.InvalidHandle, so.Err\n+\t}\n+\tnso := sw.addLocked(s, family, sotype, proto)\n+\tsw.stats.getLocked(nso.Cookie).Opened++\n+\treturn s, nil\n+}\n+\n // WSASocket wraps [syscall.WSASocket].\n func (sw *Switch) WSASocket(family, sotype, proto int32, protinfo *syscall.WSAProtocolInfo, group uint32, flags uint32) (s syscall.Handle, err error) {\n \tsw.once.Do(sw.init)\nIndex: src/net/main_windows_test.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/main_windows_test.go b/src/net/main_windows_test.go\n--- a/src/net/main_windows_test.go\t(revision 466f6c7a29bc098b0d4c987b803c779222894a11)\n+++ b/src/net/main_windows_test.go\t(revision 1bdabae205052afe1dadb2ad6f1ba612cdbc532a)\n@@ -12,6 +12,7 @@\n \n var (\n \t// Placeholders for saving original socket system calls.\n+\torigSocket      = socketFunc\n \torigWSASocket   = wsaSocketFunc\n \torigClosesocket = poll.CloseFunc\n \torigConnect     = connectFunc\n@@ -21,6 +22,7 @@\n )\n \n func installTestHooks() {\n+\tsocketFunc = sw.Socket\n \twsaSocketFunc = sw.WSASocket\n \tpoll.CloseFunc = sw.Closesocket\n \tconnectFunc = sw.Connect\n@@ -30,6 +32,7 @@\n }\n \n func uninstallTestHooks() {\n+\tsocketFunc = origSocket\n \twsaSocketFunc = origWSASocket\n \tpoll.CloseFunc = origClosesocket\n \tconnectFunc = origConnect\nIndex: src/net/sock_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/sock_windows.go b/src/net/sock_windows.go\n--- a/src/net/sock_windows.go\t(revision 466f6c7a29bc098b0d4c987b803c779222894a11)\n+++ b/src/net/sock_windows.go\t(revision 1bdabae205052afe1dadb2ad6f1ba612cdbc532a)\n@@ -20,6 +20,21 @@\n func sysSocket(family, sotype, proto int) (syscall.Handle, error) {\n \ts, err := wsaSocketFunc(int32(family), int32(sotype), int32(proto),\n \t\tnil, 0, windows.WSA_FLAG_OVERLAPPED|windows.WSA_FLAG_NO_HANDLE_INHERIT)\n+\tif err == nil {\n+\t\treturn s, nil\n+\t}\n+\t// WSA_FLAG_NO_HANDLE_INHERIT flag is not supported on some\n+\t// old versions of Windows, see\n+\t// https://msdn.microsoft.com/en-us/library/windows/desktop/ms742212(v=vs.85).aspx\n+\t// for details. Just use syscall.Socket, if windows.WSASocket failed.\n+\n+\t// See ../syscall/exec_unix.go for description of ForkLock.\n+\tsyscall.ForkLock.RLock()\n+\ts, err = socketFunc(family, sotype, proto)\n+\tif err == nil {\n+\t\tsyscall.CloseOnExec(s)\n+\t}\n+\tsyscall.ForkLock.RUnlock()\n \tif err != nil {\n \t\treturn syscall.InvalidHandle, os.NewSyscallError(\"socket\", err)\n \t}\nIndex: src/syscall/exec_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go\n--- a/src/syscall/exec_windows.go\t(revision 466f6c7a29bc098b0d4c987b803c779222894a11)\n+++ b/src/syscall/exec_windows.go\t(revision a90777dcf692dd2168577853ba743b4338721b06)\n@@ -14,7 +14,6 @@\n \t\"unsafe\"\n )\n \n-// ForkLock is not used on Windows.\n var ForkLock sync.RWMutex\n \n // EscapeArg rewrites command line argument s as prescribed\n@@ -254,6 +253,9 @@\n var zeroProcAttr ProcAttr\n var zeroSysProcAttr SysProcAttr\n \n+//go:linkname rtlGetNtVersionNumbers\n+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32)\n+\n func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) {\n \tif len(argv0) == 0 {\n \t\treturn 0, 0, EWINDOWS\n@@ -317,6 +319,17 @@\n \t\t}\n \t}\n \n+\tvar maj, min, build uint32\n+\trtlGetNtVersionNumbers(&maj, &min, &build)\n+\tisWin7 := maj < 6 || (maj == 6 && min <= 1)\n+\t// NT kernel handles are divisible by 4, with the bottom 3 bits left as\n+\t// a tag. The fully set tag correlates with the types of handles we're\n+\t// concerned about here.  Except, the kernel will interpret some\n+\t// special handle values, like -1, -2, and so forth, so kernelbase.dll\n+\t// checks to see that those bottom three bits are checked, but that top\n+\t// bit is not checked.\n+\tisLegacyWin7ConsoleHandle := func(handle Handle) bool { return isWin7 && handle&0x10000003 == 3 }\n+\n \tp, _ := GetCurrentProcess()\n \tparentProcess := p\n \tif sys.ParentProcess != 0 {\n@@ -325,7 +338,15 @@\n \tfd := make([]Handle, len(attr.Files))\n \tfor i := range attr.Files {\n \t\tif attr.Files[i] > 0 {\n-\t\t\terr := DuplicateHandle(p, Handle(attr.Files[i]), parentProcess, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)\n+\t\t\tdestinationProcessHandle := parentProcess\n+\n+\t\t\t// On Windows 7, console handles aren't real handles, and can only be duplicated\n+\t\t\t// into the current process, not a parent one, which amounts to the same thing.\n+\t\t\tif parentProcess != p && isLegacyWin7ConsoleHandle(Handle(attr.Files[i])) {\n+\t\t\t\tdestinationProcessHandle = p\n+\t\t\t}\n+\n+\t\t\terr := DuplicateHandle(p, Handle(attr.Files[i]), destinationProcessHandle, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)\n \t\t\tif err != nil {\n \t\t\t\treturn 0, 0, err\n \t\t\t}\n@@ -356,6 +377,14 @@\n \n \tfd = append(fd, sys.AdditionalInheritedHandles...)\n \n+\t// On Windows 7, console handles aren't real handles, so don't pass them\n+\t// through to PROC_THREAD_ATTRIBUTE_HANDLE_LIST.\n+\tfor i := range fd {\n+\t\tif isLegacyWin7ConsoleHandle(fd[i]) {\n+\t\t\tfd[i] = 0\n+\t\t}\n+\t}\n+\n \t// The presence of a NULL handle in the list is enough to cause PROC_THREAD_ATTRIBUTE_HANDLE_LIST\n \t// to treat the entire list as empty, so remove NULL handles.\n \tj := 0\nIndex: src/runtime/syscall_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/runtime/syscall_windows.go b/src/runtime/syscall_windows.go\n--- a/src/runtime/syscall_windows.go\t(revision a90777dcf692dd2168577853ba743b4338721b06)\n+++ b/src/runtime/syscall_windows.go\t(revision f6bddda4e8ff58a957462a1a09562924d5f3d05c)\n@@ -413,10 +413,20 @@\n \n const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800\n \n+// When available, this function will use LoadLibraryEx with the filename\n+// parameter and the important SEARCH_SYSTEM32 argument. But on systems that\n+// do not have that option, absoluteFilepath should contain a fallback\n+// to the full path inside of system32 for use with vanilla LoadLibrary.\n+//\n //go:linkname syscall_loadsystemlibrary syscall.loadsystemlibrary\n-func syscall_loadsystemlibrary(filename *uint16) (handle, err uintptr) {\n-\thandle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryExW)), uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32)\n+func syscall_loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle, err uintptr) {\n+\tif useLoadLibraryEx {\n+\t\thandle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryExW)), uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32)\n+\t} else {\n+\t\thandle, _, err = syscall_SyscallN(uintptr(unsafe.Pointer(_LoadLibraryW)), uintptr(unsafe.Pointer(absoluteFilepath)))\n+\t}\n \tKeepAlive(filename)\n+\tKeepAlive(absoluteFilepath)\n \tif handle != 0 {\n \t\terr = 0\n \t}\nIndex: src/syscall/dll_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/syscall/dll_windows.go b/src/syscall/dll_windows.go\n--- a/src/syscall/dll_windows.go\t(revision a90777dcf692dd2168577853ba743b4338721b06)\n+++ b/src/syscall/dll_windows.go\t(revision f6bddda4e8ff58a957462a1a09562924d5f3d05c)\n@@ -45,7 +45,7 @@\n //go:noescape\n func SyscallN(trap uintptr, args ...uintptr) (r1, r2 uintptr, err Errno)\n func loadlibrary(filename *uint16) (handle uintptr, err Errno)\n-func loadsystemlibrary(filename *uint16) (handle uintptr, err Errno)\n+func loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle uintptr, err Errno)\n func getprocaddress(handle uintptr, procname *uint8) (proc uintptr, err Errno)\n \n // A DLL implements access to a single DLL.\n@@ -54,6 +54,9 @@\n \tHandle Handle\n }\n \n+//go:linkname getSystemDirectory\n+func getSystemDirectory() string // Implemented in runtime package.\n+\n // LoadDLL loads the named DLL file into memory.\n //\n // If name is not an absolute path and is not a known system DLL used by\n@@ -70,7 +73,11 @@\n \tvar h uintptr\n \tvar e Errno\n \tif sysdll.IsSystemDLL[name] {\n-\t\th, e = loadsystemlibrary(namep)\n+\t\tabsoluteFilepathp, err := UTF16PtrFromString(getSystemDirectory() + name)\n+\t\tif err != nil {\n+\t\t\treturn nil, err\n+\t\t}\n+\t\th, e = loadsystemlibrary(namep, absoluteFilepathp)\n \t} else {\n \t\th, e = loadlibrary(namep)\n \t}\nIndex: src/os/removeall_at.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/os/removeall_at.go b/src/os/removeall_at.go\n--- a/src/os/removeall_at.go\t(revision f6bddda4e8ff58a957462a1a09562924d5f3d05c)\n+++ b/src/os/removeall_at.go\t(revision bed309eff415bcb3c77dd4bc3277b682b89a388d)\n@@ -2,7 +2,7 @@\n // Use of this source code is governed by a BSD-style\n // license that can be found in the LICENSE file.\n \n-//go:build unix || wasip1 || windows\n+//go:build unix || wasip1\n \n package os\n \n@@ -175,3 +175,25 @@\n \t}\n \treturn newDirFile(fd, name)\n }\n+\n+func rootRemoveAll(r *Root, name string) error {\n+\t// Consistency with os.RemoveAll: Strip trailing /s from the name,\n+\t// so RemoveAll(\"not_a_directory/\") succeeds.\n+\tfor len(name) > 0 && IsPathSeparator(name[len(name)-1]) {\n+\t\tname = name[:len(name)-1]\n+\t}\n+\tif endsWithDot(name) {\n+\t\t// Consistency with os.RemoveAll: Return EINVAL when trying to remove .\n+\t\treturn &PathError{Op: \"RemoveAll\", Path: name, Err: syscall.EINVAL}\n+\t}\n+\t_, err := doInRoot(r, name, nil, func(parent sysfdType, name string) (struct{}, error) {\n+\t\treturn struct{}{}, removeAllFrom(parent, name)\n+\t})\n+\tif IsNotExist(err) {\n+\t\treturn nil\n+\t}\n+\tif err != nil {\n+\t\treturn &PathError{Op: \"RemoveAll\", Path: name, Err: underlyingError(err)}\n+\t}\n+\treturn err\n+}\nIndex: src/os/removeall_noat.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/os/removeall_noat.go b/src/os/removeall_noat.go\n--- a/src/os/removeall_noat.go\t(revision f6bddda4e8ff58a957462a1a09562924d5f3d05c)\n+++ b/src/os/removeall_noat.go\t(revision bed309eff415bcb3c77dd4bc3277b682b89a388d)\n@@ -2,7 +2,7 @@\n // Use of this source code is governed by a BSD-style\n // license that can be found in the LICENSE file.\n \n-//go:build (js && wasm) || plan9\n+//go:build (js && wasm) || plan9 || windows\n \n package os\n \n@@ -140,3 +140,22 @@\n \t}\n \treturn err\n }\n+\n+func rootRemoveAll(r *Root, name string) error {\n+\tif endsWithDot(name) {\n+\t\t// Consistency with os.RemoveAll: Return EINVAL when trying to remove .\n+\t\treturn &PathError{Op: \"RemoveAll\", Path: name, Err: syscall.EINVAL}\n+\t}\n+\tif err := checkPathEscapesLstat(r, name); err != nil {\n+\t\tif err == syscall.ENOTDIR {\n+\t\t\t// Some intermediate path component is not a directory.\n+\t\t\t// RemoveAll treats this as success (since the target doesn't exist).\n+\t\t\treturn nil\n+\t\t}\n+\t\treturn &PathError{Op: \"RemoveAll\", Path: name, Err: err}\n+\t}\n+\tif err := RemoveAll(joinPath(r.root.name, name)); err != nil {\n+\t\treturn &PathError{Op: \"RemoveAll\", Path: name, Err: underlyingError(err)}\n+\t}\n+\treturn nil\n+}\nIndex: src/os/root_noopenat.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/os/root_noopenat.go b/src/os/root_noopenat.go\n--- a/src/os/root_noopenat.go\t(revision f6bddda4e8ff58a957462a1a09562924d5f3d05c)\n+++ b/src/os/root_noopenat.go\t(revision bed309eff415bcb3c77dd4bc3277b682b89a388d)\n@@ -11,7 +11,6 @@\n \t\"internal/filepathlite\"\n \t\"internal/stringslite\"\n \t\"sync/atomic\"\n-\t\"syscall\"\n \t\"time\"\n )\n \n@@ -185,25 +184,6 @@\n \t}\n \treturn nil\n }\n-\n-func rootRemoveAll(r *Root, name string) error {\n-\tif endsWithDot(name) {\n-\t\t// Consistency with os.RemoveAll: Return EINVAL when trying to remove .\n-\t\treturn &PathError{Op: \"RemoveAll\", Path: name, Err: syscall.EINVAL}\n-\t}\n-\tif err := checkPathEscapesLstat(r, name); err != nil {\n-\t\tif err == syscall.ENOTDIR {\n-\t\t\t// Some intermediate path component is not a directory.\n-\t\t\t// RemoveAll treats this as success (since the target doesn't exist).\n-\t\t\treturn nil\n-\t\t}\n-\t\treturn &PathError{Op: \"RemoveAll\", Path: name, Err: err}\n-\t}\n-\tif err := RemoveAll(joinPath(r.root.name, name)); err != nil {\n-\t\treturn &PathError{Op: \"RemoveAll\", Path: name, Err: underlyingError(err)}\n-\t}\n-\treturn nil\n-}\n \n func rootReadlink(r *Root, name string) (string, error) {\n \tif err := checkPathEscapesLstat(r, name); err != nil {\nIndex: src/os/root_openat.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/os/root_openat.go b/src/os/root_openat.go\n--- a/src/os/root_openat.go\t(revision f6bddda4e8ff58a957462a1a09562924d5f3d05c)\n+++ b/src/os/root_openat.go\t(revision bed309eff415bcb3c77dd4bc3277b682b89a388d)\n@@ -196,28 +196,6 @@\n \treturn nil\n }\n \n-func rootRemoveAll(r *Root, name string) error {\n-\t// Consistency with os.RemoveAll: Strip trailing /s from the name,\n-\t// so RemoveAll(\"not_a_directory/\") succeeds.\n-\tfor len(name) > 0 && IsPathSeparator(name[len(name)-1]) {\n-\t\tname = name[:len(name)-1]\n-\t}\n-\tif endsWithDot(name) {\n-\t\t// Consistency with os.RemoveAll: Return EINVAL when trying to remove .\n-\t\treturn &PathError{Op: \"RemoveAll\", Path: name, Err: syscall.EINVAL}\n-\t}\n-\t_, err := doInRoot(r, name, nil, func(parent sysfdType, name string) (struct{}, error) {\n-\t\treturn struct{}{}, removeAllFrom(parent, name)\n-\t})\n-\tif IsNotExist(err) {\n-\t\treturn nil\n-\t}\n-\tif err != nil {\n-\t\treturn &PathError{Op: \"RemoveAll\", Path: name, Err: underlyingError(err)}\n-\t}\n-\treturn err\n-}\n-\n func rootRename(r *Root, oldname, newname string) error {\n \t_, err := doInRoot(r, oldname, nil, func(oldparent sysfdType, oldname string) (struct{}, error) {\n \t\t_, err := doInRoot(r, newname, nil, func(newparent sysfdType, newname string) (struct{}, error) {\nIndex: src/os/root_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/os/root_windows.go b/src/os/root_windows.go\n--- a/src/os/root_windows.go\t(revision f6bddda4e8ff58a957462a1a09562924d5f3d05c)\n+++ b/src/os/root_windows.go\t(revision bed309eff415bcb3c77dd4bc3277b682b89a388d)\n@@ -402,3 +402,14 @@\n \t}\n \treturn fi.Mode(), nil\n }\n+\n+func checkPathEscapes(r *Root, name string) error {\n+\tif !filepathlite.IsLocal(name) {\n+\t\treturn errPathEscapes\n+\t}\n+\treturn nil\n+}\n+\n+func checkPathEscapesLstat(r *Root, name string) error {\n+\treturn checkPathEscapes(r, name)\n+}\nIndex: src/os/exec_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/os/exec_windows.go b/src/os/exec_windows.go\n--- a/src/os/exec_windows.go\t(revision bed309eff415bcb3c77dd4bc3277b682b89a388d)\n+++ b/src/os/exec_windows.go\t(revision 34b899c2fb39b092db4fa67c4417e41dc046be4b)\n@@ -10,6 +10,7 @@\n \t\"runtime\"\n \t\"syscall\"\n \t\"time\"\n+\t_ \"unsafe\"\n )\n \n // Note that Process.handle is never nil because Windows always requires\n@@ -49,9 +50,23 @@\n \t// than statusDone.\n \tp.doRelease(statusReleased)\n \n+\tvar maj, min, build uint32\n+\trtlGetNtVersionNumbers(&maj, &min, &build)\n+\tif maj < 10 {\n+\t\t// NOTE(brainman): It seems that sometimes process is not dead\n+\t\t// when WaitForSingleObject returns. But we do not know any\n+\t\t// other way to wait for it. Sleeping for a while seems to do\n+\t\t// the trick sometimes.\n+\t\t// See https://golang.org/issue/25965 for details.\n+\t\ttime.Sleep(5 * time.Millisecond)\n+\t}\n+\n \treturn &ProcessState{p.Pid, syscall.WaitStatus{ExitCode: ec}, &u}, nil\n }\n \n+//go:linkname rtlGetNtVersionNumbers syscall.rtlGetNtVersionNumbers\n+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32)\n+\n func (p *Process) signal(sig Signal) error {\n \thandle, status := p.handleTransientAcquire()\n \tswitch status {\n"
  },
  {
    "path": "core/Clash.Meta/.github/patch/go1.26.patch",
    "content": "Subject: [PATCH] Revert \"os: remove 5ms sleep on Windows in (*Process).Wait\"\nFix os.RemoveAll not working on Windows7\nRevert \"runtime: always use LoadLibraryEx to load system libraries\"\nRevert \"syscall: remove Windows 7 console handle workaround\"\nRevert \"net: remove sysSocket fallback for Windows 7\"\nRevert \"crypto/rand,runtime: switch RtlGenRandom for ProcessPrng\"\n---\nIndex: src/crypto/internal/sysrand/rand_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/crypto/internal/sysrand/rand_windows.go b/src/crypto/internal/sysrand/rand_windows.go\n--- a/src/crypto/internal/sysrand/rand_windows.go\t(revision e87b10ea2a2c6c65b80c4374af42b9c02ac9fb20)\n+++ b/src/crypto/internal/sysrand/rand_windows.go\t(revision 4b29590aa510e05686ea53de16e1e571d22203d8)\n@@ -7,5 +7,26 @@\n import \"internal/syscall/windows\"\n \n func read(b []byte) error {\n-\treturn windows.ProcessPrng(b)\n+\t// RtlGenRandom only returns 1<<32-1 bytes at a time. We only read at\n+\t// most 1<<31-1 bytes at a time so that  this works the same on 32-bit\n+\t// and 64-bit systems.\n+\treturn batched(windows.RtlGenRandom, 1<<31-1)(b)\n+}\n+\n+// batched returns a function that calls f to populate a []byte by chunking it\n+// into subslices of, at most, readMax bytes.\n+func batched(f func([]byte) error, readMax int) func([]byte) error {\n+\treturn func(out []byte) error {\n+\t\tfor len(out) > 0 {\n+\t\t\tread := len(out)\n+\t\t\tif read > readMax {\n+\t\t\t\tread = readMax\n+\t\t\t}\n+\t\t\tif err := f(out[:read]); err != nil {\n+\t\t\t\treturn err\n+\t\t\t}\n+\t\t\tout = out[read:]\n+\t\t}\n+\t\treturn nil\n+\t}\n }\nIndex: src/crypto/rand/rand.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/crypto/rand/rand.go b/src/crypto/rand/rand.go\n--- a/src/crypto/rand/rand.go\t(revision e87b10ea2a2c6c65b80c4374af42b9c02ac9fb20)\n+++ b/src/crypto/rand/rand.go\t(revision 4b29590aa510e05686ea53de16e1e571d22203d8)\n@@ -25,7 +25,7 @@\n //   - On legacy Linux (< 3.17), Reader opens /dev/urandom on first use.\n //   - On macOS, iOS, and OpenBSD Reader, uses arc4random_buf(3).\n //   - On NetBSD, Reader uses the kern.arandom sysctl.\n-//   - On Windows, Reader uses the ProcessPrng API.\n+//   - On Windows systems, Reader uses the RtlGenRandom API.\n //   - On js/wasm, Reader uses the Web Crypto API.\n //   - On wasip1/wasm, Reader uses random_get.\n //\nIndex: src/internal/syscall/windows/syscall_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/internal/syscall/windows/syscall_windows.go b/src/internal/syscall/windows/syscall_windows.go\n--- a/src/internal/syscall/windows/syscall_windows.go\t(revision e87b10ea2a2c6c65b80c4374af42b9c02ac9fb20)\n+++ b/src/internal/syscall/windows/syscall_windows.go\t(revision 4b29590aa510e05686ea53de16e1e571d22203d8)\n@@ -421,7 +421,7 @@\n //sys\tDestroyEnvironmentBlock(block *uint16) (err error) = userenv.DestroyEnvironmentBlock\n //sys\tCreateEvent(eventAttrs *SecurityAttributes, manualReset uint32, initialState uint32, name *uint16) (handle syscall.Handle, err error) = kernel32.CreateEventW\n \n-//sys\tProcessPrng(buf []byte) (err error) = bcryptprimitives.ProcessPrng\n+//sys\tRtlGenRandom(buf []byte) (err error) = advapi32.SystemFunction036\n \n type FILE_ID_BOTH_DIR_INFO struct {\n \tNextEntryOffset uint32\nIndex: src/internal/syscall/windows/zsyscall_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/internal/syscall/windows/zsyscall_windows.go b/src/internal/syscall/windows/zsyscall_windows.go\n--- a/src/internal/syscall/windows/zsyscall_windows.go\t(revision e87b10ea2a2c6c65b80c4374af42b9c02ac9fb20)\n+++ b/src/internal/syscall/windows/zsyscall_windows.go\t(revision 4b29590aa510e05686ea53de16e1e571d22203d8)\n@@ -38,7 +38,6 @@\n \n var (\n \tmodadvapi32         = syscall.NewLazyDLL(sysdll.Add(\"advapi32.dll\"))\n-\tmodbcryptprimitives = syscall.NewLazyDLL(sysdll.Add(\"bcryptprimitives.dll\"))\n \tmodiphlpapi         = syscall.NewLazyDLL(sysdll.Add(\"iphlpapi.dll\"))\n \tmodkernel32         = syscall.NewLazyDLL(sysdll.Add(\"kernel32.dll\"))\n \tmodnetapi32         = syscall.NewLazyDLL(sysdll.Add(\"netapi32.dll\"))\n@@ -65,7 +64,7 @@\n \tprocSetEntriesInAclW                  = modadvapi32.NewProc(\"SetEntriesInAclW\")\n \tprocSetNamedSecurityInfoW             = modadvapi32.NewProc(\"SetNamedSecurityInfoW\")\n \tprocSetTokenInformation               = modadvapi32.NewProc(\"SetTokenInformation\")\n-\tprocProcessPrng                       = modbcryptprimitives.NewProc(\"ProcessPrng\")\n+\tprocSystemFunction036                 = modadvapi32.NewProc(\"SystemFunction036\")\n \tprocGetAdaptersAddresses              = modiphlpapi.NewProc(\"GetAdaptersAddresses\")\n \tprocCreateEventW                      = modkernel32.NewProc(\"CreateEventW\")\n \tprocCreateIoCompletionPort            = modkernel32.NewProc(\"CreateIoCompletionPort\")\n@@ -271,12 +270,12 @@\n \treturn\n }\n \n-func ProcessPrng(buf []byte) (err error) {\n+func RtlGenRandom(buf []byte) (err error) {\n \tvar _p0 *byte\n \tif len(buf) > 0 {\n \t\t_p0 = &buf[0]\n \t}\n-\tr1, _, e1 := syscall.SyscallN(procProcessPrng.Addr(), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)))\n+\tr1, _, e1 := syscall.SyscallN(procSystemFunction036.Addr(), uintptr(unsafe.Pointer(_p0)), uintptr(len(buf)))\n \tif r1 == 0 {\n \t\terr = errnoErr(e1)\n \t}\nIndex: src/runtime/os_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/runtime/os_windows.go b/src/runtime/os_windows.go\n--- a/src/runtime/os_windows.go\t(revision e87b10ea2a2c6c65b80c4374af42b9c02ac9fb20)\n+++ b/src/runtime/os_windows.go\t(revision ce2e1a3d2c3c0d7277b4102841db1697147d2923)\n@@ -40,7 +40,8 @@\n //go:cgo_import_dynamic runtime._GetSystemInfo GetSystemInfo%1 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._GetThreadContext GetThreadContext%2 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._SetThreadContext SetThreadContext%2 \"kernel32.dll\"\n-//go:cgo_import_dynamic runtime._LoadLibraryExW LoadLibraryExW%3 \"kernel32.dll\"\n+//go:cgo_import_dynamic runtime._LoadLibraryW LoadLibraryW%1 \"kernel32.dll\"\n+//go:cgo_import_dynamic runtime._LoadLibraryA LoadLibraryA%1 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._PostQueuedCompletionStatus PostQueuedCompletionStatus%4 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._QueryPerformanceCounter QueryPerformanceCounter%1 \"kernel32.dll\"\n //go:cgo_import_dynamic runtime._QueryPerformanceFrequency QueryPerformanceFrequency%1 \"kernel32.dll\"\n@@ -74,7 +75,6 @@\n \t// Following syscalls are available on every Windows PC.\n \t// All these variables are set by the Windows executable\n \t// loader before the Go program starts.\n-\t_AddVectoredContinueHandler,\n \t_AddVectoredExceptionHandler,\n \t_CloseHandle,\n \t_CreateEventA,\n@@ -97,7 +97,8 @@\n \t_GetSystemInfo,\n \t_GetThreadContext,\n \t_SetThreadContext,\n-\t_LoadLibraryExW,\n+\t_LoadLibraryW,\n+\t_LoadLibraryA,\n \t_PostQueuedCompletionStatus,\n \t_QueryPerformanceCounter,\n \t_QueryPerformanceFrequency,\n@@ -126,8 +127,23 @@\n \t_WriteFile,\n \t_ stdFunction\n \n-\t// Use ProcessPrng to generate cryptographically random data.\n-\t_ProcessPrng stdFunction\n+\t// Following syscalls are only available on some Windows PCs.\n+\t// We will load syscalls, if available, before using them.\n+\t_AddDllDirectory,\n+\t_AddVectoredContinueHandler,\n+\t_LoadLibraryExA,\n+\t_LoadLibraryExW,\n+\t_ stdFunction\n+\n+\t// Use RtlGenRandom to generate cryptographically random data.\n+\t// This approach has been recommended by Microsoft (see issue\n+\t// 15589 for details).\n+\t// The RtlGenRandom is not listed in advapi32.dll, instead\n+\t// RtlGenRandom function can be found by searching for SystemFunction036.\n+\t// Also some versions of Mingw cannot link to SystemFunction036\n+\t// when building executable as Cgo. So load SystemFunction036\n+\t// manually during runtime startup.\n+\t_RtlGenRandom stdFunction\n \n \t// Load ntdll.dll manually during startup, otherwise Mingw\n \t// links wrong printf function to cgo executable (see issue\n@@ -144,13 +160,6 @@\n \t_ stdFunction\n )\n \n-var (\n-\tbcryptprimitivesdll = [...]uint16{'b', 'c', 'r', 'y', 'p', 't', 'p', 'r', 'i', 'm', 'i', 't', 'i', 'v', 'e', 's', '.', 'd', 'l', 'l', 0}\n-\tntdlldll            = [...]uint16{'n', 't', 'd', 'l', 'l', '.', 'd', 'l', 'l', 0}\n-\tpowrprofdll         = [...]uint16{'p', 'o', 'w', 'r', 'p', 'r', 'o', 'f', '.', 'd', 'l', 'l', 0}\n-\twinmmdll            = [...]uint16{'w', 'i', 'n', 'm', 'm', '.', 'd', 'l', 'l', 0}\n-)\n-\n // Function to be called by windows CreateThread\n // to start new os thread.\n func tstart_stdcall(newm *m)\n@@ -242,9 +251,40 @@\n \treturn unsafe.String(&sysDirectory[0], sysDirectoryLen)\n }\n \n-func windowsLoadSystemLib(name []uint16) uintptr {\n-\tconst _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800\n-\treturn stdcall(_LoadLibraryExW, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32)\n+//go:linkname syscall_getSystemDirectory syscall.getSystemDirectory\n+func syscall_getSystemDirectory() string {\n+\treturn unsafe.String(&sysDirectory[0], sysDirectoryLen)\n+}\n+\n+func windowsLoadSystemLib(name []byte) uintptr {\n+\tif useLoadLibraryEx {\n+\t\treturn stdcall(_LoadLibraryExA, uintptr(unsafe.Pointer(&name[0])), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32)\n+\t} else {\n+\t\tabsName := append(sysDirectory[:sysDirectoryLen], name...)\n+\t\treturn stdcall(_LoadLibraryA, uintptr(unsafe.Pointer(&absName[0])))\n+\t}\n+}\n+\n+const _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800\n+\n+// When available, this function will use LoadLibraryEx with the filename\n+// parameter and the important SEARCH_SYSTEM32 argument. But on systems that\n+// do not have that option, absoluteFilepath should contain a fallback\n+// to the full path inside of system32 for use with vanilla LoadLibrary.\n+//\n+//go:linkname syscall_loadsystemlibrary syscall.loadsystemlibrary\n+func syscall_loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle, err uintptr) {\n+\tif useLoadLibraryEx {\n+\t\thandle, _, err = syscall_syscalln(uintptr(unsafe.Pointer(_LoadLibraryExW)), 3, uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32)\n+\t} else {\n+\t\thandle, _, err = syscall_syscalln(uintptr(unsafe.Pointer(_LoadLibraryW)), 1, uintptr(unsafe.Pointer(absoluteFilepath)))\n+\t}\n+\tKeepAlive(filename)\n+\tKeepAlive(absoluteFilepath)\n+\tif handle != 0 {\n+\t\terr = 0\n+\t}\n+\treturn\n }\n \n //go:linkname windows_QueryPerformanceCounter internal/syscall/windows.QueryPerformanceCounter\n@@ -262,13 +302,28 @@\n }\n \n func loadOptionalSyscalls() {\n-\tbcryptPrimitives := windowsLoadSystemLib(bcryptprimitivesdll[:])\n-\tif bcryptPrimitives == 0 {\n-\t\tthrow(\"bcryptprimitives.dll not found\")\n+\tvar kernel32dll = []byte(\"kernel32.dll\\000\")\n+\tk32 := stdcall(_LoadLibraryA, uintptr(unsafe.Pointer(&kernel32dll[0])))\n+\tif k32 == 0 {\n+\t\tthrow(\"kernel32.dll not found\")\n \t}\n-\t_ProcessPrng = windowsFindfunc(bcryptPrimitives, []byte(\"ProcessPrng\\000\"))\n+\t_AddDllDirectory = windowsFindfunc(k32, []byte(\"AddDllDirectory\\000\"))\n+\t_AddVectoredContinueHandler = windowsFindfunc(k32, []byte(\"AddVectoredContinueHandler\\000\"))\n+\t_LoadLibraryExA = windowsFindfunc(k32, []byte(\"LoadLibraryExA\\000\"))\n+\t_LoadLibraryExW = windowsFindfunc(k32, []byte(\"LoadLibraryExW\\000\"))\n+\tuseLoadLibraryEx = (_LoadLibraryExW != nil && _LoadLibraryExA != nil && _AddDllDirectory != nil)\n+\n+\tinitSysDirectory()\n \n-\tn32 := windowsLoadSystemLib(ntdlldll[:])\n+\tvar advapi32dll = []byte(\"advapi32.dll\\000\")\n+\ta32 := windowsLoadSystemLib(advapi32dll)\n+\tif a32 == 0 {\n+\t\tthrow(\"advapi32.dll not found\")\n+\t}\n+\t_RtlGenRandom = windowsFindfunc(a32, []byte(\"SystemFunction036\\000\"))\n+\n+\tvar ntdll = []byte(\"ntdll.dll\\000\")\n+\tn32 := windowsLoadSystemLib(ntdll)\n \tif n32 == 0 {\n \t\tthrow(\"ntdll.dll not found\")\n \t}\n@@ -297,7 +352,7 @@\n \t\tcontext  uintptr\n \t}\n \n-\tpowrprof := windowsLoadSystemLib(powrprofdll[:])\n+\tpowrprof := windowsLoadSystemLib([]byte(\"powrprof.dll\\000\"))\n \tif powrprof == 0 {\n \t\treturn // Running on Windows 7, where we don't need it anyway.\n \t}\n@@ -351,6 +406,22 @@\n // in sys_windows_386.s and sys_windows_amd64.s:\n func getlasterror() uint32\n \n+// When loading DLLs, we prefer to use LoadLibraryEx with\n+// LOAD_LIBRARY_SEARCH_* flags, if available. LoadLibraryEx is not\n+// available on old Windows, though, and the LOAD_LIBRARY_SEARCH_*\n+// flags are not available on some versions of Windows without a\n+// security patch.\n+//\n+// https://msdn.microsoft.com/en-us/library/ms684179(v=vs.85).aspx says:\n+// \"Windows 7, Windows Server 2008 R2, Windows Vista, and Windows\n+// Server 2008: The LOAD_LIBRARY_SEARCH_* flags are available on\n+// systems that have KB2533623 installed. To determine whether the\n+// flags are available, use GetProcAddress to get the address of the\n+// AddDllDirectory, RemoveDllDirectory, or SetDefaultDllDirectories\n+// function. If GetProcAddress succeeds, the LOAD_LIBRARY_SEARCH_*\n+// flags can be used with LoadLibraryEx.\"\n+var useLoadLibraryEx bool\n+\n var timeBeginPeriodRetValue uint32\n \n // osRelaxMinNS indicates that sysmon shouldn't osRelax if the next\n@@ -417,7 +488,8 @@\n \t\t// Only load winmm.dll if we need it.\n \t\t// This avoids a dependency on winmm.dll for Go programs\n \t\t// that run on new Windows versions.\n-\t\tm32 := windowsLoadSystemLib(winmmdll[:])\n+\t\tvar winmmdll = []byte(\"winmm.dll\\000\")\n+\t\tm32 := windowsLoadSystemLib(winmmdll)\n \t\tif m32 == 0 {\n \t\t\tprint(\"runtime: LoadLibraryExW failed; errno=\", getlasterror(), \"\\n\")\n \t\t\tthrow(\"winmm.dll not found\")\n@@ -458,6 +530,28 @@\n \tcanUseLongPaths = true\n }\n \n+var osVersionInfo struct {\n+\tmajorVersion uint32\n+\tminorVersion uint32\n+\tbuildNumber  uint32\n+}\n+\n+func initOsVersionInfo() {\n+\tinfo := windows.OSVERSIONINFOW{}\n+\tinfo.OSVersionInfoSize = uint32(unsafe.Sizeof(info))\n+\tstdcall(_RtlGetVersion, uintptr(unsafe.Pointer(&info)))\n+\tosVersionInfo.majorVersion = info.MajorVersion\n+\tosVersionInfo.minorVersion = info.MinorVersion\n+\tosVersionInfo.buildNumber = info.BuildNumber\n+}\n+\n+//go:linkname rtlGetNtVersionNumbers syscall.rtlGetNtVersionNumbers\n+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32) {\n+\t*majorVersion = osVersionInfo.majorVersion\n+\t*minorVersion = osVersionInfo.minorVersion\n+\t*buildNumber = osVersionInfo.buildNumber\n+}\n+\n func osinit() {\n \tasmstdcallAddr = unsafe.Pointer(windows.AsmStdCallAddr())\n \n@@ -470,8 +564,8 @@\n \tinitHighResTimer()\n \ttimeBeginPeriodRetValue = osRelax(false)\n \n-\tinitSysDirectory()\n \tinitLongPathSupport()\n+\tinitOsVersionInfo()\n \n \tnumCPUStartup = getCPUCount()\n \n@@ -487,7 +581,7 @@\n //go:nosplit\n func readRandom(r []byte) int {\n \tn := 0\n-\tif stdcall(_ProcessPrng, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 {\n+\tif stdcall(_RtlGenRandom, uintptr(unsafe.Pointer(&r[0])), uintptr(len(r)))&0xff != 0 {\n \t\tn = len(r)\n \t}\n \treturn n\nIndex: src/net/hook_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/hook_windows.go b/src/net/hook_windows.go\n--- a/src/net/hook_windows.go\t(revision 4b29590aa510e05686ea53de16e1e571d22203d8)\n+++ b/src/net/hook_windows.go\t(revision 2263b05b2fa6ce228fde1899587baf109f1e2e0a)\n@@ -13,6 +13,7 @@\n \thostsFilePath = windows.GetSystemDirectory() + \"/Drivers/etc/hosts\"\n \n \t// Placeholders for socket system calls.\n+\tsocketFunc    func(int, int, int) (syscall.Handle, error)                                                 = syscall.Socket\n \twsaSocketFunc func(int32, int32, int32, *syscall.WSAProtocolInfo, uint32, uint32) (syscall.Handle, error) = windows.WSASocket\n \tconnectFunc   func(syscall.Handle, syscall.Sockaddr) error                                                = syscall.Connect\n \tlistenFunc    func(syscall.Handle, int) error                                                             = syscall.Listen\nIndex: src/net/internal/socktest/main_test.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/internal/socktest/main_test.go b/src/net/internal/socktest/main_test.go\n--- a/src/net/internal/socktest/main_test.go\t(revision 4b29590aa510e05686ea53de16e1e571d22203d8)\n+++ b/src/net/internal/socktest/main_test.go\t(revision 2263b05b2fa6ce228fde1899587baf109f1e2e0a)\n@@ -2,7 +2,7 @@\n // Use of this source code is governed by a BSD-style\n // license that can be found in the LICENSE file.\n \n-//go:build !js && !plan9 && !wasip1 && !windows\n+//go:build !js && !plan9 && !wasip1\n \n package socktest_test\n \nIndex: src/net/internal/socktest/main_windows_test.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/internal/socktest/main_windows_test.go b/src/net/internal/socktest/main_windows_test.go\nnew file mode 100644\n--- /dev/null\t(revision 2263b05b2fa6ce228fde1899587baf109f1e2e0a)\n+++ b/src/net/internal/socktest/main_windows_test.go\t(revision 2263b05b2fa6ce228fde1899587baf109f1e2e0a)\n@@ -0,0 +1,22 @@\n+// Copyright 2015 The Go Authors. All rights reserved.\n+// Use of this source code is governed by a BSD-style\n+// license that can be found in the LICENSE file.\n+\n+package socktest_test\n+\n+import \"syscall\"\n+\n+var (\n+\tsocketFunc func(int, int, int) (syscall.Handle, error)\n+\tcloseFunc  func(syscall.Handle) error\n+)\n+\n+func installTestHooks() {\n+\tsocketFunc = sw.Socket\n+\tcloseFunc = sw.Closesocket\n+}\n+\n+func uninstallTestHooks() {\n+\tsocketFunc = syscall.Socket\n+\tcloseFunc = syscall.Closesocket\n+}\nIndex: src/net/internal/socktest/sys_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/internal/socktest/sys_windows.go b/src/net/internal/socktest/sys_windows.go\n--- a/src/net/internal/socktest/sys_windows.go\t(revision 4b29590aa510e05686ea53de16e1e571d22203d8)\n+++ b/src/net/internal/socktest/sys_windows.go\t(revision 2263b05b2fa6ce228fde1899587baf109f1e2e0a)\n@@ -9,6 +9,38 @@\n \t\"syscall\"\n )\n \n+// Socket wraps [syscall.Socket].\n+func (sw *Switch) Socket(family, sotype, proto int) (s syscall.Handle, err error) {\n+\tsw.once.Do(sw.init)\n+\n+\tso := &Status{Cookie: cookie(family, sotype, proto)}\n+\tsw.fmu.RLock()\n+\tf, _ := sw.fltab[FilterSocket]\n+\tsw.fmu.RUnlock()\n+\n+\taf, err := f.apply(so)\n+\tif err != nil {\n+\t\treturn syscall.InvalidHandle, err\n+\t}\n+\ts, so.Err = syscall.Socket(family, sotype, proto)\n+\tif err = af.apply(so); err != nil {\n+\t\tif so.Err == nil {\n+\t\t\tsyscall.Closesocket(s)\n+\t\t}\n+\t\treturn syscall.InvalidHandle, err\n+\t}\n+\n+\tsw.smu.Lock()\n+\tdefer sw.smu.Unlock()\n+\tif so.Err != nil {\n+\t\tsw.stats.getLocked(so.Cookie).OpenFailed++\n+\t\treturn syscall.InvalidHandle, so.Err\n+\t}\n+\tnso := sw.addLocked(s, family, sotype, proto)\n+\tsw.stats.getLocked(nso.Cookie).Opened++\n+\treturn s, nil\n+}\n+\n // WSASocket wraps [syscall.WSASocket].\n func (sw *Switch) WSASocket(family, sotype, proto int32, protinfo *syscall.WSAProtocolInfo, group uint32, flags uint32) (s syscall.Handle, err error) {\n \tsw.once.Do(sw.init)\nIndex: src/net/main_windows_test.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/main_windows_test.go b/src/net/main_windows_test.go\n--- a/src/net/main_windows_test.go\t(revision 4b29590aa510e05686ea53de16e1e571d22203d8)\n+++ b/src/net/main_windows_test.go\t(revision 2263b05b2fa6ce228fde1899587baf109f1e2e0a)\n@@ -12,6 +12,7 @@\n \n var (\n \t// Placeholders for saving original socket system calls.\n+\torigSocket      = socketFunc\n \torigWSASocket   = wsaSocketFunc\n \torigClosesocket = poll.CloseFunc\n \torigConnect     = connectFunc\n@@ -21,6 +22,7 @@\n )\n \n func installTestHooks() {\n+\tsocketFunc = sw.Socket\n \twsaSocketFunc = sw.WSASocket\n \tpoll.CloseFunc = sw.Closesocket\n \tconnectFunc = sw.Connect\n@@ -30,6 +32,7 @@\n }\n \n func uninstallTestHooks() {\n+\tsocketFunc = origSocket\n \twsaSocketFunc = origWSASocket\n \tpoll.CloseFunc = origClosesocket\n \tconnectFunc = origConnect\nIndex: src/net/sock_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/net/sock_windows.go b/src/net/sock_windows.go\n--- a/src/net/sock_windows.go\t(revision 4b29590aa510e05686ea53de16e1e571d22203d8)\n+++ b/src/net/sock_windows.go\t(revision 2263b05b2fa6ce228fde1899587baf109f1e2e0a)\n@@ -20,6 +20,21 @@\n func sysSocket(family, sotype, proto int) (syscall.Handle, error) {\n \ts, err := wsaSocketFunc(int32(family), int32(sotype), int32(proto),\n \t\tnil, 0, windows.WSA_FLAG_OVERLAPPED|windows.WSA_FLAG_NO_HANDLE_INHERIT)\n+\tif err == nil {\n+\t\treturn s, nil\n+\t}\n+\t// WSA_FLAG_NO_HANDLE_INHERIT flag is not supported on some\n+\t// old versions of Windows, see\n+\t// https://msdn.microsoft.com/en-us/library/windows/desktop/ms742212(v=vs.85).aspx\n+\t// for details. Just use syscall.Socket, if windows.WSASocket failed.\n+\n+\t// See ../syscall/exec_unix.go for description of ForkLock.\n+\tsyscall.ForkLock.RLock()\n+\ts, err = socketFunc(family, sotype, proto)\n+\tif err == nil {\n+\t\tsyscall.CloseOnExec(s)\n+\t}\n+\tsyscall.ForkLock.RUnlock()\n \tif err != nil {\n \t\treturn syscall.InvalidHandle, os.NewSyscallError(\"socket\", err)\n \t}\nIndex: src/syscall/exec_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/syscall/exec_windows.go b/src/syscall/exec_windows.go\n--- a/src/syscall/exec_windows.go\t(revision 4b29590aa510e05686ea53de16e1e571d22203d8)\n+++ b/src/syscall/exec_windows.go\t(revision ae41f7abdd5d7b8b51db2c03bf819ac66b8e1eb1)\n@@ -15,7 +15,6 @@\n \t\"unsafe\"\n )\n \n-// ForkLock is not used on Windows.\n var ForkLock sync.RWMutex\n \n // EscapeArg rewrites command line argument s as prescribed\n@@ -304,6 +303,9 @@\n var zeroProcAttr ProcAttr\n var zeroSysProcAttr SysProcAttr\n \n+//go:linkname rtlGetNtVersionNumbers\n+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32)\n+\n func StartProcess(argv0 string, argv []string, attr *ProcAttr) (pid int, handle uintptr, err error) {\n \tif len(argv0) == 0 {\n \t\treturn 0, 0, EWINDOWS\n@@ -367,6 +369,17 @@\n \t\t}\n \t}\n \n+\tvar maj, min, build uint32\n+\trtlGetNtVersionNumbers(&maj, &min, &build)\n+\tisWin7 := maj < 6 || (maj == 6 && min <= 1)\n+\t// NT kernel handles are divisible by 4, with the bottom 3 bits left as\n+\t// a tag. The fully set tag correlates with the types of handles we're\n+\t// concerned about here.  Except, the kernel will interpret some\n+\t// special handle values, like -1, -2, and so forth, so kernelbase.dll\n+\t// checks to see that those bottom three bits are checked, but that top\n+\t// bit is not checked.\n+\tisLegacyWin7ConsoleHandle := func(handle Handle) bool { return isWin7 && handle&0x10000003 == 3 }\n+\n \tp, _ := GetCurrentProcess()\n \tparentProcess := p\n \tif sys.ParentProcess != 0 {\n@@ -375,7 +388,15 @@\n \tfd := make([]Handle, len(attr.Files))\n \tfor i := range attr.Files {\n \t\tif attr.Files[i] > 0 {\n-\t\t\terr := DuplicateHandle(p, Handle(attr.Files[i]), parentProcess, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)\n+\t\t\tdestinationProcessHandle := parentProcess\n+\n+\t\t\t// On Windows 7, console handles aren't real handles, and can only be duplicated\n+\t\t\t// into the current process, not a parent one, which amounts to the same thing.\n+\t\t\tif parentProcess != p && isLegacyWin7ConsoleHandle(Handle(attr.Files[i])) {\n+\t\t\t\tdestinationProcessHandle = p\n+\t\t\t}\n+\n+\t\t\terr := DuplicateHandle(p, Handle(attr.Files[i]), destinationProcessHandle, &fd[i], 0, true, DUPLICATE_SAME_ACCESS)\n \t\t\tif err != nil {\n \t\t\t\treturn 0, 0, err\n \t\t\t}\n@@ -406,6 +427,14 @@\n \n \tfd = append(fd, sys.AdditionalInheritedHandles...)\n \n+\t// On Windows 7, console handles aren't real handles, so don't pass them\n+\t// through to PROC_THREAD_ATTRIBUTE_HANDLE_LIST.\n+\tfor i := range fd {\n+\t\tif isLegacyWin7ConsoleHandle(fd[i]) {\n+\t\t\tfd[i] = 0\n+\t\t}\n+\t}\n+\n \t// The presence of a NULL handle in the list is enough to cause PROC_THREAD_ATTRIBUTE_HANDLE_LIST\n \t// to treat the entire list as empty, so remove NULL handles.\n \tj := 0\nIndex: src/syscall/dll_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/syscall/dll_windows.go b/src/syscall/dll_windows.go\n--- a/src/syscall/dll_windows.go\t(revision ae41f7abdd5d7b8b51db2c03bf819ac66b8e1eb1)\n+++ b/src/syscall/dll_windows.go\t(revision ce2e1a3d2c3c0d7277b4102841db1697147d2923)\n@@ -119,14 +119,7 @@\n }\n \n //go:linkname loadsystemlibrary\n-func loadsystemlibrary(filename *uint16) (uintptr, Errno) {\n-\tconst _LOAD_LIBRARY_SEARCH_SYSTEM32 = 0x00000800\n-\thandle, _, err := SyscallN(uintptr(__LoadLibraryExW), uintptr(unsafe.Pointer(filename)), 0, _LOAD_LIBRARY_SEARCH_SYSTEM32)\n-\tif handle != 0 {\n-\t\terr = 0\n-\t}\n-\treturn handle, err\n-}\n+func loadsystemlibrary(filename *uint16, absoluteFilepath *uint16) (handle uintptr, err Errno)\n \n //go:linkname getprocaddress\n func getprocaddress(handle uintptr, procname *uint8) (uintptr, Errno) {\n@@ -143,6 +136,9 @@\n \tHandle Handle\n }\n \n+//go:linkname getSystemDirectory\n+func getSystemDirectory() string // Implemented in runtime package.\n+\n // LoadDLL loads the named DLL file into memory.\n //\n // If name is not an absolute path and is not a known system DLL used by\n@@ -159,7 +155,11 @@\n \tvar h uintptr\n \tvar e Errno\n \tif sysdll.IsSystemDLL[name] {\n-\t\th, e = loadsystemlibrary(namep)\n+\t\tabsoluteFilepathp, err := UTF16PtrFromString(getSystemDirectory() + name)\n+\t\tif err != nil {\n+\t\t\treturn nil, err\n+\t\t}\n+\t\th, e = loadsystemlibrary(namep, absoluteFilepathp)\n \t} else {\n \t\th, e = loadlibrary(namep)\n \t}\nIndex: src/os/removeall_at.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/os/removeall_at.go b/src/os/removeall_at.go\n--- a/src/os/removeall_at.go\t(revision ce2e1a3d2c3c0d7277b4102841db1697147d2923)\n+++ b/src/os/removeall_at.go\t(revision 4ea1045cf3124221f055dbd2f3d2c9822934f661)\n@@ -2,7 +2,7 @@\n // Use of this source code is governed by a BSD-style\n // license that can be found in the LICENSE file.\n \n-//go:build unix || wasip1 || windows\n+//go:build unix || wasip1\n \n package os\n \n@@ -175,3 +175,25 @@\n \t}\n \treturn newDirFile(fd, name)\n }\n+\n+func rootRemoveAll(r *Root, name string) error {\n+\t// Consistency with os.RemoveAll: Strip trailing /s from the name,\n+\t// so RemoveAll(\"not_a_directory/\") succeeds.\n+\tfor len(name) > 0 && IsPathSeparator(name[len(name)-1]) {\n+\t\tname = name[:len(name)-1]\n+\t}\n+\tif endsWithDot(name) {\n+\t\t// Consistency with os.RemoveAll: Return EINVAL when trying to remove .\n+\t\treturn &PathError{Op: \"RemoveAll\", Path: name, Err: syscall.EINVAL}\n+\t}\n+\t_, err := doInRoot(r, name, nil, func(parent sysfdType, name string) (struct{}, error) {\n+\t\treturn struct{}{}, removeAllFrom(parent, name)\n+\t})\n+\tif IsNotExist(err) {\n+\t\treturn nil\n+\t}\n+\tif err != nil {\n+\t\treturn &PathError{Op: \"RemoveAll\", Path: name, Err: underlyingError(err)}\n+\t}\n+\treturn err\n+}\nIndex: src/os/removeall_noat.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/os/removeall_noat.go b/src/os/removeall_noat.go\n--- a/src/os/removeall_noat.go\t(revision ce2e1a3d2c3c0d7277b4102841db1697147d2923)\n+++ b/src/os/removeall_noat.go\t(revision 4ea1045cf3124221f055dbd2f3d2c9822934f661)\n@@ -2,7 +2,7 @@\n // Use of this source code is governed by a BSD-style\n // license that can be found in the LICENSE file.\n \n-//go:build (js && wasm) || plan9\n+//go:build (js && wasm) || plan9 || windows\n \n package os\n \n@@ -140,3 +140,22 @@\n \t}\n \treturn err\n }\n+\n+func rootRemoveAll(r *Root, name string) error {\n+\tif endsWithDot(name) {\n+\t\t// Consistency with os.RemoveAll: Return EINVAL when trying to remove .\n+\t\treturn &PathError{Op: \"RemoveAll\", Path: name, Err: syscall.EINVAL}\n+\t}\n+\tif err := checkPathEscapesLstat(r, name); err != nil {\n+\t\tif err == syscall.ENOTDIR {\n+\t\t\t// Some intermediate path component is not a directory.\n+\t\t\t// RemoveAll treats this as success (since the target doesn't exist).\n+\t\t\treturn nil\n+\t\t}\n+\t\treturn &PathError{Op: \"RemoveAll\", Path: name, Err: err}\n+\t}\n+\tif err := RemoveAll(joinPath(r.root.name, name)); err != nil {\n+\t\treturn &PathError{Op: \"RemoveAll\", Path: name, Err: underlyingError(err)}\n+\t}\n+\treturn nil\n+}\nIndex: src/os/root_noopenat.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/os/root_noopenat.go b/src/os/root_noopenat.go\n--- a/src/os/root_noopenat.go\t(revision ce2e1a3d2c3c0d7277b4102841db1697147d2923)\n+++ b/src/os/root_noopenat.go\t(revision 4ea1045cf3124221f055dbd2f3d2c9822934f661)\n@@ -11,7 +11,6 @@\n \t\"internal/filepathlite\"\n \t\"internal/stringslite\"\n \t\"sync/atomic\"\n-\t\"syscall\"\n \t\"time\"\n )\n \n@@ -185,25 +184,6 @@\n \t}\n \treturn nil\n }\n-\n-func rootRemoveAll(r *Root, name string) error {\n-\tif endsWithDot(name) {\n-\t\t// Consistency with os.RemoveAll: Return EINVAL when trying to remove .\n-\t\treturn &PathError{Op: \"RemoveAll\", Path: name, Err: syscall.EINVAL}\n-\t}\n-\tif err := checkPathEscapesLstat(r, name); err != nil {\n-\t\tif err == syscall.ENOTDIR {\n-\t\t\t// Some intermediate path component is not a directory.\n-\t\t\t// RemoveAll treats this as success (since the target doesn't exist).\n-\t\t\treturn nil\n-\t\t}\n-\t\treturn &PathError{Op: \"RemoveAll\", Path: name, Err: err}\n-\t}\n-\tif err := RemoveAll(joinPath(r.root.name, name)); err != nil {\n-\t\treturn &PathError{Op: \"RemoveAll\", Path: name, Err: underlyingError(err)}\n-\t}\n-\treturn nil\n-}\n \n func rootReadlink(r *Root, name string) (string, error) {\n \tif err := checkPathEscapesLstat(r, name); err != nil {\nIndex: src/os/root_openat.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/os/root_openat.go b/src/os/root_openat.go\n--- a/src/os/root_openat.go\t(revision ce2e1a3d2c3c0d7277b4102841db1697147d2923)\n+++ b/src/os/root_openat.go\t(revision 4ea1045cf3124221f055dbd2f3d2c9822934f661)\n@@ -196,28 +196,6 @@\n \treturn nil\n }\n \n-func rootRemoveAll(r *Root, name string) error {\n-\t// Consistency with os.RemoveAll: Strip trailing /s from the name,\n-\t// so RemoveAll(\"not_a_directory/\") succeeds.\n-\tfor len(name) > 0 && IsPathSeparator(name[len(name)-1]) {\n-\t\tname = name[:len(name)-1]\n-\t}\n-\tif endsWithDot(name) {\n-\t\t// Consistency with os.RemoveAll: Return EINVAL when trying to remove .\n-\t\treturn &PathError{Op: \"RemoveAll\", Path: name, Err: syscall.EINVAL}\n-\t}\n-\t_, err := doInRoot(r, name, nil, func(parent sysfdType, name string) (struct{}, error) {\n-\t\treturn struct{}{}, removeAllFrom(parent, name)\n-\t})\n-\tif IsNotExist(err) {\n-\t\treturn nil\n-\t}\n-\tif err != nil {\n-\t\treturn &PathError{Op: \"RemoveAll\", Path: name, Err: underlyingError(err)}\n-\t}\n-\treturn err\n-}\n-\n func rootRename(r *Root, oldname, newname string) error {\n \t_, err := doInRoot(r, oldname, nil, func(oldparent sysfdType, oldname string) (struct{}, error) {\n \t\t_, err := doInRoot(r, newname, nil, func(newparent sysfdType, newname string) (struct{}, error) {\nIndex: src/os/root_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/os/root_windows.go b/src/os/root_windows.go\n--- a/src/os/root_windows.go\t(revision ce2e1a3d2c3c0d7277b4102841db1697147d2923)\n+++ b/src/os/root_windows.go\t(revision 4ea1045cf3124221f055dbd2f3d2c9822934f661)\n@@ -402,3 +402,14 @@\n \t}\n \treturn fi.Mode(), nil\n }\n+\n+func checkPathEscapes(r *Root, name string) error {\n+\tif !filepathlite.IsLocal(name) {\n+\t\treturn errPathEscapes\n+\t}\n+\treturn nil\n+}\n+\n+func checkPathEscapesLstat(r *Root, name string) error {\n+\treturn checkPathEscapes(r, name)\n+}\nIndex: src/os/exec_windows.go\nIDEA additional info:\nSubsystem: com.intellij.openapi.diff.impl.patch.CharsetEP\n<+>UTF-8\n===================================================================\ndiff --git a/src/os/exec_windows.go b/src/os/exec_windows.go\n--- a/src/os/exec_windows.go\t(revision 4ea1045cf3124221f055dbd2f3d2c9822934f661)\n+++ b/src/os/exec_windows.go\t(revision 8149d992682ce76c6af804b507878e19fc966f7b)\n@@ -10,6 +10,7 @@\n \t\"runtime\"\n \t\"syscall\"\n \t\"time\"\n+\t_ \"unsafe\"\n )\n \n // Note that Process.handle is never nil because Windows always requires\n@@ -49,9 +50,23 @@\n \t// than statusDone.\n \tp.doRelease(statusReleased)\n \n+\tvar maj, min, build uint32\n+\trtlGetNtVersionNumbers(&maj, &min, &build)\n+\tif maj < 10 {\n+\t\t// NOTE(brainman): It seems that sometimes process is not dead\n+\t\t// when WaitForSingleObject returns. But we do not know any\n+\t\t// other way to wait for it. Sleeping for a while seems to do\n+\t\t// the trick sometimes.\n+\t\t// See https://golang.org/issue/25965 for details.\n+\t\ttime.Sleep(5 * time.Millisecond)\n+\t}\n+\n \treturn &ProcessState{p.Pid, syscall.WaitStatus{ExitCode: ec}, &u}, nil\n }\n \n+//go:linkname rtlGetNtVersionNumbers syscall.rtlGetNtVersionNumbers\n+func rtlGetNtVersionNumbers(majorVersion *uint32, minorVersion *uint32, buildNumber *uint32)\n+\n func (p *Process) signal(sig Signal) error {\n \thandle, status := p.handleTransientAcquire()\n \tswitch status {\n"
  },
  {
    "path": "core/Clash.Meta/.github/patch/issue77731.patch",
    "content": "From b8f897a9da7a82ad8584a22284ceac61262fcb7e Mon Sep 17 00:00:00 2001\nFrom: Jorropo <jorropo.pgm@gmail.com>\nDate: Sun, 22 Feb 2026 01:47:45 +0100\nSubject: [PATCH] runtime: fix value of ENOSYS on mips from 38 to 89\n\nFixes #77731\n\nChange-Id: Iaca444e2d5f9e19fd2de38414b357b41471a668c\n---\n\ndiff --git a/src/runtime/defs_linux_mips64x.go b/src/runtime/defs_linux_mips64x.go\nindex 7449d2c..4d0f103 100644\n--- a/src/runtime/defs_linux_mips64x.go\n+++ b/src/runtime/defs_linux_mips64x.go\n@@ -12,7 +12,7 @@\n \t_EINTR  = 0x4\n \t_EAGAIN = 0xb\n \t_ENOMEM = 0xc\n-\t_ENOSYS = 0x26\n+\t_ENOSYS = 0x59\n \n \t_PROT_NONE  = 0x0\n \t_PROT_READ  = 0x1\ndiff --git a/src/runtime/defs_linux_mipsx.go b/src/runtime/defs_linux_mipsx.go\nindex 5a446e0..b8da4d0 100644\n--- a/src/runtime/defs_linux_mipsx.go\n+++ b/src/runtime/defs_linux_mipsx.go\n@@ -12,7 +12,7 @@\n \t_EINTR  = 0x4\n \t_EAGAIN = 0xb\n \t_ENOMEM = 0xc\n-\t_ENOSYS = 0x26\n+\t_ENOSYS = 0x59\n \n \t_PROT_NONE  = 0x0\n \t_PROT_READ  = 0x1\n"
  },
  {
    "path": "core/Clash.Meta/.github/patch/issue77930.patch",
    "content": "From f4de14a515221e27c0d79446b423849a6546e3a6 Mon Sep 17 00:00:00 2001\nFrom: Brad Fitzpatrick <bradfitz@golang.org>\nDate: Tue, 24 Mar 2026 23:02:09 +0000\nSubject: [PATCH] runtime: use uname version check for 64-bit time on 32-bit\n arch codepaths\n\nThe previous fallback-on-ENOSYS logic causes issues on forks of Linux.\n\nAndroid: #77621 (CL 750040 added a workaround with a TODO,\nthis fixes that TODO)\nCauses the OS to terminate the program when running on Android\nversions <=10 since the seccomp jail does not know about the 64-bit\ntime syscall and is configured to terminate the program on any\nunknown syscall.\n\nSynology's Linux: #77930\nOn old versions of Synology's Linux they added custom vendor syscalls\nwithout adding a gap in the syscall numbers, that means when we call\nthe newer Linux syscall which was added later, Synology's Linux\ninterprets it as a completely different vendor syscall.\n\nOriginally by Jorropo in CL 751340.\n\nFixes golang/go#77930\nUpdates tailscale/go#162\nOriginally https://go-review.googlesource.com/c/go/+/758902/2\n\nCo-authored-by: Jorropo <jorropo.pgm@gmail.com>\nChange-Id: I90e15495d9249fd7f6e112f9e3ae8ad1322f56e0\n---\n .../runtime/syscall/linux/defs_linux_386.go   |  1 +\n .../runtime/syscall/linux/defs_linux_amd64.go |  1 +\n .../runtime/syscall/linux/defs_linux_arm.go   |  1 +\n .../runtime/syscall/linux/defs_linux_arm64.go |  1 +\n .../syscall/linux/defs_linux_loong64.go       |  1 +\n .../syscall/linux/defs_linux_mips64x.go       |  1 +\n .../runtime/syscall/linux/defs_linux_mipsx.go |  1 +\n .../syscall/linux/defs_linux_ppc64x.go        |  1 +\n .../syscall/linux/defs_linux_riscv64.go       |  1 +\n .../runtime/syscall/linux/defs_linux_s390x.go |  1 +\n .../runtime/syscall/linux/syscall_linux.go    | 14 +++++\n src/runtime/os_linux.go                       | 62 +++++++++++++++++++\n src/runtime/os_linux32.go                     | 28 +++------\n src/runtime/os_linux64.go                     |  2 +\n 14 files changed, 97 insertions(+), 19 deletions(-)\n\ndiff --git a/src/internal/runtime/syscall/linux/defs_linux_386.go b/src/internal/runtime/syscall/linux/defs_linux_386.go\nindex 7fdf5d3f8062fa..4e8e645dc49a66 100644\n--- a/src/internal/runtime/syscall/linux/defs_linux_386.go\n+++ b/src/internal/runtime/syscall/linux/defs_linux_386.go\n@@ -17,6 +17,7 @@ const (\n \tSYS_OPENAT        = 295\n \tSYS_PREAD64       = 180\n \tSYS_READ          = 3\n+\tSYS_UNAME         = 122\n \n \tEFD_NONBLOCK = 0x800\n \ndiff --git a/src/internal/runtime/syscall/linux/defs_linux_amd64.go b/src/internal/runtime/syscall/linux/defs_linux_amd64.go\nindex 2c8676e6e9b4d9..fa764d9ccd9b8e 100644\n--- a/src/internal/runtime/syscall/linux/defs_linux_amd64.go\n+++ b/src/internal/runtime/syscall/linux/defs_linux_amd64.go\n@@ -17,6 +17,7 @@ const (\n \tSYS_OPENAT        = 257\n \tSYS_PREAD64       = 17\n \tSYS_READ          = 0\n+\tSYS_UNAME         = 63\n \n \tEFD_NONBLOCK = 0x800\n \ndiff --git a/src/internal/runtime/syscall/linux/defs_linux_arm.go b/src/internal/runtime/syscall/linux/defs_linux_arm.go\nindex a0b395d6762734..cef556d5f6f986 100644\n--- a/src/internal/runtime/syscall/linux/defs_linux_arm.go\n+++ b/src/internal/runtime/syscall/linux/defs_linux_arm.go\n@@ -17,6 +17,7 @@ const (\n \tSYS_OPENAT        = 322\n \tSYS_PREAD64       = 180\n \tSYS_READ          = 3\n+\tSYS_UNAME         = 122\n \n \tEFD_NONBLOCK = 0x800\n \ndiff --git a/src/internal/runtime/syscall/linux/defs_linux_arm64.go b/src/internal/runtime/syscall/linux/defs_linux_arm64.go\nindex 223dce0c5b4281..eabddbac1bc063 100644\n--- a/src/internal/runtime/syscall/linux/defs_linux_arm64.go\n+++ b/src/internal/runtime/syscall/linux/defs_linux_arm64.go\n@@ -17,6 +17,7 @@ const (\n \tSYS_OPENAT        = 56\n \tSYS_PREAD64       = 67\n \tSYS_READ          = 63\n+\tSYS_UNAME         = 160\n \n \tEFD_NONBLOCK = 0x800\n \ndiff --git a/src/internal/runtime/syscall/linux/defs_linux_loong64.go b/src/internal/runtime/syscall/linux/defs_linux_loong64.go\nindex 8aa61c391dcdcb..08e5d49b83c9bd 100644\n--- a/src/internal/runtime/syscall/linux/defs_linux_loong64.go\n+++ b/src/internal/runtime/syscall/linux/defs_linux_loong64.go\n@@ -17,6 +17,7 @@ const (\n \tSYS_OPENAT        = 56\n \tSYS_PREAD64       = 67\n \tSYS_READ          = 63\n+\tSYS_UNAME         = 160\n \n \tEFD_NONBLOCK = 0x800\n \ndiff --git a/src/internal/runtime/syscall/linux/defs_linux_mips64x.go b/src/internal/runtime/syscall/linux/defs_linux_mips64x.go\nindex 84b760dc1b5545..b5794e5002af5e 100644\n--- a/src/internal/runtime/syscall/linux/defs_linux_mips64x.go\n+++ b/src/internal/runtime/syscall/linux/defs_linux_mips64x.go\n@@ -19,6 +19,7 @@ const (\n \tSYS_OPENAT        = 5247\n \tSYS_PREAD64       = 5016\n \tSYS_READ          = 5000\n+\tSYS_UNAME         = 5061\n \n \tEFD_NONBLOCK = 0x80\n \ndiff --git a/src/internal/runtime/syscall/linux/defs_linux_mipsx.go b/src/internal/runtime/syscall/linux/defs_linux_mipsx.go\nindex a9be21414c26f9..1fb4d919d1a318 100644\n--- a/src/internal/runtime/syscall/linux/defs_linux_mipsx.go\n+++ b/src/internal/runtime/syscall/linux/defs_linux_mipsx.go\n@@ -19,6 +19,7 @@ const (\n \tSYS_OPENAT        = 4288\n \tSYS_PREAD64       = 4200\n \tSYS_READ          = 4003\n+\tSYS_UNAME         = 4122\n \n \tEFD_NONBLOCK = 0x80\n \ndiff --git a/src/internal/runtime/syscall/linux/defs_linux_ppc64x.go b/src/internal/runtime/syscall/linux/defs_linux_ppc64x.go\nindex 63f4e5d7864de4..ee93ad345b810f 100644\n--- a/src/internal/runtime/syscall/linux/defs_linux_ppc64x.go\n+++ b/src/internal/runtime/syscall/linux/defs_linux_ppc64x.go\n@@ -19,6 +19,7 @@ const (\n \tSYS_OPENAT        = 286\n \tSYS_PREAD64       = 179\n \tSYS_READ          = 3\n+\tSYS_UNAME         = 122\n \n \tEFD_NONBLOCK = 0x800\n \ndiff --git a/src/internal/runtime/syscall/linux/defs_linux_riscv64.go b/src/internal/runtime/syscall/linux/defs_linux_riscv64.go\nindex 8aa61c391dcdcb..08e5d49b83c9bd 100644\n--- a/src/internal/runtime/syscall/linux/defs_linux_riscv64.go\n+++ b/src/internal/runtime/syscall/linux/defs_linux_riscv64.go\n@@ -17,6 +17,7 @@ const (\n \tSYS_OPENAT        = 56\n \tSYS_PREAD64       = 67\n \tSYS_READ          = 63\n+\tSYS_UNAME         = 160\n \n \tEFD_NONBLOCK = 0x800\n \ndiff --git a/src/internal/runtime/syscall/linux/defs_linux_s390x.go b/src/internal/runtime/syscall/linux/defs_linux_s390x.go\nindex 52945db0e5b72f..da11c704081abc 100644\n--- a/src/internal/runtime/syscall/linux/defs_linux_s390x.go\n+++ b/src/internal/runtime/syscall/linux/defs_linux_s390x.go\n@@ -17,6 +17,7 @@ const (\n \tSYS_OPENAT        = 288\n \tSYS_PREAD64       = 180\n \tSYS_READ          = 3\n+\tSYS_UNAME         = 122\n \n \tEFD_NONBLOCK = 0x800\n \ndiff --git a/src/internal/runtime/syscall/linux/syscall_linux.go b/src/internal/runtime/syscall/linux/syscall_linux.go\nindex 8201e7d1907444..b64f511b03c947 100644\n--- a/src/internal/runtime/syscall/linux/syscall_linux.go\n+++ b/src/internal/runtime/syscall/linux/syscall_linux.go\n@@ -86,3 +86,17 @@ func Pread(fd int, p []byte, offset int64) (n int, errno uintptr) {\n \t}\n \treturn int(r1), e\n }\n+\n+type Utsname struct {\n+\tSysname    [65]byte\n+\tNodename   [65]byte\n+\tRelease    [65]byte\n+\tVersion    [65]byte\n+\tMachine    [65]byte\n+\tDomainname [65]byte\n+}\n+\n+func Uname(buf *Utsname) (errno uintptr) {\n+\t_, _, e := Syscall6(SYS_UNAME, uintptr(unsafe.Pointer(buf)), 0, 0, 0, 0, 0)\n+\treturn e\n+}\ndiff --git a/src/runtime/os_linux.go b/src/runtime/os_linux.go\nindex 7e6af22d48a764..493567b5303673 100644\n--- a/src/runtime/os_linux.go\n+++ b/src/runtime/os_linux.go\n@@ -354,6 +354,7 @@ func osinit() {\n \tnumCPUStartup = getCPUCount()\n \tphysHugePageSize = getHugePageSize()\n \tvgetrandomInit()\n+\tconfigure64bitsTimeOn32BitsArchitectures()\n }\n \n var urandom_dev = []byte(\"/dev/urandom\\x00\")\n@@ -935,3 +936,64 @@ func mprotect(addr unsafe.Pointer, n uintptr, prot int32) (ret int32, errno int3\n \tr, _, err := linux.Syscall6(linux.SYS_MPROTECT, uintptr(addr), n, uintptr(prot), 0, 0, 0)\n \treturn int32(r), int32(err)\n }\n+\n+type kernelVersion struct {\n+\tmajor int\n+\tminor int\n+}\n+\n+// getKernelVersion returns major and minor kernel version numbers\n+// parsed from the uname release field.\n+func getKernelVersion() kernelVersion {\n+\tvar buf linux.Utsname\n+\tif e := linux.Uname(&buf); e != 0 {\n+\t\tthrow(\"uname failed\")\n+\t}\n+\n+\trel := gostringnocopy(&buf.Release[0])\n+\tmajor, minor, _, ok := parseRelease(rel)\n+\tif !ok {\n+\t\tthrow(\"failed to parse kernel version from uname\")\n+\t}\n+\treturn kernelVersion{major: major, minor: minor}\n+}\n+\n+// parseRelease parses a dot-separated version number. It follows the\n+// semver syntax, but allows the minor and patch versions to be\n+// elided.\n+func parseRelease(rel string) (major, minor, patch int, ok bool) {\n+\t// Strip anything after a dash or plus.\n+\tfor i := 0; i < len(rel); i++ {\n+\t\tif rel[i] == '-' || rel[i] == '+' {\n+\t\t\trel = rel[:i]\n+\t\t\tbreak\n+\t\t}\n+\t}\n+\n+\tnext := func() (int, bool) {\n+\t\tfor i := 0; i < len(rel); i++ {\n+\t\t\tif rel[i] == '.' {\n+\t\t\t\tver, err := strconv.Atoi(rel[:i])\n+\t\t\t\trel = rel[i+1:]\n+\t\t\t\treturn ver, err == nil\n+\t\t\t}\n+\t\t}\n+\t\tver, err := strconv.Atoi(rel)\n+\t\trel = \"\"\n+\t\treturn ver, err == nil\n+\t}\n+\tif major, ok = next(); !ok || rel == \"\" {\n+\t\treturn\n+\t}\n+\tif minor, ok = next(); !ok || rel == \"\" {\n+\t\treturn\n+\t}\n+\tpatch, ok = next()\n+\treturn\n+}\n+\n+// GE checks if the running kernel version\n+// is greater than or equal to the provided version.\n+func (kv kernelVersion) GE(x, y int) bool {\n+\treturn kv.major > x || (kv.major == x && kv.minor >= y)\n+}\ndiff --git a/src/runtime/os_linux32.go b/src/runtime/os_linux32.go\nindex 16de6fb350f624..02cb18f32d57d2 100644\n--- a/src/runtime/os_linux32.go\n+++ b/src/runtime/os_linux32.go\n@@ -7,27 +7,25 @@\n package runtime\n \n import (\n-\t\"internal/runtime/atomic\"\n \t\"unsafe\"\n )\n \n+func configure64bitsTimeOn32BitsArchitectures() {\n+\tuse64bitsTimeOn32bits = getKernelVersion().GE(5, 1)\n+}\n+\n //go:noescape\n func futex_time32(addr unsafe.Pointer, op int32, val uint32, ts *timespec32, addr2 unsafe.Pointer, val3 uint32) int32\n \n //go:noescape\n func futex_time64(addr unsafe.Pointer, op int32, val uint32, ts *timespec, addr2 unsafe.Pointer, val3 uint32) int32\n \n-var isFutexTime32bitOnly atomic.Bool\n+var use64bitsTimeOn32bits bool\n \n //go:nosplit\n func futex(addr unsafe.Pointer, op int32, val uint32, ts *timespec, addr2 unsafe.Pointer, val3 uint32) int32 {\n-\tif !isFutexTime32bitOnly.Load() {\n-\t\tret := futex_time64(addr, op, val, ts, addr2, val3)\n-\t\t// futex_time64 is only supported on Linux 5.0+\n-\t\tif ret != -_ENOSYS {\n-\t\t\treturn ret\n-\t\t}\n-\t\tisFutexTime32bitOnly.Store(true)\n+\tif use64bitsTimeOn32bits {\n+\t\treturn futex_time64(addr, op, val, ts, addr2, val3)\n \t}\n \t// Downgrade ts.\n \tvar ts32 timespec32\n@@ -45,17 +43,10 @@ func timer_settime32(timerid int32, flags int32, new, old *itimerspec32) int32\n //go:noescape\n func timer_settime64(timerid int32, flags int32, new, old *itimerspec) int32\n \n-var isSetTime32bitOnly atomic.Bool\n-\n //go:nosplit\n func timer_settime(timerid int32, flags int32, new, old *itimerspec) int32 {\n-\tif !isSetTime32bitOnly.Load() {\n-\t\tret := timer_settime64(timerid, flags, new, old)\n-\t\t// timer_settime64 is only supported on Linux 5.0+\n-\t\tif ret != -_ENOSYS {\n-\t\t\treturn ret\n-\t\t}\n-\t\tisSetTime32bitOnly.Store(true)\n+\tif use64bitsTimeOn32bits {\n+\t\treturn timer_settime64(timerid, flags, new, old)\n \t}\n \n \tvar newts, oldts itimerspec32\n@@ -73,6 +64,5 @@ func timer_settime(timerid int32, flags int32, new, old *itimerspec) int32 {\n \t\told32 = &oldts\n \t}\n \n-\t// Fall back to 32-bit timer\n \treturn timer_settime32(timerid, flags, new32, old32)\n }\ndiff --git a/src/runtime/os_linux64.go b/src/runtime/os_linux64.go\nindex 7b70d80fbe5a89..f9571dd7586614 100644\n--- a/src/runtime/os_linux64.go\n+++ b/src/runtime/os_linux64.go\n@@ -10,6 +10,8 @@ import (\n \t\"unsafe\"\n )\n \n+func configure64bitsTimeOn32BitsArchitectures() {}\n+\n //go:noescape\n func futex(addr unsafe.Pointer, op int32, val uint32, ts *timespec, addr2 unsafe.Pointer, val3 uint32) int32\n "
  },
  {
    "path": "core/Clash.Meta/.github/patch/issue77975.patch",
    "content": "From 1a44be4cecdc742ac6cce9825f9ffc19857c99f3 Mon Sep 17 00:00:00 2001\nFrom: database64128 <free122448@hotmail.com>\nDate: Mon, 9 Mar 2026 16:25:16 +0800\nSubject: [PATCH] [release-branch.go1.26] internal/poll: move rsan to heap on\n windows\n\nAccording to https://learn.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsarecvfrom,\nthe memory pointed to by lpFromlen must remain available during the\noverlapped I/O, and therefore cannot be allocated on the stack.\n\nCL 685417 moved the rsan field out of the operation struct and placed\nit on stack, which violates the above requirement and causes stack\ncorruption.\n\nUnfortunately, it is no longer possible to cleanly revert CL 685417.\nInstead of attempting to revert it, this CL bundles rsan together\nwith rsa in the same sync.Pool. The new wsaRsa struct is still in the\nsame size class, so no additional overhead is introduced by this\nchange.\n\nFixes #78041.\n\nChange-Id: I5ffbccb332515116ddc03fb7c40ffc9293cad2ab\nReviewed-on: https://go-review.googlesource.com/c/go/+/753040\nReviewed-by: Quim Muntal <quimmuntal@gmail.com>\nReviewed-by: Cherry Mui <cherryyz@google.com>\nCommit-Queue: Cherry Mui <cherryyz@google.com>\nLUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>\nReviewed-by: Damien Neil <dneil@google.com>\nReviewed-on: https://go-review.googlesource.com/c/go/+/753480\nReviewed-by: Mark Freeman <markfreeman@google.com>\n---\n src/internal/poll/fd_windows.go | 94 +++++++++++++++++++++------------\n 1 file changed, 59 insertions(+), 35 deletions(-)\n\ndiff --git a/src/internal/poll/fd_windows.go b/src/internal/poll/fd_windows.go\nindex 2ba967f990982f..26319548e3c310 100644\n--- a/src/internal/poll/fd_windows.go\n+++ b/src/internal/poll/fd_windows.go\n@@ -149,7 +149,7 @@ var wsaMsgPool = sync.Pool{\n \n // newWSAMsg creates a new WSAMsg with the provided parameters.\n // Use [freeWSAMsg] to free it.\n-func newWSAMsg(p []byte, oob []byte, flags int, unconnected bool) *windows.WSAMsg {\n+func newWSAMsg(p []byte, oob []byte, flags int, rsa *wsaRsa) *windows.WSAMsg {\n \t// The returned object can't be allocated in the stack because it is accessed asynchronously\n \t// by Windows in between several system calls. If the stack frame is moved while that happens,\n \t// then Windows may access invalid memory.\n@@ -166,34 +166,46 @@ func newWSAMsg(p []byte, oob []byte, flags int, unconnected bool) *windows.WSAM\n \t}\n \tmsg.Flags = uint32(flags)\n-\tif unconnected {\n-\t\tmsg.Name = wsaRsaPool.Get().(*syscall.RawSockaddrAny)\n-\t\tmsg.Namelen = int32(unsafe.Sizeof(syscall.RawSockaddrAny{}))\n+\tif rsa != nil {\n+\t\tmsg.Name = &rsa.name\n+\t\tmsg.Namelen = rsa.namelen\n \t}\n \treturn msg\n }\n \n func freeWSAMsg(msg *windows.WSAMsg) {\n \t// Clear pointers to buffers so they can be released by garbage collector.\n+\tmsg.Name = nil\n \tmsg.Buffers = nil\n \tmsg.Control.Buf = nil\n-\tif msg.Name != nil {\n-\t\twsaRsaPool.Put(msg.Name)\n-\t\tmsg.Name = nil\n-\t}\n \twsaMsgPool.Put(msg)\n }\n \n-var wsaRsaPool = sync.Pool{\n+// wsaRsa bundles a [syscall.RawSockaddrAny] with its length for efficient caching.\n+//\n+// When used by WSARecvFrom, wsaRsa must be on the heap. See\n+// https://go.dev/issue/77975.\ntype wsaRsa struct {\n+\tname    syscall.RawSockaddrAny\n+\tnamelen int32\n+}\n+\n+var wsaRsaPool = sync.Pool{\n \tNew: func() any {\n-\t\treturn new(syscall.RawSockaddrAny)\n+\t\treturn new(wsaRsa)\n \t},\n }\n \n+func newWSARsa() *wsaRsa {\n+\trsa := wsaRsaPool.Get().(*wsaRsa)\n+\trsa.name = syscall.RawSockaddrAny{}\n+\trsa.namelen = int32(unsafe.Sizeof(syscall.RawSockaddrAny{}))\n+\treturn rsa\n+}\n+\n var operationPool = sync.Pool{\n \tNew: func() any {\n \t\treturn new(operation)\n@@ -739,19 +751,18 @@ func (fd *FD) ReadFrom(buf []byte) (int, syscall.Sockaddr, error) {\n \n \tfd.pin('r', &buf[0])\n \n-\trsa := wsaRsaPool.Get().(*syscall.RawSockaddrAny)\n+\trsa := newWSARsa()\n \tdefer wsaRsaPool.Put(rsa)\n-\trsai := syscall.RawSockaddrAny{}\n \to := operationPool.Get().(*operation)\n \tdefer operationPool.Put(o)\n \to.prepared = false\n-\to.msg = newWSAMsg(buf, nil, 0, true)\n+\to.msg = newWSAMsg(buf, nil, 0, rsa)\n \tn, err := fd.execIO(o, func(o *operation) error {\n \t\to.qty = 0\n \t\treturn windows.WSARecvFrom(fd.Sysfd, &o.msg.Buffers[0], 1, &o.qty, &o.msg.Flags,\n-\t\t\trsa, &rsai, &o.o, nil)\n+\t\t\t&rsa.name, &rsa.namelen, &o.o, nil)\n \t})\n \tif err != nil {\n \t\treturn n, nil, err\n@@ -760,7 +771,7 @@ func (fd *FD) ReadFrom(buf []byte) (int, syscall.Sockaddr, error) {\n \t\treturn n, nil, nil\n \t}\n \tvar sa syscall.Sockaddr\n-\tsa, _ = rsa.Sockaddr()\n+\tsa, _ = rsa.name.Sockaddr()\n \treturn n, sa, nil\n }\n \n@@ -781,19 +792,18 @@ func (fd *FD) ReadFromInet4(buf []byte, sa4 *syscall.SockaddrInet4) (int, error)\n \n \tfd.pin('r', &buf[0])\n \n-\trsa := wsaRsaPool.Get().(*syscall.RawSockaddrAny)\n+\trsa := newWSARsa()\n \tdefer wsaRsaPool.Put(rsa)\n-\trsai := syscall.RawSockaddrAny{}\n \to := operationPool.Get().(*operation)\n \tdefer operationPool.Put(o)\n \to.prepared = false\n-\to.msg = newWSAMsg(buf, nil, 0, true)\n+\to.msg = newWSAMsg(buf, nil, 0, rsa)\n \tn, err := fd.execIO(o, func(o *operation) error {\n \t\to.qty = 0\n \t\treturn windows.WSARecvFrom(fd.Sysfd, &o.msg.Buffers[0], 1, &o.qty, &o.msg.Flags,\n-\t\t\trsa, &rsai, &o.o, nil)\n+\t\t\t&rsa.name, &rsa.namelen, &o.o, nil)\n \t})\n \tif err != nil {\n \t\treturn n, err\n@@ -801,7 +811,7 @@ func (fd *FD) ReadFromInet4(buf []byte, sa4 *syscall.SockaddrInet4) (int, error)\n \tif n == 0 {\n \t\treturn n, nil\n \t}\n-\trawToSockaddrInet4(rsa, sa4)\n+\trawToSockaddrInet4(&rsa.name, sa4)\n \treturn n, nil\n }\n \n@@ -822,19 +832,18 @@ func (fd *FD) ReadFromInet6(buf []byte, sa6 *syscall.SockaddrInet6) (int, error)\n \n \tfd.pin('r', &buf[0])\n \n-\trsa := wsaRsaPool.Get().(*syscall.RawSockaddrAny)\n+\trsa := newWSARsa()\n \tdefer wsaRsaPool.Put(rsa)\n-\trsai := syscall.RawSockaddrAny{}\n \to := operationPool.Get().(*operation)\n \tdefer operationPool.Put(o)\n \to.prepared = false\n-\to.msg = newWSAMsg(buf, nil, 0, true)\n+\to.msg = newWSAMsg(buf, nil, 0, rsa)\n \tn, err := fd.execIO(o, func(o *operation) error {\n \t\to.qty = 0\n \t\treturn windows.WSARecvFrom(fd.Sysfd, &o.msg.Buffers[0], 1, &o.qty, &o.msg.Flags,\n-\t\t\trsa, &rsai, &o.o, nil)\n+\t\t\t&rsa.name, &rsa.namelen, &o.o, nil)\n \t})\n \tif err != nil {\n \t\treturn n, err\n@@ -842,6 +851,6 @@ func (fd *FD) ReadFromInet6(buf []byte, sa6 *syscall.SockaddrInet6) (int, error)\n \tif n == 0 {\n \t\treturn n, nil\n \t}\n-\trawToSockaddrInet6(rsa, sa6)\n+\trawToSockaddrInet6(&rsa.name, sa6)\n \treturn n, nil\n }\n"
  },
  {
    "path": "core/Clash.Meta/.github/release/.fpm_systemd",
    "content": "-s dir\n--name mihomo\n--category net\n--license GPL-3.0-or-later\n--description \"The universal proxy platform.\"\n--url \"https://wiki.metacubex.one/\"\n--maintainer \"MetaCubeX <none@example.com>\"\n--deb-field \"Bug: https://github.com/MetaCubeX/mihomo/issues\"\n--no-deb-generate-changes\n--config-files /etc/mihomo/config.yaml\n\n.github/release/config.yaml=/etc/mihomo/config.yaml\n\n.github/release/mihomo.service=/usr/lib/systemd/system/mihomo.service\n.github/release/mihomo@.service=/usr/lib/systemd/system/mihomo@.service\n\n\nLICENSE=/usr/share/licenses/mihomo/LICENSE"
  },
  {
    "path": "core/Clash.Meta/.github/release/config.yaml",
    "content": "mixed-port: 7890\n\ndns:\n  enable: true\n  ipv6: true\n  enhanced-mode: fake-ip\n  fake-ip-filter:\n    - \"*\"\n    - \"+.lan\"\n    - \"+.local\"\n  nameserver:\n    - system\n\nrules:\n  - MATCH,DIRECT"
  },
  {
    "path": "core/Clash.Meta/.github/release/mihomo.service",
    "content": "[Unit]\nDescription=mihomo Daemon, Another Clash Kernel.\nDocumentation=https://wiki.metacubex.one\nAfter=network.target nss-lookup.target network-online.target\n\n[Service]\nType=simple\nCapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE\nAmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE\nExecStart=/usr/bin/mihomo -d /etc/mihomo\nExecReload=/bin/kill -HUP $MAINPID\nRestart=on-failure\nRestartSec=10\nLimitNOFILE=infinity\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "core/Clash.Meta/.github/release/mihomo@.service",
    "content": "[Unit]\nDescription=mihomo Daemon, Another Clash Kernel.\nDocumentation=https://wiki.metacubex.one\nAfter=network.target nss-lookup.target network-online.target\n\n[Service]\nType=simple\nCapabilityBoundingSet=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE\nAmbientCapabilities=CAP_NET_ADMIN CAP_NET_RAW CAP_NET_BIND_SERVICE CAP_SYS_TIME CAP_SYS_PTRACE CAP_DAC_READ_SEARCH CAP_DAC_OVERRIDE\nExecStart=/usr/bin/mihomo -d /etc/mihomo\nExecReload=/bin/kill -HUP $MAINPID\nRestart=on-failure\nRestartSec=10\nLimitNOFILE=infinity\n\n[Install]\nWantedBy=multi-user.target\n"
  },
  {
    "path": "core/Clash.Meta/.github/release.sh",
    "content": "#!/bin/bash\n\nFILENAMES=$(ls)\nfor FILENAME in $FILENAMES\ndo\n    if [[ ! ($FILENAME =~ \".exe\" || $FILENAME =~ \".sh\")]];then\n        gzip -S \".gz\" $FILENAME\n    elif [[ $FILENAME =~ \".exe\" ]];then\n        zip -m ${FILENAME%.*}.zip $FILENAME\n    else echo \"skip $FILENAME\"\n    fi\ndone\n\nFILENAMES=$(ls)\nfor FILENAME in $FILENAMES\ndo\n    if [[ $FILENAME =~ \".zip\" ]];then\n        echo \"rename $FILENAME\"\n        mv $FILENAME ${FILENAME%.*}-${VERSION}.zip\n    elif [[ $FILENAME =~ \".gz\" ]];then\n        echo \"rename $FILENAME\"\n        mv $FILENAME ${FILENAME%.*}-${VERSION}.gz\n    else\n        echo \"skip $FILENAME\"\n    fi\ndone"
  },
  {
    "path": "core/Clash.Meta/.github/rename-cgo.sh",
    "content": "#!/bin/bash\n\nFILENAMES=$(ls)\nfor FILENAME in $FILENAMES\ndo\n    if [[ $FILENAME =~ \"darwin-10.16-arm64\" ]];then\n        echo \"rename darwin-10.16-arm64 $FILENAME\"\n        mv $FILENAME mihomo-darwin-arm64-cgo\n    elif [[ $FILENAME =~ \"darwin-10.16-amd64\" ]];then\n        echo \"rename darwin-10.16-amd64 $FILENAME\"\n        mv $FILENAME mihomo-darwin-amd64-cgo\n    elif [[ $FILENAME =~ \"windows-4.0-386\" ]];then\n        echo \"rename windows 386 $FILENAME\"\n        mv $FILENAME mihomo-windows-386-cgo.exe\n    elif [[ $FILENAME =~ \"windows-4.0-amd64\" ]];then\n        echo \"rename windows amd64 $FILENAME\"\n        mv $FILENAME mihomo-windows-amd64-cgo.exe\n    elif [[ $FILENAME =~ \"mihomo-linux-arm-5\" ]];then\n        echo \"rename mihomo-linux-arm-5 $FILENAME\"\n        mv $FILENAME mihomo-linux-armv5-cgo\n    elif [[ $FILENAME =~ \"mihomo-linux-arm-6\" ]];then\n        echo \"rename mihomo-linux-arm-6 $FILENAME\"\n        mv $FILENAME mihomo-linux-armv6-cgo\n    elif [[ $FILENAME =~ \"mihomo-linux-arm-7\" ]];then\n        echo \"rename mihomo-linux-arm-7 $FILENAME\"\n        mv $FILENAME mihomo-linux-armv7-cgo\n    elif [[ $FILENAME =~ \"linux\" ]];then\n        echo \"rename linux $FILENAME\"\n        mv $FILENAME $FILENAME-cgo\n    elif [[ $FILENAME =~ \"android\" ]];then\n        echo \"rename android $FILENAME\"\n        mv $FILENAME $FILENAME-cgo\n    else echo \"skip $FILENAME\"\n    fi\ndone"
  },
  {
    "path": "core/Clash.Meta/.github/rename-go120.sh",
    "content": "#!/bin/bash\n\nFILENAMES=$(ls)\nfor FILENAME in $FILENAMES\ndo\n    if [[ ! ($FILENAME =~ \".exe\" || $FILENAME =~ \".sh\")]];then\n        mv $FILENAME ${FILENAME}-go120\n    elif [[ $FILENAME =~ \".exe\" ]];then\n        mv $FILENAME ${FILENAME%.*}-go120.exe\n    else echo \"skip $FILENAME\"\n    fi\ndone"
  },
  {
    "path": "core/Clash.Meta/.github/workflows/build.yml",
    "content": "name: Build\non:\n  workflow_dispatch:\n    inputs:\n      version:\n        description: \"Tag version to release\"\n        required: true\n  push:\n    paths-ignore:\n      - \"docs/**\"\n      - \"README.md\"\n      - \".github/ISSUE_TEMPLATE/**\"\n    branches:\n      - Alpha\n    tags:\n      - \"v*\"\n  pull_request:\n    branches:\n      - Alpha\nconcurrency:\n  group: \"${{ github.workflow }}-${{ github.ref }}\"\n  cancel-in-progress: true\n  \nenv:\n  REGISTRY: docker.io\njobs:\n  build:\n    runs-on: ubuntu-latest\n    strategy:\n      matrix:\n        jobs:\n          - { goos: linux, goarch: '386', go386: sse2, output: '386', debian: i386, rpm: i386}\n          - { goos: linux, goarch: '386', go386: softfloat, output: '386-softfloat' }\n          - { goos: linux, goarch: amd64, goamd64: v1, output: amd64-compatible} # old style file name will be removed in next released\n          - { goos: linux, goarch: amd64, goamd64: v3, output: amd64, debian: amd64, rpm: x86_64, pacman: x86_64, tarball: tarball}\n          - { goos: linux, goarch: amd64, goamd64: v1, output: amd64-v1, debian: amd64, rpm: x86_64, pacman: x86_64, test: test }\n          - { goos: linux, goarch: amd64, goamd64: v2, output: amd64-v2, debian: amd64, rpm: x86_64, pacman: x86_64}\n          - { goos: linux, goarch: amd64, goamd64: v3, output: amd64-v3, debian: amd64, rpm: x86_64, pacman: x86_64}\n          - { goos: linux, goarch: arm64, output: arm64, debian: arm64, rpm: aarch64, pacman: aarch64}  \n          - { goos: linux, goarch: arm, goarm: '5', output: armv5 }\n          - { goos: linux, goarch: arm, goarm: '6', output: armv6, debian: armel, rpm: armv6hl} \n          - { goos: linux, goarch: arm, goarm: '7', output: armv7, debian: armhf, rpm: armv7hl, pacman: armv7hl}\n          - { goos: linux, goarch: mips, gomips: hardfloat, output: mips-hardfloat }\n          - { goos: linux, goarch: mips, gomips: softfloat, output: mips-softfloat }\n          - { goos: linux, goarch: mipsle, gomips: hardfloat, output: mipsle-hardfloat }\n          - { goos: linux, goarch: mipsle, gomips: softfloat, output: mipsle-softfloat }\n          - { goos: linux, goarch: mips64, output: mips64 }\n          - { goos: linux, goarch: mips64le, output: mips64le, debian: mips64el, rpm: mips64el }\n          - { goos: linux, goarch: loong64, output: loong64-abi1, abi: '1', debian: loongarch64, rpm: loongarch64, goversion: 'custom' }\n          - { goos: linux, goarch: loong64, output: loong64-abi2, abi: '2', debian: loong64, rpm: loong64 }\n          - { goos: linux, goarch: riscv64, output: riscv64, debian: riscv64, rpm: riscv64 }\n          - { goos: linux, goarch: s390x, output: s390x, debian: s390x, rpm: s390x }\n          - { goos: linux, goarch: ppc64le, output: ppc64le, debian: ppc64el, rpm: ppc64le }\n\n          # Go 1.26 with special patch can work on macOS 10.13 High Sierra\n          # https://github.com/MetaCubeX/go/commits/release-branch.go1.26/\n          - { goos: darwin, goarch: amd64, goamd64: v1, output: amd64-compatible } # old style file name will be removed in next released\n          - { goos: darwin, goarch: amd64, goamd64: v3, output: amd64 }\n          - { goos: darwin, goarch: amd64, goamd64: v1, output: amd64-v1 }\n          - { goos: darwin, goarch: amd64, goamd64: v2, output: amd64-v2 }\n          - { goos: darwin, goarch: amd64, goamd64: v3, output: amd64-v3 }\n          - { goos: darwin, goarch: arm64, output: arm64 }\n\n          # Go 1.26 with special patch can work on Windows 7\n          # https://github.com/MetaCubeX/go/commits/release-branch.go1.26/\n          - { goos: windows, goarch: '386', output: '386' }\n          - { goos: windows, goarch: amd64, goamd64: v1, output: amd64-compatible } # old style file name will be removed in next released\n          - { goos: windows, goarch: amd64, goamd64: v3, output: amd64 }\n          - { goos: windows, goarch: amd64, goamd64: v1, output: amd64-v1 }\n          - { goos: windows, goarch: amd64, goamd64: v2, output: amd64-v2 }\n          - { goos: windows, goarch: amd64, goamd64: v3, output: amd64-v3 }\n          - { goos: windows, goarch: arm64, output: arm64 }\n\n          - { goos: freebsd, goarch: '386', output: '386' }\n          - { goos: freebsd, goarch: amd64, goamd64: v1, output: amd64-compatible } # old style file name will be removed in next released\n          - { goos: freebsd, goarch: amd64, goamd64: v3, output: amd64 }\n          - { goos: freebsd, goarch: amd64, goamd64: v1, output: amd64-v1 }\n          - { goos: freebsd, goarch: amd64, goamd64: v2, output: amd64-v2 }\n          - { goos: freebsd, goarch: amd64, goamd64: v3, output: amd64-v3 }\n          - { goos: freebsd, goarch: arm64, output: arm64 }\n\n          - { goos: android, goarch: '386', ndk: i686-linux-android34, output: '386' }\n          - { goos: android, goarch: amd64, ndk: x86_64-linux-android34, output: amd64 }\n          - { goos: android, goarch: arm, ndk: armv7a-linux-androideabi34, output: armv7 }\n          - { goos: android, goarch: arm64, ndk: aarch64-linux-android34, output: arm64-v8 }\n\n          # Go 1.25 with special patch can work on Windows 7\n          # https://github.com/MetaCubeX/go/commits/release-branch.go1.25/\n          - { goos: windows, goarch: '386', output: '386-go125', goversion: '1.25' }\n          - { goos: windows, goarch: amd64, goamd64: v1, output: amd64-v1-go125, goversion: '1.25' }\n          - { goos: windows, goarch: amd64, goamd64: v2, output: amd64-v2-go125, goversion: '1.25' }\n          - { goos: windows, goarch: amd64, goamd64: v3, output: amd64-v3-go125, goversion: '1.25' }\n\n          # Go 1.24 with special patch can work on Windows 7\n          # https://github.com/MetaCubeX/go/commits/release-branch.go1.24/\n          - { goos: windows, goarch: '386', output: '386-go124', goversion: '1.24' }\n          - { goos: windows, goarch: amd64, goamd64: v1, output: amd64-v1-go124, goversion: '1.24' }\n          - { goos: windows, goarch: amd64, goamd64: v2, output: amd64-v2-go124, goversion: '1.24' }\n          - { goos: windows, goarch: amd64, goamd64: v3, output: amd64-v3-go124, goversion: '1.24' }\n\n          # Go 1.23 with special patch can work on Windows 7\n          # https://github.com/MetaCubeX/go/commits/release-branch.go1.23/\n          - { goos: windows, goarch: '386', output: '386-go123', goversion: '1.23' }\n          - { goos: windows, goarch: amd64, goamd64: v1, output: amd64-v1-go123, goversion: '1.23' }\n          - { goos: windows, goarch: amd64, goamd64: v2, output: amd64-v2-go123, goversion: '1.23' }\n          - { goos: windows, goarch: amd64, goamd64: v3, output: amd64-v3-go123, goversion: '1.23' }\n\n          # Go 1.22 with special patch can work on Windows 7\n          # https://github.com/MetaCubeX/go/commits/release-branch.go1.22/\n          - { goos: windows, goarch: '386', output: '386-go122', goversion: '1.22' }\n          - { goos: windows, goarch: amd64, goamd64: v1, output: amd64-v1-go122, goversion: '1.22' }\n          - { goos: windows, goarch: amd64, goamd64: v2, output: amd64-v2-go122, goversion: '1.22' }\n          - { goos: windows, goarch: amd64, goamd64: v3, output: amd64-v3-go122, goversion: '1.22' }\n\n          # Go 1.21 can revert commit `9e4385` to work on Windows 7\n          # https://github.com/golang/go/issues/64622#issuecomment-1847475161\n          # (OR we can just use golang1.21.4 which unneeded any patch)\n          - { goos: windows, goarch: '386', output: '386-go121', goversion: '1.21' }\n          - { goos: windows, goarch: amd64, goamd64: v1, output: amd64-v1-go121, goversion: '1.21' }\n          - { goos: windows, goarch: amd64, goamd64: v2, output: amd64-v2-go121, goversion: '1.21' }\n          - { goos: windows, goarch: amd64, goamd64: v3, output: amd64-v3-go121, goversion: '1.21' }\n\n          # Go 1.20 is the last release that will run on any release of Windows 7, 8, Server 2008 and Server 2012. Go 1.21 will require at least Windows 10 or Server 2016.\n          - { goos: windows, goarch: '386', output: '386-go120', goversion: '1.20' }\n          - { goos: windows, goarch: amd64, goamd64: v1, output: amd64-v1-go120, goversion: '1.20' }\n          - { goos: windows, goarch: amd64, goamd64: v2, output: amd64-v2-go120, goversion: '1.20' }\n          - { goos: windows, goarch: amd64, goamd64: v3, output: amd64-v3-go120, goversion: '1.20' }\n\n          # Go 1.24 is the last release that will run on macOS 11 Big Sur. Go 1.25 will require macOS 12 Monterey or later.\n          - { goos: darwin, goarch: arm64, output: arm64-go124, goversion: '1.24' }\n          - { goos: darwin, goarch: amd64, goamd64: v1, output: amd64-v1-go124, goversion: '1.24' }\n          - { goos: darwin, goarch: amd64, goamd64: v2, output: amd64-v2-go124, goversion: '1.24' }\n          - { goos: darwin, goarch: amd64, goamd64: v3, output: amd64-v3-go124, goversion: '1.24' }\n\n          # Go 1.22 is the last release that will run on macOS 10.15 Catalina. Go 1.23 will require macOS 11 Big Sur or later.\n          - { goos: darwin, goarch: arm64, output: arm64-go122, goversion: '1.22' }\n          - { goos: darwin, goarch: amd64, goamd64: v1, output: amd64-v1-go122, goversion: '1.22' }\n          - { goos: darwin, goarch: amd64, goamd64: v2, output: amd64-v2-go122, goversion: '1.22' }\n          - { goos: darwin, goarch: amd64, goamd64: v3, output: amd64-v3-go122, goversion: '1.22' }\n\n          # Go 1.20 is the last release that will run on macOS 10.13 High Sierra or 10.14 Mojave. Go 1.21 will require macOS 10.15 Catalina or later.\n          - { goos: darwin, goarch: arm64, output: arm64-go120, goversion: '1.20' }\n          - { goos: darwin, goarch: amd64, goamd64: v1, output: amd64-v1-go120, goversion: '1.20' }\n          - { goos: darwin, goarch: amd64, goamd64: v2, output: amd64-v2-go120, goversion: '1.20' }\n          - { goos: darwin, goarch: amd64, goamd64: v3, output: amd64-v3-go120, goversion: '1.20' }\n\n          # Go 1.23 is the last release that requires Linux kernel version 2.6.32 or later. Go 1.24 will require Linux kernel version 3.2 or later.\n          - { goos: linux, goarch: '386', output: '386-go123', goversion: '1.23' }\n          - { goos: linux, goarch: amd64, goamd64: v1, output: amd64-v1-go123, goversion: '1.23', test: test }\n          - { goos: linux, goarch: amd64, goamd64: v2, output: amd64-v2-go123, goversion: '1.23' }\n          - { goos: linux, goarch: amd64, goamd64: v3, output: amd64-v3-go123, goversion: '1.23' }\n\n          # only for test\n          - { goos: linux, goarch: '386', output: '386-go120', goversion: '1.20' }\n          - { goos: linux, goarch: amd64, goamd64: v1, output: amd64-v1-go120, goversion: '1.20', test: test }\n          - { goos: linux, goarch: amd64, goamd64: v2, output: amd64-v2-go120, goversion: '1.20' }\n          - { goos: linux, goarch: amd64, goamd64: v3, output: amd64-v3-go120, goversion: '1.20' }\n\n    steps:\n    - uses: actions/checkout@v6\n\n    - name: Set up Go\n      if: ${{ matrix.jobs.goversion == '' }}\n      uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c\n      with:\n        go-download-base-url: 'https://github.com/MetaCubeX/go/releases/download/build'\n        go-version: '1.26'\n\n    - name: Set up Go\n      if: ${{ matrix.jobs.goversion != '' && matrix.jobs.goversion != 'custom' }}\n      uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c\n      with:\n        go-download-base-url: 'https://github.com/MetaCubeX/go/releases/download/build'\n        go-version: ${{ matrix.jobs.goversion }}\n\n    - name: Set up Go1.26 loongarch abi1\n      if: ${{ matrix.jobs.goarch == 'loong64' && matrix.jobs.abi == '1' }}\n      uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c\n      with:\n        go-download-base-url: 'https://github.com/MetaCubeX/loongarch64-golang/releases/download/1.26.0'\n        go-version: 1.26.0\n\n    - name: Verify Go installation\n      run: go version\n\n    - name: Verify Go env\n      run: go env\n\n      # TODO: remove after issue77930 fixed, see: https://github.com/golang/go/issues/77930\n    - name: Fix issue77930 for Golang1.26\n      if: ${{ matrix.jobs.goversion == '' }}\n      run: |\n        cd $(go env GOROOT)\n        patch --verbose -p 1 < $GITHUB_WORKSPACE/.github/patch/issue77930.patch\n\n    - name: Set variables\n      run: |\n        VERSION=\"${GITHUB_REF_NAME,,}-$(git rev-parse --short HEAD)\"\n        VERSION=\"${VERSION//\\//-}\"\n        PackageVersion=\"$(curl -s \"https://api.github.com/repos/MetaCubeX/mihomo/releases/latest\" | jq -r '.tag_name' | sed 's/v//g' | awk -F '.' '{$NF = $NF + 1; print}' OFS='.').${VERSION/-/.}\"\n        if [ -n \"${{ github.event.inputs.version }}\" ]; then\n          VERSION=${{ github.event.inputs.version }}\n          PackageVersion=\"${VERSION#v}\"\n        fi\n        echo \"VERSION=${VERSION}\" >> $GITHUB_ENV\n        echo \"PackageVersion=${PackageVersion}\" >> $GITHUB_ENV\n      \n        echo \"BUILDTIME=$(date)\" >> $GITHUB_ENV\n        echo \"CGO_ENABLED=0\" >> $GITHUB_ENV\n        echo \"BUILDTAG=-extldflags --static\" >> $GITHUB_ENV\n        echo \"GOTOOLCHAIN=local\" >> $GITHUB_ENV\n\n    - name: Setup NDK\n      if: ${{ matrix.jobs.goos == 'android' }}\n      uses: nttld/setup-ndk@v1\n      id: setup-ndk\n      with:\n        ndk-version: r29-beta1\n\n    - name: Set NDK path\n      if: ${{ matrix.jobs.goos == 'android' }}\n      run: |\n        echo \"CC=${{steps.setup-ndk.outputs.ndk-path}}/toolchains/llvm/prebuilt/linux-x86_64/bin/${{matrix.jobs.ndk}}-clang\" >> $GITHUB_ENV\n        echo \"CGO_ENABLED=1\" >> $GITHUB_ENV\n        echo \"BUILDTAG=\" >> $GITHUB_ENV\n\n    - name: Test\n      if: ${{ matrix.jobs.test == 'test' }}\n      run: |\n        export SKIP_CONCURRENT_TEST=1\n        go test ./...\n        echo \"---test with_gvisor---\"\n        go test ./... -tags \"with_gvisor\" -count=1\n\n    - name: Update CA\n      run: |\n        sudo apt-get update && sudo apt-get install ca-certificates\n        sudo update-ca-certificates\n        cp -f /etc/ssl/certs/ca-certificates.crt component/ca/ca-certificates.crt\n\n    - name: Build core\n      env:\n        GOOS: ${{matrix.jobs.goos}}\n        GOARCH: ${{matrix.jobs.goarch}}\n        GOAMD64: ${{matrix.jobs.goamd64}}\n        GO386: ${{matrix.jobs.go386}}\n        GOARM: ${{matrix.jobs.goarm}}\n        GOMIPS: ${{matrix.jobs.gomips}}\n      run: |\n        go env\n        go build -v -tags \"with_gvisor\" -trimpath -ldflags \"${BUILDTAG} -X 'github.com/metacubex/mihomo/constant.Version=${VERSION}' -X 'github.com/metacubex/mihomo/constant.BuildTime=${BUILDTIME}' -w -s -buildid=\"\n        if [ \"${{matrix.jobs.goos}}\" = \"windows\" ]; then\n          cp mihomo.exe mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}.exe\n          zip -r mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.zip mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}.exe\n        else\n          cp mihomo mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}\n          gzip -c mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}} > mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.gz\n          rm mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}\n        fi\n\n    - name: Package DEB\n      if: matrix.jobs.debian != ''\n      run: |\n        set -xeuo pipefail\n        sudo gem install fpm\n        cp .github/release/.fpm_systemd .fpm\n\n        fpm -t deb \\\n          -v \"${PackageVersion}\" \\\n          -p \"mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.deb\" \\\n          --architecture ${{ matrix.jobs.debian }} \\\n          mihomo=/usr/bin/mihomo\n\n    - name: Package RPM\n      if: matrix.jobs.rpm != ''\n      run: |\n        set -xeuo pipefail\n        sudo gem install fpm\n        cp .github/release/.fpm_systemd .fpm\n\n        fpm -t rpm \\\n          -v \"${PackageVersion}\" \\\n          -p \"mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.rpm\" \\\n          --architecture ${{ matrix.jobs.rpm }} \\\n          mihomo=/usr/bin/mihomo\n\n    - name: Package Pacman\n      if: matrix.jobs.pacman != ''\n      run: |\n        set -xeuo pipefail\n        sudo gem install fpm\n        sudo apt-get update && sudo apt-get install -y libarchive-tools\n        cp .github/release/.fpm_systemd .fpm\n\n        fpm -t pacman \\\n          -v \"${PackageVersion}\" \\\n          -p \"mihomo-${{matrix.jobs.goos}}-${{matrix.jobs.output}}-${VERSION}.pkg.tar.zst\" \\\n          --architecture ${{ matrix.jobs.pacman }} \\\n          mihomo=/usr/bin/mihomo\n\n    - name: Pack Golang Toolchain\n      if: ${{ matrix.jobs.goversion == '' && matrix.jobs.tarball == 'tarball' }}\n      run: |\n        mkdir -p $GITHUB_WORKSPACE/toolchain/go\n        cp -r $(go env GOROOT)/* $GITHUB_WORKSPACE/toolchain/go/\n        cd $GITHUB_WORKSPACE/toolchain/\n        tar -czf $GITHUB_WORKSPACE/toolchain.tar.gz .\n        rm -rf $GITHUB_WORKSPACE/toolchain\n\n    - name: Pack GoMod Vendor\n      if: ${{ matrix.jobs.goversion == '' && matrix.jobs.tarball == 'tarball' }}\n      run: |\n        go mod vendor\n        tar -czf $GITHUB_WORKSPACE/vendor.tar.gz vendor\n\n    - name: Save version\n      run: |\n        echo ${VERSION} > version.txt\n      shell: bash\n\n    - name: Archive production artifacts\n      uses: actions/upload-artifact@v7\n      with:\n        name: \"${{ matrix.jobs.goos }}-${{ matrix.jobs.output }}\"\n        path: |\n          mihomo*.gz\n          mihomo*.deb\n          mihomo*.rpm\n          mihomo*.pkg.tar.zst\n          mihomo*.zip\n          toolchain.tar.gz\n          vendor.tar.gz\n          version.txt\n          checksums.txt\n\n  Upload-Prerelease:\n    permissions: write-all\n    if: ${{ github.event_name != 'workflow_dispatch' && github.ref_type == 'branch' && !startsWith(github.event_name, 'pull_request') }}\n    needs: [build]\n    runs-on: ubuntu-latest\n    steps:\n    - name: Download all workflow run artifacts\n      uses: actions/download-artifact@v8\n      with:\n        path: bin/\n        merge-multiple: true\n\n    - name: Calculate checksums\n      run: |\n        cd bin/\n        find . -type f -not -name \"checksums.*\" -not -name \"version.txt\" | sort | xargs sha256sum > checksums.txt\n        cat checksums.txt\n      shell: bash\n\n    - name: Delete current release assets\n      uses: 8Mi-Tech/delete-release-assets-action@main\n      with:\n        github_token: ${{ secrets.GITHUB_TOKEN }}\n        tag: Prerelease-${{ github.ref_name }}\n        deleteOnlyFromDrafts: false\n    - name: Set Env\n      run: |\n        echo \"BUILDTIME=$(TZ=Asia/Shanghai date)\" >> $GITHUB_ENV\n      shell: bash\n\n    - name: Tag Repo\n      uses: richardsimko/update-tag@v1\n      with:\n        tag_name: Prerelease-${{ github.ref_name }}\n      env:\n        GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n      \n    - run: |\n        cat > release.txt << 'EOF'\n        Release created at  ${{ env.BUILDTIME }}\n        Synchronize ${{ github.ref_name }} branch code updates, keeping only the latest version\n        <br>\n        [我应该下载哪个文件? / Which file should I download?](https://github.com/MetaCubeX/mihomo/wiki/FAQ)\n        [二进制文件筛选 / Binary file selector](https://metacubex.github.io/Meta-Docs/startup/#_1)\n        [查看文档 / Docs](https://metacubex.github.io/Meta-Docs/)\n        EOF\n\n    - name: Upload Prerelease\n      uses: softprops/action-gh-release@v2\n      if: ${{  success() }}\n      with:\n        tag_name: Prerelease-${{ github.ref_name }}\n        files: |\n          bin/*\n        prerelease: true\n        generate_release_notes: true\n        body_path: release.txt\n\n  Upload-Release:\n    permissions: write-all\n    if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version != '' }}\n    needs: [build]\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v6\n        with:\n          ref: Meta\n          fetch-depth: '0'\n          fetch-tags: 'true'\n\n      - name: Get tags\n        run: |\n          echo \"CURRENTVERSION=${{ github.event.inputs.version }}\" >> $GITHUB_ENV\n          git fetch --tags\n          echo \"PREVERSION=$(git describe --tags --abbrev=0 HEAD)\" >> $GITHUB_ENV\n\n      - name: Force push Alpha branch to Meta\n        run: |\n          git config --global user.email \"github-actions[bot]@users.noreply.github.com\"\n          git config --global user.name \"github-actions[bot]\"\n          git fetch origin Alpha:Alpha\n          git push origin Alpha:Meta --force\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Tag the commit on Alpha\n        run: |\n          git checkout Alpha \n          git tag ${{ github.event.inputs.version }}\n          git push origin ${{ github.event.inputs.version }}\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n\n      - name: Generate release notes\n        run: |\n            cp ./.github/genReleaseNote.sh ./\n            bash ./genReleaseNote.sh -v ${PREVERSION}...${CURRENTVERSION}\n            rm ./genReleaseNote.sh\n  \n      - uses: actions/download-artifact@v8\n        with:\n          path: bin/\n          merge-multiple: true\n  \n      - name: Display structure of downloaded files\n        run: ls -R\n        working-directory: bin\n  \n      - name: Upload Release\n        uses: softprops/action-gh-release@v2\n        if: ${{ success() }}\n        with:\n          tag_name: ${{ github.event.inputs.version }}\n          files: bin/*\n          body_path: release.md\n\n  Docker:\n    if: ${{ !startsWith(github.event_name, 'pull_request') }}\n    permissions: write-all\n    needs: [build]\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout repository\n        uses: actions/checkout@v6\n        with:\n          fetch-depth: 0\n\n      - uses: actions/download-artifact@v8\n        with:\n          path: bin/\n          merge-multiple: true\n\n      - name: Display structure of downloaded files\n        run: ls -R\n        working-directory: bin\n\n      - name: Set up QEMU\n        uses: docker/setup-qemu-action@v4\n\n      - name: Setup Docker buildx\n        uses: docker/setup-buildx-action@v4\n        with:\n          version: latest\n      \n      # Extract metadata (tags, labels) for Docker\n      # https://github.com/docker/metadata-action\n      - name: Extract Docker metadata\n        if: ${{ github.event_name != 'workflow_dispatch' }}\n        id: meta_alpha\n        uses: docker/metadata-action@v6\n        with:\n          images: '${{ env.REGISTRY }}/${{ github.repository }}'\n      \n      # Extract metadata (tags, labels) for Docker\n      # https://github.com/docker/metadata-action\n      - name: Extract Docker metadata\n        if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version != '' }}\n        id: meta_release\n        uses: docker/metadata-action@v6\n        with:\n          images: '${{ env.REGISTRY }}/${{ github.repository }}'\n          tags: |\n            ${{ github.event.inputs.version }}\n          flavor: |\n            latest=true\n          labels: org.opencontainers.image.version=${{ github.event.inputs.version }}\n      \n      - name: Show files\n        run: |\n          ls .\n          ls bin/\n      \n      - name: login to docker REGISTRY\n        uses: docker/login-action@v4\n        with:\n          registry: ${{ env.REGISTRY }}\n          username: ${{ secrets.DOCKER_HUB_USER }}\n          password: ${{ secrets.DOCKER_HUB_TOKEN }}\n\n      # Build and push Docker image with Buildx (don't push on PR)\n      # https://github.com/docker/build-push-action\n      - name: Build and push Docker image\n        if: ${{ github.event_name != 'workflow_dispatch' }}\n        uses: docker/build-push-action@v7\n        with:\n          context: .\n          file: ./Dockerfile\n          push: ${{ github.event_name != 'pull_request' }}\n          platforms: |\n            linux/386\n            linux/amd64\n            linux/arm64\n            linux/arm/v7\n          tags: ${{ steps.meta_alpha.outputs.tags }}\n          labels: ${{ steps.meta_alpha.outputs.labels }}\n      \n      - name: Build and push Docker image\n        if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.version != '' }}\n        uses: docker/build-push-action@v7\n        with:\n          context: .\n          file: ./Dockerfile\n          push: ${{ github.event_name != 'pull_request' }}\n          platforms: |\n            linux/386\n            linux/amd64\n            linux/arm64\n            linux/arm/v7\n          tags: ${{ steps.meta_release.outputs.tags }}\n          labels: ${{ steps.meta_release.outputs.labels }}"
  },
  {
    "path": "core/Clash.Meta/.github/workflows/test.yml",
    "content": "name: Test\non:\n  push:\n    paths-ignore:\n      - \"docs/**\"\n      - \"README.md\"\n      - \".github/ISSUE_TEMPLATE/**\"\n    branches:\n      - Alpha\n    tags:\n      - \"v*\"\n  pull_request:\n    branches:\n      - Alpha\n\njobs:\n  test:\n    strategy:\n      matrix:\n        os:\n          - 'ubuntu-latest' # amd64 linux\n          - 'windows-latest' # amd64 windows\n          - 'macos-latest' # arm64 macos\n          - 'ubuntu-24.04-arm' # arm64 linux\n          - 'windows-11-arm' # arm64 windows\n          - 'macos-15-intel' # amd64 macos\n        go-version:\n          - '1.26'\n          - '1.25'\n          - '1.24'\n          - '1.23'\n          - '1.22'\n          - '1.21'\n          - '1.20'\n      fail-fast: false\n    runs-on: ${{ matrix.os }}\n    defaults:\n      run:\n        shell: bash\n    env:\n      CGO_ENABLED: 0\n      GOTOOLCHAIN: local\n      # Fix mingw trying to be smart and converting paths https://github.com/moby/moby/issues/24029#issuecomment-250412919\n      MSYS_NO_PATHCONV: true\n    steps:\n      - uses: actions/checkout@v6\n\n      - name: Set up Go\n        uses: actions/setup-go@4a3601121dd01d1626a1e23e37211e3254c1c06c\n        with:\n          go-download-base-url: 'https://github.com/MetaCubeX/go/releases/download/build'\n          go-version: ${{ matrix.go-version }}\n\n      - name: Verify Go installation\n        run: go version\n\n      - name: Verify Go env\n        run: go env\n\n      - name: Remove inbound test for macOS\n        if: ${{ runner.os == 'macOS' }}\n        run: |\n          rm -rf listener/inbound/*_test.go\n\n      - name: Test\n        run: go test ./... -v -count=1\n\n      - name: Test with tag with_gvisor\n        run: go test ./... -v -count=1 -tags \"with_gvisor\""
  },
  {
    "path": "core/Clash.Meta/.github/workflows/trigger-cmfa-update.yml",
    "content": "name: Trigger CMFA Update\non:\n  workflow_dispatch:\n  push:\n    paths-ignore:\n      - \"docs/**\"\n      - \"README.md\"\n      - \".github/ISSUE_TEMPLATE/**\"\n    branches:\n      - Alpha\n    tags:\n      - \"v*\"\n      \njobs:\n  # Send \"core-updated\" to MetaCubeX/ClashMetaForAndroid to trigger update-dependencies\n  trigger-CMFA-update:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/create-github-app-token@v3\n        id: generate-token\n        with:\n          app-id: ${{ secrets.MAINTAINER_APPID }}\n          private-key: ${{ secrets.MAINTAINER_APP_PRIVATE_KEY }}\n          owner: ${{ github.repository_owner }}\n      \n      - name: Trigger update-dependencies\n        run: |\n          curl -X POST https://api.github.com/repos/MetaCubeX/ClashMetaForAndroid/dispatches \\\n            -H \"Accept: application/vnd.github.everest-preview+json\" \\\n            -H \"Authorization: token ${{ steps.generate-token.outputs.token }}\" \\\n            -d '{\"event_type\": \"core-updated\"}'\n"
  },
  {
    "path": "core/Clash.Meta/.gitignore",
    "content": "# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\nbin/*\n\n# Test binary, build with `go test -c`\n*.test\n\n# Output of the go coverage tool, specifically when used with LiteIDE\n*.out\n\n# go mod vendor\nvendor\n\n# GoLand\n.idea/*\n\n# macOS file\n.DS_Store\n\n# test suite\ntest/config/cache*\n/output\n.vscode/\n.fleet/"
  },
  {
    "path": "core/Clash.Meta/.golangci.yaml",
    "content": "linters:\n  disable-all: true\n  enable:\n    - gofumpt\n    - staticcheck\n    - govet\n    - gci\n\nlinters-settings:\n  gci:\n    custom-order: true\n    sections:\n      - standard\n      - prefix(github.com/metacubex/mihomo)\n      - default\n  staticcheck:\n    go: '1.19'\n"
  },
  {
    "path": "core/Clash.Meta/Dockerfile",
    "content": "FROM alpine:latest as builder\nARG TARGETPLATFORM\nRUN echo \"I'm building for $TARGETPLATFORM\"\n\nRUN apk add --no-cache gzip && \\\n    mkdir /mihomo-config && \\\n    wget -O /mihomo-config/geoip.metadb https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb && \\\n    wget -O /mihomo-config/geosite.dat https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat && \\\n    wget -O /mihomo-config/geoip.dat https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat\n\nCOPY docker/file-name.sh /mihomo/file-name.sh\nWORKDIR /mihomo\nCOPY bin/ bin/\nRUN FILE_NAME=`sh file-name.sh` && echo $FILE_NAME && \\\n    FILE_NAME=`ls bin/ | egrep \"$FILE_NAME.gz\"|awk NR==1` && echo $FILE_NAME && \\\n    mv bin/$FILE_NAME mihomo.gz && gzip -d mihomo.gz && chmod +x mihomo && echo \"$FILE_NAME\" > /mihomo-config/test\nFROM alpine:latest\nLABEL org.opencontainers.image.source=\"https://github.com/MetaCubeX/mihomo\"\n\nRUN apk add --no-cache ca-certificates tzdata iptables\n\nVOLUME [\"/root/.config/mihomo/\"]\n\nCOPY --from=builder /mihomo-config/ /root/.config/mihomo/\nCOPY --from=builder /mihomo/mihomo /mihomo\nENTRYPOINT [ \"/mihomo\" ]\n"
  },
  {
    "path": "core/Clash.Meta/LICENSE",
    "content": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>\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 GNU General Public License is a free, copyleft license for\nsoftware and other kinds of works.\n\n  The licenses for most software and other practical works are designed\nto take away your freedom to share and change the works.  By contrast,\nthe GNU General Public License is intended to guarantee your freedom to\nshare and change all versions of a program--to make sure it remains free\nsoftware for all its users.  We, the Free Software Foundation, use the\nGNU General Public License for most of our software; it applies also to\nany other work released this way by its authors.  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\nthem if you wish), that you receive source code or can get it if you\nwant it, that you can change the software or use pieces of it in new\nfree programs, and that you know you can do these things.\n\n  To protect your rights, we need to prevent others from denying you\nthese rights or asking you to surrender the rights.  Therefore, you have\ncertain responsibilities if you distribute copies of the software, or if\nyou modify it: responsibilities to respect the freedom of others.\n\n  For example, if you distribute copies of such a program, whether\ngratis or for a fee, you must pass on to the recipients the same\nfreedoms that you received.  You must make sure that they, too, receive\nor can get the source code.  And you must show them these terms so they\nknow their rights.\n\n  Developers that use the GNU GPL protect your rights with two steps:\n(1) assert copyright on the software, and (2) offer you this License\ngiving you legal permission to copy, distribute and/or modify it.\n\n  For the developers' and authors' protection, the GPL clearly explains\nthat there is no warranty for this free software.  For both users' and\nauthors' sake, the GPL requires that modified versions be marked as\nchanged, so that their problems will not be attributed erroneously to\nauthors of previous versions.\n\n  Some devices are designed to deny users access to install or run\nmodified versions of the software inside them, although the manufacturer\ncan do so.  This is fundamentally incompatible with the aim of\nprotecting users' freedom to change the software.  The systematic\npattern of such abuse occurs in the area of products for individuals to\nuse, which is precisely where it is most unacceptable.  Therefore, we\nhave designed this version of the GPL to prohibit the practice for those\nproducts.  If such problems arise substantially in other domains, we\nstand ready to extend this provision to those domains in future versions\nof the GPL, as needed to protect the freedom of users.\n\n  Finally, every program is threatened constantly by software patents.\nStates should not allow patents to restrict development and use of\nsoftware on general-purpose computers, but in those that do, we wish to\navoid the special danger that patents applied to a free program could\nmake it effectively proprietary.  To prevent this, the GPL assures that\npatents cannot be used to render the program non-free.\n\n  The precise terms and conditions for copying, distribution and\nmodification follow.\n\n                       TERMS AND CONDITIONS\n\n  0. Definitions.\n\n  \"This License\" refers to version 3 of the GNU General Public License.\n\n  \"Copyright\" also means copyright-like laws that apply to other kinds of\nworks, such as semiconductor masks.\n\n  \"The Program\" refers to any copyrightable work licensed under this\nLicense.  Each licensee is addressed as \"you\".  \"Licensees\" and\n\"recipients\" may be individuals or organizations.\n\n  To \"modify\" a work means to copy from or adapt all or part of the work\nin a fashion requiring copyright permission, other than the making of an\nexact copy.  The resulting work is called a \"modified version\" of the\nearlier work or a work \"based on\" the earlier work.\n\n  A \"covered work\" means either the unmodified Program or a work based\non the Program.\n\n  To \"propagate\" a work means to do anything with it that, without\npermission, would make you directly or secondarily liable for\ninfringement under applicable copyright law, except executing it on a\ncomputer or modifying a private copy.  Propagation includes copying,\ndistribution (with or without modification), making available to the\npublic, and in some countries other activities as well.\n\n  To \"convey\" a work means any kind of propagation that enables other\nparties to make or receive copies.  Mere interaction with a user through\na computer network, with no transfer of a copy, is not conveying.\n\n  An interactive user interface displays \"Appropriate Legal Notices\"\nto the extent that it includes a convenient and prominently visible\nfeature that (1) displays an appropriate copyright notice, and (2)\ntells the user that there is no warranty for the work (except to the\nextent that warranties are provided), that licensees may convey the\nwork under this License, and how to view a copy of this License.  If\nthe interface presents a list of user commands or options, such as a\nmenu, a prominent item in the list meets this criterion.\n\n  1. Source Code.\n\n  The \"source code\" for a work means the preferred form of the work\nfor making modifications to it.  \"Object code\" means any non-source\nform of a work.\n\n  A \"Standard Interface\" means an interface that either is an official\nstandard defined by a recognized standards body, or, in the case of\ninterfaces specified for a particular programming language, one that\nis widely used among developers working in that language.\n\n  The \"System Libraries\" of an executable work include anything, other\nthan the work as a whole, that (a) is included in the normal form of\npackaging a Major Component, but which is not part of that Major\nComponent, and (b) serves only to enable use of the work with that\nMajor Component, or to implement a Standard Interface for which an\nimplementation is available to the public in source code form.  A\n\"Major Component\", in this context, means a major essential component\n(kernel, window system, and so on) of the specific operating system\n(if any) on which the executable work runs, or a compiler used to\nproduce the work, or an object code interpreter used to run it.\n\n  The \"Corresponding Source\" for a work in object code form means all\nthe source code needed to generate, install, and (for an executable\nwork) run the object code and to modify the work, including scripts to\ncontrol those activities.  However, it does not include the work's\nSystem Libraries, or general-purpose tools or generally available free\nprograms which are used unmodified in performing those activities but\nwhich are not part of the work.  For example, Corresponding Source\nincludes interface definition files associated with source files for\nthe work, and the source code for shared libraries and dynamically\nlinked subprograms that the work is specifically designed to require,\nsuch as by intimate data communication or control flow between those\nsubprograms and other parts of the work.\n\n  The Corresponding Source need not include anything that users\ncan regenerate automatically from other parts of the Corresponding\nSource.\n\n  The Corresponding Source for a work in source code form is that\nsame work.\n\n  2. Basic Permissions.\n\n  All rights granted under this License are granted for the term of\ncopyright on the Program, and are irrevocable provided the stated\nconditions are met.  This License explicitly affirms your unlimited\npermission to run the unmodified Program.  The output from running a\ncovered work is covered by this License only if the output, given its\ncontent, constitutes a covered work.  This License acknowledges your\nrights of fair use or other equivalent, as provided by copyright law.\n\n  You may make, run and propagate covered works that you do not\nconvey, without conditions so long as your license otherwise remains\nin force.  You may convey covered works to others for the sole purpose\nof having them make modifications exclusively for you, or provide you\nwith facilities for running those works, provided that you comply with\nthe terms of this License in conveying all material for which you do\nnot control copyright.  Those thus making or running the covered works\nfor you must do so exclusively on your behalf, under your direction\nand control, on terms that prohibit them from making any copies of\nyour copyrighted material outside their relationship with you.\n\n  Conveying under any other circumstances is permitted solely under\nthe conditions stated below.  Sublicensing is not allowed; section 10\nmakes it unnecessary.\n\n  3. Protecting Users' Legal Rights From Anti-Circumvention Law.\n\n  No covered work shall be deemed part of an effective technological\nmeasure under any applicable law fulfilling obligations under article\n11 of the WIPO copyright treaty adopted on 20 December 1996, or\nsimilar laws prohibiting or restricting circumvention of such\nmeasures.\n\n  When you convey a covered work, you waive any legal power to forbid\ncircumvention of technological measures to the extent such circumvention\nis effected by exercising rights under this License with respect to\nthe covered work, and you disclaim any intention to limit operation or\nmodification of the work as a means of enforcing, against the work's\nusers, your or third parties' legal rights to forbid circumvention of\ntechnological measures.\n\n  4. Conveying Verbatim Copies.\n\n  You may convey verbatim copies of the Program's source code as you\nreceive it, in any medium, provided that you conspicuously and\nappropriately publish on each copy an appropriate copyright notice;\nkeep intact all notices stating that this License and any\nnon-permissive terms added in accord with section 7 apply to the code;\nkeep intact all notices of the absence of any warranty; and give all\nrecipients a copy of this License along with the Program.\n\n  You may charge any price or no price for each copy that you convey,\nand you may offer support or warranty protection for a fee.\n\n  5. Conveying Modified Source Versions.\n\n  You may convey a work based on the Program, or the modifications to\nproduce it from the Program, in the form of source code under the\nterms of section 4, provided that you also meet all of these conditions:\n\n    a) The work must carry prominent notices stating that you modified\n    it, and giving a relevant date.\n\n    b) The work must carry prominent notices stating that it is\n    released under this License and any conditions added under section\n    7.  This requirement modifies the requirement in section 4 to\n    \"keep intact all notices\".\n\n    c) You must license the entire work, as a whole, under this\n    License to anyone who comes into possession of a copy.  This\n    License will therefore apply, along with any applicable section 7\n    additional terms, to the whole of the work, and all its parts,\n    regardless of how they are packaged.  This License gives no\n    permission to license the work in any other way, but it does not\n    invalidate such permission if you have separately received it.\n\n    d) If the work has interactive user interfaces, each must display\n    Appropriate Legal Notices; however, if the Program has interactive\n    interfaces that do not display Appropriate Legal Notices, your\n    work need not make them do so.\n\n  A compilation of a covered work with other separate and independent\nworks, which are not by their nature extensions of the covered work,\nand which are not combined with it such as to form a larger program,\nin or on a volume of a storage or distribution medium, is called an\n\"aggregate\" if the compilation and its resulting copyright are not\nused to limit the access or legal rights of the compilation's users\nbeyond what the individual works permit.  Inclusion of a covered work\nin an aggregate does not cause this License to apply to the other\nparts of the aggregate.\n\n  6. Conveying Non-Source Forms.\n\n  You may convey a covered work in object code form under the terms\nof sections 4 and 5, provided that you also convey the\nmachine-readable Corresponding Source under the terms of this License,\nin one of these ways:\n\n    a) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by the\n    Corresponding Source fixed on a durable physical medium\n    customarily used for software interchange.\n\n    b) Convey the object code in, or embodied in, a physical product\n    (including a physical distribution medium), accompanied by a\n    written offer, valid for at least three years and valid for as\n    long as you offer spare parts or customer support for that product\n    model, to give anyone who possesses the object code either (1) a\n    copy of the Corresponding Source for all the software in the\n    product that is covered by this License, on a durable physical\n    medium customarily used for software interchange, for a price no\n    more than your reasonable cost of physically performing this\n    conveying of source, or (2) access to copy the\n    Corresponding Source from a network server at no charge.\n\n    c) Convey individual copies of the object code with a copy of the\n    written offer to provide the Corresponding Source.  This\n    alternative is allowed only occasionally and noncommercially, and\n    only if you received the object code with such an offer, in accord\n    with subsection 6b.\n\n    d) Convey the object code by offering access from a designated\n    place (gratis or for a charge), and offer equivalent access to the\n    Corresponding Source in the same way through the same place at no\n    further charge.  You need not require recipients to copy the\n    Corresponding Source along with the object code.  If the place to\n    copy the object code is a network server, the Corresponding Source\n    may be on a different server (operated by you or a third party)\n    that supports equivalent copying facilities, provided you maintain\n    clear directions next to the object code saying where to find the\n    Corresponding Source.  Regardless of what server hosts the\n    Corresponding Source, you remain obligated to ensure that it is\n    available for as long as needed to satisfy these requirements.\n\n    e) Convey the object code using peer-to-peer transmission, provided\n    you inform other peers where the object code and Corresponding\n    Source of the work are being offered to the general public at no\n    charge under subsection 6d.\n\n  A separable portion of the object code, whose source code is excluded\nfrom the Corresponding Source as a System Library, need not be\nincluded in conveying the object code work.\n\n  A \"User Product\" is either (1) a \"consumer product\", which means any\ntangible personal property which is normally used for personal, family,\nor household purposes, or (2) anything designed or sold for incorporation\ninto a dwelling.  In determining whether a product is a consumer product,\ndoubtful cases shall be resolved in favor of coverage.  For a particular\nproduct received by a particular user, \"normally used\" refers to a\ntypical or common use of that class of product, regardless of the status\nof the particular user or of the way in which the particular user\nactually uses, or expects or is expected to use, the product.  A product\nis a consumer product regardless of whether the product has substantial\ncommercial, industrial or non-consumer uses, unless such uses represent\nthe only significant mode of use of the product.\n\n  \"Installation Information\" for a User Product means any methods,\nprocedures, authorization keys, or other information required to install\nand execute modified versions of a covered work in that User Product from\na modified version of its Corresponding Source.  The information must\nsuffice to ensure that the continued functioning of the modified object\ncode is in no case prevented or interfered with solely because\nmodification has been made.\n\n  If you convey an object code work under this section in, or with, or\nspecifically for use in, a User Product, and the conveying occurs as\npart of a transaction in which the right of possession and use of the\nUser Product is transferred to the recipient in perpetuity or for a\nfixed term (regardless of how the transaction is characterized), the\nCorresponding Source conveyed under this section must be accompanied\nby the Installation Information.  But this requirement does not apply\nif neither you nor any third party retains the ability to install\nmodified object code on the User Product (for example, the work has\nbeen installed in ROM).\n\n  The requirement to provide Installation Information does not include a\nrequirement to continue to provide support service, warranty, or updates\nfor a work that has been modified or installed by the recipient, or for\nthe User Product in which it has been modified or installed.  Access to a\nnetwork may be denied when the modification itself materially and\nadversely affects the operation of the network or violates the rules and\nprotocols for communication across the network.\n\n  Corresponding Source conveyed, and Installation Information provided,\nin accord with this section must be in a format that is publicly\ndocumented (and with an implementation available to the public in\nsource code form), and must require no special password or key for\nunpacking, reading or copying.\n\n  7. Additional Terms.\n\n  \"Additional permissions\" are terms that supplement the terms of this\nLicense by making exceptions from one or more of its conditions.\nAdditional permissions that are applicable to the entire Program shall\nbe treated as though they were included in this License, to the extent\nthat they are valid under applicable law.  If additional permissions\napply only to part of the Program, that part may be used separately\nunder those permissions, but the entire Program remains governed by\nthis License without regard to the additional permissions.\n\n  When you convey a copy of a covered work, you may at your option\nremove any additional permissions from that copy, or from any part of\nit.  (Additional permissions may be written to require their own\nremoval in certain cases when you modify the work.)  You may place\nadditional permissions on material, added by you to a covered work,\nfor which you have or can give appropriate copyright permission.\n\n  Notwithstanding any other provision of this License, for material you\nadd to a covered work, you may (if authorized by the copyright holders of\nthat material) supplement the terms of this License with terms:\n\n    a) Disclaiming warranty or limiting liability differently from the\n    terms of sections 15 and 16 of this License; or\n\n    b) Requiring preservation of specified reasonable legal notices or\n    author attributions in that material or in the Appropriate Legal\n    Notices displayed by works containing it; or\n\n    c) Prohibiting misrepresentation of the origin of that material, or\n    requiring that modified versions of such material be marked in\n    reasonable ways as different from the original version; or\n\n    d) Limiting the use for publicity purposes of names of licensors or\n    authors of the material; or\n\n    e) Declining to grant rights under trademark law for use of some\n    trade names, trademarks, or service marks; or\n\n    f) Requiring indemnification of licensors and authors of that\n    material by anyone who conveys the material (or modified versions of\n    it) with contractual assumptions of liability to the recipient, for\n    any liability that these contractual assumptions directly impose on\n    those licensors and authors.\n\n  All other non-permissive additional terms are considered \"further\nrestrictions\" within the meaning of section 10.  If the Program as you\nreceived it, or any part of it, contains a notice stating that it is\ngoverned by this License along with a term that is a further\nrestriction, you may remove that term.  If a license document contains\na further restriction but permits relicensing or conveying under this\nLicense, you may add to a covered work material governed by the terms\nof that license document, provided that the further restriction does\nnot survive such relicensing or conveying.\n\n  If you add terms to a covered work in accord with this section, you\nmust place, in the relevant source files, a statement of the\nadditional terms that apply to those files, or a notice indicating\nwhere to find the applicable terms.\n\n  Additional terms, permissive or non-permissive, may be stated in the\nform of a separately written license, or stated as exceptions;\nthe above requirements apply either way.\n\n  8. Termination.\n\n  You may not propagate or modify a covered work except as expressly\nprovided under this License.  Any attempt otherwise to propagate or\nmodify it is void, and will automatically terminate your rights under\nthis License (including any patent licenses granted under the third\nparagraph of section 11).\n\n  However, if you cease all violation of this License, then your\nlicense from a particular copyright holder is reinstated (a)\nprovisionally, unless and until the copyright holder explicitly and\nfinally terminates your license, and (b) permanently, if the copyright\nholder fails to notify you of the violation by some reasonable means\nprior to 60 days after the cessation.\n\n  Moreover, your license from a particular copyright holder is\nreinstated permanently if the copyright holder notifies you of the\nviolation by some reasonable means, this is the first time you have\nreceived notice of violation of this License (for any work) from that\ncopyright holder, and you cure the violation prior to 30 days after\nyour receipt of the notice.\n\n  Termination of your rights under this section does not terminate the\nlicenses of parties who have received copies or rights from you under\nthis License.  If your rights have been terminated and not permanently\nreinstated, you do not qualify to receive new licenses for the same\nmaterial under section 10.\n\n  9. Acceptance Not Required for Having Copies.\n\n  You are not required to accept this License in order to receive or\nrun a copy of the Program.  Ancillary propagation of a covered work\noccurring solely as a consequence of using peer-to-peer transmission\nto receive a copy likewise does not require acceptance.  However,\nnothing other than this License grants you permission to propagate or\nmodify any covered work.  These actions infringe copyright if you do\nnot accept this License.  Therefore, by modifying or propagating a\ncovered work, you indicate your acceptance of this License to do so.\n\n  10. Automatic Licensing of Downstream Recipients.\n\n  Each time you convey a covered work, the recipient automatically\nreceives a license from the original licensors, to run, modify and\npropagate that work, subject to this License.  You are not responsible\nfor enforcing compliance by third parties with this License.\n\n  An \"entity transaction\" is a transaction transferring control of an\norganization, or substantially all assets of one, or subdividing an\norganization, or merging organizations.  If propagation of a covered\nwork results from an entity transaction, each party to that\ntransaction who receives a copy of the work also receives whatever\nlicenses to the work the party's predecessor in interest had or could\ngive under the previous paragraph, plus a right to possession of the\nCorresponding Source of the work from the predecessor in interest, if\nthe predecessor has it or can get it with reasonable efforts.\n\n  You may not impose any further restrictions on the exercise of the\nrights granted or affirmed under this License.  For example, you may\nnot impose a license fee, royalty, or other charge for exercise of\nrights granted under this License, and you may not initiate litigation\n(including a cross-claim or counterclaim in a lawsuit) alleging that\nany patent claim is infringed by making, using, selling, offering for\nsale, or importing the Program or any portion of it.\n\n  11. Patents.\n\n  A \"contributor\" is a copyright holder who authorizes use under this\nLicense of the Program or a work on which the Program is based.  The\nwork thus licensed is called the contributor's \"contributor version\".\n\n  A contributor's \"essential patent claims\" are all patent claims\nowned or controlled by the contributor, whether already acquired or\nhereafter acquired, that would be infringed by some manner, permitted\nby this License, of making, using, or selling its contributor version,\nbut do not include claims that would be infringed only as a\nconsequence of further modification of the contributor version.  For\npurposes of this definition, \"control\" includes the right to grant\npatent sublicenses in a manner consistent with the requirements of\nthis License.\n\n  Each contributor grants you a non-exclusive, worldwide, royalty-free\npatent license under the contributor's essential patent claims, to\nmake, use, sell, offer for sale, import and otherwise run, modify and\npropagate the contents of its contributor version.\n\n  In the following three paragraphs, a \"patent license\" is any express\nagreement or commitment, however denominated, not to enforce a patent\n(such as an express permission to practice a patent or covenant not to\nsue for patent infringement).  To \"grant\" such a patent license to a\nparty means to make such an agreement or commitment not to enforce a\npatent against the party.\n\n  If you convey a covered work, knowingly relying on a patent license,\nand the Corresponding Source of the work is not available for anyone\nto copy, free of charge and under the terms of this License, through a\npublicly available network server or other readily accessible means,\nthen you must either (1) cause the Corresponding Source to be so\navailable, or (2) arrange to deprive yourself of the benefit of the\npatent license for this particular work, or (3) arrange, in a manner\nconsistent with the requirements of this License, to extend the patent\nlicense to downstream recipients.  \"Knowingly relying\" means you have\nactual knowledge that, but for the patent license, your conveying the\ncovered work in a country, or your recipient's use of the covered work\nin a country, would infringe one or more identifiable patents in that\ncountry that you have reason to believe are valid.\n\n  If, pursuant to or in connection with a single transaction or\narrangement, you convey, or propagate by procuring conveyance of, a\ncovered work, and grant a patent license to some of the parties\nreceiving the covered work authorizing them to use, propagate, modify\nor convey a specific copy of the covered work, then the patent license\nyou grant is automatically extended to all recipients of the covered\nwork and works based on it.\n\n  A patent license is \"discriminatory\" if it does not include within\nthe scope of its coverage, prohibits the exercise of, or is\nconditioned on the non-exercise of one or more of the rights that are\nspecifically granted under this License.  You may not convey a covered\nwork if you are a party to an arrangement with a third party that is\nin the business of distributing software, under which you make payment\nto the third party based on the extent of your activity of conveying\nthe work, and under which the third party grants, to any of the\nparties who would receive the covered work from you, a discriminatory\npatent license (a) in connection with copies of the covered work\nconveyed by you (or copies made from those copies), or (b) primarily\nfor and in connection with specific products or compilations that\ncontain the covered work, unless you entered into that arrangement,\nor that patent license was granted, prior to 28 March 2007.\n\n  Nothing in this License shall be construed as excluding or limiting\nany implied license or other defenses to infringement that may\notherwise be available to you under applicable patent law.\n\n  12. No Surrender of Others' Freedom.\n\n  If conditions 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 convey a\ncovered work so as to satisfy simultaneously your obligations under this\nLicense and any other pertinent obligations, then as a consequence you may\nnot convey it at all.  For example, if you agree to terms that obligate you\nto collect a royalty for further conveying from those to whom you convey\nthe Program, the only way you could satisfy both those terms and this\nLicense would be to refrain entirely from conveying the Program.\n\n  13. Use with the GNU Affero General Public License.\n\n  Notwithstanding any other provision of this License, you have\npermission to link or combine any covered work with a work licensed\nunder version 3 of the GNU Affero General Public License into a single\ncombined work, and to convey the resulting work.  The terms of this\nLicense will continue to apply to the part which is the covered work,\nbut the special requirements of the GNU Affero General Public License,\nsection 13, concerning interaction through a network will apply to the\ncombination as such.\n\n  14. Revised Versions of this License.\n\n  The Free Software Foundation may publish revised and/or new versions of\nthe GNU 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\n  Each version is given a distinguishing version number.  If the\nProgram specifies that a certain numbered version of the GNU General\nPublic License \"or any later version\" applies to it, you have the\noption of following the terms and conditions either of that numbered\nversion or of any later version published by the Free Software\nFoundation.  If the Program does not specify a version number of the\nGNU General Public License, you may choose any version ever published\nby the Free Software Foundation.\n\n  If the Program specifies that a proxy can decide which future\nversions of the GNU General Public License can be used, that proxy's\npublic statement of acceptance of a version permanently authorizes you\nto choose that version for the Program.\n\n  Later license versions may give you additional or different\npermissions.  However, no additional obligations are imposed on any\nauthor or copyright holder as a result of your choosing to follow a\nlater version.\n\n  15. Disclaimer of Warranty.\n\n  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY\nAPPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT\nHOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM \"AS IS\" WITHOUT WARRANTY\nOF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,\nTHE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR\nPURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM\nIS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF\nALL NECESSARY SERVICING, REPAIR OR CORRECTION.\n\n  16. Limitation of Liability.\n\n  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING\nWILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS\nTHE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY\nGENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE\nUSE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF\nDATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD\nPARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),\nEVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF\nSUCH DAMAGES.\n\n  17. Interpretation of Sections 15 and 16.\n\n  If the disclaimer of warranty and limitation of liability provided\nabove cannot be given local legal effect according to their terms,\nreviewing courts shall apply local law that most closely approximates\nan absolute waiver of all civil liability in connection with the\nProgram, unless a warranty or assumption of liability accompanies a\ncopy of the Program in return for a fee.\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\nstate 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 3 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\n    along with this program.  If not, see <https://www.gnu.org/licenses/>.\n\nAlso add information on how to contact you by electronic and paper mail.\n\n  If the program does terminal interaction, make it output a short\nnotice like this when it starts in an interactive mode:\n\n    <program>  Copyright (C) <year>  <name of author>\n    This program 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, your program's commands\nmight be different; for a GUI interface, you would use an \"about box\".\n\n  You should also get your employer (if you work as a programmer) or school,\nif any, to sign a \"copyright disclaimer\" for the program, if necessary.\nFor more information on this, and how to apply and follow the GNU GPL, see\n<https://www.gnu.org/licenses/>.\n\n  The GNU General Public License does not permit incorporating your program\ninto proprietary programs.  If your program is a subroutine library, you\nmay consider it more useful to permit linking proprietary applications with\nthe library.  If this is what you want to do, use the GNU Lesser General\nPublic License instead of this License.  But first, please read\n<https://www.gnu.org/licenses/why-not-lgpl.html>.\n"
  },
  {
    "path": "core/Clash.Meta/Makefile",
    "content": "NAME=mihomo\nBINDIR=bin\nBRANCH=$(shell git branch --show-current)\nifeq ($(BRANCH),Alpha)\nVERSION=alpha-$(shell git rev-parse --short HEAD)\nelse ifeq ($(BRANCH),Beta)\nVERSION=beta-$(shell git rev-parse --short HEAD)\nelse ifeq ($(BRANCH),)\nVERSION=$(shell git describe --tags)\nelse\nVERSION=$(shell git rev-parse --short HEAD)\nendif\n\nBUILDTIME=$(shell date -u)\nGOBUILD=CGO_ENABLED=0 go build -tags with_gvisor -trimpath -ldflags '-X \"github.com/metacubex/mihomo/constant.Version=$(VERSION)\" \\\n\t\t-X \"github.com/metacubex/mihomo/constant.BuildTime=$(BUILDTIME)\" \\\n\t\t-w -s -buildid='\n\nPLATFORM_LIST = \\\n\tdarwin-386 \\\n\tdarwin-amd64-compatible \\\n\tdarwin-amd64 \\\n\tdarwin-amd64-v1 \\\n\tdarwin-amd64-v2 \\\n\tdarwin-amd64-v3 \\\n\tdarwin-arm64 \\\n\tlinux-386 \\\n\tlinux-amd64-compatible \\\n\tlinux-amd64 \\\n\tlinux-amd64-v1 \\\n\tlinux-amd64-v2 \\\n\tlinux-amd64-v3 \\\n\tlinux-armv5 \\\n\tlinux-armv6 \\\n\tlinux-armv7 \\\n\tlinux-arm64 \\\n\tlinux-mips64 \\\n\tlinux-mips64le \\\n\tlinux-mips-softfloat \\\n\tlinux-mips-hardfloat \\\n\tlinux-mipsle-softfloat \\\n\tlinux-mipsle-hardfloat \\\n\tlinux-riscv64 \\\n\tlinux-loong64 \\\n\tandroid-arm64 \\\n\tfreebsd-386 \\\n\tfreebsd-amd64 \\\n\tfreebsd-arm64\n\nWINDOWS_ARCH_LIST = \\\n\twindows-386 \\\n\twindows-amd64-compatible \\\n\twindows-amd64 \\\n\twindows-amd64-v1 \\\n\twindows-amd64-v2 \\\n\twindows-amd64-v3 \\\n\twindows-arm64 \\\n    windows-arm32v7\n\nall:linux-amd64-v3 linux-arm64\\\n\tdarwin-amd64-v3 darwin-arm64\\\n \twindows-amd64-v3 windows-arm64\\\n\n\ndarwin-all: darwin-amd64-v3 darwin-arm64\n\ndocker:\n\tGOAMD64=v1 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\ndarwin-386:\n\tGOARCH=386 GOOS=darwin $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\ndarwin-amd64-compatible:\n\tGOARCH=amd64 GOOS=darwin GOAMD64=v1 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\ndarwin-amd64:\n\tGOARCH=amd64 GOOS=darwin GOAMD64=v3 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\ndarwin-amd64-v1:\n\tGOARCH=amd64 GOOS=darwin GOAMD64=v1 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\ndarwin-amd64-v2:\n\tGOARCH=amd64 GOOS=darwin GOAMD64=v2 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\ndarwin-amd64-v3:\n\tGOARCH=amd64 GOOS=darwin GOAMD64=v3 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\ndarwin-arm64:\n\tGOARCH=arm64 GOOS=darwin $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\nlinux-386:\n\tGOARCH=386 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\nlinux-amd64-compatible:\n\tGOARCH=amd64 GOOS=linux GOAMD64=v1 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\nlinux-amd64:\n\tGOARCH=amd64 GOOS=linux GOAMD64=v3 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\nlinux-amd64-v1:\n\tGOARCH=amd64 GOOS=linux GOAMD64=v1 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\nlinux-amd64-v2:\n\tGOARCH=amd64 GOOS=linux GOAMD64=v2 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\nlinux-amd64-v3:\n\tGOARCH=amd64 GOOS=linux GOAMD64=v3 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\nlinux-arm64:\n\tGOARCH=arm64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\nlinux-armv5:\n\tGOARCH=arm GOOS=linux GOARM=5 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\nlinux-armv6:\n\tGOARCH=arm GOOS=linux GOARM=6 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\nlinux-armv7:\n\tGOARCH=arm GOOS=linux GOARM=7 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\nlinux-mips-softfloat:\n\tGOARCH=mips GOMIPS=softfloat GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\nlinux-mips-hardfloat:\n\tGOARCH=mips GOMIPS=hardfloat GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\nlinux-mipsle-softfloat:\n\tGOARCH=mipsle GOMIPS=softfloat GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\nlinux-mipsle-hardfloat:\n\tGOARCH=mipsle GOMIPS=hardfloat GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\nlinux-mips64:\n\tGOARCH=mips64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\nlinux-mips64le:\n\tGOARCH=mips64le GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\nlinux-riscv64:\n\tGOARCH=riscv64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\t\nlinux-loong64:\n\tGOARCH=loong64 GOOS=linux $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\nandroid-arm64:\n\tGOARCH=arm64 GOOS=android $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\nfreebsd-386:\n\tGOARCH=386 GOOS=freebsd $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\nfreebsd-amd64:\n\tGOARCH=amd64 GOOS=freebsd GOAMD64=v3 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\nfreebsd-arm64:\n\tGOARCH=arm64 GOOS=freebsd $(GOBUILD) -o $(BINDIR)/$(NAME)-$@\n\nwindows-386:\n\tGOARCH=386 GOOS=windows $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe\n\nwindows-amd64-compatible:\n\tGOARCH=amd64 GOOS=windows GOAMD64=v1 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe\n\nwindows-amd64:\n\tGOARCH=amd64 GOOS=windows GOAMD64=v3 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe\n\nwindows-amd64-v1:\n\tGOARCH=amd64 GOOS=windows GOAMD64=v1 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe\n\nwindows-amd64-v2:\n\tGOARCH=amd64 GOOS=windows GOAMD64=v2 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe\n\nwindows-amd64-v3:\n\tGOARCH=amd64 GOOS=windows GOAMD64=v3 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe\n\nwindows-arm64:\n\tGOARCH=arm64 GOOS=windows $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe\n\nwindows-arm32v7:\n\tGOARCH=arm GOOS=windows GOARM=7 $(GOBUILD) -o $(BINDIR)/$(NAME)-$@.exe\n\ngz_releases=$(addsuffix .gz, $(PLATFORM_LIST))\nzip_releases=$(addsuffix .zip, $(WINDOWS_ARCH_LIST))\n\n$(gz_releases): %.gz : %\n\tchmod +x $(BINDIR)/$(NAME)-$(basename $@)\n\tgzip -f -S -$(VERSION).gz $(BINDIR)/$(NAME)-$(basename $@)\n\n$(zip_releases): %.zip : %\n\tzip -m -j $(BINDIR)/$(NAME)-$(basename $@)-$(VERSION).zip $(BINDIR)/$(NAME)-$(basename $@).exe\n\nall-arch: $(PLATFORM_LIST) $(WINDOWS_ARCH_LIST)\n\nreleases: $(gz_releases) $(zip_releases)\n\nvet:\n\tgo test ./...\n\nlint:\n\tgolangci-lint run ./...\n\nclean:\n\trm $(BINDIR)/*\n\nCLANG ?= clang-14\nCFLAGS := -O2 -g -Wall -Werror $(CFLAGS)\n\n"
  },
  {
    "path": "core/Clash.Meta/adapter/adapter.go",
    "content": "package adapter\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/atomic\"\n\t\"github.com/metacubex/mihomo/common/queue\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"github.com/metacubex/mihomo/common/xsync\"\n\t\"github.com/metacubex/mihomo/component/ca\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/metacubex/http\"\n)\n\nvar UnifiedDelay = atomic.NewBool(false)\n\nconst (\n\tdefaultHistoriesNum = 10\n)\n\ntype internalProxyState struct {\n\talive   atomic.Bool\n\thistory *queue.Queue[C.DelayHistory]\n}\n\ntype Proxy struct {\n\tC.ProxyAdapter\n\talive   atomic.Bool\n\thistory *queue.Queue[C.DelayHistory]\n\textra   xsync.Map[string, *internalProxyState]\n}\n\n// Adapter implements C.Proxy\nfunc (p *Proxy) Adapter() C.ProxyAdapter {\n\treturn p.ProxyAdapter\n}\n\n// AliveForTestUrl implements C.Proxy\nfunc (p *Proxy) AliveForTestUrl(url string) bool {\n\tif state, ok := p.extra.Load(url); ok {\n\t\treturn state.alive.Load()\n\t}\n\n\treturn p.alive.Load()\n}\n\n// DialContext implements C.ProxyAdapter\nfunc (p *Proxy) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, error) {\n\tconn, err := p.ProxyAdapter.DialContext(ctx, metadata)\n\treturn conn, err\n}\n\n// ListenPacketContext implements C.ProxyAdapter\nfunc (p *Proxy) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (C.PacketConn, error) {\n\tpc, err := p.ProxyAdapter.ListenPacketContext(ctx, metadata)\n\treturn pc, err\n}\n\n// DelayHistory implements C.Proxy\nfunc (p *Proxy) DelayHistory() []C.DelayHistory {\n\tqueueM := p.history.Copy()\n\thistories := []C.DelayHistory{}\n\tfor _, item := range queueM {\n\t\thistories = append(histories, item)\n\t}\n\treturn histories\n}\n\n// DelayHistoryForTestUrl implements C.Proxy\nfunc (p *Proxy) DelayHistoryForTestUrl(url string) []C.DelayHistory {\n\tvar queueM []C.DelayHistory\n\n\tif state, ok := p.extra.Load(url); ok {\n\t\tqueueM = state.history.Copy()\n\t}\n\thistories := []C.DelayHistory{}\n\tfor _, item := range queueM {\n\t\thistories = append(histories, item)\n\t}\n\treturn histories\n}\n\n// ExtraDelayHistories return all delay histories for each test URL\n// implements C.Proxy\nfunc (p *Proxy) ExtraDelayHistories() map[string]C.ProxyState {\n\thistories := map[string]C.ProxyState{}\n\n\tp.extra.Range(func(k string, v *internalProxyState) bool {\n\t\ttestUrl := k\n\t\tstate := v\n\n\t\tqueueM := state.history.Copy()\n\t\tvar history []C.DelayHistory\n\n\t\tfor _, item := range queueM {\n\t\t\thistory = append(history, item)\n\t\t}\n\n\t\thistories[testUrl] = C.ProxyState{\n\t\t\tAlive:   state.alive.Load(),\n\t\t\tHistory: history,\n\t\t}\n\t\treturn true\n\t})\n\treturn histories\n}\n\n// LastDelayForTestUrl return last history record of the specified URL. if proxy is not alive, return the max value of uint16.\n// implements C.Proxy\nfunc (p *Proxy) LastDelayForTestUrl(url string) (delay uint16) {\n\tvar maxDelay uint16 = 0xffff\n\n\talive := false\n\tvar history C.DelayHistory\n\n\tif state, ok := p.extra.Load(url); ok {\n\t\talive = state.alive.Load()\n\t\thistory = state.history.Last()\n\t}\n\n\tif !alive || history.Delay == 0 {\n\t\treturn maxDelay\n\t}\n\treturn history.Delay\n}\n\n// MarshalJSON implements C.ProxyAdapter\nfunc (p *Proxy) MarshalJSON() ([]byte, error) {\n\tinner, err := p.ProxyAdapter.MarshalJSON()\n\tif err != nil {\n\t\treturn inner, err\n\t}\n\n\tmapping := map[string]any{}\n\t_ = json.Unmarshal(inner, &mapping)\n\tmapping[\"history\"] = p.DelayHistory()\n\tmapping[\"extra\"] = p.ExtraDelayHistories()\n\tmapping[\"alive\"] = p.alive.Load()\n\tmapping[\"name\"] = p.Name()\n\tmapping[\"udp\"] = p.SupportUDP()\n\tmapping[\"uot\"] = p.SupportUOT()\n\n\tproxyInfo := p.ProxyInfo()\n\tmapping[\"xudp\"] = proxyInfo.XUDP\n\tmapping[\"tfo\"] = proxyInfo.TFO\n\tmapping[\"mptcp\"] = proxyInfo.MPTCP\n\tmapping[\"smux\"] = proxyInfo.SMUX\n\tmapping[\"interface\"] = proxyInfo.Interface\n\tmapping[\"routing-mark\"] = proxyInfo.RoutingMark\n\tmapping[\"provider-name\"] = proxyInfo.ProviderName\n\tmapping[\"dialer-proxy\"] = proxyInfo.DialerProxy\n\n\treturn json.Marshal(mapping)\n}\n\n// URLTest get the delay for the specified URL\n// implements C.Proxy\nfunc (p *Proxy) URLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16]) (t uint16, err error) {\n\tvar satisfied bool\n\n\tdefer func() {\n\t\tif UrlTestHook != nil {\n\t\t\tUrlTestHook(url, p.Name(), t)\n\t\t}\n\n\t\talive := err == nil\n\t\trecord := C.DelayHistory{Time: time.Now()}\n\t\tif alive {\n\t\t\trecord.Delay = t\n\t\t}\n\n\t\tp.alive.Store(alive)\n\t\tp.history.Put(record)\n\t\tif p.history.Len() > defaultHistoriesNum {\n\t\t\tp.history.Pop()\n\t\t}\n\n\t\tstate, _ := p.extra.LoadOrStoreFn(url, func() *internalProxyState {\n\t\t\treturn &internalProxyState{\n\t\t\t\thistory: queue.New[C.DelayHistory](defaultHistoriesNum),\n\t\t\t\talive:   atomic.NewBool(true),\n\t\t\t}\n\t\t})\n\n\t\tif !satisfied {\n\t\t\trecord.Delay = 0\n\t\t\talive = false\n\t\t}\n\n\t\tstate.alive.Store(alive)\n\t\tstate.history.Put(record)\n\t\tif state.history.Len() > defaultHistoriesNum {\n\t\t\tstate.history.Pop()\n\t\t}\n\n\t}()\n\n\tunifiedDelay := UnifiedDelay.Load()\n\n\taddr, err := urlToMetadata(url)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tstart := time.Now()\n\tinstance, err := p.DialContext(ctx, &addr)\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer func() {\n\t\t_ = instance.Close()\n\t}()\n\n\treq, err := http.NewRequest(http.MethodHead, url, nil)\n\tif err != nil {\n\t\treturn\n\t}\n\treq = req.WithContext(ctx)\n\n\ttlsConfig, err := ca.GetTLSConfig(ca.Option{})\n\tif err != nil {\n\t\treturn\n\t}\n\n\ttransport := &http.Transport{\n\t\tDialContext: func(context.Context, string, string) (net.Conn, error) {\n\t\t\treturn instance, nil\n\t\t},\n\t\t// from http.DefaultTransport\n\t\tMaxIdleConns:          100,\n\t\tIdleConnTimeout:       90 * time.Second,\n\t\tTLSHandshakeTimeout:   10 * time.Second,\n\t\tExpectContinueTimeout: 1 * time.Second,\n\t\tTLSClientConfig:       tlsConfig,\n\t}\n\n\tclient := http.Client{\n\t\tTimeout:   30 * time.Second,\n\t\tTransport: transport,\n\t\tCheckRedirect: func(req *http.Request, via []*http.Request) error {\n\t\t\treturn http.ErrUseLastResponse\n\t\t},\n\t}\n\n\tdefer client.CloseIdleConnections()\n\n\tresp, err := client.Do(req)\n\n\tif err != nil {\n\t\treturn\n\t}\n\n\t_ = resp.Body.Close()\n\n\tif unifiedDelay {\n\t\tsecond := time.Now()\n\t\tvar ignoredErr error\n\t\tvar secondResp *http.Response\n\t\tsecondResp, ignoredErr = client.Do(req)\n\t\tif ignoredErr == nil {\n\t\t\tresp = secondResp\n\t\t\t_ = resp.Body.Close()\n\t\t\tstart = second\n\t\t} else {\n\t\t\tif strings.HasPrefix(url, \"http://\") {\n\t\t\t\tlog.Errorln(\"%s failed to get the second response from %s: %v\", p.Name(), url, ignoredErr)\n\t\t\t\tlog.Warnln(\"It is recommended to use HTTPS for provider.health-check.url and group.url to ensure better reliability. Due to some proxy providers hijacking test addresses and not being compatible with repeated HEAD requests, using HTTP may result in failed tests.\")\n\t\t\t}\n\t\t}\n\t}\n\n\tsatisfied = resp != nil && (expectedStatus == nil || expectedStatus.Check(uint16(resp.StatusCode)))\n\tt = uint16(time.Since(start) / time.Millisecond)\n\treturn\n}\n\nfunc NewProxy(adapter C.ProxyAdapter) *Proxy {\n\treturn &Proxy{\n\t\tProxyAdapter: adapter,\n\t\thistory:      queue.New[C.DelayHistory](defaultHistoriesNum),\n\t\talive:        atomic.NewBool(true),\n\t}\n}\n\nfunc urlToMetadata(rawURL string) (addr C.Metadata, err error) {\n\tu, err := url.Parse(rawURL)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tport := u.Port()\n\tif port == \"\" {\n\t\tswitch u.Scheme {\n\t\tcase \"https\":\n\t\t\tport = \"443\"\n\t\tcase \"http\":\n\t\t\tport = \"80\"\n\t\tdefault:\n\t\t\terr = fmt.Errorf(\"%s scheme not Support\", rawURL)\n\t\t\treturn\n\t\t}\n\t}\n\n\terr = addr.SetRemoteAddress(net.JoinHostPort(u.Hostname(), port))\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/inbound/addition.go",
    "content": "package inbound\n\nimport (\n\t\"net\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\ntype Addition func(metadata *C.Metadata)\n\nfunc ApplyAdditions(metadata *C.Metadata, additions ...Addition) {\n\tfor _, addition := range additions {\n\t\taddition(metadata)\n\t}\n}\n\nfunc WithInName(name string) Addition {\n\treturn func(metadata *C.Metadata) {\n\t\tmetadata.InName = name\n\t}\n}\n\nfunc WithInUser(user string) Addition {\n\treturn func(metadata *C.Metadata) {\n\t\tmetadata.InUser = user\n\t}\n}\n\nfunc WithSpecialRules(specialRules string) Addition {\n\treturn func(metadata *C.Metadata) {\n\t\tmetadata.SpecialRules = specialRules\n\t}\n}\n\nfunc WithSpecialProxy(specialProxy string) Addition {\n\treturn func(metadata *C.Metadata) {\n\t\tmetadata.SpecialProxy = specialProxy\n\t}\n}\n\nfunc WithDstAddr(addr net.Addr) Addition {\n\treturn func(metadata *C.Metadata) {\n\t\t_ = metadata.SetRemoteAddr(addr)\n\t}\n}\n\nfunc WithSrcAddr(addr net.Addr) Addition {\n\treturn func(metadata *C.Metadata) {\n\t\tm := C.Metadata{}\n\t\tif err := m.SetRemoteAddr(addr); err == nil {\n\t\t\tmetadata.SrcIP = m.DstIP\n\t\t\tmetadata.SrcPort = m.DstPort\n\t\t}\n\t}\n}\n\nfunc WithInAddr(addr net.Addr) Addition {\n\treturn func(metadata *C.Metadata) {\n\t\tm := C.Metadata{}\n\t\tif err := m.SetRemoteAddr(addr); err == nil {\n\t\t\tmetadata.InIP = m.DstIP\n\t\t\tmetadata.InPort = m.DstPort\n\t\t}\n\t}\n}\n\nfunc WithDSCP(dscp uint8) Addition {\n\treturn func(metadata *C.Metadata) {\n\t\tmetadata.DSCP = dscp\n\t}\n}\n\nfunc Placeholder(metadata *C.Metadata) {}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/inbound/auth.go",
    "content": "package inbound\n\nimport (\n\t\"net\"\n\t\"net/netip\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\nvar skipAuthPrefixes []netip.Prefix\n\nfunc SetSkipAuthPrefixes(prefixes []netip.Prefix) {\n\tskipAuthPrefixes = prefixes\n}\n\nfunc SkipAuthPrefixes() []netip.Prefix {\n\treturn skipAuthPrefixes\n}\n\nfunc SkipAuthRemoteAddr(addr net.Addr) bool {\n\tm := C.Metadata{}\n\tif err := m.SetRemoteAddr(addr); err != nil {\n\t\treturn false\n\t}\n\treturn skipAuth(m.AddrPort().Addr())\n}\n\nfunc SkipAuthRemoteAddress(addr string) bool {\n\tm := C.Metadata{}\n\tif err := m.SetRemoteAddress(addr); err != nil {\n\t\treturn false\n\t}\n\treturn skipAuth(m.AddrPort().Addr())\n}\n\nfunc skipAuth(addr netip.Addr) bool {\n\treturn prefixesContains(skipAuthPrefixes, addr)\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/inbound/http.go",
    "content": "package inbound\n\nimport (\n\t\"net\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n)\n\n// NewHTTP receive normal http request and return HTTPContext\nfunc NewHTTP(target socks5.Addr, srcConn net.Conn, conn net.Conn, additions ...Addition) (net.Conn, *C.Metadata) {\n\tmetadata := parseSocksAddr(target)\n\tmetadata.NetWork = C.TCP\n\tmetadata.Type = C.HTTP\n\tmetadata.RawSrcAddr = srcConn.RemoteAddr()\n\tmetadata.RawDstAddr = srcConn.LocalAddr()\n\tApplyAdditions(metadata, WithSrcAddr(srcConn.RemoteAddr()), WithInAddr(srcConn.LocalAddr()))\n\tApplyAdditions(metadata, additions...)\n\treturn conn, metadata\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/inbound/https.go",
    "content": "package inbound\n\nimport (\n\t\"net\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\n\t\"github.com/metacubex/http\"\n)\n\n// NewHTTPS receive CONNECT request and return ConnContext\nfunc NewHTTPS(request *http.Request, conn net.Conn, additions ...Addition) (net.Conn, *C.Metadata) {\n\tmetadata := parseHTTPAddr(request)\n\tmetadata.Type = C.HTTPS\n\tmetadata.RawSrcAddr = conn.RemoteAddr()\n\tmetadata.RawDstAddr = conn.LocalAddr()\n\tApplyAdditions(metadata, WithSrcAddr(conn.RemoteAddr()), WithInAddr(conn.LocalAddr()))\n\tApplyAdditions(metadata, additions...)\n\treturn conn, metadata\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/inbound/ipfilter.go",
    "content": "package inbound\n\nimport (\n\t\"net\"\n\t\"net/netip\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\nvar lanAllowedIPs []netip.Prefix\nvar lanDisAllowedIPs []netip.Prefix\n\nfunc SetAllowedIPs(prefixes []netip.Prefix) {\n\tlanAllowedIPs = prefixes\n}\n\nfunc SetDisAllowedIPs(prefixes []netip.Prefix) {\n\tlanDisAllowedIPs = prefixes\n}\n\nfunc AllowedIPs() []netip.Prefix {\n\treturn lanAllowedIPs\n}\n\nfunc DisAllowedIPs() []netip.Prefix {\n\treturn lanDisAllowedIPs\n}\n\nfunc IsRemoteAddrDisAllowed(addr net.Addr) bool {\n\tm := C.Metadata{}\n\tif err := m.SetRemoteAddr(addr); err != nil {\n\t\treturn false\n\t}\n\tipAddr := m.AddrPort().Addr()\n\tif ipAddr.IsValid() {\n\t\treturn isAllowed(ipAddr) && !isDisAllowed(ipAddr)\n\t}\n\treturn false\n}\n\nfunc isAllowed(addr netip.Addr) bool {\n\treturn prefixesContains(lanAllowedIPs, addr)\n}\n\nfunc isDisAllowed(addr netip.Addr) bool {\n\treturn prefixesContains(lanDisAllowedIPs, addr)\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/inbound/listen.go",
    "content": "package inbound\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/netip\"\n\t\"sync\"\n\n\t\"github.com/metacubex/mihomo/component/keepalive\"\n\t\"github.com/metacubex/mihomo/component/mptcp\"\n\n\t\"github.com/metacubex/tfo-go\"\n)\n\nvar (\n\tlc = tfo.ListenConfig{\n\t\tDisableTFO: true,\n\t}\n\tmutex sync.RWMutex\n)\n\nfunc SetTfo(open bool) {\n\tmutex.Lock()\n\tdefer mutex.Unlock()\n\tlc.DisableTFO = !open\n}\n\nfunc Tfo() bool {\n\tmutex.RLock()\n\tdefer mutex.RUnlock()\n\treturn !lc.DisableTFO\n}\n\nfunc SetMPTCP(open bool) {\n\tmutex.Lock()\n\tdefer mutex.Unlock()\n\tmptcp.SetNetListenConfig(&lc.ListenConfig, open)\n}\n\nfunc MPTCP() bool {\n\tmutex.RLock()\n\tdefer mutex.RUnlock()\n\treturn mptcp.GetNetListenConfig(&lc.ListenConfig)\n}\n\nfunc preResolve(network, address string) (string, error) {\n\tswitch network { // like net.Resolver.internetAddrList but filter domain to avoid call net.Resolver.lookupIPAddr\n\tcase \"tcp\", \"tcp4\", \"tcp6\", \"udp\", \"udp4\", \"udp6\", \"ip\", \"ip4\", \"ip6\":\n\t\tif host, port, err := net.SplitHostPort(address); err == nil {\n\t\t\tswitch host {\n\t\t\tcase \"localhost\":\n\t\t\t\tswitch network {\n\t\t\t\tcase \"tcp6\", \"udp6\", \"ip6\":\n\t\t\t\t\taddress = net.JoinHostPort(\"::1\", port)\n\t\t\t\tdefault:\n\t\t\t\t\taddress = net.JoinHostPort(\"127.0.0.1\", port)\n\t\t\t\t}\n\t\t\tcase \"\": // internetAddrList can handle this special case\n\t\t\t\tbreak\n\t\t\tdefault:\n\t\t\t\tif _, err := netip.ParseAddr(host); err != nil { // not ip\n\t\t\t\t\treturn \"\", fmt.Errorf(\"invalid network address: %s\", address)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn address, nil\n}\n\nfunc ListenContext(ctx context.Context, network, address string) (net.Listener, error) {\n\taddress, err := preResolve(network, address)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tmutex.RLock()\n\tdefer mutex.RUnlock()\n\treturn lc.Listen(ctx, network, address)\n}\n\nfunc Listen(network, address string) (net.Listener, error) {\n\treturn ListenContext(context.Background(), network, address)\n}\n\nfunc ListenPacketContext(ctx context.Context, network, address string) (net.PacketConn, error) {\n\taddress, err := preResolve(network, address)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tmutex.RLock()\n\tdefer mutex.RUnlock()\n\treturn lc.ListenPacket(ctx, network, address)\n}\n\nfunc ListenPacket(network, address string) (net.PacketConn, error) {\n\treturn ListenPacketContext(context.Background(), network, address)\n}\n\nfunc init() {\n\tkeepalive.SetDisableKeepAliveCallback.Register(func(b bool) {\n\t\tmutex.Lock()\n\t\tdefer mutex.Unlock()\n\t\tkeepalive.SetNetListenConfig(&lc.ListenConfig)\n\t})\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/inbound/listen_notwindows.go",
    "content": "//go:build !windows\n\npackage inbound\n\nimport (\n\t\"net\"\n\t\"os\"\n)\n\nconst SupportNamedPipe = false\n\nfunc ListenNamedPipe(path string) (net.Listener, error) {\n\treturn nil, os.ErrInvalid\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/inbound/listen_windows.go",
    "content": "package inbound\n\nimport (\n\t\"net\"\n\t\"os\"\n\n\t\"github.com/metacubex/wireguard-go/ipc/namedpipe\"\n\t\"golang.org/x/sys/windows\"\n)\n\nconst SupportNamedPipe = true\n\n// windowsSDDL is the Security Descriptor set on the namedpipe.\n// It provides read/write access to all users and the local system.\nconst windowsSDDL = \"D:PAI(A;OICI;GWGR;;;BU)(A;OICI;GWGR;;;SY)\"\n\nfunc ListenNamedPipe(path string) (net.Listener, error) {\n\tsddl := os.Getenv(\"LISTEN_NAMEDPIPE_SDDL\")\n\tif sddl == \"\" {\n\t\tsddl = windowsSDDL\n\t}\n\tsecurityDescriptor, err := windows.SecurityDescriptorFromString(sddl)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tnamedpipeLC := namedpipe.ListenConfig{\n\t\tSecurityDescriptor: securityDescriptor,\n\t\tInputBufferSize:    256 * 1024,\n\t\tOutputBufferSize:   256 * 1024,\n\t}\n\treturn namedpipeLC.Listen(path)\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/inbound/packet.go",
    "content": "package inbound\n\nimport (\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n)\n\n// NewPacket is PacketAdapter generator\nfunc NewPacket(target socks5.Addr, packet C.UDPPacket, source C.Type, additions ...Addition) (C.UDPPacket, *C.Metadata) {\n\tmetadata := parseSocksAddr(target)\n\tmetadata.NetWork = C.UDP\n\tmetadata.Type = source\n\tmetadata.RawSrcAddr = packet.LocalAddr()\n\tmetadata.RawDstAddr = metadata.UDPAddr()\n\tApplyAdditions(metadata, WithSrcAddr(packet.LocalAddr()))\n\tif p, ok := packet.(C.UDPPacketInAddr); ok {\n\t\tApplyAdditions(metadata, WithInAddr(p.InAddr()))\n\t}\n\tApplyAdditions(metadata, additions...)\n\n\treturn packet, metadata\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/inbound/socket.go",
    "content": "package inbound\n\nimport (\n\t\"net\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n)\n\n// NewSocket receive TCP inbound and return ConnContext\nfunc NewSocket(target socks5.Addr, conn net.Conn, source C.Type, additions ...Addition) (net.Conn, *C.Metadata) {\n\tmetadata := parseSocksAddr(target)\n\tmetadata.NetWork = C.TCP\n\tmetadata.Type = source\n\tApplyAdditions(metadata, WithSrcAddr(conn.RemoteAddr()), WithInAddr(conn.LocalAddr()))\n\tApplyAdditions(metadata, additions...)\n\treturn conn, metadata\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/inbound/util.go",
    "content": "package inbound\n\nimport (\n\t\"net\"\n\t\"net/netip\"\n\t\"strings\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n\n\t\"github.com/metacubex/http\"\n)\n\nfunc parseSocksAddr(target socks5.Addr) *C.Metadata {\n\tmetadata := &C.Metadata{}\n\n\tswitch target[0] {\n\tcase socks5.AtypDomainName:\n\t\t// trim for FQDN\n\t\tmetadata.Host = strings.TrimRight(string(target[2:2+target[1]]), \".\")\n\t\tmetadata.DstPort = uint16((int(target[2+target[1]]) << 8) | int(target[2+target[1]+1]))\n\tcase socks5.AtypIPv4:\n\t\tmetadata.DstIP, _ = netip.AddrFromSlice(target[1 : 1+net.IPv4len])\n\t\tmetadata.DstPort = uint16((int(target[1+net.IPv4len]) << 8) | int(target[1+net.IPv4len+1]))\n\tcase socks5.AtypIPv6:\n\t\tmetadata.DstIP, _ = netip.AddrFromSlice(target[1 : 1+net.IPv6len])\n\t\tmetadata.DstPort = uint16((int(target[1+net.IPv6len]) << 8) | int(target[1+net.IPv6len+1]))\n\t}\n\tmetadata.DstIP = metadata.DstIP.Unmap()\n\n\treturn metadata\n}\n\nfunc parseHTTPAddr(request *http.Request) *C.Metadata {\n\thost := request.URL.Hostname()\n\tport := request.URL.Port()\n\tif port == \"\" {\n\t\tport = \"80\"\n\t}\n\n\t// trim FQDN (#737)\n\thost = strings.TrimRight(host, \".\")\n\n\tmetadata := &C.Metadata{}\n\t_ = metadata.SetRemoteAddress(net.JoinHostPort(host, port))\n\treturn metadata\n}\n\nfunc prefixesContains(prefixes []netip.Prefix, addr netip.Addr) bool {\n\tif len(prefixes) == 0 {\n\t\treturn false\n\t}\n\tif !addr.IsValid() {\n\t\treturn false\n\t}\n\taddr = addr.Unmap().WithZone(\"\") // netip.Prefix.Contains returns false if ip has an IPv6 zone\n\tfor _, prefix := range prefixes {\n\t\tif prefix.Contains(addr) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outbound/anytls.go",
    "content": "package outbound\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"strconv\"\n\t\"time\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/component/proxydialer\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/transport/anytls\"\n\t\"github.com/metacubex/mihomo/transport/vmess\"\n\n\tM \"github.com/metacubex/sing/common/metadata\"\n\t\"github.com/metacubex/sing/common/uot\"\n)\n\ntype AnyTLS struct {\n\t*Base\n\tclient *anytls.Client\n\toption *AnyTLSOption\n}\n\ntype AnyTLSOption struct {\n\tBasicOption\n\tName                     string     `proxy:\"name\"`\n\tServer                   string     `proxy:\"server\"`\n\tPort                     int        `proxy:\"port\"`\n\tPassword                 string     `proxy:\"password\"`\n\tALPN                     []string   `proxy:\"alpn,omitempty\"`\n\tSNI                      string     `proxy:\"sni,omitempty\"`\n\tECHOpts                  ECHOptions `proxy:\"ech-opts,omitempty\"`\n\tClientFingerprint        string     `proxy:\"client-fingerprint,omitempty\"`\n\tSkipCertVerify           bool       `proxy:\"skip-cert-verify,omitempty\"`\n\tFingerprint              string     `proxy:\"fingerprint,omitempty\"`\n\tCertificate              string     `proxy:\"certificate,omitempty\"`\n\tPrivateKey               string     `proxy:\"private-key,omitempty\"`\n\tUDP                      bool       `proxy:\"udp,omitempty\"`\n\tIdleSessionCheckInterval int        `proxy:\"idle-session-check-interval,omitempty\"`\n\tIdleSessionTimeout       int        `proxy:\"idle-session-timeout,omitempty\"`\n\tMinIdleSession           int        `proxy:\"min-idle-session,omitempty\"`\n}\n\nfunc (t *AnyTLS) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Conn, err error) {\n\tc, err := t.client.CreateProxy(ctx, M.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewConn(c, t), nil\n}\n\nfunc (t *AnyTLS) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {\n\tif err = t.ResolveUDP(ctx, metadata); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// create tcp\n\tc, err := t.client.CreateProxy(ctx, uot.RequestDestination(2))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// create uot on tcp\n\tdestination := M.SocksaddrFromNet(metadata.UDPAddr())\n\treturn newPacketConn(N.NewThreadSafePacketConn(uot.NewLazyConn(c, uot.Request{Destination: destination})), t), nil\n}\n\n// SupportUOT implements C.ProxyAdapter\nfunc (t *AnyTLS) SupportUOT() bool {\n\treturn true\n}\n\n// ProxyInfo implements C.ProxyAdapter\nfunc (t *AnyTLS) ProxyInfo() C.ProxyInfo {\n\tinfo := t.Base.ProxyInfo()\n\tinfo.DialerProxy = t.option.DialerProxy\n\treturn info\n}\n\n// Close implements C.ProxyAdapter\nfunc (t *AnyTLS) Close() error {\n\treturn t.client.Close()\n}\n\nfunc NewAnyTLS(option AnyTLSOption) (*AnyTLS, error) {\n\taddr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))\n\toutbound := &AnyTLS{\n\t\tBase: NewBase(BaseOption{\n\t\t\tName:         option.Name,\n\t\t\tAddr:         addr,\n\t\t\tType:         C.AnyTLS,\n\t\t\tProviderName: option.ProviderName,\n\t\t\tUDP:          option.UDP,\n\t\t\tTFO:          option.TFO,\n\t\t\tMPTCP:        option.MPTCP,\n\t\t\tInterface:    option.Interface,\n\t\t\tRoutingMark:  option.RoutingMark,\n\t\t\tPrefer:       option.IPVersion,\n\t\t}),\n\t\toption: &option,\n\t}\n\toutbound.dialer = option.NewDialer(outbound.DialOptions())\n\tsingDialer := proxydialer.NewSingDialer(outbound.dialer)\n\n\ttOption := anytls.ClientConfig{\n\t\tPassword:                 option.Password,\n\t\tServer:                   M.ParseSocksaddrHostPort(option.Server, uint16(option.Port)),\n\t\tDialer:                   singDialer,\n\t\tIdleSessionCheckInterval: time.Duration(option.IdleSessionCheckInterval) * time.Second,\n\t\tIdleSessionTimeout:       time.Duration(option.IdleSessionTimeout) * time.Second,\n\t\tMinIdleSession:           option.MinIdleSession,\n\t}\n\techConfig, err := option.ECHOpts.Parse()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\ttlsConfig := &vmess.TLSConfig{\n\t\tHost:              option.SNI,\n\t\tSkipCertVerify:    option.SkipCertVerify,\n\t\tNextProtos:        option.ALPN,\n\t\tFingerPrint:       option.Fingerprint,\n\t\tCertificate:       option.Certificate,\n\t\tPrivateKey:        option.PrivateKey,\n\t\tClientFingerprint: option.ClientFingerprint,\n\t\tECH:               echConfig,\n\t}\n\tif tlsConfig.Host == \"\" {\n\t\ttlsConfig.Host = option.Server\n\t}\n\ttOption.TLSConfig = tlsConfig\n\n\tclient := anytls.NewClient(context.TODO(), tOption)\n\toutbound.client = client\n\n\treturn outbound, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outbound/base.go",
    "content": "package outbound\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net\"\n\t\"runtime\"\n\t\"sync\"\n\t\"syscall\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"github.com/metacubex/mihomo/component/dialer\"\n\t\"github.com/metacubex/mihomo/component/proxydialer\"\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/gofrs/uuid/v5\"\n)\n\ntype ProxyAdapter interface {\n\tC.ProxyAdapter\n\tDialOptions() []dialer.Option\n\tResolveUDP(ctx context.Context, metadata *C.Metadata) error\n}\n\ntype Base struct {\n\tname   string\n\taddr   string\n\ttp     C.AdapterType\n\tpdName string\n\tudp    bool\n\txudp   bool\n\ttfo    bool\n\tmpTcp  bool\n\tiface  string\n\trmark  int\n\tprefer C.DNSPrefer\n\tdialer C.Dialer\n\tid     uuid.UUID\n}\n\ntype BaseOption struct {\n\tName         string\n\tAddr         string\n\tType         C.AdapterType\n\tProviderName string\n\tUDP          bool\n\tXUDP         bool\n\tTFO          bool\n\tMPTCP        bool\n\tInterface    string\n\tRoutingMark  int\n\tPrefer       C.DNSPrefer\n}\n\nfunc NewBase(opt BaseOption) *Base {\n\treturn &Base{\n\t\tname:   opt.Name,\n\t\taddr:   opt.Addr,\n\t\ttp:     opt.Type,\n\t\tpdName: opt.ProviderName,\n\t\tudp:    opt.UDP,\n\t\txudp:   opt.XUDP,\n\t\ttfo:    opt.TFO,\n\t\tmpTcp:  opt.MPTCP,\n\t\tiface:  opt.Interface,\n\t\trmark:  opt.RoutingMark,\n\t\tprefer: opt.Prefer,\n\t\tid:     utils.NewUUIDV4(),\n\t}\n}\n\n// Name implements C.ProxyAdapter\nfunc (b *Base) Name() string {\n\treturn b.name\n}\n\n// Id implements C.ProxyAdapter\nfunc (b *Base) Id() string {\n\treturn b.id.String()\n}\n\n// Type implements C.ProxyAdapter\nfunc (b *Base) Type() C.AdapterType {\n\treturn b.tp\n}\n\nfunc (b *Base) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, error) {\n\treturn nil, C.ErrNotSupport\n}\n\n// ListenPacketContext implements C.ProxyAdapter\nfunc (b *Base) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (C.PacketConn, error) {\n\treturn nil, C.ErrNotSupport\n}\n\n// SupportUOT implements C.ProxyAdapter\nfunc (b *Base) SupportUOT() bool {\n\treturn false\n}\n\n// SupportUDP implements C.ProxyAdapter\nfunc (b *Base) SupportUDP() bool {\n\treturn b.udp\n}\n\n// ProxyInfo implements C.ProxyAdapter\nfunc (b *Base) ProxyInfo() (info C.ProxyInfo) {\n\tinfo.XUDP = b.xudp\n\tinfo.TFO = b.tfo\n\tinfo.MPTCP = b.mpTcp\n\tinfo.SMUX = false\n\tinfo.Interface = b.iface\n\tinfo.RoutingMark = b.rmark\n\tinfo.ProviderName = b.pdName\n\treturn\n}\n\n// IsL3Protocol implements C.ProxyAdapter\nfunc (b *Base) IsL3Protocol(metadata *C.Metadata) bool {\n\treturn false\n}\n\n// MarshalJSON implements C.ProxyAdapter\nfunc (b *Base) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(map[string]string{\n\t\t\"type\": b.Type().String(),\n\t\t\"id\":   b.Id(),\n\t})\n}\n\n// Addr implements C.ProxyAdapter\nfunc (b *Base) Addr() string {\n\treturn b.addr\n}\n\n// Unwrap implements C.ProxyAdapter\nfunc (b *Base) Unwrap(metadata *C.Metadata, touch bool) C.Proxy {\n\treturn nil\n}\n\n// DialOptions return []dialer.Option from struct\nfunc (b *Base) DialOptions() (opts []dialer.Option) {\n\tif b.iface != \"\" {\n\t\topts = append(opts, dialer.WithInterface(b.iface))\n\t}\n\n\tif b.rmark != 0 {\n\t\topts = append(opts, dialer.WithRoutingMark(b.rmark))\n\t}\n\n\tswitch b.prefer {\n\tcase C.IPv4Only:\n\t\topts = append(opts, dialer.WithOnlySingleStack(true))\n\tcase C.IPv6Only:\n\t\topts = append(opts, dialer.WithOnlySingleStack(false))\n\tcase C.IPv4Prefer:\n\t\topts = append(opts, dialer.WithPreferIPv4())\n\tcase C.IPv6Prefer:\n\t\topts = append(opts, dialer.WithPreferIPv6())\n\tdefault:\n\t}\n\n\tif b.tfo {\n\t\topts = append(opts, dialer.WithTFO(true))\n\t}\n\n\tif b.mpTcp {\n\t\topts = append(opts, dialer.WithMPTCP(true))\n\t}\n\n\treturn opts\n}\n\nfunc (b *Base) ResolveUDP(ctx context.Context, metadata *C.Metadata) error {\n\tif !metadata.Resolved() {\n\t\tip, err := resolver.ResolveIP(ctx, metadata.Host)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"can't resolve ip: %w\", err)\n\t\t}\n\t\tmetadata.DstIP = ip\n\t}\n\treturn nil\n}\n\nfunc (b *Base) Close() error {\n\treturn nil\n}\n\ntype BasicOption struct {\n\tTFO         bool        `proxy:\"tfo,omitempty\"`\n\tMPTCP       bool        `proxy:\"mptcp,omitempty\"`\n\tInterface   string      `proxy:\"interface-name,omitempty\"`\n\tRoutingMark int         `proxy:\"routing-mark,omitempty\"`\n\tIPVersion   C.DNSPrefer `proxy:\"ip-version,omitempty\"`\n\tDialerProxy string      `proxy:\"dialer-proxy,omitempty\"` // don't apply this option into groups, but can set a group name in a proxy\n\n\t//\n\t// The following parameters are used internally, assign value by the structure decoder are disallowed\n\t//\n\tDialerForAPI C.Dialer `proxy:\"-\"` // the dialer used for API usage has higher priority than all the above configurations.\n\tProviderName string   `proxy:\"-\"`\n}\n\nfunc (b *BasicOption) NewDialer(opts []dialer.Option) C.Dialer {\n\tcDialer := b.DialerForAPI\n\tif cDialer == nil {\n\t\tif b.DialerProxy != \"\" {\n\t\t\tcDialer = proxydialer.NewByName(b.DialerProxy)\n\t\t} else {\n\t\t\tcDialer = dialer.NewDialer(opts...)\n\t\t}\n\t}\n\treturn cDialer\n}\n\ntype conn struct {\n\tN.ExtendedConn\n\tchain       C.Chain\n\tpdChain     C.Chain\n\tadapterAddr string\n}\n\nfunc (c *conn) RemoteDestination() string {\n\tif remoteAddr := c.RemoteAddr(); remoteAddr != nil {\n\t\tm := C.Metadata{}\n\t\tif err := m.SetRemoteAddr(remoteAddr); err == nil {\n\t\t\tif m.Valid() {\n\t\t\t\treturn m.String()\n\t\t\t}\n\t\t}\n\t}\n\thost, _, _ := net.SplitHostPort(c.adapterAddr)\n\treturn host\n}\n\n// Chains implements C.Connection\nfunc (c *conn) Chains() C.Chain {\n\treturn c.chain\n}\n\n// ProviderChains implements C.Connection\nfunc (c *conn) ProviderChains() C.Chain {\n\treturn c.pdChain\n}\n\n// AppendToChains implements C.Connection\nfunc (c *conn) AppendToChains(a C.ProxyAdapter) {\n\tc.chain = append(c.chain, a.Name())\n\tc.pdChain = append(c.pdChain, a.ProxyInfo().ProviderName)\n}\n\nfunc (c *conn) Upstream() any {\n\treturn c.ExtendedConn\n}\n\nfunc (c *conn) WriterReplaceable() bool {\n\treturn true\n}\n\nfunc (c *conn) ReaderReplaceable() bool {\n\treturn true\n}\n\nfunc (c *conn) AddRef(ref any) {\n\tc.ExtendedConn = N.NewRefConn(c.ExtendedConn, ref) // add ref for autoCloseProxyAdapter\n}\n\nfunc NewConn(c net.Conn, a C.ProxyAdapter) C.Conn {\n\tif _, ok := c.(syscall.Conn); !ok { // exclusion system conn like *net.TCPConn\n\t\tc = N.NewDeadlineConn(c) // most conn from outbound can't handle readDeadline correctly\n\t}\n\tcc := &conn{N.NewExtendedConn(c), nil, nil, a.Addr()}\n\tcc.AppendToChains(a)\n\treturn cc\n}\n\ntype packetConn struct {\n\tN.EnhancePacketConn\n\tchain       C.Chain\n\tpdChain     C.Chain\n\tadapterName string\n\tconnID      string\n\tadapterAddr string\n\tresolveUDP  func(ctx context.Context, metadata *C.Metadata) error\n}\n\nfunc (c *packetConn) ResolveUDP(ctx context.Context, metadata *C.Metadata) error {\n\treturn c.resolveUDP(ctx, metadata)\n}\n\nfunc (c *packetConn) RemoteDestination() string {\n\thost, _, _ := net.SplitHostPort(c.adapterAddr)\n\treturn host\n}\n\n// Chains implements C.Connection\nfunc (c *packetConn) Chains() C.Chain {\n\treturn c.chain\n}\n\n// ProviderChains implements C.Connection\nfunc (c *packetConn) ProviderChains() C.Chain {\n\treturn c.pdChain\n}\n\n// AppendToChains implements C.Connection\nfunc (c *packetConn) AppendToChains(a C.ProxyAdapter) {\n\tc.chain = append(c.chain, a.Name())\n\tc.pdChain = append(c.pdChain, a.ProxyInfo().ProviderName)\n}\n\nfunc (c *packetConn) LocalAddr() net.Addr {\n\tlAddr := c.EnhancePacketConn.LocalAddr()\n\treturn N.NewCustomAddr(c.adapterName, c.connID, lAddr) // make quic-go's connMultiplexer happy\n}\n\nfunc (c *packetConn) Upstream() any {\n\treturn c.EnhancePacketConn\n}\n\nfunc (c *packetConn) WriterReplaceable() bool {\n\treturn true\n}\n\nfunc (c *packetConn) ReaderReplaceable() bool {\n\treturn true\n}\n\nfunc (c *packetConn) AddRef(ref any) {\n\tc.EnhancePacketConn = N.NewRefPacketConn(c.EnhancePacketConn, ref) // add ref for autoCloseProxyAdapter\n}\n\nfunc newPacketConn(pc net.PacketConn, a ProxyAdapter) C.PacketConn {\n\tepc := N.NewEnhancePacketConn(pc)\n\tif _, ok := pc.(syscall.Conn); !ok { // exclusion system conn like *net.UDPConn\n\t\tepc = N.NewDeadlineEnhancePacketConn(epc) // most conn from outbound can't handle readDeadline correctly\n\t}\n\tcpc := &packetConn{epc, nil, nil, a.Name(), utils.NewUUIDV4().String(), a.Addr(), a.ResolveUDP}\n\tcpc.AppendToChains(a)\n\treturn cpc\n}\n\ntype AddRef interface {\n\tAddRef(ref any)\n}\n\ntype autoCloseProxyAdapter struct {\n\tProxyAdapter\n\tcloseOnce sync.Once\n\tcloseErr  error\n}\n\nfunc (p *autoCloseProxyAdapter) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Conn, err error) {\n\tc, err := p.ProxyAdapter.DialContext(ctx, metadata)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif c, ok := c.(AddRef); ok {\n\t\tc.AddRef(p)\n\t}\n\treturn c, nil\n}\n\nfunc (p *autoCloseProxyAdapter) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {\n\tpc, err := p.ProxyAdapter.ListenPacketContext(ctx, metadata)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif pc, ok := pc.(AddRef); ok {\n\t\tpc.AddRef(p)\n\t}\n\treturn pc, nil\n}\n\nfunc (p *autoCloseProxyAdapter) Close() error {\n\tp.closeOnce.Do(func() {\n\t\tlog.Debugln(\"Closing outdated proxy [%s]\", p.Name())\n\t\truntime.SetFinalizer(p, nil)\n\t\tp.closeErr = p.ProxyAdapter.Close()\n\t})\n\treturn p.closeErr\n}\n\nfunc NewAutoCloseProxyAdapter(adapter ProxyAdapter) ProxyAdapter {\n\tproxy := &autoCloseProxyAdapter{\n\t\tProxyAdapter: adapter,\n\t}\n\t// auto close ProxyAdapter\n\truntime.SetFinalizer(proxy, (*autoCloseProxyAdapter).Close)\n\treturn proxy\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outbound/direct.go",
    "content": "package outbound\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/metacubex/mihomo/component/dialer\"\n\t\"github.com/metacubex/mihomo/component/loopback\"\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\ntype Direct struct {\n\t*Base\n\tloopBack *loopback.Detector\n}\n\ntype DirectOption struct {\n\tBasicOption\n\tName string `proxy:\"name\"`\n}\n\n// DialContext implements C.ProxyAdapter\nfunc (d *Direct) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, error) {\n\tif err := d.loopBack.CheckConn(metadata); err != nil {\n\t\treturn nil, err\n\t}\n\topts := d.DialOptions()\n\topts = append(opts, dialer.WithResolver(resolver.DirectHostResolver))\n\tc, err := dialer.DialContext(ctx, \"tcp\", metadata.RemoteAddress(), opts...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn d.loopBack.NewConn(NewConn(c, d)), nil\n}\n\n// ListenPacketContext implements C.ProxyAdapter\nfunc (d *Direct) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (C.PacketConn, error) {\n\tif err := d.loopBack.CheckPacketConn(metadata); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := d.ResolveUDP(ctx, metadata); err != nil {\n\t\treturn nil, err\n\t}\n\tpc, err := dialer.NewDialer(d.DialOptions()...).ListenPacket(ctx, \"udp\", \"\", metadata.AddrPort())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn d.loopBack.NewPacketConn(newPacketConn(pc, d)), nil\n}\n\nfunc (d *Direct) ResolveUDP(ctx context.Context, metadata *C.Metadata) error {\n\tif (!metadata.Resolved() || resolver.DirectHostResolver != resolver.DefaultResolver) && metadata.Host != \"\" {\n\t\tip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, resolver.DirectHostResolver)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"can't resolve ip: %w\", err)\n\t\t}\n\t\tmetadata.DstIP = ip\n\t}\n\treturn nil\n}\n\nfunc (d *Direct) IsL3Protocol(metadata *C.Metadata) bool {\n\treturn true // tell DNSDialer don't send domain to DialContext, avoid lookback to DefaultResolver\n}\n\nfunc NewDirectWithOption(option DirectOption) *Direct {\n\treturn &Direct{\n\t\tBase: NewBase(BaseOption{\n\t\t\tName:         option.Name,\n\t\t\tType:         C.Direct,\n\t\t\tProviderName: option.ProviderName,\n\t\t\tUDP:          true,\n\t\t\tTFO:          option.TFO,\n\t\t\tMPTCP:        option.MPTCP,\n\t\t\tInterface:    option.Interface,\n\t\t\tRoutingMark:  option.RoutingMark,\n\t\t\tPrefer:       option.IPVersion,\n\t\t}),\n\t\tloopBack: loopback.NewDetector(),\n\t}\n}\n\nfunc NewDirect() *Direct {\n\treturn &Direct{\n\t\tBase: NewBase(BaseOption{\n\t\t\tName:   \"DIRECT\",\n\t\t\tType:   C.Direct,\n\t\t\tUDP:    true,\n\t\t\tPrefer: C.DualStack,\n\t\t}),\n\t\tloopBack: loopback.NewDetector(),\n\t}\n}\n\nfunc NewCompatible() *Direct {\n\treturn &Direct{\n\t\tBase: NewBase(BaseOption{\n\t\t\tName:   \"COMPATIBLE\",\n\t\t\tType:   C.Compatible,\n\t\t\tUDP:    true,\n\t\t\tPrefer: C.DualStack,\n\t\t}),\n\t\tloopBack: loopback.NewDetector(),\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outbound/dns.go",
    "content": "package outbound\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"net/netip\"\n\t\"time\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/pool\"\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\ntype Dns struct {\n\t*Base\n}\n\ntype DnsOption struct {\n\tBasicOption\n\tName string `proxy:\"name\"`\n}\n\n// DialContext implements C.ProxyAdapter\nfunc (d *Dns) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, error) {\n\tleft, right := N.Pipe()\n\tgo resolver.RelayDnsConn(context.Background(), right, 0)\n\treturn NewConn(left, d), nil\n}\n\n// ListenPacketContext implements C.ProxyAdapter\nfunc (d *Dns) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (C.PacketConn, error) {\n\tlog.Debugln(\"[DNS] hijack udp:%s from %s\", metadata.RemoteAddress(), metadata.SourceAddrPort())\n\tif err := d.ResolveUDP(ctx, metadata); err != nil {\n\t\treturn nil, err\n\t}\n\n\tctx, cancel := context.WithCancel(context.Background())\n\n\treturn newPacketConn(&dnsPacketConn{\n\t\tresponse: make(chan dnsPacket, 1),\n\t\tctx:      ctx,\n\t\tcancel:   cancel,\n\t}, d), nil\n}\n\nfunc (d *Dns) ResolveUDP(ctx context.Context, metadata *C.Metadata) error {\n\tif !metadata.Resolved() {\n\t\tmetadata.DstIP = netip.AddrFrom4([4]byte{127, 0, 0, 2})\n\t}\n\treturn nil\n}\n\ntype dnsPacket struct {\n\tdata []byte\n\tput  func()\n\taddr net.Addr\n}\n\n// dnsPacketConn implements net.PacketConn\ntype dnsPacketConn struct {\n\tresponse chan dnsPacket\n\tctx      context.Context\n\tcancel   context.CancelFunc\n}\n\nfunc (d *dnsPacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {\n\tselect {\n\tcase packet := <-d.response:\n\t\treturn packet.data, packet.put, packet.addr, nil\n\tcase <-d.ctx.Done():\n\t\treturn nil, nil, nil, net.ErrClosed\n\t}\n}\n\nfunc (d *dnsPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {\n\tselect {\n\tcase packet := <-d.response:\n\t\tn = copy(p, packet.data)\n\t\tif packet.put != nil {\n\t\t\tpacket.put()\n\t\t}\n\t\treturn n, packet.addr, nil\n\tcase <-d.ctx.Done():\n\t\treturn 0, nil, net.ErrClosed\n\t}\n}\n\nfunc (d *dnsPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {\n\tselect {\n\tcase <-d.ctx.Done():\n\t\treturn 0, net.ErrClosed\n\tdefault:\n\t}\n\n\tif len(p) > resolver.SafeDnsPacketSize {\n\t\t// wtf???\n\t\treturn len(p), nil\n\t}\n\n\tbuf := pool.Get(resolver.SafeDnsPacketSize)\n\tput := func() { _ = pool.Put(buf) }\n\tcopy(buf, p) // avoid p be changed after WriteTo returned\n\n\tgo func() { // don't block the WriteTo function\n\t\tctx, cancel := context.WithTimeout(d.ctx, resolver.DefaultDnsRelayTimeout)\n\t\tdefer cancel()\n\n\t\tbuf, err = resolver.RelayDnsPacket(ctx, buf[:len(p)], buf)\n\t\tif err != nil {\n\t\t\tput()\n\t\t\treturn\n\t\t}\n\n\t\tpacket := dnsPacket{\n\t\t\tdata: buf,\n\t\t\tput:  put,\n\t\t\taddr: addr,\n\t\t}\n\t\tselect {\n\t\tcase d.response <- packet:\n\t\t\tbreak\n\t\tcase <-d.ctx.Done():\n\t\t\tput()\n\t\t}\n\t}()\n\treturn len(p), nil\n}\n\nfunc (d *dnsPacketConn) Close() error {\n\td.cancel()\n\treturn nil\n}\n\nfunc (*dnsPacketConn) LocalAddr() net.Addr {\n\treturn &net.UDPAddr{\n\t\tIP:   net.IPv4(127, 0, 0, 1),\n\t\tPort: 53,\n\t\tZone: \"\",\n\t}\n}\n\nfunc (*dnsPacketConn) SetDeadline(t time.Time) error {\n\treturn nil\n}\n\nfunc (*dnsPacketConn) SetReadDeadline(t time.Time) error {\n\treturn nil\n}\n\nfunc (*dnsPacketConn) SetWriteDeadline(t time.Time) error {\n\treturn nil\n}\n\nfunc NewDnsWithOption(option DnsOption) *Dns {\n\treturn &Dns{\n\t\tBase: NewBase(BaseOption{\n\t\t\tName:         option.Name,\n\t\t\tType:         C.Dns,\n\t\t\tProviderName: option.ProviderName,\n\t\t\tUDP:          true,\n\t\t\tTFO:          option.TFO,\n\t\t\tMPTCP:        option.MPTCP,\n\t\t\tInterface:    option.Interface,\n\t\t\tRoutingMark:  option.RoutingMark,\n\t\t\tPrefer:       option.IPVersion,\n\t\t}),\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outbound/ech.go",
    "content": "package outbound\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\n\t\"github.com/metacubex/mihomo/component/ech\"\n\t\"github.com/metacubex/mihomo/component/resolver\"\n)\n\ntype ECHOptions struct {\n\tEnable bool   `proxy:\"enable,omitempty\" obfs:\"enable,omitempty\"`\n\tConfig string `proxy:\"config,omitempty\" obfs:\"config,omitempty\"`\n\n\tQueryServerName string `proxy:\"query-server-name,omitempty\" obfs:\"query-server-name,omitempty\"`\n}\n\nfunc (o ECHOptions) Parse() (*ech.Config, error) {\n\tif !o.Enable {\n\t\treturn nil, nil\n\t}\n\techConfig := &ech.Config{}\n\tif o.Config != \"\" {\n\t\tlist, err := base64.StdEncoding.DecodeString(o.Config)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"base64 decode ech config string failed: %v\", err)\n\t\t}\n\t\techConfig.GetEncryptedClientHelloConfigList = func(ctx context.Context, serverName string) ([]byte, error) {\n\t\t\treturn list, nil\n\t\t}\n\t} else {\n\t\techConfig.GetEncryptedClientHelloConfigList = func(ctx context.Context, serverName string) ([]byte, error) {\n\t\t\tif o.QueryServerName != \"\" { // overrides the domain name used for ECH HTTPS record queries\n\t\t\t\tserverName = o.QueryServerName\n\t\t\t}\n\t\t\treturn resolver.ResolveECHWithResolver(ctx, serverName, resolver.ProxyServerHostResolver)\n\t\t}\n\t}\n\treturn echConfig, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outbound/http.go",
    "content": "package outbound\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"strconv\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/component/ca\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/tls\"\n)\n\ntype Http struct {\n\t*Base\n\tuser      string\n\tpass      string\n\ttlsConfig *tls.Config\n\toption    *HttpOption\n}\n\ntype HttpOption struct {\n\tBasicOption\n\tName           string            `proxy:\"name\"`\n\tServer         string            `proxy:\"server\"`\n\tPort           int               `proxy:\"port\"`\n\tUserName       string            `proxy:\"username,omitempty\"`\n\tPassword       string            `proxy:\"password,omitempty\"`\n\tTLS            bool              `proxy:\"tls,omitempty\"`\n\tSNI            string            `proxy:\"sni,omitempty\"`\n\tSkipCertVerify bool              `proxy:\"skip-cert-verify,omitempty\"`\n\tFingerprint    string            `proxy:\"fingerprint,omitempty\"`\n\tCertificate    string            `proxy:\"certificate,omitempty\"`\n\tPrivateKey     string            `proxy:\"private-key,omitempty\"`\n\tHeaders        map[string]string `proxy:\"headers,omitempty\"`\n}\n\n// StreamConnContext implements C.ProxyAdapter\nfunc (h *Http) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (net.Conn, error) {\n\tif h.tlsConfig != nil {\n\t\tcc := tls.Client(c, h.tlsConfig)\n\t\terr := cc.HandshakeContext(ctx)\n\t\tc = cc\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%s connect error: %w\", h.addr, err)\n\t\t}\n\t}\n\n\tif err := h.shakeHandContext(ctx, c, metadata); err != nil {\n\t\treturn nil, err\n\t}\n\treturn c, nil\n}\n\n// DialContext implements C.ProxyAdapter\nfunc (h *Http) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Conn, err error) {\n\tc, err := h.dialer.DialContext(ctx, \"tcp\", h.addr)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s connect error: %w\", h.addr, err)\n\t}\n\n\tdefer func(c net.Conn) {\n\t\tsafeConnClose(c, err)\n\t}(c)\n\n\tc, err = h.StreamConnContext(ctx, c, metadata)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn NewConn(c, h), nil\n}\n\n// ProxyInfo implements C.ProxyAdapter\nfunc (h *Http) ProxyInfo() C.ProxyInfo {\n\tinfo := h.Base.ProxyInfo()\n\tinfo.DialerProxy = h.option.DialerProxy\n\treturn info\n}\n\nfunc (h *Http) shakeHandContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (err error) {\n\tif ctx.Done() != nil {\n\t\tdone := N.SetupContextForConn(ctx, c)\n\t\tdefer done(&err)\n\t}\n\n\taddr := metadata.RemoteAddress()\n\tHeaderString := \"CONNECT \" + addr + \" HTTP/1.1\\r\\n\"\n\ttempHeaders := map[string]string{\n\t\t\"Host\":             addr,\n\t\t\"User-Agent\":       \"Go-http-client/1.1\",\n\t\t\"Proxy-Connection\": \"Keep-Alive\",\n\t}\n\n\tfor key, value := range h.option.Headers {\n\t\ttempHeaders[key] = value\n\t}\n\n\tif h.user != \"\" && h.pass != \"\" {\n\t\tauth := h.user + \":\" + h.pass\n\t\ttempHeaders[\"Proxy-Authorization\"] = \"Basic \" + base64.StdEncoding.EncodeToString([]byte(auth))\n\t}\n\n\tfor key, value := range tempHeaders {\n\t\tHeaderString += key + \": \" + value + \"\\r\\n\"\n\t}\n\n\tHeaderString += \"\\r\\n\"\n\n\t_, err = c.Write([]byte(HeaderString))\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tresp, err := http.ReadResponse(bufio.NewReader(c), nil)\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif resp.StatusCode == http.StatusOK {\n\t\treturn nil\n\t}\n\n\tif resp.StatusCode == http.StatusProxyAuthRequired {\n\t\treturn errors.New(\"HTTP need auth\")\n\t}\n\n\tif resp.StatusCode == http.StatusMethodNotAllowed {\n\t\treturn errors.New(\"CONNECT method not allowed by proxy\")\n\t}\n\n\tif resp.StatusCode >= http.StatusInternalServerError {\n\t\treturn errors.New(resp.Status)\n\t}\n\n\treturn fmt.Errorf(\"can not connect remote err code: %d\", resp.StatusCode)\n}\n\nfunc NewHttp(option HttpOption) (*Http, error) {\n\tvar tlsConfig *tls.Config\n\tif option.TLS {\n\t\tsni := option.Server\n\t\tif option.SNI != \"\" {\n\t\t\tsni = option.SNI\n\t\t}\n\t\tvar err error\n\t\ttlsConfig, err = ca.GetTLSConfig(ca.Option{\n\t\t\tTLSConfig: &tls.Config{\n\t\t\t\tInsecureSkipVerify: option.SkipCertVerify,\n\t\t\t\tServerName:         sni,\n\t\t\t},\n\t\t\tFingerprint: option.Fingerprint,\n\t\t\tCertificate: option.Certificate,\n\t\t\tPrivateKey:  option.PrivateKey,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\toutbound := &Http{\n\t\tBase: NewBase(BaseOption{\n\t\t\tName:         option.Name,\n\t\t\tAddr:         net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),\n\t\t\tType:         C.Http,\n\t\t\tProviderName: option.ProviderName,\n\t\t\tTFO:          option.TFO,\n\t\t\tMPTCP:        option.MPTCP,\n\t\t\tInterface:    option.Interface,\n\t\t\tRoutingMark:  option.RoutingMark,\n\t\t\tPrefer:       option.IPVersion,\n\t\t}),\n\t\tuser:      option.UserName,\n\t\tpass:      option.Password,\n\t\ttlsConfig: tlsConfig,\n\t\toption:    &option,\n\t}\n\toutbound.dialer = option.NewDialer(outbound.DialOptions())\n\treturn outbound, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outbound/hysteria.go",
    "content": "package outbound\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/netip\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/component/ca\"\n\t\"github.com/metacubex/mihomo/component/dialer\"\n\t\"github.com/metacubex/mihomo/component/ech\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n\thyCongestion \"github.com/metacubex/mihomo/transport/hysteria/congestion\"\n\t\"github.com/metacubex/mihomo/transport/hysteria/core\"\n\t\"github.com/metacubex/mihomo/transport/hysteria/obfs\"\n\t\"github.com/metacubex/mihomo/transport/hysteria/pmtud_fix\"\n\t\"github.com/metacubex/mihomo/transport/hysteria/transport\"\n\t\"github.com/metacubex/mihomo/transport/hysteria/utils\"\n\n\t\"github.com/metacubex/tls\"\n\n\t\"github.com/metacubex/quic-go\"\n\t\"github.com/metacubex/quic-go/congestion\"\n\tM \"github.com/metacubex/sing/common/metadata\"\n)\n\nconst (\n\tmbpsToBps = 125000\n\n\tDefaultStreamReceiveWindow     = 15728640 // 15 MB/s\n\tDefaultConnectionReceiveWindow = 67108864 // 64 MB/s\n\n\tDefaultALPN        = \"hysteria\"\n\tDefaultProtocol    = \"udp\"\n\tDefaultHopInterval = 10\n)\n\ntype Hysteria struct {\n\t*Base\n\n\toption *HysteriaOption\n\tclient *core.Client\n\n\ttlsConfig *tls.Config\n\techConfig *ech.Config\n}\n\nfunc (h *Hysteria) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, error) {\n\ttcpConn, err := h.client.DialTCP(metadata.String(), metadata.DstPort, h.genHdc(ctx))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn NewConn(tcpConn, h), nil\n}\n\nfunc (h *Hysteria) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (C.PacketConn, error) {\n\tif err := h.ResolveUDP(ctx, metadata); err != nil {\n\t\treturn nil, err\n\t}\n\tudpConn, err := h.client.DialUDP(h.genHdc(ctx))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn newPacketConn(&hyPacketConn{udpConn}, h), nil\n}\n\nfunc (h *Hysteria) genHdc(ctx context.Context) utils.PacketDialer {\n\treturn &hyDialerWithContext{\n\t\tctx: context.Background(),\n\t\thyDialer: func(network string, rAddr net.Addr) (net.PacketConn, error) {\n\t\t\trAddrPort, _ := netip.ParseAddrPort(rAddr.String())\n\t\t\treturn h.dialer.ListenPacket(ctx, network, \"\", rAddrPort)\n\t\t},\n\t\tremoteAddr: func(addr string) (net.Addr, error) {\n\t\t\tudpAddr, err := resolveUDPAddr(ctx, \"udp\", addr, h.prefer)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\terr = h.echConfig.ClientHandle(ctx, h.tlsConfig)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn udpAddr, nil\n\t\t},\n\t}\n}\n\n// ProxyInfo implements C.ProxyAdapter\nfunc (h *Hysteria) ProxyInfo() C.ProxyInfo {\n\tinfo := h.Base.ProxyInfo()\n\tinfo.DialerProxy = h.option.DialerProxy\n\treturn info\n}\n\ntype HysteriaOption struct {\n\tBasicOption\n\tName                string     `proxy:\"name\"`\n\tServer              string     `proxy:\"server\"`\n\tPort                int        `proxy:\"port,omitempty\"`\n\tPorts               string     `proxy:\"ports,omitempty\"`\n\tProtocol            string     `proxy:\"protocol,omitempty\"`\n\tObfsProtocol        string     `proxy:\"obfs-protocol,omitempty\"` // compatible with Stash\n\tUp                  string     `proxy:\"up\"`\n\tUpSpeed             int        `proxy:\"up-speed,omitempty\"` // compatible with Stash\n\tDown                string     `proxy:\"down\"`\n\tDownSpeed           int        `proxy:\"down-speed,omitempty\"` // compatible with Stash\n\tAuth                string     `proxy:\"auth,omitempty\"`\n\tAuthString          string     `proxy:\"auth-str,omitempty\"`\n\tObfs                string     `proxy:\"obfs,omitempty\"`\n\tSNI                 string     `proxy:\"sni,omitempty\"`\n\tECHOpts             ECHOptions `proxy:\"ech-opts,omitempty\"`\n\tSkipCertVerify      bool       `proxy:\"skip-cert-verify,omitempty\"`\n\tFingerprint         string     `proxy:\"fingerprint,omitempty\"`\n\tCertificate         string     `proxy:\"certificate,omitempty\"`\n\tPrivateKey          string     `proxy:\"private-key,omitempty\"`\n\tALPN                []string   `proxy:\"alpn,omitempty\"`\n\tReceiveWindowConn   int        `proxy:\"recv-window-conn,omitempty\"`\n\tReceiveWindow       int        `proxy:\"recv-window,omitempty\"`\n\tDisableMTUDiscovery bool       `proxy:\"disable-mtu-discovery,omitempty\"`\n\tFastOpen            bool       `proxy:\"fast-open,omitempty\"`\n\tHopInterval         int        `proxy:\"hop-interval,omitempty\"`\n}\n\nfunc (c *HysteriaOption) Speed() (uint64, uint64, error) {\n\tvar up, down uint64\n\tup = StringToBps(c.Up)\n\tif up == 0 {\n\t\treturn 0, 0, fmt.Errorf(\"invaild upload speed: %s\", c.Up)\n\t}\n\n\tdown = StringToBps(c.Down)\n\tif down == 0 {\n\t\treturn 0, 0, fmt.Errorf(\"invaild download speed: %s\", c.Down)\n\t}\n\n\treturn up, down, nil\n}\n\nfunc NewHysteria(option HysteriaOption) (*Hysteria, error) {\n\tclientTransport := &transport.ClientTransport{}\n\taddr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))\n\tports := option.Ports\n\n\tserverName := option.Server\n\tif option.SNI != \"\" {\n\t\tserverName = option.SNI\n\t}\n\n\ttlsConfig, err := ca.GetTLSConfig(ca.Option{\n\t\tTLSConfig: &tls.Config{\n\t\t\tServerName:         serverName,\n\t\t\tInsecureSkipVerify: option.SkipCertVerify,\n\t\t\tMinVersion:         tls.VersionTLS13,\n\t\t},\n\t\tFingerprint: option.Fingerprint,\n\t\tCertificate: option.Certificate,\n\t\tPrivateKey:  option.PrivateKey,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif option.ALPN != nil { // structure's Decode will ensure value not nil when input has value even it was set an empty array\n\t\ttlsConfig.NextProtos = option.ALPN\n\t} else {\n\t\ttlsConfig.NextProtos = []string{DefaultALPN}\n\t}\n\n\techConfig, err := option.ECHOpts.Parse()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\ttlsClientConfig := tlsConfig\n\n\tquicConfig := &quic.Config{\n\t\tInitialStreamReceiveWindow:     uint64(option.ReceiveWindowConn),\n\t\tMaxStreamReceiveWindow:         uint64(option.ReceiveWindowConn),\n\t\tInitialConnectionReceiveWindow: uint64(option.ReceiveWindow),\n\t\tMaxConnectionReceiveWindow:     uint64(option.ReceiveWindow),\n\t\tKeepAlivePeriod:                10 * time.Second,\n\t\tDisablePathMTUDiscovery:        option.DisableMTUDiscovery,\n\t\tEnableDatagrams:                true,\n\t}\n\tif option.ObfsProtocol != \"\" {\n\t\toption.Protocol = option.ObfsProtocol\n\t}\n\tif option.Protocol == \"\" {\n\t\toption.Protocol = DefaultProtocol\n\t}\n\tif option.HopInterval == 0 {\n\t\toption.HopInterval = DefaultHopInterval\n\t}\n\thopInterval := time.Duration(int64(option.HopInterval)) * time.Second\n\tif option.ReceiveWindow == 0 {\n\t\tquicConfig.InitialStreamReceiveWindow = DefaultStreamReceiveWindow / 10\n\t\tquicConfig.MaxStreamReceiveWindow = DefaultStreamReceiveWindow\n\t}\n\tif option.ReceiveWindow == 0 {\n\t\tquicConfig.InitialConnectionReceiveWindow = DefaultConnectionReceiveWindow / 10\n\t\tquicConfig.MaxConnectionReceiveWindow = DefaultConnectionReceiveWindow\n\t}\n\tif !quicConfig.DisablePathMTUDiscovery && pmtud_fix.DisablePathMTUDiscovery {\n\t\tlog.Infoln(\"hysteria: Path MTU Discovery is not yet supported on this platform\")\n\t}\n\n\tvar auth = []byte(option.AuthString)\n\tif option.Auth != \"\" {\n\t\tauth, err = base64.StdEncoding.DecodeString(option.Auth)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tvar obfuscator obfs.Obfuscator\n\tif len(option.Obfs) > 0 {\n\t\tobfuscator = obfs.NewXPlusObfuscator([]byte(option.Obfs))\n\t}\n\n\tup, down, err := option.Speed()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif option.UpSpeed != 0 {\n\t\tup = uint64(option.UpSpeed * mbpsToBps)\n\t}\n\tif option.DownSpeed != 0 {\n\t\tdown = uint64(option.DownSpeed * mbpsToBps)\n\t}\n\tclient, err := core.NewClient(\n\t\taddr, ports, option.Protocol, auth, tlsClientConfig, quicConfig, clientTransport, up, down, func(refBPS uint64) congestion.CongestionControl {\n\t\t\treturn hyCongestion.NewBrutalSender(congestion.ByteCount(refBPS))\n\t\t}, obfuscator, hopInterval, option.FastOpen,\n\t)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"hysteria %s create error: %w\", addr, err)\n\t}\n\toutbound := &Hysteria{\n\t\tBase: NewBase(BaseOption{\n\t\t\tName:         option.Name,\n\t\t\tAddr:         addr,\n\t\t\tType:         C.Hysteria,\n\t\t\tProviderName: option.ProviderName,\n\t\t\tUDP:          true,\n\t\t\tTFO:          option.FastOpen,\n\t\t\tInterface:    option.Interface,\n\t\t\tRoutingMark:  option.RoutingMark,\n\t\t\tPrefer:       option.IPVersion,\n\t\t}),\n\t\toption:    &option,\n\t\tclient:    client,\n\t\ttlsConfig: tlsClientConfig,\n\t\techConfig: echConfig,\n\t}\n\toutbound.dialer = option.NewDialer(outbound.DialOptions())\n\n\treturn outbound, nil\n}\n\n// Close implements C.ProxyAdapter\nfunc (h *Hysteria) Close() error {\n\tif h.client != nil {\n\t\treturn h.client.Close()\n\t}\n\treturn nil\n}\n\ntype hyPacketConn struct {\n\tcore.UDPConn\n}\n\nfunc (c *hyPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {\n\tb, addrStr, err := c.UDPConn.ReadFrom()\n\tif err != nil {\n\t\treturn\n\t}\n\tn = copy(p, b)\n\taddr = M.ParseSocksaddr(addrStr).UDPAddr()\n\treturn\n}\n\nfunc (c *hyPacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {\n\tb, addrStr, err := c.UDPConn.ReadFrom()\n\tif err != nil {\n\t\treturn\n\t}\n\tdata = b\n\taddr = M.ParseSocksaddr(addrStr).UDPAddr()\n\treturn\n}\n\nfunc (c *hyPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {\n\terr = c.UDPConn.WriteTo(p, M.SocksaddrFromNet(addr).String())\n\tif err != nil {\n\t\treturn\n\t}\n\tn = len(p)\n\treturn\n}\n\ntype hyDialerWithContext struct {\n\thyDialer   func(network string, rAddr net.Addr) (net.PacketConn, error)\n\tctx        context.Context\n\tremoteAddr func(host string) (net.Addr, error)\n}\n\nfunc (h *hyDialerWithContext) ListenPacket(rAddr net.Addr) (net.PacketConn, error) {\n\tnetwork := \"udp\"\n\tif addrPort, err := netip.ParseAddrPort(rAddr.String()); err == nil {\n\t\tnetwork = dialer.ParseNetwork(network, addrPort.Addr())\n\t}\n\treturn h.hyDialer(network, rAddr)\n}\n\nfunc (h *hyDialerWithContext) Context() context.Context {\n\treturn h.ctx\n}\n\nfunc (h *hyDialerWithContext) RemoteAddr(host string) (net.Addr, error) {\n\treturn h.remoteAddr(host)\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outbound/hysteria2.go",
    "content": "package outbound\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"strconv\"\n\t\"time\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"github.com/metacubex/mihomo/component/ca\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/transport/tuic/common\"\n\n\t\"github.com/metacubex/quic-go\"\n\tqtls \"github.com/metacubex/sing-quic\"\n\t\"github.com/metacubex/sing-quic/hysteria2\"\n\tM \"github.com/metacubex/sing/common/metadata\"\n\t\"github.com/metacubex/tls\"\n)\n\nconst minHopInterval = 5\nconst defaultHopInterval = 30\n\ntype Hysteria2 struct {\n\t*Base\n\n\toption *Hysteria2Option\n\tclient *hysteria2.Client\n}\n\ntype Hysteria2Option struct {\n\tBasicOption\n\tName           string     `proxy:\"name\"`\n\tServer         string     `proxy:\"server\"`\n\tPort           int        `proxy:\"port,omitempty\"`\n\tPorts          string     `proxy:\"ports,omitempty\"`\n\tHopInterval    string     `proxy:\"hop-interval,omitempty\"`\n\tUp             string     `proxy:\"up,omitempty\"`\n\tDown           string     `proxy:\"down,omitempty\"`\n\tPassword       string     `proxy:\"password,omitempty\"`\n\tObfs           string     `proxy:\"obfs,omitempty\"`\n\tObfsPassword   string     `proxy:\"obfs-password,omitempty\"`\n\tSNI            string     `proxy:\"sni,omitempty\"`\n\tECHOpts        ECHOptions `proxy:\"ech-opts,omitempty\"`\n\tSkipCertVerify bool       `proxy:\"skip-cert-verify,omitempty\"`\n\tFingerprint    string     `proxy:\"fingerprint,omitempty\"`\n\tCertificate    string     `proxy:\"certificate,omitempty\"`\n\tPrivateKey     string     `proxy:\"private-key,omitempty\"`\n\tALPN           []string   `proxy:\"alpn,omitempty\"`\n\tCWND           int        `proxy:\"cwnd,omitempty\"`\n\tBBRProfile     string     `proxy:\"bbr-profile,omitempty\"`\n\tUdpMTU         int        `proxy:\"udp-mtu,omitempty\"`\n\n\t// quic-go special config\n\tInitialStreamReceiveWindow     uint64 `proxy:\"initial-stream-receive-window,omitempty\"`\n\tMaxStreamReceiveWindow         uint64 `proxy:\"max-stream-receive-window,omitempty\"`\n\tInitialConnectionReceiveWindow uint64 `proxy:\"initial-connection-receive-window,omitempty\"`\n\tMaxConnectionReceiveWindow     uint64 `proxy:\"max-connection-receive-window,omitempty\"`\n}\n\nfunc (h *Hysteria2) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Conn, err error) {\n\tc, err := h.client.DialConn(ctx, M.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewConn(c, h), nil\n}\n\nfunc (h *Hysteria2) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {\n\tif err = h.ResolveUDP(ctx, metadata); err != nil {\n\t\treturn nil, err\n\t}\n\tpc, err := h.client.ListenPacket(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif pc == nil {\n\t\treturn nil, errors.New(\"packetConn is nil\")\n\t}\n\treturn newPacketConn(N.NewThreadSafePacketConn(pc), h), nil\n}\n\n// Close implements C.ProxyAdapter\nfunc (h *Hysteria2) Close() error {\n\tif h.client != nil {\n\t\treturn h.client.CloseWithError(errors.New(\"proxy removed\"))\n\t}\n\treturn nil\n}\n\n// ProxyInfo implements C.ProxyAdapter\nfunc (h *Hysteria2) ProxyInfo() C.ProxyInfo {\n\tinfo := h.Base.ProxyInfo()\n\tinfo.DialerProxy = h.option.DialerProxy\n\treturn info\n}\n\nfunc NewHysteria2(option Hysteria2Option) (*Hysteria2, error) {\n\taddr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))\n\toutbound := &Hysteria2{\n\t\tBase: NewBase(BaseOption{\n\t\t\tName:         option.Name,\n\t\t\tAddr:         addr,\n\t\t\tType:         C.Hysteria2,\n\t\t\tProviderName: option.ProviderName,\n\t\t\tUDP:          true,\n\t\t\tInterface:    option.Interface,\n\t\t\tRoutingMark:  option.RoutingMark,\n\t\t\tPrefer:       option.IPVersion,\n\t\t}),\n\t\toption: &option,\n\t}\n\toutbound.dialer = option.NewDialer(outbound.DialOptions())\n\n\tvar salamanderPassword string\n\tif len(option.Obfs) > 0 {\n\t\tif option.ObfsPassword == \"\" {\n\t\t\treturn nil, errors.New(\"missing obfs password\")\n\t\t}\n\t\tswitch option.Obfs {\n\t\tcase hysteria2.ObfsTypeSalamander:\n\t\t\tsalamanderPassword = option.ObfsPassword\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unknown obfs type: %s\", option.Obfs)\n\t\t}\n\t}\n\n\tserverName := option.Server\n\tif option.SNI != \"\" {\n\t\tserverName = option.SNI\n\t}\n\n\ttlsConfig, err := ca.GetTLSConfig(ca.Option{\n\t\tTLSConfig: &tls.Config{\n\t\t\tServerName:         serverName,\n\t\t\tInsecureSkipVerify: option.SkipCertVerify,\n\t\t\tMinVersion:         tls.VersionTLS13,\n\t\t},\n\t\tFingerprint: option.Fingerprint,\n\t\tCertificate: option.Certificate,\n\t\tPrivateKey:  option.PrivateKey,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif option.ALPN != nil { // structure's Decode will ensure value not nil when input has value even it was set an empty array\n\t\ttlsConfig.NextProtos = option.ALPN\n\t}\n\n\ttlsClientConfig := tlsConfig\n\techConfig, err := option.ECHOpts.Parse()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif option.UdpMTU == 0 {\n\t\t// \"1200\" from quic-go's MaxDatagramSize\n\t\t// \"-3\" from quic-go's DatagramFrame.MaxDataLen\n\t\toption.UdpMTU = 1200 - 3\n\t}\n\n\tquicConfig := &quic.Config{\n\t\tInitialStreamReceiveWindow:     option.InitialStreamReceiveWindow,\n\t\tMaxStreamReceiveWindow:         option.MaxStreamReceiveWindow,\n\t\tInitialConnectionReceiveWindow: option.InitialConnectionReceiveWindow,\n\t\tMaxConnectionReceiveWindow:     option.MaxConnectionReceiveWindow,\n\t}\n\n\tclientOptions := hysteria2.ClientOptions{\n\t\tContext:            context.TODO(),\n\t\tLogger:             log.SingLogger,\n\t\tSendBPS:            StringToBps(option.Up),\n\t\tReceiveBPS:         StringToBps(option.Down),\n\t\tSalamanderPassword: salamanderPassword,\n\t\tPassword:           option.Password,\n\t\tTLSConfig:          tlsClientConfig,\n\t\tQUICConfig:         quicConfig,\n\t\tUDPDisabled:        false,\n\t\tUdpMTU:             option.UdpMTU,\n\t\tServerAddress:      M.ParseSocksaddr(addr),\n\t\tPacketListener:     outbound.dialer,\n\t\tQuicDialer: qtls.QuicDialerFunc(func(ctx context.Context, addr string, dialer qtls.PacketDialer, tlsCfg *tls.Config, cfg *quic.Config, early bool) (net.PacketConn, *quic.Conn, error) {\n\t\t\terr := echConfig.ClientHandle(ctx, tlsCfg)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, nil, err\n\t\t\t}\n\t\t\treturn common.DialQuic(ctx, addr, outbound.DialOptions(), dialer, tlsCfg, cfg, early)\n\t\t}),\n\t\tSetBBRCongestion: func(quicConn *quic.Conn) {\n\t\t\tcommon.SetCongestionController(quicConn, \"bbr\", option.CWND, option.BBRProfile)\n\t\t},\n\t}\n\n\tvar serverPorts []uint16\n\tif option.Ports != \"\" {\n\t\tranges, err := utils.NewUnsignedRanges[uint16](option.Ports)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tranges.Range(func(port uint16) bool {\n\t\t\tserverPorts = append(serverPorts, port)\n\t\t\treturn true\n\t\t})\n\t\tif len(serverPorts) > 0 {\n\t\t\thopRange, err := utils.NewUnsignedRange[uint64](option.HopInterval)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tstart, end := hopRange.Start(), hopRange.End()\n\t\t\tif start == 0 {\n\t\t\t\tstart = defaultHopInterval\n\t\t\t} else if start < minHopInterval {\n\t\t\t\tstart = minHopInterval\n\t\t\t}\n\t\t\tif end < start {\n\t\t\t\tend = start\n\t\t\t}\n\t\t\tclientOptions.HopInterval = time.Duration(start) * time.Second\n\t\t\tclientOptions.HopIntervalMax = time.Duration(end) * time.Second\n\t\t\tclientOptions.ServerPorts = serverPorts\n\t\t}\n\t}\n\tif option.Port == 0 && len(serverPorts) == 0 {\n\t\treturn nil, errors.New(\"invalid port\")\n\t}\n\n\tclient, err := hysteria2.NewClient(clientOptions)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\toutbound.client = client\n\n\treturn outbound, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outbound/masque.go",
    "content": "package outbound\n\nimport (\n\t\"context\"\n\t\"crypto/ecdsa\"\n\t\"crypto/x509\"\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/netip\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/atomic\"\n\t\"github.com/metacubex/mihomo/common/contextutils\"\n\t\"github.com/metacubex/mihomo/common/pool\"\n\t\"github.com/metacubex/mihomo/component/dialer\"\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/dns\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/transport/masque\"\n\t\"github.com/metacubex/mihomo/transport/tuic/common\"\n\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/quic-go\"\n\twireguard \"github.com/metacubex/sing-wireguard\"\n\tM \"github.com/metacubex/sing/common/metadata\"\n\t\"github.com/metacubex/tls\"\n)\n\ntype Masque struct {\n\t*Base\n\ttlsConfig   *tls.Config\n\tquicConfig  *quic.Config\n\ttunDevice   wireguard.Device\n\tresolver    resolver.Resolver\n\turi         string\n\th2Transport *http.Transport\n\n\trunCtx    context.Context\n\trunCancel context.CancelFunc\n\trunMutex  sync.Mutex\n\trunning   atomic.Bool\n\trunDevice atomic.Bool\n\n\toption MasqueOption\n}\n\ntype MasqueOption struct {\n\tBasicOption\n\tName           string `proxy:\"name\"`\n\tServer         string `proxy:\"server\"`\n\tPort           int    `proxy:\"port\"`\n\tPrivateKey     string `proxy:\"private-key\"`\n\tPublicKey      string `proxy:\"public-key\"`\n\tIp             string `proxy:\"ip,omitempty\"`\n\tIpv6           string `proxy:\"ipv6,omitempty\"`\n\tURI            string `proxy:\"uri,omitempty\"`\n\tSNI            string `proxy:\"sni,omitempty\"`\n\tMTU            int    `proxy:\"mtu,omitempty\"`\n\tUDP            bool   `proxy:\"udp,omitempty\"`\n\tSkipCertVerify bool   `proxy:\"skip-cert-verify,omitempty\"`\n\tNetwork        string `proxy:\"network,omitempty\"`\n\n\tCongestionController string `proxy:\"congestion-controller,omitempty\"`\n\tCWND                 int    `proxy:\"cwnd,omitempty\"`\n\tBBRProfile           string `proxy:\"bbr-profile,omitempty\"`\n\n\tRemoteDnsResolve bool     `proxy:\"remote-dns-resolve,omitempty\"`\n\tDns              []string `proxy:\"dns,omitempty\"`\n}\n\nfunc (option MasqueOption) Prefixes() ([]netip.Prefix, error) {\n\tlocalPrefixes := make([]netip.Prefix, 0, 2)\n\tif len(option.Ip) > 0 {\n\t\tif !strings.Contains(option.Ip, \"/\") {\n\t\t\toption.Ip = option.Ip + \"/32\"\n\t\t}\n\t\tif prefix, err := netip.ParsePrefix(option.Ip); err == nil {\n\t\t\tlocalPrefixes = append(localPrefixes, prefix)\n\t\t} else {\n\t\t\treturn nil, fmt.Errorf(\"ip address parse error: %w\", err)\n\t\t}\n\t}\n\tif len(option.Ipv6) > 0 {\n\t\tif !strings.Contains(option.Ipv6, \"/\") {\n\t\t\toption.Ipv6 = option.Ipv6 + \"/128\"\n\t\t}\n\t\tif prefix, err := netip.ParsePrefix(option.Ipv6); err == nil {\n\t\t\tlocalPrefixes = append(localPrefixes, prefix)\n\t\t} else {\n\t\t\treturn nil, fmt.Errorf(\"ipv6 address parse error: %w\", err)\n\t\t}\n\t}\n\tif len(localPrefixes) == 0 {\n\t\treturn nil, errors.New(\"missing local address\")\n\t}\n\treturn localPrefixes, nil\n}\n\nfunc NewMasque(option MasqueOption) (*Masque, error) {\n\toutbound := &Masque{\n\t\tBase: NewBase(BaseOption{\n\t\t\tName:         option.Name,\n\t\t\tAddr:         net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),\n\t\t\tType:         C.Masque,\n\t\t\tProviderName: option.ProviderName,\n\t\t\tUDP:          option.UDP,\n\t\t\tInterface:    option.Interface,\n\t\t\tRoutingMark:  option.RoutingMark,\n\t\t\tPrefer:       option.IPVersion,\n\t\t}),\n\t}\n\toutbound.dialer = option.NewDialer(outbound.DialOptions())\n\n\tctx, cancel := context.WithCancel(context.Background())\n\toutbound.runCtx = ctx\n\toutbound.runCancel = cancel\n\n\tprivKeyB64, err := base64.StdEncoding.DecodeString(option.PrivateKey)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to decode private key: %v\", err)\n\t}\n\tprivKey, err := x509.ParseECPrivateKey(privKeyB64)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse private key: %v\", err)\n\t}\n\n\tendpointPubKeyB64, err := base64.StdEncoding.DecodeString(option.PublicKey)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to decode public key: %v\", err)\n\t}\n\tpubKey, err := x509.ParsePKIXPublicKey(endpointPubKeyB64)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to parse public key: %v\", err)\n\t}\n\tecPubKey, ok := pubKey.(*ecdsa.PublicKey)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"failed to assert public key as ECDSA\")\n\t}\n\n\turi := option.URI\n\tif uri == \"\" {\n\t\turi = masque.ConnectURI\n\t}\n\toutbound.uri = uri\n\n\tsni := option.SNI\n\tif sni == \"\" {\n\t\tsni = masque.ConnectSNI\n\t}\n\n\ttlsConfig, err := masque.PrepareTlsConfig(privKey, ecPubKey, sni, option.SkipCertVerify)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to prepare TLS config: %v\\n\", err)\n\t}\n\toutbound.tlsConfig = tlsConfig\n\n\tif option.Network == \"h2\" {\n\t\ttlsConfig.NextProtos = []string{\"h2\"}\n\t\t// use h2c mode to disallow the net/http fallback to http1.1 when server returns a not h2 ALPN\n\t\tprotocols := new(http.Protocols)\n\t\tprotocols.SetUnencryptedHTTP2(true)\n\t\toutbound.h2Transport = &http.Transport{\n\t\t\tDialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\t\t\tc, err := outbound.dialer.DialContext(ctx, \"tcp\", outbound.addr)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\ttlsConn := tls.Client(c, tlsConfig)\n\t\t\t\terr = tlsConn.HandshakeContext(ctx)\n\t\t\t\tif err != nil {\n\t\t\t\t\t_ = c.Close()\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\treturn tlsConn, nil\n\t\t\t},\n\t\t\tProtocols: protocols,\n\t\t\tHTTP2: &http.HTTP2Config{\n\t\t\t\tSendPingTimeout: 30 * time.Second,\n\t\t\t},\n\t\t}\n\t}\n\n\toutbound.quicConfig = &quic.Config{\n\t\tEnableDatagrams:   true,\n\t\tInitialPacketSize: 1242,\n\t\tKeepAlivePeriod:   30 * time.Second,\n\t}\n\n\tprefixes, err := option.Prefixes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\toutbound.option = option\n\n\tmtu := option.MTU\n\tif mtu == 0 {\n\t\tmtu = 1280\n\t}\n\tif len(prefixes) == 0 {\n\t\treturn nil, errors.New(\"missing local address\")\n\t}\n\toutbound.tunDevice, err = wireguard.NewStackDevice(prefixes, uint32(mtu))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"create device: %w\", err)\n\t}\n\n\tvar has6 bool\n\tfor _, address := range prefixes {\n\t\tif !address.Addr().Unmap().Is4() {\n\t\t\thas6 = true\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif option.RemoteDnsResolve && len(option.Dns) > 0 {\n\t\tnss, err := dns.ParseNameServer(option.Dns)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfor i := range nss {\n\t\t\tnss[i].ProxyAdapter = outbound\n\t\t}\n\t\toutbound.resolver = dns.NewResolver(dns.Config{\n\t\t\tMain: nss,\n\t\t\tIPv6: has6,\n\t\t})\n\t}\n\n\treturn outbound, nil\n}\n\nfunc (w *Masque) run(ctx context.Context) error {\n\tif w.running.Load() {\n\t\treturn nil\n\t}\n\tw.runMutex.Lock()\n\tdefer w.runMutex.Unlock()\n\t// double-check like sync.Once\n\tif w.running.Load() {\n\t\treturn nil\n\t}\n\n\tif w.runCtx.Err() != nil {\n\t\treturn w.runCtx.Err()\n\t}\n\n\tif !w.runDevice.Load() {\n\t\terr := w.tunDevice.Start()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tw.runDevice.Store(true)\n\t}\n\n\tvar pc net.PacketConn\n\tvar closer io.Closer\n\tvar ipConn masque.IpConn\n\tvar err error\n\tif w.h2Transport != nil {\n\t\tcloser, ipConn, err = masque.ConnectTunnelH2(ctx, w.h2Transport, w.uri)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t} else {\n\t\tvar quicConn *quic.Conn\n\t\tpc, quicConn, err = common.DialQuic(ctx, w.addr, w.DialOptions(), w.dialer, w.tlsConfig, w.quicConfig, false)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tcommon.SetCongestionController(quicConn, w.option.CongestionController, w.option.CWND, w.option.BBRProfile)\n\n\t\tcloser, ipConn, err = masque.ConnectTunnel(ctx, quicConn, w.uri)\n\t\tif err != nil {\n\t\t\t_ = pc.Close()\n\t\t\treturn err\n\t\t}\n\t}\n\n\tw.running.Store(true)\n\n\trunCtx, runCancel := context.WithCancel(w.runCtx)\n\tcontextutils.AfterFunc(runCtx, func() {\n\t\tw.running.Store(false)\n\t\t_ = ipConn.Close()\n\t\t_ = closer.Close()\n\t\tif pc != nil {\n\t\t\t_ = pc.Close()\n\t\t}\n\t})\n\n\tgo func() {\n\t\tdefer runCancel()\n\t\tbuf := pool.Get(pool.UDPBufferSize)\n\t\tdefer pool.Put(buf)\n\t\tbufs := [][]byte{buf}\n\t\tsizes := []int{0}\n\t\tfor runCtx.Err() == nil {\n\t\t\t_, err := w.tunDevice.Read(bufs, sizes, 0)\n\t\t\tif err != nil {\n\t\t\t\tlog.Errorln(\"[Masque](%s) error reading from TUN device: %v\", w.name, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\ticmp, err := ipConn.WritePacket(buf[:sizes[0]])\n\t\t\tif err != nil {\n\t\t\t\tif errors.Is(err, net.ErrClosed) {\n\t\t\t\t\tlog.Errorln(\"[Masque](%s) connection closed while writing to IP connection: %v\", w.name, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tlog.Warnln(\"[Masque](%s) error writing to IP connection: %v, continuing...\", w.name, err)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif len(icmp) > 0 {\n\t\t\t\tif _, err := w.tunDevice.Write([][]byte{icmp}, 0); err != nil {\n\t\t\t\t\tlog.Warnln(\"[Masque](%s) error writing ICMP to TUN device: %v, continuing...\", w.name, err)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}()\n\n\tgo func() {\n\t\tdefer runCancel()\n\t\tfor runCtx.Err() == nil {\n\t\t\tbuf, err := ipConn.ReadPacket()\n\t\t\tif err != nil {\n\t\t\t\tif errors.Is(err, net.ErrClosed) {\n\t\t\t\t\tlog.Errorln(\"[Masque](%s) connection closed while writing to IP connection: %v\", w.name, err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tlog.Warnln(\"[Masque](%s) error reading from IP connection: %v, continuing...\", w.name, err)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif _, err := w.tunDevice.Write([][]byte{buf}, 0); err != nil {\n\t\t\t\tlog.Errorln(\"[Masque](%s) error writing to TUN device: %v\", w.name, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}()\n\n\treturn nil\n}\n\n// Close implements C.ProxyAdapter\nfunc (w *Masque) Close() error {\n\tw.runCancel()\n\tif w.tunDevice != nil {\n\t\tw.tunDevice.Close()\n\t}\n\treturn nil\n}\n\nfunc (w *Masque) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Conn, err error) {\n\tvar conn net.Conn\n\tif err = w.run(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\tif !metadata.Resolved() || w.resolver != nil {\n\t\tr := resolver.DefaultResolver\n\t\tif w.resolver != nil {\n\t\t\tr = w.resolver\n\t\t}\n\t\toptions := w.DialOptions()\n\t\toptions = append(options, dialer.WithResolver(r))\n\t\toptions = append(options, dialer.WithNetDialer(wgNetDialer{tunDevice: w.tunDevice}))\n\t\tconn, err = dialer.NewDialer(options...).DialContext(ctx, \"tcp\", metadata.RemoteAddress())\n\t} else {\n\t\tconn, err = w.tunDevice.DialContext(ctx, \"tcp\", M.SocksaddrFrom(metadata.DstIP, metadata.DstPort).Unwrap())\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif conn == nil {\n\t\treturn nil, errors.New(\"conn is nil\")\n\t}\n\treturn NewConn(conn, w), nil\n}\n\nfunc (w *Masque) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {\n\tvar pc net.PacketConn\n\tif err = w.run(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\tif err = w.ResolveUDP(ctx, metadata); err != nil {\n\t\treturn nil, err\n\t}\n\tpc, err = w.tunDevice.ListenPacket(ctx, M.SocksaddrFrom(metadata.DstIP, metadata.DstPort).Unwrap())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif pc == nil {\n\t\treturn nil, errors.New(\"packetConn is nil\")\n\t}\n\treturn newPacketConn(pc, w), nil\n}\n\nfunc (w *Masque) ResolveUDP(ctx context.Context, metadata *C.Metadata) error {\n\tif (!metadata.Resolved() || w.resolver != nil) && metadata.Host != \"\" {\n\t\tr := resolver.DefaultResolver\n\t\tif w.resolver != nil {\n\t\t\tr = w.resolver\n\t\t}\n\t\tip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"can't resolve ip: %w\", err)\n\t\t}\n\t\tmetadata.DstIP = ip\n\t}\n\treturn nil\n}\n\n// ProxyInfo implements C.ProxyAdapter\nfunc (w *Masque) ProxyInfo() C.ProxyInfo {\n\tinfo := w.Base.ProxyInfo()\n\tinfo.DialerProxy = w.option.DialerProxy\n\treturn info\n}\n\n// IsL3Protocol implements C.ProxyAdapter\nfunc (w *Masque) IsL3Protocol(metadata *C.Metadata) bool {\n\treturn true\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outbound/mieru.go",
    "content": "package outbound\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/netip\"\n\t\"strconv\"\n\t\"sync\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\n\tmieruclient \"github.com/enfein/mieru/v3/apis/client\"\n\tmierucommon \"github.com/enfein/mieru/v3/apis/common\"\n\tmierumodel \"github.com/enfein/mieru/v3/apis/model\"\n\tmierutp \"github.com/enfein/mieru/v3/apis/trafficpattern\"\n\tmierupb \"github.com/enfein/mieru/v3/pkg/appctl/appctlpb\"\n\t\"google.golang.org/protobuf/proto\"\n)\n\ntype Mieru struct {\n\t*Base\n\toption *MieruOption\n\tclient mieruclient.Client\n\tmu     sync.Mutex\n}\n\ntype MieruOption struct {\n\tBasicOption\n\tName           string `proxy:\"name\"`\n\tServer         string `proxy:\"server\"`\n\tPort           int    `proxy:\"port,omitempty\"`\n\tPortRange      string `proxy:\"port-range,omitempty\"`\n\tTransport      string `proxy:\"transport\"`\n\tUDP            bool   `proxy:\"udp,omitempty\"`\n\tUserName       string `proxy:\"username\"`\n\tPassword       string `proxy:\"password\"`\n\tMultiplexing   string `proxy:\"multiplexing,omitempty\"`\n\tHandshakeMode  string `proxy:\"handshake-mode,omitempty\"`\n\tTrafficPattern string `proxy:\"traffic-pattern,omitempty\"`\n}\n\ntype mieruPacketDialer struct {\n\tC.Dialer\n}\n\nvar _ mierucommon.PacketDialer = (*mieruPacketDialer)(nil)\n\nfunc (pd mieruPacketDialer) ListenPacket(ctx context.Context, network, laddr, raddr string) (net.PacketConn, error) {\n\trAddrPort, err := netip.ParseAddrPort(raddr)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid address %s: %w\", raddr, err)\n\t}\n\treturn pd.Dialer.ListenPacket(ctx, network, laddr, rAddrPort)\n}\n\ntype mieruDNSResolver struct {\n\tprefer C.DNSPrefer\n}\n\nvar _ mierucommon.DNSResolver = (*mieruDNSResolver)(nil)\n\nfunc (dr mieruDNSResolver) LookupIP(ctx context.Context, network, host string) (_ []net.IP, err error) {\n\tvar ip netip.Addr\n\tswitch dr.prefer {\n\tcase C.IPv4Only:\n\t\tip, err = resolver.ResolveIPv4WithResolver(ctx, host, resolver.ProxyServerHostResolver)\n\tcase C.IPv6Only:\n\t\tip, err = resolver.ResolveIPv6WithResolver(ctx, host, resolver.ProxyServerHostResolver)\n\tcase C.IPv6Prefer:\n\t\tip, err = resolver.ResolveIPPrefer6WithResolver(ctx, host, resolver.ProxyServerHostResolver)\n\tdefault:\n\t\tip, err = resolver.ResolveIPWithResolver(ctx, host, resolver.ProxyServerHostResolver)\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"can't resolve ip: %w\", err)\n\t}\n\t// TODO: handle IP4P (due to interface limitations, it's currently impossible to modify the port here)\n\treturn []net.IP{ip.AsSlice()}, nil\n}\n\n// DialContext implements C.ProxyAdapter\nfunc (m *Mieru) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, error) {\n\tif err := m.ensureClientIsRunning(); err != nil {\n\t\treturn nil, err\n\t}\n\taddr := metadataToMieruNetAddrSpec(metadata)\n\tc, err := m.client.DialContext(ctx, addr)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"dial to %s failed: %w\", addr, err)\n\t}\n\treturn NewConn(c, m), nil\n}\n\n// ListenPacketContext implements C.ProxyAdapter\nfunc (m *Mieru) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {\n\tif err = m.ResolveUDP(ctx, metadata); err != nil {\n\t\treturn nil, err\n\t}\n\tif err := m.ensureClientIsRunning(); err != nil {\n\t\treturn nil, err\n\t}\n\tc, err := m.client.DialContext(ctx, metadata.UDPAddr())\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"dial to %s failed: %w\", metadata.UDPAddr(), err)\n\t}\n\treturn newPacketConn(N.NewThreadSafePacketConn(mierucommon.NewUDPAssociateWrapper(mierucommon.NewPacketOverStreamTunnel(c))), m), nil\n}\n\n// SupportUOT implements C.ProxyAdapter\nfunc (m *Mieru) SupportUOT() bool {\n\treturn true\n}\n\n// ProxyInfo implements C.ProxyAdapter\nfunc (m *Mieru) ProxyInfo() C.ProxyInfo {\n\tinfo := m.Base.ProxyInfo()\n\tinfo.DialerProxy = m.option.DialerProxy\n\treturn info\n}\n\nfunc (m *Mieru) ensureClientIsRunning() error {\n\tm.mu.Lock()\n\tdefer m.mu.Unlock()\n\n\tif m.client.IsRunning() {\n\t\treturn nil\n\t}\n\n\t// Create a dialer and add it to the client config, before starting the client.\n\tconfig, err := m.client.Load()\n\tif err != nil {\n\t\treturn err\n\t}\n\tconfig.Dialer = m.dialer\n\tconfig.PacketDialer = mieruPacketDialer{Dialer: m.dialer}\n\tconfig.Resolver = mieruDNSResolver{prefer: m.prefer}\n\tif err := m.client.Store(config); err != nil {\n\t\treturn err\n\t}\n\n\tif err := m.client.Start(); err != nil {\n\t\treturn fmt.Errorf(\"failed to start mieru client: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc NewMieru(option MieruOption) (*Mieru, error) {\n\tconfig, err := buildMieruClientConfig(option)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to build mieru client config: %w\", err)\n\t}\n\tc := mieruclient.NewClient()\n\tif err := c.Store(config); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to store mieru client config: %w\", err)\n\t}\n\t// Client is started lazily on the first use.\n\n\tvar addr string\n\tif option.Port != 0 {\n\t\taddr = net.JoinHostPort(option.Server, strconv.Itoa(option.Port))\n\t} else {\n\t\tbeginPort, _, _ := beginAndEndPortFromPortRange(option.PortRange)\n\t\taddr = net.JoinHostPort(option.Server, strconv.Itoa(beginPort))\n\t}\n\toutbound := &Mieru{\n\t\tBase: NewBase(BaseOption{\n\t\t\tName:         option.Name,\n\t\t\tAddr:         addr,\n\t\t\tType:         C.Mieru,\n\t\t\tProviderName: option.ProviderName,\n\t\t\tUDP:          option.UDP,\n\t\t\tTFO:          option.TFO,\n\t\t\tMPTCP:        option.MPTCP,\n\t\t\tInterface:    option.Interface,\n\t\t\tRoutingMark:  option.RoutingMark,\n\t\t\tPrefer:       option.IPVersion,\n\t\t}),\n\t\toption: &option,\n\t\tclient: c,\n\t}\n\toutbound.dialer = option.NewDialer(outbound.DialOptions())\n\treturn outbound, nil\n}\n\n// Close implements C.ProxyAdapter\nfunc (m *Mieru) Close() error {\n\tm.mu.Lock()\n\tdefer m.mu.Unlock()\n\tif m.client != nil && m.client.IsRunning() {\n\t\treturn m.client.Stop()\n\t}\n\treturn nil\n}\n\nfunc metadataToMieruNetAddrSpec(metadata *C.Metadata) mierumodel.NetAddrSpec {\n\tspec := mierumodel.NetAddrSpec{\n\t\tNet: metadata.NetWork.String(),\n\t}\n\tif metadata.Host != \"\" {\n\t\tspec.AddrSpec = mierumodel.AddrSpec{\n\t\t\tFQDN: metadata.Host,\n\t\t\tPort: int(metadata.DstPort),\n\t\t}\n\t} else {\n\t\tspec.AddrSpec = mierumodel.AddrSpec{\n\t\t\tIP:   metadata.DstIP.AsSlice(),\n\t\t\tPort: int(metadata.DstPort),\n\t\t}\n\t}\n\treturn spec\n}\n\nfunc buildMieruClientConfig(option MieruOption) (*mieruclient.ClientConfig, error) {\n\tif err := validateMieruOption(option); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to validate mieru option: %w\", err)\n\t}\n\n\tvar transportProtocol = mierupb.TransportProtocol_UNKNOWN_TRANSPORT_PROTOCOL.Enum()\n\tswitch option.Transport {\n\tcase \"TCP\":\n\t\ttransportProtocol = mierupb.TransportProtocol_TCP.Enum()\n\tcase \"UDP\":\n\t\ttransportProtocol = mierupb.TransportProtocol_UDP.Enum()\n\t}\n\tvar server *mierupb.ServerEndpoint\n\tif net.ParseIP(option.Server) != nil {\n\t\t// server is an IP address\n\t\tif option.PortRange != \"\" {\n\t\t\tserver = &mierupb.ServerEndpoint{\n\t\t\t\tIpAddress: proto.String(option.Server),\n\t\t\t\tPortBindings: []*mierupb.PortBinding{\n\t\t\t\t\t{\n\t\t\t\t\t\tPortRange: proto.String(option.PortRange),\n\t\t\t\t\t\tProtocol:  transportProtocol,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\t\t} else {\n\t\t\tserver = &mierupb.ServerEndpoint{\n\t\t\t\tIpAddress: proto.String(option.Server),\n\t\t\t\tPortBindings: []*mierupb.PortBinding{\n\t\t\t\t\t{\n\t\t\t\t\t\tPort:     proto.Int32(int32(option.Port)),\n\t\t\t\t\t\tProtocol: transportProtocol,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// server is a domain name\n\t\tif option.PortRange != \"\" {\n\t\t\tserver = &mierupb.ServerEndpoint{\n\t\t\t\tDomainName: proto.String(option.Server),\n\t\t\t\tPortBindings: []*mierupb.PortBinding{\n\t\t\t\t\t{\n\t\t\t\t\t\tPortRange: proto.String(option.PortRange),\n\t\t\t\t\t\tProtocol:  transportProtocol,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\t\t} else {\n\t\t\tserver = &mierupb.ServerEndpoint{\n\t\t\t\tDomainName: proto.String(option.Server),\n\t\t\t\tPortBindings: []*mierupb.PortBinding{\n\t\t\t\t\t{\n\t\t\t\t\t\tPort:     proto.Int32(int32(option.Port)),\n\t\t\t\t\t\tProtocol: transportProtocol,\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\t\t}\n\t}\n\tconfig := &mieruclient.ClientConfig{\n\t\tProfile: &mierupb.ClientProfile{\n\t\t\tProfileName: proto.String(option.Name),\n\t\t\tUser: &mierupb.User{\n\t\t\t\tName:     proto.String(option.UserName),\n\t\t\t\tPassword: proto.String(option.Password),\n\t\t\t},\n\t\t\tServers: []*mierupb.ServerEndpoint{server},\n\t\t},\n\t\tDNSConfig: &mierucommon.ClientDNSConfig{\n\t\t\tBypassDialerDNS: true,\n\t\t},\n\t}\n\tif multiplexing, ok := mierupb.MultiplexingLevel_value[option.Multiplexing]; ok {\n\t\tconfig.Profile.Multiplexing = &mierupb.MultiplexingConfig{\n\t\t\tLevel: mierupb.MultiplexingLevel(multiplexing).Enum(),\n\t\t}\n\t}\n\tif handshakeMode, ok := mierupb.HandshakeMode_value[option.HandshakeMode]; ok {\n\t\tconfig.Profile.HandshakeMode = (*mierupb.HandshakeMode)(&handshakeMode)\n\t}\n\tif option.TrafficPattern != \"\" {\n\t\ttrafficPattern, _ := mierutp.Decode(option.TrafficPattern)\n\t\tconfig.Profile.TrafficPattern = trafficPattern\n\t}\n\treturn config, nil\n}\n\nfunc validateMieruOption(option MieruOption) error {\n\tif option.Name == \"\" {\n\t\treturn fmt.Errorf(\"name is empty\")\n\t}\n\tif option.Server == \"\" {\n\t\treturn fmt.Errorf(\"server is empty\")\n\t}\n\tif option.Port == 0 && option.PortRange == \"\" {\n\t\treturn fmt.Errorf(\"either port or port-range must be set\")\n\t}\n\tif option.Port != 0 && option.PortRange != \"\" {\n\t\treturn fmt.Errorf(\"port and port-range cannot be set at the same time\")\n\t}\n\tif option.Port != 0 && (option.Port < 1 || option.Port > 65535) {\n\t\treturn fmt.Errorf(\"port must be between 1 and 65535\")\n\t}\n\tif option.PortRange != \"\" {\n\t\tbegin, end, err := beginAndEndPortFromPortRange(option.PortRange)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"invalid port-range format\")\n\t\t}\n\t\tif begin < 1 || begin > 65535 {\n\t\t\treturn fmt.Errorf(\"begin port must be between 1 and 65535\")\n\t\t}\n\t\tif end < 1 || end > 65535 {\n\t\t\treturn fmt.Errorf(\"end port must be between 1 and 65535\")\n\t\t}\n\t\tif begin > end {\n\t\t\treturn fmt.Errorf(\"begin port must be less than or equal to end port\")\n\t\t}\n\t}\n\n\tif option.Transport != \"TCP\" && option.Transport != \"UDP\" {\n\t\treturn fmt.Errorf(\"transport must be TCP or UDP\")\n\t}\n\tif option.UserName == \"\" {\n\t\treturn fmt.Errorf(\"username is empty\")\n\t}\n\tif option.Password == \"\" {\n\t\treturn fmt.Errorf(\"password is empty\")\n\t}\n\tif option.Multiplexing != \"\" {\n\t\tif _, ok := mierupb.MultiplexingLevel_value[option.Multiplexing]; !ok {\n\t\t\treturn fmt.Errorf(\"invalid multiplexing level: %s\", option.Multiplexing)\n\t\t}\n\t}\n\tif option.HandshakeMode != \"\" {\n\t\tif _, ok := mierupb.HandshakeMode_value[option.HandshakeMode]; !ok {\n\t\t\treturn fmt.Errorf(\"invalid handshake mode: %s\", option.HandshakeMode)\n\t\t}\n\t}\n\tif option.TrafficPattern != \"\" {\n\t\ttrafficPattern, err := mierutp.Decode(option.TrafficPattern)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to decode traffic pattern %q: %w\", option.TrafficPattern, err)\n\t\t}\n\t\tif err := mierutp.Validate(trafficPattern); err != nil {\n\t\t\treturn fmt.Errorf(\"invalid traffic pattern %q: %w\", option.TrafficPattern, err)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc beginAndEndPortFromPortRange(portRange string) (int, int, error) {\n\tvar begin, end int\n\t_, err := fmt.Sscanf(portRange, \"%d-%d\", &begin, &end)\n\treturn begin, end, err\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outbound/mieru_test.go",
    "content": "package outbound\n\nimport \"testing\"\n\nfunc TestNewMieru(t *testing.T) {\n\ttestCases := []struct {\n\t\toption       MieruOption\n\t\twantBaseAddr string\n\t}{\n\t\t{\n\t\t\toption: MieruOption{\n\t\t\t\tName:      \"test\",\n\t\t\t\tServer:    \"1.2.3.4\",\n\t\t\t\tPort:      10000,\n\t\t\t\tTransport: \"TCP\",\n\t\t\t\tUserName:  \"test\",\n\t\t\t\tPassword:  \"test\",\n\t\t\t},\n\t\t\twantBaseAddr: \"1.2.3.4:10000\",\n\t\t},\n\t\t{\n\t\t\toption: MieruOption{\n\t\t\t\tName:      \"test\",\n\t\t\t\tServer:    \"2001:db8::1\",\n\t\t\t\tPortRange: \"10001-10002\",\n\t\t\t\tTransport: \"TCP\",\n\t\t\t\tUserName:  \"test\",\n\t\t\t\tPassword:  \"test\",\n\t\t\t},\n\t\t\twantBaseAddr: \"[2001:db8::1]:10001\",\n\t\t},\n\t\t{\n\t\t\toption: MieruOption{\n\t\t\t\tName:           \"test\",\n\t\t\t\tServer:         \"example.com\",\n\t\t\t\tPort:           10003,\n\t\t\t\tTransport:      \"UDP\",\n\t\t\t\tUserName:       \"test\",\n\t\t\t\tPassword:       \"test\",\n\t\t\t\tTrafficPattern: \"GgQIARAK\",\n\t\t\t},\n\t\t\twantBaseAddr: \"example.com:10003\",\n\t\t},\n\t}\n\n\tfor _, testCase := range testCases {\n\t\tmieru, err := NewMieru(testCase.option)\n\t\tif err != nil {\n\t\t\tt.Error(err)\n\t\t}\n\t\tif mieru.addr != testCase.wantBaseAddr {\n\t\t\tt.Errorf(\"got addr %q, want %q\", mieru.addr, testCase.wantBaseAddr)\n\t\t}\n\t}\n}\n\nfunc TestBeginAndEndPortFromPortRange(t *testing.T) {\n\ttestCases := []struct {\n\t\tinput  string\n\t\tbegin  int\n\t\tend    int\n\t\thasErr bool\n\t}{\n\t\t{\"1-10\", 1, 10, false},\n\t\t{\"1000-2000\", 1000, 2000, false},\n\t\t{\"65535-65535\", 65535, 65535, false},\n\t\t{\"1\", 0, 0, true},\n\t\t{\"1-\", 0, 0, true},\n\t\t{\"-10\", 0, 0, true},\n\t\t{\"a-b\", 0, 0, true},\n\t\t{\"1-b\", 0, 0, true},\n\t\t{\"a-10\", 0, 0, true},\n\t}\n\n\tfor _, testCase := range testCases {\n\t\tbegin, end, err := beginAndEndPortFromPortRange(testCase.input)\n\t\tif testCase.hasErr {\n\t\t\tif err == nil {\n\t\t\t\tt.Errorf(\"beginAndEndPortFromPortRange(%s) should return an error\", testCase.input)\n\t\t\t}\n\t\t} else {\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"beginAndEndPortFromPortRange(%s) should not return an error, but got %v\", testCase.input, err)\n\t\t\t}\n\t\t\tif begin != testCase.begin {\n\t\t\t\tt.Errorf(\"beginAndEndPortFromPortRange(%s) begin port mismatch, got %d, want %d\", testCase.input, begin, testCase.begin)\n\t\t\t}\n\t\t\tif end != testCase.end {\n\t\t\t\tt.Errorf(\"beginAndEndPortFromPortRange(%s) end port mismatch, got %d, want %d\", testCase.input, end, testCase.end)\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outbound/reality.go",
    "content": "package outbound\n\nimport (\n\t\"crypto/ecdh\"\n\t\"encoding/base64\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\n\ttlsC \"github.com/metacubex/mihomo/component/tls\"\n)\n\ntype RealityOptions struct {\n\tPublicKey string `proxy:\"public-key\"`\n\tShortID   string `proxy:\"short-id,omitempty\"`\n\n\tSupportX25519MLKEM768 bool `proxy:\"support-x25519mlkem768,omitempty\"`\n}\n\nfunc (o RealityOptions) Parse() (*tlsC.RealityConfig, error) {\n\tif o.PublicKey != \"\" {\n\t\tconfig := new(tlsC.RealityConfig)\n\t\tconfig.SupportX25519MLKEM768 = o.SupportX25519MLKEM768\n\n\t\tconst x25519ScalarSize = 32\n\t\tpublicKey, err := base64.RawURLEncoding.DecodeString(o.PublicKey)\n\t\tif err != nil || len(publicKey) != x25519ScalarSize {\n\t\t\treturn nil, errors.New(\"invalid REALITY public key\")\n\t\t}\n\t\tconfig.PublicKey, err = ecdh.X25519().NewPublicKey(publicKey)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"fail to create REALITY public key: %w\", err)\n\t\t}\n\n\t\tn := hex.DecodedLen(len(o.ShortID))\n\t\tif n > tlsC.RealityMaxShortIDLen {\n\t\t\treturn nil, errors.New(\"invalid REALITY short id\")\n\t\t}\n\t\tn, err = hex.Decode(config.ShortID[:], []byte(o.ShortID))\n\t\tif err != nil || n > tlsC.RealityMaxShortIDLen {\n\t\t\treturn nil, errors.New(\"invalid REALITY short ID\")\n\t\t}\n\n\t\treturn config, nil\n\t}\n\treturn nil, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outbound/reject.go",
    "content": "package outbound\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"net\"\n\t\"net/netip\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/buf\"\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\ntype Reject struct {\n\t*Base\n\tdrop bool\n}\n\ntype RejectOption struct {\n\tBasicOption\n\tName string `proxy:\"name\"`\n}\n\n// DialContext implements C.ProxyAdapter\nfunc (r *Reject) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, error) {\n\tif r.drop {\n\t\treturn NewConn(dropConn{}, r), nil\n\t}\n\treturn NewConn(nopConn{}, r), nil\n}\n\n// ListenPacketContext implements C.ProxyAdapter\nfunc (r *Reject) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (C.PacketConn, error) {\n\tif err := r.ResolveUDP(ctx, metadata); err != nil {\n\t\treturn nil, err\n\t}\n\treturn newPacketConn(&nopPacketConn{}, r), nil\n}\n\nfunc (r *Reject) ResolveUDP(ctx context.Context, metadata *C.Metadata) error {\n\tif !metadata.Resolved() {\n\t\tmetadata.DstIP = netip.IPv4Unspecified()\n\t}\n\treturn nil\n}\n\nfunc NewRejectWithOption(option RejectOption) *Reject {\n\treturn &Reject{\n\t\tBase: NewBase(BaseOption{\n\t\t\tName: option.Name,\n\t\t\tType: C.Reject,\n\t\t\tUDP:  true,\n\t\t}),\n\t}\n}\n\nfunc NewReject() *Reject {\n\treturn &Reject{\n\t\tBase: NewBase(BaseOption{\n\t\t\tName:   \"REJECT\",\n\t\t\tType:   C.Reject,\n\t\t\tUDP:    true,\n\t\t\tPrefer: C.DualStack,\n\t\t}),\n\t}\n}\n\nfunc NewRejectDrop() *Reject {\n\treturn &Reject{\n\t\tBase: NewBase(BaseOption{\n\t\t\tName:   \"REJECT-DROP\",\n\t\t\tType:   C.RejectDrop,\n\t\t\tUDP:    true,\n\t\t\tPrefer: C.DualStack,\n\t\t}),\n\t\tdrop: true,\n\t}\n}\n\nfunc NewPass() *Reject {\n\treturn &Reject{\n\t\tBase: &Base{\n\t\t\tname:   \"PASS\",\n\t\t\ttp:     C.Pass,\n\t\t\tudp:    true,\n\t\t\tprefer: C.DualStack,\n\t\t},\n\t}\n}\n\ntype nopConn struct{}\n\nfunc (rw nopConn) Read(b []byte) (int, error) { return 0, io.EOF }\n\nfunc (rw nopConn) ReadBuffer(buffer *buf.Buffer) error { return io.EOF }\n\nfunc (rw nopConn) Write(b []byte) (int, error)          { return 0, io.EOF }\nfunc (rw nopConn) WriteBuffer(buffer *buf.Buffer) error { return io.EOF }\nfunc (rw nopConn) Close() error                         { return nil }\nfunc (rw nopConn) LocalAddr() net.Addr                  { return nil }\nfunc (rw nopConn) RemoteAddr() net.Addr                 { return nil }\nfunc (rw nopConn) SetDeadline(time.Time) error          { return nil }\nfunc (rw nopConn) SetReadDeadline(time.Time) error      { return nil }\nfunc (rw nopConn) SetWriteDeadline(time.Time) error     { return nil }\n\nvar udpAddrIPv4Unspecified = &net.UDPAddr{IP: net.IPv4zero, Port: 0}\n\ntype nopPacketConn struct{}\n\nfunc (npc nopPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {\n\treturn len(b), nil\n}\nfunc (npc nopPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {\n\treturn 0, nil, io.EOF\n}\nfunc (npc nopPacketConn) WaitReadFrom() ([]byte, func(), net.Addr, error) {\n\treturn nil, nil, nil, io.EOF\n}\nfunc (npc nopPacketConn) Close() error                     { return nil }\nfunc (npc nopPacketConn) LocalAddr() net.Addr              { return udpAddrIPv4Unspecified }\nfunc (npc nopPacketConn) SetDeadline(time.Time) error      { return nil }\nfunc (npc nopPacketConn) SetReadDeadline(time.Time) error  { return nil }\nfunc (npc nopPacketConn) SetWriteDeadline(time.Time) error { return nil }\n\ntype dropConn struct{}\n\nfunc (rw dropConn) Read(b []byte) (int, error) { return 0, io.EOF }\nfunc (rw dropConn) ReadBuffer(buffer *buf.Buffer) error {\n\ttime.Sleep(C.DefaultDropTime)\n\treturn io.EOF\n}\nfunc (rw dropConn) Write(b []byte) (int, error)          { return 0, io.EOF }\nfunc (rw dropConn) WriteBuffer(buffer *buf.Buffer) error { return io.EOF }\nfunc (rw dropConn) Close() error                         { return nil }\nfunc (rw dropConn) LocalAddr() net.Addr                  { return nil }\nfunc (rw dropConn) RemoteAddr() net.Addr                 { return nil }\nfunc (rw dropConn) SetDeadline(time.Time) error          { return nil }\nfunc (rw dropConn) SetReadDeadline(time.Time) error      { return nil }\nfunc (rw dropConn) SetWriteDeadline(time.Time) error     { return nil }\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outbound/shadowsocks.go",
    "content": "package outbound\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"strconv\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/structure\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/ntp\"\n\tgost \"github.com/metacubex/mihomo/transport/gost-plugin\"\n\t\"github.com/metacubex/mihomo/transport/kcptun\"\n\t\"github.com/metacubex/mihomo/transport/restls\"\n\tobfs \"github.com/metacubex/mihomo/transport/simple-obfs\"\n\tshadowtls \"github.com/metacubex/mihomo/transport/sing-shadowtls\"\n\tv2rayObfs \"github.com/metacubex/mihomo/transport/v2ray-plugin\"\n\n\tshadowsocks \"github.com/metacubex/sing-shadowsocks2\"\n\t\"github.com/metacubex/sing/common/bufio\"\n\tM \"github.com/metacubex/sing/common/metadata\"\n\t\"github.com/metacubex/sing/common/uot\"\n)\n\ntype ShadowSocks struct {\n\t*Base\n\tmethod shadowsocks.Method\n\n\toption *ShadowSocksOption\n\t// obfs\n\tobfsMode        string\n\tobfsOption      *simpleObfsOption\n\tv2rayOption     *v2rayObfs.Option\n\tgostOption      *gost.Option\n\tshadowTLSOption *shadowtls.ShadowTLSOption\n\trestlsConfig    *restls.Config\n\tkcptunClient    *kcptun.Client\n}\n\ntype ShadowSocksOption struct {\n\tBasicOption\n\tName              string         `proxy:\"name\"`\n\tServer            string         `proxy:\"server\"`\n\tPort              int            `proxy:\"port\"`\n\tPassword          string         `proxy:\"password\"`\n\tCipher            string         `proxy:\"cipher\"`\n\tUDP               bool           `proxy:\"udp,omitempty\"`\n\tPlugin            string         `proxy:\"plugin,omitempty\"`\n\tPluginOpts        map[string]any `proxy:\"plugin-opts,omitempty\"`\n\tUDPOverTCP        bool           `proxy:\"udp-over-tcp,omitempty\"`\n\tUDPOverTCPVersion int            `proxy:\"udp-over-tcp-version,omitempty\"`\n\tClientFingerprint string         `proxy:\"client-fingerprint,omitempty\"`\n}\n\ntype simpleObfsOption struct {\n\tMode string `obfs:\"mode,omitempty\"`\n\tHost string `obfs:\"host,omitempty\"`\n}\n\ntype v2rayObfsOption struct {\n\tMode                     string            `obfs:\"mode\"`\n\tHost                     string            `obfs:\"host,omitempty\"`\n\tPath                     string            `obfs:\"path,omitempty\"`\n\tTLS                      bool              `obfs:\"tls,omitempty\"`\n\tECHOpts                  ECHOptions        `obfs:\"ech-opts,omitempty\"`\n\tFingerprint              string            `obfs:\"fingerprint,omitempty\"`\n\tCertificate              string            `obfs:\"certificate,omitempty\"`\n\tPrivateKey               string            `obfs:\"private-key,omitempty\"`\n\tHeaders                  map[string]string `obfs:\"headers,omitempty\"`\n\tSkipCertVerify           bool              `obfs:\"skip-cert-verify,omitempty\"`\n\tMux                      bool              `obfs:\"mux,omitempty\"`\n\tV2rayHttpUpgrade         bool              `obfs:\"v2ray-http-upgrade,omitempty\"`\n\tV2rayHttpUpgradeFastOpen bool              `obfs:\"v2ray-http-upgrade-fast-open,omitempty\"`\n}\n\ntype gostObfsOption struct {\n\tMode           string            `obfs:\"mode\"`\n\tHost           string            `obfs:\"host,omitempty\"`\n\tPath           string            `obfs:\"path,omitempty\"`\n\tTLS            bool              `obfs:\"tls,omitempty\"`\n\tECHOpts        ECHOptions        `obfs:\"ech-opts,omitempty\"`\n\tFingerprint    string            `obfs:\"fingerprint,omitempty\"`\n\tCertificate    string            `obfs:\"certificate,omitempty\"`\n\tPrivateKey     string            `obfs:\"private-key,omitempty\"`\n\tHeaders        map[string]string `obfs:\"headers,omitempty\"`\n\tSkipCertVerify bool              `obfs:\"skip-cert-verify,omitempty\"`\n\tMux            bool              `obfs:\"mux,omitempty\"`\n}\n\ntype shadowTLSOption struct {\n\tPassword       string   `obfs:\"password,omitempty\"`\n\tHost           string   `obfs:\"host\"`\n\tFingerprint    string   `obfs:\"fingerprint,omitempty\"`\n\tCertificate    string   `obfs:\"certificate,omitempty\"`\n\tPrivateKey     string   `obfs:\"private-key,omitempty\"`\n\tSkipCertVerify bool     `obfs:\"skip-cert-verify,omitempty\"`\n\tVersion        int      `obfs:\"version,omitempty\"`\n\tALPN           []string `obfs:\"alpn,omitempty\"`\n}\n\ntype restlsOption struct {\n\tPassword     string `obfs:\"password\"`\n\tHost         string `obfs:\"host\"`\n\tVersionHint  string `obfs:\"version-hint\"`\n\tRestlsScript string `obfs:\"restls-script,omitempty\"`\n}\n\ntype kcpTunOption struct {\n\tKey          string `obfs:\"key,omitempty\"`\n\tCrypt        string `obfs:\"crypt,omitempty\"`\n\tMode         string `obfs:\"mode,omitempty\"`\n\tConn         int    `obfs:\"conn,omitempty\"`\n\tAutoExpire   int    `obfs:\"autoexpire,omitempty\"`\n\tScavengeTTL  int    `obfs:\"scavengettl,omitempty\"`\n\tMTU          int    `obfs:\"mtu,omitempty\"`\n\tRateLimit    int    `obfs:\"ratelimit,omitempty\"`\n\tSndWnd       int    `obfs:\"sndwnd,omitempty\"`\n\tRcvWnd       int    `obfs:\"rcvwnd,omitempty\"`\n\tDataShard    int    `obfs:\"datashard,omitempty\"`\n\tParityShard  int    `obfs:\"parityshard,omitempty\"`\n\tDSCP         int    `obfs:\"dscp,omitempty\"`\n\tNoComp       bool   `obfs:\"nocomp,omitempty\"`\n\tAckNodelay   bool   `obfs:\"acknodelay,omitempty\"`\n\tNoDelay      int    `obfs:\"nodelay,omitempty\"`\n\tInterval     int    `obfs:\"interval,omitempty\"`\n\tResend       int    `obfs:\"resend,omitempty\"`\n\tNoCongestion int    `obfs:\"nc,omitempty\"`\n\tSockBuf      int    `obfs:\"sockbuf,omitempty\"`\n\tSmuxVer      int    `obfs:\"smuxver,omitempty\"`\n\tSmuxBuf      int    `obfs:\"smuxbuf,omitempty\"`\n\tFrameSize    int    `obfs:\"framesize,omitempty\"`\n\tStreamBuf    int    `obfs:\"streambuf,omitempty\"`\n\tKeepAlive    int    `obfs:\"keepalive,omitempty\"`\n}\n\n// StreamConnContext implements C.ProxyAdapter\nfunc (ss *ShadowSocks) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ net.Conn, err error) {\n\tuseEarly := false\n\tswitch ss.obfsMode {\n\tcase \"tls\":\n\t\tc = obfs.NewTLSObfs(c, ss.obfsOption.Host)\n\tcase \"http\":\n\t\t_, port, _ := net.SplitHostPort(ss.addr)\n\t\tc = obfs.NewHTTPObfs(c, ss.obfsOption.Host, port)\n\tcase \"websocket\":\n\t\tif ss.v2rayOption != nil {\n\t\t\tc, err = v2rayObfs.NewV2rayObfs(ctx, c, ss.v2rayOption)\n\t\t} else if ss.gostOption != nil {\n\t\t\tc, err = gost.NewGostWebsocket(ctx, c, ss.gostOption)\n\t\t} else {\n\t\t\treturn nil, fmt.Errorf(\"plugin options is required\")\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%s connect error: %w\", ss.addr, err)\n\t\t}\n\tcase shadowtls.Mode:\n\t\tc, err = shadowtls.NewShadowTLS(ctx, c, ss.shadowTLSOption)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tuseEarly = true\n\tcase restls.Mode:\n\t\tc, err = restls.NewRestls(ctx, c, ss.restlsConfig)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%s (restls) connect error: %w\", ss.addr, err)\n\t\t}\n\t\tuseEarly = true\n\t}\n\tuseEarly = useEarly || N.NeedHandshake(c)\n\tif !useEarly {\n\t\tif ctx.Done() != nil {\n\t\t\tdone := N.SetupContextForConn(ctx, c)\n\t\t\tdefer done(&err)\n\t\t}\n\t}\n\tif metadata.NetWork == C.UDP && ss.option.UDPOverTCP {\n\t\tuotDestination := uot.RequestDestination(uint8(ss.option.UDPOverTCPVersion))\n\t\tif useEarly {\n\t\t\treturn ss.method.DialEarlyConn(c, uotDestination), nil\n\t\t} else {\n\t\t\treturn ss.method.DialConn(c, uotDestination)\n\t\t}\n\t}\n\tif useEarly {\n\t\treturn ss.method.DialEarlyConn(c, M.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort)), nil\n\t} else {\n\t\treturn ss.method.DialConn(c, M.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort))\n\t}\n}\n\nfunc (ss *ShadowSocks) dialContext(ctx context.Context) (c net.Conn, err error) {\n\tif ss.kcptunClient != nil {\n\t\treturn ss.kcptunClient.OpenStream(ctx, ss.listenPacketContext)\n\t}\n\treturn ss.dialer.DialContext(ctx, \"tcp\", ss.addr)\n}\n\n// DialContext implements C.ProxyAdapter\nfunc (ss *ShadowSocks) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Conn, err error) {\n\tc, err := ss.dialContext(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s connect error: %w\", ss.addr, err)\n\t}\n\n\tdefer func(c net.Conn) {\n\t\tsafeConnClose(c, err)\n\t}(c)\n\n\tc, err = ss.StreamConnContext(ctx, c, metadata)\n\treturn NewConn(c, ss), err\n}\n\nfunc (ss *ShadowSocks) listenPacketContext(ctx context.Context) (net.PacketConn, net.Addr, error) {\n\taddr, err := resolveUDPAddr(ctx, \"udp\", ss.addr, ss.prefer)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tpc, err := ss.dialer.ListenPacket(ctx, \"udp\", \"\", addr.AddrPort())\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\treturn pc, addr, nil\n}\n\n// ListenPacketContext implements C.ProxyAdapter\nfunc (ss *ShadowSocks) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {\n\tif ss.option.UDPOverTCP {\n\t\tvar c net.Conn\n\t\tc, err = ss.DialContext(ctx, metadata)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdefer func(c net.Conn) {\n\t\t\tsafeConnClose(c, err)\n\t\t}(c)\n\t\tif err = ss.ResolveUDP(ctx, metadata); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdestination := M.SocksaddrFromNet(metadata.UDPAddr())\n\t\tif ss.option.UDPOverTCPVersion == uot.LegacyVersion {\n\t\t\treturn newPacketConn(N.NewThreadSafePacketConn(uot.NewConn(c, uot.Request{Destination: destination})), ss), nil\n\t\t} else {\n\t\t\treturn newPacketConn(N.NewThreadSafePacketConn(uot.NewLazyConn(c, uot.Request{Destination: destination})), ss), nil\n\t\t}\n\t}\n\tif err := ss.ResolveUDP(ctx, metadata); err != nil {\n\t\treturn nil, err\n\t}\n\n\tpc, addr, err := ss.listenPacketContext(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tpc = ss.method.DialPacketConn(bufio.NewBindPacketConn(pc, addr))\n\treturn newPacketConn(pc, ss), nil\n}\n\n// ProxyInfo implements C.ProxyAdapter\nfunc (ss *ShadowSocks) ProxyInfo() C.ProxyInfo {\n\tinfo := ss.Base.ProxyInfo()\n\tinfo.DialerProxy = ss.option.DialerProxy\n\treturn info\n}\n\n// SupportUOT implements C.ProxyAdapter\nfunc (ss *ShadowSocks) SupportUOT() bool {\n\treturn ss.option.UDPOverTCP\n}\n\nfunc (ss *ShadowSocks) Close() error {\n\tif ss.kcptunClient != nil {\n\t\treturn ss.kcptunClient.Close()\n\t}\n\treturn nil\n}\n\nfunc NewShadowSocks(option ShadowSocksOption) (*ShadowSocks, error) {\n\taddr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))\n\tmethod, err := shadowsocks.CreateMethod(option.Cipher, shadowsocks.MethodOptions{\n\t\tPassword: option.Password,\n\t\tTimeFunc: ntp.Now,\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"ss %s cipher: %s initialize error: %w\", addr, option.Cipher, err)\n\t}\n\n\tvar v2rayOption *v2rayObfs.Option\n\tvar gostOption *gost.Option\n\tvar obfsOption *simpleObfsOption\n\tvar shadowTLSOpt *shadowtls.ShadowTLSOption\n\tvar restlsConfig *restls.Config\n\tvar kcptunClient *kcptun.Client\n\tobfsMode := \"\"\n\n\tdecoder := structure.NewDecoder(structure.Option{TagName: \"obfs\", WeaklyTypedInput: true})\n\tif option.Plugin == \"obfs\" {\n\t\topts := simpleObfsOption{Host: \"bing.com\"}\n\t\tif err := decoder.Decode(option.PluginOpts, &opts); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"ss %s initialize obfs error: %w\", addr, err)\n\t\t}\n\n\t\tif opts.Mode != \"tls\" && opts.Mode != \"http\" {\n\t\t\treturn nil, fmt.Errorf(\"ss %s obfs mode error: %s\", addr, opts.Mode)\n\t\t}\n\t\tobfsMode = opts.Mode\n\t\tobfsOption = &opts\n\t} else if option.Plugin == \"v2ray-plugin\" {\n\t\topts := v2rayObfsOption{Host: \"bing.com\", Mux: true}\n\t\tif err := decoder.Decode(option.PluginOpts, &opts); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"ss %s initialize v2ray-plugin error: %w\", addr, err)\n\t\t}\n\n\t\tif opts.Mode != \"websocket\" {\n\t\t\treturn nil, fmt.Errorf(\"ss %s obfs mode error: %s\", addr, opts.Mode)\n\t\t}\n\t\tobfsMode = opts.Mode\n\t\tv2rayOption = &v2rayObfs.Option{\n\t\t\tHost:                     opts.Host,\n\t\t\tPath:                     opts.Path,\n\t\t\tHeaders:                  opts.Headers,\n\t\t\tMux:                      opts.Mux,\n\t\t\tV2rayHttpUpgrade:         opts.V2rayHttpUpgrade,\n\t\t\tV2rayHttpUpgradeFastOpen: opts.V2rayHttpUpgradeFastOpen,\n\t\t}\n\n\t\tif opts.TLS {\n\t\t\tv2rayOption.TLS = true\n\t\t\tv2rayOption.SkipCertVerify = opts.SkipCertVerify\n\t\t\tv2rayOption.Fingerprint = opts.Fingerprint\n\t\t\tv2rayOption.Certificate = opts.Certificate\n\t\t\tv2rayOption.PrivateKey = opts.PrivateKey\n\n\t\t\techConfig, err := opts.ECHOpts.Parse()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"ss %s initialize v2ray-plugin error: %w\", addr, err)\n\t\t\t}\n\t\t\tv2rayOption.ECHConfig = echConfig\n\t\t}\n\t} else if option.Plugin == \"gost-plugin\" {\n\t\topts := gostObfsOption{Host: \"bing.com\", Mux: true}\n\t\tif err := decoder.Decode(option.PluginOpts, &opts); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"ss %s initialize gost-plugin error: %w\", addr, err)\n\t\t}\n\n\t\tif opts.Mode != \"websocket\" {\n\t\t\treturn nil, fmt.Errorf(\"ss %s obfs mode error: %s\", addr, opts.Mode)\n\t\t}\n\t\tobfsMode = opts.Mode\n\t\tgostOption = &gost.Option{\n\t\t\tHost:    opts.Host,\n\t\t\tPath:    opts.Path,\n\t\t\tHeaders: opts.Headers,\n\t\t\tMux:     opts.Mux,\n\t\t}\n\n\t\tif opts.TLS {\n\t\t\tgostOption.TLS = true\n\t\t\tgostOption.SkipCertVerify = opts.SkipCertVerify\n\t\t\tgostOption.Fingerprint = opts.Fingerprint\n\t\t\tgostOption.Certificate = opts.Certificate\n\t\t\tgostOption.PrivateKey = opts.PrivateKey\n\n\t\t\techConfig, err := opts.ECHOpts.Parse()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"ss %s initialize gost-plugin error: %w\", addr, err)\n\t\t\t}\n\t\t\tgostOption.ECHConfig = echConfig\n\t\t}\n\t} else if option.Plugin == shadowtls.Mode {\n\t\tobfsMode = shadowtls.Mode\n\t\topt := &shadowTLSOption{\n\t\t\tVersion: 2,\n\t\t}\n\t\tif err := decoder.Decode(option.PluginOpts, opt); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"ss %s initialize shadow-tls-plugin error: %w\", addr, err)\n\t\t}\n\n\t\tshadowTLSOpt = &shadowtls.ShadowTLSOption{\n\t\t\tPassword:          opt.Password,\n\t\t\tHost:              opt.Host,\n\t\t\tFingerprint:       opt.Fingerprint,\n\t\t\tCertificate:       opt.Certificate,\n\t\t\tPrivateKey:        opt.PrivateKey,\n\t\t\tClientFingerprint: option.ClientFingerprint,\n\t\t\tSkipCertVerify:    opt.SkipCertVerify,\n\t\t\tVersion:           opt.Version,\n\t\t}\n\n\t\tif opt.ALPN != nil { // structure's Decode will ensure value not nil when input has value even it was set an empty array\n\t\t\tshadowTLSOpt.ALPN = opt.ALPN\n\t\t} else {\n\t\t\tshadowTLSOpt.ALPN = shadowtls.DefaultALPN\n\t\t}\n\t} else if option.Plugin == restls.Mode {\n\t\tobfsMode = restls.Mode\n\t\trestlsOpt := &restlsOption{}\n\t\tif err := decoder.Decode(option.PluginOpts, restlsOpt); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"ss %s initialize restls-plugin error: %w\", addr, err)\n\t\t}\n\n\t\trestlsConfig, err = restls.NewRestlsConfig(restlsOpt.Host, restlsOpt.Password, restlsOpt.VersionHint, restlsOpt.RestlsScript, option.ClientFingerprint)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"ss %s initialize restls-plugin error: %w\", addr, err)\n\t\t}\n\n\t} else if option.Plugin == kcptun.Mode {\n\t\tobfsMode = kcptun.Mode\n\t\tkcptunOpt := &kcpTunOption{}\n\t\tif err := decoder.Decode(option.PluginOpts, kcptunOpt); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"ss %s initialize kcptun-plugin error: %w\", addr, err)\n\t\t}\n\n\t\tkcptunClient = kcptun.NewClient(kcptun.Config{\n\t\t\tKey:          kcptunOpt.Key,\n\t\t\tCrypt:        kcptunOpt.Crypt,\n\t\t\tMode:         kcptunOpt.Mode,\n\t\t\tConn:         kcptunOpt.Conn,\n\t\t\tAutoExpire:   kcptunOpt.AutoExpire,\n\t\t\tScavengeTTL:  kcptunOpt.ScavengeTTL,\n\t\t\tMTU:          kcptunOpt.MTU,\n\t\t\tRateLimit:    kcptunOpt.RateLimit,\n\t\t\tSndWnd:       kcptunOpt.SndWnd,\n\t\t\tRcvWnd:       kcptunOpt.RcvWnd,\n\t\t\tDataShard:    kcptunOpt.DataShard,\n\t\t\tParityShard:  kcptunOpt.ParityShard,\n\t\t\tDSCP:         kcptunOpt.DSCP,\n\t\t\tNoComp:       kcptunOpt.NoComp,\n\t\t\tAckNodelay:   kcptunOpt.AckNodelay,\n\t\t\tNoDelay:      kcptunOpt.NoDelay,\n\t\t\tInterval:     kcptunOpt.Interval,\n\t\t\tResend:       kcptunOpt.Resend,\n\t\t\tNoCongestion: kcptunOpt.NoCongestion,\n\t\t\tSockBuf:      kcptunOpt.SockBuf,\n\t\t\tSmuxVer:      kcptunOpt.SmuxVer,\n\t\t\tSmuxBuf:      kcptunOpt.SmuxBuf,\n\t\t\tFrameSize:    kcptunOpt.FrameSize,\n\t\t\tStreamBuf:    kcptunOpt.StreamBuf,\n\t\t\tKeepAlive:    kcptunOpt.KeepAlive,\n\t\t})\n\t\toption.UDPOverTCP = true // must open uot\n\t}\n\tswitch option.UDPOverTCPVersion {\n\tcase uot.Version, uot.LegacyVersion:\n\tcase 0:\n\t\toption.UDPOverTCPVersion = uot.LegacyVersion\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"ss %s unknown udp over tcp protocol version: %d\", addr, option.UDPOverTCPVersion)\n\t}\n\n\toutbound := &ShadowSocks{\n\t\tBase: NewBase(BaseOption{\n\t\t\tName:         option.Name,\n\t\t\tAddr:         addr,\n\t\t\tType:         C.Shadowsocks,\n\t\t\tProviderName: option.ProviderName,\n\t\t\tUDP:          option.UDP,\n\t\t\tTFO:          option.TFO,\n\t\t\tMPTCP:        option.MPTCP,\n\t\t\tInterface:    option.Interface,\n\t\t\tRoutingMark:  option.RoutingMark,\n\t\t\tPrefer:       option.IPVersion,\n\t\t}),\n\t\tmethod: method,\n\n\t\toption:          &option,\n\t\tobfsMode:        obfsMode,\n\t\tv2rayOption:     v2rayOption,\n\t\tgostOption:      gostOption,\n\t\tobfsOption:      obfsOption,\n\t\tshadowTLSOption: shadowTLSOpt,\n\t\trestlsConfig:    restlsConfig,\n\t\tkcptunClient:    kcptunClient,\n\t}\n\toutbound.dialer = option.NewDialer(outbound.DialOptions())\n\treturn outbound, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outbound/shadowsocksr.go",
    "content": "package outbound\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"strconv\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/transport/shadowsocks/core\"\n\t\"github.com/metacubex/mihomo/transport/shadowsocks/shadowaead\"\n\t\"github.com/metacubex/mihomo/transport/shadowsocks/shadowstream\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n\t\"github.com/metacubex/mihomo/transport/ssr/obfs\"\n\t\"github.com/metacubex/mihomo/transport/ssr/protocol\"\n)\n\ntype ShadowSocksR struct {\n\t*Base\n\toption   *ShadowSocksROption\n\tcipher   core.Cipher\n\tobfs     obfs.Obfs\n\tprotocol protocol.Protocol\n}\n\ntype ShadowSocksROption struct {\n\tBasicOption\n\tName          string `proxy:\"name\"`\n\tServer        string `proxy:\"server\"`\n\tPort          int    `proxy:\"port\"`\n\tPassword      string `proxy:\"password\"`\n\tCipher        string `proxy:\"cipher\"`\n\tObfs          string `proxy:\"obfs\"`\n\tObfsParam     string `proxy:\"obfs-param,omitempty\"`\n\tProtocol      string `proxy:\"protocol\"`\n\tProtocolParam string `proxy:\"protocol-param,omitempty\"`\n\tUDP           bool   `proxy:\"udp,omitempty\"`\n}\n\n// StreamConnContext implements C.ProxyAdapter\nfunc (ssr *ShadowSocksR) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ net.Conn, err error) {\n\tif ctx.Done() != nil {\n\t\tdone := N.SetupContextForConn(ctx, c)\n\t\tdefer done(&err)\n\t}\n\tc = ssr.obfs.StreamConn(c)\n\tc = ssr.cipher.StreamConn(c)\n\tvar (\n\t\tiv []byte\n\t)\n\tswitch conn := c.(type) {\n\tcase *shadowstream.Conn:\n\t\tiv, err = conn.ObtainWriteIV()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\tcase *shadowaead.Conn:\n\t\treturn nil, fmt.Errorf(\"invalid connection type\")\n\t}\n\tc = ssr.protocol.StreamConn(c, iv)\n\t_, err = c.Write(serializesSocksAddr(metadata))\n\treturn c, err\n}\n\n// DialContext implements C.ProxyAdapter\nfunc (ssr *ShadowSocksR) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Conn, err error) {\n\tc, err := ssr.dialer.DialContext(ctx, \"tcp\", ssr.addr)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s connect error: %w\", ssr.addr, err)\n\t}\n\n\tdefer func(c net.Conn) {\n\t\tsafeConnClose(c, err)\n\t}(c)\n\n\tc, err = ssr.StreamConnContext(ctx, c, metadata)\n\treturn NewConn(c, ssr), err\n}\n\n// ListenPacketContext implements C.ProxyAdapter\nfunc (ssr *ShadowSocksR) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (C.PacketConn, error) {\n\tif err := ssr.ResolveUDP(ctx, metadata); err != nil {\n\t\treturn nil, err\n\t}\n\taddr, err := resolveUDPAddr(ctx, \"udp\", ssr.addr, ssr.prefer)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tpc, err := ssr.dialer.ListenPacket(ctx, \"udp\", \"\", addr.AddrPort())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tepc := ssr.cipher.PacketConn(N.NewEnhancePacketConn(pc))\n\tepc = ssr.protocol.PacketConn(epc)\n\treturn newPacketConn(&ssrPacketConn{EnhancePacketConn: epc, rAddr: addr}, ssr), nil\n}\n\n// ProxyInfo implements C.ProxyAdapter\nfunc (ssr *ShadowSocksR) ProxyInfo() C.ProxyInfo {\n\tinfo := ssr.Base.ProxyInfo()\n\tinfo.DialerProxy = ssr.option.DialerProxy\n\treturn info\n}\n\nfunc NewShadowSocksR(option ShadowSocksROption) (*ShadowSocksR, error) {\n\t// SSR protocol compatibility\n\t// https://github.com/metacubex/mihomo/pull/2056\n\tif option.Cipher == \"none\" {\n\t\toption.Cipher = \"dummy\"\n\t}\n\n\taddr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))\n\tcipher := option.Cipher\n\tpassword := option.Password\n\tcoreCiph, err := core.PickCipher(cipher, nil, password)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"ssr %s cipher: %s initialize error: %w\", addr, cipher, err)\n\t}\n\tvar (\n\t\tivSize int\n\t\tkey    []byte\n\t)\n\n\tif option.Cipher == \"dummy\" {\n\t\tivSize = 0\n\t\tkey = core.Kdf(option.Password, 16)\n\t} else {\n\t\tciph, ok := coreCiph.(*core.StreamCipher)\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"%s is not none or a supported stream cipher in ssr\", cipher)\n\t\t}\n\t\tivSize = ciph.IVSize()\n\t\tkey = ciph.Key\n\t}\n\n\tobfs, obfsOverhead, err := obfs.PickObfs(option.Obfs, &obfs.Base{\n\t\tHost:   option.Server,\n\t\tPort:   option.Port,\n\t\tKey:    key,\n\t\tIVSize: ivSize,\n\t\tParam:  option.ObfsParam,\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"ssr %s initialize obfs error: %w\", addr, err)\n\t}\n\n\tprotocol, err := protocol.PickProtocol(option.Protocol, &protocol.Base{\n\t\tKey:      key,\n\t\tOverhead: obfsOverhead,\n\t\tParam:    option.ProtocolParam,\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"ssr %s initialize protocol error: %w\", addr, err)\n\t}\n\n\toutbound := &ShadowSocksR{\n\t\tBase: NewBase(BaseOption{\n\t\t\tName:         option.Name,\n\t\t\tAddr:         addr,\n\t\t\tType:         C.ShadowsocksR,\n\t\t\tProviderName: option.ProviderName,\n\t\t\tUDP:          option.UDP,\n\t\t\tTFO:          option.TFO,\n\t\t\tMPTCP:        option.MPTCP,\n\t\t\tInterface:    option.Interface,\n\t\t\tRoutingMark:  option.RoutingMark,\n\t\t\tPrefer:       option.IPVersion,\n\t\t}),\n\t\toption:   &option,\n\t\tcipher:   coreCiph,\n\t\tobfs:     obfs,\n\t\tprotocol: protocol,\n\t}\n\toutbound.dialer = option.NewDialer(outbound.DialOptions())\n\treturn outbound, nil\n}\n\ntype ssrPacketConn struct {\n\tN.EnhancePacketConn\n\trAddr net.Addr\n}\n\nfunc (spc *ssrPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {\n\tpacket, err := socks5.EncodeUDPPacket(socks5.ParseAddrToSocksAddr(addr), b)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn spc.EnhancePacketConn.WriteTo(packet[3:], spc.rAddr)\n}\n\nfunc (spc *ssrPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {\n\tn, _, e := spc.EnhancePacketConn.ReadFrom(b)\n\tif e != nil {\n\t\treturn 0, nil, e\n\t}\n\n\taddr := socks5.SplitAddr(b[:n])\n\tif addr == nil {\n\t\treturn 0, nil, errors.New(\"parse addr error\")\n\t}\n\n\tudpAddr := addr.UDPAddr()\n\tif udpAddr == nil {\n\t\treturn 0, nil, errors.New(\"parse addr error\")\n\t}\n\n\tcopy(b, b[len(addr):])\n\treturn n - len(addr), udpAddr, e\n}\n\nfunc (spc *ssrPacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {\n\tdata, put, _, err = spc.EnhancePacketConn.WaitReadFrom()\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\n\t_addr := socks5.SplitAddr(data)\n\tif _addr == nil {\n\t\tif put != nil {\n\t\t\tput()\n\t\t}\n\t\treturn nil, nil, nil, errors.New(\"parse addr error\")\n\t}\n\n\tudpAddr := _addr.UDPAddr()\n\tif udpAddr == nil {\n\t\tif put != nil {\n\t\t\tput()\n\t\t}\n\t\treturn nil, nil, nil, errors.New(\"parse addr error\")\n\t}\n\taddr = udpAddr\n\n\tdata = data[len(_addr):]\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outbound/singmux.go",
    "content": "package outbound\n\nimport (\n\t\"context\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/component/proxydialer\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\tmux \"github.com/metacubex/sing-mux\"\n\tE \"github.com/metacubex/sing/common/exceptions\"\n\tM \"github.com/metacubex/sing/common/metadata\"\n)\n\ntype SingMux struct {\n\tProxyAdapter\n\tclient  *mux.Client\n\tonlyTcp bool\n}\n\ntype SingMuxOption struct {\n\tEnabled        bool         `proxy:\"enabled,omitempty\"`\n\tProtocol       string       `proxy:\"protocol,omitempty\"`\n\tMaxConnections int          `proxy:\"max-connections,omitempty\"`\n\tMinStreams     int          `proxy:\"min-streams,omitempty\"`\n\tMaxStreams     int          `proxy:\"max-streams,omitempty\"`\n\tPadding        bool         `proxy:\"padding,omitempty\"`\n\tStatistic      bool         `proxy:\"statistic,omitempty\"`\n\tOnlyTcp        bool         `proxy:\"only-tcp,omitempty\"`\n\tBrutalOpts     BrutalOption `proxy:\"brutal-opts,omitempty\"`\n}\n\ntype BrutalOption struct {\n\tEnabled bool   `proxy:\"enabled,omitempty\"`\n\tUp      string `proxy:\"up,omitempty\"`\n\tDown    string `proxy:\"down,omitempty\"`\n}\n\nfunc (s *SingMux) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Conn, err error) {\n\tc, err := s.client.DialContext(ctx, \"tcp\", M.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewConn(c, s), err\n}\n\nfunc (s *SingMux) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {\n\tif s.onlyTcp {\n\t\treturn s.ProxyAdapter.ListenPacketContext(ctx, metadata)\n\t}\n\tif err = s.ProxyAdapter.ResolveUDP(ctx, metadata); err != nil {\n\t\treturn nil, err\n\t}\n\tpc, err := s.client.ListenPacket(ctx, M.SocksaddrFromNet(metadata.UDPAddr()))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif pc == nil {\n\t\treturn nil, E.New(\"packetConn is nil\")\n\t}\n\treturn newPacketConn(N.NewThreadSafePacketConn(pc), s), nil\n}\n\nfunc (s *SingMux) SupportUDP() bool {\n\tif s.onlyTcp {\n\t\treturn s.ProxyAdapter.SupportUDP()\n\t}\n\treturn true\n}\n\nfunc (s *SingMux) SupportUOT() bool {\n\tif s.onlyTcp {\n\t\treturn s.ProxyAdapter.SupportUOT()\n\t}\n\treturn true\n}\n\nfunc (s *SingMux) ProxyInfo() C.ProxyInfo {\n\tinfo := s.ProxyAdapter.ProxyInfo()\n\tinfo.SMUX = true\n\treturn info\n}\n\n// Close implements C.ProxyAdapter\nfunc (s *SingMux) Close() error {\n\tif s.client != nil {\n\t\t_ = s.client.Close()\n\t}\n\treturn s.ProxyAdapter.Close()\n}\n\nfunc NewSingMux(option SingMuxOption, proxy ProxyAdapter) (ProxyAdapter, error) {\n\t// TODO\n\t// \"TCP Brutal is only supported on Linux-based systems\"\n\n\tsingDialer := proxydialer.NewSingDialer(proxydialer.New(proxy, option.Statistic))\n\tclient, err := mux.NewClient(mux.Options{\n\t\tDialer:         singDialer,\n\t\tLogger:         log.SingLogger,\n\t\tProtocol:       option.Protocol,\n\t\tMaxConnections: option.MaxConnections,\n\t\tMinStreams:     option.MinStreams,\n\t\tMaxStreams:     option.MaxStreams,\n\t\tPadding:        option.Padding,\n\t\tTCPTimeout:     C.DefaultTCPTimeout,\n\t\tBrutal: mux.BrutalOptions{\n\t\t\tEnabled:    option.BrutalOpts.Enabled,\n\t\t\tSendBPS:    StringToBps(option.BrutalOpts.Up),\n\t\t\tReceiveBPS: StringToBps(option.BrutalOpts.Down),\n\t\t},\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\toutbound := &SingMux{\n\t\tProxyAdapter: proxy,\n\t\tclient:       client,\n\t\tonlyTcp:      option.OnlyTcp,\n\t}\n\treturn outbound, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outbound/snell.go",
    "content": "package outbound\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"strconv\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/structure\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tobfs \"github.com/metacubex/mihomo/transport/simple-obfs\"\n\t\"github.com/metacubex/mihomo/transport/snell\"\n)\n\ntype Snell struct {\n\t*Base\n\toption     *SnellOption\n\tpsk        []byte\n\tpool       *snell.Pool\n\tobfsOption *simpleObfsOption\n\tversion    int\n}\n\ntype SnellOption struct {\n\tBasicOption\n\tName     string         `proxy:\"name\"`\n\tServer   string         `proxy:\"server\"`\n\tPort     int            `proxy:\"port\"`\n\tPsk      string         `proxy:\"psk\"`\n\tUDP      bool           `proxy:\"udp,omitempty\"`\n\tVersion  int            `proxy:\"version,omitempty\"`\n\tObfsOpts map[string]any `proxy:\"obfs-opts,omitempty\"`\n}\n\ntype streamOption struct {\n\tpsk        []byte\n\tversion    int\n\taddr       string\n\tobfsOption *simpleObfsOption\n}\n\nfunc snellStreamConn(c net.Conn, option streamOption) *snell.Snell {\n\tswitch option.obfsOption.Mode {\n\tcase \"tls\":\n\t\tc = obfs.NewTLSObfs(c, option.obfsOption.Host)\n\tcase \"http\":\n\t\t_, port, _ := net.SplitHostPort(option.addr)\n\t\tc = obfs.NewHTTPObfs(c, option.obfsOption.Host, port)\n\t}\n\treturn snell.StreamConn(c, option.psk, option.version)\n}\n\n// StreamConnContext implements C.ProxyAdapter\nfunc (s *Snell) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (net.Conn, error) {\n\tc = snellStreamConn(c, streamOption{s.psk, s.version, s.addr, s.obfsOption})\n\terr := s.writeHeaderContext(ctx, c, metadata)\n\treturn c, err\n}\n\nfunc (s *Snell) writeHeaderContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (err error) {\n\tif ctx.Done() != nil {\n\t\tdone := N.SetupContextForConn(ctx, c)\n\t\tdefer done(&err)\n\t}\n\n\tif metadata.NetWork == C.UDP {\n\t\terr = snell.WriteUDPHeader(c, s.version)\n\t\treturn\n\t}\n\terr = snell.WriteHeader(c, metadata.String(), uint(metadata.DstPort), s.version)\n\treturn\n}\n\n// DialContext implements C.ProxyAdapter\nfunc (s *Snell) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Conn, err error) {\n\tif s.version == snell.Version2 {\n\t\tc, err := s.pool.Get()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif err = s.writeHeaderContext(ctx, c, metadata); err != nil {\n\t\t\t_ = c.Close()\n\t\t\treturn nil, err\n\t\t}\n\t\treturn NewConn(c, s), err\n\t}\n\n\tc, err := s.dialer.DialContext(ctx, \"tcp\", s.addr)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s connect error: %w\", s.addr, err)\n\t}\n\n\tdefer func(c net.Conn) {\n\t\tsafeConnClose(c, err)\n\t}(c)\n\n\tc, err = s.StreamConnContext(ctx, c, metadata)\n\treturn NewConn(c, s), err\n}\n\n// ListenPacketContext implements C.ProxyAdapter\nfunc (s *Snell) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {\n\tif err = s.ResolveUDP(ctx, metadata); err != nil {\n\t\treturn nil, err\n\t}\n\tc, err := s.dialer.DialContext(ctx, \"tcp\", s.addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdefer func(c net.Conn) {\n\t\tsafeConnClose(c, err)\n\t}(c)\n\n\tc, err = s.StreamConnContext(ctx, c, metadata)\n\n\tpc := snell.PacketConn(c)\n\treturn newPacketConn(pc, s), nil\n}\n\n// SupportUOT implements C.ProxyAdapter\nfunc (s *Snell) SupportUOT() bool {\n\treturn true\n}\n\n// ProxyInfo implements C.ProxyAdapter\nfunc (s *Snell) ProxyInfo() C.ProxyInfo {\n\tinfo := s.Base.ProxyInfo()\n\tinfo.DialerProxy = s.option.DialerProxy\n\treturn info\n}\n\nfunc NewSnell(option SnellOption) (*Snell, error) {\n\taddr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))\n\tpsk := []byte(option.Psk)\n\n\tdecoder := structure.NewDecoder(structure.Option{TagName: \"obfs\", WeaklyTypedInput: true})\n\tobfsOption := &simpleObfsOption{Host: \"bing.com\"}\n\tif err := decoder.Decode(option.ObfsOpts, obfsOption); err != nil {\n\t\treturn nil, fmt.Errorf(\"snell %s initialize obfs error: %w\", addr, err)\n\t}\n\n\tswitch obfsOption.Mode {\n\tcase \"tls\", \"http\", \"\":\n\t\tbreak\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"snell %s obfs mode error: %s\", addr, obfsOption.Mode)\n\t}\n\n\t// backward compatible\n\tif option.Version == 0 {\n\t\toption.Version = snell.DefaultSnellVersion\n\t}\n\tswitch option.Version {\n\tcase snell.Version1, snell.Version2:\n\t\tif option.UDP {\n\t\t\treturn nil, fmt.Errorf(\"snell version %d not support UDP\", option.Version)\n\t\t}\n\tcase snell.Version3:\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"snell version error: %d\", option.Version)\n\t}\n\n\ts := &Snell{\n\t\tBase: NewBase(BaseOption{\n\t\t\tName:         option.Name,\n\t\t\tAddr:         addr,\n\t\t\tType:         C.Snell,\n\t\t\tProviderName: option.ProviderName,\n\t\t\tUDP:          option.UDP,\n\t\t\tTFO:          option.TFO,\n\t\t\tMPTCP:        option.MPTCP,\n\t\t\tInterface:    option.Interface,\n\t\t\tRoutingMark:  option.RoutingMark,\n\t\t\tPrefer:       option.IPVersion,\n\t\t}),\n\t\toption:     &option,\n\t\tpsk:        psk,\n\t\tobfsOption: obfsOption,\n\t\tversion:    option.Version,\n\t}\n\ts.dialer = option.NewDialer(s.DialOptions())\n\n\tif option.Version == snell.Version2 {\n\t\ts.pool = snell.NewPool(func(ctx context.Context) (*snell.Snell, error) {\n\t\t\tc, err := s.dialer.DialContext(ctx, \"tcp\", addr)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\treturn snellStreamConn(c, streamOption{psk, option.Version, addr, obfsOption}), nil\n\t\t})\n\t}\n\treturn s, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outbound/socks5.go",
    "content": "package outbound\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/netip\"\n\t\"strconv\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/component/ca\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n\n\t\"github.com/metacubex/tls\"\n)\n\ntype Socks5 struct {\n\t*Base\n\toption         *Socks5Option\n\tuser           string\n\tpass           string\n\ttls            bool\n\tskipCertVerify bool\n\ttlsConfig      *tls.Config\n}\n\ntype Socks5Option struct {\n\tBasicOption\n\tName           string `proxy:\"name\"`\n\tServer         string `proxy:\"server\"`\n\tPort           int    `proxy:\"port\"`\n\tUserName       string `proxy:\"username,omitempty\"`\n\tPassword       string `proxy:\"password,omitempty\"`\n\tTLS            bool   `proxy:\"tls,omitempty\"`\n\tUDP            bool   `proxy:\"udp,omitempty\"`\n\tSkipCertVerify bool   `proxy:\"skip-cert-verify,omitempty\"`\n\tFingerprint    string `proxy:\"fingerprint,omitempty\"`\n\tCertificate    string `proxy:\"certificate,omitempty\"`\n\tPrivateKey     string `proxy:\"private-key,omitempty\"`\n}\n\n// StreamConnContext implements C.ProxyAdapter\nfunc (ss *Socks5) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (net.Conn, error) {\n\tif ss.tls {\n\t\tcc := tls.Client(c, ss.tlsConfig)\n\t\terr := cc.HandshakeContext(ctx)\n\t\tc = cc\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%s connect error: %w\", ss.addr, err)\n\t\t}\n\t}\n\n\tvar user *socks5.User\n\tif ss.user != \"\" {\n\t\tuser = &socks5.User{\n\t\t\tUsername: ss.user,\n\t\t\tPassword: ss.pass,\n\t\t}\n\t}\n\tif _, err := ss.clientHandshakeContext(ctx, c, serializesSocksAddr(metadata), socks5.CmdConnect, user); err != nil {\n\t\treturn nil, err\n\t}\n\treturn c, nil\n}\n\n// DialContext implements C.ProxyAdapter\nfunc (ss *Socks5) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Conn, err error) {\n\tc, err := ss.dialer.DialContext(ctx, \"tcp\", ss.addr)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s connect error: %w\", ss.addr, err)\n\t}\n\n\tdefer func(c net.Conn) {\n\t\tsafeConnClose(c, err)\n\t}(c)\n\n\tc, err = ss.StreamConnContext(ctx, c, metadata)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn NewConn(c, ss), nil\n}\n\n// ListenPacketContext implements C.ProxyAdapter\nfunc (ss *Socks5) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {\n\tif err = ss.ResolveUDP(ctx, metadata); err != nil {\n\t\treturn nil, err\n\t}\n\tc, err := ss.dialer.DialContext(ctx, \"tcp\", ss.addr)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"%s connect error: %w\", ss.addr, err)\n\t\treturn\n\t}\n\n\tdefer func(c net.Conn) {\n\t\tsafeConnClose(c, err)\n\t}(c)\n\n\tif ss.tls {\n\t\tcc := tls.Client(c, ss.tlsConfig)\n\t\terr = cc.HandshakeContext(ctx)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%s connect error: %w\", ss.addr, err)\n\t\t}\n\t\tc = cc\n\t}\n\n\tvar user *socks5.User\n\tif ss.user != \"\" {\n\t\tuser = &socks5.User{\n\t\t\tUsername: ss.user,\n\t\t\tPassword: ss.pass,\n\t\t}\n\t}\n\n\tudpAssocateAddr := socks5.AddrFromStdAddrPort(netip.AddrPortFrom(netip.IPv4Unspecified(), 0))\n\tbindAddr, err := ss.clientHandshakeContext(ctx, c, udpAssocateAddr, socks5.CmdUDPAssociate, user)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"client hanshake error: %w\", err)\n\t\treturn\n\t}\n\n\t// Support unspecified UDP bind address.\n\tbindUDPAddr := bindAddr.UDPAddr()\n\tif bindUDPAddr == nil {\n\t\terr = errors.New(\"invalid UDP bind address\")\n\t\treturn\n\t} else if bindUDPAddr.IP.IsUnspecified() {\n\t\tserverAddr, err := resolveUDPAddr(ctx, \"udp\", ss.Addr(), C.IPv4Prefer)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tbindUDPAddr.IP = serverAddr.IP\n\t}\n\n\tpc, err := ss.dialer.ListenPacket(ctx, \"udp\", \"\", bindUDPAddr.AddrPort())\n\tif err != nil {\n\t\treturn\n\t}\n\n\tgo func() {\n\t\tio.Copy(io.Discard, c)\n\t\tc.Close()\n\t\t// A UDP association terminates when the TCP connection that the UDP\n\t\t// ASSOCIATE request arrived on terminates. RFC1928\n\t\tpc.Close()\n\t}()\n\n\treturn newPacketConn(&socksPacketConn{PacketConn: pc, rAddr: bindUDPAddr, tcpConn: c}, ss), nil\n}\n\n// ProxyInfo implements C.ProxyAdapter\nfunc (ss *Socks5) ProxyInfo() C.ProxyInfo {\n\tinfo := ss.Base.ProxyInfo()\n\tinfo.DialerProxy = ss.option.DialerProxy\n\treturn info\n}\n\nfunc (ss *Socks5) clientHandshakeContext(ctx context.Context, c net.Conn, addr socks5.Addr, command socks5.Command, user *socks5.User) (_ socks5.Addr, err error) {\n\tif ctx.Done() != nil {\n\t\tdone := N.SetupContextForConn(ctx, c)\n\t\tdefer done(&err)\n\t}\n\treturn socks5.ClientHandshake(c, addr, command, user)\n}\n\nfunc NewSocks5(option Socks5Option) (*Socks5, error) {\n\tvar tlsConfig *tls.Config\n\tif option.TLS {\n\t\tvar err error\n\t\ttlsConfig, err = ca.GetTLSConfig(ca.Option{\n\t\t\tTLSConfig: &tls.Config{\n\t\t\t\tInsecureSkipVerify: option.SkipCertVerify,\n\t\t\t\tServerName:         option.Server,\n\t\t\t},\n\t\t\tFingerprint: option.Fingerprint,\n\t\t\tCertificate: option.Certificate,\n\t\t\tPrivateKey:  option.PrivateKey,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\toutbound := &Socks5{\n\t\tBase: NewBase(BaseOption{\n\t\t\tName:         option.Name,\n\t\t\tAddr:         net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),\n\t\t\tType:         C.Socks5,\n\t\t\tProviderName: option.ProviderName,\n\t\t\tUDP:          option.UDP,\n\t\t\tTFO:          option.TFO,\n\t\t\tMPTCP:        option.MPTCP,\n\t\t\tInterface:    option.Interface,\n\t\t\tRoutingMark:  option.RoutingMark,\n\t\t\tPrefer:       option.IPVersion,\n\t\t}),\n\t\toption:         &option,\n\t\tuser:           option.UserName,\n\t\tpass:           option.Password,\n\t\ttls:            option.TLS,\n\t\tskipCertVerify: option.SkipCertVerify,\n\t\ttlsConfig:      tlsConfig,\n\t}\n\toutbound.dialer = option.NewDialer(outbound.DialOptions())\n\treturn outbound, nil\n}\n\ntype socksPacketConn struct {\n\tnet.PacketConn\n\trAddr   net.Addr\n\ttcpConn net.Conn\n}\n\nfunc (uc *socksPacketConn) WriteTo(b []byte, addr net.Addr) (n int, err error) {\n\tpacket, err := socks5.EncodeUDPPacket(socks5.ParseAddrToSocksAddr(addr), b)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn uc.PacketConn.WriteTo(packet, uc.rAddr)\n}\n\nfunc (uc *socksPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {\n\tn, _, e := uc.PacketConn.ReadFrom(b)\n\tif e != nil {\n\t\treturn 0, nil, e\n\t}\n\taddr, payload, err := socks5.DecodeUDPPacket(b)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\n\tudpAddr := addr.UDPAddr()\n\tif udpAddr == nil {\n\t\treturn 0, nil, errors.New(\"parse udp addr error\")\n\t}\n\n\t// due to DecodeUDPPacket is mutable, record addr length\n\tcopy(b, payload)\n\treturn n - len(addr) - 3, udpAddr, nil\n}\n\nfunc (uc *socksPacketConn) Close() error {\n\tuc.tcpConn.Close()\n\treturn uc.PacketConn.Close()\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outbound/ssh.go",
    "content": "package outbound\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\n\t\"github.com/metacubex/randv2\"\n\t\"github.com/metacubex/ssh\"\n)\n\ntype Ssh struct {\n\t*Base\n\n\toption *SshOption\n\n\tconfig *ssh.ClientConfig\n\tclient *ssh.Client\n\tcMutex sync.Mutex\n}\n\ntype SshOption struct {\n\tBasicOption\n\tName                 string   `proxy:\"name\"`\n\tServer               string   `proxy:\"server\"`\n\tPort                 int      `proxy:\"port\"`\n\tUserName             string   `proxy:\"username\"`\n\tPassword             string   `proxy:\"password,omitempty\"`\n\tPrivateKey           string   `proxy:\"private-key,omitempty\"`\n\tPrivateKeyPassphrase string   `proxy:\"private-key-passphrase,omitempty\"`\n\tHostKey              []string `proxy:\"host-key,omitempty\"`\n\tHostKeyAlgorithms    []string `proxy:\"host-key-algorithms,omitempty\"`\n}\n\nfunc (s *Ssh) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Conn, err error) {\n\tclient, err := s.connect(ctx, s.addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tc, err := client.DialContext(ctx, \"tcp\", metadata.RemoteAddress())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn NewConn(c, s), nil\n}\n\nfunc (s *Ssh) connect(ctx context.Context, addr string) (client *ssh.Client, err error) {\n\ts.cMutex.Lock()\n\tdefer s.cMutex.Unlock()\n\tif s.client != nil {\n\t\treturn s.client, nil\n\t}\n\tc, err := s.dialer.DialContext(ctx, \"tcp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdefer func(c net.Conn) {\n\t\tsafeConnClose(c, err)\n\t}(c)\n\n\tif ctx.Done() != nil {\n\t\tdone := N.SetupContextForConn(ctx, c)\n\t\tdefer done(&err)\n\t}\n\n\tclientConn, chans, reqs, err := ssh.NewClientConn(c, addr, s.config)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tclient = ssh.NewClient(clientConn, chans, reqs)\n\n\ts.client = client\n\n\tgo func() {\n\t\t_ = client.Wait() // wait shutdown\n\t\t_ = client.Close()\n\t\ts.cMutex.Lock()\n\t\tdefer s.cMutex.Unlock()\n\t\tif s.client == client {\n\t\t\ts.client = nil\n\t\t}\n\t}()\n\n\treturn client, nil\n}\n\n// ProxyInfo implements C.ProxyAdapter\nfunc (s *Ssh) ProxyInfo() C.ProxyInfo {\n\tinfo := s.Base.ProxyInfo()\n\tinfo.DialerProxy = s.option.DialerProxy\n\treturn info\n}\n\n// Close implements C.ProxyAdapter\nfunc (s *Ssh) Close() error {\n\ts.cMutex.Lock()\n\tdefer s.cMutex.Unlock()\n\tif s.client != nil {\n\t\treturn s.client.Close()\n\t}\n\treturn nil\n}\n\nfunc NewSsh(option SshOption) (*Ssh, error) {\n\taddr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))\n\n\tconfig := ssh.ClientConfig{\n\t\tUser:              option.UserName,\n\t\tHostKeyCallback:   ssh.InsecureIgnoreHostKey(),\n\t\tHostKeyAlgorithms: option.HostKeyAlgorithms,\n\t}\n\n\tif option.PrivateKey != \"\" {\n\t\tvar b []byte\n\t\tvar err error\n\t\tif strings.Contains(option.PrivateKey, \"PRIVATE KEY\") {\n\t\t\tb = []byte(option.PrivateKey)\n\t\t} else {\n\t\t\tpath := C.Path.Resolve(option.PrivateKey)\n\t\t\tif !C.Path.IsSafePath(path) {\n\t\t\t\treturn nil, C.Path.ErrNotSafePath(path)\n\t\t\t}\n\t\t\tb, err = os.ReadFile(path)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\tvar pKey ssh.Signer\n\t\tif option.PrivateKeyPassphrase != \"\" {\n\t\t\tpKey, err = ssh.ParsePrivateKeyWithPassphrase(b, []byte(option.PrivateKeyPassphrase))\n\t\t} else {\n\t\t\tpKey, err = ssh.ParsePrivateKey(b)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tconfig.Auth = append(config.Auth, ssh.PublicKeys(pKey))\n\t}\n\n\tif option.Password != \"\" {\n\t\tconfig.Auth = append(config.Auth, ssh.Password(option.Password))\n\t}\n\n\tif len(option.HostKey) != 0 {\n\t\tkeys := make([]ssh.PublicKey, len(option.HostKey))\n\t\tfor i, hostKey := range option.HostKey {\n\t\t\tkey, _, _, _, err := ssh.ParseAuthorizedKey([]byte(hostKey))\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"parse host key :%s\", key)\n\t\t\t}\n\t\t\tkeys[i] = key\n\t\t}\n\t\tconfig.HostKeyCallback = func(hostname string, remote net.Addr, key ssh.PublicKey) error {\n\t\t\tserverKey := key.Marshal()\n\t\t\tfor _, hostKey := range keys {\n\t\t\t\tif bytes.Equal(serverKey, hostKey.Marshal()) {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"host key mismatch, server send :%s %s\", key.Type(), base64.StdEncoding.EncodeToString(serverKey))\n\t\t}\n\t}\n\n\tversion := \"SSH-2.0-OpenSSH_\"\n\tif randv2.IntN(2) == 0 {\n\t\tversion += \"7.\" + strconv.Itoa(randv2.IntN(10))\n\t} else {\n\t\tversion += \"8.\" + strconv.Itoa(randv2.IntN(9))\n\t}\n\tconfig.ClientVersion = version\n\n\toutbound := &Ssh{\n\t\tBase: NewBase(BaseOption{\n\t\t\tName:         option.Name,\n\t\t\tAddr:         addr,\n\t\t\tType:         C.Ssh,\n\t\t\tProviderName: option.ProviderName,\n\t\t\tUDP:          false,\n\t\t\tTFO:          option.TFO,\n\t\t\tMPTCP:        option.MPTCP,\n\t\t\tInterface:    option.Interface,\n\t\t\tRoutingMark:  option.RoutingMark,\n\t\t\tPrefer:       option.IPVersion,\n\t\t}),\n\t\toption: &option,\n\t\tconfig: &config,\n\t}\n\toutbound.dialer = option.NewDialer(outbound.DialOptions())\n\treturn outbound, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outbound/sudoku.go",
    "content": "package outbound\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/transport/sudoku\"\n\t\"github.com/metacubex/mihomo/transport/sudoku/obfs/httpmask\"\n)\n\ntype Sudoku struct {\n\t*Base\n\toption   *SudokuOption\n\tbaseConf sudoku.ProtocolConfig\n\n\tmuxMu     sync.Mutex\n\tmuxClient *sudoku.MultiplexClient\n\n\thttpMaskMu     sync.Mutex\n\thttpMaskClient *httpmask.TunnelClient\n\thttpMaskKey    string\n}\n\ntype SudokuOption struct {\n\tBasicOption\n\tName               string                 `proxy:\"name\"`\n\tServer             string                 `proxy:\"server\"`\n\tPort               int                    `proxy:\"port\"`\n\tKey                string                 `proxy:\"key\"`\n\tAEADMethod         string                 `proxy:\"aead-method,omitempty\"`\n\tPaddingMin         *int                   `proxy:\"padding-min,omitempty\"`\n\tPaddingMax         *int                   `proxy:\"padding-max,omitempty\"`\n\tTableType          string                 `proxy:\"table-type,omitempty\"` // \"prefer_ascii\", \"prefer_entropy\", or directional \"up_ascii_down_entropy\"/\"up_entropy_down_ascii\"\n\tEnablePureDownlink *bool                  `proxy:\"enable-pure-downlink,omitempty\"`\n\tHTTPMask           *bool                  `proxy:\"http-mask,omitempty\"`\n\tHTTPMaskMode       string                 `proxy:\"http-mask-mode,omitempty\"`      // \"legacy\" (default), \"stream\", \"poll\", \"auto\", \"ws\"\n\tHTTPMaskTLS        bool                   `proxy:\"http-mask-tls,omitempty\"`       // only for http-mask-mode stream/poll/auto\n\tHTTPMaskHost       string                 `proxy:\"http-mask-host,omitempty\"`      // optional Host/SNI override (domain or domain:port)\n\tPathRoot           string                 `proxy:\"path-root,omitempty\"`           // optional first-level path prefix for HTTP tunnel endpoints\n\tHTTPMaskMultiplex  string                 `proxy:\"http-mask-multiplex,omitempty\"` // \"off\" (default), \"auto\" (reuse h1/h2), \"on\" (single tunnel, multi-target)\n\tHTTPMaskOptions    *SudokuHTTPMaskOptions `proxy:\"httpmask,omitempty\"`\n\tCustomTable        string                 `proxy:\"custom-table,omitempty\"`  // optional custom byte layout, e.g. xpxvvpvv\n\tCustomTables       []string               `proxy:\"custom-tables,omitempty\"` // optional table rotation patterns, overrides custom-table when non-empty\n}\n\ntype SudokuHTTPMaskOptions struct {\n\tDisable   bool   `proxy:\"disable,omitempty\"`\n\tMode      string `proxy:\"mode,omitempty\"`\n\tTLS       bool   `proxy:\"tls,omitempty\"`\n\tHost      string `proxy:\"host,omitempty\"`\n\tPathRoot  string `proxy:\"path-root,omitempty\"`\n\tMultiplex string `proxy:\"multiplex,omitempty\"`\n}\n\n// DialContext implements C.ProxyAdapter\nfunc (s *Sudoku) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Conn, err error) {\n\tcfg, err := s.buildConfig(metadata)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tmuxMode := normalizeHTTPMaskMultiplex(cfg.HTTPMaskMultiplex)\n\tif muxMode == \"on\" && !cfg.DisableHTTPMask && httpTunnelModeEnabled(cfg.HTTPMaskMode) {\n\t\tstream, muxErr := s.dialMultiplex(ctx, cfg.TargetAddress)\n\t\tif muxErr == nil {\n\t\t\treturn NewConn(stream, s), nil\n\t\t}\n\t\treturn nil, muxErr\n\t}\n\n\tc, err := s.dialAndHandshake(ctx, cfg)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer func() { safeConnClose(c, err) }()\n\n\taddrBuf, err := sudoku.EncodeAddress(cfg.TargetAddress)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"encode target address failed: %w\", err)\n\t}\n\n\tif err = sudoku.WriteKIPMessage(c, sudoku.KIPTypeOpenTCP, addrBuf); err != nil {\n\t\treturn nil, fmt.Errorf(\"send target address failed: %w\", err)\n\t}\n\n\treturn NewConn(c, s), nil\n}\n\n// ListenPacketContext implements C.ProxyAdapter\nfunc (s *Sudoku) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (C.PacketConn, error) {\n\tif err := s.ResolveUDP(ctx, metadata); err != nil {\n\t\treturn nil, err\n\t}\n\n\tcfg, err := s.buildConfig(metadata)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tc, err := s.dialAndHandshake(ctx, cfg)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err = sudoku.WriteKIPMessage(c, sudoku.KIPTypeStartUoT, nil); err != nil {\n\t\t_ = c.Close()\n\t\treturn nil, fmt.Errorf(\"start uot failed: %w\", err)\n\t}\n\n\treturn newPacketConn(N.NewThreadSafePacketConn(sudoku.NewUoTPacketConn(c)), s), nil\n}\n\n// SupportUOT implements C.ProxyAdapter\nfunc (s *Sudoku) SupportUOT() bool {\n\treturn true\n}\n\n// ProxyInfo implements C.ProxyAdapter\nfunc (s *Sudoku) ProxyInfo() C.ProxyInfo {\n\tinfo := s.Base.ProxyInfo()\n\tinfo.DialerProxy = s.option.DialerProxy\n\treturn info\n}\n\nfunc (s *Sudoku) buildConfig(metadata *C.Metadata) (*sudoku.ProtocolConfig, error) {\n\tif metadata == nil || metadata.DstPort == 0 || !metadata.Valid() {\n\t\treturn nil, fmt.Errorf(\"invalid metadata for sudoku outbound\")\n\t}\n\n\tcfg := s.baseConf\n\tcfg.TargetAddress = metadata.RemoteAddress()\n\n\tif err := cfg.ValidateClient(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn &cfg, nil\n}\n\nfunc NewSudoku(option SudokuOption) (*Sudoku, error) {\n\tif option.Server == \"\" {\n\t\treturn nil, fmt.Errorf(\"server is required\")\n\t}\n\tif option.Port <= 0 || option.Port > 65535 {\n\t\treturn nil, fmt.Errorf(\"invalid port: %d\", option.Port)\n\t}\n\tif option.Key == \"\" {\n\t\treturn nil, fmt.Errorf(\"key is required\")\n\t}\n\n\tdefaultConf := sudoku.DefaultConfig()\n\ttableType, err := sudoku.NormalizeTableType(option.TableType)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tpaddingMin, paddingMax := sudoku.ResolvePadding(option.PaddingMin, option.PaddingMax, defaultConf.PaddingMin, defaultConf.PaddingMax)\n\tenablePureDownlink := sudoku.DerefBool(option.EnablePureDownlink, defaultConf.EnablePureDownlink)\n\n\tdisableHTTPMask := defaultConf.DisableHTTPMask\n\tif option.HTTPMask != nil {\n\t\tdisableHTTPMask = !*option.HTTPMask\n\t}\n\thttpMaskMode := defaultConf.HTTPMaskMode\n\tif option.HTTPMaskMode != \"\" {\n\t\thttpMaskMode = option.HTTPMaskMode\n\t}\n\thttpMaskTLS := option.HTTPMaskTLS\n\thttpMaskHost := option.HTTPMaskHost\n\tpathRoot := strings.TrimSpace(option.PathRoot)\n\thttpMaskMultiplex := defaultConf.HTTPMaskMultiplex\n\tif option.HTTPMaskMultiplex != \"\" {\n\t\thttpMaskMultiplex = option.HTTPMaskMultiplex\n\t}\n\n\tif hm := option.HTTPMaskOptions; hm != nil {\n\t\tdisableHTTPMask = hm.Disable\n\t\tif hm.Mode != \"\" {\n\t\t\thttpMaskMode = hm.Mode\n\t\t}\n\t\thttpMaskTLS = hm.TLS\n\t\thttpMaskHost = hm.Host\n\t\tif pr := strings.TrimSpace(hm.PathRoot); pr != \"\" {\n\t\t\tpathRoot = pr\n\t\t}\n\t\tif mux := strings.TrimSpace(hm.Multiplex); mux != \"\" {\n\t\t\thttpMaskMultiplex = mux\n\t\t} else {\n\t\t\thttpMaskMultiplex = defaultConf.HTTPMaskMultiplex\n\t\t}\n\t}\n\n\tbaseConf := sudoku.ProtocolConfig{\n\t\tServerAddress:           net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),\n\t\tKey:                     option.Key,\n\t\tAEADMethod:              defaultConf.AEADMethod,\n\t\tPaddingMin:              paddingMin,\n\t\tPaddingMax:              paddingMax,\n\t\tEnablePureDownlink:      enablePureDownlink,\n\t\tHandshakeTimeoutSeconds: defaultConf.HandshakeTimeoutSeconds,\n\t\tDisableHTTPMask:         disableHTTPMask,\n\t\tHTTPMaskMode:            httpMaskMode,\n\t\tHTTPMaskTLSEnabled:      httpMaskTLS,\n\t\tHTTPMaskHost:            httpMaskHost,\n\t\tHTTPMaskPathRoot:        pathRoot,\n\t\tHTTPMaskMultiplex:       httpMaskMultiplex,\n\t}\n\ttables, err := sudoku.NewClientTablesWithCustomPatterns(sudoku.ClientAEADSeed(option.Key), tableType, option.CustomTable, option.CustomTables)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"build table(s) failed: %w\", err)\n\t}\n\tif len(tables) == 1 {\n\t\tbaseConf.Table = tables[0]\n\t} else {\n\t\tbaseConf.Tables = tables\n\t}\n\tif option.AEADMethod != \"\" {\n\t\tbaseConf.AEADMethod = option.AEADMethod\n\t}\n\n\toutbound := &Sudoku{\n\t\tBase: NewBase(BaseOption{\n\t\t\tName:         option.Name,\n\t\t\tAddr:         baseConf.ServerAddress,\n\t\t\tType:         C.Sudoku,\n\t\t\tProviderName: option.ProviderName,\n\t\t\tUDP:          true,\n\t\t\tTFO:          option.TFO,\n\t\t\tMPTCP:        option.MPTCP,\n\t\t\tInterface:    option.Interface,\n\t\t\tRoutingMark:  option.RoutingMark,\n\t\t\tPrefer:       option.IPVersion,\n\t\t}),\n\t\toption:   &option,\n\t\tbaseConf: baseConf,\n\t}\n\toutbound.dialer = option.NewDialer(outbound.DialOptions())\n\treturn outbound, nil\n}\n\nfunc (s *Sudoku) Close() error {\n\ts.resetMuxClient()\n\ts.resetHTTPMaskClient()\n\treturn s.Base.Close()\n}\n\nfunc normalizeHTTPMaskMultiplex(mode string) string {\n\tswitch strings.ToLower(strings.TrimSpace(mode)) {\n\tcase \"\", \"off\":\n\t\treturn \"off\"\n\tcase \"auto\":\n\t\treturn \"auto\"\n\tcase \"on\":\n\t\treturn \"on\"\n\tdefault:\n\t\treturn \"off\"\n\t}\n}\n\nfunc httpTunnelModeEnabled(mode string) bool {\n\tswitch strings.ToLower(strings.TrimSpace(mode)) {\n\tcase \"stream\", \"poll\", \"auto\", \"ws\":\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc (s *Sudoku) dialAndHandshake(ctx context.Context, cfg *sudoku.ProtocolConfig) (_ net.Conn, err error) {\n\tif cfg == nil {\n\t\treturn nil, fmt.Errorf(\"config is required\")\n\t}\n\n\thandshakeCfg := *cfg\n\tif !handshakeCfg.DisableHTTPMask && httpTunnelModeEnabled(handshakeCfg.HTTPMaskMode) {\n\t\thandshakeCfg.DisableHTTPMask = true\n\t}\n\n\tupgrade := func(raw net.Conn) (net.Conn, error) {\n\t\treturn sudoku.ClientHandshake(raw, &handshakeCfg)\n\t}\n\n\tvar (\n\t\tc             net.Conn\n\t\thandshakeDone bool\n\t)\n\tif !cfg.DisableHTTPMask && httpTunnelModeEnabled(cfg.HTTPMaskMode) {\n\t\tmuxMode := normalizeHTTPMaskMultiplex(cfg.HTTPMaskMultiplex)\n\t\tif muxMode == \"auto\" && strings.ToLower(strings.TrimSpace(cfg.HTTPMaskMode)) != \"ws\" {\n\t\t\tif client, cerr := s.getOrCreateHTTPMaskClient(cfg); cerr == nil && client != nil {\n\t\t\t\tc, err = client.DialTunnel(ctx, httpmask.TunnelDialOptions{\n\t\t\t\t\tMode:         cfg.HTTPMaskMode,\n\t\t\t\t\tTLSEnabled:   cfg.HTTPMaskTLSEnabled,\n\t\t\t\t\tHostOverride: cfg.HTTPMaskHost,\n\t\t\t\t\tPathRoot:     cfg.HTTPMaskPathRoot,\n\t\t\t\t\tAuthKey:      sudoku.ClientAEADSeed(cfg.Key),\n\t\t\t\t\tUpgrade:      upgrade,\n\t\t\t\t\tMultiplex:    cfg.HTTPMaskMultiplex,\n\t\t\t\t\tDialContext:  s.dialer.DialContext,\n\t\t\t\t})\n\t\t\t\tif err != nil {\n\t\t\t\t\ts.resetHTTPMaskClient()\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif c == nil && err == nil {\n\t\t\tc, err = sudoku.DialHTTPMaskTunnel(ctx, cfg.ServerAddress, cfg, s.dialer.DialContext, upgrade)\n\t\t}\n\t\tif err == nil && c != nil {\n\t\t\thandshakeDone = true\n\t\t}\n\t}\n\tif c == nil && err == nil {\n\t\tc, err = s.dialer.DialContext(ctx, \"tcp\", s.addr)\n\t}\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s connect error: %w\", s.addr, err)\n\t}\n\n\tdefer func() { safeConnClose(c, err) }()\n\n\tif ctx.Done() != nil {\n\t\tdone := N.SetupContextForConn(ctx, c)\n\t\tdefer done(&err)\n\t}\n\n\tif !handshakeDone {\n\t\tc, err = sudoku.ClientHandshake(c, &handshakeCfg)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn c, nil\n}\n\nfunc (s *Sudoku) dialMultiplex(ctx context.Context, targetAddress string) (net.Conn, error) {\n\tfor attempt := 0; attempt < 2; attempt++ {\n\t\tclient, err := s.getOrCreateMuxClient(ctx)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tstream, err := client.Dial(ctx, targetAddress)\n\t\tif err != nil {\n\t\t\ts.resetMuxClient()\n\t\t\tcontinue\n\t\t}\n\n\t\treturn stream, nil\n\t}\n\treturn nil, fmt.Errorf(\"multiplex open stream failed\")\n}\n\nfunc (s *Sudoku) getOrCreateMuxClient(ctx context.Context) (*sudoku.MultiplexClient, error) {\n\tif s == nil {\n\t\treturn nil, fmt.Errorf(\"nil adapter\")\n\t}\n\n\ts.muxMu.Lock()\n\tif s.muxClient != nil && !s.muxClient.IsClosed() {\n\t\tclient := s.muxClient\n\t\ts.muxMu.Unlock()\n\t\treturn client, nil\n\t}\n\ts.muxMu.Unlock()\n\n\ts.muxMu.Lock()\n\tdefer s.muxMu.Unlock()\n\n\tif s.muxClient != nil && !s.muxClient.IsClosed() {\n\t\treturn s.muxClient, nil\n\t}\n\n\tbaseCfg := s.baseConf\n\tbaseConn, err := s.dialAndHandshake(ctx, &baseCfg)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tclient, err := sudoku.StartMultiplexClient(baseConn)\n\tif err != nil {\n\t\t_ = baseConn.Close()\n\t\treturn nil, err\n\t}\n\n\ts.muxClient = client\n\treturn client, nil\n}\n\nfunc (s *Sudoku) resetMuxClient() {\n\ts.muxMu.Lock()\n\tdefer s.muxMu.Unlock()\n\tif s.muxClient != nil {\n\t\t_ = s.muxClient.Close()\n\t\ts.muxClient = nil\n\t}\n}\n\nfunc (s *Sudoku) resetHTTPMaskClient() {\n\ts.httpMaskMu.Lock()\n\tdefer s.httpMaskMu.Unlock()\n\tif s.httpMaskClient != nil {\n\t\ts.httpMaskClient.CloseIdleConnections()\n\t\ts.httpMaskClient = nil\n\t\ts.httpMaskKey = \"\"\n\t}\n}\n\nfunc (s *Sudoku) getOrCreateHTTPMaskClient(cfg *sudoku.ProtocolConfig) (*httpmask.TunnelClient, error) {\n\tif s == nil || cfg == nil {\n\t\treturn nil, fmt.Errorf(\"nil adapter or config\")\n\t}\n\n\tkey := cfg.ServerAddress + \"|\" + strconv.FormatBool(cfg.HTTPMaskTLSEnabled) + \"|\" + strings.TrimSpace(cfg.HTTPMaskHost)\n\n\ts.httpMaskMu.Lock()\n\tif s.httpMaskClient != nil && s.httpMaskKey == key {\n\t\tclient := s.httpMaskClient\n\t\ts.httpMaskMu.Unlock()\n\t\treturn client, nil\n\t}\n\ts.httpMaskMu.Unlock()\n\n\tclient, err := httpmask.NewTunnelClient(cfg.ServerAddress, httpmask.TunnelClientOptions{\n\t\tTLSEnabled:   cfg.HTTPMaskTLSEnabled,\n\t\tHostOverride: cfg.HTTPMaskHost,\n\t\tDialContext:  s.dialer.DialContext,\n\t\tMaxIdleConns: 32,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ts.httpMaskMu.Lock()\n\tdefer s.httpMaskMu.Unlock()\n\tif s.httpMaskClient != nil && s.httpMaskKey == key {\n\t\tclient.CloseIdleConnections()\n\t\treturn s.httpMaskClient, nil\n\t}\n\tif s.httpMaskClient != nil {\n\t\ts.httpMaskClient.CloseIdleConnections()\n\t}\n\ts.httpMaskClient = client\n\ts.httpMaskKey = key\n\treturn client, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outbound/trojan.go",
    "content": "package outbound\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"strconv\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/component/ca\"\n\t\"github.com/metacubex/mihomo/component/ech\"\n\ttlsC \"github.com/metacubex/mihomo/component/tls\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/transport/gun\"\n\t\"github.com/metacubex/mihomo/transport/shadowsocks/core\"\n\t\"github.com/metacubex/mihomo/transport/trojan\"\n\t\"github.com/metacubex/mihomo/transport/vmess\"\n\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/tls\"\n)\n\ntype Trojan struct {\n\t*Base\n\toption      *TrojanOption\n\thexPassword [trojan.KeyLength]byte\n\n\t// for gun mux\n\tgunClient *gun.Client\n\n\trealityConfig *tlsC.RealityConfig\n\techConfig     *ech.Config\n\n\tssCipher core.Cipher\n}\n\ntype TrojanOption struct {\n\tBasicOption\n\tName              string         `proxy:\"name\"`\n\tServer            string         `proxy:\"server\"`\n\tPort              int            `proxy:\"port\"`\n\tPassword          string         `proxy:\"password\"`\n\tALPN              []string       `proxy:\"alpn,omitempty\"`\n\tSNI               string         `proxy:\"sni,omitempty\"`\n\tSkipCertVerify    bool           `proxy:\"skip-cert-verify,omitempty\"`\n\tFingerprint       string         `proxy:\"fingerprint,omitempty\"`\n\tCertificate       string         `proxy:\"certificate,omitempty\"`\n\tPrivateKey        string         `proxy:\"private-key,omitempty\"`\n\tUDP               bool           `proxy:\"udp,omitempty\"`\n\tNetwork           string         `proxy:\"network,omitempty\"`\n\tECHOpts           ECHOptions     `proxy:\"ech-opts,omitempty\"`\n\tRealityOpts       RealityOptions `proxy:\"reality-opts,omitempty\"`\n\tGrpcOpts          GrpcOptions    `proxy:\"grpc-opts,omitempty\"`\n\tWSOpts            WSOptions      `proxy:\"ws-opts,omitempty\"`\n\tSSOpts            TrojanSSOption `proxy:\"ss-opts,omitempty\"`\n\tClientFingerprint string         `proxy:\"client-fingerprint,omitempty\"`\n}\n\n// TrojanSSOption from https://github.com/p4gefau1t/trojan-go/blob/v0.10.6/tunnel/shadowsocks/config.go#L5\ntype TrojanSSOption struct {\n\tEnabled  bool   `proxy:\"enabled,omitempty\"`\n\tMethod   string `proxy:\"method,omitempty\"`\n\tPassword string `proxy:\"password,omitempty\"`\n}\n\nfunc (t *Trojan) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ net.Conn, err error) {\n\tswitch t.option.Network {\n\tcase \"ws\":\n\t\thost, port, _ := net.SplitHostPort(t.addr)\n\n\t\twsOpts := &vmess.WebsocketConfig{\n\t\t\tHost:                     host,\n\t\t\tPort:                     port,\n\t\t\tPath:                     t.option.WSOpts.Path,\n\t\t\tMaxEarlyData:             t.option.WSOpts.MaxEarlyData,\n\t\t\tEarlyDataHeaderName:      t.option.WSOpts.EarlyDataHeaderName,\n\t\t\tV2rayHttpUpgrade:         t.option.WSOpts.V2rayHttpUpgrade,\n\t\t\tV2rayHttpUpgradeFastOpen: t.option.WSOpts.V2rayHttpUpgradeFastOpen,\n\t\t\tClientFingerprint:        t.option.ClientFingerprint,\n\t\t\tECHConfig:                t.echConfig,\n\t\t\tHeaders:                  http.Header{},\n\t\t}\n\n\t\tif t.option.SNI != \"\" {\n\t\t\twsOpts.Host = t.option.SNI\n\t\t}\n\n\t\tif len(t.option.WSOpts.Headers) != 0 {\n\t\t\tfor key, value := range t.option.WSOpts.Headers {\n\t\t\t\twsOpts.Headers.Add(key, value)\n\t\t\t}\n\t\t}\n\n\t\talpn := trojan.DefaultWebsocketALPN\n\t\tif t.option.ALPN != nil { // structure's Decode will ensure value not nil when input has value even it was set an empty array\n\t\t\talpn = t.option.ALPN\n\t\t}\n\n\t\twsOpts.TLS = true\n\t\twsOpts.TLSConfig, err = ca.GetTLSConfig(ca.Option{\n\t\t\tTLSConfig: &tls.Config{\n\t\t\t\tNextProtos:         alpn,\n\t\t\t\tMinVersion:         tls.VersionTLS12,\n\t\t\t\tInsecureSkipVerify: t.option.SkipCertVerify,\n\t\t\t\tServerName:         t.option.SNI,\n\t\t\t},\n\t\t\tFingerprint: t.option.Fingerprint,\n\t\t\tCertificate: t.option.Certificate,\n\t\t\tPrivateKey:  t.option.PrivateKey,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tc, err = vmess.StreamWebsocketConn(ctx, c, wsOpts)\n\tcase \"grpc\":\n\t\tbreak // already handle in dialContext\n\tdefault:\n\t\t// default tcp network\n\t\t// handle TLS\n\t\talpn := trojan.DefaultALPN\n\t\tif t.option.ALPN != nil { // structure's Decode will ensure value not nil when input has value even it was set an empty array\n\t\t\talpn = t.option.ALPN\n\t\t}\n\t\tc, err = vmess.StreamTLSConn(ctx, c, &vmess.TLSConfig{\n\t\t\tHost:              t.option.SNI,\n\t\t\tSkipCertVerify:    t.option.SkipCertVerify,\n\t\t\tFingerPrint:       t.option.Fingerprint,\n\t\t\tCertificate:       t.option.Certificate,\n\t\t\tPrivateKey:        t.option.PrivateKey,\n\t\t\tClientFingerprint: t.option.ClientFingerprint,\n\t\t\tNextProtos:        alpn,\n\t\t\tECH:               t.echConfig,\n\t\t\tReality:           t.realityConfig,\n\t\t})\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn t.streamConnContext(ctx, c, metadata)\n}\n\nfunc (t *Trojan) streamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ net.Conn, err error) {\n\tif t.ssCipher != nil {\n\t\tc = t.ssCipher.StreamConn(c)\n\t}\n\n\tif ctx.Done() != nil {\n\t\tdone := N.SetupContextForConn(ctx, c)\n\t\tdefer done(&err)\n\t}\n\tcommand := trojan.CommandTCP\n\tif metadata.NetWork == C.UDP {\n\t\tcommand = trojan.CommandUDP\n\t}\n\terr = trojan.WriteHeader(c, t.hexPassword, command, serializesSocksAddr(metadata))\n\treturn c, err\n}\n\nfunc (t *Trojan) writeHeaderContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (err error) {\n\tif ctx.Done() != nil {\n\t\tdone := N.SetupContextForConn(ctx, c)\n\t\tdefer done(&err)\n\t}\n\tcommand := trojan.CommandTCP\n\tif metadata.NetWork == C.UDP {\n\t\tcommand = trojan.CommandUDP\n\t}\n\terr = trojan.WriteHeader(c, t.hexPassword, command, serializesSocksAddr(metadata))\n\treturn err\n}\n\nfunc (t *Trojan) dialContext(ctx context.Context) (c net.Conn, err error) {\n\tswitch t.option.Network {\n\tcase \"grpc\": // gun transport\n\t\treturn t.gunClient.Dial()\n\tdefault:\n\t}\n\treturn t.dialer.DialContext(ctx, \"tcp\", t.addr)\n}\n\n// DialContext implements C.ProxyAdapter\nfunc (t *Trojan) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Conn, err error) {\n\tc, err := t.dialContext(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s connect error: %w\", t.addr, err)\n\t}\n\tdefer func(c net.Conn) {\n\t\tsafeConnClose(c, err)\n\t}(c)\n\n\tc, err = t.StreamConnContext(ctx, c, metadata)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s connect error: %w\", t.addr, err)\n\t}\n\n\treturn NewConn(c, t), err\n}\n\n// ListenPacketContext implements C.ProxyAdapter\nfunc (t *Trojan) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {\n\tif err = t.ResolveUDP(ctx, metadata); err != nil {\n\t\treturn nil, err\n\t}\n\n\tc, err := t.dialContext(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s connect error: %w\", t.addr, err)\n\t}\n\tdefer func(c net.Conn) {\n\t\tsafeConnClose(c, err)\n\t}(c)\n\n\tc, err = t.StreamConnContext(ctx, c, metadata)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s connect error: %w\", t.addr, err)\n\t}\n\n\tpc := trojan.NewPacketConn(c)\n\treturn newPacketConn(pc, t), err\n}\n\n// SupportUOT implements C.ProxyAdapter\nfunc (t *Trojan) SupportUOT() bool {\n\treturn true\n}\n\n// ProxyInfo implements C.ProxyAdapter\nfunc (t *Trojan) ProxyInfo() C.ProxyInfo {\n\tinfo := t.Base.ProxyInfo()\n\tinfo.DialerProxy = t.option.DialerProxy\n\treturn info\n}\n\n// Close implements C.ProxyAdapter\nfunc (t *Trojan) Close() error {\n\tvar errs []error\n\tif t.gunClient != nil {\n\t\tif err := t.gunClient.Close(); err != nil {\n\t\t\terrs = append(errs, err)\n\t\t}\n\t}\n\treturn errors.Join(errs...)\n}\n\nfunc NewTrojan(option TrojanOption) (*Trojan, error) {\n\taddr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))\n\n\tif option.SNI == \"\" {\n\t\toption.SNI = option.Server\n\t}\n\n\tt := &Trojan{\n\t\tBase: NewBase(BaseOption{\n\t\t\tName:         option.Name,\n\t\t\tAddr:         addr,\n\t\t\tType:         C.Trojan,\n\t\t\tProviderName: option.ProviderName,\n\t\t\tUDP:          option.UDP,\n\t\t\tTFO:          option.TFO,\n\t\t\tMPTCP:        option.MPTCP,\n\t\t\tInterface:    option.Interface,\n\t\t\tRoutingMark:  option.RoutingMark,\n\t\t\tPrefer:       option.IPVersion,\n\t\t}),\n\t\toption:      &option,\n\t\thexPassword: trojan.Key(option.Password),\n\t}\n\tt.dialer = option.NewDialer(t.DialOptions())\n\n\tvar err error\n\tt.realityConfig, err = option.RealityOpts.Parse()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tt.echConfig, err = option.ECHOpts.Parse()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif option.SSOpts.Enabled {\n\t\tif option.SSOpts.Password == \"\" {\n\t\t\treturn nil, errors.New(\"empty password\")\n\t\t}\n\t\tif option.SSOpts.Method == \"\" {\n\t\t\toption.SSOpts.Method = \"AES-128-GCM\"\n\t\t}\n\t\tciph, err := core.PickCipher(option.SSOpts.Method, nil, option.SSOpts.Password)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tt.ssCipher = ciph\n\t}\n\n\tif option.Network == \"grpc\" {\n\t\tdialFn := func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\t\tc, err := t.dialer.DialContext(ctx, \"tcp\", t.addr)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"%s connect error: %s\", t.addr, err.Error())\n\t\t\t}\n\t\t\treturn c, nil\n\t\t}\n\n\t\ttlsConfig := &vmess.TLSConfig{\n\t\t\tHost:              option.SNI,\n\t\t\tSkipCertVerify:    option.SkipCertVerify,\n\t\t\tFingerPrint:       option.Fingerprint,\n\t\t\tCertificate:       option.Certificate,\n\t\t\tPrivateKey:        option.PrivateKey,\n\t\t\tClientFingerprint: option.ClientFingerprint,\n\t\t\tNextProtos:        []string{\"h2\"},\n\t\t\tECH:               t.echConfig,\n\t\t\tReality:           t.realityConfig,\n\t\t}\n\n\t\tgunConfig := &gun.Config{\n\t\t\tServiceName:  option.GrpcOpts.GrpcServiceName,\n\t\t\tUserAgent:    option.GrpcOpts.GrpcUserAgent,\n\t\t\tHost:         option.SNI,\n\t\t\tPingInterval: option.GrpcOpts.PingInterval,\n\t\t}\n\n\t\tt.gunClient = gun.NewClient(\n\t\t\tfunc() *gun.Transport {\n\t\t\t\treturn gun.NewTransport(dialFn, tlsConfig, gunConfig)\n\t\t\t},\n\t\t\toption.GrpcOpts.MaxConnections,\n\t\t\toption.GrpcOpts.MinStreams,\n\t\t\toption.GrpcOpts.MaxStreams,\n\t\t)\n\t}\n\n\treturn t, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outbound/trusttunnel.go",
    "content": "package outbound\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"strconv\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/transport/trusttunnel\"\n\t\"github.com/metacubex/mihomo/transport/vmess\"\n)\n\ntype TrustTunnel struct {\n\t*Base\n\tclient *trusttunnel.PoolClient\n\toption *TrustTunnelOption\n}\n\ntype TrustTunnelOption struct {\n\tBasicOption\n\tName              string     `proxy:\"name\"`\n\tServer            string     `proxy:\"server\"`\n\tPort              int        `proxy:\"port\"`\n\tUserName          string     `proxy:\"username,omitempty\"`\n\tPassword          string     `proxy:\"password,omitempty\"`\n\tALPN              []string   `proxy:\"alpn,omitempty\"`\n\tSNI               string     `proxy:\"sni,omitempty\"`\n\tECHOpts           ECHOptions `proxy:\"ech-opts,omitempty\"`\n\tClientFingerprint string     `proxy:\"client-fingerprint,omitempty\"`\n\tSkipCertVerify    bool       `proxy:\"skip-cert-verify,omitempty\"`\n\tFingerprint       string     `proxy:\"fingerprint,omitempty\"`\n\tCertificate       string     `proxy:\"certificate,omitempty\"`\n\tPrivateKey        string     `proxy:\"private-key,omitempty\"`\n\tUDP               bool       `proxy:\"udp,omitempty\"`\n\tHealthCheck       bool       `proxy:\"health-check,omitempty\"`\n\t// quic options\n\tQuic                 bool   `proxy:\"quic,omitempty\"`\n\tCongestionController string `proxy:\"congestion-controller,omitempty\"`\n\tCWND                 int    `proxy:\"cwnd,omitempty\"`\n\tBBRProfile           string `proxy:\"bbr-profile,omitempty\"`\n\t// reuse options\n\tMaxConnections int `proxy:\"max-connections,omitempty\"`\n\tMinStreams     int `proxy:\"min-streams,omitempty\"`\n\tMaxStreams     int `proxy:\"max-streams,omitempty\"`\n}\n\nfunc (t *TrustTunnel) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Conn, err error) {\n\tc, err := t.client.Dial(ctx, metadata.RemoteAddress())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewConn(c, t), nil\n}\n\nfunc (t *TrustTunnel) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {\n\tif err = t.ResolveUDP(ctx, metadata); err != nil {\n\t\treturn nil, err\n\t}\n\n\tpc, err := t.client.ListenPacket(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn newPacketConn(N.NewThreadSafePacketConn(pc), t), nil\n}\n\n// SupportUOT implements C.ProxyAdapter\nfunc (t *TrustTunnel) SupportUOT() bool {\n\treturn true\n}\n\n// ProxyInfo implements C.ProxyAdapter\nfunc (t *TrustTunnel) ProxyInfo() C.ProxyInfo {\n\tinfo := t.Base.ProxyInfo()\n\tinfo.DialerProxy = t.option.DialerProxy\n\treturn info\n}\n\n// Close implements C.ProxyAdapter\nfunc (t *TrustTunnel) Close() error {\n\treturn t.client.Close()\n}\n\nfunc NewTrustTunnel(option TrustTunnelOption) (*TrustTunnel, error) {\n\taddr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))\n\toutbound := &TrustTunnel{\n\t\tBase: NewBase(BaseOption{\n\t\t\tName:         option.Name,\n\t\t\tAddr:         addr,\n\t\t\tType:         C.TrustTunnel,\n\t\t\tProviderName: option.ProviderName,\n\t\t\tUDP:          option.UDP,\n\t\t\tTFO:          option.TFO,\n\t\t\tMPTCP:        option.MPTCP,\n\t\t\tInterface:    option.Interface,\n\t\t\tRoutingMark:  option.RoutingMark,\n\t\t\tPrefer:       option.IPVersion,\n\t\t}),\n\t\toption: &option,\n\t}\n\toutbound.dialer = option.NewDialer(outbound.DialOptions())\n\n\ttOption := trusttunnel.ClientOptions{\n\t\tDialer:                outbound.dialer,\n\t\tDialOptions:           outbound.DialOptions,\n\t\tServer:                addr,\n\t\tUsername:              option.UserName,\n\t\tPassword:              option.Password,\n\t\tQUIC:                  option.Quic,\n\t\tQUICCongestionControl: option.CongestionController,\n\t\tQUICCwnd:              option.CWND,\n\t\tQUICBBRProfile:        option.BBRProfile,\n\t\tHealthCheck:           option.HealthCheck,\n\t\tMaxConnections:        option.MaxConnections,\n\t\tMinStreams:            option.MinStreams,\n\t\tMaxStreams:            option.MaxStreams,\n\t}\n\techConfig, err := option.ECHOpts.Parse()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\ttlsConfig := &vmess.TLSConfig{\n\t\tHost:              option.SNI,\n\t\tSkipCertVerify:    option.SkipCertVerify,\n\t\tNextProtos:        option.ALPN,\n\t\tFingerPrint:       option.Fingerprint,\n\t\tCertificate:       option.Certificate,\n\t\tPrivateKey:        option.PrivateKey,\n\t\tClientFingerprint: option.ClientFingerprint,\n\t\tECH:               echConfig,\n\t}\n\tif tlsConfig.Host == \"\" {\n\t\ttlsConfig.Host = option.Server\n\t}\n\ttOption.TLSConfig = tlsConfig\n\n\tclient, err := trusttunnel.NewPoolClient(context.TODO(), tOption)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\toutbound.client = client\n\n\treturn outbound, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outbound/tuic.go",
    "content": "package outbound\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"math\"\n\t\"net\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/component/ca\"\n\t\"github.com/metacubex/mihomo/component/ech\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/transport/tuic\"\n\t\"github.com/metacubex/mihomo/transport/tuic/common\"\n\n\t\"github.com/gofrs/uuid/v5\"\n\t\"github.com/metacubex/quic-go\"\n\tM \"github.com/metacubex/sing/common/metadata\"\n\t\"github.com/metacubex/sing/common/uot\"\n\t\"github.com/metacubex/tls\"\n)\n\ntype Tuic struct {\n\t*Base\n\toption *TuicOption\n\tclient *tuic.PoolClient\n\n\tquicConfig *quic.Config\n\ttlsConfig  *tls.Config\n\techConfig  *ech.Config\n}\n\ntype TuicOption struct {\n\tBasicOption\n\tName                  string   `proxy:\"name\"`\n\tServer                string   `proxy:\"server\"`\n\tPort                  int      `proxy:\"port\"`\n\tToken                 string   `proxy:\"token,omitempty\"`\n\tUUID                  string   `proxy:\"uuid,omitempty\"`\n\tPassword              string   `proxy:\"password,omitempty\"`\n\tIp                    string   `proxy:\"ip,omitempty\"`\n\tHeartbeatInterval     int      `proxy:\"heartbeat-interval,omitempty\"`\n\tALPN                  []string `proxy:\"alpn,omitempty\"`\n\tReduceRtt             bool     `proxy:\"reduce-rtt,omitempty\"`\n\tRequestTimeout        int      `proxy:\"request-timeout,omitempty\"`\n\tUdpRelayMode          string   `proxy:\"udp-relay-mode,omitempty\"`\n\tCongestionController  string   `proxy:\"congestion-controller,omitempty\"`\n\tDisableSni            bool     `proxy:\"disable-sni,omitempty\"`\n\tMaxUdpRelayPacketSize int      `proxy:\"max-udp-relay-packet-size,omitempty\"`\n\n\tFastOpen             bool       `proxy:\"fast-open,omitempty\"`\n\tMaxOpenStreams       int        `proxy:\"max-open-streams,omitempty\"`\n\tCWND                 int        `proxy:\"cwnd,omitempty\"`\n\tBBRProfile           string     `proxy:\"bbr-profile,omitempty\"`\n\tSkipCertVerify       bool       `proxy:\"skip-cert-verify,omitempty\"`\n\tFingerprint          string     `proxy:\"fingerprint,omitempty\"`\n\tCertificate          string     `proxy:\"certificate,omitempty\"`\n\tPrivateKey           string     `proxy:\"private-key,omitempty\"`\n\tReceiveWindowConn    int        `proxy:\"recv-window-conn,omitempty\"`\n\tReceiveWindow        int        `proxy:\"recv-window,omitempty\"`\n\tDisableMTUDiscovery  bool       `proxy:\"disable-mtu-discovery,omitempty\"`\n\tMaxDatagramFrameSize int        `proxy:\"max-datagram-frame-size,omitempty\"`\n\tSNI                  string     `proxy:\"sni,omitempty\"`\n\tECHOpts              ECHOptions `proxy:\"ech-opts,omitempty\"`\n\n\tUDPOverStream        bool `proxy:\"udp-over-stream,omitempty\"`\n\tUDPOverStreamVersion int  `proxy:\"udp-over-stream-version,omitempty\"`\n}\n\n// DialContext implements C.ProxyAdapter\nfunc (t *Tuic) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, error) {\n\tconn, err := t.client.DialContext(ctx, metadata)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn NewConn(conn, t), err\n}\n\n// ListenPacketContext implements C.ProxyAdapter\nfunc (t *Tuic) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {\n\tif err = t.ResolveUDP(ctx, metadata); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif t.option.UDPOverStream {\n\t\tuotDestination := uot.RequestDestination(uint8(t.option.UDPOverStreamVersion))\n\t\tuotMetadata := *metadata\n\t\tuotMetadata.Host = uotDestination.Fqdn\n\t\tuotMetadata.DstPort = uotDestination.Port\n\t\tc, err := t.DialContext(ctx, &uotMetadata)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\t// tuic uos use stream-oriented udp with a special address, so we need a net.UDPAddr\n\n\t\tdestination := M.SocksaddrFromNet(metadata.UDPAddr())\n\t\tif t.option.UDPOverStreamVersion == uot.LegacyVersion {\n\t\t\treturn newPacketConn(uot.NewConn(c, uot.Request{Destination: destination}), t), nil\n\t\t} else {\n\t\t\treturn newPacketConn(uot.NewLazyConn(c, uot.Request{Destination: destination}), t), nil\n\t\t}\n\t}\n\tpc, err := t.client.ListenPacket(ctx, metadata)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn newPacketConn(pc, t), nil\n}\n\nfunc (t *Tuic) dial(ctx context.Context) (quicConn *quic.Conn, err error) {\n\t_, quicConn, err = common.DialQuic(ctx, t.addr, t.DialOptions(), t.dialer, t.tlsConfig, t.quicConfig, t.option.ReduceRtt)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tcommon.SetCongestionController(quicConn, t.option.CongestionController, t.option.CWND, t.option.BBRProfile)\n\treturn quicConn, nil\n}\n\n// ProxyInfo implements C.ProxyAdapter\nfunc (t *Tuic) ProxyInfo() C.ProxyInfo {\n\tinfo := t.Base.ProxyInfo()\n\tinfo.DialerProxy = t.option.DialerProxy\n\treturn info\n}\n\nfunc NewTuic(option TuicOption) (*Tuic, error) {\n\taddr := net.JoinHostPort(option.Server, strconv.Itoa(option.Port))\n\tserverName := option.Server\n\tif option.SNI != \"\" {\n\t\tserverName = option.SNI\n\t}\n\n\ttlsConfig, err := ca.GetTLSConfig(ca.Option{\n\t\tTLSConfig: &tls.Config{\n\t\t\tServerName:         serverName,\n\t\t\tInsecureSkipVerify: option.SkipCertVerify,\n\t\t\tMinVersion:         tls.VersionTLS13,\n\t\t},\n\t\tFingerprint: option.Fingerprint,\n\t\tCertificate: option.Certificate,\n\t\tPrivateKey:  option.PrivateKey,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif option.ALPN != nil { // structure's Decode will ensure value not nil when input has value even it was set an empty array\n\t\ttlsConfig.NextProtos = option.ALPN\n\t} else {\n\t\ttlsConfig.NextProtos = []string{\"h3\"}\n\t}\n\n\tif option.RequestTimeout == 0 {\n\t\toption.RequestTimeout = 8000\n\t}\n\n\tif option.HeartbeatInterval <= 0 {\n\t\toption.HeartbeatInterval = 10000\n\t}\n\n\tudpRelayMode := tuic.QUIC\n\tif option.UdpRelayMode != \"quic\" {\n\t\tudpRelayMode = tuic.NATIVE\n\t}\n\n\tif option.MaxUdpRelayPacketSize == 0 {\n\t\toption.MaxUdpRelayPacketSize = 1252\n\t}\n\n\tif option.MaxOpenStreams == 0 {\n\t\toption.MaxOpenStreams = 100\n\t}\n\n\tif option.CWND == 0 {\n\t\toption.CWND = 32\n\t}\n\n\tpacketOverHead := tuic.PacketOverHeadV4\n\tif len(option.Token) == 0 {\n\t\tpacketOverHead = tuic.PacketOverHeadV5\n\t}\n\n\tif option.MaxDatagramFrameSize == 0 {\n\t\toption.MaxDatagramFrameSize = option.MaxUdpRelayPacketSize + packetOverHead\n\t}\n\n\tif option.MaxDatagramFrameSize > 1400 {\n\t\toption.MaxDatagramFrameSize = 1400\n\t}\n\toption.MaxUdpRelayPacketSize = option.MaxDatagramFrameSize - packetOverHead\n\n\t// ensure server's incoming stream can handle correctly, increase to 1.1x\n\tquicMaxOpenStreams := int64(option.MaxOpenStreams)\n\tquicMaxOpenStreams = quicMaxOpenStreams + int64(math.Ceil(float64(quicMaxOpenStreams)/10.0))\n\tquicConfig := &quic.Config{\n\t\tInitialStreamReceiveWindow:     uint64(option.ReceiveWindowConn),\n\t\tMaxStreamReceiveWindow:         uint64(option.ReceiveWindowConn),\n\t\tInitialConnectionReceiveWindow: uint64(option.ReceiveWindow),\n\t\tMaxConnectionReceiveWindow:     uint64(option.ReceiveWindow),\n\t\tMaxIncomingStreams:             quicMaxOpenStreams,\n\t\tMaxIncomingUniStreams:          quicMaxOpenStreams,\n\t\tKeepAlivePeriod:                time.Duration(option.HeartbeatInterval) * time.Millisecond,\n\t\tDisablePathMTUDiscovery:        option.DisableMTUDiscovery,\n\t\tMaxDatagramFrameSize:           int64(option.MaxDatagramFrameSize),\n\t\tEnableDatagrams:                true,\n\t}\n\tif option.ReceiveWindowConn == 0 {\n\t\tquicConfig.InitialStreamReceiveWindow = tuic.DefaultStreamReceiveWindow / 10\n\t\tquicConfig.MaxStreamReceiveWindow = tuic.DefaultStreamReceiveWindow\n\t}\n\tif option.ReceiveWindow == 0 {\n\t\tquicConfig.InitialConnectionReceiveWindow = tuic.DefaultConnectionReceiveWindow / 10\n\t\tquicConfig.MaxConnectionReceiveWindow = tuic.DefaultConnectionReceiveWindow\n\t}\n\n\tif len(option.Ip) > 0 {\n\t\taddr = net.JoinHostPort(option.Ip, strconv.Itoa(option.Port))\n\t}\n\tif option.DisableSni {\n\t\ttlsConfig.ServerName = \"\"\n\t\ttlsConfig.InsecureSkipVerify = true // tls: either ServerName or InsecureSkipVerify must be specified in the tls.Config\n\t}\n\n\techConfig, err := option.ECHOpts.Parse()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tswitch option.UDPOverStreamVersion {\n\tcase uot.Version, uot.LegacyVersion:\n\tcase 0:\n\t\toption.UDPOverStreamVersion = uot.LegacyVersion\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"tuic %s unknown udp over stream protocol version: %d\", addr, option.UDPOverStreamVersion)\n\t}\n\n\tt := &Tuic{\n\t\tBase: NewBase(BaseOption{\n\t\t\tName:         option.Name,\n\t\t\tAddr:         addr,\n\t\t\tType:         C.Tuic,\n\t\t\tProviderName: option.ProviderName,\n\t\t\tUDP:          true,\n\t\t\tTFO:          option.FastOpen,\n\t\t\tInterface:    option.Interface,\n\t\t\tRoutingMark:  option.RoutingMark,\n\t\t\tPrefer:       option.IPVersion,\n\t\t}),\n\t\toption:     &option,\n\t\tquicConfig: quicConfig,\n\t\ttlsConfig:  tlsConfig,\n\t\techConfig:  echConfig,\n\t}\n\tt.dialer = option.NewDialer(t.DialOptions())\n\n\tclientMaxOpenStreams := int64(option.MaxOpenStreams)\n\n\t// to avoid tuic's \"too many open streams\", decrease to 0.9x\n\tif clientMaxOpenStreams == 100 {\n\t\tclientMaxOpenStreams = clientMaxOpenStreams - int64(math.Ceil(float64(clientMaxOpenStreams)/10.0))\n\t}\n\n\tif clientMaxOpenStreams < 1 {\n\t\tclientMaxOpenStreams = 1\n\t}\n\n\tif len(option.Token) > 0 {\n\t\ttkn := tuic.GenTKN(option.Token)\n\t\tclientOption := &tuic.ClientOptionV4{\n\t\t\tToken:                 tkn,\n\t\t\tUdpRelayMode:          udpRelayMode,\n\t\t\tRequestTimeout:        time.Duration(option.RequestTimeout) * time.Millisecond,\n\t\t\tMaxUdpRelayPacketSize: option.MaxUdpRelayPacketSize,\n\t\t\tFastOpen:              option.FastOpen,\n\t\t\tMaxOpenStreams:        clientMaxOpenStreams,\n\t\t}\n\n\t\tt.client = tuic.NewPoolClientV4(clientOption, t.dial)\n\t} else {\n\t\tmaxUdpRelayPacketSize := option.MaxUdpRelayPacketSize\n\t\tif maxUdpRelayPacketSize > tuic.MaxFragSizeV5 {\n\t\t\tmaxUdpRelayPacketSize = tuic.MaxFragSizeV5\n\t\t}\n\t\tclientOption := &tuic.ClientOptionV5{\n\t\t\tUuid:                  uuid.FromStringOrNil(option.UUID),\n\t\t\tPassword:              option.Password,\n\t\t\tUdpRelayMode:          udpRelayMode,\n\t\t\tMaxUdpRelayPacketSize: maxUdpRelayPacketSize,\n\t\t\tMaxOpenStreams:        clientMaxOpenStreams,\n\t\t}\n\n\t\tt.client = tuic.NewPoolClientV5(clientOption, t.dial)\n\t}\n\n\treturn t, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outbound/util.go",
    "content": "package outbound\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/netip\"\n\t\"regexp\"\n\t\"strconv\"\n\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n)\n\nfunc serializesSocksAddr(metadata *C.Metadata) []byte {\n\tvar buf [][]byte\n\taddrType := metadata.AddrType()\n\tp := uint(metadata.DstPort)\n\tport := []byte{uint8(p >> 8), uint8(p & 0xff)}\n\tswitch addrType {\n\tcase C.AtypDomainName:\n\t\tlenM := uint8(len(metadata.Host))\n\t\thost := []byte(metadata.Host)\n\t\tbuf = [][]byte{{socks5.AtypDomainName, lenM}, host, port}\n\tcase C.AtypIPv4:\n\t\thost := metadata.DstIP.AsSlice()\n\t\tbuf = [][]byte{{socks5.AtypIPv4}, host, port}\n\tcase C.AtypIPv6:\n\t\thost := metadata.DstIP.AsSlice()\n\t\tbuf = [][]byte{{socks5.AtypIPv6}, host, port}\n\t}\n\treturn bytes.Join(buf, nil)\n}\n\nfunc resolveUDPAddr(ctx context.Context, network, address string, prefer C.DNSPrefer) (*net.UDPAddr, error) {\n\thost, port, err := net.SplitHostPort(address)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar ip netip.Addr\n\tswitch prefer {\n\tcase C.IPv4Only:\n\t\tip, err = resolver.ResolveIPv4WithResolver(ctx, host, resolver.ProxyServerHostResolver)\n\tcase C.IPv6Only:\n\t\tip, err = resolver.ResolveIPv6WithResolver(ctx, host, resolver.ProxyServerHostResolver)\n\tcase C.IPv6Prefer:\n\t\tip, err = resolver.ResolveIPPrefer6WithResolver(ctx, host, resolver.ProxyServerHostResolver)\n\tdefault:\n\t\tip, err = resolver.ResolveIPWithResolver(ctx, host, resolver.ProxyServerHostResolver)\n\t}\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tip, port = resolver.LookupIP4P(ip, port)\n\n\tvar uint16Port uint16\n\tif port, err := strconv.ParseUint(port, 10, 16); err == nil {\n\t\tuint16Port = uint16(port)\n\t} else {\n\t\treturn nil, err\n\t}\n\t// our resolver always unmap before return, so unneeded unmap at here\n\t// which is different with net.ResolveUDPAddr maybe return 4in6 address\n\t// 4in6 addresses can cause some strange effects on sing-based code\n\treturn net.UDPAddrFromAddrPort(netip.AddrPortFrom(ip, uint16Port)), nil\n}\n\nfunc safeConnClose(c net.Conn, err error) {\n\tif err != nil && c != nil {\n\t\t_ = c.Close()\n\t}\n}\n\nvar rateStringRegexp = regexp.MustCompile(`^(\\d+)\\s*([KMGT]?)([Bb])ps$`)\n\nfunc StringToBps(s string) uint64 {\n\tif s == \"\" {\n\t\treturn 0\n\t}\n\n\t// when have not unit, use Mbps\n\tif v, err := strconv.Atoi(s); err == nil {\n\t\treturn StringToBps(fmt.Sprintf(\"%d Mbps\", v))\n\t}\n\n\tm := rateStringRegexp.FindStringSubmatch(s)\n\tif m == nil {\n\t\treturn 0\n\t}\n\tvar n uint64 = 1\n\tswitch m[2] {\n\tcase \"T\":\n\t\tn *= 1000\n\t\tfallthrough\n\tcase \"G\":\n\t\tn *= 1000\n\t\tfallthrough\n\tcase \"M\":\n\t\tn *= 1000\n\t\tfallthrough\n\tcase \"K\":\n\t\tn *= 1000\n\t}\n\tv, _ := strconv.ParseUint(m[1], 10, 64)\n\tn *= v\n\tif m[3] == \"b\" {\n\t\t// Bits, need to convert to bytes\n\t\tn /= 8\n\t}\n\treturn n\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outbound/vless.go",
    "content": "package outbound\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/convert\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"github.com/metacubex/mihomo/component/ca\"\n\t\"github.com/metacubex/mihomo/component/ech\"\n\ttlsC \"github.com/metacubex/mihomo/component/tls\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/transport/gun\"\n\t\"github.com/metacubex/mihomo/transport/tuic/common\"\n\t\"github.com/metacubex/mihomo/transport/vless\"\n\t\"github.com/metacubex/mihomo/transport/vless/encryption\"\n\t\"github.com/metacubex/mihomo/transport/vmess\"\n\t\"github.com/metacubex/mihomo/transport/xhttp\"\n\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/quic-go\"\n\tvmessSing \"github.com/metacubex/sing-vmess\"\n\t\"github.com/metacubex/sing-vmess/packetaddr\"\n\tM \"github.com/metacubex/sing/common/metadata\"\n\t\"github.com/metacubex/tls\"\n\t\"github.com/samber/lo\"\n)\n\ntype Vless struct {\n\t*Base\n\tclient *vless.Client\n\toption *VlessOption\n\n\tencryption *encryption.ClientInstance\n\n\t// for gun mux\n\tgunClient *gun.Client\n\t// for xhttp\n\txhttpClient *xhttp.Client\n\n\trealityConfig *tlsC.RealityConfig\n\techConfig     *ech.Config\n}\n\ntype VlessOption struct {\n\tBasicOption\n\tName              string            `proxy:\"name\"`\n\tServer            string            `proxy:\"server\"`\n\tPort              int               `proxy:\"port\"`\n\tUUID              string            `proxy:\"uuid\"`\n\tFlow              string            `proxy:\"flow,omitempty\"`\n\tTLS               bool              `proxy:\"tls,omitempty\"`\n\tALPN              []string          `proxy:\"alpn,omitempty\"`\n\tUDP               bool              `proxy:\"udp,omitempty\"`\n\tPacketAddr        bool              `proxy:\"packet-addr,omitempty\"`\n\tXUDP              bool              `proxy:\"xudp,omitempty\"`\n\tPacketEncoding    string            `proxy:\"packet-encoding,omitempty\"`\n\tEncryption        string            `proxy:\"encryption,omitempty\"`\n\tNetwork           string            `proxy:\"network,omitempty\"`\n\tECHOpts           ECHOptions        `proxy:\"ech-opts,omitempty\"`\n\tRealityOpts       RealityOptions    `proxy:\"reality-opts,omitempty\"`\n\tHTTPOpts          HTTPOptions       `proxy:\"http-opts,omitempty\"`\n\tHTTP2Opts         HTTP2Options      `proxy:\"h2-opts,omitempty\"`\n\tGrpcOpts          GrpcOptions       `proxy:\"grpc-opts,omitempty\"`\n\tWSOpts            WSOptions         `proxy:\"ws-opts,omitempty\"`\n\tXHTTPOpts         XHTTPOptions      `proxy:\"xhttp-opts,omitempty\"`\n\tWSHeaders         map[string]string `proxy:\"ws-headers,omitempty\"`\n\tSkipCertVerify    bool              `proxy:\"skip-cert-verify,omitempty\"`\n\tFingerprint       string            `proxy:\"fingerprint,omitempty\"`\n\tCertificate       string            `proxy:\"certificate,omitempty\"`\n\tPrivateKey        string            `proxy:\"private-key,omitempty\"`\n\tServerName        string            `proxy:\"servername,omitempty\"`\n\tClientFingerprint string            `proxy:\"client-fingerprint,omitempty\"`\n}\n\ntype XHTTPOptions struct {\n\tPath                 string                 `proxy:\"path,omitempty\"`\n\tHost                 string                 `proxy:\"host,omitempty\"`\n\tMode                 string                 `proxy:\"mode,omitempty\"`\n\tHeaders              map[string]string      `proxy:\"headers,omitempty\"`\n\tNoGRPCHeader         bool                   `proxy:\"no-grpc-header,omitempty\"`\n\tXPaddingBytes        string                 `proxy:\"x-padding-bytes,omitempty\"`\n\tXPaddingObfsMode     bool                   `proxy:\"x-padding-obfs-mode,omitempty\"`\n\tXPaddingKey          string                 `proxy:\"x-padding-key,omitempty\"`\n\tXPaddingHeader       string                 `proxy:\"x-padding-header,omitempty\"`\n\tXPaddingPlacement    string                 `proxy:\"x-padding-placement,omitempty\"`\n\tXPaddingMethod       string                 `proxy:\"x-padding-method,omitempty\"`\n\tUplinkHTTPMethod     string                 `proxy:\"uplink-http-method,omitempty\"`\n\tSessionPlacement     string                 `proxy:\"session-placement,omitempty\"`\n\tSessionKey           string                 `proxy:\"session-key,omitempty\"`\n\tSeqPlacement         string                 `proxy:\"seq-placement,omitempty\"`\n\tSeqKey               string                 `proxy:\"seq-key,omitempty\"`\n\tUplinkDataPlacement  string                 `proxy:\"uplink-data-placement,omitempty\"`\n\tUplinkDataKey        string                 `proxy:\"uplink-data-key,omitempty\"`\n\tUplinkChunkSize      string                 `proxy:\"uplink-chunk-size,omitempty\"`\n\tScMaxEachPostBytes   string                 `proxy:\"sc-max-each-post-bytes,omitempty\"`\n\tScMinPostsIntervalMs string                 `proxy:\"sc-min-posts-interval-ms,omitempty\"`\n\tReuseSettings        *XHTTPReuseSettings    `proxy:\"reuse-settings,omitempty\"` // aka XMUX\n\tDownloadSettings     *XHTTPDownloadSettings `proxy:\"download-settings,omitempty\"`\n}\n\ntype XHTTPReuseSettings struct {\n\tMaxConcurrency   string `proxy:\"max-concurrency,omitempty\"`\n\tMaxConnections   string `proxy:\"max-connections,omitempty\"`\n\tCMaxReuseTimes   string `proxy:\"c-max-reuse-times,omitempty\"`\n\tHMaxRequestTimes string `proxy:\"h-max-request-times,omitempty\"`\n\tHMaxReusableSecs string `proxy:\"h-max-reusable-secs,omitempty\"`\n\tHKeepAlivePeriod int    `proxy:\"h-keep-alive-period,omitempty\"`\n}\n\ntype XHTTPDownloadSettings struct {\n\t// xhttp part\n\tPath          *string             `proxy:\"path,omitempty\"`\n\tHost          *string             `proxy:\"host,omitempty\"`\n\tHeaders       *map[string]string  `proxy:\"headers,omitempty\"`\n\tReuseSettings *XHTTPReuseSettings `proxy:\"reuse-settings,omitempty\"` // aka XMUX\n\t// proxy part\n\tServer            *string         `proxy:\"server,omitempty\"`\n\tPort              *int            `proxy:\"port,omitempty\"`\n\tTLS               *bool           `proxy:\"tls,omitempty\"`\n\tALPN              *[]string       `proxy:\"alpn,omitempty\"`\n\tECHOpts           *ECHOptions     `proxy:\"ech-opts,omitempty\"`\n\tRealityOpts       *RealityOptions `proxy:\"reality-opts,omitempty\"`\n\tSkipCertVerify    *bool           `proxy:\"skip-cert-verify,omitempty\"`\n\tFingerprint       *string         `proxy:\"fingerprint,omitempty\"`\n\tCertificate       *string         `proxy:\"certificate,omitempty\"`\n\tPrivateKey        *string         `proxy:\"private-key,omitempty\"`\n\tServerName        *string         `proxy:\"servername,omitempty\"`\n\tClientFingerprint *string         `proxy:\"client-fingerprint,omitempty\"`\n}\n\nfunc (v *Vless) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ net.Conn, err error) {\n\tswitch v.option.Network {\n\tcase \"ws\":\n\t\thost, port, _ := net.SplitHostPort(v.addr)\n\t\twsOpts := &vmess.WebsocketConfig{\n\t\t\tHost:                     host,\n\t\t\tPort:                     port,\n\t\t\tPath:                     v.option.WSOpts.Path,\n\t\t\tMaxEarlyData:             v.option.WSOpts.MaxEarlyData,\n\t\t\tEarlyDataHeaderName:      v.option.WSOpts.EarlyDataHeaderName,\n\t\t\tV2rayHttpUpgrade:         v.option.WSOpts.V2rayHttpUpgrade,\n\t\t\tV2rayHttpUpgradeFastOpen: v.option.WSOpts.V2rayHttpUpgradeFastOpen,\n\t\t\tClientFingerprint:        v.option.ClientFingerprint,\n\t\t\tECHConfig:                v.echConfig,\n\t\t\tHeaders:                  http.Header{},\n\t\t}\n\n\t\tif len(v.option.WSOpts.Headers) != 0 {\n\t\t\tfor key, value := range v.option.WSOpts.Headers {\n\t\t\t\twsOpts.Headers.Add(key, value)\n\t\t\t}\n\t\t}\n\t\tif v.option.TLS {\n\t\t\twsOpts.TLS = true\n\t\t\twsOpts.TLSConfig, err = ca.GetTLSConfig(ca.Option{\n\t\t\t\tTLSConfig: &tls.Config{\n\t\t\t\t\tServerName:         host,\n\t\t\t\t\tInsecureSkipVerify: v.option.SkipCertVerify,\n\t\t\t\t\tNextProtos:         []string{\"http/1.1\"},\n\t\t\t\t},\n\t\t\t\tFingerprint: v.option.Fingerprint,\n\t\t\t\tCertificate: v.option.Certificate,\n\t\t\t\tPrivateKey:  v.option.PrivateKey,\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tif v.option.ServerName != \"\" {\n\t\t\t\twsOpts.TLSConfig.ServerName = v.option.ServerName\n\t\t\t} else if host := wsOpts.Headers.Get(\"Host\"); host != \"\" {\n\t\t\t\twsOpts.TLSConfig.ServerName = host\n\t\t\t}\n\t\t} else {\n\t\t\tif host := wsOpts.Headers.Get(\"Host\"); host == \"\" {\n\t\t\t\twsOpts.Headers.Set(\"Host\", convert.RandHost())\n\t\t\t\tconvert.SetUserAgent(wsOpts.Headers)\n\t\t\t}\n\t\t}\n\t\tc, err = vmess.StreamWebsocketConn(ctx, c, wsOpts)\n\tcase \"http\":\n\t\t// readability first, so just copy default TLS logic\n\t\tc, err = v.streamTLSConn(ctx, c, false)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\thost, _, _ := net.SplitHostPort(v.addr)\n\t\thttpOpts := &vmess.HTTPConfig{\n\t\t\tHost:    host,\n\t\t\tMethod:  v.option.HTTPOpts.Method,\n\t\t\tPath:    v.option.HTTPOpts.Path,\n\t\t\tHeaders: v.option.HTTPOpts.Headers,\n\t\t}\n\n\t\tc = vmess.StreamHTTPConn(c, httpOpts)\n\tcase \"h2\":\n\t\tc, err = v.streamTLSConn(ctx, c, true)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\th2Opts := &vmess.H2Config{\n\t\t\tHosts: v.option.HTTP2Opts.Host,\n\t\t\tPath:  v.option.HTTP2Opts.Path,\n\t\t}\n\n\t\tc, err = vmess.StreamH2Conn(ctx, c, h2Opts)\n\tcase \"grpc\":\n\t\tbreak // already handle in dialContext\n\tcase \"xhttp\":\n\t\tbreak // already handle in dialContext\n\tdefault:\n\t\t// default tcp network\n\t\t// handle TLS\n\t\tc, err = v.streamTLSConn(ctx, c, false)\n\t}\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn v.streamConnContext(ctx, c, metadata)\n}\n\nfunc (v *Vless) streamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (conn net.Conn, err error) {\n\tif ctx.Done() != nil {\n\t\tdone := N.SetupContextForConn(ctx, c)\n\t\tdefer done(&err)\n\t}\n\tif v.encryption != nil {\n\t\tc, err = v.encryption.Handshake(c)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tif metadata.NetWork == C.UDP {\n\t\tif v.option.PacketAddr {\n\t\t\tmetadata = &C.Metadata{\n\t\t\t\tNetWork: C.UDP,\n\t\t\t\tHost:    packetaddr.SeqPacketMagicAddress,\n\t\t\t\tDstPort: 443,\n\t\t\t}\n\t\t} else {\n\t\t\tmetadata = &C.Metadata{ // a clear metadata only contains ip\n\t\t\t\tNetWork: C.UDP,\n\t\t\t\tDstIP:   metadata.DstIP,\n\t\t\t\tDstPort: metadata.DstPort,\n\t\t\t}\n\t\t}\n\t\tconn, err = v.client.StreamConn(c, parseVlessAddr(metadata, v.option.XUDP))\n\t} else {\n\t\tconn, err = v.client.StreamConn(c, parseVlessAddr(metadata, false))\n\t}\n\tif err != nil {\n\t\tconn = nil\n\t}\n\treturn\n}\n\nfunc (v *Vless) streamTLSConn(ctx context.Context, conn net.Conn, isH2 bool) (net.Conn, error) {\n\tif v.option.TLS {\n\t\thost, _, _ := net.SplitHostPort(v.addr)\n\n\t\ttlsOpts := vmess.TLSConfig{\n\t\t\tHost:              host,\n\t\t\tSkipCertVerify:    v.option.SkipCertVerify,\n\t\t\tFingerPrint:       v.option.Fingerprint,\n\t\t\tCertificate:       v.option.Certificate,\n\t\t\tPrivateKey:        v.option.PrivateKey,\n\t\t\tClientFingerprint: v.option.ClientFingerprint,\n\t\t\tECH:               v.echConfig,\n\t\t\tReality:           v.realityConfig,\n\t\t\tNextProtos:        v.option.ALPN,\n\t\t}\n\n\t\tif isH2 {\n\t\t\ttlsOpts.NextProtos = []string{\"h2\"}\n\t\t}\n\n\t\tif v.option.ServerName != \"\" {\n\t\t\ttlsOpts.Host = v.option.ServerName\n\t\t}\n\n\t\treturn vmess.StreamTLSConn(ctx, conn, &tlsOpts)\n\t}\n\n\treturn conn, nil\n}\n\nfunc (v *Vless) dialContext(ctx context.Context) (c net.Conn, err error) {\n\tswitch v.option.Network {\n\tcase \"grpc\": // gun transport\n\t\treturn v.gunClient.Dial()\n\tcase \"xhttp\":\n\t\treturn v.xhttpClient.Dial(ctx)\n\tdefault:\n\t}\n\treturn v.dialer.DialContext(ctx, \"tcp\", v.addr)\n}\n\n// DialContext implements C.ProxyAdapter\nfunc (v *Vless) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Conn, err error) {\n\tc, err := v.dialContext(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s connect error: %s\", v.addr, err.Error())\n\t}\n\tdefer func(c net.Conn) {\n\t\tsafeConnClose(c, err)\n\t}(c)\n\n\tc, err = v.StreamConnContext(ctx, c, metadata)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s connect error: %s\", v.addr, err.Error())\n\t}\n\treturn NewConn(c, v), err\n}\n\n// ListenPacketContext implements C.ProxyAdapter\nfunc (v *Vless) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {\n\tif err = v.ResolveUDP(ctx, metadata); err != nil {\n\t\treturn nil, err\n\t}\n\n\tc, err := v.dialContext(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s connect error: %s\", v.addr, err.Error())\n\t}\n\tdefer func(c net.Conn) {\n\t\tsafeConnClose(c, err)\n\t}(c)\n\n\tc, err = v.StreamConnContext(ctx, c, metadata)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s connect error: %s\", v.addr, err.Error())\n\t}\n\n\tif v.option.XUDP {\n\t\tvar globalID [8]byte\n\t\tif metadata.SourceValid() {\n\t\t\tglobalID = utils.GlobalID(metadata.SourceAddress())\n\t\t}\n\t\treturn newPacketConn(N.NewThreadSafePacketConn(\n\t\t\tvmessSing.NewXUDPConn(c,\n\t\t\t\tglobalID,\n\t\t\t\tM.SocksaddrFromNet(metadata.UDPAddr())),\n\t\t), v), nil\n\t} else if v.option.PacketAddr {\n\t\treturn newPacketConn(N.NewThreadSafePacketConn(\n\t\t\tpacketaddr.NewConn(v.client.PacketConn(c, metadata.UDPAddr()),\n\t\t\t\tM.SocksaddrFromNet(metadata.UDPAddr())),\n\t\t), v), nil\n\t}\n\treturn newPacketConn(N.NewThreadSafePacketConn(v.client.PacketConn(c, metadata.UDPAddr())), v), nil\n}\n\n// SupportUOT implements C.ProxyAdapter\nfunc (v *Vless) SupportUOT() bool {\n\treturn true\n}\n\n// ProxyInfo implements C.ProxyAdapter\nfunc (v *Vless) ProxyInfo() C.ProxyInfo {\n\tinfo := v.Base.ProxyInfo()\n\tinfo.DialerProxy = v.option.DialerProxy\n\treturn info\n}\n\n// Close implements C.ProxyAdapter\nfunc (v *Vless) Close() error {\n\tvar errs []error\n\tif v.gunClient != nil {\n\t\tif err := v.gunClient.Close(); err != nil {\n\t\t\terrs = append(errs, err)\n\t\t}\n\t}\n\tif v.xhttpClient != nil {\n\t\tif err := v.xhttpClient.Close(); err != nil {\n\t\t\terrs = append(errs, err)\n\t\t}\n\t}\n\treturn errors.Join(errs...)\n}\n\nfunc parseVlessAddr(metadata *C.Metadata, xudp bool) *vless.DstAddr {\n\tvar addrType byte\n\tvar addr []byte\n\tswitch metadata.AddrType() {\n\tcase C.AtypIPv4:\n\t\taddrType = vless.AtypIPv4\n\t\taddr = make([]byte, net.IPv4len)\n\t\tcopy(addr[:], metadata.DstIP.AsSlice())\n\tcase C.AtypIPv6:\n\t\taddrType = vless.AtypIPv6\n\t\taddr = make([]byte, net.IPv6len)\n\t\tcopy(addr[:], metadata.DstIP.AsSlice())\n\tcase C.AtypDomainName:\n\t\taddrType = vless.AtypDomainName\n\t\taddr = make([]byte, len(metadata.Host)+1)\n\t\taddr[0] = byte(len(metadata.Host))\n\t\tcopy(addr[1:], metadata.Host)\n\t}\n\n\treturn &vless.DstAddr{\n\t\tUDP:      metadata.NetWork == C.UDP,\n\t\tAddrType: addrType,\n\t\tAddr:     addr,\n\t\tPort:     metadata.DstPort,\n\t\tMux:      metadata.NetWork == C.UDP && xudp,\n\t}\n}\n\nfunc NewVless(option VlessOption) (*Vless, error) {\n\tvar addons *vless.Addons\n\tif len(option.Flow) >= 16 {\n\t\toption.Flow = option.Flow[:16]\n\t\tif option.Flow != vless.XRV {\n\t\t\treturn nil, fmt.Errorf(\"unsupported xtls flow type: %s\", option.Flow)\n\t\t}\n\t\taddons = &vless.Addons{\n\t\t\tFlow: option.Flow,\n\t\t}\n\t}\n\n\tswitch option.PacketEncoding {\n\tcase \"packetaddr\", \"packet\":\n\t\toption.PacketAddr = true\n\t\toption.XUDP = false\n\tdefault: // https://github.com/XTLS/Xray-core/pull/1567#issuecomment-1407305458\n\t\tif !option.PacketAddr {\n\t\t\toption.XUDP = true\n\t\t}\n\t}\n\tif option.XUDP {\n\t\toption.PacketAddr = false\n\t}\n\n\tclient, err := vless.NewClient(option.UUID, addons)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tv := &Vless{\n\t\tBase: NewBase(BaseOption{\n\t\t\tName:         option.Name,\n\t\t\tAddr:         net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),\n\t\t\tType:         C.Vless,\n\t\t\tProviderName: option.ProviderName,\n\t\t\tUDP:          option.UDP,\n\t\t\tXUDP:         option.XUDP,\n\t\t\tTFO:          option.TFO,\n\t\t\tMPTCP:        option.MPTCP,\n\t\t\tInterface:    option.Interface,\n\t\t\tRoutingMark:  option.RoutingMark,\n\t\t\tPrefer:       option.IPVersion,\n\t\t}),\n\t\tclient: client,\n\t\toption: &option,\n\t}\n\tv.dialer = option.NewDialer(v.DialOptions())\n\n\tv.encryption, err = encryption.NewClient(option.Encryption)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tv.realityConfig, err = v.option.RealityOpts.Parse()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tv.echConfig, err = v.option.ECHOpts.Parse()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tswitch option.Network {\n\tcase \"h2\":\n\t\tif len(option.HTTP2Opts.Host) == 0 {\n\t\t\toption.HTTP2Opts.Host = append(option.HTTP2Opts.Host, \"www.example.com\")\n\t\t}\n\tcase \"grpc\":\n\t\tdialFn := func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\t\tc, err := v.dialer.DialContext(ctx, \"tcp\", v.addr)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"%s connect error: %s\", v.addr, err.Error())\n\t\t\t}\n\t\t\treturn c, nil\n\t\t}\n\n\t\tgunConfig := &gun.Config{\n\t\t\tServiceName:  option.GrpcOpts.GrpcServiceName,\n\t\t\tUserAgent:    option.GrpcOpts.GrpcUserAgent,\n\t\t\tHost:         option.ServerName,\n\t\t\tPingInterval: option.GrpcOpts.PingInterval,\n\t\t}\n\t\tif option.ServerName == \"\" {\n\t\t\tgunConfig.Host = v.addr\n\t\t}\n\t\tvar tlsConfig *vmess.TLSConfig\n\t\tif option.TLS {\n\t\t\ttlsConfig = &vmess.TLSConfig{\n\t\t\t\tHost:              option.ServerName,\n\t\t\t\tSkipCertVerify:    option.SkipCertVerify,\n\t\t\t\tFingerPrint:       option.Fingerprint,\n\t\t\t\tCertificate:       option.Certificate,\n\t\t\t\tPrivateKey:        option.PrivateKey,\n\t\t\t\tClientFingerprint: option.ClientFingerprint,\n\t\t\t\tNextProtos:        []string{\"h2\"},\n\t\t\t\tECH:               v.echConfig,\n\t\t\t\tReality:           v.realityConfig,\n\t\t\t}\n\t\t\tif option.ServerName == \"\" {\n\t\t\t\thost, _, _ := net.SplitHostPort(v.addr)\n\t\t\t\ttlsConfig.Host = host\n\t\t\t}\n\t\t}\n\n\t\tv.gunClient = gun.NewClient(\n\t\t\tfunc() *gun.Transport {\n\t\t\t\treturn gun.NewTransport(dialFn, tlsConfig, gunConfig)\n\t\t\t},\n\t\t\toption.GrpcOpts.MaxConnections,\n\t\t\toption.GrpcOpts.MinStreams,\n\t\t\toption.GrpcOpts.MaxStreams,\n\t\t)\n\tcase \"xhttp\":\n\t\trequestHost := v.option.XHTTPOpts.Host\n\t\tif requestHost == \"\" {\n\t\t\tif v.option.ServerName != \"\" {\n\t\t\t\trequestHost = v.option.ServerName\n\t\t\t} else {\n\t\t\t\trequestHost = v.option.Server\n\t\t\t}\n\t\t}\n\n\t\tvar hKeepAlivePeriod time.Duration\n\n\t\tvar reuseCfg *xhttp.ReuseConfig\n\t\tif option.XHTTPOpts.ReuseSettings != nil {\n\t\t\treuseCfg = &xhttp.ReuseConfig{\n\t\t\t\tMaxConcurrency:   option.XHTTPOpts.ReuseSettings.MaxConcurrency,\n\t\t\t\tMaxConnections:   option.XHTTPOpts.ReuseSettings.MaxConnections,\n\t\t\t\tCMaxReuseTimes:   option.XHTTPOpts.ReuseSettings.CMaxReuseTimes,\n\t\t\t\tHMaxRequestTimes: option.XHTTPOpts.ReuseSettings.HMaxRequestTimes,\n\t\t\t\tHMaxReusableSecs: option.XHTTPOpts.ReuseSettings.HMaxReusableSecs,\n\t\t\t}\n\t\t\thKeepAlivePeriod = time.Duration(option.XHTTPOpts.ReuseSettings.HKeepAlivePeriod) * time.Second\n\t\t}\n\n\t\tcfg := &xhttp.Config{\n\t\t\tHost:                 requestHost,\n\t\t\tPath:                 v.option.XHTTPOpts.Path,\n\t\t\tMode:                 v.option.XHTTPOpts.Mode,\n\t\t\tHeaders:              v.option.XHTTPOpts.Headers,\n\t\t\tNoGRPCHeader:         v.option.XHTTPOpts.NoGRPCHeader,\n\t\t\tXPaddingBytes:        v.option.XHTTPOpts.XPaddingBytes,\n\t\t\tXPaddingObfsMode:     v.option.XHTTPOpts.XPaddingObfsMode,\n\t\t\tXPaddingKey:          v.option.XHTTPOpts.XPaddingKey,\n\t\t\tXPaddingHeader:       v.option.XHTTPOpts.XPaddingHeader,\n\t\t\tXPaddingPlacement:    v.option.XHTTPOpts.XPaddingPlacement,\n\t\t\tXPaddingMethod:       v.option.XHTTPOpts.XPaddingMethod,\n\t\t\tUplinkHTTPMethod:     v.option.XHTTPOpts.UplinkHTTPMethod,\n\t\t\tSessionPlacement:     v.option.XHTTPOpts.SessionPlacement,\n\t\t\tSessionKey:           v.option.XHTTPOpts.SessionKey,\n\t\t\tSeqPlacement:         v.option.XHTTPOpts.SeqPlacement,\n\t\t\tSeqKey:               v.option.XHTTPOpts.SeqKey,\n\t\t\tUplinkDataPlacement:  v.option.XHTTPOpts.UplinkDataPlacement,\n\t\t\tUplinkDataKey:        v.option.XHTTPOpts.UplinkDataKey,\n\t\t\tUplinkChunkSize:      v.option.XHTTPOpts.UplinkChunkSize,\n\t\t\tScMaxEachPostBytes:   v.option.XHTTPOpts.ScMaxEachPostBytes,\n\t\t\tScMinPostsIntervalMs: v.option.XHTTPOpts.ScMinPostsIntervalMs,\n\t\t\tReuseConfig:          reuseCfg,\n\t\t}\n\n\t\tmakeTransport := func() http.RoundTripper {\n\t\t\treturn xhttp.NewTransport(\n\t\t\t\tfunc(ctx context.Context) (net.Conn, error) {\n\t\t\t\t\treturn v.dialer.DialContext(ctx, \"tcp\", v.addr)\n\t\t\t\t},\n\t\t\t\tfunc(ctx context.Context, raw net.Conn, isH2 bool) (net.Conn, error) {\n\t\t\t\t\treturn v.streamTLSConn(ctx, raw, isH2)\n\t\t\t\t},\n\t\t\t\tfunc(ctx context.Context, cfg *quic.Config) (*quic.Conn, error) {\n\t\t\t\t\thost, _, _ := net.SplitHostPort(v.addr)\n\t\t\t\t\ttlsOpts := &vmess.TLSConfig{\n\t\t\t\t\t\tHost:              host,\n\t\t\t\t\t\tSkipCertVerify:    v.option.SkipCertVerify,\n\t\t\t\t\t\tFingerPrint:       v.option.Fingerprint,\n\t\t\t\t\t\tCertificate:       v.option.Certificate,\n\t\t\t\t\t\tPrivateKey:        v.option.PrivateKey,\n\t\t\t\t\t\tClientFingerprint: v.option.ClientFingerprint,\n\t\t\t\t\t\tECH:               v.echConfig,\n\t\t\t\t\t\tReality:           v.realityConfig,\n\t\t\t\t\t\tNextProtos:        []string{\"h3\"},\n\t\t\t\t\t}\n\t\t\t\t\tif v.option.ServerName != \"\" {\n\t\t\t\t\t\ttlsOpts.Host = v.option.ServerName\n\t\t\t\t\t}\n\t\t\t\t\tif !v.option.TLS {\n\t\t\t\t\t\treturn nil, errors.New(\"xhttp HTTP/3 requires TLS\")\n\t\t\t\t\t}\n\t\t\t\t\tif v.realityConfig != nil {\n\t\t\t\t\t\treturn nil, errors.New(\"xhttp HTTP/3 does not support reality\")\n\t\t\t\t\t}\n\t\t\t\t\ttlsConfig, err := tlsOpts.ToStdConfig()\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\n\t\t\t\t\terr = v.echConfig.ClientHandle(ctx, tlsConfig)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\t\t\t\t\t_, quicConn, err := common.DialQuic(ctx, v.addr, v.DialOptions(), v.dialer, tlsConfig, cfg, true)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t}\n\t\t\t\t\treturn quicConn, nil\n\t\t\t\t},\n\t\t\t\tv.option.ALPN,\n\t\t\t\thKeepAlivePeriod,\n\t\t\t)\n\t\t}\n\t\tvar makeDownloadTransport func() http.RoundTripper\n\n\t\tif ds := v.option.XHTTPOpts.DownloadSettings; ds != nil {\n\t\t\tif cfg.Mode == \"stream-one\" {\n\t\t\t\treturn nil, fmt.Errorf(`xhttp mode \"stream-one\" cannot be used with download-settings`)\n\t\t\t}\n\n\t\t\tdownloadServer := lo.FromPtrOr(ds.Server, v.option.Server)\n\t\t\tdownloadPort := lo.FromPtrOr(ds.Port, v.option.Port)\n\t\t\tdownloadTLS := lo.FromPtrOr(ds.TLS, v.option.TLS)\n\t\t\tdownloadALPN := lo.FromPtrOr(ds.ALPN, v.option.ALPN)\n\t\t\tdownloadEchConfig := v.echConfig\n\t\t\tif ds.ECHOpts != nil {\n\t\t\t\tdownloadEchConfig, err = ds.ECHOpts.Parse()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\t\t\tdownloadRealityCfg := v.realityConfig\n\t\t\tif ds.RealityOpts != nil {\n\t\t\t\tdownloadRealityCfg, err = ds.RealityOpts.Parse()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\t\t\tdownloadSkipCertVerify := lo.FromPtrOr(ds.SkipCertVerify, v.option.SkipCertVerify)\n\t\t\tdownloadFingerprint := lo.FromPtrOr(ds.Fingerprint, v.option.Fingerprint)\n\t\t\tdownloadCertificate := lo.FromPtrOr(ds.Certificate, v.option.Certificate)\n\t\t\tdownloadPrivateKey := lo.FromPtrOr(ds.PrivateKey, v.option.PrivateKey)\n\t\t\tdownloadServerName := lo.FromPtrOr(ds.ServerName, v.option.ServerName)\n\t\t\tdownloadClientFingerprint := lo.FromPtrOr(ds.ClientFingerprint, v.option.ClientFingerprint)\n\n\t\t\tdownloadAddr := net.JoinHostPort(downloadServer, strconv.Itoa(downloadPort))\n\n\t\t\tdownloadHost := lo.FromPtrOr(ds.Host, v.option.XHTTPOpts.Host)\n\t\t\tif downloadHost == \"\" {\n\t\t\t\tif downloadServerName != \"\" {\n\t\t\t\t\tdownloadHost = downloadServerName\n\t\t\t\t} else {\n\t\t\t\t\tdownloadHost = downloadServer\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tdownloadHKeepAlivePeriod := hKeepAlivePeriod\n\n\t\t\tdownloadCfg := *cfg // make a copy\n\t\t\tdownloadCfg.Host = downloadHost\n\t\t\tdownloadCfg.Path = lo.FromPtrOr(ds.Path, v.option.XHTTPOpts.Path)\n\t\t\tdownloadCfg.Headers = lo.FromPtrOr(ds.Headers, v.option.XHTTPOpts.Headers)\n\n\t\t\tif ds.ReuseSettings != nil {\n\t\t\t\tdownloadCfg.ReuseConfig = &xhttp.ReuseConfig{\n\t\t\t\t\tMaxConcurrency:   ds.ReuseSettings.MaxConcurrency,\n\t\t\t\t\tMaxConnections:   ds.ReuseSettings.MaxConnections,\n\t\t\t\t\tCMaxReuseTimes:   ds.ReuseSettings.CMaxReuseTimes,\n\t\t\t\t\tHMaxRequestTimes: ds.ReuseSettings.HMaxRequestTimes,\n\t\t\t\t\tHMaxReusableSecs: ds.ReuseSettings.HMaxReusableSecs,\n\t\t\t\t}\n\t\t\t\tdownloadHKeepAlivePeriod = time.Duration(ds.ReuseSettings.HKeepAlivePeriod) * time.Second\n\t\t\t}\n\n\t\t\tcfg.DownloadConfig = &downloadCfg\n\n\t\t\tmakeDownloadTransport = func() http.RoundTripper {\n\t\t\t\treturn xhttp.NewTransport(\n\t\t\t\t\tfunc(ctx context.Context) (net.Conn, error) {\n\t\t\t\t\t\treturn v.dialer.DialContext(ctx, \"tcp\", downloadAddr)\n\t\t\t\t\t},\n\t\t\t\t\tfunc(ctx context.Context, conn net.Conn, isH2 bool) (net.Conn, error) {\n\t\t\t\t\t\tif downloadTLS {\n\t\t\t\t\t\t\thost, _, _ := net.SplitHostPort(downloadAddr)\n\n\t\t\t\t\t\t\ttlsOpts := vmess.TLSConfig{\n\t\t\t\t\t\t\t\tHost:              host,\n\t\t\t\t\t\t\t\tSkipCertVerify:    downloadSkipCertVerify,\n\t\t\t\t\t\t\t\tFingerPrint:       downloadFingerprint,\n\t\t\t\t\t\t\t\tCertificate:       downloadCertificate,\n\t\t\t\t\t\t\t\tPrivateKey:        downloadPrivateKey,\n\t\t\t\t\t\t\t\tClientFingerprint: downloadClientFingerprint,\n\t\t\t\t\t\t\t\tECH:               downloadEchConfig,\n\t\t\t\t\t\t\t\tReality:           downloadRealityCfg,\n\t\t\t\t\t\t\t\tNextProtos:        downloadALPN,\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif isH2 {\n\t\t\t\t\t\t\t\ttlsOpts.NextProtos = []string{\"h2\"}\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\tif downloadServerName != \"\" {\n\t\t\t\t\t\t\t\ttlsOpts.Host = downloadServerName\n\t\t\t\t\t\t\t}\n\n\t\t\t\t\t\t\treturn vmess.StreamTLSConn(ctx, conn, &tlsOpts)\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\treturn conn, nil\n\t\t\t\t\t},\n\t\t\t\t\tfunc(ctx context.Context, cfg *quic.Config) (*quic.Conn, error) {\n\t\t\t\t\t\thost, _, _ := net.SplitHostPort(downloadAddr)\n\t\t\t\t\t\ttlsOpts := &vmess.TLSConfig{\n\t\t\t\t\t\t\tHost:              host,\n\t\t\t\t\t\t\tSkipCertVerify:    downloadSkipCertVerify,\n\t\t\t\t\t\t\tFingerPrint:       downloadFingerprint,\n\t\t\t\t\t\t\tCertificate:       downloadCertificate,\n\t\t\t\t\t\t\tPrivateKey:        downloadPrivateKey,\n\t\t\t\t\t\t\tClientFingerprint: downloadClientFingerprint,\n\t\t\t\t\t\t\tECH:               downloadEchConfig,\n\t\t\t\t\t\t\tReality:           downloadRealityCfg,\n\t\t\t\t\t\t\tNextProtos:        []string{\"h3\"},\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif downloadServerName != \"\" {\n\t\t\t\t\t\t\ttlsOpts.Host = downloadServerName\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif !downloadTLS {\n\t\t\t\t\t\t\treturn nil, errors.New(\"xhttp HTTP/3 requires TLS\")\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif downloadRealityCfg != nil {\n\t\t\t\t\t\t\treturn nil, errors.New(\"xhttp HTTP/3 does not support reality\")\n\t\t\t\t\t\t}\n\t\t\t\t\t\ttlsConfig, err := tlsOpts.ToStdConfig()\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\terr = downloadEchConfig.ClientHandle(ctx, tlsConfig)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t\t}\n\t\t\t\t\t\t_, quicConn, err := common.DialQuic(ctx, downloadAddr, v.DialOptions(), v.dialer, tlsConfig, cfg, true)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\treturn nil, err\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn quicConn, nil\n\t\t\t\t\t},\n\t\t\t\t\tdownloadALPN,\n\t\t\t\t\tdownloadHKeepAlivePeriod,\n\t\t\t\t)\n\t\t\t}\n\t\t}\n\n\t\tv.xhttpClient, err = xhttp.NewClient(cfg, makeTransport, makeDownloadTransport, v.realityConfig != nil)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\treturn v, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outbound/vmess.go",
    "content": "package outbound\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"github.com/metacubex/mihomo/component/ca\"\n\t\"github.com/metacubex/mihomo/component/ech\"\n\ttlsC \"github.com/metacubex/mihomo/component/tls\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/ntp\"\n\t\"github.com/metacubex/mihomo/transport/gun\"\n\tmihomoVMess \"github.com/metacubex/mihomo/transport/vmess\"\n\n\t\"github.com/metacubex/http\"\n\tvmess \"github.com/metacubex/sing-vmess\"\n\t\"github.com/metacubex/sing-vmess/packetaddr\"\n\tM \"github.com/metacubex/sing/common/metadata\"\n\t\"github.com/metacubex/tls\"\n)\n\nvar ErrUDPRemoteAddrMismatch = errors.New(\"udp packet dropped due to mismatched remote address\")\n\ntype Vmess struct {\n\t*Base\n\tclient *vmess.Client\n\toption *VmessOption\n\n\t// for gun mux\n\tgunClient *gun.Client\n\n\trealityConfig *tlsC.RealityConfig\n\techConfig     *ech.Config\n}\n\ntype VmessOption struct {\n\tBasicOption\n\tName                string         `proxy:\"name\"`\n\tServer              string         `proxy:\"server\"`\n\tPort                int            `proxy:\"port\"`\n\tUUID                string         `proxy:\"uuid\"`\n\tAlterID             int            `proxy:\"alterId\"`\n\tCipher              string         `proxy:\"cipher\"`\n\tUDP                 bool           `proxy:\"udp,omitempty\"`\n\tNetwork             string         `proxy:\"network,omitempty\"`\n\tTLS                 bool           `proxy:\"tls,omitempty\"`\n\tALPN                []string       `proxy:\"alpn,omitempty\"`\n\tSkipCertVerify      bool           `proxy:\"skip-cert-verify,omitempty\"`\n\tFingerprint         string         `proxy:\"fingerprint,omitempty\"`\n\tCertificate         string         `proxy:\"certificate,omitempty\"`\n\tPrivateKey          string         `proxy:\"private-key,omitempty\"`\n\tServerName          string         `proxy:\"servername,omitempty\"`\n\tECHOpts             ECHOptions     `proxy:\"ech-opts,omitempty\"`\n\tRealityOpts         RealityOptions `proxy:\"reality-opts,omitempty\"`\n\tHTTPOpts            HTTPOptions    `proxy:\"http-opts,omitempty\"`\n\tHTTP2Opts           HTTP2Options   `proxy:\"h2-opts,omitempty\"`\n\tGrpcOpts            GrpcOptions    `proxy:\"grpc-opts,omitempty\"`\n\tWSOpts              WSOptions      `proxy:\"ws-opts,omitempty\"`\n\tPacketAddr          bool           `proxy:\"packet-addr,omitempty\"`\n\tXUDP                bool           `proxy:\"xudp,omitempty\"`\n\tPacketEncoding      string         `proxy:\"packet-encoding,omitempty\"`\n\tGlobalPadding       bool           `proxy:\"global-padding,omitempty\"`\n\tAuthenticatedLength bool           `proxy:\"authenticated-length,omitempty\"`\n\tClientFingerprint   string         `proxy:\"client-fingerprint,omitempty\"`\n}\n\ntype HTTPOptions struct {\n\tMethod  string              `proxy:\"method,omitempty\"`\n\tPath    []string            `proxy:\"path,omitempty\"`\n\tHeaders map[string][]string `proxy:\"headers,omitempty\"`\n}\n\ntype HTTP2Options struct {\n\tHost []string `proxy:\"host,omitempty\"`\n\tPath string   `proxy:\"path,omitempty\"`\n}\n\ntype GrpcOptions struct {\n\tGrpcServiceName string `proxy:\"grpc-service-name,omitempty\"`\n\tGrpcUserAgent   string `proxy:\"grpc-user-agent,omitempty\"`\n\tPingInterval    int    `proxy:\"ping-interval,omitempty\"`\n\tMaxConnections  int    `proxy:\"max-connections,omitempty\"`\n\tMinStreams      int    `proxy:\"min-streams,omitempty\"`\n\tMaxStreams      int    `proxy:\"max-streams,omitempty\"`\n}\n\ntype WSOptions struct {\n\tPath                     string            `proxy:\"path,omitempty\"`\n\tHeaders                  map[string]string `proxy:\"headers,omitempty\"`\n\tMaxEarlyData             int               `proxy:\"max-early-data,omitempty\"`\n\tEarlyDataHeaderName      string            `proxy:\"early-data-header-name,omitempty\"`\n\tV2rayHttpUpgrade         bool              `proxy:\"v2ray-http-upgrade,omitempty\"`\n\tV2rayHttpUpgradeFastOpen bool              `proxy:\"v2ray-http-upgrade-fast-open,omitempty\"`\n}\n\nfunc (v *Vmess) StreamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (_ net.Conn, err error) {\n\tswitch v.option.Network {\n\tcase \"ws\":\n\t\thost, port, _ := net.SplitHostPort(v.addr)\n\t\twsOpts := &mihomoVMess.WebsocketConfig{\n\t\t\tHost:                     host,\n\t\t\tPort:                     port,\n\t\t\tPath:                     v.option.WSOpts.Path,\n\t\t\tMaxEarlyData:             v.option.WSOpts.MaxEarlyData,\n\t\t\tEarlyDataHeaderName:      v.option.WSOpts.EarlyDataHeaderName,\n\t\t\tV2rayHttpUpgrade:         v.option.WSOpts.V2rayHttpUpgrade,\n\t\t\tV2rayHttpUpgradeFastOpen: v.option.WSOpts.V2rayHttpUpgradeFastOpen,\n\t\t\tClientFingerprint:        v.option.ClientFingerprint,\n\t\t\tECHConfig:                v.echConfig,\n\t\t\tHeaders:                  http.Header{},\n\t\t}\n\n\t\tif len(v.option.WSOpts.Headers) != 0 {\n\t\t\tfor key, value := range v.option.WSOpts.Headers {\n\t\t\t\twsOpts.Headers.Add(key, value)\n\t\t\t}\n\t\t}\n\n\t\tif v.option.TLS {\n\t\t\twsOpts.TLS = true\n\t\t\twsOpts.TLSConfig, err = ca.GetTLSConfig(ca.Option{\n\t\t\t\tTLSConfig: &tls.Config{\n\t\t\t\t\tServerName:         host,\n\t\t\t\t\tInsecureSkipVerify: v.option.SkipCertVerify,\n\t\t\t\t\tNextProtos:         []string{\"http/1.1\"},\n\t\t\t\t},\n\t\t\t\tFingerprint: v.option.Fingerprint,\n\t\t\t\tCertificate: v.option.Certificate,\n\t\t\t\tPrivateKey:  v.option.PrivateKey,\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tif v.option.ServerName != \"\" {\n\t\t\t\twsOpts.TLSConfig.ServerName = v.option.ServerName\n\t\t\t} else if host := wsOpts.Headers.Get(\"Host\"); host != \"\" {\n\t\t\t\twsOpts.TLSConfig.ServerName = host\n\t\t\t}\n\t\t}\n\t\tc, err = mihomoVMess.StreamWebsocketConn(ctx, c, wsOpts)\n\tcase \"http\":\n\t\t// readability first, so just copy default TLS logic\n\t\tc, err = v.streamTLSConn(ctx, c, false)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\thost, _, _ := net.SplitHostPort(v.addr)\n\t\thttpOpts := &mihomoVMess.HTTPConfig{\n\t\t\tHost:    host,\n\t\t\tMethod:  v.option.HTTPOpts.Method,\n\t\t\tPath:    v.option.HTTPOpts.Path,\n\t\t\tHeaders: v.option.HTTPOpts.Headers,\n\t\t}\n\n\t\tc = mihomoVMess.StreamHTTPConn(c, httpOpts)\n\tcase \"h2\":\n\t\tc, err = v.streamTLSConn(ctx, c, true)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\th2Opts := &mihomoVMess.H2Config{\n\t\t\tHosts: v.option.HTTP2Opts.Host,\n\t\t\tPath:  v.option.HTTP2Opts.Path,\n\t\t}\n\n\t\tc, err = mihomoVMess.StreamH2Conn(ctx, c, h2Opts)\n\tcase \"grpc\":\n\t\tbreak // already handle in dialContext\n\tdefault:\n\t\t// default tcp network\n\t\t// handle TLS\n\t\tc, err = v.streamTLSConn(ctx, c, false)\n\t}\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn v.streamConnContext(ctx, c, metadata)\n}\n\nfunc (v *Vmess) streamConnContext(ctx context.Context, c net.Conn, metadata *C.Metadata) (conn net.Conn, err error) {\n\tuseEarly := N.NeedHandshake(c)\n\tif !useEarly {\n\t\tif ctx.Done() != nil {\n\t\t\tdone := N.SetupContextForConn(ctx, c)\n\t\t\tdefer done(&err)\n\t\t}\n\t}\n\tif metadata.NetWork == C.UDP {\n\t\tif v.option.XUDP {\n\t\t\tvar globalID [8]byte\n\t\t\tif metadata.SourceValid() {\n\t\t\t\tglobalID = utils.GlobalID(metadata.SourceAddress())\n\t\t\t}\n\t\t\tif useEarly {\n\t\t\t\tconn = v.client.DialEarlyXUDPPacketConn(c,\n\t\t\t\t\tglobalID,\n\t\t\t\t\tM.SocksaddrFromNet(metadata.UDPAddr()))\n\t\t\t} else {\n\t\t\t\tconn, err = v.client.DialXUDPPacketConn(c,\n\t\t\t\t\tglobalID,\n\t\t\t\t\tM.SocksaddrFromNet(metadata.UDPAddr()))\n\t\t\t}\n\t\t} else if v.option.PacketAddr {\n\t\t\tif useEarly {\n\t\t\t\tconn = v.client.DialEarlyPacketConn(c,\n\t\t\t\t\tM.ParseSocksaddrHostPort(packetaddr.SeqPacketMagicAddress, 443))\n\t\t\t} else {\n\t\t\t\tconn, err = v.client.DialPacketConn(c,\n\t\t\t\t\tM.ParseSocksaddrHostPort(packetaddr.SeqPacketMagicAddress, 443))\n\t\t\t}\n\t\t\tconn = packetaddr.NewBindConn(conn)\n\t\t} else {\n\t\t\tif useEarly {\n\t\t\t\tconn = v.client.DialEarlyPacketConn(c,\n\t\t\t\t\tM.SocksaddrFromNet(metadata.UDPAddr()))\n\t\t\t} else {\n\t\t\t\tconn, err = v.client.DialPacketConn(c,\n\t\t\t\t\tM.SocksaddrFromNet(metadata.UDPAddr()))\n\t\t\t}\n\t\t}\n\t} else {\n\t\tif useEarly {\n\t\t\tconn = v.client.DialEarlyConn(c,\n\t\t\t\tM.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort))\n\t\t} else {\n\t\t\tconn, err = v.client.DialConn(c,\n\t\t\t\tM.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort))\n\t\t}\n\t}\n\tif err != nil {\n\t\tconn = nil\n\t}\n\treturn\n}\n\nfunc (v *Vmess) streamTLSConn(ctx context.Context, conn net.Conn, isH2 bool) (net.Conn, error) {\n\tif v.option.TLS {\n\t\thost, _, _ := net.SplitHostPort(v.addr)\n\n\t\ttlsOpts := mihomoVMess.TLSConfig{\n\t\t\tHost:              host,\n\t\t\tSkipCertVerify:    v.option.SkipCertVerify,\n\t\t\tFingerPrint:       v.option.Fingerprint,\n\t\t\tCertificate:       v.option.Certificate,\n\t\t\tPrivateKey:        v.option.PrivateKey,\n\t\t\tClientFingerprint: v.option.ClientFingerprint,\n\t\t\tECH:               v.echConfig,\n\t\t\tReality:           v.realityConfig,\n\t\t\tNextProtos:        v.option.ALPN,\n\t\t}\n\n\t\tif isH2 {\n\t\t\ttlsOpts.NextProtos = []string{\"h2\"}\n\t\t}\n\n\t\tif v.option.ServerName != \"\" {\n\t\t\ttlsOpts.Host = v.option.ServerName\n\t\t}\n\n\t\treturn mihomoVMess.StreamTLSConn(ctx, conn, &tlsOpts)\n\t}\n\n\treturn conn, nil\n}\n\nfunc (v *Vmess) dialContext(ctx context.Context) (c net.Conn, err error) {\n\tswitch v.option.Network {\n\tcase \"grpc\": // gun transport\n\t\treturn v.gunClient.Dial()\n\tdefault:\n\t}\n\treturn v.dialer.DialContext(ctx, \"tcp\", v.addr)\n}\n\n// DialContext implements C.ProxyAdapter\nfunc (v *Vmess) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Conn, err error) {\n\tc, err := v.dialContext(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s connect error: %s\", v.addr, err.Error())\n\t}\n\tdefer func(c net.Conn) {\n\t\tsafeConnClose(c, err)\n\t}(c)\n\n\tc, err = v.StreamConnContext(ctx, c, metadata)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s connect error: %s\", v.addr, err.Error())\n\t}\n\treturn NewConn(c, v), err\n}\n\n// ListenPacketContext implements C.ProxyAdapter\nfunc (v *Vmess) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {\n\tif err = v.ResolveUDP(ctx, metadata); err != nil {\n\t\treturn nil, err\n\t}\n\n\tc, err := v.dialContext(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s connect error: %s\", v.addr, err.Error())\n\t}\n\tdefer func(c net.Conn) {\n\t\tsafeConnClose(c, err)\n\t}(c)\n\n\tc, err = v.StreamConnContext(ctx, c, metadata)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s connect error: %s\", v.addr, err.Error())\n\t}\n\n\tif pc, ok := c.(net.PacketConn); ok {\n\t\treturn newPacketConn(N.NewThreadSafePacketConn(pc), v), nil\n\t}\n\treturn newPacketConn(&vmessPacketConn{Conn: c, rAddr: metadata.UDPAddr()}, v), nil\n}\n\n// ProxyInfo implements C.ProxyAdapter\nfunc (v *Vmess) ProxyInfo() C.ProxyInfo {\n\tinfo := v.Base.ProxyInfo()\n\tinfo.DialerProxy = v.option.DialerProxy\n\treturn info\n}\n\n// Close implements C.ProxyAdapter\nfunc (v *Vmess) Close() error {\n\tvar errs []error\n\tif v.gunClient != nil {\n\t\tif err := v.gunClient.Close(); err != nil {\n\t\t\terrs = append(errs, err)\n\t\t}\n\t}\n\treturn errors.Join(errs...)\n}\n\n// SupportUOT implements C.ProxyAdapter\nfunc (v *Vmess) SupportUOT() bool {\n\treturn true\n}\n\nfunc NewVmess(option VmessOption) (*Vmess, error) {\n\tsecurity := strings.ToLower(option.Cipher)\n\tvar options []vmess.ClientOption\n\tif option.GlobalPadding {\n\t\toptions = append(options, vmess.ClientWithGlobalPadding())\n\t}\n\tif option.AuthenticatedLength {\n\t\toptions = append(options, vmess.ClientWithAuthenticatedLength())\n\t}\n\toptions = append(options, vmess.ClientWithTimeFunc(ntp.Now))\n\tclient, err := vmess.NewClient(option.UUID, security, option.AlterID, options...)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tswitch option.PacketEncoding {\n\tcase \"packetaddr\", \"packet\":\n\t\toption.PacketAddr = true\n\tcase \"xudp\":\n\t\toption.XUDP = true\n\t}\n\tif option.XUDP {\n\t\toption.PacketAddr = false\n\t}\n\n\tv := &Vmess{\n\t\tBase: NewBase(BaseOption{\n\t\t\tName:         option.Name,\n\t\t\tAddr:         net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),\n\t\t\tType:         C.Vmess,\n\t\t\tProviderName: option.ProviderName,\n\t\t\tUDP:          option.UDP,\n\t\t\tXUDP:         option.XUDP,\n\t\t\tTFO:          option.TFO,\n\t\t\tMPTCP:        option.MPTCP,\n\t\t\tInterface:    option.Interface,\n\t\t\tRoutingMark:  option.RoutingMark,\n\t\t\tPrefer:       option.IPVersion,\n\t\t}),\n\t\tclient: client,\n\t\toption: &option,\n\t}\n\tv.dialer = option.NewDialer(v.DialOptions())\n\n\tv.realityConfig, err = v.option.RealityOpts.Parse()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tv.echConfig, err = v.option.ECHOpts.Parse()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tswitch option.Network {\n\tcase \"h2\":\n\t\tif len(option.HTTP2Opts.Host) == 0 {\n\t\t\toption.HTTP2Opts.Host = append(option.HTTP2Opts.Host, \"www.example.com\")\n\t\t}\n\tcase \"grpc\":\n\t\tdialFn := func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\t\tc, err := v.dialer.DialContext(ctx, \"tcp\", v.addr)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"%s connect error: %s\", v.addr, err.Error())\n\t\t\t}\n\t\t\treturn c, nil\n\t\t}\n\n\t\tgunConfig := &gun.Config{\n\t\t\tServiceName:  option.GrpcOpts.GrpcServiceName,\n\t\t\tUserAgent:    option.GrpcOpts.GrpcUserAgent,\n\t\t\tHost:         option.ServerName,\n\t\t\tPingInterval: option.GrpcOpts.PingInterval,\n\t\t}\n\t\tif option.ServerName == \"\" {\n\t\t\tgunConfig.Host = v.addr\n\t\t}\n\t\tvar tlsConfig *mihomoVMess.TLSConfig\n\t\tif option.TLS {\n\t\t\ttlsConfig = &mihomoVMess.TLSConfig{\n\t\t\t\tHost:              option.ServerName,\n\t\t\t\tSkipCertVerify:    option.SkipCertVerify,\n\t\t\t\tFingerPrint:       option.Fingerprint,\n\t\t\t\tCertificate:       option.Certificate,\n\t\t\t\tPrivateKey:        option.PrivateKey,\n\t\t\t\tClientFingerprint: option.ClientFingerprint,\n\t\t\t\tNextProtos:        []string{\"h2\"},\n\t\t\t\tECH:               v.echConfig,\n\t\t\t\tReality:           v.realityConfig,\n\t\t\t}\n\t\t\tif option.ServerName == \"\" {\n\t\t\t\thost, _, _ := net.SplitHostPort(v.addr)\n\t\t\t\ttlsConfig.Host = host\n\t\t\t}\n\t\t}\n\n\t\tv.gunClient = gun.NewClient(\n\t\t\tfunc() *gun.Transport {\n\t\t\t\treturn gun.NewTransport(dialFn, tlsConfig, gunConfig)\n\t\t\t},\n\t\t\toption.GrpcOpts.MaxConnections,\n\t\t\toption.GrpcOpts.MinStreams,\n\t\t\toption.GrpcOpts.MaxStreams,\n\t\t)\n\t}\n\n\treturn v, nil\n}\n\ntype vmessPacketConn struct {\n\tnet.Conn\n\trAddr  net.Addr\n\taccess sync.Mutex\n}\n\n// WriteTo implments C.PacketConn.WriteTo\n// Since VMess doesn't support full cone NAT by design, we verify if addr matches uc.rAddr, and drop the packet if not.\nfunc (uc *vmessPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {\n\tallowedAddr := uc.rAddr\n\tdestAddr := addr\n\tif allowedAddr.String() != destAddr.String() {\n\t\treturn 0, ErrUDPRemoteAddrMismatch\n\t}\n\tuc.access.Lock()\n\tdefer uc.access.Unlock()\n\treturn uc.Conn.Write(b)\n}\n\nfunc (uc *vmessPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {\n\tn, err := uc.Conn.Read(b)\n\treturn n, uc.rAddr, err\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outbound/wireguard.go",
    "content": "package outbound\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/netip\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/atomic\"\n\t\"github.com/metacubex/mihomo/component/dialer\"\n\t\"github.com/metacubex/mihomo/component/proxydialer\"\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\t\"github.com/metacubex/mihomo/component/slowdown\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/dns\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\tamnezia \"github.com/metacubex/amneziawg-go/device\"\n\twireguard \"github.com/metacubex/sing-wireguard\"\n\t\"github.com/metacubex/wireguard-go/device\"\n\n\t\"github.com/metacubex/sing/common/debug\"\n\tE \"github.com/metacubex/sing/common/exceptions\"\n\tM \"github.com/metacubex/sing/common/metadata\"\n)\n\ntype wireguardGoDevice interface {\n\tClose()\n\tIpcSet(uapiConf string) error\n}\n\ntype WireGuard struct {\n\t*Base\n\tbind      *wireguard.ClientBind\n\tdevice    wireguardGoDevice\n\ttunDevice wireguard.Device\n\tresolver  resolver.Resolver\n\n\tinitOk        atomic.Bool\n\tinitMutex     sync.Mutex\n\tinitErr       error\n\toption        WireGuardOption\n\tconnectAddr   M.Socksaddr\n\tlocalPrefixes []netip.Prefix\n\n\tserverAddrMap   map[M.Socksaddr]netip.AddrPort\n\tserverAddrTime  atomic.TypedValue[time.Time]\n\tserverAddrMutex sync.Mutex\n}\n\ntype WireGuardOption struct {\n\tBasicOption\n\tWireGuardPeerOption\n\tName                string `proxy:\"name\"`\n\tIp                  string `proxy:\"ip,omitempty\"`\n\tIpv6                string `proxy:\"ipv6,omitempty\"`\n\tPrivateKey          string `proxy:\"private-key\"`\n\tWorkers             int    `proxy:\"workers,omitempty\"`\n\tMTU                 int    `proxy:\"mtu,omitempty\"`\n\tUDP                 bool   `proxy:\"udp,omitempty\"`\n\tPersistentKeepalive int    `proxy:\"persistent-keepalive,omitempty\"`\n\n\tAmneziaWGOption *AmneziaWGOption `proxy:\"amnezia-wg-option,omitempty\"`\n\n\tPeers []WireGuardPeerOption `proxy:\"peers,omitempty\"`\n\n\tRemoteDnsResolve bool     `proxy:\"remote-dns-resolve,omitempty\"`\n\tDns              []string `proxy:\"dns,omitempty\"`\n\n\tRefreshServerIPInterval int `proxy:\"refresh-server-ip-interval,omitempty\"`\n}\n\ntype WireGuardPeerOption struct {\n\tServer       string   `proxy:\"server,omitempty\"`\n\tPort         int      `proxy:\"port,omitempty\"`\n\tPublicKey    string   `proxy:\"public-key,omitempty\"`\n\tPreSharedKey string   `proxy:\"pre-shared-key,omitempty\"`\n\tReserved     []uint8  `proxy:\"reserved,omitempty\"`\n\tAllowedIPs   []string `proxy:\"allowed-ips,omitempty\"`\n}\n\ntype AmneziaWGOption struct {\n\tJC    int    `proxy:\"jc,omitempty\"`\n\tJMin  int    `proxy:\"jmin,omitempty\"`\n\tJMax  int    `proxy:\"jmax,omitempty\"`\n\tS1    int    `proxy:\"s1,omitempty\"`\n\tS2    int    `proxy:\"s2,omitempty\"`\n\tS3    int    `proxy:\"s3,omitempty\"`    // AmneziaWG v1.5 and v2\n\tS4    int    `proxy:\"s4,omitempty\"`    // AmneziaWG v1.5 and v2\n\tH1    string `proxy:\"h1,omitempty\"`    // In AmneziaWG v1.x, it was uint32, but our WeaklyTypedInput can handle this situation\n\tH2    string `proxy:\"h2,omitempty\"`    // In AmneziaWG v1.x, it was uint32, but our WeaklyTypedInput can handle this situation\n\tH3    string `proxy:\"h3,omitempty\"`    // In AmneziaWG v1.x, it was uint32, but our WeaklyTypedInput can handle this situation\n\tH4    string `proxy:\"h4,omitempty\"`    // In AmneziaWG v1.x, it was uint32, but our WeaklyTypedInput can handle this situation\n\tI1    string `proxy:\"i1,omitempty\"`    // AmneziaWG v1.5 and v2\n\tI2    string `proxy:\"i2,omitempty\"`    // AmneziaWG v1.5 and v2\n\tI3    string `proxy:\"i3,omitempty\"`    // AmneziaWG v1.5 and v2\n\tI4    string `proxy:\"i4,omitempty\"`    // AmneziaWG v1.5 and v2\n\tI5    string `proxy:\"i5,omitempty\"`    // AmneziaWG v1.5 and v2\n\tJ1    string `proxy:\"j1,omitempty\"`    // AmneziaWG v1.5 only (removed in v2)\n\tJ2    string `proxy:\"j2,omitempty\"`    // AmneziaWG v1.5 only (removed in v2)\n\tJ3    string `proxy:\"j3,omitempty\"`    // AmneziaWG v1.5 only (removed in v2)\n\tItime int64  `proxy:\"itime,omitempty\"` // AmneziaWG v1.5 only (removed in v2)\n}\n\ntype wgSingErrorHandler struct {\n\tname string\n}\n\nvar _ E.Handler = (*wgSingErrorHandler)(nil)\n\nfunc (w wgSingErrorHandler) NewError(ctx context.Context, err error) {\n\tif E.IsClosedOrCanceled(err) {\n\t\tlog.SingLogger.Debug(fmt.Sprintf(\"[WG](%s) connection closed: %s\", w.name, err))\n\t\treturn\n\t}\n\tlog.SingLogger.Error(fmt.Sprintf(\"[WG](%s) %s\", w.name, err))\n}\n\ntype wgNetDialer struct {\n\ttunDevice wireguard.Device\n}\n\nvar _ dialer.NetDialer = (*wgNetDialer)(nil)\n\nfunc (d wgNetDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {\n\treturn d.tunDevice.DialContext(ctx, network, M.ParseSocksaddr(address).Unwrap())\n}\n\nfunc (option WireGuardPeerOption) Addr() M.Socksaddr {\n\treturn M.ParseSocksaddrHostPort(option.Server, uint16(option.Port))\n}\n\nfunc (option WireGuardOption) Prefixes() ([]netip.Prefix, error) {\n\tlocalPrefixes := make([]netip.Prefix, 0, 2)\n\tif len(option.Ip) > 0 {\n\t\tif !strings.Contains(option.Ip, \"/\") {\n\t\t\toption.Ip = option.Ip + \"/32\"\n\t\t}\n\t\tif prefix, err := netip.ParsePrefix(option.Ip); err == nil {\n\t\t\tlocalPrefixes = append(localPrefixes, prefix)\n\t\t} else {\n\t\t\treturn nil, E.Cause(err, \"ip address parse error\")\n\t\t}\n\t}\n\tif len(option.Ipv6) > 0 {\n\t\tif !strings.Contains(option.Ipv6, \"/\") {\n\t\t\toption.Ipv6 = option.Ipv6 + \"/128\"\n\t\t}\n\t\tif prefix, err := netip.ParsePrefix(option.Ipv6); err == nil {\n\t\t\tlocalPrefixes = append(localPrefixes, prefix)\n\t\t} else {\n\t\t\treturn nil, E.Cause(err, \"ipv6 address parse error\")\n\t\t}\n\t}\n\tif len(localPrefixes) == 0 {\n\t\treturn nil, E.New(\"missing local address\")\n\t}\n\treturn localPrefixes, nil\n}\n\nfunc NewWireGuard(option WireGuardOption) (*WireGuard, error) {\n\toutbound := &WireGuard{\n\t\tBase: NewBase(BaseOption{\n\t\t\tName:         option.Name,\n\t\t\tAddr:         net.JoinHostPort(option.Server, strconv.Itoa(option.Port)),\n\t\t\tType:         C.WireGuard,\n\t\t\tProviderName: option.ProviderName,\n\t\t\tUDP:          option.UDP,\n\t\t\tInterface:    option.Interface,\n\t\t\tRoutingMark:  option.RoutingMark,\n\t\t\tPrefer:       option.IPVersion,\n\t\t}),\n\t}\n\toutbound.dialer = option.NewDialer(outbound.DialOptions())\n\tsingDialer := proxydialer.NewSingDialer(proxydialer.NewSlowDownDialer(outbound.dialer, slowdown.New()))\n\n\tvar reserved [3]uint8\n\tif len(option.Reserved) > 0 {\n\t\tif len(option.Reserved) != 3 {\n\t\t\treturn nil, E.New(\"invalid reserved value, required 3 bytes, got \", len(option.Reserved))\n\t\t}\n\t\tcopy(reserved[:], option.Reserved)\n\t}\n\tvar isConnect bool\n\tif len(option.Peers) < 2 {\n\t\tisConnect = true\n\t\tif len(option.Peers) == 1 {\n\t\t\toutbound.connectAddr = option.Peers[0].Addr()\n\t\t} else {\n\t\t\toutbound.connectAddr = option.Addr()\n\t\t}\n\t}\n\toutbound.bind = wireguard.NewClientBind(context.Background(), wgSingErrorHandler{outbound.Name()}, singDialer, isConnect, outbound.connectAddr.AddrPort(), reserved)\n\n\tvar err error\n\toutbound.localPrefixes, err = option.Prefixes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t{\n\t\tbytes, err := base64.StdEncoding.DecodeString(option.PrivateKey)\n\t\tif err != nil {\n\t\t\treturn nil, E.Cause(err, \"decode private key\")\n\t\t}\n\t\toption.PrivateKey = hex.EncodeToString(bytes)\n\t}\n\n\tif len(option.Peers) > 0 {\n\t\tfor i := range option.Peers {\n\t\t\tpeer := &option.Peers[i] // we need modify option here\n\t\t\tbytes, err := base64.StdEncoding.DecodeString(peer.PublicKey)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, E.Cause(err, \"decode public key for peer \", i)\n\t\t\t}\n\t\t\tpeer.PublicKey = hex.EncodeToString(bytes)\n\n\t\t\tif peer.PreSharedKey != \"\" {\n\t\t\t\tbytes, err := base64.StdEncoding.DecodeString(peer.PreSharedKey)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, E.Cause(err, \"decode pre shared key for peer \", i)\n\t\t\t\t}\n\t\t\t\tpeer.PreSharedKey = hex.EncodeToString(bytes)\n\t\t\t}\n\n\t\t\tif len(peer.AllowedIPs) == 0 {\n\t\t\t\treturn nil, E.New(\"missing allowed_ips for peer \", i)\n\t\t\t}\n\n\t\t\tif len(peer.Reserved) > 0 {\n\t\t\t\tif len(peer.Reserved) != 3 {\n\t\t\t\t\treturn nil, E.New(\"invalid reserved value for peer \", i, \", required 3 bytes, got \", len(peer.Reserved))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\t{\n\t\t\tbytes, err := base64.StdEncoding.DecodeString(option.PublicKey)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, E.Cause(err, \"decode peer public key\")\n\t\t\t}\n\t\t\toption.PublicKey = hex.EncodeToString(bytes)\n\t\t}\n\t\tif option.PreSharedKey != \"\" {\n\t\t\tbytes, err := base64.StdEncoding.DecodeString(option.PreSharedKey)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, E.Cause(err, \"decode pre shared key\")\n\t\t\t}\n\t\t\toption.PreSharedKey = hex.EncodeToString(bytes)\n\t\t}\n\t}\n\toutbound.option = option\n\n\tmtu := option.MTU\n\tif mtu == 0 {\n\t\tmtu = 1408\n\t}\n\tif len(outbound.localPrefixes) == 0 {\n\t\treturn nil, E.New(\"missing local address\")\n\t}\n\toutbound.tunDevice, err = wireguard.NewStackDevice(outbound.localPrefixes, uint32(mtu))\n\tif err != nil {\n\t\treturn nil, E.Cause(err, \"create WireGuard device\")\n\t}\n\tlogger := &device.Logger{\n\t\tVerbosef: func(format string, args ...interface{}) {\n\t\t\tlog.SingLogger.Debug(fmt.Sprintf(\"[WG](%s) %s\", option.Name, fmt.Sprintf(format, args...)))\n\t\t},\n\t\tErrorf: func(format string, args ...interface{}) {\n\t\t\tlog.SingLogger.Error(fmt.Sprintf(\"[WG](%s) %s\", option.Name, fmt.Sprintf(format, args...)))\n\t\t},\n\t}\n\tif option.AmneziaWGOption != nil {\n\t\toutbound.bind.SetParseReserved(false) // AmneziaWG don't need parse reserved\n\t\toutbound.device = amnezia.NewDevice(outbound.tunDevice, outbound.bind, logger, option.Workers)\n\t} else {\n\t\toutbound.device = device.NewDevice(outbound.tunDevice, outbound.bind, logger, option.Workers)\n\t}\n\n\tvar has6 bool\n\tfor _, address := range outbound.localPrefixes {\n\t\tif !address.Addr().Unmap().Is4() {\n\t\t\thas6 = true\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif option.RemoteDnsResolve && len(option.Dns) > 0 {\n\t\tnss, err := dns.ParseNameServer(option.Dns)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfor i := range nss {\n\t\t\tnss[i].ProxyAdapter = outbound\n\t\t}\n\t\toutbound.resolver = dns.NewResolver(dns.Config{\n\t\t\tMain: nss,\n\t\t\tIPv6: has6,\n\t\t})\n\t}\n\n\treturn outbound, nil\n}\n\nfunc (w *WireGuard) resolve(ctx context.Context, address M.Socksaddr) (netip.AddrPort, error) {\n\tif address.Addr.IsValid() {\n\t\treturn address.AddrPort(), nil\n\t}\n\tudpAddr, err := resolveUDPAddr(ctx, \"udp\", address.String(), w.prefer)\n\tif err != nil {\n\t\treturn netip.AddrPort{}, err\n\t}\n\t// net.ResolveUDPAddr maybe return 4in6 address, so unmap at here\n\taddrPort := udpAddr.AddrPort()\n\treturn netip.AddrPortFrom(addrPort.Addr().Unmap(), addrPort.Port()), nil\n}\n\nfunc (w *WireGuard) init(ctx context.Context) error {\n\terr := w.init0(ctx)\n\tif err != nil {\n\t\treturn err\n\t}\n\tw.updateServerAddr(ctx)\n\treturn nil\n}\n\nfunc (w *WireGuard) init0(ctx context.Context) error {\n\tif w.initOk.Load() {\n\t\treturn nil\n\t}\n\tw.initMutex.Lock()\n\tdefer w.initMutex.Unlock()\n\t// double check like sync.Once\n\tif w.initOk.Load() {\n\t\treturn nil\n\t}\n\tif w.initErr != nil {\n\t\treturn w.initErr\n\t}\n\n\tw.bind.ResetReservedForEndpoint()\n\tw.serverAddrMap = make(map[M.Socksaddr]netip.AddrPort)\n\tipcConf, err := w.genIpcConf(ctx, false)\n\tif err != nil {\n\t\t// !!! do not set initErr here !!!\n\t\t// let us can retry domain resolve in next time\n\t\treturn err\n\t}\n\n\tif debug.Enabled {\n\t\tlog.SingLogger.Trace(fmt.Sprintf(\"[WG](%s) created wireguard ipc conf: \\n %s\", w.option.Name, ipcConf))\n\t}\n\terr = w.device.IpcSet(ipcConf)\n\tif err != nil {\n\t\tw.initErr = E.Cause(err, \"setup wireguard\")\n\t\treturn w.initErr\n\t}\n\tw.serverAddrTime.Store(time.Now())\n\n\terr = w.tunDevice.Start()\n\tif err != nil {\n\t\tw.initErr = err\n\t\treturn w.initErr\n\t}\n\n\tw.initOk.Store(true)\n\treturn nil\n}\n\nfunc (w *WireGuard) updateServerAddr(ctx context.Context) {\n\tif w.option.RefreshServerIPInterval != 0 && time.Since(w.serverAddrTime.Load()) > time.Second*time.Duration(w.option.RefreshServerIPInterval) {\n\t\tif w.serverAddrMutex.TryLock() {\n\t\t\tdefer w.serverAddrMutex.Unlock()\n\t\t\tipcConf, err := w.genIpcConf(ctx, true)\n\t\t\tif err != nil {\n\t\t\t\tlog.Warnln(\"[WG](%s)UpdateServerAddr failed to generate wireguard ipc conf: %s\", w.option.Name, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\terr = w.device.IpcSet(ipcConf)\n\t\t\tif err != nil {\n\t\t\t\tlog.Warnln(\"[WG](%s)UpdateServerAddr failed to update wireguard ipc conf: %s\", w.option.Name, err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tw.serverAddrTime.Store(time.Now())\n\t\t}\n\t}\n}\n\nfunc (w *WireGuard) genIpcConf(ctx context.Context, updateOnly bool) (string, error) {\n\tipcConf := \"\"\n\tif !updateOnly {\n\t\tipcConf += \"private_key=\" + w.option.PrivateKey + \"\\n\"\n\t\tif w.option.AmneziaWGOption != nil {\n\t\t\tif w.option.AmneziaWGOption.JC != 0 {\n\t\t\t\tipcConf += \"jc=\" + strconv.Itoa(w.option.AmneziaWGOption.JC) + \"\\n\"\n\t\t\t}\n\t\t\tif w.option.AmneziaWGOption.JMin != 0 {\n\t\t\t\tipcConf += \"jmin=\" + strconv.Itoa(w.option.AmneziaWGOption.JMin) + \"\\n\"\n\t\t\t}\n\t\t\tif w.option.AmneziaWGOption.JMax != 0 {\n\t\t\t\tipcConf += \"jmax=\" + strconv.Itoa(w.option.AmneziaWGOption.JMax) + \"\\n\"\n\t\t\t}\n\t\t\tif w.option.AmneziaWGOption.S1 != 0 {\n\t\t\t\tipcConf += \"s1=\" + strconv.Itoa(w.option.AmneziaWGOption.S1) + \"\\n\"\n\t\t\t}\n\t\t\tif w.option.AmneziaWGOption.S2 != 0 {\n\t\t\t\tipcConf += \"s2=\" + strconv.Itoa(w.option.AmneziaWGOption.S2) + \"\\n\"\n\t\t\t}\n\t\t\tif w.option.AmneziaWGOption.S3 != 0 {\n\t\t\t\tipcConf += \"s3=\" + strconv.Itoa(w.option.AmneziaWGOption.S3) + \"\\n\"\n\t\t\t}\n\t\t\tif w.option.AmneziaWGOption.S4 != 0 {\n\t\t\t\tipcConf += \"s4=\" + strconv.Itoa(w.option.AmneziaWGOption.S4) + \"\\n\"\n\t\t\t}\n\t\t\tif w.option.AmneziaWGOption.H1 != \"\" {\n\t\t\t\tipcConf += \"h1=\" + w.option.AmneziaWGOption.H1 + \"\\n\"\n\t\t\t}\n\t\t\tif w.option.AmneziaWGOption.H2 != \"\" {\n\t\t\t\tipcConf += \"h2=\" + w.option.AmneziaWGOption.H2 + \"\\n\"\n\t\t\t}\n\t\t\tif w.option.AmneziaWGOption.H3 != \"\" {\n\t\t\t\tipcConf += \"h3=\" + w.option.AmneziaWGOption.H3 + \"\\n\"\n\t\t\t}\n\t\t\tif w.option.AmneziaWGOption.H4 != \"\" {\n\t\t\t\tipcConf += \"h4=\" + w.option.AmneziaWGOption.H4 + \"\\n\"\n\t\t\t}\n\t\t\tif w.option.AmneziaWGOption.I1 != \"\" {\n\t\t\t\tipcConf += \"i1=\" + w.option.AmneziaWGOption.I1 + \"\\n\"\n\t\t\t}\n\t\t\tif w.option.AmneziaWGOption.I2 != \"\" {\n\t\t\t\tipcConf += \"i2=\" + w.option.AmneziaWGOption.I2 + \"\\n\"\n\t\t\t}\n\t\t\tif w.option.AmneziaWGOption.I3 != \"\" {\n\t\t\t\tipcConf += \"i3=\" + w.option.AmneziaWGOption.I3 + \"\\n\"\n\t\t\t}\n\t\t\tif w.option.AmneziaWGOption.I4 != \"\" {\n\t\t\t\tipcConf += \"i4=\" + w.option.AmneziaWGOption.I4 + \"\\n\"\n\t\t\t}\n\t\t\tif w.option.AmneziaWGOption.I5 != \"\" {\n\t\t\t\tipcConf += \"i5=\" + w.option.AmneziaWGOption.I5 + \"\\n\"\n\t\t\t}\n\t\t\tif w.option.AmneziaWGOption.J1 != \"\" {\n\t\t\t\tipcConf += \"j1=\" + w.option.AmneziaWGOption.J1 + \"\\n\"\n\t\t\t}\n\t\t\tif w.option.AmneziaWGOption.J2 != \"\" {\n\t\t\t\tipcConf += \"j2=\" + w.option.AmneziaWGOption.J2 + \"\\n\"\n\t\t\t}\n\t\t\tif w.option.AmneziaWGOption.J3 != \"\" {\n\t\t\t\tipcConf += \"j3=\" + w.option.AmneziaWGOption.J3 + \"\\n\"\n\t\t\t}\n\t\t\tif w.option.AmneziaWGOption.Itime != 0 {\n\t\t\t\tipcConf += \"itime=\" + strconv.FormatInt(int64(w.option.AmneziaWGOption.Itime), 10) + \"\\n\"\n\t\t\t}\n\t\t}\n\t}\n\tif len(w.option.Peers) > 0 {\n\t\tfor i, peer := range w.option.Peers {\n\t\t\tpeerAddr := peer.Addr()\n\t\t\tdestination, err := w.resolve(ctx, peerAddr)\n\t\t\tif err != nil {\n\t\t\t\treturn \"\", E.Cause(err, \"resolve endpoint domain for peer \", i)\n\t\t\t}\n\t\t\tif w.serverAddrMap[peerAddr] != destination {\n\t\t\t\tw.serverAddrMap[peerAddr] = destination\n\t\t\t} else if updateOnly {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif len(w.option.Peers) == 1 { // must call SetConnectAddr if isConnect == true\n\t\t\t\tw.bind.SetConnectAddr(destination)\n\t\t\t}\n\t\t\tipcConf += \"public_key=\" + peer.PublicKey + \"\\n\"\n\t\t\tif updateOnly {\n\t\t\t\tipcConf += \"update_only=true\\n\"\n\t\t\t}\n\t\t\tipcConf += \"endpoint=\" + destination.String() + \"\\n\"\n\t\t\tif len(peer.Reserved) > 0 {\n\t\t\t\tvar reserved [3]uint8\n\t\t\t\tcopy(reserved[:], w.option.Reserved)\n\t\t\t\tw.bind.SetReservedForEndpoint(destination, reserved)\n\t\t\t}\n\t\t\tif updateOnly {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif peer.PreSharedKey != \"\" {\n\t\t\t\tipcConf += \"preshared_key=\" + peer.PreSharedKey + \"\\n\"\n\t\t\t}\n\t\t\tfor _, allowedIP := range peer.AllowedIPs {\n\t\t\t\tipcConf += \"allowed_ip=\" + allowedIP + \"\\n\"\n\t\t\t}\n\t\t\tif w.option.PersistentKeepalive != 0 {\n\t\t\t\tipcConf += fmt.Sprintf(\"persistent_keepalive_interval=%d\\n\", w.option.PersistentKeepalive)\n\t\t\t}\n\t\t}\n\t} else {\n\t\tdestination, err := w.resolve(ctx, w.connectAddr)\n\t\tif err != nil {\n\t\t\treturn \"\", E.Cause(err, \"resolve endpoint domain\")\n\t\t}\n\t\tif w.serverAddrMap[w.connectAddr] != destination {\n\t\t\tw.serverAddrMap[w.connectAddr] = destination\n\t\t} else if updateOnly {\n\t\t\treturn \"\", nil\n\t\t}\n\t\tw.bind.SetConnectAddr(destination) // must call SetConnectAddr if isConnect == true\n\t\tipcConf += \"public_key=\" + w.option.PublicKey + \"\\n\"\n\t\tif updateOnly {\n\t\t\tipcConf += \"update_only=true\\n\"\n\t\t}\n\t\tipcConf += \"endpoint=\" + destination.String() + \"\\n\"\n\t\tif updateOnly {\n\t\t\treturn ipcConf, nil\n\t\t}\n\t\tif w.option.PreSharedKey != \"\" {\n\t\t\tipcConf += \"preshared_key=\" + w.option.PreSharedKey + \"\\n\"\n\t\t}\n\t\tvar has4, has6 bool\n\t\tfor _, address := range w.localPrefixes {\n\t\t\tif address.Addr().Is4() {\n\t\t\t\thas4 = true\n\t\t\t} else {\n\t\t\t\thas6 = true\n\t\t\t}\n\t\t}\n\t\tif has4 {\n\t\t\tipcConf += \"allowed_ip=0.0.0.0/0\\n\"\n\t\t}\n\t\tif has6 {\n\t\t\tipcConf += \"allowed_ip=::/0\\n\"\n\t\t}\n\n\t\tif w.option.PersistentKeepalive != 0 {\n\t\t\tipcConf += fmt.Sprintf(\"persistent_keepalive_interval=%d\\n\", w.option.PersistentKeepalive)\n\t\t}\n\t}\n\treturn ipcConf, nil\n}\n\n// Close implements C.ProxyAdapter\nfunc (w *WireGuard) Close() error {\n\tif w.device != nil {\n\t\tw.device.Close()\n\t}\n\treturn nil\n}\n\nfunc (w *WireGuard) DialContext(ctx context.Context, metadata *C.Metadata) (_ C.Conn, err error) {\n\tvar conn net.Conn\n\tif err = w.init(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\tif !metadata.Resolved() || w.resolver != nil {\n\t\tr := resolver.DefaultResolver\n\t\tif w.resolver != nil {\n\t\t\tr = w.resolver\n\t\t}\n\t\toptions := w.DialOptions()\n\t\toptions = append(options, dialer.WithResolver(r))\n\t\toptions = append(options, dialer.WithNetDialer(wgNetDialer{tunDevice: w.tunDevice}))\n\t\tconn, err = dialer.NewDialer(options...).DialContext(ctx, \"tcp\", metadata.RemoteAddress())\n\t} else {\n\t\tconn, err = w.tunDevice.DialContext(ctx, \"tcp\", M.SocksaddrFrom(metadata.DstIP, metadata.DstPort).Unwrap())\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif conn == nil {\n\t\treturn nil, E.New(\"conn is nil\")\n\t}\n\treturn NewConn(conn, w), nil\n}\n\nfunc (w *WireGuard) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (_ C.PacketConn, err error) {\n\tvar pc net.PacketConn\n\tif err = w.init(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\tif err = w.ResolveUDP(ctx, metadata); err != nil {\n\t\treturn nil, err\n\t}\n\tpc, err = w.tunDevice.ListenPacket(ctx, M.SocksaddrFrom(metadata.DstIP, metadata.DstPort).Unwrap())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif pc == nil {\n\t\treturn nil, E.New(\"packetConn is nil\")\n\t}\n\treturn newPacketConn(pc, w), nil\n}\n\nfunc (w *WireGuard) ResolveUDP(ctx context.Context, metadata *C.Metadata) error {\n\tif (!metadata.Resolved() || w.resolver != nil) && metadata.Host != \"\" {\n\t\tr := resolver.DefaultResolver\n\t\tif w.resolver != nil {\n\t\t\tr = w.resolver\n\t\t}\n\t\tip, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"can't resolve ip: %w\", err)\n\t\t}\n\t\tmetadata.DstIP = ip\n\t}\n\treturn nil\n}\n\n// ProxyInfo implements C.ProxyAdapter\nfunc (w *WireGuard) ProxyInfo() C.ProxyInfo {\n\tinfo := w.Base.ProxyInfo()\n\tinfo.DialerProxy = w.option.DialerProxy\n\treturn info\n}\n\n// IsL3Protocol implements C.ProxyAdapter\nfunc (w *WireGuard) IsL3Protocol(metadata *C.Metadata) bool {\n\treturn true\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outboundgroup/fallback.go",
    "content": "package outboundgroup\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/callback\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tP \"github.com/metacubex/mihomo/constant/provider\"\n)\n\ntype Fallback struct {\n\t*GroupBase\n\tdisableUDP     bool\n\ttestUrl        string\n\tselected       string\n\texpectedStatus string\n}\n\nfunc (f *Fallback) Now() string {\n\tproxy := f.findAliveProxy(false)\n\treturn proxy.Name()\n}\n\n// DialContext implements C.ProxyAdapter\nfunc (f *Fallback) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, error) {\n\tproxy := f.findAliveProxy(true)\n\tc, err := proxy.DialContext(ctx, metadata)\n\tif err == nil {\n\t\tc.AppendToChains(f)\n\t} else {\n\t\tf.onDialFailed(proxy.Type(), err, f.healthCheck)\n\t}\n\n\tif N.NeedHandshake(c) {\n\t\tc = callback.NewFirstWriteCallBackConn(c, func(err error) {\n\t\t\tif err == nil {\n\t\t\t\tf.onDialSuccess()\n\t\t\t} else {\n\t\t\t\tf.onDialFailed(proxy.Type(), err, f.healthCheck)\n\t\t\t}\n\t\t})\n\t}\n\n\treturn c, err\n}\n\n// ListenPacketContext implements C.ProxyAdapter\nfunc (f *Fallback) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (C.PacketConn, error) {\n\tproxy := f.findAliveProxy(true)\n\tpc, err := proxy.ListenPacketContext(ctx, metadata)\n\tif err == nil {\n\t\tpc.AppendToChains(f)\n\t}\n\n\treturn pc, err\n}\n\n// SupportUDP implements C.ProxyAdapter\nfunc (f *Fallback) SupportUDP() bool {\n\tif f.disableUDP {\n\t\treturn false\n\t}\n\n\tproxy := f.findAliveProxy(false)\n\treturn proxy.SupportUDP()\n}\n\n// IsL3Protocol implements C.ProxyAdapter\nfunc (f *Fallback) IsL3Protocol(metadata *C.Metadata) bool {\n\treturn f.findAliveProxy(false).IsL3Protocol(metadata)\n}\n\n// MarshalJSON implements C.ProxyAdapter\nfunc (f *Fallback) MarshalJSON() ([]byte, error) {\n\tall := []string{}\n\tfor _, proxy := range f.GetProxies(false) {\n\t\tall = append(all, proxy.Name())\n\t}\n\treturn json.Marshal(map[string]any{\n\t\t\"type\":           f.Type().String(),\n\t\t\"now\":            f.Now(),\n\t\t\"all\":            all,\n\t\t\"testUrl\":        f.testUrl,\n\t\t\"expectedStatus\": f.expectedStatus,\n\t\t\"fixed\":          f.selected,\n\t\t\"hidden\":         f.Hidden(),\n\t\t\"icon\":           f.Icon(),\n\t})\n}\n\n// Unwrap implements C.ProxyAdapter\nfunc (f *Fallback) Unwrap(metadata *C.Metadata, touch bool) C.Proxy {\n\tproxy := f.findAliveProxy(touch)\n\treturn proxy\n}\n\nfunc (f *Fallback) findAliveProxy(touch bool) C.Proxy {\n\tproxies := f.GetProxies(touch)\n\tfor _, proxy := range proxies {\n\t\tif len(f.selected) == 0 {\n\t\t\tif proxy.AliveForTestUrl(f.testUrl) {\n\t\t\t\treturn proxy\n\t\t\t}\n\t\t} else {\n\t\t\tif proxy.Name() == f.selected {\n\t\t\t\tif proxy.AliveForTestUrl(f.testUrl) {\n\t\t\t\t\treturn proxy\n\t\t\t\t} else {\n\t\t\t\t\tf.selected = \"\"\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn proxies[0]\n}\n\nfunc (f *Fallback) Set(name string) error {\n\tvar p C.Proxy\n\tfor _, proxy := range f.GetProxies(false) {\n\t\tif proxy.Name() == name {\n\t\t\tp = proxy\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif p == nil {\n\t\treturn errors.New(\"proxy not exist\")\n\t}\n\n\tf.selected = name\n\tif !p.AliveForTestUrl(f.testUrl) {\n\t\tctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(5000))\n\t\tdefer cancel()\n\t\texpectedStatus, _ := utils.NewUnsignedRanges[uint16](f.expectedStatus)\n\t\t_, _ = p.URLTest(ctx, f.testUrl, expectedStatus)\n\t}\n\n\treturn nil\n}\n\nfunc (f *Fallback) ForceSet(name string) {\n\tf.selected = name\n}\n\nfunc (f *Fallback) Providers() []P.ProxyProvider {\n\treturn f.providers\n}\n\nfunc (f *Fallback) Proxies() []C.Proxy {\n\treturn f.GetProxies(false)\n}\n\nfunc NewFallback(option *GroupCommonOption, providers []P.ProxyProvider) *Fallback {\n\treturn &Fallback{\n\t\tGroupBase: NewGroupBase(GroupBaseOption{\n\t\t\tName:           option.Name,\n\t\t\tType:           C.Fallback,\n\t\t\tHidden:         option.Hidden,\n\t\t\tIcon:           option.Icon,\n\t\t\tFilter:         option.Filter,\n\t\t\tExcludeFilter:  option.ExcludeFilter,\n\t\t\tExcludeType:    option.ExcludeType,\n\t\t\tTestTimeout:    option.TestTimeout,\n\t\t\tMaxFailedTimes: option.MaxFailedTimes,\n\t\t\tProviders:      providers,\n\t\t}),\n\t\tdisableUDP:     option.DisableUDP,\n\t\ttestUrl:        option.URL,\n\t\texpectedStatus: option.ExpectedStatus,\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outboundgroup/groupbase.go",
    "content": "package outboundgroup\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/adapter/outbound\"\n\t\"github.com/metacubex/mihomo/common/atomic\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tP \"github.com/metacubex/mihomo/constant/provider\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/tunnel\"\n\n\t\"github.com/dlclark/regexp2\"\n\t\"golang.org/x/exp/slices\"\n)\n\ntype GroupBase struct {\n\t*outbound.Base\n\thidden            bool\n\ticon              string\n\tfilterRegs        []*regexp2.Regexp\n\texcludeFilterRegs []*regexp2.Regexp\n\texcludeTypeArray  []string\n\tproviders         []P.ProxyProvider\n\tfailedTestMux     sync.Mutex\n\tfailedTimes       int\n\tfailedTime        time.Time\n\tfailedTesting     atomic.Bool\n\ttestTimeout       int\n\tmaxFailedTimes    int\n\n\t// for GetProxies\n\tgetProxiesMutex  sync.Mutex\n\tproviderVersions []uint32\n\tproviderProxies  []C.Proxy\n}\n\ntype GroupBaseOption struct {\n\tName           string\n\tType           C.AdapterType\n\tHidden         bool\n\tIcon           string\n\tFilter         string\n\tExcludeFilter  string\n\tExcludeType    string\n\tTestTimeout    int\n\tMaxFailedTimes int\n\tProviders      []P.ProxyProvider\n}\n\nfunc NewGroupBase(opt GroupBaseOption) *GroupBase {\n\tvar excludeTypeArray []string\n\tif opt.ExcludeType != \"\" {\n\t\texcludeTypeArray = strings.Split(opt.ExcludeType, \"|\")\n\t}\n\n\tvar excludeFilterRegs []*regexp2.Regexp\n\tif opt.ExcludeFilter != \"\" {\n\t\tfor _, excludeFilter := range strings.Split(opt.ExcludeFilter, \"`\") {\n\t\t\texcludeFilterReg := regexp2.MustCompile(excludeFilter, regexp2.None)\n\t\t\texcludeFilterRegs = append(excludeFilterRegs, excludeFilterReg)\n\t\t}\n\t}\n\n\tvar filterRegs []*regexp2.Regexp\n\tif opt.Filter != \"\" {\n\t\tfor _, filter := range strings.Split(opt.Filter, \"`\") {\n\t\t\tfilterReg := regexp2.MustCompile(filter, regexp2.None)\n\t\t\tfilterRegs = append(filterRegs, filterReg)\n\t\t}\n\t}\n\n\tgb := &GroupBase{\n\t\tBase:              outbound.NewBase(outbound.BaseOption{Name: opt.Name, Type: opt.Type}),\n\t\thidden:            opt.Hidden,\n\t\ticon:              opt.Icon,\n\t\tfilterRegs:        filterRegs,\n\t\texcludeFilterRegs: excludeFilterRegs,\n\t\texcludeTypeArray:  excludeTypeArray,\n\t\tproviders:         opt.Providers,\n\t\tfailedTesting:     atomic.NewBool(false),\n\t\ttestTimeout:       opt.TestTimeout,\n\t\tmaxFailedTimes:    opt.MaxFailedTimes,\n\t}\n\n\tif gb.testTimeout == 0 {\n\t\tgb.testTimeout = 5000\n\t}\n\tif gb.maxFailedTimes == 0 {\n\t\tgb.maxFailedTimes = 5\n\t}\n\n\treturn gb\n}\n\nfunc (gb *GroupBase) Hidden() bool {\n\treturn gb.hidden\n}\n\nfunc (gb *GroupBase) Icon() string {\n\treturn gb.icon\n}\n\nfunc (gb *GroupBase) Touch() {\n\tfor _, pd := range gb.providers {\n\t\tpd.Touch()\n\t}\n}\n\nfunc (gb *GroupBase) GetProxies(touch bool) []C.Proxy {\n\tproviderVersions := make([]uint32, len(gb.providers))\n\tfor i, pd := range gb.providers {\n\t\tif touch { // touch first\n\t\t\tpd.Touch()\n\t\t}\n\t\tproviderVersions[i] = pd.Version()\n\t}\n\n\t// thread safe\n\tgb.getProxiesMutex.Lock()\n\tdefer gb.getProxiesMutex.Unlock()\n\n\t// return the cached proxies if version not changed\n\tif slices.Equal(providerVersions, gb.providerVersions) {\n\t\treturn gb.providerProxies\n\t}\n\n\tvar proxies []C.Proxy\n\tif len(gb.filterRegs) == 0 {\n\t\tfor _, pd := range gb.providers {\n\t\t\tproxies = append(proxies, pd.Proxies()...)\n\t\t}\n\t} else {\n\t\tfor _, pd := range gb.providers {\n\t\t\tif pd.VehicleType() == P.Compatible { // compatible provider unneeded filter\n\t\t\t\tproxies = append(proxies, pd.Proxies()...)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tvar newProxies []C.Proxy\n\t\t\tproxiesSet := map[string]struct{}{}\n\t\t\tfor _, filterReg := range gb.filterRegs {\n\t\t\t\tfor _, p := range pd.Proxies() {\n\t\t\t\t\tname := p.Name()\n\t\t\t\t\tif mat, _ := filterReg.MatchString(name); mat {\n\t\t\t\t\t\tif _, ok := proxiesSet[name]; !ok {\n\t\t\t\t\t\t\tproxiesSet[name] = struct{}{}\n\t\t\t\t\t\t\tnewProxies = append(newProxies, p)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tproxies = append(proxies, newProxies...)\n\t\t}\n\t}\n\n\t// Multiple filers means that proxies are sorted in the order in which the filers appear.\n\t// Although the filter has been performed once in the previous process,\n\t// when there are multiple providers, the array needs to be reordered as a whole.\n\tif len(gb.providers) > 1 && len(gb.filterRegs) > 1 {\n\t\tvar newProxies []C.Proxy\n\t\tproxiesSet := map[string]struct{}{}\n\t\tfor _, filterReg := range gb.filterRegs {\n\t\t\tfor _, p := range proxies {\n\t\t\t\tname := p.Name()\n\t\t\t\tif mat, _ := filterReg.MatchString(name); mat {\n\t\t\t\t\tif _, ok := proxiesSet[name]; !ok {\n\t\t\t\t\t\tproxiesSet[name] = struct{}{}\n\t\t\t\t\t\tnewProxies = append(newProxies, p)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tfor _, p := range proxies { // add not matched proxies at the end\n\t\t\tname := p.Name()\n\t\t\tif _, ok := proxiesSet[name]; !ok {\n\t\t\t\tproxiesSet[name] = struct{}{}\n\t\t\t\tnewProxies = append(newProxies, p)\n\t\t\t}\n\t\t}\n\t\tproxies = newProxies\n\t}\n\n\tif len(gb.excludeFilterRegs) > 0 {\n\t\tvar newProxies []C.Proxy\n\tLOOP1:\n\t\tfor _, p := range proxies {\n\t\t\tname := p.Name()\n\t\t\tfor _, excludeFilterReg := range gb.excludeFilterRegs {\n\t\t\t\tif mat, _ := excludeFilterReg.MatchString(name); mat {\n\t\t\t\t\tcontinue LOOP1\n\t\t\t\t}\n\t\t\t}\n\t\t\tnewProxies = append(newProxies, p)\n\t\t}\n\t\tproxies = newProxies\n\t}\n\n\tif gb.excludeTypeArray != nil {\n\t\tvar newProxies []C.Proxy\n\tLOOP2:\n\t\tfor _, p := range proxies {\n\t\t\tmType := p.Type().String()\n\t\t\tfor _, excludeType := range gb.excludeTypeArray {\n\t\t\t\tif strings.EqualFold(mType, excludeType) {\n\t\t\t\t\tcontinue LOOP2\n\t\t\t\t}\n\t\t\t}\n\t\t\tnewProxies = append(newProxies, p)\n\t\t}\n\t\tproxies = newProxies\n\t}\n\n\tif len(proxies) == 0 {\n\t\treturn []C.Proxy{tunnel.Proxies()[\"COMPATIBLE\"]}\n\t}\n\n\t// only cache when proxies not empty\n\tgb.providerVersions = providerVersions\n\tgb.providerProxies = proxies\n\n\treturn proxies\n}\n\nfunc (gb *GroupBase) URLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16]) (map[string]uint16, error) {\n\tvar wg sync.WaitGroup\n\tvar lock sync.Mutex\n\tmp := map[string]uint16{}\n\tproxies := gb.GetProxies(false)\n\tfor _, proxy := range proxies {\n\t\tproxy := proxy\n\t\twg.Add(1)\n\t\tgo func() {\n\t\t\tdelay, err := proxy.URLTest(ctx, url, expectedStatus)\n\t\t\tif err == nil {\n\t\t\t\tlock.Lock()\n\t\t\t\tmp[proxy.Name()] = delay\n\t\t\t\tlock.Unlock()\n\t\t\t}\n\n\t\t\twg.Done()\n\t\t}()\n\t}\n\twg.Wait()\n\n\tif len(mp) == 0 {\n\t\treturn mp, fmt.Errorf(\"get delay: all proxies timeout\")\n\t} else {\n\t\treturn mp, nil\n\t}\n}\n\nfunc (gb *GroupBase) onDialFailed(adapterType C.AdapterType, err error, fn func()) {\n\tif adapterType == C.Direct || adapterType == C.Compatible || adapterType == C.Reject || adapterType == C.Pass || adapterType == C.RejectDrop {\n\t\treturn\n\t}\n\n\tif errors.Is(err, C.ErrNotSupport) {\n\t\treturn\n\t}\n\n\tgo func() {\n\t\tif strings.Contains(err.Error(), \"connection refused\") {\n\t\t\tfn()\n\t\t\treturn\n\t\t}\n\n\t\tgb.failedTestMux.Lock()\n\t\tdefer gb.failedTestMux.Unlock()\n\n\t\tgb.failedTimes++\n\t\tif gb.failedTimes == 1 {\n\t\t\tlog.Debugln(\"ProxyGroup: %s first failed\", gb.Name())\n\t\t\tgb.failedTime = time.Now()\n\t\t} else {\n\t\t\tif time.Since(gb.failedTime) > time.Duration(gb.testTimeout)*time.Millisecond {\n\t\t\t\tgb.failedTimes = 0\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tlog.Debugln(\"ProxyGroup: %s failed count: %d\", gb.Name(), gb.failedTimes)\n\t\t\tif gb.failedTimes >= gb.maxFailedTimes {\n\t\t\t\tlog.Warnln(\"because %s failed multiple times, activate health check\", gb.Name())\n\t\t\t\tfn()\n\t\t\t}\n\t\t}\n\t}()\n}\n\nfunc (gb *GroupBase) healthCheck() {\n\tif gb.failedTesting.Load() {\n\t\treturn\n\t}\n\n\tgb.failedTesting.Store(true)\n\twg := sync.WaitGroup{}\n\tfor _, proxyProvider := range gb.providers {\n\t\twg.Add(1)\n\t\tproxyProvider := proxyProvider\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tproxyProvider.HealthCheck()\n\t\t}()\n\t}\n\n\twg.Wait()\n\tgb.failedTesting.Store(false)\n\tgb.failedTimes = 0\n}\n\nfunc (gb *GroupBase) onDialSuccess() {\n\tif !gb.failedTesting.Load() {\n\t\tgb.failedTimes = 0\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outboundgroup/loadbalance.go",
    "content": "package outboundgroup\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/callback\"\n\t\"github.com/metacubex/mihomo/common/lru\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tP \"github.com/metacubex/mihomo/constant/provider\"\n\n\t\"golang.org/x/net/publicsuffix\"\n)\n\ntype strategyFn = func(proxies []C.Proxy, metadata *C.Metadata, touch bool) C.Proxy\n\ntype LoadBalance struct {\n\t*GroupBase\n\tdisableUDP     bool\n\tstrategyFn     strategyFn\n\ttestUrl        string\n\texpectedStatus string\n}\n\nvar errStrategy = errors.New(\"unsupported strategy\")\n\nfunc parseStrategy(config map[string]any) string {\n\tif strategy, ok := config[\"strategy\"].(string); ok {\n\t\treturn strategy\n\t}\n\treturn \"consistent-hashing\"\n}\n\nfunc getKey(metadata *C.Metadata) string {\n\tif metadata == nil {\n\t\treturn \"\"\n\t}\n\n\tif metadata.Host != \"\" {\n\t\t// ip host\n\t\tif ip := net.ParseIP(metadata.Host); ip != nil {\n\t\t\treturn metadata.Host\n\t\t}\n\n\t\tif etld, err := publicsuffix.EffectiveTLDPlusOne(metadata.Host); err == nil {\n\t\t\treturn etld\n\t\t}\n\t}\n\n\tif !metadata.DstIP.IsValid() {\n\t\treturn \"\"\n\t}\n\n\treturn metadata.DstIP.String()\n}\n\nfunc getKeyWithSrcAndDst(metadata *C.Metadata) string {\n\tdst := getKey(metadata)\n\tsrc := \"\"\n\tif metadata != nil {\n\t\tsrc = metadata.SrcIP.String()\n\t}\n\n\treturn fmt.Sprintf(\"%s%s\", src, dst)\n}\n\nfunc jumpHash(key uint64, buckets int32) int32 {\n\tvar b, j int64\n\n\tfor j < int64(buckets) {\n\t\tb = j\n\t\tkey = key*2862933555777941757 + 1\n\t\tj = int64(float64(b+1) * (float64(int64(1)<<31) / float64((key>>33)+1)))\n\t}\n\n\treturn int32(b)\n}\n\n// DialContext implements C.ProxyAdapter\nfunc (lb *LoadBalance) DialContext(ctx context.Context, metadata *C.Metadata) (c C.Conn, err error) {\n\tproxy := lb.Unwrap(metadata, true)\n\tc, err = proxy.DialContext(ctx, metadata)\n\n\tif err == nil {\n\t\tc.AppendToChains(lb)\n\t} else {\n\t\tlb.onDialFailed(proxy.Type(), err, lb.healthCheck)\n\t}\n\n\tif N.NeedHandshake(c) {\n\t\tc = callback.NewFirstWriteCallBackConn(c, func(err error) {\n\t\t\tif err == nil {\n\t\t\t\tlb.onDialSuccess()\n\t\t\t} else {\n\t\t\t\tlb.onDialFailed(proxy.Type(), err, lb.healthCheck)\n\t\t\t}\n\t\t})\n\t}\n\n\treturn\n}\n\n// ListenPacketContext implements C.ProxyAdapter\nfunc (lb *LoadBalance) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (pc C.PacketConn, err error) {\n\tdefer func() {\n\t\tif err == nil {\n\t\t\tpc.AppendToChains(lb)\n\t\t}\n\t}()\n\n\tproxy := lb.Unwrap(metadata, true)\n\treturn proxy.ListenPacketContext(ctx, metadata)\n}\n\n// SupportUDP implements C.ProxyAdapter\nfunc (lb *LoadBalance) SupportUDP() bool {\n\treturn !lb.disableUDP\n}\n\n// IsL3Protocol implements C.ProxyAdapter\nfunc (lb *LoadBalance) IsL3Protocol(metadata *C.Metadata) bool {\n\treturn lb.Unwrap(metadata, false).IsL3Protocol(metadata)\n}\n\nfunc strategyRoundRobin(url string) strategyFn {\n\tidx := 0\n\tidxMutex := sync.Mutex{}\n\treturn func(proxies []C.Proxy, metadata *C.Metadata, touch bool) C.Proxy {\n\t\tidxMutex.Lock()\n\t\tdefer idxMutex.Unlock()\n\n\t\ti := 0\n\t\tlength := len(proxies)\n\n\t\tif touch {\n\t\t\tdefer func() {\n\t\t\t\tidx = (idx + i) % length\n\t\t\t}()\n\t\t}\n\n\t\tfor ; i < length; i++ {\n\t\t\tid := (idx + i) % length\n\t\t\tproxy := proxies[id]\n\t\t\tif proxy.AliveForTestUrl(url) {\n\t\t\t\ti++\n\t\t\t\treturn proxy\n\t\t\t}\n\t\t}\n\n\t\treturn proxies[0]\n\t}\n}\n\nfunc strategyConsistentHashing(url string) strategyFn {\n\tmaxRetry := 5\n\treturn func(proxies []C.Proxy, metadata *C.Metadata, touch bool) C.Proxy {\n\t\tkey := utils.MapHash(getKey(metadata))\n\t\tbuckets := int32(len(proxies))\n\t\tfor i := 0; i < maxRetry; i, key = i+1, key+1 {\n\t\t\tidx := jumpHash(key, buckets)\n\t\t\tproxy := proxies[idx]\n\t\t\tif proxy.AliveForTestUrl(url) {\n\t\t\t\treturn proxy\n\t\t\t}\n\t\t}\n\n\t\t// when availability is poor, traverse the entire list to get the available nodes\n\t\tfor _, proxy := range proxies {\n\t\t\tif proxy.AliveForTestUrl(url) {\n\t\t\t\treturn proxy\n\t\t\t}\n\t\t}\n\n\t\treturn proxies[0]\n\t}\n}\n\nfunc strategyStickySessions(url string) strategyFn {\n\tttl := time.Minute * 10\n\tmaxRetry := 5\n\tlruCache := lru.New[uint64, int](\n\t\tlru.WithAge[uint64, int](int64(ttl.Seconds())),\n\t\tlru.WithSize[uint64, int](1000))\n\treturn func(proxies []C.Proxy, metadata *C.Metadata, touch bool) C.Proxy {\n\t\tkey := utils.MapHash(getKeyWithSrcAndDst(metadata))\n\t\tlength := len(proxies)\n\t\tidx, has := lruCache.Get(key)\n\t\tif !has || idx >= length {\n\t\t\tidx = int(jumpHash(key+uint64(time.Now().UnixNano()), int32(length)))\n\t\t}\n\n\t\tnowIdx := idx\n\t\tfor i := 1; i < maxRetry; i++ {\n\t\t\tproxy := proxies[nowIdx]\n\t\t\tif proxy.AliveForTestUrl(url) {\n\t\t\t\tif !has || nowIdx != idx {\n\t\t\t\t\tlruCache.Set(key, nowIdx)\n\t\t\t\t}\n\n\t\t\t\treturn proxy\n\t\t\t} else {\n\t\t\t\tnowIdx = int(jumpHash(key+uint64(time.Now().UnixNano()), int32(length)))\n\t\t\t}\n\t\t}\n\n\t\tlruCache.Set(key, 0)\n\t\treturn proxies[0]\n\t}\n}\n\n// Unwrap implements C.ProxyAdapter\nfunc (lb *LoadBalance) Unwrap(metadata *C.Metadata, touch bool) C.Proxy {\n\tproxies := lb.GetProxies(touch)\n\treturn lb.strategyFn(proxies, metadata, touch)\n}\n\n// MarshalJSON implements C.ProxyAdapter\nfunc (lb *LoadBalance) MarshalJSON() ([]byte, error) {\n\tvar all []string\n\tfor _, proxy := range lb.GetProxies(false) {\n\t\tall = append(all, proxy.Name())\n\t}\n\treturn json.Marshal(map[string]any{\n\t\t\"type\":           lb.Type().String(),\n\t\t\"all\":            all,\n\t\t\"testUrl\":        lb.testUrl,\n\t\t\"expectedStatus\": lb.expectedStatus,\n\t\t\"hidden\":         lb.Hidden(),\n\t\t\"icon\":           lb.Icon(),\n\t})\n}\n\nfunc (lb *LoadBalance) Providers() []P.ProxyProvider {\n\treturn lb.providers\n}\n\nfunc (lb *LoadBalance) Proxies() []C.Proxy {\n\treturn lb.GetProxies(false)\n}\n\nfunc (lb *LoadBalance) Now() string {\n\treturn \"\"\n}\n\nfunc NewLoadBalance(option *GroupCommonOption, providers []P.ProxyProvider, strategy string) (lb *LoadBalance, err error) {\n\tvar strategyFn strategyFn\n\tswitch strategy {\n\tcase \"consistent-hashing\":\n\t\tstrategyFn = strategyConsistentHashing(option.URL)\n\tcase \"round-robin\":\n\t\tstrategyFn = strategyRoundRobin(option.URL)\n\tcase \"sticky-sessions\":\n\t\tstrategyFn = strategyStickySessions(option.URL)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"%w: %s\", errStrategy, strategy)\n\t}\n\treturn &LoadBalance{\n\t\tGroupBase: NewGroupBase(GroupBaseOption{\n\t\t\tName:           option.Name,\n\t\t\tType:           C.LoadBalance,\n\t\t\tHidden:         option.Hidden,\n\t\t\tIcon:           option.Icon,\n\t\t\tFilter:         option.Filter,\n\t\t\tExcludeFilter:  option.ExcludeFilter,\n\t\t\tExcludeType:    option.ExcludeType,\n\t\t\tTestTimeout:    option.TestTimeout,\n\t\t\tMaxFailedTimes: option.MaxFailedTimes,\n\t\t\tProviders:      providers,\n\t\t}),\n\t\tstrategyFn:     strategyFn,\n\t\tdisableUDP:     option.DisableUDP,\n\t\ttestUrl:        option.URL,\n\t\texpectedStatus: option.ExpectedStatus,\n\t}, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outboundgroup/parser.go",
    "content": "package outboundgroup\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/dlclark/regexp2\"\n\n\t\"github.com/metacubex/mihomo/adapter/provider\"\n\t\"github.com/metacubex/mihomo/common/structure\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tP \"github.com/metacubex/mihomo/constant/provider\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\nvar (\n\terrFormat            = errors.New(\"format error\")\n\terrType              = errors.New(\"unsupported type\")\n\terrMissProxy         = errors.New(\"`use` or `proxies` missing\")\n\terrDuplicateProvider = errors.New(\"duplicate provider name\")\n)\n\ntype GroupCommonOption struct {\n\tName                string   `group:\"name\"`\n\tType                string   `group:\"type\"`\n\tProxies             []string `group:\"proxies,omitempty\"`\n\tUse                 []string `group:\"use,omitempty\"`\n\tURL                 string   `group:\"url,omitempty\"`\n\tInterval            int      `group:\"interval,omitempty\"`\n\tTestTimeout         int      `group:\"timeout,omitempty\"`\n\tMaxFailedTimes      int      `group:\"max-failed-times,omitempty\"`\n\tLazy                bool     `group:\"lazy,omitempty\"`\n\tDisableUDP          bool     `group:\"disable-udp,omitempty\"`\n\tFilter              string   `group:\"filter,omitempty\"`\n\tExcludeFilter       string   `group:\"exclude-filter,omitempty\"`\n\tExcludeType         string   `group:\"exclude-type,omitempty\"`\n\tExpectedStatus      string   `group:\"expected-status,omitempty\"`\n\tIncludeAll          bool     `group:\"include-all,omitempty\"`\n\tIncludeAllProxies   bool     `group:\"include-all-proxies,omitempty\"`\n\tIncludeAllProviders bool     `group:\"include-all-providers,omitempty\"`\n\tHidden              bool     `group:\"hidden,omitempty\"`\n\tIcon                string   `group:\"icon,omitempty\"`\n}\n\nfunc ParseProxyGroup(config map[string]any, proxyMap map[string]C.Proxy, providersMap map[string]P.ProxyProvider, AllProxies []string, AllProviders []string) (C.ProxyAdapter, error) {\n\tdecoder := structure.NewDecoder(structure.Option{TagName: \"group\", WeaklyTypedInput: true})\n\n\tgroupOption := &GroupCommonOption{\n\t\tLazy: true,\n\t}\n\tif err := decoder.Decode(config, groupOption); err != nil {\n\t\treturn nil, errFormat\n\t}\n\n\tif groupOption.Type == \"\" || groupOption.Name == \"\" {\n\t\treturn nil, errFormat\n\t}\n\n\tif _, ok := config[\"routing-mark\"]; ok {\n\t\tlog.Errorln(\"The group [%s] with routing-mark configuration was removed, please set it directly on the proxy instead\", groupOption.Name)\n\t}\n\tif _, ok := config[\"interface-name\"]; ok {\n\t\tlog.Errorln(\"The group [%s] with interface-name configuration was removed, please set it directly on the proxy instead\", groupOption.Name)\n\t}\n\tif _, ok := config[\"dialer-proxy\"]; ok {\n\t\tlog.Errorln(\"The group [%s] with dialer-proxy configuration is not allowed, please set it directly on the proxy instead\", groupOption.Name)\n\t}\n\n\tgroupName := groupOption.Name\n\n\tproviders := []P.ProxyProvider{}\n\n\tif groupOption.IncludeAll {\n\t\tgroupOption.IncludeAllProviders = true\n\t\tgroupOption.IncludeAllProxies = true\n\t}\n\n\tif groupOption.IncludeAllProviders {\n\t\tgroupOption.Use = AllProviders\n\t}\n\tif groupOption.IncludeAllProxies {\n\t\tif groupOption.Filter != \"\" {\n\t\t\tvar filterRegs []*regexp2.Regexp\n\t\t\tfor _, filter := range strings.Split(groupOption.Filter, \"`\") {\n\t\t\t\tfilterReg := regexp2.MustCompile(filter, regexp2.None)\n\t\t\t\tfilterRegs = append(filterRegs, filterReg)\n\t\t\t}\n\t\t\tfor _, p := range AllProxies {\n\t\t\t\tfor _, filterReg := range filterRegs {\n\t\t\t\t\tif mat, _ := filterReg.MatchString(p); mat {\n\t\t\t\t\t\tgroupOption.Proxies = append(groupOption.Proxies, p)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tgroupOption.Proxies = append(groupOption.Proxies, AllProxies...)\n\t\t}\n\t\tif len(groupOption.Proxies) == 0 && len(groupOption.Use) == 0 {\n\t\t\tgroupOption.Proxies = []string{\"COMPATIBLE\"}\n\t\t}\n\t}\n\n\tif len(groupOption.Proxies) == 0 && len(groupOption.Use) == 0 {\n\t\treturn nil, fmt.Errorf(\"%s: %w\", groupName, errMissProxy)\n\t}\n\n\texpectedStatus, err := utils.NewUnsignedRanges[uint16](groupOption.ExpectedStatus)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s: %w\", groupName, err)\n\t}\n\n\tstatus := strings.TrimSpace(groupOption.ExpectedStatus)\n\tif status == \"\" {\n\t\tstatus = \"*\"\n\t}\n\tgroupOption.ExpectedStatus = status\n\n\tif len(groupOption.Use) != 0 {\n\t\tPDs, err := getProviders(providersMap, groupOption.Use)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%s: %w\", groupName, err)\n\t\t}\n\n\t\t// if test URL is empty, use the first health check URL of providers\n\t\tif groupOption.URL == \"\" {\n\t\t\tfor _, pd := range PDs {\n\t\t\t\tif pd.HealthCheckURL() != \"\" {\n\t\t\t\t\tgroupOption.URL = pd.HealthCheckURL()\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif groupOption.URL == \"\" {\n\t\t\t\tgroupOption.URL = C.DefaultTestURL\n\t\t\t}\n\t\t} else {\n\t\t\taddTestUrlToProviders(PDs, groupOption.URL, expectedStatus, groupOption.Filter, uint(groupOption.Interval))\n\t\t}\n\t\tproviders = append(providers, PDs...)\n\t}\n\n\tif len(groupOption.Proxies) != 0 {\n\t\tps, err := getProxies(proxyMap, groupOption.Proxies)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%s: %w\", groupName, err)\n\t\t}\n\n\t\tif _, ok := providersMap[groupName]; ok {\n\t\t\treturn nil, fmt.Errorf(\"%s: %w\", groupName, errDuplicateProvider)\n\t\t}\n\n\t\tif groupOption.URL == \"\" {\n\t\t\tgroupOption.URL = C.DefaultTestURL\n\t\t}\n\n\t\t// select don't need auto health check\n\t\tif groupOption.Type != \"select\" && groupOption.Type != \"relay\" {\n\t\t\tif groupOption.Interval == 0 {\n\t\t\t\tgroupOption.Interval = 300\n\t\t\t}\n\t\t}\n\n\t\thc := provider.NewHealthCheck(ps, groupOption.URL, uint(groupOption.TestTimeout), uint(groupOption.Interval), groupOption.Lazy, expectedStatus)\n\n\t\tpd, err := provider.NewCompatibleProvider(groupName, ps, hc)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"%s: %w\", groupName, err)\n\t\t}\n\n\t\tproviders = append([]P.ProxyProvider{pd}, providers...)\n\t\tprovidersMap[groupName] = pd\n\t}\n\n\tvar group C.ProxyAdapter\n\tswitch groupOption.Type {\n\tcase \"url-test\":\n\t\topts := parseURLTestOption(config)\n\t\tgroup = NewURLTest(groupOption, providers, opts...)\n\tcase \"select\":\n\t\tgroup = NewSelector(groupOption, providers)\n\tcase \"fallback\":\n\t\tgroup = NewFallback(groupOption, providers)\n\tcase \"load-balance\":\n\t\tstrategy := parseStrategy(config)\n\t\treturn NewLoadBalance(groupOption, providers, strategy)\n\tcase \"relay\":\n\t\treturn nil, fmt.Errorf(\"%w: The group [%s] with relay type was removed, please using dialer-proxy instead\", errType, groupName)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"%w: %s\", errType, groupOption.Type)\n\t}\n\n\treturn group, nil\n}\n\nfunc getProxies(mapping map[string]C.Proxy, list []string) ([]C.Proxy, error) {\n\tvar ps []C.Proxy\n\tfor _, name := range list {\n\t\tp, ok := mapping[name]\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"'%s' not found\", name)\n\t\t}\n\t\tps = append(ps, p)\n\t}\n\treturn ps, nil\n}\n\nfunc getProviders(mapping map[string]P.ProxyProvider, list []string) ([]P.ProxyProvider, error) {\n\tvar ps []P.ProxyProvider\n\tfor _, name := range list {\n\t\tp, ok := mapping[name]\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"'%s' not found\", name)\n\t\t}\n\n\t\tif p.VehicleType() == P.Compatible {\n\t\t\treturn nil, fmt.Errorf(\"proxy group %s can't contains in `use`\", name)\n\t\t}\n\t\tps = append(ps, p)\n\t}\n\treturn ps, nil\n}\n\nfunc addTestUrlToProviders(providers []P.ProxyProvider, url string, expectedStatus utils.IntRanges[uint16], filter string, interval uint) {\n\tif len(providers) == 0 || len(url) == 0 {\n\t\treturn\n\t}\n\n\tfor _, pd := range providers {\n\t\tpd.RegisterHealthCheckTask(url, expectedStatus, filter, interval)\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outboundgroup/selector.go",
    "content": "package outboundgroup\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\tP \"github.com/metacubex/mihomo/constant/provider\"\n)\n\ntype Selector struct {\n\t*GroupBase\n\tdisableUDP bool\n\tselected   string\n\ttestUrl    string\n}\n\n// DialContext implements C.ProxyAdapter\nfunc (s *Selector) DialContext(ctx context.Context, metadata *C.Metadata) (C.Conn, error) {\n\tc, err := s.selectedProxy(true).DialContext(ctx, metadata)\n\tif err == nil {\n\t\tc.AppendToChains(s)\n\t}\n\treturn c, err\n}\n\n// ListenPacketContext implements C.ProxyAdapter\nfunc (s *Selector) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (C.PacketConn, error) {\n\tpc, err := s.selectedProxy(true).ListenPacketContext(ctx, metadata)\n\tif err == nil {\n\t\tpc.AppendToChains(s)\n\t}\n\treturn pc, err\n}\n\n// SupportUDP implements C.ProxyAdapter\nfunc (s *Selector) SupportUDP() bool {\n\tif s.disableUDP {\n\t\treturn false\n\t}\n\n\treturn s.selectedProxy(false).SupportUDP()\n}\n\n// IsL3Protocol implements C.ProxyAdapter\nfunc (s *Selector) IsL3Protocol(metadata *C.Metadata) bool {\n\treturn s.selectedProxy(false).IsL3Protocol(metadata)\n}\n\n// MarshalJSON implements C.ProxyAdapter\nfunc (s *Selector) MarshalJSON() ([]byte, error) {\n\tall := []string{}\n\tfor _, proxy := range s.GetProxies(false) {\n\t\tall = append(all, proxy.Name())\n\t}\n\t// When testurl is the default value\n\t// do not append a value to ensure that the web dashboard follows the settings of the dashboard\n\tvar url string\n\tif s.testUrl != C.DefaultTestURL {\n\t\turl = s.testUrl\n\t}\n\n\treturn json.Marshal(map[string]any{\n\t\t\"type\":    s.Type().String(),\n\t\t\"now\":     s.Now(),\n\t\t\"all\":     all,\n\t\t\"testUrl\": url,\n\t\t\"hidden\":  s.Hidden(),\n\t\t\"icon\":    s.Icon(),\n\t})\n}\n\nfunc (s *Selector) Now() string {\n\treturn s.selectedProxy(false).Name()\n}\n\nfunc (s *Selector) Set(name string) error {\n\tfor _, proxy := range s.GetProxies(false) {\n\t\tif proxy.Name() == name {\n\t\t\ts.selected = name\n\t\t\treturn nil\n\t\t}\n\t}\n\n\treturn errors.New(\"proxy not exist\")\n}\n\nfunc (s *Selector) ForceSet(name string) {\n\ts.selected = name\n}\n\n// Unwrap implements C.ProxyAdapter\nfunc (s *Selector) Unwrap(metadata *C.Metadata, touch bool) C.Proxy {\n\treturn s.selectedProxy(touch)\n}\n\nfunc (s *Selector) selectedProxy(touch bool) C.Proxy {\n\tproxies := s.GetProxies(touch)\n\tfor _, proxy := range proxies {\n\t\tif proxy.Name() == s.selected {\n\t\t\treturn proxy\n\t\t}\n\t}\n\n\treturn proxies[0]\n}\n\nfunc (s *Selector) Providers() []P.ProxyProvider {\n\treturn s.providers\n}\n\nfunc (s *Selector) Proxies() []C.Proxy {\n\treturn s.GetProxies(false)\n}\n\nfunc NewSelector(option *GroupCommonOption, providers []P.ProxyProvider) *Selector {\n\treturn &Selector{\n\t\tGroupBase: NewGroupBase(GroupBaseOption{\n\t\t\tName:           option.Name,\n\t\t\tType:           C.Selector,\n\t\t\tHidden:         option.Hidden,\n\t\t\tIcon:           option.Icon,\n\t\t\tFilter:         option.Filter,\n\t\t\tExcludeFilter:  option.ExcludeFilter,\n\t\t\tExcludeType:    option.ExcludeType,\n\t\t\tTestTimeout:    option.TestTimeout,\n\t\t\tMaxFailedTimes: option.MaxFailedTimes,\n\t\t\tProviders:      providers,\n\t\t}),\n\t\tselected:   \"COMPATIBLE\",\n\t\tdisableUDP: option.DisableUDP,\n\t\ttestUrl:    option.URL,\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outboundgroup/urltest.go",
    "content": "package outboundgroup\n\nimport (\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/callback\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/singledo\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tP \"github.com/metacubex/mihomo/constant/provider\"\n)\n\ntype urlTestOption func(*URLTest)\n\nfunc urlTestWithTolerance(tolerance uint16) urlTestOption {\n\treturn func(u *URLTest) {\n\t\tu.tolerance = tolerance\n\t}\n}\n\ntype URLTest struct {\n\t*GroupBase\n\tselected       string\n\ttestUrl        string\n\texpectedStatus string\n\ttolerance      uint16\n\tdisableUDP     bool\n\tfastNode       C.Proxy\n\tfastSingle     *singledo.Single[C.Proxy]\n}\n\nfunc (u *URLTest) Now() string {\n\treturn u.fast(false).Name()\n}\n\nfunc (u *URLTest) Set(name string) error {\n\tvar p C.Proxy\n\tfor _, proxy := range u.GetProxies(false) {\n\t\tif proxy.Name() == name {\n\t\t\tp = proxy\n\t\t\tbreak\n\t\t}\n\t}\n\tif p == nil {\n\t\treturn errors.New(\"proxy not exist\")\n\t}\n\tu.ForceSet(name)\n\treturn nil\n}\n\nfunc (u *URLTest) ForceSet(name string) {\n\tu.selected = name\n\tu.fastSingle.Reset()\n}\n\n// DialContext implements C.ProxyAdapter\nfunc (u *URLTest) DialContext(ctx context.Context, metadata *C.Metadata) (c C.Conn, err error) {\n\tproxy := u.fast(true)\n\tc, err = proxy.DialContext(ctx, metadata)\n\tif err == nil {\n\t\tc.AppendToChains(u)\n\t} else {\n\t\tu.onDialFailed(proxy.Type(), err, u.healthCheck)\n\t}\n\n\tif N.NeedHandshake(c) {\n\t\tc = callback.NewFirstWriteCallBackConn(c, func(err error) {\n\t\t\tif err == nil {\n\t\t\t\tu.onDialSuccess()\n\t\t\t} else {\n\t\t\t\tu.onDialFailed(proxy.Type(), err, u.healthCheck)\n\t\t\t}\n\t\t})\n\t}\n\n\treturn c, err\n}\n\n// ListenPacketContext implements C.ProxyAdapter\nfunc (u *URLTest) ListenPacketContext(ctx context.Context, metadata *C.Metadata) (C.PacketConn, error) {\n\tproxy := u.fast(true)\n\tpc, err := proxy.ListenPacketContext(ctx, metadata)\n\tif err == nil {\n\t\tpc.AppendToChains(u)\n\t} else {\n\t\tu.onDialFailed(proxy.Type(), err, u.healthCheck)\n\t}\n\n\treturn pc, err\n}\n\n// Unwrap implements C.ProxyAdapter\nfunc (u *URLTest) Unwrap(metadata *C.Metadata, touch bool) C.Proxy {\n\treturn u.fast(touch)\n}\n\nfunc (u *URLTest) healthCheck() {\n\tu.fastSingle.Reset()\n\tu.GroupBase.healthCheck()\n\tu.fastSingle.Reset()\n}\n\nfunc (u *URLTest) fast(touch bool) C.Proxy {\n\telm, _, shared := u.fastSingle.Do(func() (C.Proxy, error) {\n\t\tproxies := u.GetProxies(touch)\n\t\tif u.selected != \"\" {\n\t\t\tfor _, proxy := range proxies {\n\t\t\t\tif !proxy.AliveForTestUrl(u.testUrl) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif proxy.Name() == u.selected {\n\t\t\t\t\tu.fastNode = proxy\n\t\t\t\t\treturn proxy, nil\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tfast := proxies[0]\n\t\tminDelay := fast.LastDelayForTestUrl(u.testUrl)\n\t\tfastNotExist := true\n\n\t\tfor _, proxy := range proxies[1:] {\n\t\t\tif u.fastNode != nil && proxy.Name() == u.fastNode.Name() {\n\t\t\t\tfastNotExist = false\n\t\t\t}\n\n\t\t\tif !proxy.AliveForTestUrl(u.testUrl) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tdelay := proxy.LastDelayForTestUrl(u.testUrl)\n\t\t\tif delay < minDelay {\n\t\t\t\tfast = proxy\n\t\t\t\tminDelay = delay\n\t\t\t}\n\n\t\t}\n\t\t// tolerance\n\t\tif u.fastNode == nil || fastNotExist || !u.fastNode.AliveForTestUrl(u.testUrl) || u.fastNode.LastDelayForTestUrl(u.testUrl) > fast.LastDelayForTestUrl(u.testUrl)+u.tolerance {\n\t\t\tu.fastNode = fast\n\t\t}\n\t\treturn u.fastNode, nil\n\t})\n\tif shared && touch { // a shared fastSingle.Do() may cause providers untouched, so we touch them again\n\t\tu.Touch()\n\t}\n\n\treturn elm\n}\n\n// SupportUDP implements C.ProxyAdapter\nfunc (u *URLTest) SupportUDP() bool {\n\tif u.disableUDP {\n\t\treturn false\n\t}\n\treturn u.fast(false).SupportUDP()\n}\n\n// IsL3Protocol implements C.ProxyAdapter\nfunc (u *URLTest) IsL3Protocol(metadata *C.Metadata) bool {\n\treturn u.fast(false).IsL3Protocol(metadata)\n}\n\n// MarshalJSON implements C.ProxyAdapter\nfunc (u *URLTest) MarshalJSON() ([]byte, error) {\n\tall := []string{}\n\tfor _, proxy := range u.GetProxies(false) {\n\t\tall = append(all, proxy.Name())\n\t}\n\treturn json.Marshal(map[string]any{\n\t\t\"type\":           u.Type().String(),\n\t\t\"now\":            u.Now(),\n\t\t\"all\":            all,\n\t\t\"testUrl\":        u.testUrl,\n\t\t\"expectedStatus\": u.expectedStatus,\n\t\t\"fixed\":          u.selected,\n\t\t\"hidden\":         u.Hidden(),\n\t\t\"icon\":           u.Icon(),\n\t})\n}\n\nfunc (u *URLTest) Providers() []P.ProxyProvider {\n\treturn u.providers\n}\n\nfunc (u *URLTest) Proxies() []C.Proxy {\n\treturn u.GetProxies(false)\n}\n\nfunc (u *URLTest) URLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16]) (map[string]uint16, error) {\n\treturn u.GroupBase.URLTest(ctx, u.testUrl, expectedStatus)\n}\n\nfunc parseURLTestOption(config map[string]any) []urlTestOption {\n\topts := []urlTestOption{}\n\n\t// tolerance\n\tif elm, ok := config[\"tolerance\"]; ok {\n\t\tif tolerance, ok := elm.(int); ok {\n\t\t\topts = append(opts, urlTestWithTolerance(uint16(tolerance)))\n\t\t}\n\t}\n\n\treturn opts\n}\n\nfunc NewURLTest(option *GroupCommonOption, providers []P.ProxyProvider, options ...urlTestOption) *URLTest {\n\turlTest := &URLTest{\n\t\tGroupBase: NewGroupBase(GroupBaseOption{\n\t\t\tName:           option.Name,\n\t\t\tType:           C.URLTest,\n\t\t\tHidden:         option.Hidden,\n\t\t\tIcon:           option.Icon,\n\t\t\tFilter:         option.Filter,\n\t\t\tExcludeFilter:  option.ExcludeFilter,\n\t\t\tExcludeType:    option.ExcludeType,\n\t\t\tTestTimeout:    option.TestTimeout,\n\t\t\tMaxFailedTimes: option.MaxFailedTimes,\n\t\t\tProviders:      providers,\n\t\t}),\n\t\tfastSingle:     singledo.NewSingle[C.Proxy](time.Second * 10),\n\t\tdisableUDP:     option.DisableUDP,\n\t\ttestUrl:        option.URL,\n\t\texpectedStatus: option.ExpectedStatus,\n\t}\n\n\tfor _, option := range options {\n\t\toption(urlTest)\n\t}\n\n\treturn urlTest\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/outboundgroup/util.go",
    "content": "package outboundgroup\n\nimport (\n\t\"context\"\n\n\t\"github.com/metacubex/mihomo/common/utils\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tP \"github.com/metacubex/mihomo/constant/provider\"\n)\n\ntype ProxyGroup interface {\n\tC.ProxyAdapter\n\n\tProviders() []P.ProxyProvider\n\tProxies() []C.Proxy\n\tNow() string\n\tTouch()\n\n\tHidden() bool\n\tIcon() string\n\n\tURLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16]) (mp map[string]uint16, err error)\n}\n\nvar _ ProxyGroup = (*Fallback)(nil)\nvar _ ProxyGroup = (*LoadBalance)(nil)\nvar _ ProxyGroup = (*URLTest)(nil)\nvar _ ProxyGroup = (*Selector)(nil)\n\ntype SelectAble interface {\n\tSet(string) error\n\tForceSet(name string)\n}\n\nvar _ SelectAble = (*Fallback)(nil)\nvar _ SelectAble = (*URLTest)(nil)\nvar _ SelectAble = (*Selector)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/adapter/parser.go",
    "content": "package adapter\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/metacubex/mihomo/adapter/outbound\"\n\t\"github.com/metacubex/mihomo/common/structure\"\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\nfunc ParseProxy(mapping map[string]any, options ...ProxyOption) (C.Proxy, error) {\n\tdecoder := structure.NewDecoder(structure.Option{TagName: \"proxy\", WeaklyTypedInput: true, KeyReplacer: structure.DefaultKeyReplacer})\n\tproxyType, existType := mapping[\"type\"].(string)\n\tif !existType {\n\t\treturn nil, fmt.Errorf(\"missing type\")\n\t}\n\n\topt := applyProxyOptions(options...)\n\tbasicOption := outbound.BasicOption{\n\t\tDialerForAPI: opt.DialerForAPI,\n\t\tProviderName: opt.ProviderName,\n\t}\n\n\tvar (\n\t\tproxy outbound.ProxyAdapter\n\t\terr   error\n\t)\n\tswitch proxyType {\n\tcase \"ss\":\n\t\tssOption := &outbound.ShadowSocksOption{BasicOption: basicOption}\n\t\terr = decoder.Decode(mapping, ssOption)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tproxy, err = outbound.NewShadowSocks(*ssOption)\n\tcase \"ssr\":\n\t\tssrOption := &outbound.ShadowSocksROption{BasicOption: basicOption}\n\t\terr = decoder.Decode(mapping, ssrOption)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tproxy, err = outbound.NewShadowSocksR(*ssrOption)\n\tcase \"socks5\":\n\t\tsocksOption := &outbound.Socks5Option{BasicOption: basicOption}\n\t\terr = decoder.Decode(mapping, socksOption)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tproxy, err = outbound.NewSocks5(*socksOption)\n\tcase \"http\":\n\t\thttpOption := &outbound.HttpOption{BasicOption: basicOption}\n\t\terr = decoder.Decode(mapping, httpOption)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tproxy, err = outbound.NewHttp(*httpOption)\n\tcase \"vmess\":\n\t\tvmessOption := &outbound.VmessOption{BasicOption: basicOption}\n\t\terr = decoder.Decode(mapping, vmessOption)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tproxy, err = outbound.NewVmess(*vmessOption)\n\tcase \"vless\":\n\t\tvlessOption := &outbound.VlessOption{BasicOption: basicOption}\n\t\terr = decoder.Decode(mapping, vlessOption)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tproxy, err = outbound.NewVless(*vlessOption)\n\tcase \"snell\":\n\t\tsnellOption := &outbound.SnellOption{BasicOption: basicOption}\n\t\terr = decoder.Decode(mapping, snellOption)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tproxy, err = outbound.NewSnell(*snellOption)\n\tcase \"trojan\":\n\t\ttrojanOption := &outbound.TrojanOption{BasicOption: basicOption}\n\t\terr = decoder.Decode(mapping, trojanOption)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tproxy, err = outbound.NewTrojan(*trojanOption)\n\tcase \"hysteria\":\n\t\thyOption := &outbound.HysteriaOption{BasicOption: basicOption}\n\t\terr = decoder.Decode(mapping, hyOption)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tproxy, err = outbound.NewHysteria(*hyOption)\n\tcase \"hysteria2\":\n\t\thyOption := &outbound.Hysteria2Option{BasicOption: basicOption}\n\t\terr = decoder.Decode(mapping, hyOption)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tproxy, err = outbound.NewHysteria2(*hyOption)\n\tcase \"wireguard\":\n\t\twgOption := &outbound.WireGuardOption{BasicOption: basicOption}\n\t\terr = decoder.Decode(mapping, wgOption)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tproxy, err = outbound.NewWireGuard(*wgOption)\n\tcase \"tuic\":\n\t\ttuicOption := &outbound.TuicOption{BasicOption: basicOption}\n\t\terr = decoder.Decode(mapping, tuicOption)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tproxy, err = outbound.NewTuic(*tuicOption)\n\tcase \"direct\":\n\t\tdirectOption := &outbound.DirectOption{BasicOption: basicOption}\n\t\terr = decoder.Decode(mapping, directOption)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tproxy = outbound.NewDirectWithOption(*directOption)\n\tcase \"dns\":\n\t\tdnsOptions := &outbound.DnsOption{BasicOption: basicOption}\n\t\terr = decoder.Decode(mapping, dnsOptions)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tproxy = outbound.NewDnsWithOption(*dnsOptions)\n\tcase \"reject\":\n\t\trejectOption := &outbound.RejectOption{BasicOption: basicOption}\n\t\terr = decoder.Decode(mapping, rejectOption)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tproxy = outbound.NewRejectWithOption(*rejectOption)\n\tcase \"ssh\":\n\t\tsshOption := &outbound.SshOption{BasicOption: basicOption}\n\t\terr = decoder.Decode(mapping, sshOption)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tproxy, err = outbound.NewSsh(*sshOption)\n\tcase \"mieru\":\n\t\tmieruOption := &outbound.MieruOption{BasicOption: basicOption}\n\t\terr = decoder.Decode(mapping, mieruOption)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tproxy, err = outbound.NewMieru(*mieruOption)\n\tcase \"anytls\":\n\t\tanytlsOption := &outbound.AnyTLSOption{BasicOption: basicOption}\n\t\terr = decoder.Decode(mapping, anytlsOption)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tproxy, err = outbound.NewAnyTLS(*anytlsOption)\n\tcase \"sudoku\":\n\t\tsudokuOption := &outbound.SudokuOption{BasicOption: basicOption}\n\t\terr = decoder.Decode(mapping, sudokuOption)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tproxy, err = outbound.NewSudoku(*sudokuOption)\n\tcase \"masque\":\n\t\tmasqueOption := &outbound.MasqueOption{BasicOption: basicOption}\n\t\terr = decoder.Decode(mapping, masqueOption)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tproxy, err = outbound.NewMasque(*masqueOption)\n\tcase \"trusttunnel\":\n\t\ttrustTunnelOption := &outbound.TrustTunnelOption{BasicOption: basicOption}\n\t\terr = decoder.Decode(mapping, trustTunnelOption)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tproxy, err = outbound.NewTrustTunnel(*trustTunnelOption)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unsupport proxy type: %s\", proxyType)\n\t}\n\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif muxMapping, muxExist := mapping[\"smux\"].(map[string]any); muxExist {\n\t\tmuxOption := &outbound.SingMuxOption{}\n\t\terr = decoder.Decode(muxMapping, muxOption)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif muxOption.Enabled {\n\t\t\tproxy, err = outbound.NewSingMux(*muxOption, proxy)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\n\tproxy = outbound.NewAutoCloseProxyAdapter(proxy)\n\treturn NewProxy(proxy), nil\n}\n\ntype proxyOption struct {\n\tDialerForAPI C.Dialer\n\tProviderName string\n}\n\nfunc applyProxyOptions(options ...ProxyOption) proxyOption {\n\topt := proxyOption{}\n\tfor _, o := range options {\n\t\to(&opt)\n\t}\n\treturn opt\n}\n\ntype ProxyOption func(opt *proxyOption)\n\nfunc WithDialerForAPI(dialer C.Dialer) ProxyOption {\n\treturn func(opt *proxyOption) {\n\t\topt.DialerForAPI = dialer\n\t}\n}\n\nfunc WithProviderName(name string) ProxyOption {\n\treturn func(opt *proxyOption) {\n\t\topt.ProviderName = name\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/patch.go",
    "content": "package adapter\n\ntype UrlTestCheck func(url string, name string, delay uint16)\n\nvar UrlTestHook UrlTestCheck\n"
  },
  {
    "path": "core/Clash.Meta/adapter/provider/healthcheck.go",
    "content": "package provider\n\nimport (\n\t\"context\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/atomic\"\n\t\"github.com/metacubex/mihomo/common/singledo\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/dlclark/regexp2\"\n\t\"golang.org/x/sync/errgroup\"\n)\n\ntype HealthCheckOption struct {\n\tURL      string\n\tInterval uint\n}\n\ntype extraOption struct {\n\texpectedStatus utils.IntRanges[uint16]\n\tfilters        map[string]struct{}\n}\n\ntype HealthCheck struct {\n\tctx            context.Context\n\tctxCancel      context.CancelFunc\n\turl            string\n\textra          map[string]*extraOption\n\tmu             sync.Mutex\n\tproxies        []C.Proxy\n\tinterval       time.Duration\n\tlazy           bool\n\texpectedStatus utils.IntRanges[uint16]\n\tlastTouch      atomic.TypedValue[time.Time]\n\tsingleDo       *singledo.Single[struct{}]\n\ttimeout        time.Duration\n}\n\nfunc (hc *HealthCheck) process() {\n\tticker := time.NewTicker(hc.interval)\n\tgo hc.check()\n\tfor {\n\t\tselect {\n\t\tcase <-ticker.C:\n\t\t\tlastTouch := hc.lastTouch.Load()\n\t\t\tsince := time.Since(lastTouch)\n\t\t\tif !hc.lazy || since < hc.interval {\n\t\t\t\thc.check()\n\t\t\t} else {\n\t\t\t\tlog.Debugln(\"Skip once health check because we are lazy\")\n\t\t\t}\n\t\tcase <-hc.ctx.Done():\n\t\t\tticker.Stop()\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (hc *HealthCheck) setProxies(proxies []C.Proxy) {\n\thc.proxies = proxies\n}\n\nfunc (hc *HealthCheck) registerHealthCheckTask(url string, expectedStatus utils.IntRanges[uint16], filter string, interval uint) {\n\turl = strings.TrimSpace(url)\n\tif len(url) == 0 || url == hc.url {\n\t\tlog.Debugln(\"ignore invalid health check url: %s\", url)\n\t\treturn\n\t}\n\n\thc.mu.Lock()\n\tdefer hc.mu.Unlock()\n\n\t// if the provider has not set up health checks, then modify it to be the same as the group's interval\n\tif hc.interval == 0 {\n\t\thc.interval = time.Duration(interval) * time.Second\n\t}\n\n\tif hc.extra == nil {\n\t\thc.extra = make(map[string]*extraOption)\n\t}\n\n\t// prioritize the use of previously registered configurations, especially those from provider\n\tif _, ok := hc.extra[url]; ok {\n\t\t// provider default health check does not set filter\n\t\tif url != hc.url && len(filter) != 0 {\n\t\t\tsplitAndAddFiltersToExtra(filter, hc.extra[url])\n\t\t}\n\n\t\tlog.Debugln(\"health check url: %s exists\", url)\n\t\treturn\n\t}\n\n\toption := &extraOption{filters: map[string]struct{}{}, expectedStatus: expectedStatus}\n\tsplitAndAddFiltersToExtra(filter, option)\n\thc.extra[url] = option\n}\n\nfunc splitAndAddFiltersToExtra(filter string, option *extraOption) {\n\tfilter = strings.TrimSpace(filter)\n\tif len(filter) != 0 {\n\t\tfor _, regex := range strings.Split(filter, \"`\") {\n\t\t\tregex = strings.TrimSpace(regex)\n\t\t\tif len(regex) != 0 {\n\t\t\t\toption.filters[regex] = struct{}{}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (hc *HealthCheck) auto() bool {\n\treturn hc.interval != 0\n}\n\nfunc (hc *HealthCheck) touch() {\n\thc.lastTouch.Store(time.Now())\n}\n\nfunc (hc *HealthCheck) check() {\n\tif len(hc.proxies) == 0 {\n\t\treturn\n\t}\n\n\t_, _, _ = hc.singleDo.Do(func() (struct{}, error) {\n\t\tid := utils.NewUUIDV4().String()\n\t\tlog.Debugln(\"Start New Health Checking {%s}\", id)\n\t\tb := new(errgroup.Group)\n\t\tb.SetLimit(10)\n\n\t\t// execute default health check\n\t\toption := &extraOption{filters: nil, expectedStatus: hc.expectedStatus}\n\t\thc.execute(b, hc.url, id, option)\n\n\t\t// execute extra health check\n\t\tif len(hc.extra) != 0 {\n\t\t\tfor url, option := range hc.extra {\n\t\t\t\thc.execute(b, url, id, option)\n\t\t\t}\n\t\t}\n\t\t_ = b.Wait()\n\t\tlog.Debugln(\"Finish A Health Checking {%s}\", id)\n\t\treturn struct{}{}, nil\n\t})\n}\n\nfunc (hc *HealthCheck) execute(b *errgroup.Group, url, uid string, option *extraOption) {\n\turl = strings.TrimSpace(url)\n\tif len(url) == 0 {\n\t\tlog.Debugln(\"Health Check has been skipped due to testUrl is empty, {%s}\", uid)\n\t\treturn\n\t}\n\n\tvar filterReg *regexp2.Regexp\n\tvar expectedStatus utils.IntRanges[uint16]\n\tif option != nil {\n\t\texpectedStatus = option.expectedStatus\n\t\tif len(option.filters) != 0 {\n\t\t\tfilters := make([]string, 0, len(option.filters))\n\t\t\tfor filter := range option.filters {\n\t\t\t\tfilters = append(filters, filter)\n\t\t\t}\n\n\t\t\tfilterReg = regexp2.MustCompile(strings.Join(filters, \"|\"), regexp2.None)\n\t\t}\n\t}\n\n\tfor _, proxy := range hc.proxies {\n\t\t// skip proxies that do not require health check\n\t\tif filterReg != nil {\n\t\t\tif match, _ := filterReg.MatchString(proxy.Name()); !match {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\tp := proxy\n\t\tb.Go(func() error {\n\t\t\tctx, cancel := context.WithTimeout(hc.ctx, hc.timeout)\n\t\t\tdefer cancel()\n\t\t\tlog.Debugln(\"Health Checking, proxy: %s, url: %s, id: {%s}\", p.Name(), url, uid)\n\t\t\t_, _ = p.URLTest(ctx, url, expectedStatus)\n\t\t\tlog.Debugln(\"Health Checked, proxy: %s, url: %s, alive: %t, delay: %d ms uid: {%s}\", p.Name(), url, p.AliveForTestUrl(url), p.LastDelayForTestUrl(url), uid)\n\t\t\treturn nil\n\t\t})\n\t}\n}\n\nfunc (hc *HealthCheck) close() {\n\thc.ctxCancel()\n}\n\nfunc NewHealthCheck(proxies []C.Proxy, url string, timeout uint, interval uint, lazy bool, expectedStatus utils.IntRanges[uint16]) *HealthCheck {\n\tif url == \"\" {\n\t\texpectedStatus = nil\n\t\tinterval = 0\n\t}\n\tif timeout == 0 {\n\t\ttimeout = 5000\n\t}\n\tctx, cancel := context.WithCancel(context.Background())\n\n\treturn &HealthCheck{\n\t\tctx:            ctx,\n\t\tctxCancel:      cancel,\n\t\tproxies:        proxies,\n\t\turl:            url,\n\t\ttimeout:        time.Duration(timeout) * time.Millisecond,\n\t\textra:          map[string]*extraOption{},\n\t\tinterval:       time.Duration(interval) * time.Second,\n\t\tlazy:           lazy,\n\t\texpectedStatus: expectedStatus,\n\t\tsingleDo:       singledo.NewSingle[struct{}](time.Second),\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/provider/override.go",
    "content": "package provider\n\nimport (\n\t\"encoding\"\n\t\"fmt\"\n\n\t\"github.com/dlclark/regexp2\"\n)\n\ntype overrideSchema struct {\n\tTFO            *bool   `provider:\"tfo,omitempty\"`\n\tMPTcp          *bool   `provider:\"mptcp,omitempty\"`\n\tUDP            *bool   `provider:\"udp,omitempty\"`\n\tUDPOverTCP     *bool   `provider:\"udp-over-tcp,omitempty\"`\n\tUp             *string `provider:\"up,omitempty\"`\n\tDown           *string `provider:\"down,omitempty\"`\n\tDialerProxy    *string `provider:\"dialer-proxy,omitempty\"`\n\tSkipCertVerify *bool   `provider:\"skip-cert-verify,omitempty\"`\n\tInterface      *string `provider:\"interface-name,omitempty\"`\n\tRoutingMark    *int    `provider:\"routing-mark,omitempty\"`\n\tIPVersion      *string `provider:\"ip-version,omitempty\"`\n\n\tAdditionalPrefix *string                   `provider:\"additional-prefix,omitempty\"`\n\tAdditionalSuffix *string                   `provider:\"additional-suffix,omitempty\"`\n\tProxyName        []overrideProxyNameSchema `provider:\"proxy-name,omitempty\"`\n}\n\ntype overrideProxyNameSchema struct {\n\t// matching expression for regex replacement\n\tPattern *regexp2.Regexp `provider:\"pattern\"`\n\t// the new content after regex matching\n\tTarget string `provider:\"target\"`\n}\n\nvar _ encoding.TextUnmarshaler = (*regexp2.Regexp)(nil) // ensure *regexp2.Regexp can decode direct by structure package\n\nfunc (o *overrideSchema) Apply(mapping map[string]any) error {\n\tif o.TFO != nil {\n\t\tmapping[\"tfo\"] = *o.TFO\n\t}\n\tif o.MPTcp != nil {\n\t\tmapping[\"mptcp\"] = *o.MPTcp\n\t}\n\tif o.UDP != nil {\n\t\tmapping[\"udp\"] = *o.UDP\n\t}\n\tif o.UDPOverTCP != nil {\n\t\tmapping[\"udp-over-tcp\"] = *o.UDPOverTCP\n\t}\n\tif o.Up != nil {\n\t\tmapping[\"up\"] = *o.Up\n\t}\n\tif o.Down != nil {\n\t\tmapping[\"down\"] = *o.Down\n\t}\n\tif o.DialerProxy != nil {\n\t\tmapping[\"dialer-proxy\"] = *o.DialerProxy\n\t}\n\tif o.SkipCertVerify != nil {\n\t\tmapping[\"skip-cert-verify\"] = *o.SkipCertVerify\n\t}\n\tif o.Interface != nil {\n\t\tmapping[\"interface-name\"] = *o.Interface\n\t}\n\tif o.RoutingMark != nil {\n\t\tmapping[\"routing-mark\"] = *o.RoutingMark\n\t}\n\tif o.IPVersion != nil {\n\t\tmapping[\"ip-version\"] = *o.IPVersion\n\t}\n\n\tfor _, expr := range o.ProxyName {\n\t\tname := mapping[\"name\"].(string)\n\t\tnewName, err := expr.Pattern.Replace(name, expr.Target, 0, -1)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"proxy name replace error: %w\", err)\n\t\t}\n\t\tmapping[\"name\"] = newName\n\t}\n\tif o.AdditionalPrefix != nil {\n\t\tmapping[\"name\"] = fmt.Sprintf(\"%s%s\", *o.AdditionalPrefix, mapping[\"name\"])\n\t}\n\tif o.AdditionalSuffix != nil {\n\t\tmapping[\"name\"] = fmt.Sprintf(\"%s%s\", mapping[\"name\"], *o.AdditionalSuffix)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/provider/parser.go",
    "content": "package provider\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/structure\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"github.com/metacubex/mihomo/component/resource\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tP \"github.com/metacubex/mihomo/constant/provider\"\n)\n\nvar (\n\terrVehicleType = errors.New(\"unsupport vehicle type\")\n)\n\ntype healthCheckSchema struct {\n\tEnable         bool   `provider:\"enable\"`\n\tURL            string `provider:\"url,omitempty\"`\n\tInterval       int    `provider:\"interval,omitempty\"`\n\tTestTimeout    int    `provider:\"timeout,omitempty\"`\n\tLazy           bool   `provider:\"lazy,omitempty\"`\n\tExpectedStatus string `provider:\"expected-status,omitempty\"`\n}\n\ntype proxyProviderSchema struct {\n\tType          string           `provider:\"type\"`\n\tPath          string           `provider:\"path,omitempty\"`\n\tURL           string           `provider:\"url,omitempty\"`\n\tProxy         string           `provider:\"proxy,omitempty\"`\n\tInterval      int              `provider:\"interval,omitempty\"`\n\tFilter        string           `provider:\"filter,omitempty\"`\n\tExcludeFilter string           `provider:\"exclude-filter,omitempty\"`\n\tExcludeType   string           `provider:\"exclude-type,omitempty\"`\n\tDialerProxy   string           `provider:\"dialer-proxy,omitempty\"`\n\tSizeLimit     int64            `provider:\"size-limit,omitempty\"`\n\tPayload       []map[string]any `provider:\"payload,omitempty\"`\n\n\tHealthCheck healthCheckSchema   `provider:\"health-check,omitempty\"`\n\tOverride    overrideSchema      `provider:\"override,omitempty\"`\n\tHeader      map[string][]string `provider:\"header,omitempty\"`\n}\n\nfunc ParseProxyProvider(name string, mapping map[string]any) (P.ProxyProvider, error) {\n\tdecoder := structure.NewDecoder(structure.Option{TagName: \"provider\", WeaklyTypedInput: true})\n\n\tschema := &proxyProviderSchema{\n\t\tHealthCheck: healthCheckSchema{\n\t\t\tLazy: true,\n\t\t},\n\t}\n\tif err := decoder.Decode(mapping, schema); err != nil {\n\t\treturn nil, err\n\t}\n\n\texpectedStatus, err := utils.NewUnsignedRanges[uint16](schema.HealthCheck.ExpectedStatus)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar hcInterval uint\n\tif schema.HealthCheck.Enable {\n\t\tif schema.HealthCheck.Interval == 0 {\n\t\t\tschema.HealthCheck.Interval = 300\n\t\t}\n\t\thcInterval = uint(schema.HealthCheck.Interval)\n\t}\n\thc := NewHealthCheck([]C.Proxy{}, schema.HealthCheck.URL, uint(schema.HealthCheck.TestTimeout), hcInterval, schema.HealthCheck.Lazy, expectedStatus)\n\n\tparser, err := NewProxiesParser(name, schema.Filter, schema.ExcludeFilter, schema.ExcludeType, schema.DialerProxy, schema.Override)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar vehicle P.Vehicle\n\tswitch schema.Type {\n\tcase \"file\":\n\t\tpath := C.Path.Resolve(schema.Path)\n\t\tif !C.Path.IsSafePath(path) {\n\t\t\treturn nil, C.Path.ErrNotSafePath(path)\n\t\t}\n\t\tvehicle = resource.NewFileVehicle(path)\n\tcase \"http\":\n\t\tpath := C.Path.GetPathByHash(\"proxies\", schema.URL)\n\t\tif schema.Path != \"\" {\n\t\t\tpath = C.Path.Resolve(schema.Path)\n\t\t\tif !C.Path.IsSafePath(path) {\n\t\t\t\treturn nil, C.Path.ErrNotSafePath(path)\n\t\t\t}\n\t\t}\n\t\tvehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, schema.Header, resource.DefaultHttpTimeout, schema.SizeLimit)\n\tcase \"inline\":\n\t\treturn NewInlineProvider(name, schema.Payload, parser, hc)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"%w: %s\", errVehicleType, schema.Type)\n\t}\n\n\tinterval := time.Duration(uint(schema.Interval)) * time.Second\n\n\treturn NewProxySetProvider(name, interval, schema.Payload, parser, vehicle, hc)\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/provider/patch.go",
    "content": "package provider\n\nfunc (pp *proxySetProvider) GetSubscriptionInfo() *SubscriptionInfo {\n\treturn pp.subscriptionInfo\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/provider/provider.go",
    "content": "package provider\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"runtime\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/adapter\"\n\t\"github.com/metacubex/mihomo/common/convert\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"github.com/metacubex/mihomo/common/yaml\"\n\t\"github.com/metacubex/mihomo/component/profile/cachefile\"\n\t\"github.com/metacubex/mihomo/component/resource\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tP \"github.com/metacubex/mihomo/constant/provider\"\n\t\"github.com/metacubex/mihomo/tunnel/statistic\"\n\n\t\"github.com/dlclark/regexp2\"\n\t\"github.com/metacubex/http\"\n)\n\nconst (\n\tReservedName = \"default\"\n)\n\ntype ProxySchema struct {\n\tProxies []map[string]any `yaml:\"proxies\"`\n}\n\ntype providerForApi struct {\n\tName             string            `json:\"name\"`\n\tType             string            `json:\"type\"`\n\tVehicleType      string            `json:\"vehicleType\"`\n\tProxies          []C.Proxy         `json:\"proxies\"`\n\tTestUrl          string            `json:\"testUrl\"`\n\tExpectedStatus   string            `json:\"expectedStatus\"`\n\tUpdatedAt        time.Time         `json:\"updatedAt,omitempty\"`\n\tSubscriptionInfo *SubscriptionInfo `json:\"subscriptionInfo,omitempty\"`\n}\n\ntype baseProvider struct {\n\tmutex       sync.RWMutex\n\tname        string\n\tproxies     []C.Proxy\n\thealthCheck *HealthCheck\n\tversion     uint32\n}\n\nfunc (bp *baseProvider) Name() string {\n\treturn bp.name\n}\n\nfunc (bp *baseProvider) Version() uint32 {\n\tbp.mutex.RLock()\n\tdefer bp.mutex.RUnlock()\n\treturn bp.version\n}\n\nfunc (bp *baseProvider) Initial() error {\n\tif bp.healthCheck.auto() {\n\t\tgo bp.healthCheck.process()\n\t}\n\treturn nil\n}\n\nfunc (bp *baseProvider) HealthCheck() {\n\tbp.healthCheck.check()\n}\n\nfunc (bp *baseProvider) Type() P.ProviderType {\n\treturn P.Proxy\n}\n\nfunc (bp *baseProvider) Proxies() []C.Proxy {\n\tbp.mutex.RLock()\n\tdefer bp.mutex.RUnlock()\n\treturn bp.proxies\n}\n\nfunc (bp *baseProvider) Count() int {\n\tbp.mutex.RLock()\n\tdefer bp.mutex.RUnlock()\n\treturn len(bp.proxies)\n}\n\nfunc (bp *baseProvider) Touch() {\n\tbp.healthCheck.touch()\n}\n\nfunc (bp *baseProvider) HealthCheckURL() string {\n\treturn bp.healthCheck.url\n}\n\nfunc (bp *baseProvider) RegisterHealthCheckTask(url string, expectedStatus utils.IntRanges[uint16], filter string, interval uint) {\n\tbp.healthCheck.registerHealthCheckTask(url, expectedStatus, filter, interval)\n}\n\nfunc (bp *baseProvider) setProxies(proxies []C.Proxy) {\n\tbp.mutex.Lock()\n\tdefer bp.mutex.Unlock()\n\tbp.proxies = proxies\n\tbp.version += 1\n\tbp.healthCheck.setProxies(proxies)\n\tif bp.healthCheck.auto() {\n\t\tgo bp.healthCheck.check()\n\t}\n}\n\nfunc (bp *baseProvider) Close() error {\n\tbp.healthCheck.close()\n\treturn nil\n}\n\n// ProxySetProvider for auto gc\ntype ProxySetProvider struct {\n\t*proxySetProvider\n}\n\ntype proxySetProvider struct {\n\tbaseProvider\n\t*resource.Fetcher[[]C.Proxy]\n\tsubscriptionInfo *SubscriptionInfo\n}\n\nfunc (pp *proxySetProvider) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(providerForApi{\n\t\tName:             pp.Name(),\n\t\tType:             pp.Type().String(),\n\t\tVehicleType:      pp.VehicleType().String(),\n\t\tProxies:          pp.Proxies(),\n\t\tTestUrl:          pp.healthCheck.url,\n\t\tExpectedStatus:   pp.healthCheck.expectedStatus.String(),\n\t\tUpdatedAt:        pp.UpdatedAt(),\n\t\tSubscriptionInfo: pp.subscriptionInfo,\n\t})\n}\n\nfunc (pp *proxySetProvider) Name() string {\n\treturn pp.Fetcher.Name()\n}\n\nfunc (pp *proxySetProvider) Update() error {\n\t_, _, err := pp.Fetcher.Update()\n\treturn err\n}\n\nfunc (pp *proxySetProvider) Initial() error {\n\tif err := pp.baseProvider.Initial(); err != nil {\n\t\treturn err\n\t}\n\t_, err := pp.Fetcher.Initial()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif subscriptionInfo := cachefile.Cache().GetSubscriptionInfo(pp.Name()); subscriptionInfo != \"\" {\n\t\tpp.subscriptionInfo = NewSubscriptionInfo(subscriptionInfo)\n\t}\n\tpp.closeAllConnections()\n\treturn nil\n}\n\nfunc (pp *proxySetProvider) closeAllConnections() {\n\tstatistic.DefaultManager.Range(func(c statistic.Tracker) bool {\n\t\tfor _, chain := range c.ProviderChains() {\n\t\t\tif chain == pp.Name() {\n\t\t\t\t_ = c.Close()\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\treturn true\n\t})\n}\n\nfunc (pp *proxySetProvider) Close() error {\n\t_ = pp.baseProvider.Close()\n\treturn pp.Fetcher.Close()\n}\n\nfunc NewProxySetProvider(name string, interval time.Duration, payload []map[string]any, parser resource.Parser[[]C.Proxy], vehicle P.Vehicle, hc *HealthCheck) (*ProxySetProvider, error) {\n\tpd := &proxySetProvider{\n\t\tbaseProvider: baseProvider{\n\t\t\tname:        name,\n\t\t\tproxies:     []C.Proxy{},\n\t\t\thealthCheck: hc,\n\t\t},\n\t}\n\n\tif len(payload) > 0 { // using as fallback proxies\n\t\tps := ProxySchema{Proxies: payload}\n\t\tbuf, err := yaml.Marshal(ps)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tproxies, err := parser(buf)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpd.proxies = proxies\n\t\t// direct call setProxies on hc to avoid starting a health check process immediately, it should be done by Initial()\n\t\thc.setProxies(proxies)\n\t}\n\n\tfetcher := resource.NewFetcher[[]C.Proxy](name, interval, vehicle, parser, pd.setProxies)\n\tpd.Fetcher = fetcher\n\tif httpVehicle, ok := vehicle.(*resource.HTTPVehicle); ok {\n\t\thttpVehicle.SetInRead(func(resp *http.Response) {\n\t\t\tif subscriptionInfo := resp.Header.Get(\"subscription-userinfo\"); subscriptionInfo != \"\" {\n\t\t\t\tcachefile.Cache().SetSubscriptionInfo(name, subscriptionInfo)\n\t\t\t\tpd.subscriptionInfo = NewSubscriptionInfo(subscriptionInfo)\n\t\t\t}\n\t\t})\n\t}\n\n\twrapper := &ProxySetProvider{pd}\n\truntime.SetFinalizer(wrapper, (*ProxySetProvider).Close)\n\treturn wrapper, nil\n}\n\nfunc (pp *ProxySetProvider) Close() error {\n\truntime.SetFinalizer(pp, nil)\n\treturn pp.proxySetProvider.Close()\n}\n\n// InlineProvider for auto gc\ntype InlineProvider struct {\n\t*inlineProvider\n}\n\ntype inlineProvider struct {\n\tbaseProvider\n\tupdateAt time.Time\n}\n\nfunc (ip *inlineProvider) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(providerForApi{\n\t\tName:           ip.Name(),\n\t\tType:           ip.Type().String(),\n\t\tVehicleType:    ip.VehicleType().String(),\n\t\tProxies:        ip.Proxies(),\n\t\tTestUrl:        ip.healthCheck.url,\n\t\tExpectedStatus: ip.healthCheck.expectedStatus.String(),\n\t\tUpdatedAt:      ip.updateAt,\n\t})\n}\n\nfunc (ip *inlineProvider) VehicleType() P.VehicleType {\n\treturn P.Inline\n}\n\nfunc (ip *inlineProvider) Update() error {\n\t// make api update happy\n\tip.updateAt = time.Now()\n\treturn nil\n}\n\nfunc NewInlineProvider(name string, payload []map[string]any, parser resource.Parser[[]C.Proxy], hc *HealthCheck) (*InlineProvider, error) {\n\tps := ProxySchema{Proxies: payload}\n\tbuf, err := yaml.Marshal(ps)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tproxies, err := parser(buf)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t// direct call setProxies on hc to avoid starting a health check process immediately, it should be done by Initial()\n\thc.setProxies(proxies)\n\n\tip := &inlineProvider{\n\t\tbaseProvider: baseProvider{\n\t\t\tname:        name,\n\t\t\tproxies:     proxies,\n\t\t\thealthCheck: hc,\n\t\t},\n\t\tupdateAt: time.Now(),\n\t}\n\twrapper := &InlineProvider{ip}\n\truntime.SetFinalizer(wrapper, (*InlineProvider).Close)\n\treturn wrapper, nil\n}\n\nfunc (ip *InlineProvider) Close() error {\n\truntime.SetFinalizer(ip, nil)\n\treturn ip.baseProvider.Close()\n}\n\n// CompatibleProvider for auto gc\ntype CompatibleProvider struct {\n\t*compatibleProvider\n}\n\ntype compatibleProvider struct {\n\tbaseProvider\n}\n\nfunc (cp *compatibleProvider) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(providerForApi{\n\t\tName:           cp.Name(),\n\t\tType:           cp.Type().String(),\n\t\tVehicleType:    cp.VehicleType().String(),\n\t\tProxies:        cp.Proxies(),\n\t\tTestUrl:        cp.healthCheck.url,\n\t\tExpectedStatus: cp.healthCheck.expectedStatus.String(),\n\t})\n}\n\nfunc (cp *compatibleProvider) Update() error {\n\treturn nil\n}\n\nfunc (cp *compatibleProvider) VehicleType() P.VehicleType {\n\treturn P.Compatible\n}\n\nfunc NewCompatibleProvider(name string, proxies []C.Proxy, hc *HealthCheck) (*CompatibleProvider, error) {\n\tif len(proxies) == 0 {\n\t\treturn nil, errors.New(\"provider need one proxy at least\")\n\t}\n\n\tpd := &compatibleProvider{\n\t\tbaseProvider: baseProvider{\n\t\t\tname:        name,\n\t\t\tproxies:     proxies,\n\t\t\thealthCheck: hc,\n\t\t},\n\t}\n\n\twrapper := &CompatibleProvider{pd}\n\truntime.SetFinalizer(wrapper, (*CompatibleProvider).Close)\n\treturn wrapper, nil\n}\n\nfunc (cp *CompatibleProvider) Close() error {\n\truntime.SetFinalizer(cp, nil)\n\treturn cp.compatibleProvider.Close()\n}\n\nfunc NewProxiesParser(pdName string, filter string, excludeFilter string, excludeType string, dialerProxy string, override overrideSchema) (resource.Parser[[]C.Proxy], error) {\n\tvar excludeTypeArray []string\n\tif excludeType != \"\" {\n\t\texcludeTypeArray = strings.Split(excludeType, \"|\")\n\t}\n\n\tvar excludeFilterRegs []*regexp2.Regexp\n\tif excludeFilter != \"\" {\n\t\tfor _, excludeFilter := range strings.Split(excludeFilter, \"`\") {\n\t\t\texcludeFilterReg, err := regexp2.Compile(excludeFilter, regexp2.None)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"invalid excludeFilter regex: %w\", err)\n\t\t\t}\n\t\t\texcludeFilterRegs = append(excludeFilterRegs, excludeFilterReg)\n\t\t}\n\t}\n\n\tvar filterRegs []*regexp2.Regexp\n\tfor _, filter := range strings.Split(filter, \"`\") {\n\t\tfilterReg, err := regexp2.Compile(filter, regexp2.None)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid filter regex: %w\", err)\n\t\t}\n\t\tfilterRegs = append(filterRegs, filterReg)\n\t}\n\n\treturn func(buf []byte) ([]C.Proxy, error) {\n\t\tschema := &ProxySchema{}\n\n\t\tif err := yaml.Unmarshal(buf, schema); err != nil {\n\t\t\tproxies, err1 := convert.ConvertsV2Ray(buf)\n\t\t\tif err1 != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"%w, %w\", err, err1)\n\t\t\t}\n\t\t\tschema.Proxies = proxies\n\t\t}\n\n\t\tif schema.Proxies == nil {\n\t\t\treturn nil, errors.New(\"file must have a `proxies` field\")\n\t\t}\n\n\t\tproxies := []C.Proxy{}\n\t\tproxiesSet := map[string]struct{}{}\n\t\tfor _, filterReg := range filterRegs {\n\t\tLOOP1:\n\t\t\tfor idx, mapping := range schema.Proxies {\n\t\t\t\tif len(excludeTypeArray) > 0 {\n\t\t\t\t\tmType, ok := mapping[\"type\"]\n\t\t\t\t\tif !ok {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tpType, ok := mType.(string)\n\t\t\t\t\tif !ok {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tfor _, excludeType := range excludeTypeArray {\n\t\t\t\t\t\tif strings.EqualFold(pType, excludeType) {\n\t\t\t\t\t\t\tcontinue LOOP1\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tmName, ok := mapping[\"name\"]\n\t\t\t\tif !ok {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tname, ok := mName.(string)\n\t\t\t\tif !ok {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif len(excludeFilterRegs) > 0 {\n\t\t\t\t\tfor _, excludeFilterReg := range excludeFilterRegs {\n\t\t\t\t\t\tif mat, _ := excludeFilterReg.MatchString(name); mat {\n\t\t\t\t\t\t\tcontinue LOOP1\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif len(filter) > 0 {\n\t\t\t\t\tif mat, _ := filterReg.MatchString(name); !mat {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif _, ok := proxiesSet[name]; ok {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tif len(dialerProxy) > 0 {\n\t\t\t\t\tmapping[\"dialer-proxy\"] = dialerProxy\n\t\t\t\t}\n\n\t\t\t\terr := override.Apply(mapping)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"proxy %d override error: %w\", idx, err)\n\t\t\t\t}\n\n\t\t\t\tproxy, err := adapter.ParseProxy(mapping, adapter.WithProviderName(pdName))\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"proxy %d error: %w\", idx, err)\n\t\t\t\t}\n\n\t\t\t\tproxiesSet[name] = struct{}{}\n\t\t\t\tproxies = append(proxies, proxy)\n\t\t\t}\n\t\t}\n\n\t\tif len(proxies) == 0 {\n\t\t\tif len(filter) > 0 {\n\t\t\t\treturn nil, errors.New(\"doesn't match any proxy, please check your filter\")\n\t\t\t}\n\t\t\treturn nil, errors.New(\"file doesn't have any proxy\")\n\t\t}\n\n\t\treturn proxies, nil\n\t}, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/adapter/provider/subscription_info.go",
    "content": "package provider\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/log\"\n)\n\ntype SubscriptionInfo struct {\n\tUpload   int64\n\tDownload int64\n\tTotal    int64\n\tExpire   int64\n}\n\nfunc NewSubscriptionInfo(userinfo string) (si *SubscriptionInfo) {\n\tuserinfo = strings.ReplaceAll(strings.ToLower(userinfo), \" \", \"\")\n\tsi = new(SubscriptionInfo)\n\n\tfor _, field := range strings.Split(userinfo, \";\") {\n\t\tname, value, ok := strings.Cut(field, \"=\")\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tintValue, err := parseValue(value)\n\t\tif err != nil {\n\t\t\tlog.Warnln(\"[Provider] get subscription-userinfo: %e\", err)\n\t\t\tcontinue\n\t\t}\n\n\t\tswitch name {\n\t\tcase \"upload\":\n\t\t\tsi.Upload = intValue\n\t\tcase \"download\":\n\t\t\tsi.Download = intValue\n\t\tcase \"total\":\n\t\t\tsi.Total = intValue\n\t\tcase \"expire\":\n\t\t\tsi.Expire = intValue\n\t\t}\n\t}\n\treturn si\n}\n\nfunc parseValue(value string) (int64, error) {\n\tif intValue, err := strconv.ParseInt(value, 10, 64); err == nil {\n\t\treturn intValue, nil\n\t}\n\n\tif floatValue, err := strconv.ParseFloat(value, 64); err == nil {\n\t\treturn int64(floatValue), nil\n\t}\n\n\treturn 0, fmt.Errorf(\"failed to parse value '%s'\", value)\n}\n"
  },
  {
    "path": "core/Clash.Meta/android_tz.go",
    "content": "// Copyright 2014 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n//go:build android && cgo\n// +build android,cgo\n\n// kanged from https://github.com/golang/mobile/blob/c713f31d574bb632a93f169b2cc99c9e753fef0e/app/android.go#L89\n\npackage main\n\n// #include <time.h>\nimport \"C\"\nimport \"time\"\n\nfunc init() {\n\tvar currentT C.time_t\n\tvar currentTM C.struct_tm\n\tC.time(&currentT)\n\tC.localtime_r(&currentT, &currentTM)\n\ttzOffset := int(currentTM.tm_gmtoff)\n\ttz := C.GoString(currentTM.tm_zone)\n\ttime.Local = time.FixedZone(tz, tzOffset)\n}\n"
  },
  {
    "path": "core/Clash.Meta/check_amd64.sh",
    "content": "#!/bin/sh\nflags=$(grep '^flags\\b' </proc/cpuinfo | head -n 1)\nflags=\" ${flags#*:} \"\n\nhas_flags () {\n  for flag; do\n    case \"$flags\" in\n      *\" $flag \"*) :;;\n      *) return 1;;\n    esac\n  done\n}\n\ndetermine_level () {\n  level=0\n  has_flags lm cmov cx8 fpu fxsr mmx syscall sse2 || return 0\n  level=1\n  has_flags cx16 lahf_lm popcnt sse4_1 sse4_2 ssse3 || return 0\n  level=2\n  has_flags avx avx2 bmi1 bmi2 f16c fma abm movbe xsave || return 0\n  level=3\n  has_flags avx512f avx512bw avx512cd avx512dq avx512vl || return 0\n  level=4\n}\n\ndetermine_level\necho \"Your CPU supports amd64-v$level\"\nreturn $level"
  },
  {
    "path": "core/Clash.Meta/common/arc/arc.go",
    "content": "package arc\n\nimport (\n\t\"sync\"\n\t\"time\"\n\n\tlist \"github.com/bahlo/generic-list-go\"\n\t\"github.com/samber/lo\"\n)\n\n//modify from https://github.com/alexanderGugel/arc\n\n// Option is part of Functional Options Pattern\ntype Option[K comparable, V any] func(*ARC[K, V])\n\nfunc WithSize[K comparable, V any](maxSize int) Option[K, V] {\n\treturn func(a *ARC[K, V]) {\n\t\ta.c = maxSize\n\t}\n}\n\ntype ARC[K comparable, V any] struct {\n\tp     int\n\tc     int\n\tt1    *list.List[*entry[K, V]]\n\tb1    *list.List[*entry[K, V]]\n\tt2    *list.List[*entry[K, V]]\n\tb2    *list.List[*entry[K, V]]\n\tmutex sync.Mutex\n\tlen   int\n\tcache map[K]*entry[K, V]\n}\n\n// New returns a new Adaptive Replacement Cache (ARC).\nfunc New[K comparable, V any](options ...Option[K, V]) *ARC[K, V] {\n\tarc := &ARC[K, V]{}\n\tarc.Clear()\n\n\tfor _, option := range options {\n\t\toption(arc)\n\t}\n\treturn arc\n}\n\nfunc (a *ARC[K, V]) Clear() {\n\ta.mutex.Lock()\n\tdefer a.mutex.Unlock()\n\n\ta.p = 0\n\ta.t1 = list.New[*entry[K, V]]()\n\ta.b1 = list.New[*entry[K, V]]()\n\ta.t2 = list.New[*entry[K, V]]()\n\ta.b2 = list.New[*entry[K, V]]()\n\ta.len = 0\n\ta.cache = make(map[K]*entry[K, V])\n}\n\n// Set inserts a new key-value pair into the cache.\n// This optimizes future access to this entry (side effect).\nfunc (a *ARC[K, V]) Set(key K, value V) {\n\ta.mutex.Lock()\n\tdefer a.mutex.Unlock()\n\n\ta.set(key, value)\n}\n\nfunc (a *ARC[K, V]) set(key K, value V) {\n\ta.setWithExpire(key, value, time.Unix(0, 0))\n}\n\n// SetWithExpire stores any representation of a response for a given key and given expires.\n// The expires time will round to second.\nfunc (a *ARC[K, V]) SetWithExpire(key K, value V, expires time.Time) {\n\ta.mutex.Lock()\n\tdefer a.mutex.Unlock()\n\n\ta.setWithExpire(key, value, expires)\n}\n\nfunc (a *ARC[K, V]) setWithExpire(key K, value V, expires time.Time) {\n\tent, ok := a.cache[key]\n\tif !ok {\n\t\ta.len++\n\t\tent := &entry[K, V]{key: key, value: value, ghost: false, expires: expires.Unix()}\n\t\ta.req(ent)\n\t\ta.cache[key] = ent\n\t\treturn\n\t}\n\n\tif ent.ghost {\n\t\ta.len++\n\t}\n\n\tent.value = value\n\tent.ghost = false\n\tent.expires = expires.Unix()\n\ta.req(ent)\n}\n\n// Get retrieves a previously via Set inserted entry.\n// This optimizes future access to this entry (side effect).\nfunc (a *ARC[K, V]) Get(key K) (value V, ok bool) {\n\ta.mutex.Lock()\n\tdefer a.mutex.Unlock()\n\n\tent, ok := a.get(key)\n\tif !ok {\n\t\treturn lo.Empty[V](), false\n\t}\n\treturn ent.value, true\n}\n\nfunc (a *ARC[K, V]) get(key K) (e *entry[K, V], ok bool) {\n\tent, ok := a.cache[key]\n\tif !ok {\n\t\treturn ent, false\n\t}\n\ta.req(ent)\n\treturn ent, !ent.ghost\n}\n\n// GetWithExpire returns any representation of a cached response,\n// a time.Time Give expected expires,\n// and a bool set to true if the key was found.\n// This method will NOT update the expires.\nfunc (a *ARC[K, V]) GetWithExpire(key K) (V, time.Time, bool) {\n\ta.mutex.Lock()\n\tdefer a.mutex.Unlock()\n\n\tent, ok := a.get(key)\n\tif !ok {\n\t\treturn lo.Empty[V](), time.Time{}, false\n\t}\n\n\treturn ent.value, time.Unix(ent.expires, 0), true\n}\n\n// Len determines the number of currently cached entries.\n// This method is side-effect free in the sense that it does not attempt to optimize random cache access.\nfunc (a *ARC[K, V]) Len() int {\n\ta.mutex.Lock()\n\tdefer a.mutex.Unlock()\n\n\treturn a.len\n}\n\nfunc (a *ARC[K, V]) req(ent *entry[K, V]) {\n\tswitch {\n\tcase ent.ll == a.t1 || ent.ll == a.t2:\n\t\t// Case I\n\t\tent.setMRU(a.t2)\n\tcase ent.ll == a.b1:\n\t\t// Case II\n\t\t// Cache Miss in t1 and t2\n\n\t\t// Adaptation\n\t\tvar d int\n\t\tif a.b1.Len() >= a.b2.Len() {\n\t\t\td = 1\n\t\t} else {\n\t\t\td = a.b2.Len() / a.b1.Len()\n\t\t}\n\t\ta.p = min(a.p+d, a.c)\n\n\t\ta.replace(ent)\n\t\tent.setMRU(a.t2)\n\tcase ent.ll == a.b2:\n\t\t// Case III\n\t\t// Cache Miss in t1 and t2\n\n\t\t// Adaptation\n\t\tvar d int\n\t\tif a.b2.Len() >= a.b1.Len() {\n\t\t\td = 1\n\t\t} else {\n\t\t\td = a.b1.Len() / a.b2.Len()\n\t\t}\n\t\ta.p = max(a.p-d, 0)\n\n\t\ta.replace(ent)\n\t\tent.setMRU(a.t2)\n\tcase ent.ll == nil && a.t1.Len()+a.b1.Len() == a.c:\n\t\t// Case IV A\n\t\tif a.t1.Len() < a.c {\n\t\t\ta.delLRU(a.b1)\n\t\t\ta.replace(ent)\n\t\t} else {\n\t\t\ta.delLRU(a.t1)\n\t\t}\n\t\tent.setMRU(a.t1)\n\tcase ent.ll == nil && a.t1.Len()+a.b1.Len() < a.c:\n\t\t// Case IV B\n\t\tif a.t1.Len()+a.t2.Len()+a.b1.Len()+a.b2.Len() >= a.c {\n\t\t\tif a.t1.Len()+a.t2.Len()+a.b1.Len()+a.b2.Len() == 2*a.c {\n\t\t\t\ta.delLRU(a.b2)\n\t\t\t}\n\t\t\ta.replace(ent)\n\t\t}\n\t\tent.setMRU(a.t1)\n\tcase ent.ll == nil:\n\t\t// Case IV, not A nor B\n\t\tent.setMRU(a.t1)\n\t}\n}\n\nfunc (a *ARC[K, V]) delLRU(list *list.List[*entry[K, V]]) {\n\tlru := list.Back()\n\tlist.Remove(lru)\n\ta.len--\n\tdelete(a.cache, lru.Value.key)\n}\n\nfunc (a *ARC[K, V]) replace(ent *entry[K, V]) {\n\tif a.t1.Len() > 0 && ((a.t1.Len() > a.p) || (ent.ll == a.b2 && a.t1.Len() == a.p)) {\n\t\tlru := a.t1.Back().Value\n\t\tlru.value = lo.Empty[V]()\n\t\tlru.ghost = true\n\t\ta.len--\n\t\tlru.setMRU(a.b1)\n\t} else {\n\t\tlru := a.t2.Back().Value\n\t\tlru.value = lo.Empty[V]()\n\t\tlru.ghost = true\n\t\ta.len--\n\t\tlru.setMRU(a.b2)\n\t}\n}\n\nfunc min(a, b int) int {\n\tif a < b {\n\t\treturn a\n\t}\n\treturn b\n}\n\nfunc max(a int, b int) int {\n\tif a < b {\n\t\treturn b\n\t}\n\treturn a\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/arc/arc_test.go",
    "content": "package arc\n\nimport (\n\t\"testing\"\n)\n\nfunc TestInsertion(t *testing.T) {\n\tcache := New[string, string](WithSize[string, string](3))\n\tif got, want := cache.Len(), 0; got != want {\n\t\tt.Errorf(\"empty cache.Len(): got %d want %d\", cache.Len(), want)\n\t}\n\n\tconst (\n\t\tk1 = \"Hello\"\n\t\tk2 = \"Hallo\"\n\t\tk3 = \"Ciao\"\n\t\tk4 = \"Salut\"\n\n\t\tv1 = \"World\"\n\t\tv2 = \"Worlds\"\n\t\tv3 = \"Welt\"\n\t)\n\n\t// Insert the first value\n\tcache.Set(k1, v1)\n\tif got, want := cache.Len(), 1; got != want {\n\t\tt.Errorf(\"insertion of key #%d: cache.Len(): got %d want %d\", want, cache.Len(), want)\n\t}\n\tif got, ok := cache.Get(k1); !ok || got != v1 {\n\t\tt.Errorf(\"cache.Get(%v): got (%v,%t) want (%v,true)\", k1, got, ok, v1)\n\t}\n\n\t// Replace existing value for a given key\n\tcache.Set(k1, v2)\n\tif got, want := cache.Len(), 1; got != want {\n\t\tt.Errorf(\"re-insertion: cache.Len(): got %d want %d\", cache.Len(), want)\n\t}\n\tif got, ok := cache.Get(k1); !ok || got != v2 {\n\t\tt.Errorf(\"re-insertion: cache.Get(%v): got (%v,%t) want (%v,true)\", k1, got, ok, v2)\n\t}\n\n\t// Add a second different key\n\tcache.Set(k2, v3)\n\tif got, want := cache.Len(), 2; got != want {\n\t\tt.Errorf(\"insertion of key #%d: cache.Len(): got %d want %d\", want, cache.Len(), want)\n\t}\n\tif got, ok := cache.Get(k1); !ok || got != v2 {\n\t\tt.Errorf(\"cache.Get(%v): got (%v,%t) want (%v,true)\", k1, got, ok, v2)\n\t}\n\tif got, ok := cache.Get(k2); !ok || got != v3 {\n\t\tt.Errorf(\"cache.Get(%v): got (%v,%t) want (%v,true)\", k2, got, ok, v3)\n\t}\n\n\t// Fill cache\n\tcache.Set(k3, v1)\n\tif got, want := cache.Len(), 3; got != want {\n\t\tt.Errorf(\"insertion of key #%d: cache.Len(): got %d want %d\", want, cache.Len(), want)\n\t}\n\n\t// Exceed size, this should not exceed size:\n\tcache.Set(k4, v1)\n\tif got, want := cache.Len(), 3; got != want {\n\t\tt.Errorf(\"insertion of key out of size: cache.Len(): got %d want %d\", cache.Len(), want)\n\t}\n}\n\nfunc TestEviction(t *testing.T) {\n\tsize := 3\n\tcache := New[string, string](WithSize[string, string](size))\n\tif got, want := cache.Len(), 0; got != want {\n\t\tt.Errorf(\"empty cache.Len(): got %d want %d\", cache.Len(), want)\n\t}\n\n\ttests := []struct {\n\t\tk, v string\n\t}{\n\t\t{\"k1\", \"v1\"},\n\t\t{\"k2\", \"v2\"},\n\t\t{\"k3\", \"v3\"},\n\t\t{\"k4\", \"v4\"},\n\t}\n\tfor i, tt := range tests[:size] {\n\t\tcache.Set(tt.k, tt.v)\n\t\tif got, want := cache.Len(), i+1; got != want {\n\t\t\tt.Errorf(\"insertion of key #%d: cache.Len(): got %d want %d\", want, cache.Len(), want)\n\t\t}\n\t}\n\n\t// Exceed size and check we don't outgrow it:\n\tcache.Set(tests[size].k, tests[size].v)\n\tif got := cache.Len(); got != size {\n\t\tt.Errorf(\"insertion of overflow key #%d: cache.Len(): got %d want %d\", 4, cache.Len(), size)\n\t}\n\n\t// Check that LRU got evicted:\n\tif got, ok := cache.Get(tests[0].k); ok || got != \"\" {\n\t\tt.Errorf(\"cache.Get(%v): got (%v,%t) want (<nil>,true)\", tests[0].k, got, ok)\n\t}\n\n\tfor _, tt := range tests[1:] {\n\t\tif got, ok := cache.Get(tt.k); !ok || got != tt.v {\n\t\t\tt.Errorf(\"cache.Get(%v): got (%v,%t) want (%v,true)\", tt.k, got, ok, tt.v)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/arc/entry.go",
    "content": "package arc\n\nimport (\n\tlist \"github.com/bahlo/generic-list-go\"\n)\n\ntype entry[K comparable, V any] struct {\n\tkey     K\n\tvalue   V\n\tll      *list.List[*entry[K, V]]\n\tel      *list.Element[*entry[K, V]]\n\tghost   bool\n\texpires int64\n}\n\nfunc (e *entry[K, V]) setLRU(list *list.List[*entry[K, V]]) {\n\te.detach()\n\te.ll = list\n\te.el = e.ll.PushBack(e)\n}\n\nfunc (e *entry[K, V]) setMRU(list *list.List[*entry[K, V]]) {\n\te.detach()\n\te.ll = list\n\te.el = e.ll.PushFront(e)\n}\n\nfunc (e *entry[K, V]) detach() {\n\tif e.ll != nil {\n\t\te.ll.Remove(e.el)\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/atomic/enum.go",
    "content": "package atomic\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"sync/atomic\"\n)\n\ntype Int32Enum[T ~int32] struct {\n\tvalue atomic.Int32\n}\n\nfunc (i *Int32Enum[T]) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(i.Load())\n}\n\nfunc (i *Int32Enum[T]) UnmarshalJSON(b []byte) error {\n\tvar v T\n\tif err := json.Unmarshal(b, &v); err != nil {\n\t\treturn err\n\t}\n\ti.Store(v)\n\treturn nil\n}\n\nfunc (i *Int32Enum[T]) MarshalYAML() (any, error) {\n\treturn i.Load(), nil\n}\n\nfunc (i *Int32Enum[T]) UnmarshalYAML(unmarshal func(any) error) error {\n\tvar v T\n\tif err := unmarshal(&v); err != nil {\n\t\treturn err\n\t}\n\ti.Store(v)\n\treturn nil\n}\n\nfunc (i *Int32Enum[T]) String() string {\n\treturn fmt.Sprint(i.Load())\n}\n\nfunc (i *Int32Enum[T]) Store(v T) {\n\ti.value.Store(int32(v))\n}\n\nfunc (i *Int32Enum[T]) Load() T {\n\treturn T(i.value.Load())\n}\n\nfunc (i *Int32Enum[T]) Swap(new T) T {\n\treturn T(i.value.Swap(int32(new)))\n}\n\nfunc (i *Int32Enum[T]) CompareAndSwap(old, new T) bool {\n\treturn i.value.CompareAndSwap(int32(old), int32(new))\n}\n\nfunc NewInt32Enum[T ~int32](v T) *Int32Enum[T] {\n\ta := &Int32Enum[T]{}\n\ta.Store(v)\n\treturn a\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/atomic/type.go",
    "content": "package atomic\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"sync/atomic\"\n)\n\ntype Bool struct {\n\tatomic.Bool\n}\n\nfunc NewBool(val bool) (i Bool) {\n\ti.Store(val)\n\treturn\n}\n\nfunc (i *Bool) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(i.Load())\n}\n\nfunc (i *Bool) UnmarshalJSON(b []byte) error {\n\tvar v bool\n\tif err := json.Unmarshal(b, &v); err != nil {\n\t\treturn err\n\t}\n\ti.Store(v)\n\treturn nil\n}\n\nfunc (i *Bool) MarshalYAML() (any, error) {\n\treturn i.Load(), nil\n}\n\nfunc (i *Bool) UnmarshalYAML(unmarshal func(any) error) error {\n\tvar v bool\n\tif err := unmarshal(&v); err != nil {\n\t\treturn err\n\t}\n\ti.Store(v)\n\treturn nil\n}\n\nfunc (i *Bool) String() string {\n\tv := i.Load()\n\treturn strconv.FormatBool(v)\n}\n\ntype Pointer[T any] struct {\n\tatomic.Pointer[T]\n}\n\nfunc NewPointer[T any](v *T) (p Pointer[T]) {\n\tif v != nil {\n\t\tp.Store(v)\n\t}\n\treturn\n}\n\nfunc (p *Pointer[T]) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(p.Load())\n}\n\nfunc (p *Pointer[T]) UnmarshalJSON(b []byte) error {\n\tvar v *T\n\tif err := json.Unmarshal(b, &v); err != nil {\n\t\treturn err\n\t}\n\tp.Store(v)\n\treturn nil\n}\n\nfunc (p *Pointer[T]) MarshalYAML() (any, error) {\n\treturn p.Load(), nil\n}\n\nfunc (p *Pointer[T]) UnmarshalYAML(unmarshal func(any) error) error {\n\tvar v *T\n\tif err := unmarshal(&v); err != nil {\n\t\treturn err\n\t}\n\tp.Store(v)\n\treturn nil\n}\n\nfunc (p *Pointer[T]) String() string {\n\treturn fmt.Sprint(p.Load())\n}\n\ntype Int32 struct {\n\tatomic.Int32\n}\n\nfunc NewInt32(val int32) (i Int32) {\n\ti.Store(val)\n\treturn\n}\n\nfunc (i *Int32) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(i.Load())\n}\n\nfunc (i *Int32) UnmarshalJSON(b []byte) error {\n\tvar v int32\n\tif err := json.Unmarshal(b, &v); err != nil {\n\t\treturn err\n\t}\n\ti.Store(v)\n\treturn nil\n}\n\nfunc (i *Int32) MarshalYAML() (any, error) {\n\treturn i.Load(), nil\n}\n\nfunc (i *Int32) UnmarshalYAML(unmarshal func(any) error) error {\n\tvar v int32\n\tif err := unmarshal(&v); err != nil {\n\t\treturn err\n\t}\n\ti.Store(v)\n\treturn nil\n}\n\nfunc (i *Int32) String() string {\n\tv := i.Load()\n\treturn strconv.FormatInt(int64(v), 10)\n}\n\ntype Int64 struct {\n\tatomic.Int64\n}\n\nfunc NewInt64(val int64) (i Int64) {\n\ti.Store(val)\n\treturn\n}\n\nfunc (i *Int64) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(i.Load())\n}\n\nfunc (i *Int64) UnmarshalJSON(b []byte) error {\n\tvar v int64\n\tif err := json.Unmarshal(b, &v); err != nil {\n\t\treturn err\n\t}\n\ti.Store(v)\n\treturn nil\n}\n\nfunc (i *Int64) MarshalYAML() (any, error) {\n\treturn i.Load(), nil\n}\n\nfunc (i *Int64) UnmarshalYAML(unmarshal func(any) error) error {\n\tvar v int64\n\tif err := unmarshal(&v); err != nil {\n\t\treturn err\n\t}\n\ti.Store(v)\n\treturn nil\n}\n\nfunc (i *Int64) String() string {\n\tv := i.Load()\n\treturn strconv.FormatInt(int64(v), 10)\n}\n\ntype Uint32 struct {\n\tatomic.Uint32\n}\n\nfunc NewUint32(val uint32) (i Uint32) {\n\ti.Store(val)\n\treturn\n}\n\nfunc (i *Uint32) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(i.Load())\n}\n\nfunc (i *Uint32) UnmarshalJSON(b []byte) error {\n\tvar v uint32\n\tif err := json.Unmarshal(b, &v); err != nil {\n\t\treturn err\n\t}\n\ti.Store(v)\n\treturn nil\n}\n\nfunc (i *Uint32) MarshalYAML() (any, error) {\n\treturn i.Load(), nil\n}\n\nfunc (i *Uint32) UnmarshalYAML(unmarshal func(any) error) error {\n\tvar v uint32\n\tif err := unmarshal(&v); err != nil {\n\t\treturn err\n\t}\n\ti.Store(v)\n\treturn nil\n}\n\nfunc (i *Uint32) String() string {\n\tv := i.Load()\n\treturn strconv.FormatUint(uint64(v), 10)\n}\n\ntype Uint64 struct {\n\tatomic.Uint64\n}\n\nfunc NewUint64(val uint64) (i Uint64) {\n\ti.Store(val)\n\treturn\n}\n\nfunc (i *Uint64) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(i.Load())\n}\n\nfunc (i *Uint64) UnmarshalJSON(b []byte) error {\n\tvar v uint64\n\tif err := json.Unmarshal(b, &v); err != nil {\n\t\treturn err\n\t}\n\ti.Store(v)\n\treturn nil\n}\n\nfunc (i *Uint64) MarshalYAML() (any, error) {\n\treturn i.Load(), nil\n}\n\nfunc (i *Uint64) UnmarshalYAML(unmarshal func(any) error) error {\n\tvar v uint64\n\tif err := unmarshal(&v); err != nil {\n\t\treturn err\n\t}\n\ti.Store(v)\n\treturn nil\n}\n\nfunc (i *Uint64) String() string {\n\tv := i.Load()\n\treturn strconv.FormatUint(uint64(v), 10)\n}\n\ntype Uintptr struct {\n\tatomic.Uintptr\n}\n\nfunc NewUintptr(val uintptr) (i Uintptr) {\n\ti.Store(val)\n\treturn\n}\n\nfunc (i *Uintptr) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(i.Load())\n}\n\nfunc (i *Uintptr) UnmarshalJSON(b []byte) error {\n\tvar v uintptr\n\tif err := json.Unmarshal(b, &v); err != nil {\n\t\treturn err\n\t}\n\ti.Store(v)\n\treturn nil\n}\n\nfunc (i *Uintptr) MarshalYAML() (any, error) {\n\treturn i.Load(), nil\n}\n\nfunc (i *Uintptr) UnmarshalYAML(unmarshal func(any) error) error {\n\tvar v uintptr\n\tif err := unmarshal(&v); err != nil {\n\t\treturn err\n\t}\n\ti.Store(v)\n\treturn nil\n}\n\nfunc (i *Uintptr) String() string {\n\tv := i.Load()\n\treturn strconv.FormatUint(uint64(v), 10)\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/atomic/value.go",
    "content": "package atomic\n\nimport (\n\t\"encoding/json\"\n\t\"sync/atomic\"\n)\n\ntype TypedValue[T any] struct {\n\tvalue atomic.Pointer[T]\n}\n\nfunc (t *TypedValue[T]) Load() (v T) {\n\tv, _ = t.LoadOk()\n\treturn\n}\n\nfunc (t *TypedValue[T]) LoadOk() (v T, ok bool) {\n\tvalue := t.value.Load()\n\tif value == nil {\n\t\treturn\n\t}\n\treturn *value, true\n}\n\nfunc (t *TypedValue[T]) Store(value T) {\n\tt.value.Store(&value)\n}\n\nfunc (t *TypedValue[T]) Swap(new T) (v T) {\n\told := t.value.Swap(&new)\n\tif old == nil {\n\t\treturn\n\t}\n\treturn *old\n}\n\nfunc (t *TypedValue[T]) CompareAndSwap(old, new T) bool {\n\tfor {\n\t\tcurrentP := t.value.Load()\n\t\tvar currentValue T\n\t\tif currentP != nil {\n\t\t\tcurrentValue = *currentP\n\t\t}\n\t\t// Compare old and current via runtime equality check.\n\t\tif any(currentValue) != any(old) {\n\t\t\treturn false\n\t\t}\n\t\tif t.value.CompareAndSwap(currentP, &new) {\n\t\t\treturn true\n\t\t}\n\t}\n}\n\nfunc (t *TypedValue[T]) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(t.Load())\n}\n\nfunc (t *TypedValue[T]) UnmarshalJSON(b []byte) error {\n\tvar v T\n\tif err := json.Unmarshal(b, &v); err != nil {\n\t\treturn err\n\t}\n\tt.Store(v)\n\treturn nil\n}\n\nfunc (t *TypedValue[T]) MarshalYAML() (any, error) {\n\treturn t.Load(), nil\n}\n\nfunc (t *TypedValue[T]) UnmarshalYAML(unmarshal func(any) error) error {\n\tvar v T\n\tif err := unmarshal(&v); err != nil {\n\t\treturn err\n\t}\n\tt.Store(v)\n\treturn nil\n}\n\nfunc NewTypedValue[T any](t T) (v TypedValue[T]) {\n\tv.Store(t)\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/atomic/value_test.go",
    "content": "package atomic\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"testing\"\n)\n\nfunc TestTypedValue(t *testing.T) {\n\t{\n\t\tvar v TypedValue[int]\n\t\tgot, gotOk := v.LoadOk()\n\t\tif got != 0 || gotOk {\n\t\t\tt.Fatalf(\"LoadOk = (%v, %v), want (0, false)\", got, gotOk)\n\t\t}\n\t\tv.Store(1)\n\t\tgot, gotOk = v.LoadOk()\n\t\tif got != 1 || !gotOk {\n\t\t\tt.Fatalf(\"LoadOk = (%v, %v), want (1, true)\", got, gotOk)\n\t\t}\n\t}\n\n\t{\n\t\tvar v TypedValue[error]\n\t\tgot, gotOk := v.LoadOk()\n\t\tif got != nil || gotOk {\n\t\t\tt.Fatalf(\"LoadOk = (%v, %v), want (nil, false)\", got, gotOk)\n\t\t}\n\t\tv.Store(io.EOF)\n\t\tgot, gotOk = v.LoadOk()\n\t\tif got != io.EOF || !gotOk {\n\t\t\tt.Fatalf(\"LoadOk = (%v, %v), want (EOF, true)\", got, gotOk)\n\t\t}\n\t\terr := &os.PathError{}\n\t\tv.Store(err)\n\t\tgot, gotOk = v.LoadOk()\n\t\tif got != err || !gotOk {\n\t\t\tt.Fatalf(\"LoadOk = (%v, %v), want (%v, true)\", got, gotOk, err)\n\t\t}\n\t\tv.Store(nil)\n\t\tgot, gotOk = v.LoadOk()\n\t\tif got != nil || !gotOk {\n\t\t\tt.Fatalf(\"LoadOk = (%v, %v), want (nil, true)\", got, gotOk)\n\t\t}\n\t}\n\n\t{\n\t\te1, e2, e3 := io.EOF, &os.PathError{}, &os.PathError{}\n\t\tvar v TypedValue[error]\n\t\tif v.CompareAndSwap(e1, e2) != false {\n\t\t\tt.Fatalf(\"CompareAndSwap = true, want false\")\n\t\t}\n\t\tif value := v.Load(); value != nil {\n\t\t\tt.Fatalf(\"Load = (%v), want (%v)\", value, nil)\n\t\t}\n\t\tif v.CompareAndSwap(nil, e1) != true {\n\t\t\tt.Fatalf(\"CompareAndSwap = false, want true\")\n\t\t}\n\t\tif value := v.Load(); value != e1 {\n\t\t\tt.Fatalf(\"Load = (%v), want (%v)\", value, e1)\n\t\t}\n\t\tif v.CompareAndSwap(e2, e3) != false {\n\t\t\tt.Fatalf(\"CompareAndSwap = true, want false\")\n\t\t}\n\t\tif value := v.Load(); value != e1 {\n\t\t\tt.Fatalf(\"Load = (%v), want (%v)\", value, e1)\n\t\t}\n\t\tif v.CompareAndSwap(e1, e2) != true {\n\t\t\tt.Fatalf(\"CompareAndSwap = false, want true\")\n\t\t}\n\t\tif value := v.Load(); value != e2 {\n\t\t\tt.Fatalf(\"Load = (%v), want (%v)\", value, e2)\n\t\t}\n\t\tif v.CompareAndSwap(e3, e2) != false {\n\t\t\tt.Fatalf(\"CompareAndSwap = true, want false\")\n\t\t}\n\t\tif value := v.Load(); value != e2 {\n\t\t\tt.Fatalf(\"Load = (%v), want (%v)\", value, e2)\n\t\t}\n\t\tif v.CompareAndSwap(nil, e3) != false {\n\t\t\tt.Fatalf(\"CompareAndSwap = true, want false\")\n\t\t}\n\t\tif value := v.Load(); value != e2 {\n\t\t\tt.Fatalf(\"Load = (%v), want (%v)\", value, e2)\n\t\t}\n\t}\n\n\t{\n\t\tc1, c2, c3 := make(chan struct{}), make(chan struct{}), make(chan struct{})\n\t\tvar v TypedValue[chan struct{}]\n\t\tif v.CompareAndSwap(c1, c2) != false {\n\t\t\tt.Fatalf(\"CompareAndSwap = true, want false\")\n\t\t}\n\t\tif value := v.Load(); value != nil {\n\t\t\tt.Fatalf(\"Load = (%v), want (%v)\", value, nil)\n\t\t}\n\t\tif v.CompareAndSwap(nil, c1) != true {\n\t\t\tt.Fatalf(\"CompareAndSwap = false, want true\")\n\t\t}\n\t\tif value := v.Load(); value != c1 {\n\t\t\tt.Fatalf(\"Load = (%v), want (%v)\", value, c1)\n\t\t}\n\t\tif v.CompareAndSwap(c2, c3) != false {\n\t\t\tt.Fatalf(\"CompareAndSwap = true, want false\")\n\t\t}\n\t\tif value := v.Load(); value != c1 {\n\t\t\tt.Fatalf(\"Load = (%v), want (%v)\", value, c1)\n\t\t}\n\t\tif v.CompareAndSwap(c1, c2) != true {\n\t\t\tt.Fatalf(\"CompareAndSwap = false, want true\")\n\t\t}\n\t\tif value := v.Load(); value != c2 {\n\t\t\tt.Fatalf(\"Load = (%v), want (%v)\", value, c2)\n\t\t}\n\t\tif v.CompareAndSwap(c3, c2) != false {\n\t\t\tt.Fatalf(\"CompareAndSwap = true, want false\")\n\t\t}\n\t\tif value := v.Load(); value != c2 {\n\t\t\tt.Fatalf(\"Load = (%v), want (%v)\", value, c2)\n\t\t}\n\t\tif v.CompareAndSwap(nil, c3) != false {\n\t\t\tt.Fatalf(\"CompareAndSwap = true, want false\")\n\t\t}\n\t\tif value := v.Load(); value != c2 {\n\t\t\tt.Fatalf(\"Load = (%v), want (%v)\", value, c2)\n\t\t}\n\t}\n\n\t{\n\t\tc1, c2, c3 := &io.LimitedReader{}, &io.SectionReader{}, &io.SectionReader{}\n\t\tvar v TypedValue[io.Reader]\n\t\tif v.CompareAndSwap(c1, c2) != false {\n\t\t\tt.Fatalf(\"CompareAndSwap = true, want false\")\n\t\t}\n\t\tif value := v.Load(); value != nil {\n\t\t\tt.Fatalf(\"Load = (%v), want (%v)\", value, nil)\n\t\t}\n\t\tif v.CompareAndSwap(nil, c1) != true {\n\t\t\tt.Fatalf(\"CompareAndSwap = false, want true\")\n\t\t}\n\t\tif value := v.Load(); value != c1 {\n\t\t\tt.Fatalf(\"Load = (%v), want (%v)\", value, c1)\n\t\t}\n\t\tif v.CompareAndSwap(c2, c3) != false {\n\t\t\tt.Fatalf(\"CompareAndSwap = true, want false\")\n\t\t}\n\t\tif value := v.Load(); value != c1 {\n\t\t\tt.Fatalf(\"Load = (%v), want (%v)\", value, c1)\n\t\t}\n\t\tif v.CompareAndSwap(c1, c2) != true {\n\t\t\tt.Fatalf(\"CompareAndSwap = false, want true\")\n\t\t}\n\t\tif value := v.Load(); value != c2 {\n\t\t\tt.Fatalf(\"Load = (%v), want (%v)\", value, c2)\n\t\t}\n\t\tif v.CompareAndSwap(c3, c2) != false {\n\t\t\tt.Fatalf(\"CompareAndSwap = true, want false\")\n\t\t}\n\t\tif value := v.Load(); value != c2 {\n\t\t\tt.Fatalf(\"Load = (%v), want (%v)\", value, c2)\n\t\t}\n\t\tif v.CompareAndSwap(nil, c3) != false {\n\t\t\tt.Fatalf(\"CompareAndSwap = true, want false\")\n\t\t}\n\t\tif value := v.Load(); value != c2 {\n\t\t\tt.Fatalf(\"Load = (%v), want (%v)\", value, c2)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/batch/batch.go",
    "content": "package batch\n\nimport (\n\t\"context\"\n\t\"sync\"\n)\n\ntype Option[T any] func(b *Batch[T])\n\ntype Result[T any] struct {\n\tValue T\n\tErr   error\n}\n\ntype Error struct {\n\tKey string\n\tErr error\n}\n\nfunc WithConcurrencyNum[T any](n int) Option[T] {\n\treturn func(b *Batch[T]) {\n\t\tq := make(chan struct{}, n)\n\t\tfor i := 0; i < n; i++ {\n\t\t\tq <- struct{}{}\n\t\t}\n\t\tb.queue = q\n\t}\n}\n\n// Batch similar to errgroup, but can control the maximum number of concurrent\ntype Batch[T any] struct {\n\tresult map[string]Result[T]\n\tqueue  chan struct{}\n\twg     sync.WaitGroup\n\tmux    sync.Mutex\n\terr    *Error\n\tonce   sync.Once\n\tcancel func()\n}\n\nfunc (b *Batch[T]) Go(key string, fn func() (T, error)) {\n\tb.wg.Add(1)\n\tgo func() {\n\t\tdefer b.wg.Done()\n\t\tif b.queue != nil {\n\t\t\t<-b.queue\n\t\t\tdefer func() {\n\t\t\t\tb.queue <- struct{}{}\n\t\t\t}()\n\t\t}\n\n\t\tvalue, err := fn()\n\t\tif err != nil {\n\t\t\tb.once.Do(func() {\n\t\t\t\tb.err = &Error{key, err}\n\t\t\t\tif b.cancel != nil {\n\t\t\t\t\tb.cancel()\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\n\t\tret := Result[T]{value, err}\n\t\tb.mux.Lock()\n\t\tdefer b.mux.Unlock()\n\t\tb.result[key] = ret\n\t}()\n}\n\nfunc (b *Batch[T]) Wait() *Error {\n\tb.wg.Wait()\n\tif b.cancel != nil {\n\t\tb.cancel()\n\t}\n\treturn b.err\n}\n\nfunc (b *Batch[T]) WaitAndGetResult() (map[string]Result[T], *Error) {\n\terr := b.Wait()\n\treturn b.Result(), err\n}\n\nfunc (b *Batch[T]) Result() map[string]Result[T] {\n\tb.mux.Lock()\n\tdefer b.mux.Unlock()\n\tcopyM := map[string]Result[T]{}\n\tfor k, v := range b.result {\n\t\tcopyM[k] = v\n\t}\n\treturn copyM\n}\n\nfunc New[T any](ctx context.Context, opts ...Option[T]) (*Batch[T], context.Context) {\n\tctx, cancel := context.WithCancel(ctx)\n\n\tb := &Batch[T]{\n\t\tresult: map[string]Result[T]{},\n\t}\n\n\tfor _, o := range opts {\n\t\to(b)\n\t}\n\n\tb.cancel = cancel\n\treturn b, ctx\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/batch/batch_test.go",
    "content": "package batch\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"strconv\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestBatch(t *testing.T) {\n\tb, _ := New[string](context.Background())\n\n\tnow := time.Now()\n\tb.Go(\"foo\", func() (string, error) {\n\t\ttime.Sleep(time.Millisecond * 100)\n\t\treturn \"foo\", nil\n\t})\n\tb.Go(\"bar\", func() (string, error) {\n\t\ttime.Sleep(time.Millisecond * 150)\n\t\treturn \"bar\", nil\n\t})\n\tresult, err := b.WaitAndGetResult()\n\n\tassert.Nil(t, err)\n\n\tduration := time.Since(now)\n\tassert.Less(t, duration, time.Millisecond*200)\n\tassert.Equal(t, 2, len(result))\n\n\tfor k, v := range result {\n\t\tassert.NoError(t, v.Err)\n\t\tassert.Equal(t, k, v.Value)\n\t}\n}\n\nfunc TestBatchWithConcurrencyNum(t *testing.T) {\n\tb, _ := New[string](\n\t\tcontext.Background(),\n\t\tWithConcurrencyNum[string](3),\n\t)\n\n\tnow := time.Now()\n\tfor i := 0; i < 7; i++ {\n\t\tidx := i\n\t\tb.Go(strconv.Itoa(idx), func() (string, error) {\n\t\t\ttime.Sleep(time.Millisecond * 100)\n\t\t\treturn strconv.Itoa(idx), nil\n\t\t})\n\t}\n\tresult, _ := b.WaitAndGetResult()\n\tduration := time.Since(now)\n\tassert.Greater(t, duration, time.Millisecond*260)\n\tassert.Equal(t, 7, len(result))\n\n\tfor k, v := range result {\n\t\tassert.NoError(t, v.Err)\n\t\tassert.Equal(t, k, v.Value)\n\t}\n}\n\nfunc TestBatchContext(t *testing.T) {\n\tb, ctx := New[string](context.Background())\n\n\tb.Go(\"error\", func() (string, error) {\n\t\ttime.Sleep(time.Millisecond * 100)\n\t\treturn \"\", errors.New(\"test error\")\n\t})\n\n\tb.Go(\"ctx\", func() (string, error) {\n\t\t<-ctx.Done()\n\t\treturn \"\", ctx.Err()\n\t})\n\n\tresult, err := b.WaitAndGetResult()\n\n\tassert.NotNil(t, err)\n\tassert.Equal(t, \"error\", err.Key)\n\n\tassert.Equal(t, ctx.Err(), result[\"ctx\"].Err)\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/buf/sing.go",
    "content": "package buf\n\nimport (\n\t\"github.com/metacubex/sing/common/buf\"\n)\n\nconst BufferSize = buf.BufferSize\n\ntype Buffer = buf.Buffer\n\nfunc New() *Buffer {\n\treturn buf.New()\n}\n\nfunc NewPacket() *Buffer {\n\treturn buf.NewPacket()\n}\n\nfunc NewSize(size int) *Buffer {\n\treturn buf.NewSize(size)\n}\n\nfunc With(data []byte) *Buffer {\n\treturn buf.With(data)\n}\n\nfunc As(data []byte) *Buffer {\n\treturn buf.As(data)\n}\n\nfunc ReleaseMulti(buffers []*Buffer) {\n\tbuf.ReleaseMulti(buffers)\n}\n\nfunc Error(_ any, err error) error {\n\treturn err\n}\n\nfunc Must(errs ...error) {\n\tfor _, err := range errs {\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n}\n\nfunc Must1[T any](result T, err error) T {\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn result\n}\n\nfunc Must2[T any, T2 any](result T, result2 T2, err error) (T, T2) {\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn result, result2\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/callback/callback.go",
    "content": "package callback\n\nimport (\n\t\"github.com/metacubex/mihomo/common/buf\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\ntype firstWriteCallBackConn struct {\n\tC.Conn\n\tcallback func(error)\n\twritten  bool\n}\n\nfunc (c *firstWriteCallBackConn) Write(b []byte) (n int, err error) {\n\tdefer func() {\n\t\tif !c.written {\n\t\t\tc.written = true\n\t\t\tc.callback(err)\n\t\t}\n\t}()\n\treturn c.Conn.Write(b)\n}\n\nfunc (c *firstWriteCallBackConn) WriteBuffer(buffer *buf.Buffer) (err error) {\n\tdefer func() {\n\t\tif !c.written {\n\t\t\tc.written = true\n\t\t\tc.callback(err)\n\t\t}\n\t}()\n\treturn c.Conn.WriteBuffer(buffer)\n}\n\nfunc (c *firstWriteCallBackConn) Upstream() any {\n\treturn c.Conn\n}\n\nfunc (c *firstWriteCallBackConn) WriterReplaceable() bool {\n\treturn c.written\n}\n\nfunc (c *firstWriteCallBackConn) ReaderReplaceable() bool {\n\treturn true\n}\n\nvar _ N.ExtendedConn = (*firstWriteCallBackConn)(nil)\n\nfunc NewFirstWriteCallBackConn(c C.Conn, callback func(error)) C.Conn {\n\treturn &firstWriteCallBackConn{\n\t\tConn:     c,\n\t\tcallback: callback,\n\t\twritten:  false,\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/callback/close_callback.go",
    "content": "package callback\n\nimport (\n\t\"sync\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\ntype closeCallbackConn struct {\n\tC.Conn\n\tcloseFunc func()\n\tcloseOnce sync.Once\n}\n\nfunc (w *closeCallbackConn) Close() error {\n\tw.closeOnce.Do(w.closeFunc)\n\treturn w.Conn.Close()\n}\n\nfunc (w *closeCallbackConn) ReaderReplaceable() bool {\n\treturn true\n}\n\nfunc (w *closeCallbackConn) WriterReplaceable() bool {\n\treturn true\n}\n\nfunc (w *closeCallbackConn) Upstream() any {\n\treturn w.Conn\n}\n\nfunc NewCloseCallbackConn(conn C.Conn, callback func()) C.Conn {\n\treturn &closeCallbackConn{Conn: conn, closeFunc: callback}\n}\n\ntype closeCallbackPacketConn struct {\n\tC.PacketConn\n\tcloseFunc func()\n\tcloseOnce sync.Once\n}\n\nfunc (w *closeCallbackPacketConn) Close() error {\n\tw.closeOnce.Do(w.closeFunc)\n\treturn w.PacketConn.Close()\n}\n\nfunc (w *closeCallbackPacketConn) ReaderReplaceable() bool {\n\treturn true\n}\n\nfunc (w *closeCallbackPacketConn) WriterReplaceable() bool {\n\treturn true\n}\n\nfunc (w *closeCallbackPacketConn) Upstream() any {\n\treturn w.PacketConn\n}\n\nfunc NewCloseCallbackPacketConn(conn C.PacketConn, callback func()) C.PacketConn {\n\treturn &closeCallbackPacketConn{PacketConn: conn, closeFunc: callback}\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/cmd/cmd.go",
    "content": "package cmd\n\nimport (\n\t\"fmt\"\n\t\"os/exec\"\n\t\"runtime\"\n\t\"strings\"\n)\n\nfunc ExecCmd(cmdStr string) (string, error) {\n\targs := splitArgs(cmdStr)\n\n\tvar cmd *exec.Cmd\n\tif len(args) == 1 {\n\t\tcmd = exec.Command(args[0])\n\t} else {\n\t\tcmd = exec.Command(args[0], args[1:]...)\n\n\t}\n\tprepareBackgroundCommand(cmd)\n\tout, err := cmd.CombinedOutput()\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"%v, %s\", err, string(out))\n\t}\n\treturn string(out), nil\n}\n\nfunc splitArgs(cmd string) []string {\n\targs := strings.Split(cmd, \" \")\n\n\t// use in pipeline\n\tif len(args) > 2 && strings.ContainsAny(cmd, \"|\") {\n\t\tsuffix := strings.Join(args[2:], \" \")\n\t\targs = append(args[:2], suffix)\n\t}\n\treturn args\n}\n\nfunc ExecShell(shellStr string) (string, error) {\n\tvar cmd *exec.Cmd\n\tif runtime.GOOS == \"windows\" {\n\t\tcmd = exec.Command(\"cmd.exe\", \"/C\", shellStr)\n\t} else {\n\t\tcmd = exec.Command(\"sh\", \"-c\", shellStr)\n\t}\n\n\tprepareBackgroundCommand(cmd)\n\tout, err := cmd.CombinedOutput()\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"%v, %s\", err, string(out))\n\t}\n\treturn string(out), nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/cmd/cmd_other.go",
    "content": "//go:build !windows\n\npackage cmd\n\nimport (\n\t\"os/exec\"\n)\n\nfunc prepareBackgroundCommand(cmd *exec.Cmd) {\n\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/cmd/cmd_test.go",
    "content": "package cmd\n\nimport (\n\t\"runtime\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestSplitArgs(t *testing.T) {\n\targs := splitArgs(\"ls\")\n\targs1 := splitArgs(\"ls -la\")\n\targs2 := splitArgs(\"bash -c ls\")\n\targs3 := splitArgs(\"bash -c ls -lahF | grep 'cmd'\")\n\n\tassert.Equal(t, 1, len(args))\n\tassert.Equal(t, 2, len(args1))\n\tassert.Equal(t, 3, len(args2))\n\tassert.Equal(t, 3, len(args3))\n}\n\nfunc TestExecCmd(t *testing.T) {\n\tif runtime.GOOS == \"windows\" {\n\t\t_, err := ExecCmd(\"cmd -c 'dir'\")\n\t\tassert.Nil(t, err)\n\t\treturn\n\t}\n\n\t_, err := ExecCmd(\"ls\")\n\t_, err1 := ExecCmd(\"ls -la\")\n\t_, err2 := ExecCmd(\"bash -c ls\")\n\t_, err3 := ExecCmd(\"bash -c ls -la\")\n\t_, err4 := ExecCmd(\"bash -c ls -la | grep 'cmd'\")\n\n\tassert.Nil(t, err)\n\tassert.Nil(t, err1)\n\tassert.Nil(t, err2)\n\tassert.Nil(t, err3)\n\tassert.Nil(t, err4)\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/cmd/cmd_windows.go",
    "content": "//go:build windows\n\npackage cmd\n\nimport (\n\t\"os/exec\"\n\t\"syscall\"\n)\n\nfunc prepareBackgroundCommand(cmd *exec.Cmd) {\n\tcmd.SysProcAttr = &syscall.SysProcAttr{HideWindow: true}\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/contextutils/afterfunc_compact.go",
    "content": "package contextutils\n\nimport (\n\t\"context\"\n\t\"sync\"\n)\n\nfunc afterFunc(ctx context.Context, f func()) (stop func() bool) {\n\tstopc := make(chan struct{})\n\tonce := sync.Once{} // either starts running f or stops f from running\n\tif ctx.Done() != nil {\n\t\tgo func() {\n\t\t\tselect {\n\t\t\tcase <-ctx.Done():\n\t\t\t\tonce.Do(func() {\n\t\t\t\t\tgo f()\n\t\t\t\t})\n\t\t\tcase <-stopc:\n\t\t\t}\n\t\t}()\n\t}\n\n\treturn func() bool {\n\t\tstopped := false\n\t\tonce.Do(func() {\n\t\t\tstopped = true\n\t\t\tclose(stopc)\n\t\t})\n\t\treturn stopped\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/contextutils/afterfunc_go120.go",
    "content": "//go:build !go1.21\n\npackage contextutils\n\nimport (\n\t\"context\"\n)\n\nfunc AfterFunc(ctx context.Context, f func()) (stop func() bool) {\n\treturn afterFunc(ctx, f)\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/contextutils/afterfunc_go121.go",
    "content": "//go:build go1.21\n\npackage contextutils\n\nimport \"context\"\n\nfunc AfterFunc(ctx context.Context, f func()) (stop func() bool) {\n\treturn context.AfterFunc(ctx, f)\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/contextutils/afterfunc_test.go",
    "content": "package contextutils\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n)\n\nconst (\n\tshortDuration    = 1 * time.Millisecond // a reasonable duration to block in a test\n\tveryLongDuration = 1000 * time.Hour     // an arbitrary upper bound on the test's running time\n)\n\nfunc TestAfterFuncCalledAfterCancel(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdonec := make(chan struct{})\n\tstop := afterFunc(ctx, func() {\n\t\tclose(donec)\n\t})\n\tselect {\n\tcase <-donec:\n\t\tt.Fatalf(\"AfterFunc called before context is done\")\n\tcase <-time.After(shortDuration):\n\t}\n\tcancel()\n\tselect {\n\tcase <-donec:\n\tcase <-time.After(veryLongDuration):\n\t\tt.Fatalf(\"AfterFunc not called after context is canceled\")\n\t}\n\tif stop() {\n\t\tt.Fatalf(\"stop() = true, want false\")\n\t}\n}\n\nfunc TestAfterFuncCalledAfterTimeout(t *testing.T) {\n\tctx, cancel := context.WithTimeout(context.Background(), shortDuration)\n\tdefer cancel()\n\tdonec := make(chan struct{})\n\tafterFunc(ctx, func() {\n\t\tclose(donec)\n\t})\n\tselect {\n\tcase <-donec:\n\tcase <-time.After(veryLongDuration):\n\t\tt.Fatalf(\"AfterFunc not called after context is canceled\")\n\t}\n}\n\nfunc TestAfterFuncCalledImmediately(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tcancel()\n\tdonec := make(chan struct{})\n\tafterFunc(ctx, func() {\n\t\tclose(donec)\n\t})\n\tselect {\n\tcase <-donec:\n\tcase <-time.After(veryLongDuration):\n\t\tt.Fatalf(\"AfterFunc not called for already-canceled context\")\n\t}\n}\n\nfunc TestAfterFuncNotCalledAfterStop(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdonec := make(chan struct{})\n\tstop := afterFunc(ctx, func() {\n\t\tclose(donec)\n\t})\n\tif !stop() {\n\t\tt.Fatalf(\"stop() = false, want true\")\n\t}\n\tcancel()\n\tselect {\n\tcase <-donec:\n\t\tt.Fatalf(\"AfterFunc called for already-canceled context\")\n\tcase <-time.After(shortDuration):\n\t}\n\tif stop() {\n\t\tt.Fatalf(\"stop() = true, want false\")\n\t}\n}\n\n// This test verifies that canceling a context does not block waiting for AfterFuncs to finish.\nfunc TestAfterFuncCalledAsynchronously(t *testing.T) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tdonec := make(chan struct{})\n\tstop := afterFunc(ctx, func() {\n\t\t// The channel send blocks until donec is read from.\n\t\tdonec <- struct{}{}\n\t})\n\tdefer stop()\n\tcancel()\n\t// After cancel returns, read from donec and unblock the AfterFunc.\n\tselect {\n\tcase <-donec:\n\tcase <-time.After(veryLongDuration):\n\t\tt.Fatalf(\"AfterFunc not called after context is canceled\")\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/contextutils/withoutcancel_compact.go",
    "content": "package contextutils\n\nimport (\n\t\"context\"\n\t\"time\"\n)\n\ntype withoutCancelCtx struct {\n\tc context.Context\n}\n\nfunc (withoutCancelCtx) Deadline() (deadline time.Time, ok bool) {\n\treturn\n}\n\nfunc (withoutCancelCtx) Done() <-chan struct{} {\n\treturn nil\n}\n\nfunc (withoutCancelCtx) Err() error {\n\treturn nil\n}\n\nfunc (c withoutCancelCtx) Value(key any) any {\n\treturn c.c.Value(key)\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/contextutils/withoutcancel_go120.go",
    "content": "//go:build !go1.21\n\npackage contextutils\n\nimport \"context\"\n\nfunc WithoutCancel(parent context.Context) context.Context {\n\tif parent == nil {\n\t\tpanic(\"cannot create context from nil parent\")\n\t}\n\treturn withoutCancelCtx{parent}\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/contextutils/withoutcancel_go121.go",
    "content": "//go:build go1.21\n\npackage contextutils\n\nimport \"context\"\n\nfunc WithoutCancel(parent context.Context) context.Context {\n\treturn context.WithoutCancel(parent)\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/convert/base64.go",
    "content": "package convert\n\nimport (\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"strings\"\n)\n\nvar (\n\tencRaw = base64.RawStdEncoding\n\tenc    = base64.StdEncoding\n)\n\n// DecodeBase64 try to decode content from the given bytes,\n// which can be in base64.RawStdEncoding, base64.StdEncoding or just plaintext.\nfunc DecodeBase64(buf []byte) []byte {\n\tresult, err := tryDecodeBase64(buf)\n\tif err != nil {\n\t\treturn buf\n\t}\n\treturn result\n}\n\nfunc tryDecodeBase64(buf []byte) ([]byte, error) {\n\tdBuf := make([]byte, encRaw.DecodedLen(len(buf)))\n\tn, err := encRaw.Decode(dBuf, buf)\n\tif err != nil {\n\t\tn, err = enc.Decode(dBuf, buf)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn dBuf[:n], nil\n}\n\nfunc urlSafe(data string) string {\n\treturn strings.NewReplacer(\"+\", \"-\", \"/\", \"_\").Replace(data)\n}\n\nfunc decodeUrlSafe(data string) string {\n\tdcBuf, err := base64.RawURLEncoding.DecodeString(data)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn string(dcBuf)\n}\n\nfunc TryDecodeBase64(s string) (decoded []byte, err error) {\n\tif len(s)%4 == 0 {\n\t\tif decoded, err = base64.StdEncoding.DecodeString(s); err == nil {\n\t\t\treturn\n\t\t}\n\t\tif decoded, err = base64.URLEncoding.DecodeString(s); err == nil {\n\t\t\treturn\n\t\t}\n\t} else {\n\t\tif decoded, err = base64.RawStdEncoding.DecodeString(s); err == nil {\n\t\t\treturn\n\t\t}\n\t\tif decoded, err = base64.RawURLEncoding.DecodeString(s); err == nil {\n\t\t\treturn\n\t\t}\n\t}\n\treturn nil, fmt.Errorf(\"invalid base64-encoded string\")\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/convert/converter.go",
    "content": "package convert\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/log\"\n)\n\n// ConvertsV2Ray convert V2Ray subscribe proxies data to mihomo proxies config\nfunc ConvertsV2Ray(buf []byte) ([]map[string]any, error) {\n\tdata := DecodeBase64(buf)\n\n\tarr := strings.Split(string(data), \"\\n\")\n\n\tproxies := make([]map[string]any, 0, len(arr))\n\tnames := make(map[string]int, 200)\n\n\tfor _, line := range arr {\n\t\tline = strings.TrimRight(line, \" \\r\")\n\t\tif line == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tscheme, body, found := strings.Cut(line, \"://\")\n\t\tif !found {\n\t\t\tcontinue\n\t\t}\n\n\t\tscheme = strings.ToLower(scheme)\n\t\tswitch scheme {\n\t\tcase \"hysteria\":\n\t\t\turlHysteria, err := url.Parse(line)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tquery := urlHysteria.Query()\n\t\t\tname := uniqueName(names, urlHysteria.Fragment)\n\t\t\thysteria := make(map[string]any, 20)\n\n\t\t\thysteria[\"name\"] = name\n\t\t\thysteria[\"type\"] = scheme\n\t\t\thysteria[\"server\"] = urlHysteria.Hostname()\n\t\t\thysteria[\"port\"] = urlHysteria.Port()\n\t\t\thysteria[\"sni\"] = query.Get(\"peer\")\n\t\t\thysteria[\"obfs\"] = query.Get(\"obfs\")\n\t\t\tif alpn := query.Get(\"alpn\"); alpn != \"\" {\n\t\t\t\thysteria[\"alpn\"] = strings.Split(alpn, \",\")\n\t\t\t}\n\t\t\thysteria[\"auth_str\"] = query.Get(\"auth\")\n\t\t\thysteria[\"protocol\"] = query.Get(\"protocol\")\n\t\t\tup := query.Get(\"up\")\n\t\t\tdown := query.Get(\"down\")\n\t\t\tif up == \"\" {\n\t\t\t\tup = query.Get(\"upmbps\")\n\t\t\t}\n\t\t\tif down == \"\" {\n\t\t\t\tdown = query.Get(\"downmbps\")\n\t\t\t}\n\t\t\thysteria[\"down\"] = down\n\t\t\thysteria[\"up\"] = up\n\t\t\thysteria[\"skip-cert-verify\"], _ = strconv.ParseBool(query.Get(\"insecure\"))\n\n\t\t\tproxies = append(proxies, hysteria)\n\n\t\tcase \"hysteria2\", \"hy2\":\n\t\t\turlHysteria2, err := url.Parse(line)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tquery := urlHysteria2.Query()\n\t\t\tname := uniqueName(names, urlHysteria2.Fragment)\n\t\t\thysteria2 := make(map[string]any, 20)\n\n\t\t\thysteria2[\"name\"] = name\n\t\t\thysteria2[\"type\"] = \"hysteria2\"\n\t\t\thysteria2[\"server\"] = urlHysteria2.Hostname()\n\t\t\tif port := urlHysteria2.Port(); port != \"\" {\n\t\t\t\thysteria2[\"port\"] = port\n\t\t\t} else {\n\t\t\t\thysteria2[\"port\"] = \"443\"\n\t\t\t}\n\t\t\thysteria2[\"obfs\"] = query.Get(\"obfs\")\n\t\t\thysteria2[\"obfs-password\"] = query.Get(\"obfs-password\")\n\t\t\thysteria2[\"sni\"] = query.Get(\"sni\")\n\t\t\thysteria2[\"skip-cert-verify\"], _ = strconv.ParseBool(query.Get(\"insecure\"))\n\t\t\tif alpn := query.Get(\"alpn\"); alpn != \"\" {\n\t\t\t\thysteria2[\"alpn\"] = strings.Split(alpn, \",\")\n\t\t\t}\n\t\t\tif auth := urlHysteria2.User.String(); auth != \"\" {\n\t\t\t\thysteria2[\"password\"] = auth\n\t\t\t}\n\t\t\thysteria2[\"fingerprint\"] = query.Get(\"pinSHA256\")\n\t\t\thysteria2[\"down\"] = query.Get(\"down\")\n\t\t\thysteria2[\"up\"] = query.Get(\"up\")\n\n\t\t\tproxies = append(proxies, hysteria2)\n\n\t\tcase \"tuic\":\n\t\t\t// A temporary unofficial TUIC share link standard\n\t\t\t// Modified from https://github.com/daeuniverse/dae/discussions/182\n\t\t\t// Changes:\n\t\t\t//   1. Support TUICv4, just replace uuid:password with token\n\t\t\t//   2. Remove `allow_insecure` field\n\t\t\turlTUIC, err := url.Parse(line)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tquery := urlTUIC.Query()\n\n\t\t\ttuic := make(map[string]any, 20)\n\t\t\ttuic[\"name\"] = uniqueName(names, urlTUIC.Fragment)\n\t\t\ttuic[\"type\"] = scheme\n\t\t\ttuic[\"server\"] = urlTUIC.Hostname()\n\t\t\ttuic[\"port\"] = urlTUIC.Port()\n\t\t\ttuic[\"udp\"] = true\n\t\t\tpassword, v5 := urlTUIC.User.Password()\n\t\t\tif v5 {\n\t\t\t\ttuic[\"uuid\"] = urlTUIC.User.Username()\n\t\t\t\ttuic[\"password\"] = password\n\t\t\t} else {\n\t\t\t\ttuic[\"token\"] = urlTUIC.User.Username()\n\t\t\t}\n\t\t\tif cc := query.Get(\"congestion_control\"); cc != \"\" {\n\t\t\t\ttuic[\"congestion-controller\"] = cc\n\t\t\t}\n\t\t\tif alpn := query.Get(\"alpn\"); alpn != \"\" {\n\t\t\t\ttuic[\"alpn\"] = strings.Split(alpn, \",\")\n\t\t\t}\n\t\t\tif sni := query.Get(\"sni\"); sni != \"\" {\n\t\t\t\ttuic[\"sni\"] = sni\n\t\t\t}\n\t\t\tif query.Get(\"disable_sni\") == \"1\" {\n\t\t\t\ttuic[\"disable-sni\"] = true\n\t\t\t}\n\t\t\tif udpRelayMode := query.Get(\"udp_relay_mode\"); udpRelayMode != \"\" {\n\t\t\t\ttuic[\"udp-relay-mode\"] = udpRelayMode\n\t\t\t}\n\n\t\t\tproxies = append(proxies, tuic)\n\n\t\tcase \"trojan\":\n\t\t\turlTrojan, err := url.Parse(line)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tquery := urlTrojan.Query()\n\n\t\t\tname := uniqueName(names, urlTrojan.Fragment)\n\t\t\ttrojan := make(map[string]any, 20)\n\n\t\t\ttrojan[\"name\"] = name\n\t\t\ttrojan[\"type\"] = scheme\n\t\t\ttrojan[\"server\"] = urlTrojan.Hostname()\n\t\t\ttrojan[\"port\"] = urlTrojan.Port()\n\t\t\ttrojan[\"password\"] = urlTrojan.User.Username()\n\t\t\ttrojan[\"udp\"] = true\n\t\t\ttrojan[\"skip-cert-verify\"], _ = strconv.ParseBool(query.Get(\"allowInsecure\"))\n\n\t\t\tif sni := query.Get(\"sni\"); sni != \"\" {\n\t\t\t\ttrojan[\"sni\"] = sni\n\t\t\t}\n\t\t\tif alpn := query.Get(\"alpn\"); alpn != \"\" {\n\t\t\t\ttrojan[\"alpn\"] = strings.Split(alpn, \",\")\n\t\t\t}\n\n\t\t\tnetwork := strings.ToLower(query.Get(\"type\"))\n\t\t\tif network != \"\" {\n\t\t\t\ttrojan[\"network\"] = network\n\t\t\t}\n\n\t\t\tswitch network {\n\t\t\tcase \"ws\":\n\t\t\t\theaders := make(map[string]any)\n\t\t\t\twsOpts := make(map[string]any)\n\n\t\t\t\theaders[\"User-Agent\"] = RandUserAgent()\n\n\t\t\t\twsOpts[\"path\"] = query.Get(\"path\")\n\t\t\t\twsOpts[\"headers\"] = headers\n\n\t\t\t\ttrojan[\"ws-opts\"] = wsOpts\n\n\t\t\tcase \"grpc\":\n\t\t\t\tgrpcOpts := make(map[string]any)\n\t\t\t\tgrpcOpts[\"grpc-service-name\"] = query.Get(\"serviceName\")\n\t\t\t\ttrojan[\"grpc-opts\"] = grpcOpts\n\t\t\t}\n\n\t\t\tif fingerprint := query.Get(\"fp\"); fingerprint == \"\" {\n\t\t\t\ttrojan[\"client-fingerprint\"] = \"chrome\"\n\t\t\t} else {\n\t\t\t\ttrojan[\"client-fingerprint\"] = fingerprint\n\t\t\t}\n\n\t\t\tif pcs := query.Get(\"pcs\"); pcs != \"\" {\n\t\t\t\ttrojan[\"fingerprint\"] = pcs\n\t\t\t}\n\n\t\t\tproxies = append(proxies, trojan)\n\n\t\tcase \"vless\":\n\t\t\turlVLess, err := url.Parse(line)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif decodedHost, err := tryDecodeBase64([]byte(urlVLess.Host)); err == nil {\n\t\t\t\turlVLess.Host = string(decodedHost)\n\t\t\t}\n\t\t\tquery := urlVLess.Query()\n\t\t\tvless := make(map[string]any, 20)\n\t\t\terr = handleVShareLink(names, urlVLess, scheme, vless)\n\t\t\tif err != nil {\n\t\t\t\tlog.Warnln(\"error:%s line:%s\", err.Error(), line)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif flow := query.Get(\"flow\"); flow != \"\" {\n\t\t\t\tvless[\"flow\"] = strings.ToLower(flow)\n\t\t\t}\n\t\t\tif encryption := query.Get(\"encryption\"); encryption != \"\" {\n\t\t\t\tvless[\"encryption\"] = encryption\n\t\t\t}\n\t\t\tproxies = append(proxies, vless)\n\n\t\tcase \"vmess\":\n\t\t\t// V2RayN-styled share link\n\t\t\t// https://github.com/2dust/v2rayN/wiki/%E5%88%86%E4%BA%AB%E9%93%BE%E6%8E%A5%E6%A0%BC%E5%BC%8F%E8%AF%B4%E6%98%8E(ver-2)\n\t\t\tdcBuf, err := tryDecodeBase64([]byte(body))\n\t\t\tif err != nil {\n\t\t\t\t// Xray VMessAEAD share link\n\t\t\t\turlVMess, err := url.Parse(line)\n\t\t\t\tif err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tquery := urlVMess.Query()\n\t\t\t\tvmess := make(map[string]any, 20)\n\t\t\t\terr = handleVShareLink(names, urlVMess, scheme, vmess)\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Warnln(\"error:%s line:%s\", err.Error(), line)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tvmess[\"alterId\"] = 0\n\t\t\t\tvmess[\"cipher\"] = \"auto\"\n\t\t\t\tif encryption := query.Get(\"encryption\"); encryption != \"\" {\n\t\t\t\t\tvmess[\"cipher\"] = encryption\n\t\t\t\t}\n\t\t\t\tproxies = append(proxies, vmess)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tjsonDc := json.NewDecoder(bytes.NewReader(dcBuf))\n\t\t\tvalues := make(map[string]any, 20)\n\n\t\t\tif jsonDc.Decode(&values) != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ttempName, ok := values[\"ps\"].(string)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tname := uniqueName(names, tempName)\n\t\t\tvmess := make(map[string]any, 20)\n\n\t\t\tvmess[\"name\"] = name\n\t\t\tvmess[\"type\"] = scheme\n\t\t\tvmess[\"server\"] = values[\"add\"]\n\t\t\tvmess[\"port\"] = values[\"port\"]\n\t\t\tvmess[\"uuid\"] = values[\"id\"]\n\t\t\tif alterId, ok := values[\"aid\"]; ok {\n\t\t\t\tvmess[\"alterId\"] = alterId\n\t\t\t} else {\n\t\t\t\tvmess[\"alterId\"] = 0\n\t\t\t}\n\t\t\tvmess[\"udp\"] = true\n\t\t\tvmess[\"xudp\"] = true\n\t\t\tvmess[\"tls\"] = false\n\t\t\tvmess[\"skip-cert-verify\"] = false\n\n\t\t\tvmess[\"cipher\"] = \"auto\"\n\t\t\tif cipher, ok := values[\"scy\"].(string); ok && cipher != \"\" {\n\t\t\t\tvmess[\"cipher\"] = cipher\n\t\t\t}\n\n\t\t\tif sni, ok := values[\"sni\"].(string); ok && sni != \"\" {\n\t\t\t\tvmess[\"servername\"] = sni\n\t\t\t}\n\n\t\t\tnetwork, ok := values[\"net\"].(string)\n\t\t\tif ok {\n\t\t\t\tnetwork = strings.ToLower(network)\n\t\t\t\tif values[\"type\"] == \"http\" {\n\t\t\t\t\tnetwork = \"http\"\n\t\t\t\t} else if network == \"http\" {\n\t\t\t\t\tnetwork = \"h2\"\n\t\t\t\t}\n\t\t\t\tvmess[\"network\"] = network\n\t\t\t}\n\n\t\t\ttls, ok := values[\"tls\"].(string)\n\t\t\tif ok {\n\t\t\t\ttls = strings.ToLower(tls)\n\t\t\t\tif strings.HasSuffix(tls, \"tls\") {\n\t\t\t\t\tvmess[\"tls\"] = true\n\t\t\t\t}\n\t\t\t\tif alpn, ok := values[\"alpn\"].(string); ok {\n\t\t\t\t\tvmess[\"alpn\"] = strings.Split(alpn, \",\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tswitch network {\n\t\t\tcase \"http\":\n\t\t\t\theaders := make(map[string]any)\n\t\t\t\thttpOpts := make(map[string]any)\n\t\t\t\tif host, ok := values[\"host\"].(string); ok && host != \"\" {\n\t\t\t\t\theaders[\"Host\"] = []string{host}\n\t\t\t\t}\n\t\t\t\thttpOpts[\"path\"] = []string{\"/\"}\n\t\t\t\tif path, ok := values[\"path\"].(string); ok && path != \"\" {\n\t\t\t\t\thttpOpts[\"path\"] = []string{path}\n\t\t\t\t}\n\t\t\t\thttpOpts[\"headers\"] = headers\n\n\t\t\t\tvmess[\"http-opts\"] = httpOpts\n\n\t\t\tcase \"h2\":\n\t\t\t\th2Opts := make(map[string]any)\n\t\t\t\th2Opts[\"path\"] = \"/\"\n\t\t\t\tif path, ok := values[\"path\"].(string); ok && path != \"\" {\n\t\t\t\t\th2Opts[\"path\"] = path\n\t\t\t\t}\n\t\t\t\tif host, ok := values[\"host\"].(string); ok && host != \"\" {\n\t\t\t\t\th2Opts[\"host\"] = []string{host}\n\t\t\t\t}\n\t\t\t\tvmess[\"h2-opts\"] = h2Opts\n\n\t\t\tcase \"ws\", \"httpupgrade\":\n\t\t\t\theaders := make(map[string]any)\n\t\t\t\twsOpts := make(map[string]any)\n\t\t\t\twsOpts[\"path\"] = \"/\"\n\t\t\t\tif host, ok := values[\"host\"].(string); ok && host != \"\" {\n\t\t\t\t\theaders[\"Host\"] = host\n\t\t\t\t}\n\t\t\t\tif path, ok := values[\"path\"].(string); ok && path != \"\" {\n\t\t\t\t\tpath := path\n\t\t\t\t\tpathURL, err := url.Parse(path)\n\t\t\t\t\tif err == nil {\n\t\t\t\t\t\tquery := pathURL.Query()\n\t\t\t\t\t\tif earlyData := query.Get(\"ed\"); earlyData != \"\" {\n\t\t\t\t\t\t\tmed, err := strconv.Atoi(earlyData)\n\t\t\t\t\t\t\tif err == nil {\n\t\t\t\t\t\t\t\tswitch network {\n\t\t\t\t\t\t\t\tcase \"ws\":\n\t\t\t\t\t\t\t\t\twsOpts[\"max-early-data\"] = med\n\t\t\t\t\t\t\t\t\twsOpts[\"early-data-header-name\"] = \"Sec-WebSocket-Protocol\"\n\t\t\t\t\t\t\t\tcase \"httpupgrade\":\n\t\t\t\t\t\t\t\t\twsOpts[\"v2ray-http-upgrade-fast-open\"] = true\n\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\tquery.Del(\"ed\")\n\t\t\t\t\t\t\t\tpathURL.RawQuery = query.Encode()\n\t\t\t\t\t\t\t\tpath = pathURL.String()\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\tif earlyDataHeader := query.Get(\"eh\"); earlyDataHeader != \"\" {\n\t\t\t\t\t\t\twsOpts[\"early-data-header-name\"] = earlyDataHeader\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\twsOpts[\"path\"] = path\n\t\t\t\t}\n\t\t\t\twsOpts[\"headers\"] = headers\n\t\t\t\tvmess[\"ws-opts\"] = wsOpts\n\n\t\t\tcase \"grpc\":\n\t\t\t\tgrpcOpts := make(map[string]any)\n\t\t\t\tgrpcOpts[\"grpc-service-name\"] = values[\"path\"]\n\t\t\t\tvmess[\"grpc-opts\"] = grpcOpts\n\t\t\t}\n\n\t\t\tproxies = append(proxies, vmess)\n\n\t\tcase \"ss\":\n\t\t\turlSS, err := url.Parse(line)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tname := uniqueName(names, urlSS.Fragment)\n\t\t\tport := urlSS.Port()\n\n\t\t\tif port == \"\" {\n\t\t\t\tdcBuf, err := encRaw.DecodeString(urlSS.Host)\n\t\t\t\tif err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\turlSS, err = url.Parse(\"ss://\" + string(dcBuf))\n\t\t\t\tif err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tvar (\n\t\t\t\tcipherRaw = urlSS.User.Username()\n\t\t\t\tcipher    string\n\t\t\t\tpassword  string\n\t\t\t)\n\t\t\tcipher = cipherRaw\n\t\t\tif password, found = urlSS.User.Password(); !found {\n\t\t\t\tdcBuf, err := base64.RawURLEncoding.DecodeString(cipherRaw)\n\t\t\t\tif err != nil {\n\t\t\t\t\tdcBuf, _ = enc.DecodeString(cipherRaw)\n\t\t\t\t}\n\t\t\t\tcipher, password, found = strings.Cut(string(dcBuf), \":\")\n\t\t\t\tif !found {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\terr = VerifyMethod(cipher, password)\n\t\t\t\tif err != nil {\n\t\t\t\t\tdcBuf, _ = encRaw.DecodeString(cipherRaw)\n\t\t\t\t\tcipher, password, found = strings.Cut(string(dcBuf), \":\")\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tss := make(map[string]any, 10)\n\n\t\t\tss[\"name\"] = name\n\t\t\tss[\"type\"] = scheme\n\t\t\tss[\"server\"] = urlSS.Hostname()\n\t\t\tss[\"port\"] = urlSS.Port()\n\t\t\tss[\"cipher\"] = cipher\n\t\t\tss[\"password\"] = password\n\t\t\tquery := urlSS.Query()\n\t\t\tss[\"udp\"] = true\n\t\t\tif query.Get(\"udp-over-tcp\") == \"true\" || query.Get(\"uot\") == \"1\" {\n\t\t\t\tss[\"udp-over-tcp\"] = true\n\t\t\t}\n\t\t\tplugin := query.Get(\"plugin\")\n\t\t\tif strings.Contains(plugin, \";\") {\n\t\t\t\tpluginInfo, _ := url.ParseQuery(\"pluginName=\" + strings.ReplaceAll(plugin, \";\", \"&\"))\n\t\t\t\tpluginName := pluginInfo.Get(\"pluginName\")\n\t\t\t\tif strings.Contains(pluginName, \"obfs\") {\n\t\t\t\t\tss[\"plugin\"] = \"obfs\"\n\t\t\t\t\tss[\"plugin-opts\"] = map[string]any{\n\t\t\t\t\t\t\"mode\": pluginInfo.Get(\"obfs\"),\n\t\t\t\t\t\t\"host\": pluginInfo.Get(\"obfs-host\"),\n\t\t\t\t\t}\n\t\t\t\t} else if strings.Contains(pluginName, \"v2ray-plugin\") {\n\t\t\t\t\tmode := pluginInfo.Get(\"mode\")\n\t\t\t\t\tif mode == \"\" {\n\t\t\t\t\t\tmode = pluginInfo.Get(\"obfs\")\n\t\t\t\t\t}\n\t\t\t\t\thost := pluginInfo.Get(\"host\")\n\t\t\t\t\tif host == \"\" {\n\t\t\t\t\t\thost = pluginInfo.Get(\"obfs-host\")\n\t\t\t\t\t}\n\t\t\t\t\tss[\"plugin\"] = \"v2ray-plugin\"\n\t\t\t\t\tss[\"plugin-opts\"] = map[string]any{\n\t\t\t\t\t\t\"mode\": mode,\n\t\t\t\t\t\t\"host\": host,\n\t\t\t\t\t\t\"path\": pluginInfo.Get(\"path\"),\n\t\t\t\t\t\t\"tls\":  strings.Contains(plugin, \"tls\"),\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tproxies = append(proxies, ss)\n\n\t\tcase \"ssr\":\n\t\t\tdcBuf, err := TryDecodeBase64(body)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// ssr://host:port:protocol:method:obfs:urlsafebase64pass/?obfsparam=urlsafebase64param&protoparam=urlsafebase64param&remarks=urlsafebase64remarks&group=urlsafebase64group&udpport=0&uot=1\n\n\t\t\tbefore, after, ok := strings.Cut(string(dcBuf), \"/?\")\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tbeforeArr := strings.Split(before, \":\")\n\n\t\t\tif len(beforeArr) != 6 {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\thost := beforeArr[0]\n\t\t\tport := beforeArr[1]\n\t\t\tprotocol := beforeArr[2]\n\t\t\tmethod := beforeArr[3]\n\t\t\tobfs := beforeArr[4]\n\t\t\tpassword := decodeUrlSafe(urlSafe(beforeArr[5]))\n\n\t\t\tquery, err := url.ParseQuery(urlSafe(after))\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tremarks := decodeUrlSafe(query.Get(\"remarks\"))\n\t\t\tname := uniqueName(names, remarks)\n\n\t\t\tobfsParam := decodeUrlSafe(query.Get(\"obfsparam\"))\n\t\t\tprotocolParam := decodeUrlSafe(query.Get(\"protoparam\"))\n\n\t\t\tssr := make(map[string]any, 20)\n\n\t\t\tssr[\"name\"] = name\n\t\t\tssr[\"type\"] = scheme\n\t\t\tssr[\"server\"] = host\n\t\t\tssr[\"port\"] = port\n\t\t\tssr[\"cipher\"] = method\n\t\t\tssr[\"password\"] = password\n\t\t\tssr[\"obfs\"] = obfs\n\t\t\tssr[\"protocol\"] = protocol\n\t\t\tssr[\"udp\"] = true\n\n\t\t\tif obfsParam != \"\" {\n\t\t\t\tssr[\"obfs-param\"] = obfsParam\n\t\t\t}\n\n\t\t\tif protocolParam != \"\" {\n\t\t\t\tssr[\"protocol-param\"] = protocolParam\n\t\t\t}\n\n\t\t\tproxies = append(proxies, ssr)\n\n\t\tcase \"socks\", \"socks5\", \"socks5h\", \"http\", \"https\":\n\t\t\tlink, err := url.Parse(line)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tserver := link.Hostname()\n\t\t\tif server == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tportStr := link.Port()\n\t\t\tif portStr == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tremarks := link.Fragment\n\t\t\tif remarks == \"\" {\n\t\t\t\tremarks = fmt.Sprintf(\"%s:%s\", server, portStr)\n\t\t\t}\n\t\t\tname := uniqueName(names, remarks)\n\t\t\tencodeStr := link.User.String()\n\t\t\tvar username, password string\n\t\t\tif encodeStr != \"\" {\n\t\t\t\tdecodeStr := string(DecodeBase64([]byte(encodeStr)))\n\t\t\t\tsplitStr := strings.Split(decodeStr, \":\")\n\n\t\t\t\t// todo: should use url.QueryUnescape ?\n\t\t\t\tusername = splitStr[0]\n\t\t\t\tif len(splitStr) == 2 {\n\t\t\t\t\tpassword = splitStr[1]\n\t\t\t\t}\n\t\t\t}\n\t\t\tsocks := make(map[string]any, 10)\n\t\t\tsocks[\"name\"] = name\n\t\t\tsocks[\"type\"] = func() string {\n\t\t\t\tswitch scheme {\n\t\t\t\tcase \"socks\", \"socks5\", \"socks5h\":\n\t\t\t\t\treturn \"socks5\"\n\t\t\t\tcase \"http\", \"https\":\n\t\t\t\t\treturn \"http\"\n\t\t\t\t}\n\t\t\t\treturn scheme\n\t\t\t}()\n\t\t\tsocks[\"server\"] = server\n\t\t\tsocks[\"port\"] = portStr\n\t\t\tsocks[\"username\"] = username\n\t\t\tsocks[\"password\"] = password\n\t\t\tsocks[\"skip-cert-verify\"] = true\n\t\t\tif scheme == \"https\" {\n\t\t\t\tsocks[\"tls\"] = true\n\t\t\t}\n\n\t\t\tproxies = append(proxies, socks)\n\n\t\tcase \"anytls\":\n\t\t\t// https://github.com/anytls/anytls-go/blob/main/docs/uri_scheme.md\n\t\t\tlink, err := url.Parse(line)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tusername := link.User.Username()\n\t\t\tpassword, exist := link.User.Password()\n\t\t\tif !exist {\n\t\t\t\tpassword = username\n\t\t\t}\n\t\t\tquery := link.Query()\n\t\t\tserver := link.Hostname()\n\t\t\tif server == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tportStr := link.Port()\n\t\t\tif portStr == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tinsecure, sni := query.Get(\"insecure\"), query.Get(\"sni\")\n\t\t\tinsecureBool := insecure == \"1\"\n\t\t\tfingerprint := query.Get(\"hpkp\")\n\n\t\t\tremarks := link.Fragment\n\t\t\tif remarks == \"\" {\n\t\t\t\tremarks = fmt.Sprintf(\"%s:%s\", server, portStr)\n\t\t\t}\n\t\t\tname := uniqueName(names, remarks)\n\t\t\tanytls := make(map[string]any, 10)\n\t\t\tanytls[\"name\"] = name\n\t\t\tanytls[\"type\"] = \"anytls\"\n\t\t\tanytls[\"server\"] = server\n\t\t\tanytls[\"port\"] = portStr\n\t\t\tanytls[\"username\"] = username\n\t\t\tanytls[\"password\"] = password\n\t\t\tanytls[\"sni\"] = sni\n\t\t\tanytls[\"fingerprint\"] = fingerprint\n\t\t\tanytls[\"skip-cert-verify\"] = insecureBool\n\t\t\tanytls[\"udp\"] = true\n\n\t\t\tproxies = append(proxies, anytls)\n\n\t\tcase \"mierus\":\n\t\t\turlMieru, err := url.Parse(line)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tquery := urlMieru.Query()\n\n\t\t\tserver := urlMieru.Hostname()\n\t\t\tif server == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tusername := urlMieru.User.Username()\n\t\t\tpassword, _ := urlMieru.User.Password()\n\n\t\t\tbaseName := urlMieru.Fragment\n\t\t\tif baseName == \"\" {\n\t\t\t\tbaseName = query.Get(\"profile\")\n\t\t\t}\n\t\t\tif baseName == \"\" {\n\t\t\t\tbaseName = server\n\t\t\t}\n\n\t\t\tmultiplexing := query.Get(\"multiplexing\")\n\t\t\thandshakeMode := query.Get(\"handshake-mode\")\n\t\t\ttrafficPattern := query.Get(\"traffic-pattern\")\n\n\t\t\tportList := query[\"port\"]\n\t\t\tprotocolList := query[\"protocol\"]\n\t\t\tif len(portList) == 0 || len(portList) != len(protocolList) {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tfor i, port := range portList {\n\t\t\t\tprotocol := protocolList[i]\n\t\t\t\tname := uniqueName(names, fmt.Sprintf(\"%s:%s/%s\", baseName, port, protocol))\n\n\t\t\t\tmieru := make(map[string]any, 15)\n\t\t\t\tmieru[\"name\"] = name\n\t\t\t\tmieru[\"type\"] = \"mieru\"\n\t\t\t\tmieru[\"server\"] = server\n\t\t\t\tmieru[\"transport\"] = protocol\n\t\t\t\tmieru[\"udp\"] = true\n\t\t\t\tmieru[\"username\"] = username\n\t\t\t\tmieru[\"password\"] = password\n\n\t\t\t\tif strings.Contains(port, \"-\") {\n\t\t\t\t\tmieru[\"port-range\"] = port\n\t\t\t\t} else {\n\t\t\t\t\tportNum, err := strconv.Atoi(port)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tmieru[\"port\"] = portNum\n\t\t\t\t}\n\n\t\t\t\tif multiplexing != \"\" {\n\t\t\t\t\tmieru[\"multiplexing\"] = multiplexing\n\t\t\t\t}\n\t\t\t\tif handshakeMode != \"\" {\n\t\t\t\t\tmieru[\"handshake-mode\"] = handshakeMode\n\t\t\t\t}\n\t\t\t\tif trafficPattern != \"\" {\n\t\t\t\t\tmieru[\"traffic-pattern\"] = trafficPattern\n\t\t\t\t}\n\n\t\t\t\tproxies = append(proxies, mieru)\n\t\t\t}\n\t\t}\n\t}\n\n\tif len(proxies) == 0 {\n\t\treturn nil, fmt.Errorf(\"convert v2ray subscribe error: format invalid\")\n\t}\n\n\treturn proxies, nil\n}\n\nfunc uniqueName(names map[string]int, name string) string {\n\tif index, ok := names[name]; ok {\n\t\tindex++\n\t\tnames[name] = index\n\t\tname = fmt.Sprintf(\"%s-%02d\", name, index)\n\t} else {\n\t\tindex = 0\n\t\tnames[name] = index\n\t}\n\treturn name\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/convert/converter_test.go",
    "content": "package convert_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/metacubex/mihomo/adapter\"\n\t. \"github.com/metacubex/mihomo/common/convert\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\n// https://v2.hysteria.network/zh/docs/developers/URI-Scheme/\nfunc TestConvertsV2Ray_normal(t *testing.T) {\n\thy2test := \"hysteria2://letmein@example.com:8443/?insecure=1&obfs=salamander&obfs-password=gawrgura&pinSHA256=65b3acd7db555768304a16abb6f4366c1a0c0bb5cec81429617f0150d7d66726&sni=real.example.com&up=114&down=514&alpn=h3,h4#hy2test\"\n\n\texpected := []map[string]interface{}{\n\t\t{\n\t\t\t\"name\":             \"hy2test\",\n\t\t\t\"type\":             \"hysteria2\",\n\t\t\t\"server\":           \"example.com\",\n\t\t\t\"port\":             \"8443\",\n\t\t\t\"sni\":              \"real.example.com\",\n\t\t\t\"obfs\":             \"salamander\",\n\t\t\t\"obfs-password\":    \"gawrgura\",\n\t\t\t\"alpn\":             []string{\"h3\", \"h4\"},\n\t\t\t\"password\":         \"letmein\",\n\t\t\t\"up\":               \"114\",\n\t\t\t\"down\":             \"514\",\n\t\t\t\"skip-cert-verify\": true,\n\t\t\t\"fingerprint\":      \"65b3acd7db555768304a16abb6f4366c1a0c0bb5cec81429617f0150d7d66726\",\n\t\t},\n\t}\n\n\tproxies, err := ConvertsV2Ray([]byte(hy2test))\n\n\tassert.Nil(t, err)\n\tassert.Equal(t, expected, proxies)\n\n\t_, err = adapter.ParseProxy(proxies[0])\n\tassert.NoError(t, err)\n}\n\nfunc TestConvertsV2RayMieru(t *testing.T) {\n\tmierusTest := \"mierus://user:pass@1.2.3.4?handshake-mode=HANDSHAKE_NO_WAIT&mtu=1400&multiplexing=MULTIPLEXING_HIGH&port=6666&port=9998-9999&port=6489&port=4896&profile=default&protocol=TCP&protocol=TCP&protocol=UDP&protocol=UDP&traffic-pattern=CCoQARoECAEQCiIYCAMQASoIMDAwMTAyMDMqCDA0MDUwNjA3\"\n\n\texpected := []map[string]any{\n\t\t{\n\t\t\t\"name\":            \"default:6666/TCP\",\n\t\t\t\"type\":            \"mieru\",\n\t\t\t\"server\":          \"1.2.3.4\",\n\t\t\t\"port\":            6666,\n\t\t\t\"transport\":       \"TCP\",\n\t\t\t\"udp\":             true,\n\t\t\t\"username\":        \"user\",\n\t\t\t\"password\":        \"pass\",\n\t\t\t\"multiplexing\":    \"MULTIPLEXING_HIGH\",\n\t\t\t\"handshake-mode\":  \"HANDSHAKE_NO_WAIT\",\n\t\t\t\"traffic-pattern\": \"CCoQARoECAEQCiIYCAMQASoIMDAwMTAyMDMqCDA0MDUwNjA3\",\n\t\t},\n\t\t{\n\t\t\t\"name\":            \"default:9998-9999/TCP\",\n\t\t\t\"type\":            \"mieru\",\n\t\t\t\"server\":          \"1.2.3.4\",\n\t\t\t\"port-range\":      \"9998-9999\",\n\t\t\t\"transport\":       \"TCP\",\n\t\t\t\"udp\":             true,\n\t\t\t\"username\":        \"user\",\n\t\t\t\"password\":        \"pass\",\n\t\t\t\"multiplexing\":    \"MULTIPLEXING_HIGH\",\n\t\t\t\"handshake-mode\":  \"HANDSHAKE_NO_WAIT\",\n\t\t\t\"traffic-pattern\": \"CCoQARoECAEQCiIYCAMQASoIMDAwMTAyMDMqCDA0MDUwNjA3\",\n\t\t},\n\t\t{\n\t\t\t\"name\":            \"default:6489/UDP\",\n\t\t\t\"type\":            \"mieru\",\n\t\t\t\"server\":          \"1.2.3.4\",\n\t\t\t\"port\":            6489,\n\t\t\t\"transport\":       \"UDP\",\n\t\t\t\"udp\":             true,\n\t\t\t\"username\":        \"user\",\n\t\t\t\"password\":        \"pass\",\n\t\t\t\"multiplexing\":    \"MULTIPLEXING_HIGH\",\n\t\t\t\"handshake-mode\":  \"HANDSHAKE_NO_WAIT\",\n\t\t\t\"traffic-pattern\": \"CCoQARoECAEQCiIYCAMQASoIMDAwMTAyMDMqCDA0MDUwNjA3\",\n\t\t},\n\t\t{\n\t\t\t\"name\":            \"default:4896/UDP\",\n\t\t\t\"type\":            \"mieru\",\n\t\t\t\"server\":          \"1.2.3.4\",\n\t\t\t\"port\":            4896,\n\t\t\t\"transport\":       \"UDP\",\n\t\t\t\"udp\":             true,\n\t\t\t\"username\":        \"user\",\n\t\t\t\"password\":        \"pass\",\n\t\t\t\"multiplexing\":    \"MULTIPLEXING_HIGH\",\n\t\t\t\"handshake-mode\":  \"HANDSHAKE_NO_WAIT\",\n\t\t\t\"traffic-pattern\": \"CCoQARoECAEQCiIYCAMQASoIMDAwMTAyMDMqCDA0MDUwNjA3\",\n\t\t},\n\t}\n\n\tproxies, err := ConvertsV2Ray([]byte(mierusTest))\n\n\tassert.Nil(t, err)\n\tassert.Equal(t, expected, proxies)\n\n\t_, err = adapter.ParseProxy(proxies[0])\n\tassert.NoError(t, err)\n}\n\nfunc TestConvertsV2RayMieruMinimal(t *testing.T) {\n\tmierusTest := \"mierus://user:pass@example.com?port=443&protocol=TCP&profile=simple\"\n\n\texpected := []map[string]any{\n\t\t{\n\t\t\t\"name\":      \"simple:443/TCP\",\n\t\t\t\"type\":      \"mieru\",\n\t\t\t\"server\":    \"example.com\",\n\t\t\t\"port\":      443,\n\t\t\t\"transport\": \"TCP\",\n\t\t\t\"udp\":       true,\n\t\t\t\"username\":  \"user\",\n\t\t\t\"password\":  \"pass\",\n\t\t},\n\t}\n\n\tproxies, err := ConvertsV2Ray([]byte(mierusTest))\n\n\tassert.Nil(t, err)\n\tassert.Equal(t, expected, proxies)\n\n\t_, err = adapter.ParseProxy(proxies[0])\n\tassert.NoError(t, err)\n}\n\nfunc TestConvertsV2RayMieruFragment(t *testing.T) {\n\tmierusTest := \"mierus://user:pass@example.com?port=443&protocol=TCP&profile=default#myproxy\"\n\n\tproxies, err := ConvertsV2Ray([]byte(mierusTest))\n\n\tassert.Nil(t, err)\n\tassert.Len(t, proxies, 1)\n\tassert.Equal(t, \"myproxy:443/TCP\", proxies[0][\"name\"])\n\n\t_, err = adapter.ParseProxy(proxies[0])\n\tassert.NoError(t, err)\n}\n\nfunc TestConvertsV2RayVlessRealityVisionTCPWithoutHeaderType(t *testing.T) {\n\tvlessTest := \"vless://a1b2c3d4-eacc-4433-981b-7e5f9a8b@142.98.76.54:34888?encryption=none&security=reality&type=tcp&sni=github.io&fp=chrome&pbk=ppQ9FwLrLIa0AOrp1WvcyiaQ37vg2WSy_CD4bIdiTUw&sid=6ba85179f3a2b4c5&flow=xtls-rprx-vision#My-VLESS-Reality-Vision\"\n\n\tproxies, err := ConvertsV2Ray([]byte(vlessTest))\n\n\tassert.Nil(t, err)\n\tassert.Len(t, proxies, 1)\n\tassert.Equal(t, \"tcp\", proxies[0][\"network\"])\n\tassert.Equal(t, \"xtls-rprx-vision\", proxies[0][\"flow\"])\n\tassert.Equal(t, \"none\", proxies[0][\"encryption\"])\n\tassert.Equal(t, \"github.io\", proxies[0][\"servername\"])\n\tassert.NotContains(t, proxies[0], \"http-opts\")\n\tassert.NotContains(t, proxies[0], \"h2-opts\")\n\n\t_, err = adapter.ParseProxy(proxies[0])\n\tassert.NoError(t, err)\n}\n\nfunc TestConvertsV2RayVlessTCPHTTPHeaderType(t *testing.T) {\n\tvlessTest := \"vless://uuid@example.com:443?security=tls&type=tcp&headerType=http&host=cdn.example.com&path=%2Fedge&method=POST#vless-http\"\n\n\tproxies, err := ConvertsV2Ray([]byte(vlessTest))\n\n\tassert.Nil(t, err)\n\tassert.Len(t, proxies, 1)\n\tassert.Equal(t, \"http\", proxies[0][\"network\"])\n\tassert.Equal(t, map[string]any{\n\t\t\"method\": \"POST\",\n\t\t\"path\":   []string{\"/edge\"},\n\t\t\"headers\": map[string]any{\n\t\t\t\"Host\": []string{\"cdn.example.com\"},\n\t\t},\n\t}, proxies[0][\"http-opts\"])\n\tassert.NotContains(t, proxies[0], \"h2-opts\")\n\n\t_, err = adapter.ParseProxy(proxies[0])\n\tassert.NoError(t, err)\n}\n\nfunc TestConvertsV2RayVlessHTTPTransportUsesH2Opts(t *testing.T) {\n\tvlessTest := \"vless://uuid@example.com:443?security=tls&type=http&host=cdn.example.com&path=%2Fgrpc#vless-h2\"\n\n\tproxies, err := ConvertsV2Ray([]byte(vlessTest))\n\n\tassert.Nil(t, err)\n\tassert.Len(t, proxies, 1)\n\tassert.Equal(t, \"h2\", proxies[0][\"network\"])\n\tassert.Equal(t, map[string]any{\n\t\t\"host\": []string{\"cdn.example.com\"},\n\t\t\"path\": \"/grpc\",\n\t}, proxies[0][\"h2-opts\"])\n\tassert.NotContains(t, proxies[0], \"http-opts\")\n\n\t_, err = adapter.ParseProxy(proxies[0])\n\tassert.NoError(t, err)\n}\n\n// Regression test for MetaCubeX/mihomo#2738: the legacy v2rayN-style\n// base64-JSON VMess parser must place `host` under h2-opts.host instead\n// of stranding it inside a non-existent h2-opts.headers.Host key.\nfunc TestConvertsV2RayVmessBase64H2Transport(t *testing.T) {\n\t// base64 payload decodes to:\n\t// {\"v\":\"2\",\"ps\":\"demo\",\"add\":\"server.example.com\",\"port\":\"443\",\n\t//  \"id\":\"b831381d-6324-4d53-ad4f-8cda48b30811\",\"aid\":\"0\",\"scy\":\"auto\",\n\t//  \"net\":\"h2\",\"type\":\"none\",\"host\":\"cdn.example.com\",\"path\":\"/grpc\",\"tls\":\"tls\"}\n\tvmessTest := \"vmess://eyJ2IjoiMiIsInBzIjoiZGVtbyIsImFkZCI6InNlcnZlci5leGFtcGxlLmNvbSIsInBvcnQiOiI0NDMiLCJpZCI6ImI4MzEzODFkLTYzMjQtNGQ1My1hZDRmLThjZGE0OGIzMDgxMSIsImFpZCI6IjAiLCJzY3kiOiJhdXRvIiwibmV0IjoiaDIiLCJ0eXBlIjoibm9uZSIsImhvc3QiOiJjZG4uZXhhbXBsZS5jb20iLCJwYXRoIjoiL2dycGMiLCJ0bHMiOiJ0bHMifQ==\"\n\n\tproxies, err := ConvertsV2Ray([]byte(vmessTest))\n\n\tassert.Nil(t, err)\n\tassert.Len(t, proxies, 1)\n\tassert.Equal(t, \"h2\", proxies[0][\"network\"])\n\tassert.Equal(t, map[string]any{\n\t\t\"host\": []string{\"cdn.example.com\"},\n\t\t\"path\": \"/grpc\",\n\t}, proxies[0][\"h2-opts\"])\n\tassert.NotContains(t, proxies[0], \"http-opts\")\n\n\t_, err = adapter.ParseProxy(proxies[0])\n\tassert.NoError(t, err)\n}\n\n// `net: http` with `type != \"http\"` is remapped to h2 transport\n// at converter.go's network-resolution step, so it must produce the\n// same h2-opts shape as `net: h2`. Guards against regression if the\n// remap rule is changed later.\nfunc TestConvertsV2RayVmessBase64HTTPRemappedToH2Transport(t *testing.T) {\n\t// base64 payload decodes to:\n\t// {\"v\":\"2\",\"ps\":\"demo-http-remapped\",\"add\":\"server.example.com\",\"port\":\"443\",\n\t//  \"id\":\"b831381d-6324-4d53-ad4f-8cda48b30811\",\"aid\":\"0\",\"scy\":\"auto\",\n\t//  \"net\":\"http\",\"type\":\"none\",\"host\":\"cdn.example.com\",\"path\":\"/grpc\",\"tls\":\"tls\"}\n\tvmessTest := \"vmess://eyJ2IjoiMiIsInBzIjoiZGVtby1odHRwLXJlbWFwcGVkIiwiYWRkIjoic2VydmVyLmV4YW1wbGUuY29tIiwicG9ydCI6IjQ0MyIsImlkIjoiYjgzMTM4MWQtNjMyNC00ZDUzLWFkNGYtOGNkYTQ4YjMwODExIiwiYWlkIjoiMCIsInNjeSI6ImF1dG8iLCJuZXQiOiJodHRwIiwidHlwZSI6Im5vbmUiLCJob3N0IjoiY2RuLmV4YW1wbGUuY29tIiwicGF0aCI6Ii9ncnBjIiwidGxzIjoidGxzIn0=\"\n\n\tproxies, err := ConvertsV2Ray([]byte(vmessTest))\n\n\tassert.Nil(t, err)\n\tassert.Len(t, proxies, 1)\n\tassert.Equal(t, \"h2\", proxies[0][\"network\"])\n\tassert.Equal(t, map[string]any{\n\t\t\"host\": []string{\"cdn.example.com\"},\n\t\t\"path\": \"/grpc\",\n\t}, proxies[0][\"h2-opts\"])\n\tassert.NotContains(t, proxies[0], \"http-opts\")\n\n\t_, err = adapter.ParseProxy(proxies[0])\n\tassert.NoError(t, err)\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/convert/util.go",
    "content": "package convert\n\nimport (\n\t\"encoding/base64\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/utils\"\n\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/randv2\"\n\t\"github.com/metacubex/sing-shadowsocks/shadowimpl\"\n)\n\nvar hostsSuffix = []string{\n\t\"-cdn.aliyuncs.com\",\n\t\".alicdn.com\",\n\t\".pan.baidu.com\",\n\t\".tbcache.com\",\n\t\".aliyuncdn.com\",\n\t\".vod.miguvideo.com\",\n\t\".cibntv.net\",\n\t\".myqcloud.com\",\n\t\".smtcdns.com\",\n\t\".alikunlun.com\",\n\t\".smtcdns.net\",\n\t\".apcdns.net\",\n\t\".cdn-go.cn\",\n\t\".cdntip.com\",\n\t\".cdntips.com\",\n\t\".alidayu.com\",\n\t\".alidns.com\",\n\t\".cdngslb.com\",\n\t\".mxhichina.com\",\n\t\".alibabadns.com\",\n}\n\nvar userAgents = []string{\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.162 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.0; Moto C Build/NRD90M.059) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0.1; SM-G532M Build/MMB29T; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/55.0.2883.91 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.111 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 5.1.1; SM-J120M Build/LMY47X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.0; Moto G (5) Build/NPPS25.137-93-14) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.0; SM-G570M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 5.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0; CAM-L03 Build/HUAWEICAM-L03) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.76 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.63 Safari/534.3\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.517.44 Safari/534.7\",\n\t\"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.63 Safari/534.3\",\n\t\"Mozilla/5.0 (Linux; Android 8.0.0; FIG-LX3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.237 Safari/534.10\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36\",\n\t\"Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/5.0.342.1 Safari/533.2\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.89 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36\",\n\t\"Mozilla/5.0 (X11; Datanyze; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 5.1.1; SM-J111M Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.120 Safari/537.36\",\n\t\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.107 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0.1; SM-J700M Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.63 Safari/537.36\",\n\t\"Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Slackware/Chrome/12.0.742.100 Safari/534.30\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.167 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36\",\n\t\"Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.100 Safari/534.30\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 8.0.0; WAS-LX3 Build/HUAWEIWAS-LX3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.1805 Safari/537.36 MVisionPlayer/1.0.0.0\",\n\t\"Mozilla/5.0 (Linux; Android 7.0; TRT-LX3 Build/HUAWEITRT-LX3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.89 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0; vivo 1610 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.124 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 4.4.2; de-de; SAMSUNG GT-I9195 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Version/1.5 Chrome/28.0.1500.94 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\",\n\t\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 8.0.0; ANE-LX3 Build/HUAWEIANE-LX3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36\",\n\t\"Mozilla/5.0 (X11; U; Linux i586; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/5.0.342.1 Safari/533.2\",\n\t\"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.65 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.0; SM-G610M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0.1; SM-J500M Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.517.44 Safari/534.7\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.104 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0; vivo 1606 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.124 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36\",\n\t\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.0; SM-G610M Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.1; vivo 1716 Build/N2G47H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.98 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.93 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.0; SM-G570M Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0; MYA-L22 Build/HUAWEIMYA-L22) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 5.1; A1601 Build/LMY47I) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.98 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.0; TRT-LX2 Build/HUAWEITRT-LX2; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/59.0.3071.125 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 5.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.17 (KHTML, like Gecko) Chrome/10.0.649.0 Safari/534.17\",\n\t\"Mozilla/5.0 (Linux; Android 6.0; CAM-L21 Build/HUAWEICAM-L21; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/62.0.3202.84 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36\",\n\t\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.3 Safari/534.24\",\n\t\"Mozilla/5.0 (Linux; Android 7.1.2; Redmi 4X Build/N2G47H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 4.4.2; SM-G7102 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.109 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 5.1; HUAWEI CUN-L22 Build/HUAWEICUN-L22; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/62.0.3202.84 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 5.1.1; A37fw Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.0; SM-J730GM Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.0; SM-G610F Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.1.2; Redmi Note 5A Build/N2G47H; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/63.0.3239.111 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.0; Redmi Note 4 Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36\",\n\t\"Mozilla/5.0 (Unknown; Linux) AppleWebKit/538.1 (KHTML, like Gecko) Chrome/v1.0.0 Safari/538.1\",\n\t\"Mozilla/5.0 (Linux; Android 7.0; BLL-L22 Build/HUAWEIBLL-L22) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.0; SM-J710F Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0.1; SM-G532M Build/MMB29T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.1.1; CPH1723 Build/N6F26Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.98 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 8.0.0; FIG-LX3 Build/HUAWEIFIG-LX3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows; U; Windows NT 6.1; de-DE) AppleWebKit/534.17 (KHTML, like Gecko) Chrome/10.0.649.0 Safari/534.17\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.63 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.65 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.1; Mi A1 Build/N2G47H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.99 Safari/533.4\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.89 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36 MVisionPlayer/1.0.0.0\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 5.1; A37f Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.93 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.76 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 5.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0.1; CPH1607 Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/63.0.3239.111 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\",\n\t\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0.1; vivo 1603 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0.1; SM-G532M Build/MMB29T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0.1; Redmi 4A Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/60.0.3112.116 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36\",\n\t\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.71 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 5.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0.1; SM-G532G Build/MMB29T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.83 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.109 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.131 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0; vivo 1713 Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.124 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.89 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36\",\n}\n\nvar (\n\thostsLen = len(hostsSuffix)\n\tuaLen    = len(userAgents)\n)\n\nfunc RandHost() string {\n\tbase := strings.ToLower(base64.RawURLEncoding.EncodeToString(utils.NewUUIDV4().Bytes()))\n\tbase = strings.ReplaceAll(base, \"-\", \"\")\n\tbase = strings.ReplaceAll(base, \"_\", \"\")\n\tbuf := []byte(base)\n\tprefix := string(buf[:3]) + \"---\"\n\tprefix += string(buf[6:8]) + \"-\"\n\tprefix += string(buf[len(buf)-8:])\n\n\treturn prefix + hostsSuffix[randv2.IntN(hostsLen)]\n}\n\nfunc RandUserAgent() string {\n\treturn userAgents[randv2.IntN(uaLen)]\n}\n\nfunc SetUserAgent(header http.Header) {\n\tif header.Get(\"User-Agent\") != \"\" {\n\t\treturn\n\t}\n\tuserAgent := RandUserAgent()\n\theader.Set(\"User-Agent\", userAgent)\n}\n\nfunc VerifyMethod(cipher, password string) (err error) {\n\t_, err = shadowimpl.FetchMethod(cipher, password, time.Now)\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/convert/v.go",
    "content": "package convert\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nfunc handleVShareLink(names map[string]int, url *url.URL, scheme string, proxy map[string]any) error {\n\t// Xray VMessAEAD / VLESS share link standard\n\t// https://github.com/XTLS/Xray-core/discussions/716\n\tquery := url.Query()\n\tproxy[\"name\"] = uniqueName(names, url.Fragment)\n\tif url.Hostname() == \"\" {\n\t\treturn errors.New(\"url.Hostname() is empty\")\n\t}\n\tif url.Port() == \"\" {\n\t\treturn errors.New(\"url.Port() is empty\")\n\t}\n\tproxy[\"type\"] = scheme\n\tproxy[\"server\"] = url.Hostname()\n\tproxy[\"port\"] = url.Port()\n\tproxy[\"uuid\"] = url.User.Username()\n\tproxy[\"udp\"] = true\n\ttls := strings.ToLower(query.Get(\"security\"))\n\tif strings.HasSuffix(tls, \"tls\") || tls == \"reality\" {\n\t\tproxy[\"tls\"] = true\n\t\tif fingerprint := query.Get(\"fp\"); fingerprint == \"\" {\n\t\t\tproxy[\"client-fingerprint\"] = \"chrome\"\n\t\t} else {\n\t\t\tproxy[\"client-fingerprint\"] = fingerprint\n\t\t}\n\t\tif alpn := query.Get(\"alpn\"); alpn != \"\" {\n\t\t\tproxy[\"alpn\"] = strings.Split(alpn, \",\")\n\t\t}\n\t\tif pcs := query.Get(\"pcs\"); pcs != \"\" {\n\t\t\tproxy[\"fingerprint\"] = pcs\n\t\t}\n\t}\n\tif sni := query.Get(\"sni\"); sni != \"\" {\n\t\tproxy[\"servername\"] = sni\n\t}\n\tif realityPublicKey := query.Get(\"pbk\"); realityPublicKey != \"\" {\n\t\tproxy[\"reality-opts\"] = map[string]any{\n\t\t\t\"public-key\": realityPublicKey,\n\t\t\t\"short-id\":   query.Get(\"sid\"),\n\t\t}\n\t}\n\n\tswitch query.Get(\"packetEncoding\") {\n\tcase \"none\":\n\tcase \"packet\":\n\t\tproxy[\"packet-addr\"] = true\n\tdefault:\n\t\tproxy[\"xudp\"] = true\n\t}\n\n\tnetwork := strings.ToLower(query.Get(\"type\"))\n\tif network == \"\" {\n\t\tnetwork = \"tcp\"\n\t}\n\tfakeType := strings.ToLower(query.Get(\"headerType\"))\n\tif network == \"tcp\" && fakeType == \"http\" {\n\t\tnetwork = \"http\"\n\t} else if network == \"http\" {\n\t\tnetwork = \"h2\"\n\t}\n\tproxy[\"network\"] = network\n\tswitch network {\n\tcase \"tcp\":\n\tcase \"http\":\n\t\theaders := make(map[string]any)\n\t\thttpOpts := make(map[string]any)\n\t\thttpOpts[\"path\"] = []string{\"/\"}\n\n\t\tif host := query.Get(\"host\"); host != \"\" {\n\t\t\theaders[\"Host\"] = []string{host}\n\t\t}\n\n\t\tif method := query.Get(\"method\"); method != \"\" {\n\t\t\thttpOpts[\"method\"] = method\n\t\t}\n\n\t\tif path := query.Get(\"path\"); path != \"\" {\n\t\t\thttpOpts[\"path\"] = []string{path}\n\t\t}\n\t\thttpOpts[\"headers\"] = headers\n\t\tproxy[\"http-opts\"] = httpOpts\n\n\tcase \"h2\":\n\t\th2Opts := make(map[string]any)\n\t\th2Opts[\"path\"] = \"/\"\n\t\tif path := query.Get(\"path\"); path != \"\" {\n\t\t\th2Opts[\"path\"] = path\n\t\t}\n\t\tif host := query.Get(\"host\"); host != \"\" {\n\t\t\th2Opts[\"host\"] = []string{host}\n\t\t}\n\t\tproxy[\"h2-opts\"] = h2Opts\n\n\tcase \"ws\", \"httpupgrade\":\n\t\theaders := make(map[string]any)\n\t\twsOpts := make(map[string]any)\n\t\theaders[\"User-Agent\"] = RandUserAgent()\n\t\theaders[\"Host\"] = query.Get(\"host\")\n\t\twsOpts[\"path\"] = query.Get(\"path\")\n\t\twsOpts[\"headers\"] = headers\n\n\t\tif earlyData := query.Get(\"ed\"); earlyData != \"\" {\n\t\t\tmed, err := strconv.Atoi(earlyData)\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"bad WebSocket max early data size: %v\", err)\n\t\t\t}\n\t\t\tswitch network {\n\t\t\tcase \"ws\":\n\t\t\t\twsOpts[\"max-early-data\"] = med\n\t\t\t\twsOpts[\"early-data-header-name\"] = \"Sec-WebSocket-Protocol\"\n\t\t\tcase \"httpupgrade\":\n\t\t\t\twsOpts[\"v2ray-http-upgrade-fast-open\"] = true\n\t\t\t}\n\t\t}\n\t\tif earlyDataHeader := query.Get(\"eh\"); earlyDataHeader != \"\" {\n\t\t\twsOpts[\"early-data-header-name\"] = earlyDataHeader\n\t\t}\n\n\t\tproxy[\"ws-opts\"] = wsOpts\n\n\tcase \"grpc\":\n\t\tgrpcOpts := make(map[string]any)\n\t\tgrpcOpts[\"grpc-service-name\"] = query.Get(\"serviceName\")\n\t\tproxy[\"grpc-opts\"] = grpcOpts\n\n\tcase \"xhttp\":\n\t\tproxy[\"network\"] = \"xhttp\"\n\t\txhttpOpts := make(map[string]any)\n\n\t\tif path := query.Get(\"path\"); path != \"\" {\n\t\t\txhttpOpts[\"path\"] = path\n\t\t}\n\n\t\tif host := query.Get(\"host\"); host != \"\" {\n\t\t\txhttpOpts[\"host\"] = host\n\t\t}\n\n\t\tif mode := query.Get(\"mode\"); mode != \"\" {\n\t\t\txhttpOpts[\"mode\"] = mode\n\t\t}\n\n\t\tif extra := query.Get(\"extra\"); extra != \"\" {\n\t\t\tvar extraMap map[string]any\n\t\t\tif err := json.Unmarshal([]byte(extra), &extraMap); err == nil {\n\t\t\t\tparseXHTTPExtra(extraMap, xhttpOpts)\n\t\t\t}\n\t\t}\n\n\t\tproxy[\"xhttp-opts\"] = xhttpOpts\n\t}\n\n\treturn nil\n}\n\n// parseXHTTPExtra maps xray-core extra JSON fields to mihomo xhttp-opts fields.\nfunc parseXHTTPExtra(extra map[string]any, opts map[string]any) {\n\t// xmuxToReuse converts an xmux map to mihomo reuse-settings.\n\txmuxToReuse := func(xmux map[string]any) map[string]any {\n\t\treuse := make(map[string]any)\n\t\tset := func(src, dst string) {\n\t\t\tif v, ok := xmux[src]; ok {\n\t\t\t\tswitch val := v.(type) {\n\t\t\t\tcase string:\n\t\t\t\t\tif val != \"\" {\n\t\t\t\t\t\treuse[dst] = val\n\t\t\t\t\t}\n\t\t\t\tcase float64:\n\t\t\t\t\treuse[dst] = strconv.FormatInt(int64(val), 10)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tset(\"maxConnections\", \"max-connections\")\n\t\tset(\"maxConcurrency\", \"max-concurrency\")\n\t\tset(\"cMaxReuseTimes\", \"c-max-reuse-times\")\n\t\tset(\"hMaxRequestTimes\", \"h-max-request-times\")\n\t\tset(\"hMaxReusableSecs\", \"h-max-reusable-secs\")\n\t\tif v, ok := xmux[\"hKeepAlivePeriod\"].(float64); ok {\n\t\t\treuse[\"h-keep-alive-period\"] = int(v)\n\t\t}\n\t\treturn reuse\n\t}\n\n\tif v, ok := extra[\"noGRPCHeader\"].(bool); ok && v {\n\t\topts[\"no-grpc-header\"] = true\n\t}\n\n\tif v, ok := extra[\"xPaddingBytes\"].(string); ok && v != \"\" {\n\t\topts[\"x-padding-bytes\"] = v\n\t}\n\n\tif v, ok := extra[\"xPaddingObfsMode\"].(bool); ok {\n\t\topts[\"x-padding-obfs-mode\"] = v\n\t}\n\n\tif v, ok := extra[\"xPaddingKey\"].(string); ok && v != \"\" {\n\t\topts[\"x-padding-key\"] = v\n\t}\n\n\tif v, ok := extra[\"xPaddingHeader\"].(string); ok && v != \"\" {\n\t\topts[\"x-padding-header\"] = v\n\t}\n\n\tif v, ok := extra[\"xPaddingPlacement\"].(string); ok && v != \"\" {\n\t\topts[\"x-padding-placement\"] = v\n\t}\n\n\tif v, ok := extra[\"xPaddingMethod\"].(string); ok && v != \"\" {\n\t\topts[\"x-padding-method\"] = v\n\t}\n\n\tif v, ok := extra[\"uplinkHttpMethod\"].(string); ok && v != \"\" {\n\t\topts[\"uplink-http-method\"] = v\n\t}\n\n\tif v, ok := extra[\"sessionPlacement\"].(string); ok && v != \"\" {\n\t\topts[\"session-placement\"] = v\n\t}\n\n\tif v, ok := extra[\"sessionKey\"].(string); ok && v != \"\" {\n\t\topts[\"session-key\"] = v\n\t}\n\n\tif v, ok := extra[\"seqPlacement\"].(string); ok && v != \"\" {\n\t\topts[\"seq-placement\"] = v\n\t}\n\n\tif v, ok := extra[\"seqKey\"].(string); ok && v != \"\" {\n\t\topts[\"seq-key\"] = v\n\t}\n\n\tif v, ok := extra[\"uplinkDataPlacement\"].(string); ok && v != \"\" {\n\t\topts[\"uplink-data-placement\"] = v\n\t}\n\n\tif v, ok := extra[\"uplinkDataKey\"].(string); ok && v != \"\" {\n\t\topts[\"uplink-data-key\"] = v\n\t}\n\n\tif v, ok := extra[\"uplinkChunkSize\"].(float64); ok {\n\t\topts[\"uplink-chunk-size\"] = int(v)\n\t}\n\n\tif v, ok := extra[\"scMaxEachPostBytes\"].(float64); ok {\n\t\topts[\"sc-max-each-post-bytes\"] = int(v)\n\t}\n\n\tif v, ok := extra[\"scMinPostsIntervalMs\"].(float64); ok {\n\t\topts[\"sc-min-posts-interval-ms\"] = int(v)\n\t}\n\n\t// xmux in root extra → reuse-settings\n\tif xmuxAny, ok := extra[\"xmux\"].(map[string]any); ok && len(xmuxAny) > 0 {\n\t\tif reuse := xmuxToReuse(xmuxAny); len(reuse) > 0 {\n\t\t\topts[\"reuse-settings\"] = reuse\n\t\t}\n\t}\n\n\tif dsAny, ok := extra[\"downloadSettings\"].(map[string]any); ok {\n\t\tds := make(map[string]any)\n\n\t\tif addr, ok := dsAny[\"address\"].(string); ok && addr != \"\" {\n\t\t\tds[\"server\"] = addr\n\t\t}\n\n\t\tif port, ok := dsAny[\"port\"].(float64); ok {\n\t\t\tds[\"port\"] = int(port)\n\t\t}\n\n\t\tsec := \"\"\n\t\tif s, ok := dsAny[\"security\"].(string); ok {\n\t\t\tsec = strings.ToLower(s)\n\t\t}\n\n\t\tif sec == \"tls\" || sec == \"reality\" {\n\t\t\tds[\"tls\"] = true\n\n\t\t\tif tlsAny, ok := dsAny[\"tlsSettings\"].(map[string]any); ok {\n\t\t\t\tif sn, ok := tlsAny[\"serverName\"].(string); ok && sn != \"\" {\n\t\t\t\t\tds[\"servername\"] = sn\n\t\t\t\t}\n\t\t\t\tif fp, ok := tlsAny[\"fingerprint\"].(string); ok && fp != \"\" {\n\t\t\t\t\tds[\"client-fingerprint\"] = fp\n\t\t\t\t}\n\t\t\t\tif alpnAny, ok := tlsAny[\"alpn\"].([]any); ok && len(alpnAny) > 0 {\n\t\t\t\t\talpnList := make([]string, 0, len(alpnAny))\n\t\t\t\t\tfor _, a := range alpnAny {\n\t\t\t\t\t\tif s, ok := a.(string); ok {\n\t\t\t\t\t\t\talpnList = append(alpnList, s)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif len(alpnList) > 0 {\n\t\t\t\t\t\tds[\"alpn\"] = alpnList\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif v, ok := tlsAny[\"allowInsecure\"].(bool); ok && v {\n\t\t\t\t\tds[\"skip-cert-verify\"] = true\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif sec == \"reality\" {\n\t\t\t\tif realityAny, ok := dsAny[\"realitySettings\"].(map[string]any); ok {\n\t\t\t\t\trealityOpts := make(map[string]any)\n\t\t\t\t\tif pk, ok := realityAny[\"publicKey\"].(string); ok && pk != \"\" {\n\t\t\t\t\t\trealityOpts[\"public-key\"] = pk\n\t\t\t\t\t}\n\t\t\t\t\tif sid, ok := realityAny[\"shortId\"].(string); ok && sid != \"\" {\n\t\t\t\t\t\trealityOpts[\"short-id\"] = sid\n\t\t\t\t\t}\n\t\t\t\t\tif len(realityOpts) > 0 {\n\t\t\t\t\t\tds[\"reality-opts\"] = realityOpts\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif xhttpAny, ok := dsAny[\"xhttpSettings\"].(map[string]any); ok {\n\t\t\tif path, ok := xhttpAny[\"path\"].(string); ok && path != \"\" {\n\t\t\t\tds[\"path\"] = path\n\t\t\t}\n\t\t\tif host, ok := xhttpAny[\"host\"].(string); ok && host != \"\" {\n\t\t\t\tds[\"host\"] = host\n\t\t\t}\n\t\t\tif headers, ok := xhttpAny[\"headers\"].(map[string]any); ok && len(headers) > 0 {\n\t\t\t\tds[\"headers\"] = headers\n\t\t\t}\n\n\t\t\t// xmux inside downloadSettings.xhttpSettings.extra → download-settings.reuse-settings\n\t\t\tif dsExtraAny, ok := xhttpAny[\"extra\"].(map[string]any); ok {\n\t\t\t\tif xmuxAny, ok := dsExtraAny[\"xmux\"].(map[string]any); ok && len(xmuxAny) > 0 {\n\t\t\t\t\tif reuse := xmuxToReuse(xmuxAny); len(reuse) > 0 {\n\t\t\t\t\t\tds[\"reuse-settings\"] = reuse\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif len(ds) > 0 {\n\t\t\topts[\"download-settings\"] = ds\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/deque/deque.go",
    "content": "package deque\n\n// copy and modified from https://github.com/gammazero/deque/blob/v1.2.0/deque.go\n// which is licensed under MIT.\n\nimport (\n\t\"fmt\"\n)\n\n// minCapacity is the smallest capacity that deque may have. Must be power of 2\n// for bitwise modulus: x % n == x & (n - 1).\nconst minCapacity = 8\n\n// Deque represents a single instance of the deque data structure. A Deque\n// instance contains items of the type specified by the type argument.\n//\n// For example, to create a Deque that contains strings do one of the\n// following:\n//\n//\tvar stringDeque deque.Deque[string]\n//\tstringDeque := new(deque.Deque[string])\n//\tstringDeque := &deque.Deque[string]{}\n//\n// To create a Deque that will never resize to have space for less than 64\n// items, specify a base capacity:\n//\n//\tvar d deque.Deque[int]\n//\td.SetBaseCap(64)\n//\n// To ensure the Deque can store 1000 items without needing to resize while\n// items are added:\n//\n//\td.Grow(1000)\n//\n// Any values supplied to [SetBaseCap] and [Grow] are rounded up to the nearest\n// power of 2, since the Deque grows by powers of 2.\ntype Deque[T any] struct {\n\tbuf    []T\n\thead   int\n\ttail   int\n\tcount  int\n\tminCap int\n}\n\n// Cap returns the current capacity of the Deque. If q is nil, q.Cap() is zero.\nfunc (q *Deque[T]) Cap() int {\n\tif q == nil {\n\t\treturn 0\n\t}\n\treturn len(q.buf)\n}\n\n// Len returns the number of elements currently stored in the queue. If q is\n// nil, q.Len() returns zero.\nfunc (q *Deque[T]) Len() int {\n\tif q == nil {\n\t\treturn 0\n\t}\n\treturn q.count\n}\n\n// PushBack appends an element to the back of the queue. Implements FIFO when\n// elements are removed with [PopFront], and LIFO when elements are removed with\n// [PopBack].\nfunc (q *Deque[T]) PushBack(elem T) {\n\tq.growIfFull()\n\n\tq.buf[q.tail] = elem\n\t// Calculate new tail position.\n\tq.tail = q.next(q.tail)\n\tq.count++\n}\n\n// PushFront prepends an element to the front of the queue.\nfunc (q *Deque[T]) PushFront(elem T) {\n\tq.growIfFull()\n\n\t// Calculate new head position.\n\tq.head = q.prev(q.head)\n\tq.buf[q.head] = elem\n\tq.count++\n}\n\n// PopFront removes and returns the element from the front of the queue.\n// Implements FIFO when used with [PushBack]. If the queue is empty, the call\n// panics.\nfunc (q *Deque[T]) PopFront() T {\n\tif q.count <= 0 {\n\t\tpanic(\"deque: PopFront() called on empty queue\")\n\t}\n\tret := q.buf[q.head]\n\tvar zero T\n\tq.buf[q.head] = zero\n\t// Calculate new head position.\n\tq.head = q.next(q.head)\n\tq.count--\n\n\tq.shrinkIfExcess()\n\treturn ret\n}\n\n// IterPopFront returns an iterator that iteratively removes items from the\n// front of the deque. This is more efficient than removing items one at a time\n// because it avoids intermediate resizing. If a resize is necessary, only one\n// is done when iteration ends.\nfunc (q *Deque[T]) IterPopFront() func(yield func(T) bool) {\n\treturn func(yield func(T) bool) {\n\t\tif q.Len() == 0 {\n\t\t\treturn\n\t\t}\n\t\tvar zero T\n\t\tfor q.count != 0 {\n\t\t\tret := q.buf[q.head]\n\t\t\tq.buf[q.head] = zero\n\t\t\tq.head = q.next(q.head)\n\t\t\tq.count--\n\t\t\tif !yield(ret) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tq.shrinkToFit()\n\t}\n}\n\n// PopBack removes and returns the element from the back of the queue.\n// Implements LIFO when used with [PushBack]. If the queue is empty, the call\n// panics.\nfunc (q *Deque[T]) PopBack() T {\n\tif q.count <= 0 {\n\t\tpanic(\"deque: PopBack() called on empty queue\")\n\t}\n\n\t// Calculate new tail position\n\tq.tail = q.prev(q.tail)\n\n\t// Remove value at tail.\n\tret := q.buf[q.tail]\n\tvar zero T\n\tq.buf[q.tail] = zero\n\tq.count--\n\n\tq.shrinkIfExcess()\n\treturn ret\n}\n\n// IterPopBack returns an iterator that iteratively removes items from the back\n// of the deque. This is more efficient than removing items one at a time\n// because it avoids intermediate resizing. If a resize is necessary, only one\n// is done when iteration ends.\nfunc (q *Deque[T]) IterPopBack() func(yield func(T) bool) {\n\treturn func(yield func(T) bool) {\n\t\tif q.Len() == 0 {\n\t\t\treturn\n\t\t}\n\t\tvar zero T\n\t\tfor q.count != 0 {\n\t\t\tq.tail = q.prev(q.tail)\n\t\t\tret := q.buf[q.tail]\n\t\t\tq.buf[q.tail] = zero\n\t\t\tq.count--\n\t\t\tif !yield(ret) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tq.shrinkToFit()\n\t}\n}\n\n// Front returns the element at the front of the queue. This is the element\n// that would be returned by [PopFront]. This call panics if the queue is\n// empty.\nfunc (q *Deque[T]) Front() T {\n\tif q.count <= 0 {\n\t\tpanic(\"deque: Front() called when empty\")\n\t}\n\treturn q.buf[q.head]\n}\n\n// Back returns the element at the back of the queue. This is the element that\n// would be returned by [PopBack]. This call panics if the queue is empty.\nfunc (q *Deque[T]) Back() T {\n\tif q.count <= 0 {\n\t\tpanic(\"deque: Back() called when empty\")\n\t}\n\treturn q.buf[q.prev(q.tail)]\n}\n\n// At returns the element at index i in the queue without removing the element\n// from the queue. This method accepts only non-negative index values. At(0)\n// refers to the first element and is the same as [Front]. At(Len()-1) refers\n// to the last element and is the same as [Back]. If the index is invalid, the\n// call panics.\n//\n// The purpose of At is to allow Deque to serve as a more general purpose\n// circular buffer, where items are only added to and removed from the ends of\n// the deque, but may be read from any place within the deque. Consider the\n// case of a fixed-size circular log buffer: A new entry is pushed onto one end\n// and when full the oldest is popped from the other end. All the log entries\n// in the buffer must be readable without altering the buffer contents.\nfunc (q *Deque[T]) At(i int) T {\n\tq.checkRange(i)\n\t// bitwise modulus\n\treturn q.buf[(q.head+i)&(len(q.buf)-1)]\n}\n\n// Set assigns the item to index i in the queue. Set indexes the deque the same\n// as [At] but perform the opposite operation. If the index is invalid, the call\n// panics.\nfunc (q *Deque[T]) Set(i int, item T) {\n\tq.checkRange(i)\n\t// bitwise modulus\n\tq.buf[(q.head+i)&(len(q.buf)-1)] = item\n}\n\n// Iter returns a go iterator to range over all items in the Deque, yielding\n// each item from front (index 0) to back (index Len()-1). Modification of\n// Deque during iteration panics.\nfunc (q *Deque[T]) Iter() func(yield func(T) bool) {\n\treturn func(yield func(T) bool) {\n\t\torigHead := q.head\n\t\torigTail := q.tail\n\t\thead := origHead\n\t\tfor i := -0; i < q.Len(); i++ {\n\t\t\tif q.head != origHead || q.tail != origTail {\n\t\t\t\tpanic(\"deque: modified during iteration\")\n\t\t\t}\n\t\t\tif !yield(q.buf[head]) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\thead = q.next(head)\n\t\t}\n\t}\n}\n\n// RIter returns a reverse go iterator to range over all items in the Deque,\n// yielding each item from back (index Len()-1) to front (index 0).\n// Modification of Deque during iteration panics.\nfunc (q *Deque[T]) RIter() func(yield func(T) bool) {\n\treturn func(yield func(T) bool) {\n\t\torigHead := q.head\n\t\torigTail := q.tail\n\t\ttail := origTail\n\t\tfor i := -0; i < q.Len(); i++ {\n\t\t\tif q.head != origHead || q.tail != origTail {\n\t\t\t\tpanic(\"deque: modified during iteration\")\n\t\t\t}\n\t\t\ttail = q.prev(tail)\n\t\t\tif !yield(q.buf[tail]) {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n\n// Clear removes all elements from the queue, but retains the current capacity.\n// This is useful when repeatedly reusing the queue at high frequency to avoid\n// GC during reuse. The queue will not be resized smaller as long as items are\n// only added. Only when items are removed is the queue subject to getting\n// resized smaller.\nfunc (q *Deque[T]) Clear() {\n\tif q.Len() == 0 {\n\t\treturn\n\t}\n\thead, tail := q.head, q.tail\n\tq.count = 0\n\tq.head = 0\n\tq.tail = 0\n\n\tif head >= tail {\n\t\t// [DEF....ABC]\n\t\tclearSlice(q.buf[head:])\n\t\thead = 0\n\t}\n\tclearSlice(q.buf[head:tail])\n}\n\nfunc clearSlice[S ~[]E, E any](s S) {\n\tvar zero E\n\tfor i := range s {\n\t\ts[i] = zero\n\t}\n}\n\n// Grow grows deque's capacity, if necessary, to guarantee space for another n\n// items. After Grow(n), at least n items can be written to the deque without\n// another allocation. If n is negative, Grow panics.\nfunc (q *Deque[T]) Grow(n int) {\n\tif n < 0 {\n\t\tpanic(\"deque.Grow: negative count\")\n\t}\n\tc := q.Cap()\n\tl := q.Len()\n\t// If already big enough.\n\tif n <= c-l {\n\t\treturn\n\t}\n\n\tif c == 0 {\n\t\tc = minCapacity\n\t}\n\n\tnewLen := l + n\n\tfor c < newLen {\n\t\tc <<= 1\n\t}\n\tif l == 0 {\n\t\tq.buf = make([]T, c)\n\t\tq.head = 0\n\t\tq.tail = 0\n\t} else {\n\t\tq.resize(c)\n\t}\n}\n\n// Copy copies the contents of the given src Deque into this Deque.\n//\n//\tn := b.Copy(a)\n//\n// is an efficient shortcut for\n//\n//\tb.Clear()\n//\tn := a.Len()\n//\tb.Grow(n)\n//\tfor i := 0; i < n; i++ {\n//\t\tb.PushBack(a.At(i))\n//\t}\nfunc (q *Deque[T]) Copy(src Deque[T]) int {\n\tq.Clear()\n\tq.Grow(src.Len())\n\tn := src.CopyOutSlice(q.buf)\n\tq.count = n\n\tq.tail = n\n\tq.head = 0\n\treturn n\n}\n\n// AppendToSlice appends from the Deque to the given slice. If the slice has\n// insufficient capacity to store all elements in Deque, then allocate a new\n// slice. Returns the resulting slice.\n//\n//\tout = q.AppendToSlice(out)\n//\n// is an efficient shortcut for\n//\n//\tfor i := 0; i < q.Len(); i++ {\n//\t\tx = append(out, q.At(i))\n//\t}\nfunc (q *Deque[T]) AppendToSlice(out []T) []T {\n\tif q.count == 0 {\n\t\treturn out\n\t}\n\n\thead, tail := q.head, q.tail\n\n\tif head >= tail {\n\t\t// [DEF....ABC]\n\t\tout = append(out, q.buf[head:]...)\n\t\thead = 0\n\t}\n\treturn append(out, q.buf[head:tail]...)\n}\n\n// CopyInSlice replaces the contents of Deque with all the elements from the\n// given slice, in. If len(in) is zero, then this is equivalent to calling\n// [Clear].\n//\n//\tq.CopyInSlice(in)\n//\n// is an efficient shortcut for\n//\n//\tq.Clear()\n//\tfor i := range in {\n//\t\tq.PushBack(in[i])\n//\t}\nfunc (q *Deque[T]) CopyInSlice(in []T) {\n\t// Allocate new buffer if more space needed.\n\tif len(q.buf) < len(in) {\n\t\tnewCap := len(q.buf)\n\t\tif newCap == 0 {\n\t\t\tnewCap = minCapacity\n\t\t\tq.minCap = minCapacity\n\t\t}\n\t\tfor newCap < len(in) {\n\t\t\tnewCap <<= 1\n\t\t}\n\t\tq.buf = make([]T, newCap)\n\t} else if len(q.buf) > len(in) {\n\t\tq.Clear()\n\t}\n\tn := copy(q.buf, in)\n\tq.count = n\n\tq.tail = n\n\tq.head = 0\n}\n\n// CopyOutSlice copies elements from the Deque into the given slice, up to the\n// size of the buffer. Returns the number of elements copied, which will be the\n// minimum of q.Len() and len(out).\n//\n//\tn := q.CopyOutSlice(out)\n//\n// is an efficient shortcut for\n//\n//\tn := min(len(out), q.Len())\n//\tfor i := 0; i < n; i++ {\n//\t \tout[i] = q.At(i)\n//\t}\n//\n// This function is preferable to one that returns a copy of the internal\n// buffer because this allows reuse of memory receiving data, for repeated copy\n// operations.\nfunc (q *Deque[T]) CopyOutSlice(out []T) int {\n\tif q.count == 0 || len(out) == 0 {\n\t\treturn 0\n\t}\n\n\thead, tail := q.head, q.tail\n\tvar n int\n\n\tif head >= tail {\n\t\t// [DEF....ABC]\n\t\tn = copy(out, q.buf[head:])\n\t\tout = out[n:]\n\t\tif len(out) == 0 {\n\t\t\treturn n\n\t\t}\n\t\thead = 0\n\t}\n\tn += copy(out, q.buf[head:tail])\n\n\treturn n\n}\n\n// Rotate rotates the deque n steps front-to-back. If n is negative, rotates\n// back-to-front. Having Deque provide Rotate avoids resizing that could happen\n// if implementing rotation using only Pop and Push methods. If q.Len() is one\n// or less, or q is nil, then Rotate does nothing.\nfunc (q *Deque[T]) Rotate(n int) {\n\tif q.Len() <= 1 {\n\t\treturn\n\t}\n\t// Rotating a multiple of q.count is same as no rotation.\n\tn %= q.count\n\tif n == 0 {\n\t\treturn\n\t}\n\n\tmodBits := len(q.buf) - 1\n\t// If no empty space in buffer, only move head and tail indexes.\n\tif q.head == q.tail {\n\t\t// Calculate new head and tail using bitwise modulus.\n\t\tq.head = (q.head + n) & modBits\n\t\tq.tail = q.head\n\t\treturn\n\t}\n\n\tvar zero T\n\n\tif n < 0 {\n\t\t// Rotate back to front.\n\t\tfor ; n < 0; n++ {\n\t\t\t// Calculate new head and tail using bitwise modulus.\n\t\t\tq.head = (q.head - 1) & modBits\n\t\t\tq.tail = (q.tail - 1) & modBits\n\t\t\t// Put tail value at head and remove value at tail.\n\t\t\tq.buf[q.head] = q.buf[q.tail]\n\t\t\tq.buf[q.tail] = zero\n\t\t}\n\t\treturn\n\t}\n\n\t// Rotate front to back.\n\tfor ; n > 0; n-- {\n\t\t// Put head value at tail and remove value at head.\n\t\tq.buf[q.tail] = q.buf[q.head]\n\t\tq.buf[q.head] = zero\n\t\t// Calculate new head and tail using bitwise modulus.\n\t\tq.head = (q.head + 1) & modBits\n\t\tq.tail = (q.tail + 1) & modBits\n\t}\n}\n\n// Index returns the index into the Deque of the first item satisfying f(item),\n// or -1 if none do. If q is nil, then -1 is always returned. Search is linear\n// starting with index 0.\nfunc (q *Deque[T]) Index(f func(T) bool) int {\n\tif q.Len() > 0 {\n\t\tmodBits := len(q.buf) - 1\n\t\tfor i := 0; i < q.count; i++ {\n\t\t\tif f(q.buf[(q.head+i)&modBits]) {\n\t\t\t\treturn i\n\t\t\t}\n\t\t}\n\t}\n\treturn -1\n}\n\n// RIndex is the same as Index, but searches from Back to Front. The index\n// returned is from Front to Back, where index 0 is the index of the item\n// returned by [Front].\nfunc (q *Deque[T]) RIndex(f func(T) bool) int {\n\tif q.Len() > 0 {\n\t\tmodBits := len(q.buf) - 1\n\t\tfor i := q.count - 1; i >= 0; i-- {\n\t\t\tif f(q.buf[(q.head+i)&modBits]) {\n\t\t\t\treturn i\n\t\t\t}\n\t\t}\n\t}\n\treturn -1\n}\n\n// Insert is used to insert an element into the middle of the queue, before the\n// element at the specified index. Insert(0,e) is the same as PushFront(e) and\n// Insert(Len(),e) is the same as PushBack(e). Out of range indexes result in\n// pushing the item onto the front of back of the deque.\n//\n// Important: Deque is optimized for O(1) operations at the ends of the queue,\n// not for operations in the the middle. Complexity of this function is\n// constant plus linear in the lesser of the distances between the index and\n// either of the ends of the queue.\nfunc (q *Deque[T]) Insert(at int, item T) {\n\tif at <= 0 {\n\t\tq.PushFront(item)\n\t\treturn\n\t}\n\tif at >= q.Len() {\n\t\tq.PushBack(item)\n\t\treturn\n\t}\n\tif at*2 < q.count {\n\t\tq.PushFront(item)\n\t\tfront := q.head\n\t\tfor i := 0; i < at; i++ {\n\t\t\tnext := q.next(front)\n\t\t\tq.buf[front], q.buf[next] = q.buf[next], q.buf[front]\n\t\t\tfront = next\n\t\t}\n\t\treturn\n\t}\n\tswaps := q.count - at\n\tq.PushBack(item)\n\tback := q.prev(q.tail)\n\tfor i := 0; i < swaps; i++ {\n\t\tprev := q.prev(back)\n\t\tq.buf[back], q.buf[prev] = q.buf[prev], q.buf[back]\n\t\tback = prev\n\t}\n}\n\n// Remove removes and returns an element from the middle of the queue, at the\n// specified index. Remove(0) is the same as [PopFront] and Remove(Len()-1) is\n// the same as [PopBack]. Accepts only non-negative index values, and panics if\n// index is out of range.\n//\n// Important: Deque is optimized for O(1) operations at the ends of the queue,\n// not for operations in the the middle. Complexity of this function is\n// constant plus linear in the lesser of the distances between the index and\n// either of the ends of the queue.\nfunc (q *Deque[T]) Remove(at int) T {\n\tq.checkRange(at)\n\trm := (q.head + at) & (len(q.buf) - 1)\n\tif at*2 < q.count {\n\t\tfor i := 0; i < at; i++ {\n\t\t\tprev := q.prev(rm)\n\t\t\tq.buf[prev], q.buf[rm] = q.buf[rm], q.buf[prev]\n\t\t\trm = prev\n\t\t}\n\t\treturn q.PopFront()\n\t}\n\tswaps := q.count - at - 1\n\tfor i := 0; i < swaps; i++ {\n\t\tnext := q.next(rm)\n\t\tq.buf[rm], q.buf[next] = q.buf[next], q.buf[rm]\n\t\trm = next\n\t}\n\treturn q.PopBack()\n}\n\n// SetBaseCap sets a base capacity so that at least the specified number of\n// items can always be stored without resizing.\nfunc (q *Deque[T]) SetBaseCap(baseCap int) {\n\tminCap := minCapacity\n\tfor minCap < baseCap {\n\t\tminCap <<= 1\n\t}\n\tq.minCap = minCap\n}\n\n// Swap exchanges the two values at idxA and idxB. It panics if either index is\n// out of range.\nfunc (q *Deque[T]) Swap(idxA, idxB int) {\n\tq.checkRange(idxA)\n\tq.checkRange(idxB)\n\tif idxA == idxB {\n\t\treturn\n\t}\n\n\trealA := (q.head + idxA) & (len(q.buf) - 1)\n\trealB := (q.head + idxB) & (len(q.buf) - 1)\n\tq.buf[realA], q.buf[realB] = q.buf[realB], q.buf[realA]\n}\n\nfunc (q *Deque[T]) checkRange(i int) {\n\tif i < 0 || i >= q.count {\n\t\tpanic(fmt.Sprintf(\"deque: index out of range %d with length %d\", i, q.Len()))\n\t}\n}\n\n// prev returns the previous buffer position wrapping around buffer.\nfunc (q *Deque[T]) prev(i int) int {\n\treturn (i - 1) & (len(q.buf) - 1) // bitwise modulus\n}\n\n// next returns the next buffer position wrapping around buffer.\nfunc (q *Deque[T]) next(i int) int {\n\treturn (i + 1) & (len(q.buf) - 1) // bitwise modulus\n}\n\n// growIfFull resizes up if the buffer is full.\nfunc (q *Deque[T]) growIfFull() {\n\tif q.count != len(q.buf) {\n\t\treturn\n\t}\n\tif len(q.buf) == 0 {\n\t\tif q.minCap == 0 {\n\t\t\tq.minCap = minCapacity\n\t\t}\n\t\tq.buf = make([]T, q.minCap)\n\t\treturn\n\t}\n\tq.resize(q.count << 1)\n}\n\n// shrinkIfExcess resize down if the buffer 1/4 full.\nfunc (q *Deque[T]) shrinkIfExcess() {\n\tif len(q.buf) > q.minCap && (q.count<<2) == len(q.buf) {\n\t\tq.resize(q.count << 1)\n\t}\n}\n\nfunc (q *Deque[T]) shrinkToFit() {\n\tif len(q.buf) > q.minCap && (q.count<<2) <= len(q.buf) {\n\t\tif q.count == 0 {\n\t\t\tq.head = 0\n\t\t\tq.tail = 0\n\t\t\tq.buf = make([]T, q.minCap)\n\t\t\treturn\n\t\t}\n\n\t\tc := q.minCap\n\t\tfor c < q.count {\n\t\t\tc <<= 1\n\t\t}\n\t\tq.resize(c)\n\t}\n}\n\n// resize resizes the deque to fit exactly twice its current contents. This is\n// used to grow the queue when it is full, and also to shrink it when it is\n// only a quarter full.\nfunc (q *Deque[T]) resize(newSize int) {\n\tnewBuf := make([]T, newSize)\n\tif q.tail > q.head {\n\t\tcopy(newBuf, q.buf[q.head:q.tail])\n\t} else {\n\t\tn := copy(newBuf, q.buf[q.head:])\n\t\tcopy(newBuf[n:], q.buf[:q.tail])\n\t}\n\n\tq.head = 0\n\tq.tail = q.count\n\tq.buf = newBuf\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/httputils/addr.go",
    "content": "package httputils\n\nimport (\n\t\"context\"\n\t\"net\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/http/httptrace\"\n)\n\ntype NetAddr struct {\n\tremoteAddr net.Addr\n\tlocalAddr  net.Addr\n}\n\nfunc (addr NetAddr) RemoteAddr() net.Addr {\n\treturn addr.remoteAddr\n}\n\nfunc (addr NetAddr) LocalAddr() net.Addr {\n\treturn addr.localAddr\n}\n\nfunc SetAddrFromRequest(addr *NetAddr, request *http.Request) {\n\tif request.RemoteAddr != \"\" {\n\t\tmetadata := C.Metadata{}\n\t\tif err := metadata.SetRemoteAddress(request.RemoteAddr); err == nil {\n\t\t\taddr.remoteAddr = net.TCPAddrFromAddrPort(metadata.AddrPort())\n\t\t}\n\t}\n\tif netAddr, ok := request.Context().Value(http.LocalAddrContextKey).(net.Addr); ok {\n\t\taddr.localAddr = netAddr\n\t}\n}\n\nfunc NewAddrContext(addr *NetAddr, ctx context.Context) context.Context {\n\treturn httptrace.WithClientTrace(ctx, &httptrace.ClientTrace{\n\t\tGotConn: func(connInfo httptrace.GotConnInfo) {\n\t\t\taddr.localAddr = connInfo.Conn.LocalAddr()\n\t\t\taddr.remoteAddr = connInfo.Conn.RemoteAddr()\n\t\t},\n\t})\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/httputils/force_close.go",
    "content": "package httputils\n\nimport (\n\t\"io\"\n\n\t\"github.com/metacubex/http\"\n)\n\ntype closeIdleTransport interface {\n\tCloseIdleConnections()\n}\n\ntype closeHttp2Connections interface {\n\tCloseHttp2Connections()\n}\n\nfunc CloseTransport(roundTripper http.RoundTripper) {\n\tif tr, ok := roundTripper.(closeIdleTransport); ok {\n\t\ttr.CloseIdleConnections() // for *http.Transport\n\t}\n\tif tr, ok := roundTripper.(closeHttp2Connections); ok {\n\t\ttr.CloseHttp2Connections() // for *http.Transport in our own fork\n\t}\n\tif tr, ok := roundTripper.(io.Closer); ok {\n\t\t_ = tr.Close() // for *http3.Transport\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/httputils/h2_transport_close.go",
    "content": "package httputils\n\nimport (\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n\t\"unsafe\"\n\n\t\"github.com/metacubex/http\"\n)\n\ntype clientConnPool struct {\n\tt *http.Http2Transport\n\n\tmu           sync.Mutex\n\tconns        map[string][]*http.Http2ClientConn // key is host:port\n\tdialing      map[string]unsafe.Pointer          // currently in-flight dials\n\tkeys         map[*http.Http2ClientConn][]string\n\taddConnCalls map[string]unsafe.Pointer // in-flight addConnIfNeeded calls\n}\n\ntype clientConn struct {\n\tt     *http.Transport\n\ttconn net.Conn // usually *tls.Conn, except specialized impls\n}\n\ntype efaceWords struct {\n\ttyp  unsafe.Pointer\n\tdata unsafe.Pointer\n}\n\ntype tlsConn interface {\n\tnet.Conn\n\tNetConn() net.Conn\n}\n\nfunc closeClientConn(cc *http.Http2ClientConn) { // like forceCloseConn() in http.Http2ClientConn but also apply for tls-like conn\n\tif conn, ok := (*clientConn)(unsafe.Pointer(cc)).tconn.(tlsConn); ok {\n\t\tt := time.AfterFunc(time.Second, func() {\n\t\t\t_ = conn.NetConn().Close()\n\t\t})\n\t\tdefer t.Stop()\n\t}\n\t_ = cc.Close()\n}\n\nfunc closeHttp2Transport(tr *http.Http2Transport) {\n\tconnPool := transportConnPool(tr)\n\tp := (*clientConnPool)((*efaceWords)(unsafe.Pointer(&connPool)).data)\n\tp.mu.Lock()\n\tdefer p.mu.Unlock()\n\tfor _, vv := range p.conns {\n\t\tfor _, cc := range vv {\n\t\t\tcloseClientConn(cc)\n\t\t}\n\t}\n\t// cleanup\n\tp.conns = make(map[string][]*http.Http2ClientConn)\n\tp.keys = make(map[*http.Http2ClientConn][]string)\n}\n\n//go:linkname transportConnPool github.com/metacubex/http.(*http2Transport).connPool\nfunc transportConnPool(t *http.Http2Transport) http.Http2ClientConnPool\n"
  },
  {
    "path": "core/Clash.Meta/common/lru/lrucache.go",
    "content": "package lru\n\n// Modified by https://github.com/die-net/lrucache\n\nimport (\n\t\"sync\"\n\t\"time\"\n\n\tlist \"github.com/bahlo/generic-list-go\"\n\t\"github.com/samber/lo\"\n)\n\n// Option is part of Functional Options Pattern\ntype Option[K comparable, V any] func(*LruCache[K, V])\n\n// EvictCallback is used to get a callback when a cache entry is evicted\ntype EvictCallback[K comparable, V any] func(key K, value V)\n\n// WithEvict set the evict callback\nfunc WithEvict[K comparable, V any](cb EvictCallback[K, V]) Option[K, V] {\n\treturn func(l *LruCache[K, V]) {\n\t\tl.onEvict = cb\n\t}\n}\n\n// WithUpdateAgeOnGet update expires when Get element\nfunc WithUpdateAgeOnGet[K comparable, V any]() Option[K, V] {\n\treturn func(l *LruCache[K, V]) {\n\t\tl.updateAgeOnGet = true\n\t}\n}\n\n// WithAge defined element max age (second)\nfunc WithAge[K comparable, V any](maxAge int64) Option[K, V] {\n\treturn func(l *LruCache[K, V]) {\n\t\tl.maxAge = maxAge\n\t}\n}\n\n// WithSize defined max length of LruCache\nfunc WithSize[K comparable, V any](maxSize int) Option[K, V] {\n\treturn func(l *LruCache[K, V]) {\n\t\tl.maxSize = maxSize\n\t}\n}\n\n// WithStale decide whether Stale return is enabled.\n// If this feature is enabled, element will not get Evicted according to `WithAge`.\nfunc WithStale[K comparable, V any](stale bool) Option[K, V] {\n\treturn func(l *LruCache[K, V]) {\n\t\tl.staleReturn = stale\n\t}\n}\n\n// LruCache is a thread-safe, in-memory lru-cache that evicts the\n// least recently used entries from memory when (if set) the entries are\n// older than maxAge (in seconds).  Use the New constructor to create one.\ntype LruCache[K comparable, V any] struct {\n\tmaxAge         int64\n\tmaxSize        int\n\tmu             sync.Mutex\n\tcache          map[K]*list.Element[*entry[K, V]]\n\tlru            *list.List[*entry[K, V]] // Front is least-recent\n\tupdateAgeOnGet bool\n\tstaleReturn    bool\n\tonEvict        EvictCallback[K, V]\n}\n\n// New creates an LruCache\nfunc New[K comparable, V any](options ...Option[K, V]) *LruCache[K, V] {\n\tlc := &LruCache[K, V]{}\n\tlc.Clear()\n\n\tfor _, option := range options {\n\t\toption(lc)\n\t}\n\n\treturn lc\n}\n\nfunc (c *LruCache[K, V]) Clear() {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tc.lru = list.New[*entry[K, V]]()\n\tc.cache = make(map[K]*list.Element[*entry[K, V]])\n}\n\n// Get returns any representation of a cached response and a bool\n// set to true if the key was found.\nfunc (c *LruCache[K, V]) Get(key K) (V, bool) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tel := c.get(key)\n\tif el == nil {\n\t\treturn lo.Empty[V](), false\n\t}\n\tvalue := el.value\n\n\treturn value, true\n}\n\nfunc (c *LruCache[K, V]) GetOrStore(key K, constructor func() V) (V, bool) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tel := c.get(key)\n\tif el == nil {\n\t\tvalue := constructor()\n\t\tc.set(key, value)\n\t\treturn value, false\n\t}\n\tvalue := el.value\n\n\treturn value, true\n}\n\n// GetWithExpire returns any representation of a cached response,\n// a time.Time Give expected expires,\n// and a bool set to true if the key was found.\n// This method will NOT check the maxAge of element and will NOT update the expires.\nfunc (c *LruCache[K, V]) GetWithExpire(key K) (V, time.Time, bool) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tel := c.get(key)\n\tif el == nil {\n\t\treturn lo.Empty[V](), time.Time{}, false\n\t}\n\n\treturn el.value, time.Unix(el.expires, 0), true\n}\n\n// Exist returns if key exist in cache but not put item to the head of linked list\nfunc (c *LruCache[K, V]) Exist(key K) bool {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\t_, ok := c.cache[key]\n\treturn ok\n}\n\n// Set stores any representation of a response for a given key.\nfunc (c *LruCache[K, V]) Set(key K, value V) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tc.set(key, value)\n}\n\nfunc (c *LruCache[K, V]) set(key K, value V) {\n\texpires := int64(0)\n\tif c.maxAge > 0 {\n\t\texpires = time.Now().Unix() + c.maxAge\n\t}\n\tc.setWithExpire(key, value, time.Unix(expires, 0))\n}\n\n// SetWithExpire stores any representation of a response for a given key and given expires.\n// The expires time will round to second.\nfunc (c *LruCache[K, V]) SetWithExpire(key K, value V, expires time.Time) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tc.setWithExpire(key, value, expires)\n}\n\nfunc (c *LruCache[K, V]) setWithExpire(key K, value V, expires time.Time) {\n\tif le, ok := c.cache[key]; ok {\n\t\tc.lru.MoveToBack(le)\n\t\te := le.Value\n\t\te.value = value\n\t\te.expires = expires.Unix()\n\t} else {\n\t\te := &entry[K, V]{key: key, value: value, expires: expires.Unix()}\n\t\tc.cache[key] = c.lru.PushBack(e)\n\n\t\tif c.maxSize > 0 {\n\t\t\tif elLen := c.lru.Len(); elLen > c.maxSize {\n\t\t\t\tc.deleteElement(c.lru.Front())\n\t\t\t}\n\t\t}\n\t}\n\n\tc.maybeDeleteOldest()\n}\n\n// CloneTo clone and overwrite elements to another LruCache\nfunc (c *LruCache[K, V]) CloneTo(n *LruCache[K, V]) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tn.mu.Lock()\n\tdefer n.mu.Unlock()\n\n\tn.lru = list.New[*entry[K, V]]()\n\tn.cache = make(map[K]*list.Element[*entry[K, V]])\n\n\tfor e := c.lru.Front(); e != nil; e = e.Next() {\n\t\telm := e.Value\n\t\tn.cache[elm.key] = n.lru.PushBack(elm)\n\t}\n}\n\nfunc (c *LruCache[K, V]) get(key K) *entry[K, V] {\n\tle, ok := c.cache[key]\n\tif !ok {\n\t\treturn nil\n\t}\n\n\tif !c.staleReturn && c.maxAge > 0 && le.Value.expires <= time.Now().Unix() {\n\t\tc.deleteElement(le)\n\t\tc.maybeDeleteOldest()\n\n\t\treturn nil\n\t}\n\n\tc.lru.MoveToBack(le)\n\tel := le.Value\n\tif c.maxAge > 0 && c.updateAgeOnGet {\n\t\tel.expires = time.Now().Unix() + c.maxAge\n\t}\n\treturn el\n}\n\n// Delete removes the value associated with a key.\nfunc (c *LruCache[K, V]) Delete(key K) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tc.delete(key)\n}\n\nfunc (c *LruCache[K, V]) delete(key K) {\n\tif le, ok := c.cache[key]; ok {\n\t\tc.deleteElement(le)\n\t}\n}\n\nfunc (c *LruCache[K, V]) maybeDeleteOldest() {\n\tif !c.staleReturn && c.maxAge > 0 {\n\t\tnow := time.Now().Unix()\n\t\tfor le := c.lru.Front(); le != nil && le.Value.expires <= now; le = c.lru.Front() {\n\t\t\tc.deleteElement(le)\n\t\t}\n\t}\n}\n\nfunc (c *LruCache[K, V]) deleteElement(le *list.Element[*entry[K, V]]) {\n\tc.lru.Remove(le)\n\te := le.Value\n\tdelete(c.cache, e.key)\n\tif c.onEvict != nil {\n\t\tc.onEvict(e.key, e.value)\n\t}\n}\n\n// Compute either sets the computed new value for the key or deletes\n// the value for the key. When the delete result of the valueFn function\n// is set to true, the value will be deleted, if it exists. When delete\n// is set to false, the value is updated to the newValue.\n// The ok result indicates whether value was computed and stored, thus, is\n// present in the map. The actual result contains the new value in cases where\n// the value was computed and stored.\nfunc (c *LruCache[K, V]) Compute(\n\tkey K,\n\tvalueFn func(oldValue V, loaded bool) (newValue V, delete bool),\n) (actual V, ok bool) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tif el := c.get(key); el != nil {\n\t\tactual, ok = el.value, true\n\t}\n\tif newValue, del := valueFn(actual, ok); del {\n\t\tif ok { // data not in cache, so needn't delete\n\t\t\tc.delete(key)\n\t\t}\n\t\treturn lo.Empty[V](), false\n\t} else {\n\t\tc.set(key, newValue)\n\t\treturn newValue, true\n\t}\n}\n\ntype entry[K comparable, V any] struct {\n\tkey     K\n\tvalue   V\n\texpires int64\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/lru/lrucache_test.go",
    "content": "package lru\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nvar entries = []struct {\n\tkey   string\n\tvalue string\n}{\n\t{\"1\", \"one\"},\n\t{\"2\", \"two\"},\n\t{\"3\", \"three\"},\n\t{\"4\", \"four\"},\n\t{\"5\", \"five\"},\n}\n\nfunc TestLRUCache(t *testing.T) {\n\tc := New[string, string]()\n\n\tfor _, e := range entries {\n\t\tc.Set(e.key, e.value)\n\t}\n\n\tc.Delete(\"missing\")\n\t_, ok := c.Get(\"missing\")\n\tassert.False(t, ok)\n\n\tfor _, e := range entries {\n\t\tvalue, ok := c.Get(e.key)\n\t\tif assert.True(t, ok) {\n\t\t\tassert.Equal(t, e.value, value)\n\t\t}\n\t}\n\n\tfor _, e := range entries {\n\t\tc.Delete(e.key)\n\n\t\t_, ok := c.Get(e.key)\n\t\tassert.False(t, ok)\n\t}\n}\n\nfunc TestLRUMaxAge(t *testing.T) {\n\tc := New[string, string](WithAge[string, string](86400))\n\n\tnow := time.Now().Unix()\n\texpected := now + 86400\n\n\t// Add one expired entry\n\tc.Set(\"foo\", \"bar\")\n\tc.lru.Back().Value.expires = now\n\n\t// Reset\n\tc.Set(\"foo\", \"bar\")\n\te := c.lru.Back().Value\n\tassert.True(t, e.expires >= now)\n\tc.lru.Back().Value.expires = now\n\n\t// Set a few and verify expiration times\n\tfor _, s := range entries {\n\t\tc.Set(s.key, s.value)\n\t\te := c.lru.Back().Value\n\t\tassert.True(t, e.expires >= expected && e.expires <= expected+10)\n\t}\n\n\t// Make sure we can get them all\n\tfor _, s := range entries {\n\t\t_, ok := c.Get(s.key)\n\t\tassert.True(t, ok)\n\t}\n\n\t// Expire all entries\n\tfor _, s := range entries {\n\t\tle, ok := c.cache[s.key]\n\t\tif assert.True(t, ok) {\n\t\t\tle.Value.expires = now\n\t\t}\n\t}\n\n\t// Get one expired entry, which should clear all expired entries\n\t_, ok := c.Get(\"3\")\n\tassert.False(t, ok)\n\tassert.Equal(t, c.lru.Len(), 0)\n}\n\nfunc TestLRUpdateOnGet(t *testing.T) {\n\tc := New[string, string](WithAge[string, string](86400), WithUpdateAgeOnGet[string, string]())\n\n\tnow := time.Now().Unix()\n\texpires := now + 86400/2\n\n\t// Add one expired entry\n\tc.Set(\"foo\", \"bar\")\n\tc.lru.Back().Value.expires = expires\n\n\t_, ok := c.Get(\"foo\")\n\tassert.True(t, ok)\n\tassert.True(t, c.lru.Back().Value.expires > expires)\n}\n\nfunc TestMaxSize(t *testing.T) {\n\tc := New[string, string](WithSize[string, string](2))\n\t// Add one expired entry\n\tc.Set(\"foo\", \"bar\")\n\t_, ok := c.Get(\"foo\")\n\tassert.True(t, ok)\n\n\tc.Set(\"bar\", \"foo\")\n\tc.Set(\"baz\", \"foo\")\n\n\t_, ok = c.Get(\"foo\")\n\tassert.False(t, ok)\n}\n\nfunc TestExist(t *testing.T) {\n\tc := New[int, int](WithSize[int, int](1))\n\tc.Set(1, 2)\n\tassert.True(t, c.Exist(1))\n\tc.Set(2, 3)\n\tassert.False(t, c.Exist(1))\n}\n\nfunc TestEvict(t *testing.T) {\n\ttemp := 0\n\tevict := func(key int, value int) {\n\t\ttemp = key + value\n\t}\n\n\tc := New[int, int](WithEvict[int, int](evict), WithSize[int, int](1))\n\tc.Set(1, 2)\n\tc.Set(2, 3)\n\n\tassert.Equal(t, temp, 3)\n}\n\nfunc TestSetWithExpire(t *testing.T) {\n\tc := New[int, *struct{}](WithAge[int, *struct{}](1))\n\tnow := time.Now().Unix()\n\n\ttenSecBefore := time.Unix(now-10, 0)\n\tc.SetWithExpire(1, &struct{}{}, tenSecBefore)\n\n\t// res is expected not to exist, and expires should be empty time.Time\n\tres, expires, exist := c.GetWithExpire(1)\n\n\tassert.True(t, nil == res)\n\tassert.Equal(t, time.Time{}, expires)\n\tassert.Equal(t, false, exist)\n}\n\nfunc TestStale(t *testing.T) {\n\tc := New[int, int](WithAge[int, int](1), WithStale[int, int](true))\n\tnow := time.Now().Unix()\n\n\ttenSecBefore := time.Unix(now-10, 0)\n\tc.SetWithExpire(1, 2, tenSecBefore)\n\n\tres, expires, exist := c.GetWithExpire(1)\n\tassert.Equal(t, 2, res)\n\tassert.Equal(t, tenSecBefore, expires)\n\tassert.Equal(t, true, exist)\n}\n\nfunc TestCloneTo(t *testing.T) {\n\to := New[string, int](WithSize[string, int](10))\n\to.Set(\"1\", 1)\n\to.Set(\"2\", 2)\n\n\tn := New[string, int](WithSize[string, int](2))\n\tn.Set(\"3\", 3)\n\tn.Set(\"4\", 4)\n\n\to.CloneTo(n)\n\n\tassert.False(t, n.Exist(\"3\"))\n\tassert.True(t, n.Exist(\"1\"))\n\n\tn.Set(\"5\", 5)\n\tassert.False(t, n.Exist(\"1\"))\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/maphash/common.go",
    "content": "package maphash\n\nimport \"hash/maphash\"\n\ntype Seed = maphash.Seed\n\nfunc MakeSeed() Seed {\n\treturn maphash.MakeSeed()\n}\n\ntype Hash = maphash.Hash\n\nfunc Bytes(seed Seed, b []byte) uint64 {\n\treturn maphash.Bytes(seed, b)\n}\n\nfunc String(seed Seed, s string) uint64 {\n\treturn maphash.String(seed, s)\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/maphash/comparable_go120.go",
    "content": "//go:build !go1.24\n\npackage maphash\n\nimport \"unsafe\"\n\nfunc Comparable[T comparable](s Seed, v T) uint64 {\n\treturn comparableHash(*(*seedTyp)(unsafe.Pointer(&s)), v)\n}\n\nfunc comparableHash[T comparable](seed seedTyp, v T) uint64 {\n\ts := seed.s\n\tvar m map[T]struct{}\n\tmTyp := iTypeOf(m)\n\tvar hasher func(unsafe.Pointer, uintptr) uintptr\n\thasher = (*iMapType)(unsafe.Pointer(mTyp)).Hasher\n\n\tp := escape(unsafe.Pointer(&v))\n\n\tif ptrSize == 8 {\n\t\treturn uint64(hasher(p, uintptr(s)))\n\t}\n\tlo := hasher(p, uintptr(s))\n\thi := hasher(p, uintptr(s>>32))\n\treturn uint64(hi)<<32 | uint64(lo)\n}\n\n// WriteComparable adds x to the data hashed by h.\nfunc WriteComparable[T comparable](h *Hash, x T) {\n\t// writeComparable (not in purego mode) directly operates on h.state\n\t// without using h.buf. Mix in the buffer length so it won't\n\t// commute with a buffered write, which either changes h.n or changes\n\t// h.state.\n\thash := (*hashTyp)(unsafe.Pointer(h))\n\tif hash.n != 0 {\n\t\thash.state.s = comparableHash(hash.state, hash.n)\n\t}\n\thash.state.s = comparableHash(hash.state, x)\n}\n\n// go/src/hash/maphash/maphash.go\ntype hashTyp struct {\n\t_     [0]func() // not comparable\n\tseed  seedTyp   // initial seed used for this hash\n\tstate seedTyp   // current hash of all flushed bytes\n\tbuf   [128]byte // unflushed byte buffer\n\tn     int       // number of unflushed bytes\n}\n\ntype seedTyp struct {\n\ts uint64\n}\n\ntype iTFlag uint8\ntype iKind uint8\ntype iNameOff int32\n\n// TypeOff is the offset to a type from moduledata.types.  See resolveTypeOff in runtime.\ntype iTypeOff int32\n\ntype iType struct {\n\tSize_       uintptr\n\tPtrBytes    uintptr // number of (prefix) bytes in the type that can contain pointers\n\tHash        uint32  // hash of type; avoids computation in hash tables\n\tTFlag       iTFlag  // extra type information flags\n\tAlign_      uint8   // alignment of variable with this type\n\tFieldAlign_ uint8   // alignment of struct field with this type\n\tKind_       iKind   // enumeration for C\n\t// function for comparing objects of this type\n\t// (ptr to object A, ptr to object B) -> ==?\n\tEqual func(unsafe.Pointer, unsafe.Pointer) bool\n\t// GCData stores the GC type data for the garbage collector.\n\t// Normally, GCData points to a bitmask that describes the\n\t// ptr/nonptr fields of the type. The bitmask will have at\n\t// least PtrBytes/ptrSize bits.\n\t// If the TFlagGCMaskOnDemand bit is set, GCData is instead a\n\t// **byte and the pointer to the bitmask is one dereference away.\n\t// The runtime will build the bitmask if needed.\n\t// (See runtime/type.go:getGCMask.)\n\t// Note: multiple types may have the same value of GCData,\n\t// including when TFlagGCMaskOnDemand is set. The types will, of course,\n\t// have the same pointer layout (but not necessarily the same size).\n\tGCData    *byte\n\tStr       iNameOff // string form\n\tPtrToThis iTypeOff // type for pointer to this type, may be zero\n}\n\ntype iMapType struct {\n\tiType\n\tKey   *iType\n\tElem  *iType\n\tGroup *iType // internal type representing a slot group\n\t// function for hashing keys (ptr to key, seed) -> hash\n\tHasher func(unsafe.Pointer, uintptr) uintptr\n}\n\nfunc iTypeOf(a any) *iType {\n\teface := *(*iEmptyInterface)(unsafe.Pointer(&a))\n\t// Types are either static (for compiler-created types) or\n\t// heap-allocated but always reachable (for reflection-created\n\t// types, held in the central map). So there is no need to\n\t// escape types. noescape here help avoid unnecessary escape\n\t// of v.\n\treturn (*iType)(noescape(unsafe.Pointer(eface.Type)))\n}\n\ntype iEmptyInterface struct {\n\tType *iType\n\tData unsafe.Pointer\n}\n\n// noescape hides a pointer from escape analysis.  noescape is\n// the identity function but escape analysis doesn't think the\n// output depends on the input.  noescape is inlined and currently\n// compiles down to zero instructions.\n// USE CAREFULLY!\n//\n// nolint:all\n//\n//go:nosplit\n//goland:noinspection ALL\nfunc noescape(p unsafe.Pointer) unsafe.Pointer {\n\tx := uintptr(p)\n\treturn unsafe.Pointer(x ^ 0)\n}\n\nvar alwaysFalse bool\nvar escapeSink any\n\n// escape forces any pointers in x to escape to the heap.\nfunc escape[T any](x T) T {\n\tif alwaysFalse {\n\t\tescapeSink = x\n\t}\n\treturn x\n}\n\n// ptrSize is the size of a pointer in bytes - unsafe.Sizeof(uintptr(0)) but as an ideal constant.\n// It is also the size of the machine's native word size (that is, 4 on 32-bit systems, 8 on 64-bit).\nconst ptrSize = 4 << (^uintptr(0) >> 63)\n\nconst testComparableAllocations = false\n"
  },
  {
    "path": "core/Clash.Meta/common/maphash/comparable_go124.go",
    "content": "//go:build go1.24\n\npackage maphash\n\nimport \"hash/maphash\"\n\nfunc Comparable[T comparable](seed Seed, v T) uint64 {\n\treturn maphash.Comparable(seed, v)\n}\n\nfunc WriteComparable[T comparable](h *Hash, x T) {\n\tmaphash.WriteComparable(h, x)\n}\n\nconst testComparableAllocations = true\n"
  },
  {
    "path": "core/Clash.Meta/common/maphash/maphash_test.go",
    "content": "// Copyright 2019 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage maphash\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"hash\"\n\t\"math\"\n\t\"reflect\"\n\t\"strings\"\n\t\"testing\"\n\t\"unsafe\"\n\n\trand \"github.com/metacubex/randv2\"\n)\n\nfunc TestUnseededHash(t *testing.T) {\n\tm := map[uint64]struct{}{}\n\tfor i := 0; i < 1000; i++ {\n\t\th := new(Hash)\n\t\tm[h.Sum64()] = struct{}{}\n\t}\n\tif len(m) < 900 {\n\t\tt.Errorf(\"empty hash not sufficiently random: got %d, want 1000\", len(m))\n\t}\n}\n\nfunc TestSeededHash(t *testing.T) {\n\ts := MakeSeed()\n\tm := map[uint64]struct{}{}\n\tfor i := 0; i < 1000; i++ {\n\t\th := new(Hash)\n\t\th.SetSeed(s)\n\t\tm[h.Sum64()] = struct{}{}\n\t}\n\tif len(m) != 1 {\n\t\tt.Errorf(\"seeded hash is random: got %d, want 1\", len(m))\n\t}\n}\n\nfunc TestHashGrouping(t *testing.T) {\n\tb := bytes.Repeat([]byte(\"foo\"), 100)\n\thh := make([]*Hash, 7)\n\tfor i := range hh {\n\t\thh[i] = new(Hash)\n\t}\n\tfor _, h := range hh[1:] {\n\t\th.SetSeed(hh[0].Seed())\n\t}\n\thh[0].Write(b)\n\thh[1].WriteString(string(b))\n\n\twriteByte := func(h *Hash, b byte) {\n\t\terr := h.WriteByte(b)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"WriteByte: %v\", err)\n\t\t}\n\t}\n\twriteSingleByte := func(h *Hash, b byte) {\n\t\t_, err := h.Write([]byte{b})\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"Write single byte: %v\", err)\n\t\t}\n\t}\n\twriteStringSingleByte := func(h *Hash, b byte) {\n\t\t_, err := h.WriteString(string([]byte{b}))\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"WriteString single byte: %v\", err)\n\t\t}\n\t}\n\n\tfor i, x := range b {\n\t\twriteByte(hh[2], x)\n\t\twriteSingleByte(hh[3], x)\n\t\tif i == 0 {\n\t\t\twriteByte(hh[4], x)\n\t\t} else {\n\t\t\twriteSingleByte(hh[4], x)\n\t\t}\n\t\twriteStringSingleByte(hh[5], x)\n\t\tif i == 0 {\n\t\t\twriteByte(hh[6], x)\n\t\t} else {\n\t\t\twriteStringSingleByte(hh[6], x)\n\t\t}\n\t}\n\n\tsum := hh[0].Sum64()\n\tfor i, h := range hh {\n\t\tif sum != h.Sum64() {\n\t\t\tt.Errorf(\"hash %d not identical to a single Write\", i)\n\t\t}\n\t}\n\n\tif sum1 := Bytes(hh[0].Seed(), b); sum1 != hh[0].Sum64() {\n\t\tt.Errorf(\"hash using Bytes not identical to a single Write\")\n\t}\n\n\tif sum1 := String(hh[0].Seed(), string(b)); sum1 != hh[0].Sum64() {\n\t\tt.Errorf(\"hash using String not identical to a single Write\")\n\t}\n}\n\nfunc TestHashBytesVsString(t *testing.T) {\n\ts := \"foo\"\n\tb := []byte(s)\n\th1 := new(Hash)\n\th2 := new(Hash)\n\th2.SetSeed(h1.Seed())\n\tn1, err1 := h1.WriteString(s)\n\tif n1 != len(s) || err1 != nil {\n\t\tt.Fatalf(\"WriteString(s) = %d, %v, want %d, nil\", n1, err1, len(s))\n\t}\n\tn2, err2 := h2.Write(b)\n\tif n2 != len(b) || err2 != nil {\n\t\tt.Fatalf(\"Write(b) = %d, %v, want %d, nil\", n2, err2, len(b))\n\t}\n\tif h1.Sum64() != h2.Sum64() {\n\t\tt.Errorf(\"hash of string and bytes not identical\")\n\t}\n}\n\nfunc TestHashHighBytes(t *testing.T) {\n\t// See issue 34925.\n\tconst N = 10\n\tm := map[uint64]struct{}{}\n\tfor i := 0; i < N; i++ {\n\t\th := new(Hash)\n\t\th.WriteString(\"foo\")\n\t\tm[h.Sum64()>>32] = struct{}{}\n\t}\n\tif len(m) < N/2 {\n\t\tt.Errorf(\"from %d seeds, wanted at least %d different hashes; got %d\", N, N/2, len(m))\n\t}\n}\n\nfunc TestRepeat(t *testing.T) {\n\th1 := new(Hash)\n\th1.WriteString(\"testing\")\n\tsum1 := h1.Sum64()\n\n\th1.Reset()\n\th1.WriteString(\"testing\")\n\tsum2 := h1.Sum64()\n\n\tif sum1 != sum2 {\n\t\tt.Errorf(\"different sum after resetting: %#x != %#x\", sum1, sum2)\n\t}\n\n\th2 := new(Hash)\n\th2.SetSeed(h1.Seed())\n\th2.WriteString(\"testing\")\n\tsum3 := h2.Sum64()\n\n\tif sum1 != sum3 {\n\t\tt.Errorf(\"different sum on the same seed: %#x != %#x\", sum1, sum3)\n\t}\n}\n\nfunc TestSeedFromSum64(t *testing.T) {\n\th1 := new(Hash)\n\th1.WriteString(\"foo\")\n\tx := h1.Sum64() // seed generated here\n\th2 := new(Hash)\n\th2.SetSeed(h1.Seed())\n\th2.WriteString(\"foo\")\n\ty := h2.Sum64()\n\tif x != y {\n\t\tt.Errorf(\"hashes don't match: want %x, got %x\", x, y)\n\t}\n}\n\nfunc TestSeedFromSeed(t *testing.T) {\n\th1 := new(Hash)\n\th1.WriteString(\"foo\")\n\t_ = h1.Seed() // seed generated here\n\tx := h1.Sum64()\n\th2 := new(Hash)\n\th2.SetSeed(h1.Seed())\n\th2.WriteString(\"foo\")\n\ty := h2.Sum64()\n\tif x != y {\n\t\tt.Errorf(\"hashes don't match: want %x, got %x\", x, y)\n\t}\n}\n\nfunc TestSeedFromFlush(t *testing.T) {\n\tb := make([]byte, 65)\n\th1 := new(Hash)\n\th1.Write(b) // seed generated here\n\tx := h1.Sum64()\n\th2 := new(Hash)\n\th2.SetSeed(h1.Seed())\n\th2.Write(b)\n\ty := h2.Sum64()\n\tif x != y {\n\t\tt.Errorf(\"hashes don't match: want %x, got %x\", x, y)\n\t}\n}\n\nfunc TestSeedFromReset(t *testing.T) {\n\th1 := new(Hash)\n\th1.WriteString(\"foo\")\n\th1.Reset() // seed generated here\n\th1.WriteString(\"foo\")\n\tx := h1.Sum64()\n\th2 := new(Hash)\n\th2.SetSeed(h1.Seed())\n\th2.WriteString(\"foo\")\n\ty := h2.Sum64()\n\tif x != y {\n\t\tt.Errorf(\"hashes don't match: want %x, got %x\", x, y)\n\t}\n}\n\nfunc negativeZero[T float32 | float64]() T {\n\tvar f T\n\tf = -f\n\treturn f\n}\n\nfunc TestComparable(t *testing.T) {\n\ttestComparable(t, int64(2))\n\ttestComparable(t, uint64(8))\n\ttestComparable(t, uintptr(12))\n\ttestComparable(t, any(\"s\"))\n\ttestComparable(t, \"s\")\n\ttestComparable(t, true)\n\ttestComparable(t, new(float64))\n\ttestComparable(t, float64(9))\n\ttestComparable(t, complex128(9i+1))\n\ttestComparable(t, struct{}{})\n\ttestComparable(t, struct {\n\t\ti int\n\t\tu uint\n\t\tb bool\n\t\tf float64\n\t\tp *int\n\t\ta any\n\t}{i: 9, u: 1, b: true, f: 9.9, p: new(int), a: 1})\n\ttype S struct {\n\t\ts string\n\t}\n\ts1 := S{s: heapStr(t)}\n\ts2 := S{s: heapStr(t)}\n\tif unsafe.StringData(s1.s) == unsafe.StringData(s2.s) {\n\t\tt.Fatalf(\"unexpected two heapStr ptr equal\")\n\t}\n\tif s1.s != s2.s {\n\t\tt.Fatalf(\"unexpected two heapStr value not equal\")\n\t}\n\ttestComparable(t, s1, s2)\n\ttestComparable(t, s1.s, s2.s)\n\ttestComparable(t, float32(0), negativeZero[float32]())\n\ttestComparable(t, float64(0), negativeZero[float64]())\n\ttestComparableNoEqual(t, math.NaN(), math.NaN())\n\ttestComparableNoEqual(t, [2]string{\"a\", \"\"}, [2]string{\"\", \"a\"})\n\ttestComparableNoEqual(t, struct{ a, b string }{\"foo\", \"\"}, struct{ a, b string }{\"\", \"foo\"})\n\ttestComparableNoEqual(t, struct{ a, b any }{int(0), struct{}{}}, struct{ a, b any }{struct{}{}, int(0)})\n}\n\nfunc testComparableNoEqual[T comparable](t *testing.T, v1, v2 T) {\n\tseed := MakeSeed()\n\tif Comparable(seed, v1) == Comparable(seed, v2) {\n\t\tt.Fatalf(\"Comparable(seed, %v) == Comparable(seed, %v)\", v1, v2)\n\t}\n}\n\nvar heapStrValue = []byte(\"aTestString\")\n\nfunc heapStr(t *testing.T) string {\n\treturn string(heapStrValue)\n}\n\nfunc testComparable[T comparable](t *testing.T, v T, v2 ...T) {\n\tt.Run(TypeFor[T]().String(), func(t *testing.T) {\n\t\tvar a, b T = v, v\n\t\tif len(v2) != 0 {\n\t\t\tb = v2[0]\n\t\t}\n\t\tvar pa *T = &a\n\t\tseed := MakeSeed()\n\t\tif Comparable(seed, a) != Comparable(seed, b) {\n\t\t\tt.Fatalf(\"Comparable(seed, %v) != Comparable(seed, %v)\", a, b)\n\t\t}\n\t\told := Comparable(seed, pa)\n\t\tstackGrow(8192)\n\t\tnew := Comparable(seed, pa)\n\t\tif old != new {\n\t\t\tt.Fatal(\"Comparable(seed, ptr) != Comparable(seed, ptr)\")\n\t\t}\n\t})\n}\n\nvar use byte\n\n//go:noinline\nfunc stackGrow(dep int) {\n\tif dep == 0 {\n\t\treturn\n\t}\n\tvar local [1024]byte\n\t// make sure local is allocated on the stack.\n\tlocal[rand.Uint64()%1024] = byte(rand.Uint64())\n\tuse = local[rand.Uint64()%1024]\n\tstackGrow(dep - 1)\n}\n\nfunc TestWriteComparable(t *testing.T) {\n\ttestWriteComparable(t, int64(2))\n\ttestWriteComparable(t, uint64(8))\n\ttestWriteComparable(t, uintptr(12))\n\ttestWriteComparable(t, any(\"s\"))\n\ttestWriteComparable(t, \"s\")\n\ttestComparable(t, true)\n\ttestWriteComparable(t, new(float64))\n\ttestWriteComparable(t, float64(9))\n\ttestWriteComparable(t, complex128(9i+1))\n\ttestWriteComparable(t, struct{}{})\n\ttestWriteComparable(t, struct {\n\t\ti int\n\t\tu uint\n\t\tb bool\n\t\tf float64\n\t\tp *int\n\t\ta any\n\t}{i: 9, u: 1, b: true, f: 9.9, p: new(int), a: 1})\n\ttype S struct {\n\t\ts string\n\t}\n\ts1 := S{s: heapStr(t)}\n\ts2 := S{s: heapStr(t)}\n\tif unsafe.StringData(s1.s) == unsafe.StringData(s2.s) {\n\t\tt.Fatalf(\"unexpected two heapStr ptr equal\")\n\t}\n\tif s1.s != s2.s {\n\t\tt.Fatalf(\"unexpected two heapStr value not equal\")\n\t}\n\ttestWriteComparable(t, s1, s2)\n\ttestWriteComparable(t, s1.s, s2.s)\n\ttestWriteComparable(t, float32(0), negativeZero[float32]())\n\ttestWriteComparable(t, float64(0), negativeZero[float64]())\n\ttestWriteComparableNoEqual(t, math.NaN(), math.NaN())\n\ttestWriteComparableNoEqual(t, [2]string{\"a\", \"\"}, [2]string{\"\", \"a\"})\n\ttestWriteComparableNoEqual(t, struct{ a, b string }{\"foo\", \"\"}, struct{ a, b string }{\"\", \"foo\"})\n\ttestWriteComparableNoEqual(t, struct{ a, b any }{int(0), struct{}{}}, struct{ a, b any }{struct{}{}, int(0)})\n}\n\nfunc testWriteComparableNoEqual[T comparable](t *testing.T, v1, v2 T) {\n\tseed := MakeSeed()\n\th1 := Hash{}\n\th2 := Hash{}\n\t*(*Seed)(unsafe.Pointer(&h1)), *(*Seed)(unsafe.Pointer(&h2)) = seed, seed\n\tWriteComparable(&h1, v1)\n\tWriteComparable(&h2, v2)\n\tif h1.Sum64() == h2.Sum64() {\n\t\tt.Fatalf(\"WriteComparable(seed, %v) == WriteComparable(seed, %v)\", v1, v2)\n\t}\n\n}\n\nfunc testWriteComparable[T comparable](t *testing.T, v T, v2 ...T) {\n\tt.Run(TypeFor[T]().String(), func(t *testing.T) {\n\t\tvar a, b T = v, v\n\t\tif len(v2) != 0 {\n\t\t\tb = v2[0]\n\t\t}\n\t\tvar pa *T = &a\n\t\th1 := Hash{}\n\t\th2 := Hash{}\n\t\t*(*Seed)(unsafe.Pointer(&h1)) = MakeSeed()\n\t\th2 = h1\n\t\tWriteComparable(&h1, a)\n\t\tWriteComparable(&h2, b)\n\t\tif h1.Sum64() != h2.Sum64() {\n\t\t\tt.Fatalf(\"WriteComparable(h, %v) != WriteComparable(h, %v)\", a, b)\n\t\t}\n\t\tWriteComparable(&h1, pa)\n\t\told := h1.Sum64()\n\t\tstackGrow(8192)\n\t\tWriteComparable(&h2, pa)\n\t\tnew := h2.Sum64()\n\t\tif old != new {\n\t\t\tt.Fatal(\"WriteComparable(seed, ptr) != WriteComparable(seed, ptr)\")\n\t\t}\n\t})\n}\n\nfunc TestComparableShouldPanic(t *testing.T) {\n\ts := []byte(\"s\")\n\ta := any(s)\n\tdefer func() {\n\t\te := recover()\n\t\terr, ok := e.(error)\n\t\tif !ok {\n\t\t\tt.Fatalf(\"Comaparable(any([]byte)) should panic\")\n\t\t}\n\t\twant := \"hash of unhashable type []uint8\"\n\t\tif s := err.Error(); !strings.Contains(s, want) {\n\t\t\tt.Fatalf(\"want %s, got %s\", want, s)\n\t\t}\n\t}()\n\tComparable(MakeSeed(), a)\n}\n\nfunc TestWriteComparableNoncommute(t *testing.T) {\n\tseed := MakeSeed()\n\tvar h1, h2 Hash\n\th1.SetSeed(seed)\n\th2.SetSeed(seed)\n\n\th1.WriteString(\"abc\")\n\tWriteComparable(&h1, 123)\n\tWriteComparable(&h2, 123)\n\th2.WriteString(\"abc\")\n\n\tif h1.Sum64() == h2.Sum64() {\n\t\tt.Errorf(\"WriteComparable and WriteString unexpectedly commute\")\n\t}\n}\n\nfunc TestComparableAllocations(t *testing.T) {\n\tif !testComparableAllocations {\n\t\tt.Skip(\"test broken in old golang version\")\n\t}\n\tseed := MakeSeed()\n\tx := heapStr(t)\n\tallocs := testing.AllocsPerRun(10, func() {\n\t\ts := \"s\" + x\n\t\tComparable(seed, s)\n\t})\n\tif allocs > 0 {\n\t\tt.Errorf(\"got %v allocs, want 0\", allocs)\n\t}\n\n\ttype S struct {\n\t\ta int\n\t\tb string\n\t}\n\tallocs = testing.AllocsPerRun(10, func() {\n\t\ts := S{123, \"s\" + x}\n\t\tComparable(seed, s)\n\t})\n\tif allocs > 0 {\n\t\tt.Errorf(\"got %v allocs, want 0\", allocs)\n\t}\n}\n\n// Make sure a Hash implements the hash.Hash and hash.Hash64 interfaces.\nvar _ hash.Hash = &Hash{}\nvar _ hash.Hash64 = &Hash{}\n\nfunc benchmarkSize(b *testing.B, size int) {\n\th := &Hash{}\n\tbuf := make([]byte, size)\n\ts := string(buf)\n\n\tb.Run(\"Write\", func(b *testing.B) {\n\t\tb.SetBytes(int64(size))\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\th.Reset()\n\t\t\th.Write(buf)\n\t\t\th.Sum64()\n\t\t}\n\t})\n\n\tb.Run(\"Bytes\", func(b *testing.B) {\n\t\tb.SetBytes(int64(size))\n\t\tseed := h.Seed()\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\tBytes(seed, buf)\n\t\t}\n\t})\n\n\tb.Run(\"String\", func(b *testing.B) {\n\t\tb.SetBytes(int64(size))\n\t\tseed := h.Seed()\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\tString(seed, s)\n\t\t}\n\t})\n}\n\nfunc BenchmarkHash(b *testing.B) {\n\tsizes := []int{4, 8, 16, 32, 64, 256, 320, 1024, 4096, 16384}\n\tfor _, size := range sizes {\n\t\tb.Run(fmt.Sprint(\"n=\", size), func(b *testing.B) {\n\t\t\tbenchmarkSize(b, size)\n\t\t})\n\t}\n}\n\nfunc benchmarkComparable[T comparable](b *testing.B, v T) {\n\tb.Run(TypeFor[T]().String(), func(b *testing.B) {\n\t\tseed := MakeSeed()\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\tComparable(seed, v)\n\t\t}\n\t})\n}\n\nfunc BenchmarkComparable(b *testing.B) {\n\ttype testStruct struct {\n\t\ti int\n\t\tu uint\n\t\tb bool\n\t\tf float64\n\t\tp *int\n\t\ta any\n\t}\n\tbenchmarkComparable(b, int64(2))\n\tbenchmarkComparable(b, uint64(8))\n\tbenchmarkComparable(b, uintptr(12))\n\tbenchmarkComparable(b, any(\"s\"))\n\tbenchmarkComparable(b, \"s\")\n\tbenchmarkComparable(b, true)\n\tbenchmarkComparable(b, new(float64))\n\tbenchmarkComparable(b, float64(9))\n\tbenchmarkComparable(b, complex128(9i+1))\n\tbenchmarkComparable(b, struct{}{})\n\tbenchmarkComparable(b, testStruct{i: 9, u: 1, b: true, f: 9.9, p: new(int), a: 1})\n}\n\n// TypeFor returns the [Type] that represents the type argument T.\nfunc TypeFor[T any]() reflect.Type {\n\tvar v T\n\tif t := reflect.TypeOf(v); t != nil {\n\t\treturn t // optimize for T being a non-interface kind\n\t}\n\treturn reflect.TypeOf((*T)(nil)).Elem() // only for an interface kind\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/murmur3/murmur.go",
    "content": "package murmur3\n\ntype bmixer interface {\n\tbmix(p []byte) (tail []byte)\n\tSize() (n int)\n\treset()\n}\n\ntype digest struct {\n\tclen int      // Digested input cumulative length.\n\ttail []byte   // 0 to Size()-1 bytes view of `buf'.\n\tbuf  [16]byte // Expected (but not required) to be Size() large.\n\tseed uint32   // Seed for initializing the hash.\n\tbmixer\n}\n\nfunc (d *digest) BlockSize() int { return 1 }\n\nfunc (d *digest) Write(p []byte) (n int, err error) {\n\tn = len(p)\n\td.clen += n\n\n\tif len(d.tail) > 0 {\n\t\t// Stick back pending bytes.\n\t\tnfree := d.Size() - len(d.tail) // nfree ∈ [1, d.Size()-1].\n\t\tif nfree < len(p) {\n\t\t\t// One full block can be formed.\n\t\t\tblock := append(d.tail, p[:nfree]...)\n\t\t\tp = p[nfree:]\n\t\t\t_ = d.bmix(block) // No tail.\n\t\t} else {\n\t\t\t// Tail's buf is large enough to prevent reallocs.\n\t\t\tp = append(d.tail, p...)\n\t\t}\n\t}\n\n\td.tail = d.bmix(p)\n\n\t// Keep own copy of the 0 to Size()-1 pending bytes.\n\tnn := copy(d.buf[:], d.tail)\n\td.tail = d.buf[:nn]\n\n\treturn n, nil\n}\n\nfunc (d *digest) Reset() {\n\td.clen = 0\n\td.tail = nil\n\td.bmixer.reset()\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/murmur3/murmur32.go",
    "content": "package murmur3\n\n// https://github.com/spaolacci/murmur3/blob/master/murmur32.go\n\nimport (\n\t\"hash\"\n\t\"math/bits\"\n\t\"unsafe\"\n)\n\n// Make sure interfaces are correctly implemented.\nvar (\n\t_ hash.Hash32 = new(digest32)\n\t_ bmixer      = new(digest32)\n)\n\nconst (\n\tc1_32 uint32 = 0xcc9e2d51\n\tc2_32 uint32 = 0x1b873593\n)\n\n// digest32 represents a partial evaluation of a 32 bites hash.\ntype digest32 struct {\n\tdigest\n\th1 uint32 // Unfinalized running hash.\n}\n\n// New32 returns new 32-bit hasher\nfunc New32() hash.Hash32 { return New32WithSeed(0) }\n\n// New32WithSeed returns new 32-bit hasher set with explicit seed value\nfunc New32WithSeed(seed uint32) hash.Hash32 {\n\td := new(digest32)\n\td.seed = seed\n\td.bmixer = d\n\td.Reset()\n\treturn d\n}\n\nfunc (d *digest32) Size() int { return 4 }\n\nfunc (d *digest32) reset() { d.h1 = d.seed }\n\nfunc (d *digest32) Sum(b []byte) []byte {\n\th := d.Sum32()\n\treturn append(b, byte(h>>24), byte(h>>16), byte(h>>8), byte(h))\n}\n\n// Digest as many blocks as possible.\nfunc (d *digest32) bmix(p []byte) (tail []byte) {\n\th1 := d.h1\n\n\tnblocks := len(p) / 4\n\tfor i := 0; i < nblocks; i++ {\n\t\tk1 := *(*uint32)(unsafe.Pointer(&p[i*4]))\n\n\t\tk1 *= c1_32\n\t\tk1 = bits.RotateLeft32(k1, 15)\n\t\tk1 *= c2_32\n\n\t\th1 ^= k1\n\t\th1 = bits.RotateLeft32(h1, 13)\n\t\th1 = h1*4 + h1 + 0xe6546b64\n\t}\n\td.h1 = h1\n\treturn p[nblocks*d.Size():]\n}\n\nfunc (d *digest32) Sum32() (h1 uint32) {\n\th1 = d.h1\n\n\tvar k1 uint32\n\tswitch len(d.tail) & 3 {\n\tcase 3:\n\t\tk1 ^= uint32(d.tail[2]) << 16\n\t\tfallthrough\n\tcase 2:\n\t\tk1 ^= uint32(d.tail[1]) << 8\n\t\tfallthrough\n\tcase 1:\n\t\tk1 ^= uint32(d.tail[0])\n\t\tk1 *= c1_32\n\t\tk1 = bits.RotateLeft32(k1, 15)\n\t\tk1 *= c2_32\n\t\th1 ^= k1\n\t}\n\n\th1 ^= uint32(d.clen)\n\n\th1 ^= h1 >> 16\n\th1 *= 0x85ebca6b\n\th1 ^= h1 >> 13\n\th1 *= 0xc2b2ae35\n\th1 ^= h1 >> 16\n\n\treturn h1\n}\n\nfunc Sum32(data []byte) uint32 { return Sum32WithSeed(data, 0) }\n\nfunc Sum32WithSeed(data []byte, seed uint32) uint32 {\n\th1 := seed\n\n\tnblocks := len(data) / 4\n\tfor i := 0; i < nblocks; i++ {\n\t\tk1 := *(*uint32)(unsafe.Pointer(&data[i*4]))\n\n\t\tk1 *= c1_32\n\t\tk1 = bits.RotateLeft32(k1, 15)\n\t\tk1 *= c2_32\n\n\t\th1 ^= k1\n\t\th1 = bits.RotateLeft32(h1, 13)\n\t\th1 = h1*4 + h1 + 0xe6546b64\n\t}\n\n\ttail := data[nblocks*4:]\n\n\tvar k1 uint32\n\tswitch len(tail) & 3 {\n\tcase 3:\n\t\tk1 ^= uint32(tail[2]) << 16\n\t\tfallthrough\n\tcase 2:\n\t\tk1 ^= uint32(tail[1]) << 8\n\t\tfallthrough\n\tcase 1:\n\t\tk1 ^= uint32(tail[0])\n\t\tk1 *= c1_32\n\t\tk1 = bits.RotateLeft32(k1, 15)\n\t\tk1 *= c2_32\n\t\th1 ^= k1\n\t}\n\n\th1 ^= uint32(len(data))\n\n\th1 ^= h1 >> 16\n\th1 *= 0x85ebca6b\n\th1 ^= h1 >> 13\n\th1 *= 0xc2b2ae35\n\th1 ^= h1 >> 16\n\n\treturn h1\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/addr.go",
    "content": "package net\n\nimport (\n\t\"net\"\n)\n\ntype CustomAddr interface {\n\tnet.Addr\n\tRawAddr() net.Addr\n}\n\ntype customAddr struct {\n\tnetworkStr string\n\taddrStr    string\n\trawAddr    net.Addr\n}\n\nfunc (a customAddr) Network() string {\n\treturn a.networkStr\n}\n\nfunc (a customAddr) String() string {\n\treturn a.addrStr\n}\n\nfunc (a customAddr) RawAddr() net.Addr {\n\treturn a.rawAddr\n}\n\nfunc NewCustomAddr(networkStr string, addrStr string, rawAddr net.Addr) CustomAddr {\n\treturn customAddr{\n\t\tnetworkStr: networkStr,\n\t\taddrStr:    addrStr,\n\t\trawAddr:    rawAddr,\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/bind.go",
    "content": "package net\n\nimport \"net\"\n\ntype bindPacketConn struct {\n\tEnhancePacketConn\n\trAddr net.Addr\n}\n\nfunc (c *bindPacketConn) Read(b []byte) (n int, err error) {\n\tn, _, err = c.EnhancePacketConn.ReadFrom(b)\n\treturn n, err\n}\n\nfunc (c *bindPacketConn) WaitRead() (data []byte, put func(), err error) {\n\tdata, put, _, err = c.EnhancePacketConn.WaitReadFrom()\n\treturn\n}\n\nfunc (c *bindPacketConn) Write(b []byte) (n int, err error) {\n\treturn c.EnhancePacketConn.WriteTo(b, c.rAddr)\n}\n\nfunc (c *bindPacketConn) RemoteAddr() net.Addr {\n\treturn c.rAddr\n}\n\nfunc (c *bindPacketConn) LocalAddr() net.Addr {\n\tif c.EnhancePacketConn.LocalAddr() == nil {\n\t\treturn &net.UDPAddr{IP: net.IPv4zero, Port: 0}\n\t} else {\n\t\treturn c.EnhancePacketConn.LocalAddr()\n\t}\n}\n\nfunc (c *bindPacketConn) Upstream() any {\n\treturn c.EnhancePacketConn\n}\n\nfunc NewBindPacketConn(pc net.PacketConn, rAddr net.Addr) net.Conn {\n\treturn &bindPacketConn{\n\t\tEnhancePacketConn: NewEnhancePacketConn(pc),\n\t\trAddr:             rAddr,\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/bufconn.go",
    "content": "package net\n\nimport (\n\t\"bufio\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/common/buf\"\n)\n\nvar _ ExtendedConn = (*BufferedConn)(nil)\n\ntype BufferedConn struct {\n\tr *bufio.Reader\n\tExtendedConn\n\tpeeked bool\n}\n\nfunc NewBufferedConn(c net.Conn) *BufferedConn {\n\tif bc, ok := c.(*BufferedConn); ok {\n\t\treturn bc\n\t}\n\treturn &BufferedConn{bufio.NewReader(c), NewExtendedConn(c), false}\n}\n\nfunc WarpConnWithBioReader(c net.Conn, br *bufio.Reader) net.Conn {\n\tif br != nil && br.Buffered() > 0 {\n\t\tif bc, ok := c.(*BufferedConn); ok && bc.r == br {\n\t\t\treturn bc\n\t\t}\n\t\treturn &BufferedConn{br, NewExtendedConn(c), true}\n\t}\n\treturn c\n}\n\n// Reader returns the internal bufio.Reader.\nfunc (c *BufferedConn) Reader() *bufio.Reader {\n\treturn c.r\n}\n\nfunc (c *BufferedConn) ResetPeeked() {\n\tc.peeked = false\n}\n\nfunc (c *BufferedConn) Peeked() bool {\n\treturn c.peeked\n}\n\n// Peek returns the next n bytes without advancing the reader.\nfunc (c *BufferedConn) Peek(n int) ([]byte, error) {\n\tc.peeked = true\n\treturn c.r.Peek(n)\n}\n\nfunc (c *BufferedConn) Discard(n int) (discarded int, err error) {\n\treturn c.r.Discard(n)\n}\n\nfunc (c *BufferedConn) Read(p []byte) (int, error) {\n\treturn c.r.Read(p)\n}\n\nfunc (c *BufferedConn) ReadByte() (byte, error) {\n\treturn c.r.ReadByte()\n}\n\nfunc (c *BufferedConn) UnreadByte() error {\n\treturn c.r.UnreadByte()\n}\n\nfunc (c *BufferedConn) Buffered() int {\n\treturn c.r.Buffered()\n}\n\nfunc (c *BufferedConn) ReadBuffer(buffer *buf.Buffer) (err error) {\n\tif c.r != nil && c.r.Buffered() > 0 {\n\t\t_, err = buffer.ReadOnceFrom(c.r)\n\t\treturn\n\t}\n\treturn c.ExtendedConn.ReadBuffer(buffer)\n}\n\nfunc (c *BufferedConn) ReadCached() *buf.Buffer { // call in sing/common/bufio.Copy\n\tif c.r != nil && c.r.Buffered() > 0 {\n\t\tlength := c.r.Buffered()\n\t\tb, _ := c.r.Peek(length)\n\t\t_, _ = c.r.Discard(length)\n\t\treturn buf.As(b)\n\t}\n\tc.r = nil // drop bufio.Reader to let gc can clean up its internal buf\n\treturn nil\n}\n\nfunc (c *BufferedConn) Upstream() any {\n\treturn c.ExtendedConn\n}\n\nfunc (c *BufferedConn) ReaderReplaceable() bool {\n\tif c.r != nil && c.r.Buffered() > 0 {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (c *BufferedConn) WriterReplaceable() bool {\n\treturn true\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/bufconn_unsafe.go",
    "content": "package net\n\nimport (\n\t\"io\"\n\t\"unsafe\"\n)\n\n// bufioReader copy from stdlib bufio/bufio.go\n// This structure has remained unchanged from go1.5 to go1.21.\ntype bufioReader struct {\n\tbuf          []byte\n\trd           io.Reader // reader provided by the client\n\tr, w         int       // buf read and write positions\n\terr          error\n\tlastByte     int // last byte read for UnreadByte; -1 means invalid\n\tlastRuneSize int // size of last rune read for UnreadRune; -1 means invalid\n}\n\nfunc (c *BufferedConn) AppendData(buf []byte) (ok bool) {\n\tb := (*bufioReader)(unsafe.Pointer(c.r))\n\tpos := len(b.buf) - b.w - len(buf)\n\tif pos >= -b.r { // len(b.buf)-(b.w - b.r) >= len(buf)\n\t\tif pos < 0 { // len(b.buf)-b.w < len(buf)\n\t\t\t// Slide existing data to beginning.\n\t\t\tcopy(b.buf, b.buf[b.r:b.w])\n\t\t\tb.w -= b.r\n\t\t\tb.r = 0\n\t\t}\n\n\t\tb.w += copy(b.buf[b.w:], buf)\n\t\treturn true\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/cached.go",
    "content": "package net\n\nimport (\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/common/buf\"\n)\n\nvar _ ExtendedConn = (*CachedConn)(nil)\n\ntype CachedConn struct {\n\tExtendedConn\n\tdata []byte\n}\n\nfunc NewCachedConn(c net.Conn, data []byte) *CachedConn {\n\treturn &CachedConn{NewExtendedConn(c), data}\n}\n\nfunc (c *CachedConn) Read(b []byte) (n int, err error) {\n\tif len(c.data) > 0 {\n\t\tn = copy(b, c.data)\n\t\tc.data = c.data[n:]\n\t\treturn\n\t}\n\treturn c.ExtendedConn.Read(b)\n}\n\nfunc (c *CachedConn) ReadCached() *buf.Buffer { // call in sing/common/bufio.Copy\n\tif len(c.data) > 0 {\n\t\treturn buf.As(c.data)\n\t}\n\treturn nil\n}\n\nfunc (c *CachedConn) Upstream() any {\n\treturn c.ExtendedConn\n}\n\nfunc (c *CachedConn) ReaderReplaceable() bool {\n\tif len(c.data) > 0 {\n\t\treturn false\n\t}\n\treturn true\n}\n\nfunc (c *CachedConn) WriterReplaceable() bool {\n\treturn true\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/context.go",
    "content": "package net\n\nimport (\n\t\"context\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/common/contextutils\"\n)\n\n// SetupContextForConn is a helper function that starts connection I/O interrupter.\n// if ctx be canceled before done called, it will close the connection.\n// should use like this:\n//\n//\tfunc streamConn(ctx context.Context, conn net.Conn) (_ net.Conn, err error) {\n//\t\tif ctx.Done() != nil {\n//\t\t\tdone := N.SetupContextForConn(ctx, conn)\n//\t\t\tdefer done(&err)\n//\t\t}\n//\t\tconn, err := xxx\n//\t\treturn conn, err\n//\t}\nfunc SetupContextForConn(ctx context.Context, conn net.Conn) (done func(*error)) {\n\tstopc := make(chan struct{})\n\tstop := contextutils.AfterFunc(ctx, func() {\n\t\t// Close the connection, discarding the error\n\t\t_ = conn.Close()\n\t\tclose(stopc)\n\t})\n\treturn func(inputErr *error) {\n\t\tif !stop() {\n\t\t\t// The AfterFunc was started, wait for it to complete.\n\t\t\t<-stopc\n\t\t\tif ctxErr := ctx.Err(); ctxErr != nil && inputErr != nil {\n\t\t\t\t// Return context error to user.\n\t\t\t\t*inputErr = ctxErr\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/context_test.go",
    "content": "package net_test\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net\"\n\t\"testing\"\n\t\"time\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc testRead(ctx context.Context, conn net.Conn) (err error) {\n\tif ctx.Done() != nil {\n\t\tdone := N.SetupContextForConn(ctx, conn)\n\t\tdefer done(&err)\n\t}\n\t_, err = conn.Read(make([]byte, 1))\n\treturn err\n}\n\nfunc TestSetupContextForConnWithCancel(t *testing.T) {\n\tt.Parallel()\n\tc1, c2 := N.Pipe()\n\tdefer c1.Close()\n\tdefer c2.Close()\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\n\terrc := make(chan error)\n\tgo func() {\n\t\terrc <- testRead(ctx, c1)\n\t}()\n\n\tselect {\n\tcase <-errc:\n\t\tt.Fatal(\"conn closed before cancel\")\n\tcase <-time.After(100 * time.Millisecond):\n\t\tcancel()\n\t}\n\n\tselect {\n\tcase err := <-errc:\n\t\tassert.ErrorIs(t, err, context.Canceled)\n\tcase <-time.After(100 * time.Millisecond):\n\t\tt.Fatal(\"conn not be canceled\")\n\t}\n}\n\nfunc TestSetupContextForConnWithTimeout1(t *testing.T) {\n\tt.Parallel()\n\tc1, c2 := N.Pipe()\n\tdefer c1.Close()\n\tdefer c2.Close()\n\tctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)\n\tdefer cancel()\n\n\terrc := make(chan error)\n\tgo func() {\n\t\terrc <- testRead(ctx, c1)\n\t}()\n\n\tselect {\n\tcase err := <-errc:\n\t\tif !errors.Is(ctx.Err(), context.DeadlineExceeded) {\n\t\t\tt.Fatal(\"conn closed before timeout\")\n\t\t}\n\t\tassert.ErrorIs(t, err, context.DeadlineExceeded)\n\tcase <-time.After(200 * time.Millisecond):\n\t\tt.Fatal(\"conn not be canceled\")\n\t}\n}\n\nfunc TestSetupContextForConnWithTimeout2(t *testing.T) {\n\tt.Parallel()\n\tc1, c2 := N.Pipe()\n\tdefer c1.Close()\n\tdefer c2.Close()\n\tctx, cancel := context.WithTimeout(context.Background(), 200*time.Millisecond)\n\tdefer cancel()\n\n\terrc := make(chan error)\n\tgo func() {\n\t\terrc <- testRead(ctx, c1)\n\t}()\n\n\tselect {\n\tcase <-errc:\n\t\tt.Fatal(\"conn closed before cancel\")\n\tcase <-time.After(100 * time.Millisecond):\n\t\tc2.Write(make([]byte, 1))\n\t}\n\n\tselect {\n\tcase err := <-errc:\n\t\tassert.Nil(t, ctx.Err())\n\t\tassert.Nil(t, err)\n\tcase <-time.After(200 * time.Millisecond):\n\t\tt.Fatal(\"conn not be canceled\")\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/deadline/conn.go",
    "content": "package deadline\n\nimport (\n\t\"net\"\n\t\"os\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/atomic\"\n\n\t\"github.com/metacubex/sing/common/buf\"\n\t\"github.com/metacubex/sing/common/bufio\"\n\t\"github.com/metacubex/sing/common/network\"\n)\n\ntype connReadResult struct {\n\tbuffer []byte\n\terr    error\n}\n\ntype Conn struct {\n\tnetwork.ExtendedConn\n\tdeadline     atomic.TypedValue[time.Time]\n\tpipeDeadline PipeDeadline\n\tdisablePipe  atomic.Bool\n\tinRead       atomic.Bool\n\tresultCh     chan *connReadResult\n}\n\nfunc IsConn(conn any) bool {\n\t_, ok := conn.(*Conn)\n\treturn ok\n}\n\nfunc NewConn(conn net.Conn) *Conn {\n\tc := &Conn{\n\t\tExtendedConn: bufio.NewExtendedConn(conn),\n\t\tpipeDeadline: MakePipeDeadline(),\n\t\tresultCh:     make(chan *connReadResult, 1),\n\t}\n\tc.resultCh <- nil\n\treturn c\n}\n\nfunc (c *Conn) Read(p []byte) (n int, err error) {\n\tselect {\n\tcase result := <-c.resultCh:\n\t\tif result != nil {\n\t\t\tn = copy(p, result.buffer)\n\t\t\terr = result.err\n\t\t\tif n >= len(result.buffer) {\n\t\t\t\tc.resultCh <- nil // finish cache read\n\t\t\t} else {\n\t\t\t\tresult.buffer = result.buffer[n:]\n\t\t\t\tc.resultCh <- result // push back for next call\n\t\t\t}\n\t\t\treturn\n\t\t} else {\n\t\t\tc.resultCh <- nil\n\t\t\tbreak\n\t\t}\n\tcase <-c.pipeDeadline.Wait():\n\t\treturn 0, os.ErrDeadlineExceeded\n\t}\n\n\tif c.disablePipe.Load() {\n\t\treturn c.ExtendedConn.Read(p)\n\t} else if c.deadline.Load().IsZero() {\n\t\tc.inRead.Store(true)\n\t\tdefer c.inRead.Store(false)\n\t\treturn c.ExtendedConn.Read(p)\n\t}\n\n\t<-c.resultCh\n\tgo c.pipeRead(len(p))\n\n\treturn c.Read(p)\n}\n\nfunc (c *Conn) pipeRead(size int) {\n\tbuffer := make([]byte, size)\n\tn, err := c.ExtendedConn.Read(buffer)\n\tbuffer = buffer[:n]\n\tc.resultCh <- &connReadResult{\n\t\tbuffer: buffer,\n\t\terr:    err,\n\t}\n}\n\nfunc (c *Conn) ReadBuffer(buffer *buf.Buffer) (err error) {\n\tselect {\n\tcase result := <-c.resultCh:\n\t\tif result != nil {\n\t\t\tn, _ := buffer.Write(result.buffer)\n\t\t\terr = result.err\n\n\t\t\tif n >= len(result.buffer) {\n\t\t\t\tc.resultCh <- nil // finish cache read\n\t\t\t} else {\n\t\t\t\tresult.buffer = result.buffer[n:]\n\t\t\t\tc.resultCh <- result // push back for next call\n\t\t\t}\n\t\t\treturn\n\t\t} else {\n\t\t\tc.resultCh <- nil\n\t\t\tbreak\n\t\t}\n\tcase <-c.pipeDeadline.Wait():\n\t\treturn os.ErrDeadlineExceeded\n\t}\n\n\tif c.disablePipe.Load() {\n\t\treturn c.ExtendedConn.ReadBuffer(buffer)\n\t} else if c.deadline.Load().IsZero() {\n\t\tc.inRead.Store(true)\n\t\tdefer c.inRead.Store(false)\n\t\treturn c.ExtendedConn.ReadBuffer(buffer)\n\t}\n\n\t<-c.resultCh\n\tgo c.pipeRead(buffer.FreeLen())\n\n\treturn c.ReadBuffer(buffer)\n}\n\nfunc (c *Conn) SetReadDeadline(t time.Time) error {\n\tif c.disablePipe.Load() {\n\t\treturn c.ExtendedConn.SetReadDeadline(t)\n\t} else if c.inRead.Load() {\n\t\tc.disablePipe.Store(true)\n\t\treturn c.ExtendedConn.SetReadDeadline(t)\n\t}\n\tc.deadline.Store(t)\n\tc.pipeDeadline.Set(t)\n\treturn nil\n}\n\nfunc (c *Conn) ReaderReplaceable() bool {\n\tselect {\n\tcase result := <-c.resultCh:\n\t\tc.resultCh <- result\n\t\tif result != nil {\n\t\t\treturn false // cache reading\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\tdefault:\n\t\treturn false // pipe reading\n\t}\n\treturn c.disablePipe.Load() || c.deadline.Load().IsZero()\n}\n\nfunc (c *Conn) WriterReplaceable() bool {\n\treturn true\n}\n\nfunc (c *Conn) Upstream() any {\n\treturn c.ExtendedConn\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/deadline/packet.go",
    "content": "package deadline\n\nimport (\n\t\"net\"\n\t\"os\"\n\t\"runtime\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/atomic\"\n\t\"github.com/metacubex/mihomo/common/net/packet\"\n)\n\ntype readResult struct {\n\tdata []byte\n\taddr net.Addr\n\terr  error\n}\n\ntype NetPacketConn struct {\n\tnet.PacketConn\n\tdeadline     atomic.TypedValue[time.Time]\n\tpipeDeadline PipeDeadline\n\tdisablePipe  atomic.Bool\n\tinRead       atomic.Bool\n\tresultCh     chan any\n}\n\nfunc NewNetPacketConn(pc net.PacketConn) net.PacketConn {\n\tnpc := &NetPacketConn{\n\t\tPacketConn:   pc,\n\t\tpipeDeadline: MakePipeDeadline(),\n\t\tresultCh:     make(chan any, 1),\n\t}\n\tnpc.resultCh <- nil\n\tif enhancePC, isEnhance := pc.(packet.EnhancePacketConn); isEnhance {\n\t\tepc := &EnhancePacketConn{\n\t\t\tNetPacketConn: npc,\n\t\t\tenhancePacketConn: enhancePacketConn{\n\t\t\t\tnetPacketConn:     npc,\n\t\t\t\tenhancePacketConn: enhancePC,\n\t\t\t},\n\t\t}\n\t\tif singPC, isSingPC := pc.(packet.SingPacketConn); isSingPC {\n\t\t\treturn &EnhanceSingPacketConn{\n\t\t\t\tEnhancePacketConn: epc,\n\t\t\t\tsingPacketConn: singPacketConn{\n\t\t\t\t\tnetPacketConn:  npc,\n\t\t\t\t\tsingPacketConn: singPC,\n\t\t\t\t},\n\t\t\t}\n\t\t}\n\t\treturn epc\n\t}\n\tif singPC, isSingPC := pc.(packet.SingPacketConn); isSingPC {\n\t\treturn &SingPacketConn{\n\t\t\tNetPacketConn: npc,\n\t\t\tsingPacketConn: singPacketConn{\n\t\t\t\tnetPacketConn:  npc,\n\t\t\t\tsingPacketConn: singPC,\n\t\t\t},\n\t\t}\n\t}\n\treturn npc\n}\n\nfunc (c *NetPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {\nFOR:\n\tfor {\n\t\tselect {\n\t\tcase result := <-c.resultCh:\n\t\t\tif result != nil {\n\t\t\t\tif result, ok := result.(*readResult); ok {\n\t\t\t\t\tn = copy(p, result.data)\n\t\t\t\t\taddr = result.addr\n\t\t\t\t\terr = result.err\n\t\t\t\t\tc.resultCh <- nil // finish cache read\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tc.resultCh <- result // another type of read\n\t\t\t\truntime.Gosched()    // allowing other goroutines to run\n\t\t\t\tcontinue FOR\n\t\t\t} else {\n\t\t\t\tc.resultCh <- nil\n\t\t\t\tbreak FOR\n\t\t\t}\n\t\tcase <-c.pipeDeadline.Wait():\n\t\t\treturn 0, nil, os.ErrDeadlineExceeded\n\t\t}\n\t}\n\n\tif c.disablePipe.Load() {\n\t\treturn c.PacketConn.ReadFrom(p)\n\t} else if c.deadline.Load().IsZero() {\n\t\tc.inRead.Store(true)\n\t\tdefer c.inRead.Store(false)\n\t\tn, addr, err = c.PacketConn.ReadFrom(p)\n\t\treturn\n\t}\n\n\t<-c.resultCh\n\tgo c.pipeReadFrom(len(p))\n\n\treturn c.ReadFrom(p)\n}\n\nfunc (c *NetPacketConn) pipeReadFrom(size int) {\n\tbuffer := make([]byte, size)\n\tn, addr, err := c.PacketConn.ReadFrom(buffer)\n\tbuffer = buffer[:n]\n\tresult := &readResult{}\n\tresult.data = buffer\n\tresult.addr = addr\n\tresult.err = err\n\tc.resultCh <- result\n}\n\nfunc (c *NetPacketConn) SetReadDeadline(t time.Time) error {\n\tif c.disablePipe.Load() {\n\t\treturn c.PacketConn.SetReadDeadline(t)\n\t} else if c.inRead.Load() {\n\t\tc.disablePipe.Store(true)\n\t\treturn c.PacketConn.SetReadDeadline(t)\n\t}\n\tc.deadline.Store(t)\n\tc.pipeDeadline.Set(t)\n\treturn nil\n}\n\nfunc (c *NetPacketConn) ReaderReplaceable() bool {\n\tselect {\n\tcase result := <-c.resultCh:\n\t\tc.resultCh <- result\n\t\tif result != nil {\n\t\t\treturn false // cache reading\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\tdefault:\n\t\treturn false // pipe reading\n\t}\n\treturn c.disablePipe.Load() || c.deadline.Load().IsZero()\n}\n\nfunc (c *NetPacketConn) WriterReplaceable() bool {\n\treturn true\n}\n\nfunc (c *NetPacketConn) Upstream() any {\n\treturn c.PacketConn\n}\n\nfunc (c *NetPacketConn) NeedAdditionalReadDeadline() bool {\n\treturn false\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/deadline/packet_enhance.go",
    "content": "package deadline\n\nimport (\n\t\"net\"\n\t\"os\"\n\t\"runtime\"\n\n\t\"github.com/metacubex/mihomo/common/net/packet\"\n)\n\ntype EnhancePacketConn struct {\n\t*NetPacketConn\n\tenhancePacketConn\n}\n\nvar _ packet.EnhancePacketConn = (*EnhancePacketConn)(nil)\n\nfunc NewEnhancePacketConn(pc packet.EnhancePacketConn) packet.EnhancePacketConn {\n\treturn NewNetPacketConn(pc).(packet.EnhancePacketConn)\n}\n\ntype enhanceReadResult struct {\n\tdata []byte\n\tput  func()\n\taddr net.Addr\n\terr  error\n}\n\ntype enhancePacketConn struct {\n\tnetPacketConn     *NetPacketConn\n\tenhancePacketConn packet.EnhancePacketConn\n}\n\nfunc (c *enhancePacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {\nFOR:\n\tfor {\n\t\tselect {\n\t\tcase result := <-c.netPacketConn.resultCh:\n\t\t\tif result != nil {\n\t\t\t\tif result, ok := result.(*enhanceReadResult); ok {\n\t\t\t\t\tdata = result.data\n\t\t\t\t\tput = result.put\n\t\t\t\t\taddr = result.addr\n\t\t\t\t\terr = result.err\n\t\t\t\t\tc.netPacketConn.resultCh <- nil // finish cache read\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tc.netPacketConn.resultCh <- result // another type of read\n\t\t\t\truntime.Gosched()                  // allowing other goroutines to run\n\t\t\t\tcontinue FOR\n\t\t\t} else {\n\t\t\t\tc.netPacketConn.resultCh <- nil\n\t\t\t\tbreak FOR\n\t\t\t}\n\t\tcase <-c.netPacketConn.pipeDeadline.Wait():\n\t\t\treturn nil, nil, nil, os.ErrDeadlineExceeded\n\t\t}\n\t}\n\n\tif c.netPacketConn.disablePipe.Load() {\n\t\treturn c.enhancePacketConn.WaitReadFrom()\n\t} else if c.netPacketConn.deadline.Load().IsZero() {\n\t\tc.netPacketConn.inRead.Store(true)\n\t\tdefer c.netPacketConn.inRead.Store(false)\n\t\tdata, put, addr, err = c.enhancePacketConn.WaitReadFrom()\n\t\treturn\n\t}\n\n\t<-c.netPacketConn.resultCh\n\tgo c.pipeWaitReadFrom()\n\n\treturn c.WaitReadFrom()\n}\n\nfunc (c *enhancePacketConn) pipeWaitReadFrom() {\n\tdata, put, addr, err := c.enhancePacketConn.WaitReadFrom()\n\tresult := &enhanceReadResult{}\n\tresult.data = data\n\tresult.put = put\n\tresult.addr = addr\n\tresult.err = err\n\tc.netPacketConn.resultCh <- result\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/deadline/packet_sing.go",
    "content": "package deadline\n\nimport (\n\t\"os\"\n\t\"runtime\"\n\n\t\"github.com/metacubex/mihomo/common/net/packet\"\n\n\t\"github.com/metacubex/sing/common/buf\"\n\t\"github.com/metacubex/sing/common/bufio\"\n\tM \"github.com/metacubex/sing/common/metadata\"\n\tN \"github.com/metacubex/sing/common/network\"\n)\n\ntype SingPacketConn struct {\n\t*NetPacketConn\n\tsingPacketConn\n}\n\nvar _ packet.SingPacketConn = (*SingPacketConn)(nil)\n\nfunc NewSingPacketConn(pc packet.SingPacketConn) packet.SingPacketConn {\n\treturn NewNetPacketConn(pc).(packet.SingPacketConn)\n}\n\ntype EnhanceSingPacketConn struct {\n\t*EnhancePacketConn\n\tsingPacketConn\n}\n\nfunc NewEnhanceSingPacketConn(pc packet.EnhanceSingPacketConn) packet.EnhanceSingPacketConn {\n\treturn NewNetPacketConn(pc).(packet.EnhanceSingPacketConn)\n}\n\nvar _ packet.EnhanceSingPacketConn = (*EnhanceSingPacketConn)(nil)\n\ntype singReadResult struct {\n\tbuffer      *buf.Buffer\n\tdestination M.Socksaddr\n\terr         error\n}\n\ntype singPacketConn struct {\n\tnetPacketConn  *NetPacketConn\n\tsingPacketConn packet.SingPacketConn\n}\n\nfunc (c *singPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {\nFOR:\n\tfor {\n\t\tselect {\n\t\tcase result := <-c.netPacketConn.resultCh:\n\t\t\tif result != nil {\n\t\t\t\tif result, ok := result.(*singReadResult); ok {\n\t\t\t\t\tdestination = result.destination\n\t\t\t\t\terr = result.err\n\t\t\t\t\tn, _ := buffer.Write(result.buffer.Bytes())\n\t\t\t\t\tresult.buffer.Advance(n)\n\t\t\t\t\tif result.buffer.IsEmpty() {\n\t\t\t\t\t\tresult.buffer.Release()\n\t\t\t\t\t}\n\t\t\t\t\tc.netPacketConn.resultCh <- nil // finish cache read\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tc.netPacketConn.resultCh <- result // another type of read\n\t\t\t\truntime.Gosched()                  // allowing other goroutines to run\n\t\t\t\tcontinue FOR\n\t\t\t} else {\n\t\t\t\tc.netPacketConn.resultCh <- nil\n\t\t\t\tbreak FOR\n\t\t\t}\n\t\tcase <-c.netPacketConn.pipeDeadline.Wait():\n\t\t\treturn M.Socksaddr{}, os.ErrDeadlineExceeded\n\t\t}\n\t}\n\n\tif c.netPacketConn.disablePipe.Load() {\n\t\treturn c.singPacketConn.ReadPacket(buffer)\n\t} else if c.netPacketConn.deadline.Load().IsZero() {\n\t\tc.netPacketConn.inRead.Store(true)\n\t\tdefer c.netPacketConn.inRead.Store(false)\n\t\tdestination, err = c.singPacketConn.ReadPacket(buffer)\n\t\treturn\n\t}\n\n\t<-c.netPacketConn.resultCh\n\tgo c.pipeReadPacket(buffer.FreeLen())\n\n\treturn c.ReadPacket(buffer)\n}\n\nfunc (c *singPacketConn) pipeReadPacket(pLen int) {\n\tbuffer := buf.NewSize(pLen)\n\tdestination, err := c.singPacketConn.ReadPacket(buffer)\n\tresult := &singReadResult{}\n\tresult.destination = destination\n\tresult.err = err\n\tc.netPacketConn.resultCh <- result\n}\n\nfunc (c *singPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {\n\treturn c.singPacketConn.WritePacket(buffer, destination)\n}\n\nfunc (c *singPacketConn) CreateReadWaiter() (N.PacketReadWaiter, bool) {\n\tprw, isReadWaiter := bufio.CreatePacketReadWaiter(c.singPacketConn)\n\tif isReadWaiter {\n\t\treturn &singPacketReadWaiter{\n\t\t\tnetPacketConn:    c.netPacketConn,\n\t\t\tpacketReadWaiter: prw,\n\t\t}, true\n\t}\n\treturn nil, false\n}\n\nvar _ N.PacketReadWaiter = (*singPacketReadWaiter)(nil)\n\ntype singPacketReadWaiter struct {\n\tnetPacketConn    *NetPacketConn\n\tpacketReadWaiter N.PacketReadWaiter\n}\n\ntype singWaitReadResult singReadResult\n\nfunc (c *singPacketReadWaiter) InitializeReadWaiter(options N.ReadWaitOptions) (needCopy bool) {\n\treturn c.packetReadWaiter.InitializeReadWaiter(options)\n}\n\nfunc (c *singPacketReadWaiter) WaitReadPacket() (buffer *buf.Buffer, destination M.Socksaddr, err error) {\nFOR:\n\tfor {\n\t\tselect {\n\t\tcase result := <-c.netPacketConn.resultCh:\n\t\t\tif result != nil {\n\t\t\t\tif result, ok := result.(*singWaitReadResult); ok {\n\t\t\t\t\tbuffer = result.buffer\n\t\t\t\t\tdestination = result.destination\n\t\t\t\t\terr = result.err\n\t\t\t\t\tc.netPacketConn.resultCh <- nil // finish cache read\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tc.netPacketConn.resultCh <- result // another type of read\n\t\t\t\truntime.Gosched()                  // allowing other goroutines to run\n\t\t\t\tcontinue FOR\n\t\t\t} else {\n\t\t\t\tc.netPacketConn.resultCh <- nil\n\t\t\t\tbreak FOR\n\t\t\t}\n\t\tcase <-c.netPacketConn.pipeDeadline.Wait():\n\t\t\treturn nil, M.Socksaddr{}, os.ErrDeadlineExceeded\n\t\t}\n\t}\n\n\tif c.netPacketConn.disablePipe.Load() {\n\t\treturn c.packetReadWaiter.WaitReadPacket()\n\t} else if c.netPacketConn.deadline.Load().IsZero() {\n\t\tc.netPacketConn.inRead.Store(true)\n\t\tdefer c.netPacketConn.inRead.Store(false)\n\t\treturn c.packetReadWaiter.WaitReadPacket()\n\t}\n\n\t<-c.netPacketConn.resultCh\n\tgo c.pipeWaitReadPacket()\n\n\treturn c.WaitReadPacket()\n}\n\nfunc (c *singPacketReadWaiter) pipeWaitReadPacket() {\n\tbuffer, destination, err := c.packetReadWaiter.WaitReadPacket()\n\tresult := &singWaitReadResult{}\n\tresult.buffer = buffer\n\tresult.destination = destination\n\tresult.err = err\n\tc.netPacketConn.resultCh <- result\n}\n\nfunc (c *singPacketReadWaiter) Upstream() any {\n\treturn c.packetReadWaiter\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/deadline/pipe.go",
    "content": "// Copyright 2010 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage deadline\n\nimport (\n\t\"sync\"\n\t\"time\"\n)\n\n// PipeDeadline is an abstraction for handling timeouts.\ntype PipeDeadline struct {\n\tmu     sync.Mutex // Guards timer and cancel\n\ttimer  *time.Timer\n\tcancel chan struct{} // Must be non-nil\n}\n\nfunc MakePipeDeadline() PipeDeadline {\n\treturn PipeDeadline{cancel: make(chan struct{})}\n}\n\n// Set sets the point in time when the deadline will time out.\n// A timeout event is signaled by closing the channel returned by waiter.\n// Once a timeout has occurred, the deadline can be refreshed by specifying a\n// t value in the future.\n//\n// A zero value for t prevents timeout.\nfunc (d *PipeDeadline) Set(t time.Time) {\n\td.mu.Lock()\n\tdefer d.mu.Unlock()\n\n\tif d.timer != nil && !d.timer.Stop() {\n\t\t<-d.cancel // Wait for the timer callback to finish and close cancel\n\t}\n\td.timer = nil\n\n\t// Time is zero, then there is no deadline.\n\tclosed := isClosedChan(d.cancel)\n\tif t.IsZero() {\n\t\tif closed {\n\t\t\td.cancel = make(chan struct{})\n\t\t}\n\t\treturn\n\t}\n\n\t// Time in the future, setup a timer to cancel in the future.\n\tif dur := time.Until(t); dur > 0 {\n\t\tif closed {\n\t\t\td.cancel = make(chan struct{})\n\t\t}\n\t\td.timer = time.AfterFunc(dur, func() {\n\t\t\tclose(d.cancel)\n\t\t})\n\t\treturn\n\t}\n\n\t// Time in the past, so close immediately.\n\tif !closed {\n\t\tclose(d.cancel)\n\t}\n}\n\n// Wait returns a channel that is closed when the deadline is exceeded.\nfunc (d *PipeDeadline) Wait() chan struct{} {\n\td.mu.Lock()\n\tdefer d.mu.Unlock()\n\treturn d.cancel\n}\n\nfunc isClosedChan(c <-chan struct{}) bool {\n\tselect {\n\tcase <-c:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc makeFilledChan() chan struct{} {\n\tch := make(chan struct{}, 1)\n\tch <- struct{}{}\n\treturn ch\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/deadline/pipe_sing.go",
    "content": "package deadline\n\nimport (\n\t\"io\"\n\t\"net\"\n\t\"os\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/sing/common/buf\"\n\tN \"github.com/metacubex/sing/common/network\"\n)\n\ntype pipeAddr struct{}\n\nfunc (pipeAddr) Network() string { return \"pipe\" }\nfunc (pipeAddr) String() string  { return \"pipe\" }\n\ntype pipe struct {\n\twrMu sync.Mutex // Serialize Write operations\n\n\t// Used by local Read to interact with remote Write.\n\t// Successful receive on rdRx is always followed by send on rdTx.\n\trdRx <-chan []byte\n\trdTx chan<- int\n\n\t// Used by local Write to interact with remote Read.\n\t// Successful send on wrTx is always followed by receive on wrRx.\n\twrTx chan<- []byte\n\twrRx <-chan int\n\n\tonce       sync.Once // Protects closing localDone\n\tlocalDone  chan struct{}\n\tremoteDone <-chan struct{}\n\n\treadDeadline  PipeDeadline\n\twriteDeadline PipeDeadline\n\n\treadWaitOptions N.ReadWaitOptions\n}\n\n// Pipe creates a synchronous, in-memory, full duplex\n// network connection; both ends implement the Conn interface.\n// Reads on one end are matched with writes on the other,\n// copying data directly between the two; there is no internal\n// buffering.\nfunc Pipe() (net.Conn, net.Conn) {\n\tcb1 := make(chan []byte)\n\tcb2 := make(chan []byte)\n\tcn1 := make(chan int)\n\tcn2 := make(chan int)\n\tdone1 := make(chan struct{})\n\tdone2 := make(chan struct{})\n\n\tp1 := &pipe{\n\t\trdRx: cb1, rdTx: cn1,\n\t\twrTx: cb2, wrRx: cn2,\n\t\tlocalDone: done1, remoteDone: done2,\n\t\treadDeadline:  MakePipeDeadline(),\n\t\twriteDeadline: MakePipeDeadline(),\n\t}\n\tp2 := &pipe{\n\t\trdRx: cb2, rdTx: cn2,\n\t\twrTx: cb1, wrRx: cn1,\n\t\tlocalDone: done2, remoteDone: done1,\n\t\treadDeadline:  MakePipeDeadline(),\n\t\twriteDeadline: MakePipeDeadline(),\n\t}\n\treturn p1, p2\n}\n\nfunc (*pipe) LocalAddr() net.Addr  { return pipeAddr{} }\nfunc (*pipe) RemoteAddr() net.Addr { return pipeAddr{} }\n\nfunc (p *pipe) Read(b []byte) (int, error) {\n\tn, err := p.read(b)\n\tif err != nil && err != io.EOF && err != io.ErrClosedPipe {\n\t\terr = &net.OpError{Op: \"read\", Net: \"pipe\", Err: err}\n\t}\n\treturn n, err\n}\n\nfunc (p *pipe) read(b []byte) (n int, err error) {\n\tswitch {\n\tcase isClosedChan(p.localDone):\n\t\treturn 0, io.ErrClosedPipe\n\tcase isClosedChan(p.remoteDone):\n\t\treturn 0, io.EOF\n\tcase isClosedChan(p.readDeadline.Wait()):\n\t\treturn 0, os.ErrDeadlineExceeded\n\t}\n\n\tselect {\n\tcase bw := <-p.rdRx:\n\t\tnr := copy(b, bw)\n\t\tp.rdTx <- nr\n\t\treturn nr, nil\n\tcase <-p.localDone:\n\t\treturn 0, io.ErrClosedPipe\n\tcase <-p.remoteDone:\n\t\treturn 0, io.EOF\n\tcase <-p.readDeadline.Wait():\n\t\treturn 0, os.ErrDeadlineExceeded\n\t}\n}\n\nfunc (p *pipe) Write(b []byte) (int, error) {\n\tn, err := p.write(b)\n\tif err != nil && err != io.ErrClosedPipe {\n\t\terr = &net.OpError{Op: \"write\", Net: \"pipe\", Err: err}\n\t}\n\treturn n, err\n}\n\nfunc (p *pipe) write(b []byte) (n int, err error) {\n\tswitch {\n\tcase isClosedChan(p.localDone):\n\t\treturn 0, io.ErrClosedPipe\n\tcase isClosedChan(p.remoteDone):\n\t\treturn 0, io.ErrClosedPipe\n\tcase isClosedChan(p.writeDeadline.Wait()):\n\t\treturn 0, os.ErrDeadlineExceeded\n\t}\n\n\tp.wrMu.Lock() // Ensure entirety of b is written together\n\tdefer p.wrMu.Unlock()\n\tfor once := true; once || len(b) > 0; once = false {\n\t\tselect {\n\t\tcase p.wrTx <- b:\n\t\t\tnw := <-p.wrRx\n\t\t\tb = b[nw:]\n\t\t\tn += nw\n\t\tcase <-p.localDone:\n\t\t\treturn n, io.ErrClosedPipe\n\t\tcase <-p.remoteDone:\n\t\t\treturn n, io.ErrClosedPipe\n\t\tcase <-p.writeDeadline.Wait():\n\t\t\treturn n, os.ErrDeadlineExceeded\n\t\t}\n\t}\n\treturn n, nil\n}\n\nfunc (p *pipe) SetDeadline(t time.Time) error {\n\tif isClosedChan(p.localDone) || isClosedChan(p.remoteDone) {\n\t\treturn io.ErrClosedPipe\n\t}\n\tp.readDeadline.Set(t)\n\tp.writeDeadline.Set(t)\n\treturn nil\n}\n\nfunc (p *pipe) SetReadDeadline(t time.Time) error {\n\tif isClosedChan(p.localDone) || isClosedChan(p.remoteDone) {\n\t\treturn io.ErrClosedPipe\n\t}\n\tp.readDeadline.Set(t)\n\treturn nil\n}\n\nfunc (p *pipe) SetWriteDeadline(t time.Time) error {\n\tif isClosedChan(p.localDone) || isClosedChan(p.remoteDone) {\n\t\treturn io.ErrClosedPipe\n\t}\n\tp.writeDeadline.Set(t)\n\treturn nil\n}\n\nfunc (p *pipe) Close() error {\n\tp.once.Do(func() { close(p.localDone) })\n\treturn nil\n}\n\nvar _ N.ReadWaiter = (*pipe)(nil)\n\nfunc (p *pipe) InitializeReadWaiter(options N.ReadWaitOptions) (needCopy bool) {\n\tp.readWaitOptions = options\n\treturn false\n}\n\nfunc (p *pipe) WaitReadBuffer() (buffer *buf.Buffer, err error) {\n\tbuffer, err = p.waitReadBuffer()\n\tif err != nil && err != io.EOF && err != io.ErrClosedPipe {\n\t\terr = &net.OpError{Op: \"read\", Net: \"pipe\", Err: err}\n\t}\n\treturn\n}\n\nfunc (p *pipe) waitReadBuffer() (buffer *buf.Buffer, err error) {\n\tswitch {\n\tcase isClosedChan(p.localDone):\n\t\treturn nil, io.ErrClosedPipe\n\tcase isClosedChan(p.remoteDone):\n\t\treturn nil, io.EOF\n\tcase isClosedChan(p.readDeadline.Wait()):\n\t\treturn nil, os.ErrDeadlineExceeded\n\t}\n\tselect {\n\tcase bw := <-p.rdRx:\n\t\tbuffer = p.readWaitOptions.NewBuffer()\n\t\tvar nr int\n\t\tnr, err = buffer.Write(bw)\n\t\tif err != nil {\n\t\t\tbuffer.Release()\n\t\t\treturn\n\t\t}\n\t\tp.readWaitOptions.PostReturn(buffer)\n\t\tp.rdTx <- nr\n\t\treturn\n\tcase <-p.localDone:\n\t\treturn nil, io.ErrClosedPipe\n\tcase <-p.remoteDone:\n\t\treturn nil, io.EOF\n\tcase <-p.readDeadline.Wait():\n\t\treturn nil, os.ErrDeadlineExceeded\n\t}\n}\n\nfunc IsPipe(conn any) bool {\n\t_, ok := conn.(*pipe)\n\treturn ok\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/earlyconn.go",
    "content": "package net\n\nimport (\n\t\"net\"\n\t\"sync\"\n\n\t\"github.com/metacubex/mihomo/common/buf\"\n\t\"github.com/metacubex/mihomo/common/once\"\n)\n\ntype earlyConn struct {\n\tExtendedConn // only expose standard N.ExtendedConn function to outside\n\tresFunc      func() error\n\tresOnce      sync.Once\n\tresErr       error\n}\n\nfunc (conn *earlyConn) Response() error {\n\tconn.resOnce.Do(func() {\n\t\tconn.resErr = conn.resFunc()\n\t})\n\treturn conn.resErr\n}\n\nfunc (conn *earlyConn) Read(b []byte) (n int, err error) {\n\terr = conn.Response()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn conn.ExtendedConn.Read(b)\n}\n\nfunc (conn *earlyConn) ReadBuffer(buffer *buf.Buffer) (err error) {\n\terr = conn.Response()\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn conn.ExtendedConn.ReadBuffer(buffer)\n}\n\nfunc (conn *earlyConn) Upstream() any {\n\treturn conn.ExtendedConn\n}\n\nfunc (conn *earlyConn) Success() bool {\n\treturn once.Done(&conn.resOnce) && conn.resErr == nil\n}\n\nfunc (conn *earlyConn) ReaderReplaceable() bool {\n\treturn conn.Success()\n}\n\nfunc (conn *earlyConn) ReaderPossiblyReplaceable() bool {\n\treturn !conn.Success()\n}\n\nfunc (conn *earlyConn) WriterReplaceable() bool {\n\treturn true\n}\n\nvar _ ExtendedConn = (*earlyConn)(nil)\n\nfunc NewEarlyConn(c net.Conn, f func() error) net.Conn {\n\treturn &earlyConn{ExtendedConn: NewExtendedConn(c), resFunc: f}\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/io.go",
    "content": "package net\n\nimport \"io\"\n\ntype ReadOnlyReader struct {\n\tio.Reader\n}\n\ntype WriteOnlyWriter struct {\n\tio.Writer\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/listener.go",
    "content": "package net\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"sync\"\n)\n\ntype handleContextListener struct {\n\tnet.Listener\n\tctx      context.Context\n\tcancel   context.CancelFunc\n\tconns    chan net.Conn\n\terr      error\n\tonce     sync.Once\n\thandle   func(context.Context, net.Conn) (net.Conn, error)\n\tpanicLog func(any)\n}\n\nfunc (l *handleContextListener) init() {\n\tgo func() {\n\t\tfor {\n\t\t\tc, err := l.Listener.Accept()\n\t\t\tif err != nil {\n\t\t\t\tl.err = err\n\t\t\t\tclose(l.conns)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tgo func() {\n\t\t\t\tdefer func() {\n\t\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\t\tif l.panicLog != nil {\n\t\t\t\t\t\t\tl.panicLog(r)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}()\n\t\t\t\tif conn, err := l.handle(l.ctx, c); err == nil {\n\t\t\t\t\tl.conns <- conn\n\t\t\t\t} else {\n\t\t\t\t\t// handle failed, close the underlying connection.\n\t\t\t\t\t_ = c.Close()\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\t}()\n}\n\nfunc (l *handleContextListener) Accept() (net.Conn, error) {\n\tl.once.Do(l.init)\n\tif c, ok := <-l.conns; ok {\n\t\treturn c, nil\n\t}\n\treturn nil, l.err\n}\n\nfunc (l *handleContextListener) Close() error {\n\tl.cancel()\n\tl.once.Do(func() { // l.init has not been called yet, so close related resources directly.\n\t\tl.err = net.ErrClosed\n\t\tclose(l.conns)\n\t})\n\tdefer func() {\n\t\t// at here, listener has been closed, so we should close all connections in the channel\n\t\tfor c := range l.conns {\n\t\t\tgo func(c net.Conn) {\n\t\t\t\tdefer func() {\n\t\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\t\tif l.panicLog != nil {\n\t\t\t\t\t\t\tl.panicLog(r)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}()\n\t\t\t\t_ = c.Close()\n\t\t\t}(c)\n\t\t}\n\t}()\n\treturn l.Listener.Close()\n}\n\nfunc NewHandleContextListener(ctx context.Context, l net.Listener, handle func(context.Context, net.Conn) (net.Conn, error), panicLog func(any)) net.Listener {\n\tctx, cancel := context.WithCancel(ctx)\n\treturn &handleContextListener{\n\t\tListener: l,\n\t\tctx:      ctx,\n\t\tcancel:   cancel,\n\t\tconns:    make(chan net.Conn),\n\t\thandle:   handle,\n\t\tpanicLog: panicLog,\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/packet/packet.go",
    "content": "package packet\n\nimport (\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/common/pool\"\n)\n\ntype WaitReadFrom interface {\n\tWaitReadFrom() (data []byte, put func(), addr net.Addr, err error)\n}\n\ntype EnhancePacketConn interface {\n\tnet.PacketConn\n\tWaitReadFrom\n}\n\nfunc NewEnhancePacketConn(pc net.PacketConn) EnhancePacketConn {\n\tif udpConn, isUDPConn := pc.(*net.UDPConn); isUDPConn {\n\t\treturn &enhanceUDPConn{UDPConn: udpConn}\n\t}\n\tif enhancePC, isEnhancePC := pc.(EnhancePacketConn); isEnhancePC {\n\t\treturn enhancePC\n\t}\n\tif singPC, isSingPC := pc.(SingPacketConn); isSingPC {\n\t\treturn newEnhanceSingPacketConn(singPC)\n\t}\n\treturn &enhancePacketConn{PacketConn: pc}\n}\n\ntype enhancePacketConn struct {\n\tnet.PacketConn\n}\n\nfunc (c *enhancePacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {\n\treturn waitReadFrom(c.PacketConn)\n}\n\nfunc (c *enhancePacketConn) Upstream() any {\n\treturn c.PacketConn\n}\n\nfunc (c *enhancePacketConn) WriterReplaceable() bool {\n\treturn true\n}\n\nfunc (c *enhancePacketConn) ReaderReplaceable() bool {\n\treturn true\n}\n\nfunc (c *enhanceUDPConn) Upstream() any {\n\treturn c.UDPConn\n}\n\nfunc (c *enhanceUDPConn) WriterReplaceable() bool {\n\treturn true\n}\n\nfunc (c *enhanceUDPConn) ReaderReplaceable() bool {\n\treturn true\n}\n\nfunc waitReadFrom(pc net.PacketConn) (data []byte, put func(), addr net.Addr, err error) {\n\treadBuf := pool.Get(pool.UDPBufferSize)\n\tput = func() {\n\t\t_ = pool.Put(readBuf)\n\t}\n\tvar readN int\n\treadN, addr, err = pc.ReadFrom(readBuf)\n\tif readN > 0 {\n\t\tdata = readBuf[:readN]\n\t} else {\n\t\tput()\n\t\tput = nil\n\t}\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/packet/packet_posix.go",
    "content": "//go:build !windows\n\npackage packet\n\nimport (\n\t\"net\"\n\t\"strconv\"\n\t\"syscall\"\n\n\t\"github.com/metacubex/mihomo/common/pool\"\n)\n\ntype enhanceUDPConn struct {\n\t*net.UDPConn\n\trawConn syscall.RawConn\n}\n\nfunc (c *enhanceUDPConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {\n\tif c.rawConn == nil {\n\t\tc.rawConn, _ = c.UDPConn.SyscallConn()\n\t}\n\tvar readErr error\n\terr = c.rawConn.Read(func(fd uintptr) (done bool) {\n\t\treadBuf := pool.Get(pool.UDPBufferSize)\n\t\tput = func() {\n\t\t\t_ = pool.Put(readBuf)\n\t\t}\n\t\tvar readFrom syscall.Sockaddr\n\t\tvar readN int\n\t\treadN, _, _, readFrom, readErr = syscall.Recvmsg(int(fd), readBuf, nil, 0)\n\t\tif readN > 0 {\n\t\t\tdata = readBuf[:readN]\n\t\t} else {\n\t\t\tput()\n\t\t\tput = nil\n\t\t\tdata = nil\n\t\t}\n\t\tif readErr == syscall.EAGAIN {\n\t\t\treturn false\n\t\t}\n\t\tif readFrom != nil {\n\t\t\tswitch from := readFrom.(type) {\n\t\t\tcase *syscall.SockaddrInet4:\n\t\t\t\tip := from.Addr // copy from.Addr; ip escapes, so this line allocates 4 bytes\n\t\t\t\taddr = &net.UDPAddr{IP: ip[:], Port: from.Port}\n\t\t\tcase *syscall.SockaddrInet6:\n\t\t\t\tip := from.Addr // copy from.Addr; ip escapes, so this line allocates 16 bytes\n\t\t\t\tzone := \"\"\n\t\t\t\tif from.ZoneId != 0 {\n\t\t\t\t\tzone = strconv.FormatInt(int64(from.ZoneId), 10)\n\t\t\t\t}\n\t\t\t\taddr = &net.UDPAddr{IP: ip[:], Port: from.Port, Zone: zone}\n\t\t\t}\n\t\t}\n\t\t// udp should not convert readN == 0 to io.EOF\n\t\t//if readN == 0 {\n\t\t//\treadErr = io.EOF\n\t\t//}\n\t\treturn true\n\t})\n\tif err != nil {\n\t\treturn\n\t}\n\tif readErr != nil {\n\t\terr = readErr\n\t\treturn\n\t}\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/packet/packet_sing.go",
    "content": "package packet\n\nimport (\n\t\"net\"\n\n\t\"github.com/metacubex/sing/common/buf\"\n\t\"github.com/metacubex/sing/common/bufio\"\n\tM \"github.com/metacubex/sing/common/metadata\"\n\tN \"github.com/metacubex/sing/common/network\"\n)\n\ntype SingPacketConn = N.NetPacketConn\n\ntype EnhanceSingPacketConn interface {\n\tSingPacketConn\n\tEnhancePacketConn\n}\n\ntype enhanceSingPacketConn struct {\n\tSingPacketConn\n\tpacketReadWaiter N.PacketReadWaiter\n}\n\nfunc (c *enhanceSingPacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {\n\tvar buff *buf.Buffer\n\tvar dest M.Socksaddr\n\trwOptions := N.ReadWaitOptions{}\n\tif c.packetReadWaiter != nil {\n\t\tc.packetReadWaiter.InitializeReadWaiter(rwOptions)\n\t\tbuff, dest, err = c.packetReadWaiter.WaitReadPacket()\n\t} else {\n\t\tbuff = rwOptions.NewPacketBuffer()\n\t\tdest, err = c.SingPacketConn.ReadPacket(buff)\n\t\tif buff != nil {\n\t\t\trwOptions.PostReturn(buff)\n\t\t}\n\t}\n\tif dest.IsFqdn() {\n\t\taddr = dest\n\t} else {\n\t\taddr = dest.UDPAddr()\n\t}\n\tif err != nil {\n\t\tbuff.Release()\n\t\treturn\n\t}\n\tif buff == nil {\n\t\treturn\n\t}\n\tif buff.IsEmpty() {\n\t\tbuff.Release()\n\t\treturn\n\t}\n\tdata = buff.Bytes()\n\tput = buff.Release\n\treturn\n}\n\nfunc (c *enhanceSingPacketConn) Upstream() any {\n\treturn c.SingPacketConn\n}\n\nfunc (c *enhanceSingPacketConn) WriterReplaceable() bool {\n\treturn true\n}\n\nfunc (c *enhanceSingPacketConn) ReaderReplaceable() bool {\n\treturn true\n}\n\nfunc newEnhanceSingPacketConn(conn SingPacketConn) *enhanceSingPacketConn {\n\tepc := &enhanceSingPacketConn{SingPacketConn: conn}\n\tif readWaiter, isReadWaiter := bufio.CreatePacketReadWaiter(conn); isReadWaiter {\n\t\tepc.packetReadWaiter = readWaiter\n\t}\n\treturn epc\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/packet/packet_windows.go",
    "content": "//go:build windows\n\npackage packet\n\nimport (\n\t\"net\"\n\t\"strconv\"\n\t\"syscall\"\n\n\t\"github.com/metacubex/mihomo/common/pool\"\n\n\t\"golang.org/x/sys/windows\"\n)\n\ntype enhanceUDPConn struct {\n\t*net.UDPConn\n\trawConn syscall.RawConn\n}\n\nfunc (c *enhanceUDPConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {\n\tif c.rawConn == nil {\n\t\tc.rawConn, _ = c.UDPConn.SyscallConn()\n\t}\n\tvar readErr error\n\thasData := false\n\terr = c.rawConn.Read(func(fd uintptr) (done bool) {\n\t\tif !hasData {\n\t\t\thasData = true\n\t\t\t// golang's internal/poll.FD.RawRead will Use a zero-byte read as a way to get notified when this\n\t\t\t// socket is readable if we return false. So the `recvfrom` syscall will not block the system thread.\n\t\t\treturn false\n\t\t}\n\t\treadBuf := pool.Get(pool.UDPBufferSize)\n\t\tput = func() {\n\t\t\t_ = pool.Put(readBuf)\n\t\t}\n\t\tvar readFrom windows.Sockaddr\n\t\tvar readN int\n\t\treadN, readFrom, readErr = windows.Recvfrom(windows.Handle(fd), readBuf, 0)\n\t\tif readN > 0 {\n\t\t\tdata = readBuf[:readN]\n\t\t} else {\n\t\t\tput()\n\t\t\tput = nil\n\t\t\tdata = nil\n\t\t}\n\t\tif readErr == windows.WSAEWOULDBLOCK {\n\t\t\treturn false\n\t\t}\n\t\tif readFrom != nil {\n\t\t\tswitch from := readFrom.(type) {\n\t\t\tcase *windows.SockaddrInet4:\n\t\t\t\tip := from.Addr // copy from.Addr; ip escapes, so this line allocates 4 bytes\n\t\t\t\taddr = &net.UDPAddr{IP: ip[:], Port: from.Port}\n\t\t\tcase *windows.SockaddrInet6:\n\t\t\t\tip := from.Addr // copy from.Addr; ip escapes, so this line allocates 16 bytes\n\t\t\t\tzone := \"\"\n\t\t\t\tif from.ZoneId != 0 {\n\t\t\t\t\tzone = strconv.FormatInt(int64(from.ZoneId), 10)\n\t\t\t\t}\n\t\t\t\taddr = &net.UDPAddr{IP: ip[:], Port: from.Port, Zone: zone}\n\t\t\t}\n\t\t}\n\t\t// udp should not convert readN == 0 to io.EOF\n\t\t//if readN == 0 {\n\t\t//\treadErr = io.EOF\n\t\t//}\n\t\thasData = false\n\t\treturn true\n\t})\n\tif err != nil {\n\t\treturn\n\t}\n\tif readErr != nil {\n\t\terr = readErr\n\t\treturn\n\t}\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/packet/ref.go",
    "content": "package packet\n\nimport (\n\t\"net\"\n\t\"runtime\"\n\t\"time\"\n)\n\ntype refPacketConn struct {\n\tpc  EnhancePacketConn\n\tref any\n}\n\nfunc (c *refPacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {\n\tdefer runtime.KeepAlive(c.ref)\n\treturn c.pc.WaitReadFrom()\n}\n\nfunc (c *refPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {\n\tdefer runtime.KeepAlive(c.ref)\n\treturn c.pc.ReadFrom(p)\n}\n\nfunc (c *refPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {\n\tdefer runtime.KeepAlive(c.ref)\n\treturn c.pc.WriteTo(p, addr)\n}\n\nfunc (c *refPacketConn) Close() error {\n\tdefer runtime.KeepAlive(c.ref)\n\treturn c.pc.Close()\n}\n\nfunc (c *refPacketConn) LocalAddr() net.Addr {\n\tdefer runtime.KeepAlive(c.ref)\n\treturn c.pc.LocalAddr()\n}\n\nfunc (c *refPacketConn) SetDeadline(t time.Time) error {\n\tdefer runtime.KeepAlive(c.ref)\n\treturn c.pc.SetDeadline(t)\n}\n\nfunc (c *refPacketConn) SetReadDeadline(t time.Time) error {\n\tdefer runtime.KeepAlive(c.ref)\n\treturn c.pc.SetReadDeadline(t)\n}\n\nfunc (c *refPacketConn) SetWriteDeadline(t time.Time) error {\n\tdefer runtime.KeepAlive(c.ref)\n\treturn c.pc.SetWriteDeadline(t)\n}\n\nfunc (c *refPacketConn) Upstream() any {\n\treturn c.pc\n}\n\nfunc (c *refPacketConn) ReaderReplaceable() bool { // Relay() will handle reference\n\treturn true\n}\n\nfunc (c *refPacketConn) WriterReplaceable() bool { // Relay() will handle reference\n\treturn true\n}\n\nfunc NewRefPacketConn(pc net.PacketConn, ref any) EnhancePacketConn {\n\trPC := &refPacketConn{pc: NewEnhancePacketConn(pc), ref: ref}\n\tif singPC, isSingPC := pc.(SingPacketConn); isSingPC {\n\t\treturn &refSingPacketConn{\n\t\t\trefPacketConn:  rPC,\n\t\t\tsingPacketConn: singPC,\n\t\t}\n\t}\n\treturn rPC\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/packet/ref_sing.go",
    "content": "package packet\n\nimport (\n\t\"runtime\"\n\n\t\"github.com/metacubex/sing/common/buf\"\n\tM \"github.com/metacubex/sing/common/metadata\"\n\tN \"github.com/metacubex/sing/common/network\"\n)\n\ntype refSingPacketConn struct {\n\t*refPacketConn\n\tsingPacketConn SingPacketConn\n}\n\nvar _ N.NetPacketConn = (*refSingPacketConn)(nil)\n\nfunc (c *refSingPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {\n\tdefer runtime.KeepAlive(c.ref)\n\treturn c.singPacketConn.WritePacket(buffer, destination)\n}\n\nfunc (c *refSingPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {\n\tdefer runtime.KeepAlive(c.ref)\n\treturn c.singPacketConn.ReadPacket(buffer)\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/packet/thread.go",
    "content": "package packet\n\nimport (\n\t\"net\"\n\t\"sync\"\n)\n\ntype threadSafePacketConn struct {\n\tEnhancePacketConn\n\taccess sync.Mutex\n}\n\nfunc (c *threadSafePacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {\n\tc.access.Lock()\n\tdefer c.access.Unlock()\n\treturn c.EnhancePacketConn.WriteTo(b, addr)\n}\n\nfunc (c *threadSafePacketConn) Upstream() any {\n\treturn c.EnhancePacketConn\n}\n\nfunc (c *threadSafePacketConn) ReaderReplaceable() bool {\n\treturn true\n}\n\nfunc NewThreadSafePacketConn(pc net.PacketConn) EnhancePacketConn {\n\ttsPC := &threadSafePacketConn{EnhancePacketConn: NewEnhancePacketConn(pc)}\n\tif singPC, isSingPC := pc.(SingPacketConn); isSingPC {\n\t\treturn &threadSafeSingPacketConn{\n\t\t\tthreadSafePacketConn: tsPC,\n\t\t\tsingPacketConn:       singPC,\n\t\t}\n\t}\n\treturn tsPC\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/packet/thread_sing.go",
    "content": "package packet\n\nimport (\n\t\"github.com/metacubex/sing/common/buf\"\n\tM \"github.com/metacubex/sing/common/metadata\"\n\tN \"github.com/metacubex/sing/common/network\"\n)\n\ntype threadSafeSingPacketConn struct {\n\t*threadSafePacketConn\n\tsingPacketConn SingPacketConn\n}\n\nvar _ N.NetPacketConn = (*threadSafeSingPacketConn)(nil)\n\nfunc (c *threadSafeSingPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {\n\tc.access.Lock()\n\tdefer c.access.Unlock()\n\treturn c.singPacketConn.WritePacket(buffer, destination)\n}\n\nfunc (c *threadSafeSingPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {\n\treturn c.singPacketConn.ReadPacket(buffer)\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/packet.go",
    "content": "package net\n\nimport (\n\t\"github.com/metacubex/mihomo/common/net/deadline\"\n\t\"github.com/metacubex/mihomo/common/net/packet\"\n)\n\ntype EnhancePacketConn = packet.EnhancePacketConn\ntype WaitReadFrom = packet.WaitReadFrom\n\nvar NewEnhancePacketConn = packet.NewEnhancePacketConn\nvar NewThreadSafePacketConn = packet.NewThreadSafePacketConn\nvar NewRefPacketConn = packet.NewRefPacketConn\n\nvar NewDeadlineNetPacketConn = deadline.NewNetPacketConn\nvar NewDeadlineEnhancePacketConn = deadline.NewEnhancePacketConn\nvar NewDeadlineSingPacketConn = deadline.NewSingPacketConn\nvar NewDeadlineEnhanceSingPacketConn = deadline.NewEnhanceSingPacketConn\n"
  },
  {
    "path": "core/Clash.Meta/common/net/refconn.go",
    "content": "package net\n\nimport (\n\t\"net\"\n\t\"runtime\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/buf\"\n)\n\ntype refConn struct {\n\tconn ExtendedConn\n\tref  any\n}\n\nfunc (c *refConn) Read(b []byte) (n int, err error) {\n\tdefer runtime.KeepAlive(c.ref)\n\treturn c.conn.Read(b)\n}\n\nfunc (c *refConn) Write(b []byte) (n int, err error) {\n\tdefer runtime.KeepAlive(c.ref)\n\treturn c.conn.Write(b)\n}\n\nfunc (c *refConn) Close() error {\n\tdefer runtime.KeepAlive(c.ref)\n\treturn c.conn.Close()\n}\n\nfunc (c *refConn) LocalAddr() net.Addr {\n\tdefer runtime.KeepAlive(c.ref)\n\treturn c.conn.LocalAddr()\n}\n\nfunc (c *refConn) RemoteAddr() net.Addr {\n\tdefer runtime.KeepAlive(c.ref)\n\treturn c.conn.RemoteAddr()\n}\n\nfunc (c *refConn) SetDeadline(t time.Time) error {\n\tdefer runtime.KeepAlive(c.ref)\n\treturn c.conn.SetDeadline(t)\n}\n\nfunc (c *refConn) SetReadDeadline(t time.Time) error {\n\tdefer runtime.KeepAlive(c.ref)\n\treturn c.conn.SetReadDeadline(t)\n}\n\nfunc (c *refConn) SetWriteDeadline(t time.Time) error {\n\tdefer runtime.KeepAlive(c.ref)\n\treturn c.conn.SetWriteDeadline(t)\n}\n\nfunc (c *refConn) Upstream() any {\n\treturn c.conn\n}\n\nfunc (c *refConn) ReadBuffer(buffer *buf.Buffer) error {\n\tdefer runtime.KeepAlive(c.ref)\n\treturn c.conn.ReadBuffer(buffer)\n}\n\nfunc (c *refConn) WriteBuffer(buffer *buf.Buffer) error {\n\tdefer runtime.KeepAlive(c.ref)\n\treturn c.conn.WriteBuffer(buffer)\n}\n\nfunc (c *refConn) ReaderReplaceable() bool { // Relay() will handle reference\n\treturn true\n}\n\nfunc (c *refConn) WriterReplaceable() bool { // Relay() will handle reference\n\treturn true\n}\n\nvar _ ExtendedConn = (*refConn)(nil)\n\nfunc NewRefConn(conn net.Conn, ref any) ExtendedConn {\n\treturn &refConn{conn: NewExtendedConn(conn), ref: ref}\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/relay.go",
    "content": "package net\n\n//import (\n//\t\"io\"\n//\t\"net\"\n//\t\"time\"\n//)\n//\n//// Relay copies between left and right bidirectionally.\n//func Relay(leftConn, rightConn net.Conn) {\n//\tch := make(chan error)\n//\n//\tgo func() {\n//\t\t// Wrapping to avoid using *net.TCPConn.(ReadFrom)\n//\t\t// See also https://github.com/metacubex/mihomo/pull/1209\n//\t\t_, err := io.Copy(WriteOnlyWriter{Writer: leftConn}, ReadOnlyReader{Reader: rightConn})\n//\t\tleftConn.SetReadDeadline(time.Now())\n//\t\tch <- err\n//\t}()\n//\n//\t_, _ = io.Copy(WriteOnlyWriter{Writer: rightConn}, ReadOnlyReader{Reader: leftConn})\n//\trightConn.SetReadDeadline(time.Now())\n//\t<-ch\n//}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/sing.go",
    "content": "package net\n\nimport (\n\t\"io\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/common/net/deadline\"\n\n\t\"github.com/metacubex/sing/common\"\n\t\"github.com/metacubex/sing/common/bufio\"\n\t\"github.com/metacubex/sing/common/network\"\n)\n\nvar NewExtendedConn = bufio.NewExtendedConn\nvar NewExtendedWriter = bufio.NewExtendedWriter\nvar NewExtendedReader = bufio.NewExtendedReader\n\ntype ExtendedConn = network.ExtendedConn\ntype ExtendedWriter = network.ExtendedWriter\ntype ExtendedReader = network.ExtendedReader\n\nvar WriteBuffer = bufio.WriteBuffer\n\ntype ReadWaitOptions = network.ReadWaitOptions\n\nvar NewReadWaitOptions = network.NewReadWaitOptions\nvar CalculateFrontHeadroom = network.CalculateFrontHeadroom\nvar CalculateRearHeadroom = network.CalculateRearHeadroom\n\ntype ReaderWithUpstream = network.ReaderWithUpstream\ntype WithUpstreamReader = network.WithUpstreamReader\ntype WriterWithUpstream = network.WriterWithUpstream\ntype WithUpstreamWriter = network.WithUpstreamWriter\ntype WithUpstream = common.WithUpstream\n\nvar UnwrapReader = network.UnwrapReader\nvar UnwrapWriter = network.UnwrapWriter\n\nfunc NewDeadlineConn(conn net.Conn) ExtendedConn {\n\tif deadline.IsPipe(conn) || deadline.IsPipe(UnwrapReader(conn)) {\n\t\treturn NewExtendedConn(conn) // pipe always have correctly deadline implement\n\t}\n\tif deadline.IsConn(conn) || deadline.IsConn(UnwrapReader(conn)) {\n\t\treturn NewExtendedConn(conn) // was a *deadline.Conn\n\t}\n\treturn deadline.NewConn(conn)\n}\n\nfunc NeedHandshake(conn any) bool {\n\tif earlyConn, isEarlyConn := common.Cast[network.EarlyConn](conn); isEarlyConn && earlyConn.NeedHandshake() {\n\t\treturn true\n\t}\n\treturn false\n}\n\ntype CountFunc = network.CountFunc\n\nvar Pipe = deadline.Pipe\n\nfunc closeWrite(writer io.Closer) error {\n\tif c, ok := common.Cast[network.WriteCloser](writer); ok {\n\t\treturn c.CloseWrite()\n\t}\n\treturn writer.Close()\n}\n\n// Relay copies between left and right bidirectionally.\n// like [bufio.CopyConn] but remove unneeded [context.Context] handle and the cost of [task.Group]\nfunc Relay(leftConn, rightConn net.Conn) {\n\tdefer func() {\n\t\t_ = leftConn.Close()\n\t\t_ = rightConn.Close()\n\t}()\n\n\tch := make(chan struct{})\n\tgo func() {\n\t\t_, err := bufio.Copy(leftConn, rightConn)\n\t\tif err == nil {\n\t\t\t_ = closeWrite(leftConn)\n\t\t} else {\n\t\t\t_ = leftConn.Close()\n\t\t}\n\t\tclose(ch)\n\t}()\n\n\t_, err := bufio.Copy(rightConn, leftConn)\n\tif err == nil {\n\t\t_ = closeWrite(rightConn)\n\t} else {\n\t\t_ = rightConn.Close()\n\t}\n\t<-ch\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/tcpip.go",
    "content": "package net\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"strings\"\n)\n\nfunc SplitNetworkType(s string) (string, string, error) {\n\tvar (\n\t\tshecme   string\n\t\thostPort string\n\t)\n\tresult := strings.Split(s, \"://\")\n\tif len(result) == 2 {\n\t\tshecme = result[0]\n\t\thostPort = result[1]\n\t} else if len(result) == 1 {\n\t\thostPort = result[0]\n\t} else {\n\t\treturn \"\", \"\", fmt.Errorf(\"tcp/udp style error\")\n\t}\n\n\tif len(shecme) == 0 {\n\t\tshecme = \"udp\"\n\t}\n\n\tif shecme != \"tcp\" && shecme != \"udp\" {\n\t\treturn \"\", \"\", fmt.Errorf(\"scheme should be tcp:// or udp://\")\n\t} else {\n\t\treturn shecme, hostPort, nil\n\t}\n}\n\nfunc SplitHostPort(s string) (host, port string, hasPort bool, err error) {\n\ttemp := s\n\thasPort = true\n\n\tif !strings.Contains(s, \":\") && !strings.Contains(s, \"]:\") {\n\t\ttemp += \":0\"\n\t\thasPort = false\n\t}\n\n\thost, port, err = net.SplitHostPort(temp)\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/net/websocket.go",
    "content": "package net\n\nimport (\n\t\"crypto/sha1\"\n\t\"encoding/base64\"\n\t\"encoding/binary\"\n\t\"math/bits\"\n)\n\n// kanged from https://github.com/nhooyr/websocket/blob/master/frame.go\n// License: MIT\n\n// MaskWebSocket applies the WebSocket masking algorithm to p\n// with the given key.\n// See https://tools.ietf.org/html/rfc6455#section-5.3\n//\n// The returned value is the correctly rotated key to\n// to continue to mask/unmask the message.\n//\n// It is optimized for LittleEndian and expects the key\n// to be in little endian.\n//\n// See https://github.com/golang/go/issues/31586\nfunc MaskWebSocket(key uint32, b []byte) uint32 {\n\tif len(b) >= 8 {\n\t\tkey64 := uint64(key)<<32 | uint64(key)\n\n\t\t// At some point in the future we can clean these unrolled loops up.\n\t\t// See https://github.com/golang/go/issues/31586#issuecomment-487436401\n\n\t\t// Then we xor until b is less than 128 bytes.\n\t\tfor len(b) >= 128 {\n\t\t\tv := binary.LittleEndian.Uint64(b)\n\t\t\tbinary.LittleEndian.PutUint64(b, v^key64)\n\t\t\tv = binary.LittleEndian.Uint64(b[8:16])\n\t\t\tbinary.LittleEndian.PutUint64(b[8:16], v^key64)\n\t\t\tv = binary.LittleEndian.Uint64(b[16:24])\n\t\t\tbinary.LittleEndian.PutUint64(b[16:24], v^key64)\n\t\t\tv = binary.LittleEndian.Uint64(b[24:32])\n\t\t\tbinary.LittleEndian.PutUint64(b[24:32], v^key64)\n\t\t\tv = binary.LittleEndian.Uint64(b[32:40])\n\t\t\tbinary.LittleEndian.PutUint64(b[32:40], v^key64)\n\t\t\tv = binary.LittleEndian.Uint64(b[40:48])\n\t\t\tbinary.LittleEndian.PutUint64(b[40:48], v^key64)\n\t\t\tv = binary.LittleEndian.Uint64(b[48:56])\n\t\t\tbinary.LittleEndian.PutUint64(b[48:56], v^key64)\n\t\t\tv = binary.LittleEndian.Uint64(b[56:64])\n\t\t\tbinary.LittleEndian.PutUint64(b[56:64], v^key64)\n\t\t\tv = binary.LittleEndian.Uint64(b[64:72])\n\t\t\tbinary.LittleEndian.PutUint64(b[64:72], v^key64)\n\t\t\tv = binary.LittleEndian.Uint64(b[72:80])\n\t\t\tbinary.LittleEndian.PutUint64(b[72:80], v^key64)\n\t\t\tv = binary.LittleEndian.Uint64(b[80:88])\n\t\t\tbinary.LittleEndian.PutUint64(b[80:88], v^key64)\n\t\t\tv = binary.LittleEndian.Uint64(b[88:96])\n\t\t\tbinary.LittleEndian.PutUint64(b[88:96], v^key64)\n\t\t\tv = binary.LittleEndian.Uint64(b[96:104])\n\t\t\tbinary.LittleEndian.PutUint64(b[96:104], v^key64)\n\t\t\tv = binary.LittleEndian.Uint64(b[104:112])\n\t\t\tbinary.LittleEndian.PutUint64(b[104:112], v^key64)\n\t\t\tv = binary.LittleEndian.Uint64(b[112:120])\n\t\t\tbinary.LittleEndian.PutUint64(b[112:120], v^key64)\n\t\t\tv = binary.LittleEndian.Uint64(b[120:128])\n\t\t\tbinary.LittleEndian.PutUint64(b[120:128], v^key64)\n\t\t\tb = b[128:]\n\t\t}\n\n\t\t// Then we xor until b is less than 64 bytes.\n\t\tfor len(b) >= 64 {\n\t\t\tv := binary.LittleEndian.Uint64(b)\n\t\t\tbinary.LittleEndian.PutUint64(b, v^key64)\n\t\t\tv = binary.LittleEndian.Uint64(b[8:16])\n\t\t\tbinary.LittleEndian.PutUint64(b[8:16], v^key64)\n\t\t\tv = binary.LittleEndian.Uint64(b[16:24])\n\t\t\tbinary.LittleEndian.PutUint64(b[16:24], v^key64)\n\t\t\tv = binary.LittleEndian.Uint64(b[24:32])\n\t\t\tbinary.LittleEndian.PutUint64(b[24:32], v^key64)\n\t\t\tv = binary.LittleEndian.Uint64(b[32:40])\n\t\t\tbinary.LittleEndian.PutUint64(b[32:40], v^key64)\n\t\t\tv = binary.LittleEndian.Uint64(b[40:48])\n\t\t\tbinary.LittleEndian.PutUint64(b[40:48], v^key64)\n\t\t\tv = binary.LittleEndian.Uint64(b[48:56])\n\t\t\tbinary.LittleEndian.PutUint64(b[48:56], v^key64)\n\t\t\tv = binary.LittleEndian.Uint64(b[56:64])\n\t\t\tbinary.LittleEndian.PutUint64(b[56:64], v^key64)\n\t\t\tb = b[64:]\n\t\t}\n\n\t\t// Then we xor until b is less than 32 bytes.\n\t\tfor len(b) >= 32 {\n\t\t\tv := binary.LittleEndian.Uint64(b)\n\t\t\tbinary.LittleEndian.PutUint64(b, v^key64)\n\t\t\tv = binary.LittleEndian.Uint64(b[8:16])\n\t\t\tbinary.LittleEndian.PutUint64(b[8:16], v^key64)\n\t\t\tv = binary.LittleEndian.Uint64(b[16:24])\n\t\t\tbinary.LittleEndian.PutUint64(b[16:24], v^key64)\n\t\t\tv = binary.LittleEndian.Uint64(b[24:32])\n\t\t\tbinary.LittleEndian.PutUint64(b[24:32], v^key64)\n\t\t\tb = b[32:]\n\t\t}\n\n\t\t// Then we xor until b is less than 16 bytes.\n\t\tfor len(b) >= 16 {\n\t\t\tv := binary.LittleEndian.Uint64(b)\n\t\t\tbinary.LittleEndian.PutUint64(b, v^key64)\n\t\t\tv = binary.LittleEndian.Uint64(b[8:16])\n\t\t\tbinary.LittleEndian.PutUint64(b[8:16], v^key64)\n\t\t\tb = b[16:]\n\t\t}\n\n\t\t// Then we xor until b is less than 8 bytes.\n\t\tfor len(b) >= 8 {\n\t\t\tv := binary.LittleEndian.Uint64(b)\n\t\t\tbinary.LittleEndian.PutUint64(b, v^key64)\n\t\t\tb = b[8:]\n\t\t}\n\t}\n\n\t// Then we xor until b is less than 4 bytes.\n\tfor len(b) >= 4 {\n\t\tv := binary.LittleEndian.Uint32(b)\n\t\tbinary.LittleEndian.PutUint32(b, v^key)\n\t\tb = b[4:]\n\t}\n\n\t// xor remaining bytes.\n\tfor i := range b {\n\t\tb[i] ^= byte(key)\n\t\tkey = bits.RotateLeft32(key, -8)\n\t}\n\n\treturn key\n}\n\nfunc GetWebSocketSecAccept(secKey string) string {\n\tconst magic = \"258EAFA5-E914-47DA-95CA-C5AB0DC85B11\"\n\tconst nonceSize = 24 // base64.StdEncoding.EncodedLen(nonceKeySize)\n\tp := make([]byte, nonceSize+len(magic))\n\tcopy(p[:nonceSize], secKey)\n\tcopy(p[nonceSize:], magic)\n\tsum := sha1.Sum(p)\n\treturn base64.StdEncoding.EncodeToString(sum[:])\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/observable/iterable.go",
    "content": "package observable\n\ntype Iterable[T any] <-chan T\n"
  },
  {
    "path": "core/Clash.Meta/common/observable/observable.go",
    "content": "package observable\n\nimport (\n\t\"errors\"\n\t\"sync\"\n)\n\ntype Observable[T any] struct {\n\titerable Iterable[T]\n\tlistener map[Subscription[T]]*Subscriber[T]\n\tmux      sync.Mutex\n\tdone     bool\n\tstopCh   chan struct{}\n}\n\nfunc (o *Observable[T]) process() {\n\tfor item := range o.iterable {\n\t\to.mux.Lock()\n\t\tfor _, sub := range o.listener {\n\t\t\tsub.Emit(item)\n\t\t}\n\t\to.mux.Unlock()\n\t}\n\to.close()\n}\n\nfunc (o *Observable[T]) close() {\n\to.mux.Lock()\n\tdefer o.mux.Unlock()\n\n\to.done = true\n\tfor _, sub := range o.listener {\n\t\tsub.Close()\n\t}\n\tclose(o.stopCh)\n}\n\nfunc (o *Observable[T]) Subscribe() (Subscription[T], error) {\n\to.mux.Lock()\n\tdefer o.mux.Unlock()\n\tif o.done {\n\t\treturn nil, errors.New(\"observable is closed\")\n\t}\n\tsubscriber := newSubscriber[T]()\n\to.listener[subscriber.Out()] = subscriber\n\treturn subscriber.Out(), nil\n}\n\nfunc (o *Observable[T]) UnSubscribe(sub Subscription[T]) {\n\to.mux.Lock()\n\tdefer o.mux.Unlock()\n\tsubscriber, exist := o.listener[sub]\n\tif !exist {\n\t\treturn\n\t}\n\tdelete(o.listener, sub)\n\tsubscriber.Close()\n}\n\nfunc NewObservable[T any](iter Iterable[T]) *Observable[T] {\n\tobservable := &Observable[T]{\n\t\titerable: iter,\n\t\tlistener: map[Subscription[T]]*Subscriber[T]{},\n\t\tstopCh:   make(chan struct{}),\n\t}\n\tgo observable.process()\n\treturn observable\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/observable/observable_test.go",
    "content": "package observable\n\nimport (\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/atomic\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc iterator[T any](item []T) chan T {\n\tch := make(chan T)\n\tgo func() {\n\t\ttime.Sleep(100 * time.Millisecond)\n\t\tfor _, elm := range item {\n\t\t\tch <- elm\n\t\t}\n\t\tclose(ch)\n\t}()\n\treturn ch\n}\n\nfunc TestObservable(t *testing.T) {\n\titer := iterator[int]([]int{1, 2, 3, 4, 5})\n\tsrc := NewObservable[int](iter)\n\tdata, err := src.Subscribe()\n\tassert.Nil(t, err)\n\tcount := 0\n\tfor range data {\n\t\tcount++\n\t}\n\tassert.Equal(t, count, 5)\n}\n\nfunc TestObservable_MultiSubscribe(t *testing.T) {\n\titer := iterator[int]([]int{1, 2, 3, 4, 5})\n\tsrc := NewObservable[int](iter)\n\tch1, _ := src.Subscribe()\n\tch2, _ := src.Subscribe()\n\tcount := atomic.NewInt32(0)\n\n\tvar wg sync.WaitGroup\n\twg.Add(2)\n\twaitCh := func(ch <-chan int) {\n\t\tfor range ch {\n\t\t\tcount.Add(1)\n\t\t}\n\t\twg.Done()\n\t}\n\tgo waitCh(ch1)\n\tgo waitCh(ch2)\n\twg.Wait()\n\tassert.Equal(t, int32(10), count.Load())\n}\n\nfunc TestObservable_UnSubscribe(t *testing.T) {\n\titer := iterator[int]([]int{1, 2, 3, 4, 5})\n\tsrc := NewObservable[int](iter)\n\tdata, err := src.Subscribe()\n\tassert.Nil(t, err)\n\tsrc.UnSubscribe(data)\n\t_, open := <-data\n\tassert.False(t, open)\n}\n\nfunc TestObservable_SubscribeClosedSource(t *testing.T) {\n\titer := iterator[int]([]int{1})\n\tsrc := NewObservable[int](iter)\n\tdata, _ := src.Subscribe()\n\t<-data\n\tselect {\n\tcase <-src.stopCh:\n\tcase <-time.After(time.Second):\n\t\tassert.Fail(t, \"timeout not stop\")\n\t}\n}\n\nfunc TestObservable_UnSubscribeWithNotExistSubscription(t *testing.T) {\n\tsub := Subscription[int](make(chan int))\n\titer := iterator[int]([]int{1})\n\tsrc := NewObservable[int](iter)\n\tsrc.UnSubscribe(sub)\n}\n\nfunc TestObservable_SubscribeGoroutineLeak(t *testing.T) {\n\titer := iterator[int]([]int{1, 2, 3, 4, 5})\n\tsrc := NewObservable[int](iter)\n\tmax := 100\n\n\tvar list []Subscription[int]\n\tfor i := 0; i < max; i++ {\n\t\tch, _ := src.Subscribe()\n\t\tlist = append(list, ch)\n\t}\n\n\tvar wg sync.WaitGroup\n\twg.Add(max)\n\twaitCh := func(ch <-chan int) {\n\t\tfor range ch {\n\t\t}\n\t\twg.Done()\n\t}\n\n\tfor _, ch := range list {\n\t\tgo waitCh(ch)\n\t}\n\twg.Wait()\n\n\tfor _, sub := range list {\n\t\t_, more := <-sub\n\t\tassert.False(t, more)\n\t}\n\n\t_, more := <-list[0]\n\tassert.False(t, more)\n}\n\nfunc Benchmark_Observable_1000(b *testing.B) {\n\tch := make(chan int)\n\to := NewObservable[int](ch)\n\tnum := 1000\n\n\tsubs := []Subscription[int]{}\n\tfor i := 0; i < num; i++ {\n\t\tsub, _ := o.Subscribe()\n\t\tsubs = append(subs, sub)\n\t}\n\n\twg := sync.WaitGroup{}\n\twg.Add(num)\n\n\tb.ResetTimer()\n\tfor _, sub := range subs {\n\t\tgo func(s Subscription[int]) {\n\t\t\tfor range s {\n\t\t\t}\n\t\t\twg.Done()\n\t\t}(sub)\n\t}\n\n\tfor i := 0; i < b.N; i++ {\n\t\tch <- i\n\t}\n\n\tclose(ch)\n\twg.Wait()\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/observable/subscriber.go",
    "content": "package observable\n\nimport (\n\t\"sync\"\n)\n\ntype Subscription[T any] <-chan T\n\ntype Subscriber[T any] struct {\n\tbuffer chan T\n\tonce   sync.Once\n}\n\nfunc (s *Subscriber[T]) Emit(item T) {\n\ts.buffer <- item\n}\n\nfunc (s *Subscriber[T]) Out() Subscription[T] {\n\treturn s.buffer\n}\n\nfunc (s *Subscriber[T]) Close() {\n\ts.once.Do(func() {\n\t\tclose(s.buffer)\n\t})\n}\n\nfunc newSubscriber[T any]() *Subscriber[T] {\n\tsub := &Subscriber[T]{\n\t\tbuffer: make(chan T, 200),\n\t}\n\treturn sub\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/once/once_go120.go",
    "content": "//go:build !go1.22\n\npackage once\n\nimport (\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"unsafe\"\n)\n\ntype Once struct {\n\tdone uint32\n\tm    sync.Mutex\n}\n\nfunc Done(once *sync.Once) bool {\n\t// atomic visit sync.Once.done\n\treturn atomic.LoadUint32((*uint32)(unsafe.Pointer(once))) == 1\n}\n\nfunc Reset(once *sync.Once) {\n\to := (*Once)(unsafe.Pointer(once))\n\to.m.Lock()\n\tdefer o.m.Unlock()\n\tatomic.StoreUint32(&o.done, 0)\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/once/once_go122.go",
    "content": "//go:build go1.22\n\npackage once\n\nimport (\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"unsafe\"\n)\n\ntype Once struct {\n\tdone atomic.Uint32\n\tm    sync.Mutex\n}\n\nfunc Done(once *sync.Once) bool {\n\t// atomic visit sync.Once.done\n\treturn (*atomic.Uint32)(unsafe.Pointer(once)).Load() == 1\n}\n\nfunc Reset(once *sync.Once) {\n\to := (*Once)(unsafe.Pointer(once))\n\to.m.Lock()\n\tdefer o.m.Unlock()\n\to.done.Store(0)\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/once/oncefunc.go",
    "content": "// Copyright 2022 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\npackage once\n\nimport \"sync\"\n\n// OnceFunc returns a function that invokes f only once. The returned function\n// may be called concurrently.\n//\n// If f panics, the returned function will panic with the same value on every call.\nfunc OnceFunc(f func()) func() {\n\tvar (\n\t\tonce  sync.Once\n\t\tvalid bool\n\t\tp     any\n\t)\n\t// Construct the inner closure just once to reduce costs on the fast path.\n\tg := func() {\n\t\tdefer func() {\n\t\t\tp = recover()\n\t\t\tif !valid {\n\t\t\t\t// Re-panic immediately so on the first call the user gets a\n\t\t\t\t// complete stack trace into f.\n\t\t\t\tpanic(p)\n\t\t\t}\n\t\t}()\n\t\tf()\n\t\tf = nil      // Do not keep f alive after invoking it.\n\t\tvalid = true // Set only if f does not panic.\n\t}\n\treturn func() {\n\t\tonce.Do(g)\n\t\tif !valid {\n\t\t\tpanic(p)\n\t\t}\n\t}\n}\n\n// OnceValue returns a function that invokes f only once and returns the value\n// returned by f. The returned function may be called concurrently.\n//\n// If f panics, the returned function will panic with the same value on every call.\nfunc OnceValue[T any](f func() T) func() T {\n\tvar (\n\t\tonce   sync.Once\n\t\tvalid  bool\n\t\tp      any\n\t\tresult T\n\t)\n\tg := func() {\n\t\tdefer func() {\n\t\t\tp = recover()\n\t\t\tif !valid {\n\t\t\t\tpanic(p)\n\t\t\t}\n\t\t}()\n\t\tresult = f()\n\t\tf = nil\n\t\tvalid = true\n\t}\n\treturn func() T {\n\t\tonce.Do(g)\n\t\tif !valid {\n\t\t\tpanic(p)\n\t\t}\n\t\treturn result\n\t}\n}\n\n// OnceValues returns a function that invokes f only once and returns the values\n// returned by f. The returned function may be called concurrently.\n//\n// If f panics, the returned function will panic with the same value on every call.\nfunc OnceValues[T1, T2 any](f func() (T1, T2)) func() (T1, T2) {\n\tvar (\n\t\tonce  sync.Once\n\t\tvalid bool\n\t\tp     any\n\t\tr1    T1\n\t\tr2    T2\n\t)\n\tg := func() {\n\t\tdefer func() {\n\t\t\tp = recover()\n\t\t\tif !valid {\n\t\t\t\tpanic(p)\n\t\t\t}\n\t\t}()\n\t\tr1, r2 = f()\n\t\tf = nil\n\t\tvalid = true\n\t}\n\treturn func() (T1, T2) {\n\t\tonce.Do(g)\n\t\tif !valid {\n\t\t\tpanic(p)\n\t\t}\n\t\treturn r1, r2\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/orderedmap/doc.go",
    "content": "package orderedmap\n\n// copy and modified from https://github.com/wk8/go-ordered-map/tree/v2.1.8\n// which is licensed under Apache v2.\n//\n// mihomo modified:\n// 1. remove dependence of mailru/easyjson for MarshalJSON\n// 2. remove dependence of buger/jsonparser for UnmarshalJSON\n"
  },
  {
    "path": "core/Clash.Meta/common/orderedmap/json.go",
    "content": "package orderedmap\n\nimport (\n\t\"bytes\"\n\t\"encoding\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n)\n\nvar (\n\t_ json.Marshaler   = &OrderedMap[int, any]{}\n\t_ json.Unmarshaler = &OrderedMap[int, any]{}\n)\n\n// MarshalJSON implements the json.Marshaler interface.\nfunc (om *OrderedMap[K, V]) MarshalJSON() ([]byte, error) { //nolint:funlen\n\tif om == nil || om.list == nil {\n\t\treturn []byte(\"null\"), nil\n\t}\n\n\tvar buf bytes.Buffer\n\tbuf.WriteByte('{')\n\tenc := json.NewEncoder(&buf)\n\tfor pair, firstIteration := om.Oldest(), true; pair != nil; pair = pair.Next() {\n\t\tif firstIteration {\n\t\t\tfirstIteration = false\n\t\t} else {\n\t\t\tbuf.WriteByte(',')\n\t\t}\n\n\t\tswitch key := any(pair.Key).(type) {\n\t\tcase string, encoding.TextMarshaler:\n\t\t\tif err := enc.Encode(pair.Key); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tcase int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64:\n\t\t\tbuf.WriteByte('\"')\n\t\t\tbuf.WriteString(fmt.Sprint(key))\n\t\t\tbuf.WriteByte('\"')\n\t\tdefault:\n\t\t\t// this switch takes care of wrapper types around primitive types, such as\n\t\t\t// type myType string\n\t\t\tswitch keyValue := reflect.ValueOf(key); keyValue.Type().Kind() {\n\t\t\tcase reflect.String:\n\t\t\t\tif err := enc.Encode(pair.Key); err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\tcase reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,\n\t\t\t\treflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:\n\t\t\t\tbuf.WriteByte('\"')\n\t\t\t\tbuf.WriteString(fmt.Sprint(key))\n\t\t\t\tbuf.WriteByte('\"')\n\t\t\tdefault:\n\t\t\t\treturn nil, fmt.Errorf(\"unsupported key type: %T\", key)\n\t\t\t}\n\t\t}\n\n\t\tbuf.WriteByte(':')\n\t\tif err := enc.Encode(pair.Value); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tbuf.WriteByte('}')\n\treturn buf.Bytes(), nil\n}\n\n// UnmarshalJSON implements the json.Unmarshaler interface.\nfunc (om *OrderedMap[K, V]) UnmarshalJSON(data []byte) error {\n\tif om.list == nil {\n\t\tom.initialize(0)\n\t}\n\n\td := json.NewDecoder(bytes.NewReader(data))\n\ttok, err := d.Token()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif tok != json.Delim('{') {\n\t\treturn errors.New(\"expect JSON object open with '{'\")\n\t}\n\n\tfor d.More() {\n\t\t// key\n\t\ttok, err = d.Token()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tkeyStr, ok := tok.(string)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"key must be a string, got %T\\n\", tok)\n\t\t}\n\n\t\tvar key K\n\t\tswitch typedKey := any(&key).(type) {\n\t\tcase *string:\n\t\t\t*typedKey = keyStr\n\t\tcase encoding.TextUnmarshaler:\n\t\t\terr = typedKey.UnmarshalText([]byte(keyStr))\n\t\tcase *int, *int8, *int16, *int32, *int64, *uint, *uint8, *uint16, *uint32, *uint64:\n\t\t\terr = json.Unmarshal([]byte(keyStr), typedKey)\n\t\tdefault:\n\t\t\t// this switch takes care of wrapper types around primitive types, such as\n\t\t\t// type myType string\n\t\t\tswitch reflect.TypeOf(key).Kind() {\n\t\t\tcase reflect.String:\n\t\t\t\tconvertedKeyData := reflect.ValueOf(keyStr).Convert(reflect.TypeOf(key))\n\t\t\t\treflect.ValueOf(&key).Elem().Set(convertedKeyData)\n\t\t\tcase reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,\n\t\t\t\treflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:\n\t\t\t\terr = json.Unmarshal([]byte(keyStr), &key)\n\t\t\tdefault:\n\t\t\t\terr = fmt.Errorf(\"unsupported key type: %T\", key)\n\t\t\t}\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// value\n\t\tvalue, _ := om.Get(key)\n\t\terr = d.Decode(&value)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tom.Set(key, value)\n\t}\n\n\ttok, err = d.Token()\n\tif err != nil {\n\t\treturn err\n\t}\n\tif tok != json.Delim('}') {\n\t\treturn errors.New(\"expect JSON object close with '}'\")\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/orderedmap/json_fuzz_test.go",
    "content": "package orderedmap\n\n// Adapted from https://github.com/dvyukov/go-fuzz-corpus/blob/c42c1b2/json/json.go\n\nimport (\n\t\"encoding/json\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc FuzzRoundTripJSON(f *testing.F) {\n\tf.Fuzz(func(t *testing.T, data []byte) {\n\t\tfor _, testCase := range []struct {\n\t\t\tname        string\n\t\t\tconstructor func() any\n\t\t\t// should be a function that asserts that 2 objects of the type returned by constructor are equal\n\t\t\tequalityAssertion func(*testing.T, any, any) bool\n\t\t}{\n\t\t\t{\n\t\t\t\tname:              \"with a string -> string map\",\n\t\t\t\tconstructor:       func() any { return &OrderedMap[string, string]{} },\n\t\t\t\tequalityAssertion: assertOrderedMapsEqual[string, string],\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:              \"with a string -> int map\",\n\t\t\t\tconstructor:       func() any { return &OrderedMap[string, int]{} },\n\t\t\t\tequalityAssertion: assertOrderedMapsEqual[string, int],\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:              \"with a string -> any map\",\n\t\t\t\tconstructor:       func() any { return &OrderedMap[string, any]{} },\n\t\t\t\tequalityAssertion: assertOrderedMapsEqual[string, any],\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:              \"with a struct with map fields\",\n\t\t\t\tconstructor:       func() any { return new(testFuzzStruct) },\n\t\t\t\tequalityAssertion: assertTestFuzzStructEqual,\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(testCase.name, func(t *testing.T) {\n\t\t\t\tv1 := testCase.constructor()\n\t\t\t\tif json.Unmarshal(data, v1) != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tjsonData, err := json.Marshal(v1)\n\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\tv2 := testCase.constructor()\n\t\t\t\trequire.NoError(t, json.Unmarshal(jsonData, v2))\n\n\t\t\t\tif !assert.True(t, testCase.equalityAssertion(t, v1, v2), \"failed with input data %q\", string(data)) {\n\t\t\t\t\t// look at that what the standard lib does with regular map, to help with debugging\n\n\t\t\t\t\tvar m1 map[string]any\n\t\t\t\t\trequire.NoError(t, json.Unmarshal(data, &m1))\n\n\t\t\t\t\tmapJsonData, err := json.Marshal(m1)\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\tvar m2 map[string]any\n\t\t\t\t\trequire.NoError(t, json.Unmarshal(mapJsonData, &m2))\n\n\t\t\t\t\tt.Logf(\"initial data = %s\", string(data))\n\t\t\t\t\tt.Logf(\"unmarshalled map = %v\", m1)\n\t\t\t\t\tt.Logf(\"re-marshalled from map = %s\", string(mapJsonData))\n\t\t\t\t\tt.Logf(\"re-marshalled from test obj = %s\", string(jsonData))\n\t\t\t\t\tt.Logf(\"re-unmarshalled map = %s\", m2)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n}\n\n// only works for fairly basic maps, that's why it's just in this file\nfunc assertOrderedMapsEqual[K comparable, V any](t *testing.T, v1, v2 any) bool {\n\tom1, ok1 := v1.(*OrderedMap[K, V])\n\tom2, ok2 := v2.(*OrderedMap[K, V])\n\n\tif !assert.True(t, ok1, \"v1 not an orderedmap\") ||\n\t\t!assert.True(t, ok2, \"v2 not an orderedmap\") {\n\t\treturn false\n\t}\n\n\tsuccess := assert.Equal(t, om1.Len(), om2.Len(), \"om1 and om2 have different lengths: %d vs %d\", om1.Len(), om2.Len())\n\n\tfor i, pair1, pair2 := 0, om1.Oldest(), om2.Oldest(); pair1 != nil && pair2 != nil; i, pair1, pair2 = i+1, pair1.Next(), pair2.Next() {\n\t\tsuccess = assert.Equal(t, pair1.Key, pair2.Key, \"different keys at position %d: %v vs %v\", i, pair1.Key, pair2.Key) && success\n\t\tsuccess = assert.Equal(t, pair1.Value, pair2.Value, \"different values at position %d: %v vs %v\", i, pair1.Value, pair2.Value) && success\n\t}\n\n\treturn success\n}\n\ntype testFuzzStruct struct {\n\tM1 *OrderedMap[int, any]\n\tM2 *OrderedMap[int, string]\n\tM3 *OrderedMap[string, string]\n}\n\nfunc assertTestFuzzStructEqual(t *testing.T, v1, v2 any) bool {\n\ts1, ok := v1.(*testFuzzStruct)\n\ts2, ok := v2.(*testFuzzStruct)\n\n\tif !assert.True(t, ok, \"v1 not an testFuzzStruct\") ||\n\t\t!assert.True(t, ok, \"v2 not an testFuzzStruct\") {\n\t\treturn false\n\t}\n\n\tsuccess := assertOrderedMapsEqual[int, any](t, s1.M1, s2.M1)\n\tsuccess = assertOrderedMapsEqual[int, string](t, s1.M2, s2.M2) && success\n\tsuccess = assertOrderedMapsEqual[string, string](t, s1.M3, s2.M3) && success\n\n\treturn success\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/orderedmap/json_test.go",
    "content": "package orderedmap\n\nimport (\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\n// to test marshalling TextMarshalers and unmarshalling TextUnmarshalers\ntype marshallable int\n\nfunc (m marshallable) MarshalText() ([]byte, error) {\n\treturn []byte(fmt.Sprintf(\"#%d#\", m)), nil\n}\n\nfunc (m *marshallable) UnmarshalText(text []byte) error {\n\tif len(text) < 3 {\n\t\treturn errors.New(\"too short\")\n\t}\n\tif text[0] != '#' || text[len(text)-1] != '#' {\n\t\treturn errors.New(\"missing prefix or suffix\")\n\t}\n\n\tvalue, err := strconv.Atoi(string(text[1 : len(text)-1]))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t*m = marshallable(value)\n\treturn nil\n}\n\nfunc TestMarshalJSON(t *testing.T) {\n\tt.Run(\"int key\", func(t *testing.T) {\n\t\tom := New[int, any]()\n\t\tom.Set(1, \"bar\")\n\t\tom.Set(7, \"baz\")\n\t\tom.Set(2, 28)\n\t\tom.Set(3, 100)\n\t\tom.Set(4, \"baz\")\n\t\tom.Set(5, \"28\")\n\t\tom.Set(6, \"100\")\n\t\tom.Set(8, \"baz\")\n\t\tom.Set(8, \"baz\")\n\t\tom.Set(9, \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque auctor augue accumsan mi maximus, quis viverra massa pretium. Phasellus imperdiet sapien a interdum sollicitudin. Duis at commodo lectus, a lacinia sem.\")\n\n\t\tb, err := json.Marshal(om)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, `{\"1\":\"bar\",\"7\":\"baz\",\"2\":28,\"3\":100,\"4\":\"baz\",\"5\":\"28\",\"6\":\"100\",\"8\":\"baz\",\"9\":\"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque auctor augue accumsan mi maximus, quis viverra massa pretium. Phasellus imperdiet sapien a interdum sollicitudin. Duis at commodo lectus, a lacinia sem.\"}`, string(b))\n\t})\n\n\tt.Run(\"string key\", func(t *testing.T) {\n\t\tom := New[string, any]()\n\t\tom.Set(\"test\", \"bar\")\n\t\tom.Set(\"abc\", true)\n\n\t\tb, err := json.Marshal(om)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, `{\"test\":\"bar\",\"abc\":true}`, string(b))\n\t})\n\n\tt.Run(\"typed string key\", func(t *testing.T) {\n\t\ttype myString string\n\t\tom := New[myString, any]()\n\t\tom.Set(\"test\", \"bar\")\n\t\tom.Set(\"abc\", true)\n\n\t\tb, err := json.Marshal(om)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, `{\"test\":\"bar\",\"abc\":true}`, string(b))\n\t})\n\n\tt.Run(\"typed int key\", func(t *testing.T) {\n\t\ttype myInt uint32\n\t\tom := New[myInt, any]()\n\t\tom.Set(1, \"bar\")\n\t\tom.Set(7, \"baz\")\n\t\tom.Set(2, 28)\n\t\tom.Set(3, 100)\n\t\tom.Set(4, \"baz\")\n\n\t\tb, err := json.Marshal(om)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, `{\"1\":\"bar\",\"7\":\"baz\",\"2\":28,\"3\":100,\"4\":\"baz\"}`, string(b))\n\t})\n\n\tt.Run(\"TextMarshaller key\", func(t *testing.T) {\n\t\tom := New[marshallable, any]()\n\t\tom.Set(marshallable(1), \"bar\")\n\t\tom.Set(marshallable(28), true)\n\n\t\tb, err := json.Marshal(om)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, `{\"#1#\":\"bar\",\"#28#\":true}`, string(b))\n\t})\n\n\tt.Run(\"empty map\", func(t *testing.T) {\n\t\tom := New[string, any]()\n\n\t\tb, err := json.Marshal(om)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, `{}`, string(b))\n\t})\n}\n\nfunc TestUnmarshallJSON(t *testing.T) {\n\tt.Run(\"int key\", func(t *testing.T) {\n\t\tdata := `{\"1\":\"bar\",\"7\":\"baz\",\"2\":28,\"3\":100,\"4\":\"baz\",\"5\":\"28\",\"6\":\"100\",\"8\":\"baz\"}`\n\n\t\tom := New[int, any]()\n\t\trequire.NoError(t, json.Unmarshal([]byte(data), &om))\n\n\t\tassertOrderedPairsEqual(t, om,\n\t\t\t[]int{1, 7, 2, 3, 4, 5, 6, 8},\n\t\t\t[]any{\"bar\", \"baz\", float64(28), float64(100), \"baz\", \"28\", \"100\", \"baz\"})\n\t})\n\n\tt.Run(\"string key\", func(t *testing.T) {\n\t\tdata := `{\"test\":\"bar\",\"abc\":true}`\n\n\t\tom := New[string, any]()\n\t\trequire.NoError(t, json.Unmarshal([]byte(data), &om))\n\n\t\tassertOrderedPairsEqual(t, om,\n\t\t\t[]string{\"test\", \"abc\"},\n\t\t\t[]any{\"bar\", true})\n\t})\n\n\tt.Run(\"typed string key\", func(t *testing.T) {\n\t\tdata := `{\"test\":\"bar\",\"abc\":true}`\n\n\t\ttype myString string\n\t\tom := New[myString, any]()\n\t\trequire.NoError(t, json.Unmarshal([]byte(data), &om))\n\n\t\tassertOrderedPairsEqual(t, om,\n\t\t\t[]myString{\"test\", \"abc\"},\n\t\t\t[]any{\"bar\", true})\n\t})\n\n\tt.Run(\"typed int key\", func(t *testing.T) {\n\t\tdata := `{\"1\":\"bar\",\"7\":\"baz\",\"2\":28,\"3\":100,\"4\":\"baz\",\"5\":\"28\",\"6\":\"100\",\"8\":\"baz\"}`\n\n\t\ttype myInt uint32\n\t\tom := New[myInt, any]()\n\t\trequire.NoError(t, json.Unmarshal([]byte(data), &om))\n\n\t\tassertOrderedPairsEqual(t, om,\n\t\t\t[]myInt{1, 7, 2, 3, 4, 5, 6, 8},\n\t\t\t[]any{\"bar\", \"baz\", float64(28), float64(100), \"baz\", \"28\", \"100\", \"baz\"})\n\t})\n\n\tt.Run(\"TextUnmarshaler key\", func(t *testing.T) {\n\t\tdata := `{\"#1#\":\"bar\",\"#28#\":true}`\n\n\t\tom := New[marshallable, any]()\n\t\trequire.NoError(t, json.Unmarshal([]byte(data), &om))\n\n\t\tassertOrderedPairsEqual(t, om,\n\t\t\t[]marshallable{1, 28},\n\t\t\t[]any{\"bar\", true})\n\t})\n\n\tt.Run(\"when fed with an input that's not an object\", func(t *testing.T) {\n\t\tfor _, data := range []string{\"true\", `[\"foo\"]`, \"42\", `\"foo\"`} {\n\t\t\tom := New[int, any]()\n\t\t\trequire.Error(t, json.Unmarshal([]byte(data), &om))\n\t\t}\n\t})\n\n\tt.Run(\"empty map\", func(t *testing.T) {\n\t\tdata := `{}`\n\n\t\tom := New[int, any]()\n\t\trequire.NoError(t, json.Unmarshal([]byte(data), &om))\n\n\t\tassertLenEqual(t, om, 0)\n\t})\n}\n\n// const specialCharacters = \"\\\\\\\\/\\\"\\b\\f\\n\\r\\t\\x00\\uffff\\ufffd世界\\u007f\\u00ff\\U0010FFFF\"\nconst specialCharacters = \"\\uffff\\ufffd世界\\u007f\\u00ff\\U0010FFFF\"\n\nfunc TestJSONSpecialCharacters(t *testing.T) {\n\tbaselineMap := map[string]any{specialCharacters: specialCharacters}\n\tbaselineData, err := json.Marshal(baselineMap)\n\trequire.NoError(t, err) // baseline proves this key is supported by official json library\n\tt.Logf(\"specialCharacters: %#v as []rune:%v\", specialCharacters, []rune(specialCharacters))\n\tt.Logf(\"baseline json data: %s\", baselineData)\n\n\tt.Run(\"marshal special characters\", func(t *testing.T) {\n\t\tom := New[string, any]()\n\t\tom.Set(specialCharacters, specialCharacters)\n\t\tb, err := json.Marshal(om)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, baselineData, b)\n\n\t\ttype myString string\n\t\tom2 := New[myString, myString]()\n\t\tom2.Set(specialCharacters, specialCharacters)\n\t\tb, err = json.Marshal(om2)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, baselineData, b)\n\t})\n\n\tt.Run(\"unmarshall special characters\", func(t *testing.T) {\n\t\tom := New[string, any]()\n\t\trequire.NoError(t, json.Unmarshal(baselineData, &om))\n\t\tassertOrderedPairsEqual(t, om,\n\t\t\t[]string{specialCharacters},\n\t\t\t[]any{specialCharacters})\n\n\t\ttype myString string\n\t\tom2 := New[myString, myString]()\n\t\trequire.NoError(t, json.Unmarshal(baselineData, &om2))\n\t\tassertOrderedPairsEqual(t, om2,\n\t\t\t[]myString{specialCharacters},\n\t\t\t[]myString{specialCharacters})\n\t})\n}\n\n// to test structs that have nested map fields\ntype nestedMaps struct {\n\tX int                                                               `json:\"x\" yaml:\"x\"`\n\tM *OrderedMap[string, []*OrderedMap[int, *OrderedMap[string, any]]] `json:\"m\" yaml:\"m\"`\n}\n\nfunc TestJSONRoundTrip(t *testing.T) {\n\tfor _, testCase := range []struct {\n\t\tname            string\n\t\tinput           string\n\t\ttargetFactory   func() any\n\t\tisPrettyPrinted bool\n\t}{\n\t\t{\n\t\t\tname: \"\",\n\t\t\tinput: `{\n    \"x\": 28,\n    \"m\": {\n        \"foo\": [\n            {\n                \"12\": {\n                    \"i\": 12,\n                    \"b\": true,\n                    \"n\": null,\n                    \"m\": {\n                        \"a\": \"b\",\n                        \"c\": 28\n                    }\n                },\n                \"28\": {\n                    \"a\": false,\n                    \"b\": [\n                        1,\n                        2,\n                        3\n                    ]\n                }\n            },\n            {\n                \"3\": {\n                    \"c\": null,\n                    \"d\": 87\n                },\n                \"4\": {\n                    \"e\": true\n                },\n                \"5\": {\n                    \"f\": 4,\n                    \"g\": 5,\n                    \"h\": 6\n                }\n            }\n        ],\n        \"bar\": [\n            {\n                \"5\": {\n                    \"foo\": \"bar\"\n                }\n            }\n        ]\n    }\n}`,\n\t\t\ttargetFactory:   func() any { return &nestedMaps{} },\n\t\t\tisPrettyPrinted: true,\n\t\t},\n\t\t{\n\t\t\tname:          \"with UTF-8 special chars in key\",\n\t\t\tinput:         `{\"�\":0}`,\n\t\t\ttargetFactory: func() any { return &OrderedMap[string, int]{} },\n\t\t},\n\t} {\n\t\tt.Run(testCase.name, func(t *testing.T) {\n\t\t\ttarget := testCase.targetFactory()\n\n\t\t\trequire.NoError(t, json.Unmarshal([]byte(testCase.input), target))\n\n\t\t\tvar (\n\t\t\t\tout []byte\n\t\t\t\terr error\n\t\t\t)\n\t\t\tif testCase.isPrettyPrinted {\n\t\t\t\tout, err = json.MarshalIndent(target, \"\", \"    \")\n\t\t\t} else {\n\t\t\t\tout, err = json.Marshal(target)\n\t\t\t}\n\n\t\t\tif assert.NoError(t, err) {\n\t\t\t\tassert.Equal(t, strings.TrimSpace(testCase.input), string(out))\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkMarshalJSON(b *testing.B) {\n\tom := New[int, any]()\n\tom.Set(1, \"bar\")\n\tom.Set(7, \"baz\")\n\tom.Set(2, 28)\n\tom.Set(3, 100)\n\tom.Set(4, \"baz\")\n\tom.Set(5, \"28\")\n\tom.Set(6, \"100\")\n\tom.Set(8, \"baz\")\n\tom.Set(8, \"baz\")\n\n\tb.ResetTimer()\n\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _ = json.Marshal(om)\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/orderedmap/orderedmap.go",
    "content": "// Package orderedmap implements an ordered map, i.e. a map that also keeps track of\n// the order in which keys were inserted.\n//\n// All operations are constant-time.\n//\n// Github repo: https://github.com/wk8/go-ordered-map\npackage orderedmap\n\nimport (\n\t\"fmt\"\n\n\tlist \"github.com/bahlo/generic-list-go\"\n)\n\ntype Pair[K comparable, V any] struct {\n\tKey   K\n\tValue V\n\n\telement *list.Element[*Pair[K, V]]\n}\n\ntype OrderedMap[K comparable, V any] struct {\n\tpairs map[K]*Pair[K, V]\n\tlist  *list.List[*Pair[K, V]]\n}\n\ntype initConfig[K comparable, V any] struct {\n\tcapacity    int\n\tinitialData []Pair[K, V]\n}\n\ntype InitOption[K comparable, V any] func(config *initConfig[K, V])\n\n// WithCapacity allows giving a capacity hint for the map, akin to the standard make(map[K]V, capacity).\nfunc WithCapacity[K comparable, V any](capacity int) InitOption[K, V] {\n\treturn func(c *initConfig[K, V]) {\n\t\tc.capacity = capacity\n\t}\n}\n\n// WithInitialData allows passing in initial data for the map.\nfunc WithInitialData[K comparable, V any](initialData ...Pair[K, V]) InitOption[K, V] {\n\treturn func(c *initConfig[K, V]) {\n\t\tc.initialData = initialData\n\t\tif c.capacity < len(initialData) {\n\t\t\tc.capacity = len(initialData)\n\t\t}\n\t}\n}\n\n// New creates a new OrderedMap.\n// options can either be one or several InitOption[K, V], or a single integer,\n// which is then interpreted as a capacity hint, à la make(map[K]V, capacity).\nfunc New[K comparable, V any](options ...any) *OrderedMap[K, V] { //nolint:varnamelen\n\torderedMap := &OrderedMap[K, V]{}\n\n\tvar config initConfig[K, V]\n\tfor _, untypedOption := range options {\n\t\tswitch option := untypedOption.(type) {\n\t\tcase int:\n\t\t\tif len(options) != 1 {\n\t\t\t\tinvalidOption()\n\t\t\t}\n\t\t\tconfig.capacity = option\n\n\t\tcase InitOption[K, V]:\n\t\t\toption(&config)\n\n\t\tdefault:\n\t\t\tinvalidOption()\n\t\t}\n\t}\n\n\torderedMap.initialize(config.capacity)\n\torderedMap.AddPairs(config.initialData...)\n\n\treturn orderedMap\n}\n\nconst invalidOptionMessage = `when using orderedmap.New[K,V]() with options, either provide one or several InitOption[K, V]; or a single integer which is then interpreted as a capacity hint, à la make(map[K]V, capacity).` //nolint:lll\n\nfunc invalidOption() { panic(invalidOptionMessage) }\n\nfunc (om *OrderedMap[K, V]) initialize(capacity int) {\n\tom.pairs = make(map[K]*Pair[K, V], capacity)\n\tom.list = list.New[*Pair[K, V]]()\n}\n\n// Get looks for the given key, and returns the value associated with it,\n// or V's nil value if not found. The boolean it returns says whether the key is present in the map.\nfunc (om *OrderedMap[K, V]) Get(key K) (val V, present bool) {\n\tif pair, present := om.pairs[key]; present {\n\t\treturn pair.Value, true\n\t}\n\n\treturn\n}\n\n// Load is an alias for Get, mostly to present an API similar to `sync.Map`'s.\nfunc (om *OrderedMap[K, V]) Load(key K) (V, bool) {\n\treturn om.Get(key)\n}\n\n// Value returns the value associated with the given key or the zero value.\nfunc (om *OrderedMap[K, V]) Value(key K) (val V) {\n\tif pair, present := om.pairs[key]; present {\n\t\tval = pair.Value\n\t}\n\treturn\n}\n\n// GetPair looks for the given key, and returns the pair associated with it,\n// or nil if not found. The Pair struct can then be used to iterate over the ordered map\n// from that point, either forward or backward.\nfunc (om *OrderedMap[K, V]) GetPair(key K) *Pair[K, V] {\n\treturn om.pairs[key]\n}\n\n// Set sets the key-value pair, and returns what `Get` would have returned\n// on that key prior to the call to `Set`.\nfunc (om *OrderedMap[K, V]) Set(key K, value V) (val V, present bool) {\n\tif pair, present := om.pairs[key]; present {\n\t\toldValue := pair.Value\n\t\tpair.Value = value\n\t\treturn oldValue, true\n\t}\n\n\tpair := &Pair[K, V]{\n\t\tKey:   key,\n\t\tValue: value,\n\t}\n\tpair.element = om.list.PushBack(pair)\n\tom.pairs[key] = pair\n\n\treturn\n}\n\n// AddPairs allows setting multiple pairs at a time. It's equivalent to calling\n// Set on each pair sequentially.\nfunc (om *OrderedMap[K, V]) AddPairs(pairs ...Pair[K, V]) {\n\tfor _, pair := range pairs {\n\t\tom.Set(pair.Key, pair.Value)\n\t}\n}\n\n// Store is an alias for Set, mostly to present an API similar to `sync.Map`'s.\nfunc (om *OrderedMap[K, V]) Store(key K, value V) (V, bool) {\n\treturn om.Set(key, value)\n}\n\n// Delete removes the key-value pair, and returns what `Get` would have returned\n// on that key prior to the call to `Delete`.\nfunc (om *OrderedMap[K, V]) Delete(key K) (val V, present bool) {\n\tif pair, present := om.pairs[key]; present {\n\t\tom.list.Remove(pair.element)\n\t\tdelete(om.pairs, key)\n\t\treturn pair.Value, true\n\t}\n\treturn\n}\n\n// Len returns the length of the ordered map.\nfunc (om *OrderedMap[K, V]) Len() int {\n\tif om == nil || om.pairs == nil {\n\t\treturn 0\n\t}\n\treturn len(om.pairs)\n}\n\n// Oldest returns a pointer to the oldest pair. It's meant to be used to iterate on the ordered map's\n// pairs from the oldest to the newest, e.g.:\n// for pair := orderedMap.Oldest(); pair != nil; pair = pair.Next() { fmt.Printf(\"%v => %v\\n\", pair.Key, pair.Value) }\nfunc (om *OrderedMap[K, V]) Oldest() *Pair[K, V] {\n\tif om == nil || om.list == nil {\n\t\treturn nil\n\t}\n\treturn listElementToPair(om.list.Front())\n}\n\n// Newest returns a pointer to the newest pair. It's meant to be used to iterate on the ordered map's\n// pairs from the newest to the oldest, e.g.:\n// for pair := orderedMap.Oldest(); pair != nil; pair = pair.Next() { fmt.Printf(\"%v => %v\\n\", pair.Key, pair.Value) }\nfunc (om *OrderedMap[K, V]) Newest() *Pair[K, V] {\n\tif om == nil || om.list == nil {\n\t\treturn nil\n\t}\n\treturn listElementToPair(om.list.Back())\n}\n\n// Next returns a pointer to the next pair.\nfunc (p *Pair[K, V]) Next() *Pair[K, V] {\n\treturn listElementToPair(p.element.Next())\n}\n\n// Prev returns a pointer to the previous pair.\nfunc (p *Pair[K, V]) Prev() *Pair[K, V] {\n\treturn listElementToPair(p.element.Prev())\n}\n\nfunc listElementToPair[K comparable, V any](element *list.Element[*Pair[K, V]]) *Pair[K, V] {\n\tif element == nil {\n\t\treturn nil\n\t}\n\treturn element.Value\n}\n\n// KeyNotFoundError may be returned by functions in this package when they're called with keys that are not present\n// in the map.\ntype KeyNotFoundError[K comparable] struct {\n\tMissingKey K\n}\n\nfunc (e *KeyNotFoundError[K]) Error() string {\n\treturn fmt.Sprintf(\"missing key: %v\", e.MissingKey)\n}\n\n// MoveAfter moves the value associated with key to its new position after the one associated with markKey.\n// Returns an error iff key or markKey are not present in the map. If an error is returned,\n// it will be a KeyNotFoundError.\nfunc (om *OrderedMap[K, V]) MoveAfter(key, markKey K) error {\n\telements, err := om.getElements(key, markKey)\n\tif err != nil {\n\t\treturn err\n\t}\n\tom.list.MoveAfter(elements[0], elements[1])\n\treturn nil\n}\n\n// MoveBefore moves the value associated with key to its new position before the one associated with markKey.\n// Returns an error iff key or markKey are not present in the map. If an error is returned,\n// it will be a KeyNotFoundError.\nfunc (om *OrderedMap[K, V]) MoveBefore(key, markKey K) error {\n\telements, err := om.getElements(key, markKey)\n\tif err != nil {\n\t\treturn err\n\t}\n\tom.list.MoveBefore(elements[0], elements[1])\n\treturn nil\n}\n\nfunc (om *OrderedMap[K, V]) getElements(keys ...K) ([]*list.Element[*Pair[K, V]], error) {\n\telements := make([]*list.Element[*Pair[K, V]], len(keys))\n\tfor i, k := range keys {\n\t\tpair, present := om.pairs[k]\n\t\tif !present {\n\t\t\treturn nil, &KeyNotFoundError[K]{k}\n\t\t}\n\t\telements[i] = pair.element\n\t}\n\treturn elements, nil\n}\n\n// MoveToBack moves the value associated with key to the back of the ordered map,\n// i.e. makes it the newest pair in the map.\n// Returns an error iff key is not present in the map. If an error is returned,\n// it will be a KeyNotFoundError.\nfunc (om *OrderedMap[K, V]) MoveToBack(key K) error {\n\t_, err := om.GetAndMoveToBack(key)\n\treturn err\n}\n\n// MoveToFront moves the value associated with key to the front of the ordered map,\n// i.e. makes it the oldest pair in the map.\n// Returns an error iff key is not present in the map. If an error is returned,\n// it will be a KeyNotFoundError.\nfunc (om *OrderedMap[K, V]) MoveToFront(key K) error {\n\t_, err := om.GetAndMoveToFront(key)\n\treturn err\n}\n\n// GetAndMoveToBack combines Get and MoveToBack in the same call. If an error is returned,\n// it will be a KeyNotFoundError.\nfunc (om *OrderedMap[K, V]) GetAndMoveToBack(key K) (val V, err error) {\n\tif pair, present := om.pairs[key]; present {\n\t\tval = pair.Value\n\t\tom.list.MoveToBack(pair.element)\n\t} else {\n\t\terr = &KeyNotFoundError[K]{key}\n\t}\n\n\treturn\n}\n\n// GetAndMoveToFront combines Get and MoveToFront in the same call. If an error is returned,\n// it will be a KeyNotFoundError.\nfunc (om *OrderedMap[K, V]) GetAndMoveToFront(key K) (val V, err error) {\n\tif pair, present := om.pairs[key]; present {\n\t\tval = pair.Value\n\t\tom.list.MoveToFront(pair.element)\n\t} else {\n\t\terr = &KeyNotFoundError[K]{key}\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/orderedmap/orderedmap_test.go",
    "content": "package orderedmap\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestBasicFeatures(t *testing.T) {\n\tn := 100\n\tom := New[int, int]()\n\n\t// set(i, 2 * i)\n\tfor i := 0; i < n; i++ {\n\t\tassertLenEqual(t, om, i)\n\t\toldValue, present := om.Set(i, 2*i)\n\t\tassertLenEqual(t, om, i+1)\n\n\t\tassert.Equal(t, 0, oldValue)\n\t\tassert.False(t, present)\n\t}\n\n\t// get what we just set\n\tfor i := 0; i < n; i++ {\n\t\tvalue, present := om.Get(i)\n\n\t\tassert.Equal(t, 2*i, value)\n\t\tassert.Equal(t, value, om.Value(i))\n\t\tassert.True(t, present)\n\t}\n\n\t// get pairs of what we just set\n\tfor i := 0; i < n; i++ {\n\t\tpair := om.GetPair(i)\n\n\t\tassert.NotNil(t, pair)\n\t\tassert.Equal(t, 2*i, pair.Value)\n\t}\n\n\t// forward iteration\n\ti := 0\n\tfor pair := om.Oldest(); pair != nil; pair = pair.Next() {\n\t\tassert.Equal(t, i, pair.Key)\n\t\tassert.Equal(t, 2*i, pair.Value)\n\t\ti++\n\t}\n\t// backward iteration\n\ti = n - 1\n\tfor pair := om.Newest(); pair != nil; pair = pair.Prev() {\n\t\tassert.Equal(t, i, pair.Key)\n\t\tassert.Equal(t, 2*i, pair.Value)\n\t\ti--\n\t}\n\n\t// forward iteration starting from known key\n\ti = 42\n\tfor pair := om.GetPair(i); pair != nil; pair = pair.Next() {\n\t\tassert.Equal(t, i, pair.Key)\n\t\tassert.Equal(t, 2*i, pair.Value)\n\t\ti++\n\t}\n\n\t// double values for pairs with even keys\n\tfor j := 0; j < n/2; j++ {\n\t\ti = 2 * j\n\t\toldValue, present := om.Set(i, 4*i)\n\n\t\tassert.Equal(t, 2*i, oldValue)\n\t\tassert.True(t, present)\n\t}\n\t// and delete pairs with odd keys\n\tfor j := 0; j < n/2; j++ {\n\t\ti = 2*j + 1\n\t\tassertLenEqual(t, om, n-j)\n\t\tvalue, present := om.Delete(i)\n\t\tassertLenEqual(t, om, n-j-1)\n\n\t\tassert.Equal(t, 2*i, value)\n\t\tassert.True(t, present)\n\n\t\t// deleting again shouldn't change anything\n\t\tvalue, present = om.Delete(i)\n\t\tassertLenEqual(t, om, n-j-1)\n\t\tassert.Equal(t, 0, value)\n\t\tassert.False(t, present)\n\t}\n\n\t// get the whole range\n\tfor j := 0; j < n/2; j++ {\n\t\ti = 2 * j\n\t\tvalue, present := om.Get(i)\n\t\tassert.Equal(t, 4*i, value)\n\t\tassert.Equal(t, value, om.Value(i))\n\t\tassert.True(t, present)\n\n\t\ti = 2*j + 1\n\t\tvalue, present = om.Get(i)\n\t\tassert.Equal(t, 0, value)\n\t\tassert.Equal(t, value, om.Value(i))\n\t\tassert.False(t, present)\n\t}\n\n\t// check iterations again\n\ti = 0\n\tfor pair := om.Oldest(); pair != nil; pair = pair.Next() {\n\t\tassert.Equal(t, i, pair.Key)\n\t\tassert.Equal(t, 4*i, pair.Value)\n\t\ti += 2\n\t}\n\ti = 2 * ((n - 1) / 2)\n\tfor pair := om.Newest(); pair != nil; pair = pair.Prev() {\n\t\tassert.Equal(t, i, pair.Key)\n\t\tassert.Equal(t, 4*i, pair.Value)\n\t\ti -= 2\n\t}\n}\n\nfunc TestUpdatingDoesntChangePairsOrder(t *testing.T) {\n\tom := New[string, any]()\n\tom.Set(\"foo\", \"bar\")\n\tom.Set(\"wk\", 28)\n\tom.Set(\"po\", 100)\n\tom.Set(\"bar\", \"baz\")\n\n\toldValue, present := om.Set(\"po\", 102)\n\tassert.Equal(t, 100, oldValue)\n\tassert.True(t, present)\n\n\tassertOrderedPairsEqual(t, om,\n\t\t[]string{\"foo\", \"wk\", \"po\", \"bar\"},\n\t\t[]any{\"bar\", 28, 102, \"baz\"})\n}\n\nfunc TestDeletingAndReinsertingChangesPairsOrder(t *testing.T) {\n\tom := New[string, any]()\n\tom.Set(\"foo\", \"bar\")\n\tom.Set(\"wk\", 28)\n\tom.Set(\"po\", 100)\n\tom.Set(\"bar\", \"baz\")\n\n\t// delete a pair\n\toldValue, present := om.Delete(\"po\")\n\tassert.Equal(t, 100, oldValue)\n\tassert.True(t, present)\n\n\t// re-insert the same pair\n\toldValue, present = om.Set(\"po\", 100)\n\tassert.Nil(t, oldValue)\n\tassert.False(t, present)\n\n\tassertOrderedPairsEqual(t, om,\n\t\t[]string{\"foo\", \"wk\", \"bar\", \"po\"},\n\t\t[]any{\"bar\", 28, \"baz\", 100})\n}\n\nfunc TestEmptyMapOperations(t *testing.T) {\n\tom := New[string, any]()\n\n\toldValue, present := om.Get(\"foo\")\n\tassert.Nil(t, oldValue)\n\tassert.Nil(t, om.Value(\"foo\"))\n\tassert.False(t, present)\n\n\toldValue, present = om.Delete(\"bar\")\n\tassert.Nil(t, oldValue)\n\tassert.False(t, present)\n\n\tassertLenEqual(t, om, 0)\n\n\tassert.Nil(t, om.Oldest())\n\tassert.Nil(t, om.Newest())\n}\n\ntype dummyTestStruct struct {\n\tvalue string\n}\n\nfunc TestPackUnpackStructs(t *testing.T) {\n\tom := New[string, dummyTestStruct]()\n\tom.Set(\"foo\", dummyTestStruct{\"foo!\"})\n\tom.Set(\"bar\", dummyTestStruct{\"bar!\"})\n\n\tvalue, present := om.Get(\"foo\")\n\tassert.True(t, present)\n\tassert.Equal(t, value, om.Value(\"foo\"))\n\tif assert.NotNil(t, value) {\n\t\tassert.Equal(t, \"foo!\", value.value)\n\t}\n\n\tvalue, present = om.Set(\"bar\", dummyTestStruct{\"baz!\"})\n\tassert.True(t, present)\n\tif assert.NotNil(t, value) {\n\t\tassert.Equal(t, \"bar!\", value.value)\n\t}\n\n\tvalue, present = om.Get(\"bar\")\n\tassert.Equal(t, value, om.Value(\"bar\"))\n\tassert.True(t, present)\n\tif assert.NotNil(t, value) {\n\t\tassert.Equal(t, \"baz!\", value.value)\n\t}\n}\n\n// shamelessly stolen from https://github.com/python/cpython/blob/e19a91e45fd54a56e39c2d12e6aaf4757030507f/Lib/test/test_ordered_dict.py#L55-L61\nfunc TestShuffle(t *testing.T) {\n\tranLen := 100\n\n\tfor _, n := range []int{0, 10, 20, 100, 1000, 10000} {\n\t\tt.Run(fmt.Sprintf(\"shuffle test with %d items\", n), func(t *testing.T) {\n\t\t\tom := New[string, string]()\n\n\t\t\tkeys := make([]string, n)\n\t\t\tvalues := make([]string, n)\n\n\t\t\tfor i := 0; i < n; i++ {\n\t\t\t\t// we prefix with the number to ensure that we don't get any duplicates\n\t\t\t\tkeys[i] = fmt.Sprintf(\"%d_%s\", i, randomHexString(t, ranLen))\n\t\t\t\tvalues[i] = randomHexString(t, ranLen)\n\n\t\t\t\tvalue, present := om.Set(keys[i], values[i])\n\t\t\t\tassert.Equal(t, \"\", value)\n\t\t\t\tassert.False(t, present)\n\t\t\t}\n\n\t\t\tassertOrderedPairsEqual(t, om, keys, values)\n\t\t})\n\t}\n}\n\nfunc TestMove(t *testing.T) {\n\tom := New[int, any]()\n\tom.Set(1, \"bar\")\n\tom.Set(2, 28)\n\tom.Set(3, 100)\n\tom.Set(4, \"baz\")\n\tom.Set(5, \"28\")\n\tom.Set(6, \"100\")\n\tom.Set(7, \"baz\")\n\tom.Set(8, \"baz\")\n\n\terr := om.MoveAfter(2, 3)\n\tassert.Nil(t, err)\n\tassertOrderedPairsEqual(t, om,\n\t\t[]int{1, 3, 2, 4, 5, 6, 7, 8},\n\t\t[]any{\"bar\", 100, 28, \"baz\", \"28\", \"100\", \"baz\", \"baz\"})\n\n\terr = om.MoveBefore(6, 4)\n\tassert.Nil(t, err)\n\tassertOrderedPairsEqual(t, om,\n\t\t[]int{1, 3, 2, 6, 4, 5, 7, 8},\n\t\t[]any{\"bar\", 100, 28, \"100\", \"baz\", \"28\", \"baz\", \"baz\"})\n\n\terr = om.MoveToBack(3)\n\tassert.Nil(t, err)\n\tassertOrderedPairsEqual(t, om,\n\t\t[]int{1, 2, 6, 4, 5, 7, 8, 3},\n\t\t[]any{\"bar\", 28, \"100\", \"baz\", \"28\", \"baz\", \"baz\", 100})\n\n\terr = om.MoveToFront(5)\n\tassert.Nil(t, err)\n\tassertOrderedPairsEqual(t, om,\n\t\t[]int{5, 1, 2, 6, 4, 7, 8, 3},\n\t\t[]any{\"28\", \"bar\", 28, \"100\", \"baz\", \"baz\", \"baz\", 100})\n\n\terr = om.MoveToFront(100)\n\tassert.Equal(t, &KeyNotFoundError[int]{100}, err)\n}\n\nfunc TestGetAndMove(t *testing.T) {\n\tom := New[int, any]()\n\tom.Set(1, \"bar\")\n\tom.Set(2, 28)\n\tom.Set(3, 100)\n\tom.Set(4, \"baz\")\n\tom.Set(5, \"28\")\n\tom.Set(6, \"100\")\n\tom.Set(7, \"baz\")\n\tom.Set(8, \"baz\")\n\n\tvalue, err := om.GetAndMoveToBack(3)\n\tassert.Nil(t, err)\n\tassert.Equal(t, 100, value)\n\tassertOrderedPairsEqual(t, om,\n\t\t[]int{1, 2, 4, 5, 6, 7, 8, 3},\n\t\t[]any{\"bar\", 28, \"baz\", \"28\", \"100\", \"baz\", \"baz\", 100})\n\n\tvalue, err = om.GetAndMoveToFront(5)\n\tassert.Nil(t, err)\n\tassert.Equal(t, \"28\", value)\n\tassertOrderedPairsEqual(t, om,\n\t\t[]int{5, 1, 2, 4, 6, 7, 8, 3},\n\t\t[]any{\"28\", \"bar\", 28, \"baz\", \"100\", \"baz\", \"baz\", 100})\n\n\tvalue, err = om.GetAndMoveToBack(100)\n\tassert.Equal(t, &KeyNotFoundError[int]{100}, err)\n}\n\nfunc TestAddPairs(t *testing.T) {\n\tom := New[int, any]()\n\tom.AddPairs(\n\t\tPair[int, any]{\n\t\t\tKey:   28,\n\t\t\tValue: \"foo\",\n\t\t},\n\t\tPair[int, any]{\n\t\t\tKey:   12,\n\t\t\tValue: \"bar\",\n\t\t},\n\t\tPair[int, any]{\n\t\t\tKey:   28,\n\t\t\tValue: \"baz\",\n\t\t},\n\t)\n\n\tassertOrderedPairsEqual(t, om,\n\t\t[]int{28, 12},\n\t\t[]any{\"baz\", \"bar\"})\n}\n\n// sadly, we can't test the \"actual\" capacity here, see https://github.com/golang/go/issues/52157\nfunc TestNewWithCapacity(t *testing.T) {\n\tzero := New[int, string](0)\n\tassert.Empty(t, zero.Len())\n\n\tassert.PanicsWithValue(t, invalidOptionMessage, func() {\n\t\t_ = New[int, string](1, 2)\n\t})\n\tassert.PanicsWithValue(t, invalidOptionMessage, func() {\n\t\t_ = New[int, string](1, 2, 3)\n\t})\n\n\tom := New[int, string](-1)\n\tom.Set(1337, \"quarante-deux\")\n\tassert.Equal(t, 1, om.Len())\n}\n\nfunc TestNewWithOptions(t *testing.T) {\n\tt.Run(\"wih capacity\", func(t *testing.T) {\n\t\tom := New[string, any](WithCapacity[string, any](98))\n\t\tassert.Equal(t, 0, om.Len())\n\t})\n\n\tt.Run(\"with initial data\", func(t *testing.T) {\n\t\tom := New[string, int](WithInitialData(\n\t\t\tPair[string, int]{\n\t\t\t\tKey:   \"a\",\n\t\t\t\tValue: 1,\n\t\t\t},\n\t\t\tPair[string, int]{\n\t\t\t\tKey:   \"b\",\n\t\t\t\tValue: 2,\n\t\t\t},\n\t\t\tPair[string, int]{\n\t\t\t\tKey:   \"c\",\n\t\t\t\tValue: 3,\n\t\t\t},\n\t\t))\n\n\t\tassertOrderedPairsEqual(t, om,\n\t\t\t[]string{\"a\", \"b\", \"c\"},\n\t\t\t[]int{1, 2, 3})\n\t})\n\n\tt.Run(\"with an invalid option type\", func(t *testing.T) {\n\t\tassert.PanicsWithValue(t, invalidOptionMessage, func() {\n\t\t\t_ = New[int, string](\"foo\")\n\t\t})\n\t})\n}\n\nfunc TestNilMap(t *testing.T) {\n\t// we want certain behaviors of a nil ordered map to be the same as they are for standard nil maps\n\tvar om *OrderedMap[int, any]\n\n\tt.Run(\"len\", func(t *testing.T) {\n\t\tassert.Equal(t, 0, om.Len())\n\t})\n\n\tt.Run(\"iterating - akin to range\", func(t *testing.T) {\n\t\tassert.Nil(t, om.Oldest())\n\t\tassert.Nil(t, om.Newest())\n\t})\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/orderedmap/utils_test.go",
    "content": "package orderedmap\n\nimport (\n\t\"crypto/rand\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\n// assertOrderedPairsEqual asserts that the map contains the given keys and values\n// from oldest to newest.\nfunc assertOrderedPairsEqual[K comparable, V any](\n\tt *testing.T, orderedMap *OrderedMap[K, V], expectedKeys []K, expectedValues []V,\n) {\n\tt.Helper()\n\n\tassertOrderedPairsEqualFromNewest(t, orderedMap, expectedKeys, expectedValues)\n\tassertOrderedPairsEqualFromOldest(t, orderedMap, expectedKeys, expectedValues)\n}\n\nfunc assertOrderedPairsEqualFromNewest[K comparable, V any](\n\tt *testing.T, orderedMap *OrderedMap[K, V], expectedKeys []K, expectedValues []V,\n) {\n\tt.Helper()\n\n\tif assert.Equal(t, len(expectedKeys), len(expectedValues)) && assert.Equal(t, len(expectedKeys), orderedMap.Len()) {\n\t\ti := orderedMap.Len() - 1\n\t\tfor pair := orderedMap.Newest(); pair != nil; pair = pair.Prev() {\n\t\t\tassert.Equal(t, expectedKeys[i], pair.Key, \"from newest index=%d on key\", i)\n\t\t\tassert.Equal(t, expectedValues[i], pair.Value, \"from newest index=%d on value\", i)\n\t\t\ti--\n\t\t}\n\t}\n}\n\nfunc assertOrderedPairsEqualFromOldest[K comparable, V any](\n\tt *testing.T, orderedMap *OrderedMap[K, V], expectedKeys []K, expectedValues []V,\n) {\n\tt.Helper()\n\n\tif assert.Equal(t, len(expectedKeys), len(expectedValues)) && assert.Equal(t, len(expectedKeys), orderedMap.Len()) {\n\t\ti := 0\n\t\tfor pair := orderedMap.Oldest(); pair != nil; pair = pair.Next() {\n\t\t\tassert.Equal(t, expectedKeys[i], pair.Key, \"from oldest index=%d on key\", i)\n\t\t\tassert.Equal(t, expectedValues[i], pair.Value, \"from oldest index=%d on value\", i)\n\t\t\ti++\n\t\t}\n\t}\n}\n\nfunc assertLenEqual[K comparable, V any](t *testing.T, orderedMap *OrderedMap[K, V], expectedLen int) {\n\tt.Helper()\n\n\tassert.Equal(t, expectedLen, orderedMap.Len())\n\n\t// also check the list length, for good measure\n\tassert.Equal(t, expectedLen, orderedMap.list.Len())\n}\n\nfunc randomHexString(t *testing.T, length int) string {\n\tt.Helper()\n\n\tb := length / 2 //nolint:gomnd\n\trandBytes := make([]byte, b)\n\n\tif n, err := rand.Read(randBytes); err != nil || n != b {\n\t\tif err == nil {\n\t\t\terr = fmt.Errorf(\"only got %v random bytes, expected %v\", n, b)\n\t\t}\n\t\tt.Fatal(err)\n\t}\n\n\treturn hex.EncodeToString(randBytes)\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/orderedmap/yaml.go",
    "content": "package orderedmap\n\nimport (\n\t\"fmt\"\n\n\t\"gopkg.in/yaml.v3\"\n)\n\nvar (\n\t_ yaml.Marshaler   = &OrderedMap[int, any]{}\n\t_ yaml.Unmarshaler = &OrderedMap[int, any]{}\n)\n\n// MarshalYAML implements the yaml.Marshaler interface.\nfunc (om *OrderedMap[K, V]) MarshalYAML() (interface{}, error) {\n\tif om == nil {\n\t\treturn []byte(\"null\"), nil\n\t}\n\n\tnode := yaml.Node{\n\t\tKind: yaml.MappingNode,\n\t}\n\n\tfor pair := om.Oldest(); pair != nil; pair = pair.Next() {\n\t\tkey, value := pair.Key, pair.Value\n\n\t\tkeyNode := &yaml.Node{}\n\n\t\t// serialize key to yaml, then deserialize it back into the node\n\t\t// this is a hack to get the correct tag for the key\n\t\tif err := keyNode.Encode(key); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tvalueNode := &yaml.Node{}\n\t\tif err := valueNode.Encode(value); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tnode.Content = append(node.Content, keyNode, valueNode)\n\t}\n\n\treturn &node, nil\n}\n\n// UnmarshalYAML implements the yaml.Unmarshaler interface.\nfunc (om *OrderedMap[K, V]) UnmarshalYAML(value *yaml.Node) error {\n\tif value.Kind != yaml.MappingNode {\n\t\treturn fmt.Errorf(\"pipeline must contain YAML mapping, has %v\", value.Kind)\n\t}\n\n\tif om.list == nil {\n\t\tom.initialize(0)\n\t}\n\n\tfor index := 0; index < len(value.Content); index += 2 {\n\t\tvar key K\n\t\tvar val V\n\n\t\tif err := value.Content[index].Decode(&key); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif err := value.Content[index+1].Decode(&val); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tom.Set(key, val)\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/orderedmap/yaml_fuzz_test.go",
    "content": "package orderedmap\n\n// Adapted from https://github.com/dvyukov/go-fuzz-corpus/blob/c42c1b2/json/json.go\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"gopkg.in/yaml.v3\"\n)\n\nfunc FuzzRoundTripYAML(f *testing.F) {\n\tf.Fuzz(func(t *testing.T, data []byte) {\n\t\tfor _, testCase := range []struct {\n\t\t\tname        string\n\t\t\tconstructor func() any\n\t\t\t// should be a function that asserts that 2 objects of the type returned by constructor are equal\n\t\t\tequalityAssertion func(*testing.T, any, any) bool\n\t\t}{\n\t\t\t{\n\t\t\t\tname:              \"with a string -> string map\",\n\t\t\t\tconstructor:       func() any { return &OrderedMap[string, string]{} },\n\t\t\t\tequalityAssertion: assertOrderedMapsEqual[string, string],\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:              \"with a string -> int map\",\n\t\t\t\tconstructor:       func() any { return &OrderedMap[string, int]{} },\n\t\t\t\tequalityAssertion: assertOrderedMapsEqual[string, int],\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:              \"with a string -> any map\",\n\t\t\t\tconstructor:       func() any { return &OrderedMap[string, any]{} },\n\t\t\t\tequalityAssertion: assertOrderedMapsEqual[string, any],\n\t\t\t},\n\t\t\t{\n\t\t\t\tname:              \"with a struct with map fields\",\n\t\t\t\tconstructor:       func() any { return new(testFuzzStruct) },\n\t\t\t\tequalityAssertion: assertTestFuzzStructEqual,\n\t\t\t},\n\t\t} {\n\t\t\tt.Run(testCase.name, func(t *testing.T) {\n\t\t\t\tv1 := testCase.constructor()\n\t\t\t\tif yaml.Unmarshal(data, v1) != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tt.Log(data)\n\t\t\t\tt.Log(v1)\n\n\t\t\t\tyamlData, err := yaml.Marshal(v1)\n\t\t\t\trequire.NoError(t, err)\n\t\t\t\tt.Log(string(yamlData))\n\n\t\t\t\tv2 := testCase.constructor()\n\t\t\t\terr = yaml.Unmarshal(yamlData, v2)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Log(string(yamlData))\n\t\t\t\t\tt.Fatal(err)\n\t\t\t\t}\n\n\t\t\t\tif !assert.True(t, testCase.equalityAssertion(t, v1, v2), \"failed with input data %q\", string(data)) {\n\t\t\t\t\t// look at that what the standard lib does with regular map, to help with debugging\n\n\t\t\t\t\tvar m1 map[string]any\n\t\t\t\t\trequire.NoError(t, yaml.Unmarshal(data, &m1))\n\n\t\t\t\t\tmapJsonData, err := yaml.Marshal(m1)\n\t\t\t\t\trequire.NoError(t, err)\n\n\t\t\t\t\tvar m2 map[string]any\n\t\t\t\t\trequire.NoError(t, yaml.Unmarshal(mapJsonData, &m2))\n\n\t\t\t\t\tt.Logf(\"initial data = %s\", string(data))\n\t\t\t\t\tt.Logf(\"unmarshalled map = %v\", m1)\n\t\t\t\t\tt.Logf(\"re-marshalled from map = %s\", string(mapJsonData))\n\t\t\t\t\tt.Logf(\"re-marshalled from test obj = %s\", string(yamlData))\n\t\t\t\t\tt.Logf(\"re-unmarshalled map = %s\", m2)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/orderedmap/yaml_test.go",
    "content": "package orderedmap\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n\t\"gopkg.in/yaml.v3\"\n)\n\nfunc TestMarshalYAML(t *testing.T) {\n\tt.Run(\"int key\", func(t *testing.T) {\n\t\tom := New[int, any]()\n\t\tom.Set(1, \"bar\")\n\t\tom.Set(7, \"baz\")\n\t\tom.Set(2, 28)\n\t\tom.Set(3, 100)\n\t\tom.Set(4, \"baz\")\n\t\tom.Set(5, \"28\")\n\t\tom.Set(6, \"100\")\n\t\tom.Set(8, \"baz\")\n\t\tom.Set(8, \"baz\")\n\t\tom.Set(9, \"Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque auctor augue accumsan mi maximus, quis viverra massa pretium. Phasellus imperdiet sapien a interdum sollicitudin. Duis at commodo lectus, a lacinia sem.\")\n\n\t\tb, err := yaml.Marshal(om)\n\n\t\texpected := `1: bar\n7: baz\n2: 28\n3: 100\n4: baz\n5: \"28\"\n6: \"100\"\n8: baz\n9: Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quisque auctor augue accumsan mi maximus, quis viverra massa pretium. Phasellus imperdiet sapien a interdum sollicitudin. Duis at commodo lectus, a lacinia sem.\n`\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, expected, string(b))\n\t})\n\n\tt.Run(\"string key\", func(t *testing.T) {\n\t\tom := New[string, any]()\n\t\tom.Set(\"test\", \"bar\")\n\t\tom.Set(\"abc\", true)\n\n\t\tb, err := yaml.Marshal(om)\n\t\tassert.NoError(t, err)\n\t\texpected := `test: bar\nabc: true\n`\n\t\tassert.Equal(t, expected, string(b))\n\t})\n\n\tt.Run(\"typed string key\", func(t *testing.T) {\n\t\ttype myString string\n\t\tom := New[myString, any]()\n\t\tom.Set(\"test\", \"bar\")\n\t\tom.Set(\"abc\", true)\n\n\t\tb, err := yaml.Marshal(om)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, `test: bar\nabc: true\n`, string(b))\n\t})\n\n\tt.Run(\"typed int key\", func(t *testing.T) {\n\t\ttype myInt uint32\n\t\tom := New[myInt, any]()\n\t\tom.Set(1, \"bar\")\n\t\tom.Set(7, \"baz\")\n\t\tom.Set(2, 28)\n\t\tom.Set(3, 100)\n\t\tom.Set(4, \"baz\")\n\n\t\tb, err := yaml.Marshal(om)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, `1: bar\n7: baz\n2: 28\n3: 100\n4: baz\n`, string(b))\n\t})\n\n\tt.Run(\"TextMarshaller key\", func(t *testing.T) {\n\t\tom := New[marshallable, any]()\n\t\tom.Set(marshallable(1), \"bar\")\n\t\tom.Set(marshallable(28), true)\n\n\t\tb, err := yaml.Marshal(om)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, `'#1#': bar\n'#28#': true\n`, string(b))\n\t})\n\n\tt.Run(\"empty map with 0 elements\", func(t *testing.T) {\n\t\tom := New[string, any]()\n\n\t\tb, err := yaml.Marshal(om)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"{}\\n\", string(b))\n\t})\n\n\tt.Run(\"empty map with no elements (null)\", func(t *testing.T) {\n\t\tom := &OrderedMap[string, string]{}\n\n\t\tb, err := yaml.Marshal(om)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, \"{}\\n\", string(b))\n\t})\n}\n\nfunc TestUnmarshallYAML(t *testing.T) {\n\tt.Run(\"int key\", func(t *testing.T) {\n\t\tdata := `\n1: bar\n7: baz\n2: 28\n3: 100\n4: baz\n5: \"28\"\n6: \"100\"\n8: baz\n`\n\t\tom := New[int, any]()\n\t\trequire.NoError(t, yaml.Unmarshal([]byte(data), &om))\n\n\t\tassertOrderedPairsEqual(t, om,\n\t\t\t[]int{1, 7, 2, 3, 4, 5, 6, 8},\n\t\t\t[]any{\"bar\", \"baz\", 28, 100, \"baz\", \"28\", \"100\", \"baz\"})\n\n\t\t// serialize back to yaml to make sure things are equal\n\t})\n\n\tt.Run(\"string key\", func(t *testing.T) {\n\t\tdata := `{\"test\":\"bar\",\"abc\":true}`\n\n\t\tom := New[string, any]()\n\t\trequire.NoError(t, yaml.Unmarshal([]byte(data), &om))\n\n\t\tassertOrderedPairsEqual(t, om,\n\t\t\t[]string{\"test\", \"abc\"},\n\t\t\t[]any{\"bar\", true})\n\t})\n\n\tt.Run(\"typed string key\", func(t *testing.T) {\n\t\tdata := `{\"test\":\"bar\",\"abc\":true}`\n\n\t\ttype myString string\n\t\tom := New[myString, any]()\n\t\trequire.NoError(t, yaml.Unmarshal([]byte(data), &om))\n\n\t\tassertOrderedPairsEqual(t, om,\n\t\t\t[]myString{\"test\", \"abc\"},\n\t\t\t[]any{\"bar\", true})\n\t})\n\n\tt.Run(\"typed int key\", func(t *testing.T) {\n\t\tdata := `\n1: bar\n7: baz\n2: 28\n3: 100\n4: baz\n5: \"28\"\n6: \"100\"\n8: baz\n`\n\t\ttype myInt uint32\n\t\tom := New[myInt, any]()\n\t\trequire.NoError(t, yaml.Unmarshal([]byte(data), &om))\n\n\t\tassertOrderedPairsEqual(t, om,\n\t\t\t[]myInt{1, 7, 2, 3, 4, 5, 6, 8},\n\t\t\t[]any{\"bar\", \"baz\", 28, 100, \"baz\", \"28\", \"100\", \"baz\"})\n\t})\n\n\tt.Run(\"TextUnmarshaler key\", func(t *testing.T) {\n\t\tdata := `{\"#1#\":\"bar\",\"#28#\":true}`\n\n\t\tom := New[marshallable, any]()\n\t\trequire.NoError(t, yaml.Unmarshal([]byte(data), &om))\n\n\t\tassertOrderedPairsEqual(t, om,\n\t\t\t[]marshallable{1, 28},\n\t\t\t[]any{\"bar\", true})\n\t})\n\n\tt.Run(\"when fed with an input that's not an object\", func(t *testing.T) {\n\t\tfor _, data := range []string{\"true\", `[\"foo\"]`, \"42\", `\"foo\"`} {\n\t\t\tom := New[int, any]()\n\t\t\trequire.Error(t, yaml.Unmarshal([]byte(data), &om))\n\t\t}\n\t})\n\n\tt.Run(\"empty map\", func(t *testing.T) {\n\t\tdata := `{}`\n\n\t\tom := New[int, any]()\n\t\trequire.NoError(t, yaml.Unmarshal([]byte(data), &om))\n\n\t\tassertLenEqual(t, om, 0)\n\t})\n}\n\nfunc TestYAMLSpecialCharacters(t *testing.T) {\n\tbaselineMap := map[string]any{specialCharacters: specialCharacters}\n\tbaselineData, err := yaml.Marshal(baselineMap)\n\trequire.NoError(t, err) // baseline proves this key is supported by official yaml library\n\tt.Logf(\"specialCharacters: %#v as []rune:%v\", specialCharacters, []rune(specialCharacters))\n\tt.Logf(\"baseline yaml data: %s\", baselineData)\n\n\tt.Run(\"marshal special characters\", func(t *testing.T) {\n\t\tom := New[string, any]()\n\t\tom.Set(specialCharacters, specialCharacters)\n\t\tb, err := yaml.Marshal(om)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, baselineData, b)\n\n\t\ttype myString string\n\t\tom2 := New[myString, myString]()\n\t\tom2.Set(specialCharacters, specialCharacters)\n\t\tb, err = yaml.Marshal(om2)\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, baselineData, b)\n\t})\n\n\tt.Run(\"unmarshall special characters\", func(t *testing.T) {\n\t\tom := New[string, any]()\n\t\trequire.NoError(t, yaml.Unmarshal(baselineData, &om))\n\t\tassertOrderedPairsEqual(t, om,\n\t\t\t[]string{specialCharacters},\n\t\t\t[]any{specialCharacters})\n\n\t\ttype myString string\n\t\tom2 := New[myString, myString]()\n\t\trequire.NoError(t, yaml.Unmarshal(baselineData, &om2))\n\t\tassertOrderedPairsEqual(t, om2,\n\t\t\t[]myString{specialCharacters},\n\t\t\t[]myString{specialCharacters})\n\t})\n}\n\nfunc TestYAMLRoundTrip(t *testing.T) {\n\tfor _, testCase := range []struct {\n\t\tname          string\n\t\tinput         string\n\t\ttargetFactory func() any\n\t}{\n\t\t{\n\t\t\tname:  \"empty map\",\n\t\t\tinput: \"{}\\n\",\n\t\t\ttargetFactory: func() any {\n\t\t\t\treturn &OrderedMap[string, any]{}\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tname: \"\",\n\t\t\tinput: `x: 28\nm:\n    bar:\n        - 5:\n            foo: bar\n    foo:\n        - 12:\n            b: true\n            i: 12\n            m:\n                a: b\n                c: 28\n            \"n\": null\n          28:\n            a: false\n            b:\n                - 1\n                - 2\n                - 3\n        - 3:\n            c: null\n            d: 87\n          4:\n            e: true\n          5:\n            f: 4\n            g: 5\n            h: 6\n`,\n\t\t\ttargetFactory: func() any { return &nestedMaps{} },\n\t\t},\n\t\t{\n\t\t\tname:          \"with UTF-8 special chars in key\",\n\t\t\tinput:         \"�: 0\\n\",\n\t\t\ttargetFactory: func() any { return &OrderedMap[string, int]{} },\n\t\t},\n\t} {\n\t\tt.Run(testCase.name, func(t *testing.T) {\n\t\t\ttarget := testCase.targetFactory()\n\n\t\t\trequire.NoError(t, yaml.Unmarshal([]byte(testCase.input), target))\n\n\t\t\tvar (\n\t\t\t\tout []byte\n\t\t\t\terr error\n\t\t\t)\n\n\t\t\tout, err = yaml.Marshal(target)\n\n\t\t\tif assert.NoError(t, err) {\n\t\t\t\tassert.Equal(t, testCase.input, string(out))\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkMarshalYAML(b *testing.B) {\n\tom := New[int, any]()\n\tom.Set(1, \"bar\")\n\tom.Set(7, \"baz\")\n\tom.Set(2, 28)\n\tom.Set(3, 100)\n\tom.Set(4, \"baz\")\n\tom.Set(5, \"28\")\n\tom.Set(6, \"100\")\n\tom.Set(8, \"baz\")\n\tom.Set(8, \"baz\")\n\n\tb.ResetTimer()\n\n\tfor i := 0; i < b.N; i++ {\n\t\t_, _ = yaml.Marshal(om)\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/picker/picker.go",
    "content": "package picker\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n)\n\n// Picker provides synchronization, and Context cancelation\n// for groups of goroutines working on subtasks of a common task.\n// Inspired by errGroup\ntype Picker[T any] struct {\n\tctx    context.Context\n\tcancel func()\n\n\twg sync.WaitGroup\n\n\tonce    sync.Once\n\terrOnce sync.Once\n\tresult  T\n\terr     error\n}\n\nfunc newPicker[T any](ctx context.Context, cancel func()) *Picker[T] {\n\treturn &Picker[T]{\n\t\tctx:    ctx,\n\t\tcancel: cancel,\n\t}\n}\n\n// WithContext returns a new Picker and an associated Context derived from ctx.\n// and cancel when first element return.\nfunc WithContext[T any](ctx context.Context) (*Picker[T], context.Context) {\n\tctx, cancel := context.WithCancel(ctx)\n\treturn newPicker[T](ctx, cancel), ctx\n}\n\n// WithTimeout returns a new Picker and an associated Context derived from ctx with timeout.\nfunc WithTimeout[T any](ctx context.Context, timeout time.Duration) (*Picker[T], context.Context) {\n\tctx, cancel := context.WithTimeout(ctx, timeout)\n\treturn newPicker[T](ctx, cancel), ctx\n}\n\n// Wait blocks until all function calls from the Go method have returned,\n// then returns the first nil error result (if any) from them.\nfunc (p *Picker[T]) Wait() T {\n\tp.wg.Wait()\n\tif p.cancel != nil {\n\t\tp.cancel()\n\t\tp.cancel = nil\n\t}\n\treturn p.result\n}\n\n// Error return the first error (if all success return nil)\nfunc (p *Picker[T]) Error() error {\n\treturn p.err\n}\n\n// Go calls the given function in a new goroutine.\n// The first call to return a nil error cancels the group; its result will be returned by Wait.\nfunc (p *Picker[T]) Go(f func() (T, error)) {\n\tp.wg.Add(1)\n\n\tgo func() {\n\t\tdefer p.wg.Done()\n\n\t\tif ret, err := f(); err == nil {\n\t\t\tp.once.Do(func() {\n\t\t\t\tp.result = ret\n\t\t\t\tif p.cancel != nil {\n\t\t\t\t\tp.cancel()\n\t\t\t\t\tp.cancel = nil\n\t\t\t\t}\n\t\t\t})\n\t\t} else {\n\t\t\tp.errOnce.Do(func() {\n\t\t\t\tp.err = err\n\t\t\t})\n\t\t}\n\t}()\n}\n\n// Close cancels the picker context and releases resources associated with it.\n// If Wait has been called, then there is no need to call Close.\nfunc (p *Picker[T]) Close() error {\n\tif p.cancel != nil {\n\t\tp.cancel()\n\t\tp.cancel = nil\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/picker/picker_test.go",
    "content": "package picker\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/samber/lo\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc sleepAndSend[T any](ctx context.Context, delay int, input T) func() (T, error) {\n\treturn func() (T, error) {\n\t\ttimer := time.NewTimer(time.Millisecond * time.Duration(delay))\n\t\tselect {\n\t\tcase <-timer.C:\n\t\t\treturn input, nil\n\t\tcase <-ctx.Done():\n\t\t\treturn lo.Empty[T](), ctx.Err()\n\t\t}\n\t}\n}\n\nfunc TestPicker_Basic(t *testing.T) {\n\tt.Parallel()\n\tpicker, ctx := WithContext[int](context.Background())\n\tpicker.Go(sleepAndSend(ctx, 200, 2))\n\tpicker.Go(sleepAndSend(ctx, 100, 1))\n\n\tnumber := picker.Wait()\n\tassert.NotNil(t, number)\n\tassert.Equal(t, number, 1)\n}\n\nfunc TestPicker_Timeout(t *testing.T) {\n\tt.Parallel()\n\tpicker, ctx := WithTimeout[int](context.Background(), time.Millisecond*5)\n\tpicker.Go(sleepAndSend(ctx, 100, 1))\n\n\tnumber := picker.Wait()\n\tassert.Equal(t, number, lo.Empty[int]())\n\tassert.NotNil(t, picker.Error())\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/pool/alloc.go",
    "content": "package pool\n\n// Inspired by https://github.com/xtaci/smux/blob/master/alloc.go\n\nimport (\n\t\"errors\"\n\t\"math/bits\"\n\t\"sync\"\n)\n\nvar DefaultAllocator = NewAllocator()\n\ntype Allocator interface {\n\tGet(size int) []byte\n\tPut(buf []byte) error\n}\n\n// defaultAllocator for incoming frames, optimized to prevent overwriting after zeroing\ntype defaultAllocator struct {\n\tbuffers [11]sync.Pool\n}\n\n// NewAllocator initiates a []byte allocator for frames less than 65536 bytes,\n// the waste(memory fragmentation) of space allocation is guaranteed to be\n// no more than 50%.\nfunc NewAllocator() Allocator {\n\treturn &defaultAllocator{\n\t\tbuffers: [...]sync.Pool{ // 64B -> 64K\n\t\t\t{New: func() any { return new([1 << 6]byte) }},\n\t\t\t{New: func() any { return new([1 << 7]byte) }},\n\t\t\t{New: func() any { return new([1 << 8]byte) }},\n\t\t\t{New: func() any { return new([1 << 9]byte) }},\n\t\t\t{New: func() any { return new([1 << 10]byte) }},\n\t\t\t{New: func() any { return new([1 << 11]byte) }},\n\t\t\t{New: func() any { return new([1 << 12]byte) }},\n\t\t\t{New: func() any { return new([1 << 13]byte) }},\n\t\t\t{New: func() any { return new([1 << 14]byte) }},\n\t\t\t{New: func() any { return new([1 << 15]byte) }},\n\t\t\t{New: func() any { return new([1 << 16]byte) }},\n\t\t},\n\t}\n}\n\n// Get a []byte from pool with most appropriate cap\nfunc (alloc *defaultAllocator) Get(size int) []byte {\n\tswitch {\n\tcase size < 0:\n\t\tpanic(\"alloc.Get: len out of range\")\n\tcase size == 0:\n\t\treturn nil\n\tcase size > 65536:\n\t\treturn make([]byte, size)\n\tdefault:\n\t\tvar index uint16\n\t\tif size > 64 {\n\t\t\tindex = msb(size)\n\t\t\tif size != 1<<index {\n\t\t\t\tindex += 1\n\t\t\t}\n\t\t\tindex -= 6\n\t\t}\n\n\t\tbuffer := alloc.buffers[index].Get()\n\t\tswitch index {\n\t\tcase 0:\n\t\t\treturn buffer.(*[1 << 6]byte)[:size]\n\t\tcase 1:\n\t\t\treturn buffer.(*[1 << 7]byte)[:size]\n\t\tcase 2:\n\t\t\treturn buffer.(*[1 << 8]byte)[:size]\n\t\tcase 3:\n\t\t\treturn buffer.(*[1 << 9]byte)[:size]\n\t\tcase 4:\n\t\t\treturn buffer.(*[1 << 10]byte)[:size]\n\t\tcase 5:\n\t\t\treturn buffer.(*[1 << 11]byte)[:size]\n\t\tcase 6:\n\t\t\treturn buffer.(*[1 << 12]byte)[:size]\n\t\tcase 7:\n\t\t\treturn buffer.(*[1 << 13]byte)[:size]\n\t\tcase 8:\n\t\t\treturn buffer.(*[1 << 14]byte)[:size]\n\t\tcase 9:\n\t\t\treturn buffer.(*[1 << 15]byte)[:size]\n\t\tcase 10:\n\t\t\treturn buffer.(*[1 << 16]byte)[:size]\n\t\tdefault:\n\t\t\tpanic(\"invalid pool index\")\n\t\t}\n\t}\n}\n\n// Put returns a []byte to pool for future use,\n// which the cap must be exactly 2^n\nfunc (alloc *defaultAllocator) Put(buf []byte) error {\n\tif cap(buf) == 0 || cap(buf) > 65536 {\n\t\treturn nil\n\t}\n\n\tbits := msb(cap(buf))\n\tif cap(buf) != 1<<bits {\n\t\treturn errors.New(\"allocator Put() incorrect buffer size\")\n\t}\n\tif cap(buf) < 1<<6 {\n\t\treturn nil\n\t}\n\tbits -= 6\n\tbuf = buf[:cap(buf)]\n\n\t//nolint\n\t//lint:ignore SA6002 ignore temporarily\n\tswitch bits {\n\tcase 0:\n\t\talloc.buffers[bits].Put((*[1 << 6]byte)(buf))\n\tcase 1:\n\t\talloc.buffers[bits].Put((*[1 << 7]byte)(buf))\n\tcase 2:\n\t\talloc.buffers[bits].Put((*[1 << 8]byte)(buf))\n\tcase 3:\n\t\talloc.buffers[bits].Put((*[1 << 9]byte)(buf))\n\tcase 4:\n\t\talloc.buffers[bits].Put((*[1 << 10]byte)(buf))\n\tcase 5:\n\t\talloc.buffers[bits].Put((*[1 << 11]byte)(buf))\n\tcase 6:\n\t\talloc.buffers[bits].Put((*[1 << 12]byte)(buf))\n\tcase 7:\n\t\talloc.buffers[bits].Put((*[1 << 13]byte)(buf))\n\tcase 8:\n\t\talloc.buffers[bits].Put((*[1 << 14]byte)(buf))\n\tcase 9:\n\t\talloc.buffers[bits].Put((*[1 << 15]byte)(buf))\n\tcase 10:\n\t\talloc.buffers[bits].Put((*[1 << 16]byte)(buf))\n\tdefault:\n\t\tpanic(\"invalid pool index\")\n\t}\n\treturn nil\n}\n\n// msb return the pos of most significant bit\nfunc msb(size int) uint16 {\n\treturn uint16(bits.Len32(uint32(size)) - 1)\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/pool/alloc_test.go",
    "content": "package pool\n\nimport (\n\t\"testing\"\n\n\t\"github.com/metacubex/randv2\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestAllocGet(t *testing.T) {\n\talloc := NewAllocator()\n\tassert.Nil(t, alloc.Get(0))\n\tassert.Equal(t, 1, len(alloc.Get(1)))\n\tassert.Equal(t, 2, len(alloc.Get(2)))\n\tassert.Equal(t, 3, len(alloc.Get(3)))\n\tassert.Equal(t, 64, cap(alloc.Get(3)))\n\tassert.Equal(t, 64, cap(alloc.Get(4)))\n\tassert.Equal(t, 1023, len(alloc.Get(1023)))\n\tassert.Equal(t, 1024, cap(alloc.Get(1023)))\n\tassert.Equal(t, 1024, len(alloc.Get(1024)))\n\tassert.Equal(t, 65536, len(alloc.Get(65536)))\n\tassert.Equal(t, 65537, len(alloc.Get(65537)))\n}\n\nfunc TestAllocPut(t *testing.T) {\n\talloc := NewAllocator()\n\tassert.Nil(t, alloc.Put(nil), \"put nil misbehavior\")\n\tassert.NotNil(t, alloc.Put(make([]byte, 3)), \"put elem:3 []bytes misbehavior\")\n\tassert.Nil(t, alloc.Put(make([]byte, 4)), \"put elem:4 []bytes misbehavior\")\n\tassert.Nil(t, alloc.Put(make([]byte, 1023, 1024)), \"put elem:1024 []bytes misbehavior\")\n\tassert.Nil(t, alloc.Put(make([]byte, 65536)), \"put elem:65536 []bytes misbehavior\")\n\tassert.Nil(t, alloc.Put(make([]byte, 65537)), \"put elem:65537 []bytes misbehavior\")\n}\n\nfunc TestAllocPutThenGet(t *testing.T) {\n\talloc := NewAllocator()\n\tdata := alloc.Get(4)\n\talloc.Put(data)\n\tnewData := alloc.Get(4)\n\n\tassert.Equal(t, cap(data), cap(newData), \"different cap while alloc.Get()\")\n}\n\nfunc BenchmarkMSB(b *testing.B) {\n\tfor i := 0; i < b.N; i++ {\n\t\tmsb(randv2.Int())\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/pool/buffer.go",
    "content": "package pool\n\nimport (\n\t\"bytes\"\n\t\"sync\"\n)\n\nvar bufferPool = sync.Pool{New: func() any { return &bytes.Buffer{} }}\n\nfunc GetBuffer() *bytes.Buffer {\n\treturn bufferPool.Get().(*bytes.Buffer)\n}\n\nfunc PutBuffer(buf *bytes.Buffer) {\n\tbuf.Reset()\n\tbufferPool.Put(buf)\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/pool/buffer_low_memory.go",
    "content": "//go:build with_low_memory\n\npackage pool\n\nconst (\n\t// RelayBufferSize using for tcp\n\t// io.Copy default buffer size is 32 KiB\n\tRelayBufferSize = 8 * 1024\n\n\t// UDPBufferSize using for udp\n\t// Most UDPs are smaller than the MTU, and the TUN's MTU\n\t// set to 9000, so the UDP Buffer size set to 16Kib\n\tUDPBufferSize = 8 * 1024\n)\n"
  },
  {
    "path": "core/Clash.Meta/common/pool/buffer_standard.go",
    "content": "//go:build !with_low_memory\n\npackage pool\n\nconst (\n\t// RelayBufferSize using for tcp\n\t// io.Copy default buffer size is 32 KiB\n\tRelayBufferSize = 16 * 1024\n\n\t// UDPBufferSize using for udp\n\t// Most UDPs are smaller than the MTU, and the TUN's MTU\n\t// set to 9000, so the UDP Buffer size set to 16Kib\n\tUDPBufferSize = 16 * 1024\n)\n"
  },
  {
    "path": "core/Clash.Meta/common/pool/pool.go",
    "content": "package pool\n\nfunc Get(size int) []byte {\n\treturn DefaultAllocator.Get(size)\n}\n\nfunc Put(buf []byte) error {\n\treturn DefaultAllocator.Put(buf)\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/pool/sing.go",
    "content": "package pool\n\nimport \"github.com/metacubex/sing/common/buf\"\n\nfunc init() {\n\tbuf.DefaultAllocator = DefaultAllocator\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/queue/queue.go",
    "content": "package queue\n\nimport (\n\t\"sync\"\n)\n\n// Queue is a simple concurrent safe queue\ntype Queue[T any] struct {\n\titems []T\n\tlock  sync.RWMutex\n}\n\n// Put add the item to the queue.\nfunc (q *Queue[T]) Put(items ...T) {\n\tif len(items) == 0 {\n\t\treturn\n\t}\n\n\tq.lock.Lock()\n\tq.items = append(q.items, items...)\n\tq.lock.Unlock()\n}\n\n// Pop returns the head of items.\nfunc (q *Queue[T]) Pop() (head T) {\n\tif len(q.items) == 0 {\n\t\treturn\n\t}\n\n\tq.lock.Lock()\n\thead = q.items[0]\n\tq.items = q.items[1:]\n\tq.lock.Unlock()\n\treturn head\n}\n\n// Last returns the last of item.\nfunc (q *Queue[T]) Last() (last T) {\n\tif len(q.items) == 0 {\n\t\treturn\n\t}\n\n\tq.lock.RLock()\n\tlast = q.items[len(q.items)-1]\n\tq.lock.RUnlock()\n\treturn last\n}\n\n// Copy get the copy of queue.\nfunc (q *Queue[T]) Copy() (items []T) {\n\tq.lock.RLock()\n\titems = append(items, q.items...)\n\tq.lock.RUnlock()\n\treturn items\n}\n\n// Len returns the number of items in this queue.\nfunc (q *Queue[T]) Len() int64 {\n\tq.lock.RLock()\n\tdefer q.lock.RUnlock()\n\n\treturn int64(len(q.items))\n}\n\n// New is a constructor for a new concurrent safe queue.\nfunc New[T any](hint int64) *Queue[T] {\n\treturn &Queue[T]{\n\t\titems: make([]T, 0, hint),\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/queue/queue_test.go",
    "content": "package queue\n\nimport (\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\n// TestQueuePut tests the Put method of Queue\nfunc TestQueuePut(t *testing.T) {\n\t// Initialize a new queue\n\tq := New[int](10)\n\n\t// Test putting a single item\n\tq.Put(1)\n\tassert.Equal(t, int64(1), q.Len(), \"Queue length should be 1 after putting one item\")\n\n\t// Test putting multiple items\n\tq.Put(2, 3, 4)\n\tassert.Equal(t, int64(4), q.Len(), \"Queue length should be 4 after putting three more items\")\n\n\t// Test putting zero items (should not change queue)\n\tq.Put()\n\tassert.Equal(t, int64(4), q.Len(), \"Queue length should remain unchanged when putting zero items\")\n}\n\n// TestQueuePop tests the Pop method of Queue\nfunc TestQueuePop(t *testing.T) {\n\t// Initialize a new queue with items\n\tq := New[int](10)\n\tq.Put(1, 2, 3)\n\n\t// Test popping items in FIFO order\n\titem := q.Pop()\n\tassert.Equal(t, 1, item, \"First item popped should be 1\")\n\tassert.Equal(t, int64(2), q.Len(), \"Queue length should be 2 after popping one item\")\n\n\titem = q.Pop()\n\tassert.Equal(t, 2, item, \"Second item popped should be 2\")\n\tassert.Equal(t, int64(1), q.Len(), \"Queue length should be 1 after popping two items\")\n\n\titem = q.Pop()\n\tassert.Equal(t, 3, item, \"Third item popped should be 3\")\n\tassert.Equal(t, int64(0), q.Len(), \"Queue length should be 0 after popping all items\")\n}\n\n// TestQueuePopEmpty tests the Pop method on an empty queue\nfunc TestQueuePopEmpty(t *testing.T) {\n\t// Initialize a new empty queue\n\tq := New[int](0)\n\n\t// Test popping from an empty queue\n\titem := q.Pop()\n\tassert.Equal(t, 0, item, \"Popping from an empty queue should return the zero value\")\n\tassert.Equal(t, int64(0), q.Len(), \"Queue length should remain 0 after popping from an empty queue\")\n}\n\n// TestQueueLast tests the Last method of Queue\nfunc TestQueueLast(t *testing.T) {\n\t// Initialize a new queue with items\n\tq := New[int](10)\n\tq.Put(1, 2, 3)\n\n\t// Test getting the last item\n\titem := q.Last()\n\tassert.Equal(t, 3, item, \"Last item should be 3\")\n\tassert.Equal(t, int64(3), q.Len(), \"Queue length should remain unchanged after calling Last\")\n\n\t// Test Last on an empty queue\n\temptyQ := New[int](0)\n\temptyItem := emptyQ.Last()\n\tassert.Equal(t, 0, emptyItem, \"Last on an empty queue should return the zero value\")\n}\n\n// TestQueueCopy tests the Copy method of Queue\nfunc TestQueueCopy(t *testing.T) {\n\t// Initialize a new queue with items\n\tq := New[int](10)\n\tq.Put(1, 2, 3)\n\n\t// Test copying the queue\n\tcopy := q.Copy()\n\tassert.Equal(t, 3, len(copy), \"Copy should have the same number of items as the original queue\")\n\tassert.Equal(t, 1, copy[0], \"First item in copy should be 1\")\n\tassert.Equal(t, 2, copy[1], \"Second item in copy should be 2\")\n\tassert.Equal(t, 3, copy[2], \"Third item in copy should be 3\")\n\n\t// Verify that modifying the copy doesn't affect the original queue\n\tcopy[0] = 99\n\tassert.Equal(t, 1, q.Pop(), \"Original queue should not be affected by modifying the copy\")\n}\n\n// TestQueueLen tests the Len method of Queue\nfunc TestQueueLen(t *testing.T) {\n\t// Initialize a new empty queue\n\tq := New[int](10)\n\tassert.Equal(t, int64(0), q.Len(), \"New queue should have length 0\")\n\n\t// Add items and check length\n\tq.Put(1, 2)\n\tassert.Equal(t, int64(2), q.Len(), \"Queue length should be 2 after putting two items\")\n\n\t// Remove an item and check length\n\tq.Pop()\n\tassert.Equal(t, int64(1), q.Len(), \"Queue length should be 1 after popping one item\")\n}\n\n// TestQueueNew tests the New constructor\nfunc TestQueueNew(t *testing.T) {\n\t// Test creating a new queue with different hints\n\tq1 := New[int](0)\n\tassert.NotNil(t, q1, \"New queue should not be nil\")\n\tassert.Equal(t, int64(0), q1.Len(), \"New queue should have length 0\")\n\n\tq2 := New[int](10)\n\tassert.NotNil(t, q2, \"New queue should not be nil\")\n\tassert.Equal(t, int64(0), q2.Len(), \"New queue should have length 0\")\n\n\t// Test with a different type\n\tq3 := New[string](5)\n\tassert.NotNil(t, q3, \"New queue should not be nil\")\n\tassert.Equal(t, int64(0), q3.Len(), \"New queue should have length 0\")\n}\n\n// TestQueueConcurrency tests the concurrency safety of Queue\nfunc TestQueueConcurrency(t *testing.T) {\n\t// Initialize a new queue\n\tq := New[int](100)\n\n\t// Number of goroutines and operations\n\tgoroutines := 10\n\toperations := 100\n\n\t// Wait group to synchronize goroutines\n\twg := sync.WaitGroup{}\n\twg.Add(goroutines * 2) // For both producers and consumers\n\n\t// Start producer goroutines\n\tfor i := 0; i < goroutines; i++ {\n\t\tgo func(id int) {\n\t\t\tdefer wg.Done()\n\t\t\tfor j := 0; j < operations; j++ {\n\t\t\t\tq.Put(id*operations + j)\n\t\t\t\t// Small sleep to increase chance of race conditions\n\t\t\t\ttime.Sleep(time.Microsecond)\n\t\t\t}\n\t\t}(i)\n\t}\n\n\t// Start consumer goroutines\n\tconsumed := make(chan int, goroutines*operations)\n\tfor i := 0; i < goroutines; i++ {\n\t\tgo func() {\n\t\t\tdefer wg.Done()\n\t\t\tfor j := 0; j < operations; j++ {\n\t\t\t\t// Try to pop an item, but don't block if queue is empty\n\t\t\t\t// Use a mutex to avoid race condition between Len() check and Pop()\n\t\t\t\tq.lock.Lock()\n\t\t\t\tif len(q.items) > 0 {\n\t\t\t\t\titem := q.items[0]\n\t\t\t\t\tq.items = q.items[1:]\n\t\t\t\t\tq.lock.Unlock()\n\t\t\t\t\tconsumed <- item\n\t\t\t\t} else {\n\t\t\t\t\tq.lock.Unlock()\n\t\t\t\t}\n\t\t\t\t// Small sleep to increase chance of race conditions\n\t\t\t\ttime.Sleep(time.Microsecond)\n\t\t\t}\n\t\t}()\n\t}\n\n\t// Wait for all goroutines to finish\n\twg.Wait()\n\t// Close the consumed channel\n\tclose(consumed)\n\n\t// Count the number of consumed items\n\tconsumedCount := 0\n\tfor range consumed {\n\t\tconsumedCount++\n\t}\n\n\t// Check that the queue is in a consistent state\n\ttotalItems := goroutines * operations\n\tremaining := int(q.Len())\n\tassert.Equal(t, totalItems, consumedCount+remaining, \"Total items should equal consumed items plus remaining items\")\n}\n\n// TestQueueWithDifferentTypes tests the Queue with different types\nfunc TestQueueWithDifferentTypes(t *testing.T) {\n\t// Test with string type\n\tqString := New[string](5)\n\tqString.Put(\"hello\", \"world\")\n\tassert.Equal(t, int64(2), qString.Len(), \"Queue length should be 2\")\n\tassert.Equal(t, \"hello\", qString.Pop(), \"First item should be 'hello'\")\n\tassert.Equal(t, \"world\", qString.Pop(), \"Second item should be 'world'\")\n\n\t// Test with struct type\n\ttype Person struct {\n\t\tName string\n\t\tAge  int\n\t}\n\n\tqStruct := New[Person](5)\n\tqStruct.Put(Person{Name: \"Alice\", Age: 30}, Person{Name: \"Bob\", Age: 25})\n\tassert.Equal(t, int64(2), qStruct.Len(), \"Queue length should be 2\")\n\n\tfirstPerson := qStruct.Pop()\n\tassert.Equal(t, \"Alice\", firstPerson.Name, \"First person's name should be 'Alice'\")\n\tsecondPerson := qStruct.Pop()\n\tassert.Equal(t, \"Bob\", secondPerson.Name, \"Second person's name should be 'Bob'\")\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/singledo/singledo.go",
    "content": "package singledo\n\nimport (\n\t\"sync\"\n\t\"time\"\n)\n\ntype call[T any] struct {\n\twg  sync.WaitGroup\n\tval T\n\terr error\n}\n\ntype Single[T any] struct {\n\tmux    sync.Mutex\n\twait   time.Duration\n\tcall   *call[T]\n\tresult *Result[T]\n}\n\ntype Result[T any] struct {\n\tVal  T\n\tErr  error\n\tTime time.Time\n}\n\n// Do single.Do likes sync.singleFlight\nfunc (s *Single[T]) Do(fn func() (T, error)) (v T, err error, shared bool) {\n\ts.mux.Lock()\n\tresult := s.result\n\tif result != nil && time.Since(result.Time) < s.wait {\n\t\ts.mux.Unlock()\n\t\treturn result.Val, result.Err, true\n\t}\n\ts.result = nil // The result has expired, clear it\n\n\tif callM := s.call; callM != nil {\n\t\ts.mux.Unlock()\n\t\tcallM.wg.Wait()\n\t\treturn callM.val, callM.err, true\n\t}\n\n\tcallM := &call[T]{}\n\tcallM.wg.Add(1)\n\ts.call = callM\n\ts.mux.Unlock()\n\tcallM.val, callM.err = fn()\n\tcallM.wg.Done()\n\n\ts.mux.Lock()\n\tif s.call == callM { // maybe reset when fn is running\n\t\ts.call = nil\n\t\ts.result = &Result[T]{callM.val, callM.err, time.Now()}\n\t}\n\ts.mux.Unlock()\n\treturn callM.val, callM.err, false\n}\n\nfunc (s *Single[T]) Reset() {\n\ts.mux.Lock()\n\ts.call = nil\n\ts.result = nil\n\ts.mux.Unlock()\n}\n\nfunc NewSingle[T any](wait time.Duration) *Single[T] {\n\treturn &Single[T]{wait: wait}\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/singledo/singledo_test.go",
    "content": "package singledo\n\nimport (\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/atomic\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestBasic(t *testing.T) {\n\tt.Parallel()\n\tsingle := NewSingle[int](time.Millisecond * 200)\n\tfoo := 0\n\tshardCount := atomic.NewInt32(0)\n\tcall := func() (int, error) {\n\t\tfoo++\n\t\ttime.Sleep(time.Millisecond * 20)\n\t\treturn 0, nil\n\t}\n\n\tvar wg sync.WaitGroup\n\tconst n = 5\n\twg.Add(n)\n\tfor i := 0; i < n; i++ {\n\t\tgo func() {\n\t\t\t_, _, shard := single.Do(call)\n\t\t\tif shard {\n\t\t\t\tshardCount.Add(1)\n\t\t\t}\n\t\t\twg.Done()\n\t\t}()\n\t}\n\n\twg.Wait()\n\tassert.Equal(t, 1, foo)\n\tassert.Equal(t, int32(4), shardCount.Load())\n}\n\nfunc TestTimer(t *testing.T) {\n\tt.Parallel()\n\tsingle := NewSingle[int](time.Millisecond * 200)\n\tfoo := 0\n\tcallM := func() (int, error) {\n\t\tfoo++\n\t\treturn 0, nil\n\t}\n\n\t_, _, _ = single.Do(callM)\n\ttime.Sleep(100 * time.Millisecond)\n\t_, _, shard := single.Do(callM)\n\n\tassert.Equal(t, 1, foo)\n\tassert.True(t, shard)\n}\n\nfunc TestReset(t *testing.T) {\n\tt.Parallel()\n\tsingle := NewSingle[int](time.Millisecond * 200)\n\tfoo := 0\n\tcallM := func() (int, error) {\n\t\tfoo++\n\t\treturn 0, nil\n\t}\n\n\t_, _, _ = single.Do(callM)\n\tsingle.Reset()\n\t_, _, _ = single.Do(callM)\n\n\tassert.Equal(t, 2, foo)\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/singleflight/singleflight.go",
    "content": "// copy and modify from \"golang.org/x/sync/singleflight\"\n\n// Copyright 2013 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// Package singleflight provides a duplicate function call suppression\n// mechanism.\npackage singleflight\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"runtime\"\n\t\"runtime/debug\"\n\t\"sync\"\n)\n\n// errGoexit indicates the runtime.Goexit was called in\n// the user given function.\nvar errGoexit = errors.New(\"runtime.Goexit was called\")\n\n// A panicError is an arbitrary value recovered from a panic\n// with the stack trace during the execution of given function.\ntype panicError struct {\n\tvalue interface{}\n\tstack []byte\n}\n\n// Error implements error interface.\nfunc (p *panicError) Error() string {\n\treturn fmt.Sprintf(\"%v\\n\\n%s\", p.value, p.stack)\n}\n\nfunc (p *panicError) Unwrap() error {\n\terr, ok := p.value.(error)\n\tif !ok {\n\t\treturn nil\n\t}\n\n\treturn err\n}\n\nfunc newPanicError(v interface{}) error {\n\tstack := debug.Stack()\n\n\t// The first line of the stack trace is of the form \"goroutine N [status]:\"\n\t// but by the time the panic reaches Do the goroutine may no longer exist\n\t// and its status will have changed. Trim out the misleading line.\n\tif line := bytes.IndexByte(stack[:], '\\n'); line >= 0 {\n\t\tstack = stack[line+1:]\n\t}\n\treturn &panicError{value: v, stack: stack}\n}\n\n// call is an in-flight or completed singleflight.Do call\ntype call[T any] struct {\n\twg sync.WaitGroup\n\n\t// These fields are written once before the WaitGroup is done\n\t// and are only read after the WaitGroup is done.\n\tval T\n\terr error\n\n\t// These fields are read and written with the singleflight\n\t// mutex held before the WaitGroup is done, and are read but\n\t// not written after the WaitGroup is done.\n\tdups  int\n\tchans []chan<- Result[T]\n}\n\n// Group represents a class of work and forms a namespace in\n// which units of work can be executed with duplicate suppression.\ntype Group[T any] struct {\n\tmu sync.Mutex          // protects m\n\tm  map[string]*call[T] // lazily initialized\n\n\tStoreResult bool\n}\n\n// Result holds the results of Do, so they can be passed\n// on a channel.\ntype Result[T any] struct {\n\tVal    T\n\tErr    error\n\tShared bool\n}\n\n// Do executes and returns the results of the given function, making\n// sure that only one execution is in-flight for a given key at a\n// time. If a duplicate comes in, the duplicate caller waits for the\n// original to complete and receives the same results.\n// The return value shared indicates whether v was given to multiple callers.\nfunc (g *Group[T]) Do(key string, fn func() (T, error)) (v T, err error, shared bool) {\n\tg.mu.Lock()\n\tif g.m == nil {\n\t\tg.m = make(map[string]*call[T])\n\t}\n\tif c, ok := g.m[key]; ok {\n\t\tc.dups++\n\t\tg.mu.Unlock()\n\t\tc.wg.Wait()\n\n\t\tif e, ok := c.err.(*panicError); ok {\n\t\t\tpanic(e)\n\t\t} else if c.err == errGoexit {\n\t\t\truntime.Goexit()\n\t\t}\n\t\treturn c.val, c.err, true\n\t}\n\tc := new(call[T])\n\tc.wg.Add(1)\n\tg.m[key] = c\n\tg.mu.Unlock()\n\n\tg.doCall(c, key, fn)\n\treturn c.val, c.err, c.dups > 0\n}\n\n// DoChan is like Do but returns a channel that will receive the\n// results when they are ready.\n//\n// The returned channel will not be closed.\nfunc (g *Group[T]) DoChan(key string, fn func() (T, error)) <-chan Result[T] {\n\tch := make(chan Result[T], 1)\n\tg.mu.Lock()\n\tif g.m == nil {\n\t\tg.m = make(map[string]*call[T])\n\t}\n\tif c, ok := g.m[key]; ok {\n\t\tc.dups++\n\t\tc.chans = append(c.chans, ch)\n\t\tg.mu.Unlock()\n\t\treturn ch\n\t}\n\tc := &call[T]{chans: []chan<- Result[T]{ch}}\n\tc.wg.Add(1)\n\tg.m[key] = c\n\tg.mu.Unlock()\n\n\tgo g.doCall(c, key, fn)\n\n\treturn ch\n}\n\n// doCall handles the single call for a key.\nfunc (g *Group[T]) doCall(c *call[T], key string, fn func() (T, error)) {\n\tnormalReturn := false\n\trecovered := false\n\n\t// use double-defer to distinguish panic from runtime.Goexit,\n\t// more details see https://golang.org/cl/134395\n\tdefer func() {\n\t\t// the given function invoked runtime.Goexit\n\t\tif !normalReturn && !recovered {\n\t\t\tc.err = errGoexit\n\t\t}\n\n\t\tg.mu.Lock()\n\t\tdefer g.mu.Unlock()\n\t\tc.wg.Done()\n\t\tif g.m[key] == c && !g.StoreResult {\n\t\t\tdelete(g.m, key)\n\t\t}\n\n\t\tif e, ok := c.err.(*panicError); ok {\n\t\t\t// In order to prevent the waiting channels from being blocked forever,\n\t\t\t// needs to ensure that this panic cannot be recovered.\n\t\t\tif len(c.chans) > 0 {\n\t\t\t\tgo panic(e)\n\t\t\t\tselect {} // Keep this goroutine around so that it will appear in the crash dump.\n\t\t\t} else {\n\t\t\t\tpanic(e)\n\t\t\t}\n\t\t} else if c.err == errGoexit {\n\t\t\t// Already in the process of goexit, no need to call again\n\t\t} else {\n\t\t\t// Normal return\n\t\t\tfor _, ch := range c.chans {\n\t\t\t\tch <- Result[T]{c.val, c.err, c.dups > 0}\n\t\t\t}\n\t\t}\n\t}()\n\n\tfunc() {\n\t\tdefer func() {\n\t\t\tif !normalReturn {\n\t\t\t\t// Ideally, we would wait to take a stack trace until we've determined\n\t\t\t\t// whether this is a panic or a runtime.Goexit.\n\t\t\t\t//\n\t\t\t\t// Unfortunately, the only way we can distinguish the two is to see\n\t\t\t\t// whether the recover stopped the goroutine from terminating, and by\n\t\t\t\t// the time we know that, the part of the stack trace relevant to the\n\t\t\t\t// panic has been discarded.\n\t\t\t\tif r := recover(); r != nil {\n\t\t\t\t\tc.err = newPanicError(r)\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\n\t\tc.val, c.err = fn()\n\t\tnormalReturn = true\n\t}()\n\n\tif !normalReturn {\n\t\trecovered = true\n\t}\n}\n\n// Forget tells the singleflight to forget about a key.  Future calls\n// to Do for this key will call the function rather than waiting for\n// an earlier call to complete.\nfunc (g *Group[T]) Forget(key string) {\n\tg.mu.Lock()\n\tdelete(g.m, key)\n\tg.mu.Unlock()\n}\n\nfunc (g *Group[T]) Reset() {\n\tg.mu.Lock()\n\tg.m = nil\n\tg.mu.Unlock()\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/sockopt/reuse_common.go",
    "content": "package sockopt\n\nimport (\n\t\"net\"\n\t\"syscall\"\n)\n\nfunc RawConnReuseaddr(rc syscall.RawConn) (err error) {\n\tvar innerErr error\n\terr = rc.Control(func(fd uintptr) {\n\t\tinnerErr = reuseControl(fd)\n\t})\n\n\tif innerErr != nil {\n\t\terr = innerErr\n\t}\n\treturn\n}\n\nfunc UDPReuseaddr(c net.PacketConn) error {\n\tif c, ok := c.(syscall.Conn); ok {\n\t\trc, err := c.SyscallConn()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn RawConnReuseaddr(rc)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/sockopt/reuse_other.go",
    "content": "//go:build !darwin && !dragonfly && !freebsd && !linux && !netbsd && !openbsd && !solaris && !windows\n\npackage sockopt\n\nfunc reuseControl(fd uintptr) error { return nil }\n"
  },
  {
    "path": "core/Clash.Meta/common/sockopt/reuse_unix.go",
    "content": "//go:build darwin || dragonfly || freebsd || linux || netbsd || openbsd || solaris\n\npackage sockopt\n\nimport (\n\t\"golang.org/x/sys/unix\"\n)\n\nfunc reuseControl(fd uintptr) error {\n\te1 := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEADDR, 1)\n\te2 := unix.SetsockoptInt(int(fd), unix.SOL_SOCKET, unix.SO_REUSEPORT, 1)\n\n\tif e1 != nil {\n\t\treturn e1\n\t}\n\n\tif e2 != nil {\n\t\treturn e2\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/sockopt/reuse_windows.go",
    "content": "package sockopt\n\nimport (\n\t\"golang.org/x/sys/windows\"\n)\n\nfunc reuseControl(fd uintptr) error {\n\treturn windows.SetsockoptInt(windows.Handle(fd), windows.SOL_SOCKET, windows.SO_REUSEADDR, 1)\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/structure/structure.go",
    "content": "package structure\n\n// references: https://github.com/mitchellh/mapstructure\n\nimport (\n\t\"encoding\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"sort\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// Option is the configuration that is used to create a new decoder\ntype Option struct {\n\tTagName          string\n\tWeaklyTypedInput bool\n\tKeyReplacer      *strings.Replacer\n}\n\nvar DefaultKeyReplacer = strings.NewReplacer(\"_\", \"-\")\n\n// Decoder is the core of structure\ntype Decoder struct {\n\toption *Option\n}\n\n// NewDecoder return a Decoder by Option\nfunc NewDecoder(option Option) *Decoder {\n\tif option.TagName == \"\" {\n\t\toption.TagName = \"structure\"\n\t}\n\treturn &Decoder{option: &option}\n}\n\n// Decode transform a map[string]any to a struct\nfunc (d *Decoder) Decode(src map[string]any, dst any) error {\n\tif reflect.TypeOf(dst).Kind() != reflect.Ptr {\n\t\treturn fmt.Errorf(\"decode must recive a ptr struct\")\n\t}\n\treturn d.decode(\"\", src, reflect.ValueOf(dst).Elem())\n}\n\n// isNil returns true if the input is nil or a typed nil pointer.\nfunc isNil(input any) bool {\n\tif input == nil {\n\t\treturn true\n\t}\n\tval := reflect.ValueOf(input)\n\treturn val.Kind() == reflect.Pointer && val.IsNil()\n}\n\nfunc (d *Decoder) decode(name string, data any, val reflect.Value) error {\n\tif isNil(data) {\n\t\t// If the data is nil, then we don't set anything\n\t\t// Maybe we should set to zero value?\n\t\treturn nil\n\t}\n\tif !reflect.ValueOf(data).IsValid() {\n\t\t// If the input value is invalid, then we just set the value\n\t\t// to be the zero value.\n\t\tval.Set(reflect.Zero(val.Type()))\n\t\treturn nil\n\t}\n\tfor {\n\t\tkind := val.Kind()\n\t\tif kind == reflect.Pointer && val.IsNil() {\n\t\t\tval.Set(reflect.New(val.Type().Elem()))\n\t\t}\n\t\tif ok, err := d.decodeTextUnmarshaller(name, data, val); ok {\n\t\t\treturn err\n\t\t}\n\t\tswitch {\n\t\tcase isInt(kind):\n\t\t\treturn d.decodeInt(name, data, val)\n\t\tcase isUint(kind):\n\t\t\treturn d.decodeUint(name, data, val)\n\t\tcase isFloat(kind):\n\t\t\treturn d.decodeFloat(name, data, val)\n\t\t}\n\t\tswitch kind {\n\t\tcase reflect.Pointer:\n\t\t\tval = val.Elem()\n\t\t\tcontinue\n\t\tcase reflect.String:\n\t\t\treturn d.decodeString(name, data, val)\n\t\tcase reflect.Bool:\n\t\t\treturn d.decodeBool(name, data, val)\n\t\tcase reflect.Slice:\n\t\t\treturn d.decodeSlice(name, data, val)\n\t\tcase reflect.Map:\n\t\t\treturn d.decodeMap(name, data, val)\n\t\tcase reflect.Interface:\n\t\t\treturn d.setInterface(name, data, val)\n\t\tcase reflect.Struct:\n\t\t\treturn d.decodeStruct(name, data, val)\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"type %s not support\", val.Kind().String())\n\t\t}\n\t}\n}\n\nfunc isInt(kind reflect.Kind) bool {\n\tswitch kind {\n\tcase reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc isUint(kind reflect.Kind) bool {\n\tswitch kind {\n\tcase reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc isFloat(kind reflect.Kind) bool {\n\tswitch kind {\n\tcase reflect.Float32, reflect.Float64:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc (d *Decoder) decodeInt(name string, data any, val reflect.Value) (err error) {\n\tdataVal := reflect.ValueOf(data)\n\tkind := dataVal.Kind()\n\tswitch {\n\tcase isInt(kind):\n\t\tval.SetInt(dataVal.Int())\n\tcase isUint(kind) && d.option.WeaklyTypedInput:\n\t\tval.SetInt(int64(dataVal.Uint()))\n\tcase isFloat(kind) && d.option.WeaklyTypedInput:\n\t\tval.SetInt(int64(dataVal.Float()))\n\tcase kind == reflect.String && d.option.WeaklyTypedInput:\n\t\tvar i int64\n\t\ti, err = strconv.ParseInt(dataVal.String(), 0, val.Type().Bits())\n\t\tif err == nil {\n\t\t\tval.SetInt(i)\n\t\t} else {\n\t\t\terr = fmt.Errorf(\"cannot parse '%s' as int: %s\", name, err)\n\t\t}\n\tdefault:\n\t\terr = fmt.Errorf(\n\t\t\t\"'%s' expected type '%s', got unconvertible type '%s'\",\n\t\t\tname, val.Type(), dataVal.Type(),\n\t\t)\n\t}\n\treturn err\n}\n\nfunc (d *Decoder) decodeUint(name string, data any, val reflect.Value) (err error) {\n\tdataVal := reflect.ValueOf(data)\n\tkind := dataVal.Kind()\n\tswitch {\n\tcase isUint(kind):\n\t\tval.SetUint(dataVal.Uint())\n\tcase isInt(kind) && d.option.WeaklyTypedInput:\n\t\tval.SetUint(uint64(dataVal.Int()))\n\tcase isFloat(kind) && d.option.WeaklyTypedInput:\n\t\tval.SetUint(uint64(dataVal.Float()))\n\tcase kind == reflect.String && d.option.WeaklyTypedInput:\n\t\tvar i uint64\n\t\ti, err = strconv.ParseUint(dataVal.String(), 0, val.Type().Bits())\n\t\tif err == nil {\n\t\t\tval.SetUint(i)\n\t\t} else {\n\t\t\terr = fmt.Errorf(\"cannot parse '%s' as int: %s\", name, err)\n\t\t}\n\tdefault:\n\t\terr = fmt.Errorf(\n\t\t\t\"'%s' expected type '%s', got unconvertible type '%s'\",\n\t\t\tname, val.Type(), dataVal.Type(),\n\t\t)\n\t}\n\treturn err\n}\n\nfunc (d *Decoder) decodeFloat(name string, data any, val reflect.Value) (err error) {\n\tdataVal := reflect.ValueOf(data)\n\tkind := dataVal.Kind()\n\tswitch {\n\tcase isFloat(kind):\n\t\tval.SetFloat(dataVal.Float())\n\tcase isUint(kind):\n\t\tval.SetFloat(float64(dataVal.Uint()))\n\tcase isInt(kind) && d.option.WeaklyTypedInput:\n\t\tval.SetFloat(float64(dataVal.Int()))\n\tcase kind == reflect.String && d.option.WeaklyTypedInput:\n\t\tvar i float64\n\t\ti, err = strconv.ParseFloat(dataVal.String(), val.Type().Bits())\n\t\tif err == nil {\n\t\t\tval.SetFloat(i)\n\t\t} else {\n\t\t\terr = fmt.Errorf(\"cannot parse '%s' as int: %s\", name, err)\n\t\t}\n\tdefault:\n\t\terr = fmt.Errorf(\n\t\t\t\"'%s' expected type '%s', got unconvertible type '%s'\",\n\t\t\tname, val.Type(), dataVal.Type(),\n\t\t)\n\t}\n\treturn err\n}\n\nfunc (d *Decoder) decodeString(name string, data any, val reflect.Value) (err error) {\n\tdataVal := reflect.ValueOf(data)\n\tkind := dataVal.Kind()\n\tswitch {\n\tcase kind == reflect.String:\n\t\tval.SetString(dataVal.String())\n\tcase isInt(kind) && d.option.WeaklyTypedInput:\n\t\tval.SetString(strconv.FormatInt(dataVal.Int(), 10))\n\tcase isUint(kind) && d.option.WeaklyTypedInput:\n\t\tval.SetString(strconv.FormatUint(dataVal.Uint(), 10))\n\tcase isFloat(kind) && d.option.WeaklyTypedInput:\n\t\tval.SetString(strconv.FormatFloat(dataVal.Float(), 'E', -1, dataVal.Type().Bits()))\n\tdefault:\n\t\terr = fmt.Errorf(\n\t\t\t\"'%s' expected type '%s', got unconvertible type '%s'\",\n\t\t\tname, val.Type(), dataVal.Type(),\n\t\t)\n\t}\n\treturn err\n}\n\nfunc (d *Decoder) decodeBool(name string, data any, val reflect.Value) (err error) {\n\tdataVal := reflect.ValueOf(data)\n\tkind := dataVal.Kind()\n\tswitch {\n\tcase kind == reflect.Bool:\n\t\tval.SetBool(dataVal.Bool())\n\tcase isInt(kind) && d.option.WeaklyTypedInput:\n\t\tval.SetBool(dataVal.Int() != 0)\n\tcase isUint(kind) && d.option.WeaklyTypedInput:\n\t\tval.SetBool(dataVal.Uint() != 0)\n\tdefault:\n\t\terr = fmt.Errorf(\n\t\t\t\"'%s' expected type '%s', got unconvertible type '%s'\",\n\t\t\tname, val.Type(), dataVal.Type(),\n\t\t)\n\t}\n\treturn err\n}\n\nfunc (d *Decoder) decodeSlice(name string, data any, val reflect.Value) error {\n\tdataVal := reflect.Indirect(reflect.ValueOf(data))\n\tvalType := val.Type()\n\tvalElemType := valType.Elem()\n\n\tif dataVal.Kind() == reflect.String && valElemType.Kind() == reflect.Uint8 { // from encoding/json\n\t\ts := []byte(dataVal.String())\n\t\tb := make([]byte, base64.StdEncoding.DecodedLen(len(s)))\n\t\tn, err := base64.StdEncoding.Decode(b, s)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"try decode '%s' by base64 error: %w\", name, err)\n\t\t}\n\t\tval.SetBytes(b[:n])\n\t\treturn nil\n\t}\n\n\tif dataVal.Kind() != reflect.Slice {\n\t\treturn fmt.Errorf(\"'%s' is not a slice\", name)\n\t}\n\n\tvalSlice := val\n\t// make a new slice with cap(val)==cap(dataVal)\n\t// the caller can determine whether the original configuration contains this item by judging whether the value is nil.\n\tvalSlice = reflect.MakeSlice(valType, 0, dataVal.Len())\n\tfor i := 0; i < dataVal.Len(); i++ {\n\t\tcurrentData := dataVal.Index(i).Interface()\n\t\tfor valSlice.Len() <= i {\n\t\t\tvalSlice = reflect.Append(valSlice, reflect.Zero(valElemType))\n\t\t}\n\t\tfieldName := fmt.Sprintf(\"%s[%d]\", name, i)\n\t\tif currentData == nil {\n\t\t\t// in weakly type mode, null will convert to zero value\n\t\t\tif d.option.WeaklyTypedInput {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t// in non-weakly type mode, null will convert to nil if element's zero value is nil, otherwise return an error\n\t\t\tif elemKind := valElemType.Kind(); elemKind == reflect.Map || elemKind == reflect.Slice {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\treturn fmt.Errorf(\"'%s' can not be null\", fieldName)\n\t\t}\n\t\tcurrentField := valSlice.Index(i)\n\t\tif err := d.decode(fieldName, currentData, currentField); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tval.Set(valSlice)\n\treturn nil\n}\n\nfunc (d *Decoder) decodeMap(name string, data any, val reflect.Value) error {\n\tvalType := val.Type()\n\tvalKeyType := valType.Key()\n\tvalElemType := valType.Elem()\n\n\tvalMap := val\n\n\tif valMap.IsNil() {\n\t\tmapType := reflect.MapOf(valKeyType, valElemType)\n\t\tvalMap = reflect.MakeMap(mapType)\n\t}\n\n\tdataVal := reflect.Indirect(reflect.ValueOf(data))\n\tif dataVal.Kind() != reflect.Map {\n\t\treturn fmt.Errorf(\"'%s' expected a map, got '%s'\", name, dataVal.Kind())\n\t}\n\n\treturn d.decodeMapFromMap(name, dataVal, val, valMap)\n}\n\nfunc (d *Decoder) decodeMapFromMap(name string, dataVal reflect.Value, val reflect.Value, valMap reflect.Value) error {\n\tvalType := val.Type()\n\tvalKeyType := valType.Key()\n\tvalElemType := valType.Elem()\n\n\terrors := make([]string, 0)\n\n\tif dataVal.Len() == 0 {\n\t\tif dataVal.IsNil() {\n\t\t\tif !val.IsNil() {\n\t\t\t\tval.Set(dataVal)\n\t\t\t}\n\t\t} else {\n\t\t\tval.Set(valMap)\n\t\t}\n\n\t\treturn nil\n\t}\n\n\tfor _, k := range dataVal.MapKeys() {\n\t\tfieldName := fmt.Sprintf(\"%s[%s]\", name, k)\n\n\t\tcurrentKey := reflect.Indirect(reflect.New(valKeyType))\n\t\tif err := d.decode(fieldName, k.Interface(), currentKey); err != nil {\n\t\t\terrors = append(errors, err.Error())\n\t\t\tcontinue\n\t\t}\n\n\t\tv := dataVal.MapIndex(k).Interface()\n\t\tif v == nil {\n\t\t\terrors = append(errors, fmt.Sprintf(\"filed %s invalid\", fieldName))\n\t\t\tcontinue\n\t\t}\n\n\t\tcurrentVal := reflect.Indirect(reflect.New(valElemType))\n\t\tif err := d.decode(fieldName, v, currentVal); err != nil {\n\t\t\terrors = append(errors, err.Error())\n\t\t\tcontinue\n\t\t}\n\n\t\tvalMap.SetMapIndex(currentKey, currentVal)\n\t}\n\n\tval.Set(valMap)\n\n\tif len(errors) > 0 {\n\t\treturn fmt.Errorf(strings.Join(errors, \",\"))\n\t}\n\n\treturn nil\n}\n\nfunc (d *Decoder) decodeStruct(name string, data any, val reflect.Value) error {\n\tdataVal := reflect.Indirect(reflect.ValueOf(data))\n\n\t// If the type of the value to write to and the data match directly,\n\t// then we just set it directly instead of recursing into the structure.\n\tif dataVal.Type() == val.Type() {\n\t\tval.Set(dataVal)\n\t\treturn nil\n\t}\n\n\tdataValKind := dataVal.Kind()\n\tswitch dataValKind {\n\tcase reflect.Map:\n\t\treturn d.decodeStructFromMap(name, dataVal, val)\n\tdefault:\n\t\treturn fmt.Errorf(\"'%s' expected a map, got '%s'\", name, dataVal.Kind())\n\t}\n}\n\nfunc (d *Decoder) decodeStructFromMap(name string, dataVal, val reflect.Value) error {\n\tdataValType := dataVal.Type()\n\tif kind := dataValType.Key().Kind(); kind != reflect.String && kind != reflect.Interface {\n\t\treturn fmt.Errorf(\n\t\t\t\"'%s' needs a map with string keys, has '%s' keys\",\n\t\t\tname, dataValType.Key().Kind())\n\t}\n\n\tdataValKeys := make(map[reflect.Value]struct{})\n\tdataValKeysUnused := make(map[any]struct{})\n\tfor _, dataValKey := range dataVal.MapKeys() {\n\t\tdataValKeys[dataValKey] = struct{}{}\n\t\tdataValKeysUnused[dataValKey.Interface()] = struct{}{}\n\t}\n\n\ttargetValKeysUnused := make(map[any]struct{})\n\terrors := make([]string, 0)\n\n\t// This slice will keep track of all the structs we'll be decoding.\n\t// There can be more than one struct if there are embedded structs\n\t// that are squashed.\n\tstructs := make([]reflect.Value, 1, 5)\n\tstructs[0] = val\n\n\t// Compile the list of all the fields that we're going to be decoding\n\t// from all the structs.\n\ttype field struct {\n\t\tfield reflect.StructField\n\t\tval   reflect.Value\n\t}\n\n\t// remainField is set to a valid field set with the \"remain\" tag if\n\t// we are keeping track of remaining values.\n\tvar remainField *field\n\n\tvar fields []field\n\tfor len(structs) > 0 {\n\t\tstructVal := structs[0]\n\t\tstructs = structs[1:]\n\n\t\tstructType := structVal.Type()\n\n\t\tfor i := 0; i < structType.NumField(); i++ {\n\t\t\tfieldType := structType.Field(i)\n\t\t\tfieldVal := structVal.Field(i)\n\t\t\tif fieldVal.Kind() == reflect.Ptr && fieldVal.Elem().Kind() == reflect.Struct {\n\t\t\t\t// Handle embedded struct pointers as embedded structs.\n\t\t\t\tfieldVal = fieldVal.Elem()\n\t\t\t}\n\n\t\t\t// If \"squash\" is specified in the tag, we squash the field down.\n\t\t\tsquash := fieldVal.Kind() == reflect.Struct && fieldType.Anonymous\n\t\t\tremain := false\n\n\t\t\t// We always parse the tags cause we're looking for other tags too\n\t\t\ttagParts := strings.Split(fieldType.Tag.Get(d.option.TagName), \",\")\n\t\t\tfor _, tag := range tagParts[1:] {\n\t\t\t\tif tag == \"squash\" {\n\t\t\t\t\tsquash = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\n\t\t\t\tif tag == \"remain\" {\n\t\t\t\t\tremain = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif squash {\n\t\t\t\tif fieldVal.Kind() != reflect.Struct {\n\t\t\t\t\terrors = append(errors,\n\t\t\t\t\t\tfmt.Errorf(\"%s: unsupported type for squash: %s\", fieldType.Name, fieldVal.Kind()).Error())\n\t\t\t\t} else {\n\t\t\t\t\tstructs = append(structs, fieldVal)\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Build our field\n\t\t\tif remain {\n\t\t\t\tremainField = &field{fieldType, fieldVal}\n\t\t\t} else {\n\t\t\t\t// Normal struct field, store it away\n\t\t\t\tfields = append(fields, field{fieldType, fieldVal})\n\t\t\t}\n\t\t}\n\t}\n\n\t// for fieldType, field := range fields {\n\tfor _, f := range fields {\n\t\tfield, fieldValue := f.field, f.val\n\t\tfieldName := field.Name\n\n\t\ttagParts := strings.Split(field.Tag.Get(d.option.TagName), \",\")\n\t\ttagValue := tagParts[0]\n\t\tif tagValue != \"\" {\n\t\t\tfieldName = tagValue\n\t\t}\n\n\t\tif tagValue == \"-\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tomitempty := false\n\t\tfor _, tag := range tagParts[1:] {\n\t\t\tif tag == \"omitempty\" {\n\t\t\t\tomitempty = true\n\t\t\t}\n\t\t}\n\n\t\trawMapKey := reflect.ValueOf(fieldName)\n\t\trawMapVal := dataVal.MapIndex(rawMapKey)\n\t\tif !rawMapVal.IsValid() {\n\t\t\t// Do a slower search by iterating over each key and\n\t\t\t// doing case-insensitive search.\n\t\t\tif d.option.KeyReplacer != nil {\n\t\t\t\tfieldName = d.option.KeyReplacer.Replace(fieldName)\n\t\t\t}\n\t\t\tfor dataValKey := range dataValKeys {\n\t\t\t\tmK, ok := dataValKey.Interface().(string)\n\t\t\t\tif !ok {\n\t\t\t\t\t// Not a string key\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif d.option.KeyReplacer != nil {\n\t\t\t\t\tmK = d.option.KeyReplacer.Replace(mK)\n\t\t\t\t}\n\n\t\t\t\tif strings.EqualFold(mK, fieldName) {\n\t\t\t\t\trawMapKey = dataValKey\n\t\t\t\t\trawMapVal = dataVal.MapIndex(dataValKey)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif !rawMapVal.IsValid() {\n\t\t\t\t// There was no matching key in the map for the value in\n\t\t\t\t// the struct. Remember it for potential errors and metadata.\n\t\t\t\tif !omitempty {\n\t\t\t\t\ttargetValKeysUnused[fieldName] = struct{}{}\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\t// Delete the key we're using from the unused map so we stop tracking\n\t\tdelete(dataValKeysUnused, rawMapKey.Interface())\n\n\t\tif !fieldValue.IsValid() {\n\t\t\t// This should never happen\n\t\t\tpanic(\"field is not valid\")\n\t\t}\n\n\t\t// If we can't set the field, then it is unexported or something,\n\t\t// and we just continue onwards.\n\t\tif !fieldValue.CanSet() {\n\t\t\tcontinue\n\t\t}\n\n\t\t// If the name is empty string, then we're at the root, and we\n\t\t// don't dot-join the fields.\n\t\tif name != \"\" {\n\t\t\tfieldName = name + \".\" + fieldName\n\t\t}\n\n\t\tif err := d.decode(fieldName, rawMapVal.Interface(), fieldValue); err != nil {\n\t\t\terrors = append(errors, err.Error())\n\t\t}\n\t}\n\n\t// If we have a \"remain\"-tagged field and we have unused keys then\n\t// we put the unused keys directly into the remain field.\n\tif remainField != nil && len(dataValKeysUnused) > 0 {\n\t\t// Build a map of only the unused values\n\t\tremain := map[interface{}]interface{}{}\n\t\tfor key := range dataValKeysUnused {\n\t\t\tremain[key] = dataVal.MapIndex(reflect.ValueOf(key)).Interface()\n\t\t}\n\n\t\t// Decode it as-if we were just decoding this map onto our map.\n\t\tif err := d.decodeMap(name, remain, remainField.val); err != nil {\n\t\t\terrors = append(errors, err.Error())\n\t\t}\n\n\t\t// Set the map to nil so we have none so that the next check will\n\t\t// not error (ErrorUnused)\n\t\tdataValKeysUnused = nil\n\t}\n\n\tif len(targetValKeysUnused) > 0 {\n\t\tkeys := make([]string, 0, len(targetValKeysUnused))\n\t\tfor rawKey := range targetValKeysUnused {\n\t\t\tkeys = append(keys, rawKey.(string))\n\t\t}\n\t\tsort.Strings(keys)\n\n\t\terr := fmt.Errorf(\"'%s' has unset fields: %s\", name, strings.Join(keys, \", \"))\n\t\terrors = append(errors, err.Error())\n\t}\n\n\tif len(errors) > 0 {\n\t\treturn fmt.Errorf(strings.Join(errors, \",\"))\n\t}\n\n\treturn nil\n}\n\nfunc (d *Decoder) setInterface(name string, data any, val reflect.Value) (err error) {\n\tdataVal := reflect.ValueOf(data)\n\tval.Set(dataVal)\n\treturn nil\n}\n\nfunc (d *Decoder) decodeTextUnmarshaller(name string, data any, val reflect.Value) (bool, error) {\n\tif !val.CanAddr() {\n\t\treturn false, nil\n\t}\n\tvalAddr := val.Addr()\n\tif !valAddr.CanInterface() {\n\t\treturn false, nil\n\t}\n\tunmarshaller, ok := valAddr.Interface().(encoding.TextUnmarshaler)\n\tif !ok {\n\t\treturn false, nil\n\t}\n\tvar str string\n\tif err := d.decodeString(name, data, reflect.Indirect(reflect.ValueOf(&str))); err != nil {\n\t\treturn false, err\n\t}\n\tif err := unmarshaller.UnmarshalText([]byte(str)); err != nil {\n\t\treturn true, fmt.Errorf(\"cannot parse '%s' as %s: %s\", name, val.Type(), err)\n\t}\n\treturn true, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/structure/structure_test.go",
    "content": "package structure\n\nimport (\n\t\"strconv\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nvar (\n\tdecoder         = NewDecoder(Option{TagName: \"test\"})\n\tweakTypeDecoder = NewDecoder(Option{TagName: \"test\", WeaklyTypedInput: true})\n)\n\ntype Baz struct {\n\tFoo int    `test:\"foo\"`\n\tBar string `test:\"bar\"`\n}\n\ntype BazSlice struct {\n\tFoo int      `test:\"foo\"`\n\tBar []string `test:\"bar\"`\n}\n\ntype BazOptional struct {\n\tFoo int    `test:\"foo,omitempty\"`\n\tBar string `test:\"bar,omitempty\"`\n}\n\nfunc TestStructure_Basic(t *testing.T) {\n\trawMap := map[string]any{\n\t\t\"foo\":   1,\n\t\t\"bar\":   \"test\",\n\t\t\"extra\": false,\n\t}\n\n\tgoal := &Baz{\n\t\tFoo: 1,\n\t\tBar: \"test\",\n\t}\n\n\ts := &Baz{}\n\terr := decoder.Decode(rawMap, s)\n\tassert.Nil(t, err)\n\tassert.Equal(t, goal, s)\n}\n\nfunc TestStructure_Slice(t *testing.T) {\n\trawMap := map[string]any{\n\t\t\"foo\": 1,\n\t\t\"bar\": []string{\"one\", \"two\"},\n\t}\n\n\tgoal := &BazSlice{\n\t\tFoo: 1,\n\t\tBar: []string{\"one\", \"two\"},\n\t}\n\n\ts := &BazSlice{}\n\terr := decoder.Decode(rawMap, s)\n\tassert.Nil(t, err)\n\tassert.Equal(t, goal, s)\n}\n\nfunc TestStructure_Optional(t *testing.T) {\n\trawMap := map[string]any{\n\t\t\"foo\": 1,\n\t}\n\n\tgoal := &BazOptional{\n\t\tFoo: 1,\n\t}\n\n\ts := &BazOptional{}\n\terr := decoder.Decode(rawMap, s)\n\tassert.Nil(t, err)\n\tassert.Equal(t, goal, s)\n}\n\nfunc TestStructure_MissingKey(t *testing.T) {\n\trawMap := map[string]any{\n\t\t\"foo\": 1,\n\t}\n\n\ts := &Baz{}\n\terr := decoder.Decode(rawMap, s)\n\tassert.NotNilf(t, err, \"should throw error: %#v\", s)\n}\n\nfunc TestStructure_ParamError(t *testing.T) {\n\trawMap := map[string]any{}\n\ts := Baz{}\n\terr := decoder.Decode(rawMap, s)\n\tassert.NotNilf(t, err, \"should throw error: %#v\", s)\n}\n\nfunc TestStructure_SliceTypeError(t *testing.T) {\n\trawMap := map[string]any{\n\t\t\"foo\": 1,\n\t\t\"bar\": []int{1, 2},\n\t}\n\n\ts := &BazSlice{}\n\terr := decoder.Decode(rawMap, s)\n\tassert.NotNilf(t, err, \"should throw error: %#v\", s)\n}\n\nfunc TestStructure_WeakType(t *testing.T) {\n\trawMap := map[string]any{\n\t\t\"foo\": \"1\",\n\t\t\"bar\": []int{1},\n\t}\n\n\tgoal := &BazSlice{\n\t\tFoo: 1,\n\t\tBar: []string{\"1\"},\n\t}\n\n\ts := &BazSlice{}\n\terr := weakTypeDecoder.Decode(rawMap, s)\n\tassert.Nil(t, err)\n\tassert.Equal(t, goal, s)\n}\n\nfunc TestStructure_Nest(t *testing.T) {\n\trawMap := map[string]any{\n\t\t\"foo\": 1,\n\t}\n\n\tgoal := BazOptional{\n\t\tFoo: 1,\n\t}\n\n\ts := &struct {\n\t\tBazOptional\n\t}{}\n\terr := decoder.Decode(rawMap, s)\n\tassert.Nil(t, err)\n\tassert.Equal(t, s.BazOptional, goal)\n}\n\nfunc TestStructure_DoubleNest(t *testing.T) {\n\trawMap := map[string]any{\n\t\t\"bar\": map[string]any{\n\t\t\t\"foo\": 1,\n\t\t},\n\t}\n\n\tgoal := BazOptional{\n\t\tFoo: 1,\n\t}\n\n\ts := &struct {\n\t\tBar struct {\n\t\t\tBazOptional\n\t\t} `test:\"bar\"`\n\t}{}\n\terr := decoder.Decode(rawMap, s)\n\tassert.Nil(t, err)\n\tassert.Equal(t, s.Bar.BazOptional, goal)\n}\n\nfunc TestStructure_Remain(t *testing.T) {\n\trawMap := map[string]any{\n\t\t\"foo\":   1,\n\t\t\"bar\":   \"test\",\n\t\t\"extra\": false,\n\t}\n\n\tgoal := &Baz{\n\t\tFoo: 1,\n\t\tBar: \"test\",\n\t}\n\n\ts := &struct {\n\t\tBaz\n\t\tRemain map[string]any `test:\",remain\"`\n\t}{}\n\terr := decoder.Decode(rawMap, s)\n\tassert.Nil(t, err)\n\tassert.Equal(t, *goal, s.Baz)\n\tassert.Equal(t, map[string]any{\"extra\": false}, s.Remain)\n}\n\nfunc TestStructure_SliceNilValue(t *testing.T) {\n\trawMap := map[string]any{\n\t\t\"foo\": 1,\n\t\t\"bar\": []any{\"bar\", nil},\n\t}\n\n\tgoal := &BazSlice{\n\t\tFoo: 1,\n\t\tBar: []string{\"bar\", \"\"},\n\t}\n\n\ts := &BazSlice{}\n\terr := weakTypeDecoder.Decode(rawMap, s)\n\tassert.Nil(t, err)\n\tassert.Equal(t, goal.Bar, s.Bar)\n\n\ts = &BazSlice{}\n\terr = decoder.Decode(rawMap, s)\n\tassert.NotNil(t, err)\n}\n\nfunc TestStructure_SliceNilValueComplex(t *testing.T) {\n\trawMap := map[string]any{\n\t\t\"bar\": []any{map[string]any{\"bar\": \"foo\"}, nil},\n\t}\n\n\ts := &struct {\n\t\tBar []map[string]any `test:\"bar\"`\n\t}{}\n\n\terr := decoder.Decode(rawMap, s)\n\tassert.Nil(t, err)\n\tassert.Nil(t, s.Bar[1])\n\n\tss := &struct {\n\t\tBar []Baz `test:\"bar\"`\n\t}{}\n\n\terr = decoder.Decode(rawMap, ss)\n\tassert.NotNil(t, err)\n}\n\nfunc TestStructure_SliceCap(t *testing.T) {\n\trawMap := map[string]any{\n\t\t\"foo\": []string{},\n\t}\n\n\ts := &struct {\n\t\tFoo []string `test:\"foo,omitempty\"`\n\t\tBar []string `test:\"bar,omitempty\"`\n\t}{}\n\n\terr := decoder.Decode(rawMap, s)\n\tassert.Nil(t, err)\n\tassert.NotNil(t, s.Foo) // structure's Decode will ensure value not nil when input has value even it was set an empty array\n\tassert.Nil(t, s.Bar)\n}\n\nfunc TestStructure_Base64(t *testing.T) {\n\trawMap := map[string]any{\n\t\t\"foo\": \"AQID\",\n\t}\n\n\ts := &struct {\n\t\tFoo []byte `test:\"foo\"`\n\t}{}\n\n\terr := decoder.Decode(rawMap, s)\n\tassert.Nil(t, err)\n\tassert.Equal(t, []byte{1, 2, 3}, s.Foo)\n}\n\nfunc TestStructure_Pointer(t *testing.T) {\n\trawMap := map[string]any{\n\t\t\"foo\": \"foo\",\n\t}\n\n\ts := &struct {\n\t\tFoo *string `test:\"foo,omitempty\"`\n\t\tBar *string `test:\"bar,omitempty\"`\n\t}{}\n\n\terr := decoder.Decode(rawMap, s)\n\tassert.Nil(t, err)\n\tassert.NotNil(t, s.Foo)\n\tassert.Equal(t, \"foo\", *s.Foo)\n\tassert.Nil(t, s.Bar)\n}\n\nfunc TestStructure_PointerStruct(t *testing.T) {\n\trawMap := map[string]any{\n\t\t\"foo\": \"foo\",\n\t}\n\n\ts := &struct {\n\t\tFoo *string `test:\"foo,omitempty\"`\n\t\tBar *Baz    `test:\"bar,omitempty\"`\n\t}{}\n\n\terr := decoder.Decode(rawMap, s)\n\tassert.Nil(t, err)\n\tassert.NotNil(t, s.Foo)\n\tassert.Equal(t, \"foo\", *s.Foo)\n\tassert.Nil(t, s.Bar)\n}\n\ntype num struct {\n\ta int\n}\n\nfunc (n *num) UnmarshalText(text []byte) (err error) {\n\tn.a, err = strconv.Atoi(string(text))\n\treturn\n}\n\nfunc TestStructure_TextUnmarshaller(t *testing.T) {\n\trawMap := map[string]any{\n\t\t\"num\":     \"255\",\n\t\t\"num_p\":   \"127\",\n\t\t\"num_arr\": []string{\"1\", \"2\", \"3\"},\n\t}\n\n\ts := &struct {\n\t\tNum    num   `test:\"num\"`\n\t\tNumP   *num  `test:\"num_p\"`\n\t\tNumArr []num `test:\"num_arr\"`\n\t}{}\n\n\terr := decoder.Decode(rawMap, s)\n\tassert.Nil(t, err)\n\tassert.Equal(t, 255, s.Num.a)\n\tassert.NotNil(t, s.NumP)\n\tassert.Equal(t, s.NumP.a, 127)\n\tassert.Equal(t, s.NumArr, []num{{1}, {2}, {3}})\n\n\t// test WeaklyTypedInput\n\trawMap[\"num\"] = 256\n\terr = decoder.Decode(rawMap, s)\n\tassert.NotNilf(t, err, \"should throw error: %#v\", s)\n\terr = weakTypeDecoder.Decode(rawMap, s)\n\tassert.Nil(t, err)\n\tassert.Equal(t, 256, s.Num.a)\n\n\t// test invalid input\n\trawMap[\"num_p\"] = \"abc\"\n\terr = decoder.Decode(rawMap, s)\n\tassert.NotNilf(t, err, \"should throw error: %#v\", s)\n}\n\nfunc TestStructure_Null(t *testing.T) {\n\trawMap := map[string]any{\n\t\t\"opt\": map[string]any{\n\t\t\t\"bar\": nil,\n\t\t},\n\t}\n\n\ts := struct {\n\t\tOpt struct {\n\t\t\tBar string `test:\"bar,optional\"`\n\t\t} `test:\"opt,optional\"`\n\t}{}\n\n\terr := decoder.Decode(rawMap, &s)\n\tassert.Nil(t, err)\n\tassert.Equal(t, s.Opt.Bar, \"\")\n}\n\nfunc TestStructure_Ignore(t *testing.T) {\n\trawMap := map[string]any{\n\t\t\"-\": \"newData\",\n\t}\n\n\ts := struct {\n\t\tMustIgnore string `test:\"-\"`\n\t}{MustIgnore: \"oldData\"}\n\n\terr := decoder.Decode(rawMap, &s)\n\tassert.Nil(t, err)\n\tassert.Equal(t, s.MustIgnore, \"oldData\")\n\n\t// test omitempty\n\tdelete(rawMap, \"-\")\n\terr = decoder.Decode(rawMap, &s)\n\tassert.Nil(t, err)\n\tassert.Equal(t, s.MustIgnore, \"oldData\")\n}\n\nfunc TestStructure_IgnoreInNest(t *testing.T) {\n\trawMap := map[string]any{\n\t\t\"-\": \"newData\",\n\t}\n\n\ttype TP struct {\n\t\tMustIgnore string `test:\"-\"`\n\t}\n\n\ts := struct {\n\t\tTP\n\t}{TP{MustIgnore: \"oldData\"}}\n\n\terr := decoder.Decode(rawMap, &s)\n\tassert.Nil(t, err)\n\tassert.Equal(t, s.MustIgnore, \"oldData\")\n\n\t// test omitempty\n\tdelete(rawMap, \"-\")\n\terr = decoder.Decode(rawMap, &s)\n\tassert.Nil(t, err)\n\tassert.Equal(t, s.MustIgnore, \"oldData\")\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/utils/callback.go",
    "content": "package utils\n\nimport (\n\t\"io\"\n\t\"sync\"\n\n\tlist \"github.com/bahlo/generic-list-go\"\n)\n\ntype Callback[T any] struct {\n\tlist  list.List[func(T)]\n\tmutex sync.RWMutex\n}\n\nfunc NewCallback[T any]() *Callback[T] {\n\treturn &Callback[T]{}\n}\n\nfunc (c *Callback[T]) Register(item func(T)) io.Closer {\n\tc.mutex.Lock()\n\tdefer c.mutex.Unlock()\n\telement := c.list.PushBack(item)\n\treturn &callbackCloser[T]{\n\t\telement:  element,\n\t\tcallback: c,\n\t}\n}\n\nfunc (c *Callback[T]) Emit(item T) {\n\tc.mutex.RLock()\n\tdefer c.mutex.RUnlock()\n\tfor element := c.list.Front(); element != nil; element = element.Next() {\n\t\tgo element.Value(item)\n\t}\n}\n\ntype callbackCloser[T any] struct {\n\telement  *list.Element[func(T)]\n\tcallback *Callback[T]\n\tonce     sync.Once\n}\n\nfunc (c *callbackCloser[T]) Close() error {\n\tc.once.Do(func() {\n\t\tc.callback.mutex.Lock()\n\t\tdefer c.callback.mutex.Unlock()\n\t\tc.callback.list.Remove(c.element)\n\t})\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/utils/global_id.go",
    "content": "package utils\n\nimport (\n\t\"hash/maphash\"\n\t\"unsafe\"\n)\n\nvar globalSeed = maphash.MakeSeed()\n\nfunc GlobalID(material string) (id [8]byte) {\n\t*(*uint64)(unsafe.Pointer(&id[0])) = maphash.String(globalSeed, material)\n\treturn\n}\n\nfunc MapHash(material string) uint64 {\n\treturn maphash.String(globalSeed, material)\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/utils/hash.go",
    "content": "package utils\n\nimport (\n\t\"crypto/md5\"\n\t\"encoding/hex\"\n\t\"errors\"\n)\n\n// HashType warps hash array inside struct\n// someday can change to other hash algorithm simply\ntype HashType struct {\n\tmd5 [md5.Size]byte // MD5\n}\n\nfunc MakeHash(data []byte) HashType {\n\treturn HashType{md5.Sum(data)}\n}\n\nfunc (h HashType) Equal(hash HashType) bool {\n\treturn h.md5 == hash.md5\n}\n\nfunc (h HashType) Bytes() []byte {\n\treturn h.md5[:]\n}\n\nfunc (h HashType) String() string {\n\treturn hex.EncodeToString(h.Bytes())\n}\n\nfunc (h HashType) MarshalText() ([]byte, error) {\n\treturn []byte(h.String()), nil\n}\n\nfunc (h *HashType) UnmarshalText(data []byte) error {\n\tif hex.DecodedLen(len(data)) != md5.Size {\n\t\treturn errors.New(\"invalid hash length\")\n\t}\n\t_, err := hex.Decode(h.md5[:], data)\n\treturn err\n}\n\nfunc (h HashType) MarshalBinary() ([]byte, error) {\n\treturn h.md5[:], nil\n}\n\nfunc (h *HashType) UnmarshalBinary(data []byte) error {\n\tif len(data) != md5.Size {\n\t\treturn errors.New(\"invalid hash length\")\n\t}\n\tcopy(h.md5[:], data)\n\treturn nil\n}\n\nfunc (h HashType) Len() int {\n\treturn len(h.md5)\n}\n\nfunc (h HashType) IsValid() bool {\n\tvar zero HashType\n\treturn h != zero\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/utils/manipulation.go",
    "content": "package utils\n\nimport \"github.com/samber/lo\"\n\nfunc EmptyOr[T comparable](v T, def T) T {\n\tret, _ := lo.Coalesce(v, def)\n\treturn ret\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/utils/must.go",
    "content": "package utils\n\nfunc MustOK[T any](result T, ok bool) T {\n\tif ok {\n\t\treturn result\n\t}\n\tpanic(\"operation failed\")\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/utils/range.go",
    "content": "package utils\n\nimport (\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"golang.org/x/exp/constraints\"\n)\n\ntype Range[T constraints.Ordered] struct {\n\tstart T\n\tend   T\n}\n\nfunc NewRange[T constraints.Ordered](start, end T) Range[T] {\n\tif start > end {\n\t\treturn Range[T]{\n\t\t\tstart: end,\n\t\t\tend:   start,\n\t\t}\n\t}\n\n\treturn Range[T]{\n\t\tstart: start,\n\t\tend:   end,\n\t}\n}\n\nfunc (r Range[T]) Contains(t T) bool {\n\treturn t >= r.start && t <= r.end\n}\n\nfunc (r Range[T]) LeftContains(t T) bool {\n\treturn t >= r.start && t < r.end\n}\n\nfunc (r Range[T]) RightContains(t T) bool {\n\treturn t > r.start && t <= r.end\n}\n\nfunc (r Range[T]) Start() T {\n\treturn r.start\n}\n\nfunc (r Range[T]) End() T {\n\treturn r.end\n}\n\nfunc (r Range[T]) String() string {\n\tif r.start == r.end {\n\t\treturn fmt.Sprintf(\"%v\", r.start)\n\t}\n\treturn fmt.Sprintf(\"%v-%v\", r.start, r.end)\n}\n\nfunc NewUnsignedRange[T constraints.Unsigned](expected string) (Range[T], error) {\n\treturn newIntRange(expected, parseUnsigned[T])\n}\n\nfunc NewSignedRange[T constraints.Signed](expected string) (Range[T], error) {\n\treturn newIntRange(expected, parseSigned[T])\n}\n\nfunc newIntRange[T constraints.Integer](s string, parseFn func(string) (T, error)) (Range[T], error) {\n\ts = strings.TrimSpace(s)\n\tif len(s) == 0 {\n\t\treturn NewRange[T](0, 0), nil\n\t}\n\tstatus := strings.Split(s, \"-\")\n\tstart, err := parseFn(strings.Trim(status[0], \"[ ]\"))\n\tif err != nil {\n\t\treturn Range[T]{}, fmt.Errorf(\"invalid range: %s\", s)\n\t}\n\tswitch len(status) {\n\tcase 1: // Port range\n\t\treturn NewRange(start, start), nil\n\tcase 2: // Single port\n\t\tend, err := parseFn(strings.Trim(status[1], \"[ ]\"))\n\t\tif err != nil {\n\t\t\treturn Range[T]{}, fmt.Errorf(\"invalid range: %s\", s)\n\t\t}\n\t\treturn NewRange(start, end), nil\n\tdefault:\n\t\treturn Range[T]{}, fmt.Errorf(\"invalid range: %s\", s)\n\t}\n}\n\nfunc parseUnsigned[T constraints.Unsigned](s string) (T, error) {\n\tif val, err := strconv.ParseUint(s, 10, 64); err == nil {\n\t\treturn T(val), nil\n\t} else {\n\t\treturn 0, err\n\t}\n}\n\nfunc parseSigned[T constraints.Signed](s string) (T, error) {\n\tif val, err := strconv.ParseInt(s, 10, 64); err == nil {\n\t\treturn T(val), nil\n\t} else {\n\t\treturn 0, err\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/utils/ranges.go",
    "content": "package utils\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"sort\"\n\t\"strings\"\n\n\t\"golang.org/x/exp/constraints\"\n)\n\ntype IntRanges[T constraints.Integer] []Range[T]\n\nvar errIntRanges = errors.New(\"intRanges error\")\n\nfunc newIntRanges[T constraints.Integer](expected string, parseFn func(string) (T, error)) (IntRanges[T], error) {\n\t// example: 200 or 200/302 or 200-400 or 200/204/401-429/501-503\n\texpected = strings.TrimSpace(expected)\n\tif len(expected) == 0 || expected == \"*\" {\n\t\treturn nil, nil\n\t}\n\n\t// support: 200,302 or 200,204,401-429,501-503\n\texpected = strings.ReplaceAll(expected, \",\", \"/\")\n\tlist := strings.Split(expected, \"/\")\n\tif len(list) > 28 {\n\t\treturn nil, fmt.Errorf(\"%w, too many ranges to use, maximum support 28 ranges\", errIntRanges)\n\t}\n\n\treturn newIntRangesFromList[T](list, parseFn)\n}\n\nfunc newIntRangesFromList[T constraints.Integer](list []string, parseFn func(string) (T, error)) (IntRanges[T], error) {\n\tvar ranges IntRanges[T]\n\tfor _, s := range list {\n\t\tif s == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tr, err := newIntRange[T](s, parseFn)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tranges = append(ranges, r)\n\t}\n\n\treturn ranges, nil\n}\n\nfunc NewUnsignedRanges[T constraints.Unsigned](expected string) (IntRanges[T], error) {\n\treturn newIntRanges(expected, parseUnsigned[T])\n}\n\nfunc NewUnsignedRangesFromList[T constraints.Unsigned](list []string) (IntRanges[T], error) {\n\treturn newIntRangesFromList(list, parseUnsigned[T])\n}\n\nfunc NewSignedRanges[T constraints.Signed](expected string) (IntRanges[T], error) {\n\treturn newIntRanges(expected, parseSigned[T])\n}\n\nfunc NewSignedRangesFromList[T constraints.Signed](list []string) (IntRanges[T], error) {\n\treturn newIntRangesFromList(list, parseSigned[T])\n}\n\nfunc (ranges IntRanges[T]) Check(status T) bool {\n\tif len(ranges) == 0 {\n\t\treturn true\n\t}\n\n\tfor _, segment := range ranges {\n\t\tif segment.Contains(status) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc (ranges IntRanges[T]) String() string {\n\tif len(ranges) == 0 {\n\t\treturn \"*\"\n\t}\n\n\tterms := make([]string, len(ranges))\n\tfor i, r := range ranges {\n\t\tterms[i] = r.String()\n\t}\n\n\treturn strings.Join(terms, \"/\")\n}\n\nfunc (ranges IntRanges[T]) Range(f func(t T) bool) {\n\tif len(ranges) == 0 {\n\t\treturn\n\t}\n\n\tfor _, r := range ranges {\n\t\tfor i := r.Start(); i <= r.End() && i >= r.Start(); i++ {\n\t\t\tif !f(i) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif i+1 < i { // integer overflow\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (ranges IntRanges[T]) Merge() (mergedRanges IntRanges[T]) {\n\tif len(ranges) == 0 {\n\t\treturn\n\t}\n\tsort.Slice(ranges, func(i, j int) bool {\n\t\treturn ranges[i].Start() < ranges[j].Start()\n\t})\n\tmergedRanges = ranges[:1]\n\tvar rangeIndex int\n\tfor _, r := range ranges[1:] {\n\t\tif mergedRanges[rangeIndex].End()+1 > mergedRanges[rangeIndex].End() && // integer overflow\n\t\t\tr.Start() > mergedRanges[rangeIndex].End()+1 {\n\t\t\tmergedRanges = append(mergedRanges, r)\n\t\t\trangeIndex++\n\t\t} else if r.End() > mergedRanges[rangeIndex].End() {\n\t\t\tmergedRanges[rangeIndex].end = r.End()\n\t\t}\n\t}\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/utils/ranges_test.go",
    "content": "package utils\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"testing\"\n)\n\nfunc TestMergeRanges(t *testing.T) {\n\tt.Parallel()\n\tfor _, testRange := range []struct {\n\t\tranges   IntRanges[uint16]\n\t\texpected IntRanges[uint16]\n\t}{\n\t\t{\n\t\t\tranges: IntRanges[uint16]{\n\t\t\t\tNewRange[uint16](0, 1),\n\t\t\t\tNewRange[uint16](1, 2),\n\t\t\t},\n\t\t\texpected: IntRanges[uint16]{\n\t\t\t\tNewRange[uint16](0, 2),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tranges: IntRanges[uint16]{\n\t\t\t\tNewRange[uint16](0, 3),\n\t\t\t\tNewRange[uint16](5, 7),\n\t\t\t\tNewRange[uint16](8, 9),\n\t\t\t\tNewRange[uint16](10, 10),\n\t\t\t},\n\t\t\texpected: IntRanges[uint16]{\n\t\t\t\tNewRange[uint16](0, 3),\n\t\t\t\tNewRange[uint16](5, 10),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tranges: IntRanges[uint16]{\n\t\t\t\tNewRange[uint16](1, 3),\n\t\t\t\tNewRange[uint16](2, 6),\n\t\t\t\tNewRange[uint16](8, 10),\n\t\t\t\tNewRange[uint16](15, 18),\n\t\t\t},\n\t\t\texpected: IntRanges[uint16]{\n\t\t\t\tNewRange[uint16](1, 6),\n\t\t\t\tNewRange[uint16](8, 10),\n\t\t\t\tNewRange[uint16](15, 18),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tranges: IntRanges[uint16]{\n\t\t\t\tNewRange[uint16](1, 3),\n\t\t\t\tNewRange[uint16](2, 7),\n\t\t\t\tNewRange[uint16](2, 6),\n\t\t\t},\n\t\t\texpected: IntRanges[uint16]{\n\t\t\t\tNewRange[uint16](1, 7),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tranges: IntRanges[uint16]{\n\t\t\t\tNewRange[uint16](1, 3),\n\t\t\t\tNewRange[uint16](2, 6),\n\t\t\t\tNewRange[uint16](2, 7),\n\t\t\t},\n\t\t\texpected: IntRanges[uint16]{\n\t\t\t\tNewRange[uint16](1, 7),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tranges: IntRanges[uint16]{\n\t\t\t\tNewRange[uint16](1, 3),\n\t\t\t\tNewRange[uint16](2, 65535),\n\t\t\t\tNewRange[uint16](2, 7),\n\t\t\t\tNewRange[uint16](3, 16),\n\t\t\t},\n\t\t\texpected: IntRanges[uint16]{\n\t\t\t\tNewRange[uint16](1, 65535),\n\t\t\t},\n\t\t},\n\t} {\n\t\tassert.Equal(t, testRange.expected, testRange.ranges.Merge())\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/utils/slice.go",
    "content": "package utils\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"reflect\"\n)\n\nfunc Filter[T comparable](tSlice []T, filter func(t T) bool) []T {\n\tresult := make([]T, 0)\n\tfor _, t := range tSlice {\n\t\tif filter(t) {\n\t\t\tresult = append(result, t)\n\t\t}\n\t}\n\treturn result\n}\n\nfunc Map[T any, N any](arr []T, block func(it T) N) []N {\n\tif arr == nil { // keep nil\n\t\treturn nil\n\t}\n\tretArr := make([]N, 0, len(arr))\n\tfor index := range arr {\n\t\tretArr = append(retArr, block(arr[index]))\n\t}\n\treturn retArr\n}\n\nfunc ToStringSlice(value any) ([]string, error) {\n\tstrArr := make([]string, 0)\n\tswitch reflect.TypeOf(value).Kind() {\n\tcase reflect.Slice, reflect.Array:\n\t\torigin := reflect.ValueOf(value)\n\t\tfor i := 0; i < origin.Len(); i++ {\n\t\t\titem := fmt.Sprintf(\"%v\", origin.Index(i))\n\t\t\tstrArr = append(strArr, item)\n\t\t}\n\tcase reflect.String:\n\t\tstrArr = append(strArr, fmt.Sprintf(\"%v\", value))\n\tdefault:\n\t\treturn nil, errors.New(\"value format error, must be string or array\")\n\t}\n\treturn strArr, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/utils/string_unsafe.go",
    "content": "package utils\n\nimport \"unsafe\"\n\n// ImmutableBytesFromString is equivalent to []byte(s), except that it uses the\n// same memory backing s instead of making a heap-allocated copy. This is only\n// valid if the returned slice is never mutated.\nfunc ImmutableBytesFromString(s string) []byte {\n\tb := unsafe.StringData(s)\n\treturn unsafe.Slice(b, len(s))\n}\n\n// StringFromImmutableBytes is equivalent to string(bs), except that it uses\n// the same memory backing bs instead of making a heap-allocated copy. This is\n// only valid if bs is never mutated after StringFromImmutableBytes returns.\nfunc StringFromImmutableBytes(bs []byte) string {\n\tif len(bs) == 0 {\n\t\treturn \"\"\n\t}\n\treturn unsafe.String(&bs[0], len(bs))\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/utils/strings.go",
    "content": "package utils\n\nfunc Reverse(s string) string {\n\ta := []rune(s)\n\tfor i, j := 0, len(a)-1; i < j; i, j = i+1, j-1 {\n\t\ta[i], a[j] = a[j], a[i]\n\t}\n\treturn string(a)\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/utils/uuid.go",
    "content": "package utils\n\nimport (\n\t\"crypto/md5\"\n\t\"crypto/rand\"\n\t\"crypto/sha1\"\n\n\t\"github.com/gofrs/uuid/v5\"\n)\n\n// NewUUIDV3 returns a UUID based on the MD5 hash of the namespace UUID and name.\nfunc NewUUIDV3(ns uuid.UUID, name string) (u uuid.UUID) {\n\th := md5.New()\n\th.Write(ns[:])\n\th.Write([]byte(name))\n\tcopy(u[:], h.Sum(make([]byte, 0, md5.Size)))\n\n\tu.SetVersion(uuid.V3)\n\tu.SetVariant(uuid.VariantRFC9562)\n\treturn u\n}\n\n// NewUUIDV4 returns a new version 4 UUID.\n//\n// Version 4 UUIDs contain 122 bits of random data.\nfunc NewUUIDV4() (u uuid.UUID) {\n\trand.Read(u[:])\n\tu.SetVersion(uuid.V4)\n\tu.SetVariant(uuid.VariantRFC9562)\n\treturn u\n}\n\n// NewUUIDV5 returns a UUID based on SHA-1 hash of the namespace UUID and name.\nfunc NewUUIDV5(ns uuid.UUID, name string) (u uuid.UUID) {\n\th := sha1.New()\n\th.Write(ns[:])\n\th.Write([]byte(name))\n\tcopy(u[:], h.Sum(make([]byte, 0, sha1.Size)))\n\n\tu.SetVersion(uuid.V5)\n\tu.SetVariant(uuid.VariantRFC9562)\n\treturn u\n}\n\n// UUIDMap https://github.com/XTLS/Xray-core/issues/158#issue-783294090\nfunc UUIDMap(str string) uuid.UUID {\n\tu, err := uuid.FromString(str)\n\tif err != nil {\n\t\treturn NewUUIDV5(uuid.Nil, str)\n\t}\n\treturn u\n}"
  },
  {
    "path": "core/Clash.Meta/common/utils/uuid_test.go",
    "content": "package utils\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n\n\t\"github.com/gofrs/uuid/v5\"\n)\n\nfunc TestUUIDMap(t *testing.T) {\n\ttype args struct {\n\t\tstr string\n\t}\n\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twant    uuid.UUID\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"uuid-test-1\",\n\t\t\targs: args{\n\t\t\t\tstr: \"82410302-039e-41b6-98b0-d964084b4170\",\n\t\t\t},\n\t\t\twant: uuid.FromStringOrNil(\"82410302-039e-41b6-98b0-d964084b4170\"),\n\t\t},\n\t\t{\n\t\t\tname: \"uuid-test-2\",\n\t\t\targs: args{\n\t\t\t\tstr: \"88c502e6-d7eb-4c8e-8259-94cb13d83c77\",\n\t\t\t},\n\t\t\twant: uuid.FromStringOrNil(\"88c502e6-d7eb-4c8e-8259-94cb13d83c77\"),\n\t\t},\n\t\t{\n\t\t\tname: \"uuid-map-1\",\n\t\t\targs: args{\n\t\t\t\tstr: \"123456\",\n\t\t\t},\n\t\t\twant: uuid.FromStringOrNil(\"f8598425-92f2-5508-a071-4fc67f9040ac\"),\n\t\t},\n\t\t// GENERATED BY 'xray uuid -i'\n\t\t{\n\t\t\tname: \"uuid-map-2\",\n\t\t\targs: args{\n\t\t\t\tstr: \"a9dk23bz0\",\n\t\t\t},\n\t\t\twant: uuid.FromStringOrNil(\"c91481b6-fc0f-5d9e-b166-5ddf07b9c3c5\"),\n\t\t},\n\t\t{\n\t\t\tname: \"uuid-map-2\",\n\t\t\targs: args{\n\t\t\t\tstr: \"中文123\",\n\t\t\t},\n\t\t\twant: uuid.FromStringOrNil(\"145c544c-2229-59e5-8dbb-3f33b7610d26\"),\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot := UUIDMap(tt.args.str)\n\t\t\tif !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"UUIDMap() got = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}"
  },
  {
    "path": "core/Clash.Meta/common/xsync/map.go",
    "content": "package xsync\n\n// copy and modified from https://github.com/puzpuzpuz/xsync/blob/v4.2.0/map.go\n// which is licensed under Apache v2.\n//\n// mihomo modified:\n// 1. restore xsync/v3's LoadOrCompute api and rename to LoadOrStoreFn.\n// 2. the zero Map is ready for use.\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"math/bits\"\n\t\"runtime\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"unsafe\"\n\n\t\"github.com/metacubex/mihomo/common/maphash\"\n)\n\nconst (\n\t// number of Map entries per bucket; 5 entries lead to size of 64B\n\t// (one cache line) on 64-bit machines\n\tentriesPerMapBucket = 5\n\t// threshold fraction of table occupation to start a table shrinking\n\t// when deleting the last entry in a bucket chain\n\tmapShrinkFraction = 128\n\t// map load factor to trigger a table resize during insertion;\n\t// a map holds up to mapLoadFactor*entriesPerMapBucket*mapTableLen\n\t// key-value pairs (this is a soft limit)\n\tmapLoadFactor = 0.75\n\t// minimal table size, i.e. number of buckets; thus, minimal map\n\t// capacity can be calculated as entriesPerMapBucket*defaultMinMapTableLen\n\tdefaultMinMapTableLen = 32\n\t// minimum counter stripes to use\n\tminMapCounterLen = 8\n\t// maximum counter stripes to use; stands for around 4KB of memory\n\tmaxMapCounterLen         = 32\n\tdefaultMeta       uint64 = 0x8080808080808080\n\tmetaMask          uint64 = 0xffffffffff\n\tdefaultMetaMasked uint64 = defaultMeta & metaMask\n\temptyMetaSlot     uint8  = 0x80\n\t// minimal number of buckets to transfer when participating in cooperative\n\t// resize; should be at least defaultMinMapTableLen\n\tminResizeTransferStride = 64\n\t// upper limit for max number of additional goroutines that participate\n\t// in cooperative resize; must be changed simultaneously with resizeCtl\n\t// and the related code\n\tmaxResizeHelpersLimit = (1 << 5) - 1\n)\n\n// max number of additional goroutines that participate in cooperative resize;\n// \"resize owner\" goroutine isn't counted\nvar maxResizeHelpers = func() int32 {\n\tv := int32(parallelism() - 1)\n\tif v < 1 {\n\t\tv = 1\n\t}\n\tif v > maxResizeHelpersLimit {\n\t\tv = maxResizeHelpersLimit\n\t}\n\treturn v\n}()\n\ntype mapResizeHint int\n\nconst (\n\tmapGrowHint   mapResizeHint = 0\n\tmapShrinkHint mapResizeHint = 1\n\tmapClearHint  mapResizeHint = 2\n)\n\ntype ComputeOp int\n\nconst (\n\t// CancelOp signals to Compute to not do anything as a result\n\t// of executing the lambda. If the entry was not present in\n\t// the map, nothing happens, and if it was present, the\n\t// returned value is ignored.\n\tCancelOp ComputeOp = iota\n\t// UpdateOp signals to Compute to update the entry to the\n\t// value returned by the lambda, creating it if necessary.\n\tUpdateOp\n\t// DeleteOp signals to Compute to always delete the entry\n\t// from the map.\n\tDeleteOp\n)\n\ntype loadOp int\n\nconst (\n\tnoLoadOp loadOp = iota\n\tloadOrComputeOp\n\tloadAndDeleteOp\n)\n\n// Map is like a Go map[K]V but is safe for concurrent\n// use by multiple goroutines without additional locking or\n// coordination. It follows the interface of sync.Map with\n// a number of valuable extensions like Compute or Size.\n//\n// A Map must not be copied after first use.\n//\n// Map uses a modified version of Cache-Line Hash Table (CLHT)\n// data structure: https://github.com/LPD-EPFL/CLHT\n//\n// CLHT is built around idea to organize the hash table in\n// cache-line-sized buckets, so that on all modern CPUs update\n// operations complete with at most one cache-line transfer.\n// Also, Get operations involve no write to memory, as well as no\n// mutexes or any other sort of locks. Due to this design, in all\n// considered scenarios Map outperforms sync.Map.\n//\n// Map also borrows ideas from Java's j.u.c.ConcurrentHashMap\n// (immutable K/V pair structs instead of atomic snapshots)\n// and C++'s absl::flat_hash_map (meta memory and SWAR-based\n// lookups).\ntype Map[K comparable, V any] struct {\n\tinitOnce     sync.Once\n\ttotalGrowths atomic.Int64\n\ttotalShrinks atomic.Int64\n\ttable        atomic.Pointer[mapTable[K, V]]\n\t// table being transferred to\n\tnextTable atomic.Pointer[mapTable[K, V]]\n\t// resize control state: combines resize sequence number (upper 59 bits) and\n\t// the current number of resize helpers (lower 5 bits);\n\t// odd values of resize sequence mean in-progress resize\n\tresizeCtl atomic.Uint64\n\t// only used along with resizeCond\n\tresizeMu sync.Mutex\n\t// used to wake up resize waiters (concurrent writes)\n\tresizeCond sync.Cond\n\t// transfer progress index for resize\n\tresizeIdx   atomic.Int64\n\tminTableLen int\n\tgrowOnly    bool\n}\n\ntype mapTable[K comparable, V any] struct {\n\tbuckets []bucketPadded\n\t// striped counter for number of table entries;\n\t// used to determine if a table shrinking is needed\n\t// occupies min(buckets_memory/1024, 64KB) of memory\n\tsize []counterStripe\n\tseed maphash.Seed\n}\n\ntype counterStripe struct {\n\tc int64\n\t// Padding to prevent false sharing.\n\t_ [cacheLineSize - 8]byte\n}\n\n// bucketPadded is a CL-sized map bucket holding up to\n// entriesPerMapBucket entries.\ntype bucketPadded struct {\n\t//lint:ignore U1000 ensure each bucket takes two cache lines on both 32 and 64-bit archs\n\tpad [cacheLineSize - unsafe.Sizeof(bucket{})]byte\n\tbucket\n}\n\ntype bucket struct {\n\tmeta    uint64\n\tentries [entriesPerMapBucket]unsafe.Pointer // *entry\n\tnext    unsafe.Pointer                      // *bucketPadded\n\tmu      sync.Mutex\n}\n\n// entry is an immutable map entry.\ntype entry[K comparable, V any] struct {\n\tkey   K\n\tvalue V\n}\n\n// MapConfig defines configurable Map options.\ntype MapConfig struct {\n\tsizeHint int\n\tgrowOnly bool\n}\n\n// WithPresize configures new Map instance with capacity enough\n// to hold sizeHint entries. The capacity is treated as the minimal\n// capacity meaning that the underlying hash table will never shrink\n// to a smaller capacity. If sizeHint is zero or negative, the value\n// is ignored.\nfunc WithPresize(sizeHint int) func(*MapConfig) {\n\treturn func(c *MapConfig) {\n\t\tc.sizeHint = sizeHint\n\t}\n}\n\n// WithGrowOnly configures new Map instance to be grow-only.\n// This means that the underlying hash table grows in capacity when\n// new keys are added, but does not shrink when keys are deleted.\n// The only exception to this rule is the Clear method which\n// shrinks the hash table back to the initial capacity.\nfunc WithGrowOnly() func(*MapConfig) {\n\treturn func(c *MapConfig) {\n\t\tc.growOnly = true\n\t}\n}\n\n// NewMap creates a new Map instance configured with the given\n// options.\nfunc NewMap[K comparable, V any](options ...func(*MapConfig)) *Map[K, V] {\n\tc := &MapConfig{}\n\tfor _, o := range options {\n\t\to(c)\n\t}\n\n\tm := &Map[K, V]{}\n\tif c.sizeHint > defaultMinMapTableLen*entriesPerMapBucket {\n\t\ttableLen := nextPowOf2(uint32((float64(c.sizeHint) / entriesPerMapBucket) / mapLoadFactor))\n\t\tm.minTableLen = int(tableLen)\n\t}\n\tm.growOnly = c.growOnly\n\treturn m\n}\n\nfunc (m *Map[K, V]) init() {\n\tif m.minTableLen == 0 {\n\t\tm.minTableLen = defaultMinMapTableLen\n\t}\n\tm.resizeCond = *sync.NewCond(&m.resizeMu)\n\ttable := newMapTable[K, V](m.minTableLen, maphash.MakeSeed())\n\tm.minTableLen = len(table.buckets)\n\tm.table.Store(table)\n}\n\nfunc newMapTable[K comparable, V any](minTableLen int, seed maphash.Seed) *mapTable[K, V] {\n\tbuckets := make([]bucketPadded, minTableLen)\n\tfor i := range buckets {\n\t\tbuckets[i].meta = defaultMeta\n\t}\n\tcounterLen := minTableLen >> 10\n\tif counterLen < minMapCounterLen {\n\t\tcounterLen = minMapCounterLen\n\t} else if counterLen > maxMapCounterLen {\n\t\tcounterLen = maxMapCounterLen\n\t}\n\tcounter := make([]counterStripe, counterLen)\n\tt := &mapTable[K, V]{\n\t\tbuckets: buckets,\n\t\tsize:    counter,\n\t\tseed:    seed,\n\t}\n\treturn t\n}\n\n// ToPlainMap returns a native map with a copy of xsync Map's\n// contents. The copied xsync Map should not be modified while\n// this call is made. If the copied Map is modified, the copying\n// behavior is the same as in the Range method.\nfunc ToPlainMap[K comparable, V any](m *Map[K, V]) map[K]V {\n\tpm := make(map[K]V)\n\tif m != nil {\n\t\tm.Range(func(key K, value V) bool {\n\t\t\tpm[key] = value\n\t\t\treturn true\n\t\t})\n\t}\n\treturn pm\n}\n\n// Load returns the value stored in the map for a key, or zero value\n// of type V if no value is present.\n// The ok result indicates whether value was found in the map.\nfunc (m *Map[K, V]) Load(key K) (value V, ok bool) {\n\tm.initOnce.Do(m.init)\n\ttable := m.table.Load()\n\thash := maphash.Comparable(table.seed, key)\n\th1 := h1(hash)\n\th2w := broadcast(h2(hash))\n\tbidx := uint64(len(table.buckets)-1) & h1\n\tb := &table.buckets[bidx]\n\tfor {\n\t\tmetaw := atomic.LoadUint64(&b.meta)\n\t\tmarkedw := markZeroBytes(metaw^h2w) & metaMask\n\t\tfor markedw != 0 {\n\t\t\tidx := firstMarkedByteIndex(markedw)\n\t\t\teptr := atomic.LoadPointer(&b.entries[idx])\n\t\t\tif eptr != nil {\n\t\t\t\te := (*entry[K, V])(eptr)\n\t\t\t\tif e.key == key {\n\t\t\t\t\treturn e.value, true\n\t\t\t\t}\n\t\t\t}\n\t\t\tmarkedw &= markedw - 1\n\t\t}\n\t\tbptr := atomic.LoadPointer(&b.next)\n\t\tif bptr == nil {\n\t\t\treturn\n\t\t}\n\t\tb = (*bucketPadded)(bptr)\n\t}\n}\n\n// Store sets the value for a key.\nfunc (m *Map[K, V]) Store(key K, value V) {\n\tm.doCompute(\n\t\tkey,\n\t\tfunc(V, bool) (V, ComputeOp) {\n\t\t\treturn value, UpdateOp\n\t\t},\n\t\tnoLoadOp,\n\t\tfalse,\n\t)\n}\n\n// LoadOrStore returns the existing value for the key if present.\n// Otherwise, it stores and returns the given value.\n// The loaded result is true if the value was loaded, false if stored.\nfunc (m *Map[K, V]) LoadOrStore(key K, value V) (actual V, loaded bool) {\n\treturn m.doCompute(\n\t\tkey,\n\t\tfunc(oldValue V, loaded bool) (V, ComputeOp) {\n\t\t\tif loaded {\n\t\t\t\treturn oldValue, CancelOp\n\t\t\t}\n\t\t\treturn value, UpdateOp\n\t\t},\n\t\tloadOrComputeOp,\n\t\tfalse,\n\t)\n}\n\n// LoadAndStore returns the existing value for the key if present,\n// while setting the new value for the key.\n// It stores the new value and returns the existing one, if present.\n// The loaded result is true if the existing value was loaded,\n// false otherwise.\nfunc (m *Map[K, V]) LoadAndStore(key K, value V) (actual V, loaded bool) {\n\treturn m.doCompute(\n\t\tkey,\n\t\tfunc(V, bool) (V, ComputeOp) {\n\t\t\treturn value, UpdateOp\n\t\t},\n\t\tnoLoadOp,\n\t\tfalse,\n\t)\n}\n\n// LoadOrCompute returns the existing value for the key if\n// present. Otherwise, it tries to compute the value using the\n// provided function and, if successful, stores and returns\n// the computed value. The loaded result is true if the value was\n// loaded, or false if computed. If valueFn returns true as the\n// cancel value, the computation is cancelled and the zero value\n// for type V is returned.\n//\n// This call locks a hash table bucket while the compute function\n// is executed. It means that modifications on other entries in\n// the bucket will be blocked until the valueFn executes. Consider\n// this when the function includes long-running operations.\nfunc (m *Map[K, V]) LoadOrCompute(\n\tkey K,\n\tvalueFn func() (newValue V, cancel bool),\n) (value V, loaded bool) {\n\treturn m.doCompute(\n\t\tkey,\n\t\tfunc(oldValue V, loaded bool) (V, ComputeOp) {\n\t\t\tif loaded {\n\t\t\t\treturn oldValue, CancelOp\n\t\t\t}\n\t\t\tnewValue, c := valueFn()\n\t\t\tif !c {\n\t\t\t\treturn newValue, UpdateOp\n\t\t\t}\n\t\t\treturn oldValue, CancelOp\n\t\t},\n\t\tloadOrComputeOp,\n\t\tfalse,\n\t)\n}\n\n// Compute either sets the computed new value for the key,\n// deletes the value for the key, or does nothing, based on\n// the returned [ComputeOp]. When the op returned by valueFn\n// is [UpdateOp], the value is updated to the new value. If\n// it is [DeleteOp], the entry is removed from the map\n// altogether. And finally, if the op is [CancelOp] then the\n// entry is left as-is. In other words, if it did not already\n// exist, it is not created, and if it did exist, it is not\n// updated. This is useful to synchronously execute some\n// operation on the value without incurring the cost of\n// updating the map every time. The ok result indicates\n// whether the entry is present in the map after the compute\n// operation. The actual result contains the value of the map\n// if a corresponding entry is present, or the zero value\n// otherwise. See the example for a few use cases.\n//\n// This call locks a hash table bucket while the compute function\n// is executed. It means that modifications on other entries in\n// the bucket will be blocked until the valueFn executes. Consider\n// this when the function includes long-running operations.\nfunc (m *Map[K, V]) Compute(\n\tkey K,\n\tvalueFn func(oldValue V, loaded bool) (newValue V, op ComputeOp),\n) (actual V, ok bool) {\n\treturn m.doCompute(key, valueFn, noLoadOp, true)\n}\n\n// LoadAndDelete deletes the value for a key, returning the previous\n// value if any. The loaded result reports whether the key was\n// present.\nfunc (m *Map[K, V]) LoadAndDelete(key K) (value V, loaded bool) {\n\treturn m.doCompute(\n\t\tkey,\n\t\tfunc(value V, loaded bool) (V, ComputeOp) {\n\t\t\treturn value, DeleteOp\n\t\t},\n\t\tloadAndDeleteOp,\n\t\tfalse,\n\t)\n}\n\n// Delete deletes the value for a key.\nfunc (m *Map[K, V]) Delete(key K) {\n\tm.LoadAndDelete(key)\n}\n\nfunc (m *Map[K, V]) doCompute(\n\tkey K,\n\tvalueFn func(oldValue V, loaded bool) (V, ComputeOp),\n\tloadOp loadOp,\n\tcomputeOnly bool,\n) (V, bool) {\n\tm.initOnce.Do(m.init)\n\tfor {\n\tcompute_attempt:\n\t\tvar (\n\t\t\temptyb   *bucketPadded\n\t\t\temptyidx int\n\t\t)\n\t\ttable := m.table.Load()\n\t\ttableLen := len(table.buckets)\n\t\thash := maphash.Comparable(table.seed, key)\n\t\th1 := h1(hash)\n\t\th2 := h2(hash)\n\t\th2w := broadcast(h2)\n\t\tbidx := uint64(len(table.buckets)-1) & h1\n\t\trootb := &table.buckets[bidx]\n\n\t\tif loadOp != noLoadOp {\n\t\t\tb := rootb\n\t\tload:\n\t\t\tfor {\n\t\t\t\tmetaw := atomic.LoadUint64(&b.meta)\n\t\t\t\tmarkedw := markZeroBytes(metaw^h2w) & metaMask\n\t\t\t\tfor markedw != 0 {\n\t\t\t\t\tidx := firstMarkedByteIndex(markedw)\n\t\t\t\t\teptr := atomic.LoadPointer(&b.entries[idx])\n\t\t\t\t\tif eptr != nil {\n\t\t\t\t\t\te := (*entry[K, V])(eptr)\n\t\t\t\t\t\tif e.key == key {\n\t\t\t\t\t\t\tif loadOp == loadOrComputeOp {\n\t\t\t\t\t\t\t\treturn e.value, true\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\tbreak load\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tmarkedw &= markedw - 1\n\t\t\t\t}\n\t\t\t\tbptr := atomic.LoadPointer(&b.next)\n\t\t\t\tif bptr == nil {\n\t\t\t\t\tif loadOp == loadAndDeleteOp {\n\t\t\t\t\t\treturn *new(V), false\n\t\t\t\t\t}\n\t\t\t\t\tbreak load\n\t\t\t\t}\n\t\t\t\tb = (*bucketPadded)(bptr)\n\t\t\t}\n\t\t}\n\n\t\trootb.mu.Lock()\n\t\t// The following two checks must go in reverse to what's\n\t\t// in the resize method.\n\t\tif seq := resizeSeq(m.resizeCtl.Load()); seq&1 == 1 {\n\t\t\t// Resize is in progress. Help with the transfer, then go for another attempt.\n\t\t\trootb.mu.Unlock()\n\t\t\tm.helpResize(seq)\n\t\t\tgoto compute_attempt\n\t\t}\n\t\tif m.newerTableExists(table) {\n\t\t\t// Someone resized the table. Go for another attempt.\n\t\t\trootb.mu.Unlock()\n\t\t\tgoto compute_attempt\n\t\t}\n\t\tb := rootb\n\t\tfor {\n\t\t\tmetaw := b.meta\n\t\t\tmarkedw := markZeroBytes(metaw^h2w) & metaMask\n\t\t\tfor markedw != 0 {\n\t\t\t\tidx := firstMarkedByteIndex(markedw)\n\t\t\t\teptr := b.entries[idx]\n\t\t\t\tif eptr != nil {\n\t\t\t\t\te := (*entry[K, V])(eptr)\n\t\t\t\t\tif e.key == key {\n\t\t\t\t\t\t// In-place update/delete.\n\t\t\t\t\t\t// We get a copy of the value via an interface{} on each call,\n\t\t\t\t\t\t// thus the live value pointers are unique. Otherwise atomic\n\t\t\t\t\t\t// snapshot won't be correct in case of multiple Store calls\n\t\t\t\t\t\t// using the same value.\n\t\t\t\t\t\toldv := e.value\n\t\t\t\t\t\tnewv, op := valueFn(oldv, true)\n\t\t\t\t\t\tswitch op {\n\t\t\t\t\t\tcase DeleteOp:\n\t\t\t\t\t\t\t// Deletion.\n\t\t\t\t\t\t\t// First we update the hash, then the entry.\n\t\t\t\t\t\t\tnewmetaw := setByte(metaw, emptyMetaSlot, idx)\n\t\t\t\t\t\t\tatomic.StoreUint64(&b.meta, newmetaw)\n\t\t\t\t\t\t\tatomic.StorePointer(&b.entries[idx], nil)\n\t\t\t\t\t\t\trootb.mu.Unlock()\n\t\t\t\t\t\t\ttable.addSize(bidx, -1)\n\t\t\t\t\t\t\t// Might need to shrink the table if we left bucket empty.\n\t\t\t\t\t\t\tif newmetaw == defaultMeta {\n\t\t\t\t\t\t\t\tm.resize(table, mapShrinkHint)\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\treturn oldv, !computeOnly\n\t\t\t\t\t\tcase UpdateOp:\n\t\t\t\t\t\t\tnewe := new(entry[K, V])\n\t\t\t\t\t\t\tnewe.key = key\n\t\t\t\t\t\t\tnewe.value = newv\n\t\t\t\t\t\t\tatomic.StorePointer(&b.entries[idx], unsafe.Pointer(newe))\n\t\t\t\t\t\tcase CancelOp:\n\t\t\t\t\t\t\tnewv = oldv\n\t\t\t\t\t\t}\n\t\t\t\t\t\trootb.mu.Unlock()\n\t\t\t\t\t\tif computeOnly {\n\t\t\t\t\t\t\t// Compute expects the new value to be returned.\n\t\t\t\t\t\t\treturn newv, true\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// LoadAndStore expects the old value to be returned.\n\t\t\t\t\t\treturn oldv, true\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tmarkedw &= markedw - 1\n\t\t\t}\n\t\t\tif emptyb == nil {\n\t\t\t\t// Search for empty entries (up to 5 per bucket).\n\t\t\t\temptyw := metaw & defaultMetaMasked\n\t\t\t\tif emptyw != 0 {\n\t\t\t\t\tidx := firstMarkedByteIndex(emptyw)\n\t\t\t\t\temptyb = b\n\t\t\t\t\temptyidx = idx\n\t\t\t\t}\n\t\t\t}\n\t\t\tif b.next == nil {\n\t\t\t\tif emptyb != nil {\n\t\t\t\t\t// Insertion into an existing bucket.\n\t\t\t\t\tvar zeroV V\n\t\t\t\t\tnewValue, op := valueFn(zeroV, false)\n\t\t\t\t\tswitch op {\n\t\t\t\t\tcase DeleteOp, CancelOp:\n\t\t\t\t\t\trootb.mu.Unlock()\n\t\t\t\t\t\treturn zeroV, false\n\t\t\t\t\tdefault:\n\t\t\t\t\t\tnewe := new(entry[K, V])\n\t\t\t\t\t\tnewe.key = key\n\t\t\t\t\t\tnewe.value = newValue\n\t\t\t\t\t\t// First we update meta, then the entry.\n\t\t\t\t\t\tatomic.StoreUint64(&emptyb.meta, setByte(emptyb.meta, h2, emptyidx))\n\t\t\t\t\t\tatomic.StorePointer(&emptyb.entries[emptyidx], unsafe.Pointer(newe))\n\t\t\t\t\t\trootb.mu.Unlock()\n\t\t\t\t\t\ttable.addSize(bidx, 1)\n\t\t\t\t\t\treturn newValue, computeOnly\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tgrowThreshold := float64(tableLen) * entriesPerMapBucket * mapLoadFactor\n\t\t\t\tif table.sumSize() > int64(growThreshold) {\n\t\t\t\t\t// Need to grow the table. Then go for another attempt.\n\t\t\t\t\trootb.mu.Unlock()\n\t\t\t\t\tm.resize(table, mapGrowHint)\n\t\t\t\t\tgoto compute_attempt\n\t\t\t\t}\n\t\t\t\t// Insertion into a new bucket.\n\t\t\t\tvar zeroV V\n\t\t\t\tnewValue, op := valueFn(zeroV, false)\n\t\t\t\tswitch op {\n\t\t\t\tcase DeleteOp, CancelOp:\n\t\t\t\t\trootb.mu.Unlock()\n\t\t\t\t\treturn newValue, false\n\t\t\t\tdefault:\n\t\t\t\t\t// Create and append a bucket.\n\t\t\t\t\tnewb := new(bucketPadded)\n\t\t\t\t\tnewb.meta = setByte(defaultMeta, h2, 0)\n\t\t\t\t\tnewe := new(entry[K, V])\n\t\t\t\t\tnewe.key = key\n\t\t\t\t\tnewe.value = newValue\n\t\t\t\t\tnewb.entries[0] = unsafe.Pointer(newe)\n\t\t\t\t\tatomic.StorePointer(&b.next, unsafe.Pointer(newb))\n\t\t\t\t\trootb.mu.Unlock()\n\t\t\t\t\ttable.addSize(bidx, 1)\n\t\t\t\t\treturn newValue, computeOnly\n\t\t\t\t}\n\t\t\t}\n\t\t\tb = (*bucketPadded)(b.next)\n\t\t}\n\t}\n}\n\nfunc (m *Map[K, V]) newerTableExists(table *mapTable[K, V]) bool {\n\treturn table != m.table.Load()\n}\n\nfunc resizeSeq(ctl uint64) uint64 {\n\treturn ctl >> 5\n}\n\nfunc resizeHelpers(ctl uint64) uint64 {\n\treturn ctl & maxResizeHelpersLimit\n}\n\nfunc resizeCtl(seq uint64, helpers uint64) uint64 {\n\treturn (seq << 5) | (helpers & maxResizeHelpersLimit)\n}\n\nfunc (m *Map[K, V]) waitForResize() {\n\tm.resizeMu.Lock()\n\tfor resizeSeq(m.resizeCtl.Load())&1 == 1 {\n\t\tm.resizeCond.Wait()\n\t}\n\tm.resizeMu.Unlock()\n}\n\nfunc (m *Map[K, V]) resize(knownTable *mapTable[K, V], hint mapResizeHint) {\n\tknownTableLen := len(knownTable.buckets)\n\t// Fast path for shrink attempts.\n\tif hint == mapShrinkHint {\n\t\tif m.growOnly ||\n\t\t\tm.minTableLen == knownTableLen ||\n\t\t\tknownTable.sumSize() > int64((knownTableLen*entriesPerMapBucket)/mapShrinkFraction) {\n\t\t\treturn\n\t\t}\n\t}\n\t// Slow path.\n\tseq := resizeSeq(m.resizeCtl.Load())\n\tif seq&1 == 1 || !m.resizeCtl.CompareAndSwap(resizeCtl(seq, 0), resizeCtl(seq+1, 0)) {\n\t\tm.helpResize(seq)\n\t\treturn\n\t}\n\tvar newTable *mapTable[K, V]\n\ttable := m.table.Load()\n\ttableLen := len(table.buckets)\n\tswitch hint {\n\tcase mapGrowHint:\n\t\t// Grow the table with factor of 2.\n\t\t// We must keep the same table seed here to keep the same hash codes\n\t\t// allowing us to avoid locking destination buckets when resizing.\n\t\tm.totalGrowths.Add(1)\n\t\tnewTable = newMapTable[K, V](tableLen<<1, table.seed)\n\tcase mapShrinkHint:\n\t\tshrinkThreshold := int64((tableLen * entriesPerMapBucket) / mapShrinkFraction)\n\t\tif tableLen > m.minTableLen && table.sumSize() <= shrinkThreshold {\n\t\t\t// Shrink the table with factor of 2.\n\t\t\t// It's fine to generate a new seed since full locking\n\t\t\t// is required anyway.\n\t\t\tm.totalShrinks.Add(1)\n\t\t\tnewTable = newMapTable[K, V](tableLen>>1, maphash.MakeSeed())\n\t\t} else {\n\t\t\t// No need to shrink. Wake up all waiters and give up.\n\t\t\tm.resizeMu.Lock()\n\t\t\tm.resizeCtl.Store(resizeCtl(seq+2, 0))\n\t\t\tm.resizeCond.Broadcast()\n\t\t\tm.resizeMu.Unlock()\n\t\t\treturn\n\t\t}\n\tcase mapClearHint:\n\t\tnewTable = newMapTable[K, V](m.minTableLen, maphash.MakeSeed())\n\tdefault:\n\t\tpanic(fmt.Sprintf(\"unexpected resize hint: %d\", hint))\n\t}\n\n\t// Copy the data only if we're not clearing the map.\n\tif hint != mapClearHint {\n\t\t// Set up cooperative transfer state.\n\t\t// Next table must be published as the last step.\n\t\tm.resizeIdx.Store(0)\n\t\tm.nextTable.Store(newTable)\n\t\t// Copy the buckets.\n\t\tm.transfer(table, newTable)\n\t}\n\n\t// We're about to publish the new table, but before that\n\t// we must wait for all helpers to finish.\n\tfor resizeHelpers(m.resizeCtl.Load()) != 0 {\n\t\truntime.Gosched()\n\t}\n\tm.table.Store(newTable)\n\tm.nextTable.Store(nil)\n\tctl := resizeCtl(seq+1, 0)\n\tnewCtl := resizeCtl(seq+2, 0)\n\t// Increment the sequence number and wake up all waiters.\n\tm.resizeMu.Lock()\n\t// There may be slowpoke helpers who have just incremented\n\t// the helper counter. This CAS loop makes sure to wait\n\t// for them to back off.\n\tfor !m.resizeCtl.CompareAndSwap(ctl, newCtl) {\n\t\truntime.Gosched()\n\t}\n\tm.resizeCond.Broadcast()\n\tm.resizeMu.Unlock()\n}\n\nfunc (m *Map[K, V]) helpResize(seq uint64) {\n\tfor {\n\t\ttable := m.table.Load()\n\t\tnextTable := m.nextTable.Load()\n\t\tif resizeSeq(m.resizeCtl.Load()) == seq {\n\t\t\tif nextTable == nil || nextTable == table {\n\t\t\t\t// Carry on until the next table is set by the main\n\t\t\t\t// resize goroutine or until the resize finishes.\n\t\t\t\truntime.Gosched()\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t// The resize is still in-progress, so let's try registering\n\t\t\t// as a helper.\n\t\t\tfor {\n\t\t\t\tctl := m.resizeCtl.Load()\n\t\t\t\tif resizeSeq(ctl) != seq || resizeHelpers(ctl) >= uint64(maxResizeHelpers) {\n\t\t\t\t\t// The resize has ended or there are too many helpers.\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tif m.resizeCtl.CompareAndSwap(ctl, ctl+1) {\n\t\t\t\t\t// Yay, we're a resize helper!\n\t\t\t\t\tm.transfer(table, nextTable)\n\t\t\t\t\t// Don't forget to unregister as a helper.\n\t\t\t\t\tm.resizeCtl.Add(^uint64(0))\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tm.waitForResize()\n\t\t}\n\t\tbreak\n\t}\n}\n\nfunc (m *Map[K, V]) transfer(table, newTable *mapTable[K, V]) {\n\ttableLen := len(table.buckets)\n\tnewTableLen := len(newTable.buckets)\n\tstride := (tableLen >> 3) / int(maxResizeHelpers)\n\tif stride < minResizeTransferStride {\n\t\tstride = minResizeTransferStride\n\t}\n\tfor {\n\t\t// Claim work by incrementing resizeIdx.\n\t\tnextIdx := m.resizeIdx.Add(int64(stride))\n\t\tstart := int(nextIdx) - stride\n\t\tif start < 0 {\n\t\t\tstart = 0\n\t\t}\n\t\tif start > tableLen {\n\t\t\tbreak\n\t\t}\n\t\tend := int(nextIdx)\n\t\tif end > tableLen {\n\t\t\tend = tableLen\n\t\t}\n\t\t// Transfer buckets in this range.\n\t\ttotal := 0\n\t\tif newTableLen > tableLen {\n\t\t\t// We're growing the table with 2x multiplier, so entries from a N bucket can\n\t\t\t// only be transferred to N and 2*N buckets in the new table. Thus, destination\n\t\t\t// buckets written by the resize helpers don't intersect, so we don't need to\n\t\t\t// acquire locks in the destination buckets.\n\t\t\tfor i := start; i < end; i++ {\n\t\t\t\ttotal += transferBucketUnsafe(&table.buckets[i], newTable)\n\t\t\t}\n\t\t} else {\n\t\t\t// We're shrinking the table, so all locks must be acquired.\n\t\t\tfor i := start; i < end; i++ {\n\t\t\t\ttotal += transferBucket(&table.buckets[i], newTable)\n\t\t\t}\n\t\t}\n\t\t// The exact counter stripe doesn't matter here, so pick up the one\n\t\t// that corresponds to the start value to avoid contention.\n\t\tnewTable.addSize(uint64(start), total)\n\t}\n}\n\n// Doesn't acquire dest bucket lock.\nfunc transferBucketUnsafe[K comparable, V any](\n\tb *bucketPadded,\n\tdestTable *mapTable[K, V],\n) (copied int) {\n\trootb := b\n\trootb.mu.Lock()\n\tfor {\n\t\tfor i := 0; i < entriesPerMapBucket; i++ {\n\t\t\tif eptr := b.entries[i]; eptr != nil {\n\t\t\t\te := (*entry[K, V])(eptr)\n\t\t\t\thash := maphash.Comparable(destTable.seed, e.key)\n\t\t\t\tbidx := uint64(len(destTable.buckets)-1) & h1(hash)\n\t\t\t\tdestb := &destTable.buckets[bidx]\n\t\t\t\tappendToBucket(h2(hash), e, destb)\n\t\t\t\tcopied++\n\t\t\t}\n\t\t}\n\t\tif b.next == nil {\n\t\t\trootb.mu.Unlock()\n\t\t\treturn\n\t\t}\n\t\tb = (*bucketPadded)(b.next)\n\t}\n}\n\nfunc transferBucket[K comparable, V any](\n\tb *bucketPadded,\n\tdestTable *mapTable[K, V],\n) (copied int) {\n\trootb := b\n\trootb.mu.Lock()\n\tfor {\n\t\tfor i := 0; i < entriesPerMapBucket; i++ {\n\t\t\tif eptr := b.entries[i]; eptr != nil {\n\t\t\t\te := (*entry[K, V])(eptr)\n\t\t\t\thash := maphash.Comparable(destTable.seed, e.key)\n\t\t\t\tbidx := uint64(len(destTable.buckets)-1) & h1(hash)\n\t\t\t\tdestb := &destTable.buckets[bidx]\n\t\t\t\tdestb.mu.Lock()\n\t\t\t\tappendToBucket(h2(hash), e, destb)\n\t\t\t\tdestb.mu.Unlock()\n\t\t\t\tcopied++\n\t\t\t}\n\t\t}\n\t\tif b.next == nil {\n\t\t\trootb.mu.Unlock()\n\t\t\treturn\n\t\t}\n\t\tb = (*bucketPadded)(b.next)\n\t}\n}\n\n// Range calls f sequentially for each key and value present in the\n// map. If f returns false, range stops the iteration.\n//\n// Range does not necessarily correspond to any consistent snapshot\n// of the Map's contents: no key will be visited more than once, but\n// if the value for any key is stored or deleted concurrently, Range\n// may reflect any mapping for that key from any point during the\n// Range call.\n//\n// It is safe to modify the map while iterating it, including entry\n// creation, modification and deletion. However, the concurrent\n// modification rule apply, i.e. the changes may be not reflected\n// in the subsequently iterated entries.\nfunc (m *Map[K, V]) Range(f func(key K, value V) bool) {\n\tm.initOnce.Do(m.init)\n\t// Pre-allocate array big enough to fit entries for most hash tables.\n\tbentries := make([]*entry[K, V], 0, 16*entriesPerMapBucket)\n\ttable := m.table.Load()\n\tfor i := range table.buckets {\n\t\trootb := &table.buckets[i]\n\t\tb := rootb\n\t\t// Prevent concurrent modifications and copy all entries into\n\t\t// the intermediate slice.\n\t\trootb.mu.Lock()\n\t\tfor {\n\t\t\tfor i := 0; i < entriesPerMapBucket; i++ {\n\t\t\t\tif b.entries[i] != nil {\n\t\t\t\t\tbentries = append(bentries, (*entry[K, V])(b.entries[i]))\n\t\t\t\t}\n\t\t\t}\n\t\t\tif b.next == nil {\n\t\t\t\trootb.mu.Unlock()\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tb = (*bucketPadded)(b.next)\n\t\t}\n\t\t// Call the function for all copied entries.\n\t\tfor j, e := range bentries {\n\t\t\tif !f(e.key, e.value) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\t// Remove the reference to avoid preventing the copied\n\t\t\t// entries from being GCed until this method finishes.\n\t\t\tbentries[j] = nil\n\t\t}\n\t\tbentries = bentries[:0]\n\t}\n}\n\n// Clear deletes all keys and values currently stored in the map.\nfunc (m *Map[K, V]) Clear() {\n\tm.initOnce.Do(m.init)\n\tm.resize(m.table.Load(), mapClearHint)\n}\n\n// Size returns current size of the map.\nfunc (m *Map[K, V]) Size() int {\n\tm.initOnce.Do(m.init)\n\treturn int(m.table.Load().sumSize())\n}\n\n// It is safe to use plain stores here because the destination bucket must be\n// either locked or exclusively written to by the helper during resize.\nfunc appendToBucket[K comparable, V any](h2 uint8, e *entry[K, V], b *bucketPadded) {\n\tfor {\n\t\tfor i := 0; i < entriesPerMapBucket; i++ {\n\t\t\tif b.entries[i] == nil {\n\t\t\t\tb.meta = setByte(b.meta, h2, i)\n\t\t\t\tb.entries[i] = unsafe.Pointer(e)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tif b.next == nil {\n\t\t\tnewb := new(bucketPadded)\n\t\t\tnewb.meta = setByte(defaultMeta, h2, 0)\n\t\t\tnewb.entries[0] = unsafe.Pointer(e)\n\t\t\tb.next = unsafe.Pointer(newb)\n\t\t\treturn\n\t\t}\n\t\tb = (*bucketPadded)(b.next)\n\t}\n}\n\nfunc (table *mapTable[K, V]) addSize(bucketIdx uint64, delta int) {\n\tcidx := uint64(len(table.size)-1) & bucketIdx\n\tatomic.AddInt64(&table.size[cidx].c, int64(delta))\n}\n\nfunc (table *mapTable[K, V]) sumSize() int64 {\n\tsum := int64(0)\n\tfor i := range table.size {\n\t\tsum += atomic.LoadInt64(&table.size[i].c)\n\t}\n\treturn sum\n}\n\nfunc h1(h uint64) uint64 {\n\treturn h >> 7\n}\n\nfunc h2(h uint64) uint8 {\n\treturn uint8(h & 0x7f)\n}\n\n// MapStats is Map statistics.\n//\n// Warning: map statistics are intented to be used for diagnostic\n// purposes, not for production code. This means that breaking changes\n// may be introduced into this struct even between minor releases.\ntype MapStats struct {\n\t// RootBuckets is the number of root buckets in the hash table.\n\t// Each bucket holds a few entries.\n\tRootBuckets int\n\t// TotalBuckets is the total number of buckets in the hash table,\n\t// including root and their chained buckets. Each bucket holds\n\t// a few entries.\n\tTotalBuckets int\n\t// EmptyBuckets is the number of buckets that hold no entries.\n\tEmptyBuckets int\n\t// Capacity is the Map capacity, i.e. the total number of\n\t// entries that all buckets can physically hold. This number\n\t// does not consider the load factor.\n\tCapacity int\n\t// Size is the exact number of entries stored in the map.\n\tSize int\n\t// Counter is the number of entries stored in the map according\n\t// to the internal atomic counter. In case of concurrent map\n\t// modifications this number may be different from Size.\n\tCounter int\n\t// CounterLen is the number of internal atomic counter stripes.\n\t// This number may grow with the map capacity to improve\n\t// multithreaded scalability.\n\tCounterLen int\n\t// MinEntries is the minimum number of entries per a chain of\n\t// buckets, i.e. a root bucket and its chained buckets.\n\tMinEntries int\n\t// MinEntries is the maximum number of entries per a chain of\n\t// buckets, i.e. a root bucket and its chained buckets.\n\tMaxEntries int\n\t// TotalGrowths is the number of times the hash table grew.\n\tTotalGrowths int64\n\t// TotalGrowths is the number of times the hash table shrinked.\n\tTotalShrinks int64\n}\n\n// ToString returns string representation of map stats.\nfunc (s *MapStats) ToString() string {\n\tvar sb strings.Builder\n\tsb.WriteString(\"MapStats{\\n\")\n\tsb.WriteString(fmt.Sprintf(\"RootBuckets:  %d\\n\", s.RootBuckets))\n\tsb.WriteString(fmt.Sprintf(\"TotalBuckets: %d\\n\", s.TotalBuckets))\n\tsb.WriteString(fmt.Sprintf(\"EmptyBuckets: %d\\n\", s.EmptyBuckets))\n\tsb.WriteString(fmt.Sprintf(\"Capacity:     %d\\n\", s.Capacity))\n\tsb.WriteString(fmt.Sprintf(\"Size:         %d\\n\", s.Size))\n\tsb.WriteString(fmt.Sprintf(\"Counter:      %d\\n\", s.Counter))\n\tsb.WriteString(fmt.Sprintf(\"CounterLen:   %d\\n\", s.CounterLen))\n\tsb.WriteString(fmt.Sprintf(\"MinEntries:   %d\\n\", s.MinEntries))\n\tsb.WriteString(fmt.Sprintf(\"MaxEntries:   %d\\n\", s.MaxEntries))\n\tsb.WriteString(fmt.Sprintf(\"TotalGrowths: %d\\n\", s.TotalGrowths))\n\tsb.WriteString(fmt.Sprintf(\"TotalShrinks: %d\\n\", s.TotalShrinks))\n\tsb.WriteString(\"}\\n\")\n\treturn sb.String()\n}\n\n// Stats returns statistics for the Map. Just like other map\n// methods, this one is thread-safe. Yet it's an O(N) operation,\n// so it should be used only for diagnostics or debugging purposes.\nfunc (m *Map[K, V]) Stats() MapStats {\n\tm.initOnce.Do(m.init)\n\tstats := MapStats{\n\t\tTotalGrowths: m.totalGrowths.Load(),\n\t\tTotalShrinks: m.totalShrinks.Load(),\n\t\tMinEntries:   math.MaxInt32,\n\t}\n\ttable := m.table.Load()\n\tstats.RootBuckets = len(table.buckets)\n\tstats.Counter = int(table.sumSize())\n\tstats.CounterLen = len(table.size)\n\tfor i := range table.buckets {\n\t\tnentries := 0\n\t\tb := &table.buckets[i]\n\t\tstats.TotalBuckets++\n\t\tfor {\n\t\t\tnentriesLocal := 0\n\t\t\tstats.Capacity += entriesPerMapBucket\n\t\t\tfor i := 0; i < entriesPerMapBucket; i++ {\n\t\t\t\tif atomic.LoadPointer(&b.entries[i]) != nil {\n\t\t\t\t\tstats.Size++\n\t\t\t\t\tnentriesLocal++\n\t\t\t\t}\n\t\t\t}\n\t\t\tnentries += nentriesLocal\n\t\t\tif nentriesLocal == 0 {\n\t\t\t\tstats.EmptyBuckets++\n\t\t\t}\n\t\t\tif b.next == nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tb = (*bucketPadded)(atomic.LoadPointer(&b.next))\n\t\t\tstats.TotalBuckets++\n\t\t}\n\t\tif nentries < stats.MinEntries {\n\t\t\tstats.MinEntries = nentries\n\t\t}\n\t\tif nentries > stats.MaxEntries {\n\t\t\tstats.MaxEntries = nentries\n\t\t}\n\t}\n\treturn stats\n}\n\nconst (\n\t// cacheLineSize is used in paddings to prevent false sharing;\n\t// 64B are used instead of 128B as a compromise between\n\t// memory footprint and performance; 128B usage may give ~30%\n\t// improvement on NUMA machines.\n\tcacheLineSize = 64\n)\n\n// nextPowOf2 computes the next highest power of 2 of 32-bit v.\n// Source: https://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2\nfunc nextPowOf2(v uint32) uint32 {\n\tif v == 0 {\n\t\treturn 1\n\t}\n\tv--\n\tv |= v >> 1\n\tv |= v >> 2\n\tv |= v >> 4\n\tv |= v >> 8\n\tv |= v >> 16\n\tv++\n\treturn v\n}\n\nfunc parallelism() uint32 {\n\tmaxProcs := uint32(runtime.GOMAXPROCS(0))\n\tnumCores := uint32(runtime.NumCPU())\n\tif maxProcs < numCores {\n\t\treturn maxProcs\n\t}\n\treturn numCores\n}\n\nfunc broadcast(b uint8) uint64 {\n\treturn 0x101010101010101 * uint64(b)\n}\n\nfunc firstMarkedByteIndex(w uint64) int {\n\treturn bits.TrailingZeros64(w) >> 3\n}\n\n// SWAR byte search: may produce false positives, e.g. for 0x0100,\n// so make sure to double-check bytes found by this function.\nfunc markZeroBytes(w uint64) uint64 {\n\treturn ((w - 0x0101010101010101) & (^w) & 0x8080808080808080)\n}\n\n// Sets byte of the input word at the specified index to the given value.\nfunc setByte(w uint64, b uint8, idx int) uint64 {\n\tshift := idx << 3\n\treturn (w &^ (0xff << shift)) | (uint64(b) << shift)\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/xsync/map_extra.go",
    "content": "package xsync\n\n// LoadOrStoreFn returns the existing value for the key if\n// present. Otherwise, it tries to compute the value using the\n// provided function and, if successful, stores and returns\n// the computed value. The loaded result is true if the value was\n// loaded, or false if computed.\n//\n// This call locks a hash table bucket while the compute function\n// is executed. It means that modifications on other entries in\n// the bucket will be blocked until the valueFn executes. Consider\n// this when the function includes long-running operations.\n//\n// Recovery this API and renamed from xsync/v3's LoadOrCompute.\n// We unneeded support no-op (cancel) compute operation, it will only add complexity to existing code.\nfunc (m *Map[K, V]) LoadOrStoreFn(key K, valueFn func() V) (actual V, loaded bool) {\n\treturn m.doCompute(\n\t\tkey,\n\t\tfunc(oldValue V, loaded bool) (V, ComputeOp) {\n\t\t\tif loaded {\n\t\t\t\treturn oldValue, CancelOp\n\t\t\t}\n\t\t\treturn valueFn(), UpdateOp\n\t\t},\n\t\tloadOrComputeOp,\n\t\tfalse,\n\t)\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/xsync/map_extra_test.go",
    "content": "package xsync\n\nimport (\n\t\"strconv\"\n\t\"testing\"\n)\n\nfunc TestMapOfLoadOrStoreFn(t *testing.T) {\n\tconst numEntries = 1000\n\tm := NewMap[string, int]()\n\tfor i := 0; i < numEntries; i++ {\n\t\tv, loaded := m.LoadOrStoreFn(strconv.Itoa(i), func() int {\n\t\t\treturn i\n\t\t})\n\t\tif loaded {\n\t\t\tt.Fatalf(\"value not computed for %d\", i)\n\t\t}\n\t\tif v != i {\n\t\t\tt.Fatalf(\"values do not match for %d: %v\", i, v)\n\t\t}\n\t}\n\tfor i := 0; i < numEntries; i++ {\n\t\tv, loaded := m.LoadOrStoreFn(strconv.Itoa(i), func() int {\n\t\t\treturn i\n\t\t})\n\t\tif !loaded {\n\t\t\tt.Fatalf(\"value not loaded for %d\", i)\n\t\t}\n\t\tif v != i {\n\t\t\tt.Fatalf(\"values do not match for %d: %v\", i, v)\n\t\t}\n\t}\n}\n\nfunc TestMapOfLoadOrStoreFn_FunctionCalledOnce(t *testing.T) {\n\tm := NewMap[int, int]()\n\tfor i := 0; i < 100; {\n\t\tm.LoadOrStoreFn(i, func() (v int) {\n\t\t\tv, i = i, i+1\n\t\t\treturn v\n\t\t})\n\t}\n\tm.Range(func(k, v int) bool {\n\t\tif k != v {\n\t\t\tt.Fatalf(\"%dth key is not equal to value %d\", k, v)\n\t\t}\n\t\treturn true\n\t})\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/xsync/map_test.go",
    "content": "package xsync\n\nimport (\n\t\"math\"\n\t\"math/rand\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\t\"unsafe\"\n\n\t\"github.com/metacubex/randv2\"\n)\n\nconst (\n\t// number of entries to use in benchmarks\n\tbenchmarkNumEntries = 1_000\n\t// key prefix used in benchmarks\n\tbenchmarkKeyPrefix = \"what_a_looooooooooooooooooooooong_key_prefix_\"\n)\n\ntype point struct {\n\tx int32\n\ty int32\n}\n\nvar benchmarkCases = []struct {\n\tname           string\n\treadPercentage int\n}{\n\t{\"reads=100%\", 100}, // 100% loads,    0% stores,    0% deletes\n\t{\"reads=99%\", 99},   //  99% loads,  0.5% stores,  0.5% deletes\n\t{\"reads=90%\", 90},   //  90% loads,    5% stores,    5% deletes\n\t{\"reads=75%\", 75},   //  75% loads, 12.5% stores, 12.5% deletes\n}\n\nvar benchmarkKeys []string\n\nfunc init() {\n\tbenchmarkKeys = make([]string, benchmarkNumEntries)\n\tfor i := 0; i < benchmarkNumEntries; i++ {\n\t\tbenchmarkKeys[i] = benchmarkKeyPrefix + strconv.Itoa(i)\n\t}\n}\n\nfunc runParallel(b *testing.B, benchFn func(pb *testing.PB)) {\n\tb.ResetTimer()\n\tstart := time.Now()\n\tb.RunParallel(benchFn)\n\topsPerSec := float64(b.N) / float64(time.Since(start).Seconds())\n\tb.ReportMetric(opsPerSec, \"ops/s\")\n}\n\nfunc TestMap_BucketStructSize(t *testing.T) {\n\tsize := unsafe.Sizeof(bucketPadded{})\n\tif size != 64 {\n\t\tt.Fatalf(\"size of 64B (one cache line) is expected, got: %d\", size)\n\t}\n\tsize = unsafe.Sizeof(bucketPadded{})\n\tif size != 64 {\n\t\tt.Fatalf(\"size of 64B (one cache line) is expected, got: %d\", size)\n\t}\n}\n\nfunc TestMap_MissingEntry(t *testing.T) {\n\tm := NewMap[string, string]()\n\tv, ok := m.Load(\"foo\")\n\tif ok {\n\t\tt.Fatalf(\"value was not expected: %v\", v)\n\t}\n\tif deleted, loaded := m.LoadAndDelete(\"foo\"); loaded {\n\t\tt.Fatalf(\"value was not expected %v\", deleted)\n\t}\n\tif actual, loaded := m.LoadOrStore(\"foo\", \"bar\"); loaded {\n\t\tt.Fatalf(\"value was not expected %v\", actual)\n\t}\n}\n\nfunc TestMap_EmptyStringKey(t *testing.T) {\n\tm := NewMap[string, string]()\n\tm.Store(\"\", \"foobar\")\n\tv, ok := m.Load(\"\")\n\tif !ok {\n\t\tt.Fatal(\"value was expected\")\n\t}\n\tif v != \"foobar\" {\n\t\tt.Fatalf(\"value does not match: %v\", v)\n\t}\n}\n\nfunc TestMapStore_NilValue(t *testing.T) {\n\tm := NewMap[string, *struct{}]()\n\tm.Store(\"foo\", nil)\n\tv, ok := m.Load(\"foo\")\n\tif !ok {\n\t\tt.Fatal(\"nil value was expected\")\n\t}\n\tif v != nil {\n\t\tt.Fatalf(\"value was not nil: %v\", v)\n\t}\n}\n\nfunc TestMapLoadOrStore_NilValue(t *testing.T) {\n\tm := NewMap[string, *struct{}]()\n\tm.LoadOrStore(\"foo\", nil)\n\tv, loaded := m.LoadOrStore(\"foo\", nil)\n\tif !loaded {\n\t\tt.Fatal(\"nil value was expected\")\n\t}\n\tif v != nil {\n\t\tt.Fatalf(\"value was not nil: %v\", v)\n\t}\n}\n\nfunc TestMapLoadOrStore_NonNilValue(t *testing.T) {\n\ttype foo struct{}\n\tm := NewMap[string, *foo]()\n\tnewv := &foo{}\n\tv, loaded := m.LoadOrStore(\"foo\", newv)\n\tif loaded {\n\t\tt.Fatal(\"no value was expected\")\n\t}\n\tif v != newv {\n\t\tt.Fatalf(\"value does not match: %v\", v)\n\t}\n\tnewv2 := &foo{}\n\tv, loaded = m.LoadOrStore(\"foo\", newv2)\n\tif !loaded {\n\t\tt.Fatal(\"value was expected\")\n\t}\n\tif v != newv {\n\t\tt.Fatalf(\"value does not match: %v\", v)\n\t}\n}\n\nfunc TestMapLoadAndStore_NilValue(t *testing.T) {\n\tm := NewMap[string, *struct{}]()\n\tm.LoadAndStore(\"foo\", nil)\n\tv, loaded := m.LoadAndStore(\"foo\", nil)\n\tif !loaded {\n\t\tt.Fatal(\"nil value was expected\")\n\t}\n\tif v != nil {\n\t\tt.Fatalf(\"value was not nil: %v\", v)\n\t}\n\tv, loaded = m.Load(\"foo\")\n\tif !loaded {\n\t\tt.Fatal(\"nil value was expected\")\n\t}\n\tif v != nil {\n\t\tt.Fatalf(\"value was not nil: %v\", v)\n\t}\n}\n\nfunc TestMapLoadAndStore_NonNilValue(t *testing.T) {\n\tm := NewMap[string, int]()\n\tv1 := 1\n\tv, loaded := m.LoadAndStore(\"foo\", v1)\n\tif loaded {\n\t\tt.Fatal(\"no value was expected\")\n\t}\n\tif v != v1 {\n\t\tt.Fatalf(\"value does not match: %v\", v)\n\t}\n\tv2 := 2\n\tv, loaded = m.LoadAndStore(\"foo\", v2)\n\tif !loaded {\n\t\tt.Fatal(\"value was expected\")\n\t}\n\tif v != v1 {\n\t\tt.Fatalf(\"value does not match: %v\", v)\n\t}\n\tv, loaded = m.Load(\"foo\")\n\tif !loaded {\n\t\tt.Fatal(\"value was expected\")\n\t}\n\tif v != v2 {\n\t\tt.Fatalf(\"value does not match: %v\", v)\n\t}\n}\n\nfunc TestMapRange(t *testing.T) {\n\tconst numEntries = 1000\n\tm := NewMap[string, int]()\n\tfor i := 0; i < numEntries; i++ {\n\t\tm.Store(strconv.Itoa(i), i)\n\t}\n\titers := 0\n\tmet := make(map[string]int)\n\tm.Range(func(key string, value int) bool {\n\t\tif key != strconv.Itoa(value) {\n\t\t\tt.Fatalf(\"got unexpected key/value for iteration %d: %v/%v\", iters, key, value)\n\t\t\treturn false\n\t\t}\n\t\tmet[key] += 1\n\t\titers++\n\t\treturn true\n\t})\n\tif iters != numEntries {\n\t\tt.Fatalf(\"got unexpected number of iterations: %d\", iters)\n\t}\n\tfor i := 0; i < numEntries; i++ {\n\t\tif c := met[strconv.Itoa(i)]; c != 1 {\n\t\t\tt.Fatalf(\"range did not iterate correctly over %d: %d\", i, c)\n\t\t}\n\t}\n}\n\nfunc TestMapRange_FalseReturned(t *testing.T) {\n\tm := NewMap[string, int]()\n\tfor i := 0; i < 100; i++ {\n\t\tm.Store(strconv.Itoa(i), i)\n\t}\n\titers := 0\n\tm.Range(func(key string, value int) bool {\n\t\titers++\n\t\treturn iters != 13\n\t})\n\tif iters != 13 {\n\t\tt.Fatalf(\"got unexpected number of iterations: %d\", iters)\n\t}\n}\n\nfunc TestMapRange_NestedDelete(t *testing.T) {\n\tconst numEntries = 256\n\tm := NewMap[string, int]()\n\tfor i := 0; i < numEntries; i++ {\n\t\tm.Store(strconv.Itoa(i), i)\n\t}\n\tm.Range(func(key string, value int) bool {\n\t\tm.Delete(key)\n\t\treturn true\n\t})\n\tfor i := 0; i < numEntries; i++ {\n\t\tif _, ok := m.Load(strconv.Itoa(i)); ok {\n\t\t\tt.Fatalf(\"value found for %d\", i)\n\t\t}\n\t}\n}\n\nfunc TestMapStringStore(t *testing.T) {\n\tconst numEntries = 128\n\tm := NewMap[string, int]()\n\tfor i := 0; i < numEntries; i++ {\n\t\tm.Store(strconv.Itoa(i), i)\n\t}\n\tfor i := 0; i < numEntries; i++ {\n\t\tv, ok := m.Load(strconv.Itoa(i))\n\t\tif !ok {\n\t\t\tt.Fatalf(\"value not found for %d\", i)\n\t\t}\n\t\tif v != i {\n\t\t\tt.Fatalf(\"values do not match for %d: %v\", i, v)\n\t\t}\n\t}\n}\n\nfunc TestMapIntStore(t *testing.T) {\n\tconst numEntries = 128\n\tm := NewMap[int, int]()\n\tfor i := 0; i < numEntries; i++ {\n\t\tm.Store(i, i)\n\t}\n\tfor i := 0; i < numEntries; i++ {\n\t\tv, ok := m.Load(i)\n\t\tif !ok {\n\t\t\tt.Fatalf(\"value not found for %d\", i)\n\t\t}\n\t\tif v != i {\n\t\t\tt.Fatalf(\"values do not match for %d: %v\", i, v)\n\t\t}\n\t}\n}\n\nfunc TestMapStore_StructKeys_IntValues(t *testing.T) {\n\tconst numEntries = 128\n\tm := NewMap[point, int]()\n\tfor i := 0; i < numEntries; i++ {\n\t\tm.Store(point{int32(i), -int32(i)}, i)\n\t}\n\tfor i := 0; i < numEntries; i++ {\n\t\tv, ok := m.Load(point{int32(i), -int32(i)})\n\t\tif !ok {\n\t\t\tt.Fatalf(\"value not found for %d\", i)\n\t\t}\n\t\tif v != i {\n\t\t\tt.Fatalf(\"values do not match for %d: %v\", i, v)\n\t\t}\n\t}\n}\n\nfunc TestMapStore_StructKeys_StructValues(t *testing.T) {\n\tconst numEntries = 128\n\tm := NewMap[point, point]()\n\tfor i := 0; i < numEntries; i++ {\n\t\tm.Store(point{int32(i), -int32(i)}, point{-int32(i), int32(i)})\n\t}\n\tfor i := 0; i < numEntries; i++ {\n\t\tv, ok := m.Load(point{int32(i), -int32(i)})\n\t\tif !ok {\n\t\t\tt.Fatalf(\"value not found for %d\", i)\n\t\t}\n\t\tif v.x != -int32(i) {\n\t\t\tt.Fatalf(\"x value does not match for %d: %v\", i, v)\n\t\t}\n\t\tif v.y != int32(i) {\n\t\t\tt.Fatalf(\"y value does not match for %d: %v\", i, v)\n\t\t}\n\t}\n}\n\nfunc TestMapLoadOrStore(t *testing.T) {\n\tconst numEntries = 1000\n\tm := NewMap[string, int]()\n\tfor i := 0; i < numEntries; i++ {\n\t\tm.Store(strconv.Itoa(i), i)\n\t}\n\tfor i := 0; i < numEntries; i++ {\n\t\tif _, loaded := m.LoadOrStore(strconv.Itoa(i), i); !loaded {\n\t\t\tt.Fatalf(\"value not found for %d\", i)\n\t\t}\n\t}\n}\n\nfunc TestMapLoadOrCompute(t *testing.T) {\n\tconst numEntries = 1000\n\tm := NewMap[string, int]()\n\tfor i := 0; i < numEntries; i++ {\n\t\tv, loaded := m.LoadOrCompute(strconv.Itoa(i), func() (newValue int, cancel bool) {\n\t\t\treturn i, true\n\t\t})\n\t\tif loaded {\n\t\t\tt.Fatalf(\"value not computed for %d\", i)\n\t\t}\n\t\tif v != 0 {\n\t\t\tt.Fatalf(\"values do not match for %d: %v\", i, v)\n\t\t}\n\t}\n\tif m.Size() != 0 {\n\t\tt.Fatalf(\"zero map size expected: %d\", m.Size())\n\t}\n\tfor i := 0; i < numEntries; i++ {\n\t\tv, loaded := m.LoadOrCompute(strconv.Itoa(i), func() (newValue int, cancel bool) {\n\t\t\treturn i, false\n\t\t})\n\t\tif loaded {\n\t\t\tt.Fatalf(\"value not computed for %d\", i)\n\t\t}\n\t\tif v != i {\n\t\t\tt.Fatalf(\"values do not match for %d: %v\", i, v)\n\t\t}\n\t}\n\tfor i := 0; i < numEntries; i++ {\n\t\tv, loaded := m.LoadOrCompute(strconv.Itoa(i), func() (newValue int, cancel bool) {\n\t\t\tt.Fatalf(\"value func invoked\")\n\t\t\treturn newValue, false\n\t\t})\n\t\tif !loaded {\n\t\t\tt.Fatalf(\"value not loaded for %d\", i)\n\t\t}\n\t\tif v != i {\n\t\t\tt.Fatalf(\"values do not match for %d: %v\", i, v)\n\t\t}\n\t}\n}\n\nfunc TestMapLoadOrCompute_FunctionCalledOnce(t *testing.T) {\n\tm := NewMap[int, int]()\n\tfor i := 0; i < 100; {\n\t\tm.LoadOrCompute(i, func() (newValue int, cancel bool) {\n\t\t\tnewValue, i = i, i+1\n\t\t\treturn newValue, false\n\t\t})\n\t}\n\tm.Range(func(k, v int) bool {\n\t\tif k != v {\n\t\t\tt.Fatalf(\"%dth key is not equal to value %d\", k, v)\n\t\t}\n\t\treturn true\n\t})\n}\n\nfunc TestMapOfCompute(t *testing.T) {\n\tm := NewMap[string, int]()\n\t// Store a new value.\n\tv, ok := m.Compute(\"foobar\", func(oldValue int, loaded bool) (newValue int, op ComputeOp) {\n\t\tif oldValue != 0 {\n\t\t\tt.Fatalf(\"oldValue should be 0 when computing a new value: %d\", oldValue)\n\t\t}\n\t\tif loaded {\n\t\t\tt.Fatal(\"loaded should be false when computing a new value\")\n\t\t}\n\t\tnewValue = 42\n\t\top = UpdateOp\n\t\treturn\n\t})\n\tif v != 42 {\n\t\tt.Fatalf(\"v should be 42 when computing a new value: %d\", v)\n\t}\n\tif !ok {\n\t\tt.Fatal(\"ok should be true when computing a new value\")\n\t}\n\t// Update an existing value.\n\tv, ok = m.Compute(\"foobar\", func(oldValue int, loaded bool) (newValue int, op ComputeOp) {\n\t\tif oldValue != 42 {\n\t\t\tt.Fatalf(\"oldValue should be 42 when updating the value: %d\", oldValue)\n\t\t}\n\t\tif !loaded {\n\t\t\tt.Fatal(\"loaded should be true when updating the value\")\n\t\t}\n\t\tnewValue = oldValue + 42\n\t\top = UpdateOp\n\t\treturn\n\t})\n\tif v != 84 {\n\t\tt.Fatalf(\"v should be 84 when updating the value: %d\", v)\n\t}\n\tif !ok {\n\t\tt.Fatal(\"ok should be true when updating the value\")\n\t}\n\t// Check that NoOp doesn't update the value\n\tv, ok = m.Compute(\"foobar\", func(oldValue int, loaded bool) (newValue int, op ComputeOp) {\n\t\treturn 0, CancelOp\n\t})\n\tif v != 84 {\n\t\tt.Fatalf(\"v should be 84 after using NoOp: %d\", v)\n\t}\n\tif !ok {\n\t\tt.Fatal(\"ok should be true when updating the value\")\n\t}\n\t// Delete an existing value.\n\tv, ok = m.Compute(\"foobar\", func(oldValue int, loaded bool) (newValue int, op ComputeOp) {\n\t\tif oldValue != 84 {\n\t\t\tt.Fatalf(\"oldValue should be 84 when deleting the value: %d\", oldValue)\n\t\t}\n\t\tif !loaded {\n\t\t\tt.Fatal(\"loaded should be true when deleting the value\")\n\t\t}\n\t\top = DeleteOp\n\t\treturn\n\t})\n\tif v != 84 {\n\t\tt.Fatalf(\"v should be 84 when deleting the value: %d\", v)\n\t}\n\tif ok {\n\t\tt.Fatal(\"ok should be false when deleting the value\")\n\t}\n\t// Try to delete a non-existing value. Notice different key.\n\tv, ok = m.Compute(\"barbaz\", func(oldValue int, loaded bool) (newValue int, op ComputeOp) {\n\t\tif oldValue != 0 {\n\t\t\tt.Fatalf(\"oldValue should be 0 when trying to delete a non-existing value: %d\", oldValue)\n\t\t}\n\t\tif loaded {\n\t\t\tt.Fatal(\"loaded should be false when trying to delete a non-existing value\")\n\t\t}\n\t\t// We're returning a non-zero value, but the map should ignore it.\n\t\tnewValue = 42\n\t\top = DeleteOp\n\t\treturn\n\t})\n\tif v != 0 {\n\t\tt.Fatalf(\"v should be 0 when trying to delete a non-existing value: %d\", v)\n\t}\n\tif ok {\n\t\tt.Fatal(\"ok should be false when trying to delete a non-existing value\")\n\t}\n\t// Try NoOp on a non-existing value\n\tv, ok = m.Compute(\"barbaz\", func(oldValue int, loaded bool) (newValue int, op ComputeOp) {\n\t\tif oldValue != 0 {\n\t\t\tt.Fatalf(\"oldValue should be 0 when trying to delete a non-existing value: %d\", oldValue)\n\t\t}\n\t\tif loaded {\n\t\t\tt.Fatal(\"loaded should be false when trying to delete a non-existing value\")\n\t\t}\n\t\t// We're returning a non-zero value, but the map should ignore it.\n\t\tnewValue = 42\n\t\top = CancelOp\n\t\treturn\n\t})\n\tif v != 0 {\n\t\tt.Fatalf(\"v should be 0 when trying to delete a non-existing value: %d\", v)\n\t}\n\tif ok {\n\t\tt.Fatal(\"ok should be false when trying to delete a non-existing value\")\n\t}\n}\n\nfunc TestMapStringStoreThenDelete(t *testing.T) {\n\tconst numEntries = 1000\n\tm := NewMap[string, int]()\n\tfor i := 0; i < numEntries; i++ {\n\t\tm.Store(strconv.Itoa(i), i)\n\t}\n\tfor i := 0; i < numEntries; i++ {\n\t\tm.Delete(strconv.Itoa(i))\n\t\tif _, ok := m.Load(strconv.Itoa(i)); ok {\n\t\t\tt.Fatalf(\"value was not expected for %d\", i)\n\t\t}\n\t}\n}\n\nfunc TestMapIntStoreThenDelete(t *testing.T) {\n\tconst numEntries = 1000\n\tm := NewMap[int32, int32]()\n\tfor i := 0; i < numEntries; i++ {\n\t\tm.Store(int32(i), int32(i))\n\t}\n\tfor i := 0; i < numEntries; i++ {\n\t\tm.Delete(int32(i))\n\t\tif _, ok := m.Load(int32(i)); ok {\n\t\t\tt.Fatalf(\"value was not expected for %d\", i)\n\t\t}\n\t}\n}\n\nfunc TestMapStructStoreThenDelete(t *testing.T) {\n\tconst numEntries = 1000\n\tm := NewMap[point, string]()\n\tfor i := 0; i < numEntries; i++ {\n\t\tm.Store(point{int32(i), 42}, strconv.Itoa(i))\n\t}\n\tfor i := 0; i < numEntries; i++ {\n\t\tm.Delete(point{int32(i), 42})\n\t\tif _, ok := m.Load(point{int32(i), 42}); ok {\n\t\t\tt.Fatalf(\"value was not expected for %d\", i)\n\t\t}\n\t}\n}\n\nfunc TestMapStringStoreThenLoadAndDelete(t *testing.T) {\n\tconst numEntries = 1000\n\tm := NewMap[string, int]()\n\tfor i := 0; i < numEntries; i++ {\n\t\tm.Store(strconv.Itoa(i), i)\n\t}\n\tfor i := 0; i < numEntries; i++ {\n\t\tif v, loaded := m.LoadAndDelete(strconv.Itoa(i)); !loaded || v != i {\n\t\t\tt.Fatalf(\"value was not found or different for %d: %v\", i, v)\n\t\t}\n\t\tif _, ok := m.Load(strconv.Itoa(i)); ok {\n\t\t\tt.Fatalf(\"value was not expected for %d\", i)\n\t\t}\n\t}\n}\n\nfunc TestMapIntStoreThenLoadAndDelete(t *testing.T) {\n\tconst numEntries = 1000\n\tm := NewMap[int, int]()\n\tfor i := 0; i < numEntries; i++ {\n\t\tm.Store(i, i)\n\t}\n\tfor i := 0; i < numEntries; i++ {\n\t\tif _, loaded := m.LoadAndDelete(i); !loaded {\n\t\t\tt.Fatalf(\"value was not found for %d\", i)\n\t\t}\n\t\tif _, ok := m.Load(i); ok {\n\t\t\tt.Fatalf(\"value was not expected for %d\", i)\n\t\t}\n\t}\n}\n\nfunc TestMapStructStoreThenLoadAndDelete(t *testing.T) {\n\tconst numEntries = 1000\n\tm := NewMap[point, int]()\n\tfor i := 0; i < numEntries; i++ {\n\t\tm.Store(point{42, int32(i)}, i)\n\t}\n\tfor i := 0; i < numEntries; i++ {\n\t\tif _, loaded := m.LoadAndDelete(point{42, int32(i)}); !loaded {\n\t\t\tt.Fatalf(\"value was not found for %d\", i)\n\t\t}\n\t\tif _, ok := m.Load(point{42, int32(i)}); ok {\n\t\t\tt.Fatalf(\"value was not expected for %d\", i)\n\t\t}\n\t}\n}\n\nfunc TestMapStoreThenParallelDelete_DoesNotShrinkBelowMinTableLen(t *testing.T) {\n\tconst numEntries = 1000\n\tm := NewMap[int, int]()\n\tfor i := 0; i < numEntries; i++ {\n\t\tm.Store(i, i)\n\t}\n\n\tcdone := make(chan bool)\n\tgo func() {\n\t\tfor i := 0; i < numEntries; i++ {\n\t\t\tm.Delete(i)\n\t\t}\n\t\tcdone <- true\n\t}()\n\tgo func() {\n\t\tfor i := 0; i < numEntries; i++ {\n\t\t\tm.Delete(i)\n\t\t}\n\t\tcdone <- true\n\t}()\n\n\t// Wait for the goroutines to finish.\n\t<-cdone\n\t<-cdone\n\n\tstats := m.Stats()\n\tif stats.RootBuckets != defaultMinMapTableLen {\n\t\tt.Fatalf(\"table length was different from the minimum: %d\", stats.RootBuckets)\n\t}\n}\n\nfunc sizeBasedOnTypedRange(m *Map[string, int]) int {\n\tsize := 0\n\tm.Range(func(key string, value int) bool {\n\t\tsize++\n\t\treturn true\n\t})\n\treturn size\n}\n\nfunc TestMapSize(t *testing.T) {\n\tconst numEntries = 1000\n\tm := NewMap[string, int]()\n\tsize := m.Size()\n\tif size != 0 {\n\t\tt.Fatalf(\"zero size expected: %d\", size)\n\t}\n\texpectedSize := 0\n\tfor i := 0; i < numEntries; i++ {\n\t\tm.Store(strconv.Itoa(i), i)\n\t\texpectedSize++\n\t\tsize := m.Size()\n\t\tif size != expectedSize {\n\t\t\tt.Fatalf(\"size of %d was expected, got: %d\", expectedSize, size)\n\t\t}\n\t\trsize := sizeBasedOnTypedRange(m)\n\t\tif size != rsize {\n\t\t\tt.Fatalf(\"size does not match number of entries in Range: %v, %v\", size, rsize)\n\t\t}\n\t}\n\tfor i := 0; i < numEntries; i++ {\n\t\tm.Delete(strconv.Itoa(i))\n\t\texpectedSize--\n\t\tsize := m.Size()\n\t\tif size != expectedSize {\n\t\t\tt.Fatalf(\"size of %d was expected, got: %d\", expectedSize, size)\n\t\t}\n\t\trsize := sizeBasedOnTypedRange(m)\n\t\tif size != rsize {\n\t\t\tt.Fatalf(\"size does not match number of entries in Range: %v, %v\", size, rsize)\n\t\t}\n\t}\n}\n\nfunc TestMapClear(t *testing.T) {\n\tconst numEntries = 1000\n\tm := NewMap[string, int]()\n\tfor i := 0; i < numEntries; i++ {\n\t\tm.Store(strconv.Itoa(i), i)\n\t}\n\tsize := m.Size()\n\tif size != numEntries {\n\t\tt.Fatalf(\"size of %d was expected, got: %d\", numEntries, size)\n\t}\n\tm.Clear()\n\tsize = m.Size()\n\tif size != 0 {\n\t\tt.Fatalf(\"zero size was expected, got: %d\", size)\n\t}\n\trsize := sizeBasedOnTypedRange(m)\n\tif rsize != 0 {\n\t\tt.Fatalf(\"zero number of entries in Range was expected, got: %d\", rsize)\n\t}\n}\n\nfunc assertMapCapacity[K comparable, V any](t *testing.T, m *Map[K, V], expectedCap int) {\n\tstats := m.Stats()\n\tif stats.Capacity != expectedCap {\n\t\tt.Fatalf(\"capacity was different from %d: %d\", expectedCap, stats.Capacity)\n\t}\n}\n\nfunc TestNewMapWithPresize(t *testing.T) {\n\tassertMapCapacity(t, NewMap[string, string](), defaultMinMapTableLen*entriesPerMapBucket)\n\tassertMapCapacity(t, NewMap[string, string](WithPresize(0)), defaultMinMapTableLen*entriesPerMapBucket)\n\tassertMapCapacity(t, NewMap[string, string](WithPresize(-100)), defaultMinMapTableLen*entriesPerMapBucket)\n\tassertMapCapacity(t, NewMap[string, string](WithPresize(500)), 1280)\n\tassertMapCapacity(t, NewMap[int, int](WithPresize(1_000_000)), 2621440)\n\tassertMapCapacity(t, NewMap[point, point](WithPresize(100)), 160)\n}\n\nfunc TestNewMapWithPresize_DoesNotShrinkBelowMinTableLen(t *testing.T) {\n\tconst minTableLen = 1024\n\tconst numEntries = int(minTableLen * entriesPerMapBucket * mapLoadFactor)\n\tm := NewMap[int, int](WithPresize(numEntries))\n\tfor i := 0; i < 2*numEntries; i++ {\n\t\tm.Store(i, i)\n\t}\n\n\tstats := m.Stats()\n\tif stats.RootBuckets <= minTableLen {\n\t\tt.Fatalf(\"table did not grow: %d\", stats.RootBuckets)\n\t}\n\n\tfor i := 0; i < 2*numEntries; i++ {\n\t\tm.Delete(i)\n\t}\n\n\tstats = m.Stats()\n\tif stats.RootBuckets != minTableLen {\n\t\tt.Fatalf(\"table length was different from the minimum: %d\", stats.RootBuckets)\n\t}\n}\n\nfunc TestNewMapGrowOnly_OnlyShrinksOnClear(t *testing.T) {\n\tconst minTableLen = 128\n\tconst numEntries = minTableLen * entriesPerMapBucket\n\tm := NewMap[int, int](WithPresize(numEntries), WithGrowOnly())\n\n\tstats := m.Stats()\n\tinitialTableLen := stats.RootBuckets\n\n\tfor i := 0; i < 2*numEntries; i++ {\n\t\tm.Store(i, i)\n\t}\n\tstats = m.Stats()\n\tmaxTableLen := stats.RootBuckets\n\tif maxTableLen <= minTableLen {\n\t\tt.Fatalf(\"table did not grow: %d\", maxTableLen)\n\t}\n\n\tfor i := 0; i < numEntries; i++ {\n\t\tm.Delete(i)\n\t}\n\tstats = m.Stats()\n\tif stats.RootBuckets != maxTableLen {\n\t\tt.Fatalf(\"table length was different from the expected: %d\", stats.RootBuckets)\n\t}\n\n\tm.Clear()\n\tstats = m.Stats()\n\tif stats.RootBuckets != initialTableLen {\n\t\tt.Fatalf(\"table length was different from the initial: %d\", stats.RootBuckets)\n\t}\n}\n\nfunc TestMapResize(t *testing.T) {\n\tm := NewMap[string, int]()\n\tconst numEntries = 100_000\n\n\tfor i := 0; i < numEntries; i++ {\n\t\tm.Store(strconv.Itoa(i), i)\n\t}\n\tstats := m.Stats()\n\tif stats.Size != numEntries {\n\t\tt.Fatalf(\"size was too small: %d\", stats.Size)\n\t}\n\texpectedCapacity := int(math.RoundToEven(mapLoadFactor+1)) * stats.RootBuckets * entriesPerMapBucket\n\tif stats.Capacity > expectedCapacity {\n\t\tt.Fatalf(\"capacity was too large: %d, expected: %d\", stats.Capacity, expectedCapacity)\n\t}\n\tif stats.RootBuckets <= defaultMinMapTableLen {\n\t\tt.Fatalf(\"table was too small: %d\", stats.RootBuckets)\n\t}\n\tif stats.TotalGrowths == 0 {\n\t\tt.Fatalf(\"non-zero total growths expected: %d\", stats.TotalGrowths)\n\t}\n\tif stats.TotalShrinks > 0 {\n\t\tt.Fatalf(\"zero total shrinks expected: %d\", stats.TotalShrinks)\n\t}\n\t// This is useful when debugging table resize and occupancy.\n\t// Use -v flag to see the output.\n\tt.Log(stats.ToString())\n\n\tfor i := 0; i < numEntries; i++ {\n\t\tm.Delete(strconv.Itoa(i))\n\t}\n\tstats = m.Stats()\n\tif stats.Size > 0 {\n\t\tt.Fatalf(\"zero size was expected: %d\", stats.Size)\n\t}\n\texpectedCapacity = stats.RootBuckets * entriesPerMapBucket\n\tif stats.Capacity != expectedCapacity {\n\t\tt.Fatalf(\"capacity was too large: %d, expected: %d\", stats.Capacity, expectedCapacity)\n\t}\n\tif stats.RootBuckets != defaultMinMapTableLen {\n\t\tt.Fatalf(\"table was too large: %d\", stats.RootBuckets)\n\t}\n\tif stats.TotalShrinks == 0 {\n\t\tt.Fatalf(\"non-zero total shrinks expected: %d\", stats.TotalShrinks)\n\t}\n\tt.Log(stats.ToString())\n}\n\nfunc TestMapResize_CounterLenLimit(t *testing.T) {\n\tconst numEntries = 1_000_000\n\tm := NewMap[string, string]()\n\n\tfor i := 0; i < numEntries; i++ {\n\t\tm.Store(\"foo\"+strconv.Itoa(i), \"bar\"+strconv.Itoa(i))\n\t}\n\tstats := m.Stats()\n\tif stats.Size != numEntries {\n\t\tt.Fatalf(\"size was too small: %d\", stats.Size)\n\t}\n\tif stats.CounterLen != maxMapCounterLen {\n\t\tt.Fatalf(\"number of counter stripes was too large: %d, expected: %d\",\n\t\t\tstats.CounterLen, maxMapCounterLen)\n\t}\n}\n\nfunc testParallelResize(t *testing.T, numGoroutines int) {\n\tm := NewMap[int, int]()\n\n\t// Fill the map to trigger resizing\n\tconst initialEntries = 10000\n\tconst newEntries = 5000\n\tfor i := 0; i < initialEntries; i++ {\n\t\tm.Store(i, i*2)\n\t}\n\n\t// Start concurrent operations that should trigger helping behavior\n\tvar wg sync.WaitGroup\n\n\t// Launch goroutines that will encounter resize operations\n\tfor g := 0; g < numGoroutines; g++ {\n\t\twg.Add(1)\n\t\tgo func(goroutineID int) {\n\t\t\tdefer wg.Done()\n\n\t\t\t// Perform many operations to trigger resize and helping\n\t\t\tfor i := 0; i < newEntries; i++ {\n\t\t\t\tkey := goroutineID*newEntries + i + initialEntries\n\t\t\t\tm.Store(key, key*2)\n\n\t\t\t\t// Verify the value\n\t\t\t\tif val, ok := m.Load(key); !ok || val != key*2 {\n\t\t\t\t\tt.Errorf(\"Failed to load key %d: got %v, %v\", key, val, ok)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}(g)\n\t}\n\n\twg.Wait()\n\n\t// Verify all entries are present\n\tfinalSize := m.Size()\n\texpectedSize := initialEntries + numGoroutines*newEntries\n\tif finalSize != expectedSize {\n\t\tt.Errorf(\"Expected size %d, got %d\", expectedSize, finalSize)\n\t}\n\n\tstats := m.Stats()\n\tif stats.TotalGrowths == 0 {\n\t\tt.Error(\"Expected at least one table growth due to concurrent operations\")\n\t}\n}\n\nfunc TestMapParallelResize(t *testing.T) {\n\ttestParallelResize(t, 1)\n\ttestParallelResize(t, runtime.GOMAXPROCS(0))\n\ttestParallelResize(t, 100)\n}\n\nfunc testParallelResizeWithSameKeys(t *testing.T, numGoroutines int) {\n\tm := NewMap[int, int]()\n\n\t// Fill the map to trigger resizing\n\tconst entries = 1000\n\tfor i := 0; i < entries; i++ {\n\t\tm.Store(2*i, 2*i)\n\t}\n\n\t// Start concurrent operations that should trigger helping behavior\n\tvar wg sync.WaitGroup\n\n\t// Launch goroutines that will encounter resize operations\n\tfor g := 0; g < numGoroutines; g++ {\n\t\twg.Add(1)\n\t\tgo func(goroutineID int) {\n\t\t\tdefer wg.Done()\n\t\t\tfor i := 0; i < 10*entries; i++ {\n\t\t\t\tm.Store(i, i)\n\t\t\t}\n\t\t}(g)\n\t}\n\n\twg.Wait()\n\n\t// Verify all entries are present\n\tfinalSize := m.Size()\n\texpectedSize := 10 * entries\n\tif finalSize != expectedSize {\n\t\tt.Errorf(\"Expected size %d, got %d\", expectedSize, finalSize)\n\t}\n\n\tstats := m.Stats()\n\tif stats.TotalGrowths == 0 {\n\t\tt.Error(\"Expected at least one table growth due to concurrent operations\")\n\t}\n}\n\nfunc TestMapParallelResize_IntersectingKeys(t *testing.T) {\n\ttestParallelResizeWithSameKeys(t, 1)\n\ttestParallelResizeWithSameKeys(t, runtime.GOMAXPROCS(0))\n\ttestParallelResizeWithSameKeys(t, 100)\n}\n\nfunc testParallelShrinking(t *testing.T, numGoroutines int) {\n\tm := NewMap[int, int]()\n\n\t// Fill the map to trigger resizing\n\tconst entries = 100000\n\tfor i := 0; i < entries; i++ {\n\t\tm.Store(i, i)\n\t}\n\n\t// Start concurrent operations that should trigger helping behavior\n\tvar wg sync.WaitGroup\n\n\t// Launch goroutines that will encounter resize operations\n\tfor g := 0; g < numGoroutines; g++ {\n\t\twg.Add(1)\n\t\tgo func(goroutineID int) {\n\t\t\tdefer wg.Done()\n\t\t\tfor i := 0; i < entries; i++ {\n\t\t\t\tm.Delete(i)\n\t\t\t}\n\t\t}(g)\n\t}\n\n\twg.Wait()\n\n\t// Verify all entries are present\n\tfinalSize := m.Size()\n\tif finalSize != 0 {\n\t\tt.Errorf(\"Expected size 0, got %d\", finalSize)\n\t}\n\n\tstats := m.Stats()\n\tif stats.TotalShrinks == 0 {\n\t\tt.Error(\"Expected at least one table shrinking due to concurrent operations\")\n\t}\n}\n\nfunc TestMapParallelShrinking(t *testing.T) {\n\ttestParallelShrinking(t, 1)\n\ttestParallelShrinking(t, runtime.GOMAXPROCS(0))\n\ttestParallelShrinking(t, 100)\n}\n\nfunc parallelSeqMapGrower(m *Map[int, int], numEntries int, positive bool, cdone chan bool) {\n\tfor i := 0; i < numEntries; i++ {\n\t\tif positive {\n\t\t\tm.Store(i, i)\n\t\t} else {\n\t\t\tm.Store(-i, -i)\n\t\t}\n\t}\n\tcdone <- true\n}\n\nfunc TestMapParallelGrowth_GrowOnly(t *testing.T) {\n\tconst numEntries = 100_000\n\tm := NewMap[int, int]()\n\tcdone := make(chan bool)\n\tgo parallelSeqMapGrower(m, numEntries, true, cdone)\n\tgo parallelSeqMapGrower(m, numEntries, false, cdone)\n\t// Wait for the goroutines to finish.\n\t<-cdone\n\t<-cdone\n\t// Verify map contents.\n\tfor i := -numEntries + 1; i < numEntries; i++ {\n\t\tv, ok := m.Load(i)\n\t\tif !ok {\n\t\t\tt.Fatalf(\"value not found for %d\", i)\n\t\t}\n\t\tif v != i {\n\t\t\tt.Fatalf(\"values do not match for %d: %v\", i, v)\n\t\t}\n\t}\n\tif s := m.Size(); s != 2*numEntries-1 {\n\t\tt.Fatalf(\"unexpected size: %v\", s)\n\t}\n}\n\nfunc parallelRandMapResizer(t *testing.T, m *Map[string, int], numIters, numEntries int, cdone chan bool) {\n\tr := rand.New(rand.NewSource(time.Now().UnixNano()))\n\tfor i := 0; i < numIters; i++ {\n\t\tcoin := r.Int63n(2)\n\t\tfor j := 0; j < numEntries; j++ {\n\t\t\tif coin == 1 {\n\t\t\t\tm.Store(strconv.Itoa(j), j)\n\t\t\t} else {\n\t\t\t\tm.Delete(strconv.Itoa(j))\n\t\t\t}\n\t\t}\n\t}\n\tcdone <- true\n}\n\nfunc TestMapParallelGrowth(t *testing.T) {\n\tconst numIters = 1_000\n\tconst numEntries = 2 * entriesPerMapBucket * defaultMinMapTableLen\n\tm := NewMap[string, int]()\n\tcdone := make(chan bool)\n\tgo parallelRandMapResizer(t, m, numIters, numEntries, cdone)\n\tgo parallelRandMapResizer(t, m, numIters, numEntries, cdone)\n\t// Wait for the goroutines to finish.\n\t<-cdone\n\t<-cdone\n\t// Verify map contents.\n\tfor i := 0; i < numEntries; i++ {\n\t\tv, ok := m.Load(strconv.Itoa(i))\n\t\tif !ok {\n\t\t\t// The entry may be deleted and that's ok.\n\t\t\tcontinue\n\t\t}\n\t\tif v != i {\n\t\t\tt.Fatalf(\"values do not match for %d: %v\", i, v)\n\t\t}\n\t}\n\ts := m.Size()\n\tif s > numEntries {\n\t\tt.Fatalf(\"unexpected size: %v\", s)\n\t}\n\trs := sizeBasedOnTypedRange(m)\n\tif s != rs {\n\t\tt.Fatalf(\"size does not match number of entries in Range: %v, %v\", s, rs)\n\t}\n}\n\nfunc parallelRandMapClearer(t *testing.T, m *Map[string, int], numIters, numEntries int, cdone chan bool) {\n\tr := rand.New(rand.NewSource(time.Now().UnixNano()))\n\tfor i := 0; i < numIters; i++ {\n\t\tcoin := r.Int63n(2)\n\t\tfor j := 0; j < numEntries; j++ {\n\t\t\tif coin == 1 {\n\t\t\t\tm.Store(strconv.Itoa(j), j)\n\t\t\t} else {\n\t\t\t\tm.Clear()\n\t\t\t}\n\t\t}\n\t}\n\tcdone <- true\n}\n\nfunc TestMapParallelClear(t *testing.T) {\n\tconst numIters = 100\n\tconst numEntries = 1_000\n\tm := NewMap[string, int]()\n\tcdone := make(chan bool)\n\tgo parallelRandMapClearer(t, m, numIters, numEntries, cdone)\n\tgo parallelRandMapClearer(t, m, numIters, numEntries, cdone)\n\t// Wait for the goroutines to finish.\n\t<-cdone\n\t<-cdone\n\t// Verify map size.\n\ts := m.Size()\n\tif s > numEntries {\n\t\tt.Fatalf(\"unexpected size: %v\", s)\n\t}\n\trs := sizeBasedOnTypedRange(m)\n\tif s != rs {\n\t\tt.Fatalf(\"size does not match number of entries in Range: %v, %v\", s, rs)\n\t}\n}\n\nfunc parallelSeqMapStorer(t *testing.T, m *Map[string, int], storeEach, numIters, numEntries int, cdone chan bool) {\n\tfor i := 0; i < numIters; i++ {\n\t\tfor j := 0; j < numEntries; j++ {\n\t\t\tif storeEach == 0 || j%storeEach == 0 {\n\t\t\t\tm.Store(strconv.Itoa(j), j)\n\t\t\t\t// Due to atomic snapshots we must see a \"<j>\"/j pair.\n\t\t\t\tv, ok := m.Load(strconv.Itoa(j))\n\t\t\t\tif !ok {\n\t\t\t\t\tt.Errorf(\"value was not found for %d\", j)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tif v != j {\n\t\t\t\t\tt.Errorf(\"value was not expected for %d: %d\", j, v)\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tcdone <- true\n}\n\nfunc TestMapParallelStores(t *testing.T) {\n\tconst numStorers = 4\n\tconst numIters = 10_000\n\tconst numEntries = 100\n\tm := NewMap[string, int]()\n\tcdone := make(chan bool)\n\tfor i := 0; i < numStorers; i++ {\n\t\tgo parallelSeqMapStorer(t, m, i, numIters, numEntries, cdone)\n\t}\n\t// Wait for the goroutines to finish.\n\tfor i := 0; i < numStorers; i++ {\n\t\t<-cdone\n\t}\n\t// Verify map contents.\n\tfor i := 0; i < numEntries; i++ {\n\t\tv, ok := m.Load(strconv.Itoa(i))\n\t\tif !ok {\n\t\t\tt.Fatalf(\"value not found for %d\", i)\n\t\t}\n\t\tif v != i {\n\t\t\tt.Fatalf(\"values do not match for %d: %v\", i, v)\n\t\t}\n\t}\n}\n\nfunc parallelRandMapStorer(t *testing.T, m *Map[string, int], numIters, numEntries int, cdone chan bool) {\n\tr := rand.New(rand.NewSource(time.Now().UnixNano()))\n\tfor i := 0; i < numIters; i++ {\n\t\tj := r.Intn(numEntries)\n\t\tif v, loaded := m.LoadOrStore(strconv.Itoa(j), j); loaded {\n\t\t\tif v != j {\n\t\t\t\tt.Errorf(\"value was not expected for %d: %d\", j, v)\n\t\t\t}\n\t\t}\n\t}\n\tcdone <- true\n}\n\nfunc parallelRandMapDeleter(t *testing.T, m *Map[string, int], numIters, numEntries int, cdone chan bool) {\n\tr := rand.New(rand.NewSource(time.Now().UnixNano()))\n\tfor i := 0; i < numIters; i++ {\n\t\tj := r.Intn(numEntries)\n\t\tif v, loaded := m.LoadAndDelete(strconv.Itoa(j)); loaded {\n\t\t\tif v != j {\n\t\t\t\tt.Errorf(\"value was not expected for %d: %d\", j, v)\n\t\t\t}\n\t\t}\n\t}\n\tcdone <- true\n}\n\nfunc parallelMapLoader(t *testing.T, m *Map[string, int], numIters, numEntries int, cdone chan bool) {\n\tfor i := 0; i < numIters; i++ {\n\t\tfor j := 0; j < numEntries; j++ {\n\t\t\t// Due to atomic snapshots we must either see no entry, or a \"<j>\"/j pair.\n\t\t\tif v, ok := m.Load(strconv.Itoa(j)); ok {\n\t\t\t\tif v != j {\n\t\t\t\t\tt.Errorf(\"value was not expected for %d: %d\", j, v)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tcdone <- true\n}\n\nfunc TestMapAtomicSnapshot(t *testing.T) {\n\tconst numIters = 100_000\n\tconst numEntries = 100\n\tm := NewMap[string, int]()\n\tcdone := make(chan bool)\n\t// Update or delete random entry in parallel with loads.\n\tgo parallelRandMapStorer(t, m, numIters, numEntries, cdone)\n\tgo parallelRandMapDeleter(t, m, numIters, numEntries, cdone)\n\tgo parallelMapLoader(t, m, numIters, numEntries, cdone)\n\t// Wait for the goroutines to finish.\n\tfor i := 0; i < 3; i++ {\n\t\t<-cdone\n\t}\n}\n\nfunc TestMapParallelStoresAndDeletes(t *testing.T) {\n\tconst numWorkers = 2\n\tconst numIters = 100_000\n\tconst numEntries = 1000\n\tm := NewMap[string, int]()\n\tcdone := make(chan bool)\n\t// Update random entry in parallel with deletes.\n\tfor i := 0; i < numWorkers; i++ {\n\t\tgo parallelRandMapStorer(t, m, numIters, numEntries, cdone)\n\t\tgo parallelRandMapDeleter(t, m, numIters, numEntries, cdone)\n\t}\n\t// Wait for the goroutines to finish.\n\tfor i := 0; i < 2*numWorkers; i++ {\n\t\t<-cdone\n\t}\n}\n\nfunc parallelMapComputer(m *Map[uint64, uint64], numIters, numEntries int, cdone chan bool) {\n\tfor i := 0; i < numIters; i++ {\n\t\tfor j := 0; j < numEntries; j++ {\n\t\t\tm.Compute(uint64(j), func(oldValue uint64, loaded bool) (newValue uint64, op ComputeOp) {\n\t\t\t\treturn oldValue + 1, UpdateOp\n\t\t\t})\n\t\t}\n\t}\n\tcdone <- true\n}\n\nfunc TestMapParallelComputes(t *testing.T) {\n\tconst numWorkers = 4 // Also stands for numEntries.\n\tconst numIters = 10_000\n\tm := NewMap[uint64, uint64]()\n\tcdone := make(chan bool)\n\tfor i := 0; i < numWorkers; i++ {\n\t\tgo parallelMapComputer(m, numIters, numWorkers, cdone)\n\t}\n\t// Wait for the goroutines to finish.\n\tfor i := 0; i < numWorkers; i++ {\n\t\t<-cdone\n\t}\n\t// Verify map contents.\n\tfor i := 0; i < numWorkers; i++ {\n\t\tv, ok := m.Load(uint64(i))\n\t\tif !ok {\n\t\t\tt.Fatalf(\"value not found for %d\", i)\n\t\t}\n\t\tif v != numWorkers*numIters {\n\t\t\tt.Fatalf(\"values do not match for %d: %v\", i, v)\n\t\t}\n\t}\n}\n\nfunc parallelRangeMapStorer(m *Map[int, int], numEntries int, stopFlag *int64, cdone chan bool) {\n\tfor {\n\t\tfor i := 0; i < numEntries; i++ {\n\t\t\tm.Store(i, i)\n\t\t}\n\t\tif atomic.LoadInt64(stopFlag) != 0 {\n\t\t\tbreak\n\t\t}\n\t}\n\tcdone <- true\n}\n\nfunc parallelRangeMapDeleter(m *Map[int, int], numEntries int, stopFlag *int64, cdone chan bool) {\n\tfor {\n\t\tfor i := 0; i < numEntries; i++ {\n\t\t\tm.Delete(i)\n\t\t}\n\t\tif atomic.LoadInt64(stopFlag) != 0 {\n\t\t\tbreak\n\t\t}\n\t}\n\tcdone <- true\n}\n\nfunc TestMapParallelRange(t *testing.T) {\n\tconst numEntries = 10_000\n\tm := NewMap[int, int](WithPresize(numEntries))\n\tfor i := 0; i < numEntries; i++ {\n\t\tm.Store(i, i)\n\t}\n\t// Start goroutines that would be storing and deleting items in parallel.\n\tcdone := make(chan bool)\n\tstopFlag := int64(0)\n\tgo parallelRangeMapStorer(m, numEntries, &stopFlag, cdone)\n\tgo parallelRangeMapDeleter(m, numEntries, &stopFlag, cdone)\n\t// Iterate the map and verify that no duplicate keys were met.\n\tmet := make(map[int]int)\n\tm.Range(func(key int, value int) bool {\n\t\tif key != value {\n\t\t\tt.Fatalf(\"got unexpected value for key %d: %d\", key, value)\n\t\t\treturn false\n\t\t}\n\t\tmet[key] += 1\n\t\treturn true\n\t})\n\tif len(met) == 0 {\n\t\tt.Fatal(\"no entries were met when iterating\")\n\t}\n\tfor k, c := range met {\n\t\tif c != 1 {\n\t\t\tt.Fatalf(\"met key %d multiple times: %d\", k, c)\n\t\t}\n\t}\n\t// Make sure that both goroutines finish.\n\tatomic.StoreInt64(&stopFlag, 1)\n\t<-cdone\n\t<-cdone\n}\n\nfunc parallelMapShrinker(t *testing.T, m *Map[uint64, *point], numIters, numEntries int, stopFlag *int64, cdone chan bool) {\n\tfor i := 0; i < numIters; i++ {\n\t\tfor j := 0; j < numEntries; j++ {\n\t\t\tif p, loaded := m.LoadOrStore(uint64(j), &point{int32(j), int32(j)}); loaded {\n\t\t\t\tt.Errorf(\"value was present for %d: %v\", j, p)\n\t\t\t}\n\t\t}\n\t\tfor j := 0; j < numEntries; j++ {\n\t\t\tm.Delete(uint64(j))\n\t\t}\n\t}\n\tatomic.StoreInt64(stopFlag, 1)\n\tcdone <- true\n}\n\nfunc parallelMapUpdater(t *testing.T, m *Map[uint64, *point], idx int, stopFlag *int64, cdone chan bool) {\n\tfor atomic.LoadInt64(stopFlag) != 1 {\n\t\tsleepUs := int(randv2.Uint64() % 10)\n\t\tif p, loaded := m.LoadOrStore(uint64(idx), &point{int32(idx), int32(idx)}); loaded {\n\t\t\tt.Errorf(\"value was present for %d: %v\", idx, p)\n\t\t}\n\t\ttime.Sleep(time.Duration(sleepUs) * time.Microsecond)\n\t\tif _, ok := m.Load(uint64(idx)); !ok {\n\t\t\tt.Errorf(\"value was not found for %d\", idx)\n\t\t}\n\t\tm.Delete(uint64(idx))\n\t}\n\tcdone <- true\n}\n\nfunc TestMapDoesNotLoseEntriesOnResize(t *testing.T) {\n\tconst numIters = 10_000\n\tconst numEntries = 128\n\tm := NewMap[uint64, *point]()\n\tcdone := make(chan bool)\n\tstopFlag := int64(0)\n\tgo parallelMapShrinker(t, m, numIters, numEntries, &stopFlag, cdone)\n\tgo parallelMapUpdater(t, m, numEntries, &stopFlag, cdone)\n\t// Wait for the goroutines to finish.\n\t<-cdone\n\t<-cdone\n\t// Verify map contents.\n\tif s := m.Size(); s != 0 {\n\t\tt.Fatalf(\"map is not empty: %d\", s)\n\t}\n}\n\nfunc TestMapStats(t *testing.T) {\n\tm := NewMap[int, int]()\n\n\tstats := m.Stats()\n\tif stats.RootBuckets != defaultMinMapTableLen {\n\t\tt.Fatalf(\"unexpected number of root buckets: %d\", stats.RootBuckets)\n\t}\n\tif stats.TotalBuckets != stats.RootBuckets {\n\t\tt.Fatalf(\"unexpected number of total buckets: %d\", stats.TotalBuckets)\n\t}\n\tif stats.EmptyBuckets != stats.RootBuckets {\n\t\tt.Fatalf(\"unexpected number of empty buckets: %d\", stats.EmptyBuckets)\n\t}\n\tif stats.Capacity != entriesPerMapBucket*defaultMinMapTableLen {\n\t\tt.Fatalf(\"unexpected capacity: %d\", stats.Capacity)\n\t}\n\tif stats.Size != 0 {\n\t\tt.Fatalf(\"unexpected size: %d\", stats.Size)\n\t}\n\tif stats.Counter != 0 {\n\t\tt.Fatalf(\"unexpected counter: %d\", stats.Counter)\n\t}\n\tif stats.CounterLen != 8 {\n\t\tt.Fatalf(\"unexpected counter length: %d\", stats.CounterLen)\n\t}\n\n\tfor i := 0; i < 200; i++ {\n\t\tm.Store(i, i)\n\t}\n\n\tstats = m.Stats()\n\tif stats.RootBuckets != 2*defaultMinMapTableLen {\n\t\tt.Fatalf(\"unexpected number of root buckets: %d\", stats.RootBuckets)\n\t}\n\tif stats.TotalBuckets < stats.RootBuckets {\n\t\tt.Fatalf(\"unexpected number of total buckets: %d\", stats.TotalBuckets)\n\t}\n\tif stats.EmptyBuckets >= stats.RootBuckets {\n\t\tt.Fatalf(\"unexpected number of empty buckets: %d\", stats.EmptyBuckets)\n\t}\n\tif stats.Capacity < 2*entriesPerMapBucket*defaultMinMapTableLen {\n\t\tt.Fatalf(\"unexpected capacity: %d\", stats.Capacity)\n\t}\n\tif stats.Size != 200 {\n\t\tt.Fatalf(\"unexpected size: %d\", stats.Size)\n\t}\n\tif stats.Counter != 200 {\n\t\tt.Fatalf(\"unexpected counter: %d\", stats.Counter)\n\t}\n\tif stats.CounterLen != 8 {\n\t\tt.Fatalf(\"unexpected counter length: %d\", stats.CounterLen)\n\t}\n}\n\nfunc TestToPlainMap_NilPointer(t *testing.T) {\n\tpm := ToPlainMap[int, int](nil)\n\tif len(pm) != 0 {\n\t\tt.Fatalf(\"got unexpected size of nil map copy: %d\", len(pm))\n\t}\n}\n\nfunc TestToPlainMap(t *testing.T) {\n\tconst numEntries = 1000\n\tm := NewMap[int, int]()\n\tfor i := 0; i < numEntries; i++ {\n\t\tm.Store(i, i)\n\t}\n\tpm := ToPlainMap[int, int](m)\n\tif len(pm) != numEntries {\n\t\tt.Fatalf(\"got unexpected size of nil map copy: %d\", len(pm))\n\t}\n\tfor i := 0; i < numEntries; i++ {\n\t\tif v := pm[i]; v != i {\n\t\t\tt.Fatalf(\"unexpected value for key %d: %d\", i, v)\n\t\t}\n\t}\n}\n\nfunc BenchmarkMap_NoWarmUp(b *testing.B) {\n\tfor _, bc := range benchmarkCases {\n\t\tif bc.readPercentage == 100 {\n\t\t\t// This benchmark doesn't make sense without a warm-up.\n\t\t\tcontinue\n\t\t}\n\t\tb.Run(bc.name, func(b *testing.B) {\n\t\t\tm := NewMap[string, int]()\n\t\t\tbenchmarkMapStringKeys(b, func(k string) (int, bool) {\n\t\t\t\treturn m.Load(k)\n\t\t\t}, func(k string, v int) {\n\t\t\t\tm.Store(k, v)\n\t\t\t}, func(k string) {\n\t\t\t\tm.Delete(k)\n\t\t\t}, bc.readPercentage)\n\t\t})\n\t}\n}\n\nfunc BenchmarkMap_WarmUp(b *testing.B) {\n\tfor _, bc := range benchmarkCases {\n\t\tb.Run(bc.name, func(b *testing.B) {\n\t\t\tm := NewMap[string, int](WithPresize(benchmarkNumEntries))\n\t\t\tfor i := 0; i < benchmarkNumEntries; i++ {\n\t\t\t\tm.Store(benchmarkKeyPrefix+strconv.Itoa(i), i)\n\t\t\t}\n\t\t\tb.ResetTimer()\n\t\t\tbenchmarkMapStringKeys(b, func(k string) (int, bool) {\n\t\t\t\treturn m.Load(k)\n\t\t\t}, func(k string, v int) {\n\t\t\t\tm.Store(k, v)\n\t\t\t}, func(k string) {\n\t\t\t\tm.Delete(k)\n\t\t\t}, bc.readPercentage)\n\t\t})\n\t}\n}\n\nfunc benchmarkMapStringKeys(\n\tb *testing.B,\n\tloadFn func(k string) (int, bool),\n\tstoreFn func(k string, v int),\n\tdeleteFn func(k string),\n\treadPercentage int,\n) {\n\trunParallel(b, func(pb *testing.PB) {\n\t\t// convert percent to permille to support 99% case\n\t\tstoreThreshold := 10 * readPercentage\n\t\tdeleteThreshold := 10*readPercentage + ((1000 - 10*readPercentage) / 2)\n\t\tfor pb.Next() {\n\t\t\top := int(randv2.Uint64() % 1000)\n\t\t\ti := int(randv2.Uint64() % benchmarkNumEntries)\n\t\t\tif op >= deleteThreshold {\n\t\t\t\tdeleteFn(benchmarkKeys[i])\n\t\t\t} else if op >= storeThreshold {\n\t\t\t\tstoreFn(benchmarkKeys[i], i)\n\t\t\t} else {\n\t\t\t\tloadFn(benchmarkKeys[i])\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkMapInt_NoWarmUp(b *testing.B) {\n\tfor _, bc := range benchmarkCases {\n\t\tif bc.readPercentage == 100 {\n\t\t\t// This benchmark doesn't make sense without a warm-up.\n\t\t\tcontinue\n\t\t}\n\t\tb.Run(bc.name, func(b *testing.B) {\n\t\t\tm := NewMap[int, int]()\n\t\t\tbenchmarkMapIntKeys(b, func(k int) (int, bool) {\n\t\t\t\treturn m.Load(k)\n\t\t\t}, func(k int, v int) {\n\t\t\t\tm.Store(k, v)\n\t\t\t}, func(k int) {\n\t\t\t\tm.Delete(k)\n\t\t\t}, bc.readPercentage)\n\t\t})\n\t}\n}\n\nfunc BenchmarkMapInt_WarmUp(b *testing.B) {\n\tfor _, bc := range benchmarkCases {\n\t\tb.Run(bc.name, func(b *testing.B) {\n\t\t\tm := NewMap[int, int](WithPresize(benchmarkNumEntries))\n\t\t\tfor i := 0; i < benchmarkNumEntries; i++ {\n\t\t\t\tm.Store(i, i)\n\t\t\t}\n\t\t\tb.ResetTimer()\n\t\t\tbenchmarkMapIntKeys(b, func(k int) (int, bool) {\n\t\t\t\treturn m.Load(k)\n\t\t\t}, func(k int, v int) {\n\t\t\t\tm.Store(k, v)\n\t\t\t}, func(k int) {\n\t\t\t\tm.Delete(k)\n\t\t\t}, bc.readPercentage)\n\t\t})\n\t}\n}\n\nfunc BenchmarkIntMapStandard_NoWarmUp(b *testing.B) {\n\tfor _, bc := range benchmarkCases {\n\t\tif bc.readPercentage == 100 {\n\t\t\t// This benchmark doesn't make sense without a warm-up.\n\t\t\tcontinue\n\t\t}\n\t\tb.Run(bc.name, func(b *testing.B) {\n\t\t\tvar m sync.Map\n\t\t\tbenchmarkMapIntKeys(b, func(k int) (value int, ok bool) {\n\t\t\t\tv, ok := m.Load(k)\n\t\t\t\tif ok {\n\t\t\t\t\treturn v.(int), ok\n\t\t\t\t} else {\n\t\t\t\t\treturn 0, false\n\t\t\t\t}\n\t\t\t}, func(k int, v int) {\n\t\t\t\tm.Store(k, v)\n\t\t\t}, func(k int) {\n\t\t\t\tm.Delete(k)\n\t\t\t}, bc.readPercentage)\n\t\t})\n\t}\n}\n\n// This is a nice scenario for sync.Map since a lot of updates\n// will hit the readOnly part of the map.\nfunc BenchmarkIntMapStandard_WarmUp(b *testing.B) {\n\tfor _, bc := range benchmarkCases {\n\t\tb.Run(bc.name, func(b *testing.B) {\n\t\t\tvar m sync.Map\n\t\t\tfor i := 0; i < benchmarkNumEntries; i++ {\n\t\t\t\tm.Store(i, i)\n\t\t\t}\n\t\t\tb.ResetTimer()\n\t\t\tbenchmarkMapIntKeys(b, func(k int) (value int, ok bool) {\n\t\t\t\tv, ok := m.Load(k)\n\t\t\t\tif ok {\n\t\t\t\t\treturn v.(int), ok\n\t\t\t\t} else {\n\t\t\t\t\treturn 0, false\n\t\t\t\t}\n\t\t\t}, func(k int, v int) {\n\t\t\t\tm.Store(k, v)\n\t\t\t}, func(k int) {\n\t\t\t\tm.Delete(k)\n\t\t\t}, bc.readPercentage)\n\t\t})\n\t}\n}\n\nfunc benchmarkMapIntKeys(\n\tb *testing.B,\n\tloadFn func(k int) (int, bool),\n\tstoreFn func(k int, v int),\n\tdeleteFn func(k int),\n\treadPercentage int,\n) {\n\trunParallel(b, func(pb *testing.PB) {\n\t\t// convert percent to permille to support 99% case\n\t\tstoreThreshold := 10 * readPercentage\n\t\tdeleteThreshold := 10*readPercentage + ((1000 - 10*readPercentage) / 2)\n\t\tfor pb.Next() {\n\t\t\top := int(randv2.Uint64() % 1000)\n\t\t\ti := int(randv2.Uint64() % benchmarkNumEntries)\n\t\t\tif op >= deleteThreshold {\n\t\t\t\tdeleteFn(i)\n\t\t\t} else if op >= storeThreshold {\n\t\t\t\tstoreFn(i, i)\n\t\t\t} else {\n\t\t\t\tloadFn(i)\n\t\t\t}\n\t\t}\n\t})\n}\n\nfunc BenchmarkMapRange(b *testing.B) {\n\tm := NewMap[string, int](WithPresize(benchmarkNumEntries))\n\tfor i := 0; i < benchmarkNumEntries; i++ {\n\t\tm.Store(benchmarkKeys[i], i)\n\t}\n\tb.ResetTimer()\n\trunParallel(b, func(pb *testing.PB) {\n\t\tfoo := 0\n\t\tfor pb.Next() {\n\t\t\tm.Range(func(key string, value int) bool {\n\t\t\t\tfoo++\n\t\t\t\treturn true\n\t\t\t})\n\t\t\t_ = foo\n\t\t}\n\t})\n}\n\n// Benchmarks noop performance of Compute\nfunc BenchmarkMapCompute(b *testing.B) {\n\ttests := []struct {\n\t\tName string\n\t\tOp   ComputeOp\n\t}{\n\t\t{\n\t\t\tName: \"UpdateOp\",\n\t\t\tOp:   UpdateOp,\n\t\t},\n\t\t{\n\t\t\tName: \"CancelOp\",\n\t\t\tOp:   CancelOp,\n\t\t},\n\t}\n\tfor _, test := range tests {\n\t\tb.Run(\"op=\"+test.Name, func(b *testing.B) {\n\t\t\tm := NewMap[struct{}, bool]()\n\t\t\tm.Store(struct{}{}, true)\n\t\t\tb.ResetTimer()\n\t\t\tfor i := 0; i < b.N; i++ {\n\t\t\t\tm.Compute(struct{}{}, func(oldValue bool, loaded bool) (newValue bool, op ComputeOp) {\n\t\t\t\t\treturn oldValue, test.Op\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc BenchmarkMapParallelRehashing(b *testing.B) {\n\ttests := []struct {\n\t\tname       string\n\t\tgoroutines int\n\t\tnumEntries int\n\t}{\n\t\t{\"1goroutine_10M\", 1, 10_000_000},\n\t\t{\"4goroutines_10M\", 4, 10_000_000},\n\t\t{\"8goroutines_10M\", 8, 10_000_000},\n\t}\n\tfor _, test := range tests {\n\t\tb.Run(test.name, func(b *testing.B) {\n\t\t\tfor i := 0; i < b.N; i++ {\n\t\t\t\tm := NewMap[int, int]()\n\n\t\t\t\tvar wg sync.WaitGroup\n\t\t\t\tentriesPerGoroutine := test.numEntries / test.goroutines\n\n\t\t\t\tstart := time.Now()\n\n\t\t\t\tfor g := 0; g < test.goroutines; g++ {\n\t\t\t\t\twg.Add(1)\n\t\t\t\t\tgo func(goroutineID int) {\n\t\t\t\t\t\tdefer wg.Done()\n\t\t\t\t\t\tbase := goroutineID * entriesPerGoroutine\n\t\t\t\t\t\tfor j := 0; j < entriesPerGoroutine; j++ {\n\t\t\t\t\t\t\tkey := base + j\n\t\t\t\t\t\t\tm.Store(key, key)\n\t\t\t\t\t\t}\n\t\t\t\t\t}(g)\n\t\t\t\t}\n\n\t\t\t\twg.Wait()\n\t\t\t\tduration := time.Since(start)\n\n\t\t\t\tb.ReportMetric(float64(test.numEntries)/duration.Seconds(), \"entries/s\")\n\n\t\t\t\tfinalSize := m.Size()\n\t\t\t\tif finalSize != test.numEntries {\n\t\t\t\t\tb.Fatalf(\"Expected size %d, got %d\", test.numEntries, finalSize)\n\t\t\t\t}\n\n\t\t\t\tstats := m.Stats()\n\t\t\t\tif stats.TotalGrowths == 0 {\n\t\t\t\t\tb.Error(\"Expected at least one table growth during rehashing\")\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNextPowOf2(t *testing.T) {\n\tif nextPowOf2(0) != 1 {\n\t\tt.Error(\"nextPowOf2 failed\")\n\t}\n\tif nextPowOf2(1) != 1 {\n\t\tt.Error(\"nextPowOf2 failed\")\n\t}\n\tif nextPowOf2(2) != 2 {\n\t\tt.Error(\"nextPowOf2 failed\")\n\t}\n\tif nextPowOf2(3) != 4 {\n\t\tt.Error(\"nextPowOf2 failed\")\n\t}\n}\n\nfunc TestBroadcast(t *testing.T) {\n\ttestCases := []struct {\n\t\tinput    uint8\n\t\texpected uint64\n\t}{\n\t\t{\n\t\t\tinput:    0,\n\t\t\texpected: 0,\n\t\t},\n\t\t{\n\t\t\tinput:    1,\n\t\t\texpected: 0x0101010101010101,\n\t\t},\n\t\t{\n\t\t\tinput:    2,\n\t\t\texpected: 0x0202020202020202,\n\t\t},\n\t\t{\n\t\t\tinput:    42,\n\t\t\texpected: 0x2a2a2a2a2a2a2a2a,\n\t\t},\n\t\t{\n\t\t\tinput:    127,\n\t\t\texpected: 0x7f7f7f7f7f7f7f7f,\n\t\t},\n\t\t{\n\t\t\tinput:    255,\n\t\t\texpected: 0xffffffffffffffff,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(strconv.Itoa(int(tc.input)), func(t *testing.T) {\n\t\t\tif broadcast(tc.input) != tc.expected {\n\t\t\t\tt.Errorf(\"unexpected result: %x\", broadcast(tc.input))\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestFirstMarkedByteIndex(t *testing.T) {\n\ttestCases := []struct {\n\t\tinput    uint64\n\t\texpected int\n\t}{\n\t\t{\n\t\t\tinput:    0,\n\t\t\texpected: 8,\n\t\t},\n\t\t{\n\t\t\tinput:    0x8080808080808080,\n\t\t\texpected: 0,\n\t\t},\n\t\t{\n\t\t\tinput:    0x0000000000000080,\n\t\t\texpected: 0,\n\t\t},\n\t\t{\n\t\t\tinput:    0x0000000000008000,\n\t\t\texpected: 1,\n\t\t},\n\t\t{\n\t\t\tinput:    0x0000000000800000,\n\t\t\texpected: 2,\n\t\t},\n\t\t{\n\t\t\tinput:    0x0000000080000000,\n\t\t\texpected: 3,\n\t\t},\n\t\t{\n\t\t\tinput:    0x0000008000000000,\n\t\t\texpected: 4,\n\t\t},\n\t\t{\n\t\t\tinput:    0x0000800000000000,\n\t\t\texpected: 5,\n\t\t},\n\t\t{\n\t\t\tinput:    0x0080000000000000,\n\t\t\texpected: 6,\n\t\t},\n\t\t{\n\t\t\tinput:    0x8000000000000000,\n\t\t\texpected: 7,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(strconv.Itoa(int(tc.input)), func(t *testing.T) {\n\t\t\tif firstMarkedByteIndex(tc.input) != tc.expected {\n\t\t\t\tt.Errorf(\"unexpected result: %x\", firstMarkedByteIndex(tc.input))\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMarkZeroBytes(t *testing.T) {\n\ttestCases := []struct {\n\t\tinput    uint64\n\t\texpected uint64\n\t}{\n\t\t{\n\t\t\tinput:    0xffffffffffffffff,\n\t\t\texpected: 0,\n\t\t},\n\t\t{\n\t\t\tinput:    0,\n\t\t\texpected: 0x8080808080808080,\n\t\t},\n\t\t{\n\t\t\tinput:    1,\n\t\t\texpected: 0x8080808080808000,\n\t\t},\n\t\t{\n\t\t\tinput:    1 << 9,\n\t\t\texpected: 0x8080808080800080,\n\t\t},\n\t\t{\n\t\t\tinput:    1 << 17,\n\t\t\texpected: 0x8080808080008080,\n\t\t},\n\t\t{\n\t\t\tinput:    1 << 25,\n\t\t\texpected: 0x8080808000808080,\n\t\t},\n\t\t{\n\t\t\tinput:    1 << 33,\n\t\t\texpected: 0x8080800080808080,\n\t\t},\n\t\t{\n\t\t\tinput:    1 << 41,\n\t\t\texpected: 0x8080008080808080,\n\t\t},\n\t\t{\n\t\t\tinput:    1 << 49,\n\t\t\texpected: 0x8000808080808080,\n\t\t},\n\t\t{\n\t\t\tinput:    1 << 57,\n\t\t\texpected: 0x0080808080808080,\n\t\t},\n\t\t// false positive\n\t\t{\n\t\t\tinput:    0x0100,\n\t\t\texpected: 0x8080808080808080,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(strconv.Itoa(int(tc.input)), func(t *testing.T) {\n\t\t\tif markZeroBytes(tc.input) != tc.expected {\n\t\t\t\tt.Errorf(\"unexpected result: %x\", markZeroBytes(tc.input))\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestSetByte(t *testing.T) {\n\ttestCases := []struct {\n\t\tword     uint64\n\t\tb        uint8\n\t\tidx      int\n\t\texpected uint64\n\t}{\n\t\t{\n\t\t\tword:     0xffffffffffffffff,\n\t\t\tb:        0,\n\t\t\tidx:      0,\n\t\t\texpected: 0xffffffffffffff00,\n\t\t},\n\t\t{\n\t\t\tword:     0xffffffffffffffff,\n\t\t\tb:        1,\n\t\t\tidx:      1,\n\t\t\texpected: 0xffffffffffff01ff,\n\t\t},\n\t\t{\n\t\t\tword:     0xffffffffffffffff,\n\t\t\tb:        2,\n\t\t\tidx:      2,\n\t\t\texpected: 0xffffffffff02ffff,\n\t\t},\n\t\t{\n\t\t\tword:     0xffffffffffffffff,\n\t\t\tb:        3,\n\t\t\tidx:      3,\n\t\t\texpected: 0xffffffff03ffffff,\n\t\t},\n\t\t{\n\t\t\tword:     0xffffffffffffffff,\n\t\t\tb:        4,\n\t\t\tidx:      4,\n\t\t\texpected: 0xffffff04ffffffff,\n\t\t},\n\t\t{\n\t\t\tword:     0xffffffffffffffff,\n\t\t\tb:        5,\n\t\t\tidx:      5,\n\t\t\texpected: 0xffff05ffffffffff,\n\t\t},\n\t\t{\n\t\t\tword:     0xffffffffffffffff,\n\t\t\tb:        6,\n\t\t\tidx:      6,\n\t\t\texpected: 0xff06ffffffffffff,\n\t\t},\n\t\t{\n\t\t\tword:     0xffffffffffffffff,\n\t\t\tb:        7,\n\t\t\tidx:      7,\n\t\t\texpected: 0x07ffffffffffffff,\n\t\t},\n\t\t{\n\t\t\tword:     0,\n\t\t\tb:        0xff,\n\t\t\tidx:      7,\n\t\t\texpected: 0xff00000000000000,\n\t\t},\n\t}\n\n\tfor _, tc := range testCases {\n\t\tt.Run(strconv.Itoa(int(tc.word)), func(t *testing.T) {\n\t\t\tif setByte(tc.word, tc.b, tc.idx) != tc.expected {\n\t\t\t\tt.Errorf(\"unexpected result: %x\", setByte(tc.word, tc.b, tc.idx))\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/common/yaml/yaml.go",
    "content": "// Package yaml provides a common entrance for YAML marshaling and unmarshalling.\npackage yaml\n\nimport (\n\t\"gopkg.in/yaml.v3\"\n)\n\nfunc Unmarshal(in []byte, out any) (err error) {\n\treturn yaml.Unmarshal(in, out)\n}\n\nfunc Marshal(in any) (out []byte, err error) {\n\treturn yaml.Marshal(in)\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/auth/auth.go",
    "content": "package auth\n\ntype Authenticator interface {\n\tVerify(user string, pass string) bool\n\tUsers() []string\n}\n\ntype AuthStore interface {\n\tAuthenticator() Authenticator\n\tSetAuthenticator(Authenticator)\n}\n\ntype AuthUser struct {\n\tUser string\n\tPass string\n}\n\ntype inMemoryAuthenticator struct {\n\tstorage   map[string]string\n\tusernames []string\n}\n\nfunc (au *inMemoryAuthenticator) Verify(user string, pass string) bool {\n\trealPass, ok := au.storage[user]\n\treturn ok && realPass == pass\n}\n\nfunc (au *inMemoryAuthenticator) Users() []string { return au.usernames }\n\nfunc NewAuthenticator(users []AuthUser) Authenticator {\n\tif len(users) == 0 {\n\t\treturn nil\n\t}\n\tau := &inMemoryAuthenticator{\n\t\tstorage:   make(map[string]string),\n\t\tusernames: make([]string, 0, len(users)),\n\t}\n\tfor _, user := range users {\n\t\tau.storage[user.User] = user.Pass\n\t\tau.usernames = append(au.usernames, user.User)\n\t}\n\treturn au\n}\n\nvar AlwaysValid Authenticator = alwaysValid{}\n\ntype alwaysValid struct{}\n\nfunc (alwaysValid) Verify(string, string) bool { return true }\n\nfunc (alwaysValid) Users() []string { return nil }\n"
  },
  {
    "path": "core/Clash.Meta/component/ca/auth.go",
    "content": "package ca\n\nimport (\n\t\"github.com/metacubex/tls\"\n)\n\ntype ClientAuthType = tls.ClientAuthType\n\nconst (\n\tNoClientCert               = tls.NoClientCert\n\tRequestClientCert          = tls.RequestClientCert\n\tRequireAnyClientCert       = tls.RequireAnyClientCert\n\tVerifyClientCertIfGiven    = tls.VerifyClientCertIfGiven\n\tRequireAndVerifyClientCert = tls.RequireAndVerifyClientCert\n)\n\nfunc ClientAuthTypeFromString(s string) ClientAuthType {\n\tswitch s {\n\tcase \"request\":\n\t\treturn RequestClientCert\n\tcase \"require-any\":\n\t\treturn RequireAnyClientCert\n\tcase \"verify-if-given\":\n\t\treturn VerifyClientCertIfGiven\n\tcase \"require-and-verify\":\n\t\treturn RequireAndVerifyClientCert\n\tdefault:\n\t\treturn NoClientCert\n\t}\n}\n\nfunc ClientAuthTypeToString(t ClientAuthType) string {\n\tswitch t {\n\tcase RequestClientCert:\n\t\treturn \"request\"\n\tcase RequireAnyClientCert:\n\t\treturn \"require-any\"\n\tcase VerifyClientCertIfGiven:\n\t\treturn \"verify-if-given\"\n\tcase RequireAndVerifyClientCert:\n\t\treturn \"require-and-verify\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/ca/ca-certificates.crt",
    "content": ""
  },
  {
    "path": "core/Clash.Meta/component/ca/config.go",
    "content": "package ca\n\nimport (\n\t\"crypto/x509\"\n\t_ \"embed\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"strconv\"\n\t\"sync\"\n\n\t\"github.com/metacubex/mihomo/common/once\"\n\t\"github.com/metacubex/mihomo/ntp\"\n\n\t\"github.com/metacubex/tls\"\n)\n\nvar globalCertPool *x509.CertPool\nvar mutex sync.RWMutex\nvar errNotMatch = errors.New(\"certificate fingerprints do not match\")\n\n//go:embed ca-certificates.crt\nvar _CaCertificates []byte\nvar DisableEmbedCa, _ = strconv.ParseBool(os.Getenv(\"DISABLE_EMBED_CA\"))\nvar DisableSystemCa, _ = strconv.ParseBool(os.Getenv(\"DISABLE_SYSTEM_CA\"))\n\nfunc AddCertificate(certificate string) error {\n\tmutex.Lock()\n\tdefer mutex.Unlock()\n\n\tif certificate == \"\" {\n\t\treturn fmt.Errorf(\"certificate is empty\")\n\t}\n\n\tif globalCertPool == nil {\n\t\tinitializeCertPool()\n\t}\n\n\tif globalCertPool.AppendCertsFromPEM([]byte(certificate)) {\n\t\treturn nil\n\t} else if cert, err := x509.ParseCertificate([]byte(certificate)); err == nil {\n\t\tglobalCertPool.AddCert(cert)\n\t\treturn nil\n\t} else {\n\t\treturn fmt.Errorf(\"add certificate failed\")\n\t}\n}\n\nfunc initializeCertPool() {\n\tvar err error\n\tif DisableSystemCa {\n\t\tglobalCertPool = x509.NewCertPool()\n\t} else {\n\t\tglobalCertPool, err = x509.SystemCertPool()\n\t\tif err != nil {\n\t\t\tglobalCertPool = x509.NewCertPool()\n\t\t}\n\t}\n\tif !DisableEmbedCa {\n\t\tglobalCertPool.AppendCertsFromPEM(_CaCertificates)\n\t}\n}\n\nfunc ResetCertificate() {\n\tmutex.Lock()\n\tdefer mutex.Unlock()\n\tinitializeCertPool()\n}\n\nfunc GetCertPool() *x509.CertPool {\n\tmutex.Lock()\n\tdefer mutex.Unlock()\n\tif globalCertPool == nil {\n\t\tinitializeCertPool()\n\t}\n\treturn globalCertPool\n}\n\ntype Option struct {\n\tTLSConfig   *tls.Config\n\tFingerprint string\n\tZeroTrust   bool\n\tCertificate string\n\tPrivateKey  string\n}\n\nfunc GetTLSConfig(opt Option) (tlsConfig *tls.Config, err error) {\n\ttlsConfig = opt.TLSConfig\n\tif tlsConfig == nil {\n\t\ttlsConfig = &tls.Config{}\n\t}\n\ttlsConfig.Time = ntp.Now\n\n\tif opt.ZeroTrust {\n\t\ttlsConfig.RootCAs = zeroTrustCertPool()\n\t} else {\n\t\ttlsConfig.RootCAs = GetCertPool()\n\t}\n\n\tif len(opt.Fingerprint) > 0 {\n\t\tverifier, err := NewFingerprintVerifier(opt.Fingerprint, tlsConfig.Time)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttlsConfig.VerifyConnection = func(state tls.ConnectionState) error {\n\t\t\t// [ConnectionState.ServerName] can return the actual ServerName needed for verification,\n\t\t\t// avoiding inconsistencies caused by [tlsConfig.ServerName] being modified after the [NewFingerprintVerifier] call.\n\t\t\t// https://github.com/golang/go/issues/36736#issuecomment-587925536\n\t\t\treturn verifier(state.PeerCertificates, state.ServerName)\n\t\t}\n\t\ttlsConfig.InsecureSkipVerify = true\n\t}\n\n\tif len(opt.Certificate) > 0 || len(opt.PrivateKey) > 0 {\n\t\tcertLoader, err := NewTLSKeyPairLoader(opt.Certificate, opt.PrivateKey)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttlsConfig.GetClientCertificate = func(*tls.CertificateRequestInfo) (*tls.Certificate, error) {\n\t\t\treturn certLoader()\n\t\t}\n\t}\n\treturn tlsConfig, nil\n}\n\nvar zeroTrustCertPool = once.OnceValue(func() *x509.CertPool {\n\tif len(_CaCertificates) != 0 { // always using embed cert first\n\t\tzeroTrustCertPool := x509.NewCertPool()\n\t\tif zeroTrustCertPool.AppendCertsFromPEM(_CaCertificates) {\n\t\t\treturn zeroTrustCertPool\n\t\t}\n\t}\n\treturn nil // fallback to system pool\n})\n"
  },
  {
    "path": "core/Clash.Meta/component/ca/fingerprint.go",
    "content": "package ca\n\nimport (\n\t\"bytes\"\n\t\"crypto/sha256\"\n\t\"crypto/x509\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"strings\"\n\t\"time\"\n)\n\n// NewFingerprintVerifier returns a function that verifies whether a certificate's SHA-256 fingerprint matches the given one.\nfunc NewFingerprintVerifier(fingerprint string, time func() time.Time) (func(certs []*x509.Certificate, serverName string) error, error) {\n\tswitch fingerprint {\n\tcase \"chrome\", \"firefox\", \"safari\", \"ios\", \"android\", \"edge\", \"360\", \"qq\", \"random\", \"randomized\": // WTF???\n\t\treturn nil, fmt.Errorf(\"`fingerprint` is used for TLS certificate pinning. If you need to specify the browser fingerprint, use `client-fingerprint`\")\n\t}\n\tfingerprint = strings.TrimSpace(strings.Replace(fingerprint, \":\", \"\", -1))\n\tfpByte, err := hex.DecodeString(fingerprint)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"fingerprint string decode error: %w\", err)\n\t}\n\n\tif len(fpByte) != 32 {\n\t\treturn nil, fmt.Errorf(\"fingerprint string length error,need sha256 fingerprint\")\n\t}\n\n\treturn func(certs []*x509.Certificate, serverName string) error {\n\t\t// ssl pining\n\t\tfor i, cert := range certs {\n\t\t\thash := sha256.Sum256(cert.Raw)\n\t\t\tif bytes.Equal(fpByte, hash[:]) {\n\t\t\t\tif i > 0 {\n\t\t\t\t\t// When the fingerprint matches a non-leaf certificate,\n\t\t\t\t\t// the certificate chain validity is verified using the certificate as the trusted root certificate.\n\t\t\t\t\topts := x509.VerifyOptions{\n\t\t\t\t\t\tRoots:         x509.NewCertPool(),\n\t\t\t\t\t\tIntermediates: x509.NewCertPool(),\n\t\t\t\t\t\tDNSName:       serverName,\n\t\t\t\t\t}\n\t\t\t\t\tif time != nil {\n\t\t\t\t\t\topts.CurrentTime = time()\n\t\t\t\t\t}\n\t\t\t\t\topts.Roots.AddCert(certs[i])\n\t\t\t\t\tfor _, cert := range certs[1 : i+1] { // stop at i\n\t\t\t\t\t\topts.Intermediates.AddCert(cert)\n\t\t\t\t\t}\n\t\t\t\t\t_, err := certs[0].Verify(opts)\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\treturn errNotMatch\n\t}, nil\n}\n\n// CalculateFingerprint computes the SHA-256 fingerprint of the given DER-encoded certificate and returns it as a hex string.\nfunc CalculateFingerprint(certDER []byte) string {\n\thash := sha256.Sum256(certDER)\n\treturn hex.EncodeToString(hash[:])\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/ca/fingerprint_test.go",
    "content": "package ca\n\nimport (\n\t\"crypto/x509\"\n\t\"encoding/pem\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestFingerprintVerifierLeaf(t *testing.T) {\n\tleafFingerprint := CalculateFingerprint(leafCert.Raw)\n\tverifier, err := NewFingerprintVerifier(leafFingerprint, certTime)\n\trequire.NoError(t, err)\n\n\terr = verifier([]*x509.Certificate{leafCert, intermediateCert, rootCert}, \"\")\n\tassert.NoError(t, err)\n\n\terr = verifier([]*x509.Certificate{leafCert, intermediateCert, rootCert}, leafServerName)\n\tassert.NoError(t, err)\n\n\terr = verifier([]*x509.Certificate{leafCert, intermediateCert, rootCert}, wrongLeafServerName)\n\tassert.NoError(t, err)\n\n\terr = verifier([]*x509.Certificate{leafCert, smimeIntermediateCert, rootCert}, \"\")\n\tassert.NoError(t, err)\n\n\terr = verifier([]*x509.Certificate{leafCert, smimeIntermediateCert, rootCert}, leafServerName)\n\tassert.NoError(t, err)\n\n\terr = verifier([]*x509.Certificate{leafCert, smimeIntermediateCert, rootCert}, wrongLeafServerName)\n\tassert.NoError(t, err)\n\n\terr = verifier([]*x509.Certificate{leafCert, intermediateCert, smimeRootCert}, \"\")\n\tassert.NoError(t, err)\n\n\terr = verifier([]*x509.Certificate{leafCert, intermediateCert, smimeRootCert}, leafServerName)\n\tassert.NoError(t, err)\n\n\terr = verifier([]*x509.Certificate{leafCert, intermediateCert, smimeRootCert}, wrongLeafServerName)\n\tassert.NoError(t, err)\n\n\terr = verifier([]*x509.Certificate{leafWithInvalidHashCert, intermediateCert, rootCert}, \"\")\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{leafWithInvalidHashCert, intermediateCert, rootCert}, leafServerName)\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{leafWithInvalidHashCert, intermediateCert, rootCert}, wrongLeafServerName)\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, rootCert}, \"\")\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, rootCert}, leafServerName)\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, rootCert}, wrongLeafServerName)\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, smimeRootCert}, \"\")\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, smimeRootCert}, leafServerName)\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, smimeRootCert}, wrongLeafServerName)\n\tassert.Error(t, err)\n}\n\nfunc TestFingerprintVerifierIntermediate(t *testing.T) {\n\tintermediateFingerprint := CalculateFingerprint(intermediateCert.Raw)\n\tverifier, err := NewFingerprintVerifier(intermediateFingerprint, certTime)\n\trequire.NoError(t, err)\n\n\terr = verifier([]*x509.Certificate{leafCert, intermediateCert, rootCert}, \"\")\n\tassert.NoError(t, err)\n\n\terr = verifier([]*x509.Certificate{leafCert, intermediateCert, rootCert}, leafServerName)\n\tassert.NoError(t, err)\n\n\terr = verifier([]*x509.Certificate{leafCert, intermediateCert, rootCert}, wrongLeafServerName)\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{leafCert, smimeIntermediateCert, rootCert}, \"\")\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{leafCert, smimeIntermediateCert, rootCert}, leafServerName)\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{leafCert, smimeIntermediateCert, rootCert}, wrongLeafServerName)\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{leafCert, intermediateCert, smimeRootCert}, \"\")\n\tassert.NoError(t, err)\n\n\terr = verifier([]*x509.Certificate{leafCert, intermediateCert, smimeRootCert}, leafServerName)\n\tassert.NoError(t, err)\n\n\terr = verifier([]*x509.Certificate{leafCert, intermediateCert, smimeRootCert}, wrongLeafServerName)\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{leafWithInvalidHashCert, intermediateCert, rootCert}, \"\")\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{leafWithInvalidHashCert, intermediateCert, rootCert}, leafServerName)\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{leafWithInvalidHashCert, intermediateCert, rootCert}, wrongLeafServerName)\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, rootCert}, \"\")\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, rootCert}, leafServerName)\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, rootCert}, wrongLeafServerName)\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, smimeRootCert}, \"\")\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, smimeRootCert}, leafServerName)\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, smimeRootCert}, wrongLeafServerName)\n\tassert.Error(t, err)\n}\n\nfunc TestFingerprintVerifierRoot(t *testing.T) {\n\trootFingerprint := CalculateFingerprint(rootCert.Raw)\n\tverifier, err := NewFingerprintVerifier(rootFingerprint, certTime)\n\trequire.NoError(t, err)\n\n\terr = verifier([]*x509.Certificate{leafCert, intermediateCert, rootCert}, \"\")\n\tassert.NoError(t, err)\n\n\terr = verifier([]*x509.Certificate{leafCert, intermediateCert, rootCert}, leafServerName)\n\tassert.NoError(t, err)\n\n\terr = verifier([]*x509.Certificate{leafCert, intermediateCert, rootCert}, wrongLeafServerName)\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{leafCert, smimeIntermediateCert, rootCert}, \"\")\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{leafCert, smimeIntermediateCert, rootCert}, leafServerName)\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{leafCert, smimeIntermediateCert, rootCert}, wrongLeafServerName)\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{leafCert, intermediateCert, smimeRootCert}, \"\")\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{leafCert, intermediateCert, smimeRootCert}, leafServerName)\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{leafCert, intermediateCert, smimeRootCert}, wrongLeafServerName)\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{leafWithInvalidHashCert, intermediateCert, rootCert}, \"\")\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{leafWithInvalidHashCert, intermediateCert, rootCert}, leafServerName)\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{leafWithInvalidHashCert, intermediateCert, rootCert}, wrongLeafServerName)\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, rootCert}, \"\")\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, rootCert}, leafServerName)\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, rootCert}, wrongLeafServerName)\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, smimeRootCert}, \"\")\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, smimeRootCert}, leafServerName)\n\tassert.Error(t, err)\n\n\terr = verifier([]*x509.Certificate{smimeLeafCert, intermediateCert, smimeRootCert}, wrongLeafServerName)\n\tassert.Error(t, err)\n}\n\nvar rootPEM, _ = pem.Decode([]byte(gtsRoot))\nvar rootCert, _ = x509.ParseCertificate(rootPEM.Bytes)\nvar intermediatePEM, _ = pem.Decode([]byte(gtsIntermediate))\nvar intermediateCert, _ = x509.ParseCertificate(intermediatePEM.Bytes)\nvar leafPEM, _ = pem.Decode([]byte(googleLeaf))\nvar leafCert, _ = x509.ParseCertificate(leafPEM.Bytes)\nvar leafWithInvalidHashPEM, _ = pem.Decode([]byte(googleLeafWithInvalidHash))\nvar leafWithInvalidHashCert, _ = x509.ParseCertificate(leafWithInvalidHashPEM.Bytes)\nvar smimeRootPEM, _ = pem.Decode([]byte(smimeRoot))\nvar smimeRootCert, _ = x509.ParseCertificate(smimeRootPEM.Bytes)\nvar smimeIntermediatePEM, _ = pem.Decode([]byte(smimeIntermediate))\nvar smimeIntermediateCert, _ = x509.ParseCertificate(smimeIntermediatePEM.Bytes)\nvar smimeLeafPEM, _ = pem.Decode([]byte(smimeLeaf))\nvar smimeLeafCert, _ = x509.ParseCertificate(smimeLeafPEM.Bytes)\nvar certTime = func() time.Time { return time.Unix(1677615892, 0) }\n\nconst leafServerName = \"www.google.com\"\nconst wrongLeafServerName = \"www.google.com.cn\"\n\nconst gtsIntermediate = `-----BEGIN CERTIFICATE-----\nMIIFljCCA36gAwIBAgINAgO8U1lrNMcY9QFQZjANBgkqhkiG9w0BAQsFADBHMQsw\nCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU\nMBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMjAwODEzMDAwMDQyWhcNMjcwOTMwMDAw\nMDQyWjBGMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp\nY2VzIExMQzETMBEGA1UEAxMKR1RTIENBIDFDMzCCASIwDQYJKoZIhvcNAQEBBQAD\nggEPADCCAQoCggEBAPWI3+dijB43+DdCkH9sh9D7ZYIl/ejLa6T/belaI+KZ9hzp\nkgOZE3wJCor6QtZeViSqejOEH9Hpabu5dOxXTGZok3c3VVP+ORBNtzS7XyV3NzsX\nlOo85Z3VvMO0Q+sup0fvsEQRY9i0QYXdQTBIkxu/t/bgRQIh4JZCF8/ZK2VWNAcm\nBA2o/X3KLu/qSHw3TT8An4Pf73WELnlXXPxXbhqW//yMmqaZviXZf5YsBvcRKgKA\ngOtjGDxQSYflispfGStZloEAoPtR28p3CwvJlk/vcEnHXG0g/Zm0tOLKLnf9LdwL\ntmsTDIwZKxeWmLnwi/agJ7u2441Rj72ux5uxiZ0CAwEAAaOCAYAwggF8MA4GA1Ud\nDwEB/wQEAwIBhjAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIwEgYDVR0T\nAQH/BAgwBgEB/wIBADAdBgNVHQ4EFgQUinR/r4XN7pXNPZzQ4kYU83E1HScwHwYD\nVR0jBBgwFoAU5K8rJnEaK0gnhS9SZizv8IkTcT4waAYIKwYBBQUHAQEEXDBaMCYG\nCCsGAQUFBzABhhpodHRwOi8vb2NzcC5wa2kuZ29vZy9ndHNyMTAwBggrBgEFBQcw\nAoYkaHR0cDovL3BraS5nb29nL3JlcG8vY2VydHMvZ3RzcjEuZGVyMDQGA1UdHwQt\nMCswKaAnoCWGI2h0dHA6Ly9jcmwucGtpLmdvb2cvZ3RzcjEvZ3RzcjEuY3JsMFcG\nA1UdIARQME4wOAYKKwYBBAHWeQIFAzAqMCgGCCsGAQUFBwIBFhxodHRwczovL3Br\naS5nb29nL3JlcG9zaXRvcnkvMAgGBmeBDAECATAIBgZngQwBAgIwDQYJKoZIhvcN\nAQELBQADggIBAIl9rCBcDDy+mqhXlRu0rvqrpXJxtDaV/d9AEQNMwkYUuxQkq/BQ\ncSLbrcRuf8/xam/IgxvYzolfh2yHuKkMo5uhYpSTld9brmYZCwKWnvy15xBpPnrL\nRklfRuFBsdeYTWU0AIAaP0+fbH9JAIFTQaSSIYKCGvGjRFsqUBITTcFTNvNCCK9U\n+o53UxtkOCcXCb1YyRt8OS1b887U7ZfbFAO/CVMkH8IMBHmYJvJh8VNS/UKMG2Yr\nPxWhu//2m+OBmgEGcYk1KCTd4b3rGS3hSMs9WYNRtHTGnXzGsYZbr8w0xNPM1IER\nlQCh9BIiAfq0g3GvjLeMcySsN1PCAJA/Ef5c7TaUEDu9Ka7ixzpiO2xj2YC/WXGs\nYye5TBeg2vZzFb8q3o/zpWwygTMD0IZRcZk0upONXbVRWPeyk+gB9lm+cZv9TSjO\nz23HFtz30dZGm6fKa+l3D/2gthsjgx0QGtkJAITgRNOidSOzNIb2ILCkXhAd4FJG\nAJ2xDx8hcFH1mt0G/FX0Kw4zd8NLQsLxdxP8c4CU6x+7Nz/OAipmsHMdMqUybDKw\njuDEI/9bfU1lcKwrmz3O2+BtjjKAvpafkmO8l7tdufThcV4q5O8DIrGKZTqPwJNl\n1IXNDw9bg1kWRxYtnCQ6yICmJhSFm/Y3m6xv+cXDBlHz4n/FsRC6UfTd\n-----END CERTIFICATE-----`\n\nconst gtsRoot = `-----BEGIN CERTIFICATE-----\nMIIFVzCCAz+gAwIBAgINAgPlk28xsBNJiGuiFzANBgkqhkiG9w0BAQwFADBHMQsw\nCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExMQzEU\nMBIGA1UEAxMLR1RTIFJvb3QgUjEwHhcNMTYwNjIyMDAwMDAwWhcNMzYwNjIyMDAw\nMDAwWjBHMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZp\nY2VzIExMQzEUMBIGA1UEAxMLR1RTIFJvb3QgUjEwggIiMA0GCSqGSIb3DQEBAQUA\nA4ICDwAwggIKAoICAQC2EQKLHuOhd5s73L+UPreVp0A8of2C+X0yBoJx9vaMf/vo\n27xqLpeXo4xL+Sv2sfnOhB2x+cWX3u+58qPpvBKJXqeqUqv4IyfLpLGcY9vXmX7w\nCl7raKb0xlpHDU0QM+NOsROjyBhsS+z8CZDfnWQpJSMHobTSPS5g4M/SCYe7zUjw\nTcLCeoiKu7rPWRnWr4+wB7CeMfGCwcDfLqZtbBkOtdh+JhpFAz2weaSUKK0Pfybl\nqAj+lug8aJRT7oM6iCsVlgmy4HqMLnXWnOunVmSPlk9orj2XwoSPwLxAwAtcvfaH\nszVsrBhQf4TgTM2S0yDpM7xSma8ytSmzJSq0SPly4cpk9+aCEI3oncKKiPo4Zor8\nY/kB+Xj9e1x3+naH+uzfsQ55lVe0vSbv1gHR6xYKu44LtcXFilWr06zqkUspzBmk\nMiVOKvFlRNACzqrOSbTqn3yDsEB750Orp2yjj32JgfpMpf/VjsPOS+C12LOORc92\nwO1AK/1TD7Cn1TsNsYqiA94xrcx36m97PtbfkSIS5r762DL8EGMUUXLeXdYWk70p\naDPvOmbsB4om3xPXV2V4J95eSRQAogB/mqghtqmxlbCluQ0WEdrHbEg8QOB+DVrN\nVjzRlwW5y0vtOUucxD/SVRNuJLDWcfr0wbrM7Rv1/oFB2ACYPTrIrnqYNxgFlQID\nAQABo0IwQDAOBgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4E\nFgQU5K8rJnEaK0gnhS9SZizv8IkTcT4wDQYJKoZIhvcNAQEMBQADggIBAJ+qQibb\nC5u+/x6Wki4+omVKapi6Ist9wTrYggoGxval3sBOh2Z5ofmmWJyq+bXmYOfg6LEe\nQkEzCzc9zolwFcq1JKjPa7XSQCGYzyI0zzvFIoTgxQ6KfF2I5DUkzps+GlQebtuy\nh6f88/qBVRRiClmpIgUxPoLW7ttXNLwzldMXG+gnoot7TiYaelpkttGsN/H9oPM4\n7HLwEXWdyzRSjeZ2axfG34arJ45JK3VmgRAhpuo+9K4l/3wV3s6MJT/KYnAK9y8J\nZgfIPxz88NtFMN9iiMG1D53Dn0reWVlHxYciNuaCp+0KueIHoI17eko8cdLiA6Ef\nMgfdG+RCzgwARWGAtQsgWSl4vflVy2PFPEz0tv/bal8xa5meLMFrUKTX5hgUvYU/\nZ6tGn6D/Qqc6f1zLXbBwHSs09dR2CQzreExZBfMzQsNhFRAbd03OIozUhfJFfbdT\n6u9AWpQKXCBfTkBdYiJ23//OYb2MI3jSNwLgjt7RETeJ9r/tSQdirpLsQBqvFAnZ\n0E6yove+7u7Y/9waLd64NnHi/Hm3lCXRSHNboTXns5lndcEZOitHTtNCjv0xyBZm\n2tIMPNuzjsmhDYAPexZ3FL//2wmUspO8IFgV6dtxQ/PeEMMA3KgqlbbC1j+Qa3bb\nbP6MvPJwNQzcmRk13NfIRmPVNnGuV/u3gm3c\n-----END CERTIFICATE-----`\n\nconst googleLeaf = `-----BEGIN CERTIFICATE-----\nMIIFUjCCBDqgAwIBAgIQERmRWTzVoz0SMeozw2RM3DANBgkqhkiG9w0BAQsFADBG\nMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM\nQzETMBEGA1UEAxMKR1RTIENBIDFDMzAeFw0yMzAxMDIwODE5MTlaFw0yMzAzMjcw\nODE5MThaMBkxFzAVBgNVBAMTDnd3dy5nb29nbGUuY29tMIIBIjANBgkqhkiG9w0B\nAQEFAAOCAQ8AMIIBCgKCAQEAq30odrKMT54TJikMKL8S+lwoCMT5geP0u9pWjk6a\nwdB6i3kO+UE4ijCAmhbcZKeKaLnGJ38weZNwB1ayabCYyX7hDiC/nRcZU49LX5+o\n55kDVaNn14YKkg2kCeX25HDxSwaOsNAIXKPTqiQL5LPvc4Twhl8HY51hhNWQrTEr\nN775eYbixEULvyVLq5BLbCOpPo8n0/MTjQ32ku1jQq3GIYMJC/Rf2VW5doF6t9zs\nKleflAN8OdKp0ME9OHg0T1P3yyb67T7n0SpisHbeG06AmQcKJF9g/9VPJtRf4l1Q\nWRPDC+6JUqzXCxAGmIRGZ7TNMxPMBW/7DRX6w8oLKVNb0wIDAQABo4ICZzCCAmMw\nDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQC\nMAAwHQYDVR0OBBYEFBnboj3lf9+Xat4oEgo6ZtIMr8ZuMB8GA1UdIwQYMBaAFIp0\nf6+Fze6VzT2c0OJGFPNxNR0nMGoGCCsGAQUFBwEBBF4wXDAnBggrBgEFBQcwAYYb\naHR0cDovL29jc3AucGtpLmdvb2cvZ3RzMWMzMDEGCCsGAQUFBzAChiVodHRwOi8v\ncGtpLmdvb2cvcmVwby9jZXJ0cy9ndHMxYzMuZGVyMBkGA1UdEQQSMBCCDnd3dy5n\nb29nbGUuY29tMCEGA1UdIAQaMBgwCAYGZ4EMAQIBMAwGCisGAQQB1nkCBQMwPAYD\nVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybHMucGtpLmdvb2cvZ3RzMWMzL1FPdkow\nTjFzVDJBLmNybDCCAQQGCisGAQQB1nkCBAIEgfUEgfIA8AB2AHoyjFTYty22IOo4\n4FIe6YQWcDIThU070ivBOlejUutSAAABhXHHOiUAAAQDAEcwRQIgBUkikUIXdo+S\n3T8PP0/cvokhUlumRE3GRWGL4WRMLpcCIQDY+bwK384mZxyXGZ5lwNRTAPNzT8Fx\n1+//nbaGK3BQMAB2AOg+0No+9QY1MudXKLyJa8kD08vREWvs62nhd31tBr1uAAAB\nhXHHOfQAAAQDAEcwRQIgLoVydNfMFKV9IoZR+M0UuJ2zOqbxIRum7Sn9RMPOBGMC\nIQD1/BgzCSDTvYvco6kpB6ifKSbg5gcb5KTnYxQYwRW14TANBgkqhkiG9w0BAQsF\nAAOCAQEA2bQQu30e3OFu0bmvQHmcqYvXBu6tF6e5b5b+hj4O+Rn7BXTTmaYX3M6p\nMsfRH4YVJJMB/dc3PROR2VtnKFC6gAZX+RKM6nXnZhIlOdmQnonS1ecOL19PliUd\nVXbwKjXqAO0Ljd9y9oXaXnyPyHmUJNI5YXAcxE+XXiOZhcZuMYyWmoEKJQ/XlSga\nzWfTn1IcKhA3IC7A1n/5bkkWD1Xi1mdWFQ6DQDMp//667zz7pKOgFMlB93aPDjvI\nc78zEqNswn6xGKXpWF5xVwdFcsx9HKhJ6UAi2bQ/KQ1yb7LPUOR6wXXWrG1cLnNP\ni8eNLnKL9PXQ+5SwJFCzfEhcIZuhzg==\n-----END CERTIFICATE-----`\n\n// googleLeafWithInvalidHash is the same as googleLeaf, but the signature\n// algorithm in the certificate contains a nonsense OID.\nconst googleLeafWithInvalidHash = `-----BEGIN CERTIFICATE-----\nMIIFUjCCBDqgAwIBAgIQERmRWTzVoz0SMeozw2RM3DANBgkqhkiG9w0BAQ4FADBG\nMQswCQYDVQQGEwJVUzEiMCAGA1UEChMZR29vZ2xlIFRydXN0IFNlcnZpY2VzIExM\nQzETMBEGA1UEAxMKR1RTIENBIDFDMzAeFw0yMzAxMDIwODE5MTlaFw0yMzAzMjcw\nODE5MThaMBkxFzAVBgNVBAMTDnd3dy5nb29nbGUuY29tMIIBIjANBgkqhkiG9w0B\nAQEFAAOCAQ8AMIIBCgKCAQEAq30odrKMT54TJikMKL8S+lwoCMT5geP0u9pWjk6a\nwdB6i3kO+UE4ijCAmhbcZKeKaLnGJ38weZNwB1ayabCYyX7hDiC/nRcZU49LX5+o\n55kDVaNn14YKkg2kCeX25HDxSwaOsNAIXKPTqiQL5LPvc4Twhl8HY51hhNWQrTEr\nN775eYbixEULvyVLq5BLbCOpPo8n0/MTjQ32ku1jQq3GIYMJC/Rf2VW5doF6t9zs\nKleflAN8OdKp0ME9OHg0T1P3yyb67T7n0SpisHbeG06AmQcKJF9g/9VPJtRf4l1Q\nWRPDC+6JUqzXCxAGmIRGZ7TNMxPMBW/7DRX6w8oLKVNb0wIDAQABo4ICZzCCAmMw\nDgYDVR0PAQH/BAQDAgWgMBMGA1UdJQQMMAoGCCsGAQUFBwMBMAwGA1UdEwEB/wQC\nMAAwHQYDVR0OBBYEFBnboj3lf9+Xat4oEgo6ZtIMr8ZuMB8GA1UdIwQYMBaAFIp0\nf6+Fze6VzT2c0OJGFPNxNR0nMGoGCCsGAQUFBwEBBF4wXDAnBggrBgEFBQcwAYYb\naHR0cDovL29jc3AucGtpLmdvb2cvZ3RzMWMzMDEGCCsGAQUFBzAChiVodHRwOi8v\ncGtpLmdvb2cvcmVwby9jZXJ0cy9ndHMxYzMuZGVyMBkGA1UdEQQSMBCCDnd3dy5n\nb29nbGUuY29tMCEGA1UdIAQaMBgwCAYGZ4EMAQIBMAwGCisGAQQB1nkCBQMwPAYD\nVR0fBDUwMzAxoC+gLYYraHR0cDovL2NybHMucGtpLmdvb2cvZ3RzMWMzL1FPdkow\nTjFzVDJBLmNybDCCAQQGCisGAQQB1nkCBAIEgfUEgfIA8AB2AHoyjFTYty22IOo4\n4FIe6YQWcDIThU070ivBOlejUutSAAABhXHHOiUAAAQDAEcwRQIgBUkikUIXdo+S\n3T8PP0/cvokhUlumRE3GRWGL4WRMLpcCIQDY+bwK384mZxyXGZ5lwNRTAPNzT8Fx\n1+//nbaGK3BQMAB2AOg+0No+9QY1MudXKLyJa8kD08vREWvs62nhd31tBr1uAAAB\nhXHHOfQAAAQDAEcwRQIgLoVydNfMFKV9IoZR+M0UuJ2zOqbxIRum7Sn9RMPOBGMC\nIQD1/BgzCSDTvYvco6kpB6ifKSbg5gcb5KTnYxQYwRW14TANBgkqhkiG9w0BAQ4F\nAAOCAQEA2bQQu30e3OFu0bmvQHmcqYvXBu6tF6e5b5b+hj4O+Rn7BXTTmaYX3M6p\nMsfRH4YVJJMB/dc3PROR2VtnKFC6gAZX+RKM6nXnZhIlOdmQnonS1ecOL19PliUd\nVXbwKjXqAO0Ljd9y9oXaXnyPyHmUJNI5YXAcxE+XXiOZhcZuMYyWmoEKJQ/XlSga\nzWfTn1IcKhA3IC7A1n/5bkkWD1Xi1mdWFQ6DQDMp//667zz7pKOgFMlB93aPDjvI\nc78zEqNswn6xGKXpWF5xVwdFcsx9HKhJ6UAi2bQ/KQ1yb7LPUOR6wXXWrG1cLnNP\ni8eNLnKL9PXQ+5SwJFCzfEhcIZuhzg==\n-----END CERTIFICATE-----`\n\nconst smimeLeaf = `-----BEGIN CERTIFICATE-----\nMIIIPDCCBiSgAwIBAgIQaMDxFS0pOMxZZeOBxoTJtjANBgkqhkiG9w0BAQsFADCB\nnTELMAkGA1UEBhMCRVMxFDASBgNVBAoMC0laRU5QRSBTLkEuMTowOAYDVQQLDDFB\nWlogWml1cnRhZ2lyaSBwdWJsaWtvYSAtIENlcnRpZmljYWRvIHB1YmxpY28gU0NB\nMTwwOgYDVQQDDDNFQUVrbyBIZXJyaSBBZG1pbmlzdHJhemlvZW4gQ0EgLSBDQSBB\nQVBQIFZhc2NhcyAoMikwHhcNMTcwNzEyMDg1MzIxWhcNMjEwNzEyMDg1MzIxWjCC\nAQwxDzANBgNVBAoMBklaRU5QRTE4MDYGA1UECwwvWml1cnRhZ2lyaSBrb3Jwb3Jh\ndGlib2EtQ2VydGlmaWNhZG8gY29ycG9yYXRpdm8xQzBBBgNVBAsMOkNvbmRpY2lv\nbmVzIGRlIHVzbyBlbiB3d3cuaXplbnBlLmNvbSBub2xhIGVyYWJpbGkgamFraXRl\na28xFzAVBgNVBC4TDi1kbmkgOTk5OTk5ODlaMSQwIgYDVQQDDBtDT1JQT1JBVElW\nTyBGSUNUSUNJTyBBQ1RJVk8xFDASBgNVBCoMC0NPUlBPUkFUSVZPMREwDwYDVQQE\nDAhGSUNUSUNJTzESMBAGA1UEBRMJOTk5OTk5ODlaMIIBIjANBgkqhkiG9w0BAQEF\nAAOCAQ8AMIIBCgKCAQEAwVOMwUDfBtsH0XuxYnb+v/L774jMH8valX7RPH8cl2Lb\nSiqSo0RchW2RGA2d1yuYHlpChC9jGmt0X/g66/E/+q2hUJlfJtqVDJFwtFYV4u2S\nyzA3J36V4PRkPQrKxAsbzZriFXAF10XgiHQz9aVeMMJ9GBhmh9+DK8Tm4cMF6i8l\n+AuC35KdngPF1x0ealTYrYZplpEJFO7CiW42aLi6vQkDR2R7nmZA4AT69teqBWsK\n0DZ93/f0G/3+vnWwNTBF0lB6dIXoaz8OMSyHLqGnmmAtMrzbjAr/O/WWgbB/BqhR\nqjJQ7Ui16cuDldXaWQ/rkMzsxmsAox0UF+zdQNvXUQIDAQABo4IDBDCCAwAwgccG\nA1UdEgSBvzCBvIYVaHR0cDovL3d3dy5pemVucGUuY29tgQ9pbmZvQGl6ZW5wZS5j\nb22kgZEwgY4xRzBFBgNVBAoMPklaRU5QRSBTLkEuIC0gQ0lGIEEwMTMzNzI2MC1S\nTWVyYy5WaXRvcmlhLUdhc3RlaXogVDEwNTUgRjYyIFM4MUMwQQYDVQQJDDpBdmRh\nIGRlbCBNZWRpdGVycmFuZW8gRXRvcmJpZGVhIDE0IC0gMDEwMTAgVml0b3JpYS1H\nYXN0ZWl6MB4GA1UdEQQXMBWBE2ZpY3RpY2lvQGl6ZW5wZS5ldXMwDgYDVR0PAQH/\nBAQDAgXgMCkGA1UdJQQiMCAGCCsGAQUFBwMCBggrBgEFBQcDBAYKKwYBBAGCNxQC\nAjAdBgNVHQ4EFgQUyeoOD4cgcljKY0JvrNuX2waFQLAwHwYDVR0jBBgwFoAUwKlK\n90clh/+8taaJzoLSRqiJ66MwggEnBgNVHSAEggEeMIIBGjCCARYGCisGAQQB8zkB\nAQEwggEGMDMGCCsGAQUFBwIBFidodHRwOi8vd3d3Lml6ZW5wZS5jb20vcnBhc2Nh\nY29ycG9yYXRpdm8wgc4GCCsGAQUFBwICMIHBGoG+Wml1cnRhZ2lyaWEgRXVza2Fs\nIEF1dG9ub21pYSBFcmtpZGVnb2tvIHNla3RvcmUgcHVibGlrb2tvIGVyYWt1bmRl\nZW4gYmFybmUtc2FyZWV0YW4gYmFrYXJyaWsgZXJhYmlsIGRhaXRla2UuIFVzbyBy\nZXN0cmluZ2lkbyBhbCBhbWJpdG8gZGUgcmVkZXMgaW50ZXJuYXMgZGUgRW50aWRh\nZGVzIGRlbCBTZWN0b3IgUHVibGljbyBWYXNjbzAyBggrBgEFBQcBAQQmMCQwIgYI\nKwYBBQUHMAGGFmh0dHA6Ly9vY3NwLml6ZW5wZS5jb20wOgYDVR0fBDMwMTAvoC2g\nK4YpaHR0cDovL2NybC5pemVucGUuY29tL2NnaS1iaW4vY3JsaW50ZXJuYTIwDQYJ\nKoZIhvcNAQELBQADggIBAIy5PQ+UZlCRq6ig43vpHwlwuD9daAYeejV0Q+ZbgWAE\nGtO0kT/ytw95ZEJMNiMw3fYfPRlh27ThqiT0VDXZJDlzmn7JZd6QFcdXkCsiuv4+\nZoXAg/QwnA3SGUUO9aVaXyuOIIuvOfb9MzoGp9xk23SMV3eiLAaLMLqwB5DTfBdt\nBGI7L1MnGJBv8RfP/TL67aJ5bgq2ri4S8vGHtXSjcZ0+rCEOLJtmDNMnTZxancg3\n/H5edeNd+n6Z48LO+JHRxQufbC4mVNxVLMIP9EkGUejlq4E4w6zb5NwCQczJbSWL\ni31rk2orsNsDlyaLGsWZp3JSNX6RmodU4KAUPor4jUJuUhrrm3Spb73gKlV/gcIw\nbCE7mML1Kss3x1ySaXsis6SZtLpGWKkW2iguPWPs0ydV6RPhmsCxieMwPPIJ87vS\n5IejfgyBae7RSuAIHyNFy4uI5xwvwUFf6OZ7az8qtW7ImFOgng3Ds+W9k1S2CNTx\nd0cnKTfA6IpjGo8EeHcxnIXT8NPImWaRj0qqonvYady7ci6U4m3lkNSdXNn1afgw\nmYust+gxVtOZs1gk2MUCgJ1V1X+g7r/Cg7viIn6TLkLrpS1kS1hvMqkl9M+7XqPo\nQd95nJKOkusQpy99X4dF/lfbYAQnnjnqh3DLD2gvYObXFaAYFaiBKTiMTV2X72F+\n-----END CERTIFICATE-----`\n\nconst smimeIntermediate = `-----BEGIN CERTIFICATE-----\nMIIHNzCCBSGgAwIBAgIQJMXIqlZvjuhMvqcFXOFkpDALBgkqhkiG9w0BAQswODEL\nMAkGA1UEBhMCRVMxFDASBgNVBAoMC0laRU5QRSBTLkEuMRMwEQYDVQQDDApJemVu\ncGUuY29tMB4XDTEwMTAyMDA4MjMzM1oXDTM3MTIxMjIzMDAwMFowgZ0xCzAJBgNV\nBAYTAkVTMRQwEgYDVQQKDAtJWkVOUEUgUy5BLjE6MDgGA1UECwwxQVpaIFppdXJ0\nYWdpcmkgcHVibGlrb2EgLSBDZXJ0aWZpY2FkbyBwdWJsaWNvIFNDQTE8MDoGA1UE\nAwwzRUFFa28gSGVycmkgQWRtaW5pc3RyYXppb2VuIENBIC0gQ0EgQUFQUCBWYXNj\nYXMgKDIpMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAoIM7nEdI0N1h\nrR5T4xuV/usKDoMIasaiKvfLhbwxaNtTt+a7W/6wV5bv3svQFIy3sUXjjdzV1nG2\nTo2wo/YSPQiOt8exWvOapvL21ogiof+kelWnXFjWaKJI/vThHYLgIYEMj/y4HdtU\nojI646rZwqsb4YGAopwgmkDfUh5jOhV2IcYE3TgJAYWVkj6jku9PLaIsHiarAHjD\nPY8dig8a4SRv0gm5Yk7FXLmW1d14oxQBDeHZ7zOEXfpafxdEDO2SNaRJjpkh8XRr\nPGqkg2y1Q3gT6b4537jz+StyDIJ3omylmlJsGCwqT7p8mEqjGJ5kC5I2VnjXKuNn\nsoShc72khWZVUJiJo5SGuAkNE2ZXqltBVm5Jv6QweQKsX6bkcMc4IZok4a+hx8FM\n8IBpGf/I94pU6HzGXqCyc1d46drJgDY9mXa+6YDAJFl3xeXOOW2iGCfwXqhiCrKL\nMYvyMZzqF3QH5q4nb3ZnehYvraeMFXJXDn+Utqp8vd2r7ShfQJz01KtM4hgKdgSg\njtW+shkVVN5ng/fPN85ovfAH2BHXFfHmQn4zKsYnLitpwYM/7S1HxlT61cdQ7Nnk\n3LZTYEgAoOmEmdheklT40WAYakksXGM5VrzG7x9S7s1Tm+Vb5LSThdHC8bxxwyTb\nKsDRDNJ84N9fPDO6qHnzaL2upQ43PycCAwEAAaOCAdkwggHVMIHHBgNVHREEgb8w\ngbyGFWh0dHA6Ly93d3cuaXplbnBlLmNvbYEPaW5mb0BpemVucGUuY29tpIGRMIGO\nMUcwRQYDVQQKDD5JWkVOUEUgUy5BLiAtIENJRiBBMDEzMzcyNjAtUk1lcmMuVml0\nb3JpYS1HYXN0ZWl6IFQxMDU1IEY2MiBTODFDMEEGA1UECQw6QXZkYSBkZWwgTWVk\naXRlcnJhbmVvIEV0b3JiaWRlYSAxNCAtIDAxMDEwIFZpdG9yaWEtR2FzdGVpejAP\nBgNVHRMBAf8EBTADAQH/MA4GA1UdDwEB/wQEAwIBBjAdBgNVHQ4EFgQUwKlK90cl\nh/+8taaJzoLSRqiJ66MwHwYDVR0jBBgwFoAUHRxlDqjyJXu0kc/ksbHmvVV0bAUw\nOgYDVR0gBDMwMTAvBgRVHSAAMCcwJQYIKwYBBQUHAgEWGWh0dHA6Ly93d3cuaXpl\nbnBlLmNvbS9jcHMwNwYIKwYBBQUHAQEEKzApMCcGCCsGAQUFBzABhhtodHRwOi8v\nb2NzcC5pemVucGUuY29tOjgwOTQwMwYDVR0fBCwwKjAooCagJIYiaHR0cDovL2Ny\nbC5pemVucGUuY29tL2NnaS1iaW4vYXJsMjALBgkqhkiG9w0BAQsDggIBAMbjc3HM\n3DG9ubWPkzsF0QsktukpujbTTcGk4h20G7SPRy1DiiTxrRzdAMWGjZioOP3/fKCS\nM539qH0M+gsySNie+iKlbSZJUyE635T1tKw+G7bDUapjlH1xyv55NC5I6wCXGC6E\n3TEP5B/E7dZD0s9E4lS511ubVZivFgOzMYo1DO96diny/N/V1enaTCpRl1qH1OyL\nxUYTijV4ph2gL6exwuG7pxfRcVNHYlrRaXWfTz3F6NBKyULxrI3P/y6JAtN1GqT4\nVF/+vMygx22n0DufGepBwTQz6/rr1ulSZ+eMnuJiTXgh/BzQnkUsXTb8mHII25iR\n0oYF2qAsk6ecWbLiDpkHKIDHmML21MZE13MS8NSvTHoqJO4LyAmDe6SaeNHtrPlK\nb6mzE1BN2ug+ZaX8wLA5IMPFaf0jKhb/Cxu8INsxjt00brsErCc9ip1VNaH0M4bi\n1tGxfiew2436FaeyUxW7Pl6G5GgkNbuUc7QIoRy06DdU/U38BxW3uyJMY60zwHvS\nFlKAn0OvYp4niKhAJwaBVN3kowmJuOU5Rid+TUnfyxbJ9cttSgzaF3hP/N4zgMEM\n5tikXUskeckt8LUK96EH0QyssavAMECUEb/xrupyRdYWwjQGvNLq6T5+fViDGyOw\nk+lzD44wofy8paAy9uC9Owae0zMEzhcsyRm7\n-----END CERTIFICATE-----`\n\nconst smimeRoot = `-----BEGIN CERTIFICATE-----\nMIIF8TCCA9mgAwIBAgIQALC3WhZIX7/hy/WL1xnmfTANBgkqhkiG9w0BAQsFADA4\nMQswCQYDVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6\nZW5wZS5jb20wHhcNMDcxMjEzMTMwODI4WhcNMzcxMjEzMDgyNzI1WjA4MQswCQYD\nVQQGEwJFUzEUMBIGA1UECgwLSVpFTlBFIFMuQS4xEzARBgNVBAMMCkl6ZW5wZS5j\nb20wggIiMA0GCSqGSIb3DQEBAQUAA4ICDwAwggIKAoICAQDJ03rKDx6sp4boFmVq\nscIbRTJxldn+EFvMr+eleQGPicPK8lVx93e+d5TzcqQsRNiekpsUOqHnJJAKClaO\nxdgmlOHZSOEtPtoKct2jmRXagaKH9HtuJneJWK3W6wyyQXpzbm3benhB6QiIEn6H\nLmYRY2xU+zydcsC8Lv/Ct90NduM61/e0aL6i9eOBbsFGb12N4E3GVFWJGjMxCrFX\nuaOKmMPsOzTFlUFpfnXCPCDFYbpRR6AgkJOhkEvzTnyFRVSa0QUmQbC1TR0zvsQD\nyCV8wXDbO/QJLVQnSKwv4cSsPsjLkkxTOTcj7NMB+eAJRE1NZMDhDVqHIrytG6P+\nJrUV86f8hBnp7KGItERphIPzidF0BqnMC9bC3ieFUCbKF7jJeodWLBoBHmy+E60Q\nrLUk9TiRodZL2vG70t5HtfG8gfZZa88ZU+mNFctKy6lvROUbQc/hhqfK0GqfvEyN\nBjNaooXlkDWgYlwWTvDjovoDGrQscbNYLN57C9saD+veIR8GdwYDsMnvmfzAuU8L\nhij+0rnq49qlw0dpEuDb8PYZi+17cNcC1u2HGCgsBCRMd+RIihrGO5rUD8r6ddIB\nQFqNeb+Lz0vPqhbBleStTIo+F5HUsWLlguWABKQDfo2/2n+iD5dPDNMN+9fR5XJ+\nHMh3/1uaD7euBUbl8agW7EekFwIDAQABo4H2MIHzMIGwBgNVHREEgagwgaWBD2lu\nZm9AaXplbnBlLmNvbaSBkTCBjjFHMEUGA1UECgw+SVpFTlBFIFMuQS4gLSBDSUYg\nQTAxMzM3MjYwLVJNZXJjLlZpdG9yaWEtR2FzdGVpeiBUMTA1NSBGNjIgUzgxQzBB\nBgNVBAkMOkF2ZGEgZGVsIE1lZGl0ZXJyYW5lbyBFdG9yYmlkZWEgMTQgLSAwMTAx\nMCBWaXRvcmlhLUdhc3RlaXowDwYDVR0TAQH/BAUwAwEB/zAOBgNVHQ8BAf8EBAMC\nAQYwHQYDVR0OBBYEFB0cZQ6o8iV7tJHP5LGx5r1VdGwFMA0GCSqGSIb3DQEBCwUA\nA4ICAQB4pgwWSp9MiDrAyw6lFn2fuUhfGI8NYjb2zRlrrKvV9pF9rnHzP7MOeIWb\nlaQnIUdCSnxIOvVFfLMMjlF4rJUT3sb9fbgakEyrkgPH7UIBzg/YsfqikuFgba56\nawmqxinuaElnMIAkejEWOVt+8Rwu3WwJrfIxwYJOubv5vr8qhT/AQKM6WfxZSzwo\nJNu0FXWuDYi6LnPAvViH5ULy617uHjAimcs30cQhbIHsvm0m5hzkQiCeR7Csg1lw\nLDXWrzY0tM07+DKo7+N4ifuNRSzanLh+QBxh5z6ikixL8s36mLYp//Pye6kfLqCT\nVyvehQP5aTfLnnhqBbTFMXiJ7HqnheG5ezzevh55hM6fcA5ZwjUukCox2eRFekGk\nLhObNA5me0mrZJfQRsN5nXJQY6aYWwa9SG3YOYNw6DXwBdGqvOPbyALqfP2C2sJb\nUjWumDqtujWTI6cfSN01RpiyEGjkpTHCClguGYEQyVB1/OpaFs4R1+7vUIgtYf8/\nQnMFlEPVjjxOAToZpR9GTnfQXeWBIiGH/pR9hNiTrdZoQ0iy2+tzJOeRf1SktoA+\nnaM8THLCV8Sg1Mw4J87VBp6iSNnpn86CcDaTmjvfliHjWbcM2pE38P1ZWrOZyGls\nQyYBNWNgVYkDOnXYukrZVP/u3oDYLdE41V4tC5h9Pmzb/CaIxw==\n-----END CERTIFICATE-----`\n"
  },
  {
    "path": "core/Clash.Meta/component/ca/fix_windows.go",
    "content": "package ca\n\nimport (\n\t\"github.com/metacubex/mihomo/constant/features\"\n)\n\nfunc init() {\n\t// crypto/x509: certificate validation in Windows fails to validate IP in SAN\n\t// https://github.com/golang/go/issues/37176\n\t// As far as I can tell this is still the case on most older versions of Windows (but seems to be fixed in 10)\n\tif features.WindowsMajorVersion < 10 && len(_CaCertificates) > 0 {\n\t\tDisableSystemCa = true\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/ca/keypair.go",
    "content": "package ca\n\nimport (\n\t\"crypto\"\n\t\"crypto/ecdsa\"\n\t\"crypto/ed25519\"\n\t\"crypto/elliptic\"\n\t\"crypto/rand\"\n\t\"crypto/rsa\"\n\t\"crypto/x509\"\n\t\"encoding/pem\"\n\t\"fmt\"\n\t\"math/big\"\n\t\"os\"\n\t\"runtime\"\n\t\"sync\"\n\t\"time\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\n\t\"github.com/metacubex/fswatch\"\n\t\"github.com/metacubex/tls\"\n)\n\n// NewTLSKeyPairLoader creates a loader function for TLS key pairs from the provided certificate and private key data or file paths.\n// If both certificate and privateKey are empty, generates a random TLS RSA key pair.\nfunc NewTLSKeyPairLoader(certificate, privateKey string) (func() (*tls.Certificate, error), error) {\n\tif certificate == \"\" && privateKey == \"\" {\n\t\tvar err error\n\t\tcertificate, privateKey, _, err = NewRandomTLSKeyPair(KeyPairTypeRSA)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tcert, painTextErr := tls.X509KeyPair([]byte(certificate), []byte(privateKey))\n\tif painTextErr == nil {\n\t\treturn func() (*tls.Certificate, error) {\n\t\t\treturn &cert, nil\n\t\t}, nil\n\t}\n\n\tcertificate = C.Path.Resolve(certificate)\n\tprivateKey = C.Path.Resolve(privateKey)\n\tvar loadErr error\n\tif !C.Path.IsSafePath(certificate) {\n\t\tloadErr = C.Path.ErrNotSafePath(certificate)\n\t} else if !C.Path.IsSafePath(privateKey) {\n\t\tloadErr = C.Path.ErrNotSafePath(privateKey)\n\t} else {\n\t\tcert, loadErr = tls.LoadX509KeyPair(certificate, privateKey)\n\t}\n\tif loadErr != nil {\n\t\treturn nil, fmt.Errorf(\"parse certificate failed, maybe format error:%s, or path error: %s\", painTextErr.Error(), loadErr.Error())\n\t}\n\tgcFlag := new(os.File) // tiny (on the order of 16 bytes or less) and pointer-free objects may never run the finalizer, so we choose new an os.File\n\tupdateMutex := sync.RWMutex{}\n\tif watcher, err := fswatch.NewWatcher(fswatch.Options{Path: []string{certificate, privateKey}, Callback: func(path string) {\n\t\tupdateMutex.Lock()\n\t\tdefer updateMutex.Unlock()\n\t\tif newCert, err := tls.LoadX509KeyPair(certificate, privateKey); err == nil {\n\t\t\tcert = newCert\n\t\t}\n\t}}); err == nil {\n\t\tif err = watcher.Start(); err == nil {\n\t\t\truntime.SetFinalizer(gcFlag, func(f *os.File) {\n\t\t\t\t_ = watcher.Close()\n\t\t\t})\n\t\t}\n\t}\n\treturn func() (*tls.Certificate, error) {\n\t\tdefer runtime.KeepAlive(gcFlag)\n\t\tupdateMutex.RLock()\n\t\tdefer updateMutex.RUnlock()\n\t\treturn &cert, nil\n\t}, nil\n}\n\nfunc LoadCertificates(certificate string) (*x509.CertPool, error) {\n\tpool := x509.NewCertPool()\n\tif pool.AppendCertsFromPEM([]byte(certificate)) {\n\t\treturn pool, nil\n\t}\n\tpainTextErr := fmt.Errorf(\"invalid certificate: %s\", certificate)\n\n\tcertificate = C.Path.Resolve(certificate)\n\tvar loadErr error\n\tif !C.Path.IsSafePath(certificate) {\n\t\tloadErr = C.Path.ErrNotSafePath(certificate)\n\t} else {\n\t\tcertPEMBlock, err := os.ReadFile(certificate)\n\t\tif pool.AppendCertsFromPEM(certPEMBlock) {\n\t\t\treturn pool, nil\n\t\t}\n\t\tloadErr = err\n\t}\n\tif loadErr != nil {\n\t\treturn nil, fmt.Errorf(\"parse certificate failed, maybe format error:%s, or path error: %s\", painTextErr.Error(), loadErr.Error())\n\t}\n\t//TODO: support dynamic update pool too\n\t//      blocked by: https://github.com/golang/go/issues/64796\n\t//      maybe we can direct add `GetRootCAs` and `GetClientCAs` to ourselves tls fork\n\treturn pool, nil\n}\n\ntype KeyPairType string\n\nconst (\n\tKeyPairTypeRSA     KeyPairType = \"rsa\"\n\tKeyPairTypeP256    KeyPairType = \"p256\"\n\tKeyPairTypeP384    KeyPairType = \"p384\"\n\tKeyPairTypeEd25519 KeyPairType = \"ed25519\"\n)\n\n// NewRandomTLSKeyPair generates a random TLS key pair based on the specified KeyPairType and returns it with a SHA256 fingerprint.\n// Note: Most browsers do not support KeyPairTypeEd25519 type of certificate, and utls.UConn will also reject this type of certificate.\nfunc NewRandomTLSKeyPair(keyPairType KeyPairType) (certificate string, privateKey string, fingerprint string, err error) {\n\tvar key crypto.Signer\n\tswitch keyPairType {\n\tcase KeyPairTypeRSA:\n\t\tkey, err = rsa.GenerateKey(rand.Reader, 2048)\n\tcase KeyPairTypeP256:\n\t\tkey, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader)\n\tcase KeyPairTypeP384:\n\t\tkey, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader)\n\tcase KeyPairTypeEd25519:\n\t\t_, key, err = ed25519.GenerateKey(rand.Reader)\n\tdefault: // fallback to KeyPairTypeRSA\n\t\tkey, err = rsa.GenerateKey(rand.Reader, 2048)\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\n\ttemplate := x509.Certificate{\n\t\tSerialNumber: big.NewInt(1),\n\t\tNotBefore:    time.Now().Add(-time.Hour * 24 * 365),\n\t\tNotAfter:     time.Now().Add(time.Hour * 24 * 365),\n\t}\n\tcertDER, err := x509.CreateCertificate(rand.Reader, &template, &template, key.Public(), key)\n\tif err != nil {\n\t\treturn\n\t}\n\tprivBytes, err := x509.MarshalPKCS8PrivateKey(key)\n\tif err != nil {\n\t\treturn\n\t}\n\tfingerprint = CalculateFingerprint(certDER)\n\tprivateKey = string(pem.EncodeToMemory(&pem.Block{Type: \"PRIVATE KEY\", Bytes: privBytes}))\n\tcertificate = string(pem.EncodeToMemory(&pem.Block{Type: \"CERTIFICATE\", Bytes: certDER}))\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/cidr/ipcidr_set.go",
    "content": "package cidr\n\nimport (\n\t\"fmt\"\n\t\"net/netip\"\n\t\"unsafe\"\n\n\t\"go4.org/netipx\"\n)\n\ntype IpCidrSet struct {\n\t// must same with netipx.IPSet\n\trr []netipx.IPRange\n}\n\nfunc NewIpCidrSet() *IpCidrSet {\n\treturn &IpCidrSet{}\n}\n\nfunc (set *IpCidrSet) AddIpCidrForString(ipCidr string) error {\n\tprefix, err := netip.ParsePrefix(ipCidr)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn set.AddIpCidr(prefix)\n}\n\nfunc (set *IpCidrSet) AddIpCidr(ipCidr netip.Prefix) (err error) {\n\tif r := netipx.RangeOfPrefix(ipCidr); r.IsValid() {\n\t\tset.rr = append(set.rr, r)\n\t} else {\n\t\terr = fmt.Errorf(\"not valid ipcidr range: %s\", ipCidr)\n\t}\n\treturn\n}\n\nfunc (set *IpCidrSet) IsContainForString(ipString string) bool {\n\tip, err := netip.ParseAddr(ipString)\n\tif err != nil {\n\t\treturn false\n\t}\n\treturn set.IsContain(ip)\n}\n\nfunc (set *IpCidrSet) IsContain(ip netip.Addr) bool {\n\treturn set.ToIPSet().Contains(ip.WithZone(\"\"))\n}\n\n// MatchIp implements C.IpMatcher\nfunc (set *IpCidrSet) MatchIp(ip netip.Addr) bool {\n\tif set.IsEmpty() {\n\t\treturn false\n\t}\n\treturn set.IsContain(ip)\n}\n\nfunc (set *IpCidrSet) Merge() error {\n\tvar b netipx.IPSetBuilder\n\tb.AddSet(set.ToIPSet())\n\ti, err := b.IPSet()\n\tif err != nil {\n\t\treturn err\n\t}\n\tset.fromIPSet(i)\n\treturn nil\n}\n\nfunc (set *IpCidrSet) IsEmpty() bool {\n\treturn set == nil || len(set.rr) == 0\n}\n\nfunc (set *IpCidrSet) Foreach(f func(prefix netip.Prefix) bool) {\n\tfor _, r := range set.rr {\n\t\tfor _, prefix := range r.Prefixes() {\n\t\t\tif !f(prefix) {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n\n// ToIPSet not safe convert to *netipx.IPSet\n// be careful, must be used after Merge\nfunc (set *IpCidrSet) ToIPSet() *netipx.IPSet {\n\treturn (*netipx.IPSet)(unsafe.Pointer(set))\n}\n\nfunc (set *IpCidrSet) fromIPSet(i *netipx.IPSet) {\n\t*set = *(*IpCidrSet)(unsafe.Pointer(i))\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/cidr/ipcidr_set_bin.go",
    "content": "package cidr\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"io\"\n\t\"net/netip\"\n\n\t\"go4.org/netipx\"\n)\n\nfunc (ss *IpCidrSet) WriteBin(w io.Writer) (err error) {\n\t// version\n\t_, err = w.Write([]byte{1})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// rr\n\terr = binary.Write(w, binary.BigEndian, int64(len(ss.rr)))\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, r := range ss.rr {\n\t\terr = binary.Write(w, binary.BigEndian, r.From().As16())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\terr = binary.Write(w, binary.BigEndian, r.To().As16())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc ReadIpCidrSet(r io.Reader) (ss *IpCidrSet, err error) {\n\t// version\n\tversion := make([]byte, 1)\n\t_, err = io.ReadFull(r, version)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif version[0] != 1 {\n\t\treturn nil, errors.New(\"version is invalid\")\n\t}\n\n\tss = NewIpCidrSet()\n\tvar length int64\n\n\t// rr\n\terr = binary.Read(r, binary.BigEndian, &length)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif length < 1 {\n\t\treturn nil, errors.New(\"length is invalid\")\n\t}\n\tss.rr = make([]netipx.IPRange, length)\n\tfor i := int64(0); i < length; i++ {\n\t\tvar a16 [16]byte\n\t\terr = binary.Read(r, binary.BigEndian, &a16)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfrom := netip.AddrFrom16(a16).Unmap()\n\t\terr = binary.Read(r, binary.BigEndian, &a16)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tto := netip.AddrFrom16(a16).Unmap()\n\t\tss.rr[i] = netipx.IPRangeFrom(from, to)\n\t}\n\n\treturn ss, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/cidr/ipcidr_set_test.go",
    "content": "package cidr\n\nimport (\n\t\"testing\"\n)\n\nfunc TestIpv4(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\tipCidr   string\n\t\tip       string\n\t\texpected bool\n\t}{\n\t\t{\n\t\t\tname:     \"Test Case 1\",\n\t\t\tipCidr:   \"149.154.160.0/20\",\n\t\t\tip:       \"149.154.160.0\",\n\t\t\texpected: true,\n\t\t},\n\t\t{\n\t\t\tname:     \"Test Case 2\",\n\t\t\tipCidr:   \"192.168.0.0/16\",\n\t\t\tip:       \"10.0.0.1\",\n\t\t\texpected: false,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tset := &IpCidrSet{}\n\t\t\tset.AddIpCidrForString(test.ipCidr)\n\n\t\t\tresult := set.IsContainForString(test.ip)\n\t\t\tif result != test.expected {\n\t\t\t\tt.Errorf(\"Expected result: %v, got: %v\", test.expected, result)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestIpv6(t *testing.T) {\n\ttests := []struct {\n\t\tname     string\n\t\tipCidr   string\n\t\tip       string\n\t\texpected bool\n\t}{\n\t\t{\n\t\t\tname:     \"Test Case 1\",\n\t\t\tipCidr:   \"2409:8000::/20\",\n\t\t\tip:       \"2409:8087:1e03:21::27\",\n\t\t\texpected: true,\n\t\t},\n\t\t{\n\t\t\tname:     \"Test Case 2\",\n\t\t\tipCidr:   \"240e::/16\",\n\t\t\tip:       \"240e:964:ea02:100:1800::71\",\n\t\t\texpected: true,\n\t\t},\n\t}\n\t// Add more test cases as needed\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tset := &IpCidrSet{}\n\t\t\tset.AddIpCidrForString(test.ipCidr)\n\n\t\t\tresult := set.IsContainForString(test.ip)\n\t\t\tif result != test.expected {\n\t\t\t\tt.Errorf(\"Expected result: %v, got: %v\", test.expected, result)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestMerge(t *testing.T) {\n\ttests := []struct {\n\t\tname        string\n\t\tipCidr1     string\n\t\tipCidr2     string\n\t\tipCidr3     string\n\t\texpectedLen int\n\t}{\n\t\t{\n\t\t\tname:        \"Test Case 1\",\n\t\t\tipCidr1:     \"2409:8000::/20\",\n\t\t\tipCidr2:     \"2409:8000::/21\",\n\t\t\tipCidr3:     \"2409:8000::/48\",\n\t\t\texpectedLen: 1,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\tt.Run(test.name, func(t *testing.T) {\n\t\t\tset := &IpCidrSet{}\n\t\t\tset.AddIpCidrForString(test.ipCidr1)\n\t\t\tset.AddIpCidrForString(test.ipCidr2)\n\t\t\tset.Merge()\n\n\t\t\trangesLen := len(set.rr)\n\n\t\t\tif rangesLen != test.expectedLen {\n\t\t\t\tt.Errorf(\"Expected len: %v, got: %v\", test.expectedLen, rangesLen)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/dhcp/conn.go",
    "content": "package dhcp\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"net/netip\"\n\t\"runtime\"\n\n\t\"github.com/metacubex/mihomo/component/dialer\"\n)\n\nfunc ListenDHCPClient(ctx context.Context, ifaceName string) (net.PacketConn, error) {\n\tlistenAddr := \"0.0.0.0:68\"\n\tif runtime.GOOS == \"linux\" || runtime.GOOS == \"android\" {\n\t\tlistenAddr = \"255.255.255.255:68\"\n\t}\n\n\toptions := []dialer.Option{\n\t\tdialer.WithInterface(ifaceName),\n\t\tdialer.WithAddrReuse(true),\n\t}\n\n\t// fallback bind on windows, because syscall bind can not receive broadcast\n\tif runtime.GOOS == \"windows\" {\n\t\toptions = append(options, dialer.WithFallbackBind(true))\n\t}\n\n\treturn dialer.ListenPacket(ctx, \"udp4\", listenAddr, netip.AddrPortFrom(netip.AddrFrom4([4]byte{255, 255, 255, 255}), 67), options...)\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/dhcp/dhcp.go",
    "content": "package dhcp\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net\"\n\t\"net/netip\"\n\n\t\"github.com/metacubex/mihomo/component/iface\"\n\n\t\"github.com/insomniacslk/dhcp/dhcpv4\"\n)\n\nvar (\n\tErrNotResponding = errors.New(\"DHCP not responding\")\n\tErrNotFound      = errors.New(\"DNS option not found\")\n)\n\nfunc ResolveDNSFromDHCP(context context.Context, ifaceName string) ([]netip.Addr, error) {\n\tconn, err := ListenDHCPClient(context, ifaceName)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer func() {\n\t\t_ = conn.Close()\n\t}()\n\n\tresult := make(chan []netip.Addr, 1)\n\n\tifaceObj, err := iface.ResolveInterface(ifaceName)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdiscovery, err := dhcpv4.NewDiscovery(ifaceObj.HardwareAddr, dhcpv4.WithBroadcast(true), dhcpv4.WithRequestedOptions(dhcpv4.OptionDomainNameServer))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tgo receiveOffer(conn, discovery.TransactionID, result)\n\n\t_, err = conn.WriteTo(discovery.ToBytes(), &net.UDPAddr{IP: net.IPv4bcast, Port: 67})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tselect {\n\tcase r, ok := <-result:\n\t\tif !ok {\n\t\t\treturn nil, ErrNotFound\n\t\t}\n\t\treturn r, nil\n\tcase <-context.Done():\n\t\treturn nil, ErrNotResponding\n\t}\n}\n\nfunc receiveOffer(conn net.PacketConn, id dhcpv4.TransactionID, result chan<- []netip.Addr) {\n\tdefer close(result)\n\n\tbuf := make([]byte, dhcpv4.MaxMessageSize)\n\n\tfor {\n\t\tn, _, err := conn.ReadFrom(buf)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tpkt, err := dhcpv4.FromBytes(buf[:n])\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif pkt.MessageType() != dhcpv4.MessageTypeOffer {\n\t\t\tcontinue\n\t\t}\n\n\t\tif pkt.TransactionID != id {\n\t\t\tcontinue\n\t\t}\n\n\t\tdns := pkt.DNS()\n\t\tl := len(dns)\n\t\tif l == 0 {\n\t\t\treturn\n\t\t}\n\n\t\tresults := make([]netip.Addr, 0, len(dns))\n\t\tfor _, ip := range dns {\n\t\t\tif addr, ok := netip.AddrFromSlice(ip); ok {\n\t\t\t\tresults = append(results, addr.Unmap())\n\t\t\t}\n\t\t}\n\n\t\tresult <- results\n\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/dialer/bind.go",
    "content": "package dialer\n\nimport (\n\t\"net\"\n\t\"net/netip\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/component/iface\"\n)\n\nfunc LookupLocalAddrFromIfaceName(ifaceName string, network string, destination netip.Addr, port int) (net.Addr, error) {\n\tifaceObj, err := iface.ResolveInterface(ifaceName)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdestination = destination.Unmap()\n\n\tvar addr netip.Prefix\n\tswitch network {\n\tcase \"udp4\", \"tcp4\":\n\t\taddr, err = ifaceObj.PickIPv4Addr(destination)\n\tcase \"tcp6\", \"udp6\":\n\t\taddr, err = ifaceObj.PickIPv6Addr(destination)\n\tdefault:\n\t\tif destination.IsValid() {\n\t\t\tif destination.Is4() {\n\t\t\t\taddr, err = ifaceObj.PickIPv4Addr(destination)\n\t\t\t} else {\n\t\t\t\taddr, err = ifaceObj.PickIPv6Addr(destination)\n\t\t\t}\n\t\t} else {\n\t\t\taddr, err = ifaceObj.PickIPv4Addr(destination)\n\t\t}\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif strings.HasPrefix(network, \"tcp\") {\n\t\treturn &net.TCPAddr{\n\t\t\tIP:   addr.Addr().AsSlice(),\n\t\t\tPort: port,\n\t\t}, nil\n\t} else if strings.HasPrefix(network, \"udp\") {\n\t\treturn &net.UDPAddr{\n\t\t\tIP:   addr.Addr().AsSlice(),\n\t\t\tPort: port,\n\t\t}, nil\n\t}\n\n\treturn nil, iface.ErrAddrNotFound\n}\n\nfunc fallbackBindIfaceToDialer(ifaceName string, dialer *net.Dialer, network string, destination netip.Addr) error {\n\tif !destination.IsGlobalUnicast() {\n\t\treturn nil\n\t}\n\n\tlocal := uint64(0)\n\tif dialer.LocalAddr != nil {\n\t\t_, port, err := net.SplitHostPort(dialer.LocalAddr.String())\n\t\tif err == nil {\n\t\t\tlocal, _ = strconv.ParseUint(port, 10, 16)\n\t\t}\n\t}\n\n\taddr, err := LookupLocalAddrFromIfaceName(ifaceName, network, destination, int(local))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdialer.LocalAddr = addr\n\n\treturn nil\n}\n\nfunc fallbackBindIfaceToListenConfig(ifaceName string, _ *net.ListenConfig, network, address string, rAddrPort netip.AddrPort) (string, error) {\n\t_, port, err := net.SplitHostPort(address)\n\tif err != nil {\n\t\tport = \"0\"\n\t}\n\n\tlocal, _ := strconv.ParseUint(port, 10, 16)\n\n\taddr, err := LookupLocalAddrFromIfaceName(ifaceName, network, netip.Addr{}, int(local))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\treturn addr.String(), nil\n}\n\nfunc fallbackParseNetwork(network string, addr netip.Addr) string {\n\t// fix fallbackBindIfaceToListenConfig() force bind to an ipv4 address\n\tif !strings.HasSuffix(network, \"4\") &&\n\t\t!strings.HasSuffix(network, \"6\") &&\n\t\taddr.Unmap().Is6() {\n\t\tnetwork += \"6\"\n\t}\n\treturn network\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/dialer/bind_darwin.go",
    "content": "package dialer\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"net/netip\"\n\t\"syscall\"\n\n\t\"github.com/metacubex/mihomo/component/iface\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nfunc bindControl(ifaceIdx int) controlFn {\n\treturn func(ctx context.Context, network, address string, c syscall.RawConn) (err error) {\n\t\taddrPort, err := netip.ParseAddrPort(address)\n\t\tif err == nil && !addrPort.Addr().IsGlobalUnicast() {\n\t\t\treturn\n\t\t}\n\n\t\tvar innerErr error\n\t\terr = c.Control(func(fd uintptr) {\n\t\t\tswitch network {\n\t\t\tcase \"tcp6\", \"udp6\", \"ip6\":\n\t\t\t\tinnerErr = unix.SetsockoptInt(int(fd), unix.IPPROTO_IPV6, unix.IPV6_BOUND_IF, ifaceIdx)\n\t\t\tdefault:\n\t\t\t\tinnerErr = unix.SetsockoptInt(int(fd), unix.IPPROTO_IP, unix.IP_BOUND_IF, ifaceIdx)\n\t\t\t}\n\t\t})\n\n\t\tif innerErr != nil {\n\t\t\terr = innerErr\n\t\t}\n\n\t\treturn\n\t}\n}\n\nfunc bindIfaceToDialer(ifaceName string, dialer *net.Dialer, _ string, _ netip.Addr) error {\n\tifaceObj, err := iface.ResolveInterface(ifaceName)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\taddControlToDialer(dialer, bindControl(ifaceObj.Index))\n\treturn nil\n}\n\nfunc bindIfaceToListenConfig(ifaceName string, lc *net.ListenConfig, _, address string, rAddrPort netip.AddrPort) (string, error) {\n\tifaceObj, err := iface.ResolveInterface(ifaceName)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\taddControlToListenConfig(lc, bindControl(ifaceObj.Index))\n\treturn address, nil\n}\n\nfunc ParseNetwork(network string, addr netip.Addr) string {\n\treturn network\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/dialer/bind_linux.go",
    "content": "package dialer\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"net/netip\"\n\t\"syscall\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nfunc bindControl(ifaceName string) controlFn {\n\treturn func(ctx context.Context, network, address string, c syscall.RawConn) (err error) {\n\t\taddrPort, err := netip.ParseAddrPort(address)\n\t\tif err == nil && !addrPort.Addr().IsGlobalUnicast() {\n\t\t\treturn\n\t\t}\n\n\t\tvar innerErr error\n\t\terr = c.Control(func(fd uintptr) {\n\t\t\tinnerErr = unix.BindToDevice(int(fd), ifaceName)\n\t\t})\n\n\t\tif innerErr != nil {\n\t\t\terr = innerErr\n\t\t}\n\n\t\treturn\n\t}\n}\n\nfunc bindIfaceToDialer(ifaceName string, dialer *net.Dialer, _ string, _ netip.Addr) error {\n\taddControlToDialer(dialer, bindControl(ifaceName))\n\n\treturn nil\n}\n\nfunc bindIfaceToListenConfig(ifaceName string, lc *net.ListenConfig, _, address string, rAddrPort netip.AddrPort) (string, error) {\n\taddControlToListenConfig(lc, bindControl(ifaceName))\n\n\treturn address, nil\n}\n\nfunc ParseNetwork(network string, addr netip.Addr) string {\n\treturn network\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/dialer/bind_others.go",
    "content": "//go:build !linux && !darwin && !windows\n\npackage dialer\n\nimport (\n\t\"net\"\n\t\"net/netip\"\n)\n\nfunc bindIfaceToDialer(ifaceName string, dialer *net.Dialer, network string, destination netip.Addr) error {\n\treturn fallbackBindIfaceToDialer(ifaceName, dialer, network, destination)\n}\n\nfunc bindIfaceToListenConfig(ifaceName string, lc *net.ListenConfig, network, address string, rAddrPort netip.AddrPort) (string, error) {\n\treturn fallbackBindIfaceToListenConfig(ifaceName, lc, network, address, rAddrPort)\n}\n\nfunc ParseNetwork(network string, addr netip.Addr) string {\n\treturn fallbackParseNetwork(network, addr)\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/dialer/bind_windows.go",
    "content": "package dialer\n\nimport (\n\t\"context\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/netip\"\n\t\"syscall\"\n\t\"unsafe\"\n\n\t\"github.com/metacubex/mihomo/component/iface\"\n)\n\nconst (\n\tIP_UNICAST_IF   = 31\n\tIPV6_UNICAST_IF = 31\n)\n\nfunc bind4(handle syscall.Handle, ifaceIdx int) error {\n\tvar bytes [4]byte\n\tbinary.BigEndian.PutUint32(bytes[:], uint32(ifaceIdx))\n\tidx := *(*uint32)(unsafe.Pointer(&bytes[0]))\n\terr := syscall.SetsockoptInt(handle, syscall.IPPROTO_IP, IP_UNICAST_IF, int(idx))\n\tif err != nil {\n\t\terr = fmt.Errorf(\"bind4: %w\", err)\n\t}\n\treturn err\n}\n\nfunc bind6(handle syscall.Handle, ifaceIdx int) error {\n\terr := syscall.SetsockoptInt(handle, syscall.IPPROTO_IPV6, IPV6_UNICAST_IF, ifaceIdx)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"bind6: %w\", err)\n\t}\n\treturn err\n}\n\nfunc bindControl(ifaceIdx int, rAddrPort netip.AddrPort) controlFn {\n\treturn func(ctx context.Context, network, address string, c syscall.RawConn) (err error) {\n\t\taddrPort, err := netip.ParseAddrPort(address)\n\t\tif err == nil && !addrPort.Addr().IsGlobalUnicast() {\n\t\t\treturn\n\t\t}\n\n\t\tvar innerErr error\n\t\terr = c.Control(func(fd uintptr) {\n\t\t\thandle := syscall.Handle(fd)\n\t\t\tbind6err := bind6(handle, ifaceIdx)\n\t\t\tbind4err := bind4(handle, ifaceIdx)\n\t\t\tswitch network {\n\t\t\tcase \"ip6\", \"tcp6\":\n\t\t\t\tinnerErr = bind6err\n\t\t\tcase \"ip4\", \"tcp4\", \"udp4\":\n\t\t\t\tinnerErr = bind4err\n\t\t\tcase \"udp6\":\n\t\t\t\t// golang will set network to udp6 when listenUDP on wildcard ip (eg: \":0\", \"\")\n\t\t\t\tif (!addrPort.Addr().IsValid() || addrPort.Addr().IsUnspecified()) && bind6err != nil && rAddrPort.Addr().Unmap().Is4() {\n\t\t\t\t\t// try bind ipv6, if failed, ignore. it's a workaround for windows disable interface ipv6\n\t\t\t\t\tif bind4err != nil {\n\t\t\t\t\t\tinnerErr = fmt.Errorf(\"%w (%s)\", bind6err, bind4err)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tinnerErr = nil\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tinnerErr = bind6err\n\t\t\t\t}\n\t\t\t}\n\t\t})\n\n\t\tif innerErr != nil {\n\t\t\terr = innerErr\n\t\t}\n\n\t\treturn\n\t}\n}\n\nfunc bindIfaceToDialer(ifaceName string, dialer *net.Dialer, _ string, destination netip.Addr) error {\n\tifaceObj, err := iface.ResolveInterface(ifaceName)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\taddControlToDialer(dialer, bindControl(ifaceObj.Index, netip.AddrPortFrom(destination, 0)))\n\treturn nil\n}\n\nfunc bindIfaceToListenConfig(ifaceName string, lc *net.ListenConfig, _, address string, rAddrPort netip.AddrPort) (string, error) {\n\tifaceObj, err := iface.ResolveInterface(ifaceName)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\taddControlToListenConfig(lc, bindControl(ifaceObj.Index, rAddrPort))\n\treturn address, nil\n}\n\nfunc ParseNetwork(network string, addr netip.Addr) string {\n\treturn network\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/dialer/control.go",
    "content": "package dialer\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"syscall\"\n)\n\ntype controlFn = func(ctx context.Context, network, address string, c syscall.RawConn) error\n\nfunc addControlToListenConfig(lc *net.ListenConfig, fn controlFn) {\n\tllc := *lc\n\tlc.Control = func(network, address string, c syscall.RawConn) (err error) {\n\t\tswitch {\n\t\tcase llc.Control != nil:\n\t\t\tif err = llc.Control(network, address, c); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\treturn fn(context.Background(), network, address, c)\n\t}\n}\n\nfunc addControlToDialer(d *net.Dialer, fn controlFn) {\n\tld := *d\n\td.ControlContext = func(ctx context.Context, network, address string, c syscall.RawConn) (err error) {\n\t\tswitch {\n\t\tcase ld.ControlContext != nil:\n\t\t\tif err = ld.ControlContext(ctx, network, address, c); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\tcase ld.Control != nil:\n\t\t\tif err = ld.Control(network, address, c); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\treturn fn(ctx, network, address, c)\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/dialer/dialer.go",
    "content": "package dialer\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/netip\"\n\t\"os\"\n\t\"strings\"\n\t\"sync\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/atomic\"\n\t\"github.com/metacubex/mihomo/component/keepalive\"\n\t\"github.com/metacubex/mihomo/component/mptcp\"\n\t\"github.com/metacubex/mihomo/component/resolver\"\n)\n\nconst (\n\tDefaultTCPTimeout = 5 * time.Second\n\tDefaultUDPTimeout = DefaultTCPTimeout\n\n\tdualStackFallbackTimeout = 300 * time.Millisecond\n)\n\nvar (\n\ttcpConcurrent = atomic.NewBool(false)\n)\n\nfunc SetTcpConcurrent(concurrent bool) {\n\ttcpConcurrent.Store(concurrent)\n}\n\nfunc GetTcpConcurrent() bool {\n\treturn tcpConcurrent.Load()\n}\n\nfunc DialContext(ctx context.Context, network, address string, options ...Option) (net.Conn, error) {\n\topt := applyOptions(options...)\n\n\tif opt.network == 4 || opt.network == 6 {\n\t\tif strings.Contains(network, \"tcp\") {\n\t\t\tnetwork = \"tcp\"\n\t\t} else {\n\t\t\tnetwork = \"udp\"\n\t\t}\n\n\t\tnetwork = fmt.Sprintf(\"%s%d\", network, opt.network)\n\t}\n\n\tips, port, err := parseAddr(ctx, network, address, opt.resolver)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttcpConcurrent := GetTcpConcurrent()\n\n\tswitch network {\n\tcase \"tcp4\", \"tcp6\", \"udp4\", \"udp6\":\n\t\tif tcpConcurrent {\n\t\t\treturn parallelDialContext(ctx, network, ips, port, opt)\n\t\t}\n\t\treturn serialDialContext(ctx, network, ips, port, opt)\n\tcase \"tcp\", \"udp\":\n\t\tif tcpConcurrent {\n\t\t\tif opt.prefer != 4 && opt.prefer != 6 {\n\t\t\t\treturn parallelDialContext(ctx, network, ips, port, opt)\n\t\t\t}\n\t\t\treturn dualStackDialContext(ctx, parallelDialContext, network, ips, port, opt)\n\t\t}\n\t\treturn dualStackDialContext(ctx, serialDialContext, network, ips, port, opt)\n\tdefault:\n\t\treturn nil, ErrorInvalidedNetworkStack\n\t}\n}\n\nfunc ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort, options ...Option) (net.PacketConn, error) {\n\topt := applyOptions(options...)\n\n\tlc := &net.ListenConfig{}\n\tif opt.addrReuse {\n\t\taddrReuseToListenConfig(lc)\n\t}\n\tif DefaultSocketHook != nil { // ignore interfaceName, routingMark when DefaultSocketHook not null (in CMFA)\n\t\tsocketHookToListenConfig(lc)\n\t} else {\n\t\tif opt.interfaceName == \"\" {\n\t\t\topt.interfaceName = DefaultInterface.Load()\n\t\t}\n\t\tif opt.interfaceName == \"\" {\n\t\t\tif finder := DefaultInterfaceFinder.Load(); finder != nil {\n\t\t\t\topt.interfaceName = finder.FindInterfaceName(rAddrPort.Addr().Unmap())\n\t\t\t}\n\t\t}\n\t\tif rAddrPort.Addr().Unmap().IsLoopback() {\n\t\t\t// avoid \"The requested address is not valid in its context.\"\n\t\t\topt.interfaceName = \"\"\n\t\t}\n\t\tif opt.interfaceName != \"\" {\n\t\t\tbind := bindIfaceToListenConfig\n\t\t\tif opt.fallbackBind {\n\t\t\t\tbind = fallbackBindIfaceToListenConfig\n\t\t\t}\n\t\t\taddr, err := bind(opt.interfaceName, lc, network, address, rAddrPort)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\taddress = addr\n\t\t}\n\t\tif opt.routingMark == 0 {\n\t\t\topt.routingMark = int(DefaultRoutingMark.Load())\n\t\t}\n\t\tif opt.routingMark != 0 {\n\t\t\tbindMarkToListenConfig(opt.routingMark, lc, network, address)\n\t\t}\n\t}\n\n\treturn lc.ListenPacket(ctx, network, address)\n}\n\nfunc dialContext(ctx context.Context, network string, destination netip.Addr, port string, opt option) (net.Conn, error) {\n\tvar address string\n\tdestination, port = resolver.LookupIP4P(destination, port)\n\taddress = net.JoinHostPort(destination.String(), port)\n\n\tnetDialer := opt.netDialer\n\tswitch netDialer.(type) {\n\tcase nil:\n\t\tnetDialer = &net.Dialer{}\n\tcase *net.Dialer:\n\t\t_netDialer := *netDialer.(*net.Dialer)\n\t\tnetDialer = &_netDialer // make a copy\n\tdefault:\n\t\treturn netDialer.DialContext(ctx, network, address)\n\t}\n\n\tdialer := netDialer.(*net.Dialer)\n\tkeepalive.SetNetDialer(dialer)\n\tmptcp.SetNetDialer(dialer, opt.mpTcp)\n\n\tif DefaultSocketHook != nil { // ignore interfaceName, routingMark and tfo when DefaultSocketHook not null (in CMFA)\n\t\tsocketHookToToDialer(dialer)\n\t} else {\n\t\tif opt.interfaceName == \"\" {\n\t\t\topt.interfaceName = DefaultInterface.Load()\n\t\t}\n\t\tif opt.interfaceName == \"\" {\n\t\t\tif finder := DefaultInterfaceFinder.Load(); finder != nil {\n\t\t\t\topt.interfaceName = finder.FindInterfaceName(destination)\n\t\t\t}\n\t\t}\n\t\tif opt.interfaceName != \"\" {\n\t\t\tbind := bindIfaceToDialer\n\t\t\tif opt.fallbackBind {\n\t\t\t\tbind = fallbackBindIfaceToDialer\n\t\t\t}\n\t\t\tif err := bind(opt.interfaceName, dialer, network, destination); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\tif opt.routingMark == 0 {\n\t\t\topt.routingMark = int(DefaultRoutingMark.Load())\n\t\t}\n\t\tif opt.routingMark != 0 {\n\t\t\tbindMarkToDialer(opt.routingMark, dialer, network, destination)\n\t\t}\n\t\tif opt.tfo && !DisableTFO {\n\t\t\treturn dialTFO(ctx, *dialer, network, address)\n\t\t}\n\t}\n\n\treturn dialer.DialContext(ctx, network, address)\n}\n\nfunc ICMPControl(destination netip.Addr) func(network, address string, conn syscall.RawConn) error {\n\treturn func(network, address string, conn syscall.RawConn) error {\n\t\tif DefaultSocketHook != nil {\n\t\t\treturn DefaultSocketHook(network, address, conn)\n\t\t}\n\t\tdialer := &net.Dialer{}\n\t\tinterfaceName := DefaultInterface.Load()\n\t\tif interfaceName == \"\" {\n\t\t\tif finder := DefaultInterfaceFinder.Load(); finder != nil {\n\t\t\t\tinterfaceName = finder.FindInterfaceName(destination)\n\t\t\t}\n\t\t}\n\t\tif interfaceName != \"\" {\n\t\t\tif err := bindIfaceToDialer(interfaceName, dialer, network, destination); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\troutingMark := int(DefaultRoutingMark.Load())\n\t\tif routingMark != 0 {\n\t\t\tbindMarkToDialer(routingMark, dialer, network, destination)\n\t\t}\n\t\tif dialer.ControlContext != nil {\n\t\t\treturn dialer.ControlContext(context.TODO(), network, address, conn)\n\t\t}\n\t\treturn nil\n\t}\n}\n\ntype dialFunc func(ctx context.Context, network string, ips []netip.Addr, port string, opt option) (net.Conn, error)\n\nfunc dualStackDialContext(ctx context.Context, dialFn dialFunc, network string, ips []netip.Addr, port string, opt option) (net.Conn, error) {\n\tipv4s, ipv6s := resolver.SortationAddr(ips)\n\tif len(ipv4s) == 0 && len(ipv6s) == 0 {\n\t\treturn nil, ErrorNoIpAddress\n\t}\n\tif len(ipv4s) == 0 && len(ipv6s) != 0 {\n\t\treturn dialFn(ctx, network, ipv6s, port, opt)\n\t}\n\tif len(ipv4s) != 0 && len(ipv6s) == 0 {\n\t\treturn dialFn(ctx, network, ipv4s, port, opt)\n\t}\n\n\tpreferIPVersion := opt.prefer\n\tfallbackTicker := time.NewTicker(dualStackFallbackTimeout)\n\tdefer fallbackTicker.Stop()\n\n\tresults := make(chan dialResult)\n\treturned := make(chan struct{})\n\tdefer close(returned)\n\n\tvar wg sync.WaitGroup\n\n\tracer := func(ips []netip.Addr, isPrimary bool) {\n\t\tdefer wg.Done()\n\t\tresult := dialResult{isPrimary: isPrimary}\n\t\tdefer func() {\n\t\t\tselect {\n\t\t\tcase results <- result:\n\t\t\tcase <-returned:\n\t\t\t\tif result.Conn != nil && result.error == nil {\n\t\t\t\t\t_ = result.Conn.Close()\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\t\tresult.Conn, result.error = dialFn(ctx, network, ips, port, opt)\n\t}\n\n\tif len(ipv4s) != 0 {\n\t\twg.Add(1)\n\t\tgo racer(ipv4s, preferIPVersion != 6)\n\t}\n\n\tif len(ipv6s) != 0 {\n\t\twg.Add(1)\n\t\tgo racer(ipv6s, preferIPVersion != 4)\n\t}\n\n\tgo func() {\n\t\twg.Wait()\n\t\tclose(results)\n\t}()\n\n\tvar fallback dialResult\n\tvar errs []error\n\nloop:\n\tfor {\n\t\tselect {\n\t\tcase <-fallbackTicker.C:\n\t\t\tif fallback.error == nil && fallback.Conn != nil {\n\t\t\t\treturn fallback.Conn, nil\n\t\t\t}\n\t\tcase res, ok := <-results:\n\t\t\tif !ok {\n\t\t\t\tbreak loop\n\t\t\t}\n\t\t\tif res.error == nil {\n\t\t\t\tif res.isPrimary {\n\t\t\t\t\treturn res.Conn, nil\n\t\t\t\t}\n\t\t\t\tfallback = res\n\t\t\t} else {\n\t\t\t\tif res.isPrimary {\n\t\t\t\t\terrs = append([]error{fmt.Errorf(\"connect failed: %w\", res.error)}, errs...)\n\t\t\t\t} else {\n\t\t\t\t\terrs = append(errs, fmt.Errorf(\"connect failed: %w\", res.error))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif fallback.error == nil && fallback.Conn != nil {\n\t\treturn fallback.Conn, nil\n\t}\n\treturn nil, errors.Join(errs...)\n}\n\nfunc parallelDialContext(ctx context.Context, network string, ips []netip.Addr, port string, opt option) (net.Conn, error) {\n\tif len(ips) == 0 {\n\t\treturn nil, ErrorNoIpAddress\n\t}\n\tif len(ips) == 1 {\n\t\treturn dialContext(ctx, network, ips[0], port, opt)\n\t}\n\tresults := make(chan dialResult)\n\treturned := make(chan struct{})\n\tdefer close(returned)\n\tracer := func(ctx context.Context, ip netip.Addr) {\n\t\tresult := dialResult{isPrimary: true, ip: ip}\n\t\tdefer func() {\n\t\t\tselect {\n\t\t\tcase results <- result:\n\t\t\tcase <-returned:\n\t\t\t\tif result.Conn != nil && result.error == nil {\n\t\t\t\t\t_ = result.Conn.Close()\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\t\tresult.Conn, result.error = dialContext(ctx, network, ip, port, opt)\n\t}\n\n\tfor _, ip := range ips {\n\t\tgo racer(ctx, ip)\n\t}\n\tvar errs []error\n\tfor i := 0; i < len(ips); i++ {\n\t\tres := <-results\n\t\tif res.error == nil {\n\t\t\treturn res.Conn, nil\n\t\t}\n\t\terrs = append(errs, res.error)\n\t}\n\n\tif len(errs) > 0 {\n\t\treturn nil, errors.Join(errs...)\n\t}\n\treturn nil, os.ErrDeadlineExceeded\n}\n\nfunc serialDialContext(ctx context.Context, network string, ips []netip.Addr, port string, opt option) (net.Conn, error) {\n\tif len(ips) == 0 {\n\t\treturn nil, ErrorNoIpAddress\n\t}\n\tvar errs []error\n\tfor _, ip := range ips {\n\t\tif conn, err := dialContext(ctx, network, ip, port, opt); err == nil {\n\t\t\treturn conn, nil\n\t\t} else {\n\t\t\terrs = append(errs, err)\n\t\t}\n\t}\n\treturn nil, errors.Join(errs...)\n}\n\ntype dialResult struct {\n\tip netip.Addr\n\tnet.Conn\n\terror\n\tisPrimary bool\n}\n\nfunc parseAddr(ctx context.Context, network, address string, preferResolver resolver.Resolver) ([]netip.Addr, string, error) {\n\thost, port, err := net.SplitHostPort(address)\n\tif err != nil {\n\t\treturn nil, \"-1\", err\n\t}\n\n\tif preferResolver == nil {\n\t\tpreferResolver = resolver.ProxyServerHostResolver\n\t}\n\n\tvar ips []netip.Addr\n\tswitch network {\n\tcase \"tcp4\", \"udp4\":\n\t\tips, err = resolver.LookupIPv4WithResolver(ctx, host, preferResolver)\n\tcase \"tcp6\", \"udp6\":\n\t\tips, err = resolver.LookupIPv6WithResolver(ctx, host, preferResolver)\n\tdefault:\n\t\tips, err = resolver.LookupIPWithResolver(ctx, host, preferResolver)\n\t}\n\tif err != nil {\n\t\treturn nil, \"-1\", fmt.Errorf(\"dns resolve failed: %w\", err)\n\t}\n\tfor i, ip := range ips {\n\t\tif ip.Is4In6() {\n\t\t\tips[i] = ip.Unmap()\n\t\t}\n\t}\n\treturn ips, port, nil\n}\n\ntype Dialer struct {\n\tOpt option\n}\n\nfunc (d Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {\n\treturn DialContext(ctx, network, address, WithOption(d.Opt))\n}\n\nfunc (d Dialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) {\n\treturn ListenPacket(ctx, ParseNetwork(network, rAddrPort.Addr()), address, rAddrPort, WithOption(d.Opt))\n}\n\nfunc NewDialer(options ...Option) Dialer {\n\topt := applyOptions(options...)\n\treturn Dialer{Opt: opt}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/dialer/error.go",
    "content": "package dialer\n\nimport (\n\t\"errors\"\n)\n\nvar (\n\tErrorNoIpAddress           = errors.New(\"no ip address\")\n\tErrorInvalidedNetworkStack = errors.New(\"invalided network stack\")\n)\n"
  },
  {
    "path": "core/Clash.Meta/component/dialer/mark_linux.go",
    "content": "//go:build linux\n\npackage dialer\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"net/netip\"\n\t\"syscall\"\n)\n\nfunc bindMarkToDialer(mark int, dialer *net.Dialer, _ string, _ netip.Addr) {\n\taddControlToDialer(dialer, bindMarkToControl(mark))\n}\n\nfunc bindMarkToListenConfig(mark int, lc *net.ListenConfig, _, _ string) {\n\taddControlToListenConfig(lc, bindMarkToControl(mark))\n}\n\nfunc bindMarkToControl(mark int) controlFn {\n\treturn func(ctx context.Context, network, address string, c syscall.RawConn) (err error) {\n\n\t\taddrPort, err := netip.ParseAddrPort(address)\n\t\tif err == nil && !addrPort.Addr().IsGlobalUnicast() {\n\t\t\treturn\n\t\t}\n\n\t\tvar innerErr error\n\t\terr = c.Control(func(fd uintptr) {\n\t\t\tinnerErr = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_MARK, mark)\n\t\t})\n\t\tif innerErr != nil {\n\t\t\terr = innerErr\n\t\t}\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/dialer/mark_nonlinux.go",
    "content": "//go:build !linux\n\npackage dialer\n\nimport (\n\t\"net\"\n\t\"net/netip\"\n\t\"sync\"\n\n\t\"github.com/metacubex/mihomo/log\"\n)\n\nvar printMarkWarnOnce sync.Once\n\nfunc printMarkWarn() {\n\tprintMarkWarnOnce.Do(func() {\n\t\tlog.Warnln(\"Routing mark on socket is not supported on current platform\")\n\t})\n}\n\nfunc bindMarkToDialer(mark int, dialer *net.Dialer, _ string, _ netip.Addr) {\n\tprintMarkWarn()\n}\n\nfunc bindMarkToListenConfig(mark int, lc *net.ListenConfig, _, _ string) {\n\tprintMarkWarn()\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/dialer/options.go",
    "content": "package dialer\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"net/netip\"\n\n\t\"github.com/metacubex/mihomo/common/atomic\"\n\t\"github.com/metacubex/mihomo/component/resolver\"\n)\n\nvar (\n\tDefaultInterface   = atomic.NewTypedValue[string](\"\")\n\tDefaultRoutingMark = atomic.NewInt32(0)\n\n\tDefaultInterfaceFinder = atomic.NewTypedValue[InterfaceFinder](nil)\n)\n\ntype InterfaceFinder interface {\n\tFindInterfaceName(destination netip.Addr) string\n}\n\ntype NetDialer interface {\n\tDialContext(ctx context.Context, network, address string) (net.Conn, error)\n}\n\ntype NetDialerFunc func(ctx context.Context, network, address string) (net.Conn, error)\n\nfunc (f NetDialerFunc) DialContext(ctx context.Context, network, address string) (net.Conn, error) {\n\treturn f(ctx, network, address)\n}\n\ntype option struct {\n\tinterfaceName string\n\tfallbackBind  bool\n\taddrReuse     bool\n\troutingMark   int\n\tnetwork       int\n\tprefer        int\n\ttfo           bool\n\tmpTcp         bool\n\tresolver      resolver.Resolver\n\tnetDialer     NetDialer\n}\n\ntype Option func(opt *option)\n\nfunc WithInterface(name string) Option {\n\treturn func(opt *option) {\n\t\topt.interfaceName = name\n\t}\n}\n\nfunc WithFallbackBind(fallback bool) Option {\n\treturn func(opt *option) {\n\t\topt.fallbackBind = fallback\n\t}\n}\n\nfunc WithAddrReuse(reuse bool) Option {\n\treturn func(opt *option) {\n\t\topt.addrReuse = reuse\n\t}\n}\n\nfunc WithRoutingMark(mark int) Option {\n\treturn func(opt *option) {\n\t\topt.routingMark = mark\n\t}\n}\n\nfunc WithResolver(r resolver.Resolver) Option {\n\treturn func(opt *option) {\n\t\topt.resolver = r\n\t}\n}\n\nfunc WithPreferIPv4() Option {\n\treturn func(opt *option) {\n\t\topt.prefer = 4\n\t}\n}\n\nfunc WithPreferIPv6() Option {\n\treturn func(opt *option) {\n\t\topt.prefer = 6\n\t}\n}\n\nfunc WithOnlySingleStack(isIPv4 bool) Option {\n\treturn func(opt *option) {\n\t\tif isIPv4 {\n\t\t\topt.network = 4\n\t\t} else {\n\t\t\topt.network = 6\n\t\t}\n\t}\n}\n\nfunc WithTFO(tfo bool) Option {\n\treturn func(opt *option) {\n\t\topt.tfo = tfo\n\t}\n}\n\nfunc WithMPTCP(mpTcp bool) Option {\n\treturn func(opt *option) {\n\t\topt.mpTcp = mpTcp\n\t}\n}\n\nfunc WithNetDialer(netDialer NetDialer) Option {\n\treturn func(opt *option) {\n\t\topt.netDialer = netDialer\n\t}\n}\n\nfunc WithOption(o option) Option {\n\treturn func(opt *option) {\n\t\t*opt = o\n\t}\n}\n\nfunc WithOptions(options ...Option) Option {\n\treturn func(opt *option) {\n\t\tfor _, o := range options {\n\t\t\to(opt)\n\t\t}\n\t}\n}\n\nfunc IsZeroOptions(opts []Option) bool {\n\treturn applyOptions(opts...) == option{}\n}\n\nfunc applyOptions(options ...Option) option {\n\topt := option{}\n\tfor _, o := range options {\n\t\to(&opt)\n\t}\n\treturn opt\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/dialer/reuse.go",
    "content": "package dialer\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"syscall\"\n\n\t\"github.com/metacubex/mihomo/common/sockopt\"\n)\n\nfunc addrReuseToListenConfig(lc *net.ListenConfig) {\n\taddControlToListenConfig(lc, func(ctx context.Context, network, address string, c syscall.RawConn) error {\n\t\treturn sockopt.RawConnReuseaddr(c)\n\t})\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/dialer/socket_hook.go",
    "content": "package dialer\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"syscall\"\n)\n\n// SocketControl\n// never change type traits because it's used in CMFA\ntype SocketControl func(network, address string, conn syscall.RawConn) error\n\n// DefaultSocketHook\n// never change type traits because it's used in CMFA\nvar DefaultSocketHook SocketControl\n\nfunc socketHookToToDialer(dialer *net.Dialer) {\n\taddControlToDialer(dialer, func(ctx context.Context, network, address string, c syscall.RawConn) error {\n\t\treturn DefaultSocketHook(network, address, c)\n\t})\n}\n\nfunc socketHookToListenConfig(lc *net.ListenConfig) {\n\taddControlToListenConfig(lc, func(ctx context.Context, network, address string, c syscall.RawConn) error {\n\t\treturn DefaultSocketHook(network, address, c)\n\t})\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/dialer/tfo.go",
    "content": "package dialer\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/metacubex/tfo-go\"\n)\n\nvar DisableTFO = false\n\ntype tfoConn struct {\n\tnet.Conn\n\tclosed bool\n\tdialed chan bool\n\tcancel context.CancelFunc\n\tctx    context.Context\n\tdialFn func(ctx context.Context, earlyData []byte) (net.Conn, error)\n}\n\nfunc (c *tfoConn) Dial(earlyData []byte) (err error) {\n\tconn, err := c.dialFn(c.ctx, earlyData)\n\tif err != nil {\n\t\treturn\n\t}\n\tc.Conn = conn\n\tc.dialed <- true\n\treturn err\n}\n\nfunc (c *tfoConn) Read(b []byte) (n int, err error) {\n\tif c.closed {\n\t\treturn 0, io.ErrClosedPipe\n\t}\n\tif c.Conn == nil {\n\t\tselect {\n\t\tcase <-c.ctx.Done():\n\t\t\treturn 0, io.ErrUnexpectedEOF\n\t\tcase <-c.dialed:\n\t\t}\n\t}\n\treturn c.Conn.Read(b)\n}\n\nfunc (c *tfoConn) Write(b []byte) (n int, err error) {\n\tif c.closed {\n\t\treturn 0, io.ErrClosedPipe\n\t}\n\tif c.Conn == nil {\n\t\tif err := c.Dial(b); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\treturn len(b), nil\n\t}\n\n\treturn c.Conn.Write(b)\n}\n\nfunc (c *tfoConn) Close() error {\n\tc.closed = true\n\tc.cancel()\n\tif c.Conn == nil {\n\t\treturn nil\n\t}\n\treturn c.Conn.Close()\n}\n\nfunc (c *tfoConn) LocalAddr() net.Addr {\n\tif c.Conn == nil {\n\t\treturn &net.TCPAddr{}\n\t}\n\treturn c.Conn.LocalAddr()\n}\n\nfunc (c *tfoConn) RemoteAddr() net.Addr {\n\tif c.Conn == nil {\n\t\treturn &net.TCPAddr{}\n\t}\n\treturn c.Conn.RemoteAddr()\n}\n\nfunc (c *tfoConn) SetDeadline(t time.Time) error {\n\tif err := c.SetReadDeadline(t); err != nil {\n\t\treturn err\n\t}\n\treturn c.SetWriteDeadline(t)\n}\n\nfunc (c *tfoConn) SetReadDeadline(t time.Time) error {\n\tif c.Conn == nil {\n\t\treturn nil\n\t}\n\treturn c.Conn.SetReadDeadline(t)\n}\n\nfunc (c *tfoConn) SetWriteDeadline(t time.Time) error {\n\tif c.Conn == nil {\n\t\treturn nil\n\t}\n\treturn c.Conn.SetWriteDeadline(t)\n}\n\nfunc (c *tfoConn) Upstream() any {\n\tif c.Conn == nil { // ensure return a nil interface not an interface with nil value\n\t\treturn nil\n\t}\n\treturn c.Conn\n}\n\nfunc (c *tfoConn) NeedAdditionalReadDeadline() bool {\n\treturn c.Conn == nil\n}\n\nfunc (c *tfoConn) NeedHandshake() bool {\n\treturn c.Conn == nil\n}\n\nfunc (c *tfoConn) ReaderReplaceable() bool {\n\treturn c.Conn != nil\n}\n\nfunc (c *tfoConn) WriterReplaceable() bool {\n\treturn c.Conn != nil\n}\n\nfunc dialTFO(ctx context.Context, netDialer net.Dialer, network, address string) (net.Conn, error) {\n\tctx, cancel := context.WithTimeout(context.Background(), DefaultTCPTimeout)\n\tdialer := tfo.Dialer{Dialer: netDialer, DisableTFO: false}\n\treturn &tfoConn{\n\t\tdialed: make(chan bool, 1),\n\t\tcancel: cancel,\n\t\tctx:    ctx,\n\t\tdialFn: func(ctx context.Context, earlyData []byte) (net.Conn, error) {\n\t\t\treturn dialer.DialContext(ctx, network, address, earlyData)\n\t\t},\n\t}, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/dialer/tfo_windows.go",
    "content": "package dialer\n\nimport \"github.com/metacubex/mihomo/constant/features\"\n\nfunc init() {\n\t// According to MSDN, this option is available since Windows 10, 1607\n\t// https://msdn.microsoft.com/en-us/library/windows/desktop/ms738596(v=vs.85).aspx\n\tif features.WindowsMajorVersion < 10 || (features.WindowsMajorVersion == 10 && features.WindowsBuildNumber < 14393) {\n\t\tDisableTFO = true\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/ech/ech.go",
    "content": "package ech\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\ttlsC \"github.com/metacubex/mihomo/component/tls\"\n\t\"github.com/metacubex/tls\"\n)\n\ntype Config struct {\n\tGetEncryptedClientHelloConfigList func(ctx context.Context, serverName string) ([]byte, error)\n}\n\nfunc (cfg *Config) ClientHandle(ctx context.Context, tlsConfig *tls.Config) (err error) {\n\tif cfg == nil {\n\t\treturn nil\n\t}\n\techConfigList, err := cfg.GetEncryptedClientHelloConfigList(ctx, tlsConfig.ServerName)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"resolve ECH config error: %w\", err)\n\t}\n\n\ttlsConfig.EncryptedClientHelloConfigList = echConfigList\n\tif tlsConfig.MinVersion != 0 && tlsConfig.MinVersion < tls.VersionTLS13 {\n\t\ttlsConfig.MinVersion = tls.VersionTLS13\n\t}\n\tif tlsConfig.MaxVersion != 0 && tlsConfig.MaxVersion < tls.VersionTLS13 {\n\t\ttlsConfig.MaxVersion = tls.VersionTLS13\n\t}\n\treturn nil\n}\n\nfunc (cfg *Config) ClientHandleUTLS(ctx context.Context, tlsConfig *tlsC.Config) (err error) {\n\tif cfg == nil {\n\t\treturn nil\n\t}\n\techConfigList, err := cfg.GetEncryptedClientHelloConfigList(ctx, tlsConfig.ServerName)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"resolve ECH config error: %w\", err)\n\t}\n\n\ttlsConfig.EncryptedClientHelloConfigList = echConfigList\n\tif tlsConfig.MinVersion != 0 && tlsConfig.MinVersion < tlsC.VersionTLS13 {\n\t\ttlsConfig.MinVersion = tlsC.VersionTLS13\n\t}\n\tif tlsConfig.MaxVersion != 0 && tlsConfig.MaxVersion < tlsC.VersionTLS13 {\n\t\ttlsConfig.MaxVersion = tlsC.VersionTLS13\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/ech/echparser/echparser.go",
    "content": "package echparser\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\n\t\"golang.org/x/crypto/cryptobyte\"\n)\n\n// export from std's crypto/tls/ech.go\n\nconst extensionEncryptedClientHello = 0xfe0d\n\ntype ECHCipher struct {\n\tKDFID  uint16\n\tAEADID uint16\n}\n\ntype ECHExtension struct {\n\tType uint16\n\tData []byte\n}\n\ntype ECHConfig struct {\n\traw []byte\n\n\tVersion uint16\n\tLength  uint16\n\n\tConfigID             uint8\n\tKemID                uint16\n\tPublicKey            []byte\n\tSymmetricCipherSuite []ECHCipher\n\n\tMaxNameLength uint8\n\tPublicName    []byte\n\tExtensions    []ECHExtension\n}\n\nvar ErrMalformedECHConfigList = errors.New(\"tls: malformed ECHConfigList\")\n\ntype EchConfigErr struct {\n\tfield string\n}\n\nfunc (e *EchConfigErr) Error() string {\n\tif e.field == \"\" {\n\t\treturn \"tls: malformed ECHConfig\"\n\t}\n\treturn fmt.Sprintf(\"tls: malformed ECHConfig, invalid %s field\", e.field)\n}\n\nfunc ParseECHConfig(enc []byte) (skip bool, ec ECHConfig, err error) {\n\ts := cryptobyte.String(enc)\n\tec.raw = []byte(enc)\n\tif !s.ReadUint16(&ec.Version) {\n\t\treturn false, ECHConfig{}, &EchConfigErr{\"version\"}\n\t}\n\tif !s.ReadUint16(&ec.Length) {\n\t\treturn false, ECHConfig{}, &EchConfigErr{\"length\"}\n\t}\n\tif len(ec.raw) < int(ec.Length)+4 {\n\t\treturn false, ECHConfig{}, &EchConfigErr{\"length\"}\n\t}\n\tec.raw = ec.raw[:ec.Length+4]\n\tif ec.Version != extensionEncryptedClientHello {\n\t\ts.Skip(int(ec.Length))\n\t\treturn true, ECHConfig{}, nil\n\t}\n\tif !s.ReadUint8(&ec.ConfigID) {\n\t\treturn false, ECHConfig{}, &EchConfigErr{\"config_id\"}\n\t}\n\tif !s.ReadUint16(&ec.KemID) {\n\t\treturn false, ECHConfig{}, &EchConfigErr{\"kem_id\"}\n\t}\n\tif !s.ReadUint16LengthPrefixed((*cryptobyte.String)(&ec.PublicKey)) {\n\t\treturn false, ECHConfig{}, &EchConfigErr{\"public_key\"}\n\t}\n\tvar cipherSuites cryptobyte.String\n\tif !s.ReadUint16LengthPrefixed(&cipherSuites) {\n\t\treturn false, ECHConfig{}, &EchConfigErr{\"cipher_suites\"}\n\t}\n\tfor !cipherSuites.Empty() {\n\t\tvar c ECHCipher\n\t\tif !cipherSuites.ReadUint16(&c.KDFID) {\n\t\t\treturn false, ECHConfig{}, &EchConfigErr{\"cipher_suites kdf_id\"}\n\t\t}\n\t\tif !cipherSuites.ReadUint16(&c.AEADID) {\n\t\t\treturn false, ECHConfig{}, &EchConfigErr{\"cipher_suites aead_id\"}\n\t\t}\n\t\tec.SymmetricCipherSuite = append(ec.SymmetricCipherSuite, c)\n\t}\n\tif !s.ReadUint8(&ec.MaxNameLength) {\n\t\treturn false, ECHConfig{}, &EchConfigErr{\"maximum_name_length\"}\n\t}\n\tvar publicName cryptobyte.String\n\tif !s.ReadUint8LengthPrefixed(&publicName) {\n\t\treturn false, ECHConfig{}, &EchConfigErr{\"public_name\"}\n\t}\n\tec.PublicName = publicName\n\tvar extensions cryptobyte.String\n\tif !s.ReadUint16LengthPrefixed(&extensions) {\n\t\treturn false, ECHConfig{}, &EchConfigErr{\"extensions\"}\n\t}\n\tfor !extensions.Empty() {\n\t\tvar e ECHExtension\n\t\tif !extensions.ReadUint16(&e.Type) {\n\t\t\treturn false, ECHConfig{}, &EchConfigErr{\"extensions type\"}\n\t\t}\n\t\tif !extensions.ReadUint16LengthPrefixed((*cryptobyte.String)(&e.Data)) {\n\t\t\treturn false, ECHConfig{}, &EchConfigErr{\"extensions data\"}\n\t\t}\n\t\tec.Extensions = append(ec.Extensions, e)\n\t}\n\n\treturn false, ec, nil\n}\n\n// ParseECHConfigList parses a draft-ietf-tls-esni-18 ECHConfigList, returning a\n// slice of parsed ECHConfigs, in the same order they were parsed, or an error\n// if the list is malformed.\nfunc ParseECHConfigList(data []byte) ([]ECHConfig, error) {\n\ts := cryptobyte.String(data)\n\tvar length uint16\n\tif !s.ReadUint16(&length) {\n\t\treturn nil, ErrMalformedECHConfigList\n\t}\n\tif length != uint16(len(data)-2) {\n\t\treturn nil, ErrMalformedECHConfigList\n\t}\n\tvar configs []ECHConfig\n\tfor len(s) > 0 {\n\t\tif len(s) < 4 {\n\t\t\treturn nil, errors.New(\"tls: malformed ECHConfig\")\n\t\t}\n\t\tconfigLen := uint16(s[2])<<8 | uint16(s[3])\n\t\tskip, ec, err := ParseECHConfig(s)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ts = s[configLen+4:]\n\t\tif !skip {\n\t\t\tconfigs = append(configs, ec)\n\t\t}\n\t}\n\treturn configs, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/ech/key.go",
    "content": "package ech\n\nimport (\n\t\"crypto/ecdh\"\n\t\"crypto/rand\"\n\t\"encoding/base64\"\n\t\"encoding/pem\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"runtime\"\n\t\"sync\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\n\t\"github.com/metacubex/fswatch\"\n\t\"github.com/metacubex/tls\"\n\t\"golang.org/x/crypto/cryptobyte\"\n)\n\nconst (\n\tAEAD_AES_128_GCM      = 0x0001\n\tAEAD_AES_256_GCM      = 0x0002\n\tAEAD_ChaCha20Poly1305 = 0x0003\n)\n\nconst extensionEncryptedClientHello = 0xfe0d\nconst DHKEM_X25519_HKDF_SHA256 = 0x0020\nconst KDF_HKDF_SHA256 = 0x0001\n\n// sortedSupportedAEADs is just a sorted version of hpke.SupportedAEADS.\n// We need this so that when we insert them into ECHConfigs the ordering\n// is stable.\nvar sortedSupportedAEADs = []uint16{AEAD_AES_128_GCM, AEAD_AES_256_GCM, AEAD_ChaCha20Poly1305}\n\nfunc marshalECHConfig(id uint8, pubKey []byte, publicName string, maxNameLen uint8) []byte {\n\tbuilder := cryptobyte.NewBuilder(nil)\n\n\tbuilder.AddUint16(extensionEncryptedClientHello)\n\tbuilder.AddUint16LengthPrefixed(func(builder *cryptobyte.Builder) {\n\t\tbuilder.AddUint8(id)\n\n\t\tbuilder.AddUint16(DHKEM_X25519_HKDF_SHA256) // The only DHKEM we support\n\t\tbuilder.AddUint16LengthPrefixed(func(builder *cryptobyte.Builder) {\n\t\t\tbuilder.AddBytes(pubKey)\n\t\t})\n\t\tbuilder.AddUint16LengthPrefixed(func(builder *cryptobyte.Builder) {\n\t\t\tfor _, aeadID := range sortedSupportedAEADs {\n\t\t\t\tbuilder.AddUint16(KDF_HKDF_SHA256) // The only KDF we support\n\t\t\t\tbuilder.AddUint16(aeadID)\n\t\t\t}\n\t\t})\n\t\tbuilder.AddUint8(maxNameLen)\n\t\tbuilder.AddUint8LengthPrefixed(func(builder *cryptobyte.Builder) {\n\t\t\tbuilder.AddBytes([]byte(publicName))\n\t\t})\n\t\tbuilder.AddUint16(0) // extensions\n\t})\n\n\treturn builder.BytesOrPanic()\n}\n\nfunc GenECHConfig(publicName string) (configBase64 string, keyPem string, err error) {\n\techKey, err := ecdh.X25519().GenerateKey(rand.Reader)\n\tif err != nil {\n\t\treturn\n\t}\n\n\techConfig := marshalECHConfig(0, echKey.PublicKey().Bytes(), publicName, 0)\n\n\tbuilder := cryptobyte.NewBuilder(nil)\n\tbuilder.AddUint16LengthPrefixed(func(builder *cryptobyte.Builder) {\n\t\tbuilder.AddBytes(echConfig)\n\t})\n\techConfigList := builder.BytesOrPanic()\n\n\tbuilder2 := cryptobyte.NewBuilder(nil)\n\tbuilder2.AddUint16LengthPrefixed(func(builder *cryptobyte.Builder) {\n\t\tbuilder.AddBytes(echKey.Bytes())\n\t})\n\tbuilder2.AddUint16LengthPrefixed(func(builder *cryptobyte.Builder) {\n\t\tbuilder.AddBytes(echConfig)\n\t})\n\techConfigKeys := builder2.BytesOrPanic()\n\n\tconfigBase64 = base64.StdEncoding.EncodeToString(echConfigList)\n\tkeyPem = string(pem.EncodeToMemory(&pem.Block{Type: \"ECH KEYS\", Bytes: echConfigKeys}))\n\treturn\n}\n\nfunc UnmarshalECHKeys(raw []byte) ([]tls.EncryptedClientHelloKey, error) {\n\tvar keys []tls.EncryptedClientHelloKey\n\trawString := cryptobyte.String(raw)\n\tfor !rawString.Empty() {\n\t\tvar key tls.EncryptedClientHelloKey\n\t\tif !rawString.ReadUint16LengthPrefixed((*cryptobyte.String)(&key.PrivateKey)) {\n\t\t\treturn nil, errors.New(\"error parsing private key\")\n\t\t}\n\t\tif !rawString.ReadUint16LengthPrefixed((*cryptobyte.String)(&key.Config)) {\n\t\t\treturn nil, errors.New(\"error parsing config\")\n\t\t}\n\t\tkeys = append(keys, key)\n\t}\n\tif len(keys) == 0 {\n\t\treturn nil, errors.New(\"empty ECH keys\")\n\t}\n\treturn keys, nil\n}\n\nfunc LoadECHKey(key string, tlsConfig *tls.Config) error {\n\tif key == \"\" {\n\t\treturn nil\n\t}\n\techKeys, painTextErr := loadECHKey([]byte(key))\n\tif painTextErr == nil {\n\t\ttlsConfig.GetEncryptedClientHelloKeys = func(info *tls.ClientHelloInfo) ([]tls.EncryptedClientHelloKey, error) {\n\t\t\treturn echKeys, nil\n\t\t}\n\t\treturn nil\n\t}\n\tkey = C.Path.Resolve(key)\n\tvar loadErr error\n\tif !C.Path.IsSafePath(key) {\n\t\tloadErr = C.Path.ErrNotSafePath(key)\n\t} else {\n\t\tvar echKey []byte\n\t\techKey, loadErr = os.ReadFile(key)\n\t\tif loadErr == nil {\n\t\t\techKeys, loadErr = loadECHKey(echKey)\n\t\t}\n\t}\n\tif loadErr != nil {\n\t\treturn fmt.Errorf(\"parse ECH keys failed, maybe format error:%s, or path error: %s\", painTextErr.Error(), loadErr.Error())\n\t}\n\tgcFlag := new(os.File) // tiny (on the order of 16 bytes or less) and pointer-free objects may never run the finalizer, so we choose new an os.File\n\tupdateMutex := sync.RWMutex{}\n\tif watcher, err := fswatch.NewWatcher(fswatch.Options{Path: []string{key}, Callback: func(path string) {\n\t\tupdateMutex.Lock()\n\t\tdefer updateMutex.Unlock()\n\t\tif echKey, err := os.ReadFile(key); err == nil {\n\t\t\tif newEchKeys, err := loadECHKey(echKey); err == nil {\n\t\t\t\techKeys = newEchKeys\n\t\t\t}\n\t\t}\n\t}}); err == nil {\n\t\tif err = watcher.Start(); err == nil {\n\t\t\truntime.SetFinalizer(gcFlag, func(f *os.File) {\n\t\t\t\t_ = watcher.Close()\n\t\t\t})\n\t\t}\n\t}\n\ttlsConfig.GetEncryptedClientHelloKeys = func(info *tls.ClientHelloInfo) ([]tls.EncryptedClientHelloKey, error) {\n\t\tdefer runtime.KeepAlive(gcFlag)\n\t\tupdateMutex.RLock()\n\t\tdefer updateMutex.RUnlock()\n\t\treturn echKeys, nil\n\t}\n\treturn nil\n}\n\nfunc loadECHKey(echKey []byte) ([]tls.EncryptedClientHelloKey, error) {\n\tblock, rest := pem.Decode(echKey)\n\tif block == nil || block.Type != \"ECH KEYS\" || len(rest) > 0 {\n\t\treturn nil, errors.New(\"invalid ECH keys pem\")\n\t}\n\techKeys, err := UnmarshalECHKeys(block.Bytes)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"parse ECH keys: %w\", err)\n\t}\n\treturn echKeys, err\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/ech/key_test.go",
    "content": "package ech\n\nimport (\n\t\"encoding/base64\"\n\t\"testing\"\n\n\t\"github.com/metacubex/mihomo/component/ech/echparser\"\n)\n\nfunc TestGenECHConfig(t *testing.T) {\n\tdomain := \"www.example.com\"\n\tconfigBase64, _, err := GenECHConfig(domain)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\techConfigList, err := base64.StdEncoding.DecodeString(configBase64)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\techConfigs, err := echparser.ParseECHConfigList(echConfigList)\n\tif err != nil {\n\t\tt.Error(err)\n\t}\n\tif len(echConfigs) == 0 {\n\t\tt.Error(\"no ech config\")\n\t}\n\tif publicName := string(echConfigs[0].PublicName); publicName != domain {\n\t\tt.Error(\"ech config domain error, expect \", domain, \" got\", publicName)\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/fakeip/cachefile.go",
    "content": "package fakeip\n\nimport (\n\t\"net/netip\"\n\n\t\"github.com/metacubex/mihomo/component/profile/cachefile\"\n)\n\ntype cachefileStore struct {\n\tcache *cachefile.FakeIpStore\n}\n\n// GetByHost implements store.GetByHost\nfunc (c *cachefileStore) GetByHost(host string) (netip.Addr, bool) {\n\treturn c.cache.GetByHost(host)\n}\n\n// PutByHost implements store.PutByHost\nfunc (c *cachefileStore) PutByHost(host string, ip netip.Addr) {\n\tc.cache.PutByHost(host, ip)\n}\n\n// GetByIP implements store.GetByIP\nfunc (c *cachefileStore) GetByIP(ip netip.Addr) (string, bool) {\n\treturn c.cache.GetByIP(ip)\n}\n\n// PutByIP implements store.PutByIP\nfunc (c *cachefileStore) PutByIP(ip netip.Addr, host string) {\n\tc.cache.PutByIP(ip, host)\n}\n\n// DelByIP implements store.DelByIP\nfunc (c *cachefileStore) DelByIP(ip netip.Addr) {\n\tc.cache.DelByIP(ip)\n}\n\n// Exist implements store.Exist\nfunc (c *cachefileStore) Exist(ip netip.Addr) bool {\n\t_, exist := c.GetByIP(ip)\n\treturn exist\n}\n\n// CloneTo implements store.CloneTo\n// already persistence\nfunc (c *cachefileStore) CloneTo(store store) {}\n\n// FlushFakeIP implements store.FlushFakeIP\nfunc (c *cachefileStore) FlushFakeIP() error {\n\treturn c.cache.FlushFakeIP()\n}\n\nfunc newCachefileStore(cache *cachefile.CacheFile, prefix netip.Prefix) *cachefileStore {\n\tif prefix.Addr().Is6() {\n\t\treturn &cachefileStore{cache.FakeIpStore6()}\n\t} else {\n\t\treturn &cachefileStore{cache.FakeIpStore()}\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/fakeip/memory.go",
    "content": "package fakeip\n\nimport (\n\t\"net/netip\"\n\n\t\"github.com/metacubex/mihomo/common/lru\"\n)\n\ntype memoryStore struct {\n\tcacheIP   *lru.LruCache[string, netip.Addr]\n\tcacheHost *lru.LruCache[netip.Addr, string]\n}\n\n// GetByHost implements store.GetByHost\nfunc (m *memoryStore) GetByHost(host string) (netip.Addr, bool) {\n\tif ip, exist := m.cacheIP.Get(host); exist {\n\t\t// ensure ip --> host on head of linked list\n\t\tm.cacheHost.Get(ip)\n\t\treturn ip, true\n\t}\n\n\treturn netip.Addr{}, false\n}\n\n// PutByHost implements store.PutByHost\nfunc (m *memoryStore) PutByHost(host string, ip netip.Addr) {\n\tm.cacheIP.Set(host, ip)\n}\n\n// GetByIP implements store.GetByIP\nfunc (m *memoryStore) GetByIP(ip netip.Addr) (string, bool) {\n\tif host, exist := m.cacheHost.Get(ip); exist {\n\t\t// ensure host --> ip on head of linked list\n\t\tm.cacheIP.Get(host)\n\t\treturn host, true\n\t}\n\n\treturn \"\", false\n}\n\n// PutByIP implements store.PutByIP\nfunc (m *memoryStore) PutByIP(ip netip.Addr, host string) {\n\tm.cacheHost.Set(ip, host)\n}\n\n// DelByIP implements store.DelByIP\nfunc (m *memoryStore) DelByIP(ip netip.Addr) {\n\tif host, exist := m.cacheHost.Get(ip); exist {\n\t\tm.cacheIP.Delete(host)\n\t}\n\tm.cacheHost.Delete(ip)\n}\n\n// Exist implements store.Exist\nfunc (m *memoryStore) Exist(ip netip.Addr) bool {\n\treturn m.cacheHost.Exist(ip)\n}\n\n// CloneTo implements store.CloneTo\n// only for memoryStore to memoryStore\nfunc (m *memoryStore) CloneTo(store store) {\n\tif ms, ok := store.(*memoryStore); ok {\n\t\tm.cacheIP.CloneTo(ms.cacheIP)\n\t\tm.cacheHost.CloneTo(ms.cacheHost)\n\t}\n}\n\n// FlushFakeIP implements store.FlushFakeIP\nfunc (m *memoryStore) FlushFakeIP() error {\n\tm.cacheIP.Clear()\n\tm.cacheHost.Clear()\n\treturn nil\n}\n\nfunc newMemoryStore(size int) *memoryStore {\n\treturn &memoryStore{\n\t\tcacheIP:   lru.New[string, netip.Addr](lru.WithSize[string, netip.Addr](size)),\n\t\tcacheHost: lru.New[netip.Addr, string](lru.WithSize[netip.Addr, string](size)),\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/fakeip/pool.go",
    "content": "package fakeip\n\nimport (\n\t\"errors\"\n\t\"net/netip\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/metacubex/mihomo/component/profile/cachefile\"\n\n\t\"go4.org/netipx\"\n)\n\nconst (\n\toffsetKey = \"key-offset-fake-ip\"\n\tcycleKey  = \"key-cycle-fake-ip\"\n)\n\ntype store interface {\n\tGetByHost(host string) (netip.Addr, bool)\n\tPutByHost(host string, ip netip.Addr)\n\tGetByIP(ip netip.Addr) (string, bool)\n\tPutByIP(ip netip.Addr, host string)\n\tDelByIP(ip netip.Addr)\n\tExist(ip netip.Addr) bool\n\tCloneTo(store)\n\tFlushFakeIP() error\n}\n\n// Pool is an implementation about fake ip generator without storage\ntype Pool struct {\n\tgateway netip.Addr\n\tfirst   netip.Addr\n\tlast    netip.Addr\n\toffset  netip.Addr\n\tcycle   bool\n\tmux     sync.Mutex\n\tipnet   netip.Prefix\n\tstore   store\n}\n\n// Lookup return a fake ip with host\nfunc (p *Pool) Lookup(host string) netip.Addr {\n\tp.mux.Lock()\n\tdefer p.mux.Unlock()\n\n\t// RFC4343: DNS Case Insensitive, we SHOULD return result with all cases.\n\thost = strings.ToLower(host)\n\tif ip, exist := p.store.GetByHost(host); exist {\n\t\treturn ip\n\t}\n\n\tip := p.get(host)\n\tp.store.PutByHost(host, ip)\n\treturn ip\n}\n\n// LookBack return host with the fake ip\nfunc (p *Pool) LookBack(ip netip.Addr) (string, bool) {\n\tp.mux.Lock()\n\tdefer p.mux.Unlock()\n\n\treturn p.store.GetByIP(ip)\n}\n\n// Exist returns if given ip exists in fake-ip pool\nfunc (p *Pool) Exist(ip netip.Addr) bool {\n\tp.mux.Lock()\n\tdefer p.mux.Unlock()\n\n\treturn p.store.Exist(ip)\n}\n\n// Gateway return gateway ip\nfunc (p *Pool) Gateway() netip.Addr {\n\treturn p.gateway\n}\n\n// Broadcast return the last ip\nfunc (p *Pool) Broadcast() netip.Addr {\n\treturn p.last\n}\n\n// IPNet return raw ipnet\nfunc (p *Pool) IPNet() netip.Prefix {\n\treturn p.ipnet\n}\n\n// CloneFrom clone cache from old pool\nfunc (p *Pool) CloneFrom(o *Pool) {\n\to.store.CloneTo(p.store)\n}\n\nfunc (p *Pool) get(host string) netip.Addr {\n\tp.offset = p.offset.Next()\n\n\tif !p.offset.Less(p.last) {\n\t\tp.cycle = true\n\t\tp.offset = p.first\n\t}\n\n\tif p.cycle || p.store.Exist(p.offset) {\n\t\tp.store.DelByIP(p.offset)\n\t}\n\n\tp.store.PutByIP(p.offset, host)\n\treturn p.offset\n}\n\nfunc (p *Pool) FlushFakeIP() error {\n\terr := p.store.FlushFakeIP()\n\tif err == nil {\n\t\tp.cycle = false\n\t\tp.offset = p.first.Prev()\n\t}\n\treturn err\n}\n\nfunc (p *Pool) StoreState() {\n\tif s, ok := p.store.(*cachefileStore); ok {\n\t\ts.PutByHost(offsetKey, p.offset)\n\t\tif p.cycle {\n\t\t\ts.PutByHost(cycleKey, p.offset)\n\t\t}\n\t}\n}\n\nfunc (p *Pool) restoreState() {\n\tif s, ok := p.store.(*cachefileStore); ok {\n\t\tif _, exist := s.GetByHost(cycleKey); exist {\n\t\t\tp.cycle = true\n\t\t}\n\n\t\tif offset, exist := s.GetByHost(offsetKey); exist {\n\t\t\tif p.ipnet.Contains(offset) {\n\t\t\t\tp.offset = offset\n\t\t\t} else {\n\t\t\t\t_ = p.FlushFakeIP()\n\t\t\t}\n\t\t} else if s.Exist(p.first) {\n\t\t\t_ = p.FlushFakeIP()\n\t\t}\n\t}\n}\n\ntype Options struct {\n\tIPNet netip.Prefix\n\n\t// Size sets the maximum number of entries in memory\n\t// and does not work if Persistence is true\n\tSize int\n\n\t// Persistence will save the data to disk.\n\t// Size will not work and record will be fully stored.\n\tPersistence bool\n}\n\n// New return Pool instance\nfunc New(options Options) (*Pool, error) {\n\tvar (\n\t\thostAddr = options.IPNet.Masked().Addr()\n\t\tgateway  = hostAddr.Next()\n\t\tfirst    = gateway.Next().Next().Next() // default start with 198.18.0.4\n\t\tlast     = netipx.PrefixLastIP(options.IPNet)\n\t)\n\n\tif !options.IPNet.IsValid() || !first.IsValid() || !first.Less(last) {\n\t\treturn nil, errors.New(\"ipnet don't have valid ip\")\n\t}\n\n\tpool := &Pool{\n\t\tgateway: gateway,\n\t\tfirst:   first,\n\t\tlast:    last,\n\t\toffset:  first.Prev(),\n\t\tcycle:   false,\n\t\tipnet:   options.IPNet,\n\t}\n\tif options.Persistence {\n\t\tpool.store = newCachefileStore(cachefile.Cache(), options.IPNet)\n\t} else {\n\t\tpool.store = newMemoryStore(options.Size)\n\t}\n\n\tpool.restoreState()\n\n\treturn pool, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/fakeip/pool_test.go",
    "content": "package fakeip\n\nimport (\n\t\"fmt\"\n\t\"net/netip\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/component/profile/cachefile\"\n\n\t\"github.com/metacubex/bbolt\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc createPools(options Options) ([]*Pool, string, error) {\n\tpool, err := New(options)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tfilePool, tempfile, err := createCachefileStore(options)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\n\treturn []*Pool{pool, filePool}, tempfile, nil\n}\n\nfunc createCachefileStore(options Options) (*Pool, string, error) {\n\tpool, err := New(options)\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\tf, err := os.CreateTemp(\"\", \"mihomo\")\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\n\tdb, err := bbolt.Open(f.Name(), 0o666, &bbolt.Options{Timeout: time.Second})\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\n\tpool.store = newCachefileStore(&cachefile.CacheFile{DB: db}, options.IPNet)\n\treturn pool, f.Name(), nil\n}\n\nfunc TestPool_Basic(t *testing.T) {\n\tipnet := netip.MustParsePrefix(\"192.168.0.0/28\")\n\tpools, tempfile, err := createPools(Options{\n\t\tIPNet: ipnet,\n\t\tSize:  10,\n\t})\n\tassert.Nil(t, err)\n\tdefer os.Remove(tempfile)\n\n\tfor _, pool := range pools {\n\t\tfirst := pool.Lookup(\"foo.com\")\n\t\tlast := pool.Lookup(\"bar.com\")\n\t\tbar, exist := pool.LookBack(last)\n\n\t\tassert.Equal(t, first, netip.AddrFrom4([4]byte{192, 168, 0, 4}))\n\t\tassert.Equal(t, pool.Lookup(\"foo.com\"), netip.AddrFrom4([4]byte{192, 168, 0, 4}))\n\t\tassert.Equal(t, last, netip.AddrFrom4([4]byte{192, 168, 0, 5}))\n\t\tassert.True(t, exist)\n\t\tassert.Equal(t, bar, \"bar.com\")\n\t\tassert.Equal(t, pool.Gateway(), netip.AddrFrom4([4]byte{192, 168, 0, 1}))\n\t\tassert.Equal(t, pool.Broadcast(), netip.AddrFrom4([4]byte{192, 168, 0, 15}))\n\t\tassert.Equal(t, pool.IPNet().String(), ipnet.String())\n\t\tassert.True(t, pool.Exist(netip.AddrFrom4([4]byte{192, 168, 0, 5})))\n\t\tassert.False(t, pool.Exist(netip.AddrFrom4([4]byte{192, 168, 0, 6})))\n\t\tassert.False(t, pool.Exist(netip.MustParseAddr(\"::1\")))\n\t}\n}\n\nfunc TestPool_BasicV6(t *testing.T) {\n\tipnet := netip.MustParsePrefix(\"2001:4860:4860::8888/118\")\n\tpools, tempfile, err := createPools(Options{\n\t\tIPNet: ipnet,\n\t\tSize:  10,\n\t})\n\tassert.Nil(t, err)\n\tdefer os.Remove(tempfile)\n\n\tfor _, pool := range pools {\n\t\tfirst := pool.Lookup(\"foo.com\")\n\t\tlast := pool.Lookup(\"bar.com\")\n\t\tbar, exist := pool.LookBack(last)\n\n\t\tassert.Equal(t, first, netip.MustParseAddr(\"2001:4860:4860:0000:0000:0000:0000:8804\"))\n\t\tassert.Equal(t, pool.Lookup(\"foo.com\"), netip.MustParseAddr(\"2001:4860:4860:0000:0000:0000:0000:8804\"))\n\t\tassert.Equal(t, last, netip.MustParseAddr(\"2001:4860:4860:0000:0000:0000:0000:8805\"))\n\t\tassert.True(t, exist)\n\t\tassert.Equal(t, bar, \"bar.com\")\n\t\tassert.Equal(t, pool.Gateway(), netip.MustParseAddr(\"2001:4860:4860:0000:0000:0000:0000:8801\"))\n\t\tassert.Equal(t, pool.Broadcast(), netip.MustParseAddr(\"2001:4860:4860:0000:0000:0000:0000:8bff\"))\n\t\tassert.Equal(t, pool.IPNet().String(), ipnet.String())\n\t\tassert.True(t, pool.Exist(netip.MustParseAddr(\"2001:4860:4860:0000:0000:0000:0000:8805\")))\n\t\tassert.False(t, pool.Exist(netip.MustParseAddr(\"2001:4860:4860:0000:0000:0000:0000:8806\")))\n\t\tassert.False(t, pool.Exist(netip.MustParseAddr(\"127.0.0.1\")))\n\t}\n}\n\nfunc TestPool_Case_Insensitive(t *testing.T) {\n\tipnet := netip.MustParsePrefix(\"192.168.0.1/29\")\n\tpools, tempfile, err := createPools(Options{\n\t\tIPNet: ipnet,\n\t\tSize:  10,\n\t})\n\tassert.Nil(t, err)\n\tdefer os.Remove(tempfile)\n\n\tfor _, pool := range pools {\n\t\tfirst := pool.Lookup(\"foo.com\")\n\t\tlast := pool.Lookup(\"Foo.Com\")\n\t\tfoo, exist := pool.LookBack(last)\n\n\t\tassert.Equal(t, first, pool.Lookup(\"Foo.Com\"))\n\t\tassert.Equal(t, pool.Lookup(\"fOo.cOM\"), first)\n\t\tassert.True(t, exist)\n\t\tassert.Equal(t, foo, \"foo.com\")\n\t}\n}\n\nfunc TestPool_CycleUsed(t *testing.T) {\n\tipnet := netip.MustParsePrefix(\"192.168.0.16/28\")\n\tpools, tempfile, err := createPools(Options{\n\t\tIPNet: ipnet,\n\t\tSize:  10,\n\t})\n\tassert.Nil(t, err)\n\tdefer os.Remove(tempfile)\n\n\tfor _, pool := range pools {\n\t\tfoo := pool.Lookup(\"foo.com\")\n\t\tbar := pool.Lookup(\"bar.com\")\n\t\tfor i := 0; i < 9; i++ {\n\t\t\tpool.Lookup(fmt.Sprintf(\"%d.com\", i))\n\t\t}\n\t\tbaz := pool.Lookup(\"baz.com\")\n\t\tnext := pool.Lookup(\"foo.com\")\n\t\tassert.Equal(t, foo, baz)\n\t\tassert.Equal(t, next, bar)\n\t}\n}\n\nfunc TestPool_MaxCacheSize(t *testing.T) {\n\tipnet := netip.MustParsePrefix(\"192.168.0.1/24\")\n\tpool, _ := New(Options{\n\t\tIPNet: ipnet,\n\t\tSize:  2,\n\t})\n\n\tfirst := pool.Lookup(\"foo.com\")\n\tpool.Lookup(\"bar.com\")\n\tpool.Lookup(\"baz.com\")\n\tnext := pool.Lookup(\"foo.com\")\n\n\tassert.NotEqual(t, first, next)\n}\n\nfunc TestPool_DoubleMapping(t *testing.T) {\n\tipnet := netip.MustParsePrefix(\"192.168.0.1/24\")\n\tpool, _ := New(Options{\n\t\tIPNet: ipnet,\n\t\tSize:  2,\n\t})\n\n\t// fill cache\n\tfooIP := pool.Lookup(\"foo.com\")\n\tbazIP := pool.Lookup(\"baz.com\")\n\n\t// make foo.com hot\n\tpool.Lookup(\"foo.com\")\n\n\t// should drop baz.com\n\tbarIP := pool.Lookup(\"bar.com\")\n\n\t_, fooExist := pool.LookBack(fooIP)\n\t_, bazExist := pool.LookBack(bazIP)\n\t_, barExist := pool.LookBack(barIP)\n\n\tnewBazIP := pool.Lookup(\"baz.com\")\n\n\tassert.True(t, fooExist)\n\tassert.False(t, bazExist)\n\tassert.True(t, barExist)\n\n\tassert.NotEqual(t, bazIP, newBazIP)\n}\n\nfunc TestPool_Clone(t *testing.T) {\n\tipnet := netip.MustParsePrefix(\"192.168.0.1/24\")\n\tpool, _ := New(Options{\n\t\tIPNet: ipnet,\n\t\tSize:  2,\n\t})\n\n\tfirst := pool.Lookup(\"foo.com\")\n\tlast := pool.Lookup(\"bar.com\")\n\tassert.Equal(t, first, netip.AddrFrom4([4]byte{192, 168, 0, 4}))\n\tassert.Equal(t, last, netip.AddrFrom4([4]byte{192, 168, 0, 5}))\n\n\tnewPool, _ := New(Options{\n\t\tIPNet: ipnet,\n\t\tSize:  2,\n\t})\n\tnewPool.CloneFrom(pool)\n\t_, firstExist := newPool.LookBack(first)\n\t_, lastExist := newPool.LookBack(last)\n\tassert.True(t, firstExist)\n\tassert.True(t, lastExist)\n}\n\nfunc TestPool_Error(t *testing.T) {\n\tipnet := netip.MustParsePrefix(\"192.168.0.1/31\")\n\t_, err := New(Options{\n\t\tIPNet: ipnet,\n\t\tSize:  10,\n\t})\n\n\tassert.Error(t, err)\n}\n\nfunc TestPool_FlushFileCache(t *testing.T) {\n\tipnet := netip.MustParsePrefix(\"192.168.0.1/28\")\n\tpools, tempfile, err := createPools(Options{\n\t\tIPNet: ipnet,\n\t\tSize:  10,\n\t})\n\tassert.Nil(t, err)\n\tdefer os.Remove(tempfile)\n\n\tfor _, pool := range pools {\n\t\tfoo := pool.Lookup(\"foo.com\")\n\t\tbar := pool.Lookup(\"baz.com\")\n\t\tbax := pool.Lookup(\"baz.com\")\n\t\tfox := pool.Lookup(\"foo.com\")\n\n\t\terr = pool.FlushFakeIP()\n\t\tassert.Nil(t, err)\n\n\t\tnext := pool.Lookup(\"baz.com\")\n\t\tbaz := pool.Lookup(\"foo.com\")\n\t\tnero := pool.Lookup(\"foo.com\")\n\n\t\tassert.Equal(t, foo, fox)\n\t\tassert.Equal(t, foo, next)\n\t\tassert.NotEqual(t, foo, baz)\n\t\tassert.Equal(t, bar, bax)\n\t\tassert.Equal(t, bar, baz)\n\t\tassert.NotEqual(t, bar, next)\n\t\tassert.Equal(t, baz, nero)\n\t}\n}\n\nfunc TestPool_FlushMemoryCache(t *testing.T) {\n\tipnet := netip.MustParsePrefix(\"192.168.0.1/28\")\n\tpool, _ := New(Options{\n\t\tIPNet: ipnet,\n\t\tSize:  10,\n\t})\n\n\tfoo := pool.Lookup(\"foo.com\")\n\tbar := pool.Lookup(\"baz.com\")\n\tbax := pool.Lookup(\"baz.com\")\n\tfox := pool.Lookup(\"foo.com\")\n\n\terr := pool.FlushFakeIP()\n\tassert.Nil(t, err)\n\n\tnext := pool.Lookup(\"baz.com\")\n\tbaz := pool.Lookup(\"foo.com\")\n\tnero := pool.Lookup(\"foo.com\")\n\n\tassert.Equal(t, foo, fox)\n\tassert.Equal(t, foo, next)\n\tassert.NotEqual(t, foo, baz)\n\tassert.Equal(t, bar, bax)\n\tassert.Equal(t, bar, baz)\n\tassert.NotEqual(t, bar, next)\n\tassert.Equal(t, baz, nero)\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/fakeip/skipper.go",
    "content": "package fakeip\n\nimport (\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\nconst (\n\tUseFakeIP = \"fake-ip\"\n\tUseRealIP = \"real-ip\"\n)\n\ntype Skipper struct {\n\tRules []C.Rule\n\tHost  []C.DomainMatcher\n\tMode  C.FilterMode\n}\n\n// ShouldSkipped return if domain should be skipped\nfunc (p *Skipper) ShouldSkipped(domain string) bool {\n\tif len(p.Rules) > 0 {\n\t\tmetadata := &C.Metadata{Host: domain}\n\t\tfor _, rule := range p.Rules {\n\t\t\tif matched, action := rule.Match(metadata, C.RuleMatchHelper{}); matched {\n\t\t\t\treturn action == UseRealIP\n\t\t\t}\n\t\t}\n\t\treturn false\n\t}\n\n\tshould := p.shouldSkipped(domain)\n\tif p.Mode == C.FilterWhiteList {\n\t\treturn !should\n\t}\n\treturn should\n}\n\nfunc (p *Skipper) shouldSkipped(domain string) bool {\n\tfor _, matcher := range p.Host {\n\t\tif matcher.MatchDomain(domain) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/fakeip/skipper_test.go",
    "content": "package fakeip\n\nimport (\n\t\"testing\"\n\n\t\"github.com/metacubex/mihomo/component/trie\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestSkipper_BlackList(t *testing.T) {\n\ttree := trie.New[struct{}]()\n\tassert.NoError(t, tree.Insert(\"example.com\", struct{}{}))\n\tassert.False(t, tree.IsEmpty())\n\tskipper := &Skipper{\n\t\tHost: []C.DomainMatcher{tree.NewDomainSet()},\n\t}\n\tassert.True(t, skipper.ShouldSkipped(\"example.com\"))\n\tassert.False(t, skipper.ShouldSkipped(\"foo.com\"))\n\tassert.False(t, skipper.shouldSkipped(\"baz.com\"))\n}\n\nfunc TestSkipper_WhiteList(t *testing.T) {\n\ttree := trie.New[struct{}]()\n\tassert.NoError(t, tree.Insert(\"example.com\", struct{}{}))\n\tassert.False(t, tree.IsEmpty())\n\tskipper := &Skipper{\n\t\tHost: []C.DomainMatcher{tree.NewDomainSet()},\n\t\tMode: C.FilterWhiteList,\n\t}\n\tassert.False(t, skipper.ShouldSkipped(\"example.com\"))\n\tassert.True(t, skipper.ShouldSkipped(\"foo.com\"))\n\tassert.True(t, skipper.ShouldSkipped(\"baz.com\"))\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/generator/cmd.go",
    "content": "package generator\n\nimport (\n\t\"encoding/base64\"\n\t\"fmt\"\n\n\t\"github.com/metacubex/mihomo/component/ech\"\n\t\"github.com/metacubex/mihomo/transport/sudoku\"\n\t\"github.com/metacubex/mihomo/transport/vless/encryption\"\n\n\t\"github.com/gofrs/uuid/v5\"\n)\n\nfunc Main(args []string) {\n\tif len(args) < 1 {\n\t\tpanic(\"Using: generate uuid/reality-keypair/wg-keypair/ech-keypair/vless-mlkem768/vless-x25519/sudoku-keypair\")\n\t}\n\tswitch args[0] {\n\tcase \"uuid\":\n\t\tnewUUID, err := uuid.NewV4()\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tfmt.Println(newUUID.String())\n\tcase \"reality-keypair\":\n\t\tprivateKey, err := GenX25519PrivateKey()\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tfmt.Println(\"PrivateKey: \" + base64.RawURLEncoding.EncodeToString(privateKey.Bytes()))\n\t\tfmt.Println(\"PublicKey: \" + base64.RawURLEncoding.EncodeToString(privateKey.PublicKey().Bytes()))\n\tcase \"wg-keypair\":\n\t\tprivateKey, err := GenX25519PrivateKey()\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tfmt.Println(\"PrivateKey: \" + base64.StdEncoding.EncodeToString(privateKey.Bytes()))\n\t\tfmt.Println(\"PublicKey: \" + base64.StdEncoding.EncodeToString(privateKey.PublicKey().Bytes()))\n\tcase \"ech-keypair\":\n\t\tif len(args) < 2 {\n\t\t\tpanic(\"Using: generate ech-keypair <plain_server_name>\")\n\t\t}\n\t\tconfigBase64, keyPem, err := ech.GenECHConfig(args[1])\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tfmt.Println(\"Config:\", configBase64)\n\t\tfmt.Println(\"Key:\", keyPem)\n\tcase \"vless-mlkem768\":\n\t\tvar seed string\n\t\tif len(args) > 1 {\n\t\t\tseed = args[1]\n\t\t}\n\t\tseedBase64, clientBase64, hash32Base64, err := encryption.GenMLKEM768(seed)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tfmt.Println(\"Seed: \" + seedBase64)\n\t\tfmt.Println(\"Client: \" + clientBase64)\n\t\tfmt.Println(\"Hash32: \" + hash32Base64)\n\t\tfmt.Println(\"-----------------------\")\n\t\tfmt.Println(\"      Lazy-Config      \")\n\t\tfmt.Println(\"-----------------------\")\n\t\tfmt.Printf(\"[Server] decryption: \\\"mlkem768x25519plus.native.600s.%s\\\"\\n\", seedBase64)\n\t\tfmt.Printf(\"[Client] encryption: \\\"mlkem768x25519plus.native.0rtt.%s\\\"\\n\", clientBase64)\n\tcase \"vless-x25519\":\n\t\tvar privateKey string\n\t\tif len(args) > 1 {\n\t\t\tprivateKey = args[1]\n\t\t}\n\t\tprivateKeyBase64, passwordBase64, hash32Base64, err := encryption.GenX25519(privateKey)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tfmt.Println(\"PrivateKey: \" + privateKeyBase64)\n\t\tfmt.Println(\"Password: \" + passwordBase64)\n\t\tfmt.Println(\"Hash32: \" + hash32Base64)\n\t\tfmt.Println(\"-----------------------\")\n\t\tfmt.Println(\"      Lazy-Config      \")\n\t\tfmt.Println(\"-----------------------\")\n\t\tfmt.Printf(\"[Server] decryption: \\\"mlkem768x25519plus.native.600s.%s\\\"\\n\", privateKeyBase64)\n\t\tfmt.Printf(\"[Client] encryption: \\\"mlkem768x25519plus.native.0rtt.%s\\\"\\n\", passwordBase64)\n\tcase \"sudoku-keypair\":\n\t\tprivateKey, publicKey, err := sudoku.GenKeyPair()\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\t// Output: Available Private Key for client, Master Public Key for server\n\t\tfmt.Println(\"PrivateKey: \" + privateKey)\n\t\tfmt.Println(\"PublicKey: \" + publicKey)\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/generator/x25519.go",
    "content": "package generator\n\nimport (\n\t\"crypto/ecdh\"\n\t\"crypto/rand\"\n)\n\nconst X25519KeySize = 32\n\nfunc GenX25519PrivateKey() (*ecdh.PrivateKey, error) {\n\tvar privateKey [X25519KeySize]byte\n\t_, err := rand.Read(privateKey[:])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Avoid generating equivalent X25519 private keys\n\t// https://github.com/XTLS/Xray-core/pull/1747\n\t//\n\t// Modify random bytes using algorithm described at:\n\t// https://cr.yp.to/ecdh.html.\n\tprivateKey[0] &= 248\n\tprivateKey[31] &= 127\n\tprivateKey[31] |= 64\n\n\treturn ecdh.X25519().NewPrivateKey(privateKey[:])\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/geodata/attr.go",
    "content": "package geodata\n\nimport (\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/component/geodata/router\"\n)\n\ntype AttributeList struct {\n\tmatcher []BooleanMatcher\n}\n\nfunc (al *AttributeList) Match(domain *router.Domain) bool {\n\tfor _, matcher := range al.matcher {\n\t\tif !matcher.Match(domain) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (al *AttributeList) IsEmpty() bool {\n\treturn len(al.matcher) == 0\n}\n\nfunc (al *AttributeList) String() string {\n\tmatcher := make([]string, len(al.matcher))\n\tfor i, match := range al.matcher {\n\t\tmatcher[i] = string(match)\n\t}\n\treturn strings.Join(matcher, \",\")\n}\n\nfunc parseAttrs(attrs []string) *AttributeList {\n\tal := new(AttributeList)\n\tfor _, attr := range attrs {\n\t\ttrimmedAttr := strings.ToLower(strings.TrimSpace(attr))\n\t\tif len(trimmedAttr) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tal.matcher = append(al.matcher, BooleanMatcher(trimmedAttr))\n\t}\n\treturn al\n}\n\ntype AttributeMatcher interface {\n\tMatch(*router.Domain) bool\n}\n\ntype BooleanMatcher string\n\nfunc (m BooleanMatcher) Match(domain *router.Domain) bool {\n\tfor _, attr := range domain.Attribute {\n\t\tif strings.EqualFold(attr.GetKey(), string(m)) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/geodata/geodata.go",
    "content": "package geodata\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/metacubex/mihomo/component/geodata/router\"\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\ntype loader struct {\n\tLoaderImplementation\n}\n\nfunc (l *loader) LoadGeoSite(list string) ([]*router.Domain, error) {\n\treturn l.LoadSiteByPath(C.GeositeName, list)\n}\n\nfunc (l *loader) LoadGeoIP(country string) ([]*router.CIDR, error) {\n\treturn l.LoadIPByPath(C.GeoipName, country)\n}\n\nvar loaders map[string]func() LoaderImplementation\n\nfunc RegisterGeoDataLoaderImplementationCreator(name string, loader func() LoaderImplementation) {\n\tif loaders == nil {\n\t\tloaders = map[string]func() LoaderImplementation{}\n\t}\n\tloaders[name] = loader\n}\n\nfunc getGeoDataLoaderImplementation(name string) (LoaderImplementation, error) {\n\tif geoLoader, ok := loaders[name]; ok {\n\t\treturn geoLoader(), nil\n\t}\n\treturn nil, fmt.Errorf(\"unable to locate GeoData loader %s\", name)\n}\n\nfunc GetGeoDataLoader(name string) (Loader, error) {\n\tloadImpl, err := getGeoDataLoaderImplementation(name)\n\tif err == nil {\n\t\treturn &loader{loadImpl}, nil\n\t}\n\treturn nil, err\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/geodata/geodataproto.go",
    "content": "package geodata\n\nimport (\n\t\"github.com/metacubex/mihomo/component/geodata/router\"\n)\n\ntype LoaderImplementation interface {\n\tLoadSiteByPath(filename, list string) ([]*router.Domain, error)\n\tLoadSiteByBytes(geositeBytes []byte, list string) ([]*router.Domain, error)\n\tLoadIPByPath(filename, country string) ([]*router.CIDR, error)\n\tLoadIPByBytes(geoipBytes []byte, country string) ([]*router.CIDR, error)\n}\n\ntype Loader interface {\n\tLoaderImplementation\n\tLoadGeoSite(list string) ([]*router.Domain, error)\n\tLoadGeoIP(country string) ([]*router.CIDR, error)\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/geodata/init.go",
    "content": "package geodata\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/atomic\"\n\tmihomoHttp \"github.com/metacubex/mihomo/component/http\"\n\t\"github.com/metacubex/mihomo/component/mmdb\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/metacubex/http\"\n)\n\nvar (\n\tinitGeoSite bool\n\tinitGeoIP   int\n\tinitASN     bool\n\n\tinitGeoSiteMutex sync.Mutex\n\tinitGeoIPMutex   sync.Mutex\n\tinitASNMutex     sync.Mutex\n\n\tgeoIpEnable   atomic.Bool\n\tgeoSiteEnable atomic.Bool\n\tasnEnable     atomic.Bool\n\n\tgeoIpUrl   string\n\tmmdbUrl    string\n\tgeoSiteUrl string\n\tasnUrl     string\n)\n\nfunc GeoIpUrl() string {\n\treturn geoIpUrl\n}\n\nfunc SetGeoIpUrl(url string) {\n\tgeoIpUrl = url\n}\n\nfunc MmdbUrl() string {\n\treturn mmdbUrl\n}\n\nfunc SetMmdbUrl(url string) {\n\tmmdbUrl = url\n}\n\nfunc GeoSiteUrl() string {\n\treturn geoSiteUrl\n}\n\nfunc SetGeoSiteUrl(url string) {\n\tgeoSiteUrl = url\n}\n\nfunc ASNUrl() string {\n\treturn asnUrl\n}\n\nfunc SetASNUrl(url string) {\n\tasnUrl = url\n}\n\nfunc downloadToPath(url string, path string) (err error) {\n\tctx, cancel := context.WithTimeout(context.Background(), time.Second*90)\n\tdefer cancel()\n\tresp, err := mihomoHttp.HttpRequest(ctx, url, http.MethodGet, nil, nil)\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer resp.Body.Close()\n\n\tf, err := os.OpenFile(path, os.O_CREATE|os.O_WRONLY, 0o644)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer f.Close()\n\t_, err = io.Copy(f, resp.Body)\n\n\treturn err\n}\n\nfunc InitGeoSite() error {\n\tgeoSiteEnable.Store(true)\n\tinitGeoSiteMutex.Lock()\n\tdefer initGeoSiteMutex.Unlock()\n\tif _, err := os.Stat(C.Path.GeoSite()); os.IsNotExist(err) {\n\t\tlog.Infoln(\"Can't find GeoSite.dat, start download\")\n\t\tif err := downloadToPath(GeoSiteUrl(), C.Path.GeoSite()); err != nil {\n\t\t\treturn fmt.Errorf(\"can't download GeoSite.dat: %s\", err.Error())\n\t\t}\n\t\tlog.Infoln(\"Download GeoSite.dat finish\")\n\t\tinitGeoSite = false\n\t}\n\tif !initGeoSite {\n\t\tif err := Verify(C.GeositeName); err != nil {\n\t\t\tlog.Warnln(\"GeoSite.dat invalid, remove and download: %s\", err)\n\t\t\tif err := os.Remove(C.Path.GeoSite()); err != nil {\n\t\t\t\treturn fmt.Errorf(\"can't remove invalid GeoSite.dat: %s\", err.Error())\n\t\t\t}\n\t\t\tif err := downloadToPath(GeoSiteUrl(), C.Path.GeoSite()); err != nil {\n\t\t\t\treturn fmt.Errorf(\"can't download GeoSite.dat: %s\", err.Error())\n\t\t\t}\n\t\t}\n\t\tinitGeoSite = true\n\t}\n\treturn nil\n}\n\nfunc InitGeoIP() error {\n\tgeoIpEnable.Store(true)\n\tinitGeoIPMutex.Lock()\n\tdefer initGeoIPMutex.Unlock()\n\tif GeodataMode() {\n\t\tif _, err := os.Stat(C.Path.GeoIP()); os.IsNotExist(err) {\n\t\t\tlog.Infoln(\"Can't find GeoIP.dat, start download\")\n\t\t\tif err := downloadToPath(GeoIpUrl(), C.Path.GeoIP()); err != nil {\n\t\t\t\treturn fmt.Errorf(\"can't download GeoIP.dat: %s\", err.Error())\n\t\t\t}\n\t\t\tlog.Infoln(\"Download GeoIP.dat finish\")\n\t\t\tinitGeoIP = 0\n\t\t}\n\n\t\tif initGeoIP != 1 {\n\t\t\tif err := Verify(C.GeoipName); err != nil {\n\t\t\t\tlog.Warnln(\"GeoIP.dat invalid, remove and download: %s\", err)\n\t\t\t\tif err := os.Remove(C.Path.GeoIP()); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"can't remove invalid GeoIP.dat: %s\", err.Error())\n\t\t\t\t}\n\t\t\t\tif err := downloadToPath(GeoIpUrl(), C.Path.GeoIP()); err != nil {\n\t\t\t\t\treturn fmt.Errorf(\"can't download GeoIP.dat: %s\", err.Error())\n\t\t\t\t}\n\t\t\t}\n\t\t\tinitGeoIP = 1\n\t\t}\n\t\treturn nil\n\t}\n\n\tif _, err := os.Stat(C.Path.MMDB()); os.IsNotExist(err) {\n\t\tlog.Infoln(\"Can't find MMDB, start download\")\n\t\tif err := downloadToPath(MmdbUrl(), C.Path.MMDB()); err != nil {\n\t\t\treturn fmt.Errorf(\"can't download MMDB: %s\", err.Error())\n\t\t}\n\t}\n\n\tif initGeoIP != 2 {\n\t\tif !mmdb.Verify(C.Path.MMDB()) {\n\t\t\tlog.Warnln(\"MMDB invalid, remove and download\")\n\t\t\tif err := os.Remove(C.Path.MMDB()); err != nil {\n\t\t\t\treturn fmt.Errorf(\"can't remove invalid MMDB: %s\", err.Error())\n\t\t\t}\n\t\t\tif err := downloadToPath(MmdbUrl(), C.Path.MMDB()); err != nil {\n\t\t\t\treturn fmt.Errorf(\"can't download MMDB: %s\", err.Error())\n\t\t\t}\n\t\t}\n\t\tinitGeoIP = 2\n\t}\n\treturn nil\n}\n\nfunc InitASN() error {\n\tasnEnable.Store(true)\n\tinitASNMutex.Lock()\n\tdefer initASNMutex.Unlock()\n\tif _, err := os.Stat(C.Path.ASN()); os.IsNotExist(err) {\n\t\tlog.Infoln(\"Can't find ASN.mmdb, start download\")\n\t\tif err := downloadToPath(ASNUrl(), C.Path.ASN()); err != nil {\n\t\t\treturn fmt.Errorf(\"can't download ASN.mmdb: %s\", err.Error())\n\t\t}\n\t\tlog.Infoln(\"Download ASN.mmdb finish\")\n\t\tinitASN = false\n\t}\n\tif !initASN {\n\t\tif !mmdb.Verify(C.Path.ASN()) {\n\t\t\tlog.Warnln(\"ASN invalid, remove and download\")\n\t\t\tif err := os.Remove(C.Path.ASN()); err != nil {\n\t\t\t\treturn fmt.Errorf(\"can't remove invalid ASN: %s\", err.Error())\n\t\t\t}\n\t\t\tif err := downloadToPath(ASNUrl(), C.Path.ASN()); err != nil {\n\t\t\t\treturn fmt.Errorf(\"can't download ASN: %s\", err.Error())\n\t\t\t}\n\t\t}\n\t\tinitASN = true\n\t}\n\treturn nil\n}\n\nfunc GeoIpEnable() bool {\n\treturn geoIpEnable.Load()\n}\n\nfunc GeoSiteEnable() bool {\n\treturn geoSiteEnable.Load()\n}\n\nfunc ASNEnable() bool {\n\treturn asnEnable.Load()\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/geodata/memconservative/cache.go",
    "content": "package memconservative\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/component/geodata/router\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"google.golang.org/protobuf/proto\"\n)\n\ntype GeoIPCache map[string]*router.GeoIP\n\nfunc (g GeoIPCache) Has(key string) bool {\n\treturn !(g.Get(key) == nil)\n}\n\nfunc (g GeoIPCache) Get(key string) *router.GeoIP {\n\tif g == nil {\n\t\treturn nil\n\t}\n\treturn g[key]\n}\n\nfunc (g GeoIPCache) Set(key string, value *router.GeoIP) {\n\tif g == nil {\n\t\tg = make(map[string]*router.GeoIP)\n\t}\n\tg[key] = value\n}\n\nfunc (g GeoIPCache) Unmarshal(filename, code string) (*router.GeoIP, error) {\n\tasset := C.Path.GetAssetLocation(filename)\n\tidx := strings.ToLower(asset + \":\" + code)\n\tif g.Has(idx) {\n\t\treturn g.Get(idx), nil\n\t}\n\n\tgeoipBytes, err := Decode(asset, code)\n\tswitch err {\n\tcase nil:\n\t\tvar geoip router.GeoIP\n\t\tif err := proto.Unmarshal(geoipBytes, &geoip); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tg.Set(idx, &geoip)\n\t\treturn &geoip, nil\n\n\tcase errCodeNotFound:\n\t\treturn nil, fmt.Errorf(\"country code %s%s%s\", code, \" not found in \", filename)\n\n\tcase errFailedToReadBytes, errFailedToReadExpectedLenBytes,\n\t\terrInvalidGeodataFile, errInvalidGeodataVarintLength:\n\t\tlog.Warnln(\"failed to decode geoip file: %s%s\", filename, \", fallback to the original ReadFile method\")\n\t\tgeoipBytes, err = os.ReadFile(asset)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tvar geoipList router.GeoIPList\n\t\tif err := proto.Unmarshal(geoipBytes, &geoipList); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfor _, geoip := range geoipList.GetEntry() {\n\t\t\tif strings.EqualFold(code, geoip.GetCountryCode()) {\n\t\t\t\tg.Set(idx, geoip)\n\t\t\t\treturn geoip, nil\n\t\t\t}\n\t\t}\n\n\tdefault:\n\t\treturn nil, err\n\t}\n\n\treturn nil, fmt.Errorf(\"country code %s%s%s\", code, \" not found in \", filename)\n}\n\ntype GeoSiteCache map[string]*router.GeoSite\n\nfunc (g GeoSiteCache) Has(key string) bool {\n\treturn !(g.Get(key) == nil)\n}\n\nfunc (g GeoSiteCache) Get(key string) *router.GeoSite {\n\tif g == nil {\n\t\treturn nil\n\t}\n\treturn g[key]\n}\n\nfunc (g GeoSiteCache) Set(key string, value *router.GeoSite) {\n\tif g == nil {\n\t\tg = make(map[string]*router.GeoSite)\n\t}\n\tg[key] = value\n}\n\nfunc (g GeoSiteCache) Unmarshal(filename, code string) (*router.GeoSite, error) {\n\tasset := C.Path.GetAssetLocation(filename)\n\tidx := strings.ToLower(asset + \":\" + code)\n\tif g.Has(idx) {\n\t\treturn g.Get(idx), nil\n\t}\n\n\tgeositeBytes, err := Decode(asset, code)\n\tswitch err {\n\tcase nil:\n\t\tvar geosite router.GeoSite\n\t\tif err := proto.Unmarshal(geositeBytes, &geosite); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tg.Set(idx, &geosite)\n\t\treturn &geosite, nil\n\n\tcase errCodeNotFound:\n\t\treturn nil, fmt.Errorf(\"list %s%s%s\", code, \" not found in \", filename)\n\n\tcase errFailedToReadBytes, errFailedToReadExpectedLenBytes,\n\t\terrInvalidGeodataFile, errInvalidGeodataVarintLength:\n\t\tlog.Warnln(\"failed to decode geosite file: %s%s\", filename, \", fallback to the original ReadFile method\")\n\t\tgeositeBytes, err = os.ReadFile(asset)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tvar geositeList router.GeoSiteList\n\t\tif err := proto.Unmarshal(geositeBytes, &geositeList); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tfor _, geosite := range geositeList.GetEntry() {\n\t\t\tif strings.EqualFold(code, geosite.GetCountryCode()) {\n\t\t\t\tg.Set(idx, geosite)\n\t\t\t\treturn geosite, nil\n\t\t\t}\n\t\t}\n\n\tdefault:\n\t\treturn nil, err\n\t}\n\n\treturn nil, fmt.Errorf(\"list %s%s%s\", code, \" not found in \", filename)\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/geodata/memconservative/decode.go",
    "content": "package memconservative\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n\n\t\"google.golang.org/protobuf/encoding/protowire\"\n)\n\nvar (\n\terrFailedToReadBytes            = errors.New(\"failed to read bytes\")\n\terrFailedToReadExpectedLenBytes = errors.New(\"failed to read expected length of bytes\")\n\terrInvalidGeodataFile           = errors.New(\"invalid geodata file\")\n\terrInvalidGeodataVarintLength   = errors.New(\"invalid geodata varint length\")\n\terrCodeNotFound                 = errors.New(\"code not found\")\n)\n\nfunc emitBytes(f io.ReadSeeker, code string) ([]byte, error) {\n\tcount := 1\n\tisInner := false\n\ttempContainer := make([]byte, 0, 5)\n\n\tvar result []byte\n\tvar advancedN uint64 = 1\n\tvar geoDataVarintLength, codeVarintLength, varintLenByteLen uint64 = 0, 0, 0\n\nLoop:\n\tfor {\n\t\tcontainer := make([]byte, advancedN)\n\t\tbytesRead, err := f.Read(container)\n\t\tif err == io.EOF {\n\t\t\treturn nil, errCodeNotFound\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, errFailedToReadBytes\n\t\t}\n\t\tif bytesRead != len(container) {\n\t\t\treturn nil, errFailedToReadExpectedLenBytes\n\t\t}\n\n\t\tswitch count {\n\t\tcase 1, 3: // data type ((field_number << 3) | wire_type)\n\t\t\tif container[0] != 10 { // byte `0A` equals to `10` in decimal\n\t\t\t\treturn nil, errInvalidGeodataFile\n\t\t\t}\n\t\t\tadvancedN = 1\n\t\t\tcount++\n\t\tcase 2, 4: // data length\n\t\t\ttempContainer = append(tempContainer, container...)\n\t\t\tif container[0] > 127 { // max one-byte-length byte `7F`(0FFF FFFF) equals to `127` in decimal\n\t\t\t\tadvancedN = 1\n\t\t\t\tgoto Loop\n\t\t\t}\n\t\t\tlenVarint, n := protowire.ConsumeVarint(tempContainer)\n\t\t\tif n < 0 {\n\t\t\t\treturn nil, errInvalidGeodataVarintLength\n\t\t\t}\n\t\t\ttempContainer = nil\n\t\t\tif !isInner {\n\t\t\t\tisInner = true\n\t\t\t\tgeoDataVarintLength = lenVarint\n\t\t\t\tadvancedN = 1\n\t\t\t} else {\n\t\t\t\tisInner = false\n\t\t\t\tcodeVarintLength = lenVarint\n\t\t\t\tvarintLenByteLen = uint64(n)\n\t\t\t\tadvancedN = codeVarintLength\n\t\t\t}\n\t\t\tcount++\n\t\tcase 5: // data value\n\t\t\tif strings.EqualFold(string(container), code) {\n\t\t\t\tcount++\n\t\t\t\toffset := -(1 + int64(varintLenByteLen) + int64(codeVarintLength))\n\t\t\t\t_, _ = f.Seek(offset, 1)        // back to the start of GeoIP or GeoSite varint\n\t\t\t\tadvancedN = geoDataVarintLength // the number of bytes to be read in next round\n\t\t\t} else {\n\t\t\t\tcount = 1\n\t\t\t\toffset := int64(geoDataVarintLength) - int64(codeVarintLength) - int64(varintLenByteLen) - 1\n\t\t\t\t_, _ = f.Seek(offset, 1) // skip the unmatched GeoIP or GeoSite varint\n\t\t\t\tadvancedN = 1            // the next round will be the start of another GeoIPList or GeoSiteList\n\t\t\t}\n\t\tcase 6: // matched GeoIP or GeoSite varint\n\t\t\tresult = container\n\t\t\tbreak Loop\n\t\t}\n\t}\n\treturn result, nil\n}\n\nfunc Decode(filename, code string) ([]byte, error) {\n\tf, err := os.Open(filename)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to open file: %s, base error: %s\", filename, err.Error())\n\t}\n\tdefer func(f *os.File) {\n\t\t_ = f.Close()\n\t}(f)\n\n\tgeoBytes, err := emitBytes(f, code)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn geoBytes, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/geodata/memconservative/memc.go",
    "content": "package memconservative\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"runtime\"\n\n\t\"github.com/metacubex/mihomo/component/geodata\"\n\t\"github.com/metacubex/mihomo/component/geodata/router\"\n)\n\ntype memConservativeLoader struct {\n\tgeoipcache   GeoIPCache\n\tgeositecache GeoSiteCache\n}\n\nfunc (m *memConservativeLoader) LoadIPByPath(filename, country string) ([]*router.CIDR, error) {\n\tdefer runtime.GC()\n\tgeoip, err := m.geoipcache.Unmarshal(filename, country)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to decode geodata file: %s, base error: %s\", filename, err.Error())\n\t}\n\treturn geoip.Cidr, nil\n}\n\nfunc (m *memConservativeLoader) LoadIPByBytes(geoipBytes []byte, country string) ([]*router.CIDR, error) {\n\treturn nil, errors.New(\"memConservative do not support LoadIPByBytes\")\n}\n\nfunc (m *memConservativeLoader) LoadSiteByPath(filename, list string) ([]*router.Domain, error) {\n\tdefer runtime.GC()\n\tgeosite, err := m.geositecache.Unmarshal(filename, list)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to decode geodata file: %s, base error: %s\", filename, err.Error())\n\t}\n\treturn geosite.Domain, nil\n}\n\nfunc (m *memConservativeLoader) LoadSiteByBytes(geositeBytes []byte, list string) ([]*router.Domain, error) {\n\treturn nil, errors.New(\"memConservative do not support LoadSiteByBytes\")\n}\n\nfunc newMemConservativeLoader() geodata.LoaderImplementation {\n\treturn &memConservativeLoader{make(map[string]*router.GeoIP), make(map[string]*router.GeoSite)}\n}\n\nfunc init() {\n\tgeodata.RegisterGeoDataLoaderImplementationCreator(\"memconservative\", newMemConservativeLoader)\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/geodata/package_info.go",
    "content": "// Modified from: https://github.com/v2fly/v2ray-core/tree/master/infra/conf/geodata\n// License: MIT\n\npackage geodata\n"
  },
  {
    "path": "core/Clash.Meta/component/geodata/router/condition.go",
    "content": "package router\n\nimport (\n\t\"fmt\"\n\t\"net/netip\"\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/component/cidr\"\n\t\"github.com/metacubex/mihomo/component/geodata/strmatcher\"\n\t\"github.com/metacubex/mihomo/component/trie\"\n)\n\nvar matcherTypeMap = map[Domain_Type]strmatcher.Type{\n\tDomain_Plain:  strmatcher.Substr,\n\tDomain_Regex:  strmatcher.Regex,\n\tDomain_Domain: strmatcher.Domain,\n\tDomain_Full:   strmatcher.Full,\n}\n\nfunc domainToMatcher(domain *Domain) (strmatcher.Matcher, error) {\n\tmatcherType, f := matcherTypeMap[domain.Type]\n\tif !f {\n\t\treturn nil, fmt.Errorf(\"unsupported domain type %v\", domain.Type)\n\t}\n\n\tmatcher, err := matcherType.New(domain.Value)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to create domain matcher, base error: %s\", err.Error())\n\t}\n\n\treturn matcher, nil\n}\n\ntype DomainMatcher interface {\n\tApplyDomain(string) bool\n\tCount() int\n}\n\ntype succinctDomainMatcher struct {\n\tset           *trie.DomainSet\n\totherMatchers []strmatcher.Matcher\n\tcount         int\n}\n\nfunc (m *succinctDomainMatcher) ApplyDomain(domain string) bool {\n\tisMatched := m.set.Has(domain)\n\tif !isMatched {\n\t\tfor _, matcher := range m.otherMatchers {\n\t\t\tisMatched = matcher.Match(domain)\n\t\t\tif isMatched {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\treturn isMatched\n}\n\nfunc (m *succinctDomainMatcher) Count() int {\n\treturn m.count\n}\n\nfunc NewSuccinctMatcherGroup(domains []*Domain) (DomainMatcher, error) {\n\tt := trie.New[struct{}]()\n\tm := &succinctDomainMatcher{\n\t\tcount: len(domains),\n\t}\n\tfor _, d := range domains {\n\t\tswitch d.Type {\n\t\tcase Domain_Plain, Domain_Regex:\n\t\t\tmatcher, err := matcherTypeMap[d.Type].New(d.Value)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tm.otherMatchers = append(m.otherMatchers, matcher)\n\n\t\tcase Domain_Domain:\n\t\t\terr := t.Insert(\"+.\"+d.Value, struct{}{})\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\tcase Domain_Full:\n\t\t\terr := t.Insert(d.Value, struct{}{})\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\tm.set = t.NewDomainSet()\n\treturn m, nil\n}\n\ntype v2rayDomainMatcher struct {\n\tmatchers strmatcher.IndexMatcher\n\tcount    int\n}\n\nfunc NewMphMatcherGroup(domains []*Domain) (DomainMatcher, error) {\n\tg := strmatcher.NewMphMatcherGroup()\n\tfor _, d := range domains {\n\t\tmatcherType, f := matcherTypeMap[d.Type]\n\t\tif !f {\n\t\t\treturn nil, fmt.Errorf(\"unsupported domain type %v\", d.Type)\n\t\t}\n\t\t_, err := g.AddPattern(d.Value, matcherType)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tg.Build()\n\treturn &v2rayDomainMatcher{\n\t\tmatchers: g,\n\t\tcount:    len(domains),\n\t}, nil\n}\n\nfunc (m *v2rayDomainMatcher) ApplyDomain(domain string) bool {\n\treturn len(m.matchers.Match(strings.ToLower(domain))) > 0\n}\n\nfunc (m *v2rayDomainMatcher) Count() int {\n\treturn m.count\n}\n\ntype notDomainMatcher struct {\n\tDomainMatcher\n}\n\nfunc (m notDomainMatcher) ApplyDomain(domain string) bool {\n\treturn !m.DomainMatcher.ApplyDomain(domain)\n}\n\nfunc NewNotDomainMatcherGroup(matcher DomainMatcher) DomainMatcher {\n\treturn notDomainMatcher{matcher}\n}\n\ntype IPMatcher interface {\n\tMatch(ip netip.Addr) bool\n\tCount() int\n}\n\ntype geoIPMatcher struct {\n\tcidrSet *cidr.IpCidrSet\n\tcount   int\n}\n\n// Match returns true if the given ip is included by the GeoIP.\nfunc (m *geoIPMatcher) Match(ip netip.Addr) bool {\n\treturn m.cidrSet.IsContain(ip)\n}\n\nfunc (m *geoIPMatcher) Count() int {\n\treturn m.count\n}\n\nfunc NewGeoIPMatcher(cidrList []*CIDR) (IPMatcher, error) {\n\tm := &geoIPMatcher{\n\t\tcidrSet: cidr.NewIpCidrSet(),\n\t\tcount:   len(cidrList),\n\t}\n\tfor _, cidr := range cidrList {\n\t\taddr, ok := netip.AddrFromSlice(cidr.Ip)\n\t\tif !ok {\n\t\t\treturn nil, fmt.Errorf(\"error when loading GeoIP: invalid IP: %s\", cidr.Ip)\n\t\t}\n\t\terr := m.cidrSet.AddIpCidr(netip.PrefixFrom(addr, int(cidr.Prefix)))\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"error when loading GeoIP: %w\", err)\n\t\t}\n\t}\n\terr := m.cidrSet.Merge()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn m, nil\n}\n\ntype notIPMatcher struct {\n\tIPMatcher\n}\n\nfunc (m notIPMatcher) Match(ip netip.Addr) bool {\n\treturn !m.IPMatcher.Match(ip)\n}\n\nfunc NewNotIpMatcherGroup(matcher IPMatcher) IPMatcher {\n\treturn notIPMatcher{matcher}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/geodata/router/config.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.28.0\n// \tprotoc        v3.19.1\n// source: component/geodata/router/config.proto\n\npackage router\n\nimport (\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\n// Type of domain value.\ntype Domain_Type int32\n\nconst (\n\t// The value is used as is.\n\tDomain_Plain Domain_Type = 0\n\t// The value is used as a regular expression.\n\tDomain_Regex Domain_Type = 1\n\t// The value is a root domain.\n\tDomain_Domain Domain_Type = 2\n\t// The value is a domain.\n\tDomain_Full Domain_Type = 3\n)\n\n// Enum value maps for Domain_Type.\nvar (\n\tDomain_Type_name = map[int32]string{\n\t\t0: \"Plain\",\n\t\t1: \"Regex\",\n\t\t2: \"Domain\",\n\t\t3: \"Full\",\n\t}\n\tDomain_Type_value = map[string]int32{\n\t\t\"Plain\":  0,\n\t\t\"Regex\":  1,\n\t\t\"Domain\": 2,\n\t\t\"Full\":   3,\n\t}\n)\n\nfunc (x Domain_Type) Enum() *Domain_Type {\n\tp := new(Domain_Type)\n\t*p = x\n\treturn p\n}\n\nfunc (x Domain_Type) String() string {\n\treturn protoimpl.X.EnumStringOf(x.Descriptor(), protoreflect.EnumNumber(x))\n}\n\nfunc (Domain_Type) Descriptor() protoreflect.EnumDescriptor {\n\treturn file_component_geodata_router_config_proto_enumTypes[0].Descriptor()\n}\n\nfunc (Domain_Type) Type() protoreflect.EnumType {\n\treturn &file_component_geodata_router_config_proto_enumTypes[0]\n}\n\nfunc (x Domain_Type) Number() protoreflect.EnumNumber {\n\treturn protoreflect.EnumNumber(x)\n}\n\n// Deprecated: Use Domain_Type.Descriptor instead.\nfunc (Domain_Type) EnumDescriptor() ([]byte, []int) {\n\treturn file_component_geodata_router_config_proto_rawDescGZIP(), []int{0, 0}\n}\n\n// Domain for routing decision.\ntype Domain struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// Domain matching type.\n\tType Domain_Type `protobuf:\"varint,1,opt,name=type,proto3,enum=mihomo.component.geodata.router.Domain_Type\" json:\"type,omitempty\"`\n\t// Domain value.\n\tValue string `protobuf:\"bytes,2,opt,name=value,proto3\" json:\"value,omitempty\"`\n\t// Attributes of this domain. May be used for filtering.\n\tAttribute []*Domain_Attribute `protobuf:\"bytes,3,rep,name=attribute,proto3\" json:\"attribute,omitempty\"`\n}\n\nfunc (x *Domain) Reset() {\n\t*x = Domain{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_component_geodata_router_config_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Domain) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Domain) ProtoMessage() {}\n\nfunc (x *Domain) ProtoReflect() protoreflect.Message {\n\tmi := &file_component_geodata_router_config_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Domain.ProtoReflect.Descriptor instead.\nfunc (*Domain) Descriptor() ([]byte, []int) {\n\treturn file_component_geodata_router_config_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *Domain) GetType() Domain_Type {\n\tif x != nil {\n\t\treturn x.Type\n\t}\n\treturn Domain_Plain\n}\n\nfunc (x *Domain) GetValue() string {\n\tif x != nil {\n\t\treturn x.Value\n\t}\n\treturn \"\"\n}\n\nfunc (x *Domain) GetAttribute() []*Domain_Attribute {\n\tif x != nil {\n\t\treturn x.Attribute\n\t}\n\treturn nil\n}\n\n// IP for routing decision, in CIDR form.\ntype CIDR struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\t// IP address, should be either 4 or 16 bytes.\n\tIp []byte `protobuf:\"bytes,1,opt,name=ip,proto3\" json:\"ip,omitempty\"`\n\t// Number of leading ones in the network mask.\n\tPrefix uint32 `protobuf:\"varint,2,opt,name=prefix,proto3\" json:\"prefix,omitempty\"`\n}\n\nfunc (x *CIDR) Reset() {\n\t*x = CIDR{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_component_geodata_router_config_proto_msgTypes[1]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *CIDR) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*CIDR) ProtoMessage() {}\n\nfunc (x *CIDR) ProtoReflect() protoreflect.Message {\n\tmi := &file_component_geodata_router_config_proto_msgTypes[1]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use CIDR.ProtoReflect.Descriptor instead.\nfunc (*CIDR) Descriptor() ([]byte, []int) {\n\treturn file_component_geodata_router_config_proto_rawDescGZIP(), []int{1}\n}\n\nfunc (x *CIDR) GetIp() []byte {\n\tif x != nil {\n\t\treturn x.Ip\n\t}\n\treturn nil\n}\n\nfunc (x *CIDR) GetPrefix() uint32 {\n\tif x != nil {\n\t\treturn x.Prefix\n\t}\n\treturn 0\n}\n\ntype GeoIP struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tCountryCode  string  `protobuf:\"bytes,1,opt,name=country_code,json=countryCode,proto3\" json:\"country_code,omitempty\"`\n\tCidr         []*CIDR `protobuf:\"bytes,2,rep,name=cidr,proto3\" json:\"cidr,omitempty\"`\n\tReverseMatch bool    `protobuf:\"varint,3,opt,name=reverse_match,json=reverseMatch,proto3\" json:\"reverse_match,omitempty\"`\n}\n\nfunc (x *GeoIP) Reset() {\n\t*x = GeoIP{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_component_geodata_router_config_proto_msgTypes[2]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GeoIP) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GeoIP) ProtoMessage() {}\n\nfunc (x *GeoIP) ProtoReflect() protoreflect.Message {\n\tmi := &file_component_geodata_router_config_proto_msgTypes[2]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GeoIP.ProtoReflect.Descriptor instead.\nfunc (*GeoIP) Descriptor() ([]byte, []int) {\n\treturn file_component_geodata_router_config_proto_rawDescGZIP(), []int{2}\n}\n\nfunc (x *GeoIP) GetCountryCode() string {\n\tif x != nil {\n\t\treturn x.CountryCode\n\t}\n\treturn \"\"\n}\n\nfunc (x *GeoIP) GetCidr() []*CIDR {\n\tif x != nil {\n\t\treturn x.Cidr\n\t}\n\treturn nil\n}\n\nfunc (x *GeoIP) GetReverseMatch() bool {\n\tif x != nil {\n\t\treturn x.ReverseMatch\n\t}\n\treturn false\n}\n\ntype GeoIPList struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tEntry []*GeoIP `protobuf:\"bytes,1,rep,name=entry,proto3\" json:\"entry,omitempty\"`\n}\n\nfunc (x *GeoIPList) Reset() {\n\t*x = GeoIPList{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_component_geodata_router_config_proto_msgTypes[3]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GeoIPList) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GeoIPList) ProtoMessage() {}\n\nfunc (x *GeoIPList) ProtoReflect() protoreflect.Message {\n\tmi := &file_component_geodata_router_config_proto_msgTypes[3]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GeoIPList.ProtoReflect.Descriptor instead.\nfunc (*GeoIPList) Descriptor() ([]byte, []int) {\n\treturn file_component_geodata_router_config_proto_rawDescGZIP(), []int{3}\n}\n\nfunc (x *GeoIPList) GetEntry() []*GeoIP {\n\tif x != nil {\n\t\treturn x.Entry\n\t}\n\treturn nil\n}\n\ntype GeoSite struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tCountryCode string    `protobuf:\"bytes,1,opt,name=country_code,json=countryCode,proto3\" json:\"country_code,omitempty\"`\n\tDomain      []*Domain `protobuf:\"bytes,2,rep,name=domain,proto3\" json:\"domain,omitempty\"`\n}\n\nfunc (x *GeoSite) Reset() {\n\t*x = GeoSite{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_component_geodata_router_config_proto_msgTypes[4]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GeoSite) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GeoSite) ProtoMessage() {}\n\nfunc (x *GeoSite) ProtoReflect() protoreflect.Message {\n\tmi := &file_component_geodata_router_config_proto_msgTypes[4]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GeoSite.ProtoReflect.Descriptor instead.\nfunc (*GeoSite) Descriptor() ([]byte, []int) {\n\treturn file_component_geodata_router_config_proto_rawDescGZIP(), []int{4}\n}\n\nfunc (x *GeoSite) GetCountryCode() string {\n\tif x != nil {\n\t\treturn x.CountryCode\n\t}\n\treturn \"\"\n}\n\nfunc (x *GeoSite) GetDomain() []*Domain {\n\tif x != nil {\n\t\treturn x.Domain\n\t}\n\treturn nil\n}\n\ntype GeoSiteList struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tEntry []*GeoSite `protobuf:\"bytes,1,rep,name=entry,proto3\" json:\"entry,omitempty\"`\n}\n\nfunc (x *GeoSiteList) Reset() {\n\t*x = GeoSiteList{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_component_geodata_router_config_proto_msgTypes[5]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *GeoSiteList) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*GeoSiteList) ProtoMessage() {}\n\nfunc (x *GeoSiteList) ProtoReflect() protoreflect.Message {\n\tmi := &file_component_geodata_router_config_proto_msgTypes[5]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use GeoSiteList.ProtoReflect.Descriptor instead.\nfunc (*GeoSiteList) Descriptor() ([]byte, []int) {\n\treturn file_component_geodata_router_config_proto_rawDescGZIP(), []int{5}\n}\n\nfunc (x *GeoSiteList) GetEntry() []*GeoSite {\n\tif x != nil {\n\t\treturn x.Entry\n\t}\n\treturn nil\n}\n\ntype Domain_Attribute struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tKey string `protobuf:\"bytes,1,opt,name=key,proto3\" json:\"key,omitempty\"`\n\t// Types that are assignable to TypedValue:\n\t//\t*Domain_Attribute_BoolValue\n\t//\t*Domain_Attribute_IntValue\n\tTypedValue isDomain_Attribute_TypedValue `protobuf_oneof:\"typed_value\"`\n}\n\nfunc (x *Domain_Attribute) Reset() {\n\t*x = Domain_Attribute{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_component_geodata_router_config_proto_msgTypes[6]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Domain_Attribute) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Domain_Attribute) ProtoMessage() {}\n\nfunc (x *Domain_Attribute) ProtoReflect() protoreflect.Message {\n\tmi := &file_component_geodata_router_config_proto_msgTypes[6]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Domain_Attribute.ProtoReflect.Descriptor instead.\nfunc (*Domain_Attribute) Descriptor() ([]byte, []int) {\n\treturn file_component_geodata_router_config_proto_rawDescGZIP(), []int{0, 0}\n}\n\nfunc (x *Domain_Attribute) GetKey() string {\n\tif x != nil {\n\t\treturn x.Key\n\t}\n\treturn \"\"\n}\n\nfunc (m *Domain_Attribute) GetTypedValue() isDomain_Attribute_TypedValue {\n\tif m != nil {\n\t\treturn m.TypedValue\n\t}\n\treturn nil\n}\n\nfunc (x *Domain_Attribute) GetBoolValue() bool {\n\tif x, ok := x.GetTypedValue().(*Domain_Attribute_BoolValue); ok {\n\t\treturn x.BoolValue\n\t}\n\treturn false\n}\n\nfunc (x *Domain_Attribute) GetIntValue() int64 {\n\tif x, ok := x.GetTypedValue().(*Domain_Attribute_IntValue); ok {\n\t\treturn x.IntValue\n\t}\n\treturn 0\n}\n\ntype isDomain_Attribute_TypedValue interface {\n\tisDomain_Attribute_TypedValue()\n}\n\ntype Domain_Attribute_BoolValue struct {\n\tBoolValue bool `protobuf:\"varint,2,opt,name=bool_value,json=boolValue,proto3,oneof\"`\n}\n\ntype Domain_Attribute_IntValue struct {\n\tIntValue int64 `protobuf:\"varint,3,opt,name=int_value,json=intValue,proto3,oneof\"`\n}\n\nfunc (*Domain_Attribute_BoolValue) isDomain_Attribute_TypedValue() {}\n\nfunc (*Domain_Attribute_IntValue) isDomain_Attribute_TypedValue() {}\n\nvar File_component_geodata_router_config_proto protoreflect.FileDescriptor\n\nvar file_component_geodata_router_config_proto_rawDesc = []byte{\n\t0x0a, 0x25, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x2f, 0x67, 0x65, 0x6f, 0x64,\n\t0x61, 0x74, 0x61, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69,\n\t0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1e, 0x63, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x63,\n\t0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x6f, 0x64, 0x61, 0x74, 0x61,\n\t0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x22, 0xd1, 0x02, 0x0a, 0x06, 0x44, 0x6f, 0x6d, 0x61,\n\t0x69, 0x6e, 0x12, 0x3f, 0x0a, 0x04, 0x74, 0x79, 0x70, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0e,\n\t0x32, 0x2b, 0x2e, 0x63, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65,\n\t0x6e, 0x74, 0x2e, 0x67, 0x65, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65,\n\t0x72, 0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04, 0x74,\n\t0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01,\n\t0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x4e, 0x0a, 0x09, 0x61, 0x74, 0x74,\n\t0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x18, 0x03, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x30, 0x2e, 0x63,\n\t0x6c, 0x61, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x2e, 0x67,\n\t0x65, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x44, 0x6f,\n\t0x6d, 0x61, 0x69, 0x6e, 0x2e, 0x41, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x52, 0x09,\n\t0x61, 0x74, 0x74, 0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x1a, 0x6c, 0x0a, 0x09, 0x41, 0x74, 0x74,\n\t0x72, 0x69, 0x62, 0x75, 0x74, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20,\n\t0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x1f, 0x0a, 0x0a, 0x62, 0x6f, 0x6f, 0x6c,\n\t0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x08, 0x48, 0x00, 0x52, 0x09,\n\t0x62, 0x6f, 0x6f, 0x6c, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x12, 0x1d, 0x0a, 0x09, 0x69, 0x6e, 0x74,\n\t0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x03, 0x20, 0x01, 0x28, 0x03, 0x48, 0x00, 0x52, 0x08,\n\t0x69, 0x6e, 0x74, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x74, 0x79, 0x70, 0x65,\n\t0x64, 0x5f, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x22, 0x32, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12,\n\t0x09, 0x0a, 0x05, 0x50, 0x6c, 0x61, 0x69, 0x6e, 0x10, 0x00, 0x12, 0x09, 0x0a, 0x05, 0x52, 0x65,\n\t0x67, 0x65, 0x78, 0x10, 0x01, 0x12, 0x0a, 0x0a, 0x06, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x10,\n\t0x02, 0x12, 0x08, 0x0a, 0x04, 0x46, 0x75, 0x6c, 0x6c, 0x10, 0x03, 0x22, 0x2e, 0x0a, 0x04, 0x43,\n\t0x49, 0x44, 0x52, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x70, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c, 0x52,\n\t0x02, 0x69, 0x70, 0x12, 0x16, 0x0a, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x02, 0x20,\n\t0x01, 0x28, 0x0d, 0x52, 0x06, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x22, 0x89, 0x01, 0x0a, 0x05,\n\t0x47, 0x65, 0x6f, 0x49, 0x50, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79,\n\t0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x75,\n\t0x6e, 0x74, 0x72, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x12, 0x38, 0x0a, 0x04, 0x63, 0x69, 0x64, 0x72,\n\t0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x63, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x63,\n\t0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x6f, 0x64, 0x61, 0x74, 0x61,\n\t0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x43, 0x49, 0x44, 0x52, 0x52, 0x04, 0x63, 0x69,\n\t0x64, 0x72, 0x12, 0x23, 0x0a, 0x0d, 0x72, 0x65, 0x76, 0x65, 0x72, 0x73, 0x65, 0x5f, 0x6d, 0x61,\n\t0x74, 0x63, 0x68, 0x18, 0x03, 0x20, 0x01, 0x28, 0x08, 0x52, 0x0c, 0x72, 0x65, 0x76, 0x65, 0x72,\n\t0x73, 0x65, 0x4d, 0x61, 0x74, 0x63, 0x68, 0x22, 0x48, 0x0a, 0x09, 0x47, 0x65, 0x6f, 0x49, 0x50,\n\t0x4c, 0x69, 0x73, 0x74, 0x12, 0x3b, 0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20,\n\t0x03, 0x28, 0x0b, 0x32, 0x25, 0x2e, 0x63, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x70,\n\t0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x72, 0x6f,\n\t0x75, 0x74, 0x65, 0x72, 0x2e, 0x47, 0x65, 0x6f, 0x49, 0x50, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72,\n\t0x79, 0x22, 0x6c, 0x0a, 0x07, 0x47, 0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x12, 0x21, 0x0a, 0x0c,\n\t0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x18, 0x01, 0x20, 0x01,\n\t0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x75, 0x6e, 0x74, 0x72, 0x79, 0x43, 0x6f, 0x64, 0x65, 0x12,\n\t0x3e, 0x0a, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x18, 0x02, 0x20, 0x03, 0x28, 0x0b, 0x32,\n\t0x26, 0x2e, 0x63, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e,\n\t0x74, 0x2e, 0x67, 0x65, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72,\n\t0x2e, 0x44, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x52, 0x06, 0x64, 0x6f, 0x6d, 0x61, 0x69, 0x6e, 0x22,\n\t0x4c, 0x0a, 0x0b, 0x47, 0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x3d,\n\t0x0a, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x27, 0x2e,\n\t0x63, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x2e,\n\t0x67, 0x65, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x2e, 0x47,\n\t0x65, 0x6f, 0x53, 0x69, 0x74, 0x65, 0x52, 0x05, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x42, 0x7c, 0x0a,\n\t0x22, 0x63, 0x6f, 0x6d, 0x2e, 0x63, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x63, 0x6f, 0x6d, 0x70, 0x6f,\n\t0x6e, 0x65, 0x6e, 0x74, 0x2e, 0x67, 0x65, 0x6f, 0x64, 0x61, 0x74, 0x61, 0x2e, 0x72, 0x6f, 0x75,\n\t0x74, 0x65, 0x72, 0x50, 0x01, 0x5a, 0x33, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f,\n\t0x6d, 0x2f, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x2f, 0x63, 0x6c, 0x61, 0x73,\n\t0x68, 0x2f, 0x63, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x2f, 0x67, 0x65, 0x6f, 0x64,\n\t0x61, 0x74, 0x61, 0x2f, 0x72, 0x6f, 0x75, 0x74, 0x65, 0x72, 0xaa, 0x02, 0x1e, 0x43, 0x6c, 0x61,\n\t0x73, 0x68, 0x2e, 0x43, 0x6f, 0x6d, 0x70, 0x6f, 0x6e, 0x65, 0x6e, 0x74, 0x2e, 0x47, 0x65, 0x6f,\n\t0x64, 0x61, 0x74, 0x61, 0x2e, 0x52, 0x6f, 0x75, 0x74, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f,\n\t0x74, 0x6f, 0x33,\n}\n\nvar (\n\tfile_component_geodata_router_config_proto_rawDescOnce sync.Once\n\tfile_component_geodata_router_config_proto_rawDescData = file_component_geodata_router_config_proto_rawDesc\n)\n\nfunc file_component_geodata_router_config_proto_rawDescGZIP() []byte {\n\tfile_component_geodata_router_config_proto_rawDescOnce.Do(func() {\n\t\tfile_component_geodata_router_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_component_geodata_router_config_proto_rawDescData)\n\t})\n\treturn file_component_geodata_router_config_proto_rawDescData\n}\n\nvar file_component_geodata_router_config_proto_enumTypes = make([]protoimpl.EnumInfo, 1)\nvar file_component_geodata_router_config_proto_msgTypes = make([]protoimpl.MessageInfo, 7)\nvar file_component_geodata_router_config_proto_goTypes = []interface{}{\n\t(Domain_Type)(0),         // 0: mihomo.component.geodata.router.Domain.Type\n\t(*Domain)(nil),           // 1: mihomo.component.geodata.router.Domain\n\t(*CIDR)(nil),             // 2: mihomo.component.geodata.router.CIDR\n\t(*GeoIP)(nil),            // 3: mihomo.component.geodata.router.GeoIP\n\t(*GeoIPList)(nil),        // 4: mihomo.component.geodata.router.GeoIPList\n\t(*GeoSite)(nil),          // 5: mihomo.component.geodata.router.GeoSite\n\t(*GeoSiteList)(nil),      // 6: mihomo.component.geodata.router.GeoSiteList\n\t(*Domain_Attribute)(nil), // 7: mihomo.component.geodata.router.Domain.Attribute\n}\nvar file_component_geodata_router_config_proto_depIdxs = []int32{\n\t0, // 0: mihomo.component.geodata.router.Domain.type:type_name -> mihomo.component.geodata.router.Domain.Type\n\t7, // 1: mihomo.component.geodata.router.Domain.attribute:type_name -> mihomo.component.geodata.router.Domain.Attribute\n\t2, // 2: mihomo.component.geodata.router.GeoIP.cidr:type_name -> mihomo.component.geodata.router.CIDR\n\t3, // 3: mihomo.component.geodata.router.GeoIPList.entry:type_name -> mihomo.component.geodata.router.GeoIP\n\t1, // 4: mihomo.component.geodata.router.GeoSite.domain:type_name -> mihomo.component.geodata.router.Domain\n\t5, // 5: mihomo.component.geodata.router.GeoSiteList.entry:type_name -> mihomo.component.geodata.router.GeoSite\n\t6, // [6:6] is the sub-list for method output_type\n\t6, // [6:6] is the sub-list for method input_type\n\t6, // [6:6] is the sub-list for extension type_name\n\t6, // [6:6] is the sub-list for extension extendee\n\t0, // [0:6] is the sub-list for field type_name\n}\n\nfunc init() { file_component_geodata_router_config_proto_init() }\nfunc file_component_geodata_router_config_proto_init() {\n\tif File_component_geodata_router_config_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_component_geodata_router_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Domain); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_component_geodata_router_config_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*CIDR); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_component_geodata_router_config_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*GeoIP); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_component_geodata_router_config_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*GeoIPList); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_component_geodata_router_config_proto_msgTypes[4].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*GeoSite); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_component_geodata_router_config_proto_msgTypes[5].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*GeoSiteList); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t\tfile_component_geodata_router_config_proto_msgTypes[6].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Domain_Attribute); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\tfile_component_geodata_router_config_proto_msgTypes[6].OneofWrappers = []interface{}{\n\t\t(*Domain_Attribute_BoolValue)(nil),\n\t\t(*Domain_Attribute_IntValue)(nil),\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_component_geodata_router_config_proto_rawDesc,\n\t\t\tNumEnums:      1,\n\t\t\tNumMessages:   7,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   0,\n\t\t},\n\t\tGoTypes:           file_component_geodata_router_config_proto_goTypes,\n\t\tDependencyIndexes: file_component_geodata_router_config_proto_depIdxs,\n\t\tEnumInfos:         file_component_geodata_router_config_proto_enumTypes,\n\t\tMessageInfos:      file_component_geodata_router_config_proto_msgTypes,\n\t}.Build()\n\tFile_component_geodata_router_config_proto = out.File\n\tfile_component_geodata_router_config_proto_rawDesc = nil\n\tfile_component_geodata_router_config_proto_goTypes = nil\n\tfile_component_geodata_router_config_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/geodata/router/config.proto",
    "content": "syntax = \"proto3\";\n\npackage mihomo.component.geodata.router;\noption csharp_namespace = \"Mihomo.Component.Geodata.Router\";\noption go_package = \"github.com/metacubex/mihomo/component/geodata/router\";\noption java_package = \"com.mihomo.component.geodata.router\";\noption java_multiple_files = true;\n\n// Domain for routing decision.\nmessage Domain {\n  // Type of domain value.\n  enum Type {\n    // The value is used as is.\n    Plain = 0;\n    // The value is used as a regular expression.\n    Regex = 1;\n    // The value is a root domain.\n    Domain = 2;\n    // The value is a domain.\n    Full = 3;\n  }\n\n  // Domain matching type.\n  Type type = 1;\n\n  // Domain value.\n  string value = 2;\n\n  message Attribute {\n    string key = 1;\n\n    oneof typed_value {\n      bool bool_value = 2;\n      int64 int_value = 3;\n    }\n  }\n\n  // Attributes of this domain. May be used for filtering.\n  repeated Attribute attribute = 3;\n}\n\n// IP for routing decision, in CIDR form.\nmessage CIDR {\n  // IP address, should be either 4 or 16 bytes.\n  bytes ip = 1;\n\n  // Number of leading ones in the network mask.\n  uint32 prefix = 2;\n}\n\nmessage GeoIP {\n  string country_code = 1;\n  repeated CIDR cidr = 2;\n  bool reverse_match = 3;\n}\n\nmessage GeoIPList {\n  repeated GeoIP entry = 1;\n}\n\nmessage GeoSite {\n  string country_code = 1;\n  repeated Domain domain = 2;\n}\n\nmessage GeoSiteList {\n  repeated GeoSite entry = 1;\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/geodata/standard/standard.go",
    "content": "package standard\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/component/geodata\"\n\t\"github.com/metacubex/mihomo/component/geodata/router\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\n\t\"google.golang.org/protobuf/proto\"\n)\n\nfunc ReadFile(path string) ([]byte, error) {\n\treader, err := os.Open(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer func(reader *os.File) {\n\t\t_ = reader.Close()\n\t}(reader)\n\n\treturn io.ReadAll(reader)\n}\n\nfunc ReadAsset(file string) ([]byte, error) {\n\treturn ReadFile(C.Path.GetAssetLocation(file))\n}\n\nfunc loadIP(geoipBytes []byte, country string) ([]*router.CIDR, error) {\n\tvar geoipList router.GeoIPList\n\tif err := proto.Unmarshal(geoipBytes, &geoipList); err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor _, geoip := range geoipList.Entry {\n\t\tif strings.EqualFold(geoip.CountryCode, country) {\n\t\t\treturn geoip.Cidr, nil\n\t\t}\n\t}\n\n\treturn nil, fmt.Errorf(\"country %s not found\", country)\n}\n\nfunc loadSite(geositeBytes []byte, list string) ([]*router.Domain, error) {\n\tvar geositeList router.GeoSiteList\n\tif err := proto.Unmarshal(geositeBytes, &geositeList); err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor _, site := range geositeList.Entry {\n\t\tif strings.EqualFold(site.CountryCode, list) {\n\t\t\treturn site.Domain, nil\n\t\t}\n\t}\n\n\treturn nil, fmt.Errorf(\"list %s not found\", list)\n}\n\ntype standardLoader struct{}\n\nfunc (d standardLoader) LoadSiteByPath(filename, list string) ([]*router.Domain, error) {\n\tgeositeBytes, err := ReadAsset(filename)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to open file: %s, base error: %s\", filename, err.Error())\n\t}\n\treturn loadSite(geositeBytes, list)\n}\n\nfunc (d standardLoader) LoadSiteByBytes(geositeBytes []byte, list string) ([]*router.Domain, error) {\n\treturn loadSite(geositeBytes, list)\n}\n\nfunc (d standardLoader) LoadIPByPath(filename, country string) ([]*router.CIDR, error) {\n\tgeoipBytes, err := ReadAsset(filename)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to open file: %s, base error: %s\", filename, err.Error())\n\t}\n\treturn loadIP(geoipBytes, country)\n}\n\nfunc (d standardLoader) LoadIPByBytes(geoipBytes []byte, country string) ([]*router.CIDR, error) {\n\treturn loadIP(geoipBytes, country)\n}\n\nfunc init() {\n\tgeodata.RegisterGeoDataLoaderImplementationCreator(\"standard\", func() geodata.LoaderImplementation {\n\t\treturn standardLoader{}\n\t})\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/geodata/strmatcher/ac_automaton_matcher.go",
    "content": "package strmatcher\n\nimport (\n\tlist \"github.com/bahlo/generic-list-go\"\n)\n\nconst validCharCount = 53\n\ntype MatchType struct {\n\tmatchType Type\n\texist     bool\n}\n\nconst (\n\tTrieEdge bool = true\n\tFailEdge bool = false\n)\n\ntype Edge struct {\n\tedgeType bool\n\tnextNode int\n}\n\ntype ACAutomaton struct {\n\ttrie   [][validCharCount]Edge\n\tfail   []int\n\texists []MatchType\n\tcount  int\n}\n\nfunc newNode() [validCharCount]Edge {\n\tvar s [validCharCount]Edge\n\tfor i := range s {\n\t\ts[i] = Edge{\n\t\t\tedgeType: FailEdge,\n\t\t\tnextNode: 0,\n\t\t}\n\t}\n\treturn s\n}\n\nvar char2Index = [...]int{\n\t'A':  0,\n\t'a':  0,\n\t'B':  1,\n\t'b':  1,\n\t'C':  2,\n\t'c':  2,\n\t'D':  3,\n\t'd':  3,\n\t'E':  4,\n\t'e':  4,\n\t'F':  5,\n\t'f':  5,\n\t'G':  6,\n\t'g':  6,\n\t'H':  7,\n\t'h':  7,\n\t'I':  8,\n\t'i':  8,\n\t'J':  9,\n\t'j':  9,\n\t'K':  10,\n\t'k':  10,\n\t'L':  11,\n\t'l':  11,\n\t'M':  12,\n\t'm':  12,\n\t'N':  13,\n\t'n':  13,\n\t'O':  14,\n\t'o':  14,\n\t'P':  15,\n\t'p':  15,\n\t'Q':  16,\n\t'q':  16,\n\t'R':  17,\n\t'r':  17,\n\t'S':  18,\n\t's':  18,\n\t'T':  19,\n\t't':  19,\n\t'U':  20,\n\t'u':  20,\n\t'V':  21,\n\t'v':  21,\n\t'W':  22,\n\t'w':  22,\n\t'X':  23,\n\t'x':  23,\n\t'Y':  24,\n\t'y':  24,\n\t'Z':  25,\n\t'z':  25,\n\t'!':  26,\n\t'$':  27,\n\t'&':  28,\n\t'\\'': 29,\n\t'(':  30,\n\t')':  31,\n\t'*':  32,\n\t'+':  33,\n\t',':  34,\n\t';':  35,\n\t'=':  36,\n\t':':  37,\n\t'%':  38,\n\t'-':  39,\n\t'.':  40,\n\t'_':  41,\n\t'~':  42,\n\t'0':  43,\n\t'1':  44,\n\t'2':  45,\n\t'3':  46,\n\t'4':  47,\n\t'5':  48,\n\t'6':  49,\n\t'7':  50,\n\t'8':  51,\n\t'9':  52,\n}\n\nfunc NewACAutomaton() *ACAutomaton {\n\tac := new(ACAutomaton)\n\tac.trie = append(ac.trie, newNode())\n\tac.fail = append(ac.fail, 0)\n\tac.exists = append(ac.exists, MatchType{\n\t\tmatchType: Full,\n\t\texist:     false,\n\t})\n\treturn ac\n}\n\nfunc (ac *ACAutomaton) Add(domain string, t Type) {\n\tnode := 0\n\tfor i := len(domain) - 1; i >= 0; i-- {\n\t\tidx := char2Index[domain[i]]\n\t\tif ac.trie[node][idx].nextNode == 0 {\n\t\t\tac.count++\n\t\t\tif len(ac.trie) < ac.count+1 {\n\t\t\t\tac.trie = append(ac.trie, newNode())\n\t\t\t\tac.fail = append(ac.fail, 0)\n\t\t\t\tac.exists = append(ac.exists, MatchType{\n\t\t\t\t\tmatchType: Full,\n\t\t\t\t\texist:     false,\n\t\t\t\t})\n\t\t\t}\n\t\t\tac.trie[node][idx] = Edge{\n\t\t\t\tedgeType: TrieEdge,\n\t\t\t\tnextNode: ac.count,\n\t\t\t}\n\t\t}\n\t\tnode = ac.trie[node][idx].nextNode\n\t}\n\tac.exists[node] = MatchType{\n\t\tmatchType: t,\n\t\texist:     true,\n\t}\n\tswitch t {\n\tcase Domain:\n\t\tac.exists[node] = MatchType{\n\t\t\tmatchType: Full,\n\t\t\texist:     true,\n\t\t}\n\t\tidx := char2Index['.']\n\t\tif ac.trie[node][idx].nextNode == 0 {\n\t\t\tac.count++\n\t\t\tif len(ac.trie) < ac.count+1 {\n\t\t\t\tac.trie = append(ac.trie, newNode())\n\t\t\t\tac.fail = append(ac.fail, 0)\n\t\t\t\tac.exists = append(ac.exists, MatchType{\n\t\t\t\t\tmatchType: Full,\n\t\t\t\t\texist:     false,\n\t\t\t\t})\n\t\t\t}\n\t\t\tac.trie[node][idx] = Edge{\n\t\t\t\tedgeType: TrieEdge,\n\t\t\t\tnextNode: ac.count,\n\t\t\t}\n\t\t}\n\t\tnode = ac.trie[node][idx].nextNode\n\t\tac.exists[node] = MatchType{\n\t\t\tmatchType: t,\n\t\t\texist:     true,\n\t\t}\n\tdefault:\n\t\tbreak\n\t}\n}\n\nfunc (ac *ACAutomaton) Build() {\n\tqueue := list.New[Edge]()\n\tfor i := 0; i < validCharCount; i++ {\n\t\tif ac.trie[0][i].nextNode != 0 {\n\t\t\tqueue.PushBack(ac.trie[0][i])\n\t\t}\n\t}\n\tfor {\n\t\tfront := queue.Front()\n\t\tif front == nil {\n\t\t\tbreak\n\t\t} else {\n\t\t\tnode := front.Value.nextNode\n\t\t\tqueue.Remove(front)\n\t\t\tfor i := 0; i < validCharCount; i++ {\n\t\t\t\tif ac.trie[node][i].nextNode != 0 {\n\t\t\t\t\tac.fail[ac.trie[node][i].nextNode] = ac.trie[ac.fail[node]][i].nextNode\n\t\t\t\t\tqueue.PushBack(ac.trie[node][i])\n\t\t\t\t} else {\n\t\t\t\t\tac.trie[node][i] = Edge{\n\t\t\t\t\t\tedgeType: FailEdge,\n\t\t\t\t\t\tnextNode: ac.trie[ac.fail[node]][i].nextNode,\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (ac *ACAutomaton) Match(s string) bool {\n\tnode := 0\n\tfullMatch := true\n\t// 1. the match string is all through trie edge. FULL MATCH or DOMAIN\n\t// 2. the match string is through a fail edge. NOT FULL MATCH\n\t// 2.1 Through a fail edge, but there exists a valid node. SUBSTR\n\tfor i := len(s) - 1; i >= 0; i-- {\n\t\tidx := char2Index[s[i]]\n\t\tfullMatch = fullMatch && ac.trie[node][idx].edgeType\n\t\tnode = ac.trie[node][idx].nextNode\n\t\tswitch ac.exists[node].matchType {\n\t\tcase Substr:\n\t\t\treturn true\n\t\tcase Domain:\n\t\t\tif fullMatch {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\treturn fullMatch && ac.exists[node].exist\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/geodata/strmatcher/matchers.go",
    "content": "package strmatcher\n\nimport (\n\t\"regexp\"\n\t\"strings\"\n)\n\ntype fullMatcher string\n\nfunc (m fullMatcher) Match(s string) bool {\n\treturn string(m) == s\n}\n\nfunc (m fullMatcher) String() string {\n\treturn \"full:\" + string(m)\n}\n\ntype substrMatcher string\n\nfunc (m substrMatcher) Match(s string) bool {\n\treturn strings.Contains(s, string(m))\n}\n\nfunc (m substrMatcher) String() string {\n\treturn \"keyword:\" + string(m)\n}\n\ntype domainMatcher string\n\nfunc (m domainMatcher) Match(s string) bool {\n\tpattern := string(m)\n\tif !strings.HasSuffix(s, pattern) {\n\t\treturn false\n\t}\n\treturn len(s) == len(pattern) || s[len(s)-len(pattern)-1] == '.'\n}\n\nfunc (m domainMatcher) String() string {\n\treturn \"domain:\" + string(m)\n}\n\ntype regexMatcher struct {\n\tpattern *regexp.Regexp\n}\n\nfunc (m *regexMatcher) Match(s string) bool {\n\treturn m.pattern.MatchString(s)\n}\n\nfunc (m *regexMatcher) String() string {\n\treturn \"regexp:\" + m.pattern.String()\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/geodata/strmatcher/mph_matcher.go",
    "content": "package strmatcher\n\nimport (\n\t\"math/bits\"\n\t\"regexp\"\n\t\"sort\"\n\t\"strings\"\n\t\"unsafe\"\n)\n\n// PrimeRK is the prime base used in Rabin-Karp algorithm.\nconst PrimeRK = 16777619\n\n// calculate the rolling murmurHash of given string\nfunc RollingHash(s string) uint32 {\n\th := uint32(0)\n\tfor i := len(s) - 1; i >= 0; i-- {\n\t\th = h*PrimeRK + uint32(s[i])\n\t}\n\treturn h\n}\n\n// A MphMatcherGroup is divided into three parts:\n// 1. `full` and `domain` patterns are matched by Rabin-Karp algorithm and minimal perfect hash table;\n// 2. `substr` patterns are matched by ac automaton;\n// 3. `regex` patterns are matched with the regex library.\ntype MphMatcherGroup struct {\n\tac            *ACAutomaton\n\totherMatchers []matcherEntry\n\trules         []string\n\tlevel0        []uint32\n\tlevel0Mask    int\n\tlevel1        []uint32\n\tlevel1Mask    int\n\tcount         uint32\n\truleMap       *map[string]uint32\n}\n\nfunc (g *MphMatcherGroup) AddFullOrDomainPattern(pattern string, t Type) {\n\th := RollingHash(pattern)\n\tswitch t {\n\tcase Domain:\n\t\t(*g.ruleMap)[\".\"+pattern] = h*PrimeRK + uint32('.')\n\t\tfallthrough\n\tcase Full:\n\t\t(*g.ruleMap)[pattern] = h\n\tdefault:\n\t}\n}\n\nfunc NewMphMatcherGroup() *MphMatcherGroup {\n\treturn &MphMatcherGroup{\n\t\tac:            nil,\n\t\totherMatchers: nil,\n\t\trules:         nil,\n\t\tlevel0:        nil,\n\t\tlevel0Mask:    0,\n\t\tlevel1:        nil,\n\t\tlevel1Mask:    0,\n\t\tcount:         1,\n\t\truleMap:       &map[string]uint32{},\n\t}\n}\n\n// AddPattern adds a pattern to MphMatcherGroup\nfunc (g *MphMatcherGroup) AddPattern(pattern string, t Type) (uint32, error) {\n\tswitch t {\n\tcase Substr:\n\t\tif g.ac == nil {\n\t\t\tg.ac = NewACAutomaton()\n\t\t}\n\t\tg.ac.Add(pattern, t)\n\tcase Full, Domain:\n\t\tpattern = strings.ToLower(pattern)\n\t\tg.AddFullOrDomainPattern(pattern, t)\n\tcase Regex:\n\t\tr, err := regexp.Compile(pattern)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tg.otherMatchers = append(g.otherMatchers, matcherEntry{\n\t\t\tm:  &regexMatcher{pattern: r},\n\t\t\tid: g.count,\n\t\t})\n\tdefault:\n\t\tpanic(\"Unknown type\")\n\t}\n\treturn g.count, nil\n}\n\n// Build builds a minimal perfect hash table and ac automaton from insert rules\nfunc (g *MphMatcherGroup) Build() {\n\tif g.ac != nil {\n\t\tg.ac.Build()\n\t}\n\tkeyLen := len(*g.ruleMap)\n\tif keyLen == 0 {\n\t\tkeyLen = 1\n\t\t(*g.ruleMap)[\"empty___\"] = RollingHash(\"empty___\")\n\t}\n\tg.level0 = make([]uint32, nextPow2(keyLen/4))\n\tg.level0Mask = len(g.level0) - 1\n\tg.level1 = make([]uint32, nextPow2(keyLen))\n\tg.level1Mask = len(g.level1) - 1\n\tsparseBuckets := make([][]int, len(g.level0))\n\tvar ruleIdx int\n\tfor rule, hash := range *g.ruleMap {\n\t\tn := int(hash) & g.level0Mask\n\t\tg.rules = append(g.rules, rule)\n\t\tsparseBuckets[n] = append(sparseBuckets[n], ruleIdx)\n\t\truleIdx++\n\t}\n\tg.ruleMap = nil\n\tvar buckets []indexBucket\n\tfor n, vals := range sparseBuckets {\n\t\tif len(vals) > 0 {\n\t\t\tbuckets = append(buckets, indexBucket{n, vals})\n\t\t}\n\t}\n\tsort.Sort(bySize(buckets))\n\n\tocc := make([]bool, len(g.level1))\n\tvar tmpOcc []int\n\tfor _, bucket := range buckets {\n\t\tseed := uint32(0)\n\t\tfor {\n\t\t\tfindSeed := true\n\t\t\ttmpOcc = tmpOcc[:0]\n\t\t\tfor _, i := range bucket.vals {\n\t\t\t\tn := int(strhashFallback(unsafe.Pointer(&g.rules[i]), uintptr(seed))) & g.level1Mask\n\t\t\t\tif occ[n] {\n\t\t\t\t\tfor _, n := range tmpOcc {\n\t\t\t\t\t\tocc[n] = false\n\t\t\t\t\t}\n\t\t\t\t\tseed++\n\t\t\t\t\tfindSeed = false\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tocc[n] = true\n\t\t\t\ttmpOcc = append(tmpOcc, n)\n\t\t\t\tg.level1[n] = uint32(i)\n\t\t\t}\n\t\t\tif findSeed {\n\t\t\t\tg.level0[bucket.n] = seed\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc nextPow2(v int) int {\n\tif v <= 1 {\n\t\treturn 1\n\t}\n\tconst MaxUInt = ^uint(0)\n\tn := (MaxUInt >> bits.LeadingZeros(uint(v))) + 1\n\treturn int(n)\n}\n\n// Lookup searches for s in t and returns its index and whether it was found.\nfunc (g *MphMatcherGroup) Lookup(h uint32, s string) bool {\n\ti0 := int(h) & g.level0Mask\n\tseed := g.level0[i0]\n\ti1 := int(strhashFallback(unsafe.Pointer(&s), uintptr(seed))) & g.level1Mask\n\tn := g.level1[i1]\n\treturn s == g.rules[int(n)]\n}\n\n// Match implements IndexMatcher.Match.\nfunc (g *MphMatcherGroup) Match(pattern string) []uint32 {\n\tresult := []uint32{}\n\thash := uint32(0)\n\tfor i := len(pattern) - 1; i >= 0; i-- {\n\t\thash = hash*PrimeRK + uint32(pattern[i])\n\t\tif pattern[i] == '.' {\n\t\t\tif g.Lookup(hash, pattern[i:]) {\n\t\t\t\tresult = append(result, 1)\n\t\t\t\treturn result\n\t\t\t}\n\t\t}\n\t}\n\tif g.Lookup(hash, pattern) {\n\t\tresult = append(result, 1)\n\t\treturn result\n\t}\n\tif g.ac != nil && g.ac.Match(pattern) {\n\t\tresult = append(result, 1)\n\t\treturn result\n\t}\n\tfor _, e := range g.otherMatchers {\n\t\tif e.m.Match(pattern) {\n\t\t\tresult = append(result, e.id)\n\t\t\treturn result\n\t\t}\n\t}\n\treturn nil\n}\n\ntype indexBucket struct {\n\tn    int\n\tvals []int\n}\n\ntype bySize []indexBucket\n\nfunc (s bySize) Len() int           { return len(s) }\nfunc (s bySize) Less(i, j int) bool { return len(s[i].vals) > len(s[j].vals) }\nfunc (s bySize) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }\n\ntype stringStruct struct {\n\tstr unsafe.Pointer\n\tlen int\n}\n\nfunc strhashFallback(a unsafe.Pointer, h uintptr) uintptr {\n\tx := (*stringStruct)(a)\n\treturn memhashFallback(x.str, h, uintptr(x.len))\n}\n\nconst (\n\t// Constants for multiplication: four random odd 64-bit numbers.\n\tm1 = 16877499708836156737\n\tm2 = 2820277070424839065\n\tm3 = 9497967016996688599\n\tm4 = 15839092249703872147\n)\n\nvar hashkey = [4]uintptr{1, 1, 1, 1}\n\nfunc memhashFallback(p unsafe.Pointer, seed, s uintptr) uintptr {\n\th := uint64(seed + s*hashkey[0])\ntail:\n\tswitch {\n\tcase s == 0:\n\tcase s < 4:\n\t\th ^= uint64(*(*byte)(p))\n\t\th ^= uint64(*(*byte)(unsafe.Add(p, s>>1))) << 8\n\t\th ^= uint64(*(*byte)(unsafe.Add(p, s-1))) << 16\n\t\th = bits.RotateLeft64(h*m1, 31) * m2\n\tcase s <= 8:\n\t\th ^= uint64(readUnaligned32(p))\n\t\th ^= uint64(readUnaligned32(unsafe.Add(p, s-4))) << 32\n\t\th = bits.RotateLeft64(h*m1, 31) * m2\n\tcase s <= 16:\n\t\th ^= readUnaligned64(p)\n\t\th = bits.RotateLeft64(h*m1, 31) * m2\n\t\th ^= readUnaligned64(unsafe.Add(p, s-8))\n\t\th = bits.RotateLeft64(h*m1, 31) * m2\n\tcase s <= 32:\n\t\th ^= readUnaligned64(p)\n\t\th = bits.RotateLeft64(h*m1, 31) * m2\n\t\th ^= readUnaligned64(unsafe.Add(p, 8))\n\t\th = bits.RotateLeft64(h*m1, 31) * m2\n\t\th ^= readUnaligned64(unsafe.Add(p, s-16))\n\t\th = bits.RotateLeft64(h*m1, 31) * m2\n\t\th ^= readUnaligned64(unsafe.Add(p, s-8))\n\t\th = bits.RotateLeft64(h*m1, 31) * m2\n\tdefault:\n\t\tv1 := h\n\t\tv2 := uint64(seed * hashkey[1])\n\t\tv3 := uint64(seed * hashkey[2])\n\t\tv4 := uint64(seed * hashkey[3])\n\t\tfor s >= 32 {\n\t\t\tv1 ^= readUnaligned64(p)\n\t\t\tv1 = bits.RotateLeft64(v1*m1, 31) * m2\n\t\t\tp = unsafe.Add(p, 8)\n\t\t\tv2 ^= readUnaligned64(p)\n\t\t\tv2 = bits.RotateLeft64(v2*m2, 31) * m3\n\t\t\tp = unsafe.Add(p, 8)\n\t\t\tv3 ^= readUnaligned64(p)\n\t\t\tv3 = bits.RotateLeft64(v3*m3, 31) * m4\n\t\t\tp = unsafe.Add(p, 8)\n\t\t\tv4 ^= readUnaligned64(p)\n\t\t\tv4 = bits.RotateLeft64(v4*m4, 31) * m1\n\t\t\tp = unsafe.Add(p, 8)\n\t\t\ts -= 32\n\t\t}\n\t\th = v1 ^ v2 ^ v3 ^ v4\n\t\tgoto tail\n\t}\n\n\th ^= h >> 29\n\th *= m3\n\th ^= h >> 32\n\treturn uintptr(h)\n}\n\nfunc readUnaligned32(p unsafe.Pointer) uint32 {\n\tq := (*[4]byte)(p)\n\treturn uint32(q[0]) | uint32(q[1])<<8 | uint32(q[2])<<16 | uint32(q[3])<<24\n}\n\nfunc readUnaligned64(p unsafe.Pointer) uint64 {\n\tq := (*[8]byte)(p)\n\treturn uint64(q[0]) | uint64(q[1])<<8 | uint64(q[2])<<16 | uint64(q[3])<<24 | uint64(q[4])<<32 | uint64(q[5])<<40 | uint64(q[6])<<48 | uint64(q[7])<<56\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/geodata/strmatcher/package_info.go",
    "content": "// Modified from: https://github.com/v2fly/v2ray-core/tree/master/common/strmatcher\n// License: MIT\n\npackage strmatcher\n"
  },
  {
    "path": "core/Clash.Meta/component/geodata/strmatcher/strmatcher.go",
    "content": "package strmatcher\n\nimport (\n\t\"regexp\"\n)\n\n// Matcher is the interface to determine a string matches a pattern.\ntype Matcher interface {\n\t// Match returns true if the given string matches a predefined pattern.\n\tMatch(string) bool\n\tString() string\n}\n\n// Type is the type of the matcher.\ntype Type byte\n\nconst (\n\t// Full is the type of matcher that the input string must exactly equal to the pattern.\n\tFull Type = iota\n\t// Substr is the type of matcher that the input string must contain the pattern as a sub-string.\n\tSubstr\n\t// Domain is the type of matcher that the input string must be a sub-domain or itself of the pattern.\n\tDomain\n\t// Regex is the type of matcher that the input string must matches the regular-expression pattern.\n\tRegex\n)\n\n// New creates a new Matcher based on the given pattern.\nfunc (t Type) New(pattern string) (Matcher, error) {\n\t// 1. regex matching is case-sensitive\n\tswitch t {\n\tcase Full:\n\t\treturn fullMatcher(pattern), nil\n\tcase Substr:\n\t\treturn substrMatcher(pattern), nil\n\tcase Domain:\n\t\treturn domainMatcher(pattern), nil\n\tcase Regex:\n\t\tr, err := regexp.Compile(pattern)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn &regexMatcher{\n\t\t\tpattern: r,\n\t\t}, nil\n\tdefault:\n\t\tpanic(\"Unknown type\")\n\t}\n}\n\n// IndexMatcher is the interface for matching with a group of matchers.\ntype IndexMatcher interface {\n\t// Match returns the index of a matcher that matches the input. It returns empty array if no such matcher exists.\n\tMatch(input string) []uint32\n}\n\ntype matcherEntry struct {\n\tm  Matcher\n\tid uint32\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/geodata/utils.go",
    "content": "package geodata\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/common/singleflight\"\n\t\"github.com/metacubex/mihomo/component/geodata/router\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\nvar (\n\tgeoMode        bool\n\tgeoLoaderName  = \"memconservative\"\n\tgeoSiteMatcher = \"succinct\"\n)\n\n//  geoLoaderName = \"standard\"\n\nfunc GeodataMode() bool {\n\treturn geoMode\n}\n\nfunc LoaderName() string {\n\treturn geoLoaderName\n}\n\nfunc SiteMatcherName() string {\n\treturn geoSiteMatcher\n}\n\nfunc SetGeodataMode(newGeodataMode bool) {\n\tgeoMode = newGeodataMode\n}\n\nfunc SetLoader(newLoader string) {\n\tif newLoader == \"memc\" {\n\t\tnewLoader = \"memconservative\"\n\t}\n\tgeoLoaderName = newLoader\n}\n\nfunc SetSiteMatcher(newMatcher string) {\n\tswitch newMatcher {\n\tcase \"mph\", \"hybrid\":\n\t\tgeoSiteMatcher = \"mph\"\n\tdefault:\n\t\tgeoSiteMatcher = \"succinct\"\n\t}\n}\n\nfunc Verify(name string) error {\n\tswitch name {\n\tcase C.GeositeName:\n\t\t_, err := LoadGeoSiteMatcher(\"CN\")\n\t\treturn err\n\tcase C.GeoipName:\n\t\t_, err := LoadGeoIPMatcher(\"CN\")\n\t\treturn err\n\tdefault:\n\t\treturn fmt.Errorf(\"not support name\")\n\t}\n}\n\nvar loadGeoSiteMatcherListSF = singleflight.Group[[]*router.Domain]{StoreResult: true}\nvar loadGeoSiteMatcherSF = singleflight.Group[router.DomainMatcher]{StoreResult: true}\n\nfunc LoadGeoSiteMatcher(countryCode string) (router.DomainMatcher, error) {\n\tif countryCode == \"\" {\n\t\treturn nil, fmt.Errorf(\"country code could not be empty\")\n\t}\n\n\tnot := false\n\tif countryCode[0] == '!' {\n\t\tnot = true\n\t\tcountryCode = countryCode[1:]\n\t\tif countryCode == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"country code could not be empty\")\n\t\t}\n\t}\n\tcountryCode = strings.ToLower(countryCode)\n\n\tparts := strings.Split(countryCode, \"@\")\n\tlistName := strings.TrimSpace(parts[0])\n\tattrVal := parts[1:]\n\tattrs := parseAttrs(attrVal)\n\n\tif listName == \"\" {\n\t\treturn nil, fmt.Errorf(\"empty listname in rule: %s\", countryCode)\n\t}\n\n\tmatcherName := listName\n\tif !attrs.IsEmpty() {\n\t\tmatcherName += \"@\" + attrs.String()\n\t}\n\tmatcher, err, shared := loadGeoSiteMatcherSF.Do(matcherName, func() (router.DomainMatcher, error) {\n\t\tlog.Infoln(\"Load GeoSite rule: %s\", matcherName)\n\t\tdomains, err, shared := loadGeoSiteMatcherListSF.Do(listName, func() ([]*router.Domain, error) {\n\t\t\tgeoLoader, err := GetGeoDataLoader(geoLoaderName)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn geoLoader.LoadGeoSite(listName)\n\t\t})\n\t\tif err != nil {\n\t\t\tif !shared {\n\t\t\t\tloadGeoSiteMatcherListSF.Forget(listName) // don't store the error result\n\t\t\t}\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif attrs.IsEmpty() {\n\t\t\tif strings.Contains(countryCode, \"@\") {\n\t\t\t\tlog.Warnln(\"empty attribute list: %s\", countryCode)\n\t\t\t}\n\t\t} else {\n\t\t\tfilteredDomains := make([]*router.Domain, 0, len(domains))\n\t\t\thasAttrMatched := false\n\t\t\tfor _, domain := range domains {\n\t\t\t\tif attrs.Match(domain) {\n\t\t\t\t\thasAttrMatched = true\n\t\t\t\t\tfilteredDomains = append(filteredDomains, domain)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif !hasAttrMatched {\n\t\t\t\tlog.Warnln(\"attribute match no rule: geosite: %s\", countryCode)\n\t\t\t}\n\t\t\tdomains = filteredDomains\n\t\t}\n\n\t\t/**\n\t\tlinear: linear algorithm\n\t\tmatcher, err := router.NewDomainMatcher(domains)\n\t\tmph：minimal perfect hash algorithm\n\t\t*/\n\t\tif geoSiteMatcher == \"mph\" {\n\t\t\treturn router.NewMphMatcherGroup(domains)\n\t\t} else {\n\t\t\treturn router.NewSuccinctMatcherGroup(domains)\n\t\t}\n\t})\n\tif err != nil {\n\t\tif !shared {\n\t\t\tloadGeoSiteMatcherSF.Forget(matcherName) // don't store the error result\n\t\t}\n\t\treturn nil, err\n\t}\n\tif not {\n\t\tmatcher = router.NewNotDomainMatcherGroup(matcher)\n\t}\n\n\treturn matcher, nil\n}\n\nvar loadGeoIPMatcherSF = singleflight.Group[router.IPMatcher]{StoreResult: true}\n\nfunc LoadGeoIPMatcher(country string) (router.IPMatcher, error) {\n\tif len(country) == 0 {\n\t\treturn nil, fmt.Errorf(\"country code could not be empty\")\n\t}\n\n\tnot := false\n\tif country[0] == '!' {\n\t\tnot = true\n\t\tcountry = country[1:]\n\t}\n\tcountry = strings.ToLower(country)\n\n\tmatcher, err, shared := loadGeoIPMatcherSF.Do(country, func() (router.IPMatcher, error) {\n\t\tlog.Infoln(\"Load GeoIP rule: %s\", country)\n\t\tgeoLoader, err := GetGeoDataLoader(geoLoaderName)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tcidrList, err := geoLoader.LoadGeoIP(country)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn router.NewGeoIPMatcher(cidrList)\n\t})\n\tif err != nil {\n\t\tif !shared {\n\t\t\tloadGeoIPMatcherSF.Forget(country) // don't store the error result\n\t\t\tlog.Warnln(\"Load GeoIP rule: %s\", country)\n\t\t}\n\t\treturn nil, err\n\t}\n\tif not {\n\t\tmatcher = router.NewNotIpMatcherGroup(matcher)\n\t}\n\treturn matcher, nil\n}\n\nfunc ClearGeoSiteCache() {\n\tloadGeoSiteMatcherListSF.Reset()\n\tloadGeoSiteMatcherSF.Reset()\n}\n\nfunc ClearGeoIPCache() {\n\tloadGeoIPMatcherSF.Reset()\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/http/http.go",
    "content": "package http\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"net\"\n\tURL \"net/url\"\n\t\"runtime\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/component/ca\"\n\t\"github.com/metacubex/mihomo/component/dialer\"\n\t\"github.com/metacubex/mihomo/listener/inner\"\n\n\t\"github.com/metacubex/http\"\n)\n\nvar (\n\tua string\n)\n\nfunc UA() string {\n\treturn ua\n}\n\nfunc SetUA(UA string) {\n\tua = UA\n}\n\nfunc HttpRequest(ctx context.Context, url, method string, header map[string][]string, body io.Reader, options ...Option) (*http.Response, error) {\n\topt := option{}\n\tfor _, o := range options {\n\t\to(&opt)\n\t}\n\tmethod = strings.ToUpper(method)\n\turlRes, err := URL.Parse(url)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treq, err := http.NewRequest(method, urlRes.String(), body)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor k, v := range header {\n\t\tfor _, v := range v {\n\t\t\treq.Header.Add(k, v)\n\t\t}\n\t}\n\n\tif req.Header.Get(\"User-Agent\") == \"\" {\n\t\treq.Header.Set(\"User-Agent\", UA())\n\t}\n\n\tif user := urlRes.User; user != nil {\n\t\tpassword, _ := user.Password()\n\t\treq.SetBasicAuth(user.Username(), password)\n\t}\n\n\treq = req.WithContext(ctx)\n\n\ttlsConfig, err := ca.GetTLSConfig(opt.caOption)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttransport := &http.Transport{\n\t\t// from http.DefaultTransport\n\t\tDisableKeepAlives:     runtime.GOOS == \"android\",\n\t\tMaxIdleConns:          100,\n\t\tIdleConnTimeout:       30 * time.Second,\n\t\tTLSHandshakeTimeout:   10 * time.Second,\n\t\tExpectContinueTimeout: 1 * time.Second,\n\t\tDialContext: func(ctx context.Context, network, address string) (net.Conn, error) {\n\t\t\tif conn, err := inner.HandleTcp(inner.GetTunnel(), address, opt.specialProxy); err == nil {\n\t\t\t\treturn conn, nil\n\t\t\t} else {\n\t\t\t\treturn dialer.DialContext(ctx, network, address)\n\t\t\t}\n\t\t},\n\t\tTLSClientConfig: tlsConfig,\n\t}\n\n\tclient := http.Client{Transport: transport}\n\treturn client.Do(req)\n}\n\ntype Option func(opt *option)\n\ntype option struct {\n\tspecialProxy string\n\tcaOption     ca.Option\n}\n\nfunc WithSpecialProxy(name string) Option {\n\treturn func(opt *option) {\n\t\topt.specialProxy = name\n\t}\n}\n\nfunc WithCAOption(caOption ca.Option) Option {\n\treturn func(opt *option) {\n\t\topt.caOption = caOption\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/iface/iface.go",
    "content": "package iface\n\nimport (\n\t\"errors\"\n\t\"net\"\n\t\"net/netip\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/singledo\"\n\n\t\"github.com/metacubex/bart\"\n)\n\ntype Interface struct {\n\tIndex        int\n\tMTU          int\n\tName         string\n\tHardwareAddr net.HardwareAddr\n\tFlags        net.Flags\n\tAddresses    []netip.Prefix\n}\n\nvar (\n\tErrIfaceNotFound = errors.New(\"interface not found\")\n\tErrAddrNotFound  = errors.New(\"addr not found\")\n)\n\nvar (\n\tifaceLogMu         sync.Mutex\n\tifaceLogLastTime   time.Time\n\tifaceLogCount      int\n\tifaceLogSuppressed bool\n)\n\ntype ifaceCache struct {\n\tifMapByName map[string]*Interface\n\tifMapByAddr map[netip.Addr]*Interface\n\tifTable     bart.Table[*Interface]\n}\n\nvar caches = singledo.NewSingle[*ifaceCache](time.Second * 20)\n\nfunc ShouldLogIfaceError() bool {\n\tifaceLogMu.Lock()\n\tdefer ifaceLogMu.Unlock()\n\n\tnow := time.Now()\n\tif now.Sub(ifaceLogLastTime) >= time.Second {\n\t\tifaceLogLastTime = now\n\t\tifaceLogCount = 0\n\t\tif ifaceLogSuppressed {\n\t\t\tifaceLogSuppressed = false\n\t\t\treturn true\n\t\t}\n\t}\n\tif ifaceLogCount >= 10 {\n\t\tif !ifaceLogSuppressed {\n\t\t\tifaceLogSuppressed = true\n\t\t\treturn true\n\t\t}\n\t\treturn false\n\t}\n\tifaceLogCount++\n\treturn true\n}\n\nfunc getCache() (*ifaceCache, error) {\n\tvalue, err, _ := caches.Do(func() (*ifaceCache, error) {\n\t\tifaces, err := net.Interfaces()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tcache := &ifaceCache{\n\t\t\tifMapByName: make(map[string]*Interface),\n\t\t\tifMapByAddr: make(map[netip.Addr]*Interface),\n\t\t}\n\n\t\tfor _, iface := range ifaces {\n\t\t\taddrs, err := iface.Addrs()\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tipNets := make([]netip.Prefix, 0, len(addrs))\n\t\t\tfor _, addr := range addrs {\n\t\t\t\tvar pf netip.Prefix\n\t\t\t\tswitch ipNet := addr.(type) {\n\t\t\t\tcase *net.IPNet:\n\t\t\t\t\tip, _ := netip.AddrFromSlice(ipNet.IP)\n\t\t\t\t\tones, bits := ipNet.Mask.Size()\n\t\t\t\t\tif bits == 32 {\n\t\t\t\t\t\tip = ip.Unmap()\n\t\t\t\t\t}\n\t\t\t\t\tpf = netip.PrefixFrom(ip, ones)\n\t\t\t\tcase *net.IPAddr:\n\t\t\t\t\tip, _ := netip.AddrFromSlice(ipNet.IP)\n\t\t\t\t\tip = ip.Unmap()\n\t\t\t\t\tpf = netip.PrefixFrom(ip, ip.BitLen())\n\t\t\t\t}\n\t\t\t\tif pf.IsValid() {\n\t\t\t\t\tipNets = append(ipNets, pf)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tifaceObj := &Interface{\n\t\t\t\tIndex:        iface.Index,\n\t\t\t\tMTU:          iface.MTU,\n\t\t\t\tName:         iface.Name,\n\t\t\t\tHardwareAddr: iface.HardwareAddr,\n\t\t\t\tFlags:        iface.Flags,\n\t\t\t\tAddresses:    ipNets,\n\t\t\t}\n\t\t\tcache.ifMapByName[iface.Name] = ifaceObj\n\n\t\t\tif iface.Flags&net.FlagUp == 0 {\n\t\t\t\tcontinue // interface down\n\t\t\t}\n\t\t\tfor _, prefix := range ipNets {\n\t\t\t\tcache.ifMapByAddr[prefix.Addr()] = ifaceObj\n\t\t\t\tcache.ifTable.Insert(prefix, ifaceObj)\n\t\t\t}\n\t\t}\n\n\t\treturn cache, nil\n\t})\n\treturn value, err\n}\n\nfunc Interfaces() (map[string]*Interface, error) {\n\tcache, err := getCache()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn cache.ifMapByName, nil\n}\n\nfunc ResolveInterface(name string) (*Interface, error) {\n\tifaces, err := Interfaces()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tiface, ok := ifaces[name]\n\tif !ok {\n\t\tif ShouldLogIfaceError() {\n\t\t\treturn nil, ErrIfaceNotFound\n\t\t}\n\t\treturn nil, ErrIfaceNotFound\n\t}\n\n\treturn iface, nil\n}\n\nfunc ResolveInterfaceByAddr(addr netip.Addr) (*Interface, error) {\n\tcache, err := getCache()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t// maybe two interfaces have the same prefix but different address\n\t// so direct check address equal before do a route lookup (longest prefix match)\n\tif iface, ok := cache.ifMapByAddr[addr]; ok {\n\t\treturn iface, nil\n\t}\n\tiface, ok := cache.ifTable.Lookup(addr)\n\tif !ok {\n\t\t// 始终返回错误，但只在需要时记录日志\n\t\tif ShouldLogIfaceError() {\n\t\t\treturn nil, ErrIfaceNotFound\n\t\t}\n\t\treturn nil, ErrIfaceNotFound\n\t}\n\n\treturn iface, nil\n}\n\nfunc IsLocalIp(addr netip.Addr) (bool, error) {\n\tcache, err := getCache()\n\tif err != nil {\n\t\treturn false, err\n\t}\n\t_, ok := cache.ifMapByAddr[addr]\n\treturn ok, nil\n}\n\nfunc FlushCache() {\n\tcaches.Reset()\n}\n\nfunc (iface *Interface) PickIPv4Addr(destination netip.Addr) (netip.Prefix, error) {\n\treturn iface.pickIPAddr(destination, func(addr netip.Prefix) bool {\n\t\treturn addr.Addr().Is4()\n\t})\n}\n\nfunc (iface *Interface) PickIPv6Addr(destination netip.Addr) (netip.Prefix, error) {\n\treturn iface.pickIPAddr(destination, func(addr netip.Prefix) bool {\n\t\treturn addr.Addr().Is6()\n\t})\n}\n\nfunc (iface *Interface) pickIPAddr(destination netip.Addr, accept func(addr netip.Prefix) bool) (netip.Prefix, error) {\n\tvar fallback netip.Prefix\n\n\tfor _, addr := range iface.Addresses {\n\t\tif !accept(addr) {\n\t\t\tcontinue\n\t\t}\n\n\t\tif !fallback.IsValid() && !addr.Addr().IsLinkLocalUnicast() {\n\t\t\tfallback = addr\n\n\t\t\tif !destination.IsValid() {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif destination.IsValid() && addr.Contains(destination) {\n\t\t\treturn addr, nil\n\t\t}\n\t}\n\n\tif !fallback.IsValid() {\n\t\treturn netip.Prefix{}, ErrAddrNotFound\n\t}\n\n\treturn fallback, nil\n}"
  },
  {
    "path": "core/Clash.Meta/component/keepalive/tcp_keepalive.go",
    "content": "package keepalive\n\nimport (\n\t\"net\"\n\t\"runtime\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/atomic\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n)\n\nvar (\n\tkeepAliveIdle     = atomic.NewInt64(0)\n\tkeepAliveInterval = atomic.NewInt64(0)\n\tdisableKeepAlive  = atomic.NewBool(false)\n\n\tSetDisableKeepAliveCallback = utils.NewCallback[bool]()\n)\n\nfunc SetKeepAliveIdle(t time.Duration) {\n\tkeepAliveIdle.Store(int64(t))\n}\n\nfunc SetKeepAliveInterval(t time.Duration) {\n\tkeepAliveInterval.Store(int64(t))\n}\n\nfunc KeepAliveIdle() time.Duration {\n\treturn time.Duration(keepAliveIdle.Load())\n}\n\nfunc KeepAliveInterval() time.Duration {\n\treturn time.Duration(keepAliveInterval.Load())\n}\n\nfunc SetDisableKeepAlive(disable bool) {\n\tif runtime.GOOS == \"android\" {\n\t\tsetDisableKeepAlive(true)\n\t} else {\n\t\tsetDisableKeepAlive(disable)\n\t}\n}\n\nfunc setDisableKeepAlive(disable bool) {\n\tdisableKeepAlive.Store(disable)\n\tSetDisableKeepAliveCallback.Emit(disable)\n}\n\nfunc DisableKeepAlive() bool {\n\treturn disableKeepAlive.Load()\n}\n\nfunc SetNetDialer(dialer *net.Dialer) {\n\tsetNetDialer(dialer)\n}\n\nfunc SetNetListenConfig(lc *net.ListenConfig) {\n\tsetNetListenConfig(lc)\n}\n\nfunc TCPKeepAlive(c net.Conn) {\n\tif tcp, ok := c.(TCPConn); ok && tcp != nil {\n\t\ttcpKeepAlive(tcp)\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/keepalive/tcp_keepalive_go122.go",
    "content": "//go:build !go1.23\n\npackage keepalive\n\nimport (\n\t\"net\"\n\t\"time\"\n)\n\ntype TCPConn interface {\n\tnet.Conn\n\tSetKeepAlive(keepalive bool) error\n\tSetKeepAlivePeriod(d time.Duration) error\n}\n\nfunc tcpKeepAlive(tcp TCPConn) {\n\tif DisableKeepAlive() {\n\t\t_ = tcp.SetKeepAlive(false)\n\t} else {\n\t\t_ = tcp.SetKeepAlive(true)\n\t\t_ = tcp.SetKeepAlivePeriod(KeepAliveInterval())\n\t}\n}\n\nfunc setNetDialer(dialer *net.Dialer) {\n\tif DisableKeepAlive() {\n\t\tdialer.KeepAlive = -1 // If negative, keep-alive probes are disabled.\n\t} else {\n\t\tdialer.KeepAlive = KeepAliveInterval()\n\t}\n}\n\nfunc setNetListenConfig(lc *net.ListenConfig) {\n\tif DisableKeepAlive() {\n\t\tlc.KeepAlive = -1 // If negative, keep-alive probes are disabled.\n\t} else {\n\t\tlc.KeepAlive = KeepAliveInterval()\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/keepalive/tcp_keepalive_go123.go",
    "content": "//go:build go1.23\n\npackage keepalive\n\nimport \"net\"\n\ntype TCPConn interface {\n\tnet.Conn\n\tSetKeepAlive(keepalive bool) error\n\tSetKeepAliveConfig(config net.KeepAliveConfig) error\n}\n\nfunc keepAliveConfig() net.KeepAliveConfig {\n\tconfig := net.KeepAliveConfig{\n\t\tEnable:   true,\n\t\tIdle:     KeepAliveIdle(),\n\t\tInterval: KeepAliveInterval(),\n\t}\n\tif !SupportTCPKeepAliveCount() {\n\t\t// it's recommended to set both Idle and Interval to non-negative values in conjunction with a -1\n\t\t// for Count on those old Windows if you intend to customize the TCP keep-alive settings.\n\t\tconfig.Count = -1\n\t}\n\treturn config\n}\n\nfunc tcpKeepAlive(tcp TCPConn) {\n\tif DisableKeepAlive() {\n\t\t_ = tcp.SetKeepAlive(false)\n\t} else {\n\t\t_ = tcp.SetKeepAliveConfig(keepAliveConfig())\n\t}\n}\n\nfunc setNetDialer(dialer *net.Dialer) {\n\tif DisableKeepAlive() {\n\t\tdialer.KeepAlive = -1 // If negative, keep-alive probes are disabled.\n\t\tdialer.KeepAliveConfig.Enable = false\n\t} else {\n\t\tdialer.KeepAliveConfig = keepAliveConfig()\n\t}\n}\n\nfunc setNetListenConfig(lc *net.ListenConfig) {\n\tif DisableKeepAlive() {\n\t\tlc.KeepAlive = -1 // If negative, keep-alive probes are disabled.\n\t\tlc.KeepAliveConfig.Enable = false\n\t} else {\n\t\tlc.KeepAliveConfig = keepAliveConfig()\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/keepalive/tcp_keepalive_go123_unix.go",
    "content": "//go:build go1.23 && unix\n\npackage keepalive\n\nfunc SupportTCPKeepAliveIdle() bool {\n\treturn true\n}\n\nfunc SupportTCPKeepAliveInterval() bool {\n\treturn true\n}\n\nfunc SupportTCPKeepAliveCount() bool {\n\treturn true\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/keepalive/tcp_keepalive_go123_windows.go",
    "content": "//go:build go1.23 && windows\n\n// copy and modify from golang1.23's internal/syscall/windows/version_windows.go\n\npackage keepalive\n\nimport (\n\t\"errors\"\n\t\"sync\"\n\t\"syscall\"\n\n\t\"github.com/metacubex/mihomo/constant/features\"\n\n\t\"golang.org/x/sys/windows\"\n)\n\nvar (\n\tsupportTCPKeepAliveIdle     bool\n\tsupportTCPKeepAliveInterval bool\n\tsupportTCPKeepAliveCount    bool\n)\n\nvar initTCPKeepAlive = sync.OnceFunc(func() {\n\ts, err := windows.WSASocket(syscall.AF_INET, syscall.SOCK_STREAM, syscall.IPPROTO_TCP, nil, 0, windows.WSA_FLAG_NO_HANDLE_INHERIT)\n\tif err != nil {\n\t\t// Fallback to checking the Windows version.\n\t\tmajor, build := features.WindowsMajorVersion, features.WindowsBuildNumber\n\t\tsupportTCPKeepAliveIdle = major >= 10 && build >= 16299\n\t\tsupportTCPKeepAliveInterval = major >= 10 && build >= 16299\n\t\tsupportTCPKeepAliveCount = major >= 10 && build >= 15063\n\t\treturn\n\t}\n\tdefer windows.Closesocket(s)\n\tvar optSupported = func(opt int) bool {\n\t\terr := windows.SetsockoptInt(s, syscall.IPPROTO_TCP, opt, 1)\n\t\treturn !errors.Is(err, syscall.WSAENOPROTOOPT)\n\t}\n\tsupportTCPKeepAliveIdle = optSupported(windows.TCP_KEEPIDLE)\n\tsupportTCPKeepAliveInterval = optSupported(windows.TCP_KEEPINTVL)\n\tsupportTCPKeepAliveCount = optSupported(windows.TCP_KEEPCNT)\n})\n\n// SupportTCPKeepAliveIdle indicates whether TCP_KEEPIDLE is supported.\n// The minimal requirement is Windows 10.0.16299.\nfunc SupportTCPKeepAliveIdle() bool {\n\tinitTCPKeepAlive()\n\treturn supportTCPKeepAliveIdle\n}\n\n// SupportTCPKeepAliveInterval indicates whether TCP_KEEPINTVL is supported.\n// The minimal requirement is Windows 10.0.16299.\nfunc SupportTCPKeepAliveInterval() bool {\n\tinitTCPKeepAlive()\n\treturn supportTCPKeepAliveInterval\n}\n\n// SupportTCPKeepAliveCount indicates whether TCP_KEEPCNT is supported.\n// supports TCP_KEEPCNT.\n// The minimal requirement is Windows 10.0.15063.\nfunc SupportTCPKeepAliveCount() bool {\n\tinitTCPKeepAlive()\n\treturn supportTCPKeepAliveCount\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/loopback/detector.go",
    "content": "package loopback\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net/netip\"\n\t\"os\"\n\t\"strconv\"\n\n\t\"github.com/metacubex/mihomo/common/callback\"\n\t\"github.com/metacubex/mihomo/common/xsync\"\n\t\"github.com/metacubex/mihomo/component/iface\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/constant/features\"\n)\n\nvar disableLoopBackDetector, _ = strconv.ParseBool(os.Getenv(\"DISABLE_LOOPBACK_DETECTOR\"))\n\nfunc init() {\n\tif features.Android {\n\t\tdisableLoopBackDetector = true\n\t}\n}\n\nvar ErrReject = errors.New(\"reject loopback connection\")\n\ntype Detector struct {\n\tconnMap       xsync.Map[netip.AddrPort, struct{}]\n\tpacketConnMap xsync.Map[uint16, struct{}]\n}\n\nfunc NewDetector() *Detector {\n\tif disableLoopBackDetector {\n\t\treturn nil\n\t}\n\treturn &Detector{}\n}\n\nfunc (l *Detector) NewConn(conn C.Conn) C.Conn {\n\tif l == nil {\n\t\treturn conn\n\t}\n\tmetadata := C.Metadata{}\n\tif metadata.SetRemoteAddr(conn.LocalAddr()) != nil {\n\t\treturn conn\n\t}\n\tconnAddr := metadata.AddrPort()\n\tif !connAddr.IsValid() {\n\t\treturn conn\n\t}\n\tl.connMap.Store(connAddr, struct{}{})\n\treturn callback.NewCloseCallbackConn(conn, func() {\n\t\tl.connMap.Delete(connAddr)\n\t})\n}\n\nfunc (l *Detector) NewPacketConn(conn C.PacketConn) C.PacketConn {\n\tif l == nil {\n\t\treturn conn\n\t}\n\tmetadata := C.Metadata{}\n\tif metadata.SetRemoteAddr(conn.LocalAddr()) != nil {\n\t\treturn conn\n\t}\n\tconnAddr := metadata.AddrPort()\n\tif !connAddr.IsValid() {\n\t\treturn conn\n\t}\n\tport := connAddr.Port()\n\tl.packetConnMap.Store(port, struct{}{})\n\treturn callback.NewCloseCallbackPacketConn(conn, func() {\n\t\tl.packetConnMap.Delete(port)\n\t})\n}\n\nfunc (l *Detector) CheckConn(metadata *C.Metadata) error {\n\tif l == nil {\n\t\treturn nil\n\t}\n\tconnAddr := metadata.SourceAddrPort()\n\tif !connAddr.IsValid() {\n\t\treturn nil\n\t}\n\tif _, ok := l.connMap.Load(connAddr); ok {\n\t\treturn fmt.Errorf(\"%w to: %s\", ErrReject, metadata.RemoteAddress())\n\t}\n\treturn nil\n}\n\nfunc (l *Detector) CheckPacketConn(metadata *C.Metadata) error {\n\tif l == nil {\n\t\treturn nil\n\t}\n\tconnAddr := metadata.SourceAddrPort()\n\tif !connAddr.IsValid() {\n\t\treturn nil\n\t}\n\n\tisLocalIp, err := iface.IsLocalIp(connAddr.Addr())\n\tif err != nil {\n\t\treturn err\n\t}\n\tif !isLocalIp && !connAddr.Addr().IsLoopback() {\n\t\treturn nil\n\t}\n\n\tif _, ok := l.packetConnMap.Load(connAddr.Port()); ok {\n\t\treturn fmt.Errorf(\"%w to: %s\", ErrReject, metadata.RemoteAddress())\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/memory/memory.go",
    "content": "// Package memory return MemoryInfoStat\n// modify from https://github.com/shirou/gopsutil/tree/v4.25.8/process\npackage memory\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"math\"\n)\n\nvar ErrNotImplementedError = errors.New(\"not implemented yet\")\n\ntype MemoryInfoStat struct {\n\tRSS uint64 `json:\"rss\"` // bytes\n\tVMS uint64 `json:\"vms\"` // bytes\n}\n\n// PrettyByteSize convert size in bytes to Bytes, Kilobytes, Megabytes, GB and TB\n// https://gist.github.com/anikitenko/b41206a49727b83a530142c76b1cb82d?permalink_comment_id=4467913#gistcomment-4467913\nfunc PrettyByteSize(b uint64) string {\n\tbf := float64(b)\n\tfor _, unit := range []string{\"\", \"Ki\", \"Mi\", \"Gi\", \"Ti\", \"Pi\", \"Ei\", \"Zi\"} {\n\t\tif math.Abs(bf) < 1024.0 {\n\t\t\treturn fmt.Sprintf(\"%3.1f%sB\", bf, unit)\n\t\t}\n\t\tbf /= 1024.0\n\t}\n\treturn fmt.Sprintf(\"%.1fYiB\", bf)\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/memory/memory_darwin.go",
    "content": "package memory\n\nimport (\n\t\"syscall\"\n\t\"unsafe\"\n\t_ \"unsafe\"\n)\n\nconst PROC_PIDTASKINFO = 4\n\ntype ProcTaskInfo struct {\n\tVirtual_size      uint64\n\tResident_size     uint64\n\tTotal_user        uint64\n\tTotal_system      uint64\n\tThreads_user      uint64\n\tThreads_system    uint64\n\tPolicy            int32\n\tFaults            int32\n\tPageins           int32\n\tCow_faults        int32\n\tMessages_sent     int32\n\tMessages_received int32\n\tSyscalls_mach     int32\n\tSyscalls_unix     int32\n\tCsw               int32\n\tThreadnum         int32\n\tNumrunning        int32\n\tPriority          int32\n}\n\nfunc GetMemoryInfo(pid int32) (*MemoryInfoStat, error) {\n\tvar ti ProcTaskInfo\n\t_, _, errno := syscall_syscall6(proc_pidinfo_trampoline_addr, uintptr(pid), PROC_PIDTASKINFO, 0, uintptr(unsafe.Pointer(&ti)), unsafe.Sizeof(ti), 0)\n\tif errno != 0 {\n\t\treturn nil, errno\n\t}\n\n\tret := &MemoryInfoStat{\n\t\tRSS: uint64(ti.Resident_size),\n\t\tVMS: uint64(ti.Virtual_size),\n\t}\n\treturn ret, nil\n}\n\nvar proc_pidinfo_trampoline_addr uintptr\n\n//go:cgo_import_dynamic proc_pidinfo proc_pidinfo \"/usr/lib/libSystem.B.dylib\"\n\n// from golang.org/x/sys@v0.30.0/unix/syscall_darwin_libSystem.go\n\n// Implemented in the runtime package (runtime/sys_darwin.go)\nfunc syscall_syscall(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno)\nfunc syscall_syscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno)\nfunc syscall_syscall6X(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno)\nfunc syscall_syscall9(fn, a1, a2, a3, a4, a5, a6, a7, a8, a9 uintptr) (r1, r2 uintptr, err syscall.Errno) // 32-bit only\nfunc syscall_rawSyscall(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno)\nfunc syscall_rawSyscall6(fn, a1, a2, a3, a4, a5, a6 uintptr) (r1, r2 uintptr, err syscall.Errno)\nfunc syscall_syscallPtr(fn, a1, a2, a3 uintptr) (r1, r2 uintptr, err syscall.Errno)\n\n//go:linkname syscall_syscall syscall.syscall\n//go:linkname syscall_syscall6 syscall.syscall6\n//go:linkname syscall_syscall6X syscall.syscall6X\n//go:linkname syscall_syscall9 syscall.syscall9\n//go:linkname syscall_rawSyscall syscall.rawSyscall\n//go:linkname syscall_rawSyscall6 syscall.rawSyscall6\n//go:linkname syscall_syscallPtr syscall.syscallPtr\n"
  },
  {
    "path": "core/Clash.Meta/component/memory/memory_darwin_amd64.s",
    "content": "// go run mkasm.go darwin amd64\n// Code generated by the command above; DO NOT EDIT.\n\n#include \"textflag.h\"\n\nTEXT proc_pidinfo_trampoline<>(SB),NOSPLIT,$0-0\n\tJMP\tproc_pidinfo(SB)\nGLOBL\t·proc_pidinfo_trampoline_addr(SB), RODATA, $8\nDATA\t·proc_pidinfo_trampoline_addr(SB)/8, $proc_pidinfo_trampoline<>(SB)\n"
  },
  {
    "path": "core/Clash.Meta/component/memory/memory_darwin_arm64.s",
    "content": "// go run mkasm.go darwin arm64\n// Code generated by the command above; DO NOT EDIT.\n\n#include \"textflag.h\"\n\nTEXT proc_pidinfo_trampoline<>(SB),NOSPLIT,$0-0\n\tJMP\tproc_pidinfo(SB)\nGLOBL\t·proc_pidinfo_trampoline_addr(SB), RODATA, $8\nDATA\t·proc_pidinfo_trampoline_addr(SB)/8, $proc_pidinfo_trampoline<>(SB)\n"
  },
  {
    "path": "core/Clash.Meta/component/memory/memory_falllback.go",
    "content": "//go:build !darwin && !linux && !freebsd && !openbsd && !windows\n\npackage memory\n\nfunc GetMemoryInfo(pid int32) (*MemoryInfoStat, error) {\n\treturn nil, ErrNotImplementedError\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/memory/memory_freebsd.go",
    "content": "package memory\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"unsafe\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nconst (\n\tCTLKern          = 1\n\tKernProc         = 14\n\tKernProcPID      = 1\n)\n\nfunc CallSyscall(mib []int32) ([]byte, uint64, error) {\n\tmibptr := unsafe.Pointer(&mib[0])\n\tmiblen := uint64(len(mib))\n\n\t// get required buffer size\n\tlength := uint64(0)\n\t_, _, err := unix.Syscall6(\n\t\tunix.SYS___SYSCTL,\n\t\tuintptr(mibptr),\n\t\tuintptr(miblen),\n\t\t0,\n\t\tuintptr(unsafe.Pointer(&length)),\n\t\t0,\n\t\t0)\n\tif err != 0 {\n\t\tvar b []byte\n\t\treturn b, length, err\n\t}\n\tif length == 0 {\n\t\tvar b []byte\n\t\treturn b, length, err\n\t}\n\t// get proc info itself\n\tbuf := make([]byte, length)\n\t_, _, err = unix.Syscall6(\n\t\tunix.SYS___SYSCTL,\n\t\tuintptr(mibptr),\n\t\tuintptr(miblen),\n\t\tuintptr(unsafe.Pointer(&buf[0])),\n\t\tuintptr(unsafe.Pointer(&length)),\n\t\t0,\n\t\t0)\n\tif err != 0 {\n\t\treturn buf, length, err\n\t}\n\n\treturn buf, length, nil\n}\n\nfunc parseKinfoProc(buf []byte) (KinfoProc, error) {\n\tvar k KinfoProc\n\tbr := bytes.NewReader(buf)\n\terr := binary.Read(br, binary.LittleEndian, &k)\n\treturn k, err\n}\n\nfunc getKProc(pid int32) (*KinfoProc, error) {\n\tmib := []int32{CTLKern, KernProc, KernProcPID, pid}\n\n\tbuf, length, err := CallSyscall(mib)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif length != sizeOfKinfoProc {\n\t\treturn nil, errors.New(\"unexpected size of KinfoProc\")\n\t}\n\n\tk, err := parseKinfoProc(buf)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &k, nil\n}\n\nfunc GetMemoryInfo(pid int32) (*MemoryInfoStat, error) {\n\tk, err := getKProc(pid)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tv, err := unix.Sysctl(\"vm.stats.vm.v_page_size\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tpageSize := binary.LittleEndian.Uint16([]byte(v))\n\n\treturn &MemoryInfoStat{\n\t\tRSS: uint64(k.Rssize) * uint64(pageSize),\n\t\tVMS: uint64(k.Size),\n\t}, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/memory/memory_freebsd_386.go",
    "content": "package memory\n\nconst sizeOfKinfoProc    = 0x300\n\ntype Timeval struct {\n\tSec  int32\n\tUsec int32\n}\n\ntype Rusage struct {\n\tUtime    Timeval\n\tStime    Timeval\n\tMaxrss   int32\n\tIxrss    int32\n\tIdrss    int32\n\tIsrss    int32\n\tMinflt   int32\n\tMajflt   int32\n\tNswap    int32\n\tInblock  int32\n\tOublock  int32\n\tMsgsnd   int32\n\tMsgrcv   int32\n\tNsignals int32\n\tNvcsw    int32\n\tNivcsw   int32\n}\n\ntype KinfoProc struct {\n\tStructsize   int32\n\tLayout       int32\n\tArgs         int32 /* pargs */\n\tPaddr        int32 /* proc */\n\tAddr         int32 /* user */\n\tTracep       int32 /* vnode */\n\tTextvp       int32 /* vnode */\n\tFd           int32 /* filedesc */\n\tVmspace      int32 /* vmspace */\n\tWchan        int32\n\tPid          int32\n\tPpid         int32\n\tPgid         int32\n\tTpgid        int32\n\tSid          int32\n\tTsid         int32\n\tJobc         int16\n\tSpare_short1 int16\n\tTdev         uint32\n\tSiglist      [16]byte /* sigset */\n\tSigmask      [16]byte /* sigset */\n\tSigignore    [16]byte /* sigset */\n\tSigcatch     [16]byte /* sigset */\n\tUid          uint32\n\tRuid         uint32\n\tSvuid        uint32\n\tRgid         uint32\n\tSvgid        uint32\n\tNgroups      int16\n\tSpare_short2 int16\n\tGroups       [16]uint32\n\tSize         uint32\n\tRssize       int32\n\tSwrss        int32\n\tTsize        int32\n\tDsize        int32\n\tSsize        int32\n\tXstat        uint16\n\tAcflag       uint16\n\tPctcpu       uint32\n\tEstcpu       uint32\n\tSlptime      uint32\n\tSwtime       uint32\n\tCow          uint32\n\tRuntime      uint64\n\tStart        Timeval\n\tChildtime    Timeval\n\tFlag         int32\n\tKiflag       int32\n\tTraceflag    int32\n\tStat         int8\n\tNice         int8\n\tLock         int8\n\tRqindex      int8\n\tOncpu        uint8\n\tLastcpu      uint8\n\tTdname       [17]int8\n\tWmesg        [9]int8\n\tLogin        [18]int8\n\tLockname     [9]int8\n\tComm         [20]int8\n\tEmul         [17]int8\n\tLoginclass   [18]int8\n\tSparestrings [50]int8\n\tSpareints    [7]int32\n\tFlag2        int32\n\tFibnum       int32\n\tCr_flags     uint32\n\tJid          int32\n\tNumthreads   int32\n\tTid          int32\n\tPri          Priority\n\tRusage       Rusage\n\tRusage_ch    Rusage\n\tPcb          int32 /* pcb */\n\tKstack       int32\n\tUdata        int32\n\tTdaddr       int32 /* thread */\n\tSpareptrs    [6]int32\n\tSparelongs   [12]int32\n\tSflag        int32\n\tTdflags      int32\n}\n\ntype Priority struct {\n\tClass  uint8\n\tLevel  uint8\n\tNative uint8\n\tUser   uint8\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/memory/memory_freebsd_amd64.go",
    "content": "package memory\n\nconst sizeOfKinfoProc = 0x440\n\ntype Timeval struct {\n\tSec  int64\n\tUsec int64\n}\n\ntype Rusage struct {\n\tUtime    Timeval\n\tStime    Timeval\n\tMaxrss   int64\n\tIxrss    int64\n\tIdrss    int64\n\tIsrss    int64\n\tMinflt   int64\n\tMajflt   int64\n\tNswap    int64\n\tInblock  int64\n\tOublock  int64\n\tMsgsnd   int64\n\tMsgrcv   int64\n\tNsignals int64\n\tNvcsw    int64\n\tNivcsw   int64\n}\n\ntype KinfoProc struct {\n\tStructsize     int32\n\tLayout         int32\n\tArgs           int64 /* pargs */\n\tPaddr          int64 /* proc */\n\tAddr           int64 /* user */\n\tTracep         int64 /* vnode */\n\tTextvp         int64 /* vnode */\n\tFd             int64 /* filedesc */\n\tVmspace        int64 /* vmspace */\n\tWchan          int64\n\tPid            int32\n\tPpid           int32\n\tPgid           int32\n\tTpgid          int32\n\tSid            int32\n\tTsid           int32\n\tJobc           int16\n\tSpare_short1   int16\n\tTdev_freebsd11 uint32\n\tSiglist        [16]byte /* sigset */\n\tSigmask        [16]byte /* sigset */\n\tSigignore      [16]byte /* sigset */\n\tSigcatch       [16]byte /* sigset */\n\tUid            uint32\n\tRuid           uint32\n\tSvuid          uint32\n\tRgid           uint32\n\tSvgid          uint32\n\tNgroups        int16\n\tSpare_short2   int16\n\tGroups         [16]uint32\n\tSize           uint64\n\tRssize         int64\n\tSwrss          int64\n\tTsize          int64\n\tDsize          int64\n\tSsize          int64\n\tXstat          uint16\n\tAcflag         uint16\n\tPctcpu         uint32\n\tEstcpu         uint32\n\tSlptime        uint32\n\tSwtime         uint32\n\tCow            uint32\n\tRuntime        uint64\n\tStart          Timeval\n\tChildtime      Timeval\n\tFlag           int64\n\tKiflag         int64\n\tTraceflag      int32\n\tStat           int8\n\tNice           int8\n\tLock           int8\n\tRqindex        int8\n\tOncpu_old      uint8\n\tLastcpu_old    uint8\n\tTdname         [17]int8\n\tWmesg          [9]int8\n\tLogin          [18]int8\n\tLockname       [9]int8\n\tComm           [20]int8\n\tEmul           [17]int8\n\tLoginclass     [18]int8\n\tMoretdname     [4]int8\n\tSparestrings   [46]int8\n\tSpareints      [2]int32\n\tTdev           uint64\n\tOncpu          int32\n\tLastcpu        int32\n\tTracer         int32\n\tFlag2          int32\n\tFibnum         int32\n\tCr_flags       uint32\n\tJid            int32\n\tNumthreads     int32\n\tTid            int32\n\tPri            Priority\n\tRusage         Rusage\n\tRusage_ch      Rusage\n\tPcb            int64 /* pcb */\n\tKstack         int64\n\tUdata          int64\n\tTdaddr         int64 /* thread */\n\tPd             int64 /* pwddesc, not accurate */\n\tSpareptrs      [5]int64\n\tSparelongs     [12]int64\n\tSflag          int64\n\tTdflags        int64\n}\n\ntype Priority struct {\n\tClass  uint8\n\tLevel  uint8\n\tNative uint8\n\tUser   uint8\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/memory/memory_freebsd_arm.go",
    "content": "package memory\n\nconst sizeOfKinfoProc = 0x440\n\ntype Timeval struct {\n\tSec  int64\n\tUsec int64\n}\n\ntype Rusage struct {\n\tUtime    Timeval\n\tStime    Timeval\n\tMaxrss   int32\n\tIxrss    int32\n\tIdrss    int32\n\tIsrss    int32\n\tMinflt   int32\n\tMajflt   int32\n\tNswap    int32\n\tInblock  int32\n\tOublock  int32\n\tMsgsnd   int32\n\tMsgrcv   int32\n\tNsignals int32\n\tNvcsw    int32\n\tNivcsw   int32\n}\n\ntype KinfoProc struct {\n\tStructsize   int32\n\tLayout       int32\n\tArgs         int32 /* pargs */\n\tPaddr        int32 /* proc */\n\tAddr         int32 /* user */\n\tTracep       int32 /* vnode */\n\tTextvp       int32 /* vnode */\n\tFd           int32 /* filedesc */\n\tVmspace      int32 /* vmspace */\n\tWchan        int32\n\tPid          int32\n\tPpid         int32\n\tPgid         int32\n\tTpgid        int32\n\tSid          int32\n\tTsid         int32\n\tJobc         int16\n\tSpare_short1 int16\n\tTdev         uint32\n\tSiglist      [16]byte /* sigset */\n\tSigmask      [16]byte /* sigset */\n\tSigignore    [16]byte /* sigset */\n\tSigcatch     [16]byte /* sigset */\n\tUid          uint32\n\tRuid         uint32\n\tSvuid        uint32\n\tRgid         uint32\n\tSvgid        uint32\n\tNgroups      int16\n\tSpare_short2 int16\n\tGroups       [16]uint32\n\tSize         uint32\n\tRssize       int32\n\tSwrss        int32\n\tTsize        int32\n\tDsize        int32\n\tSsize        int32\n\tXstat        uint16\n\tAcflag       uint16\n\tPctcpu       uint32\n\tEstcpu       uint32\n\tSlptime      uint32\n\tSwtime       uint32\n\tCow          uint32\n\tRuntime      uint64\n\tStart        Timeval\n\tChildtime    Timeval\n\tFlag         int32\n\tKiflag       int32\n\tTraceflag    int32\n\tStat         int8\n\tNice         int8\n\tLock         int8\n\tRqindex      int8\n\tOncpu        uint8\n\tLastcpu      uint8\n\tTdname       [17]int8\n\tWmesg        [9]int8\n\tLogin        [18]int8\n\tLockname     [9]int8\n\tComm         [20]int8\n\tEmul         [17]int8\n\tLoginclass   [18]int8\n\tSparestrings [50]int8\n\tSpareints    [4]int32\n\tFlag2        int32\n\tFibnum       int32\n\tCr_flags     uint32\n\tJid          int32\n\tNumthreads   int32\n\tTid          int32\n\tPri          Priority\n\tRusage       Rusage\n\tRusage_ch    Rusage\n\tPcb          int32 /* pcb */\n\tKstack       int32\n\tUdata        int32\n\tTdaddr       int32 /* thread */\n\tSpareptrs    [6]int64\n\tSparelongs   [12]int64\n\tSflag        int64\n\tTdflags      int64\n}\n\ntype Priority struct {\n\tClass  uint8\n\tLevel  uint8\n\tNative uint8\n\tUser   uint8\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/memory/memory_freebsd_arm64.go",
    "content": "package memory\n\nconst sizeOfKinfoProc = 0x440\n\ntype Timeval struct {\n\tSec  int64\n\tUsec int64\n}\n\ntype Rusage struct {\n\tUtime    Timeval\n\tStime    Timeval\n\tMaxrss   int64\n\tIxrss    int64\n\tIdrss    int64\n\tIsrss    int64\n\tMinflt   int64\n\tMajflt   int64\n\tNswap    int64\n\tInblock  int64\n\tOublock  int64\n\tMsgsnd   int64\n\tMsgrcv   int64\n\tNsignals int64\n\tNvcsw    int64\n\tNivcsw   int64\n}\n\ntype KinfoProc struct {\n\tStructsize     int32\n\tLayout         int32\n\tArgs           int64 /* pargs */\n\tPaddr          int64 /* proc */\n\tAddr           int64 /* user */\n\tTracep         int64 /* vnode */\n\tTextvp         int64 /* vnode */\n\tFd             int64 /* filedesc */\n\tVmspace        int64 /* vmspace */\n\tWchan          int64\n\tPid            int32\n\tPpid           int32\n\tPgid           int32\n\tTpgid          int32\n\tSid            int32\n\tTsid           int32\n\tJobc           int16\n\tSpare_short1   int16\n\tTdev_freebsd11 uint32\n\tSiglist        [16]byte /* sigset */\n\tSigmask        [16]byte /* sigset */\n\tSigignore      [16]byte /* sigset */\n\tSigcatch       [16]byte /* sigset */\n\tUid            uint32\n\tRuid           uint32\n\tSvuid          uint32\n\tRgid           uint32\n\tSvgid          uint32\n\tNgroups        int16\n\tSpare_short2   int16\n\tGroups         [16]uint32\n\tSize           uint64\n\tRssize         int64\n\tSwrss          int64\n\tTsize          int64\n\tDsize          int64\n\tSsize          int64\n\tXstat          uint16\n\tAcflag         uint16\n\tPctcpu         uint32\n\tEstcpu         uint32\n\tSlptime        uint32\n\tSwtime         uint32\n\tCow            uint32\n\tRuntime        uint64\n\tStart          Timeval\n\tChildtime      Timeval\n\tFlag           int64\n\tKiflag         int64\n\tTraceflag      int32\n\tStat           uint8\n\tNice           int8\n\tLock           uint8\n\tRqindex        uint8\n\tOncpu_old      uint8\n\tLastcpu_old    uint8\n\tTdname         [17]uint8\n\tWmesg          [9]uint8\n\tLogin          [18]uint8\n\tLockname       [9]uint8\n\tComm           [20]int8 // changed from uint8 by hand\n\tEmul           [17]uint8\n\tLoginclass     [18]uint8\n\tMoretdname     [4]uint8\n\tSparestrings   [46]uint8\n\tSpareints      [2]int32\n\tTdev           uint64\n\tOncpu          int32\n\tLastcpu        int32\n\tTracer         int32\n\tFlag2          int32\n\tFibnum         int32\n\tCr_flags       uint32\n\tJid            int32\n\tNumthreads     int32\n\tTid            int32\n\tPri            Priority\n\tRusage         Rusage\n\tRusage_ch      Rusage\n\tPcb            int64 /* pcb */\n\tKstack         int64\n\tUdata          int64\n\tTdaddr         int64 /* thread */\n\tPd             int64 /* pwddesc, not accurate */\n\tSpareptrs      [5]int64\n\tSparelongs     [12]int64\n\tSflag          int64\n\tTdflags        int64\n}\n\ntype Priority struct {\n\tClass  uint8\n\tLevel  uint8\n\tNative uint8\n\tUser   uint8\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/memory/memory_linux.go",
    "content": "package memory\n\nimport (\n\t\"os\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nvar pageSize = uint64(os.Getpagesize())\n\nfunc GetMemoryInfo(pid int32) (*MemoryInfoStat, error) {\n\tproc := os.Getenv(\"HOST_PROC\")\n\tif proc == \"\" {\n\t\tproc = \"/proc\"\n\t}\n\tmemPath := filepath.Join(proc, strconv.Itoa(int(pid)), \"statm\")\n\tcontents, err := os.ReadFile(memPath)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfields := strings.Split(string(contents), \" \")\n\n\tvms, err := strconv.ParseUint(fields[0], 10, 64)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\trss, err := strconv.ParseUint(fields[1], 10, 64)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tmemInfo := &MemoryInfoStat{\n\t\tRSS: rss * pageSize,\n\t\tVMS: vms * pageSize,\n\t}\n\treturn memInfo, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/memory/memory_openbsd.go",
    "content": "package memory\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"unsafe\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nconst (\n\tCTLKern     = 1\n\tKernProc    = 14\n\tKernProcPID = 1\n)\n\nfunc callKernProcSyscall(op int32, arg int32) ([]byte, uint64, error) {\n\tmib := []int32{CTLKern, KernProc, op, arg, sizeOfKinfoProc, 0}\n\tmibptr := unsafe.Pointer(&mib[0])\n\tmiblen := uint64(len(mib))\n\tlength := uint64(0)\n\t_, _, err := unix.Syscall6(\n\t\tunix.SYS___SYSCTL,\n\t\tuintptr(mibptr),\n\t\tuintptr(miblen),\n\t\t0,\n\t\tuintptr(unsafe.Pointer(&length)),\n\t\t0,\n\t\t0)\n\tif err != 0 {\n\t\treturn nil, length, err\n\t}\n\n\tcount := int32(length / uint64(sizeOfKinfoProc))\n\tmib = []int32{CTLKern, KernProc, op, arg, sizeOfKinfoProc, count}\n\tmibptr = unsafe.Pointer(&mib[0])\n\tmiblen = uint64(len(mib))\n\t// get proc info itself\n\tbuf := make([]byte, length)\n\t_, _, err = unix.Syscall6(\n\t\tunix.SYS___SYSCTL,\n\t\tuintptr(mibptr),\n\t\tuintptr(miblen),\n\t\tuintptr(unsafe.Pointer(&buf[0])),\n\t\tuintptr(unsafe.Pointer(&length)),\n\t\t0,\n\t\t0)\n\tif err != 0 {\n\t\treturn buf, length, err\n\t}\n\n\treturn buf, length, nil\n}\n\nfunc parseKinfoProc(buf []byte) (KinfoProc, error) {\n\tvar k KinfoProc\n\tbr := bytes.NewReader(buf)\n\terr := binary.Read(br, binary.LittleEndian, &k)\n\treturn k, err\n}\n\nfunc getKProc(pid int32) (*KinfoProc, error) {\n\tbuf, length, err := callKernProcSyscall(KernProcPID, pid)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif length != sizeOfKinfoProc {\n\t\treturn nil, errors.New(\"unexpected size of KinfoProc\")\n\t}\n\n\tk, err := parseKinfoProc(buf)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &k, nil\n}\n\nfunc GetMemoryInfo(pid int32) (*MemoryInfoStat, error) {\n\tk, err := getKProc(pid)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tuvmexp, err := unix.SysctlUvmexp(\"vm.uvmexp\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tpageSize := uint64(uvmexp.Pagesize)\n\n\treturn &MemoryInfoStat{\n\t\tRSS: uint64(k.Vm_rssize) * pageSize,\n\t\tVMS: uint64(k.Vm_tsize) + uint64(k.Vm_dsize) +\n\t\t\tuint64(k.Vm_ssize),\n\t}, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/memory/memory_openbsd_386.go",
    "content": "package memory\n\nconst sizeOfKinfoProc = 0x264\n\ntype KinfoProc struct {\n\tForw         uint64\n\tBack         uint64\n\tPaddr        uint64\n\tAddr         uint64\n\tFd           uint64\n\tStats        uint64\n\tLimit        uint64\n\tVmspace      uint64\n\tSigacts      uint64\n\tSess         uint64\n\tTsess        uint64\n\tRu           uint64\n\tEflag        int32\n\tExitsig      int32\n\tFlag         int32\n\tPid          int32\n\tPpid         int32\n\tSid          int32\n\tX_pgid       int32\n\tTpgid        int32\n\tUid          uint32\n\tRuid         uint32\n\tGid          uint32\n\tRgid         uint32\n\tGroups       [16]uint32\n\tNgroups      int16\n\tJobc         int16\n\tTdev         uint32\n\tEstcpu       uint32\n\tRtime_sec    uint32\n\tRtime_usec   uint32\n\tCpticks      int32\n\tPctcpu       uint32\n\tSwtime       uint32\n\tSlptime      uint32\n\tSchedflags   int32\n\tUticks       uint64\n\tSticks       uint64\n\tIticks       uint64\n\tTracep       uint64\n\tTraceflag    int32\n\tHoldcnt      int32\n\tSiglist      int32\n\tSigmask      uint32\n\tSigignore    uint32\n\tSigcatch     uint32\n\tStat         int8\n\tPriority     uint8\n\tUsrpri       uint8\n\tNice         uint8\n\tXstat        uint16\n\tAcflag       uint16\n\tComm         [24]int8\n\tWmesg        [8]int8\n\tWchan        uint64\n\tLogin        [32]int8\n\tVm_rssize    int32\n\tVm_tsize     int32\n\tVm_dsize     int32\n\tVm_ssize     int32\n\tUvalid       int64\n\tUstart_sec   uint64\n\tUstart_usec  uint32\n\tUutime_sec   uint32\n\tUutime_usec  uint32\n\tUstime_sec   uint32\n\tUstime_usec  uint32\n\tUru_maxrss   uint64\n\tUru_ixrss    uint64\n\tUru_idrss    uint64\n\tUru_isrss    uint64\n\tUru_minflt   uint64\n\tUru_majflt   uint64\n\tUru_nswap    uint64\n\tUru_inblock  uint64\n\tUru_oublock  uint64\n\tUru_msgsnd   uint64\n\tUru_msgrcv   uint64\n\tUru_nsignals uint64\n\tUru_nvcsw    uint64\n\tUru_nivcsw   uint64\n\tUctime_sec   uint32\n\tUctime_usec  uint32\n\tPsflags      int32\n\tSpare        int32\n\tSvuid        uint32\n\tSvgid        uint32\n\tEmul         [8]int8\n\tRlim_rss_cur uint64\n\tCpuid        uint64\n\tVm_map_size  uint64\n\tTid          int32\n\tRtableid     uint32\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/memory/memory_openbsd_amd64.go",
    "content": "package memory\n\nconst sizeOfKinfoProc = 0x268\n\ntype KinfoProc struct {\n\tForw         uint64\n\tBack         uint64\n\tPaddr        uint64\n\tAddr         uint64\n\tFd           uint64\n\tStats        uint64\n\tLimit        uint64\n\tVmspace      uint64\n\tSigacts      uint64\n\tSess         uint64\n\tTsess        uint64\n\tRu           uint64\n\tEflag        int32\n\tExitsig      int32\n\tFlag         int32\n\tPid          int32\n\tPpid         int32\n\tSid          int32\n\tX_pgid       int32\n\tTpgid        int32\n\tUid          uint32\n\tRuid         uint32\n\tGid          uint32\n\tRgid         uint32\n\tGroups       [16]uint32\n\tNgroups      int16\n\tJobc         int16\n\tTdev         uint32\n\tEstcpu       uint32\n\tRtime_sec    uint32\n\tRtime_usec   uint32\n\tCpticks      int32\n\tPctcpu       uint32\n\tSwtime       uint32\n\tSlptime      uint32\n\tSchedflags   int32\n\tUticks       uint64\n\tSticks       uint64\n\tIticks       uint64\n\tTracep       uint64\n\tTraceflag    int32\n\tHoldcnt      int32\n\tSiglist      int32\n\tSigmask      uint32\n\tSigignore    uint32\n\tSigcatch     uint32\n\tStat         int8\n\tPriority     uint8\n\tUsrpri       uint8\n\tNice         uint8\n\tXstat        uint16\n\tAcflag       uint16\n\tComm         [24]int8\n\tWmesg        [8]int8\n\tWchan        uint64\n\tLogin        [32]int8\n\tVm_rssize    int32\n\tVm_tsize     int32\n\tVm_dsize     int32\n\tVm_ssize     int32\n\tUvalid       int64\n\tUstart_sec   uint64\n\tUstart_usec  uint32\n\tUutime_sec   uint32\n\tUutime_usec  uint32\n\tUstime_sec   uint32\n\tUstime_usec  uint32\n\tPad_cgo_0    [4]byte\n\tUru_maxrss   uint64\n\tUru_ixrss    uint64\n\tUru_idrss    uint64\n\tUru_isrss    uint64\n\tUru_minflt   uint64\n\tUru_majflt   uint64\n\tUru_nswap    uint64\n\tUru_inblock  uint64\n\tUru_oublock  uint64\n\tUru_msgsnd   uint64\n\tUru_msgrcv   uint64\n\tUru_nsignals uint64\n\tUru_nvcsw    uint64\n\tUru_nivcsw   uint64\n\tUctime_sec   uint32\n\tUctime_usec  uint32\n\tPsflags      int32\n\tSpare        int32\n\tSvuid        uint32\n\tSvgid        uint32\n\tEmul         [8]int8\n\tRlim_rss_cur uint64\n\tCpuid        uint64\n\tVm_map_size  uint64\n\tTid          int32\n\tRtableid     uint32\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/memory/memory_openbsd_arm.go",
    "content": "package memory\n\nconst sizeOfKinfoProc = 0x264\n\ntype KinfoProc struct {\n\tForw         uint64\n\tBack         uint64\n\tPaddr        uint64\n\tAddr         uint64\n\tFd           uint64\n\tStats        uint64\n\tLimit        uint64\n\tVmspace      uint64\n\tSigacts      uint64\n\tSess         uint64\n\tTsess        uint64\n\tRu           uint64\n\tEflag        int32\n\tExitsig      int32\n\tFlag         int32\n\tPid          int32\n\tPpid         int32\n\tSid          int32\n\tX_pgid       int32\n\tTpgid        int32\n\tUid          uint32\n\tRuid         uint32\n\tGid          uint32\n\tRgid         uint32\n\tGroups       [16]uint32\n\tNgroups      int16\n\tJobc         int16\n\tTdev         uint32\n\tEstcpu       uint32\n\tRtime_sec    uint32\n\tRtime_usec   uint32\n\tCpticks      int32\n\tPctcpu       uint32\n\tSwtime       uint32\n\tSlptime      uint32\n\tSchedflags   int32\n\tUticks       uint64\n\tSticks       uint64\n\tIticks       uint64\n\tTracep       uint64\n\tTraceflag    int32\n\tHoldcnt      int32\n\tSiglist      int32\n\tSigmask      uint32\n\tSigignore    uint32\n\tSigcatch     uint32\n\tStat         int8\n\tPriority     uint8\n\tUsrpri       uint8\n\tNice         uint8\n\tXstat        uint16\n\tAcflag       uint16\n\tComm         [24]int8\n\tWmesg        [8]int8\n\tWchan        uint64\n\tLogin        [32]int8\n\tVm_rssize    int32\n\tVm_tsize     int32\n\tVm_dsize     int32\n\tVm_ssize     int32\n\tUvalid       int64\n\tUstart_sec   uint64\n\tUstart_usec  uint32\n\tUutime_sec   uint32\n\tUutime_usec  uint32\n\tUstime_sec   uint32\n\tUstime_usec  uint32\n\tUru_maxrss   uint64\n\tUru_ixrss    uint64\n\tUru_idrss    uint64\n\tUru_isrss    uint64\n\tUru_minflt   uint64\n\tUru_majflt   uint64\n\tUru_nswap    uint64\n\tUru_inblock  uint64\n\tUru_oublock  uint64\n\tUru_msgsnd   uint64\n\tUru_msgrcv   uint64\n\tUru_nsignals uint64\n\tUru_nvcsw    uint64\n\tUru_nivcsw   uint64\n\tUctime_sec   uint32\n\tUctime_usec  uint32\n\tPsflags      int32\n\tSpare        int32\n\tSvuid        uint32\n\tSvgid        uint32\n\tEmul         [8]int8\n\tRlim_rss_cur uint64\n\tCpuid        uint64\n\tVm_map_size  uint64\n\tTid          int32\n\tRtableid     uint32\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/memory/memory_openbsd_arm64.go",
    "content": "package memory\n\nconst sizeOfKinfoProc = 0x270\n\ntype KinfoProc struct {\n\tForw         uint64\n\tBack         uint64\n\tPaddr        uint64\n\tAddr         uint64\n\tFd           uint64\n\tStats        uint64\n\tLimit        uint64\n\tVmspace      uint64\n\tSigacts      uint64\n\tSess         uint64\n\tTsess        uint64\n\tRu           uint64\n\tEflag        int32\n\tExitsig      int32\n\tFlag         int32\n\tPid          int32\n\tPpid         int32\n\tSid          int32\n\tX_pgid       int32\n\tTpgid        int32\n\tUid          uint32\n\tRuid         uint32\n\tGid          uint32\n\tRgid         uint32\n\tGroups       [16]uint32\n\tNgroups      int16\n\tJobc         int16\n\tTdev         uint32\n\tEstcpu       uint32\n\tRtime_sec    uint32\n\tRtime_usec   uint32\n\tCpticks      int32\n\tPctcpu       uint32\n\tSwtime       uint32\n\tSlptime      uint32\n\tSchedflags   int32\n\tUticks       uint64\n\tSticks       uint64\n\tIticks       uint64\n\tTracep       uint64\n\tTraceflag    int32\n\tHoldcnt      int32\n\tSiglist      int32\n\tSigmask      uint32\n\tSigignore    uint32\n\tSigcatch     uint32\n\tStat         int8\n\tPriority     uint8\n\tUsrpri       uint8\n\tNice         uint8\n\tXstat        uint16\n\tAcflag       uint16\n\tComm         [24]int8\n\tWmesg        [8]uint8\n\tWchan        uint64\n\tLogin        [32]uint8\n\tVm_rssize    int32\n\tVm_tsize     int32\n\tVm_dsize     int32\n\tVm_ssize     int32\n\tUvalid       int64\n\tUstart_sec   uint64\n\tUstart_usec  uint32\n\tUutime_sec   uint32\n\tUutime_usec  uint32\n\tUstime_sec   uint32\n\tUstime_usec  uint32\n\tUru_maxrss   uint64\n\tUru_ixrss    uint64\n\tUru_idrss    uint64\n\tUru_isrss    uint64\n\tUru_minflt   uint64\n\tUru_majflt   uint64\n\tUru_nswap    uint64\n\tUru_inblock  uint64\n\tUru_oublock  uint64\n\tUru_msgsnd   uint64\n\tUru_msgrcv   uint64\n\tUru_nsignals uint64\n\tUru_nvcsw    uint64\n\tUru_nivcsw   uint64\n\tUctime_sec   uint32\n\tUctime_usec  uint32\n\tPsflags      uint32\n\tSpare        int32\n\tSvuid        uint32\n\tSvgid        uint32\n\tEmul         [8]uint8\n\tRlim_rss_cur uint64\n\tCpuid        uint64\n\tVm_map_size  uint64\n\tTid          int32\n\tRtableid     uint32\n\tPledge       uint64\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/memory/memory_openbsd_riscv64.go",
    "content": "package memory\n\nconst sizeOfKinfoProc = 0x288\n\ntype KinfoProc struct {\n\tForw         uint64\n\tBack         uint64\n\tPaddr        uint64\n\tAddr         uint64\n\tFd           uint64\n\tStats        uint64\n\tLimit        uint64\n\tVmspace      uint64\n\tSigacts      uint64\n\tSess         uint64\n\tTsess        uint64\n\tRu           uint64\n\tEflag        int32\n\tExitsig      int32\n\tFlag         int32\n\tPid          int32\n\tPpid         int32\n\tSid          int32\n\tX_pgid       int32\n\tTpgid        int32\n\tUid          uint32\n\tRuid         uint32\n\tGid          uint32\n\tRgid         uint32\n\tGroups       [16]uint32\n\tNgroups      int16\n\tJobc         int16\n\tTdev         uint32\n\tEstcpu       uint32\n\tRtime_sec    uint32\n\tRtime_usec   uint32\n\tCpticks      int32\n\tPctcpu       uint32\n\tSwtime       uint32\n\tSlptime      uint32\n\tSchedflags   int32\n\tUticks       uint64\n\tSticks       uint64\n\tIticks       uint64\n\tTracep       uint64\n\tTraceflag    int32\n\tHoldcnt      int32\n\tSiglist      int32\n\tSigmask      uint32\n\tSigignore    uint32\n\tSigcatch     uint32\n\tStat         int8\n\tPriority     uint8\n\tUsrpri       uint8\n\tNice         uint8\n\tXstat        uint16\n\tSpare        uint16\n\tComm         [24]int8\n\tWmesg        [8]uint8\n\tWchan        uint64\n\tLogin        [32]uint8\n\tVm_rssize    int32\n\tVm_tsize     int32\n\tVm_dsize     int32\n\tVm_ssize     int32\n\tUvalid       int64\n\tUstart_sec   uint64\n\tUstart_usec  uint32\n\tUutime_sec   uint32\n\tUutime_usec  uint32\n\tUstime_sec   uint32\n\tUstime_usec  uint32\n\tUru_maxrss   uint64\n\tUru_ixrss    uint64\n\tUru_idrss    uint64\n\tUru_isrss    uint64\n\tUru_minflt   uint64\n\tUru_majflt   uint64\n\tUru_nswap    uint64\n\tUru_inblock  uint64\n\tUru_oublock  uint64\n\tUru_msgsnd   uint64\n\tUru_msgrcv   uint64\n\tUru_nsignals uint64\n\tUru_nvcsw    uint64\n\tUru_nivcsw   uint64\n\tUctime_sec   uint32\n\tUctime_usec  uint32\n\tPsflags      uint32\n\tAcflag       uint32\n\tSvuid        uint32\n\tSvgid        uint32\n\tEmul         [8]uint8\n\tRlim_rss_cur uint64\n\tCpuid        uint64\n\tVm_map_size  uint64\n\tTid          int32\n\tRtableid     uint32\n\tPledge       uint64\n\tName         [24]uint8\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/memory/memory_test.go",
    "content": "package memory\n\nimport (\n\t\"errors\"\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestMemoryInfo(t *testing.T) {\n\tv, err := GetMemoryInfo(int32(os.Getpid()))\n\tif errors.Is(err, ErrNotImplementedError) {\n\t\tt.Skip(\"not implemented\")\n\t}\n\trequire.NoErrorf(t, err, \"getting memory info error %v\", err)\n\tempty := MemoryInfoStat{}\n\tif v == nil || *v == empty {\n\t\tt.Errorf(\"could not get memory info %v\", v)\n\t} else {\n\t\tt.Logf(\"memory info {RSS:%s, VMS:%s}\", PrettyByteSize(v.RSS), PrettyByteSize(v.VMS))\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/memory/memory_windows.go",
    "content": "package memory\n\nimport (\n\t\"syscall\"\n\t\"unsafe\"\n\n\t\"golang.org/x/sys/windows\"\n)\n\nvar (\n\tmodpsapi                 = windows.NewLazySystemDLL(\"psapi.dll\")\n\tprocGetProcessMemoryInfo = modpsapi.NewProc(\"GetProcessMemoryInfo\")\n)\n\nconst processQueryInformation = windows.PROCESS_QUERY_LIMITED_INFORMATION\n\ntype PROCESS_MEMORY_COUNTERS struct {\n\tCB                         uint32\n\tPageFaultCount             uint32\n\tPeakWorkingSetSize         uint64\n\tWorkingSetSize             uint64\n\tQuotaPeakPagedPoolUsage    uint64\n\tQuotaPagedPoolUsage        uint64\n\tQuotaPeakNonPagedPoolUsage uint64\n\tQuotaNonPagedPoolUsage     uint64\n\tPagefileUsage              uint64\n\tPeakPagefileUsage          uint64\n}\n\nfunc getProcessMemoryInfo(h windows.Handle, mem *PROCESS_MEMORY_COUNTERS) (err error) {\n\tr1, _, e1 := syscall.Syscall(procGetProcessMemoryInfo.Addr(), 3, uintptr(h), uintptr(unsafe.Pointer(mem)), uintptr(unsafe.Sizeof(*mem)))\n\tif r1 == 0 {\n\t\tif e1 != 0 {\n\t\t\terr = error(e1)\n\t\t} else {\n\t\t\terr = syscall.EINVAL\n\t\t}\n\t}\n\treturn\n}\n\nfunc getMemoryInfo(pid int32) (PROCESS_MEMORY_COUNTERS, error) {\n\tvar mem PROCESS_MEMORY_COUNTERS\n\tc, err := windows.OpenProcess(processQueryInformation, false, uint32(pid))\n\tif err != nil {\n\t\treturn mem, err\n\t}\n\tdefer windows.CloseHandle(c)\n\tif err := getProcessMemoryInfo(c, &mem); err != nil {\n\t\treturn mem, err\n\t}\n\n\treturn mem, err\n}\n\nfunc GetMemoryInfo(pid int32) (*MemoryInfoStat, error) {\n\tmem, err := getMemoryInfo(pid)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tret := &MemoryInfoStat{\n\t\tRSS: uint64(mem.WorkingSetSize),\n\t\tVMS: uint64(mem.PagefileUsage),\n\t}\n\treturn ret, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/mmdb/mmdb.go",
    "content": "package mmdb\n\nimport (\n\t\"sync\"\n\n\tmihomoOnce \"github.com/metacubex/mihomo/common/once\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/oschwald/maxminddb-golang\"\n)\n\ntype databaseType = uint8\n\nconst (\n\ttypeMaxmind databaseType = iota\n\ttypeSing\n\ttypeMetaV0\n)\n\nvar (\n\tipReader  IPReader\n\tasnReader ASNReader\n\tipOnce    sync.Once\n\tasnOnce   sync.Once\n)\n\nfunc LoadFromBytes(buffer []byte) {\n\tipOnce.Do(func() {\n\t\tmmdb, err := maxminddb.FromBytes(buffer)\n\t\tif err != nil {\n\t\t\tlog.Fatalln(\"Can't load mmdb: %s\", err.Error())\n\t\t}\n\t\tipReader = IPReader{Reader: mmdb}\n\t\tswitch mmdb.Metadata.DatabaseType {\n\t\tcase \"sing-geoip\":\n\t\t\tipReader.databaseType = typeSing\n\t\tcase \"Meta-geoip0\":\n\t\t\tipReader.databaseType = typeMetaV0\n\t\tdefault:\n\t\t\tipReader.databaseType = typeMaxmind\n\t\t}\n\t})\n}\n\nfunc Verify(path string) bool {\n\tinstance, err := maxminddb.Open(path)\n\tif err == nil {\n\t\tinstance.Close()\n\t}\n\treturn err == nil\n}\n\nfunc IPInstance() IPReader {\n\tipOnce.Do(func() {\n\t\tmmdbPath := C.Path.MMDB()\n\t\tlog.Infoln(\"Load MMDB file: %s\", mmdbPath)\n\t\tmmdb, err := maxminddb.Open(mmdbPath)\n\t\tif err != nil {\n\t\t\tlog.Fatalln(\"Can't load MMDB: %s\", err.Error())\n\t\t}\n\t\tipReader = IPReader{Reader: mmdb}\n\t\tswitch mmdb.Metadata.DatabaseType {\n\t\tcase \"sing-geoip\":\n\t\t\tipReader.databaseType = typeSing\n\t\tcase \"Meta-geoip0\":\n\t\t\tipReader.databaseType = typeMetaV0\n\t\tdefault:\n\t\t\tipReader.databaseType = typeMaxmind\n\t\t}\n\t})\n\n\treturn ipReader\n}\n\nfunc ASNInstance() ASNReader {\n\tasnOnce.Do(func() {\n\t\tASNPath := C.Path.ASN()\n\t\tlog.Infoln(\"Load ASN file: %s\", ASNPath)\n\t\tasn, err := maxminddb.Open(ASNPath)\n\t\tif err != nil {\n\t\t\tlog.Fatalln(\"Can't load ASN: %s\", err.Error())\n\t\t}\n\t\tasnReader = ASNReader{Reader: asn}\n\t})\n\n\treturn asnReader\n}\n\nfunc ReloadIP() {\n\tmihomoOnce.Reset(&ipOnce)\n}\n\nfunc ReloadASN() {\n\tmihomoOnce.Reset(&asnOnce)\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/mmdb/reader.go",
    "content": "package mmdb\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/oschwald/maxminddb-golang\"\n)\n\ntype geoip2Country struct {\n\tCountry struct {\n\t\tIsoCode string `maxminddb:\"iso_code\"`\n\t} `maxminddb:\"country\"`\n}\n\ntype IPReader struct {\n\t*maxminddb.Reader\n\tdatabaseType\n}\n\ntype ASNReader struct {\n\t*maxminddb.Reader\n}\n\ntype GeoLite2 struct {\n\tAutonomousSystemNumber       uint32 `maxminddb:\"autonomous_system_number\"`\n\tAutonomousSystemOrganization string `maxminddb:\"autonomous_system_organization\"`\n}\n\ntype IPInfo struct {\n\tASN  string `maxminddb:\"asn\"`\n\tName string `maxminddb:\"name\"`\n}\n\nfunc (r IPReader) LookupCode(ipAddress net.IP) []string {\n\tswitch r.databaseType {\n\tcase typeMaxmind:\n\t\tvar country geoip2Country\n\t\t_ = r.Lookup(ipAddress, &country)\n\t\tif country.Country.IsoCode == \"\" {\n\t\t\treturn []string{}\n\t\t}\n\t\treturn []string{strings.ToLower(country.Country.IsoCode)}\n\n\tcase typeSing:\n\t\tvar code string\n\t\t_ = r.Lookup(ipAddress, &code)\n\t\tif code == \"\" {\n\t\t\treturn []string{}\n\t\t}\n\t\treturn []string{code}\n\n\tcase typeMetaV0:\n\t\tvar record any\n\t\t_ = r.Lookup(ipAddress, &record)\n\t\tswitch record := record.(type) {\n\t\tcase string:\n\t\t\treturn []string{record}\n\t\tcase []any: // lookup returned type of slice is []any\n\t\t\tresult := make([]string, 0, len(record))\n\t\t\tfor _, item := range record {\n\t\t\t\tresult = append(result, item.(string))\n\t\t\t}\n\t\t\treturn result\n\t\t}\n\t\treturn []string{}\n\n\tdefault:\n\t\tpanic(fmt.Sprint(\"unknown geoip database type:\", r.databaseType))\n\t}\n}\n\nfunc (r ASNReader) LookupASN(ip net.IP) (string, string) {\n\tswitch r.Metadata.DatabaseType {\n\tcase \"GeoLite2-ASN\", \"DBIP-ASN-Lite (compat=GeoLite2-ASN)\":\n\t\tvar result GeoLite2\n\t\t_ = r.Lookup(ip, &result)\n\t\treturn fmt.Sprint(result.AutonomousSystemNumber), result.AutonomousSystemOrganization\n\tcase \"ipinfo generic_asn_free.mmdb\":\n\t\tvar result IPInfo\n\t\t_ = r.Lookup(ip, &result)\n\t\treturn result.ASN[2:], result.Name\n\tdefault:\n\t\tlog.Warnln(\"Unsupported ASN type: %s\", r.Metadata.DatabaseType)\n\t}\n\treturn \"\", \"\"\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/mptcp/mptcp_go120.go",
    "content": "//go:build !go1.21\n\npackage mptcp\n\nimport (\n\t\"net\"\n)\n\nconst MultipathTCPAvailable = false\n\nfunc SetNetDialer(dialer *net.Dialer, open bool) {\n}\n\nfunc GetNetDialer(dialer *net.Dialer) bool {\n\treturn false\n}\n\nfunc SetNetListenConfig(listenConfig *net.ListenConfig, open bool) {\n}\n\nfunc GetNetListenConfig(listenConfig *net.ListenConfig) bool {\n\treturn false\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/mptcp/mptcp_go121.go",
    "content": "//go:build go1.21\n\npackage mptcp\n\nimport \"net\"\n\nconst MultipathTCPAvailable = true\n\nfunc SetNetDialer(dialer *net.Dialer, open bool) {\n\tdialer.SetMultipathTCP(open)\n}\n\nfunc GetNetDialer(dialer *net.Dialer) bool {\n\treturn dialer.MultipathTCP()\n}\n\nfunc SetNetListenConfig(listenConfig *net.ListenConfig, open bool) {\n\tlistenConfig.SetMultipathTCP(open)\n}\n\nfunc GetNetListenConfig(listenConfig *net.ListenConfig) bool {\n\treturn listenConfig.MultipathTCP()\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/nat/proxy.go",
    "content": "package nat\n\nimport (\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/common/atomic\"\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\ntype writeBackProxy struct {\n\twb atomic.TypedValue[C.WriteBack]\n}\n\nfunc (w *writeBackProxy) WriteBack(b []byte, addr net.Addr) (n int, err error) {\n\treturn w.wb.Load().WriteBack(b, addr)\n}\n\nfunc (w *writeBackProxy) UpdateWriteBack(wb C.WriteBack) {\n\tw.wb.Store(wb)\n}\n\nfunc NewWriteBackProxy(wb C.WriteBack) C.WriteBackProxy {\n\tw := &writeBackProxy{}\n\tw.UpdateWriteBack(wb)\n\treturn w\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/nat/table.go",
    "content": "package nat\n\nimport (\n\t\"net\"\n\t\"sync\"\n\n\t\"github.com/metacubex/mihomo/common/xsync\"\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\ntype Table struct {\n\tmapping xsync.Map[string, *entry]\n}\n\ntype entry struct {\n\tPacketSender    C.PacketSender\n\tLocalUDPConnMap xsync.Map[string, *net.UDPConn]\n\tLocalLockMap    xsync.Map[string, *sync.Cond]\n}\n\nfunc (t *Table) GetOrCreate(key string, maker func() C.PacketSender) (C.PacketSender, bool) {\n\titem, loaded := t.mapping.LoadOrStoreFn(key, func() *entry {\n\t\treturn &entry{\n\t\t\tPacketSender: maker(),\n\t\t}\n\t})\n\treturn item.PacketSender, loaded\n}\n\nfunc (t *Table) Delete(key string) {\n\tt.mapping.Delete(key)\n}\n\nfunc (t *Table) GetForLocalConn(lAddr, rAddr string) *net.UDPConn {\n\tentry, exist := t.getEntry(lAddr)\n\tif !exist {\n\t\treturn nil\n\t}\n\titem, exist := entry.LocalUDPConnMap.Load(rAddr)\n\tif !exist {\n\t\treturn nil\n\t}\n\treturn item\n}\n\nfunc (t *Table) AddForLocalConn(lAddr, rAddr string, conn *net.UDPConn) bool {\n\tentry, exist := t.getEntry(lAddr)\n\tif !exist {\n\t\treturn false\n\t}\n\tentry.LocalUDPConnMap.Store(rAddr, conn)\n\treturn true\n}\n\nfunc (t *Table) RangeForLocalConn(lAddr string, f func(key string, value *net.UDPConn) bool) {\n\tentry, exist := t.getEntry(lAddr)\n\tif !exist {\n\t\treturn\n\t}\n\tentry.LocalUDPConnMap.Range(f)\n}\n\nfunc (t *Table) GetOrCreateLockForLocalConn(lAddr, key string) (*sync.Cond, bool) {\n\tentry, loaded := t.getEntry(lAddr)\n\tif !loaded {\n\t\treturn nil, false\n\t}\n\titem, loaded := entry.LocalLockMap.LoadOrStoreFn(key, makeLock)\n\treturn item, loaded\n}\n\nfunc (t *Table) DeleteForLocalConn(lAddr, key string) {\n\tentry, loaded := t.getEntry(lAddr)\n\tif !loaded {\n\t\treturn\n\t}\n\tentry.LocalUDPConnMap.Delete(key)\n}\n\nfunc (t *Table) DeleteLockForLocalConn(lAddr, key string) {\n\tentry, loaded := t.getEntry(lAddr)\n\tif !loaded {\n\t\treturn\n\t}\n\tentry.LocalLockMap.Delete(key)\n}\n\nfunc (t *Table) getEntry(key string) (*entry, bool) {\n\treturn t.mapping.Load(key)\n}\n\nfunc makeLock() *sync.Cond {\n\treturn sync.NewCond(&sync.Mutex{})\n}\n\n// New return *Cache\nfunc New() *Table {\n\treturn &Table{}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/pool/pool.go",
    "content": "package pool\n\nimport (\n\t\"context\"\n\t\"runtime\"\n\t\"time\"\n)\n\ntype Factory[T any] func(context.Context) (T, error)\n\ntype entry[T any] struct {\n\telm  T\n\ttime time.Time\n}\n\ntype Option[T any] func(*pool[T])\n\n// WithEvict set the evict callback\nfunc WithEvict[T any](cb func(T)) Option[T] {\n\treturn func(p *pool[T]) {\n\t\tp.evict = cb\n\t}\n}\n\n// WithAge defined element max age (millisecond)\nfunc WithAge[T any](maxAge int64) Option[T] {\n\treturn func(p *pool[T]) {\n\t\tp.maxAge = maxAge\n\t}\n}\n\n// WithSize defined max size of Pool\nfunc WithSize[T any](maxSize int) Option[T] {\n\treturn func(p *pool[T]) {\n\t\tp.ch = make(chan *entry[T], maxSize)\n\t}\n}\n\n// Pool is for GC, see New for detail\ntype Pool[T any] struct {\n\t*pool[T]\n}\n\ntype pool[T any] struct {\n\tch      chan *entry[T]\n\tfactory Factory[T]\n\tevict   func(T)\n\tmaxAge  int64\n}\n\nfunc (p *pool[T]) GetContext(ctx context.Context) (T, error) {\n\tnow := time.Now()\n\tfor {\n\t\tselect {\n\t\tcase item := <-p.ch:\n\t\t\telm := item\n\t\t\tif p.maxAge != 0 && now.Sub(item.time).Milliseconds() > p.maxAge {\n\t\t\t\tif p.evict != nil {\n\t\t\t\t\tp.evict(elm.elm)\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\treturn elm.elm, nil\n\t\tdefault:\n\t\t\treturn p.factory(ctx)\n\t\t}\n\t}\n}\n\nfunc (p *pool[T]) Get() (T, error) {\n\treturn p.GetContext(context.Background())\n}\n\nfunc (p *pool[T]) Put(item T) {\n\te := &entry[T]{\n\t\telm:  item,\n\t\ttime: time.Now(),\n\t}\n\n\tselect {\n\tcase p.ch <- e:\n\t\treturn\n\tdefault:\n\t\t// pool is full\n\t\tif p.evict != nil {\n\t\t\tp.evict(item)\n\t\t}\n\t\treturn\n\t}\n}\n\nfunc recycle[T any](p *Pool[T]) {\n\tfor item := range p.pool.ch {\n\t\tif p.pool.evict != nil {\n\t\t\tp.pool.evict(item.elm)\n\t\t}\n\t}\n}\n\nfunc New[T any](factory Factory[T], options ...Option[T]) *Pool[T] {\n\tp := &pool[T]{\n\t\tch:      make(chan *entry[T], 10),\n\t\tfactory: factory,\n\t}\n\n\tfor _, option := range options {\n\t\toption(p)\n\t}\n\n\tP := &Pool[T]{p}\n\truntime.SetFinalizer(P, recycle[T])\n\treturn P\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/pool/pool_test.go",
    "content": "package pool\n\nimport (\n\t\"context\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc lg() Factory[int] {\n\tinitial := -1\n\treturn func(context.Context) (int, error) {\n\t\tinitial++\n\t\treturn initial, nil\n\t}\n}\n\nfunc TestPool_Basic(t *testing.T) {\n\tg := lg()\n\tpool := New[int](g)\n\n\telm, _ := pool.Get()\n\tassert.Equal(t, 0, elm)\n\tpool.Put(elm)\n\telm, _ = pool.Get()\n\tassert.Equal(t, 0, elm)\n\telm, _ = pool.Get()\n\tassert.Equal(t, 1, elm)\n}\n\nfunc TestPool_MaxSize(t *testing.T) {\n\tg := lg()\n\tsize := 5\n\tpool := New[int](g, WithSize[int](size))\n\n\tvar items []int\n\n\tfor i := 0; i < size; i++ {\n\t\titem, _ := pool.Get()\n\t\titems = append(items, item)\n\t}\n\n\textra, _ := pool.Get()\n\tassert.Equal(t, size, extra)\n\n\tfor _, item := range items {\n\t\tpool.Put(item)\n\t}\n\n\tpool.Put(extra)\n\n\tfor _, item := range items {\n\t\telm, _ := pool.Get()\n\t\tassert.Equal(t, item, elm)\n\t}\n}\n\nfunc TestPool_MaxAge(t *testing.T) {\n\tg := lg()\n\tpool := New[int](g, WithAge[int](20))\n\n\telm, _ := pool.Get()\n\tpool.Put(elm)\n\n\telm, _ = pool.Get()\n\tassert.Equal(t, 0, elm)\n\tpool.Put(elm)\n\n\ttime.Sleep(time.Millisecond * 22)\n\telm, _ = pool.Get()\n\tassert.Equal(t, 1, elm)\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/power/event.go",
    "content": "package power\n\ntype Type uint8\n\nconst (\n\tSUSPEND Type = iota\n\tRESUME\n\tRESUMEAUTOMATIC // Because the user is not present, most applications should do nothing.\n)\n\nfunc (t Type) String() string {\n\tswitch t {\n\tcase SUSPEND:\n\t\treturn \"SUSPEND\"\n\tcase RESUME:\n\t\treturn \"RESUME\"\n\tcase RESUMEAUTOMATIC:\n\t\treturn \"RESUMEAUTOMATIC\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/power/event_other.go",
    "content": "//go:build !windows\n\npackage power\n\nimport \"errors\"\n\nfunc NewEventListener(cb func(Type)) (func(), error) {\n\treturn nil, errors.New(\"not support on this platform\")\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/power/event_windows.go",
    "content": "package power\n\n// modify from https://github.com/golang/go/blob/b634f6fdcbebee23b7da709a243f3db217b64776/src/runtime/os_windows.go#L257\n\nimport (\n\t\"runtime\"\n\t\"unsafe\"\n\n\t\"golang.org/x/sys/windows\"\n)\n\nvar (\n\tlibPowrProf                              = windows.NewLazySystemDLL(\"powrprof.dll\")\n\tpowerRegisterSuspendResumeNotification   = libPowrProf.NewProc(\"PowerRegisterSuspendResumeNotification\")\n\tpowerUnregisterSuspendResumeNotification = libPowrProf.NewProc(\"PowerUnregisterSuspendResumeNotification\")\n)\n\nfunc NewEventListener(cb func(Type)) (func(), error) {\n\tif err := powerRegisterSuspendResumeNotification.Find(); err != nil {\n\t\treturn nil, err // Running on Windows 7, where we don't need it anyway.\n\t}\n\tif err := powerUnregisterSuspendResumeNotification.Find(); err != nil {\n\t\treturn nil, err // Running on Windows 7, where we don't need it anyway.\n\t}\n\n\t// Defines the type of event\n\tconst (\n\t\tPBT_APMSUSPEND         uint32 = 4\n\t\tPBT_APMRESUMESUSPEND   uint32 = 7\n\t\tPBT_APMRESUMEAUTOMATIC uint32 = 18\n\t)\n\n\tconst (\n\t\t_DEVICE_NOTIFY_CALLBACK = 2\n\t)\n\ttype _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS struct {\n\t\tcallback uintptr\n\t\tcontext  uintptr\n\t}\n\n\tvar fn interface{} = func(context uintptr, changeType uint32, setting uintptr) uintptr {\n\t\tswitch changeType {\n\t\tcase PBT_APMSUSPEND:\n\t\t\tcb(SUSPEND)\n\t\tcase PBT_APMRESUMESUSPEND:\n\t\t\tcb(RESUME)\n\t\tcase PBT_APMRESUMEAUTOMATIC:\n\t\t\tcb(RESUMEAUTOMATIC)\n\t\t}\n\t\treturn 0\n\t}\n\n\tparams := _DEVICE_NOTIFY_SUBSCRIBE_PARAMETERS{\n\t\tcallback: windows.NewCallback(fn),\n\t}\n\thandle := uintptr(0)\n\n\t// DWORD PowerRegisterSuspendResumeNotification(\n\t//  [in]  DWORD         Flags,\n\t//  [in]  HANDLE        Recipient,\n\t//  [out] PHPOWERNOTIFY RegistrationHandle\n\t//);\n\t_, _, err := powerRegisterSuspendResumeNotification.Call(\n\t\t_DEVICE_NOTIFY_CALLBACK,\n\t\tuintptr(unsafe.Pointer(&params)),\n\t\tuintptr(unsafe.Pointer(&handle)),\n\t)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn func() {\n\t\t// DWORD PowerUnregisterSuspendResumeNotification(\n\t\t//  [in, out] HPOWERNOTIFY RegistrationHandle\n\t\t//);\n\t\t_, _, _ = powerUnregisterSuspendResumeNotification.Call(\n\t\t\thandle,\n\t\t)\n\t\truntime.KeepAlive(params)\n\t\truntime.KeepAlive(handle)\n\t}, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/process/find_process_mode.go",
    "content": "package process\n\nimport (\n\t\"errors\"\n\t\"strings\"\n)\n\nconst (\n\tFindProcessStrict FindProcessMode = iota\n\tFindProcessAlways\n\tFindProcessOff\n)\n\nvar (\n\tvalidModes = map[string]FindProcessMode{\n\t\tFindProcessStrict.String(): FindProcessStrict,\n\t\tFindProcessAlways.String(): FindProcessAlways,\n\t\tFindProcessOff.String():    FindProcessOff,\n\t}\n)\n\ntype FindProcessMode int32\n\n// UnmarshalText unserialize FindProcessMode\nfunc (m *FindProcessMode) UnmarshalText(data []byte) error {\n\treturn m.Set(string(data))\n}\n\nfunc (m *FindProcessMode) Set(value string) error {\n\tmode, exist := validModes[strings.ToLower(value)]\n\tif !exist {\n\t\treturn errors.New(\"invalid find process mode\")\n\t}\n\t*m = mode\n\treturn nil\n}\n\n// MarshalText serialize FindProcessMode\nfunc (m FindProcessMode) MarshalText() ([]byte, error) {\n\treturn []byte(m.String()), nil\n}\n\nfunc (m FindProcessMode) String() string {\n\tswitch m {\n\tcase FindProcessAlways:\n\t\treturn \"always\"\n\tcase FindProcessOff:\n\t\treturn \"off\"\n\tdefault:\n\t\treturn \"strict\"\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/process/process.go",
    "content": "package process\n\nimport (\n\t\"errors\"\n\t\"net/netip\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\nvar (\n\tErrInvalidNetwork     = errors.New(\"invalid network\")\n\tErrPlatformNotSupport = errors.New(\"not support on this platform\")\n\tErrNotFound           = errors.New(\"process not found\")\n)\n\nconst (\n\tTCP = \"tcp\"\n\tUDP = \"udp\"\n)\n\nfunc FindProcessName(network string, srcIP netip.Addr, srcPort int) (uint32, string, error) {\n\treturn findProcessName(network, srcIP, srcPort)\n}\n\n// PackageNameResolver\n// never change type traits because it's used in CMFA\ntype PackageNameResolver func(metadata *C.Metadata) (string, error)\n\n// DefaultPackageNameResolver\n// never change type traits because it's used in CMFA\nvar DefaultPackageNameResolver PackageNameResolver\n\nfunc FindPackageName(metadata *C.Metadata) (string, error) {\n\tif resolver := DefaultPackageNameResolver; resolver != nil {\n\t\treturn resolver(metadata)\n\t}\n\treturn \"\", ErrPlatformNotSupport\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/process/process_darwin.go",
    "content": "package process\n\nimport (\n\t\"encoding/binary\"\n\t\"net/netip\"\n\t\"strconv\"\n\t\"strings\"\n\t\"syscall\"\n\t\"unsafe\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nconst (\n\tprocpidpathinfo     = 0xb\n\tprocpidpathinfosize = 1024\n\tproccallnumpidinfo  = 0x2\n)\n\nvar structSize = func() int {\n\tvalue, _ := syscall.Sysctl(\"kern.osrelease\")\n\tmajor, _, _ := strings.Cut(value, \".\")\n\tn, _ := strconv.ParseInt(major, 10, 64)\n\tswitch true {\n\tcase n >= 22:\n\t\treturn 408\n\tdefault:\n\t\t// from darwin-xnu/bsd/netinet/in_pcblist.c:get_pcblist_n\n\t\t// size/offset are round up (aligned) to 8 bytes in darwin\n\t\t// rup8(sizeof(xinpcb_n)) + rup8(sizeof(xsocket_n)) +\n\t\t// 2 * rup8(sizeof(xsockbuf_n)) + rup8(sizeof(xsockstat_n))\n\t\treturn 384\n\t}\n}()\n\nfunc findProcessName(network string, ip netip.Addr, port int) (uint32, string, error) {\n\tvar spath string\n\tswitch network {\n\tcase TCP:\n\t\tspath = \"net.inet.tcp.pcblist_n\"\n\tcase UDP:\n\t\tspath = \"net.inet.udp.pcblist_n\"\n\tdefault:\n\t\treturn 0, \"\", ErrInvalidNetwork\n\t}\n\n\tisIPv4 := ip.Is4()\n\n\tvalue, err := unix.SysctlRaw(spath)\n\tif err != nil {\n\t\treturn 0, \"\", err\n\t}\n\n\tbuf := value\n\titemSize := structSize\n\tif network == TCP {\n\t\t// rup8(sizeof(xtcpcb_n))\n\t\titemSize += 208\n\t}\n\n\tvar fallbackUDPProcess string\n\t// skip the first xinpgen(24 bytes) block\n\tfor i := 24; i+itemSize <= len(buf); i += itemSize {\n\t\t// offset of xinpcb_n and xsocket_n\n\t\tinp, so := i, i+104\n\n\t\tsrcPort := binary.BigEndian.Uint16(buf[inp+18 : inp+20])\n\t\tif uint16(port) != srcPort {\n\t\t\tcontinue\n\t\t}\n\n\t\t// xinpcb_n.inp_vflag\n\t\tflag := buf[inp+44]\n\n\t\tvar (\n\t\t\tsrcIP     netip.Addr\n\t\t\tsrcIsIPv4 bool\n\t\t)\n\t\tswitch {\n\t\tcase flag&0x1 > 0 && isIPv4:\n\t\t\t// ipv4\n\t\t\tsrcIP, _ = netip.AddrFromSlice(buf[inp+76 : inp+80])\n\t\t\tsrcIsIPv4 = true\n\t\tcase flag&0x2 > 0 && !isIPv4:\n\t\t\t// ipv6\n\t\t\tsrcIP, _ = netip.AddrFromSlice(buf[inp+64 : inp+80])\n\t\tdefault:\n\t\t\tcontinue\n\t\t}\n\t\tsrcIP = srcIP.Unmap()\n\n\t\tif ip == srcIP {\n\t\t\t// xsocket_n.so_last_pid\n\t\t\tpid := readNativeUint32(buf[so+68 : so+72])\n\t\t\tpp, err := getExecPathFromPID(pid)\n\t\t\treturn 0, pp, err\n\t\t}\n\n\t\t// udp packet connection may be not equal with srcIP\n\t\tif network == UDP && srcIP.IsUnspecified() && isIPv4 == srcIsIPv4 {\n\t\t\tfallbackUDPProcess, _ = getExecPathFromPID(readNativeUint32(buf[so+68 : so+72]))\n\t\t}\n\t}\n\n\tif network == UDP && fallbackUDPProcess != \"\" {\n\t\treturn 0, fallbackUDPProcess, nil\n\t}\n\n\treturn 0, \"\", ErrNotFound\n}\n\nfunc getExecPathFromPID(pid uint32) (string, error) {\n\tbuf := make([]byte, procpidpathinfosize)\n\t_, _, errno := syscall.Syscall6(\n\t\tsyscall.SYS_PROC_INFO,\n\t\tproccallnumpidinfo,\n\t\tuintptr(pid),\n\t\tprocpidpathinfo,\n\t\t0,\n\t\tuintptr(unsafe.Pointer(&buf[0])),\n\t\tprocpidpathinfosize)\n\tif errno != 0 {\n\t\treturn \"\", errno\n\t}\n\n\treturn unix.ByteSliceToString(buf), nil\n}\n\nfunc readNativeUint32(b []byte) uint32 {\n\treturn *(*uint32)(unsafe.Pointer(&b[0]))\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/process/process_freebsd_amd64.go",
    "content": "package process\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"net/netip\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"syscall\"\n\t\"unsafe\"\n\n\t\"github.com/metacubex/mihomo/log\"\n)\n\n// store process name for when dealing with multiple PROCESS-NAME rules\nvar (\n\tdefaultSearcher *searcher\n\n\tonce sync.Once\n)\n\nfunc findProcessName(network string, ip netip.Addr, srcPort int) (uint32, string, error) {\n\tonce.Do(func() {\n\t\tif err := initSearcher(); err != nil {\n\t\t\tlog.Errorln(\"Initialize PROCESS-NAME failed: %s\", err.Error())\n\t\t\tlog.Warnln(\"All PROCESS-NAME rules will be skipped\")\n\t\t\treturn\n\t\t}\n\t})\n\n\tif defaultSearcher == nil {\n\t\treturn 0, \"\", ErrPlatformNotSupport\n\t}\n\n\tvar spath string\n\tisTCP := network == TCP\n\tswitch network {\n\tcase TCP:\n\t\tspath = \"net.inet.tcp.pcblist\"\n\tcase UDP:\n\t\tspath = \"net.inet.udp.pcblist\"\n\tdefault:\n\t\treturn 0, \"\", ErrInvalidNetwork\n\t}\n\n\tvalue, err := syscall.Sysctl(spath)\n\tif err != nil {\n\t\treturn 0, \"\", err\n\t}\n\n\tbuf := []byte(value)\n\tpid, err := defaultSearcher.Search(buf, ip, uint16(srcPort), isTCP)\n\tif err != nil {\n\t\treturn 0, \"\", err\n\t}\n\n\tpp, err := getExecPathFromPID(pid)\n\treturn 0, pp, err\n}\n\nfunc getExecPathFromPID(pid uint32) (string, error) {\n\tbuf := make([]byte, 2048)\n\tsize := uint64(len(buf))\n\t// CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, pid\n\tmib := [4]uint32{1, 14, 12, pid}\n\n\t_, _, errno := syscall.Syscall6(\n\t\tsyscall.SYS___SYSCTL,\n\t\tuintptr(unsafe.Pointer(&mib[0])),\n\t\tuintptr(len(mib)),\n\t\tuintptr(unsafe.Pointer(&buf[0])),\n\t\tuintptr(unsafe.Pointer(&size)),\n\t\t0,\n\t\t0)\n\tif errno != 0 || size == 0 {\n\t\treturn \"\", errno\n\t}\n\n\treturn string(buf[:size-1]), nil\n}\n\nfunc readNativeUint32(b []byte) uint32 {\n\treturn *(*uint32)(unsafe.Pointer(&b[0]))\n}\n\ntype searcher struct {\n\t// sizeof(struct xinpgen)\n\theadSize int\n\t// sizeof(struct xtcpcb)\n\ttcpItemSize int\n\t// sizeof(struct xinpcb)\n\tudpItemSize  int\n\tudpInpOffset int\n\tport         int\n\tip           int\n\tvflag        int\n\tsocket       int\n\n\t// sizeof(struct xfile)\n\tfileItemSize int\n\tdata         int\n\tpid          int\n}\n\nfunc (s *searcher) Search(buf []byte, ip netip.Addr, port uint16, isTCP bool) (uint32, error) {\n\tvar itemSize int\n\tvar inpOffset int\n\n\tif isTCP {\n\t\t// struct xtcpcb\n\t\titemSize = s.tcpItemSize\n\t\tinpOffset = 8\n\t} else {\n\t\t// struct xinpcb\n\t\titemSize = s.udpItemSize\n\t\tinpOffset = s.udpInpOffset\n\t}\n\n\tisIPv4 := ip.Is4()\n\t// skip the first xinpgen block\n\tfor i := s.headSize; i+itemSize <= len(buf); i += itemSize {\n\t\tinp := i + inpOffset\n\n\t\tsrcPort := binary.BigEndian.Uint16(buf[inp+s.port : inp+s.port+2])\n\n\t\tif port != srcPort {\n\t\t\tcontinue\n\t\t}\n\n\t\t// xinpcb.inp_vflag\n\t\tflag := buf[inp+s.vflag]\n\n\t\tvar srcIP netip.Addr\n\t\tswitch {\n\t\tcase flag&0x1 > 0 && isIPv4:\n\t\t\t// ipv4\n\t\t\tsrcIP, _ = netip.AddrFromSlice(buf[inp+s.ip : inp+s.ip+4])\n\t\tcase flag&0x2 > 0 && !isIPv4:\n\t\t\t// ipv6\n\t\t\tsrcIP, _ = netip.AddrFromSlice(buf[inp+s.ip-12 : inp+s.ip+4])\n\t\tdefault:\n\t\t\tcontinue\n\t\t}\n\t\tsrcIP = srcIP.Unmap()\n\n\t\tif ip != srcIP {\n\t\t\tcontinue\n\t\t}\n\n\t\t// xsocket.xso_so, interpreted as big endian anyway since it's only used for comparison\n\t\tsocket := binary.BigEndian.Uint64(buf[inp+s.socket : inp+s.socket+8])\n\t\treturn s.searchSocketPid(socket)\n\t}\n\treturn 0, ErrNotFound\n}\n\nfunc (s *searcher) searchSocketPid(socket uint64) (uint32, error) {\n\tvalue, err := syscall.Sysctl(\"kern.file\")\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tbuf := []byte(value)\n\n\t// struct xfile\n\titemSize := s.fileItemSize\n\tfor i := 0; i+itemSize <= len(buf); i += itemSize {\n\t\t// xfile.xf_data\n\t\tdata := binary.BigEndian.Uint64(buf[i+s.data : i+s.data+8])\n\t\tif data == socket {\n\t\t\t// xfile.xf_pid\n\t\t\tpid := readNativeUint32(buf[i+s.pid : i+s.pid+4])\n\t\t\treturn pid, nil\n\t\t}\n\t}\n\treturn 0, ErrNotFound\n}\n\nfunc newSearcher(major int) *searcher {\n\tvar s *searcher\n\tswitch major {\n\tcase 11:\n\t\ts = &searcher{\n\t\t\theadSize:     32,\n\t\t\ttcpItemSize:  1304,\n\t\t\tudpItemSize:  632,\n\t\t\tport:         198,\n\t\t\tip:           228,\n\t\t\tvflag:        116,\n\t\t\tsocket:       88,\n\t\t\tfileItemSize: 80,\n\t\t\tdata:         56,\n\t\t\tpid:          8,\n\t\t\tudpInpOffset: 8,\n\t\t}\n\tcase 12:\n\t\tfallthrough\n\tcase 13:\n\t\tfallthrough\n\tcase 14:\n\t\tfallthrough\n\tcase 15:\n\t\ts = &searcher{\n\t\t\theadSize:     64,\n\t\t\ttcpItemSize:  744,\n\t\t\tudpItemSize:  400,\n\t\t\tport:         254,\n\t\t\tip:           284,\n\t\t\tvflag:        392,\n\t\t\tsocket:       16,\n\t\t\tfileItemSize: 128,\n\t\t\tdata:         56,\n\t\t\tpid:          8,\n\t\t}\n\t}\n\treturn s\n}\n\nfunc initSearcher() error {\n\tosRelease, err := syscall.Sysctl(\"kern.osrelease\")\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdot := strings.Index(osRelease, \".\")\n\tif dot != -1 {\n\t\tosRelease = osRelease[:dot]\n\t}\n\tmajor, err := strconv.Atoi(osRelease)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefaultSearcher = newSearcher(major)\n\tif defaultSearcher == nil {\n\t\treturn fmt.Errorf(\"unsupported freebsd version %d\", major)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/process/process_linux.go",
    "content": "package process\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"net/netip\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"syscall\"\n\t\"unicode\"\n\t\"unsafe\"\n\n\t\"github.com/mdlayher/netlink\"\n\t\"golang.org/x/sys/unix\"\n)\n\nconst (\n\tSOCK_DIAG_BY_FAMILY  = 20\n\tinetDiagRequestSize  = int(unsafe.Sizeof(inetDiagRequest{}))\n\tinetDiagResponseSize = int(unsafe.Sizeof(inetDiagResponse{}))\n)\n\ntype inetDiagRequest struct {\n\tFamily   byte\n\tProtocol byte\n\tExt      byte\n\tPad      byte\n\tStates   uint32\n\n\tSrcPort [2]byte\n\tDstPort [2]byte\n\tSrc     [16]byte\n\tDst     [16]byte\n\tIf      uint32\n\tCookie  [2]uint32\n}\n\ntype inetDiagResponse struct {\n\tFamily  byte\n\tState   byte\n\tTimer   byte\n\tReTrans byte\n\n\tSrcPort [2]byte\n\tDstPort [2]byte\n\tSrc     [16]byte\n\tDst     [16]byte\n\tIf      uint32\n\tCookie  [2]uint32\n\n\tExpires uint32\n\tRQueue  uint32\n\tWQueue  uint32\n\tUID     uint32\n\tINode   uint32\n}\n\nfunc findProcessName(network string, ip netip.Addr, srcPort int) (uint32, string, error) {\n\tuid, inode, err := resolveSocketByNetlink(network, ip, srcPort)\n\tif runtime.GOOS == \"android\" {\n\t\t// on Android (especially recent releases), netlink INET_DIAG can fail or return UID 0 / empty process info for some apps\n\t\t// so trying fallback to resolve /proc/net/{tcp,tcp6,udp,udp6}\n\t\tif err != nil {\n\t\t\tuid, inode, err = resolveSocketByProcFS(network, ip, srcPort)\n\t\t} else if uid == 0 {\n\t\t\tpUID, pInode, pErr := resolveSocketByProcFS(network, ip, srcPort)\n\t\t\tif pErr == nil && pUID != 0 {\n\t\t\t\tuid, inode, err = pUID, pInode, nil\n\t\t\t}\n\t\t}\n\t}\n\tif err != nil {\n\t\treturn 0, \"\", err\n\t}\n\tpp, err := resolveProcessNameByProcSearch(inode, uid)\n\tif runtime.GOOS == \"android\" {\n\t\t// if inode-based /proc/<pid>/fd resolution fails but UID is known,\n\t\t// fall back to resolving the process/package name by UID (typical on Android where all app processes share one UID).\n\t\tif err != nil && uid != 0 {\n\t\t\tpp, err = resolveProcessNameByUID(uid)\n\t\t}\n\t}\n\treturn uid, pp, err\n}\n\nfunc resolveSocketByNetlink(network string, ip netip.Addr, srcPort int) (uint32, uint32, error) {\n\trequest := &inetDiagRequest{\n\t\tStates: 0xffffffff,\n\t\tCookie: [2]uint32{0xffffffff, 0xffffffff},\n\t}\n\n\tif ip.Is4() {\n\t\trequest.Family = unix.AF_INET\n\t} else {\n\t\trequest.Family = unix.AF_INET6\n\t}\n\n\tif strings.HasPrefix(network, \"tcp\") {\n\t\trequest.Protocol = unix.IPPROTO_TCP\n\t} else if strings.HasPrefix(network, \"udp\") {\n\t\trequest.Protocol = unix.IPPROTO_UDP\n\t} else {\n\t\treturn 0, 0, ErrInvalidNetwork\n\t}\n\n\tcopy(request.Src[:], ip.AsSlice())\n\n\tbinary.BigEndian.PutUint16(request.SrcPort[:], uint16(srcPort))\n\n\tconn, err := netlink.Dial(unix.NETLINK_INET_DIAG, nil)\n\tif err != nil {\n\t\treturn 0, 0, err\n\t}\n\tdefer conn.Close()\n\n\tmessage := netlink.Message{\n\t\tHeader: netlink.Header{\n\t\t\tType:  SOCK_DIAG_BY_FAMILY,\n\t\t\tFlags: netlink.Request | netlink.Dump,\n\t\t},\n\t\tData: (*(*[inetDiagRequestSize]byte)(unsafe.Pointer(request)))[:],\n\t}\n\n\tmessages, err := conn.Execute(message)\n\tif err != nil {\n\t\treturn 0, 0, err\n\t}\n\n\tfor _, msg := range messages {\n\t\tif len(msg.Data) < inetDiagResponseSize {\n\t\t\tcontinue\n\t\t}\n\n\t\tresponse := (*inetDiagResponse)(unsafe.Pointer(&msg.Data[0]))\n\n\t\treturn response.UID, response.INode, nil\n\t}\n\n\treturn 0, 0, ErrNotFound\n}\n\nfunc resolveProcessNameByProcSearch(inode, uid uint32) (string, error) {\n\tfiles, err := os.ReadDir(\"/proc\")\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tbuffer := make([]byte, unix.PathMax)\n\tsocket := fmt.Appendf(nil, \"socket:[%d]\", inode)\n\n\tfor _, f := range files {\n\t\tif !f.IsDir() || !isPid(f.Name()) {\n\t\t\tcontinue\n\t\t}\n\n\t\tinfo, err := f.Info()\n\t\tif err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tif info.Sys().(*syscall.Stat_t).Uid != uid {\n\t\t\tcontinue\n\t\t}\n\n\t\tprocessPath := filepath.Join(\"/proc\", f.Name())\n\t\tfdPath := filepath.Join(processPath, \"fd\")\n\n\t\tfds, err := os.ReadDir(fdPath)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tfor _, fd := range fds {\n\t\t\tn, err := unix.Readlink(filepath.Join(fdPath, fd.Name()), buffer)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif runtime.GOOS == \"android\" {\n\t\t\t\tif bytes.Equal(buffer[:n], socket) {\n\t\t\t\t\tcmdline, err := os.ReadFile(path.Join(processPath, \"cmdline\"))\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn \"\", err\n\t\t\t\t\t}\n\n\t\t\t\t\treturn splitCmdline(cmdline), nil\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tif bytes.Equal(buffer[:n], socket) {\n\t\t\t\t\treturn os.Readlink(filepath.Join(processPath, \"exe\"))\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn \"\", fmt.Errorf(\"process of uid(%d),inode(%d) not found\", uid, inode)\n}\n\n// resolveProcessNameByUID returns a process name for any process with uid.\n// On Android all processes of one app share the same UID; used when inode\n// lookup fails (socket closed / TIME_WAIT).\nfunc resolveProcessNameByUID(uid uint32) (string, error) {\n\tfiles, err := os.ReadDir(\"/proc\")\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tfor _, f := range files {\n\t\tif !f.IsDir() || !isPid(f.Name()) {\n\t\t\tcontinue\n\t\t}\n\n\t\tinfo, err := f.Info()\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tif info.Sys().(*syscall.Stat_t).Uid != uid {\n\t\t\tcontinue\n\t\t}\n\n\t\tprocessPath := filepath.Join(\"/proc\", f.Name())\n\t\tif runtime.GOOS == \"android\" {\n\t\t\tcmdline, err := os.ReadFile(path.Join(processPath, \"cmdline\"))\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif name := splitCmdline(cmdline); name != \"\" {\n\t\t\t\treturn name, nil\n\t\t\t}\n\t\t} else {\n\t\t\tif exe, err := os.Readlink(filepath.Join(processPath, \"exe\")); err == nil {\n\t\t\t\treturn exe, nil\n\t\t\t}\n\t\t}\n\t}\n\n\treturn \"\", fmt.Errorf(\"no process found with uid %d\", uid)\n}\n\nfunc splitCmdline(cmdline []byte) string {\n\tcmdline = bytes.Trim(cmdline, \" \")\n\n\tidx := bytes.IndexFunc(cmdline, func(r rune) bool {\n\t\treturn unicode.IsControl(r) || unicode.IsSpace(r)\n\t})\n\n\tif idx == -1 {\n\t\treturn filepath.Base(string(cmdline))\n\t}\n\treturn filepath.Base(string(cmdline[:idx]))\n}\n\nfunc isPid(s string) bool {\n\treturn strings.IndexFunc(s, func(r rune) bool {\n\t\treturn !unicode.IsDigit(r)\n\t}) == -1\n}\n\n// resolveSocketByProcFS finds UID and inode from /proc/net/{tcp,tcp6,udp,udp6}.\n// In TUN mode metadata sourceIP is often the gateway (e.g. fake-ip range), not\n// the socket's real local address; we match by local port first and prefer\n// exact IP+port when it matches.\nfunc resolveSocketByProcFS(network string, ip netip.Addr, srcPort int) (uint32, uint32, error) {\n\tvar proto string\n\tswitch {\n\tcase strings.HasPrefix(network, \"tcp\"):\n\t\tproto = \"tcp\"\n\tcase strings.HasPrefix(network, \"udp\"):\n\t\tproto = \"udp\"\n\tdefault:\n\t\treturn 0, 0, ErrInvalidNetwork\n\t}\n\n\ttargetPort := uint16(srcPort)\n\tunmapped := ip.Unmap()\n\tfiles := []string{\"/proc/net/\" + proto, \"/proc/net/\" + proto + \"6\"}\n\n\tvar bestUID, bestInode uint32\n\tfound := false\n\n\tfor _, path := range files {\n\t\tisV6 := strings.HasSuffix(path, \"6\")\n\n\t\tvar matchIP netip.Addr\n\t\tif unmapped.Is4() {\n\t\t\tif isV6 {\n\t\t\t\tmatchIP = netip.AddrFrom16(unmapped.As16())\n\t\t\t} else {\n\t\t\t\tmatchIP = unmapped\n\t\t\t}\n\t\t} else {\n\t\t\tif !isV6 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tmatchIP = unmapped\n\t\t}\n\n\t\tuid, inode, exact, err := searchProcNetFileByPort(path, matchIP, targetPort)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif exact {\n\t\t\treturn uid, inode, nil\n\t\t}\n\n\t\tif !found || (bestUID == 0 && uid != 0) {\n\t\t\tbestUID = uid\n\t\t\tbestInode = inode\n\t\t\tfound = true\n\t\t}\n\t}\n\n\tif found {\n\t\treturn bestUID, bestInode, nil\n\t}\n\treturn 0, 0, ErrNotFound\n}\n\n// searchProcNetFileByPort scans /proc/net/* for local_address matching targetPort.\n// Exact IP+port wins; else port-only (skips inode==0 entries used by TIME_WAIT).\nfunc searchProcNetFileByPort(path string, targetIP netip.Addr, targetPort uint16) (uid, inode uint32, exact bool, err error) {\n\tf, err := os.Open(path)\n\tif err != nil {\n\t\treturn 0, 0, false, err\n\t}\n\tdefer f.Close()\n\n\tisV6 := strings.HasSuffix(path, \"6\")\n\tscanner := bufio.NewScanner(f)\n\t// skip header\n\tscanner.Scan()\n\n\tfor scanner.Scan() {\n\t\tfields := strings.Fields(scanner.Text())\n\t\tif len(fields) < 10 {\n\t\t\tcontinue\n\t\t}\n\n\t\tlocalAddr := fields[1]\n\t\tparts := strings.Split(localAddr, \":\")\n\t\tif len(parts) != 2 {\n\t\t\tcontinue\n\t\t}\n\n\t\tportHex := parts[1]\n\t\tport, err := strconv.ParseUint(portHex, 16, 16)\n\t\tif err != nil || uint16(port) != targetPort {\n\t\t\tcontinue\n\t\t}\n\n\t\tinodeStr := fields[9]\n\t\tif inodeStr == \"0\" {\n\t\t\tcontinue // TIME_WAIT entries have inode 0\n\t\t}\n\t\tinode64, err := strconv.ParseUint(inodeStr, 10, 32)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tuid64, _ := strconv.ParseUint(fields[7], 10, 32)\n\n\t\taddrHex := parts[0]\n\t\tif isV6 {\n\t\t\taddrBytes, err := hex.DecodeString(addrHex)\n\t\t\tif err != nil || len(addrBytes) != 16 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t// IPv6 addresses in /proc/net/tcp6 are in network byte order (big-endian)\n\t\t\tvar addr [16]byte\n\t\t\tcopy(addr[:], addrBytes)\n\t\t\tparsedIP := netip.AddrFrom16(addr)\n\t\t\tif parsedIP == targetIP {\n\t\t\t\treturn uint32(uid64), uint32(inode64), true, nil\n\t\t\t}\n\t\t} else {\n\t\t\taddrBytes, err := hex.DecodeString(addrHex)\n\t\t\tif err != nil || len(addrBytes) != 4 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t// IPv4 addresses in /proc/net/tcp are in little-endian order\n\t\t\tparsedIP := netip.AddrFrom4([4]byte{addrBytes[3], addrBytes[2], addrBytes[1], addrBytes[0]})\n\t\t\tif parsedIP == targetIP {\n\t\t\t\treturn uint32(uid64), uint32(inode64), true, nil\n\t\t\t}\n\t\t}\n\n\t\t// port matched but IP didn't - save as best effort\n\t\tif !exact {\n\t\t\tuid = uint32(uid64)\n\t\t\tinode = uint32(inode64)\n\t\t\texact = false\n\t\t}\n\t}\n\n\treturn uid, inode, exact, scanner.Err()\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/process/process_other.go",
    "content": "//go:build !darwin && !linux && !windows && (!freebsd || !amd64)\n\npackage process\n\nimport \"net/netip\"\n\nfunc findProcessName(network string, ip netip.Addr, srcPort int) (uint32, string, error) {\n\treturn 0, \"\", ErrPlatformNotSupport\n}\n\nfunc resolveSocketByNetlink(network string, ip netip.Addr, srcPort int) (uint32, uint32, error) {\n\treturn 0, 0, ErrPlatformNotSupport\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/process/process_windows.go",
    "content": "package process\n\nimport (\n\t\"fmt\"\n\t\"net/netip\"\n\t\"sync\"\n\t\"syscall\"\n\t\"unsafe\"\n\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"golang.org/x/sys/windows\"\n)\n\nconst (\n\ttcpTableFunc      = \"GetExtendedTcpTable\"\n\ttcpTablePidConn   = 4\n\tudpTableFunc      = \"GetExtendedUdpTable\"\n\tudpTablePid       = 1\n\tqueryProcNameFunc = \"QueryFullProcessImageNameW\"\n)\n\nvar (\n\tgetExTCPTable uintptr\n\tgetExUDPTable uintptr\n\tqueryProcName uintptr\n\n\tonce sync.Once\n)\n\nfunc resolveSocketByNetlink(network string, ip netip.Addr, srcPort int) (uint32, uint32, error) {\n\treturn 0, 0, ErrPlatformNotSupport\n}\n\nfunc initWin32API() error {\n\th, err := windows.LoadLibrary(\"iphlpapi.dll\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"LoadLibrary iphlpapi.dll failed: %s\", err.Error())\n\t}\n\n\tgetExTCPTable, err = windows.GetProcAddress(h, tcpTableFunc)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"GetProcAddress of %s failed: %s\", tcpTableFunc, err.Error())\n\t}\n\n\tgetExUDPTable, err = windows.GetProcAddress(h, udpTableFunc)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"GetProcAddress of %s failed: %s\", udpTableFunc, err.Error())\n\t}\n\n\th, err = windows.LoadLibrary(\"kernel32.dll\")\n\tif err != nil {\n\t\treturn fmt.Errorf(\"LoadLibrary kernel32.dll failed: %s\", err.Error())\n\t}\n\n\tqueryProcName, err = windows.GetProcAddress(h, queryProcNameFunc)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"GetProcAddress of %s failed: %s\", queryProcNameFunc, err.Error())\n\t}\n\n\treturn nil\n}\n\nfunc findProcessName(network string, ip netip.Addr, srcPort int) (uint32, string, error) {\n\tonce.Do(func() {\n\t\terr := initWin32API()\n\t\tif err != nil {\n\t\t\tlog.Errorln(\"Initialize PROCESS-NAME failed: %s\", err.Error())\n\t\t\tlog.Warnln(\"All PROCESS-NAMES rules will be skipped\")\n\t\t\treturn\n\t\t}\n\t})\n\tfamily := windows.AF_INET\n\tif ip.Is6() {\n\t\tfamily = windows.AF_INET6\n\t}\n\n\tvar class int\n\tvar fn uintptr\n\tswitch network {\n\tcase TCP:\n\t\tfn = getExTCPTable\n\t\tclass = tcpTablePidConn\n\tcase UDP:\n\t\tfn = getExUDPTable\n\t\tclass = udpTablePid\n\tdefault:\n\t\treturn 0, \"\", ErrInvalidNetwork\n\t}\n\n\tbuf, err := getTransportTable(fn, family, class)\n\tif err != nil {\n\t\treturn 0, \"\", err\n\t}\n\n\ts := newSearcher(family == windows.AF_INET, network == TCP)\n\n\tpid, err := s.Search(buf, ip, uint16(srcPort))\n\tif err != nil {\n\t\treturn 0, \"\", err\n\t}\n\tpp, err := getExecPathFromPID(pid)\n\treturn 0, pp, err\n}\n\ntype searcher struct {\n\titemSize int\n\tport     int\n\tip       int\n\tipSize   int\n\tpid      int\n\ttcpState int\n}\n\nfunc (s *searcher) Search(b []byte, ip netip.Addr, port uint16) (uint32, error) {\n\tn := int(readNativeUint32(b[:4]))\n\titemSize := s.itemSize\n\tfor i := 0; i < n; i++ {\n\t\trow := b[4+itemSize*i : 4+itemSize*(i+1)]\n\n\t\tif s.tcpState >= 0 {\n\t\t\ttcpState := readNativeUint32(row[s.tcpState : s.tcpState+4])\n\t\t\t// MIB_TCP_STATE_ESTAB, only check established connections for TCP\n\t\t\tif tcpState != 5 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\t// according to MSDN, only the lower 16 bits of dwLocalPort are used and the port number is in network endian.\n\t\t// this field can be illustrated as follows depends on different machine endianess:\n\t\t//     little endian: [ MSB LSB  0   0  ]   interpret as native uint32 is ((LSB<<8)|MSB)\n\t\t//       big  endian: [  0   0  MSB LSB ]   interpret as native uint32 is ((MSB<<8)|LSB)\n\t\t// so we need an syscall.Ntohs on the lower 16 bits after read the port as native uint32\n\t\tsrcPort := syscall.Ntohs(uint16(readNativeUint32(row[s.port : s.port+4])))\n\t\tif srcPort != port {\n\t\t\tcontinue\n\t\t}\n\n\t\tsrcIP, _ := netip.AddrFromSlice(row[s.ip : s.ip+s.ipSize])\n\t\tsrcIP = srcIP.Unmap()\n\t\t// windows binds an unbound udp socket to 0.0.0.0/[::] while first sendto\n\t\tif ip != srcIP && (!srcIP.IsUnspecified() || s.tcpState != -1) {\n\t\t\tcontinue\n\t\t}\n\n\t\tpid := readNativeUint32(row[s.pid : s.pid+4])\n\t\treturn pid, nil\n\t}\n\treturn 0, ErrNotFound\n}\n\nfunc newSearcher(isV4, isTCP bool) *searcher {\n\tvar itemSize, port, ip, ipSize, pid int\n\ttcpState := -1\n\tswitch {\n\tcase isV4 && isTCP:\n\t\t// struct MIB_TCPROW_OWNER_PID\n\t\titemSize, port, ip, ipSize, pid, tcpState = 24, 8, 4, 4, 20, 0\n\tcase isV4 && !isTCP:\n\t\t// struct MIB_UDPROW_OWNER_PID\n\t\titemSize, port, ip, ipSize, pid = 12, 4, 0, 4, 8\n\tcase !isV4 && isTCP:\n\t\t// struct MIB_TCP6ROW_OWNER_PID\n\t\titemSize, port, ip, ipSize, pid, tcpState = 56, 20, 0, 16, 52, 48\n\tcase !isV4 && !isTCP:\n\t\t// struct MIB_UDP6ROW_OWNER_PID\n\t\titemSize, port, ip, ipSize, pid = 28, 20, 0, 16, 24\n\t}\n\n\treturn &searcher{\n\t\titemSize: itemSize,\n\t\tport:     port,\n\t\tip:       ip,\n\t\tipSize:   ipSize,\n\t\tpid:      pid,\n\t\ttcpState: tcpState,\n\t}\n}\n\nfunc getTransportTable(fn uintptr, family int, class int) ([]byte, error) {\n\tfor size, buf := uint32(8), make([]byte, 8); ; {\n\t\tptr := unsafe.Pointer(&buf[0])\n\t\terr, _, _ := syscall.Syscall6(fn, 6, uintptr(ptr), uintptr(unsafe.Pointer(&size)), 0, uintptr(family), uintptr(class), 0)\n\n\t\tswitch err {\n\t\tcase 0:\n\t\t\treturn buf, nil\n\t\tcase uintptr(syscall.ERROR_INSUFFICIENT_BUFFER):\n\t\t\tbuf = make([]byte, size)\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"syscall error: %d\", err)\n\t\t}\n\t}\n}\n\nfunc readNativeUint32(b []byte) uint32 {\n\treturn *(*uint32)(unsafe.Pointer(&b[0]))\n}\n\nfunc getExecPathFromPID(pid uint32) (string, error) {\n\t// kernel process starts with a colon in order to distinguish with normal processes\n\tswitch pid {\n\tcase 0:\n\t\t// reserved pid for system idle process\n\t\treturn \":System Idle Process\", nil\n\tcase 4:\n\t\t// reserved pid for windows kernel image\n\t\treturn \":System\", nil\n\t}\n\th, err := windows.OpenProcess(windows.PROCESS_QUERY_LIMITED_INFORMATION, false, pid)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer windows.CloseHandle(h)\n\n\tbuf := make([]uint16, syscall.MAX_LONG_PATH)\n\tsize := uint32(len(buf))\n\tr1, _, err := syscall.Syscall6(\n\t\tqueryProcName, 4,\n\t\tuintptr(h),\n\t\tuintptr(0),\n\t\tuintptr(unsafe.Pointer(&buf[0])),\n\t\tuintptr(unsafe.Pointer(&size)),\n\t\t0, 0)\n\tif r1 == 0 {\n\t\treturn \"\", err\n\t}\n\treturn syscall.UTF16ToString(buf[:size]), nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/profile/cachefile/cache.go",
    "content": "package cachefile\n\nimport (\n\t\"os\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/component/profile\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/metacubex/bbolt\"\n)\n\nvar (\n\tinitOnce     sync.Once\n\tfileMode     os.FileMode = 0o666\n\tdefaultCache *CacheFile\n\n\tbucketSelected         = []byte(\"selected\")\n\tbucketFakeip           = []byte(\"fakeip\")\n\tbucketFakeip6          = []byte(\"fakeip6\")\n\tbucketETag             = []byte(\"etag\")\n\tbucketSubscriptionInfo = []byte(\"subscriptioninfo\")\n\tbucketStorage          = []byte(\"storage\")\n)\n\n// CacheFile store and update the cache file\ntype CacheFile struct {\n\tDB *bbolt.DB\n}\n\nfunc (c *CacheFile) SetSelected(group, selected string) {\n\tif !profile.StoreSelected.Load() {\n\t\treturn\n\t} else if c.DB == nil {\n\t\treturn\n\t}\n\n\terr := c.DB.Batch(func(t *bbolt.Tx) error {\n\t\tbucket, err := t.CreateBucketIfNotExists(bucketSelected)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn bucket.Put([]byte(group), []byte(selected))\n\t})\n\tif err != nil {\n\t\tlog.Warnln(\"[CacheFile] write cache to %s failed: %s\", c.DB.Path(), err.Error())\n\t\treturn\n\t}\n}\n\nfunc (c *CacheFile) SelectedMap() map[string]string {\n\tif !profile.StoreSelected.Load() {\n\t\treturn nil\n\t} else if c.DB == nil {\n\t\treturn nil\n\t}\n\n\tmapping := map[string]string{}\n\tc.DB.View(func(t *bbolt.Tx) error {\n\t\tbucket := t.Bucket(bucketSelected)\n\t\tif bucket == nil {\n\t\t\treturn nil\n\t\t}\n\n\t\tc := bucket.Cursor()\n\t\tfor k, v := c.First(); k != nil; k, v = c.Next() {\n\t\t\tmapping[string(k)] = string(v)\n\t\t}\n\t\treturn nil\n\t})\n\treturn mapping\n}\n\nfunc (c *CacheFile) Close() error {\n\treturn c.DB.Close()\n}\n\nfunc initCache() {\n\toptions := bbolt.Options{Timeout: time.Second}\n\tdb, err := bbolt.Open(C.Path.Cache(), fileMode, &options)\n\tswitch err {\n\tcase bbolt.ErrInvalid, bbolt.ErrChecksum, bbolt.ErrVersionMismatch:\n\t\tif err = os.Remove(C.Path.Cache()); err != nil {\n\t\t\tlog.Warnln(\"[CacheFile] remove invalid cache file error: %s\", err.Error())\n\t\t\tbreak\n\t\t}\n\t\tlog.Infoln(\"[CacheFile] remove invalid cache file and create new one\")\n\t\tdb, err = bbolt.Open(C.Path.Cache(), fileMode, &options)\n\t}\n\tif err != nil {\n\t\tlog.Warnln(\"[CacheFile] can't open cache file: %s\", err.Error())\n\t}\n\n\tdefaultCache = &CacheFile{\n\t\tDB: db,\n\t}\n}\n\n// Cache return singleton of CacheFile\nfunc Cache() *CacheFile {\n\tinitOnce.Do(initCache)\n\n\treturn defaultCache\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/profile/cachefile/etag.go",
    "content": "package cachefile\n\nimport (\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/metacubex/bbolt\"\n\t\"github.com/vmihailenco/msgpack/v5\"\n)\n\ntype EtagWithHash struct {\n\tHash utils.HashType\n\tETag string\n\tTime time.Time\n}\n\nfunc (c *CacheFile) SetETagWithHash(url string, etagWithHash EtagWithHash) {\n\tif c.DB == nil {\n\t\treturn\n\t}\n\n\tdata, err := msgpack.Marshal(etagWithHash)\n\tif err != nil {\n\t\treturn // maybe panic is better\n\t}\n\n\terr = c.DB.Batch(func(t *bbolt.Tx) error {\n\t\tbucket, err := t.CreateBucketIfNotExists(bucketETag)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn bucket.Put([]byte(url), data)\n\t})\n\tif err != nil {\n\t\tlog.Warnln(\"[CacheFile] write cache to %s failed: %s\", c.DB.Path(), err.Error())\n\t\treturn\n\t}\n}\nfunc (c *CacheFile) GetETagWithHash(key string) (etagWithHash EtagWithHash) {\n\tif c.DB == nil {\n\t\treturn\n\t}\n\tc.DB.View(func(t *bbolt.Tx) error {\n\t\tif bucket := t.Bucket(bucketETag); bucket != nil {\n\t\t\tif v := bucket.Get([]byte(key)); v != nil {\n\t\t\t\tif err := msgpack.Unmarshal(v, &etagWithHash); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t})\n\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/profile/cachefile/fakeip.go",
    "content": "package cachefile\n\nimport (\n\t\"net/netip\"\n\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/metacubex/bbolt\"\n)\n\ntype FakeIpStore struct {\n\t*CacheFile\n\tbucketName []byte\n}\n\nfunc (c *CacheFile) FakeIpStore() *FakeIpStore {\n\treturn &FakeIpStore{c, bucketFakeip}\n}\n\nfunc (c *CacheFile) FakeIpStore6() *FakeIpStore {\n\treturn &FakeIpStore{c, bucketFakeip6}\n}\n\nfunc (c *FakeIpStore) GetByHost(host string) (ip netip.Addr, exist bool) {\n\tif c.DB == nil {\n\t\treturn\n\t}\n\tc.DB.View(func(t *bbolt.Tx) error {\n\t\tif bucket := t.Bucket(c.bucketName); bucket != nil {\n\t\t\tif v := bucket.Get([]byte(host)); v != nil {\n\t\t\t\tip, exist = netip.AddrFromSlice(v)\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t})\n\treturn\n}\n\nfunc (c *FakeIpStore) PutByHost(host string, ip netip.Addr) {\n\tif c.DB == nil {\n\t\treturn\n\t}\n\terr := c.DB.Batch(func(t *bbolt.Tx) error {\n\t\tbucket, err := t.CreateBucketIfNotExists(c.bucketName)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn bucket.Put([]byte(host), ip.AsSlice())\n\t})\n\tif err != nil {\n\t\tlog.Warnln(\"[CacheFile] write cache to %s failed: %s\", c.DB.Path(), err.Error())\n\t}\n}\n\nfunc (c *FakeIpStore) GetByIP(ip netip.Addr) (host string, exist bool) {\n\tif c.DB == nil {\n\t\treturn\n\t}\n\tc.DB.View(func(t *bbolt.Tx) error {\n\t\tif bucket := t.Bucket(c.bucketName); bucket != nil {\n\t\t\tif v := bucket.Get(ip.AsSlice()); v != nil {\n\t\t\t\thost, exist = string(v), true\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t})\n\treturn\n}\n\nfunc (c *FakeIpStore) PutByIP(ip netip.Addr, host string) {\n\tif c.DB == nil {\n\t\treturn\n\t}\n\terr := c.DB.Batch(func(t *bbolt.Tx) error {\n\t\tbucket, err := t.CreateBucketIfNotExists(c.bucketName)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn bucket.Put(ip.AsSlice(), []byte(host))\n\t})\n\tif err != nil {\n\t\tlog.Warnln(\"[CacheFile] write cache to %s failed: %s\", c.DB.Path(), err.Error())\n\t}\n}\n\nfunc (c *FakeIpStore) DelByIP(ip netip.Addr) {\n\tif c.DB == nil {\n\t\treturn\n\t}\n\n\taddr := ip.AsSlice()\n\terr := c.DB.Batch(func(t *bbolt.Tx) error {\n\t\tbucket, err := t.CreateBucketIfNotExists(c.bucketName)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\thost := bucket.Get(addr)\n\t\terr = bucket.Delete(addr)\n\t\tif len(host) > 0 {\n\t\t\tif err = bucket.Delete(host); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t\treturn err\n\t})\n\tif err != nil {\n\t\tlog.Warnln(\"[CacheFile] write cache to %s failed: %s\", c.DB.Path(), err.Error())\n\t}\n}\n\nfunc (c *FakeIpStore) FlushFakeIP() error {\n\terr := c.DB.Batch(func(t *bbolt.Tx) error {\n\t\tbucket := t.Bucket(c.bucketName)\n\t\tif bucket == nil {\n\t\t\treturn nil\n\t\t}\n\t\treturn t.DeleteBucket(c.bucketName)\n\t})\n\treturn err\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/profile/cachefile/storage.go",
    "content": "package cachefile\n\nimport (\n\t\"sort\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/metacubex/bbolt\"\n\t\"github.com/vmihailenco/msgpack/v5\"\n)\n\nconst storageSizeLimit = 1024 * 1024\nconst storageKeySizeLimit = 64\nconst maxStorageEntries = storageSizeLimit / storageKeySizeLimit\n\ntype StorageData struct {\n\tData []byte\n\tTime time.Time\n}\n\nfunc decodeStorageData(v []byte) (StorageData, error) {\n\tvar storage StorageData\n\tif err := msgpack.Unmarshal(v, &storage); err != nil {\n\t\treturn StorageData{}, err\n\t}\n\treturn storage, nil\n}\n\nfunc (c *CacheFile) GetStorage(key string) []byte {\n\tif c.DB == nil {\n\t\treturn nil\n\t}\n\tvar data []byte\n\tdecodeFailed := false\n\terr := c.DB.View(func(t *bbolt.Tx) error {\n\t\tif bucket := t.Bucket(bucketStorage); bucket != nil {\n\t\t\tif v := bucket.Get([]byte(key)); v != nil {\n\t\t\t\tstorage, err := decodeStorageData(v)\n\t\t\t\tif err != nil {\n\t\t\t\t\tdecodeFailed = true\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tdata = storage.Data\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\tlog.Warnln(\"[CacheFile] read cache for key %s failed: %s\", key, err.Error())\n\t\tif decodeFailed {\n\t\t\tc.DeleteStorage(key)\n\t\t}\n\t\treturn nil\n\t}\n\treturn data\n}\n\nfunc (c *CacheFile) SetStorage(key string, data []byte) {\n\tif c.DB == nil {\n\t\treturn\n\t}\n\tif len(key) > storageKeySizeLimit {\n\t\tlog.Warnln(\"[CacheFile] skip storage for key %s: key exceeds %d bytes\", key, storageKeySizeLimit)\n\t\treturn\n\t}\n\tif len(data) > storageSizeLimit {\n\t\tlog.Warnln(\"[CacheFile] skip storage for key %s: payload exceeds %d bytes\", key, storageSizeLimit)\n\t\treturn\n\t}\n\tkeyBytes := []byte(key)\n\tpayload, err := msgpack.Marshal(StorageData{\n\t\tData: data,\n\t\tTime: time.Now(),\n\t})\n\tif err != nil {\n\t\treturn\n\t}\n\terr = c.DB.Batch(func(t *bbolt.Tx) error {\n\t\tbucket, err := t.CreateBucketIfNotExists(bucketStorage)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\ttype storageEntry struct {\n\t\t\tKey  string\n\t\t\tData StorageData\n\t\t}\n\n\t\tentries := make(map[string]StorageData)\n\t\tusedSize := 0\n\t\tentryCount := 0\n\t\tcorruptedKeys := make([][]byte, 0)\n\t\tc := bucket.Cursor()\n\t\tfor k, v := c.First(); k != nil; k, v = c.Next() {\n\t\t\tstorage, err := decodeStorageData(v)\n\t\t\tif err != nil {\n\t\t\t\tlog.Warnln(\"[CacheFile] drop corrupted storage entry %s: %s\", string(k), err.Error())\n\t\t\t\tcorruptedKeys = append(corruptedKeys, append([]byte(nil), k...))\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tentryKey := string(k)\n\t\t\tentries[entryKey] = storage\n\t\t\tif entryKey != key {\n\t\t\t\tusedSize += len(storage.Data)\n\t\t\t\tentryCount++\n\t\t\t}\n\t\t}\n\t\tfor _, k := range corruptedKeys {\n\t\t\tif err := bucket.Delete(k); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\n\t\tevictionQueue := make([]storageEntry, 0, len(entries))\n\t\tfor entryKey, storage := range entries {\n\t\t\tif entryKey == key {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tevictionQueue = append(evictionQueue, storageEntry{\n\t\t\t\tKey:  entryKey,\n\t\t\t\tData: storage,\n\t\t\t})\n\t\t}\n\t\tsort.Slice(evictionQueue, func(i, j int) bool {\n\t\t\tleft := evictionQueue[i]\n\t\t\tright := evictionQueue[j]\n\t\t\tif left.Data.Time.Equal(right.Data.Time) {\n\t\t\t\treturn left.Key < right.Key\n\t\t\t}\n\t\t\treturn left.Data.Time.Before(right.Data.Time)\n\t\t})\n\n\t\tfor _, entry := range evictionQueue {\n\t\t\tif usedSize+len(data) <= storageSizeLimit && entryCount < maxStorageEntries {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tif err := bucket.Delete([]byte(entry.Key)); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tlog.Infoln(\"[CacheFile] evict storage entry %s to make room for %s\", entry.Key, key)\n\t\t\tusedSize -= len(entry.Data.Data)\n\t\t\tentryCount--\n\t\t}\n\t\treturn bucket.Put(keyBytes, payload)\n\t})\n\tif err != nil {\n\t\tlog.Warnln(\"[CacheFile] write cache to %s failed: %s\", c.DB.Path(), err.Error())\n\t}\n}\n\nfunc (c *CacheFile) DeleteStorage(key string) {\n\tif c.DB == nil {\n\t\treturn\n\t}\n\terr := c.DB.Batch(func(t *bbolt.Tx) error {\n\t\tbucket := t.Bucket(bucketStorage)\n\t\tif bucket == nil {\n\t\t\treturn nil\n\t\t}\n\t\treturn bucket.Delete([]byte(key))\n\t})\n\tif err != nil {\n\t\tlog.Warnln(\"[CacheFile] delete cache from %s failed: %s\", c.DB.Path(), err.Error())\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/profile/cachefile/subscriptioninfo.go",
    "content": "package cachefile\n\nimport (\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/metacubex/bbolt\"\n)\n\nfunc (c *CacheFile) SetSubscriptionInfo(name string, userInfo string) {\n\tif c.DB == nil {\n\t\treturn\n\t}\n\n\terr := c.DB.Batch(func(t *bbolt.Tx) error {\n\t\tbucket, err := t.CreateBucketIfNotExists(bucketSubscriptionInfo)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn bucket.Put([]byte(name), []byte(userInfo))\n\t})\n\tif err != nil {\n\t\tlog.Warnln(\"[CacheFile] write cache to %s failed: %s\", c.DB.Path(), err.Error())\n\t\treturn\n\t}\n}\nfunc (c *CacheFile) GetSubscriptionInfo(name string) (userInfo string) {\n\tif c.DB == nil {\n\t\treturn\n\t}\n\tc.DB.View(func(t *bbolt.Tx) error {\n\t\tif bucket := t.Bucket(bucketSubscriptionInfo); bucket != nil {\n\t\t\tif v := bucket.Get([]byte(name)); v != nil {\n\t\t\t\tuserInfo = string(v)\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t})\n\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/profile/profile.go",
    "content": "package profile\n\nimport (\n\t\"github.com/metacubex/mihomo/common/atomic\"\n)\n\n// StoreSelected is a global switch for storing selected proxy to cache\nvar StoreSelected = atomic.NewBool(true)\n"
  },
  {
    "path": "core/Clash.Meta/component/proxydialer/byname.go",
    "content": "package proxydialer\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/netip\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/tunnel\"\n)\n\ntype byNameProxyDialer struct {\n\tproxyName string\n}\n\nfunc (d byNameProxyDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {\n\tproxies := tunnel.Proxies()\n\tproxy, ok := proxies[d.proxyName]\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"proxyName[%s] not found\", d.proxyName)\n\t}\n\treturn New(proxy, true).DialContext(ctx, network, address)\n}\n\nfunc (d byNameProxyDialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) {\n\tproxies := tunnel.Proxies()\n\tproxy, ok := proxies[d.proxyName]\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"proxyName[%s] not found\", d.proxyName)\n\t}\n\treturn New(proxy, true).ListenPacket(ctx, network, address, rAddrPort)\n}\n\nfunc NewByName(proxyName string) C.Dialer {\n\treturn byNameProxyDialer{proxyName: proxyName}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/proxydialer/proxydialer.go",
    "content": "package proxydialer\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"net/netip\"\n\t\"strings\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/tunnel/statistic\"\n)\n\ntype proxyDialer struct {\n\tproxy     C.ProxyAdapter\n\tstatistic bool\n}\n\nfunc New(proxy C.ProxyAdapter, statistic bool) C.Dialer {\n\treturn proxyDialer{proxy: proxy, statistic: statistic}\n}\n\nfunc (p proxyDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {\n\tcurrentMeta := &C.Metadata{Type: C.INNER}\n\tif err := currentMeta.SetRemoteAddress(address); err != nil {\n\t\treturn nil, err\n\t}\n\tif strings.Contains(network, \"udp\") { // using in wireguard outbound\n\t\tpc, err := p.listenPacket(ctx, currentMeta)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif !currentMeta.Resolved() { // should not happen, maybe by a wrongly implemented proxy, but we can handle this (:\n\t\t\terr = pc.ResolveUDP(ctx, currentMeta)\n\t\t\tif err != nil {\n\t\t\t\t_ = pc.Close()\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\treturn N.NewBindPacketConn(pc, currentMeta.UDPAddr()), nil\n\t}\n\tconn, err := p.proxy.DialContext(ctx, currentMeta)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif p.statistic {\n\t\tconn = statistic.NewTCPTracker(conn, statistic.DefaultManager, currentMeta, nil, 0, 0, false)\n\t}\n\treturn conn, err\n}\n\nfunc (p proxyDialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) {\n\tcurrentMeta := &C.Metadata{Type: C.INNER, DstIP: rAddrPort.Addr(), DstPort: rAddrPort.Port()}\n\treturn p.listenPacket(ctx, currentMeta)\n}\n\nfunc (p proxyDialer) listenPacket(ctx context.Context, currentMeta *C.Metadata) (C.PacketConn, error) {\n\tcurrentMeta.NetWork = C.UDP\n\tpc, err := p.proxy.ListenPacketContext(ctx, currentMeta)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif p.statistic {\n\t\tpc = statistic.NewUDPTracker(pc, statistic.DefaultManager, currentMeta, nil, 0, 0, false)\n\t}\n\treturn pc, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/proxydialer/sing.go",
    "content": "package proxydialer\n\nimport (\n\t\"context\"\n\t\"net\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\n\tM \"github.com/metacubex/sing/common/metadata\"\n\tN \"github.com/metacubex/sing/common/network\"\n)\n\ntype SingDialer interface {\n\tN.Dialer\n}\n\ntype singDialer struct {\n\tcDialer C.Dialer\n}\n\nvar _ N.Dialer = (*singDialer)(nil)\n\nfunc (d singDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {\n\treturn d.cDialer.DialContext(ctx, network, destination.String())\n}\n\nfunc (d singDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {\n\treturn d.cDialer.ListenPacket(ctx, \"udp\", \"\", destination.AddrPort())\n}\n\nfunc NewSingDialer(cDialer C.Dialer) SingDialer {\n\treturn singDialer{cDialer: cDialer}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/proxydialer/slowdown.go",
    "content": "package proxydialer\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"net/netip\"\n\n\t\"github.com/metacubex/mihomo/component/slowdown\"\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\ntype SlowDownDialer struct {\n\tC.Dialer\n\tSlowdown *slowdown.SlowDown\n}\n\nfunc (d SlowDownDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {\n\treturn slowdown.Do(d.Slowdown, ctx, func() (net.Conn, error) {\n\t\treturn d.Dialer.DialContext(ctx, network, address)\n\t})\n}\n\nfunc (d SlowDownDialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) {\n\treturn slowdown.Do(d.Slowdown, ctx, func() (net.PacketConn, error) {\n\t\treturn d.Dialer.ListenPacket(ctx, network, address, rAddrPort)\n\t})\n}\n\nfunc NewSlowDownDialer(d C.Dialer, sd *slowdown.SlowDown) SlowDownDialer {\n\treturn SlowDownDialer{\n\t\tDialer:   d,\n\t\tSlowdown: sd,\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/proxydialer/slowdown_sing.go",
    "content": "package proxydialer\n\nimport (\n\t\"context\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/component/slowdown\"\n\tM \"github.com/metacubex/sing/common/metadata\"\n)\n\ntype SlowDownSingDialer struct {\n\tSingDialer\n\tSlowdown *slowdown.SlowDown\n}\n\nfunc (d SlowDownSingDialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {\n\treturn slowdown.Do(d.Slowdown, ctx, func() (net.Conn, error) {\n\t\treturn d.SingDialer.DialContext(ctx, network, destination)\n\t})\n}\n\nfunc (d SlowDownSingDialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {\n\treturn slowdown.Do(d.Slowdown, ctx, func() (net.PacketConn, error) {\n\t\treturn d.SingDialer.ListenPacket(ctx, destination)\n\t})\n}\n\nfunc NewSlowDownSingDialer(d SingDialer, sd *slowdown.SlowDown) SlowDownSingDialer {\n\treturn SlowDownSingDialer{\n\t\tSingDialer: d,\n\t\tSlowdown:   sd,\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/resolver/enhancer.go",
    "content": "package resolver\n\nimport \"net/netip\"\n\nvar DefaultHostMapper Enhancer\n\ntype Enhancer interface {\n\tFakeIPEnabled() bool\n\tMappingEnabled() bool\n\tIsFakeIP(netip.Addr) bool\n\tIsFakeBroadcastIP(netip.Addr) bool\n\tIsExistFakeIP(netip.Addr) bool\n\tFindHostByIP(netip.Addr) (string, bool)\n\tFlushFakeIP() error\n\tInsertHostByIP(netip.Addr, string)\n\tStoreFakePoolState()\n}\n\nfunc FakeIPEnabled() bool {\n\tif mapper := DefaultHostMapper; mapper != nil {\n\t\treturn mapper.FakeIPEnabled()\n\t}\n\n\treturn false\n}\n\nfunc MappingEnabled() bool {\n\tif mapper := DefaultHostMapper; mapper != nil {\n\t\treturn mapper.MappingEnabled()\n\t}\n\n\treturn false\n}\n\nfunc IsFakeIP(ip netip.Addr) bool {\n\tif mapper := DefaultHostMapper; mapper != nil {\n\t\treturn mapper.IsFakeIP(ip)\n\t}\n\n\treturn false\n}\n\nfunc IsFakeBroadcastIP(ip netip.Addr) bool {\n\tif mapper := DefaultHostMapper; mapper != nil {\n\t\treturn mapper.IsFakeBroadcastIP(ip)\n\t}\n\n\treturn false\n}\n\nfunc IsExistFakeIP(ip netip.Addr) bool {\n\tif mapper := DefaultHostMapper; mapper != nil {\n\t\treturn mapper.IsExistFakeIP(ip)\n\t}\n\n\treturn false\n}\n\nfunc InsertHostByIP(ip netip.Addr, host string) {\n\tif mapper := DefaultHostMapper; mapper != nil {\n\t\tmapper.InsertHostByIP(ip, host)\n\t}\n}\n\nfunc FindHostByIP(ip netip.Addr) (string, bool) {\n\tif mapper := DefaultHostMapper; mapper != nil {\n\t\treturn mapper.FindHostByIP(ip)\n\t}\n\n\treturn \"\", false\n}\n\nfunc FlushFakeIP() error {\n\tif mapper := DefaultHostMapper; mapper != nil {\n\t\treturn mapper.FlushFakeIP()\n\t}\n\treturn nil\n}\n\nfunc StoreFakePoolState() {\n\tif mapper := DefaultHostMapper; mapper != nil {\n\t\tmapper.StoreFakePoolState()\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/resolver/host.go",
    "content": "package resolver\n\nimport (\n\t\"errors\"\n\t\"net/netip\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t_ \"unsafe\"\n\n\t\"github.com/metacubex/mihomo/component/resolver/hosts\"\n\t\"github.com/metacubex/mihomo/component/trie\"\n\t\"github.com/metacubex/randv2\"\n)\n\nvar (\n\tDisableSystemHosts, _ = strconv.ParseBool(os.Getenv(\"DISABLE_SYSTEM_HOSTS\"))\n\tUseSystemHosts        bool\n)\n\ntype Hosts struct {\n\t*trie.DomainTrie[HostValue]\n}\n\nfunc NewHosts(hosts *trie.DomainTrie[HostValue]) Hosts {\n\treturn Hosts{\n\t\thosts,\n\t}\n}\n\n// Return the search result and whether to match the parameter `isDomain`\nfunc (h *Hosts) Search(domain string, isDomain bool) (*HostValue, bool) {\n\tif value := h.DomainTrie.Search(domain); value != nil {\n\t\thostValue := value.Data()\n\t\tfor {\n\t\t\tif isDomain && hostValue.IsDomain {\n\t\t\t\treturn &hostValue, true\n\t\t\t} else {\n\t\t\t\tif node := h.DomainTrie.Search(hostValue.Domain); node != nil {\n\t\t\t\t\thostValue = node.Data()\n\t\t\t\t} else {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif isDomain == hostValue.IsDomain {\n\t\t\treturn &hostValue, true\n\t\t}\n\n\t\treturn &hostValue, false\n\t}\n\n\tif !isDomain && !DisableSystemHosts && UseSystemHosts {\n\t\taddr, _ := hosts.LookupStaticHost(domain)\n\t\tif hostValue, err := NewHostValue(addr); err == nil {\n\t\t\treturn &hostValue, true\n\t\t}\n\t}\n\treturn nil, false\n}\n\ntype HostValue struct {\n\tIsDomain bool\n\tIPs      []netip.Addr\n\tDomain   string\n}\n\nfunc NewHostValue(value []string) (HostValue, error) {\n\tisDomain := true\n\tips := make([]netip.Addr, 0, len(value))\n\tdomain := \"\"\n\tswitch len(value) {\n\tcase 0:\n\t\treturn HostValue{}, errors.New(\"value is empty\")\n\tcase 1:\n\t\thost := value[0]\n\t\tif ip, err := netip.ParseAddr(host); err == nil {\n\t\t\tips = append(ips, ip.Unmap())\n\t\t\tisDomain = false\n\t\t} else {\n\t\t\tdomain = host\n\t\t}\n\tdefault: // > 1\n\t\tisDomain = false\n\t\tfor _, str := range value {\n\t\t\tif ip, err := netip.ParseAddr(str); err == nil {\n\t\t\t\tips = append(ips, ip.Unmap())\n\t\t\t} else {\n\t\t\t\treturn HostValue{}, err\n\t\t\t}\n\t\t}\n\t}\n\tif isDomain {\n\t\treturn NewHostValueByDomain(domain)\n\t}\n\treturn NewHostValueByIPs(ips)\n}\n\nfunc NewHostValueByIPs(ips []netip.Addr) (HostValue, error) {\n\tif len(ips) == 0 {\n\t\treturn HostValue{}, errors.New(\"ip list is empty\")\n\t}\n\treturn HostValue{\n\t\tIsDomain: false,\n\t\tIPs:      ips,\n\t}, nil\n}\n\nfunc NewHostValueByDomain(domain string) (HostValue, error) {\n\tdomain = strings.Trim(domain, \".\")\n\titem := strings.Split(domain, \".\")\n\tif len(item) < 2 {\n\t\treturn HostValue{}, errors.New(\"invalid domain\")\n\t}\n\treturn HostValue{\n\t\tIsDomain: true,\n\t\tDomain:   domain,\n\t}, nil\n}\n\nfunc (hv HostValue) RandIP() (netip.Addr, error) {\n\tif hv.IsDomain {\n\t\treturn netip.Addr{}, errors.New(\"value type is error\")\n\t}\n\treturn hv.IPs[randv2.IntN(len(hv.IPs))], nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/resolver/hosts/hosts.go",
    "content": "package hosts\n\n// this file copy and modify from golang's std net/hosts.go\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"io/fs\"\n\t\"net/netip\"\n\t\"os\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n)\n\nvar hostsFilePath = \"/etc/hosts\"\n\nconst cacheMaxAge = 5 * time.Second\n\nfunc parseLiteralIP(addr string) string {\n\tip, err := netip.ParseAddr(addr)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn ip.String()\n}\n\ntype byName struct {\n\taddrs         []string\n\tcanonicalName string\n}\n\n// hosts contains known host entries.\nvar hosts struct {\n\tsync.Mutex\n\n\t// Key for the list of literal IP addresses must be a host\n\t// name. It would be part of DNS labels, a FQDN or an absolute\n\t// FQDN.\n\t// For now the key is converted to lower case for convenience.\n\tbyName map[string]byName\n\n\t// Key for the list of host names must be a literal IP address\n\t// including IPv6 address with zone identifier.\n\t// We don't support old-classful IP address notation.\n\tbyAddr map[string][]string\n\n\texpire time.Time\n\tpath   string\n\tmtime  time.Time\n\tsize   int64\n}\n\nfunc readHosts() {\n\tnow := time.Now()\n\thp := hostsFilePath\n\n\tif now.Before(hosts.expire) && hosts.path == hp && len(hosts.byName) > 0 {\n\t\treturn\n\t}\n\tmtime, size, err := stat(hp)\n\tif err == nil && hosts.path == hp && hosts.mtime.Equal(mtime) && hosts.size == size {\n\t\thosts.expire = now.Add(cacheMaxAge)\n\t\treturn\n\t}\n\n\ths := make(map[string]byName)\n\tis := make(map[string][]string)\n\n\tfile, err := open(hp)\n\tif err != nil {\n\t\tif !errors.Is(err, fs.ErrNotExist) && !errors.Is(err, fs.ErrPermission) {\n\t\t\treturn\n\t\t}\n\t}\n\n\tif file != nil {\n\t\tdefer file.close()\n\t\tfor line, ok := file.readLine(); ok; line, ok = file.readLine() {\n\t\t\tif i := strings.IndexByte(line, '#'); i >= 0 {\n\t\t\t\t// Discard comments.\n\t\t\t\tline = line[0:i]\n\t\t\t}\n\t\t\tf := getFields(line)\n\t\t\tif len(f) < 2 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\taddr := parseLiteralIP(f[0])\n\t\t\tif addr == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tvar canonical string\n\t\t\tfor i := 1; i < len(f); i++ {\n\t\t\t\tname := absDomainName(f[i])\n\t\t\t\th := []byte(f[i])\n\t\t\t\tlowerASCIIBytes(h)\n\t\t\t\tkey := absDomainName(string(h))\n\n\t\t\t\tif i == 1 {\n\t\t\t\t\tcanonical = key\n\t\t\t\t}\n\n\t\t\t\tis[addr] = append(is[addr], name)\n\n\t\t\t\tif v, ok := hs[key]; ok {\n\t\t\t\t\ths[key] = byName{\n\t\t\t\t\t\taddrs:         append(v.addrs, addr),\n\t\t\t\t\t\tcanonicalName: v.canonicalName,\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\ths[key] = byName{\n\t\t\t\t\taddrs:         []string{addr},\n\t\t\t\t\tcanonicalName: canonical,\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\t// Update the data cache.\n\thosts.expire = now.Add(cacheMaxAge)\n\thosts.path = hp\n\thosts.byName = hs\n\thosts.byAddr = is\n\thosts.mtime = mtime\n\thosts.size = size\n}\n\n// LookupStaticHost looks up the addresses and the canonical name for the given host from /etc/hosts.\nfunc LookupStaticHost(host string) ([]string, string) {\n\thosts.Lock()\n\tdefer hosts.Unlock()\n\treadHosts()\n\tif len(hosts.byName) != 0 {\n\t\tif hasUpperCase(host) {\n\t\t\tlowerHost := []byte(host)\n\t\t\tlowerASCIIBytes(lowerHost)\n\t\t\thost = string(lowerHost)\n\t\t}\n\t\tif byName, ok := hosts.byName[absDomainName(host)]; ok {\n\t\t\tipsCp := make([]string, len(byName.addrs))\n\t\t\tcopy(ipsCp, byName.addrs)\n\t\t\treturn ipsCp, byName.canonicalName\n\t\t}\n\t}\n\treturn nil, \"\"\n}\n\n// LookupStaticAddr looks up the hosts for the given address from /etc/hosts.\nfunc LookupStaticAddr(addr string) []string {\n\thosts.Lock()\n\tdefer hosts.Unlock()\n\treadHosts()\n\taddr = parseLiteralIP(addr)\n\tif addr == \"\" {\n\t\treturn nil\n\t}\n\tif len(hosts.byAddr) != 0 {\n\t\tif hosts, ok := hosts.byAddr[addr]; ok {\n\t\t\thostsCp := make([]string, len(hosts))\n\t\t\tcopy(hostsCp, hosts)\n\t\t\treturn hostsCp\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc stat(name string) (mtime time.Time, size int64, err error) {\n\tst, err := os.Stat(name)\n\tif err != nil {\n\t\treturn time.Time{}, 0, err\n\t}\n\treturn st.ModTime(), st.Size(), nil\n}\n\ntype file struct {\n\tfile  *os.File\n\tdata  []byte\n\tatEOF bool\n}\n\nfunc (f *file) close() { f.file.Close() }\n\nfunc (f *file) getLineFromData() (s string, ok bool) {\n\tdata := f.data\n\ti := 0\n\tfor i = 0; i < len(data); i++ {\n\t\tif data[i] == '\\n' {\n\t\t\ts = string(data[0:i])\n\t\t\tok = true\n\t\t\t// move data\n\t\t\ti++\n\t\t\tn := len(data) - i\n\t\t\tcopy(data[0:], data[i:])\n\t\t\tf.data = data[0:n]\n\t\t\treturn\n\t\t}\n\t}\n\tif f.atEOF && len(f.data) > 0 {\n\t\t// EOF, return all we have\n\t\ts = string(data)\n\t\tf.data = f.data[0:0]\n\t\tok = true\n\t}\n\treturn\n}\n\nfunc (f *file) readLine() (s string, ok bool) {\n\tif s, ok = f.getLineFromData(); ok {\n\t\treturn\n\t}\n\tif len(f.data) < cap(f.data) {\n\t\tln := len(f.data)\n\t\tn, err := io.ReadFull(f.file, f.data[ln:cap(f.data)])\n\t\tif n >= 0 {\n\t\t\tf.data = f.data[0 : ln+n]\n\t\t}\n\t\tif err == io.EOF || err == io.ErrUnexpectedEOF {\n\t\t\tf.atEOF = true\n\t\t}\n\t}\n\ts, ok = f.getLineFromData()\n\treturn\n}\n\nfunc (f *file) stat() (mtime time.Time, size int64, err error) {\n\tst, err := f.file.Stat()\n\tif err != nil {\n\t\treturn time.Time{}, 0, err\n\t}\n\treturn st.ModTime(), st.Size(), nil\n}\n\nfunc open(name string) (*file, error) {\n\tfd, err := os.Open(name)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &file{fd, make([]byte, 0, 64*1024), false}, nil\n}\n\nfunc getFields(s string) []string { return splitAtBytes(s, \" \\r\\t\\n\") }\n\n// Count occurrences in s of any bytes in t.\nfunc countAnyByte(s string, t string) int {\n\tn := 0\n\tfor i := 0; i < len(s); i++ {\n\t\tif strings.IndexByte(t, s[i]) >= 0 {\n\t\t\tn++\n\t\t}\n\t}\n\treturn n\n}\n\n// Split s at any bytes in t.\nfunc splitAtBytes(s string, t string) []string {\n\ta := make([]string, 1+countAnyByte(s, t))\n\tn := 0\n\tlast := 0\n\tfor i := 0; i < len(s); i++ {\n\t\tif strings.IndexByte(t, s[i]) >= 0 {\n\t\t\tif last < i {\n\t\t\t\ta[n] = s[last:i]\n\t\t\t\tn++\n\t\t\t}\n\t\t\tlast = i + 1\n\t\t}\n\t}\n\tif last < len(s) {\n\t\ta[n] = s[last:]\n\t\tn++\n\t}\n\treturn a[0:n]\n}\n\n// lowerASCIIBytes makes x ASCII lowercase in-place.\nfunc lowerASCIIBytes(x []byte) {\n\tfor i, b := range x {\n\t\tif 'A' <= b && b <= 'Z' {\n\t\t\tx[i] += 'a' - 'A'\n\t\t}\n\t}\n}\n\n// hasUpperCase tells whether the given string contains at least one upper-case.\nfunc hasUpperCase(s string) bool {\n\tfor i := range s {\n\t\tif 'A' <= s[i] && s[i] <= 'Z' {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// absDomainName returns an absolute domain name which ends with a\n// trailing dot to match pure Go reverse resolver and all other lookup\n// routines.\n// See golang.org/issue/12189.\n// But we don't want to add dots for local names from /etc/hosts.\n// It's hard to tell so we settle on the heuristic that names without dots\n// (like \"localhost\" or \"myhost\") do not get trailing dots, but any other\n// names do.\nfunc absDomainName(s string) string {\n\tif strings.IndexByte(s, '.') != -1 && s[len(s)-1] != '.' {\n\t\ts += \".\"\n\t}\n\treturn s\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/resolver/hosts/hosts_windows.go",
    "content": "package hosts\n\n// this file copy and modify from golang's std net/hook_windows.go\n\nimport (\n\t\"golang.org/x/sys/windows\"\n)\n\nfunc init() {\n\tif dir, err := windows.GetSystemDirectory(); err == nil {\n\t\thostsFilePath = dir + \"/Drivers/etc/hosts\"\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/resolver/ip4p.go",
    "content": "package resolver\n\nimport (\n\t\"net\"\n\t\"net/netip\"\n\t\"strconv\"\n\n\t\"github.com/metacubex/mihomo/log\"\n)\n\nvar (\n\tip4PEnable bool\n)\n\nfunc GetIP4PEnable() bool {\n\treturn ip4PEnable\n}\n\nfunc SetIP4PEnable(enableIP4PConvert bool) {\n\tip4PEnable = enableIP4PConvert\n}\n\n// kanged from https://github.com/heiher/frp/blob/ip4p/client/ip4p.go\n\nfunc LookupIP4P(addr netip.Addr, port string) (netip.Addr, string) {\n\tif ip4PEnable {\n\t\tip := addr.AsSlice()\n\t\tif ip[0] == 0x20 && ip[1] == 0x01 &&\n\t\t\tip[2] == 0x00 && ip[3] == 0x00 {\n\t\t\taddr = netip.AddrFrom4([4]byte{ip[12], ip[13], ip[14], ip[15]})\n\t\t\tport = strconv.Itoa(int(ip[10])<<8 + int(ip[11]))\n\t\t\tlog.Debugln(\"Convert IP4P address %s to %s\", ip, net.JoinHostPort(addr.String(), port))\n\t\t\treturn addr, port\n\t\t}\n\t}\n\treturn addr, port\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/resolver/relay.go",
    "content": "package resolver\n\nimport (\n\t\"context\"\n\t\"encoding/binary\"\n\t\"io\"\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/pool\"\n\n\tD \"github.com/miekg/dns\"\n)\n\nconst DefaultDnsReadTimeout = time.Second * 10\nconst DefaultDnsRelayTimeout = time.Second * 5\n\nconst SafeDnsPacketSize = 2 * 1024 // safe size which is 1232 from https://dnsflagday.net/2020/, so 2048 is enough\n\nfunc RelayDnsConn(ctx context.Context, conn net.Conn, readTimeout time.Duration) error {\n\tbuff := pool.Get(pool.UDPBufferSize)\n\tdefer func() {\n\t\t_ = pool.Put(buff)\n\t\t_ = conn.Close()\n\t}()\n\tfor {\n\t\tif readTimeout > 0 {\n\t\t\t_ = conn.SetReadDeadline(time.Now().Add(readTimeout))\n\t\t}\n\n\t\tlength := uint16(0)\n\t\tif err := binary.Read(conn, binary.BigEndian, &length); err != nil {\n\t\t\tbreak\n\t\t}\n\n\t\tif int(length) > len(buff) {\n\t\t\tbreak\n\t\t}\n\n\t\tn, err := io.ReadFull(conn, buff[:length])\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\n\t\terr = func() error {\n\t\t\tctx, cancel := context.WithTimeout(ctx, DefaultDnsRelayTimeout)\n\t\t\tdefer cancel()\n\t\t\tinData := buff[:n]\n\t\t\toutBuff := buff[2:]\n\t\t\tmsg, err := relayDnsPacket(ctx, inData, outBuff, 0)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tif &msg[0] == &outBuff[0] { // msg is still in the buff\n\t\t\t\tbinary.BigEndian.PutUint16(buff[:2], uint16(len(msg)))\n\t\t\t\toutBuff = buff[:2+len(msg)]\n\t\t\t} else { // buff not big enough (WTF???)\n\t\t\t\tnewBuff := pool.Get(len(msg) + 2)\n\t\t\t\tdefer pool.Put(newBuff)\n\t\t\t\tbinary.BigEndian.PutUint16(newBuff[:2], uint16(len(msg)))\n\t\t\t\tcopy(newBuff[2:], msg)\n\t\t\t\toutBuff = newBuff\n\t\t\t}\n\n\t\t\t_, err = conn.Write(outBuff)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn nil\n\t\t}()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc relayDnsPacket(ctx context.Context, payload []byte, target []byte, maxSize int) ([]byte, error) {\n\tmsg := &D.Msg{}\n\tif err := msg.Unpack(payload); err != nil {\n\t\treturn nil, err\n\t}\n\n\tr, err := ServeMsg(ctx, msg)\n\tif err != nil {\n\t\tm := new(D.Msg)\n\t\tm.SetRcode(msg, D.RcodeServerFailure)\n\t\treturn m.PackBuffer(target)\n\t}\n\n\tr.SetRcode(msg, r.Rcode)\n\tif maxSize > 0 {\n\t\tr.Truncate(maxSize)\n\t}\n\tr.Compress = true\n\treturn r.PackBuffer(target)\n}\n\n// RelayDnsPacket will truncate udp message up to SafeDnsPacketSize\nfunc RelayDnsPacket(ctx context.Context, payload []byte, target []byte) ([]byte, error) {\n\treturn relayDnsPacket(ctx, payload, target, SafeDnsPacketSize)\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/resolver/resolver.go",
    "content": "package resolver\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/netip\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"github.com/metacubex/mihomo/component/trie\"\n\n\t\"github.com/metacubex/randv2\"\n\t\"github.com/miekg/dns\"\n)\n\nvar (\n\t// DefaultResolver aim to resolve ip\n\tDefaultResolver Resolver\n\n\t// ProxyServerHostResolver resolve ip for proxies server host, only nil when DefaultResolver is nil\n\tProxyServerHostResolver Resolver\n\n\t// DirectHostResolver resolve ip for direct outbound host, only nil when DefaultResolver is nil\n\tDirectHostResolver Resolver\n\n\t// SystemResolver always using system dns, and was init in dns module\n\tSystemResolver Resolver\n\n\t// DisableIPv6 means don't resolve ipv6 host\n\t// default value is true\n\tDisableIPv6 = true\n\n\t// DefaultHosts aim to resolve hosts\n\tDefaultHosts = NewHosts(trie.New[HostValue]())\n\n\t// DefaultDNSTimeout defined the default dns request timeout\n\tDefaultDNSTimeout = time.Second * 5\n)\n\nvar (\n\tErrIPNotFound   = errors.New(\"couldn't find ip\")\n\tErrIPVersion    = errors.New(\"ip version error\")\n\tErrIPv6Disabled = errors.New(\"ipv6 disabled\")\n)\n\ntype Resolver interface {\n\tLookupIP(ctx context.Context, host string) (ips []netip.Addr, err error)\n\tLookupIPv4(ctx context.Context, host string) (ips []netip.Addr, err error)\n\tLookupIPv6(ctx context.Context, host string) (ips []netip.Addr, err error)\n\tResolveECH(ctx context.Context, host string) ([]byte, error)\n\tExchangeContext(ctx context.Context, m *dns.Msg) (msg *dns.Msg, err error)\n\tInvalid() bool\n\tClearCache()\n\tResetConnection()\n}\n\n// LookupIPv4WithResolver same as LookupIPv4, but with a resolver\nfunc LookupIPv4WithResolver(ctx context.Context, host string, r Resolver) ([]netip.Addr, error) {\n\tif node, ok := DefaultHosts.Search(host, false); ok {\n\t\tif addrs := utils.Filter(node.IPs, func(ip netip.Addr) bool {\n\t\t\treturn ip.Is4()\n\t\t}); len(addrs) > 0 {\n\t\t\treturn addrs, nil\n\t\t}\n\t}\n\n\tip, err := netip.ParseAddr(host)\n\tif err == nil {\n\t\tip = ip.Unmap()\n\t\tif ip.Is4() {\n\t\t\treturn []netip.Addr{ip}, nil\n\t\t}\n\t\treturn []netip.Addr{}, ErrIPVersion\n\t}\n\n\tif r != nil && r.Invalid() {\n\t\treturn r.LookupIPv4(ctx, host)\n\t}\n\n\treturn SystemResolver.LookupIPv4(ctx, host)\n}\n\n// LookupIPv4 with a host, return ipv4 list\nfunc LookupIPv4(ctx context.Context, host string) ([]netip.Addr, error) {\n\treturn LookupIPv4WithResolver(ctx, host, DefaultResolver)\n}\n\n// ResolveIPv4WithResolver same as ResolveIPv4, but with a resolver\nfunc ResolveIPv4WithResolver(ctx context.Context, host string, r Resolver) (netip.Addr, error) {\n\tips, err := LookupIPv4WithResolver(ctx, host, r)\n\tif err != nil {\n\t\treturn netip.Addr{}, err\n\t} else if len(ips) == 0 {\n\t\treturn netip.Addr{}, fmt.Errorf(\"%w: %s\", ErrIPNotFound, host)\n\t}\n\treturn ips[randv2.IntN(len(ips))], nil\n}\n\n// ResolveIPv4 with a host, return ipv4\nfunc ResolveIPv4(ctx context.Context, host string) (netip.Addr, error) {\n\treturn ResolveIPv4WithResolver(ctx, host, DefaultResolver)\n}\n\n// LookupIPv6WithResolver same as LookupIPv6, but with a resolver\nfunc LookupIPv6WithResolver(ctx context.Context, host string, r Resolver) ([]netip.Addr, error) {\n\tif DisableIPv6 {\n\t\treturn nil, ErrIPv6Disabled\n\t}\n\n\tif node, ok := DefaultHosts.Search(host, false); ok {\n\t\tif addrs := utils.Filter(node.IPs, func(ip netip.Addr) bool {\n\t\t\treturn ip.Is6()\n\t\t}); len(addrs) > 0 {\n\t\t\treturn addrs, nil\n\t\t}\n\t}\n\n\tif ip, err := netip.ParseAddr(host); err == nil {\n\t\tip = ip.Unmap()\n\t\tif ip.Is6() {\n\t\t\treturn []netip.Addr{ip}, nil\n\t\t}\n\t\treturn nil, ErrIPVersion\n\t}\n\n\tif r != nil && r.Invalid() {\n\t\treturn r.LookupIPv6(ctx, host)\n\t}\n\n\treturn SystemResolver.LookupIPv6(ctx, host)\n}\n\n// LookupIPv6 with a host, return ipv6 list\nfunc LookupIPv6(ctx context.Context, host string) ([]netip.Addr, error) {\n\treturn LookupIPv6WithResolver(ctx, host, DefaultResolver)\n}\n\n// ResolveIPv6WithResolver same as ResolveIPv6, but with a resolver\nfunc ResolveIPv6WithResolver(ctx context.Context, host string, r Resolver) (netip.Addr, error) {\n\tips, err := LookupIPv6WithResolver(ctx, host, r)\n\tif err != nil {\n\t\treturn netip.Addr{}, err\n\t} else if len(ips) == 0 {\n\t\treturn netip.Addr{}, fmt.Errorf(\"%w: %s\", ErrIPNotFound, host)\n\t}\n\treturn ips[randv2.IntN(len(ips))], nil\n}\n\nfunc ResolveIPv6(ctx context.Context, host string) (netip.Addr, error) {\n\treturn ResolveIPv6WithResolver(ctx, host, DefaultResolver)\n}\n\n// LookupIPWithResolver same as LookupIP, but with a resolver\nfunc LookupIPWithResolver(ctx context.Context, host string, r Resolver) ([]netip.Addr, error) {\n\tif node, ok := DefaultHosts.Search(host, false); ok {\n\t\treturn node.IPs, nil\n\t}\n\n\tif r != nil && r.Invalid() {\n\t\tif DisableIPv6 {\n\t\t\treturn r.LookupIPv4(ctx, host)\n\t\t}\n\t\treturn r.LookupIP(ctx, host)\n\t} else if DisableIPv6 {\n\t\treturn LookupIPv4WithResolver(ctx, host, r)\n\t}\n\n\tif ip, err := netip.ParseAddr(host); err == nil {\n\t\tip = ip.Unmap()\n\t\treturn []netip.Addr{ip}, nil\n\t}\n\n\treturn SystemResolver.LookupIP(ctx, host)\n}\n\n// LookupIP with a host, return ip\nfunc LookupIP(ctx context.Context, host string) ([]netip.Addr, error) {\n\treturn LookupIPWithResolver(ctx, host, DefaultResolver)\n}\n\n// ResolveIPWithResolver same as ResolveIP, but with a resolver\nfunc ResolveIPWithResolver(ctx context.Context, host string, r Resolver) (netip.Addr, error) {\n\tips, err := LookupIPWithResolver(ctx, host, r)\n\tif err != nil {\n\t\treturn netip.Addr{}, err\n\t} else if len(ips) == 0 {\n\t\treturn netip.Addr{}, fmt.Errorf(\"%w: %s\", ErrIPNotFound, host)\n\t}\n\tipv4s, ipv6s := SortationAddr(ips)\n\tif len(ipv4s) > 0 {\n\t\treturn ipv4s[randv2.IntN(len(ipv4s))], nil\n\t}\n\treturn ipv6s[randv2.IntN(len(ipv6s))], nil\n}\n\n// ResolveIP with a host, return ip and priority return TypeA\nfunc ResolveIP(ctx context.Context, host string) (netip.Addr, error) {\n\treturn ResolveIPWithResolver(ctx, host, DefaultResolver)\n}\n\n// ResolveIPPrefer6WithResolver same as ResolveIP, but with a resolver\nfunc ResolveIPPrefer6WithResolver(ctx context.Context, host string, r Resolver) (netip.Addr, error) {\n\tips, err := LookupIPWithResolver(ctx, host, r)\n\tif err != nil {\n\t\treturn netip.Addr{}, err\n\t} else if len(ips) == 0 {\n\t\treturn netip.Addr{}, fmt.Errorf(\"%w: %s\", ErrIPNotFound, host)\n\t}\n\tipv4s, ipv6s := SortationAddr(ips)\n\tif len(ipv6s) > 0 {\n\t\treturn ipv6s[randv2.IntN(len(ipv6s))], nil\n\t}\n\treturn ipv4s[randv2.IntN(len(ipv4s))], nil\n}\n\n// ResolveIPPrefer6 with a host, return ip and priority return TypeAAAA\nfunc ResolveIPPrefer6(ctx context.Context, host string) (netip.Addr, error) {\n\treturn ResolveIPPrefer6WithResolver(ctx, host, DefaultResolver)\n}\n\nfunc ResolveECHWithResolver(ctx context.Context, host string, r Resolver) ([]byte, error) {\n\tif r != nil && r.Invalid() {\n\t\treturn r.ResolveECH(ctx, host)\n\t}\n\treturn SystemResolver.ResolveECH(ctx, host)\n}\n\nfunc ResolveECH(ctx context.Context, host string) ([]byte, error) {\n\treturn ResolveECHWithResolver(ctx, host, DefaultResolver)\n}\n\nfunc ClearCache() {\n\tif DefaultResolver != nil {\n\t\tgo DefaultResolver.ClearCache()\n\t}\n\tgo SystemResolver.ClearCache() // SystemResolver unneeded check nil\n}\n\nfunc ResetConnection() {\n\tif DefaultResolver != nil {\n\t\tgo DefaultResolver.ResetConnection()\n\t}\n\tgo SystemResolver.ResetConnection() // SystemResolver unneeded check nil\n}\n\nfunc SortationAddr(ips []netip.Addr) (ipv4s, ipv6s []netip.Addr) {\n\tfor _, v := range ips {\n\t\tif v.Unmap().Is4() {\n\t\t\tipv4s = append(ipv4s, v)\n\t\t} else {\n\t\t\tipv6s = append(ipv6s, v)\n\t\t}\n\t}\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/resolver/service.go",
    "content": "package resolver\n\nimport (\n\t\"context\"\n\n\tD \"github.com/miekg/dns\"\n)\n\nvar DefaultService Service\n\ntype Service interface {\n\tServeMsg(ctx context.Context, msg *D.Msg) (*D.Msg, error)\n}\n\n// ServeMsg with a dns.Msg, return resolve dns.Msg\nfunc ServeMsg(ctx context.Context, msg *D.Msg) (*D.Msg, error) {\n\tif server := DefaultService; server != nil {\n\t\treturn server.ServeMsg(ctx, msg)\n\t}\n\n\treturn nil, ErrIPNotFound\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/resolver/system.go",
    "content": "package resolver\n\nimport \"sync\"\n\nvar blacklist struct {\n\tMap   map[string]struct{}\n\tMutex sync.Mutex\n}\n\nfunc init() {\n\tblacklist.Map = make(map[string]struct{})\n}\n\nfunc AddSystemDnsBlacklist(names ...string) {\n\tblacklist.Mutex.Lock()\n\tdefer blacklist.Mutex.Unlock()\n\tfor _, name := range names {\n\t\tblacklist.Map[name] = struct{}{}\n\t}\n}\n\nfunc RemoveSystemDnsBlacklist(names ...string) {\n\tblacklist.Mutex.Lock()\n\tdefer blacklist.Mutex.Unlock()\n\tfor _, name := range names {\n\t\tdelete(blacklist.Map, name)\n\t}\n}\n\nfunc IsSystemDnsBlacklisted(names ...string) bool {\n\tblacklist.Mutex.Lock()\n\tdefer blacklist.Mutex.Unlock()\n\tfor _, name := range names {\n\t\tif _, ok := blacklist.Map[name]; ok {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/resource/fetcher.go",
    "content": "package resource\n\nimport (\n\t\"context\"\n\t\"os\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"github.com/metacubex/mihomo/component/slowdown\"\n\tP \"github.com/metacubex/mihomo/constant/provider\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/metacubex/fswatch\"\n\t\"github.com/samber/lo\"\n)\n\ntype Parser[V any] func([]byte) (V, error)\n\ntype Fetcher[V any] struct {\n\tctx          context.Context\n\tctxCancel    context.CancelFunc\n\tresourceType string\n\tname         string\n\tvehicle      P.Vehicle\n\tupdatedAt    time.Time\n\thash         utils.HashType\n\tparser       Parser[V]\n\tinterval     time.Duration\n\tonUpdate     func(V)\n\twatcher      *fswatch.Watcher\n\tloadBufMutex sync.Mutex\n\tbackoff      slowdown.Backoff\n}\n\nfunc (f *Fetcher[V]) Name() string {\n\treturn f.name\n}\n\nfunc (f *Fetcher[V]) Vehicle() P.Vehicle {\n\treturn f.vehicle\n}\n\nfunc (f *Fetcher[V]) VehicleType() P.VehicleType {\n\treturn f.vehicle.Type()\n}\n\nfunc (f *Fetcher[V]) UpdatedAt() time.Time {\n\treturn f.updatedAt\n}\n\nfunc (f *Fetcher[V]) Initial() (V, error) {\n\tif stat, fErr := os.Stat(f.vehicle.Path()); fErr == nil {\n\t\t// local file exists, use it first\n\t\tbuf, err := os.ReadFile(f.vehicle.Path())\n\t\tmodTime := stat.ModTime()\n\t\tcontents, _, err := f.loadBuf(buf, utils.MakeHash(buf), false)\n\t\tf.updatedAt = modTime // reset updatedAt to file's modTime\n\n\t\tif err == nil {\n\t\t\terr = f.startPullLoop(time.Since(modTime) > f.interval)\n\t\t\tif err != nil {\n\t\t\t\treturn lo.Empty[V](), err\n\t\t\t}\n\t\t\treturn contents, nil\n\t\t}\n\t}\n\n\t// parse local file error, fallback to remote\n\tcontents, _, updateErr := f.Update()\n\n\t// start the pull loop even if f.Update() failed\n\terr := f.startPullLoop(false)\n\tif err != nil {\n\t\treturn lo.Empty[V](), err\n\t}\n\n\tif updateErr != nil {\n\t\treturn lo.Empty[V](), updateErr\n\t}\n\n\treturn contents, nil\n}\n\nfunc (f *Fetcher[V]) Update() (V, bool, error) {\n\tbuf, hash, err := f.vehicle.Read(f.ctx, f.hash)\n\tif err != nil {\n\t\tf.backoff.AddAttempt() // add a failed attempt to backoff\n\t\treturn lo.Empty[V](), false, err\n\t}\n\treturn f.loadBuf(buf, hash, f.vehicle.Type() != P.File)\n}\n\nfunc (f *Fetcher[V]) SideUpdate(buf []byte) (V, bool, error) {\n\treturn f.loadBuf(buf, utils.MakeHash(buf), true)\n}\n\nfunc (f *Fetcher[V]) loadBuf(buf []byte, hash utils.HashType, updateFile bool) (V, bool, error) {\n\tf.loadBufMutex.Lock()\n\tdefer f.loadBufMutex.Unlock()\n\n\tnow := time.Now()\n\tif f.hash.Equal(hash) {\n\t\tif updateFile {\n\t\t\t_ = os.Chtimes(f.vehicle.Path(), now, now)\n\t\t}\n\t\tf.updatedAt = now\n\t\tf.backoff.Reset() // no error, reset backoff\n\t\treturn lo.Empty[V](), true, nil\n\t}\n\n\tif buf == nil { // f.hash has been changed between f.vehicle.Read but should not happen (cause by concurrent)\n\t\treturn lo.Empty[V](), true, nil\n\t}\n\n\tcontents, err := f.parser(buf)\n\tif err != nil {\n\t\tf.backoff.AddAttempt() // add a failed attempt to backoff\n\t\treturn lo.Empty[V](), false, err\n\t}\n\tf.backoff.Reset() // no error, reset backoff\n\n\tif updateFile {\n\t\tif err = f.vehicle.Write(buf); err != nil {\n\t\t\treturn lo.Empty[V](), false, err\n\t\t}\n\t}\n\tf.updatedAt = now\n\tf.hash = hash\n\n\tif f.onUpdate != nil {\n\t\tf.onUpdate(contents)\n\t}\n\n\treturn contents, false, nil\n}\n\nfunc (f *Fetcher[V]) Close() error {\n\tf.ctxCancel()\n\tif f.watcher != nil {\n\t\t_ = f.watcher.Close()\n\t}\n\treturn nil\n}\n\nfunc (f *Fetcher[V]) pullLoop(forceUpdate bool) {\n\tinitialInterval := f.interval - time.Since(f.updatedAt)\n\tif initialInterval > f.interval {\n\t\tinitialInterval = f.interval\n\t}\n\n\tif forceUpdate {\n\t\t// Delay 10 seconds\n\t\tinitialInterval = 10 * time.Second\n\t}\n\tif attempt := f.backoff.Attempt(); attempt > 0 { // f.Update() was failed, decrease the interval from backoff to achieve fast retry\n\t\tif duration := f.backoff.ForAttempt(attempt); duration < initialInterval {\n\t\t\tinitialInterval = duration\n\t\t}\n\t}\n\n\ttimer := time.NewTimer(initialInterval)\n\tdefer timer.Stop()\n\tfor {\n\t\tselect {\n\t\tcase <-timer.C:\n\t\t\tif forceUpdate {\n\t\t\t\tlog.Warnln(\"[Provider] %s not updated for a long time, force refresh\", f.Name())\n\t\t\t\tforceUpdate = false\n\t\t\t}\n\t\t\tf.updateWithLog()\n\t\t\tinterval := f.interval\n\t\t\tif attempt := f.backoff.Attempt(); attempt > 0 { // f.Update() was failed, decrease the interval from backoff to achieve fast retry\n\t\t\t\tif duration := f.backoff.ForAttempt(attempt); duration < interval {\n\t\t\t\t\tinterval = duration\n\t\t\t\t}\n\t\t\t}\n\t\t\ttimer.Reset(interval)\n\t\tcase <-f.ctx.Done():\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (f *Fetcher[V]) startPullLoop(forceUpdate bool) (err error) {\n\t// pull contents automatically\n\tif f.vehicle.Type() == P.File {\n\t\tf.watcher, err = fswatch.NewWatcher(fswatch.Options{\n\t\t\tPath:     []string{f.vehicle.Path()},\n\t\t\tCallback: f.updateCallback,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\terr = f.watcher.Start()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t} else if f.interval > 0 {\n\t\tgo f.pullLoop(forceUpdate)\n\t}\n\treturn\n}\n\nfunc (f *Fetcher[V]) updateCallback(path string) {\n\tf.updateWithLog()\n}\n\nfunc (f *Fetcher[V]) updateWithLog() {\n\t_, same, err := f.Update()\n\tif err != nil {\n\t\tlog.Warnln(\"[Provider] %s pull error: %s\", f.Name(), err.Error())\n\t\treturn\n\t}\n\n\tif same {\n\t\tlog.Debugln(\"[Provider] %s's content doesn't change\", f.Name())\n\t\treturn\n\t}\n\n\tlog.Infoln(\"[Provider] %s's content update\", f.Name())\n\treturn\n}\n\nfunc NewFetcher[V any](name string, interval time.Duration, vehicle P.Vehicle, parser Parser[V], onUpdate func(V)) *Fetcher[V] {\n\tctx, cancel := context.WithCancel(context.Background())\n\tminBackoff := 10 * time.Second\n\tif interval < minBackoff {\n\t\tminBackoff = interval\n\t}\n\treturn &Fetcher[V]{\n\t\tctx:       ctx,\n\t\tctxCancel: cancel,\n\t\tname:      name,\n\t\tvehicle:   vehicle,\n\t\tparser:    parser,\n\t\tonUpdate:  onUpdate,\n\t\tinterval:  interval,\n\t\tbackoff: slowdown.Backoff{\n\t\t\tFactor: 2,\n\t\t\tJitter: false,\n\t\t\tMin:    minBackoff,\n\t\t\tMax:    interval,\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/resource/vehicle.go",
    "content": "package resource\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"io\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/utils\"\n\tmihomoHttp \"github.com/metacubex/mihomo/component/http\"\n\t\"github.com/metacubex/mihomo/component/profile/cachefile\"\n\tP \"github.com/metacubex/mihomo/constant/provider\"\n\n\t\"github.com/metacubex/http\"\n)\n\nconst (\n\tDefaultHttpTimeout = time.Second * 20\n\n\tfileMode os.FileMode = 0o666\n\tdirMode  os.FileMode = 0o755\n)\n\nvar (\n\tetag = false\n)\n\nfunc ETag() bool {\n\treturn etag\n}\n\nfunc SetETag(b bool) {\n\tetag = b\n}\n\nfunc safeWrite(path string, buf []byte) error {\n\tdir := filepath.Dir(path)\n\n\tif _, err := os.Stat(dir); os.IsNotExist(err) {\n\t\tif err := os.MkdirAll(dir, dirMode); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\treturn os.WriteFile(path, buf, fileMode)\n}\n\ntype FileVehicle struct {\n\tpath string\n}\n\nfunc (f *FileVehicle) Type() P.VehicleType {\n\treturn P.File\n}\n\nfunc (f *FileVehicle) Path() string {\n\treturn f.path\n}\n\nfunc (f *FileVehicle) Url() string {\n\treturn \"file://\" + f.path\n}\n\nfunc (f *FileVehicle) Read(ctx context.Context, oldHash utils.HashType) (buf []byte, hash utils.HashType, err error) {\n\tbuf, err = os.ReadFile(f.path)\n\tif err != nil {\n\t\treturn\n\t}\n\thash = utils.MakeHash(buf)\n\treturn\n}\n\nfunc (f *FileVehicle) Proxy() string {\n\treturn \"\"\n}\n\nfunc (f *FileVehicle) Write(buf []byte) error {\n\treturn safeWrite(f.path, buf)\n}\n\nfunc NewFileVehicle(path string) *FileVehicle {\n\treturn &FileVehicle{path: path}\n}\n\ntype HTTPVehicle struct {\n\turl       string\n\tpath      string\n\tproxy     string\n\theader    http.Header\n\ttimeout   time.Duration\n\tsizeLimit int64\n\tinRead    func(response *http.Response)\n\tprovider  P.ProxyProvider\n}\n\nfunc (h *HTTPVehicle) Url() string {\n\treturn h.url\n}\n\nfunc (h *HTTPVehicle) Type() P.VehicleType {\n\treturn P.HTTP\n}\n\nfunc (h *HTTPVehicle) Path() string {\n\treturn h.path\n}\n\nfunc (h *HTTPVehicle) Proxy() string {\n\treturn h.proxy\n}\n\nfunc (h *HTTPVehicle) Write(buf []byte) error {\n\treturn safeWrite(h.path, buf)\n}\n\nfunc (h *HTTPVehicle) SetInRead(fn func(response *http.Response)) {\n\th.inRead = fn\n}\n\nfunc (h *HTTPVehicle) Read(ctx context.Context, oldHash utils.HashType) (buf []byte, hash utils.HashType, err error) {\n\tctx, cancel := context.WithTimeout(ctx, h.timeout)\n\tdefer cancel()\n\theader := h.header\n\tsetIfNoneMatch := false\n\tif etag && oldHash.IsValid() {\n\t\tetagWithHash := cachefile.Cache().GetETagWithHash(h.url)\n\t\tif oldHash.Equal(etagWithHash.Hash) && etagWithHash.ETag != \"\" {\n\t\t\tif header == nil {\n\t\t\t\theader = http.Header{}\n\t\t\t} else {\n\t\t\t\theader = header.Clone()\n\t\t\t}\n\t\t\theader.Set(\"If-None-Match\", etagWithHash.ETag)\n\t\t\tsetIfNoneMatch = true\n\t\t}\n\t}\n\tresp, err := mihomoHttp.HttpRequest(ctx, h.url, http.MethodGet, header, nil, mihomoHttp.WithSpecialProxy(h.proxy))\n\tif err != nil {\n\t\treturn\n\t}\n\tdefer resp.Body.Close()\n\n\tif h.inRead != nil {\n\t\th.inRead(resp)\n\t}\n\n\tif resp.StatusCode < 200 || resp.StatusCode > 299 {\n\t\tif setIfNoneMatch && resp.StatusCode == http.StatusNotModified {\n\t\t\treturn nil, oldHash, nil\n\t\t}\n\t\terr = errors.New(resp.Status)\n\t\treturn\n\t}\n\tvar reader io.Reader = resp.Body\n\tif h.sizeLimit > 0 {\n\t\treader = io.LimitReader(reader, h.sizeLimit)\n\t}\n\tbuf, err = io.ReadAll(reader)\n\tif err != nil {\n\t\treturn\n\t}\n\thash = utils.MakeHash(buf)\n\tif etag {\n\t\tcachefile.Cache().SetETagWithHash(h.url, cachefile.EtagWithHash{\n\t\t\tHash: hash,\n\t\t\tETag: resp.Header.Get(\"ETag\"),\n\t\t\tTime: time.Now(),\n\t\t})\n\t}\n\treturn\n}\n\nfunc NewHTTPVehicle(url string, path string, proxy string, header http.Header, timeout time.Duration, sizeLimit int64) *HTTPVehicle {\n\treturn &HTTPVehicle{\n\t\turl:       url,\n\t\tpath:      path,\n\t\tproxy:     proxy,\n\t\theader:    header,\n\t\ttimeout:   timeout,\n\t\tsizeLimit: sizeLimit,\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/slowdown/backoff.go",
    "content": "// modify from https://github.com/jpillora/backoff/blob/v1.0.0/backoff.go\n\npackage slowdown\n\nimport (\n\t\"math\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/metacubex/randv2\"\n)\n\n// Backoff is a time.Duration counter, starting at Min. After every call to\n// the Duration method the current timing is multiplied by Factor, but it\n// never exceeds Max.\n//\n// Backoff is not generally concurrent-safe, but the ForAttempt method can\n// be used concurrently.\ntype Backoff struct {\n\tattempt atomic.Uint64\n\t// Factor is the multiplying factor for each increment step\n\tFactor float64\n\t// Jitter eases contention by randomizing backoff steps\n\tJitter bool\n\t// Min and Max are the minimum and maximum values of the counter\n\tMin, Max time.Duration\n}\n\n// Duration returns the duration for the current attempt before incrementing\n// the attempt counter. See ForAttempt.\nfunc (b *Backoff) Duration() time.Duration {\n\td := b.ForAttempt(float64(b.attempt.Add(1) - 1))\n\treturn d\n}\n\nconst maxInt64 = float64(math.MaxInt64 - 512)\n\n// ForAttempt returns the duration for a specific attempt. This is useful if\n// you have a large number of independent Backoffs, but don't want use\n// unnecessary memory storing the Backoff parameters per Backoff. The first\n// attempt should be 0.\n//\n// ForAttempt is concurrent-safe.\nfunc (b *Backoff) ForAttempt(attempt float64) time.Duration {\n\t// Zero-values are nonsensical, so we use\n\t// them to apply defaults\n\tmin := b.Min\n\tif min <= 0 {\n\t\tmin = 100 * time.Millisecond\n\t}\n\tmax := b.Max\n\tif max <= 0 {\n\t\tmax = 10 * time.Second\n\t}\n\tif min >= max {\n\t\t// short-circuit\n\t\treturn max\n\t}\n\tfactor := b.Factor\n\tif factor <= 0 {\n\t\tfactor = 2\n\t}\n\t//calculate this duration\n\tminf := float64(min)\n\tdurf := minf * math.Pow(factor, attempt)\n\tif b.Jitter {\n\t\tdurf = randv2.Float64()*(durf-minf) + minf\n\t}\n\t//ensure float64 wont overflow int64\n\tif durf > maxInt64 {\n\t\treturn max\n\t}\n\tdur := time.Duration(durf)\n\t//keep within bounds\n\tif dur < min {\n\t\treturn min\n\t}\n\tif dur > max {\n\t\treturn max\n\t}\n\treturn dur\n}\n\n// Reset restarts the current attempt counter at zero.\nfunc (b *Backoff) Reset() {\n\tb.attempt.Store(0)\n}\n\n// Attempt returns the current attempt counter value.\nfunc (b *Backoff) Attempt() float64 {\n\treturn float64(b.attempt.Load())\n}\n\n// AddAttempt adds one to the attempt counter.\nfunc (b *Backoff) AddAttempt() {\n\tb.attempt.Add(1)\n}\n\n// Copy returns a backoff with equals constraints as the original\nfunc (b *Backoff) Copy() *Backoff {\n\treturn &Backoff{\n\t\tFactor: b.Factor,\n\t\tJitter: b.Jitter,\n\t\tMin:    b.Min,\n\t\tMax:    b.Max,\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/slowdown/slowdown.go",
    "content": "package slowdown\n\nimport (\n\t\"context\"\n\t\"sync/atomic\"\n\t\"time\"\n)\n\ntype SlowDown struct {\n\terrTimes atomic.Int64\n\tbackoff  Backoff\n}\n\nfunc (s *SlowDown) Wait(ctx context.Context) (err error) {\n\ttimer := time.NewTimer(s.backoff.Duration())\n\tdefer timer.Stop()\n\tselect {\n\tcase <-timer.C:\n\tcase <-ctx.Done():\n\t\terr = ctx.Err()\n\t}\n\treturn\n}\n\nfunc New() *SlowDown {\n\treturn &SlowDown{\n\t\tbackoff: Backoff{\n\t\t\tMin:    10 * time.Millisecond,\n\t\t\tMax:    1 * time.Second,\n\t\t\tFactor: 2,\n\t\t\tJitter: true,\n\t\t},\n\t}\n}\n\nfunc Do[T any](s *SlowDown, ctx context.Context, fn func() (T, error)) (t T, err error) {\n\tif s.errTimes.Load() > 10 {\n\t\terr = s.Wait(ctx)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tt, err = fn()\n\tif err != nil {\n\t\ts.errTimes.Add(1)\n\t\treturn\n\t}\n\ts.errTimes.Store(0)\n\ts.backoff.Reset()\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/sniffer/base_sniffer.go",
    "content": "package sniffer\n\nimport (\n\t\"errors\"\n\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/constant/sniffer\"\n)\n\ntype SnifferConfig struct {\n\tOverrideDest bool\n\tPorts        utils.IntRanges[uint16]\n}\n\ntype BaseSniffer struct {\n\tports              utils.IntRanges[uint16]\n\tsupportNetworkType constant.NetWork\n}\n\n// Protocol implements sniffer.Sniffer\nfunc (*BaseSniffer) Protocol() string {\n\treturn \"unknown\"\n}\n\n// SniffData implements sniffer.Sniffer\nfunc (*BaseSniffer) SniffData(bytes []byte) (string, error) {\n\treturn \"\", errors.New(\"TODO\")\n}\n\n// SupportNetwork implements sniffer.Sniffer\nfunc (bs *BaseSniffer) SupportNetwork() constant.NetWork {\n\treturn bs.supportNetworkType\n}\n\n// SupportPort implements sniffer.Sniffer\nfunc (bs *BaseSniffer) SupportPort(port uint16) bool {\n\treturn bs.ports.Check(port)\n}\n\nfunc NewBaseSniffer(ports utils.IntRanges[uint16], networkType constant.NetWork) *BaseSniffer {\n\treturn &BaseSniffer{\n\t\tports:              ports,\n\t\tsupportNetworkType: networkType,\n\t}\n}\n\nvar _ sniffer.Sniffer = (*BaseSniffer)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/component/sniffer/dispatcher.go",
    "content": "package sniffer\n\nimport (\n\t\"errors\"\n\t\"net\"\n\t\"net/netip\"\n\t\"time\"\n\n\t\"github.com/metacubex/sing/common/metadata\"\n\n\t\"github.com/metacubex/mihomo/common/lru\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/constant/sniffer\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\nvar (\n\tErrorUnsupportedSniffer = errors.New(\"unsupported sniffer\")\n\tErrorSniffFailed        = errors.New(\"all sniffer failed\")\n\tErrNoClue               = errors.New(\"not enough information for making a decision\")\n)\n\ntype Dispatcher struct {\n\tenable          bool\n\tsniffers        map[sniffer.Sniffer]SnifferConfig\n\tforceDomain     []C.DomainMatcher\n\tskipSrcAddress  []C.IpMatcher\n\tskipDstAddress  []C.IpMatcher\n\tskipDomain      []C.DomainMatcher\n\tskipList        *lru.LruCache[netip.AddrPort, uint8]\n\tforceDnsMapping bool\n\tparsePureIp     bool\n}\n\nfunc (sd *Dispatcher) shouldOverride(metadata *C.Metadata) bool {\n\tfor _, matcher := range sd.skipDstAddress {\n\t\tif matcher.MatchIp(metadata.DstIP) {\n\t\t\treturn false\n\t\t}\n\t}\n\tfor _, matcher := range sd.skipSrcAddress {\n\t\tif matcher.MatchIp(metadata.SrcIP) {\n\t\t\treturn false\n\t\t}\n\t}\n\tif metadata.Host == \"\" && sd.parsePureIp {\n\t\treturn true\n\t}\n\tif metadata.DNSMode == C.DNSMapping && sd.forceDnsMapping {\n\t\treturn true\n\t}\n\treturn sd.forceSniff(metadata)\n}\n\nfunc (sd *Dispatcher) forceSniff(metadata *C.Metadata) bool {\n\tfor _, matcher := range sd.forceDomain {\n\t\tif matcher.MatchDomain(metadata.Host) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\n// UDPSniff is called when a UDP NAT is created and passed the first initialization packet.\n// It may return a wrapped packetSender if the sniffer process needs to wait for multiple packets.\n// This function must be non-blocking, and any blocking operations should be done in the wrapped packetSender.\nfunc (sd *Dispatcher) UDPSniff(packet C.PacketAdapter, packetSender C.PacketSender) C.PacketSender {\n\tmetadata := packet.Metadata()\n\tif sd.shouldOverride(metadata) {\n\t\tfor current, config := range sd.sniffers {\n\t\t\tif current.SupportNetwork() == C.UDP || current.SupportNetwork() == C.ALLNet {\n\t\t\t\tinWhitelist := current.SupportPort(metadata.DstPort)\n\t\t\t\toverrideDest := config.OverrideDest\n\n\t\t\t\tif inWhitelist {\n\t\t\t\t\treplaceDomain := func(metadata *C.Metadata, host string) {\n\t\t\t\t\t\tif sd.domainCanReplace(host) {\n\t\t\t\t\t\t\treplaceDomain(metadata, host, overrideDest)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tlog.Debugln(\"[Sniffer] Skip sni[%s]\", host)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tif wrapable, ok := current.(sniffer.MultiPacketSniffer); ok {\n\t\t\t\t\t\treturn wrapable.WrapperSender(packetSender, replaceDomain)\n\t\t\t\t\t}\n\n\t\t\t\t\thost, err := current.SniffData(packet.Data())\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\n\t\t\t\t\treplaceDomain(metadata, host)\n\t\t\t\t\treturn packetSender\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn packetSender\n}\n\n// TCPSniff returns true if the connection is sniffed to have a domain\nfunc (sd *Dispatcher) TCPSniff(conn *N.BufferedConn, metadata *C.Metadata) bool {\n\tif sd.shouldOverride(metadata) {\n\t\tinWhitelist := false\n\t\toverrideDest := false\n\t\tfor sniffer, config := range sd.sniffers {\n\t\t\tif sniffer.SupportNetwork() == C.TCP || sniffer.SupportNetwork() == C.ALLNet {\n\t\t\t\tinWhitelist = sniffer.SupportPort(metadata.DstPort)\n\t\t\t\tif inWhitelist {\n\t\t\t\t\toverrideDest = config.OverrideDest\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif !inWhitelist {\n\t\t\treturn false\n\t\t}\n\t\tforceSniffer := sd.forceSniff(metadata)\n\n\t\tdst := metadata.AddrPort()\n\t\tif !forceSniffer {\n\t\t\tif count, ok := sd.skipList.Get(dst); ok && count > 5 {\n\t\t\t\tlog.Debugln(\"[Sniffer] Skip sniffing[%s] due to multiple failures\", dst)\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\n\t\thost, err := sd.sniffDomain(conn, metadata)\n\t\tif err != nil {\n\t\t\tif !forceSniffer {\n\t\t\t\tsd.cacheSniffFailed(metadata)\n\t\t\t}\n\t\t\tlog.Debugln(\"[Sniffer] All sniffing sniff failed with from [%s:%d] to [%s:%d]\", metadata.SrcIP, metadata.SrcPort, metadata.String(), metadata.DstPort)\n\t\t\treturn false\n\t\t}\n\n\t\tif !sd.domainCanReplace(host) {\n\t\t\tlog.Debugln(\"[Sniffer] Skip sni[%s]\", host)\n\t\t\treturn false\n\t\t}\n\n\t\tsd.skipList.Delete(dst)\n\n\t\treplaceDomain(metadata, host, overrideDest)\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc replaceDomain(metadata *C.Metadata, host string, overrideDest bool) {\n\tmetadata.SniffHost = host\n\tif overrideDest {\n\t\tlog.Debugln(\"[Sniffer] Sniff %s [%s]-->[%s] success, replace domain [%s]-->[%s]\",\n\t\t\tmetadata.NetWork,\n\t\t\tmetadata.SourceDetail(),\n\t\t\tmetadata.RemoteAddress(),\n\t\t\tmetadata.Host, host)\n\t\tmetadata.Host = host\n\t\tmetadata.DstIP = netip.Addr{}\n\t}\n\tmetadata.DNSMode = C.DNSNormal\n}\n\nfunc (sd *Dispatcher) domainCanReplace(host string) bool {\n\tif host == \".\" || !metadata.IsDomainName(host) {\n\t\treturn false\n\t}\n\tfor _, matcher := range sd.skipDomain {\n\t\tif matcher.MatchDomain(host) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc (sd *Dispatcher) Enable() bool {\n\treturn sd != nil && sd.enable\n}\n\nfunc (sd *Dispatcher) sniffDomain(conn *N.BufferedConn, metadata *C.Metadata) (string, error) {\n\t//defer func(start time.Time) {\n\t//\tlog.Debugln(\"[Sniffer] [%s] Sniffing took %s\", metadata.DstIP, time.Since(start))\n\t//}(time.Now())\n\n\tfor s := range sd.sniffers {\n\t\tif s.SupportNetwork() == C.TCP && s.SupportPort(metadata.DstPort) {\n\t\t\t_ = conn.SetReadDeadline(time.Now().Add(1 * time.Second))\n\t\t\t_, err := conn.Peek(1)\n\t\t\t_ = conn.SetReadDeadline(time.Time{})\n\t\t\tif err != nil {\n\t\t\t\t_, ok := err.(*net.OpError)\n\t\t\t\tif ok {\n\t\t\t\t\tsd.cacheSniffFailed(metadata)\n\t\t\t\t\tlog.Errorln(\"[Sniffer] [%s] [%s] may not have any sent data, Consider adding skip\", metadata.DstIP, s.Protocol())\n\t\t\t\t\t_ = conn.Close()\n\t\t\t\t}\n\n\t\t\t\treturn \"\", err\n\t\t\t}\n\n\t\t\tbufferedLen := conn.Buffered()\n\t\t\tbytes, err := conn.Peek(bufferedLen)\n\t\t\tif err != nil {\n\t\t\t\tlog.Debugln(\"[Sniffer] [%s] [%s] the data length not enough, error: %v\", metadata.DstIP, s.Protocol(), err)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\thost, err := s.SniffData(bytes)\n\t\t\tvar e *errNeedAtLeastData\n\t\t\tif errors.As(err, &e) {\n\t\t\t\t//log.Debugln(\"[Sniffer] [%s] [%s] %v, got length: %d\", metadata.DstIP, s.Protocol(), e, len(bytes))\n\t\t\t\t_ = conn.SetReadDeadline(time.Now().Add(1 * time.Second))\n\t\t\t\tbytes, err = conn.Peek(e.length)\n\t\t\t\t_ = conn.SetReadDeadline(time.Time{})\n\t\t\t\t//log.Debugln(\"[Sniffer] [%s] [%s] try again, got length: %d\", metadata.DstIP, s.Protocol(), len(bytes))\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Debugln(\"[Sniffer] [%s] [%s] the data length not enough, error: %v\", metadata.DstIP, s.Protocol(), err)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\thost, err = s.SniffData(bytes)\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\t//log.Debugln(\"[Sniffer] [%s] [%s] Sniff data failed, error: %v\", metadata.DstIP, s.Protocol(), err)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t_, err = netip.ParseAddr(host)\n\t\t\tif err == nil {\n\t\t\t\t//log.Debugln(\"[Sniffer] [%s] [%s] Sniff data failed, got host [%s]\", metadata.DstIP, s.Protocol(), host)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t//log.Debugln(\"[Sniffer] [%s] [%s] Sniffed [%s]\", metadata.DstIP, s.Protocol(), host)\n\t\t\treturn host, nil\n\t\t}\n\t}\n\n\treturn \"\", ErrorSniffFailed\n}\n\nfunc (sd *Dispatcher) cacheSniffFailed(metadata *C.Metadata) {\n\tdst := metadata.AddrPort()\n\tsd.skipList.Compute(dst, func(oldValue uint8, loaded bool) (newValue uint8, delete bool) {\n\t\tif oldValue <= 5 {\n\t\t\toldValue++\n\t\t}\n\t\treturn oldValue, false\n\t})\n}\n\ntype Config struct {\n\tEnable          bool\n\tSniffers        map[sniffer.Type]SnifferConfig\n\tForceDomain     []C.DomainMatcher\n\tSkipSrcAddress  []C.IpMatcher\n\tSkipDstAddress  []C.IpMatcher\n\tSkipDomain      []C.DomainMatcher\n\tForceDnsMapping bool\n\tParsePureIp     bool\n}\n\nfunc NewDispatcher(snifferConfig *Config) (*Dispatcher, error) {\n\tdispatcher := Dispatcher{\n\t\tenable:          snifferConfig.Enable,\n\t\tforceDomain:     snifferConfig.ForceDomain,\n\t\tskipSrcAddress:  snifferConfig.SkipSrcAddress,\n\t\tskipDstAddress:  snifferConfig.SkipDstAddress,\n\t\tskipDomain:      snifferConfig.SkipDomain,\n\t\tskipList:        lru.New(lru.WithSize[netip.AddrPort, uint8](128), lru.WithAge[netip.AddrPort, uint8](600)),\n\t\tforceDnsMapping: snifferConfig.ForceDnsMapping,\n\t\tparsePureIp:     snifferConfig.ParsePureIp,\n\t\tsniffers:        make(map[sniffer.Sniffer]SnifferConfig, len(snifferConfig.Sniffers)),\n\t}\n\n\tfor snifferName, config := range snifferConfig.Sniffers {\n\t\ts, err := NewSniffer(snifferName, config)\n\t\tif err != nil {\n\t\t\tlog.Errorln(\"Sniffer name[%s] is error\", snifferName)\n\t\t\treturn &Dispatcher{enable: false}, err\n\t\t}\n\t\tdispatcher.sniffers[s] = config\n\t}\n\n\treturn &dispatcher, nil\n}\n\nfunc NewSniffer(name sniffer.Type, snifferConfig SnifferConfig) (sniffer.Sniffer, error) {\n\tswitch name {\n\tcase sniffer.TLS:\n\t\treturn NewTLSSniffer(snifferConfig)\n\tcase sniffer.HTTP:\n\t\treturn NewHTTPSniffer(snifferConfig)\n\tcase sniffer.QUIC:\n\t\treturn NewQuicSniffer(snifferConfig)\n\tdefault:\n\t\treturn nil, ErrorUnsupportedSniffer\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/sniffer/http_sniffer.go",
    "content": "package sniffer\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/common/utils\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/constant/sniffer\"\n)\n\nvar (\n\t// refer to https://pkg.go.dev/net/http@master#pkg-constants\n\tmethods          = [...]string{\"get\", \"post\", \"head\", \"put\", \"delete\", \"options\", \"connect\", \"patch\", \"trace\"}\n\terrNotHTTPMethod = errors.New(\"not an HTTP method\")\n)\n\ntype version byte\n\nconst (\n\tHTTP1 version = iota\n\tHTTP2\n)\n\ntype HTTPSniffer struct {\n\t*BaseSniffer\n\tversion version\n\thost    string\n}\n\nvar _ sniffer.Sniffer = (*HTTPSniffer)(nil)\n\nfunc NewHTTPSniffer(snifferConfig SnifferConfig) (*HTTPSniffer, error) {\n\tports := snifferConfig.Ports\n\tif len(ports) == 0 {\n\t\tports = utils.IntRanges[uint16]{utils.NewRange[uint16](80, 80)}\n\t}\n\treturn &HTTPSniffer{\n\t\tBaseSniffer: NewBaseSniffer(ports, C.TCP),\n\t}, nil\n}\n\nfunc (http *HTTPSniffer) Protocol() string {\n\tswitch http.version {\n\tcase HTTP1:\n\t\treturn \"http1\"\n\tcase HTTP2:\n\t\treturn \"http2\"\n\tdefault:\n\t\treturn \"unknown\"\n\t}\n}\n\nfunc (http *HTTPSniffer) SupportNetwork() C.NetWork {\n\treturn C.TCP\n}\n\nfunc (http *HTTPSniffer) SniffData(bytes []byte) (string, error) {\n\tdomain, err := SniffHTTP(bytes)\n\tif err == nil {\n\t\treturn *domain, nil\n\t} else {\n\t\treturn \"\", err\n\t}\n}\n\nfunc beginWithHTTPMethod(b []byte) error {\n\tfor _, m := range &methods {\n\t\tif len(b) >= len(m) && strings.EqualFold(string(b[:len(m)]), m) {\n\t\t\treturn nil\n\t\t}\n\n\t\tif len(b) < len(m) {\n\t\t\treturn ErrNoClue\n\t\t}\n\t}\n\treturn errNotHTTPMethod\n}\n\nfunc SniffHTTP(b []byte) (*string, error) {\n\tif err := beginWithHTTPMethod(b); err != nil {\n\t\treturn nil, err\n\t}\n\n\t_ = &HTTPSniffer{\n\t\tversion: HTTP1,\n\t}\n\n\theaders := bytes.Split(b, []byte{'\\n'})\n\tfor i := 1; i < len(headers); i++ {\n\t\theader := headers[i]\n\t\tif len(header) == 0 {\n\t\t\tbreak\n\t\t}\n\t\tparts := bytes.SplitN(header, []byte{':'}, 2)\n\t\tif len(parts) != 2 {\n\t\t\tcontinue\n\t\t}\n\t\tkey := strings.ToLower(string(parts[0]))\n\t\tif key == \"host\" {\n\t\t\trawHost := strings.ToLower(string(bytes.TrimSpace(parts[1])))\n\t\t\thost, _, err := net.SplitHostPort(rawHost)\n\t\t\tif err != nil {\n\t\t\t\tif addrError, ok := err.(*net.AddrError); ok && strings.Contains(addrError.Err, \"missing port\") {\n\t\t\t\t\treturn parseHost(rawHost)\n\t\t\t\t} else {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif net.ParseIP(host) != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"host is ip\")\n\t\t\t}\n\n\t\t\treturn &host, nil\n\t\t}\n\t}\n\treturn nil, ErrNoClue\n}\n\nfunc parseHost(host string) (*string, error) {\n\tif strings.HasPrefix(host, \"[\") && strings.HasSuffix(host, \"]\") {\n\t\tif net.ParseIP(host[1:len(host)-1]) != nil {\n\t\t\treturn nil, fmt.Errorf(\"host is ip\")\n\t\t}\n\t}\n\n\tif net.ParseIP(host) != nil {\n\t\treturn nil, fmt.Errorf(\"host is ip\")\n\t}\n\n\treturn &host, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/sniffer/quic_sniffer.go",
    "content": "package sniffer\n\nimport (\n\t\"crypto\"\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"io\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/buf\"\n\t\"github.com/metacubex/mihomo/common/pool\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"github.com/metacubex/mihomo/constant\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/constant/sniffer\"\n\n\t\"github.com/metacubex/quic-go/quicvarint\"\n\t\"golang.org/x/crypto/hkdf\"\n)\n\n// Modified from https://github.com/v2fly/v2ray-core/blob/master/common/protocol/quic/sniff.go\n\nconst (\n\tversionDraft29 uint32 = 0xff00001d\n\tversion1       uint32 = 0x1\n\n\tquicPacketTypeInitial = 0x00\n\tquicPacketType0RTT    = 0x01\n\n\t// Timeout before quic sniffer all packets\n\tquicWaitConn = time.Second * 3\n\n\t// maxCryptoStreamOffset is the maximum offset allowed on any of the crypto streams.\n\t// This limits the size of the ClientHello and Certificates that can be received.\n\tmaxCryptoStreamOffset = 16 * (1 << 10)\n)\n\nvar (\n\tquicSaltOld       = []byte{0xaf, 0xbf, 0xec, 0x28, 0x99, 0x93, 0xd2, 0x4c, 0x9e, 0x97, 0x86, 0xf1, 0x9c, 0x61, 0x11, 0xe0, 0x43, 0x90, 0xa8, 0x99}\n\tquicSalt          = []byte{0x38, 0x76, 0x2c, 0xf7, 0xf5, 0x59, 0x34, 0xb3, 0x4d, 0x17, 0x9a, 0xe6, 0xa4, 0xc8, 0x0c, 0xad, 0xcc, 0xbb, 0x7f, 0x0a}\n\terrNotQuic        = errors.New(\"not QUIC\")\n\terrNotQuicInitial = errors.New(\"not QUIC initial packet\")\n)\n\nvar _ sniffer.Sniffer = (*QuicSniffer)(nil)\nvar _ sniffer.MultiPacketSniffer = (*QuicSniffer)(nil)\n\ntype QuicSniffer struct {\n\t*BaseSniffer\n}\n\nfunc NewQuicSniffer(snifferConfig SnifferConfig) (*QuicSniffer, error) {\n\tports := snifferConfig.Ports\n\tif len(ports) == 0 {\n\t\tports = utils.IntRanges[uint16]{utils.NewRange[uint16](443, 443)}\n\t}\n\treturn &QuicSniffer{\n\t\tBaseSniffer: NewBaseSniffer(ports, C.UDP),\n\t}, nil\n}\n\nfunc (sniffer *QuicSniffer) Protocol() string {\n\treturn \"quic\"\n}\n\nfunc (sniffer *QuicSniffer) SupportNetwork() C.NetWork {\n\treturn C.UDP\n}\n\nfunc (sniffer *QuicSniffer) SniffData(b []byte) (string, error) {\n\treturn \"\", ErrorUnsupportedSniffer\n}\n\nfunc (sniffer *QuicSniffer) WrapperSender(packetSender constant.PacketSender, replaceDomain sniffer.ReplaceDomain) constant.PacketSender {\n\treturn &quicPacketSender{\n\t\tPacketSender:  packetSender,\n\t\treplaceDomain: replaceDomain,\n\t\tchClose:       make(chan struct{}),\n\t}\n}\n\nvar _ constant.PacketSender = (*quicPacketSender)(nil)\n\ntype quicPacketSender struct {\n\tlock   sync.RWMutex\n\tranges utils.IntRanges[uint64]\n\tbuffer []byte\n\tresult *string\n\n\treplaceDomain sniffer.ReplaceDomain\n\n\tconstant.PacketSender\n\n\tchClose chan struct{}\n\tclosed  bool\n}\n\n// Send will send PacketAdapter nonblocking\n// the implement must call UDPPacket.Drop() inside Send\nfunc (q *quicPacketSender) Send(current constant.PacketAdapter) {\n\tdefer q.PacketSender.Send(current)\n\n\tq.lock.RLock()\n\tif q.closed {\n\t\tq.lock.RUnlock()\n\t\treturn\n\t}\n\tq.lock.RUnlock()\n\n\terr := q.readQuicData(current.Data())\n\tif err != nil {\n\t\tq.close()\n\t\treturn\n\t}\n}\n\n// DoSniff wait sniffer recv all fragments and update the domain\nfunc (q *quicPacketSender) DoSniff(metadata *constant.Metadata) error {\n\tselect {\n\tcase <-q.chClose:\n\t\tq.lock.RLock()\n\t\tif q.result != nil {\n\t\t\thost := *q.result\n\t\t\tq.replaceDomain(metadata, host)\n\t\t}\n\t\tq.lock.RUnlock()\n\t\tbreak\n\tcase <-time.After(quicWaitConn):\n\t\tq.close()\n\t}\n\n\treturn q.PacketSender.DoSniff(metadata)\n}\n\n// Close stop the Process loop\nfunc (q *quicPacketSender) Close() {\n\tq.PacketSender.Close()\n\tq.close()\n}\n\nfunc (q *quicPacketSender) close() {\n\tq.lock.Lock()\n\tq.closeLocked()\n\tq.lock.Unlock()\n}\n\nfunc (q *quicPacketSender) closeLocked() {\n\tif !q.closed {\n\t\tclose(q.chClose)\n\t\tq.closed = true\n\t\tif q.buffer != nil {\n\t\t\t_ = pool.Put(q.buffer)\n\t\t\tq.buffer = nil\n\t\t}\n\t\tq.ranges = nil\n\t}\n}\n\nfunc (q *quicPacketSender) readQuicData(b []byte) error {\n\tbuffer := buf.As(b)\n\ttypeByte, err := buffer.ReadByte()\n\tif err != nil {\n\t\treturn errNotQuic\n\t}\n\tisLongHeader := typeByte&0x80 > 0\n\tif !isLongHeader || typeByte&0x40 == 0 {\n\t\treturn errNotQuicInitial\n\t}\n\n\tvb, err := buffer.ReadBytes(4)\n\tif err != nil {\n\t\treturn errNotQuic\n\t}\n\n\tversionNumber := binary.BigEndian.Uint32(vb)\n\n\tif versionNumber != 0 && typeByte&0x40 == 0 {\n\t\treturn errNotQuic\n\t} else if versionNumber != versionDraft29 && versionNumber != version1 {\n\t\treturn errNotQuic\n\t}\n\n\tconnIdLen, err := buffer.ReadByte()\n\tif err != nil || connIdLen == 0 {\n\t\treturn errNotQuic\n\t}\n\tdestConnID := make([]byte, int(connIdLen))\n\tif _, err := io.ReadFull(buffer, destConnID); err != nil {\n\t\treturn errNotQuic\n\t}\n\n\tpacketType := (typeByte & 0x30) >> 4\n\tif packetType != quicPacketTypeInitial {\n\t\treturn nil\n\t}\n\n\tif l, err := buffer.ReadByte(); err != nil {\n\t\treturn errNotQuic\n\t} else if _, err := buffer.ReadBytes(int(l)); err != nil {\n\t\treturn errNotQuic\n\t}\n\n\ttokenLen, err := quicvarint.Read(buffer)\n\tif err != nil || tokenLen > uint64(len(b)) {\n\t\treturn errNotQuic\n\t}\n\n\tif _, err = buffer.ReadBytes(int(tokenLen)); err != nil {\n\t\treturn errNotQuic\n\t}\n\n\tpacketLen, err := quicvarint.Read(buffer)\n\tif err != nil {\n\t\treturn errNotQuic\n\t}\n\n\thdrLen := len(b) - buffer.Len()\n\n\tvar salt []byte\n\tif versionNumber == version1 {\n\t\tsalt = quicSalt\n\t} else {\n\t\tsalt = quicSaltOld\n\t}\n\tinitialSecret := hkdf.Extract(crypto.SHA256.New, destConnID, salt)\n\tsecret := hkdfExpandLabel(crypto.SHA256, initialSecret, []byte{}, \"client in\", crypto.SHA256.Size())\n\thpKey := hkdfExpandLabel(crypto.SHA256, secret, []byte{}, \"quic hp\", 16)\n\tblock, err := aes.NewCipher(hpKey)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tcache := buf.NewPacket()\n\tdefer cache.Release()\n\n\tmask := cache.Extend(block.BlockSize())\n\tblock.Encrypt(mask, b[hdrLen+4:hdrLen+4+16])\n\tfirstByte := b[0]\n\t// Encrypt/decrypt first byte.\n\n\tif isLongHeader {\n\t\t// Long header: 4 bits masked\n\t\t// High 4 bits are not protected.\n\t\tfirstByte ^= mask[0] & 0x0f\n\t} else {\n\t\t// Short header: 5 bits masked\n\t\t// High 3 bits are not protected.\n\t\tfirstByte ^= mask[0] & 0x1f\n\t}\n\tpacketNumberLength := int(firstByte&0x3 + 1) // max = 4 (64-bit sequence number)\n\textHdrLen := hdrLen + packetNumberLength\n\n\t// copy to avoid modify origin data\n\textHdr := cache.Extend(extHdrLen)\n\tcopy(extHdr, b)\n\textHdr[0] = firstByte\n\n\tpacketNumber := extHdr[hdrLen:extHdrLen]\n\t// Encrypt/decrypt packet number.\n\tfor i := range packetNumber {\n\t\tpacketNumber[i] ^= mask[1+i]\n\t}\n\n\tif int(packetLen)+hdrLen > len(b) || extHdrLen > len(b) {\n\t\treturn errNotQuic\n\t}\n\n\tdata := b[extHdrLen : int(packetLen)+hdrLen]\n\n\tkey := hkdfExpandLabel(crypto.SHA256, secret, []byte{}, \"quic key\", 16)\n\tiv := hkdfExpandLabel(crypto.SHA256, secret, []byte{}, \"quic iv\", 12)\n\taesCipher, err := aes.NewCipher(key)\n\tif err != nil {\n\t\treturn err\n\t}\n\taead, err := cipher.NewGCM(aesCipher)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// We only decrypt once, so we do not need to XOR it back.\n\t// https://github.com/quic-go/qtls-go1-20/blob/e132a0e6cb45e20ac0b705454849a11d09ba5a54/cipher_suites.go#L496\n\tfor i, b := range packetNumber {\n\t\tiv[len(iv)-len(packetNumber)+i] ^= b\n\t}\n\tdst := cache.Extend(len(data))\n\tdecrypted, err := aead.Open(dst[:0], iv, data, extHdr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tbuffer = buf.As(decrypted)\n\n\tfor i := 0; !buffer.IsEmpty(); i++ {\n\t\tq.lock.RLock()\n\t\tif q.closed {\n\t\t\tq.lock.RUnlock()\n\t\t\t// close() was called, just return\n\t\t\treturn nil\n\t\t}\n\t\tq.lock.RUnlock()\n\n\t\tframeType := byte(0x0) // Default to PADDING frame\n\t\tfor frameType == 0x0 && !buffer.IsEmpty() {\n\t\t\tframeType, _ = buffer.ReadByte()\n\t\t}\n\t\tswitch frameType {\n\t\tcase 0x00: // PADDING frame\n\t\tcase 0x01: // PING frame\n\t\tcase 0x02, 0x03: // ACK frame\n\t\t\tif _, err = quicvarint.Read(buffer); err != nil { // Field: Largest Acknowledged\n\t\t\t\treturn io.ErrUnexpectedEOF\n\t\t\t}\n\t\t\tif _, err = quicvarint.Read(buffer); err != nil { // Field: ACK Delay\n\t\t\t\treturn io.ErrUnexpectedEOF\n\t\t\t}\n\t\t\tackRangeCount, err := quicvarint.Read(buffer) // Field: ACK Range Count\n\t\t\tif err != nil {\n\t\t\t\treturn io.ErrUnexpectedEOF\n\t\t\t}\n\t\t\tif _, err = quicvarint.Read(buffer); err != nil { // Field: First ACK Range\n\t\t\t\treturn io.ErrUnexpectedEOF\n\t\t\t}\n\t\t\tfor i := 0; i < int(ackRangeCount); i++ { // Field: ACK Range\n\t\t\t\tif _, err = quicvarint.Read(buffer); err != nil { // Field: ACK Range -> Gap\n\t\t\t\t\treturn io.ErrUnexpectedEOF\n\t\t\t\t}\n\t\t\t\tif _, err = quicvarint.Read(buffer); err != nil { // Field: ACK Range -> ACK Range Length\n\t\t\t\t\treturn io.ErrUnexpectedEOF\n\t\t\t\t}\n\t\t\t}\n\t\t\tif frameType == 0x03 {\n\t\t\t\tif _, err = quicvarint.Read(buffer); err != nil { // Field: ECN Counts -> ECT0 Count\n\t\t\t\t\treturn io.ErrUnexpectedEOF\n\t\t\t\t}\n\t\t\t\tif _, err = quicvarint.Read(buffer); err != nil { // Field: ECN Counts -> ECT1 Count\n\t\t\t\t\treturn io.ErrUnexpectedEOF\n\t\t\t\t}\n\t\t\t\tif _, err = quicvarint.Read(buffer); err != nil { //nolint:misspell // Field: ECN Counts -> ECT-CE Count\n\t\t\t\t\treturn io.ErrUnexpectedEOF\n\t\t\t\t}\n\t\t\t}\n\t\tcase 0x06: // CRYPTO frame, we will use this frame\n\t\t\toffset, err := quicvarint.Read(buffer) // Field: Offset\n\t\t\tif err != nil {\n\t\t\t\treturn io.ErrUnexpectedEOF\n\t\t\t}\n\t\t\tlength, err := quicvarint.Read(buffer) // Field: Length\n\t\t\tif err != nil || length > uint64(buffer.Len()) {\n\t\t\t\treturn io.ErrUnexpectedEOF\n\t\t\t}\n\n\t\t\tend := offset + length\n\t\t\tif end > maxCryptoStreamOffset {\n\t\t\t\treturn io.ErrShortBuffer\n\t\t\t}\n\n\t\t\tq.lock.Lock()\n\t\t\tif q.closed {\n\t\t\t\tq.lock.Unlock()\n\t\t\t\t// close() was called, just return\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tif q.buffer == nil {\n\t\t\t\tq.buffer = pool.Get(maxCryptoStreamOffset)[:end]\n\t\t\t} else if end > uint64(len(q.buffer)) {\n\t\t\t\tq.buffer = q.buffer[:end]\n\t\t\t}\n\t\t\ttarget := q.buffer[offset:end]\n\t\t\tif _, err := buffer.Read(target); err != nil { // Field: Crypto Data\n\t\t\t\tq.lock.Unlock()\n\t\t\t\treturn io.ErrUnexpectedEOF\n\t\t\t}\n\t\t\tq.ranges = append(q.ranges, utils.NewRange(offset, end))\n\t\t\tq.ranges = q.ranges.Merge()\n\t\t\tq.lock.Unlock()\n\t\tcase 0x1c: // CONNECTION_CLOSE frame, only 0x1c is permitted in initial packet\n\t\t\tif _, err = quicvarint.Read(buffer); err != nil { // Field: Error Code\n\t\t\t\treturn io.ErrUnexpectedEOF\n\t\t\t}\n\t\t\tif _, err = quicvarint.Read(buffer); err != nil { // Field: Frame Type\n\t\t\t\treturn io.ErrUnexpectedEOF\n\t\t\t}\n\t\t\tlength, err := quicvarint.Read(buffer) // Field: Reason Phrase Length\n\t\t\tif err != nil {\n\t\t\t\treturn io.ErrUnexpectedEOF\n\t\t\t}\n\t\t\tif _, err := buffer.ReadBytes(int(length)); err != nil { // Field: Reason Phrase\n\t\t\t\treturn io.ErrUnexpectedEOF\n\t\t\t}\n\t\tdefault:\n\t\t\t// Only above frame types are permitted in initial packet.\n\t\t\t// See https://www.rfc-editor.org/rfc/rfc9000.html#section-17.2.2-8\n\t\t\treturn errNotQuicInitial\n\t\t}\n\t}\n\n\treturn q.tryAssemble()\n}\n\nfunc (q *quicPacketSender) tryAssemble() error {\n\tq.lock.RLock()\n\n\tif q.closed {\n\t\tq.lock.RUnlock()\n\t\t// close() was called, just return\n\t\treturn nil\n\t}\n\n\tif len(q.ranges) != 1 || q.ranges[0].Start() != 0 || q.ranges[0].End() != uint64(len(q.buffer)) {\n\t\tq.lock.RUnlock()\n\t\t// incomplete fragment, just return\n\t\treturn nil\n\t}\n\n\tif len(q.buffer) <= 4 ||\n\t\t// Handshake Type (1) + uint24 Length (3) + ClientHello body\n\t\t// maxCryptoStreamOffset is in the valid range of uint16 so just ignore the q.buffer[1]\n\t\tint(binary.BigEndian.Uint16([]byte{q.buffer[2], q.buffer[3]})+4) != len(q.buffer) {\n\t\tq.lock.RUnlock()\n\t\t// end of segment not reached, just return\n\t\treturn nil\n\t}\n\n\tdomain, err := ReadClientHello(q.buffer)\n\tq.lock.RUnlock()\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tq.lock.Lock()\n\tq.result = domain\n\tq.closeLocked()\n\tq.lock.Unlock()\n\n\treturn nil\n}\n\nfunc hkdfExpandLabel(hash crypto.Hash, secret, context []byte, label string, length int) []byte {\n\tb := make([]byte, 3, 3+6+len(label)+1+len(context))\n\tbinary.BigEndian.PutUint16(b, uint16(length))\n\tb[2] = uint8(6 + len(label))\n\tb = append(b, []byte(\"tls13 \")...)\n\tb = append(b, []byte(label)...)\n\tb = b[:3+6+len(label)+1]\n\tb[3+6+len(label)] = uint8(len(context))\n\tb = append(b, context...)\n\n\tout := make([]byte, length)\n\tn, err := hkdf.Expand(hash.New, secret, b).Read(out)\n\tif err != nil || n != length {\n\t\tpanic(\"quic: HKDF-Expand-Label invocation failed unexpectedly\")\n\t}\n\treturn out\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/sniffer/sniff_test.go",
    "content": "package sniffer\n\nimport (\n\t\"bytes\"\n\t\"encoding/hex\"\n\t\"net\"\n\t\"net/netip\"\n\t\"testing\"\n\n\t\"github.com/metacubex/mihomo/constant\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\ntype fakeSender struct {\n\tconstant.PacketSender\n}\n\nvar _ constant.PacketSender = (*fakeSender)(nil)\n\nfunc (e *fakeSender) Send(packet constant.PacketAdapter) {\n\t// Ensure that the wrapper's Send can correctly handle the situation where the packet is directly discarded.\n\tpacket.Drop()\n}\n\nfunc (e *fakeSender) DoSniff(metadata *constant.Metadata) error { return nil }\n\ntype fakeUDPPacket struct {\n\tdata  []byte\n\tdata2 []byte // backup\n}\n\nfunc (s *fakeUDPPacket) InAddr() net.Addr {\n\treturn net.UDPAddrFromAddrPort(netip.AddrPortFrom(netip.IPv4Unspecified(), 0))\n}\n\nfunc (s *fakeUDPPacket) LocalAddr() net.Addr {\n\treturn net.UDPAddrFromAddrPort(netip.AddrPortFrom(netip.IPv4Unspecified(), 0))\n}\n\nfunc (s *fakeUDPPacket) Data() []byte {\n\treturn s.data\n}\n\nfunc (s *fakeUDPPacket) WriteBack(b []byte, addr net.Addr) (n int, err error) {\n\treturn 0, net.ErrClosed\n}\n\nfunc (s *fakeUDPPacket) Drop() {\n\tfor i := range s.data {\n\t\tif s.data[i] != s.data2[i] { // ensure input data not changed\n\t\t\tpanic(\"data has been changed!\")\n\t\t}\n\t\ts.data[i] = 0 // forcing data to become illegal\n\t}\n\ts.data = nil\n}\n\nvar _ constant.UDPPacket = (*fakeUDPPacket)(nil)\n\nfunc asPacket(data string) constant.PacketAdapter {\n\tpktData, _ := hex.DecodeString(data)\n\n\tmeta := &constant.Metadata{}\n\tpkt := &fakeUDPPacket{data: pktData, data2: bytes.Clone(pktData)}\n\tpktAdp := constant.NewPacketAdapter(pkt, meta)\n\n\treturn pktAdp\n}\n\nconst fakeHost = \"fake.host.com\"\n\nfunc testQuicSniffer(data []string, async bool) (string, string, error) {\n\tq, err := NewQuicSniffer(SnifferConfig{})\n\tif err != nil {\n\t\treturn \"\", \"\", err\n\t}\n\n\tresultCh := make(chan *constant.Metadata, 1)\n\temptySender := &fakeSender{}\n\n\tsender := q.WrapperSender(emptySender, func(metadata *constant.Metadata, host string) {\n\t\treplaceDomain(metadata, host, true)\n\t})\n\n\tgo func() {\n\t\tmeta := constant.Metadata{Host: fakeHost}\n\t\terr := sender.DoSniff(&meta)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tresultCh <- &meta\n\t}()\n\n\tfor _, d := range data {\n\t\tif async {\n\t\t\tgo sender.Send(asPacket(d))\n\t\t} else {\n\t\t\tsender.Send(asPacket(d))\n\t\t}\n\t}\n\n\tmeta := <-resultCh\n\treturn meta.SniffHost, meta.Host, nil\n}\n\nfunc TestQuicHeaders(t *testing.T) {\n\n\tcases := []struct {\n\t\tinput   []string\n\t\tdomain  string\n\t\tinvalid bool\n\t}{\n\t\t//Normal domain quic sniff\n\t\t{\n\t\t\tinput:  []string{\"cd0000000108f1fb7bcc78aa5e7203a8f86400421531fe825b19541876db6c55c38890cd73149d267a084afee6087304095417a3033df6a81bbb71d8512e7a3e16df1e277cae5df3182cb214b8fe982ba3fdffbaa9ffec474547d55945f0fddbeadfb0b5243890b2fa3da45169e2bd34ec04b2e29382f48d612b28432a559757504d158e9e505407a77dd34f4b60b8d3b555ee85aacd6648686802f4de25e7216b19e54c5f78e8a5963380c742d861306db4c16e4f7fc94957aa50b9578a0b61f1e406b2ad5f0cd3cd271c4d99476409797b0c3cb3efec256118912d4b7e4fd79d9cb9016b6e5eaa4f5e57b637b217755daf8968a4092bed0ed5413f5d04904b3a61e4064f9211b2629e5b52a89c7b19f37a713e41e27743ea6dfa736dfa1bb0a4b2bc8c8dc632c6ce963493a20c550e6fdb2475213665e9a85cfc394da9cec0cf41f0c8abed3fc83be5245b2b5aa5e825d29349f721d30774ef5bf965b540f3d8d98febe20956b1fc8fa047e10e7d2f921c9c6622389e02322e80621a1cf5264e245b7276966eb02932584e3f7038bd36aa908766ad3fb98344025dec18670d6db43a1c5daac00937fce7b7c7d61ff4e6efd01a2bdee0ee183108b926393df4f3d74bbcbb015f240e7e346b7d01c41111a401225ce3b095ab4623a5836169bf9599eeca79d1d2e9b2202b5960a09211e978058d6fc0484eff3e91ce4649a5e3ba15b906d334cf66e28d9ff575406e1ae1ac2febafd72870b6f5d58fc5fb949cb1f40feb7c1d9ce5e71b\"},\n\t\t\tdomain: \"www.google.com\",\n\t\t},\n\t\t{\n\t\t\tinput:  []string{\"c3000000011266f50524e8d0fe88cbf51e3ad71a13198235000044c82dc5d943fb34cc6d5c5e433610dc7a44f5951935c2c1d14ac641b02472340a892c4492dbfe3f8262109108fc36d96bdc1e9e46b5f1f6ef6104add2aafbfd8e79246eb3b4637541aaed7d195571724e642ab4d31c909f1db86e7d8516117ce8716bd1e3acb664c499086b0f3bc7258595420e7bb969f934457d195e832ffff4ffddf11123eeadacc48190e356c8f0f6abc381deb7e285e3b0613a795b19bddb9f002ffdf6fd70f0ff2072302b33d2421aac6540bb9f0e85c7237af0dd56225b2264d769160febab952e64bd5155f23e58c6113891143f946591032b41816aed3ac54f521f60605f86791de24c5765b664c1348cc53d5d631b4bbefe1915f2b21fefafb47badeb72d8ba1fd5c3cfeb0ba9d0112396f170e94cd33952c4fa87997b870931bf1a300e8e127f530815ff087815b4f9d004cbcd17013ac143847572a1655a5b36e054e8b9951d747c2c6ff25d7b2edb13a2a6b8074062332f2191f6830cf435a4ed9db5d9c4eb43a143bf3edf0c48f6f9435dafad4afb743a5a33990379df953ecd388e848aff0ebba9ccc052b8303c0bd1fee7e7553af1894e81b7772818bb69249540ccb8cfb47b1517abaf71c81c3bd271f1a5f1b66465f850f377c9db682b8e543c3d0c10fcd2dee263630889b7d1d521d1d27e866ea4ab5f43790d6a7f76ceefd5783678ca92cc131fa42fc4a01e2a81cad734ddf17a53e1bda8e0a21afc9e8c1118c9459b13519f5b3c3d9692c92234f01129d47ae8ec70625170847472801190b46d36f73b868f55f5a18a3cb05af6d38610e0829e4fbf13ddcc202341702e43dcf33be76ff4afe327e5783287c137aad075752940b41e7d9f5146e36d908897c6d7a9fdc343fde2d9c9d6e6a6b237669bd3e6abe0a732861a679eadfa29a876c6a646953c9361830811b012b26b31c9e7158f8de9c9a108346ddee3dd3886da6258364c1281bff8e055f6384e3a23e198b5e6b726fa7f811b3338072019d4b5fd05891770d11e3ed6ab5f7ed33db1c6220c5aa8fa1909949ac55d5435b75982e17aa80940fa574f0aba4dc340129cad491fdf1f5e05c4e83e36ad29ff38f15e1c9436c792024442f57f07583d671dd05446c84ea20b471303f6ae4e5e13f244d671e0ebe94d3d5c17d3f3f378cdd51fa8a6d2c977c78a2397dd1e251cd979803d617d45f575e5d9db0a28b3c4c25fe2af24af5bddac09786b6d6d8aa19cfbd5409bdbfed7d518ef5c863f3ee757bd9d37cddc546cc57d2e52b6ae58789f297a300f1d76c3842603eae4b1224de31a939a68875c86e697aeebf7ebc65568f43fc681bacab830ac4a2164d324e90067125bad702192d01cb3cb3d2689ae681967e86fd7ac93a25cf2e905c88ca5ad7d11962f021754cf3f61224517bd3411d5b5a83955bcea79d702466d073a6eaadc1202b3693e555b051a5b19457023a01e7f943742bb7f5f8aeba8d4e363973aebdccfb12479619cfb93e833be702a307e796dc7431a48abd9b755b392c510b98cd20ef778e2ac88d6a04f23ba8a253d7eb7c13e0c88c3a21f7e23857c58704d139703a47e0965bf2dc8810dc36894ac1f3da73c155e271c106a718b2d184e4e5637c820fe909984642960edfc9e62ac50af5dd3feee6bc560ced7bda676d4e290c9c5916fad52180bbc83d3483e95c79bac15c209936f21042dc2b6253eefdac06e7f4745044eaa0acedabf1d1c8cd9402738\"},\n\t\t\tdomain: \"cloudflare-dns.com\",\n\t\t},\n\n\t\t// Fragmented quic sniff\n\t\t{\n\t\t\tinput: []string{\n\t\t\t\t\"c70000000108afb466a232f7f9f2000044d0168a15a021477ecb9731ed77784d42301462e2d59b0395adc1fa6b569d428583f100860d6b6ae29b6c1b8c0f9c0d9081475ff801f34a9e0677adf685f02b1169fe86c683fb51934915ff43921a73b98fb0b734406f8dd90ce6060d75e923b0d3c738291b421bf16de27ed4785d727ce589f5d0957c413c81d6ee75052e3ab50fe53f1abbb24a138a52e1412683992ad769e65ed301a736914843543e2a3e11eb395726d4fcc9283f8607b38685069f63d05ab8bf38aa24d4073a1e68fa1b6087cec44d7fa628342e9d88a0d20b381014cdd1a07b9d913a3bbcad0cfbddd0560617cf26054138075eb86e06db1e68781541587302e6dda86cae779f9848fcefcc33626f8953bfe4dc293d23e74c87020e79e9ffd58ee345382bd4d1d6e5a3389b0a977124708d05e3c305545857041734dc7092901ab54604b3750b3139dd3b8f2bd94cda89d85be3756fda6f0cfb6f66af3d2e36a7808ff7bce271a0272f8dbc88193ede31613433985cd35c7bd9b627d434e7b2e94b38402b8f1b5619a903572dcf4c2b864c6ee66657c9ec81e03fbe765037f83b2229171888ba08651fc78a1b50c7cc52f6dfe8273723e08932b1a16a6b717a80b5520cf3f40e46f9d9c350eaa914bf99dd4ab700cfdae21437daf695916d4f3121235e4913e0657d8cdcf4afd8f2c7ef977a2dfe49f46fef46c8fa6932e745311d4a6eb3124d5e0a204b9e3227e86a55e662f7002d4f4a72cba8c77c3adc3eff076dfb9195cf68455cecbbfc9b5444d9c4a4775bba68d57ff52edac6ce6ff4efbf6466579bf68308f2ba9a59b2c09506064091a86af621e9dae52366a90599db0d64a23944bc48966b6d3ab8e20f4afb5b0e94370d26a89a9c4207b454554e58ac74f62ffb3eb2686eaa596b9610322a5ce8eeb42f2ead1c71b11b51bc4f1800eb549a2bb529ca4a0d165ae461e45b556b2365e9459d531489d59d0dfa544a76c5c00b0a01270741d4061a331c32fd6cd0e68bbce49137b852e215c9db52f3e430416d8979520e5270be324f3d93132358c0eac35a4618ea7aad997dbbd8e99d4ea577271b935e3fe928f90abd94593806d272a565a414686b8e56c28e34b77671de6a696b09414380bc658c69a309d3225ba8493e9076dac776c845ce11a7ccd6cae58fba5434014250f3e211058b2efe3424b991d679a02ba949b086ba12144c7df3e049b5d026f386e4ae712c9b0b4b02730dd6862ed4e72730224cb6ec9101c5cbb7ee4fc30d497bb1dbf74ffdd49d8cae6c7c9a364ede453d9ae25edf27a2153ab285f3e3be66b2968d67a56480f1f74c4fe61dc69db3451f5b113d7ca02e5afa8627f579c07a9b1814853fb8fdaf0c0f220f89725c757f5617ba4e43cb4f3ad9ce18f48f23d10f9e8950b0fd737070655730532896d93df8768860ebf941365d0634db399feab1f8a88bad28d25e689c5a57321debb8d1435130e90a699e17fa5255f2063f09659a432e9ab5f89eeabe12756bfc5e02fcae2b78a9d0f570934b8d4af8f4afbd57549176f465a0cea485dd89c95a8ae915b4b99548a4c939710c16908f968368baf5f547cfee07f3cbb6142041d6e6084aac253a0d3aeac628cfe76f87b94c3806cb14a912ce8e4981e316511d5ede36f526805d6c3fab5b72d9d91f4eacd26e28cb181ec66611818f5c206ddd52488707a940dc12144ae825d25929bc32b718f46e471fdb30762d299b45c84f6310a72b60\",\n\t\t\t\t\"c20000000108afb466a232f7f9f2000044d00582e8683e329a63e5bf4dc93e93e325ff661e74b9cabefdfbf6065c7ab203c8a629534e87e5f2d4c0f463352904642358b8f137e99802c3a26cf22235782a777769ecd134c6b4d0dce6aa10b485c45ccdcf6deb805342e99ef97e2777aee0b2a44073843fccc2f8eb837031f76a8e968cb01c13c1268af095f54f860958e4062a84e2527bcc9b25a7791650a844de1b0c4b2476282a0e00c9de9d39a41914d1e797a88a8997b96b25a4c194762912b2ddee0e01a365f1afa1e82ea266c14ae94e47c90b5679e2cd00e63ee5a834505ca33463751bac22f3b87afc80099335dc7bfd12b7df224a23ced3d2e25b58a04c4b5cb089ca187abc54d782973c7bb157cc515c7508431ff5bdc227871da58b9ca8a9a576960f38edb384112b08e4c70672a6f23d17d9d901342e56c12370deaaafcb22810eb352f1a6d9377e96bdc1ad4dd397dbc6a227b70f204c1a4e9a4db2705763b82ec4df1fab11420aae547155c6b49abceeed997ff01b7d24e369c65f7edf18665d067c7d2bda5ec8623281fce8c77d893cb8a42053756713e910894a58ef5bf3d9f3a41071026660dd7cd05e1640767ec68f78e22c1716700ca9c0f076f90a65cffc394c10a32071c6532d07b59414181070d08c9c84e3d13842718d51bf90dd36ab1b3f708df7eeb3939dc8553787308983c3e9ba971e7d447788477a7140196c2f717b9ba4f5da92d73316dd11c1d1830b4200f26f733a6c65ec1cc21549b485e3a43dc7a2b68e95466a53544082a20d9a43387a7ccbfd353f7e590b7047f13bfc0d91923c2d75dad4f8091ea96502f98e83e5c30e52e4cd5c670f6c2248ce37cd6ee8b3970531fbf0c53c5fa9a0d73200442b755c91fa4f70524ffe8a36063b6709d3aa9f6b53eb0aaecc57a8c8c9a7ac5e57e03e9cfb290b67dd8222a245ff5439914147e2799fd1cd2ca2cb22fda299443b81e8024adc59d098058432fa4bde376b8e59075f6b86427b4ef6cd7c83b5c08add0c3d3543aee8d672c41cb287c1f0a17f1bc30f62a57490afb2d9f401bf302fd473ddbaf63f6883221579743d6aa1f386b8b2f5db06d7d6c36be81f29fafd14b82e863d744f116ce2be4921631f1fb2797289fffa9ee16a3e537ddfa52350546bc544459c0c9d66fdcbd41612cfc0e2744f50927983a3224291c1ae51608fbc00f40c60ec72573a7e128c3415b0d9a7db52de8ff763dd66e2eeb03ef2e67838c9e68cfddae4b86a3f34a69e0a473b5a73ab627282648df7912c11a4bf033ade185a8f438036b99b960aa6213c800abbbd751248a7ae600357ab888433125d49c5643705ecb8c86f2980050edd7e3c579ad6fcae9bbe2c8d8b38004426f35eadb543a3bef42355acb1b94c21d7eae7b6ed422ca0d58fa03b227b035628871465ed6509254c8a3bf43dfadbb247ecbc52d80d65e9c03c4bc7bc35a829502bde3868af9c33737cd88d70f7427790313eed4ed1938955c5dd360212ef700f274efcc8c26ea94c4e2e0937d475c5c4909edfb66714d15d12e153e5586725ce0c47e8a1506bb197366754ca8960508f22fe7b83a5eaa40f05f3cb87464dc6b848080c0e0cecf2dae82bfa42cc6f52694478dc3d00ab0e1ed696b98e26c7fd34d2efd969f83e284c28ce3f27b178f4691c772011f61722266153142dd0d526393e6c6848d201115b256e65f12b911a983bc2f96a5b4b99f63f0b58485a521553a3e1d4498ac5d4ee70c3f9\",\n\t\t\t},\n\t\t\tdomain: \"quic.nginx.org\",\n\t\t},\n\t\t{\n\t\t\tinput: []string{\n\t\t\t\t\"c00000000108e63b9140d034563d000044d066e1913892ec1d84c179dfa9596e0ce930171a134a09446a888d9e579a6f7bd77df6deda715b028d64f7866603c6deb468d60ecc6488b5e5ee2e2daa1840b76ead998023593c9ebc4178ec89cb198d3c79a867e27177a74ee5f3db74ea194e36e328047ffc3890192665a6feba09ba1e224967fa9575dc7b094e1c29c7f3be9961ba62e3e063f674a09786b7611138e1edaee32cd1d47839e840a74f25ed786463fc48bf3d38a4c793178ab7cbf5a3eb974415b9f9ef7861dfc73460594332f5545c7b7037043afdfc1aa62ac3dfb76ec2c6ae8ebd351f7483992c762d6483b3e2c1454c8ed939ce43f858ccca22d9149cc9da16af86a010be7f3248cf19fa442e94d625ec7f7144b01ac9afb8fb8c595d4cd12fcd2b2d9986371ae65f6f216bed152b79d2782d60f1f01e06b359f88900c4bb3f987f3ce336854a5beaaa616813af4e5f9bd82dca0af6886b544fff0261807bbd8cf90213299f5802b98edc27a6606be8e2bbc18fa7519eac260dcda139f164796a082908459c31aa964a5d3f6fed8944ad61bda126991468f3b7627f2470179619864f234a395ea3bd4f7ba4c0cdf9f5f0dd95d7d59476f2d2a36521c13886265a2fbbd4345e8d1d1e7b5d01a58fb11de23730b087e2b702200155a1ebd50db5751d279438822ac158173533140998a3056893bf470ac84720cb37a4a3205fa88267abc56520bcddacee06011d929c3a114314822d8ccf7cfef89f2fcf0a4fef800afbfca4a62ee848f22066f68c7d3c5c9a24402d422fc2fd5da6d3b470b0ea253f12a883705f7f78bd67006ade4f1c8a3e8fa052656b5b40dacd8062228871cc3bfb1a9c38472b0a720c3c750430edbcbdcecd46b144dfcaa009fee06770238d0270e80671e8ee5f5df18b86dfe8df2f121245c0710ccaefecbeda0ba3db945c768624dc38f21a4ac53741f4e58a5052f3d667fc466b69905f05d0843cfcb830163fae18dd1eb0ce62a59420db9c44958a0eca9ba4258c8060a9956343f155da6c55b2060427d07d9e311729d2971439c7541ae2babfce25a3f5f361fde86c39ef6c04e4e3cf7dd70c9cc0758ec5db3f0cb368e2447080af51c8a5fa6b84ec3175d2d3e6d877b6953e433b4e94b52e1a5f2a1ca37124c27e47f9de5d4c74644181cd37f3f3863ca529c0847bba91c246dadba94b4566b08eaa06a0db4d58b8cb0c8d3070533306a3089891b24a7c4e11b3aa50d5628fc1d136388e8bfbc420a6f12701333ccdc95dec25d09ce25fa4b654260965b91f05b1542c2ee02008d01de4419f14d6749c4bcfcc45a332ba0772def720ea3c8d207802418137b733e779eb406dace0b4b5f5e5e14c787f3e044e6d8160f90fc3c65bcc7f3449205b63294fbc11e9bb92c007d1cb59183eafbf76be9680224cb442806500d71870777d087bf864890848f4a79424c02304f2a6ee2b07f9257f4a2f185ee21239625e246cf680e74b85d292cca44261c6cee6da39bfac3882d28fe547a500f79519ffcd3f54ff5a905c99f22a5e8142c903c41adbe1eb9770b6cf554688529091b126ed2168a23bb191c2b89728e31773623bc58bcb9baebc2c664c79d6ffee7e4404e039723eb05e7f7835c87212431a0131603fcc3fe090cc2fda8239b8f42188b35f98d7fff949b3044544b3bb962ae236a664d76d0c751d9c9ed1271715d240f111febdf7045502f2afd7de8aaaac650511e7bc7716a5b6622ae925abb7\",\n\t\t\t\t\"c90000000108e63b9140d034563d000044d00b2498988864d8b7f59a00d26165f5ae638fc9b1c12d546ffd86212ccd85f654259cb8b8c9d753c696ddad7ee4847bf3b3c10063606cf3972f75e17ae23e73b6a3029f23541f674256d19677665cdd0b8ac15c3f60984bc14ff5dc7a9ae37395516204f2020965713fccaf35cb0a5823085cd6211d681dc6b39be9db46cbfef154a2b9049ed202e9088961b0b710e94bd73259b0967e4d6b8cdfd5b72774fee2f2ceb16bcafa010f247c43b0a9ca25578e7d45bfda7edb82e91f8e1c0a2cfa990223bf97ece42862d3f329521fe2d12493b717f174f966d173102e5cca10943d5b612101d65d0dd48b44416f9ac1eac4575558ecaaa39c47ade2dee6e25fd219d799b499143b47a5bf449701b939c1dde111349cd0d63efd2ff74fbd3573ed40abfdb2310e2740da40fc50c7a137a3f32c3a26b3d407f80e669fe7f9a3542fdd412a9cb53f845d9c1af0814377bf92e30f05ee387fb8675807a6de083c85d3d7860601c8170923c53e5773ee388b68e510a28cd7009c485bd4cb861eddfdd265de042e5a018d20cb810614e2bb17b0f52d6bf620a6f173e0b41951e1b83ffb29e3b3b3c5d9fff13acd3b409021195201d003e281d8cda7b0f02c273e17b1f9b9e8cec4296d65a1c4923b78a2e4273cb42e4e159980472e440078e542eeddcc5a9bfefa5a72871fbcd9ebb74fef20a50215bf75cfd8572d5ab9ac5945e8d6ca35884caf0af0446ee9aab0a1cc3a452ec79c9de786119e63bb3a75fce0ae29c15a0c320fff87e87cc23a05e75b4f4b30b75c6aa036c4b6657f8200ea014185b31ee7fcd00d1eaf40973f347fae227f89d41794fa57ac1ed1efda3ba840ef27852cf33a9dc9e2d77b56af9ced9e75707837aa8c5395cdc15134ba132de87152ce53d506c53284dab912bbc276542504cc94afaca71a5173ff13ea6cb45b47dde9965428ba5d8eb968cc2a5729c2f9b8f1c1de208943a2cd565196e040dcc415d769ceb6300c7909d7e32bbbe83c4cbf4d49f6e34fe56b651838628f3a0001e99f39cafe45c98e455aff8d98f89942a862f7505b9f7fe3f64dacf8c574affacf91c2c05f094127acaa5187f9dfa188f67db421243a02e583942138c2edf45fec4c6b6a8a791da9055be247e9b252e9f7c1330e76f9cb3aa5feebb21f871315b5fb90a1df0b8056513b74daeb6ac995f85c64150ad115a14830d145e5f4e6638c26987b676a1dd19a9775df29ab442ce6143b0fbf8f8d4618084896e34812ed59d63041e2b4ccf6c959a6c849813dd926082bb7b1adedf69246547f335552bcdbae7e466ac31e07e442530ad114abebc6f58015b786e7f35644307fa7ad3d9248c56c8ff472735c6911da1843fe53821b8f5180f8844db4a9f7a826a919fd93c4db4d25861054929260dcdc46d085827c46d60f1097424a6ef250f5aaf3235c80230eda4eb580ce93e1ac8aac422a7aa1241562af601981b84b74949f1c476705c8030eb5d447b2414f9716ff3fd606cd750030b94345c016078bdcb97b7ebc24f661fbd08802f32df18d6a2aa85bfe2e9b8dc76b121c44ae9f29e4413051b527e99fde29720724337476c0eff325cb6220a290a9eb852151c84836729d6a223032e2c638857d9e7f469b84d7d650c45e56e763aee73f902e82b055425c4568725e2d4efd7fde8b02906bda48af86bf47ea27ff00f4528494b74be9bbff001cc841449a184a4e00d64e51a72660a2c21f704f\",\n\t\t\t},\n\t\t\tdomain: \"chat.openai.com\",\n\t\t},\n\t\t// Fragmented quic and 0-rtt packet sniff\n\t\t{\n\t\t\tinput: []string{\n\t\t\t\t\"de0000000108c2751a596bd51c6e004041948ab7d9d493e9e1e9902a7734534fb9eaddc70ca7f821d1b58a406b23ba9db1d03266ae74765b03fac21c284fd50cb0a3d1ca71d8c3cabef5553dd1cb748ac662\",\n\t\t\t\t\"c50000000108c2751a596bd51c6e000044d0538af4ba75e226a6fc7f43e7f1f59610973b8a6670bb8338ca7ef7d90f81aa59f179dae5f8f6dbd24ec6fe576b28f6ce6cd46f26de143b8c99cdadaecf2041948a61bd5a8591486e10022fd100aa20e6423b4f4ca5773edb1aba79b73d6150ee185e66da60e658b2a698098462122b6b80c7fbc5542b0b8e9532898c1f31aa2ef55cbdf036d74c3069abbb261660f048d950b00b7db279ec2bc39912102679ddbffb53f1b1921f137fce43e164af86c72908532f4cdc48eb462a9d9e9cdd6d3c3faaf8aa8aea312dcac5d6aa75b1ade4af6901576649da7e3efd4199b92107d7acee8bbf06734b2484957c3d8cbb1f3fc0ccd56c55223628ed8ea514ffd101bac370c97b28c7da81175ab0508c0002d458cf41f7159dfce22b447c1ec502c186b782c1854718b7fc0fc39e5c09aee31113fc4c5003803fc27ca48850c08a54dbbfdef6ea9a6a138cac0ecd045cfd5607cb6c99c39c0cb21778857f97416b78fa7c6ac8ae3fa2ef2adb3b85fe3fdba70ef9265bb3d54e56ec68b8887d54d02d4a571a6b793ae4df8ff171c881a554b5c5a7848351d446ab94c90ee9c600f03b785fee6300450a4ffc2a55d417952e15449a491296d463ac6942bce4ca93c99440396bc8984073ec028b11ad412e97e26f9248031dc4b1a6ae385803bb578fa1a3b3a58a8ef19c6c511f17b28a275e8c40e51fca8f410a4a1879b5d8749a44a6a9f97c0c9df25318cc28fd0cc61eea78ddc603a17e74eb542c8c08cdbaafa3b44566db4d67e8d1429332375cd30cdaead9594c46d8ce91bce9813c3ca23f55ec2f4dd3ff141471bc3df590367bc65e4830018ff7d845ec4987d11e471d114c48acd1ae9b7670341a34077ae59ea6c3bfc4675cf419d37db48a98a5573b69867039731f537098b46415a193f50b2c85bf9e5da45d6757c5c366e21f04ea62d64b81c28be5148d89e53535414067cf609e59686b7fd135f5cb473e57f6c82dbb291308a1065e0f755935d77517adecee55e72cf37ecaab1b5c0c6e0c7463a014e7e439757913f6e43abb6af775d21ab6e43cbdbcd1935a000cf8025ebc11378d86d6f72d51bf2dfe4be1db5d3b0fcacd13e1b9fbaac6e9153c3d1f4e876f2fa9c3cfc84fd0910b778105b66be70827b1830b7b3c9633af5d83ad527efd81498cbbdd112873cc5ced573e6579acfe817b62280c2122b582b591d52b96cac047bff91192a5cfc001d15c811e055dcb1c9710dc892258ed1ab5152af2cfc57a0b93205dd41fd82b86090b4281b1493a8828ebc96bbd603b888cbca4a15799a5f3eaef93655d5609948080ca57c696d0ffc9a07665bdb063b547bb5a862c3b058c9efb2e7b79cf405fd83efacaa4b8e3a1fd126270587119756562c03d69a9cb67550369030a0204e531cb8df91ab2dfa2e4106c590c59b1b13c447843937929a574d3ea1785db0d52b4b2eeefd1a07c69729bec7c2813c9eb1249f706b3cc14a3d489d6b42a641dfd9e91aa70c7d3222e154af2d7fc1a8f48e5ba11739ae128d1f32ff929aaf4b249df5ea23f7847301e36ffda02342cdf1bd9dfd1979cbd8de32eb8b1eb8c415ddd267efe53f54678d9fc32435b34b00ee2256d8b6190e30a280df5bc48cf9fd669a52469954deccb0f1da37371d513ea57f31ead22a34f9379c7931fd18286d9fde6ecfaac8ca2a9be79d688c5401c65407543c066532f6621f256551c4a98a86b543c576ed0f3254daa4915\",\n\t\t\t\t\"cc0000000108c2751a596bd51c6e000044d07b624bf3d95fb3b7299b67dd836fbbbeb05a51650f9b2da3b2695070a0d19ab0d5334cc04de7ea7494fbe6c438f4e84fa56a3f246132468b5b4f1ba0fcc0251cf278338e15fdd715d5bfed18c1f98ca3cd3cc7b6f904aeeea2914a8b998dd3ae7df694c49c1742dccbf4c3472ddfe2e447959655459c11f18bddd9481eb597b887fb3f90a7d0f05224a144f87a5fdad502ea1e46c1c9f4b4154bafa4542c026296040228703bcd020202acceb772b596bf788341cceca864c8907037c39739e511b04e8ba956efa0fb5cb151ac90eb5817444f6488d593325ad4466058ba45214b965c5738f33d5591624584559ba18e89913b868619d498072e3aa1f333f5d6e3d1db88b28adf7d9350c3c383c1eda894f36bf1bb2a58c7a5e5c8b20597b71a099e46bbd3d8894877e43b0183919185b4e9f059472203979d3334c535fc4eaedebebc79bd1e423184765047a50e6dcc76ba2b23ad23511cae2edd2ad8e7f7f302226dbf6c0e4dbc8c08cda26340b9abfef1ef3333cd511295f14c87197d7890576b4076dd9686047854e67733599d96a99194aecf7b927cae2e5fa4568afc71e748dabd3bd71e6c3984f45b06a068a7c9c3a1ca7b5c245a9bb2cc7e2726e833e283430a25b6ccad55bc5b7644b44f99fedeff3c3bbf995a0387cf1e45a5684e5d1c01350d0cd2d615ffb6d1011d80ad16b75925efcbee483e4e2c0e2386e9e1b35b5a107ed97058adb60e323342989559856faeaafe5149bdcd60c113230f9923b2f654c95f986944a014198686f9c2275053c05080e3bf9fab7d46302948b152e2f2fb1ecbe71b412016b3f25ae512ad45cd096d5f284a0c2808b5eab03b4b9b2dff4d81bf234e75e30d480f39a5f9737563e31a19b14d1038296915af33e0ac0dc18e9c871e539e8772d525e5fa19afc582b1c00ae573af39fe293e16d182bbe57af5bee1c0939862ffb62e3d52a60aeb71e4db2a4a1708e75afa5f37d72cd6c0e036abcb4eb8db6515fbbdf98be95d0a6d261a9445797a8f38c3579a2f04c9f5b74dfd1ffaba2c6aa05959704b9b8cb0db30bcc360711c5afef0d1e7c2b076466dcaab104c70f3cc0cda33d7a47462c3fa3d7e34a99b2d8ff3fe5cafd27ede28b9e09b547cf955b97b0d0d4ec126957601c6982d176252be422df3366118895ca25fe27a96c9c234d484fe98634fb9e970e0d2b096f2ced5d56603505990a65363726c828aed2df0f112e0c44f058424ff5c25ae60aa2cb5fdfa289e8ebb63908365aa4e4609eae87e567f1e86d92c43992e6d505f55226fe3533f9fc9c9facff9dae02a3e3c97ca54191bebab93881c0e89b9de5bb4acd5c6fed5b1e7978803f693bfdbc125b4d08fd34fdc6aaf02444c4b06010b0eb2f15d86850a7aa5af05af438f6b7345fad4315f631bc5b017c7482e7af725a09844472f48e4de79b15284932a7e99a46ae72b187ee3faaa0f31a36726056e86eb706bc8eac04b68a3302307a157c91639f30bafc2d180670625673310a9a45a171063011e59c57c8eb67353a8ea344a87853e7b600c2b49a7a1b60a2904c0ea55951af6430667ecdfa6e90a8d2d0ed9857ba5b876cf78af190d5013d16208d2b30d02cf2c23e6ad1466f76c30d11034d5d2eca113e2764b2fb6298fc4940c16d971e28e3e6e5d0e8eea1ccb9b4b89741ed675861fc3680457ee08547f4efcf68bb6247313f8218ae3ec372e51ba8786ecae115dcff241e0\",\n\t\t\t},\n\t\t\tdomain: \"fonts.gstatic.com\",\n\t\t},\n\t\t// Test sniffer packet out of order\n\t\t{\n\t\t\tinput: []string{\n\t\t\t\t\"c50000000108b4b6d5c8b9a19769004047007e07df0d887979774085206f2e7f0146b02a8699715a54fc71ef27ab5a9e8cfbf155497bed9e25934aa74db1b3b270112472b7bf7587423b3ab2aaf99de34cdc591bfe04cc0a448875483ec1f071622121a49c456dc3ce16bae5f61f84ceaef9e8b71db56479845b764507dd9416e8c44b8c93406a230945eb8e484471c1b6207c9afd944fa0fee555a5c966f27ccffb4bfed37fe3936f2c84e9852c0d46c7e2e94b897fcef18c4b0b83d966aef75c0af4240325a24668bc017e0d3f69680ea5b2f59bd0b964062bc40190be86aef3ed0716a18a67057f309faaf3a040222812142a399deb72ebb330d03d59961e2ca10cf78d40886dd094368a881db261068920968f6adf7a7b1266faf8842e71840a29859e877c66e3ebc47d7fe3ee586b6512d9b0e1bea82b302647706473e68dc8209f4e9ca19f1dd25fe386e62c21d9c741e75cb8b11606739ba3de6d6325ee3a9cd1bb2b9613746140ccdaaf936eefdaa1ca7ad73d684e5d82b1ba1dd3356ca0c881f6eee72c02c8b78d02a8217a8fd972e463c77374d0fcbb761459e3ab0bb5492e516d7d4304c19c16a4bed11ea7f4e75616a26a7c81b04ffa580cce04d59825b8ed929578f9219e64bdbc6352ae6e4150a993fc3cc27ce4d66c62893866b9053bb737ac40364094b53d91e8b325b9dab5f537af04f10bf8db644897b0b03b42b1bd6c3aedfe018a6e4f6533183649f4ef6a6300383430f86e802fb4e51976d056a3c40c3b53c847b8308cfbe54dc2d20b8cdc870c73f5fc22c376c35d9a85348ca6a2288ae03dda6b97f0f502f35219e19cff3a810143289cb1f0715f8785028f887bf02c656c9cc372bdc419290f05957ad3dee82b56db352db65aca58e6fa0bd2f753160dd9e7214968c0496be1ab49f978a9252e49266939fedf542760abd653dd38b1659bcf452c753cb89e8235bcf732afcff8f524331be9b6f4a5081c81255e68c358b3444fb1d57bf5659d86b6674544fe2826ca81ee52f93a17b3291826678e488c3074c259223845e4083a413af7fc93d9992823620a8d29d321438a760293e36c4232216207060dd3ee5c4036250ede71ca9cbe335a1e068eb3ff6c10a7f1c8204750d6d0f3145014949a7b4e88a723566ee5446f960a95d9f81cc45155443da561d85a3a311df8172a1c4eb118bf27ec4b3cc4573b1ab421d96d41cc1e5557797ca68f701fe75c474527144d30b9bb00a117637f88896b0b2dcb9bb29ba144ec384b5a085e82e7387e0560a4621423c306b041ad42e84928ce23bc2a7f995ef5c21616de43be8a1657847489b32c8e364846389e7c8cae99530c499f3662a2ae7090e54958ba940b5d3eaf1333ebcecc7f06f29f68ffd97defe65017519c29d355ecb0a4b47ab08dbad8cb0cc5c86de65dfa703110c60a0c55281925018fb4ef49fe5d0132dcc86602c2ab9921a8f3451480d3e931f01c2f9a81873435bb83860128aa78dcc950fb13e416d90ea969aa92763f9caefa0fe3ef4ea82e3af4a3e717fabcc589fe8cb9bfba6810ddf7def8c1445fc0048fb07be043a628e9c920bb72c04d3b9472caafc6c14bffb854a1ba2170dda919322a6d79eab92e3a88888a224093946b87840033fe41941f780f569eaf1fdac55e36b74514d72d09823d71f48f5d5f0ceb7b6d69c5da0e0408c1b13c265d4775db6a0f952ae72bd5c277b22c4be2f2728451ce31e921c856000d20da0489103bad6a6ab4\",\n\t\t\t\t\"c60000000108b4b6d5c8b9a19769004047007e07df0d887979774085206f2e7f0146b02a8699715a54fc71ef27ab5a9e8cfbf155497bed9e25934aa74db1b3b270112472b7bf7587423b3ab2aaf99de34cdc591bfe04cc0a44884e9e716461869ca408431e1ba92740c598aa74e9cd45706f28942f9cc64dbfd7c292cd33e82b50ae0e2e08dc478c19886718cde33e56c38517f8834d64904bf4fb1d30650caedecb9567ea8ef50157c287a2741e98a00f8e7e19e76bbb0143ac7862a49393f17ec66aa0e2c02123ffb5abcc96ccf92cd542c8f571bd7a4382ff81432d11f83796959696c38f2029db6c6a536a9ea24b74c848b95882562d74739ac95f5a069d48e8756d1a9750c7ebc23d4ee22d617b29b415b7458b3bb8106c22de3a9ace9ec689e6e00471aa33e570f7481d15911d7cf46a429cee1a416558c5e78360795d905ff1e0c81d18fcf4954131fa5b9289ed2291e122cdffd666c66209aa2cab01730739249ce293b3ba3abb31683c108bdfd51f54593f47411077e948f01105bd9bfff1578d235674e96a8b9cfdde119edaa960b84e70fd681312514151de1d5939c79abdfe4953e22be5ad3e6e242d0ce9b3f2e589ce3c768f610d4d3a32e33225d8a5ce2ad74a9b40859cdd9ea99f14fa2a7018e4b6aa6e46a0d73d46d161ec5d3b30bd55078e23987865551a605a33472931428ce222040d20c07d1ebe970e576d9d54ae688a3fe9388adda3da4d011a7cbb604f1f19d2ef1be7ef4713bfa84d4d69ffa606a08b61a1ebb99aacc4e19d0c5034642da1ce2d7d5abecc8adbbc6d7f72ff2da4ce5228ff8626509b38e17b31717c0b7821558b021ba81502d54da7e778d4526367109333383e7c67d5d5bde86bd4001fa13a703ff9259e1c2268ca8f4ed2e6c022a7466e2178bc725f59792803ba28c629e3df7696c416dc294b510920077b2d2b258fdc3506c36c42d37796c8fdb20ba797ee68fdc410325a355f6c1189aa9fc9ee220d42186677e3955cd3c844ce505cd601f04201cc390e923db2ea6407fa2fb4ca7f3f82d0a82d52697ff5ba5d4633bb0d655d7ee3348b89c9cb42870cdfe7c0c162babab4208a9a54700c5785d4134e9e33361480e3512ac8b556e11775536e90ee1270a4cb4d6bf2faa72d7e1f23ceb4fc3aded0e423b6be6a55bc25e5a99163b4f5f72ec4a24fe96f68c739d1848c92c4236a5a637d19871456b8dae671ea6ae5c16ed4fc257612a0821e6dc1cbe2ef4963a1436925dcc4e6ce528fa75e41f7721b379fae8ca09e6fb51d0c3e3ae6c19b98860ab9f74013146c6d375656dd1f530abfa64670a510390e9a54bb9a4ad19977491377c8cd743597bc156ee3f58cfcafa5a547b20852749e66fa8838c100ebde039ea25c8ec32b0c6325b793797546a095e79b9388d8e67dc6b4b3892f93ecd13e64ba4b2ad26fc810fedc374b831921531344c581927da9ba822bd625584d98c7582759ae40f01e14277a0a13d30c2c12536df698330d8aa6a3613a42c493c42692b468b4a2cc6bb6dd45684ee6115848110bf517074efd93bf212c071013f4359f140cfed17bbe10328f2026cb8ada16427122d3fc8a933119a1e3e4cfe2b95cbc73af5044cb099cf34247228972495488ebaa4696280d17665c421be5f1727c5d5b013d8aac0e9943bbbb7fbc2162a4000a306dffe3bc4425cf272f1ebb63c8e4998f867fa6b05d71a8642e29392244d4e2e2351bc149d665efe1b9519cb1b15005393f938d\",\n\t\t\t},\n\t\t\tdomain: \"ogads-pa.clients6.google.com\",\n\t\t},\n\t\t{\n\t\t\tinput: []string{\n\t\t\t\t\"cf0000000108277148f2b916666000404700403986db57eaa4b165be8ab9c95452bddb922eb35b7610a8e664f6b4620d870507c241290ce885c36d7672c51d94063bf893e01bc79e1d81bf023338da3d22f63bc7aa433f9944884c88b10f198e849dddbc1e9f9bac61f98f67f27d5452da6e2bda1f5210a145b1f1416ad2fc15e60aa00444362630650bcd0ee47999b689a40100dcacf40a4c3d74fa6293d4a5cb0487d8c76787c04dd2b47ec7718df5a2dc6942069062617b3d40a95360802957419433436c9065bda5a6156291d909a079b6d3819941368d7e17a2e97e36be829bb421b44545af47e37d7815ee1f200ca28ffd361d955ebe0484fb234a7e8a7c68ad824fd14d517fa7b35f878beebaf3dd22bd9f7a39cb7e0fd8369cdd28c05a06323be7af0b2d69ed2a2f4ea9f25d000de71bf5bd6765a20ddf81d976cff2321f1a4584ad6c4b7e9a42a6d4aa3a02b59f7d994a8e4a3070a4646e51fdf354448420ebfd0aa9118d010d019cc168f2fe5a9ff0c42e6091676be11f28a372ea97d008a1a02efd58149106cfdec7ef86f5416c4b1a408d8efba6c8d4742d781374ff0a1a8ac183bffa1345dc8e3a7cce04f66cc865f434decb912dc9e8e811eb59b80d3e39d5788639ae7c5ede73a935edb47d907725656be0522195bd2c099b0241f36664fad1543e4ae43862252662707fb424a8f5f9486b8e3779ac24bac457671ad664475d1fc9eb1de3c46f624b559742b3477953552e44f20cc1725a11ab915423fdce7cfbc8dafebc0c43d1ac3d3373ca2f0210924433c46e5fcface47a65579efaa1999d52b2632f69c33c3c63537c01be68fb679f9229f8f68c5caaa23dc4c61d3c45dee90affed984dbbfb06b2659447400b4dcbf6e574719e8d49fe0dacea9509182a42f6463138d8693a3b8d797d3bb6b0b02648829d666341373939ac41a57e90fdc2469623b6e2d772199d7c806d5998f439603c0de8413f9d29f79323ec5410b409ab8c95547ab50bb921fe0c407b7aaaf663389bdea5ba56c023dc4622d6dd9cacd8f318a6a0297d041cc6ed455d906be50dc85a25ecb32f4a565432fec9f359833be1c6a6b7b4bd119d3c4b29932eeec8d140dd467ab4d969bd23e9d2a95b92835587f32428f957b6785b8206a4834e00a3013e0b6a5855f16207268bbdf311572c54d2e6ff9c659cd02c258f494c3b168ea170c69138b63e0dde487b72576e87657befa44548b0b4e1e5a837dbbe66a559cd1df8f2151ba513930243fd2b7705bd29b183dff966224d87ffabb74017d634ab2e4b368052504a7f6bc1c62d39a29dc2dcfba683bee2039e376ff391abbd13a0b89512fd8f6a4e66051dfa04e0e1a3cb4bd56a9b17e27651873bf2ed50f65cf1cc608afaf06fe7e6238347adb66f01d1f0b9b51f0078615553cb8ff8d6786b87e19dbc44000025693c4b34cfd695601a680efdc1e7465a981b0f028cbd3dbb938789f240e39223290e34ec303ff5c78a4a637ac04dad60d744f82e96c3c9e8ed6cb0248ac73b5b3a92007edfc1277c3cc6fa1d0045c1c371820f06bedaf046dd999665cc4745ddf8934084ae02e9238acae6dea330b5798e046138f5b15011875eae72d6eb6689e56e0ac5c5d9e25dc4fc1874cf37265e68ce5b8630b84ad8dab7704474f0bfd08ac295b3a508284fb6ff201f0aee6388d0e1d5cdaaf4c20429874792109f5b8e2f3eae6c397e46a510ed829a6746e523481465f64be4e145c83d6fa6951229d3\",\n\t\t\t\t\"c90000000108277148f2b916666000404700403986db57eaa4b165be8ab9c95452bddb922eb35b7610a8e664f6b4620d870507c241290ce885c36d7672c51d94063bf893e01bc79e1d81bf023338da3d22f63bc7aa433f994488828e6082edd53e228164d8862067483762ea9523c90d565b9e4b185b7805eaf8220664264e82a95164ab6cab4fb3f5e795e246e7205aca236b3c94dde0ff4fa66ce0924d654829d59c3eb690470b20c5011c739102257e9c2247dee67c0b98190d0015154d31041aadb026b8d3a828c861a15dccdab0cec8cc99b8d6c2acddbb93ab66253e87ac39016507dba42e8fa9f5d22c7f27645a02361842a59ebb2eafefd0f3b92bd9692a96b93875defcfe2796243be8861c59ce5ab03f1d65d308ae456cb9656da1f01026ef0807cc9930021b29d69b36881c3e7d70fc68799ca81922008db93c9ca4a365ee191e214d9829481fd430194ad4583a0ab2e920c25244d7d64662872b3b69ab413ccf0dfb6bf2ea9a9b93e04ed19f8a0e146613ca9d511179f80aeab40d573590d38a7c10840e3f8b9ac1bd23b0826aecabf6d1cdb2aef02deb982c2029dd6d8bc21da6c262c8116b7b383ce8c9eec69da3e16c044dd96ae08a98595d128e89dd55e6dc8eb08b8d51327278027137f60a0e1b42878f98ca898587474f6d509c3a58ff4dd7f8b10905c200cf3170bdec725ee14a1ac8ebd1022509d3e499f5e72168eac43264d7246daa0bfc81a216ca97730b7e043cad8d8a9af5c443a5d15e9a88d82b6750c740eecfd63561712a185c69532b1a18b23513d7cf871f14d164ec544f22b6a8cc77d6fae5fc6e47eb64f08617098d229da78a378d6a0864684a7978f650c7922c907f97b0ebf2be29cc834ffe995c9636b310f4a8c2c5623c3b7b533518193d226923f111da1a0e8055b9053ad7f7504d194fbc3ae2b41cef30aa099624d5e229ffb56d5883a5a09163d22455cac52e37ee0ed5367b7c3bbcd4818a46b9b363b592c53c780eeae2c8b80a1d60d296614c998a9774f76453a58bc55d1c26bb10dc321c159858d7ba2f7855ba01aadf3585632c097e5471591dcc24d87e9b76509c10e2710310e4869de710ce0f484d326be751f8e9f765a685312423f1801aefb28dfe0c8f286432356d06857101a67a432497c5849111db2792fa0ee4ffff49a9124c152bcff82da1951258f989681e4f1338357f2c9f82333f6051b188f640bf200a0a75be1d35d2301e8d3813f7ba1926a28a0df05c21413cc0c4090c1e4ba4877dca8e129876c72ab3a801b4093320f5f685120680541d97889eea5dfcaf07a7ecb00c0ba0ae193969a4cddcbd753609a5304ea88783358ab0ae005c6af27bb58b2c4282186461ea50540845e2e2a2f4efced88c8ab9cd9fb4a226a265714c77ce7b79d1a40bd00b24cbba498dafde6bbad91686cf2e13e75669234bc342218887ba910ac81680122ddd36466e7e8a983d5a0fc18a6e9a386762c32132be08abe5554e334ec7d88734cfad9a378553b71222c55f6aa114392e015dfa2bb6cb4ad241c6bca82fdd0a00eb8d6b4afac61268130dea2807a97e4c0adc0e2be39abccbe64dca5c480e09c4bebb8b598e4f60afb0e92dce710859013b1ffa9c78fbf380160f31b1e72340dea86d353ff0e95884b72e2c2c10f6eff5f36c588ee845b7bc97c3b6bec4aa879dd0eb56b838b7bc2ec6e66a5b5517908197a67566dce7df421a8daedc98848c70d1d2c39b2f3538e6f17800bee3d3\",\n\t\t\t\t\"df0000000108277148f2b916666000403a52317841946860def0d7829c06fec03ffe8b97f84e10116fafd93d1d2d39bbfda0d148778c21bb1e1667eb789b1ff70c2e3d557ed9c31570d20d\",\n\t\t\t\t\"d00000000108277148f2b91666600044cd5e860e3fa7ac4769ec75d9b7d20f19e69265939a42afd3c4248a7f5210358a044f42869567e72a05642e96ddffa67bf24ec2d966d860c6accdee01d6917c8c43d4d089d8bb63ff848b617c13fbeefafcbb049ab0822a9ca7716c95af84d019b755b145dfe43c218555d1a7e047deda7d8db352a386b2b6d03f2e7f4510f47ff4ab199348dfa81c86bea5d09d7c7af4ef3f04e99fe4e6c21d53c4335407e27913129152033f17580f97d0345c8487a7ad329dc5c97b298ec7b80fee7813f1d6f94945a44ff662a69453c2dc7ac5e8a1cb90400e63818632d7f9654f140a61280df183b3d9f9b824e53d82f2c14ea3de89befdc79b84a4a3eb659a41db25622add94f2ad4b0d5977f1091aae0a4b83c7b41bca61c6c8d807ef02a8ce6240b76d442559a8b338b39418d27e99aff38840fc79a20995af65b3bbe1e3177074079a47578c51655a4016363364fd2c108d384e602deebd022da3c814549cd57d73c5bfc20e279045e2ad436fbd7e7c9e1985f0ec2f422e310e7aa8cfc48e637f9ac61d06d6482cb40b4376ff3c7abff3c3c26634689ae16d704bab1343d6413fc7b6c076eb0454eda2e0d1e077db40c922ebba6b0b1fa814e3ba76d8d6c4289abbd655f0cf5968eb2aba7131680b44da8910056a76647a6dfea95f27364a7ce694b8fbe19ebcb2a47e7350d33a36f7f5ca67af5e934f449125f4aae870a5b23b4370680ee02b194784d5d188ecdf58ae5454221406bde0ddd3e50d3363a564d6ca9fe0fb57d4df8716cb430cf553be573aa690e5645075ec74edd38cf23215bd50bcda0639dfbbe08dd6c476249e35da819ea6ccef808911b0eef6efaa4947244472795bc071d7154ed87e4a43575b3d61a551fcfccfb7ca3edaee9324f33f54dc9809747e59e24e79f256e8e72f01b8647f71c4b9dc260715fd9d83d3dbd9e124c432c04b3398e74efa3869fe129e368c15b6ca234a243fcac675adcc1db247e3f8485ac4a78f4a1ce2db3b437a1960b02f0c227901d165dcc05abdb3929a80dff2eaf72816185d4af4e28eea05430b736ddd2962e03ec64fa48649dd610e0e221c48f781b45cd9963c176126110e662369874e6a55f28039a23484c5c53714fc2d2030b48f1c895102ca9ad8acae1ec4eb0ae8d8bde31cd74fc515930078d22ad07dc3c7221ddbc4027c746207fa038b31080714091459c9a66ba4f5912d8d3905d3a9a47e4d8829a8110c96c0c9c81291c7985073808814109364df15b04520dc07e8d67cafcda71f0ca59423df5fadae92417a8661b3cdbbf6b1059780fb8b43eb4dcdacf731bb8db26294f978f6be7506b87d17a95367cdb83000565a4986e66dd60d0851f9b593d68790f8097434f62ea7a7396017c3c84754845d3a97f028cf8697d929a2826451653ccf84aba4d2f40fa530b258c13f08c6523c3c02d9669fd46b6a51f20ad323857d767150e3530a66bf88976dbadf99aeea549254c07e11e14085979b60f3b7e1728a4a2d7a35b0377c6501ae7d1d4bba338fb51a17ca8f7e698bd70cd01e8f30edf3e83591a2eb0038811e347bfcfab159b0d1ff6153e0f9ee4c129cfb7687e30b82eed74130c466eee06506dde50805b58c2acccd4cf4b2cc86c52fa2af602a8a7064eb9d90e1c568373b19e43ef4e7c1e4e1c9a58ccedf80a02a46ed64e68e72d4e75c7436e2bc0ba59f95a00456e5680af9e6cf4bf3a6d302ddaf8847cfd5ea606797\",\n\t\t\t\t\"d30000000108277148f2b91666600043d24b66b2531ed9f9c13b07b2654186b0410a608592fdf728479734933197ec06a1cde860f36b3170fb2a9c85c62a7867ba6520dcb2d0ab2f6a484d9ebf8237d7a6f3c1fb16c1e0458ccf20e6d1b298a7530cea42636166027d92812915e76fbcc436a5e414147672dd7b0d19ff24513800e63cd86984f1c93ef1430bb848d37830eed61675d7c9999b92c6e5796d384554c74dd5a163de341ab309d6b0cb028aa08e56d79c60980d4a49a1c095456ca119fc3f04e496c93a084d017f60c6e031d6e9ad2e4fa699bb4b0c92fdcb44131129db0d30ce9efb740d3db0339127d9bdd1d4f677b1cb532a33647851ba9bb20bd8d6aa593271a85c3a9dc9835065663e61faa8dc6af209a0caf183d0fda3d4839d40edd5659dd053778642db8fba21f1f793e45c5c517e68bbef8543e3a727743c7bf87d047d441d13226b9021fac56904872774cf6768dc91db8ea489a244500e9e527acdc0088437357acf9397b014e66fef2db1248f9c6a578af07d7a02b1356fee02e27b8207e57633fa7bfd87ccd382e368c14b946aea780fcbe696d6e4fa3aa589184e104177db2fc3d91d4af120d9da3bdad021d003796b8261b590d8113f995dc1db4fac1c62cb68370d41cc87c982815017ae2143d5a469b742d019e5556d813877fec9d021cb37f80e5987d9f743c2b39093a34f6654164a8185a5caefbbef8ea17f62f6801a3fd89fae333c878cec9b25d10dfee2abca65d7c909ad2e4f11736d13b1642df4c5a0761f8f29f35f37def9ed327f4a9d8e53269fa6c7cedd0f4fd67d6cde81934e291d9fca695cc9745890cb54503e29e09f4a30f80e2f574bbbeedb7d20481c583d8362d22b2dbec09494095a043cdae283e86f905d8807f7b7c0f06ce968487bbca1e20b87245b68f24537a7c7e768c838f1bf26650afdabec2c0bb9736b345473f279c9b73ecf0d2c4aea49330ecfef0949ef7cb81861b05950ec0772db856365b136ba75d5509c01d7a970c84ebc77d8d5c3ceae1ef5f3079afc7d78965ffa3bc4c64ef1b4718ffb488a571528c83b615c43022616bb4c494c838b556df5ede711a688b0315c1ce6e2892247df582b7c3f2b06cac0bd8d670e2b581f074750596ba162189060b8af3dfc650ba3b45932edc4f94f08741d3072bfd1ef8159b27a7f3673a4fc504304c12116e3c2d7636c663c9fa1b2f5571be88769f33ccb94a09abd9c5a7dc8a8c2031bb2bc256b84aeb68a9abf7673151cec41b48bdd74f395a46acf30dae43e060e596bb2e739274210701ee9bb6cc3ff81ace751e375a01f17b3c5cc5f1234c488d69611bb27f6e3ee17e3c3843ebe4a280d6aa8ef017058a872810a437f85331adb3cb8d382650897b1b1589ee6\",\n\t\t\t\t\"dd0000000108277148f2b9166660004053972df1beb451f73eb070e33ed63f681eb9b7e1e03f20baff3f54157598c7dd90a0de49850a3ccd6eb1b1cfc9dc6d3ba9ed1c0a19c69bf433da300d3cecc4ef151c44a721d680e3e3aaaf3eefec23091c5fde22\",\n\t\t\t\t\"c90000000108277148f2b916666000404700403986db57eaa4b165be8ab9c95452bddb922eb35b7610a8e664f6b4620d870507c241290ce885c36d7672c51d94063bf893e01bc79e1d81bf023338da3d22f63bc7aa433f99448825ee873013b006b2a6c87c7581c7117bfeb4ec3d68405a68d9488f6d58474dd16539677e869812ead055e70d655a660062e17083995c0dbecd565c79800b11c8ca0c351ecffa61e707d62d443b3810bc60d4ef87aa99b979ff55ee1ea46b65436c15534e5315113138aed6daa9f04d3050d77a7e379c83b948d3797177c1793e59b2555423bd52595d93e293ea8ffa3c428c6dbba4e202d76933caf6a5609b0a4aa6cf4fd2aadb6505382381ef2d5b33efc43eba24c84b7805baf2ddab44a50180e5e6f2a31f9ea8089aef562d3b578a799d61befec99c016fadec3363f68a1be4ca1e13e8bdd2809a1dacc41134663e22f21978167c5ee8ef49652ae152fc6c1bcf52109cd3076cdd599cb43261941de7aed148d7d3e956cd615549a9647496f43f998daad4c841cc40ce1501fbfc152b957c94be558f6743061e312d746137db2ae6a44e181587dbf6b0d9508cef4aefd99ea5d3369898bd4c3df5e95ac89eaaba54019ffe0402b8f567c91b9371e80c621c67d3c831331acc063892bbd8a81cfc0498e78474b11e8c05dd8f540c449505342ae95f6281940aae973db35b8e31ff801f6bc8975f592538881ae9cc4cedcbdb39a784a9fe962a1f12be51c11b91d4dedf649bb5672dec8e03db97b0d69fce36edfbecb6836644bad1ab8e6d4e13644d9c3476db0e8a8eb4b5a5c32f7a5604c8e19700c53602839478531579cb4c4bb5cc969cf482f325dd837629318baf128920d9978e23296d7016e6c05c954f95881b4f9f7e43bcea393951e91af0e4a671400dc435bd2a1616c60618df2476d0ece060dbbda11e751e256956a0dbcd7e4a8d6d85a3319f22a2c5f26dad50e82f70f3dd91feff19c775aa60499a3b7daa57e344c07c3787e99d53303488801d2b17cdbfdee61ea3fc473f6c146f06eb60d70594a59e0ed79cee6ca4a5f78b037637ddab69fb8522c0f7bf37aa7f59cc7fa659e759db69966455944975cd22a1a1355f35a589a4978c8f3272e1c4f6793288a00ab879299aa6ad02d966e3dc67cee0c808b1a046458cff9bdac25a4071eb10038a6389a0ef7233003641bd4ee1efad0e9b2f693396a89ca0db3c05b6abfed3b246eb1b23a6b77e8b486f26d9c3dde9dd6f3637a8115940ed2ca762ca6320609f61c37ffc9c3f2f7a0f27edc9891c2eeac49ba258a0d09c35c4fe1dc52d4d9319aa9b1a271a5d8d2d3a75fef4d59fb04679ba526aecbd19d73f72fee537630444326e2543ce564c669bf378499738385dda9ac63521a1b91f580d0737a7326009f0ff0dcb05aa8b86222c934d9ddb4628e30b6e12ae370154ab39c605431b4c40683592afcfd6fccf35df9fe5850442595d24be3d9f4298bf3d541f09e7e71f552c88eed9642df46953622d5aea05b5060325304ec81c0447ac95b90f9da4359e3286938f06aea3d45030cb836be15b1c65e3edf44cbcfe2f01ef8d7209c69d7c81334c866ebee50e418a28336cea1982069b4df090eab81303761d1af337e083f1e0ad1440a02ef1eefb03506c39d2377807e335ee64bdb76527f786223cee5233299eda9fcb1d38f19c34480f790a328b0735f80908e3aa70086df828d56b6c79516f71a24c9d94f60335f86e9d29c0c5d3872b\",\n\t\t\t\t\"dd0000000108277148f2b916666000406672db10ab41db38c01f7021709bac4d1659d872623eb5852b12b494535d13779a88d37e9685da572f6b2de35793a519a457493456ac4ee242933cf92d783f783656899c31832274bf1c26d24720d9d8ecfec598e19c58a478d2991dfc1cda3000f7bd7bd17e80\",\n\t\t\t\t\"d60000000108277148f2b916666000404ed98b1b4ac35c0c0ef18c88adf08a6701ccb0876ea75aac8c128349936fa3cb6728e4e58de8673dd7dc8457b092957f26bc8194233bb81c7e78127844f9b833f196dc46c5cb4064c773f3c6e0bc73\",\n\t\t\t},\n\t\t\tdomain: \"www.google.com\",\n\t\t},\n\t\t// invalid packet\n\t\t{\n\t\t\tinput:   []string{\"00000000000000000000\"},\n\t\t\tinvalid: true,\n\t\t},\n\t}\n\n\tfor _, test := range cases {\n\t\tdata, host, err := testQuicSniffer(test.input, true)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, test.domain, data)\n\t\tif test.invalid {\n\t\t\tassert.Equal(t, fakeHost, host)\n\t\t} else {\n\t\t\tassert.Equal(t, test.domain, host)\n\t\t}\n\n\t\tdata, host, err = testQuicSniffer(test.input, false)\n\t\tassert.NoError(t, err)\n\t\tassert.Equal(t, test.domain, data)\n\t\tif test.invalid {\n\t\t\tassert.Equal(t, fakeHost, host)\n\t\t} else {\n\t\t\tassert.Equal(t, test.domain, host)\n\t\t}\n\t}\n}\n\nfunc TestTLSHeaders(t *testing.T) {\n\tcases := []struct {\n\t\tinput  []byte\n\t\tdomain string\n\t\terr    bool\n\t}{\n\t\t{\n\t\t\tinput: []byte{\n\t\t\t\t0x16, 0x03, 0x01, 0x00, 0xc8, 0x01, 0x00, 0x00,\n\t\t\t\t0xc4, 0x03, 0x03, 0x1a, 0xac, 0xb2, 0xa8, 0xfe,\n\t\t\t\t0xb4, 0x96, 0x04, 0x5b, 0xca, 0xf7, 0xc1, 0xf4,\n\t\t\t\t0x2e, 0x53, 0x24, 0x6e, 0x34, 0x0c, 0x58, 0x36,\n\t\t\t\t0x71, 0x97, 0x59, 0xe9, 0x41, 0x66, 0xe2, 0x43,\n\t\t\t\t0xa0, 0x13, 0xb6, 0x00, 0x00, 0x20, 0x1a, 0x1a,\n\t\t\t\t0xc0, 0x2b, 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30,\n\t\t\t\t0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0x14, 0xcc, 0x13,\n\t\t\t\t0xc0, 0x13, 0xc0, 0x14, 0x00, 0x9c, 0x00, 0x9d,\n\t\t\t\t0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a, 0x01, 0x00,\n\t\t\t\t0x00, 0x7b, 0xba, 0xba, 0x00, 0x00, 0xff, 0x01,\n\t\t\t\t0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00,\n\t\t\t\t0x14, 0x00, 0x00, 0x11, 0x63, 0x2e, 0x73, 0x2d,\n\t\t\t\t0x6d, 0x69, 0x63, 0x72, 0x6f, 0x73, 0x6f, 0x66,\n\t\t\t\t0x74, 0x2e, 0x63, 0x6f, 0x6d, 0x00, 0x17, 0x00,\n\t\t\t\t0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0d, 0x00,\n\t\t\t\t0x14, 0x00, 0x12, 0x04, 0x03, 0x08, 0x04, 0x04,\n\t\t\t\t0x01, 0x05, 0x03, 0x08, 0x05, 0x05, 0x01, 0x08,\n\t\t\t\t0x06, 0x06, 0x01, 0x02, 0x01, 0x00, 0x05, 0x00,\n\t\t\t\t0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x12,\n\t\t\t\t0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x0c,\n\t\t\t\t0x02, 0x68, 0x32, 0x08, 0x68, 0x74, 0x74, 0x70,\n\t\t\t\t0x2f, 0x31, 0x2e, 0x31, 0x00, 0x0b, 0x00, 0x02,\n\t\t\t\t0x01, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08,\n\t\t\t\t0xaa, 0xaa, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18,\n\t\t\t\t0xaa, 0xaa, 0x00, 0x01, 0x00,\n\t\t\t},\n\t\t\tdomain: \"c.s-microsoft.com\",\n\t\t\terr:    false,\n\t\t},\n\t\t{\n\t\t\tinput: []byte{\n\t\t\t\t0x16, 0x03, 0x01, 0x00, 0xee, 0x01, 0x00, 0x00,\n\t\t\t\t0xea, 0x03, 0x03, 0xe7, 0x91, 0x9e, 0x93, 0xca,\n\t\t\t\t0x78, 0x1b, 0x3c, 0xe0, 0x65, 0x25, 0x58, 0xb5,\n\t\t\t\t0x93, 0xe1, 0x0f, 0x85, 0xec, 0x9a, 0x66, 0x8e,\n\t\t\t\t0x61, 0x82, 0x88, 0xc8, 0xfc, 0xae, 0x1e, 0xca,\n\t\t\t\t0xd7, 0xa5, 0x63, 0x20, 0xbd, 0x1c, 0x00, 0x00,\n\t\t\t\t0x8b, 0xee, 0x09, 0xe3, 0x47, 0x6a, 0x0e, 0x74,\n\t\t\t\t0xb0, 0xbc, 0xa3, 0x02, 0xa7, 0x35, 0xe8, 0x85,\n\t\t\t\t0x70, 0x7c, 0x7a, 0xf0, 0x00, 0xdf, 0x4a, 0xea,\n\t\t\t\t0x87, 0x01, 0x14, 0x91, 0x00, 0x20, 0xea, 0xea,\n\t\t\t\t0xc0, 0x2b, 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30,\n\t\t\t\t0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0x14, 0xcc, 0x13,\n\t\t\t\t0xc0, 0x13, 0xc0, 0x14, 0x00, 0x9c, 0x00, 0x9d,\n\t\t\t\t0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a, 0x01, 0x00,\n\t\t\t\t0x00, 0x81, 0x9a, 0x9a, 0x00, 0x00, 0xff, 0x01,\n\t\t\t\t0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x18, 0x00,\n\t\t\t\t0x16, 0x00, 0x00, 0x13, 0x77, 0x77, 0x77, 0x30,\n\t\t\t\t0x37, 0x2e, 0x63, 0x6c, 0x69, 0x63, 0x6b, 0x74,\n\t\t\t\t0x61, 0x6c, 0x65, 0x2e, 0x6e, 0x65, 0x74, 0x00,\n\t\t\t\t0x17, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,\n\t\t\t\t0x0d, 0x00, 0x14, 0x00, 0x12, 0x04, 0x03, 0x08,\n\t\t\t\t0x04, 0x04, 0x01, 0x05, 0x03, 0x08, 0x05, 0x05,\n\t\t\t\t0x01, 0x08, 0x06, 0x06, 0x01, 0x02, 0x01, 0x00,\n\t\t\t\t0x05, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00,\n\t\t\t\t0x00, 0x12, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0e,\n\t\t\t\t0x00, 0x0c, 0x02, 0x68, 0x32, 0x08, 0x68, 0x74,\n\t\t\t\t0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31, 0x75, 0x50,\n\t\t\t\t0x00, 0x00, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00,\n\t\t\t\t0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x9a, 0x9a,\n\t\t\t\t0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x8a, 0x8a,\n\t\t\t\t0x00, 0x01, 0x00,\n\t\t\t},\n\t\t\tdomain: \"www07.clicktale.net\",\n\t\t\terr:    false,\n\t\t},\n\t\t{\n\t\t\tinput: []byte{\n\t\t\t\t0x16, 0x03, 0x01, 0x00, 0xe6, 0x01, 0x00, 0x00, 0xe2, 0x03, 0x03, 0x81, 0x47, 0xc1,\n\t\t\t\t0x66, 0xd5, 0x1b, 0xfa, 0x4b, 0xb5, 0xe0, 0x2a, 0xe1, 0xa7, 0x87, 0x13, 0x1d, 0x11, 0xaa, 0xc6,\n\t\t\t\t0xce, 0xfc, 0x7f, 0xab, 0x94, 0xc8, 0x62, 0xad, 0xc8, 0xab, 0x0c, 0xdd, 0xcb, 0x20, 0x6f, 0x9d,\n\t\t\t\t0x07, 0xf1, 0x95, 0x3e, 0x99, 0xd8, 0xf3, 0x6d, 0x97, 0xee, 0x19, 0x0b, 0x06, 0x1b, 0xf4, 0x84,\n\t\t\t\t0x0b, 0xb6, 0x8f, 0xcc, 0xde, 0xe2, 0xd0, 0x2d, 0x6b, 0x0c, 0x1f, 0x52, 0x53, 0x13, 0x00, 0x08,\n\t\t\t\t0x13, 0x02, 0x13, 0x03, 0x13, 0x01, 0x00, 0xff, 0x01, 0x00, 0x00, 0x91, 0x00, 0x00, 0x00, 0x0c,\n\t\t\t\t0x00, 0x0a, 0x00, 0x00, 0x07, 0x64, 0x6f, 0x67, 0x66, 0x69, 0x73, 0x68, 0x00, 0x0b, 0x00, 0x04,\n\t\t\t\t0x03, 0x00, 0x01, 0x02, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x0a, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x1e,\n\t\t\t\t0x00, 0x19, 0x00, 0x18, 0x00, 0x23, 0x00, 0x00, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00,\n\t\t\t\t0x00, 0x0d, 0x00, 0x1e, 0x00, 0x1c, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x08, 0x07, 0x08, 0x08,\n\t\t\t\t0x08, 0x09, 0x08, 0x0a, 0x08, 0x0b, 0x08, 0x04, 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01,\n\t\t\t\t0x06, 0x01, 0x00, 0x2b, 0x00, 0x07, 0x06, 0x7f, 0x1c, 0x7f, 0x1b, 0x7f, 0x1a, 0x00, 0x2d, 0x00,\n\t\t\t\t0x02, 0x01, 0x01, 0x00, 0x33, 0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20, 0x2f, 0x35, 0x0c,\n\t\t\t\t0xb6, 0x90, 0x0a, 0xb7, 0xd5, 0xc4, 0x1b, 0x2f, 0x60, 0xaa, 0x56, 0x7b, 0x3f, 0x71, 0xc8, 0x01,\n\t\t\t\t0x7e, 0x86, 0xd3, 0xb7, 0x0c, 0x29, 0x1a, 0x9e, 0x5b, 0x38, 0x3f, 0x01, 0x72,\n\t\t\t},\n\t\t\tdomain: \"dogfish\",\n\t\t\terr:    false,\n\t\t},\n\t\t{\n\t\t\tinput: []byte{\n\t\t\t\t0x16, 0x03, 0x01, 0x01, 0x03, 0x01, 0x00, 0x00,\n\t\t\t\t0xff, 0x03, 0x03, 0x3d, 0x89, 0x52, 0x9e, 0xee,\n\t\t\t\t0xbe, 0x17, 0x63, 0x75, 0xef, 0x29, 0xbd, 0x14,\n\t\t\t\t0x6a, 0x49, 0xe0, 0x2c, 0x37, 0x57, 0x71, 0x62,\n\t\t\t\t0x82, 0x44, 0x94, 0x8f, 0x6e, 0x94, 0x08, 0x45,\n\t\t\t\t0x7f, 0xdb, 0xc1, 0x00, 0x00, 0x3e, 0xc0, 0x2c,\n\t\t\t\t0xc0, 0x30, 0x00, 0x9f, 0xcc, 0xa9, 0xcc, 0xa8,\n\t\t\t\t0xcc, 0xaa, 0xc0, 0x2b, 0xc0, 0x2f, 0x00, 0x9e,\n\t\t\t\t0xc0, 0x24, 0xc0, 0x28, 0x00, 0x6b, 0xc0, 0x23,\n\t\t\t\t0xc0, 0x27, 0x00, 0x67, 0xc0, 0x0a, 0xc0, 0x14,\n\t\t\t\t0x00, 0x39, 0xc0, 0x09, 0xc0, 0x13, 0x00, 0x33,\n\t\t\t\t0x00, 0x9d, 0x00, 0x9c, 0x13, 0x02, 0x13, 0x03,\n\t\t\t\t0x13, 0x01, 0x00, 0x3d, 0x00, 0x3c, 0x00, 0x35,\n\t\t\t\t0x00, 0x2f, 0x00, 0xff, 0x01, 0x00, 0x00, 0x98,\n\t\t\t\t0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x00,\n\t\t\t\t0x0b, 0x31, 0x30, 0x2e, 0x34, 0x32, 0x2e, 0x30,\n\t\t\t\t0x2e, 0x32, 0x34, 0x33, 0x00, 0x0b, 0x00, 0x04,\n\t\t\t\t0x03, 0x00, 0x01, 0x02, 0x00, 0x0a, 0x00, 0x0a,\n\t\t\t\t0x00, 0x08, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x19,\n\t\t\t\t0x00, 0x18, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0d,\n\t\t\t\t0x00, 0x20, 0x00, 0x1e, 0x04, 0x03, 0x05, 0x03,\n\t\t\t\t0x06, 0x03, 0x08, 0x04, 0x08, 0x05, 0x08, 0x06,\n\t\t\t\t0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x02, 0x03,\n\t\t\t\t0x02, 0x01, 0x02, 0x02, 0x04, 0x02, 0x05, 0x02,\n\t\t\t\t0x06, 0x02, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17,\n\t\t\t\t0x00, 0x00, 0x00, 0x2b, 0x00, 0x09, 0x08, 0x7f,\n\t\t\t\t0x14, 0x03, 0x03, 0x03, 0x02, 0x03, 0x01, 0x00,\n\t\t\t\t0x2d, 0x00, 0x03, 0x02, 0x01, 0x00, 0x00, 0x28,\n\t\t\t\t0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, 0x20,\n\t\t\t\t0x13, 0x7c, 0x6e, 0x97, 0xc4, 0xfd, 0x09, 0x2e,\n\t\t\t\t0x70, 0x2f, 0x73, 0x5a, 0x9b, 0x57, 0x4d, 0x5f,\n\t\t\t\t0x2b, 0x73, 0x2c, 0xa5, 0x4a, 0x98, 0x40, 0x3d,\n\t\t\t\t0x75, 0x6e, 0xb4, 0x76, 0xf9, 0x48, 0x8f, 0x36,\n\t\t\t},\n\t\t\tdomain: \"10.42.0.243\",\n\t\t\terr:    false,\n\t\t},\n\t}\n\n\tfor _, test := range cases {\n\t\tinput := bytes.Clone(test.input)\n\t\tdomain, err := SniffTLS(test.input)\n\t\tif test.err {\n\t\t\tif err == nil {\n\t\t\t\tt.Errorf(\"Exepct error but nil in test %v\", test)\n\t\t\t}\n\t\t} else {\n\t\t\tif err != nil {\n\t\t\t\tt.Errorf(\"Expect no error but actually %s in test %v\", err.Error(), test)\n\t\t\t}\n\t\t\tif *domain != test.domain {\n\t\t\t\tt.Error(\"expect domain \", test.domain, \" but got \", domain)\n\t\t\t}\n\t\t}\n\t\tassert.Equal(t, input, test.input)\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/sniffer/tls_sniffer.go",
    "content": "package sniffer\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/common/utils\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/constant/sniffer\"\n)\n\nvar (\n\terrNotTLS         = errors.New(\"not TLS header\")\n\terrNotClientHello = errors.New(\"not client hello\")\n)\n\ntype errNeedAtLeastData struct {\n\tlength int\n\terr    error\n}\n\nfunc (e *errNeedAtLeastData) Error() string {\n\treturn fmt.Sprintf(\"%v, need at least length: %d\", e.err, e.length)\n}\n\nfunc (e *errNeedAtLeastData) Unwrap() error {\n\treturn e.err\n}\n\nvar _ sniffer.Sniffer = (*TLSSniffer)(nil)\n\ntype TLSSniffer struct {\n\t*BaseSniffer\n}\n\nfunc NewTLSSniffer(snifferConfig SnifferConfig) (*TLSSniffer, error) {\n\tports := snifferConfig.Ports\n\tif len(ports) == 0 {\n\t\tports = utils.IntRanges[uint16]{utils.NewRange[uint16](443, 443)}\n\t}\n\treturn &TLSSniffer{\n\t\tBaseSniffer: NewBaseSniffer(ports, C.TCP),\n\t}, nil\n}\n\nfunc (tls *TLSSniffer) Protocol() string {\n\treturn \"tls\"\n}\n\nfunc (tls *TLSSniffer) SupportNetwork() C.NetWork {\n\treturn C.TCP\n}\n\nfunc (tls *TLSSniffer) SniffData(bytes []byte) (string, error) {\n\tdomain, err := SniffTLS(bytes)\n\tif err == nil {\n\t\treturn *domain, nil\n\t} else {\n\t\treturn \"\", err\n\t}\n}\n\nfunc IsValidTLSVersion(major, minor byte) bool {\n\treturn major == 3\n}\n\n// ReadClientHello returns server name (if any) from TLS client hello message.\n// https://github.com/golang/go/blob/master/src/crypto/tls/handshake_messages.go#L300\nfunc ReadClientHello(data []byte) (*string, error) {\n\tif len(data) < 42 {\n\t\treturn nil, ErrNoClue\n\t}\n\tsessionIDLen := int(data[38])\n\tif sessionIDLen > 32 || len(data) < 39+sessionIDLen {\n\t\treturn nil, ErrNoClue\n\t}\n\tdata = data[39+sessionIDLen:]\n\tif len(data) < 2 {\n\t\treturn nil, ErrNoClue\n\t}\n\t// cipherSuiteLen is the number of bytes of cipher suite numbers. Since\n\t// they are uint16s, the number must be even.\n\tcipherSuiteLen := int(data[0])<<8 | int(data[1])\n\tif cipherSuiteLen%2 == 1 || len(data) < 2+cipherSuiteLen {\n\t\treturn nil, errNotClientHello\n\t}\n\tdata = data[2+cipherSuiteLen:]\n\tif len(data) < 1 {\n\t\treturn nil, ErrNoClue\n\t}\n\tcompressionMethodsLen := int(data[0])\n\tif len(data) < 1+compressionMethodsLen {\n\t\treturn nil, ErrNoClue\n\t}\n\tdata = data[1+compressionMethodsLen:]\n\n\tif len(data) == 0 {\n\t\treturn nil, errNotClientHello\n\t}\n\tif len(data) < 2 {\n\t\treturn nil, errNotClientHello\n\t}\n\n\textensionsLength := int(data[0])<<8 | int(data[1])\n\tdata = data[2:]\n\tif extensionsLength != len(data) {\n\t\treturn nil, errNotClientHello\n\t}\n\n\tfor len(data) != 0 {\n\t\tif len(data) < 4 {\n\t\t\treturn nil, errNotClientHello\n\t\t}\n\t\textension := uint16(data[0])<<8 | uint16(data[1])\n\t\tlength := int(data[2])<<8 | int(data[3])\n\t\tdata = data[4:]\n\t\tif len(data) < length {\n\t\t\treturn nil, errNotClientHello\n\t\t}\n\n\t\tif extension == 0x00 { /* extensionServerName */\n\t\t\td := data[:length]\n\t\t\tif len(d) < 2 {\n\t\t\t\treturn nil, errNotClientHello\n\t\t\t}\n\t\t\tnamesLen := int(d[0])<<8 | int(d[1])\n\t\t\td = d[2:]\n\t\t\tif len(d) != namesLen {\n\t\t\t\treturn nil, errNotClientHello\n\t\t\t}\n\t\t\tfor len(d) > 0 {\n\t\t\t\tif len(d) < 3 {\n\t\t\t\t\treturn nil, errNotClientHello\n\t\t\t\t}\n\t\t\t\tnameType := d[0]\n\t\t\t\tnameLen := int(d[1])<<8 | int(d[2])\n\t\t\t\td = d[3:]\n\t\t\t\tif len(d) < nameLen {\n\t\t\t\t\treturn nil, errNotClientHello\n\t\t\t\t}\n\t\t\t\tif nameType == 0 {\n\t\t\t\t\tserverName := string(d[:nameLen])\n\t\t\t\t\t// An SNI value may not include a\n\t\t\t\t\t// trailing dot. See\n\t\t\t\t\t// https://tools.ietf.org/html/rfc6066#section-3.\n\t\t\t\t\tif strings.HasSuffix(serverName, \".\") {\n\t\t\t\t\t\treturn nil, errNotClientHello\n\t\t\t\t\t}\n\n\t\t\t\t\treturn &serverName, nil\n\t\t\t\t}\n\n\t\t\t\td = d[nameLen:]\n\t\t\t}\n\t\t}\n\t\tdata = data[length:]\n\t}\n\n\treturn nil, errNotTLS\n}\n\nfunc SniffTLS(b []byte) (*string, error) {\n\tif len(b) < 5 {\n\t\treturn nil, ErrNoClue\n\t}\n\n\tif b[0] != 0x16 /* TLS Handshake */ {\n\t\treturn nil, errNotTLS\n\t}\n\tif !IsValidTLSVersion(b[1], b[2]) {\n\t\treturn nil, errNotTLS\n\t}\n\theaderLen := int(binary.BigEndian.Uint16(b[3:5]))\n\tif 5+headerLen > len(b) {\n\t\treturn nil, &errNeedAtLeastData{\n\t\t\tlength: 5 + headerLen,\n\t\t\terr:    ErrNoClue,\n\t\t}\n\t}\n\n\tdomain, err := ReadClientHello(b[5 : 5+headerLen])\n\tif err == nil {\n\t\treturn domain, nil\n\t}\n\treturn nil, err\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/tls/httpserver.go",
    "content": "package tls\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"runtime/debug\"\n\t\"time\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/metacubex/http\"\n)\n\nfunc extractTlsHandshakeTimeoutFromServer(s *http.Server) time.Duration {\n\tvar ret time.Duration\n\tfor _, v := range [...]time.Duration{\n\t\ts.ReadHeaderTimeout,\n\t\ts.ReadTimeout,\n\t\ts.WriteTimeout,\n\t} {\n\t\tif v <= 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif ret == 0 || v < ret {\n\t\t\tret = v\n\t\t}\n\t}\n\treturn ret\n}\n\n// NewListenerForHttps returns a net.Listener for (*http.Server).Serve()\n// the \"func (c *conn) serve(ctx context.Context)\" in http\\server.go\n// only do tls handshake and check NegotiatedProtocol with std's *tls.Conn\n// so we do the same logic to let http2 (not h2c) work fine\nfunc NewListenerForHttps(l net.Listener, httpServer *http.Server, tlsConfig *Config) net.Listener {\n\thttp2Server := &http.Http2Server{}\n\t_ = http.Http2ConfigureServer(httpServer, http2Server)\n\treturn N.NewHandleContextListener(context.Background(), l, func(ctx context.Context, conn net.Conn) (net.Conn, error) {\n\t\tc := Server(conn, tlsConfig)\n\n\t\ttlsTO := extractTlsHandshakeTimeoutFromServer(httpServer)\n\t\tif tlsTO > 0 {\n\t\t\tdl := time.Now().Add(tlsTO)\n\t\t\t_ = conn.SetReadDeadline(dl)\n\t\t\t_ = conn.SetWriteDeadline(dl)\n\t\t}\n\n\t\terr := c.HandshakeContext(ctx)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\t// Restore Conn-level deadlines.\n\t\tif tlsTO > 0 {\n\t\t\t_ = conn.SetReadDeadline(time.Time{})\n\t\t\t_ = conn.SetWriteDeadline(time.Time{})\n\t\t}\n\n\t\tif c.ConnectionState().NegotiatedProtocol == http.Http2NextProtoTLS {\n\t\t\thttp2Server.ServeConn(c, &http.Http2ServeConnOpts{BaseConfig: httpServer})\n\t\t\treturn nil, net.ErrClosed\n\t\t}\n\t\treturn c, nil\n\t}, func(a any) {\n\t\tstack := debug.Stack()\n\t\tlog.Errorln(\"https server panic: %s\\n%s\", a, stack)\n\t})\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/tls/reality.go",
    "content": "package tls\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\t\"crypto/ecdh\"\n\t\"crypto/ed25519\"\n\t\"crypto/hmac\"\n\t\"crypto/sha256\"\n\t\"crypto/sha512\"\n\t\"crypto/x509\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"net\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/ntp\"\n\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/randv2\"\n\tutls \"github.com/metacubex/utls\"\n\t\"golang.org/x/crypto/hkdf\"\n)\n\nconst RealityMaxShortIDLen = 8\n\ntype RealityConfig struct {\n\tPublicKey *ecdh.PublicKey\n\tShortID   [RealityMaxShortIDLen]byte\n\n\tSupportX25519MLKEM768 bool\n}\n\nfunc GetRealityConn(ctx context.Context, conn net.Conn, fingerprint UClientHelloID, serverName string, realityConfig *RealityConfig) (net.Conn, error) {\n\tfor retry := 0; ; retry++ {\n\t\tverifier := &realityVerifier{\n\t\t\tserverName: serverName,\n\t\t}\n\t\tuConfig := &utls.Config{\n\t\t\tTime:                   ntp.Now,\n\t\t\tServerName:             serverName,\n\t\t\tInsecureSkipVerify:     true,\n\t\t\tSessionTicketsDisabled: true,\n\t\t\tVerifyConnection:       verifier.VerifyConnection,\n\t\t}\n\n\t\tuConn := utls.UClient(conn, uConfig, fingerprint)\n\t\tverifier.UConn = uConn\n\t\terr := uConn.BuildHandshakeState()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif !realityConfig.SupportX25519MLKEM768 { // for X25519MLKEM768 does not work properly with the old reality server\n\t\t\terr = BuildRemovedX25519MLKEM768HandshakeState(uConn)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\n\t\thello := uConn.HandshakeState.Hello\n\t\trawSessionID := hello.Raw[39 : 39+32] // the location of session ID\n\t\tfor i := range rawSessionID {         // https://github.com/golang/go/issues/5373\n\t\t\trawSessionID[i] = 0\n\t\t}\n\n\t\tbinary.BigEndian.PutUint64(hello.SessionId, uint64(ntp.Now().Unix()))\n\n\t\tcopy(hello.SessionId[8:], realityConfig.ShortID[:])\n\t\thello.SessionId[0] = 1\n\t\thello.SessionId[1] = 8\n\t\thello.SessionId[2] = 2\n\n\t\t//log.Debugln(\"REALITY hello.sessionId[:16]: %v\", hello.SessionId[:16])\n\n\t\tkeyShareKeys := uConn.HandshakeState.State13.KeyShareKeys\n\t\tif keyShareKeys == nil {\n\t\t\t// WTF???\n\t\t\tif retry > 2 {\n\t\t\t\treturn nil, errors.New(\"nil keyShareKeys\")\n\t\t\t}\n\t\t\tcontinue // retry\n\t\t}\n\t\tecdheKey := keyShareKeys.Ecdhe\n\t\tif ecdheKey == nil {\n\t\t\tecdheKey = keyShareKeys.MlkemEcdhe\n\t\t}\n\t\tif ecdheKey == nil {\n\t\t\t// WTF???\n\t\t\tif retry > 2 {\n\t\t\t\treturn nil, errors.New(\"nil ecdheKey\")\n\t\t\t}\n\t\t\tcontinue // retry\n\t\t}\n\t\tauthKey, err := ecdheKey.ECDH(realityConfig.PublicKey)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif authKey == nil {\n\t\t\treturn nil, errors.New(\"nil auth_key\")\n\t\t}\n\t\tverifier.authKey = authKey\n\t\t_, err = hkdf.New(sha256.New, authKey, hello.Random[:20], []byte(\"REALITY\")).Read(authKey)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\taesBlock, _ := aes.NewCipher(authKey)\n\t\taeadCipher, _ := cipher.NewGCM(aesBlock)\n\t\taeadCipher.Seal(hello.SessionId[:0], hello.Random[20:], hello.SessionId[:16], hello.Raw)\n\t\tcopy(hello.Raw[39:], hello.SessionId)\n\t\t//log.Debugln(\"REALITY hello.sessionId: %v\", hello.SessionId)\n\t\t//log.Debugln(\"REALITY uConn.AuthKey: %v\", authKey)\n\n\t\terr = uConn.HandshakeContext(ctx)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tlog.Debugln(\"REALITY Authentication: %v, AEAD: %T\", verifier.verified, aeadCipher)\n\n\t\tif !verifier.verified {\n\t\t\tgo realityClientFallback(uConn, uConfig.ServerName, fingerprint)\n\t\t\treturn nil, errors.New(\"REALITY authentication failed\")\n\t\t}\n\n\t\treturn uConn, nil\n\t}\n}\n\nfunc realityClientFallback(uConn net.Conn, serverName string, fingerprint utls.ClientHelloID) {\n\tdefer uConn.Close()\n\t// use h2c mode to disallow the net/http fallback to http1.1\n\tprotocols := new(http.Protocols)\n\tprotocols.SetUnencryptedHTTP2(true)\n\tclient := http.Client{\n\t\tTransport: &http.Transport{\n\t\t\tDialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\t\t\treturn uConn, nil\n\t\t\t},\n\t\t\tProtocols: protocols,\n\t\t},\n\t}\n\trequest, err := http.NewRequest(\"GET\", \"https://\"+serverName, nil)\n\tif err != nil {\n\t\treturn\n\t}\n\trequest.Header.Set(\"User-Agent\", fingerprint.Client)\n\trequest.AddCookie(&http.Cookie{Name: \"padding\", Value: strings.Repeat(\"0\", randv2.IntN(32)+30)})\n\tresponse, err := client.Do(request)\n\tif err != nil {\n\t\treturn\n\t}\n\t//_, _ = io.Copy(io.Discard, response.Body)\n\ttime.Sleep(time.Duration(5+randv2.IntN(10)) * time.Second)\n\tresponse.Body.Close()\n\tclient.CloseIdleConnections()\n}\n\ntype realityVerifier struct {\n\t*utls.UConn\n\tserverName string\n\tauthKey    []byte\n\tverified   bool\n}\n\nfunc (c *realityVerifier) VerifyConnection(state utls.ConnectionState) error {\n\tlog.Debugln(\"REALITY localAddr: %v is using X25519MLKEM768 for TLS' communication: %v\", c.RemoteAddr(), c.HandshakeState.ServerHello.ServerShare.Group == utls.X25519MLKEM768)\n\tcerts := state.PeerCertificates\n\tif pub, ok := certs[0].PublicKey.(ed25519.PublicKey); ok {\n\t\th := hmac.New(sha512.New, c.authKey)\n\t\th.Write(pub)\n\t\tif bytes.Equal(h.Sum(nil), certs[0].Signature) {\n\t\t\tc.verified = true\n\t\t\treturn nil\n\t\t}\n\t}\n\topts := x509.VerifyOptions{\n\t\tDNSName:       c.serverName,\n\t\tIntermediates: x509.NewCertPool(),\n\t\tCurrentTime:   ntp.Now(),\n\t}\n\tfor _, cert := range certs[1:] {\n\t\topts.Intermediates.AddCert(cert)\n\t}\n\tif _, err := certs[0].Verify(opts); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/tls/utls.go",
    "content": "package tls\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"reflect\"\n\t\"unsafe\"\n\n\t\"github.com/metacubex/mihomo/common/once\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/metacubex/tls\"\n\tutls \"github.com/metacubex/utls\"\n\t\"github.com/mroth/weightedrand/v2\"\n\t\"golang.org/x/exp/slices\"\n)\n\ntype Conn = utls.Conn\ntype UConn = utls.UConn\ntype UClientHelloID = utls.ClientHelloID\n\nconst VersionTLS12 = utls.VersionTLS12\nconst VersionTLS13 = utls.VersionTLS13\n\nfunc Client(c net.Conn, config *utls.Config) *Conn {\n\treturn utls.Client(c, config)\n}\n\nfunc UClient(c net.Conn, config *utls.Config, fingerprint UClientHelloID) *UConn {\n\treturn utls.UClient(c, config, fingerprint)\n}\n\nfunc Server(c net.Conn, config *utls.Config) *Conn {\n\treturn utls.Server(c, config)\n}\n\nfunc NewListener(inner net.Listener, config *Config) net.Listener {\n\treturn utls.NewListener(inner, config)\n}\n\nfunc GetFingerprint(clientFingerprint string) (UClientHelloID, bool) {\n\tif len(clientFingerprint) == 0 {\n\t\tclientFingerprint = globalFingerprint\n\t}\n\tif len(clientFingerprint) == 0 || clientFingerprint == \"none\" {\n\t\treturn UClientHelloID{}, false\n\t}\n\n\tif clientFingerprint == \"random\" {\n\t\tfingerprint := randomFingerprint()\n\t\tlog.Debugln(\"use initial random HelloID:%s\", fingerprint.Client)\n\t\treturn fingerprint, true\n\t}\n\n\tif fingerprint, ok := fingerprints[clientFingerprint]; ok {\n\t\tlog.Debugln(\"use specified fingerprint:%s\", fingerprint.Client)\n\t\treturn fingerprint, true\n\t} else {\n\t\tlog.Warnln(\"wrong clientFingerprint:%s\", clientFingerprint)\n\t\treturn UClientHelloID{}, false\n\t}\n}\n\nvar randomFingerprint = once.OnceValue(func() UClientHelloID {\n\tchooser, _ := weightedrand.NewChooser(\n\t\tweightedrand.NewChoice(\"chrome\", 6),\n\t\tweightedrand.NewChoice(\"safari\", 3),\n\t\tweightedrand.NewChoice(\"ios\", 2),\n\t\tweightedrand.NewChoice(\"firefox\", 1),\n\t)\n\tinitClient := chooser.Pick()\n\tlog.Debugln(\"initial random HelloID:%s\", initClient)\n\tfingerprint, ok := fingerprints[initClient]\n\tif !ok {\n\t\tlog.Warnln(\"error in initial random HelloID:%s\", initClient)\n\t}\n\treturn fingerprint\n})\n\nvar fingerprints = map[string]UClientHelloID{\n\t\"chrome\":  utls.HelloChrome_Auto,\n\t\"firefox\": utls.HelloFirefox_Auto,\n\t\"safari\":  utls.HelloSafari_Auto,\n\t\"ios\":     utls.HelloIOS_Auto,\n\t\"android\": utls.HelloAndroid_11_OkHttp,\n\t\"edge\":    utls.HelloEdge_Auto,\n\t\"360\":     utls.Hello360_Auto,\n\t\"qq\":      utls.HelloQQ_Auto,\n\t\"random\":  {},\n\n\t// classical fingerprints without X25519MLKEM768\n\t\"chrome120\":  utls.HelloChrome_120,\n\t\"firefox120\": utls.HelloFirefox_120,\n\t\"safari16\":   utls.HelloSafari_16_0,\n\n\t// deprecated fingerprints should not be used\n\t\"chrome_psk\":                 utls.HelloChrome_100_PSK,\n\t\"chrome_psk_shuffle\":         utls.HelloChrome_106_Shuffle,\n\t\"chrome_padding_psk_shuffle\": utls.HelloChrome_114_Padding_PSK_Shuf,\n\t\"chrome_pq\":                  utls.HelloChrome_115_PQ,\n\t\"chrome_pq_psk\":              utls.HelloChrome_115_PQ_PSK,\n\t\"randomized\":                 utls.HelloRandomized,\n}\n\nfunc init() {\n\tweights := utls.DefaultWeights\n\tweights.TLSVersMax_Set_VersionTLS13 = 1\n\tweights.FirstKeyShare_Set_CurveP256 = 0\n\trandomized := utls.HelloRandomized\n\trandomized.Seed, _ = utls.NewPRNGSeed()\n\trandomized.Weights = &weights\n\tfingerprints[\"randomized\"] = randomized\n}\n\ntype Certificate = utls.Certificate\n\nfunc UCertificate(it tls.Certificate) utls.Certificate {\n\treturn utls.Certificate{\n\t\tCertificate: it.Certificate,\n\t\tPrivateKey:  it.PrivateKey,\n\t\tSupportedSignatureAlgorithms: utils.Map(it.SupportedSignatureAlgorithms, func(it tls.SignatureScheme) utls.SignatureScheme {\n\t\t\treturn utls.SignatureScheme(it)\n\t\t}),\n\t\tOCSPStaple:                  it.OCSPStaple,\n\t\tSignedCertificateTimestamps: it.SignedCertificateTimestamps,\n\t\tLeaf:                        it.Leaf,\n\t}\n}\n\ntype EncryptedClientHelloKey = utls.EncryptedClientHelloKey\n\nfunc UEncryptedClientHelloKey(it tls.EncryptedClientHelloKey) utls.EncryptedClientHelloKey {\n\treturn utls.EncryptedClientHelloKey{\n\t\tConfig:      it.Config,\n\t\tPrivateKey:  it.PrivateKey,\n\t\tSendAsRetry: it.SendAsRetry,\n\t}\n}\n\ntype ConnectionState = utls.ConnectionState\n\ntype Config = utls.Config\n\nvar tlsCertificateRequestInfoCtxOffset = utils.MustOK(reflect.TypeOf((*tls.CertificateRequestInfo)(nil)).Elem().FieldByName(\"ctx\")).Offset\nvar tlsClientHelloInfoCtxOffset = utils.MustOK(reflect.TypeOf((*tls.ClientHelloInfo)(nil)).Elem().FieldByName(\"ctx\")).Offset\nvar tlsConnectionStateEkmOffset = utils.MustOK(reflect.TypeOf((*tls.ConnectionState)(nil)).Elem().FieldByName(\"ekm\")).Offset\nvar utlsConnectionStateEkmOffset = utils.MustOK(reflect.TypeOf((*utls.ConnectionState)(nil)).Elem().FieldByName(\"ekm\")).Offset\n\nfunc tlsConnectionState(state utls.ConnectionState) (tlsState tls.ConnectionState) {\n\ttlsState = tls.ConnectionState{\n\t\tVersion:           state.Version,\n\t\tHandshakeComplete: state.HandshakeComplete,\n\t\tDidResume:         state.DidResume,\n\t\tCipherSuite:       state.CipherSuite,\n\t\t//CurveID:                     state.CurveID,\n\t\tNegotiatedProtocol:          state.NegotiatedProtocol,\n\t\tNegotiatedProtocolIsMutual:  state.NegotiatedProtocolIsMutual,\n\t\tServerName:                  state.ServerName,\n\t\tPeerCertificates:            state.PeerCertificates,\n\t\tVerifiedChains:              state.VerifiedChains,\n\t\tSignedCertificateTimestamps: state.SignedCertificateTimestamps,\n\t\tOCSPResponse:                state.OCSPResponse,\n\t\tTLSUnique:                   state.TLSUnique,\n\t\tECHAccepted:                 state.ECHAccepted,\n\t\t//HelloRetryRequest:           state.HelloRetryRequest,\n\t}\n\t// The layout of map, chan, and func types is equivalent to *T.\n\t// state.ekm is a func(label string, context []byte, length int) ([]byte, error)\n\t*(*unsafe.Pointer)(unsafe.Add(unsafe.Pointer(&tlsState), tlsConnectionStateEkmOffset)) =\n\t\t*(*unsafe.Pointer)(unsafe.Add(unsafe.Pointer(&state), utlsConnectionStateEkmOffset))\n\treturn\n}\n\nfunc UConfig(config *tls.Config) *utls.Config {\n\tcfg := &utls.Config{\n\t\tRand:                  config.Rand,\n\t\tTime:                  config.Time,\n\t\tCertificates:          utils.Map(config.Certificates, UCertificate),\n\t\tVerifyPeerCertificate: config.VerifyPeerCertificate,\n\t\tRootCAs:               config.RootCAs,\n\t\tNextProtos:            config.NextProtos,\n\t\tServerName:            config.ServerName,\n\t\tClientAuth:            utls.ClientAuthType(config.ClientAuth),\n\t\tClientCAs:             config.ClientCAs,\n\t\tInsecureSkipVerify:    config.InsecureSkipVerify,\n\t\tCipherSuites:          config.CipherSuites,\n\t\tMinVersion:            config.MinVersion,\n\t\tMaxVersion:            config.MaxVersion,\n\t\tCurvePreferences: utils.Map(config.CurvePreferences, func(it tls.CurveID) utls.CurveID {\n\t\t\treturn utls.CurveID(it)\n\t\t}),\n\t\tSessionTicketsDisabled: config.SessionTicketsDisabled,\n\t\tRenegotiation:          utls.RenegotiationSupport(config.Renegotiation),\n\t\tKeyLogWriter:           config.KeyLogWriter,\n\t}\n\tif config.GetClientCertificate != nil {\n\t\tcfg.GetClientCertificate = func(info *utls.CertificateRequestInfo) (*utls.Certificate, error) {\n\t\t\ttlsInfo := &tls.CertificateRequestInfo{\n\t\t\t\tAcceptableCAs: info.AcceptableCAs,\n\t\t\t\tSignatureSchemes: utils.Map(info.SignatureSchemes, func(it utls.SignatureScheme) tls.SignatureScheme {\n\t\t\t\t\treturn tls.SignatureScheme(it)\n\t\t\t\t}),\n\t\t\t\tVersion: info.Version,\n\t\t\t}\n\t\t\t*(*context.Context)(unsafe.Add(unsafe.Pointer(tlsInfo), tlsCertificateRequestInfoCtxOffset)) = info.Context() // for tlsInfo.ctx\n\t\t\tcert, err := config.GetClientCertificate(tlsInfo)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tuCert := UCertificate(*cert)\n\t\t\treturn &uCert, err\n\t\t}\n\t}\n\tif config.GetCertificate != nil {\n\t\tcfg.GetCertificate = func(info *utls.ClientHelloInfo) (*utls.Certificate, error) {\n\t\t\ttlsInfo := &tls.ClientHelloInfo{\n\t\t\t\tCipherSuites: info.CipherSuites,\n\t\t\t\tServerName:   info.ServerName,\n\t\t\t\tSupportedCurves: utils.Map(info.SupportedCurves, func(it utls.CurveID) tls.CurveID {\n\t\t\t\t\treturn tls.CurveID(it)\n\t\t\t\t}),\n\t\t\t\tSupportedPoints: info.SupportedPoints,\n\t\t\t\tSignatureSchemes: utils.Map(info.SignatureSchemes, func(it utls.SignatureScheme) tls.SignatureScheme {\n\t\t\t\t\treturn tls.SignatureScheme(it)\n\t\t\t\t}),\n\t\t\t\tSupportedProtos:   info.SupportedProtos,\n\t\t\t\tSupportedVersions: info.SupportedVersions,\n\t\t\t\tExtensions:        info.Extensions,\n\t\t\t\tConn:              info.Conn,\n\t\t\t\t//HelloRetryRequest: info.HelloRetryRequest,\n\t\t\t}\n\t\t\t*(*context.Context)(unsafe.Add(unsafe.Pointer(tlsInfo), tlsClientHelloInfoCtxOffset)) = info.Context() // for tlsInfo.ctx\n\t\t\tcert, err := config.GetCertificate(tlsInfo)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tuCert := UCertificate(*cert)\n\t\t\treturn &uCert, err\n\t\t}\n\t}\n\tif config.VerifyConnection != nil {\n\t\tcfg.VerifyConnection = func(state utls.ConnectionState) error {\n\t\t\treturn config.VerifyConnection(tlsConnectionState(state))\n\t\t}\n\t}\n\tconfig.EncryptedClientHelloConfigList = cfg.EncryptedClientHelloConfigList\n\tif config.EncryptedClientHelloRejectionVerify != nil {\n\t\tcfg.EncryptedClientHelloRejectionVerify = func(state utls.ConnectionState) error {\n\t\t\treturn config.EncryptedClientHelloRejectionVerify(tlsConnectionState(state))\n\t\t}\n\t}\n\t//cfg.GetEncryptedClientHelloKeys =\n\tcfg.EncryptedClientHelloKeys = utils.Map(config.EncryptedClientHelloKeys, UEncryptedClientHelloKey)\n\treturn cfg\n}\n\n// BuildWebsocketHandshakeState it will only send http/1.1 in its ALPN.\n// Copy from https://github.com/XTLS/Xray-core/blob/main/transport/internet/tls/tls.go\nfunc BuildWebsocketHandshakeState(c *UConn) error {\n\t// Build the handshake state. This will apply every variable of the TLS of the\n\t// fingerprint in the UConn\n\tif err := c.BuildHandshakeState(); err != nil {\n\t\treturn err\n\t}\n\t// Iterate over extensions and check for utls.ALPNExtension\n\thasALPNExtension := false\n\tfor _, extension := range c.Extensions {\n\t\tif alpn, ok := extension.(*utls.ALPNExtension); ok {\n\t\t\thasALPNExtension = true\n\t\t\talpn.AlpnProtocols = []string{\"http/1.1\"}\n\t\t\tbreak\n\t\t}\n\t}\n\tif !hasALPNExtension { // Append extension if doesn't exists\n\t\tc.Extensions = append(c.Extensions, &utls.ALPNExtension{AlpnProtocols: []string{\"http/1.1\"}})\n\t}\n\t// Rebuild the client hello\n\tif err := c.BuildHandshakeState(); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc BuildRemovedX25519MLKEM768HandshakeState(c *UConn) error {\n\t// Build the handshake state. This will apply every variable of the TLS of the\n\t// fingerprint in the UConn\n\tif err := c.BuildHandshakeState(); err != nil {\n\t\treturn err\n\t}\n\t// Iterate over extensions and check\n\tfor _, extension := range c.Extensions {\n\t\tif ce, ok := extension.(*utls.SupportedCurvesExtension); ok {\n\t\t\tce.Curves = slices.DeleteFunc(ce.Curves, func(curveID utls.CurveID) bool {\n\t\t\t\treturn curveID == utls.X25519MLKEM768\n\t\t\t})\n\t\t}\n\t\tif ks, ok := extension.(*utls.KeyShareExtension); ok {\n\t\t\tks.KeyShares = slices.DeleteFunc(ks.KeyShares, func(share utls.KeyShare) bool {\n\t\t\t\treturn share.Group == utls.X25519MLKEM768\n\t\t\t})\n\t\t}\n\t}\n\t// Rebuild the client hello\n\tif err := c.BuildHandshakeState(); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc GetTLSConnectionState(conn net.Conn) (tlsState tls.ConnectionState) {\n\tswitch tlsConn := conn.(type) {\n\tcase interface{ ConnectionState() tls.ConnectionState }:\n\t\tstate := tlsConn.ConnectionState()\n\t\treturn state\n\tcase interface{ ConnectionState() utls.ConnectionState }:\n\t\tstate := tlsConn.ConnectionState()\n\t\treturn tlsConnectionState(state)\n\t}\n\treturn\n}\n\nvar globalFingerprint string\n\nfunc SetGlobalFingerprint(fingerprint string) {\n\tglobalFingerprint = fingerprint\n}\n\nfunc GetGlobalFingerprint() string {\n\treturn globalFingerprint\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/trie/domain.go",
    "content": "package trie\n\nimport (\n\t\"errors\"\n\t\"strings\"\n\t\"unicode\"\n\t\"unicode/utf8\"\n)\n\nconst (\n\twildcard        = \"*\"\n\tdotWildcard     = \"\"\n\tcomplexWildcard = \"+\"\n\tdomainStep      = \".\"\n)\n\n// ErrInvalidDomain means insert domain is invalid\nvar ErrInvalidDomain = errors.New(\"invalid domain\")\n\n// DomainTrie contains the main logic for adding and searching nodes for domain segments.\n// support wildcard domain (e.g *.google.com)\ntype DomainTrie[T any] struct {\n\troot *Node[T]\n}\n\nfunc ValidAndSplitDomain(domain string) ([]string, bool) {\n\tif domain != \"\" && domain[len(domain)-1] == '.' {\n\t\treturn nil, false\n\t}\n\tif domain != \"\" {\n\t\tif r, _ := utf8.DecodeRuneInString(domain); unicode.IsSpace(r) {\n\t\t\treturn nil, false\n\t\t}\n\t\tif r, _ := utf8.DecodeLastRuneInString(domain); unicode.IsSpace(r) {\n\t\t\treturn nil, false\n\t\t}\n\t}\n\tdomain = strings.ToLower(domain)\n\tparts := strings.Split(domain, domainStep)\n\tif len(parts) == 1 {\n\t\tif parts[0] == \"\" {\n\t\t\treturn nil, false\n\t\t}\n\n\t\treturn parts, true\n\t}\n\n\tfor _, part := range parts[1:] {\n\t\tif part == \"\" {\n\t\t\treturn nil, false\n\t\t}\n\t}\n\n\treturn parts, true\n}\n\n// Insert adds a node to the trie.\n// Support\n// 1. www.example.com\n// 2. *.example.com\n// 3. subdomain.*.example.com\n// 4. .example.com\n// 5. +.example.com\nfunc (t *DomainTrie[T]) Insert(domain string, data T) error {\n\tparts, valid := ValidAndSplitDomain(domain)\n\tif !valid {\n\t\treturn ErrInvalidDomain\n\t}\n\n\tif parts[0] == complexWildcard {\n\t\tt.insert(parts[1:], data)\n\t\tparts[0] = dotWildcard\n\t\tt.insert(parts, data)\n\t} else {\n\t\tt.insert(parts, data)\n\t}\n\n\treturn nil\n}\n\nfunc (t *DomainTrie[T]) insert(parts []string, data T) {\n\tnode := t.root\n\t// reverse storage domain part to save space\n\tfor i := len(parts) - 1; i >= 0; i-- {\n\t\tpart := parts[i]\n\t\tnode = node.getOrNewChild(part)\n\t}\n\n\tnode.setData(data)\n}\n\n// Search is the most important part of the Trie.\n// Priority as:\n// 1. static part\n// 2. wildcard domain\n// 2. dot wildcard domain\nfunc (t *DomainTrie[T]) Search(domain string) *Node[T] {\n\tparts, valid := ValidAndSplitDomain(domain)\n\tif !valid || parts[0] == \"\" {\n\t\treturn nil\n\t}\n\n\tn := t.search(t.root, parts)\n\n\tif n.isEmpty() {\n\t\treturn nil\n\t}\n\n\treturn n\n}\n\nfunc (t *DomainTrie[T]) search(node *Node[T], parts []string) *Node[T] {\n\tif len(parts) == 0 {\n\t\treturn node\n\t}\n\n\tif c := node.getChild(parts[len(parts)-1]); c != nil {\n\t\tif n := t.search(c, parts[:len(parts)-1]); !n.isEmpty() {\n\t\t\treturn n\n\t\t}\n\t}\n\n\tif c := node.getChild(wildcard); c != nil {\n\t\tif n := t.search(c, parts[:len(parts)-1]); !n.isEmpty() {\n\t\t\treturn n\n\t\t}\n\t}\n\n\treturn node.getChild(dotWildcard)\n}\n\nfunc (t *DomainTrie[T]) Optimize() {\n\tt.root.optimize()\n}\n\nfunc (t *DomainTrie[T]) Foreach(fn func(domain string, data T) bool) {\n\tfor key, data := range t.root.getChildren() {\n\t\trecursion([]string{key}, data, fn)\n\t\tif !data.isEmpty() {\n\t\t\tif !fn(joinDomain([]string{key}), data.data) {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (t *DomainTrie[T]) IsEmpty() bool {\n\tif t == nil || t.root == nil {\n\t\treturn true\n\t}\n\treturn len(t.root.getChildren()) == 0\n}\n\nfunc recursion[T any](items []string, node *Node[T], fn func(domain string, data T) bool) bool {\n\tfor key, data := range node.getChildren() {\n\t\tnewItems := append([]string{key}, items...)\n\t\tif !data.isEmpty() {\n\t\t\tdomain := joinDomain(newItems)\n\t\t\tif domain[0] == domainStepByte {\n\t\t\t\tdomain = complexWildcard + domain\n\t\t\t}\n\t\t\tif !fn(domain, data.Data()) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\t\tif !recursion(newItems, data, fn) {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc joinDomain(items []string) string {\n\treturn strings.Join(items, domainStep)\n}\n\n// New returns a new, empty Trie.\nfunc New[T any]() *DomainTrie[T] {\n\treturn &DomainTrie[T]{root: newNode[T]()}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/trie/domain_set.go",
    "content": "package trie\n\n// Package succinct provides several succinct data types.\n// Modify from https://github.com/openacid/succinct/blob/d4684c35d123f7528b14e03c24327231723db704/sskv.go\n\nimport (\n\t\"sort\"\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"github.com/openacid/low/bitmap\"\n)\n\nconst (\n\tcomplexWildcardByte = byte('+')\n\twildcardByte        = byte('*')\n\tdomainStepByte      = byte('.')\n)\n\ntype DomainSet struct {\n\tleaves, labelBitmap []uint64\n\tlabels              []byte\n\tranks, selects      []int32\n}\n\ntype qElt struct{ s, e, col int }\n\n// NewDomainSet creates a new *DomainSet struct, from a DomainTrie.\nfunc (t *DomainTrie[T]) NewDomainSet() *DomainSet {\n\treserveDomains := make([]string, 0)\n\tt.Foreach(func(domain string, data T) bool {\n\t\treserveDomains = append(reserveDomains, utils.Reverse(domain))\n\t\treturn true\n\t})\n\t// ensure that the same prefix is continuous\n\t// and according to the ascending sequence of length\n\tsort.Strings(reserveDomains)\n\tkeys := reserveDomains\n\tif len(keys) == 0 {\n\t\treturn nil\n\t}\n\tss := &DomainSet{}\n\tlIdx := 0\n\n\tqueue := []qElt{{0, len(keys), 0}}\n\tfor i := 0; i < len(queue); i++ {\n\t\telt := queue[i]\n\t\tif elt.col == len(keys[elt.s]) {\n\t\t\telt.s++\n\t\t\t// a leaf node\n\t\t\tsetBit(&ss.leaves, i, 1)\n\t\t}\n\n\t\tfor j := elt.s; j < elt.e; {\n\n\t\t\tfrm := j\n\n\t\t\tfor ; j < elt.e && keys[j][elt.col] == keys[frm][elt.col]; j++ {\n\t\t\t}\n\t\t\tqueue = append(queue, qElt{frm, j, elt.col + 1})\n\t\t\tss.labels = append(ss.labels, keys[frm][elt.col])\n\t\t\tsetBit(&ss.labelBitmap, lIdx, 0)\n\t\t\tlIdx++\n\t\t}\n\t\tsetBit(&ss.labelBitmap, lIdx, 1)\n\t\tlIdx++\n\t}\n\n\tss.init()\n\treturn ss\n}\n\n// Has query for a key and return whether it presents in the DomainSet.\nfunc (ss *DomainSet) Has(key string) bool {\n\tif ss == nil {\n\t\treturn false\n\t}\n\tkey = utils.Reverse(key)\n\tkey = strings.ToLower(key)\n\t// no more labels in this node\n\t// skip character matching\n\t// go to next level\n\tnodeId, bmIdx := 0, 0\n\ttype wildcardCursor struct {\n\t\tbmIdx, index int\n\t}\n\tstack := make([]wildcardCursor, 0)\n\tfor i := 0; i < len(key); i++ {\n\tRESTART:\n\t\tc := key[i]\n\t\tfor ; ; bmIdx++ {\n\t\t\tif getBit(ss.labelBitmap, bmIdx) != 0 {\n\t\t\t\tif len(stack) > 0 {\n\t\t\t\t\tcursor := stack[len(stack)-1]\n\t\t\t\t\tstack = stack[0 : len(stack)-1]\n\t\t\t\t\t// back wildcard and find next node\n\t\t\t\t\tnextNodeId := countZeros(ss.labelBitmap, ss.ranks, cursor.bmIdx+1)\n\t\t\t\t\tnextBmIdx := selectIthOne(ss.labelBitmap, ss.ranks, ss.selects, nextNodeId-1) + 1\n\t\t\t\t\tj := cursor.index\n\t\t\t\t\tfor ; j < len(key) && key[j] != domainStepByte; j++ {\n\t\t\t\t\t}\n\t\t\t\t\tif j == len(key) {\n\t\t\t\t\t\tif getBit(ss.leaves, nextNodeId) != 0 {\n\t\t\t\t\t\t\treturn true\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tgoto RESTART\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tfor ; nextBmIdx-nextNodeId < len(ss.labels); nextBmIdx++ {\n\t\t\t\t\t\tif ss.labels[nextBmIdx-nextNodeId] == domainStepByte {\n\t\t\t\t\t\t\tbmIdx = nextBmIdx\n\t\t\t\t\t\t\tnodeId = nextNodeId\n\t\t\t\t\t\t\ti = j\n\t\t\t\t\t\t\tgoto RESTART\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn false\n\t\t\t}\n\t\t\t// handle wildcard for domain\n\t\t\tif ss.labels[bmIdx-nodeId] == complexWildcardByte {\n\t\t\t\treturn true\n\t\t\t} else if ss.labels[bmIdx-nodeId] == wildcardByte {\n\t\t\t\tcursor := wildcardCursor{}\n\t\t\t\tcursor.bmIdx = bmIdx\n\t\t\t\tcursor.index = i\n\t\t\t\tstack = append(stack, cursor)\n\t\t\t} else if ss.labels[bmIdx-nodeId] == c {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\tnodeId = countZeros(ss.labelBitmap, ss.ranks, bmIdx+1)\n\t\tbmIdx = selectIthOne(ss.labelBitmap, ss.ranks, ss.selects, nodeId-1) + 1\n\t}\n\n\treturn getBit(ss.leaves, nodeId) != 0\n\n}\n\nfunc (ss *DomainSet) keys(f func(key string) bool) {\n\tvar currentKey []byte\n\tvar traverse func(int, int) bool\n\ttraverse = func(nodeId, bmIdx int) bool {\n\t\tif getBit(ss.leaves, nodeId) != 0 {\n\t\t\tif !f(string(currentKey)) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t}\n\n\t\tfor ; ; bmIdx++ {\n\t\t\tif getBit(ss.labelBitmap, bmIdx) != 0 {\n\t\t\t\treturn true\n\t\t\t}\n\t\t\tnextLabel := ss.labels[bmIdx-nodeId]\n\t\t\tcurrentKey = append(currentKey, nextLabel)\n\t\t\tnextNodeId := countZeros(ss.labelBitmap, ss.ranks, bmIdx+1)\n\t\t\tnextBmIdx := selectIthOne(ss.labelBitmap, ss.ranks, ss.selects, nextNodeId-1) + 1\n\t\t\tif !traverse(nextNodeId, nextBmIdx) {\n\t\t\t\treturn false\n\t\t\t}\n\t\t\tcurrentKey = currentKey[:len(currentKey)-1]\n\t\t}\n\t}\n\n\ttraverse(0, 0)\n\treturn\n}\n\nfunc (ss *DomainSet) Foreach(f func(key string) bool) {\n\tss.keys(func(key string) bool {\n\t\treturn f(utils.Reverse(key))\n\t})\n}\n\n// MatchDomain implements C.DomainMatcher\nfunc (ss *DomainSet) MatchDomain(domain string) bool {\n\treturn ss.Has(domain)\n}\n\nfunc setBit(bm *[]uint64, i int, v int) {\n\tfor i>>6 >= len(*bm) {\n\t\t*bm = append(*bm, 0)\n\t}\n\t(*bm)[i>>6] |= uint64(v) << uint(i&63)\n}\n\nfunc getBit(bm []uint64, i int) uint64 {\n\treturn bm[i>>6] & (1 << uint(i&63))\n}\n\n// init builds pre-calculated cache to speed up rank() and select()\nfunc (ss *DomainSet) init() {\n\tss.selects, ss.ranks = bitmap.IndexSelect32R64(ss.labelBitmap)\n}\n\n// countZeros counts the number of \"0\" in a bitmap before the i-th bit(excluding\n// the i-th bit) on behalf of rank index.\n// E.g.:\n//\n//\tcountZeros(\"010010\", 4) == 3\n//\t//          012345\nfunc countZeros(bm []uint64, ranks []int32, i int) int {\n\ta, _ := bitmap.Rank64(bm, ranks, int32(i))\n\treturn i - int(a)\n}\n\n// selectIthOne returns the index of the i-th \"1\" in a bitmap, on behalf of rank\n// and select indexes.\n// E.g.:\n//\n//\tselectIthOne(\"010010\", 1) == 4\n//\t//            012345\nfunc selectIthOne(bm []uint64, ranks, selects []int32, i int) int {\n\ta, _ := bitmap.Select32R64(bm, selects, ranks, int32(i))\n\treturn int(a)\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/trie/domain_set_bin.go",
    "content": "package trie\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"io\"\n)\n\nfunc (ss *DomainSet) WriteBin(w io.Writer) (err error) {\n\t// version\n\t_, err = w.Write([]byte{1})\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// leaves\n\terr = binary.Write(w, binary.BigEndian, int64(len(ss.leaves)))\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, d := range ss.leaves {\n\t\terr = binary.Write(w, binary.BigEndian, d)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\t// labelBitmap\n\terr = binary.Write(w, binary.BigEndian, int64(len(ss.labelBitmap)))\n\tif err != nil {\n\t\treturn err\n\t}\n\tfor _, d := range ss.labelBitmap {\n\t\terr = binary.Write(w, binary.BigEndian, d)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\t// labels\n\terr = binary.Write(w, binary.BigEndian, int64(len(ss.labels)))\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = w.Write(ss.labels)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc ReadDomainSetBin(r io.Reader) (ds *DomainSet, err error) {\n\t// version\n\tversion := make([]byte, 1)\n\t_, err = io.ReadFull(r, version)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif version[0] != 1 {\n\t\treturn nil, errors.New(\"version is invalid\")\n\t}\n\n\tds = &DomainSet{}\n\tvar length int64\n\n\t// leaves\n\terr = binary.Read(r, binary.BigEndian, &length)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif length < 1 {\n\t\treturn nil, errors.New(\"length is invalid\")\n\t}\n\tds.leaves = make([]uint64, length)\n\tfor i := int64(0); i < length; i++ {\n\t\terr = binary.Read(r, binary.BigEndian, &ds.leaves[i])\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\t// labelBitmap\n\terr = binary.Read(r, binary.BigEndian, &length)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif length < 1 {\n\t\treturn nil, errors.New(\"length is invalid\")\n\t}\n\tds.labelBitmap = make([]uint64, length)\n\tfor i := int64(0); i < length; i++ {\n\t\terr = binary.Read(r, binary.BigEndian, &ds.labelBitmap[i])\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\t// labels\n\terr = binary.Read(r, binary.BigEndian, &length)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif length < 1 {\n\t\treturn nil, errors.New(\"length is invalid\")\n\t}\n\tds.labels = make([]byte, length)\n\t_, err = io.ReadFull(r, ds.labels)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tds.init()\n\treturn ds, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/trie/domain_set_test.go",
    "content": "package trie_test\n\nimport (\n\t\"golang.org/x/exp/slices\"\n\t\"testing\"\n\n\t\"github.com/metacubex/mihomo/component/trie\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc testDump(t *testing.T, tree *trie.DomainTrie[struct{}], set *trie.DomainSet) {\n\tvar dataSrc []string\n\ttree.Foreach(func(domain string, data struct{}) bool {\n\t\tdataSrc = append(dataSrc, domain)\n\t\treturn true\n\t})\n\tslices.Sort(dataSrc)\n\tvar dataSet []string\n\tset.Foreach(func(key string) bool {\n\t\tdataSet = append(dataSet, key)\n\t\treturn true\n\t})\n\tslices.Sort(dataSet)\n\tassert.Equal(t, dataSrc, dataSet)\n}\n\nfunc TestDomainSet(t *testing.T) {\n\ttree := trie.New[struct{}]()\n\tdomainSet := []string{\n\t\t\"baidu.com\",\n\t\t\"google.com\",\n\t\t\"www.google.com\",\n\t\t\"test.a.net\",\n\t\t\"test.a.oc\",\n\t\t\"Mijia Cloud\",\n\t\t\".qq.com\",\n\t\t\"+.cn\",\n\t}\n\n\tfor _, domain := range domainSet {\n\t\tassert.NoError(t, tree.Insert(domain, struct{}{}))\n\t}\n\tassert.False(t, tree.IsEmpty())\n\tset := tree.NewDomainSet()\n\tassert.NotNil(t, set)\n\tassert.True(t, set.Has(\"test.cn\"))\n\tassert.True(t, set.Has(\"cn\"))\n\tassert.True(t, set.Has(\"Mijia Cloud\"))\n\tassert.True(t, set.Has(\"test.a.net\"))\n\tassert.True(t, set.Has(\"www.qq.com\"))\n\tassert.True(t, set.Has(\"google.com\"))\n\tassert.False(t, set.Has(\"qq.com\"))\n\tassert.False(t, set.Has(\"www.baidu.com\"))\n\ttestDump(t, tree, set)\n}\n\nfunc TestDomainSetComplexWildcard(t *testing.T) {\n\ttree := trie.New[struct{}]()\n\tdomainSet := []string{\n\t\t\"+.baidu.com\",\n\t\t\"+.a.baidu.com\",\n\t\t\"www.baidu.com\",\n\t\t\"+.bb.baidu.com\",\n\t\t\"test.a.net\",\n\t\t\"test.a.oc\",\n\t\t\"www.qq.com\",\n\t}\n\n\tfor _, domain := range domainSet {\n\t\tassert.NoError(t, tree.Insert(domain, struct{}{}))\n\t}\n\tassert.False(t, tree.IsEmpty())\n\tset := tree.NewDomainSet()\n\tassert.NotNil(t, set)\n\tassert.False(t, set.Has(\"google.com\"))\n\tassert.True(t, set.Has(\"www.baidu.com\"))\n\tassert.True(t, set.Has(\"test.test.baidu.com\"))\n\ttestDump(t, tree, set)\n}\n\nfunc TestDomainSetWildcard(t *testing.T) {\n\ttree := trie.New[struct{}]()\n\tdomainSet := []string{\n\t\t\"*.*.*.baidu.com\",\n\t\t\"www.baidu.*\",\n\t\t\"stun.*.*\",\n\t\t\"*.*.qq.com\",\n\t\t\"test.*.baidu.com\",\n\t\t\"*.apple.com\",\n\t}\n\n\tfor _, domain := range domainSet {\n\t\tassert.NoError(t, tree.Insert(domain, struct{}{}))\n\t}\n\tassert.False(t, tree.IsEmpty())\n\tset := tree.NewDomainSet()\n\tassert.NotNil(t, set)\n\tassert.True(t, set.Has(\"www.baidu.com\"))\n\tassert.True(t, set.Has(\"test.test.baidu.com\"))\n\tassert.True(t, set.Has(\"test.test.qq.com\"))\n\tassert.True(t, set.Has(\"stun.ab.cd\"))\n\tassert.False(t, set.Has(\"test.baidu.com\"))\n\tassert.False(t, set.Has(\"www.google.com\"))\n\tassert.False(t, set.Has(\"a.www.google.com\"))\n\tassert.False(t, set.Has(\"test.qq.com\"))\n\tassert.False(t, set.Has(\"test.test.test.qq.com\"))\n\ttestDump(t, tree, set)\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/trie/domain_test.go",
    "content": "package trie_test\n\nimport (\n\t\"net/netip\"\n\t\"testing\"\n\n\t\"github.com/metacubex/mihomo/component/trie\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nvar localIP = netip.AddrFrom4([4]byte{127, 0, 0, 1})\n\nfunc TestTrie_Basic(t *testing.T) {\n\ttree := trie.New[netip.Addr]()\n\tdomains := []string{\n\t\t\"example.com\",\n\t\t\"google.com\",\n\t\t\"localhost\",\n\t}\n\n\tfor _, domain := range domains {\n\t\tassert.NoError(t, tree.Insert(domain, localIP))\n\t}\n\n\tnode := tree.Search(\"example.com\")\n\tassert.NotNil(t, node)\n\tassert.True(t, node.Data() == localIP)\n\tassert.NotNil(t, tree.Insert(\"\", localIP))\n\tassert.Nil(t, tree.Search(\"\"))\n\tassert.NotNil(t, tree.Search(\"localhost\"))\n\tassert.Nil(t, tree.Search(\"www.google.com\"))\n}\n\nfunc TestTrie_Wildcard(t *testing.T) {\n\ttree := trie.New[netip.Addr]()\n\tdomains := []string{\n\t\t\"*.example.com\",\n\t\t\"sub.*.example.com\",\n\t\t\"*.dev\",\n\t\t\".org\",\n\t\t\".example.net\",\n\t\t\".apple.*\",\n\t\t\"+.foo.com\",\n\t\t\"+.stun.*.*\",\n\t\t\"+.stun.*.*.*\",\n\t\t\"+.stun.*.*.*.*\",\n\t\t\"stun.l.google.com\",\n\t}\n\n\tfor _, domain := range domains {\n\t\tassert.NoError(t, tree.Insert(domain, localIP))\n\t}\n\n\tassert.NotNil(t, tree.Search(\"sub.example.com\"))\n\tassert.NotNil(t, tree.Search(\"sub.foo.example.com\"))\n\tassert.NotNil(t, tree.Search(\"test.org\"))\n\tassert.NotNil(t, tree.Search(\"test.example.net\"))\n\tassert.NotNil(t, tree.Search(\"test.apple.com\"))\n\tassert.NotNil(t, tree.Search(\"test.foo.com\"))\n\tassert.NotNil(t, tree.Search(\"foo.com\"))\n\tassert.NotNil(t, tree.Search(\"global.stun.website.com\"))\n\tassert.Nil(t, tree.Search(\"foo.sub.example.com\"))\n\tassert.Nil(t, tree.Search(\"foo.example.dev\"))\n\tassert.Nil(t, tree.Search(\"example.com\"))\n}\n\nfunc TestTrie_Priority(t *testing.T) {\n\ttree := trie.New[int]()\n\tdomains := []string{\n\t\t\".dev\",\n\t\t\"example.dev\",\n\t\t\"*.example.dev\",\n\t\t\"test.example.dev\",\n\t}\n\n\tassertFn := func(domain string, data int) {\n\t\tnode := tree.Search(domain)\n\t\tassert.NotNil(t, node)\n\t\tassert.Equal(t, data, node.Data())\n\t}\n\n\tfor idx, domain := range domains {\n\t\tassert.NoError(t, tree.Insert(domain, idx+1))\n\t}\n\n\tassertFn(\"test.dev\", 1)\n\tassertFn(\"foo.bar.dev\", 1)\n\tassertFn(\"example.dev\", 2)\n\tassertFn(\"foo.example.dev\", 3)\n\tassertFn(\"test.example.dev\", 4)\n}\n\nfunc TestTrie_Boundary(t *testing.T) {\n\ttree := trie.New[netip.Addr]()\n\tassert.NoError(t, tree.Insert(\"*.dev\", localIP))\n\n\tassert.NotNil(t, tree.Insert(\".\", localIP))\n\tassert.NotNil(t, tree.Insert(\"..dev\", localIP))\n\tassert.Nil(t, tree.Search(\"dev\"))\n}\n\nfunc TestTrie_WildcardBoundary(t *testing.T) {\n\ttree := trie.New[netip.Addr]()\n\tassert.NoError(t, tree.Insert(\"+.*\", localIP))\n\tassert.NoError(t, tree.Insert(\"stun.*.*.*\", localIP))\n\n\tassert.NotNil(t, tree.Search(\"example.com\"))\n}\n\nfunc TestTrie_Foreach(t *testing.T) {\n\ttree := trie.New[netip.Addr]()\n\tdomainList := []string{\n\t\t\"google.com\",\n\t\t\"stun.*.*.*\",\n\t\t\"test.*.google.com\",\n\t\t\"+.baidu.com\",\n\t\t\"*.baidu.com\",\n\t\t\"*.*.baidu.com\",\n\t}\n\tfor _, domain := range domainList {\n\t\tassert.NoError(t, tree.Insert(domain, localIP))\n\t}\n\tcount := 0\n\ttree.Foreach(func(domain string, data netip.Addr) bool {\n\t\tcount++\n\t\treturn true\n\t})\n\tassert.Equal(t, 7, count)\n}\n\nfunc TestTrie_Space(t *testing.T) {\n\tvalidDomain := func(domain string) bool {\n\t\t_, ok := trie.ValidAndSplitDomain(domain)\n\t\treturn ok\n\t}\n\tassert.True(t, validDomain(\"google.com\"))\n\tassert.False(t, validDomain(\" google.com\"))\n\tassert.False(t, validDomain(\" google.com \"))\n\tassert.True(t, validDomain(\"Mijia Cloud\"))\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/trie/ipcidr_node.go",
    "content": "package trie\n\nimport \"errors\"\n\nvar (\n\tErrorOverMaxValue = errors.New(\"the value don't over max value\")\n)\n\ntype IpCidrNode struct {\n\tMark     bool\n\tchild    map[uint32]*IpCidrNode\n\tmaxValue uint32\n}\n\nfunc NewIpCidrNode(mark bool, maxValue uint32) *IpCidrNode {\n\tipCidrNode := &IpCidrNode{\n\t\tMark:     mark,\n\t\tchild:    map[uint32]*IpCidrNode{},\n\t\tmaxValue: maxValue,\n\t}\n\n\treturn ipCidrNode\n}\n\nfunc (n *IpCidrNode) addChild(value uint32) error {\n\tif value > n.maxValue {\n\t\treturn ErrorOverMaxValue\n\t}\n\n\tn.child[value] = NewIpCidrNode(false, n.maxValue)\n\treturn nil\n}\n\nfunc (n *IpCidrNode) hasChild(value uint32) bool {\n\treturn n.getChild(value) != nil\n}\n\nfunc (n *IpCidrNode) getChild(value uint32) *IpCidrNode {\n\tif value <= n.maxValue {\n\t\treturn n.child[value]\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/trie/ipcidr_trie.go",
    "content": "package trie\n\nimport (\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/log\"\n)\n\ntype IPV6 bool\n\nconst (\n\tipv4GroupMaxValue = 0xFF\n\tipv6GroupMaxValue = 0xFFFF\n)\n\ntype IpCidrTrie struct {\n\tipv4Trie *IpCidrNode\n\tipv6Trie *IpCidrNode\n}\n\nfunc NewIpCidrTrie() *IpCidrTrie {\n\treturn &IpCidrTrie{\n\t\tipv4Trie: NewIpCidrNode(false, ipv4GroupMaxValue),\n\t\tipv6Trie: NewIpCidrNode(false, ipv6GroupMaxValue),\n\t}\n}\n\nfunc (trie *IpCidrTrie) AddIpCidr(ipCidr *net.IPNet) error {\n\tsubIpCidr, subCidr, isIpv4, err := ipCidrToSubIpCidr(ipCidr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, sub := range subIpCidr {\n\t\taddIpCidr(trie, isIpv4, sub, subCidr/8)\n\t}\n\n\treturn nil\n}\n\nfunc (trie *IpCidrTrie) AddIpCidrForString(ipCidr string) error {\n\t_, ipNet, err := net.ParseCIDR(ipCidr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn trie.AddIpCidr(ipNet)\n}\n\nfunc (trie *IpCidrTrie) IsContain(ip net.IP) bool {\n\tif ip == nil {\n\t\treturn false\n\t}\n\tisIpv4 := len(ip) == net.IPv4len\n\tvar groupValues []uint32\n\tvar ipCidrNode *IpCidrNode\n\n\tif isIpv4 {\n\t\tipCidrNode = trie.ipv4Trie\n\t\tfor _, group := range ip {\n\t\t\tgroupValues = append(groupValues, uint32(group))\n\t\t}\n\t} else {\n\t\tipCidrNode = trie.ipv6Trie\n\t\tfor i := 0; i < len(ip); i += 2 {\n\t\t\tgroupValues = append(groupValues, getIpv6GroupValue(ip[i], ip[i+1]))\n\t\t}\n\t}\n\n\treturn search(ipCidrNode, groupValues) != nil\n}\n\nfunc (trie *IpCidrTrie) IsContainForString(ipString string) bool {\n\tip := net.ParseIP(ipString)\n\t// deal with 4in6\n\tactualIp := ip.To4()\n\tif actualIp == nil {\n\t\tactualIp = ip\n\t}\n\treturn trie.IsContain(actualIp)\n}\n\nfunc ipCidrToSubIpCidr(ipNet *net.IPNet) ([]net.IP, int, bool, error) {\n\tmaskSize, _ := ipNet.Mask.Size()\n\tvar (\n\t\tipList      []net.IP\n\t\tnewMaskSize int\n\t\tisIpv4      bool\n\t\terr         error\n\t)\n\tisIpv4 = len(ipNet.IP) == net.IPv4len\n\tipList, newMaskSize, err = subIpCidr(ipNet.IP, maskSize, isIpv4)\n\n\treturn ipList, newMaskSize, isIpv4, err\n}\n\nfunc subIpCidr(ip net.IP, maskSize int, isIpv4 bool) ([]net.IP, int, error) {\n\tvar subIpCidrList []net.IP\n\tgroupSize := 8\n\tif !isIpv4 {\n\t\tgroupSize = 16\n\t}\n\n\tif maskSize%groupSize == 0 {\n\t\treturn append(subIpCidrList, ip), maskSize, nil\n\t}\n\n\tlastByteMaskSize := maskSize % 8\n\tlastByteMaskIndex := maskSize / 8\n\tsubIpCidrNum := 0xFF >> lastByteMaskSize\n\tfor i := 0; i <= subIpCidrNum; i++ {\n\t\tsubIpCidr := make([]byte, len(ip))\n\t\tcopy(subIpCidr, ip)\n\t\tsubIpCidr[lastByteMaskIndex] += byte(i)\n\t\tsubIpCidrList = append(subIpCidrList, subIpCidr)\n\t}\n\n\tnewMaskSize := (lastByteMaskIndex + 1) * 8\n\tif !isIpv4 {\n\t\tnewMaskSize = (lastByteMaskIndex/2 + 1) * 16\n\t}\n\n\treturn subIpCidrList, newMaskSize, nil\n}\n\nfunc addIpCidr(trie *IpCidrTrie, isIpv4 bool, ip net.IP, groupSize int) {\n\tif isIpv4 {\n\t\taddIpv4Cidr(trie, ip, groupSize)\n\t} else {\n\t\taddIpv6Cidr(trie, ip, groupSize)\n\t}\n}\n\nfunc addIpv4Cidr(trie *IpCidrTrie, ip net.IP, groupSize int) {\n\tpreNode := trie.ipv4Trie\n\tnode := preNode.getChild(uint32(ip[0]))\n\tif node == nil {\n\t\terr := preNode.addChild(uint32(ip[0]))\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tnode = preNode.getChild(uint32(ip[0]))\n\t}\n\n\tfor i := 1; i < groupSize; i++ {\n\t\tif node.Mark {\n\t\t\treturn\n\t\t}\n\n\t\tgroupValue := uint32(ip[i])\n\t\tif !node.hasChild(groupValue) {\n\t\t\terr := node.addChild(groupValue)\n\t\t\tif err != nil {\n\t\t\t\tlog.Errorln(err.Error())\n\t\t\t}\n\t\t}\n\n\t\tpreNode = node\n\t\tnode = node.getChild(groupValue)\n\t\tif node == nil {\n\t\t\terr := preNode.addChild(uint32(ip[i-1]))\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tnode = preNode.getChild(uint32(ip[i-1]))\n\t\t}\n\t}\n\n\tnode.Mark = true\n\tcleanChild(node)\n}\n\nfunc addIpv6Cidr(trie *IpCidrTrie, ip net.IP, groupSize int) {\n\tpreNode := trie.ipv6Trie\n\tnode := preNode.getChild(getIpv6GroupValue(ip[0], ip[1]))\n\tif node == nil {\n\t\terr := preNode.addChild(getIpv6GroupValue(ip[0], ip[1]))\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tnode = preNode.getChild(getIpv6GroupValue(ip[0], ip[1]))\n\t}\n\n\tfor i := 2; i < groupSize; i += 2 {\n\t\tif ip[i] == 0 && ip[i+1] == 0 {\n\t\t\tnode.Mark = true\n\t\t}\n\n\t\tif node.Mark {\n\t\t\treturn\n\t\t}\n\n\t\tgroupValue := getIpv6GroupValue(ip[i], ip[i+1])\n\t\tif !node.hasChild(groupValue) {\n\t\t\terr := node.addChild(groupValue)\n\t\t\tif err != nil {\n\t\t\t\tlog.Errorln(err.Error())\n\t\t\t}\n\t\t}\n\n\t\tpreNode = node\n\t\tnode = node.getChild(groupValue)\n\t\tif node == nil {\n\t\t\terr := preNode.addChild(getIpv6GroupValue(ip[i-2], ip[i-1]))\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tnode = preNode.getChild(getIpv6GroupValue(ip[i-2], ip[i-1]))\n\t\t}\n\t}\n\n\tnode.Mark = true\n\tcleanChild(node)\n}\n\nfunc getIpv6GroupValue(high, low byte) uint32 {\n\treturn (uint32(high) << 8) | uint32(low)\n}\n\nfunc cleanChild(node *IpCidrNode) {\n\tfor i := uint32(0); i < uint32(len(node.child)); i++ {\n\t\tdelete(node.child, i)\n\t}\n}\n\nfunc search(root *IpCidrNode, groupValues []uint32) *IpCidrNode {\n\tnode := root.getChild(groupValues[0])\n\tif node == nil || node.Mark {\n\t\treturn node\n\t}\n\n\tfor _, value := range groupValues[1:] {\n\t\tif !node.hasChild(value) {\n\t\t\treturn nil\n\t\t}\n\n\t\tnode = node.getChild(value)\n\n\t\tif node == nil || node.Mark {\n\t\t\treturn node\n\t\t}\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/trie/node.go",
    "content": "package trie\n\nimport \"strings\"\n\n// Node is the trie's node\ntype Node[T any] struct {\n\tchildMap  map[string]*Node[T]\n\tchildNode *Node[T] // optimize for only one child\n\tchildStr  string\n\tinited    bool\n\tdata      T\n}\n\nfunc (n *Node[T]) getChild(s string) *Node[T] {\n\tif n.childMap == nil {\n\t\tif n.childNode != nil && n.childStr == s {\n\t\t\treturn n.childNode\n\t\t}\n\t\treturn nil\n\t}\n\treturn n.childMap[s]\n}\n\nfunc (n *Node[T]) hasChild(s string) bool {\n\treturn n.getChild(s) != nil\n}\n\nfunc (n *Node[T]) addChild(s string, child *Node[T]) {\n\tif n.childMap == nil {\n\t\tif n.childNode == nil {\n\t\t\tn.childStr = s\n\t\t\tn.childNode = child\n\t\t\treturn\n\t\t}\n\t\tn.childMap = map[string]*Node[T]{}\n\t\tif n.childNode != nil {\n\t\t\tn.childMap[n.childStr] = n.childNode\n\t\t}\n\t\tn.childStr = \"\"\n\t\tn.childNode = nil\n\t}\n\n\tn.childMap[s] = child\n}\n\nfunc (n *Node[T]) getOrNewChild(s string) *Node[T] {\n\tnode := n.getChild(s)\n\tif node == nil {\n\t\tnode = newNode[T]()\n\t\tn.addChild(s, node)\n\t}\n\treturn node\n}\n\nfunc (n *Node[T]) optimize() {\n\tif len(n.childStr) > 0 {\n\t\tn.childStr = strClone(n.childStr)\n\t}\n\tif n.childNode != nil {\n\t\tn.childNode.optimize()\n\t}\n\tif n.childMap == nil {\n\t\treturn\n\t}\n\tswitch len(n.childMap) {\n\tcase 0:\n\t\tn.childMap = nil\n\t\treturn\n\tcase 1:\n\t\tfor key := range n.childMap {\n\t\t\tn.childStr = key\n\t\t\tn.childNode = n.childMap[key]\n\t\t}\n\t\tn.childMap = nil\n\t\tn.optimize()\n\t\treturn\n\t}\n\tchildren := make(map[string]*Node[T], len(n.childMap)) // avoid map reallocate memory\n\tfor key := range n.childMap {\n\t\tchild := n.childMap[key]\n\t\tif child == nil {\n\t\t\tcontinue\n\t\t}\n\t\tkey = strClone(key)\n\t\tchildren[key] = child\n\t\tchild.optimize()\n\t}\n\tn.childMap = children\n}\n\nfunc strClone(key string) string {\n\tswitch key { // try to save string's memory\n\tcase wildcard:\n\t\tkey = wildcard\n\tcase dotWildcard:\n\t\tkey = dotWildcard\n\tcase complexWildcard:\n\t\tkey = complexWildcard\n\tcase domainStep:\n\t\tkey = domainStep\n\tdefault:\n\t\tkey = strings.Clone(key)\n\t}\n\treturn key\n}\n\nfunc (n *Node[T]) isEmpty() bool {\n\tif n == nil || n.inited == false {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (n *Node[T]) setData(data T) {\n\tn.data = data\n\tn.inited = true\n}\n\nfunc (n *Node[T]) getChildren() map[string]*Node[T] {\n\tif n.childMap == nil {\n\t\tif n.childNode != nil {\n\t\t\tm := make(map[string]*Node[T])\n\t\t\tm[n.childStr] = n.childNode\n\t\t\treturn m\n\t\t}\n\t} else {\n\t\treturn n.childMap\n\t}\n\treturn nil\n}\nfunc (n *Node[T]) Data() T {\n\treturn n.data\n}\n\nfunc newNode[T any]() *Node[T] {\n\treturn &Node[T]{}\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/trie/trie_test.go",
    "content": "package trie\n\nimport (\n\t\"net\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestIpv4AddSuccess(t *testing.T) {\n\ttrie := NewIpCidrTrie()\n\terr := trie.AddIpCidrForString(\"10.0.0.2/16\")\n\tassert.Equal(t, nil, err)\n}\n\nfunc TestIpv4AddFail(t *testing.T) {\n\ttrie := NewIpCidrTrie()\n\terr := trie.AddIpCidrForString(\"333.00.23.2/23\")\n\tassert.IsType(t, new(net.ParseError), err)\n\n\terr = trie.AddIpCidrForString(\"22.3.34.2/222\")\n\tassert.IsType(t, new(net.ParseError), err)\n\n\terr = trie.AddIpCidrForString(\"2.2.2.2\")\n\tassert.IsType(t, new(net.ParseError), err)\n}\n\nfunc TestIpv4Search(t *testing.T) {\n\ttrie := NewIpCidrTrie()\n\t// Boundary testing\n\tassert.NoError(t, trie.AddIpCidrForString(\"149.154.160.0/20\"))\n\tassert.Equal(t, true, trie.IsContainForString(\"149.154.160.0\"))\n\tassert.Equal(t, true, trie.IsContainForString(\"149.154.175.255\"))\n\tassert.Equal(t, false, trie.IsContainForString(\"149.154.176.0\"))\n\tassert.Equal(t, false, trie.IsContainForString(\"149.154.159.255\"))\n\n\tassert.NoError(t, trie.AddIpCidrForString(\"129.2.36.0/16\"))\n\tassert.NoError(t, trie.AddIpCidrForString(\"10.2.36.0/18\"))\n\tassert.NoError(t, trie.AddIpCidrForString(\"16.2.23.0/24\"))\n\tassert.NoError(t, trie.AddIpCidrForString(\"11.2.13.2/26\"))\n\tassert.NoError(t, trie.AddIpCidrForString(\"55.5.6.3/8\"))\n\tassert.NoError(t, trie.AddIpCidrForString(\"66.23.25.4/6\"))\n\n\tassert.Equal(t, true, trie.IsContainForString(\"129.2.3.65\"))\n\tassert.Equal(t, false, trie.IsContainForString(\"15.2.3.1\"))\n\tassert.Equal(t, true, trie.IsContainForString(\"11.2.13.1\"))\n\tassert.Equal(t, true, trie.IsContainForString(\"55.0.0.0\"))\n\tassert.Equal(t, true, trie.IsContainForString(\"64.0.0.0\"))\n\tassert.Equal(t, false, trie.IsContainForString(\"128.0.0.0\"))\n\n\tassert.Equal(t, false, trie.IsContain(net.ParseIP(\"22\")))\n\tassert.Equal(t, false, trie.IsContain(net.ParseIP(\"\")))\n\n}\n\nfunc TestIpv6AddSuccess(t *testing.T) {\n\ttrie := NewIpCidrTrie()\n\terr := trie.AddIpCidrForString(\"2001:0db8:02de:0000:0000:0000:0000:0e13/32\")\n\tassert.Equal(t, nil, err)\n\n\terr = trie.AddIpCidrForString(\"2001:1db8:f2de::0e13/18\")\n\tassert.Equal(t, nil, err)\n}\n\nfunc TestIpv6AddFail(t *testing.T) {\n\ttrie := NewIpCidrTrie()\n\terr := trie.AddIpCidrForString(\"2001::25de::cade/23\")\n\tassert.IsType(t, new(net.ParseError), err)\n\n\terr = trie.AddIpCidrForString(\"2001:0fa3:25de::cade/222\")\n\tassert.IsType(t, new(net.ParseError), err)\n\n\terr = trie.AddIpCidrForString(\"2001:0fa3:25de::cade\")\n\tassert.IsType(t, new(net.ParseError), err)\n}\n\nfunc TestIpv6SearchSub(t *testing.T) {\n\ttrie := NewIpCidrTrie()\n\tassert.NoError(t, trie.AddIpCidrForString(\"240e::/18\"))\n\n\tassert.Equal(t, true, trie.IsContainForString(\"240e:964:ea02:100:1800::71\"))\n\n}\n\nfunc TestIpv6Search(t *testing.T) {\n\ttrie := NewIpCidrTrie()\n\n\t// Boundary testing\n\tassert.NoError(t, trie.AddIpCidrForString(\"2a0a:f280::/32\"))\n\tassert.Equal(t, true, trie.IsContainForString(\"2a0a:f280:0000:0000:0000:0000:0000:0000\"))\n\tassert.Equal(t, true, trie.IsContainForString(\"2a0a:f280:ffff:ffff:ffff:ffff:ffff:ffff\"))\n\tassert.Equal(t, false, trie.IsContainForString(\"2a0a:f279:ffff:ffff:ffff:ffff:ffff:ffff\"))\n\tassert.Equal(t, false, trie.IsContainForString(\"2a0a:f281:0000:0000:0000:0000:0000:0000\"))\n\n\tassert.NoError(t, trie.AddIpCidrForString(\"2001:b28:f23d:f001::e/128\"))\n\tassert.NoError(t, trie.AddIpCidrForString(\"2001:67c:4e8:f002::e/12\"))\n\tassert.NoError(t, trie.AddIpCidrForString(\"2001:b28:f23d:f003::e/96\"))\n\tassert.NoError(t, trie.AddIpCidrForString(\"2001:67c:4e8:f002::a/32\"))\n\tassert.NoError(t, trie.AddIpCidrForString(\"2001:67c:4e8:f004::a/60\"))\n\tassert.NoError(t, trie.AddIpCidrForString(\"2001:b28:f23f:f005::a/64\"))\n\tassert.Equal(t, true, trie.IsContainForString(\"2001:b28:f23d:f001::e\"))\n\tassert.Equal(t, false, trie.IsContainForString(\"2222::fff2\"))\n\tassert.Equal(t, true, trie.IsContainForString(\"2000::ffa0\"))\n\tassert.Equal(t, true, trie.IsContainForString(\"2001:b28:f23f:f005:5662::\"))\n\tassert.Equal(t, true, trie.IsContainForString(\"2001:67c:4e8:9666::1213\"))\n\n\tassert.Equal(t, false, trie.IsContain(net.ParseIP(\"22233:22\")))\n}\n\nfunc TestIpv4InIpv6(t *testing.T) {\n\ttrie := NewIpCidrTrie()\n\n\t// Boundary testing\n\tassert.NoError(t, trie.AddIpCidrForString(\"::ffff:198.18.5.138/128\"))\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/updater/patch.go",
    "content": "package updater\n\nimport (\n\t\"fmt\"\n\t\"github.com/metacubex/mihomo/component/geodata\"\n\t\"github.com/metacubex/mihomo/component/mmdb\"\n\t\"github.com/oschwald/maxminddb-golang\"\n)\n\nfunc UpdateMMDBWithPath(path string) (err error) {\n\tdefer mmdb.ReloadIP()\n\tdata, err := downloadForBytes(geodata.MmdbUrl())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"can't download MMDB database file: %w\", err)\n\t}\n\tinstance, err := maxminddb.FromBytes(data)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid MMDB database file: %s\", err)\n\t}\n\t_ = instance.Close()\n\n\tmmdb.IPInstance().Reader.Close()\n\tif err = saveFile(data, path); err != nil {\n\t\treturn fmt.Errorf(\"can't save MMDB database file: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc UpdateASNWithPath(path string) (err error) {\n\tdefer mmdb.ReloadASN()\n\tdata, err := downloadForBytes(geodata.ASNUrl())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"can't download ASN database file: %w\", err)\n\t}\n\n\tinstance, err := maxminddb.FromBytes(data)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid ASN database file: %s\", err)\n\t}\n\t_ = instance.Close()\n\n\tmmdb.ASNInstance().Reader.Close()\n\tif err = saveFile(data, path); err != nil {\n\t\treturn fmt.Errorf(\"can't save ASN database file: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc UpdateGeoIpWithPath(path string) (err error) {\n\tgeoLoader, err := geodata.GetGeoDataLoader(\"standard\")\n\tdata, err := downloadForBytes(geodata.GeoIpUrl())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"can't download GeoIP database file: %w\", err)\n\t}\n\tif _, err = geoLoader.LoadIPByBytes(data, \"cn\"); err != nil {\n\t\treturn fmt.Errorf(\"invalid GeoIP database file: %s\", err)\n\t}\n\tif err = saveFile(data, path); err != nil {\n\t\treturn fmt.Errorf(\"can't save GeoIP database file: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc UpdateGeoSiteWithPath(path string) (err error) {\n\tgeoLoader, err := geodata.GetGeoDataLoader(\"standard\")\n\tdata, err := downloadForBytes(geodata.GeoSiteUrl())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"can't download GeoSite database file: %w\", err)\n\t}\n\n\tif _, err = geoLoader.LoadSiteByBytes(data, \"cn\"); err != nil {\n\t\treturn fmt.Errorf(\"invalid GeoSite database file: %s\", err)\n\t}\n\n\tif err = saveFile(data, path); err != nil {\n\t\treturn fmt.Errorf(\"can't save GeoSite database file: %w\", err)\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/updater/update_core.go",
    "content": "package updater\n\nimport (\n\t\"archive/zip\"\n\t\"compress/gzip\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"os/exec\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/component/ca\"\n\tmihomoHttp \"github.com/metacubex/mihomo/component/http\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/constant/features\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/metacubex/http\"\n)\n\nconst (\n\tbaseReleaseURL    = \"https://github.com/MetaCubeX/mihomo/releases/latest/download/\"\n\tversionReleaseURL = \"https://github.com/MetaCubeX/mihomo/releases/latest/download/version.txt\"\n\n\tbaseAlphaURL    = \"https://github.com/MetaCubeX/mihomo/releases/download/Prerelease-Alpha/\"\n\tversionAlphaURL = \"https://github.com/MetaCubeX/mihomo/releases/download/Prerelease-Alpha/version.txt\"\n\n\t// MaxPackageFileSize is a maximum package file length in bytes. The largest\n\t// package whose size is limited by this constant currently has the size of\n\t// approximately 32 MiB.\n\tMaxPackageFileSize = 32 * 1024 * 1024\n)\n\nconst (\n\tReleaseChannel = \"release\"\n\tAlphaChannel   = \"alpha\"\n)\n\n// CoreUpdater is the mihomo updater.\n// modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/updater/updater.go\ntype CoreUpdater struct {\n\tmu sync.Mutex\n}\n\nvar DefaultCoreUpdater = CoreUpdater{}\n\nfunc (u *CoreUpdater) CoreBaseName() string {\n\tswitch runtime.GOARCH {\n\tcase \"arm\":\n\t\t// mihomo-linux-armv5\n\t\treturn fmt.Sprintf(\"mihomo-%s-%sv%s\", runtime.GOOS, runtime.GOARCH, features.GOARM)\n\tcase \"arm64\":\n\t\tif runtime.GOOS == \"android\" {\n\t\t\t// mihomo-android-arm64-v8\n\t\t\treturn fmt.Sprintf(\"mihomo-%s-%s-v8\", runtime.GOOS, runtime.GOARCH)\n\t\t} else {\n\t\t\t// mihomo-linux-arm64\n\t\t\treturn fmt.Sprintf(\"mihomo-%s-%s\", runtime.GOOS, runtime.GOARCH)\n\t\t}\n\tcase \"mips\", \"mipsle\":\n\t\t// mihomo-linux-mips-hardfloat\n\t\treturn fmt.Sprintf(\"mihomo-%s-%s-%s\", runtime.GOOS, runtime.GOARCH, features.GOMIPS)\n\tcase \"amd64\":\n\t\t// mihomo-linux-amd64-v1\n\t\treturn fmt.Sprintf(\"mihomo-%s-%s-%s\", runtime.GOOS, runtime.GOARCH, features.GOAMD64)\n\tdefault:\n\t\t// mihomo-linux-386\n\t\t// mihomo-linux-mips64\n\t\t// mihomo-linux-riscv64\n\t\t// mihomo-linux-s390x\n\t\treturn fmt.Sprintf(\"mihomo-%s-%s\", runtime.GOOS, runtime.GOARCH)\n\t}\n}\n\nfunc (u *CoreUpdater) Update(currentExePath string, channel string, force bool) (err error) {\n\tu.mu.Lock()\n\tdefer u.mu.Unlock()\n\n\tinfo, err := os.Stat(currentExePath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"check currentExePath %q: %w\", currentExePath, err)\n\t}\n\n\tbaseURL := baseAlphaURL\n\tversionURL := versionAlphaURL\n\tswitch strings.ToLower(channel) {\n\tcase ReleaseChannel:\n\t\tbaseURL = baseReleaseURL\n\t\tversionURL = versionReleaseURL\n\tcase AlphaChannel:\n\t\tbreak\n\tdefault: // auto\n\t\tif !strings.HasPrefix(C.Version, \"alpha\") {\n\t\t\tbaseURL = baseReleaseURL\n\t\t\tversionURL = versionReleaseURL\n\t\t}\n\t}\n\n\tlatestVersion, err := u.getLatestVersion(versionURL)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"get latest version: %w\", err)\n\t}\n\tlog.Infoln(\"current version %s, latest version %s\", C.Version, latestVersion)\n\n\tif latestVersion == C.Version && !force {\n\t\t// don't change this output, some downstream dependencies on the upgrader's output fields\n\t\treturn fmt.Errorf(\"update error: already using latest version %s\", C.Version)\n\t}\n\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tlog.Errorln(\"updater: failed: %v\", err)\n\t\t} else {\n\t\t\tlog.Infoln(\"updater: finished\")\n\t\t}\n\t}()\n\n\t// ---- prepare ----\n\tmihomoBaseName := u.CoreBaseName()\n\tpackageName := mihomoBaseName + \"-\" + latestVersion\n\tif runtime.GOOS == \"windows\" {\n\t\tpackageName = packageName + \".zip\"\n\t} else {\n\t\tpackageName = packageName + \".gz\"\n\t}\n\tpackageURL := baseURL + packageName\n\tlog.Infoln(\"updater: updating using url: %s\", packageURL)\n\n\tworkDir := filepath.Dir(currentExePath)\n\tbackupDir := filepath.Join(workDir, \"meta-backup\")\n\tupdateDir := filepath.Join(workDir, \"meta-update\")\n\tpackagePath := filepath.Join(updateDir, packageName)\n\t//log.Infoln(packagePath)\n\n\tupdateExeName := mihomoBaseName\n\tif runtime.GOOS == \"windows\" {\n\t\tupdateExeName = updateExeName + \".exe\"\n\t}\n\tlog.Infoln(\"updateExeName: %s\", updateExeName)\n\tupdateExePath := filepath.Join(updateDir, updateExeName)\n\tbackupExePath := filepath.Join(backupDir, filepath.Base(currentExePath))\n\n\tdefer u.clean(updateDir)\n\n\terr = u.download(updateDir, packagePath, packageURL)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"downloading: %w\", err)\n\t}\n\n\terr = u.unpack(updateDir, packagePath, info.Mode())\n\tif err != nil {\n\t\treturn fmt.Errorf(\"unpacking: %w\", err)\n\t}\n\n\terr = u.backup(currentExePath, backupExePath, backupDir)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"backuping: %w\", err)\n\t}\n\n\terr = u.copyFile(updateExePath, currentExePath)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"replacing: %w\", err)\n\t}\n\n\treturn nil\n}\n\nfunc (u *CoreUpdater) getLatestVersion(versionURL string) (version string, err error) {\n\tctx, cancel := context.WithTimeout(context.Background(), time.Second*5)\n\tdefer cancel()\n\tresp, err := mihomoHttp.HttpRequest(ctx, versionURL, http.MethodGet, nil, nil, mihomoHttp.WithCAOption(ca.Option{ZeroTrust: true}))\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer func() {\n\t\tcloseErr := resp.Body.Close()\n\t\tif closeErr != nil && err == nil {\n\t\t\terr = closeErr\n\t\t}\n\t}()\n\n\tbody, err := io.ReadAll(resp.Body)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tcontent := strings.TrimRight(string(body), \"\\n\")\n\treturn content, nil\n}\n\n// download package file and save it to disk\nfunc (u *CoreUpdater) download(updateDir, packagePath, packageURL string) (err error) {\n\tctx, cancel := context.WithTimeout(context.Background(), time.Second*90)\n\tdefer cancel()\n\tresp, err := mihomoHttp.HttpRequest(ctx, packageURL, http.MethodGet, nil, nil, mihomoHttp.WithCAOption(ca.Option{ZeroTrust: true}))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"http request failed: %w\", err)\n\t}\n\n\tdefer func() {\n\t\tcloseErr := resp.Body.Close()\n\t\tif closeErr != nil && err == nil {\n\t\t\terr = closeErr\n\t\t}\n\t}()\n\n\tlog.Debugln(\"updateDir %s\", updateDir)\n\terr = os.Mkdir(updateDir, 0o755)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"mkdir error: %w\", err)\n\t}\n\n\tlog.Debugln(\"updater: saving package to file %s\", packagePath)\n\t// Create the output file\n\twc, err := os.OpenFile(packagePath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0o755)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"os.OpenFile(%s): %w\", packagePath, err)\n\t}\n\n\tdefer func() {\n\t\tcloseErr := wc.Close()\n\t\tif closeErr != nil && err == nil {\n\t\t\terr = closeErr\n\t\t}\n\t}()\n\n\tlog.Debugln(\"updater: reading http body\")\n\t// This use of io.Copy is now safe, because we limited body's Reader.\n\tn, err := io.Copy(wc, io.LimitReader(resp.Body, MaxPackageFileSize))\n\tif err != nil {\n\t\treturn fmt.Errorf(\"io.Copy(): %w\", err)\n\t}\n\tif n == MaxPackageFileSize {\n\t\t// Use whether n is equal to MaxPackageFileSize to determine whether the limit has been reached.\n\t\t// It is also possible that the size of the downloaded file is exactly the same as the maximum limit,\n\t\t// but we should not consider this too rare situation.\n\t\treturn fmt.Errorf(\"attempted to read more than %d bytes\", MaxPackageFileSize)\n\t}\n\tlog.Debugln(\"updater: downloaded package to file %s\", packagePath)\n\n\treturn nil\n}\n\n// unpack extracts the files from the downloaded archive.\nfunc (u *CoreUpdater) unpack(updateDir, packagePath string, fileMode os.FileMode) error {\n\tlog.Infoln(\"updater: unpacking package\")\n\tif strings.HasSuffix(packagePath, \".zip\") {\n\t\t_, err := u.zipFileUnpack(packagePath, updateDir, fileMode)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\".zip unpack failed: %w\", err)\n\t\t}\n\n\t} else if strings.HasSuffix(packagePath, \".gz\") {\n\t\t_, err := u.gzFileUnpack(packagePath, updateDir, fileMode)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\".gz unpack failed: %w\", err)\n\t\t}\n\n\t} else {\n\t\treturn fmt.Errorf(\"unknown package extension\")\n\t}\n\n\treturn nil\n}\n\n// backup creates a backup of the current executable file.\nfunc (u *CoreUpdater) backup(currentExePath, backupExePath, backupDir string) (err error) {\n\tlog.Infoln(\"updater: backing up current ExecFile:%s to %s\", currentExePath, backupExePath)\n\t_ = os.Mkdir(backupDir, 0o755)\n\n\t// On Windows, since the running executable cannot be overwritten or deleted, it uses os.Rename to move the file to the backup path.\n\t// On other platforms, it copies the file to the backup path, preserving the original file and its permissions.\n\t// The backup directory is created if it does not exist.\n\tif runtime.GOOS == \"windows\" {\n\t\terr = os.Rename(currentExePath, backupExePath)\n\t} else {\n\t\terr = u.copyFile(currentExePath, backupExePath)\n\t}\n\tif err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\n// clean removes the temporary directory itself and all it's contents.\nfunc (u *CoreUpdater) clean(updateDir string) {\n\t_ = os.RemoveAll(updateDir)\n}\n\n// Unpack a single .gz file to the specified directory\n// Existing files are overwritten\n// All files are created inside outDir, subdirectories are not created\n// Return the output file name\nfunc (u *CoreUpdater) gzFileUnpack(gzfile, outDir string, fileMode os.FileMode) (outputName string, err error) {\n\tf, err := os.Open(gzfile)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"os.Open(): %w\", err)\n\t}\n\n\tdefer func() {\n\t\tcloseErr := f.Close()\n\t\tif closeErr != nil && err == nil {\n\t\t\terr = closeErr\n\t\t}\n\t}()\n\n\tgzReader, err := gzip.NewReader(f)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"gzip.NewReader(): %w\", err)\n\t}\n\n\tdefer func() {\n\t\tcloseErr := gzReader.Close()\n\t\tif closeErr != nil && err == nil {\n\t\t\terr = closeErr\n\t\t}\n\t}()\n\t// Get the original file name from the .gz file header\n\toriginalName := gzReader.Header.Name\n\tif originalName == \"\" {\n\t\t// Fallback: remove the .gz extension from the input file name if the header doesn't provide the original name\n\t\toriginalName = filepath.Base(gzfile)\n\t\toriginalName = strings.TrimSuffix(originalName, \".gz\")\n\t}\n\n\toutputName = filepath.Join(outDir, originalName)\n\n\t// Create the output file\n\twc, err := os.OpenFile(outputName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, fileMode)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"os.OpenFile(%s): %w\", outputName, err)\n\t}\n\n\tdefer func() {\n\t\tcloseErr := wc.Close()\n\t\tif closeErr != nil && err == nil {\n\t\t\terr = closeErr\n\t\t}\n\t}()\n\n\t// Copy the contents of the gzReader to the output file\n\t_, err = io.Copy(wc, gzReader)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"io.Copy(): %w\", err)\n\t}\n\n\treturn outputName, nil\n}\n\n// Unpack a single file from .zip file to the specified directory\n// Existing files are overwritten\n// All files are created inside 'outDir', subdirectories are not created\n// Return the output file name\nfunc (u *CoreUpdater) zipFileUnpack(zipfile, outDir string, fileMode os.FileMode) (outputName string, err error) {\n\tzrc, err := zip.OpenReader(zipfile)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"zip.OpenReader(): %w\", err)\n\t}\n\n\tdefer func() {\n\t\tcloseErr := zrc.Close()\n\t\tif closeErr != nil && err == nil {\n\t\t\terr = closeErr\n\t\t}\n\t}()\n\tif len(zrc.File) == 0 {\n\t\treturn \"\", fmt.Errorf(\"no files in the zip archive\")\n\t}\n\n\t// Assuming the first file in the zip archive is the target file\n\tzf := zrc.File[0]\n\tvar rc io.ReadCloser\n\trc, err = zf.Open()\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"zip file Open(): %w\", err)\n\t}\n\n\tdefer func() {\n\t\tcloseErr := rc.Close()\n\t\tif closeErr != nil && err == nil {\n\t\t\terr = closeErr\n\t\t}\n\t}()\n\tfi := zf.FileInfo()\n\tname := fi.Name()\n\toutputName = filepath.Join(outDir, name)\n\n\tif fi.IsDir() {\n\t\treturn \"\", fmt.Errorf(\"the target file is a directory\")\n\t}\n\n\tvar wc io.WriteCloser\n\twc, err = os.OpenFile(outputName, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, fileMode)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"os.OpenFile(): %w\", err)\n\t}\n\n\tdefer func() {\n\t\tcloseErr := wc.Close()\n\t\tif closeErr != nil && err == nil {\n\t\t\terr = closeErr\n\t\t}\n\t}()\n\t_, err = io.Copy(wc, rc)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"io.Copy(): %w\", err)\n\t}\n\n\treturn outputName, nil\n}\n\n// Copy file on disk\nfunc (u *CoreUpdater) copyFile(src, dst string) (err error) {\n\trc, err := os.Open(src)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"os.Open(%s): %w\", src, err)\n\t}\n\n\tdefer func() {\n\t\tcloseErr := rc.Close()\n\t\tif closeErr != nil && err == nil {\n\t\t\terr = closeErr\n\t\t}\n\t}()\n\n\tinfo, err := rc.Stat()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"rc.Stat(): %w\", err)\n\t}\n\n\t// Create the output file\n\t// If the file does not exist, creates it with permissions perm (before umask);\n\t// otherwise truncates it before writing, without changing permissions.\n\twc, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, info.Mode())\n\tif err != nil {\n\t\t// On some file system (such as Android's /data) maybe return error: \"text file busy\"\n\t\t// Let's delete the target file and recreate it\n\t\terr = os.Remove(dst)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"os.Remove(%s): %w\", dst, err)\n\t\t}\n\t\twc, err = os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, info.Mode())\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"os.OpenFile(%s): %w\", dst, err)\n\t\t}\n\t}\n\n\tdefer func() {\n\t\tcloseErr := wc.Close()\n\t\tif closeErr != nil && err == nil {\n\t\t\terr = closeErr\n\t\t}\n\t}()\n\n\t_, err = io.Copy(wc, rc)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"io.Copy(): %w\", err)\n\t}\n\n\tif runtime.GOOS == \"darwin\" {\n\t\terr = exec.Command(\"/usr/bin/codesign\", \"--sign\", \"-\", dst).Run()\n\t\tif err != nil {\n\t\t\tlog.Warnln(\"codesign failed: %v\", err)\n\t\t}\n\t}\n\n\tlog.Infoln(\"updater: copy: %s to %s\", src, dst)\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/updater/update_core_test.go",
    "content": "package updater\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n)\n\nfunc TestCoreBaseName(t *testing.T) {\n\tfmt.Println(\"Core base name =\", DefaultCoreUpdater.CoreBaseName())\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/updater/update_geo.go",
    "content": "package updater\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"os\"\n\t\"runtime\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/atomic\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"github.com/metacubex/mihomo/component/geodata\"\n\t_ \"github.com/metacubex/mihomo/component/geodata/standard\"\n\t\"github.com/metacubex/mihomo/component/mmdb\"\n\t\"github.com/metacubex/mihomo/component/resource\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/oschwald/maxminddb-golang\"\n\t\"golang.org/x/sync/errgroup\"\n)\n\nvar (\n\tautoUpdate     bool\n\tupdateInterval int\n\n\tupdatingGeo atomic.Bool\n)\n\nfunc GeoAutoUpdate() bool {\n\treturn autoUpdate\n}\n\nfunc GeoUpdateInterval() int {\n\treturn updateInterval\n}\n\nfunc SetGeoAutoUpdate(newAutoUpdate bool) {\n\tautoUpdate = newAutoUpdate\n}\n\nfunc SetGeoUpdateInterval(newGeoUpdateInterval int) {\n\tupdateInterval = newGeoUpdateInterval\n}\n\nfunc UpdateMMDB() (err error) {\n\tvehicle := resource.NewHTTPVehicle(geodata.MmdbUrl(), C.Path.MMDB(), \"\", nil, defaultHttpTimeout, 0)\n\tvar oldHash utils.HashType\n\tif buf, err := os.ReadFile(vehicle.Path()); err == nil {\n\t\toldHash = utils.MakeHash(buf)\n\t}\n\tdata, hash, err := vehicle.Read(context.Background(), oldHash)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"can't download MMDB database file: %w\", err)\n\t}\n\tif oldHash.Equal(hash) { // same hash, ignored\n\t\treturn nil\n\t}\n\tif len(data) == 0 {\n\t\treturn fmt.Errorf(\"can't download MMDB database file: no data\")\n\t}\n\n\tinstance, err := maxminddb.FromBytes(data)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid MMDB database file: %s\", err)\n\t}\n\t_ = instance.Close()\n\n\tdefer mmdb.ReloadIP()\n\tmmdb.IPInstance().Reader.Close() //  mmdb is loaded with mmap, so it needs to be closed before overwriting the file\n\tif err = vehicle.Write(data); err != nil {\n\t\treturn fmt.Errorf(\"can't save MMDB database file: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc UpdateASN() (err error) {\n\tvehicle := resource.NewHTTPVehicle(geodata.ASNUrl(), C.Path.ASN(), \"\", nil, defaultHttpTimeout, 0)\n\tvar oldHash utils.HashType\n\tif buf, err := os.ReadFile(vehicle.Path()); err == nil {\n\t\toldHash = utils.MakeHash(buf)\n\t}\n\tdata, hash, err := vehicle.Read(context.Background(), oldHash)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"can't download ASN database file: %w\", err)\n\t}\n\tif oldHash.Equal(hash) { // same hash, ignored\n\t\treturn nil\n\t}\n\tif len(data) == 0 {\n\t\treturn fmt.Errorf(\"can't download ASN database file: no data\")\n\t}\n\n\tinstance, err := maxminddb.FromBytes(data)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"invalid ASN database file: %s\", err)\n\t}\n\t_ = instance.Close()\n\n\tdefer mmdb.ReloadASN()\n\tmmdb.ASNInstance().Reader.Close() //  mmdb is loaded with mmap, so it needs to be closed before overwriting the file\n\tif err = vehicle.Write(data); err != nil {\n\t\treturn fmt.Errorf(\"can't save ASN database file: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc UpdateGeoIp() (err error) {\n\tgeoLoader, err := geodata.GetGeoDataLoader(\"standard\")\n\n\tvehicle := resource.NewHTTPVehicle(geodata.GeoIpUrl(), C.Path.GeoIP(), \"\", nil, defaultHttpTimeout, 0)\n\tvar oldHash utils.HashType\n\tif buf, err := os.ReadFile(vehicle.Path()); err == nil {\n\t\toldHash = utils.MakeHash(buf)\n\t}\n\tdata, hash, err := vehicle.Read(context.Background(), oldHash)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"can't download GeoIP database file: %w\", err)\n\t}\n\tif oldHash.Equal(hash) { // same hash, ignored\n\t\treturn nil\n\t}\n\tif len(data) == 0 {\n\t\treturn fmt.Errorf(\"can't download GeoIP database file: no data\")\n\t}\n\n\tif _, err = geoLoader.LoadIPByBytes(data, \"cn\"); err != nil {\n\t\treturn fmt.Errorf(\"invalid GeoIP database file: %s\", err)\n\t}\n\n\tdefer geodata.ClearGeoIPCache()\n\tif err = vehicle.Write(data); err != nil {\n\t\treturn fmt.Errorf(\"can't save GeoIP database file: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc UpdateGeoSite() (err error) {\n\tgeoLoader, err := geodata.GetGeoDataLoader(\"standard\")\n\n\tvehicle := resource.NewHTTPVehicle(geodata.GeoSiteUrl(), C.Path.GeoSite(), \"\", nil, defaultHttpTimeout, 0)\n\tvar oldHash utils.HashType\n\tif buf, err := os.ReadFile(vehicle.Path()); err == nil {\n\t\toldHash = utils.MakeHash(buf)\n\t}\n\tdata, hash, err := vehicle.Read(context.Background(), oldHash)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"can't download GeoSite database file: %w\", err)\n\t}\n\tif oldHash.Equal(hash) { // same hash, ignored\n\t\treturn nil\n\t}\n\tif len(data) == 0 {\n\t\treturn fmt.Errorf(\"can't download GeoSite database file: no data\")\n\t}\n\n\tif _, err = geoLoader.LoadSiteByBytes(data, \"cn\"); err != nil {\n\t\treturn fmt.Errorf(\"invalid GeoSite database file: %s\", err)\n\t}\n\n\tdefer geodata.ClearGeoSiteCache()\n\tif err = vehicle.Write(data); err != nil {\n\t\treturn fmt.Errorf(\"can't save GeoSite database file: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc updateGeoDatabases() error {\n\tdefer runtime.GC()\n\n\tb := errgroup.Group{}\n\n\tif geodata.GeoIpEnable() {\n\t\tif geodata.GeodataMode() {\n\t\t\tb.Go(UpdateGeoIp)\n\t\t} else {\n\t\t\tb.Go(UpdateMMDB)\n\t\t}\n\t}\n\n\tif geodata.ASNEnable() {\n\t\tb.Go(UpdateASN)\n\t}\n\n\tif geodata.GeoSiteEnable() {\n\t\tb.Go(UpdateGeoSite)\n\t}\n\n\treturn b.Wait()\n}\n\nvar ErrGetDatabaseUpdateSkip = errors.New(\"GEO database is updating, skip\")\n\nfunc UpdateGeoDatabases() error {\n\tlog.Infoln(\"[GEO] Start updating GEO database\")\n\n\tif updatingGeo.Load() {\n\t\treturn ErrGetDatabaseUpdateSkip\n\t}\n\n\tupdatingGeo.Store(true)\n\tdefer updatingGeo.Store(false)\n\n\tlog.Infoln(\"[GEO] Updating GEO database\")\n\n\tif err := updateGeoDatabases(); err != nil {\n\t\tlog.Errorln(\"[GEO] update GEO database error: %s\", err.Error())\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc getUpdateTime() (time time.Time, err error) {\n\tfilesToCheck := []string{\n\t\tC.Path.GeoIP(),\n\t\tC.Path.MMDB(),\n\t\tC.Path.ASN(),\n\t\tC.Path.GeoSite(),\n\t}\n\n\tfor _, file := range filesToCheck {\n\t\tvar fileInfo os.FileInfo\n\t\tfileInfo, err = os.Stat(file)\n\t\tif err == nil {\n\t\t\treturn fileInfo.ModTime(), nil\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc RegisterGeoUpdater() {\n\tif updateInterval <= 0 {\n\t\tlog.Errorln(\"[GEO] Invalid update interval: %d\", updateInterval)\n\t\treturn\n\t}\n\n\tgo func() {\n\t\tticker := time.NewTicker(time.Duration(updateInterval) * time.Hour)\n\t\tdefer ticker.Stop()\n\n\t\tlastUpdate, err := getUpdateTime()\n\t\tif err != nil {\n\t\t\tlog.Errorln(\"[GEO] Get GEO database update time error: %s\", err.Error())\n\t\t\treturn\n\t\t}\n\n\t\tlog.Infoln(\"[GEO] last update time %s\", lastUpdate)\n\t\tif lastUpdate.Add(time.Duration(updateInterval) * time.Hour).Before(time.Now()) {\n\t\t\tlog.Infoln(\"[GEO] Database has not been updated for %v, update now\", time.Duration(updateInterval)*time.Hour)\n\t\t\tif err := UpdateGeoDatabases(); err != nil {\n\t\t\t\tlog.Errorln(\"[GEO] Failed to update GEO database: %s\", err.Error())\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tfor range ticker.C {\n\t\t\tlog.Infoln(\"[GEO] updating database every %d hours\", updateInterval)\n\t\t\tif err := UpdateGeoDatabases(); err != nil {\n\t\t\t\tlog.Errorln(\"[GEO] Failed to update GEO database: %s\", err.Error())\n\t\t\t}\n\t\t}\n\t}()\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/updater/update_ui.go",
    "content": "package updater\n\nimport (\n\t\"archive/tar\"\n\t\"archive/zip\"\n\t\"bytes\"\n\t\"compress/gzip\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\t\"path\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"sync\"\n\t\"syscall\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\ntype UIUpdater struct {\n\texternalUIURL  string\n\texternalUIPath string\n\tautoDownloadUI bool\n\n\tmutex sync.Mutex\n}\n\ntype compressionType int\n\nconst (\n\ttypeUnknown compressionType = iota\n\ttypeZip\n\ttypeTarGzip\n)\n\nfunc (t compressionType) String() string {\n\tswitch t {\n\tcase typeZip:\n\t\treturn \"zip\"\n\tcase typeTarGzip:\n\t\treturn \"tar.gz\"\n\tdefault:\n\t\treturn \"unknown\"\n\t}\n}\n\nvar DefaultUiUpdater = &UIUpdater{}\n\nfunc NewUiUpdater(externalUI, externalUIURL, externalUIName string) *UIUpdater {\n\tupdater := &UIUpdater{}\n\t// checkout externalUI exist\n\tif externalUI != \"\" {\n\t\tupdater.autoDownloadUI = true\n\t\tupdater.externalUIPath = C.Path.Resolve(externalUI)\n\t} else {\n\t\t// default externalUI path\n\t\tupdater.externalUIPath = path.Join(C.Path.HomeDir(), \"ui\")\n\t}\n\n\t// checkout UIpath/name exist\n\tif externalUIName != \"\" {\n\t\tupdater.autoDownloadUI = true\n\t\tupdater.externalUIPath = path.Join(updater.externalUIPath, externalUIName)\n\t}\n\n\tif externalUIURL != \"\" {\n\t\tupdater.externalUIURL = externalUIURL\n\t}\n\treturn updater\n}\n\nfunc (u *UIUpdater) AutoDownloadUI() {\n\tu.mutex.Lock()\n\tdefer u.mutex.Unlock()\n\tif u.autoDownloadUI {\n\t\tdirEntries, _ := os.ReadDir(u.externalUIPath)\n\t\tif len(dirEntries) > 0 {\n\t\t\tlog.Infoln(\"UI already exists, skip downloading\")\n\t\t} else {\n\t\t\tlog.Infoln(\"External UI downloading ...\")\n\t\t\terr := u.downloadUI()\n\t\t\tif err != nil {\n\t\t\t\tlog.Errorln(\"Error downloading UI: %s\", err)\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (u *UIUpdater) DownloadUI() error {\n\tu.mutex.Lock()\n\tdefer u.mutex.Unlock()\n\treturn u.downloadUI()\n}\n\nfunc detectFileType(data []byte) compressionType {\n\tif len(data) < 4 {\n\t\treturn typeUnknown\n\t}\n\n\t// Zip: 0x50 0x4B 0x03 0x04\n\tif data[0] == 0x50 && data[1] == 0x4B && data[2] == 0x03 && data[3] == 0x04 {\n\t\treturn typeZip\n\t}\n\n\t// GZip: 0x1F 0x8B\n\tif data[0] == 0x1F && data[1] == 0x8B {\n\t\treturn typeTarGzip\n\t}\n\n\treturn typeUnknown\n}\n\nfunc (u *UIUpdater) downloadUI() error {\n\tdata, err := downloadForBytes(u.externalUIURL)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"can't download file: %w\", err)\n\t}\n\n\ttmpDir := C.Path.Resolve(\"downloadUI.tmp\")\n\tdefer os.RemoveAll(tmpDir)\n\n\tos.RemoveAll(tmpDir) // cleanup tmp dir before extract\n\tlog.Debugln(\"extractedFolder: %s\", tmpDir)\n\terr = extract(data, tmpDir)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"can't extract compressed file: %w\", err)\n\t}\n\n\tlog.Debugln(\"cleanupFolder: %s\", u.externalUIPath)\n\terr = cleanup(u.externalUIPath) // cleanup files in dir don't remove dir itself\n\tif err != nil {\n\t\tif !os.IsNotExist(err) {\n\t\t\treturn fmt.Errorf(\"cleanup exist file error: %w\", err)\n\t\t}\n\t}\n\n\terr = u.prepareUIPath()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"prepare UI path failed: %w\", err)\n\t}\n\n\tlog.Debugln(\"moveFolder from %s to %s\", tmpDir, u.externalUIPath)\n\terr = moveDir(tmpDir, u.externalUIPath) // move files from tmp to target\n\tif err != nil {\n\t\treturn fmt.Errorf(\"move UI folder failed: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc (u *UIUpdater) prepareUIPath() error {\n\tif _, err := os.Stat(u.externalUIPath); os.IsNotExist(err) {\n\t\tlog.Infoln(\"dir %s does not exist, creating\", u.externalUIPath)\n\t\tif err := os.MkdirAll(u.externalUIPath, os.ModePerm); err != nil {\n\t\t\tlog.Warnln(\"create dir %s error: %s\", u.externalUIPath, err)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc unzip(data []byte, dest string) error {\n\tr, err := zip.NewReader(bytes.NewReader(data), int64(len(data)))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// check whether or not only exists singleRoot dir\n\n\tfor _, f := range r.File {\n\t\tfpath := filepath.Join(dest, f.Name)\n\n\t\tif !inDest(fpath, dest) {\n\t\t\treturn fmt.Errorf(\"invalid file path: %s\", fpath)\n\t\t}\n\t\tinfo := f.FileInfo()\n\t\tif info.IsDir() {\n\t\t\tos.MkdirAll(fpath, os.ModePerm)\n\t\t\tcontinue\n\t\t}\n\t\tif info.Mode()&os.ModeSymlink != 0 {\n\t\t\tcontinue // disallow symlink\n\t\t}\n\t\tif err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {\n\t\t\treturn err\n\t\t}\n\t\toutFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode().Perm())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\trc, err := f.Open()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err = io.Copy(outFile, rc)\n\t\toutFile.Close()\n\t\trc.Close()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc untgz(data []byte, dest string) error {\n\tgzr, err := gzip.NewReader(bytes.NewReader(data))\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer gzr.Close()\n\n\ttr := tar.NewReader(gzr)\n\n\tfor {\n\t\theader, err := tr.Next()\n\t\tif err == io.EOF {\n\t\t\tbreak\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tfpath := filepath.Join(dest, header.Name)\n\n\t\tif !inDest(fpath, dest) {\n\t\t\treturn fmt.Errorf(\"invalid file path: %s\", fpath)\n\t\t}\n\n\t\tswitch header.Typeflag {\n\t\tcase tar.TypeDir:\n\t\t\tif err = os.MkdirAll(fpath, os.FileMode(header.Mode)); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\tcase tar.TypeReg:\n\t\t\tif err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\toutFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, os.FileMode(header.Mode).Perm())\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif _, err := io.Copy(outFile, tr); err != nil {\n\t\t\t\toutFile.Close()\n\t\t\t\treturn err\n\t\t\t}\n\t\t\toutFile.Close()\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc extract(data []byte, dest string) error {\n\tfileType := detectFileType(data)\n\tlog.Debugln(\"compression Type: %s\", fileType)\n\tswitch fileType {\n\tcase typeZip:\n\t\treturn unzip(data, dest)\n\tcase typeTarGzip:\n\t\treturn untgz(data, dest)\n\tdefault:\n\t\treturn fmt.Errorf(\"unknown or unsupported file type\")\n\t}\n}\n\nfunc cleanTarPath(path string) string {\n\t// remove prefix ./ or ../\n\tpath = strings.TrimPrefix(path, \"./\")\n\tpath = strings.TrimPrefix(path, \"../\")\n\n\t// normalize path\n\tpath = filepath.Clean(path)\n\n\t// transfer delimiters to system std\n\tpath = filepath.FromSlash(path)\n\n\t// remove prefix path delimiters\n\tpath = strings.TrimPrefix(path, string(os.PathSeparator))\n\n\treturn path\n}\n\nfunc cleanup(root string) error {\n\tdirEntryList, err := os.ReadDir(root)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, dirEntry := range dirEntryList {\n\t\terr = os.RemoveAll(filepath.Join(root, dirEntry.Name()))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc moveDir(src string, dst string) error {\n\tdirEntryList, err := os.ReadDir(src)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif len(dirEntryList) == 1 && dirEntryList[0].IsDir() {\n\t\tsrc = filepath.Join(src, dirEntryList[0].Name())\n\t\tlog.Debugln(\"match the singleRoot: %s\", src)\n\t\tdirEntryList, err = os.ReadDir(src)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tfor _, dirEntry := range dirEntryList {\n\t\tsrcPath := filepath.Join(src, dirEntry.Name())\n\t\tdstPath := filepath.Join(dst, dirEntry.Name())\n\t\terr = os.Rename(srcPath, dstPath)\n\t\tif err != nil {\n\t\t\t// Fallback for invalid cross-device link (errno:18).\n\t\t\tif errors.Is(err, syscall.Errno(18)) {\n\t\t\t\terr = copyAll(srcPath, dstPath)\n\t\t\t\t_ = os.RemoveAll(srcPath)\n\t\t\t}\n\t\t}\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// copyAll copy the src path and any children it contains to dst\n// modify from [os.CopyFS]\nfunc copyAll(src, dst string) error {\n\treturn filepath.Walk(src, func(path string, info os.FileInfo, err error) error {\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tfpath, err := filepath.Rel(src, path)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tnewPath := filepath.Join(dst, fpath)\n\n\t\tswitch info.Mode().Type() {\n\t\tcase os.ModeDir:\n\t\t\treturn os.MkdirAll(newPath, info.Mode().Perm())\n\t\tcase os.ModeSymlink:\n\t\t\ttarget, err := os.Readlink(path)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn os.Symlink(target, newPath)\n\t\tcase 0:\n\t\t\tr, err := os.Open(path)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tdefer r.Close()\n\t\t\tw, err := os.OpenFile(newPath, os.O_CREATE|os.O_EXCL|os.O_WRONLY, info.Mode().Perm())\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tif _, err := io.Copy(w, r); err != nil {\n\t\t\t\tw.Close()\n\t\t\t\treturn &os.PathError{Op: \"Copy\", Path: newPath, Err: err}\n\t\t\t}\n\t\t\treturn w.Close()\n\t\tdefault:\n\t\t\treturn &os.PathError{Op: \"CopyFS\", Path: path, Err: os.ErrInvalid}\n\t\t}\n\t})\n}\n\nfunc inDest(fpath, dest string) bool {\n\tif rel, err := filepath.Rel(dest, fpath); err == nil {\n\t\tif filepath.IsLocal(rel) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/updater/utils.go",
    "content": "package updater\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"os\"\n\t\"time\"\n\n\tmihomoHttp \"github.com/metacubex/mihomo/component/http\"\n\n\t\"github.com/metacubex/http\"\n)\n\nconst defaultHttpTimeout = time.Second * 90\n\nfunc downloadForBytes(url string) ([]byte, error) {\n\tctx, cancel := context.WithTimeout(context.Background(), defaultHttpTimeout)\n\tdefer cancel()\n\tresp, err := mihomoHttp.HttpRequest(ctx, url, http.MethodGet, nil, nil)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer resp.Body.Close()\n\n\treturn io.ReadAll(resp.Body)\n}\n\nfunc saveFile(bytes []byte, path string) error {\n\treturn os.WriteFile(path, bytes, 0o644)\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/wildcard/wildcard.go",
    "content": "// Package wildcard modified IGLOU-EU/go-wildcard to support:\n//\n//\t`*` matches zero or more characters\n//\t`?` matches exactly one character\n//\n// The original go-wildcard library used `.` to match exactly one character, and `?` to match zero or one character.\n// `.` is a valid delimiter in domain name matching and should not be used as a wildcard.\n// The `?` matching logic strictly matches only one character in most scenarios.\n// So, the `?` matching logic in the original go-wildcard library has been removed and its wildcard `.` has been replaced with `?`.\npackage wildcard\n\n// copy and modified from https://github.com/IGLOU-EU/go-wildcard/tree/ce22b7af48e487517a492d3727d9386492043e21\n// which is licensed under OpenBSD's ISC-style license.\n// Copyright (c) 2023 Iglou.eu contact@iglou.eu Copyright (c) 2023 Adrien Kara adrien@iglou.eu\n\nfunc Match(pattern, s string) bool {\n\tif pattern == \"\" {\n\t\treturn s == pattern\n\t}\n\tif pattern == \"*\" || s == pattern {\n\t\treturn true\n\t}\n\n\treturn matchByString(pattern, s)\n}\n\nfunc matchByString(pattern, s string) bool {\n\tvar patternIndex, sIndex, lastStar int\n\tpatternLen := len(pattern)\n\tsLen := len(s)\n\tstar := -1\n\nLoop:\n\tif sIndex >= sLen {\n\t\tgoto checkPattern\n\t}\n\n\tif patternIndex >= patternLen {\n\t\tif star != -1 {\n\t\t\tpatternIndex = star + 1\n\t\t\tlastStar++\n\t\t\tsIndex = lastStar\n\t\t\tgoto Loop\n\t\t}\n\t\treturn false\n\t}\n\tswitch pattern[patternIndex] {\n\tcase '?':\n\t\t// It matches any single character. So, we don't need to check anything.\n\tcase '*':\n\t\t// '*' matches zero or more characters. Store its position and increment the pattern index.\n\t\tstar = patternIndex\n\t\tlastStar = sIndex\n\t\tpatternIndex++\n\t\tgoto Loop\n\tdefault:\n\t\t// If the characters don't match, check if there was a previous '*' to backtrack.\n\t\tif pattern[patternIndex] != s[sIndex] {\n\t\t\tif star != -1 {\n\t\t\t\tpatternIndex = star + 1\n\t\t\t\tlastStar++\n\t\t\t\tsIndex = lastStar\n\t\t\t\tgoto Loop\n\t\t\t}\n\n\t\t\treturn false\n\t\t}\n\t}\n\n\tpatternIndex++\n\tsIndex++\n\tgoto Loop\n\n\t// Check if the remaining pattern characters are '*', which can match the end of the string.\ncheckPattern:\n\tif patternIndex < patternLen {\n\t\tif pattern[patternIndex] == '*' {\n\t\t\tpatternIndex++\n\t\t\tgoto checkPattern\n\t\t}\n\t}\n\n\treturn patternIndex == patternLen\n}\n"
  },
  {
    "path": "core/Clash.Meta/component/wildcard/wildcard_test.go",
    "content": "package wildcard\n\n/*\n * copy and modified from https://github.com/IGLOU-EU/go-wildcard/tree/ce22b7af48e487517a492d3727d9386492043e21\n *\n * Copyright (c) 2023 Iglou.eu <contact@iglou.eu>\n * Copyright (c) 2023 Adrien Kara <adrien@iglou.eu>\n *\n * Licensed under the BSD 3-Clause License,\n */\n\nimport (\n\t\"testing\"\n)\n\n// TestMatch validates the logic of wild card matching,\n// it need to support '*', '?' and only validate for byte comparison\n// over string, not rune or grapheme cluster\nfunc TestMatch(t *testing.T) {\n\tcases := []struct {\n\t\ts       string\n\t\tpattern string\n\t\tresult  bool\n\t}{\n\t\t{\"\", \"\", true},\n\t\t{\"\", \"*\", true},\n\t\t{\"\", \"**\", true},\n\t\t{\"\", \"?\", false},\n\t\t{\"\", \"?*\", false},\n\t\t{\"\", \"*?\", false},\n\n\t\t{\"a\", \"\", false},\n\t\t{\"a\", \"a\", true},\n\t\t{\"a\", \"*\", true},\n\t\t{\"a\", \"**\", true},\n\t\t{\"a\", \"?\", true},\n\t\t{\"a\", \"?*\", true},\n\t\t{\"a\", \"*?\", true},\n\n\t\t{\"match the exact string\", \"match the exact string\", true},\n\t\t{\"do not match a different string\", \"this is a different string\", false},\n\t\t{\"Match The Exact String WITH DIFFERENT CASE\", \"Match The Exact String WITH DIFFERENT CASE\", true},\n\t\t{\"do not match a different string WITH DIFFERENT CASE\", \"this is a different string WITH DIFFERENT CASE\", false},\n\t\t{\"Do Not Match The Exact String With Different Case\", \"do not match the exact string with different case\", false},\n\t\t{\"match an emoji 😃\", \"match an emoji 😃\", true},\n\t\t{\"do not match because of different emoji 😃\", \"do not match because of different emoji 😄\", false},\n\t\t{\"🌅☕️📰👨‍💼👩‍💼🏢🖥️💼💻📊📈📉👨‍👩‍👧‍👦🍝🕰️💪🏋️‍♂️🏋️‍♀️🏋️‍♂️💼🚴‍♂️🚴‍♀️🚴‍♂️🛀💤🌃\", \"🌅☕️📰👨‍💼👩‍💼🏢🖥️💼💻📊📈📉👨‍👩‍👧‍👦🍝🕰️💪🏋️‍♂️🏋️‍♀️🏋️‍♂️💼🚴‍♂️🚴‍♀️🚴‍♂️🛀💤🌃\", true},\n\t\t{\"🌅☕️📰👨‍💼👩‍💼🏢🖥️💼💻📊📈📉👨‍👩‍👧‍👦🍝🕰️💪🏋️‍♂️🏋️‍♀️🏋️‍♂️💼🚴‍♂️🚴‍♀️🚴‍♂️🛀💤🌃\", \"🦌🐇🦡🐿️🌲🌳🏰🌳🌲🌞🌧️❄️🌬️⛈️🔥🎄🎅🎁🎉🎊🥳👨‍👩‍👧‍👦💏👪💖👩‍💼🛀\", false},\n\n\t\t{\"match a string with a *\", \"match a string *\", true},\n\t\t{\"match a string with a * at the beginning\", \"* at the beginning\", true},\n\t\t{\"match a string with two *\", \"match * with *\", true},\n\t\t{\"do not match a string with extra and a *\", \"do not match a string * with more\", false},\n\n\t\t{\"match a string with a ?\", \"match ? string with a ?\", true},\n\t\t{\"match a string with a ? at the beginning\", \"?atch a string with a ? at the beginning\", true},\n\t\t{\"match a string with two ?\", \"match a ??ring with two ?\", true},\n\t\t{\"do not match a string with extra ?\", \"do not match a string with extra ??\", false},\n\n\t\t{\"abc.edf.hjg\", \"abc.edf.hjg\", true},\n\t\t{\"abc.edf.hjg\", \"ab.cedf.hjg\", false},\n\t\t{\"abc.edf.hjg\", \"abc.edfh.jg\", false},\n\t\t{\"abc.edf.hjg\", \"abc.edf.hjq\", false},\n\n\t\t{\"abc.edf.hjg\", \"abc.*.hjg\", true},\n\t\t{\"abc.edf.hjg\", \"abc.*.hjq\", false},\n\t\t{\"abc.edf.hjg\", \"abc*hjg\", true},\n\t\t{\"abc.edf.hjg\", \"abc*hjq\", false},\n\t\t{\"abc.edf.hjg\", \"a*g\", true},\n\t\t{\"abc.edf.hjg\", \"a*q\", false},\n\n\t\t{\"abc.edf.hjg\", \"ab?.edf.hjg\", true},\n\t\t{\"abc.edf.hjg\", \"?b?.edf.hjg\", true},\n\t\t{\"abc.edf.hjg\", \"??c.edf.hjg\", true},\n\t\t{\"abc.edf.hjg\", \"a??.edf.hjg\", true},\n\t\t{\"abc.edf.hjg\", \"ab??.edf.hjg\", false},\n\t\t{\"abc.edf.hjg\", \"??.edf.hjg\", false},\n\t}\n\n\tfor i, c := range cases {\n\t\tt.Run(c.s, func(t *testing.T) {\n\t\t\tresult := Match(c.pattern, c.s)\n\t\t\tif c.result != result {\n\t\t\t\tt.Errorf(\"Test %d: Expected `%v`, found `%v`; With Pattern: `%s` and String: `%s`\", i+1, c.result, result, c.pattern, c.s)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc match(pattern, name string) bool { // https://research.swtch.com/glob\n\tpx := 0\n\tnx := 0\n\tnextPx := 0\n\tnextNx := 0\n\tfor px < len(pattern) || nx < len(name) {\n\t\tif px < len(pattern) {\n\t\t\tc := pattern[px]\n\t\t\tswitch c {\n\t\t\tdefault: // ordinary character\n\t\t\t\tif nx < len(name) && name[nx] == c {\n\t\t\t\t\tpx++\n\t\t\t\t\tnx++\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\tcase '?': // single-character wildcard\n\t\t\t\tif nx < len(name) {\n\t\t\t\t\tpx++\n\t\t\t\t\tnx++\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\tcase '*': // zero-or-more-character wildcard\n\t\t\t\t// Try to match at nx.\n\t\t\t\t// If that doesn't work out,\n\t\t\t\t// restart at nx+1 next.\n\t\t\t\tnextPx = px\n\t\t\t\tnextNx = nx + 1\n\t\t\t\tpx++\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\t// Mismatch. Maybe restart.\n\t\tif 0 < nextNx && nextNx <= len(name) {\n\t\t\tpx = nextPx\n\t\t\tnx = nextNx\n\t\t\tcontinue\n\t\t}\n\t\treturn false\n\t}\n\t// Matched all of pattern to all of name. Success.\n\treturn true\n}\n\nfunc FuzzMatch(f *testing.F) {\n\tf.Fuzz(func(t *testing.T, pattern, name string) {\n\t\tresult1 := Match(pattern, name)\n\t\tresult2 := match(pattern, name)\n\t\tif result1 != result2 {\n\t\t\tt.Fatalf(\"Match failed for pattern `%s` and name `%s`\", pattern, name)\n\t\t}\n\t})\n}\n\nfunc BenchmarkMatch(b *testing.B) {\n\tcases := []struct {\n\t\ts       string\n\t\tpattern string\n\t\tresult  bool\n\t}{\n\t\t{\"abc.edf.hjg\", \"abc.edf.hjg\", true},\n\t\t{\"abc.edf.hjg\", \"ab.cedf.hjg\", false},\n\t\t{\"abc.edf.hjg\", \"abc.edfh.jg\", false},\n\t\t{\"abc.edf.hjg\", \"abc.edf.hjq\", false},\n\n\t\t{\"abc.edf.hjg\", \"abc.*.hjg\", true},\n\t\t{\"abc.edf.hjg\", \"abc.*.hjq\", false},\n\t\t{\"abc.edf.hjg\", \"abc*hjg\", true},\n\t\t{\"abc.edf.hjg\", \"abc*hjq\", false},\n\t\t{\"abc.edf.hjg\", \"a*g\", true},\n\t\t{\"abc.edf.hjg\", \"a*q\", false},\n\n\t\t{\"abc.edf.hjg\", \"ab?.edf.hjg\", true},\n\t\t{\"abc.edf.hjg\", \"?b?.edf.hjg\", true},\n\t\t{\"abc.edf.hjg\", \"??c.edf.hjg\", true},\n\t\t{\"abc.edf.hjg\", \"a??.edf.hjg\", true},\n\t\t{\"abc.edf.hjg\", \"ab??.edf.hjg\", false},\n\t\t{\"abc.edf.hjg\", \"??.edf.hjg\", false},\n\n\t\t{\"r4.cdn-aa-wow-this-is-long-a1.video-yajusenpai1145141919810-oh-hell-yeah-this-is-also-very-long-and-sukka-the-fox-has-a-very-big-fluffy-fox-tail-ao-wu-ao-wu-regex-and-wildcard-both-might-have-deadly-back-tracing-issue-be-careful-or-use-linear-matching.com\", \"*.cdn-*-*.video**.com\", true},\n\t}\n\n\tb.Run(\"Match\", func(b *testing.B) {\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\tfor _, c := range cases {\n\t\t\t\tresult := Match(c.pattern, c.s)\n\t\t\t\tif c.result != result {\n\t\t\t\t\tb.Errorf(\"Test %d: Expected `%v`, found `%v`; With Pattern: `%s` and String: `%s`\", i+1, c.result, result, c.pattern, c.s)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t})\n\n\tb.Run(\"match\", func(b *testing.B) {\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\tfor _, c := range cases {\n\t\t\t\tresult := match(c.pattern, c.s)\n\t\t\t\tif c.result != result {\n\t\t\t\t\tb.Errorf(\"Test %d: Expected `%v`, found `%v`; With Pattern: `%s` and String: `%s`\", i+1, c.result, result, c.pattern, c.s)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "core/Clash.Meta/config/config.go",
    "content": "package config\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/netip\"\n\t\"net/url\"\n\t\"path/filepath\"\n\t\"strings\"\n\t\"time\"\n\t_ \"unsafe\"\n\n\t\"github.com/metacubex/mihomo/adapter\"\n\t\"github.com/metacubex/mihomo/adapter/outbound\"\n\t\"github.com/metacubex/mihomo/adapter/outboundgroup\"\n\t\"github.com/metacubex/mihomo/adapter/provider\"\n\t\"github.com/metacubex/mihomo/common/orderedmap\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"github.com/metacubex/mihomo/common/yaml\"\n\t\"github.com/metacubex/mihomo/component/auth\"\n\t\"github.com/metacubex/mihomo/component/cidr\"\n\t\"github.com/metacubex/mihomo/component/fakeip\"\n\t\"github.com/metacubex/mihomo/component/geodata\"\n\t\"github.com/metacubex/mihomo/component/process\"\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\t\"github.com/metacubex/mihomo/component/sniffer\"\n\t\"github.com/metacubex/mihomo/component/trie\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tP \"github.com/metacubex/mihomo/constant/provider\"\n\tsnifferTypes \"github.com/metacubex/mihomo/constant/sniffer\"\n\t\"github.com/metacubex/mihomo/dns\"\n\t\"github.com/metacubex/mihomo/listener\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/log\"\n\tR \"github.com/metacubex/mihomo/rules\"\n\tRC \"github.com/metacubex/mihomo/rules/common\"\n\tRP \"github.com/metacubex/mihomo/rules/provider\"\n\tRW \"github.com/metacubex/mihomo/rules/wrapper\"\n\tT \"github.com/metacubex/mihomo/tunnel\"\n\n\t\"golang.org/x/exp/slices\"\n)\n\n// General config\ntype General struct {\n\tInbound\n\tMode                    T.TunnelMode            `json:\"mode\"`\n\tUnifiedDelay            bool                    `json:\"unified-delay\"`\n\tLogLevel                log.LogLevel            `json:\"log-level\"`\n\tIPv6                    bool                    `json:\"ipv6\"`\n\tInterface               string                  `json:\"interface-name\"`\n\tRoutingMark             int                     `json:\"routing-mark\"`\n\tGeoXUrl                 GeoXUrl                 `json:\"geox-url\"`\n\tGeoAutoUpdate           bool                    `json:\"geo-auto-update\"`\n\tGeoUpdateInterval       int                     `json:\"geo-update-interval\"`\n\tGeodataMode             bool                    `json:\"geodata-mode\"`\n\tGeodataLoader           string                  `json:\"geodata-loader\"`\n\tGeositeMatcher          string                  `json:\"geosite-matcher\"`\n\tTCPConcurrent           bool                    `json:\"tcp-concurrent\"`\n\tFindProcessMode         process.FindProcessMode `json:\"find-process-mode\"`\n\tSniffing                bool                    `json:\"sniffing\"`\n\tGlobalClientFingerprint string                  `json:\"global-client-fingerprint\"`\n\tGlobalUA                string                  `json:\"global-ua\"`\n\tETagSupport             bool                    `json:\"etag-support\"`\n\tKeepAliveIdle           int                     `json:\"keep-alive-idle\"`\n\tKeepAliveInterval       int                     `json:\"keep-alive-interval\"`\n\tDisableKeepAlive        bool                    `json:\"disable-keep-alive\"`\n}\n\n// Inbound config\ntype Inbound struct {\n\tPort              int            `json:\"port\"`\n\tSocksPort         int            `json:\"socks-port\"`\n\tRedirPort         int            `json:\"redir-port\"`\n\tTProxyPort        int            `json:\"tproxy-port\"`\n\tMixedPort         int            `json:\"mixed-port\"`\n\tTun               LC.Tun         `json:\"tun\"`\n\tTuicServer        LC.TuicServer  `json:\"tuic-server\"`\n\tShadowSocksConfig string         `json:\"ss-config\"`\n\tVmessConfig       string         `json:\"vmess-config\"`\n\tAuthentication    []string       `json:\"authentication\"`\n\tSkipAuthPrefixes  []netip.Prefix `json:\"skip-auth-prefixes\"`\n\tLanAllowedIPs     []netip.Prefix `json:\"lan-allowed-ips\"`\n\tLanDisAllowedIPs  []netip.Prefix `json:\"lan-disallowed-ips\"`\n\tAllowLan          bool           `json:\"allow-lan\"`\n\tBindAddress       string         `json:\"bind-address\"`\n\tInboundTfo        bool           `json:\"inbound-tfo\"`\n\tInboundMPTCP      bool           `json:\"inbound-mptcp\"`\n}\n\n// GeoXUrl config\ntype GeoXUrl struct {\n\tGeoIp   string `json:\"geo-ip\"`\n\tMmdb    string `json:\"mmdb\"`\n\tASN     string `json:\"asn\"`\n\tGeoSite string `json:\"geo-site\"`\n}\n\n// Controller config\ntype Controller struct {\n\tExternalController     string\n\tExternalControllerTLS  string\n\tExternalControllerUnix string\n\tExternalControllerPipe string\n\tExternalUI             string\n\tExternalUIURL          string\n\tExternalUIName         string\n\tExternalDohServer      string\n\tSecret                 string\n\tCors                   Cors\n}\n\ntype Cors struct {\n\tAllowOrigins        []string\n\tAllowPrivateNetwork bool\n}\n\n// Experimental config\ntype Experimental struct {\n\tQUICGoDisableGSO bool\n\tQUICGoDisableECN bool\n\tIP4PEnable       bool\n}\n\n// IPTables config\ntype IPTables struct {\n\tEnable           bool\n\tInboundInterface string\n\tBypass           []string\n\tDnsRedirect      bool\n}\n\n// NTP config\ntype NTP struct {\n\tEnable        bool\n\tServer        string\n\tPort          int\n\tInterval      int\n\tDialerProxy   string\n\tWriteToSystem bool\n}\n\n// DNS config\ntype DNS struct {\n\tEnable                bool\n\tPreferH3              bool\n\tIPv6                  bool\n\tIPv6Timeout           uint\n\tUseHosts              bool\n\tUseSystemHosts        bool\n\tNameServer            []dns.NameServer\n\tFallback              []dns.NameServer\n\tFallbackIPFilter      []C.IpMatcher\n\tFallbackDomainFilter  []C.DomainMatcher\n\tListen                string\n\tEnhancedMode          C.DNSMode\n\tDefaultNameserver     []dns.NameServer\n\tCacheAlgorithm        string\n\tCacheMaxSize          int\n\tFakeIPRange           netip.Prefix\n\tFakeIPPool            *fakeip.Pool\n\tFakeIPRange6          netip.Prefix\n\tFakeIPPool6           *fakeip.Pool\n\tFakeIPSkipper         *fakeip.Skipper\n\tFakeIPTTL             int\n\tNameServerPolicy      []dns.Policy\n\tProxyServerNameserver []dns.NameServer\n\tProxyServerPolicy     []dns.Policy\n\tDirectNameServer      []dns.NameServer\n\tDirectFollowPolicy    bool\n}\n\n// Profile config\ntype Profile struct {\n\tStoreSelected bool\n\tStoreFakeIP   bool\n}\n\n// TLS config\ntype TLS struct {\n\tCertificate     string\n\tPrivateKey      string\n\tClientAuthType  string\n\tClientAuthCert  string\n\tEchKey          string\n\tCustomTrustCert []string\n}\n\n// Config is mihomo config manager\ntype Config struct {\n\tGeneral       *General\n\tController    *Controller\n\tExperimental  *Experimental\n\tIPTables      *IPTables\n\tNTP           *NTP\n\tDNS           *DNS\n\tHosts         *trie.DomainTrie[resolver.HostValue]\n\tProfile       *Profile\n\tRules         []C.Rule\n\tSubRules      map[string][]C.Rule\n\tUsers         []auth.AuthUser\n\tProxies       map[string]C.Proxy\n\tListeners     map[string]C.InboundListener\n\tProviders     map[string]P.ProxyProvider\n\tRuleProviders map[string]P.RuleProvider\n\tTunnels       []LC.Tunnel\n\tSniffer       *sniffer.Config\n\tTLS           *TLS\n}\n\ntype RawCors struct {\n\tAllowOrigins        []string `yaml:\"allow-origins\" json:\"allow-origins\"`\n\tAllowPrivateNetwork bool     `yaml:\"allow-private-network\" json:\"allow-private-network\"`\n}\n\ntype RawDNS struct {\n\tEnable                       bool                                `yaml:\"enable\" json:\"enable\"`\n\tPreferH3                     bool                                `yaml:\"prefer-h3\" json:\"prefer-h3\"`\n\tIPv6                         bool                                `yaml:\"ipv6\" json:\"ipv6\"`\n\tIPv6Timeout                  uint                                `yaml:\"ipv6-timeout\" json:\"ipv6-timeout\"`\n\tUseHosts                     bool                                `yaml:\"use-hosts\" json:\"use-hosts\"`\n\tUseSystemHosts               bool                                `yaml:\"use-system-hosts\" json:\"use-system-hosts\"`\n\tRespectRules                 bool                                `yaml:\"respect-rules\" json:\"respect-rules\"`\n\tNameServer                   []string                            `yaml:\"nameserver\" json:\"nameserver\"`\n\tFallback                     []string                            `yaml:\"fallback\" json:\"fallback\"`\n\tFallbackFilter               RawFallbackFilter                   `yaml:\"fallback-filter\" json:\"fallback-filter\"`\n\tListen                       string                              `yaml:\"listen\" json:\"listen\"`\n\tEnhancedMode                 C.DNSMode                           `yaml:\"enhanced-mode\" json:\"enhanced-mode\"`\n\tFakeIPRange                  string                              `yaml:\"fake-ip-range\" json:\"fake-ip-range\"`\n\tFakeIPRange6                 string                              `yaml:\"fake-ip-range6\" json:\"fake-ip-range6\"`\n\tFakeIPFilter                 []string                            `yaml:\"fake-ip-filter\" json:\"fake-ip-filter\"`\n\tFakeIPFilterMode             C.FilterMode                        `yaml:\"fake-ip-filter-mode\" json:\"fake-ip-filter-mode\"`\n\tFakeIPTTL                    int                                 `yaml:\"fake-ip-ttl\" json:\"fake-ip-ttl\"`\n\tDefaultNameserver            []string                            `yaml:\"default-nameserver\" json:\"default-nameserver\"`\n\tCacheAlgorithm               string                              `yaml:\"cache-algorithm\" json:\"cache-algorithm\"`\n\tCacheMaxSize                 int                                 `yaml:\"cache-max-size\" json:\"cache-max-size\"`\n\tNameServerPolicy             *orderedmap.OrderedMap[string, any] `yaml:\"nameserver-policy\" json:\"nameserver-policy\"`\n\tProxyServerNameserver        []string                            `yaml:\"proxy-server-nameserver\" json:\"proxy-server-nameserver\"`\n\tProxyServerNameserverPolicy  *orderedmap.OrderedMap[string, any] `yaml:\"proxy-server-nameserver-policy\" json:\"proxy-server-nameserver-policy\"`\n\tDirectNameServer             []string                            `yaml:\"direct-nameserver\" json:\"direct-nameserver\"`\n\tDirectNameServerFollowPolicy bool                                `yaml:\"direct-nameserver-follow-policy\" json:\"direct-nameserver-follow-policy\"`\n}\n\ntype RawFallbackFilter struct {\n\tGeoIP     bool     `yaml:\"geoip\" json:\"geoip\"`\n\tGeoIPCode string   `yaml:\"geoip-code\" json:\"geoip-code\"`\n\tIPCIDR    []string `yaml:\"ipcidr\" json:\"ipcidr\"`\n\tDomain    []string `yaml:\"domain\" json:\"domain\"`\n\tGeoSite   []string `yaml:\"geosite\" json:\"geosite\"`\n}\n\ntype RawClashForAndroid struct {\n\tAppendSystemDNS   bool   `yaml:\"append-system-dns\" json:\"append-system-dns\"`\n\tUiSubtitlePattern string `yaml:\"ui-subtitle-pattern\" json:\"ui-subtitle-pattern\"`\n}\n\ntype RawNTP struct {\n\tEnable        bool   `yaml:\"enable\" json:\"enable\"`\n\tServer        string `yaml:\"server\" json:\"server\"`\n\tPort          int    `yaml:\"port\" json:\"port\"`\n\tInterval      int    `yaml:\"interval\" json:\"interval\"`\n\tDialerProxy   string `yaml:\"dialer-proxy\" json:\"dialer-proxy\"`\n\tWriteToSystem bool   `yaml:\"write-to-system\" json:\"write-to-system\"`\n}\n\ntype RawTun struct {\n\tEnable              bool       `yaml:\"enable\" json:\"enable\"`\n\tDevice              string     `yaml:\"device\" json:\"device\"`\n\tStack               C.TUNStack `yaml:\"stack\" json:\"stack\"`\n\tDNSHijack           []string   `yaml:\"dns-hijack\" json:\"dns-hijack\"`\n\tAutoRoute           bool       `yaml:\"auto-route\" json:\"auto-route\"`\n\tAutoDetectInterface bool       `yaml:\"auto-detect-interface\"`\n\n\tMTU        uint32 `yaml:\"mtu\" json:\"mtu,omitempty\"`\n\tGSO        bool   `yaml:\"gso\" json:\"gso,omitempty\"`\n\tGSOMaxSize uint32 `yaml:\"gso-max-size\" json:\"gso-max-size,omitempty\"`\n\t//Inet4Address           []netip.Prefix `yaml:\"inet4-address\" json:\"inet4-address,omitempty\"`\n\tInet6Address                          []netip.Prefix `yaml:\"inet6-address\" json:\"inet6-address,omitempty\"`\n\tIPRoute2TableIndex                    int            `yaml:\"iproute2-table-index\" json:\"iproute2-table-index,omitempty\"`\n\tIPRoute2RuleIndex                     int            `yaml:\"iproute2-rule-index\" json:\"iproute2-rule-index,omitempty\"`\n\tAutoRedirect                          bool           `yaml:\"auto-redirect\" json:\"auto-redirect,omitempty\"`\n\tAutoRedirectInputMark                 uint32         `yaml:\"auto-redirect-input-mark\" json:\"auto-redirect-input-mark,omitempty\"`\n\tAutoRedirectOutputMark                uint32         `yaml:\"auto-redirect-output-mark\" json:\"auto-redirect-output-mark,omitempty\"`\n\tAutoRedirectIPRoute2FallbackRuleIndex int            `yaml:\"auto-redirect-iproute2-fallback-rule-index\" json:\"auto-redirect-iproute2-fallback-rule-index,omitempty\"`\n\tLoopbackAddress                       []netip.Addr   `yaml:\"loopback-address\" json:\"loopback-address,omitempty\"`\n\tStrictRoute                           bool           `yaml:\"strict-route\" json:\"strict-route,omitempty\"`\n\tRouteAddress                          []netip.Prefix `yaml:\"route-address\" json:\"route-address,omitempty\"`\n\tRouteAddressSet                       []string       `yaml:\"route-address-set\" json:\"route-address-set,omitempty\"`\n\tRouteExcludeAddress                   []netip.Prefix `yaml:\"route-exclude-address\" json:\"route-exclude-address,omitempty\"`\n\tRouteExcludeAddressSet                []string       `yaml:\"route-exclude-address-set\" json:\"route-exclude-address-set,omitempty\"`\n\tIncludeInterface                      []string       `yaml:\"include-interface\" json:\"include-interface,omitempty\"`\n\tExcludeInterface                      []string       `yaml:\"exclude-interface\" json:\"exclude-interface,omitempty\"`\n\tIncludeUID                            []uint32       `yaml:\"include-uid\" json:\"include-uid,omitempty\"`\n\tIncludeUIDRange                       []string       `yaml:\"include-uid-range\" json:\"include-uid-range,omitempty\"`\n\tExcludeUID                            []uint32       `yaml:\"exclude-uid\" json:\"exclude-uid,omitempty\"`\n\tExcludeUIDRange                       []string       `yaml:\"exclude-uid-range\" json:\"exclude-uid-range,omitempty\"`\n\tExcludeSrcPort                        []uint16       `yaml:\"exclude-src-port\" json:\"exclude-src-port,omitempty\"`\n\tExcludeSrcPortRange                   []string       `yaml:\"exclude-src-port-range\" json:\"exclude-src-port-range,omitempty\"`\n\tExcludeDstPort                        []uint16       `yaml:\"exclude-dst-port\" json:\"exclude-dst-port,omitempty\"`\n\tExcludeDstPortRange                   []string       `yaml:\"exclude-dst-port-range\" json:\"exclude-dst-port-range,omitempty\"`\n\tIncludeAndroidUser                    []int          `yaml:\"include-android-user\" json:\"include-android-user,omitempty\"`\n\tIncludePackage                        []string       `yaml:\"include-package\" json:\"include-package,omitempty\"`\n\tExcludePackage                        []string       `yaml:\"exclude-package\" json:\"exclude-package,omitempty\"`\n\tEndpointIndependentNat                bool           `yaml:\"endpoint-independent-nat\" json:\"endpoint-independent-nat,omitempty\"`\n\tUDPTimeout                            int64          `yaml:\"udp-timeout\" json:\"udp-timeout,omitempty\"`\n\tDisableICMPForwarding                 bool           `yaml:\"disable-icmp-forwarding\" json:\"disable-icmp-forwarding,omitempty\"`\n\tFileDescriptor                        int            `yaml:\"file-descriptor\" json:\"file-descriptor\"`\n\n\tInet4RouteAddress        []netip.Prefix `yaml:\"inet4-route-address\" json:\"inet4-route-address,omitempty\"`\n\tInet6RouteAddress        []netip.Prefix `yaml:\"inet6-route-address\" json:\"inet6-route-address,omitempty\"`\n\tInet4RouteExcludeAddress []netip.Prefix `yaml:\"inet4-route-exclude-address\" json:\"inet4-route-exclude-address,omitempty\"`\n\tInet6RouteExcludeAddress []netip.Prefix `yaml:\"inet6-route-exclude-address\" json:\"inet6-route-exclude-address,omitempty\"`\n\n\t// darwin special config\n\tRecvMsgX bool `yaml:\"recvmsgx\" json:\"recvmsgx,omitempty\"`\n\tSendMsgX bool `yaml:\"sendmsgx\" json:\"sendmsgx,omitempty\"`\n}\n\ntype RawTuicServer struct {\n\tEnable                bool              `yaml:\"enable\" json:\"enable\"`\n\tListen                string            `yaml:\"listen\" json:\"listen\"`\n\tToken                 []string          `yaml:\"token\" json:\"token\"`\n\tUsers                 map[string]string `yaml:\"users\" json:\"users,omitempty\"`\n\tCertificate           string            `yaml:\"certificate\" json:\"certificate\"`\n\tPrivateKey            string            `yaml:\"private-key\" json:\"private-key\"`\n\tCongestionController  string            `yaml:\"congestion-controller\" json:\"congestion-controller,omitempty\"`\n\tMaxIdleTime           int               `yaml:\"max-idle-time\" json:\"max-idle-time,omitempty\"`\n\tAuthenticationTimeout int               `yaml:\"authentication-timeout\" json:\"authentication-timeout,omitempty\"`\n\tALPN                  []string          `yaml:\"alpn\" json:\"alpn,omitempty\"`\n\tMaxUdpRelayPacketSize int               `yaml:\"max-udp-relay-packet-size\" json:\"max-udp-relay-packet-size,omitempty\"`\n\tCWND                  int               `yaml:\"cwnd\" json:\"cwnd,omitempty\"`\n}\n\ntype RawIPTables struct {\n\tEnable           bool     `yaml:\"enable\" json:\"enable\"`\n\tInboundInterface string   `yaml:\"inbound-interface\" json:\"inbound-interface\"`\n\tBypass           []string `yaml:\"bypass\" json:\"bypass\"`\n\tDnsRedirect      bool     `yaml:\"dns-redirect\" json:\"dns-redirect\"`\n}\n\ntype RawExperimental struct {\n\tFingerprints     []string `yaml:\"fingerprints\"`\n\tQUICGoDisableGSO bool     `yaml:\"quic-go-disable-gso\"`\n\tQUICGoDisableECN bool     `yaml:\"quic-go-disable-ecn\"`\n\tIP4PEnable       bool     `yaml:\"dialer-ip4p-convert\"`\n}\n\ntype RawProfile struct {\n\tStoreSelected bool `yaml:\"store-selected\" json:\"store-selected\"`\n\tStoreFakeIP   bool `yaml:\"store-fake-ip\" json:\"store-fake-ip\"`\n}\n\ntype RawGeoXUrl struct {\n\tGeoIp   string `yaml:\"geoip\" json:\"geoip\"`\n\tMmdb    string `yaml:\"mmdb\" json:\"mmdb\"`\n\tASN     string `yaml:\"asn\" json:\"asn\"`\n\tGeoSite string `yaml:\"geosite\" json:\"geosite\"`\n}\n\ntype RawSniffer struct {\n\tEnable          bool     `yaml:\"enable\" json:\"enable\"`\n\tOverrideDest    bool     `yaml:\"override-destination\" json:\"override-destination\"`\n\tSniffing        []string `yaml:\"sniffing\" json:\"sniffing\"`\n\tForceDomain     []string `yaml:\"force-domain\" json:\"force-domain\"`\n\tSkipSrcAddress  []string `yaml:\"skip-src-address\" json:\"skip-src-address\"`\n\tSkipDstAddress  []string `yaml:\"skip-dst-address\" json:\"skip-dst-address\"`\n\tSkipDomain      []string `yaml:\"skip-domain\" json:\"skip-domain\"`\n\tPorts           []string `yaml:\"port-whitelist\" json:\"port-whitelist\"`\n\tForceDnsMapping bool     `yaml:\"force-dns-mapping\" json:\"force-dns-mapping\"`\n\tParsePureIp     bool     `yaml:\"parse-pure-ip\" json:\"parse-pure-ip\"`\n\n\tSniff map[string]RawSniffingConfig `yaml:\"sniff\" json:\"sniff\"`\n}\n\ntype RawSniffingConfig struct {\n\tPorts        []string `yaml:\"ports\" json:\"ports\"`\n\tOverrideDest *bool    `yaml:\"override-destination\" json:\"override-destination\"`\n}\n\ntype RawTLS struct {\n\tCertificate     string   `yaml:\"certificate\" json:\"certificate\"`\n\tPrivateKey      string   `yaml:\"private-key\" json:\"private-key\"`\n\tClientAuthType  string   `yaml:\"client-auth-type\" json:\"client-auth-type\"`\n\tClientAuthCert  string   `yaml:\"client-auth-cert\" json:\"client-auth-cert\"`\n\tEchKey          string   `yaml:\"ech-key\" json:\"ech-key\"`\n\tCustomTrustCert []string `yaml:\"custom-certifactes\" json:\"custom-certifactes\"`\n}\n\ntype RawConfig struct {\n\tPort                    int                     `yaml:\"port\" json:\"port\"`\n\tSocksPort               int                     `yaml:\"socks-port\" json:\"socks-port\"`\n\tRedirPort               int                     `yaml:\"redir-port\" json:\"redir-port\"`\n\tTProxyPort              int                     `yaml:\"tproxy-port\" json:\"tproxy-port\"`\n\tMixedPort               int                     `yaml:\"mixed-port\" json:\"mixed-port\"`\n\tShadowSocksConfig       string                  `yaml:\"ss-config\" json:\"ss-config\"`\n\tVmessConfig             string                  `yaml:\"vmess-config\" json:\"vmess-config\"`\n\tInboundTfo              bool                    `yaml:\"inbound-tfo\" json:\"inbound-tfo\"`\n\tInboundMPTCP            bool                    `yaml:\"inbound-mptcp\" json:\"inbound-mptcp\"`\n\tAuthentication          []string                `yaml:\"authentication\" json:\"authentication\"`\n\tSkipAuthPrefixes        []netip.Prefix          `yaml:\"skip-auth-prefixes\" json:\"skip-auth-prefixes\"`\n\tLanAllowedIPs           []netip.Prefix          `yaml:\"lan-allowed-ips\" json:\"lan-allowed-ips\"`\n\tLanDisAllowedIPs        []netip.Prefix          `yaml:\"lan-disallowed-ips\" json:\"lan-disallowed-ips\"`\n\tAllowLan                bool                    `yaml:\"allow-lan\" json:\"allow-lan\"`\n\tBindAddress             string                  `yaml:\"bind-address\" json:\"bind-address\"`\n\tMode                    T.TunnelMode            `yaml:\"mode\" json:\"mode\"`\n\tUnifiedDelay            bool                    `yaml:\"unified-delay\" json:\"unified-delay\"`\n\tLogLevel                log.LogLevel            `yaml:\"log-level\" json:\"log-level\"`\n\tIPv6                    bool                    `yaml:\"ipv6\" json:\"ipv6\"`\n\tExternalController      string                  `yaml:\"external-controller\" json:\"external-controller\"`\n\tExternalControllerPipe  string                  `yaml:\"external-controller-pipe\" json:\"external-controller-pipe\"`\n\tExternalControllerUnix  string                  `yaml:\"external-controller-unix\" json:\"external-controller-unix\"`\n\tExternalControllerTLS   string                  `yaml:\"external-controller-tls\" json:\"external-controller-tls\"`\n\tExternalControllerCors  RawCors                 `yaml:\"external-controller-cors\" json:\"external-controller-cors\"`\n\tExternalUI              string                  `yaml:\"external-ui\" json:\"external-ui\"`\n\tExternalUIURL           string                  `yaml:\"external-ui-url\" json:\"external-ui-url\"`\n\tExternalUIName          string                  `yaml:\"external-ui-name\" json:\"external-ui-name\"`\n\tExternalDohServer       string                  `yaml:\"external-doh-server\" json:\"external-doh-server\"`\n\tSecret                  string                  `yaml:\"secret\" json:\"secret\"`\n\tInterface               string                  `yaml:\"interface-name\" json:\"interface-name\"`\n\tRoutingMark             int                     `yaml:\"routing-mark\" json:\"routing-mark\"`\n\tTunnels                 []LC.Tunnel             `yaml:\"tunnels\" json:\"tunnels\"`\n\tGeoAutoUpdate           bool                    `yaml:\"geo-auto-update\" json:\"geo-auto-update\"`\n\tGeoUpdateInterval       int                     `yaml:\"geo-update-interval\" json:\"geo-update-interval\"`\n\tGeodataMode             bool                    `yaml:\"geodata-mode\" json:\"geodata-mode\"`\n\tGeodataLoader           string                  `yaml:\"geodata-loader\" json:\"geodata-loader\"`\n\tGeositeMatcher          string                  `yaml:\"geosite-matcher\" json:\"geosite-matcher\"`\n\tTCPConcurrent           bool                    `yaml:\"tcp-concurrent\" json:\"tcp-concurrent\"`\n\tFindProcessMode         process.FindProcessMode `yaml:\"find-process-mode\" json:\"find-process-mode\"`\n\tGlobalClientFingerprint string                  `yaml:\"global-client-fingerprint\" json:\"global-client-fingerprint\"`\n\tGlobalUA                string                  `yaml:\"global-ua\" json:\"global-ua\"`\n\tETagSupport             bool                    `yaml:\"etag-support\" json:\"etag-support\"`\n\tKeepAliveIdle           int                     `yaml:\"keep-alive-idle\" json:\"keep-alive-idle\"`\n\tKeepAliveInterval       int                     `yaml:\"keep-alive-interval\" json:\"keep-alive-interval\"`\n\tDisableKeepAlive        bool                    `yaml:\"disable-keep-alive\" json:\"disable-keep-alive\"`\n\n\tProxyProvider map[string]map[string]any `yaml:\"proxy-providers\" json:\"proxy-providers\"`\n\tRuleProvider  map[string]map[string]any `yaml:\"rule-providers\" json:\"rule-providers\"`\n\tProxy         []map[string]any          `yaml:\"proxies\" json:\"proxies\"`\n\tProxyGroup    []map[string]any          `yaml:\"proxy-groups\" json:\"proxy-groups\"`\n\tRule          []string                  `yaml:\"rules\" json:\"rule\"`\n\tSubRules      map[string][]string       `yaml:\"sub-rules\" json:\"sub-rules\"`\n\tListeners     []map[string]any          `yaml:\"listeners\" json:\"listeners\"`\n\tHosts         map[string]any            `yaml:\"hosts\" json:\"hosts\"`\n\tDNS           RawDNS                    `yaml:\"dns\" json:\"dns\"`\n\tNTP           RawNTP                    `yaml:\"ntp\" json:\"ntp\"`\n\tTun           RawTun                    `yaml:\"tun\" json:\"tun\"`\n\tTuicServer    RawTuicServer             `yaml:\"tuic-server\" json:\"tuic-server\"`\n\tIPTables      RawIPTables               `yaml:\"iptables\" json:\"iptables\"`\n\tExperimental  RawExperimental           `yaml:\"experimental\" json:\"experimental\"`\n\tProfile       RawProfile                `yaml:\"profile\" json:\"profile\"`\n\tGeoXUrl       RawGeoXUrl                `yaml:\"geox-url\" json:\"geox-url\"`\n\tSniffer       RawSniffer                `yaml:\"sniffer\" json:\"sniffer\"`\n\tTLS           RawTLS                    `yaml:\"tls\" json:\"tls\"`\n\n\tClashForAndroid RawClashForAndroid `yaml:\"clash-for-android\" json:\"clash-for-android\"`\n}\n\n// Parse config\nfunc Parse(buf []byte) (*Config, error) {\n\trawCfg, err := UnmarshalRawConfig(buf)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn ParseRawConfig(rawCfg)\n}\n\nfunc DefaultRawConfig() *RawConfig {\n\treturn &RawConfig{\n\t\tAllowLan:          false,\n\t\tBindAddress:       \"*\",\n\t\tLanAllowedIPs:     []netip.Prefix{netip.MustParsePrefix(\"0.0.0.0/0\"), netip.MustParsePrefix(\"::/0\")},\n\t\tIPv6:              true,\n\t\tMode:              T.Rule,\n\t\tGeoAutoUpdate:     false,\n\t\tGeoUpdateInterval: 24,\n\t\tGeodataMode:       geodata.GeodataMode(),\n\t\tGeodataLoader:     \"memconservative\",\n\t\tUnifiedDelay:      false,\n\t\tAuthentication:    []string{},\n\t\tLogLevel:          log.INFO,\n\t\tHosts:             map[string]any{},\n\t\tRule:              []string{},\n\t\tProxy:             []map[string]any{},\n\t\tProxyGroup:        []map[string]any{},\n\t\tTCPConcurrent:     false,\n\t\tFindProcessMode:   process.FindProcessStrict,\n\t\tGlobalUA:          \"Clash.Meta/ClashMetaForAndroid/5.0\",\n\t\tETagSupport:       true,\n\t\tDNS: RawDNS{\n\t\t\tEnable:         false,\n\t\t\tIPv6:           false,\n\t\t\tUseHosts:       true,\n\t\t\tUseSystemHosts: true,\n\t\t\tIPv6Timeout:    100,\n\t\t\tEnhancedMode:   C.DNSMapping,\n\t\t\tFakeIPRange:    \"198.18.0.1/16\",\n\t\t\tFakeIPTTL:      1,\n\t\t\tFallbackFilter: RawFallbackFilter{\n\t\t\t\tGeoIP:     true,\n\t\t\t\tGeoIPCode: \"CN\",\n\t\t\t\tIPCIDR:    []string{},\n\t\t\t\tGeoSite:   []string{},\n\t\t\t},\n\t\t\tDefaultNameserver: []string{\n\t\t\t\t\"114.114.114.114\",\n\t\t\t\t\"223.5.5.5\",\n\t\t\t\t\"8.8.8.8\",\n\t\t\t\t\"1.0.0.1\",\n\t\t\t},\n\t\t\tNameServer: []string{\n\t\t\t\t\"https://doh.pub/dns-query\",\n\t\t\t\t\"tls://223.5.5.5:853\",\n\t\t\t},\n\t\t\tFakeIPFilter: []string{\n\t\t\t\t\"dns.msftnsci.com\",\n\t\t\t\t\"www.msftnsci.com\",\n\t\t\t\t\"www.msftconnecttest.com\",\n\t\t\t},\n\t\t\tFakeIPFilterMode: C.FilterBlackList,\n\t\t},\n\t\tNTP: RawNTP{\n\t\t\tEnable:        false,\n\t\t\tWriteToSystem: false,\n\t\t\tServer:        \"time.apple.com\",\n\t\t\tPort:          123,\n\t\t\tInterval:      30,\n\t\t},\n\t\tTun: RawTun{\n\t\t\tEnable:              false,\n\t\t\tDevice:              \"\",\n\t\t\tStack:               C.TunGvisor,\n\t\t\tDNSHijack:           []string{\"0.0.0.0:53\"}, // default hijack all dns query\n\t\t\tAutoRoute:           true,\n\t\t\tAutoDetectInterface: true,\n\t\t\tInet6Address:        []netip.Prefix{netip.MustParsePrefix(\"fdfe:dcba:9876::1/126\")},\n\t\t\tRecvMsgX:            true,\n\t\t\tSendMsgX:            false, // In the current implementation, if enabled, the kernel may freeze during multi-thread downloads, so it is disabled by default.\n\t\t},\n\t\tTuicServer: RawTuicServer{\n\t\t\tEnable:                false,\n\t\t\tToken:                 nil,\n\t\t\tUsers:                 nil,\n\t\t\tCertificate:           \"\",\n\t\t\tPrivateKey:            \"\",\n\t\t\tListen:                \"\",\n\t\t\tCongestionController:  \"\",\n\t\t\tMaxIdleTime:           15000,\n\t\t\tAuthenticationTimeout: 1000,\n\t\t\tALPN:                  []string{\"h3\"},\n\t\t\tMaxUdpRelayPacketSize: 1500,\n\t\t},\n\t\tIPTables: RawIPTables{\n\t\t\tEnable:           false,\n\t\t\tInboundInterface: \"lo\",\n\t\t\tBypass:           []string{},\n\t\t\tDnsRedirect:      true,\n\t\t},\n\t\tExperimental: RawExperimental{\n\t\t\t// https://github.com/quic-go/quic-go/issues/4178\n\t\t\t// Quic-go currently cannot automatically fall back on platforms that do not support ecn, so this feature is turned off by default.\n\t\t\tQUICGoDisableECN: true,\n\t\t},\n\t\tProfile: RawProfile{\n\t\t\tStoreSelected: true,\n\t\t},\n\t\tGeoXUrl: RawGeoXUrl{\n\t\t\tMmdb:    \"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.metadb\",\n\t\t\tASN:     \"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/GeoLite2-ASN.mmdb\",\n\t\t\tGeoIp:   \"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geoip.dat\",\n\t\t\tGeoSite: \"https://github.com/MetaCubeX/meta-rules-dat/releases/download/latest/geosite.dat\",\n\t\t},\n\t\tSniffer: RawSniffer{\n\t\t\tEnable:          false,\n\t\t\tSniff:           map[string]RawSniffingConfig{},\n\t\t\tForceDomain:     []string{},\n\t\t\tSkipDomain:      []string{},\n\t\t\tPorts:           []string{},\n\t\t\tForceDnsMapping: true,\n\t\t\tParsePureIp:     true,\n\t\t\tOverrideDest:    true,\n\t\t},\n\t\tExternalUIURL: \"https://github.com/Zephyruso/zashboard/releases/latest/download/dist-no-fonts.zip\",\n\t\tExternalControllerCors: RawCors{\n\t\t\tAllowOrigins:        []string{\"*\"},\n\t\t\tAllowPrivateNetwork: true,\n\t\t},\n\t}\n}\n\nfunc UnmarshalRawConfig(buf []byte) (*RawConfig, error) {\n\t// config with default value\n\trawCfg := DefaultRawConfig()\n\n\tif err := yaml.Unmarshal(buf, rawCfg); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn rawCfg, nil\n}\n\nfunc ParseRawConfig(rawCfg *RawConfig) (*Config, error) {\n\tconfig := &Config{}\n\tlog.Infoln(\"Start initial configuration in progress\") //Segment finished in xxm\n\tstartTime := time.Now()\n\n\tgeneral, err := parseGeneral(rawCfg)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconfig.General = general\n\n\t// We need to temporarily apply some configuration in general and roll back after parsing the complete configuration.\n\t// The loading and downloading of geodata in the parseRules and parseRuleProviders rely on these.\n\t// This implementation is very disgusting, but there is currently no better solution\n\trollback := temporaryUpdateGeneral(config.General)\n\tdefer rollback()\n\n\tcontroller, err := parseController(rawCfg)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconfig.Controller = controller\n\n\texperimental, err := parseExperimental(rawCfg)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconfig.Experimental = experimental\n\n\tiptables, err := parseIPTables(rawCfg)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconfig.IPTables = iptables\n\n\tntpCfg, err := parseNTP(rawCfg)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconfig.NTP = ntpCfg\n\n\tprofile, err := parseProfile(rawCfg)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconfig.Profile = profile\n\n\ttlsCfg, err := parseTLS(rawCfg)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconfig.TLS = tlsCfg\n\n\tproxies, providers, err := parseProxies(rawCfg)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconfig.Proxies = proxies\n\tconfig.Providers = providers\n\n\tlisteners, err := parseListeners(rawCfg)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconfig.Listeners = listeners\n\n\tlog.Infoln(\"Geodata Loader mode: %s\", geodata.LoaderName())\n\tlog.Infoln(\"Geosite Matcher implementation: %s\", geodata.SiteMatcherName())\n\truleProviders, err := parseRuleProviders(rawCfg)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconfig.RuleProviders = ruleProviders\n\n\tsubRules, err := parseSubRules(rawCfg, proxies, ruleProviders)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconfig.SubRules = subRules\n\n\trules, err := parseRules(rawCfg.Rule, proxies, ruleProviders, subRules, \"rules\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconfig.Rules = rules\n\n\thosts, err := parseHosts(rawCfg)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconfig.Hosts = hosts\n\n\tparseIPV6(rawCfg) // must before DNS and Tun\n\n\tdnsCfg, err := parseDNS(rawCfg, ruleProviders)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconfig.DNS = dnsCfg\n\n\terr = parseTun(rawCfg.Tun, dnsCfg, config.General)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\terr = parseTuicServer(rawCfg.TuicServer, config.General)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tconfig.Users = parseAuthentication(rawCfg.Authentication)\n\n\tconfig.Tunnels = rawCfg.Tunnels\n\t// verify tunnels\n\tfor _, t := range config.Tunnels {\n\t\tif len(t.Proxy) > 0 {\n\t\t\tif _, ok := config.Proxies[t.Proxy]; !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"tunnel proxy %s not found\", t.Proxy)\n\t\t\t}\n\t\t}\n\t}\n\n\tconfig.Sniffer, err = parseSniffer(rawCfg.Sniffer, ruleProviders)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\telapsedTime := time.Since(startTime) / time.Millisecond                     // duration in ms\n\tlog.Infoln(\"Initial configuration complete, total time: %dms\", elapsedTime) //Segment finished in xxm\n\n\treturn config, nil\n}\n\n//go:linkname temporaryUpdateGeneral\nfunc temporaryUpdateGeneral(general *General) func()\n\nfunc parseGeneral(cfg *RawConfig) (*General, error) {\n\treturn &General{\n\t\tInbound: Inbound{\n\t\t\tPort:              cfg.Port,\n\t\t\tSocksPort:         cfg.SocksPort,\n\t\t\tRedirPort:         cfg.RedirPort,\n\t\t\tTProxyPort:        cfg.TProxyPort,\n\t\t\tMixedPort:         cfg.MixedPort,\n\t\t\tShadowSocksConfig: cfg.ShadowSocksConfig,\n\t\t\tVmessConfig:       cfg.VmessConfig,\n\t\t\tAllowLan:          cfg.AllowLan,\n\t\t\tSkipAuthPrefixes:  cfg.SkipAuthPrefixes,\n\t\t\tLanAllowedIPs:     cfg.LanAllowedIPs,\n\t\t\tLanDisAllowedIPs:  cfg.LanDisAllowedIPs,\n\t\t\tBindAddress:       cfg.BindAddress,\n\t\t\tInboundTfo:        cfg.InboundTfo,\n\t\t\tInboundMPTCP:      cfg.InboundMPTCP,\n\t\t},\n\t\tUnifiedDelay: cfg.UnifiedDelay,\n\t\tMode:         cfg.Mode,\n\t\tLogLevel:     cfg.LogLevel,\n\t\tIPv6:         cfg.IPv6,\n\t\tInterface:    cfg.Interface,\n\t\tRoutingMark:  cfg.RoutingMark,\n\t\tGeoXUrl: GeoXUrl{\n\t\t\tGeoIp:   cfg.GeoXUrl.GeoIp,\n\t\t\tMmdb:    cfg.GeoXUrl.Mmdb,\n\t\t\tASN:     cfg.GeoXUrl.ASN,\n\t\t\tGeoSite: cfg.GeoXUrl.GeoSite,\n\t\t},\n\t\tGeoAutoUpdate:           cfg.GeoAutoUpdate,\n\t\tGeoUpdateInterval:       cfg.GeoUpdateInterval,\n\t\tGeodataMode:             cfg.GeodataMode,\n\t\tGeodataLoader:           cfg.GeodataLoader,\n\t\tGeositeMatcher:          cfg.GeositeMatcher,\n\t\tTCPConcurrent:           cfg.TCPConcurrent,\n\t\tFindProcessMode:         cfg.FindProcessMode,\n\t\tGlobalClientFingerprint: cfg.GlobalClientFingerprint,\n\t\tGlobalUA:                cfg.GlobalUA,\n\t\tETagSupport:             cfg.ETagSupport,\n\t\tKeepAliveIdle:           cfg.KeepAliveIdle,\n\t\tKeepAliveInterval:       cfg.KeepAliveInterval,\n\t\tDisableKeepAlive:        cfg.DisableKeepAlive,\n\t}, nil\n}\n\nfunc parseController(cfg *RawConfig) (*Controller, error) {\n\tif path := cfg.ExternalUI; path != \"\" && !C.Path.IsSafePath(path) {\n\t\treturn nil, C.Path.ErrNotSafePath(path)\n\t}\n\tif uiName := cfg.ExternalUIName; uiName != \"\" && !filepath.IsLocal(uiName) {\n\t\treturn nil, fmt.Errorf(\"external UI name is not local: %s\", uiName)\n\t}\n\treturn &Controller{\n\t\tExternalController:     cfg.ExternalController,\n\t\tExternalUI:             cfg.ExternalUI,\n\t\tExternalUIURL:          cfg.ExternalUIURL,\n\t\tExternalUIName:         cfg.ExternalUIName,\n\t\tSecret:                 cfg.Secret,\n\t\tExternalControllerPipe: cfg.ExternalControllerPipe,\n\t\tExternalControllerUnix: cfg.ExternalControllerUnix,\n\t\tExternalControllerTLS:  cfg.ExternalControllerTLS,\n\t\tExternalDohServer:      cfg.ExternalDohServer,\n\t\tCors: Cors{\n\t\t\tAllowOrigins:        cfg.ExternalControllerCors.AllowOrigins,\n\t\t\tAllowPrivateNetwork: cfg.ExternalControllerCors.AllowPrivateNetwork,\n\t\t},\n\t}, nil\n}\n\nfunc parseExperimental(cfg *RawConfig) (*Experimental, error) {\n\treturn &Experimental{\n\t\tQUICGoDisableGSO: cfg.Experimental.QUICGoDisableGSO,\n\t\tQUICGoDisableECN: cfg.Experimental.QUICGoDisableECN,\n\t\tIP4PEnable:       cfg.Experimental.IP4PEnable,\n\t}, nil\n}\n\nfunc parseIPTables(cfg *RawConfig) (*IPTables, error) {\n\treturn &IPTables{\n\t\tEnable:           cfg.IPTables.Enable,\n\t\tInboundInterface: cfg.IPTables.InboundInterface,\n\t\tBypass:           cfg.IPTables.Bypass,\n\t\tDnsRedirect:      cfg.IPTables.DnsRedirect,\n\t}, nil\n}\n\nfunc parseNTP(cfg *RawConfig) (*NTP, error) {\n\treturn &NTP{\n\t\tEnable:        cfg.NTP.Enable,\n\t\tServer:        cfg.NTP.Server,\n\t\tPort:          cfg.NTP.Port,\n\t\tInterval:      cfg.NTP.Interval,\n\t\tDialerProxy:   cfg.NTP.DialerProxy,\n\t\tWriteToSystem: cfg.NTP.WriteToSystem,\n\t}, nil\n}\n\nfunc parseProfile(cfg *RawConfig) (*Profile, error) {\n\treturn &Profile{\n\t\tStoreSelected: cfg.Profile.StoreSelected,\n\t\tStoreFakeIP:   cfg.Profile.StoreFakeIP,\n\t}, nil\n}\n\nfunc parseTLS(cfg *RawConfig) (*TLS, error) {\n\treturn &TLS{\n\t\tCertificate:     cfg.TLS.Certificate,\n\t\tPrivateKey:      cfg.TLS.PrivateKey,\n\t\tClientAuthType:  cfg.TLS.ClientAuthType,\n\t\tClientAuthCert:  cfg.TLS.ClientAuthCert,\n\t\tEchKey:          cfg.TLS.EchKey,\n\t\tCustomTrustCert: cfg.TLS.CustomTrustCert,\n\t}, nil\n}\n\nfunc parseProxies(cfg *RawConfig) (proxies map[string]C.Proxy, providersMap map[string]P.ProxyProvider, err error) {\n\tproxies = make(map[string]C.Proxy)\n\tprovidersMap = make(map[string]P.ProxyProvider)\n\tproxiesConfig := cfg.Proxy\n\tgroupsConfig := cfg.ProxyGroup\n\tprovidersConfig := cfg.ProxyProvider\n\n\tvar (\n\t\tproxyList  []string\n\t\tAllProxies []string\n\t\thasGlobal  bool\n\t)\n\n\tproxies[\"DIRECT\"] = adapter.NewProxy(outbound.NewDirect())\n\tproxies[\"REJECT\"] = adapter.NewProxy(outbound.NewReject())\n\tproxies[\"REJECT-DROP\"] = adapter.NewProxy(outbound.NewRejectDrop())\n\tproxies[\"COMPATIBLE\"] = adapter.NewProxy(outbound.NewCompatible())\n\tproxies[\"PASS\"] = adapter.NewProxy(outbound.NewPass())\n\tproxyList = append(proxyList, \"DIRECT\", \"REJECT\")\n\n\t// parse proxy\n\tfor idx, mapping := range proxiesConfig {\n\t\tproxy, err := adapter.ParseProxy(mapping)\n\t\tif err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"proxy %d: %w\", idx, err)\n\t\t}\n\n\t\tif _, exist := proxies[proxy.Name()]; exist {\n\t\t\treturn nil, nil, fmt.Errorf(\"proxy %s is the duplicate name\", proxy.Name())\n\t\t}\n\t\tproxies[proxy.Name()] = proxy\n\t\tproxyList = append(proxyList, proxy.Name())\n\t\tAllProxies = append(AllProxies, proxy.Name())\n\t}\n\n\t// keep the original order of ProxyGroups in config file\n\tfor idx, mapping := range groupsConfig {\n\t\tgroupName, existName := mapping[\"name\"].(string)\n\t\tif !existName {\n\t\t\treturn nil, nil, fmt.Errorf(\"proxy group %d: missing name\", idx)\n\t\t}\n\t\tif groupName == \"GLOBAL\" {\n\t\t\thasGlobal = true\n\t\t}\n\t\tproxyList = append(proxyList, groupName)\n\t}\n\n\t// check if any loop exists and sort the ProxyGroups\n\tif err := proxyGroupsDagSort(groupsConfig); err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\tvar AllProviders []string\n\t// parse and initial providers\n\tfor name, mapping := range providersConfig {\n\t\tif name == provider.ReservedName {\n\t\t\treturn nil, nil, fmt.Errorf(\"can not defined a provider called `%s`\", provider.ReservedName)\n\t\t}\n\n\t\tpd, err := provider.ParseProxyProvider(name, mapping)\n\t\tif err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"parse proxy provider %s error: %w\", name, err)\n\t\t}\n\n\t\tprovidersMap[name] = pd\n\t\tAllProviders = append(AllProviders, name)\n\t}\n\n\tslices.Sort(AllProxies)\n\tslices.Sort(AllProviders)\n\n\t// parse proxy group\n\tfor idx, mapping := range groupsConfig {\n\t\tgroup, err := outboundgroup.ParseProxyGroup(mapping, proxies, providersMap, AllProxies, AllProviders)\n\t\tif err != nil {\n\t\t\treturn nil, nil, fmt.Errorf(\"proxy group[%d]: %w\", idx, err)\n\t\t}\n\n\t\tgroupName := group.Name()\n\t\tif _, exist := proxies[groupName]; exist {\n\t\t\treturn nil, nil, fmt.Errorf(\"proxy group %s: the duplicate name\", groupName)\n\t\t}\n\n\t\tproxies[groupName] = adapter.NewProxy(group)\n\t}\n\n\tvar ps []C.Proxy\n\tfor _, v := range proxyList {\n\t\tif proxies[v].Type() == C.Pass {\n\t\t\tcontinue\n\t\t}\n\t\tps = append(ps, proxies[v])\n\t}\n\thc := provider.NewHealthCheck(ps, \"\", 5000, 0, true, nil)\n\tpd, _ := provider.NewCompatibleProvider(provider.ReservedName, ps, hc)\n\tprovidersMap[provider.ReservedName] = pd\n\n\tif !hasGlobal {\n\t\tglobal := outboundgroup.NewSelector(\n\t\t\t&outboundgroup.GroupCommonOption{\n\t\t\t\tName: \"GLOBAL\",\n\t\t\t},\n\t\t\t[]P.ProxyProvider{pd},\n\t\t)\n\t\tproxies[\"GLOBAL\"] = adapter.NewProxy(global)\n\t}\n\n\t// validate dialer-proxy references\n\tif err := validateDialerProxies(proxies); err != nil {\n\t\treturn nil, nil, err\n\t}\n\n\treturn proxies, providersMap, nil\n}\n\nfunc parseListeners(cfg *RawConfig) (listeners map[string]C.InboundListener, err error) {\n\tlisteners = make(map[string]C.InboundListener)\n\tfor index, mapping := range cfg.Listeners {\n\t\tinboundListener, err := listener.ParseListener(mapping)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"proxy %d: %w\", index, err)\n\t\t}\n\n\t\tname := inboundListener.Name()\n\t\tif _, exist := mapping[name]; exist {\n\t\t\treturn nil, fmt.Errorf(\"listener %s is the duplicate name\", name)\n\t\t}\n\n\t\tlisteners[name] = inboundListener\n\n\t}\n\treturn\n}\n\nfunc parseRuleProviders(cfg *RawConfig) (ruleProviders map[string]P.RuleProvider, err error) {\n\tRP.SetTunnel(T.Tunnel)\n\truleProviders = map[string]P.RuleProvider{}\n\t// parse rule provider\n\tfor name, mapping := range cfg.RuleProvider {\n\t\trp, err := RP.ParseRuleProvider(name, mapping, R.ParseRule)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\truleProviders[name] = rp\n\t}\n\treturn\n}\n\nfunc parseSubRules(cfg *RawConfig, proxies map[string]C.Proxy, ruleProviders map[string]P.RuleProvider) (subRules map[string][]C.Rule, err error) {\n\tsubRules = map[string][]C.Rule{}\n\tfor name := range cfg.SubRules {\n\t\tsubRules[name] = make([]C.Rule, 0)\n\t}\n\tfor name, rawRules := range cfg.SubRules {\n\t\tif len(name) == 0 {\n\t\t\treturn nil, fmt.Errorf(\"sub-rule name is empty\")\n\t\t}\n\t\tvar rules []C.Rule\n\t\trules, err = parseRules(rawRules, proxies, ruleProviders, subRules, fmt.Sprintf(\"sub-rules[%s]\", name))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tsubRules[name] = rules\n\t}\n\n\tif err = verifySubRule(subRules); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn\n}\n\nfunc verifySubRule(subRules map[string][]C.Rule) error {\n\tfor name := range subRules {\n\t\terr := verifySubRuleCircularReferences(name, subRules, []string{})\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc verifySubRuleCircularReferences(n string, subRules map[string][]C.Rule, arr []string) error {\n\tisInArray := func(v string, array []string) bool {\n\t\tfor _, c := range array {\n\t\t\tif v == c {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t\treturn false\n\t}\n\n\tarr = append(arr, n)\n\tfor i, rule := range subRules[n] {\n\t\tif rule.RuleType() == C.SubRules {\n\t\t\tif _, ok := subRules[rule.Adapter()]; !ok {\n\t\t\t\treturn fmt.Errorf(\"sub-rule[%d:%s] error: [%s] not found\", i, n, rule.Adapter())\n\t\t\t}\n\t\t\tif isInArray(rule.Adapter(), arr) {\n\t\t\t\tarr = append(arr, rule.Adapter())\n\t\t\t\treturn fmt.Errorf(\"sub-rule error: circular references [%s]\", strings.Join(arr, \"->\"))\n\t\t\t}\n\n\t\t\tif err := verifySubRuleCircularReferences(rule.Adapter(), subRules, arr); err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc parseRules(rulesConfig []string, proxies map[string]C.Proxy, ruleProviders map[string]P.RuleProvider, subRules map[string][]C.Rule, format string) ([]C.Rule, error) {\n\tvar rules []C.Rule\n\n\t// parse rules\n\tfor idx, line := range rulesConfig {\n\t\ttp, payload, target, params := RC.ParseRulePayload(line, true)\n\t\tif target == \"\" {\n\t\t\treturn nil, fmt.Errorf(\"%s[%d] [%s] error: format invalid\", format, idx, line)\n\t\t}\n\n\t\tif _, ok := proxies[target]; !ok {\n\t\t\tif tp != \"SUB-RULE\" {\n\t\t\t\treturn nil, fmt.Errorf(\"%s[%d] [%s] error: proxy [%s] not found\", format, idx, line, target)\n\t\t\t} else if _, ok = subRules[target]; !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"%s[%d] [%s] error: sub-rule [%s] not found\", format, idx, line, target)\n\t\t\t}\n\t\t}\n\n\t\tparsed, parseErr := R.ParseRule(tp, payload, target, params, subRules)\n\t\tif parseErr != nil {\n\t\t\treturn nil, fmt.Errorf(\"%s[%d] [%s] error: %s\", format, idx, line, parseErr.Error())\n\t\t}\n\n\t\tfor _, name := range parsed.ProviderNames() {\n\t\t\tif _, ok := ruleProviders[name]; !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"%s[%d] [%s] error: rule set [%s] not found\", format, idx, line, name)\n\t\t\t}\n\t\t}\n\n\t\tif format == \"rules\" { // only wrap top level rules\n\t\t\tparsed = RW.NewRuleWrapper(parsed)\n\t\t}\n\n\t\trules = append(rules, parsed)\n\t}\n\n\treturn rules, nil\n}\n\nfunc parseHosts(cfg *RawConfig) (*trie.DomainTrie[resolver.HostValue], error) {\n\ttree := trie.New[resolver.HostValue]()\n\n\t// add default hosts\n\thostValue, _ := resolver.NewHostValueByIPs(\n\t\t[]netip.Addr{netip.AddrFrom4([4]byte{127, 0, 0, 1})})\n\tif err := tree.Insert(\"localhost\", hostValue); err != nil {\n\t\tlog.Errorln(\"insert localhost to host error: %s\", err.Error())\n\t}\n\n\tif len(cfg.Hosts) != 0 {\n\t\tfor domain, anyValue := range cfg.Hosts {\n\t\t\thosts, err := utils.ToStringSlice(anyValue)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif len(hosts) == 1 && hosts[0] == \"lan\" {\n\t\t\t\tif addrs, err := net.InterfaceAddrs(); err != nil {\n\t\t\t\t\tlog.Errorln(\"insert lan to host error: %s\", err)\n\t\t\t\t} else {\n\t\t\t\t\thosts = make([]string, 0, len(addrs))\n\t\t\t\t\tfor _, addr := range addrs {\n\t\t\t\t\t\tif ipnet, ok := addr.(*net.IPNet); ok && !ipnet.IP.IsLoopback() && !ipnet.IP.IsLinkLocalUnicast() {\n\t\t\t\t\t\t\thosts = append(hosts, ipnet.IP.String())\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tvalue, err := resolver.NewHostValue(hosts)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"%s is not a valid value\", anyValue)\n\t\t\t}\n\t\t\tif value.IsDomain {\n\t\t\t\tnode := tree.Search(value.Domain)\n\t\t\t\tfor node != nil && node.Data().IsDomain {\n\t\t\t\t\tif node.Data().Domain == domain {\n\t\t\t\t\t\treturn nil, fmt.Errorf(\"%s, there is a cycle in domain name mapping\", domain)\n\t\t\t\t\t}\n\t\t\t\t\tnode = tree.Search(node.Data().Domain)\n\t\t\t\t}\n\t\t\t}\n\t\t\t_ = tree.Insert(domain, value)\n\t\t}\n\t}\n\ttree.Optimize()\n\n\treturn tree, nil\n}\n\nfunc hostWithDefaultPort(host string, defPort string) (string, error) {\n\thostname, port, err := net.SplitHostPort(host)\n\tif err != nil {\n\t\tif !strings.Contains(err.Error(), \"missing port in address\") {\n\t\t\treturn \"\", err\n\t\t}\n\t\thost = host + \":\" + defPort\n\t\tif hostname, port, err = net.SplitHostPort(host); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t}\n\n\treturn net.JoinHostPort(hostname, port), nil\n}\n\nfunc parseNameServer(servers []string, respectRules bool, preferH3 bool) ([]dns.NameServer, error) {\n\tvar nameservers []dns.NameServer\n\n\tfor idx, server := range servers {\n\t\tserver = parsePureDNSServer(server)\n\t\tu, err := url.Parse(server)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"DNS NameServer[%d] format error: %s\", idx, err.Error())\n\t\t}\n\n\t\tvar proxyName string\n\t\tparams := map[string]string{}\n\t\tfor _, s := range strings.Split(u.Fragment, \"&\") {\n\t\t\tarr := strings.SplitN(s, \"=\", 2)\n\t\t\tswitch len(arr) {\n\t\t\tcase 1:\n\t\t\t\tproxyName = arr[0]\n\t\t\tcase 2:\n\t\t\t\tparams[arr[0]] = arr[1]\n\t\t\t}\n\t\t}\n\n\t\tvar addr, dnsNetType string\n\t\tswitch u.Scheme {\n\t\tcase \"udp\":\n\t\t\taddr, err = hostWithDefaultPort(u.Host, \"53\")\n\t\t\tdnsNetType = \"\" // UDP\n\t\tcase \"tcp\":\n\t\t\taddr, err = hostWithDefaultPort(u.Host, \"53\")\n\t\t\tdnsNetType = \"tcp\" // TCP\n\t\tcase \"tls\":\n\t\t\taddr, err = hostWithDefaultPort(u.Host, \"853\")\n\t\t\tdnsNetType = \"tls\" // DNS over TLS\n\t\tcase \"http\", \"https\":\n\t\t\taddr, err = hostWithDefaultPort(u.Host, \"443\")\n\t\t\tdnsNetType = \"https\" // DNS over HTTPS\n\t\t\tif u.Scheme == \"http\" {\n\t\t\t\taddr, err = hostWithDefaultPort(u.Host, \"80\")\n\t\t\t}\n\t\t\tif err == nil {\n\t\t\t\tclearURL := url.URL{Scheme: u.Scheme, Host: addr, Path: u.Path, User: u.User}\n\t\t\t\taddr = clearURL.String()\n\t\t\t}\n\t\tcase \"quic\":\n\t\t\taddr, err = hostWithDefaultPort(u.Host, \"853\")\n\t\t\tdnsNetType = \"quic\" // DNS over QUIC\n\t\tcase \"system\":\n\t\t\tdnsNetType = \"system\" // System DNS\n\t\tcase \"dhcp\":\n\t\t\taddr = server[len(\"dhcp://\"):] // some special notation cannot be parsed by url\n\t\t\tdnsNetType = \"dhcp\"            // UDP from DHCP\n\t\t\tif addr == \"system\" {          // Compatible with old writing \"dhcp://system\"\n\t\t\t\tdnsNetType = \"system\"\n\t\t\t\taddr = \"\"\n\t\t\t}\n\t\tcase \"rcode\":\n\t\t\tdnsNetType = \"rcode\"\n\t\t\taddr = u.Host\n\t\t\tswitch addr {\n\t\t\tcase \"success\",\n\t\t\t\t\"format_error\",\n\t\t\t\t\"server_failure\",\n\t\t\t\t\"name_error\",\n\t\t\t\t\"not_implemented\",\n\t\t\t\t\"refused\":\n\t\t\tdefault:\n\t\t\t\terr = fmt.Errorf(\"unsupported RCode type: %s\", addr)\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"DNS NameServer[%d] unsupport scheme: %s\", idx, u.Scheme)\n\t\t}\n\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"DNS NameServer[%d] format error: %s\", idx, err.Error())\n\t\t}\n\n\t\tif respectRules && len(proxyName) == 0 {\n\t\t\tproxyName = dns.RespectRules\n\t\t}\n\n\t\tnameserver := dns.NameServer{\n\t\t\tNet:       dnsNetType,\n\t\t\tAddr:      addr,\n\t\t\tProxyName: proxyName,\n\t\t\tParams:    params,\n\t\t\tPreferH3:  preferH3,\n\t\t}\n\t\tif slices.ContainsFunc(nameservers, nameserver.Equal) {\n\t\t\tcontinue // skip duplicates nameserver\n\t\t}\n\n\t\tnameservers = append(nameservers, nameserver)\n\t}\n\treturn nameservers, nil\n}\n\nfunc init() {\n\tdns.ParseNameServer = func(servers []string) ([]dns.NameServer, error) { // using by wireguard\n\t\treturn parseNameServer(servers, false, false)\n\t}\n}\n\nfunc parsePureDNSServer(server string) string {\n\taddPre := func(server string) string {\n\t\treturn \"udp://\" + server\n\t}\n\n\tif server == \"system\" {\n\t\treturn \"system://\"\n\t}\n\n\tif ip, err := netip.ParseAddr(server); err != nil {\n\t\tif strings.Contains(server, \"://\") {\n\t\t\treturn server\n\t\t}\n\t\treturn addPre(server)\n\t} else {\n\t\tif ip.Is4() {\n\t\t\treturn addPre(server)\n\t\t} else {\n\t\t\treturn addPre(\"[\" + server + \"]\")\n\t\t}\n\t}\n}\n\nfunc parseNameServerPolicy(nsPolicy *orderedmap.OrderedMap[string, any], ruleProviders map[string]P.RuleProvider, respectRules bool, preferH3 bool) ([]dns.Policy, error) {\n\tvar policy []dns.Policy\n\n\tfor pair := nsPolicy.Oldest(); pair != nil; pair = pair.Next() {\n\t\tk, v := pair.Key, pair.Value\n\t\tservers, err := utils.ToStringSlice(v)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tnameservers, err := parseNameServer(servers, respectRules, preferH3)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tkLower := strings.ToLower(k)\n\t\tif strings.Contains(kLower, \",\") {\n\t\t\tif strings.HasPrefix(kLower, \"geosite:\") {\n\t\t\t\tsubkeys := strings.Split(k, \":\")\n\t\t\t\tsubkeys = subkeys[1:]\n\t\t\t\tsubkeys = strings.Split(subkeys[0], \",\")\n\t\t\t\tfor _, subkey := range subkeys {\n\t\t\t\t\tnewKey := \"geosite:\" + subkey\n\t\t\t\t\tpolicy = append(policy, dns.Policy{Domain: newKey, NameServers: nameservers})\n\t\t\t\t}\n\t\t\t} else if strings.HasPrefix(kLower, \"rule-set:\") {\n\t\t\t\tsubkeys := strings.Split(k, \":\")\n\t\t\t\tsubkeys = subkeys[1:]\n\t\t\t\tsubkeys = strings.Split(subkeys[0], \",\")\n\t\t\t\tfor _, subkey := range subkeys {\n\t\t\t\t\tnewKey := \"rule-set:\" + subkey\n\t\t\t\t\tpolicy = append(policy, dns.Policy{Domain: newKey, NameServers: nameservers})\n\t\t\t\t}\n\t\t\t} else {\n\t\t\t\tsubkeys := strings.Split(k, \",\")\n\t\t\t\tfor _, subkey := range subkeys {\n\t\t\t\t\tpolicy = append(policy, dns.Policy{Domain: subkey, NameServers: nameservers})\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif strings.HasPrefix(kLower, \"geosite:\") {\n\t\t\t\tpolicy = append(policy, dns.Policy{Domain: \"geosite:\" + k[8:], NameServers: nameservers})\n\t\t\t} else if strings.HasPrefix(kLower, \"rule-set:\") {\n\t\t\t\tpolicy = append(policy, dns.Policy{Domain: \"rule-set:\" + k[9:], NameServers: nameservers})\n\t\t\t} else {\n\t\t\t\tpolicy = append(policy, dns.Policy{Domain: k, NameServers: nameservers})\n\t\t\t}\n\t\t}\n\t}\n\n\tfor idx, p := range policy {\n\t\tdomain, nameservers := p.Domain, p.NameServers\n\n\t\tif strings.HasPrefix(domain, \"rule-set:\") {\n\t\t\tdomainSetName := domain[9:]\n\t\t\tmatcher, err := parseDomainRuleSet(domainSetName, \"dns.nameserver-policy\", ruleProviders)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tpolicy[idx] = dns.Policy{Matcher: matcher, NameServers: nameservers}\n\t\t} else if strings.HasPrefix(domain, \"geosite:\") {\n\t\t\tcountry := domain[8:]\n\t\t\tmatcher, err := RC.NewGEOSITE(country, \"dns.nameserver-policy\")\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tpolicy[idx] = dns.Policy{Matcher: matcher, NameServers: nameservers}\n\t\t} else {\n\t\t\tif _, valid := trie.ValidAndSplitDomain(domain); !valid {\n\t\t\t\treturn nil, fmt.Errorf(\"DNS ResoverRule invalid domain: %s\", domain)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn policy, nil\n}\n\nfunc parseDNS(rawCfg *RawConfig, ruleProviders map[string]P.RuleProvider) (*DNS, error) {\n\tcfg := rawCfg.DNS\n\tif cfg.Enable && len(cfg.NameServer) == 0 {\n\t\treturn nil, fmt.Errorf(\"if DNS configuration is turned on, NameServer cannot be empty\")\n\t}\n\n\tif cfg.RespectRules && len(cfg.ProxyServerNameserver) == 0 {\n\t\treturn nil, fmt.Errorf(\"if “respect-rules” is turned on, “proxy-server-nameserver” cannot be empty\")\n\t}\n\n\tdnsCfg := &DNS{\n\t\tEnable:         cfg.Enable,\n\t\tListen:         cfg.Listen,\n\t\tPreferH3:       cfg.PreferH3,\n\t\tIPv6Timeout:    cfg.IPv6Timeout,\n\t\tIPv6:           cfg.IPv6,\n\t\tUseHosts:       cfg.UseHosts,\n\t\tUseSystemHosts: cfg.UseSystemHosts,\n\t\tEnhancedMode:   cfg.EnhancedMode,\n\t\tCacheAlgorithm: cfg.CacheAlgorithm,\n\t\tCacheMaxSize:   cfg.CacheMaxSize,\n\t}\n\tvar err error\n\tif dnsCfg.NameServer, err = parseNameServer(cfg.NameServer, cfg.RespectRules, cfg.PreferH3); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif dnsCfg.Fallback, err = parseNameServer(cfg.Fallback, cfg.RespectRules, cfg.PreferH3); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif dnsCfg.NameServerPolicy, err = parseNameServerPolicy(cfg.NameServerPolicy, ruleProviders, cfg.RespectRules, cfg.PreferH3); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif dnsCfg.ProxyServerNameserver, err = parseNameServer(cfg.ProxyServerNameserver, false, cfg.PreferH3); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif dnsCfg.ProxyServerPolicy, err = parseNameServerPolicy(cfg.ProxyServerNameserverPolicy, ruleProviders, false, cfg.PreferH3); err != nil {\n\t\treturn nil, err\n\t}\n\tif len(dnsCfg.ProxyServerPolicy) != 0 && len(dnsCfg.ProxyServerNameserver) == 0 {\n\t\treturn nil, errors.New(\"disallow empty `proxy-server-nameserver` when `proxy-server-nameserver-policy` is set\")\n\t}\n\n\tif dnsCfg.DirectNameServer, err = parseNameServer(cfg.DirectNameServer, false, cfg.PreferH3); err != nil {\n\t\treturn nil, err\n\t}\n\tdnsCfg.DirectFollowPolicy = cfg.DirectNameServerFollowPolicy\n\n\tif len(cfg.DefaultNameserver) == 0 {\n\t\treturn nil, errors.New(\"default nameserver should have at least one nameserver\")\n\t}\n\tif dnsCfg.DefaultNameserver, err = parseNameServer(cfg.DefaultNameserver, false, cfg.PreferH3); err != nil {\n\t\treturn nil, err\n\t}\n\t// check default nameserver is pure ip addr\n\tfor _, ns := range dnsCfg.DefaultNameserver {\n\t\tif ns.Net == \"system\" {\n\t\t\tcontinue\n\t\t}\n\t\thost, _, err := net.SplitHostPort(ns.Addr)\n\t\tif err != nil || net.ParseIP(host) == nil {\n\t\t\tu, err := url.Parse(ns.Addr)\n\t\t\tif err == nil && net.ParseIP(u.Host) == nil {\n\t\t\t\tif ip, _, err := net.SplitHostPort(u.Host); err != nil || net.ParseIP(ip) == nil {\n\t\t\t\t\treturn nil, errors.New(\"default nameserver should be pure IP\")\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tif cfg.FakeIPRange != \"\" {\n\t\tdnsCfg.FakeIPRange, err = netip.ParsePrefix(cfg.FakeIPRange)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif !dnsCfg.FakeIPRange.Addr().Is4() {\n\t\t\treturn nil, errors.New(\"dns.fake-ip-range must be a IPv4 prefix\")\n\t\t}\n\t}\n\n\tif cfg.FakeIPRange6 != \"\" {\n\t\tdnsCfg.FakeIPRange6, err = netip.ParsePrefix(cfg.FakeIPRange6)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif !dnsCfg.FakeIPRange6.Addr().Is6() {\n\t\t\treturn nil, errors.New(\"dns.fake-ip-range6 must be a IPv6 prefix\")\n\t\t}\n\t}\n\n\tif cfg.EnhancedMode == C.DNSFakeIP {\n\t\tvar fakeIPTrie *trie.DomainTrie[struct{}]\n\t\tif len(dnsCfg.Fallback) != 0 {\n\t\t\tfakeIPTrie = trie.New[struct{}]()\n\t\t\tfor _, fb := range dnsCfg.Fallback {\n\t\t\t\tif net.ParseIP(fb.Addr) != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t_ = fakeIPTrie.Insert(fb.Addr, struct{}{})\n\t\t\t}\n\t\t}\n\n\t\tskipper := &fakeip.Skipper{Mode: cfg.FakeIPFilterMode}\n\n\t\tif cfg.FakeIPFilterMode == C.FilterRule {\n\t\t\trules, err := parseFakeIPRules(cfg.FakeIPFilter, ruleProviders)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tskipper.Rules = rules\n\t\t} else {\n\t\t\thost, err := parseDomain(cfg.FakeIPFilter, fakeIPTrie, \"dns.fake-ip-filter\", ruleProviders)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tskipper.Host = host\n\t\t}\n\n\t\tdnsCfg.FakeIPSkipper = skipper\n\t\tdnsCfg.FakeIPTTL = cfg.FakeIPTTL\n\n\t\tif dnsCfg.FakeIPRange.IsValid() {\n\t\t\tpool, err := fakeip.New(fakeip.Options{\n\t\t\t\tIPNet:       dnsCfg.FakeIPRange,\n\t\t\t\tSize:        1000,\n\t\t\t\tPersistence: rawCfg.Profile.StoreFakeIP,\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tdnsCfg.FakeIPPool = pool\n\t\t}\n\n\t\tif dnsCfg.FakeIPRange6.IsValid() {\n\t\t\tpool6, err := fakeip.New(fakeip.Options{\n\t\t\t\tIPNet:       dnsCfg.FakeIPRange6,\n\t\t\t\tSize:        1000,\n\t\t\t\tPersistence: rawCfg.Profile.StoreFakeIP,\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tdnsCfg.FakeIPPool6 = pool6\n\t\t}\n\n\t\tif dnsCfg.FakeIPPool == nil && dnsCfg.FakeIPPool6 == nil {\n\t\t\treturn nil, errors.New(\"disallow `fake-ip-range` and `fake-ip-range6` both empty with fake-ip mode\")\n\t\t}\n\t}\n\n\tif len(cfg.Fallback) != 0 {\n\t\tif cfg.FallbackFilter.GeoIP {\n\t\t\tmatcher, err := RC.NewGEOIP(cfg.FallbackFilter.GeoIPCode, \"dns.fallback-filter.geoip\", false, true)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"load GeoIP dns fallback filter error, %w\", err)\n\t\t\t}\n\t\t\tdnsCfg.FallbackIPFilter = append(dnsCfg.FallbackIPFilter, matcher.DnsFallbackFilter())\n\t\t}\n\t\tif len(cfg.FallbackFilter.IPCIDR) > 0 {\n\t\t\tcidrSet := cidr.NewIpCidrSet()\n\t\t\tfor idx, ipcidr := range cfg.FallbackFilter.IPCIDR {\n\t\t\t\terr = cidrSet.AddIpCidrForString(ipcidr)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"DNS FallbackIP[%d] format error: %w\", idx, err)\n\t\t\t\t}\n\t\t\t}\n\t\t\terr = cidrSet.Merge()\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tmatcher := cidrSet // dns.fallback-filter.ipcidr\n\t\t\tdnsCfg.FallbackIPFilter = append(dnsCfg.FallbackIPFilter, matcher)\n\t\t}\n\t\tif len(cfg.FallbackFilter.Domain) > 0 {\n\t\t\tdomainTrie := trie.New[struct{}]()\n\t\t\tfor idx, domain := range cfg.FallbackFilter.Domain {\n\t\t\t\terr = domainTrie.Insert(domain, struct{}{})\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"DNS FallbackDomain[%d] format error: %w\", idx, err)\n\t\t\t\t}\n\t\t\t}\n\t\t\tmatcher := domainTrie.NewDomainSet() // dns.fallback-filter.domain\n\t\t\tdnsCfg.FallbackDomainFilter = append(dnsCfg.FallbackDomainFilter, matcher)\n\t\t}\n\t\tif len(cfg.FallbackFilter.GeoSite) > 0 {\n\t\t\tlog.Warnln(\"replace fallback-filter.geosite with nameserver-policy, it will be removed in the future\")\n\t\t\tfor idx, geoSite := range cfg.FallbackFilter.GeoSite {\n\t\t\t\tmatcher, err := RC.NewGEOSITE(geoSite, \"dns.fallback-filter.geosite\")\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, fmt.Errorf(\"DNS FallbackGeosite[%d] format error: %w\", idx, err)\n\t\t\t\t}\n\t\t\t\tdnsCfg.FallbackDomainFilter = append(dnsCfg.FallbackDomainFilter, matcher)\n\t\t\t}\n\t\t}\n\t}\n\n\treturn dnsCfg, nil\n}\n\nfunc parseFakeIPRules(rawRules []string, ruleProviders map[string]P.RuleProvider) ([]C.Rule, error) {\n\tvar rules []C.Rule\n\n\tfor idx, line := range rawRules {\n\t\ttp, payload, action, params := RC.ParseRulePayload(line, true)\n\n\t\taction = strings.ToLower(action)\n\t\tif action != fakeip.UseFakeIP && action != fakeip.UseRealIP {\n\t\t\treturn nil, fmt.Errorf(\"dns.fake-ip-filter[%d] [%s] error: invalid action '%s', must be 'fake-ip' or 'real-ip'\", idx, line, action)\n\t\t}\n\n\t\tif tp == \"RULE-SET\" {\n\t\t\tif rp, ok := ruleProviders[payload]; !ok {\n\t\t\t\treturn nil, fmt.Errorf(\"dns.fake-ip-filter[%d] [%s] error: rule-set '%s' not found\", idx, line, payload)\n\t\t\t} else {\n\t\t\t\tswitch rp.Behavior() {\n\t\t\t\tcase P.IPCIDR:\n\t\t\t\t\treturn nil, fmt.Errorf(\"dns.fake-ip-filter[%d] [%s] error: rule-set behavior is %s, must be domain or classical\", idx, line, rp.Behavior())\n\t\t\t\tcase P.Classical:\n\t\t\t\t\tlog.Warnln(\"%s provider is %s, only matching domain rules in fake-ip-filter\", rp.Name(), rp.Behavior())\n\t\t\t\tdefault:\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tparsed, err := R.ParseRule(tp, payload, action, params, nil)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"dns.fake-ip-filter[%d] [%s] error: %w\", idx, line, err)\n\t\t}\n\n\t\tif !isDomainRule(parsed.RuleType()) && parsed.RuleType() != C.MATCH {\n\t\t\treturn nil, fmt.Errorf(\"dns.fake-ip-filter[%d] [%s] error: rule type '%s' not supported, only domain-based rules allowed\", idx, line, tp)\n\t\t}\n\n\t\trules = append(rules, parsed)\n\t}\n\n\treturn rules, nil\n}\n\nfunc isDomainRule(rt C.RuleType) bool {\n\tswitch rt {\n\tcase C.Domain, C.DomainSuffix, C.DomainKeyword, C.DomainRegex, C.DomainWildcard, C.GEOSITE, C.RuleSet:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc parseAuthentication(rawRecords []string) []auth.AuthUser {\n\tvar users []auth.AuthUser\n\tfor _, line := range rawRecords {\n\t\tif user, pass, found := strings.Cut(line, \":\"); found {\n\t\t\tusers = append(users, auth.AuthUser{User: user, Pass: pass})\n\t\t}\n\t}\n\treturn users\n}\n\nfunc parseIPV6(rawCfg *RawConfig) {\n\tif !rawCfg.IPv6 || !verifyIP6() {\n\t\trawCfg.DNS.FakeIPRange6 = \"\"\n\t\trawCfg.Tun.Inet6Address = nil\n\t}\n}\n\nfunc parseTun(rawTun RawTun, dns *DNS, general *General) error {\n\ttunAddressPrefix := dns.FakeIPRange\n\tif !tunAddressPrefix.IsValid() {\n\t\ttunAddressPrefix = netip.MustParsePrefix(\"198.18.0.1/16\")\n\t}\n\ttunAddressPrefix = netip.PrefixFrom(tunAddressPrefix.Addr(), 30)\n\n\tgeneral.Tun = LC.Tun{\n\t\tEnable:              rawTun.Enable,\n\t\tDevice:              rawTun.Device,\n\t\tStack:               rawTun.Stack,\n\t\tDNSHijack:           rawTun.DNSHijack,\n\t\tAutoRoute:           rawTun.AutoRoute,\n\t\tAutoDetectInterface: rawTun.AutoDetectInterface,\n\n\t\tMTU:                                   rawTun.MTU,\n\t\tGSO:                                   rawTun.GSO,\n\t\tGSOMaxSize:                            rawTun.GSOMaxSize,\n\t\tInet4Address:                          []netip.Prefix{tunAddressPrefix},\n\t\tInet6Address:                          rawTun.Inet6Address,\n\t\tIPRoute2TableIndex:                    rawTun.IPRoute2TableIndex,\n\t\tIPRoute2RuleIndex:                     rawTun.IPRoute2RuleIndex,\n\t\tAutoRedirect:                          rawTun.AutoRedirect,\n\t\tAutoRedirectInputMark:                 rawTun.AutoRedirectInputMark,\n\t\tAutoRedirectOutputMark:                rawTun.AutoRedirectOutputMark,\n\t\tAutoRedirectIPRoute2FallbackRuleIndex: rawTun.AutoRedirectIPRoute2FallbackRuleIndex,\n\t\tLoopbackAddress:                       rawTun.LoopbackAddress,\n\t\tStrictRoute:                           rawTun.StrictRoute,\n\t\tRouteAddress:                          rawTun.RouteAddress,\n\t\tRouteAddressSet:                       rawTun.RouteAddressSet,\n\t\tRouteExcludeAddress:                   rawTun.RouteExcludeAddress,\n\t\tRouteExcludeAddressSet:                rawTun.RouteExcludeAddressSet,\n\t\tIncludeInterface:                      rawTun.IncludeInterface,\n\t\tExcludeInterface:                      rawTun.ExcludeInterface,\n\t\tIncludeUID:                            rawTun.IncludeUID,\n\t\tIncludeUIDRange:                       rawTun.IncludeUIDRange,\n\t\tExcludeUID:                            rawTun.ExcludeUID,\n\t\tExcludeUIDRange:                       rawTun.ExcludeUIDRange,\n\t\tExcludeSrcPort:                        rawTun.ExcludeSrcPort,\n\t\tExcludeSrcPortRange:                   rawTun.ExcludeSrcPortRange,\n\t\tExcludeDstPort:                        rawTun.ExcludeDstPort,\n\t\tExcludeDstPortRange:                   rawTun.ExcludeDstPortRange,\n\t\tIncludeAndroidUser:                    rawTun.IncludeAndroidUser,\n\t\tIncludePackage:                        rawTun.IncludePackage,\n\t\tExcludePackage:                        rawTun.ExcludePackage,\n\t\tEndpointIndependentNat:                rawTun.EndpointIndependentNat,\n\t\tUDPTimeout:                            rawTun.UDPTimeout,\n\t\tDisableICMPForwarding:                 rawTun.DisableICMPForwarding,\n\t\tFileDescriptor:                        rawTun.FileDescriptor,\n\n\t\tInet4RouteAddress:        rawTun.Inet4RouteAddress,\n\t\tInet6RouteAddress:        rawTun.Inet6RouteAddress,\n\t\tInet4RouteExcludeAddress: rawTun.Inet4RouteExcludeAddress,\n\t\tInet6RouteExcludeAddress: rawTun.Inet6RouteExcludeAddress,\n\n\t\tRecvMsgX: rawTun.RecvMsgX,\n\t\tSendMsgX: rawTun.SendMsgX,\n\t}\n\n\treturn nil\n}\n\nfunc parseTuicServer(rawTuic RawTuicServer, general *General) error {\n\tgeneral.TuicServer = LC.TuicServer{\n\t\tEnable:                rawTuic.Enable,\n\t\tListen:                rawTuic.Listen,\n\t\tToken:                 rawTuic.Token,\n\t\tUsers:                 rawTuic.Users,\n\t\tCertificate:           rawTuic.Certificate,\n\t\tPrivateKey:            rawTuic.PrivateKey,\n\t\tCongestionController:  rawTuic.CongestionController,\n\t\tMaxIdleTime:           rawTuic.MaxIdleTime,\n\t\tAuthenticationTimeout: rawTuic.AuthenticationTimeout,\n\t\tALPN:                  rawTuic.ALPN,\n\t\tMaxUdpRelayPacketSize: rawTuic.MaxUdpRelayPacketSize,\n\t\tCWND:                  rawTuic.CWND,\n\t}\n\treturn nil\n}\n\nfunc parseSniffer(snifferRaw RawSniffer, ruleProviders map[string]P.RuleProvider) (*sniffer.Config, error) {\n\tsnifferConfig := &sniffer.Config{\n\t\tEnable:          snifferRaw.Enable,\n\t\tForceDnsMapping: snifferRaw.ForceDnsMapping,\n\t\tParsePureIp:     snifferRaw.ParsePureIp,\n\t}\n\tloadSniffer := make(map[snifferTypes.Type]sniffer.SnifferConfig)\n\n\tif len(snifferRaw.Sniff) != 0 {\n\t\tfor sniffType, sniffConfig := range snifferRaw.Sniff {\n\t\t\tfind := false\n\t\t\tports, err := utils.NewUnsignedRangesFromList[uint16](sniffConfig.Ports)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\toverrideDest := snifferRaw.OverrideDest\n\t\t\tif sniffConfig.OverrideDest != nil {\n\t\t\t\toverrideDest = *sniffConfig.OverrideDest\n\t\t\t}\n\t\t\tfor _, snifferType := range snifferTypes.List {\n\t\t\t\tif snifferType.String() == strings.ToUpper(sniffType) {\n\t\t\t\t\tfind = true\n\t\t\t\t\tloadSniffer[snifferType] = sniffer.SnifferConfig{\n\t\t\t\t\t\tPorts:        ports,\n\t\t\t\t\t\tOverrideDest: overrideDest,\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif !find {\n\t\t\t\treturn nil, fmt.Errorf(\"not find the sniffer[%s]\", sniffType)\n\t\t\t}\n\t\t}\n\t} else {\n\t\tif snifferConfig.Enable && len(snifferRaw.Sniffing) != 0 {\n\t\t\t// Deprecated: Use Sniff instead\n\t\t\tlog.Warnln(\"Deprecated: Use Sniff instead\")\n\t\t}\n\t\tglobalPorts, err := utils.NewUnsignedRangesFromList[uint16](snifferRaw.Ports)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tfor _, snifferName := range snifferRaw.Sniffing {\n\t\t\tfind := false\n\t\t\tfor _, snifferType := range snifferTypes.List {\n\t\t\t\tif snifferType.String() == strings.ToUpper(snifferName) {\n\t\t\t\t\tfind = true\n\t\t\t\t\tloadSniffer[snifferType] = sniffer.SnifferConfig{\n\t\t\t\t\t\tPorts:        globalPorts,\n\t\t\t\t\t\tOverrideDest: snifferRaw.OverrideDest,\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif !find {\n\t\t\t\treturn nil, fmt.Errorf(\"not find the sniffer[%s]\", snifferName)\n\t\t\t}\n\t\t}\n\t}\n\n\tsnifferConfig.Sniffers = loadSniffer\n\n\tforceDomain, err := parseDomain(snifferRaw.ForceDomain, nil, \"sniffer.force-domain\", ruleProviders)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error in force-domain, error:%w\", err)\n\t}\n\tsnifferConfig.ForceDomain = forceDomain\n\n\tskipSrcAddress, err := parseIPCIDR(snifferRaw.SkipSrcAddress, nil, \"sniffer.skip-src-address\", ruleProviders)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error in skip-src-address, error:%w\", err)\n\t}\n\tsnifferConfig.SkipSrcAddress = skipSrcAddress\n\n\tskipDstAddress, err := parseIPCIDR(snifferRaw.SkipDstAddress, nil, \"sniffer.skip-dst-address\", ruleProviders)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error in skip-dst-address, error:%w\", err)\n\t}\n\tsnifferConfig.SkipDstAddress = skipDstAddress\n\n\tskipDomain, err := parseDomain(snifferRaw.SkipDomain, nil, \"sniffer.skip-domain\", ruleProviders)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"error in skip-domain, error:%w\", err)\n\t}\n\tsnifferConfig.SkipDomain = skipDomain\n\n\treturn snifferConfig, nil\n}\n\nfunc parseIPCIDR(addresses []string, cidrSet *cidr.IpCidrSet, adapterName string, ruleProviders map[string]P.RuleProvider) (matchers []C.IpMatcher, err error) {\n\tvar matcher C.IpMatcher\n\tfor _, ipcidr := range addresses {\n\t\tipcidrLower := strings.ToLower(ipcidr)\n\t\tif strings.HasPrefix(ipcidrLower, \"geoip:\") {\n\t\t\tsubkeys := strings.Split(ipcidr, \":\")\n\t\t\tsubkeys = subkeys[1:]\n\t\t\tsubkeys = strings.Split(subkeys[0], \",\")\n\t\t\tfor _, country := range subkeys {\n\t\t\t\tmatcher, err = RC.NewGEOIP(country, adapterName, false, false)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tmatchers = append(matchers, matcher)\n\t\t\t}\n\t\t} else if strings.HasPrefix(ipcidrLower, \"rule-set:\") {\n\t\t\tsubkeys := strings.Split(ipcidr, \":\")\n\t\t\tsubkeys = subkeys[1:]\n\t\t\tsubkeys = strings.Split(subkeys[0], \",\")\n\t\t\tfor _, domainSetName := range subkeys {\n\t\t\t\tmatcher, err = parseIPRuleSet(domainSetName, adapterName, ruleProviders)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tmatchers = append(matchers, matcher)\n\t\t\t}\n\t\t} else {\n\t\t\tif cidrSet == nil {\n\t\t\t\tcidrSet = cidr.NewIpCidrSet()\n\t\t\t}\n\t\t\terr = cidrSet.AddIpCidrForString(ipcidr)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\tif !cidrSet.IsEmpty() {\n\t\terr = cidrSet.Merge()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tmatcher = cidrSet\n\t\tmatchers = append(matchers, matcher)\n\t}\n\treturn\n}\n\nfunc parseDomain(domains []string, domainTrie *trie.DomainTrie[struct{}], adapterName string, ruleProviders map[string]P.RuleProvider) (matchers []C.DomainMatcher, err error) {\n\tvar matcher C.DomainMatcher\n\tfor _, domain := range domains {\n\t\tdomainLower := strings.ToLower(domain)\n\t\tif strings.HasPrefix(domainLower, \"geosite:\") {\n\t\t\tsubkeys := strings.Split(domain, \":\")\n\t\t\tsubkeys = subkeys[1:]\n\t\t\tsubkeys = strings.Split(subkeys[0], \",\")\n\t\t\tfor _, country := range subkeys {\n\t\t\t\tmatcher, err = RC.NewGEOSITE(country, adapterName)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tmatchers = append(matchers, matcher)\n\t\t\t}\n\t\t} else if strings.HasPrefix(domainLower, \"rule-set:\") {\n\t\t\tsubkeys := strings.Split(domain, \":\")\n\t\t\tsubkeys = subkeys[1:]\n\t\t\tsubkeys = strings.Split(subkeys[0], \",\")\n\t\t\tfor _, domainSetName := range subkeys {\n\t\t\t\tmatcher, err = parseDomainRuleSet(domainSetName, adapterName, ruleProviders)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tmatchers = append(matchers, matcher)\n\t\t\t}\n\t\t} else {\n\t\t\tif domainTrie == nil {\n\t\t\t\tdomainTrie = trie.New[struct{}]()\n\t\t\t}\n\t\t\terr = domainTrie.Insert(domain, struct{}{})\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\tif !domainTrie.IsEmpty() {\n\t\tmatcher = domainTrie.NewDomainSet()\n\t\tmatchers = append(matchers, matcher)\n\t}\n\treturn\n}\n\nfunc parseIPRuleSet(domainSetName string, adapterName string, ruleProviders map[string]P.RuleProvider) (C.IpMatcher, error) {\n\tif rp, ok := ruleProviders[domainSetName]; !ok {\n\t\treturn nil, fmt.Errorf(\"not found rule-set: %s\", domainSetName)\n\t} else {\n\t\tswitch rp.Behavior() {\n\t\tcase P.Domain:\n\t\t\treturn nil, fmt.Errorf(\"rule provider type error, except ipcidr,actual %s\", rp.Behavior())\n\t\tcase P.Classical:\n\t\t\tlog.Warnln(\"%s provider is %s, only matching it contain ip rule\", rp.Name(), rp.Behavior())\n\t\tdefault:\n\t\t}\n\t}\n\treturn RP.NewRuleSet(domainSetName, adapterName, false, true)\n}\n\nfunc parseDomainRuleSet(domainSetName string, adapterName string, ruleProviders map[string]P.RuleProvider) (C.DomainMatcher, error) {\n\tif rp, ok := ruleProviders[domainSetName]; !ok {\n\t\treturn nil, fmt.Errorf(\"not found rule-set: %s\", domainSetName)\n\t} else {\n\t\tswitch rp.Behavior() {\n\t\tcase P.IPCIDR:\n\t\t\treturn nil, fmt.Errorf(\"rule provider type error, except domain,actual %s\", rp.Behavior())\n\t\tcase P.Classical:\n\t\t\tlog.Warnln(\"%s provider is %s, only matching it contain domain rule\", rp.Name(), rp.Behavior())\n\t\tdefault:\n\t\t}\n\t}\n\treturn RP.NewRuleSet(domainSetName, adapterName, false, true)\n}\n"
  },
  {
    "path": "core/Clash.Meta/config/initial.go",
    "content": "package config\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\n// Init prepare necessary files\nfunc Init(dir string) error {\n\t// initial homedir\n\tif _, err := os.Stat(dir); os.IsNotExist(err) {\n\t\tif err := os.MkdirAll(dir, 0o777); err != nil {\n\t\t\treturn fmt.Errorf(\"can't create config directory %s: %s\", dir, err.Error())\n\t\t}\n\t}\n\n\t// initial config.yaml\n\tif _, err := os.Stat(C.Path.Config()); os.IsNotExist(err) {\n\t\tlog.Infoln(\"Can't find config, create a initial config file\")\n\t\tf, err := os.OpenFile(C.Path.Config(), os.O_CREATE|os.O_WRONLY, 0o644)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"can't create file %s: %s\", C.Path.Config(), err.Error())\n\t\t}\n\t\tf.Write([]byte(`mixed-port: 7890`))\n\t\tf.Close()\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/config/utils.go",
    "content": "package config\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"net/netip\"\n\t\"os\"\n\t\"strconv\"\n\n\t\"github.com/metacubex/mihomo/adapter/outboundgroup\"\n\t\"github.com/metacubex/mihomo/common/structure\"\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\n// Check if ProxyGroups form DAG(Directed Acyclic Graph), and sort all ProxyGroups by dependency order.\n// Meanwhile, record the original index in the config file.\n// If loop is detected, return an error with location of loop.\nfunc proxyGroupsDagSort(groupsConfig []map[string]any) error {\n\ttype graphNode struct {\n\t\tindegree int\n\t\t// topological order\n\t\ttopo int\n\t\t// the original data in `groupsConfig`\n\t\tdata map[string]any\n\t\t// `outdegree` and `from` are used in loop locating\n\t\toutdegree int\n\t\toption    *outboundgroup.GroupCommonOption\n\t\tfrom      []string\n\t}\n\n\tdecoder := structure.NewDecoder(structure.Option{TagName: \"group\", WeaklyTypedInput: true})\n\tgraph := make(map[string]*graphNode)\n\n\t// Step 1.1 build dependency graph\n\tfor _, mapping := range groupsConfig {\n\t\toption := &outboundgroup.GroupCommonOption{}\n\t\tif err := decoder.Decode(mapping, option); err != nil {\n\t\t\treturn fmt.Errorf(\"ProxyGroup %s: %s\", option.Name, err.Error())\n\t\t}\n\n\t\tgroupName := option.Name\n\t\tif node, ok := graph[groupName]; ok {\n\t\t\tif node.data != nil {\n\t\t\t\treturn fmt.Errorf(\"ProxyGroup %s: duplicate group name\", groupName)\n\t\t\t}\n\t\t\tnode.data = mapping\n\t\t\tnode.option = option\n\t\t} else {\n\t\t\tgraph[groupName] = &graphNode{0, -1, mapping, 0, option, nil}\n\t\t}\n\n\t\tfor _, proxy := range option.Proxies {\n\t\t\tif node, ex := graph[proxy]; ex {\n\t\t\t\tnode.indegree++\n\t\t\t} else {\n\t\t\t\tgraph[proxy] = &graphNode{1, -1, nil, 0, nil, nil}\n\t\t\t}\n\t\t}\n\t}\n\t// Step 1.2 Topological Sort\n\t// topological index of **ProxyGroup**\n\tindex := 0\n\tqueue := make([]string, 0)\n\tfor name, node := range graph {\n\t\t// in the beginning, put nodes that have `node.indegree == 0` into queue.\n\t\tif node.indegree == 0 {\n\t\t\tqueue = append(queue, name)\n\t\t}\n\t}\n\t// every element in queue have indegree == 0\n\tfor ; len(queue) > 0; queue = queue[1:] {\n\t\tname := queue[0]\n\t\tnode := graph[name]\n\t\tif node.option != nil {\n\t\t\tindex++\n\t\t\tgroupsConfig[len(groupsConfig)-index] = node.data\n\t\t\tif len(node.option.Proxies) == 0 {\n\t\t\t\tdelete(graph, name)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tfor _, proxy := range node.option.Proxies {\n\t\t\t\tchild := graph[proxy]\n\t\t\t\tchild.indegree--\n\t\t\t\tif child.indegree == 0 {\n\t\t\t\t\tqueue = append(queue, proxy)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tdelete(graph, name)\n\t}\n\n\t// no loop is detected, return sorted ProxyGroup\n\tif len(graph) == 0 {\n\t\treturn nil\n\t}\n\n\t// if loop is detected, locate the loop and throw an error\n\t// Step 2.1 rebuild the graph, fill `outdegree` and `from` filed\n\tfor name, node := range graph {\n\t\tif node.option == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tif len(node.option.Proxies) == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\tfor _, proxy := range node.option.Proxies {\n\t\t\tnode.outdegree++\n\t\t\tchild := graph[proxy]\n\t\t\tif child.from == nil {\n\t\t\t\tchild.from = make([]string, 0, child.indegree)\n\t\t\t}\n\t\t\tchild.from = append(child.from, name)\n\t\t}\n\t}\n\t// Step 2.2 remove nodes outside the loop. so that we have only the loops remain in `graph`\n\tqueue = make([]string, 0)\n\t// initialize queue with node have outdegree == 0\n\tfor name, node := range graph {\n\t\tif node.outdegree == 0 {\n\t\t\tqueue = append(queue, name)\n\t\t}\n\t}\n\t// every element in queue have outdegree == 0\n\tfor ; len(queue) > 0; queue = queue[1:] {\n\t\tname := queue[0]\n\t\tnode := graph[name]\n\t\tfor _, f := range node.from {\n\t\t\tgraph[f].outdegree--\n\t\t\tif graph[f].outdegree == 0 {\n\t\t\t\tqueue = append(queue, f)\n\t\t\t}\n\t\t}\n\t\tdelete(graph, name)\n\t}\n\t// Step 2.3 report the elements in loop\n\tloopElements := make([]string, 0, len(graph))\n\tfor name := range graph {\n\t\tloopElements = append(loopElements, name)\n\t\tdelete(graph, name)\n\t}\n\treturn fmt.Errorf(\"loop is detected in ProxyGroup, please check following ProxyGroups: %v\", loopElements)\n}\n\n// validateDialerProxies checks if all dialer-proxy references are valid\nfunc validateDialerProxies(proxies map[string]C.Proxy) error {\n\tgraph := make(map[string]string) // proxy name -> dialer-proxy name\n\n\t// collect all proxies with dialer-proxy configured\n\tfor name, proxy := range proxies {\n\t\tdialerProxy := proxy.ProxyInfo().DialerProxy\n\t\tif dialerProxy != \"\" {\n\t\t\t// validate each dialer-proxy reference\n\t\t\t_, exist := proxies[dialerProxy]\n\t\t\tif !exist {\n\t\t\t\treturn fmt.Errorf(\"proxy [%s] dialer-proxy [%s] not found\", name, dialerProxy)\n\t\t\t}\n\n\t\t\t// build dependency graph\n\t\t\tgraph[name] = dialerProxy\n\t\t}\n\t}\n\n\t// perform depth-first search to detect cycles for each proxy\n\tfor name := range graph {\n\t\tvisited := make(map[string]bool, len(graph))\n\t\tpath := make([]string, 0, len(graph))\n\t\tif validateDialerProxiesHasCycle(name, graph, visited, path) {\n\t\t\treturn fmt.Errorf(\"proxy [%s] has circular dialer-proxy dependency\", name)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// validateDialerProxiesHasCycle performs DFS to detect if there's a cycle starting from current proxy\nfunc validateDialerProxiesHasCycle(current string, graph map[string]string, visited map[string]bool, path []string) bool {\n\t// check if current is already in path (cycle detected)\n\tfor _, p := range path {\n\t\tif p == current {\n\t\t\treturn true\n\t\t}\n\t}\n\n\t// already visited and no cycle\n\tif visited[current] {\n\t\treturn false\n\t}\n\n\tvisited[current] = true\n\tpath = append(path, current)\n\n\t// check dialer-proxy of current proxy\n\tif dialerProxy, exists := graph[current]; exists {\n\t\tif validateDialerProxiesHasCycle(dialerProxy, graph, visited, path) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc verifyIP6() bool {\n\tif skip, _ := strconv.ParseBool(os.Getenv(\"SKIP_SYSTEM_IPV6_CHECK\")); skip {\n\t\treturn true\n\t}\n\tif iAddrs, err := net.InterfaceAddrs(); err == nil {\n\t\tfor _, addr := range iAddrs {\n\t\t\tif prefix, err := netip.ParsePrefix(addr.String()); err == nil {\n\t\t\t\tif addr := prefix.Addr().Unmap(); addr.Is6() && addr.IsGlobalUnicast() {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else {\n\t\t// eg: Calling net.InterfaceAddrs() fails on Android SDK 30\n\t\t// https://github.com/golang/go/issues/40569\n\t\treturn true // just ignore\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "core/Clash.Meta/config/utils_test.go",
    "content": "package config\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestValidateDialerProxies(t *testing.T) {\n\ttestCases := []struct {\n\t\ttestName    string\n\t\tproxy       []map[string]any\n\t\terrContains string\n\t}{\n\t\t{\n\t\t\ttestName: \"ValidReference\",\n\t\t\tproxy: []map[string]any{ // create proxy with valid dialer-proxy reference\n\t\t\t\t{\"name\": \"base-proxy\", \"type\": \"socks5\", \"server\": \"127.0.0.1\", \"port\": 1080},\n\t\t\t\t{\"name\": \"proxy-with-dialer\", \"type\": \"socks5\", \"server\": \"127.0.0.1\", \"port\": 1081, \"dialer-proxy\": \"base-proxy\"},\n\t\t\t},\n\t\t\terrContains: \"\",\n\t\t},\n\t\t{\n\t\t\ttestName: \"NotFoundReference\",\n\t\t\tproxy: []map[string]any{ // create proxy with non-existent dialer-proxy reference\n\t\t\t\t{\"name\": \"proxy-with-dialer\", \"type\": \"socks5\", \"server\": \"127.0.0.1\", \"port\": 1081, \"dialer-proxy\": \"non-existent-proxy\"},\n\t\t\t},\n\t\t\terrContains: \"not found\",\n\t\t},\n\t\t{\n\t\t\ttestName: \"CircularDependency\",\n\t\t\tproxy: []map[string]any{\n\t\t\t\t// create proxy A that references B\n\t\t\t\t{\"name\": \"proxy-a\", \"type\": \"socks5\", \"server\": \"127.0.0.1\", \"port\": 1080, \"dialer-proxy\": \"proxy-c\"},\n\t\t\t\t// create proxy B that references C\n\t\t\t\t{\"name\": \"proxy-b\", \"type\": \"socks5\", \"server\": \"127.0.0.1\", \"port\": 1081, \"dialer-proxy\": \"proxy-a\"},\n\t\t\t\t// create proxy C that references A (creates cycle)\n\t\t\t\t{\"name\": \"proxy-c\", \"type\": \"socks5\", \"server\": \"127.0.0.1\", \"port\": 1082, \"dialer-proxy\": \"proxy-a\"},\n\t\t\t},\n\t\t\terrContains: \"circular\",\n\t\t},\n\t\t{\n\t\t\ttestName: \"ComplexChain\",\n\t\t\tproxy: []map[string]any{ // create a valid chain: proxy-d -> proxy-c -> proxy-b -> proxy-a\n\t\t\t\t{\"name\": \"proxy-a\", \"type\": \"socks5\", \"server\": \"127.0.0.1\", \"port\": 1080},\n\t\t\t\t{\"name\": \"proxy-b\", \"type\": \"socks5\", \"server\": \"127.0.0.1\", \"port\": 1081, \"dialer-proxy\": \"proxy-a\"},\n\t\t\t\t{\"name\": \"proxy-c\", \"type\": \"socks5\", \"server\": \"127.0.0.1\", \"port\": 1082, \"dialer-proxy\": \"proxy-b\"},\n\t\t\t\t{\"name\": \"proxy-d\", \"type\": \"socks5\", \"server\": \"127.0.0.1\", \"port\": 1083, \"dialer-proxy\": \"proxy-c\"},\n\t\t\t},\n\t\t\terrContains: \"\",\n\t\t},\n\t\t{\n\t\t\ttestName: \"EmptyDialerProxy\",\n\t\t\tproxy: []map[string]any{ // create proxy without dialer-proxy\n\t\t\t\t{\"name\": \"simple-proxy\", \"type\": \"socks5\", \"server\": \"127.0.0.1\", \"port\": 1080},\n\t\t\t},\n\t\t\terrContains: \"\",\n\t\t},\n\t\t{\n\t\t\ttestName: \"SelfReference\",\n\t\t\tproxy: []map[string]any{ // create proxy that references itself\n\t\t\t\t{\"name\": \"self-proxy\", \"type\": \"socks5\", \"server\": \"127.0.0.1\", \"port\": 1080, \"dialer-proxy\": \"self-proxy\"},\n\t\t\t},\n\t\t\terrContains: \"circular\",\n\t\t},\n\t}\n\n\tfor _, testCase := range testCases {\n\t\tt.Run(testCase.testName, func(t *testing.T) {\n\t\t\tconfig := RawConfig{Proxy: testCase.proxy}\n\t\t\t_, _, err := parseProxies(&config)\n\t\t\tif testCase.errContains == \"\" {\n\t\t\t\tassert.NoError(t, err, testCase.testName)\n\t\t\t} else {\n\t\t\t\tassert.ErrorContains(t, err, testCase.errContains, testCase.testName)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/constant/adapters.go",
    "content": "package constant\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/netip\"\n\t\"sync\"\n\t\"time\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"github.com/metacubex/mihomo/component/dialer\"\n)\n\n// Adapter Type\nconst (\n\tDirect AdapterType = iota\n\tReject\n\tRejectDrop\n\tCompatible\n\tPass\n\tDns\n\n\tRelay\n\tSelector\n\tFallback\n\tURLTest\n\tLoadBalance\n\n\tShadowsocks\n\tShadowsocksR\n\tSnell\n\tSocks5\n\tHttp\n\tVmess\n\tVless\n\tTrojan\n\tHysteria\n\tHysteria2\n\tWireGuard\n\tTuic\n\tSsh\n\tMieru\n\tAnyTLS\n\tSudoku\n\tMasque\n\tTrustTunnel\n)\n\nconst (\n\tDefaultTCPTimeout = dialer.DefaultTCPTimeout\n\tDefaultUDPTimeout = dialer.DefaultUDPTimeout\n\tDefaultDropTime   = 12 * DefaultTCPTimeout\n\tDefaultTLSTimeout = DefaultTCPTimeout\n)\n\nvar DefaultTestURL = \"https://www.gstatic.com/generate_204\"\n\nvar ErrNotSupport = errors.New(\"no support\")\n\ntype Connection interface {\n\tChains() Chain\n\tProviderChains() Chain\n\tAppendToChains(adapter ProxyAdapter)\n\tRemoteDestination() string\n}\n\ntype Chain []string\n\nfunc (c Chain) String() string {\n\tswitch len(c) {\n\tcase 0:\n\t\treturn \"\"\n\tcase 1:\n\t\treturn c[0]\n\tdefault:\n\t\treturn fmt.Sprintf(\"%s[%s]\", c[len(c)-1], c[0])\n\t}\n}\n\nfunc (c Chain) Last() string {\n\tswitch len(c) {\n\tcase 0:\n\t\treturn \"\"\n\tdefault:\n\t\treturn c[len(c)-1]\n\t}\n}\n\ntype Conn interface {\n\tN.ExtendedConn\n\tConnection\n}\n\ntype PacketConn interface {\n\tN.EnhancePacketConn\n\tConnection\n\tResolveUDP(ctx context.Context, metadata *Metadata) error\n}\n\ntype Dialer interface {\n\tDialContext(ctx context.Context, network, address string) (net.Conn, error)\n\tListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error)\n}\n\ntype ProxyInfo struct {\n\tXUDP         bool\n\tTFO          bool\n\tMPTCP        bool\n\tSMUX         bool\n\tInterface    string\n\tRoutingMark  int\n\tProviderName string\n\tDialerProxy  string\n}\n\ntype ProxyAdapter interface {\n\tName() string\n\tType() AdapterType\n\tAddr() string\n\tSupportUDP() bool\n\n\t// ProxyInfo contains some extra information maybe useful for MarshalJSON\n\tProxyInfo() ProxyInfo\n\tMarshalJSON() ([]byte, error)\n\n\t// DialContext return a C.Conn with protocol which\n\t// contains multiplexing-related reuse logic (if any)\n\tDialContext(ctx context.Context, metadata *Metadata) (Conn, error)\n\tListenPacketContext(ctx context.Context, metadata *Metadata) (PacketConn, error)\n\n\t// SupportUOT return UDP over TCP support\n\tSupportUOT() bool\n\n\t// IsL3Protocol return ProxyAdapter working in L3 (tell dns module not pass the domain to avoid loopback)\n\tIsL3Protocol(metadata *Metadata) bool\n\n\t// Unwrap extracts the proxy from a proxy-group. It returns nil when nothing to extract.\n\tUnwrap(metadata *Metadata, touch bool) Proxy\n\n\t// Close releasing associated resources\n\tClose() error\n}\n\ntype DelayHistory struct {\n\tTime  time.Time `json:\"time\"`\n\tDelay uint16    `json:\"delay\"`\n}\n\ntype ProxyState struct {\n\tAlive   bool           `json:\"alive\"`\n\tHistory []DelayHistory `json:\"history\"`\n}\n\ntype DelayHistoryStoreType int\n\ntype Proxy interface {\n\tProxyAdapter\n\tAdapter() ProxyAdapter\n\tAliveForTestUrl(url string) bool\n\tDelayHistory() []DelayHistory\n\tExtraDelayHistories() map[string]ProxyState\n\tLastDelayForTestUrl(url string) uint16\n\tURLTest(ctx context.Context, url string, expectedStatus utils.IntRanges[uint16]) (uint16, error)\n}\n\n// AdapterType is enum of adapter type\ntype AdapterType int\n\nfunc (at AdapterType) String() string {\n\tswitch at {\n\tcase Direct:\n\t\treturn \"Direct\"\n\tcase Reject:\n\t\treturn \"Reject\"\n\tcase RejectDrop:\n\t\treturn \"RejectDrop\"\n\tcase Compatible:\n\t\treturn \"Compatible\"\n\tcase Pass:\n\t\treturn \"Pass\"\n\tcase Dns:\n\t\treturn \"Dns\"\n\tcase Shadowsocks:\n\t\treturn \"Shadowsocks\"\n\tcase ShadowsocksR:\n\t\treturn \"ShadowsocksR\"\n\tcase Snell:\n\t\treturn \"Snell\"\n\tcase Socks5:\n\t\treturn \"Socks5\"\n\tcase Http:\n\t\treturn \"Http\"\n\tcase Vmess:\n\t\treturn \"Vmess\"\n\tcase Vless:\n\t\treturn \"Vless\"\n\tcase Trojan:\n\t\treturn \"Trojan\"\n\tcase Hysteria:\n\t\treturn \"Hysteria\"\n\tcase Hysteria2:\n\t\treturn \"Hysteria2\"\n\tcase WireGuard:\n\t\treturn \"WireGuard\"\n\tcase Tuic:\n\t\treturn \"Tuic\"\n\tcase Ssh:\n\t\treturn \"Ssh\"\n\tcase Mieru:\n\t\treturn \"Mieru\"\n\tcase AnyTLS:\n\t\treturn \"AnyTLS\"\n\tcase Sudoku:\n\t\treturn \"Sudoku\"\n\tcase Masque:\n\t\treturn \"Masque\"\n\tcase TrustTunnel:\n\t\treturn \"TrustTunnel\"\n\tcase Relay:\n\t\treturn \"Relay\"\n\tcase Selector:\n\t\treturn \"Selector\"\n\tcase Fallback:\n\t\treturn \"Fallback\"\n\tcase URLTest:\n\t\treturn \"URLTest\"\n\tcase LoadBalance:\n\t\treturn \"LoadBalance\"\n\tdefault:\n\t\treturn \"Unknown\"\n\t}\n}\n\n// UDPPacket contains the data of UDP packet, and offers control/info of UDP packet's source\ntype UDPPacket interface {\n\t// Data get the payload of UDP Packet\n\tData() []byte\n\n\t// WriteBack writes the payload with source IP/Port equals addr\n\t// - variable source IP/Port is important to STUN\n\t// - if addr is not provided, WriteBack will write out UDP packet with SourceIP/Port equals to original Target,\n\t//   this is important when using Fake-IP.\n\tWriteBack\n\n\t// Drop call after packet is used, could recycle buffer in this function.\n\tDrop()\n\n\t// LocalAddr returns the source IP/Port of packet\n\tLocalAddr() net.Addr\n}\n\ntype UDPPacketInAddr interface {\n\tInAddr() net.Addr\n}\n\n// PacketAdapter is a UDP Packet adapter for socks/redir/tun\ntype PacketAdapter interface {\n\tUDPPacket\n\t// Metadata returns destination metadata\n\tMetadata() *Metadata\n\t// Key is a SNAT key\n\tKey() string\n}\n\ntype packetAdapter struct {\n\tUDPPacket\n\tmetadata *Metadata\n\tkey      string\n}\n\n// Metadata returns destination metadata\nfunc (s *packetAdapter) Metadata() *Metadata {\n\treturn s.metadata\n}\n\n// Key is a SNAT key\nfunc (s *packetAdapter) Key() string {\n\treturn s.key\n}\n\nfunc NewPacketAdapter(packet UDPPacket, metadata *Metadata) PacketAdapter {\n\treturn &packetAdapter{\n\t\tpacket,\n\t\tmetadata,\n\t\tpacket.LocalAddr().String(),\n\t}\n}\n\ntype WriteBack interface {\n\tWriteBack(b []byte, addr net.Addr) (n int, err error)\n}\n\ntype WriteBackProxy interface {\n\tWriteBack\n\tUpdateWriteBack(wb WriteBack)\n}\n\ntype PacketSender interface {\n\t// Send will send PacketAdapter nonblocking\n\t// the implement must call UDPPacket.Drop() inside Send\n\tSend(PacketAdapter)\n\t// Process is a blocking loop to send PacketAdapter to PacketConn and update the WriteBackProxy\n\tProcess(PacketConn, WriteBackProxy)\n\t// Close stop the Process loop\n\tClose()\n\t// DoSniff will blocking after sniffer work done\n\tDoSniff(*Metadata) error\n\t// AddMapping add a destination NAT record\n\tAddMapping(originMetadata *Metadata, metadata *Metadata)\n\t// RestoreReadFrom restore destination NAT for ReadFrom\n\t// the implement must ensure returned netip.Add is valid (or just return input addr)\n\tRestoreReadFrom(addr netip.Addr) netip.Addr\n}\n\ntype NatTable interface {\n\tGetOrCreate(key string, maker func() PacketSender) (PacketSender, bool)\n\n\tDelete(key string)\n\n\tGetForLocalConn(lAddr, rAddr string) *net.UDPConn\n\n\tAddForLocalConn(lAddr, rAddr string, conn *net.UDPConn) bool\n\n\tRangeForLocalConn(lAddr string, f func(key string, value *net.UDPConn) bool)\n\n\tGetOrCreateLockForLocalConn(lAddr string, key string) (*sync.Cond, bool)\n\n\tDeleteForLocalConn(lAddr, key string)\n\n\tDeleteLockForLocalConn(lAddr, key string)\n}\n"
  },
  {
    "path": "core/Clash.Meta/constant/context.go",
    "content": "package constant\n\nimport (\n\t\"net\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\n\t\"github.com/gofrs/uuid/v5\"\n)\n\ntype PlainContext interface {\n\tID() uuid.UUID\n}\n\ntype ConnContext interface {\n\tPlainContext\n\tMetadata() *Metadata\n\tConn() *N.BufferedConn\n}\n\ntype PacketConnContext interface {\n\tPlainContext\n\tMetadata() *Metadata\n\tPacketConn() net.PacketConn\n}\n"
  },
  {
    "path": "core/Clash.Meta/constant/dns.go",
    "content": "package constant\n\nimport (\n\t\"errors\"\n\t\"strings\"\n)\n\n// DNSModeMapping is a mapping for EnhancedMode enum\nvar DNSModeMapping = map[string]DNSMode{\n\tDNSNormal.String():  DNSNormal,\n\tDNSFakeIP.String():  DNSFakeIP,\n\tDNSMapping.String(): DNSMapping,\n}\n\nconst (\n\tDNSNormal DNSMode = iota\n\tDNSFakeIP\n\tDNSMapping\n\tDNSHosts\n)\n\ntype DNSMode int\n\n// UnmarshalText unserialize EnhancedMode\nfunc (e *DNSMode) UnmarshalText(data []byte) error {\n\tmode, exist := DNSModeMapping[strings.ToLower(string(data))]\n\tif !exist {\n\t\treturn errors.New(\"invalid mode\")\n\t}\n\t*e = mode\n\treturn nil\n}\n\n// MarshalText serialize EnhancedMode\nfunc (e DNSMode) MarshalText() ([]byte, error) {\n\treturn []byte(e.String()), nil\n}\n\nfunc (e DNSMode) String() string {\n\tswitch e {\n\tcase DNSNormal:\n\t\treturn \"normal\"\n\tcase DNSFakeIP:\n\t\treturn \"fake-ip\"\n\tcase DNSMapping:\n\t\treturn \"redir-host\"\n\tcase DNSHosts:\n\t\treturn \"hosts\"\n\tdefault:\n\t\treturn \"unknown\"\n\t}\n}\n\ntype DNSPrefer int\n\nconst (\n\tDualStack DNSPrefer = iota\n\tIPv4Only\n\tIPv6Only\n\tIPv4Prefer\n\tIPv6Prefer\n)\n\nvar dnsPreferMap = map[string]DNSPrefer{\n\tDualStack.String():  DualStack,\n\tIPv4Only.String():   IPv4Only,\n\tIPv6Only.String():   IPv6Only,\n\tIPv4Prefer.String(): IPv4Prefer,\n\tIPv6Prefer.String(): IPv6Prefer,\n}\n\nfunc (d DNSPrefer) String() string {\n\tswitch d {\n\tcase DualStack:\n\t\treturn \"dual\"\n\tcase IPv4Only:\n\t\treturn \"ipv4\"\n\tcase IPv6Only:\n\t\treturn \"ipv6\"\n\tcase IPv4Prefer:\n\t\treturn \"ipv4-prefer\"\n\tcase IPv6Prefer:\n\t\treturn \"ipv6-prefer\"\n\tdefault:\n\t\treturn \"dual\"\n\t}\n}\n\nfunc (d DNSPrefer) MarshalText() ([]byte, error) {\n\treturn []byte(d.String()), nil\n}\n\nfunc (d *DNSPrefer) UnmarshalText(data []byte) error {\n\tp, exist := dnsPreferMap[strings.ToLower(string(data))]\n\tif !exist {\n\t\tp = DualStack\n\t}\n\t*d = p\n\treturn nil\n}\n\n// FilterModeMapping is a mapping for FilterMode enum\nvar FilterModeMapping = map[string]FilterMode{\n\tFilterBlackList.String(): FilterBlackList,\n\tFilterWhiteList.String(): FilterWhiteList,\n\tFilterRule.String():      FilterRule,\n}\n\ntype FilterMode int\n\nconst (\n\tFilterBlackList FilterMode = iota\n\tFilterWhiteList\n\tFilterRule\n)\n\nfunc (e FilterMode) String() string {\n\tswitch e {\n\tcase FilterBlackList:\n\t\treturn \"blacklist\"\n\tcase FilterWhiteList:\n\t\treturn \"whitelist\"\n\tcase FilterRule:\n\t\treturn \"rule\"\n\tdefault:\n\t\treturn \"unknown\"\n\t}\n}\n\nfunc (e FilterMode) MarshalText() ([]byte, error) {\n\treturn []byte(e.String()), nil\n}\n\nfunc (e *FilterMode) UnmarshalText(data []byte) error {\n\tmode, exist := FilterModeMapping[strings.ToLower(string(data))]\n\tif !exist {\n\t\treturn errors.New(\"invalid mode\")\n\t}\n\t*e = mode\n\treturn nil\n}\n\ntype HTTPVersion string\n\nconst (\n\t// HTTPVersion11 is HTTP/1.1.\n\tHTTPVersion11 HTTPVersion = \"http/1.1\"\n\t// HTTPVersion2 is HTTP/2.\n\tHTTPVersion2 HTTPVersion = \"h2\"\n\t// HTTPVersion3 is HTTP/3.\n\tHTTPVersion3 HTTPVersion = \"h3\"\n)\n"
  },
  {
    "path": "core/Clash.Meta/constant/features/android.go",
    "content": "//go:build android\n\npackage features\n\nconst Android = true\n"
  },
  {
    "path": "core/Clash.Meta/constant/features/android_stub.go",
    "content": "//go:build !android\n\npackage features\n\nconst Android = false\n"
  },
  {
    "path": "core/Clash.Meta/constant/features/goflags.go",
    "content": "package features\n\nimport \"runtime/debug\"\n\nvar (\n\tGOARM   string\n\tGOMIPS  string\n\tGOAMD64 string\n)\n\nfunc init() {\n\tif info, ok := debug.ReadBuildInfo(); ok {\n\t\tfor _, bs := range info.Settings {\n\t\t\tswitch bs.Key {\n\t\t\tcase \"GOARM\":\n\t\t\t\tGOARM = bs.Value\n\t\t\tcase \"GOMIPS\":\n\t\t\t\tGOMIPS = bs.Value\n\t\t\tcase \"GOAMD64\":\n\t\t\t\tGOAMD64 = bs.Value\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/constant/features/low_memory.go",
    "content": "//go:build with_low_memory\n\npackage features\n\nconst WithLowMemory = true\n"
  },
  {
    "path": "core/Clash.Meta/constant/features/low_memory_stub.go",
    "content": "//go:build !with_low_memory\n\npackage features\n\nconst WithLowMemory = false\n"
  },
  {
    "path": "core/Clash.Meta/constant/features/no_fake_tcp.go",
    "content": "//go:build no_fake_tcp\n\npackage features\n\nconst NoFakeTCP = true\n"
  },
  {
    "path": "core/Clash.Meta/constant/features/no_fake_tcp_stub.go",
    "content": "//go:build !no_fake_tcp\n\npackage features\n\nconst NoFakeTCP = false\n"
  },
  {
    "path": "core/Clash.Meta/constant/features/tags.go",
    "content": "package features\n\nfunc Tags() (tags []string) {\n\tif WithLowMemory {\n\t\ttags = append(tags, \"with_low_memory\")\n\t}\n\tif NoFakeTCP {\n\t\ttags = append(tags, \"no_fake_tcp\")\n\t}\n\tif WithGVisor {\n\t\ttags = append(tags, \"with_gvisor\")\n\t}\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/constant/features/version.go",
    "content": "package features\n\nvar WindowsMajorVersion uint32\nvar WindowsMinorVersion uint32\nvar WindowsBuildNumber uint32\n"
  },
  {
    "path": "core/Clash.Meta/constant/features/version_windows.go",
    "content": "package features\n\nimport \"golang.org/x/sys/windows\"\n\nfunc init() {\n\tversion := windows.RtlGetVersion()\n\tWindowsMajorVersion = version.MajorVersion\n\tWindowsMinorVersion = version.MinorVersion\n\tWindowsBuildNumber = version.BuildNumber\n}\n"
  },
  {
    "path": "core/Clash.Meta/constant/features/with_gvisor.go",
    "content": "//go:build with_gvisor\n\npackage features\n\nconst WithGVisor = true\n"
  },
  {
    "path": "core/Clash.Meta/constant/features/with_gvisor_stub.go",
    "content": "//go:build !with_gvisor\n\npackage features\n\nconst WithGVisor = false\n"
  },
  {
    "path": "core/Clash.Meta/constant/listener.go",
    "content": "package constant\n\nimport \"net\"\n\ntype Listener interface {\n\tRawAddress() string\n\tAddress() string\n\tClose() error\n}\n\ntype MultiAddrListener interface {\n\tClose() error\n\tConfig() string\n\tAddrList() (addrList []net.Addr)\n}\n\ntype InboundListener interface {\n\tName() string\n\tListen(tunnel Tunnel) error\n\tClose() error\n\tAddress() string\n\tRawAddress() string\n\tConfig() InboundConfig\n}\n\ntype InboundConfig interface {\n\tName() string\n\tEqual(config InboundConfig) bool\n}\n"
  },
  {
    "path": "core/Clash.Meta/constant/matcher.go",
    "content": "package constant\n\nimport \"net/netip\"\n\ntype DomainMatcher interface {\n\tMatchDomain(domain string) bool\n}\n\ntype IpMatcher interface {\n\tMatchIp(ip netip.Addr) bool\n}\n"
  },
  {
    "path": "core/Clash.Meta/constant/metadata.go",
    "content": "package constant\n\nimport (\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/netip\"\n\t\"strconv\"\n)\n\n// SOCKS address types as defined in RFC 1928 section 5.\nconst (\n\tAtypIPv4       AddrType = 1\n\tAtypDomainName AddrType = 3\n\tAtypIPv6       AddrType = 4\n)\n\nconst (\n\tTCP NetWork = iota\n\tUDP\n\tALLNet\n\tInvalidNet = 0xff\n)\n\nconst (\n\tHTTP Type = iota\n\tHTTPS\n\tSOCKS4\n\tSOCKS5\n\tSHADOWSOCKS\n\tVMESS\n\tVLESS\n\tREDIR\n\tTPROXY\n\tTROJAN\n\tTUNNEL\n\tTUN\n\tTUIC\n\tHYSTERIA2\n\tANYTLS\n\tMIERU\n\tSUDOKU\n\tTRUSTTUNNEL\n\tINNER\n)\n\ntype AddrType byte\n\nfunc (a AddrType) String() string {\n\tswitch a {\n\tcase AtypIPv4:\n\t\treturn \"IPv4\"\n\tcase AtypDomainName:\n\t\treturn \"DomainName\"\n\tcase AtypIPv6:\n\t\treturn \"IPv6\"\n\tdefault:\n\t\treturn \"Unknown\"\n\t}\n}\n\ntype NetWork int\n\nfunc (n NetWork) String() string {\n\tswitch n {\n\tcase TCP:\n\t\treturn \"tcp\"\n\tcase UDP:\n\t\treturn \"udp\"\n\tcase ALLNet:\n\t\treturn \"all\"\n\tdefault:\n\t\treturn \"invalid\"\n\t}\n}\n\nfunc (n NetWork) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(n.String())\n}\n\ntype Type int\n\nfunc (t Type) String() string {\n\tswitch t {\n\tcase HTTP:\n\t\treturn \"HTTP\"\n\tcase HTTPS:\n\t\treturn \"HTTPS\"\n\tcase SOCKS4:\n\t\treturn \"Socks4\"\n\tcase SOCKS5:\n\t\treturn \"Socks5\"\n\tcase SHADOWSOCKS:\n\t\treturn \"ShadowSocks\"\n\tcase VMESS:\n\t\treturn \"Vmess\"\n\tcase VLESS:\n\t\treturn \"Vless\"\n\tcase REDIR:\n\t\treturn \"Redir\"\n\tcase TPROXY:\n\t\treturn \"TProxy\"\n\tcase TROJAN:\n\t\treturn \"Trojan\"\n\tcase TUNNEL:\n\t\treturn \"Tunnel\"\n\tcase TUN:\n\t\treturn \"Tun\"\n\tcase TUIC:\n\t\treturn \"Tuic\"\n\tcase HYSTERIA2:\n\t\treturn \"Hysteria2\"\n\tcase ANYTLS:\n\t\treturn \"AnyTLS\"\n\tcase MIERU:\n\t\treturn \"Mieru\"\n\tcase SUDOKU:\n\t\treturn \"Sudoku\"\n\tcase TRUSTTUNNEL:\n\t\treturn \"TrustTunnel\"\n\tcase INNER:\n\t\treturn \"Inner\"\n\tdefault:\n\t\treturn \"Unknown\"\n\t}\n}\n\nfunc ParseType(t string) (*Type, error) {\n\tvar res Type\n\tswitch t {\n\tcase \"HTTP\":\n\t\tres = HTTP\n\tcase \"HTTPS\":\n\t\tres = HTTPS\n\tcase \"SOCKS4\":\n\t\tres = SOCKS4\n\tcase \"SOCKS5\":\n\t\tres = SOCKS5\n\tcase \"SHADOWSOCKS\":\n\t\tres = SHADOWSOCKS\n\tcase \"VMESS\":\n\t\tres = VMESS\n\tcase \"VLESS\":\n\t\tres = VLESS\n\tcase \"REDIR\":\n\t\tres = REDIR\n\tcase \"TPROXY\":\n\t\tres = TPROXY\n\tcase \"TROJAN\":\n\t\tres = TROJAN\n\tcase \"TUNNEL\":\n\t\tres = TUNNEL\n\tcase \"TUN\":\n\t\tres = TUN\n\tcase \"TUIC\":\n\t\tres = TUIC\n\tcase \"HYSTERIA2\":\n\t\tres = HYSTERIA2\n\tcase \"ANYTLS\":\n\t\tres = ANYTLS\n\tcase \"MIERU\":\n\t\tres = MIERU\n\tcase \"SUDOKU\":\n\t\tres = SUDOKU\n\tcase \"TRUSTTUNNEL\":\n\t\tres = TRUSTTUNNEL\n\tcase \"INNER\":\n\t\tres = INNER\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown type: %s\", t)\n\t}\n\treturn &res, nil\n}\n\nfunc (t Type) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(t.String())\n}\n\n// Metadata is used to store connection address\ntype Metadata struct {\n\tNetWork      NetWork    `json:\"network\"`\n\tType         Type       `json:\"type\"`\n\tSrcIP        netip.Addr `json:\"sourceIP\"`\n\tDstIP        netip.Addr `json:\"destinationIP\"`\n\tSrcGeoIP     []string   `json:\"sourceGeoIP\"`      // can be nil if never queried, empty slice if got no result\n\tDstGeoIP     []string   `json:\"destinationGeoIP\"` // can be nil if never queried, empty slice if got no result\n\tSrcIPASN     string     `json:\"sourceIPASN\"`\n\tDstIPASN     string     `json:\"destinationIPASN\"`\n\tSrcPort      uint16     `json:\"sourcePort,string\"`      // `,string` is used to compatible with old version json output\n\tDstPort      uint16     `json:\"destinationPort,string\"` // `,string` is used to compatible with old version json output\n\tInIP         netip.Addr `json:\"inboundIP\"`\n\tInPort       uint16     `json:\"inboundPort,string\"` // `,string` is used to compatible with old version json output\n\tInName       string     `json:\"inboundName\"`\n\tInUser       string     `json:\"inboundUser\"`\n\tHost         string     `json:\"host\"`\n\tDNSMode      DNSMode    `json:\"dnsMode\"`\n\tUid          uint32     `json:\"uid\"`\n\tProcess      string     `json:\"process\"`\n\tProcessPath  string     `json:\"processPath\"`\n\tSpecialProxy string     `json:\"specialProxy\"`\n\tSpecialRules string     `json:\"specialRules\"`\n\tRemoteDst    string     `json:\"remoteDestination\"`\n\tDSCP         uint8      `json:\"dscp\"`\n\n\tRawSrcAddr net.Addr `json:\"-\"`\n\tRawDstAddr net.Addr `json:\"-\"`\n\t// Only domain rule\n\tSniffHost string `json:\"sniffHost\"`\n}\n\nfunc (m *Metadata) RemoteAddress() string {\n\treturn net.JoinHostPort(m.String(), strconv.FormatUint(uint64(m.DstPort), 10))\n}\n\nfunc (m *Metadata) SourceAddress() string {\n\treturn net.JoinHostPort(m.SrcIP.String(), strconv.FormatUint(uint64(m.SrcPort), 10))\n}\n\nfunc (m *Metadata) SourceAddrPort() netip.AddrPort {\n\treturn netip.AddrPortFrom(m.SrcIP.Unmap(), m.SrcPort)\n}\n\nfunc (m *Metadata) SourceDetail() string {\n\tif m.Type == INNER {\n\t\treturn fmt.Sprintf(\"%s\", MihomoName)\n\t}\n\n\tswitch {\n\tcase m.Process != \"\" && m.Uid != 0:\n\t\treturn fmt.Sprintf(\"%s(%s, uid=%d)\", m.SourceAddress(), m.Process, m.Uid)\n\tcase m.Uid != 0:\n\t\treturn fmt.Sprintf(\"%s(uid=%d)\", m.SourceAddress(), m.Uid)\n\tcase m.Process != \"\":\n\t\treturn fmt.Sprintf(\"%s(%s)\", m.SourceAddress(), m.Process)\n\tdefault:\n\t\treturn fmt.Sprintf(\"%s\", m.SourceAddress())\n\t}\n}\n\nfunc (m *Metadata) SourceValid() bool {\n\treturn m.SrcPort != 0 && m.SrcIP.IsValid()\n}\n\nfunc (m *Metadata) AddrType() AddrType {\n\tswitch true {\n\tcase m.Host != \"\" || !m.DstIP.IsValid():\n\t\treturn AtypDomainName\n\tcase m.DstIP.Is4():\n\t\treturn AtypIPv4\n\tdefault:\n\t\treturn AtypIPv6\n\t}\n}\n\nfunc (m *Metadata) Resolved() bool {\n\treturn m.DstIP.IsValid()\n}\n\nfunc (m *Metadata) RuleHost() string {\n\tif len(m.SniffHost) == 0 {\n\t\treturn m.Host\n\t} else {\n\t\treturn m.SniffHost\n\t}\n}\n\n// Pure is used to solve unexpected behavior\n// when dialing proxy connection in DNSMapping mode.\nfunc (m *Metadata) Pure() *Metadata {\n\tif (m.DNSMode == DNSMapping || m.DNSMode == DNSHosts) && m.DstIP.IsValid() {\n\t\tcopyM := *m\n\t\tcopyM.Host = \"\"\n\t\treturn &copyM\n\t}\n\n\treturn m\n}\n\nfunc (m *Metadata) Clone() *Metadata {\n\tcopyM := *m\n\treturn &copyM\n}\n\nfunc (m *Metadata) AddrPort() netip.AddrPort {\n\treturn netip.AddrPortFrom(m.DstIP.Unmap(), m.DstPort)\n}\n\nfunc (m *Metadata) UDPAddr() *net.UDPAddr {\n\tif m.NetWork != UDP || !m.DstIP.IsValid() {\n\t\treturn nil\n\t}\n\treturn net.UDPAddrFromAddrPort(m.AddrPort())\n}\n\nfunc (m *Metadata) String() string {\n\tif m.Host != \"\" {\n\t\treturn m.Host\n\t} else if m.DstIP.IsValid() {\n\t\treturn m.DstIP.String()\n\t} else {\n\t\treturn \"<nil>\"\n\t}\n}\n\nfunc (m *Metadata) Valid() bool {\n\treturn m.Host != \"\" || m.DstIP.IsValid()\n}\n\nfunc (m *Metadata) SetRemoteAddr(addr net.Addr) error {\n\tif addr == nil {\n\t\treturn nil\n\t}\n\tif rawAddr, ok := addr.(interface{ RawAddr() net.Addr }); ok {\n\t\tif rawAddr := rawAddr.RawAddr(); rawAddr != nil {\n\t\t\tif err := m.SetRemoteAddr(rawAddr); err == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\tif addr, ok := addr.(interface{ AddrPort() netip.AddrPort }); ok { // *net.TCPAddr, *net.UDPAddr, M.Socksaddr\n\t\tif addrPort := addr.AddrPort(); addrPort.Port() != 0 {\n\t\t\tm.DstPort = addrPort.Port()\n\t\t\tif addrPort.IsValid() { // sing's M.Socksaddr maybe return an invalid AddrPort if it's a DomainName\n\t\t\t\tm.DstIP = addrPort.Addr().Unmap()\n\t\t\t\treturn nil\n\t\t\t} else {\n\t\t\t\tif addr, ok := addr.(interface{ AddrString() string }); ok { // must be sing's M.Socksaddr\n\t\t\t\t\tm.Host = addr.AddrString() // actually is M.Socksaddr.Fqdn\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn m.SetRemoteAddress(addr.String())\n}\n\nfunc (m *Metadata) SetRemoteAddress(rawAddress string) error {\n\thost, port, err := net.SplitHostPort(rawAddress)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar uint16Port uint16\n\tif port, err := strconv.ParseUint(port, 10, 16); err == nil {\n\t\tuint16Port = uint16(port)\n\t}\n\n\tif ip, err := netip.ParseAddr(host); err != nil {\n\t\tm.Host = host\n\t\tm.DstIP = netip.Addr{}\n\t} else {\n\t\tm.Host = \"\"\n\t\tm.DstIP = ip.Unmap()\n\t}\n\tm.DstPort = uint16Port\n\n\treturn nil\n}\n\nfunc (m *Metadata) SwapSrcDst() {\n\tm.SrcIP, m.DstIP = m.DstIP, m.SrcIP\n\tm.SrcPort, m.DstPort = m.DstPort, m.SrcPort\n\tm.SrcIPASN, m.DstIPASN = m.DstIPASN, m.SrcIPASN\n\tm.SrcGeoIP, m.DstGeoIP = m.DstGeoIP, m.SrcGeoIP\n}\n"
  },
  {
    "path": "core/Clash.Meta/constant/path.go",
    "content": "package constant\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\tP \"path\"\n\t\"path/filepath\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"github.com/metacubex/mihomo/constant/features\"\n)\n\nconst Name = \"mihomo\"\n\nvar (\n\tGeositeName = \"GeoSite.dat\"\n\tGeoipName   = \"GeoIP.dat\"\n\tASNName     = \"ASN.mmdb\"\n)\n\n// Path is used to get the configuration path\n//\n// on Unix systems, `$HOME/.config/mihomo`.\n// on Windows, `%USERPROFILE%/.config/mihomo`.\nvar Path = func() *path {\n\thomeDir, err := os.UserHomeDir()\n\tif err != nil {\n\t\thomeDir, _ = os.Getwd()\n\t}\n\tallowUnsafePath, _ := strconv.ParseBool(os.Getenv(\"SKIP_SAFE_PATH_CHECK\"))\n\thomeDir = P.Join(homeDir, \".config\", Name)\n\n\tif _, err = os.Stat(homeDir); err != nil {\n\t\tif configHome, ok := os.LookupEnv(\"XDG_CONFIG_HOME\"); ok {\n\t\t\thomeDir = P.Join(configHome, Name)\n\t\t}\n\t}\n\n\tvar safePaths []string\n\tfor _, safePath := range filepath.SplitList(os.Getenv(\"SAFE_PATHS\")) {\n\t\tsafePath = strings.TrimSpace(safePath)\n\t\tif len(safePath) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tsafePaths = append(safePaths, safePath)\n\t}\n\n\treturn &path{homeDir: homeDir, configFile: \"config.yaml\", allowUnsafePath: allowUnsafePath, safePaths: safePaths}\n}()\n\ntype path struct {\n\thomeDir         string\n\tconfigFile      string\n\tallowUnsafePath bool\n\tsafePaths       []string\n}\n\n// SetHomeDir is used to set the configuration path\nfunc SetHomeDir(root string) {\n\tPath.homeDir = root\n}\n\n// SetConfig is used to set the configuration file\nfunc SetConfig(file string) {\n\tPath.configFile = file\n}\n\nfunc (p *path) HomeDir() string {\n\treturn p.homeDir\n}\n\nfunc (p *path) Config() string {\n\treturn p.configFile\n}\n\n// Resolve return a absolute path or a relative path with homedir\nfunc (p *path) Resolve(path string) string {\n\tif !filepath.IsAbs(path) {\n\t\treturn filepath.Join(p.HomeDir(), path)\n\t}\n\treturn path\n}\n\n// IsSafePath return true if path is a subpath of homedir (or in the SAFE_PATHS environment variable)\nfunc (p *path) IsSafePath(path string) bool {\n\tif p.allowUnsafePath || features.Android {\n\t\treturn true\n\t}\n\tpath = p.Resolve(path)\n\tfor _, safePath := range p.SafePaths() {\n\t\tif rel, err := filepath.Rel(safePath, path); err == nil {\n\t\t\tif filepath.IsLocal(rel) {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (p *path) SafePaths() []string {\n\treturn append([]string{p.homeDir}, p.safePaths...) // add homedir to safePaths\n}\n\nfunc (p *path) ErrNotSafePath(path string) error {\n\treturn ErrNotSafePath{Path: path, SafePaths: p.SafePaths()}\n}\n\ntype ErrNotSafePath struct {\n\tPath      string\n\tSafePaths []string\n}\n\nfunc (e ErrNotSafePath) Error() string {\n\treturn fmt.Sprintf(\"path is not subpath of home directory or SAFE_PATHS: %s \\n allowed paths: %s\", e.Path, e.SafePaths)\n}\n\nfunc (p *path) GetPathByHash(prefix, name string) string {\n\thash := utils.MakeHash([]byte(name))\n\tfilename := hash.String()\n\treturn filepath.Join(p.HomeDir(), prefix, filename)\n}\n\nfunc (p *path) MMDB() string {\n\tfiles, err := os.ReadDir(p.homeDir)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\tfor _, fi := range files {\n\t\tif fi.IsDir() {\n\t\t\t// 目录则直接跳过\n\t\t\tcontinue\n\t\t} else {\n\t\t\tif strings.EqualFold(fi.Name(), \"Country.mmdb\") ||\n\t\t\t\tstrings.EqualFold(fi.Name(), \"geoip.db\") ||\n\t\t\t\tstrings.EqualFold(fi.Name(), \"geoip.metadb\") {\n\t\t\t\tGeoipName = fi.Name()\n\t\t\t\treturn P.Join(p.homeDir, fi.Name())\n\t\t\t}\n\t\t}\n\t}\n\treturn P.Join(p.homeDir, \"geoip.metadb\")\n}\n\nfunc (p *path) ASN() string {\n\tfiles, err := os.ReadDir(p.homeDir)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\tfor _, fi := range files {\n\t\tif fi.IsDir() {\n\t\t\t// 目录则直接跳过\n\t\t\tcontinue\n\t\t} else {\n\t\t\tif strings.EqualFold(fi.Name(), \"ASN.mmdb\") {\n\t\t\t\tASNName = fi.Name()\n\t\t\t\treturn P.Join(p.homeDir, fi.Name())\n\t\t\t}\n\t\t}\n\t}\n\treturn P.Join(p.homeDir, ASNName)\n}\n\nfunc (p *path) OldCache() string {\n\treturn P.Join(p.homeDir, \".cache\")\n}\n\nfunc (p *path) Cache() string {\n\treturn P.Join(p.homeDir, \"cache.db\")\n}\n\nfunc (p *path) GeoIP() string {\n\tfiles, err := os.ReadDir(p.homeDir)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\tfor _, fi := range files {\n\t\tif fi.IsDir() {\n\t\t\t// 目录则直接跳过\n\t\t\tcontinue\n\t\t} else {\n\t\t\tif strings.EqualFold(fi.Name(), \"GeoIP.dat\") {\n\t\t\t\tGeoipName = fi.Name()\n\t\t\t\treturn P.Join(p.homeDir, fi.Name())\n\t\t\t}\n\t\t}\n\t}\n\treturn P.Join(p.homeDir, \"GeoIP.dat\")\n}\n\nfunc (p *path) GeoSite() string {\n\tfiles, err := os.ReadDir(p.homeDir)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\tfor _, fi := range files {\n\t\tif fi.IsDir() {\n\t\t\t// 目录则直接跳过\n\t\t\tcontinue\n\t\t} else {\n\t\t\tif strings.EqualFold(fi.Name(), \"GeoSite.dat\") {\n\t\t\t\tGeositeName = fi.Name()\n\t\t\t\treturn P.Join(p.homeDir, fi.Name())\n\t\t\t}\n\t\t}\n\t}\n\treturn P.Join(p.homeDir, \"GeoSite.dat\")\n}\n\nfunc (p *path) GetAssetLocation(file string) string {\n\treturn P.Join(p.homeDir, file)\n}\n\nfunc (p *path) GetExecutableFullPath() string {\n\texePath, err := os.Executable()\n\tif err != nil {\n\t\treturn \"mihomo\"\n\t}\n\tres, _ := filepath.EvalSymlinks(exePath)\n\treturn res\n}\n"
  },
  {
    "path": "core/Clash.Meta/constant/path_test.go",
    "content": "package constant\n\nimport (\n\t\"github.com/stretchr/testify/assert\"\n\t\"testing\"\n)\n\nfunc TestPath(t *testing.T) {\n\tassert.False(t, (&path{}).IsSafePath(\"/usr/share/metacubexd/\"))\n\tassert.True(t, (&path{\n\t\tsafePaths: []string{\"/usr/share/metacubexd\"},\n\t}).IsSafePath(\"/usr/share/metacubexd/\"))\n\n\tassert.False(t, (&path{}).IsSafePath(\"../metacubexd/\"))\n\tassert.True(t, (&path{\n\t\thomeDir:   \"/usr/share/mihomo\",\n\t\tsafePaths: []string{\"/usr/share/metacubexd\"},\n\t}).IsSafePath(\"../metacubexd/\"))\n\tassert.False(t, (&path{\n\t\thomeDir:   \"/usr/share/mihomo\",\n\t\tsafePaths: []string{\"/usr/share/ycad\"},\n\t}).IsSafePath(\"../metacubexd/\"))\n\n\tassert.False(t, (&path{}).IsSafePath(\"/opt/mykeys/key1.key\"))\n\tassert.True(t, (&path{\n\t\tsafePaths: []string{\"/opt/mykeys\"},\n\t}).IsSafePath(\"/opt/mykeys/key1.key\"))\n\tassert.True(t, (&path{\n\t\tsafePaths: []string{\"/opt/mykeys/\"},\n\t}).IsSafePath(\"/opt/mykeys/key1.key\"))\n\tassert.True(t, (&path{\n\t\tsafePaths: []string{\"/opt/mykeys/key1.key\"},\n\t}).IsSafePath(\"/opt/mykeys/key1.key\"))\n\n\tassert.True(t, (&path{}).IsSafePath(\"key1.key\"))\n\tassert.True(t, (&path{}).IsSafePath(\"./key1.key\"))\n\tassert.True(t, (&path{}).IsSafePath(\"./mykey/key1.key\"))\n\tassert.True(t, (&path{}).IsSafePath(\"./mykey/../key1.key\"))\n\tassert.False(t, (&path{}).IsSafePath(\"./mykey/../../key1.key\"))\n\n}\n"
  },
  {
    "path": "core/Clash.Meta/constant/provider/interface.go",
    "content": "package provider\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"github.com/metacubex/mihomo/constant\"\n)\n\n// Vehicle Type\nconst (\n\tFile VehicleType = iota\n\tHTTP\n\tCompatible\n\tInline\n)\n\n// VehicleType defined\ntype VehicleType int\n\nfunc (v VehicleType) String() string {\n\tswitch v {\n\tcase File:\n\t\treturn \"File\"\n\tcase HTTP:\n\t\treturn \"HTTP\"\n\tcase Compatible:\n\t\treturn \"Compatible\"\n\tcase Inline:\n\t\treturn \"Inline\"\n\tdefault:\n\t\treturn \"Unknown\"\n\t}\n}\n\ntype Vehicle interface {\n\tRead(ctx context.Context, oldHash utils.HashType) (buf []byte, hash utils.HashType, err error)\n\tWrite(buf []byte) error\n\tPath() string\n\tUrl() string\n\tProxy() string\n\tType() VehicleType\n}\n\n// Provider Type\nconst (\n\tProxy ProviderType = iota\n\tRule\n)\n\n// ProviderType defined\ntype ProviderType int\n\nfunc (pt ProviderType) String() string {\n\tswitch pt {\n\tcase Proxy:\n\t\treturn \"Proxy\"\n\tcase Rule:\n\t\treturn \"Rule\"\n\tdefault:\n\t\treturn \"Unknown\"\n\t}\n}\n\n// Provider interface\ntype Provider interface {\n\tName() string\n\tVehicleType() VehicleType\n\tType() ProviderType\n\tInitial() error\n\tUpdate() error\n}\n\n// ProxyProvider interface\ntype ProxyProvider interface {\n\tProvider\n\tProxies() []constant.Proxy\n\tCount() int\n\t// Touch is used to inform the provider that the proxy is actually being used while getting the list of proxies.\n\t// Commonly used in DialContext and DialPacketConn\n\tTouch()\n\tHealthCheck()\n\tVersion() uint32\n\tRegisterHealthCheckTask(url string, expectedStatus utils.IntRanges[uint16], filter string, interval uint)\n\tHealthCheckURL() string\n}\n\n// RuleProvider interface\ntype RuleProvider interface {\n\tProvider\n\tBehavior() RuleBehavior\n\tCount() int\n\tMatch(metadata *constant.Metadata, helper constant.RuleMatchHelper) bool\n\tStrategy() any\n}\n\n// Rule Behavior\nconst (\n\tDomain RuleBehavior = iota\n\tIPCIDR\n\tClassical\n)\n\n// RuleBehavior defined\ntype RuleBehavior int\n\nfunc (rt RuleBehavior) String() string {\n\tswitch rt {\n\tcase Domain:\n\t\treturn \"Domain\"\n\tcase IPCIDR:\n\t\treturn \"IPCIDR\"\n\tcase Classical:\n\t\treturn \"Classical\"\n\tdefault:\n\t\treturn \"Unknown\"\n\t}\n}\n\nfunc (rt RuleBehavior) Byte() byte {\n\tswitch rt {\n\tcase Domain:\n\t\treturn 0\n\tcase IPCIDR:\n\t\treturn 1\n\tcase Classical:\n\t\treturn 2\n\tdefault:\n\t\treturn 255\n\t}\n}\n\nfunc ParseBehavior(s string) (behavior RuleBehavior, err error) {\n\tswitch s {\n\tcase \"domain\":\n\t\tbehavior = Domain\n\tcase \"ipcidr\":\n\t\tbehavior = IPCIDR\n\tcase \"classical\":\n\t\tbehavior = Classical\n\tdefault:\n\t\terr = fmt.Errorf(\"unsupported behavior type: %s\", s)\n\t}\n\treturn\n}\n\nconst (\n\tYamlRule RuleFormat = iota\n\tTextRule\n\tMrsRule\n)\n\ntype RuleFormat int\n\nfunc (rf RuleFormat) String() string {\n\tswitch rf {\n\tcase YamlRule:\n\t\treturn \"YamlRule\"\n\tcase TextRule:\n\t\treturn \"TextRule\"\n\tcase MrsRule:\n\t\treturn \"MrsRule\"\n\tdefault:\n\t\treturn \"Unknown\"\n\t}\n}\n\nfunc ParseRuleFormat(s string) (format RuleFormat, err error) {\n\tswitch s {\n\tcase \"\", \"yaml\":\n\t\tformat = YamlRule\n\tcase \"text\":\n\t\tformat = TextRule\n\tcase \"mrs\":\n\t\tformat = MrsRule\n\tdefault:\n\t\terr = fmt.Errorf(\"unsupported format type: %s\", s)\n\t}\n\treturn\n}\n\ntype Tunnel interface {\n\tProviders() map[string]ProxyProvider\n\tRuleProviders() map[string]RuleProvider\n\tRuleUpdateCallback() *utils.Callback[RuleProvider]\n}\n"
  },
  {
    "path": "core/Clash.Meta/constant/rule.go",
    "content": "package constant\n\nimport \"time\"\n\n// Rule Type\nconst (\n\tDomain RuleType = iota\n\tDomainSuffix\n\tDomainKeyword\n\tDomainRegex\n\tDomainWildcard\n\tGEOSITE\n\tGEOIP\n\tSrcGEOIP\n\tIPASN\n\tSrcIPASN\n\tIPCIDR\n\tSrcIPCIDR\n\tIPSuffix\n\tSrcIPSuffix\n\tSrcPort\n\tDstPort\n\tInPort\n\tDSCP\n\tInUser\n\tInName\n\tInType\n\tProcessName\n\tProcessPath\n\tProcessNameRegex\n\tProcessPathRegex\n\tProcessNameWildcard\n\tProcessPathWildcard\n\tRuleSet\n\tNetwork\n\tUid\n\tSubRules\n\tMATCH\n\tAND\n\tOR\n\tNOT\n)\n\ntype RuleType int\n\nfunc (rt RuleType) String() string {\n\tswitch rt {\n\tcase Domain:\n\t\treturn \"Domain\"\n\tcase DomainSuffix:\n\t\treturn \"DomainSuffix\"\n\tcase DomainKeyword:\n\t\treturn \"DomainKeyword\"\n\tcase DomainRegex:\n\t\treturn \"DomainRegex\"\n\tcase DomainWildcard:\n\t\treturn \"DomainWildcard\"\n\tcase GEOSITE:\n\t\treturn \"GeoSite\"\n\tcase GEOIP:\n\t\treturn \"GeoIP\"\n\tcase SrcGEOIP:\n\t\treturn \"SrcGeoIP\"\n\tcase IPASN:\n\t\treturn \"IPASN\"\n\tcase SrcIPASN:\n\t\treturn \"SrcIPASN\"\n\tcase IPCIDR:\n\t\treturn \"IPCIDR\"\n\tcase SrcIPCIDR:\n\t\treturn \"SrcIPCIDR\"\n\tcase IPSuffix:\n\t\treturn \"IPSuffix\"\n\tcase SrcIPSuffix:\n\t\treturn \"SrcIPSuffix\"\n\tcase SrcPort:\n\t\treturn \"SrcPort\"\n\tcase DstPort:\n\t\treturn \"DstPort\"\n\tcase InPort:\n\t\treturn \"InPort\"\n\tcase InUser:\n\t\treturn \"InUser\"\n\tcase InName:\n\t\treturn \"InName\"\n\tcase InType:\n\t\treturn \"InType\"\n\tcase ProcessName:\n\t\treturn \"ProcessName\"\n\tcase ProcessPath:\n\t\treturn \"ProcessPath\"\n\tcase ProcessNameRegex:\n\t\treturn \"ProcessNameRegex\"\n\tcase ProcessPathRegex:\n\t\treturn \"ProcessPathRegex\"\n\tcase ProcessNameWildcard:\n\t\treturn \"ProcessNameWildcard\"\n\tcase ProcessPathWildcard:\n\t\treturn \"ProcessPathWildcard\"\n\tcase MATCH:\n\t\treturn \"Match\"\n\tcase RuleSet:\n\t\treturn \"RuleSet\"\n\tcase Network:\n\t\treturn \"Network\"\n\tcase DSCP:\n\t\treturn \"DSCP\"\n\tcase Uid:\n\t\treturn \"Uid\"\n\tcase SubRules:\n\t\treturn \"SubRules\"\n\tcase AND:\n\t\treturn \"AND\"\n\tcase OR:\n\t\treturn \"OR\"\n\tcase NOT:\n\t\treturn \"NOT\"\n\tdefault:\n\t\treturn \"Unknown\"\n\t}\n}\n\ntype Rule interface {\n\tRuleType() RuleType\n\tMatch(metadata *Metadata, helper RuleMatchHelper) (bool, string)\n\tAdapter() string\n\tPayload() string\n\tProviderNames() []string\n}\n\ntype RuleWrapper interface {\n\tRule\n\n\t// SetDisabled to set enable/disable rule\n\tSetDisabled(v bool)\n\t// IsDisabled return rule is disabled or not\n\tIsDisabled() bool\n\n\t// HitCount for statistics\n\tHitCount() uint64\n\t// HitAt for statistics\n\tHitAt() time.Time\n\t// MissCount for statistics\n\tMissCount() uint64\n\t// MissAt for statistics\n\tMissAt() time.Time\n\n\t// Unwrap return Rule\n\tUnwrap() Rule\n}\n\ntype RuleMatchHelper struct {\n\tResolveIP   func()\n\tFindProcess func()\n}\n\ntype RuleGroup interface {\n\tRule\n\tGetRecodeSize() int\n}\n"
  },
  {
    "path": "core/Clash.Meta/constant/sniffer/sniffer.go",
    "content": "package sniffer\n\nimport \"github.com/metacubex/mihomo/constant\"\n\ntype Sniffer interface {\n\tSupportNetwork() constant.NetWork\n\t// SniffData must not change input bytes\n\tSniffData(bytes []byte) (string, error)\n\tProtocol() string\n\tSupportPort(port uint16) bool\n}\n\ntype ReplaceDomain func(metadata *constant.Metadata, host string)\n\ntype MultiPacketSniffer interface {\n\tWrapperSender(packetSender constant.PacketSender, replaceDomain ReplaceDomain) constant.PacketSender\n}\n\nconst (\n\tTLS Type = iota\n\tHTTP\n\tQUIC\n)\n\nvar (\n\tList = []Type{TLS, HTTP, QUIC}\n)\n\ntype Type int\n\nfunc (rt Type) String() string {\n\tswitch rt {\n\tcase TLS:\n\t\treturn \"TLS\"\n\tcase HTTP:\n\t\treturn \"HTTP\"\n\tcase QUIC:\n\t\treturn \"QUIC\"\n\tdefault:\n\t\treturn \"Unknown\"\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/constant/tun.go",
    "content": "package constant\n\nimport (\n\t\"errors\"\n\t\"strings\"\n)\n\nvar StackTypeMapping = map[string]TUNStack{\n\tstrings.ToLower(TunGvisor.String()): TunGvisor,\n\tstrings.ToLower(TunSystem.String()): TunSystem,\n\tstrings.ToLower(TunMixed.String()):  TunMixed,\n}\n\nconst (\n\tTunGvisor TUNStack = iota\n\tTunSystem\n\tTunMixed\n)\n\ntype TUNStack int\n\n// UnmarshalText unserialize TUNStack\nfunc (e *TUNStack) UnmarshalText(data []byte) error {\n\tmode, exist := StackTypeMapping[strings.ToLower(string(data))]\n\tif !exist {\n\t\treturn errors.New(\"invalid tun stack\")\n\t}\n\t*e = mode\n\treturn nil\n}\n\n// MarshalText serialize TUNStack with json\nfunc (e TUNStack) MarshalText() ([]byte, error) {\n\treturn []byte(e.String()), nil\n}\n\nfunc (e TUNStack) String() string {\n\tswitch e {\n\tcase TunGvisor:\n\t\treturn \"gVisor\"\n\tcase TunSystem:\n\t\treturn \"System\"\n\tcase TunMixed:\n\t\treturn \"Mixed\"\n\tdefault:\n\t\treturn \"unknown\"\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/constant/tunnel.go",
    "content": "package constant\n\nimport \"net\"\n\ntype Tunnel interface {\n\t// HandleTCPConn will handle a tcp connection blocking\n\tHandleTCPConn(conn net.Conn, metadata *Metadata)\n\t// HandleUDPPacket will handle a udp packet nonblocking\n\tHandleUDPPacket(packet UDPPacket, metadata *Metadata)\n\t// NatTable return nat table\n\tNatTable() NatTable\n}\n"
  },
  {
    "path": "core/Clash.Meta/constant/version.go",
    "content": "package constant\n\nvar (\n\tMeta       = true\n\tVersion    = \"1.19.24\"\n\tBuildTime  = \"unknown time\"\n\tMihomoName = \"mihomo\"\n)\n"
  },
  {
    "path": "core/Clash.Meta/context/conn.go",
    "content": "package context\n\nimport (\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"net\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\n\t\"github.com/gofrs/uuid/v5\"\n)\n\ntype ConnContext struct {\n\tid       uuid.UUID\n\tmetadata *C.Metadata\n\tconn     *N.BufferedConn\n}\n\nfunc NewConnContext(conn net.Conn, metadata *C.Metadata) *ConnContext {\n\treturn &ConnContext{\n\t\tid:       utils.NewUUIDV4(),\n\t\tmetadata: metadata,\n\t\tconn:     N.NewBufferedConn(conn),\n\t}\n}\n\n// ID implement C.ConnContext ID\nfunc (c *ConnContext) ID() uuid.UUID {\n\treturn c.id\n}\n\n// Metadata implement C.ConnContext Metadata\nfunc (c *ConnContext) Metadata() *C.Metadata {\n\treturn c.metadata\n}\n\n// Conn implement C.ConnContext Conn\nfunc (c *ConnContext) Conn() *N.BufferedConn {\n\treturn c.conn\n}\n"
  },
  {
    "path": "core/Clash.Meta/context/dns.go",
    "content": "package context\n\nimport (\n\t\"context\"\n\n\t\"github.com/metacubex/mihomo/common/utils\"\n\n\t\"github.com/gofrs/uuid/v5\"\n)\n\nconst (\n\tDNSTypeHost   = \"host\"\n\tDNSTypeFakeIP = \"fakeip\"\n\tDNSTypeRaw    = \"raw\"\n)\n\ntype DNSContext struct {\n\tcontext.Context\n\n\tid uuid.UUID\n\ttp string\n}\n\nfunc NewDNSContext(ctx context.Context) *DNSContext {\n\treturn &DNSContext{\n\t\tContext: ctx,\n\n\t\tid: utils.NewUUIDV4(),\n\t}\n}\n\n// ID implement C.PlainContext ID\nfunc (c *DNSContext) ID() uuid.UUID {\n\treturn c.id\n}\n\n// SetType set type of response\nfunc (c *DNSContext) SetType(tp string) {\n\tc.tp = tp\n}\n\n// Type return type of response\nfunc (c *DNSContext) Type() string {\n\treturn c.tp\n}\n"
  },
  {
    "path": "core/Clash.Meta/context/packetconn.go",
    "content": "package context\n\nimport (\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/common/utils\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\n\t\"github.com/gofrs/uuid/v5\"\n)\n\ntype PacketConnContext struct {\n\tid         uuid.UUID\n\tmetadata   *C.Metadata\n\tpacketConn net.PacketConn\n}\n\nfunc NewPacketConnContext(metadata *C.Metadata) *PacketConnContext {\n\treturn &PacketConnContext{\n\t\tid:       utils.NewUUIDV4(),\n\t\tmetadata: metadata,\n\t}\n}\n\n// ID implement C.PacketConnContext ID\nfunc (pc *PacketConnContext) ID() uuid.UUID {\n\treturn pc.id\n}\n\n// Metadata implement C.PacketConnContext Metadata\nfunc (pc *PacketConnContext) Metadata() *C.Metadata {\n\treturn pc.metadata\n}\n\n// PacketConn implement C.PacketConnContext PacketConn\nfunc (pc *PacketConnContext) PacketConn() net.PacketConn {\n\treturn pc.packetConn\n}\n\n// InjectPacketConn injectPacketConn manually\nfunc (pc *PacketConnContext) InjectPacketConn(pconn C.PacketConn) {\n\tpc.packetConn = pconn\n}\n"
  },
  {
    "path": "core/Clash.Meta/dns/client.go",
    "content": "package dns\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"strings\"\n\t\"time\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\tD \"github.com/miekg/dns\"\n)\n\ntype client struct {\n\tport   string\n\thost   string\n\tdialer *dnsDialer\n\tschema string\n}\n\nvar _ dnsClient = (*client)(nil)\n\n// Address implements dnsClient\nfunc (c *client) Address() string {\n\treturn fmt.Sprintf(\"%s://%s\", c.schema, net.JoinHostPort(c.host, c.port))\n}\n\nfunc (c *client) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) {\n\tnetwork := \"udp\"\n\tif c.schema != \"udp\" {\n\t\tnetwork = \"tcp\"\n\t}\n\n\taddr := net.JoinHostPort(c.host, c.port)\n\tconn, err := c.dialer.DialContext(ctx, network, addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer conn.Close()\n\n\t// miekg/dns ExchangeContext doesn't respond to context cancel.\n\t// this is a workaround\n\ttype result struct {\n\t\tmsg *D.Msg\n\t\terr error\n\t}\n\tch := make(chan result, 1)\n\tgo func() {\n\t\tdClient := &D.Client{\n\t\t\tUDPSize: 4096,\n\t\t\tTimeout: 5 * time.Second,\n\t\t}\n\t\tdConn := &D.Conn{\n\t\t\tConn:    conn,\n\t\t\tUDPSize: dClient.UDPSize,\n\t\t}\n\n\t\tmsg, _, err := dClient.ExchangeWithConn(m, dConn)\n\n\t\t// Resolvers MUST resend queries over TCP if they receive a truncated UDP response (with TC=1 set)!\n\t\tif msg != nil && msg.Truncated && network == \"udp\" {\n\t\t\tnetwork = \"tcp\"\n\t\t\tlog.Debugln(\"[DNS] Truncated reply from %s:%s for %s over UDP, retrying over TCP\", c.host, c.port, m.Question[0].String())\n\t\t\tvar tcpConn net.Conn\n\t\t\ttcpConn, err = c.dialer.DialContext(ctx, network, addr)\n\t\t\tif err != nil {\n\t\t\t\tch <- result{msg, err}\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdefer tcpConn.Close()\n\t\t\tdConn.Conn = tcpConn\n\t\t\tmsg, _, err = dClient.ExchangeWithConn(m, dConn)\n\t\t}\n\n\t\tch <- result{msg, err}\n\t}()\n\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn nil, ctx.Err()\n\tcase ret := <-ch:\n\t\treturn ret.msg, ret.err\n\t}\n}\n\nfunc (c *client) ResetConnection() {}\n\nfunc newClient(addr string, resolver *Resolver, netType string, params map[string]string, proxyAdapter C.ProxyAdapter, proxyName string) *client {\n\thost, port, _ := net.SplitHostPort(addr)\n\tc := &client{\n\t\tport:   port,\n\t\thost:   host,\n\t\tdialer: newDNSDialer(resolver, proxyAdapter, proxyName),\n\t\tschema: \"udp\",\n\t}\n\tif strings.HasPrefix(netType, \"tcp\") {\n\t\tc.schema = \"tcp\"\n\t}\n\treturn c\n}\n"
  },
  {
    "path": "core/Clash.Meta/dns/dhcp.go",
    "content": "package dns\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"net/netip\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/component/dhcp\"\n\t\"github.com/metacubex/mihomo/component/iface\"\n\tD \"github.com/miekg/dns\"\n)\n\nconst (\n\tIfaceTTL    = time.Second * 20\n\tDHCPTTL     = time.Hour\n\tDHCPTimeout = time.Minute\n)\n\ntype dhcpClient struct {\n\tifaceName string\n\n\tlock            sync.Mutex\n\tifaceInvalidate time.Time\n\tdnsInvalidate   time.Time\n\n\tifaceAddr netip.Prefix\n\tdone      chan struct{}\n\tclients   []dnsClient\n\terr       error\n}\n\nvar _ dnsClient = (*dhcpClient)(nil)\n\n// Address implements dnsClient\nfunc (d *dhcpClient) Address() string {\n\taddrs := make([]string, 0)\n\tfor _, c := range d.clients {\n\t\taddrs = append(addrs, c.Address())\n\t}\n\treturn strings.Join(addrs, \",\")\n}\n\nfunc (d *dhcpClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) {\n\tclients, err := d.resolve(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tmsg, _, err = batchExchange(ctx, clients, m)\n\treturn\n}\n\nfunc (d *dhcpClient) ResetConnection() {\n\tfor _, client := range d.clients {\n\t\tclient.ResetConnection()\n\t}\n}\n\nfunc (d *dhcpClient) resolve(ctx context.Context) ([]dnsClient, error) {\n\td.lock.Lock()\n\n\tinvalidated, err := d.invalidate()\n\tif err != nil {\n\t\td.err = err\n\t} else if invalidated {\n\t\tdone := make(chan struct{})\n\n\t\td.done = done\n\n\t\tgo func() {\n\t\t\tctx, cancel := context.WithTimeout(context.Background(), DHCPTimeout)\n\t\t\tdefer cancel()\n\n\t\t\tvar res []dnsClient\n\t\t\tdns, err := dhcp.ResolveDNSFromDHCP(ctx, d.ifaceName)\n\t\t\t// dns never empty if err is nil\n\t\t\tif err == nil {\n\t\t\t\tnameserver := make([]NameServer, 0, len(dns))\n\t\t\t\tfor _, item := range dns {\n\t\t\t\t\tnameserver = append(nameserver, NameServer{\n\t\t\t\t\t\tAddr:      net.JoinHostPort(item.String(), \"53\"),\n\t\t\t\t\t\tProxyName: d.ifaceName,\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tres = transform(nameserver, nil)\n\t\t\t}\n\n\t\t\td.lock.Lock()\n\t\t\tdefer d.lock.Unlock()\n\n\t\t\tclose(done)\n\n\t\t\td.done = nil\n\t\t\td.clients = res\n\t\t\td.err = err\n\t\t}()\n\t}\n\n\td.lock.Unlock()\n\n\tfor {\n\t\td.lock.Lock()\n\n\t\tres, err, done := d.clients, d.err, d.done\n\n\t\td.lock.Unlock()\n\n\t\t// initializing\n\t\tif res == nil && err == nil {\n\t\t\tselect {\n\t\t\tcase <-done:\n\t\t\t\tcontinue\n\t\t\tcase <-ctx.Done():\n\t\t\t\treturn nil, ctx.Err()\n\t\t\t}\n\t\t}\n\n\t\t// dirty return\n\t\treturn res, err\n\t}\n}\n\nfunc (d *dhcpClient) invalidate() (bool, error) {\n\tif time.Now().Before(d.ifaceInvalidate) {\n\t\treturn false, nil\n\t}\n\n\td.ifaceInvalidate = time.Now().Add(IfaceTTL)\n\n\tifaceObj, err := iface.ResolveInterface(d.ifaceName)\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\taddr, err := ifaceObj.PickIPv4Addr(netip.Addr{})\n\tif err != nil {\n\t\treturn false, err\n\t}\n\n\tif time.Now().Before(d.dnsInvalidate) && d.ifaceAddr == addr {\n\t\treturn false, nil\n\t}\n\n\td.dnsInvalidate = time.Now().Add(DHCPTTL)\n\td.ifaceAddr = addr\n\n\treturn d.done == nil, nil\n}\n\nfunc newDHCPClient(ifaceName string) *dhcpClient {\n\treturn &dhcpClient{ifaceName: ifaceName}\n}\n"
  },
  {
    "path": "core/Clash.Meta/dns/dialer.go",
    "content": "package dns\n\n// export functions from tunnel module\n\nimport \"github.com/metacubex/mihomo/tunnel\"\n\nconst RespectRules = tunnel.DnsRespectRules\n\ntype dnsDialer = tunnel.DNSDialer\n\nvar newDNSDialer = tunnel.NewDNSDialer\n"
  },
  {
    "path": "core/Clash.Meta/dns/doh.go",
    "content": "package dns\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/url\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/component/ca\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/quic-go\"\n\t\"github.com/metacubex/quic-go/http3\"\n\t\"github.com/metacubex/tls\"\n\tD \"github.com/miekg/dns\"\n\t\"golang.org/x/exp/slices\"\n)\n\n// Values to configure HTTP and HTTP/2 transport.\nconst (\n\t// transportDefaultSendPingTimeout is the default timeout for pinging\n\t// idle connections in HTTP/2 transport.\n\ttransportDefaultSendPingTimeout = 30 * time.Second\n\n\t// transportDefaultIdleConnTimeout is the default timeout for idle\n\t// connections in HTTP transport.\n\ttransportDefaultIdleConnTimeout = 5 * time.Minute\n\n\t// dohMaxConnsPerHost controls the maximum number of connections for\n\t// each host.  Note, that setting it to 1 may cause issues with Go's http\n\t// implementation, see https://github.com/AdguardTeam/dnsproxy/issues/278.\n\tdohMaxConnsPerHost = 2\n\tdialTimeout        = 10 * time.Second\n\n\t// dohMaxIdleConns controls the maximum number of connections being idle\n\t// at the same time.\n\tdohMaxIdleConns = 2\n\tmaxElapsedTime  = time.Second * 30\n)\n\nvar DefaultHTTPVersions = []C.HTTPVersion{C.HTTPVersion11, C.HTTPVersion2}\n\n// dnsOverHTTPS is a struct that implements the Upstream interface for the\n// DNS-over-HTTPS protocol.\ntype dnsOverHTTPS struct {\n\t// The Client's Transport typically has internal state (cached TCP\n\t// connections), so Clients should be reused instead of created as\n\t// needed. Clients are safe for concurrent use by multiple goroutines.\n\tclient   *http.Client\n\tclientMu sync.Mutex\n\n\t// quicConfig is the QUIC configuration that is used if HTTP/3 is enabled\n\t// for this upstream.\n\tquicConfig      *quic.Config\n\tquicConfigGuard sync.Mutex\n\n\turl            *url.URL\n\thttpVersions   []C.HTTPVersion\n\tdialer         *dnsDialer\n\taddr           string\n\tskipCertVerify bool\n}\n\n// type check\nvar _ dnsClient = (*dnsOverHTTPS)(nil)\n\n// newDoH returns the DNS-over-HTTPS Upstream.\nfunc newDoHClient(urlString string, r *Resolver, preferH3 bool, params map[string]string, proxyAdapter C.ProxyAdapter, proxyName string) dnsClient {\n\tu, _ := url.Parse(urlString)\n\thttpVersions := DefaultHTTPVersions\n\tif preferH3 {\n\t\thttpVersions = append(httpVersions, C.HTTPVersion3)\n\t}\n\n\tif params[\"h3\"] == \"true\" {\n\t\thttpVersions = []C.HTTPVersion{C.HTTPVersion3}\n\t}\n\n\tdoh := &dnsOverHTTPS{\n\t\turl:    u,\n\t\taddr:   u.String(),\n\t\tdialer: newDNSDialer(r, proxyAdapter, proxyName),\n\t\tquicConfig: &quic.Config{\n\t\t\tKeepAlivePeriod: QUICKeepAlivePeriod,\n\t\t\tTokenStore:      newQUICTokenStore(),\n\t\t},\n\t\thttpVersions: httpVersions,\n\t}\n\n\tif params[\"skip-cert-verify\"] == \"true\" {\n\t\tdoh.skipCertVerify = true\n\t}\n\n\truntime.SetFinalizer(doh, (*dnsOverHTTPS).Close)\n\n\treturn doh\n}\n\n// Address implements the Upstream interface for *dnsOverHTTPS.\nfunc (doh *dnsOverHTTPS) Address() string {\n\treturn doh.addr\n}\n\nfunc (doh *dnsOverHTTPS) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) {\n\t// Quote from https://www.rfc-editor.org/rfc/rfc8484.html:\n\t// In order to maximize HTTP cache friendliness, DoH clients using media\n\t// formats that include the ID field from the DNS message header, such\n\t// as \"application/dns-message\", SHOULD use a DNS ID of 0 in every DNS\n\t// request.\n\tm = m.Copy()\n\tid := m.Id\n\tm.Id = 0\n\tdefer func() {\n\t\t// Restore the original ID to not break compatibility with proxies.\n\t\tm.Id = id\n\t\tif msg != nil {\n\t\t\tmsg.Id = id\n\t\t}\n\t}()\n\n\t// Check if there was already an active client before sending the request.\n\t// We'll only attempt to re-connect if there was one.\n\tclient, isCached, err := doh.getClient(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to init http client: %w\", err)\n\t}\n\n\t// Make the first attempt to send the DNS query.\n\tmsg, err = doh.exchangeHTTPS(ctx, client, m)\n\n\t// Make up to 2 attempts to re-create the HTTP client and send the request\n\t// again.  There are several cases (mostly, with QUIC) where this workaround\n\t// is necessary to make HTTP client usable.  We need to make 2 attempts in\n\t// the case when the connection was closed (due to inactivity for example)\n\t// AND the server refuses to open a 0-RTT connection.\n\tfor i := 0; isCached && doh.shouldRetry(err) && i < 2; i++ {\n\t\tclient, err = doh.resetClient(ctx, err)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to reset http client: %w\", err)\n\t\t}\n\n\t\tmsg, err = doh.exchangeHTTPS(ctx, client, m)\n\t}\n\n\tif err != nil && !errors.Is(err, context.Canceled) && !errors.Is(err, context.DeadlineExceeded) {\n\t\t// If the request failed anyway, make sure we don't use this client.\n\t\t_, resErr := doh.resetClient(ctx, err)\n\n\t\treturn nil, fmt.Errorf(\"%w (resErr:%v)\", err, resErr)\n\t}\n\n\treturn msg, err\n}\n\n// Close implements the Upstream interface for *dnsOverHTTPS.\nfunc (doh *dnsOverHTTPS) Close() (err error) {\n\tdoh.clientMu.Lock()\n\tdefer doh.clientMu.Unlock()\n\n\truntime.SetFinalizer(doh, nil)\n\n\tif doh.client == nil {\n\t\treturn nil\n\t}\n\n\treturn doh.closeClient(doh.client)\n}\n\nfunc (doh *dnsOverHTTPS) ResetConnection() {\n\tdoh.clientMu.Lock()\n\tdefer doh.clientMu.Unlock()\n\n\tif doh.client == nil {\n\t\treturn\n\t}\n\n\t_ = doh.closeClient(doh.client)\n\tdoh.client = nil\n}\n\n// closeClient cleans up resources used by client if necessary.\nfunc (doh *dnsOverHTTPS) closeClient(client *http.Client) (err error) {\n\tclient.CloseIdleConnections()\n\n\tif isHTTP3(client) { // HTTP/3 may leak due to keep-alive connections.\n\t\treturn client.Transport.(io.Closer).Close()\n\t}\n\n\treturn nil\n}\n\n// exchangeHTTPS sends the DNS query to a DoH resolver using the specified\n// http.Client instance.\nfunc (doh *dnsOverHTTPS) exchangeHTTPS(ctx context.Context, client *http.Client, req *D.Msg) (resp *D.Msg, err error) {\n\tbuf, err := req.Pack()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"packing message: %w\", err)\n\t}\n\n\t// It appears, that GET requests are more memory-efficient with Golang\n\t// implementation of HTTP/2.\n\tmethod := http.MethodGet\n\tif isHTTP3(client) {\n\t\t// If we're using HTTP/3, use http3.MethodGet0RTT to force using 0-RTT.\n\t\tmethod = http3.MethodGet0RTT\n\t}\n\n\trequestUrl := *doh.url // don't modify origin url\n\trequestUrl.RawQuery = fmt.Sprintf(\"dns=%s\", base64.RawURLEncoding.EncodeToString(buf))\n\thttpReq, err := http.NewRequestWithContext(ctx, method, requestUrl.String(), nil)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"creating http request to %s: %w\", doh.url, err)\n\t}\n\n\thttpReq.Header.Set(\"Accept\", \"application/dns-message\")\n\thttpReq.Header.Set(\"User-Agent\", \"\")\n\thttpResp, err := client.Do(httpReq)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"requesting %s: %w\", doh.url, err)\n\t}\n\tdefer httpResp.Body.Close()\n\n\tbody, err := io.ReadAll(httpResp.Body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"reading %s: %w\", doh.url, err)\n\t}\n\n\tif httpResp.StatusCode != http.StatusOK {\n\t\treturn nil,\n\t\t\tfmt.Errorf(\n\t\t\t\t\"expected status %d, got %d from %s\",\n\t\t\t\thttp.StatusOK,\n\t\t\t\thttpResp.StatusCode,\n\t\t\t\tdoh.url,\n\t\t\t)\n\t}\n\n\tresp = &D.Msg{}\n\terr = resp.Unpack(body)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\n\t\t\t\"unpacking response from %s: body is %s: %w\",\n\t\t\tdoh.url,\n\t\t\tbody,\n\t\t\terr,\n\t\t)\n\t}\n\n\tif resp.Id != req.Id {\n\t\terr = D.ErrId\n\t}\n\n\treturn resp, err\n}\n\n// shouldRetry checks what error we have received and returns true if we should\n// re-create the HTTP client and retry the request.\nfunc (doh *dnsOverHTTPS) shouldRetry(err error) (ok bool) {\n\tif err == nil {\n\t\treturn false\n\t}\n\n\tvar netErr net.Error\n\tif errors.As(err, &netErr) && netErr.Timeout() {\n\t\t// If this is a timeout error, trying to forcibly re-create the HTTP\n\t\t// client instance.  This is an attempt to fix an issue with DoH client\n\t\t// stalling after a network change.\n\t\t//\n\t\t// See https://github.com/AdguardTeam/AdGuardHome/issues/3217.\n\t\treturn true\n\t}\n\n\tif isQUICRetryError(err) {\n\t\treturn true\n\t}\n\n\treturn false\n}\n\n// resetClient triggers re-creation of the *http.Client that is used by this\n// upstream.  This method accepts the error that caused resetting client as\n// depending on the error we may also reset the QUIC config.\nfunc (doh *dnsOverHTTPS) resetClient(ctx context.Context, resetErr error) (client *http.Client, err error) {\n\tdoh.clientMu.Lock()\n\tdefer doh.clientMu.Unlock()\n\n\tif errors.Is(resetErr, quic.Err0RTTRejected) {\n\t\t// Reset the TokenStore only if 0-RTT was rejected.\n\t\tdoh.resetQUICConfig()\n\t}\n\n\toldClient := doh.client\n\tif oldClient != nil {\n\t\tcloseErr := doh.closeClient(oldClient)\n\t\tif closeErr != nil {\n\t\t\tlog.Warnln(\"warning: failed to close the old http client: %v\", closeErr)\n\t\t}\n\t}\n\n\tlog.Debugln(\"re-creating the http client due to %v\", resetErr)\n\tdoh.client, err = doh.createClient(ctx)\n\n\treturn doh.client, err\n}\n\n// getQUICConfig returns the QUIC config in a thread-safe manner.  Note, that\n// this method returns a pointer, it is forbidden to change its properties.\nfunc (doh *dnsOverHTTPS) getQUICConfig() (c *quic.Config) {\n\tdoh.quicConfigGuard.Lock()\n\tdefer doh.quicConfigGuard.Unlock()\n\n\treturn doh.quicConfig\n}\n\n// resetQUICConfig Re-create the token store to make sure we're not trying to\n// use invalid for 0-RTT.\nfunc (doh *dnsOverHTTPS) resetQUICConfig() {\n\tdoh.quicConfigGuard.Lock()\n\tdefer doh.quicConfigGuard.Unlock()\n\n\tdoh.quicConfig = doh.quicConfig.Clone()\n\tdoh.quicConfig.TokenStore = newQUICTokenStore()\n}\n\n// getClient gets or lazily initializes an HTTP client (and transport) that will\n// be used for this DoH resolver.\nfunc (doh *dnsOverHTTPS) getClient(ctx context.Context) (c *http.Client, isCached bool, err error) {\n\tstartTime := time.Now()\n\n\tdoh.clientMu.Lock()\n\tdefer doh.clientMu.Unlock()\n\tif doh.client != nil {\n\t\treturn doh.client, true, nil\n\t}\n\n\t// Timeout can be exceeded while waiting for the lock. This happens quite\n\t// often on mobile devices.\n\telapsed := time.Since(startTime)\n\tif elapsed > maxElapsedTime {\n\t\treturn nil, false, fmt.Errorf(\"timeout exceeded: %s\", elapsed)\n\t}\n\n\tlog.Debugln(\"creating a new http client\")\n\tdoh.client, err = doh.createClient(ctx)\n\n\treturn doh.client, false, err\n}\n\n// createClient creates a new *http.Client instance.  The HTTP protocol version\n// will depend on whether HTTP3 is allowed and provided by this upstream.  Note,\n// that we'll attempt to establish a QUIC connection when creating the client in\n// order to check whether HTTP3 is supported.\nfunc (doh *dnsOverHTTPS) createClient(ctx context.Context) (*http.Client, error) {\n\ttransport, err := doh.createTransport(ctx)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[%s] initializing http transport: %w\", doh.url.String(), err)\n\t}\n\n\tclient := &http.Client{\n\t\tTransport: transport,\n\t\tTimeout:   DefaultTimeout,\n\t\tJar:       nil,\n\t}\n\n\tdoh.client = client\n\n\treturn doh.client, nil\n}\n\n// createTransport initializes an HTTP transport that will be used specifically\n// for this DoH resolver.  This HTTP transport ensures that the HTTP requests\n// will be sent exactly to the IP address got from the bootstrap resolver. Note,\n// that this function will first attempt to establish a QUIC connection (if\n// HTTP3 is enabled in the upstream options).  If this attempt is successful,\n// it returns an HTTP3 transport, otherwise it returns the H1/H2 transport.\nfunc (doh *dnsOverHTTPS) createTransport(ctx context.Context) (t http.RoundTripper, err error) {\n\ttransport := &http.Transport{\n\t\tDisableCompression: true,\n\t\tDialContext:        doh.dialer.DialContext,\n\t\tIdleConnTimeout:    transportDefaultIdleConnTimeout,\n\t\tMaxConnsPerHost:    dohMaxConnsPerHost,\n\t\tMaxIdleConns:       dohMaxIdleConns,\n\t}\n\n\tif doh.url.Scheme == \"http\" {\n\t\treturn transport, nil\n\t}\n\n\ttlsConfig, err := ca.GetTLSConfig(ca.Option{\n\t\tTLSConfig: &tls.Config{\n\t\t\tInsecureSkipVerify:     doh.skipCertVerify,\n\t\t\tMinVersion:             tls.VersionTLS12,\n\t\t\tSessionTicketsDisabled: false,\n\t\t},\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar nextProtos []string\n\tfor _, v := range doh.httpVersions {\n\t\tnextProtos = append(nextProtos, string(v))\n\t}\n\ttlsConfig.NextProtos = nextProtos\n\ttransport.TLSClientConfig = tlsConfig\n\n\tif slices.Contains(doh.httpVersions, C.HTTPVersion3) {\n\t\t// First, we attempt to create an HTTP3 transport.  If the probe QUIC\n\t\t// connection is established successfully, we'll be using HTTP3 for this\n\t\t// upstream.\n\t\ttransportH3, err := doh.createTransportH3(ctx, tlsConfig)\n\t\tif err == nil {\n\t\t\tlog.Debugln(\"[%s] using HTTP/3 for this upstream: QUIC was faster\", doh.url.String())\n\t\t\treturn transportH3, nil\n\t\t}\n\t}\n\n\tlog.Debugln(\"[%s] using HTTP/2 for this upstream: %v\", doh.url.String(), err)\n\n\tif !doh.supportsHTTP() {\n\t\treturn nil, errors.New(\"HTTP1/1 and HTTP2 are not supported by this upstream\")\n\t}\n\n\t// Since we have a custom DialContext, we need to use this field to\n\t// make golang http.Client attempt to use HTTP/2. Otherwise, it would\n\t// only be used when negotiated on the TLS level.\n\ttransport.ForceAttemptHTTP2 = true\n\n\t// Enable HTTP/2 pings on idle connections.\n\ttransport.HTTP2 = &http.HTTP2Config{\n\t\tSendPingTimeout: transportDefaultSendPingTimeout,\n\t}\n\n\treturn transport, nil\n}\n\n// http3Transport is a wrapper over *http3.Transport that tries to optimize\n// its behavior.  The main thing that it does is trying to force use a single\n// connection to a host instead of creating a new one all the time.  It also\n// helps mitigate race issues with quic-go.\ntype http3Transport struct {\n\tbaseTransport *http3.Transport\n\n\tclosed bool\n\tmu     sync.RWMutex\n}\n\n// type check\nvar _ http.RoundTripper = (*http3Transport)(nil)\n\n// RoundTrip implements the http.RoundTripper interface for *http3Transport.\nfunc (h *http3Transport) RoundTrip(req *http.Request) (resp *http.Response, err error) {\n\th.mu.RLock()\n\tdefer h.mu.RUnlock()\n\n\tif h.closed {\n\t\treturn nil, net.ErrClosed\n\t}\n\n\t// Try to use cached connection to the target host if it's available.\n\tresp, err = h.baseTransport.RoundTripOpt(req, http3.RoundTripOpt{OnlyCachedConn: true})\n\n\tif errors.Is(err, http3.ErrNoCachedConn) {\n\t\t// If there are no cached connection, trigger creating a new one.\n\t\tresp, err = h.baseTransport.RoundTrip(req)\n\t}\n\n\treturn resp, err\n}\n\n// type check\nvar _ io.Closer = (*http3Transport)(nil)\n\n// Close implements the io.Closer interface for *http3Transport.\nfunc (h *http3Transport) Close() (err error) {\n\th.mu.Lock()\n\tdefer h.mu.Unlock()\n\n\th.closed = true\n\n\treturn h.baseTransport.Close()\n}\n\nfunc (h *http3Transport) CloseIdleConnections() {\n\th.mu.RLock()\n\tdefer h.mu.RUnlock()\n\n\th.baseTransport.CloseIdleConnections()\n}\n\n// createTransportH3 tries to create an HTTP/3 transport for this upstream.\n// We should be able to fall back to H1/H2 in case if HTTP/3 is unavailable or\n// if it is too slow.  In order to do that, this method will run two probes\n// in parallel (one for TLS, the other one for QUIC) and if QUIC is faster it\n// will create the *http3.Transport instance.\nfunc (doh *dnsOverHTTPS) createTransportH3(\n\tctx context.Context,\n\ttlsConfig *tls.Config,\n) (roundTripper http.RoundTripper, err error) {\n\tif !doh.supportsH3() {\n\t\treturn nil, errors.New(\"HTTP3 support is not enabled\")\n\t}\n\n\taddr, err := doh.probeH3(ctx, tlsConfig)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\trt := &http3.Transport{\n\t\tDial: func(\n\t\t\tctx context.Context,\n\n\t\t\t// Ignore the address and always connect to the one that we got\n\t\t\t// from the bootstrapper.\n\t\t\t_ string,\n\t\t\ttlsCfg *tls.Config,\n\t\t\tcfg *quic.Config,\n\t\t) (c *quic.Conn, err error) {\n\t\t\treturn doh.dialQuic(ctx, addr, tlsCfg, cfg)\n\t\t},\n\t\tDisableCompression: true,\n\t\tTLSClientConfig:    tlsConfig,\n\t\tQUICConfig:         doh.getQUICConfig(),\n\t}\n\n\treturn &http3Transport{baseTransport: rt}, nil\n}\n\nfunc (doh *dnsOverHTTPS) dialQuic(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (*quic.Conn, error) {\n\tip, port, err := net.SplitHostPort(addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tportInt, err := strconv.Atoi(port)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tudpAddr := net.UDPAddr{\n\t\tIP:   net.ParseIP(ip),\n\t\tPort: portInt,\n\t}\n\tpacketConn, err := doh.dialer.ListenPacket(ctx, \"udp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\ttransport := quic.Transport{Conn: packetConn}\n\ttransport.SetCreatedConn(true) // auto close conn\n\ttransport.SetSingleUse(true)   // auto close transport\n\ttlsCfg = tlsCfg.Clone()\n\tif host, _, err := net.SplitHostPort(doh.url.Host); err == nil {\n\t\ttlsCfg.ServerName = host\n\t} else {\n\t\t// It's ok if net.SplitHostPort returns an error - it could be a hostname/IP address without a port.\n\t\ttlsCfg.ServerName = doh.url.Host\n\t}\n\tquicConn, err := transport.DialEarly(ctx, &udpAddr, tlsCfg, cfg)\n\tif err != nil {\n\t\t_ = packetConn.Close()\n\t\treturn nil, err\n\t}\n\treturn quicConn, nil\n}\n\n// probeH3 runs a test to check whether QUIC is faster than TLS for this\n// upstream.  If the test is successful it will return the address that we\n// should use to establish the QUIC connections.\nfunc (doh *dnsOverHTTPS) probeH3(\n\tctx context.Context,\n\ttlsConfig *tls.Config,\n) (addr string, err error) {\n\t// We're using bootstrapped address instead of what's passed to the function\n\t// it does not create an actual connection, but it helps us determine\n\t// what IP is actually reachable (when there are v4/v6 addresses).\n\trawConn, err := doh.dialer.DialContext(ctx, \"udp\", doh.url.Host)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"failed to dial: %w\", err)\n\t}\n\taddr = rawConn.RemoteAddr().String()\n\t// It's never actually used.\n\t_ = rawConn.Close()\n\n\t// Avoid spending time on probing if this upstream only supports HTTP/3.\n\tif doh.supportsH3() && !doh.supportsHTTP() {\n\t\treturn addr, nil\n\t}\n\n\t// Use a new *tls.Config with empty session cache for probe connections.\n\t// Surprisingly, this is really important since otherwise it invalidates\n\t// the existing cache.\n\t// TODO(ameshkov): figure out why the sessions cache invalidates here.\n\tprobeTLSCfg := tlsConfig.Clone()\n\tprobeTLSCfg.ClientSessionCache = nil\n\n\t// Do not expose probe connections to the callbacks that are passed to\n\t// the bootstrap options to avoid side-effects.\n\t// TODO(ameshkov): consider exposing, somehow mark that this is a probe.\n\tprobeTLSCfg.VerifyPeerCertificate = nil\n\tprobeTLSCfg.VerifyConnection = nil\n\n\t// Run probeQUIC and probeTLS in parallel and see which one is faster.\n\tchQuic := make(chan error, 1)\n\tchTLS := make(chan error, 1)\n\tgo doh.probeQUIC(ctx, addr, probeTLSCfg, chQuic)\n\tgo doh.probeTLS(ctx, probeTLSCfg, chTLS)\n\n\tselect {\n\tcase quicErr := <-chQuic:\n\t\tif quicErr != nil {\n\t\t\t// QUIC failed, return error since HTTP3 was not preferred.\n\t\t\treturn \"\", quicErr\n\t\t}\n\n\t\t// Return immediately, QUIC was faster.\n\t\treturn addr, quicErr\n\tcase tlsErr := <-chTLS:\n\t\tif tlsErr != nil {\n\t\t\t// Return immediately, TLS failed.\n\t\t\tlog.Debugln(\"probing TLS: %v\", tlsErr)\n\t\t\treturn addr, nil\n\t\t}\n\n\t\treturn \"\", errors.New(\"TLS was faster than QUIC, prefer it\")\n\t}\n}\n\n// probeQUIC attempts to establish a QUIC connection to the specified address.\n// We run probeQUIC and probeTLS in parallel and see which one is faster.\nfunc (doh *dnsOverHTTPS) probeQUIC(ctx context.Context, addr string, tlsConfig *tls.Config, ch chan error) {\n\tstartTime := time.Now()\n\tconn, err := doh.dialQuic(ctx, addr, tlsConfig, doh.getQUICConfig())\n\tif err != nil {\n\t\tch <- fmt.Errorf(\"opening QUIC connection to %s: %w\", doh.Address(), err)\n\t\treturn\n\t}\n\n\t// Ignore the error since there's no way we can use it for anything useful.\n\t_ = conn.CloseWithError(QUICCodeNoError, \"\")\n\n\tch <- nil\n\n\telapsed := time.Now().Sub(startTime)\n\tlog.Debugln(\"elapsed on establishing a QUIC connection: %s\", elapsed)\n}\n\n// probeTLS attempts to establish a TLS connection to the specified address. We\n// run probeQUIC and probeTLS in parallel and see which one is faster.\nfunc (doh *dnsOverHTTPS) probeTLS(ctx context.Context, tlsConfig *tls.Config, ch chan error) {\n\tstartTime := time.Now()\n\n\tconn, err := doh.tlsDial(ctx, \"tcp\", tlsConfig)\n\tif err != nil {\n\t\tch <- fmt.Errorf(\"opening TLS connection: %w\", err)\n\t\treturn\n\t}\n\n\t// Ignore the error since there's no way we can use it for anything useful.\n\t_ = conn.Close()\n\n\tch <- nil\n\n\telapsed := time.Now().Sub(startTime)\n\tlog.Debugln(\"elapsed on establishing a TLS connection: %s\", elapsed)\n}\n\n// supportsH3 returns true if HTTP/3 is supported by this upstream.\nfunc (doh *dnsOverHTTPS) supportsH3() (ok bool) {\n\tfor _, v := range doh.supportedHTTPVersions() {\n\t\tif v == C.HTTPVersion3 {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// supportsHTTP returns true if HTTP/1.1 or HTTP2 is supported by this upstream.\nfunc (doh *dnsOverHTTPS) supportsHTTP() (ok bool) {\n\tfor _, v := range doh.supportedHTTPVersions() {\n\t\tif v == C.HTTPVersion11 || v == C.HTTPVersion2 {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\n// supportedHTTPVersions returns the list of supported HTTP versions.\nfunc (doh *dnsOverHTTPS) supportedHTTPVersions() (v []C.HTTPVersion) {\n\tv = doh.httpVersions\n\tif v == nil {\n\t\tv = DefaultHTTPVersions\n\t}\n\n\treturn v\n}\n\n// isHTTP3 checks if the *http.Client is an HTTP/3 client.\nfunc isHTTP3(client *http.Client) (ok bool) {\n\t_, ok = client.Transport.(*http3Transport)\n\n\treturn ok\n}\n\n// tlsDial is basically the same as tls.DialWithDialer, but we will call our own\n// dialContext function to get connection.\nfunc (doh *dnsOverHTTPS) tlsDial(ctx context.Context, network string, config *tls.Config) (*tls.Conn, error) {\n\t// We're using bootstrapped address instead of what's passed\n\t// to the function.\n\trawConn, err := doh.dialer.DialContext(ctx, network, doh.url.Host)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// We want the timeout to cover the whole process: TCP connection and\n\t// TLS handshake dialTimeout will be used as connection deadLine.\n\tconn := tls.Client(rawConn, config)\n\n\tctx, cancel := context.WithTimeout(ctx, dialTimeout)\n\tdefer cancel()\n\n\terr = conn.HandshakeContext(ctx)\n\tif err != nil {\n\t\t_ = rawConn.Close()\n\t\treturn nil, err\n\t}\n\n\treturn conn, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/dns/doq.go",
    "content": "package dns\n\nimport (\n\t\"context\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/pool\"\n\t\"github.com/metacubex/mihomo/component/ca\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/metacubex/quic-go\"\n\t\"github.com/metacubex/tls\"\n\tD \"github.com/miekg/dns\"\n)\n\nconst NextProtoDQ = \"doq\"\nconst (\n\t// QUICCodeNoError is used when the connection or stream needs to be closed,\n\t// but there is no error to signal.\n\tQUICCodeNoError = quic.ApplicationErrorCode(0)\n\t// QUICCodeInternalError signals that the DoQ implementation encountered\n\t// an internal error and is incapable of pursuing the transaction or the\n\t// connection.\n\tQUICCodeInternalError = quic.ApplicationErrorCode(1)\n\t// QUICKeepAlivePeriod is the value that we pass to *quic.Config and that\n\t// controls the period with with keep-alive frames are being sent to the\n\t// connection. We set it to 20s as it would be in the quic-go@v0.27.1 with\n\t// KeepAlive field set to true This value is specified in\n\t// https://pkg.go.dev/github.com/metacubex/quic-go/internal/protocol#MaxKeepAliveInterval.\n\t//\n\t// TODO(ameshkov):  Consider making it configurable.\n\tQUICKeepAlivePeriod = time.Second * 20\n\tDefaultTimeout      = time.Second * 5\n)\n\n// dnsOverQUIC is a struct that implements the Upstream interface for the\n// DNS-over-QUIC protocol (spec: https://www.rfc-editor.org/rfc/rfc9250.html).\ntype dnsOverQUIC struct {\n\t// quicConfig is the QUIC configuration that is used for establishing\n\t// connections to the upstream.  This configuration includes the TokenStore\n\t// that needs to be stored for the lifetime of dnsOverQUIC since we can\n\t// re-create the connection.\n\tquicConfig      *quic.Config\n\tquicConfigGuard sync.Mutex\n\n\t// conn is the current active QUIC connection.  It can be closed and\n\t// re-opened when needed.\n\tconn   *quic.Conn\n\tconnMu sync.RWMutex\n\n\taddr           string\n\tdialer         *dnsDialer\n\tskipCertVerify bool\n}\n\n// type check\nvar _ dnsClient = (*dnsOverQUIC)(nil)\n\n// newDoQ returns the DNS-over-QUIC Upstream.\nfunc newDoQ(addr string, resolver *Resolver, params map[string]string, proxyAdapter C.ProxyAdapter, proxyName string) *dnsOverQUIC {\n\tdoq := &dnsOverQUIC{\n\t\taddr:   addr,\n\t\tdialer: newDNSDialer(resolver, proxyAdapter, proxyName),\n\t\tquicConfig: &quic.Config{\n\t\t\tKeepAlivePeriod: QUICKeepAlivePeriod,\n\t\t\tTokenStore:      newQUICTokenStore(),\n\t\t},\n\t}\n\n\tif params[\"skip-cert-verify\"] == \"true\" {\n\t\tdoq.skipCertVerify = true\n\t}\n\n\truntime.SetFinalizer(doq, (*dnsOverQUIC).Close)\n\treturn doq\n}\n\n// Address implements the Upstream interface for *dnsOverQUIC.\nfunc (doq *dnsOverQUIC) Address() string { return doq.addr }\n\nfunc (doq *dnsOverQUIC) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) {\n\t// When sending queries over a QUIC connection, the DNS Message ID MUST be\n\t// set to zero.\n\tm = m.Copy()\n\tid := m.Id\n\tm.Id = 0\n\tdefer func() {\n\t\t// Restore the original ID to not break compatibility with proxies.\n\t\tm.Id = id\n\t\tif msg != nil {\n\t\t\tmsg.Id = id\n\t\t}\n\t}()\n\n\t// Check if there was already an active conn before sending the request.\n\t// We'll only attempt to re-connect if there was one.\n\thasConnection := doq.hasConnection()\n\n\t// Make the first attempt to send the DNS query.\n\tmsg, err = doq.exchangeQUIC(ctx, m)\n\n\t// Make up to 2 attempts to re-open the QUIC connection and send the request\n\t// again.  There are several cases where this workaround is necessary to\n\t// make DoQ usable.  We need to make 2 attempts in the case when the\n\t// connection was closed (due to inactivity for example) AND the server\n\t// refuses to open a 0-RTT connection.\n\tfor i := 0; hasConnection && doq.shouldRetry(err) && i < 2; i++ {\n\t\tlog.Debugln(\"re-creating the QUIC connection and retrying due to %v\", err)\n\n\t\t// Close the active connection to make sure we'll try to re-connect.\n\t\tdoq.closeConnWithError(err)\n\n\t\t// Retry sending the request.\n\t\tmsg, err = doq.exchangeQUIC(ctx, m)\n\t}\n\n\tif err != nil {\n\t\t// If we're unable to exchange messages, make sure the connection is\n\t\t// closed and signal about an internal error.\n\t\tdoq.closeConnWithError(err)\n\t}\n\n\treturn msg, err\n}\n\n// Close implements the Upstream interface for *dnsOverQUIC.\nfunc (doq *dnsOverQUIC) Close() (err error) {\n\tdoq.connMu.Lock()\n\tdefer doq.connMu.Unlock()\n\n\truntime.SetFinalizer(doq, nil)\n\n\tif doq.conn != nil {\n\t\terr = doq.conn.CloseWithError(QUICCodeNoError, \"\")\n\t}\n\n\treturn err\n}\n\nfunc (doq *dnsOverQUIC) ResetConnection() {\n\tdoq.closeConnWithError(nil)\n}\n\n// exchangeQUIC attempts to open a QUIC connection, send the DNS message\n// through it and return the response it got from the server.\nfunc (doq *dnsOverQUIC) exchangeQUIC(ctx context.Context, msg *D.Msg) (resp *D.Msg, err error) {\n\tvar conn *quic.Conn\n\tconn, err = doq.getConnection(ctx, true)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar buf []byte\n\tbuf, err = msg.Pack()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to pack DNS message for DoQ: %w\", err)\n\t}\n\n\tvar stream *quic.Stream\n\tstream, err = doq.openStream(ctx, conn)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t_, err = stream.Write(AddPrefix(buf))\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to write to a QUIC stream: %w\", err)\n\t}\n\n\t// The client MUST send the DNS query over the selected stream, and MUST\n\t// indicate through the STREAM FIN mechanism that no further data will\n\t// be sent on that stream. Note, that stream.Close() closes the\n\t// write-direction of the stream, but does not prevent reading from it.\n\t_ = stream.Close()\n\n\treturn doq.readMsg(stream)\n}\n\n// AddPrefix adds a 2-byte prefix with the DNS message length.\nfunc AddPrefix(b []byte) (m []byte) {\n\tm = make([]byte, 2+len(b))\n\tbinary.BigEndian.PutUint16(m, uint16(len(b)))\n\tcopy(m[2:], b)\n\n\treturn m\n}\n\n// shouldRetry checks what error we received and decides whether it is required\n// to re-open the connection and retry sending the request.\nfunc (doq *dnsOverQUIC) shouldRetry(err error) (ok bool) {\n\treturn isQUICRetryError(err)\n}\n\n// getConnection opens or returns an existing *quic.Conn. useCached\n// argument controls whether we should try to use the existing cached\n// connection.  If it is false, we will forcibly create a new connection and\n// close the existing one if needed.\nfunc (doq *dnsOverQUIC) getConnection(ctx context.Context, useCached bool) (*quic.Conn, error) {\n\tvar conn *quic.Conn\n\tdoq.connMu.RLock()\n\tconn = doq.conn\n\tif conn != nil && useCached {\n\t\tdoq.connMu.RUnlock()\n\n\t\treturn conn, nil\n\t}\n\tif conn != nil {\n\t\t// we're recreating the connection, let's create a new one.\n\t\t_ = conn.CloseWithError(QUICCodeNoError, \"\")\n\t}\n\tdoq.connMu.RUnlock()\n\n\tdoq.connMu.Lock()\n\tdefer doq.connMu.Unlock()\n\n\tvar err error\n\tconn, err = doq.openConnection(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdoq.conn = conn\n\n\treturn conn, nil\n}\n\n// hasConnection returns true if there's an active QUIC connection.\nfunc (doq *dnsOverQUIC) hasConnection() (ok bool) {\n\tdoq.connMu.Lock()\n\tdefer doq.connMu.Unlock()\n\n\treturn doq.conn != nil\n}\n\n// getQUICConfig returns the QUIC config in a thread-safe manner.  Note, that\n// this method returns a pointer, it is forbidden to change its properties.\nfunc (doq *dnsOverQUIC) getQUICConfig() (c *quic.Config) {\n\tdoq.quicConfigGuard.Lock()\n\tdefer doq.quicConfigGuard.Unlock()\n\n\treturn doq.quicConfig\n}\n\n// resetQUICConfig re-creates the tokens store as we may need to use a new one\n// if we failed to connect.\nfunc (doq *dnsOverQUIC) resetQUICConfig() {\n\tdoq.quicConfigGuard.Lock()\n\tdefer doq.quicConfigGuard.Unlock()\n\n\tdoq.quicConfig = doq.quicConfig.Clone()\n\tdoq.quicConfig.TokenStore = newQUICTokenStore()\n}\n\n// openStream opens a new QUIC stream for the specified connection.\nfunc (doq *dnsOverQUIC) openStream(ctx context.Context, conn *quic.Conn) (*quic.Stream, error) {\n\tctx, cancel := context.WithCancel(ctx)\n\tdefer cancel()\n\n\tstream, err := conn.OpenStreamSync(ctx)\n\tif err == nil {\n\t\treturn stream, nil\n\t}\n\n\t// We can get here if the old QUIC connection is not valid anymore.  We\n\t// should try to re-create the connection again in this case.\n\tnewConn, err := doq.getConnection(ctx, false)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t// Open a new stream.\n\treturn newConn.OpenStreamSync(ctx)\n}\n\n// openConnection opens a new QUIC connection.\nfunc (doq *dnsOverQUIC) openConnection(ctx context.Context) (quicConn *quic.Conn, err error) {\n\t// we're using bootstrapped address instead of what's passed to the function\n\t// it does not create an actual connection, but it helps us determine\n\t// what IP is actually reachable (when there're v4/v6 addresses).\n\trawConn, err := doq.dialer.DialContext(ctx, \"udp\", doq.addr)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to open a QUIC connection: %w\", err)\n\t}\n\taddr := rawConn.RemoteAddr().String()\n\t// It's never actually used\n\t_ = rawConn.Close()\n\n\tip, port, err := net.SplitHostPort(addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tp, err := strconv.Atoi(port)\n\tudpAddr := net.UDPAddr{IP: net.ParseIP(ip), Port: p}\n\tpacketConn, err := doq.dialer.ListenPacket(ctx, \"udp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\thost, _, err := net.SplitHostPort(doq.addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttlsConfig, err := ca.GetTLSConfig(ca.Option{\n\t\tTLSConfig: &tls.Config{\n\t\t\tServerName:         host,\n\t\t\tInsecureSkipVerify: doq.skipCertVerify,\n\t\t\tNextProtos: []string{\n\t\t\t\tNextProtoDQ,\n\t\t\t},\n\t\t\tSessionTicketsDisabled: false,\n\t\t},\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttransport := quic.Transport{Conn: packetConn}\n\ttransport.SetCreatedConn(true) // auto close conn\n\ttransport.SetSingleUse(true)   // auto close transport\n\tquicConn, err = transport.Dial(ctx, &udpAddr, tlsConfig, doq.getQUICConfig())\n\tif err != nil {\n\t\t_ = packetConn.Close()\n\t\treturn nil, fmt.Errorf(\"opening quic connection to %s: %w\", doq.addr, err)\n\t}\n\n\treturn quicConn, nil\n}\n\n// closeConnWithError closes the active connection with error to make sure that\n// new queries were processed in another connection.  We can do that in the case\n// of a fatal error.\nfunc (doq *dnsOverQUIC) closeConnWithError(err error) {\n\tdoq.connMu.Lock()\n\tdefer doq.connMu.Unlock()\n\n\tif doq.conn == nil {\n\t\t// Do nothing, there's no active conn anyways.\n\t\treturn\n\t}\n\n\tcode := QUICCodeNoError\n\tif err != nil {\n\t\tcode = QUICCodeInternalError\n\t}\n\n\tif errors.Is(err, quic.Err0RTTRejected) {\n\t\t// Reset the TokenStore only if 0-RTT was rejected.\n\t\tdoq.resetQUICConfig()\n\t}\n\n\terr = doq.conn.CloseWithError(code, \"\")\n\tif err != nil {\n\t\tlog.Errorln(\"failed to close the conn: %v\", err)\n\t}\n\tdoq.conn = nil\n}\n\n// readMsg reads the incoming DNS message from the QUIC stream.\nfunc (doq *dnsOverQUIC) readMsg(stream *quic.Stream) (m *D.Msg, err error) {\n\trespBuf := pool.Get(MaxMsgSize)\n\tdefer pool.Put(respBuf)\n\n\tn, err := stream.Read(respBuf)\n\tif err != nil && n == 0 {\n\t\treturn nil, fmt.Errorf(\"reading response from %s: %w\", doq.Address(), err)\n\t}\n\n\t// All DNS messages (queries and responses) sent over DoQ connections MUST\n\t// be encoded as a 2-octet length field followed by the message content as\n\t// specified in [RFC1035].\n\t// IMPORTANT: Note, that we ignore this prefix here as this implementation\n\t// does not support receiving multiple messages over a single connection.\n\tm = new(D.Msg)\n\terr = m.Unpack(respBuf[2:])\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"unpacking response from %s: %w\", doq.Address(), err)\n\t}\n\n\treturn m, nil\n}\n\n// newQUICTokenStore creates a new quic.TokenStore that is necessary to have\n// in order to benefit from 0-RTT.\nfunc newQUICTokenStore() (s quic.TokenStore) {\n\t// You can read more on address validation here:\n\t// https://datatracker.ietf.org/doc/html/rfc9000#section-8.1\n\t// Setting maxOrigins to 1 and tokensPerOrigin to 10 assuming that this is\n\t// more than enough for the way we use it (one connection per upstream).\n\treturn quic.NewLRUTokenStore(1, 10)\n}\n\n// isQUICRetryError checks the error and determines whether it may signal that\n// we should re-create the QUIC connection.  This requirement is caused by\n// quic-go issues, see the comments inside this function.\n// TODO(ameshkov): re-test when updating quic-go.\nfunc isQUICRetryError(err error) (ok bool) {\n\tvar qAppErr *quic.ApplicationError\n\tif errors.As(err, &qAppErr) && qAppErr.ErrorCode == 0 {\n\t\t// This error is often returned when the server has been restarted,\n\t\t// and we try to use the same connection on the client-side. It seems,\n\t\t// that the old connections aren't closed immediately on the server-side\n\t\t// and that's why one can run into this.\n\t\t// In addition to that, quic-go HTTP3 client implementation does not\n\t\t// clean up dead connections (this one is specific to DoH3 upstream):\n\t\t// https://github.com/metacubex/quic-go/issues/765\n\t\treturn true\n\t}\n\n\tvar qIdleErr *quic.IdleTimeoutError\n\tif errors.As(err, &qIdleErr) {\n\t\t// This error means that the connection was closed due to being idle.\n\t\t// In this case we should forcibly re-create the QUIC connection.\n\t\t// Reproducing is rather simple, stop the server and wait for 30 seconds\n\t\t// then try to send another request via the same upstream.\n\t\treturn true\n\t}\n\n\tvar resetErr *quic.StatelessResetError\n\tif errors.As(err, &resetErr) {\n\t\t// A stateless reset is sent when a server receives a QUIC packet that\n\t\t// it doesn't know how to decrypt.  For instance, it may happen when\n\t\t// the server was recently rebooted.  We should reconnect and try again\n\t\t// in this case.\n\t\treturn true\n\t}\n\n\tvar qTransportError *quic.TransportError\n\tif errors.As(err, &qTransportError) && qTransportError.ErrorCode == quic.NoError {\n\t\t// A transport error with the NO_ERROR error code could be sent by the\n\t\t// server when it considers that it's time to close the connection.\n\t\t// For example, Google DNS eventually closes an active connection with\n\t\t// the NO_ERROR code and \"Connection max age expired\" message:\n\t\t// https://github.com/AdguardTeam/dnsproxy/issues/283\n\t\treturn true\n\t}\n\n\tif errors.Is(err, quic.Err0RTTRejected) {\n\t\t// This error happens when we try to establish a 0-RTT connection with\n\t\t// a token the server is no more aware of.  This can be reproduced by\n\t\t// restarting the QUIC server (it will clear its tokens cache).  The\n\t\t// next connection attempt will return this error until the client's\n\t\t// tokens cache is purged.\n\t\treturn true\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "core/Clash.Meta/dns/dot.go",
    "content": "package dns\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"runtime\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/deque\"\n\t\"github.com/metacubex/mihomo/component/ca\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\n\t\"github.com/metacubex/tls\"\n\tD \"github.com/miekg/dns\"\n)\n\nconst maxOldDotConns = 8\n\ntype dnsOverTLS struct {\n\tport           string\n\thost           string\n\tdialer         *dnsDialer\n\tskipCertVerify bool\n\tdisableReuse   bool\n\n\taccess      sync.Mutex\n\tconnections deque.Deque[net.Conn] // LIFO\n}\n\nvar _ dnsClient = (*dnsOverTLS)(nil)\n\n// Address implements dnsClient\nfunc (t *dnsOverTLS) Address() string {\n\treturn fmt.Sprintf(\"tls://%s\", net.JoinHostPort(t.host, t.port))\n}\n\nfunc (t *dnsOverTLS) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) {\n\t// miekg/dns ExchangeContext doesn't respond to context cancel.\n\t// this is a workaround\n\ttype result struct {\n\t\tmsg *D.Msg\n\t\terr error\n\t}\n\tch := make(chan result, 1)\n\n\tgo func() {\n\t\tvar msg *D.Msg\n\t\tvar err error\n\t\tdefer func() { ch <- result{msg, err} }()\n\t\tfor { // retry loop; only retry when reusing old conn\n\t\t\terr = ctx.Err() // check context first\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tvar conn net.Conn\n\t\t\tisOldConn := true\n\n\t\t\tif !t.disableReuse {\n\t\t\t\tt.access.Lock()\n\t\t\t\tif t.connections.Len() > 0 {\n\t\t\t\t\tconn = t.connections.PopBack()\n\t\t\t\t}\n\t\t\t\tt.access.Unlock()\n\t\t\t}\n\n\t\t\tif conn == nil {\n\t\t\t\tconn, err = t.dialContext(ctx)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tisOldConn = false\n\t\t\t}\n\n\t\t\tdClient := &D.Client{\n\t\t\t\tUDPSize: 4096,\n\t\t\t\tTimeout: 5 * time.Second,\n\t\t\t}\n\t\t\tdConn := &D.Conn{\n\t\t\t\tConn:    conn,\n\t\t\t\tUDPSize: dClient.UDPSize,\n\t\t\t}\n\n\t\t\tmsg, _, err = dClient.ExchangeWithConn(m, dConn)\n\t\t\tif err != nil {\n\t\t\t\t_ = conn.Close()\n\t\t\t\tconn = nil\n\t\t\t\tif isOldConn { // retry\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif !t.disableReuse {\n\t\t\t\tt.access.Lock()\n\t\t\t\tif t.connections.Len() >= maxOldDotConns {\n\t\t\t\t\toldConn := t.connections.PopFront()\n\t\t\t\t\tgo oldConn.Close() // close in a new goroutine, not blocking the current task\n\t\t\t\t}\n\t\t\t\tt.connections.PushBack(conn)\n\t\t\t\tt.access.Unlock()\n\t\t\t} else {\n\t\t\t\t_ = conn.Close()\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}()\n\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn nil, ctx.Err()\n\tcase ret := <-ch:\n\t\treturn ret.msg, ret.err\n\t}\n}\n\nfunc (t *dnsOverTLS) dialContext(ctx context.Context) (net.Conn, error) {\n\tconn, err := t.dialer.DialContext(ctx, \"tcp\", net.JoinHostPort(t.host, t.port))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttlsConfig, err := ca.GetTLSConfig(ca.Option{\n\t\tTLSConfig: &tls.Config{\n\t\t\tServerName:         t.host,\n\t\t\tInsecureSkipVerify: t.skipCertVerify,\n\t\t},\n\t})\n\tif err != nil {\n\t\t_ = conn.Close()\n\t\treturn nil, err\n\t}\n\ttlsConn := tls.Client(conn, tlsConfig)\n\tif err = tlsConn.HandshakeContext(ctx); err != nil {\n\t\t_ = conn.Close()\n\t\treturn nil, err\n\t}\n\tconn = tlsConn\n\n\treturn conn, nil\n}\n\nfunc (t *dnsOverTLS) ResetConnection() {\n\tif !t.disableReuse {\n\t\tt.access.Lock()\n\t\tfor t.connections.Len() > 0 {\n\t\t\toldConn := t.connections.PopFront()\n\t\t\tgo oldConn.Close() // close in a new goroutine, not blocking the current task\n\t\t}\n\t\tt.access.Unlock()\n\t}\n}\n\nfunc (t *dnsOverTLS) Close() error {\n\truntime.SetFinalizer(t, nil)\n\tt.ResetConnection()\n\treturn nil\n}\n\nfunc newDoTClient(addr string, resolver *Resolver, params map[string]string, proxyAdapter C.ProxyAdapter, proxyName string) *dnsOverTLS {\n\thost, port, _ := net.SplitHostPort(addr)\n\tc := &dnsOverTLS{\n\t\tport:   port,\n\t\thost:   host,\n\t\tdialer: newDNSDialer(resolver, proxyAdapter, proxyName),\n\t}\n\tc.connections.SetBaseCap(maxOldDotConns)\n\tif params[\"skip-cert-verify\"] == \"true\" {\n\t\tc.skipCertVerify = true\n\t}\n\tif params[\"disable-reuse\"] == \"true\" {\n\t\tc.disableReuse = true\n\t}\n\truntime.SetFinalizer(c, (*dnsOverTLS).Close)\n\treturn c\n}\n"
  },
  {
    "path": "core/Clash.Meta/dns/edns0_subnet.go",
    "content": "package dns\n\nimport (\n\t\"net/netip\"\n\n\t\"github.com/miekg/dns\"\n)\n\nfunc setEdns0Subnet(message *dns.Msg, clientSubnet netip.Prefix, override bool) bool {\n\tvar (\n\t\toptRecord    *dns.OPT\n\t\tsubnetOption *dns.EDNS0_SUBNET\n\t)\nfindExists:\n\tfor _, record := range message.Extra {\n\t\tvar isOPTRecord bool\n\t\tif optRecord, isOPTRecord = record.(*dns.OPT); isOPTRecord {\n\t\t\tfor _, option := range optRecord.Option {\n\t\t\t\tvar isEDNS0Subnet bool\n\t\t\t\tif subnetOption, isEDNS0Subnet = option.(*dns.EDNS0_SUBNET); isEDNS0Subnet {\n\t\t\t\t\tif !override {\n\t\t\t\t\t\treturn false\n\t\t\t\t\t}\n\t\t\t\t\tbreak findExists\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif optRecord == nil {\n\t\toptRecord = &dns.OPT{\n\t\t\tHdr: dns.RR_Header{\n\t\t\t\tName:   \".\",\n\t\t\t\tRrtype: dns.TypeOPT,\n\t\t\t},\n\t\t}\n\t\tmessage.Extra = append(message.Extra, optRecord)\n\t}\n\tif subnetOption == nil {\n\t\tsubnetOption = new(dns.EDNS0_SUBNET)\n\t\toptRecord.Option = append(optRecord.Option, subnetOption)\n\t}\n\tsubnetOption.Code = dns.EDNS0SUBNET\n\tif clientSubnet.Addr().Is4() {\n\t\tsubnetOption.Family = 1\n\t} else {\n\t\tsubnetOption.Family = 2\n\t}\n\tsubnetOption.SourceNetmask = uint8(clientSubnet.Bits())\n\tsubnetOption.Address = clientSubnet.Addr().AsSlice()\n\treturn true\n}\n"
  },
  {
    "path": "core/Clash.Meta/dns/enhancer.go",
    "content": "package dns\n\nimport (\n\t\"errors\"\n\t\"net/netip\"\n\n\t\"github.com/metacubex/mihomo/common/lru\"\n\t\"github.com/metacubex/mihomo/component/fakeip\"\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\ntype ResolverEnhancer struct {\n\tmode          C.DNSMode\n\tfakeIPPool    *fakeip.Pool\n\tfakeIPPool6   *fakeip.Pool\n\tfakeIPSkipper *fakeip.Skipper\n\tfakeIPTTL     int\n\tmapping       *lru.LruCache[netip.Addr, string]\n\tuseHosts      bool\n}\n\nfunc (h *ResolverEnhancer) FakeIPEnabled() bool {\n\treturn h.mode == C.DNSFakeIP\n}\n\nfunc (h *ResolverEnhancer) MappingEnabled() bool {\n\treturn h.mode == C.DNSFakeIP || h.mode == C.DNSMapping\n}\n\nfunc (h *ResolverEnhancer) IsExistFakeIP(ip netip.Addr) bool {\n\tif !h.FakeIPEnabled() {\n\t\treturn false\n\t}\n\n\tif pool := h.fakeIPPool; pool != nil {\n\t\tif pool.Exist(ip) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\tif pool6 := h.fakeIPPool6; pool6 != nil {\n\t\tif pool6.Exist(ip) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc (h *ResolverEnhancer) IsFakeIP(ip netip.Addr) bool {\n\tif !h.FakeIPEnabled() {\n\t\treturn false\n\t}\n\n\tif pool := h.fakeIPPool; pool != nil {\n\t\tif pool.IPNet().Contains(ip) && ip != pool.Gateway() && ip != pool.Broadcast() {\n\t\t\treturn true\n\t\t}\n\t}\n\n\tif pool6 := h.fakeIPPool6; pool6 != nil {\n\t\tif pool6.IPNet().Contains(ip) && ip != pool6.Gateway() && ip != pool6.Broadcast() {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc (h *ResolverEnhancer) IsFakeBroadcastIP(ip netip.Addr) bool {\n\tif !h.FakeIPEnabled() {\n\t\treturn false\n\t}\n\n\tif pool := h.fakeIPPool; pool != nil {\n\t\tif pool.Broadcast() == ip {\n\t\t\treturn true\n\t\t}\n\t}\n\n\tif pool6 := h.fakeIPPool6; pool6 != nil {\n\t\tif pool6.Broadcast() == ip {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc (h *ResolverEnhancer) FindHostByIP(ip netip.Addr) (string, bool) {\n\tif pool := h.fakeIPPool; pool != nil {\n\t\tif host, existed := pool.LookBack(ip); existed {\n\t\t\treturn host, true\n\t\t}\n\t}\n\n\tif pool6 := h.fakeIPPool6; pool6 != nil {\n\t\tif host, existed := pool6.LookBack(ip); existed {\n\t\t\treturn host, true\n\t\t}\n\t}\n\n\tif mapping := h.mapping; mapping != nil {\n\t\tif host, existed := h.mapping.Get(ip); existed {\n\t\t\treturn host, true\n\t\t}\n\t}\n\n\treturn \"\", false\n}\n\nfunc (h *ResolverEnhancer) InsertHostByIP(ip netip.Addr, host string) {\n\tif mapping := h.mapping; mapping != nil {\n\t\th.mapping.Set(ip, host)\n\t}\n}\n\nfunc (h *ResolverEnhancer) FlushFakeIP() error {\n\tvar errs []error\n\tif pool := h.fakeIPPool; pool != nil {\n\t\tif err := pool.FlushFakeIP(); err != nil {\n\t\t\terrs = append(errs, err)\n\t\t}\n\t}\n\tif pool6 := h.fakeIPPool6; pool6 != nil {\n\t\tif err := pool6.FlushFakeIP(); err != nil {\n\t\t\terrs = append(errs, err)\n\t\t}\n\t}\n\tif len(errs) > 0 {\n\t\treturn errors.Join(errs...)\n\t}\n\treturn nil\n}\n\nfunc (h *ResolverEnhancer) PatchFrom(o *ResolverEnhancer) {\n\tif h.mapping != nil && o.mapping != nil {\n\t\to.mapping.CloneTo(h.mapping)\n\t}\n\n\tif h.fakeIPPool != nil && o.fakeIPPool != nil {\n\t\th.fakeIPPool.CloneFrom(o.fakeIPPool)\n\t}\n\n\tif h.fakeIPPool6 != nil && o.fakeIPPool6 != nil {\n\t\th.fakeIPPool6.CloneFrom(o.fakeIPPool6)\n\t}\n}\n\nfunc (h *ResolverEnhancer) StoreFakePoolState() {\n\tif h.fakeIPPool != nil {\n\t\th.fakeIPPool.StoreState()\n\t}\n\n\tif h.fakeIPPool6 != nil {\n\t\th.fakeIPPool6.StoreState()\n\t}\n}\n\ntype EnhancerConfig struct {\n\tIPv6          bool\n\tEnhancedMode  C.DNSMode\n\tFakeIPPool    *fakeip.Pool\n\tFakeIPPool6   *fakeip.Pool\n\tFakeIPSkipper *fakeip.Skipper\n\tFakeIPTTL     int\n\tUseHosts      bool\n}\n\nfunc NewEnhancer(cfg EnhancerConfig) *ResolverEnhancer {\n\te := &ResolverEnhancer{\n\t\tmode:     cfg.EnhancedMode,\n\t\tuseHosts: cfg.UseHosts,\n\t}\n\n\tif cfg.EnhancedMode != C.DNSNormal {\n\t\te.fakeIPPool = cfg.FakeIPPool\n\t\tif cfg.IPv6 {\n\t\t\te.fakeIPPool6 = cfg.FakeIPPool6\n\t\t}\n\t\te.fakeIPSkipper = cfg.FakeIPSkipper\n\t\te.fakeIPTTL = cfg.FakeIPTTL\n\t\tif e.fakeIPTTL < 1 {\n\t\t\te.fakeIPTTL = 1\n\t\t}\n\t\te.mapping = lru.New(lru.WithSize[netip.Addr, string](4096))\n\t}\n\n\treturn e\n}\n"
  },
  {
    "path": "core/Clash.Meta/dns/middleware.go",
    "content": "package dns\n\nimport (\n\t\"net/netip\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/lru\"\n\t\"github.com/metacubex/mihomo/component/fakeip\"\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\ticontext \"github.com/metacubex/mihomo/context\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\tD \"github.com/miekg/dns\"\n)\n\ntype (\n\thandler    func(ctx *icontext.DNSContext, r *D.Msg) (*D.Msg, error)\n\tmiddleware func(next handler) handler\n)\n\nfunc withHosts(mapping *lru.LruCache[netip.Addr, string]) middleware {\n\treturn func(next handler) handler {\n\t\treturn func(ctx *icontext.DNSContext, r *D.Msg) (*D.Msg, error) {\n\t\t\tq := r.Question[0]\n\n\t\t\tif !isIPRequest(q) {\n\t\t\t\treturn next(ctx, r)\n\t\t\t}\n\n\t\t\thost := strings.TrimRight(q.Name, \".\")\n\t\t\thandleCName := func(resp *D.Msg, domain string) {\n\t\t\t\trr := &D.CNAME{}\n\t\t\t\trr.Hdr = D.RR_Header{Name: q.Name, Rrtype: D.TypeCNAME, Class: D.ClassINET, Ttl: 10}\n\t\t\t\trr.Target = domain + \".\"\n\t\t\t\tresp.Answer = append([]D.RR{rr}, resp.Answer...)\n\t\t\t}\n\t\t\trecord, ok := resolver.DefaultHosts.Search(host, q.Qtype != D.TypeA && q.Qtype != D.TypeAAAA)\n\t\t\tif !ok {\n\t\t\t\tif record != nil && record.IsDomain {\n\t\t\t\t\t// replace request domain\n\t\t\t\t\tnewR := r.Copy()\n\t\t\t\t\tnewR.Question[0].Name = record.Domain + \".\"\n\t\t\t\t\tresp, err := next(ctx, newR)\n\t\t\t\t\tif err == nil {\n\t\t\t\t\t\tresp.Id = r.Id\n\t\t\t\t\t\tresp.Question = r.Question\n\t\t\t\t\t\thandleCName(resp, record.Domain)\n\t\t\t\t\t}\n\t\t\t\t\treturn resp, err\n\t\t\t\t}\n\t\t\t\treturn next(ctx, r)\n\t\t\t}\n\n\t\t\tmsg := r.Copy()\n\t\t\thandleIPs := func() {\n\t\t\t\tfor _, ipAddr := range record.IPs {\n\t\t\t\t\tif ipAddr.Is4() && q.Qtype == D.TypeA {\n\t\t\t\t\t\trr := &D.A{}\n\t\t\t\t\t\trr.Hdr = D.RR_Header{Name: q.Name, Rrtype: D.TypeA, Class: D.ClassINET, Ttl: 10}\n\t\t\t\t\t\trr.A = ipAddr.AsSlice()\n\t\t\t\t\t\tmsg.Answer = append(msg.Answer, rr)\n\t\t\t\t\t\tif mapping != nil {\n\t\t\t\t\t\t\tmapping.SetWithExpire(ipAddr, host, time.Now().Add(time.Second*10))\n\t\t\t\t\t\t}\n\t\t\t\t\t} else if ipAddr.Is6() && q.Qtype == D.TypeAAAA {\n\t\t\t\t\t\trr := &D.AAAA{}\n\t\t\t\t\t\trr.Hdr = D.RR_Header{Name: q.Name, Rrtype: D.TypeAAAA, Class: D.ClassINET, Ttl: 10}\n\t\t\t\t\t\trr.AAAA = ipAddr.AsSlice()\n\t\t\t\t\t\tmsg.Answer = append(msg.Answer, rr)\n\t\t\t\t\t\tif mapping != nil {\n\t\t\t\t\t\t\tmapping.SetWithExpire(ipAddr, host, time.Now().Add(time.Second*10))\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tswitch q.Qtype {\n\t\t\tcase D.TypeA:\n\t\t\t\thandleIPs()\n\t\t\tcase D.TypeAAAA:\n\t\t\t\thandleIPs()\n\t\t\tcase D.TypeCNAME:\n\t\t\t\thandleCName(r, record.Domain)\n\t\t\tdefault:\n\t\t\t\treturn next(ctx, r)\n\t\t\t}\n\n\t\t\tctx.SetType(icontext.DNSTypeHost)\n\t\t\tmsg.SetRcode(r, D.RcodeSuccess)\n\t\t\tmsg.Authoritative = true\n\t\t\tmsg.RecursionAvailable = true\n\t\t\treturn msg, nil\n\t\t}\n\t}\n}\n\nfunc withMapping(mapping *lru.LruCache[netip.Addr, string]) middleware {\n\treturn func(next handler) handler {\n\t\treturn func(ctx *icontext.DNSContext, r *D.Msg) (*D.Msg, error) {\n\t\t\tq := r.Question[0]\n\n\t\t\tif !isIPRequest(q) {\n\t\t\t\treturn next(ctx, r)\n\t\t\t}\n\n\t\t\tmsg, err := next(ctx, r)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\thost := strings.TrimRight(q.Name, \".\")\n\n\t\t\tfor _, ans := range msg.Answer {\n\t\t\t\tvar ip netip.Addr\n\t\t\t\tvar ttl uint32\n\n\t\t\t\tswitch a := ans.(type) {\n\t\t\t\tcase *D.A:\n\t\t\t\t\tip, _ = netip.AddrFromSlice(a.A)\n\t\t\t\t\tttl = a.Hdr.Ttl\n\t\t\t\tcase *D.AAAA:\n\t\t\t\t\tip, _ = netip.AddrFromSlice(a.AAAA)\n\t\t\t\t\tttl = a.Hdr.Ttl\n\t\t\t\tdefault:\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif !ip.IsValid() {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif !ip.IsGlobalUnicast() {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tip = ip.Unmap()\n\n\t\t\t\tif ttl < 1 {\n\t\t\t\t\tttl = 1\n\t\t\t\t}\n\n\t\t\t\tmapping.SetWithExpire(ip, host, time.Now().Add(time.Second*time.Duration(ttl)))\n\t\t\t}\n\n\t\t\treturn msg, nil\n\t\t}\n\t}\n}\n\nfunc withFakeIP(skipper *fakeip.Skipper, fakePool *fakeip.Pool, fakePool6 *fakeip.Pool, fakeIPTTL int) middleware {\n\treturn func(next handler) handler {\n\t\treturn func(ctx *icontext.DNSContext, r *D.Msg) (*D.Msg, error) {\n\t\t\tq := r.Question[0]\n\n\t\t\thost := strings.TrimRight(q.Name, \".\")\n\t\t\tif skipper.ShouldSkipped(host) {\n\t\t\t\treturn next(ctx, r)\n\t\t\t}\n\n\t\t\tvar rr D.RR\n\t\t\tswitch q.Qtype {\n\t\t\tcase D.TypeA:\n\t\t\t\tif fakePool == nil {\n\t\t\t\t\treturn handleMsgWithEmptyAnswer(r), nil\n\t\t\t\t}\n\t\t\t\tip := fakePool.Lookup(host)\n\t\t\t\trr = &D.A{\n\t\t\t\t\tHdr: D.RR_Header{Name: q.Name, Rrtype: D.TypeA, Class: D.ClassINET, Ttl: dnsDefaultTTL},\n\t\t\t\t\tA:   ip.AsSlice(),\n\t\t\t\t}\n\t\t\tcase D.TypeAAAA:\n\t\t\t\tif fakePool6 == nil {\n\t\t\t\t\treturn handleMsgWithEmptyAnswer(r), nil\n\t\t\t\t}\n\t\t\t\tip := fakePool6.Lookup(host)\n\t\t\t\trr = &D.AAAA{\n\t\t\t\t\tHdr:  D.RR_Header{Name: q.Name, Rrtype: D.TypeAAAA, Class: D.ClassINET, Ttl: dnsDefaultTTL},\n\t\t\t\t\tAAAA: ip.AsSlice(),\n\t\t\t\t}\n\t\t\tcase D.TypeSVCB, D.TypeHTTPS:\n\t\t\t\treturn handleMsgWithEmptyAnswer(r), nil\n\t\t\tdefault:\n\t\t\t\treturn next(ctx, r)\n\t\t\t}\n\n\t\t\tmsg := r.Copy()\n\t\t\tmsg.Answer = []D.RR{rr}\n\n\t\t\tctx.SetType(icontext.DNSTypeFakeIP)\n\t\t\tsetMsgTTL(msg, uint32(fakeIPTTL))\n\t\t\tmsg.SetRcode(r, D.RcodeSuccess)\n\t\t\tmsg.Authoritative = true\n\t\t\tmsg.RecursionAvailable = true\n\n\t\t\treturn msg, nil\n\t\t}\n\t}\n}\n\nfunc withResolver(resolver *Resolver) handler {\n\treturn func(ctx *icontext.DNSContext, r *D.Msg) (*D.Msg, error) {\n\t\tctx.SetType(icontext.DNSTypeRaw)\n\n\t\tq := r.Question[0]\n\n\t\t// return a empty AAAA msg when ipv6 disabled\n\t\tif !resolver.ipv6 && q.Qtype == D.TypeAAAA {\n\t\t\treturn handleMsgWithEmptyAnswer(r), nil\n\t\t}\n\n\t\tmsg, err := resolver.ExchangeContext(ctx, r)\n\t\tif err != nil {\n\t\t\tlog.Debugln(\"[DNS Server] Exchange %s failed: %v\", q.String(), err)\n\t\t\treturn msg, err\n\t\t}\n\t\tmsg.SetRcode(r, msg.Rcode)\n\t\tmsg.Authoritative = true\n\n\t\treturn msg, nil\n\t}\n}\n\nfunc compose(middlewares []middleware, endpoint handler) handler {\n\tlength := len(middlewares)\n\th := endpoint\n\tfor i := length - 1; i >= 0; i-- {\n\t\tmiddleware := middlewares[i]\n\t\th = middleware(h)\n\t}\n\n\treturn h\n}\n\nfunc newHandler(resolver *Resolver, mapper *ResolverEnhancer) handler {\n\tvar middlewares []middleware\n\n\tif mapper.useHosts {\n\t\tmiddlewares = append(middlewares, withHosts(mapper.mapping))\n\t}\n\n\tif mapper.mode == C.DNSFakeIP {\n\t\tmiddlewares = append(middlewares, withFakeIP(mapper.fakeIPSkipper, mapper.fakeIPPool, mapper.fakeIPPool6, mapper.fakeIPTTL))\n\t}\n\n\tif mapper.mode != C.DNSNormal {\n\t\tmiddlewares = append(middlewares, withMapping(mapper.mapping))\n\t}\n\n\treturn compose(middlewares, withResolver(resolver))\n}\n"
  },
  {
    "path": "core/Clash.Meta/dns/patch_android.go",
    "content": "//go:build android\n\npackage dns\n\nimport (\n\t\"github.com/metacubex/mihomo/component/resolver\"\n)\n\nvar systemResolver []dnsClient\n\nfunc FlushCacheWithDefaultResolver() {\n\tresolver.ClearCache()\n\tresolver.ResetConnection()\n}\n\nfunc UpdateSystemDNS(addr []string) {\n\tif len(addr) == 0 {\n\t\tsystemResolver = nil\n\t}\n\n\tns := make([]NameServer, 0, len(addr))\n\tfor _, d := range addr {\n\t\tns = append(ns, NameServer{Addr: d})\n\t}\n\n\tsystemResolver = transform(ns, nil)\n}\n\nfunc (c *systemClient) getDnsClients() ([]dnsClient, error) {\n\treturn systemResolver, nil\n}\n\nfunc (c *systemClient) ResetConnection() {\n\tfor _, r := range systemResolver {\n\t\tr.ResetConnection()\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/dns/policy.go",
    "content": "package dns\n\nimport (\n\t\"github.com/metacubex/mihomo/component/trie\"\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\ntype dnsPolicy interface {\n\tMatch(domain string) []dnsClient\n}\n\ntype domainTriePolicy struct {\n\t*trie.DomainTrie[[]dnsClient]\n}\n\nfunc (p domainTriePolicy) Match(domain string) []dnsClient {\n\trecord := p.DomainTrie.Search(domain)\n\tif record != nil {\n\t\treturn record.Data()\n\t}\n\treturn nil\n}\n\ntype domainMatcherPolicy struct {\n\tmatcher    C.DomainMatcher\n\tdnsClients []dnsClient\n}\n\nfunc (p domainMatcherPolicy) Match(domain string) []dnsClient {\n\tif p.matcher.MatchDomain(domain) {\n\t\treturn p.dnsClients\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/dns/rcode.go",
    "content": "package dns\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\tD \"github.com/miekg/dns\"\n)\n\nfunc newRCodeClient(addr string) rcodeClient {\n\tvar rcode int\n\tswitch addr {\n\tcase \"success\":\n\t\trcode = D.RcodeSuccess\n\tcase \"format_error\":\n\t\trcode = D.RcodeFormatError\n\tcase \"server_failure\":\n\t\trcode = D.RcodeServerFailure\n\tcase \"name_error\":\n\t\trcode = D.RcodeNameError\n\tcase \"not_implemented\":\n\t\trcode = D.RcodeNotImplemented\n\tcase \"refused\":\n\t\trcode = D.RcodeRefused\n\tdefault:\n\t\tpanic(fmt.Errorf(\"unsupported RCode type: %s\", addr))\n\t}\n\n\treturn rcodeClient{\n\t\trcode: rcode,\n\t\taddr:  \"rcode://\" + addr,\n\t}\n}\n\ntype rcodeClient struct {\n\trcode int\n\taddr  string\n}\n\nvar _ dnsClient = rcodeClient{}\n\nfunc (r rcodeClient) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) {\n\tm.Response = true\n\tm.Rcode = r.rcode\n\treturn m, nil\n}\n\nfunc (r rcodeClient) Address() string {\n\treturn r.addr\n}\n\nfunc (r rcodeClient) ResetConnection() {}\n"
  },
  {
    "path": "core/Clash.Meta/dns/resolver.go",
    "content": "package dns\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net/netip\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/arc\"\n\t\"github.com/metacubex/mihomo/common/lru\"\n\t\"github.com/metacubex/mihomo/common/singleflight\"\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\t\"github.com/metacubex/mihomo/component/trie\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\tD \"github.com/miekg/dns\"\n\t\"github.com/samber/lo\"\n\t\"golang.org/x/exp/maps\"\n)\n\ntype dnsClient interface {\n\tExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error)\n\tAddress() string\n\tResetConnection()\n}\n\ntype dnsCache interface {\n\tGetWithExpire(key string) (*D.Msg, time.Time, bool)\n\tSetWithExpire(key string, value *D.Msg, expire time.Time)\n\tClear()\n}\n\ntype result struct {\n\tMsg   *D.Msg\n\tError error\n}\n\ntype Resolver struct {\n\tipv6                  bool\n\tipv6Timeout           time.Duration\n\tmain                  []dnsClient\n\tfallback              []dnsClient\n\tfallbackDomainFilters []C.DomainMatcher\n\tfallbackIPFilters     []C.IpMatcher\n\tgroup                 singleflight.Group[*D.Msg]\n\tcache                 dnsCache\n\tpolicy                []dnsPolicy\n\tdefaultResolver       *Resolver\n}\n\nfunc (r *Resolver) LookupIPPrimaryIPv4(ctx context.Context, host string) (ips []netip.Addr, err error) {\n\tch := make(chan []netip.Addr, 1)\n\tgo func() {\n\t\tdefer close(ch)\n\t\tip, err := r.lookupIP(ctx, host, D.TypeAAAA)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tch <- ip\n\t}()\n\n\tips, err = r.lookupIP(ctx, host, D.TypeA)\n\tif err == nil {\n\t\treturn\n\t}\n\n\tip, open := <-ch\n\tif !open {\n\t\treturn nil, resolver.ErrIPNotFound\n\t}\n\n\treturn ip, nil\n}\n\nfunc (r *Resolver) LookupIP(ctx context.Context, host string) (ips []netip.Addr, err error) {\n\tch := make(chan []netip.Addr, 1)\n\tgo func() {\n\t\tdefer close(ch)\n\t\tip, err := r.lookupIP(ctx, host, D.TypeAAAA)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tch <- ip\n\t}()\n\n\tips, err = r.lookupIP(ctx, host, D.TypeA)\n\tvar waitIPv6 *time.Timer\n\tif r != nil && r.ipv6Timeout > 0 {\n\t\twaitIPv6 = time.NewTimer(r.ipv6Timeout)\n\t} else {\n\t\twaitIPv6 = time.NewTimer(100 * time.Millisecond)\n\t}\n\tdefer waitIPv6.Stop()\n\tselect {\n\tcase ipv6s, open := <-ch:\n\t\tif !open && err != nil {\n\t\t\treturn nil, resolver.ErrIPNotFound\n\t\t}\n\t\tips = append(ips, ipv6s...)\n\tcase <-waitIPv6.C:\n\t\t// wait ipv6 result\n\t}\n\n\treturn ips, nil\n}\n\n// LookupIPv4 request with TypeA\nfunc (r *Resolver) LookupIPv4(ctx context.Context, host string) ([]netip.Addr, error) {\n\treturn r.lookupIP(ctx, host, D.TypeA)\n}\n\n// LookupIPv6 request with TypeAAAA\nfunc (r *Resolver) LookupIPv6(ctx context.Context, host string) ([]netip.Addr, error) {\n\treturn r.lookupIP(ctx, host, D.TypeAAAA)\n}\n\nfunc (r *Resolver) shouldIPFallback(ip netip.Addr) bool {\n\tfor _, filter := range r.fallbackIPFilters {\n\t\tif filter.MatchIp(ip) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (r *Resolver) ResolveECH(ctx context.Context, host string) ([]byte, error) {\n\tquery := &D.Msg{}\n\tquery.SetQuestion(D.Fqdn(host), D.TypeHTTPS)\n\n\tmsg, err := r.ExchangeContext(ctx, query)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor _, rr := range msg.Answer {\n\t\tswitch resource := rr.(type) {\n\t\tcase *D.HTTPS:\n\t\t\tfor _, value := range resource.Value {\n\t\t\t\tif echConfig, ok := value.(*D.SVCBECHConfig); ok {\n\t\t\t\t\treturn echConfig.ECH, nil\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn nil, errors.New(\"no ECH config found in DNS records\")\n}\n\n// ExchangeContext a batch of dns request with context.Context, and it use cache\nfunc (r *Resolver) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) {\n\tif len(m.Question) == 0 {\n\t\treturn nil, errors.New(\"should have one question at least\")\n\t}\n\tcontinueFetch := false\n\tdefer func() {\n\t\tif continueFetch || errors.Is(err, context.DeadlineExceeded) || errors.Is(err, context.Canceled) {\n\t\t\tgo func() {\n\t\t\t\tctx, cancel := context.WithTimeout(context.Background(), resolver.DefaultDNSTimeout)\n\t\t\t\tdefer cancel()\n\t\t\t\t_, _ = r.exchangeWithoutCache(ctx, m) // ignore result, just for putMsgToCache\n\t\t\t}()\n\t\t}\n\t}()\n\n\tq := m.Question[0]\n\tdomain := msgToDomain(m)\n\tmsg, expireTime, hit := getMsgFromCache(r.cache, q)\n\tif hit {\n\t\tlog.Debugln(\"[DNS] cache hit %s --> %s, expire at %s\", domain, msgToLogString(msg), expireTime.Format(\"2006-01-02 15:04:05\"))\n\t\tnow := time.Now()\n\t\tif expireTime.Before(now) {\n\t\t\tsetMsgTTL(msg, uint32(1)) // Continue fetch\n\t\t\tcontinueFetch = true\n\t\t} else {\n\t\t\t// updating TTL by subtracting common delta time from each DNS record\n\t\t\tupdateMsgTTL(msg, uint32(time.Until(expireTime).Seconds()))\n\t\t}\n\t\treturn\n\t}\n\treturn r.exchangeWithoutCache(ctx, m)\n}\n\n// ExchangeWithoutCache a batch of dns request, and it do NOT GET from cache\nfunc (r *Resolver) exchangeWithoutCache(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) {\n\tq := m.Question[0]\n\n\tretryNum := 0\n\tretryMax := 3\n\tfn := func() (result *D.Msg, err error) {\n\t\tctx, cancel := context.WithTimeout(context.Background(), resolver.DefaultDNSTimeout) // reset timeout in singleflight\n\t\tdefer cancel()\n\t\tcache := false\n\n\t\tdefer func() {\n\t\t\tif err != nil {\n\t\t\t\tresult = &D.Msg{}\n\t\t\t\tresult.Opcode = retryNum\n\t\t\t\tretryNum++\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif cache {\n\t\t\t\tputMsgToCache(r.cache, q, result)\n\t\t\t}\n\t\t}()\n\n\t\tisIPReq := isIPRequest(q)\n\t\tif isIPReq {\n\t\t\tcache = true\n\t\t\treturn r.ipExchange(ctx, m)\n\t\t}\n\n\t\tif matched := r.matchPolicy(m); len(matched) != 0 {\n\t\t\tresult, cache, err = batchExchange(ctx, matched, m)\n\t\t\treturn\n\t\t}\n\t\tresult, cache, err = batchExchange(ctx, r.main, m)\n\t\treturn\n\t}\n\n\tch := r.group.DoChan(q.String(), fn)\n\n\tvar result singleflight.Result[*D.Msg]\n\n\tselect {\n\tcase result = <-ch:\n\t\tbreak\n\tcase <-ctx.Done():\n\t\tselect {\n\t\tcase result = <-ch: // maybe ctxDone and chFinish in same time, get DoChan's result as much as possible\n\t\t\tbreak\n\t\tdefault:\n\t\t\tgo func() { // start a retrying monitor in background\n\t\t\t\tresult := <-ch\n\t\t\t\tret, err, shared := result.Val, result.Err, result.Shared\n\t\t\t\tif err != nil && !shared && ret.Opcode < retryMax { // retry\n\t\t\t\t\tr.group.DoChan(q.String(), fn)\n\t\t\t\t}\n\t\t\t}()\n\t\t\treturn nil, ctx.Err()\n\t\t}\n\t}\n\n\tret, err, shared := result.Val, result.Err, result.Shared\n\tif err != nil && !shared && ret.Opcode < retryMax { // retry\n\t\tr.group.DoChan(q.String(), fn)\n\t}\n\n\tif err == nil {\n\t\tmsg = ret\n\t\tif shared {\n\t\t\tmsg = msg.Copy()\n\t\t}\n\t}\n\n\treturn\n}\n\nfunc (r *Resolver) matchPolicy(m *D.Msg) []dnsClient {\n\tif r.policy == nil {\n\t\treturn nil\n\t}\n\n\tdomain := msgToDomain(m)\n\tif domain == \"\" {\n\t\treturn nil\n\t}\n\n\tfor _, policy := range r.policy {\n\t\tif dnsClients := policy.Match(domain); len(dnsClients) > 0 {\n\t\t\treturn dnsClients\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (r *Resolver) shouldOnlyQueryFallback(m *D.Msg) bool {\n\tif r.fallback == nil || len(r.fallbackDomainFilters) == 0 {\n\t\treturn false\n\t}\n\n\tdomain := msgToDomain(m)\n\n\tif domain == \"\" {\n\t\treturn false\n\t}\n\n\tfor _, df := range r.fallbackDomainFilters {\n\t\tif df.MatchDomain(domain) {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc (r *Resolver) ipExchange(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) {\n\tif matched := r.matchPolicy(m); len(matched) != 0 {\n\t\tres := <-r.asyncExchange(ctx, matched, m)\n\t\treturn res.Msg, res.Error\n\t}\n\n\tonlyFallback := r.shouldOnlyQueryFallback(m)\n\n\tif onlyFallback {\n\t\tres := <-r.asyncExchange(ctx, r.fallback, m)\n\t\treturn res.Msg, res.Error\n\t}\n\n\tmsgCh := r.asyncExchange(ctx, r.main, m)\n\n\tif r.fallback == nil || len(r.fallback) == 0 { // directly return if no fallback servers are available\n\t\tres := <-msgCh\n\t\tmsg, err = res.Msg, res.Error\n\t\treturn\n\t}\n\n\tres := <-msgCh\n\tif res.Error == nil {\n\t\tif ips := msgToIP(res.Msg); len(ips) != 0 {\n\t\t\tshouldNotFallback := lo.EveryBy(ips, func(ip netip.Addr) bool {\n\t\t\t\treturn !r.shouldIPFallback(ip)\n\t\t\t})\n\t\t\tif shouldNotFallback {\n\t\t\t\tmsg, err = res.Msg, res.Error // no need to wait for fallback result\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\tres = <-r.asyncExchange(ctx, r.fallback, m)\n\tmsg, err = res.Msg, res.Error\n\treturn\n}\n\nfunc (r *Resolver) lookupIP(ctx context.Context, host string, dnsType uint16) (ips []netip.Addr, err error) {\n\tip, err := netip.ParseAddr(host)\n\tif err == nil {\n\t\tip = ip.Unmap()\n\t\tisIPv4 := ip.Is4()\n\t\tif dnsType == D.TypeAAAA && !isIPv4 {\n\t\t\treturn []netip.Addr{ip}, nil\n\t\t} else if dnsType == D.TypeA && isIPv4 {\n\t\t\treturn []netip.Addr{ip}, nil\n\t\t} else {\n\t\t\treturn []netip.Addr{}, resolver.ErrIPVersion\n\t\t}\n\t}\n\n\tquery := &D.Msg{}\n\tquery.SetQuestion(D.Fqdn(host), dnsType)\n\n\tmsg, err := r.ExchangeContext(ctx, query)\n\tif err != nil {\n\t\treturn []netip.Addr{}, err\n\t}\n\n\tips = msgToIP(msg)\n\tipLength := len(ips)\n\tif ipLength == 0 {\n\t\treturn []netip.Addr{}, resolver.ErrIPNotFound\n\t}\n\n\treturn\n}\n\nfunc (r *Resolver) asyncExchange(ctx context.Context, client []dnsClient, msg *D.Msg) <-chan *result {\n\tch := make(chan *result, 1)\n\tgo func() {\n\t\tres, _, err := batchExchange(ctx, client, msg)\n\t\tch <- &result{Msg: res, Error: err}\n\t}()\n\treturn ch\n}\n\n// Invalid return this resolver can or can't be used\nfunc (r *Resolver) Invalid() bool {\n\tif r == nil {\n\t\treturn false\n\t}\n\treturn len(r.main) > 0\n}\n\nfunc (r *Resolver) ClearCache() {\n\tif r != nil && r.cache != nil {\n\t\tr.cache.Clear()\n\t}\n}\n\nfunc (r *Resolver) ResetConnection() {\n\tif r != nil {\n\t\tfor _, c := range r.main {\n\t\t\tc.ResetConnection()\n\t\t}\n\t\tfor _, c := range r.fallback {\n\t\t\tc.ResetConnection()\n\t\t}\n\t\tif dr := r.defaultResolver; dr != nil {\n\t\t\tdr.ResetConnection()\n\t\t}\n\t}\n}\n\ntype NameServer struct {\n\tNet          string\n\tAddr         string\n\tProxyAdapter C.ProxyAdapter\n\tProxyName    string\n\tParams       map[string]string\n\tPreferH3     bool\n}\n\nfunc (ns NameServer) Equal(ns2 NameServer) bool {\n\tdefer func() {\n\t\t// C.ProxyAdapter compare maybe panic, just ignore\n\t\trecover()\n\t}()\n\tif ns.Net == ns2.Net &&\n\t\tns.Addr == ns2.Addr &&\n\t\tns.ProxyAdapter == ns2.ProxyAdapter &&\n\t\tns.ProxyName == ns2.ProxyName &&\n\t\tmaps.Equal(ns.Params, ns2.Params) &&\n\t\tns.PreferH3 == ns2.PreferH3 {\n\t\treturn true\n\t}\n\treturn false\n}\n\ntype Policy struct {\n\tDomain      string\n\tMatcher     C.DomainMatcher\n\tNameServers []NameServer\n}\n\ntype Config struct {\n\tMain, Fallback       []NameServer\n\tDefault              []NameServer\n\tProxyServer          []NameServer\n\tDirectServer         []NameServer\n\tDirectFollowPolicy   bool\n\tIPv6                 bool\n\tIPv6Timeout          uint\n\tFallbackIPFilter     []C.IpMatcher\n\tFallbackDomainFilter []C.DomainMatcher\n\tPolicy               []Policy\n\tProxyServerPolicy    []Policy\n\tCacheAlgorithm       string\n\tCacheMaxSize         int\n}\n\nfunc (config Config) newCache() dnsCache {\n\tif config.CacheMaxSize == 0 {\n\t\tconfig.CacheMaxSize = 4096\n\t}\n\tswitch config.CacheAlgorithm {\n\tcase \"arc\":\n\t\treturn arc.New(arc.WithSize[string, *D.Msg](config.CacheMaxSize))\n\tdefault:\n\t\treturn lru.New(lru.WithSize[string, *D.Msg](config.CacheMaxSize), lru.WithStale[string, *D.Msg](true))\n\t}\n}\n\ntype Resolvers struct {\n\t*Resolver\n\tProxyResolver  *Resolver\n\tDirectResolver *Resolver\n}\n\nfunc (rs Resolvers) ClearCache() {\n\trs.Resolver.ClearCache()\n\trs.ProxyResolver.ClearCache()\n\trs.DirectResolver.ClearCache()\n}\n\nfunc (rs Resolvers) ResetConnection() {\n\trs.Resolver.ResetConnection()\n\trs.ProxyResolver.ResetConnection()\n\trs.DirectResolver.ResetConnection()\n}\n\nfunc NewResolver(config Config) (rs Resolvers) {\n\tdefaultResolver := &Resolver{\n\t\tmain:        transform(config.Default, nil),\n\t\tcache:       config.newCache(),\n\t\tipv6Timeout: time.Duration(config.IPv6Timeout) * time.Millisecond,\n\t}\n\n\tvar nameServerCache []struct {\n\t\tNameServer\n\t\tdnsClient\n\t}\n\tcacheTransform := func(nameserver []NameServer) (result []dnsClient) {\n\tLOOP:\n\t\tfor _, ns := range nameserver {\n\t\t\tfor _, nsc := range nameServerCache {\n\t\t\t\tif nsc.NameServer.Equal(ns) {\n\t\t\t\t\tresult = append(result, nsc.dnsClient)\n\t\t\t\t\tcontinue LOOP\n\t\t\t\t}\n\t\t\t}\n\t\t\t// not in cache\n\t\t\tdc := transform([]NameServer{ns}, defaultResolver)\n\t\t\tif len(dc) > 0 {\n\t\t\t\tdc := dc[0]\n\t\t\t\tnameServerCache = append(nameServerCache, struct {\n\t\t\t\t\tNameServer\n\t\t\t\t\tdnsClient\n\t\t\t\t}{NameServer: ns, dnsClient: dc})\n\t\t\t\tresult = append(result, dc)\n\t\t\t}\n\t\t}\n\t\treturn\n\t}\n\n\tmakePolicy := func(policies []Policy) (dnsPolicies []dnsPolicy) {\n\t\tvar triePolicy *trie.DomainTrie[[]dnsClient]\n\t\tinsertPolicy := func(policy dnsPolicy) {\n\t\t\tif triePolicy != nil {\n\t\t\t\ttriePolicy.Optimize()\n\t\t\t\tdnsPolicies = append(dnsPolicies, domainTriePolicy{triePolicy})\n\t\t\t\ttriePolicy = nil\n\t\t\t}\n\t\t\tif policy != nil {\n\t\t\t\tdnsPolicies = append(dnsPolicies, policy)\n\t\t\t}\n\t\t}\n\n\t\tfor _, policy := range policies {\n\t\t\tif policy.Matcher != nil {\n\t\t\t\tinsertPolicy(domainMatcherPolicy{matcher: policy.Matcher, dnsClients: cacheTransform(policy.NameServers)})\n\t\t\t} else {\n\t\t\t\tif triePolicy == nil {\n\t\t\t\t\ttriePolicy = trie.New[[]dnsClient]()\n\t\t\t\t}\n\t\t\t\t_ = triePolicy.Insert(policy.Domain, cacheTransform(policy.NameServers))\n\t\t\t}\n\t\t}\n\t\tinsertPolicy(nil)\n\t\treturn\n\t}\n\n\tr := &Resolver{\n\t\tipv6:        config.IPv6,\n\t\tmain:        cacheTransform(config.Main),\n\t\tcache:       config.newCache(),\n\t\tipv6Timeout: time.Duration(config.IPv6Timeout) * time.Millisecond,\n\t\tpolicy:      makePolicy(config.Policy),\n\t}\n\tr.defaultResolver = defaultResolver\n\trs.Resolver = r\n\n\tif len(config.ProxyServer) != 0 {\n\t\trs.ProxyResolver = &Resolver{\n\t\t\tipv6:        config.IPv6,\n\t\t\tmain:        cacheTransform(config.ProxyServer),\n\t\t\tcache:       config.newCache(),\n\t\t\tipv6Timeout: time.Duration(config.IPv6Timeout) * time.Millisecond,\n\t\t\tpolicy:      makePolicy(config.ProxyServerPolicy),\n\t\t}\n\t}\n\n\tif len(config.DirectServer) != 0 {\n\t\trs.DirectResolver = &Resolver{\n\t\t\tipv6:        config.IPv6,\n\t\t\tmain:        cacheTransform(config.DirectServer),\n\t\t\tcache:       config.newCache(),\n\t\t\tipv6Timeout: time.Duration(config.IPv6Timeout) * time.Millisecond,\n\t\t}\n\t\tif config.DirectFollowPolicy {\n\t\t\trs.DirectResolver.policy = r.policy\n\t\t}\n\t}\n\n\tif len(config.Fallback) != 0 {\n\t\tr.fallback = cacheTransform(config.Fallback)\n\t\tr.fallbackIPFilters = config.FallbackIPFilter\n\t\tr.fallbackDomainFilters = config.FallbackDomainFilter\n\t}\n\n\treturn\n}\n\nvar ParseNameServer func(servers []string) ([]NameServer, error) // define in config/config.go\n"
  },
  {
    "path": "core/Clash.Meta/dns/server.go",
    "content": "package dns\n\nimport (\n\t\"context\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\t\"github.com/metacubex/mihomo/common/sockopt\"\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\tD \"github.com/miekg/dns\"\n)\n\nvar (\n\taddress string\n\tserver  = &Server{}\n\n\tdnsDefaultTTL uint32 = 600\n)\n\ntype Server struct {\n\tservice   resolver.Service\n\ttcpServer *D.Server\n\tudpServer *D.Server\n}\n\n// ServeDNS implement D.Handler ServeDNS\nfunc (s *Server) ServeDNS(w D.ResponseWriter, r *D.Msg) {\n\tmsg, err := s.service.ServeMsg(context.Background(), r)\n\tif err != nil {\n\t\tm := new(D.Msg)\n\t\tm.SetRcode(r, D.RcodeServerFailure)\n\t\t// does not matter if this write fails\n\t\tw.WriteMsg(m)\n\t\treturn\n\t}\n\tmsg.Compress = true\n\tw.WriteMsg(msg)\n}\n\nfunc (s *Server) SetService(service resolver.Service) {\n\ts.service = service\n}\n\nfunc ReCreateServer(addr string, service resolver.Service) {\n\tif addr == address && service != nil {\n\t\tserver.SetService(service)\n\t\treturn\n\t}\n\n\tif server.tcpServer != nil {\n\t\t_ = server.tcpServer.Shutdown()\n\t\tserver.tcpServer = nil\n\t}\n\n\tif server.udpServer != nil {\n\t\t_ = server.udpServer.Shutdown()\n\t\tserver.udpServer = nil\n\t}\n\n\tserver.service = nil\n\taddress = \"\"\n\n\tif addr == \"\" || service == nil {\n\t\treturn\n\t}\n\n\tvar err error\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tlog.Errorln(\"Start DNS server error: %s\", err.Error())\n\t\t}\n\t}()\n\n\t_, port, err := net.SplitHostPort(addr)\n\tif port == \"0\" || port == \"\" || err != nil {\n\t\treturn\n\t}\n\n\taddress = addr\n\tserver = &Server{service: service}\n\n\tgo func() {\n\t\tp, err := inbound.ListenPacket(\"udp\", addr)\n\t\tif err != nil {\n\t\t\tlog.Errorln(\"Start DNS server(UDP) error: %s\", err.Error())\n\t\t\treturn\n\t\t}\n\n\t\tif err := sockopt.UDPReuseaddr(p); err != nil {\n\t\t\tlog.Warnln(\"Failed to Reuse UDP Address: %s\", err)\n\t\t}\n\n\t\tlog.Infoln(\"DNS server(UDP) listening at: %s\", p.LocalAddr().String())\n\t\tserver.udpServer = &D.Server{Addr: addr, PacketConn: p, Handler: server}\n\t\t_ = server.udpServer.ActivateAndServe()\n\t}()\n\n\tgo func() {\n\t\tl, err := inbound.Listen(\"tcp\", addr)\n\t\tif err != nil {\n\t\t\tlog.Errorln(\"Start DNS server(TCP) error: %s\", err.Error())\n\t\t\treturn\n\t\t}\n\n\t\tlog.Infoln(\"DNS server(TCP) listening at: %s\", l.Addr().String())\n\t\tserver.tcpServer = &D.Server{Addr: addr, Listener: l, Handler: server}\n\t\t_ = server.tcpServer.ActivateAndServe()\n\t}()\n\n}\n"
  },
  {
    "path": "core/Clash.Meta/dns/service.go",
    "content": "package dns\n\nimport (\n\t\"context\"\n\t\"errors\"\n\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\ticontext \"github.com/metacubex/mihomo/context\"\n\tD \"github.com/miekg/dns\"\n)\n\ntype Service struct {\n\thandler handler\n}\n\n// ServeMsg implement [resolver.Service] ResolveMsg\nfunc (s *Service) ServeMsg(ctx context.Context, msg *D.Msg) (*D.Msg, error) {\n\tif len(msg.Question) == 0 {\n\t\treturn nil, errors.New(\"at least one question is required\")\n\t}\n\n\treturn s.handler(icontext.NewDNSContext(ctx), msg)\n}\n\nvar _ resolver.Service = (*Service)(nil)\n\nfunc NewService(resolver *Resolver, mapper *ResolverEnhancer) *Service {\n\treturn &Service{handler: newHandler(resolver, mapper)}\n}\n"
  },
  {
    "path": "core/Clash.Meta/dns/system.go",
    "content": "package dns\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\n\tD \"github.com/miekg/dns\"\n)\n\nconst (\n\tSystemDnsFlushTime   = 5 * time.Minute\n\tSystemDnsDeleteTimes = 12 // 12*5 = 60min\n)\n\ntype systemDnsClient struct {\n\tdisableTimes uint32\n\tdnsClient\n}\n\ntype systemClient struct {\n\tmu         sync.Mutex\n\tdnsClients map[string]*systemDnsClient\n\tlastFlush  time.Time\n\tdefaultNS  []dnsClient\n}\n\nfunc (c *systemClient) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) {\n\tdnsClients, err := c.getDnsClients()\n\tif len(dnsClients) == 0 && len(c.defaultNS) > 0 {\n\t\tdnsClients = c.defaultNS\n\t\terr = nil\n\t}\n\tif err != nil {\n\t\treturn\n\t}\n\tmsg, _, err = batchExchange(ctx, dnsClients, m)\n\treturn\n}\n\n// Address implements dnsClient\nfunc (c *systemClient) Address() string {\n\tdnsClients, _ := c.getDnsClients()\n\tisDefault := \"\"\n\tif len(dnsClients) == 0 && len(c.defaultNS) > 0 {\n\t\tdnsClients = c.defaultNS\n\t\tisDefault = \"[defaultNS]\"\n\t}\n\taddrs := make([]string, 0, len(dnsClients))\n\tfor _, c := range dnsClients {\n\t\taddrs = append(addrs, c.Address())\n\t}\n\treturn fmt.Sprintf(\"system%s(%s)\", isDefault, strings.Join(addrs, \",\"))\n}\n\nvar _ dnsClient = (*systemClient)(nil)\n\nfunc newSystemClient() *systemClient {\n\treturn &systemClient{\n\t\tdnsClients: map[string]*systemDnsClient{},\n\t}\n}\n\nfunc init() {\n\tr := NewResolver(Config{})\n\tc := newSystemClient()\n\tc.defaultNS = transform([]NameServer{{Addr: \"114.114.114.114:53\"}, {Addr: \"8.8.8.8:53\"}}, nil)\n\tr.main = []dnsClient{c}\n\tresolver.SystemResolver = r\n}\n"
  },
  {
    "path": "core/Clash.Meta/dns/system_common.go",
    "content": "//go:build !android\n\npackage dns\n\nimport (\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"golang.org/x/exp/slices\"\n)\n\nfunc (c *systemClient) getDnsClients() ([]dnsClient, error) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\tvar err error\n\tif time.Since(c.lastFlush) > SystemDnsFlushTime {\n\t\tvar nameservers []string\n\t\tif nameservers, err = dnsReadConfig(); err == nil {\n\t\t\tlog.Debugln(\"[DNS] system dns update to %s\", nameservers)\n\t\t\tfor _, addr := range nameservers {\n\t\t\t\tif resolver.IsSystemDnsBlacklisted(addr) {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif _, ok := c.dnsClients[addr]; !ok {\n\t\t\t\t\tclients := transform(\n\t\t\t\t\t\t[]NameServer{{\n\t\t\t\t\t\t\tAddr: net.JoinHostPort(addr, \"53\"),\n\t\t\t\t\t\t\tNet:  \"udp\",\n\t\t\t\t\t\t}},\n\t\t\t\t\t\tnil,\n\t\t\t\t\t)\n\t\t\t\t\tif len(clients) > 0 {\n\t\t\t\t\t\tc.dnsClients[addr] = &systemDnsClient{\n\t\t\t\t\t\t\tdisableTimes: 0,\n\t\t\t\t\t\t\tdnsClient:    clients[0],\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tavailable := 0\n\t\t\tfor nameserver, sdc := range c.dnsClients {\n\t\t\t\tif slices.Contains(nameservers, nameserver) {\n\t\t\t\t\tsdc.disableTimes = 0 // enable\n\t\t\t\t\tavailable++\n\t\t\t\t} else {\n\t\t\t\t\tif sdc.disableTimes > SystemDnsDeleteTimes {\n\t\t\t\t\t\tdelete(c.dnsClients, nameserver) // drop too old dnsClient\n\t\t\t\t\t} else {\n\t\t\t\t\t\tsdc.disableTimes++\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif available > 0 {\n\t\t\t\tc.lastFlush = time.Now()\n\t\t\t}\n\t\t}\n\t}\n\tdnsClients := make([]dnsClient, 0, len(c.dnsClients))\n\tfor _, sdc := range c.dnsClients {\n\t\tif sdc.disableTimes == 0 {\n\t\t\tdnsClients = append(dnsClients, sdc.dnsClient)\n\t\t}\n\t}\n\tif len(dnsClients) > 0 {\n\t\treturn dnsClients, nil\n\t}\n\treturn nil, err\n}\n\nfunc (c *systemClient) ResetConnection() {}\n"
  },
  {
    "path": "core/Clash.Meta/dns/system_posix.go",
    "content": "//go:build !windows\n\npackage dns\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"net/netip\"\n\t\"os\"\n\t\"strings\"\n)\n\nconst resolvConf = \"/etc/resolv.conf\"\n\nfunc dnsReadConfig() (servers []string, err error) {\n\tfile, err := os.Open(resolvConf)\n\tif err != nil {\n\t\terr = fmt.Errorf(\"failed to read %s: %w\", resolvConf, err)\n\t\treturn\n\t}\n\tdefer func() { _ = file.Close() }()\n\tscanner := bufio.NewScanner(file)\n\tfor scanner.Scan() {\n\t\tline := scanner.Text()\n\t\tif len(line) > 0 && (line[0] == ';' || line[0] == '#') {\n\t\t\t// comment.\n\t\t\tcontinue\n\t\t}\n\t\tf := strings.Fields(line)\n\t\tif len(f) < 1 {\n\t\t\tcontinue\n\t\t}\n\t\tswitch f[0] {\n\t\tcase \"nameserver\": // add one name server\n\t\t\tif len(f) > 1 {\n\t\t\t\tif addr, err := netip.ParseAddr(f[1]); err == nil {\n\t\t\t\t\tservers = append(servers, addr.String())\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/dns/system_windows.go",
    "content": "//go:build windows\n\npackage dns\n\nimport (\n\t\"net/netip\"\n\t\"os\"\n\t\"strconv\"\n\t\"syscall\"\n\t\"unsafe\"\n\n\t\"golang.org/x/exp/slices\"\n\t\"golang.org/x/sys/windows\"\n)\n\nfunc dnsReadConfig() (servers []string, err error) {\n\taas, err := adapterAddresses()\n\tif err != nil {\n\t\treturn\n\t}\n\tfor _, aa := range aas {\n\t\t// Only take interfaces whose OperStatus is IfOperStatusUp(0x01) into DNS configs.\n\t\tif aa.OperStatus != windows.IfOperStatusUp {\n\t\t\tcontinue\n\t\t}\n\n\t\t// Only take interfaces which have at least one gateway\n\t\tif aa.FirstGatewayAddress == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\tfor dns := aa.FirstDnsServerAddress; dns != nil; dns = dns.Next {\n\t\t\tsa, err := dns.Address.Sockaddr.Sockaddr()\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tvar ip netip.Addr\n\t\t\tswitch sa := sa.(type) {\n\t\t\tcase *syscall.SockaddrInet4:\n\t\t\t\tip = netip.AddrFrom4(sa.Addr)\n\t\t\tcase *syscall.SockaddrInet6:\n\t\t\t\tif sa.Addr[0] == 0xfe && sa.Addr[1] == 0xc0 {\n\t\t\t\t\t// Ignore these fec0/10 ones. Windows seems to\n\t\t\t\t\t// populate them as defaults on its misc rando\n\t\t\t\t\t// interfaces.\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tip = netip.AddrFrom16(sa.Addr)\n\t\t\t\tif sa.ZoneId != 0 {\n\t\t\t\t\tip = ip.WithZone(strconv.FormatInt(int64(sa.ZoneId), 10))\n\t\t\t\t}\n\t\t\t\t//continue\n\t\t\tdefault:\n\t\t\t\t// Unexpected type.\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tipStr := ip.String()\n\t\t\tif slices.Contains(servers, ipStr) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tservers = append(servers, ipStr)\n\t\t}\n\t}\n\treturn\n}\n\n// adapterAddresses returns a list of IP adapter and address\n// structures. The structure contains an IP adapter and flattened\n// multiple IP addresses including unicast, anycast and multicast\n// addresses.\nfunc adapterAddresses() ([]*windows.IpAdapterAddresses, error) {\n\tvar b []byte\n\tl := uint32(15000) // recommended initial size\n\tfor {\n\t\tb = make([]byte, l)\n\t\tconst flags = windows.GAA_FLAG_INCLUDE_PREFIX | windows.GAA_FLAG_INCLUDE_GATEWAYS\n\t\terr := windows.GetAdaptersAddresses(syscall.AF_UNSPEC, flags, 0, (*windows.IpAdapterAddresses)(unsafe.Pointer(&b[0])), &l)\n\t\tif err == nil {\n\t\t\tif l == 0 {\n\t\t\t\treturn nil, nil\n\t\t\t}\n\t\t\tbreak\n\t\t}\n\t\tif err.(syscall.Errno) != syscall.ERROR_BUFFER_OVERFLOW {\n\t\t\treturn nil, os.NewSyscallError(\"getadaptersaddresses\", err)\n\t\t}\n\t\tif l <= uint32(len(b)) {\n\t\t\treturn nil, os.NewSyscallError(\"getadaptersaddresses\", err)\n\t\t}\n\t}\n\tvar aas []*windows.IpAdapterAddresses\n\tfor aa := (*windows.IpAdapterAddresses)(unsafe.Pointer(&b[0])); aa != nil; aa = aa.Next {\n\t\taas = append(aas, aa)\n\t}\n\treturn aas, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/dns/util.go",
    "content": "package dns\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/netip\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/picker\"\n\t\"github.com/metacubex/mihomo/component/ech/echparser\"\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\tD \"github.com/miekg/dns\"\n\t\"github.com/samber/lo\"\n\t\"golang.org/x/exp/slices\"\n)\n\nconst (\n\tMaxMsgSize = 65535\n)\n\nconst serverFailureCacheTTL uint32 = 5\n\nfunc minimalTTL(records []D.RR) uint32 {\n\trr := lo.MinBy(records, func(r1 D.RR, r2 D.RR) bool {\n\t\treturn r1.Header().Ttl < r2.Header().Ttl\n\t})\n\tif rr == nil {\n\t\treturn 0\n\t}\n\treturn rr.Header().Ttl\n}\n\nfunc updateTTL(records []D.RR, ttl uint32) {\n\tif len(records) == 0 {\n\t\treturn\n\t}\n\tdelta := minimalTTL(records) - ttl\n\tfor i := range records {\n\t\trecords[i].Header().Ttl = lo.Clamp(records[i].Header().Ttl-delta, 1, records[i].Header().Ttl)\n\t}\n}\n\n// getMsgFromCache returns a cached dns message if it exists, otherwise returns nil.\n// the returned msg is a copy of the original msg, so it can be modified without affecting the original msg.\nfunc getMsgFromCache(c dnsCache, q D.Question) (*D.Msg, time.Time, bool) {\n\tmsg, expireTime, hit := c.GetWithExpire(q.String())\n\tif msg != nil {\n\t\tmsg = msg.Copy() // never modify the original msg\n\t}\n\treturn msg, expireTime, hit\n}\n\n// putMsgToCache puts a dns message into the cache.\n// the msg is copied before being stored in the cache, so it can be modified without affecting the original msg.\nfunc putMsgToCache(c dnsCache, q D.Question, msg *D.Msg) {\n\t// skip dns cache for acme challenge\n\tif q.Qtype == D.TypeTXT && strings.HasPrefix(q.Name, \"_acme-challenge.\") {\n\t\tlog.Debugln(\"[DNS] dns cache ignored because of acme challenge for: %s\", q.Name)\n\t\treturn\n\t}\n\n\tmsg = msg.Copy() // never modify the original msg\n\n\t// OPT RRs MUST NOT be cached, forwarded, or stored in or loaded from master files.\n\tmsg.Extra = lo.Filter(msg.Extra, func(rr D.RR, index int) bool {\n\t\treturn rr.Header().Rrtype != D.TypeOPT\n\t})\n\n\tvar ttl uint32\n\tif msg.Rcode == D.RcodeServerFailure {\n\t\t// [...] a resolver MAY cache a server failure response.\n\t\t// If it does so it MUST NOT cache it for longer than five (5) minutes [...]\n\t\tttl = serverFailureCacheTTL\n\t} else {\n\t\tttl = minimalTTL(lo.Concat(msg.Answer, msg.Ns, msg.Extra))\n\t}\n\tif ttl == 0 {\n\t\treturn\n\t}\n\n\tc.SetWithExpire(q.String(), msg, time.Now().Add(time.Duration(ttl)*time.Second))\n}\n\nfunc setMsgTTL(msg *D.Msg, ttl uint32) {\n\tfor _, answer := range msg.Answer {\n\t\tanswer.Header().Ttl = ttl\n\t}\n\n\tfor _, ns := range msg.Ns {\n\t\tns.Header().Ttl = ttl\n\t}\n\n\tfor _, extra := range msg.Extra {\n\t\textra.Header().Ttl = ttl\n\t}\n}\n\nfunc updateMsgTTL(msg *D.Msg, ttl uint32) {\n\tupdateTTL(msg.Answer, ttl)\n\tupdateTTL(msg.Ns, ttl)\n\tupdateTTL(msg.Extra, ttl)\n}\n\nfunc isIPRequest(q D.Question) bool {\n\treturn q.Qclass == D.ClassINET && (q.Qtype == D.TypeA || q.Qtype == D.TypeAAAA || q.Qtype == D.TypeCNAME)\n}\n\nfunc transform(servers []NameServer, resolver *Resolver) []dnsClient {\n\tret := make([]dnsClient, 0, len(servers))\n\tfor _, s := range servers {\n\t\tvar c dnsClient\n\t\tswitch s.Net {\n\t\tcase \"tls\":\n\t\t\tc = newDoTClient(s.Addr, resolver, s.Params, s.ProxyAdapter, s.ProxyName)\n\t\tcase \"https\":\n\t\t\tc = newDoHClient(s.Addr, resolver, s.PreferH3, s.Params, s.ProxyAdapter, s.ProxyName)\n\t\tcase \"dhcp\":\n\t\t\tc = newDHCPClient(s.Addr)\n\t\tcase \"system\":\n\t\t\tc = newSystemClient()\n\t\tcase \"rcode\":\n\t\t\tc = newRCodeClient(s.Addr)\n\t\tcase \"quic\":\n\t\t\tc = newDoQ(s.Addr, resolver, s.Params, s.ProxyAdapter, s.ProxyName)\n\t\tdefault:\n\t\t\tc = newClient(s.Addr, resolver, s.Net, s.Params, s.ProxyAdapter, s.ProxyName)\n\t\t}\n\n\t\tc = warpClientWithEdns0Subnet(c, s.Params)\n\t\tc = warpClientWithDisableTypes(c, s.Params)\n\n\t\tret = append(ret, c)\n\t}\n\treturn ret\n}\n\ntype clientWithDisableTypes struct {\n\tdnsClient\n\tdisableTypes map[uint16]struct{}\n}\n\nfunc (c clientWithDisableTypes) ExchangeContext(ctx context.Context, m *D.Msg) (msg *D.Msg, err error) {\n\t// filter dns request\n\tif slices.ContainsFunc(m.Question, c.inQuestion) {\n\t\t// In fact, DNS requests are not allowed to contain multiple questions:\n\t\t// https://stackoverflow.com/questions/4082081/requesting-a-and-aaaa-records-in-single-dns-query/4083071\n\t\t// so, when we find a question containing the type, we can simply discard the entire dns request.\n\t\treturn handleMsgWithEmptyAnswer(m), nil\n\t}\n\n\t// do real exchange\n\tmsg, err = c.dnsClient.ExchangeContext(ctx, m)\n\tif err != nil {\n\t\treturn\n\t}\n\n\t// filter dns response\n\tmsg.Answer = slices.DeleteFunc(msg.Answer, c.inRR)\n\tmsg.Ns = slices.DeleteFunc(msg.Ns, c.inRR)\n\tmsg.Extra = slices.DeleteFunc(msg.Extra, c.inRR)\n\treturn\n}\n\nfunc (c clientWithDisableTypes) inQuestion(q D.Question) bool {\n\t_, ok := c.disableTypes[q.Qtype]\n\treturn ok\n}\n\nfunc (c clientWithDisableTypes) inRR(rr D.RR) bool {\n\t_, ok := c.disableTypes[rr.Header().Rrtype]\n\treturn ok\n}\n\nfunc warpClientWithDisableTypes(c dnsClient, params map[string]string) dnsClient {\n\tdisableTypes := make(map[uint16]struct{})\n\tif params[\"disable-ipv4\"] == \"true\" {\n\t\tdisableTypes[D.TypeA] = struct{}{}\n\t}\n\tif params[\"disable-ipv6\"] == \"true\" {\n\t\tdisableTypes[D.TypeAAAA] = struct{}{}\n\t}\n\tfor key, value := range params {\n\t\tconst prefix = \"disable-qtype-\"\n\t\tif strings.HasPrefix(key, prefix) && value == \"true\" { // eg: disable-qtype-65=true\n\t\t\tqType, err := strconv.ParseUint(key[len(prefix):], 10, 16)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif _, ok := D.TypeToRR[uint16(qType)]; !ok { // check valid RR_Header.Rrtype and Question.qtype\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tdisableTypes[uint16(qType)] = struct{}{}\n\t\t}\n\t}\n\tif len(disableTypes) > 0 {\n\t\treturn clientWithDisableTypes{c, disableTypes}\n\t}\n\treturn c\n}\n\ntype clientWithEdns0Subnet struct {\n\tdnsClient\n\tecsPrefix   netip.Prefix\n\tecsOverride bool\n}\n\nfunc (c clientWithEdns0Subnet) ExchangeContext(ctx context.Context, m *D.Msg) (*D.Msg, error) {\n\tm = m.Copy()\n\tsetEdns0Subnet(m, c.ecsPrefix, c.ecsOverride)\n\treturn c.dnsClient.ExchangeContext(ctx, m)\n}\n\nfunc warpClientWithEdns0Subnet(c dnsClient, params map[string]string) dnsClient {\n\tvar ecsPrefix netip.Prefix\n\tvar ecsOverride bool\n\tif ecs := params[\"ecs\"]; ecs != \"\" {\n\t\tprefix, err := netip.ParsePrefix(ecs)\n\t\tif err != nil {\n\t\t\taddr, err := netip.ParseAddr(ecs)\n\t\t\tif err != nil {\n\t\t\t\tlog.Warnln(\"DNS [%s] config with invalid ecs: %s\", c.Address(), ecs)\n\t\t\t} else {\n\t\t\t\tecsPrefix = netip.PrefixFrom(addr, addr.BitLen())\n\t\t\t}\n\t\t} else {\n\t\t\tecsPrefix = prefix\n\t\t}\n\t}\n\n\tif ecsPrefix.IsValid() {\n\t\tlog.Debugln(\"DNS [%s] config with ecs: %s\", c.Address(), ecsPrefix)\n\t\tif params[\"ecs-override\"] == \"true\" {\n\t\t\tecsOverride = true\n\t\t}\n\t\treturn clientWithEdns0Subnet{c, ecsPrefix, ecsOverride}\n\t}\n\treturn c\n}\n\nfunc handleMsgWithEmptyAnswer(r *D.Msg) *D.Msg {\n\tmsg := &D.Msg{}\n\tmsg.Answer = []D.RR{}\n\n\tmsg.SetRcode(r, D.RcodeSuccess)\n\tmsg.Authoritative = true\n\tmsg.RecursionAvailable = true\n\n\treturn msg\n}\n\nfunc msgToIP(msg *D.Msg) (ips []netip.Addr) {\n\tfor _, answer := range msg.Answer {\n\t\tvar ip netip.Addr\n\t\tswitch ans := answer.(type) {\n\t\tcase *D.AAAA:\n\t\t\tip, _ = netip.AddrFromSlice(ans.AAAA)\n\t\tcase *D.A:\n\t\t\tip, _ = netip.AddrFromSlice(ans.A)\n\t\tdefault:\n\t\t\tcontinue\n\t\t}\n\t\tif !ip.IsValid() {\n\t\t\tcontinue\n\t\t}\n\t\tip = ip.Unmap()\n\t\tips = append(ips, ip)\n\t}\n\treturn\n}\n\nfunc msgToDomain(msg *D.Msg) string {\n\tif len(msg.Question) > 0 {\n\t\treturn strings.TrimRight(msg.Question[0].Name, \".\")\n\t}\n\n\treturn \"\"\n}\n\nfunc msgToQtype(msg *D.Msg) (uint16, string) {\n\tif len(msg.Question) > 0 {\n\t\tqType := msg.Question[0].Qtype\n\t\treturn qType, D.Type(qType).String()\n\t}\n\treturn 0, \"\"\n}\n\nfunc msgToHTTPSRRInfo(msg *D.Msg) string {\n\tvar alpns []string\n\tvar publicName string\n\tvar hasIPv4, hasIPv6 bool\n\n\tcollect := func(rrs []D.RR) {\n\t\tfor _, rr := range rrs {\n\t\t\thttpsRR, ok := rr.(*D.HTTPS)\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tfor _, kv := range httpsRR.Value {\n\t\t\t\tswitch v := kv.(type) {\n\t\t\t\tcase *D.SVCBAlpn:\n\t\t\t\t\tif len(alpns) == 0 && len(v.Alpn) > 0 {\n\t\t\t\t\t\talpns = append(alpns, v.Alpn...)\n\t\t\t\t\t}\n\t\t\t\tcase *D.SVCBIPv4Hint:\n\t\t\t\t\tif len(v.Hint) > 0 {\n\t\t\t\t\t\thasIPv4 = true\n\t\t\t\t\t}\n\t\t\t\tcase *D.SVCBIPv6Hint:\n\t\t\t\t\tif len(v.Hint) > 0 {\n\t\t\t\t\t\thasIPv6 = true\n\t\t\t\t\t}\n\t\t\t\tcase *D.SVCBECHConfig:\n\t\t\t\t\tif publicName == \"\" && len(v.ECH) > 0 {\n\t\t\t\t\t\tif cfgs, err := echparser.ParseECHConfigList(v.ECH); err == nil && len(cfgs) > 0 {\n\t\t\t\t\t\t\tpublicName = string(cfgs[0].PublicName)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tcollect(msg.Answer)\n\n\t//TODO: Do we need to process the data in msg.Extra?\n\t//      If so, do we need to validate whether the domain names within it match our request?\n\t//      To simplify the problem, let's ignore it for now.\n\t//collect(msg.Extra)\n\n\tif len(alpns) == 0 && publicName == \"\" && !hasIPv4 && !hasIPv6 {\n\t\treturn \"\"\n\t}\n\n\tvar parts []string\n\tif len(alpns) > 0 {\n\t\tparts = append(parts, \"alpn:\"+strings.Join(alpns, \",\"))\n\t}\n\tif publicName != \"\" {\n\t\tparts = append(parts, \"pn:\"+publicName)\n\t}\n\tif hasIPv4 {\n\t\tparts = append(parts, \"ipv4hint\")\n\t}\n\tif hasIPv6 {\n\t\tparts = append(parts, \"ipv6hint\")\n\t}\n\n\treturn strings.Join(parts, \";\")\n}\n\nfunc msgToLogString(msg *D.Msg) string {\n\tqType, qTypeStr := msgToQtype(msg)\n\tswitch qType {\n\tcase D.TypeHTTPS:\n\t\treturn fmt.Sprintf(\"[%s] %s\", msgToHTTPSRRInfo(msg), qTypeStr)\n\tdefault:\n\t\treturn fmt.Sprintf(\"%s %s\", msgToIP(msg), qTypeStr)\n\t}\n}\n\nfunc batchExchange(ctx context.Context, clients []dnsClient, m *D.Msg) (msg *D.Msg, cache bool, err error) {\n\tcache = true\n\tfast, ctx := picker.WithTimeout[*D.Msg](ctx, resolver.DefaultDNSTimeout)\n\tdefer fast.Close()\n\tdomain := msgToDomain(m)\n\t_, qTypeStr := msgToQtype(m)\n\tfor _, client := range clients {\n\t\tif _, isRCodeClient := client.(rcodeClient); isRCodeClient {\n\t\t\tmsg, err = client.ExchangeContext(ctx, m)\n\t\t\treturn msg, false, err\n\t\t}\n\t\tclient := client // shadow define client to ensure the value captured by the closure will not be changed in the next loop\n\t\tfast.Go(func() (*D.Msg, error) {\n\t\t\tlog.Debugln(\"[DNS] resolve %s %s from %s\", domain, qTypeStr, client.Address())\n\t\t\tm, err := client.ExchangeContext(ctx, m)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t} else if cache && (m.Rcode == D.RcodeServerFailure || m.Rcode == D.RcodeRefused) {\n\t\t\t\t// currently, cache indicates whether this msg was from a RCode client,\n\t\t\t\t// so we would ignore RCode errors from RCode clients.\n\t\t\t\treturn nil, errors.New(\"server failure: \" + D.RcodeToString[m.Rcode])\n\t\t\t}\n\t\t\tlog.Debugln(\"[DNS] %s --> %s from %s\", domain, msgToLogString(m), client.Address())\n\t\t\treturn m, nil\n\t\t})\n\t}\n\n\tmsg = fast.Wait()\n\tif msg == nil {\n\t\terr = errors.New(\"all DNS requests failed\")\n\t\tif fErr := fast.Error(); fErr != nil {\n\t\t\terr = fmt.Errorf(\"%w, first error: %w\", err, fErr)\n\t\t}\n\t}\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/docker/file-name.sh",
    "content": "#!/bin/sh\nos=\"mihomo-linux-\"\ncase $TARGETPLATFORM in\n    \"linux/amd64\")\n        arch=\"amd64-v1\"\n        ;;\n    \"linux/386\")\n        arch=\"386\"\n        ;;\n    \"linux/arm64\")\n        arch=\"arm64\"\n        ;;\n    \"linux/arm/v7\")\n        arch=\"armv7\"\n        ;;\n    \"linux/riscv64\")\n        arch=\"riscv64\"\n        ;;\n    *)\n        echo \"Unknown architecture\"\n        exit 1\n        ;;        \nesac\nfile_name=\"$os$arch-$(cat bin/version.txt)\"\necho $file_name\n"
  },
  {
    "path": "core/Clash.Meta/docs/config.yaml",
    "content": "# port: 7890 # HTTP(S) 代理服务器端口\n# socks-port: 7891 # SOCKS5 代理端口\nmixed-port: 10801 # HTTP(S) 和 SOCKS 代理混合端口\n# redir-port: 7892 # 透明代理端口，用于 Linux 和 MacOS\n\n# Transparent proxy server port for Linux (TProxy TCP and TProxy UDP)\n# tproxy-port: 7893\n\nallow-lan: true # 允许局域网连接\nbind-address: \"*\" # 绑定 IP 地址，仅作用于 allow-lan 为 true，'*'表示所有地址\nauthentication: # http,socks 入口的验证用户名，密码\n  - \"username:password\"\nskip-auth-prefixes: # 设置跳过验证的 IP 段\n  - 127.0.0.1/8\n  - ::1/128\nlan-allowed-ips: # 允许连接的 IP 地址段，仅作用于 allow-lan 为 true, 默认值为 0.0.0.0/0 和::/0\n  - 0.0.0.0/0\n  - ::/0\nlan-disallowed-ips: # 禁止连接的 IP 地址段，黑名单优先级高于白名单，默认值为空\n  - 192.168.0.3/32\n\n#  find-process-mode has 3 values:always, strict, off\n#  - always, 开启，强制匹配所有进程\n#  - strict, 默认，由 mihomo 判断是否开启\n#  - off, 不匹配进程，推荐在路由器上使用此模式\nfind-process-mode: strict\n\nmode: rule\n\n#自定义 geodata url\ngeox-url:\n  geoip: \"https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.dat\"\n  geosite: \"https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geosite.dat\"\n  mmdb: \"https://fastly.jsdelivr.net/gh/MetaCubeX/meta-rules-dat@release/geoip.metadb\"\n\ngeo-auto-update: false # 是否自动更新 geodata\ngeo-update-interval: 24 # 更新间隔，单位：小时\n\n# Matcher implementation used by GeoSite, available implementations:\n# - succinct (default, same as rule-set)\n# - mph (from V2Ray, also `hybrid` in Xray)\n# geosite-matcher: succinct\n\nlog-level: debug # 日志等级 silent/error/warning/info/debug\n\nipv6: true # 开启 IPv6 总开关，关闭阻断所有 IPv6 链接和屏蔽 DNS 请求 AAAA 记录\n\ntls:\n  certificate: string # 证书 PEM 格式，或者 证书的路径\n  private-key: string # 证书对应的私钥 PEM 格式，或者私钥路径\n  # 下面两项为mTLS配置项，如果client-auth-type设置为 \"verify-if-given\" 或 \"require-and-verify\" 则client-auth-cert必须不为空\n  # client-auth-type: \"\" # 可选值：\"\"、\"request\"、\"require-any\"、\"verify-if-given\"、\"require-and-verify\"\n  # client-auth-cert: string # 证书 PEM 格式，或者 证书的路径\n  # 如果填写则开启ech（可由 mihomo generate ech-keypair <明文域名> 生成）\n  # ech-key: |\n  #   -----BEGIN ECH KEYS-----\n  #   ACATwY30o/RKgD6hgeQxwrSiApLaCgU+HKh7B6SUrAHaDwBD/g0APwAAIAAgHjzK\n  #   madSJjYQIf9o1N5GXjkW4DEEeb17qMxHdwMdNnwADAABAAEAAQACAAEAAwAIdGVz\n  #   dC5jb20AAA==\n  #   -----END ECH KEYS-----\n  custom-certifactes:\n    - |\n      -----BEGIN CERTIFICATE-----\n      format/pem...\n      -----END CERTIFICATE-----\n\nexternal-controller: 0.0.0.0:9093 # RESTful API 监听地址\nexternal-controller-tls: 0.0.0.0:9443 # RESTful API HTTPS 监听地址，需要配置 tls 部分配置文件\n# secret: \"123456\" # `Authorization:Bearer ${secret}`\n\n# RESTful API CORS标头配置\nexternal-controller-cors:\n  allow-origins:\n    - \"*\"\n  allow-private-network: true\n\n# RESTful API Unix socket 监听地址（ windows版本大于17063也可以使用，即大于等于1803/RS4版本即可使用 ）\n# ！！！注意： 从Unix socket访问api接口不会验证secret， 如果开启请自行保证安全问题 ！！！\n# 测试方法： curl -v --unix-socket \"mihomo.sock\" http://localhost/\nexternal-controller-unix: mihomo.sock\n\n# RESTful API Windows namedpipe 监听地址\n# ！！！注意： 从Windows namedpipe访问api接口不会验证secret， 如果开启请自行保证安全问题 ！！！\nexternal-controller-pipe: \\\\.\\pipe\\mihomo\n\n# tcp-concurrent: true # TCP 并发连接所有 IP, 将使用最快握手的 TCP\n\n# 配置 WEB UI 目录，使用 http://{{external-controller}}/ui 访问\nexternal-ui: /path/to/ui/folder/\nexternal-ui-name: xd\n# 目前支持下载zip,tgz格式的压缩包\nexternal-ui-url: \"https://github.com/MetaCubeX/metacubexd/archive/refs/heads/gh-pages.zip\"\n\n# 在RESTful API端口上开启DOH服务器\n# ！！！该URL不会验证secret， 如果开启请自行保证安全问题 ！！！\nexternal-doh-server: /dns-query\n\n# interface-name: en0 # 设置出口网卡\n\n#  TCP keep alive interval\n# disable-keep-alive: false #目前在android端强制为true\n# keep-alive-idle: 15\n# keep-alive-interval: 15\n\n# routing-mark:6666 # 配置 fwmark 仅用于 Linux\nexperimental:\n  # Disable quic-go GSO support. This may result in reduced performance on Linux.\n  # This is not recommended for most users.\n  # Only users encountering issues with quic-go's internal implementation should enable this,\n  # and they should disable it as soon as the issue is resolved.\n  # This field will be removed when quic-go fixes all their issues in GSO.\n  # This equivalent to the environment variable QUIC_GO_DISABLE_GSO=1.\n  #quic-go-disable-gso: true\n\n# 类似于 /etc/hosts, 仅支持配置单个 IP\nhosts:\n# '*.mihomo.dev': 127.0.0.1\n# '.dev': 127.0.0.1\n# 'alpha.mihomo.dev': '::1'\n# test.com: [1.1.1.1, 2.2.2.2]\n# home.lan: lan # lan 为特别字段，将加入本地所有网卡的地址\n# baidu.com: google.com # 只允许配置一个别名\n\nprofile: # 存储 select 选择记录\n  store-selected: false\n\n  # 持久化 fake-ip\n  store-fake-ip: true\n\n# Tun 配置\ntun:\n  enable: false\n  stack: system # gvisor/mixed\n  dns-hijack:\n    - 0.0.0.0:53 # 需要劫持的 DNS\n  # auto-detect-interface: true # 自动识别出口网卡\n  # auto-route: true # 配置路由表\n  # mtu: 9000 # 最大传输单元\n  # gso: false # 启用通用分段卸载，仅支持 Linux\n  # gso-max-size: 65536 # 通用分段卸载包的最大大小\n  auto-redirect: false # 自动配置 iptables 以重定向 TCP 连接。仅支持 Linux。带有 auto-redirect 的 auto-route 现在可以在路由器上按预期工作，无需干预。\n  # strict-route: true # 将所有连接路由到 tun 来防止泄漏，但你的设备将无法其他设备被访问\n  # disable-icmp-forwarding: true # 禁用 ICMP 转发，防止某些情况下的 ICMP 环回问题，ping 将不会显示真实的延迟\n  route-address-set: # 将指定规则集中的目标 IP CIDR 规则添加到防火墙, 不匹配的流量将绕过路由, 仅支持 Linux，且需要 nftables，`auto-route` 和 `auto-redirect` 已启用。\n    - ruleset-1\n    - ruleset-2\n  route-exclude-address-set: # 将指定规则集中的目标 IP CIDR 规则添加到防火墙, 匹配的流量将绕过路由, 仅支持 Linux，且需要 nftables，`auto-route` 和 `auto-redirect` 已启用。\n    - ruleset-3\n    - ruleset-4\n  route-address: # 启用 auto-route 时使用自定义路由而不是默认路由\n    - 0.0.0.0/1\n    - 128.0.0.0/1\n    - \"::/1\"\n    - \"8000::/1\"\n  # inet4-route-address: # 启用 auto-route 时使用自定义路由而不是默认路由（旧写法）\n  #   - 0.0.0.0/1\n  #   - 128.0.0.0/1\n  # inet6-route-address: # 启用 auto-route 时使用自定义路由而不是默认路由（旧写法）\n  #   - \"::/1\"\n  #   - \"8000::/1\"\n  # endpoint-independent-nat: false # 启用独立于端点的 NAT\n  # include-interface: # 限制被路由的接口。默认不限制，与 `exclude-interface` 冲突\n  #   - \"lan0\"\n  # exclude-interface: # 排除路由的接口，与 `include-interface` 冲突\n  #   - \"lan1\"\n  # include-uid: # UID 规则仅在 Linux 下被支持，并且需要 auto-route\n  # - 0\n  # include-uid-range: # 限制被路由的的用户范围\n  # - 1000:9999\n  # exclude-uid: # 排除路由的的用户\n  #- 1000\n  # exclude-uid-range: # 排除路由的的用户范围\n  # - 1000:9999\n  # include-mac-address:\n  # - 00:11:22:33:44:55\n  # exclude-mac-address:\n  # - 00:11:22:33:44:55\n\n  # Android 用户和应用规则仅在 Android 下被支持\n  # 并且需要 auto-route\n\n  # include-android-user: # 限制被路由的 Android 用户\n  # - 0\n  # - 10\n  # include-package: # 限制被路由的 Android 应用包名\n  # - com.android.chrome\n  # exclude-package: # 排除被路由的 Android 应用包名\n  # - com.android.captiveportallogin\n\n# 嗅探域名 可选配置\nsniffer:\n  enable: false\n  ## 对 redir-host 类型识别的流量进行强制嗅探\n  ## 如：Tun、Redir 和 TProxy 并 DNS 为 redir-host 皆属于\n  # force-dns-mapping: false\n  ## 对所有未获取到域名的流量进行强制嗅探\n  # parse-pure-ip: false\n  # 是否使用嗅探结果作为实际访问，默认 true\n  # 全局配置，优先级低于 sniffer.sniff 实际配置\n  override-destination: false\n  sniff: # TLS 和 QUIC 默认如果不配置 ports 默认嗅探 443\n    QUIC:\n    #  ports: [ 443 ]\n    TLS:\n    #  ports: [443, 8443]\n\n    # 默认嗅探 80\n    HTTP: # 需要嗅探的端口\n      ports: [80, 8080-8880]\n      # 可覆盖 sniffer.override-destination\n      override-destination: true\n  force-domain:\n    - +.v2ex.com\n  # skip-src-address: # 对于来源ip跳过嗅探\n  #   - 192.168.0.3/32\n  # skip-dst-address: # 对于目标ip跳过嗅探\n  #   - 192.168.0.3/32\n  ## 对嗅探结果进行跳过\n  # skip-domain:\n  #   - Mijia Cloud\n  # 需要嗅探协议\n  # 已废弃，若 sniffer.sniff 配置则此项无效\n  sniffing:\n    - tls\n    - http\n  # 强制对此域名进行嗅探\n\n  # 仅对白名单中的端口进行嗅探，默认为 443，80\n  # 已废弃，若 sniffer.sniff 配置则此项无效\n  port-whitelist:\n    - \"80\"\n    - \"443\"\n    # - 8000-9999\n\ntunnels: # one line config\n  - tcp/udp,127.0.0.1:6553,114.114.114.114:53,proxy\n  - tcp,127.0.0.1:6666,rds.mysql.com:3306,vpn\n  # full yaml config\n  - network: [tcp, udp]\n    address: 127.0.0.1:7777\n    target: target.com\n    proxy: proxy\n\n# DNS 配置\ndns:\n  cache-algorithm: arc\n  enable: false # 关闭将使用系统 DNS\n  prefer-h3: false # 是否开启 DoH 支持 HTTP/3，将并发尝试\n  listen: 0.0.0.0:53 # 开启 DNS 服务器监听\n  # ipv6: false # false 将返回 AAAA 的空结果\n  # ipv6-timeout: 300 # 单位：ms，内部双栈并发时，向上游查询 AAAA 时，等待 AAAA 的时间，默认 100ms\n  # 用于解析 nameserver，fallback 以及其他 DNS 服务器配置的，DNS 服务域名\n  # 只能使用纯 IP 地址，可使用加密 DNS\n  default-nameserver:\n    - 114.114.114.114\n    - 8.8.8.8\n    - tls://1.12.12.12:853\n    - tls://223.5.5.5:853\n    - system # append DNS server from system configuration. If not found, it would print an error log and skip.\n  enhanced-mode: fake-ip # or redir-host\n\n  fake-ip-range: 198.18.0.1/16 # fake-ip 池设置\n  # fake-ip-range6: fdfe:dcba:9876::1/64 # fake-ip6 池设置\n\n  # 配置不使用 fake-ip 的域名\n  fake-ip-filter:\n    - '*.lan'\n    - localhost.ptlogin2.qq.com\n    # fakeip-filter 为 rule-providers 中的名为 fakeip-filter 规则订阅，\n    # 且 behavior 必须为 domain/classical，当为 classical 时仅会生效域名类规则\n    - rule-set:fakeip-filter\n    # fakeip-filter 为 geosite 中名为 fakeip-filter 的分类（需要自行保证该分类存在）\n    - geosite:fakeip-filter\n\n    # 当 fake-ip-filter-mode: rule 时开启规则模式\n    # fake-ip 与路由 rules 匹配逻辑一致(自上而下)，语法也一致，支持GEOSITE、RuleSet、DOMAIN*、MATCH\n    - RULE-SET,reject-domain,fake-ip # 自定义 RuleSet behavior 必须为 domain/classical，当为 classical 时仅会生效域名类规则\n    - RULE-SET,proxy-domain,fake-ip\n    - GEOSITE,gfw,fake-ip\n    - DOMAIN,www.baidu.com,real-ip\n    - DOMAIN-SUFFIX,qq.com,real-ip\n    - DOMAIN-SUFFIX,jd.com,fake-ip\n    - MATCH,fake-ip # 最后 fake-ip or real-ip\n\n  # 配置fake-ip-filter的匹配模式，默认为blacklist，即如果匹配成功不返回fake-ip\n  # 可设置为whitelist，即只有匹配成功才返回fake-ip\n  # 也可配置为rule，规则模式语法见fake-ip-filter说明\n  fake-ip-filter-mode: blacklist\n  # 配置fakeip查询返回的TTL，非必要情况下请勿修改\n  fake-ip-ttl: 1\n\n  # use-hosts: true # 查询 hosts\n\n  # 配置后面的nameserver、fallback和nameserver-policy向dns服务器的连接过程是否遵守遵守rules规则\n  # 如果为false（默认值）则这三部分的dns服务器在未特别指定的情况下会直连\n  # 如果为true，将会按照rules的规则匹配链接方式（走代理或直连），如果有特别指定则任然以指定值为准\n  # 仅当proxy-server-nameserver非空时可以开启此选项, 强烈不建议和prefer-h3一起使用\n  # 此外，这三者配置中的dns服务器如果出现域名会采用default-nameserver配置项解析，也请确保正确配置default-nameserver\n  respect-rules: false\n\n  # DNS 主要域名配置\n  # 支持 UDP，TCP，DoT，DoH，DoQ\n  # 这部分为主要 DNS 配置，影响所有直连，确保使用对大陆解析精准的 DNS\n  nameserver:\n    - 114.114.114.114 # default value\n    - 8.8.8.8 # default value\n    - tls://223.5.5.5:853 # DNS over TLS\n    - https://doh.pub/dns-query # DNS over HTTPS\n    - https://dns.alidns.com/dns-query#h3=true # 强制 HTTP/3，与 perfer-h3 无关，强制开启 DoH 的 HTTP/3 支持，若不支持将无法使用\n    - https://mozilla.cloudflare-dns.com/dns-query#DNS&h3=true # 指定策略组和使用 HTTP/3\n    - dhcp://en0 # dns from dhcp\n    - quic://dns.adguard.com:784 # DNS over QUIC\n    # - '8.8.8.8#RULES' # 效果同respect-rules，但仅对该服务器生效\n    # - '8.8.8.8#en0' # 兼容指定 DNS 出口网卡\n\n  # 当配置 fallback 时，会查询 nameserver 中返回的 IP 是否为 CN，非必要配置\n  # 当不是 CN，则使用 fallback 中的 DNS 查询结果\n  # 确保配置 fallback 时能够正常查询\n  # fallback:\n  #   - tcp://1.1.1.1\n  #   - 'tcp://1.1.1.1#ProxyGroupName' # 指定 DNS 过代理查询，ProxyGroupName 为策略组名或节点名，过代理配置优先于配置出口网卡，当找不到策略组或节点名则设置为出口网卡\n\n  # 专用于节点域名解析的 DNS 服务器，非必要配置项，如果不填则遵循nameserver-policy、nameserver和fallback的配置\n  # proxy-server-nameserver:\n  #   - https://doh.pub/dns-query\n  #   - tls://223.5.5.5:853\n  # proxy-server-nameserver-policy: # 格式同nameserver-policy，仅用于节点域名解析，当且仅当proxy-server-nameserver不为空时生效\n  #   'www.yournode.com': '114.114.114.114'\n\n  # 专用于direct出口域名解析的 DNS 服务器，非必要配置项，如果不填则遵循nameserver-policy、nameserver和fallback的配置\n  # direct-nameserver:\n  #   - system://\n  # direct-nameserver-follow-policy: false # 是否遵循nameserver-policy，默认为不遵守，仅当direct-nameserver不为空时生效\n\n  # 配置 fallback 使用条件\n  # fallback-filter:\n  #   geoip: true # 配置是否使用 geoip\n  #   geoip-code: CN # 当 nameserver 域名的 IP 查询 geoip 库为 CN 时，不使用 fallback 中的 DNS 查询结果\n  #   配置强制 fallback，优先于 IP 判断，具体分类自行查看 geosite 库\n  #   geosite:\n  #     - gfw\n  #   如果不匹配 ipcidr 则使用 nameservers 中的结果\n  #   ipcidr:\n  #     - 240.0.0.0/4\n  #   domain:\n  #     - '+.google.com'\n  #     - '+.facebook.com'\n  #     - '+.youtube.com'\n\n  # 配置查询域名使用的 DNS 服务器\n  nameserver-policy:\n    #   'www.baidu.com': '114.114.114.114'\n    #   '+.internal.crop.com': '10.0.0.1'\n    \"geosite:cn,private,apple\":\n      - https://doh.pub/dns-query\n      - https://dns.alidns.com/dns-query\n    \"geosite:category-ads-all\": rcode://success\n    \"www.baidu.com,+.google.cn\": [223.5.5.5, https://dns.alidns.com/dns-query]\n    ## global，dns 为 rule-providers 中的名为 global 和 dns 规则订阅，\n    ## 且 behavior 必须为 domain/classical，当为 classical 时仅会生效域名类规则\n    # \"rule-set:global,dns\": 8.8.8.8\n\nproxies: # socks5\n  - name: \"socks\"\n    type: socks5\n    server: server\n    port: 443\n    # username: username\n    # password: password\n    # tls: true\n    # fingerprint: xxxx # 配置指纹将实现 SSL Pining 效果, 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取\n    # 下面两项如果填写则开启 mTLS（需要同时填写）\n    # certificate: ./client.crt # 证书 PEM 格式，或者 证书的路径\n    # private-key: ./client.key # 证书对应的私钥 PEM 格式，或者私钥路径\n    # skip-cert-verify: true\n    # udp: true\n    # ip-version: ipv6\n\n  # http\n  - name: \"http\"\n    type: http\n    server: server\n    port: 443\n    # username: username\n    # password: password\n    # tls: true # https\n    # skip-cert-verify: true\n    # sni: custom.com\n    # fingerprint: xxxx # 配置指纹将实现 SSL Pining 效果, 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取\n    # 下面两项如果填写则开启 mTLS（需要同时填写）\n    # certificate: ./client.crt # 证书 PEM 格式，或者 证书的路径\n    # private-key: ./client.key # 证书对应的私钥 PEM 格式，或者私钥路径\n    # ip-version: dual\n\n  # Snell\n  # Beware that there's currently no UDP support yet\n  - name: \"snell\"\n    type: snell\n    server: server\n    port: 44046\n    psk: yourpsk\n    # version: 2\n    # obfs-opts:\n    # mode: http # or tls\n    # host: bing.com\n\n  # Shadowsocks\n  # cipher支持:\n  #   aes-128-gcm aes-192-gcm aes-256-gcm\n  #   aes-128-cfb aes-192-cfb aes-256-cfb\n  #   aes-128-ctr aes-192-ctr aes-256-ctr\n  #   rc4-md5 chacha20-ietf xchacha20\n  #   chacha20-ietf-poly1305 xchacha20-ietf-poly1305\n  #   2022-blake3-aes-128-gcm 2022-blake3-aes-256-gcm 2022-blake3-chacha20-poly1305\n  - name: \"ss1\"\n    type: ss\n    server: server\n    port: 443\n    cipher: chacha20-ietf-poly1305\n    password: \"password\"\n    # udp: true\n    # udp-over-tcp: false\n    # ip-version: ipv4 # 设置节点使用 IP 版本，可选：dual，ipv4，ipv6，ipv4-prefer，ipv6-prefer。默认使用 dual\n    # ipv4：仅使用 IPv4  ipv6：仅使用 IPv6\n    # ipv4-prefer：优先使用 IPv4 对于 TCP 会进行双栈解析，并发链接但是优先使用 IPv4 链接，\n    # UDP 则为双栈解析，获取结果中的第一个 IPv4\n    # ipv6-prefer 同 ipv4-prefer\n    # 现有协议都支持此参数，TCP 效果仅在开启 tcp-concurrent 生效\n    smux:\n      enabled: false\n      protocol: smux # smux/yamux/h2mux\n      # max-connections: 4 # Maximum connections. Conflict with max-streams.\n      # min-streams: 4 # Minimum multiplexed streams in a connection before opening a new connection. Conflict with max-streams.\n      # max-streams: 0 # Maximum multiplexed streams in a connection before opening a new connection. Conflict with max-connections and min-streams.\n      # padding: false # Enable padding. Requires sing-box server version 1.3-beta9 or later.\n      # statistic: false # 控制是否将底层连接显示在面板中，方便打断底层连接\n      # only-tcp: false # 如果设置为 true, smux 的设置将不会对 udp 生效，udp 连接会直接走底层协议\n\n  - name: \"ss2\"\n    type: ss\n    server: server\n    port: 443\n    cipher: chacha20-ietf-poly1305\n    password: \"password\"\n    plugin: obfs\n    plugin-opts:\n      mode: tls # or http\n      # host: bing.com\n\n  - name: \"ss3\"\n    type: ss\n    server: server\n    port: 443\n    cipher: chacha20-ietf-poly1305\n    password: \"password\"\n    plugin: v2ray-plugin\n    plugin-opts:\n      mode: websocket # no QUIC now\n      # tls: true # wss\n      # fingerprint: xxxx # 配置指纹将实现 SSL Pining 效果, 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取\n      # 下面两项如果填写则开启 mTLS（需要同时填写）\n      # certificate: ./client.crt # 证书 PEM 格式，或者 证书的路径\n      # private-key: ./client.key # 证书对应的私钥 PEM 格式，或者私钥路径\n      # ech-opts:\n      #   enable: true # 必须手动开启\n      #   # 如果config为空则通过dns解析，不为空则通过该值指定，格式为经过base64编码的ech参数（dig +short TYPE65 tls-ech.dev）\n      #   config: AEn+DQBFKwAgACABWIHUGj4u+PIggYXcR5JF0gYk3dCRioBW8uJq9H4mKAAIAAEAAQABAANAEnB1YmxpYy50bHMtZWNoLmRldgAA\n      #   # query-server-name: xxx.com # 可选项，不为空时用于指定通过dns解析时的域名\n      # skip-cert-verify: true\n      # host: bing.com\n      # path: \"/\"\n      # mux: true\n      # headers:\n      #   custom: value\n      # v2ray-http-upgrade: false\n      # v2ray-http-upgrade-fast-open: false\n\n  - name: \"ss4-shadow-tls\"\n    type: ss\n    server: server\n    port: 443\n    cipher: chacha20-ietf-poly1305\n    password: \"password\"\n    plugin: shadow-tls\n    client-fingerprint: chrome\n    plugin-opts:\n      host: \"cloud.tencent.com\"\n      password: \"shadow_tls_password\"\n      version: 2 # support 1/2/3\n      # alpn: [\"h2\",\"http/1.1\"]\n\n  - name: \"ss5\"\n    type: ss\n    server: server\n    port: 443\n    cipher: chacha20-ietf-poly1305\n    password: \"password\"\n    plugin: gost-plugin\n    plugin-opts:\n      mode: websocket\n      # tls: true # wss\n      # fingerprint: xxxx # 配置指纹将实现 SSL Pining 效果, 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取\n      # 下面两项如果填写则开启 mTLS（需要同时填写）\n      # certificate: ./client.crt # 证书 PEM 格式，或者 证书的路径\n      # private-key: ./client.key # 证书对应的私钥 PEM 格式，或者私钥路径\n      # skip-cert-verify: true\n      # host: bing.com\n      # path: \"/\"\n      # mux: true\n      # headers:\n      #   custom: value\n\n  - name: \"ss-restls-tls13\"\n    type: ss\n    server: [YOUR_SERVER_IP]\n    port: 443\n    cipher: chacha20-ietf-poly1305\n    password: [YOUR_SS_PASSWORD]\n    client-fingerprint:\n      chrome # One of: chrome, ios, firefox or safari\n      # 可以是 chrome, ios, firefox, safari 中的一个\n    plugin: restls\n    plugin-opts:\n      host:\n        \"www.microsoft.com\" # Must be a TLS 1.3 server\n        # 应当是一个 TLS 1.3 服务器\n      password: [YOUR_RESTLS_PASSWORD]\n      version-hint: \"tls13\"\n      # Control your post-handshake traffic through restls-script\n      # Hide proxy behaviors like \"tls in tls\".\n      # see https://github.com/3andne/restls/blob/main/Restls-Script:%20Hide%20Your%20Proxy%20Traffic%20Behavior.md\n      # 用 restls 剧本来控制握手后的行为，隐藏\"tls in tls\"等特征\n      # 详情：https://github.com/3andne/restls/blob/main/Restls-Script:%20%E9%9A%90%E8%97%8F%E4%BD%A0%E7%9A%84%E4%BB%A3%E7%90%86%E8%A1%8C%E4%B8%BA.md\n      restls-script: \"300?100<1,400~100,350~100,600~100,300~200,300~100\"\n\n  - name: \"ss-restls-tls12\"\n    type: ss\n    server: [YOUR_SERVER_IP]\n    port: 443\n    cipher: chacha20-ietf-poly1305\n    password: [YOUR_SS_PASSWORD]\n    client-fingerprint:\n      chrome # One of: chrome, ios, firefox or safari\n      # 可以是 chrome, ios, firefox, safari 中的一个\n    plugin: restls\n    plugin-opts:\n      host:\n        \"vscode.dev\" # Must be a TLS 1.2 server\n        # 应当是一个 TLS 1.2 服务器\n      password: [YOUR_RESTLS_PASSWORD]\n      version-hint: \"tls12\"\n      restls-script: \"1000?100<1,500~100,350~100,600~100,400~200\"\n\n  - name: \"ss-kcptun\"\n    type: ss\n    server: [YOUR_SERVER_IP]\n    port: 443\n    cipher: chacha20-ietf-poly1305\n    password: [YOUR_SS_PASSWORD]\n    plugin: kcptun\n    plugin-opts:\n      key: it's a secrect # pre-shared secret between client and server\n      crypt: aes # aes, aes-128, aes-128-gcm, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, none, null\n      mode: fast # profiles: fast3, fast2, fast, normal, manual\n      conn: 1 # set num of UDP connections to server\n      autoexpire: 0 # set auto expiration time(in seconds) for a single UDP connection, 0 to disable\n      scavengettl: 600 # set how long an expired connection can live (in seconds)\n      mtu: 1350 # set maximum transmission unit for UDP packets\n      ratelimit: 0 # set maximum outgoing speed (in bytes per second) for a single KCP connection, 0 to disable. Also known as packet pacing\n      sndwnd: 128 # set send window size(num of packets)\n      rcvwnd: 512 # set receive window size(num of packets)\n      datashard: 10 # set reed-solomon erasure coding - datashard\n      parityshard: 3 # set reed-solomon erasure coding - parityshard\n      dscp: 0 # set DSCP(6bit)\n      nocomp: false # disable compression\n      acknodelay: false # flush ack immediately when a packet is received\n      nodelay: 0\n      interval: 50\n      resend: 0\n      sockbuf: 4194304 # per-socket buffer in bytes\n      smuxver: 1 # specify smux version, available 1,2\n      smuxbuf: 4194304 # the overall de-mux buffer in bytes\n      framesize: 8192 # smux max frame size\n      streambuf: 2097152 # per stream receive buffer in bytes, smux v2+\n      keepalive: 10 # seconds between heartbeats\n\n  # vmess\n  # cipher 支持 auto/aes-128-gcm/chacha20-poly1305/none\n  - name: \"vmess\"\n    type: vmess\n    server: server\n    port: 443\n    uuid: uuid\n    alterId: 32\n    cipher: auto\n    # udp: true\n    # tls: true\n    # fingerprint: xxxx # 配置指纹将实现 SSL Pining 效果, 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取\n    # 下面两项如果填写则开启 mTLS（需要同时填写）\n    # certificate: ./client.crt # 证书 PEM 格式，或者 证书的路径\n    # private-key: ./client.key # 证书对应的私钥 PEM 格式，或者私钥路径\n    # client-fingerprint: chrome    # Available: \"chrome\",\"firefox\",\"safari\",\"ios\",\"random\", currently only support TLS transport in TCP/GRPC/WS/HTTP for VLESS/Vmess and trojan.\n    # skip-cert-verify: true\n    # servername: example.com # priority over wss host\n    # network: ws\n    # ech-opts:\n    #   enable: true # 必须手动开启\n    #   # 如果config为空则通过dns解析，不为空则通过该值指定，格式为经过base64编码的ech参数（dig +short TYPE65 tls-ech.dev）\n    #   config: AEn+DQBFKwAgACABWIHUGj4u+PIggYXcR5JF0gYk3dCRioBW8uJq9H4mKAAIAAEAAQABAANAEnB1YmxpYy50bHMtZWNoLmRldgAA\n    #   # query-server-name: xxx.com # 可选项，不为空时用于指定通过dns解析时的域名\n    # ws-opts:\n      # path: /path\n      # headers:\n      #   Host: v2ray.com\n      # max-early-data: 2048\n      # early-data-header-name: Sec-WebSocket-Protocol\n      # v2ray-http-upgrade: false\n      # v2ray-http-upgrade-fast-open: false\n\n  - name: \"vmess-h2\"\n    type: vmess\n    server: server\n    port: 443\n    uuid: uuid\n    alterId: 32\n    cipher: auto\n    network: h2\n    tls: true\n    # fingerprint: xxxx # 配置指纹将实现 SSL Pining 效果, 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取\n    # 下面两项如果填写则开启 mTLS（需要同时填写）\n    # certificate: ./client.crt # 证书 PEM 格式，或者 证书的路径\n    # private-key: ./client.key # 证书对应的私钥 PEM 格式，或者私钥路径\n    h2-opts:\n      host:\n        - http.example.com\n        - http-alt.example.com\n      path: /\n\n  - name: \"vmess-http\"\n    type: vmess\n    server: server\n    port: 443\n    uuid: uuid\n    alterId: 32\n    cipher: auto\n    # udp: true\n    # network: http\n    # http-opts:\n    #   method: \"GET\"\n    #   path:\n    #     - '/'\n    #     - '/video'\n    #   headers:\n    #     Connection:\n    #       - keep-alive\n    # ip-version: ipv4 # 设置使用 IP 类型偏好，可选：ipv4，ipv6，dual，默认值：dual\n\n  - name: vmess-grpc\n    server: server\n    port: 443\n    type: vmess\n    uuid: uuid\n    alterId: 32\n    cipher: auto\n    network: grpc\n    tls: true\n    # fingerprint: xxxx # 配置指纹将实现 SSL Pining 效果, 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取\n    # 下面两项如果填写则开启 mTLS（需要同时填写）\n    # certificate: ./client.crt # 证书 PEM 格式，或者 证书的路径\n    # private-key: ./client.key # 证书对应的私钥 PEM 格式，或者私钥路径\n    servername: example.com\n    # skip-cert-verify: true\n    grpc-opts:\n      grpc-service-name: \"example\"\n      # grpc-user-agent: \"grpc-go/1.36.0\"\n      # ping-interval: 0 # 默认关闭，单位为秒\n      # max-connections: 1 # Maximum connections. Conflict with max-streams.\n      # min-streams: 0 # Minimum multiplexed streams in a connection before opening a new connection. Conflict with max-streams.\n      # max-streams: 0 # Maximum multiplexed streams in a connection before opening a new connection. Conflict with max-connections and min-streams.\n    # ip-version: ipv4\n\n  # vless\n  - name: \"vless-tcp\"\n    type: vless\n    server: server\n    port: 443\n    uuid: uuid\n    network: tcp\n    servername: example.com # AKA SNI\n    # skip-cert-verify: true\n    # 下面两项如果填写则开启 mTLS（需要同时填写）\n    # certificate: ./client.crt # 证书 PEM 格式，或者 证书的路径\n    # private-key: ./client.key # 证书对应的私钥 PEM 格式，或者私钥路径\n    # fingerprint: xxxx # 配置指纹将实现 SSL Pining 效果, 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取\n    # client-fingerprint: random # Available: \"chrome\",\"firefox\",\"safari\",\"random\",\"none\"\n    # ech-opts:\n    #   enable: true # 必须手动开启\n    #   # 如果config为空则通过dns解析，不为空则通过该值指定，格式为经过base64编码的ech参数（dig +short TYPE65 tls-ech.dev）\n    #   config: AEn+DQBFKwAgACABWIHUGj4u+PIggYXcR5JF0gYk3dCRioBW8uJq9H4mKAAIAAEAAQABAANAEnB1YmxpYy50bHMtZWNoLmRldgAA\n    #   # query-server-name: xxx.com # 可选项，不为空时用于指定通过dns解析时的域名\n\n  - name: \"vless-vision\"\n    type: vless\n    server: server\n    port: 443\n    uuid: uuid\n    network: tcp\n    tls: true\n    udp: true\n    flow: xtls-rprx-vision\n    client-fingerprint: chrome\n    # 下面两项如果填写则开启 mTLS（需要同时填写）\n    # certificate: ./client.crt # 证书 PEM 格式，或者 证书的路径\n    # private-key: ./client.key # 证书对应的私钥 PEM 格式，或者私钥路径\n    # fingerprint: xxxx # 配置指纹将实现 SSL Pining 效果, 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取\n    # skip-cert-verify: true\n\n  - name: \"vless-encryption\"\n    type: vless\n    server: server\n    port: 443\n    uuid: uuid\n    network: tcp\n    # -------------------------\n    # vless encryption客户端配置：\n    # （native/xorpub 的 XTLS Vision 可以 Splice。只使用 1-RTT 模式 / 若服务端发的 ticket 中秒数不为零则 0-RTT 复用）\n    # / 是只能选一个，后面 base64 至少一个，无限串联，使用  mihomo generate vless-x25519 和 mihomo generate vless-mlkem768 生成，替换值时需去掉括号\n    #\n    # Padding 是可选的参数，仅作用于 1-RTT 以消除握手的长度特征，双端默认值均为 \"100-111-1111.75-0-111.50-0-3333\"：\n    # 在 1-RTT client/server hello 后以 100% 的概率粘上随机 111 到 1111 字节的 padding\n    # 以 75% 的概率等待随机 0 到 111 毫秒（\"probability-from-to\"）\n    # 再次以 50% 的概率发送随机 0 到 3333 字节的 padding（若为 0 则不 Write()）\n    # 服务端、客户端可以设置不同的 padding 参数，按 len、gap 的顺序无限串联，第一个 padding 需概率 100%、至少 35 字节\n    # -------------------------\n    encryption: \"mlkem768x25519plus.native/xorpub/random.1rtt/0rtt.(padding len).(padding gap).(X25519 Password).(ML-KEM-768 Client)...\"\n    tls: false #可以不开启tls\n    udp: true\n\n  - name: \"vless-reality-vision\"\n    type: vless\n    server: server\n    port: 443\n    uuid: uuid\n    network: tcp\n    tls: true\n    udp: true\n    flow: xtls-rprx-vision\n    servername: www.microsoft.com # REALITY servername\n    reality-opts:\n      public-key: xxx\n      short-id: xxx # optional\n      support-x25519mlkem768: false # 如果服务端支持可手动设置为true\n    client-fingerprint: chrome # cannot be empty\n\n  - name: \"vless-reality-grpc\"\n    type: vless\n    server: server\n    port: 443\n    uuid: uuid\n    network: grpc\n    tls: true\n    udp: true\n    flow:\n    # skip-cert-verify: true\n    client-fingerprint: chrome\n    servername: testingcf.jsdelivr.net\n    grpc-opts:\n      grpc-service-name: \"grpc\"\n      # grpc-user-agent: \"grpc-go/1.36.0\"\n      # ping-interval: 0 # 默认关闭，单位为秒\n      # max-connections: 1 # Maximum connections. Conflict with max-streams.\n      # min-streams: 0 # Minimum multiplexed streams in a connection before opening a new connection. Conflict with max-streams.\n      # max-streams: 0 # Maximum multiplexed streams in a connection before opening a new connection. Conflict with max-connections and min-streams.\n      \n    reality-opts:\n      public-key: CrrQSjAG_YkHLwvM2M-7XkKJilgL5upBKCp0od0tLhE\n      short-id: 10f897e26c4b9478\n      support-x25519mlkem768: false # 如果服务端支持可手动设置为true\n\n  - name: \"vless-ws\"\n    type: vless\n    server: server\n    port: 443\n    uuid: uuid\n    udp: true\n    tls: true\n    network: ws\n    # client-fingerprint: random # Available: \"chrome\",\"firefox\",\"safari\",\"random\",\"none\"\n    servername: example.com # priority over wss host\n    # skip-cert-verify: true\n    # fingerprint: xxxx # 配置指纹将实现 SSL Pining 效果, 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取\n    # 下面两项如果填写则开启 mTLS（需要同时填写）\n    # certificate: ./client.crt # 证书 PEM 格式，或者 证书的路径\n    # private-key: ./client.key # 证书对应的私钥 PEM 格式，或者私钥路径\n    ws-opts:\n      path: \"/\"\n      headers:\n        Host: example.com\n      # v2ray-http-upgrade: false\n      # v2ray-http-upgrade-fast-open: false\n\n  - name: \"vless-xhttp\"\n    type: vless\n    server: server\n    port: 443\n    uuid: uuid\n    udp: true\n    tls: true\n    network: xhttp\n    alpn: [h2] # 默认仅支持h2，如果开启h3模式需要设置alpn: [h3]，如果开启http1.1模式需要设置alpn: [http/1.1]\n    # ech-opts: ...\n    # reality-opts: ...\n    # skip-cert-verify: false\n    # fingerprint: ...\n    # certificate: ...\n    # private-key: ...\n    servername: xxx.com\n    client-fingerprint: chrome\n    encryption: \"\"\n    xhttp-opts:\n      path: \"/\"\n      host: xxx.com\n      # mode: \"stream-one\" # Available: \"stream-one\", \"stream-up\" or \"packet-up\"\n      # headers:\n      #   X-Forwarded-For: \"\"\n      # no-grpc-header: false\n      # x-padding-bytes: \"100-1000\"\n      # x-padding-obfs-mode: false\n      # x-padding-key: x_padding\n      # x-padding-header: Referer\n      # x-padding-placement: queryInHeader # Available: queryInHeader, cookie, header, query\n      # x-padding-method: repeat-x # Available: repeat-x, tokenish\n      # uplink-http-method: POST # Available: POST, PUT, PATCH, DELETE\n      # session-placement: path # Available: path, query, cookie, header\n      # session-key: \"\"\n      # seq-placement: path # Available: path, query, cookie, header\n      # seq-key: \"\"\n      # uplink-data-placement: body # Available: body, cookie, header\n      # uplink-data-key: \"\"\n      # uplink-chunk-size: 0 # only applicable when uplink-data-placement is not body\n      # sc-max-each-post-bytes: 1000000\n      # sc-min-posts-interval-ms: 30\n      # reuse-settings: # aka XMUX\n      #   max-concurrency: \"16-32\"\n      #   max-connections: \"0\"\n      #   c-max-reuse-times: \"0\"\n      #   h-max-request-times: \"600-900\"\n      #   h-max-reusable-secs: \"1800-3000\"\n      #   h-keep-alive-period: 0\n      # download-settings:\n      #   ## xhttp part\n      #   path: \"/\"\n      #   host: xxx.com\n      #   headers:\n      #     X-Forwarded-For: \"\"\n      #   reuse-settings: # aka XMUX\n      #     max-concurrency: \"16-32\"\n      #     max-connections: \"0\"\n      #     c-max-reuse-times: \"0\"\n      #     h-max-request-times: \"600-900\"\n      #     h-max-reusable-secs: \"1800-3000\"\n      #     h-keep-alive-period: 0\n      #   ## proxy part\n      #   server: server\n      #   port: 443\n      #   tls: true\n      #   alpn: ...\n      #   ech-opts: ...\n      #   reality-opts: ...\n      #   skip-cert-verify: false\n      #   fingerprint: ...\n      #   certificate: ...\n      #   private-key: ...\n      #   servername: xxx.com\n      #   client-fingerprint: chrome\n\n\n  # Trojan\n  - name: \"trojan\"\n    type: trojan\n    server: server\n    port: 443\n    password: yourpsk\n    # client-fingerprint: random # Available: \"chrome\",\"firefox\",\"safari\",\"random\",\"none\"\n    # fingerprint: xxxx # 配置指纹将实现 SSL Pining 效果, 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取\n    # 下面两项如果填写则开启 mTLS（需要同时填写）\n    # certificate: ./client.crt # 证书 PEM 格式，或者 证书的路径\n    # private-key: ./client.key # 证书对应的私钥 PEM 格式，或者私钥路径\n    # udp: true\n    # sni: example.com # aka server name\n    # alpn:\n    #   - h2\n    #   - http/1.1\n    # skip-cert-verify: true\n    # ss-opts: # like trojan-go's `shadowsocks` config\n    #   enabled: false\n    #   method: aes-128-gcm # aes-128-gcm/aes-256-gcm/chacha20-ietf-poly1305\n    #   password: \"example\"\n    # ech-opts:\n    #   enable: true # 必须手动开启\n    #   # 如果config为空则通过dns解析，不为空则通过该值指定，格式为经过base64编码的ech参数（dig +short TYPE65 tls-ech.dev）\n    #   config: AEn+DQBFKwAgACABWIHUGj4u+PIggYXcR5JF0gYk3dCRioBW8uJq9H4mKAAIAAEAAQABAANAEnB1YmxpYy50bHMtZWNoLmRldgAA\n    #   # query-server-name: xxx.com # 可选项，不为空时用于指定通过dns解析时的域名\n\n  - name: trojan-grpc\n    server: server\n    port: 443\n    type: trojan\n    password: \"example\"\n    network: grpc\n    sni: example.com\n    # skip-cert-verify: true\n    # fingerprint: xxxx # 配置指纹将实现 SSL Pining 效果, 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取\n    # 下面两项如果填写则开启 mTLS（需要同时填写）\n    # certificate: ./client.crt # 证书 PEM 格式，或者 证书的路径\n    # private-key: ./client.key # 证书对应的私钥 PEM 格式，或者私钥路径\n    udp: true\n    grpc-opts:\n      grpc-service-name: \"example\"\n      # grpc-user-agent: \"grpc-go/1.36.0\"\n      # ping-interval: 0 # 默认关闭，单位为秒\n      # max-connections: 1 # Maximum connections. Conflict with max-streams.\n      # min-streams: 0 # Minimum multiplexed streams in a connection before opening a new connection. Conflict with max-streams.\n      # max-streams: 0 # Maximum multiplexed streams in a connection before opening a new connection. Conflict with max-connections and min-streams.\n\n  - name: trojan-ws\n    server: server\n    port: 443\n    type: trojan\n    password: \"example\"\n    network: ws\n    sni: example.com\n    # skip-cert-verify: true\n    # fingerprint: xxxx # 配置指纹将实现 SSL Pining 效果, 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取\n    # 下面两项如果填写则开启 mTLS（需要同时填写）\n    # certificate: ./client.crt # 证书 PEM 格式，或者 证书的路径\n    # private-key: ./client.key # 证书对应的私钥 PEM 格式，或者私钥路径\n    udp: true\n    # ws-opts:\n    #   path: /path\n    #   headers:\n    #     Host: example.com\n    #   v2ray-http-upgrade: false\n    #   v2ray-http-upgrade-fast-open: false\n\n  - name: \"trojan-xtls\"\n    type: trojan\n    server: server\n    port: 443\n    password: yourpsk\n    flow: \"xtls-rprx-direct\" # xtls-rprx-origin xtls-rprx-direct\n    flow-show: true\n    # udp: true\n    # sni: example.com # aka server name\n    # skip-cert-verify: true\n    # fingerprint: xxxx # 配置指纹将实现 SSL Pining 效果, 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取\n    # 下面两项如果填写则开启 mTLS（需要同时填写）\n    # certificate: ./client.crt # 证书 PEM 格式，或者 证书的路径\n    # private-key: ./client.key # 证书对应的私钥 PEM 格式，或者私钥路径\n\n  #hysteria\n  - name: \"hysteria\"\n    type: hysteria\n    server: server.com\n    port: 443\n    # ports: 1000,2000-3000,5000 # port 不可省略\n    auth-str: yourpassword\n    # obfs: obfs_str\n    # alpn:\n    #   - h3\n    protocol: udp # 支持 udp/wechat-video/faketcp\n    up: \"30 Mbps\" # 若不写单位，默认为 Mbps\n    down: \"200 Mbps\" # 若不写单位，默认为 Mbps\n    # sni: server.com\n    # ech-opts:\n    #   enable: true # 必须手动开启\n    #   # 如果config为空则通过dns解析，不为空则通过该值指定，格式为经过base64编码的ech参数（dig +short TYPE65 tls-ech.dev）\n    #   config: AEn+DQBFKwAgACABWIHUGj4u+PIggYXcR5JF0gYk3dCRioBW8uJq9H4mKAAIAAEAAQABAANAEnB1YmxpYy50bHMtZWNoLmRldgAA\n    #   # query-server-name: xxx.com # 可选项，不为空时用于指定通过dns解析时的域名\n    # skip-cert-verify: false\n    # recv-window-conn: 12582912\n    # recv-window: 52428800\n    # disable-mtu-discovery: false\n    # fingerprint: xxxx # 配置指纹将实现 SSL Pining 效果, 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取\n    # 下面两项如果填写则开启 mTLS（需要同时填写）\n    # certificate: ./client.crt # 证书 PEM 格式，或者 证书的路径\n    # private-key: ./client.key # 证书对应的私钥 PEM 格式，或者私钥路径\n    # fast-open: true # 支持 TCP 快速打开，默认为 false\n\n  #hysteria2\n  - name: \"hysteria2\"\n    type: hysteria2\n    server: server.com\n    port: 443\n    # ports: 1000,2000-3000,5000 # port 不可省略\n    # hop-interval: 15 # 支持填写\"15-30\"会每次随机选取其中一个值作为切换间隔，仅支持写一个范围（即不允许出现逗号）\n    #  up 和 down 均不写或为 0 则使用 BBR 流控\n    # up: \"30 Mbps\" # 若不写单位，默认为 Mbps\n    # down: \"200 Mbps\" # 若不写单位，默认为 Mbps\n    # bbr-profile: \"\" # Available: \"standard\", \"conservative\", \"aggressive\". Default: \"standard\"\n    password: yourpassword\n    # obfs: salamander # 默认为空，如果填写则开启 obfs，目前仅支持 salamander\n    # obfs-password: yourpassword\n    # sni: server.com\n    # ech-opts:\n    #   enable: true # 必须手动开启\n    #   # 如果config为空则通过dns解析，不为空则通过该值指定，格式为经过base64编码的ech参数（dig +short TYPE65 tls-ech.dev）\n    #   config: AEn+DQBFKwAgACABWIHUGj4u+PIggYXcR5JF0gYk3dCRioBW8uJq9H4mKAAIAAEAAQABAANAEnB1YmxpYy50bHMtZWNoLmRldgAA\n    #   # query-server-name: xxx.com # 可选项，不为空时用于指定通过dns解析时的域名\n    # skip-cert-verify: false\n    # fingerprint: xxxx # 配置指纹将实现 SSL Pining 效果, 可使用 openssl x509 -noout -fingerprint -sha256 -inform pem -in yourcert.pem 获取\n    # 下面两项如果填写则开启 mTLS（需要同时填写）\n    # certificate: ./client.crt # 证书 PEM 格式，或者 证书的路径\n    # private-key: ./client.key # 证书对应的私钥 PEM 格式，或者私钥路径\n    # alpn:\n    #   - h3\n    ###quic-go特殊配置项，不要随意修改除非你知道你在干什么###\n    # initial-stream-receive-window： 8388608\n    # max-stream-receive-window： 8388608\n    # initial-connection-receive-window： 20971520\n    # max-connection-receive-window： 20971520\n\n  # wireguard\n  - name: \"wg\"\n    type: wireguard\n    server: 162.159.192.1\n    port: 2480\n    ip: 172.16.0.2\n    ipv6: fd01:5ca1:ab1e:80fa:ab85:6eea:213f:f4a5\n    public-key: Cr8hWlKvtDt7nrvf+f0brNQQzabAqrjfBvas9pmowjo=\n    #    pre-shared-key: 31aIhAPwktDGpH4JDhA8GNvjFXEf/a6+UaQRyOAiyfM=\n    private-key: eCtXsJZ27+4PbhDkHnB923tkUn2Gj59wZw5wFA75MnU=\n    udp: true\n    reserved: \"U4An\"\n    # 数组格式也是合法的\n    # reserved: [209,98,59]\n    # persistent-keepalive: 0\n    # 一个出站代理的标识。当值不为空时，将使用指定的 proxy 发出连接\n    # dialer-proxy: \"ss1\"\n    # remote-dns-resolve: true # 强制 dns 远程解析，默认值为 false\n    # dns: [ 1.1.1.1, 8.8.8.8 ] # 仅在 remote-dns-resolve 为 true 时生效\n    # refresh-server-ip-interval: 60 # 重新解析server ip的间隔，单位为秒，默认值为0即仅第一次链接时解析server域名，仅应在server域名对应的IP会发生变化时启用该选项（如家宽ddns）\n    # 如果 peers 不为空，该段落中的 allowed-ips 不可为空；前面段落的 server,port,public-key,pre-shared-key 均会被忽略，但 private-key 会被保留且只能在顶层指定\n    # peers:\n    #   - server: 162.159.192.1\n    #     port: 2480\n    #     public-key: Cr8hWlKvtDt7nrvf+f0brNQQzabAqrjfBvas9pmowjo=\n    #     # pre-shared-key: 31aIhAPwktDGpH4JDhA8GNvjFXEf/a6+UaQRyOAiyfM=\n    #     allowed-ips: ['0.0.0.0/0']\n    #     reserved: [209,98,59]\n    # 如果存在则开启AmneziaWG功能\n    # amnezia-wg-option:\n    #   jc: 5\n    #   jmin: 500\n    #   jmax: 501\n    #   s1: 30\n    #   s2: 40\n    #   s3: 50                                            # AmneziaWG v1.5 and v2\n    #   s4: 5                                             # AmneziaWG v1.5 and v2\n    #   h1: 123456                                        # AmneziaWG v1.0 and v1.5\n    #   h2: 67543                                         # AmneziaWG v1.0 and v1.5\n    #   h3: 123123                                        # AmneziaWG v1.0 and v1.5\n    #   h4: 32345                                         # AmneziaWG v1.0 and v1.5\n    #   h1: 123456-123500                                 # AmneziaWG v2.0 only\n    #   h2: 67543-67550                                   # AmneziaWG v2.0 only\n    #   h3: 123123-123200                                 # AmneziaWG v2.0 only\n    #   h4: 32345-32350                                   # AmneziaWG v2.0 only\n    #   i1: <b 0xf6ab3267fa><c><b 0xf6ab><t><r 10><wt 10> # AmneziaWG v1.5 and v2\n    #   i2: <b 0xf6ab3267fa><r 100>                       # AmneziaWG v1.5 and v2\n    #   i3: \"\"                                            # AmneziaWG v1.5 and v2\n    #   i4: \"\"                                            # AmneziaWG v1.5 and v2\n    #   i5: \"\"                                            # AmneziaWG v1.5 and v2\n    #   j1: <b 0xffffffff><c><b 0xf6ab><t><r 10>          # AmneziaWG v1.5 only (removed in v2)\n    #   j2: <c><b 0xf6ab><t><wt 1000>                     # AmneziaWG v1.5 only (removed in v2)\n    #   j3: <t><b 0xf6ab><c><r 10>                        # AmneziaWG v1.5 only (removed in v2)\n    #   itime: 60                                         # AmneziaWG v1.5 only (removed in v2)\n\n  # masque\n  - name: \"masque\"\n    type: masque\n    server: 162.159.198.1\n    port: 443\n    private-key: MHcCAQEEILI1eOtnbEIh89Fj4yNDuFR6UjayCKI3NdLl3DhetimWoAoGCCqGSM49AwEHoUQDQgAEgyXrE8v+hHsHy3ewSb3WcRjYgCrM9T9hiE0Uv6k2DZ1+4kefrDT9v1Q/8wdRigTf6t6gGNUV8W+IUMdrfUt+9g==\n    public-key: MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIaU7MToJm9NKp8YfGxR6r+/h4mcG7SxI8tsW8OR1A5tv/zCzVbCRRh2t87/kxnP6lAy0lkr7qYwu+ox+k3dr6w==\n    ip: 172.16.0.2\n    ipv6: 2606:4700:110:84c0:163a:4914:a0ad:3342\n    mtu: 1280\n    udp: true\n    # 一个出站代理的标识。当值不为空时，将使用指定的 proxy 发出连接\n    # dialer-proxy: \"ss1\"\n    # remote-dns-resolve: true # 强制 dns 远程解析，默认值为 false\n    # dns: [ 1.1.1.1, 8.8.8.8 ] # 仅在 remote-dns-resolve 为 true 时生效\n    # congestion-controller: bbr # 默认不开启\n\n  # masque-h2\n  - name: \"masque-h2\"\n    type: masque\n    server: 162.159.198.2\n    port: 443\n    private-key: MHcCAQEEILI1eOtnbEIh89Fj4yNDuFR6UjayCKI3NdLl3DhetimWoAoGCCqGSM49AwEHoUQDQgAEgyXrE8v+hHsHy3ewSb3WcRjYgCrM9T9hiE0Uv6k2DZ1+4kefrDT9v1Q/8wdRigTf6t6gGNUV8W+IUMdrfUt+9g==\n    public-key: MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEIaU7MToJm9NKp8YfGxR6r+/h4mcG7SxI8tsW8OR1A5tv/zCzVbCRRh2t87/kxnP6lAy0lkr7qYwu+ox+k3dr6w==\n    ip: 172.16.0.2\n    ipv6: 2606:4700:110:84c0:163a:4914:a0ad:3342\n    mtu: 1280\n    udp: true\n    network: h2\n    # 一个出站代理的标识。当值不为空时，将使用指定的 proxy 发出连接\n    # dialer-proxy: \"ss1\"\n    # remote-dns-resolve: true # 强制 dns 远程解析，默认值为 false\n    # dns: [ 1.1.1.1, 8.8.8.8 ] # 仅在 remote-dns-resolve 为 true 时生效\n\n  # tuic\n  - name: tuic\n    server: www.example.com\n    port: 10443\n    type: tuic\n    # tuicV4 必须填写 token（不可同时填写 uuid 和 password）\n    token: TOKEN\n    # tuicV5 必须填写 uuid 和 password（不可同时填写 token）\n    uuid: 00000000-0000-0000-0000-000000000001\n    password: PASSWORD_1\n    # ip: 127.0.0.1 # for overwriting the DNS lookup result of the server address set in option 'server'\n    # heartbeat-interval: 10000\n    # alpn: [h3]\n    disable-sni: true\n    reduce-rtt: true\n    request-timeout: 8000\n    udp-relay-mode: native # Available: \"native\", \"quic\". Default: \"native\"\n    # congestion-controller: bbr # Available: \"cubic\", \"new_reno\", \"bbr\". Default: \"cubic\"\n    # cwnd: 10 # default: 32\n    # bbr-profile: \"\" # Available: \"standard\", \"conservative\", \"aggressive\". Default: \"standard\"\n    # max-udp-relay-packet-size: 1500\n    # fast-open: true\n    # skip-cert-verify: true\n    # max-open-streams: 20 # default 100, too many open streams may hurt performance\n    # sni: example.com\n    # ech-opts:\n    #   enable: true # 必须手动开启\n    #   # 如果config为空则通过dns解析，不为空则通过该值指定，格式为经过base64编码的ech参数（dig +short TYPE65 tls-ech.dev）\n    #   config: AEn+DQBFKwAgACABWIHUGj4u+PIggYXcR5JF0gYk3dCRioBW8uJq9H4mKAAIAAEAAQABAANAEnB1YmxpYy50bHMtZWNoLmRldgAA\n    #   # query-server-name: xxx.com # 可选项，不为空时用于指定通过dns解析时的域名\n    #\n    # meta 和 sing-box 私有扩展，将 ss-uot 用于 udp 中继，开启此选项后 udp-relay-mode 将失效\n    # 警告，与原版 tuic 不兼容！！！\n    # udp-over-stream: false\n    # udp-over-stream-version: 1\n\n  # ShadowsocksR\n  # The supported ciphers (encryption methods): all stream ciphers in ss\n  # The supported obfses:\n  #   plain http_simple http_post\n  #   random_head tls1.2_ticket_auth tls1.2_ticket_fastauth\n  # The supported protocols:\n  #   origin auth_sha1_v4 auth_aes128_md5\n  #   auth_aes128_sha1 auth_chain_a auth_chain_b\n  - name: \"ssr\"\n    type: ssr\n    server: server\n    port: 443\n    cipher: chacha20-ietf\n    password: \"password\"\n    obfs: tls1.2_ticket_auth\n    protocol: auth_sha1_v4\n    # obfs-param: domain.tld\n    # protocol-param: \"#\"\n    # udp: true\n\n  - name: \"ssh-out\"\n    type: ssh\n\n    server: 127.0.0.1\n    port: 22\n    username: root\n    password: password\n    privateKey: path\n\n  # mieru\n  - name: mieru\n    type: mieru\n    server: 1.2.3.4\n    port: 2999\n    # port-range: 2090-2099 #（不可同时填写 port 和 port-range）\n    transport: TCP # 支持 TCP 或者 UDP\n    udp: true # 支持 UDP over TCP\n    username: user\n    password: password\n    # 可以使用的值包括 MULTIPLEXING_OFF, MULTIPLEXING_LOW, MULTIPLEXING_MIDDLE, MULTIPLEXING_HIGH。其中 MULTIPLEXING_OFF 会关闭多路复用功能。默认值为 MULTIPLEXING_LOW。\n    # multiplexing: MULTIPLEXING_LOW\n    # 如果想开启 0-RTT 握手，请设置为 HANDSHAKE_NO_WAIT，否则请设置为 HANDSHAKE_STANDARD。默认值为 HANDSHAKE_STANDARD\n    # handshake-mode: HANDSHAKE_STANDARD\n    # 一个 base64 字符串用于微调网络行为\n    # traffic-pattern: \"\"\n\n  # sudoku\n  - name: sudoku\n    type: sudoku\n    server: server_ip/domain # 1.2.3.4 or domain\n    port: 443 \n    key: \"<client_key>\" # 如果你使用sudoku生成的ED25519密钥对，请填写密钥对中的私钥，否则填入和服务端相同的uuid\n    aead-method: chacha20-poly1305 # 可选：chacha20-poly1305、aes-128-gcm、none（不建议；none 不提供 AEAD 保护）\n    padding-min: 2 # 最小填充率（0-100）\n    padding-max: 7 # 最大填充率（0-100，必须 >= padding-min）\n    table-type: prefer_ascii    # 可选值：prefer_ascii、prefer_entropy、up_ascii_down_entropy、up_entropy_down_ascii\n    # custom-table: xpxvvpvv    # 可选，自定义字节布局，必须包含2个x、2个p、4个v，可随意组合；只对 entropy 方向生效\n    # custom-tables: [\"xpxvvpvv\", \"vxpvxvvp\"] # 可选，自定义字节布局列表（x/v/p），非空时覆盖 custom-table\n    # 推荐：使用 httpmask 对象统一管理 HTTPMask 相关字段：\n    httpmask:\n      disable: false # true 禁用所有 HTTP 伪装/隧道\n      mode: legacy # 可选：legacy（默认）、stream（split-stream）、poll、auto（先 stream 再 poll）、ws（WebSocket 隧道）\n      # tls: true # 可选：按需开启 HTTPS/WSS\n      # host: \"\" # 可选：覆盖 Host/SNI（支持 example.com 或 example.com:443）；仅在 mode 为 stream/poll/auto/ws 时生效\n      # path-root: \"\" # 可选：HTTP 隧道端点一级路径前缀（双方需一致），例如 \"aabbcc\" 或 \"/aabbcc/\" => /aabbcc/session、/aabbcc/stream、/aabbcc/api/v1/upload、/aabbcc/ws\n      # multiplex: \"off\" # 可选字符串：off（默认）、auto（复用底层 HTTP 连接，减少建链 RTT）、on（Sudoku mux 单隧道多目标；仅在 mode=stream/poll/auto 生效；ws 强制 off）\n    enable-pure-downlink: false # 可选：false=带宽优化下行；true=纯 Sudoku 下行\n\n  # anytls\n  - name: anytls\n    type: anytls\n    server: 1.2.3.4\n    port: 443\n    password: \"<your password>\"\n    # client-fingerprint: chrome\n    udp: true\n    # idle-session-check-interval: 30 # seconds\n    # idle-session-timeout: 30 # seconds\n    # min-idle-session: 0\n    # sni: \"example.com\"\n    # alpn:\n    #   - h2\n    #   - http/1.1\n    # skip-cert-verify: true\n\n  # trusttunnel\n  - name: trusttunnel\n    type: trusttunnel\n    server: 1.2.3.4\n    port: 443\n    username: username\n    password: password\n    # client-fingerprint: chrome\n    health-check: true\n    udp: true\n    # sni: \"example.com\"\n    # alpn:\n    #   - h2\n    # skip-cert-verify: true\n    ### quic options\n    # quic: true # 默认为false\n    # congestion-controller: bbr\n    # bbr-profile: \"\" # Available: \"standard\", \"conservative\", \"aggressive\". Default: \"standard\"\n    ### reuse options\n    # max-connections: 8 # Maximum connections. Conflict with max-streams.\n    # min-streams: 5 # Minimum multiplexed streams in a connection before opening a new connection. Conflict with max-streams.\n    # max-streams: 0 # Maximum multiplexed streams in a connection before opening a new connection. Conflict with max-connections and min-streams.\n\n# dns 出站会将请求劫持到内部 dns 模块，所有请求均在内部处理\n  - name: \"dns-out\"\n    type: dns\n\n  # 配置指定 interface-name 和 fwmark 的 DIRECT\n  - name: en1-direct\n    type: direct\n    interface-name: en1\n    routing-mark: 6667\nproxy-groups:\n  # 代理链，目前 relay 可以支持 udp 的只有 vmess/vless/trojan/ss/ssr/tuic\n  # wireguard 目前不支持在 relay 中使用，请使用 proxy 中的 dialer-proxy 配置项\n  # Traffic: mihomo <-> http <-> vmess <-> ss1 <-> ss2 <-> Internet\n  - name: \"relay\"\n    type: relay\n    proxies:\n      - http\n      - vmess\n      - ss1\n      - ss2\n\n  # url-test 将按照 url 测试结果使用延迟最低节点\n  - name: \"auto\"\n    type: url-test\n    proxies:\n      - ss1\n      - ss2\n      - vmess1\n    # tolerance: 150\n    # lazy: true\n    # expected-status: 204 # 当健康检查返回状态码与期望值不符时，认为节点不可用\n    url: \"https://cp.cloudflare.com/generate_204\"\n    interval: 300\n\n  # fallback 将按照 url 测试结果按照节点顺序选择\n  - name: \"fallback-auto\"\n    type: fallback\n    proxies:\n      - ss1\n      - ss2\n      - vmess1\n    url: \"https://cp.cloudflare.com/generate_204\"\n    interval: 300\n\n  # load-balance 将按照算法随机选择节点\n  - name: \"load-balance\"\n    type: load-balance\n    proxies:\n      - ss1\n      - ss2\n      - vmess1\n    url: \"https://cp.cloudflare.com/generate_204\"\n    interval: 300\n  # strategy: consistent-hashing # 可选 round-robin 和 sticky-sessions\n\n  # select 用户自行选择节点\n  - name: Proxy\n    type: select\n    # disable-udp: true\n    proxies:\n      - ss1\n      - ss2\n      - vmess1\n      - auto\n\n  - name: UseProvider\n    type: select\n    filter: \"HK|TW\" # 正则表达式，过滤 provider1 中节点名包含 HK 或 TW\n    use:\n      - provider1\n    proxies:\n      - Proxy\n      - DIRECT\n\n# Mihomo 格式的节点或支持 *ray 的分享格式\nproxy-providers:\n  provider1:\n    type: http # http 的 path 可空置，默认储存路径为 homedir 的 proxies 文件夹，文件名为 url 的 md5\n    url: \"url\"\n    interval: 3600\n    path: ./provider1.yaml # 默认只允许存储在 mihomo 的 Home Dir，如果想存储到其他位置，请通过设置 SAFE_PATHS 环境变量指定额外的安全路径。该环境变量的语法同本操作系统的PATH环境变量解析规则（即Windows下以分号分割，其他系统下以冒号分割）\n    proxy: DIRECT\n    # size-limit: 10240 # 限制下载文件最大为10kb，默认为0即不限制文件大小\n    header:\n      User-Agent:\n      - \"Clash/v1.18.0\"\n      - \"mihomo/1.18.3\"\n      # Accept:\n      # - 'application/vnd.github.v3.raw'\n      # Authorization:\n      # - 'token 1231231'\n    health-check:\n      enable: true\n      interval: 600\n      # lazy: true\n      url: https://cp.cloudflare.com/generate_204\n      # expected-status: 204 # 当健康检查返回状态码与期望值不符时，认为节点不可用\n    override: # 覆写节点加载时的一些配置项\n      skip-cert-verify: true\n      udp: true\n      # down: \"50 Mbps\"\n      # up: \"10 Mbps\"\n      # dialer-proxy: proxy\n      # interface-name: tailscale0\n      # routing-mark: 233\n      # ip-version: ipv4-prefer\n      # additional-prefix: \"[provider1]\"\n      # additional-suffix: \"test\"\n      # # 名字替换，支持正则表达式\n      # proxy-name:\n      #   - pattern: \"test\"\n      #     target: \"TEST\"\n      #   - pattern: \"IPLC-(.*?)倍\"\n      #     target: \"iplc x $1\"\n\n  provider2:\n    type: inline\n    dialer-proxy: proxy\n    payload:\n      - name: \"ss1\"\n        type: ss\n        server: server\n        port: 443\n        cipher: chacha20-ietf-poly1305\n        password: \"password\"\n\n  test:\n    type: file\n    path: /test.yaml\n    health-check:\n      enable: true\n      interval: 36000\n      url: https://cp.cloudflare.com/generate_204\nrule-providers:\n  rule1:\n    behavior: classical # domain ipcidr\n    interval: 259200\n    path: /path/to/save/file.yaml # 默认只允许存储在 mihomo 的 Home Dir，如果想存储到其他位置，请通过设置 SAFE_PATHS 环境变量指定额外的安全路径。该环境变量的语法同本操作系统的PATH环境变量解析规则（即Windows下以分号分割，其他系统下以冒号分割）\n    type: http # http 的 path 可空置，默认储存路径为 homedir 的 rules 文件夹，文件名为 url 的 md5\n    url: \"url\"\n    proxy: DIRECT\n    # size-limit: 10240 # 限制下载文件最大为10kb，默认为0即不限制文件大小\n  rule2:\n    behavior: classical\n    interval: 259200\n    path: /path/to/save/file.yaml\n    type: file\n  rule3:\n    # mrs类型ruleset，目前仅支持domain和ipcidr(即不支持classical），\n    #\n    # 对于behavior=domain:\n    #  - format=yaml 可以通过“mihomo convert-ruleset domain yaml XXX.yaml XXX.mrs”转换到mrs格式\n    #  - format=text 可以通过“mihomo convert-ruleset domain text XXX.text XXX.mrs”转换到mrs格式\n    #  - XXX.mrs 可以通过\"mihomo convert-ruleset domain mrs XXX.mrs XXX.text\"转换回text格式（暂不支持转换回yaml格式）\n    #\n    # 对于behavior=ipcidr:\n    #  - format=yaml 可以通过“mihomo convert-ruleset ipcidr yaml XXX.yaml XXX.mrs”转换到mrs格式\n    #  - format=text 可以通过“mihomo convert-ruleset ipcidr text XXX.text XXX.mrs”转换到mrs格式\n    #  - XXX.mrs 可以通过\"mihomo convert-ruleset ipcidr mrs XXX.mrs XXX.text\"转换回text格式（暂不支持转换回yaml格式）\n    #\n    type: http\n    url: \"url\"\n    format: mrs\n    behavior: domain\n    path: /path/to/save/file.mrs\n  rule4:\n    type: inline\n    behavior: domain # classical / ipcidr\n    payload:\n      - '.blogger.com'\n      - '*.*.microsoft.com'\n      - 'books.itunes.apple.com'\n\nrules:\n  - RULE-SET,rule1,REJECT\n  - IP-ASN,1,PROXY\n  - DOMAIN-REGEX,^abc,DIRECT\n  - DOMAIN-SUFFIX,baidu.com,DIRECT\n  - DOMAIN-KEYWORD,google,ss1\n  - DOMAIN-WILDCARD,test.*.mihomo.com,ss1\n  - IP-CIDR,1.1.1.1/32,ss1\n  - IP-CIDR6,2409::/64,DIRECT\n  # 当满足条件是 TCP 或 UDP 流量时，使用名为 sub-rule-name1 的规则集\n  - SUB-RULE,(OR,((NETWORK,TCP),(NETWORK,UDP))),sub-rule-name1\n  - SUB-RULE,(AND,((NETWORK,UDP))),sub-rule-name2\n# 定义多个子规则集，规则将以分叉匹配，使用 SUB-RULE 使用\n#                                               google.com(not match)--> baidu.com(match)\n#                                                /                                ｜\n#                                               /                                 ｜\n#  https://baidu.com  --> rule1 --> rule2 --> sub-rule-name1(match tcp)          使用 DIRECT\n#\n#\n#                                              google.com(not match)--> baidu.com(not match)\n#                                                /                            ｜\n#                                               /                             ｜\n#  dns 1.1.1.1  --> rule1 --> rule2 --> sub-rule-name1(match udp)         sub-rule-name2(match udp)\n#                                                                             ｜\n#                                                                             ｜\n#                                                                 使用 REJECT <-- 1.1.1.1/32(match)\n#\n\nsub-rules:\n  sub-rule-name1:\n    - DOMAIN,google.com,ss1\n    - DOMAIN,baidu.com,DIRECT\n  sub-rule-name2:\n    - IP-CIDR,1.1.1.1/32,REJECT\n    - IP-CIDR,8.8.8.8/32,ss1\n    - DOMAIN,dns.alidns.com,REJECT\n\n# 流量入站\nlisteners:\n  - name: socks5-in-1\n    type: socks\n    port: 10808 # 支持使用ports格式，例如200,302 or 200,204,401-429,501-503\n    #listen: 0.0.0.0 # 默认监听 0.0.0.0\n    # rule: sub-rule-name1 # 默认使用 rules，如果未找到 sub-rule 则直接使用 rules\n    # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理\n    # udp: false # 默认 true\n    # users: # 如果不填写users项，则遵从全局authentication设置，如果填写会忽略全局设置, 如想跳过该入站的验证可填写 users: []\n    #   - username: aaa\n    #     password: aaa\n    # 下面两项如果填写则开启 tls（需要同时填写）\n    # certificate: ./server.crt # 证书 PEM 格式，或者 证书的路径\n    # private-key: ./server.key # 证书对应的私钥 PEM 格式，或者私钥路径\n    # 下面两项为mTLS配置项，如果client-auth-type设置为 \"verify-if-given\" 或 \"require-and-verify\" 则client-auth-cert必须不为空\n    # client-auth-type: \"\" # 可选值：\"\"、\"request\"、\"require-any\"、\"verify-if-given\"、\"require-and-verify\"\n    # client-auth-cert: string # 证书 PEM 格式，或者 证书的路径\n    # 如果填写则开启ech（可由 mihomo generate ech-keypair <明文域名> 生成）\n    # ech-key: |\n    #   -----BEGIN ECH KEYS-----\n    #   ACATwY30o/RKgD6hgeQxwrSiApLaCgU+HKh7B6SUrAHaDwBD/g0APwAAIAAgHjzK\n    #   madSJjYQIf9o1N5GXjkW4DEEeb17qMxHdwMdNnwADAABAAEAAQACAAEAAwAIdGVz\n    #   dC5jb20AAA==\n    #   -----END ECH KEYS-----\n\n  - name: http-in-1\n    type: http\n    port: 10809 # 支持使用ports格式，例如200,302 or 200,204,401-429,501-503\n    listen: 0.0.0.0\n    # rule: sub-rule-name1 # 默认使用 rules，如果未找到 sub-rule 则直接使用 rules\n    # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时，这里的 proxy 名称必须合法，否则会出错)\n    # users: # 如果不填写users项，则遵从全局authentication设置，如果填写会忽略全局设置, 如想跳过该入站的验证可填写 users: []\n    #   - username: aaa\n    #     password: aaa\n    # 下面两项如果填写则开启 tls（需要同时填写）\n    # certificate: ./server.crt # 证书 PEM 格式，或者 证书的路径\n    # private-key: ./server.key # 证书对应的私钥 PEM 格式，或者私钥路径\n    # 下面两项为mTLS配置项，如果client-auth-type设置为 \"verify-if-given\" 或 \"require-and-verify\" 则client-auth-cert必须不为空\n    # client-auth-type: \"\" # 可选值：\"\"、\"request\"、\"require-any\"、\"verify-if-given\"、\"require-and-verify\"\n    # client-auth-cert: string # 证书 PEM 格式，或者 证书的路径\n    # 如果填写则开启ech（可由 mihomo generate ech-keypair <明文域名> 生成）\n    # ech-key: |\n    #   -----BEGIN ECH KEYS-----\n    #   ACATwY30o/RKgD6hgeQxwrSiApLaCgU+HKh7B6SUrAHaDwBD/g0APwAAIAAgHjzK\n    #   madSJjYQIf9o1N5GXjkW4DEEeb17qMxHdwMdNnwADAABAAEAAQACAAEAAwAIdGVz\n    #   dC5jb20AAA==\n    #   -----END ECH KEYS-----\n\n  - name: mixed-in-1\n    type: mixed #  HTTP(S) 和 SOCKS 代理混合\n    port: 10810 # 支持使用ports格式，例如200,302 or 200,204,401-429,501-503\n    listen: 0.0.0.0\n    # rule: sub-rule-name1 # 默认使用 rules，如果未找到 sub-rule 则直接使用 rules\n    # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时，这里的 proxy 名称必须合法，否则会出错)\n    # udp: false # 默认 true\n    # users: # 如果不填写users项，则遵从全局authentication设置，如果填写会忽略全局设置, 如想跳过该入站的验证可填写 users: []\n    #   - username: aaa\n    #     password: aaa\n    # 下面两项如果填写则开启 tls（需要同时填写）\n    # certificate: ./server.crt # 证书 PEM 格式，或者 证书的路径\n    # private-key: ./server.key # 证书对应的私钥 PEM 格式，或者私钥路径\n    # 下面两项为mTLS配置项，如果client-auth-type设置为 \"verify-if-given\" 或 \"require-and-verify\" 则client-auth-cert必须不为空\n    # client-auth-type: \"\" # 可选值：\"\"、\"request\"、\"require-any\"、\"verify-if-given\"、\"require-and-verify\"\n    # client-auth-cert: string # 证书 PEM 格式，或者 证书的路径\n    # 如果填写则开启ech（可由 mihomo generate ech-keypair <明文域名> 生成）\n    # ech-key: |\n    #   -----BEGIN ECH KEYS-----\n    #   ACATwY30o/RKgD6hgeQxwrSiApLaCgU+HKh7B6SUrAHaDwBD/g0APwAAIAAgHjzK\n    #   madSJjYQIf9o1N5GXjkW4DEEeb17qMxHdwMdNnwADAABAAEAAQACAAEAAwAIdGVz\n    #   dC5jb20AAA==\n    #   -----END ECH KEYS-----\n\n  - name: redir-in-1\n    type: redir\n    port: 10811 # 支持使用ports格式，例如200,302 or 200,204,401-429,501-503\n    listen: 0.0.0.0\n    # rule: sub-rule-name1 # 默认使用 rules，如果未找到 sub-rule 则直接使用 rules\n    # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时，这里的 proxy 名称必须合法，否则会出错)\n\n  - name: tproxy-in-1\n    type: tproxy\n    port: 10812 # 支持使用ports格式，例如200,302 or 200,204,401-429,501-503\n    listen: 0.0.0.0\n    # rule: sub-rule-name1 # 默认使用 rules，如果未找到 sub-rule 则直接使用 rules\n    # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时，这里的 proxy 名称必须合法，否则会出错)\n    # udp: false # 默认 true\n\n  - name: shadowsocks-in-1\n    type: shadowsocks\n    port: 10813 # 支持使用ports格式，例如200,302 or 200,204,401-429,501-503\n    listen: 0.0.0.0\n    # rule: sub-rule-name1 # 默认使用 rules，如果未找到 sub-rule 则直接使用 rules\n    # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时，这里的 proxy 名称必须合法，否则会出错)\n    password: vlmpIPSyHH6f4S8WVPdRIHIlzmB+GIRfoH3aNJ/t9Gg=\n    cipher: 2022-blake3-aes-256-gcm\n    # shadow-tls:\n    #   enable: false # 设置为true时开启\n    #   version: 3 # 支持v1/v2/v3\n    #   password: password # v2设置项\n    #   users: # v3设置项\n    #     - name: 1\n    #       password: password\n    #   handshake:\n    #     dest: test.com:443\n    # kcp-tun:\n    #   enable: false\n    #   key: it's a secrect # pre-shared secret between client and server\n    #   crypt: aes # aes, aes-128, aes-128-gcm, aes-192, salsa20, blowfish, twofish, cast5, 3des, tea, xtea, xor, none, null\n    #   mode: fast # profiles: fast3, fast2, fast, normal, manual\n    #   conn: 1 # set num of UDP connections to server\n    #   autoexpire: 0 # set auto expiration time(in seconds) for a single UDP connection, 0 to disable\n    #   scavengettl: 600 # set how long an expired connection can live (in seconds)\n    #   ratelimit: 0 # set maximum outgoing speed (in bytes per second) for a single KCP connection, 0 to disable. Also known as packet pacing\n    #   mtu: 1350 # set maximum transmission unit for UDP packets\n    #   sndwnd: 128 # set send window size(num of packets)\n    #   rcvwnd: 512 # set receive window size(num of packets)\n    #   datashard: 10 # set reed-solomon erasure coding - datashard\n    #   parityshard: 3 # set reed-solomon erasure coding - parityshard\n    #   dscp: 0 # set DSCP(6bit)\n    #   nocomp: false # disable compression\n    #   acknodelay: false # flush ack immediately when a packet is received\n    #   nodelay: 0\n    #   interval: 50\n    #   resend: 0\n    #   sockbuf: 4194304 # per-socket buffer in bytes\n    #   smuxver: 1 # specify smux version, available 1,2\n    #   smuxbuf: 4194304 # the overall de-mux buffer in bytes\n    #   framesize: 8192 # smux max frame size\n    #   streambuf: 2097152 # per stream receive buffer in bytes, smux v2+\n    #   keepalive: 10 # seconds between heartbeats\n\n  - name: vmess-in-1\n    type: vmess\n    port: 10814 # 支持使用ports格式，例如200,302 or 200,204,401-429,501-503\n    listen: 0.0.0.0\n    # rule: sub-rule-name1 # 默认使用 rules，如果未找到 sub-rule 则直接使用 rules\n    # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时，这里的 proxy 名称必须合法，否则会出错)\n    users:\n      - username: 1\n        uuid: 9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68\n        alterId: 1\n    # ws-path: \"/\" # 如果不为空则开启 websocket 传输层\n    # grpc-service-name: \"GunService\" # 如果不为空则开启 grpc 传输层\n    # 下面两项如果填写则开启 tls（需要同时填写）\n    # certificate: ./server.crt # 证书 PEM 格式，或者 证书的路径\n    # private-key: ./server.key # 证书对应的私钥 PEM 格式，或者私钥路径\n    # 下面两项为mTLS配置项，如果client-auth-type设置为 \"verify-if-given\" 或 \"require-and-verify\" 则client-auth-cert必须不为空\n    # client-auth-type: \"\" # 可选值：\"\"、\"request\"、\"require-any\"、\"verify-if-given\"、\"require-and-verify\"\n    # client-auth-cert: string # 证书 PEM 格式，或者 证书的路径\n    # 如果填写则开启ech（可由 mihomo generate ech-keypair <明文域名> 生成）\n    # ech-key: |\n    #   -----BEGIN ECH KEYS-----\n    #   ACATwY30o/RKgD6hgeQxwrSiApLaCgU+HKh7B6SUrAHaDwBD/g0APwAAIAAgHjzK\n    #   madSJjYQIf9o1N5GXjkW4DEEeb17qMxHdwMdNnwADAABAAEAAQACAAEAAwAIdGVz\n    #   dC5jb20AAA==\n    #   -----END ECH KEYS-----\n    # 如果填写reality-config则开启reality（注意不可与certificate和private-key同时填写）\n    # reality-config:\n    #   dest: test.com:443\n    #   private-key: jNXHt1yRo0vDuchQlIP6Z0ZvjT3KtzVI-T4E7RoLJS0 # 可由 mihomo generate reality-keypair 命令生成\n    #   short-id:\n    #     - 0123456789abcdef\n    #   server-names:\n    #     - test.com\n    #   #下列两个 limit 为选填，可对未通过验证的回落连接限速，bytesPerSec 默认为 0 即不启用\n    #   #回落限速是一种特征，不建议启用，如果您是面板/一键脚本开发者，务必让这些参数随机化\n    #   limit-fallback-upload:\n    #     after-bytes: 0 # 传输指定字节后开始限速\n    #     bytes-per-sec: 0 # 基准速率（字节/秒）\n    #     burst-bytes-per-sec: 0 # 突发速率（字节/秒），大于 bytesPerSec 时生效\n    #   limit-fallback-download:\n    #     after-bytes: 0 # 传输指定字节后开始限速\n    #     bytes-per-sec: 0 # 基准速率（字节/秒）\n    #     burst-bytes-per-sec: 0 # 突发速率（字节/秒），大于 bytesPerSec 时生效\n\n  - name: tuic-in-1\n    type: tuic\n    port: 10815 # 支持使用ports格式，例如200,302 or 200,204,401-429,501-503\n    listen: 0.0.0.0\n    # rule: sub-rule-name1 # 默认使用 rules，如果未找到 sub-rule 则直接使用 rules\n    # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时，这里的 proxy 名称必须合法，否则会出错)\n    # token:    # tuicV4 填写（可以同时填写 users）\n    #   - TOKEN\n    # users:    # tuicV5 填写（可以同时填写 token）\n    #   00000000-0000-0000-0000-000000000000: PASSWORD_0\n    #   00000000-0000-0000-0000-000000000001: PASSWORD_1\n    #  certificate: ./server.crt # 证书 PEM 格式，或者 证书的路径\n    #  private-key: ./server.key # 证书对应的私钥 PEM 格式，或者私钥路径\n    #  下面两项为mTLS配置项，如果client-auth-type设置为 \"verify-if-given\" 或 \"require-and-verify\" 则client-auth-cert必须不为空\n    #  client-auth-type: \"\" # 可选值：\"\"、\"request\"、\"require-any\"、\"verify-if-given\"、\"require-and-verify\"\n    #  client-auth-cert: string # 证书 PEM 格式，或者 证书的路径\n    #  如果填写则开启ech（可由 mihomo generate ech-keypair <明文域名> 生成）\n    #  ech-key: |\n    #    -----BEGIN ECH KEYS-----\n    #    ACATwY30o/RKgD6hgeQxwrSiApLaCgU+HKh7B6SUrAHaDwBD/g0APwAAIAAgHjzK\n    #    madSJjYQIf9o1N5GXjkW4DEEeb17qMxHdwMdNnwADAABAAEAAQACAAEAAwAIdGVz\n    #    dC5jb20AAA==\n    #    -----END ECH KEYS-----\n    #  congestion-controller: bbr\n    #  bbr-profile: \"\" # Available: \"standard\", \"conservative\", \"aggressive\". Default: \"standard\"\n    #  max-idle-time: 15000\n    #  authentication-timeout: 1000\n    #  alpn:\n    #    - h3\n    #  max-udp-relay-packet-size: 1500\n\n  - name: tunnel-in-1\n    type: tunnel\n    port: 10816 # 支持使用ports格式，例如200,302 or 200,204,401-429,501-503\n    listen: 0.0.0.0\n    # rule: sub-rule-name1 # 默认使用 rules，如果未找到 sub-rule 则直接使用 rules\n    # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时，这里的 proxy 名称必须合法，否则会出错)\n    network: [tcp, udp]\n    target: target.com\n\n  - name: vless-in-1\n    type: vless\n    port: 10817 # 支持使用ports格式，例如200,302 or 200,204,401-429,501-503\n    listen: 0.0.0.0\n    # rule: sub-rule-name1 # 默认使用 rules，如果未找到 sub-rule 则直接使用 rules\n    # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时，这里的 proxy 名称必须合法，否则会出错)\n    users:\n      - username: 1\n        uuid: 9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68\n        flow: xtls-rprx-vision\n    # ws-path: \"/\" # 如果不为空则开启 websocket 传输层\n    # grpc-service-name: \"GunService\" # 如果不为空则开启 grpc 传输层\n    # xhttp-config: # 如果不为空则开启 xhttp 传输层\n    #   path: \"/\"\n    #   host: \"\"\n    #   mode: auto # Available: \"stream-one\", \"stream-up\" or \"packet-up\"\n    #   no-sse-header: false\n    #   x-padding-bytes: \"100-1000\"\n    #   x-padding-obfs-mode: false\n    #   x-padding-key: x_padding\n    #   x-padding-header: Referer\n    #   x-padding-placement: queryInHeader # Available: queryInHeader, cookie, header, query\n    #   x-padding-method: repeat-x # Available: repeat-x, tokenish\n    #   uplink-http-method: POST # Available: POST, PUT, PATCH, DELETE\n    #   session-placement: path # Available: path, query, cookie, header\n    #   session-key: \"\"\n    #   seq-placement: path # Available: path, query, cookie, header\n    #   seq-key: \"\"\n    #   uplink-data-placement: body # Available: body, cookie, header\n    #   uplink-data-key: \"\"\n    #   uplink-chunk-size: 0 # only applicable when uplink-data-placement is not body\n    #   sc-max-buffered-posts: 30\n    #   sc-stream-up-server-secs: \"20-80\"\n    #   sc-max-each-post-bytes: 1000000\n    # -------------------------\n    # vless encryption服务端配置：\n    # （原生外观 / 只 XOR 公钥 / 全随机数。1-RTT 每次下发随机 300 到 600 秒的 ticket 以便 0-RTT 复用 / 只允许 1-RTT）\n    # 填写 \"600s\" 会每次随机取 50% 到 100%，即相当于填写 \"300-600s\"\n    # / 是只能选一个，后面 base64 至少一个，无限串联，使用 mihomo generate vless-x25519 和 mihomo generate vless-mlkem768 生成，替换值时需去掉括号\n    #\n    # Padding 是可选的参数，仅作用于 1-RTT 以消除握手的长度特征，双端默认值均为 \"100-111-1111.75-0-111.50-0-3333\"：\n    # 在 1-RTT client/server hello 后以 100% 的概率粘上随机 111 到 1111 字节的 padding\n    # 以 75% 的概率等待随机 0 到 111 毫秒（\"probability-from-to\"）\n    # 再次以 50% 的概率发送随机 0 到 3333 字节的 padding（若为 0 则不 Write()）\n    # 服务端、客户端可以设置不同的 padding 参数，按 len、gap 的顺序无限串联，第一个 padding 需概率 100%、至少 35 字节\n    # -------------------------\n    # decryption: \"mlkem768x25519plus.native/xorpub/random.600s(300-600s)/0s.(padding len).(padding gap).(X25519 PrivateKey).(ML-KEM-768 Seed)...\"\n    # 下面两项如果填写则开启 tls（需要同时填写）\n    # certificate: ./server.crt # 证书 PEM 格式，或者 证书的路径\n    # private-key: ./server.key # 证书对应的私钥 PEM 格式，或者私钥路径\n    # 下面两项为mTLS配置项，如果client-auth-type设置为 \"verify-if-given\" 或 \"require-and-verify\" 则client-auth-cert必须不为空\n    # client-auth-type: \"\" # 可选值：\"\"、\"request\"、\"require-any\"、\"verify-if-given\"、\"require-and-verify\"\n    # client-auth-cert: string # 证书 PEM 格式，或者 证书的路径\n    # 如果填写则开启ech（可由 mihomo generate ech-keypair <明文域名> 生成）\n    # ech-key: |\n    #   -----BEGIN ECH KEYS-----\n    #   ACATwY30o/RKgD6hgeQxwrSiApLaCgU+HKh7B6SUrAHaDwBD/g0APwAAIAAgHjzK\n    #   madSJjYQIf9o1N5GXjkW4DEEeb17qMxHdwMdNnwADAABAAEAAQACAAEAAwAIdGVz\n    #   dC5jb20AAA==\n    #   -----END ECH KEYS-----\n    # 如果填写reality-config则开启reality（注意不可与certificate和private-key同时填写）\n    reality-config:\n      dest: test.com:443\n      private-key: jNXHt1yRo0vDuchQlIP6Z0ZvjT3KtzVI-T4E7RoLJS0 # 可由 mihomo generate reality-keypair 命令生成\n      short-id:\n        - 0123456789abcdef\n      server-names:\n        - test.com\n      #下列两个 limit 为选填，可对未通过验证的回落连接限速，bytesPerSec 默认为 0 即不启用\n      #回落限速是一种特征，不建议启用，如果您是面板/一键脚本开发者，务必让这些参数随机化\n      limit-fallback-upload:\n        after-bytes: 0 # 传输指定字节后开始限速\n        bytes-per-sec: 0 # 基准速率（字节/秒）\n        burst-bytes-per-sec: 0 # 突发速率（字节/秒），大于 bytesPerSec 时生效\n      limit-fallback-download:\n        after-bytes: 0 # 传输指定字节后开始限速\n        bytes-per-sec: 0 # 基准速率（字节/秒）\n        burst-bytes-per-sec: 0 # 突发速率（字节/秒），大于 bytesPerSec 时生效\n    ### 注意，对于vless listener, 至少需要填写 “certificate和private-key” 或 “reality-config” 或 “decryption” 的其中一项 ###\n\n  - name: anytls-in-1\n    type: anytls\n    port: 10818 # 支持使用ports格式，例如200,302 or 200,204,401-429,501-503\n    listen: 0.0.0.0\n    users:\n      username1: password1\n      username2: password2\n    # \"certificate\" and \"private-key\" are required\n    certificate: ./server.crt # 证书 PEM 格式，或者 证书的路径\n    private-key: ./server.key\n    # 下面两项为mTLS配置项，如果client-auth-type设置为 \"verify-if-given\" 或 \"require-and-verify\" 则client-auth-cert必须不为空\n    # client-auth-type: \"\" # 可选值：\"\"、\"request\"、\"require-any\"、\"verify-if-given\"、\"require-and-verify\"\n    # client-auth-cert: string # 证书 PEM 格式，或者 证书的路径\n    # 如果填写则开启ech（可由 mihomo generate ech-keypair <明文域名> 生成）\n    # ech-key: |\n    #   -----BEGIN ECH KEYS-----\n    #   ACATwY30o/RKgD6hgeQxwrSiApLaCgU+HKh7B6SUrAHaDwBD/g0APwAAIAAgHjzK\n    #   madSJjYQIf9o1N5GXjkW4DEEeb17qMxHdwMdNnwADAABAAEAAQACAAEAAwAIdGVz\n    #   dC5jb20AAA==\n    #   -----END ECH KEYS-----\n    # padding-scheme: \"\" # https://github.com/anytls/anytls-go/blob/main/docs/protocol.md#cmdupdatepaddingscheme\n\n  - name: mieru-in-1\n    type: mieru\n    port: 10818 # 支持使用ports格式，例如200,302 or 200,204,401-429,501-503\n    listen: 0.0.0.0\n    transport: TCP # 支持 TCP 或者 UDP\n    users:\n      username1: password1\n      username2: password2\n    # 一个 base64 字符串用于微调网络行为\n    # traffic-pattern: \"\"\n    # 如果开启，且客户端不发送用户提示，代理服务器将拒绝连接\n    # user-hint-is-mandatory: false\n\n  - name: sudoku-in-1\n    type: sudoku\n    port: 8443 # 仅支持单端口\n    listen: 0.0.0.0\n    key: \"<server_key>\" # 如果你使用sudoku生成的ED25519密钥对，此处是密钥对中的公钥，当然，你也可以仅仅使用任意uuid充当key\n    aead-method: chacha20-poly1305 # 可选：chacha20-poly1305、aes-128-gcm、none（不建议；none 不提供 AEAD 保护）\n    padding-min: 1 # 最小填充率（0-100）\n    padding-max: 15 # 最大填充率（0-100，必须 >= padding-min）\n    table-type: prefer_ascii # 可选值：prefer_ascii、prefer_entropy、up_ascii_down_entropy、up_entropy_down_ascii\n    # custom-table: xpxvvpvv # 可选，自定义字节布局，必须包含2个x、2个p、4个v，可随意组合；只对 entropy 方向生效\n    # custom-tables: [\"xpxvvpvv\", \"vxpvxvvp\"] # 可选，自定义字节布局列表（x/v/p），用于多表轮换；非空时覆盖 custom-table\n    handshake-timeout: 5   # 可选（秒）\n    enable-pure-downlink: false # 可选：false=带宽优化下行；true=纯 Sudoku 下行\n    # 推荐：使用 httpmask 对象统一管理 HTTPMask 相关字段：\n    httpmask:\n      disable: false # true 禁用所有 HTTP 伪装/隧道\n      mode: legacy # 可选：legacy（默认）、stream（split-stream）、poll、auto（先 stream 再 poll）、ws（WebSocket 隧道）\n      # path-root: \"\" # 可选：HTTP 隧道端点一级路径前缀（双方需一致），例如 \"aabbcc\" 或 \"/aabbcc/\" => /aabbcc/session、/aabbcc/stream、/aabbcc/api/v1/upload、/aabbcc/ws\n    #\n    # fallback: \"127.0.0.1:80\" # 可选：用于可连接请求的回落转发，可与其他服务共端口\n\n\n\n  - name: trojan-in-1\n    type: trojan\n    port: 10819 # 支持使用ports格式，例如200,302 or 200,204,401-429,501-503\n    listen: 0.0.0.0\n    # rule: sub-rule-name1 # 默认使用 rules，如果未找到 sub-rule 则直接使用 rules\n    # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时，这里的 proxy 名称必须合法，否则会出错)\n    users:\n      - username: 1\n        password: 9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68\n    # ws-path: \"/\" # 如果不为空则开启 websocket 传输层\n    # grpc-service-name: \"GunService\" # 如果不为空则开启 grpc 传输层\n    # 下面两项如果填写则开启 tls（需要同时填写）\n    certificate: ./server.crt # 证书 PEM 格式，或者 证书的路径\n    private-key: ./server.key # 证书对应的私钥 PEM 格式，或者私钥路径\n    # 下面两项为mTLS配置项，如果client-auth-type设置为 \"verify-if-given\" 或 \"require-and-verify\" 则client-auth-cert必须不为空\n    # client-auth-type: \"\" # 可选值：\"\"、\"request\"、\"require-any\"、\"verify-if-given\"、\"require-and-verify\"\n    # client-auth-cert: string # 证书 PEM 格式，或者 证书的路径\n    # 如果填写则开启ech（可由 mihomo generate ech-keypair <明文域名> 生成）\n    # ech-key: |\n    #   -----BEGIN ECH KEYS-----\n    #   ACATwY30o/RKgD6hgeQxwrSiApLaCgU+HKh7B6SUrAHaDwBD/g0APwAAIAAgHjzK\n    #   madSJjYQIf9o1N5GXjkW4DEEeb17qMxHdwMdNnwADAABAAEAAQACAAEAAwAIdGVz\n    #   dC5jb20AAA==\n    #   -----END ECH KEYS-----\n    # 如果填写reality-config则开启reality（注意不可与certificate和private-key同时填写）\n    # reality-config:\n    #   dest: test.com:443\n    #   private-key: jNXHt1yRo0vDuchQlIP6Z0ZvjT3KtzVI-T4E7RoLJS0 # 可由 mihomo generate reality-keypair 命令生成\n    #   short-id:\n    #     - 0123456789abcdef\n    #   server-names:\n    #     - test.com\n    #   #下列两个 limit 为选填，可对未通过验证的回落连接限速，bytesPerSec 默认为 0 即不启用\n    #   #回落限速是一种特征，不建议启用，如果您是面板/一键脚本开发者，务必让这些参数随机化\n    #   limit-fallback-upload:\n    #     after-bytes: 0 # 传输指定字节后开始限速\n    #     bytes-per-sec: 0 # 基准速率（字节/秒）\n    #     burst-bytes-per-sec: 0 # 突发速率（字节/秒），大于 bytesPerSec 时生效\n    #   limit-fallback-download:\n    #     after-bytes: 0 # 传输指定字节后开始限速\n    #     bytes-per-sec: 0 # 基准速率（字节/秒）\n    #     burst-bytes-per-sec: 0 # 突发速率（字节/秒），大于 bytesPerSec 时生效\n    # ss-option: # like trojan-go's `shadowsocks` config\n    #   enabled: false\n    #   method: aes-128-gcm # aes-128-gcm/aes-256-gcm/chacha20-ietf-poly1305\n    #   password: \"example\"\n    ### 注意，对于trojan listener, 至少需要填写 “certificate和private-key” 或 “reality-config” 或 “ss-option” 的其中一项 ###\n\n  - name: hysteria2-in-1\n    type: hysteria2\n    port: 10820 # 支持使用ports格式，例如200,302 or 200,204,401-429,501-503\n    listen: 0.0.0.0\n    # rule: sub-rule-name1 # 默认使用 rules，如果未找到 sub-rule 则直接使用 rules\n    # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时，这里的 proxy 名称必须合法，否则会出错)\n    users:\n      00000000-0000-0000-0000-000000000000: PASSWORD_0\n      00000000-0000-0000-0000-000000000001: PASSWORD_1\n    #  certificate: ./server.crt # 证书 PEM 格式，或者 证书的路径\n    #  private-key: ./server.key # 证书对应的私钥 PEM 格式，或者私钥路径\n    #  下面两项为mTLS配置项，如果client-auth-type设置为 \"verify-if-given\" 或 \"require-and-verify\" 则client-auth-cert必须不为空\n    #  client-auth-type: \"\" # 可选值：\"\"、\"request\"、\"require-any\"、\"verify-if-given\"、\"require-and-verify\"\n    #  client-auth-cert: string # 证书 PEM 格式，或者 证书的路径\n    #  如果填写则开启ech（可由 mihomo generate ech-keypair <明文域名> 生成）\n    #  ech-key: |\n    #    -----BEGIN ECH KEYS-----\n    #    ACATwY30o/RKgD6hgeQxwrSiApLaCgU+HKh7B6SUrAHaDwBD/g0APwAAIAAgHjzK\n    #    madSJjYQIf9o1N5GXjkW4DEEeb17qMxHdwMdNnwADAABAAEAAQACAAEAAwAIdGVz\n    #    dC5jb20AAA==\n    #    -----END ECH KEYS-----\n    ##  up 和 down 均不写或为 0 则使用 BBR 流控\n    #  up: \"30 Mbps\" # 若不写单位，默认为 Mbps\n    #  down: \"200 Mbps\" # 若不写单位，默认为 Mbps\n    #  obfs: salamander # 默认为空，如果填写则开启 obfs，目前仅支持 salamander\n    #  obfs-password: yourpassword\n    #  bbr-profile: \"\" # Available: \"standard\", \"conservative\", \"aggressive\". Default: \"standard\"\n    #  max-idle-time: 15000\n    #  alpn:\n    #    - h3\n    #  ignore-client-bandwidth: false\n    # HTTP3 服务器认证失败时的行为 （URL 字符串配置）,如果 masquerade 未配置，则返回 404 页\n    #  masquerade: file:///var/www # 作为文件服务器\n    #  masquerade: http://127.0.0.1:8080\t#作为反向代理\n    #  masquerade: https://127.0.0.1:8080\t#作为反向代理\n\n  - name: trusttunnel-in-1\n    type: trusttunnel\n    port: 10821 # 支持使用ports格式，例如200,302 or 200,204,401-429,501-503\n    listen: 0.0.0.0\n    # rule: sub-rule-name1 # 默认使用 rules，如果未找到 sub-rule 则直接使用 rules\n    # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时，这里的 proxy 名称必须合法，否则会出错)\n    users:\n      - username: 1\n        password: 9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68\n    certificate: ./server.crt # 证书 PEM 格式，或者 证书的路径\n    private-key: ./server.key # 证书对应的私钥 PEM 格式，或者私钥路径\n    network: [\"tcp\", \"udp\"] # http2+http3\n    congestion-controller: bbr\n    # bbr-profile: \"\" # Available: \"standard\", \"conservative\", \"aggressive\". Default: \"standard\"\n    # 下面两项为mTLS配置项，如果client-auth-type设置为 \"verify-if-given\" 或 \"require-and-verify\" 则client-auth-cert必须不为空\n    # client-auth-type: \"\" # 可选值：\"\"、\"request\"、\"require-any\"、\"verify-if-given\"、\"require-and-verify\"\n    # client-auth-cert: string # 证书 PEM 格式，或者 证书的路径\n    # 如果填写则开启ech（可由 mihomo generate ech-keypair <明文域名> 生成）\n    # ech-key: |\n    #   -----BEGIN ECH KEYS-----\n    #   ACATwY30o/RKgD6hgeQxwrSiApLaCgU+HKh7B6SUrAHaDwBD/g0APwAAIAAgHjzK\n    #   madSJjYQIf9o1N5GXjkW4DEEeb17qMxHdwMdNnwADAABAAEAAQACAAEAAwAIdGVz\n    #   dC5jb20AAA==\n    #   -----END ECH KEYS-----\n\n  # 注意，listeners中的tun仅提供给高级用户使用，普通用户应使用顶层配置中的tun\n  - name: tun-in-1\n    type: tun\n    # rule: sub-rule-name1 # 默认使用 rules，如果未找到 sub-rule 则直接使用 rules\n    # proxy: proxy # 如果不为空则直接将该入站流量交由指定 proxy 处理 (当 proxy 不为空时，这里的 proxy 名称必须合法，否则会出错)\n    stack: system # gvisor / mixed\n    dns-hijack:\n    - 0.0.0.0:53 # 需要劫持的 DNS\n    # auto-detect-interface: false # 自动识别出口网卡\n    # auto-route: false # 配置路由表\n    # mtu: 9000 # 最大传输单元\n    inet4-address: # 必须手动设置 ipv4 地址段\n    - 198.19.0.1/30\n    inet6-address: # 必须手动设置 ipv6 地址段\n    - \"fdfe:dcba:9877::1/126\"\n    # strict-route: true # 将所有连接路由到 tun 来防止泄漏，但你的设备将无法其他设备被访问\n    # inet4-route-address: # 启用 auto-route 时使用自定义路由而不是默认路由\n    # - 0.0.0.0/1\n    # - 128.0.0.0/1\n    # inet6-route-address: # 启用 auto-route 时使用自定义路由而不是默认路由\n    # - \"::/1\"\n    # - \"8000::/1\"\n    # endpoint-independent-nat: false # 启用独立于端点的 NAT\n    # include-uid: # UID 规则仅在 Linux 下被支持，并且需要 auto-route\n    # - 0\n    # include-uid-range: # 限制被路由的的用户范围\n    # - 1000:99999\n    # exclude-uid: # 排除路由的的用户\n    # - 1000\n    # exclude-uid-range: # 排除路由的的用户范围\n    # - 1000:99999\n    # include-mac-address:\n    # - 00:11:22:33:44:55\n    # exclude-mac-address:\n    # - 00:11:22:33:44:55\n\n    # Android 用户和应用规则仅在 Android 下被支持\n    # 并且需要 auto-route\n\n    # include-android-user: # 限制被路由的 Android 用户\n    # - 0\n    # - 10\n    # include-package: # 限制被路由的 Android 应用包名\n    # - com.android.chrome\n    # exclude-package: # 排除被路由的 Android 应用包名\n    # - com.android.captiveportallogin\n    # disable-icmp-forwarding: true # 禁用 ICMP 转发，防止某些情况下的 ICMP 环回问题，ping 将不会显示真实的延迟\n# 入口配置与 Listener 等价，传入流量将和 socks,mixed 等入口一样按照 mode 所指定的方式进行匹配处理\n# shadowsocks,vmess 入口配置（传入流量将和 socks,mixed 等入口一样按照 mode 所指定的方式进行匹配处理）\n# ss-config: ss://2022-blake3-aes-256-gcm:vlmpIPSyHH6f4S8WVPdRIHIlzmB+GIRfoH3aNJ/t9Gg=@:23456\n# vmess-config: vmess://1:9d0cb9d0-964f-4ef6-897d-6c6b3ccf9e68@:12345\n\n# tuic 服务器入口（传入流量将和 socks,mixed 等入口一样按照 mode 所指定的方式进行匹配处理）\n# tuic-server:\n#  enable: true\n#  listen: 127.0.0.1:10443\n#  token:    # tuicV4 填写（可以同时填写 users）\n#    - TOKEN\n#  users:    # tuicV5 填写（可以同时填写 token）\n#    00000000-0000-0000-0000-000000000000: PASSWORD_0\n#    00000000-0000-0000-0000-000000000001: PASSWORD_1\n#  certificate: ./server.crt\n#  private-key: ./server.key\n#  congestion-controller: bbr\n#  bbr-profile: \"\" # Available: \"standard\", \"conservative\", \"aggressive\". Default: \"standard\"\n#  max-idle-time: 15000\n#  authentication-timeout: 1000\n#  alpn:\n#    - h3\n#  max-udp-relay-packet-size: 1500"
  },
  {
    "path": "core/Clash.Meta/flake.nix",
    "content": "{\n  description = \"Another Mihomo Kernel\";\n\n  inputs.nixpkgs.url = \"github:NixOS/nixpkgs/master\";\n\n  inputs.utils.url = \"github:numtide/flake-utils\";\n\n  outputs = { self, nixpkgs, utils }:\n    utils.lib.eachDefaultSystem\n      (system:\n        let\n          pkgs = import nixpkgs {\n            inherit system;\n            overlays = [ self.overlay ];\n          };\n        in\n        rec {\n          packages.default = pkgs.mihomo-meta;\n        }\n      ) //\n    (\n      let version = nixpkgs.lib.substring 0 8 self.lastModifiedDate or self.lastModified or \"19700101\"; in\n      {\n        overlay = final: prev: {\n\n          mihomo-meta = final.buildGo119Module {\n            pname = \"mihomo-meta\";\n            inherit version;\n            src = ./.;\n\n            vendorSha256 = \"sha256-W5oiPtTRin0731QQWr98xZ2Vpk97HYcBtKoi1OKZz+w=\";\n\n            # Do not build testing suit\n            excludedPackages = [ \"./test\" ];\n\n            CGO_ENABLED = 0;\n\n            ldflags = [\n              \"-s\"\n              \"-w\"\n              \"-X github.com/metacubex/mihomo/constant.Version=dev-${version}\"\n              \"-X github.com/metacubex/mihomo/constant.BuildTime=${version}\"\n            ];\n            \n            tags = [\n              \"with_gvisor\"\n            ];\n\n            # Network required \n            doCheck = false;\n\n            postInstall = ''\n              mv $out/bin/mihomo $out/bin/mihomo-meta\n            '';\n\n          };\n        };\n      }\n    );\n}\n\n"
  },
  {
    "path": "core/Clash.Meta/go.mod",
    "content": "module github.com/metacubex/mihomo\n\ngo 1.20\n\nrequire (\n\tgithub.com/bahlo/generic-list-go v0.2.0\n\tgithub.com/coreos/go-iptables v0.8.0\n\tgithub.com/dlclark/regexp2 v1.12.0\n\tgithub.com/enfein/mieru/v3 v3.31.0\n\tgithub.com/gobwas/ws v1.4.0\n\tgithub.com/gofrs/uuid/v5 v5.4.0\n\tgithub.com/golang/snappy v1.0.0\n\tgithub.com/metacubex/amneziawg-go v0.0.0-20251104174305-5a0e9f7e361d\n\tgithub.com/metacubex/bart v0.26.0\n\tgithub.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b\n\tgithub.com/metacubex/blake3 v0.1.0\n\tgithub.com/metacubex/chacha v0.1.5\n\tgithub.com/metacubex/chi v0.1.0\n\tgithub.com/metacubex/connect-ip-go v0.0.0-20260412152424-e1625567920a\n\tgithub.com/metacubex/cpu v0.1.1\n\tgithub.com/metacubex/edwards25519 v1.2.0\n\tgithub.com/metacubex/fswatch v0.1.1\n\tgithub.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759\n\tgithub.com/metacubex/http v0.1.6\n\tgithub.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604\n\tgithub.com/metacubex/mhurl v0.1.0\n\tgithub.com/metacubex/mlkem v0.1.0\n\tgithub.com/metacubex/quic-go v0.59.1-0.20260413153657-53bb22f2c306\n\tgithub.com/metacubex/randv2 v0.2.0\n\tgithub.com/metacubex/restls-client-go v0.1.7\n\tgithub.com/metacubex/sing v0.5.7\n\tgithub.com/metacubex/sing-mux v0.3.9\n\tgithub.com/metacubex/sing-quic v0.0.0-20260414034501-3ea3410d197a\n\tgithub.com/metacubex/sing-shadowsocks v0.2.12\n\tgithub.com/metacubex/sing-shadowsocks2 v0.2.7\n\tgithub.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2\n\tgithub.com/metacubex/sing-tun v0.4.18\n\tgithub.com/metacubex/sing-vmess v0.2.5\n\tgithub.com/metacubex/sing-wireguard v0.0.0-20260507084707-690d479ec947\n\tgithub.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141\n\tgithub.com/metacubex/ssh v0.1.0\n\tgithub.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443\n\tgithub.com/metacubex/tls v0.1.5\n\tgithub.com/metacubex/utls v1.8.4\n\tgithub.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f\n\tgithub.com/mroth/weightedrand/v2 v2.1.0\n\tgithub.com/openacid/low v0.1.21\n\tgithub.com/samber/lo v1.53.0\n\tgithub.com/sirupsen/logrus v1.9.4\n\tgithub.com/stretchr/testify v1.11.1\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1\n\tgithub.com/yosida95/uritemplate/v3 v3.0.2\n\tgitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7\n\tgo.uber.org/automaxprocs v1.6.0\n\tgo4.org/netipx v0.0.0-20231129151722-fdeea329fbba\n\tgopkg.in/yaml.v3 v3.0.1\n)\n\n// lastest version compatible with golang1.20\nrequire (\n\tgithub.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905\n\tgithub.com/klauspost/compress v1.17.9\n\tgithub.com/mdlayher/netlink v1.7.2\n\tgithub.com/miekg/dns v1.1.63\n\tgithub.com/oschwald/maxminddb-golang v1.12.0\n\tgolang.org/x/crypto v0.33.0\n\tgolang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e\n\tgolang.org/x/net v0.35.0\n\tgolang.org/x/sync v0.11.0\n\tgolang.org/x/sys v0.30.0\n\tgoogle.golang.org/protobuf v1.34.2\n)\n\nrequire (\n\tgithub.com/RyuaNerin/go-krypto v1.3.0 // indirect\n\tgithub.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 // indirect\n\tgithub.com/ajg/form v1.5.1 // indirect\n\tgithub.com/andybalholm/brotli v1.0.6 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/dunglas/httpsfv v1.0.2 // indirect\n\tgithub.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358 // indirect\n\tgithub.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 // indirect\n\tgithub.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 // indirect\n\tgithub.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 // indirect\n\tgithub.com/fsnotify/fsnotify v1.9.0 // indirect\n\tgithub.com/gaukas/godicttls v0.0.4 // indirect\n\tgithub.com/go-ole/go-ole v1.3.0 // indirect\n\tgithub.com/gobwas/httphead v0.1.0 // indirect\n\tgithub.com/gobwas/pool v0.2.1 // indirect\n\tgithub.com/google/btree v1.1.3 // indirect\n\tgithub.com/google/go-cmp v0.6.0 // indirect\n\tgithub.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect\n\tgithub.com/josharian/native v1.1.0 // indirect\n\tgithub.com/klauspost/cpuid/v2 v2.2.6 // indirect\n\tgithub.com/klauspost/reedsolomon v1.12.3 // indirect\n\tgithub.com/kr/text v0.2.0 // indirect\n\tgithub.com/mdlayher/socket v0.5.1 // indirect\n\tgithub.com/metacubex/ascon v0.1.0 // indirect\n\tgithub.com/metacubex/gvisor v0.0.0-20251227095601-261ec1326fe8 // indirect\n\tgithub.com/metacubex/hkdf v0.1.0 // indirect\n\tgithub.com/metacubex/hpke v0.1.0 // indirect\n\tgithub.com/metacubex/nftables v0.0.0-20260426003805-208c2c1ba2cb // indirect\n\tgithub.com/metacubex/qpack v0.6.0 // indirect\n\tgithub.com/metacubex/yamux v0.0.0-20250918083631-dd5f17c0be49 // indirect\n\tgithub.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect\n\tgithub.com/pierrec/lz4/v4 v4.1.14 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgithub.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect\n\tgithub.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect\n\tgithub.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect\n\tgithub.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e // indirect\n\tgithub.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect\n\tgithub.com/vishvananda/netns v0.0.5 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect\n\tgolang.org/x/mod v0.20.0 // indirect\n\tgolang.org/x/text v0.22.0 // indirect\n\tgolang.org/x/time v0.10.0 // indirect\n\tgolang.org/x/tools v0.24.0 // indirect\n)\n\n// for https://github.com/golang/protobuf/issues/1704\nreplace google.golang.org/protobuf => github.com/metacubex/protobuf-go v0.0.0-20260306035419-7ceee0674686\n"
  },
  {
    "path": "core/Clash.Meta/go.sum",
    "content": "github.com/RyuaNerin/go-krypto v1.3.0 h1:smavTzSMAx8iuVlGb4pEwl9MD2qicqMzuXR2QWp2/Pg=\ngithub.com/RyuaNerin/go-krypto v1.3.0/go.mod h1:9R9TU936laAIqAmjcHo/LsaXYOZlymudOAxjaBf62UM=\ngithub.com/RyuaNerin/testingutil v0.1.0 h1:IYT6JL57RV3U2ml3dLHZsVtPOP6yNK7WUVdzzlpNrss=\ngithub.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 h1:cDVUiFo+npB0ZASqnw4q90ylaVAbnYyx0JYqK4YcGok=\ngithub.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344/go.mod h1:9pIqrY6SXNL8vjRQE5Hd/OL5GyK/9MrGUWs87z/eFfk=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=\ngithub.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=\ngithub.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=\ngithub.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=\ngithub.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=\ngithub.com/coreos/go-iptables v0.8.0 h1:MPc2P89IhuVpLI7ETL/2tx3XZ61VeICZjYqDEgNsPRc=\ngithub.com/coreos/go-iptables v0.8.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=\ngithub.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/dlclark/regexp2 v1.12.0 h1:0j4c5qQmnC6XOWNjP3PIXURXN2gWx76rd3KvgdPkCz8=\ngithub.com/dlclark/regexp2 v1.12.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=\ngithub.com/dunglas/httpsfv v1.0.2 h1:iERDp/YAfnojSDJ7PW3dj1AReJz4MrwbECSSE59JWL0=\ngithub.com/dunglas/httpsfv v1.0.2/go.mod h1:zID2mqw9mFsnt7YC3vYQ9/cjq30q41W+1AnDwH8TiMg=\ngithub.com/enfein/mieru/v3 v3.31.0 h1:Fl2ocRCRXJzMygzdRjBHgqI996ZuIDHUmyQyovSf9sA=\ngithub.com/enfein/mieru/v3 v3.31.0/go.mod h1:zJBUCsi5rxyvHM8fjFf+GLaEl4OEjjBXr1s5F6Qd3hM=\ngithub.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358 h1:kXYqH/sL8dS/FdoFjr12ePjnLPorPo2FsnrHNuXSDyo=\ngithub.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I=\ngithub.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g=\ngithub.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391/go.mod h1:K2R7GhgxrlJzHw2qiPWsCZXf/kXEJN9PLnQK73Ll0po=\ngithub.com/ericlagergren/saferand v0.0.0-20220206064634-960a4dd2bc5c h1:RUzBDdZ+e/HEe2Nh8lYsduiPAZygUfVXJn0Ncj5sHMg=\ngithub.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 h1:tlDMEdcPRQKBEz5nGDMvswiajqh7k8ogWRlhRwKy5mY=\ngithub.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1/go.mod h1:4RfsapbGx2j/vU5xC/5/9qB3kn9Awp1YDiEnN43QrJ4=\ngithub.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 h1:fuGucgPk5dN6wzfnxl3D0D3rVLw4v2SbBT9jb4VnxzA=\ngithub.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010/go.mod h1:JtBcj7sBuTTRupn7c2bFspMDIObMJsVK8TeUvpShPok=\ngithub.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=\ngithub.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=\ngithub.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk=\ngithub.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=\ngithub.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=\ngithub.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=\ngithub.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=\ngithub.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=\ngithub.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=\ngithub.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=\ngithub.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs=\ngithub.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc=\ngithub.com/gofrs/uuid/v5 v5.4.0 h1:EfbpCTjqMuGyq5ZJwxqzn3Cbr2d0rUZU7v5ycAk/e/0=\ngithub.com/gofrs/uuid/v5 v5.4.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=\ngithub.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=\ngithub.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k=\ngithub.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=\ngithub.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I=\ngithub.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 h1:q3OEI9RaN/wwcx+qgGo6ZaoJkCiDYe/gjDLfq7lQQF4=\ngithub.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905/go.mod h1:VvGYjkZoJyKqlmT1yzakUs4mfKMNB0XdODP0+rdml6k=\ngithub.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=\ngithub.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=\ngithub.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=\ngithub.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=\ngithub.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=\ngithub.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=\ngithub.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=\ngithub.com/klauspost/reedsolomon v1.12.3 h1:tzUznbfc3OFwJaTebv/QdhnFf2Xvb7gZ24XaHLBPmdc=\ngithub.com/klauspost/reedsolomon v1.12.3/go.mod h1:3K5rXwABAvzGeR01r6pWZieUALXO/Tq7bFKGIb4m4WI=\ngithub.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=\ngithub.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=\ngithub.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=\ngithub.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=\ngithub.com/metacubex/amneziawg-go v0.0.0-20251104174305-5a0e9f7e361d h1:vAJ0ZT4aO803F1uw2roIA9yH7Sxzox34tVVyye1bz6c=\ngithub.com/metacubex/amneziawg-go v0.0.0-20251104174305-5a0e9f7e361d/go.mod h1:MsM/5czONyXMJ3PRr5DbQ4O/BxzAnJWOIcJdLzW6qHY=\ngithub.com/metacubex/ascon v0.1.0 h1:6ZWxmXYszT1XXtwkf6nxfFhc/OTtQ9R3Vyj1jN32lGM=\ngithub.com/metacubex/ascon v0.1.0/go.mod h1:eV5oim4cVPPdEL8/EYaTZ0iIKARH9pnhAK/fcT5Kacc=\ngithub.com/metacubex/bart v0.26.0 h1:d/bBTvVatfVWGfQbiDpYKI1bXUJgjaabB2KpK1Tnk6w=\ngithub.com/metacubex/bart v0.26.0/go.mod h1:DCcyfP4MC+Zy7sLK7XeGuMw+P5K9mIRsYOBgiE8icsI=\ngithub.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b h1:j7dadXD8I2KTmMt8jg1JcaP1ANL3JEObJPdANKcSYPY=\ngithub.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b/go.mod h1:+WmP0VJZDkDszvpa83HzfUp6QzARl/IKkMorH4+nODw=\ngithub.com/metacubex/blake3 v0.1.0 h1:KGnjh/56REO7U+cgZA8dnBhxdP7jByrG7hTP+bu6cqY=\ngithub.com/metacubex/blake3 v0.1.0/go.mod h1:CCkLdzFrqf7xmxCdhQFvJsRRV2mwOLDoSPg6vUTB9Uk=\ngithub.com/metacubex/chacha v0.1.5 h1:fKWMb/5c7ZrY8Uoqi79PPFxl+qwR7X/q0OrsAubyX2M=\ngithub.com/metacubex/chacha v0.1.5/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8=\ngithub.com/metacubex/chi v0.1.0 h1:rjNDyDj50nRpicG43CNkIw4ssiCbmDL8d7wJXKlUCsg=\ngithub.com/metacubex/chi v0.1.0/go.mod h1:zM5u5oMQt8b2DjvDHvzadKrP6B2ztmasL1YHRMbVV+g=\ngithub.com/metacubex/connect-ip-go v0.0.0-20260412152424-e1625567920a h1:Ph5UfTWDsGruZ+v95Df1ycTflQFmpZBFg2LUvj2kx/M=\ngithub.com/metacubex/connect-ip-go v0.0.0-20260412152424-e1625567920a/go.mod h1:xYC8Ik7/rN6no+vTRuWMEziGwm3brA0wNM/zZP9qhOQ=\ngithub.com/metacubex/cpu v0.1.1 h1:rRV5HGmeuGzjiKI3hYbL0dCd0qGwM7VUtk4ICXD06mI=\ngithub.com/metacubex/cpu v0.1.1/go.mod h1:09VEt4dSRLR+bOA8l4w4NDuzGZ8n5dkMv7e8axgEeTU=\ngithub.com/metacubex/edwards25519 v1.2.0 h1:pIQZLBsjQgg3Nl/c86YYFEUAbL5qQRnPq4LrgIw0KK4=\ngithub.com/metacubex/edwards25519 v1.2.0/go.mod h1:NCQF3J/Ki7382FJuokwsywEIIEI/gro/3smyXgQJsx0=\ngithub.com/metacubex/fswatch v0.1.1 h1:jqU7C/v+g0qc2RUFgmAOPoVvfl2BXXUXEumn6oQuxhU=\ngithub.com/metacubex/fswatch v0.1.1/go.mod h1:czrTT7Zlbz7vWft8RQu9Qqh+JoX+Nnb+UabuyN1YsgI=\ngithub.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvOzK9ubNCCkQ+ldc4YSH/rILn53l/xGBFHHI=\ngithub.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88=\ngithub.com/metacubex/gvisor v0.0.0-20251227095601-261ec1326fe8 h1:hUL81H0Ic/XIDkvtn9M1pmfDdfid7JzYQToY4Ps1TvQ=\ngithub.com/metacubex/gvisor v0.0.0-20251227095601-261ec1326fe8/go.mod h1:8LpS0IJW1VmWzUm3ylb0e2SK5QDm5lO/2qwWLZgRpBU=\ngithub.com/metacubex/hkdf v0.1.0 h1:fPA6VzXK8cU1foc/TOmGCDmSa7pZbxlnqhl3RNsthaA=\ngithub.com/metacubex/hkdf v0.1.0/go.mod h1:3seEfds3smgTAXqUGn+tgEJH3uXdsUjOiduG/2EtvZ4=\ngithub.com/metacubex/hpke v0.1.0 h1:gu2jUNhraehWi0P/z5HX2md3d7L1FhPQE6/Q0E9r9xQ=\ngithub.com/metacubex/hpke v0.1.0/go.mod h1:vfDm6gfgrwlXUxKDkWbcE44hXtmc1uxLDm2BcR11b3U=\ngithub.com/metacubex/http v0.1.6 h1:xvXuvXMCMxCWMF5nEJF4yiKvXL+p2atWMzs37e80m1I=\ngithub.com/metacubex/http v0.1.6/go.mod h1:Nxx0zZAo2AhRfanyL+fmmK6ACMtVsfpwIl1aFAik2Eg=\ngithub.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604 h1:hJwCVlE3ojViC35MGHB+FBr8TuIf3BUFn2EQ1VIamsI=\ngithub.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604/go.mod h1:lpmN3m269b3V5jFCWtffqBLS4U3QQoIid9ugtO+OhVc=\ngithub.com/metacubex/mhurl v0.1.0 h1:ZdW4Zxe3j3uJ89gNytOazHu6kbHn5owutN/VfXOI8GE=\ngithub.com/metacubex/mhurl v0.1.0/go.mod h1:2qpQImCbXoUs6GwJrjuEXKelPyoimsIXr07eNKZdS00=\ngithub.com/metacubex/mlkem v0.1.0 h1:wFClitonSFcmipzzQvax75beLQU+D7JuC+VK1RzSL8I=\ngithub.com/metacubex/mlkem v0.1.0/go.mod h1:amhaXZVeYNShuy9BILcR7P0gbeo/QLZsnqCdL8U2PDQ=\ngithub.com/metacubex/nftables v0.0.0-20260426003805-208c2c1ba2cb h1:wk6mHYPURSUvWcUv72gNP79oiylFsscBSDPJ6ieV6Iw=\ngithub.com/metacubex/nftables v0.0.0-20260426003805-208c2c1ba2cb/go.mod h1:73ZrCfhdkW4F2E2GAlta3km/S2RHhFNogCMtWZV2anQ=\ngithub.com/metacubex/protobuf-go v0.0.0-20260306035419-7ceee0674686 h1:PIXmYT2anQt9V8vdmwixtbIJxOpoPXJfIACHPjXEgnE=\ngithub.com/metacubex/protobuf-go v0.0.0-20260306035419-7ceee0674686/go.mod h1:eQV7juxFZIdRgjMxtVqP+6BssKoTZQ1RM0fc58BsCZY=\ngithub.com/metacubex/qpack v0.6.0 h1:YqClGIMOpiRYLjV1qOs483Od08MdPgRnHjt90FuaAKw=\ngithub.com/metacubex/qpack v0.6.0/go.mod h1:lKGSi7Xk94IMvHGOmxS9eIei3bvIqpOAImEBsaOwTkA=\ngithub.com/metacubex/quic-go v0.59.1-0.20260413153657-53bb22f2c306 h1:HlGLmLsWJMLSu0CMI9z/BmEnithB4oXM5Rom6/0Qxtg=\ngithub.com/metacubex/quic-go v0.59.1-0.20260413153657-53bb22f2c306/go.mod h1:oNzMrmylS897M3zSMuapIdwSwfq6F2qW01Z3NhVRJhk=\ngithub.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=\ngithub.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY=\ngithub.com/metacubex/restls-client-go v0.1.7 h1:eCwiXCTQb5WJu9IlgYvDBA1OgrINv58dEe7hcN5H15k=\ngithub.com/metacubex/restls-client-go v0.1.7/go.mod h1:BN/U52vPw7j8VTSh2vleD/MnmVKCov84mS5VcjVHH4g=\ngithub.com/metacubex/sing v0.5.7 h1:8OC+fhKFSv/l9ehEhJRaZZAOuthfZo68SteBVLe8QqM=\ngithub.com/metacubex/sing v0.5.7/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=\ngithub.com/metacubex/sing-mux v0.3.9 h1:/aoBD2+sK2qsXDlNDe3hkR0GZuFDtwIZhOeGUx9W0Yk=\ngithub.com/metacubex/sing-mux v0.3.9/go.mod h1:8bT7ZKT3clRrJjYc/x5CRYibC1TX/bK73a3r3+2E+Fc=\ngithub.com/metacubex/sing-quic v0.0.0-20260414034501-3ea3410d197a h1:977o0ZYYbiQAGuOxql7Q6UN3rEy59OyAE0tELq4gZfI=\ngithub.com/metacubex/sing-quic v0.0.0-20260414034501-3ea3410d197a/go.mod h1:6ayFGfzzBE85csgQkM3gf4neFq6s0losHlPRSxY+nuk=\ngithub.com/metacubex/sing-shadowsocks v0.2.12 h1:Wqzo8bYXrK5aWqxu/TjlTnYZzAKtKsaFQBdr6IHFaBE=\ngithub.com/metacubex/sing-shadowsocks v0.2.12/go.mod h1:2e5EIaw0rxKrm1YTRmiMnDulwbGxH9hAFlrwQLQMQkU=\ngithub.com/metacubex/sing-shadowsocks2 v0.2.7 h1:hSuuc0YpsfiqYqt1o+fP4m34BQz4e6wVj3PPBVhor3A=\ngithub.com/metacubex/sing-shadowsocks2 v0.2.7/go.mod h1:vOEbfKC60txi0ca+yUlqEwOGc3Obl6cnSgx9Gf45KjE=\ngithub.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MYPm7Wme3/OAY2FFzVq9d9GxPHOqu5AQfg/ddhI=\ngithub.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E=\ngithub.com/metacubex/sing-tun v0.4.18 h1:WRzAosG0YkT3aZq5RJWtF+RdCgeJ8EpooS5ZM1lkXo0=\ngithub.com/metacubex/sing-tun v0.4.18/go.mod h1:g4I/JNplDBhXLF+aQWgFbhNeJPSXQOWS9HvLeNvkgeA=\ngithub.com/metacubex/sing-vmess v0.2.5 h1:m9Zt5I27lB9fmLMZfism9sH2LcnAfShZfwSkf6/KJoE=\ngithub.com/metacubex/sing-vmess v0.2.5/go.mod h1:AwtlzUgf8COe9tRYAKqWZ+leDH7p5U98a0ZUpYehl8Q=\ngithub.com/metacubex/sing-wireguard v0.0.0-20260507084707-690d479ec947 h1:IB03BvRQtvjWScyOK5jSQVJYY8osmZXHL+4VCEFMWcM=\ngithub.com/metacubex/sing-wireguard v0.0.0-20260507084707-690d479ec947/go.mod h1:jpAkVLPnCpGSfNyVmj6Cq4YbuZsFepm/Dc+9BAOcR80=\ngithub.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141 h1:DK2l6m2Fc85H2BhiAPgbJygiWhesPlfGmF+9Vw6ARdk=\ngithub.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141/go.mod h1:/yI4OiGOSn0SURhZdJF3CbtPg3nwK700bG8TZLMBvAg=\ngithub.com/metacubex/ssh v0.1.0 h1:iGfr99qk/eMHzUnQ/0bTxXT8+8SWqLSHBWDHoAhngzw=\ngithub.com/metacubex/ssh v0.1.0/go.mod h1:NUtl0d+/f2cG9ECEpMM8iCVOpmggQlC13oLeDUONDlU=\ngithub.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443 h1:H6TnfM12tOoTizYE/qBHH3nEuibIelmHI+BVSxVJr8o=\ngithub.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=\ngithub.com/metacubex/tls v0.1.5 h1:ECcB83dj+zadnhlKcLnUUf1Sq6+vU0f/zoyU0+9oPTc=\ngithub.com/metacubex/tls v0.1.5/go.mod h1:0XeVdL0cBw+8i5Hqy3lVeP9IyD/LFTq02ExvHM6rzEM=\ngithub.com/metacubex/utls v1.8.4 h1:HmL9nUApDdWSkgUyodfwF6hSjtiwCGGdyhaSpEejKpg=\ngithub.com/metacubex/utls v1.8.4/go.mod h1:kncGGVhFaoGn5M3pFe3SXhZCzsbCJayNOH4UEqTKTko=\ngithub.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f h1:FGBPRb1zUabhPhDrlKEjQ9lgIwQ6cHL4x8M9lrERhbk=\ngithub.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f/go.mod h1:oPGcV994OGJedmmxrcK9+ni7jUEMGhR+uVQAdaduIP4=\ngithub.com/metacubex/yamux v0.0.0-20250918083631-dd5f17c0be49 h1:lhlqpYHopuTLx9xQt22kSA9HtnyTDmk5XjjQVCGHe2E=\ngithub.com/metacubex/yamux v0.0.0-20250918083631-dd5f17c0be49/go.mod h1:MBeEa9IVBphH7vc3LNtW6ZujVXFizotPo3OEiHQ+TNU=\ngithub.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=\ngithub.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=\ngithub.com/mroth/weightedrand/v2 v2.1.0 h1:o1ascnB1CIVzsqlfArQQjeMy1U0NcIbBO5rfd5E/OeU=\ngithub.com/mroth/weightedrand/v2 v2.1.0/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU=\ngithub.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 h1:1102pQc2SEPp5+xrS26wEaeb26sZy6k9/ZXlZN+eXE4=\ngithub.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7/go.mod h1:UqoUn6cHESlliMhOnKLWr+CBH+e3bazUPvFj1XZwAjs=\ngithub.com/openacid/errors v0.8.1/go.mod h1:GUQEJJOJE3W9skHm8E8Y4phdl2LLEN8iD7c5gcGgdx0=\ngithub.com/openacid/low v0.1.21 h1:Tr2GNu4N/+rGRYdOsEHOE89cxUIaDViZbVmKz29uKGo=\ngithub.com/openacid/low v0.1.21/go.mod h1:q+MsKI6Pz2xsCkzV4BLj7NR5M4EX0sGz5AqotpZDVh0=\ngithub.com/openacid/must v0.1.3/go.mod h1:luPiXCuJlEo3UUFQngVQokV0MPGryeYvtCbQPs3U1+I=\ngithub.com/openacid/testkeys v0.1.6/go.mod h1:MfA7cACzBpbiwekivj8StqX0WIRmqlMsci1c37CA3Do=\ngithub.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=\ngithub.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=\ngithub.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=\ngithub.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g=\ngithub.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=\ngithub.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=\ngithub.com/samber/lo v1.53.0 h1:t975lj2py4kJPQ6haz1QMgtId2gtmfktACxIXArw3HM=\ngithub.com/samber/lo v1.53.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=\ngithub.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b h1:rXHg9GrUEtWZhEkrykicdND3VPjlVbYiLdX9J7gimS8=\ngithub.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b/go.mod h1:X7qrxNQViEaAN9LNZOPl9PfvQtp3V3c7LTo0dvGi0fM=\ngithub.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c h1:DjKMC30y6yjG3IxDaeAj3PCoRr+IsO+bzyT+Se2m2Hk=\ngithub.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c/go.mod h1:NV/a66PhhWYVmUMaotlXJ8fIEFB98u+c8l/CQIEFLrU=\ngithub.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e h1:ur8uMsPIFG3i4Gi093BQITvwH9znsz2VUZmnmwHvpIo=\ngithub.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e/go.mod h1:+e5fBW3bpPyo+3uLo513gIUblc03egGjMM0+5GKbzK8=\ngithub.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=\ngithub.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=\ngithub.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA=\ngithub.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=\ngithub.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=\ngithub.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=\ngithub.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=\ngithub.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=\ngithub.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=\ngithub.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=\ngithub.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=\ngitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 h1:UNrDfkQqiEYzdMlNsVvBYOAJWZjdktqFE9tQh5BT2+4=\ngitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7/go.mod h1:E+rxHvJG9H6PUdzq9NRG6csuLN3XUx98BfGOVWNYnXs=\ngitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo=\ngitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ=\ngo.uber.org/automaxprocs v1.6.0 h1:O3y2/QNTOdbF+e/dpXNNW7Rx2hZ4sTIPyybbxyNqTUs=\ngo.uber.org/automaxprocs v1.6.0/go.mod h1:ifeIMSnPZuznNm6jmdzmU3/bfk01Fe2fotchwEFJ8r8=\ngo.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=\ngo4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=\ngo4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=\ngolang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=\ngolang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk=\ngolang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=\ngolang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=\ngolang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=\ngolang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=\ngolang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=\ngolang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=\ngolang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=\ngolang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=\ngolang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=\ngolang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "core/Clash.Meta/hub/executor/concurrent_load_limit.go",
    "content": "//go:build !386 && !amd64 && !arm64 && !arm64be && !mipsle && !mips\n\npackage executor\n\nconst concurrentCount = 5\n"
  },
  {
    "path": "core/Clash.Meta/hub/executor/concurrent_load_single.go",
    "content": "//go:build mips || mipsle\n\npackage executor\n\nconst concurrentCount = 1\n"
  },
  {
    "path": "core/Clash.Meta/hub/executor/concurrent_load_unlimit.go",
    "content": "//go:build 386 || amd64 || arm64 || arm64be\n\npackage executor\n\nimport \"math\"\n\nconst concurrentCount = math.MaxInt\n"
  },
  {
    "path": "core/Clash.Meta/hub/executor/executor.go",
    "content": "package executor\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"net/netip\"\n\t\"os\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"sync\"\n\t\"time\"\n\t_ \"unsafe\"\n\n\t\"github.com/metacubex/mihomo/adapter\"\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\t\"github.com/metacubex/mihomo/adapter/outboundgroup\"\n\t\"github.com/metacubex/mihomo/component/auth\"\n\t\"github.com/metacubex/mihomo/component/ca\"\n\t\"github.com/metacubex/mihomo/component/dialer\"\n\t\"github.com/metacubex/mihomo/component/geodata\"\n\tmihomoHttp \"github.com/metacubex/mihomo/component/http\"\n\t\"github.com/metacubex/mihomo/component/iface\"\n\t\"github.com/metacubex/mihomo/component/keepalive\"\n\t\"github.com/metacubex/mihomo/component/profile\"\n\t\"github.com/metacubex/mihomo/component/profile/cachefile\"\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\t\"github.com/metacubex/mihomo/component/resource\"\n\t\"github.com/metacubex/mihomo/component/sniffer\"\n\ttlsC \"github.com/metacubex/mihomo/component/tls\"\n\t\"github.com/metacubex/mihomo/component/trie\"\n\t\"github.com/metacubex/mihomo/component/updater\"\n\t\"github.com/metacubex/mihomo/config\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tP \"github.com/metacubex/mihomo/constant/provider\"\n\t\"github.com/metacubex/mihomo/dns\"\n\t\"github.com/metacubex/mihomo/listener\"\n\tauthStore \"github.com/metacubex/mihomo/listener/auth\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/inner\"\n\t\"github.com/metacubex/mihomo/listener/tproxy\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/ntp/ntp\"\n\t\"github.com/metacubex/mihomo/tunnel\"\n)\n\nvar mux sync.Mutex\n\nfunc readConfig(path string) ([]byte, error) {\n\tif _, err := os.Stat(path); os.IsNotExist(err) {\n\t\treturn nil, err\n\t}\n\tdata, err := os.ReadFile(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif len(data) == 0 {\n\t\treturn nil, fmt.Errorf(\"configuration file %s is empty\", path)\n\t}\n\n\treturn data, err\n}\n\n// Parse config with default config path\nfunc Parse() (*config.Config, error) {\n\treturn ParseWithPath(C.Path.Config())\n}\n\n// ParseWithPath parse config with custom config path\nfunc ParseWithPath(path string) (*config.Config, error) {\n\tbuf, err := readConfig(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn ParseWithBytes(buf)\n}\n\n// ParseWithBytes config with buffer\nfunc ParseWithBytes(buf []byte) (*config.Config, error) {\n\treturn config.Parse(buf)\n}\n\n// ApplyConfig dispatch configure to all parts without ExternalController\nfunc ApplyConfig(cfg *config.Config, force bool) {\n\tmux.Lock()\n\tdefer mux.Unlock()\n\tlog.SetLevel(cfg.General.LogLevel)\n\n\ttunnel.OnSuspend()\n\n\tca.ResetCertificate()\n\tfor _, c := range cfg.TLS.CustomTrustCert {\n\t\tif err := ca.AddCertificate(c); err != nil {\n\t\t\tlog.Warnln(\"%s\\nadd error: %s\", c, err.Error())\n\t\t}\n\t}\n\n\tupdateExperimental(cfg.Experimental)\n\tupdateUsers(cfg.Users)\n\tupdateProxies(cfg.Proxies, cfg.Providers)\n\tupdateRules(cfg.Rules, cfg.SubRules, cfg.RuleProviders)\n\tupdateSniffer(cfg.Sniffer)\n\tupdateHosts(cfg.Hosts)\n\tupdateGeneral(cfg.General, true)\n\tupdateNTP(cfg.NTP)\n\tupdateDNS(cfg.DNS, cfg.General.IPv6)\n\t//updateListeners(cfg.General, cfg.Listeners, force)\n\t//updateTun(cfg.General) // tun should not care \"force\"\n\tupdateIPTables(cfg)\n\tupdateTunnels(cfg.Tunnels)\n\n\ttunnel.OnInnerLoading()\n\n\tinitInnerTcp()\n\tloadProvider(cfg.Providers)\n\tupdateProfile(cfg)\n\tloadProvider(cfg.RuleProviders)\n\truntime.GC()\n\ttunnel.OnRunning()\n\tupdateUpdater(cfg)\n\n\tresolver.ResetConnection()\n}\n\nfunc initInnerTcp() {\n\tinner.New(tunnel.Tunnel)\n}\n\nfunc GetGeneral() *config.General {\n\tports := listener.GetPorts()\n\tvar authenticator []string\n\tif auth := authStore.Default.Authenticator(); auth != nil {\n\t\tauthenticator = auth.Users()\n\t}\n\n\tgeneral := &config.General{\n\t\tInbound: config.Inbound{\n\t\t\tPort:              ports.Port,\n\t\t\tSocksPort:         ports.SocksPort,\n\t\t\tRedirPort:         ports.RedirPort,\n\t\t\tTProxyPort:        ports.TProxyPort,\n\t\t\tMixedPort:         ports.MixedPort,\n\t\t\tTun:               listener.GetTunConf(),\n\t\t\tTuicServer:        listener.GetTuicConf(),\n\t\t\tShadowSocksConfig: ports.ShadowSocksConfig,\n\t\t\tVmessConfig:       ports.VmessConfig,\n\t\t\tAuthentication:    authenticator,\n\t\t\tSkipAuthPrefixes:  inbound.SkipAuthPrefixes(),\n\t\t\tLanAllowedIPs:     inbound.AllowedIPs(),\n\t\t\tLanDisAllowedIPs:  inbound.DisAllowedIPs(),\n\t\t\tAllowLan:          listener.AllowLan(),\n\t\t\tBindAddress:       listener.BindAddress(),\n\t\t\tInboundTfo:        inbound.Tfo(),\n\t\t\tInboundMPTCP:      inbound.MPTCP(),\n\t\t},\n\t\tMode:         tunnel.Mode(),\n\t\tUnifiedDelay: adapter.UnifiedDelay.Load(),\n\t\tLogLevel:     log.Level(),\n\t\tIPv6:         !resolver.DisableIPv6,\n\t\tInterface:    dialer.DefaultInterface.Load(),\n\t\tRoutingMark:  int(dialer.DefaultRoutingMark.Load()),\n\t\tGeoXUrl: config.GeoXUrl{\n\t\t\tGeoIp:   geodata.GeoIpUrl(),\n\t\t\tMmdb:    geodata.MmdbUrl(),\n\t\t\tASN:     geodata.ASNUrl(),\n\t\t\tGeoSite: geodata.GeoSiteUrl(),\n\t\t},\n\t\tGeoAutoUpdate:           updater.GeoAutoUpdate(),\n\t\tGeoUpdateInterval:       updater.GeoUpdateInterval(),\n\t\tGeodataMode:             geodata.GeodataMode(),\n\t\tGeodataLoader:           geodata.LoaderName(),\n\t\tGeositeMatcher:          geodata.SiteMatcherName(),\n\t\tTCPConcurrent:           dialer.GetTcpConcurrent(),\n\t\tFindProcessMode:         tunnel.FindProcessMode(),\n\t\tSniffing:                tunnel.IsSniffing(),\n\t\tGlobalClientFingerprint: tlsC.GetGlobalFingerprint(),\n\t\tGlobalUA:                mihomoHttp.UA(),\n\t\tETagSupport:             resource.ETag(),\n\t\tKeepAliveInterval:       int(keepalive.KeepAliveInterval() / time.Second),\n\t\tKeepAliveIdle:           int(keepalive.KeepAliveIdle() / time.Second),\n\t\tDisableKeepAlive:        keepalive.DisableKeepAlive(),\n\t}\n\n\treturn general\n}\n\nfunc updateListeners(general *config.General, listeners map[string]C.InboundListener, force bool) {\n\tlistener.PatchInboundListeners(listeners, tunnel.Tunnel, true)\n\tif !force {\n\t\treturn\n\t}\n\n\tallowLan := general.AllowLan\n\tlistener.SetAllowLan(allowLan)\n\tinbound.SetSkipAuthPrefixes(general.SkipAuthPrefixes)\n\tinbound.SetAllowedIPs(general.LanAllowedIPs)\n\tinbound.SetDisAllowedIPs(general.LanDisAllowedIPs)\n\n\tbindAddress := general.BindAddress\n\tlistener.SetBindAddress(bindAddress)\n\tlistener.ReCreateHTTP(general.Port, tunnel.Tunnel)\n\tlistener.ReCreateSocks(general.SocksPort, tunnel.Tunnel)\n\tlistener.ReCreateRedir(general.RedirPort, tunnel.Tunnel)\n\tlistener.ReCreateTProxy(general.TProxyPort, tunnel.Tunnel)\n\tlistener.ReCreateMixed(general.MixedPort, tunnel.Tunnel)\n\tlistener.ReCreateShadowSocks(general.ShadowSocksConfig, tunnel.Tunnel)\n\tlistener.ReCreateVmess(general.VmessConfig, tunnel.Tunnel)\n\tlistener.ReCreateTuic(general.TuicServer, tunnel.Tunnel)\n}\n\nfunc updateTun(general *config.General) {\n\tlistener.ReCreateTun(general.Tun, tunnel.Tunnel)\n}\n\nfunc updateExperimental(c *config.Experimental) {\n\tif c.QUICGoDisableGSO {\n\t\t_ = os.Setenv(\"QUIC_GO_DISABLE_GSO\", strconv.FormatBool(true))\n\t}\n\tif c.QUICGoDisableECN {\n\t\t_ = os.Setenv(\"QUIC_GO_DISABLE_ECN\", strconv.FormatBool(true))\n\t}\n\tresolver.SetIP4PEnable(c.IP4PEnable)\n}\n\nfunc updateNTP(c *config.NTP) {\n\tif c.Enable {\n\t\tntp.ReCreateNTPService(\n\t\t\tnet.JoinHostPort(c.Server, strconv.Itoa(c.Port)),\n\t\t\ttime.Duration(c.Interval),\n\t\t\tc.DialerProxy,\n\t\t\tc.WriteToSystem,\n\t\t)\n\t} else {\n\t\tntp.ReCreateNTPService(\"\", 0, \"\", false)\n\t}\n}\n\nfunc updateDNS(c *config.DNS, generalIPv6 bool) {\n\tif !c.Enable {\n\t\tresolver.DefaultResolver = nil\n\t\tresolver.DefaultHostMapper = nil\n\t\tresolver.DefaultService = nil\n\t\tresolver.ProxyServerHostResolver = nil\n\t\tresolver.DirectHostResolver = nil\n\t\tdns.ReCreateServer(\"\", nil)\n\t\treturn\n\t}\n\n\tipv6 := c.IPv6 && generalIPv6\n\tr := dns.NewResolver(dns.Config{\n\t\tMain:                 c.NameServer,\n\t\tFallback:             c.Fallback,\n\t\tIPv6:                 ipv6,\n\t\tIPv6Timeout:          c.IPv6Timeout,\n\t\tFallbackIPFilter:     c.FallbackIPFilter,\n\t\tFallbackDomainFilter: c.FallbackDomainFilter,\n\t\tDefault:              c.DefaultNameserver,\n\t\tPolicy:               c.NameServerPolicy,\n\t\tProxyServer:          c.ProxyServerNameserver,\n\t\tProxyServerPolicy:    c.ProxyServerPolicy,\n\t\tDirectServer:         c.DirectNameServer,\n\t\tDirectFollowPolicy:   c.DirectFollowPolicy,\n\t\tCacheAlgorithm:       c.CacheAlgorithm,\n\t\tCacheMaxSize:         c.CacheMaxSize,\n\t})\n\tm := dns.NewEnhancer(dns.EnhancerConfig{\n\t\tIPv6:          ipv6,\n\t\tEnhancedMode:  c.EnhancedMode,\n\t\tFakeIPPool:    c.FakeIPPool,\n\t\tFakeIPPool6:   c.FakeIPPool6,\n\t\tFakeIPSkipper: c.FakeIPSkipper,\n\t\tFakeIPTTL:     c.FakeIPTTL,\n\t\tUseHosts:      c.UseHosts,\n\t})\n\n\t// reuse cache of old host mapper\n\tif old := resolver.DefaultHostMapper; old != nil {\n\t\tm.PatchFrom(old.(*dns.ResolverEnhancer))\n\t}\n\n\ts := dns.NewService(r.Resolver, m)\n\n\tresolver.DefaultResolver = r\n\tresolver.DefaultHostMapper = m\n\tresolver.DefaultService = s\n\tresolver.UseSystemHosts = c.UseSystemHosts\n\n\tif r.ProxyResolver.Invalid() {\n\t\tresolver.ProxyServerHostResolver = r.ProxyResolver\n\t} else {\n\t\tresolver.ProxyServerHostResolver = r.Resolver\n\t}\n\n\tif r.DirectResolver.Invalid() {\n\t\tresolver.DirectHostResolver = r.DirectResolver\n\t} else {\n\t\tresolver.DirectHostResolver = r.Resolver\n\t}\n\n\tdns.ReCreateServer(c.Listen, s)\n}\n\nfunc updateHosts(tree *trie.DomainTrie[resolver.HostValue]) {\n\tresolver.DefaultHosts = resolver.NewHosts(tree)\n}\n\nfunc updateProxies(proxies map[string]C.Proxy, providers map[string]P.ProxyProvider) {\n\ttunnel.UpdateProxies(proxies, providers)\n}\n\nfunc updateRules(rules []C.Rule, subRules map[string][]C.Rule, ruleProviders map[string]P.RuleProvider) {\n\ttunnel.UpdateRules(rules, subRules, ruleProviders)\n}\n\nfunc loadProvider[T P.Provider](providers map[string]T) {\n\tload := func(pv T) {\n\t\tname := pv.Name()\n\t\tif pv.VehicleType() == P.Compatible {\n\t\t\tlog.Infoln(\"Start initial compatible provider %s\", name)\n\t\t} else {\n\t\t\tlog.Infoln(\"Start initial provider %s\", name)\n\t\t}\n\n\t\tif err := pv.Initial(); err != nil {\n\t\t\tswitch pv.Type() {\n\t\t\tcase P.Proxy:\n\t\t\t\t{\n\t\t\t\t\tlog.Warnln(\"initial proxy provider %s error: %v\", name, err)\n\t\t\t\t}\n\t\t\tcase P.Rule:\n\t\t\t\t{\n\t\t\t\t\tlog.Warnln(\"initial rule provider %s error: %v\", name, err)\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tif DefaultProviderLoadedHook != nil {\n\t\t\t\tDefaultProviderLoadedHook(name)\n\t\t\t}\n\t\t}\n\t}\n\n\twg := sync.WaitGroup{}\n\tch := make(chan struct{}, concurrentCount)\n\tfor _, pv := range providers {\n\t\tpv := pv\n\t\twg.Add(1)\n\t\tch <- struct{}{}\n\t\tgo func() {\n\t\t\tdefer func() { <-ch; wg.Done() }()\n\t\t\tload(pv)\n\t\t}()\n\t}\n\twg.Wait()\n}\n\nfunc updateSniffer(snifferConfig *sniffer.Config) {\n\tdispatcher, err := sniffer.NewDispatcher(snifferConfig)\n\tif err != nil {\n\t\tlog.Warnln(\"initial sniffer failed, err:%v\", err)\n\t}\n\n\ttunnel.UpdateSniffer(dispatcher)\n\n\tif snifferConfig.Enable {\n\t\tlog.Infoln(\"Sniffer is loaded and working\")\n\t} else {\n\t\tlog.Infoln(\"Sniffer is closed\")\n\t}\n}\n\nfunc updateTunnels(tunnels []LC.Tunnel) {\n\tlistener.PatchTunnel(tunnels, tunnel.Tunnel)\n}\n\nfunc updateUpdater(cfg *config.Config) {\n\tgeneral := cfg.General\n\tupdater.SetGeoAutoUpdate(general.GeoAutoUpdate)\n\tupdater.SetGeoUpdateInterval(general.GeoUpdateInterval)\n\n\tcontroller := cfg.Controller\n\tupdater.DefaultUiUpdater = updater.NewUiUpdater(controller.ExternalUI, controller.ExternalUIURL, controller.ExternalUIName)\n\tupdater.DefaultUiUpdater.AutoDownloadUI()\n}\n\n//go:linkname temporaryUpdateGeneral github.com/metacubex/mihomo/config.temporaryUpdateGeneral\nfunc temporaryUpdateGeneral(general *config.General) func() {\n\toldGeneral := GetGeneral()\n\tupdateGeneral(general, false)\n\treturn func() {\n\t\tupdateGeneral(oldGeneral, false)\n\t}\n}\n\nfunc updateGeneral(general *config.General, logging bool) {\n\ttunnel.SetMode(general.Mode)\n\ttunnel.SetFindProcessMode(general.FindProcessMode)\n\tresolver.DisableIPv6 = !general.IPv6\n\n\tdialer.SetTcpConcurrent(general.TCPConcurrent)\n\tif logging && general.TCPConcurrent {\n\t\tlog.Infoln(\"Use tcp concurrent\")\n\t}\n\n\tinbound.SetTfo(general.InboundTfo)\n\tinbound.SetMPTCP(general.InboundMPTCP)\n\n\tkeepalive.SetKeepAliveIdle(time.Duration(general.KeepAliveIdle) * time.Second)\n\tkeepalive.SetKeepAliveInterval(time.Duration(general.KeepAliveInterval) * time.Second)\n\tkeepalive.SetDisableKeepAlive(general.DisableKeepAlive)\n\n\tadapter.UnifiedDelay.Store(general.UnifiedDelay)\n\n\tdialer.DefaultInterface.Store(general.Interface)\n\tdialer.DefaultRoutingMark.Store(int32(general.RoutingMark))\n\tif logging && general.RoutingMark > 0 {\n\t\tlog.Infoln(\"Use routing mark: %#x\", general.RoutingMark)\n\t}\n\n\tiface.FlushCache()\n\n\tgeodata.SetGeodataMode(general.GeodataMode)\n\tgeodata.SetLoader(general.GeodataLoader)\n\tgeodata.SetSiteMatcher(general.GeositeMatcher)\n\tgeodata.SetGeoIpUrl(general.GeoXUrl.GeoIp)\n\tgeodata.SetGeoSiteUrl(general.GeoXUrl.GeoSite)\n\tgeodata.SetMmdbUrl(general.GeoXUrl.Mmdb)\n\tgeodata.SetASNUrl(general.GeoXUrl.ASN)\n\tmihomoHttp.SetUA(general.GlobalUA)\n\tresource.SetETag(general.ETagSupport)\n\n\tif general.GlobalClientFingerprint != \"\" {\n\t\tlog.Warnln(\"The `global-client-fingerprint` configuration is deprecated, please set `client-fingerprint` directly on the proxy instead\")\n\t}\n\ttlsC.SetGlobalFingerprint(general.GlobalClientFingerprint)\n}\n\nfunc updateUsers(users []auth.AuthUser) {\n\tauthenticator := auth.NewAuthenticator(users)\n\tauthStore.Default.SetAuthenticator(authenticator)\n\tif authenticator != nil {\n\t\tlog.Infoln(\"Authentication of local server updated\")\n\t}\n}\n\nfunc updateProfile(cfg *config.Config) {\n\tprofileCfg := cfg.Profile\n\n\tprofile.StoreSelected.Store(profileCfg.StoreSelected)\n\tif profileCfg.StoreSelected {\n\t\tpatchSelectGroup(cfg.Proxies)\n\t}\n}\n\nfunc patchSelectGroup(proxies map[string]C.Proxy) {\n\tmapping := cachefile.Cache().SelectedMap()\n\tif mapping == nil {\n\t\treturn\n\t}\n\n\tfor name, outbound := range proxies {\n\t\tselector, ok := outbound.Adapter().(outboundgroup.SelectAble)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tselected, exist := mapping[name]\n\t\tif !exist {\n\t\t\tcontinue\n\t\t}\n\n\t\tif outbound.Type() == C.URLTest {\n\t\t\tcachefile.Cache().SetSelected(name, \"\")\n\t\t\tselector.ForceSet(\"\")\n\t\t\tcontinue\n\t\t}\n\n\t\tselector.ForceSet(selected)\n\t}\n}\n\nfunc updateIPTables(cfg *config.Config) {\n\ttproxy.CleanupTProxyIPTables()\n\n\tiptables := cfg.IPTables\n\tif runtime.GOOS != \"linux\" || !iptables.Enable {\n\t\treturn\n\t}\n\n\tvar err error\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tlog.Errorln(\"[IPTABLES] setting iptables failed: %s\", err.Error())\n\t\t\tos.Exit(2)\n\t\t}\n\t}()\n\n\tif cfg.General.Tun.Enable {\n\t\terr = fmt.Errorf(\"when tun is enabled, iptables cannot be set automatically\")\n\t\treturn\n\t}\n\n\tvar (\n\t\tinboundInterface = \"lo\"\n\t\tbypass           = iptables.Bypass\n\t\ttProxyPort       = cfg.General.TProxyPort\n\t\tdnsCfg           = cfg.DNS\n\t\tDnsRedirect      = iptables.DnsRedirect\n\n\t\tdnsPort netip.AddrPort\n\t)\n\n\tif tProxyPort == 0 {\n\t\terr = fmt.Errorf(\"tproxy-port must be greater than zero\")\n\t\treturn\n\t}\n\n\tif DnsRedirect {\n\t\tif !dnsCfg.Enable {\n\t\t\terr = fmt.Errorf(\"DNS server must be enable\")\n\t\t\treturn\n\t\t}\n\n\t\tdnsPort, err = netip.ParseAddrPort(dnsCfg.Listen)\n\t\tif err != nil {\n\t\t\terr = fmt.Errorf(\"DNS server must be correct\")\n\t\t\treturn\n\t\t}\n\t}\n\n\tif iptables.InboundInterface != \"\" {\n\t\tinboundInterface = iptables.InboundInterface\n\t}\n\n\tdialer.DefaultRoutingMark.CompareAndSwap(0, 2158)\n\n\terr = tproxy.SetTProxyIPTables(inboundInterface, bypass, uint16(tProxyPort), DnsRedirect, dnsPort.Port())\n\tif err != nil {\n\t\treturn\n\t}\n\n\tlog.Infoln(\"[IPTABLES] Setting iptables completed\")\n}\n\nfunc Shutdown() {\n\tlistener.Cleanup()\n\ttproxy.CleanupTProxyIPTables()\n\tresolver.StoreFakePoolState()\n\n\tlog.Warnln(\"Mihomo shutting down\")\n}"
  },
  {
    "path": "core/Clash.Meta/hub/executor/patch.go",
    "content": "package executor\n\ntype ProviderLoadedHook func(providerName string)\n\nvar DefaultProviderLoadedHook ProviderLoadedHook\n"
  },
  {
    "path": "core/Clash.Meta/hub/hub.go",
    "content": "package hub\n\nimport (\n\t\"github.com/metacubex/mihomo/config\"\n\t\"github.com/metacubex/mihomo/hub/executor\"\n\t\"github.com/metacubex/mihomo/hub/route\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\ntype Option func(*config.Config)\n\nfunc WithExternalUI(externalUI string) Option {\n\treturn func(cfg *config.Config) {\n\t\tcfg.Controller.ExternalUI = externalUI\n\t}\n}\n\nfunc WithExternalController(externalController string) Option {\n\treturn func(cfg *config.Config) {\n\t\tcfg.Controller.ExternalController = externalController\n\t}\n}\n\nfunc WithExternalControllerUnix(externalControllerUnix string) Option {\n\treturn func(cfg *config.Config) {\n\t\tcfg.Controller.ExternalControllerUnix = externalControllerUnix\n\t}\n}\n\nfunc WithExternalControllerPipe(externalControllerPipe string) Option {\n\treturn func(cfg *config.Config) {\n\t\tcfg.Controller.ExternalControllerPipe = externalControllerPipe\n\t}\n}\n\nfunc WithSecret(secret string) Option {\n\treturn func(cfg *config.Config) {\n\t\tcfg.Controller.Secret = secret\n\t}\n}\n\n// ApplyConfig dispatch configure to all parts include ExternalController\nfunc ApplyConfig(cfg *config.Config) {\n\tapplyRoute(cfg)\n\texecutor.ApplyConfig(cfg, true)\n}\n\nfunc applyRoute(cfg *config.Config) {\n\tif cfg.Controller.ExternalUI != \"\" {\n\t\troute.SetUIPath(cfg.Controller.ExternalUI)\n\t}\n\troute.ReCreateServer(&route.Config{\n\t\tAddr:           cfg.Controller.ExternalController,\n\t\tTLSAddr:        cfg.Controller.ExternalControllerTLS,\n\t\tUnixAddr:       cfg.Controller.ExternalControllerUnix,\n\t\tPipeAddr:       cfg.Controller.ExternalControllerPipe,\n\t\tSecret:         cfg.Controller.Secret,\n\t\tCertificate:    cfg.TLS.Certificate,\n\t\tPrivateKey:     cfg.TLS.PrivateKey,\n\t\tClientAuthType: cfg.TLS.ClientAuthType,\n\t\tClientAuthCert: cfg.TLS.ClientAuthCert,\n\t\tEchKey:         cfg.TLS.EchKey,\n\t\tDohServer:      cfg.Controller.ExternalDohServer,\n\t\tIsDebug:        cfg.General.LogLevel == log.DEBUG,\n\t\tCors: route.Cors{\n\t\t\tAllowOrigins:        cfg.Controller.Cors.AllowOrigins,\n\t\t\tAllowPrivateNetwork: cfg.Controller.Cors.AllowPrivateNetwork,\n\t\t},\n\t})\n}\n\n// Parse call at the beginning of mihomo\nfunc Parse(configBytes []byte, options ...Option) error {\n\tvar cfg *config.Config\n\tvar err error\n\n\tif len(configBytes) != 0 {\n\t\tcfg, err = executor.ParseWithBytes(configBytes)\n\t} else {\n\t\tcfg, err = executor.Parse()\n\t}\n\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, option := range options {\n\t\toption(cfg)\n\t}\n\n\tApplyConfig(cfg)\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/hub/route/cache.go",
    "content": "package route\n\nimport (\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\n\t\"github.com/metacubex/chi\"\n\t\"github.com/metacubex/chi/render\"\n\t\"github.com/metacubex/http\"\n)\n\nfunc cacheRouter() http.Handler {\n\tr := chi.NewRouter()\n\tr.Post(\"/fakeip/flush\", flushFakeIPPool)\n\tr.Post(\"/dns/flush\", flushDnsCache)\n\treturn r\n}\n\nfunc flushFakeIPPool(w http.ResponseWriter, r *http.Request) {\n\terr := resolver.FlushFakeIP()\n\tif err != nil {\n\t\trender.Status(r, http.StatusBadRequest)\n\t\trender.JSON(w, r, newError(err.Error()))\n\t\treturn\n\t}\n\trender.NoContent(w, r)\n}\n\nfunc flushDnsCache(w http.ResponseWriter, r *http.Request) {\n\tresolver.ClearCache()\n\trender.NoContent(w, r)\n}\n"
  },
  {
    "path": "core/Clash.Meta/hub/route/common.go",
    "content": "package route\n\nimport (\n\t\"bufio\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\n\t\"github.com/metacubex/chi\"\n\t\"github.com/metacubex/http\"\n)\n\n// When name is composed of a partial escape string, Golang does not unescape it\nfunc getEscapeParam(r *http.Request, paramName string) string {\n\tparam := chi.URLParam(r, paramName)\n\tif newParam, err := url.PathUnescape(param); err == nil {\n\t\tparam = newParam\n\t}\n\treturn param\n}\n\n// wsUpgrade upgrades http connection to the websocket connection.\n//\n// It hijacks net.Conn from w and returns received net.Conn and\n// bufio.ReadWriter.\nfunc wsUpgrade(r *http.Request, w http.ResponseWriter) (conn net.Conn, rw *bufio.ReadWriter, err error) {\n\t// See https://tools.ietf.org/html/rfc6455#section-4.1\n\t// The method of the request MUST be GET, and the HTTP version MUST be at least 1.1.\n\tvar nonce string\n\tif r.Method != http.MethodGet {\n\t\terr = errors.New(\"handshake error: bad HTTP request method\")\n\t\tbody := err.Error()\n\t\tw.Header().Set(\"Content-Type\", \"text/plain; charset=utf-8\")\n\t\tw.Header().Set(\"Content-Length\", strconv.Itoa(len(body)))\n\t\tw.WriteHeader(http.StatusMethodNotAllowed)\n\t\tw.Write([]byte(body))\n\t\treturn nil, nil, err\n\t} else if r.ProtoMajor < 1 || (r.ProtoMajor == 1 && r.ProtoMinor < 1) {\n\t\terr = errors.New(\"handshake error: bad HTTP protocol version\")\n\t\tbody := err.Error()\n\t\tw.Header().Set(\"Content-Type\", \"text/plain; charset=utf-8\")\n\t\tw.Header().Set(\"Content-Length\", strconv.Itoa(len(body)))\n\t\tw.WriteHeader(http.StatusHTTPVersionNotSupported)\n\t\tw.Write([]byte(body))\n\t\treturn nil, nil, err\n\t} else if r.Host == \"\" {\n\t\terr = errors.New(\"handshake error: bad Host header\")\n\t\tbody := err.Error()\n\t\tw.Header().Set(\"Content-Type\", \"text/plain; charset=utf-8\")\n\t\tw.Header().Set(\"Content-Length\", strconv.Itoa(len(body)))\n\t\tw.WriteHeader(http.StatusBadRequest)\n\t\tw.Write([]byte(body))\n\t\treturn nil, nil, err\n\t} else if u := r.Header.Get(\"Upgrade\"); u != \"websocket\" && !strings.EqualFold(u, \"websocket\") {\n\t\terr = errors.New(\"handshake error: bad Upgrade header\")\n\t\tbody := err.Error()\n\t\tw.Header().Set(\"Content-Type\", \"text/plain; charset=utf-8\")\n\t\tw.Header().Set(\"Content-Length\", strconv.Itoa(len(body)))\n\t\tw.WriteHeader(http.StatusBadRequest)\n\t\tw.Write([]byte(body))\n\t\treturn nil, nil, err\n\t} else if c := r.Header.Get(\"Connection\"); c != \"Upgrade\" && !strings.Contains(strings.ToLower(c), \"upgrade\") {\n\t\terr = errors.New(\"handshake error: bad Connection header\")\n\t\tbody := err.Error()\n\t\tw.Header().Set(\"Content-Type\", \"text/plain; charset=utf-8\")\n\t\tw.Header().Set(\"Content-Length\", strconv.Itoa(len(body)))\n\t\tw.WriteHeader(http.StatusBadRequest)\n\t\tw.Write([]byte(body))\n\t\treturn nil, nil, err\n\t} else if nonce = r.Header.Get(\"Sec-WebSocket-Key\"); len(nonce) != 24 {\n\t\terr = errors.New(\"handshake error: bad Sec-WebSocket-Key header\")\n\t\tbody := err.Error()\n\t\tw.Header().Set(\"Content-Type\", \"text/plain; charset=utf-8\")\n\t\tw.Header().Set(\"Content-Length\", strconv.Itoa(len(body)))\n\t\tw.WriteHeader(http.StatusBadRequest)\n\t\tw.Write([]byte(body))\n\t\treturn nil, nil, err\n\t} else if v := r.Header.Get(\"Sec-WebSocket-Version\"); v != \"13\" {\n\t\terr = errors.New(\"handshake error: bad Sec-WebSocket-Version header\")\n\t\tbody := err.Error()\n\t\tw.Header().Set(\"Content-Type\", \"text/plain; charset=utf-8\")\n\t\tw.Header().Set(\"Content-Length\", strconv.Itoa(len(body)))\n\t\tif v != \"\" {\n\t\t\t// According to RFC6455:\n\t\t\t// If this version does not match a version understood by the server, the\n\t\t\t// server MUST abort the WebSocket handshake described in this section and\n\t\t\t// instead send an appropriate HTTP error code (such as 426 Upgrade Required)\n\t\t\t// and a |Sec-WebSocket-Version| header field indicating the version(s) the\n\t\t\t// server is capable of understanding.\n\t\t\tw.Header().Set(\"Sec-WebSocket-Version\", \"13\")\n\t\t\tw.WriteHeader(http.StatusUpgradeRequired)\n\t\t} else {\n\t\t\tw.WriteHeader(http.StatusBadRequest)\n\t\t}\n\t\tw.Write([]byte(body))\n\t\treturn nil, nil, err\n\t}\n\n\tconn, rw, err = http.NewResponseController(w).Hijack()\n\tif err != nil {\n\t\tbody := err.Error()\n\t\tw.Header().Set(\"Content-Type\", \"text/plain; charset=utf-8\")\n\t\tw.Header().Set(\"Content-Length\", strconv.Itoa(len(body)))\n\t\tw.WriteHeader(http.StatusInternalServerError)\n\t\tw.Write([]byte(body))\n\t\treturn nil, nil, err\n\t}\n\n\t// Clear deadlines set by server.\n\tconn.SetDeadline(time.Time{})\n\n\trw.Writer.WriteString(\"HTTP/1.1 101 Switching Protocols\\r\\n\")\n\theader := http.Header{}\n\theader.Set(\"Upgrade\", \"websocket\")\n\theader.Set(\"Connection\", \"Upgrade\")\n\theader.Set(\"Sec-WebSocket-Accept\", N.GetWebSocketSecAccept(nonce))\n\theader.Write(rw.Writer)\n\trw.Writer.WriteString(\"\\r\\n\")\n\terr = rw.Writer.Flush()\n\n\treturn conn, rw, err\n}\n\n// wsWriteServerMessage writes message to w, considering that caller represents server side.\nfunc wsWriteServerMessage(w io.Writer, op byte, p []byte) error {\n\tdataLen := len(p)\n\n\t// Make slice of bytes with capacity 14 that could hold any header.\n\tbts := make([]byte, 14)\n\n\tbts[0] |= 0x80   //FIN\n\tbts[0] |= 0 << 4 //RSV\n\tbts[0] |= op     //OPCODE\n\n\tvar n int\n\tswitch {\n\tcase dataLen < 126:\n\t\tbts[1] = byte(dataLen)\n\t\tn = 2\n\tcase dataLen < 65536:\n\t\tbts[1] = 126\n\t\tbinary.BigEndian.PutUint16(bts[2:4], uint16(dataLen))\n\t\tn = 4\n\tdefault:\n\t\tbts[1] = 127\n\t\tbinary.BigEndian.PutUint64(bts[2:10], uint64(dataLen))\n\t\tn = 10\n\t}\n\n\t_, err := w.Write(bts[:n])\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = w.Write(p)\n\treturn err\n}\n\n// wsWriteServerText is the same as wsWriteServerMessage with ws.OpText.\nfunc wsWriteServerText(w io.Writer, p []byte) error {\n\tconst opText = 0x1\n\treturn wsWriteServerMessage(w, opText, p)\n}\n\n// wsWriteServerBinary is the same as wsWriteServerMessage with ws.OpBinary.\nfunc wsWriteServerBinary(w io.Writer, p []byte) error {\n\tconst opBinary = 0x2\n\treturn wsWriteServerMessage(w, opBinary, p)\n}\n"
  },
  {
    "path": "core/Clash.Meta/hub/route/configs.go",
    "content": "package route\n\nimport (\n\t\"net/netip\"\n\t\"path/filepath\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\t\"github.com/metacubex/mihomo/component/dialer\"\n\t\"github.com/metacubex/mihomo/component/process\"\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\t\"github.com/metacubex/mihomo/component/updater\"\n\t\"github.com/metacubex/mihomo/config\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/hub/executor\"\n\t\"github.com/metacubex/mihomo/listener\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/tunnel\"\n\n\t\"github.com/metacubex/chi\"\n\t\"github.com/metacubex/chi/render\"\n\t\"github.com/metacubex/http\"\n)\n\nfunc configRouter() http.Handler {\n\tr := chi.NewRouter()\n\tr.Get(\"/\", getConfigs)\n\tif !embedMode { // disallow update/patch configs in embed mode\n\t\tr.Put(\"/\", updateConfigs)\n\t\tr.Post(\"/geo\", updateGeoDatabases)\n\t\tr.Patch(\"/\", patchConfigs)\n\t}\n\treturn r\n}\n\ntype configSchema struct {\n\tPort              *int                     `json:\"port\"`\n\tSocksPort         *int                     `json:\"socks-port\"`\n\tRedirPort         *int                     `json:\"redir-port\"`\n\tTProxyPort        *int                     `json:\"tproxy-port\"`\n\tMixedPort         *int                     `json:\"mixed-port\"`\n\tTun               *tunSchema               `json:\"tun\"`\n\tTuicServer        *tuicServerSchema        `json:\"tuic-server\"`\n\tShadowSocksConfig *string                  `json:\"ss-config\"`\n\tVmessConfig       *string                  `json:\"vmess-config\"`\n\tTcptunConfig      *string                  `json:\"tcptun-config\"`\n\tUdptunConfig      *string                  `json:\"udptun-config\"`\n\tAllowLan          *bool                    `json:\"allow-lan\"`\n\tSkipAuthPrefixes  *[]netip.Prefix          `json:\"skip-auth-prefixes\"`\n\tLanAllowedIPs     *[]netip.Prefix          `json:\"lan-allowed-ips\"`\n\tLanDisAllowedIPs  *[]netip.Prefix          `json:\"lan-disallowed-ips\"`\n\tBindAddress       *string                  `json:\"bind-address\"`\n\tMode              *tunnel.TunnelMode       `json:\"mode\"`\n\tLogLevel          *log.LogLevel            `json:\"log-level\"`\n\tIPv6              *bool                    `json:\"ipv6\"`\n\tSniffing          *bool                    `json:\"sniffing\"`\n\tTcpConcurrent     *bool                    `json:\"tcp-concurrent\"`\n\tFindProcessMode   *process.FindProcessMode `json:\"find-process-mode\"`\n\tInterfaceName     *string                  `json:\"interface-name\"`\n}\n\ntype tunSchema struct {\n\tEnable              bool        `yaml:\"enable\" json:\"enable\"`\n\tDevice              *string     `yaml:\"device\" json:\"device\"`\n\tStack               *C.TUNStack `yaml:\"stack\" json:\"stack\"`\n\tDNSHijack           *[]string   `yaml:\"dns-hijack\" json:\"dns-hijack\"`\n\tAutoRoute           *bool       `yaml:\"auto-route\" json:\"auto-route\"`\n\tAutoDetectInterface *bool       `yaml:\"auto-detect-interface\" json:\"auto-detect-interface\"`\n\n\tMTU        *uint32 `yaml:\"mtu\" json:\"mtu,omitempty\"`\n\tGSO        *bool   `yaml:\"gso\" json:\"gso,omitempty\"`\n\tGSOMaxSize *uint32 `yaml:\"gso-max-size\" json:\"gso-max-size,omitempty\"`\n\t//Inet4Address           *[]netip.Prefix `yaml:\"inet4-address\" json:\"inet4-address,omitempty\"`\n\tInet6Address                          *[]netip.Prefix `yaml:\"inet6-address\" json:\"inet6-address,omitempty\"`\n\tIPRoute2TableIndex                    *int            `yaml:\"iproute2-table-index\" json:\"iproute2-table-index,omitempty\"`\n\tIPRoute2RuleIndex                     *int            `yaml:\"iproute2-rule-index\" json:\"iproute2-rule-index,omitempty\"`\n\tAutoRedirect                          *bool           `yaml:\"auto-redirect\" json:\"auto-redirect,omitempty\"`\n\tAutoRedirectInputMark                 *uint32         `yaml:\"auto-redirect-input-mark\" json:\"auto-redirect-input-mark,omitempty\"`\n\tAutoRedirectOutputMark                *uint32         `yaml:\"auto-redirect-output-mark\" json:\"auto-redirect-output-mark,omitempty\"`\n\tAutoRedirectIPRoute2FallbackRuleIndex *int            `yaml:\"auto-redirect-iproute2-fallback-rule-index\" json:\"auto-redirect-iproute2-fallback-rule-index,omitempty\"`\n\tLoopbackAddress                       *[]netip.Addr   `yaml:\"loopback-address\" json:\"loopback-address,omitempty\"`\n\tStrictRoute                           *bool           `yaml:\"strict-route\" json:\"strict-route,omitempty\"`\n\tRouteAddress                          *[]netip.Prefix `yaml:\"route-address\" json:\"route-address,omitempty\"`\n\tRouteAddressSet                       *[]string       `yaml:\"route-address-set\" json:\"route-address-set,omitempty\"`\n\tRouteExcludeAddress                   *[]netip.Prefix `yaml:\"route-exclude-address\" json:\"route-exclude-address,omitempty\"`\n\tRouteExcludeAddressSet                *[]string       `yaml:\"route-exclude-address-set\" json:\"route-exclude-address-set,omitempty\"`\n\tIncludeInterface                      *[]string       `yaml:\"include-interface\" json:\"include-interface,omitempty\"`\n\tExcludeInterface                      *[]string       `yaml:\"exclude-interface\" json:\"exclude-interface,omitempty\"`\n\tIncludeUID                            *[]uint32       `yaml:\"include-uid\" json:\"include-uid,omitempty\"`\n\tIncludeUIDRange                       *[]string       `yaml:\"include-uid-range\" json:\"include-uid-range,omitempty\"`\n\tExcludeUID                            *[]uint32       `yaml:\"exclude-uid\" json:\"exclude-uid,omitempty\"`\n\tExcludeUIDRange                       *[]string       `yaml:\"exclude-uid-range\" json:\"exclude-uid-range,omitempty\"`\n\tIncludeAndroidUser                    *[]int          `yaml:\"include-android-user\" json:\"include-android-user,omitempty\"`\n\tIncludePackage                        *[]string       `yaml:\"include-package\" json:\"include-package,omitempty\"`\n\tExcludePackage                        *[]string       `yaml:\"exclude-package\" json:\"exclude-package,omitempty\"`\n\tIncludeMACAddress                     *[]string       `yaml:\"include-mac-address\" json:\"include-mac-address,omitempty\"`\n\tExcludeMACAddress                     *[]string       `yaml:\"exclude-mac-address\" json:\"exclude-mac-address,omitempty\"`\n\tEndpointIndependentNat                *bool           `yaml:\"endpoint-independent-nat\" json:\"endpoint-independent-nat,omitempty\"`\n\tUDPTimeout                            *int64          `yaml:\"udp-timeout\" json:\"udp-timeout,omitempty\"`\n\tFileDescriptor                        *int            `yaml:\"file-descriptor\" json:\"file-descriptor\"`\n\n\tInet4RouteAddress        *[]netip.Prefix `yaml:\"inet4-route-address\" json:\"inet4-route-address,omitempty\"`\n\tInet6RouteAddress        *[]netip.Prefix `yaml:\"inet6-route-address\" json:\"inet6-route-address,omitempty\"`\n\tInet4RouteExcludeAddress *[]netip.Prefix `yaml:\"inet4-route-exclude-address\" json:\"inet4-route-exclude-address,omitempty\"`\n\tInet6RouteExcludeAddress *[]netip.Prefix `yaml:\"inet6-route-exclude-address\" json:\"inet6-route-exclude-address,omitempty\"`\n\n\t// darwin special config\n\tRecvMsgX *bool `yaml:\"recvmsgx\" json:\"recvmsgx,omitempty\"`\n\tSendMsgX *bool `yaml:\"sendmsgx\" json:\"sendmsgx,omitempty\"`\n}\n\ntype tuicServerSchema struct {\n\tEnable                bool               `yaml:\"enable\" json:\"enable\"`\n\tListen                *string            `yaml:\"listen\" json:\"listen\"`\n\tToken                 *[]string          `yaml:\"token\" json:\"token\"`\n\tUsers                 *map[string]string `yaml:\"users\" json:\"users,omitempty\"`\n\tCertificate           *string            `yaml:\"certificate\" json:\"certificate\"`\n\tPrivateKey            *string            `yaml:\"private-key\" json:\"private-key\"`\n\tCongestionController  *string            `yaml:\"congestion-controller\" json:\"congestion-controller,omitempty\"`\n\tMaxIdleTime           *int               `yaml:\"max-idle-time\" json:\"max-idle-time,omitempty\"`\n\tAuthenticationTimeout *int               `yaml:\"authentication-timeout\" json:\"authentication-timeout,omitempty\"`\n\tALPN                  *[]string          `yaml:\"alpn\" json:\"alpn,omitempty\"`\n\tMaxUdpRelayPacketSize *int               `yaml:\"max-udp-relay-packet-size\" json:\"max-udp-relay-packet-size,omitempty\"`\n\tCWND                  *int               `yaml:\"cwnd\" json:\"cwnd,omitempty\"`\n\tBBRProfile            *string            `yaml:\"bbr-profile\" json:\"bbr-profile,omitempty\"`\n}\n\nfunc getConfigs(w http.ResponseWriter, r *http.Request) {\n\tgeneral := executor.GetGeneral()\n\trender.JSON(w, r, general)\n}\n\nfunc pointerOrDefault[T any](p *T, def T) T {\n\tif p != nil {\n\t\treturn *p\n\t}\n\treturn def\n}\n\nfunc pointerOrDefaultTun(p *tunSchema, def LC.Tun) LC.Tun {\n\tif p != nil {\n\t\tdef.Enable = p.Enable\n\t\tif p.Device != nil {\n\t\t\tdef.Device = *p.Device\n\t\t}\n\t\tif p.Stack != nil {\n\t\t\tdef.Stack = *p.Stack\n\t\t}\n\t\tif p.DNSHijack != nil {\n\t\t\tdef.DNSHijack = *p.DNSHijack\n\t\t}\n\t\tif p.AutoRoute != nil {\n\t\t\tdef.AutoRoute = *p.AutoRoute\n\t\t}\n\t\tif p.AutoDetectInterface != nil {\n\t\t\tdef.AutoDetectInterface = *p.AutoDetectInterface\n\t\t}\n\t\tif p.MTU != nil {\n\t\t\tdef.MTU = *p.MTU\n\t\t}\n\t\tif p.GSO != nil {\n\t\t\tdef.GSO = *p.GSO\n\t\t}\n\t\tif p.GSOMaxSize != nil {\n\t\t\tdef.GSOMaxSize = *p.GSOMaxSize\n\t\t}\n\t\t//if p.Inet4Address != nil {\n\t\t//\tdef.Inet4Address = *p.Inet4Address\n\t\t//}\n\t\tif p.Inet6Address != nil {\n\t\t\tdef.Inet6Address = *p.Inet6Address\n\t\t}\n\t\tif p.IPRoute2TableIndex != nil {\n\t\t\tdef.IPRoute2TableIndex = *p.IPRoute2TableIndex\n\t\t}\n\t\tif p.IPRoute2RuleIndex != nil {\n\t\t\tdef.IPRoute2RuleIndex = *p.IPRoute2RuleIndex\n\t\t}\n\t\tif p.AutoRedirect != nil {\n\t\t\tdef.AutoRedirect = *p.AutoRedirect\n\t\t}\n\t\tif p.AutoRedirectInputMark != nil {\n\t\t\tdef.AutoRedirectInputMark = *p.AutoRedirectInputMark\n\t\t}\n\t\tif p.AutoRedirectOutputMark != nil {\n\t\t\tdef.AutoRedirectOutputMark = *p.AutoRedirectOutputMark\n\t\t}\n\t\tif p.AutoRedirectIPRoute2FallbackRuleIndex != nil {\n\t\t\tdef.AutoRedirectIPRoute2FallbackRuleIndex = *p.AutoRedirectIPRoute2FallbackRuleIndex\n\t\t}\n\t\tif p.LoopbackAddress != nil {\n\t\t\tdef.LoopbackAddress = *p.LoopbackAddress\n\t\t}\n\t\tif p.StrictRoute != nil {\n\t\t\tdef.StrictRoute = *p.StrictRoute\n\t\t}\n\t\tif p.RouteAddress != nil {\n\t\t\tdef.RouteAddress = *p.RouteAddress\n\t\t}\n\t\tif p.RouteAddressSet != nil {\n\t\t\tdef.RouteAddressSet = *p.RouteAddressSet\n\t\t}\n\t\tif p.RouteExcludeAddress != nil {\n\t\t\tdef.RouteExcludeAddress = *p.RouteExcludeAddress\n\t\t}\n\t\tif p.RouteExcludeAddressSet != nil {\n\t\t\tdef.RouteExcludeAddressSet = *p.RouteExcludeAddressSet\n\t\t}\n\t\tif p.Inet4RouteAddress != nil {\n\t\t\tdef.Inet4RouteAddress = *p.Inet4RouteAddress\n\t\t}\n\t\tif p.Inet6RouteAddress != nil {\n\t\t\tdef.Inet6RouteAddress = *p.Inet6RouteAddress\n\t\t}\n\t\tif p.Inet4RouteExcludeAddress != nil {\n\t\t\tdef.Inet4RouteExcludeAddress = *p.Inet4RouteExcludeAddress\n\t\t}\n\t\tif p.Inet6RouteExcludeAddress != nil {\n\t\t\tdef.Inet6RouteExcludeAddress = *p.Inet6RouteExcludeAddress\n\t\t}\n\t\tif p.IncludeInterface != nil {\n\t\t\tdef.IncludeInterface = *p.IncludeInterface\n\t\t}\n\t\tif p.ExcludeInterface != nil {\n\t\t\tdef.ExcludeInterface = *p.ExcludeInterface\n\t\t}\n\t\tif p.IncludeUID != nil {\n\t\t\tdef.IncludeUID = *p.IncludeUID\n\t\t}\n\t\tif p.IncludeUIDRange != nil {\n\t\t\tdef.IncludeUIDRange = *p.IncludeUIDRange\n\t\t}\n\t\tif p.ExcludeUID != nil {\n\t\t\tdef.ExcludeUID = *p.ExcludeUID\n\t\t}\n\t\tif p.ExcludeUIDRange != nil {\n\t\t\tdef.ExcludeUIDRange = *p.ExcludeUIDRange\n\t\t}\n\t\tif p.IncludeAndroidUser != nil {\n\t\t\tdef.IncludeAndroidUser = *p.IncludeAndroidUser\n\t\t}\n\t\tif p.IncludePackage != nil {\n\t\t\tdef.IncludePackage = *p.IncludePackage\n\t\t}\n\t\tif p.ExcludePackage != nil {\n\t\t\tdef.ExcludePackage = *p.ExcludePackage\n\t\t}\n\t\tif p.IncludeMACAddress != nil {\n\t\t\tdef.IncludeMACAddress = *p.IncludeMACAddress\n\t\t}\n\t\tif p.ExcludeMACAddress != nil {\n\t\t\tdef.ExcludeMACAddress = *p.ExcludeMACAddress\n\t\t}\n\t\tif p.EndpointIndependentNat != nil {\n\t\t\tdef.EndpointIndependentNat = *p.EndpointIndependentNat\n\t\t}\n\t\tif p.UDPTimeout != nil {\n\t\t\tdef.UDPTimeout = *p.UDPTimeout\n\t\t}\n\t\tif p.FileDescriptor != nil {\n\t\t\tdef.FileDescriptor = *p.FileDescriptor\n\t\t}\n\t\tif p.RecvMsgX != nil {\n\t\t\tdef.RecvMsgX = *p.RecvMsgX\n\t\t}\n\t\tif p.SendMsgX != nil {\n\t\t\tdef.SendMsgX = *p.SendMsgX\n\t\t}\n\t}\n\treturn def\n}\n\nfunc pointerOrDefaultTuicServer(p *tuicServerSchema, def LC.TuicServer) LC.TuicServer {\n\tif p != nil {\n\t\tdef.Enable = p.Enable\n\t\tif p.Listen != nil {\n\t\t\tdef.Listen = *p.Listen\n\t\t}\n\t\tif p.Token != nil {\n\t\t\tdef.Token = *p.Token\n\t\t}\n\t\tif p.Users != nil {\n\t\t\tdef.Users = *p.Users\n\t\t}\n\t\tif p.Certificate != nil {\n\t\t\tdef.Certificate = *p.Certificate\n\t\t}\n\t\tif p.PrivateKey != nil {\n\t\t\tdef.PrivateKey = *p.PrivateKey\n\t\t}\n\t\tif p.CongestionController != nil {\n\t\t\tdef.CongestionController = *p.CongestionController\n\t\t}\n\t\tif p.MaxIdleTime != nil {\n\t\t\tdef.MaxIdleTime = *p.MaxIdleTime\n\t\t}\n\t\tif p.AuthenticationTimeout != nil {\n\t\t\tdef.AuthenticationTimeout = *p.AuthenticationTimeout\n\t\t}\n\t\tif p.ALPN != nil {\n\t\t\tdef.ALPN = *p.ALPN\n\t\t}\n\t\tif p.MaxUdpRelayPacketSize != nil {\n\t\t\tdef.MaxUdpRelayPacketSize = *p.MaxUdpRelayPacketSize\n\t\t}\n\t\tif p.CWND != nil {\n\t\t\tdef.CWND = *p.CWND\n\t\t}\n\t\tif p.BBRProfile != nil {\n\t\t\tdef.BBRProfile = *p.BBRProfile\n\t\t}\n\t}\n\treturn def\n}\n\nfunc patchConfigs(w http.ResponseWriter, r *http.Request) {\n\tgeneral := &configSchema{}\n\tif err := render.DecodeJSON(r.Body, &general); err != nil {\n\t\trender.Status(r, http.StatusBadRequest)\n\t\trender.JSON(w, r, ErrBadRequest)\n\t\treturn\n\t}\n\n\tif general.AllowLan != nil {\n\t\tlistener.SetAllowLan(*general.AllowLan)\n\t}\n\n\tif general.SkipAuthPrefixes != nil {\n\t\tinbound.SetSkipAuthPrefixes(*general.SkipAuthPrefixes)\n\t}\n\n\tif general.LanAllowedIPs != nil {\n\t\tinbound.SetAllowedIPs(*general.LanAllowedIPs)\n\t}\n\n\tif general.LanDisAllowedIPs != nil {\n\t\tinbound.SetDisAllowedIPs(*general.LanDisAllowedIPs)\n\t}\n\n\tif general.BindAddress != nil {\n\t\tlistener.SetBindAddress(*general.BindAddress)\n\t}\n\n\tif general.Sniffing != nil {\n\t\ttunnel.SetSniffing(*general.Sniffing)\n\t}\n\n\tif general.TcpConcurrent != nil {\n\t\tdialer.SetTcpConcurrent(*general.TcpConcurrent)\n\t}\n\n\tif general.InterfaceName != nil {\n\t\tdialer.DefaultInterface.Store(*general.InterfaceName)\n\t}\n\n\tports := listener.GetPorts()\n\n\tlistener.ReCreateHTTP(pointerOrDefault(general.Port, ports.Port), tunnel.Tunnel)\n\tlistener.ReCreateSocks(pointerOrDefault(general.SocksPort, ports.SocksPort), tunnel.Tunnel)\n\tlistener.ReCreateRedir(pointerOrDefault(general.RedirPort, ports.RedirPort), tunnel.Tunnel)\n\tlistener.ReCreateTProxy(pointerOrDefault(general.TProxyPort, ports.TProxyPort), tunnel.Tunnel)\n\tlistener.ReCreateMixed(pointerOrDefault(general.MixedPort, ports.MixedPort), tunnel.Tunnel)\n\tlistener.ReCreateTun(pointerOrDefaultTun(general.Tun, listener.LastTunConf), tunnel.Tunnel)\n\tlistener.ReCreateShadowSocks(pointerOrDefault(general.ShadowSocksConfig, ports.ShadowSocksConfig), tunnel.Tunnel)\n\tlistener.ReCreateVmess(pointerOrDefault(general.VmessConfig, ports.VmessConfig), tunnel.Tunnel)\n\tlistener.ReCreateTuic(pointerOrDefaultTuicServer(general.TuicServer, listener.LastTuicConf), tunnel.Tunnel)\n\n\tif general.Mode != nil {\n\t\ttunnel.SetMode(*general.Mode)\n\t}\n\n\tif general.FindProcessMode != nil {\n\t\ttunnel.SetFindProcessMode(*general.FindProcessMode)\n\t}\n\n\tif general.LogLevel != nil {\n\t\tlog.SetLevel(*general.LogLevel)\n\t}\n\n\tif general.IPv6 != nil {\n\t\tresolver.DisableIPv6 = !*general.IPv6\n\t}\n\n\trender.NoContent(w, r)\n}\n\nfunc updateConfigs(w http.ResponseWriter, r *http.Request) {\n\treq := struct {\n\t\tPath    string `json:\"path\"`\n\t\tPayload string `json:\"payload\"`\n\t}{}\n\tif err := render.DecodeJSON(r.Body, &req); err != nil {\n\t\trender.Status(r, http.StatusBadRequest)\n\t\trender.JSON(w, r, ErrBadRequest)\n\t\treturn\n\t}\n\n\tforce := r.URL.Query().Get(\"force\") == \"true\"\n\tvar cfg *config.Config\n\tvar err error\n\n\tif req.Payload != \"\" {\n\t\tcfg, err = executor.ParseWithBytes([]byte(req.Payload))\n\t\tif err != nil {\n\t\t\trender.Status(r, http.StatusBadRequest)\n\t\t\trender.JSON(w, r, newError(err.Error()))\n\t\t\treturn\n\t\t}\n\t} else {\n\t\tif req.Path == \"\" { // default path unneeded any safe check\n\t\t\treq.Path = C.Path.Config()\n\t\t} else {\n\t\t\tif !filepath.IsAbs(req.Path) {\n\t\t\t\trender.Status(r, http.StatusBadRequest)\n\t\t\t\trender.JSON(w, r, newError(\"path is not a absolute path\"))\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tif !C.Path.IsSafePath(req.Path) {\n\t\t\t\trender.Status(r, http.StatusBadRequest)\n\t\t\t\trender.JSON(w, r, newError(C.Path.ErrNotSafePath(req.Path).Error()))\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tcfg, err = executor.ParseWithPath(req.Path)\n\t\tif err != nil {\n\t\t\trender.Status(r, http.StatusBadRequest)\n\t\t\trender.JSON(w, r, newError(err.Error()))\n\t\t\treturn\n\t\t}\n\t}\n\n\texecutor.ApplyConfig(cfg, force)\n\trender.NoContent(w, r)\n}\n\nfunc updateGeoDatabases(w http.ResponseWriter, r *http.Request) {\n\terr := updater.UpdateGeoDatabases()\n\tif err != nil {\n\t\tlog.Errorln(\"[GEO] update GEO databases failed: %v\", err)\n\t\trender.Status(r, http.StatusInternalServerError)\n\t\trender.JSON(w, r, newError(err.Error()))\n\t\treturn\n\t}\n\n\trender.NoContent(w, r)\n}\n"
  },
  {
    "path": "core/Clash.Meta/hub/route/connections.go",
    "content": "package route\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/tunnel/statistic\"\n\n\t\"github.com/metacubex/chi\"\n\t\"github.com/metacubex/chi/render\"\n\t\"github.com/metacubex/http\"\n)\n\nfunc connectionRouter() http.Handler {\n\tr := chi.NewRouter()\n\tr.Get(\"/\", getConnections)\n\tr.Delete(\"/\", closeAllConnections)\n\tr.Delete(\"/{id}\", closeConnection)\n\treturn r\n}\n\nfunc getConnections(w http.ResponseWriter, r *http.Request) {\n\tif !(r.Header.Get(\"Upgrade\") == \"websocket\") {\n\t\tsnapshot := statistic.DefaultManager.Snapshot()\n\t\trender.JSON(w, r, snapshot)\n\t\treturn\n\t}\n\n\tconn, _, err := wsUpgrade(r, w)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tintervalStr := r.URL.Query().Get(\"interval\")\n\tinterval := 1000\n\tif intervalStr != \"\" {\n\t\tt, err := strconv.Atoi(intervalStr)\n\t\tif err != nil {\n\t\t\trender.Status(r, http.StatusBadRequest)\n\t\t\trender.JSON(w, r, ErrBadRequest)\n\t\t\treturn\n\t\t}\n\n\t\tinterval = t\n\t}\n\n\tbuf := &bytes.Buffer{}\n\tsendSnapshot := func() error {\n\t\tbuf.Reset()\n\t\tsnapshot := statistic.DefaultManager.Snapshot()\n\t\tif err := json.NewEncoder(buf).Encode(snapshot); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn wsWriteServerText(conn, buf.Bytes())\n\t}\n\n\tif err := sendSnapshot(); err != nil {\n\t\treturn\n\t}\n\n\ttick := time.NewTicker(time.Millisecond * time.Duration(interval))\n\tdefer tick.Stop()\n\tfor range tick.C {\n\t\tif err := sendSnapshot(); err != nil {\n\t\t\tbreak\n\t\t}\n\t}\n}\n\nfunc closeConnection(w http.ResponseWriter, r *http.Request) {\n\tid := chi.URLParam(r, \"id\")\n\tif c := statistic.DefaultManager.Get(id); c != nil {\n\t\t_ = c.Close()\n\t}\n\trender.NoContent(w, r)\n}\n\nfunc closeAllConnections(w http.ResponseWriter, r *http.Request) {\n\tstatistic.DefaultManager.Range(func(c statistic.Tracker) bool {\n\t\t_ = c.Close()\n\t\treturn true\n\t})\n\trender.NoContent(w, r)\n}\n"
  },
  {
    "path": "core/Clash.Meta/hub/route/ctxkeys.go",
    "content": "package route\n\nvar (\n\tCtxKeyProxyName    = contextKey(\"proxy name\")\n\tCtxKeyProviderName = contextKey(\"provider name\")\n\tCtxKeyProxy        = contextKey(\"proxy\")\n\tCtxKeyProvider     = contextKey(\"provider\")\n)\n\ntype contextKey string\n\nfunc (c contextKey) String() string {\n\treturn \"mihomo context key \" + string(c)\n}\n"
  },
  {
    "path": "core/Clash.Meta/hub/route/dns.go",
    "content": "package route\n\nimport (\n\t\"context\"\n\t\"math\"\n\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\n\t\"github.com/metacubex/chi\"\n\t\"github.com/metacubex/chi/render\"\n\t\"github.com/metacubex/http\"\n\t\"github.com/miekg/dns\"\n\t\"github.com/samber/lo\"\n)\n\nfunc dnsRouter() http.Handler {\n\tr := chi.NewRouter()\n\tr.Get(\"/query\", queryDNS)\n\treturn r\n}\n\nfunc queryDNS(w http.ResponseWriter, r *http.Request) {\n\tif resolver.DefaultResolver == nil {\n\t\trender.Status(r, http.StatusInternalServerError)\n\t\trender.JSON(w, r, newError(\"DNS section is disabled\"))\n\t\treturn\n\t}\n\n\tname := r.URL.Query().Get(\"name\")\n\tqTypeStr, _ := lo.Coalesce(r.URL.Query().Get(\"type\"), \"A\")\n\n\tqType, exist := dns.StringToType[qTypeStr]\n\tif !exist {\n\t\trender.Status(r, http.StatusBadRequest)\n\t\trender.JSON(w, r, newError(\"invalid query type\"))\n\t\treturn\n\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), resolver.DefaultDNSTimeout)\n\tdefer cancel()\n\n\tmsg := dns.Msg{}\n\tmsg.SetQuestion(dns.Fqdn(name), qType)\n\tresp, err := resolver.DefaultResolver.ExchangeContext(ctx, &msg)\n\tif err != nil {\n\t\trender.Status(r, http.StatusInternalServerError)\n\t\trender.JSON(w, r, newError(err.Error()))\n\t\treturn\n\t}\n\n\tresponseData := render.M{\n\t\t\"Status\":   resp.Rcode,\n\t\t\"Question\": resp.Question,\n\t\t\"TC\":       resp.Truncated,\n\t\t\"RD\":       resp.RecursionDesired,\n\t\t\"RA\":       resp.RecursionAvailable,\n\t\t\"AD\":       resp.AuthenticatedData,\n\t\t\"CD\":       resp.CheckingDisabled,\n\t}\n\n\trr2Json := func(rr dns.RR, _ int) render.M {\n\t\theader := rr.Header()\n\t\treturn render.M{\n\t\t\t\"name\": header.Name,\n\t\t\t\"type\": header.Rrtype,\n\t\t\t\"TTL\":  header.Ttl,\n\t\t\t\"data\": lo.Substring(rr.String(), len(header.String()), math.MaxUint),\n\t\t}\n\t}\n\n\tif len(resp.Answer) > 0 {\n\t\tresponseData[\"Answer\"] = lo.Map(resp.Answer, rr2Json)\n\t}\n\tif len(resp.Ns) > 0 {\n\t\tresponseData[\"Authority\"] = lo.Map(resp.Ns, rr2Json)\n\t}\n\tif len(resp.Extra) > 0 {\n\t\tresponseData[\"Additional\"] = lo.Map(resp.Extra, rr2Json)\n\t}\n\n\trender.JSON(w, r, responseData)\n}\n"
  },
  {
    "path": "core/Clash.Meta/hub/route/doh.go",
    "content": "package route\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"io\"\n\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\n\t\"github.com/metacubex/chi/render\"\n\t\"github.com/metacubex/http\"\n)\n\nfunc dohRouter() http.Handler {\n\treturn http.HandlerFunc(dohHandler)\n}\n\nfunc dohHandler(w http.ResponseWriter, r *http.Request) {\n\tif resolver.DefaultResolver == nil {\n\t\trender.Status(r, http.StatusInternalServerError)\n\t\trender.PlainText(w, r, \"DNS section is disabled\")\n\t\treturn\n\t}\n\n\tvar dnsData []byte\n\tvar err error\n\tswitch r.Method {\n\tcase \"GET\":\n\t\tdnsData, err = base64.RawURLEncoding.DecodeString(r.URL.Query().Get(\"dns\"))\n\tcase \"POST\":\n\t\tif r.Header.Get(\"Content-Type\") != \"application/dns-message\" {\n\t\t\trender.Status(r, http.StatusInternalServerError)\n\t\t\trender.PlainText(w, r, \"invalid content-type\")\n\t\t\treturn\n\t\t}\n\t\treader := io.LimitReader(r.Body, 65535) // according to rfc8484, the maximum size of the DNS message is 65535 bytes\n\t\tdnsData, err = io.ReadAll(reader)\n\t\t_ = r.Body.Close()\n\tdefault:\n\t\trender.Status(r, http.StatusMethodNotAllowed)\n\t\trender.PlainText(w, r, \"method not allowed\")\n\t\treturn\n\t}\n\tif err != nil {\n\t\trender.Status(r, http.StatusInternalServerError)\n\t\trender.PlainText(w, r, err.Error())\n\t\treturn\n\t}\n\n\tctx, cancel := context.WithTimeout(context.Background(), resolver.DefaultDNSTimeout)\n\tdefer cancel()\n\n\tdnsData, err = resolver.RelayDnsPacket(ctx, dnsData, dnsData)\n\tif err != nil {\n\t\trender.Status(r, http.StatusInternalServerError)\n\t\trender.PlainText(w, r, err.Error())\n\t\treturn\n\t}\n\n\tw.Header().Set(\"Content-Type\", \"application/dns-message\")\n\tw.WriteHeader(http.StatusOK)\n\t_, _ = w.Write(dnsData)\n}\n"
  },
  {
    "path": "core/Clash.Meta/hub/route/errors.go",
    "content": "package route\n\nvar (\n\tErrUnauthorized   = newError(\"Unauthorized\")\n\tErrBadRequest     = newError(\"Body invalid\")\n\tErrForbidden      = newError(\"Forbidden\")\n\tErrNotFound       = newError(\"Resource not found\")\n\tErrRequestTimeout = newError(\"Timeout\")\n)\n\n// HTTPError is custom HTTP error for API\ntype HTTPError struct {\n\tMessage string `json:\"message\"`\n}\n\nfunc (e *HTTPError) Error() string {\n\treturn e.Message\n}\n\nfunc newError(msg string) *HTTPError {\n\treturn &HTTPError{Message: msg}\n}\n"
  },
  {
    "path": "core/Clash.Meta/hub/route/external.go",
    "content": "package route\n\nimport \"github.com/metacubex/chi\"\n\ntype externalRouter func(r chi.Router)\n\nvar externalRouters = make([]externalRouter, 0)\n\nfunc Register(route ...externalRouter) {\n\texternalRouters = append(externalRouters, route...)\n}\n\nfunc addExternalRouters(r chi.Router) {\n\tif len(externalRouters) == 0 {\n\t\treturn\n\t}\n\n\tfor _, caller := range externalRouters {\n\t\tcaller(r)\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/hub/route/groups.go",
    "content": "package route\n\nimport (\n\t\"context\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/adapter/outboundgroup\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"github.com/metacubex/mihomo/component/profile/cachefile\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/tunnel\"\n\n\t\"github.com/metacubex/chi\"\n\t\"github.com/metacubex/chi/render\"\n\t\"github.com/metacubex/http\"\n)\n\nfunc groupRouter() http.Handler {\n\tr := chi.NewRouter()\n\tr.Get(\"/\", getGroups)\n\n\tr.Route(\"/{name}\", func(r chi.Router) {\n\t\tr.Use(parseProxyName, findProxyByName)\n\t\tr.Get(\"/\", getGroup)\n\t\tr.Get(\"/delay\", getGroupDelay)\n\t})\n\treturn r\n}\n\nfunc getGroups(w http.ResponseWriter, r *http.Request) {\n\tvar gs []C.Proxy\n\tfor _, p := range tunnel.Proxies() {\n\t\tif _, ok := p.Adapter().(outboundgroup.ProxyGroup); ok {\n\t\t\tgs = append(gs, p)\n\t\t}\n\t}\n\trender.JSON(w, r, render.M{\n\t\t\"proxies\": gs,\n\t})\n}\n\nfunc getGroup(w http.ResponseWriter, r *http.Request) {\n\tproxy := r.Context().Value(CtxKeyProxy).(C.Proxy)\n\tif _, ok := proxy.Adapter().(outboundgroup.ProxyGroup); ok {\n\t\trender.JSON(w, r, proxy)\n\t\treturn\n\t}\n\trender.Status(r, http.StatusNotFound)\n\trender.JSON(w, r, ErrNotFound)\n}\n\nfunc getGroupDelay(w http.ResponseWriter, r *http.Request) {\n\tproxy := r.Context().Value(CtxKeyProxy).(C.Proxy)\n\tgroup, ok := proxy.Adapter().(outboundgroup.ProxyGroup)\n\tif !ok {\n\t\trender.Status(r, http.StatusNotFound)\n\t\trender.JSON(w, r, ErrNotFound)\n\t\treturn\n\t}\n\n\tif selectAble, ok := proxy.Adapter().(outboundgroup.SelectAble); ok && proxy.Type() != C.Selector {\n\t\tselectAble.ForceSet(\"\")\n\t\tcachefile.Cache().SetSelected(proxy.Name(), \"\")\n\t}\n\n\tquery := r.URL.Query()\n\turl := query.Get(\"url\")\n\ttimeout, err := strconv.ParseInt(query.Get(\"timeout\"), 10, 32)\n\tif err != nil {\n\t\trender.Status(r, http.StatusBadRequest)\n\t\trender.JSON(w, r, ErrBadRequest)\n\t\treturn\n\t}\n\n\texpectedStatus, err := utils.NewUnsignedRanges[uint16](query.Get(\"expected\"))\n\tif err != nil {\n\t\trender.Status(r, http.StatusBadRequest)\n\t\trender.JSON(w, r, ErrBadRequest)\n\t\treturn\n\t}\n\n\tctx, cancel := context.WithTimeout(r.Context(), time.Millisecond*time.Duration(timeout))\n\tdefer cancel()\n\n\tdm, err := group.URLTest(ctx, url, expectedStatus)\n\tif err != nil {\n\t\trender.Status(r, http.StatusGatewayTimeout)\n\t\trender.JSON(w, r, newError(err.Error()))\n\t\treturn\n\t}\n\n\trender.JSON(w, r, dm)\n}\n"
  },
  {
    "path": "core/Clash.Meta/hub/route/patch_android.go",
    "content": "//go:build android && cmfa\n\npackage route\n\nfunc init() {\n\tSetEmbedMode(true) // set embed mode default\n}\n"
  },
  {
    "path": "core/Clash.Meta/hub/route/provider.go",
    "content": "package route\n\nimport (\n\t\"context\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\tP \"github.com/metacubex/mihomo/constant/provider\"\n\t\"github.com/metacubex/mihomo/tunnel\"\n\n\t\"github.com/metacubex/chi\"\n\t\"github.com/metacubex/chi/render\"\n\t\"github.com/metacubex/http\"\n\t\"github.com/samber/lo\"\n)\n\nfunc proxyProviderRouter() http.Handler {\n\tr := chi.NewRouter()\n\tr.Get(\"/\", getProviders)\n\n\tr.Route(\"/{providerName}\", func(r chi.Router) {\n\t\tr.Use(parseProviderName, findProviderByName)\n\t\tr.Get(\"/\", getProvider)\n\t\tr.Put(\"/\", updateProvider)\n\t\tr.Get(\"/healthcheck\", healthCheckProvider)\n\t\tr.Mount(\"/\", proxyProviderProxyRouter())\n\t})\n\treturn r\n}\n\nfunc proxyProviderProxyRouter() http.Handler {\n\tr := chi.NewRouter()\n\tr.Route(\"/{name}\", func(r chi.Router) {\n\t\tr.Use(parseProxyName, findProviderProxyByName)\n\t\tr.Get(\"/\", getProxy)\n\t\tr.Get(\"/healthcheck\", getProxyDelay)\n\t})\n\treturn r\n}\n\nfunc getProviders(w http.ResponseWriter, r *http.Request) {\n\tproviders := tunnel.Providers()\n\trender.JSON(w, r, render.M{\n\t\t\"providers\": providers,\n\t})\n}\n\nfunc getProvider(w http.ResponseWriter, r *http.Request) {\n\tprovider := r.Context().Value(CtxKeyProvider).(P.ProxyProvider)\n\trender.JSON(w, r, provider)\n}\n\nfunc updateProvider(w http.ResponseWriter, r *http.Request) {\n\tprovider := r.Context().Value(CtxKeyProvider).(P.ProxyProvider)\n\tif err := provider.Update(); err != nil {\n\t\trender.Status(r, http.StatusServiceUnavailable)\n\t\trender.JSON(w, r, newError(err.Error()))\n\t\treturn\n\t}\n\trender.NoContent(w, r)\n}\n\nfunc healthCheckProvider(w http.ResponseWriter, r *http.Request) {\n\tprovider := r.Context().Value(CtxKeyProvider).(P.ProxyProvider)\n\tprovider.HealthCheck()\n\trender.NoContent(w, r)\n}\n\nfunc parseProviderName(next http.Handler) http.Handler {\n\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tname := getEscapeParam(r, \"providerName\")\n\t\tctx := context.WithValue(r.Context(), CtxKeyProviderName, name)\n\t\tnext.ServeHTTP(w, r.WithContext(ctx))\n\t})\n}\n\nfunc findProviderByName(next http.Handler) http.Handler {\n\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tname := r.Context().Value(CtxKeyProviderName).(string)\n\t\tproviders := tunnel.Providers()\n\t\tprovider, exist := providers[name]\n\t\tif !exist {\n\t\t\trender.Status(r, http.StatusNotFound)\n\t\t\trender.JSON(w, r, ErrNotFound)\n\t\t\treturn\n\t\t}\n\n\t\tctx := context.WithValue(r.Context(), CtxKeyProvider, provider)\n\t\tnext.ServeHTTP(w, r.WithContext(ctx))\n\t})\n}\n\nfunc findProviderProxyByName(next http.Handler) http.Handler {\n\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tvar (\n\t\t\tname = r.Context().Value(CtxKeyProxyName).(string)\n\t\t\tpd   = r.Context().Value(CtxKeyProvider).(P.ProxyProvider)\n\t\t)\n\t\tproxy, exist := lo.Find(pd.Proxies(), func(proxy C.Proxy) bool {\n\t\t\treturn proxy.Name() == name\n\t\t})\n\n\t\tif !exist {\n\t\t\trender.Status(r, http.StatusNotFound)\n\t\t\trender.JSON(w, r, ErrNotFound)\n\t\t\treturn\n\t\t}\n\n\t\tctx := context.WithValue(r.Context(), CtxKeyProxy, proxy)\n\t\tnext.ServeHTTP(w, r.WithContext(ctx))\n\t})\n}\n\nfunc ruleProviderRouter() http.Handler {\n\tr := chi.NewRouter()\n\tr.Get(\"/\", getRuleProviders)\n\tr.Route(\"/{name}\", func(r chi.Router) {\n\t\tr.Use(parseRuleProviderName, findRuleProviderByName)\n\t\tr.Put(\"/\", updateRuleProvider)\n\t})\n\treturn r\n}\n\nfunc getRuleProviders(w http.ResponseWriter, r *http.Request) {\n\truleProviders := tunnel.RuleProviders()\n\trender.JSON(w, r, render.M{\n\t\t\"providers\": ruleProviders,\n\t})\n}\n\nfunc updateRuleProvider(w http.ResponseWriter, r *http.Request) {\n\tprovider := r.Context().Value(CtxKeyProvider).(P.RuleProvider)\n\tif err := provider.Update(); err != nil {\n\t\trender.Status(r, http.StatusServiceUnavailable)\n\t\trender.JSON(w, r, newError(err.Error()))\n\t\treturn\n\t}\n\trender.NoContent(w, r)\n}\n\nfunc parseRuleProviderName(next http.Handler) http.Handler {\n\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tname := getEscapeParam(r, \"name\")\n\t\tctx := context.WithValue(r.Context(), CtxKeyProviderName, name)\n\t\tnext.ServeHTTP(w, r.WithContext(ctx))\n\t})\n}\n\nfunc findRuleProviderByName(next http.Handler) http.Handler {\n\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tname := r.Context().Value(CtxKeyProviderName).(string)\n\t\tproviders := tunnel.RuleProviders()\n\t\tprovider, exist := providers[name]\n\t\tif !exist {\n\t\t\trender.Status(r, http.StatusNotFound)\n\t\t\trender.JSON(w, r, ErrNotFound)\n\t\t\treturn\n\t\t}\n\n\t\tctx := context.WithValue(r.Context(), CtxKeyProvider, provider)\n\t\tnext.ServeHTTP(w, r.WithContext(ctx))\n\t})\n}\n"
  },
  {
    "path": "core/Clash.Meta/hub/route/proxies.go",
    "content": "package route\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/adapter/outboundgroup\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"github.com/metacubex/mihomo/component/profile/cachefile\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/tunnel\"\n\n\t\"github.com/metacubex/chi\"\n\t\"github.com/metacubex/chi/render\"\n\t\"github.com/metacubex/http\"\n)\n\nvar (\n\tSwitchProxiesCallback func(sGroup string, sProxy string)\n)\n\nfunc proxyRouter() http.Handler {\n\tr := chi.NewRouter()\n\tr.Get(\"/\", getProxies)\n\n\tr.Route(\"/{name}\", func(r chi.Router) {\n\t\tr.Use(parseProxyName, findProxyByName)\n\t\tr.Get(\"/\", getProxy)\n\t\tr.Get(\"/delay\", getProxyDelay)\n\t\tr.Put(\"/\", updateProxy)\n\t\tr.Delete(\"/\", unfixedProxy)\n\t})\n\treturn r\n}\n\nfunc parseProxyName(next http.Handler) http.Handler {\n\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tname := getEscapeParam(r, \"name\")\n\t\tctx := context.WithValue(r.Context(), CtxKeyProxyName, name)\n\t\tnext.ServeHTTP(w, r.WithContext(ctx))\n\t})\n}\n\nfunc findProxyByName(next http.Handler) http.Handler {\n\treturn http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tname := r.Context().Value(CtxKeyProxyName).(string)\n\t\tproxies := proxiesWithProviders()\n\t\tproxy, exist := proxies[name]\n\t\tif !exist {\n\t\t\trender.Status(r, http.StatusNotFound)\n\t\t\trender.JSON(w, r, ErrNotFound)\n\t\t\treturn\n\t\t}\n\n\t\tctx := context.WithValue(r.Context(), CtxKeyProxy, proxy)\n\t\tnext.ServeHTTP(w, r.WithContext(ctx))\n\t})\n}\n\nfunc getProxies(w http.ResponseWriter, r *http.Request) {\n\tproxies := proxiesWithProviders()\n\trender.JSON(w, r, render.M{\n\t\t\"proxies\": proxies,\n\t})\n}\n\nfunc getProxy(w http.ResponseWriter, r *http.Request) {\n\tproxy := r.Context().Value(CtxKeyProxy).(C.Proxy)\n\trender.JSON(w, r, proxy)\n}\n\nfunc updateProxy(w http.ResponseWriter, r *http.Request) {\n\treq := struct {\n\t\tName string `json:\"name\"`\n\t}{}\n\tif err := render.DecodeJSON(r.Body, &req); err != nil {\n\t\trender.Status(r, http.StatusBadRequest)\n\t\trender.JSON(w, r, ErrBadRequest)\n\t\treturn\n\t}\n\n\tproxy := r.Context().Value(CtxKeyProxy).(C.Proxy)\n\tselector, ok := proxy.Adapter().(outboundgroup.SelectAble)\n\tif !ok {\n\t\trender.Status(r, http.StatusBadRequest)\n\t\trender.JSON(w, r, newError(\"Must be a Selector\"))\n\t\treturn\n\t}\n\n\tif err := selector.Set(req.Name); err != nil {\n\t\trender.Status(r, http.StatusBadRequest)\n\t\trender.JSON(w, r, newError(fmt.Sprintf(\"Selector update error: %s\", err.Error())))\n\t\treturn\n\t}\n\n\tcachefile.Cache().SetSelected(proxy.Name(), req.Name)\n\tif SwitchProxiesCallback != nil {\n\t\t// refresh tray menu\n\t\tgo SwitchProxiesCallback(proxy.Name(), req.Name)\n\t}\n\trender.NoContent(w, r)\n}\n\nfunc getProxyDelay(w http.ResponseWriter, r *http.Request) {\n\tquery := r.URL.Query()\n\turl := query.Get(\"url\")\n\ttimeout, err := strconv.ParseInt(query.Get(\"timeout\"), 10, 16)\n\tif err != nil {\n\t\trender.Status(r, http.StatusBadRequest)\n\t\trender.JSON(w, r, ErrBadRequest)\n\t\treturn\n\t}\n\n\texpectedStatus, err := utils.NewUnsignedRanges[uint16](query.Get(\"expected\"))\n\tif err != nil {\n\t\trender.Status(r, http.StatusBadRequest)\n\t\trender.JSON(w, r, ErrBadRequest)\n\t\treturn\n\t}\n\n\tproxy := r.Context().Value(CtxKeyProxy).(C.Proxy)\n\n\tctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(timeout))\n\tdefer cancel()\n\n\tdelay, err := proxy.URLTest(ctx, url, expectedStatus)\n\tif ctx.Err() != nil {\n\t\trender.Status(r, http.StatusGatewayTimeout)\n\t\trender.JSON(w, r, ErrRequestTimeout)\n\t\treturn\n\t}\n\n\tif err != nil || delay == 0 {\n\t\trender.Status(r, http.StatusServiceUnavailable)\n\t\tif err != nil && delay != 0 {\n\t\t\trender.JSON(w, r, err)\n\t\t} else {\n\t\t\trender.JSON(w, r, newError(\"An error occurred in the delay test\"))\n\t\t}\n\t\treturn\n\t}\n\n\trender.JSON(w, r, render.M{\n\t\t\"delay\": delay,\n\t})\n}\n\nfunc unfixedProxy(w http.ResponseWriter, r *http.Request) {\n\tproxy := r.Context().Value(CtxKeyProxy).(C.Proxy)\n\tif selectAble, ok := proxy.Adapter().(outboundgroup.SelectAble); ok && proxy.Type() != C.Selector {\n\t\tselectAble.ForceSet(\"\")\n\t\tcachefile.Cache().SetSelected(proxy.Name(), \"\")\n\t\trender.NoContent(w, r)\n\t\treturn\n\t}\n\trender.Status(r, http.StatusBadRequest)\n\trender.JSON(w, r, ErrBadRequest)\n}\n\n// proxiesWithProviders merges all proxies from tunnel\n//\n// Deprecated: This function is poorly implemented and should not be called by any new code.\n// It is left here only to ensure the compatibility of the output of the existing RESTful API.\nfunc proxiesWithProviders() map[string]C.Proxy {\n\tallProxies := make(map[string]C.Proxy)\n\tfor name, proxy := range tunnel.Proxies() {\n\t\tallProxies[name] = proxy\n\t}\n\tfor _, p := range tunnel.Providers() {\n\t\tfor _, proxy := range p.Proxies() {\n\t\t\tname := proxy.Name()\n\t\t\tallProxies[name] = proxy\n\t\t}\n\t}\n\treturn allProxies\n}\n"
  },
  {
    "path": "core/Clash.Meta/hub/route/restart.go",
    "content": "package route\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\t\"os/exec\"\n\t\"runtime\"\n\t\"syscall\"\n\n\t\"github.com/metacubex/mihomo/hub/executor\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/metacubex/chi\"\n\t\"github.com/metacubex/chi/render\"\n\t\"github.com/metacubex/http\"\n)\n\nfunc restartRouter() http.Handler {\n\tr := chi.NewRouter()\n\tr.Post(\"/\", func(w http.ResponseWriter, r *http.Request) {\n\t\trender.Status(r, http.StatusForbidden)\n\t\trender.JSON(w, r, newError(\"Not supported\"))\n\t})\n\treturn r\n}\n\nfunc restart(w http.ResponseWriter, r *http.Request) {\n\t// modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/home/controlupdate.go#L108\n\texecPath, err := os.Executable()\n\tif err != nil {\n\t\trender.Status(r, http.StatusInternalServerError)\n\t\trender.JSON(w, r, newError(fmt.Sprintf(\"getting path: %s\", err)))\n\t\treturn\n\t}\n\n\trender.JSON(w, r, render.M{\"status\": \"ok\"})\n\tif f, ok := w.(http.Flusher); ok {\n\t\tf.Flush()\n\t}\n\n\t// modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/home/controlupdate.go#L180\n\t// The background context is used because the underlying functions wrap it\n\t// with timeout and shut down the server, which handles current request.  It\n\t// also should be done in a separate goroutine for the same reason.\n\tgo restartExecutable(execPath)\n}\n\nfunc restartExecutable(execPath string) {\n\tvar err error\n\texecutor.Shutdown()\n\tif runtime.GOOS == \"windows\" {\n\t\tcmd := exec.Command(execPath, os.Args[1:]...)\n\t\tlog.Infoln(\"restarting: %q %q\", execPath, os.Args[1:])\n\t\tcmd.Stdin = os.Stdin\n\t\tcmd.Stdout = os.Stdout\n\t\tcmd.Stderr = os.Stderr\n\t\terr = cmd.Start()\n\t\tif err != nil {\n\t\t\tlog.Fatalln(\"restarting: %s\", err)\n\t\t}\n\n\t\tos.Exit(0)\n\t}\n\n\tlog.Infoln(\"restarting: %q %q\", execPath, os.Args[1:])\n\terr = syscall.Exec(execPath, os.Args, os.Environ())\n\tif err != nil {\n\t\tlog.Fatalln(\"restarting: %s\", err)\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/hub/route/rules.go",
    "content": "package route\n\nimport (\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/tunnel\"\n\n\t\"github.com/metacubex/chi\"\n\t\"github.com/metacubex/chi/render\"\n\t\"github.com/metacubex/http\"\n)\n\nfunc ruleRouter() http.Handler {\n\tr := chi.NewRouter()\n\tr.Get(\"/\", getRules)\n\tif !embedMode { // disallow update/patch rules in embed mode\n\t\tr.Patch(\"/disable\", disableRules)\n\t}\n\treturn r\n}\n\ntype Rule struct {\n\tIndex   int    `json:\"index\"`\n\tType    string `json:\"type\"`\n\tPayload string `json:\"payload\"`\n\tProxy   string `json:\"proxy\"`\n\tSize    int    `json:\"size\"`\n\n\t// Extra contains information from RuleWrapper\n\tExtra *RuleExtra `json:\"extra,omitempty\"`\n}\n\ntype RuleExtra struct {\n\tDisabled  bool      `json:\"disabled\"`\n\tHitCount  uint64    `json:\"hitCount\"`\n\tHitAt     time.Time `json:\"hitAt\"`\n\tMissCount uint64    `json:\"missCount\"`\n\tMissAt    time.Time `json:\"missAt\"`\n}\n\nfunc getRules(w http.ResponseWriter, r *http.Request) {\n\trawRules := tunnel.Rules()\n\trules := make([]Rule, 0, len(rawRules))\n\tfor index, rule := range rawRules {\n\t\tr := Rule{\n\t\t\tIndex:   index,\n\t\t\tType:    rule.RuleType().String(),\n\t\t\tPayload: rule.Payload(),\n\t\t\tProxy:   rule.Adapter(),\n\t\t\tSize:    -1,\n\t\t}\n\t\tif ruleWrapper, ok := rule.(constant.RuleWrapper); ok {\n\t\t\tr.Extra = &RuleExtra{\n\t\t\t\tDisabled:  ruleWrapper.IsDisabled(),\n\t\t\t\tHitCount:  ruleWrapper.HitCount(),\n\t\t\t\tHitAt:     ruleWrapper.HitAt(),\n\t\t\t\tMissCount: ruleWrapper.MissCount(),\n\t\t\t\tMissAt:    ruleWrapper.MissAt(),\n\t\t\t}\n\t\t\trule = ruleWrapper.Unwrap() // unwrap RuleWrapper\n\t\t}\n\t\tif rule.RuleType() == constant.GEOIP || rule.RuleType() == constant.GEOSITE {\n\t\t\tr.Size = rule.(constant.RuleGroup).GetRecodeSize()\n\t\t}\n\t\trules = append(rules, r)\n\n\t}\n\n\trender.JSON(w, r, render.M{\n\t\t\"rules\": rules,\n\t})\n}\n\n// disableRules disable or enable rules by their indexes.\nfunc disableRules(w http.ResponseWriter, r *http.Request) {\n\t// key: rule index, value: disabled\n\tvar payload map[int]bool\n\tif err := render.DecodeJSON(r.Body, &payload); err != nil {\n\t\trender.Status(r, http.StatusBadRequest)\n\t\trender.JSON(w, r, ErrBadRequest)\n\t\treturn\n\t}\n\n\tif len(payload) != 0 {\n\t\trules := tunnel.Rules()\n\t\tfor index, disabled := range payload {\n\t\t\tif index < 0 || index >= len(rules) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\trule := rules[index]\n\t\t\tif ruleWrapper, ok := rule.(constant.RuleWrapper); ok {\n\t\t\t\truleWrapper.SetDisabled(disabled)\n\t\t\t}\n\t\t}\n\t}\n\n\trender.NoContent(w, r)\n}\n"
  },
  {
    "path": "core/Clash.Meta/hub/route/server.go",
    "content": "package route\n\nimport (\n\t\"bytes\"\n\t\"crypto/subtle\"\n\t\"encoding/json\"\n\t\"net\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime/debug\"\n\t\"strings\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"github.com/metacubex/mihomo/component/ca\"\n\t\"github.com/metacubex/mihomo/component/ech\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/ntp\"\n\t\"github.com/metacubex/mihomo/tunnel/statistic\"\n\n\t\"github.com/metacubex/chi\"\n\t\"github.com/metacubex/chi/cors\"\n\t\"github.com/metacubex/chi/middleware\"\n\t\"github.com/metacubex/chi/render\"\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/tls\"\n)\n\nvar (\n\tuiPath = \"\"\n\n\thttpServer *http.Server\n\ttlsServer  *http.Server\n\tunixServer *http.Server\n\tpipeServer *http.Server\n\n\tembedMode = false\n)\n\nfunc SetEmbedMode(embed bool) {\n\tembedMode = embed\n}\n\ntype Traffic struct {\n\tUp        int64 `json:\"up\"`\n\tDown      int64 `json:\"down\"`\n\tUpTotal   int64 `json:\"upTotal\"`\n\tDownTotal int64 `json:\"downTotal\"`\n}\n\ntype Memory struct {\n\tInuse   uint64 `json:\"inuse\"`\n\tOSLimit uint64 `json:\"oslimit\"` // maybe we need it in the future\n}\n\ntype Config struct {\n\tAddr           string\n\tTLSAddr        string\n\tUnixAddr       string\n\tPipeAddr       string\n\tSecret         string\n\tCertificate    string\n\tPrivateKey     string\n\tClientAuthType string\n\tClientAuthCert string\n\tEchKey         string\n\tDohServer      string\n\tIsDebug        bool\n\tCors           Cors\n}\n\ntype Cors struct {\n\tAllowOrigins        []string\n\tAllowPrivateNetwork bool\n}\n\nfunc (c Cors) Apply(r chi.Router) {\n\tr.Use(cors.New(cors.Options{\n\t\tAllowedOrigins:      c.AllowOrigins,\n\t\tAllowedMethods:      []string{\"GET\", \"POST\", \"PUT\", \"PATCH\", \"DELETE\"},\n\t\tAllowedHeaders:      []string{\"Content-Type\", \"Authorization\"},\n\t\tAllowPrivateNetwork: c.AllowPrivateNetwork,\n\t\tMaxAge:              300,\n\t}).Handler)\n}\n\nfunc ReCreateServer(cfg *Config) {\n\tgo start(cfg)\n\tgo startTLS(cfg)\n\tgo startUnix(cfg)\n\tif inbound.SupportNamedPipe {\n\t\tgo startPipe(cfg)\n\t}\n}\n\nfunc SetUIPath(path string) {\n\tuiPath = C.Path.Resolve(path)\n}\n\nfunc router(isDebug bool, secret string, dohServer string, cors Cors) *chi.Mux {\n\tr := chi.NewRouter()\n\tcors.Apply(r)\n\tif isDebug {\n\t\tr.Mount(\"/debug\", func() http.Handler {\n\t\t\tr := chi.NewRouter()\n\t\t\tr.Put(\"/gc\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\t\tdebug.FreeOSMemory()\n\t\t\t})\n\t\t\thandler := middleware.Profiler\n\t\t\tr.Mount(\"/\", handler())\n\t\t\treturn r\n\t\t}())\n\t}\n\tr.Group(func(r chi.Router) {\n\t\tif secret != \"\" {\n\t\t\tr.Use(authentication(secret))\n\t\t}\n\t\tr.Get(\"/\", hello)\n\t\tr.Get(\"/logs\", getLogs)\n\t\tr.Get(\"/traffic\", traffic)\n\t\tr.Get(\"/memory\", memory)\n\t\tr.Get(\"/version\", version)\n\t\tr.Mount(\"/configs\", configRouter())\n\t\tr.Mount(\"/proxies\", proxyRouter())\n\t\tr.Mount(\"/group\", groupRouter())\n\t\tr.Mount(\"/rules\", ruleRouter())\n\t\tr.Mount(\"/connections\", connectionRouter())\n\t\tr.Mount(\"/providers/proxies\", proxyProviderRouter())\n\t\tr.Mount(\"/providers/rules\", ruleProviderRouter())\n\t\tr.Mount(\"/cache\", cacheRouter())\n\t\tr.Mount(\"/dns\", dnsRouter())\n\t\tr.Mount(\"/storage\", storageRouter())\n\t\tif !embedMode { // disallow restart in embed mode\n\t\t\tr.Mount(\"/restart\", restartRouter())\n\t\t}\n\t\tr.Mount(\"/upgrade\", upgradeRouter())\n\t\taddExternalRouters(r)\n\n\t})\n\n\tif uiPath != \"\" {\n\t\tr.Group(func(r chi.Router) {\n\t\t\tfs := http.StripPrefix(\"/ui\", http.FileServer(http.Dir(uiPath)))\n\t\t\tr.Get(\"/ui\", http.RedirectHandler(\"/ui/\", http.StatusTemporaryRedirect).ServeHTTP)\n\t\t\tr.Get(\"/ui/*\", func(w http.ResponseWriter, r *http.Request) {\n\t\t\t\tfs.ServeHTTP(w, r)\n\t\t\t})\n\t\t})\n\t}\n\tif len(dohServer) > 0 && dohServer[0] == '/' {\n\t\tr.Mount(dohServer, dohRouter())\n\t}\n\n\treturn r\n}\n\nfunc start(cfg *Config) {\n\t// first stop existing server\n\tif httpServer != nil {\n\t\t_ = httpServer.Close()\n\t\thttpServer = nil\n\t}\n\n\t// handle addr\n\tif len(cfg.Addr) > 0 {\n\t\tl, err := inbound.Listen(\"tcp\", cfg.Addr)\n\t\tif err != nil {\n\t\t\tlog.Errorln(\"External controller listen error: %s\", err)\n\t\t\treturn\n\t\t}\n\t\tlog.Infoln(\"RESTful API listening at: %s\", l.Addr().String())\n\n\t\tserver := &http.Server{\n\t\t\tHandler: router(cfg.IsDebug, cfg.Secret, cfg.DohServer, cfg.Cors),\n\t\t}\n\t\thttpServer = server\n\t\tif err = server.Serve(l); err != nil {\n\t\t\tlog.Infoln(\"External controller serve error: %s\", err)\n\t\t}\n\t}\n}\n\nfunc startTLS(cfg *Config) {\n\t// first stop existing server\n\tif tlsServer != nil {\n\t\t_ = tlsServer.Close()\n\t\ttlsServer = nil\n\t}\n\n\t// handle tlsAddr\n\tif len(cfg.TLSAddr) > 0 {\n\t\tcertLoader, err := ca.NewTLSKeyPairLoader(cfg.Certificate, cfg.PrivateKey)\n\t\tif err != nil {\n\t\t\tlog.Errorln(\"External controller tls listen error: %s\", err)\n\t\t\treturn\n\t\t}\n\n\t\tl, err := inbound.Listen(\"tcp\", cfg.TLSAddr)\n\t\tif err != nil {\n\t\t\tlog.Errorln(\"External controller tls listen error: %s\", err)\n\t\t\treturn\n\t\t}\n\n\t\tlog.Infoln(\"RESTful API tls listening at: %s\", l.Addr().String())\n\t\ttlsConfig := &tls.Config{Time: ntp.Now}\n\t\ttlsConfig.NextProtos = []string{\"h2\", \"http/1.1\"}\n\t\ttlsConfig.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) {\n\t\t\treturn certLoader()\n\t\t}\n\t\ttlsConfig.ClientAuth = ca.ClientAuthTypeFromString(cfg.ClientAuthType)\n\t\tif len(cfg.ClientAuthCert) > 0 {\n\t\t\tif tlsConfig.ClientAuth == tls.NoClientCert {\n\t\t\t\ttlsConfig.ClientAuth = tls.RequireAndVerifyClientCert\n\t\t\t}\n\t\t}\n\t\tif tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert {\n\t\t\tpool, err := ca.LoadCertificates(cfg.ClientAuthCert)\n\t\t\tif err != nil {\n\t\t\t\tlog.Errorln(\"External controller tls listen error: %s\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttlsConfig.ClientCAs = pool\n\t\t}\n\n\t\tif cfg.EchKey != \"\" {\n\t\t\terr = ech.LoadECHKey(cfg.EchKey, tlsConfig)\n\t\t\tif err != nil {\n\t\t\t\tlog.Errorln(\"External controller tls serve error: %s\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tserver := &http.Server{\n\t\t\tHandler: router(cfg.IsDebug, cfg.Secret, cfg.DohServer, cfg.Cors),\n\t\t}\n\t\ttlsServer = server\n\t\tif err = server.Serve(tls.NewListener(l, tlsConfig)); err != nil {\n\t\t\tlog.Errorln(\"External controller tls serve error: %s\", err)\n\t\t}\n\t}\n}\n\nfunc startUnix(cfg *Config) {\n\t// first stop existing server\n\tif unixServer != nil {\n\t\t_ = unixServer.Close()\n\t\tunixServer = nil\n\t}\n\n\t// handle addr\n\tif len(cfg.UnixAddr) > 0 {\n\t\taddr := C.Path.Resolve(cfg.UnixAddr)\n\n\t\tdir := filepath.Dir(addr)\n\t\tif _, err := os.Stat(dir); os.IsNotExist(err) {\n\t\t\tif err := os.MkdirAll(dir, 0o755); err != nil {\n\t\t\t\tlog.Errorln(\"External controller unix listen error: %s\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\t// https://devblogs.microsoft.com/commandline/af_unix-comes-to-windows/\n\t\t//\n\t\t// Note: As mentioned above in the ‘security’ section, when a socket binds a socket to a valid pathname address,\n\t\t// a socket file is created within the filesystem. On Linux, the application is expected to unlink\n\t\t// (see the notes section in the man page for AF_UNIX) before any other socket can be bound to the same address.\n\t\t// The same applies to Windows unix sockets, except that, DeleteFile (or any other file delete API)\n\t\t// should be used to delete the socket file prior to calling bind with the same path.\n\t\t_ = syscall.Unlink(addr)\n\n\t\tl, err := inbound.Listen(\"unix\", addr)\n\t\tif err != nil {\n\t\t\tlog.Errorln(\"External controller unix listen error: %s\", err)\n\t\t\treturn\n\t\t}\n\t\t_ = os.Chmod(addr, 0o666)\n\t\tlog.Infoln(\"RESTful API unix listening at: %s\", l.Addr().String())\n\n\t\tserver := &http.Server{\n\t\t\tHandler: router(cfg.IsDebug, \"\", cfg.DohServer, cfg.Cors),\n\t\t}\n\t\tunixServer = server\n\t\tif err = server.Serve(l); err != nil {\n\t\t\tlog.Errorln(\"External controller unix serve error: %s\", err)\n\t\t}\n\t}\n}\n\nfunc startPipe(cfg *Config) {\n\t// first stop existing server\n\tif pipeServer != nil {\n\t\t_ = pipeServer.Close()\n\t\tpipeServer = nil\n\t}\n\n\t// handle addr\n\tif len(cfg.PipeAddr) > 0 {\n\t\tif !strings.HasPrefix(cfg.PipeAddr, \"\\\\\\\\.\\\\pipe\\\\\") { // windows namedpipe must start with \"\\\\.\\pipe\\\"\n\t\t\tlog.Errorln(\"External controller pipe listen error: windows namedpipe must start with \\\"\\\\\\\\.\\\\pipe\\\\\\\"\")\n\t\t\treturn\n\t\t}\n\n\t\tl, err := inbound.ListenNamedPipe(cfg.PipeAddr)\n\t\tif err != nil {\n\t\t\tlog.Errorln(\"External controller pipe listen error: %s\", err)\n\t\t\treturn\n\t\t}\n\t\tlog.Infoln(\"RESTful API pipe listening at: %s\", l.Addr().String())\n\n\t\tserver := &http.Server{\n\t\t\tHandler: router(cfg.IsDebug, \"\", cfg.DohServer, cfg.Cors),\n\t\t}\n\t\tpipeServer = server\n\t\tif err = server.Serve(l); err != nil {\n\t\t\tlog.Errorln(\"External controller pipe serve error: %s\", err)\n\t\t}\n\t}\n}\n\nfunc safeEqual(a, b string) bool {\n\taBuf := utils.ImmutableBytesFromString(a)\n\tbBuf := utils.ImmutableBytesFromString(b)\n\treturn subtle.ConstantTimeCompare(aBuf, bBuf) == 1\n}\n\nfunc authentication(secret string) func(http.Handler) http.Handler {\n\treturn func(next http.Handler) http.Handler {\n\t\tfn := func(w http.ResponseWriter, r *http.Request) {\n\t\t\t// Browser websocket not support custom header\n\t\t\tif r.Header.Get(\"Upgrade\") == \"websocket\" && r.URL.Query().Get(\"token\") != \"\" {\n\t\t\t\ttoken := r.URL.Query().Get(\"token\")\n\t\t\t\tif !safeEqual(token, secret) {\n\t\t\t\t\trender.Status(r, http.StatusUnauthorized)\n\t\t\t\t\trender.JSON(w, r, ErrUnauthorized)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tnext.ServeHTTP(w, r)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\theader := r.Header.Get(\"Authorization\")\n\t\t\tbearer, token, found := strings.Cut(header, \" \")\n\n\t\t\thasInvalidHeader := bearer != \"Bearer\"\n\t\t\thasInvalidSecret := !found || !safeEqual(token, secret)\n\t\t\tif hasInvalidHeader || hasInvalidSecret {\n\t\t\t\trender.Status(r, http.StatusUnauthorized)\n\t\t\t\trender.JSON(w, r, ErrUnauthorized)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tnext.ServeHTTP(w, r)\n\t\t}\n\t\treturn http.HandlerFunc(fn)\n\t}\n}\n\nfunc hello(w http.ResponseWriter, r *http.Request) {\n\trender.JSON(w, r, render.M{\"hello\": \"mihomo\"})\n}\n\nfunc traffic(w http.ResponseWriter, r *http.Request) {\n\tvar wsConn net.Conn\n\tif r.Header.Get(\"Upgrade\") == \"websocket\" {\n\t\tvar err error\n\t\twsConn, _, err = wsUpgrade(r, w)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tif wsConn == nil {\n\t\tw.Header().Set(\"Content-Type\", \"application/json\")\n\t\trender.Status(r, http.StatusOK)\n\t}\n\n\ttick := time.NewTicker(time.Second)\n\tdefer tick.Stop()\n\tt := statistic.DefaultManager\n\tbuf := &bytes.Buffer{}\n\tvar err error\n\tfor range tick.C {\n\t\tbuf.Reset()\n\t\tup, down := t.Now()\n\t\tupTotal, downTotal := t.Total()\n\t\tif err := json.NewEncoder(buf).Encode(Traffic{\n\t\t\tUp:        up,\n\t\t\tDown:      down,\n\t\t\tUpTotal:   upTotal,\n\t\t\tDownTotal: downTotal,\n\t\t}); err != nil {\n\t\t\tbreak\n\t\t}\n\n\t\tif wsConn == nil {\n\t\t\t_, err = w.Write(buf.Bytes())\n\t\t\tw.(http.Flusher).Flush()\n\t\t} else {\n\t\t\terr = wsWriteServerText(wsConn, buf.Bytes())\n\t\t}\n\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t}\n}\n\nfunc memory(w http.ResponseWriter, r *http.Request) {\n\tvar wsConn net.Conn\n\tif r.Header.Get(\"Upgrade\") == \"websocket\" {\n\t\tvar err error\n\t\twsConn, _, err = wsUpgrade(r, w)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tif wsConn == nil {\n\t\tw.Header().Set(\"Content-Type\", \"application/json\")\n\t\trender.Status(r, http.StatusOK)\n\t}\n\n\ttick := time.NewTicker(time.Second)\n\tdefer tick.Stop()\n\tt := statistic.DefaultManager\n\tbuf := &bytes.Buffer{}\n\tvar err error\n\tfirst := true\n\tfor range tick.C {\n\t\tbuf.Reset()\n\n\t\tinuse := t.Memory()\n\t\t// make chat.js begin with zero\n\t\t// this is shit var,but we need output 0 for first time\n\t\tif first {\n\t\t\tinuse = 0\n\t\t\tfirst = false\n\t\t}\n\t\tif err := json.NewEncoder(buf).Encode(Memory{\n\t\t\tInuse:   inuse,\n\t\t\tOSLimit: 0,\n\t\t}); err != nil {\n\t\t\tbreak\n\t\t}\n\t\tif wsConn == nil {\n\t\t\t_, err = w.Write(buf.Bytes())\n\t\t\tw.(http.Flusher).Flush()\n\t\t} else {\n\t\t\terr = wsWriteServerText(wsConn, buf.Bytes())\n\t\t}\n\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t}\n}\n\ntype Log struct {\n\tType    string `json:\"type\"`\n\tPayload string `json:\"payload\"`\n}\ntype LogStructuredField struct {\n\tKey   string `json:\"key\"`\n\tValue string `json:\"value\"`\n}\ntype LogStructured struct {\n\tTime    string               `json:\"time\"`\n\tLevel   string               `json:\"level\"`\n\tMessage string               `json:\"message\"`\n\tFields  []LogStructuredField `json:\"fields\"`\n}\n\nfunc getLogs(w http.ResponseWriter, r *http.Request) {\n\tlevelText := r.URL.Query().Get(\"level\")\n\tif levelText == \"\" {\n\t\tlevelText = \"info\"\n\t}\n\n\tformatText := r.URL.Query().Get(\"format\")\n\tisStructured := false\n\tif formatText == \"structured\" {\n\t\tisStructured = true\n\t}\n\n\tlevel, ok := log.LogLevelMapping[levelText]\n\tif !ok {\n\t\trender.Status(r, http.StatusBadRequest)\n\t\trender.JSON(w, r, ErrBadRequest)\n\t\treturn\n\t}\n\n\tvar wsConn net.Conn\n\tif r.Header.Get(\"Upgrade\") == \"websocket\" {\n\t\tvar err error\n\t\twsConn, _, err = wsUpgrade(r, w)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tif wsConn == nil {\n\t\tw.Header().Set(\"Content-Type\", \"application/json\")\n\t\trender.Status(r, http.StatusOK)\n\t}\n\n\tch := make(chan log.Event, 1024)\n\tsub := log.Subscribe()\n\tdefer log.UnSubscribe(sub)\n\tbuf := &bytes.Buffer{}\n\n\tgo func() {\n\t\tfor logM := range sub {\n\t\t\tselect {\n\t\t\tcase ch <- logM:\n\t\t\tdefault:\n\t\t\t}\n\t\t}\n\t\tclose(ch)\n\t}()\n\n\tfor logM := range ch {\n\t\tif logM.LogLevel < level {\n\t\t\tcontinue\n\t\t}\n\t\tbuf.Reset()\n\n\t\tif !isStructured {\n\t\t\tif err := json.NewEncoder(buf).Encode(Log{\n\t\t\t\tType:    logM.Type(),\n\t\t\t\tPayload: logM.Payload,\n\t\t\t}); err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t} else {\n\t\t\tnewLevel := logM.Type()\n\t\t\tif newLevel == \"warning\" {\n\t\t\t\tnewLevel = \"warn\"\n\t\t\t}\n\t\t\tif err := json.NewEncoder(buf).Encode(LogStructured{\n\t\t\t\tTime:    time.Now().Format(time.TimeOnly),\n\t\t\t\tLevel:   newLevel,\n\t\t\t\tMessage: logM.Payload,\n\t\t\t\tFields:  []LogStructuredField{},\n\t\t\t}); err != nil {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tvar err error\n\t\tif wsConn == nil {\n\t\t\t_, err = w.Write(buf.Bytes())\n\t\t\tw.(http.Flusher).Flush()\n\t\t} else {\n\t\t\terr = wsWriteServerText(wsConn, buf.Bytes())\n\t\t}\n\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t}\n}\n\nfunc version(w http.ResponseWriter, r *http.Request) {\n\trender.JSON(w, r, render.M{\"meta\": C.Meta, \"version\": C.Version})\n}\n"
  },
  {
    "path": "core/Clash.Meta/hub/route/storage.go",
    "content": "package route\n\nimport (\n\t\"encoding/json\"\n\t\"io\"\n\n\t\"github.com/metacubex/mihomo/component/profile/cachefile\"\n\n\t\"github.com/metacubex/chi\"\n\t\"github.com/metacubex/chi/render\"\n\t\"github.com/metacubex/http\"\n)\n\nfunc storageRouter() http.Handler {\n\tr := chi.NewRouter()\n\tr.Get(\"/{key}\", getStorage)\n\tr.Put(\"/{key}\", setStorage)\n\tr.Delete(\"/{key}\", deleteStorage)\n\treturn r\n}\n\nfunc getStorage(w http.ResponseWriter, r *http.Request) {\n\tkey := getEscapeParam(r, \"key\")\n\tdata := cachefile.Cache().GetStorage(key)\n\tw.Header().Set(\"Content-Type\", \"application/json\")\n\tif len(data) == 0 {\n\t\tw.Write([]byte(\"null\"))\n\t\treturn\n\t}\n\tw.Write(data)\n}\n\nfunc setStorage(w http.ResponseWriter, r *http.Request) {\n\tkey := getEscapeParam(r, \"key\")\n\tdata, err := io.ReadAll(r.Body)\n\tif err != nil {\n\t\trender.Status(r, http.StatusBadRequest)\n\t\trender.JSON(w, r, newError(err.Error()))\n\t\treturn\n\t}\n\tif !json.Valid(data) {\n\t\trender.Status(r, http.StatusBadRequest)\n\t\trender.JSON(w, r, ErrBadRequest)\n\t\treturn\n\t}\n\tif len(data) > 1024*1024 {\n\t\trender.Status(r, http.StatusRequestEntityTooLarge)\n\t\trender.JSON(w, r, newError(\"payload exceeds 1MB limit\"))\n\t\treturn\n\t}\n\tcachefile.Cache().SetStorage(key, data)\n\trender.NoContent(w, r)\n}\n\nfunc deleteStorage(w http.ResponseWriter, r *http.Request) {\n\tkey := getEscapeParam(r, \"key\")\n\tcachefile.Cache().DeleteStorage(key)\n\trender.NoContent(w, r)\n}\n"
  },
  {
    "path": "core/Clash.Meta/hub/route/upgrade.go",
    "content": "package route\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/metacubex/mihomo/component/updater\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/metacubex/chi\"\n\t\"github.com/metacubex/chi/render\"\n\t\"github.com/metacubex/http\"\n)\n\nfunc upgradeRouter() http.Handler {\n\tr := chi.NewRouter()\n\tr.Post(\"/ui\", updateUI)\n\tr.Post(\"/\", func(w http.ResponseWriter, r *http.Request) {\n\t\trender.Status(r, http.StatusForbidden)\n\t\trender.JSON(w, r, newError(\"Not supported\"))\n\t})\n\tif !embedMode {\n\t\tr.Post(\"/geo\", updateGeoDatabases)\n\t}\n\treturn r\n}\n\nfunc upgradeCore(w http.ResponseWriter, r *http.Request) {\n\t// modify from https://github.com/AdguardTeam/AdGuardHome/blob/595484e0b3fb4c457f9bb727a6b94faa78a66c5f/internal/home/controlupdate.go#L108\n\tlog.Infoln(\"start update\")\n\texecPath, err := os.Executable()\n\tif err != nil {\n\t\trender.Status(r, http.StatusInternalServerError)\n\t\trender.JSON(w, r, newError(fmt.Sprintf(\"getting path: %s\", err)))\n\t\treturn\n\t}\n\n\tquery := r.URL.Query()\n\tchannel := query.Get(\"channel\")\n\tforce := query.Get(\"force\") == \"true\"\n\n\terr = updater.DefaultCoreUpdater.Update(execPath, channel, force)\n\tif err != nil {\n\t\tlog.Warnln(\"%s\", err)\n\t\trender.Status(r, http.StatusInternalServerError)\n\t\trender.JSON(w, r, newError(fmt.Sprintf(\"%s\", err)))\n\t\treturn\n\t}\n\n\trender.JSON(w, r, render.M{\"status\": \"ok\"})\n\tif f, ok := w.(http.Flusher); ok {\n\t\tf.Flush()\n\t}\n\n\tgo restartExecutable(execPath)\n}\n\nfunc updateUI(w http.ResponseWriter, r *http.Request) {\n\terr := updater.DefaultUiUpdater.DownloadUI()\n\tif err != nil {\n\t\tlog.Warnln(\"%s\", err)\n\t\trender.Status(r, http.StatusInternalServerError)\n\t\trender.JSON(w, r, newError(fmt.Sprintf(\"%s\", err)))\n\t\treturn\n\t}\n\n\trender.JSON(w, r, render.M{\"status\": \"ok\"})\n\tif f, ok := w.(http.Flusher); ok {\n\t\tf.Flush()\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/anytls/server.go",
    "content": "package anytls\n\nimport (\n\t\"context\"\n\t\"crypto/sha256\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"net\"\n\t\"strings\"\n\t\"sync/atomic\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\t\"github.com/metacubex/mihomo/common/buf\"\n\t\"github.com/metacubex/mihomo/component/ca\"\n\t\"github.com/metacubex/mihomo/component/ech\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/sing\"\n\t\"github.com/metacubex/mihomo/ntp\"\n\t\"github.com/metacubex/mihomo/transport/anytls/padding\"\n\t\"github.com/metacubex/mihomo/transport/anytls/session\"\n\n\t\"github.com/metacubex/sing/common/auth\"\n\t\"github.com/metacubex/sing/common/bufio\"\n\tM \"github.com/metacubex/sing/common/metadata\"\n\t\"github.com/metacubex/tls\"\n)\n\ntype Listener struct {\n\tclosed    bool\n\tconfig    LC.AnyTLSServer\n\tlisteners []net.Listener\n\ttlsConfig *tls.Config\n\tuserMap   map[[32]byte]string\n\tpadding   atomic.Pointer[padding.PaddingFactory]\n}\n\nfunc New(config LC.AnyTLSServer, tunnel C.Tunnel, additions ...inbound.Addition) (sl *Listener, err error) {\n\tif len(additions) == 0 {\n\t\tadditions = []inbound.Addition{\n\t\t\tinbound.WithInName(\"DEFAULT-ANYTLS\"),\n\t\t\tinbound.WithSpecialRules(\"\"),\n\t\t}\n\t}\n\n\ttlsConfig := &tls.Config{Time: ntp.Now}\n\tif config.Certificate != \"\" && config.PrivateKey != \"\" {\n\t\tcertLoader, err := ca.NewTLSKeyPairLoader(config.Certificate, config.PrivateKey)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttlsConfig.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) {\n\t\t\treturn certLoader()\n\t\t}\n\n\t\tif config.EchKey != \"\" {\n\t\t\terr = ech.LoadECHKey(config.EchKey, tlsConfig)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\ttlsConfig.ClientAuth = ca.ClientAuthTypeFromString(config.ClientAuthType)\n\tif len(config.ClientAuthCert) > 0 {\n\t\tif tlsConfig.ClientAuth == tls.NoClientCert {\n\t\t\ttlsConfig.ClientAuth = tls.RequireAndVerifyClientCert\n\t\t}\n\t}\n\tif tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert {\n\t\tpool, err := ca.LoadCertificates(config.ClientAuthCert)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttlsConfig.ClientCAs = pool\n\t}\n\n\tsl = &Listener{\n\t\tconfig:    config,\n\t\ttlsConfig: tlsConfig,\n\t\tuserMap:   make(map[[32]byte]string),\n\t}\n\n\tfor user, password := range config.Users {\n\t\tsl.userMap[sha256.Sum256([]byte(password))] = user\n\t}\n\n\tif len(config.PaddingScheme) > 0 {\n\t\tif !padding.UpdatePaddingScheme([]byte(config.PaddingScheme), &sl.padding) {\n\t\t\treturn nil, errors.New(\"incorrect padding scheme format\")\n\t\t}\n\t} else {\n\t\tpadding.UpdatePaddingScheme(padding.DefaultPaddingScheme, &sl.padding)\n\t}\n\n\t// Using sing handler can automatically handle UoT\n\th, err := sing.NewListenerHandler(sing.ListenerConfig{\n\t\tTunnel:    tunnel,\n\t\tType:      C.ANYTLS,\n\t\tAdditions: additions,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfor _, addr := range strings.Split(config.Listen, \",\") {\n\t\taddr := addr\n\n\t\t//TCP\n\t\tl, err := inbound.Listen(\"tcp\", addr)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif tlsConfig.GetCertificate != nil {\n\t\t\tl = tls.NewListener(l, tlsConfig)\n\t\t} else {\n\t\t\treturn nil, errors.New(\"disallow using AnyTLS without certificates config\")\n\t\t}\n\t\tsl.listeners = append(sl.listeners, l)\n\n\t\tgo func() {\n\t\t\tfor {\n\t\t\t\tc, err := l.Accept()\n\t\t\t\tif err != nil {\n\t\t\t\t\tif sl.closed {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tgo sl.HandleConn(c, h)\n\t\t\t}\n\t\t}()\n\t}\n\n\treturn sl, nil\n}\n\nfunc (l *Listener) Close() error {\n\tl.closed = true\n\tvar retErr error\n\tfor _, lis := range l.listeners {\n\t\terr := lis.Close()\n\t\tif err != nil {\n\t\t\tretErr = err\n\t\t}\n\t}\n\treturn retErr\n}\n\nfunc (l *Listener) Config() string {\n\treturn l.config.String()\n}\n\nfunc (l *Listener) AddrList() (addrList []net.Addr) {\n\tfor _, lis := range l.listeners {\n\t\taddrList = append(addrList, lis.Addr())\n\t}\n\treturn\n}\n\nfunc (l *Listener) HandleConn(conn net.Conn, h *sing.ListenerHandler) {\n\tctx := context.TODO()\n\tdefer conn.Close()\n\n\tb := buf.NewPacket()\n\tdefer b.Release()\n\n\t_, err := b.ReadOnceFrom(conn)\n\tif err != nil {\n\t\treturn\n\t}\n\tconn = bufio.NewCachedConn(conn, b)\n\n\tby, err := b.ReadBytes(32)\n\tif err != nil {\n\t\treturn\n\t}\n\tvar passwordSha256 [32]byte\n\tcopy(passwordSha256[:], by)\n\tif user, ok := l.userMap[passwordSha256]; ok {\n\t\tctx = auth.ContextWithUser(ctx, user)\n\t} else {\n\t\treturn\n\t}\n\tby, err = b.ReadBytes(2)\n\tif err != nil {\n\t\treturn\n\t}\n\tpaddingLen := binary.BigEndian.Uint16(by)\n\tif paddingLen > 0 {\n\t\t_, err = b.ReadBytes(int(paddingLen))\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tsession := session.NewServerSession(conn, func(stream *session.Stream) {\n\t\tdefer stream.Close()\n\n\t\tdestination, err := M.SocksaddrSerializer.ReadAddrPort(stream)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\t// It seems that mihomo does not implement a connection error reporting mechanism, so we report success directly.\n\t\terr = stream.HandshakeSuccess()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\th.NewConnection(ctx, stream, M.Metadata{\n\t\t\tSource:      M.SocksaddrFromNet(conn.RemoteAddr()),\n\t\t\tDestination: destination,\n\t\t})\n\t}, &l.padding)\n\tsession.Run()\n\tsession.Close()\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/auth/auth.go",
    "content": "package auth\n\nimport (\n\t\"github.com/metacubex/mihomo/component/auth\"\n)\n\ntype authStore struct {\n\tauthenticator auth.Authenticator\n}\n\nfunc (a *authStore) Authenticator() auth.Authenticator {\n\treturn a.authenticator\n}\n\nfunc (a *authStore) SetAuthenticator(authenticator auth.Authenticator) {\n\ta.authenticator = authenticator\n}\n\nfunc NewAuthStore(authenticator auth.Authenticator) auth.AuthStore {\n\treturn &authStore{authenticator}\n}\n\nvar Default auth.AuthStore = NewAuthStore(nil)\n\ntype nilAuthStore struct{}\n\nfunc (a *nilAuthStore) Authenticator() auth.Authenticator {\n\treturn nil\n}\n\nfunc (a *nilAuthStore) SetAuthenticator(authenticator auth.Authenticator) {}\n\nvar Nil auth.AuthStore = (*nilAuthStore)(nil) // always return nil, even call SetAuthenticator() with a non-nil authenticator\n"
  },
  {
    "path": "core/Clash.Meta/listener/config/anytls.go",
    "content": "package config\n\nimport (\n\t\"encoding/json\"\n)\n\ntype AnyTLSServer struct {\n\tEnable         bool              `yaml:\"enable\" json:\"enable\"`\n\tListen         string            `yaml:\"listen\" json:\"listen\"`\n\tUsers          map[string]string `yaml:\"users\" json:\"users,omitempty\"`\n\tCertificate    string            `yaml:\"certificate\" json:\"certificate\"`\n\tPrivateKey     string            `yaml:\"private-key\" json:\"private-key\"`\n\tClientAuthType string            `yaml:\"client-auth-type\" json:\"client-auth-type,omitempty\"`\n\tClientAuthCert string            `yaml:\"client-auth-cert\" json:\"client-auth-cert,omitempty\"`\n\tEchKey         string            `yaml:\"ech-key\" json:\"ech-key\"`\n\tPaddingScheme  string            `yaml:\"padding-scheme\" json:\"padding-scheme,omitempty\"`\n}\n\nfunc (t AnyTLSServer) String() string {\n\tb, _ := json.Marshal(t)\n\treturn string(b)\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/config/auth.go",
    "content": "package config\n\nimport (\n\t\"github.com/metacubex/mihomo/component/auth\"\n\t\"github.com/metacubex/mihomo/listener/reality\"\n)\n\n// AuthServer for http/socks/mixed server\ntype AuthServer struct {\n\tEnable         bool\n\tListen         string\n\tAuthStore      auth.AuthStore\n\tCertificate    string\n\tPrivateKey     string\n\tClientAuthType string\n\tClientAuthCert string\n\tEchKey         string\n\tRealityConfig  reality.Config\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/config/hysteria2.go",
    "content": "package config\n\nimport (\n\t\"github.com/metacubex/mihomo/listener/sing\"\n\n\t\"encoding/json\"\n)\n\ntype Hysteria2Server struct {\n\tEnable                bool              `yaml:\"enable\" json:\"enable\"`\n\tListen                string            `yaml:\"listen\" json:\"listen\"`\n\tUsers                 map[string]string `yaml:\"users\" json:\"users,omitempty\"`\n\tObfs                  string            `yaml:\"obfs\" json:\"obfs,omitempty\"`\n\tObfsPassword          string            `yaml:\"obfs-password\" json:\"obfs-password,omitempty\"`\n\tCertificate           string            `yaml:\"certificate\" json:\"certificate\"`\n\tPrivateKey            string            `yaml:\"private-key\" json:\"private-key\"`\n\tClientAuthType        string            `yaml:\"client-auth-type\" json:\"client-auth-type,omitempty\"`\n\tClientAuthCert        string            `yaml:\"client-auth-cert\" json:\"client-auth-cert,omitempty\"`\n\tEchKey                string            `yaml:\"ech-key\" json:\"ech-key,omitempty\"`\n\tMaxIdleTime           int               `yaml:\"max-idle-time\" json:\"max-idle-time,omitempty\"`\n\tALPN                  []string          `yaml:\"alpn\" json:\"alpn,omitempty\"`\n\tUp                    string            `yaml:\"up\" json:\"up,omitempty\"`\n\tDown                  string            `yaml:\"down\" json:\"down,omitempty\"`\n\tIgnoreClientBandwidth bool              `yaml:\"ignore-client-bandwidth\" json:\"ignore-client-bandwidth,omitempty\"`\n\tMasquerade            string            `yaml:\"masquerade\" json:\"masquerade,omitempty\"`\n\tCWND                  int               `yaml:\"cwnd\" json:\"cwnd,omitempty\"`\n\tBBRProfile            string            `yaml:\"bbr-profile\" json:\"bbr-profile,omitempty\"`\n\tUdpMTU                int               `yaml:\"udp-mtu\" json:\"udp-mtu,omitempty\"`\n\tMuxOption             sing.MuxOption    `yaml:\"mux-option\" json:\"mux-option,omitempty\"`\n\n\t// quic-go special config\n\tInitialStreamReceiveWindow     uint64 `yaml:\"initial-stream-receive-window\" json:\"initial-stream-receive-window,omitempty\"`\n\tMaxStreamReceiveWindow         uint64 `yaml:\"max-stream-receive-window\" json:\"max-stream-receive-window,omitempty\"`\n\tInitialConnectionReceiveWindow uint64 `yaml:\"initial-connection-receive-window\" json:\"initial-connection-receive-window,omitempty\"`\n\tMaxConnectionReceiveWindow     uint64 `yaml:\"max-connection-receive-window\" json:\"max-connection-receive-window,omitempty\"`\n}\n\nfunc (h Hysteria2Server) String() string {\n\tb, _ := json.Marshal(h)\n\treturn string(b)\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/config/kcptun.go",
    "content": "package config\n\nimport \"github.com/metacubex/mihomo/transport/kcptun\"\n\ntype KcpTun struct {\n\tEnable        bool `json:\"enable\"`\n\tkcptun.Config `json:\",inline\"`\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/config/shadowsocks.go",
    "content": "package config\n\nimport (\n\t\"github.com/metacubex/mihomo/listener/sing\"\n\n\t\"encoding/json\"\n)\n\ntype ShadowsocksServer struct {\n\tEnable    bool\n\tListen    string\n\tPassword  string\n\tCipher    string\n\tUdp       bool\n\tMuxOption sing.MuxOption `yaml:\"mux-option\" json:\"mux-option,omitempty\"`\n\tShadowTLS ShadowTLS      `yaml:\"shadow-tls\" json:\"shadow-tls,omitempty\"`\n\tKcpTun    KcpTun         `yaml:\"kcp-tun\" json:\"kcp-tun,omitempty\"`\n}\n\nfunc (t ShadowsocksServer) String() string {\n\tb, _ := json.Marshal(t)\n\treturn string(b)\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/config/shadowtls.go",
    "content": "package config\n\ntype ShadowTLS struct {\n\tEnable                 bool\n\tVersion                int\n\tPassword               string\n\tUsers                  []ShadowTLSUser\n\tHandshake              ShadowTLSHandshakeOptions\n\tHandshakeForServerName map[string]ShadowTLSHandshakeOptions\n\tStrictMode             bool\n\tWildcardSNI            string\n}\n\ntype ShadowTLSUser struct {\n\tName     string\n\tPassword string\n}\n\ntype ShadowTLSHandshakeOptions struct {\n\tDest  string\n\tProxy string\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/config/sudoku.go",
    "content": "package config\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/metacubex/mihomo/listener/sing\"\n)\n\n// SudokuServer describes a Sudoku inbound server configuration.\n// It is internal to the listener layer and mainly used for logging and wiring.\ntype SudokuServer struct {\n\tEnable                 bool     `json:\"enable\"`\n\tListen                 string   `json:\"listen\"`\n\tKey                    string   `json:\"key\"`\n\tAEADMethod             string   `json:\"aead-method,omitempty\"`\n\tPaddingMin             *int     `json:\"padding-min,omitempty\"`\n\tPaddingMax             *int     `json:\"padding-max,omitempty\"`\n\tTableType              string   `json:\"table-type,omitempty\"`\n\tHandshakeTimeoutSecond *int     `json:\"handshake-timeout,omitempty\"`\n\tEnablePureDownlink     *bool    `json:\"enable-pure-downlink,omitempty\"`\n\tCustomTable            string   `json:\"custom-table,omitempty\"`\n\tCustomTables           []string `json:\"custom-tables,omitempty\"`\n\tDisableHTTPMask        bool     `json:\"disable-http-mask,omitempty\"`\n\tHTTPMaskMode           string   `json:\"http-mask-mode,omitempty\"`\n\tPathRoot               string   `json:\"path-root,omitempty\"`\n\tFallback               string   `json:\"fallback,omitempty\"`\n\n\t// mihomo private extension (not the part of standard Sudoku protocol)\n\tMuxOption sing.MuxOption `json:\"mux-option,omitempty\"`\n}\n\nfunc (s SudokuServer) String() string {\n\tb, _ := json.Marshal(s)\n\treturn string(b)\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/config/trojan.go",
    "content": "package config\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/metacubex/mihomo/listener/reality\"\n\t\"github.com/metacubex/mihomo/listener/sing\"\n)\n\ntype TrojanUser struct {\n\tUsername string\n\tPassword string\n}\n\ntype TrojanServer struct {\n\tEnable          bool\n\tListen          string\n\tUsers           []TrojanUser\n\tWsPath          string\n\tGrpcServiceName string\n\tCertificate     string\n\tPrivateKey      string\n\tClientAuthType  string\n\tClientAuthCert  string\n\tEchKey          string\n\tRealityConfig   reality.Config\n\tMuxOption       sing.MuxOption\n\tTrojanSSOption  TrojanSSOption\n}\n\n// TrojanSSOption from https://github.com/p4gefau1t/trojan-go/blob/v0.10.6/tunnel/shadowsocks/config.go#L5\ntype TrojanSSOption struct {\n\tEnabled  bool\n\tMethod   string\n\tPassword string\n}\n\nfunc (t TrojanServer) String() string {\n\tb, _ := json.Marshal(t)\n\treturn string(b)\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/config/trusttunnel.go",
    "content": "package config\n\nimport (\n\t\"encoding/json\"\n)\n\ntype TrustTunnelServer struct {\n\tEnable               bool              `yaml:\"enable\" json:\"enable\"`\n\tListen               string            `yaml:\"listen\" json:\"listen\"`\n\tUsers                map[string]string `yaml:\"users\" json:\"users,omitempty\"`\n\tCertificate          string            `yaml:\"certificate\" json:\"certificate\"`\n\tPrivateKey           string            `yaml:\"private-key\" json:\"private-key\"`\n\tClientAuthType       string            `yaml:\"client-auth-type\" json:\"client-auth-type,omitempty\"`\n\tClientAuthCert       string            `yaml:\"client-auth-cert\" json:\"client-auth-cert,omitempty\"`\n\tEchKey               string            `yaml:\"ech-key\" json:\"ech-key\"`\n\tNetwork              []string          `yaml:\"network\" json:\"network,omitempty\"`\n\tCongestionController string            `yaml:\"congestion-controller\" json:\"congestion-controller,omitempty\"`\n\tCWND                 int               `yaml:\"cwnd\" json:\"cwnd,omitempty\"`\n\tBBRProfile           string            `yaml:\"bbr-profile\" json:\"bbr-profile,omitempty\"`\n}\n\nfunc (t TrustTunnelServer) String() string {\n\tb, _ := json.Marshal(t)\n\treturn string(b)\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/config/tuic.go",
    "content": "package config\n\nimport (\n\t\"github.com/metacubex/mihomo/listener/sing\"\n\n\t\"encoding/json\"\n)\n\ntype TuicServer struct {\n\tEnable                bool              `yaml:\"enable\" json:\"enable\"`\n\tListen                string            `yaml:\"listen\" json:\"listen\"`\n\tToken                 []string          `yaml:\"token\" json:\"token,omitempty\"`\n\tUsers                 map[string]string `yaml:\"users\" json:\"users,omitempty\"`\n\tCertificate           string            `yaml:\"certificate\" json:\"certificate\"`\n\tPrivateKey            string            `yaml:\"private-key\" json:\"private-key\"`\n\tClientAuthType        string            `yaml:\"client-auth-type\" json:\"client-auth-type,omitempty\"`\n\tClientAuthCert        string            `yaml:\"client-auth-cert\" json:\"client-auth-cert,omitempty\"`\n\tEchKey                string            `yaml:\"ech-key\" json:\"ech-key\"`\n\tCongestionController  string            `yaml:\"congestion-controller\" json:\"congestion-controller,omitempty\"`\n\tMaxIdleTime           int               `yaml:\"max-idle-time\" json:\"max-idle-time,omitempty\"`\n\tAuthenticationTimeout int               `yaml:\"authentication-timeout\" json:\"authentication-timeout,omitempty\"`\n\tALPN                  []string          `yaml:\"alpn\" json:\"alpn,omitempty\"`\n\tMaxUdpRelayPacketSize int               `yaml:\"max-udp-relay-packet-size\" json:\"max-udp-relay-packet-size,omitempty\"`\n\tMaxDatagramFrameSize  int               `yaml:\"max-datagram-frame-size\" json:\"max-datagram-frame-size,omitempty\"`\n\tCWND                  int               `yaml:\"cwnd\" json:\"cwnd,omitempty\"`\n\tBBRProfile            string            `yaml:\"bbr-profile\" json:\"bbr-profile,omitempty\"`\n\tMuxOption             sing.MuxOption    `yaml:\"mux-option\" json:\"mux-option,omitempty\"`\n}\n\nfunc (t TuicServer) String() string {\n\tb, _ := json.Marshal(t)\n\treturn string(b)\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/config/tun.go",
    "content": "package config\n\nimport (\n\t\"net/netip\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\n\t\"go4.org/netipx\"\n\t\"golang.org/x/exp/slices\"\n)\n\ntype Tun struct {\n\tEnable              bool       `yaml:\"enable\" json:\"enable\"`\n\tDevice              string     `yaml:\"device\" json:\"device\"`\n\tStack               C.TUNStack `yaml:\"stack\" json:\"stack\"`\n\tDNSHijack           []string   `yaml:\"dns-hijack\" json:\"dns-hijack\"`\n\tAutoRoute           bool       `yaml:\"auto-route\" json:\"auto-route\"`\n\tAutoDetectInterface bool       `yaml:\"auto-detect-interface\" json:\"auto-detect-interface\"`\n\n\tMTU                                   uint32         `yaml:\"mtu\" json:\"mtu,omitempty\"`\n\tGSO                                   bool           `yaml:\"gso\" json:\"gso,omitempty\"`\n\tGSOMaxSize                            uint32         `yaml:\"gso-max-size\" json:\"gso-max-size,omitempty\"`\n\tInet4Address                          []netip.Prefix `yaml:\"inet4-address\" json:\"inet4-address,omitempty\"`\n\tInet6Address                          []netip.Prefix `yaml:\"inet6-address\" json:\"inet6-address,omitempty\"`\n\tIPRoute2TableIndex                    int            `yaml:\"iproute2-table-index\" json:\"iproute2-table-index,omitempty\"`\n\tIPRoute2RuleIndex                     int            `yaml:\"iproute2-rule-index\" json:\"iproute2-rule-index,omitempty\"`\n\tAutoRedirect                          bool           `yaml:\"auto-redirect\" json:\"auto-redirect,omitempty\"`\n\tAutoRedirectInputMark                 uint32         `yaml:\"auto-redirect-input-mark\" json:\"auto-redirect-input-mark,omitempty\"`\n\tAutoRedirectOutputMark                uint32         `yaml:\"auto-redirect-output-mark\" json:\"auto-redirect-output-mark,omitempty\"`\n\tAutoRedirectIPRoute2FallbackRuleIndex int            `yaml:\"auto-redirect-iproute2-fallback-rule-index\" json:\"auto-redirect-iproute2-fallback-rule-index,omitempty\"`\n\tLoopbackAddress                       []netip.Addr   `yaml:\"loopback-address\" json:\"loopback-address,omitempty\"`\n\tStrictRoute                           bool           `yaml:\"strict-route\" json:\"strict-route,omitempty\"`\n\tRouteAddress                          []netip.Prefix `yaml:\"route-address\" json:\"route-address,omitempty\"`\n\tRouteAddressSet                       []string       `yaml:\"route-address-set\" json:\"route-address-set,omitempty\"`\n\tRouteExcludeAddress                   []netip.Prefix `yaml:\"route-exclude-address\" json:\"route-exclude-address,omitempty\"`\n\tRouteExcludeAddressSet                []string       `yaml:\"route-exclude-address-set\" json:\"route-exclude-address-set,omitempty\"`\n\tIncludeInterface                      []string       `yaml:\"include-interface\" json:\"include-interface,omitempty\"`\n\tExcludeInterface                      []string       `yaml:\"exclude-interface\" json:\"exclude-interface,omitempty\"`\n\tIncludeUID                            []uint32       `yaml:\"include-uid\" json:\"include-uid,omitempty\"`\n\tIncludeUIDRange                       []string       `yaml:\"include-uid-range\" json:\"include-uid-range,omitempty\"`\n\tExcludeUID                            []uint32       `yaml:\"exclude-uid\" json:\"exclude-uid,omitempty\"`\n\tExcludeUIDRange                       []string       `yaml:\"exclude-uid-range\" json:\"exclude-uid-range,omitempty\"`\n\tExcludeSrcPort                        []uint16       `yaml:\"exclude-src-port\" json:\"exclude-src-port,omitempty\"`\n\tExcludeSrcPortRange                   []string       `yaml:\"exclude-src-port-range\" json:\"exclude-src-port-range,omitempty\"`\n\tExcludeDstPort                        []uint16       `yaml:\"exclude-dst-port\" json:\"exclude-dst-port,omitempty\"`\n\tExcludeDstPortRange                   []string       `yaml:\"exclude-dst-port-range\" json:\"exclude-dst-port-range,omitempty\"`\n\tIncludeAndroidUser                    []int          `yaml:\"include-android-user\" json:\"include-android-user,omitempty\"`\n\tIncludePackage                        []string       `yaml:\"include-package\" json:\"include-package,omitempty\"`\n\tExcludePackage                        []string       `yaml:\"exclude-package\" json:\"exclude-package,omitempty\"`\n\tIncludeMACAddress                     []string       `yaml:\"include-mac-address\" json:\"include-mac-address,omitempty\"`\n\tExcludeMACAddress                     []string       `yaml:\"exclude-mac-address\" json:\"exclude-mac-address,omitempty\"`\n\tEndpointIndependentNat                bool           `yaml:\"endpoint-independent-nat\" json:\"endpoint-independent-nat,omitempty\"`\n\tUDPTimeout                            int64          `yaml:\"udp-timeout\" json:\"udp-timeout,omitempty\"`\n\tDisableICMPForwarding                 bool           `yaml:\"disable-icmp-forwarding\" json:\"disable-icmp-forwarding,omitempty\"`\n\tFileDescriptor                        int            `yaml:\"file-descriptor\" json:\"file-descriptor\"`\n\n\tInet4RouteAddress        []netip.Prefix `yaml:\"inet4-route-address\" json:\"inet4-route-address,omitempty\"`\n\tInet6RouteAddress        []netip.Prefix `yaml:\"inet6-route-address\" json:\"inet6-route-address,omitempty\"`\n\tInet4RouteExcludeAddress []netip.Prefix `yaml:\"inet4-route-exclude-address\" json:\"inet4-route-exclude-address,omitempty\"`\n\tInet6RouteExcludeAddress []netip.Prefix `yaml:\"inet6-route-exclude-address\" json:\"inet6-route-exclude-address,omitempty\"`\n\n\t// darwin special config\n\tRecvMsgX bool `yaml:\"recvmsgx\" json:\"recvmsgx,omitempty\"`\n\tSendMsgX bool `yaml:\"sendmsgx\" json:\"sendmsgx,omitempty\"`\n}\n\nfunc (t *Tun) Sort() {\n\tslices.Sort(t.DNSHijack)\n\n\tslices.SortFunc(t.Inet4Address, netipx.ComparePrefix)\n\tslices.SortFunc(t.Inet6Address, netipx.ComparePrefix)\n\tslices.SortFunc(t.RouteAddress, netipx.ComparePrefix)\n\tslices.Sort(t.RouteAddressSet)\n\tslices.SortFunc(t.RouteExcludeAddress, netipx.ComparePrefix)\n\tslices.Sort(t.RouteExcludeAddressSet)\n\tslices.Sort(t.IncludeInterface)\n\tslices.Sort(t.ExcludeInterface)\n\tslices.Sort(t.IncludeUID)\n\tslices.Sort(t.IncludeUIDRange)\n\tslices.Sort(t.ExcludeUID)\n\tslices.Sort(t.ExcludeUIDRange)\n\tslices.Sort(t.IncludeAndroidUser)\n\tslices.Sort(t.IncludePackage)\n\tslices.Sort(t.ExcludePackage)\n\tslices.Sort(t.IncludeMACAddress)\n\tslices.Sort(t.ExcludeMACAddress)\n\n\tslices.SortFunc(t.Inet4RouteAddress, netipx.ComparePrefix)\n\tslices.SortFunc(t.Inet6RouteAddress, netipx.ComparePrefix)\n\tslices.SortFunc(t.Inet4RouteExcludeAddress, netipx.ComparePrefix)\n\tslices.SortFunc(t.Inet6RouteExcludeAddress, netipx.ComparePrefix)\n}\n\nfunc (t *Tun) Equal(other Tun) bool {\n\tif t.Enable != other.Enable {\n\t\treturn false\n\t}\n\tif t.Device != other.Device {\n\t\treturn false\n\t}\n\tif t.Stack != other.Stack {\n\t\treturn false\n\t}\n\tif !slices.Equal(t.DNSHijack, other.DNSHijack) {\n\t\treturn false\n\t}\n\tif t.AutoRoute != other.AutoRoute {\n\t\treturn false\n\t}\n\tif t.AutoDetectInterface != other.AutoDetectInterface {\n\t\treturn false\n\t}\n\n\tif t.MTU != other.MTU {\n\t\treturn false\n\t}\n\tif t.GSO != other.GSO {\n\t\treturn false\n\t}\n\tif t.GSOMaxSize != other.GSOMaxSize {\n\t\treturn false\n\t}\n\tif !slices.Equal(t.Inet4Address, other.Inet4Address) {\n\t\treturn false\n\t}\n\tif !slices.Equal(t.Inet6Address, other.Inet6Address) {\n\t\treturn false\n\t}\n\tif t.IPRoute2TableIndex != other.IPRoute2TableIndex {\n\t\treturn false\n\t}\n\tif t.IPRoute2RuleIndex != other.IPRoute2RuleIndex {\n\t\treturn false\n\t}\n\tif t.AutoRedirect != other.AutoRedirect {\n\t\treturn false\n\t}\n\tif t.AutoRedirectInputMark != other.AutoRedirectInputMark {\n\t\treturn false\n\t}\n\tif t.AutoRedirectOutputMark != other.AutoRedirectOutputMark {\n\t\treturn false\n\t}\n\tif t.AutoRedirectIPRoute2FallbackRuleIndex != other.AutoRedirectIPRoute2FallbackRuleIndex {\n\t\treturn false\n\t}\n\tif !slices.Equal(t.RouteAddress, other.RouteAddress) {\n\t\treturn false\n\t}\n\tif t.StrictRoute != other.StrictRoute {\n\t\treturn false\n\t}\n\tif !slices.Equal(t.RouteAddress, other.RouteAddress) {\n\t\treturn false\n\t}\n\tif !slices.Equal(t.RouteAddressSet, other.RouteAddressSet) {\n\t\treturn false\n\t}\n\tif !slices.Equal(t.RouteExcludeAddress, other.RouteExcludeAddress) {\n\t\treturn false\n\t}\n\tif !slices.Equal(t.RouteExcludeAddressSet, other.RouteExcludeAddressSet) {\n\t\treturn false\n\t}\n\tif !slices.Equal(t.IncludeInterface, other.IncludeInterface) {\n\t\treturn false\n\t}\n\tif !slices.Equal(t.ExcludeInterface, other.ExcludeInterface) {\n\t\treturn false\n\t}\n\tif !slices.Equal(t.IncludeUID, other.IncludeUID) {\n\t\treturn false\n\t}\n\tif !slices.Equal(t.IncludeUIDRange, other.IncludeUIDRange) {\n\t\treturn false\n\t}\n\tif !slices.Equal(t.ExcludeUID, other.ExcludeUID) {\n\t\treturn false\n\t}\n\tif !slices.Equal(t.ExcludeUIDRange, other.ExcludeUIDRange) {\n\t\treturn false\n\t}\n\tif !slices.Equal(t.IncludeAndroidUser, other.IncludeAndroidUser) {\n\t\treturn false\n\t}\n\tif !slices.Equal(t.IncludePackage, other.IncludePackage) {\n\t\treturn false\n\t}\n\tif !slices.Equal(t.ExcludePackage, other.ExcludePackage) {\n\t\treturn false\n\t}\n\tif !slices.Equal(t.IncludeMACAddress, other.IncludeMACAddress) {\n\t\treturn false\n\t}\n\tif !slices.Equal(t.ExcludeMACAddress, other.ExcludeMACAddress) {\n\t\treturn false\n\t}\n\tif t.EndpointIndependentNat != other.EndpointIndependentNat {\n\t\treturn false\n\t}\n\tif t.UDPTimeout != other.UDPTimeout {\n\t\treturn false\n\t}\n\tif t.DisableICMPForwarding != other.DisableICMPForwarding {\n\t\treturn false\n\t}\n\tif t.FileDescriptor != other.FileDescriptor {\n\t\treturn false\n\t}\n\n\tif !slices.Equal(t.Inet4RouteAddress, other.Inet4RouteAddress) {\n\t\treturn false\n\t}\n\tif !slices.Equal(t.Inet6RouteAddress, other.Inet6RouteAddress) {\n\t\treturn false\n\t}\n\tif !slices.Equal(t.Inet4RouteExcludeAddress, other.Inet4RouteExcludeAddress) {\n\t\treturn false\n\t}\n\tif !slices.Equal(t.Inet6RouteExcludeAddress, other.Inet6RouteExcludeAddress) {\n\t\treturn false\n\t}\n\n\tif t.RecvMsgX != other.RecvMsgX {\n\t\treturn false\n\t}\n\tif t.SendMsgX != other.SendMsgX {\n\t\treturn false\n\t}\n\n\treturn true\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/config/tunnel.go",
    "content": "package config\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"strings\"\n\n\t\"github.com/samber/lo\"\n)\n\ntype tunnel struct {\n\tNetwork []string `yaml:\"network\"`\n\tAddress string   `yaml:\"address\"`\n\tTarget  string   `yaml:\"target\"`\n\tProxy   string   `yaml:\"proxy\"`\n}\n\ntype Tunnel tunnel\n\n// UnmarshalYAML implements yaml.Unmarshaler\nfunc (t *Tunnel) UnmarshalYAML(unmarshal func(any) error) error {\n\tvar tp string\n\tif err := unmarshal(&tp); err != nil {\n\t\tvar inner tunnel\n\t\tif err := unmarshal(&inner); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t*t = Tunnel(inner)\n\t\treturn nil\n\t}\n\n\t// parse udp/tcp,address,target,proxy\n\tparts := lo.Map(strings.Split(tp, \",\"), func(s string, _ int) string {\n\t\treturn strings.TrimSpace(s)\n\t})\n\tif len(parts) != 3 && len(parts) != 4 {\n\t\treturn fmt.Errorf(\"invalid tunnel config %s\", tp)\n\t}\n\tnetwork := strings.Split(parts[0], \"/\")\n\n\t// validate network\n\tfor _, n := range network {\n\t\tswitch n {\n\t\tcase \"tcp\", \"udp\":\n\t\tdefault:\n\t\t\treturn fmt.Errorf(\"invalid tunnel network %s\", n)\n\t\t}\n\t}\n\n\t// validate address and target\n\taddress := parts[1]\n\ttarget := parts[2]\n\tfor _, addr := range []string{address, target} {\n\t\tif _, _, err := net.SplitHostPort(addr); err != nil {\n\t\t\treturn fmt.Errorf(\"invalid tunnel target or address %s\", addr)\n\t\t}\n\t}\n\n\t*t = Tunnel(tunnel{\n\t\tNetwork: network,\n\t\tAddress: address,\n\t\tTarget:  target,\n\t})\n\tif len(parts) == 4 {\n\t\tt.Proxy = parts[3]\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/config/vless.go",
    "content": "package config\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/metacubex/mihomo/listener/reality\"\n\t\"github.com/metacubex/mihomo/listener/sing\"\n)\n\ntype VlessUser struct {\n\tUsername string\n\tUUID     string\n\tFlow     string\n}\n\ntype VlessServer struct {\n\tEnable          bool\n\tListen          string\n\tUsers           []VlessUser\n\tDecryption      string\n\tWsPath          string\n\tXHTTPConfig     XHTTPConfig\n\tGrpcServiceName string\n\tCertificate     string\n\tPrivateKey      string\n\tClientAuthType  string\n\tClientAuthCert  string\n\tEchKey          string\n\tRealityConfig   reality.Config\n\tMuxOption       sing.MuxOption `yaml:\"mux-option\" json:\"mux-option,omitempty\"`\n}\n\ntype XHTTPConfig struct {\n\tPath                 string\n\tHost                 string\n\tMode                 string\n\tXPaddingBytes        string\n\tXPaddingObfsMode     bool\n\tXPaddingKey          string\n\tXPaddingHeader       string\n\tXPaddingPlacement    string\n\tXPaddingMethod       string\n\tUplinkHTTPMethod     string\n\tSessionPlacement     string\n\tSessionKey           string\n\tSeqPlacement         string\n\tSeqKey               string\n\tUplinkDataPlacement  string\n\tUplinkDataKey        string\n\tUplinkChunkSize      string\n\tNoSSEHeader          bool\n\tScStreamUpServerSecs string\n\tScMaxBufferedPosts   string\n\tScMaxEachPostBytes   string\n}\n\nfunc (t VlessServer) String() string {\n\tb, _ := json.Marshal(t)\n\treturn string(b)\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/config/vmess.go",
    "content": "package config\n\nimport (\n\t\"encoding/json\"\n\n\t\"github.com/metacubex/mihomo/listener/reality\"\n\t\"github.com/metacubex/mihomo/listener/sing\"\n)\n\ntype VmessUser struct {\n\tUsername string\n\tUUID     string\n\tAlterID  int\n}\n\ntype VmessServer struct {\n\tEnable          bool\n\tListen          string\n\tUsers           []VmessUser\n\tWsPath          string\n\tGrpcServiceName string\n\tCertificate     string\n\tPrivateKey      string\n\tClientAuthType  string\n\tClientAuthCert  string\n\tEchKey          string\n\tRealityConfig   reality.Config\n\tMuxOption       sing.MuxOption `yaml:\"mux-option\" json:\"mux-option,omitempty\"`\n}\n\nfunc (t VmessServer) String() string {\n\tb, _ := json.Marshal(t)\n\treturn string(b)\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/http/client.go",
    "content": "package http\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n\n\t\"github.com/metacubex/http\"\n)\n\nfunc newClient(srcConn net.Conn, tunnel C.Tunnel, additions []inbound.Addition) *http.Client { // additions using slice let caller can change its value (without size) after newClient return\n\treturn &http.Client{\n\t\tTransport: &http.Transport{\n\t\t\t// from http.DefaultTransport\n\t\t\tMaxIdleConns:          100,\n\t\t\tIdleConnTimeout:       90 * time.Second,\n\t\t\tTLSHandshakeTimeout:   10 * time.Second,\n\t\t\tExpectContinueTimeout: 1 * time.Second,\n\t\t\tDisableCompression:    true, // prevents the Transport add \"Accept-Encoding: gzip\"\n\t\t\tDialContext: func(context context.Context, network, address string) (net.Conn, error) {\n\t\t\t\tif network != \"tcp\" && network != \"tcp4\" && network != \"tcp6\" {\n\t\t\t\t\treturn nil, errors.New(\"unsupported network \" + network)\n\t\t\t\t}\n\n\t\t\t\tdstAddr := socks5.ParseAddr(address)\n\t\t\t\tif dstAddr == nil {\n\t\t\t\t\treturn nil, socks5.ErrAddressNotSupported\n\t\t\t\t}\n\n\t\t\t\tleft, right := N.Pipe()\n\n\t\t\t\tgo tunnel.HandleTCPConn(inbound.NewHTTP(dstAddr, srcConn, right, additions...))\n\n\t\t\t\treturn left, nil\n\t\t\t},\n\t\t},\n\t\tCheckRedirect: func(req *http.Request, via []*http.Request) error {\n\t\t\treturn http.ErrUseLastResponse\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/http/hack.go",
    "content": "package http\n\nimport (\n\t\"bufio\"\n\t_ \"unsafe\"\n\n\t\"github.com/metacubex/http\"\n)\n\n//go:linkname ReadRequest github.com/metacubex/http.readRequest\nfunc ReadRequest(b *bufio.Reader) (req *http.Request, err error)\n"
  },
  {
    "path": "core/Clash.Meta/listener/http/patch_android.go",
    "content": "//go:build android\n\npackage http\n\nimport \"net\"\n\nfunc (l *Listener) Listener() net.Listener {\n\treturn l.listener\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/http/proxy.go",
    "content": "package http\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/component/auth\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/metacubex/http\"\n)\n\ntype bodyWrapper struct {\n\tio.ReadCloser\n\tonce     sync.Once\n\tonHitEOF func()\n}\n\nfunc (b *bodyWrapper) Read(p []byte) (n int, err error) {\n\tn, err = b.ReadCloser.Read(p)\n\tif err == io.EOF && b.onHitEOF != nil {\n\t\tb.once.Do(b.onHitEOF)\n\t}\n\treturn n, err\n}\n\nfunc HandleConn(c net.Conn, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) {\n\tadditions = append(additions, inbound.Placeholder) // Add a placeholder for InUser\n\tinUserIdx := len(additions) - 1\n\tclient := newClient(c, tunnel, additions)\n\tdefer client.CloseIdleConnections()\n\tctx, cancel := context.WithCancel(context.Background())\n\tdefer cancel()\n\tpeekMutex := sync.Mutex{}\n\n\tconn := N.NewBufferedConn(c)\n\n\tauthenticator := store.Authenticator()\n\ttrusted := authenticator == nil // disable authenticate if lru is nil\n\tlastUser := \"\"\n\n\tfor {\n\t\tpeekMutex.Lock()\n\t\trequest, err := ReadRequest(conn.Reader())\n\t\tpeekMutex.Unlock()\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\n\t\trequest.RemoteAddr = conn.RemoteAddr().String()\n\n\t\tkeepAlive := strings.TrimSpace(strings.ToLower(request.Header.Get(\"Proxy-Connection\"))) == \"keep-alive\"\n\n\t\tresp, user := authenticate(request, authenticator) // always call authenticate function to get user\n\t\tif resp == nil {\n\t\t\ttrusted = true\n\t\t}\n\t\tadditions[inUserIdx] = inbound.WithInUser(user)\n\n\t\tif trusted {\n\t\t\tif request.Method == http.MethodConnect {\n\t\t\t\t// Manual writing to support CONNECT for http 1.0 (workaround for uplay client)\n\t\t\t\tif _, err = fmt.Fprintf(conn, \"HTTP/%d.%d %03d %s\\r\\n\\r\\n\", request.ProtoMajor, request.ProtoMinor, http.StatusOK, \"Connection established\"); err != nil {\n\t\t\t\t\tbreak // close connection\n\t\t\t\t}\n\n\t\t\t\ttunnel.HandleTCPConn(inbound.NewHTTPS(request, conn, additions...))\n\n\t\t\t\treturn // hijack connection\n\t\t\t}\n\n\t\t\thost := request.Header.Get(\"Host\")\n\t\t\tif host != \"\" {\n\t\t\t\trequest.Host = host\n\t\t\t}\n\n\t\t\trequest.RequestURI = \"\"\n\n\t\t\tif isUpgradeRequest(request) {\n\t\t\t\thandleUpgrade(conn, request, tunnel, additions...)\n\n\t\t\t\treturn // hijack connection\n\t\t\t}\n\n\t\t\t// ensure there is a client with correct additions\n\t\t\t// when the authenticated user changed, outbound client should close idle connections\n\t\t\tif user != lastUser {\n\t\t\t\tclient.CloseIdleConnections()\n\t\t\t\tlastUser = user\n\t\t\t}\n\n\t\t\tremoveHopByHopHeaders(request.Header)\n\t\t\tremoveExtraHTTPHostPort(request)\n\n\t\t\tif request.URL.Scheme == \"\" || request.URL.Host == \"\" {\n\t\t\t\tresp = responseWith(request, http.StatusBadRequest)\n\t\t\t} else {\n\t\t\t\trequest = request.WithContext(ctx)\n\n\t\t\t\tstartBackgroundRead := func() {\n\t\t\t\t\tgo func() {\n\t\t\t\t\t\tpeekMutex.Lock()\n\t\t\t\t\t\tdefer peekMutex.Unlock()\n\t\t\t\t\t\t_, err := conn.Peek(1)\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tcancel()\n\t\t\t\t\t\t}\n\t\t\t\t\t}()\n\t\t\t\t}\n\t\t\t\tif request.Body == nil || request.Body == http.NoBody {\n\t\t\t\t\tstartBackgroundRead()\n\t\t\t\t} else {\n\t\t\t\t\trequest.Body = &bodyWrapper{ReadCloser: request.Body, onHitEOF: startBackgroundRead}\n\t\t\t\t}\n\t\t\t\tresp, err = client.Do(request)\n\t\t\t\tif err != nil {\n\t\t\t\t\tresp = responseWith(request, http.StatusBadGateway)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tremoveHopByHopHeaders(resp.Header)\n\t\t}\n\n\t\tif !keepAlive {\n\t\t\tresp.Close = true // close connection if keep-alive is not set\n\t\t}\n\t\tif keepAlive && resp.ContentLength > 0 {\n\t\t\tresp.Close = false // don't need to close connection if content length is positive numbers\n\t\t}\n\n\t\tif !resp.Close {\n\t\t\tresp.Header.Set(\"Proxy-Connection\", \"keep-alive\")\n\t\t\tresp.Header.Set(\"Connection\", \"keep-alive\")\n\t\t\tresp.Header.Set(\"Keep-Alive\", \"timeout=4\")\n\t\t}\n\n\t\terr = resp.Write(conn)\n\t\tif err != nil || resp.Close {\n\t\t\tbreak // close connection\n\t\t}\n\t}\n\n\t_ = conn.Close()\n}\n\nfunc authenticate(request *http.Request, authenticator auth.Authenticator) (resp *http.Response, user string) {\n\tcredential := parseBasicProxyAuthorization(request)\n\tif credential == \"\" && authenticator != nil {\n\t\tresp = responseWith(request, http.StatusProxyAuthRequired)\n\t\tresp.Header.Set(\"Proxy-Authenticate\", \"Basic\")\n\t\treturn\n\t}\n\tuser, pass, err := decodeBasicProxyAuthorization(credential)\n\tauthed := authenticator == nil || (err == nil && authenticator.Verify(user, pass))\n\tif !authed {\n\t\tlog.Infoln(\"Auth failed from %s\", request.RemoteAddr)\n\t\treturn responseWith(request, http.StatusForbidden), user\n\t}\n\tlog.Debugln(\"Auth success from %s -> %s\", request.RemoteAddr, user)\n\treturn\n}\n\nfunc responseWith(request *http.Request, statusCode int) *http.Response {\n\treturn &http.Response{\n\t\tStatusCode: statusCode,\n\t\tStatus:     http.StatusText(statusCode),\n\t\tProto:      request.Proto,\n\t\tProtoMajor: request.ProtoMajor,\n\t\tProtoMinor: request.ProtoMinor,\n\t\tHeader:     http.Header{},\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/http/server.go",
    "content": "package http\n\nimport (\n\t\"errors\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\t\"github.com/metacubex/mihomo/component/ca\"\n\t\"github.com/metacubex/mihomo/component/ech\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tauthStore \"github.com/metacubex/mihomo/listener/auth\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/reality\"\n\t\"github.com/metacubex/mihomo/ntp\"\n\n\t\"github.com/metacubex/tls\"\n)\n\ntype Listener struct {\n\tlistener net.Listener\n\taddr     string\n\tclosed   bool\n}\n\n// RawAddress implements C.Listener\nfunc (l *Listener) RawAddress() string {\n\treturn l.addr\n}\n\n// Address implements C.Listener\nfunc (l *Listener) Address() string {\n\treturn l.listener.Addr().String()\n}\n\n// Close implements C.Listener\nfunc (l *Listener) Close() error {\n\tl.closed = true\n\treturn l.listener.Close()\n}\n\nfunc New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {\n\treturn NewWithConfig(LC.AuthServer{Enable: true, Listen: addr, AuthStore: authStore.Default}, tunnel, additions...)\n}\n\n// NewWithAuthenticate\n// never change type traits because it's used in CMFA\nfunc NewWithAuthenticate(addr string, tunnel C.Tunnel, authenticate bool, additions ...inbound.Addition) (*Listener, error) {\n\tstore := authStore.Default\n\tif !authenticate {\n\t\tstore = authStore.Nil\n\t}\n\treturn NewWithConfig(LC.AuthServer{Enable: true, Listen: addr, AuthStore: store}, tunnel, additions...)\n}\n\nfunc NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {\n\tisDefault := false\n\tif len(additions) == 0 {\n\t\tisDefault = true\n\t\tadditions = []inbound.Addition{\n\t\t\tinbound.WithInName(\"DEFAULT-HTTP\"),\n\t\t\tinbound.WithSpecialRules(\"\"),\n\t\t}\n\t}\n\n\tl, err := inbound.Listen(\"tcp\", config.Listen)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttlsConfig := &tls.Config{Time: ntp.Now}\n\tvar realityBuilder *reality.Builder\n\n\tif config.Certificate != \"\" && config.PrivateKey != \"\" {\n\t\tcertLoader, err := ca.NewTLSKeyPairLoader(config.Certificate, config.PrivateKey)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttlsConfig.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) {\n\t\t\treturn certLoader()\n\t\t}\n\n\t\tif config.EchKey != \"\" {\n\t\t\terr = ech.LoadECHKey(config.EchKey, tlsConfig)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\ttlsConfig.ClientAuth = ca.ClientAuthTypeFromString(config.ClientAuthType)\n\tif len(config.ClientAuthCert) > 0 {\n\t\tif tlsConfig.ClientAuth == tls.NoClientCert {\n\t\t\ttlsConfig.ClientAuth = tls.RequireAndVerifyClientCert\n\t\t}\n\t}\n\tif tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert {\n\t\tpool, err := ca.LoadCertificates(config.ClientAuthCert)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttlsConfig.ClientCAs = pool\n\t}\n\tif config.RealityConfig.PrivateKey != \"\" {\n\t\tif tlsConfig.GetCertificate != nil {\n\t\t\treturn nil, errors.New(\"certificate is unavailable in reality\")\n\t\t}\n\t\tif tlsConfig.ClientAuth != tls.NoClientCert {\n\t\t\treturn nil, errors.New(\"client-auth is unavailable in reality\")\n\t\t}\n\t\trealityBuilder, err = config.RealityConfig.Build(tunnel)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tif realityBuilder != nil {\n\t\tl = realityBuilder.NewListener(l)\n\t} else if tlsConfig.GetCertificate != nil {\n\t\tl = tls.NewListener(l, tlsConfig)\n\t}\n\n\thl := &Listener{\n\t\tlistener: l,\n\t\taddr:     config.Listen,\n\t}\n\n\tgo func() {\n\t\tfor {\n\t\t\tconn, err := hl.listener.Accept()\n\t\t\tif err != nil {\n\t\t\t\tif hl.closed {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tstore := config.AuthStore\n\t\t\tif isDefault || store == authStore.Default { // only apply on default listener\n\t\t\t\tif !inbound.IsRemoteAddrDisAllowed(conn.RemoteAddr()) {\n\t\t\t\t\t_ = conn.Close()\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif inbound.SkipAuthRemoteAddr(conn.RemoteAddr()) {\n\t\t\t\t\tstore = authStore.Nil\n\t\t\t\t}\n\t\t\t}\n\t\t\tgo HandleConn(conn, tunnel, store, additions...)\n\t\t}\n\t}()\n\n\treturn hl, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/http/upgrade.go",
    "content": "package http\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/tls\"\n)\n\nfunc isUpgradeRequest(req *http.Request) bool {\n\tfor _, header := range req.Header[\"Connection\"] {\n\t\tfor _, elm := range strings.Split(header, \",\") {\n\t\t\tif strings.EqualFold(strings.TrimSpace(elm), \"Upgrade\") {\n\t\t\t\treturn true\n\t\t\t}\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc handleUpgrade(conn net.Conn, request *http.Request, tunnel C.Tunnel, additions ...inbound.Addition) {\n\tdefer conn.Close()\n\n\tremoveProxyHeaders(request.Header)\n\tremoveExtraHTTPHostPort(request)\n\n\taddress := request.Host\n\tif _, _, err := net.SplitHostPort(address); err != nil {\n\t\taddress = net.JoinHostPort(address, \"80\")\n\t}\n\n\tdstAddr := socks5.ParseAddr(address)\n\tif dstAddr == nil {\n\t\treturn\n\t}\n\n\tleft, right := N.Pipe()\n\n\tgo tunnel.HandleTCPConn(inbound.NewHTTP(dstAddr, conn, right, additions...))\n\n\tvar bufferedLeft *N.BufferedConn\n\tif request.TLS != nil {\n\t\ttlsConn := tls.Client(left, &tls.Config{\n\t\t\tServerName: request.URL.Hostname(),\n\t\t})\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)\n\t\tdefer cancel()\n\t\tif tlsConn.HandshakeContext(ctx) != nil {\n\t\t\t_ = left.Close()\n\t\t\treturn\n\t\t}\n\n\t\tbufferedLeft = N.NewBufferedConn(tlsConn)\n\t} else {\n\t\tbufferedLeft = N.NewBufferedConn(left)\n\t}\n\tdefer func() {\n\t\t_ = bufferedLeft.Close()\n\t}()\n\n\terr := request.Write(bufferedLeft)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tresp, err := http.ReadResponse(bufferedLeft.Reader(), request)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tremoveProxyHeaders(resp.Header)\n\n\terr = resp.Write(conn)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tif resp.StatusCode == http.StatusSwitchingProtocols {\n\t\tN.Relay(bufferedLeft, conn)\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/http/utils.go",
    "content": "package http\n\nimport (\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"net\"\n\t\"net/netip\"\n\t\"strings\"\n\n\t\"github.com/metacubex/http\"\n)\n\n// removeHopByHopHeaders remove Proxy-* headers\nfunc removeProxyHeaders(header http.Header) {\n\theader.Del(\"Proxy-Connection\")\n\theader.Del(\"Proxy-Authenticate\")\n\theader.Del(\"Proxy-Authorization\")\n}\n\n// removeHopByHopHeaders remove hop-by-hop header\nfunc removeHopByHopHeaders(header http.Header) {\n\t// Strip hop-by-hop header based on RFC:\n\t// http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.5.1\n\t// https://www.mnot.net/blog/2011/07/11/what_proxies_must_do\n\n\tremoveProxyHeaders(header)\n\n\theader.Del(\"TE\")\n\theader.Del(\"Trailers\")\n\theader.Del(\"Transfer-Encoding\")\n\theader.Del(\"Upgrade\")\n\n\tconnections := header.Get(\"Connection\")\n\theader.Del(\"Connection\")\n\tif len(connections) == 0 {\n\t\treturn\n\t}\n\tfor _, h := range strings.Split(connections, \",\") {\n\t\theader.Del(strings.TrimSpace(h))\n\t}\n}\n\n// removeExtraHTTPHostPort remove extra host port (example.com:80 --> example.com)\n// It resolves the behavior of some HTTP servers that do not handle host:80 (e.g. baidu.com)\nfunc removeExtraHTTPHostPort(req *http.Request) {\n\thost := req.Host\n\tif host == \"\" {\n\t\thost = req.URL.Host\n\t}\n\n\tif pHost, port, err := net.SplitHostPort(host); err == nil && port == \"80\" {\n\t\thost = pHost\n\t\tif ip, err := netip.ParseAddr(pHost); err == nil && ip.Is6() {\n\t\t\t// RFC 2617 Sec 3.2.2, for IPv6 literal\n\t\t\t// addresses the Host header needs to follow the RFC 2732 grammar for \"host\"\n\t\t\thost = \"[\" + host + \"]\"\n\t\t}\n\t}\n\n\treq.Host = host\n\treq.URL.Host = host\n}\n\n// parseBasicProxyAuthorization parse header Proxy-Authorization and return base64-encoded credential\nfunc parseBasicProxyAuthorization(request *http.Request) string {\n\tvalue := request.Header.Get(\"Proxy-Authorization\")\n\tconst prefix = \"Basic \"\n\t// According to RFC7617, the scheme should be case-insensitive.\n\t// In practice, some implementations do use different case styles, causing authentication to fail\n\t// eg: https://github.com/algesten/ureq/blob/381fd42cfcb80a5eb709d64860aa0ae726f17b8e/src/unversioned/transport/connect.rs#L118\n\tif len(value) < len(prefix) || !strings.EqualFold(value[:len(prefix)], prefix) {\n\t\treturn \"\"\n\t}\n\n\treturn value[6:] // value[len(\"Basic \"):]\n}\n\n// decodeBasicProxyAuthorization decode base64-encoded credential\nfunc decodeBasicProxyAuthorization(credential string) (string, string, error) {\n\tplain, err := base64.StdEncoding.DecodeString(credential)\n\tif err != nil {\n\t\treturn \"\", \"\", err\n\t}\n\n\tuser, pass, found := strings.Cut(string(plain), \":\")\n\tif !found {\n\t\treturn \"\", \"\", errors.New(\"invalid login\")\n\t}\n\n\treturn user, pass, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/anytls.go",
    "content": "package inbound\n\nimport (\n\t\"strings\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/listener/anytls\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\ntype AnyTLSOption struct {\n\tBaseOption\n\tUsers          map[string]string `inbound:\"users,omitempty\"`\n\tCertificate    string            `inbound:\"certificate\"`\n\tPrivateKey     string            `inbound:\"private-key\"`\n\tClientAuthType string            `inbound:\"client-auth-type,omitempty\"`\n\tClientAuthCert string            `inbound:\"client-auth-cert,omitempty\"`\n\tEchKey         string            `inbound:\"ech-key,omitempty\"`\n\tPaddingScheme  string            `inbound:\"padding-scheme,omitempty\"`\n}\n\nfunc (o AnyTLSOption) Equal(config C.InboundConfig) bool {\n\treturn optionToString(o) == optionToString(config)\n}\n\ntype AnyTLS struct {\n\t*Base\n\tconfig *AnyTLSOption\n\tl      C.MultiAddrListener\n\tvs     LC.AnyTLSServer\n}\n\nfunc NewAnyTLS(options *AnyTLSOption) (*AnyTLS, error) {\n\tbase, err := NewBase(&options.BaseOption)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &AnyTLS{\n\t\tBase:   base,\n\t\tconfig: options,\n\t\tvs: LC.AnyTLSServer{\n\t\t\tEnable:         true,\n\t\t\tListen:         base.RawAddress(),\n\t\t\tUsers:          options.Users,\n\t\t\tCertificate:    options.Certificate,\n\t\t\tPrivateKey:     options.PrivateKey,\n\t\t\tClientAuthType: options.ClientAuthType,\n\t\t\tClientAuthCert: options.ClientAuthCert,\n\t\t\tEchKey:         options.EchKey,\n\t\t\tPaddingScheme:  options.PaddingScheme,\n\t\t},\n\t}, nil\n}\n\n// Config implements constant.InboundListener\nfunc (v *AnyTLS) Config() C.InboundConfig {\n\treturn v.config\n}\n\n// Address implements constant.InboundListener\nfunc (v *AnyTLS) Address() string {\n\tvar addrList []string\n\tif v.l != nil {\n\t\tfor _, addr := range v.l.AddrList() {\n\t\t\taddrList = append(addrList, addr.String())\n\t\t}\n\t}\n\treturn strings.Join(addrList, \",\")\n}\n\n// Listen implements constant.InboundListener\nfunc (v *AnyTLS) Listen(tunnel C.Tunnel) error {\n\tvar err error\n\tv.l, err = anytls.New(v.vs, tunnel, v.Additions()...)\n\tif err != nil {\n\t\treturn err\n\t}\n\tlog.Infoln(\"AnyTLS[%s] proxy listening at: %s\", v.Name(), v.Address())\n\treturn nil\n}\n\n// Close implements constant.InboundListener\nfunc (v *AnyTLS) Close() error {\n\treturn v.l.Close()\n}\n\nvar _ C.InboundListener = (*AnyTLS)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/anytls_test.go",
    "content": "package inbound_test\n\nimport (\n\t\"net/netip\"\n\t\"testing\"\n\n\t\"github.com/metacubex/mihomo/adapter/outbound\"\n\t\"github.com/metacubex/mihomo/listener/inbound\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc testInboundAnyTLS(t *testing.T, inboundOptions inbound.AnyTLSOption, outboundOptions outbound.AnyTLSOption) {\n\tt.Parallel()\n\tinboundOptions.BaseOption = inbound.BaseOption{\n\t\tNameStr: \"anytls_inbound\",\n\t\tListen:  \"127.0.0.1\",\n\t\tPort:    \"0\",\n\t}\n\tinboundOptions.Users = map[string]string{\"test\": userUUID}\n\tin, err := inbound.NewAnyTLS(&inboundOptions)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\n\ttunnel := NewHttpTestTunnel()\n\tdefer tunnel.Close()\n\n\terr = in.Listen(tunnel)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\tdefer in.Close()\n\n\taddrPort, err := netip.ParseAddrPort(in.Address())\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\n\toutboundOptions.Name = \"anytls_outbound\"\n\toutboundOptions.Server = addrPort.Addr().String()\n\toutboundOptions.Port = int(addrPort.Port())\n\toutboundOptions.Password = userUUID\n\toutboundOptions.DialerForAPI = tunnel.NewDialer()\n\n\tout, err := outbound.NewAnyTLS(outboundOptions)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\tdefer out.Close()\n\n\ttunnel.DoTest(t, out)\n}\n\nfunc TestInboundAnyTLS_TLS(t *testing.T) {\n\tinboundOptions := inbound.AnyTLSOption{\n\t\tCertificate: tlsCertificate,\n\t\tPrivateKey:  tlsPrivateKey,\n\t}\n\toutboundOptions := outbound.AnyTLSOption{\n\t\tFingerprint: tlsFingerprint,\n\t}\n\ttestInboundAnyTLS(t, inboundOptions, outboundOptions)\n\tt.Run(\"ECH\", func(t *testing.T) {\n\t\tinboundOptions := inboundOptions\n\t\toutboundOptions := outboundOptions\n\t\tinboundOptions.EchKey = echKeyPem\n\t\toutboundOptions.ECHOpts = outbound.ECHOptions{\n\t\t\tEnable: true,\n\t\t\tConfig: echConfigBase64,\n\t\t}\n\t\ttestInboundAnyTLS(t, inboundOptions, outboundOptions)\n\t})\n\tt.Run(\"mTLS\", func(t *testing.T) {\n\t\tinboundOptions := inboundOptions\n\t\toutboundOptions := outboundOptions\n\t\tinboundOptions.ClientAuthCert = tlsAuthCertificate\n\t\toutboundOptions.Certificate = tlsAuthCertificate\n\t\toutboundOptions.PrivateKey = tlsAuthPrivateKey\n\t\ttestInboundAnyTLS(t, inboundOptions, outboundOptions)\n\t})\n\tt.Run(\"mTLS+ECH\", func(t *testing.T) {\n\t\tinboundOptions := inboundOptions\n\t\toutboundOptions := outboundOptions\n\t\tinboundOptions.ClientAuthCert = tlsAuthCertificate\n\t\toutboundOptions.Certificate = tlsAuthCertificate\n\t\toutboundOptions.PrivateKey = tlsAuthPrivateKey\n\t\tinboundOptions.EchKey = echKeyPem\n\t\toutboundOptions.ECHOpts = outbound.ECHOptions{\n\t\t\tEnable: true,\n\t\t\tConfig: echConfigBase64,\n\t\t}\n\t\ttestInboundAnyTLS(t, inboundOptions, outboundOptions)\n\t})\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/auth.go",
    "content": "package inbound\n\nimport (\n\t\"github.com/metacubex/mihomo/component/auth\"\n\tauthStore \"github.com/metacubex/mihomo/listener/auth\"\n)\n\ntype AuthUser struct {\n\tUsername string `inbound:\"username\"`\n\tPassword string `inbound:\"password\"`\n}\n\ntype AuthUsers []AuthUser\n\nfunc (a AuthUsers) GetAuthStore() auth.AuthStore {\n\tif a != nil { // structure's Decode will ensure value not nil when input has value even it was set an empty array\n\t\tif len(a) == 0 {\n\t\t\treturn authStore.Nil\n\t\t}\n\t\tusers := make([]auth.AuthUser, len(a))\n\t\tfor i, user := range a {\n\t\t\tusers[i] = auth.AuthUser{\n\t\t\t\tUser: user.Username,\n\t\t\t\tPass: user.Password,\n\t\t\t}\n\t\t}\n\t\tauthenticator := auth.NewAuthenticator(users)\n\t\treturn authStore.NewAuthStore(authenticator)\n\t}\n\treturn authStore.Default\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/base.go",
    "content": "package inbound\n\nimport (\n\t\"encoding/json\"\n\t\"net\"\n\t\"net/netip\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\ntype Base struct {\n\tconfig       *BaseOption\n\tname         string\n\tspecialRules string\n\tlistenAddr   netip.Addr\n\tports        utils.IntRanges[uint16]\n}\n\nfunc NewBase(options *BaseOption) (*Base, error) {\n\tif options.Listen == \"\" {\n\t\toptions.Listen = \"0.0.0.0\"\n\t}\n\taddr, err := netip.ParseAddr(options.Listen)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tports, err := utils.NewUnsignedRanges[uint16](options.Port)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &Base{\n\t\tname:         options.Name(),\n\t\tlistenAddr:   addr,\n\t\tspecialRules: options.SpecialRules,\n\t\tports:        ports,\n\t\tconfig:       options,\n\t}, nil\n}\n\n// Config implements constant.InboundListener\nfunc (b *Base) Config() C.InboundConfig {\n\treturn b.config\n}\n\n// Address implements constant.InboundListener\nfunc (b *Base) Address() string {\n\treturn b.RawAddress()\n}\n\n// Close implements constant.InboundListener\nfunc (*Base) Close() error {\n\treturn nil\n}\n\n// Name implements constant.InboundListener\nfunc (b *Base) Name() string {\n\treturn b.name\n}\n\n// RawAddress implements constant.InboundListener\nfunc (b *Base) RawAddress() string {\n\tif len(b.ports) == 0 {\n\t\treturn net.JoinHostPort(b.listenAddr.String(), \"0\")\n\t}\n\taddress := make([]string, 0, len(b.ports))\n\tb.ports.Range(func(port uint16) bool {\n\t\taddress = append(address, net.JoinHostPort(b.listenAddr.String(), strconv.Itoa(int(port))))\n\t\treturn true\n\t})\n\treturn strings.Join(address, \",\")\n}\n\n// Listen implements constant.InboundListener\nfunc (*Base) Listen(tunnel C.Tunnel) error {\n\treturn nil\n}\n\nfunc (b *Base) Additions() []inbound.Addition {\n\treturn b.config.Additions()\n}\n\nvar _ C.InboundListener = (*Base)(nil)\n\ntype BaseOption struct {\n\tNameStr      string `inbound:\"name\"`\n\tListen       string `inbound:\"listen,omitempty\"`\n\tPort         string `inbound:\"port,omitempty\"`\n\tSpecialRules string `inbound:\"rule,omitempty\"`\n\tSpecialProxy string `inbound:\"proxy,omitempty\"`\n}\n\nfunc (o BaseOption) Name() string {\n\treturn o.NameStr\n}\n\nfunc (o BaseOption) Equal(config C.InboundConfig) bool {\n\treturn optionToString(o) == optionToString(config)\n}\n\nfunc (o BaseOption) Additions() []inbound.Addition {\n\treturn []inbound.Addition{\n\t\tinbound.WithInName(o.NameStr),\n\t\tinbound.WithSpecialRules(o.SpecialRules),\n\t\tinbound.WithSpecialProxy(o.SpecialProxy),\n\t}\n}\n\nvar _ C.InboundConfig = (*BaseOption)(nil)\n\nfunc optionToString(option any) string {\n\tstr, _ := json.Marshal(option)\n\treturn string(str)\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/common_test.go",
    "content": "package inbound_test\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/rand\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/netip\"\n\t\"os\"\n\t\"strconv\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/pool\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"github.com/metacubex/mihomo/component/ca\"\n\t\"github.com/metacubex/mihomo/component/dialer\"\n\t\"github.com/metacubex/mihomo/component/ech\"\n\t\"github.com/metacubex/mihomo/component/generator\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\n\t\"github.com/metacubex/chi\"\n\t\"github.com/metacubex/chi/render\"\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/tls\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nvar httpPath = \"/inbound_test\"\nvar httpData = make([]byte, 2*pool.RelayBufferSize)\nvar remoteAddr = netip.MustParseAddr(\"1.2.3.4\")\nvar userUUID = utils.NewUUIDV4().String()\nvar tlsCertificate, tlsPrivateKey, tlsFingerprint, _ = ca.NewRandomTLSKeyPair(ca.KeyPairTypeP256)\nvar tlsAuthCertificate, tlsAuthPrivateKey, _, _ = ca.NewRandomTLSKeyPair(ca.KeyPairTypeP256)\nvar tlsConfigCert, _ = tls.X509KeyPair([]byte(tlsCertificate), []byte(tlsPrivateKey))\nvar tlsConfig = &tls.Config{Certificates: []tls.Certificate{tlsConfigCert}, NextProtos: []string{\"h2\", \"http/1.1\"}}\nvar tlsClientConfig, _ = ca.GetTLSConfig(ca.Option{Fingerprint: tlsFingerprint})\nvar realityPrivateKey, realityPublickey string\nvar realityDest = \"itunes.apple.com\"\nvar realityShortid = \"10f897e26c4b9478\"\nvar realityRealDial = false\nvar echPublicSni = \"public.sni\"\nvar echConfigBase64, echKeyPem, _ = ech.GenECHConfig(echPublicSni)\n\nfunc init() {\n\trand.Read(httpData)\n\tprivateKey, err := generator.GenX25519PrivateKey()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\trealityPrivateKey = base64.RawURLEncoding.EncodeToString(privateKey.Bytes())\n\trealityPublickey = base64.RawURLEncoding.EncodeToString(privateKey.PublicKey().Bytes())\n}\n\ntype TestDialer struct {\n\tdialer C.Dialer\n\tctx    context.Context\n}\n\nfunc (t *TestDialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {\nstart:\n\tconn, err := t.dialer.DialContext(ctx, network, address)\n\tif err != nil && ctx.Err() == nil && t.ctx.Err() == nil {\n\t\t// We are conducting tests locally, and they shouldn't fail.\n\t\t// However, a large number of requests in a short period during concurrent testing can exhaust system ports.\n\t\t// This can lead to various errors such as WSAECONNREFUSED and WSAENOBUFS.\n\t\t// So we just retry if the context is not canceled.\n\t\tgoto start\n\t}\n\treturn conn, err\n}\n\nfunc (t *TestDialer) ListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error) {\n\treturn t.dialer.ListenPacket(ctx, network, address, rAddrPort)\n}\n\nvar _ C.Dialer = (*TestDialer)(nil)\n\ntype TestTunnel struct {\n\tHandleTCPConnFn    func(conn net.Conn, metadata *C.Metadata)\n\tHandleUDPPacketFn  func(packet C.UDPPacket, metadata *C.Metadata)\n\tNatTableFn         func() C.NatTable\n\tCloseFn            func() error\n\tDoSequentialTestFn func(t *testing.T, proxy C.ProxyAdapter)\n\tDoConcurrentTestFn func(t *testing.T, proxy C.ProxyAdapter)\n\tNewDialerFn        func() C.Dialer\n}\n\nfunc (tt *TestTunnel) HandleTCPConn(conn net.Conn, metadata *C.Metadata) {\n\ttt.HandleTCPConnFn(conn, metadata)\n}\n\nfunc (tt *TestTunnel) HandleUDPPacket(packet C.UDPPacket, metadata *C.Metadata) {\n\ttt.HandleUDPPacketFn(packet, metadata)\n}\n\nfunc (tt *TestTunnel) NatTable() C.NatTable {\n\treturn tt.NatTableFn()\n}\n\nfunc (tt *TestTunnel) Close() error {\n\treturn tt.CloseFn()\n}\n\nfunc (tt *TestTunnel) DoTest(t *testing.T, proxy C.ProxyAdapter) {\n\ttt.DoSequentialTestFn(t, proxy)\n\ttt.DoConcurrentTestFn(t, proxy)\n}\n\nfunc (tt *TestTunnel) DoSequentialTest(t *testing.T, proxy C.ProxyAdapter) {\n\ttt.DoSequentialTestFn(t, proxy)\n}\n\nfunc (tt *TestTunnel) DoConcurrentTest(t *testing.T, proxy C.ProxyAdapter) {\n\ttt.DoConcurrentTestFn(t, proxy)\n}\n\nfunc (tt *TestTunnel) NewDialer() C.Dialer {\n\treturn tt.NewDialerFn()\n}\n\ntype TestTunnelListener struct {\n\tch     chan net.Conn\n\tctx    context.Context\n\tcancel context.CancelFunc\n\taddr   net.Addr\n}\n\nfunc (t *TestTunnelListener) Accept() (net.Conn, error) {\n\tselect {\n\tcase conn, ok := <-t.ch:\n\t\tif !ok {\n\t\t\treturn nil, net.ErrClosed\n\t\t}\n\t\treturn conn, nil\n\tcase <-t.ctx.Done():\n\t\treturn nil, t.ctx.Err()\n\t}\n}\n\nfunc (t *TestTunnelListener) Close() error {\n\tt.cancel()\n\treturn nil\n}\n\nfunc (t *TestTunnelListener) Addr() net.Addr {\n\treturn t.addr\n}\n\ntype WaitCloseConn struct {\n\tnet.Conn\n\tch   chan struct{}\n\tonce sync.Once\n}\n\nfunc (c *WaitCloseConn) Close() error {\n\terr := c.Conn.Close()\n\tc.once.Do(func() {\n\t\tclose(c.ch)\n\t})\n\treturn err\n}\n\nvar _ C.Tunnel = (*TestTunnel)(nil)\nvar _ net.Listener = (*TestTunnelListener)(nil)\n\nfunc NewHttpTestTunnel() *TestTunnel {\n\tctx, cancel := context.WithCancel(context.Background())\n\tln := &TestTunnelListener{ch: make(chan net.Conn), ctx: ctx, cancel: cancel, addr: net.TCPAddrFromAddrPort(netip.AddrPortFrom(remoteAddr, 0))}\n\n\tr := chi.NewRouter()\n\tr.Get(httpPath, func(w http.ResponseWriter, r *http.Request) {\n\t\tquery := r.URL.Query()\n\t\tsize, err := strconv.Atoi(query.Get(\"size\"))\n\t\tif err != nil {\n\t\t\trender.Status(r, http.StatusBadRequest)\n\t\t\trender.PlainText(w, r, err.Error())\n\t\t\treturn\n\t\t}\n\t\tio.Copy(io.Discard, r.Body)\n\t\trender.Data(w, r, httpData[:size])\n\t})\n\t//h2Server := &http.Http2Server{}\n\tserver := http.Server{Handler: r}\n\t//_ = http.Http2ConfigureServer(&server, h2Server)\n\tgo server.Serve(ln)\n\ttestFn := func(t *testing.T, proxy C.ProxyAdapter, proto string, size int) {\n\t\treq, err := http.NewRequest(http.MethodGet, fmt.Sprintf(\"%s://%s%s?size=%d\", proto, remoteAddr, httpPath, size), bytes.NewReader(httpData[:size]))\n\t\tif !assert.NoError(t, err) {\n\t\t\treturn\n\t\t}\n\t\treq = req.WithContext(ctx)\n\n\t\tvar dstPort uint16 = 80\n\t\tif proto == \"https\" {\n\t\t\tdstPort = 443\n\t\t}\n\t\tmetadata := &C.Metadata{\n\t\t\tNetWork: C.TCP,\n\t\t\tDstIP:   remoteAddr,\n\t\t\tDstPort: dstPort,\n\t\t}\n\t\tinstance, err := proxy.DialContext(ctx, metadata)\n\t\tif !assert.NoError(t, err) {\n\t\t\treturn\n\t\t}\n\t\tdefer instance.Close()\n\n\t\tvar dialNum atomic.Int32\n\t\tvar extraConns []net.Conn\n\t\tvar extraConnsMu sync.Mutex\n\t\tdefer func() {\n\t\t\textraConnsMu.Lock()\n\t\t\textraConns := append([]net.Conn{}, extraConns...) // clone conn list avoid race condition\n\t\t\textraConnsMu.Unlock()\n\t\t\tfor _, conn := range extraConns {\n\t\t\t\t_ = conn.Close()\n\t\t\t}\n\t\t}()\n\n\t\ttransport := &http.Transport{\n\t\t\tDialContext: func(ctx context.Context, network string, addr string) (net.Conn, error) {\n\t\t\t\tdianNum := dialNum.Add(1)\n\t\t\t\tif dianNum == 1 { // first dial, return instance\n\t\t\t\t\treturn instance, nil\n\t\t\t\t}\n\t\t\t\tt.Logf(\"transport dial time %d more than once in: %s\", dianNum, t.Name())\n\t\t\t\tconn, err := proxy.DialContext(ctx, metadata)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\textraConnsMu.Lock()\n\t\t\t\textraConns = append(extraConns, conn)\n\t\t\t\textraConnsMu.Unlock()\n\t\t\t\treturn conn, nil\n\t\t\t},\n\t\t\t//// from http.DefaultTransport\n\t\t\t//MaxIdleConns:          100,\n\t\t\t//IdleConnTimeout:       90 * time.Second,\n\t\t\t//TLSHandshakeTimeout:   10 * time.Second,\n\t\t\t//ExpectContinueTimeout: 1 * time.Second,\n\t\t\t// for our self-signed cert\n\t\t\tTLSClientConfig: tlsClientConfig.Clone(),\n\t\t\t// open http2\n\t\t\tForceAttemptHTTP2: true,\n\t\t}\n\n\t\tclient := http.Client{\n\t\t\tTimeout:   60 * time.Second,\n\t\t\tTransport: transport,\n\t\t\tCheckRedirect: func(req *http.Request, via []*http.Request) error {\n\t\t\t\treturn http.ErrUseLastResponse\n\t\t\t},\n\t\t}\n\n\t\tdefer client.CloseIdleConnections()\n\n\t\tresp, err := client.Do(req)\n\t\tif !assert.NoError(t, err) {\n\t\t\treturn\n\t\t}\n\n\t\tdefer resp.Body.Close()\n\n\t\tassert.Equal(t, http.StatusOK, resp.StatusCode)\n\t\tif proto == \"https\" { // ensure server using http2\n\t\t\tassert.Equal(t, 2, resp.ProtoMajor)\n\t\t}\n\n\t\tdata, err := io.ReadAll(resp.Body)\n\t\tif !assert.NoError(t, err) {\n\t\t\treturn\n\t\t}\n\t\tassert.Equal(t, httpData[:size], data)\n\t}\n\n\tsequentialTestFn := func(t *testing.T, proxy C.ProxyAdapter) {\n\t\t// Sequential testing for debugging\n\t\tt.Run(\"Sequential\", func(t *testing.T) {\n\t\t\ttestFn(t, proxy, \"http\", len(httpData))\n\t\t\ttestFn(t, proxy, \"https\", len(httpData))\n\t\t})\n\t}\n\n\tconcurrentTestFn := func(t *testing.T, proxy C.ProxyAdapter) {\n\t\t// Concurrent testing to detect stress\n\t\tt.Run(\"Concurrent\", func(t *testing.T) {\n\t\t\tif skip, _ := strconv.ParseBool(os.Getenv(\"SKIP_CONCURRENT_TEST\")); skip {\n\t\t\t\tt.Skip(\"skip concurrent test\")\n\t\t\t}\n\t\t\twg := sync.WaitGroup{}\n\t\t\tnum := len(httpData) / 1024\n\t\t\tfor i := 1; i <= num; i++ {\n\t\t\t\ti := i\n\t\t\t\twg.Add(1)\n\t\t\t\tgo func() {\n\t\t\t\t\ttestFn(t, proxy, \"https\", i*1024)\n\t\t\t\t\tdefer wg.Done()\n\t\t\t\t}()\n\t\t\t}\n\t\t\tfor i := 1; i <= num; i++ {\n\t\t\t\ti := i\n\t\t\t\twg.Add(1)\n\t\t\t\tgo func() {\n\t\t\t\t\ttestFn(t, proxy, \"http\", i*1024)\n\t\t\t\t\tdefer wg.Done()\n\t\t\t\t}()\n\t\t\t}\n\t\t\twg.Wait()\n\t\t})\n\t}\n\n\ttunnel := &TestTunnel{\n\t\tHandleTCPConnFn: func(conn net.Conn, metadata *C.Metadata) {\n\t\t\tdefer conn.Close()\n\t\t\tif metadata.DstIP != remoteAddr && metadata.Host != realityDest {\n\t\t\t\treturn // not match, just return\n\t\t\t}\n\t\t\tc := &WaitCloseConn{\n\t\t\t\tConn: conn,\n\t\t\t\tch:   make(chan struct{}),\n\t\t\t}\n\t\t\tif metadata.DstPort == 443 {\n\t\t\t\ttlsConn := tls.Server(c, tlsConfig)\n\t\t\t\tif metadata.Host == realityDest { // ignore the tls handshake error for realityDest\n\t\t\t\t\tif realityRealDial {\n\t\t\t\t\t\trconn, err := dialer.DialContext(ctx, \"tcp\", metadata.RemoteAddress())\n\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\tpanic(err)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tN.Relay(rconn, conn)\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t//ctx, cancel := context.WithTimeout(ctx, C.DefaultTLSTimeout)\n\t\t\t\t//defer cancel()\n\t\t\t\tif err := tlsConn.HandshakeContext(ctx); err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t//if tlsConn.ConnectionState().NegotiatedProtocol == http.Http2NextProtoTLS {\n\t\t\t\t//\th2Server.ServeConn(tlsConn, &http.Http2ServeConnOpts{BaseConfig: &server})\n\t\t\t\t//} else {\n\t\t\t\t//\tln.ch <- tlsConn\n\t\t\t\t//}\n\t\t\t\tln.ch <- tlsConn\n\t\t\t} else {\n\t\t\t\tln.ch <- c\n\t\t\t}\n\t\t\t<-c.ch\n\t\t},\n\t\tCloseFn:            ln.Close,\n\t\tDoSequentialTestFn: sequentialTestFn,\n\t\tDoConcurrentTestFn: concurrentTestFn,\n\t\tNewDialerFn:        func() C.Dialer { return &TestDialer{dialer: dialer.NewDialer(), ctx: ctx} },\n\t}\n\treturn tunnel\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/http.go",
    "content": "package inbound\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/http\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\ntype HTTPOption struct {\n\tBaseOption\n\tUsers          AuthUsers     `inbound:\"users,omitempty\"`\n\tCertificate    string        `inbound:\"certificate,omitempty\"`\n\tPrivateKey     string        `inbound:\"private-key,omitempty\"`\n\tClientAuthType string        `inbound:\"client-auth-type,omitempty\"`\n\tClientAuthCert string        `inbound:\"client-auth-cert,omitempty\"`\n\tEchKey         string        `inbound:\"ech-key,omitempty\"`\n\tRealityConfig  RealityConfig `inbound:\"reality-config,omitempty\"`\n}\n\nfunc (o HTTPOption) Equal(config C.InboundConfig) bool {\n\treturn optionToString(o) == optionToString(config)\n}\n\ntype HTTP struct {\n\t*Base\n\tconfig *HTTPOption\n\tl      []*http.Listener\n}\n\nfunc NewHTTP(options *HTTPOption) (*HTTP, error) {\n\tbase, err := NewBase(&options.BaseOption)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &HTTP{\n\t\tBase:   base,\n\t\tconfig: options,\n\t}, nil\n}\n\n// Config implements constant.InboundListener\nfunc (h *HTTP) Config() C.InboundConfig {\n\treturn h.config\n}\n\n// Address implements constant.InboundListener\nfunc (h *HTTP) Address() string {\n\tvar addrList []string\n\tfor _, l := range h.l {\n\t\taddrList = append(addrList, l.Address())\n\t}\n\treturn strings.Join(addrList, \",\")\n}\n\n// Listen implements constant.InboundListener\nfunc (h *HTTP) Listen(tunnel C.Tunnel) error {\n\tfor _, addr := range strings.Split(h.RawAddress(), \",\") {\n\t\tl, err := http.NewWithConfig(\n\t\t\tLC.AuthServer{\n\t\t\t\tEnable:         true,\n\t\t\t\tListen:         addr,\n\t\t\t\tAuthStore:      h.config.Users.GetAuthStore(),\n\t\t\t\tCertificate:    h.config.Certificate,\n\t\t\t\tPrivateKey:     h.config.PrivateKey,\n\t\t\t\tClientAuthType: h.config.ClientAuthType,\n\t\t\t\tClientAuthCert: h.config.ClientAuthCert,\n\t\t\t\tEchKey:         h.config.EchKey,\n\t\t\t\tRealityConfig:  h.config.RealityConfig.Build(),\n\t\t\t},\n\t\t\ttunnel,\n\t\t\th.Additions()...,\n\t\t)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\th.l = append(h.l, l)\n\t}\n\tlog.Infoln(\"HTTP[%s] proxy listening at: %s\", h.Name(), h.Address())\n\treturn nil\n}\n\n// Close implements constant.InboundListener\nfunc (h *HTTP) Close() error {\n\tvar errs []error\n\tfor _, l := range h.l {\n\t\terr := l.Close()\n\t\tif err != nil {\n\t\t\terrs = append(errs, fmt.Errorf(\"close tcp listener %s err: %w\", l.Address(), err))\n\t\t}\n\t}\n\tif len(errs) > 0 {\n\t\treturn errors.Join(errs...)\n\t}\n\treturn nil\n}\n\nvar _ C.InboundListener = (*HTTP)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/hysteria2.go",
    "content": "package inbound\n\nimport (\n\t\"strings\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/sing_hysteria2\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\ntype Hysteria2Option struct {\n\tBaseOption\n\tUsers                 map[string]string `inbound:\"users,omitempty\"`\n\tObfs                  string            `inbound:\"obfs,omitempty\"`\n\tObfsPassword          string            `inbound:\"obfs-password,omitempty\"`\n\tCertificate           string            `inbound:\"certificate\"`\n\tPrivateKey            string            `inbound:\"private-key\"`\n\tClientAuthType        string            `inbound:\"client-auth-type,omitempty\"`\n\tClientAuthCert        string            `inbound:\"client-auth-cert,omitempty\"`\n\tEchKey                string            `inbound:\"ech-key,omitempty\"`\n\tMaxIdleTime           int               `inbound:\"max-idle-time,omitempty\"`\n\tALPN                  []string          `inbound:\"alpn,omitempty\"`\n\tUp                    string            `inbound:\"up,omitempty\"`\n\tDown                  string            `inbound:\"down,omitempty\"`\n\tIgnoreClientBandwidth bool              `inbound:\"ignore-client-bandwidth,omitempty\"`\n\tMasquerade            string            `inbound:\"masquerade,omitempty\"`\n\tCWND                  int               `inbound:\"cwnd,omitempty\"`\n\tBBRProfile            string            `inbound:\"bbr-profile,omitempty\"`\n\tUdpMTU                int               `inbound:\"udp-mtu,omitempty\"`\n\tMuxOption             MuxOption         `inbound:\"mux-option,omitempty\"`\n\n\t// quic-go special config\n\tInitialStreamReceiveWindow     uint64 `inbound:\"initial-stream-receive-window,omitempty\"`\n\tMaxStreamReceiveWindow         uint64 `inbound:\"max-stream-receive-window,omitempty\"`\n\tInitialConnectionReceiveWindow uint64 `inbound:\"initial-connection-receive-window,omitempty\"`\n\tMaxConnectionReceiveWindow     uint64 `inbound:\"max-connection-receive-window,omitempty\"`\n}\n\nfunc (o Hysteria2Option) Equal(config C.InboundConfig) bool {\n\treturn optionToString(o) == optionToString(config)\n}\n\ntype Hysteria2 struct {\n\t*Base\n\tconfig *Hysteria2Option\n\tl      *sing_hysteria2.Listener\n\tts     LC.Hysteria2Server\n}\n\nfunc NewHysteria2(options *Hysteria2Option) (*Hysteria2, error) {\n\tbase, err := NewBase(&options.BaseOption)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &Hysteria2{\n\t\tBase:   base,\n\t\tconfig: options,\n\t\tts: LC.Hysteria2Server{\n\t\t\tEnable:                true,\n\t\t\tListen:                base.RawAddress(),\n\t\t\tUsers:                 options.Users,\n\t\t\tObfs:                  options.Obfs,\n\t\t\tObfsPassword:          options.ObfsPassword,\n\t\t\tCertificate:           options.Certificate,\n\t\t\tPrivateKey:            options.PrivateKey,\n\t\t\tClientAuthType:        options.ClientAuthType,\n\t\t\tClientAuthCert:        options.ClientAuthCert,\n\t\t\tEchKey:                options.EchKey,\n\t\t\tMaxIdleTime:           options.MaxIdleTime,\n\t\t\tALPN:                  options.ALPN,\n\t\t\tUp:                    options.Up,\n\t\t\tDown:                  options.Down,\n\t\t\tIgnoreClientBandwidth: options.IgnoreClientBandwidth,\n\t\t\tMasquerade:            options.Masquerade,\n\t\t\tCWND:                  options.CWND,\n\t\t\tBBRProfile:            options.BBRProfile,\n\t\t\tUdpMTU:                options.UdpMTU,\n\t\t\tMuxOption:             options.MuxOption.Build(),\n\t\t\t// quic-go special config\n\t\t\tInitialStreamReceiveWindow:     options.InitialStreamReceiveWindow,\n\t\t\tMaxStreamReceiveWindow:         options.MaxStreamReceiveWindow,\n\t\t\tInitialConnectionReceiveWindow: options.InitialConnectionReceiveWindow,\n\t\t\tMaxConnectionReceiveWindow:     options.MaxConnectionReceiveWindow,\n\t\t},\n\t}, nil\n}\n\n// Config implements constant.InboundListener\nfunc (t *Hysteria2) Config() C.InboundConfig {\n\treturn t.config\n}\n\n// Address implements constant.InboundListener\nfunc (t *Hysteria2) Address() string {\n\tvar addrList []string\n\tif t.l != nil {\n\t\tfor _, addr := range t.l.AddrList() {\n\t\t\taddrList = append(addrList, addr.String())\n\t\t}\n\t}\n\treturn strings.Join(addrList, \",\")\n}\n\n// Listen implements constant.InboundListener\nfunc (t *Hysteria2) Listen(tunnel C.Tunnel) error {\n\tvar err error\n\tt.l, err = sing_hysteria2.New(t.ts, tunnel, t.Additions()...)\n\tif err != nil {\n\t\treturn err\n\t}\n\tlog.Infoln(\"Hysteria2[%s] proxy listening at: %s\", t.Name(), t.Address())\n\treturn nil\n}\n\n// Close implements constant.InboundListener\nfunc (t *Hysteria2) Close() error {\n\treturn t.l.Close()\n}\n\nvar _ C.InboundListener = (*Hysteria2)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/hysteria2_test.go",
    "content": "package inbound_test\n\nimport (\n\t\"net/netip\"\n\t\"testing\"\n\n\t\"github.com/metacubex/mihomo/adapter/outbound\"\n\t\"github.com/metacubex/mihomo/listener/inbound\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc testInboundHysteria2(t *testing.T, inboundOptions inbound.Hysteria2Option, outboundOptions outbound.Hysteria2Option) {\n\tt.Parallel()\n\tinboundOptions.BaseOption = inbound.BaseOption{\n\t\tNameStr: \"hysteria2_inbound\",\n\t\tListen:  \"127.0.0.1\",\n\t\tPort:    \"0\",\n\t}\n\tinboundOptions.Users = map[string]string{\"test\": userUUID}\n\tin, err := inbound.NewHysteria2(&inboundOptions)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\n\ttunnel := NewHttpTestTunnel()\n\tdefer tunnel.Close()\n\n\terr = in.Listen(tunnel)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\tdefer in.Close()\n\n\taddrPort, err := netip.ParseAddrPort(in.Address())\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\n\toutboundOptions.Name = \"hysteria2_outbound\"\n\toutboundOptions.Server = addrPort.Addr().String()\n\toutboundOptions.Port = int(addrPort.Port())\n\toutboundOptions.Password = userUUID\n\toutboundOptions.DialerForAPI = tunnel.NewDialer()\n\n\tout, err := outbound.NewHysteria2(outboundOptions)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\tdefer out.Close()\n\n\ttunnel.DoTest(t, out)\n}\n\nfunc testInboundHysteria2TLS(t *testing.T, inboundOptions inbound.Hysteria2Option, outboundOptions outbound.Hysteria2Option) {\n\ttestInboundHysteria2(t, inboundOptions, outboundOptions)\n\tt.Run(\"ECH\", func(t *testing.T) {\n\t\tinboundOptions := inboundOptions\n\t\toutboundOptions := outboundOptions\n\t\tinboundOptions.EchKey = echKeyPem\n\t\toutboundOptions.ECHOpts = outbound.ECHOptions{\n\t\t\tEnable: true,\n\t\t\tConfig: echConfigBase64,\n\t\t}\n\t\ttestInboundHysteria2(t, inboundOptions, outboundOptions)\n\t})\n\tt.Run(\"mTLS\", func(t *testing.T) {\n\t\tinboundOptions := inboundOptions\n\t\toutboundOptions := outboundOptions\n\t\tinboundOptions.ClientAuthCert = tlsAuthCertificate\n\t\toutboundOptions.Certificate = tlsAuthCertificate\n\t\toutboundOptions.PrivateKey = tlsAuthPrivateKey\n\t\ttestInboundHysteria2(t, inboundOptions, outboundOptions)\n\t})\n\tt.Run(\"mTLS+ECH\", func(t *testing.T) {\n\t\tinboundOptions := inboundOptions\n\t\toutboundOptions := outboundOptions\n\t\tinboundOptions.ClientAuthCert = tlsAuthCertificate\n\t\toutboundOptions.Certificate = tlsAuthCertificate\n\t\toutboundOptions.PrivateKey = tlsAuthPrivateKey\n\t\tinboundOptions.EchKey = echKeyPem\n\t\toutboundOptions.ECHOpts = outbound.ECHOptions{\n\t\t\tEnable: true,\n\t\t\tConfig: echConfigBase64,\n\t\t}\n\t\ttestInboundHysteria2(t, inboundOptions, outboundOptions)\n\t})\n}\n\nfunc TestInboundHysteria2_TLS(t *testing.T) {\n\tinboundOptions := inbound.Hysteria2Option{\n\t\tCertificate: tlsCertificate,\n\t\tPrivateKey:  tlsPrivateKey,\n\t}\n\toutboundOptions := outbound.Hysteria2Option{\n\t\tFingerprint: tlsFingerprint,\n\t}\n\ttestInboundHysteria2TLS(t, inboundOptions, outboundOptions)\n}\n\nfunc TestInboundHysteria2_Salamander(t *testing.T) {\n\tinboundOptions := inbound.Hysteria2Option{\n\t\tCertificate:  tlsCertificate,\n\t\tPrivateKey:   tlsPrivateKey,\n\t\tObfs:         \"salamander\",\n\t\tObfsPassword: userUUID,\n\t}\n\toutboundOptions := outbound.Hysteria2Option{\n\t\tFingerprint:  tlsFingerprint,\n\t\tObfs:         \"salamander\",\n\t\tObfsPassword: userUUID,\n\t}\n\ttestInboundHysteria2TLS(t, inboundOptions, outboundOptions)\n}\n\nfunc TestInboundHysteria2_Brutal(t *testing.T) {\n\tinboundOptions := inbound.Hysteria2Option{\n\t\tCertificate: tlsCertificate,\n\t\tPrivateKey:  tlsPrivateKey,\n\t\tUp:          \"30 Mbps\",\n\t\tDown:        \"200 Mbps\",\n\t}\n\toutboundOptions := outbound.Hysteria2Option{\n\t\tFingerprint: tlsFingerprint,\n\t\tUp:          \"30 Mbps\",\n\t\tDown:        \"200 Mbps\",\n\t}\n\ttestInboundHysteria2TLS(t, inboundOptions, outboundOptions)\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/kcptun.go",
    "content": "package inbound\n\nimport (\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/transport/kcptun\"\n)\n\ntype KcpTun struct {\n\tEnable       bool   `inbound:\"enable\"`\n\tKey          string `inbound:\"key,omitempty\"`\n\tCrypt        string `inbound:\"crypt,omitempty\"`\n\tMode         string `inbound:\"mode,omitempty\"`\n\tConn         int    `inbound:\"conn,omitempty\"`\n\tAutoExpire   int    `inbound:\"autoexpire,omitempty\"`\n\tScavengeTTL  int    `inbound:\"scavengettl,omitempty\"`\n\tMTU          int    `inbound:\"mtu,omitempty\"`\n\tRateLimit    int    `inbound:\"ratelimit,omitempty\"`\n\tSndWnd       int    `inbound:\"sndwnd,omitempty\"`\n\tRcvWnd       int    `inbound:\"rcvwnd,omitempty\"`\n\tDataShard    int    `inbound:\"datashard,omitempty\"`\n\tParityShard  int    `inbound:\"parityshard,omitempty\"`\n\tDSCP         int    `inbound:\"dscp,omitempty\"`\n\tNoComp       bool   `inbound:\"nocomp,omitempty\"`\n\tAckNodelay   bool   `inbound:\"acknodelay,omitempty\"`\n\tNoDelay      int    `inbound:\"nodelay,omitempty\"`\n\tInterval     int    `inbound:\"interval,omitempty\"`\n\tResend       int    `inbound:\"resend,omitempty\"`\n\tNoCongestion int    `inbound:\"nc,omitempty\"`\n\tSockBuf      int    `inbound:\"sockbuf,omitempty\"`\n\tSmuxVer      int    `inbound:\"smuxver,omitempty\"`\n\tSmuxBuf      int    `inbound:\"smuxbuf,omitempty\"`\n\tFrameSize    int    `inbound:\"framesize,omitempty\"`\n\tStreamBuf    int    `inbound:\"streambuf,omitempty\"`\n\tKeepAlive    int    `inbound:\"keepalive,omitempty\"`\n}\n\nfunc (c KcpTun) Build() LC.KcpTun {\n\treturn LC.KcpTun{\n\t\tEnable: c.Enable,\n\t\tConfig: kcptun.Config{\n\t\t\tKey:          c.Key,\n\t\t\tCrypt:        c.Crypt,\n\t\t\tMode:         c.Mode,\n\t\t\tConn:         c.Conn,\n\t\t\tAutoExpire:   c.AutoExpire,\n\t\t\tScavengeTTL:  c.ScavengeTTL,\n\t\t\tMTU:          c.MTU,\n\t\t\tRateLimit:    c.RateLimit,\n\t\t\tSndWnd:       c.SndWnd,\n\t\t\tRcvWnd:       c.RcvWnd,\n\t\t\tDataShard:    c.DataShard,\n\t\t\tParityShard:  c.ParityShard,\n\t\t\tDSCP:         c.DSCP,\n\t\t\tNoComp:       c.NoComp,\n\t\t\tAckNodelay:   c.AckNodelay,\n\t\t\tNoDelay:      c.NoDelay,\n\t\t\tInterval:     c.Interval,\n\t\t\tResend:       c.Resend,\n\t\t\tNoCongestion: c.NoCongestion,\n\t\t\tSockBuf:      c.SockBuf,\n\t\t\tSmuxVer:      c.SmuxVer,\n\t\t\tSmuxBuf:      c.SmuxBuf,\n\t\t\tFrameSize:    c.FrameSize,\n\t\t\tStreamBuf:    c.StreamBuf,\n\t\t\tKeepAlive:    c.KeepAlive,\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/mieru.go",
    "content": "package inbound\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"sync\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/listener/mieru\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"google.golang.org/protobuf/proto\"\n\n\tmieruserver \"github.com/enfein/mieru/v3/apis/server\"\n\tmierutp \"github.com/enfein/mieru/v3/apis/trafficpattern\"\n\tmierupb \"github.com/enfein/mieru/v3/pkg/appctl/appctlpb\"\n)\n\ntype Mieru struct {\n\t*Base\n\toption *MieruOption\n\tserver mieruserver.Server\n\tmu     sync.Mutex\n}\n\ntype MieruOption struct {\n\tBaseOption\n\tTransport           string            `inbound:\"transport\"`\n\tUsers               map[string]string `inbound:\"users\"`\n\tTrafficPattern      string            `inbound:\"traffic-pattern,omitempty\"`\n\tUserHintIsMandatory bool              `inbound:\"user-hint-is-mandatory,omitempty\"`\n}\n\ntype mieruListenerFactory struct{}\n\nfunc (mieruListenerFactory) Listen(ctx context.Context, network, address string) (net.Listener, error) {\n\treturn inbound.ListenContext(ctx, network, address)\n}\n\nfunc (mieruListenerFactory) ListenPacket(ctx context.Context, network, address string) (net.PacketConn, error) {\n\treturn inbound.ListenPacketContext(ctx, network, address)\n}\n\nfunc NewMieru(option *MieruOption) (*Mieru, error) {\n\tbase, err := NewBase(&option.BaseOption)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tconfig, err := buildMieruServerConfig(option, base.ports)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to build mieru server config: %w\", err)\n\t}\n\ts := mieruserver.NewServer()\n\tif err := s.Store(config); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to store mieru server config: %w\", err)\n\t}\n\t// Server is started lazily when Listen() is called for the first time.\n\treturn &Mieru{\n\t\tBase:   base,\n\t\toption: option,\n\t\tserver: s,\n\t}, nil\n}\n\nfunc (m *Mieru) Config() C.InboundConfig {\n\treturn m.option\n}\n\nfunc (m *Mieru) Listen(tunnel C.Tunnel) error {\n\tm.mu.Lock()\n\tdefer m.mu.Unlock()\n\n\tif !m.server.IsRunning() {\n\t\tif err := m.server.Start(); err != nil {\n\t\t\treturn fmt.Errorf(\"failed to start mieru server: %w\", err)\n\t\t}\n\t}\n\n\tadditions := m.config.Additions()\n\tif len(additions) == 0 {\n\t\tadditions = []inbound.Addition{\n\t\t\tinbound.WithInName(\"DEFAULT-MIERU\"),\n\t\t\tinbound.WithSpecialRules(\"\"),\n\t\t}\n\t}\n\n\tgo func() {\n\t\tfor {\n\t\t\tc, req, err := m.server.Accept()\n\t\t\tif err != nil {\n\t\t\t\tif !m.server.IsRunning() {\n\t\t\t\t\tbreak\n\t\t\t\t} else {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\tgo mieru.Handle(c, tunnel, req, additions...)\n\t\t}\n\t}()\n\tlog.Infoln(\"Mieru[%s] proxy listening at: %s\", m.Name(), m.Address())\n\treturn nil\n}\n\nfunc (m *Mieru) Close() error {\n\tm.mu.Lock()\n\tdefer m.mu.Unlock()\n\n\tif m.server.IsRunning() {\n\t\treturn m.server.Stop()\n\t}\n\n\treturn nil\n}\n\nvar _ C.InboundListener = (*Mieru)(nil)\n\nfunc (o MieruOption) Equal(config C.InboundConfig) bool {\n\treturn optionToString(o) == optionToString(config)\n}\n\nfunc buildMieruServerConfig(option *MieruOption, ports utils.IntRanges[uint16]) (*mieruserver.ServerConfig, error) {\n\tif err := validateMieruOption(option); err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to validate mieru option: %w\", err)\n\t}\n\tif len(ports) == 0 {\n\t\treturn nil, fmt.Errorf(\"port is not set\")\n\t}\n\n\tvar transportProtocol *mierupb.TransportProtocol\n\tswitch option.Transport {\n\tcase \"TCP\":\n\t\ttransportProtocol = mierupb.TransportProtocol_TCP.Enum()\n\tcase \"UDP\":\n\t\ttransportProtocol = mierupb.TransportProtocol_UDP.Enum()\n\t}\n\tvar portBindings []*mierupb.PortBinding\n\tfor _, portRange := range ports {\n\t\tif portRange.Start() == portRange.End() {\n\t\t\tportBindings = append(portBindings, &mierupb.PortBinding{\n\t\t\t\tPort:     proto.Int32(int32(portRange.Start())),\n\t\t\t\tProtocol: transportProtocol,\n\t\t\t})\n\t\t} else {\n\t\t\tportBindings = append(portBindings, &mierupb.PortBinding{\n\t\t\t\tPortRange: proto.String(fmt.Sprintf(\"%d-%d\", portRange.Start(), portRange.End())),\n\t\t\t\tProtocol:  transportProtocol,\n\t\t\t})\n\t\t}\n\t}\n\tvar users []*mierupb.User\n\tfor username, password := range option.Users {\n\t\tusers = append(users, &mierupb.User{\n\t\t\tName:     proto.String(username),\n\t\t\tPassword: proto.String(password),\n\t\t})\n\t}\n\tvar trafficPattern *mierupb.TrafficPattern\n\ttrafficPattern, _ = mierutp.Decode(option.TrafficPattern)\n\tvar advancedSettings *mierupb.ServerAdvancedSettings\n\tif option.UserHintIsMandatory {\n\t\tadvancedSettings = &mierupb.ServerAdvancedSettings{\n\t\t\tUserHintIsMandatory: proto.Bool(true),\n\t\t}\n\t}\n\treturn &mieruserver.ServerConfig{\n\t\tConfig: &mierupb.ServerConfig{\n\t\t\tPortBindings:     portBindings,\n\t\t\tUsers:            users,\n\t\t\tTrafficPattern:   trafficPattern,\n\t\t\tAdvancedSettings: advancedSettings,\n\t\t},\n\t\tStreamListenerFactory: mieruListenerFactory{},\n\t\tPacketListenerFactory: mieruListenerFactory{},\n\t}, nil\n}\n\nfunc validateMieruOption(option *MieruOption) error {\n\tif option.Transport != \"TCP\" && option.Transport != \"UDP\" {\n\t\treturn fmt.Errorf(\"transport must be TCP or UDP\")\n\t}\n\tif len(option.Users) == 0 {\n\t\treturn fmt.Errorf(\"users is empty\")\n\t}\n\tfor username, password := range option.Users {\n\t\tif username == \"\" {\n\t\t\treturn fmt.Errorf(\"username is empty\")\n\t\t}\n\t\tif password == \"\" {\n\t\t\treturn fmt.Errorf(\"password is empty\")\n\t\t}\n\t}\n\tif option.TrafficPattern != \"\" {\n\t\ttrafficPattern, err := mierutp.Decode(option.TrafficPattern)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"failed to decode traffic pattern %q: %w\", option.TrafficPattern, err)\n\t\t}\n\t\tif err := mierutp.Validate(trafficPattern); err != nil {\n\t\t\treturn fmt.Errorf(\"invalid traffic pattern %q: %w\", option.TrafficPattern, err)\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/mieru_test.go",
    "content": "package inbound_test\n\nimport (\n\t\"net\"\n\t\"net/netip\"\n\t\"strconv\"\n\t\"testing\"\n\n\t\"github.com/metacubex/mihomo/adapter/outbound\"\n\t\"github.com/metacubex/mihomo/listener/inbound\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestNewMieru(t *testing.T) {\n\ttype args struct {\n\t\toption *inbound.MieruOption\n\t}\n\ttests := []struct {\n\t\tname    string\n\t\targs    args\n\t\twantErr bool\n\t}{\n\t\t{\n\t\t\tname: \"valid with port\",\n\t\t\targs: args{\n\t\t\t\toption: &inbound.MieruOption{\n\t\t\t\t\tBaseOption: inbound.BaseOption{\n\t\t\t\t\t\tPort: \"8080\",\n\t\t\t\t\t},\n\t\t\t\t\tTransport: \"TCP\",\n\t\t\t\t\tUsers:     map[string]string{\"user\": \"pass\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"valid with port range\",\n\t\t\targs: args{\n\t\t\t\toption: &inbound.MieruOption{\n\t\t\t\t\tBaseOption: inbound.BaseOption{\n\t\t\t\t\t\tPort: \"8090-8099\",\n\t\t\t\t\t},\n\t\t\t\t\tTransport: \"UDP\",\n\t\t\t\t\tUsers:     map[string]string{\"user\": \"pass\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"valid mix of port and port-range\",\n\t\t\targs: args{\n\t\t\t\toption: &inbound.MieruOption{\n\t\t\t\t\tBaseOption: inbound.BaseOption{\n\t\t\t\t\t\tPort: \"8080,8090-8099\",\n\t\t\t\t\t},\n\t\t\t\t\tTransport: \"TCP\",\n\t\t\t\t\tUsers:     map[string]string{\"user\": \"pass\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"valid traffic pattern\",\n\t\t\targs: args{\n\t\t\t\toption: &inbound.MieruOption{\n\t\t\t\t\tBaseOption: inbound.BaseOption{\n\t\t\t\t\t\tPort: \"8080\",\n\t\t\t\t\t},\n\t\t\t\t\tTransport:      \"TCP\",\n\t\t\t\t\tUsers:          map[string]string{\"user\": \"pass\"},\n\t\t\t\t\tTrafficPattern: \"GgQIARAK\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: false,\n\t\t},\n\t\t{\n\t\t\tname: \"invalid - no port\",\n\t\t\targs: args{\n\t\t\t\toption: &inbound.MieruOption{\n\t\t\t\t\tTransport: \"TCP\",\n\t\t\t\t\tUsers:     map[string]string{\"user\": \"pass\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"invalid - transport\",\n\t\t\targs: args{\n\t\t\t\toption: &inbound.MieruOption{\n\t\t\t\t\tBaseOption: inbound.BaseOption{\n\t\t\t\t\t\tPort: \"8080\",\n\t\t\t\t\t},\n\t\t\t\t\tTransport: \"INVALID\",\n\t\t\t\t\tUsers:     map[string]string{\"user\": \"pass\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"invalid - no transport\",\n\t\t\targs: args{\n\t\t\t\toption: &inbound.MieruOption{\n\t\t\t\t\tBaseOption: inbound.BaseOption{\n\t\t\t\t\t\tPort: \"8080\",\n\t\t\t\t\t},\n\t\t\t\t\tUsers: map[string]string{\"user\": \"pass\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"invalid - no users\",\n\t\t\targs: args{\n\t\t\t\toption: &inbound.MieruOption{\n\t\t\t\t\tBaseOption: inbound.BaseOption{\n\t\t\t\t\t\tPort: \"8080\",\n\t\t\t\t\t},\n\t\t\t\t\tTransport: \"TCP\",\n\t\t\t\t\tUsers:     map[string]string{},\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"invalid - empty username\",\n\t\t\targs: args{\n\t\t\t\toption: &inbound.MieruOption{\n\t\t\t\t\tBaseOption: inbound.BaseOption{\n\t\t\t\t\t\tPort: \"8080\",\n\t\t\t\t\t},\n\t\t\t\t\tTransport: \"TCP\",\n\t\t\t\t\tUsers:     map[string]string{\"\": \"pass\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"invalid - empty password\",\n\t\t\targs: args{\n\t\t\t\toption: &inbound.MieruOption{\n\t\t\t\t\tBaseOption: inbound.BaseOption{\n\t\t\t\t\t\tPort: \"8080\",\n\t\t\t\t\t},\n\t\t\t\t\tTransport: \"TCP\",\n\t\t\t\t\tUsers:     map[string]string{\"user\": \"\"},\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t\t{\n\t\t\tname: \"invalid traffic pattern\",\n\t\t\targs: args{\n\t\t\t\toption: &inbound.MieruOption{\n\t\t\t\t\tBaseOption: inbound.BaseOption{\n\t\t\t\t\t\tPort: \"8080\",\n\t\t\t\t\t},\n\t\t\t\t\tTransport:      \"TCP\",\n\t\t\t\t\tUsers:          map[string]string{\"user\": \"pass\"},\n\t\t\t\t\tTrafficPattern: \"1212ababXYYX\",\n\t\t\t\t},\n\t\t\t},\n\t\t\twantErr: true,\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tgot, err := inbound.NewMieru(tt.args.option)\n\t\t\tif (err != nil) != tt.wantErr {\n\t\t\t\tt.Errorf(\"NewMieru() error = %v, wantErr %v\", err, tt.wantErr)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err == nil {\n\t\t\t\tgot.Close()\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestInboundMieru(t *testing.T) {\n\tt.Run(\"TCP_HANDSHAKE_STANDARD\", func(t *testing.T) {\n\t\ttestInboundMieruTCP(t, \"HANDSHAKE_STANDARD\")\n\t})\n\tt.Run(\"TCP_HANDSHAKE_NO_WAIT\", func(t *testing.T) {\n\t\ttestInboundMieruTCP(t, \"HANDSHAKE_NO_WAIT\")\n\t})\n\tt.Run(\"UDP_HANDSHAKE_STANDARD\", func(t *testing.T) {\n\t\ttestInboundMieruUDP(t, \"HANDSHAKE_STANDARD\")\n\t})\n\tt.Run(\"UDP_HANDSHAKE_NO_WAIT\", func(t *testing.T) {\n\t\ttestInboundMieruUDP(t, \"HANDSHAKE_NO_WAIT\")\n\t})\n}\n\nfunc testInboundMieruTCP(t *testing.T, handshakeMode string) {\n\tt.Parallel()\n\tl, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\tport := l.Addr().(*net.TCPAddr).Port\n\tl.Close()\n\n\tinboundOptions := inbound.MieruOption{\n\t\tBaseOption: inbound.BaseOption{\n\t\t\tNameStr: \"mieru_inbound_tcp\",\n\t\t\tListen:  \"127.0.0.1\",\n\t\t\tPort:    strconv.Itoa(port),\n\t\t},\n\t\tTransport:           \"TCP\",\n\t\tUsers:               map[string]string{\"test\": \"password\"},\n\t\tUserHintIsMandatory: true,\n\t}\n\tin, err := inbound.NewMieru(&inboundOptions)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\n\ttunnel := NewHttpTestTunnel()\n\tdefer tunnel.Close()\n\n\terr = in.Listen(tunnel)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\tdefer in.Close()\n\n\taddrPort, err := netip.ParseAddrPort(in.Address())\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\toutboundOptions := outbound.MieruOption{\n\t\tName:          \"mieru_outbound_tcp\",\n\t\tServer:        addrPort.Addr().String(),\n\t\tPort:          int(addrPort.Port()),\n\t\tTransport:     \"TCP\",\n\t\tUserName:      \"test\",\n\t\tPassword:      \"password\",\n\t\tHandshakeMode: handshakeMode,\n\t}\n\toutboundOptions.DialerForAPI = tunnel.NewDialer()\n\tout, err := outbound.NewMieru(outboundOptions)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\tdefer out.Close()\n\n\ttunnel.DoTest(t, out)\n}\n\nfunc testInboundMieruUDP(t *testing.T, handshakeMode string) {\n\tt.Parallel()\n\tl, err := net.ListenPacket(\"udp\", \"127.0.0.1:0\")\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\tport := l.LocalAddr().(*net.UDPAddr).Port\n\tl.Close()\n\n\tinboundOptions := inbound.MieruOption{\n\t\tBaseOption: inbound.BaseOption{\n\t\t\tNameStr: \"mieru_inbound_udp\",\n\t\t\tListen:  \"127.0.0.1\",\n\t\t\tPort:    strconv.Itoa(port),\n\t\t},\n\t\tTransport:           \"UDP\",\n\t\tUsers:               map[string]string{\"test\": \"password\"},\n\t\tUserHintIsMandatory: true,\n\t}\n\tin, err := inbound.NewMieru(&inboundOptions)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\n\ttunnel := NewHttpTestTunnel()\n\tdefer tunnel.Close()\n\n\terr = in.Listen(tunnel)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\tdefer in.Close()\n\n\taddrPort, err := netip.ParseAddrPort(in.Address())\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\toutboundOptions := outbound.MieruOption{\n\t\tName:          \"mieru_outbound_udp\",\n\t\tServer:        addrPort.Addr().String(),\n\t\tPort:          int(addrPort.Port()),\n\t\tTransport:     \"UDP\",\n\t\tUserName:      \"test\",\n\t\tPassword:      \"password\",\n\t\tHandshakeMode: handshakeMode,\n\t}\n\toutboundOptions.DialerForAPI = tunnel.NewDialer()\n\tout, err := outbound.NewMieru(outboundOptions)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\tdefer out.Close()\n\n\ttunnel.DoSequentialTest(t, out)\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/mixed.go",
    "content": "package inbound\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/mixed\"\n\t\"github.com/metacubex/mihomo/listener/socks\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\ntype MixedOption struct {\n\tBaseOption\n\tUsers          AuthUsers     `inbound:\"users,omitempty\"`\n\tUDP            bool          `inbound:\"udp,omitempty\"`\n\tCertificate    string        `inbound:\"certificate,omitempty\"`\n\tPrivateKey     string        `inbound:\"private-key,omitempty\"`\n\tClientAuthType string        `inbound:\"client-auth-type,omitempty\"`\n\tClientAuthCert string        `inbound:\"client-auth-cert,omitempty\"`\n\tEchKey         string        `inbound:\"ech-key,omitempty\"`\n\tRealityConfig  RealityConfig `inbound:\"reality-config,omitempty\"`\n}\n\nfunc (o MixedOption) Equal(config C.InboundConfig) bool {\n\treturn optionToString(o) == optionToString(config)\n}\n\ntype Mixed struct {\n\t*Base\n\tconfig *MixedOption\n\tl      []*mixed.Listener\n\tlUDP   []*socks.UDPListener\n\tudp    bool\n}\n\nfunc NewMixed(options *MixedOption) (*Mixed, error) {\n\tbase, err := NewBase(&options.BaseOption)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &Mixed{\n\t\tBase:   base,\n\t\tconfig: options,\n\t\tudp:    options.UDP,\n\t}, nil\n}\n\n// Config implements constant.InboundListener\nfunc (m *Mixed) Config() C.InboundConfig {\n\treturn m.config\n}\n\n// Address implements constant.InboundListener\nfunc (m *Mixed) Address() string {\n\tvar addrList []string\n\tfor _, l := range m.l {\n\t\taddrList = append(addrList, l.Address())\n\t}\n\treturn strings.Join(addrList, \",\")\n}\n\n// Listen implements constant.InboundListener\nfunc (m *Mixed) Listen(tunnel C.Tunnel) error {\n\tfor _, addr := range strings.Split(m.RawAddress(), \",\") {\n\t\tl, err := mixed.NewWithConfig(\n\t\t\tLC.AuthServer{\n\t\t\t\tEnable:         true,\n\t\t\t\tListen:         addr,\n\t\t\t\tAuthStore:      m.config.Users.GetAuthStore(),\n\t\t\t\tCertificate:    m.config.Certificate,\n\t\t\t\tPrivateKey:     m.config.PrivateKey,\n\t\t\t\tClientAuthType: m.config.ClientAuthType,\n\t\t\t\tClientAuthCert: m.config.ClientAuthCert,\n\t\t\t\tEchKey:         m.config.EchKey,\n\t\t\t\tRealityConfig:  m.config.RealityConfig.Build(),\n\t\t\t},\n\t\t\ttunnel,\n\t\t\tm.Additions()...,\n\t\t)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tm.l = append(m.l, l)\n\t\tif m.udp {\n\t\t\tlUDP, err := socks.NewUDP(addr, tunnel, m.Additions()...)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tm.lUDP = append(m.lUDP, lUDP)\n\t\t}\n\t}\n\tlog.Infoln(\"Mixed(http+socks)[%s] proxy listening at: %s\", m.Name(), m.Address())\n\treturn nil\n}\n\n// Close implements constant.InboundListener\nfunc (m *Mixed) Close() error {\n\tvar errs []error\n\tfor _, l := range m.l {\n\t\terr := l.Close()\n\t\tif err != nil {\n\t\t\terrs = append(errs, fmt.Errorf(\"close tcp listener %s err: %w\", l.Address(), err))\n\t\t}\n\t}\n\tfor _, l := range m.lUDP {\n\t\terr := l.Close()\n\t\tif err != nil {\n\t\t\terrs = append(errs, fmt.Errorf(\"close udp listener %s err: %w\", l.Address(), err))\n\t\t}\n\t}\n\tif len(errs) > 0 {\n\t\treturn errors.Join(errs...)\n\t}\n\treturn nil\n}\n\nvar _ C.InboundListener = (*Mixed)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/mux.go",
    "content": "package inbound\n\nimport \"github.com/metacubex/mihomo/listener/sing\"\n\ntype MuxOption struct {\n\tPadding bool          `inbound:\"padding,omitempty\"`\n\tBrutal  BrutalOptions `inbound:\"brutal,omitempty\"`\n}\n\ntype BrutalOptions struct {\n\tEnabled bool   `inbound:\"enabled,omitempty\"`\n\tUp      string `inbound:\"up,omitempty\"`\n\tDown    string `inbound:\"down,omitempty\"`\n}\n\nfunc (m MuxOption) Build() sing.MuxOption {\n\treturn sing.MuxOption{\n\t\tPadding: m.Padding,\n\t\tBrutal: sing.BrutalOptions{\n\t\t\tEnabled: m.Brutal.Enabled,\n\t\t\tUp:      m.Brutal.Up,\n\t\t\tDown:    m.Brutal.Down,\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/mux_test.go",
    "content": "package inbound_test\n\nimport (\n\t\"testing\"\n\n\t\"github.com/metacubex/mihomo/adapter/outbound\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"golang.org/x/exp/slices\"\n)\n\nvar singMuxProtocolList = []string{\"h2mux\", \"smux\", \"yamux\"}\nvar singMuxProtocolListLong = []string{\"yamux\"} // don't test \"smux\", \"h2mux\" because it has some confused bugs\n\n// notCloseProxyAdapter is a proxy adapter that does not close the underlying outbound.ProxyAdapter.\n// The outbound.SingMux will close the underlying outbound.ProxyAdapter when it is closed, but we don't want to close it.\n// The underlying outbound.ProxyAdapter should only be closed by the caller of testSingMux.\ntype notCloseProxyAdapter struct {\n\toutbound.ProxyAdapter\n}\n\nfunc (n *notCloseProxyAdapter) Close() error {\n\treturn nil\n}\n\nfunc testSingMux(t *testing.T, tunnel *TestTunnel, out outbound.ProxyAdapter) {\n\tt.Run(\"singmux\", func(t *testing.T) {\n\t\tfor _, protocol := range singMuxProtocolList {\n\t\t\tprotocol := protocol\n\t\t\tt.Run(protocol, func(t *testing.T) {\n\t\t\t\tsingMuxOption := outbound.SingMuxOption{\n\t\t\t\t\tEnabled:  true,\n\t\t\t\t\tProtocol: protocol,\n\t\t\t\t}\n\t\t\t\tout, err := outbound.NewSingMux(singMuxOption, &notCloseProxyAdapter{out})\n\t\t\t\tif !assert.NoError(t, err) {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tdefer out.Close()\n\n\t\t\t\ttunnel.DoSequentialTest(t, out)\n\t\t\t\tif slices.Contains(singMuxProtocolListLong, protocol) {\n\t\t\t\t\ttunnel.DoConcurrentTest(t, out)\n\t\t\t\t}\n\t\t\t})\n\t\t}\n\t})\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/reality.go",
    "content": "package inbound\n\nimport \"github.com/metacubex/mihomo/listener/reality\"\n\ntype RealityConfig struct {\n\tDest              string   `inbound:\"dest\"`\n\tPrivateKey        string   `inbound:\"private-key\"`\n\tShortID           []string `inbound:\"short-id\"`\n\tServerNames       []string `inbound:\"server-names\"`\n\tMaxTimeDifference int      `inbound:\"max-time-difference,omitempty\"`\n\tProxy             string   `inbound:\"proxy,omitempty\"`\n\n\tLimitFallbackUpload   RealityLimitFallback `inbound:\"limit-fallback-upload,omitempty\"`\n\tLimitFallbackDownload RealityLimitFallback `inbound:\"limit-fallback-download,omitempty\"`\n}\n\ntype RealityLimitFallback struct {\n\tAfterBytes       uint64 `inbound:\"after-bytes,omitempty\"`\n\tBytesPerSec      uint64 `inbound:\"bytes-per-sec,omitempty\"`\n\tBurstBytesPerSec uint64 `inbound:\"burst-bytes-per-sec,omitempty\"`\n}\n\nfunc (c RealityConfig) Build() reality.Config {\n\treturn reality.Config{\n\t\tDest:              c.Dest,\n\t\tPrivateKey:        c.PrivateKey,\n\t\tShortID:           c.ShortID,\n\t\tServerNames:       c.ServerNames,\n\t\tMaxTimeDifference: c.MaxTimeDifference,\n\t\tProxy:             c.Proxy,\n\n\t\tLimitFallbackUpload: reality.LimitFallback{\n\t\t\tAfterBytes:       c.LimitFallbackUpload.AfterBytes,\n\t\t\tBytesPerSec:      c.LimitFallbackUpload.BytesPerSec,\n\t\t\tBurstBytesPerSec: c.LimitFallbackUpload.BurstBytesPerSec,\n\t\t},\n\t\tLimitFallbackDownload: reality.LimitFallback{\n\t\t\tAfterBytes:       c.LimitFallbackDownload.AfterBytes,\n\t\t\tBytesPerSec:      c.LimitFallbackDownload.BytesPerSec,\n\t\t\tBurstBytesPerSec: c.LimitFallbackDownload.BurstBytesPerSec,\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/redir.go",
    "content": "package inbound\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/listener/redir\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\ntype RedirOption struct {\n\tBaseOption\n}\n\nfunc (o RedirOption) Equal(config C.InboundConfig) bool {\n\treturn optionToString(o) == optionToString(config)\n}\n\ntype Redir struct {\n\t*Base\n\tconfig *RedirOption\n\tl      []*redir.Listener\n}\n\nfunc NewRedir(options *RedirOption) (*Redir, error) {\n\tbase, err := NewBase(&options.BaseOption)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &Redir{\n\t\tBase:   base,\n\t\tconfig: options,\n\t}, nil\n}\n\n// Config implements constant.InboundListener\nfunc (r *Redir) Config() C.InboundConfig {\n\treturn r.config\n}\n\n// Address implements constant.InboundListener\nfunc (r *Redir) Address() string {\n\tvar addrList []string\n\tfor _, l := range r.l {\n\t\taddrList = append(addrList, l.Address())\n\t}\n\treturn strings.Join(addrList, \",\")\n}\n\n// Listen implements constant.InboundListener\nfunc (r *Redir) Listen(tunnel C.Tunnel) error {\n\tfor _, addr := range strings.Split(r.RawAddress(), \",\") {\n\t\tl, err := redir.New(addr, tunnel, r.Additions()...)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tr.l = append(r.l, l)\n\t}\n\tlog.Infoln(\"Redir[%s] proxy listening at: %s\", r.Name(), r.Address())\n\treturn nil\n}\n\n// Close implements constant.InboundListener\nfunc (r *Redir) Close() error {\n\tvar errs []error\n\tfor _, l := range r.l {\n\t\terr := l.Close()\n\t\tif err != nil {\n\t\t\terrs = append(errs, fmt.Errorf(\"close redir listener %s err: %w\", l.Address(), err))\n\t\t}\n\t}\n\tif len(errs) > 0 {\n\t\treturn errors.Join(errs...)\n\t}\n\treturn nil\n}\n\nvar _ C.InboundListener = (*Redir)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/shadowsocks.go",
    "content": "package inbound\n\nimport (\n\t\"strings\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/sing_shadowsocks\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\ntype ShadowSocksOption struct {\n\tBaseOption\n\tPassword  string    `inbound:\"password\"`\n\tCipher    string    `inbound:\"cipher\"`\n\tUDP       bool      `inbound:\"udp,omitempty\"`\n\tMuxOption MuxOption `inbound:\"mux-option,omitempty\"`\n\tShadowTLS ShadowTLS `inbound:\"shadow-tls,omitempty\"`\n\tKcpTun    KcpTun    `inbound:\"kcp-tun,omitempty\"`\n}\n\nfunc (o ShadowSocksOption) Equal(config C.InboundConfig) bool {\n\treturn optionToString(o) == optionToString(config)\n}\n\ntype ShadowSocks struct {\n\t*Base\n\tconfig *ShadowSocksOption\n\tl      C.MultiAddrListener\n\tss     LC.ShadowsocksServer\n}\n\nfunc NewShadowSocks(options *ShadowSocksOption) (*ShadowSocks, error) {\n\tbase, err := NewBase(&options.BaseOption)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &ShadowSocks{\n\t\tBase:   base,\n\t\tconfig: options,\n\t\tss: LC.ShadowsocksServer{\n\t\t\tEnable:    true,\n\t\t\tListen:    base.RawAddress(),\n\t\t\tPassword:  options.Password,\n\t\t\tCipher:    options.Cipher,\n\t\t\tUdp:       options.UDP,\n\t\t\tMuxOption: options.MuxOption.Build(),\n\t\t\tShadowTLS: options.ShadowTLS.Build(),\n\t\t\tKcpTun:    options.KcpTun.Build(),\n\t\t},\n\t}, nil\n}\n\n// Config implements constant.InboundListener\nfunc (s *ShadowSocks) Config() C.InboundConfig {\n\treturn s.config\n}\n\n// Address implements constant.InboundListener\nfunc (s *ShadowSocks) Address() string {\n\tvar addrList []string\n\tif s.l != nil {\n\t\tfor _, addr := range s.l.AddrList() {\n\t\t\taddrList = append(addrList, addr.String())\n\t\t}\n\t}\n\treturn strings.Join(addrList, \",\")\n}\n\n// Listen implements constant.InboundListener\nfunc (s *ShadowSocks) Listen(tunnel C.Tunnel) error {\n\tvar err error\n\ts.l, err = sing_shadowsocks.New(s.ss, tunnel, s.Additions()...)\n\tif err != nil {\n\t\treturn err\n\t}\n\tlog.Infoln(\"ShadowSocks[%s] proxy listening at: %s\", s.Name(), s.Address())\n\treturn nil\n}\n\n// Close implements constant.InboundListener\nfunc (s *ShadowSocks) Close() error {\n\treturn s.l.Close()\n}\n\nvar _ C.InboundListener = (*ShadowSocks)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/shadowsocks_test.go",
    "content": "package inbound_test\n\nimport (\n\t\"crypto/rand\"\n\t\"encoding/base64\"\n\t\"net\"\n\t\"net/netip\"\n\t\"runtime\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/metacubex/mihomo/adapter/outbound\"\n\t\"github.com/metacubex/mihomo/listener/inbound\"\n\t\"github.com/metacubex/mihomo/transport/kcptun\"\n\tshadowtls \"github.com/metacubex/mihomo/transport/sing-shadowtls\"\n\n\tshadowsocks \"github.com/metacubex/sing-shadowsocks\"\n\t\"github.com/metacubex/sing-shadowsocks/shadowaead\"\n\t\"github.com/metacubex/sing-shadowsocks/shadowaead_2022\"\n\t\"github.com/metacubex/sing-shadowsocks/shadowstream\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nvar noneList = []string{shadowsocks.MethodNone}\nvar shadowsocksCipherLists = [][]string{noneList, shadowaead.List, shadowaead_2022.List, shadowstream.List}\nvar shadowsocksCipherShortLists = [][]string{noneList, shadowaead.List[:5]} // for test shadowTLS and kcptun\nvar shadowsocksPassword32 string\nvar shadowsocksPassword16 string\n\nfunc init() {\n\tpasswordBytes := make([]byte, 32)\n\trand.Read(passwordBytes)\n\tshadowsocksPassword32 = base64.StdEncoding.EncodeToString(passwordBytes)\n\tshadowsocksPassword16 = base64.StdEncoding.EncodeToString(passwordBytes[:16])\n}\n\nfunc testInboundShadowSocks(t *testing.T, inboundOptions inbound.ShadowSocksOption, outboundOptions outbound.ShadowSocksOption, cipherLists [][]string, enableSingMux bool) {\n\tt.Parallel()\n\tfor _, cipherList := range cipherLists {\n\t\tfor i, cipher := range cipherList {\n\t\t\tenableSingMux := enableSingMux && i == 0\n\t\t\tcipher := cipher\n\t\t\tt.Run(cipher, func(t *testing.T) {\n\t\t\t\tinboundOptions, outboundOptions := inboundOptions, outboundOptions // don't modify outside options value\n\t\t\t\tinboundOptions.Cipher = cipher\n\t\t\t\toutboundOptions.Cipher = cipher\n\t\t\t\ttestInboundShadowSocks0(t, inboundOptions, outboundOptions, enableSingMux)\n\t\t\t})\n\t\t}\n\t}\n}\n\nfunc testInboundShadowSocks0(t *testing.T, inboundOptions inbound.ShadowSocksOption, outboundOptions outbound.ShadowSocksOption, enableSingMux bool) {\n\tt.Parallel()\n\tpassword := shadowsocksPassword32\n\tif strings.Contains(inboundOptions.Cipher, \"-128-\") {\n\t\tpassword = shadowsocksPassword16\n\t}\n\tinboundOptions.BaseOption = inbound.BaseOption{\n\t\tNameStr: \"shadowsocks_inbound\",\n\t\tListen:  \"127.0.0.1\",\n\t\tPort:    \"0\",\n\t}\n\tinboundOptions.Password = password\n\tin, err := inbound.NewShadowSocks(&inboundOptions)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\n\ttunnel := NewHttpTestTunnel()\n\tdefer tunnel.Close()\n\n\terr = in.Listen(tunnel)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\tdefer in.Close()\n\n\taddrPort, err := netip.ParseAddrPort(in.Address())\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\n\toutboundOptions.Name = \"shadowsocks_outbound\"\n\toutboundOptions.Server = addrPort.Addr().String()\n\toutboundOptions.Port = int(addrPort.Port())\n\toutboundOptions.Password = password\n\toutboundOptions.DialerForAPI = tunnel.NewDialer()\n\n\tout, err := outbound.NewShadowSocks(outboundOptions)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\tdefer out.Close()\n\n\ttunnel.DoTest(t, out)\n\n\tif enableSingMux {\n\t\ttestSingMux(t, tunnel, out)\n\t}\n}\n\nfunc TestInboundShadowSocks_Basic(t *testing.T) {\n\tinboundOptions := inbound.ShadowSocksOption{}\n\toutboundOptions := outbound.ShadowSocksOption{}\n\ttestInboundShadowSocks(t, inboundOptions, outboundOptions, shadowsocksCipherLists, true)\n}\n\nfunc testInboundShadowSocksShadowTls(t *testing.T, inboundOptions inbound.ShadowSocksOption, outboundOptions outbound.ShadowSocksOption) {\n\tt.Parallel()\n\tt.Run(\"Conn\", func(t *testing.T) {\n\t\tinboundOptions, outboundOptions := inboundOptions, outboundOptions // don't modify outside options value\n\t\ttestInboundShadowSocks(t, inboundOptions, outboundOptions, shadowsocksCipherShortLists, true)\n\t})\n\tt.Run(\"UConn\", func(t *testing.T) {\n\t\tinboundOptions, outboundOptions := inboundOptions, outboundOptions // don't modify outside options value\n\t\toutboundOptions.ClientFingerprint = \"chrome\"\n\t\ttestInboundShadowSocks(t, inboundOptions, outboundOptions, shadowsocksCipherShortLists, true)\n\t})\n}\n\nfunc TestInboundShadowSocks_ShadowTlsv1(t *testing.T) {\n\tinboundOptions := inbound.ShadowSocksOption{\n\t\tShadowTLS: inbound.ShadowTLS{\n\t\t\tEnable:    true,\n\t\t\tVersion:   1,\n\t\t\tHandshake: inbound.ShadowTLSHandshakeOptions{Dest: net.JoinHostPort(realityDest, \"443\")},\n\t\t},\n\t}\n\toutboundOptions := outbound.ShadowSocksOption{\n\t\tPlugin:     shadowtls.Mode,\n\t\tPluginOpts: map[string]any{\"host\": realityDest, \"fingerprint\": tlsFingerprint, \"version\": 1},\n\t}\n\ttestInboundShadowSocksShadowTls(t, inboundOptions, outboundOptions)\n}\n\nfunc TestInboundShadowSocks_ShadowTlsv2(t *testing.T) {\n\tinboundOptions := inbound.ShadowSocksOption{\n\t\tShadowTLS: inbound.ShadowTLS{\n\t\t\tEnable:    true,\n\t\t\tVersion:   2,\n\t\t\tPassword:  shadowsocksPassword16,\n\t\t\tHandshake: inbound.ShadowTLSHandshakeOptions{Dest: net.JoinHostPort(realityDest, \"443\")},\n\t\t},\n\t}\n\toutboundOptions := outbound.ShadowSocksOption{\n\t\tPlugin:     shadowtls.Mode,\n\t\tPluginOpts: map[string]any{\"host\": realityDest, \"password\": shadowsocksPassword16, \"fingerprint\": tlsFingerprint, \"version\": 2},\n\t}\n\toutboundOptions.PluginOpts[\"alpn\"] = []string{\"http/1.1\"} // shadowtls v2 work confuse with http/2 server, so we set alpn to http/1.1 to pass the test\n\ttestInboundShadowSocksShadowTls(t, inboundOptions, outboundOptions)\n}\n\nfunc TestInboundShadowSocks_ShadowTlsv3(t *testing.T) {\n\tinboundOptions := inbound.ShadowSocksOption{\n\t\tShadowTLS: inbound.ShadowTLS{\n\t\t\tEnable:    true,\n\t\t\tVersion:   3,\n\t\t\tUsers:     []inbound.ShadowTLSUser{{Name: \"test\", Password: shadowsocksPassword16}},\n\t\t\tHandshake: inbound.ShadowTLSHandshakeOptions{Dest: net.JoinHostPort(realityDest, \"443\")},\n\t\t},\n\t}\n\toutboundOptions := outbound.ShadowSocksOption{\n\t\tPlugin:     shadowtls.Mode,\n\t\tPluginOpts: map[string]any{\"host\": realityDest, \"password\": shadowsocksPassword16, \"fingerprint\": tlsFingerprint, \"version\": 3},\n\t}\n\ttestInboundShadowSocksShadowTls(t, inboundOptions, outboundOptions)\n}\n\nfunc TestInboundShadowSocks_KcpTun(t *testing.T) {\n\tif runtime.GOOS == \"windows\" && strings.HasPrefix(runtime.Version(), \"go1.20\") {\n\t\tt.Skip(\"skip kcptun test on windows go1.20\")\n\t}\n\tinboundOptions := inbound.ShadowSocksOption{\n\t\tKcpTun: inbound.KcpTun{\n\t\t\tEnable: true,\n\t\t\tKey:    shadowsocksPassword16,\n\t\t},\n\t}\n\toutboundOptions := outbound.ShadowSocksOption{\n\t\tPlugin:     kcptun.Mode,\n\t\tPluginOpts: map[string]any{\"key\": shadowsocksPassword16},\n\t}\n\ttestInboundShadowSocks(t, inboundOptions, outboundOptions, shadowsocksCipherShortLists, false)\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/shadowtls.go",
    "content": "package inbound\n\nimport (\n\t\"github.com/metacubex/mihomo/common/utils\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n)\n\ntype ShadowTLS struct {\n\tEnable                 bool                                 `inbound:\"enable\"`\n\tVersion                int                                  `inbound:\"version,omitempty\"`\n\tPassword               string                               `inbound:\"password,omitempty\"`\n\tUsers                  []ShadowTLSUser                      `inbound:\"users,omitempty\"`\n\tHandshake              ShadowTLSHandshakeOptions            `inbound:\"handshake,omitempty\"`\n\tHandshakeForServerName map[string]ShadowTLSHandshakeOptions `inbound:\"handshake-for-server-name,omitempty\"`\n\tStrictMode             bool                                 `inbound:\"strict-mode,omitempty\"`\n\tWildcardSNI            string                               `inbound:\"wildcard-sni,omitempty\"`\n}\n\ntype ShadowTLSUser struct {\n\tName     string `inbound:\"name,omitempty\"`\n\tPassword string `inbound:\"password,omitempty\"`\n}\n\ntype ShadowTLSHandshakeOptions struct {\n\tDest  string `inbound:\"dest\"`\n\tProxy string `inbound:\"proxy,omitempty\"`\n}\n\nfunc (c ShadowTLS) Build() LC.ShadowTLS {\n\thandshakeForServerName := make(map[string]LC.ShadowTLSHandshakeOptions)\n\tfor k, v := range c.HandshakeForServerName {\n\t\thandshakeForServerName[k] = v.Build()\n\t}\n\treturn LC.ShadowTLS{\n\t\tEnable:                 c.Enable,\n\t\tVersion:                c.Version,\n\t\tPassword:               c.Password,\n\t\tUsers:                  utils.Map(c.Users, ShadowTLSUser.Build),\n\t\tHandshake:              c.Handshake.Build(),\n\t\tHandshakeForServerName: handshakeForServerName,\n\t\tStrictMode:             c.StrictMode,\n\t\tWildcardSNI:            c.WildcardSNI,\n\t}\n}\n\nfunc (c ShadowTLSUser) Build() LC.ShadowTLSUser {\n\treturn LC.ShadowTLSUser{\n\t\tName:     c.Name,\n\t\tPassword: c.Password,\n\t}\n}\n\nfunc (c ShadowTLSHandshakeOptions) Build() LC.ShadowTLSHandshakeOptions {\n\treturn LC.ShadowTLSHandshakeOptions{\n\t\tDest:  c.Dest,\n\t\tProxy: c.Proxy,\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/socks.go",
    "content": "package inbound\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/socks\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\ntype SocksOption struct {\n\tBaseOption\n\tUsers          AuthUsers     `inbound:\"users,omitempty\"`\n\tUDP            bool          `inbound:\"udp,omitempty\"`\n\tCertificate    string        `inbound:\"certificate,omitempty\"`\n\tPrivateKey     string        `inbound:\"private-key,omitempty\"`\n\tClientAuthType string        `inbound:\"client-auth-type,omitempty\"`\n\tClientAuthCert string        `inbound:\"client-auth-cert,omitempty\"`\n\tEchKey         string        `inbound:\"ech-key,omitempty\"`\n\tRealityConfig  RealityConfig `inbound:\"reality-config,omitempty\"`\n}\n\nfunc (o SocksOption) Equal(config C.InboundConfig) bool {\n\treturn optionToString(o) == optionToString(config)\n}\n\ntype Socks struct {\n\t*Base\n\tconfig *SocksOption\n\tudp    bool\n\tstl    []*socks.Listener\n\tsul    []*socks.UDPListener\n}\n\nfunc NewSocks(options *SocksOption) (*Socks, error) {\n\tbase, err := NewBase(&options.BaseOption)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &Socks{\n\t\tBase:   base,\n\t\tconfig: options,\n\t\tudp:    options.UDP,\n\t}, nil\n}\n\n// Config implements constant.InboundListener\nfunc (s *Socks) Config() C.InboundConfig {\n\treturn s.config\n}\n\n// Close implements constant.InboundListener\nfunc (s *Socks) Close() error {\n\tvar errs []error\n\tfor _, l := range s.stl {\n\t\terr := l.Close()\n\t\tif err != nil {\n\t\t\terrs = append(errs, fmt.Errorf(\"close tcp listener %s err: %w\", l.Address(), err))\n\t\t}\n\t}\n\tfor _, l := range s.sul {\n\t\terr := l.Close()\n\t\tif err != nil {\n\t\t\terrs = append(errs, fmt.Errorf(\"close udp listener %s err: %w\", l.Address(), err))\n\t\t}\n\t}\n\tif len(errs) > 0 {\n\t\treturn errors.Join(errs...)\n\t}\n\treturn nil\n}\n\n// Address implements constant.InboundListener\nfunc (s *Socks) Address() string {\n\tvar addrList []string\n\tfor _, l := range s.stl {\n\t\taddrList = append(addrList, l.Address())\n\t}\n\treturn strings.Join(addrList, \",\")\n}\n\n// Listen implements constant.InboundListener\nfunc (s *Socks) Listen(tunnel C.Tunnel) error {\n\tfor _, addr := range strings.Split(s.RawAddress(), \",\") {\n\t\tstl, err := socks.NewWithConfig(\n\t\t\tLC.AuthServer{\n\t\t\t\tEnable:         true,\n\t\t\t\tListen:         addr,\n\t\t\t\tAuthStore:      s.config.Users.GetAuthStore(),\n\t\t\t\tCertificate:    s.config.Certificate,\n\t\t\t\tPrivateKey:     s.config.PrivateKey,\n\t\t\t\tClientAuthType: s.config.ClientAuthType,\n\t\t\t\tClientAuthCert: s.config.ClientAuthCert,\n\t\t\t\tEchKey:         s.config.EchKey,\n\t\t\t\tRealityConfig:  s.config.RealityConfig.Build(),\n\t\t\t},\n\t\t\ttunnel,\n\t\t\ts.Additions()...,\n\t\t)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\ts.stl = append(s.stl, stl)\n\t\tif s.udp {\n\t\t\tsul, err := socks.NewUDP(addr, tunnel, s.Additions()...)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\ts.sul = append(s.sul, sul)\n\t\t}\n\t}\n\n\tlog.Infoln(\"SOCKS[%s] proxy listening at: %s\", s.Name(), s.Address())\n\treturn nil\n}\n\nvar _ C.InboundListener = (*Socks)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/sudoku.go",
    "content": "package inbound\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/sudoku\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\ntype SudokuOption struct {\n\tBaseOption\n\tKey                    string                 `inbound:\"key\"`\n\tAEADMethod             string                 `inbound:\"aead-method,omitempty\"`\n\tPaddingMin             *int                   `inbound:\"padding-min,omitempty\"`\n\tPaddingMax             *int                   `inbound:\"padding-max,omitempty\"`\n\tTableType              string                 `inbound:\"table-type,omitempty\"` // \"prefer_ascii\", \"prefer_entropy\", or directional \"up_ascii_down_entropy\"/\"up_entropy_down_ascii\"\n\tHandshakeTimeoutSecond *int                   `inbound:\"handshake-timeout,omitempty\"`\n\tEnablePureDownlink     *bool                  `inbound:\"enable-pure-downlink,omitempty\"`\n\tCustomTable            string                 `inbound:\"custom-table,omitempty\"` // optional custom byte layout, e.g. xpxvvpvv\n\tCustomTables           []string               `inbound:\"custom-tables,omitempty\"`\n\tDisableHTTPMask        bool                   `inbound:\"disable-http-mask,omitempty\"`\n\tHTTPMaskMode           string                 `inbound:\"http-mask-mode,omitempty\"` // \"legacy\" (default), \"stream\", \"poll\", \"auto\"\n\tPathRoot               string                 `inbound:\"path-root,omitempty\"`      // optional first-level path prefix for HTTP tunnel endpoints\n\tFallback               string                 `inbound:\"fallback,omitempty\"`\n\tHTTPMaskOptions        *SudokuHTTPMaskOptions `inbound:\"httpmask,omitempty\"`\n\n\t// mihomo private extension (not the part of standard Sudoku protocol)\n\tMuxOption MuxOption `inbound:\"mux-option,omitempty\"`\n}\n\ntype SudokuHTTPMaskOptions struct {\n\tDisable  bool   `inbound:\"disable,omitempty\"`\n\tMode     string `inbound:\"mode,omitempty\"`\n\tPathRoot string `inbound:\"path-root,omitempty\"`\n}\n\nfunc (o SudokuOption) Equal(config C.InboundConfig) bool {\n\treturn optionToString(o) == optionToString(config)\n}\n\ntype Sudoku struct {\n\t*Base\n\tconfig     *SudokuOption\n\tlisteners  []*sudoku.Listener\n\tserverConf LC.SudokuServer\n}\n\nfunc NewSudoku(options *SudokuOption) (*Sudoku, error) {\n\tif options.Key == \"\" {\n\t\treturn nil, fmt.Errorf(\"sudoku inbound requires key\")\n\t}\n\tbase, err := NewBase(&options.BaseOption)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tserverConf := LC.SudokuServer{\n\t\tEnable:                 true,\n\t\tListen:                 base.RawAddress(),\n\t\tKey:                    options.Key,\n\t\tAEADMethod:             options.AEADMethod,\n\t\tPaddingMin:             options.PaddingMin,\n\t\tPaddingMax:             options.PaddingMax,\n\t\tTableType:              options.TableType,\n\t\tHandshakeTimeoutSecond: options.HandshakeTimeoutSecond,\n\t\tEnablePureDownlink:     options.EnablePureDownlink,\n\t\tCustomTable:            options.CustomTable,\n\t\tCustomTables:           options.CustomTables,\n\t\tDisableHTTPMask:        options.DisableHTTPMask,\n\t\tHTTPMaskMode:           options.HTTPMaskMode,\n\t\tPathRoot:               strings.TrimSpace(options.PathRoot),\n\t\tFallback:               strings.TrimSpace(options.Fallback),\n\t}\n\tif hm := options.HTTPMaskOptions; hm != nil {\n\t\tserverConf.DisableHTTPMask = hm.Disable\n\t\tif hm.Mode != \"\" {\n\t\t\tserverConf.HTTPMaskMode = hm.Mode\n\t\t}\n\t\tif pr := strings.TrimSpace(hm.PathRoot); pr != \"\" {\n\t\t\tserverConf.PathRoot = pr\n\t\t}\n\t}\n\tserverConf.MuxOption = options.MuxOption.Build()\n\n\treturn &Sudoku{\n\t\tBase:       base,\n\t\tconfig:     options,\n\t\tserverConf: serverConf,\n\t}, nil\n}\n\n// Config implements constant.InboundListener\nfunc (s *Sudoku) Config() C.InboundConfig {\n\treturn s.config\n}\n\n// Address implements constant.InboundListener\nfunc (s *Sudoku) Address() string {\n\tvar addrList []string\n\tfor _, l := range s.listeners {\n\t\taddrList = append(addrList, l.Address())\n\t}\n\treturn strings.Join(addrList, \",\")\n}\n\n// Listen implements constant.InboundListener\nfunc (s *Sudoku) Listen(tunnel C.Tunnel) error {\n\tif s.serverConf.Key == \"\" {\n\t\treturn fmt.Errorf(\"sudoku inbound requires key\")\n\t}\n\n\tvar errs []error\n\tfor _, addr := range strings.Split(s.RawAddress(), \",\") {\n\t\tconf := s.serverConf\n\t\tconf.Listen = addr\n\n\t\tl, err := sudoku.New(conf, tunnel, s.Additions()...)\n\t\tif err != nil {\n\t\t\terrs = append(errs, err)\n\t\t\tcontinue\n\t\t}\n\t\ts.listeners = append(s.listeners, l)\n\t}\n\tif len(errs) > 0 {\n\t\treturn errors.Join(errs...)\n\t}\n\n\tlog.Infoln(\"Sudoku[%s] inbound listening at: %s\", s.Name(), s.Address())\n\treturn nil\n}\n\n// Close implements constant.InboundListener\nfunc (s *Sudoku) Close() error {\n\tvar errs []error\n\tfor _, l := range s.listeners {\n\t\tif err := l.Close(); err != nil {\n\t\t\terrs = append(errs, err)\n\t\t}\n\t}\n\tif len(errs) > 0 {\n\t\treturn errors.Join(errs...)\n\t}\n\treturn nil\n}\n\nvar _ C.InboundListener = (*Sudoku)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/sudoku_test.go",
    "content": "package inbound_test\n\nimport (\n\t\"net/netip\"\n\t\"runtime\"\n\t\"testing\"\n\n\t\"github.com/metacubex/mihomo/adapter/outbound\"\n\t\"github.com/metacubex/mihomo/listener/inbound\"\n\t\"github.com/metacubex/mihomo/transport/sudoku\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nvar sudokuPrivateKey, sudokuPublicKey, _ = sudoku.GenKeyPair()\n\nfunc testInboundSudoku(t *testing.T, inboundOptions inbound.SudokuOption, outboundOptions outbound.SudokuOption) {\n\tt.Parallel()\n\n\tinboundOptions.BaseOption = inbound.BaseOption{\n\t\tNameStr: \"sudoku_inbound\",\n\t\tListen:  \"127.0.0.1\",\n\t\tPort:    \"0\",\n\t}\n\tin, err := inbound.NewSudoku(&inboundOptions)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\n\ttunnel := NewHttpTestTunnel()\n\tdefer tunnel.Close()\n\n\terr = in.Listen(tunnel)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\tdefer in.Close()\n\n\taddrPort, err := netip.ParseAddrPort(in.Address())\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\n\toutboundOptions.Name = \"sudoku_outbound\"\n\toutboundOptions.Server = addrPort.Addr().String()\n\toutboundOptions.Port = int(addrPort.Port())\n\toutboundOptions.DialerForAPI = tunnel.NewDialer()\n\n\tout, err := outbound.NewSudoku(outboundOptions)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\tdefer out.Close()\n\n\ttunnel.DoTest(t, out)\n\n\ttestSingMux(t, tunnel, out)\n}\n\nfunc TestInboundSudoku_Basic(t *testing.T) {\n\tkey := \"test_key\"\n\tinboundOptions := inbound.SudokuOption{\n\t\tKey: key,\n\t}\n\toutboundOptions := outbound.SudokuOption{\n\t\tKey: key,\n\t}\n\ttestInboundSudoku(t, inboundOptions, outboundOptions)\n\n\tt.Run(\"ed25519key\", func(t *testing.T) {\n\t\tinboundOptions := inboundOptions\n\t\toutboundOptions := outboundOptions\n\t\tinboundOptions.Key = sudokuPublicKey\n\t\toutboundOptions.Key = sudokuPrivateKey\n\t\ttestInboundSudoku(t, inboundOptions, outboundOptions)\n\t})\n}\n\nfunc TestInboundSudoku_Entropy(t *testing.T) {\n\tkey := \"test_key_entropy\"\n\tinboundOptions := inbound.SudokuOption{\n\t\tKey:       key,\n\t\tTableType: \"prefer_entropy\",\n\t}\n\toutboundOptions := outbound.SudokuOption{\n\t\tKey:       key,\n\t\tTableType: \"prefer_entropy\",\n\t}\n\ttestInboundSudoku(t, inboundOptions, outboundOptions)\n\n\tt.Run(\"ed25519key\", func(t *testing.T) {\n\t\tinboundOptions := inboundOptions\n\t\toutboundOptions := outboundOptions\n\t\tinboundOptions.Key = sudokuPublicKey\n\t\toutboundOptions.Key = sudokuPrivateKey\n\t\ttestInboundSudoku(t, inboundOptions, outboundOptions)\n\t})\n}\n\nfunc TestInboundSudoku_Padding(t *testing.T) {\n\tkey := \"test_key_padding\"\n\tpaddingMin := 10\n\tpaddingMax := 100\n\tinboundOptions := inbound.SudokuOption{\n\t\tKey:        key,\n\t\tPaddingMin: &paddingMin,\n\t\tPaddingMax: &paddingMax,\n\t}\n\toutboundOptions := outbound.SudokuOption{\n\t\tKey:        key,\n\t\tPaddingMin: &paddingMin,\n\t\tPaddingMax: &paddingMax,\n\t}\n\ttestInboundSudoku(t, inboundOptions, outboundOptions)\n\n\tt.Run(\"ed25519key\", func(t *testing.T) {\n\t\tinboundOptions := inboundOptions\n\t\toutboundOptions := outboundOptions\n\t\tinboundOptions.Key = sudokuPublicKey\n\t\toutboundOptions.Key = sudokuPrivateKey\n\t\ttestInboundSudoku(t, inboundOptions, outboundOptions)\n\t})\n}\n\nfunc TestInboundSudoku_PackedDownlink(t *testing.T) {\n\tkey := \"test_key_packed\"\n\tenablePure := false\n\tinboundOptions := inbound.SudokuOption{\n\t\tKey:                key,\n\t\tEnablePureDownlink: &enablePure,\n\t}\n\toutboundOptions := outbound.SudokuOption{\n\t\tKey:                key,\n\t\tEnablePureDownlink: &enablePure,\n\t}\n\ttestInboundSudoku(t, inboundOptions, outboundOptions)\n\n\tt.Run(\"ed25519key\", func(t *testing.T) {\n\t\tinboundOptions := inboundOptions\n\t\toutboundOptions := outboundOptions\n\t\tinboundOptions.Key = sudokuPublicKey\n\t\toutboundOptions.Key = sudokuPrivateKey\n\t\ttestInboundSudoku(t, inboundOptions, outboundOptions)\n\t})\n}\n\nfunc TestInboundSudoku_CustomTable(t *testing.T) {\n\tkey := \"test_key_custom\"\n\tcustom := \"xpxvvpvv\"\n\tinboundOptions := inbound.SudokuOption{\n\t\tKey:         key,\n\t\tTableType:   \"prefer_entropy\",\n\t\tCustomTable: custom,\n\t}\n\toutboundOptions := outbound.SudokuOption{\n\t\tKey:         key,\n\t\tTableType:   \"prefer_entropy\",\n\t\tCustomTable: custom,\n\t}\n\ttestInboundSudoku(t, inboundOptions, outboundOptions)\n\n\tt.Run(\"ed25519key\", func(t *testing.T) {\n\t\tinboundOptions := inboundOptions\n\t\toutboundOptions := outboundOptions\n\t\tinboundOptions.Key = sudokuPublicKey\n\t\toutboundOptions.Key = sudokuPrivateKey\n\t\ttestInboundSudoku(t, inboundOptions, outboundOptions)\n\t})\n}\n\nfunc TestInboundSudoku_HTTPMaskMode(t *testing.T) {\n\tif runtime.GOOS == \"windows\" {\n\t\tt.Skip(\"temporarily skipped on windows due to intermittent failures; tracked in PR\")\n\t}\n\n\tkey := \"test_key_http_mask_mode\"\n\n\tfor _, mode := range []string{\"ws\", \"stream\", \"poll\", \"auto\"} {\n\t\tmode := mode\n\t\tt.Run(mode, func(t *testing.T) {\n\t\t\tinboundOptions := inbound.SudokuOption{\n\t\t\t\tKey:          key,\n\t\t\t\tHTTPMaskMode: mode,\n\t\t\t}\n\t\t\thttpMask := true\n\t\t\toutboundOptions := outbound.SudokuOption{\n\t\t\t\tKey:          key,\n\t\t\t\tHTTPMask:     &httpMask,\n\t\t\t\tHTTPMaskMode: mode,\n\t\t\t}\n\t\t\ttestInboundSudoku(t, inboundOptions, outboundOptions)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/tproxy.go",
    "content": "package inbound\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/listener/tproxy\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\ntype TProxyOption struct {\n\tBaseOption\n\tUDP bool `inbound:\"udp,omitempty\"`\n}\n\nfunc (o TProxyOption) Equal(config C.InboundConfig) bool {\n\treturn optionToString(o) == optionToString(config)\n}\n\ntype TProxy struct {\n\t*Base\n\tconfig *TProxyOption\n\tlUDP   []*tproxy.UDPListener\n\tlTCP   []*tproxy.Listener\n\tudp    bool\n}\n\nfunc NewTProxy(options *TProxyOption) (*TProxy, error) {\n\tbase, err := NewBase(&options.BaseOption)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &TProxy{\n\t\tBase:   base,\n\t\tconfig: options,\n\t\tudp:    options.UDP,\n\t}, nil\n\n}\n\n// Config implements constant.InboundListener\nfunc (t *TProxy) Config() C.InboundConfig {\n\treturn t.config\n}\n\n// Address implements constant.InboundListener\nfunc (t *TProxy) Address() string {\n\tvar addrList []string\n\tfor _, l := range t.lTCP {\n\t\taddrList = append(addrList, l.Address())\n\t}\n\treturn strings.Join(addrList, \",\")\n}\n\n// Listen implements constant.InboundListener\nfunc (t *TProxy) Listen(tunnel C.Tunnel) error {\n\tfor _, addr := range strings.Split(t.RawAddress(), \",\") {\n\t\tlTCP, err := tproxy.New(addr, tunnel, t.Additions()...)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tt.lTCP = append(t.lTCP, lTCP)\n\t\tif t.udp {\n\t\t\tlUDP, err := tproxy.NewUDP(addr, tunnel, t.Additions()...)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tt.lUDP = append(t.lUDP, lUDP)\n\t\t}\n\t}\n\tlog.Infoln(\"TProxy[%s] proxy listening at: %s\", t.Name(), t.Address())\n\treturn nil\n}\n\n// Close implements constant.InboundListener\nfunc (t *TProxy) Close() error {\n\tvar errs []error\n\tfor _, l := range t.lTCP {\n\t\terr := l.Close()\n\t\tif err != nil {\n\t\t\terrs = append(errs, fmt.Errorf(\"close tcp listener %s err: %w\", l.Address(), err))\n\t\t}\n\t}\n\tfor _, l := range t.lUDP {\n\t\terr := l.Close()\n\t\tif err != nil {\n\t\t\terrs = append(errs, fmt.Errorf(\"close udp listener %s err: %w\", l.Address(), err))\n\t\t}\n\t}\n\tif len(errs) > 0 {\n\t\treturn errors.Join(errs...)\n\t}\n\treturn nil\n}\n\nvar _ C.InboundListener = (*TProxy)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/trojan.go",
    "content": "package inbound\n\nimport (\n\t\"strings\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/trojan\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\ntype TrojanOption struct {\n\tBaseOption\n\tUsers           []TrojanUser   `inbound:\"users\"`\n\tWsPath          string         `inbound:\"ws-path,omitempty\"`\n\tGrpcServiceName string         `inbound:\"grpc-service-name,omitempty\"`\n\tCertificate     string         `inbound:\"certificate,omitempty\"`\n\tPrivateKey      string         `inbound:\"private-key,omitempty\"`\n\tClientAuthType  string         `inbound:\"client-auth-type,omitempty\"`\n\tClientAuthCert  string         `inbound:\"client-auth-cert,omitempty\"`\n\tEchKey          string         `inbound:\"ech-key,omitempty\"`\n\tRealityConfig   RealityConfig  `inbound:\"reality-config,omitempty\"`\n\tMuxOption       MuxOption      `inbound:\"mux-option,omitempty\"`\n\tSSOption        TrojanSSOption `inbound:\"ss-option,omitempty\"`\n}\n\ntype TrojanUser struct {\n\tUsername string `inbound:\"username,omitempty\"`\n\tPassword string `inbound:\"password\"`\n}\n\n// TrojanSSOption from https://github.com/p4gefau1t/trojan-go/blob/v0.10.6/tunnel/shadowsocks/config.go#L5\ntype TrojanSSOption struct {\n\tEnabled  bool   `inbound:\"enabled,omitempty\"`\n\tMethod   string `inbound:\"method,omitempty\"`\n\tPassword string `inbound:\"password,omitempty\"`\n}\n\nfunc (o TrojanOption) Equal(config C.InboundConfig) bool {\n\treturn optionToString(o) == optionToString(config)\n}\n\ntype Trojan struct {\n\t*Base\n\tconfig *TrojanOption\n\tl      C.MultiAddrListener\n\tvs     LC.TrojanServer\n}\n\nfunc NewTrojan(options *TrojanOption) (*Trojan, error) {\n\tbase, err := NewBase(&options.BaseOption)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tusers := make([]LC.TrojanUser, len(options.Users))\n\tfor i, v := range options.Users {\n\t\tusers[i] = LC.TrojanUser{\n\t\t\tUsername: v.Username,\n\t\t\tPassword: v.Password,\n\t\t}\n\t}\n\treturn &Trojan{\n\t\tBase:   base,\n\t\tconfig: options,\n\t\tvs: LC.TrojanServer{\n\t\t\tEnable:          true,\n\t\t\tListen:          base.RawAddress(),\n\t\t\tUsers:           users,\n\t\t\tWsPath:          options.WsPath,\n\t\t\tGrpcServiceName: options.GrpcServiceName,\n\t\t\tCertificate:     options.Certificate,\n\t\t\tPrivateKey:      options.PrivateKey,\n\t\t\tClientAuthType:  options.ClientAuthType,\n\t\t\tClientAuthCert:  options.ClientAuthCert,\n\t\t\tEchKey:          options.EchKey,\n\t\t\tRealityConfig:   options.RealityConfig.Build(),\n\t\t\tMuxOption:       options.MuxOption.Build(),\n\t\t\tTrojanSSOption: LC.TrojanSSOption{\n\t\t\t\tEnabled:  options.SSOption.Enabled,\n\t\t\t\tMethod:   options.SSOption.Method,\n\t\t\t\tPassword: options.SSOption.Password,\n\t\t\t},\n\t\t},\n\t}, nil\n}\n\n// Config implements constant.InboundListener\nfunc (v *Trojan) Config() C.InboundConfig {\n\treturn v.config\n}\n\n// Address implements constant.InboundListener\nfunc (v *Trojan) Address() string {\n\tvar addrList []string\n\tif v.l != nil {\n\t\tfor _, addr := range v.l.AddrList() {\n\t\t\taddrList = append(addrList, addr.String())\n\t\t}\n\t}\n\treturn strings.Join(addrList, \",\")\n}\n\n// Listen implements constant.InboundListener\nfunc (v *Trojan) Listen(tunnel C.Tunnel) error {\n\tvar err error\n\tv.l, err = trojan.New(v.vs, tunnel, v.Additions()...)\n\tif err != nil {\n\t\treturn err\n\t}\n\tlog.Infoln(\"Trojan[%s] proxy listening at: %s\", v.Name(), v.Address())\n\treturn nil\n}\n\n// Close implements constant.InboundListener\nfunc (v *Trojan) Close() error {\n\treturn v.l.Close()\n}\n\nvar _ C.InboundListener = (*Trojan)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/trojan_test.go",
    "content": "package inbound_test\n\nimport (\n\t\"net\"\n\t\"net/netip\"\n\t\"testing\"\n\n\t\"github.com/metacubex/mihomo/adapter/outbound\"\n\t\"github.com/metacubex/mihomo/listener/inbound\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc testInboundTrojan(t *testing.T, inboundOptions inbound.TrojanOption, outboundOptions outbound.TrojanOption) {\n\tt.Parallel()\n\tinboundOptions.BaseOption = inbound.BaseOption{\n\t\tNameStr: \"trojan_inbound\",\n\t\tListen:  \"127.0.0.1\",\n\t\tPort:    \"0\",\n\t}\n\tinboundOptions.Users = []inbound.TrojanUser{\n\t\t{Username: \"test\", Password: userUUID},\n\t}\n\tin, err := inbound.NewTrojan(&inboundOptions)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\n\ttunnel := NewHttpTestTunnel()\n\tdefer tunnel.Close()\n\n\terr = in.Listen(tunnel)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\tdefer in.Close()\n\n\taddrPort, err := netip.ParseAddrPort(in.Address())\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\n\toutboundOptions.Name = \"trojan_outbound\"\n\toutboundOptions.Server = addrPort.Addr().String()\n\toutboundOptions.Port = int(addrPort.Port())\n\toutboundOptions.Password = userUUID\n\toutboundOptions.DialerForAPI = tunnel.NewDialer()\n\n\tout, err := outbound.NewTrojan(outboundOptions)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\tdefer out.Close()\n\n\ttunnel.DoTest(t, out)\n\n\tif outboundOptions.Network == \"grpc\" { // don't test sing-mux over grpc\n\t\treturn\n\t}\n\ttestSingMux(t, tunnel, out)\n}\n\nfunc testInboundTrojanTLS(t *testing.T, inboundOptions inbound.TrojanOption, outboundOptions outbound.TrojanOption) {\n\ttestInboundTrojan(t, inboundOptions, outboundOptions)\n\tt.Run(\"ECH\", func(t *testing.T) {\n\t\tinboundOptions := inboundOptions\n\t\toutboundOptions := outboundOptions\n\t\tinboundOptions.EchKey = echKeyPem\n\t\toutboundOptions.ECHOpts = outbound.ECHOptions{\n\t\t\tEnable: true,\n\t\t\tConfig: echConfigBase64,\n\t\t}\n\t\ttestInboundTrojan(t, inboundOptions, outboundOptions)\n\t})\n\tt.Run(\"mTLS\", func(t *testing.T) {\n\t\tinboundOptions := inboundOptions\n\t\toutboundOptions := outboundOptions\n\t\tinboundOptions.ClientAuthCert = tlsAuthCertificate\n\t\toutboundOptions.Certificate = tlsAuthCertificate\n\t\toutboundOptions.PrivateKey = tlsAuthPrivateKey\n\t\ttestInboundTrojan(t, inboundOptions, outboundOptions)\n\t})\n\tt.Run(\"mTLS+ECH\", func(t *testing.T) {\n\t\tinboundOptions := inboundOptions\n\t\toutboundOptions := outboundOptions\n\t\tinboundOptions.ClientAuthCert = tlsAuthCertificate\n\t\toutboundOptions.Certificate = tlsAuthCertificate\n\t\toutboundOptions.PrivateKey = tlsAuthPrivateKey\n\t\tinboundOptions.EchKey = echKeyPem\n\t\toutboundOptions.ECHOpts = outbound.ECHOptions{\n\t\t\tEnable: true,\n\t\t\tConfig: echConfigBase64,\n\t\t}\n\t\ttestInboundTrojan(t, inboundOptions, outboundOptions)\n\t})\n}\n\nfunc TestInboundTrojan_TLS(t *testing.T) {\n\tinboundOptions := inbound.TrojanOption{\n\t\tCertificate: tlsCertificate,\n\t\tPrivateKey:  tlsPrivateKey,\n\t}\n\toutboundOptions := outbound.TrojanOption{\n\t\tFingerprint: tlsFingerprint,\n\t}\n\ttestInboundTrojanTLS(t, inboundOptions, outboundOptions)\n}\n\nfunc TestInboundTrojan_Wss1(t *testing.T) {\n\tinboundOptions := inbound.TrojanOption{\n\t\tCertificate: tlsCertificate,\n\t\tPrivateKey:  tlsPrivateKey,\n\t\tWsPath:      \"/ws\",\n\t}\n\toutboundOptions := outbound.TrojanOption{\n\t\tFingerprint: tlsFingerprint,\n\t\tNetwork:     \"ws\",\n\t\tWSOpts: outbound.WSOptions{\n\t\t\tPath: \"/ws\",\n\t\t},\n\t}\n\ttestInboundTrojanTLS(t, inboundOptions, outboundOptions)\n}\n\nfunc TestInboundTrojan_Wss2(t *testing.T) {\n\tinboundOptions := inbound.TrojanOption{\n\t\tCertificate:     tlsCertificate,\n\t\tPrivateKey:      tlsPrivateKey,\n\t\tWsPath:          \"/ws\",\n\t\tGrpcServiceName: \"GunService\",\n\t}\n\toutboundOptions := outbound.TrojanOption{\n\t\tFingerprint: tlsFingerprint,\n\t\tNetwork:     \"ws\",\n\t\tWSOpts: outbound.WSOptions{\n\t\t\tPath: \"/ws\",\n\t\t},\n\t}\n\ttestInboundTrojanTLS(t, inboundOptions, outboundOptions)\n}\n\nfunc TestInboundTrojan_Grpc1(t *testing.T) {\n\tinboundOptions := inbound.TrojanOption{\n\t\tCertificate:     tlsCertificate,\n\t\tPrivateKey:      tlsPrivateKey,\n\t\tGrpcServiceName: \"GunService\",\n\t}\n\toutboundOptions := outbound.TrojanOption{\n\t\tFingerprint: tlsFingerprint,\n\t\tNetwork:     \"grpc\",\n\t\tGrpcOpts:    outbound.GrpcOptions{GrpcServiceName: \"GunService\"},\n\t}\n\ttestInboundTrojanTLS(t, inboundOptions, outboundOptions)\n}\n\nfunc TestInboundTrojan_Grpc2(t *testing.T) {\n\tinboundOptions := inbound.TrojanOption{\n\t\tCertificate:     tlsCertificate,\n\t\tPrivateKey:      tlsPrivateKey,\n\t\tWsPath:          \"/ws\",\n\t\tGrpcServiceName: \"GunService\",\n\t}\n\toutboundOptions := outbound.TrojanOption{\n\t\tFingerprint: tlsFingerprint,\n\t\tNetwork:     \"grpc\",\n\t\tGrpcOpts:    outbound.GrpcOptions{GrpcServiceName: \"GunService\"},\n\t}\n\ttestInboundTrojanTLS(t, inboundOptions, outboundOptions)\n}\n\nfunc TestInboundTrojan_Reality(t *testing.T) {\n\tinboundOptions := inbound.TrojanOption{\n\t\tRealityConfig: inbound.RealityConfig{\n\t\t\tDest:        net.JoinHostPort(realityDest, \"443\"),\n\t\t\tPrivateKey:  realityPrivateKey,\n\t\t\tShortID:     []string{realityShortid},\n\t\t\tServerNames: []string{realityDest},\n\t\t},\n\t}\n\toutboundOptions := outbound.TrojanOption{\n\t\tSNI: realityDest,\n\t\tRealityOpts: outbound.RealityOptions{\n\t\t\tPublicKey: realityPublickey,\n\t\t\tShortID:   realityShortid,\n\t\t},\n\t\tClientFingerprint: \"chrome\",\n\t}\n\ttestInboundTrojan(t, inboundOptions, outboundOptions)\n}\n\nfunc TestInboundTrojan_Reality_Grpc(t *testing.T) {\n\tinboundOptions := inbound.TrojanOption{\n\t\tRealityConfig: inbound.RealityConfig{\n\t\t\tDest:        net.JoinHostPort(realityDest, \"443\"),\n\t\t\tPrivateKey:  realityPrivateKey,\n\t\t\tShortID:     []string{realityShortid},\n\t\t\tServerNames: []string{realityDest},\n\t\t},\n\t\tGrpcServiceName: \"GunService\",\n\t}\n\toutboundOptions := outbound.TrojanOption{\n\t\tSNI: realityDest,\n\t\tRealityOpts: outbound.RealityOptions{\n\t\t\tPublicKey: realityPublickey,\n\t\t\tShortID:   realityShortid,\n\t\t},\n\t\tClientFingerprint: \"chrome\",\n\t\tNetwork:           \"grpc\",\n\t\tGrpcOpts:          outbound.GrpcOptions{GrpcServiceName: \"GunService\"},\n\t}\n\ttestInboundTrojan(t, inboundOptions, outboundOptions)\n}\n\nfunc TestInboundTrojan_TLS_TrojanSS(t *testing.T) {\n\tinboundOptions := inbound.TrojanOption{\n\t\tCertificate: tlsCertificate,\n\t\tPrivateKey:  tlsPrivateKey,\n\t\tSSOption: inbound.TrojanSSOption{\n\t\t\tEnabled:  true,\n\t\t\tMethod:   \"\",\n\t\t\tPassword: \"password\",\n\t\t},\n\t}\n\toutboundOptions := outbound.TrojanOption{\n\t\tFingerprint: tlsFingerprint,\n\t\tSSOpts: outbound.TrojanSSOption{\n\t\t\tEnabled:  true,\n\t\t\tMethod:   \"\",\n\t\t\tPassword: \"password\",\n\t\t},\n\t}\n\ttestInboundTrojanTLS(t, inboundOptions, outboundOptions)\n}\n\nfunc TestInboundTrojan_Wss_TrojanSS(t *testing.T) {\n\tinboundOptions := inbound.TrojanOption{\n\t\tCertificate: tlsCertificate,\n\t\tPrivateKey:  tlsPrivateKey,\n\t\tSSOption: inbound.TrojanSSOption{\n\t\t\tEnabled:  true,\n\t\t\tMethod:   \"\",\n\t\t\tPassword: \"password\",\n\t\t},\n\t\tWsPath: \"/ws\",\n\t}\n\toutboundOptions := outbound.TrojanOption{\n\t\tFingerprint: tlsFingerprint,\n\t\tSSOpts: outbound.TrojanSSOption{\n\t\t\tEnabled:  true,\n\t\t\tMethod:   \"\",\n\t\t\tPassword: \"password\",\n\t\t},\n\t\tNetwork: \"ws\",\n\t\tWSOpts: outbound.WSOptions{\n\t\t\tPath: \"/ws\",\n\t\t},\n\t}\n\ttestInboundTrojanTLS(t, inboundOptions, outboundOptions)\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/trusttunnel.go",
    "content": "package inbound\n\nimport (\n\t\"strings\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/trusttunnel\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\ntype TrustTunnelOption struct {\n\tBaseOption\n\tUsers                AuthUsers `inbound:\"users,omitempty\"`\n\tCertificate          string    `inbound:\"certificate\"`\n\tPrivateKey           string    `inbound:\"private-key\"`\n\tClientAuthType       string    `inbound:\"client-auth-type,omitempty\"`\n\tClientAuthCert       string    `inbound:\"client-auth-cert,omitempty\"`\n\tEchKey               string    `inbound:\"ech-key,omitempty\"`\n\tNetwork              []string  `inbound:\"network,omitempty\"`\n\tCongestionController string    `inbound:\"congestion-controller,omitempty\"`\n\tCWND                 int       `inbound:\"cwnd,omitempty\"`\n\tBBRProfile           string    `inbound:\"bbr-profile,omitempty\"`\n}\n\nfunc (o TrustTunnelOption) Equal(config C.InboundConfig) bool {\n\treturn optionToString(o) == optionToString(config)\n}\n\ntype TrustTunnel struct {\n\t*Base\n\tconfig *TrustTunnelOption\n\tl      C.MultiAddrListener\n\tvs     LC.TrustTunnelServer\n}\n\nfunc NewTrustTunnel(options *TrustTunnelOption) (*TrustTunnel, error) {\n\tbase, err := NewBase(&options.BaseOption)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tusers := make(map[string]string)\n\tfor _, user := range options.Users {\n\t\tusers[user.Username] = user.Password\n\t}\n\treturn &TrustTunnel{\n\t\tBase:   base,\n\t\tconfig: options,\n\t\tvs: LC.TrustTunnelServer{\n\t\t\tEnable:               true,\n\t\t\tListen:               base.RawAddress(),\n\t\t\tUsers:                users,\n\t\t\tCertificate:          options.Certificate,\n\t\t\tPrivateKey:           options.PrivateKey,\n\t\t\tClientAuthType:       options.ClientAuthType,\n\t\t\tClientAuthCert:       options.ClientAuthCert,\n\t\t\tEchKey:               options.EchKey,\n\t\t\tNetwork:              options.Network,\n\t\t\tCongestionController: options.CongestionController,\n\t\t\tCWND:                 options.CWND,\n\t\t\tBBRProfile:           options.BBRProfile,\n\t\t},\n\t}, nil\n}\n\n// Config implements constant.InboundListener\nfunc (v *TrustTunnel) Config() C.InboundConfig {\n\treturn v.config\n}\n\n// Address implements constant.InboundListener\nfunc (v *TrustTunnel) Address() string {\n\tvar addrList []string\n\tif v.l != nil {\n\t\tfor _, addr := range v.l.AddrList() {\n\t\t\taddrList = append(addrList, addr.String())\n\t\t}\n\t}\n\treturn strings.Join(addrList, \",\")\n}\n\n// Listen implements constant.InboundListener\nfunc (v *TrustTunnel) Listen(tunnel C.Tunnel) error {\n\tvar err error\n\tv.l, err = trusttunnel.New(v.vs, tunnel, v.Additions()...)\n\tif err != nil {\n\t\treturn err\n\t}\n\tlog.Infoln(\"TrustTunnel[%s] proxy listening at: %s\", v.Name(), v.Address())\n\treturn nil\n}\n\n// Close implements constant.InboundListener\nfunc (v *TrustTunnel) Close() error {\n\treturn v.l.Close()\n}\n\nvar _ C.InboundListener = (*TrustTunnel)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/trusttunnel_test.go",
    "content": "package inbound_test\n\nimport (\n\t\"net/netip\"\n\t\"testing\"\n\n\t\"github.com/metacubex/mihomo/adapter/outbound\"\n\t\"github.com/metacubex/mihomo/listener/inbound\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc testInboundTrustTunnel(t *testing.T, inboundOptions inbound.TrustTunnelOption, outboundOptions outbound.TrustTunnelOption) {\n\tt.Parallel()\n\tinboundOptions.BaseOption = inbound.BaseOption{\n\t\tNameStr: \"trusttunnel_inbound\",\n\t\tListen:  \"127.0.0.1\",\n\t\tPort:    \"0\",\n\t}\n\tinboundOptions.Users = []inbound.AuthUser{{Username: \"test\", Password: userUUID}}\n\tin, err := inbound.NewTrustTunnel(&inboundOptions)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\n\ttunnel := NewHttpTestTunnel()\n\tdefer tunnel.Close()\n\n\terr = in.Listen(tunnel)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\tdefer in.Close()\n\n\taddrPort, err := netip.ParseAddrPort(in.Address())\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\n\toutboundOptions.Name = \"trusttunnel_outbound\"\n\toutboundOptions.Server = addrPort.Addr().String()\n\toutboundOptions.Port = int(addrPort.Port())\n\toutboundOptions.UserName = \"test\"\n\toutboundOptions.Password = userUUID\n\toutboundOptions.DialerForAPI = tunnel.NewDialer()\n\n\tout, err := outbound.NewTrustTunnel(outboundOptions)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\tdefer out.Close()\n\n\ttunnel.DoTest(t, out)\n}\n\nfunc testInboundTrustTunnelTLS(t *testing.T, quic bool) {\n\tinboundOptions := inbound.TrustTunnelOption{\n\t\tCertificate: tlsCertificate,\n\t\tPrivateKey:  tlsPrivateKey,\n\t}\n\toutboundOptions := outbound.TrustTunnelOption{\n\t\tFingerprint: tlsFingerprint,\n\t\tHealthCheck: true,\n\t}\n\tif quic {\n\t\tinboundOptions.Network = []string{\"udp\"}\n\t\tinboundOptions.CongestionController = \"bbr\"\n\t\toutboundOptions.Quic = true\n\t}\n\ttestInboundTrustTunnel(t, inboundOptions, outboundOptions)\n\tt.Run(\"ECH\", func(t *testing.T) {\n\t\tinboundOptions := inboundOptions\n\t\toutboundOptions := outboundOptions\n\t\tinboundOptions.EchKey = echKeyPem\n\t\toutboundOptions.ECHOpts = outbound.ECHOptions{\n\t\t\tEnable: true,\n\t\t\tConfig: echConfigBase64,\n\t\t}\n\t\ttestInboundTrustTunnel(t, inboundOptions, outboundOptions)\n\t})\n\tt.Run(\"mTLS\", func(t *testing.T) {\n\t\tinboundOptions := inboundOptions\n\t\toutboundOptions := outboundOptions\n\t\tinboundOptions.ClientAuthCert = tlsAuthCertificate\n\t\toutboundOptions.Certificate = tlsAuthCertificate\n\t\toutboundOptions.PrivateKey = tlsAuthPrivateKey\n\t\ttestInboundTrustTunnel(t, inboundOptions, outboundOptions)\n\t})\n\tt.Run(\"mTLS+ECH\", func(t *testing.T) {\n\t\tinboundOptions := inboundOptions\n\t\toutboundOptions := outboundOptions\n\t\tinboundOptions.ClientAuthCert = tlsAuthCertificate\n\t\toutboundOptions.Certificate = tlsAuthCertificate\n\t\toutboundOptions.PrivateKey = tlsAuthPrivateKey\n\t\tinboundOptions.EchKey = echKeyPem\n\t\toutboundOptions.ECHOpts = outbound.ECHOptions{\n\t\t\tEnable: true,\n\t\t\tConfig: echConfigBase64,\n\t\t}\n\t\ttestInboundTrustTunnel(t, inboundOptions, outboundOptions)\n\t})\n}\n\nfunc TestInboundTrustTunnel_H2(t *testing.T) {\n\ttestInboundTrustTunnelTLS(t, true)\n}\n\nfunc TestInboundTrustTunnel_QUIC(t *testing.T) {\n\ttestInboundTrustTunnelTLS(t, true)\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/tuic.go",
    "content": "package inbound\n\nimport (\n\t\"strings\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/tuic\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\ntype TuicOption struct {\n\tBaseOption\n\tToken                 []string          `inbound:\"token,omitempty\"`\n\tUsers                 map[string]string `inbound:\"users,omitempty\"`\n\tCertificate           string            `inbound:\"certificate\"`\n\tPrivateKey            string            `inbound:\"private-key\"`\n\tClientAuthType        string            `inbound:\"client-auth-type,omitempty\"`\n\tClientAuthCert        string            `inbound:\"client-auth-cert,omitempty\"`\n\tEchKey                string            `inbound:\"ech-key,omitempty\"`\n\tCongestionController  string            `inbound:\"congestion-controller,omitempty\"`\n\tMaxIdleTime           int               `inbound:\"max-idle-time,omitempty\"`\n\tAuthenticationTimeout int               `inbound:\"authentication-timeout,omitempty\"`\n\tALPN                  []string          `inbound:\"alpn,omitempty\"`\n\tMaxUdpRelayPacketSize int               `inbound:\"max-udp-relay-packet-size,omitempty\"`\n\tCWND                  int               `inbound:\"cwnd,omitempty\"`\n\tBBRProfile            string            `inbound:\"bbr-profile,omitempty\"`\n\tMuxOption             MuxOption         `inbound:\"mux-option,omitempty\"`\n}\n\nfunc (o TuicOption) Equal(config C.InboundConfig) bool {\n\treturn optionToString(o) == optionToString(config)\n}\n\ntype Tuic struct {\n\t*Base\n\tconfig *TuicOption\n\tl      *tuic.Listener\n\tts     LC.TuicServer\n}\n\nfunc NewTuic(options *TuicOption) (*Tuic, error) {\n\tbase, err := NewBase(&options.BaseOption)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &Tuic{\n\t\tBase:   base,\n\t\tconfig: options,\n\t\tts: LC.TuicServer{\n\t\t\tEnable:                true,\n\t\t\tListen:                base.RawAddress(),\n\t\t\tToken:                 options.Token,\n\t\t\tUsers:                 options.Users,\n\t\t\tCertificate:           options.Certificate,\n\t\t\tPrivateKey:            options.PrivateKey,\n\t\t\tClientAuthType:        options.ClientAuthType,\n\t\t\tClientAuthCert:        options.ClientAuthCert,\n\t\t\tEchKey:                options.EchKey,\n\t\t\tCongestionController:  options.CongestionController,\n\t\t\tMaxIdleTime:           options.MaxIdleTime,\n\t\t\tAuthenticationTimeout: options.AuthenticationTimeout,\n\t\t\tALPN:                  options.ALPN,\n\t\t\tMaxUdpRelayPacketSize: options.MaxUdpRelayPacketSize,\n\t\t\tCWND:                  options.CWND,\n\t\t\tBBRProfile:            options.BBRProfile,\n\t\t\tMuxOption:             options.MuxOption.Build(),\n\t\t},\n\t}, nil\n}\n\n// Config implements constant.InboundListener\nfunc (t *Tuic) Config() C.InboundConfig {\n\treturn t.config\n}\n\n// Address implements constant.InboundListener\nfunc (t *Tuic) Address() string {\n\tvar addrList []string\n\tif t.l != nil {\n\t\tfor _, addr := range t.l.AddrList() {\n\t\t\taddrList = append(addrList, addr.String())\n\t\t}\n\t}\n\treturn strings.Join(addrList, \",\")\n}\n\n// Listen implements constant.InboundListener\nfunc (t *Tuic) Listen(tunnel C.Tunnel) error {\n\tvar err error\n\tt.l, err = tuic.New(t.ts, tunnel, t.Additions()...)\n\tif err != nil {\n\t\treturn err\n\t}\n\tlog.Infoln(\"Tuic[%s] proxy listening at: %s\", t.Name(), t.Address())\n\treturn nil\n}\n\n// Close implements constant.InboundListener\nfunc (t *Tuic) Close() error {\n\treturn t.l.Close()\n}\n\nvar _ C.InboundListener = (*Tuic)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/tuic_test.go",
    "content": "package inbound_test\n\nimport (\n\t\"net/netip\"\n\t\"testing\"\n\n\t\"github.com/metacubex/mihomo/adapter/outbound\"\n\t\"github.com/metacubex/mihomo/listener/inbound\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nvar tuicCCs = []string{\"cubic\", \"new_reno\", \"bbr\"}\n\nfunc testInboundTuic(t *testing.T, inboundOptions inbound.TuicOption, outboundOptions outbound.TuicOption) {\n\tt.Parallel()\n\tinboundOptions.Users = map[string]string{userUUID: userUUID}\n\tinboundOptions.Token = []string{userUUID}\n\n\tfor _, tuicCC := range tuicCCs {\n\t\ttuicCC := tuicCC\n\t\tt.Run(tuicCC, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tt.Run(\"v4\", func(t *testing.T) {\n\t\t\t\tinboundOptions, outboundOptions := inboundOptions, outboundOptions // don't modify outside options value\n\t\t\t\toutboundOptions.Token = userUUID\n\t\t\t\toutboundOptions.CongestionController = tuicCC\n\t\t\t\tinboundOptions.CongestionController = tuicCC\n\t\t\t\ttestInboundTuic0(t, inboundOptions, outboundOptions)\n\t\t\t})\n\t\t\tt.Run(\"v5\", func(t *testing.T) {\n\t\t\t\tinboundOptions, outboundOptions := inboundOptions, outboundOptions // don't modify outside options value\n\t\t\t\toutboundOptions.UUID = userUUID\n\t\t\t\toutboundOptions.Password = userUUID\n\t\t\t\toutboundOptions.CongestionController = tuicCC\n\t\t\t\tinboundOptions.CongestionController = tuicCC\n\t\t\t\ttestInboundTuic0(t, inboundOptions, outboundOptions)\n\t\t\t})\n\t\t})\n\t}\n}\n\nfunc testInboundTuic0(t *testing.T, inboundOptions inbound.TuicOption, outboundOptions outbound.TuicOption) {\n\tt.Parallel()\n\tinboundOptions.BaseOption = inbound.BaseOption{\n\t\tNameStr: \"tuic_inbound\",\n\t\tListen:  \"127.0.0.1\",\n\t\tPort:    \"0\",\n\t}\n\tin, err := inbound.NewTuic(&inboundOptions)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\n\ttunnel := NewHttpTestTunnel()\n\tdefer tunnel.Close()\n\n\terr = in.Listen(tunnel)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\tdefer in.Close()\n\n\taddrPort, err := netip.ParseAddrPort(in.Address())\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\n\toutboundOptions.Name = \"tuic_outbound\"\n\toutboundOptions.Server = addrPort.Addr().String()\n\toutboundOptions.Port = int(addrPort.Port())\n\toutboundOptions.DialerForAPI = tunnel.NewDialer()\n\n\tout, err := outbound.NewTuic(outboundOptions)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\tdefer out.Close()\n\n\ttunnel.DoTest(t, out)\n}\n\nfunc TestInboundTuic_TLS(t *testing.T) {\n\tinboundOptions := inbound.TuicOption{\n\t\tCertificate:           tlsCertificate,\n\t\tPrivateKey:            tlsPrivateKey,\n\t\tAuthenticationTimeout: 5000,\n\t}\n\toutboundOptions := outbound.TuicOption{\n\t\tFingerprint: tlsFingerprint,\n\t}\n\ttestInboundTuic(t, inboundOptions, outboundOptions)\n\tt.Run(\"ECH\", func(t *testing.T) {\n\t\tinboundOptions := inboundOptions\n\t\toutboundOptions := outboundOptions\n\t\tinboundOptions.EchKey = echKeyPem\n\t\toutboundOptions.ECHOpts = outbound.ECHOptions{\n\t\t\tEnable: true,\n\t\t\tConfig: echConfigBase64,\n\t\t}\n\t\ttestInboundTuic(t, inboundOptions, outboundOptions)\n\t})\n\tt.Run(\"mTLS\", func(t *testing.T) {\n\t\tinboundOptions := inboundOptions\n\t\toutboundOptions := outboundOptions\n\t\tinboundOptions.ClientAuthCert = tlsAuthCertificate\n\t\toutboundOptions.Certificate = tlsAuthCertificate\n\t\toutboundOptions.PrivateKey = tlsAuthPrivateKey\n\t\ttestInboundTuic(t, inboundOptions, outboundOptions)\n\t})\n\tt.Run(\"mTLS+ECH\", func(t *testing.T) {\n\t\tinboundOptions := inboundOptions\n\t\toutboundOptions := outboundOptions\n\t\tinboundOptions.ClientAuthCert = tlsAuthCertificate\n\t\toutboundOptions.Certificate = tlsAuthCertificate\n\t\toutboundOptions.PrivateKey = tlsAuthPrivateKey\n\t\tinboundOptions.EchKey = echKeyPem\n\t\toutboundOptions.ECHOpts = outbound.ECHOptions{\n\t\t\tEnable: true,\n\t\t\tConfig: echConfigBase64,\n\t\t}\n\t\ttestInboundTuic(t, inboundOptions, outboundOptions)\n\t})\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/tun.go",
    "content": "package inbound\n\nimport (\n\t\"encoding\"\n\t\"net/netip\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/sing_tun\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\ntype TunOption struct {\n\tBaseOption\n\tDevice              string     `inbound:\"device,omitempty\"`\n\tStack               C.TUNStack `inbound:\"stack,omitempty\"`\n\tDNSHijack           []string   `inbound:\"dns-hijack,omitempty\"`\n\tAutoRoute           bool       `inbound:\"auto-route,omitempty\"`\n\tAutoDetectInterface bool       `inbound:\"auto-detect-interface,omitempty\"`\n\n\tMTU                                   uint32         `inbound:\"mtu,omitempty\"`\n\tGSO                                   bool           `inbound:\"gso,omitempty\"`\n\tGSOMaxSize                            uint32         `inbound:\"gso-max-size,omitempty\"`\n\tInet4Address                          []netip.Prefix `inbound:\"inet4-address,omitempty\"`\n\tInet6Address                          []netip.Prefix `inbound:\"inet6-address,omitempty\"`\n\tIPRoute2TableIndex                    int            `inbound:\"iproute2-table-index,omitempty\"`\n\tIPRoute2RuleIndex                     int            `inbound:\"iproute2-rule-index,omitempty\"`\n\tAutoRedirect                          bool           `inbound:\"auto-redirect,omitempty\"`\n\tAutoRedirectInputMark                 uint32         `inbound:\"auto-redirect-input-mark,omitempty\"`\n\tAutoRedirectOutputMark                uint32         `inbound:\"auto-redirect-output-mark,omitempty\"`\n\tAutoRedirectIPRoute2FallbackRuleIndex int            `inbound:\"auto-redirect-iproute2-fallback-rule-index,omitempty\"`\n\tLoopbackAddress                       []netip.Addr   `inbound:\"loopback-address,omitempty\"`\n\tStrictRoute                           bool           `inbound:\"strict-route,omitempty\"`\n\tRouteAddress                          []netip.Prefix `inbound:\"route-address,omitempty\"`\n\tRouteAddressSet                       []string       `inbound:\"route-address-set,omitempty\"`\n\tRouteExcludeAddress                   []netip.Prefix `inbound:\"route-exclude-address,omitempty\"`\n\tRouteExcludeAddressSet                []string       `inbound:\"route-exclude-address-set,omitempty\"`\n\tIncludeInterface                      []string       `inbound:\"include-interface,omitempty\"`\n\tExcludeInterface                      []string       `inbound:\"exclude-interface,omitempty\"`\n\tIncludeUID                            []uint32       `inbound:\"include-uid,omitempty\"`\n\tIncludeUIDRange                       []string       `inbound:\"include-uid-range,omitempty\"`\n\tExcludeUID                            []uint32       `inbound:\"exclude-uid,omitempty\"`\n\tExcludeUIDRange                       []string       `inbound:\"exclude-uid-range,omitempty\"`\n\tExcludeSrcPort                        []uint16       `inbound:\"exclude-src-port,omitempty\"`\n\tExcludeSrcPortRange                   []string       `inbound:\"exclude-src-port-range,omitempty\"`\n\tExcludeDstPort                        []uint16       `inbound:\"exclude-dst-port,omitempty\"`\n\tExcludeDstPortRange                   []string       `inbound:\"exclude-dst-port-range,omitempty\"`\n\tIncludeAndroidUser                    []int          `inbound:\"include-android-user,omitempty\"`\n\tIncludePackage                        []string       `inbound:\"include-package,omitempty\"`\n\tExcludePackage                        []string       `inbound:\"exclude-package,omitempty\"`\n\tIncludeMACAddress                     []string       `inbound:\"include-mac-address,omitempty\"`\n\tExcludeMACAddress                     []string       `inbound:\"exclude-mac-address,omitempty\"`\n\tEndpointIndependentNat                bool           `inbound:\"endpoint-independent-nat,omitempty\"`\n\tUDPTimeout                            int64          `inbound:\"udp-timeout,omitempty\"`\n\tDisableICMPForwarding                 bool           `inbound:\"disable-icmp-forwarding,omitempty\"`\n\tFileDescriptor                        int            `inbound:\"file-descriptor,omitempty\"`\n\n\tInet4RouteAddress        []netip.Prefix `inbound:\"inet4-route-address,omitempty\"`\n\tInet6RouteAddress        []netip.Prefix `inbound:\"inet6-route-address,omitempty\"`\n\tInet4RouteExcludeAddress []netip.Prefix `inbound:\"inet4-route-exclude-address,omitempty\"`\n\tInet6RouteExcludeAddress []netip.Prefix `inbound:\"inet6-route-exclude-address,omitempty\"`\n\n\t// darwin special config\n\tRecvMsgX bool `inbound:\"recvmsgx,omitempty\"`\n\tSendMsgX bool `inbound:\"sendmsgx,omitempty\"`\n}\n\nvar _ encoding.TextUnmarshaler = (*netip.Addr)(nil)   // ensure netip.Addr can decode direct by structure package\nvar _ encoding.TextUnmarshaler = (*netip.Prefix)(nil) // ensure netip.Prefix can decode direct by structure package\nvar _ encoding.TextUnmarshaler = (*C.TUNStack)(nil)   // ensure C.TUNStack can decode direct by structure package\n\nfunc (o TunOption) Equal(config C.InboundConfig) bool {\n\treturn optionToString(o) == optionToString(config)\n}\n\ntype Tun struct {\n\t*Base\n\tconfig *TunOption\n\tl      *sing_tun.Listener\n\ttun    LC.Tun\n}\n\nfunc NewTun(options *TunOption) (*Tun, error) {\n\tbase, err := NewBase(&options.BaseOption)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &Tun{\n\t\tBase:   base,\n\t\tconfig: options,\n\t\ttun: LC.Tun{\n\t\t\tEnable:                                true,\n\t\t\tDevice:                                options.Device,\n\t\t\tStack:                                 options.Stack,\n\t\t\tDNSHijack:                             options.DNSHijack,\n\t\t\tAutoRoute:                             options.AutoRoute,\n\t\t\tAutoDetectInterface:                   options.AutoDetectInterface,\n\t\t\tMTU:                                   options.MTU,\n\t\t\tGSO:                                   options.GSO,\n\t\t\tGSOMaxSize:                            options.GSOMaxSize,\n\t\t\tInet4Address:                          options.Inet4Address,\n\t\t\tInet6Address:                          options.Inet6Address,\n\t\t\tIPRoute2TableIndex:                    options.IPRoute2TableIndex,\n\t\t\tIPRoute2RuleIndex:                     options.IPRoute2RuleIndex,\n\t\t\tAutoRedirect:                          options.AutoRedirect,\n\t\t\tAutoRedirectInputMark:                 options.AutoRedirectInputMark,\n\t\t\tAutoRedirectOutputMark:                options.AutoRedirectOutputMark,\n\t\t\tAutoRedirectIPRoute2FallbackRuleIndex: options.AutoRedirectIPRoute2FallbackRuleIndex,\n\t\t\tLoopbackAddress:                       options.LoopbackAddress,\n\t\t\tStrictRoute:                           options.StrictRoute,\n\t\t\tRouteAddress:                          options.RouteAddress,\n\t\t\tRouteAddressSet:                       options.RouteAddressSet,\n\t\t\tRouteExcludeAddress:                   options.RouteExcludeAddress,\n\t\t\tRouteExcludeAddressSet:                options.RouteExcludeAddressSet,\n\t\t\tIncludeInterface:                      options.IncludeInterface,\n\t\t\tExcludeInterface:                      options.ExcludeInterface,\n\t\t\tIncludeUID:                            options.IncludeUID,\n\t\t\tIncludeUIDRange:                       options.IncludeUIDRange,\n\t\t\tExcludeUID:                            options.ExcludeUID,\n\t\t\tExcludeUIDRange:                       options.ExcludeUIDRange,\n\t\t\tExcludeSrcPort:                        options.ExcludeSrcPort,\n\t\t\tExcludeSrcPortRange:                   options.ExcludeSrcPortRange,\n\t\t\tExcludeDstPort:                        options.ExcludeDstPort,\n\t\t\tExcludeDstPortRange:                   options.ExcludeDstPortRange,\n\t\t\tIncludeAndroidUser:                    options.IncludeAndroidUser,\n\t\t\tIncludePackage:                        options.IncludePackage,\n\t\t\tExcludePackage:                        options.ExcludePackage,\n\t\t\tIncludeMACAddress:                     options.IncludeMACAddress,\n\t\t\tExcludeMACAddress:                     options.ExcludeMACAddress,\n\t\t\tEndpointIndependentNat:                options.EndpointIndependentNat,\n\t\t\tUDPTimeout:                            options.UDPTimeout,\n\t\t\tDisableICMPForwarding:                 options.DisableICMPForwarding,\n\t\t\tFileDescriptor:                        options.FileDescriptor,\n\n\t\t\tInet4RouteAddress:        options.Inet4RouteAddress,\n\t\t\tInet6RouteAddress:        options.Inet6RouteAddress,\n\t\t\tInet4RouteExcludeAddress: options.Inet4RouteExcludeAddress,\n\t\t\tInet6RouteExcludeAddress: options.Inet6RouteExcludeAddress,\n\n\t\t\tRecvMsgX: options.RecvMsgX,\n\t\t\tSendMsgX: options.SendMsgX,\n\t\t},\n\t}, nil\n}\n\n// Config implements constant.InboundListener\nfunc (t *Tun) Config() C.InboundConfig {\n\treturn t.config\n}\n\n// Address implements constant.InboundListener\nfunc (t *Tun) Address() string {\n\treturn t.l.Address()\n}\n\n// Listen implements constant.InboundListener\nfunc (t *Tun) Listen(tunnel C.Tunnel) error {\n\tvar err error\n\tt.l, err = sing_tun.New(t.tun, tunnel, t.Additions()...)\n\tif err != nil {\n\t\treturn err\n\t}\n\tlog.Infoln(\"Tun[%s] proxy listening at: %s\", t.Name(), t.Address())\n\treturn nil\n}\n\n// Close implements constant.InboundListener\nfunc (t *Tun) Close() error {\n\treturn t.l.Close()\n}\n\nvar _ C.InboundListener = (*Tun)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/tunnel.go",
    "content": "package inbound\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"strings\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\tLT \"github.com/metacubex/mihomo/listener/tunnel\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\ntype TunnelOption struct {\n\tBaseOption\n\tNetwork []string `inbound:\"network\"`\n\tTarget  string   `inbound:\"target\"`\n}\n\nfunc (o TunnelOption) Equal(config C.InboundConfig) bool {\n\treturn optionToString(o) == optionToString(config)\n}\n\ntype Tunnel struct {\n\t*Base\n\tconfig *TunnelOption\n\tttl    []*LT.Listener\n\ttul    []*LT.PacketConn\n}\n\nfunc NewTunnel(options *TunnelOption) (*Tunnel, error) {\n\tbase, err := NewBase(&options.BaseOption)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &Tunnel{\n\t\tBase:   base,\n\t\tconfig: options,\n\t}, nil\n}\n\n// Config implements constant.InboundListener\nfunc (t *Tunnel) Config() C.InboundConfig {\n\treturn t.config\n}\n\n// Close implements constant.InboundListener\nfunc (t *Tunnel) Close() error {\n\tvar errs []error\n\tfor _, l := range t.ttl {\n\t\terr := l.Close()\n\t\tif err != nil {\n\t\t\terrs = append(errs, fmt.Errorf(\"close tcp listener %s err: %w\", l.Address(), err))\n\t\t}\n\t}\n\tfor _, l := range t.tul {\n\t\terr := l.Close()\n\t\tif err != nil {\n\t\t\terrs = append(errs, fmt.Errorf(\"close udp listener %s err: %w\", l.Address(), err))\n\t\t}\n\t}\n\tif len(errs) > 0 {\n\t\treturn errors.Join(errs...)\n\t}\n\treturn nil\n}\n\n// Address implements constant.InboundListener\nfunc (t *Tunnel) Address() string {\n\tvar addrList []string\n\tfor _, l := range t.ttl {\n\t\taddrList = append(addrList, \"tcp://\"+l.Address())\n\t}\n\tfor _, l := range t.tul {\n\t\taddrList = append(addrList, \"udp://\"+l.Address())\n\t}\n\treturn strings.Join(addrList, \",\")\n}\n\n// Listen implements constant.InboundListener\nfunc (t *Tunnel) Listen(tunnel C.Tunnel) error {\n\tfor _, addr := range strings.Split(t.RawAddress(), \",\") {\n\t\tfor _, network := range t.config.Network {\n\t\t\tswitch network {\n\t\t\tcase \"tcp\":\n\t\t\t\tttl, err := LT.New(addr, t.config.Target, t.config.SpecialProxy, tunnel, t.Additions()...)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tt.ttl = append(t.ttl, ttl)\n\t\t\tcase \"udp\":\n\t\t\t\ttul, err := LT.NewUDP(addr, t.config.Target, t.config.SpecialProxy, tunnel, t.Additions()...)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\tt.tul = append(t.tul, tul)\n\t\t\tdefault:\n\t\t\t\tlog.Warnln(\"unknown network type: %s, passed\", network)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t}\n\tlog.Infoln(\"Tunnel[%s](%s)proxy listening at: %s\", t.Name(), t.config.Target, t.Address())\n\treturn nil\n}\n\nvar _ C.InboundListener = (*Tunnel)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/vless.go",
    "content": "package inbound\n\nimport (\n\t\"strings\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/sing_vless\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\ntype VlessOption struct {\n\tBaseOption\n\tUsers           []VlessUser   `inbound:\"users\"`\n\tDecryption      string        `inbound:\"decryption,omitempty\"`\n\tWsPath          string        `inbound:\"ws-path,omitempty\"`\n\tXHTTPConfig     XHTTPConfig   `inbound:\"xhttp-config,omitempty\"`\n\tGrpcServiceName string        `inbound:\"grpc-service-name,omitempty\"`\n\tCertificate     string        `inbound:\"certificate,omitempty\"`\n\tPrivateKey      string        `inbound:\"private-key,omitempty\"`\n\tClientAuthType  string        `inbound:\"client-auth-type,omitempty\"`\n\tClientAuthCert  string        `inbound:\"client-auth-cert,omitempty\"`\n\tEchKey          string        `inbound:\"ech-key,omitempty\"`\n\tRealityConfig   RealityConfig `inbound:\"reality-config,omitempty\"`\n\tMuxOption       MuxOption     `inbound:\"mux-option,omitempty\"`\n}\n\ntype VlessUser struct {\n\tUsername string `inbound:\"username,omitempty\"`\n\tUUID     string `inbound:\"uuid\"`\n\tFlow     string `inbound:\"flow,omitempty\"`\n}\n\ntype XHTTPConfig struct {\n\tPath                 string `inbound:\"path,omitempty\"`\n\tHost                 string `inbound:\"host,omitempty\"`\n\tMode                 string `inbound:\"mode,omitempty\"`\n\tXPaddingBytes        string `inbound:\"x-padding-bytes,omitempty\"`\n\tXPaddingObfsMode     bool   `inbound:\"x-padding-obfs-mode,omitempty\"`\n\tXPaddingKey          string `inbound:\"x-padding-key,omitempty\"`\n\tXPaddingHeader       string `inbound:\"x-padding-header,omitempty\"`\n\tXPaddingPlacement    string `inbound:\"x-padding-placement,omitempty\"`\n\tXPaddingMethod       string `inbound:\"x-padding-method,omitempty\"`\n\tUplinkHTTPMethod     string `inbound:\"uplink-http-method,omitempty\"`\n\tSessionPlacement     string `inbound:\"session-placement,omitempty\"`\n\tSessionKey           string `inbound:\"session-key,omitempty\"`\n\tSeqPlacement         string `inbound:\"seq-placement,omitempty\"`\n\tSeqKey               string `inbound:\"seq-key,omitempty\"`\n\tUplinkDataPlacement  string `inbound:\"uplink-data-placement,omitempty\"`\n\tUplinkDataKey        string `inbound:\"uplink-data-key,omitempty\"`\n\tUplinkChunkSize      string `inbound:\"uplink-chunk-size,omitempty\"`\n\tNoSSEHeader          bool   `inbound:\"no-sse-header,omitempty\"`\n\tScStreamUpServerSecs string `inbound:\"sc-stream-up-server-secs,omitempty\"`\n\tScMaxBufferedPosts   string `inbound:\"sc-max-buffered-posts,omitempty\"`\n\tScMaxEachPostBytes   string `inbound:\"sc-max-each-post-bytes,omitempty\"`\n}\n\nfunc (o XHTTPConfig) Build() LC.XHTTPConfig {\n\treturn LC.XHTTPConfig{\n\t\tPath:                 o.Path,\n\t\tHost:                 o.Host,\n\t\tMode:                 o.Mode,\n\t\tNoSSEHeader:          o.NoSSEHeader,\n\t\tXPaddingBytes:        o.XPaddingBytes,\n\t\tXPaddingObfsMode:     o.XPaddingObfsMode,\n\t\tXPaddingKey:          o.XPaddingKey,\n\t\tXPaddingHeader:       o.XPaddingHeader,\n\t\tXPaddingPlacement:    o.XPaddingPlacement,\n\t\tUplinkHTTPMethod:     o.UplinkHTTPMethod,\n\t\tSessionPlacement:     o.SessionPlacement,\n\t\tSessionKey:           o.SessionKey,\n\t\tSeqPlacement:         o.SeqPlacement,\n\t\tSeqKey:               o.SeqKey,\n\t\tUplinkDataPlacement:  o.UplinkDataPlacement,\n\t\tUplinkDataKey:        o.UplinkDataKey,\n\t\tUplinkChunkSize:      o.UplinkChunkSize,\n\t\tScStreamUpServerSecs: o.ScStreamUpServerSecs,\n\t\tScMaxBufferedPosts:   o.ScMaxBufferedPosts,\n\t\tScMaxEachPostBytes:   o.ScMaxEachPostBytes,\n\t}\n}\n\nfunc (o VlessOption) Equal(config C.InboundConfig) bool {\n\treturn optionToString(o) == optionToString(config)\n}\n\ntype Vless struct {\n\t*Base\n\tconfig *VlessOption\n\tl      C.MultiAddrListener\n\tvs     LC.VlessServer\n}\n\nfunc NewVless(options *VlessOption) (*Vless, error) {\n\tbase, err := NewBase(&options.BaseOption)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tusers := make([]LC.VlessUser, len(options.Users))\n\tfor i, v := range options.Users {\n\t\tusers[i] = LC.VlessUser{\n\t\t\tUsername: v.Username,\n\t\t\tUUID:     v.UUID,\n\t\t\tFlow:     v.Flow,\n\t\t}\n\t}\n\treturn &Vless{\n\t\tBase:   base,\n\t\tconfig: options,\n\t\tvs: LC.VlessServer{\n\t\t\tEnable:          true,\n\t\t\tListen:          base.RawAddress(),\n\t\t\tUsers:           users,\n\t\t\tDecryption:      options.Decryption,\n\t\t\tWsPath:          options.WsPath,\n\t\t\tXHTTPConfig:     options.XHTTPConfig.Build(),\n\t\t\tGrpcServiceName: options.GrpcServiceName,\n\t\t\tCertificate:     options.Certificate,\n\t\t\tPrivateKey:      options.PrivateKey,\n\t\t\tClientAuthType:  options.ClientAuthType,\n\t\t\tClientAuthCert:  options.ClientAuthCert,\n\t\t\tEchKey:          options.EchKey,\n\t\t\tRealityConfig:   options.RealityConfig.Build(),\n\t\t\tMuxOption:       options.MuxOption.Build(),\n\t\t},\n\t}, nil\n}\n\n// Config implements constant.InboundListener\nfunc (v *Vless) Config() C.InboundConfig {\n\treturn v.config\n}\n\n// Address implements constant.InboundListener\nfunc (v *Vless) Address() string {\n\tvar addrList []string\n\tif v.l != nil {\n\t\tfor _, addr := range v.l.AddrList() {\n\t\t\taddrList = append(addrList, addr.String())\n\t\t}\n\t}\n\treturn strings.Join(addrList, \",\")\n}\n\n// Listen implements constant.InboundListener\nfunc (v *Vless) Listen(tunnel C.Tunnel) error {\n\tvar err error\n\tv.l, err = sing_vless.New(v.vs, tunnel, v.Additions()...)\n\tif err != nil {\n\t\treturn err\n\t}\n\tlog.Infoln(\"Vless[%s] proxy listening at: %s\", v.Name(), v.Address())\n\treturn nil\n}\n\n// Close implements constant.InboundListener\nfunc (v *Vless) Close() error {\n\treturn v.l.Close()\n}\n\nvar _ C.InboundListener = (*Vless)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/vless_test.go",
    "content": "package inbound_test\n\nimport (\n\t\"net\"\n\t\"net/netip\"\n\t\"testing\"\n\n\t\"github.com/metacubex/mihomo/adapter/outbound\"\n\t\"github.com/metacubex/mihomo/listener/inbound\"\n\t\"github.com/metacubex/mihomo/transport/vless/encryption\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc testInboundVless(t *testing.T, inboundOptions inbound.VlessOption, outboundOptions outbound.VlessOption) {\n\tt.Parallel()\n\tinboundOptions.BaseOption = inbound.BaseOption{\n\t\tNameStr: \"vless_inbound\",\n\t\tListen:  \"127.0.0.1\",\n\t\tPort:    \"0\",\n\t}\n\tinboundOptions.Users = []inbound.VlessUser{\n\t\t{Username: \"test\", UUID: userUUID, Flow: \"xtls-rprx-vision\"},\n\t}\n\tin, err := inbound.NewVless(&inboundOptions)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\n\ttunnel := NewHttpTestTunnel()\n\tdefer tunnel.Close()\n\n\terr = in.Listen(tunnel)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\tdefer in.Close()\n\n\taddrPort, err := netip.ParseAddrPort(in.Address())\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\n\toutboundOptions.Name = \"vless_outbound\"\n\toutboundOptions.Server = addrPort.Addr().String()\n\toutboundOptions.Port = int(addrPort.Port())\n\toutboundOptions.UUID = userUUID\n\toutboundOptions.DialerForAPI = tunnel.NewDialer()\n\n\tout, err := outbound.NewVless(outboundOptions)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\tdefer out.Close()\n\n\ttunnel.DoTest(t, out)\n\n\tif outboundOptions.Network == \"grpc\" { // don't test sing-mux over grpc\n\t\treturn\n\t}\n\ttestSingMux(t, tunnel, out)\n}\n\nfunc testInboundVlessTLS(t *testing.T, inboundOptions inbound.VlessOption, outboundOptions outbound.VlessOption, testVision bool) {\n\ttestInboundVless(t, inboundOptions, outboundOptions)\n\tif testVision {\n\t\tt.Run(\"xtls-rprx-vision\", func(t *testing.T) {\n\t\t\toutboundOptions := outboundOptions\n\t\t\toutboundOptions.Flow = \"xtls-rprx-vision\"\n\t\t\ttestInboundVless(t, inboundOptions, outboundOptions)\n\t\t})\n\t}\n\tt.Run(\"ECH\", func(t *testing.T) {\n\t\tinboundOptions := inboundOptions\n\t\toutboundOptions := outboundOptions\n\t\tinboundOptions.EchKey = echKeyPem\n\t\toutboundOptions.ECHOpts = outbound.ECHOptions{\n\t\t\tEnable: true,\n\t\t\tConfig: echConfigBase64,\n\t\t}\n\t\ttestInboundVless(t, inboundOptions, outboundOptions)\n\t\tif testVision {\n\t\t\tt.Run(\"xtls-rprx-vision\", func(t *testing.T) {\n\t\t\t\toutboundOptions := outboundOptions\n\t\t\t\toutboundOptions.Flow = \"xtls-rprx-vision\"\n\t\t\t\ttestInboundVless(t, inboundOptions, outboundOptions)\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"mTLS\", func(t *testing.T) {\n\t\tinboundOptions := inboundOptions\n\t\toutboundOptions := outboundOptions\n\t\tinboundOptions.ClientAuthCert = tlsAuthCertificate\n\t\toutboundOptions.Certificate = tlsAuthCertificate\n\t\toutboundOptions.PrivateKey = tlsAuthPrivateKey\n\t\ttestInboundVless(t, inboundOptions, outboundOptions)\n\t\tif testVision {\n\t\t\tt.Run(\"xtls-rprx-vision\", func(t *testing.T) {\n\t\t\t\toutboundOptions := outboundOptions\n\t\t\t\toutboundOptions.Flow = \"xtls-rprx-vision\"\n\t\t\t\ttestInboundVless(t, inboundOptions, outboundOptions)\n\t\t\t})\n\t\t}\n\t})\n\tt.Run(\"mTLS+ECH\", func(t *testing.T) {\n\t\tinboundOptions := inboundOptions\n\t\toutboundOptions := outboundOptions\n\t\tinboundOptions.ClientAuthCert = tlsAuthCertificate\n\t\toutboundOptions.Certificate = tlsAuthCertificate\n\t\toutboundOptions.PrivateKey = tlsAuthPrivateKey\n\t\tinboundOptions.EchKey = echKeyPem\n\t\toutboundOptions.ECHOpts = outbound.ECHOptions{\n\t\t\tEnable: true,\n\t\t\tConfig: echConfigBase64,\n\t\t}\n\t\ttestInboundVless(t, inboundOptions, outboundOptions)\n\t\tif testVision {\n\t\t\tt.Run(\"xtls-rprx-vision\", func(t *testing.T) {\n\t\t\t\toutboundOptions := outboundOptions\n\t\t\t\toutboundOptions.Flow = \"xtls-rprx-vision\"\n\t\t\t\ttestInboundVless(t, inboundOptions, outboundOptions)\n\t\t\t})\n\t\t}\n\t})\n}\n\nfunc TestInboundVless_TLS(t *testing.T) {\n\tinboundOptions := inbound.VlessOption{\n\t\tCertificate: tlsCertificate,\n\t\tPrivateKey:  tlsPrivateKey,\n\t}\n\toutboundOptions := outbound.VlessOption{\n\t\tTLS:         true,\n\t\tFingerprint: tlsFingerprint,\n\t}\n\ttestInboundVlessTLS(t, inboundOptions, outboundOptions, true)\n}\n\nfunc TestInboundVless_Encryption(t *testing.T) {\n\tseedBase64, clientBase64, _, err := encryption.GenMLKEM768(\"\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t\treturn\n\t}\n\tprivateKeyBase64, passwordBase64, _, err := encryption.GenX25519(\"\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t\treturn\n\t}\n\tpaddings := []struct {\n\t\tname string\n\t\tdata string\n\t}{\n\t\t{\"unconfigured-padding\", \"\"},\n\t\t{\"default-padding\", \"100-111-1111.75-0-111.50-0-3333.\"},\n\t\t{\"old-padding\", \"100-100-1000.\"}, // Xray-core v25.8.29\n\t\t{\"custom-padding\", \"100-1234-7890.33-0-1111.66-0-6666.55-111-777.\"},\n\t}\n\tvar modes = []string{\n\t\t\"native\",\n\t\t\"xorpub\",\n\t\t\"random\",\n\t}\n\tfor i := range modes {\n\t\tmode := modes[i]\n\t\tt.Run(mode, func(t *testing.T) {\n\t\t\tt.Parallel()\n\t\t\tfor i := range paddings {\n\t\t\t\tpadding := paddings[i].data\n\t\t\t\tt.Run(paddings[i].name, func(t *testing.T) {\n\t\t\t\t\tt.Parallel()\n\t\t\t\t\tinboundOptions := inbound.VlessOption{\n\t\t\t\t\t\tDecryption: \"mlkem768x25519plus.\" + mode + \".600s.\" + padding + privateKeyBase64 + \".\" + seedBase64,\n\t\t\t\t\t}\n\t\t\t\t\toutboundOptions := outbound.VlessOption{\n\t\t\t\t\t\tEncryption: \"mlkem768x25519plus.\" + mode + \".0rtt.\" + padding + passwordBase64 + \".\" + clientBase64,\n\t\t\t\t\t}\n\t\t\t\t\tt.Run(\"raw\", func(t *testing.T) {\n\t\t\t\t\t\ttestInboundVless(t, inboundOptions, outboundOptions)\n\t\t\t\t\t\tt.Run(\"xtls-rprx-vision\", func(t *testing.T) {\n\t\t\t\t\t\t\toutboundOptions := outboundOptions\n\t\t\t\t\t\t\toutboundOptions.Flow = \"xtls-rprx-vision\"\n\t\t\t\t\t\t\ttestInboundVless(t, inboundOptions, outboundOptions)\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\t\tt.Run(\"ws\", func(t *testing.T) {\n\t\t\t\t\t\tinboundOptions := inboundOptions\n\t\t\t\t\t\tinboundOptions.WsPath = \"/ws\"\n\t\t\t\t\t\toutboundOptions := outboundOptions\n\t\t\t\t\t\toutboundOptions.Network = \"ws\"\n\t\t\t\t\t\toutboundOptions.WSOpts = outbound.WSOptions{Path: \"/ws\"}\n\t\t\t\t\t\ttestInboundVless(t, inboundOptions, outboundOptions)\n\t\t\t\t\t\tt.Run(\"xtls-rprx-vision\", func(t *testing.T) {\n\t\t\t\t\t\t\toutboundOptions := outboundOptions\n\t\t\t\t\t\t\toutboundOptions.Flow = \"xtls-rprx-vision\"\n\t\t\t\t\t\t\ttestInboundVless(t, inboundOptions, outboundOptions)\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\t\tt.Run(\"grpc\", func(t *testing.T) {\n\t\t\t\t\t\tinboundOptions := inboundOptions\n\t\t\t\t\t\tinboundOptions.GrpcServiceName = \"GunService\"\n\t\t\t\t\t\toutboundOptions := outboundOptions\n\t\t\t\t\t\toutboundOptions.Network = \"grpc\"\n\t\t\t\t\t\toutboundOptions.GrpcOpts = outbound.GrpcOptions{GrpcServiceName: \"GunService\"}\n\t\t\t\t\t\ttestInboundVless(t, inboundOptions, outboundOptions)\n\t\t\t\t\t\tt.Run(\"xtls-rprx-vision\", func(t *testing.T) {\n\t\t\t\t\t\t\toutboundOptions := outboundOptions\n\t\t\t\t\t\t\toutboundOptions.Flow = \"xtls-rprx-vision\"\n\t\t\t\t\t\t\ttestInboundVless(t, inboundOptions, outboundOptions)\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t}\n}\n\nfunc TestInboundVless_Wss1(t *testing.T) {\n\tinboundOptions := inbound.VlessOption{\n\t\tCertificate: tlsCertificate,\n\t\tPrivateKey:  tlsPrivateKey,\n\t\tWsPath:      \"/ws\",\n\t}\n\toutboundOptions := outbound.VlessOption{\n\t\tTLS:         true,\n\t\tFingerprint: tlsFingerprint,\n\t\tNetwork:     \"ws\",\n\t\tWSOpts:      outbound.WSOptions{Path: \"/ws\"},\n\t}\n\ttestInboundVlessTLS(t, inboundOptions, outboundOptions, false)\n}\n\nfunc TestInboundVless_Wss2(t *testing.T) {\n\tinboundOptions := inbound.VlessOption{\n\t\tCertificate:     tlsCertificate,\n\t\tPrivateKey:      tlsPrivateKey,\n\t\tWsPath:          \"/ws\",\n\t\tGrpcServiceName: \"GunService\",\n\t}\n\toutboundOptions := outbound.VlessOption{\n\t\tTLS:         true,\n\t\tFingerprint: tlsFingerprint,\n\t\tNetwork:     \"ws\",\n\t\tWSOpts:      outbound.WSOptions{Path: \"/ws\"},\n\t}\n\ttestInboundVlessTLS(t, inboundOptions, outboundOptions, false)\n}\n\nfunc TestInboundVless_Grpc1(t *testing.T) {\n\tinboundOptions := inbound.VlessOption{\n\t\tCertificate:     tlsCertificate,\n\t\tPrivateKey:      tlsPrivateKey,\n\t\tGrpcServiceName: \"GunService\",\n\t}\n\toutboundOptions := outbound.VlessOption{\n\t\tTLS:         true,\n\t\tFingerprint: tlsFingerprint,\n\t\tNetwork:     \"grpc\",\n\t\tGrpcOpts:    outbound.GrpcOptions{GrpcServiceName: \"GunService\"},\n\t}\n\ttestInboundVlessTLS(t, inboundOptions, outboundOptions, false)\n}\n\nfunc TestInboundVless_Grpc2(t *testing.T) {\n\tinboundOptions := inbound.VlessOption{\n\t\tCertificate:     tlsCertificate,\n\t\tPrivateKey:      tlsPrivateKey,\n\t\tWsPath:          \"/ws\",\n\t\tGrpcServiceName: \"GunService\",\n\t}\n\toutboundOptions := outbound.VlessOption{\n\t\tTLS:         true,\n\t\tFingerprint: tlsFingerprint,\n\t\tNetwork:     \"grpc\",\n\t\tGrpcOpts:    outbound.GrpcOptions{GrpcServiceName: \"GunService\"},\n\t}\n\ttestInboundVlessTLS(t, inboundOptions, outboundOptions, false)\n}\n\nfunc TestInboundVless_Reality(t *testing.T) {\n\tinboundOptions := inbound.VlessOption{\n\t\tRealityConfig: inbound.RealityConfig{\n\t\t\tDest:        net.JoinHostPort(realityDest, \"443\"),\n\t\t\tPrivateKey:  realityPrivateKey,\n\t\t\tShortID:     []string{realityShortid},\n\t\t\tServerNames: []string{realityDest},\n\t\t},\n\t}\n\toutboundOptions := outbound.VlessOption{\n\t\tTLS:        true,\n\t\tServerName: realityDest,\n\t\tRealityOpts: outbound.RealityOptions{\n\t\t\tPublicKey: realityPublickey,\n\t\t\tShortID:   realityShortid,\n\t\t},\n\t\tClientFingerprint: \"chrome\",\n\t}\n\ttestInboundVless(t, inboundOptions, outboundOptions)\n\tt.Run(\"xtls-rprx-vision\", func(t *testing.T) {\n\t\toutboundOptions := outboundOptions\n\t\toutboundOptions.Flow = \"xtls-rprx-vision\"\n\t\ttestInboundVless(t, inboundOptions, outboundOptions)\n\t})\n\tt.Run(\"X25519MLKEM768\", func(t *testing.T) {\n\t\toutboundOptions := outboundOptions\n\t\toutboundOptions.RealityOpts.SupportX25519MLKEM768 = true\n\t\ttestInboundVless(t, inboundOptions, outboundOptions)\n\t\tt.Run(\"xtls-rprx-vision\", func(t *testing.T) {\n\t\t\toutboundOptions := outboundOptions\n\t\t\toutboundOptions.Flow = \"xtls-rprx-vision\"\n\t\t\ttestInboundVless(t, inboundOptions, outboundOptions)\n\t\t})\n\t})\n}\n\nfunc TestInboundVless_Reality_Grpc(t *testing.T) {\n\tinboundOptions := inbound.VlessOption{\n\t\tRealityConfig: inbound.RealityConfig{\n\t\t\tDest:        net.JoinHostPort(realityDest, \"443\"),\n\t\t\tPrivateKey:  realityPrivateKey,\n\t\t\tShortID:     []string{realityShortid},\n\t\t\tServerNames: []string{realityDest},\n\t\t},\n\t\tGrpcServiceName: \"GunService\",\n\t}\n\toutboundOptions := outbound.VlessOption{\n\t\tTLS:        true,\n\t\tServerName: realityDest,\n\t\tRealityOpts: outbound.RealityOptions{\n\t\t\tPublicKey: realityPublickey,\n\t\t\tShortID:   realityShortid,\n\t\t},\n\t\tClientFingerprint: \"chrome\",\n\t\tNetwork:           \"grpc\",\n\t\tGrpcOpts:          outbound.GrpcOptions{GrpcServiceName: \"GunService\"},\n\t}\n\ttestInboundVless(t, inboundOptions, outboundOptions)\n\tt.Run(\"X25519MLKEM768\", func(t *testing.T) {\n\t\toutboundOptions := outboundOptions\n\t\toutboundOptions.RealityOpts.SupportX25519MLKEM768 = true\n\t\ttestInboundVless(t, inboundOptions, outboundOptions)\n\t})\n}\n\nfunc TestInboundVless_XHTTP(t *testing.T) {\n\ttestCases := []struct {\n\t\tmode string\n\t}{\n\t\t{mode: \"auto\"},\n\t\t{mode: \"stream-one\"},\n\t\t{mode: \"stream-up\"},\n\t\t{mode: \"packet-up\"},\n\t}\n\tfor _, testCase := range testCases {\n\t\ttestCase := testCase\n\t\tt.Run(testCase.mode, func(t *testing.T) {\n\t\t\tgetConfig := func() (inbound.VlessOption, outbound.VlessOption) {\n\t\t\t\tinboundOptions := inbound.VlessOption{\n\t\t\t\t\tCertificate: tlsCertificate,\n\t\t\t\t\tPrivateKey:  tlsPrivateKey,\n\t\t\t\t\tXHTTPConfig: inbound.XHTTPConfig{\n\t\t\t\t\t\tPath: \"/vless-xhttp\",\n\t\t\t\t\t\tHost: \"example.com\",\n\t\t\t\t\t\tMode: testCase.mode,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\toutboundOptions := outbound.VlessOption{\n\t\t\t\t\tTLS:               true,\n\t\t\t\t\tFingerprint:       tlsFingerprint,\n\t\t\t\t\tServerName:        \"example.org\",\n\t\t\t\t\tClientFingerprint: \"chrome\",\n\t\t\t\t\tNetwork:           \"xhttp\",\n\t\t\t\t\tXHTTPOpts: outbound.XHTTPOptions{\n\t\t\t\t\t\tPath: \"/vless-xhttp\",\n\t\t\t\t\t\tHost: \"example.com\",\n\t\t\t\t\t\tMode: testCase.mode,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\treturn inboundOptions, outboundOptions\n\t\t\t}\n\t\t\ttestInboundVless_XHTTP(t, getConfig, testCase.mode)\n\t\t})\n\t}\n}\n\nfunc testInboundVless_XHTTP(t *testing.T, getConfig func() (inbound.VlessOption, outbound.VlessOption), mode string) {\n\tt.Run(\"nosplit\", func(t *testing.T) {\n\t\tt.Run(\"single\", func(t *testing.T) {\n\t\t\tinboundOptions, outboundOptions := getConfig()\n\t\t\ttestInboundVlessTLS(t, inboundOptions, outboundOptions, false)\n\t\t})\n\n\t\tt.Run(\"reuse\", func(t *testing.T) {\n\t\t\tinboundOptions, outboundOptions := getConfig()\n\t\t\ttestInboundVlessTLS(t, inboundOptions, withXHTTPReuse(outboundOptions), false)\n\t\t})\n\t})\n\n\tt.Run(\"split\", func(t *testing.T) {\n\t\tif mode == \"stream-one\" { // stream-one not supported download settings\n\t\t\treturn\n\t\t}\n\n\t\tt.Run(\"single\", func(t *testing.T) {\n\t\t\tinboundOptions, outboundOptions := getConfig()\n\t\t\toutboundOptions.XHTTPOpts.DownloadSettings = &outbound.XHTTPDownloadSettings{}\n\t\t\ttestInboundVlessTLS(t, inboundOptions, outboundOptions, false)\n\t\t})\n\n\t\tt.Run(\"reuse\", func(t *testing.T) {\n\t\t\tinboundOptions, outboundOptions := getConfig()\n\t\t\toutboundOptions.XHTTPOpts.DownloadSettings = &outbound.XHTTPDownloadSettings{}\n\t\t\ttestInboundVlessTLS(t, inboundOptions, withXHTTPReuse(outboundOptions), false)\n\t\t})\n\t})\n}\n\nfunc TestInboundVless_XHTTP_Reality(t *testing.T) {\n\ttestCases := []struct {\n\t\tmode string\n\t}{\n\t\t{mode: \"auto\"},\n\t\t{mode: \"stream-one\"},\n\t\t{mode: \"stream-up\"},\n\t\t{mode: \"packet-up\"},\n\t}\n\tfor _, testCase := range testCases {\n\t\ttestCase := testCase\n\t\tt.Run(testCase.mode, func(t *testing.T) {\n\t\t\tgetConfig := func() (inbound.VlessOption, outbound.VlessOption) {\n\t\t\t\tinboundOptions := inbound.VlessOption{\n\t\t\t\t\tRealityConfig: inbound.RealityConfig{\n\t\t\t\t\t\tDest:        net.JoinHostPort(realityDest, \"443\"),\n\t\t\t\t\t\tPrivateKey:  realityPrivateKey,\n\t\t\t\t\t\tShortID:     []string{realityShortid},\n\t\t\t\t\t\tServerNames: []string{realityDest},\n\t\t\t\t\t},\n\t\t\t\t\tXHTTPConfig: inbound.XHTTPConfig{\n\t\t\t\t\t\tPath: \"/vless-xhttp\",\n\t\t\t\t\t\tHost: \"example.com\",\n\t\t\t\t\t\tMode: testCase.mode,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\toutboundOptions := outbound.VlessOption{\n\t\t\t\t\tTLS:        true,\n\t\t\t\t\tServerName: realityDest,\n\t\t\t\t\tRealityOpts: outbound.RealityOptions{\n\t\t\t\t\t\tPublicKey: realityPublickey,\n\t\t\t\t\t\tShortID:   realityShortid,\n\t\t\t\t\t},\n\t\t\t\t\tClientFingerprint: \"chrome\",\n\t\t\t\t\tNetwork:           \"xhttp\",\n\t\t\t\t\tXHTTPOpts: outbound.XHTTPOptions{\n\t\t\t\t\t\tPath: \"/vless-xhttp\",\n\t\t\t\t\t\tHost: \"example.com\",\n\t\t\t\t\t\tMode: testCase.mode,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\treturn inboundOptions, outboundOptions\n\t\t\t}\n\t\t\ttestInboundVless_XHTTP_Reality(t, getConfig, testCase.mode)\n\t\t})\n\t}\n}\n\nfunc testInboundVless_XHTTP_Reality(t *testing.T, getConfig func() (inbound.VlessOption, outbound.VlessOption), mode string) {\n\tt.Run(\"nosplit\", func(t *testing.T) {\n\t\tt.Run(\"single\", func(t *testing.T) {\n\t\t\tinboundOptions, outboundOptions := getConfig()\n\t\t\ttestInboundVless(t, inboundOptions, outboundOptions)\n\t\t})\n\n\t\tt.Run(\"reuse\", func(t *testing.T) {\n\t\t\tinboundOptions, outboundOptions := getConfig()\n\t\t\ttestInboundVless(t, inboundOptions, withXHTTPReuse(outboundOptions))\n\t\t})\n\t})\n\n\tt.Run(\"split\", func(t *testing.T) {\n\t\tif mode == \"stream-one\" { // stream-one not supported download settings\n\t\t\treturn\n\t\t}\n\n\t\tt.Run(\"single\", func(t *testing.T) {\n\t\t\tinboundOptions, outboundOptions := getConfig()\n\t\t\toutboundOptions.XHTTPOpts.DownloadSettings = &outbound.XHTTPDownloadSettings{}\n\t\t\ttestInboundVless(t, inboundOptions, outboundOptions)\n\t\t})\n\n\t\tt.Run(\"reuse\", func(t *testing.T) {\n\t\t\tinboundOptions, outboundOptions := getConfig()\n\t\t\toutboundOptions.XHTTPOpts.DownloadSettings = &outbound.XHTTPDownloadSettings{}\n\t\t\ttestInboundVless(t, inboundOptions, withXHTTPReuse(outboundOptions))\n\t\t})\n\t})\n}\n\nfunc TestInboundVless_XHTTP_Encryption(t *testing.T) {\n\tprivateKeyBase64, passwordBase64, _, err := encryption.GenX25519(\"\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t\treturn\n\t}\n\ttestCases := []struct {\n\t\tmode string\n\t}{\n\t\t{mode: \"auto\"},\n\t\t{mode: \"stream-one\"},\n\t\t{mode: \"stream-up\"},\n\t\t{mode: \"packet-up\"},\n\t}\n\tfor _, testCase := range testCases {\n\t\ttestCase := testCase\n\t\tt.Run(testCase.mode, func(t *testing.T) {\n\t\t\tgetConfig := func() (inbound.VlessOption, outbound.VlessOption) {\n\t\t\t\tinboundOptions := inbound.VlessOption{\n\t\t\t\t\tDecryption: \"mlkem768x25519plus.native.600s.\" + privateKeyBase64,\n\t\t\t\t\tXHTTPConfig: inbound.XHTTPConfig{\n\t\t\t\t\t\tPath: \"/vless-xhttp\",\n\t\t\t\t\t\tHost: \"example.com\",\n\t\t\t\t\t\tMode: testCase.mode,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\toutboundOptions := outbound.VlessOption{\n\t\t\t\t\tEncryption: \"mlkem768x25519plus.native.0rtt.\" + passwordBase64,\n\t\t\t\t\tNetwork:    \"xhttp\",\n\t\t\t\t\tXHTTPOpts: outbound.XHTTPOptions{\n\t\t\t\t\t\tPath: \"/vless-xhttp\",\n\t\t\t\t\t\tHost: \"example.com\",\n\t\t\t\t\t\tMode: testCase.mode,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\treturn inboundOptions, outboundOptions\n\t\t\t}\n\t\t\ttestInboundVless_XHTTP_Encryption(t, getConfig, testCase.mode)\n\t\t})\n\t}\n}\n\nfunc testInboundVless_XHTTP_Encryption(t *testing.T, getConfig func() (inbound.VlessOption, outbound.VlessOption), mode string) {\n\tt.Run(\"nosplit\", func(t *testing.T) {\n\t\tt.Run(\"single\", func(t *testing.T) {\n\t\t\tinboundOptions, outboundOptions := getConfig()\n\t\t\ttestInboundVless(t, inboundOptions, outboundOptions)\n\t\t})\n\n\t\tt.Run(\"reuse\", func(t *testing.T) {\n\t\t\tinboundOptions, outboundOptions := getConfig()\n\t\t\ttestInboundVless(t, inboundOptions, withXHTTPReuse(outboundOptions))\n\t\t})\n\t})\n\n\tt.Run(\"split\", func(t *testing.T) {\n\t\tif mode == \"stream-one\" { // stream-one not supported download settings\n\t\t\treturn\n\t\t}\n\n\t\tt.Run(\"single\", func(t *testing.T) {\n\t\t\tinboundOptions, outboundOptions := getConfig()\n\t\t\toutboundOptions.XHTTPOpts.DownloadSettings = &outbound.XHTTPDownloadSettings{}\n\t\t\ttestInboundVless(t, inboundOptions, outboundOptions)\n\t\t})\n\n\t\tt.Run(\"reuse\", func(t *testing.T) {\n\t\t\tinboundOptions, outboundOptions := getConfig()\n\t\t\toutboundOptions.XHTTPOpts.DownloadSettings = &outbound.XHTTPDownloadSettings{}\n\t\t\ttestInboundVless(t, inboundOptions, withXHTTPReuse(outboundOptions))\n\t\t})\n\t})\n}\n\nfunc TestInboundVless_XHTTP_H1(t *testing.T) {\n\ttestCases := []struct {\n\t\tmode string\n\t}{\n\t\t{mode: \"auto\"},\n\t\t{mode: \"stream-one\"},\n\t\t{mode: \"stream-up\"},\n\t\t{mode: \"packet-up\"},\n\t}\n\tfor _, testCase := range testCases {\n\t\ttestCase := testCase\n\t\tt.Run(testCase.mode, func(t *testing.T) {\n\t\t\tgetConfig := func() (inbound.VlessOption, outbound.VlessOption) {\n\t\t\t\tinboundOptions := inbound.VlessOption{\n\t\t\t\t\tCertificate: tlsCertificate,\n\t\t\t\t\tPrivateKey:  tlsPrivateKey,\n\t\t\t\t\tXHTTPConfig: inbound.XHTTPConfig{\n\t\t\t\t\t\tPath: \"/vless-xhttp\",\n\t\t\t\t\t\tHost: \"example.com\",\n\t\t\t\t\t\tMode: testCase.mode,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\toutboundOptions := outbound.VlessOption{\n\t\t\t\t\tTLS:         true,\n\t\t\t\t\tFingerprint: tlsFingerprint,\n\t\t\t\t\tNetwork:     \"xhttp\",\n\t\t\t\t\tALPN:        []string{\"http/1.1\"},\n\t\t\t\t\tXHTTPOpts: outbound.XHTTPOptions{\n\t\t\t\t\t\tPath: \"/vless-xhttp\",\n\t\t\t\t\t\tHost: \"example.com\",\n\t\t\t\t\t\tMode: testCase.mode,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\treturn inboundOptions, outboundOptions\n\t\t\t}\n\t\t\ttestInboundVless_XHTTP(t, getConfig, testCase.mode)\n\t\t})\n\t}\n}\n\nfunc TestInboundVless_XHTTP_H1_Encryption(t *testing.T) {\n\tprivateKeyBase64, passwordBase64, _, err := encryption.GenX25519(\"\")\n\tif err != nil {\n\t\tt.Fatal(err)\n\t\treturn\n\t}\n\ttestCases := []struct {\n\t\tmode string\n\t}{\n\t\t{mode: \"auto\"},\n\t\t{mode: \"stream-one\"},\n\t\t{mode: \"stream-up\"},\n\t\t{mode: \"packet-up\"},\n\t}\n\tfor _, testCase := range testCases {\n\t\ttestCase := testCase\n\t\tt.Run(testCase.mode, func(t *testing.T) {\n\t\t\tgetConfig := func() (inbound.VlessOption, outbound.VlessOption) {\n\t\t\t\tinboundOptions := inbound.VlessOption{\n\t\t\t\t\tDecryption: \"mlkem768x25519plus.native.600s.\" + privateKeyBase64,\n\t\t\t\t\tXHTTPConfig: inbound.XHTTPConfig{\n\t\t\t\t\t\tPath: \"/vless-xhttp\",\n\t\t\t\t\t\tHost: \"example.com\",\n\t\t\t\t\t\tMode: testCase.mode,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\toutboundOptions := outbound.VlessOption{\n\t\t\t\t\tEncryption: \"mlkem768x25519plus.native.0rtt.\" + passwordBase64,\n\t\t\t\t\tNetwork:    \"xhttp\",\n\t\t\t\t\tALPN:       []string{\"http/1.1\"},\n\t\t\t\t\tXHTTPOpts: outbound.XHTTPOptions{\n\t\t\t\t\t\tPath: \"/vless-xhttp\",\n\t\t\t\t\t\tHost: \"example.com\",\n\t\t\t\t\t\tMode: testCase.mode,\n\t\t\t\t\t},\n\t\t\t\t}\n\t\t\t\treturn inboundOptions, outboundOptions\n\t\t\t}\n\t\t\ttestInboundVless_XHTTP_Encryption(t, getConfig, testCase.mode)\n\t\t})\n\t}\n}\n\nfunc withXHTTPReuse(out outbound.VlessOption) outbound.VlessOption {\n\tout.XHTTPOpts.ReuseSettings = &outbound.XHTTPReuseSettings{\n\t\tMaxConnections:   \"0\",\n\t\tMaxConcurrency:   \"16-32\",\n\t\tCMaxReuseTimes:   \"0\",\n\t\tHMaxRequestTimes: \"600-900\",\n\t\tHMaxReusableSecs: \"1800-3000\",\n\t}\n\tif out.XHTTPOpts.DownloadSettings != nil {\n\t\tout.XHTTPOpts.DownloadSettings.ReuseSettings = &outbound.XHTTPReuseSettings{\n\t\t\tMaxConnections:   \"0\",\n\t\t\tMaxConcurrency:   \"16-32\",\n\t\t\tCMaxReuseTimes:   \"0\",\n\t\t\tHMaxRequestTimes: \"600-900\",\n\t\t\tHMaxReusableSecs: \"1800-3000\",\n\t\t}\n\t}\n\treturn out\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/vmess.go",
    "content": "package inbound\n\nimport (\n\t\"strings\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/sing_vmess\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\ntype VmessOption struct {\n\tBaseOption\n\tUsers           []VmessUser   `inbound:\"users\"`\n\tWsPath          string        `inbound:\"ws-path,omitempty\"`\n\tGrpcServiceName string        `inbound:\"grpc-service-name,omitempty\"`\n\tCertificate     string        `inbound:\"certificate,omitempty\"`\n\tPrivateKey      string        `inbound:\"private-key,omitempty\"`\n\tClientAuthType  string        `inbound:\"client-auth-type,omitempty\"`\n\tClientAuthCert  string        `inbound:\"client-auth-cert,omitempty\"`\n\tEchKey          string        `inbound:\"ech-key,omitempty\"`\n\tRealityConfig   RealityConfig `inbound:\"reality-config,omitempty\"`\n\tMuxOption       MuxOption     `inbound:\"mux-option,omitempty\"`\n}\n\ntype VmessUser struct {\n\tUsername string `inbound:\"username,omitempty\"`\n\tUUID     string `inbound:\"uuid\"`\n\tAlterID  int    `inbound:\"alterId,omitempty\"`\n}\n\nfunc (o VmessOption) Equal(config C.InboundConfig) bool {\n\treturn optionToString(o) == optionToString(config)\n}\n\ntype Vmess struct {\n\t*Base\n\tconfig *VmessOption\n\tl      C.MultiAddrListener\n\tvs     LC.VmessServer\n}\n\nfunc NewVmess(options *VmessOption) (*Vmess, error) {\n\tbase, err := NewBase(&options.BaseOption)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tusers := make([]LC.VmessUser, len(options.Users))\n\tfor i, v := range options.Users {\n\t\tusers[i] = LC.VmessUser{\n\t\t\tUsername: v.Username,\n\t\t\tUUID:     v.UUID,\n\t\t\tAlterID:  v.AlterID,\n\t\t}\n\t}\n\treturn &Vmess{\n\t\tBase:   base,\n\t\tconfig: options,\n\t\tvs: LC.VmessServer{\n\t\t\tEnable:          true,\n\t\t\tListen:          base.RawAddress(),\n\t\t\tUsers:           users,\n\t\t\tWsPath:          options.WsPath,\n\t\t\tGrpcServiceName: options.GrpcServiceName,\n\t\t\tCertificate:     options.Certificate,\n\t\t\tPrivateKey:      options.PrivateKey,\n\t\t\tClientAuthType:  options.ClientAuthType,\n\t\t\tClientAuthCert:  options.ClientAuthCert,\n\t\t\tEchKey:          options.EchKey,\n\t\t\tRealityConfig:   options.RealityConfig.Build(),\n\t\t\tMuxOption:       options.MuxOption.Build(),\n\t\t},\n\t}, nil\n}\n\n// Config implements constant.InboundListener\nfunc (v *Vmess) Config() C.InboundConfig {\n\treturn v.config\n}\n\n// Address implements constant.InboundListener\nfunc (v *Vmess) Address() string {\n\tvar addrList []string\n\tif v.l != nil {\n\t\tfor _, addr := range v.l.AddrList() {\n\t\t\taddrList = append(addrList, addr.String())\n\t\t}\n\t}\n\treturn strings.Join(addrList, \",\")\n}\n\n// Listen implements constant.InboundListener\nfunc (v *Vmess) Listen(tunnel C.Tunnel) error {\n\tvar err error\n\tv.l, err = sing_vmess.New(v.vs, tunnel, v.Additions()...)\n\tif err != nil {\n\t\treturn err\n\t}\n\tlog.Infoln(\"Vmess[%s] proxy listening at: %s\", v.Name(), v.Address())\n\treturn nil\n}\n\n// Close implements constant.InboundListener\nfunc (v *Vmess) Close() error {\n\treturn v.l.Close()\n}\n\nvar _ C.InboundListener = (*Vmess)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/listener/inbound/vmess_test.go",
    "content": "package inbound_test\n\nimport (\n\t\"net\"\n\t\"net/netip\"\n\t\"testing\"\n\n\t\"github.com/metacubex/mihomo/adapter/outbound\"\n\t\"github.com/metacubex/mihomo/listener/inbound\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc testInboundVMess(t *testing.T, inboundOptions inbound.VmessOption, outboundOptions outbound.VmessOption) {\n\tt.Parallel()\n\tinboundOptions.BaseOption = inbound.BaseOption{\n\t\tNameStr: \"vmess_inbound\",\n\t\tListen:  \"127.0.0.1\",\n\t\tPort:    \"0\",\n\t}\n\tinboundOptions.Users = []inbound.VmessUser{\n\t\t{Username: \"test\", UUID: userUUID, AlterID: 0},\n\t}\n\tin, err := inbound.NewVmess(&inboundOptions)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\n\ttunnel := NewHttpTestTunnel()\n\tdefer tunnel.Close()\n\n\terr = in.Listen(tunnel)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\tdefer in.Close()\n\n\taddrPort, err := netip.ParseAddrPort(in.Address())\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\n\toutboundOptions.Name = \"vmess_outbound\"\n\toutboundOptions.Server = addrPort.Addr().String()\n\toutboundOptions.Port = int(addrPort.Port())\n\toutboundOptions.UUID = userUUID\n\toutboundOptions.AlterID = 0\n\toutboundOptions.Cipher = \"auto\"\n\toutboundOptions.DialerForAPI = tunnel.NewDialer()\n\n\tout, err := outbound.NewVmess(outboundOptions)\n\tif !assert.NoError(t, err) {\n\t\treturn\n\t}\n\tdefer out.Close()\n\n\ttunnel.DoTest(t, out)\n\n\tif outboundOptions.Network == \"grpc\" { // don't test sing-mux over grpc\n\t\treturn\n\t}\n\ttestSingMux(t, tunnel, out)\n}\n\nfunc TestInboundVMess_Basic(t *testing.T) {\n\tinboundOptions := inbound.VmessOption{}\n\toutboundOptions := outbound.VmessOption{}\n\ttestInboundVMess(t, inboundOptions, outboundOptions)\n}\n\nfunc testInboundVMessTLS(t *testing.T, inboundOptions inbound.VmessOption, outboundOptions outbound.VmessOption) {\n\ttestInboundVMess(t, inboundOptions, outboundOptions)\n\tt.Run(\"ECH\", func(t *testing.T) {\n\t\tinboundOptions := inboundOptions\n\t\toutboundOptions := outboundOptions\n\t\tinboundOptions.EchKey = echKeyPem\n\t\toutboundOptions.ECHOpts = outbound.ECHOptions{\n\t\t\tEnable: true,\n\t\t\tConfig: echConfigBase64,\n\t\t}\n\t\ttestInboundVMess(t, inboundOptions, outboundOptions)\n\t})\n\tt.Run(\"mTLS\", func(t *testing.T) {\n\t\tinboundOptions := inboundOptions\n\t\toutboundOptions := outboundOptions\n\t\tinboundOptions.ClientAuthCert = tlsAuthCertificate\n\t\toutboundOptions.Certificate = tlsAuthCertificate\n\t\toutboundOptions.PrivateKey = tlsAuthPrivateKey\n\t\ttestInboundVMess(t, inboundOptions, outboundOptions)\n\t})\n\tt.Run(\"mTLS+ECH\", func(t *testing.T) {\n\t\tinboundOptions := inboundOptions\n\t\toutboundOptions := outboundOptions\n\t\tinboundOptions.ClientAuthCert = tlsAuthCertificate\n\t\toutboundOptions.Certificate = tlsAuthCertificate\n\t\toutboundOptions.PrivateKey = tlsAuthPrivateKey\n\t\tinboundOptions.EchKey = echKeyPem\n\t\toutboundOptions.ECHOpts = outbound.ECHOptions{\n\t\t\tEnable: true,\n\t\t\tConfig: echConfigBase64,\n\t\t}\n\t\ttestInboundVMess(t, inboundOptions, outboundOptions)\n\t})\n}\n\nfunc TestInboundVMess_TLS(t *testing.T) {\n\tinboundOptions := inbound.VmessOption{\n\t\tCertificate: tlsCertificate,\n\t\tPrivateKey:  tlsPrivateKey,\n\t}\n\toutboundOptions := outbound.VmessOption{\n\t\tTLS:         true,\n\t\tFingerprint: tlsFingerprint,\n\t}\n\ttestInboundVMessTLS(t, inboundOptions, outboundOptions)\n}\n\nfunc TestInboundVMess_Ws(t *testing.T) {\n\tinboundOptions := inbound.VmessOption{\n\t\tWsPath: \"/ws\",\n\t}\n\toutboundOptions := outbound.VmessOption{\n\t\tNetwork: \"ws\",\n\t\tWSOpts: outbound.WSOptions{\n\t\t\tPath: \"/ws\",\n\t\t},\n\t}\n\ttestInboundVMess(t, inboundOptions, outboundOptions)\n}\n\nfunc TestInboundVMess_Ws_ed1(t *testing.T) {\n\tinboundOptions := inbound.VmessOption{\n\t\tWsPath: \"/ws\",\n\t}\n\toutboundOptions := outbound.VmessOption{\n\t\tNetwork: \"ws\",\n\t\tWSOpts: outbound.WSOptions{\n\t\t\tPath: \"/ws?ed=2048\",\n\t\t},\n\t}\n\ttestInboundVMess(t, inboundOptions, outboundOptions)\n}\n\nfunc TestInboundVMess_Ws_ed2(t *testing.T) {\n\tinboundOptions := inbound.VmessOption{\n\t\tWsPath: \"/ws\",\n\t}\n\toutboundOptions := outbound.VmessOption{\n\t\tNetwork: \"ws\",\n\t\tWSOpts: outbound.WSOptions{\n\t\t\tPath:                \"/ws\",\n\t\t\tMaxEarlyData:        2048,\n\t\t\tEarlyDataHeaderName: \"Sec-WebSocket-Protocol\",\n\t\t},\n\t}\n\ttestInboundVMess(t, inboundOptions, outboundOptions)\n}\n\nfunc TestInboundVMess_Ws_Upgrade1(t *testing.T) {\n\tinboundOptions := inbound.VmessOption{\n\t\tWsPath: \"/ws\",\n\t}\n\toutboundOptions := outbound.VmessOption{\n\t\tNetwork: \"ws\",\n\t\tWSOpts: outbound.WSOptions{\n\t\t\tPath:             \"/ws\",\n\t\t\tV2rayHttpUpgrade: true,\n\t\t},\n\t}\n\ttestInboundVMess(t, inboundOptions, outboundOptions)\n}\n\nfunc TestInboundVMess_Ws_Upgrade2(t *testing.T) {\n\tinboundOptions := inbound.VmessOption{\n\t\tWsPath: \"/ws\",\n\t}\n\toutboundOptions := outbound.VmessOption{\n\t\tNetwork: \"ws\",\n\t\tWSOpts: outbound.WSOptions{\n\t\t\tPath:                     \"/ws\",\n\t\t\tV2rayHttpUpgrade:         true,\n\t\t\tV2rayHttpUpgradeFastOpen: true,\n\t\t},\n\t}\n\ttestInboundVMess(t, inboundOptions, outboundOptions)\n}\n\nfunc TestInboundVMess_Wss1(t *testing.T) {\n\tinboundOptions := inbound.VmessOption{\n\t\tCertificate: tlsCertificate,\n\t\tPrivateKey:  tlsPrivateKey,\n\t\tWsPath:      \"/ws\",\n\t}\n\toutboundOptions := outbound.VmessOption{\n\t\tTLS:         true,\n\t\tFingerprint: tlsFingerprint,\n\t\tNetwork:     \"ws\",\n\t\tWSOpts: outbound.WSOptions{\n\t\t\tPath: \"/ws\",\n\t\t},\n\t}\n\ttestInboundVMessTLS(t, inboundOptions, outboundOptions)\n}\n\nfunc TestInboundVMess_Wss2(t *testing.T) {\n\tinboundOptions := inbound.VmessOption{\n\t\tCertificate:     tlsCertificate,\n\t\tPrivateKey:      tlsPrivateKey,\n\t\tWsPath:          \"/ws\",\n\t\tGrpcServiceName: \"GunService\",\n\t}\n\toutboundOptions := outbound.VmessOption{\n\t\tTLS:         true,\n\t\tFingerprint: tlsFingerprint,\n\t\tNetwork:     \"ws\",\n\t\tWSOpts: outbound.WSOptions{\n\t\t\tPath: \"/ws\",\n\t\t},\n\t}\n\ttestInboundVMessTLS(t, inboundOptions, outboundOptions)\n}\n\nfunc TestInboundVMess_Grpc1(t *testing.T) {\n\tinboundOptions := inbound.VmessOption{\n\t\tCertificate:     tlsCertificate,\n\t\tPrivateKey:      tlsPrivateKey,\n\t\tGrpcServiceName: \"GunService\",\n\t}\n\toutboundOptions := outbound.VmessOption{\n\t\tTLS:         true,\n\t\tFingerprint: tlsFingerprint,\n\t\tNetwork:     \"grpc\",\n\t\tGrpcOpts:    outbound.GrpcOptions{GrpcServiceName: \"GunService\"},\n\t}\n\ttestInboundVMessTLS(t, inboundOptions, outboundOptions)\n}\n\nfunc TestInboundVMess_Grpc2(t *testing.T) {\n\tinboundOptions := inbound.VmessOption{\n\t\tCertificate:     tlsCertificate,\n\t\tPrivateKey:      tlsPrivateKey,\n\t\tWsPath:          \"/ws\",\n\t\tGrpcServiceName: \"GunService\",\n\t}\n\toutboundOptions := outbound.VmessOption{\n\t\tTLS:         true,\n\t\tFingerprint: tlsFingerprint,\n\t\tNetwork:     \"grpc\",\n\t\tGrpcOpts:    outbound.GrpcOptions{GrpcServiceName: \"GunService\"},\n\t}\n\ttestInboundVMessTLS(t, inboundOptions, outboundOptions)\n}\n\nfunc TestInboundVMess_Reality(t *testing.T) {\n\tinboundOptions := inbound.VmessOption{\n\t\tRealityConfig: inbound.RealityConfig{\n\t\t\tDest:        net.JoinHostPort(realityDest, \"443\"),\n\t\t\tPrivateKey:  realityPrivateKey,\n\t\t\tShortID:     []string{realityShortid},\n\t\t\tServerNames: []string{realityDest},\n\t\t},\n\t}\n\toutboundOptions := outbound.VmessOption{\n\t\tTLS:        true,\n\t\tServerName: realityDest,\n\t\tRealityOpts: outbound.RealityOptions{\n\t\t\tPublicKey: realityPublickey,\n\t\t\tShortID:   realityShortid,\n\t\t},\n\t\tClientFingerprint: \"chrome\",\n\t}\n\ttestInboundVMess(t, inboundOptions, outboundOptions)\n}\n\nfunc TestInboundVMess_Reality_Grpc(t *testing.T) {\n\tinboundOptions := inbound.VmessOption{\n\t\tRealityConfig: inbound.RealityConfig{\n\t\t\tDest:        net.JoinHostPort(realityDest, \"443\"),\n\t\t\tPrivateKey:  realityPrivateKey,\n\t\t\tShortID:     []string{realityShortid},\n\t\t\tServerNames: []string{realityDest},\n\t\t},\n\t\tGrpcServiceName: \"GunService\",\n\t}\n\toutboundOptions := outbound.VmessOption{\n\t\tTLS:        true,\n\t\tServerName: realityDest,\n\t\tRealityOpts: outbound.RealityOptions{\n\t\t\tPublicKey: realityPublickey,\n\t\t\tShortID:   realityShortid,\n\t\t},\n\t\tClientFingerprint: \"chrome\",\n\t\tNetwork:           \"grpc\",\n\t\tGrpcOpts:          outbound.GrpcOptions{GrpcServiceName: \"GunService\"},\n\t}\n\ttestInboundVMess(t, inboundOptions, outboundOptions)\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/inner/tcp.go",
    "content": "package inner\n\nimport (\n\t\"errors\"\n\t\"net\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\nvar tunnel C.Tunnel\n\nfunc New(t C.Tunnel) {\n\ttunnel = t\n}\n\nfunc GetTunnel() C.Tunnel {\n\treturn tunnel\n}\n\nfunc HandleTcp(tunnel C.Tunnel, address string, proxy string) (conn net.Conn, err error) {\n\tif tunnel == nil {\n\t\treturn nil, errors.New(\"tunnel uninitialized\")\n\t}\n\t// executor Parsed\n\tconn1, conn2 := N.Pipe()\n\n\tmetadata := &C.Metadata{}\n\tmetadata.NetWork = C.TCP\n\tmetadata.Type = C.INNER\n\tmetadata.DNSMode = C.DNSNormal\n\tmetadata.Process = C.MihomoName\n\tif proxy != \"\" {\n\t\tmetadata.SpecialProxy = proxy\n\t}\n\tif err = metadata.SetRemoteAddress(address); err != nil {\n\t\treturn nil, err\n\t}\n\n\tgo tunnel.HandleTCPConn(conn2, metadata)\n\treturn conn1, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/listener.go",
    "content": "package listener\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/http\"\n\t\"github.com/metacubex/mihomo/listener/mixed\"\n\t\"github.com/metacubex/mihomo/listener/redir\"\n\tembedSS \"github.com/metacubex/mihomo/listener/shadowsocks\"\n\t\"github.com/metacubex/mihomo/listener/sing_shadowsocks\"\n\t\"github.com/metacubex/mihomo/listener/sing_tun\"\n\t\"github.com/metacubex/mihomo/listener/sing_vmess\"\n\t\"github.com/metacubex/mihomo/listener/socks\"\n\t\"github.com/metacubex/mihomo/listener/tproxy\"\n\t\"github.com/metacubex/mihomo/listener/tuic\"\n\tLT \"github.com/metacubex/mihomo/listener/tunnel\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/samber/lo\"\n)\n\nvar (\n\tallowLan    = false\n\tbindAddress = \"*\"\n\n\tsocksListener       *socks.Listener\n\tsocksUDPListener    *socks.UDPListener\n\thttpListener        *http.Listener\n\tredirListener       *redir.Listener\n\tredirUDPListener    *tproxy.UDPListener\n\ttproxyListener      *tproxy.Listener\n\ttproxyUDPListener   *tproxy.UDPListener\n\tmixedListener       *mixed.Listener\n\tmixedUDPLister      *socks.UDPListener\n\ttunnelTCPListeners  = map[string]*LT.Listener{}\n\ttunnelUDPListeners  = map[string]*LT.PacketConn{}\n\tinboundListeners    = map[string]C.InboundListener{}\n\ttunLister           *sing_tun.Listener\n\tshadowSocksListener C.MultiAddrListener\n\tvmessListener       *sing_vmess.Listener\n\ttuicListener        *tuic.Listener\n\n\t// lock for recreate function\n\tsocksMux   sync.Mutex\n\thttpMux    sync.Mutex\n\tredirMux   sync.Mutex\n\ttproxyMux  sync.Mutex\n\tmixedMux   sync.Mutex\n\ttunnelMux  sync.Mutex\n\tinboundMux sync.Mutex\n\ttunMux     sync.Mutex\n\tssMux      sync.Mutex\n\tvmessMux   sync.Mutex\n\ttuicMux    sync.Mutex\n\n\tLastTunConf  LC.Tun\n\tLastTuicConf LC.TuicServer\n)\n\ntype Ports struct {\n\tPort              int    `json:\"port\"`\n\tSocksPort         int    `json:\"socks-port\"`\n\tRedirPort         int    `json:\"redir-port\"`\n\tTProxyPort        int    `json:\"tproxy-port\"`\n\tMixedPort         int    `json:\"mixed-port\"`\n\tShadowSocksConfig string `json:\"ss-config\"`\n\tVmessConfig       string `json:\"vmess-config\"`\n}\n\nfunc GetTunConf() LC.Tun {\n\tif tunLister == nil {\n\t\treturn LastTunConf\n\t}\n\treturn tunLister.Config()\n}\n\nfunc GetTuicConf() LC.TuicServer {\n\tif tuicListener == nil {\n\t\treturn LC.TuicServer{Enable: false}\n\t}\n\treturn tuicListener.Config()\n}\n\nfunc AllowLan() bool {\n\treturn allowLan\n}\n\nfunc BindAddress() string {\n\treturn bindAddress\n}\n\nfunc SetAllowLan(al bool) {\n\tallowLan = al\n}\n\nfunc SetBindAddress(host string) {\n\tbindAddress = host\n}\n\nfunc ReCreateHTTP(port int, tunnel C.Tunnel) {\n\thttpMux.Lock()\n\tdefer httpMux.Unlock()\n\n\tvar err error\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tlog.Errorln(\"Start HTTP server error: %s\", err.Error())\n\t\t}\n\t}()\n\n\taddr := genAddr(bindAddress, port, allowLan)\n\n\tif httpListener != nil {\n\t\tif httpListener.RawAddress() == addr {\n\t\t\treturn\n\t\t}\n\t\thttpListener.Close()\n\t\thttpListener = nil\n\t}\n\n\tif portIsZero(addr) {\n\t\treturn\n\t}\n\n\thttpListener, err = http.New(addr, tunnel)\n\tif err != nil {\n\t\tlog.Errorln(\"Start HTTP server error: %s\", err.Error())\n\t\treturn\n\t}\n\n\tlog.Infoln(\"HTTP proxy listening at: %s\", httpListener.Address())\n}\n\nfunc ReCreateSocks(port int, tunnel C.Tunnel) {\n\tsocksMux.Lock()\n\tdefer socksMux.Unlock()\n\n\tvar err error\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tlog.Errorln(\"Start SOCKS server error: %s\", err.Error())\n\t\t}\n\t}()\n\n\taddr := genAddr(bindAddress, port, allowLan)\n\n\tshouldTCPIgnore := false\n\tshouldUDPIgnore := false\n\n\tif socksListener != nil {\n\t\tif socksListener.RawAddress() != addr {\n\t\t\tsocksListener.Close()\n\t\t\tsocksListener = nil\n\t\t} else {\n\t\t\tshouldTCPIgnore = true\n\t\t}\n\t}\n\n\tif socksUDPListener != nil {\n\t\tif socksUDPListener.RawAddress() != addr {\n\t\t\tsocksUDPListener.Close()\n\t\t\tsocksUDPListener = nil\n\t\t} else {\n\t\t\tshouldUDPIgnore = true\n\t\t}\n\t}\n\n\tif shouldTCPIgnore && shouldUDPIgnore {\n\t\treturn\n\t}\n\n\tif portIsZero(addr) {\n\t\treturn\n\t}\n\n\ttcpListener, err := socks.New(addr, tunnel)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tudpListener, err := socks.NewUDP(addr, tunnel)\n\tif err != nil {\n\t\ttcpListener.Close()\n\t\treturn\n\t}\n\n\tsocksListener = tcpListener\n\tsocksUDPListener = udpListener\n\n\tlog.Infoln(\"SOCKS proxy listening at: %s\", socksListener.Address())\n}\n\nfunc ReCreateRedir(port int, tunnel C.Tunnel) {\n\tredirMux.Lock()\n\tdefer redirMux.Unlock()\n\n\tvar err error\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tlog.Errorln(\"Start Redir server error: %s\", err.Error())\n\t\t}\n\t}()\n\n\taddr := genAddr(bindAddress, port, allowLan)\n\n\tif redirListener != nil {\n\t\tif redirListener.RawAddress() == addr {\n\t\t\treturn\n\t\t}\n\t\tredirListener.Close()\n\t\tredirListener = nil\n\t}\n\n\tif redirUDPListener != nil {\n\t\tif redirUDPListener.RawAddress() == addr {\n\t\t\treturn\n\t\t}\n\t\tredirUDPListener.Close()\n\t\tredirUDPListener = nil\n\t}\n\n\tif portIsZero(addr) {\n\t\treturn\n\t}\n\n\tredirListener, err = redir.New(addr, tunnel)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tredirUDPListener, err = tproxy.NewUDP(addr, tunnel)\n\tif err != nil {\n\t\tlog.Warnln(\"Failed to start Redir UDP Listener: %s\", err)\n\t}\n\n\tlog.Infoln(\"Redirect proxy listening at: %s\", redirListener.Address())\n}\n\nfunc ReCreateShadowSocks(shadowSocksConfig string, tunnel C.Tunnel) {\n\tssMux.Lock()\n\tdefer ssMux.Unlock()\n\n\tvar err error\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tlog.Errorln(\"Start ShadowSocks server error: %s\", err.Error())\n\t\t}\n\t}()\n\n\tvar ssConfig LC.ShadowsocksServer\n\tif addr, cipher, password, err := embedSS.ParseSSURL(shadowSocksConfig); err == nil {\n\t\tssConfig = LC.ShadowsocksServer{\n\t\t\tEnable:   len(shadowSocksConfig) > 0,\n\t\t\tListen:   addr,\n\t\t\tPassword: password,\n\t\t\tCipher:   cipher,\n\t\t\tUdp:      true,\n\t\t}\n\t}\n\n\tshouldIgnore := false\n\n\tif shadowSocksListener != nil {\n\t\tif shadowSocksListener.Config() != ssConfig.String() {\n\t\t\tshadowSocksListener.Close()\n\t\t\tshadowSocksListener = nil\n\t\t} else {\n\t\t\tshouldIgnore = true\n\t\t}\n\t}\n\n\tif shouldIgnore {\n\t\treturn\n\t}\n\n\tif !ssConfig.Enable {\n\t\treturn\n\t}\n\n\tlistener, err := sing_shadowsocks.New(ssConfig, tunnel)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tshadowSocksListener = listener\n\n\tfor _, addr := range shadowSocksListener.AddrList() {\n\t\tlog.Infoln(\"ShadowSocks proxy listening at: %s\", addr.String())\n\t}\n\treturn\n}\n\nfunc ReCreateVmess(vmessConfig string, tunnel C.Tunnel) {\n\tvmessMux.Lock()\n\tdefer vmessMux.Unlock()\n\n\tvar err error\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tlog.Errorln(\"Start Vmess server error: %s\", err.Error())\n\t\t}\n\t}()\n\n\tvar vsConfig LC.VmessServer\n\tif addr, username, password, err := sing_vmess.ParseVmessURL(vmessConfig); err == nil {\n\t\tvsConfig = LC.VmessServer{\n\t\t\tEnable: len(vmessConfig) > 0,\n\t\t\tListen: addr,\n\t\t\tUsers:  []LC.VmessUser{{Username: username, UUID: password, AlterID: 1}},\n\t\t}\n\t}\n\n\tshouldIgnore := false\n\n\tif vmessListener != nil {\n\t\tif vmessListener.Config() != vsConfig.String() {\n\t\t\tvmessListener.Close()\n\t\t\tvmessListener = nil\n\t\t} else {\n\t\t\tshouldIgnore = true\n\t\t}\n\t}\n\n\tif shouldIgnore {\n\t\treturn\n\t}\n\n\tif !vsConfig.Enable {\n\t\treturn\n\t}\n\n\tlistener, err := sing_vmess.New(vsConfig, tunnel)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tvmessListener = listener\n\n\tfor _, addr := range vmessListener.AddrList() {\n\t\tlog.Infoln(\"Vmess proxy listening at: %s\", addr.String())\n\t}\n\treturn\n}\n\nfunc ReCreateTuic(config LC.TuicServer, tunnel C.Tunnel) {\n\ttuicMux.Lock()\n\tdefer func() {\n\t\tLastTuicConf = config\n\t\ttuicMux.Unlock()\n\t}()\n\tshouldIgnore := false\n\n\tvar err error\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tlog.Errorln(\"Start Tuic server error: %s\", err.Error())\n\t\t}\n\t}()\n\n\tif tuicListener != nil {\n\t\tif tuicListener.Config().String() != config.String() {\n\t\t\ttuicListener.Close()\n\t\t\ttuicListener = nil\n\t\t} else {\n\t\t\tshouldIgnore = true\n\t\t}\n\t}\n\n\tif shouldIgnore {\n\t\treturn\n\t}\n\n\tif !config.Enable {\n\t\treturn\n\t}\n\n\tlistener, err := tuic.New(config, tunnel)\n\tif err != nil {\n\t\treturn\n\t}\n\n\ttuicListener = listener\n\n\tfor _, addr := range tuicListener.AddrList() {\n\t\tlog.Infoln(\"Tuic proxy listening at: %s\", addr.String())\n\t}\n\treturn\n}\n\nfunc ReCreateTProxy(port int, tunnel C.Tunnel) {\n\ttproxyMux.Lock()\n\tdefer tproxyMux.Unlock()\n\n\tvar err error\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tlog.Errorln(\"Start TProxy server error: %s\", err.Error())\n\t\t}\n\t}()\n\n\taddr := genAddr(bindAddress, port, allowLan)\n\n\tif tproxyListener != nil {\n\t\tif tproxyListener.RawAddress() == addr {\n\t\t\treturn\n\t\t}\n\t\ttproxyListener.Close()\n\t\ttproxyListener = nil\n\t}\n\n\tif tproxyUDPListener != nil {\n\t\tif tproxyUDPListener.RawAddress() == addr {\n\t\t\treturn\n\t\t}\n\t\ttproxyUDPListener.Close()\n\t\ttproxyUDPListener = nil\n\t}\n\n\tif portIsZero(addr) {\n\t\treturn\n\t}\n\n\ttproxyListener, err = tproxy.New(addr, tunnel)\n\tif err != nil {\n\t\treturn\n\t}\n\n\ttproxyUDPListener, err = tproxy.NewUDP(addr, tunnel)\n\tif err != nil {\n\t\tlog.Warnln(\"Failed to start TProxy UDP Listener: %s\", err)\n\t}\n\n\tlog.Infoln(\"TProxy server listening at: %s\", tproxyListener.Address())\n}\n\nfunc ReCreateMixed(port int, tunnel C.Tunnel) {\n\tmixedMux.Lock()\n\tdefer mixedMux.Unlock()\n\n\tvar err error\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tlog.Errorln(\"Start Mixed(http+socks) server error: %s\", err.Error())\n\t\t}\n\t}()\n\n\taddr := genAddr(bindAddress, port, allowLan)\n\n\tshouldTCPIgnore := false\n\tshouldUDPIgnore := false\n\n\tif mixedListener != nil {\n\t\tif mixedListener.RawAddress() != addr {\n\t\t\tmixedListener.Close()\n\t\t\tmixedListener = nil\n\t\t} else {\n\t\t\tshouldTCPIgnore = true\n\t\t}\n\t}\n\tif mixedUDPLister != nil {\n\t\tif mixedUDPLister.RawAddress() != addr {\n\t\t\tmixedUDPLister.Close()\n\t\t\tmixedUDPLister = nil\n\t\t} else {\n\t\t\tshouldUDPIgnore = true\n\t\t}\n\t}\n\n\tif shouldTCPIgnore && shouldUDPIgnore {\n\t\treturn\n\t}\n\n\tif portIsZero(addr) {\n\t\treturn\n\t}\n\n\tmixedListener, err = mixed.New(addr, tunnel)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tmixedUDPLister, err = socks.NewUDP(addr, tunnel)\n\tif err != nil {\n\t\tmixedListener.Close()\n\t\treturn\n\t}\n\n\tlog.Infoln(\"Mixed(http+socks) proxy listening at: %s\", mixedListener.Address())\n}\n\nfunc ReCreateTun(tunConf LC.Tun, tunnel C.Tunnel) {\n\ttunConf.Sort()\n\n\ttunMux.Lock()\n\tdefer func() {\n\t\tLastTunConf = tunConf\n\t\ttunMux.Unlock()\n\t}()\n\n\tvar err error\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tlog.Errorln(\"Start TUN listening error: %s\", err.Error())\n\t\t\ttunConf.Enable = false\n\t\t}\n\t}()\n\n\tif tunConf.Equal(LastTunConf) {\n\t\tif tunLister != nil { // some default value in dialer maybe changed when config reload, reset at here\n\t\t\ttunLister.OnReload()\n\t\t}\n\t\treturn\n\t}\n\n\tcloseTunListener()\n\n\tif !tunConf.Enable {\n\t\treturn\n\t}\n\n\tlister, err := sing_tun.New(tunConf, tunnel)\n\tif err != nil {\n\t\treturn\n\t}\n\ttunLister = lister\n\n\tlog.Infoln(\"[TUN] Tun adapter listening at: %s\", tunLister.Address())\n}\n\nfunc PatchTunnel(tunnels []LC.Tunnel, tunnel C.Tunnel) {\n\ttunnelMux.Lock()\n\tdefer tunnelMux.Unlock()\n\n\ttype addrProxy struct {\n\t\tnetwork string\n\t\taddr    string\n\t\ttarget  string\n\t\tproxy   string\n\t}\n\n\ttcpOld := lo.Map(\n\t\tlo.Keys(tunnelTCPListeners),\n\t\tfunc(key string, _ int) addrProxy {\n\t\t\tparts := strings.Split(key, \"/\")\n\t\t\treturn addrProxy{\n\t\t\t\tnetwork: \"tcp\",\n\t\t\t\taddr:    parts[0],\n\t\t\t\ttarget:  parts[1],\n\t\t\t\tproxy:   parts[2],\n\t\t\t}\n\t\t},\n\t)\n\tudpOld := lo.Map(\n\t\tlo.Keys(tunnelUDPListeners),\n\t\tfunc(key string, _ int) addrProxy {\n\t\t\tparts := strings.Split(key, \"/\")\n\t\t\treturn addrProxy{\n\t\t\t\tnetwork: \"udp\",\n\t\t\t\taddr:    parts[0],\n\t\t\t\ttarget:  parts[1],\n\t\t\t\tproxy:   parts[2],\n\t\t\t}\n\t\t},\n\t)\n\toldElm := lo.Union(tcpOld, udpOld)\n\n\tnewElm := lo.FlatMap(\n\t\ttunnels,\n\t\tfunc(tunnel LC.Tunnel, _ int) []addrProxy {\n\t\t\treturn lo.Map(\n\t\t\t\ttunnel.Network,\n\t\t\t\tfunc(network string, _ int) addrProxy {\n\t\t\t\t\treturn addrProxy{\n\t\t\t\t\t\tnetwork: network,\n\t\t\t\t\t\taddr:    tunnel.Address,\n\t\t\t\t\t\ttarget:  tunnel.Target,\n\t\t\t\t\t\tproxy:   tunnel.Proxy,\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t)\n\t\t},\n\t)\n\n\tneedClose, needCreate := lo.Difference(oldElm, newElm)\n\n\tfor _, elm := range needClose {\n\t\tkey := fmt.Sprintf(\"%s/%s/%s\", elm.addr, elm.target, elm.proxy)\n\t\tif elm.network == \"tcp\" {\n\t\t\ttunnelTCPListeners[key].Close()\n\t\t\tdelete(tunnelTCPListeners, key)\n\t\t} else {\n\t\t\ttunnelUDPListeners[key].Close()\n\t\t\tdelete(tunnelUDPListeners, key)\n\t\t}\n\t}\n\n\tfor _, elm := range needCreate {\n\t\tkey := fmt.Sprintf(\"%s/%s/%s\", elm.addr, elm.target, elm.proxy)\n\t\tif elm.network == \"tcp\" {\n\t\t\tl, err := LT.New(elm.addr, elm.target, elm.proxy, tunnel)\n\t\t\tif err != nil {\n\t\t\t\tlog.Errorln(\"Start tunnel %s error: %s\", elm.target, err.Error())\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ttunnelTCPListeners[key] = l\n\t\t\tlog.Infoln(\"Tunnel(tcp/%s) proxy %s listening at: %s\", elm.target, elm.proxy, tunnelTCPListeners[key].Address())\n\t\t} else {\n\t\t\tl, err := LT.NewUDP(elm.addr, elm.target, elm.proxy, tunnel)\n\t\t\tif err != nil {\n\t\t\t\tlog.Errorln(\"Start tunnel %s error: %s\", elm.target, err.Error())\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ttunnelUDPListeners[key] = l\n\t\t\tlog.Infoln(\"Tunnel(udp/%s) proxy %s listening at: %s\", elm.target, elm.proxy, tunnelUDPListeners[key].Address())\n\t\t}\n\t}\n}\n\nfunc PatchInboundListeners(newListenerMap map[string]C.InboundListener, tunnel C.Tunnel, dropOld bool) {\n\tinboundMux.Lock()\n\tdefer inboundMux.Unlock()\n\n\tfor name, newListener := range newListenerMap {\n\t\tif oldListener, ok := inboundListeners[name]; ok {\n\t\t\tif !oldListener.Config().Equal(newListener.Config()) {\n\t\t\t\t_ = oldListener.Close()\n\t\t\t} else {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\t\tif err := newListener.Listen(tunnel); err != nil {\n\t\t\tlog.Errorln(\"Listener %s listen err: %s\", name, err.Error())\n\t\t\tcontinue\n\t\t}\n\t\tinboundListeners[name] = newListener\n\t}\n\n\tif dropOld {\n\t\tfor name, oldListener := range inboundListeners {\n\t\t\tif _, ok := newListenerMap[name]; !ok {\n\t\t\t\t_ = oldListener.Close()\n\t\t\t\tdelete(inboundListeners, name)\n\t\t\t}\n\t\t}\n\t}\n}\n\n// GetPorts return the ports of proxy servers\nfunc GetPorts() *Ports {\n\tports := &Ports{}\n\n\tif httpListener != nil {\n\t\t_, portStr, _ := net.SplitHostPort(httpListener.Address())\n\t\tport, _ := strconv.Atoi(portStr)\n\t\tports.Port = port\n\t}\n\n\tif socksListener != nil {\n\t\t_, portStr, _ := net.SplitHostPort(socksListener.Address())\n\t\tport, _ := strconv.Atoi(portStr)\n\t\tports.SocksPort = port\n\t}\n\n\tif redirListener != nil {\n\t\t_, portStr, _ := net.SplitHostPort(redirListener.Address())\n\t\tport, _ := strconv.Atoi(portStr)\n\t\tports.RedirPort = port\n\t}\n\n\tif tproxyListener != nil {\n\t\t_, portStr, _ := net.SplitHostPort(tproxyListener.Address())\n\t\tport, _ := strconv.Atoi(portStr)\n\t\tports.TProxyPort = port\n\t}\n\n\tif mixedListener != nil {\n\t\t_, portStr, _ := net.SplitHostPort(mixedListener.Address())\n\t\tport, _ := strconv.Atoi(portStr)\n\t\tports.MixedPort = port\n\t}\n\n\tif shadowSocksListener != nil {\n\t\tports.ShadowSocksConfig = shadowSocksListener.Config()\n\t}\n\n\tif vmessListener != nil {\n\t\tports.VmessConfig = vmessListener.Config()\n\t}\n\n\treturn ports\n}\n\nfunc portIsZero(addr string) bool {\n\t_, port, err := net.SplitHostPort(addr)\n\tif port == \"0\" || port == \"\" || err != nil {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc genAddr(host string, port int, allowLan bool) string {\n\tif allowLan {\n\t\tif host == \"*\" {\n\t\t\treturn fmt.Sprintf(\":%d\", port)\n\t\t}\n\t\treturn fmt.Sprintf(\"%s:%d\", host, port)\n\t}\n\n\treturn fmt.Sprintf(\"127.0.0.1:%d\", port)\n}\n\nfunc closeTunListener() {\n\tif tunLister != nil {\n\t\ttunLister.Close()\n\t\ttunLister = nil\n\t}\n}\n\nfunc Cleanup() {\n\tcloseTunListener()\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/mieru/server.go",
    "content": "package mieru\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\t\"net/netip\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n\n\tmierucommon \"github.com/enfein/mieru/v3/apis/common\"\n\tmieruconstant \"github.com/enfein/mieru/v3/apis/constant\"\n\tmierumodel \"github.com/enfein/mieru/v3/apis/model\"\n)\n\nfunc Handle(conn net.Conn, tunnel C.Tunnel, request *mierumodel.Request, additions ...inbound.Addition) {\n\t// Return a fake response to the client.\n\tresp := &mierumodel.Response{\n\t\tReply: mieruconstant.Socks5ReplySuccess,\n\t\tBindAddr: mierumodel.AddrSpec{\n\t\t\tIP:   net.IPv4zero,\n\t\t\tPort: 0,\n\t\t},\n\t}\n\tif err := resp.WriteToSocks5(conn); err != nil {\n\t\tconn.Close()\n\t\treturn\n\t}\n\n\t// Handle the connection with tunnel.\n\tswitch request.Command {\n\tcase mieruconstant.Socks5ConnectCmd: // TCP\n\t\tmetadata := &C.Metadata{\n\t\t\tNetWork: C.TCP,\n\t\t\tType:    C.MIERU,\n\t\t\tDstPort: uint16(request.DstAddr.Port),\n\t\t}\n\t\tif request.DstAddr.FQDN != \"\" {\n\t\t\tmetadata.Host = request.DstAddr.FQDN\n\t\t} else if request.DstAddr.IP != nil {\n\t\t\tmetadata.DstIP, _ = netip.AddrFromSlice(request.DstAddr.IP)\n\t\t\tmetadata.DstIP = metadata.DstIP.Unmap()\n\t\t}\n\t\tinbound.ApplyAdditions(\n\t\t\tmetadata,\n\t\t\tinbound.WithInName(conn.(mierucommon.UserContext).UserName()),\n\t\t\tinbound.WithSrcAddr(conn.RemoteAddr()),\n\t\t\tinbound.WithInAddr(conn.LocalAddr()),\n\t\t)\n\t\tinbound.ApplyAdditions(metadata, additions...)\n\t\ttunnel.HandleTCPConn(conn, metadata)\n\tcase mieruconstant.Socks5UDPAssociateCmd: // UDP\n\t\tpc := mierucommon.NewPacketOverStreamTunnel(conn)\n\t\tep := N.NewEnhancePacketConn(pc)\n\t\tfor {\n\t\t\tdata, put, addr, err := ep.WaitReadFrom()\n\t\t\tif err != nil {\n\t\t\t\tif put != nil {\n\t\t\t\t\t// Unresolved UDP packet, return buffer to the pool.\n\t\t\t\t\tput()\n\t\t\t\t}\n\t\t\t\t// mieru returns EOF or ErrUnexpectedEOF when a session is closed.\n\t\t\t\tif errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) || errors.Is(err, io.ErrClosedPipe) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\ttarget, payload, err := socks5.DecodeUDPPacket(data)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tpacket := &packet{\n\t\t\t\tpc:      ep,\n\t\t\t\taddr:    addr,\n\t\t\t\tpayload: payload,\n\t\t\t\tput:     put,\n\t\t\t}\n\t\t\ttunnel.HandleUDPPacket(inbound.NewPacket(target, packet, C.MIERU, additions...))\n\t\t}\n\t}\n}\n\ntype packet struct {\n\tpc      net.PacketConn\n\taddr    net.Addr // source (i.e. remote) IP & Port of the packet\n\tpayload []byte\n\tput     func()\n}\n\nvar _ C.UDPPacket = (*packet)(nil)\nvar _ C.UDPPacketInAddr = (*packet)(nil)\n\nfunc (c *packet) Data() []byte {\n\treturn c.payload\n}\n\nfunc (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {\n\tpacket, err := socks5.EncodeUDPPacket(socks5.ParseAddrToSocksAddr(addr), b)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn c.pc.WriteTo(packet, c.addr)\n}\n\nfunc (c *packet) Drop() {\n\tif c.put != nil {\n\t\tc.put()\n\t\tc.put = nil\n\t}\n\tc.payload = nil\n}\n\nfunc (c *packet) LocalAddr() net.Addr {\n\treturn c.addr\n}\n\nfunc (c *packet) InAddr() net.Addr {\n\treturn c.pc.LocalAddr()\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/mixed/mixed.go",
    "content": "package mixed\n\nimport (\n\t\"errors\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/component/auth\"\n\t\"github.com/metacubex/mihomo/component/ca\"\n\t\"github.com/metacubex/mihomo/component/ech\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tauthStore \"github.com/metacubex/mihomo/listener/auth\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/http\"\n\t\"github.com/metacubex/mihomo/listener/reality\"\n\t\"github.com/metacubex/mihomo/listener/socks\"\n\t\"github.com/metacubex/mihomo/ntp\"\n\t\"github.com/metacubex/mihomo/transport/socks4\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n\n\t\"github.com/metacubex/tls\"\n)\n\ntype Listener struct {\n\tlistener net.Listener\n\taddr     string\n\tclosed   bool\n}\n\n// RawAddress implements C.Listener\nfunc (l *Listener) RawAddress() string {\n\treturn l.addr\n}\n\n// Address implements C.Listener\nfunc (l *Listener) Address() string {\n\treturn l.listener.Addr().String()\n}\n\n// Close implements C.Listener\nfunc (l *Listener) Close() error {\n\tl.closed = true\n\treturn l.listener.Close()\n}\n\nfunc New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {\n\treturn NewWithConfig(LC.AuthServer{Enable: true, Listen: addr, AuthStore: authStore.Default}, tunnel, additions...)\n}\n\nfunc NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {\n\tisDefault := false\n\tif len(additions) == 0 {\n\t\tisDefault = true\n\t\tadditions = []inbound.Addition{\n\t\t\tinbound.WithInName(\"DEFAULT-MIXED\"),\n\t\t\tinbound.WithSpecialRules(\"\"),\n\t\t}\n\t}\n\n\tl, err := inbound.Listen(\"tcp\", config.Listen)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttlsConfig := &tls.Config{Time: ntp.Now}\n\tvar realityBuilder *reality.Builder\n\n\tif config.Certificate != \"\" && config.PrivateKey != \"\" {\n\t\tcertLoader, err := ca.NewTLSKeyPairLoader(config.Certificate, config.PrivateKey)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttlsConfig.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) {\n\t\t\treturn certLoader()\n\t\t}\n\n\t\tif config.EchKey != \"\" {\n\t\t\terr = ech.LoadECHKey(config.EchKey, tlsConfig)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\ttlsConfig.ClientAuth = ca.ClientAuthTypeFromString(config.ClientAuthType)\n\tif len(config.ClientAuthCert) > 0 {\n\t\tif tlsConfig.ClientAuth == tls.NoClientCert {\n\t\t\ttlsConfig.ClientAuth = tls.RequireAndVerifyClientCert\n\t\t}\n\t}\n\tif tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert {\n\t\tpool, err := ca.LoadCertificates(config.ClientAuthCert)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttlsConfig.ClientCAs = pool\n\t}\n\tif config.RealityConfig.PrivateKey != \"\" {\n\t\tif tlsConfig.GetCertificate != nil {\n\t\t\treturn nil, errors.New(\"certificate is unavailable in reality\")\n\t\t}\n\t\tif tlsConfig.ClientAuth != tls.NoClientCert {\n\t\t\treturn nil, errors.New(\"client-auth is unavailable in reality\")\n\t\t}\n\t\trealityBuilder, err = config.RealityConfig.Build(tunnel)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tif realityBuilder != nil {\n\t\tl = realityBuilder.NewListener(l)\n\t} else if tlsConfig.GetCertificate != nil {\n\t\tl = tls.NewListener(l, tlsConfig)\n\t}\n\n\tml := &Listener{\n\t\tlistener: l,\n\t\taddr:     config.Listen,\n\t}\n\tgo func() {\n\t\tfor {\n\t\t\tc, err := ml.listener.Accept()\n\t\t\tif err != nil {\n\t\t\t\tif ml.closed {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tstore := config.AuthStore\n\t\t\tif isDefault || store == authStore.Default { // only apply on default listener\n\t\t\t\tif !inbound.IsRemoteAddrDisAllowed(c.RemoteAddr()) {\n\t\t\t\t\t_ = c.Close()\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif inbound.SkipAuthRemoteAddr(c.RemoteAddr()) {\n\t\t\t\t\tstore = authStore.Nil\n\t\t\t\t}\n\t\t\t}\n\t\t\tgo handleConn(c, tunnel, store, additions...)\n\t\t}\n\t}()\n\n\treturn ml, nil\n}\n\nfunc handleConn(conn net.Conn, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) {\n\tbufConn := N.NewBufferedConn(conn)\n\thead, err := bufConn.Peek(1)\n\tif err != nil {\n\t\tconn.Close()\n\t\treturn\n\t}\n\n\tswitch head[0] {\n\tcase socks4.Version:\n\t\tsocks.HandleSocks4(bufConn, tunnel, store, additions...)\n\tcase socks5.Version:\n\t\tsocks.HandleSocks5(bufConn, tunnel, store, additions...)\n\tdefault:\n\t\thttp.HandleConn(bufConn, tunnel, store, additions...)\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/parse.go",
    "content": "package listener\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/metacubex/mihomo/common/structure\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tIN \"github.com/metacubex/mihomo/listener/inbound\"\n)\n\nfunc ParseListener(mapping map[string]any) (C.InboundListener, error) {\n\tdecoder := structure.NewDecoder(structure.Option{TagName: \"inbound\", WeaklyTypedInput: true, KeyReplacer: structure.DefaultKeyReplacer})\n\tproxyType, existType := mapping[\"type\"].(string)\n\tif !existType {\n\t\treturn nil, fmt.Errorf(\"missing type\")\n\t}\n\n\tvar (\n\t\tlistener C.InboundListener\n\t\terr      error\n\t)\n\tswitch proxyType {\n\tcase \"socks\":\n\t\tsocksOption := &IN.SocksOption{UDP: true}\n\t\terr = decoder.Decode(mapping, socksOption)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlistener, err = IN.NewSocks(socksOption)\n\tcase \"http\":\n\t\thttpOption := &IN.HTTPOption{}\n\t\terr = decoder.Decode(mapping, httpOption)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlistener, err = IN.NewHTTP(httpOption)\n\tcase \"tproxy\":\n\t\ttproxyOption := &IN.TProxyOption{UDP: true}\n\t\terr = decoder.Decode(mapping, tproxyOption)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlistener, err = IN.NewTProxy(tproxyOption)\n\tcase \"redir\":\n\t\tredirOption := &IN.RedirOption{}\n\t\terr = decoder.Decode(mapping, redirOption)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlistener, err = IN.NewRedir(redirOption)\n\tcase \"mixed\":\n\t\tmixedOption := &IN.MixedOption{UDP: true}\n\t\terr = decoder.Decode(mapping, mixedOption)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlistener, err = IN.NewMixed(mixedOption)\n\tcase \"tunnel\":\n\t\ttunnelOption := &IN.TunnelOption{}\n\t\terr = decoder.Decode(mapping, tunnelOption)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlistener, err = IN.NewTunnel(tunnelOption)\n\tcase \"tun\":\n\t\ttunOption := &IN.TunOption{\n\t\t\tStack:     C.TunGvisor,\n\t\t\tDNSHijack: []string{\"0.0.0.0:53\"}, // default hijack all dns query\n\t\t}\n\t\terr = decoder.Decode(mapping, tunOption)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlistener, err = IN.NewTun(tunOption)\n\tcase \"shadowsocks\":\n\t\tshadowsocksOption := &IN.ShadowSocksOption{UDP: true}\n\t\terr = decoder.Decode(mapping, shadowsocksOption)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlistener, err = IN.NewShadowSocks(shadowsocksOption)\n\tcase \"vmess\":\n\t\tvmessOption := &IN.VmessOption{}\n\t\terr = decoder.Decode(mapping, vmessOption)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlistener, err = IN.NewVmess(vmessOption)\n\tcase \"vless\":\n\t\tvlessOption := &IN.VlessOption{}\n\t\terr = decoder.Decode(mapping, vlessOption)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlistener, err = IN.NewVless(vlessOption)\n\tcase \"trojan\":\n\t\ttrojanOption := &IN.TrojanOption{}\n\t\terr = decoder.Decode(mapping, trojanOption)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlistener, err = IN.NewTrojan(trojanOption)\n\tcase \"hysteria2\":\n\t\thysteria2Option := &IN.Hysteria2Option{}\n\t\terr = decoder.Decode(mapping, hysteria2Option)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlistener, err = IN.NewHysteria2(hysteria2Option)\n\tcase \"tuic\":\n\t\ttuicOption := &IN.TuicOption{\n\t\t\tMaxIdleTime:           15000,\n\t\t\tAuthenticationTimeout: 1000,\n\t\t\tALPN:                  []string{\"h3\"},\n\t\t\tMaxUdpRelayPacketSize: 1500,\n\t\t\tCongestionController:  \"bbr\",\n\t\t}\n\t\terr = decoder.Decode(mapping, tuicOption)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlistener, err = IN.NewTuic(tuicOption)\n\tcase \"anytls\":\n\t\tanytlsOption := &IN.AnyTLSOption{}\n\t\terr = decoder.Decode(mapping, anytlsOption)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlistener, err = IN.NewAnyTLS(anytlsOption)\n\tcase \"mieru\":\n\t\tmieruOption := &IN.MieruOption{}\n\t\terr = decoder.Decode(mapping, mieruOption)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlistener, err = IN.NewMieru(mieruOption)\n\tcase \"sudoku\":\n\t\tsudokuOption := &IN.SudokuOption{}\n\t\terr = decoder.Decode(mapping, sudokuOption)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlistener, err = IN.NewSudoku(sudokuOption)\n\tcase \"trusttunnel\":\n\t\ttrusttunnelOption := &IN.TrustTunnelOption{}\n\t\terr = decoder.Decode(mapping, trusttunnelOption)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlistener, err = IN.NewTrustTunnel(trusttunnelOption)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unsupport proxy type: %s\", proxyType)\n\t}\n\treturn listener, err\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/patch.go",
    "content": "package listener\n\nfunc StopListener() {\n\n\tif socksListener != nil {\n\t\t_ = socksListener.Close()\n\t\tsocksListener = nil\n\t}\n\n\tif socksUDPListener != nil {\n\t\t_ = socksUDPListener.Close()\n\t\tsocksUDPListener = nil\n\t}\n\n\tif httpListener != nil {\n\t\t_ = httpListener.Close()\n\t\thttpListener = nil\n\t}\n\n\tif redirListener != nil {\n\t\t_ = redirListener.Close()\n\t\tredirListener = nil\n\t}\n\n\tif redirUDPListener != nil {\n\t\t_ = redirUDPListener.Close()\n\t\tredirUDPListener = nil\n\t}\n\n\tif tproxyListener != nil {\n\t\t_ = tproxyListener.Close()\n\t\ttproxyListener = nil\n\t}\n\n\tif tproxyUDPListener != nil {\n\t\t_ = tproxyUDPListener.Close()\n\t\ttproxyUDPListener = nil\n\t}\n\n\tif mixedListener != nil {\n\t\t_ = mixedListener.Close()\n\t\tmixedListener = nil\n\t}\n\n\tif mixedUDPLister != nil {\n\t\t_ = mixedUDPLister.Close()\n\t\tmixedUDPLister = nil\n\t}\n\n\tif tunLister != nil {\n\t\t_ = tunLister.Close()\n\t\ttunLister = nil\n\t}\n\n\tif shadowSocksListener != nil {\n\t\t_ = shadowSocksListener.Close()\n\t\tshadowSocksListener = nil\n\t}\n\n\tif shadowSocksListener != nil {\n\t\t_ = shadowSocksListener.Close()\n\t\tshadowSocksListener = nil\n\t}\n\n\tif vmessListener != nil {\n\t\t_ = vmessListener.Close()\n\t\tvmessListener = nil\n\t}\n\n\tif tuicListener != nil {\n\t\t_ = tuicListener.Close()\n\t\ttuicListener = nil\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/reality/reality.go",
    "content": "package reality\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"runtime/debug\"\n\t\"time\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/listener/inner\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/ntp\"\n\n\tutls \"github.com/metacubex/utls\"\n)\n\ntype Conn = utls.Conn\ntype LimitFallback = utls.RealityLimitFallback\n\ntype Config struct {\n\tDest              string\n\tPrivateKey        string\n\tShortID           []string\n\tServerNames       []string\n\tMaxTimeDifference int\n\tProxy             string\n\n\tLimitFallbackUpload   LimitFallback\n\tLimitFallbackDownload LimitFallback\n}\n\nfunc (c Config) Build(tunnel C.Tunnel) (*Builder, error) {\n\trealityConfig := &utls.RealityConfig{}\n\trealityConfig.SessionTicketsDisabled = true\n\trealityConfig.Type = \"tcp\"\n\trealityConfig.Dest = c.Dest\n\trealityConfig.Time = ntp.Now\n\trealityConfig.ServerNames = make(map[string]bool)\n\trealityConfig.Log = log.Debugln\n\tfor _, it := range c.ServerNames {\n\t\trealityConfig.ServerNames[it] = true\n\t}\n\tprivateKey, err := base64.RawURLEncoding.DecodeString(c.PrivateKey)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"decode private key: %w\", err)\n\t}\n\tif len(privateKey) != 32 {\n\t\treturn nil, errors.New(\"invalid private key\")\n\t}\n\trealityConfig.PrivateKey = privateKey\n\n\trealityConfig.MaxTimeDiff = time.Duration(c.MaxTimeDifference) * time.Microsecond\n\n\trealityConfig.ShortIds = make(map[[8]byte]bool)\n\tfor i, shortIDString := range c.ShortID {\n\t\tvar shortID [8]byte\n\t\tdecodedLen := hex.DecodedLen(len(shortIDString))\n\t\tif decodedLen > 8 {\n\t\t\treturn nil, fmt.Errorf(\"invalid short_id[%d]: %s\", i, shortIDString)\n\t\t}\n\t\tdecodedLen, err = hex.Decode(shortID[:], []byte(shortIDString))\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"decode short_id[%d] '%s': %w\", i, shortIDString, err)\n\t\t}\n\t\tif decodedLen > 8 {\n\t\t\treturn nil, fmt.Errorf(\"invalid short_id[%d]: %s\", i, shortIDString)\n\t\t}\n\t\trealityConfig.ShortIds[shortID] = true\n\t}\n\n\trealityConfig.DialContext = func(ctx context.Context, network, address string) (net.Conn, error) {\n\t\treturn inner.HandleTcp(tunnel, address, c.Proxy)\n\t}\n\n\trealityConfig.LimitFallbackUpload = c.LimitFallbackUpload\n\trealityConfig.LimitFallbackDownload = c.LimitFallbackDownload\n\n\treturn &Builder{realityConfig}, nil\n}\n\ntype Builder struct {\n\trealityConfig *utls.RealityConfig\n}\n\nfunc (b Builder) NewListener(l net.Listener) net.Listener {\n\treturn N.NewHandleContextListener(context.Background(), l, func(ctx context.Context, conn net.Conn) (net.Conn, error) {\n\t\tc, err := utls.RealityServer(ctx, conn, b.realityConfig)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\t// Due to low implementation quality, the reality server intercepted half-close and caused memory leaks.\n\t\t// We fixed it by calling Close() directly.\n\t\treturn realityConnWrapper{c}, nil\n\t}, func(a any) {\n\t\tstack := debug.Stack()\n\t\tlog.Errorln(\"reality server panic: %s\\n%s\", a, stack)\n\t})\n}\n\ntype realityConnWrapper struct {\n\t*utls.Conn\n}\n\nfunc (c realityConnWrapper) Upstream() any {\n\treturn c.Conn\n}\n\nfunc (c realityConnWrapper) CloseWrite() error {\n\treturn c.Close()\n}\n\nfunc (c realityConnWrapper) ReaderReplaceable() bool {\n\treturn true\n}\n\nfunc (c realityConnWrapper) WriterReplaceable() bool {\n\treturn true\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/redir/tcp.go",
    "content": "package redir\n\nimport (\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\t\"github.com/metacubex/mihomo/component/keepalive\"\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\ntype Listener struct {\n\tlistener net.Listener\n\taddr     string\n\tclosed   bool\n}\n\n// RawAddress implements C.Listener\nfunc (l *Listener) RawAddress() string {\n\treturn l.addr\n}\n\n// Address implements C.Listener\nfunc (l *Listener) Address() string {\n\treturn l.listener.Addr().String()\n}\n\n// Close implements C.Listener\nfunc (l *Listener) Close() error {\n\tl.closed = true\n\treturn l.listener.Close()\n}\n\nfunc New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {\n\tif len(additions) == 0 {\n\t\tadditions = []inbound.Addition{\n\t\t\tinbound.WithInName(\"DEFAULT-REDIR\"),\n\t\t\tinbound.WithSpecialRules(\"\"),\n\t\t}\n\t}\n\n\tl, err := net.Listen(\"tcp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\trl := &Listener{\n\t\tlistener: l,\n\t\taddr:     addr,\n\t}\n\n\tgo func() {\n\t\tfor {\n\t\t\tc, err := l.Accept()\n\t\t\tif err != nil {\n\t\t\t\tif rl.closed {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tgo handleRedir(c, tunnel, additions...)\n\t\t}\n\t}()\n\n\treturn rl, nil\n}\n\nfunc handleRedir(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) {\n\ttarget, err := parserPacket(conn)\n\tif err != nil {\n\t\tconn.Close()\n\t\treturn\n\t}\n\tkeepalive.TCPKeepAlive(conn)\n\ttunnel.HandleTCPConn(inbound.NewSocket(target, conn, C.REDIR, additions...))\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/redir/tcp_darwin.go",
    "content": "package redir\n\nimport (\n\t\"net\"\n\t\"syscall\"\n\t\"unsafe\"\n\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n)\n\nfunc parserPacket(c net.Conn) (socks5.Addr, error) {\n\tconst (\n\t\tPfInout     = 0\n\t\tPfIn        = 1\n\t\tPfOut       = 2\n\t\tIOCOut      = 0x40000000\n\t\tIOCIn       = 0x80000000\n\t\tIOCInOut    = IOCIn | IOCOut\n\t\tIOCPARMMask = 0x1FFF\n\t\tLEN         = 4*16 + 4*4 + 4*1\n\t\t// #define\t_IOC(inout,group,num,len) (inout | ((len & IOCPARMMask) << 16) | ((group) << 8) | (num))\n\t\t// #define\t_IOWR(g,n,t)\t_IOC(IOCInOut,\t(g), (n), sizeof(t))\n\t\t// #define DIOCNATLOOK\t\t_IOWR('D', 23, struct pfioc_natlook)\n\t\tDIOCNATLOOK = IOCInOut | ((LEN & IOCPARMMask) << 16) | ('D' << 8) | 23\n\t)\n\n\tfd, err := syscall.Open(\"/dev/pf\", 0, syscall.O_RDONLY)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdefer syscall.Close(fd)\n\n\tnl := struct { // struct pfioc_natlook\n\t\tsaddr, daddr, rsaddr, rdaddr       [16]byte\n\t\tsxport, dxport, rsxport, rdxport   [4]byte\n\t\taf, proto, protoVariant, direction uint8\n\t}{\n\t\taf:        syscall.AF_INET,\n\t\tproto:     syscall.IPPROTO_TCP,\n\t\tdirection: PfOut,\n\t}\n\tsaddr := c.RemoteAddr().(*net.TCPAddr)\n\tdaddr := c.LocalAddr().(*net.TCPAddr)\n\tcopy(nl.saddr[:], saddr.IP)\n\tcopy(nl.daddr[:], daddr.IP)\n\tnl.sxport[0], nl.sxport[1] = byte(saddr.Port>>8), byte(saddr.Port)\n\tnl.dxport[0], nl.dxport[1] = byte(daddr.Port>>8), byte(daddr.Port)\n\n\tif _, _, errno := syscall.Syscall(syscall.SYS_IOCTL, uintptr(fd), DIOCNATLOOK, uintptr(unsafe.Pointer(&nl))); errno != 0 {\n\t\treturn nil, errno\n\t}\n\n\taddr := make([]byte, 1+net.IPv4len+2)\n\taddr[0] = socks5.AtypIPv4\n\tcopy(addr[1:1+net.IPv4len], nl.rdaddr[:4])\n\tcopy(addr[1+net.IPv4len:], nl.rdxport[:2])\n\treturn addr, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/redir/tcp_freebsd.go",
    "content": "package redir\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"net\"\n\t\"net/netip\"\n\t\"syscall\"\n\t\"unsafe\"\n\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nconst (\n\tSO_ORIGINAL_DST      = 80 // from linux/include/uapi/linux/netfilter_ipv4.h\n\tIP6T_SO_ORIGINAL_DST = 80 // from linux/include/uapi/linux/netfilter_ipv6/ip6_tables.h\n)\n\nfunc parserPacket(conn net.Conn) (socks5.Addr, error) {\n\tc, ok := conn.(*net.TCPConn)\n\tif !ok {\n\t\treturn nil, errors.New(\"only work with TCP connection\")\n\t}\n\n\trc, err := c.SyscallConn()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar addr netip.AddrPort\n\n\trc.Control(func(fd uintptr) {\n\t\tif ip4 := c.LocalAddr().(*net.TCPAddr).IP.To4(); ip4 != nil {\n\t\t\taddr, err = getorigdst(fd)\n\t\t} else {\n\t\t\taddr, err = getorigdst6(fd)\n\t\t}\n\t})\n\n\treturn socks5.AddrFromStdAddrPort(addr), err\n}\n\n// Call getorigdst() from linux/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c\nfunc getorigdst(fd uintptr) (netip.AddrPort, error) {\n\taddr := unix.RawSockaddrInet4{}\n\tsize := uint32(unsafe.Sizeof(addr))\n\t_, _, err := syscall.Syscall6(syscall.SYS_GETSOCKOPT, fd, syscall.IPPROTO_IP, SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&addr)), uintptr(unsafe.Pointer(&size)), 0)\n\tif err != 0 {\n\t\treturn netip.AddrPort{}, err\n\t}\n\tport := binary.BigEndian.Uint16((*(*[2]byte)(unsafe.Pointer(&addr.Port)))[:])\n\treturn netip.AddrPortFrom(netip.AddrFrom4(addr.Addr), port), nil\n}\n\nfunc getorigdst6(fd uintptr) (netip.AddrPort, error) {\n\taddr := unix.RawSockaddrInet6{}\n\tsize := uint32(unsafe.Sizeof(addr))\n\t_, _, err := syscall.Syscall6(syscall.SYS_GETSOCKOPT, fd, syscall.IPPROTO_IPV6, IP6T_SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&addr)), uintptr(unsafe.Pointer(&size)), 0)\n\tif err != 0 {\n\t\treturn netip.AddrPort{}, err\n\t}\n\tport := binary.BigEndian.Uint16((*(*[2]byte)(unsafe.Pointer(&addr.Port)))[:])\n\treturn netip.AddrPortFrom(netip.AddrFrom16(addr.Addr), port), nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/redir/tcp_linux.go",
    "content": "package redir\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"net\"\n\t\"net/netip\"\n\t\"syscall\"\n\t\"unsafe\"\n\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nconst (\n\tSO_ORIGINAL_DST      = 80 // from linux/include/uapi/linux/netfilter_ipv4.h\n\tIP6T_SO_ORIGINAL_DST = 80 // from linux/include/uapi/linux/netfilter_ipv6/ip6_tables.h\n)\n\nfunc parserPacket(conn net.Conn) (socks5.Addr, error) {\n\tc, ok := conn.(*net.TCPConn)\n\tif !ok {\n\t\treturn nil, errors.New(\"only work with TCP connection\")\n\t}\n\n\trc, err := c.SyscallConn()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar addr netip.AddrPort\n\n\trc.Control(func(fd uintptr) {\n\t\tif ip4 := c.LocalAddr().(*net.TCPAddr).IP.To4(); ip4 != nil {\n\t\t\taddr, err = getorigdst(fd)\n\t\t} else {\n\t\t\taddr, err = getorigdst6(fd)\n\t\t}\n\t})\n\n\treturn socks5.AddrFromStdAddrPort(addr), err\n}\n\n// Call getorigdst() from linux/net/ipv4/netfilter/nf_conntrack_l3proto_ipv4.c\nfunc getorigdst(fd uintptr) (netip.AddrPort, error) {\n\taddr := unix.RawSockaddrInet4{}\n\tsize := uint32(unsafe.Sizeof(addr))\n\tif err := socketcall(GETSOCKOPT, fd, syscall.IPPROTO_IP, SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&addr)), uintptr(unsafe.Pointer(&size)), 0); err != nil {\n\t\treturn netip.AddrPort{}, err\n\t}\n\tport := binary.BigEndian.Uint16((*(*[2]byte)(unsafe.Pointer(&addr.Port)))[:])\n\treturn netip.AddrPortFrom(netip.AddrFrom4(addr.Addr), port), nil\n}\n\nfunc getorigdst6(fd uintptr) (netip.AddrPort, error) {\n\taddr := unix.RawSockaddrInet6{}\n\tsize := uint32(unsafe.Sizeof(addr))\n\tif err := socketcall(GETSOCKOPT, fd, syscall.IPPROTO_IPV6, IP6T_SO_ORIGINAL_DST, uintptr(unsafe.Pointer(&addr)), uintptr(unsafe.Pointer(&size)), 0); err != nil {\n\t\treturn netip.AddrPort{}, err\n\t}\n\tport := binary.BigEndian.Uint16((*(*[2]byte)(unsafe.Pointer(&addr.Port)))[:])\n\treturn netip.AddrPortFrom(netip.AddrFrom16(addr.Addr), port), nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/redir/tcp_linux_386.go",
    "content": "package redir\n\nimport (\n\t\"syscall\"\n\t\"unsafe\"\n)\n\nconst GETSOCKOPT = 15 // https://golang.org/src/syscall/syscall_linux_386.go#L183\n\nfunc socketcall(call, a0, a1, a2, a3, a4, a5 uintptr) error {\n\tvar a [6]uintptr\n\ta[0], a[1], a[2], a[3], a[4], a[5] = a0, a1, a2, a3, a4, a5\n\tif _, _, errno := syscall.Syscall6(syscall.SYS_SOCKETCALL, call, uintptr(unsafe.Pointer(&a)), 0, 0, 0, 0); errno != 0 {\n\t\treturn errno\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/redir/tcp_linux_other.go",
    "content": "//go:build linux && !386\n\npackage redir\n\nimport \"syscall\"\n\nconst GETSOCKOPT = syscall.SYS_GETSOCKOPT\n\nfunc socketcall(call, a0, a1, a2, a3, a4, a5 uintptr) error {\n\tif _, _, errno := syscall.Syscall6(call, a0, a1, a2, a3, a4, a5); errno != 0 {\n\t\treturn errno\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/redir/tcp_other.go",
    "content": "//go:build !darwin && !linux && !freebsd\n\npackage redir\n\nimport (\n\t\"errors\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n)\n\nfunc parserPacket(conn net.Conn) (socks5.Addr, error) {\n\treturn nil, errors.New(\"system not support yet\")\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/shadowsocks/tcp.go",
    "content": "package shadowsocks\n\nimport (\n\t\"net\"\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/sing\"\n\t\"github.com/metacubex/mihomo/transport/shadowsocks/core\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n)\n\ntype Listener struct {\n\tclosed       bool\n\tconfig       LC.ShadowsocksServer\n\tlisteners    []net.Listener\n\tudpListeners []*UDPListener\n\tpickCipher   core.Cipher\n\thandler      *sing.ListenerHandler\n}\n\nvar _listener *Listener\n\nfunc New(config LC.ShadowsocksServer, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {\n\tpickCipher, err := core.PickCipher(config.Cipher, nil, config.Password)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\th, err := sing.NewListenerHandler(sing.ListenerConfig{\n\t\tTunnel:    tunnel,\n\t\tType:      C.SHADOWSOCKS,\n\t\tAdditions: additions,\n\t\tMuxOption: config.MuxOption,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsl := &Listener{false, config, nil, nil, pickCipher, h}\n\t_listener = sl\n\n\tfor _, addr := range strings.Split(config.Listen, \",\") {\n\t\taddr := addr\n\n\t\tif config.Udp {\n\t\t\t//UDP\n\t\t\tul, err := NewUDP(addr, pickCipher, tunnel, additions...)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tsl.udpListeners = append(sl.udpListeners, ul)\n\t\t}\n\n\t\t//TCP\n\t\tl, err := inbound.Listen(\"tcp\", addr)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tsl.listeners = append(sl.listeners, l)\n\n\t\tgo func() {\n\t\t\tfor {\n\t\t\t\tc, err := l.Accept()\n\t\t\t\tif err != nil {\n\t\t\t\t\tif sl.closed {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tgo sl.HandleConn(c, tunnel, additions...)\n\t\t\t}\n\t\t}()\n\t}\n\n\treturn sl, nil\n}\n\nfunc (l *Listener) Close() error {\n\tvar retErr error\n\tfor _, lis := range l.listeners {\n\t\terr := lis.Close()\n\t\tif err != nil {\n\t\t\tretErr = err\n\t\t}\n\t}\n\tfor _, lis := range l.udpListeners {\n\t\terr := lis.Close()\n\t\tif err != nil {\n\t\t\tretErr = err\n\t\t}\n\t}\n\treturn retErr\n}\n\nfunc (l *Listener) Config() string {\n\treturn l.config.String()\n}\n\nfunc (l *Listener) AddrList() (addrList []net.Addr) {\n\tfor _, lis := range l.listeners {\n\t\taddrList = append(addrList, lis.Addr())\n\t}\n\tfor _, lis := range l.udpListeners {\n\t\taddrList = append(addrList, lis.LocalAddr())\n\t}\n\treturn\n}\n\nfunc (l *Listener) HandleConn(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) {\n\tconn = l.pickCipher.StreamConn(conn)\n\tconn = N.NewDeadlineConn(conn) // embed ss can't handle readDeadline correctly\n\n\ttarget, err := socks5.ReadAddr0(conn)\n\tif err != nil {\n\t\t_ = conn.Close()\n\t\treturn\n\t}\n\tl.handler.HandleSocket(target, conn, additions...)\n\t//tunnel.HandleTCPConn(inbound.NewSocket(target, conn, C.SHADOWSOCKS, additions...))\n}\n\nfunc HandleShadowSocks(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) bool {\n\tif _listener != nil && _listener.pickCipher != nil {\n\t\tgo _listener.HandleConn(conn, tunnel, additions...)\n\t\treturn true\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/shadowsocks/udp.go",
    "content": "package shadowsocks\n\nimport (\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/sockopt\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/transport/shadowsocks/core\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n)\n\ntype UDPListener struct {\n\tpacketConn net.PacketConn\n\tclosed     bool\n}\n\nfunc NewUDP(addr string, pickCipher core.Cipher, tunnel C.Tunnel, additions ...inbound.Addition) (*UDPListener, error) {\n\tl, err := inbound.ListenPacket(\"udp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := sockopt.UDPReuseaddr(l); err != nil {\n\t\tlog.Warnln(\"Failed to Reuse UDP Address: %s\", err)\n\t}\n\n\tsl := &UDPListener{l, false}\n\tconn := pickCipher.PacketConn(N.NewEnhancePacketConn(l))\n\tgo func() {\n\t\tfor {\n\t\t\tdata, put, remoteAddr, err := conn.WaitReadFrom()\n\t\t\tif err != nil {\n\t\t\t\tif put != nil {\n\t\t\t\t\tput()\n\t\t\t\t}\n\t\t\t\tif sl.closed {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\thandleSocksUDP(conn, tunnel, data, put, remoteAddr, additions...)\n\t\t}\n\t}()\n\n\treturn sl, nil\n}\n\nfunc (l *UDPListener) Close() error {\n\tl.closed = true\n\treturn l.packetConn.Close()\n}\n\nfunc (l *UDPListener) LocalAddr() net.Addr {\n\treturn l.packetConn.LocalAddr()\n}\n\nfunc handleSocksUDP(pc net.PacketConn, tunnel C.Tunnel, buf []byte, put func(), addr net.Addr, additions ...inbound.Addition) {\n\ttgtAddr := socks5.SplitAddr(buf)\n\tif tgtAddr == nil {\n\t\t// Unresolved UDP packet, return buffer to the pool\n\t\tif put != nil {\n\t\t\tput()\n\t\t}\n\t\treturn\n\t}\n\ttarget := tgtAddr\n\tpayload := buf[len(tgtAddr):]\n\n\tpacket := &packet{\n\t\tpc:      pc,\n\t\trAddr:   addr,\n\t\tpayload: payload,\n\t\tput:     put,\n\t}\n\ttunnel.HandleUDPPacket(inbound.NewPacket(target, packet, C.SHADOWSOCKS, additions...))\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/shadowsocks/utils.go",
    "content": "package shadowsocks\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n\n\t\"github.com/metacubex/mhurl\"\n)\n\ntype packet struct {\n\tpc      net.PacketConn\n\trAddr   net.Addr\n\tpayload []byte\n\tput     func()\n}\n\nfunc (c *packet) Data() []byte {\n\treturn c.payload\n}\n\n// WriteBack wirtes UDP packet with source(ip, port) = `addr`\nfunc (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {\n\tif addr == nil {\n\t\terr = errors.New(\"address is invalid\")\n\t\treturn\n\t}\n\tpacket := bytes.Join([][]byte{socks5.ParseAddrToSocksAddr(addr), b}, []byte{})\n\treturn c.pc.WriteTo(packet, c.rAddr)\n}\n\n// LocalAddr returns the source IP/Port of UDP Packet\nfunc (c *packet) LocalAddr() net.Addr {\n\treturn c.rAddr\n}\n\nfunc (c *packet) Drop() {\n\tif c.put != nil {\n\t\tc.put()\n\t\tc.put = nil\n\t}\n\tc.payload = nil\n}\n\nfunc (c *packet) InAddr() net.Addr {\n\treturn c.pc.LocalAddr()\n}\n\nfunc ParseSSURL(s string) (addr, cipher, password string, err error) {\n\tu, err := mhurl.Parse(s) // we need multiple hosts url supports\n\tif err != nil {\n\t\treturn\n\t}\n\n\taddr = u.Host\n\tif u.User != nil {\n\t\tcipher = u.User.Username()\n\t\tpassword, _ = u.User.Password()\n\t}\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/shadowsocks/utils_test.go",
    "content": "package shadowsocks\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestParseSSURL(t *testing.T) {\n\tfor _, test := range []struct{ method, passwd, hosts string }{\n\t\t{method: \"aes-256-gcm\", passwd: \"password\", hosts: \":1000,:2000,:3000\"},\n\t\t{method: \"aes-256-gcm\", passwd: \"password\", hosts: \"127.0.0.1:1000,127.0.0.1:2000,127.0.0.1:3000\"},\n\t\t{method: \"aes-256-gcm\", passwd: \"password\", hosts: \"[::1]:1000,[::1]:2000,[::1]:3000\"},\n\t} {\n\t\taddr, cipher, password, err := ParseSSURL(fmt.Sprintf(\"ss://%s:%s@%s\", test.method, test.passwd, test.hosts))\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, test.hosts, addr)\n\t\trequire.Equal(t, test.method, cipher)\n\t\trequire.Equal(t, test.passwd, password)\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/sing/context.go",
    "content": "package sing\n\nimport (\n\t\"context\"\n\t\"golang.org/x/exp/slices\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\n\t\"github.com/metacubex/sing/common/auth\"\n)\n\ntype contextKey string\n\nvar ctxKeyAdditions = contextKey(\"Additions\")\n\nfunc WithAdditions(ctx context.Context, additions ...inbound.Addition) context.Context {\n\treturn context.WithValue(ctx, ctxKeyAdditions, additions)\n}\n\nfunc getAdditions(ctx context.Context) (additions []inbound.Addition) {\n\tif v := ctx.Value(ctxKeyAdditions); v != nil {\n\t\tif a, ok := v.([]inbound.Addition); ok {\n\t\t\tadditions = a\n\t\t}\n\t}\n\tif user, ok := auth.UserFromContext[string](ctx); ok {\n\t\tadditions = slices.Clone(additions)\n\t\tadditions = append(additions, inbound.WithInUser(user))\n\t}\n\treturn\n}\n\nvar ctxKeyInAddr = contextKey(\"InAddr\")\n\nfunc WithInAddr(ctx context.Context, inAddr net.Addr) context.Context {\n\treturn context.WithValue(ctx, ctxKeyInAddr, inAddr)\n}\n\nfunc getInAddr(ctx context.Context) net.Addr {\n\tif v := ctx.Value(ctxKeyInAddr); v != nil {\n\t\tif a, ok := v.(net.Addr); ok {\n\t\t\treturn a\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/sing/dialer.go",
    "content": "package sing\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/listener/inner\"\n\n\tM \"github.com/metacubex/sing/common/metadata\"\n\tN \"github.com/metacubex/sing/common/network\"\n)\n\ntype Dialer struct {\n\tt     C.Tunnel\n\tproxy string\n}\n\nfunc (d Dialer) DialContext(ctx context.Context, network string, destination M.Socksaddr) (net.Conn, error) {\n\tif network != \"tcp\" && network != \"tcp4\" && network != \"tcp6\" {\n\t\treturn nil, fmt.Errorf(\"unsupported network %s\", network)\n\t}\n\treturn inner.HandleTcp(d.t, destination.String(), d.proxy)\n}\n\nfunc (d Dialer) ListenPacket(ctx context.Context, destination M.Socksaddr) (net.PacketConn, error) {\n\treturn nil, fmt.Errorf(\"unsupported ListenPacket\")\n}\n\nvar _ N.Dialer = (*Dialer)(nil)\n\nfunc NewDialer(t C.Tunnel, proxy string) (d *Dialer) {\n\treturn &Dialer{t, proxy}\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/sing/sing.go",
    "content": "package sing\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net\"\n\t\"net/netip\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\t\"github.com/metacubex/mihomo/adapter/outbound\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\tmux \"github.com/metacubex/sing-mux\"\n\tvmess \"github.com/metacubex/sing-vmess\"\n\t\"github.com/metacubex/sing-vmess/packetaddr\"\n\t\"github.com/metacubex/sing/common\"\n\t\"github.com/metacubex/sing/common/buf\"\n\t\"github.com/metacubex/sing/common/bufio\"\n\t\"github.com/metacubex/sing/common/bufio/deadline\"\n\tE \"github.com/metacubex/sing/common/exceptions\"\n\tM \"github.com/metacubex/sing/common/metadata\"\n\t\"github.com/metacubex/sing/common/network\"\n\t\"github.com/metacubex/sing/common/uot\"\n)\n\nconst UDPTimeout = 5 * time.Minute\n\ntype ListenerConfig struct {\n\tTunnel     C.Tunnel\n\tType       C.Type\n\tAdditions  []inbound.Addition\n\tUDPTimeout time.Duration\n\tMuxOption  MuxOption\n}\n\ntype MuxOption struct {\n\tPadding bool          `yaml:\"padding\" json:\"padding,omitempty\"`\n\tBrutal  BrutalOptions `yaml:\"brutal\" json:\"brutal,omitempty\"`\n}\n\ntype BrutalOptions struct {\n\tEnabled bool   `yaml:\"enabled\" json:\"enabled\"`\n\tUp      string `yaml:\"up\" json:\"up,omitempty\"`\n\tDown    string `yaml:\"down\" json:\"down,omitempty\"`\n}\n\ntype ListenerHandler struct {\n\tListenerConfig\n\tmuxService *mux.Service\n}\n\nfunc UpstreamMetadata(metadata M.Metadata) M.Metadata {\n\treturn M.Metadata{\n\t\tSource:      metadata.Source,\n\t\tDestination: metadata.Destination,\n\t}\n}\n\nfunc ConvertMetadata(metadata *C.Metadata) M.Metadata {\n\treturn M.Metadata{\n\t\tProtocol:    metadata.Type.String(),\n\t\tSource:      M.SocksaddrFrom(metadata.SrcIP, metadata.SrcPort),\n\t\tDestination: M.ParseSocksaddrHostPort(metadata.String(), metadata.DstPort),\n\t}\n}\n\nfunc NewListenerHandler(lc ListenerConfig) (h *ListenerHandler, err error) {\n\th = &ListenerHandler{ListenerConfig: lc}\n\th.muxService, err = mux.NewService(mux.ServiceOptions{\n\t\tNewStreamContext: func(ctx context.Context, conn net.Conn) context.Context {\n\t\t\treturn ctx\n\t\t},\n\t\tLogger:  log.SingInfoToDebugLogger, // convert sing-mux info log to debug\n\t\tHandler: h,\n\t\tPadding: lc.MuxOption.Padding,\n\t\tBrutal: mux.BrutalOptions{\n\t\t\tEnabled:    lc.MuxOption.Brutal.Enabled,\n\t\t\tSendBPS:    outbound.StringToBps(lc.MuxOption.Brutal.Up),\n\t\t\tReceiveBPS: outbound.StringToBps(lc.MuxOption.Brutal.Down),\n\t\t},\n\t})\n\treturn\n}\n\nfunc (h *ListenerHandler) IsSpecialFqdn(fqdn string) bool {\n\tswitch fqdn {\n\tcase mux.Destination.Fqdn,\n\t\tvmess.MuxDestination.Fqdn,\n\t\tuot.MagicAddress,\n\t\tuot.LegacyMagicAddress:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc (h *ListenerHandler) ParseSpecialFqdn(ctx context.Context, conn net.Conn, metadata M.Metadata) error {\n\tswitch metadata.Destination.Fqdn {\n\tcase mux.Destination.Fqdn:\n\t\treturn h.muxService.NewConnection(ctx, conn, UpstreamMetadata(metadata))\n\tcase vmess.MuxDestination.Fqdn:\n\t\treturn vmess.HandleMuxConnection(ctx, conn, metadata, h)\n\tcase uot.MagicAddress:\n\t\trequest, err := uot.ReadRequest(conn)\n\t\tif err != nil {\n\t\t\treturn E.Cause(err, \"read UoT request\")\n\t\t}\n\t\tmetadata.Destination = request.Destination\n\t\treturn h.NewPacketConnection(ctx, uot.NewConn(conn, *request), metadata)\n\tcase uot.LegacyMagicAddress:\n\t\tmetadata.Destination = M.Socksaddr{Addr: netip.IPv4Unspecified()}\n\t\treturn h.NewPacketConnection(ctx, uot.NewConn(conn, uot.Request{}), metadata)\n\t}\n\treturn errors.New(\"not special fqdn\")\n}\n\nfunc (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {\n\tif h.IsSpecialFqdn(metadata.Destination.Fqdn) {\n\t\treturn h.ParseSpecialFqdn(ctx, conn, metadata)\n\t}\n\n\tif deadline.NeedAdditionalReadDeadline(conn) {\n\t\tconn = N.NewDeadlineConn(conn) // conn from sing should check NeedAdditionalReadDeadline\n\t}\n\n\tcMetadata := &C.Metadata{\n\t\tNetWork: C.TCP,\n\t\tType:    h.Type,\n\t}\n\tif metadata.Source.IsIP() && metadata.Source.Fqdn == \"\" {\n\t\tcMetadata.RawSrcAddr = metadata.Source.Unwrap().TCPAddr()\n\t}\n\tif metadata.Destination.IsIP() && metadata.Destination.Fqdn == \"\" {\n\t\tcMetadata.RawDstAddr = metadata.Destination.Unwrap().TCPAddr()\n\t}\n\tinbound.ApplyAdditions(cMetadata, inbound.WithDstAddr(metadata.Destination), inbound.WithSrcAddr(metadata.Source), inbound.WithInAddr(conn.LocalAddr()))\n\tinbound.ApplyAdditions(cMetadata, h.Additions...)\n\tinbound.ApplyAdditions(cMetadata, getAdditions(ctx)...)\n\n\th.Tunnel.HandleTCPConn(conn, cMetadata) // this goroutine must exit after conn unused\n\treturn nil\n}\n\nfunc (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.PacketConn, metadata M.Metadata) error {\n\tif metadata.Destination.Fqdn == packetaddr.SeqPacketMagicAddress {\n\t\tconn = packetaddr.NewConn(bufio.NewNetPacketConn(conn), M.Socksaddr{})\n\t}\n\n\tconnID := utils.NewUUIDV4().String() // make a new SNAT key\n\n\tdefer func() { _ = conn.Close() }()\n\tmutex := sync.Mutex{}\n\twriter := bufio.NewNetPacketWriter(conn) // a new interface to set nil in defer\n\tdefer func() {\n\t\tmutex.Lock() // this goroutine must exit after all conn.WritePacket() is not running\n\t\tdefer mutex.Unlock()\n\t\twriter = nil\n\t}()\n\trwOptions := network.ReadWaitOptions{}\n\treadWaiter, isReadWaiter := bufio.CreatePacketReadWaiter(conn)\n\tif isReadWaiter {\n\t\treadWaiter.InitializeReadWaiter(rwOptions)\n\t}\n\tfor {\n\t\tvar (\n\t\t\tbuff *buf.Buffer\n\t\t\tdest M.Socksaddr\n\t\t\terr  error\n\t\t)\n\t\tif isReadWaiter {\n\t\t\tbuff, dest, err = readWaiter.WaitReadPacket()\n\t\t} else {\n\t\t\tbuff = rwOptions.NewPacketBuffer()\n\t\t\tdest, err = conn.ReadPacket(buff)\n\t\t\tif buff != nil {\n\t\t\t\trwOptions.PostReturn(buff)\n\t\t\t}\n\t\t}\n\t\tif err != nil {\n\t\t\tbuff.Release()\n\t\t\tif ShouldIgnorePacketError(err) {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\treturn err\n\t\t}\n\t\tcPacket := &packet{\n\t\t\twriter: &writer,\n\t\t\tmutex:  &mutex,\n\t\t\trAddr:  metadata.Source.UDPAddr(),\n\t\t\tlAddr:  conn.LocalAddr(),\n\t\t\tbuff:   buff,\n\t\t}\n\t\tcPacket.rAddr = N.NewCustomAddr(h.Type.String(), connID, cPacket.rAddr) // for tunnel's handleUDPConn\n\t\tif lAddr := getInAddr(ctx); lAddr != nil {\n\t\t\tcPacket.lAddr = lAddr\n\t\t}\n\t\th.handlePacket(ctx, cPacket, metadata.Source, dest)\n\t}\n\treturn nil\n}\n\ntype localAddr interface {\n\tLocalAddr() net.Addr\n}\n\nfunc (h *ListenerHandler) NewPacket(ctx context.Context, key netip.AddrPort, buffer *buf.Buffer, metadata M.Metadata, init func(natConn network.PacketConn) network.PacketWriter) {\n\twriter := bufio.NewNetPacketWriter(init(nil))\n\tmutex := sync.Mutex{}\n\tcPacket := &packet{\n\t\twriter: &writer,\n\t\tmutex:  &mutex,\n\t\trAddr:  metadata.Source.UDPAddr(), // TODO: using key argument to make a SNAT key\n\t\tbuff:   buffer,\n\t}\n\tif conn, ok := common.Cast[localAddr](writer); ok { // tun does not have real inAddr\n\t\tcPacket.lAddr = conn.LocalAddr()\n\t}\n\th.handlePacket(ctx, cPacket, metadata.Source, metadata.Destination)\n}\n\nfunc (h *ListenerHandler) handlePacket(ctx context.Context, cPacket *packet, source M.Socksaddr, destination M.Socksaddr) {\n\tcMetadata := &C.Metadata{\n\t\tNetWork: C.UDP,\n\t\tType:    h.Type,\n\t}\n\tif source.IsIP() && source.Fqdn == \"\" {\n\t\tcMetadata.RawSrcAddr = source.Unwrap().UDPAddr()\n\t}\n\tif destination.IsIP() && destination.Fqdn == \"\" {\n\t\tcMetadata.RawDstAddr = destination.Unwrap().UDPAddr()\n\t}\n\tinbound.ApplyAdditions(cMetadata, inbound.WithDstAddr(destination), inbound.WithSrcAddr(source), inbound.WithInAddr(cPacket.InAddr()))\n\tinbound.ApplyAdditions(cMetadata, h.Additions...)\n\tinbound.ApplyAdditions(cMetadata, getAdditions(ctx)...)\n\n\th.Tunnel.HandleUDPPacket(cPacket, cMetadata)\n}\n\nfunc (h *ListenerHandler) NewError(ctx context.Context, err error) {\n\tlog.Warnln(\"%s listener get error: %+v\", h.Type.String(), err)\n}\n\nfunc (h *ListenerHandler) TypeMutation(typ C.Type) *ListenerHandler {\n\thandler := *h\n\thandler.Type = typ\n\treturn &handler\n}\n\nfunc ShouldIgnorePacketError(err error) bool {\n\t// ignore simple error\n\tif E.IsTimeout(err) || E.IsClosed(err) || E.IsCanceled(err) {\n\t\treturn true\n\t}\n\treturn false\n}\n\ntype packet struct {\n\twriter *network.NetPacketWriter\n\tmutex  *sync.Mutex\n\trAddr  net.Addr\n\tlAddr  net.Addr\n\tbuff   *buf.Buffer\n}\n\nfunc (c *packet) Data() []byte {\n\treturn c.buff.Bytes()\n}\n\n// WriteBack wirtes UDP packet with source(ip, port) = `addr`\nfunc (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {\n\tif addr == nil {\n\t\terr = errors.New(\"address is invalid\")\n\t\treturn\n\t}\n\n\tc.mutex.Lock()\n\tdefer c.mutex.Unlock()\n\tconn := *c.writer\n\tif conn == nil {\n\t\terr = errors.New(\"writeBack to closed connection\")\n\t\treturn\n\t}\n\n\treturn conn.WriteTo(b, addr)\n}\n\n// LocalAddr returns the source IP/Port of UDP Packet\nfunc (c *packet) LocalAddr() net.Addr {\n\treturn c.rAddr\n}\n\nfunc (c *packet) Drop() {\n\tc.buff.Release()\n}\n\nfunc (c *packet) InAddr() net.Addr {\n\treturn c.lAddr\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/sing/util.go",
    "content": "package sing\n\nimport (\n\t\"context\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n)\n\n// HandleSocket like inbound.NewSocket combine with Tunnel.HandleTCPConn but also handel specialFqdn\nfunc (h *ListenerHandler) HandleSocket(target socks5.Addr, conn net.Conn, _additions ...inbound.Addition) {\n\tconn, metadata := inbound.NewSocket(target, conn, h.Type, h.Additions...)\n\tif h.IsSpecialFqdn(metadata.Host) {\n\t\terr := h.ParseSpecialFqdn(\n\t\t\tWithAdditions(context.Background(), _additions...),\n\t\t\tconn,\n\t\t\tConvertMetadata(metadata),\n\t\t)\n\t\tif err != nil {\n\t\t\t_ = conn.Close()\n\t\t}\n\t} else {\n\t\tinbound.ApplyAdditions(metadata, _additions...)\n\t\th.Tunnel.HandleTCPConn(conn, metadata)\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/sing_hysteria2/server.go",
    "content": "package sing_hysteria2\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\t\"github.com/metacubex/mihomo/adapter/outbound\"\n\t\"github.com/metacubex/mihomo/common/sockopt\"\n\t\"github.com/metacubex/mihomo/component/ca\"\n\t\"github.com/metacubex/mihomo/component/ech\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/inner\"\n\t\"github.com/metacubex/mihomo/listener/sing\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/ntp\"\n\t\"github.com/metacubex/mihomo/transport/tuic/common\"\n\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/http/httputil\"\n\t\"github.com/metacubex/quic-go\"\n\t\"github.com/metacubex/sing-quic/hysteria2\"\n\tE \"github.com/metacubex/sing/common/exceptions\"\n\t\"github.com/metacubex/tls\"\n)\n\ntype Listener struct {\n\tclosed       bool\n\tconfig       LC.Hysteria2Server\n\tudpListeners []net.PacketConn\n\tservices     []*hysteria2.Service[string]\n}\n\nfunc New(config LC.Hysteria2Server, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {\n\tvar sl *Listener\n\tvar err error\n\tif len(additions) == 0 {\n\t\tadditions = []inbound.Addition{\n\t\t\tinbound.WithInName(\"DEFAULT-HYSTERIA2\"),\n\t\t\tinbound.WithSpecialRules(\"\"),\n\t\t}\n\t}\n\n\th, err := sing.NewListenerHandler(sing.ListenerConfig{\n\t\tTunnel:    tunnel,\n\t\tType:      C.HYSTERIA2,\n\t\tAdditions: additions,\n\t\tMuxOption: config.MuxOption,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsl = &Listener{false, config, nil, nil}\n\n\ttlsConfig := &tls.Config{\n\t\tTime:       ntp.Now,\n\t\tMinVersion: tls.VersionTLS13,\n\t}\n\tcertLoader, err := ca.NewTLSKeyPairLoader(config.Certificate, config.PrivateKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\ttlsConfig.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) {\n\t\treturn certLoader()\n\t}\n\ttlsConfig.ClientAuth = ca.ClientAuthTypeFromString(config.ClientAuthType)\n\tif len(config.ClientAuthCert) > 0 {\n\t\tif tlsConfig.ClientAuth == tls.NoClientCert {\n\t\t\ttlsConfig.ClientAuth = tls.RequireAndVerifyClientCert\n\t\t}\n\t}\n\tif tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert {\n\t\tpool, err := ca.LoadCertificates(config.ClientAuthCert)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttlsConfig.ClientCAs = pool\n\t}\n\n\tif config.EchKey != \"\" {\n\t\terr = ech.LoadECHKey(config.EchKey, tlsConfig)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tif len(config.ALPN) > 0 {\n\t\ttlsConfig.NextProtos = config.ALPN\n\t} else {\n\t\ttlsConfig.NextProtos = []string{\"h3\"}\n\t}\n\n\tvar salamanderPassword string\n\tif len(config.Obfs) > 0 {\n\t\tif config.ObfsPassword == \"\" {\n\t\t\treturn nil, errors.New(\"missing obfs password\")\n\t\t}\n\t\tswitch config.Obfs {\n\t\tcase hysteria2.ObfsTypeSalamander:\n\t\t\tsalamanderPassword = config.ObfsPassword\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"unknown obfs type: %s\", config.Obfs)\n\t\t}\n\t}\n\tvar masqueradeHandler http.Handler\n\tif config.Masquerade != \"\" {\n\t\tmasqueradeURL, err := url.Parse(config.Masquerade)\n\t\tif err != nil {\n\t\t\treturn nil, E.Cause(err, \"parse masquerade URL\")\n\t\t}\n\t\tswitch masqueradeURL.Scheme {\n\t\tcase \"file\":\n\t\t\tmasqueradeHandler = http.FileServer(http.Dir(masqueradeURL.Path))\n\t\tcase \"http\", \"https\":\n\t\t\tmasqueradeHandler = &httputil.ReverseProxy{\n\t\t\t\tRewrite: func(r *httputil.ProxyRequest) {\n\t\t\t\t\tr.SetURL(masqueradeURL)\n\t\t\t\t\tr.Out.Host = r.In.Host\n\t\t\t\t},\n\t\t\t\tErrorHandler: func(w http.ResponseWriter, r *http.Request, err error) {\n\t\t\t\t\tw.WriteHeader(http.StatusBadGateway)\n\t\t\t\t},\n\t\t\t\tTransport: &http.Transport{\n\t\t\t\t\t// fellow hysteria2's code skip verify\n\t\t\t\t\tTLSClientConfig: &tls.Config{\n\t\t\t\t\t\tInsecureSkipVerify: true,\n\t\t\t\t\t},\n\t\t\t\t\t// from http.DefaultTransport\n\t\t\t\t\tForceAttemptHTTP2:     true,\n\t\t\t\t\tMaxIdleConns:          100,\n\t\t\t\t\tIdleConnTimeout:       90 * time.Second,\n\t\t\t\t\tTLSHandshakeTimeout:   10 * time.Second,\n\t\t\t\t\tExpectContinueTimeout: 1 * time.Second,\n\t\t\t\t\tDialContext: func(ctx context.Context, network, address string) (net.Conn, error) {\n\t\t\t\t\t\treturn inner.HandleTcp(tunnel, address, \"\")\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, E.New(\"unknown masquerade URL scheme: \", masqueradeURL.Scheme)\n\t\t}\n\t}\n\n\tif config.UdpMTU == 0 {\n\t\t// \"1200\" from quic-go's MaxDatagramSize\n\t\t// \"-3\" from quic-go's DatagramFrame.MaxDataLen\n\t\tconfig.UdpMTU = 1200 - 3\n\t}\n\n\tquicConfig := &quic.Config{\n\t\tInitialStreamReceiveWindow:     config.InitialStreamReceiveWindow,\n\t\tMaxStreamReceiveWindow:         config.MaxStreamReceiveWindow,\n\t\tInitialConnectionReceiveWindow: config.InitialConnectionReceiveWindow,\n\t\tMaxConnectionReceiveWindow:     config.MaxConnectionReceiveWindow,\n\t}\n\n\tservice, err := hysteria2.NewService[string](hysteria2.ServiceOptions{\n\t\tContext:               context.Background(),\n\t\tLogger:                log.SingLogger,\n\t\tSendBPS:               outbound.StringToBps(config.Up),\n\t\tReceiveBPS:            outbound.StringToBps(config.Down),\n\t\tSalamanderPassword:    salamanderPassword,\n\t\tTLSConfig:             tlsConfig,\n\t\tQUICConfig:            quicConfig,\n\t\tIgnoreClientBandwidth: config.IgnoreClientBandwidth,\n\t\tUDPTimeout:            sing.UDPTimeout,\n\t\tHandler:               h,\n\t\tMasqueradeHandler:     masqueradeHandler,\n\t\tUdpMTU:                config.UdpMTU,\n\t\tSetBBRCongestion: func(quicConn *quic.Conn) {\n\t\t\tcommon.SetCongestionController(quicConn, \"bbr\", config.CWND, config.BBRProfile)\n\t\t},\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tuserNameList := make([]string, 0, len(config.Users))\n\tuserPasswordList := make([]string, 0, len(config.Users))\n\tfor name, password := range config.Users {\n\t\tuserNameList = append(userNameList, name)\n\t\tuserPasswordList = append(userPasswordList, password)\n\t}\n\tservice.UpdateUsers(userNameList, userPasswordList)\n\n\tfor _, addr := range strings.Split(config.Listen, \",\") {\n\t\taddr := addr\n\t\t_service := *service\n\t\tservice := &_service // make a copy\n\n\t\tul, err := inbound.ListenPacket(\"udp\", addr)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif err := sockopt.UDPReuseaddr(ul); err != nil {\n\t\t\tlog.Warnln(\"Failed to Reuse UDP Address: %s\", err)\n\t\t}\n\n\t\tsl.udpListeners = append(sl.udpListeners, ul)\n\t\tsl.services = append(sl.services, service)\n\n\t\tgo func() {\n\t\t\t_ = service.Start(ul)\n\t\t}()\n\t}\n\n\treturn sl, nil\n}\n\nfunc (l *Listener) Close() error {\n\tl.closed = true\n\tvar retErr error\n\tfor _, service := range l.services {\n\t\terr := service.Close()\n\t\tif err != nil {\n\t\t\tretErr = err\n\t\t}\n\t}\n\tfor _, lis := range l.udpListeners {\n\t\terr := lis.Close()\n\t\tif err != nil {\n\t\t\tretErr = err\n\t\t}\n\t}\n\treturn retErr\n}\n\nfunc (l *Listener) Config() string {\n\treturn l.config.String()\n}\n\nfunc (l *Listener) AddrList() (addrList []net.Addr) {\n\tfor _, lis := range l.udpListeners {\n\t\taddrList = append(addrList, lis.LocalAddr())\n\t}\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/sing_shadowsocks/server.go",
    "content": "package sing_shadowsocks\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\t\"github.com/metacubex/mihomo/common/sockopt\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\tembedSS \"github.com/metacubex/mihomo/listener/shadowsocks\"\n\t\"github.com/metacubex/mihomo/listener/sing\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/ntp\"\n\t\"github.com/metacubex/mihomo/transport/kcptun\"\n\n\tshadowsocks \"github.com/metacubex/sing-shadowsocks\"\n\t\"github.com/metacubex/sing-shadowsocks/shadowaead\"\n\t\"github.com/metacubex/sing-shadowsocks/shadowaead_2022\"\n\tshadowtls \"github.com/metacubex/sing-shadowtls\"\n\t\"github.com/metacubex/sing/common\"\n\t\"github.com/metacubex/sing/common/buf\"\n\t\"github.com/metacubex/sing/common/bufio\"\n\tM \"github.com/metacubex/sing/common/metadata\"\n\t\"github.com/metacubex/sing/common/network\"\n)\n\ntype Listener struct {\n\tclosed       bool\n\tconfig       LC.ShadowsocksServer\n\tlisteners    []net.Listener\n\tudpListeners []net.PacketConn\n\tservice      shadowsocks.Service\n\tshadowTLS    *shadowtls.Service\n}\n\nvar _listener *Listener\n\n// shadowTLSService is a wrapper for shadowsocks.Service to support shadowTLS.\ntype shadowTLSService struct {\n\tshadowsocks.Service\n\tshadowTLS *shadowtls.Service\n}\n\nfunc (s *shadowTLSService) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {\n\tif s.shadowTLS != nil {\n\t\treturn s.shadowTLS.NewConnection(ctx, conn, metadata)\n\t}\n\treturn s.Service.NewConnection(ctx, conn, metadata)\n}\n\nfunc New(config LC.ShadowsocksServer, tunnel C.Tunnel, additions ...inbound.Addition) (C.MultiAddrListener, error) {\n\tvar sl *Listener\n\tvar err error\n\tif len(additions) == 0 {\n\t\tadditions = []inbound.Addition{\n\t\t\tinbound.WithInName(\"DEFAULT-SHADOWSOCKS\"),\n\t\t\tinbound.WithSpecialRules(\"\"),\n\t\t}\n\t\tdefer func() {\n\t\t\t_listener = sl\n\t\t}()\n\t}\n\n\tudpTimeout := int64(sing.UDPTimeout.Seconds())\n\n\th, err := sing.NewListenerHandler(sing.ListenerConfig{\n\t\tTunnel:    tunnel,\n\t\tType:      C.SHADOWSOCKS,\n\t\tAdditions: additions,\n\t\tMuxOption: config.MuxOption,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsl = &Listener{}\n\tsl.config = config\n\n\tswitch {\n\tcase config.Cipher == shadowsocks.MethodNone:\n\t\tsl.service = shadowsocks.NewNoneService(udpTimeout, h)\n\tcase common.Contains(shadowaead.List, config.Cipher):\n\t\tsl.service, err = shadowaead.NewService(config.Cipher, nil, config.Password, udpTimeout, h)\n\tcase common.Contains(shadowaead_2022.List, config.Cipher):\n\t\tsl.service, err = shadowaead_2022.NewServiceWithPassword(config.Cipher, config.Password, udpTimeout, h, ntp.Now)\n\tdefault:\n\t\terr = fmt.Errorf(\"shadowsocks: unsupported method: %s\", config.Cipher)\n\t\treturn embedSS.New(config, tunnel, additions...)\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif config.ShadowTLS.Enable {\n\t\tbuildHandshake := func(handshake LC.ShadowTLSHandshakeOptions) (handshakeConfig shadowtls.HandshakeConfig) {\n\t\t\thandshakeConfig.Server = M.ParseSocksaddr(handshake.Dest)\n\t\t\thandshakeConfig.Dialer = sing.NewDialer(tunnel, handshake.Proxy)\n\t\t\treturn\n\t\t}\n\t\tvar handshakeForServerName map[string]shadowtls.HandshakeConfig\n\t\tif config.ShadowTLS.Version > 1 {\n\t\t\thandshakeForServerName = make(map[string]shadowtls.HandshakeConfig)\n\t\t\tfor serverName, serverOptions := range config.ShadowTLS.HandshakeForServerName {\n\t\t\t\thandshakeForServerName[serverName] = buildHandshake(serverOptions)\n\t\t\t}\n\t\t}\n\t\tvar wildcardSNI shadowtls.WildcardSNI\n\t\tswitch config.ShadowTLS.WildcardSNI {\n\t\tcase \"authed\":\n\t\t\twildcardSNI = shadowtls.WildcardSNIAuthed\n\t\tcase \"all\":\n\t\t\twildcardSNI = shadowtls.WildcardSNIAll\n\t\tdefault:\n\t\t\twildcardSNI = shadowtls.WildcardSNIOff\n\t\t}\n\t\tvar shadowTLS *shadowtls.Service\n\t\tshadowTLS, err = shadowtls.NewService(shadowtls.ServiceConfig{\n\t\t\tVersion:  config.ShadowTLS.Version,\n\t\t\tPassword: config.ShadowTLS.Password,\n\t\t\tUsers: common.Map(config.ShadowTLS.Users, func(it LC.ShadowTLSUser) shadowtls.User {\n\t\t\t\treturn shadowtls.User{Name: it.Name, Password: it.Password}\n\t\t\t}),\n\t\t\tHandshake:              buildHandshake(config.ShadowTLS.Handshake),\n\t\t\tHandshakeForServerName: handshakeForServerName,\n\t\t\tStrictMode:             config.ShadowTLS.StrictMode,\n\t\t\tWildcardSNI:            wildcardSNI,\n\t\t\tHandler:                sl.service,\n\t\t\tLogger:                 log.SingLogger,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tsl.service = &shadowTLSService{\n\t\t\tService:   sl.service,\n\t\t\tshadowTLS: shadowTLS,\n\t\t}\n\t}\n\n\tvar kcptunServer *kcptun.Server\n\tif config.KcpTun.Enable {\n\t\tkcptunServer = kcptun.NewServer(config.KcpTun.Config)\n\t\tconfig.Udp = true\n\t}\n\n\tfor _, addr := range strings.Split(config.Listen, \",\") {\n\t\taddr := addr\n\n\t\tif config.Udp {\n\t\t\t//UDP\n\t\t\tul, err := inbound.ListenPacket(\"udp\", addr)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tif err := sockopt.UDPReuseaddr(ul); err != nil {\n\t\t\t\tlog.Warnln(\"Failed to Reuse UDP Address: %s\", err)\n\t\t\t}\n\n\t\t\tsl.udpListeners = append(sl.udpListeners, ul)\n\n\t\t\tif kcptunServer != nil {\n\t\t\t\tgo kcptunServer.Serve(ul, func(c net.Conn) {\n\t\t\t\t\tsl.HandleConn(c, tunnel)\n\t\t\t\t})\n\n\t\t\t\tcontinue // skip tcp listener\n\t\t\t}\n\n\t\t\tgo func() {\n\t\t\t\tconn := bufio.NewPacketConn(ul)\n\t\t\t\trwOptions := network.NewReadWaitOptions(conn, sl.service)\n\t\t\t\treadWaiter, isReadWaiter := bufio.CreatePacketReadWaiter(conn)\n\t\t\t\tif isReadWaiter {\n\t\t\t\t\treadWaiter.InitializeReadWaiter(rwOptions)\n\t\t\t\t}\n\t\t\t\tfor {\n\t\t\t\t\tvar (\n\t\t\t\t\t\tbuff *buf.Buffer\n\t\t\t\t\t\tdest M.Socksaddr\n\t\t\t\t\t\terr  error\n\t\t\t\t\t)\n\t\t\t\t\tbuff = nil // clear last loop status, avoid repeat release\n\t\t\t\t\tif isReadWaiter {\n\t\t\t\t\t\tbuff, dest, err = readWaiter.WaitReadPacket()\n\t\t\t\t\t} else {\n\t\t\t\t\t\tbuff = rwOptions.NewPacketBuffer()\n\t\t\t\t\t\tdest, err = conn.ReadPacket(buff)\n\t\t\t\t\t\tif buff != nil {\n\t\t\t\t\t\t\trwOptions.PostReturn(buff)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tbuff.Release()\n\t\t\t\t\t\tif sl.closed {\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tctx := context.TODO()\n\t\t\t\t\tctx = sing.WithInAddr(ctx, ul.LocalAddr())\n\t\t\t\t\t_ = sl.service.NewPacket(ctx, conn, buff, M.Metadata{\n\t\t\t\t\t\tProtocol: \"shadowsocks\",\n\t\t\t\t\t\tSource:   dest,\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\n\t\t//TCP\n\t\tl, err := inbound.Listen(\"tcp\", addr)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tsl.listeners = append(sl.listeners, l)\n\n\t\tgo func() {\n\t\t\tfor {\n\t\t\t\tc, err := l.Accept()\n\t\t\t\tif err != nil {\n\t\t\t\t\tif sl.closed {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tgo sl.HandleConn(c, tunnel)\n\t\t\t}\n\t\t}()\n\t}\n\n\treturn sl, nil\n}\n\nfunc (l *Listener) Close() error {\n\tl.closed = true\n\tvar retErr error\n\tfor _, lis := range l.listeners {\n\t\terr := lis.Close()\n\t\tif err != nil {\n\t\t\tretErr = err\n\t\t}\n\t}\n\tfor _, lis := range l.udpListeners {\n\t\terr := lis.Close()\n\t\tif err != nil {\n\t\t\tretErr = err\n\t\t}\n\t}\n\treturn retErr\n}\n\nfunc (l *Listener) Config() string {\n\treturn l.config.String()\n}\n\nfunc (l *Listener) AddrList() (addrList []net.Addr) {\n\tfor _, lis := range l.listeners {\n\t\taddrList = append(addrList, lis.Addr())\n\t}\n\tfor _, lis := range l.udpListeners {\n\t\taddrList = append(addrList, lis.LocalAddr())\n\t}\n\treturn\n}\n\nfunc (l *Listener) HandleConn(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) {\n\tctx := sing.WithAdditions(context.TODO(), additions...)\n\terr := l.service.NewConnection(ctx, conn, M.Metadata{\n\t\tProtocol: \"shadowsocks\",\n\t\tSource:   M.SocksaddrFromNet(conn.RemoteAddr()),\n\t})\n\tif err != nil {\n\t\t_ = conn.Close()\n\t\treturn\n\t}\n}\n\nfunc HandleShadowSocks(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) bool {\n\tif _listener != nil && _listener.service != nil {\n\t\tgo _listener.HandleConn(conn, tunnel, additions...)\n\t\treturn true\n\t}\n\treturn embedSS.HandleShadowSocks(conn, tunnel, additions...)\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/sing_tun/dns.go",
    "content": "package sing_tun\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"net/netip\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/listener/sing\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/metacubex/sing/common/buf\"\n\t\"github.com/metacubex/sing/common/bufio\"\n\tM \"github.com/metacubex/sing/common/metadata\"\n\t\"github.com/metacubex/sing/common/network\"\n)\n\nfunc (h *ListenerHandler) ShouldHijackDns(targetAddr netip.AddrPort) bool {\n\tfor _, addrPort := range h.DnsAddrPorts {\n\t\tif addrPort == targetAddr || (addrPort.Addr().IsUnspecified() && targetAddr.Port() == 53) {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (h *ListenerHandler) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {\n\tif h.ShouldHijackDns(metadata.Destination.AddrPort()) {\n\t\tlog.Debugln(\"[DNS] hijack tcp:%s\", metadata.Destination.String())\n\t\treturn resolver.RelayDnsConn(ctx, conn, resolver.DefaultDnsReadTimeout)\n\t}\n\treturn h.ListenerHandler.NewConnection(ctx, conn, metadata)\n}\n\nfunc (h *ListenerHandler) NewPacket(ctx context.Context, key netip.AddrPort, buffer *buf.Buffer, metadata M.Metadata, init func(natConn network.PacketConn) network.PacketWriter) {\n\tif h.ShouldHijackDns(metadata.Destination.AddrPort()) {\n\t\tlog.Debugln(\"[DNS] hijack udp:%s from %s\", metadata.Destination.String(), metadata.Source.String())\n\t\twriter := init(nil)\n\t\trwOptions := network.ReadWaitOptions{\n\t\t\tFrontHeadroom: network.CalculateFrontHeadroom(writer),\n\t\t\tRearHeadroom:  network.CalculateRearHeadroom(writer),\n\t\t\tMTU:           resolver.SafeDnsPacketSize,\n\t\t}\n\t\tgo relayDnsPacket(ctx, buffer, rwOptions, metadata.Destination, nil, &writer)\n\t\treturn\n\t}\n\th.ListenerHandler.NewPacket(ctx, key, buffer, metadata, init)\n}\n\nfunc (h *ListenerHandler) NewPacketConnection(ctx context.Context, conn network.PacketConn, metadata M.Metadata) error {\n\tif h.ShouldHijackDns(metadata.Destination.AddrPort()) {\n\t\tlog.Debugln(\"[DNS] hijack udp:%s from %s\", metadata.Destination.String(), metadata.Source.String())\n\t\tdefer func() { _ = conn.Close() }()\n\t\tmutex := sync.Mutex{}\n\t\tvar writer network.PacketWriter = conn // a new interface to set nil in defer\n\t\tdefer func() {\n\t\t\tmutex.Lock() // this goroutine must exit after all conn.WritePacket() is not running\n\t\t\tdefer mutex.Unlock()\n\t\t\twriter = nil\n\t\t}()\n\t\trwOptions := network.ReadWaitOptions{\n\t\t\tFrontHeadroom: network.CalculateFrontHeadroom(conn),\n\t\t\tRearHeadroom:  network.CalculateRearHeadroom(conn),\n\t\t\tMTU:           resolver.SafeDnsPacketSize,\n\t\t}\n\t\treadWaiter, isReadWaiter := bufio.CreatePacketReadWaiter(conn)\n\t\tif isReadWaiter {\n\t\t\treadWaiter.InitializeReadWaiter(rwOptions)\n\t\t}\n\t\tfor {\n\t\t\tvar (\n\t\t\t\treadBuff *buf.Buffer\n\t\t\t\tdest     M.Socksaddr\n\t\t\t\terr      error\n\t\t\t)\n\t\t\t_ = conn.SetReadDeadline(time.Now().Add(resolver.DefaultDnsReadTimeout))\n\t\t\treadBuff = nil // clear last loop status, avoid repeat release\n\t\t\tif isReadWaiter {\n\t\t\t\treadBuff, dest, err = readWaiter.WaitReadPacket()\n\t\t\t} else {\n\t\t\t\treadBuff = rwOptions.NewPacketBuffer()\n\t\t\t\tdest, err = conn.ReadPacket(readBuff)\n\t\t\t\tif readBuff != nil {\n\t\t\t\t\trwOptions.PostReturn(readBuff)\n\t\t\t\t}\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\tif readBuff != nil {\n\t\t\t\t\treadBuff.Release()\n\t\t\t\t}\n\t\t\t\tif sing.ShouldIgnorePacketError(err) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tgo relayDnsPacket(ctx, readBuff, rwOptions, dest, &mutex, &writer)\n\t\t}\n\t\treturn nil\n\t}\n\treturn h.ListenerHandler.NewPacketConnection(ctx, conn, metadata)\n}\n\nfunc relayDnsPacket(ctx context.Context, readBuff *buf.Buffer, rwOptions network.ReadWaitOptions, dest M.Socksaddr, mutex *sync.Mutex, writer *network.PacketWriter) {\n\tctx, cancel := context.WithTimeout(ctx, resolver.DefaultDnsRelayTimeout)\n\tdefer cancel()\n\tinData := readBuff.Bytes()\n\twriteBuff := readBuff\n\twriteBuff.Resize(writeBuff.Start(), 0)\n\tif len(writeBuff.FreeBytes()) < resolver.SafeDnsPacketSize { // only create a new buffer when space don't enough\n\t\twriteBuff = rwOptions.NewPacketBuffer()\n\t}\n\tmsg, err := resolver.RelayDnsPacket(ctx, inData, writeBuff.FreeBytes())\n\tif writeBuff != readBuff {\n\t\treadBuff.Release()\n\t}\n\tif err != nil {\n\t\twriteBuff.Release()\n\t\treturn\n\t}\n\twriteBuff.Truncate(len(msg))\n\tif mutex != nil {\n\t\tmutex.Lock()\n\t\tdefer mutex.Unlock()\n\t}\n\tconn := *writer\n\tif conn == nil {\n\t\twriteBuff.Release()\n\t\treturn\n\t}\n\terr = conn.WritePacket(writeBuff, dest) // WritePacket will release writeBuff\n\tif err != nil {\n\t\twriteBuff.Release()\n\t\treturn\n\t}\n}\n\nfunc (h *ListenerHandler) TypeMutation(typ C.Type) *ListenerHandler {\n\thandle := *h\n\thandle.ListenerHandler = h.ListenerHandler.TypeMutation(typ)\n\treturn &handle\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/sing_tun/iface.go",
    "content": "package sing_tun\n\nimport (\n\t\"net\"\n\t\"net/netip\"\n\n\t\"github.com/metacubex/mihomo/component/iface\"\n\n\t\"github.com/metacubex/sing/common/control\"\n)\n\ntype defaultInterfaceFinder struct{}\n\nvar DefaultInterfaceFinder control.InterfaceFinder = (*defaultInterfaceFinder)(nil)\n\nfunc (f *defaultInterfaceFinder) Update() error {\n\tiface.FlushCache()\n\t_, err := iface.Interfaces()\n\treturn err\n}\n\nfunc (f *defaultInterfaceFinder) Interfaces() []control.Interface {\n\tifaces, err := iface.Interfaces()\n\tif err != nil {\n\t\treturn nil\n\t}\n\tinterfaces := make([]control.Interface, 0, len(ifaces))\n\tfor _, _interface := range ifaces {\n\t\tinterfaces = append(interfaces, control.Interface(*_interface))\n\t}\n\n\treturn interfaces\n}\n\nfunc (f *defaultInterfaceFinder) ByName(name string) (*control.Interface, error) {\n\tnetInterface, err := iface.ResolveInterface(name)\n\tif err == nil {\n\t\treturn (*control.Interface)(netInterface), nil\n\t}\n\tif _, err := net.InterfaceByName(name); err == nil {\n\t\terr = f.Update()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn f.ByName(name)\n\t}\n\treturn nil, err\n}\n\nfunc (f *defaultInterfaceFinder) ByIndex(index int) (*control.Interface, error) {\n\tifaces, err := iface.Interfaces()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tfor _, netInterface := range ifaces {\n\t\tif netInterface.Index == index {\n\t\t\treturn (*control.Interface)(netInterface), nil\n\t\t}\n\t}\n\t_, err = net.InterfaceByIndex(index)\n\tif err == nil {\n\t\terr = f.Update()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn f.ByIndex(index)\n\t}\n\treturn nil, iface.ErrIfaceNotFound\n}\n\nfunc (f *defaultInterfaceFinder) ByAddr(addr netip.Addr) (*control.Interface, error) {\n\tnetInterface, err := iface.ResolveInterfaceByAddr(addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn (*control.Interface)(netInterface), nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/sing_tun/prepare.go",
    "content": "package sing_tun\n\nimport (\n\t\"context\"\n\t\"net/netip\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/component/dialer\"\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\ttun \"github.com/metacubex/sing-tun\"\n\t\"github.com/metacubex/sing-tun/ping\"\n\tM \"github.com/metacubex/sing/common/metadata\"\n\tN \"github.com/metacubex/sing/common/network\"\n)\n\nfunc (h *ListenerHandler) PrepareConnection(network string, source M.Socksaddr, destination M.Socksaddr, routeContext tun.DirectRouteContext, timeout time.Duration) (tun.DirectRouteDestination, error) {\n\tswitch network {\n\tcase N.NetworkICMP: // our fork only send those type to PrepareConnection now\n\t\tif h.DisableICMPForwarding || h.skipPingForwardingByAddr(destination.Addr) { // skip if ICMP handling is disabled or other condition\n\t\t\tlog.Infoln(\"[ICMP] %s %s --> %s using fake ping echo\", network, source, destination)\n\t\t\treturn nil, nil\n\t\t}\n\t\tlog.Infoln(\"[ICMP] %s %s --> %s using DIRECT\", network, source, destination)\n\t\tdirectRouteDestination, err := ping.ConnectDestination(context.TODO(), log.SingLogger, dialer.ICMPControl(destination.Addr), destination.Addr, routeContext, timeout)\n\t\tif err != nil {\n\t\t\tlog.Warnln(\"[ICMP] failed to connect to %s\", destination)\n\t\t\treturn nil, err\n\t\t}\n\t\tlog.Debugln(\"[ICMP] success connect to %s\", destination)\n\t\treturn directRouteDestination, nil\n\t}\n\treturn nil, nil\n}\n\nfunc (h *ListenerHandler) skipPingForwardingByAddr(addr netip.Addr) bool {\n\tfor _, prefix := range h.Inet4Address { // skip in interface ipv4 range\n\t\tif prefix.Contains(addr) {\n\t\t\treturn true\n\t\t}\n\t}\n\tfor _, prefix := range h.Inet6Address { // skip in interface ipv6 range\n\t\tif prefix.Contains(addr) {\n\t\t\treturn true\n\t\t}\n\t}\n\tif resolver.IsFakeIP(addr) { // skip in fakeIp pool\n\t\treturn true\n\t}\n\treturn false\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/sing_tun/redirect_linux.go",
    "content": "package sing_tun\n\nconst supportRedirect = true\n"
  },
  {
    "path": "core/Clash.Meta/listener/sing_tun/redirect_stub.go",
    "content": "//go:build !linux\n\npackage sing_tun\n\nconst supportRedirect = false\n"
  },
  {
    "path": "core/Clash.Meta/listener/sing_tun/server.go",
    "content": "package sing_tun\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/netip\"\n\t\"os\"\n\t\"runtime\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\t\"github.com/metacubex/mihomo/component/dialer\"\n\t\"github.com/metacubex/mihomo/component/iface\"\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tP \"github.com/metacubex/mihomo/constant/provider\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/sing\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"golang.org/x/exp/constraints\"\n\n\ttun \"github.com/metacubex/sing-tun\"\n\t\"github.com/metacubex/sing/common\"\n\t\"github.com/metacubex/sing/common/control\"\n\tE \"github.com/metacubex/sing/common/exceptions\"\n\tF \"github.com/metacubex/sing/common/format\"\n\t\"github.com/metacubex/sing/common/ranges\"\n\n\t\"go4.org/netipx\"\n\t\"golang.org/x/exp/maps\"\n\t\"golang.org/x/exp/slices\"\n)\n\nvar InterfaceName = \"Meta\"\nvar EnforceBindInterface = false\n\nvar (\n\ttunLogMu       sync.Mutex\n\ttunLogLastTime time.Time\n\ttunLogCount    int\n)\n\nfunc shouldLogTun() bool {\n\ttunLogMu.Lock()\n\tdefer tunLogMu.Unlock()\n\n\tnow := time.Now()\n\tif now.Sub(tunLogLastTime) >= time.Second {\n\t\ttunLogLastTime = now\n\t\ttunLogCount = 0\n\t}\n\tif tunLogCount >= 10 {\n\t\treturn false\n\t}\n\ttunLogCount++\n\treturn true\n}\n\ntype Listener struct {\n\tclosed  bool\n\toptions LC.Tun\n\thandler *ListenerHandler\n\ttunName string\n\taddrStr string\n\n\ttunIf    tun.Tun\n\ttunStack tun.Stack\n\n\tnetworkUpdateMonitor    tun.NetworkUpdateMonitor\n\tdefaultInterfaceMonitor tun.DefaultInterfaceMonitor\n\tpackageManager          tun.PackageManager\n\tautoRedirect            tun.AutoRedirect\n\tautoRedirectOutputMark  int32\n\n\tcDialerInterfaceFinder dialer.InterfaceFinder\n\n\truleUpdateCallbackCloser io.Closer\n\truleUpdateMutex          sync.Mutex\n\trouteAddressMap          map[string]*netipx.IPSet\n\trouteExcludeAddressMap   map[string]*netipx.IPSet\n\trouteAddressSet          []*netipx.IPSet\n\trouteExcludeAddressSet   []*netipx.IPSet\n\n\tdnsServerIp []string\n}\n\ntype ListenerHandler struct {\n\t*sing.ListenerHandler\n\tDnsAddrPorts          []netip.AddrPort\n\tInet4Address          []netip.Prefix\n\tInet6Address          []netip.Prefix\n\tDisableICMPForwarding bool\n}\n\nvar emptyAddressSet = []*netipx.IPSet{{}}\n\nfunc CalculateInterfaceName(name string) (tunName string) {\n\tif runtime.GOOS == \"darwin\" {\n\t\ttunName = \"utun\"\n\t} else if name != \"\" {\n\t\ttunName = name\n\t\treturn\n\t} else {\n\t\ttunName = \"tun\"\n\t}\n\tinterfaces, err := net.Interfaces()\n\tif err != nil {\n\t\treturn\n\t}\n\ttunIndex := 0\n\tindexArr := make([]int, 0, len(interfaces))\n\tfor _, netInterface := range interfaces {\n\t\tif strings.HasPrefix(netInterface.Name, tunName) {\n\t\t\tindex, parseErr := strconv.ParseInt(netInterface.Name[len(tunName):], 10, 16)\n\t\t\tif parseErr == nil {\n\t\t\t\tindexArr = append(indexArr, int(index))\n\t\t\t}\n\t\t}\n\t}\n\tslices.Sort(indexArr)\n\tindexArr = slices.Compact(indexArr)\n\tfor _, index := range indexArr {\n\t\tif index == tunIndex {\n\t\t\ttunIndex += 1\n\t\t} else { // indexArr already sorted and distinct, so this tunIndex nobody used\n\t\t\tbreak\n\t\t}\n\t}\n\ttunName = F.ToString(tunName, tunIndex)\n\treturn\n}\n\nfunc checkTunName(tunName string) (ok bool) {\n\tdefer func() {\n\t\tif !ok {\n\t\t\tlog.Warnln(\"[TUN] Unsupported tunName(%s) in %s, force regenerate by ourselves.\", tunName, runtime.GOOS)\n\t\t}\n\t}()\n\tif runtime.GOOS == \"darwin\" {\n\t\tif len(tunName) <= 4 {\n\t\t\treturn false\n\t\t}\n\t\tif tunName[:4] != \"utun\" {\n\t\t\treturn false\n\t\t}\n\t\tif _, parseErr := strconv.ParseInt(tunName[4:], 10, 16); parseErr != nil {\n\t\t\treturn false\n\t\t}\n\t}\n\treturn true\n}\n\nfunc New(options LC.Tun, tunnel C.Tunnel, additions ...inbound.Addition) (l *Listener, err error) {\n\tif len(additions) == 0 {\n\t\tadditions = []inbound.Addition{\n\t\t\tinbound.WithInName(\"DEFAULT-TUN\"),\n\t\t\tinbound.WithSpecialRules(\"\"),\n\t\t}\n\t}\n\tctx := context.TODO()\n\trpTunnel := tunnel.(P.Tunnel)\n\tif options.GSOMaxSize == 0 {\n\t\toptions.GSOMaxSize = 65536\n\t}\n\tif !supportRedirect {\n\t\toptions.AutoRedirect = false\n\t}\n\ttunName := options.Device\n\tif options.FileDescriptor == 0 && (tunName == \"\" || !checkTunName(tunName)) {\n\t\ttunName = CalculateInterfaceName(InterfaceName)\n\t\toptions.Device = tunName\n\t}\n\tforwarderBindInterface := false\n\tif options.FileDescriptor > 0 {\n\t\tif tunnelName, err := getTunnelName(int32(options.FileDescriptor)); err == nil {\n\t\t\ttunName = tunnelName // sing-tun must have the truth tun interface name even it from a fd\n\t\t\t//forwarderBindInterface = true\n\t\t\tlog.Debugln(\"[TUN] use tun name %s for fd %d\", tunnelName, options.FileDescriptor)\n\t\t} else {\n\t\t\tlog.Warnln(\"[TUN] get tun name failed for fd %d, fallback to use tun interface name %s\", options.FileDescriptor, tunName)\n\t\t}\n\t}\n\trouteAddress := options.RouteAddress\n\tif len(options.Inet4RouteAddress) > 0 {\n\t\trouteAddress = append(routeAddress, options.Inet4RouteAddress...)\n\t}\n\tif len(options.Inet6RouteAddress) > 0 {\n\t\trouteAddress = append(routeAddress, options.Inet6RouteAddress...)\n\t}\n\tinet4RouteAddress := common.Filter(routeAddress, func(it netip.Prefix) bool {\n\t\treturn it.Addr().Is4()\n\t})\n\tinet6RouteAddress := common.Filter(routeAddress, func(it netip.Prefix) bool {\n\t\treturn it.Addr().Is6()\n\t})\n\trouteExcludeAddress := options.RouteExcludeAddress\n\tif len(options.Inet4RouteExcludeAddress) > 0 {\n\t\trouteExcludeAddress = append(routeExcludeAddress, options.Inet4RouteExcludeAddress...)\n\t}\n\tif len(options.Inet6RouteExcludeAddress) > 0 {\n\t\trouteExcludeAddress = append(routeExcludeAddress, options.Inet6RouteExcludeAddress...)\n\t}\n\tinet4RouteExcludeAddress := common.Filter(routeExcludeAddress, func(it netip.Prefix) bool {\n\t\treturn it.Addr().Is4()\n\t})\n\tinet6RouteExcludeAddress := common.Filter(routeExcludeAddress, func(it netip.Prefix) bool {\n\t\treturn it.Addr().Is6()\n\t})\n\ttunMTU := options.MTU\n\tif tunMTU == 0 {\n\t\ttunMTU = 9000\n\t}\n\tvar udpTimeout time.Duration\n\tif options.UDPTimeout != 0 {\n\t\tudpTimeout = time.Second * time.Duration(options.UDPTimeout)\n\t} else {\n\t\tudpTimeout = sing.UDPTimeout\n\t}\n\ttableIndex := options.IPRoute2TableIndex\n\tif tableIndex == 0 {\n\t\ttableIndex = tun.DefaultIPRoute2TableIndex\n\t}\n\truleIndex := options.IPRoute2RuleIndex\n\tif ruleIndex == 0 {\n\t\truleIndex = tun.DefaultIPRoute2RuleIndex\n\t}\n\tautoRedirectFallbackRuleIndex := options.AutoRedirectIPRoute2FallbackRuleIndex\n\tif autoRedirectFallbackRuleIndex == 0 {\n\t\tautoRedirectFallbackRuleIndex = tun.DefaultIPRoute2AutoRedirectFallbackRuleIndex\n\t}\n\tinputMark := options.AutoRedirectInputMark\n\tif inputMark == 0 {\n\t\tinputMark = tun.DefaultAutoRedirectInputMark\n\t}\n\toutputMark := options.AutoRedirectOutputMark\n\tif outputMark == 0 {\n\t\toutputMark = tun.DefaultAutoRedirectOutputMark\n\t}\n\tincludeUID := uidToRange(options.IncludeUID)\n\tif len(options.IncludeUIDRange) > 0 {\n\t\tvar err error\n\t\tincludeUID, err = parseRange(includeUID, options.IncludeUIDRange)\n\t\tif err != nil {\n\t\t\treturn nil, E.Cause(err, \"parse include_uid_range\")\n\t\t}\n\t}\n\texcludeUID := uidToRange(options.ExcludeUID)\n\tif len(options.ExcludeUIDRange) > 0 {\n\t\tvar err error\n\t\texcludeUID, err = parseRange(excludeUID, options.ExcludeUIDRange)\n\t\tif err != nil {\n\t\t\treturn nil, E.Cause(err, \"parse exclude_uid_range\")\n\t\t}\n\t}\n\texcludeSrcPort := uidToRange(options.ExcludeSrcPort)\n\tif len(options.ExcludeSrcPortRange) > 0 {\n\t\tvar err error\n\t\texcludeSrcPort, err = parseRange(excludeSrcPort, options.ExcludeSrcPortRange)\n\t\tif err != nil {\n\t\t\treturn nil, E.Cause(err, \"parse exclude_src_port_range\")\n\t\t}\n\t}\n\texcludeDstPort := uidToRange(options.ExcludeDstPort)\n\tif len(options.ExcludeDstPortRange) > 0 {\n\t\tvar err error\n\t\texcludeDstPort, err = parseRange(excludeDstPort, options.ExcludeDstPortRange)\n\t\tif err != nil {\n\t\t\treturn nil, E.Cause(err, \"parse exclude_dst_port_range\")\n\t\t}\n\t}\n\tvar includeMACAddress []net.HardwareAddr\n\tfor _, mac := range options.IncludeMACAddress {\n\t\taddr, err := net.ParseMAC(mac)\n\t\tif err != nil {\n\t\t\treturn nil, E.Cause(err, \"parse include_mac_address\")\n\t\t}\n\t\tincludeMACAddress = append(includeMACAddress, addr)\n\t}\n\tvar excludeMACAddress []net.HardwareAddr\n\tfor _, mac := range options.ExcludeMACAddress {\n\t\taddr, err := net.ParseMAC(mac)\n\t\tif err != nil {\n\t\t\treturn nil, E.Cause(err, \"parse exclude_mac_address\")\n\t\t}\n\t\texcludeMACAddress = append(excludeMACAddress, addr)\n\t}\n\n\tvar dnsAdds []netip.AddrPort\n\n\tfor _, d := range options.DNSHijack {\n\t\tif _, after, ok := strings.Cut(d, \"://\"); ok {\n\t\t\td = after\n\t\t}\n\t\td = strings.Replace(d, \"any\", \"0.0.0.0\", 1)\n\t\taddrPort, err := netip.ParseAddrPort(d)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"parse dns-hijack url error: %w\", err)\n\t\t}\n\n\t\tdnsAdds = append(dnsAdds, addrPort)\n\t}\n\n\tvar dnsServerIp []string\n\tfor _, a := range options.Inet4Address {\n\t\taddrPort := netip.AddrPortFrom(a.Addr().Next(), 53)\n\t\tdnsServerIp = append(dnsServerIp, a.Addr().Next().String())\n\t\tdnsAdds = append(dnsAdds, addrPort)\n\t}\n\tfor _, a := range options.Inet6Address {\n\t\taddrPort := netip.AddrPortFrom(a.Addr().Next(), 53)\n\t\tdnsServerIp = append(dnsServerIp, a.Addr().Next().String())\n\t\tdnsAdds = append(dnsAdds, addrPort)\n\t}\n\n\th, err := sing.NewListenerHandler(sing.ListenerConfig{\n\t\tTunnel:    tunnel,\n\t\tType:      C.TUN,\n\t\tAdditions: additions,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\thandler := &ListenerHandler{\n\t\tListenerHandler:       h,\n\t\tDnsAddrPorts:          dnsAdds,\n\t\tInet4Address:          options.Inet4Address,\n\t\tInet6Address:          options.Inet6Address,\n\t\tDisableICMPForwarding: options.DisableICMPForwarding,\n\t}\n\tl = &Listener{\n\t\tclosed:  false,\n\t\toptions: options,\n\t\thandler: handler,\n\t\ttunName: tunName,\n\t}\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tl.Close()\n\t\t\tl = nil\n\t\t}\n\t}()\n\n\tinterfaceFinder := DefaultInterfaceFinder\n\n\tvar networkUpdateMonitor tun.NetworkUpdateMonitor\n\tvar defaultInterfaceMonitor tun.DefaultInterfaceMonitor\n\tif options.AutoRoute || options.AutoDetectInterface { // don't start NetworkUpdateMonitor because netlink banned by google on Android14+\n\t\tnetworkUpdateMonitor, err = tun.NewNetworkUpdateMonitor(log.SingLogger)\n\t\tif err != nil {\n\t\t\terr = E.Cause(err, \"create NetworkUpdateMonitor\")\n\t\t\treturn\n\t\t}\n\t\tl.networkUpdateMonitor = networkUpdateMonitor\n\t\terr = networkUpdateMonitor.Start()\n\t\tif err != nil {\n\t\t\terr = E.Cause(err, \"start NetworkUpdateMonitor\")\n\t\t\treturn\n\t\t}\n\n\t\toverrideAndroidVPN := true\n\t\tif disable, _ := strconv.ParseBool(os.Getenv(\"DISABLE_OVERRIDE_ANDROID_VPN\")); disable {\n\t\t\toverrideAndroidVPN = false\n\t\t}\n\t\tdefaultInterfaceMonitor, err = tun.NewDefaultInterfaceMonitor(networkUpdateMonitor, log.SingLogger, tun.DefaultInterfaceMonitorOptions{InterfaceFinder: interfaceFinder, OverrideAndroidVPN: overrideAndroidVPN})\n\t\tif err != nil {\n\t\t\terr = E.Cause(err, \"create DefaultInterfaceMonitor\")\n\t\t\treturn\n\t\t}\n\t\tl.defaultInterfaceMonitor = defaultInterfaceMonitor\n\t\tdefaultInterfaceMonitor.RegisterCallback(func(defaultInterface *control.Interface, event int) {\n\t\t\tif defaultInterface != nil {\n\t\t\t\tlog.Warnln(\"[TUN] default interface changed by monitor, => %s\", defaultInterface.Name)\n\t\t\t} else {\n\t\t\t\tlog.Errorln(\"[TUN] default interface lost by monitor\")\n\t\t\t}\n\t\t\tiface.FlushCache()\n\t\t\tresolver.ResetConnection() // reset resolver's connection after default interface changed\n\t\t})\n\t\terr = defaultInterfaceMonitor.Start()\n\t\tif err != nil {\n\t\t\terr = E.Cause(err, \"start DefaultInterfaceMonitor\")\n\t\t\treturn\n\t\t}\n\n\t\tif options.AutoDetectInterface {\n\t\t\tl.cDialerInterfaceFinder = &cDialerInterfaceFinder{\n\t\t\t\ttunName:                 tunName,\n\t\t\t\tdefaultInterfaceMonitor: defaultInterfaceMonitor,\n\t\t\t}\n\t\t\tif !dialer.DefaultInterfaceFinder.CompareAndSwap(nil, l.cDialerInterfaceFinder) {\n\t\t\t\terr = E.New(\"not allowed two tun listener using auto-detect-interface\")\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n\n\ttunOptions := tun.Options{\n\t\tName:                                  tunName,\n\t\tMTU:                                   tunMTU,\n\t\tGSO:                                   options.GSO,\n\t\tInet4Address:                          options.Inet4Address,\n\t\tInet6Address:                          options.Inet6Address,\n\t\tAutoRoute:                             options.AutoRoute,\n\t\tIPRoute2TableIndex:                    tableIndex,\n\t\tIPRoute2RuleIndex:                     ruleIndex,\n\t\tIPRoute2AutoRedirectFallbackRuleIndex: autoRedirectFallbackRuleIndex,\n\t\tAutoRedirectInputMark:                 inputMark,\n\t\tAutoRedirectOutputMark:                outputMark,\n\t\tInet4LoopbackAddress:                  common.Filter(options.LoopbackAddress, netip.Addr.Is4),\n\t\tInet6LoopbackAddress:                  common.Filter(options.LoopbackAddress, netip.Addr.Is6),\n\t\tStrictRoute:                           options.StrictRoute,\n\t\tInet4RouteAddress:                     inet4RouteAddress,\n\t\tInet6RouteAddress:                     inet6RouteAddress,\n\t\tInet4RouteExcludeAddress:              inet4RouteExcludeAddress,\n\t\tInet6RouteExcludeAddress:              inet6RouteExcludeAddress,\n\t\tIncludeInterface:                      options.IncludeInterface,\n\t\tExcludeInterface:                      options.ExcludeInterface,\n\t\tIncludeUID:                            includeUID,\n\t\tExcludeUID:                            excludeUID,\n\t\tExcludeSrcPort:                        excludeSrcPort,\n\t\tExcludeDstPort:                        excludeDstPort,\n\t\tIncludeAndroidUser:                    options.IncludeAndroidUser,\n\t\tIncludePackage:                        options.IncludePackage,\n\t\tExcludePackage:                        options.ExcludePackage,\n\t\tIncludeMACAddress:                     includeMACAddress,\n\t\tExcludeMACAddress:                     excludeMACAddress,\n\t\tFileDescriptor:                        options.FileDescriptor,\n\t\tInterfaceMonitor:                      defaultInterfaceMonitor,\n\t\tEXP_RecvMsgX:                          options.RecvMsgX,\n\t\tEXP_SendMsgX:                          options.SendMsgX,\n\t}\n\n\tif options.AutoRedirect {\n\t\tl.routeAddressMap = make(map[string]*netipx.IPSet)\n\t\tl.routeExcludeAddressMap = make(map[string]*netipx.IPSet)\n\n\t\tif !options.AutoRoute {\n\t\t\treturn nil, E.New(\"`auto-route` is required by `auto-redirect`\")\n\t\t}\n\t\tdisableNFTables, dErr := strconv.ParseBool(os.Getenv(\"DISABLE_NFTABLES\"))\n\t\tl.autoRedirect, err = tun.NewAutoRedirect(tun.AutoRedirectOptions{\n\t\t\tTunOptions:             &tunOptions,\n\t\t\tContext:                ctx,\n\t\t\tHandler:                handler.TypeMutation(C.REDIR),\n\t\t\tLogger:                 log.SingLogger,\n\t\t\tNetworkMonitor:         l.networkUpdateMonitor,\n\t\t\tInterfaceFinder:        interfaceFinder,\n\t\t\tTableName:              \"mihomo\",\n\t\t\tDisableNFTables:        dErr == nil && disableNFTables,\n\t\t\tRouteAddressSet:        &l.routeAddressSet,\n\t\t\tRouteExcludeAddressSet: &l.routeExcludeAddressSet,\n\t\t})\n\t\tif err != nil {\n\t\t\terr = E.Cause(err, \"initialize auto redirect\")\n\t\t\treturn\n\t\t}\n\n\t\tvar markMode bool\n\t\tfor _, routeAddressSet := range options.RouteAddressSet {\n\t\t\trp, loaded := rpTunnel.RuleProviders()[routeAddressSet]\n\t\t\tif !loaded {\n\t\t\t\terr = E.New(\"parse route-address-set: rule-set not found: \", routeAddressSet)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tl.updateRule(rp, false, false)\n\t\t\tmarkMode = true\n\t\t}\n\t\tfor _, routeExcludeAddressSet := range options.RouteExcludeAddressSet {\n\t\t\trp, loaded := rpTunnel.RuleProviders()[routeExcludeAddressSet]\n\t\t\tif !loaded {\n\t\t\t\terr = E.New(\"parse route-exclude_address-set: rule-set not found: \", routeExcludeAddressSet)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tl.updateRule(rp, true, false)\n\t\t\tmarkMode = true\n\t\t}\n\t\tif markMode {\n\t\t\ttunOptions.AutoRedirectMarkMode = true\n\t\t}\n\n\t}\n\n\ttunIf, err := tunNew(tunOptions)\n\tif err != nil {\n\t\terr = E.Cause(err, \"configure tun interface\")\n\t\treturn\n\t}\n\n\tl.dnsServerIp = dnsServerIp\n\t// after tun.New sing-tun has set DNS to TUN interface\n\tresolver.AddSystemDnsBlacklist(dnsServerIp...)\n\n\tstackOptions := tun.StackOptions{\n\t\tContext:                ctx,\n\t\tTun:                    tunIf,\n\t\tTunOptions:             tunOptions,\n\t\tEndpointIndependentNat: options.EndpointIndependentNat,\n\t\tUDPTimeout:             udpTimeout,\n\t\tHandler:                handler,\n\t\tLogger:                 log.SingLogger,\n\t\tForwarderBindInterface: forwarderBindInterface,\n\t\tInterfaceFinder:        interfaceFinder,\n\t\tEnforceBindInterface:   EnforceBindInterface,\n\t}\n\tl.tunIf = tunIf\n\n\ttunStack, err := tun.NewStack(strings.ToLower(options.Stack.String()), stackOptions)\n\tif err != nil {\n\t\treturn\n\t}\n\n\terr = tunStack.Start()\n\tif err != nil {\n\t\treturn\n\t}\n\tl.tunStack = tunStack\n\n\tif l.autoRedirect != nil {\n\t\tif len(l.options.RouteAddressSet) > 0 && len(l.routeAddressSet) == 0 {\n\t\t\tl.routeAddressSet = emptyAddressSet // without this we can't call UpdateRouteAddressSet after Start\n\t\t}\n\t\tif len(l.options.RouteExcludeAddressSet) > 0 && len(l.routeExcludeAddressSet) == 0 {\n\t\t\tl.routeExcludeAddressSet = emptyAddressSet // without this we can't call UpdateRouteAddressSet after Start\n\t\t}\n\t\terr = l.autoRedirect.Start()\n\t\tif err != nil {\n\t\t\terr = E.Cause(err, \"auto redirect\")\n\t\t\treturn\n\t\t}\n\t\tif tunOptions.AutoRedirectMarkMode {\n\t\t\tl.autoRedirectOutputMark = int32(outputMark)\n\t\t\tif !dialer.DefaultRoutingMark.CompareAndSwap(0, l.autoRedirectOutputMark) {\n\t\t\t\terr = E.New(\"not allowed setting global routing-mark when working with autoRedirectMarkMode\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\tl.autoRedirect.UpdateRouteAddressSet()\n\t\t\tl.ruleUpdateCallbackCloser = rpTunnel.RuleUpdateCallback().Register(l.ruleUpdateCallback)\n\t\t}\n\t}\n\n\tif !l.options.AutoDetectInterface {\n\t\tresolver.ResetConnection()\n\t}\n\n\tif options.FileDescriptor != 0 {\n\t\ttunName = fmt.Sprintf(\"%s(fd=%d)\", tunName, options.FileDescriptor)\n\t}\n\tl.addrStr = fmt.Sprintf(\"%s(%s,%s), mtu: %d, auto route: %v, auto redir: %v, ip stack: %s\",\n\t\ttunName, tunOptions.Inet4Address, tunOptions.Inet6Address, tunMTU, options.AutoRoute, options.AutoRedirect, options.Stack)\n\treturn\n}\n\nfunc (l *Listener) ruleUpdateCallback(ruleProvider P.RuleProvider) {\n\tname := ruleProvider.Name()\n\tif slices.Contains(l.options.RouteAddressSet, name) {\n\t\tl.updateRule(ruleProvider, false, true)\n\t\treturn\n\t}\n\tif slices.Contains(l.options.RouteExcludeAddressSet, name) {\n\t\tl.updateRule(ruleProvider, true, true)\n\t\treturn\n\t}\n}\n\ntype toIpCidr interface {\n\tToIpCidr() *netipx.IPSet\n}\n\nfunc (l *Listener) updateRule(ruleProvider P.RuleProvider, exclude bool, update bool) {\n\tl.ruleUpdateMutex.Lock()\n\tdefer l.ruleUpdateMutex.Unlock()\n\tname := ruleProvider.Name()\n\tswitch rp := ruleProvider.Strategy().(type) {\n\tcase toIpCidr:\n\t\tif !exclude {\n\t\t\tipCidr := rp.ToIpCidr()\n\t\t\tif ipCidr != nil {\n\t\t\t\tl.routeAddressMap[name] = ipCidr\n\t\t\t} else {\n\t\t\t\tdelete(l.routeAddressMap, name)\n\t\t\t}\n\t\t\tl.routeAddressSet = maps.Values(l.routeAddressMap)\n\t\t} else {\n\t\t\tipCidr := rp.ToIpCidr()\n\t\t\tif ipCidr != nil {\n\t\t\t\tl.routeExcludeAddressMap[name] = ipCidr\n\t\t\t} else {\n\t\t\t\tdelete(l.routeExcludeAddressMap, name)\n\t\t\t}\n\t\t\tl.routeExcludeAddressSet = maps.Values(l.routeExcludeAddressMap)\n\t\t}\n\tdefault:\n\t\treturn\n\t}\n\tif update && l.autoRedirect != nil {\n\t\tl.autoRedirect.UpdateRouteAddressSet()\n\t}\n}\n\nfunc (l *Listener) OnReload() {\n\tif l.autoRedirectOutputMark != 0 {\n\t\tdialer.DefaultRoutingMark.CompareAndSwap(0, l.autoRedirectOutputMark)\n\t}\n\tif l.cDialerInterfaceFinder != nil {\n\t\tdialer.DefaultInterfaceFinder.CompareAndSwap(nil, l.cDialerInterfaceFinder)\n\t}\n}\n\ntype cDialerInterfaceFinder struct {\n\ttunName                 string\n\tdefaultInterfaceMonitor tun.DefaultInterfaceMonitor\n}\n\nfunc (d *cDialerInterfaceFinder) DefaultInterfaceName(destination netip.Addr) string {\n\tif netInterface, _ := DefaultInterfaceFinder.ByAddr(destination); netInterface != nil {\n\t\treturn netInterface.Name\n\t}\n\tif netInterface := d.defaultInterfaceMonitor.DefaultInterface(); netInterface != nil {\n\t\treturn netInterface.Name\n\t}\n\treturn \"\"\n}\n\nfunc (d *cDialerInterfaceFinder) FindInterfaceName(destination netip.Addr) string {\n\tfor _, dest := range []netip.Addr{destination, netip.IPv4Unspecified(), netip.IPv6Unspecified()} {\n\t\tautoDetectInterfaceName := d.DefaultInterfaceName(dest)\n\t\tif autoDetectInterfaceName == d.tunName {\n\t\t\tif shouldLogTun() {\n\t\t\t\tlog.Warnln(\"[TUN] Auto detect interface for %s get same name with tun\", destination.String())\n\t\t\t}\n\t\t} else if autoDetectInterfaceName == \"\" || autoDetectInterfaceName == \"<nil>\" {\n\t\t\tif shouldLogTun() {\n\t\t\t\tlog.Warnln(\"[TUN] Auto detect interface for %s get empty name.\", destination.String())\n\t\t\t}\n\t\t} else {\n\t\t\tlog.Debugln(\"[TUN] Auto detect interface for %s --> %s\", destination, autoDetectInterfaceName)\n\t\t\treturn autoDetectInterfaceName\n\t\t}\n\t}\n\tif shouldLogTun() {\n\t\tlog.Warnln(\"[TUN] Auto detect interface for %s failed, return '<invalid>' to avoid lookback\", destination)\n\t}\n\treturn \"<invalid>\"\n}\n\nfunc uidToRange[T constraints.Integer](uidList []T) []ranges.Range[T] {\n\treturn common.Map(uidList, func(uid T) ranges.Range[T] {\n\t\treturn ranges.NewSingle(uid)\n\t})\n}\n\nfunc parseRange[T constraints.Integer](uidRanges []ranges.Range[T], rangeList []string) ([]ranges.Range[T], error) {\n\tfor _, uidRange := range rangeList {\n\t\tif !strings.Contains(uidRange, \":\") {\n\t\t\treturn nil, E.New(\"missing ':' in range: \", uidRange)\n\t\t}\n\t\tsubIndex := strings.Index(uidRange, \":\")\n\t\tif subIndex == 0 {\n\t\t\treturn nil, E.New(\"missing range start: \", uidRange)\n\t\t} else if subIndex == len(uidRange)-1 {\n\t\t\treturn nil, E.New(\"missing range end: \", uidRange)\n\t\t}\n\t\tvar start, end uint64\n\t\tvar err error\n\t\tstart, err = strconv.ParseUint(uidRange[:subIndex], 0, 32)\n\t\tif err != nil {\n\t\t\treturn nil, E.Cause(err, \"parse range start\")\n\t\t}\n\t\tend, err = strconv.ParseUint(uidRange[subIndex+1:], 0, 32)\n\t\tif err != nil {\n\t\t\treturn nil, E.Cause(err, \"parse range end\")\n\t\t}\n\t\tuidRanges = append(uidRanges, ranges.New(T(start), T(end)))\n\t}\n\treturn uidRanges, nil\n}\n\nfunc (l *Listener) Close() error {\n\tl.closed = true\n\tresolver.RemoveSystemDnsBlacklist(l.dnsServerIp...)\n\tif l.autoRedirectOutputMark != 0 {\n\t\tdialer.DefaultRoutingMark.CompareAndSwap(l.autoRedirectOutputMark, 0)\n\t}\n\tif l.cDialerInterfaceFinder != nil {\n\t\tdialer.DefaultInterfaceFinder.CompareAndSwap(l.cDialerInterfaceFinder, nil)\n\t}\n\treturn common.Close(\n\t\tl.ruleUpdateCallbackCloser,\n\t\tl.tunStack,\n\t\tl.tunIf,\n\t\tl.autoRedirect,\n\t\tl.defaultInterfaceMonitor,\n\t\tl.networkUpdateMonitor,\n\t\tl.packageManager,\n\t)\n}\n\nfunc (l *Listener) Config() LC.Tun {\n\treturn l.options\n}\n\nfunc (l *Listener) Address() string {\n\treturn l.addrStr\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/sing_tun/server_notwindows.go",
    "content": "//go:build !windows\n\npackage sing_tun\n\nimport (\n\ttun \"github.com/metacubex/sing-tun\"\n)\n\nfunc tunNew(options tun.Options) (tun.Tun, error) {\n\treturn tun.New(options)\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/sing_tun/server_windows.go",
    "content": "package sing_tun\n\nimport (\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/constant/features\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\ttun \"github.com/metacubex/sing-tun\"\n)\n\nfunc tunNew(options tun.Options) (tunIf tun.Tun, err error) {\n\tmaxRetry := 3\n\tfor i := 0; i < maxRetry; i++ {\n\t\ttimeBegin := time.Now()\n\t\ttunIf, err = tun.New(options)\n\t\tif err == nil {\n\t\t\treturn\n\t\t}\n\t\ttimeEnd := time.Now()\n\t\tif timeEnd.Sub(timeBegin) < 1*time.Second { // retrying for \"Cannot create a file when that file already exists.\"\n\t\t\treturn\n\t\t}\n\t\tlog.Warnln(\"Start Tun interface timeout: %s [retrying %d/%d]\", err, i+1, maxRetry)\n\t}\n\treturn\n}\n\nfunc init() {\n\ttun.TunnelType = InterfaceName\n\n\tif features.WindowsMajorVersion < 10 {\n\t\t// to resolve \"bind: The requested address is not valid in its context\"\n\t\tEnforceBindInterface = true\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/sing_tun/tun_name_darwin.go",
    "content": "package sing_tun\n\nimport \"golang.org/x/sys/unix\"\n\nfunc getTunnelName(fd int32) (string, error) {\n\treturn unix.GetsockoptString(\n\t\tint(fd),\n\t\t2, /* #define SYSPROTO_CONTROL 2 */\n\t\t2, /* #define UTUN_OPT_IFNAME 2 */\n\t)\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/sing_tun/tun_name_linux.go",
    "content": "package sing_tun\n\nimport (\n\t\"fmt\"\n\t\"golang.org/x/sys/unix\"\n\t\"syscall\"\n\t\"unsafe\"\n)\n\nconst ifReqSize = unix.IFNAMSIZ + 64\n\nfunc getTunnelName(fd int32) (string, error) {\n\tvar ifr [ifReqSize]byte\n\tvar errno syscall.Errno\n\t_, _, errno = unix.Syscall(\n\t\tunix.SYS_IOCTL,\n\t\tuintptr(fd),\n\t\tuintptr(unix.TUNGETIFF),\n\t\tuintptr(unsafe.Pointer(&ifr[0])),\n\t)\n\tif errno != 0 {\n\t\treturn \"\", fmt.Errorf(\"failed to get name of TUN device: %w\", errno)\n\t}\n\treturn unix.ByteSliceToString(ifr[:]), nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/sing_tun/tun_name_other.go",
    "content": "//go:build !(darwin || linux)\n\npackage sing_tun\n\nimport \"os\"\n\nfunc getTunnelName(fd int32) (string, error) {\n\treturn \"\", os.ErrInvalid\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/sing_vless/server.go",
    "content": "package sing_vless\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\t\"github.com/metacubex/mihomo/component/ca\"\n\t\"github.com/metacubex/mihomo/component/ech\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/reality\"\n\t\"github.com/metacubex/mihomo/listener/sing\"\n\t\"github.com/metacubex/mihomo/ntp\"\n\t\"github.com/metacubex/mihomo/transport/gun\"\n\t\"github.com/metacubex/mihomo/transport/vless/encryption\"\n\tmihomoVMess \"github.com/metacubex/mihomo/transport/vmess\"\n\t\"github.com/metacubex/mihomo/transport/xhttp\"\n\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/sing/common\"\n\t\"github.com/metacubex/sing/common/metadata\"\n\t\"github.com/metacubex/tls\"\n\t\"golang.org/x/exp/slices\"\n)\n\ntype Listener struct {\n\tclosed     bool\n\tconfig     LC.VlessServer\n\tlisteners  []net.Listener\n\tservice    *Service[string]\n\tdecryption *encryption.ServerInstance\n}\n\nfunc New(config LC.VlessServer, tunnel C.Tunnel, additions ...inbound.Addition) (sl *Listener, err error) {\n\tif len(additions) == 0 {\n\t\tadditions = []inbound.Addition{\n\t\t\tinbound.WithInName(\"DEFAULT-VLESS\"),\n\t\t\tinbound.WithSpecialRules(\"\"),\n\t\t}\n\t}\n\th, err := sing.NewListenerHandler(sing.ListenerConfig{\n\t\tTunnel:    tunnel,\n\t\tType:      C.VLESS,\n\t\tAdditions: additions,\n\t\tMuxOption: config.MuxOption,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tservice := NewService[string](h)\n\tservice.UpdateUsers(\n\t\tcommon.Map(config.Users, func(it LC.VlessUser) string {\n\t\t\treturn it.Username\n\t\t}),\n\t\tcommon.Map(config.Users, func(it LC.VlessUser) string {\n\t\t\treturn it.UUID\n\t\t}),\n\t\tcommon.Map(config.Users, func(it LC.VlessUser) string {\n\t\t\treturn it.Flow\n\t\t}))\n\n\tsl = &Listener{config: config, service: service}\n\n\tsl.decryption, err = encryption.NewServer(config.Decryption)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif sl.decryption != nil {\n\t\tdefer func() { // decryption must be closed to avoid the goroutine leak\n\t\t\tif err != nil {\n\t\t\t\t_ = sl.decryption.Close()\n\t\t\t\tsl.decryption = nil\n\t\t\t}\n\t\t}()\n\t}\n\n\thttpServer := http.Server{\n\t\tIdleTimeout: 30 * time.Second,\n\t\tProtocols:   new(http.Protocols),\n\t}\n\ttlsConfig := &tls.Config{Time: ntp.Now}\n\tvar realityBuilder *reality.Builder\n\n\tif config.Certificate != \"\" && config.PrivateKey != \"\" {\n\t\tcertLoader, err := ca.NewTLSKeyPairLoader(config.Certificate, config.PrivateKey)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttlsConfig.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) {\n\t\t\treturn certLoader()\n\t\t}\n\n\t\tif config.EchKey != \"\" {\n\t\t\terr = ech.LoadECHKey(config.EchKey, tlsConfig)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\ttlsConfig.ClientAuth = ca.ClientAuthTypeFromString(config.ClientAuthType)\n\tif len(config.ClientAuthCert) > 0 {\n\t\tif tlsConfig.ClientAuth == tls.NoClientCert {\n\t\t\ttlsConfig.ClientAuth = tls.RequireAndVerifyClientCert\n\t\t}\n\t}\n\tif tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert {\n\t\tpool, err := ca.LoadCertificates(config.ClientAuthCert)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttlsConfig.ClientCAs = pool\n\t}\n\tif config.RealityConfig.PrivateKey != \"\" {\n\t\tif tlsConfig.GetCertificate != nil {\n\t\t\treturn nil, errors.New(\"certificate is unavailable in reality\")\n\t\t}\n\t\tif tlsConfig.ClientAuth != tls.NoClientCert {\n\t\t\treturn nil, errors.New(\"client-auth is unavailable in reality\")\n\t\t}\n\t\trealityBuilder, err = config.RealityConfig.Build(tunnel)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tif config.WsPath != \"\" {\n\t\thttpMux := http.NewServeMux()\n\t\thttpMux.HandleFunc(config.WsPath, func(w http.ResponseWriter, r *http.Request) {\n\t\t\tconn, err := mihomoVMess.StreamUpgradedWebsocketConn(w, r)\n\t\t\tif err != nil {\n\t\t\t\thttp.Error(w, err.Error(), 500)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tsl.HandleConn(conn, tunnel, additions...)\n\t\t})\n\t\thttpServer.Handler = httpMux\n\t\thttpServer.Protocols.SetHTTP1(true)\n\t\ttlsConfig.NextProtos = append(tlsConfig.NextProtos, \"http/1.1\")\n\t}\n\tif config.GrpcServiceName != \"\" {\n\t\thttpServer.Handler = gun.NewServerHandler(gun.ServerOption{\n\t\t\tServiceName: config.GrpcServiceName,\n\t\t\tConnHandler: func(conn net.Conn) {\n\t\t\t\tsl.HandleConn(conn, tunnel, additions...)\n\t\t\t},\n\t\t\tHttpHandler: httpServer.Handler,\n\t\t})\n\t\thttpServer.Protocols.SetHTTP2(true)\n\t\t// SetUnencryptedHTTP2 to ensure we can work in plain http2 and some tls conn is not *tls.Conn (like *reality.Conn)\n\t\thttpServer.Protocols.SetUnencryptedHTTP2(true)\n\t\ttlsConfig.NextProtos = append([]string{\"h2\"}, tlsConfig.NextProtos...) // h2 must before http/1.1\n\t}\n\tif config.XHTTPConfig.Mode != \"\" {\n\t\tswitch config.XHTTPConfig.Mode {\n\t\tcase \"auto\", \"stream-up\", \"stream-one\", \"packet-up\":\n\t\tdefault:\n\t\t\treturn nil, errors.New(\"unsupported xhttp mode\")\n\t\t}\n\t}\n\tif config.XHTTPConfig.Path != \"\" || config.XHTTPConfig.Host != \"\" || config.XHTTPConfig.Mode != \"\" {\n\t\thttpServer.Handler, err = xhttp.NewServerHandler(xhttp.ServerOption{\n\t\t\tConfig: xhttp.Config{\n\t\t\t\tHost:                 config.XHTTPConfig.Host,\n\t\t\t\tPath:                 config.XHTTPConfig.Path,\n\t\t\t\tMode:                 config.XHTTPConfig.Mode,\n\t\t\t\tXPaddingBytes:        config.XHTTPConfig.XPaddingBytes,\n\t\t\t\tXPaddingObfsMode:     config.XHTTPConfig.XPaddingObfsMode,\n\t\t\t\tXPaddingKey:          config.XHTTPConfig.XPaddingKey,\n\t\t\t\tXPaddingHeader:       config.XHTTPConfig.XPaddingHeader,\n\t\t\t\tXPaddingPlacement:    config.XHTTPConfig.XPaddingPlacement,\n\t\t\t\tXPaddingMethod:       config.XHTTPConfig.XPaddingMethod,\n\t\t\t\tUplinkHTTPMethod:     config.XHTTPConfig.UplinkHTTPMethod,\n\t\t\t\tSessionPlacement:     config.XHTTPConfig.SessionPlacement,\n\t\t\t\tSessionKey:           config.XHTTPConfig.SessionKey,\n\t\t\t\tSeqPlacement:         config.XHTTPConfig.SeqPlacement,\n\t\t\t\tSeqKey:               config.XHTTPConfig.SeqKey,\n\t\t\t\tUplinkDataPlacement:  config.XHTTPConfig.UplinkDataPlacement,\n\t\t\t\tUplinkDataKey:        config.XHTTPConfig.UplinkDataKey,\n\t\t\t\tUplinkChunkSize:      config.XHTTPConfig.UplinkChunkSize,\n\t\t\t\tNoSSEHeader:          config.XHTTPConfig.NoSSEHeader,\n\t\t\t\tScStreamUpServerSecs: config.XHTTPConfig.ScStreamUpServerSecs,\n\t\t\t\tScMaxBufferedPosts:   config.XHTTPConfig.ScMaxBufferedPosts,\n\t\t\t\tScMaxEachPostBytes:   config.XHTTPConfig.ScMaxEachPostBytes,\n\t\t\t},\n\t\t\tConnHandler: func(conn net.Conn) {\n\t\t\t\tsl.HandleConn(conn, tunnel, additions...)\n\t\t\t},\n\t\t\tHttpHandler: httpServer.Handler,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\thttpServer.Protocols.SetHTTP1(true)\n\t\thttpServer.Protocols.SetHTTP2(true)\n\t\t// SetUnencryptedHTTP2 to ensure we can work in plain http2 and some tls conn is not *tls.Conn (like *reality.Conn)\n\t\thttpServer.Protocols.SetUnencryptedHTTP2(true)\n\t\tif !slices.Contains(tlsConfig.NextProtos, \"http/1.1\") {\n\t\t\ttlsConfig.NextProtos = append([]string{\"http/1.1\"}, tlsConfig.NextProtos...)\n\t\t}\n\t\tif !slices.Contains(tlsConfig.NextProtos, \"h2\") {\n\t\t\ttlsConfig.NextProtos = append([]string{\"h2\"}, tlsConfig.NextProtos...)\n\t\t}\n\t}\n\tfor _, addr := range strings.Split(config.Listen, \",\") {\n\t\taddr := addr\n\n\t\t//TCP\n\t\tl, err := inbound.Listen(\"tcp\", addr)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif realityBuilder != nil {\n\t\t\tl = realityBuilder.NewListener(l)\n\t\t} else if tlsConfig.GetCertificate != nil {\n\t\t\tl = tls.NewListener(l, tlsConfig)\n\t\t} else if sl.decryption == nil {\n\t\t\treturn nil, errors.New(\"disallow using Vless without any certificates/reality/decryption config\")\n\t\t}\n\t\tsl.listeners = append(sl.listeners, l)\n\n\t\tgo func() {\n\t\t\tif httpServer.Handler != nil {\n\t\t\t\t_ = httpServer.Serve(l)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfor {\n\t\t\t\tc, err := l.Accept()\n\t\t\t\tif err != nil {\n\t\t\t\t\tif sl.closed {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tgo sl.HandleConn(c, tunnel)\n\t\t\t}\n\t\t}()\n\t}\n\n\treturn sl, nil\n}\n\nfunc (l *Listener) Close() error {\n\tl.closed = true\n\tvar retErr error\n\tfor _, lis := range l.listeners {\n\t\terr := lis.Close()\n\t\tif err != nil {\n\t\t\tretErr = err\n\t\t}\n\t}\n\tif l.decryption != nil {\n\t\t_ = l.decryption.Close()\n\t}\n\treturn retErr\n}\n\nfunc (l *Listener) Config() string {\n\treturn l.config.String()\n}\n\nfunc (l *Listener) AddrList() (addrList []net.Addr) {\n\tfor _, lis := range l.listeners {\n\t\taddrList = append(addrList, lis.Addr())\n\t}\n\treturn\n}\n\nfunc (l *Listener) HandleConn(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) {\n\tctx := sing.WithAdditions(context.TODO(), additions...)\n\tif l.decryption != nil {\n\t\tvar err error\n\t\tconn, err = l.decryption.Handshake(conn, nil)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\terr := l.service.NewConnection(ctx, conn, metadata.Metadata{\n\t\tProtocol: \"vless\",\n\t\tSource:   metadata.SocksaddrFromNet(conn.RemoteAddr()),\n\t})\n\tif err != nil {\n\t\t_ = conn.Close()\n\t\treturn\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/sing_vless/service.go",
    "content": "package sing_vless\n\n// copy and modify from https://github.com/SagerNet/sing-vmess/tree/3c1cf255413250b09a57e4ecdf1def1fa505e3cc/vless\n\nimport (\n\t\"context\"\n\t\"encoding/binary\"\n\t\"io\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"github.com/metacubex/mihomo/transport/vless\"\n\t\"github.com/metacubex/mihomo/transport/vless/vision\"\n\n\t\"github.com/gofrs/uuid/v5\"\n\t\"github.com/metacubex/sing-vmess\"\n\t\"github.com/metacubex/sing/common/auth\"\n\t\"github.com/metacubex/sing/common/buf\"\n\t\"github.com/metacubex/sing/common/bufio\"\n\tE \"github.com/metacubex/sing/common/exceptions\"\n\tM \"github.com/metacubex/sing/common/metadata\"\n\tN \"github.com/metacubex/sing/common/network\"\n\t\"google.golang.org/protobuf/proto\"\n)\n\ntype Service[T comparable] struct {\n\tuserMap  map[[16]byte]T\n\tuserFlow map[T]string\n\thandler  Handler\n}\n\ntype Handler interface {\n\tN.TCPConnectionHandler\n\tN.UDPConnectionHandler\n\tE.Handler\n}\n\nfunc NewService[T comparable](handler Handler) *Service[T] {\n\treturn &Service[T]{\n\t\thandler: handler,\n\t}\n}\n\nfunc (s *Service[T]) UpdateUsers(userList []T, userUUIDList []string, userFlowList []string) {\n\tuserMap := make(map[[16]byte]T)\n\tuserFlowMap := make(map[T]string)\n\tfor i, userName := range userList {\n\t\tuserID := utils.UUIDMap(userUUIDList[i])\n\t\tuserMap[userID] = userName\n\t\tuserFlowMap[userName] = userFlowList[i]\n\t}\n\ts.userMap = userMap\n\ts.userFlow = userFlowMap\n}\n\nvar _ N.TCPConnectionHandler = (*Service[int])(nil)\n\nfunc (s *Service[T]) NewConnection(ctx context.Context, conn net.Conn, metadata M.Metadata) error {\n\tvar version uint8\n\terr := binary.Read(conn, binary.BigEndian, &version)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif version != vless.Version {\n\t\treturn E.New(\"unknown version: \", version)\n\t}\n\n\tvar requestUUID [16]byte\n\t_, err = io.ReadFull(conn, requestUUID[:])\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar addonsLen uint8\n\terr = binary.Read(conn, binary.BigEndian, &addonsLen)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar addons vless.Addons\n\tif addonsLen > 0 {\n\t\taddonsBytes := make([]byte, addonsLen)\n\t\t_, err = io.ReadFull(conn, addonsBytes)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\terr = proto.Unmarshal(addonsBytes, &addons)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tvar command byte\n\terr = binary.Read(conn, binary.BigEndian, &command)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tvar destination M.Socksaddr\n\tif command != vless.CommandMux {\n\t\tdestination, err = vmess.AddressSerializer.ReadAddrPort(conn)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\n\tuser, loaded := s.userMap[requestUUID]\n\tif !loaded {\n\t\treturn E.New(\"unknown UUID: \", uuid.FromBytesOrNil(requestUUID[:]))\n\t}\n\tctx = auth.ContextWithUser(ctx, user)\n\tmetadata.Destination = destination\n\n\tuserFlow := s.userFlow[user]\n\trequestFlow := addons.Flow\n\tif requestFlow != userFlow && requestFlow != \"\" {\n\t\treturn E.New(\"flow mismatch: expected \", flowName(userFlow), \", but got \", flowName(requestFlow))\n\t}\n\n\tresponseConn := &serverConn{ExtendedConn: bufio.NewExtendedConn(conn)}\n\tswitch requestFlow {\n\tcase vless.XRV:\n\t\tconn, err = vision.NewConn(responseConn, conn, requestUUID)\n\t\tif err != nil {\n\t\t\treturn E.Cause(err, \"initialize vision\")\n\t\t}\n\tcase \"\":\n\t\tconn = responseConn\n\tdefault:\n\t\treturn E.New(\"unknown flow: \", requestFlow)\n\t}\n\tswitch command {\n\tcase vless.CommandTCP:\n\t\treturn s.handler.NewConnection(ctx, conn, metadata)\n\tcase vless.CommandUDP:\n\t\tif requestFlow == vless.XRV {\n\t\t\treturn E.New(vless.XRV, \" flow does not support UDP\")\n\t\t}\n\t\treturn s.handler.NewPacketConnection(ctx, &serverPacketConn{ExtendedConn: bufio.NewExtendedConn(conn), destination: destination}, metadata)\n\tcase vless.CommandMux:\n\t\treturn vmess.HandleMuxConnection(ctx, conn, metadata, s.handler)\n\tdefault:\n\t\treturn E.New(\"unknown command: \", command)\n\t}\n}\n\nfunc flowName(value string) string {\n\tif value == \"\" {\n\t\treturn \"none\"\n\t}\n\treturn value\n}\n\ntype serverConn struct {\n\tN.ExtendedConn\n\tresponseWritten bool\n}\n\nfunc (c *serverConn) Write(b []byte) (n int, err error) {\n\tif !c.responseWritten {\n\t\tbuffer := buf.NewSize(2 + len(b))\n\t\tbuffer.WriteByte(vless.Version)\n\t\tbuffer.WriteByte(0)\n\t\tbuffer.Write(b)\n\t\t_, err = c.ExtendedConn.Write(buffer.Bytes())\n\t\tbuffer.Release()\n\t\tif err == nil {\n\t\t\tn = len(b)\n\t\t}\n\t\tc.responseWritten = true\n\t\treturn\n\t}\n\treturn c.ExtendedConn.Write(b)\n}\n\nfunc (c *serverConn) WriteBuffer(buffer *buf.Buffer) error {\n\tif !c.responseWritten {\n\t\theader := buffer.ExtendHeader(2)\n\t\theader[0] = vless.Version\n\t\theader[1] = 0\n\t\tc.responseWritten = true\n\t}\n\treturn c.ExtendedConn.WriteBuffer(buffer)\n}\n\nfunc (c *serverConn) FrontHeadroom() int {\n\tif c.responseWritten {\n\t\treturn 0\n\t}\n\treturn 2\n}\n\nfunc (c *serverConn) ReaderReplaceable() bool {\n\treturn true\n}\n\nfunc (c *serverConn) WriterReplaceable() bool {\n\treturn c.responseWritten\n}\n\nfunc (c *serverConn) NeedAdditionalReadDeadline() bool {\n\treturn true\n}\n\nfunc (c *serverConn) Upstream() any {\n\treturn c.ExtendedConn\n}\n\ntype serverPacketConn struct {\n\tN.ExtendedConn\n\tdestination     M.Socksaddr\n\treadWaitOptions N.ReadWaitOptions\n}\n\nfunc (c *serverPacketConn) InitializeReadWaiter(options N.ReadWaitOptions) (needCopy bool) {\n\tc.readWaitOptions = options\n\treturn false\n}\n\nfunc (c *serverPacketConn) WaitReadPacket() (buffer *buf.Buffer, destination M.Socksaddr, err error) {\n\tvar packetLen uint16\n\terr = binary.Read(c.ExtendedConn, binary.BigEndian, &packetLen)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tbuffer = c.readWaitOptions.NewPacketBuffer()\n\t_, err = buffer.ReadFullFrom(c.ExtendedConn, int(packetLen))\n\tif err != nil {\n\t\tbuffer.Release()\n\t\treturn\n\t}\n\tc.readWaitOptions.PostReturn(buffer)\n\n\tdestination = c.destination\n\treturn\n}\n\nfunc (c *serverPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {\n\tvar packetLen uint16\n\terr = binary.Read(c.ExtendedConn, binary.BigEndian, &packetLen)\n\tif err != nil {\n\t\treturn\n\t}\n\tif len(p) < int(packetLen) {\n\t\terr = io.ErrShortBuffer\n\t\treturn\n\t}\n\tn, err = io.ReadFull(c.ExtendedConn, p[:packetLen])\n\tif err != nil {\n\t\treturn\n\t}\n\tif c.destination.IsFqdn() {\n\t\taddr = c.destination\n\t} else {\n\t\taddr = c.destination.UDPAddr()\n\t}\n\treturn\n}\n\nfunc (c *serverPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {\n\terr = binary.Write(c.ExtendedConn, binary.BigEndian, uint16(len(p)))\n\tif err != nil {\n\t\treturn\n\t}\n\treturn c.ExtendedConn.Write(p)\n}\n\nfunc (c *serverPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {\n\tvar packetLen uint16\n\terr = binary.Read(c.ExtendedConn, binary.BigEndian, &packetLen)\n\tif err != nil {\n\t\treturn\n\t}\n\n\t_, err = buffer.ReadFullFrom(c.ExtendedConn, int(packetLen))\n\tif err != nil {\n\t\treturn\n\t}\n\n\tdestination = c.destination\n\treturn\n}\n\nfunc (c *serverPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {\n\tpacketLen := buffer.Len()\n\tbinary.BigEndian.PutUint16(buffer.ExtendHeader(2), uint16(packetLen))\n\treturn c.ExtendedConn.WriteBuffer(buffer)\n}\n\nfunc (c *serverPacketConn) FrontHeadroom() int {\n\treturn 2\n}\n\nfunc (c *serverPacketConn) NeedAdditionalReadDeadline() bool {\n\treturn true\n}\n\nfunc (c *serverPacketConn) Upstream() any {\n\treturn c.ExtendedConn\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/sing_vmess/server.go",
    "content": "package sing_vmess\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\t\"github.com/metacubex/mihomo/component/ca\"\n\t\"github.com/metacubex/mihomo/component/ech\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/reality\"\n\t\"github.com/metacubex/mihomo/listener/sing\"\n\t\"github.com/metacubex/mihomo/ntp\"\n\t\"github.com/metacubex/mihomo/transport/gun\"\n\tmihomoVMess \"github.com/metacubex/mihomo/transport/vmess\"\n\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/mhurl\"\n\tvmess \"github.com/metacubex/sing-vmess\"\n\t\"github.com/metacubex/sing/common\"\n\t\"github.com/metacubex/sing/common/metadata\"\n\t\"github.com/metacubex/tls\"\n)\n\ntype Listener struct {\n\tclosed    bool\n\tconfig    LC.VmessServer\n\tlisteners []net.Listener\n\tservice   *vmess.Service[string]\n}\n\nvar _listener *Listener\n\nfunc New(config LC.VmessServer, tunnel C.Tunnel, additions ...inbound.Addition) (sl *Listener, err error) {\n\tif len(additions) == 0 {\n\t\tadditions = []inbound.Addition{\n\t\t\tinbound.WithInName(\"DEFAULT-VMESS\"),\n\t\t\tinbound.WithSpecialRules(\"\"),\n\t\t}\n\t\tdefer func() {\n\t\t\t_listener = sl\n\t\t}()\n\t}\n\th, err := sing.NewListenerHandler(sing.ListenerConfig{\n\t\tTunnel:    tunnel,\n\t\tType:      C.VMESS,\n\t\tAdditions: additions,\n\t\tMuxOption: config.MuxOption,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tservice := vmess.NewService[string](h, vmess.ServiceWithDisableHeaderProtection(), vmess.ServiceWithTimeFunc(ntp.Now))\n\terr = service.UpdateUsers(\n\t\tcommon.Map(config.Users, func(it LC.VmessUser) string {\n\t\t\treturn it.Username\n\t\t}),\n\t\tcommon.Map(config.Users, func(it LC.VmessUser) string {\n\t\t\treturn it.UUID\n\t\t}),\n\t\tcommon.Map(config.Users, func(it LC.VmessUser) int {\n\t\t\treturn it.AlterID\n\t\t}))\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\terr = service.Start()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsl = &Listener{false, config, nil, service}\n\n\thttpServer := http.Server{\n\t\tIdleTimeout: 30 * time.Second,\n\t\tProtocols:   new(http.Protocols),\n\t}\n\ttlsConfig := &tls.Config{Time: ntp.Now}\n\tvar realityBuilder *reality.Builder\n\n\tif config.Certificate != \"\" && config.PrivateKey != \"\" {\n\t\tcertLoader, err := ca.NewTLSKeyPairLoader(config.Certificate, config.PrivateKey)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttlsConfig.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) {\n\t\t\treturn certLoader()\n\t\t}\n\n\t\tif config.EchKey != \"\" {\n\t\t\terr = ech.LoadECHKey(config.EchKey, tlsConfig)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\ttlsConfig.ClientAuth = ca.ClientAuthTypeFromString(config.ClientAuthType)\n\tif len(config.ClientAuthCert) > 0 {\n\t\tif tlsConfig.ClientAuth == tls.NoClientCert {\n\t\t\ttlsConfig.ClientAuth = tls.RequireAndVerifyClientCert\n\t\t}\n\t}\n\tif tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert {\n\t\tpool, err := ca.LoadCertificates(config.ClientAuthCert)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttlsConfig.ClientCAs = pool\n\t}\n\tif config.RealityConfig.PrivateKey != \"\" {\n\t\tif tlsConfig.GetCertificate != nil {\n\t\t\treturn nil, errors.New(\"certificate is unavailable in reality\")\n\t\t}\n\t\tif tlsConfig.ClientAuth != tls.NoClientCert {\n\t\t\treturn nil, errors.New(\"client-auth is unavailable in reality\")\n\t\t}\n\t\trealityBuilder, err = config.RealityConfig.Build(tunnel)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tif config.WsPath != \"\" {\n\t\thttpMux := http.NewServeMux()\n\t\thttpMux.HandleFunc(config.WsPath, func(w http.ResponseWriter, r *http.Request) {\n\t\t\tconn, err := mihomoVMess.StreamUpgradedWebsocketConn(w, r)\n\t\t\tif err != nil {\n\t\t\t\thttp.Error(w, err.Error(), 500)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tsl.HandleConn(conn, tunnel, additions...)\n\t\t})\n\t\thttpServer.Handler = httpMux\n\t\thttpServer.Protocols.SetHTTP1(true)\n\t\ttlsConfig.NextProtos = append(tlsConfig.NextProtos, \"http/1.1\")\n\t}\n\tif config.GrpcServiceName != \"\" {\n\t\thttpServer.Handler = gun.NewServerHandler(gun.ServerOption{\n\t\t\tServiceName: config.GrpcServiceName,\n\t\t\tConnHandler: func(conn net.Conn) {\n\t\t\t\tsl.HandleConn(conn, tunnel, additions...)\n\t\t\t},\n\t\t\tHttpHandler: httpServer.Handler,\n\t\t})\n\t\thttpServer.Protocols.SetHTTP2(true)\n\t\t// SetUnencryptedHTTP2 to ensure we can work in plain http2 and some tls conn is not *tls.Conn (like *reality.Conn)\n\t\thttpServer.Protocols.SetUnencryptedHTTP2(true)\n\t\ttlsConfig.NextProtos = append([]string{\"h2\"}, tlsConfig.NextProtos...) // h2 must before http/1.1\n\t}\n\n\tfor _, addr := range strings.Split(config.Listen, \",\") {\n\t\taddr := addr\n\n\t\t//TCP\n\t\tl, err := inbound.Listen(\"tcp\", addr)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif realityBuilder != nil {\n\t\t\tl = realityBuilder.NewListener(l)\n\t\t} else if tlsConfig.GetCertificate != nil {\n\t\t\tl = tls.NewListener(l, tlsConfig)\n\t\t}\n\t\tsl.listeners = append(sl.listeners, l)\n\n\t\tgo func() {\n\t\t\tif httpServer.Handler != nil {\n\t\t\t\t_ = httpServer.Serve(l)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfor {\n\t\t\t\tc, err := l.Accept()\n\t\t\t\tif err != nil {\n\t\t\t\t\tif sl.closed {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tgo sl.HandleConn(c, tunnel)\n\t\t\t}\n\t\t}()\n\t}\n\n\treturn sl, nil\n}\n\nfunc (l *Listener) Close() error {\n\tl.closed = true\n\tvar retErr error\n\tfor _, lis := range l.listeners {\n\t\terr := lis.Close()\n\t\tif err != nil {\n\t\t\tretErr = err\n\t\t}\n\t}\n\terr := l.service.Close()\n\tif err != nil {\n\t\tretErr = err\n\t}\n\treturn retErr\n}\n\nfunc (l *Listener) Config() string {\n\treturn l.config.String()\n}\n\nfunc (l *Listener) AddrList() (addrList []net.Addr) {\n\tfor _, lis := range l.listeners {\n\t\taddrList = append(addrList, lis.Addr())\n\t}\n\treturn\n}\n\nfunc (l *Listener) HandleConn(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) {\n\tctx := sing.WithAdditions(context.TODO(), additions...)\n\terr := l.service.NewConnection(ctx, conn, metadata.Metadata{\n\t\tProtocol: \"vmess\",\n\t\tSource:   metadata.SocksaddrFromNet(conn.RemoteAddr()),\n\t})\n\tif err != nil {\n\t\t_ = conn.Close()\n\t\treturn\n\t}\n}\n\nfunc HandleVmess(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) bool {\n\tif _listener != nil && _listener.service != nil {\n\t\tgo _listener.HandleConn(conn, tunnel, additions...)\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc ParseVmessURL(s string) (addr, username, password string, err error) {\n\tu, err := mhurl.Parse(s) // we need multiple hosts url supports\n\tif err != nil {\n\t\treturn\n\t}\n\n\taddr = u.Host\n\tif u.User != nil {\n\t\tusername = u.User.Username()\n\t\tpassword, _ = u.User.Password()\n\t}\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/sing_vmess/server_test.go",
    "content": "package sing_vmess\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestParseVmessURL(t *testing.T) {\n\tfor _, test := range []struct{ username, passwd, hosts string }{\n\t\t{username: \"username\", passwd: \"password\", hosts: \":1000,:2000,:3000\"},\n\t\t{username: \"username\", passwd: \"password\", hosts: \"127.0.0.1:1000,127.0.0.1:2000,127.0.0.1:3000\"},\n\t\t{username: \"username\", passwd: \"password\", hosts: \"[::1]:1000,[::1]:2000,[::1]:3000\"},\n\t} {\n\t\taddr, username, password, err := ParseVmessURL(fmt.Sprintf(\"vmess://%s:%s@%s\", test.username, test.passwd, test.hosts))\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, test.hosts, addr)\n\t\trequire.Equal(t, test.username, username)\n\t\trequire.Equal(t, test.passwd, password)\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/socks/tcp.go",
    "content": "package socks\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/component/auth\"\n\t\"github.com/metacubex/mihomo/component/ca\"\n\t\"github.com/metacubex/mihomo/component/ech\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tauthStore \"github.com/metacubex/mihomo/listener/auth\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/reality\"\n\t\"github.com/metacubex/mihomo/ntp\"\n\t\"github.com/metacubex/mihomo/transport/socks4\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n\n\t\"github.com/metacubex/tls\"\n)\n\ntype Listener struct {\n\tlistener net.Listener\n\taddr     string\n\tclosed   bool\n}\n\n// RawAddress implements C.Listener\nfunc (l *Listener) RawAddress() string {\n\treturn l.addr\n}\n\n// Address implements C.Listener\nfunc (l *Listener) Address() string {\n\treturn l.listener.Addr().String()\n}\n\n// Close implements C.Listener\nfunc (l *Listener) Close() error {\n\tl.closed = true\n\treturn l.listener.Close()\n}\n\nfunc New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {\n\treturn NewWithConfig(LC.AuthServer{Enable: true, Listen: addr, AuthStore: authStore.Default}, tunnel, additions...)\n}\n\nfunc NewWithConfig(config LC.AuthServer, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {\n\tisDefault := false\n\tif len(additions) == 0 {\n\t\tisDefault = true\n\t\tadditions = []inbound.Addition{\n\t\t\tinbound.WithInName(\"DEFAULT-SOCKS\"),\n\t\t\tinbound.WithSpecialRules(\"\"),\n\t\t}\n\t}\n\n\tl, err := inbound.Listen(\"tcp\", config.Listen)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttlsConfig := &tls.Config{Time: ntp.Now}\n\tvar realityBuilder *reality.Builder\n\n\tif config.Certificate != \"\" && config.PrivateKey != \"\" {\n\t\tcertLoader, err := ca.NewTLSKeyPairLoader(config.Certificate, config.PrivateKey)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttlsConfig.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) {\n\t\t\treturn certLoader()\n\t\t}\n\n\t\tif config.EchKey != \"\" {\n\t\t\terr = ech.LoadECHKey(config.EchKey, tlsConfig)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\ttlsConfig.ClientAuth = ca.ClientAuthTypeFromString(config.ClientAuthType)\n\tif len(config.ClientAuthCert) > 0 {\n\t\tif tlsConfig.ClientAuth == tls.NoClientCert {\n\t\t\ttlsConfig.ClientAuth = tls.RequireAndVerifyClientCert\n\t\t}\n\t}\n\tif tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert {\n\t\tpool, err := ca.LoadCertificates(config.ClientAuthCert)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttlsConfig.ClientCAs = pool\n\t}\n\tif config.RealityConfig.PrivateKey != \"\" {\n\t\tif tlsConfig.GetCertificate != nil {\n\t\t\treturn nil, errors.New(\"certificate is unavailable in reality\")\n\t\t}\n\t\tif tlsConfig.ClientAuth != tls.NoClientCert {\n\t\t\treturn nil, errors.New(\"client-auth is unavailable in reality\")\n\t\t}\n\t\trealityBuilder, err = config.RealityConfig.Build(tunnel)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tif realityBuilder != nil {\n\t\tl = realityBuilder.NewListener(l)\n\t} else if tlsConfig.GetCertificate != nil {\n\t\tl = tls.NewListener(l, tlsConfig)\n\t}\n\n\tsl := &Listener{\n\t\tlistener: l,\n\t\taddr:     config.Listen,\n\t}\n\tgo func() {\n\t\tfor {\n\t\t\tc, err := l.Accept()\n\t\t\tif err != nil {\n\t\t\t\tif sl.closed {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tstore := config.AuthStore\n\t\t\tif isDefault || store == authStore.Default { // only apply on default listener\n\t\t\t\tif !inbound.IsRemoteAddrDisAllowed(c.RemoteAddr()) {\n\t\t\t\t\t_ = c.Close()\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif inbound.SkipAuthRemoteAddr(c.RemoteAddr()) {\n\t\t\t\t\tstore = authStore.Nil\n\t\t\t\t}\n\t\t\t}\n\t\t\tgo handleSocks(c, tunnel, store, additions...)\n\t\t}\n\t}()\n\n\treturn sl, nil\n}\n\nfunc handleSocks(conn net.Conn, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) {\n\tbufConn := N.NewBufferedConn(conn)\n\thead, err := bufConn.Peek(1)\n\tif err != nil {\n\t\tconn.Close()\n\t\treturn\n\t}\n\n\tswitch head[0] {\n\tcase socks4.Version:\n\t\tHandleSocks4(bufConn, tunnel, store, additions...)\n\tcase socks5.Version:\n\t\tHandleSocks5(bufConn, tunnel, store, additions...)\n\tdefault:\n\t\tconn.Close()\n\t}\n}\n\nfunc HandleSocks4(conn net.Conn, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) {\n\tauthenticator := store.Authenticator()\n\taddr, _, user, err := socks4.ServerHandshake(conn, authenticator)\n\tif err != nil {\n\t\tconn.Close()\n\t\treturn\n\t}\n\tadditions = append(additions, inbound.WithInUser(user))\n\ttunnel.HandleTCPConn(inbound.NewSocket(socks5.ParseAddr(addr), conn, C.SOCKS4, additions...))\n}\n\nfunc HandleSocks5(conn net.Conn, tunnel C.Tunnel, store auth.AuthStore, additions ...inbound.Addition) {\n\tauthenticator := store.Authenticator()\n\ttarget, command, user, err := socks5.ServerHandshake(conn, authenticator)\n\tif err != nil {\n\t\tconn.Close()\n\t\treturn\n\t}\n\tif command == socks5.CmdUDPAssociate {\n\t\tdefer conn.Close()\n\t\tio.Copy(io.Discard, conn)\n\t\treturn\n\t}\n\tadditions = append(additions, inbound.WithInUser(user))\n\ttunnel.HandleTCPConn(inbound.NewSocket(target, conn, C.SOCKS5, additions...))\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/socks/udp.go",
    "content": "package socks\n\nimport (\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/sockopt\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n)\n\ntype UDPListener struct {\n\tpacketConn net.PacketConn\n\taddr       string\n\tclosed     bool\n}\n\n// RawAddress implements C.Listener\nfunc (l *UDPListener) RawAddress() string {\n\treturn l.addr\n}\n\n// Address implements C.Listener\nfunc (l *UDPListener) Address() string {\n\treturn l.packetConn.LocalAddr().String()\n}\n\n// Close implements C.Listener\nfunc (l *UDPListener) Close() error {\n\tl.closed = true\n\treturn l.packetConn.Close()\n}\n\nfunc NewUDP(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*UDPListener, error) {\n\tif len(additions) == 0 {\n\t\tadditions = []inbound.Addition{\n\t\t\tinbound.WithInName(\"DEFAULT-SOCKS\"),\n\t\t\tinbound.WithSpecialRules(\"\"),\n\t\t}\n\t}\n\tl, err := inbound.ListenPacket(\"udp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err := sockopt.UDPReuseaddr(l); err != nil {\n\t\tlog.Warnln(\"Failed to Reuse UDP Address: %s\", err)\n\t}\n\n\tsl := &UDPListener{\n\t\tpacketConn: l,\n\t\taddr:       addr,\n\t}\n\tconn := N.NewEnhancePacketConn(l)\n\tgo func() {\n\t\tfor {\n\t\t\tdata, put, remoteAddr, err := conn.WaitReadFrom()\n\t\t\tif err != nil {\n\t\t\t\tif put != nil {\n\t\t\t\t\tput()\n\t\t\t\t}\n\t\t\t\tif sl.closed {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\thandleSocksUDP(l, tunnel, data, put, remoteAddr, additions...)\n\t\t}\n\t}()\n\n\treturn sl, nil\n}\n\nfunc handleSocksUDP(pc net.PacketConn, tunnel C.Tunnel, buf []byte, put func(), addr net.Addr, additions ...inbound.Addition) {\n\ttarget, payload, err := socks5.DecodeUDPPacket(buf)\n\tif err != nil {\n\t\t// Unresolved UDP packet, return buffer to the pool\n\t\tif put != nil {\n\t\t\tput()\n\t\t}\n\t\treturn\n\t}\n\tpacket := &packet{\n\t\tpc:      pc,\n\t\trAddr:   addr,\n\t\tpayload: payload,\n\t\tput:     put,\n\t}\n\ttunnel.HandleUDPPacket(inbound.NewPacket(target, packet, C.SOCKS5, additions...))\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/socks/utils.go",
    "content": "package socks\n\nimport (\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n)\n\ntype packet struct {\n\tpc      net.PacketConn\n\trAddr   net.Addr\n\tpayload []byte\n\tput     func()\n}\n\nfunc (c *packet) Data() []byte {\n\treturn c.payload\n}\n\n// WriteBack write UDP packet with source(ip, port) = `addr`\nfunc (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {\n\tpacket, err := socks5.EncodeUDPPacket(socks5.ParseAddrToSocksAddr(addr), b)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn c.pc.WriteTo(packet, c.rAddr)\n}\n\n// LocalAddr returns the source IP/Port of UDP Packet\nfunc (c *packet) LocalAddr() net.Addr {\n\treturn c.rAddr\n}\n\nfunc (c *packet) Drop() {\n\tif c.put != nil {\n\t\tc.put()\n\t\tc.put = nil\n\t}\n\tc.payload = nil\n}\n\nfunc (c *packet) InAddr() net.Addr {\n\treturn c.pc.LocalAddr()\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/sudoku/server.go",
    "content": "package sudoku\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/inner\"\n\t\"github.com/metacubex/mihomo/listener/sing\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n\t\"github.com/metacubex/mihomo/transport/sudoku\"\n)\n\ntype Listener struct {\n\tlistener  net.Listener\n\taddr      string\n\tclosed    bool\n\tprotoConf sudoku.ProtocolConfig\n\ttunnelSrv *sudoku.HTTPMaskTunnelServer\n\tfallback  string\n\thandler   *sing.ListenerHandler\n}\n\n// RawAddress implements C.Listener\nfunc (l *Listener) RawAddress() string {\n\treturn l.addr\n}\n\n// Address implements C.Listener\nfunc (l *Listener) Address() string {\n\tif l.listener == nil {\n\t\treturn \"\"\n\t}\n\treturn l.listener.Addr().String()\n}\n\n// Close implements C.Listener\nfunc (l *Listener) Close() error {\n\tl.closed = true\n\tif l.listener != nil {\n\t\treturn l.listener.Close()\n\t}\n\treturn nil\n}\n\nfunc (l *Listener) handleConn(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) {\n\tlog.Debugln(\"[Sudoku] accepted %s\", conn.RemoteAddr())\n\thandshakeConn := conn\n\thandshakeCfg := &l.protoConf\n\tcloseConns := func() {\n\t\t_ = handshakeConn.Close()\n\t\tif handshakeConn != conn {\n\t\t\t_ = conn.Close()\n\t\t}\n\t}\n\tif l.tunnelSrv != nil {\n\t\tc, cfg, done, err := l.tunnelSrv.WrapConn(conn)\n\t\tif err != nil {\n\t\t\tcloseConns()\n\t\t\treturn\n\t\t}\n\t\tif done {\n\t\t\treturn\n\t\t}\n\t\tif c != nil {\n\t\t\thandshakeConn = c\n\t\t}\n\t\tif cfg != nil {\n\t\t\thandshakeCfg = cfg\n\t\t}\n\t}\n\n\tif l.fallback != \"\" {\n\t\tif r, ok := handshakeConn.(interface{ IsHTTPMaskRejected() bool }); ok && r.IsHTTPMaskRejected() {\n\t\t\tfb, err := inner.HandleTcp(tunnel, l.fallback, \"\")\n\t\t\tif err != nil {\n\t\t\t\tcloseConns()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tN.Relay(handshakeConn, fb)\n\t\t\treturn\n\t\t}\n\t}\n\n\tcConn, meta, err := sudoku.ServerHandshake(handshakeConn, handshakeCfg)\n\tif err != nil {\n\t\tfallbackAddr := l.fallback\n\t\tvar susp *sudoku.SuspiciousError\n\t\tisSuspicious := errors.As(err, &susp) && susp != nil && susp.Conn != nil\n\t\tif isSuspicious {\n\t\t\tlog.Warnln(\"[Sudoku] suspicious handshake from %s: %v\", conn.RemoteAddr(), err)\n\t\t\tif fallbackAddr != \"\" {\n\t\t\t\tfb, err := inner.HandleTcp(tunnel, fallbackAddr, \"\")\n\t\t\t\tif err == nil {\n\t\t\t\t\trelayToFallback(susp.Conn, conn, fb)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t} else {\n\t\t\tlog.Debugln(\"[Sudoku] handshake failed from %s: %v\", conn.RemoteAddr(), err)\n\t\t}\n\t\tcloseConns()\n\t\treturn\n\t}\n\n\tsession, err := sudoku.ReadServerSession(cConn, meta)\n\tif err != nil {\n\t\tlog.Warnln(\"[Sudoku] read session failed from %s: %v\", conn.RemoteAddr(), err)\n\t\t_ = cConn.Close()\n\t\tif handshakeConn != conn {\n\t\t\t_ = conn.Close()\n\t\t}\n\t\treturn\n\t}\n\n\tswitch session.Type {\n\tcase sudoku.SessionTypeUoT:\n\t\tl.handleUoTSession(session.Conn, tunnel, additions...)\n\tcase sudoku.SessionTypeMultiplex:\n\t\tmux, err := sudoku.AcceptMultiplexServer(session.Conn)\n\t\tif err != nil {\n\t\t\t_ = session.Conn.Close()\n\t\t\treturn\n\t\t}\n\t\tdefer mux.Close()\n\n\t\tfor {\n\t\t\tstream, target, err := mux.AcceptTCP()\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ttargetAddr := socks5.ParseAddr(target)\n\t\t\tif targetAddr == nil {\n\t\t\t\t_ = stream.Close()\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tgo l.handler.HandleSocket(targetAddr, stream, additions...)\n\t\t}\n\tdefault:\n\t\ttargetAddr := socks5.ParseAddr(session.Target)\n\t\tif targetAddr == nil {\n\t\t\tlog.Warnln(\"[Sudoku] invalid target from %s: %q\", conn.RemoteAddr(), session.Target)\n\t\t\t_ = session.Conn.Close()\n\t\t\treturn\n\t\t}\n\t\tl.handler.HandleSocket(targetAddr, session.Conn, additions...)\n\t\t//tunnel.HandleTCPConn(inbound.NewSocket(targetAddr, session.Conn, C.SUDOKU, additions...))\n\t}\n}\n\nfunc (l *Listener) handleUoTSession(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) {\n\twriter := sudoku.NewUoTPacketConn(conn)\n\tremoteAddr := conn.RemoteAddr()\n\tconnID := utils.NewUUIDV4().String() // make a new SNAT key\n\n\tfor {\n\t\taddrStr, payload, err := sudoku.ReadDatagram(conn)\n\t\tif err != nil {\n\t\t\tif !errors.Is(err, io.EOF) {\n\t\t\t\tlog.Debugln(\"[Sudoku][UoT] session closed: %v\", err)\n\t\t\t}\n\t\t\t_ = conn.Close()\n\t\t\treturn\n\t\t}\n\n\t\ttarget := socks5.ParseAddr(addrStr)\n\t\tif target == nil {\n\t\t\tlog.Debugln(\"[Sudoku][UoT] drop invalid target: %s\", addrStr)\n\t\t\tcontinue\n\t\t}\n\n\t\tcPacket := &uotPacket{\n\t\t\tpayload: payload,\n\t\t\twriter:  writer,\n\t\t\trAddr:   remoteAddr,\n\t\t}\n\t\tcPacket.rAddr = N.NewCustomAddr(C.SUDOKU.String(), connID, cPacket.rAddr) // for tunnel's handleUDPConn\n\t\ttunnel.HandleUDPPacket(inbound.NewPacket(target, cPacket, C.SUDOKU, additions...))\n\t}\n}\n\ntype uotPacket struct {\n\tpayload []byte\n\twriter  *sudoku.UoTPacketConn\n\trAddr   net.Addr\n}\n\nfunc (p *uotPacket) Data() []byte {\n\treturn p.payload\n}\n\nfunc (p *uotPacket) WriteBack(b []byte, addr net.Addr) (int, error) {\n\treturn p.writer.WriteTo(b, addr)\n}\n\nfunc (p *uotPacket) Drop() {\n\tp.payload = nil\n}\n\nfunc (p *uotPacket) LocalAddr() net.Addr {\n\treturn p.rAddr\n}\n\nfunc relayToFallback(wrapper net.Conn, rawConn net.Conn, fallback net.Conn) {\n\tif wrapper != nil {\n\t\tif recorder, ok := wrapper.(interface{ GetBufferedAndRecorded() []byte }); ok {\n\t\t\tbadData := recorder.GetBufferedAndRecorded()\n\t\t\tif len(badData) > 0 {\n\t\t\t\t_ = fallback.SetWriteDeadline(time.Now().Add(3 * time.Second))\n\t\t\t\tif _, err := io.Copy(fallback, bytes.NewReader(badData)); err != nil {\n\t\t\t\t\t_ = fallback.Close()\n\t\t\t\t\t_ = rawConn.Close()\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\t_ = fallback.SetWriteDeadline(time.Time{})\n\t\t\t}\n\t\t}\n\t}\n\tN.Relay(rawConn, fallback)\n}\n\nfunc New(config LC.SudokuServer, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {\n\tif len(additions) == 0 {\n\t\tadditions = []inbound.Addition{\n\t\t\tinbound.WithInName(\"DEFAULT-SUDOKU\"),\n\t\t\tinbound.WithSpecialRules(\"\"),\n\t\t}\n\t}\n\n\t// Using sing handler for sing-mux support\n\th, err := sing.NewListenerHandler(sing.ListenerConfig{\n\t\tTunnel:    tunnel,\n\t\tType:      C.SUDOKU,\n\t\tAdditions: additions,\n\t\tMuxOption: config.MuxOption,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tl, err := inbound.Listen(\"tcp\", config.Listen)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttableType, err := sudoku.NormalizeTableType(config.TableType)\n\tif err != nil {\n\t\t_ = l.Close()\n\t\treturn nil, err\n\t}\n\n\tdefaultConf := sudoku.DefaultConfig()\n\tpaddingMin, paddingMax := sudoku.ResolvePadding(config.PaddingMin, config.PaddingMax, defaultConf.PaddingMin, defaultConf.PaddingMax)\n\tenablePureDownlink := sudoku.DerefBool(config.EnablePureDownlink, defaultConf.EnablePureDownlink)\n\n\ttables, err := sudoku.NewServerTablesWithCustomPatterns(sudoku.ServerAEADSeed(config.Key), tableType, config.CustomTable, config.CustomTables)\n\tif err != nil {\n\t\t_ = l.Close()\n\t\treturn nil, err\n\t}\n\n\thandshakeTimeout := sudoku.DerefInt(config.HandshakeTimeoutSecond, defaultConf.HandshakeTimeoutSeconds)\n\n\tprotoConf := sudoku.ProtocolConfig{\n\t\tKey:                     config.Key,\n\t\tAEADMethod:              defaultConf.AEADMethod,\n\t\tPaddingMin:              paddingMin,\n\t\tPaddingMax:              paddingMax,\n\t\tEnablePureDownlink:      enablePureDownlink,\n\t\tHandshakeTimeoutSeconds: handshakeTimeout,\n\t\tDisableHTTPMask:         config.DisableHTTPMask,\n\t\tHTTPMaskMode:            config.HTTPMaskMode,\n\t\tHTTPMaskPathRoot:        strings.TrimSpace(config.PathRoot),\n\t}\n\tif len(tables) == 1 {\n\t\tprotoConf.Table = tables[0]\n\t} else {\n\t\tprotoConf.Tables = tables\n\t}\n\tif config.AEADMethod != \"\" {\n\t\tprotoConf.AEADMethod = config.AEADMethod\n\t}\n\n\tsl := &Listener{\n\t\tlistener:  l,\n\t\taddr:      config.Listen,\n\t\tprotoConf: protoConf,\n\t\thandler:   h,\n\t\tfallback:  strings.TrimSpace(config.Fallback),\n\t}\n\tif sl.fallback != \"\" {\n\t\tsl.tunnelSrv = sudoku.NewHTTPMaskTunnelServerWithFallback(&sl.protoConf)\n\t} else {\n\t\tsl.tunnelSrv = sudoku.NewHTTPMaskTunnelServer(&sl.protoConf)\n\t}\n\n\tgo func() {\n\t\tfor {\n\t\t\tc, err := l.Accept()\n\t\t\tif err != nil {\n\t\t\t\tif sl.closed {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tgo sl.handleConn(c, tunnel, additions...)\n\t\t}\n\t}()\n\n\treturn sl, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/tproxy/packet.go",
    "content": "package tproxy\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/netip\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\t\"github.com/metacubex/mihomo/common/pool\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\ntype packet struct {\n\tpc     net.PacketConn\n\tlAddr  netip.AddrPort\n\tbuf    []byte\n\ttunnel C.Tunnel\n}\n\nfunc (c *packet) Data() []byte {\n\treturn c.buf\n}\n\n// WriteBack opens a new socket binding `addr` to write UDP packet back\nfunc (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {\n\ttc, err := createOrGetLocalConn(addr, c.LocalAddr(), c.tunnel)\n\tif err != nil {\n\t\tn = 0\n\t\treturn\n\t}\n\tn, err = tc.Write(b)\n\treturn\n}\n\n// LocalAddr returns the source IP/Port of UDP Packet\nfunc (c *packet) LocalAddr() net.Addr {\n\treturn &net.UDPAddr{IP: c.lAddr.Addr().AsSlice(), Port: int(c.lAddr.Port()), Zone: c.lAddr.Addr().Zone()}\n}\n\nfunc (c *packet) Drop() {\n\t_ = pool.Put(c.buf)\n\tc.buf = nil\n}\n\nfunc (c *packet) InAddr() net.Addr {\n\treturn c.pc.LocalAddr()\n}\n\n// this function listen at rAddr and write to lAddr\n// for here, rAddr is the ip/port client want to access\n// lAddr is the ip/port client opened\nfunc createOrGetLocalConn(rAddr, lAddr net.Addr, tunnel C.Tunnel) (*net.UDPConn, error) {\n\tremote := rAddr.String()\n\tlocal := lAddr.String()\n\tnatTable := tunnel.NatTable()\n\tlocalConn := natTable.GetForLocalConn(local, remote)\n\t// localConn not exist\n\tif localConn == nil {\n\t\tcond, loaded := natTable.GetOrCreateLockForLocalConn(local, remote)\n\t\tif loaded {\n\t\t\tcond.L.Lock()\n\t\t\tcond.Wait()\n\t\t\t// we should get localConn here\n\t\t\tlocalConn = natTable.GetForLocalConn(local, remote)\n\t\t\tif localConn == nil {\n\t\t\t\treturn nil, fmt.Errorf(\"localConn is nil, nat entry not exist\")\n\t\t\t}\n\t\t\tcond.L.Unlock()\n\t\t} else {\n\t\t\tif cond == nil {\n\t\t\t\treturn nil, fmt.Errorf(\"cond is nil, nat entry not exist\")\n\t\t\t}\n\t\t\tdefer func() {\n\t\t\t\tnatTable.DeleteLockForLocalConn(local, remote)\n\t\t\t\tcond.Broadcast()\n\t\t\t}()\n\t\t\tconn, err := listenLocalConn(rAddr, lAddr, tunnel)\n\t\t\tif err != nil {\n\t\t\t\tlog.Errorln(\"listenLocalConn failed with error: %s, packet loss (rAddr[%T]=%s lAddr[%T]=%s)\", err.Error(), rAddr, remote, lAddr, local)\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tnatTable.AddForLocalConn(local, remote, conn)\n\t\t\tlocalConn = conn\n\t\t}\n\t}\n\treturn localConn, nil\n}\n\n// this function listen at rAddr\n// and send what received to program itself, then send to real remote\nfunc listenLocalConn(rAddr, lAddr net.Addr, tunnel C.Tunnel) (*net.UDPConn, error) {\n\tadditions := []inbound.Addition{\n\t\tinbound.WithInName(\"DEFAULT-TPROXY\"),\n\t\tinbound.WithSpecialRules(\"\"),\n\t}\n\tlc, err := dialUDP(\"udp\", rAddr.(*net.UDPAddr).AddrPort(), lAddr.(*net.UDPAddr).AddrPort())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tgo func() {\n\t\tlog.Debugln(\"TProxy listenLocalConn rAddr=%s lAddr=%s\", rAddr.String(), lAddr.String())\n\t\tfor {\n\t\t\tbuf := pool.Get(pool.UDPBufferSize)\n\t\t\tbr, err := lc.Read(buf)\n\t\t\tif err != nil {\n\t\t\t\tif errors.Is(err, net.ErrClosed) {\n\t\t\t\t\tlog.Debugln(\"TProxy local conn listener exit.. rAddr=%s lAddr=%s\", rAddr.String(), lAddr.String())\n\t\t\t\t\tpool.Put(buf)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\t// since following localPackets are pass through this socket which listen rAddr\n\t\t\t// I choose current listener as packet's packet conn\n\t\t\thandlePacketConn(lc, tunnel, buf[:br], lAddr.(*net.UDPAddr).AddrPort(), rAddr.(*net.UDPAddr).AddrPort(), additions...)\n\t\t}\n\t}()\n\treturn lc, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/tproxy/setsockopt_linux.go",
    "content": "//go:build linux\n\npackage tproxy\n\nimport (\n\t\"net\"\n\t\"syscall\"\n)\n\nfunc setsockopt(rc syscall.RawConn, addr string) error {\n\tisIPv6 := true\n\thost, _, err := net.SplitHostPort(addr)\n\tif err != nil {\n\t\treturn err\n\t}\n\tip := net.ParseIP(host)\n\tif ip != nil && ip.To4() != nil {\n\t\tisIPv6 = false\n\t}\n\n\trc.Control(func(fd uintptr) {\n\t\terr = syscall.SetsockoptInt(int(fd), syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1)\n\n\t\tif err == nil {\n\t\t\terr = syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_TRANSPARENT, 1)\n\t\t}\n\t\tif err == nil && isIPv6 {\n\t\t\terr = syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, IPV6_TRANSPARENT, 1)\n\t\t}\n\n\t\tif err == nil {\n\t\t\terr = syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_RECVORIGDSTADDR, 1)\n\t\t}\n\t\tif err == nil && isIPv6 {\n\t\t\terr = syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, IPV6_RECVORIGDSTADDR, 1)\n\t\t}\n\n\t\tif err == nil {\n\t\t\t_ = setDSCPsockopt(fd, isIPv6)\n\t\t}\n\t})\n\n\treturn err\n}\n\nfunc setDSCPsockopt(fd uintptr, isIPv6 bool) (err error) {\n\tif err == nil {\n\t\terr = syscall.SetsockoptInt(int(fd), syscall.SOL_IP, syscall.IP_RECVTOS, 1)\n\t}\n\n\tif err == nil && isIPv6 {\n\t\terr = syscall.SetsockoptInt(int(fd), syscall.SOL_IPV6, syscall.IPV6_RECVTCLASS, 1)\n\t}\n\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/tproxy/setsockopt_other.go",
    "content": "//go:build !linux\n\npackage tproxy\n\nimport (\n\t\"errors\"\n\t\"syscall\"\n)\n\nfunc setsockopt(rc syscall.RawConn, addr string) error {\n\treturn errors.New(\"not supported on current platform\")\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/tproxy/tproxy.go",
    "content": "package tproxy\n\nimport (\n\t\"context\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\t\"github.com/metacubex/mihomo/component/keepalive\"\n\t\"github.com/metacubex/mihomo/component/mptcp\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n)\n\ntype Listener struct {\n\tlistener net.Listener\n\taddr     string\n\tclosed   bool\n}\n\n// RawAddress implements C.Listener\nfunc (l *Listener) RawAddress() string {\n\treturn l.addr\n}\n\n// Address implements C.Listener\nfunc (l *Listener) Address() string {\n\treturn l.listener.Addr().String()\n}\n\n// Close implements C.Listener\nfunc (l *Listener) Close() error {\n\tl.closed = true\n\treturn l.listener.Close()\n}\n\nfunc (l *Listener) handleTProxy(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) {\n\ttarget := socks5.ParseAddrToSocksAddr(conn.LocalAddr())\n\tkeepalive.TCPKeepAlive(conn)\n\t// TProxy's conn.LocalAddr() is target address, so we set from l.listener\n\tadditions = append([]inbound.Addition{inbound.WithInAddr(l.listener.Addr())}, additions...)\n\ttunnel.HandleTCPConn(inbound.NewSocket(target, conn, C.TPROXY, additions...))\n}\n\nfunc New(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {\n\tif len(additions) == 0 {\n\t\tadditions = []inbound.Addition{\n\t\t\tinbound.WithInName(\"DEFAULT-TPROXY\"),\n\t\t\tinbound.WithSpecialRules(\"\"),\n\t\t}\n\t}\n\t// Golang will then enable mptcp support for listeners by default when the major version of go.mod is 1.24 or higher.\n\t// This can cause tproxy to malfunction on certain Linux kernel versions, so we force to disable mptcp for tproxy.\n\tlc := net.ListenConfig{}\n\tmptcp.SetNetListenConfig(&lc, false)\n\tl, err := lc.Listen(context.Background(), \"tcp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttl := l.(*net.TCPListener)\n\trc, err := tl.SyscallConn()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\terr = setsockopt(rc, addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\trl := &Listener{\n\t\tlistener: l,\n\t\taddr:     addr,\n\t}\n\n\tgo func() {\n\t\tfor {\n\t\t\tc, err := l.Accept()\n\t\t\tif err != nil {\n\t\t\t\tif rl.closed {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tgo rl.handleTProxy(c, tunnel, additions...)\n\t\t}\n\t}()\n\n\treturn rl, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/tproxy/tproxy_iptables.go",
    "content": "package tproxy\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"runtime\"\n\n\t\"github.com/metacubex/mihomo/common/cmd\"\n\t\"github.com/metacubex/mihomo/component/dialer\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\nvar (\n\tdnsPort       uint16\n\ttProxyPort    uint16\n\tinterfaceName string\n\tDnsRedirect   bool\n)\n\nconst (\n\tPROXY_FWMARK      = \"0x2d0\"\n\tPROXY_ROUTE_TABLE = \"0x2d0\"\n)\n\nfunc SetTProxyIPTables(ifname string, bypass []string, tport uint16, dnsredir bool, dport uint16) error {\n\tif _, err := cmd.ExecCmd(\"iptables -V\"); err != nil {\n\t\treturn fmt.Errorf(\"current operations system [%s] are not support iptables or command iptables does not exist\", runtime.GOOS)\n\t}\n\n\tif ifname == \"\" {\n\t\treturn errors.New(\"the 'interface-name' can not be empty\")\n\t}\n\n\tinterfaceName = ifname\n\ttProxyPort = tport\n\tDnsRedirect = dnsredir\n\tdnsPort = dport\n\n\t// add route\n\texecCmd(fmt.Sprintf(\"ip -f inet rule add fwmark %s lookup %s\", PROXY_FWMARK, PROXY_ROUTE_TABLE))\n\texecCmd(fmt.Sprintf(\"ip -f inet route add local default dev %s table %s\", interfaceName, PROXY_ROUTE_TABLE))\n\n\t// set FORWARD\n\tif interfaceName != \"lo\" {\n\t\texecCmd(\"sysctl -w net.ipv4.ip_forward=1\")\n\t\texecCmd(fmt.Sprintf(\"iptables -t filter -A FORWARD -o %s -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT\", interfaceName))\n\t\texecCmd(fmt.Sprintf(\"iptables -t filter -A FORWARD -o %s -j ACCEPT\", interfaceName))\n\t\texecCmd(fmt.Sprintf(\"iptables -t filter -A FORWARD -i %s ! -o %s -j ACCEPT\", interfaceName, interfaceName))\n\t\texecCmd(fmt.Sprintf(\"iptables -t filter -A FORWARD -i %s -o %s -j ACCEPT\", interfaceName, interfaceName))\n\t}\n\n\t// set mihomo divert\n\texecCmd(\"iptables -t mangle -N mihomo_divert\")\n\texecCmd(\"iptables -t mangle -F mihomo_divert\")\n\texecCmd(fmt.Sprintf(\"iptables -t mangle -A mihomo_divert -j MARK --set-mark %s\", PROXY_FWMARK))\n\texecCmd(\"iptables -t mangle -A mihomo_divert -j ACCEPT\")\n\n\t// set pre routing\n\texecCmd(\"iptables -t mangle -N mihomo_prerouting\")\n\texecCmd(\"iptables -t mangle -F mihomo_prerouting\")\n\texecCmd(\"iptables -t mangle -A mihomo_prerouting -s 172.17.0.0/16 -j RETURN\")\n\tif DnsRedirect {\n\t\texecCmd(\"iptables -t mangle -A mihomo_prerouting -p udp --dport 53 -j ACCEPT\")\n\t\texecCmd(\"iptables -t mangle -A mihomo_prerouting -p tcp --dport 53 -j ACCEPT\")\n\t}\n\texecCmd(\"iptables -t mangle -A mihomo_prerouting -m addrtype --dst-type LOCAL -j RETURN\")\n\taddLocalnetworkToChain(\"mihomo_prerouting\", bypass)\n\texecCmd(\"iptables -t mangle -A mihomo_prerouting -p tcp -m socket -j mihomo_divert\")\n\texecCmd(\"iptables -t mangle -A mihomo_prerouting -p udp -m socket -j mihomo_divert\")\n\texecCmd(fmt.Sprintf(\"iptables -t mangle -A mihomo_prerouting -p tcp -j TPROXY --on-port %d --tproxy-mark %s/%s\", tProxyPort, PROXY_FWMARK, PROXY_FWMARK))\n\texecCmd(fmt.Sprintf(\"iptables -t mangle -A mihomo_prerouting -p udp -j TPROXY --on-port %d --tproxy-mark %s/%s\", tProxyPort, PROXY_FWMARK, PROXY_FWMARK))\n\texecCmd(\"iptables -t mangle -A PREROUTING -j mihomo_prerouting\")\n\n\tif DnsRedirect {\n\t\texecCmd(fmt.Sprintf(\"iptables -t nat -I PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p tcp --dport 53 -j REDIRECT --to %d\", dnsPort))\n\t\texecCmd(fmt.Sprintf(\"iptables -t nat -I PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p udp --dport 53 -j REDIRECT --to %d\", dnsPort))\n\t}\n\n\t// set post routing\n\tif interfaceName != \"lo\" {\n\t\texecCmd(fmt.Sprintf(\"iptables -t nat -A POSTROUTING -o %s -m addrtype ! --src-type LOCAL -j MASQUERADE\", interfaceName))\n\t}\n\n\t// set output\n\texecCmd(\"iptables -t mangle -N mihomo_output\")\n\texecCmd(\"iptables -t mangle -F mihomo_output\")\n\texecCmd(fmt.Sprintf(\"iptables -t mangle -A mihomo_output -m mark --mark %#x -j RETURN\", dialer.DefaultRoutingMark.Load()))\n\tif DnsRedirect {\n\t\texecCmd(\"iptables -t mangle -A mihomo_output -p udp -m multiport --dports 53,123,137 -j ACCEPT\")\n\t\texecCmd(\"iptables -t mangle -A mihomo_output -p tcp --dport 53 -j ACCEPT\")\n\t}\n\texecCmd(\"iptables -t mangle -A mihomo_output -m addrtype --dst-type LOCAL -j RETURN\")\n\texecCmd(\"iptables -t mangle -A mihomo_output -m addrtype --dst-type BROADCAST -j RETURN\")\n\taddLocalnetworkToChain(\"mihomo_output\", bypass)\n\texecCmd(fmt.Sprintf(\"iptables -t mangle -A mihomo_output -p tcp -j MARK --set-mark %s\", PROXY_FWMARK))\n\texecCmd(fmt.Sprintf(\"iptables -t mangle -A mihomo_output -p udp -j MARK --set-mark %s\", PROXY_FWMARK))\n\texecCmd(fmt.Sprintf(\"iptables -t mangle -I OUTPUT -o %s -j mihomo_output\", interfaceName))\n\n\t// set dns output\n\tif DnsRedirect {\n\t\texecCmd(\"iptables -t nat -N mihomo_dns_output\")\n\t\texecCmd(\"iptables -t nat -F mihomo_dns_output\")\n\t\texecCmd(fmt.Sprintf(\"iptables -t nat -A mihomo_dns_output -m mark --mark %#x -j RETURN\", dialer.DefaultRoutingMark.Load()))\n\t\texecCmd(\"iptables -t nat -A mihomo_dns_output -s 172.17.0.0/16 -j RETURN\")\n\t\texecCmd(fmt.Sprintf(\"iptables -t nat -A mihomo_dns_output -p udp -j REDIRECT --to-ports %d\", dnsPort))\n\t\texecCmd(fmt.Sprintf(\"iptables -t nat -A mihomo_dns_output -p tcp -j REDIRECT --to-ports %d\", dnsPort))\n\t\texecCmd(\"iptables -t nat -I OUTPUT -p tcp --dport 53 -j mihomo_dns_output\")\n\t\texecCmd(\"iptables -t nat -I OUTPUT -p udp --dport 53 -j mihomo_dns_output\")\n\t}\n\n\treturn nil\n}\n\nfunc CleanupTProxyIPTables() {\n\tif runtime.GOOS != \"linux\" || interfaceName == \"\" || tProxyPort == 0 {\n\t\treturn\n\t}\n\n\tlog.Warnln(\"Cleanup tproxy linux iptables\")\n\n\tdialer.DefaultRoutingMark.CompareAndSwap(2158, 0)\n\n\tif _, err := cmd.ExecCmd(\"iptables -t mangle -L mihomo_divert\"); err != nil {\n\t\treturn\n\t}\n\n\t// clean route\n\texecCmd(fmt.Sprintf(\"ip -f inet rule del fwmark %s lookup %s\", PROXY_FWMARK, PROXY_ROUTE_TABLE))\n\texecCmd(fmt.Sprintf(\"ip -f inet route del local default dev %s table %s\", interfaceName, PROXY_ROUTE_TABLE))\n\n\t// clean FORWARD\n\tif interfaceName != \"lo\" {\n\t\texecCmd(fmt.Sprintf(\"iptables -t filter -D FORWARD -i %s ! -o %s -j ACCEPT\", interfaceName, interfaceName))\n\t\texecCmd(fmt.Sprintf(\"iptables -t filter -D FORWARD -i %s -o %s -j ACCEPT\", interfaceName, interfaceName))\n\t\texecCmd(fmt.Sprintf(\"iptables -t filter -D FORWARD -o %s -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT\", interfaceName))\n\t\texecCmd(fmt.Sprintf(\"iptables -t filter -D FORWARD -o %s -j ACCEPT\", interfaceName))\n\t}\n\n\t// clean PREROUTING\n\tif DnsRedirect {\n\t\texecCmd(fmt.Sprintf(\"iptables -t nat -D PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p tcp --dport 53 -j REDIRECT --to %d\", dnsPort))\n\t\texecCmd(fmt.Sprintf(\"iptables -t nat -D PREROUTING ! -s 172.17.0.0/16 ! -d 127.0.0.0/8 -p udp --dport 53 -j REDIRECT --to %d\", dnsPort))\n\t}\n\texecCmd(\"iptables -t mangle -D PREROUTING -j mihomo_prerouting\")\n\n\t// clean POSTROUTING\n\tif interfaceName != \"lo\" {\n\t\texecCmd(fmt.Sprintf(\"iptables -t nat -D POSTROUTING -o %s -m addrtype ! --src-type LOCAL -j MASQUERADE\", interfaceName))\n\t}\n\n\t// clean OUTPUT\n\texecCmd(fmt.Sprintf(\"iptables -t mangle -D OUTPUT -o %s -j mihomo_output\", interfaceName))\n\tif DnsRedirect {\n\t\texecCmd(\"iptables -t nat -D OUTPUT -p tcp --dport 53 -j mihomo_dns_output\")\n\t\texecCmd(\"iptables -t nat -D OUTPUT -p udp --dport 53 -j mihomo_dns_output\")\n\t}\n\n\t// clean chain\n\texecCmd(\"iptables -t mangle -F mihomo_prerouting\")\n\texecCmd(\"iptables -t mangle -X mihomo_prerouting\")\n\texecCmd(\"iptables -t mangle -F mihomo_divert\")\n\texecCmd(\"iptables -t mangle -X mihomo_divert\")\n\texecCmd(\"iptables -t mangle -F mihomo_output\")\n\texecCmd(\"iptables -t mangle -X mihomo_output\")\n\tif DnsRedirect {\n\t\texecCmd(\"iptables -t nat -F mihomo_dns_output\")\n\t\texecCmd(\"iptables -t nat -X mihomo_dns_output\")\n\t}\n\tinterfaceName = \"\"\n\ttProxyPort = 0\n\tdnsPort = 0\n}\n\nfunc addLocalnetworkToChain(chain string, bypass []string) {\n\tfor _, bp := range bypass {\n\t\t_, _, err := net.ParseCIDR(bp)\n\t\tif err != nil {\n\t\t\tlog.Warnln(\"[IPTABLES] %s\", err)\n\t\t\tcontinue\n\t\t}\n\t\texecCmd(fmt.Sprintf(\"iptables -t mangle -A %s -d %s -j RETURN\", chain, bp))\n\t}\n\texecCmd(fmt.Sprintf(\"iptables -t mangle -A %s -d 0.0.0.0/8 -j RETURN\", chain))\n\texecCmd(fmt.Sprintf(\"iptables -t mangle -A %s -d 10.0.0.0/8 -j RETURN\", chain))\n\texecCmd(fmt.Sprintf(\"iptables -t mangle -A %s -d 100.64.0.0/10 -j RETURN\", chain))\n\texecCmd(fmt.Sprintf(\"iptables -t mangle -A %s -d 127.0.0.0/8 -j RETURN\", chain))\n\texecCmd(fmt.Sprintf(\"iptables -t mangle -A %s -d 169.254.0.0/16 -j RETURN\", chain))\n\texecCmd(fmt.Sprintf(\"iptables -t mangle -A %s -d 172.16.0.0/12 -j RETURN\", chain))\n\texecCmd(fmt.Sprintf(\"iptables -t mangle -A %s -d 192.0.0.0/24 -j RETURN\", chain))\n\texecCmd(fmt.Sprintf(\"iptables -t mangle -A %s -d 192.0.2.0/24 -j RETURN\", chain))\n\texecCmd(fmt.Sprintf(\"iptables -t mangle -A %s -d 192.88.99.0/24 -j RETURN\", chain))\n\texecCmd(fmt.Sprintf(\"iptables -t mangle -A %s -d 192.168.0.0/16 -j RETURN\", chain))\n\texecCmd(fmt.Sprintf(\"iptables -t mangle -A %s -d 198.51.100.0/24 -j RETURN\", chain))\n\texecCmd(fmt.Sprintf(\"iptables -t mangle -A %s -d 203.0.113.0/24 -j RETURN\", chain))\n\texecCmd(fmt.Sprintf(\"iptables -t mangle -A %s -d 224.0.0.0/4 -j RETURN\", chain))\n\texecCmd(fmt.Sprintf(\"iptables -t mangle -A %s -d 240.0.0.0/4 -j RETURN\", chain))\n\texecCmd(fmt.Sprintf(\"iptables -t mangle -A %s -d 255.255.255.255/32 -j RETURN\", chain))\n}\n\nfunc execCmd(cmdStr string) {\n\tlog.Debugln(\"[IPTABLES] %s\", cmdStr)\n\n\t_, err := cmd.ExecCmd(cmdStr)\n\tif err != nil {\n\t\tlog.Warnln(\"[IPTABLES] exec cmd: %v\", err)\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/tproxy/udp.go",
    "content": "package tproxy\n\nimport (\n\t\"net\"\n\t\"net/netip\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\t\"github.com/metacubex/mihomo/common/pool\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n)\n\ntype UDPListener struct {\n\tpacketConn net.PacketConn\n\taddr       string\n\tclosed     bool\n}\n\n// RawAddress implements C.Listener\nfunc (l *UDPListener) RawAddress() string {\n\treturn l.addr\n}\n\n// Address implements C.Listener\nfunc (l *UDPListener) Address() string {\n\treturn l.packetConn.LocalAddr().String()\n}\n\n// Close implements C.Listener\nfunc (l *UDPListener) Close() error {\n\tl.closed = true\n\treturn l.packetConn.Close()\n}\n\nfunc NewUDP(addr string, tunnel C.Tunnel, additions ...inbound.Addition) (*UDPListener, error) {\n\tif len(additions) == 0 {\n\t\tadditions = []inbound.Addition{\n\t\t\tinbound.WithInName(\"DEFAULT-TPROXY\"),\n\t\t\tinbound.WithSpecialRules(\"\"),\n\t\t}\n\t}\n\tl, err := net.ListenPacket(\"udp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\trl := &UDPListener{\n\t\tpacketConn: l,\n\t\taddr:       addr,\n\t}\n\n\tc := l.(*net.UDPConn)\n\n\trc, err := c.SyscallConn()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\terr = setsockopt(rc, addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tgo func() {\n\t\toob := make([]byte, 1024)\n\t\tfor {\n\t\t\tbuf := pool.Get(pool.UDPBufferSize)\n\t\t\tn, oobn, _, lAddr, err := c.ReadMsgUDPAddrPort(buf, oob)\n\t\t\tif err != nil {\n\t\t\t\tpool.Put(buf)\n\t\t\t\tif rl.closed {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\trAddr, err := getOrigDst(oob[:oobn])\n\t\t\tif err != nil {\n\t\t\t\tpool.Put(buf)\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tdscp, _ := getDSCP(oob[:oobn])\n\t\t\tadditions := append(additions, inbound.WithDSCP(dscp)) // don't change outside additions\n\n\t\t\tif rAddr.Addr().Is4() {\n\t\t\t\t// try to unmap 4in6 address\n\t\t\t\tlAddr = netip.AddrPortFrom(lAddr.Addr().Unmap(), lAddr.Port())\n\t\t\t}\n\t\t\thandlePacketConn(l, tunnel, buf[:n], lAddr, rAddr, additions...)\n\t\t}\n\t}()\n\n\treturn rl, nil\n}\n\nfunc handlePacketConn(pc net.PacketConn, tunnel C.Tunnel, buf []byte, lAddr, rAddr netip.AddrPort, additions ...inbound.Addition) {\n\ttarget := socks5.AddrFromStdAddrPort(rAddr)\n\tpkt := &packet{\n\t\tpc:     pc,\n\t\tlAddr:  lAddr,\n\t\tbuf:    buf,\n\t\ttunnel: tunnel,\n\t}\n\ttunnel.HandleUDPPacket(inbound.NewPacket(target, pkt, C.TPROXY, additions...))\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/tproxy/udp_linux.go",
    "content": "//go:build linux\n\npackage tproxy\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"net/netip\"\n\t\"os\"\n\t\"strconv\"\n\t\"syscall\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nconst (\n\tIPV6_TRANSPARENT     = 0x4b\n\tIPV6_RECVORIGDSTADDR = 0x4a\n)\n\n// dialUDP acts like net.DialUDP for transparent proxy.\n// It binds to a non-local address(`lAddr`).\nfunc dialUDP(network string, lAddr, rAddr netip.AddrPort) (uc *net.UDPConn, err error) {\n\trSockAddr, err := udpAddrToSockAddr(rAddr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlSockAddr, err := udpAddrToSockAddr(lAddr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tfd, err := syscall.Socket(udpAddrFamily(network, lAddr, rAddr), syscall.SOCK_DGRAM, 0)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdefer func() {\n\t\tif err != nil {\n\t\t\tsyscall.Close(fd)\n\t\t}\n\t}()\n\n\tif err = syscall.SetsockoptInt(fd, syscall.SOL_SOCKET, syscall.SO_REUSEADDR, 1); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err = syscall.SetsockoptInt(fd, syscall.SOL_IP, syscall.IP_TRANSPARENT, 1); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err = syscall.Bind(fd, lSockAddr); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif err = syscall.Connect(fd, rSockAddr); err != nil {\n\t\treturn nil, err\n\t}\n\n\tfdFile := os.NewFile(uintptr(fd), fmt.Sprintf(\"net-udp-dial-%s\", rAddr.String()))\n\tdefer fdFile.Close()\n\n\tc, err := net.FileConn(fdFile)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn c.(*net.UDPConn), nil\n}\n\nfunc udpAddrToSockAddr(addr netip.AddrPort) (syscall.Sockaddr, error) {\n\tif addr.Addr().Is4() {\n\t\treturn &syscall.SockaddrInet4{Addr: addr.Addr().As4(), Port: int(addr.Port())}, nil\n\t}\n\n\tzoneID, err := strconv.ParseUint(addr.Addr().Zone(), 10, 32)\n\tif err != nil {\n\t\tzoneID = 0\n\t}\n\n\treturn &syscall.SockaddrInet6{Addr: addr.Addr().As16(), Port: int(addr.Port()), ZoneId: uint32(zoneID)}, nil\n}\n\nfunc udpAddrFamily(net string, lAddr, rAddr netip.AddrPort) int {\n\tswitch net[len(net)-1] {\n\tcase '4':\n\t\treturn syscall.AF_INET\n\tcase '6':\n\t\treturn syscall.AF_INET6\n\t}\n\n\tif lAddr.Addr().Is4() && rAddr.Addr().Is4() {\n\t\treturn syscall.AF_INET\n\t}\n\treturn syscall.AF_INET6\n}\n\nfunc getOrigDst(oob []byte) (netip.AddrPort, error) {\n\t// oob contains socket control messages which we need to parse.\n\tscms, err := unix.ParseSocketControlMessage(oob)\n\tif err != nil {\n\t\treturn netip.AddrPort{}, fmt.Errorf(\"parse control message: %w\", err)\n\t}\n\n\t// retrieve the destination address from the SCM.\n\tvar sa unix.Sockaddr\n\tfor i := range scms {\n\t\tsa, err = unix.ParseOrigDstAddr(&scms[i])\n\t\tif err == nil {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif err != nil {\n\t\treturn netip.AddrPort{}, fmt.Errorf(\"retrieve destination: %w\", err)\n\t}\n\n\t// encode the destination address into a cmsg.\n\tvar rAddr netip.AddrPort\n\tswitch v := sa.(type) {\n\tcase *unix.SockaddrInet4:\n\t\trAddr = netip.AddrPortFrom(netip.AddrFrom4(v.Addr), uint16(v.Port))\n\tcase *unix.SockaddrInet6:\n\t\trAddr = netip.AddrPortFrom(netip.AddrFrom16(v.Addr), uint16(v.Port))\n\tdefault:\n\t\treturn netip.AddrPort{}, fmt.Errorf(\"unsupported address type: %T\", v)\n\t}\n\n\treturn rAddr, nil\n}\n\nfunc getDSCP(oob []byte) (uint8, error) {\n\tscms, err := unix.ParseSocketControlMessage(oob)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"parse control message: %w\", err)\n\t}\n\tvar dscp uint8\n\tfor i := range scms {\n\t\tdscp, err = parseDSCP(&scms[i])\n\t\tif err == nil {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"retrieve DSCP: %w\", err)\n\t}\n\treturn dscp, nil\n}\n\nfunc parseDSCP(m *unix.SocketControlMessage) (uint8, error) {\n\tswitch {\n\tcase m.Header.Level == unix.SOL_IP && m.Header.Type == unix.IP_TOS:\n\t\tdscp := uint8(m.Data[0] >> 2)\n\t\treturn dscp, nil\n\n\tcase m.Header.Level == unix.SOL_IPV6 && m.Header.Type == unix.IPV6_TCLASS:\n\t\tdscp := uint8(m.Data[0] >> 2)\n\t\treturn dscp, nil\n\n\tdefault:\n\t\treturn 0, nil\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/tproxy/udp_other.go",
    "content": "//go:build !linux\n\npackage tproxy\n\nimport (\n\t\"errors\"\n\t\"net\"\n\t\"net/netip\"\n)\n\nfunc getOrigDst(oob []byte) (netip.AddrPort, error) {\n\treturn netip.AddrPort{}, errors.New(\"UDP redir not supported on current platform\")\n}\n\nfunc getDSCP(oob []byte) (uint8, error) {\n\treturn 0, errors.New(\"UDP redir not supported on current platform\")\n}\n\nfunc dialUDP(network string, lAddr, rAddr netip.AddrPort) (*net.UDPConn, error) {\n\treturn nil, errors.New(\"UDP redir not supported on current platform\")\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/trojan/packet.go",
    "content": "package trojan\n\nimport (\n\t\"errors\"\n\t\"net\"\n)\n\ntype packet struct {\n\tpc      net.PacketConn\n\trAddr   net.Addr\n\tpayload []byte\n\tput     func()\n}\n\nfunc (c *packet) Data() []byte {\n\treturn c.payload\n}\n\n// WriteBack wirtes UDP packet with source(ip, port) = `addr`\nfunc (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {\n\tif addr == nil {\n\t\terr = errors.New(\"address is invalid\")\n\t\treturn\n\t}\n\treturn c.pc.WriteTo(b, addr)\n}\n\n// LocalAddr returns the source IP/Port of UDP Packet\nfunc (c *packet) LocalAddr() net.Addr {\n\treturn c.rAddr\n}\n\nfunc (c *packet) Drop() {\n\tif c.put != nil {\n\t\tc.put()\n\t\tc.put = nil\n\t}\n\tc.payload = nil\n}\n\nfunc (c *packet) InAddr() net.Addr {\n\treturn c.pc.LocalAddr()\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/trojan/server.go",
    "content": "package trojan\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"github.com/metacubex/mihomo/component/ca\"\n\t\"github.com/metacubex/mihomo/component/ech\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/reality\"\n\t\"github.com/metacubex/mihomo/listener/sing\"\n\t\"github.com/metacubex/mihomo/ntp\"\n\t\"github.com/metacubex/mihomo/transport/gun\"\n\t\"github.com/metacubex/mihomo/transport/shadowsocks/core\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n\t\"github.com/metacubex/mihomo/transport/trojan\"\n\tmihomoVMess \"github.com/metacubex/mihomo/transport/vmess\"\n\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/smux\"\n\t\"github.com/metacubex/tls\"\n)\n\ntype Listener struct {\n\tclosed     bool\n\tconfig     LC.TrojanServer\n\tlisteners  []net.Listener\n\tkeys       map[[trojan.KeyLength]byte]string\n\tpickCipher core.Cipher\n\thandler    *sing.ListenerHandler\n}\n\nfunc New(config LC.TrojanServer, tunnel C.Tunnel, additions ...inbound.Addition) (sl *Listener, err error) {\n\tif len(additions) == 0 {\n\t\tadditions = []inbound.Addition{\n\t\t\tinbound.WithInName(\"DEFAULT-TROJAN\"),\n\t\t\tinbound.WithSpecialRules(\"\"),\n\t\t}\n\t}\n\th, err := sing.NewListenerHandler(sing.ListenerConfig{\n\t\tTunnel:    tunnel,\n\t\tType:      C.TROJAN,\n\t\tAdditions: additions,\n\t\tMuxOption: config.MuxOption,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tkeys := make(map[[trojan.KeyLength]byte]string)\n\tfor _, user := range config.Users {\n\t\tkeys[trojan.Key(user.Password)] = user.Username\n\t}\n\n\tvar pickCipher core.Cipher\n\tif config.TrojanSSOption.Enabled {\n\t\tif config.TrojanSSOption.Password == \"\" {\n\t\t\treturn nil, errors.New(\"empty password\")\n\t\t}\n\t\tif config.TrojanSSOption.Method == \"\" {\n\t\t\tconfig.TrojanSSOption.Method = \"AES-128-GCM\"\n\t\t}\n\t\tpickCipher, err = core.PickCipher(config.TrojanSSOption.Method, nil, config.TrojanSSOption.Password)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tsl = &Listener{false, config, nil, keys, pickCipher, h}\n\n\thttpServer := http.Server{\n\t\tIdleTimeout: 30 * time.Second,\n\t\tProtocols:   new(http.Protocols),\n\t}\n\ttlsConfig := &tls.Config{Time: ntp.Now}\n\tvar realityBuilder *reality.Builder\n\n\tif config.Certificate != \"\" && config.PrivateKey != \"\" {\n\t\tcertLoader, err := ca.NewTLSKeyPairLoader(config.Certificate, config.PrivateKey)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttlsConfig.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) {\n\t\t\treturn certLoader()\n\t\t}\n\n\t\tif config.EchKey != \"\" {\n\t\t\terr = ech.LoadECHKey(config.EchKey, tlsConfig)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\ttlsConfig.ClientAuth = ca.ClientAuthTypeFromString(config.ClientAuthType)\n\tif len(config.ClientAuthCert) > 0 {\n\t\tif tlsConfig.ClientAuth == tls.NoClientCert {\n\t\t\ttlsConfig.ClientAuth = tls.RequireAndVerifyClientCert\n\t\t}\n\t}\n\tif tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert {\n\t\tpool, err := ca.LoadCertificates(config.ClientAuthCert)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttlsConfig.ClientCAs = pool\n\t}\n\tif config.RealityConfig.PrivateKey != \"\" {\n\t\tif tlsConfig.GetCertificate != nil {\n\t\t\treturn nil, errors.New(\"certificate is unavailable in reality\")\n\t\t}\n\t\tif tlsConfig.ClientAuth != tls.NoClientCert {\n\t\t\treturn nil, errors.New(\"client-auth is unavailable in reality\")\n\t\t}\n\t\trealityBuilder, err = config.RealityConfig.Build(tunnel)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tif config.WsPath != \"\" {\n\t\thttpMux := http.NewServeMux()\n\t\thttpMux.HandleFunc(config.WsPath, func(w http.ResponseWriter, r *http.Request) {\n\t\t\tconn, err := mihomoVMess.StreamUpgradedWebsocketConn(w, r)\n\t\t\tif err != nil {\n\t\t\t\thttp.Error(w, err.Error(), 500)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tsl.HandleConn(conn, tunnel, additions...)\n\t\t})\n\t\thttpServer.Handler = httpMux\n\t\thttpServer.Protocols.SetHTTP1(true)\n\t\ttlsConfig.NextProtos = append(tlsConfig.NextProtos, \"http/1.1\")\n\t}\n\tif config.GrpcServiceName != \"\" {\n\t\thttpServer.Handler = gun.NewServerHandler(gun.ServerOption{\n\t\t\tServiceName: config.GrpcServiceName,\n\t\t\tConnHandler: func(conn net.Conn) {\n\t\t\t\tsl.HandleConn(conn, tunnel, additions...)\n\t\t\t},\n\t\t\tHttpHandler: httpServer.Handler,\n\t\t})\n\t\thttpServer.Protocols.SetHTTP2(true)\n\t\t// SetUnencryptedHTTP2 to ensure we can work in plain http2 and some tls conn is not *tls.Conn (like *reality.Conn)\n\t\thttpServer.Protocols.SetUnencryptedHTTP2(true)\n\t\ttlsConfig.NextProtos = append([]string{\"h2\"}, tlsConfig.NextProtos...) // h2 must before http/1.1\n\t}\n\n\tfor _, addr := range strings.Split(config.Listen, \",\") {\n\t\taddr := addr\n\n\t\t//TCP\n\t\tl, err := inbound.Listen(\"tcp\", addr)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif realityBuilder != nil {\n\t\t\tl = realityBuilder.NewListener(l)\n\t\t} else if tlsConfig.GetCertificate != nil {\n\t\t\tl = tls.NewListener(l, tlsConfig)\n\t\t} else if !config.TrojanSSOption.Enabled {\n\t\t\treturn nil, errors.New(\"disallow using Trojan without both certificates/reality/ss config\")\n\t\t}\n\t\tsl.listeners = append(sl.listeners, l)\n\n\t\tgo func() {\n\t\t\tif httpServer.Handler != nil {\n\t\t\t\t_ = httpServer.Serve(l)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tfor {\n\t\t\t\tc, err := l.Accept()\n\t\t\t\tif err != nil {\n\t\t\t\t\tif sl.closed {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tgo sl.HandleConn(c, tunnel, additions...)\n\t\t\t}\n\t\t}()\n\t}\n\n\treturn sl, nil\n}\n\nfunc (l *Listener) Close() error {\n\tl.closed = true\n\tvar retErr error\n\tfor _, lis := range l.listeners {\n\t\terr := lis.Close()\n\t\tif err != nil {\n\t\t\tretErr = err\n\t\t}\n\t}\n\treturn retErr\n}\n\nfunc (l *Listener) Config() string {\n\treturn l.config.String()\n}\n\nfunc (l *Listener) AddrList() (addrList []net.Addr) {\n\tfor _, lis := range l.listeners {\n\t\taddrList = append(addrList, lis.Addr())\n\t}\n\treturn\n}\n\nfunc (l *Listener) HandleConn(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) {\n\tdefer conn.Close()\n\n\tif l.pickCipher != nil {\n\t\tconn = l.pickCipher.StreamConn(conn)\n\t}\n\n\tvar key [trojan.KeyLength]byte\n\tif _, err := io.ReadFull(conn, key[:]); err != nil {\n\t\t//log.Warnln(\"read key error: %s\", err.Error())\n\t\treturn\n\t}\n\n\tif user, ok := l.keys[key]; ok {\n\t\tadditions = append(additions, inbound.WithInUser(user))\n\t} else {\n\t\t//log.Warnln(\"no such key\")\n\t\treturn\n\t}\n\n\tvar crlf [2]byte\n\tif _, err := io.ReadFull(conn, crlf[:]); err != nil {\n\t\t//log.Warnln(\"read crlf error: %s\", err.Error())\n\t\treturn\n\t}\n\n\tl.handleConn(false, conn, tunnel, additions...)\n}\n\nfunc (l *Listener) handleConn(inMux bool, conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) {\n\tif inMux {\n\t\tdefer conn.Close()\n\t}\n\n\tcommand, err := socks5.ReadByte(conn)\n\tif err != nil {\n\t\t//log.Warnln(\"read command error: %s\", err.Error())\n\t\treturn\n\t}\n\n\tswitch command {\n\tcase trojan.CommandTCP, trojan.CommandUDP, trojan.CommandMux:\n\tdefault:\n\t\t//log.Warnln(\"unknown command: %d\", command)\n\t\treturn\n\t}\n\n\ttarget, err := socks5.ReadAddr0(conn)\n\tif err != nil {\n\t\t//log.Warnln(\"read target error: %s\", err.Error())\n\t\treturn\n\t}\n\n\tif !inMux {\n\t\tvar crlf [2]byte\n\t\tif _, err := io.ReadFull(conn, crlf[:]); err != nil {\n\t\t\t//log.Warnln(\"read crlf error: %s\", err.Error())\n\t\t\treturn\n\t\t}\n\t}\n\n\tswitch command {\n\tcase trojan.CommandTCP:\n\t\t//tunnel.HandleTCPConn(inbound.NewSocket(target, conn, C.TROJAN, additions...))\n\t\tl.handler.HandleSocket(target, conn, additions...)\n\tcase trojan.CommandUDP:\n\t\tpc := trojan.NewPacketConn(conn)\n\t\tremoteAddr := conn.RemoteAddr()\n\t\tconnID := utils.NewUUIDV4().String() // make a new SNAT key\n\n\t\tfor {\n\t\t\tdata, put, addr, err := pc.WaitReadFrom()\n\t\t\tif err != nil {\n\t\t\t\tif put != nil {\n\t\t\t\t\tput()\n\t\t\t\t}\n\t\t\t\tbreak\n\t\t\t}\n\t\t\ttarget := socks5.ParseAddrToSocksAddr(addr)\n\t\t\tcPacket := &packet{\n\t\t\t\tpc:      pc,\n\t\t\t\trAddr:   remoteAddr,\n\t\t\t\tpayload: data,\n\t\t\t\tput:     put,\n\t\t\t}\n\t\t\tcPacket.rAddr = N.NewCustomAddr(C.TROJAN.String(), connID, cPacket.rAddr) // for tunnel's handleUDPConn\n\n\t\t\ttunnel.HandleUDPPacket(inbound.NewPacket(target, cPacket, C.TROJAN, additions...))\n\t\t}\n\tcase trojan.CommandMux:\n\t\tif inMux {\n\t\t\t//log.Warnln(\"invalid command: %d\", command)\n\t\t\treturn\n\t\t}\n\t\tsmuxConfig := smux.DefaultConfig()\n\t\tsmuxConfig.KeepAliveDisabled = true\n\t\tsession, err := smux.Server(conn, smuxConfig)\n\t\tif err != nil {\n\t\t\t//log.Warnln(\"smux server error: %s\", err.Error())\n\t\t\treturn\n\t\t}\n\t\tdefer session.Close()\n\t\tfor {\n\t\t\tstream, err := session.AcceptStream()\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tgo l.handleConn(true, stream, tunnel, additions...)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/trusttunnel/server.go",
    "content": "package trusttunnel\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net\"\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\t\"github.com/metacubex/mihomo/common/sockopt\"\n\t\"github.com/metacubex/mihomo/component/ca\"\n\t\"github.com/metacubex/mihomo/component/ech\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/sing\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/ntp\"\n\t\"github.com/metacubex/mihomo/transport/trusttunnel\"\n\n\t\"github.com/metacubex/tls\"\n)\n\ntype Listener struct {\n\tclosed       bool\n\tconfig       LC.TrustTunnelServer\n\tlisteners    []net.Listener\n\tudpListeners []net.PacketConn\n\ttlsConfig    *tls.Config\n\tservices     []*trusttunnel.Service\n}\n\nfunc New(config LC.TrustTunnelServer, tunnel C.Tunnel, additions ...inbound.Addition) (sl *Listener, err error) {\n\tif len(additions) == 0 {\n\t\tadditions = []inbound.Addition{\n\t\t\tinbound.WithInName(\"DEFAULT-TRUSTTUNNEL\"),\n\t\t\tinbound.WithSpecialRules(\"\"),\n\t\t}\n\t}\n\n\ttlsConfig := &tls.Config{Time: ntp.Now}\n\tif config.Certificate != \"\" && config.PrivateKey != \"\" {\n\t\tcertLoader, err := ca.NewTLSKeyPairLoader(config.Certificate, config.PrivateKey)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttlsConfig.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) {\n\t\t\treturn certLoader()\n\t\t}\n\n\t\tif config.EchKey != \"\" {\n\t\t\terr = ech.LoadECHKey(config.EchKey, tlsConfig)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t}\n\ttlsConfig.ClientAuth = ca.ClientAuthTypeFromString(config.ClientAuthType)\n\tif len(config.ClientAuthCert) > 0 {\n\t\tif tlsConfig.ClientAuth == tls.NoClientCert {\n\t\t\ttlsConfig.ClientAuth = tls.RequireAndVerifyClientCert\n\t\t}\n\t}\n\tif tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert {\n\t\tpool, err := ca.LoadCertificates(config.ClientAuthCert)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttlsConfig.ClientCAs = pool\n\t}\n\n\tsl = &Listener{\n\t\tconfig:    config,\n\t\ttlsConfig: tlsConfig,\n\t}\n\n\th, err := sing.NewListenerHandler(sing.ListenerConfig{\n\t\tTunnel:    tunnel,\n\t\tType:      C.TRUSTTUNNEL,\n\t\tAdditions: additions,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif tlsConfig.GetCertificate == nil {\n\t\treturn nil, errors.New(\"disallow using TrustTunnel without certificates config\")\n\t}\n\n\tif len(config.Network) == 0 {\n\t\tconfig.Network = []string{\"tcp\"}\n\t}\n\tlistenTCP, listenUDP := false, false\n\tfor _, network := range config.Network {\n\t\tnetwork = strings.ToLower(network)\n\t\tswitch {\n\t\tcase strings.HasPrefix(network, \"tcp\"):\n\t\t\tlistenTCP = true\n\t\tcase strings.HasPrefix(network, \"udp\"):\n\t\t\tlistenUDP = true\n\t\t}\n\t}\n\n\tfor _, addr := range strings.Split(config.Listen, \",\") {\n\t\taddr := addr\n\n\t\tvar (\n\t\t\ttcpListener net.Listener\n\t\t\tudpConn     net.PacketConn\n\t\t)\n\t\tif listenTCP {\n\t\t\ttcpListener, err = inbound.Listen(\"tcp\", addr)\n\t\t\tif err != nil {\n\t\t\t\t_ = sl.Close()\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tsl.listeners = append(sl.listeners, tcpListener)\n\t\t}\n\t\tif listenUDP {\n\t\t\tudpConn, err = inbound.ListenPacket(\"udp\", addr)\n\t\t\tif err != nil {\n\t\t\t\t_ = sl.Close()\n\t\t\t\treturn nil, err\n\t\t\t}\n\n\t\t\tif err := sockopt.UDPReuseaddr(udpConn); err != nil {\n\t\t\t\tlog.Warnln(\"Failed to Reuse UDP Address: %s\", err)\n\t\t\t}\n\t\t\tsl.udpListeners = append(sl.udpListeners, udpConn)\n\t\t}\n\n\t\tservice := trusttunnel.NewService(trusttunnel.ServiceOptions{\n\t\t\tCtx:                   context.Background(),\n\t\t\tLogger:                log.SingLogger,\n\t\t\tHandler:               h,\n\t\t\tICMPHandler:           nil,\n\t\t\tQUICCongestionControl: config.CongestionController,\n\t\t\tQUICCwnd:              config.CWND,\n\t\t\tQUICBBRProfile:        config.BBRProfile,\n\t\t})\n\t\tservice.UpdateUsers(config.Users)\n\t\terr = service.Start(tcpListener, udpConn, tlsConfig)\n\t\tif err != nil {\n\t\t\t_ = sl.Close()\n\t\t\treturn nil, err\n\t\t}\n\n\t\tsl.services = append(sl.services, service)\n\t}\n\n\treturn sl, nil\n}\n\nfunc (l *Listener) Close() error {\n\tl.closed = true\n\tvar retErr error\n\tfor _, lis := range l.services {\n\t\terr := lis.Close()\n\t\tif err != nil {\n\t\t\tretErr = err\n\t\t}\n\t}\n\tfor _, lis := range l.listeners {\n\t\terr := lis.Close()\n\t\tif err != nil {\n\t\t\tretErr = err\n\t\t}\n\t}\n\tfor _, lis := range l.udpListeners {\n\t\terr := lis.Close()\n\t\tif err != nil {\n\t\t\tretErr = err\n\t\t}\n\t}\n\treturn retErr\n}\n\nfunc (l *Listener) Config() string {\n\treturn l.config.String()\n}\n\nfunc (l *Listener) AddrList() (addrList []net.Addr) {\n\tfor _, lis := range l.listeners {\n\t\taddrList = append(addrList, lis.Addr())\n\t}\n\tfor _, lis := range l.udpListeners {\n\t\taddrList = append(addrList, lis.LocalAddr())\n\t}\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/tuic/server.go",
    "content": "package tuic\n\nimport (\n\t\"net\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\t\"github.com/metacubex/mihomo/common/sockopt\"\n\t\"github.com/metacubex/mihomo/component/ca\"\n\t\"github.com/metacubex/mihomo/component/ech\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/sing\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/ntp\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n\t\"github.com/metacubex/mihomo/transport/tuic\"\n\n\t\"github.com/gofrs/uuid/v5\"\n\t\"github.com/metacubex/quic-go\"\n\t\"github.com/metacubex/tls\"\n\t\"golang.org/x/exp/slices\"\n)\n\nconst ServerMaxIncomingStreams = (1 << 32) - 1\n\ntype Listener struct {\n\tclosed       bool\n\tconfig       LC.TuicServer\n\tudpListeners []net.PacketConn\n\tservers      []*tuic.Server\n}\n\nfunc New(config LC.TuicServer, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {\n\tif len(additions) == 0 {\n\t\tadditions = []inbound.Addition{\n\t\t\tinbound.WithInName(\"DEFAULT-TUIC\"),\n\t\t\tinbound.WithSpecialRules(\"\"),\n\t\t}\n\t}\n\th, err := sing.NewListenerHandler(sing.ListenerConfig{\n\t\tTunnel:    tunnel,\n\t\tType:      C.TUIC,\n\t\tAdditions: additions,\n\t\tMuxOption: config.MuxOption,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttlsConfig := &tls.Config{\n\t\tTime:       ntp.Now,\n\t\tMinVersion: tls.VersionTLS13,\n\t}\n\tcertLoader, err := ca.NewTLSKeyPairLoader(config.Certificate, config.PrivateKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\ttlsConfig.GetCertificate = func(*tls.ClientHelloInfo) (*tls.Certificate, error) {\n\t\treturn certLoader()\n\t}\n\ttlsConfig.ClientAuth = ca.ClientAuthTypeFromString(config.ClientAuthType)\n\tif len(config.ClientAuthCert) > 0 {\n\t\tif tlsConfig.ClientAuth == tls.NoClientCert {\n\t\t\ttlsConfig.ClientAuth = tls.RequireAndVerifyClientCert\n\t\t}\n\t}\n\tif tlsConfig.ClientAuth == tls.VerifyClientCertIfGiven || tlsConfig.ClientAuth == tls.RequireAndVerifyClientCert {\n\t\tpool, err := ca.LoadCertificates(config.ClientAuthCert)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttlsConfig.ClientCAs = pool\n\t}\n\n\tif config.EchKey != \"\" {\n\t\terr = ech.LoadECHKey(config.EchKey, tlsConfig)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tif len(config.ALPN) > 0 {\n\t\ttlsConfig.NextProtos = config.ALPN\n\t} else {\n\t\ttlsConfig.NextProtos = []string{\"h3\"}\n\t}\n\n\tif config.MaxIdleTime == 0 {\n\t\tconfig.MaxIdleTime = 15000\n\t}\n\tif config.AuthenticationTimeout == 0 {\n\t\tconfig.AuthenticationTimeout = 1000\n\t}\n\n\tquicConfig := &quic.Config{\n\t\tMaxIdleTimeout:        time.Duration(config.MaxIdleTime) * time.Millisecond,\n\t\tMaxIncomingStreams:    ServerMaxIncomingStreams,\n\t\tMaxIncomingUniStreams: ServerMaxIncomingStreams,\n\t\tEnableDatagrams:       true,\n\t\tAllow0RTT:             true,\n\t\tDisablePathManager:    true, // for port hopping\n\t}\n\tquicConfig.InitialStreamReceiveWindow = tuic.DefaultStreamReceiveWindow / 10\n\tquicConfig.MaxStreamReceiveWindow = tuic.DefaultStreamReceiveWindow\n\tquicConfig.InitialConnectionReceiveWindow = tuic.DefaultConnectionReceiveWindow / 10\n\tquicConfig.MaxConnectionReceiveWindow = tuic.DefaultConnectionReceiveWindow\n\n\tpacketOverHead := tuic.PacketOverHeadV4\n\tif len(config.Token) == 0 {\n\t\tpacketOverHead = tuic.PacketOverHeadV5\n\t}\n\n\tif config.CWND == 0 {\n\t\tconfig.CWND = 32\n\t}\n\n\tif config.MaxUdpRelayPacketSize == 0 {\n\t\tconfig.MaxUdpRelayPacketSize = 1500\n\t}\n\tmaxDatagramFrameSize := config.MaxUdpRelayPacketSize + packetOverHead\n\tif maxDatagramFrameSize > 1400 {\n\t\tmaxDatagramFrameSize = 1400\n\t}\n\tconfig.MaxUdpRelayPacketSize = maxDatagramFrameSize - packetOverHead\n\tquicConfig.MaxDatagramFrameSize = int64(maxDatagramFrameSize)\n\n\thandleTcpFn := func(conn net.Conn, addr socks5.Addr, _additions ...inbound.Addition) error {\n\t\t//newAdditions := additions\n\t\t//if len(_additions) > 0 {\n\t\t//\tnewAdditions = slices.Clone(additions)\n\t\t//\tnewAdditions = append(newAdditions, _additions...)\n\t\t//}\n\t\t//conn, metadata := inbound.NewSocket(addr, conn, C.TUIC, newAdditions...)\n\t\t//go tunnel.HandleTCPConn(conn, metadata)\n\t\tgo h.HandleSocket(addr, conn, _additions...) // h.HandleSocket will block, so open a new goroutine\n\t\treturn nil\n\t}\n\thandleUdpFn := func(addr socks5.Addr, packet C.UDPPacket, _additions ...inbound.Addition) error {\n\t\tnewAdditions := additions\n\t\tif len(_additions) > 0 {\n\t\t\tnewAdditions = slices.Clone(additions)\n\t\t\tnewAdditions = append(newAdditions, _additions...)\n\t\t}\n\t\ttunnel.HandleUDPPacket(inbound.NewPacket(addr, packet, C.TUIC, newAdditions...))\n\t\treturn nil\n\t}\n\n\toption := &tuic.ServerOption{\n\t\tHandleTcpFn:           handleTcpFn,\n\t\tHandleUdpFn:           handleUdpFn,\n\t\tTlsConfig:             tlsConfig,\n\t\tQuicConfig:            quicConfig,\n\t\tCongestionController:  config.CongestionController,\n\t\tAuthenticationTimeout: time.Duration(config.AuthenticationTimeout) * time.Millisecond,\n\t\tMaxUdpRelayPacketSize: config.MaxUdpRelayPacketSize,\n\t\tCWND:                  config.CWND,\n\t\tBBRProfile:            config.BBRProfile,\n\t}\n\tif len(config.Token) > 0 {\n\t\ttokens := make([][32]byte, len(config.Token))\n\t\tfor i, token := range config.Token {\n\t\t\ttokens[i] = tuic.GenTKN(token)\n\t\t}\n\t\toption.Tokens = tokens\n\t}\n\tif len(config.Users) > 0 {\n\t\tusers := make(map[[16]byte]string)\n\t\tfor _uuid, password := range config.Users {\n\t\t\tusers[uuid.FromStringOrNil(_uuid)] = password\n\t\t}\n\t\toption.Users = users\n\t}\n\n\tsl := &Listener{false, config, nil, nil}\n\n\tfor _, addr := range strings.Split(config.Listen, \",\") {\n\t\taddr := addr\n\n\t\tul, err := inbound.ListenPacket(\"udp\", addr)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif err := sockopt.UDPReuseaddr(ul); err != nil {\n\t\t\tlog.Warnln(\"Failed to Reuse UDP Address: %s\", err)\n\t\t}\n\n\t\tsl.udpListeners = append(sl.udpListeners, ul)\n\n\t\tvar server *tuic.Server\n\t\tserver, err = tuic.NewServer(option, ul)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tsl.servers = append(sl.servers, server)\n\n\t\tgo func() {\n\t\t\terr := server.Serve()\n\t\t\tif err != nil {\n\t\t\t\tif sl.closed {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\t}\n\n\treturn sl, nil\n}\n\nfunc (l *Listener) Close() error {\n\tl.closed = true\n\tvar retErr error\n\tfor _, lis := range l.servers {\n\t\terr := lis.Close()\n\t\tif err != nil {\n\t\t\tretErr = err\n\t\t}\n\t}\n\tfor _, lis := range l.udpListeners {\n\t\terr := lis.Close()\n\t\tif err != nil {\n\t\t\tretErr = err\n\t\t}\n\t}\n\treturn retErr\n}\n\nfunc (l *Listener) Config() LC.TuicServer {\n\treturn l.config\n}\n\nfunc (l *Listener) AddrList() (addrList []net.Addr) {\n\tfor _, lis := range l.udpListeners {\n\t\taddrList = append(addrList, lis.LocalAddr())\n\t}\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/tunnel/packet.go",
    "content": "package tunnel\n\nimport (\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/common/pool\"\n)\n\ntype packet struct {\n\tpc      net.PacketConn\n\trAddr   net.Addr\n\tpayload []byte\n}\n\nfunc (c *packet) Data() []byte {\n\treturn c.payload\n}\n\n// WriteBack write UDP packet with source(ip, port) = `addr`\nfunc (c *packet) WriteBack(b []byte, addr net.Addr) (n int, err error) {\n\treturn c.pc.WriteTo(b, c.rAddr)\n}\n\n// LocalAddr returns the source IP/Port of UDP Packet\nfunc (c *packet) LocalAddr() net.Addr {\n\treturn c.rAddr\n}\n\nfunc (c *packet) Drop() {\n\t_ = pool.Put(c.payload)\n\tc.payload = nil\n}\n\nfunc (c *packet) InAddr() net.Addr {\n\treturn c.pc.LocalAddr()\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/tunnel/tcp.go",
    "content": "package tunnel\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n)\n\ntype Listener struct {\n\tlistener net.Listener\n\taddr     string\n\ttarget   socks5.Addr\n\tproxy    string\n\tclosed   bool\n}\n\n// RawAddress implements C.Listener\nfunc (l *Listener) RawAddress() string {\n\treturn l.addr\n}\n\n// Address implements C.Listener\nfunc (l *Listener) Address() string {\n\treturn l.listener.Addr().String()\n}\n\n// Close implements C.Listener\nfunc (l *Listener) Close() error {\n\tl.closed = true\n\treturn l.listener.Close()\n}\n\nfunc (l *Listener) handleTCP(conn net.Conn, tunnel C.Tunnel, additions ...inbound.Addition) {\n\ttunnel.HandleTCPConn(inbound.NewSocket(l.target, conn, C.TUNNEL, additions...))\n}\n\nfunc New(addr, target, proxy string, tunnel C.Tunnel, additions ...inbound.Addition) (*Listener, error) {\n\tl, err := inbound.Listen(\"tcp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttargetAddr := socks5.ParseAddr(target)\n\tif targetAddr == nil {\n\t\treturn nil, fmt.Errorf(\"invalid target address %s\", target)\n\t}\n\n\trl := &Listener{\n\t\tlistener: l,\n\t\ttarget:   targetAddr,\n\t\tproxy:    proxy,\n\t\taddr:     addr,\n\t}\n\n\tif proxy != \"\" {\n\t\tadditions = append([]inbound.Addition{inbound.WithSpecialProxy(proxy)}, additions...)\n\t}\n\n\tgo func() {\n\t\tfor {\n\t\t\tc, err := l.Accept()\n\t\t\tif err != nil {\n\t\t\t\tif rl.closed {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tgo rl.handleTCP(c, tunnel, additions...)\n\t\t}\n\t}()\n\n\treturn rl, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/listener/tunnel/udp.go",
    "content": "package tunnel\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\t\"github.com/metacubex/mihomo/common/pool\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n)\n\ntype PacketConn struct {\n\tconn   net.PacketConn\n\taddr   string\n\ttarget socks5.Addr\n\tproxy  string\n\tclosed bool\n}\n\n// RawAddress implements C.Listener\nfunc (l *PacketConn) RawAddress() string {\n\treturn l.addr\n}\n\n// Address implements C.Listener\nfunc (l *PacketConn) Address() string {\n\treturn l.conn.LocalAddr().String()\n}\n\n// Close implements C.Listener\nfunc (l *PacketConn) Close() error {\n\tl.closed = true\n\treturn l.conn.Close()\n}\n\nfunc NewUDP(addr, target, proxy string, tunnel C.Tunnel, additions ...inbound.Addition) (*PacketConn, error) {\n\tl, err := net.ListenPacket(\"udp\", addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttargetAddr := socks5.ParseAddr(target)\n\tif targetAddr == nil {\n\t\treturn nil, fmt.Errorf(\"invalid target address %s\", target)\n\t}\n\n\tsl := &PacketConn{\n\t\tconn:   l,\n\t\ttarget: targetAddr,\n\t\tproxy:  proxy,\n\t\taddr:   addr,\n\t}\n\n\tif proxy != \"\" {\n\t\tadditions = append([]inbound.Addition{inbound.WithSpecialProxy(proxy)}, additions...)\n\t}\n\n\tgo func() {\n\t\tfor {\n\t\t\tbuf := pool.Get(pool.UDPBufferSize)\n\t\t\tn, remoteAddr, err := l.ReadFrom(buf)\n\t\t\tif err != nil {\n\t\t\t\tpool.Put(buf)\n\t\t\t\tif sl.closed {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tsl.handleUDP(l, tunnel, buf[:n], remoteAddr, additions...)\n\t\t}\n\t}()\n\n\treturn sl, nil\n}\n\nfunc (l *PacketConn) handleUDP(pc net.PacketConn, tunnel C.Tunnel, buf []byte, addr net.Addr, additions ...inbound.Addition) {\n\tcPacket := &packet{\n\t\tpc:      pc,\n\t\trAddr:   addr,\n\t\tpayload: buf,\n\t}\n\n\ttunnel.HandleUDPPacket(inbound.NewPacket(l.target, cPacket, C.TUNNEL, additions...))\n}\n"
  },
  {
    "path": "core/Clash.Meta/log/level.go",
    "content": "package log\n\nimport (\n\t\"errors\"\n\t\"strings\"\n)\n\n// LogLevelMapping is a mapping for LogLevel enum\nvar LogLevelMapping = map[string]LogLevel{\n\tERROR.String():   ERROR,\n\tWARNING.String(): WARNING,\n\tINFO.String():    INFO,\n\tDEBUG.String():   DEBUG,\n\tSILENT.String():  SILENT,\n}\n\nconst (\n\tDEBUG LogLevel = iota\n\tINFO\n\tWARNING\n\tERROR\n\tSILENT\n)\n\ntype LogLevel int\n\n// UnmarshalText unserialize LogLevel\nfunc (l *LogLevel) UnmarshalText(data []byte) error {\n\tlevel, exist := LogLevelMapping[strings.ToLower(string(data))]\n\tif !exist {\n\t\treturn errors.New(\"invalid log-level\")\n\t}\n\t*l = level\n\treturn nil\n}\n\n// MarshalText serialize LogLevel\nfunc (l LogLevel) MarshalText() ([]byte, error) {\n\treturn []byte(l.String()), nil\n}\n\nfunc (l LogLevel) String() string {\n\tswitch l {\n\tcase INFO:\n\t\treturn \"info\"\n\tcase WARNING:\n\t\treturn \"warning\"\n\tcase ERROR:\n\t\treturn \"error\"\n\tcase DEBUG:\n\t\treturn \"debug\"\n\tcase SILENT:\n\t\treturn \"silent\"\n\tdefault:\n\t\treturn \"unknown\"\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/log/log.go",
    "content": "package log\n\nimport (\n\t\"fmt\"\n\t\"os\"\n\n\t\"github.com/metacubex/mihomo/common/observable\"\n\n\tlog \"github.com/sirupsen/logrus\"\n)\n\nvar (\n\tlogCh  = make(chan Event)\n\tsource = observable.NewObservable[Event](logCh)\n\tlevel  = INFO\n)\n\nfunc init() {\n\tlog.SetOutput(os.Stdout)\n\tlog.SetLevel(log.DebugLevel)\n\tlog.SetFormatter(&log.TextFormatter{\n\t\tFullTimestamp:             true,\n\t\tTimestampFormat:           \"2006-01-02T15:04:05.000000000Z07:00\",\n\t\tEnvironmentOverrideColors: true,\n\t})\n}\n\ntype Event struct {\n\tLogLevel LogLevel\n\tPayload  string\n}\n\nfunc (e *Event) Type() string {\n\treturn e.LogLevel.String()\n}\n\nfunc Infoln(format string, v ...any) {\n\tevent := newLog(INFO, format, v...)\n\tlogCh <- event\n\tprint(event)\n}\n\nfunc Warnln(format string, v ...any) {\n\tevent := newLog(WARNING, format, v...)\n\tlogCh <- event\n\tprint(event)\n}\n\nfunc Errorln(format string, v ...any) {\n\tevent := newLog(ERROR, format, v...)\n\tlogCh <- event\n\tprint(event)\n}\n\nfunc Debugln(format string, v ...any) {\n\tevent := newLog(DEBUG, format, v...)\n\tlogCh <- event\n\tprint(event)\n}\n\nfunc Fatalln(format string, v ...any) {\n\tlog.Fatalf(format, v...)\n}\n\nfunc Subscribe() observable.Subscription[Event] {\n\tsub, _ := source.Subscribe()\n\treturn sub\n}\n\nfunc UnSubscribe(sub observable.Subscription[Event]) {\n\tsource.UnSubscribe(sub)\n}\n\nfunc Level() LogLevel {\n\treturn level\n}\n\nfunc SetLevel(newLevel LogLevel) {\n\tlevel = newLevel\n}\n\nfunc print(data Event) {\n\tif data.LogLevel < level {\n\t\treturn\n\t}\n\n\tswitch data.LogLevel {\n\tcase INFO:\n\t\tlog.Infoln(data.Payload)\n\tcase WARNING:\n\t\tlog.Warnln(data.Payload)\n\tcase ERROR:\n\t\tlog.Errorln(data.Payload)\n\tcase DEBUG:\n\t\tlog.Debugln(data.Payload)\n\t}\n}\n\nfunc newLog(logLevel LogLevel, format string, v ...any) Event {\n\treturn Event{\n\t\tLogLevel: logLevel,\n\t\tPayload:  fmt.Sprintf(format, v...),\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/log/sing.go",
    "content": "package log\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\n\tL \"github.com/metacubex/sing/common/logger\"\n)\n\ntype singLogger struct{}\n\nfunc (l singLogger) TraceContext(ctx context.Context, args ...any) {\n\tDebugln(fmt.Sprint(args...))\n}\n\nfunc (l singLogger) DebugContext(ctx context.Context, args ...any) {\n\tDebugln(fmt.Sprint(args...))\n}\n\nfunc (l singLogger) InfoContext(ctx context.Context, args ...any) {\n\tInfoln(fmt.Sprint(args...))\n}\n\nfunc (l singLogger) WarnContext(ctx context.Context, args ...any) {\n\tWarnln(fmt.Sprint(args...))\n}\n\nfunc (l singLogger) ErrorContext(ctx context.Context, args ...any) {\n\tErrorln(fmt.Sprint(args...))\n}\n\nfunc (l singLogger) FatalContext(ctx context.Context, args ...any) {\n\tFatalln(fmt.Sprint(args...))\n}\n\nfunc (l singLogger) PanicContext(ctx context.Context, args ...any) {\n\tFatalln(fmt.Sprint(args...))\n}\n\nfunc (l singLogger) Trace(args ...any) {\n\tDebugln(fmt.Sprint(args...))\n}\n\nfunc (l singLogger) Debug(args ...any) {\n\tDebugln(fmt.Sprint(args...))\n}\n\nfunc (l singLogger) Info(args ...any) {\n\tInfoln(fmt.Sprint(args...))\n}\n\nfunc (l singLogger) Warn(args ...any) {\n\tWarnln(fmt.Sprint(args...))\n}\n\nfunc (l singLogger) Error(args ...any) {\n\tErrorln(fmt.Sprint(args...))\n}\n\nfunc (l singLogger) Fatal(args ...any) {\n\tFatalln(fmt.Sprint(args...))\n}\n\nfunc (l singLogger) Panic(args ...any) {\n\tFatalln(fmt.Sprint(args...))\n}\n\ntype singInfoToDebugLogger struct {\n\tsingLogger\n}\n\nfunc (l singInfoToDebugLogger) InfoContext(ctx context.Context, args ...any) {\n\tDebugln(fmt.Sprint(args...))\n}\n\nfunc (l singInfoToDebugLogger) Info(args ...any) {\n\tDebugln(fmt.Sprint(args...))\n}\n\nvar SingLogger L.ContextLogger = singLogger{}\nvar SingInfoToDebugLogger L.ContextLogger = singInfoToDebugLogger{}\n"
  },
  {
    "path": "core/Clash.Meta/main.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"flag\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"os\"\n\t\"os/signal\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"syscall\"\n\n\t\"github.com/metacubex/mihomo/common/cmd\"\n\t\"github.com/metacubex/mihomo/component/generator\"\n\t\"github.com/metacubex/mihomo/component/geodata\"\n\t\"github.com/metacubex/mihomo/component/updater\"\n\t\"github.com/metacubex/mihomo/config\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/constant/features\"\n\t\"github.com/metacubex/mihomo/hub\"\n\t\"github.com/metacubex/mihomo/hub/executor\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/rules/provider\"\n\n\t\"go.uber.org/automaxprocs/maxprocs\"\n)\n\nvar (\n\tversion                bool\n\ttestConfig             bool\n\tgeodataMode            bool\n\thomeDir                string\n\tconfigFile             string\n\tconfigString           string\n\tconfigBytes            []byte\n\texternalUI             string\n\texternalController     string\n\texternalControllerUnix string\n\texternalControllerPipe string\n\tsecret                 string\n\tpostUp                 string\n\tpostDown               string\n)\n\nfunc init() {\n\tflag.StringVar(&homeDir, \"d\", os.Getenv(\"CLASH_HOME_DIR\"), \"set configuration directory\")\n\tflag.StringVar(&configFile, \"f\", os.Getenv(\"CLASH_CONFIG_FILE\"), \"specify configuration file\")\n\tflag.StringVar(&configString, \"config\", os.Getenv(\"CLASH_CONFIG_STRING\"), \"specify base64-encoded configuration string\")\n\tflag.StringVar(&externalUI, \"ext-ui\", os.Getenv(\"CLASH_OVERRIDE_EXTERNAL_UI_DIR\"), \"override external ui directory\")\n\tflag.StringVar(&externalController, \"ext-ctl\", os.Getenv(\"CLASH_OVERRIDE_EXTERNAL_CONTROLLER\"), \"override external controller address\")\n\tflag.StringVar(&externalControllerUnix, \"ext-ctl-unix\", os.Getenv(\"CLASH_OVERRIDE_EXTERNAL_CONTROLLER_UNIX\"), \"override external controller unix address\")\n\tflag.StringVar(&externalControllerPipe, \"ext-ctl-pipe\", os.Getenv(\"CLASH_OVERRIDE_EXTERNAL_CONTROLLER_PIPE\"), \"override external controller pipe address\")\n\tflag.StringVar(&secret, \"secret\", os.Getenv(\"CLASH_OVERRIDE_SECRET\"), \"override secret for RESTful API\")\n\tflag.StringVar(&postUp, \"post-up\", os.Getenv(\"CLASH_POST_UP\"), \"set post-up script\")\n\tflag.StringVar(&postDown, \"post-down\", os.Getenv(\"CLASH_POST_DOWN\"), \"set post-down script\")\n\tflag.BoolVar(&geodataMode, \"m\", false, \"set geodata mode\")\n\tflag.BoolVar(&version, \"v\", false, \"show current version of mihomo\")\n\tflag.BoolVar(&testConfig, \"t\", false, \"test configuration and exit\")\n\tflag.Parse()\n}\n\nfunc main() {\n\t// Defensive programming: panic when code mistakenly calls net.DefaultResolver\n\tnet.DefaultResolver.PreferGo = true\n\tnet.DefaultResolver.Dial = func(ctx context.Context, network, address string) (net.Conn, error) {\n\t\t//panic(\"should never be called\")\n\t\tbuf := make([]byte, 1024)\n\t\tfor {\n\t\t\tn := runtime.Stack(buf, true)\n\t\t\tif n < len(buf) {\n\t\t\t\tbuf = buf[:n]\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tbuf = make([]byte, 2*len(buf))\n\t\t}\n\t\tfmt.Fprintf(os.Stderr, \"panic: should never be called\\n\\n%s\", buf) // always print all goroutine stack\n\t\tos.Exit(2)\n\t\treturn nil, nil\n\t}\n\n\t_, _ = maxprocs.Set(maxprocs.Logger(func(string, ...any) {}))\n\n\tif len(os.Args) > 1 && os.Args[1] == \"convert-ruleset\" {\n\t\tprovider.ConvertMain(os.Args[2:])\n\t\treturn\n\t}\n\n\tif len(os.Args) > 1 && os.Args[1] == \"generate\" {\n\t\tgenerator.Main(os.Args[2:])\n\t\treturn\n\t}\n\n\tif version {\n\t\tfmt.Printf(\"Mihomo Meta %s %s %s with %s %s\\n\",\n\t\t\tC.Version, runtime.GOOS, runtime.GOARCH, runtime.Version(), C.BuildTime)\n\t\tif tags := features.Tags(); len(tags) != 0 {\n\t\t\tfmt.Printf(\"Use tags: %s\\n\", strings.Join(tags, \", \"))\n\t\t}\n\n\t\treturn\n\t}\n\n\tif homeDir != \"\" {\n\t\tif !filepath.IsAbs(homeDir) {\n\t\t\tcurrentDir, _ := os.Getwd()\n\t\t\thomeDir = filepath.Join(currentDir, homeDir)\n\t\t}\n\t\tC.SetHomeDir(homeDir)\n\t}\n\n\tif geodataMode {\n\t\tgeodata.SetGeodataMode(true)\n\t}\n\n\tif configString != \"\" {\n\t\tvar err error\n\t\tconfigBytes, err = base64.StdEncoding.DecodeString(configString)\n\t\tif err != nil {\n\t\t\tlog.Fatalln(\"Initial configuration error: %s\", err.Error())\n\t\t\treturn\n\t\t}\n\t} else if configFile == \"-\" {\n\t\tvar err error\n\t\tconfigBytes, err = io.ReadAll(os.Stdin)\n\t\tif err != nil {\n\t\t\tlog.Fatalln(\"Initial configuration error: %s\", err.Error())\n\t\t\treturn\n\t\t}\n\t} else {\n\t\tif configFile != \"\" {\n\t\t\tif !filepath.IsAbs(configFile) {\n\t\t\t\tcurrentDir, _ := os.Getwd()\n\t\t\t\tconfigFile = filepath.Join(currentDir, configFile)\n\t\t\t}\n\t\t} else {\n\t\t\tconfigFile = filepath.Join(C.Path.HomeDir(), C.Path.Config())\n\t\t}\n\t\tC.SetConfig(configFile)\n\n\t\tif err := config.Init(C.Path.HomeDir()); err != nil {\n\t\t\tlog.Fatalln(\"Initial configuration directory error: %s\", err.Error())\n\t\t}\n\t}\n\n\tif testConfig {\n\t\tif len(configBytes) != 0 {\n\t\t\tif _, err := executor.ParseWithBytes(configBytes); err != nil {\n\t\t\t\tlog.Errorln(err.Error())\n\t\t\t\tfmt.Println(\"configuration test failed\")\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t} else {\n\t\t\tif _, err := executor.Parse(); err != nil {\n\t\t\t\tlog.Errorln(err.Error())\n\t\t\t\tfmt.Printf(\"configuration file %s test failed\\n\", C.Path.Config())\n\t\t\t\tos.Exit(1)\n\t\t\t}\n\t\t}\n\t\tfmt.Printf(\"configuration file %s test is successful\\n\", C.Path.Config())\n\t\treturn\n\t}\n\n\tvar options []hub.Option\n\tif externalUI != \"\" {\n\t\toptions = append(options, hub.WithExternalUI(externalUI))\n\t}\n\tif externalController != \"\" {\n\t\toptions = append(options, hub.WithExternalController(externalController))\n\t}\n\tif externalControllerUnix != \"\" {\n\t\toptions = append(options, hub.WithExternalControllerUnix(externalControllerUnix))\n\t}\n\tif externalControllerPipe != \"\" {\n\t\toptions = append(options, hub.WithExternalControllerPipe(externalControllerPipe))\n\t}\n\tif secret != \"\" {\n\t\toptions = append(options, hub.WithSecret(secret))\n\t}\n\n\tif err := hub.Parse(configBytes, options...); err != nil {\n\t\tlog.Fatalln(\"Parse config error: %s\", err.Error())\n\t}\n\n\tif updater.GeoAutoUpdate() {\n\t\tupdater.RegisterGeoUpdater()\n\t}\n\n\tif postDown != \"\" {\n\t\tdefer func() {\n\t\t\tif _, err := cmd.ExecShell(postDown); err != nil {\n\t\t\t\tlog.Errorln(\"post-down script error: %s\", err.Error())\n\t\t\t}\n\t\t}()\n\t}\n\tif postUp != \"\" {\n\t\tif _, err := cmd.ExecShell(postUp); err != nil {\n\t\t\tlog.Fatalln(\"post-up script error: %s\", err.Error())\n\t\t}\n\t}\n\n\tdefer executor.Shutdown()\n\n\ttermSign := make(chan os.Signal, 1)\n\thupSign := make(chan os.Signal, 1)\n\tsignal.Notify(termSign, syscall.SIGINT, syscall.SIGTERM)\n\tsignal.Notify(hupSign, syscall.SIGHUP)\n\tfor {\n\t\tselect {\n\t\tcase <-termSign:\n\t\t\treturn\n\t\tcase <-hupSign:\n\t\t\tif err := hub.Parse(configBytes, options...); err != nil {\n\t\t\t\tlog.Errorln(\"Parse config error: %s\", err.Error())\n\t\t\t}\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/ntp/ntp/service.go",
    "content": "package ntp\n\nimport (\n\t\"context\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/component/dialer\"\n\t\"github.com/metacubex/mihomo/component/proxydialer\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n\tmihomoNtp \"github.com/metacubex/mihomo/ntp\"\n\n\tM \"github.com/metacubex/sing/common/metadata\"\n\t\"github.com/metacubex/sing/common/ntp\"\n)\n\nvar globalSrv *Service\nvar globalMu sync.Mutex\n\ntype Service struct {\n\tserver         M.Socksaddr\n\tdialer         proxydialer.SingDialer\n\tticker         *time.Ticker\n\tctx            context.Context\n\tcancel         context.CancelFunc\n\tsyncSystemTime bool\n}\n\nfunc ReCreateNTPService(server string, interval time.Duration, dialerProxy string, syncSystemTime bool) {\n\tglobalMu.Lock()\n\tdefer globalMu.Unlock()\n\tif globalSrv != nil {\n\t\tglobalSrv.Stop()\n\t}\n\tif server == \"\" || interval <= 0 {\n\t\treturn\n\t}\n\tctx, cancel := context.WithCancel(context.Background())\n\tvar cDialer C.Dialer = dialer.NewDialer()\n\tif dialerProxy != \"\" {\n\t\tcDialer = proxydialer.NewByName(dialerProxy)\n\t}\n\tglobalSrv = &Service{\n\t\tserver:         M.ParseSocksaddr(server),\n\t\tdialer:         proxydialer.NewSingDialer(cDialer),\n\t\tticker:         time.NewTicker(interval * time.Minute),\n\t\tctx:            ctx,\n\t\tcancel:         cancel,\n\t\tsyncSystemTime: syncSystemTime,\n\t}\n\tglobalSrv.Start()\n}\n\nfunc (srv *Service) Start() {\n\tlog.Infoln(\"NTP service start, sync system time is %t\", srv.syncSystemTime)\n\tgo srv.loopUpdate()\n}\n\nfunc (srv *Service) Stop() {\n\tlog.Infoln(\"NTP service stop\")\n\tsrv.cancel()\n}\n\nfunc (srv *Service) update() error {\n\tvar response *ntp.Response\n\tvar err error\n\tfor i := 0; i < 3; i++ {\n\t\tresponse, err = ntp.Exchange(srv.ctx, srv.dialer, srv.server)\n\t\tif err != nil {\n\t\t\tif srv.ctx.Err() != nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\toffset := response.ClockOffset\n\t\tif offset > time.Duration(0) {\n\t\t\tlog.Infoln(\"System clock is ahead of NTP time by %s\", offset)\n\t\t} else if offset < time.Duration(0) {\n\t\t\tlog.Infoln(\"System clock is behind NTP time by %s\", -offset)\n\t\t}\n\t\tmihomoNtp.SetOffset(offset)\n\t\tif srv.syncSystemTime {\n\t\t\ttimeNow := response.Time\n\t\t\tsyncErr := setSystemTime(timeNow)\n\t\t\tif syncErr == nil {\n\t\t\t\tlog.Infoln(\"Sync system time success: %s\", timeNow.Local().Format(ntp.TimeLayout))\n\t\t\t} else {\n\t\t\t\tlog.Errorln(\"Write time to system: %s\", syncErr)\n\t\t\t\tsrv.syncSystemTime = false\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\treturn err\n}\n\nfunc (srv *Service) loopUpdate() {\n\tdefer mihomoNtp.SetOffset(0)\n\tdefer srv.ticker.Stop()\n\tfor {\n\t\terr := srv.update()\n\t\tif err != nil {\n\t\t\tlog.Warnln(\"Sync time failed: %s\", err)\n\t\t}\n\t\tselect {\n\t\tcase <-srv.ctx.Done():\n\t\t\treturn\n\t\tcase <-srv.ticker.C:\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/ntp/ntp/time_stub.go",
    "content": "//go:build !(windows || linux || darwin)\n\npackage ntp\n\nimport (\n\t\"os\"\n\t\"time\"\n)\n\nfunc setSystemTime(nowTime time.Time) error {\n\treturn os.ErrInvalid\n}\n"
  },
  {
    "path": "core/Clash.Meta/ntp/ntp/time_unix.go",
    "content": "//go:build linux || darwin\n\npackage ntp\n\nimport (\n\t\"time\"\n\n\t\"golang.org/x/sys/unix\"\n)\n\nfunc setSystemTime(nowTime time.Time) error {\n\ttimeVal := unix.NsecToTimeval(nowTime.UnixNano())\n\treturn unix.Settimeofday(&timeVal)\n}\n"
  },
  {
    "path": "core/Clash.Meta/ntp/ntp/time_windows.go",
    "content": "package ntp\n\nimport (\n\t\"time\"\n\t\"unsafe\"\n\n\t\"golang.org/x/sys/windows\"\n)\n\nfunc setSystemTime(nowTime time.Time) error {\n\tvar systemTime windows.Systemtime\n\tsystemTime.Year = uint16(nowTime.Year())\n\tsystemTime.Month = uint16(nowTime.Month())\n\tsystemTime.Day = uint16(nowTime.Day())\n\tsystemTime.Hour = uint16(nowTime.Hour())\n\tsystemTime.Minute = uint16(nowTime.Minute())\n\tsystemTime.Second = uint16(nowTime.Second())\n\tsystemTime.Milliseconds = uint16(nowTime.UnixMilli() - nowTime.Unix()*1000)\n\n\tdllKernel32 := windows.NewLazySystemDLL(\"kernel32.dll\")\n\tproc := dllKernel32.NewProc(\"SetSystemTime\")\n\n\t_, _, err := proc.Call(\n\t\tuintptr(unsafe.Pointer(&systemTime)),\n\t)\n\n\tif err != nil && err.Error() != \"The operation completed successfully.\" {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/ntp/time.go",
    "content": "// Package ntp provide time.Now\n//\n// DON'T import other package in mihomo to keep minimal internal dependencies\npackage ntp\n\nimport (\n\t\"time\"\n\n\t\"sync/atomic\"\n)\n\nvar _offset atomic.Int64 // [time.Duration]\n\nfunc SetOffset(offset time.Duration) {\n\t_offset.Store(int64(offset))\n}\n\nfunc GetOffset() time.Duration {\n\treturn time.Duration(_offset.Load())\n}\n\nfunc Now() time.Time {\n\tnow := time.Now()\n\tif offset := GetOffset(); offset != 0 {\n\t\tnow = now.Add(offset)\n\t}\n\treturn now\n}\n"
  },
  {
    "path": "core/Clash.Meta/rules/common/base.go",
    "content": "package common\n\nimport (\n\t\"errors\"\n\t\"strings\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\n\t\"golang.org/x/exp/slices\"\n)\n\nvar (\n\terrPayload = errors.New(\"payloadRule error\")\n)\n\n// params\nvar (\n\tNoResolve = \"no-resolve\"\n\tSrc       = \"src\"\n)\n\ntype Base struct {\n}\n\nfunc (b *Base) ProviderNames() []string { return nil }\n\nfunc ParseParams(params []string) (isSrc bool, noResolve bool) {\n\tisSrc = slices.Contains(params, Src)\n\tif isSrc {\n\t\tnoResolve = true\n\t} else {\n\t\tnoResolve = slices.Contains(params, NoResolve)\n\t}\n\treturn\n}\n\nfunc trimArr(arr []string) (r []string) {\n\tfor _, e := range arr {\n\t\tr = append(r, strings.Trim(e, \" \"))\n\t}\n\treturn\n}\n\n// ParseRulePayload parse rule format like:\n// `tp,payload,target(,params...)` or `tp,payload(,params...)`\n// needTarget control the format contains `target` in string\nfunc ParseRulePayload(ruleRaw string, needTarget bool) (tp, payload, target string, params []string) {\n\titem := trimArr(strings.Split(ruleRaw, \",\"))\n\ttp = strings.ToUpper(item[0])\n\tif len(item) > 1 {\n\t\tswitch tp {\n\t\tcase \"MATCH\":\n\t\t\t// MATCH doesn't contain payload and params\n\t\t\ttarget = item[1]\n\t\tcase \"NOT\", \"OR\", \"AND\", \"SUB-RULE\", \"DOMAIN-REGEX\", \"PROCESS-NAME-REGEX\", \"PROCESS-PATH-REGEX\":\n\t\t\t// some type of rules that has comma in payload and don't need params\n\t\t\tif needTarget {\n\t\t\t\tl := len(item)\n\t\t\t\ttarget = item[l-1] // don't have params so target must at the end of slices\n\t\t\t\titem = item[:l-1]  // remove the target from slices\n\t\t\t}\n\t\t\tpayload = strings.Join(item[1:], \",\")\n\t\tdefault:\n\t\t\tpayload = item[1]\n\t\t\tif len(item) > 2 {\n\t\t\t\tif needTarget {\n\t\t\t\t\ttarget = item[2]\n\t\t\t\t\tif len(item) > 3 {\n\t\t\t\t\t\tparams = item[3:]\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\tparams = item[2:]\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\treturn\n}\n\ntype ParseRuleFunc func(tp, payload, target string, params []string, subRules map[string][]C.Rule) (C.Rule, error)\n"
  },
  {
    "path": "core/Clash.Meta/rules/common/domain.go",
    "content": "package common\n\nimport (\n\t\"strings\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\ntype Domain struct {\n\tBase\n\tdomain  string\n\tadapter string\n}\n\nfunc (d *Domain) RuleType() C.RuleType {\n\treturn C.Domain\n}\n\nfunc (d *Domain) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {\n\treturn metadata.RuleHost() == d.domain, d.adapter\n}\n\nfunc (d *Domain) Adapter() string {\n\treturn d.adapter\n}\n\nfunc (d *Domain) Payload() string {\n\treturn d.domain\n}\n\nfunc NewDomain(domain string, adapter string) *Domain {\n\treturn &Domain{\n\t\tBase:    Base{},\n\t\tdomain:  strings.ToLower(domain),\n\t\tadapter: adapter,\n\t}\n}\n\nvar _ C.Rule = (*Domain)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/rules/common/domain_keyword.go",
    "content": "package common\n\nimport (\n\t\"strings\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\ntype DomainKeyword struct {\n\tBase\n\tkeyword string\n\tadapter string\n}\n\nfunc (dk *DomainKeyword) RuleType() C.RuleType {\n\treturn C.DomainKeyword\n}\n\nfunc (dk *DomainKeyword) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {\n\tdomain := metadata.RuleHost()\n\treturn strings.Contains(domain, dk.keyword), dk.adapter\n}\n\nfunc (dk *DomainKeyword) Adapter() string {\n\treturn dk.adapter\n}\n\nfunc (dk *DomainKeyword) Payload() string {\n\treturn dk.keyword\n}\n\nfunc NewDomainKeyword(keyword string, adapter string) *DomainKeyword {\n\treturn &DomainKeyword{\n\t\tBase:    Base{},\n\t\tkeyword: strings.ToLower(keyword),\n\t\tadapter: adapter,\n\t}\n}\n\nvar _ C.Rule = (*DomainKeyword)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/rules/common/domain_regex.go",
    "content": "package common\n\nimport (\n\tC \"github.com/metacubex/mihomo/constant\"\n\n\t\"github.com/dlclark/regexp2\"\n)\n\ntype DomainRegex struct {\n\tBase\n\tregex   *regexp2.Regexp\n\tadapter string\n}\n\nfunc (dr *DomainRegex) RuleType() C.RuleType {\n\treturn C.DomainRegex\n}\n\nfunc (dr *DomainRegex) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {\n\tdomain := metadata.RuleHost()\n\tmatch, _ := dr.regex.MatchString(domain)\n\treturn match, dr.adapter\n}\n\nfunc (dr *DomainRegex) Adapter() string {\n\treturn dr.adapter\n}\n\nfunc (dr *DomainRegex) Payload() string {\n\treturn dr.regex.String()\n}\n\nfunc NewDomainRegex(regex string, adapter string) (*DomainRegex, error) {\n\tr, err := regexp2.Compile(regex, regexp2.IgnoreCase)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &DomainRegex{\n\t\tBase:    Base{},\n\t\tregex:   r,\n\t\tadapter: adapter,\n\t}, nil\n}\n\nvar _ C.Rule = (*DomainRegex)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/rules/common/domain_suffix.go",
    "content": "package common\n\nimport (\n\t\"strings\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\ntype DomainSuffix struct {\n\tBase\n\tsuffix  string\n\tadapter string\n}\n\nfunc (ds *DomainSuffix) RuleType() C.RuleType {\n\treturn C.DomainSuffix\n}\n\nfunc (ds *DomainSuffix) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {\n\tdomain := metadata.RuleHost()\n\treturn strings.HasSuffix(domain, \".\"+ds.suffix) || domain == ds.suffix, ds.adapter\n}\n\nfunc (ds *DomainSuffix) Adapter() string {\n\treturn ds.adapter\n}\n\nfunc (ds *DomainSuffix) Payload() string {\n\treturn ds.suffix\n}\n\nfunc NewDomainSuffix(suffix string, adapter string) *DomainSuffix {\n\treturn &DomainSuffix{\n\t\tBase:    Base{},\n\t\tsuffix:  strings.ToLower(suffix),\n\t\tadapter: adapter,\n\t}\n}\n\nvar _ C.Rule = (*DomainSuffix)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/rules/common/domain_wildcard.go",
    "content": "package common\n\nimport (\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/component/wildcard\"\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\ntype DomainWildcard struct {\n\tBase\n\tpattern string\n\tadapter string\n}\n\nfunc (dw *DomainWildcard) RuleType() C.RuleType {\n\treturn C.DomainWildcard\n}\n\nfunc (dw *DomainWildcard) Match(metadata *C.Metadata, _ C.RuleMatchHelper) (bool, string) {\n\treturn wildcard.Match(dw.pattern, metadata.Host), dw.adapter\n}\n\nfunc (dw *DomainWildcard) Adapter() string {\n\treturn dw.adapter\n}\n\nfunc (dw *DomainWildcard) Payload() string {\n\treturn dw.pattern\n}\n\nvar _ C.Rule = (*DomainWildcard)(nil)\n\nfunc NewDomainWildcard(pattern string, adapter string) (*DomainWildcard, error) {\n\tpattern = strings.ToLower(pattern)\n\treturn &DomainWildcard{\n\t\tBase:    Base{},\n\t\tpattern: pattern,\n\t\tadapter: adapter,\n\t}, nil\n}\n\nvar _ C.Rule = (*DomainWildcard)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/rules/common/dscp.go",
    "content": "package common\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/metacubex/mihomo/common/utils\"\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\ntype DSCP struct {\n\tBase\n\tranges  utils.IntRanges[uint8]\n\tpayload string\n\tadapter string\n}\n\nfunc (d *DSCP) RuleType() C.RuleType {\n\treturn C.DSCP\n}\n\nfunc (d *DSCP) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {\n\treturn d.ranges.Check(metadata.DSCP), d.adapter\n}\n\nfunc (d *DSCP) Adapter() string {\n\treturn d.adapter\n}\n\nfunc (d *DSCP) Payload() string {\n\treturn d.payload\n}\n\nfunc NewDSCP(dscp string, adapter string) (*DSCP, error) {\n\tranges, err := utils.NewUnsignedRanges[uint8](dscp)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"parse DSCP rule fail: %w\", err)\n\t}\n\tfor _, r := range ranges {\n\t\tif r.End() > 63 {\n\t\t\treturn nil, fmt.Errorf(\"DSCP couldn't be negative or exceed 63\")\n\t\t}\n\t}\n\treturn &DSCP{\n\t\tBase:    Base{},\n\t\tpayload: dscp,\n\t\tranges:  ranges,\n\t\tadapter: adapter,\n\t}, nil\n}\n\nvar _ C.Rule = (*DSCP)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/rules/common/final.go",
    "content": "package common\n\nimport (\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\ntype Match struct {\n\tBase\n\tadapter string\n}\n\nfunc (f *Match) RuleType() C.RuleType {\n\treturn C.MATCH\n}\n\nfunc (f *Match) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {\n\treturn true, f.adapter\n}\n\nfunc (f *Match) Adapter() string {\n\treturn f.adapter\n}\n\nfunc (f *Match) Payload() string {\n\treturn \"\"\n}\n\nfunc NewMatch(adapter string) *Match {\n\treturn &Match{\n\t\tBase:    Base{},\n\t\tadapter: adapter,\n\t}\n}\n\nvar _ C.Rule = (*Match)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/rules/common/geoip.go",
    "content": "package common\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net/netip\"\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/component/geodata\"\n\t\"github.com/metacubex/mihomo/component/geodata/router\"\n\t\"github.com/metacubex/mihomo/component/mmdb\"\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"golang.org/x/exp/slices\"\n)\n\ntype GEOIP struct {\n\tBase\n\tcountry     string\n\tadapter     string\n\tnoResolveIP bool\n\tisSourceIP  bool\n}\n\nvar _ C.Rule = (*GEOIP)(nil)\n\nfunc (g *GEOIP) RuleType() C.RuleType {\n\tif g.isSourceIP {\n\t\treturn C.SrcGEOIP\n\t}\n\treturn C.GEOIP\n}\n\nfunc (g *GEOIP) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {\n\tif !g.noResolveIP && !g.isSourceIP && helper.ResolveIP != nil {\n\t\thelper.ResolveIP()\n\t}\n\n\tip := metadata.DstIP\n\tif g.isSourceIP {\n\t\tip = metadata.SrcIP\n\t}\n\tif !ip.IsValid() {\n\t\treturn false, \"\"\n\t}\n\n\tif g.country == \"lan\" {\n\t\treturn g.isLan(ip), g.adapter\n\t}\n\n\tif geodata.GeodataMode() {\n\t\tif g.isSourceIP {\n\t\t\tif slices.Contains(metadata.SrcGeoIP, g.country) {\n\t\t\t\treturn true, g.adapter\n\t\t\t}\n\t\t} else {\n\t\t\tif slices.Contains(metadata.DstGeoIP, g.country) {\n\t\t\t\treturn true, g.adapter\n\t\t\t}\n\t\t}\n\t\tmatcher, err := g.getIPMatcher()\n\t\tif err != nil {\n\t\t\treturn false, \"\"\n\t\t}\n\t\tmatch := matcher.Match(ip)\n\t\tif match {\n\t\t\tif g.isSourceIP {\n\t\t\t\tmetadata.SrcGeoIP = append(metadata.SrcGeoIP, g.country)\n\t\t\t} else {\n\t\t\t\tmetadata.DstGeoIP = append(metadata.DstGeoIP, g.country)\n\t\t\t}\n\t\t}\n\t\treturn match, g.adapter\n\t}\n\n\tif g.isSourceIP {\n\t\tif metadata.SrcGeoIP != nil {\n\t\t\treturn slices.Contains(metadata.SrcGeoIP, g.country), g.adapter\n\t\t}\n\t} else {\n\t\tif metadata.DstGeoIP != nil {\n\t\t\treturn slices.Contains(metadata.DstGeoIP, g.country), g.adapter\n\t\t}\n\t}\n\tcodes := mmdb.IPInstance().LookupCode(ip.AsSlice())\n\tif g.isSourceIP {\n\t\tmetadata.SrcGeoIP = codes\n\t} else {\n\t\tmetadata.DstGeoIP = codes\n\t}\n\tif slices.Contains(codes, g.country) {\n\t\treturn true, g.adapter\n\t}\n\treturn false, \"\"\n}\n\n// MatchIp implements C.IpMatcher\nfunc (g *GEOIP) MatchIp(ip netip.Addr) bool {\n\tif !ip.IsValid() {\n\t\treturn false\n\t}\n\n\tif g.country == \"lan\" {\n\t\treturn g.isLan(ip)\n\t}\n\n\tif geodata.GeodataMode() {\n\t\tmatcher, err := g.getIPMatcher()\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\treturn matcher.Match(ip)\n\t}\n\n\tcodes := mmdb.IPInstance().LookupCode(ip.AsSlice())\n\treturn slices.Contains(codes, g.country)\n}\n\n// MatchIp implements C.IpMatcher\nfunc (g dnsFallbackFilter) MatchIp(ip netip.Addr) bool {\n\tif !ip.IsValid() {\n\t\treturn false\n\t}\n\n\tif g.isLan(ip) { // compatible with original behavior\n\t\treturn false\n\t}\n\n\tif g.country == \"lan\" {\n\t\treturn !g.isLan(ip)\n\t}\n\n\tif geodata.GeodataMode() {\n\t\tmatcher, err := g.getIPMatcher()\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\treturn !matcher.Match(ip)\n\t}\n\n\tcodes := mmdb.IPInstance().LookupCode(ip.AsSlice())\n\treturn !slices.Contains(codes, g.country)\n}\n\ntype dnsFallbackFilter struct {\n\t*GEOIP\n}\n\nfunc (g *GEOIP) DnsFallbackFilter() C.IpMatcher { // for dns.fallback-filter.geoip\n\treturn dnsFallbackFilter{GEOIP: g}\n}\n\nfunc (g *GEOIP) isLan(ip netip.Addr) bool {\n\treturn ip.IsPrivate() ||\n\t\tip.IsUnspecified() ||\n\t\tip.IsLoopback() ||\n\t\tip.IsMulticast() ||\n\t\tip.IsLinkLocalUnicast() ||\n\t\tresolver.IsFakeBroadcastIP(ip)\n}\n\nfunc (g *GEOIP) Adapter() string {\n\treturn g.adapter\n}\n\nfunc (g *GEOIP) Payload() string {\n\treturn g.country\n}\n\nfunc (g *GEOIP) GetCountry() string {\n\treturn g.country\n}\n\nfunc (g *GEOIP) GetIPMatcher() (router.IPMatcher, error) {\n\tif geodata.GeodataMode() {\n\t\treturn g.getIPMatcher()\n\t}\n\treturn nil, errors.New(\"not geodata mode\")\n}\n\nfunc (g *GEOIP) getIPMatcher() (router.IPMatcher, error) {\n\tgeoIPMatcher, err := geodata.LoadGeoIPMatcher(g.country)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"[GeoIP] %w\", err)\n\t}\n\treturn geoIPMatcher, nil\n\n}\n\nfunc (g *GEOIP) GetRecodeSize() int {\n\t// skip pseudorule lan\n\tif g.country == \"lan\" {\n\t\treturn 0\n\t}\n\n\tif matcher, err := g.GetIPMatcher(); err == nil {\n\t\treturn matcher.Count()\n\t}\n\treturn 0\n}\n\nfunc NewGEOIP(country string, adapter string, isSrc, noResolveIP bool) (*GEOIP, error) {\n\tcountry = strings.ToLower(country)\n\n\tgeoip := &GEOIP{\n\t\tBase:        Base{},\n\t\tcountry:     country,\n\t\tadapter:     adapter,\n\t\tnoResolveIP: noResolveIP,\n\t\tisSourceIP:  isSrc,\n\t}\n\n\tif country == \"lan\" {\n\t\treturn geoip, nil\n\t}\n\n\tif err := geodata.InitGeoIP(); err != nil {\n\t\tlog.Errorln(\"can't initial GeoIP: %s\", err)\n\t\treturn nil, err\n\t}\n\n\tif geodata.GeodataMode() {\n\t\tgeoIPMatcher, err := geoip.getIPMatcher() // test load\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlog.Infoln(\"Finished initial GeoIP rule %s => %s, records: %d\", country, adapter, geoIPMatcher.Count())\n\t}\n\n\treturn geoip, nil\n}\n\nvar _ C.Rule = (*GEOIP)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/rules/common/geosite.go",
    "content": "package common\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/metacubex/mihomo/component/geodata\"\n\t_ \"github.com/metacubex/mihomo/component/geodata/memconservative\"\n\t\"github.com/metacubex/mihomo/component/geodata/router\"\n\t_ \"github.com/metacubex/mihomo/component/geodata/standard\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\ntype GEOSITE struct {\n\tBase\n\tcountry    string\n\tadapter    string\n\trecodeSize int\n}\n\nfunc (gs *GEOSITE) RuleType() C.RuleType {\n\treturn C.GEOSITE\n}\n\nfunc (gs *GEOSITE) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {\n\treturn gs.MatchDomain(metadata.RuleHost()), gs.adapter\n}\n\n// MatchDomain implements C.DomainMatcher\nfunc (gs *GEOSITE) MatchDomain(domain string) bool {\n\tif len(domain) == 0 {\n\t\treturn false\n\t}\n\tmatcher, err := gs.GetDomainMatcher()\n\tif err != nil {\n\t\treturn false\n\t}\n\treturn matcher.ApplyDomain(domain)\n}\n\nfunc (gs *GEOSITE) Adapter() string {\n\treturn gs.adapter\n}\n\nfunc (gs *GEOSITE) Payload() string {\n\treturn gs.country\n}\n\nfunc (gs *GEOSITE) GetDomainMatcher() (router.DomainMatcher, error) {\n\tmatcher, err := geodata.LoadGeoSiteMatcher(gs.country)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"load GeoSite data error, %w\", err)\n\t}\n\treturn matcher, nil\n}\n\nfunc (gs *GEOSITE) GetRecodeSize() int {\n\tif matcher, err := gs.GetDomainMatcher(); err == nil {\n\t\treturn matcher.Count()\n\t}\n\treturn 0\n}\n\nfunc NewGEOSITE(country string, adapter string) (*GEOSITE, error) {\n\tif err := geodata.InitGeoSite(); err != nil {\n\t\tlog.Errorln(\"can't initial GeoSite: %s\", err)\n\t\treturn nil, err\n\t}\n\n\tgeoSite := &GEOSITE{\n\t\tBase:    Base{},\n\t\tcountry: country,\n\t\tadapter: adapter,\n\t}\n\n\tmatcher, err := geoSite.GetDomainMatcher() // test load\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tlog.Infoln(\"Finished initial GeoSite rule %s => %s, records: %d\", country, adapter, matcher.Count())\n\n\treturn geoSite, nil\n}\n\nvar _ C.Rule = (*GEOSITE)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/rules/common/in_name.go",
    "content": "package common\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\ntype InName struct {\n\tBase\n\tnames   []string\n\tadapter string\n\tpayload string\n}\n\nfunc (u *InName) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {\n\tfor _, name := range u.names {\n\t\tif metadata.InName == name {\n\t\t\treturn true, u.adapter\n\t\t}\n\t}\n\treturn false, \"\"\n}\n\nfunc (u *InName) RuleType() C.RuleType {\n\treturn C.InName\n}\n\nfunc (u *InName) Adapter() string {\n\treturn u.adapter\n}\n\nfunc (u *InName) Payload() string {\n\treturn u.payload\n}\n\nfunc NewInName(iNames, adapter string) (*InName, error) {\n\tnames := strings.Split(iNames, \"/\")\n\tfor i, name := range names {\n\t\tname = strings.TrimSpace(name)\n\t\tif len(name) == 0 {\n\t\t\treturn nil, fmt.Errorf(\"in name couldn't be empty\")\n\t\t}\n\t\tnames[i] = name\n\t}\n\n\treturn &InName{\n\t\tBase:    Base{},\n\t\tnames:   names,\n\t\tadapter: adapter,\n\t\tpayload: iNames,\n\t}, nil\n}\n\nvar _ C.Rule = (*InName)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/rules/common/in_type.go",
    "content": "package common\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\ntype InType struct {\n\tBase\n\ttypes   []C.Type\n\tadapter string\n\tpayload string\n}\n\nfunc (u *InType) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {\n\tfor _, tp := range u.types {\n\t\tif metadata.Type == tp {\n\t\t\treturn true, u.adapter\n\t\t}\n\t}\n\treturn false, \"\"\n}\n\nfunc (u *InType) RuleType() C.RuleType {\n\treturn C.InType\n}\n\nfunc (u *InType) Adapter() string {\n\treturn u.adapter\n}\n\nfunc (u *InType) Payload() string {\n\treturn u.payload\n}\n\nfunc NewInType(iTypes, adapter string) (*InType, error) {\n\ttypes := strings.Split(iTypes, \"/\")\n\tfor i, tp := range types {\n\t\ttp = strings.TrimSpace(tp)\n\t\tif len(tp) == 0 {\n\t\t\treturn nil, fmt.Errorf(\"in type couldn't be empty\")\n\t\t}\n\t\ttypes[i] = tp\n\t}\n\n\ttps, err := parseInTypes(types)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &InType{\n\t\tBase:    Base{},\n\t\ttypes:   tps,\n\t\tadapter: adapter,\n\t\tpayload: strings.ToUpper(iTypes),\n\t}, nil\n}\n\nfunc parseInTypes(tps []string) (res []C.Type, err error) {\n\tfor _, tp := range tps {\n\t\tutp := strings.ToUpper(tp)\n\t\tvar r *C.Type\n\t\tif utp == \"SOCKS\" {\n\t\t\tr, _ = C.ParseType(\"SOCKS4\")\n\t\t\tres = append(res, *r)\n\t\t\tr, _ = C.ParseType(\"SOCKS5\")\n\t\t\tres = append(res, *r)\n\t\t} else {\n\t\t\tr, err = C.ParseType(utp)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tres = append(res, *r)\n\t\t}\n\t}\n\treturn\n}\n\nvar _ C.Rule = (*InType)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/rules/common/in_user.go",
    "content": "package common\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\ntype InUser struct {\n\tBase\n\tusers   []string\n\tadapter string\n\tpayload string\n}\n\nfunc (u *InUser) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {\n\tfor _, user := range u.users {\n\t\tif metadata.InUser == user {\n\t\t\treturn true, u.adapter\n\t\t}\n\t}\n\treturn false, \"\"\n}\n\nfunc (u *InUser) RuleType() C.RuleType {\n\treturn C.InUser\n}\n\nfunc (u *InUser) Adapter() string {\n\treturn u.adapter\n}\n\nfunc (u *InUser) Payload() string {\n\treturn u.payload\n}\n\nfunc NewInUser(iUsers, adapter string) (*InUser, error) {\n\tusers := strings.Split(iUsers, \"/\")\n\tfor i, user := range users {\n\t\tuser = strings.TrimSpace(user)\n\t\tif len(user) == 0 {\n\t\t\treturn nil, fmt.Errorf(\"in user couldn't be empty\")\n\t\t}\n\t\tusers[i] = user\n\t}\n\n\treturn &InUser{\n\t\tBase:    Base{},\n\t\tusers:   users,\n\t\tadapter: adapter,\n\t\tpayload: iUsers,\n\t}, nil\n}\n\nvar _ C.Rule = (*InUser)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/rules/common/ipasn.go",
    "content": "package common\n\nimport (\n\t\"github.com/metacubex/mihomo/component/geodata\"\n\t\"github.com/metacubex/mihomo/component/mmdb\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\ntype ASN struct {\n\tBase\n\tasn         string\n\tadapter     string\n\tnoResolveIP bool\n\tisSourceIP  bool\n}\n\nfunc (a *ASN) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {\n\tif !a.noResolveIP && !a.isSourceIP && helper.ResolveIP != nil {\n\t\thelper.ResolveIP()\n\t}\n\n\tip := metadata.DstIP\n\tif a.isSourceIP {\n\t\tip = metadata.SrcIP\n\t}\n\tif !ip.IsValid() {\n\t\treturn false, \"\"\n\t}\n\n\tasn, aso := mmdb.ASNInstance().LookupASN(ip.AsSlice())\n\tif a.isSourceIP {\n\t\tmetadata.SrcIPASN = asn + \" \" + aso\n\t} else {\n\t\tmetadata.DstIPASN = asn + \" \" + aso\n\t}\n\n\treturn a.asn == asn, a.adapter\n}\n\nfunc (a *ASN) RuleType() C.RuleType {\n\tif a.isSourceIP {\n\t\treturn C.SrcIPASN\n\t}\n\treturn C.IPASN\n}\n\nfunc (a *ASN) Adapter() string {\n\treturn a.adapter\n}\n\nfunc (a *ASN) Payload() string {\n\treturn a.asn\n}\n\nfunc (a *ASN) GetASN() string {\n\treturn a.asn\n}\n\nfunc NewIPASN(asn string, adapter string, isSrc, noResolveIP bool) (*ASN, error) {\n\tif err := geodata.InitASN(); err != nil {\n\t\tlog.Errorln(\"can't initial ASN: %s\", err)\n\t\treturn nil, err\n\t}\n\n\treturn &ASN{\n\t\tBase:        Base{},\n\t\tasn:         asn,\n\t\tadapter:     adapter,\n\t\tnoResolveIP: noResolveIP,\n\t\tisSourceIP:  isSrc,\n\t}, nil\n}\n\nvar _ C.Rule = (*ASN)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/rules/common/ipcidr.go",
    "content": "package common\n\nimport (\n\t\"net/netip\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\ntype IPCIDROption func(*IPCIDR)\n\nfunc WithIPCIDRSourceIP(b bool) IPCIDROption {\n\treturn func(i *IPCIDR) {\n\t\ti.isSourceIP = b\n\t}\n}\n\nfunc WithIPCIDRNoResolve(noResolve bool) IPCIDROption {\n\treturn func(i *IPCIDR) {\n\t\ti.noResolveIP = noResolve\n\t}\n}\n\ntype IPCIDR struct {\n\tBase\n\tipnet       netip.Prefix\n\tadapter     string\n\tisSourceIP  bool\n\tnoResolveIP bool\n}\n\nfunc (i *IPCIDR) RuleType() C.RuleType {\n\tif i.isSourceIP {\n\t\treturn C.SrcIPCIDR\n\t}\n\treturn C.IPCIDR\n}\n\nfunc (i *IPCIDR) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {\n\tif !i.noResolveIP && !i.isSourceIP && helper.ResolveIP != nil {\n\t\thelper.ResolveIP()\n\t}\n\n\tip := metadata.DstIP\n\tif i.isSourceIP {\n\t\tip = metadata.SrcIP\n\t}\n\treturn ip.IsValid() && i.ipnet.Contains(ip.WithZone(\"\")), i.adapter\n}\n\nfunc (i *IPCIDR) Adapter() string {\n\treturn i.adapter\n}\n\nfunc (i *IPCIDR) Payload() string {\n\treturn i.ipnet.String()\n}\n\nfunc NewIPCIDR(s string, adapter string, opts ...IPCIDROption) (*IPCIDR, error) {\n\tipnet, err := netip.ParsePrefix(s)\n\tif err != nil {\n\t\treturn nil, errPayload\n\t}\n\n\tipcidr := &IPCIDR{\n\t\tBase:    Base{},\n\t\tipnet:   ipnet,\n\t\tadapter: adapter,\n\t}\n\n\tfor _, o := range opts {\n\t\to(ipcidr)\n\t}\n\n\treturn ipcidr, nil\n}\n\nvar _ C.Rule = (*IPCIDR)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/rules/common/ipsuffix.go",
    "content": "package common\n\nimport (\n\t\"net/netip\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\ntype IPSuffix struct {\n\tBase\n\tipBytes     []byte\n\tbits        int\n\tpayload     string\n\tadapter     string\n\tisSourceIP  bool\n\tnoResolveIP bool\n}\n\nfunc (is *IPSuffix) RuleType() C.RuleType {\n\tif is.isSourceIP {\n\t\treturn C.SrcIPSuffix\n\t}\n\treturn C.IPSuffix\n}\n\nfunc (is *IPSuffix) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {\n\tif !is.noResolveIP && !is.isSourceIP && helper.ResolveIP != nil {\n\t\thelper.ResolveIP()\n\t}\n\n\tip := metadata.DstIP\n\tif is.isSourceIP {\n\t\tip = metadata.SrcIP\n\t}\n\n\tmIPBytes := ip.AsSlice()\n\tif len(is.ipBytes) != len(mIPBytes) {\n\t\treturn false, \"\"\n\t}\n\n\tsize := len(mIPBytes)\n\tbits := is.bits\n\n\tfor i := bits / 8; i > 0; i-- {\n\t\tif is.ipBytes[size-i] != mIPBytes[size-i] {\n\t\t\treturn false, \"\"\n\t\t}\n\t}\n\n\tif (is.ipBytes[size-bits/8-1] << (8 - bits%8)) != (mIPBytes[size-bits/8-1] << (8 - bits%8)) {\n\t\treturn false, \"\"\n\t}\n\n\treturn true, is.adapter\n}\n\nfunc (is *IPSuffix) Adapter() string {\n\treturn is.adapter\n}\n\nfunc (is *IPSuffix) Payload() string {\n\treturn is.payload\n}\n\nfunc NewIPSuffix(payload, adapter string, isSrc, noResolveIP bool) (*IPSuffix, error) {\n\tipnet, err := netip.ParsePrefix(payload)\n\tif err != nil {\n\t\treturn nil, errPayload\n\t}\n\n\treturn &IPSuffix{\n\t\tBase:        Base{},\n\t\tpayload:     payload,\n\t\tipBytes:     ipnet.Addr().AsSlice(),\n\t\tbits:        ipnet.Bits(),\n\t\tadapter:     adapter,\n\t\tisSourceIP:  isSrc,\n\t\tnoResolveIP: noResolveIP,\n\t}, nil\n}\n\nvar _ C.Rule = (*IPSuffix)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/rules/common/network_type.go",
    "content": "package common\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\ntype NetworkType struct {\n\tBase\n\tnetwork C.NetWork\n\tadapter string\n}\n\nfunc NewNetworkType(network, adapter string) (*NetworkType, error) {\n\tntType := NetworkType{\n\t\tBase: Base{},\n\t}\n\n\tntType.adapter = adapter\n\tswitch strings.ToUpper(network) {\n\tcase \"TCP\":\n\t\tntType.network = C.TCP\n\tcase \"UDP\":\n\t\tntType.network = C.UDP\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unsupported network type, only TCP/UDP\")\n\t}\n\n\treturn &ntType, nil\n}\n\nfunc (n *NetworkType) RuleType() C.RuleType {\n\treturn C.Network\n}\n\nfunc (n *NetworkType) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {\n\treturn n.network == metadata.NetWork, n.adapter\n}\n\nfunc (n *NetworkType) Adapter() string {\n\treturn n.adapter\n}\n\nfunc (n *NetworkType) Payload() string {\n\treturn n.network.String()\n}\n\nvar _ C.Rule = (*NetworkType)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/rules/common/port.go",
    "content": "package common\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/metacubex/mihomo/common/utils\"\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\ntype Port struct {\n\tBase\n\tadapter    string\n\tport       string\n\truleType   C.RuleType\n\tportRanges utils.IntRanges[uint16]\n}\n\nfunc (p *Port) RuleType() C.RuleType {\n\treturn p.ruleType\n}\n\nfunc (p *Port) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {\n\ttargetPort := metadata.DstPort\n\tswitch p.ruleType {\n\tcase C.InPort:\n\t\ttargetPort = metadata.InPort\n\tcase C.SrcPort:\n\t\ttargetPort = metadata.SrcPort\n\t}\n\treturn p.portRanges.Check(targetPort), p.adapter\n}\n\nfunc (p *Port) Adapter() string {\n\treturn p.adapter\n}\n\nfunc (p *Port) Payload() string {\n\treturn p.port\n}\n\nfunc NewPort(port string, adapter string, ruleType C.RuleType) (*Port, error) {\n\tportRanges, err := utils.NewUnsignedRanges[uint16](port)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%w, %w\", errPayload, err)\n\t}\n\n\tif len(portRanges) == 0 {\n\t\treturn nil, errPayload\n\t}\n\n\treturn &Port{\n\t\tBase:       Base{},\n\t\tadapter:    adapter,\n\t\tport:       port,\n\t\truleType:   ruleType,\n\t\tportRanges: portRanges,\n\t}, nil\n}\n\nvar _ C.Rule = (*Port)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/rules/common/process.go",
    "content": "package common\n\nimport (\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/component/wildcard\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\n\t\"github.com/dlclark/regexp2\"\n)\n\ntype Process struct {\n\tBase\n\tpattern  string\n\tadapter  string\n\truleType C.RuleType\n\tregexp   *regexp2.Regexp\n}\n\nfunc (ps *Process) Payload() string {\n\treturn ps.pattern\n}\n\nfunc (ps *Process) Adapter() string {\n\treturn ps.adapter\n}\n\nfunc (ps *Process) RuleType() C.RuleType {\n\treturn ps.ruleType\n}\n\nfunc (ps *Process) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {\n\tif helper.FindProcess != nil {\n\t\thelper.FindProcess()\n\t}\n\tvar target string\n\tswitch ps.ruleType {\n\tcase C.ProcessName, C.ProcessNameRegex, C.ProcessNameWildcard:\n\t\ttarget = metadata.Process\n\tdefault:\n\t\ttarget = metadata.ProcessPath\n\t}\n\n\tswitch ps.ruleType {\n\tcase C.ProcessNameRegex, C.ProcessPathRegex:\n\t\tmatch, _ := ps.regexp.MatchString(target)\n\t\treturn match, ps.adapter\n\tcase C.ProcessNameWildcard, C.ProcessPathWildcard:\n\t\treturn wildcard.Match(strings.ToLower(ps.pattern), strings.ToLower(target)), ps.adapter\n\tdefault:\n\t\treturn strings.EqualFold(target, ps.pattern), ps.adapter\n\t}\n}\n\nfunc NewProcess(pattern string, adapter string, ruleType C.RuleType) (*Process, error) {\n\tps := &Process{\n\t\tBase:     Base{},\n\t\tpattern:  pattern,\n\t\tadapter:  adapter,\n\t\truleType: ruleType,\n\t}\n\tswitch ps.ruleType {\n\tcase C.ProcessNameRegex, C.ProcessPathRegex:\n\t\tr, err := regexp2.Compile(pattern, regexp2.IgnoreCase)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tps.regexp = r\n\tdefault:\n\t}\n\treturn ps, nil\n}\n\nvar _ C.Rule = (*Process)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/rules/common/uid.go",
    "content": "package common\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\n\t\"github.com/metacubex/mihomo/common/utils\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\ntype Uid struct {\n\tBase\n\tuids    utils.IntRanges[uint32]\n\toUid    string\n\tadapter string\n}\n\nfunc NewUid(oUid, adapter string) (*Uid, error) {\n\tif !(runtime.GOOS == \"linux\" || runtime.GOOS == \"android\") {\n\t\treturn nil, fmt.Errorf(\"uid rule not support this platform\")\n\t}\n\n\tuidRange, err := utils.NewUnsignedRanges[uint32](oUid)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%w, %w\", errPayload, err)\n\t}\n\n\tif len(uidRange) == 0 {\n\t\treturn nil, errPayload\n\t}\n\treturn &Uid{\n\t\tBase:    Base{},\n\t\tadapter: adapter,\n\t\toUid:    oUid,\n\t\tuids:    uidRange,\n\t}, nil\n}\n\nfunc (u *Uid) RuleType() C.RuleType {\n\treturn C.Uid\n}\n\nfunc (u *Uid) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {\n\tif helper.FindProcess != nil {\n\t\thelper.FindProcess()\n\t}\n\tif metadata.Uid != 0 {\n\t\tif u.uids.Check(metadata.Uid) {\n\t\t\treturn true, u.adapter\n\t\t}\n\t}\n\tlog.Warnln(\"[UID] could not get uid from %s\", metadata.String())\n\treturn false, \"\"\n}\n\nfunc (u *Uid) Adapter() string {\n\treturn u.adapter\n}\n\nfunc (u *Uid) Payload() string {\n\treturn u.oUid\n}\n\nvar _ C.Rule = (*Uid)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/rules/logic/logic.go",
    "content": "package logic\n\nimport (\n\t\"fmt\"\n\t\"sort\"\n\t\"strings\"\n\t\"sync\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/rules/common\"\n)\n\ntype Logic struct {\n\tcommon.Base\n\tpayload  string\n\tadapter  string\n\truleType C.RuleType\n\trules    []C.Rule\n\tsubRules map[string][]C.Rule\n\n\tpayloadOnce sync.Once\n}\n\nfunc NewSubRule(payload, adapter string, subRules map[string][]C.Rule, parseRule common.ParseRuleFunc) (*Logic, error) {\n\tlogic := &Logic{Base: common.Base{}, payload: payload, adapter: adapter, ruleType: C.SubRules, subRules: subRules}\n\terr := logic.parsePayload(fmt.Sprintf(\"(%s)\", payload), parseRule)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif len(logic.rules) != 1 {\n\t\treturn nil, fmt.Errorf(\"Sub-Rule rule must contain one rule\")\n\t}\n\treturn logic, nil\n}\n\nfunc NewNOT(payload string, adapter string, parseRule common.ParseRuleFunc) (*Logic, error) {\n\tlogic := &Logic{Base: common.Base{}, payload: payload, adapter: adapter, ruleType: C.NOT}\n\terr := logic.parsePayload(payload, parseRule)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif len(logic.rules) != 1 {\n\t\treturn nil, fmt.Errorf(\"not rule must contain one rule\")\n\t}\n\treturn logic, nil\n}\n\nfunc NewOR(payload string, adapter string, parseRule common.ParseRuleFunc) (*Logic, error) {\n\tlogic := &Logic{Base: common.Base{}, payload: payload, adapter: adapter, ruleType: C.OR}\n\terr := logic.parsePayload(payload, parseRule)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn logic, nil\n}\n\nfunc NewAND(payload string, adapter string, parseRule common.ParseRuleFunc) (*Logic, error) {\n\tlogic := &Logic{Base: common.Base{}, payload: payload, adapter: adapter, ruleType: C.AND}\n\terr := logic.parsePayload(payload, parseRule)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn logic, nil\n}\n\ntype Range struct {\n\tstart int\n\tend   int\n}\n\nfunc (r Range) containRange(preStart, preEnd int) bool {\n\treturn preStart < r.start && preEnd > r.end\n}\n\nfunc (logic *Logic) payloadToRule(subPayload string, parseRule common.ParseRuleFunc) (C.Rule, error) {\n\ttp, payload, target, param := common.ParseRulePayload(subPayload, false)\n\tswitch tp {\n\tcase \"MATCH\", \"SUB-RULE\":\n\t\treturn nil, fmt.Errorf(\"unsupported rule type [%s] on logic rule\", tp)\n\tcase \"\":\n\t\treturn nil, fmt.Errorf(\"[%s] format is error\", subPayload)\n\t}\n\treturn parseRule(tp, payload, target, param, nil)\n}\n\nfunc (logic *Logic) format(payload string) ([]Range, error) {\n\tstack := make([]int, 0)\n\tsubRanges := make([]Range, 0)\n\tfor i, c := range payload {\n\t\tif c == '(' {\n\t\t\tstack = append(stack, i) // push\n\t\t} else if c == ')' {\n\t\t\tif len(stack) == 0 {\n\t\t\t\treturn nil, fmt.Errorf(\"missing '('\")\n\t\t\t}\n\n\t\t\tback := len(stack) - 1\n\t\t\tstart := stack[back] // back\n\t\t\tstack = stack[:back] // pop\n\t\t\tsubRanges = append(subRanges, Range{\n\t\t\t\tstart: start,\n\t\t\t\tend:   i,\n\t\t\t})\n\t\t}\n\t}\n\n\tif len(stack) != 0 {\n\t\treturn nil, fmt.Errorf(\"format error is missing )\")\n\t}\n\n\tsort.Slice(subRanges, func(i, j int) bool {\n\t\treturn subRanges[i].start < subRanges[j].start\n\t})\n\n\treturn subRanges, nil\n}\n\nfunc (logic *Logic) findSubRuleRange(payload string, ruleRanges []Range) []Range {\n\tpayloadLen := len(payload)\n\tsubRuleRange := make([]Range, 0)\n\tfor _, rr := range ruleRanges {\n\t\tif rr.start == 0 && rr.end == payloadLen-1 {\n\t\t\t// 最大范围跳过\n\t\t\tcontinue\n\t\t}\n\n\t\tcontainInSub := false\n\t\tfor _, r := range subRuleRange {\n\t\t\tif rr.containRange(r.start, r.end) {\n\t\t\t\t// The subRuleRange contains a range of rr, which is the next level node of the tree\n\t\t\t\tcontainInSub = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\n\t\tif !containInSub {\n\t\t\tsubRuleRange = append(subRuleRange, rr)\n\t\t}\n\t}\n\n\treturn subRuleRange\n}\n\nfunc (logic *Logic) parsePayload(payload string, parseRule common.ParseRuleFunc) error {\n\tif !strings.HasPrefix(payload, \"(\") || !strings.HasSuffix(payload, \")\") { // the payload must be \"(xxx)\" format\n\t\treturn fmt.Errorf(\"payload format error\")\n\t}\n\n\tsubAllRanges, err := logic.format(payload)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\trules := make([]C.Rule, 0, len(subAllRanges))\n\n\tsubRanges := logic.findSubRuleRange(payload, subAllRanges)\n\tfor _, subRange := range subRanges {\n\t\tsubPayload := payload[subRange.start+1 : subRange.end]\n\n\t\trule, err := logic.payloadToRule(subPayload, parseRule)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\trules = append(rules, rule)\n\t}\n\n\tlogic.rules = rules\n\n\treturn nil\n}\n\nfunc (logic *Logic) RuleType() C.RuleType {\n\treturn logic.ruleType\n}\n\nfunc matchSubRules(metadata *C.Metadata, name string, subRules map[string][]C.Rule, helper C.RuleMatchHelper) (bool, string) {\n\tfor _, rule := range subRules[name] {\n\t\tif m, a := rule.Match(metadata, helper); m {\n\t\t\tif rule.RuleType() == C.SubRules {\n\t\t\t\treturn matchSubRules(metadata, rule.Adapter(), subRules, helper)\n\t\t\t} else {\n\t\t\t\treturn m, a\n\t\t\t}\n\t\t}\n\t}\n\treturn false, \"\"\n}\n\nfunc (logic *Logic) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {\n\tswitch logic.ruleType {\n\tcase C.SubRules:\n\t\tif m, _ := logic.rules[0].Match(metadata, helper); m {\n\t\t\treturn matchSubRules(metadata, logic.adapter, logic.subRules, helper)\n\t\t}\n\t\treturn false, \"\"\n\tcase C.NOT:\n\t\tif m, _ := logic.rules[0].Match(metadata, helper); !m {\n\t\t\treturn true, logic.adapter\n\t\t}\n\t\treturn false, \"\"\n\tcase C.OR:\n\t\tfor _, rule := range logic.rules {\n\t\t\tif m, _ := rule.Match(metadata, helper); m {\n\t\t\t\treturn true, logic.adapter\n\t\t\t}\n\t\t}\n\t\treturn false, \"\"\n\tcase C.AND:\n\t\tfor _, rule := range logic.rules {\n\t\t\tif m, _ := rule.Match(metadata, helper); !m {\n\t\t\t\treturn false, logic.adapter\n\t\t\t}\n\t\t}\n\t\treturn true, logic.adapter\n\tdefault:\n\t\treturn false, \"\"\n\t}\n}\n\nfunc (logic *Logic) Adapter() string {\n\treturn logic.adapter\n}\n\nfunc (logic *Logic) Payload() string {\n\tlogic.payloadOnce.Do(func() { // a little bit expensive, so only computed once\n\t\tswitch logic.ruleType {\n\t\tcase C.NOT:\n\t\t\tlogic.payload = fmt.Sprintf(\"(!(%s,%s))\", logic.rules[0].RuleType(), logic.rules[0].Payload())\n\t\tcase C.OR:\n\t\t\tpayloads := make([]string, 0, len(logic.rules))\n\t\t\tfor _, rule := range logic.rules {\n\t\t\t\tpayloads = append(payloads, fmt.Sprintf(\"(%s,%s)\", rule.RuleType().String(), rule.Payload()))\n\t\t\t}\n\t\t\tlogic.payload = fmt.Sprintf(\"(%s)\", strings.Join(payloads, \" || \"))\n\t\tcase C.AND:\n\t\t\tpayloads := make([]string, 0, len(logic.rules))\n\t\t\tfor _, rule := range logic.rules {\n\t\t\t\tpayloads = append(payloads, fmt.Sprintf(\"(%s,%s)\", rule.RuleType().String(), rule.Payload()))\n\t\t\t}\n\t\t\tlogic.payload = fmt.Sprintf(\"(%s)\", strings.Join(payloads, \" && \"))\n\t\tdefault:\n\t\t}\n\t})\n\treturn logic.payload\n}\n\nfunc (logic *Logic) ProviderNames() (names []string) {\n\tfor _, rule := range logic.rules {\n\t\tnames = append(names, rule.ProviderNames()...)\n\t}\n\treturn\n}\n\nvar _ C.Rule = (*Logic)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/rules/logic_test/logic_test.go",
    "content": "package logic_test\n\nimport (\n\t\"testing\"\n\n\t// https://github.com/golang/go/wiki/CodeReviewComments#import-dot\n\t. \"github.com/metacubex/mihomo/rules/logic\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/rules\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nvar ParseRule = rules.ParseRule\n\nfunc TestAND(t *testing.T) {\n\tand, err := NewAND(\"((DOMAIN,baidu.com),(NETWORK,TCP),(DST-PORT,10001-65535))\", \"DIRECT\", ParseRule)\n\tassert.Equal(t, nil, err)\n\tassert.Equal(t, \"DIRECT\", and.Adapter())\n\tm, _ := and.Match(&C.Metadata{\n\t\tHost:    \"baidu.com\",\n\t\tNetWork: C.TCP,\n\t\tDstPort: 20000,\n\t}, C.RuleMatchHelper{})\n\tassert.Equal(t, true, m)\n\n\tand, err = NewAND(\"(DOMAIN,baidu.com),(NETWORK,TCP),(DST-PORT,10001-65535))\", \"DIRECT\", ParseRule)\n\tassert.NotEqual(t, nil, err)\n\n\tand, err = NewAND(\"((AND,(DOMAIN,baidu.com),(NETWORK,TCP)),(NETWORK,TCP),(DST-PORT,10001-65535))\", \"DIRECT\", ParseRule)\n\tassert.Equal(t, nil, err)\n}\n\nfunc TestNOT(t *testing.T) {\n\tnot, err := NewNOT(\"((DST-PORT,6000-6500))\", \"REJECT\", ParseRule)\n\tassert.Equal(t, nil, err)\n\tm, _ := not.Match(&C.Metadata{\n\t\tDstPort: 6100,\n\t}, C.RuleMatchHelper{})\n\tassert.Equal(t, false, m)\n\n\t_, err = NewNOT(\"(DST-PORT,5600-6666)\", \"DIRECT\", ParseRule)\n\tassert.NotEqual(t, nil, err)\n\n\t_, err = NewNOT(\"DST-PORT,5600-6666\", \"DIRECT\", ParseRule)\n\tassert.NotEqual(t, nil, err)\n\n\t_, err = NewNOT(\"((DST-PORT,5600-6666),(DOMAIN,baidu.com))\", \"DIRECT\", ParseRule)\n\tassert.NotEqual(t, nil, err)\n\n\t_, err = NewNOT(\"(())\", \"DIRECT\", ParseRule)\n\tassert.NotEqual(t, nil, err)\n}\n\nfunc TestOR(t *testing.T) {\n\tor, err := NewOR(\"((DOMAIN,baidu.com),(NETWORK,TCP),(DST-PORT,10001-65535))\", \"DIRECT\", ParseRule)\n\tassert.Equal(t, nil, err)\n\tm, _ := or.Match(&C.Metadata{\n\t\tNetWork: C.TCP,\n\t}, C.RuleMatchHelper{})\n\tassert.Equal(t, true, m)\n}\n"
  },
  {
    "path": "core/Clash.Meta/rules/parser.go",
    "content": "package rules\n\nimport (\n\t\"fmt\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\tRC \"github.com/metacubex/mihomo/rules/common\"\n\t\"github.com/metacubex/mihomo/rules/logic\"\n\tRP \"github.com/metacubex/mihomo/rules/provider\"\n)\n\nfunc ParseRule(tp, payload, target string, params []string, subRules map[string][]C.Rule) (parsed C.Rule, parseErr error) {\n\tif tp != \"MATCH\" && payload == \"\" { // only MATCH allowed doesn't contain payload\n\t\treturn nil, fmt.Errorf(\"missing subsequent parameters: %s\", tp)\n\t}\n\n\tswitch tp {\n\tcase \"DOMAIN\":\n\t\tparsed = RC.NewDomain(payload, target)\n\tcase \"DOMAIN-SUFFIX\":\n\t\tparsed = RC.NewDomainSuffix(payload, target)\n\tcase \"DOMAIN-KEYWORD\":\n\t\tparsed = RC.NewDomainKeyword(payload, target)\n\tcase \"DOMAIN-REGEX\":\n\t\tparsed, parseErr = RC.NewDomainRegex(payload, target)\n\tcase \"DOMAIN-WILDCARD\":\n\t\tparsed, parseErr = RC.NewDomainWildcard(payload, target)\n\tcase \"GEOSITE\":\n\t\tparsed, parseErr = RC.NewGEOSITE(payload, target)\n\tcase \"GEOIP\":\n\t\tisSrc, noResolve := RC.ParseParams(params)\n\t\tparsed, parseErr = RC.NewGEOIP(payload, target, isSrc, noResolve)\n\tcase \"SRC-GEOIP\":\n\t\tparsed, parseErr = RC.NewGEOIP(payload, target, true, true)\n\tcase \"IP-ASN\":\n\t\tisSrc, noResolve := RC.ParseParams(params)\n\t\tparsed, parseErr = RC.NewIPASN(payload, target, isSrc, noResolve)\n\tcase \"SRC-IP-ASN\":\n\t\tparsed, parseErr = RC.NewIPASN(payload, target, true, true)\n\tcase \"IP-CIDR\", \"IP-CIDR6\":\n\t\tisSrc, noResolve := RC.ParseParams(params)\n\t\tparsed, parseErr = RC.NewIPCIDR(payload, target, RC.WithIPCIDRSourceIP(isSrc), RC.WithIPCIDRNoResolve(noResolve))\n\tcase \"SRC-IP-CIDR\":\n\t\tparsed, parseErr = RC.NewIPCIDR(payload, target, RC.WithIPCIDRSourceIP(true), RC.WithIPCIDRNoResolve(true))\n\tcase \"IP-SUFFIX\":\n\t\tisSrc, noResolve := RC.ParseParams(params)\n\t\tparsed, parseErr = RC.NewIPSuffix(payload, target, isSrc, noResolve)\n\tcase \"SRC-IP-SUFFIX\":\n\t\tparsed, parseErr = RC.NewIPSuffix(payload, target, true, true)\n\tcase \"SRC-PORT\":\n\t\tparsed, parseErr = RC.NewPort(payload, target, C.SrcPort)\n\tcase \"DST-PORT\":\n\t\tparsed, parseErr = RC.NewPort(payload, target, C.DstPort)\n\tcase \"IN-PORT\":\n\t\tparsed, parseErr = RC.NewPort(payload, target, C.InPort)\n\tcase \"DSCP\":\n\t\tparsed, parseErr = RC.NewDSCP(payload, target)\n\tcase \"PROCESS-NAME\":\n\t\tparsed, parseErr = RC.NewProcess(payload, target, C.ProcessName)\n\tcase \"PROCESS-PATH\":\n\t\tparsed, parseErr = RC.NewProcess(payload, target, C.ProcessPath)\n\tcase \"PROCESS-NAME-REGEX\":\n\t\tparsed, parseErr = RC.NewProcess(payload, target, C.ProcessNameRegex)\n\tcase \"PROCESS-PATH-REGEX\":\n\t\tparsed, parseErr = RC.NewProcess(payload, target, C.ProcessPathRegex)\n\tcase \"PROCESS-NAME-WILDCARD\":\n\t\tparsed, parseErr = RC.NewProcess(payload, target, C.ProcessNameWildcard)\n\tcase \"PROCESS-PATH-WILDCARD\":\n\t\tparsed, parseErr = RC.NewProcess(payload, target, C.ProcessPathWildcard)\n\tcase \"NETWORK\":\n\t\tparsed, parseErr = RC.NewNetworkType(payload, target)\n\tcase \"UID\":\n\t\tparsed, parseErr = RC.NewUid(payload, target)\n\tcase \"IN-TYPE\":\n\t\tparsed, parseErr = RC.NewInType(payload, target)\n\tcase \"IN-USER\":\n\t\tparsed, parseErr = RC.NewInUser(payload, target)\n\tcase \"IN-NAME\":\n\t\tparsed, parseErr = RC.NewInName(payload, target)\n\tcase \"SUB-RULE\":\n\t\tparsed, parseErr = logic.NewSubRule(payload, target, subRules, ParseRule)\n\tcase \"AND\":\n\t\tparsed, parseErr = logic.NewAND(payload, target, ParseRule)\n\tcase \"OR\":\n\t\tparsed, parseErr = logic.NewOR(payload, target, ParseRule)\n\tcase \"NOT\":\n\t\tparsed, parseErr = logic.NewNOT(payload, target, ParseRule)\n\tcase \"RULE-SET\":\n\t\tisSrc, noResolve := RC.ParseParams(params)\n\t\tparsed, parseErr = RP.NewRuleSet(payload, target, isSrc, noResolve)\n\tcase \"MATCH\":\n\t\tparsed = RC.NewMatch(target)\n\t\tparseErr = nil\n\tdefault:\n\t\tparseErr = fmt.Errorf(\"unsupported rule type: %s\", tp)\n\t}\n\n\tif parseErr != nil {\n\t\treturn nil, parseErr\n\t}\n\n\treturn\n}\n\nvar _ RC.ParseRuleFunc = ParseRule\n"
  },
  {
    "path": "core/Clash.Meta/rules/provider/classical_strategy.go",
    "content": "package provider\n\nimport (\n\t\"fmt\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\tP \"github.com/metacubex/mihomo/constant/provider\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/rules/common\"\n)\n\ntype classicalStrategy struct {\n\trules []C.Rule\n\tcount int\n\tparse common.ParseRuleFunc\n}\n\nfunc (c *classicalStrategy) Behavior() P.RuleBehavior {\n\treturn P.Classical\n}\n\nfunc (c *classicalStrategy) Match(metadata *C.Metadata, helper C.RuleMatchHelper) bool {\n\tfor _, rule := range c.rules {\n\t\tif m, _ := rule.Match(metadata, helper); m {\n\t\t\treturn true\n\t\t}\n\t}\n\n\treturn false\n}\n\nfunc (c *classicalStrategy) Count() int {\n\treturn c.count\n}\n\nfunc (c *classicalStrategy) Reset() {\n\tc.rules = nil\n\tc.count = 0\n}\n\nfunc (c *classicalStrategy) Insert(rule string) {\n\tr, err := c.payloadToRule(rule)\n\tif err != nil {\n\t\tlog.Warnln(\"parse classical rule [%s] error: %s\", rule, err.Error())\n\t} else {\n\t\tc.rules = append(c.rules, r)\n\t\tc.count++\n\t}\n}\n\nfunc (c *classicalStrategy) payloadToRule(rule string) (C.Rule, error) {\n\ttp, payload, target, params := common.ParseRulePayload(rule, false)\n\tswitch tp {\n\tcase \"MATCH\", \"RULE-SET\", \"SUB-RULE\":\n\t\treturn nil, fmt.Errorf(\"unsupported rule type on classical rule-set: %s\", tp)\n\t}\n\treturn c.parse(tp, payload, target, params, nil)\n}\n\nfunc (c *classicalStrategy) FinishInsert() {}\n\nfunc NewClassicalStrategy(parse common.ParseRuleFunc) *classicalStrategy {\n\treturn &classicalStrategy{rules: []C.Rule{}, parse: parse}\n}\n"
  },
  {
    "path": "core/Clash.Meta/rules/provider/domain_strategy.go",
    "content": "package provider\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/component/trie\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tP \"github.com/metacubex/mihomo/constant/provider\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"golang.org/x/exp/slices\"\n)\n\ntype domainStrategy struct {\n\tcount      int\n\tdomainTrie *trie.DomainTrie[struct{}]\n\tdomainSet  *trie.DomainSet\n}\n\nfunc (d *domainStrategy) Behavior() P.RuleBehavior {\n\treturn P.Domain\n}\n\nfunc (d *domainStrategy) Match(metadata *C.Metadata, helper C.RuleMatchHelper) bool {\n\treturn d.domainSet != nil && d.domainSet.Has(metadata.RuleHost())\n}\n\nfunc (d *domainStrategy) Count() int {\n\treturn d.count\n}\n\nfunc (d *domainStrategy) Reset() {\n\td.domainTrie = trie.New[struct{}]()\n\td.domainSet = nil\n\td.count = 0\n}\n\nfunc (d *domainStrategy) Insert(rule string) {\n\tif strings.ContainsRune(rule, '/') {\n\t\tlog.Warnln(\"invalid domain:[%s]\", rule)\n\t\treturn\n\t}\n\terr := d.domainTrie.Insert(rule, struct{}{})\n\tif err != nil {\n\t\tlog.Warnln(\"invalid domain:[%s]\", rule)\n\t} else {\n\t\td.count++\n\t}\n}\n\nfunc (d *domainStrategy) FinishInsert() {\n\td.domainSet = d.domainTrie.NewDomainSet()\n\td.domainTrie = nil\n}\n\nfunc (d *domainStrategy) FromMrs(r io.Reader, count int) error {\n\tdomainSet, err := trie.ReadDomainSetBin(r)\n\tif err != nil {\n\t\treturn err\n\t}\n\td.count = count\n\td.domainSet = domainSet\n\treturn nil\n}\n\nfunc (d *domainStrategy) WriteMrs(w io.Writer) error {\n\tif d.domainSet == nil {\n\t\treturn errors.New(\"nil domainSet\")\n\t}\n\treturn d.domainSet.WriteBin(w)\n}\n\nfunc (d *domainStrategy) DumpMrs(f func(key string) bool) {\n\tif d.domainSet != nil {\n\t\tvar keys []string\n\t\td.domainSet.Foreach(func(key string) bool {\n\t\t\tkeys = append(keys, key)\n\t\t\treturn true\n\t\t})\n\t\tslices.Sort(keys)\n\n\t\tfor _, key := range keys {\n\t\t\tif _, ok := slices.BinarySearch(keys, \"+.\"+key); ok {\n\t\t\t\tcontinue // ignore the rules added by trie internal processing\n\t\t\t}\n\t\t\tif !f(key) {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n\nvar _ mrsRuleStrategy = (*domainStrategy)(nil)\n\nfunc NewDomainStrategy() *domainStrategy {\n\treturn &domainStrategy{}\n}\n"
  },
  {
    "path": "core/Clash.Meta/rules/provider/ipcidr_strategy.go",
    "content": "package provider\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"net/netip\"\n\n\t\"github.com/metacubex/mihomo/component/cidr\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tP \"github.com/metacubex/mihomo/constant/provider\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"go4.org/netipx\"\n)\n\ntype ipcidrStrategy struct {\n\tcount   int\n\tcidrSet *cidr.IpCidrSet\n\t//trie    *trie.IpCidrTrie\n}\n\nfunc (i *ipcidrStrategy) Behavior() P.RuleBehavior {\n\treturn P.IPCIDR\n}\n\nfunc (i *ipcidrStrategy) Match(metadata *C.Metadata, helper C.RuleMatchHelper) bool {\n\tif helper.ResolveIP != nil {\n\t\thelper.ResolveIP()\n\t}\n\t// return i.trie != nil && i.trie.IsContain(metadata.DstIP.AsSlice())\n\treturn i.cidrSet != nil && i.cidrSet.IsContain(metadata.DstIP)\n}\n\nfunc (i *ipcidrStrategy) Count() int {\n\treturn i.count\n}\n\nfunc (i *ipcidrStrategy) Reset() {\n\t// i.trie = trie.NewIpCidrTrie()\n\ti.cidrSet = cidr.NewIpCidrSet()\n\ti.count = 0\n}\n\nfunc (i *ipcidrStrategy) Insert(rule string) {\n\t//err := i.trie.AddIpCidrForString(rule)\n\terr := i.cidrSet.AddIpCidrForString(rule)\n\tif err != nil {\n\t\tlog.Warnln(\"invalid Ipcidr:[%s]\", rule)\n\t} else {\n\t\ti.count++\n\t}\n}\n\nfunc (i *ipcidrStrategy) FinishInsert() {\n\ti.cidrSet.Merge()\n}\n\nfunc (i *ipcidrStrategy) FromMrs(r io.Reader, count int) error {\n\tcidrSet, err := cidr.ReadIpCidrSet(r)\n\tif err != nil {\n\t\treturn err\n\t}\n\ti.count = count\n\ti.cidrSet = cidrSet\n\treturn nil\n}\n\nfunc (i *ipcidrStrategy) WriteMrs(w io.Writer) error {\n\tif i.cidrSet == nil {\n\t\treturn errors.New(\"nil cidrSet\")\n\t}\n\treturn i.cidrSet.WriteBin(w)\n}\n\nfunc (i *ipcidrStrategy) DumpMrs(f func(key string) bool) {\n\tif i.cidrSet != nil {\n\t\ti.cidrSet.Foreach(func(prefix netip.Prefix) bool {\n\t\t\treturn f(prefix.String())\n\t\t})\n\t}\n}\n\nfunc (i *ipcidrStrategy) ToIpCidr() *netipx.IPSet {\n\treturn i.cidrSet.ToIPSet()\n}\n\nfunc NewIPCidrStrategy() *ipcidrStrategy {\n\treturn &ipcidrStrategy{}\n}\n"
  },
  {
    "path": "core/Clash.Meta/rules/provider/mrs_converter.go",
    "content": "package provider\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\n\tP \"github.com/metacubex/mihomo/constant/provider\"\n\n\t\"github.com/klauspost/compress/zstd\"\n)\n\nfunc ConvertToMrs(buf []byte, behavior P.RuleBehavior, format P.RuleFormat, w io.Writer) (err error) {\n\tstrategy := newStrategy(behavior, nil)\n\tstrategy, err = rulesParse(buf, strategy, format)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif strategy.Count() == 0 {\n\t\treturn errors.New(\"empty rule\")\n\t}\n\tif _strategy, ok := strategy.(mrsRuleStrategy); ok {\n\t\tif format == P.MrsRule { // export to TextRule\n\t\t\t_strategy.DumpMrs(func(key string) bool {\n\t\t\t\t_, err = fmt.Fprintln(w, key)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn false\n\t\t\t\t}\n\t\t\t\treturn true\n\t\t\t})\n\t\t\treturn nil\n\t\t}\n\n\t\tvar encoder *zstd.Encoder\n\t\tencoder, err = zstd.NewWriter(w)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tdefer func() {\n\t\t\tzstdErr := encoder.Close()\n\t\t\tif err == nil {\n\t\t\t\terr = zstdErr\n\t\t\t}\n\t\t}()\n\n\t\t// header\n\t\t_, err = encoder.Write(MrsMagicBytes[:])\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// behavior\n\t\t_behavior := []byte{behavior.Byte()}\n\t\t_, err = encoder.Write(_behavior[:])\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// count\n\t\tcount := int64(_strategy.Count())\n\t\terr = binary.Write(encoder, binary.BigEndian, count)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\t// extra (reserved for future using)\n\t\tvar extra []byte\n\t\terr = binary.Write(encoder, binary.BigEndian, int64(len(extra)))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\t_, err = encoder.Write(extra)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\treturn _strategy.WriteMrs(encoder)\n\t} else {\n\t\treturn ErrInvalidFormat\n\t}\n}\n\nfunc ConvertMain(args []string) {\n\tif len(args) > 3 {\n\t\tbehavior, err := P.ParseBehavior(args[0])\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tformat, err := P.ParseRuleFormat(args[1])\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t\tsource := args[2]\n\t\ttarget := args[3]\n\n\t\tsourceFile, err := os.ReadFile(source)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\ttargetFile, err := os.OpenFile(target, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0644)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\terr = ConvertToMrs(sourceFile, behavior, format, targetFile)\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\terr = targetFile.Close()\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t} else {\n\t\tpanic(\"Usage: convert-ruleset <behavior> <format> <source file> <target file>\")\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/rules/provider/mrs_reader.go",
    "content": "package provider\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"github.com/klauspost/compress/zstd\"\n)\n\nvar MrsMagicBytes = [4]byte{'M', 'R', 'S', 1} // MRSv1\n\nfunc rulesMrsParse(buf []byte, strategy ruleStrategy) (ruleStrategy, error) {\n\tif _strategy, ok := strategy.(mrsRuleStrategy); ok {\n\t\treader, err := zstd.NewReader(bytes.NewReader(buf))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdefer reader.Close()\n\n\t\t// header\n\t\tvar header [4]byte\n\t\t_, err = io.ReadFull(reader, header[:])\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif header != MrsMagicBytes {\n\t\t\treturn nil, fmt.Errorf(\"invalid MrsMagic bytes\")\n\t\t}\n\n\t\t// behavior\n\t\tvar _behavior [1]byte\n\t\t_, err = io.ReadFull(reader, _behavior[:])\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif _behavior[0] != strategy.Behavior().Byte() {\n\t\t\treturn nil, fmt.Errorf(\"invalid behavior\")\n\t\t}\n\n\t\t// count\n\t\tvar count int64\n\t\terr = binary.Read(reader, binary.BigEndian, &count)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\t// extra (reserved for future using)\n\t\tvar length int64\n\t\terr = binary.Read(reader, binary.BigEndian, &length)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif length < 0 {\n\t\t\treturn nil, errors.New(\"length is invalid\")\n\t\t}\n\t\tif length > 0 {\n\t\t\textra := make([]byte, length)\n\t\t\t_, err = io.ReadFull(reader, extra)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\n\t\terr = _strategy.FromMrs(reader, int(count))\n\t\treturn strategy, err\n\t} else {\n\t\treturn nil, ErrInvalidFormat\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/rules/provider/parse.go",
    "content": "package provider\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/structure\"\n\t\"github.com/metacubex/mihomo/component/resource\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tP \"github.com/metacubex/mihomo/constant/provider\"\n\t\"github.com/metacubex/mihomo/rules/common\"\n)\n\ntype ruleProviderSchema struct {\n\tType      string              `provider:\"type\"`\n\tBehavior  string              `provider:\"behavior\"`\n\tPath      string              `provider:\"path,omitempty\"`\n\tURL       string              `provider:\"url,omitempty\"`\n\tProxy     string              `provider:\"proxy,omitempty\"`\n\tFormat    string              `provider:\"format,omitempty\"`\n\tInterval  int                 `provider:\"interval,omitempty\"`\n\tSizeLimit int64               `provider:\"size-limit,omitempty\"`\n\tPayload   []string            `provider:\"payload,omitempty\"`\n\tHeader    map[string][]string `provider:\"header,omitempty\"`\n}\n\nfunc ParseRuleProvider(name string, mapping map[string]any, parse common.ParseRuleFunc) (P.RuleProvider, error) {\n\tschema := &ruleProviderSchema{}\n\tdecoder := structure.NewDecoder(structure.Option{TagName: \"provider\", WeaklyTypedInput: true})\n\tif err := decoder.Decode(mapping, schema); err != nil {\n\t\treturn nil, err\n\t}\n\tbehavior, err := P.ParseBehavior(schema.Behavior)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tformat, err := P.ParseRuleFormat(schema.Format)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar vehicle P.Vehicle\n\tswitch schema.Type {\n\tcase \"file\":\n\t\tpath := C.Path.Resolve(schema.Path)\n\t\tif !C.Path.IsSafePath(path) {\n\t\t\treturn nil, C.Path.ErrNotSafePath(path)\n\t\t}\n\t\tvehicle = resource.NewFileVehicle(path)\n\tcase \"http\":\n\t\tpath := C.Path.GetPathByHash(\"rules\", schema.URL)\n\t\tif schema.Path != \"\" {\n\t\t\tpath = C.Path.Resolve(schema.Path)\n\t\t\tif !C.Path.IsSafePath(path) {\n\t\t\t\treturn nil, C.Path.ErrNotSafePath(path)\n\t\t\t}\n\t\t}\n\t\tvehicle = resource.NewHTTPVehicle(schema.URL, path, schema.Proxy, schema.Header, resource.DefaultHttpTimeout, schema.SizeLimit)\n\tcase \"inline\":\n\t\treturn NewInlineProvider(name, behavior, schema.Payload, parse), nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unsupported vehicle type: %s\", schema.Type)\n\t}\n\n\tinterval := time.Duration(uint(schema.Interval)) * time.Second\n\n\treturn NewRuleSetProvider(name, behavior, format, interval, vehicle, schema.Payload, parse), nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/rules/provider/patch_android.go",
    "content": "//go:build android\n\npackage provider\n\nimport \"time\"\n\nvar (\n\tsuspended bool\n)\n\ntype UpdatableProvider interface {\n\tUpdatedAt() time.Time\n}\n\nfunc Suspend(s bool) {\n\tsuspended = s\n}\n"
  },
  {
    "path": "core/Clash.Meta/rules/provider/provider.go",
    "content": "package provider\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"io\"\n\t\"runtime\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/pool\"\n\t\"github.com/metacubex/mihomo/common/yaml\"\n\t\"github.com/metacubex/mihomo/component/resource\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\tP \"github.com/metacubex/mihomo/constant/provider\"\n\t\"github.com/metacubex/mihomo/rules/common\"\n)\n\nvar tunnel P.Tunnel\n\nfunc SetTunnel(t P.Tunnel) {\n\ttunnel = t\n}\n\ntype RulePayload struct {\n\t/**\n\tkey: Domain or IP Cidr\n\tvalue: Rule type or is empty\n\t*/\n\tPayload []string `yaml:\"payload\"`\n\tRules   []string `yaml:\"rules\"`\n}\n\ntype providerForApi struct {\n\tBehavior    string    `json:\"behavior\"`\n\tFormat      string    `json:\"format\"`\n\tName        string    `json:\"name\"`\n\tRuleCount   int       `json:\"ruleCount\"`\n\tType        string    `json:\"type\"`\n\tVehicleType string    `json:\"vehicleType\"`\n\tUpdatedAt   time.Time `json:\"updatedAt\"`\n\tPayload     []string  `json:\"payload,omitempty\"`\n}\n\ntype ruleStrategy interface {\n\tBehavior() P.RuleBehavior\n\tMatch(metadata *C.Metadata, helper C.RuleMatchHelper) bool\n\tCount() int\n\tReset()\n\tInsert(rule string)\n\tFinishInsert()\n}\n\ntype mrsRuleStrategy interface {\n\truleStrategy\n\tFromMrs(r io.Reader, count int) error\n\tWriteMrs(w io.Writer) error\n\tDumpMrs(f func(key string) bool)\n}\n\ntype baseProvider struct {\n\tbehavior P.RuleBehavior\n\tstrategy ruleStrategy\n}\n\nfunc (bp *baseProvider) Type() P.ProviderType {\n\treturn P.Rule\n}\n\nfunc (bp *baseProvider) Behavior() P.RuleBehavior {\n\treturn bp.behavior\n}\n\nfunc (bp *baseProvider) Count() int {\n\treturn bp.strategy.Count()\n}\n\nfunc (bp *baseProvider) Match(metadata *C.Metadata, helper C.RuleMatchHelper) bool {\n\treturn bp.strategy != nil && bp.strategy.Match(metadata, helper)\n}\n\nfunc (bp *baseProvider) Strategy() any {\n\treturn bp.strategy\n}\n\ntype ruleSetProvider struct {\n\tbaseProvider\n\t*resource.Fetcher[ruleStrategy]\n\tformat P.RuleFormat\n}\n\ntype RuleSetProvider struct {\n\t*ruleSetProvider\n}\n\nfunc (rp *ruleSetProvider) Initial() error {\n\t_, err := rp.Fetcher.Initial()\n\treturn err\n}\n\nfunc (rp *ruleSetProvider) Update() error {\n\t_, _, err := rp.Fetcher.Update()\n\treturn err\n}\n\nfunc (rp *ruleSetProvider) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(\n\t\tproviderForApi{\n\t\t\tBehavior:    rp.behavior.String(),\n\t\t\tFormat:      rp.format.String(),\n\t\t\tName:        rp.Fetcher.Name(),\n\t\t\tRuleCount:   rp.strategy.Count(),\n\t\t\tType:        rp.Type().String(),\n\t\t\tUpdatedAt:   rp.UpdatedAt(),\n\t\t\tVehicleType: rp.VehicleType().String(),\n\t\t})\n}\n\nfunc (rp *RuleSetProvider) Close() error {\n\truntime.SetFinalizer(rp, nil)\n\treturn rp.ruleSetProvider.Close()\n}\n\nfunc NewRuleSetProvider(name string, behavior P.RuleBehavior, format P.RuleFormat, interval time.Duration, vehicle P.Vehicle, payload []string, parse common.ParseRuleFunc) P.RuleProvider {\n\trp := &ruleSetProvider{\n\t\tbaseProvider: baseProvider{\n\t\t\tbehavior: behavior,\n\t\t},\n\t\tformat: format,\n\t}\n\n\tonUpdate := func(strategy ruleStrategy) {\n\t\trp.strategy = strategy\n\t\ttunnel.RuleUpdateCallback().Emit(rp)\n\t}\n\n\trp.strategy = newStrategy(behavior, parse)\n\tif len(payload) > 0 { // using as fallback rules\n\t\trp.strategy = rulesParseInline(payload, rp.strategy)\n\t}\n\trp.Fetcher = resource.NewFetcher(name, interval, vehicle, func(bytes []byte) (ruleStrategy, error) {\n\t\treturn rulesParse(bytes, newStrategy(behavior, parse), format)\n\t}, onUpdate)\n\n\twrapper := &RuleSetProvider{\n\t\trp,\n\t}\n\n\truntime.SetFinalizer(wrapper, (*RuleSetProvider).Close)\n\treturn wrapper\n}\n\nfunc newStrategy(behavior P.RuleBehavior, parse common.ParseRuleFunc) ruleStrategy {\n\tswitch behavior {\n\tcase P.Domain:\n\t\tstrategy := NewDomainStrategy()\n\t\treturn strategy\n\tcase P.IPCIDR:\n\t\tstrategy := NewIPCidrStrategy()\n\t\treturn strategy\n\tcase P.Classical:\n\t\tstrategy := NewClassicalStrategy(parse)\n\t\treturn strategy\n\tdefault:\n\t\treturn nil\n\t}\n}\n\nvar (\n\tErrNoPayload     = errors.New(\"file must have a `payload` field\")\n\tErrInvalidFormat = errors.New(\"invalid format\")\n)\n\nfunc rulesParse(buf []byte, strategy ruleStrategy, format P.RuleFormat) (ruleStrategy, error) {\n\tstrategy.Reset()\n\tif format == P.MrsRule {\n\t\treturn rulesMrsParse(buf, strategy)\n\t}\n\n\tschema := &RulePayload{}\n\n\tfirstLineBuffer := pool.GetBuffer()\n\tdefer pool.PutBuffer(firstLineBuffer)\n\tfirstLineLength := 0\n\n\ts := 0 // search start index\n\tfor s < len(buf) {\n\t\t// search buffer for a new line.\n\t\tline := buf[s:]\n\t\tif i := bytes.IndexByte(line, '\\n'); i >= 0 {\n\t\t\ti += s\n\t\t\tline = buf[s : i+1]\n\t\t\ts = i + 1\n\t\t} else {\n\t\t\ts = len(buf)                                      // stop loop in next step\n\t\t\tif firstLineLength == 0 && format == P.YamlRule { // no head or only one line body\n\t\t\t\treturn nil, ErrNoPayload\n\t\t\t}\n\t\t}\n\t\tvar str string\n\t\tswitch format {\n\t\tcase P.TextRule:\n\t\t\tstr = string(line)\n\t\t\tstr = strings.TrimSpace(str)\n\t\t\tif len(str) == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif str[0] == '#' { // comment\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif strings.HasPrefix(str, \"//\") { // comment in Premium core\n\t\t\t\tcontinue\n\t\t\t}\n\t\tcase P.YamlRule:\n\t\t\ttrimLine := bytes.TrimSpace(line)\n\t\t\tif len(trimLine) == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif trimLine[0] == '#' { // comment\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tfirstLineBuffer.Write(line)\n\t\t\tif firstLineLength == 0 { // find payload head\n\t\t\t\tfirstLineLength = firstLineBuffer.Len()\n\t\t\t\tfirstLineBuffer.WriteString(\"  - ''\") // a test line\n\n\t\t\t\terr := yaml.Unmarshal(firstLineBuffer.Bytes(), schema)\n\t\t\t\tfirstLineBuffer.Truncate(firstLineLength)\n\t\t\t\tif err == nil && (len(schema.Rules) > 0 || len(schema.Payload) > 0) { // found\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\t// not found or err!=nil\n\t\t\t\tfirstLineBuffer.Truncate(0)\n\t\t\t\tfirstLineLength = 0\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// parse payload body\n\t\t\terr := yaml.Unmarshal(firstLineBuffer.Bytes(), schema)\n\t\t\tfirstLineBuffer.Truncate(firstLineLength)\n\t\t\tif err != nil {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif len(schema.Rules) > 0 {\n\t\t\t\tstr = schema.Rules[0]\n\t\t\t}\n\t\t\tif len(schema.Payload) > 0 {\n\t\t\t\tstr = schema.Payload[0]\n\t\t\t}\n\t\tdefault:\n\t\t\treturn nil, ErrInvalidFormat\n\t\t}\n\n\t\tif str == \"\" {\n\t\t\tcontinue\n\t\t}\n\n\t\tstrategy.Insert(str)\n\t}\n\n\tstrategy.FinishInsert()\n\n\treturn strategy, nil\n}\n\nfunc rulesParseInline(rs []string, strategy ruleStrategy) ruleStrategy {\n\tstrategy.Reset()\n\tfor _, r := range rs {\n\t\tif r != \"\" {\n\t\t\tstrategy.Insert(r)\n\t\t}\n\t}\n\tstrategy.FinishInsert()\n\treturn strategy\n}\n\ntype InlineProvider struct {\n\t*inlineProvider\n}\n\ntype inlineProvider struct {\n\tbaseProvider\n\tname     string\n\tupdateAt time.Time\n\tpayload  []string\n}\n\nfunc (i *inlineProvider) Name() string {\n\treturn i.name\n}\n\nfunc (i *inlineProvider) Initial() error {\n\treturn nil\n}\n\nfunc (i *inlineProvider) Update() error {\n\t// make api update happy\n\ti.updateAt = time.Now()\n\treturn nil\n}\n\nfunc (i *inlineProvider) VehicleType() P.VehicleType {\n\treturn P.Inline\n}\n\nfunc (i *inlineProvider) MarshalJSON() ([]byte, error) {\n\treturn json.Marshal(\n\t\tproviderForApi{\n\t\t\tBehavior:    i.behavior.String(),\n\t\t\tName:        i.Name(),\n\t\t\tRuleCount:   i.strategy.Count(),\n\t\t\tType:        i.Type().String(),\n\t\t\tVehicleType: i.VehicleType().String(),\n\t\t\tUpdatedAt:   i.updateAt,\n\t\t\tPayload:     i.payload,\n\t\t})\n}\n\nfunc NewInlineProvider(name string, behavior P.RuleBehavior, payload []string, parse common.ParseRuleFunc) P.RuleProvider {\n\tip := &inlineProvider{\n\t\tbaseProvider: baseProvider{\n\t\t\tbehavior: behavior,\n\t\t\tstrategy: newStrategy(behavior, parse),\n\t\t},\n\t\tpayload:  payload,\n\t\tname:     name,\n\t\tupdateAt: time.Now(),\n\t}\n\tip.strategy = rulesParseInline(payload, ip.strategy)\n\n\twrapper := &InlineProvider{\n\t\tip,\n\t}\n\n\t//runtime.SetFinalizer(wrapper, (*InlineProvider).Close)\n\treturn wrapper\n}\n"
  },
  {
    "path": "core/Clash.Meta/rules/provider/rule_set.go",
    "content": "package provider\n\nimport (\n\t\"net/netip\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\tP \"github.com/metacubex/mihomo/constant/provider\"\n\t\"github.com/metacubex/mihomo/rules/common\"\n)\n\ntype RuleSet struct {\n\tcommon.Base\n\truleProviderName string\n\tadapter          string\n\tisSrc            bool\n\tnoResolveIP      bool\n}\n\nfunc (rs *RuleSet) RuleType() C.RuleType {\n\treturn C.RuleSet\n}\n\nfunc (rs *RuleSet) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {\n\tif provider, ok := rs.getProvider(); ok {\n\t\tif rs.isSrc {\n\t\t\tmetadata.SwapSrcDst()\n\t\t\tdefer metadata.SwapSrcDst()\n\n\t\t\thelper.ResolveIP = nil // src mode should not resolve ip\n\t\t} else if rs.noResolveIP {\n\t\t\thelper.ResolveIP = nil\n\t\t}\n\t\treturn provider.Match(metadata, helper), rs.adapter\n\t}\n\treturn false, \"\"\n}\n\n// MatchDomain implements C.DomainMatcher\nfunc (rs *RuleSet) MatchDomain(domain string) bool {\n\tok, _ := rs.Match(&C.Metadata{Host: domain}, C.RuleMatchHelper{})\n\treturn ok\n}\n\n// MatchIp implements C.IpMatcher\nfunc (rs *RuleSet) MatchIp(ip netip.Addr) bool {\n\tok, _ := rs.Match(&C.Metadata{DstIP: ip}, C.RuleMatchHelper{})\n\treturn ok\n}\n\nfunc (rs *RuleSet) Adapter() string {\n\treturn rs.adapter\n}\n\nfunc (rs *RuleSet) Payload() string {\n\treturn rs.ruleProviderName\n}\n\nfunc (rs *RuleSet) ProviderNames() []string {\n\treturn []string{rs.ruleProviderName}\n}\n\nfunc (rs *RuleSet) getProvider() (P.RuleProvider, bool) {\n\tpp, ok := tunnel.RuleProviders()[rs.ruleProviderName]\n\treturn pp, ok\n}\n\nfunc NewRuleSet(ruleProviderName string, adapter string, isSrc bool, noResolveIP bool) (*RuleSet, error) {\n\trs := &RuleSet{\n\t\tBase:             common.Base{},\n\t\truleProviderName: ruleProviderName,\n\t\tadapter:          adapter,\n\t\tisSrc:            isSrc,\n\t\tnoResolveIP:      noResolveIP,\n\t}\n\treturn rs, nil\n}\n\nvar _ C.Rule = (*RuleSet)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/rules/wrapper/wrapper.go",
    "content": "package wrapper\n\nimport (\n\t\"sync/atomic\"\n\t\"time\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\ntype RuleWrapper struct {\n\tC.Rule\n\tdisabled  atomic.Bool\n\thitCount  atomic.Uint64\n\thitAt     atomicTime\n\tmissCount atomic.Uint64\n\tmissAt    atomicTime\n}\n\nfunc (r *RuleWrapper) IsDisabled() bool {\n\treturn r.disabled.Load()\n}\n\nfunc (r *RuleWrapper) SetDisabled(v bool) {\n\tr.disabled.Store(v)\n}\n\nfunc (r *RuleWrapper) HitCount() uint64 {\n\treturn r.hitCount.Load()\n}\n\nfunc (r *RuleWrapper) HitAt() time.Time {\n\treturn r.hitAt.Load()\n}\n\nfunc (r *RuleWrapper) MissCount() uint64 {\n\treturn r.missCount.Load()\n}\n\nfunc (r *RuleWrapper) MissAt() time.Time {\n\treturn r.missAt.Load()\n}\n\nfunc (r *RuleWrapper) Unwrap() C.Rule {\n\treturn r.Rule\n}\n\nfunc (r *RuleWrapper) Hit() {\n\tr.hitCount.Add(1)\n\tr.hitAt.Store(time.Now())\n}\n\nfunc (r *RuleWrapper) Miss() {\n\tr.missCount.Add(1)\n\tr.missAt.Store(time.Now())\n}\n\nfunc (r *RuleWrapper) Match(metadata *C.Metadata, helper C.RuleMatchHelper) (bool, string) {\n\tif r.IsDisabled() {\n\t\treturn false, \"\"\n\t}\n\tok, adapter := r.Rule.Match(metadata, helper)\n\tif ok {\n\t\tr.Hit()\n\t} else {\n\t\tr.Miss()\n\t}\n\treturn ok, adapter\n}\n\nfunc NewRuleWrapper(rule C.Rule) C.RuleWrapper {\n\treturn &RuleWrapper{Rule: rule}\n}\n\n// atomicTime is a wrapper of [atomic.Int64] to provide atomic time storage.\n// it only saves unix nanosecond export from time.Time.\n// unlike atomic.TypedValue[time.Time] always escapes a new time.Time to heap when storing.\n// that will lead to higher GC pressure during high frequency writes.\n// be careful, it discards monotime so should not be used for internal time comparisons.\ntype atomicTime struct {\n\ti atomic.Int64\n}\n\nfunc (t *atomicTime) Load() time.Time {\n\treturn time.Unix(0, t.i.Load())\n}\n\nfunc (t *atomicTime) Store(v time.Time) {\n\tt.i.Store(v.UnixNano())\n}\n\nfunc (t *atomicTime) Swap(v time.Time) time.Time {\n\treturn time.Unix(0, t.i.Swap(v.UnixNano()))\n}\n\nfunc (t *atomicTime) CompareAndSwap(old, new time.Time) bool {\n\treturn t.i.CompareAndSwap(old.UnixNano(), new.UnixNano())\n}\n\nfunc (t *atomicTime) MarshalText() ([]byte, error) {\n\treturn t.Load().MarshalText()\n}\n\nfunc (t *atomicTime) UnmarshalText(text []byte) error {\n\tvar v time.Time\n\tif err := v.UnmarshalText(text); err != nil {\n\t\treturn err\n\t}\n\tt.Store(v)\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/test/.golangci.yaml",
    "content": "linters:\n  disable-all: true\n  enable:\n    - gofumpt\n    - govet\n    - gci\n    - staticcheck\n\nlinters-settings:\n  gci:\n    sections:\n      - standard\n      - prefix(github.com/metacubex/mihomo)\n      - default\n  staticcheck:\n    go: '1.19'\n"
  },
  {
    "path": "core/Clash.Meta/test/Makefile",
    "content": "lint:\n\tGOOS=darwin golangci-lint run ./...\n\tGOOS=linux golangci-lint run ./...\n\ntest:\n\tgo test -p 1 -v ./...\n\nbenchmark:\n\tgo test -benchmem -run=^$$ -bench .\n"
  },
  {
    "path": "core/Clash.Meta/test/clash_test.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"crypto/md5\"\n\t\"crypto/rand\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/netip\"\n\t\"os\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/docker/docker/api/types\"\n\t\"github.com/docker/docker/client\"\n\t\"github.com/docker/go-connections/nat\"\n\t\"github.com/metacubex/mihomo/adapter/outbound\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/hub/executor\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nconst (\n\tImageShadowsocks     = \"mritd/shadowsocks:latest\"\n\tImageShadowsocksRust = \"ghcr.io/shadowsocks/ssserver-rust:latest\"\n\tImageVmess           = \"v2fly/v2fly-core:v4.45.2\"\n\tImageVmessLatest     = \"sagernet/v2fly-core:latest\"\n\tImageVless           = \"teddysun/xray:latest\"\n\tImageTrojan          = \"trojangfw/trojan:latest\"\n\tImageTrojanGo        = \"p4gefau1t/trojan-go:latest\"\n\tImageSnell           = \"ghcr.io/icpz/snell-server:latest\"\n\tImageXray            = \"teddysun/xray:latest\"\n\tImageHysteria        = \"tobyxdd/hysteria:latest\"\n)\n\nvar (\n\twaitTime = time.Second\n\tlocalIP  = netip.MustParseAddr(\"127.0.0.1\")\n\n\tdefaultExposedPorts = nat.PortSet{\n\t\t\"10002/tcp\": struct{}{},\n\t\t\"10002/udp\": struct{}{},\n\t}\n\tdefaultPortBindings = nat.PortMap{\n\t\t\"10002/tcp\": []nat.PortBinding{\n\t\t\t{HostPort: \"10002\", HostIP: \"0.0.0.0\"},\n\t\t},\n\t\t\"10002/udp\": []nat.PortBinding{\n\t\t\t{HostPort: \"10002\", HostIP: \"0.0.0.0\"},\n\t\t},\n\t}\n\tisDarwin = runtime.GOOS == \"darwin\"\n)\n\nfunc init() {\n\tcurrentDir, err := os.Getwd()\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\thomeDir := filepath.Join(currentDir, \"config\")\n\tC.SetHomeDir(homeDir)\n\n\tif isDarwin {\n\t\tlocalIP, err = defaultRouteIP()\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\t}\n\n\tc, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\tdefer c.Close()\n\n\tlist, err := c.ImageList(context.Background(), types.ImageListOptions{All: true})\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\n\timageExist := func(image string) bool {\n\t\tfor _, item := range list {\n\t\t\tfor _, tag := range item.RepoTags {\n\t\t\t\tif image == tag {\n\t\t\t\t\treturn true\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\treturn false\n\t}\n\n\timages := []string{\n\t\tImageShadowsocks,\n\t\tImageShadowsocksRust,\n\t\tImageVmess,\n\t\tImageVless,\n\t\tImageTrojan,\n\t\tImageTrojanGo,\n\t\tImageSnell,\n\t\tImageXray,\n\t\tImageHysteria,\n\t}\n\n\tfor _, image := range images {\n\t\tif imageExist(image) {\n\t\t\tcontinue\n\t\t}\n\n\t\tprintln(\"pulling image:\", image)\n\t\timageStream, err := c.ImagePull(context.Background(), image, types.ImagePullOptions{})\n\t\tif err != nil {\n\t\t\tpanic(err)\n\t\t}\n\n\t\tio.Copy(io.Discard, imageStream)\n\t}\n}\n\nvar clean = `\nport: 0\nsocks-port: 0\nmixed-port: 0\nredir-port: 0\ntproxy-port: 0\ndns:\n\tenable: false\n`\n\nfunc cleanup() {\n\tparseAndApply(clean)\n}\n\nfunc parseAndApply(cfgStr string) error {\n\tcfg, err := executor.ParseWithBytes([]byte(cfgStr))\n\tif err != nil {\n\t\treturn err\n\t}\n\n\texecutor.ApplyConfig(cfg, true)\n\treturn nil\n}\n\nfunc newPingPongPair() (chan []byte, chan []byte, func(t *testing.T) error) {\n\tpingCh := make(chan []byte)\n\tpongCh := make(chan []byte)\n\ttest := func(t *testing.T) error {\n\t\tdefer close(pingCh)\n\t\tdefer close(pongCh)\n\t\tpingOpen := false\n\t\tpongOpen := false\n\t\tvar recv []byte\n\n\t\tfor {\n\t\t\tif pingOpen && pongOpen {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tselect {\n\t\t\tcase recv, pingOpen = <-pingCh:\n\t\t\t\tassert.True(t, pingOpen)\n\t\t\t\tassert.Equal(t, []byte(\"ping\"), recv)\n\t\t\tcase recv, pongOpen = <-pongCh:\n\t\t\t\tassert.True(t, pongOpen)\n\t\t\t\tassert.Equal(t, []byte(\"pong\"), recv)\n\t\t\tcase <-time.After(10 * time.Second):\n\t\t\t\treturn errors.New(\"timeout\")\n\t\t\t}\n\t\t}\n\t\treturn nil\n\t}\n\n\treturn pingCh, pongCh, test\n}\n\nfunc newLargeDataPair() (chan hashPair, chan hashPair, func(t *testing.T) error) {\n\tpingCh := make(chan hashPair)\n\tpongCh := make(chan hashPair)\n\ttest := func(t *testing.T) error {\n\t\tdefer close(pingCh)\n\t\tdefer close(pongCh)\n\t\tpingOpen := false\n\t\tpongOpen := false\n\t\tvar serverPair hashPair\n\t\tvar clientPair hashPair\n\n\t\tfor {\n\t\t\tif pingOpen && pongOpen {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\tselect {\n\t\t\tcase serverPair, pingOpen = <-pingCh:\n\t\t\t\tassert.True(t, pingOpen)\n\t\t\tcase clientPair, pongOpen = <-pongCh:\n\t\t\t\tassert.True(t, pongOpen)\n\t\t\tcase <-time.After(10 * time.Second):\n\t\t\t\treturn errors.New(\"timeout\")\n\t\t\t}\n\t\t}\n\n\t\tassert.Equal(t, serverPair.recvHash, clientPair.sendHash)\n\t\tassert.Equal(t, serverPair.sendHash, clientPair.recvHash)\n\n\t\treturn nil\n\t}\n\n\treturn pingCh, pongCh, test\n}\n\nfunc testPingPongWithSocksPort(t *testing.T, port int) {\n\tpingCh, pongCh, test := newPingPongPair()\n\tgo func() {\n\t\tl, err := Listen(\"tcp\", \":10001\")\n\t\trequire.NoError(t, err)\n\t\tdefer l.Close()\n\n\t\tc, err := l.Accept()\n\t\trequire.NoError(t, err)\n\n\t\tbuf := make([]byte, 4)\n\t\t_, err = io.ReadFull(c, buf)\n\t\trequire.NoError(t, err)\n\n\t\tpingCh <- buf\n\t\t_, err = c.Write([]byte(\"pong\"))\n\t\trequire.NoError(t, err)\n\t}()\n\n\tgo func() {\n\t\tc, err := net.Dial(\"tcp\", fmt.Sprintf(\"127.0.0.1:%d\", port))\n\t\trequire.NoError(t, err)\n\t\tdefer c.Close()\n\n\t\t_, err = socks5.ClientHandshake(c, socks5.ParseAddr(\"127.0.0.1:10001\"), socks5.CmdConnect, nil)\n\t\trequire.NoError(t, err)\n\n\t\t_, err = c.Write([]byte(\"ping\"))\n\t\trequire.NoError(t, err)\n\n\t\tbuf := make([]byte, 4)\n\t\t_, err = io.ReadFull(c, buf)\n\t\trequire.NoError(t, err)\n\n\t\tpongCh <- buf\n\t}()\n\n\ttest(t)\n}\n\nfunc testPingPongWithConn(t *testing.T, cc func() net.Conn) error {\n\tl, err := Listen(\"tcp\", \":10001\")\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer l.Close()\n\n\tpingCh, pongCh, test := newPingPongPair()\n\tgo func() {\n\t\tc, err := l.Accept()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tbuf := make([]byte, 4)\n\t\tif _, err := io.ReadFull(c, buf); err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tpingCh <- buf\n\t\tif _, err := c.Write([]byte(\"pong\")); err != nil {\n\t\t\treturn\n\t\t}\n\t}()\n\n\tc := cc()\n\tdefer c.Close()\n\n\tgo func() {\n\t\tif _, err := c.Write([]byte(\"ping\")); err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tbuf := make([]byte, 4)\n\t\tif _, err := io.ReadFull(c, buf); err != nil {\n\t\t\tt.Error(err)\n\t\t\treturn\n\t\t}\n\n\t\tpongCh <- buf\n\t}()\n\n\treturn test(t)\n}\n\nfunc testPingPongWithPacketConn(t *testing.T, pc net.PacketConn) error {\n\tl, err := ListenPacket(\"udp\", \":10001\")\n\trequire.NoError(t, err)\n\tdefer l.Close()\n\n\trAddr := &net.UDPAddr{IP: localIP.AsSlice(), Port: 10001}\n\n\tpingCh, pongCh, test := newPingPongPair()\n\tgo func() {\n\t\tbuf := make([]byte, 1024)\n\t\tn, rAddr, err := l.ReadFrom(buf)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tpingCh <- buf[:n]\n\t\tif _, err := l.WriteTo([]byte(\"pong\"), rAddr); err != nil {\n\t\t\treturn\n\t\t}\n\t}()\n\n\tgo func() {\n\t\tif _, err := pc.WriteTo([]byte(\"ping\"), rAddr); err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tbuf := make([]byte, 1024)\n\t\tn, _, err := pc.ReadFrom(buf)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tpongCh <- buf[:n]\n\t}()\n\n\treturn test(t)\n}\n\ntype hashPair struct {\n\tsendHash map[int][]byte\n\trecvHash map[int][]byte\n}\n\nfunc testLargeDataWithConn(t *testing.T, cc func() net.Conn) error {\n\tl, err := Listen(\"tcp\", \":10001\")\n\trequire.NoError(t, err)\n\tdefer l.Close()\n\n\ttimes := 100\n\tchunkSize := int64(64 * 1024)\n\n\tpingCh, pongCh, test := newLargeDataPair()\n\twriteRandData := func(conn net.Conn) (map[int][]byte, error) {\n\t\tbuf := make([]byte, chunkSize)\n\t\thashMap := map[int][]byte{}\n\t\tfor i := 0; i < times; i++ {\n\t\t\tif _, err := rand.Read(buf[1:]); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tbuf[0] = byte(i)\n\n\t\t\thash := md5.Sum(buf)\n\t\t\thashMap[i] = hash[:]\n\n\t\t\tif _, err := conn.Write(buf); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\n\t\treturn hashMap, nil\n\t}\n\n\tgo func() {\n\t\tc, err := l.Accept()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tdefer c.Close()\n\n\t\thashMap := map[int][]byte{}\n\t\tbuf := make([]byte, chunkSize)\n\n\t\tfor i := 0; i < times; i++ {\n\t\t\t_, err := io.ReadFull(c, buf)\n\t\t\tif err != nil {\n\t\t\t\tt.Log(err.Error())\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\thash := md5.Sum(buf)\n\t\t\thashMap[int(buf[0])] = hash[:]\n\t\t}\n\n\t\tsendHash, err := writeRandData(c)\n\t\tif err != nil {\n\t\t\tt.Log(err.Error())\n\t\t\treturn\n\t\t}\n\n\t\tpingCh <- hashPair{\n\t\t\tsendHash: sendHash,\n\t\t\trecvHash: hashMap,\n\t\t}\n\t}()\n\n\tc := cc()\n\tdefer c.Close()\n\n\tgo func() {\n\t\tsendHash, err := writeRandData(c)\n\t\tif err != nil {\n\t\t\tt.Log(err.Error())\n\t\t\treturn\n\t\t}\n\n\t\thashMap := map[int][]byte{}\n\t\tbuf := make([]byte, chunkSize)\n\n\t\tfor i := 0; i < times; i++ {\n\t\t\t_, err := io.ReadFull(c, buf)\n\t\t\tif err != nil {\n\t\t\t\tt.Log(err.Error())\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\thash := md5.Sum(buf)\n\t\t\thashMap[int(buf[0])] = hash[:]\n\t\t}\n\n\t\tpongCh <- hashPair{\n\t\t\tsendHash: sendHash,\n\t\t\trecvHash: hashMap,\n\t\t}\n\t}()\n\n\treturn test(t)\n}\n\nfunc testLargeDataWithPacketConn(t *testing.T, pc net.PacketConn) error {\n\tl, err := ListenPacket(\"udp\", \":10001\")\n\trequire.NoError(t, err)\n\tdefer l.Close()\n\n\trAddr := &net.UDPAddr{IP: localIP.AsSlice(), Port: 10001}\n\n\ttimes := 50\n\tchunkSize := int64(1024)\n\n\tpingCh, pongCh, test := newLargeDataPair()\n\twriteRandData := func(pc net.PacketConn, addr net.Addr) (map[int][]byte, error) {\n\t\thashMap := map[int][]byte{}\n\t\tmux := sync.Mutex{}\n\t\tgo func() {\n\t\t\tfor i := 0; i < times; i++ {\n\t\t\t\tbuf := make([]byte, chunkSize)\n\t\t\t\tif _, err := rand.Read(buf[1:]); err != nil {\n\t\t\t\t\tt.Log(err.Error())\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tbuf[0] = byte(i)\n\n\t\t\t\thash := md5.Sum(buf)\n\t\t\t\tmux.Lock()\n\t\t\t\thashMap[i] = hash[:]\n\t\t\t\tmux.Unlock()\n\n\t\t\t\tif _, err := pc.WriteTo(buf, addr); err != nil {\n\t\t\t\t\tt.Log(err.Error())\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\n\t\treturn hashMap, nil\n\t}\n\n\tgo func() {\n\t\tvar rAddr net.Addr\n\t\thashMap := map[int][]byte{}\n\t\tbuf := make([]byte, 64*1024)\n\n\t\tfor i := 0; i < times; i++ {\n\t\t\t_, rAddr, err = l.ReadFrom(buf)\n\t\t\tif err != nil {\n\t\t\t\tt.Log(err.Error())\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\thash := md5.Sum(buf[:chunkSize])\n\t\t\thashMap[int(buf[0])] = hash[:]\n\t\t}\n\n\t\tsendHash, err := writeRandData(l, rAddr)\n\t\tif err != nil {\n\t\t\tt.Log(err.Error())\n\t\t\treturn\n\t\t}\n\n\t\tpingCh <- hashPair{\n\t\t\tsendHash: sendHash,\n\t\t\trecvHash: hashMap,\n\t\t}\n\t}()\n\n\tgo func() {\n\t\tsendHash, err := writeRandData(pc, rAddr)\n\t\tif err != nil {\n\t\t\tt.Log(err.Error())\n\t\t\treturn\n\t\t}\n\n\t\thashMap := map[int][]byte{}\n\t\tbuf := make([]byte, 64*1024)\n\n\t\tfor i := 0; i < times; i++ {\n\t\t\t_, _, err := pc.ReadFrom(buf)\n\t\t\tif err != nil {\n\t\t\t\tt.Log(err.Error())\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\thash := md5.Sum(buf[:chunkSize])\n\t\t\thashMap[int(buf[0])] = hash[:]\n\t\t}\n\n\t\tpongCh <- hashPair{\n\t\t\tsendHash: sendHash,\n\t\t\trecvHash: hashMap,\n\t\t}\n\t}()\n\n\treturn test(t)\n}\n\nfunc testPacketConnTimeout(t *testing.T, pc net.PacketConn) error {\n\terr := pc.SetReadDeadline(time.Now().Add(time.Millisecond * 300))\n\trequire.NoError(t, err)\n\n\terrCh := make(chan error, 1)\n\tgo func() {\n\t\tbuf := make([]byte, 1024)\n\t\t_, _, err := pc.ReadFrom(buf)\n\t\terrCh <- err\n\t}()\n\n\tselect {\n\tcase <-errCh:\n\t\treturn nil\n\tcase <-time.After(time.Second * 10):\n\t\treturn errors.New(\"timeout\")\n\t}\n}\n\nfunc testSuit(t *testing.T, proxy C.ProxyAdapter) {\n\tassert.NoError(t, testPingPongWithConn(t, func() net.Conn {\n\t\tconn, err := proxy.DialContext(context.Background(), &C.Metadata{\n\t\t\tHost:    localIP.String(),\n\t\t\tDstPort: 10001,\n\t\t})\n\t\trequire.NoError(t, err)\n\t\treturn conn\n\t}))\n\n\tassert.NoError(t, testLargeDataWithConn(t, func() net.Conn {\n\t\tconn, err := proxy.DialContext(context.Background(), &C.Metadata{\n\t\t\tHost:    localIP.String(),\n\t\t\tDstPort: 10001,\n\t\t})\n\t\trequire.NoError(t, err)\n\t\treturn conn\n\t}))\n\n\tif !proxy.SupportUDP() {\n\t\treturn\n\t}\n\n\tpc, err := proxy.ListenPacketContext(context.Background(), &C.Metadata{\n\t\tNetWork: C.UDP,\n\t\tDstIP:   localIP,\n\t\tDstPort: 10001,\n\t})\n\trequire.NoError(t, err)\n\tdefer pc.Close()\n\n\tassert.NoError(t, testPingPongWithPacketConn(t, pc))\n\n\tpc, err = proxy.ListenPacketContext(context.Background(), &C.Metadata{\n\t\tNetWork: C.UDP,\n\t\tDstIP:   localIP,\n\t\tDstPort: 10001,\n\t})\n\trequire.NoError(t, err)\n\tdefer pc.Close()\n\n\tassert.NoError(t, testLargeDataWithPacketConn(t, pc))\n\n\tpc, err = proxy.ListenPacketContext(context.Background(), &C.Metadata{\n\t\tNetWork: C.UDP,\n\t\tDstIP:   localIP,\n\t\tDstPort: 10001,\n\t})\n\trequire.NoError(t, err)\n\tdefer pc.Close()\n\n\tassert.NoError(t, testPacketConnTimeout(t, pc))\n}\n\nfunc benchmarkProxy(b *testing.B, proxy C.ProxyAdapter) {\n\tl, err := Listen(\"tcp\", \":10001\")\n\trequire.NoError(b, err)\n\tdefer l.Close()\n\n\tchunkSize := int64(16 * 1024)\n\tchunk := make([]byte, chunkSize)\n\trand.Read(chunk)\n\n\tgo func() {\n\t\tc, err := l.Accept()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tdefer c.Close()\n\n\t\tgo func() {\n\t\t\tfor {\n\t\t\t\t_, err := c.Write(chunk)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t}()\n\t\tio.Copy(io.Discard, c)\n\t}()\n\n\tconn, err := proxy.DialContext(context.Background(), &C.Metadata{\n\t\tHost:    localIP.String(),\n\t\tDstPort: 10001,\n\t})\n\trequire.NoError(b, err)\n\n\t_, err = conn.Write([]byte(\"skip protocol handshake\"))\n\trequire.NoError(b, err)\n\n\tb.Run(\"Write\", func(b *testing.B) {\n\t\tb.SetBytes(chunkSize)\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\tconn.Write(chunk)\n\t\t}\n\t})\n\n\tb.Run(\"Read\", func(b *testing.B) {\n\t\tb.SetBytes(chunkSize)\n\t\tbuf := make([]byte, chunkSize)\n\t\tfor i := 0; i < b.N; i++ {\n\t\t\tio.ReadFull(conn, buf)\n\t\t}\n\t})\n}\n\nfunc TestMihomo_Basic(t *testing.T) {\n\tbasic := `\nmixed-port: 10000\nlog-level: silent\n`\n\n\terr := parseAndApply(basic)\n\trequire.NoError(t, err)\n\tdefer cleanup()\n\n\trequire.True(t, TCPing(net.JoinHostPort(localIP.String(), \"10000\")))\n\ttestPingPongWithSocksPort(t, 10000)\n}\n\nfunc Benchmark_Direct(b *testing.B) {\n\tproxy := outbound.NewDirect()\n\tbenchmarkProxy(b, proxy)\n}\n"
  },
  {
    "path": "core/Clash.Meta/test/config/example.org-key.pem",
    "content": "-----BEGIN PRIVATE KEY-----\nMIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQDQ+c++LkDTdaw5\n5spCu9MWMcvVdrYBZZ5qZy7DskphSUSQp25cIu34GJXVPNxtbWx1CQCmdLlwqXvo\nPfUt5/pz9qsfhdAbzFduZQgGd7GTQOTJBDrAhm2+iVsQyGHHhF68muN+SgT+AtRE\nsJyZoHNYtjjWEIHQ++FHEDqwUVnj6Ut99LHlyfCjOZ5+WyBiKCjyMNots/gDep7R\ni4X2kMTqNMIIqPUcAaP5EQk41bJbFhKe915qN9b1dRISKFKmiWeOsxgTB/O/EaL5\nLsBYwZ/BiIMDk30aZvzRJeloasIR3z4hrKQqBfB0lfeIdiPpJIs5rXJQEiWH89ge\ngplsLbfrAgMBAAECggEBAKpMGaZzDPMF/v8Ee6lcZM2+cMyZPALxa+JsCakCvyh+\ny7hSKVY+RM0cQ+YM/djTBkJtvrDniEMuasI803PAitI7nwJGSuyMXmehP6P9oKFO\njeLeZn6ETiSqzKJlmYE89vMeCevdqCnT5mW/wy5Smg0eGj0gIJpM2S3PJPSQpv9Z\nots0JXkwooJcpGWzlwPkjSouY2gDbE4Coi+jmYLNjA1k5RbggcutnUCZZkJ6yMNv\nH52VjnkffpAFHRouK/YgF+5nbMyyw5YTLOyTWBq7qfBMsXynkWLU73GC/xDZa3yG\no/Ph2knXCjgLmCRessTOObdOXedjnGWIjiqF8fVboDECgYEA6x5CteYiwthDBULZ\nCG5nE9VKkRHJYdArm+VjmGbzK51tKli112avmU4r3ol907+mEa4tWLkPqdZrrL49\naHltuHizZJixJcw0rcI302ot/Ov0gkF9V55gnAQS/Kemvx9FHWm5NHdYvbObzj33\nbYRLJBtJWzYg9M8Bw9ZrUnegc/MCgYEA44kq5OSYCbyu3eaX8XHTtFhuQHNFjwl7\nXk/Oel6PVZzmt+oOlDHnOfGSB/KpR3YXxFRngiiPZzbrOwFyPGe7HIfg03HAXiJh\nivEfrPHbQqQUI/4b44GpDy6bhNtz777ivFGYEt21vpwd89rFiye+RkqF8eL/evxO\npUayDZYvwikCgYEA07wFoZ/lkAiHmpZPsxsRcrfzFd+pto9splEWtumHdbCo3ajT\n4W5VFr9iHF8/VFDT8jokFjFaXL1/bCpKTOqFl8oC68XiSkKy8gPkmFyXm5y2LhNi\nGGTFZdr5alRkgttbN5i9M/WCkhvMZRhC2Xp43MRB9IUzeqNtWHqhXbvjYGcCgYEA\nvTMOztviLJ6PjYa0K5lp31l0+/SeD21j/y0/VPOSHi9kjeN7EfFZAw6DTkaSShDB\nfIhutYVCkSHSgfMW6XGb3gKCiW/Z9KyEDYOowicuGgDTmoYu7IOhbzVjLhtJET7Z\nzJvQZ0eiW4f3RBFTF/4JMuu+6z7FD6ADSV06qx+KQNkCgYBw26iQxmT5e/4kVv8X\nDzBJ1HuliKBnnzZA1YRjB4H8F6Yrq+9qur1Lurez4YlbkGV8yPFt+Iu82ViUWL28\n9T7Jgp3TOpf8qOqsWFv8HldpEZbE0Tcib4x6s+zOg/aw0ac/xOPY1sCVFB81VODP\nXCar+uxMBXI1zbXqd9QdEwy4Ig==\n-----END PRIVATE KEY-----\n"
  },
  {
    "path": "core/Clash.Meta/test/config/example.org.pem",
    "content": "-----BEGIN CERTIFICATE-----\nMIIESzCCArOgAwIBAgIQIi5xRZvFZaSweWU9Y5mExjANBgkqhkiG9w0BAQsFADCB\nhzEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMS4wLAYDVQQLDCVkcmVh\nbWFjcm9ARHJlYW1hY3JvLmxvY2FsIChEcmVhbWFjcm8pMTUwMwYDVQQDDCxta2Nl\ncnQgZHJlYW1hY3JvQERyZWFtYWNyby5sb2NhbCAoRHJlYW1hY3JvKTAeFw0yMTAz\nMTcxNDQwMzZaFw0yMzA2MTcxNDQwMzZaMFkxJzAlBgNVBAoTHm1rY2VydCBkZXZl\nbG9wbWVudCBjZXJ0aWZpY2F0ZTEuMCwGA1UECwwlZHJlYW1hY3JvQERyZWFtYWNy\nby5sb2NhbCAoRHJlYW1hY3JvKTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoC\nggEBAND5z74uQNN1rDnmykK70xYxy9V2tgFlnmpnLsOySmFJRJCnblwi7fgYldU8\n3G1tbHUJAKZ0uXCpe+g99S3n+nP2qx+F0BvMV25lCAZ3sZNA5MkEOsCGbb6JWxDI\nYceEXrya435KBP4C1ESwnJmgc1i2ONYQgdD74UcQOrBRWePpS330seXJ8KM5nn5b\nIGIoKPIw2i2z+AN6ntGLhfaQxOo0wgio9RwBo/kRCTjVslsWEp73Xmo31vV1EhIo\nUqaJZ46zGBMH878RovkuwFjBn8GIgwOTfRpm/NEl6WhqwhHfPiGspCoF8HSV94h2\nI+kkizmtclASJYfz2B6CmWwtt+sCAwEAAaNgMF4wDgYDVR0PAQH/BAQDAgWgMBMG\nA1UdJQQMMAoGCCsGAQUFBwMBMB8GA1UdIwQYMBaAFO800LQ6Pa85RH4EbMmFH6ln\nF150MBYGA1UdEQQPMA2CC2V4YW1wbGUub3JnMA0GCSqGSIb3DQEBCwUAA4IBgQAP\nTsF53h7bvJcUXT3Y9yZ2vnW6xr9r92tNnM1Gfo3D2Yyn9oLf2YrfJng6WZ04Fhqa\nWh0HOvE0n6yPNpm/Q7mh64DrgolZ8Ce5H4RTJDAabHU9XhEzfGSVtzRSFsz+szu1\nY30IV+08DxxqMmNPspYdpAET2Lwyk2WhnARGiGw11CRkQCEkVEe6d702vS9UGBUz\nDu6lmCYCm0SbFrZ0CGgmHSHoTcCtf3EjVam7dPg3yWiPbWjvhXxgip6hz9sCqkhG\nWA5f+fPgSZ1I9U4i+uYnqjfrzwgC08RwUYordm15F6gPvXw+KVwDO8yUYQoEH0b6\nAFJtbzoAXDysvBC6kWYFFOr62EaisaEkELTS/NrPD9ux1eKbxcxHCwEtVjgC0CL6\ngAxEAQ+9maJMbrAFhsOBbGGFC+mMCGg4eEyx6+iMB0oQe0W7QFeRUAFi7Ptc/ocS\ntZ9lbrfX1/wrcTTWIYWE+xH6oeb4fhs29kxjHcf2l+tQzmpl0aP3Z/bMW4BSB+w=\n-----END CERTIFICATE-----\n"
  },
  {
    "path": "core/Clash.Meta/test/config/hysteria.json",
    "content": "{\n  \"listen\": \":10002\",\n  \"cert\": \"/home/ubuntu/my.crt\",\n  \"key\": \"/home/ubuntu/my.key\",\n  \"obfs\": \"fuck me till the daylight\",\n  \"up_mbps\": 100,\n  \"down_mbps\": 100\n}"
  },
  {
    "path": "core/Clash.Meta/test/config/snell-http.conf",
    "content": "[snell-server]\nlisten = 0.0.0.0:10002\npsk = password\nobfs = http\n"
  },
  {
    "path": "core/Clash.Meta/test/config/snell-tls.conf",
    "content": "[snell-server]\nlisten = 0.0.0.0:10002\npsk = password\nobfs = tls\n"
  },
  {
    "path": "core/Clash.Meta/test/config/snell.conf",
    "content": "[snell-server]\nlisten = 0.0.0.0:10002\npsk = password\n"
  },
  {
    "path": "core/Clash.Meta/test/config/trojan-grpc.json",
    "content": "{\n    \"inbounds\": [\n        {\n            \"port\": 10002,\n            \"listen\": \"0.0.0.0\",\n            \"protocol\": \"trojan\",\n            \"settings\": {\n                \"clients\": [\n                    {\n                        \"password\": \"example\",\n                        \"email\": \"grpc@example.com\"\n                    }\n                ]\n            },\n            \"streamSettings\": {\n                \"network\": \"grpc\",\n                \"security\": \"tls\",\n                \"tlsSettings\": {\n                    \"certificates\": [\n                        {\n                            \"certificateFile\": \"/etc/ssl/v2ray/fullchain.pem\",\n                            \"keyFile\": \"/etc/ssl/v2ray/privkey.pem\"\n                        }\n                    ]\n                },\n                \"grpcSettings\": {\n                    \"serviceName\": \"example\"\n                }\n            }\n        }\n    ],\n    \"outbounds\": [\n        {\n            \"protocol\": \"freedom\"\n        }\n    ],\n    \"log\": {\n        \"loglevel\": \"debug\"\n    }\n}"
  },
  {
    "path": "core/Clash.Meta/test/config/trojan-ws.json",
    "content": "{\n    \"run_type\": \"server\",\n    \"local_addr\": \"0.0.0.0\",\n    \"local_port\": 10002,\n    \"disable_http_check\": true,\n    \"password\": [\n        \"example\"\n    ],\n    \"websocket\": {\n        \"enabled\": true,\n        \"path\": \"/\",\n        \"host\": \"example.org\"\n    },\n    \"ssl\": {\n        \"verify\": true,\n        \"cert\": \"/fullchain.pem\",\n        \"key\": \"/privkey.pem\",\n        \"sni\": \"example.org\"\n    }\n}"
  },
  {
    "path": "core/Clash.Meta/test/config/trojan-xtls.json",
    "content": "{\n    \"inbounds\": [\n        {\n            \"port\": 10002,\n            \"listen\": \"0.0.0.0\",\n            \"protocol\": \"trojan\",\n            \"settings\": {\n                \"clients\": [\n                    {\n                        \"password\": \"example\",\n                        \"email\": \"xtls@example.com\",\n                        \"flow\": \"xtls-rprx-direct\",\n                        \"level\": 0\n                    }\n                ]\n            },\n            \"streamSettings\": {\n                \"network\": \"tcp\",\n                \"security\": \"xtls\",\n                \"xtlsSettings\": {\n                    \"certificates\": [\n                        {\n                            \"certificateFile\": \"/etc/ssl/v2ray/fullchain.pem\",\n                            \"keyFile\": \"/etc/ssl/v2ray/privkey.pem\"\n                        }\n                    ]\n                }\n            }\n        }\n    ],\n    \"outbounds\": [\n        {\n            \"protocol\": \"freedom\"\n        }\n    ],\n    \"log\": {\n        \"loglevel\": \"debug\"\n    }\n}"
  },
  {
    "path": "core/Clash.Meta/test/config/trojan.json",
    "content": "{\n    \"run_type\": \"server\",\n    \"local_addr\": \"0.0.0.0\",\n    \"local_port\": 10002,\n    \"password\": [\n        \"password\"\n    ],\n    \"log_level\": 1,\n    \"ssl\": {\n        \"cert\": \"/path/to/certificate.crt\",\n        \"key\": \"/path/to/private.key\",\n        \"key_password\": \"\",\n        \"cipher\": \"ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384\",\n        \"cipher_tls13\": \"TLS_AES_128_GCM_SHA256:TLS_CHACHA20_POLY1305_SHA256:TLS_AES_256_GCM_SHA384\",\n        \"prefer_server_cipher\": true,\n        \"alpn\": [\n            \"http/1.1\"\n        ],\n        \"alpn_port_override\": {\n            \"h2\": 81\n        },\n        \"reuse_session\": true,\n        \"session_ticket\": false,\n        \"session_timeout\": 600,\n        \"plain_http_response\": \"\",\n        \"curves\": \"\",\n        \"dhparam\": \"\"\n    },\n    \"tcp\": {\n        \"prefer_ipv4\": false,\n        \"no_delay\": true,\n        \"keep_alive\": true,\n        \"reuse_port\": false,\n        \"fast_open\": false,\n        \"fast_open_qlen\": 20\n    },\n    \"mysql\": {\n        \"enabled\": false\n    }\n}"
  },
  {
    "path": "core/Clash.Meta/test/config/vless-tls.json",
    "content": "{\n    \"inbounds\": [\n        {\n            \"port\": 10002,\n            \"listen\": \"0.0.0.0\",\n            \"protocol\": \"vless\",\n            \"settings\": {\n                \"clients\": [\n                    {\n                        \"id\": \"b831381d-6324-4d53-ad4f-8cda48b30811\",\n                        \"level\": 0,\n                        \"email\": \"love@example.com\"\n                    }\n                ],\n                \"decryption\": \"none\"\n            },\n            \"streamSettings\": {\n                \"network\": \"tcp\",\n                \"security\": \"tls\",\n                \"tlsSettings\": {\n                    \"certificates\": [\n                        {\n                            \"certificateFile\": \"/etc/ssl/v2ray/fullchain.pem\",\n                            \"keyFile\": \"/etc/ssl/v2ray/privkey.pem\"\n                        }\n                    ]\n                }\n            }\n        }\n    ],\n    \"outbounds\": [\n        {\n            \"protocol\": \"freedom\"\n        }\n    ],\n    \"log\": {\n        \"loglevel\": \"debug\"\n    }\n}"
  },
  {
    "path": "core/Clash.Meta/test/config/vless-ws.json",
    "content": "{\n    \"inbounds\": [\n        {\n            \"port\": 10002,\n            \"listen\": \"0.0.0.0\",\n            \"protocol\": \"vless\",\n            \"settings\": {\n                \"clients\": [\n                    {\n                        \"id\": \"b831381d-6324-4d53-ad4f-8cda48b30811\",\n                        \"level\": 0,\n                        \"email\": \"ws@example.com\"\n                    }\n                ],\n                \"decryption\": \"none\"\n            },\n            \"streamSettings\": {\n                \"network\": \"ws\",\n                \"security\": \"tls\",\n                \"tlsSettings\": {\n                    \"certificates\": [\n                        {\n                            \"certificateFile\": \"/etc/ssl/v2ray/fullchain.pem\",\n                            \"keyFile\": \"/etc/ssl/v2ray/privkey.pem\"\n                        }\n                    ]\n                }\n            }\n        }\n    ],\n    \"outbounds\": [\n        {\n            \"protocol\": \"freedom\"\n        }\n    ]\n}"
  },
  {
    "path": "core/Clash.Meta/test/config/vless-xtls.json",
    "content": "{\n    \"inbounds\": [\n        {\n            \"port\": 10002,\n            \"listen\": \"0.0.0.0\",\n            \"protocol\": \"vless\",\n            \"settings\": {\n                \"clients\": [\n                    {\n                        \"id\": \"b831381d-6324-4d53-ad4f-8cda48b30811\",\n                        \"email\": \"xtls@example.com\",\n                        \"flow\": \"xtls-rprx-direct\",\n                        \"level\": 0\n                    }\n                ],\n                \"decryption\": \"none\"\n            },\n            \"streamSettings\": {\n                \"network\": \"tcp\",\n                \"security\": \"xtls\",\n                \"xtlsSettings\": {\n                    \"certificates\": [\n                        {\n                            \"certificateFile\": \"/etc/ssl/v2ray/fullchain.pem\",\n                            \"keyFile\": \"/etc/ssl/v2ray/privkey.pem\"\n                        }\n                    ]\n                }\n            }\n        }\n    ],\n    \"outbounds\": [\n        {\n            \"protocol\": \"freedom\"\n        }\n    ],\n    \"log\": {\n        \"loglevel\": \"debug\"\n    }\n}"
  },
  {
    "path": "core/Clash.Meta/test/config/vmess-grpc.json",
    "content": "{\n    \"inbounds\": [\n        {\n            \"port\": 10002,\n            \"listen\": \"0.0.0.0\",\n            \"protocol\": \"vmess\",\n            \"settings\": {\n                \"clients\": [\n                    {\n                        \"id\": \"b831381d-6324-4d53-ad4f-8cda48b30811\"\n                    }\n                ]\n            },\n            \"streamSettings\": {\n                \"network\": \"grpc\",\n                \"security\": \"tls\",\n                \"tlsSettings\": {\n                    \"certificates\": [\n                        {\n                            \"certificateFile\": \"/etc/ssl/v2ray/fullchain.pem\",\n                            \"keyFile\": \"/etc/ssl/v2ray/privkey.pem\"\n                        }\n                    ]\n                },\n                \"grpcSettings\": {\n                    \"serviceName\": \"example!\"\n                }\n            }\n        }\n    ],\n    \"outbounds\": [\n        {\n            \"protocol\": \"freedom\"\n        }\n    ],\n    \"log\": {\n        \"loglevel\": \"debug\"\n    }\n}"
  },
  {
    "path": "core/Clash.Meta/test/config/vmess-http.json",
    "content": "{\n    \"inbounds\": [\n        {\n            \"port\": 10002,\n            \"listen\": \"0.0.0.0\",\n            \"protocol\": \"vmess\",\n            \"settings\": {\n                \"clients\": [\n                    {\n                        \"id\": \"b831381d-6324-4d53-ad4f-8cda48b30811\"\n                    }\n                ]\n            },\n            \"streamSettings\": {\n                \"network\": \"tcp\",\n                \"tcpSettings\": {\n                    \"header\": {\n                        \"type\": \"http\",\n                        \"response\": {\n                            \"version\": \"1.1\",\n                            \"status\": \"200\",\n                            \"reason\": \"OK\",\n                            \"headers\": {\n                                \"Content-Type\": [\n                                    \"application/octet-stream\",\n                                    \"video/mpeg\",\n                                    \"application/x-msdownload\",\n                                    \"text/html\",\n                                    \"application/x-shockwave-flash\"\n                                ],\n                                \"Transfer-Encoding\": [\n                                    \"chunked\"\n                                ],\n                                \"Connection\": [\n                                    \"keep-alive\"\n                                ],\n                                \"Pragma\": \"no-cache\"\n                            }\n                        }\n                    }\n                },\n                \"security\": \"none\"\n            }\n        }\n    ],\n    \"outbounds\": [\n        {\n            \"protocol\": \"freedom\"\n        }\n    ],\n    \"log\": {\n        \"loglevel\": \"debug\"\n    }\n}"
  },
  {
    "path": "core/Clash.Meta/test/config/vmess-http2.json",
    "content": "{\n    \"inbounds\": [\n        {\n            \"port\": 10002,\n            \"listen\": \"0.0.0.0\",\n            \"protocol\": \"vmess\",\n            \"settings\": {\n                \"clients\": [\n                    {\n                        \"id\": \"b831381d-6324-4d53-ad4f-8cda48b30811\"\n                    }\n                ]\n            },\n            \"streamSettings\": {\n                \"network\": \"http\",\n                \"security\": \"tls\",\n                \"tlsSettings\": {\n                    \"certificates\": [\n                        {\n                            \"certificateFile\": \"/etc/ssl/v2ray/fullchain.pem\",\n                            \"keyFile\": \"/etc/ssl/v2ray/privkey.pem\"\n                        }\n                    ]\n                },\n                \"httpSettings\": {\n                    \"host\": [\n                        \"example.org\"\n                    ],\n                    \"path\": \"/test\"\n                }\n            }\n        }\n    ],\n    \"outbounds\": [\n        {\n            \"protocol\": \"freedom\"\n        }\n    ],\n    \"log\": {\n        \"loglevel\": \"debug\"\n    }\n}"
  },
  {
    "path": "core/Clash.Meta/test/config/vmess-tls.json",
    "content": "{\n    \"inbounds\": [\n        {\n            \"port\": 10002,\n            \"listen\": \"0.0.0.0\",\n            \"protocol\": \"vmess\",\n            \"settings\": {\n                \"clients\": [\n                    {\n                        \"id\": \"b831381d-6324-4d53-ad4f-8cda48b30811\"\n                    }\n                ]\n            },\n            \"streamSettings\": {\n                \"network\": \"tcp\",\n                \"security\": \"tls\",\n                \"tlsSettings\": {\n                    \"certificates\": [\n                        {\n                            \"certificateFile\": \"/etc/ssl/v2ray/fullchain.pem\",\n                            \"keyFile\": \"/etc/ssl/v2ray/privkey.pem\"\n                        }\n                    ]\n                }\n            }\n        }\n    ],\n    \"outbounds\": [\n        {\n            \"protocol\": \"freedom\"\n        }\n    ],\n    \"log\": {\n        \"loglevel\": \"debug\"\n    }\n}"
  },
  {
    "path": "core/Clash.Meta/test/config/vmess-ws-0rtt.json",
    "content": "{\n    \"inbounds\": [\n        {\n            \"port\": 10002,\n            \"listen\": \"0.0.0.0\",\n            \"protocol\": \"vmess\",\n            \"settings\": {\n                \"clients\": [\n                    {\n                        \"id\": \"b831381d-6324-4d53-ad4f-8cda48b30811\"\n                    }\n                ]\n            },\n            \"streamSettings\": {\n                \"network\": \"ws\",\n                \"security\": \"none\",\n                \"wsSettings\": {\n                    \"maxEarlyData\": 128,\n                    \"earlyDataHeaderName\": \"Sec-WebSocket-Protocol\"\n                }\n            }\n        }\n    ],\n    \"outbounds\": [\n        {\n            \"protocol\": \"freedom\"\n        }\n    ]\n}"
  },
  {
    "path": "core/Clash.Meta/test/config/vmess-ws-tls.json",
    "content": "{\n    \"inbounds\": [\n        {\n            \"port\": 10002,\n            \"listen\": \"0.0.0.0\",\n            \"protocol\": \"vmess\",\n            \"settings\": {\n                \"clients\": [\n                    {\n                        \"id\": \"b831381d-6324-4d53-ad4f-8cda48b30811\"\n                    }\n                ]\n            },\n            \"streamSettings\": {\n                \"network\": \"ws\",\n                \"security\": \"tls\",\n                \"tlsSettings\": {\n                    \"certificates\": [\n                        {\n                            \"certificateFile\": \"/etc/ssl/v2ray/fullchain.pem\",\n                            \"keyFile\": \"/etc/ssl/v2ray/privkey.pem\"\n                        }\n                    ]\n                }\n            }\n        }\n    ],\n    \"outbounds\": [\n        {\n            \"protocol\": \"freedom\"\n        }\n    ]\n}"
  },
  {
    "path": "core/Clash.Meta/test/config/vmess-ws.json",
    "content": "{\n    \"inbounds\": [\n        {\n            \"port\": 10002,\n            \"listen\": \"0.0.0.0\",\n            \"protocol\": \"vmess\",\n            \"settings\": {\n                \"clients\": [\n                    {\n                        \"id\": \"b831381d-6324-4d53-ad4f-8cda48b30811\"\n                    }\n                ]\n            },\n            \"streamSettings\": {\n                \"network\": \"ws\",\n                \"security\": \"none\"\n            }\n        }\n    ],\n    \"outbounds\": [\n        {\n            \"protocol\": \"freedom\"\n        }\n    ]\n}"
  },
  {
    "path": "core/Clash.Meta/test/config/vmess.json",
    "content": "{\n    \"inbounds\": [\n        {\n            \"port\": 10002,\n            \"listen\": \"0.0.0.0\",\n            \"protocol\": \"vmess\",\n            \"settings\": {\n                \"clients\": [\n                    {\n                        \"id\": \"b831381d-6324-4d53-ad4f-8cda48b30811\"\n                    }\n                ]\n            },\n            \"streamSettings\": {\n                \"network\": \"tcp\"\n            }\n        }\n    ],\n    \"outbounds\": [\n        {\n            \"protocol\": \"freedom\"\n        }\n    ],\n    \"log\": {\n        \"loglevel\": \"debug\"\n    }\n}"
  },
  {
    "path": "core/Clash.Meta/test/config/xray-shadowsocks.json",
    "content": "{\n    \"inbounds\": [\n        {\n            \"port\": 10002,\n            \"listen\": \"0.0.0.0\",\n            \"protocol\": \"shadowsocks\",\n            \"settings\": {\n                \"network\": \"tcp,udp\",\n                \"clients\": [\n                    {\n                        \"method\": \"aes-128-gcm\",\n                        \"level\": 0,\n                        \"password\": \"FzcLbKs2dY9mhL\"\n                    }\n                ]\n            }\n        }\n    ],\n    \"outbounds\": [\n        {\n            \"protocol\": \"freedom\"\n        }\n    ],\n    \"log\": {\n        \"loglevel\": \"debug\"\n    }\n}"
  },
  {
    "path": "core/Clash.Meta/test/dns_test.go",
    "content": "package main\n\nimport (\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/miekg/dns\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc exchange(address, domain string, tp uint16) ([]dns.RR, error) {\n\tclient := dns.Client{}\n\tquery := &dns.Msg{}\n\tquery.SetQuestion(dns.Fqdn(domain), tp)\n\n\tr, _, err := client.Exchange(query, address)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn r.Answer, nil\n}\n\nfunc TestMihomo_DNS(t *testing.T) {\n\tbasic := `\nlog-level: silent\ndns:\n  enable: true\n  listen: 0.0.0.0:8553\n  nameserver:\n    - 119.29.29.29\n`\n\n\terr := parseAndApply(basic)\n\trequire.NoError(t, err)\n\tdefer cleanup()\n\n\ttime.Sleep(waitTime)\n\n\trr, err := exchange(\"127.0.0.1:8553\", \"1.1.1.1.nip.io\", dns.TypeA)\n\tassert.NoError(t, err)\n\tassert.NotEmptyf(t, rr, \"record empty\")\n\n\trecord := rr[0].(*dns.A)\n\tassert.Equal(t, record.A.String(), \"1.1.1.1\")\n\n\trr, err = exchange(\"127.0.0.1:8553\", \"2606-4700-4700--1111.sslip.io\", dns.TypeAAAA)\n\tassert.NoError(t, err)\n\tassert.Empty(t, rr)\n}\n\nfunc TestMihomo_DNSHostAndFakeIP(t *testing.T) {\n\tbasic := `\nlog-level: silent\nhosts:\n  foo.mihomo.dev: 1.1.1.1\ndns:\n  enable: true\n  listen: 0.0.0.0:8553\n  ipv6: true\n  enhanced-mode: fake-ip\n  fake-ip-range: 198.18.0.1/16\n  fake-ip-filter:\n    - .sslip.io\n  nameserver:\n    - 119.29.29.29\n`\n\n\terr := parseAndApply(basic)\n\trequire.NoError(t, err)\n\tdefer cleanup()\n\n\ttime.Sleep(waitTime)\n\n\ttype domainPair struct {\n\t\tdomain string\n\t\tip     string\n\t}\n\n\tlist := []domainPair{\n\t\t{\"foo.org\", \"198.18.0.4\"},\n\t\t{\"bar.org\", \"198.18.0.5\"},\n\t\t{\"foo.org\", \"198.18.0.4\"},\n\t\t{\"foo.mihomo.dev\", \"1.1.1.1\"},\n\t}\n\n\tfor _, pair := range list {\n\t\trr, err := exchange(\"127.0.0.1:8553\", pair.domain, dns.TypeA)\n\t\tassert.NoError(t, err)\n\t\tassert.NotEmpty(t, rr)\n\n\t\trecord := rr[0].(*dns.A)\n\t\tassert.Equal(t, record.A.String(), pair.ip)\n\t}\n\n\trr, err := exchange(\"127.0.0.1:8553\", \"2606-4700-4700--1111.sslip.io\", dns.TypeAAAA)\n\tassert.NoError(t, err)\n\tassert.NotEmpty(t, rr)\n\tassert.Equal(t, rr[0].(*dns.AAAA).AAAA.String(), \"2606:4700:4700::1111\")\n}\n"
  },
  {
    "path": "core/Clash.Meta/test/docker_test.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"os\"\n\n\t\"github.com/docker/docker/api/types\"\n\t\"github.com/docker/docker/api/types/container\"\n\t\"github.com/docker/docker/client\"\n)\n\nfunc startContainer(cfg *container.Config, hostCfg *container.HostConfig, name string) (string, error) {\n\tc, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tdefer c.Close()\n\n\tif !isDarwin {\n\t\thostCfg.NetworkMode = \"host\"\n\t}\n\n\tcontainer, err := c.ContainerCreate(context.Background(), cfg, hostCfg, nil, nil, name)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tif err = c.ContainerStart(context.Background(), container.ID, types.ContainerStartOptions{}); err != nil {\n\t\treturn \"\", err\n\t}\n\n\tresponse, err := c.ContainerAttach(context.Background(), container.ID, types.ContainerAttachOptions{\n\t\tStdout: true,\n\t\tStderr: true,\n\t\tLogs:   true,\n\t})\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\tgo func() {\n\t\tresponse.Reader.WriteTo(os.Stderr)\n\t}()\n\n\treturn container.ID, nil\n}\n\nfunc cleanContainer(id string) error {\n\tc, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer c.Close()\n\n\tremoveOpts := types.ContainerRemoveOptions{Force: true}\n\treturn c.ContainerRemove(context.Background(), id, removeOpts)\n}\n"
  },
  {
    "path": "core/Clash.Meta/test/go.mod",
    "content": "module mihomo-test\n\ngo 1.20\n\nrequire (\n\tgithub.com/docker/docker v20.10.21+incompatible\n\tgithub.com/docker/go-connections v0.4.0\n\tgithub.com/metacubex/mihomo v0.0.0\n\tgithub.com/miekg/dns v1.1.57\n\tgithub.com/stretchr/testify v1.8.4\n\tgolang.org/x/net v0.18.0\n)\n\nreplace github.com/metacubex/mihomo => ../\n\nrequire (\n\tgithub.com/3andne/restls-client-go v0.1.6 // indirect\n\tgithub.com/Microsoft/go-winio v0.6.0 // indirect\n\tgithub.com/RyuaNerin/go-krypto v1.0.2 // indirect\n\tgithub.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 // indirect\n\tgithub.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da // indirect\n\tgithub.com/andybalholm/brotli v1.0.5 // indirect\n\tgithub.com/bahlo/generic-list-go v0.2.0 // indirect\n\tgithub.com/buger/jsonparser v1.1.1 // indirect\n\tgithub.com/coreos/go-iptables v0.7.0 // indirect\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/distribution/reference v0.5.0 // indirect\n\tgithub.com/dlclark/regexp2 v1.10.0 // indirect\n\tgithub.com/docker/distribution v2.8.3+incompatible // indirect\n\tgithub.com/docker/go-units v0.4.0 // indirect\n\tgithub.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 // indirect\n\tgithub.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 // indirect\n\tgithub.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 // indirect\n\tgithub.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 // indirect\n\tgithub.com/fsnotify/fsnotify v1.7.0 // indirect\n\tgithub.com/gaukas/godicttls v0.0.4 // indirect\n\tgithub.com/go-ole/go-ole v1.3.0 // indirect\n\tgithub.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect\n\tgithub.com/gobwas/httphead v0.1.0 // indirect\n\tgithub.com/gobwas/pool v0.2.1 // indirect\n\tgithub.com/gobwas/ws v1.3.1 // indirect\n\tgithub.com/gofrs/uuid/v5 v5.0.0 // indirect\n\tgithub.com/gogo/protobuf v1.3.2 // indirect\n\tgithub.com/google/btree v1.1.2 // indirect\n\tgithub.com/google/go-cmp v0.6.0 // indirect\n\tgithub.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 // indirect\n\tgithub.com/hashicorp/yamux v0.1.1 // indirect\n\tgithub.com/insomniacslk/dhcp v0.0.0-20231016090811-6a2c8fbdcc1c // indirect\n\tgithub.com/josharian/native v1.1.0 // indirect\n\tgithub.com/jpillora/backoff v1.0.0 // indirect\n\tgithub.com/klauspost/compress v1.16.7 // indirect\n\tgithub.com/klauspost/cpuid/v2 v2.2.6 // indirect\n\tgithub.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect\n\tgithub.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 // indirect\n\tgithub.com/mailru/easyjson v0.7.7 // indirect\n\tgithub.com/mdlayher/netlink v1.7.2 // indirect\n\tgithub.com/mdlayher/socket v0.4.1 // indirect\n\tgithub.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 // indirect\n\tgithub.com/metacubex/gvisor v0.0.0-20231001104248-0f672c3fb8d8 // indirect\n\tgithub.com/metacubex/quic-go v0.40.1-0.20231130135418-0c1b47cf9394 // indirect\n\tgithub.com/metacubex/sing-quic v0.0.0-20231130141855-0022295e524b // indirect\n\tgithub.com/metacubex/sing-shadowsocks v0.2.5 // indirect\n\tgithub.com/metacubex/sing-shadowsocks2 v0.1.4 // indirect\n\tgithub.com/metacubex/sing-tun v0.1.15-0.20231103033938-170591e8d5bd // indirect\n\tgithub.com/metacubex/sing-vmess v0.1.9-0.20230921005247-a0488d7dac74 // indirect\n\tgithub.com/metacubex/sing-wireguard v0.0.0-20231001110902-321836559170 // indirect\n\tgithub.com/moby/term v0.5.0 // indirect\n\tgithub.com/morikuni/aec v1.0.0 // indirect\n\tgithub.com/mroth/weightedrand/v2 v2.1.0 // indirect\n\tgithub.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect\n\tgithub.com/onsi/ginkgo/v2 v2.9.5 // indirect\n\tgithub.com/openacid/low v0.1.21 // indirect\n\tgithub.com/opencontainers/go-digest v1.0.0 // indirect\n\tgithub.com/opencontainers/image-spec v1.0.2 // indirect\n\tgithub.com/oschwald/maxminddb-golang v1.12.0 // indirect\n\tgithub.com/pierrec/lz4/v4 v4.1.14 // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgithub.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect\n\tgithub.com/puzpuzpuz/xsync/v3 v3.0.2 // indirect\n\tgithub.com/quic-go/qpack v0.4.0 // indirect\n\tgithub.com/quic-go/qtls-go1-20 v0.4.1 // indirect\n\tgithub.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a // indirect\n\tgithub.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 // indirect\n\tgithub.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 // indirect\n\tgithub.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c // indirect\n\tgithub.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07 // indirect\n\tgithub.com/sagernet/sing-shadowtls v0.1.4 // indirect\n\tgithub.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 // indirect\n\tgithub.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 // indirect\n\tgithub.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 // indirect\n\tgithub.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f // indirect\n\tgithub.com/samber/lo v1.38.1 // indirect\n\tgithub.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 // indirect\n\tgithub.com/shirou/gopsutil/v3 v3.23.10 // indirect\n\tgithub.com/shoenig/go-m1cpu v0.1.6 // indirect\n\tgithub.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect\n\tgithub.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect\n\tgithub.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e // indirect\n\tgithub.com/sirupsen/logrus v1.9.3 // indirect\n\tgithub.com/tklauser/go-sysconf v0.3.12 // indirect\n\tgithub.com/tklauser/numcpus v0.6.1 // indirect\n\tgithub.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect\n\tgithub.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 // indirect\n\tgithub.com/wk8/go-ordered-map/v2 v2.1.8 // indirect\n\tgithub.com/yusufpapurcu/wmi v1.2.3 // indirect\n\tgithub.com/zhangyunhao116/fastrand v0.3.0 // indirect\n\tgitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect\n\tgo.uber.org/mock v0.3.0 // indirect\n\tgo4.org/netipx v0.0.0-20230824141953-6213f710f925 // indirect\n\tgolang.org/x/crypto v0.16.0 // indirect\n\tgolang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect\n\tgolang.org/x/mod v0.14.0 // indirect\n\tgolang.org/x/sync v0.5.0 // indirect\n\tgolang.org/x/sys v0.15.0 // indirect\n\tgolang.org/x/text v0.14.0 // indirect\n\tgolang.org/x/time v0.3.0 // indirect\n\tgolang.org/x/tools v0.15.0 // indirect\n\tgoogle.golang.org/protobuf v1.31.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n\tlukechampine.com/blake3 v1.2.1 // indirect\n)\n"
  },
  {
    "path": "core/Clash.Meta/test/go.sum",
    "content": "github.com/3andne/restls-client-go v0.1.6 h1:tRx/YilqW7iHpgmEL4E1D8dAsuB0tFF3uvncS+B6I08=\ngithub.com/3andne/restls-client-go v0.1.6/go.mod h1:iEdTZNt9kzPIxjIGSMScUFSBrUH6bFRNg0BWlP4orEY=\ngithub.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8=\ngithub.com/Microsoft/go-winio v0.6.0 h1:slsWYD/zyx7lCXoZVlvQrj0hPTM1HI4+v1sIda2yDvg=\ngithub.com/Microsoft/go-winio v0.6.0/go.mod h1:cTAf44im0RAYeL23bpB+fzCyDH2MJiz2BO69KH/soAE=\ngithub.com/RyuaNerin/go-krypto v1.0.2 h1:9KiZrrBs+tDrQ66dNy4nrX6SzntKtSKdm0wKHhdB4WM=\ngithub.com/RyuaNerin/go-krypto v1.0.2/go.mod h1:17LzMeJCgzGTkPH3TmfzRnEJ/yA7ErhTPp9sxIqONtA=\ngithub.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 h1:cDVUiFo+npB0ZASqnw4q90ylaVAbnYyx0JYqK4YcGok=\ngithub.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344/go.mod h1:9pIqrY6SXNL8vjRQE5Hd/OL5GyK/9MrGUWs87z/eFfk=\ngithub.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da h1:KjTM2ks9d14ZYCvmHS9iAKVt9AyzRSqNU1qabPih5BY=\ngithub.com/aead/chacha20 v0.0.0-20180709150244-8b13a72661da/go.mod h1:eHEWzANqSiWQsof+nXEI9bUVUyV6F53Fp89EuCh2EAA=\ngithub.com/andybalholm/brotli v1.0.5 h1:8uQZIdzKmjc/iuPu7O2ioW48L81FgatrcpfFmiq/cCs=\ngithub.com/andybalholm/brotli v1.0.5/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=\ngithub.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=\ngithub.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=\ngithub.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=\ngithub.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=\ngithub.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=\ngithub.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=\ngithub.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=\ngithub.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=\ngithub.com/coreos/go-iptables v0.7.0 h1:XWM3V+MPRr5/q51NuWSgU0fqMad64Zyxs8ZUoMsamr8=\ngithub.com/coreos/go-iptables v0.7.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=\ngithub.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=\ngithub.com/dlclark/regexp2 v1.10.0 h1:+/GIL799phkJqYW+3YbOd8LCcbHzT0Pbo8zl70MHsq0=\ngithub.com/dlclark/regexp2 v1.10.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=\ngithub.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk=\ngithub.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w=\ngithub.com/docker/docker v20.10.21+incompatible h1:UTLdBmHk3bEY+w8qeO5KttOhy6OmXWsl/FEet9Uswog=\ngithub.com/docker/docker v20.10.21+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk=\ngithub.com/docker/go-connections v0.4.0 h1:El9xVISelRB7BuFusrZozjnkIM5YnzCViNKohAFqRJQ=\ngithub.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec=\ngithub.com/docker/go-units v0.4.0 h1:3uh0PgVws3nIA0Q+MwDC8yjEPf9zjRfZZWXZYDct3Tw=\ngithub.com/docker/go-units v0.4.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=\ngithub.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9 h1:/5RkVc9Rc81XmMyVqawCiDyrBHZbLAZgTTCqou4mwj8=\ngithub.com/ericlagergren/aegis v0.0.0-20230312195928-b4ce538b56f9/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I=\ngithub.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g=\ngithub.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391/go.mod h1:K2R7GhgxrlJzHw2qiPWsCZXf/kXEJN9PLnQK73Ll0po=\ngithub.com/ericlagergren/saferand v0.0.0-20220206064634-960a4dd2bc5c h1:RUzBDdZ+e/HEe2Nh8lYsduiPAZygUfVXJn0Ncj5sHMg=\ngithub.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 h1:tlDMEdcPRQKBEz5nGDMvswiajqh7k8ogWRlhRwKy5mY=\ngithub.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1/go.mod h1:4RfsapbGx2j/vU5xC/5/9qB3kn9Awp1YDiEnN43QrJ4=\ngithub.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 h1:fuGucgPk5dN6wzfnxl3D0D3rVLw4v2SbBT9jb4VnxzA=\ngithub.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010/go.mod h1:JtBcj7sBuTTRupn7c2bFspMDIObMJsVK8TeUvpShPok=\ngithub.com/frankban/quicktest v1.14.5 h1:dfYrrRyLtiqT9GyKXgdh+k4inNeTvmGbuSgZ3lx3GhA=\ngithub.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA=\ngithub.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM=\ngithub.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk=\ngithub.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=\ngithub.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ=\ngithub.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=\ngithub.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=\ngithub.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=\ngithub.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=\ngithub.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls=\ngithub.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=\ngithub.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=\ngithub.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=\ngithub.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=\ngithub.com/gobwas/ws v1.3.1 h1:Qi34dfLMWJbiKaNbDVzM9x27nZBjmkaW6i4+Ku+pGVU=\ngithub.com/gobwas/ws v1.3.1/go.mod h1:hRKAFb8wOxFROYNsT1bqfWnhX+b5MFeJM9r2ZSwg/KY=\ngithub.com/gofrs/uuid/v5 v5.0.0 h1:p544++a97kEL+svbcFbCQVM9KFu0Yo25UoISXGNNH9M=\ngithub.com/gofrs/uuid/v5 v5.0.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=\ngithub.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=\ngithub.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=\ngithub.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg=\ngithub.com/google/btree v1.1.2 h1:xf4v41cLI2Z6FxbKm+8Bu+m8ifhj15JuZ9sa0jZCMUU=\ngithub.com/google/btree v1.1.2/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=\ngithub.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=\ngithub.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/pprof v0.0.0-20210407192527-94a9f03dee38 h1:yAJXTCF9TqKcTiHJAE8dj7HMvPfh66eeA2JYW7eFpSE=\ngithub.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE=\ngithub.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I=\ngithub.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=\ngithub.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=\ngithub.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=\ngithub.com/insomniacslk/dhcp v0.0.0-20231016090811-6a2c8fbdcc1c h1:PgxFEySCI41sH0mB7/2XswdXbUykQsRUGod8Rn+NubM=\ngithub.com/insomniacslk/dhcp v0.0.0-20231016090811-6a2c8fbdcc1c/go.mod h1:3A9PQ1cunSDF/1rbTq99Ts4pVnycWg+vlPkfeD2NLFI=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=\ngithub.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=\ngithub.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=\ngithub.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA=\ngithub.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=\ngithub.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=\ngithub.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=\ngithub.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=\ngithub.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=\ngithub.com/klauspost/cpuid/v2 v2.2.6 h1:ndNyv040zDGIDh8thGkXYjnFtiN02M1PVVF+JE/48xc=\ngithub.com/klauspost/cpuid/v2 v2.2.6/go.mod h1:Lcz8mBdAVJIBVzewtcLocK12l3Y+JytZYpaMropDUws=\ngithub.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=\ngithub.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I=\ngithub.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40 h1:EnfXoSqDfSNJv0VBNqY/88RNnhSGYkrHaO0mmFGbVsc=\ngithub.com/lunixbochs/struc v0.0.0-20200707160740-784aaebc1d40/go.mod h1:vy1vK6wD6j7xX6O6hXe621WabdtNkou2h7uRtTfRMyg=\ngithub.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=\ngithub.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=\ngithub.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=\ngithub.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=\ngithub.com/mdlayher/socket v0.4.1 h1:eM9y2/jlbs1M615oshPQOHZzj6R6wMT7bX5NPiQvn2U=\ngithub.com/mdlayher/socket v0.4.1/go.mod h1:cAqeGjoufqdxWkD7DkpyS+wcefOtmu5OQ8KuoJGIReA=\ngithub.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvOzK9ubNCCkQ+ldc4YSH/rILn53l/xGBFHHI=\ngithub.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88=\ngithub.com/metacubex/gvisor v0.0.0-20231001104248-0f672c3fb8d8 h1:npBvaPAT145UY8682AzpUMWpdIxJti/WPLjy7gCiYYs=\ngithub.com/metacubex/gvisor v0.0.0-20231001104248-0f672c3fb8d8/go.mod h1:ZR6Gas7P1GcADCVBc1uOrA0bLQqDDyp70+63fD/BE2c=\ngithub.com/metacubex/quic-go v0.40.1-0.20231130135418-0c1b47cf9394 h1:dIT+KB2hknBCrwVAXPeY9tpzzkOZP5m40yqUteRT6/Y=\ngithub.com/metacubex/quic-go v0.40.1-0.20231130135418-0c1b47cf9394/go.mod h1:F/t8VnA47xoia8ABlNA4InkZjssvFJ5p6E6jKdbkgAs=\ngithub.com/metacubex/sing-quic v0.0.0-20231130141855-0022295e524b h1:7XXoEePvxfkQN9b2wB8UXU3uzb9uL8syEFF7A9VAKKQ=\ngithub.com/metacubex/sing-quic v0.0.0-20231130141855-0022295e524b/go.mod h1:Gu5/zqZDd5G1AUtoV2yjAPWOEy7zwbU2DBUjdxJh0Kw=\ngithub.com/metacubex/sing-shadowsocks v0.2.5 h1:O2RRSHlKGEpAVG/OHJQxyHqDy8uvvdCW/oW2TDBOIhc=\ngithub.com/metacubex/sing-shadowsocks v0.2.5/go.mod h1:Xz2uW9BEYGEoA8B4XEpoxt7ERHClFCwsMAvWaruoyMo=\ngithub.com/metacubex/sing-shadowsocks2 v0.1.4 h1:OOCf8lgsVcpTOJUeaFAMzyKVebaQOBnKirDdUdBoKIE=\ngithub.com/metacubex/sing-shadowsocks2 v0.1.4/go.mod h1:Qz028sLfdY3qxGRm9FDI+IM2Ae3ty2wR7HIzD/56h/k=\ngithub.com/metacubex/sing-tun v0.1.15-0.20231103033938-170591e8d5bd h1:k0+92eARqyTAovGhg2AxdsMWHjUsdiGCnR5NuXF3CQY=\ngithub.com/metacubex/sing-tun v0.1.15-0.20231103033938-170591e8d5bd/go.mod h1:Q7zmpJ+qOvMMXyUoYlxGQuWkqALUpXzFSSqO+KLPyzA=\ngithub.com/metacubex/sing-vmess v0.1.9-0.20230921005247-a0488d7dac74 h1:FtupiyFkaVjFvRa7B/uDtRWg5BNsoyPC9MTev3sDasY=\ngithub.com/metacubex/sing-vmess v0.1.9-0.20230921005247-a0488d7dac74/go.mod h1:8EWBZpc+qNvf5gmvjAtMHK1/DpcWqzfcBL842K00BsM=\ngithub.com/metacubex/sing-wireguard v0.0.0-20231001110902-321836559170 h1:DBGA0hmrP4pVIwLiXUONdphjcppED+plmVaKf1oqkwk=\ngithub.com/metacubex/sing-wireguard v0.0.0-20231001110902-321836559170/go.mod h1:/VbJfbdLnANE+SKXyMk/96sTRrD4GdFLh5mkegqqFcY=\ngithub.com/miekg/dns v1.1.57 h1:Jzi7ApEIzwEPLHWRcafCN9LZSBbqQpxjt/wpgvg7wcM=\ngithub.com/miekg/dns v1.1.57/go.mod h1:uqRjCRUuEAA6qsOiJvDd+CFo/vW+y5WR6SNmHE55hZk=\ngithub.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0=\ngithub.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y=\ngithub.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A=\ngithub.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc=\ngithub.com/mroth/weightedrand/v2 v2.1.0 h1:o1ascnB1CIVzsqlfArQQjeMy1U0NcIbBO5rfd5E/OeU=\ngithub.com/mroth/weightedrand/v2 v2.1.0/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU=\ngithub.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 h1:1102pQc2SEPp5+xrS26wEaeb26sZy6k9/ZXlZN+eXE4=\ngithub.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7/go.mod h1:UqoUn6cHESlliMhOnKLWr+CBH+e3bazUPvFj1XZwAjs=\ngithub.com/onsi/ginkgo/v2 v2.9.5 h1:+6Hr4uxzP4XIUyAkg61dWBw8lb/gc4/X5luuxN/EC+Q=\ngithub.com/onsi/ginkgo/v2 v2.9.5/go.mod h1:tvAoo1QUJwNEU2ITftXTpR7R1RbCzoZUOs3RonqW57k=\ngithub.com/onsi/gomega v1.27.6 h1:ENqfyGeS5AX/rlXDd/ETokDz93u0YufY1Pgxuy/PvWE=\ngithub.com/openacid/errors v0.8.1/go.mod h1:GUQEJJOJE3W9skHm8E8Y4phdl2LLEN8iD7c5gcGgdx0=\ngithub.com/openacid/low v0.1.21 h1:Tr2GNu4N/+rGRYdOsEHOE89cxUIaDViZbVmKz29uKGo=\ngithub.com/openacid/low v0.1.21/go.mod h1:q+MsKI6Pz2xsCkzV4BLj7NR5M4EX0sGz5AqotpZDVh0=\ngithub.com/openacid/must v0.1.3/go.mod h1:luPiXCuJlEo3UUFQngVQokV0MPGryeYvtCbQPs3U1+I=\ngithub.com/openacid/testkeys v0.1.6/go.mod h1:MfA7cACzBpbiwekivj8StqX0WIRmqlMsci1c37CA3Do=\ngithub.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=\ngithub.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=\ngithub.com/opencontainers/image-spec v1.0.2 h1:9yCKha/T5XdGtO0q9Q9a6T5NUCsTn/DrBg0D7ufOcFM=\ngithub.com/opencontainers/image-spec v1.0.2/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0=\ngithub.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=\ngithub.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=\ngithub.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=\ngithub.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw=\ngithub.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE=\ngithub.com/puzpuzpuz/xsync/v3 v3.0.2 h1:3yESHrRFYr6xzkz61LLkvNiPFXxJEAABanTQpKbAaew=\ngithub.com/puzpuzpuz/xsync/v3 v3.0.2/go.mod h1:VjzYrABPabuM4KyBh1Ftq6u8nhwY5tBPKP9jpmh0nnA=\ngithub.com/quic-go/qpack v0.4.0 h1:Cr9BXA1sQS2SmDUWjSofMPNKmvF6IiIfDRmgU0w1ZCo=\ngithub.com/quic-go/qpack v0.4.0/go.mod h1:UZVnYIfi5GRk+zI9UMaCPsmZ2xKJP7XBUvVyT1Knj9A=\ngithub.com/quic-go/qtls-go1-20 v0.4.1 h1:D33340mCNDAIKBqXuAvexTNMUByrYmFYVfKfDN5nfFs=\ngithub.com/quic-go/qtls-go1-20 v0.4.1/go.mod h1:X9Nh97ZL80Z+bX/gUXMbipO6OxdiDi58b/fMC9mAL+k=\ngithub.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8=\ngithub.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a h1:+NkI2670SQpQWvkkD2QgdTuzQG263YZ+2emfpeyGqW0=\ngithub.com/sagernet/bbolt v0.0.0-20231014093535-ea5cb2fe9f0a/go.mod h1:63s7jpZqcDAIpj8oI/1v4Izok+npJOHACFCU6+huCkM=\ngithub.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61 h1:5+m7c6AkmAylhauulqN/c5dnh8/KssrE9c93TQrXldA=\ngithub.com/sagernet/go-tun2socks v1.16.12-0.20220818015926-16cb67876a61/go.mod h1:QUQ4RRHD6hGGHdFMEtR8T2P6GS6R3D/CXKdaYHKKXms=\ngithub.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97 h1:iL5gZI3uFp0X6EslacyapiRz7LLSJyr4RajF/BhMVyE=\ngithub.com/sagernet/netlink v0.0.0-20220905062125-8043b4a9aa97/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=\ngithub.com/sagernet/sing v0.0.0-20220817130738-ce854cda8522/go.mod h1:QVsS5L/ZA2Q5UhQwLrn0Trw+msNd/NPGEhBKR/ioWiY=\ngithub.com/sagernet/sing v0.1.8/go.mod h1:jt1w2u7lJQFFSGLiRrRIs5YWmx4kAPfWuOejuDW9qMk=\ngithub.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c h1:uask61Pxc3nGqsOSjqnBKrwfODWRoEa80lXm04LNk0E=\ngithub.com/sagernet/sing v0.2.18-0.20231108041402-4fbbd193203c/go.mod h1:OL6k2F0vHmEzXz2KW19qQzu172FDgSbUSODylighuVo=\ngithub.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07 h1:ncKb5tVOsCQgCsv6UpsA0jinbNb5OQ5GMPJlyQP3EHM=\ngithub.com/sagernet/sing-mux v0.1.5-0.20231109075101-6b086ed6bb07/go.mod h1:u/MZf32xPG8jEKe3t+xUV67EBnKtDtCaPhsJQOQGUYU=\ngithub.com/sagernet/sing-shadowtls v0.1.4 h1:aTgBSJEgnumzFenPvc+kbD9/W0PywzWevnVpEx6Tw3k=\ngithub.com/sagernet/sing-shadowtls v0.1.4/go.mod h1:F8NBgsY5YN2beQavdgdm1DPlhaKQlaL6lpDdcBglGK4=\ngithub.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37 h1:HuE6xSwco/Xed8ajZ+coeYLmioq0Qp1/Z2zczFaV8as=\ngithub.com/sagernet/smux v0.0.0-20230312102458-337ec2a5af37/go.mod h1:3skNSftZDJWTGVtVaM2jfbce8qHnmH/AGDRe62iNOg0=\ngithub.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6 h1:Px+hN4Vzgx+iCGVnWH5A8eR7JhNnIV3rGQmBxA7cw6Q=\ngithub.com/sagernet/tfo-go v0.0.0-20230816093905-5a5c285d44a6/go.mod h1:zovq6vTvEM6ECiqE3Eeb9rpIylPpamPcmrJ9tv0Bt0M=\ngithub.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2 h1:kDUqhc9Vsk5HJuhfIATJ8oQwBmpOZJuozQG7Vk88lL4=\ngithub.com/sagernet/utls v0.0.0-20230309024959-6732c2ab36f2/go.mod h1:JKQMZq/O2qnZjdrt+B57olmfgEmLtY9iiSIEYtWvoSM=\ngithub.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f h1:Kvo8w8Y9lzFGB/7z09MJ3TR99TFtfI/IuY87Ygcycho=\ngithub.com/sagernet/wireguard-go v0.0.0-20230807125731-5d4a7ef2dc5f/go.mod h1:mySs0abhpc/gLlvhoq7HP1RzOaRmIXVeZGCh++zoApk=\ngithub.com/samber/lo v1.38.1 h1:j2XEAqXKb09Am4ebOg31SpvzUTTs6EN3VfgeLUhPdXM=\ngithub.com/samber/lo v1.38.1/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA=\ngithub.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9 h1:rc/CcqLH3lh8n+csdOuDfP+NuykE0U6AeYSJJHKDgSg=\ngithub.com/scjalliance/comshim v0.0.0-20230315213746-5e51f40bd3b9/go.mod h1:a/83NAfUXvEuLpmxDssAXxgUgrEy12MId3Wd7OTs76s=\ngithub.com/shirou/gopsutil/v3 v3.23.10 h1:/N42opWlYzegYaVkWejXWJpbzKv2JDy3mrgGzKsh9hM=\ngithub.com/shirou/gopsutil/v3 v3.23.10/go.mod h1:JIE26kpucQi+innVlAUnIEOSBhBUkirr5b44yr55+WE=\ngithub.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=\ngithub.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=\ngithub.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=\ngithub.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=\ngithub.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b h1:rXHg9GrUEtWZhEkrykicdND3VPjlVbYiLdX9J7gimS8=\ngithub.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b/go.mod h1:X7qrxNQViEaAN9LNZOPl9PfvQtp3V3c7LTo0dvGi0fM=\ngithub.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c h1:DjKMC30y6yjG3IxDaeAj3PCoRr+IsO+bzyT+Se2m2Hk=\ngithub.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c/go.mod h1:NV/a66PhhWYVmUMaotlXJ8fIEFB98u+c8l/CQIEFLrU=\ngithub.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e h1:ur8uMsPIFG3i4Gi093BQITvwH9znsz2VUZmnmwHvpIo=\ngithub.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e/go.mod h1:+e5fBW3bpPyo+3uLo513gIUblc03egGjMM0+5GKbzK8=\ngithub.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=\ngithub.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=\ngithub.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=\ngithub.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=\ngithub.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=\ngithub.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU=\ngithub.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI=\ngithub.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk=\ngithub.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY=\ngithub.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA=\ngithub.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=\ngithub.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=\ngithub.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=\ngithub.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=\ngithub.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74 h1:gga7acRE695APm9hlsSMoOoE65U4/TcqNj90mc69Rlg=\ngithub.com/vishvananda/netns v0.0.0-20211101163701-50045581ed74/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=\ngithub.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=\ngithub.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=\ngithub.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngithub.com/yusufpapurcu/wmi v1.2.3 h1:E1ctvB7uKFMOJw3fdOW32DwGE9I7t++CRUEMKvFoFiw=\ngithub.com/yusufpapurcu/wmi v1.2.3/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0=\ngithub.com/zhangyunhao116/fastrand v0.3.0 h1:7bwe124xcckPulX6fxtr2lFdO2KQqaefdtbk+mqO/Ig=\ngithub.com/zhangyunhao116/fastrand v0.3.0/go.mod h1:0v5KgHho0VE6HU192HnY15de/oDS8UrbBChIFjIhBtc=\ngitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo=\ngitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ=\ngo.uber.org/mock v0.3.0 h1:3mUxI1No2/60yUYax92Pt8eNOEecx2D3lcXZh2NEZJo=\ngo.uber.org/mock v0.3.0/go.mod h1:a6FSlNadKUHUa9IP5Vyt1zh4fC7uAwxMutEAscFbkZc=\ngo4.org/netipx v0.0.0-20230824141953-6213f710f925 h1:eeQDDVKFkx0g4Hyy8pHgmZaK0EqB4SD6rvKbUdN3ziQ=\ngo4.org/netipx v0.0.0-20230824141953-6213f710f925/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY=\ngolang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4=\ngolang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa h1:FRnLl4eNAQl8hwxVVC17teOw8kdjVDVAiFMtgUdTSRQ=\ngolang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa/go.mod h1:zk2irFbV9DP96SEBUUAy67IdHUaZuSnrz1n472HUCLE=\ngolang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.14.0 h1:dGoOF9QVLYng8IHTm7BAyWqCqSheQ5pYWGhzW00YJr0=\ngolang.org/x/mod v0.14.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/net v0.18.0 h1:mIYleuAkSbHh0tCv7RvjL3F6ZVbLjq4+R7zbOn3Kokg=\ngolang.org/x/net v0.18.0/go.mod h1:/czyP5RqHAH4odGYxBJ1qz0+CE5WZ+2j1YgoEo8F2jQ=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.5.0 h1:60k92dhOjHxJkrqnwsfl8KuaHbn/5dl0lUPUklKo3qE=\ngolang.org/x/sync v0.5.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20191204072324-ce4227a45e2e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.0.0-20220731174439-a90be440212d/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc=\ngolang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ=\ngolang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=\ngolang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4=\ngolang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=\ngolang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/tools v0.15.0 h1:zdAyfUGbYmuVokhzVmghFl2ZJh5QhcfebBgmVPFYA+8=\ngolang.org/x/tools v0.15.0/go.mod h1:hpksKq4dtpQWS1uQ61JkdqWM3LscIS6Slf+VVkm+wQk=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=\ngoogle.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8=\ngoogle.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngotest.tools/v3 v3.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o=\nlukechampine.com/blake3 v1.2.1 h1:YuqqRuaqsGV71BV/nm9xlI0MKUv4QC54jQnBChWbGnI=\nlukechampine.com/blake3 v1.2.1/go.mod h1:0OFRp7fBtAylGVCO40o87sbupkyIGgbpv1+M1k1LM6k=\n"
  },
  {
    "path": "core/Clash.Meta/test/hysteria_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/docker/docker/api/types/container\"\n\t\"github.com/metacubex/mihomo/adapter/outbound\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestMihomo_Hysteria(t *testing.T) {\n\tcfg := &container.Config{\n\t\tImage:        ImageHysteria,\n\t\tExposedPorts: defaultExposedPorts,\n\t\tCmd:          []string{\"server\"},\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t\tBinds: []string{\n\t\t\tfmt.Sprintf(\"%s:/config.json\", C.Path.Resolve(\"hysteria.json\")),\n\t\t\tfmt.Sprintf(\"%s:/home/ubuntu/my.crt\", C.Path.Resolve(\"example.org.pem\")),\n\t\t\tfmt.Sprintf(\"%s:/home/ubuntu/my.key\", C.Path.Resolve(\"example.org-key.pem\")),\n\t\t},\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"hysteria\")\n\tif err != nil {\n\t\tassert.FailNow(t, err.Error())\n\t}\n\n\tt.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewHysteria(outbound.HysteriaOption{\n\t\tName:           \"hysteria\",\n\t\tServer:         localIP.String(),\n\t\tPort:           10002,\n\t\tObfs:           \"fuck me till the daylight\",\n\t\tUp:             \"100\",\n\t\tDown:           \"100\",\n\t\tSkipCertVerify: true,\n\t})\n\tif err != nil {\n\t\tassert.FailNow(t, err.Error())\n\t}\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n"
  },
  {
    "path": "core/Clash.Meta/test/snell_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/docker/docker/api/types/container\"\n\t\"github.com/metacubex/mihomo/adapter/outbound\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestMihomo_SnellObfsHTTP(t *testing.T) {\n\tcfg := &container.Config{\n\t\tImage:        ImageSnell,\n\t\tExposedPorts: defaultExposedPorts,\n\t\tCmd:          []string{\"-c\", \"/config.conf\"},\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t\tBinds:        []string{fmt.Sprintf(\"%s:/config.conf\", C.Path.Resolve(\"snell-http.conf\"))},\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"snell-http\")\n\trequire.NoError(t, err)\n\n\tt.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewSnell(outbound.SnellOption{\n\t\tName:   \"snell\",\n\t\tServer: localIP.String(),\n\t\tPort:   10002,\n\t\tPsk:    \"password\",\n\t\tObfsOpts: map[string]any{\n\t\t\t\"mode\": \"http\",\n\t\t},\n\t})\n\trequire.NoError(t, err)\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n\nfunc TestMihomo_SnellObfsTLS(t *testing.T) {\n\tcfg := &container.Config{\n\t\tImage:        ImageSnell,\n\t\tExposedPorts: defaultExposedPorts,\n\t\tCmd:          []string{\"-c\", \"/config.conf\"},\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t\tBinds:        []string{fmt.Sprintf(\"%s:/config.conf\", C.Path.Resolve(\"snell-tls.conf\"))},\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"snell-tls\")\n\trequire.NoError(t, err)\n\n\tt.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewSnell(outbound.SnellOption{\n\t\tName:   \"snell\",\n\t\tServer: localIP.String(),\n\t\tPort:   10002,\n\t\tPsk:    \"password\",\n\t\tObfsOpts: map[string]any{\n\t\t\t\"mode\": \"tls\",\n\t\t},\n\t})\n\trequire.NoError(t, err)\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n\nfunc TestMihomo_Snell(t *testing.T) {\n\tcfg := &container.Config{\n\t\tImage:        ImageSnell,\n\t\tExposedPorts: defaultExposedPorts,\n\t\tCmd:          []string{\"-c\", \"/config.conf\"},\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t\tBinds:        []string{fmt.Sprintf(\"%s:/config.conf\", C.Path.Resolve(\"snell.conf\"))},\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"snell\")\n\trequire.NoError(t, err)\n\n\tt.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewSnell(outbound.SnellOption{\n\t\tName:   \"snell\",\n\t\tServer: localIP.String(),\n\t\tPort:   10002,\n\t\tPsk:    \"password\",\n\t})\n\trequire.NoError(t, err)\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n\nfunc TestMihomo_Snellv3(t *testing.T) {\n\tcfg := &container.Config{\n\t\tImage:        ImageSnell,\n\t\tExposedPorts: defaultExposedPorts,\n\t\tCmd:          []string{\"-c\", \"/config.conf\"},\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t\tBinds:        []string{fmt.Sprintf(\"%s:/config.conf\", C.Path.Resolve(\"snell.conf\"))},\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"snell\")\n\trequire.NoError(t, err)\n\n\tt.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewSnell(outbound.SnellOption{\n\t\tName:    \"snell\",\n\t\tServer:  localIP.String(),\n\t\tPort:    10002,\n\t\tPsk:     \"password\",\n\t\tUDP:     true,\n\t\tVersion: 3,\n\t})\n\trequire.NoError(t, err)\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n\nfunc Benchmark_Snell(b *testing.B) {\n\tcfg := &container.Config{\n\t\tImage:        ImageSnell,\n\t\tExposedPorts: defaultExposedPorts,\n\t\tCmd:          []string{\"-c\", \"/config.conf\"},\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t\tBinds:        []string{fmt.Sprintf(\"%s:/config.conf\", C.Path.Resolve(\"snell-http.conf\"))},\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"snell-bench\")\n\trequire.NoError(b, err)\n\n\tb.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewSnell(outbound.SnellOption{\n\t\tName:   \"snell\",\n\t\tServer: localIP.String(),\n\t\tPort:   10002,\n\t\tPsk:    \"password\",\n\t\tObfsOpts: map[string]any{\n\t\t\t\"mode\": \"http\",\n\t\t},\n\t})\n\trequire.NoError(b, err)\n\n\ttime.Sleep(waitTime)\n\tbenchmarkProxy(b, proxy)\n}\n"
  },
  {
    "path": "core/Clash.Meta/test/ss_test.go",
    "content": "package main\n\nimport (\n\t\"crypto/rand\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"net\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/docker/docker/api/types/container\"\n\t\"github.com/metacubex/mihomo/adapter/outbound\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestMihomo_Shadowsocks(t *testing.T) {\n\tfor _, method := range []string{\n\t\t\"aes-128-ctr\",\n\t\t\"aes-192-ctr\",\n\t\t\"aes-256-ctr\",\n\t\t\"aes-128-cfb\",\n\t\t\"aes-192-cfb\",\n\t\t\"aes-256-cfb\",\n\t\t\"rc4-md5\",\n\t\t\"chacha20-ietf\",\n\t\t\"aes-128-gcm\",\n\t\t\"aes-256-gcm\",\n\t\t\"chacha20-ietf-poly1305\",\n\t\t\"xchacha20-ietf-poly1305\",\n\t} {\n\t\tt.Run(method, func(t *testing.T) {\n\t\t\ttestMihomo_Shadowsocks(t, method, \"FzcLbKs2dY9mhL\")\n\t\t})\n\t}\n\tfor _, method := range []string{\n\t\t\"aes-128-gcm\",\n\t\t\"aes-256-gcm\",\n\t\t\"chacha20-ietf-poly1305\",\n\t} {\n\t\tt.Run(method, func(t *testing.T) {\n\t\t\ttestMihomo_ShadowsocksRust(t, method, \"FzcLbKs2dY9mhL\")\n\t\t})\n\t}\n}\n\nfunc TestMihomo_Shadowsocks2022(t *testing.T) {\n\tfor _, method := range []string{\n\t\t\"2022-blake3-aes-128-gcm\",\n\t} {\n\t\tt.Run(method, func(t *testing.T) {\n\t\t\ttestMihomo_ShadowsocksRust(t, method, mkKey(16))\n\t\t})\n\t}\n\tfor _, method := range []string{\n\t\t\"2022-blake3-aes-256-gcm\",\n\t\t\"2022-blake3-chacha20-poly1305\",\n\t} {\n\t\tt.Run(method, func(t *testing.T) {\n\t\t\ttestMihomo_ShadowsocksRust(t, method, mkKey(32))\n\t\t})\n\t}\n}\n\nfunc mkKey(bits int) string {\n\tk := make([]byte, bits)\n\trand.Read(k)\n\treturn base64.StdEncoding.EncodeToString(k)\n}\n\nfunc testMihomo_Shadowsocks(t *testing.T, method string, password string) {\n\tcfg := &container.Config{\n\t\tImage: ImageShadowsocks,\n\t\tEnv: []string{\n\t\t\t\"SS_MODULE=ss-server\",\n\t\t\t\"SS_CONFIG=-s 0.0.0.0 -u -p 10002 -m \" + method + \" -k \" + password,\n\t\t},\n\t\tExposedPorts: defaultExposedPorts,\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"ss\")\n\trequire.NoError(t, err)\n\n\tt.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewShadowSocks(outbound.ShadowSocksOption{\n\t\tName:     \"ss\",\n\t\tServer:   localIP.String(),\n\t\tPort:     10002,\n\t\tPassword: password,\n\t\tCipher:   method,\n\t\tUDP:      true,\n\t})\n\trequire.NoError(t, err)\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n\nfunc testMihomo_ShadowsocksRust(t *testing.T, method string, password string) {\n\tcfg := &container.Config{\n\t\tImage:        ImageShadowsocksRust,\n\t\tEntrypoint:   []string{\"ssserver\"},\n\t\tCmd:          []string{\"-s\", \"0.0.0.0:10002\", \"-m\", method, \"-k\", password, \"-U\", \"-v\"},\n\t\tExposedPorts: defaultExposedPorts,\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"ss-rust\")\n\trequire.NoError(t, err)\n\n\tt.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewShadowSocks(outbound.ShadowSocksOption{\n\t\tName:     \"ss\",\n\t\tServer:   localIP.String(),\n\t\tPort:     10002,\n\t\tPassword: password,\n\t\tCipher:   method,\n\t\tUDP:      true,\n\t})\n\trequire.NoError(t, err)\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n\nfunc TestMihomo_ShadowsocksObfsHTTP(t *testing.T) {\n\tcfg := &container.Config{\n\t\tImage: ImageShadowsocks,\n\t\tEnv: []string{\n\t\t\t\"SS_MODULE=ss-server\",\n\t\t\t\"SS_CONFIG=-s 0.0.0.0 -u -p 10002 -m chacha20-ietf-poly1305 -k FzcLbKs2dY9mhL --plugin obfs-server --plugin-opts obfs=http\",\n\t\t},\n\t\tExposedPorts: defaultExposedPorts,\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"ss-obfs-http\")\n\trequire.NoError(t, err)\n\n\tt.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewShadowSocks(outbound.ShadowSocksOption{\n\t\tName:     \"ss\",\n\t\tServer:   localIP.String(),\n\t\tPort:     10002,\n\t\tPassword: \"FzcLbKs2dY9mhL\",\n\t\tCipher:   \"chacha20-ietf-poly1305\",\n\t\tUDP:      true,\n\t\tPlugin:   \"obfs\",\n\t\tPluginOpts: map[string]any{\n\t\t\t\"mode\": \"http\",\n\t\t},\n\t})\n\trequire.NoError(t, err)\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n\nfunc TestMihomo_ShadowsocksObfsTLS(t *testing.T) {\n\tcfg := &container.Config{\n\t\tImage: ImageShadowsocks,\n\t\tEnv: []string{\n\t\t\t\"SS_MODULE=ss-server\",\n\t\t\t\"SS_CONFIG=-s 0.0.0.0 -u -p 10002 -m chacha20-ietf-poly1305 -k FzcLbKs2dY9mhL --plugin obfs-server --plugin-opts obfs=tls\",\n\t\t},\n\t\tExposedPorts: defaultExposedPorts,\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"ss-obfs-tls\")\n\trequire.NoError(t, err)\n\n\tt.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewShadowSocks(outbound.ShadowSocksOption{\n\t\tName:     \"ss\",\n\t\tServer:   localIP.String(),\n\t\tPort:     10002,\n\t\tPassword: \"FzcLbKs2dY9mhL\",\n\t\tCipher:   \"chacha20-ietf-poly1305\",\n\t\tUDP:      true,\n\t\tPlugin:   \"obfs\",\n\t\tPluginOpts: map[string]any{\n\t\t\t\"mode\": \"tls\",\n\t\t},\n\t})\n\trequire.NoError(t, err)\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n\nfunc TestMihomo_ShadowsocksV2RayPlugin(t *testing.T) {\n\tcfg := &container.Config{\n\t\tImage: ImageShadowsocks,\n\t\tEnv: []string{\n\t\t\t\"SS_MODULE=ss-server\",\n\t\t\t\"SS_CONFIG=-s 0.0.0.0 -u -p 10002 -m chacha20-ietf-poly1305 -k FzcLbKs2dY9mhL --plugin v2ray-plugin --plugin-opts=server\",\n\t\t},\n\t\tExposedPorts: defaultExposedPorts,\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"ss-v2ray-plugin\")\n\trequire.NoError(t, err)\n\n\tt.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewShadowSocks(outbound.ShadowSocksOption{\n\t\tName:     \"ss\",\n\t\tServer:   localIP.String(),\n\t\tPort:     10002,\n\t\tPassword: \"FzcLbKs2dY9mhL\",\n\t\tCipher:   \"chacha20-ietf-poly1305\",\n\t\tUDP:      true,\n\t\tPlugin:   \"v2ray-plugin\",\n\t\tPluginOpts: map[string]any{\n\t\t\t\"mode\": \"websocket\",\n\t\t},\n\t})\n\trequire.NoError(t, err)\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n\nfunc Benchmark_Shadowsocks(b *testing.B) {\n\tcfg := &container.Config{\n\t\tImage:        ImageShadowsocksRust,\n\t\tEntrypoint:   []string{\"ssserver\"},\n\t\tCmd:          []string{\"-s\", \"0.0.0.0:10002\", \"-m\", \"aes-256-gcm\", \"-k\", \"FzcLbKs2dY9mhL\", \"-U\"},\n\t\tExposedPorts: defaultExposedPorts,\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"ss-bench\")\n\trequire.NoError(b, err)\n\n\tb.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewShadowSocks(outbound.ShadowSocksOption{\n\t\tName:     \"ss\",\n\t\tServer:   localIP.String(),\n\t\tPort:     10002,\n\t\tPassword: \"FzcLbKs2dY9mhL\",\n\t\tCipher:   \"aes-256-gcm\",\n\t\tUDP:      true,\n\t})\n\trequire.NoError(b, err)\n\n\trequire.True(b, TCPing(net.JoinHostPort(localIP.String(), \"10002\")))\n\tbenchmarkProxy(b, proxy)\n}\n\nfunc TestMihomo_ShadowsocksUoT(t *testing.T) {\n\tconfigPath := C.Path.Resolve(\"xray-shadowsocks.json\")\n\n\tcfg := &container.Config{\n\t\tImage:        ImageVless,\n\t\tExposedPorts: defaultExposedPorts,\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t\tBinds:        []string{fmt.Sprintf(\"%s:/etc/xray/config.json\", configPath)},\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"xray-ss\")\n\trequire.NoError(t, err)\n\n\tt.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewShadowSocks(outbound.ShadowSocksOption{\n\t\tName:       \"ss\",\n\t\tServer:     localIP.String(),\n\t\tPort:       10002,\n\t\tPassword:   \"FzcLbKs2dY9mhL\",\n\t\tCipher:     \"aes-128-gcm\",\n\t\tUDP:        true,\n\t\tUDPOverTCP: true,\n\t})\n\trequire.NoError(t, err)\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n"
  },
  {
    "path": "core/Clash.Meta/test/trojan_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/docker/docker/api/types/container\"\n\t\"github.com/metacubex/mihomo/adapter/outbound\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestMihomo_Trojan(t *testing.T) {\n\tcfg := &container.Config{\n\t\tImage:        ImageTrojan,\n\t\tExposedPorts: defaultExposedPorts,\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t\tBinds: []string{\n\t\t\tfmt.Sprintf(\"%s:/config/config.json\", C.Path.Resolve(\"trojan.json\")),\n\t\t\tfmt.Sprintf(\"%s:/path/to/certificate.crt\", C.Path.Resolve(\"example.org.pem\")),\n\t\t\tfmt.Sprintf(\"%s:/path/to/private.key\", C.Path.Resolve(\"example.org-key.pem\")),\n\t\t},\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"trojan\")\n\trequire.NoError(t, err)\n\n\tt.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewTrojan(outbound.TrojanOption{\n\t\tName:           \"trojan\",\n\t\tServer:         localIP.String(),\n\t\tPort:           10002,\n\t\tPassword:       \"password\",\n\t\tSNI:            \"example.org\",\n\t\tSkipCertVerify: true,\n\t\tUDP:            true,\n\t})\n\trequire.NoError(t, err)\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n\nfunc TestMihomo_TrojanGrpc(t *testing.T) {\n\tcfg := &container.Config{\n\t\tImage:        ImageXray,\n\t\tExposedPorts: defaultExposedPorts,\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t\tBinds: []string{\n\t\t\tfmt.Sprintf(\"%s:/etc/xray/config.json\", C.Path.Resolve(\"trojan-grpc.json\")),\n\t\t\tfmt.Sprintf(\"%s:/etc/ssl/v2ray/fullchain.pem\", C.Path.Resolve(\"example.org.pem\")),\n\t\t\tfmt.Sprintf(\"%s:/etc/ssl/v2ray/privkey.pem\", C.Path.Resolve(\"example.org-key.pem\")),\n\t\t},\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"trojan-grpc\")\n\trequire.NoError(t, err)\n\tt.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewTrojan(outbound.TrojanOption{\n\t\tName:           \"trojan\",\n\t\tServer:         localIP.String(),\n\t\tPort:           10002,\n\t\tPassword:       \"example\",\n\t\tSNI:            \"example.org\",\n\t\tSkipCertVerify: true,\n\t\tUDP:            true,\n\t\tNetwork:        \"grpc\",\n\t\tGrpcOpts: outbound.GrpcOptions{\n\t\t\tGrpcServiceName: \"example\",\n\t\t},\n\t})\n\trequire.NoError(t, err)\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n\nfunc TestMihomo_TrojanWebsocket(t *testing.T) {\n\tcfg := &container.Config{\n\t\tImage:        ImageTrojanGo,\n\t\tExposedPorts: defaultExposedPorts,\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t\tBinds: []string{\n\t\t\tfmt.Sprintf(\"%s:/etc/trojan-go/config.json\", C.Path.Resolve(\"trojan-ws.json\")),\n\t\t\tfmt.Sprintf(\"%s:/fullchain.pem\", C.Path.Resolve(\"example.org.pem\")),\n\t\t\tfmt.Sprintf(\"%s:/privkey.pem\", C.Path.Resolve(\"example.org-key.pem\")),\n\t\t},\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"trojan-ws\")\n\trequire.NoError(t, err)\n\tt.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewTrojan(outbound.TrojanOption{\n\t\tName:           \"trojan\",\n\t\tServer:         localIP.String(),\n\t\tPort:           10002,\n\t\tPassword:       \"example\",\n\t\tSNI:            \"example.org\",\n\t\tSkipCertVerify: true,\n\t\tUDP:            true,\n\t\tNetwork:        \"ws\",\n\t})\n\trequire.NoError(t, err)\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n\nfunc TestMihomo_TrojanXTLS(t *testing.T) {\n\tcfg := &container.Config{\n\t\tImage:        ImageXray,\n\t\tExposedPorts: defaultExposedPorts,\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t\tBinds: []string{\n\t\t\tfmt.Sprintf(\"%s:/etc/xray/config.json\", C.Path.Resolve(\"trojan-xtls.json\")),\n\t\t\tfmt.Sprintf(\"%s:/etc/ssl/v2ray/fullchain.pem\", C.Path.Resolve(\"example.org.pem\")),\n\t\t\tfmt.Sprintf(\"%s:/etc/ssl/v2ray/privkey.pem\", C.Path.Resolve(\"example.org-key.pem\")),\n\t\t},\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"trojan-xtls\")\n\tif err != nil {\n\t\trequire.NoError(t, err)\n\t}\n\tdefer cleanContainer(id)\n\n\tproxy, err := outbound.NewTrojan(outbound.TrojanOption{\n\t\tName:           \"trojan\",\n\t\tServer:         localIP.String(),\n\t\tPort:           10002,\n\t\tPassword:       \"example\",\n\t\tSNI:            \"example.org\",\n\t\tSkipCertVerify: true,\n\t\tUDP:            true,\n\t\tNetwork:        \"tcp\",\n\t\tFlow:           \"xtls-rprx-direct\",\n\t\tFlowShow:       true,\n\t})\n\tif err != nil {\n\t\trequire.NoError(t, err)\n\t}\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n\nfunc Benchmark_Trojan(b *testing.B) {\n\tcfg := &container.Config{\n\t\tImage:        ImageTrojan,\n\t\tExposedPorts: defaultExposedPorts,\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t\tBinds: []string{\n\t\t\tfmt.Sprintf(\"%s:/config/config.json\", C.Path.Resolve(\"trojan.json\")),\n\t\t\tfmt.Sprintf(\"%s:/path/to/certificate.crt\", C.Path.Resolve(\"example.org.pem\")),\n\t\t\tfmt.Sprintf(\"%s:/path/to/private.key\", C.Path.Resolve(\"example.org-key.pem\")),\n\t\t},\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"trojan-bench\")\n\trequire.NoError(b, err)\n\n\tb.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewTrojan(outbound.TrojanOption{\n\t\tName:           \"trojan\",\n\t\tServer:         localIP.String(),\n\t\tPort:           10002,\n\t\tPassword:       \"password\",\n\t\tSNI:            \"example.org\",\n\t\tSkipCertVerify: true,\n\t\tUDP:            true,\n\t})\n\trequire.NoError(b, err)\n\n\trequire.True(b, TCPing(net.JoinHostPort(localIP.String(), \"10002\")))\n\tbenchmarkProxy(b, proxy)\n}\n"
  },
  {
    "path": "core/Clash.Meta/test/util.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"time\"\n)\n\nfunc Listen(network, address string) (net.Listener, error) {\n\tlc := net.ListenConfig{}\n\n\tvar lastErr error\n\tfor i := 0; i < 5; i++ {\n\t\tl, err := lc.Listen(context.Background(), network, address)\n\t\tif err == nil {\n\t\t\treturn l, nil\n\t\t}\n\n\t\tlastErr = err\n\t\ttime.Sleep(time.Millisecond * 200)\n\t}\n\treturn nil, lastErr\n}\n\nfunc ListenPacket(network, address string) (net.PacketConn, error) {\n\tvar lastErr error\n\tfor i := 0; i < 5; i++ {\n\t\tl, err := net.ListenPacket(network, address)\n\t\tif err == nil {\n\t\t\treturn l, nil\n\t\t}\n\n\t\tlastErr = err\n\t\ttime.Sleep(time.Millisecond * 200)\n\t}\n\treturn nil, lastErr\n}\n\nfunc TCPing(addr string) bool {\n\tfor i := 0; i < 10; i++ {\n\t\tconn, err := net.Dial(\"tcp\", addr)\n\t\tif err == nil {\n\t\t\tconn.Close()\n\t\t\treturn true\n\t\t}\n\t\ttime.Sleep(time.Millisecond * 500)\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "core/Clash.Meta/test/util_darwin_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"net/netip\"\n\t\"syscall\"\n\n\t\"golang.org/x/net/route\"\n)\n\nfunc defaultRouteIP() (netip.Addr, error) {\n\tidx, err := defaultRouteInterfaceIndex()\n\tif err != nil {\n\t\treturn netip.Addr{}, err\n\t}\n\tiface, err := net.InterfaceByIndex(idx)\n\tif err != nil {\n\t\treturn netip.Addr{}, err\n\t}\n\taddrs, err := iface.Addrs()\n\tif err != nil {\n\t\treturn netip.Addr{}, err\n\t}\n\tfor _, addr := range addrs {\n\t\tip := addr.(*net.IPNet).IP\n\t\tif ip.To4() != nil {\n\t\t\ta, _ := netip.AddrFromSlice(ip)\n\t\t\treturn a, nil\n\t\t}\n\t}\n\n\treturn netip.Addr{}, err\n}\n\nfunc defaultRouteInterfaceIndex() (int, error) {\n\trib, err := route.FetchRIB(syscall.AF_UNSPEC, syscall.NET_RT_DUMP2, 0)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"route.FetchRIB: %w\", err)\n\t}\n\tmsgs, err := route.ParseRIB(syscall.NET_RT_IFLIST2, rib)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"route.ParseRIB: %w\", err)\n\t}\n\tfor _, message := range msgs {\n\t\trouteMessage := message.(*route.RouteMessage)\n\t\tif routeMessage.Flags&(syscall.RTF_UP|syscall.RTF_GATEWAY|syscall.RTF_STATIC) == 0 {\n\t\t\tcontinue\n\t\t}\n\n\t\taddresses := routeMessage.Addrs\n\n\t\tdestination, ok := addresses[0].(*route.Inet4Addr)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tif destination.IP != [4]byte{0, 0, 0, 0} {\n\t\t\tcontinue\n\t\t}\n\n\t\tswitch addresses[1].(type) {\n\t\tcase *route.Inet4Addr:\n\t\t\treturn routeMessage.Index, nil\n\t\tdefault:\n\t\t\tcontinue\n\t\t}\n\t}\n\n\treturn 0, fmt.Errorf(\"ambiguous gateway interfaces found\")\n}\n"
  },
  {
    "path": "core/Clash.Meta/test/util_other_test.go",
    "content": "//go:build !darwin\n\npackage main\n\nimport (\n\t\"errors\"\n\t\"net/netip\"\n)\n\nfunc defaultRouteIP() (netip.Addr, error) {\n\treturn netip.Addr{}, errors.New(\"not supported\")\n}\n"
  },
  {
    "path": "core/Clash.Meta/test/vless_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/docker/docker/api/types/container\"\n\t\"github.com/metacubex/mihomo/adapter/outbound\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\n// TODO: fix udp test\nfunc TestMihomo_VlessTLS(t *testing.T) {\n\tcfg := &container.Config{\n\t\tImage:        ImageVmess,\n\t\tExposedPorts: defaultExposedPorts,\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t\tBinds: []string{\n\t\t\tfmt.Sprintf(\"%s:/etc/v2ray/config.json\", C.Path.Resolve(\"vless-tls.json\")),\n\t\t\tfmt.Sprintf(\"%s:/etc/ssl/v2ray/fullchain.pem\", C.Path.Resolve(\"example.org.pem\")),\n\t\t\tfmt.Sprintf(\"%s:/etc/ssl/v2ray/privkey.pem\", C.Path.Resolve(\"example.org-key.pem\")),\n\t\t},\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"vless-tls\")\n\tif err != nil {\n\t\tassert.FailNow(t, err.Error())\n\t}\n\tdefer cleanContainer(id)\n\n\tproxy, err := outbound.NewVless(outbound.VlessOption{\n\t\tName:           \"vless\",\n\t\tServer:         localIP.String(),\n\t\tPort:           10002,\n\t\tUUID:           \"b831381d-6324-4d53-ad4f-8cda48b30811\",\n\t\tTLS:            true,\n\t\tSkipCertVerify: true,\n\t\tServerName:     \"example.org\",\n\t\tUDP:            true,\n\t})\n\tif err != nil {\n\t\tassert.FailNow(t, err.Error())\n\t}\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n\n// TODO: fix udp test\nfunc TestMihomo_VlessXTLS(t *testing.T) {\n\tcfg := &container.Config{\n\t\tImage:        ImageXray,\n\t\tExposedPorts: defaultExposedPorts,\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t\tBinds: []string{\n\t\t\tfmt.Sprintf(\"%s:/etc/xray/config.json\", C.Path.Resolve(\"vless-xtls.json\")),\n\t\t\tfmt.Sprintf(\"%s:/etc/ssl/v2ray/fullchain.pem\", C.Path.Resolve(\"example.org.pem\")),\n\t\t\tfmt.Sprintf(\"%s:/etc/ssl/v2ray/privkey.pem\", C.Path.Resolve(\"example.org-key.pem\")),\n\t\t},\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"vless-xtls\")\n\tif err != nil {\n\t\tassert.FailNow(t, err.Error())\n\t}\n\tdefer cleanContainer(id)\n\n\tproxy, err := outbound.NewVless(outbound.VlessOption{\n\t\tName:           \"vless\",\n\t\tServer:         localIP.String(),\n\t\tPort:           10002,\n\t\tUUID:           \"b831381d-6324-4d53-ad4f-8cda48b30811\",\n\t\tTLS:            true,\n\t\tSkipCertVerify: true,\n\t\tServerName:     \"example.org\",\n\t\tUDP:            true,\n\t\tFlow:           \"xtls-rprx-direct\",\n\t})\n\tif err != nil {\n\t\tassert.FailNow(t, err.Error())\n\t}\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n\n// TODO: fix udp test\nfunc TestMihomo_VlessWS(t *testing.T) {\n\tcfg := &container.Config{\n\t\tImage:        ImageVmess,\n\t\tExposedPorts: defaultExposedPorts,\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t\tBinds: []string{\n\t\t\tfmt.Sprintf(\"%s:/etc/v2ray/config.json\", C.Path.Resolve(\"vless-ws.json\")),\n\t\t\tfmt.Sprintf(\"%s:/etc/ssl/v2ray/fullchain.pem\", C.Path.Resolve(\"example.org.pem\")),\n\t\t\tfmt.Sprintf(\"%s:/etc/ssl/v2ray/privkey.pem\", C.Path.Resolve(\"example.org-key.pem\")),\n\t\t},\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"vless-ws\")\n\tif err != nil {\n\t\tassert.FailNow(t, err.Error())\n\t}\n\tdefer cleanContainer(id)\n\n\tproxy, err := outbound.NewVless(outbound.VlessOption{\n\t\tName:           \"vless\",\n\t\tServer:         localIP.String(),\n\t\tPort:           10002,\n\t\tUUID:           \"b831381d-6324-4d53-ad4f-8cda48b30811\",\n\t\tTLS:            true,\n\t\tSkipCertVerify: true,\n\t\tServerName:     \"example.org\",\n\t\tNetwork:        \"ws\",\n\t\tUDP:            true,\n\t})\n\tif err != nil {\n\t\tassert.FailNow(t, err.Error())\n\t}\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n"
  },
  {
    "path": "core/Clash.Meta/test/vmess_test.go",
    "content": "package main\n\nimport (\n\t\"fmt\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/docker/docker/api/types/container\"\n\t\"github.com/metacubex/mihomo/adapter/outbound\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestMihomo_Vmess(t *testing.T) {\n\tconfigPath := C.Path.Resolve(\"vmess.json\")\n\n\tcfg := &container.Config{\n\t\tImage:        ImageVmess,\n\t\tExposedPorts: defaultExposedPorts,\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t\tBinds:        []string{fmt.Sprintf(\"%s:/etc/v2ray/config.json\", configPath)},\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"vmess\")\n\trequire.NoError(t, err)\n\n\tt.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewVmess(outbound.VmessOption{\n\t\tName:   \"vmess\",\n\t\tServer: localIP.String(),\n\t\tPort:   10002,\n\t\tUUID:   \"b831381d-6324-4d53-ad4f-8cda48b30811\",\n\t\tCipher: \"auto\",\n\t\tUDP:    true,\n\t})\n\trequire.NoError(t, err)\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n\nfunc TestMihomo_VmessAuthenticatedLength(t *testing.T) {\n\tconfigPath := C.Path.Resolve(\"vmess.json\")\n\n\tcfg := &container.Config{\n\t\tImage:        ImageVmess,\n\t\tExposedPorts: defaultExposedPorts,\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t\tBinds:        []string{fmt.Sprintf(\"%s:/etc/v2ray/config.json\", configPath)},\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"vmess\")\n\trequire.NoError(t, err)\n\n\tt.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewVmess(outbound.VmessOption{\n\t\tName:                \"vmess\",\n\t\tServer:              localIP.String(),\n\t\tPort:                10002,\n\t\tUUID:                \"b831381d-6324-4d53-ad4f-8cda48b30811\",\n\t\tCipher:              \"auto\",\n\t\tUDP:                 true,\n\t\tAuthenticatedLength: true,\n\t})\n\trequire.NoError(t, err)\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n\nfunc TestMihomo_VmessPacketAddr(t *testing.T) {\n\tconfigPath := C.Path.Resolve(\"vmess.json\")\n\n\tcfg := &container.Config{\n\t\tImage:        ImageVmessLatest,\n\t\tExposedPorts: defaultExposedPorts,\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t\tBinds:        []string{fmt.Sprintf(\"%s:/etc/v2ray/config.json\", configPath)},\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"vmess\")\n\trequire.NoError(t, err)\n\n\tt.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewVmess(outbound.VmessOption{\n\t\tName:       \"vmess\",\n\t\tServer:     localIP.String(),\n\t\tPort:       10002,\n\t\tUUID:       \"b831381d-6324-4d53-ad4f-8cda48b30811\",\n\t\tCipher:     \"auto\",\n\t\tUDP:        true,\n\t\tPacketAddr: true,\n\t})\n\trequire.NoError(t, err)\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n\nfunc TestMihomo_VmessTLS(t *testing.T) {\n\tcfg := &container.Config{\n\t\tImage:        ImageVmess,\n\t\tExposedPorts: defaultExposedPorts,\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t\tBinds: []string{\n\t\t\tfmt.Sprintf(\"%s:/etc/v2ray/config.json\", C.Path.Resolve(\"vmess-tls.json\")),\n\t\t\tfmt.Sprintf(\"%s:/etc/ssl/v2ray/fullchain.pem\", C.Path.Resolve(\"example.org.pem\")),\n\t\t\tfmt.Sprintf(\"%s:/etc/ssl/v2ray/privkey.pem\", C.Path.Resolve(\"example.org-key.pem\")),\n\t\t},\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"vmess-tls\")\n\trequire.NoError(t, err)\n\tt.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewVmess(outbound.VmessOption{\n\t\tName:           \"vmess\",\n\t\tServer:         localIP.String(),\n\t\tPort:           10002,\n\t\tUUID:           \"b831381d-6324-4d53-ad4f-8cda48b30811\",\n\t\tCipher:         \"auto\",\n\t\tTLS:            true,\n\t\tSkipCertVerify: true,\n\t\tServerName:     \"example.org\",\n\t\tUDP:            true,\n\t})\n\trequire.NoError(t, err)\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n\nfunc TestMihomo_VmessHTTP2(t *testing.T) {\n\tcfg := &container.Config{\n\t\tImage:        ImageVmess,\n\t\tExposedPorts: defaultExposedPorts,\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t\tBinds: []string{\n\t\t\tfmt.Sprintf(\"%s:/etc/v2ray/config.json\", C.Path.Resolve(\"vmess-http2.json\")),\n\t\t\tfmt.Sprintf(\"%s:/etc/ssl/v2ray/fullchain.pem\", C.Path.Resolve(\"example.org.pem\")),\n\t\t\tfmt.Sprintf(\"%s:/etc/ssl/v2ray/privkey.pem\", C.Path.Resolve(\"example.org-key.pem\")),\n\t\t},\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"vmess-http2\")\n\trequire.NoError(t, err)\n\tt.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewVmess(outbound.VmessOption{\n\t\tName:           \"vmess\",\n\t\tServer:         localIP.String(),\n\t\tPort:           10002,\n\t\tUUID:           \"b831381d-6324-4d53-ad4f-8cda48b30811\",\n\t\tCipher:         \"auto\",\n\t\tNetwork:        \"h2\",\n\t\tTLS:            true,\n\t\tSkipCertVerify: true,\n\t\tServerName:     \"example.org\",\n\t\tUDP:            true,\n\t\tHTTP2Opts: outbound.HTTP2Options{\n\t\t\tHost: []string{\"example.org\"},\n\t\t\tPath: \"/test\",\n\t\t},\n\t})\n\trequire.NoError(t, err)\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n\nfunc TestMihomo_VmessHTTP(t *testing.T) {\n\tcfg := &container.Config{\n\t\tImage:        ImageVmess,\n\t\tExposedPorts: defaultExposedPorts,\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t\tBinds: []string{\n\t\t\tfmt.Sprintf(\"%s:/etc/v2ray/config.json\", C.Path.Resolve(\"vmess-http.json\")),\n\t\t},\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"vmess-http\")\n\trequire.NoError(t, err)\n\tt.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewVmess(outbound.VmessOption{\n\t\tName:    \"vmess\",\n\t\tServer:  localIP.String(),\n\t\tPort:    10002,\n\t\tUUID:    \"b831381d-6324-4d53-ad4f-8cda48b30811\",\n\t\tCipher:  \"auto\",\n\t\tNetwork: \"http\",\n\t\tUDP:     true,\n\t\tHTTPOpts: outbound.HTTPOptions{\n\t\t\tMethod: \"GET\",\n\t\t\tPath:   []string{\"/\"},\n\t\t\tHeaders: map[string][]string{\n\t\t\t\t\"Host\": {\"www.amazon.com\"},\n\t\t\t\t\"User-Agent\": {\n\t\t\t\t\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36 Edg/84.0.522.49\",\n\t\t\t\t},\n\t\t\t\t\"Accept-Encoding\": {\n\t\t\t\t\t\"gzip, deflate\",\n\t\t\t\t},\n\t\t\t\t\"Connection\": {\n\t\t\t\t\t\"keep-alive\",\n\t\t\t\t},\n\t\t\t\t\"Pragma\": {\"no-cache\"},\n\t\t\t},\n\t\t},\n\t})\n\trequire.NoError(t, err)\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n\nfunc TestMihomo_VmessWebsocket(t *testing.T) {\n\tcfg := &container.Config{\n\t\tImage:        ImageVmess,\n\t\tExposedPorts: defaultExposedPorts,\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t\tBinds: []string{\n\t\t\tfmt.Sprintf(\"%s:/etc/v2ray/config.json\", C.Path.Resolve(\"vmess-ws.json\")),\n\t\t},\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"vmess-ws\")\n\trequire.NoError(t, err)\n\tt.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewVmess(outbound.VmessOption{\n\t\tName:    \"vmess\",\n\t\tServer:  localIP.String(),\n\t\tPort:    10002,\n\t\tUUID:    \"b831381d-6324-4d53-ad4f-8cda48b30811\",\n\t\tCipher:  \"auto\",\n\t\tNetwork: \"ws\",\n\t\tUDP:     true,\n\t})\n\trequire.NoError(t, err)\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n\nfunc TestMihomo_VmessWebsocketTLS(t *testing.T) {\n\tcfg := &container.Config{\n\t\tImage:        ImageVmess,\n\t\tExposedPorts: defaultExposedPorts,\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t\tBinds: []string{\n\t\t\tfmt.Sprintf(\"%s:/etc/v2ray/config.json\", C.Path.Resolve(\"vmess-ws-tls.json\")),\n\t\t\tfmt.Sprintf(\"%s:/etc/ssl/v2ray/fullchain.pem\", C.Path.Resolve(\"example.org.pem\")),\n\t\t\tfmt.Sprintf(\"%s:/etc/ssl/v2ray/privkey.pem\", C.Path.Resolve(\"example.org-key.pem\")),\n\t\t},\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"vmess-ws\")\n\trequire.NoError(t, err)\n\tt.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewVmess(outbound.VmessOption{\n\t\tName:           \"vmess\",\n\t\tServer:         localIP.String(),\n\t\tPort:           10002,\n\t\tUUID:           \"b831381d-6324-4d53-ad4f-8cda48b30811\",\n\t\tCipher:         \"auto\",\n\t\tNetwork:        \"ws\",\n\t\tTLS:            true,\n\t\tSkipCertVerify: true,\n\t\tUDP:            true,\n\t})\n\trequire.NoError(t, err)\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n\nfunc TestMihomo_VmessGrpc(t *testing.T) {\n\tcfg := &container.Config{\n\t\tImage:        ImageVmess,\n\t\tExposedPorts: defaultExposedPorts,\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t\tBinds: []string{\n\t\t\tfmt.Sprintf(\"%s:/etc/v2ray/config.json\", C.Path.Resolve(\"vmess-grpc.json\")),\n\t\t\tfmt.Sprintf(\"%s:/etc/ssl/v2ray/fullchain.pem\", C.Path.Resolve(\"example.org.pem\")),\n\t\t\tfmt.Sprintf(\"%s:/etc/ssl/v2ray/privkey.pem\", C.Path.Resolve(\"example.org-key.pem\")),\n\t\t},\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"vmess-grpc\")\n\trequire.NoError(t, err)\n\tt.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewVmess(outbound.VmessOption{\n\t\tName:           \"vmess\",\n\t\tServer:         localIP.String(),\n\t\tPort:           10002,\n\t\tUUID:           \"b831381d-6324-4d53-ad4f-8cda48b30811\",\n\t\tCipher:         \"auto\",\n\t\tNetwork:        \"grpc\",\n\t\tTLS:            true,\n\t\tSkipCertVerify: true,\n\t\tUDP:            true,\n\t\tServerName:     \"example.org\",\n\t\tGrpcOpts: outbound.GrpcOptions{\n\t\t\tGrpcServiceName: \"example!\",\n\t\t},\n\t})\n\trequire.NoError(t, err)\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n\nfunc TestMihomo_VmessWebsocket0RTT(t *testing.T) {\n\tcfg := &container.Config{\n\t\tImage:        ImageVmess,\n\t\tExposedPorts: defaultExposedPorts,\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t\tBinds: []string{\n\t\t\tfmt.Sprintf(\"%s:/etc/v2ray/config.json\", C.Path.Resolve(\"vmess-ws-0rtt.json\")),\n\t\t},\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"vmess-ws-0rtt\")\n\trequire.NoError(t, err)\n\tt.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewVmess(outbound.VmessOption{\n\t\tName:       \"vmess\",\n\t\tServer:     localIP.String(),\n\t\tPort:       10002,\n\t\tUUID:       \"b831381d-6324-4d53-ad4f-8cda48b30811\",\n\t\tCipher:     \"auto\",\n\t\tNetwork:    \"ws\",\n\t\tUDP:        true,\n\t\tServerName: \"example.org\",\n\t\tWSOpts: outbound.WSOptions{\n\t\t\tMaxEarlyData:        2048,\n\t\t\tEarlyDataHeaderName: \"Sec-WebSocket-Protocol\",\n\t\t},\n\t})\n\trequire.NoError(t, err)\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n\nfunc TestMihomo_VmessWebsocketXray0RTT(t *testing.T) {\n\tcfg := &container.Config{\n\t\tImage:        ImageXray,\n\t\tExposedPorts: defaultExposedPorts,\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t\tBinds: []string{\n\t\t\tfmt.Sprintf(\"%s:/etc/xray/config.json\", C.Path.Resolve(\"vmess-ws-0rtt.json\")),\n\t\t},\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"vmess-xray-ws-0rtt\")\n\trequire.NoError(t, err)\n\tt.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewVmess(outbound.VmessOption{\n\t\tName:       \"vmess\",\n\t\tServer:     localIP.String(),\n\t\tPort:       10002,\n\t\tUUID:       \"b831381d-6324-4d53-ad4f-8cda48b30811\",\n\t\tCipher:     \"auto\",\n\t\tNetwork:    \"ws\",\n\t\tUDP:        true,\n\t\tServerName: \"example.org\",\n\t\tWSOpts: outbound.WSOptions{\n\t\t\tPath: \"/?ed=2048\",\n\t\t},\n\t})\n\trequire.NoError(t, err)\n\n\ttime.Sleep(waitTime)\n\ttestSuit(t, proxy)\n}\n\nfunc Benchmark_Vmess(b *testing.B) {\n\tconfigPath := C.Path.Resolve(\"vmess.json\")\n\n\tcfg := &container.Config{\n\t\tImage:        ImageVmess,\n\t\tExposedPorts: defaultExposedPorts,\n\t}\n\thostCfg := &container.HostConfig{\n\t\tPortBindings: defaultPortBindings,\n\t\tBinds:        []string{fmt.Sprintf(\"%s:/etc/v2ray/config.json\", configPath)},\n\t}\n\n\tid, err := startContainer(cfg, hostCfg, \"vmess-bench\")\n\trequire.NoError(b, err)\n\n\tb.Cleanup(func() {\n\t\tcleanContainer(id)\n\t})\n\n\tproxy, err := outbound.NewVmess(outbound.VmessOption{\n\t\tName:    \"vmess\",\n\t\tServer:  localIP.String(),\n\t\tPort:    10002,\n\t\tUUID:    \"b831381d-6324-4d53-ad4f-8cda48b30811\",\n\t\tCipher:  \"auto\",\n\t\tAlterID: 0,\n\t\tUDP:     true,\n\t})\n\trequire.NoError(b, err)\n\n\ttime.Sleep(waitTime)\n\tbenchmarkProxy(b, proxy)\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/anytls/client.go",
    "content": "package anytls\n\nimport (\n\t\"context\"\n\t\"crypto/sha256\"\n\t\"encoding/binary\"\n\t\"net\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/buf\"\n\t\"github.com/metacubex/mihomo/transport/anytls/padding\"\n\t\"github.com/metacubex/mihomo/transport/anytls/session\"\n\t\"github.com/metacubex/mihomo/transport/vmess\"\n\n\tM \"github.com/metacubex/sing/common/metadata\"\n\tN \"github.com/metacubex/sing/common/network\"\n)\n\ntype ClientConfig struct {\n\tPassword                 string\n\tIdleSessionCheckInterval time.Duration\n\tIdleSessionTimeout       time.Duration\n\tMinIdleSession           int\n\tServer                   M.Socksaddr\n\tDialer                   N.Dialer\n\tTLSConfig                *vmess.TLSConfig\n}\n\ntype Client struct {\n\tpasswordSha256 []byte\n\ttlsConfig      *vmess.TLSConfig\n\tdialer         N.Dialer\n\tserver         M.Socksaddr\n\tsessionClient  *session.Client\n\tpadding        atomic.Pointer[padding.PaddingFactory]\n}\n\nfunc NewClient(ctx context.Context, config ClientConfig) *Client {\n\tpw := sha256.Sum256([]byte(config.Password))\n\tc := &Client{\n\t\tpasswordSha256: pw[:],\n\t\ttlsConfig:      config.TLSConfig,\n\t\tdialer:         config.Dialer,\n\t\tserver:         config.Server,\n\t}\n\t// Initialize the padding state of this client\n\tpadding.UpdatePaddingScheme(padding.DefaultPaddingScheme, &c.padding)\n\tc.sessionClient = session.NewClient(ctx, c.createOutboundTLSConnection, &c.padding, config.IdleSessionCheckInterval, config.IdleSessionTimeout, config.MinIdleSession)\n\treturn c\n}\n\nfunc (c *Client) CreateProxy(ctx context.Context, destination M.Socksaddr) (net.Conn, error) {\n\tconn, err := c.sessionClient.CreateStream(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\terr = M.SocksaddrSerializer.WriteAddrPort(conn, destination)\n\tif err != nil {\n\t\tconn.Close()\n\t\treturn nil, err\n\t}\n\treturn conn, nil\n}\n\nfunc (c *Client) createOutboundTLSConnection(ctx context.Context) (net.Conn, error) {\n\tconn, err := c.dialer.DialContext(ctx, N.NetworkTCP, c.server)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tb := buf.NewPacket()\n\tdefer b.Release()\n\n\tb.Write(c.passwordSha256)\n\tvar paddingLen int\n\tif pad := c.padding.Load().GenerateRecordPayloadSizes(0); len(pad) > 0 {\n\t\tpaddingLen = pad[0]\n\t}\n\tbinary.BigEndian.PutUint16(b.Extend(2), uint16(paddingLen))\n\tif paddingLen > 0 {\n\t\tb.WriteZeroN(paddingLen)\n\t}\n\n\ttlsConn, err := vmess.StreamTLSConn(ctx, conn, c.tlsConfig)\n\tif err != nil {\n\t\tconn.Close()\n\t\treturn nil, err\n\t}\n\n\t_, err = b.WriteTo(tlsConn)\n\tif err != nil {\n\t\ttlsConn.Close()\n\t\treturn nil, err\n\t}\n\treturn tlsConn, nil\n}\n\nfunc (h *Client) Close() error {\n\treturn h.sessionClient.Close()\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/anytls/padding/padding.go",
    "content": "package padding\n\nimport (\n\t\"crypto/md5\"\n\t\"crypto/rand\"\n\t\"fmt\"\n\t\"math/big\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync/atomic\"\n\n\t\"github.com/metacubex/mihomo/transport/anytls/util\"\n)\n\nconst CheckMark = -1\n\nvar DefaultPaddingScheme = []byte(`stop=8\n0=30-30\n1=100-400\n2=400-500,c,500-1000,c,500-1000,c,500-1000,c,500-1000\n3=9-9,500-1000\n4=500-1000\n5=500-1000\n6=500-1000\n7=500-1000`)\n\ntype PaddingFactory struct {\n\tscheme    util.StringMap\n\tRawScheme []byte\n\tStop      uint32\n\tMd5       string\n}\n\nfunc UpdatePaddingScheme(rawScheme []byte, to *atomic.Pointer[PaddingFactory]) bool {\n\tif p := NewPaddingFactory(rawScheme); p != nil {\n\t\tto.Store(p)\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc NewPaddingFactory(rawScheme []byte) *PaddingFactory {\n\tp := &PaddingFactory{\n\t\tRawScheme: rawScheme,\n\t\tMd5:       fmt.Sprintf(\"%x\", md5.Sum(rawScheme)),\n\t}\n\tscheme := util.StringMapFromBytes(rawScheme)\n\tif len(scheme) == 0 {\n\t\treturn nil\n\t}\n\tif stop, err := strconv.Atoi(scheme[\"stop\"]); err == nil {\n\t\tp.Stop = uint32(stop)\n\t} else {\n\t\treturn nil\n\t}\n\tp.scheme = scheme\n\treturn p\n}\n\nfunc (p *PaddingFactory) GenerateRecordPayloadSizes(pkt uint32) (pktSizes []int) {\n\tif s, ok := p.scheme[strconv.Itoa(int(pkt))]; ok {\n\t\tsRanges := strings.Split(s, \",\")\n\t\tfor _, sRange := range sRanges {\n\t\t\tsRangeMinMax := strings.Split(sRange, \"-\")\n\t\t\tif len(sRangeMinMax) == 2 {\n\t\t\t\t_min, err := strconv.ParseInt(sRangeMinMax[0], 10, 64)\n\t\t\t\tif err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\t_max, err := strconv.ParseInt(sRangeMinMax[1], 10, 64)\n\t\t\t\tif err != nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif _min > _max {\n\t\t\t\t\t_min, _max = _max, _min\n\t\t\t\t}\n\t\t\t\tif _min <= 0 || _max <= 0 {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t\tif _min == _max {\n\t\t\t\t\tpktSizes = append(pktSizes, int(_min))\n\t\t\t\t} else {\n\t\t\t\t\ti, _ := rand.Int(rand.Reader, big.NewInt(_max-_min))\n\t\t\t\t\tpktSizes = append(pktSizes, int(i.Int64()+_min))\n\t\t\t\t}\n\t\t\t} else if sRange == \"c\" {\n\t\t\t\tpktSizes = append(pktSizes, CheckMark)\n\t\t\t}\n\t\t}\n\t}\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/anytls/pipe/deadline.go",
    "content": "package pipe\n\nimport (\n\t\"sync\"\n\t\"time\"\n)\n\n// PipeDeadline is an abstraction for handling timeouts.\ntype PipeDeadline struct {\n\tmu     sync.Mutex // Guards timer and cancel\n\ttimer  *time.Timer\n\tcancel chan struct{} // Must be non-nil\n}\n\nfunc MakePipeDeadline() PipeDeadline {\n\treturn PipeDeadline{cancel: make(chan struct{})}\n}\n\n// Set sets the point in time when the deadline will time out.\n// A timeout event is signaled by closing the channel returned by waiter.\n// Once a timeout has occurred, the deadline can be refreshed by specifying a\n// t value in the future.\n//\n// A zero value for t prevents timeout.\nfunc (d *PipeDeadline) Set(t time.Time) {\n\td.mu.Lock()\n\tdefer d.mu.Unlock()\n\n\tif d.timer != nil && !d.timer.Stop() {\n\t\t<-d.cancel // Wait for the timer callback to finish and close cancel\n\t}\n\td.timer = nil\n\n\t// Time is zero, then there is no deadline.\n\tclosed := isClosedChan(d.cancel)\n\tif t.IsZero() {\n\t\tif closed {\n\t\t\td.cancel = make(chan struct{})\n\t\t}\n\t\treturn\n\t}\n\n\t// Time in the future, setup a timer to cancel in the future.\n\tif dur := time.Until(t); dur > 0 {\n\t\tif closed {\n\t\t\td.cancel = make(chan struct{})\n\t\t}\n\t\td.timer = time.AfterFunc(dur, func() {\n\t\t\tclose(d.cancel)\n\t\t})\n\t\treturn\n\t}\n\n\t// Time in the past, so close immediately.\n\tif !closed {\n\t\tclose(d.cancel)\n\t}\n}\n\n// Wait returns a channel that is closed when the deadline is exceeded.\nfunc (d *PipeDeadline) Wait() chan struct{} {\n\td.mu.Lock()\n\tdefer d.mu.Unlock()\n\treturn d.cancel\n}\n\nfunc isClosedChan(c <-chan struct{}) bool {\n\tselect {\n\tcase <-c:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/anytls/pipe/io_pipe.go",
    "content": "// Copyright 2009 The Go Authors. All rights reserved.\n// Use of this source code is governed by a BSD-style\n// license that can be found in the LICENSE file.\n\n// Pipe adapter to connect code expecting an io.Reader\n// with code expecting an io.Writer.\n\npackage pipe\n\nimport (\n\t\"io\"\n\t\"os\"\n\t\"sync\"\n\t\"time\"\n)\n\n// onceError is an object that will only store an error once.\ntype onceError struct {\n\tsync.Mutex // guards following\n\terr        error\n}\n\nfunc (a *onceError) Store(err error) {\n\ta.Lock()\n\tdefer a.Unlock()\n\tif a.err != nil {\n\t\treturn\n\t}\n\ta.err = err\n}\nfunc (a *onceError) Load() error {\n\ta.Lock()\n\tdefer a.Unlock()\n\treturn a.err\n}\n\n// A pipe is the shared pipe structure underlying PipeReader and PipeWriter.\ntype pipe struct {\n\twrMu sync.Mutex // Serializes Write operations\n\twrCh chan []byte\n\trdCh chan int\n\n\tonce sync.Once // Protects closing done\n\tdone chan struct{}\n\trerr onceError\n\twerr onceError\n\n\treadDeadline  PipeDeadline\n\twriteDeadline PipeDeadline\n}\n\nfunc (p *pipe) read(b []byte) (n int, err error) {\n\tselect {\n\tcase <-p.done:\n\t\treturn 0, p.readCloseError()\n\tcase <-p.readDeadline.Wait():\n\t\treturn 0, os.ErrDeadlineExceeded\n\tdefault:\n\t}\n\n\tselect {\n\tcase bw := <-p.wrCh:\n\t\tnr := copy(b, bw)\n\t\tp.rdCh <- nr\n\t\treturn nr, nil\n\tcase <-p.done:\n\t\treturn 0, p.readCloseError()\n\tcase <-p.readDeadline.Wait():\n\t\treturn 0, os.ErrDeadlineExceeded\n\t}\n}\n\nfunc (p *pipe) closeRead(err error) error {\n\tif err == nil {\n\t\terr = io.ErrClosedPipe\n\t}\n\tp.rerr.Store(err)\n\tp.once.Do(func() { close(p.done) })\n\treturn nil\n}\n\nfunc (p *pipe) write(b []byte) (n int, err error) {\n\tselect {\n\tcase <-p.done:\n\t\treturn 0, p.writeCloseError()\n\tcase <-p.writeDeadline.Wait():\n\t\treturn 0, os.ErrDeadlineExceeded\n\tdefault:\n\t\tp.wrMu.Lock()\n\t\tdefer p.wrMu.Unlock()\n\t}\n\n\tfor once := true; once || len(b) > 0; once = false {\n\t\tselect {\n\t\tcase p.wrCh <- b:\n\t\t\tnw := <-p.rdCh\n\t\t\tb = b[nw:]\n\t\t\tn += nw\n\t\tcase <-p.done:\n\t\t\treturn n, p.writeCloseError()\n\t\tcase <-p.writeDeadline.Wait():\n\t\t\treturn n, os.ErrDeadlineExceeded\n\t\t}\n\t}\n\treturn n, nil\n}\n\nfunc (p *pipe) closeWrite(err error) error {\n\tif err == nil {\n\t\terr = io.EOF\n\t}\n\tp.werr.Store(err)\n\tp.once.Do(func() { close(p.done) })\n\treturn nil\n}\n\n// readCloseError is considered internal to the pipe type.\nfunc (p *pipe) readCloseError() error {\n\trerr := p.rerr.Load()\n\tif werr := p.werr.Load(); rerr == nil && werr != nil {\n\t\treturn werr\n\t}\n\treturn io.ErrClosedPipe\n}\n\n// writeCloseError is considered internal to the pipe type.\nfunc (p *pipe) writeCloseError() error {\n\twerr := p.werr.Load()\n\tif rerr := p.rerr.Load(); werr == nil && rerr != nil {\n\t\treturn rerr\n\t}\n\treturn io.ErrClosedPipe\n}\n\n// A PipeReader is the read half of a pipe.\ntype PipeReader struct{ pipe }\n\n// Read implements the standard Read interface:\n// it reads data from the pipe, blocking until a writer\n// arrives or the write end is closed.\n// If the write end is closed with an error, that error is\n// returned as err; otherwise err is EOF.\nfunc (r *PipeReader) Read(data []byte) (n int, err error) {\n\treturn r.pipe.read(data)\n}\n\n// Close closes the reader; subsequent writes to the\n// write half of the pipe will return the error [ErrClosedPipe].\nfunc (r *PipeReader) Close() error {\n\treturn r.CloseWithError(nil)\n}\n\n// CloseWithError closes the reader; subsequent writes\n// to the write half of the pipe will return the error err.\n//\n// CloseWithError never overwrites the previous error if it exists\n// and always returns nil.\nfunc (r *PipeReader) CloseWithError(err error) error {\n\treturn r.pipe.closeRead(err)\n}\n\n// A PipeWriter is the write half of a pipe.\ntype PipeWriter struct{ r PipeReader }\n\n// Write implements the standard Write interface:\n// it writes data to the pipe, blocking until one or more readers\n// have consumed all the data or the read end is closed.\n// If the read end is closed with an error, that err is\n// returned as err; otherwise err is [ErrClosedPipe].\nfunc (w *PipeWriter) Write(data []byte) (n int, err error) {\n\treturn w.r.pipe.write(data)\n}\n\n// Close closes the writer; subsequent reads from the\n// read half of the pipe will return no bytes and EOF.\nfunc (w *PipeWriter) Close() error {\n\treturn w.CloseWithError(nil)\n}\n\n// CloseWithError closes the writer; subsequent reads from the\n// read half of the pipe will return no bytes and the error err,\n// or EOF if err is nil.\n//\n// CloseWithError never overwrites the previous error if it exists\n// and always returns nil.\nfunc (w *PipeWriter) CloseWithError(err error) error {\n\treturn w.r.pipe.closeWrite(err)\n}\n\n// Pipe creates a synchronous in-memory pipe.\n// It can be used to connect code expecting an [io.Reader]\n// with code expecting an [io.Writer].\n//\n// Reads and Writes on the pipe are matched one to one\n// except when multiple Reads are needed to consume a single Write.\n// That is, each Write to the [PipeWriter] blocks until it has satisfied\n// one or more Reads from the [PipeReader] that fully consume\n// the written data.\n// The data is copied directly from the Write to the corresponding\n// Read (or Reads); there is no internal buffering.\n//\n// It is safe to call Read and Write in parallel with each other or with Close.\n// Parallel calls to Read and parallel calls to Write are also safe:\n// the individual calls will be gated sequentially.\n//\n// Added SetReadDeadline and SetWriteDeadline methods based on `io.Pipe`.\nfunc Pipe() (*PipeReader, *PipeWriter) {\n\tpw := &PipeWriter{r: PipeReader{pipe: pipe{\n\t\twrCh:          make(chan []byte),\n\t\trdCh:          make(chan int),\n\t\tdone:          make(chan struct{}),\n\t\treadDeadline:  MakePipeDeadline(),\n\t\twriteDeadline: MakePipeDeadline(),\n\t}}}\n\treturn &pw.r, pw\n}\n\nfunc (p *PipeReader) SetReadDeadline(t time.Time) error {\n\tif isClosedChan(p.done) {\n\t\treturn io.ErrClosedPipe\n\t}\n\tp.readDeadline.Set(t)\n\treturn nil\n}\n\nfunc (p *PipeWriter) SetWriteDeadline(t time.Time) error {\n\tif isClosedChan(p.r.done) {\n\t\treturn io.ErrClosedPipe\n\t}\n\tp.r.writeDeadline.Set(t)\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/anytls/session/client.go",
    "content": "package session\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"math\"\n\t\"net\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/transport/anytls/padding\"\n\t\"github.com/metacubex/mihomo/transport/anytls/skiplist\"\n\t\"github.com/metacubex/mihomo/transport/anytls/util\"\n)\n\ntype Client struct {\n\tdie       context.Context\n\tdieCancel context.CancelFunc\n\n\tdialOut util.DialOutFunc\n\n\tsessionCounter atomic.Uint64\n\n\tidleSession     *skiplist.SkipList[uint64, *Session]\n\tidleSessionLock sync.Mutex\n\n\tsessions     map[uint64]*Session\n\tsessionsLock sync.Mutex\n\n\tpadding *atomic.Pointer[padding.PaddingFactory]\n\n\tidleSessionTimeout time.Duration\n\tminIdleSession     int\n}\n\nfunc NewClient(ctx context.Context, dialOut util.DialOutFunc, _padding *atomic.Pointer[padding.PaddingFactory], idleSessionCheckInterval, idleSessionTimeout time.Duration, minIdleSession int) *Client {\n\tc := &Client{\n\t\tsessions:           make(map[uint64]*Session),\n\t\tdialOut:            dialOut,\n\t\tpadding:            _padding,\n\t\tidleSessionTimeout: idleSessionTimeout,\n\t\tminIdleSession:     minIdleSession,\n\t}\n\tif idleSessionCheckInterval <= time.Second*5 {\n\t\tidleSessionCheckInterval = time.Second * 30\n\t}\n\tif c.idleSessionTimeout <= time.Second*5 {\n\t\tc.idleSessionTimeout = time.Second * 30\n\t}\n\tc.die, c.dieCancel = context.WithCancel(ctx)\n\tc.idleSession = skiplist.NewSkipList[uint64, *Session]()\n\tutil.StartRoutine(c.die, idleSessionCheckInterval, c.idleCleanup)\n\treturn c\n}\n\nfunc (c *Client) CreateStream(ctx context.Context) (net.Conn, error) {\n\tselect {\n\tcase <-c.die.Done():\n\t\treturn nil, io.ErrClosedPipe\n\tdefault:\n\t}\n\n\tvar session *Session\n\tvar stream *Stream\n\tvar err error\n\n\tsession = c.getIdleSession()\n\tif session == nil {\n\t\tsession, err = c.createSession(ctx)\n\t}\n\tif session == nil {\n\t\treturn nil, fmt.Errorf(\"failed to create session: %w\", err)\n\t}\n\tstream, err = session.OpenStream()\n\tif err != nil {\n\t\tsession.Close()\n\t\treturn nil, fmt.Errorf(\"failed to create stream: %w\", err)\n\t}\n\n\tstream.dieHook = func() {\n\t\t// If Session is not closed, put this Stream to pool\n\t\tif !session.IsClosed() {\n\t\t\tselect {\n\t\t\tcase <-c.die.Done():\n\t\t\t\t// Now client has been closed\n\t\t\t\tgo session.Close()\n\t\t\tdefault:\n\t\t\t\tc.idleSessionLock.Lock()\n\t\t\t\tsession.idleSince = time.Now()\n\t\t\t\tc.idleSession.Insert(math.MaxUint64-session.seq, session)\n\t\t\t\tc.idleSessionLock.Unlock()\n\t\t\t}\n\t\t}\n\t}\n\n\treturn stream, nil\n}\n\nfunc (c *Client) getIdleSession() (idle *Session) {\n\tc.idleSessionLock.Lock()\n\tif !c.idleSession.IsEmpty() {\n\t\tit := c.idleSession.Iterate()\n\t\tidle = it.Value()\n\t\tc.idleSession.Remove(it.Key())\n\t}\n\tc.idleSessionLock.Unlock()\n\treturn\n}\n\nfunc (c *Client) createSession(ctx context.Context) (*Session, error) {\n\tunderlying, err := c.dialOut(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tsession := NewClientSession(underlying, c.padding)\n\tsession.seq = c.sessionCounter.Add(1)\n\tsession.dieHook = func() {\n\t\tc.idleSessionLock.Lock()\n\t\tc.idleSession.Remove(math.MaxUint64 - session.seq)\n\t\tc.idleSessionLock.Unlock()\n\n\t\tc.sessionsLock.Lock()\n\t\tdelete(c.sessions, session.seq)\n\t\tc.sessionsLock.Unlock()\n\t}\n\n\tc.sessionsLock.Lock()\n\tc.sessions[session.seq] = session\n\tc.sessionsLock.Unlock()\n\n\tsession.Run()\n\treturn session, nil\n}\n\nfunc (c *Client) Close() error {\n\tc.dieCancel()\n\n\tc.sessionsLock.Lock()\n\tsessionToClose := make([]*Session, 0, len(c.sessions))\n\tfor _, session := range c.sessions {\n\t\tsessionToClose = append(sessionToClose, session)\n\t}\n\tc.sessions = make(map[uint64]*Session)\n\tc.sessionsLock.Unlock()\n\n\tfor _, session := range sessionToClose {\n\t\tsession.Close()\n\t}\n\n\treturn nil\n}\n\nfunc (c *Client) idleCleanup() {\n\tc.idleCleanupExpTime(time.Now().Add(-c.idleSessionTimeout))\n}\n\nfunc (c *Client) idleCleanupExpTime(expTime time.Time) {\n\tactiveCount := 0\n\tsessionToClose := make([]*Session, 0, c.idleSession.Len())\n\n\tc.idleSessionLock.Lock()\n\tit := c.idleSession.Iterate()\n\tfor it.IsNotEnd() {\n\t\tsession := it.Value()\n\t\tkey := it.Key()\n\t\tit.MoveToNext()\n\n\t\tif !session.idleSince.Before(expTime) {\n\t\t\tactiveCount++\n\t\t\tcontinue\n\t\t}\n\n\t\tif activeCount < c.minIdleSession {\n\t\t\tsession.idleSince = time.Now()\n\t\t\tactiveCount++\n\t\t\tcontinue\n\t\t}\n\n\t\tsessionToClose = append(sessionToClose, session)\n\t\tc.idleSession.Remove(key)\n\t}\n\tc.idleSessionLock.Unlock()\n\n\tfor _, session := range sessionToClose {\n\t\tsession.Close()\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/anytls/session/frame.go",
    "content": "package session\n\nimport (\n\t\"encoding/binary\"\n)\n\nconst ( // cmds\n\tcmdWaste               = 0 // Paddings\n\tcmdSYN                 = 1 // stream open\n\tcmdPSH                 = 2 // data push\n\tcmdFIN                 = 3 // stream close, a.k.a EOF mark\n\tcmdSettings            = 4 // Settings (Client send to Server)\n\tcmdAlert               = 5 // Alert\n\tcmdUpdatePaddingScheme = 6 // update padding scheme\n\t// Since version 2\n\tcmdSYNACK         = 7  // Server reports to the client that the stream has been opened\n\tcmdHeartRequest   = 8  // Keep alive command\n\tcmdHeartResponse  = 9  // Keep alive command\n\tcmdServerSettings = 10 // Settings (Server send to client)\n)\n\nconst (\n\theaderOverHeadSize = 1 + 4 + 2\n)\n\n// frame defines a packet from or to be multiplexed into a single connection\ntype frame struct {\n\tcmd  byte   // 1\n\tsid  uint32 // 4\n\tdata []byte // 2 + len(data)\n}\n\nfunc newFrame(cmd byte, sid uint32) frame {\n\treturn frame{cmd: cmd, sid: sid}\n}\n\ntype rawHeader [headerOverHeadSize]byte\n\nfunc (h rawHeader) Cmd() byte {\n\treturn h[0]\n}\n\nfunc (h rawHeader) StreamID() uint32 {\n\treturn binary.BigEndian.Uint32(h[1:])\n}\n\nfunc (h rawHeader) Length() uint16 {\n\treturn binary.BigEndian.Uint16(h[5:])\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/anytls/session/session.go",
    "content": "package session\n\nimport (\n\t\"crypto/md5\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"runtime/debug\"\n\t\"strconv\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/buf\"\n\t\"github.com/metacubex/mihomo/common/pool\"\n\t\"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/transport/anytls/padding\"\n\t\"github.com/metacubex/mihomo/transport/anytls/util\"\n)\n\ntype Session struct {\n\tconn     net.Conn\n\tconnLock sync.Mutex\n\n\tstreams    map[uint32]*Stream\n\tstreamId   atomic.Uint32\n\tstreamLock sync.RWMutex\n\n\tdieOnce sync.Once\n\tdie     chan struct{}\n\tdieHook func()\n\n\tsynDone     func()\n\tsynDoneLock sync.Mutex\n\n\t// pool\n\tseq       uint64\n\tidleSince time.Time\n\tpadding   *atomic.Pointer[padding.PaddingFactory]\n\n\tpeerVersion byte\n\n\t// client\n\tisClient    bool\n\tsendPadding bool\n\tbuffering   bool\n\tbuffer      []byte\n\tpktCounter  atomic.Uint32\n\n\t// server\n\tonNewStream func(stream *Stream)\n}\n\nfunc NewClientSession(conn net.Conn, _padding *atomic.Pointer[padding.PaddingFactory]) *Session {\n\ts := &Session{\n\t\tconn:        conn,\n\t\tisClient:    true,\n\t\tsendPadding: true,\n\t\tpadding:     _padding,\n\t}\n\ts.die = make(chan struct{})\n\ts.streams = make(map[uint32]*Stream)\n\treturn s\n}\n\nfunc NewServerSession(conn net.Conn, onNewStream func(stream *Stream), _padding *atomic.Pointer[padding.PaddingFactory]) *Session {\n\ts := &Session{\n\t\tconn:        conn,\n\t\tonNewStream: onNewStream,\n\t\tpadding:     _padding,\n\t}\n\ts.die = make(chan struct{})\n\ts.streams = make(map[uint32]*Stream)\n\treturn s\n}\n\nfunc (s *Session) Run() {\n\tif !s.isClient {\n\t\ts.recvLoop()\n\t\treturn\n\t}\n\n\tsettings := util.StringMap{\n\t\t\"v\":           \"2\",\n\t\t\"client\":      \"mihomo/\" + constant.Version,\n\t\t\"padding-md5\": s.padding.Load().Md5,\n\t}\n\tf := newFrame(cmdSettings, 0)\n\tf.data = settings.ToBytes()\n\ts.buffering = true\n\ts.writeControlFrame(f)\n\n\tgo s.recvLoop()\n}\n\n// IsClosed does a safe check to see if we have shutdown\nfunc (s *Session) IsClosed() bool {\n\tselect {\n\tcase <-s.die:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\n// Close is used to close the session and all streams.\nfunc (s *Session) Close() error {\n\tvar once bool\n\ts.dieOnce.Do(func() {\n\t\tclose(s.die)\n\t\tonce = true\n\t})\n\tif once {\n\t\tif s.dieHook != nil {\n\t\t\ts.dieHook()\n\t\t\ts.dieHook = nil\n\t\t}\n\t\ts.streamLock.Lock()\n\t\tfor _, stream := range s.streams {\n\t\t\tstream.closeLocally()\n\t\t}\n\t\ts.streams = make(map[uint32]*Stream)\n\t\ts.streamLock.Unlock()\n\t\treturn s.conn.Close()\n\t} else {\n\t\treturn io.ErrClosedPipe\n\t}\n}\n\n// OpenStream is used to create a new stream for CLIENT\nfunc (s *Session) OpenStream() (*Stream, error) {\n\tif s.IsClosed() {\n\t\treturn nil, io.ErrClosedPipe\n\t}\n\n\tsid := s.streamId.Add(1)\n\tstream := newStream(sid, s)\n\n\tif sid >= 2 && s.peerVersion >= 2 {\n\t\ts.synDoneLock.Lock()\n\t\tif s.synDone != nil {\n\t\t\ts.synDone()\n\t\t}\n\t\ts.synDone = util.NewDeadlineWatcher(time.Second*3, func() {\n\t\t\ts.Close()\n\t\t})\n\t\ts.synDoneLock.Unlock()\n\t}\n\n\tif _, err := s.writeControlFrame(newFrame(cmdSYN, sid)); err != nil {\n\t\treturn nil, err\n\t}\n\n\ts.buffering = false // proxy Write it's SocksAddr to flush the buffer\n\n\ts.streamLock.Lock()\n\tdefer s.streamLock.Unlock()\n\tselect {\n\tcase <-s.die:\n\t\treturn nil, io.ErrClosedPipe\n\tdefault:\n\t\ts.streams[sid] = stream\n\t\treturn stream, nil\n\t}\n}\n\nfunc (s *Session) recvLoop() error {\n\tdefer func() {\n\t\tif r := recover(); r != nil {\n\t\t\tlog.Errorln(\"[BUG] %v %s\", r, string(debug.Stack()))\n\t\t}\n\t}()\n\tdefer s.Close()\n\n\tvar receivedSettingsFromClient bool\n\tvar hdr rawHeader\n\n\tfor {\n\t\tif s.IsClosed() {\n\t\t\treturn io.ErrClosedPipe\n\t\t}\n\t\t// read header first\n\t\tif _, err := io.ReadFull(s.conn, hdr[:]); err == nil {\n\t\t\tsid := hdr.StreamID()\n\t\t\tswitch hdr.Cmd() {\n\t\t\tcase cmdPSH:\n\t\t\t\tif hdr.Length() > 0 {\n\t\t\t\t\tbuffer := pool.Get(int(hdr.Length()))\n\t\t\t\t\tif _, err := io.ReadFull(s.conn, buffer); err == nil {\n\t\t\t\t\t\ts.streamLock.RLock()\n\t\t\t\t\t\tstream, ok := s.streams[sid]\n\t\t\t\t\t\ts.streamLock.RUnlock()\n\t\t\t\t\t\tif ok {\n\t\t\t\t\t\t\tstream.pipeW.Write(buffer)\n\t\t\t\t\t\t}\n\t\t\t\t\t\tpool.Put(buffer)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tpool.Put(buffer)\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase cmdSYN: // should be server only\n\t\t\t\tif !s.isClient && !receivedSettingsFromClient {\n\t\t\t\t\tf := newFrame(cmdAlert, 0)\n\t\t\t\t\tf.data = []byte(\"client did not send its settings\")\n\t\t\t\t\ts.writeControlFrame(f)\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\ts.streamLock.Lock()\n\t\t\t\tif _, ok := s.streams[sid]; !ok {\n\t\t\t\t\tstream := newStream(sid, s)\n\t\t\t\t\ts.streams[sid] = stream\n\t\t\t\t\tgo func() {\n\t\t\t\t\t\tif s.onNewStream != nil {\n\t\t\t\t\t\t\ts.onNewStream(stream)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tstream.Close()\n\t\t\t\t\t\t}\n\t\t\t\t\t}()\n\t\t\t\t}\n\t\t\t\ts.streamLock.Unlock()\n\t\t\tcase cmdSYNACK: // should be client only\n\t\t\t\ts.synDoneLock.Lock()\n\t\t\t\tif s.synDone != nil {\n\t\t\t\t\ts.synDone()\n\t\t\t\t\ts.synDone = nil\n\t\t\t\t}\n\t\t\t\ts.synDoneLock.Unlock()\n\t\t\t\tif hdr.Length() > 0 {\n\t\t\t\t\tbuffer := pool.Get(int(hdr.Length()))\n\t\t\t\t\tif _, err := io.ReadFull(s.conn, buffer); err != nil {\n\t\t\t\t\t\tpool.Put(buffer)\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t\t// report error\n\t\t\t\t\ts.streamLock.RLock()\n\t\t\t\t\tstream, ok := s.streams[sid]\n\t\t\t\t\ts.streamLock.RUnlock()\n\t\t\t\t\tif ok {\n\t\t\t\t\t\tstream.closeWithError(fmt.Errorf(\"remote: %s\", string(buffer)))\n\t\t\t\t\t}\n\t\t\t\t\tpool.Put(buffer)\n\t\t\t\t}\n\t\t\tcase cmdFIN:\n\t\t\t\ts.streamLock.Lock()\n\t\t\t\tstream, ok := s.streams[sid]\n\t\t\t\tdelete(s.streams, sid)\n\t\t\t\ts.streamLock.Unlock()\n\t\t\t\tif ok {\n\t\t\t\t\tstream.closeLocally()\n\t\t\t\t}\n\t\t\tcase cmdWaste:\n\t\t\t\tif hdr.Length() > 0 {\n\t\t\t\t\tbuffer := pool.Get(int(hdr.Length()))\n\t\t\t\t\tif _, err := io.ReadFull(s.conn, buffer); err != nil {\n\t\t\t\t\t\tpool.Put(buffer)\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t\tpool.Put(buffer)\n\t\t\t\t}\n\t\t\tcase cmdSettings:\n\t\t\t\tif hdr.Length() > 0 {\n\t\t\t\t\tbuffer := pool.Get(int(hdr.Length()))\n\t\t\t\t\tif _, err := io.ReadFull(s.conn, buffer); err != nil {\n\t\t\t\t\t\tpool.Put(buffer)\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t\tif !s.isClient {\n\t\t\t\t\t\treceivedSettingsFromClient = true\n\t\t\t\t\t\tm := util.StringMapFromBytes(buffer)\n\t\t\t\t\t\tpaddingF := s.padding.Load()\n\t\t\t\t\t\tif m[\"padding-md5\"] != paddingF.Md5 {\n\t\t\t\t\t\t\tf := newFrame(cmdUpdatePaddingScheme, 0)\n\t\t\t\t\t\t\tf.data = paddingF.RawScheme\n\t\t\t\t\t\t\t_, err = s.writeControlFrame(f)\n\t\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\t\tpool.Put(buffer)\n\t\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// check client's version\n\t\t\t\t\t\tif v, err := strconv.Atoi(m[\"v\"]); err == nil && v >= 2 {\n\t\t\t\t\t\t\ts.peerVersion = byte(v)\n\t\t\t\t\t\t\t// send cmdServerSettings\n\t\t\t\t\t\t\tf := newFrame(cmdServerSettings, 0)\n\t\t\t\t\t\t\tf.data = util.StringMap{\n\t\t\t\t\t\t\t\t\"v\": \"2\",\n\t\t\t\t\t\t\t}.ToBytes()\n\t\t\t\t\t\t\t_, err = s.writeControlFrame(f)\n\t\t\t\t\t\t\tif err != nil {\n\t\t\t\t\t\t\t\tpool.Put(buffer)\n\t\t\t\t\t\t\t\treturn err\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tpool.Put(buffer)\n\t\t\t\t}\n\t\t\tcase cmdAlert:\n\t\t\t\tif hdr.Length() > 0 {\n\t\t\t\t\tbuffer := pool.Get(int(hdr.Length()))\n\t\t\t\t\tif _, err := io.ReadFull(s.conn, buffer); err != nil {\n\t\t\t\t\t\tpool.Put(buffer)\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t\tif s.isClient {\n\t\t\t\t\t\tlog.Errorln(\"[Alert from server] %s\", string(buffer))\n\t\t\t\t\t}\n\t\t\t\t\tpool.Put(buffer)\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\tcase cmdUpdatePaddingScheme:\n\t\t\t\tif hdr.Length() > 0 {\n\t\t\t\t\t// `rawScheme` Do not use buffer to prevent subsequent misuse\n\t\t\t\t\trawScheme := make([]byte, int(hdr.Length()))\n\t\t\t\t\tif _, err := io.ReadFull(s.conn, rawScheme); err != nil {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t\tif s.isClient {\n\t\t\t\t\t\tif padding.UpdatePaddingScheme(rawScheme, s.padding) {\n\t\t\t\t\t\t\tlog.Debugln(\"[Update padding succeed] %x\\n\", md5.Sum(rawScheme))\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tlog.Warnln(\"[Update padding failed] %x\\n\", md5.Sum(rawScheme))\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase cmdHeartRequest:\n\t\t\t\tif _, err := s.writeControlFrame(newFrame(cmdHeartResponse, sid)); err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\tcase cmdHeartResponse:\n\t\t\t\t// Active keepalive checking is not implemented yet\n\t\t\t\tbreak\n\t\t\tcase cmdServerSettings:\n\t\t\t\tif hdr.Length() > 0 {\n\t\t\t\t\tbuffer := pool.Get(int(hdr.Length()))\n\t\t\t\t\tif _, err := io.ReadFull(s.conn, buffer); err != nil {\n\t\t\t\t\t\tpool.Put(buffer)\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t\tif s.isClient {\n\t\t\t\t\t\t// check server's version\n\t\t\t\t\t\tm := util.StringMapFromBytes(buffer)\n\t\t\t\t\t\tif v, err := strconv.Atoi(m[\"v\"]); err == nil {\n\t\t\t\t\t\t\ts.peerVersion = byte(v)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tpool.Put(buffer)\n\t\t\t\t}\n\t\t\tdefault:\n\t\t\t\t// I don't know what command it is (can't have data)\n\t\t\t}\n\t\t} else {\n\t\t\treturn err\n\t\t}\n\t}\n}\n\nfunc (s *Session) streamClosed(sid uint32) error {\n\tif s.IsClosed() {\n\t\treturn io.ErrClosedPipe\n\t}\n\t_, err := s.writeControlFrame(newFrame(cmdFIN, sid))\n\ts.streamLock.Lock()\n\tdelete(s.streams, sid)\n\ts.streamLock.Unlock()\n\treturn err\n}\n\nfunc (s *Session) writeDataFrame(sid uint32, data []byte) (int, error) {\n\tdataLen := len(data)\n\n\tbuffer := buf.NewSize(dataLen + headerOverHeadSize)\n\tbuffer.WriteByte(cmdPSH)\n\tbinary.BigEndian.PutUint32(buffer.Extend(4), sid)\n\tbinary.BigEndian.PutUint16(buffer.Extend(2), uint16(dataLen))\n\tbuffer.Write(data)\n\t_, err := s.writeConn(buffer.Bytes())\n\tbuffer.Release()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn dataLen, nil\n}\n\nfunc (s *Session) writeControlFrame(frame frame) (int, error) {\n\tdataLen := len(frame.data)\n\n\tbuffer := buf.NewSize(dataLen + headerOverHeadSize)\n\tbuffer.WriteByte(frame.cmd)\n\tbinary.BigEndian.PutUint32(buffer.Extend(4), frame.sid)\n\tbinary.BigEndian.PutUint16(buffer.Extend(2), uint16(dataLen))\n\tbuffer.Write(frame.data)\n\n\ts.conn.SetWriteDeadline(time.Now().Add(time.Second * 5))\n\n\t_, err := s.writeConn(buffer.Bytes())\n\tbuffer.Release()\n\tif err != nil {\n\t\ts.Close()\n\t\treturn 0, err\n\t}\n\n\ts.conn.SetWriteDeadline(time.Time{})\n\n\treturn dataLen, nil\n}\n\nfunc (s *Session) writeConn(b []byte) (n int, err error) {\n\ts.connLock.Lock()\n\tdefer s.connLock.Unlock()\n\n\tif s.buffering {\n\t\ts.buffer = append(s.buffer, b...)\n\t\treturn len(b), nil\n\t} else if len(s.buffer) > 0 {\n\t\tb = append(s.buffer, b...)\n\t\ts.buffer = nil\n\t}\n\n\t// calulate & send padding\n\tif s.sendPadding {\n\t\tpkt := s.pktCounter.Add(1)\n\t\tpaddingF := s.padding.Load()\n\t\tif pkt < paddingF.Stop {\n\t\t\tpktSizes := paddingF.GenerateRecordPayloadSizes(pkt)\n\t\t\tfor _, l := range pktSizes {\n\t\t\t\tremainPayloadLen := len(b)\n\t\t\t\tif l == padding.CheckMark {\n\t\t\t\t\tif remainPayloadLen == 0 {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t} else {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif remainPayloadLen > l { // this packet is all payload\n\t\t\t\t\t_, err = s.conn.Write(b[:l])\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn 0, err\n\t\t\t\t\t}\n\t\t\t\t\tn += l\n\t\t\t\t\tb = b[l:]\n\t\t\t\t} else if remainPayloadLen > 0 { // this packet contains padding and the last part of payload\n\t\t\t\t\tpaddingLen := l - remainPayloadLen - headerOverHeadSize\n\t\t\t\t\tif paddingLen > 0 {\n\t\t\t\t\t\tpadding := make([]byte, headerOverHeadSize+paddingLen)\n\t\t\t\t\t\tpadding[0] = cmdWaste\n\t\t\t\t\t\tbinary.BigEndian.PutUint32(padding[1:5], 0)\n\t\t\t\t\t\tbinary.BigEndian.PutUint16(padding[5:7], uint16(paddingLen))\n\t\t\t\t\t\tb = append(b, padding...)\n\t\t\t\t\t}\n\t\t\t\t\t_, err = s.conn.Write(b)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn 0, err\n\t\t\t\t\t}\n\t\t\t\t\tn += remainPayloadLen\n\t\t\t\t\tb = nil\n\t\t\t\t} else { // this packet is all padding\n\t\t\t\t\tpadding := make([]byte, headerOverHeadSize+l)\n\t\t\t\t\tpadding[0] = cmdWaste\n\t\t\t\t\tbinary.BigEndian.PutUint32(padding[1:5], 0)\n\t\t\t\t\tbinary.BigEndian.PutUint16(padding[5:7], uint16(l))\n\t\t\t\t\t_, err = s.conn.Write(padding)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\treturn 0, err\n\t\t\t\t\t}\n\t\t\t\t\tb = nil\n\t\t\t\t}\n\t\t\t}\n\t\t\t// maybe still remain payload to write\n\t\t\tif len(b) == 0 {\n\t\t\t\treturn\n\t\t\t} else {\n\t\t\t\tn2, err := s.conn.Write(b)\n\t\t\t\treturn n + n2, err\n\t\t\t}\n\t\t} else {\n\t\t\ts.sendPadding = false\n\t\t}\n\t}\n\n\treturn s.conn.Write(b)\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/anytls/session/stream.go",
    "content": "package session\n\nimport (\n\t\"io\"\n\t\"net\"\n\t\"os\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/transport/anytls/pipe\"\n)\n\n// Stream implements net.Conn\ntype Stream struct {\n\tid uint32\n\n\tsess *Session\n\n\tpipeR         *pipe.PipeReader\n\tpipeW         *pipe.PipeWriter\n\twriteDeadline pipe.PipeDeadline\n\n\tdieOnce sync.Once\n\tdieHook func()\n\tdieErr  error\n\n\treportOnce sync.Once\n}\n\n// newStream initiates a Stream struct\nfunc newStream(id uint32, sess *Session) *Stream {\n\ts := new(Stream)\n\ts.id = id\n\ts.sess = sess\n\ts.pipeR, s.pipeW = pipe.Pipe()\n\ts.writeDeadline = pipe.MakePipeDeadline()\n\treturn s\n}\n\n// Read implements net.Conn\nfunc (s *Stream) Read(b []byte) (n int, err error) {\n\tn, err = s.pipeR.Read(b)\n\tif n == 0 && s.dieErr != nil {\n\t\terr = s.dieErr\n\t}\n\treturn\n}\n\n// Write implements net.Conn\nfunc (s *Stream) Write(b []byte) (n int, err error) {\n\tselect {\n\tcase <-s.writeDeadline.Wait():\n\t\treturn 0, os.ErrDeadlineExceeded\n\tdefault:\n\t}\n\tif s.dieErr != nil {\n\t\treturn 0, s.dieErr\n\t}\n\tn, err = s.sess.writeDataFrame(s.id, b)\n\treturn\n}\n\n// Close implements net.Conn\nfunc (s *Stream) Close() error {\n\treturn s.closeWithError(io.ErrClosedPipe)\n}\n\n// closeLocally only closes Stream and don't notify remote peer\nfunc (s *Stream) closeLocally() {\n\tvar once bool\n\ts.dieOnce.Do(func() {\n\t\ts.dieErr = net.ErrClosed\n\t\ts.pipeR.Close()\n\t\tonce = true\n\t})\n\tif once {\n\t\tif s.dieHook != nil {\n\t\t\ts.dieHook()\n\t\t\ts.dieHook = nil\n\t\t}\n\t}\n}\n\nfunc (s *Stream) closeWithError(err error) error {\n\tvar once bool\n\ts.dieOnce.Do(func() {\n\t\ts.dieErr = err\n\t\ts.pipeR.Close()\n\t\tonce = true\n\t})\n\tif once {\n\t\tif s.dieHook != nil {\n\t\t\ts.dieHook()\n\t\t\ts.dieHook = nil\n\t\t}\n\t\treturn s.sess.streamClosed(s.id)\n\t} else {\n\t\treturn s.dieErr\n\t}\n}\n\nfunc (s *Stream) SetReadDeadline(t time.Time) error {\n\treturn s.pipeR.SetReadDeadline(t)\n}\n\nfunc (s *Stream) SetWriteDeadline(t time.Time) error {\n\ts.writeDeadline.Set(t)\n\treturn nil\n}\n\nfunc (s *Stream) SetDeadline(t time.Time) error {\n\ts.SetWriteDeadline(t)\n\treturn s.SetReadDeadline(t)\n}\n\n// LocalAddr satisfies net.Conn interface\nfunc (s *Stream) LocalAddr() net.Addr {\n\tif ts, ok := s.sess.conn.(interface {\n\t\tLocalAddr() net.Addr\n\t}); ok {\n\t\treturn ts.LocalAddr()\n\t}\n\treturn nil\n}\n\n// RemoteAddr satisfies net.Conn interface\nfunc (s *Stream) RemoteAddr() net.Addr {\n\tif ts, ok := s.sess.conn.(interface {\n\t\tRemoteAddr() net.Addr\n\t}); ok {\n\t\treturn ts.RemoteAddr()\n\t}\n\treturn nil\n}\n\n// HandshakeFailure should be called when Server fail to create outbound proxy\nfunc (s *Stream) HandshakeFailure(err error) error {\n\tvar once bool\n\ts.reportOnce.Do(func() {\n\t\tonce = true\n\t})\n\tif once && err != nil && s.sess.peerVersion >= 2 {\n\t\tf := newFrame(cmdSYNACK, s.id)\n\t\tf.data = []byte(err.Error())\n\t\tif _, err := s.sess.writeControlFrame(f); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// HandshakeSuccess should be called when Server success to create outbound proxy\nfunc (s *Stream) HandshakeSuccess() error {\n\tvar once bool\n\ts.reportOnce.Do(func() {\n\t\tonce = true\n\t})\n\tif once && s.sess.peerVersion >= 2 {\n\t\tif _, err := s.sess.writeControlFrame(newFrame(cmdSYNACK, s.id)); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/anytls/skiplist/contianer.go",
    "content": "package skiplist\n\n// Container is a holder object that stores a collection of other objects.\ntype Container interface {\n\tIsEmpty() bool // IsEmpty checks if the container has no elements.\n\tLen() int      // Len returns the number of elements in the container.\n\tClear()        // Clear erases all elements from the container. After this call, Len() returns zero.\n}\n\n// Map is a associative container that contains key-value pairs with unique keys.\ntype Map[K any, V any] interface {\n\tContainer\n\tHas(K) bool                        // Checks whether the container contains element with specific key.\n\tFind(K) *V                         // Finds element with specific key.\n\tInsert(K, V)                       // Inserts a key-value pair in to the container or replace existing value.\n\tRemove(K) bool                     // Remove element with specific key.\n\tForEach(func(K, V))                // Iterate the container.\n\tForEachIf(func(K, V) bool)         // Iterate the container, stops when the callback returns false.\n\tForEachMutable(func(K, *V))        // Iterate the container, *V is mutable.\n\tForEachMutableIf(func(K, *V) bool) // Iterate the container, *V is mutable, stops when the callback returns false.\n}\n\n// Set is a containers that store unique elements.\ntype Set[K any] interface {\n\tContainer\n\tHas(K) bool             // Checks whether the container contains element with specific key.\n\tInsert(K)               // Inserts a key-value pair in to the container or replace existing value.\n\tInsertN(...K)           // Inserts multiple key-value pairs in to the container or replace existing value.\n\tRemove(K) bool          // Remove element with specific key.\n\tRemoveN(...K)           // Remove multiple elements with specific keys.\n\tForEach(func(K))        // Iterate the container.\n\tForEachIf(func(K) bool) // Iterate the container, stops when the callback returns false.\n}\n\n// Iterator is the interface for container's iterator.\ntype Iterator[T any] interface {\n\tIsNotEnd() bool // Whether it is point to the end of the range.\n\tMoveToNext()    // Let it point to the next element.\n\tValue() T       // Return the value of current element.\n}\n\n// MapIterator is the interface for map's iterator.\ntype MapIterator[K any, V any] interface {\n\tIterator[V]\n\tKey() K // The key of the element\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/anytls/skiplist/skiplist.go",
    "content": "package skiplist\n\n// This implementation is based on https://github.com/liyue201/gostl/tree/master/ds/skiplist\n// (many thanks), added many optimizations, such as:\n//\n//  - adaptive level\n//  - lesser search for prevs when key already exists.\n//  - reduce memory allocations\n//  - richer interface.\n//\n// etc.\n\nimport (\n\t\"math/bits\"\n\t\"math/rand\"\n\t\"time\"\n)\n\nconst (\n\tskipListMaxLevel = 40\n)\n\n// SkipList is a probabilistic data structure that seem likely to supplant balanced trees as the\n// implementation method of choice for many applications. Skip list algorithms have the same\n// asymptotic expected time bounds as balanced trees and are simpler, faster and use less space.\n//\n// See https://en.wikipedia.org/wiki/Skip_list for more details.\ntype SkipList[K any, V any] struct {\n\tlevel int                // Current level, may increase dynamically during insertion\n\tlen   int                // Total elements numner in the skiplist.\n\thead  skipListNode[K, V] // head.next[level] is the head of each level.\n\t// This cache is used to save the previous nodes when modifying the skip list to avoid\n\t// allocating memory each time it is called.\n\tprevsCache []*skipListNode[K, V]\n\trander     *rand.Rand\n\timpl       skipListImpl[K, V]\n}\n\n// NewSkipList creates a new SkipList for Ordered key type.\nfunc NewSkipList[K Ordered, V any]() *SkipList[K, V] {\n\tsl := skipListOrdered[K, V]{}\n\tsl.init()\n\tsl.impl = (skipListImpl[K, V])(&sl)\n\treturn &sl.SkipList\n}\n\n// NewSkipListFromMap creates a new SkipList from a map.\nfunc NewSkipListFromMap[K Ordered, V any](m map[K]V) *SkipList[K, V] {\n\tsl := NewSkipList[K, V]()\n\tfor k, v := range m {\n\t\tsl.Insert(k, v)\n\t}\n\treturn sl\n}\n\n// NewSkipListFunc creates a new SkipList with specified compare function keyCmp.\nfunc NewSkipListFunc[K any, V any](keyCmp CompareFn[K]) *SkipList[K, V] {\n\tsl := skipListFunc[K, V]{}\n\tsl.init()\n\tsl.keyCmp = keyCmp\n\tsl.impl = skipListImpl[K, V](&sl)\n\treturn &sl.SkipList\n}\n\n// IsEmpty implements the Container interface.\nfunc (sl *SkipList[K, V]) IsEmpty() bool {\n\treturn sl.len == 0\n}\n\n// Len implements the Container interface.\nfunc (sl *SkipList[K, V]) Len() int {\n\treturn sl.len\n}\n\n// Clear implements the Container interface.\nfunc (sl *SkipList[K, V]) Clear() {\n\tfor i := range sl.head.next {\n\t\tsl.head.next[i] = nil\n\t}\n\tsl.level = 1\n\tsl.len = 0\n}\n\n// Iterate return an iterator to the skiplist.\nfunc (sl *SkipList[K, V]) Iterate() MapIterator[K, V] {\n\treturn &skipListIterator[K, V]{sl.head.next[0], nil}\n}\n\n// Insert inserts a key-value pair into the skiplist.\n// If the key is already in the skip list, it's value will be updated.\nfunc (sl *SkipList[K, V]) Insert(key K, value V) {\n\tnode, prevs := sl.impl.findInsertPoint(key)\n\n\tif node != nil {\n\t\t// Already exist, update the value\n\t\tnode.value = value\n\t\treturn\n\t}\n\n\tlevel := sl.randomLevel()\n\tnode = newSkipListNode(level, key, value)\n\n\tminLevel := level\n\tif sl.level < level {\n\t\tminLevel = sl.level\n\t}\n\tfor i := 0; i < minLevel; i++ {\n\t\tnode.next[i] = prevs[i].next[i]\n\t\tprevs[i].next[i] = node\n\t}\n\n\tif level > sl.level {\n\t\tfor i := sl.level; i < level; i++ {\n\t\t\tsl.head.next[i] = node\n\t\t}\n\t\tsl.level = level\n\t}\n\n\tsl.len++\n}\n\n// Find returns the value associated with the passed key if the key is in the skiplist, otherwise\n// returns nil.\nfunc (sl *SkipList[K, V]) Find(key K) *V {\n\tnode := sl.impl.findNode(key)\n\tif node != nil {\n\t\treturn &node.value\n\t}\n\treturn nil\n}\n\n// Has implement the Map interface.\nfunc (sl *SkipList[K, V]) Has(key K) bool {\n\treturn sl.impl.findNode(key) != nil\n}\n\n// LowerBound returns an iterator to the first element in the skiplist that\n// does not satisfy element < value (i.e. greater or equal to),\n// or a end itetator if no such element is found.\nfunc (sl *SkipList[K, V]) LowerBound(key K) MapIterator[K, V] {\n\treturn &skipListIterator[K, V]{sl.impl.lowerBound(key), nil}\n}\n\n// UpperBound returns an iterator to the first element in the skiplist that\n// does not satisfy value < element (i.e. strictly greater),\n// or a end itetator if no such element is found.\nfunc (sl *SkipList[K, V]) UpperBound(key K) MapIterator[K, V] {\n\treturn &skipListIterator[K, V]{sl.impl.upperBound(key), nil}\n}\n\n// FindRange returns an iterator in range [first, last) (last is not includeed).\nfunc (sl *SkipList[K, V]) FindRange(first, last K) MapIterator[K, V] {\n\treturn &skipListIterator[K, V]{sl.impl.lowerBound(first), sl.impl.upperBound(last)}\n}\n\n// Remove removes the key-value pair associated with the passed key and returns true if the key is\n// in the skiplist, otherwise returns false.\nfunc (sl *SkipList[K, V]) Remove(key K) bool {\n\tnode, prevs := sl.impl.findRemovePoint(key)\n\tif node == nil {\n\t\treturn false\n\t}\n\tfor i, v := range node.next {\n\t\tprevs[i].next[i] = v\n\t}\n\tfor sl.level > 1 && sl.head.next[sl.level-1] == nil {\n\t\tsl.level--\n\t}\n\tsl.len--\n\treturn true\n}\n\n// ForEach implements the Map interface.\nfunc (sl *SkipList[K, V]) ForEach(op func(K, V)) {\n\tfor e := sl.head.next[0]; e != nil; e = e.next[0] {\n\t\top(e.key, e.value)\n\t}\n}\n\n// ForEachMutable implements the Map interface.\nfunc (sl *SkipList[K, V]) ForEachMutable(op func(K, *V)) {\n\tfor e := sl.head.next[0]; e != nil; e = e.next[0] {\n\t\top(e.key, &e.value)\n\t}\n}\n\n// ForEachIf implements the Map interface.\nfunc (sl *SkipList[K, V]) ForEachIf(op func(K, V) bool) {\n\tfor e := sl.head.next[0]; e != nil; e = e.next[0] {\n\t\tif !op(e.key, e.value) {\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// ForEachMutableIf implements the Map interface.\nfunc (sl *SkipList[K, V]) ForEachMutableIf(op func(K, *V) bool) {\n\tfor e := sl.head.next[0]; e != nil; e = e.next[0] {\n\t\tif !op(e.key, &e.value) {\n\t\t\treturn\n\t\t}\n\t}\n}\n\n/// SkipList implementation part.\n\ntype skipListNode[K any, V any] struct {\n\tkey   K\n\tvalue V\n\tnext  []*skipListNode[K, V]\n}\n\n//go:generate bash ./skiplist_newnode_generate.sh skipListMaxLevel skiplist_newnode.go\n// func newSkipListNode[K Ordered, V any](level int, key K, value V) *skipListNode[K, V]\n\ntype skipListIterator[K any, V any] struct {\n\tnode, end *skipListNode[K, V]\n}\n\nfunc (it *skipListIterator[K, V]) IsNotEnd() bool {\n\treturn it.node != it.end\n}\n\nfunc (it *skipListIterator[K, V]) MoveToNext() {\n\tit.node = it.node.next[0]\n}\n\nfunc (it *skipListIterator[K, V]) Key() K {\n\treturn it.node.key\n}\n\nfunc (it *skipListIterator[K, V]) Value() V {\n\treturn it.node.value\n}\n\n// skipListImpl is an interface to provide different implementation for Ordered key or CompareFn.\n//\n// We can use CompareFn to cumpare Ordered keys, but a separated implementation is much faster.\n// We don't make the whole skip list an interface, in order to share the type independented method.\n// And because these methods are called directly without going through the interface, they are also\n// much faster.\ntype skipListImpl[K any, V any] interface {\n\tfindNode(key K) *skipListNode[K, V]\n\tlowerBound(key K) *skipListNode[K, V]\n\tupperBound(key K) *skipListNode[K, V]\n\tfindInsertPoint(key K) (*skipListNode[K, V], []*skipListNode[K, V])\n\tfindRemovePoint(key K) (*skipListNode[K, V], []*skipListNode[K, V])\n}\n\nfunc (sl *SkipList[K, V]) init() {\n\tsl.level = 1\n\t// #nosec G404 -- This is not a security condition\n\tsl.rander = rand.New(rand.NewSource(time.Now().Unix()))\n\tsl.prevsCache = make([]*skipListNode[K, V], skipListMaxLevel)\n\tsl.head.next = make([]*skipListNode[K, V], skipListMaxLevel)\n}\n\nfunc (sl *SkipList[K, V]) randomLevel() int {\n\ttotal := uint64(1)<<uint64(skipListMaxLevel) - 1 // 2^n-1\n\tk := sl.rander.Uint64() % total\n\tlevel := skipListMaxLevel - bits.Len64(k) + 1\n\t// Since levels are randomly generated, most should be less than log2(s.len).\n\t// Then make a limit according to sl.len to avoid unexpectedly large value.\n\tfor level > 3 && 1<<(level-3) > sl.len {\n\t\tlevel--\n\t}\n\n\treturn level\n}\n\n/// skipListOrdered part\n\n// skipListOrdered is the skip list implementation for Ordered types.\ntype skipListOrdered[K Ordered, V any] struct {\n\tSkipList[K, V]\n}\n\nfunc (sl *skipListOrdered[K, V]) findNode(key K) *skipListNode[K, V] {\n\treturn sl.doFindNode(key, true)\n}\n\nfunc (sl *skipListOrdered[K, V]) doFindNode(key K, eq bool) *skipListNode[K, V] {\n\t// This function execute the job of findNode if eq is true, otherwise lowBound.\n\t// Passing the control variable eq is ugly but it's faster than testing node\n\t// again outside the function in findNode.\n\tprev := &sl.head\n\tfor i := sl.level - 1; i >= 0; i-- {\n\t\tfor cur := prev.next[i]; cur != nil; cur = cur.next[i] {\n\t\t\tif cur.key == key {\n\t\t\t\treturn cur\n\t\t\t}\n\t\t\tif cur.key > key {\n\t\t\t\t// All other node in this level must be greater than the key,\n\t\t\t\t// search the next level.\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tprev = cur\n\t\t}\n\t}\n\tif eq {\n\t\treturn nil\n\t}\n\treturn prev.next[0]\n}\n\nfunc (sl *skipListOrdered[K, V]) lowerBound(key K) *skipListNode[K, V] {\n\treturn sl.doFindNode(key, false)\n}\n\nfunc (sl *skipListOrdered[K, V]) upperBound(key K) *skipListNode[K, V] {\n\tnode := sl.lowerBound(key)\n\tif node != nil && node.key == key {\n\t\treturn node.next[0]\n\t}\n\treturn node\n}\n\n// findInsertPoint returns (*node, nil) to the existed node if the key exists,\n// or (nil, []*node) to the previous nodes if the key doesn't exist\nfunc (sl *skipListOrdered[K, V]) findInsertPoint(key K) (*skipListNode[K, V], []*skipListNode[K, V]) {\n\tprevs := sl.prevsCache[0:sl.level]\n\tprev := &sl.head\n\tfor i := sl.level - 1; i >= 0; i-- {\n\t\tfor next := prev.next[i]; next != nil; next = next.next[i] {\n\t\t\tif next.key == key {\n\t\t\t\t// The key is already existed, prevs are useless because no new node insertion.\n\t\t\t\t// stop searching.\n\t\t\t\treturn next, nil\n\t\t\t}\n\t\t\tif next.key > key {\n\t\t\t\t// All other node in this level must be greater than the key,\n\t\t\t\t// search the next level.\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tprev = next\n\t\t}\n\t\tprevs[i] = prev\n\t}\n\treturn nil, prevs\n}\n\n// findRemovePoint finds the node which match the key and it's previous nodes.\nfunc (sl *skipListOrdered[K, V]) findRemovePoint(key K) (*skipListNode[K, V], []*skipListNode[K, V]) {\n\tprevs := sl.findPrevNodes(key)\n\tnode := prevs[0].next[0]\n\tif node == nil || node.key != key {\n\t\treturn nil, nil\n\t}\n\treturn node, prevs\n}\n\nfunc (sl *skipListOrdered[K, V]) findPrevNodes(key K) []*skipListNode[K, V] {\n\tprevs := sl.prevsCache[0:sl.level]\n\tprev := &sl.head\n\tfor i := sl.level - 1; i >= 0; i-- {\n\t\tfor next := prev.next[i]; next != nil; next = next.next[i] {\n\t\t\tif next.key >= key {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tprev = next\n\t\t}\n\t\tprevs[i] = prev\n\t}\n\treturn prevs\n}\n\n/// skipListFunc part\n\n// skipListFunc is the skip list implementation which compare keys with func.\ntype skipListFunc[K any, V any] struct {\n\tSkipList[K, V]\n\tkeyCmp CompareFn[K]\n}\n\nfunc (sl *skipListFunc[K, V]) findNode(key K) *skipListNode[K, V] {\n\tnode := sl.lowerBound(key)\n\tif node != nil && sl.keyCmp(node.key, key) == 0 {\n\t\treturn node\n\t}\n\treturn nil\n}\n\nfunc (sl *skipListFunc[K, V]) lowerBound(key K) *skipListNode[K, V] {\n\tvar prev = &sl.head\n\tfor i := sl.level - 1; i >= 0; i-- {\n\t\tcur := prev.next[i]\n\t\tfor ; cur != nil; cur = cur.next[i] {\n\t\t\tcmpRet := sl.keyCmp(cur.key, key)\n\t\t\tif cmpRet == 0 {\n\t\t\t\treturn cur\n\t\t\t}\n\t\t\tif cmpRet > 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tprev = cur\n\t\t}\n\t}\n\treturn prev.next[0]\n}\n\nfunc (sl *skipListFunc[K, V]) upperBound(key K) *skipListNode[K, V] {\n\tnode := sl.lowerBound(key)\n\tif node != nil && sl.keyCmp(node.key, key) == 0 {\n\t\treturn node.next[0]\n\t}\n\treturn node\n}\n\n// findInsertPoint returns (*node, nil) to the existed node if the key exists,\n// or (nil, []*node) to the previous nodes if the key doesn't exist\nfunc (sl *skipListFunc[K, V]) findInsertPoint(key K) (*skipListNode[K, V], []*skipListNode[K, V]) {\n\tprevs := sl.prevsCache[0:sl.level]\n\tprev := &sl.head\n\tfor i := sl.level - 1; i >= 0; i-- {\n\t\tfor cur := prev.next[i]; cur != nil; cur = cur.next[i] {\n\t\t\tr := sl.keyCmp(cur.key, key)\n\t\t\tif r == 0 {\n\t\t\t\t// The key is already existed, prevs are useless because no new node insertion.\n\t\t\t\t// stop searching.\n\t\t\t\treturn cur, nil\n\t\t\t}\n\t\t\tif r > 0 {\n\t\t\t\t// All other node in this level must be greater than the key,\n\t\t\t\t// search the next level.\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tprev = cur\n\t\t}\n\t\tprevs[i] = prev\n\t}\n\treturn nil, prevs\n}\n\n// findRemovePoint finds the node which match the key and it's previous nodes.\nfunc (sl *skipListFunc[K, V]) findRemovePoint(key K) (*skipListNode[K, V], []*skipListNode[K, V]) {\n\tprevs := sl.findPrevNodes(key)\n\tnode := prevs[0].next[0]\n\tif node == nil || sl.keyCmp(node.key, key) != 0 {\n\t\treturn nil, nil\n\t}\n\treturn node, prevs\n}\n\nfunc (sl *skipListFunc[K, V]) findPrevNodes(key K) []*skipListNode[K, V] {\n\tprevs := sl.prevsCache[0:sl.level]\n\tprev := &sl.head\n\tfor i := sl.level - 1; i >= 0; i-- {\n\t\tfor next := prev.next[i]; next != nil; next = next.next[i] {\n\t\t\tif sl.keyCmp(next.key, key) >= 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tprev = next\n\t\t}\n\t\tprevs[i] = prev\n\t}\n\treturn prevs\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/anytls/skiplist/skiplist_newnode.go",
    "content": "// AUTO GENERATED CODE, DON'T EDIT!!!\n// EDIT skiplist_newnode_generate.sh accordingly.\n\npackage skiplist\n\n// newSkipListNode creates a new node initialized with specified key, value and next slice.\nfunc newSkipListNode[K any, V any](level int, key K, value V) *skipListNode[K, V] {\n\t// For nodes with each levels, point their next slice to the nexts array allocated together,\n\t// which can reduce 1 memory allocation and improve performance.\n\t//\n\t// The generics of the golang doesn't support non-type parameters like in C++,\n\t// so we have to generate it manually.\n\tswitch level {\n\tcase 1:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [1]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 2:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [2]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 3:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [3]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 4:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [4]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 5:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [5]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 6:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [6]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 7:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [7]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 8:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [8]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 9:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [9]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 10:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [10]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 11:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [11]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 12:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [12]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 13:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [13]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 14:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [14]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 15:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [15]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 16:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [16]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 17:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [17]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 18:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [18]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 19:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [19]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 20:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [20]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 21:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [21]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 22:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [22]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 23:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [23]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 24:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [24]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 25:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [25]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 26:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [26]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 27:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [27]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 28:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [28]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 29:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [29]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 30:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [30]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 31:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [31]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 32:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [32]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 33:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [33]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 34:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [34]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 35:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [35]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 36:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [36]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 37:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [37]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 38:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [38]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 39:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [39]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\tcase 40:\n\t\tn := struct {\n\t\t\thead  skipListNode[K, V]\n\t\t\tnexts [40]*skipListNode[K, V]\n\t\t}{head: skipListNode[K, V]{key, value, nil}}\n\t\tn.head.next = n.nexts[:]\n\t\treturn &n.head\n\t}\n\n\tpanic(\"should not reach here\")\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/anytls/skiplist/types.go",
    "content": "package skiplist\n\n// Signed is a constraint that permits any signed integer type.\n// If future releases of Go add new predeclared signed integer types,\n// this constraint will be modified to include them.\ntype Signed interface {\n\t~int | ~int8 | ~int16 | ~int32 | ~int64\n}\n\n// Unsigned is a constraint that permits any unsigned integer type.\n// If future releases of Go add new predeclared unsigned integer types,\n// this constraint will be modified to include them.\ntype Unsigned interface {\n\t~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 | ~uintptr\n}\n\n// Integer is a constraint that permits any integer type.\n// If future releases of Go add new predeclared integer types,\n// this constraint will be modified to include them.\ntype Integer interface {\n\tSigned | Unsigned\n}\n\n// Float is a constraint that permits any floating-point type.\n// If future releases of Go add new predeclared floating-point types,\n// this constraint will be modified to include them.\ntype Float interface {\n\t~float32 | ~float64\n}\n\n// Ordered is a constraint that permits any ordered type: any type\n// that supports the operators < <= >= >.\n// If future releases of Go add new ordered types,\n// this constraint will be modified to include them.\ntype Ordered interface {\n\tInteger | Float | ~string\n}\n\n// Numeric is a constraint that permits any numeric type.\ntype Numeric interface {\n\tInteger | Float\n}\n\n// LessFn is a function that returns whether 'a' is less than 'b'.\ntype LessFn[T any] func(a, b T) bool\n\n// CompareFn is a 3 way compare function that\n// returns 1  if a >  b,\n// returns 0  if a == b,\n// returns -1 if a < b.\ntype CompareFn[T any] func(a, b T) int\n\n// HashFn is a function that returns the hash of 't'.\ntype HashFn[T any] func(t T) uint64\n\n// Equals wraps the '==' operator for comparable types.\nfunc Equals[T comparable](a, b T) bool {\n\treturn a == b\n}\n\n// Less wraps the '<' operator for ordered types.\nfunc Less[T Ordered](a, b T) bool {\n\treturn a < b\n}\n\n// OrderedCompare provide default CompareFn for ordered types.\nfunc OrderedCompare[T Ordered](a, b T) int {\n\tif a < b {\n\t\treturn -1\n\t}\n\tif a > b {\n\t\treturn 1\n\t}\n\treturn 0\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/anytls/util/deadline.go",
    "content": "package util\n\nimport (\n\t\"sync\"\n\t\"time\"\n)\n\nfunc NewDeadlineWatcher(ddl time.Duration, timeOut func()) (done func()) {\n\tt := time.NewTimer(ddl)\n\tcloseCh := make(chan struct{})\n\tgo func() {\n\t\tdefer t.Stop()\n\t\tselect {\n\t\tcase <-closeCh:\n\t\tcase <-t.C:\n\t\t\ttimeOut()\n\t\t}\n\t}()\n\tvar once sync.Once\n\treturn func() {\n\t\tonce.Do(func() {\n\t\t\tclose(closeCh)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/anytls/util/routine.go",
    "content": "package util\n\nimport (\n\t\"context\"\n\t\"runtime/debug\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/log\"\n)\n\nfunc StartRoutine(ctx context.Context, d time.Duration, f func()) {\n\tgo func() {\n\t\tdefer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t\tlog.Errorln(\"[BUG] %v %s\", r, string(debug.Stack()))\n\t\t\t}\n\t\t}()\n\t\tfor {\n\t\t\ttime.Sleep(d)\n\t\t\tf()\n\t\t\tselect {\n\t\t\tcase <-ctx.Done():\n\t\t\t\treturn\n\t\t\tdefault:\n\t\t\t}\n\t\t}\n\t}()\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/anytls/util/string_map.go",
    "content": "package util\n\nimport (\n\t\"strings\"\n)\n\ntype StringMap map[string]string\n\nfunc (s StringMap) ToBytes() []byte {\n\tvar lines []string\n\tfor k, v := range s {\n\t\tlines = append(lines, k+\"=\"+v)\n\t}\n\treturn []byte(strings.Join(lines, \"\\n\"))\n}\n\nfunc StringMapFromBytes(b []byte) StringMap {\n\tvar m = make(StringMap)\n\tvar lines = strings.Split(string(b), \"\\n\")\n\tfor _, line := range lines {\n\t\tv := strings.SplitN(line, \"=\", 2)\n\t\tif len(v) == 2 {\n\t\t\tm[v[0]] = v[1]\n\t\t}\n\t}\n\treturn m\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/anytls/util/type.go",
    "content": "package util\n\nimport (\n\t\"context\"\n\t\"net\"\n)\n\ntype DialOutFunc func(ctx context.Context) (net.Conn, error)\n"
  },
  {
    "path": "core/Clash.Meta/transport/gost-plugin/websocket.go",
    "content": "package gost\n\nimport (\n\t\"context\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/component/ca\"\n\t\"github.com/metacubex/mihomo/component/ech\"\n\t\"github.com/metacubex/mihomo/transport/vmess\"\n\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/smux\"\n\t\"github.com/metacubex/tls\"\n)\n\n// Option is options of gost websocket\ntype Option struct {\n\tHost           string\n\tPort           string\n\tPath           string\n\tHeaders        map[string]string\n\tTLS            bool\n\tECHConfig      *ech.Config\n\tSkipCertVerify bool\n\tFingerprint    string\n\tCertificate    string\n\tPrivateKey     string\n\tMux            bool\n}\n\n// muxConn is a wrapper around smux.Stream that also closes the session when closed\ntype muxConn struct {\n\tnet.Conn\n\tsession *smux.Session\n}\n\nfunc (m *muxConn) Close() error {\n\tstreamErr := m.Conn.Close()\n\tsessionErr := m.session.Close()\n\n\t// Return stream error if there is one, otherwise return session error\n\tif streamErr != nil {\n\t\treturn streamErr\n\t}\n\treturn sessionErr\n}\n\n// NewGostWebsocket return a gost websocket\nfunc NewGostWebsocket(ctx context.Context, conn net.Conn, option *Option) (net.Conn, error) {\n\theader := http.Header{}\n\tfor k, v := range option.Headers {\n\t\theader.Add(k, v)\n\t}\n\n\tconfig := &vmess.WebsocketConfig{\n\t\tHost:      option.Host,\n\t\tPort:      option.Port,\n\t\tPath:      option.Path,\n\t\tECHConfig: option.ECHConfig,\n\t\tHeaders:   header,\n\t}\n\n\tvar err error\n\tif option.TLS {\n\t\tconfig.TLS = true\n\t\tconfig.TLSConfig, err = ca.GetTLSConfig(ca.Option{\n\t\t\tTLSConfig: &tls.Config{\n\t\t\t\tServerName:         option.Host,\n\t\t\t\tInsecureSkipVerify: option.SkipCertVerify,\n\t\t\t\tNextProtos:         []string{\"http/1.1\"},\n\t\t\t},\n\t\t\tFingerprint: option.Fingerprint,\n\t\t\tCertificate: option.Certificate,\n\t\t\tPrivateKey:  option.PrivateKey,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif host := config.Headers.Get(\"Host\"); host != \"\" {\n\t\t\tconfig.TLSConfig.ServerName = host\n\t\t}\n\t}\n\n\tconn, err = vmess.StreamWebsocketConn(ctx, conn, config)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif option.Mux {\n\t\tconfig := smux.DefaultConfig()\n\t\tconfig.KeepAliveDisabled = true\n\n\t\tsession, err := smux.Client(conn, config)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tstream, err := session.OpenStream()\n\t\tif err != nil {\n\t\t\tsession.Close()\n\t\t\treturn nil, err\n\t\t}\n\n\t\treturn &muxConn{\n\t\t\tConn:    stream,\n\t\t\tsession: session,\n\t\t}, nil\n\t}\n\treturn conn, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/gun/gun.go",
    "content": "// Modified from: https://github.com/Qv2ray/gun-lite\n// License: MIT\n\npackage gun\n\nimport (\n\t\"context\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/url\"\n\t\"strings\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/buf\"\n\t\"github.com/metacubex/mihomo/common/httputils\"\n\t\"github.com/metacubex/mihomo/common/pool\"\n\ttlsC \"github.com/metacubex/mihomo/component/tls\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/transport/vmess\"\n\n\t\"github.com/metacubex/http\"\n)\n\nconst (\n\tHttp2NextProtoTLS = \"h2\"\n)\n\nvar (\n\tErrInvalidLength = errors.New(\"invalid length\")\n\tErrSmallBuffer   = errors.New(\"buffer too small\")\n)\n\nvar defaultHeader = http.Header{\n\t\"Content-Type\": []string{\"application/grpc\"},\n\t\"User-Agent\":   []string{\"grpc-go/1.36.0\"},\n}\n\ntype DialFn = func(ctx context.Context, network, addr string) (net.Conn, error)\n\ntype Conn struct {\n\tinitFn func(addr *httputils.NetAddr) (io.ReadCloser, error)\n\twriter io.Writer // writer must not nil\n\tcloser io.Closer\n\thttputils.NetAddr\n\n\tinitOnce sync.Once\n\tinitErr  error\n\treader   io.ReadCloser\n\tremain   int\n\n\tcloseMutex sync.Mutex\n\tclosed     bool\n\tonClose    func()\n\n\t// deadlines\n\tdeadline *time.Timer\n}\n\ntype Config struct {\n\tServiceName  string\n\tUserAgent    string\n\tHost         string\n\tPingInterval int\n}\n\nfunc (g *Conn) initReader() {\n\treader, err := g.initFn(&g.NetAddr)\n\tif err != nil {\n\t\tg.initErr = err\n\t\tif closer, ok := g.writer.(io.Closer); ok {\n\t\t\tcloser.Close()\n\t\t}\n\t\treturn\n\t}\n\n\tg.closeMutex.Lock()\n\tdefer g.closeMutex.Unlock()\n\tif g.closed { // if g.Close() be called between g.initFn(), direct close the initFn returned reader\n\t\t_ = reader.Close()\n\t\tg.initErr = net.ErrClosed\n\t\treturn\n\t}\n\n\tg.reader = reader\n}\n\nfunc (g *Conn) Init() error {\n\tg.initOnce.Do(g.initReader)\n\treturn g.initErr\n}\n\nfunc (g *Conn) Read(b []byte) (n int, err error) {\n\tif err = g.Init(); err != nil {\n\t\treturn\n\t}\n\treturn g.read(b)\n}\n\nfunc (g *Conn) read(b []byte) (n int, err error) {\n\tif g.remain > 0 {\n\t\tsize := g.remain\n\t\tif len(b) < size {\n\t\t\tsize = len(b)\n\t\t}\n\n\t\tn, err = g.reader.Read(b[:size])\n\t\tg.remain -= n\n\t\treturn\n\t}\n\n\t// 0x00 grpclength(uint32) 0x0A uleb128 payload\n\tvar discard [6]byte\n\t_, err = io.ReadFull(g.reader, discard[:])\n\tif err != nil {\n\t\tif err == io.ErrUnexpectedEOF {\n\t\t\terr = io.EOF\n\t\t}\n\t\treturn 0, err\n\t}\n\n\tprotobufPayloadLen, err := ReadUVariant(g.reader)\n\tif err != nil {\n\t\treturn 0, ErrInvalidLength\n\t}\n\tg.remain = int(protobufPayloadLen)\n\treturn g.read(b)\n}\n\nfunc (g *Conn) Write(b []byte) (n int, err error) {\n\tdataLen := len(b)\n\tvarLen := UVarintLen(uint64(dataLen))\n\tbuf := pool.Get(5 + 1 + varLen + dataLen)\n\tdefer pool.Put(buf)\n\t_ = buf[6] // bounds check hint to compiler\n\tbuf[0] = 0x00\n\tbinary.BigEndian.PutUint32(buf[1:5], uint32(1+varLen+dataLen))\n\tbuf[5] = 0x0A\n\tbinary.PutUvarint(buf[6:], uint64(dataLen))\n\tcopy(buf[6+varLen:], b)\n\n\t_, err = g.writer.Write(buf)\n\tif err == io.ErrClosedPipe {\n\t\tif initErr := g.Init(); initErr != nil {\n\t\t\terr = initErr\n\t\t}\n\t}\n\n\tif flusher, ok := g.writer.(http.Flusher); ok {\n\t\tflusher.Flush()\n\t}\n\n\treturn len(b), err\n}\n\nfunc (g *Conn) WriteBuffer(buffer *buf.Buffer) error {\n\tdefer buffer.Release()\n\tdataLen := buffer.Len()\n\tvarLen := UVarintLen(uint64(dataLen))\n\theader := buffer.ExtendHeader(6 + varLen)\n\t_ = header[6] // bounds check hint to compiler\n\theader[0] = 0x00\n\tbinary.BigEndian.PutUint32(header[1:5], uint32(1+varLen+dataLen))\n\theader[5] = 0x0A\n\tbinary.PutUvarint(header[6:], uint64(dataLen))\n\t_, err := g.writer.Write(buffer.Bytes())\n\n\tif err == io.ErrClosedPipe {\n\t\tif initErr := g.Init(); initErr != nil {\n\t\t\terr = initErr\n\t\t}\n\t}\n\n\tif flusher, ok := g.writer.(http.Flusher); ok {\n\t\tflusher.Flush()\n\t}\n\n\treturn err\n}\n\nfunc (g *Conn) FrontHeadroom() int {\n\treturn 6 + binary.MaxVarintLen64\n}\n\nfunc (g *Conn) Close() error {\n\tg.closeMutex.Lock()\n\tdefer g.closeMutex.Unlock()\n\tif g.closed {\n\t\treturn nil\n\t}\n\tg.closed = true\n\n\tvar errorArr []error\n\n\tif closer, ok := g.writer.(io.Closer); ok {\n\t\tif err := closer.Close(); err != nil {\n\t\t\terrorArr = append(errorArr, err)\n\t\t}\n\t}\n\n\tif reader := g.reader; reader != nil {\n\t\tif err := reader.Close(); err != nil {\n\t\t\terrorArr = append(errorArr, err)\n\t\t}\n\t}\n\n\tif closer := g.closer; closer != nil {\n\t\tif err := closer.Close(); err != nil {\n\t\t\terrorArr = append(errorArr, err)\n\t\t}\n\t}\n\n\tif g.onClose != nil {\n\t\tg.onClose()\n\t}\n\n\treturn errors.Join(errorArr...)\n}\n\nfunc (g *Conn) SetReadDeadline(t time.Time) error  { return g.SetDeadline(t) }\nfunc (g *Conn) SetWriteDeadline(t time.Time) error { return g.SetDeadline(t) }\n\nfunc (g *Conn) SetDeadline(t time.Time) error {\n\tif t.IsZero() {\n\t\tif g.deadline != nil {\n\t\t\tg.deadline.Stop()\n\t\t\tg.deadline = nil\n\t\t}\n\t\treturn nil\n\t}\n\td := time.Until(t)\n\tif g.deadline != nil {\n\t\tg.deadline.Reset(d)\n\t\treturn nil\n\t}\n\tg.deadline = time.AfterFunc(d, func() {\n\t\tg.Close()\n\t})\n\treturn nil\n}\n\ntype Transport struct {\n\ttransport *http.Transport\n\tcfg       *Config\n\tctx       context.Context\n\tcancel    context.CancelFunc\n\tcloseOnce sync.Once\n\tcount     atomic.Int64\n}\n\nfunc (t *Transport) Close() error {\n\tt.closeOnce.Do(func() {\n\t\tt.cancel()\n\t\thttputils.CloseTransport(t.transport)\n\t})\n\treturn nil\n}\n\nfunc NewTransport(dialFn DialFn, tlsConfig *vmess.TLSConfig, gunCfg *Config) *Transport {\n\tdialFunc := func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\tctx, cancel := context.WithTimeout(ctx, C.DefaultTLSTimeout)\n\t\tdefer cancel()\n\t\tpconn, err := dialFn(ctx, network, addr)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif tlsConfig == nil {\n\t\t\treturn pconn, nil\n\t\t}\n\n\t\tconn, err := vmess.StreamTLSConn(ctx, pconn, tlsConfig)\n\t\tif err != nil {\n\t\t\t_ = pconn.Close()\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif tlsConfig.Reality == nil { // reality doesn't return the negotiated ALPN\n\t\t\tstate := tlsC.GetTLSConnectionState(conn)\n\t\t\tif p := state.NegotiatedProtocol; p != Http2NextProtoTLS {\n\t\t\t\t_ = conn.Close()\n\t\t\t\treturn nil, fmt.Errorf(\"http2: unexpected ALPN protocol %s, want %s\", p, Http2NextProtoTLS)\n\t\t\t}\n\t\t}\n\t\treturn conn, nil\n\t}\n\n\t// use h2c mode to disallow the net/http fallback to http1.1\n\tprotocols := new(http.Protocols)\n\tprotocols.SetUnencryptedHTTP2(true)\n\ttransport := &http.Transport{\n\t\tDialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\t\twrapped, err := dialFunc(ctx, network, addr)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn wrapped, nil\n\t\t},\n\t\tProtocols:          protocols,\n\t\tDisableCompression: true,\n\t\tHTTP2: &http.HTTP2Config{\n\t\t\tSendPingTimeout: time.Duration(gunCfg.PingInterval) * time.Second, // If zero, no health check is performed,\n\t\t\tPingTimeout:     0,\n\t\t},\n\t}\n\n\tctx, cancel := context.WithCancel(context.Background())\n\twrap := &Transport{\n\t\ttransport: transport,\n\t\tcfg:       gunCfg,\n\t\tctx:       ctx,\n\t\tcancel:    cancel,\n\t}\n\treturn wrap\n}\n\nfunc ServiceNameToPath(serviceName string) string {\n\tif strings.HasPrefix(serviceName, \"/\") { // custom paths\n\t\treturn serviceName\n\t}\n\treturn \"/\" + serviceName + \"/Tun\"\n}\n\nfunc (t *Transport) Dial() (net.Conn, error) {\n\tserviceName := \"GunService\"\n\tif t.cfg.ServiceName != \"\" {\n\t\tserviceName = t.cfg.ServiceName\n\t}\n\tpath := ServiceNameToPath(serviceName)\n\n\treader, writer := io.Pipe()\n\n\theader := defaultHeader.Clone()\n\tif t.cfg.UserAgent != \"\" {\n\t\theader.Set(\"User-Agent\", t.cfg.UserAgent)\n\t}\n\n\trequest := &http.Request{\n\t\tMethod: http.MethodPost,\n\t\tBody:   reader,\n\t\tURL: &url.URL{\n\t\t\tScheme: \"https\",\n\t\t\tHost:   t.cfg.Host,\n\t\t\tPath:   path,\n\t\t\t// for unescape path\n\t\t\tOpaque: \"//\" + t.cfg.Host + path,\n\t\t},\n\t\tProto:      \"HTTP/2\",\n\t\tProtoMajor: 2,\n\t\tProtoMinor: 0,\n\t\tHeader:     header,\n\t}\n\trequest = request.WithContext(t.ctx)\n\tinitStarted := make(chan struct{})\n\n\tconn := &Conn{\n\t\tinitFn: func(addr *httputils.NetAddr) (io.ReadCloser, error) {\n\t\t\tclose(initStarted)\n\t\t\trequest = request.WithContext(httputils.NewAddrContext(addr, request.Context()))\n\t\t\tresponse, err := t.transport.RoundTrip(request)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn response.Body, nil\n\t\t},\n\t\twriter: writer,\n\t}\n\n\tt.count.Add(1)\n\tconn.onClose = func() { t.count.Add(-1) }\n\n\tgo conn.Init()\n\n\t// ensure conn.initOnce.Do has been called before return\n\t// prevent the race caused by the return side immediately calling conn.Close\n\t<-initStarted\n\n\treturn conn, nil\n}\n\ntype Client struct {\n\tmutex          sync.Mutex\n\tmaxConnections int\n\tminStreams     int\n\tmaxStreams     int\n\ttransports     []*Transport\n\tmaker          func() *Transport\n}\n\nfunc NewClient(maker func() *Transport, maxConnections, minStreams, maxStreams int) *Client {\n\tif maxConnections == 0 && minStreams == 0 && maxStreams == 0 {\n\t\tmaxConnections = 1\n\t}\n\treturn &Client{\n\t\tmaxConnections: maxConnections,\n\t\tminStreams:     minStreams,\n\t\tmaxStreams:     maxStreams,\n\t\tmaker:          maker,\n\t}\n}\n\nfunc (c *Client) Dial() (net.Conn, error) {\n\treturn c.getTransport().Dial()\n}\n\nfunc (c *Client) Close() error {\n\tc.mutex.Lock()\n\tdefer c.mutex.Unlock()\n\tvar errs []error\n\tfor _, t := range c.transports {\n\t\tif err := t.Close(); err != nil {\n\t\t\terrs = append(errs, err)\n\t\t}\n\t}\n\tc.transports = nil\n\treturn errors.Join(errs...)\n}\n\nfunc (c *Client) getTransport() *Transport {\n\tc.mutex.Lock()\n\tdefer c.mutex.Unlock()\n\tvar transport *Transport\n\tfor _, t := range c.transports {\n\t\tif transport == nil || t.count.Load() < transport.count.Load() {\n\t\t\ttransport = t\n\t\t}\n\t}\n\tif transport == nil {\n\t\treturn c.newTransportLocked()\n\t}\n\tnumStreams := int(transport.count.Load())\n\tif numStreams == 0 {\n\t\treturn transport\n\t}\n\tif c.maxConnections > 0 {\n\t\tif len(c.transports) >= c.maxConnections || numStreams < c.minStreams {\n\t\t\treturn transport\n\t\t}\n\t} else {\n\t\tif c.maxStreams > 0 && numStreams < c.maxStreams {\n\t\t\treturn transport\n\t\t}\n\t}\n\treturn c.newTransportLocked()\n}\n\nfunc (c *Client) newTransportLocked() *Transport {\n\ttransport := c.maker()\n\tc.transports = append(c.transports, transport)\n\treturn transport\n}\n\nfunc StreamGunWithConn(conn net.Conn, tlsConfig *vmess.TLSConfig, gunCfg *Config) (net.Conn, error) {\n\tdialFn := func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\treturn conn, nil\n\t}\n\n\ttransport := NewTransport(dialFn, tlsConfig, gunCfg)\n\tc, err := transport.Dial()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif c, ok := c.(*Conn); ok { // The incoming net.Conn should be closed synchronously with the generated gun.Conn\n\t\tc.closer = conn\n\t}\n\treturn c, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/gun/server.go",
    "content": "package gun\n\nimport (\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/metacubex/mihomo/common/buf\"\n\t\"github.com/metacubex/mihomo/common/httputils\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\n\t\"github.com/metacubex/http\"\n)\n\ntype ServerOption struct {\n\tServiceName string\n\tConnHandler func(conn net.Conn)\n\tHttpHandler http.Handler\n}\n\nfunc NewServerHandler(options ServerOption) http.Handler {\n\tpath := ServiceNameToPath(options.ServiceName)\n\tconnHandler := options.ConnHandler\n\thttpHandler := options.HttpHandler\n\tif httpHandler == nil {\n\t\thttpHandler = http.NewServeMux()\n\t}\n\treturn http.HandlerFunc(func(writer http.ResponseWriter, request *http.Request) {\n\t\tif request.URL.Path == path &&\n\t\t\trequest.Method == http.MethodPost &&\n\t\t\tstrings.HasPrefix(request.Header.Get(\"Content-Type\"), \"application/grpc\") {\n\n\t\t\twriter.Header().Set(\"Content-Type\", \"application/grpc\")\n\t\t\twriter.Header().Set(\"TE\", \"trailers\")\n\t\t\twriter.WriteHeader(http.StatusOK)\n\n\t\t\tconn := &Conn{\n\t\t\t\tinitFn: func(addr *httputils.NetAddr) (io.ReadCloser, error) {\n\t\t\t\t\thttputils.SetAddrFromRequest(addr, request)\n\t\t\t\t\treturn h2RequestBodyWrapper{request.Body}, nil\n\t\t\t\t},\n\t\t\t\twriter: writer,\n\t\t\t}\n\t\t\t_ = conn.Init()\n\n\t\t\twrapper := &h2ConnWrapper{\n\t\t\t\t// gun.Conn can't correct handle ReadDeadline\n\t\t\t\t// so call N.NewDeadlineConn to add a safe wrapper\n\t\t\t\tExtendedConn: N.NewDeadlineConn(conn),\n\t\t\t}\n\t\t\tconnHandler(wrapper)\n\t\t\twrapper.CloseWrapper()\n\n\t\t\treturn\n\t\t}\n\n\t\thttpHandler.ServeHTTP(writer, request)\n\t})\n}\n\n// h2RequestBodyWrapper used to conceal the h2-special typed error before return to caller\ntype h2RequestBodyWrapper struct {\n\tio.ReadCloser\n}\n\nfunc (r h2RequestBodyWrapper) Read(p []byte) (n int, err error) {\n\tn, err = r.ReadCloser.Read(p)\n\tif err != nil && err != io.EOF {\n\t\terr = fmt.Errorf(\"h2: %s\", err.Error())\n\t}\n\treturn\n}\n\n// h2ConnWrapper used to avoid \"panic: Write called after Handler finished\" for gun.Conn\ntype h2ConnWrapper struct {\n\tN.ExtendedConn\n\taccess sync.Mutex\n\tclosed bool\n}\n\nfunc (w *h2ConnWrapper) Write(p []byte) (n int, err error) {\n\tw.access.Lock()\n\tdefer w.access.Unlock()\n\tif w.closed {\n\t\treturn 0, net.ErrClosed\n\t}\n\treturn w.ExtendedConn.Write(p)\n}\n\nfunc (w *h2ConnWrapper) WriteBuffer(buffer *buf.Buffer) error {\n\tw.access.Lock()\n\tdefer w.access.Unlock()\n\tif w.closed {\n\t\treturn net.ErrClosed\n\t}\n\treturn w.ExtendedConn.WriteBuffer(buffer)\n}\n\nfunc (w *h2ConnWrapper) CloseWrapper() {\n\tw.access.Lock()\n\tdefer w.access.Unlock()\n\tw.closed = true\n}\n\nfunc (w *h2ConnWrapper) Upstream() any {\n\treturn w.ExtendedConn\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/gun/utils.go",
    "content": "package gun\n\nimport (\n\t\"encoding/binary\"\n\t\"io\"\n)\n\ntype stubByteReader struct {\n\tio.Reader\n}\n\nfunc (r stubByteReader) ReadByte() (byte, error) {\n\tvar b [1]byte\n\tvar n int\n\tvar err error\n\tfor n == 0 && err == nil {\n\t\tn, err = r.Read(b[:])\n\t}\n\n\tif n == 1 && err == io.EOF {\n\t\terr = nil\n\t}\n\treturn b[0], err\n}\n\nfunc ToByteReader(reader io.Reader) io.ByteReader {\n\tif byteReader, ok := reader.(io.ByteReader); ok {\n\t\treturn byteReader\n\t}\n\treturn &stubByteReader{reader}\n}\n\nfunc ReadUVariant(reader io.Reader) (uint64, error) {\n\treturn binary.ReadUvarint(ToByteReader(reader))\n}\n\nfunc UVarintLen(x uint64) int {\n\tswitch {\n\tcase x < 1<<(7*1):\n\t\treturn 1\n\tcase x < 1<<(7*2):\n\t\treturn 2\n\tcase x < 1<<(7*3):\n\t\treturn 3\n\tcase x < 1<<(7*4):\n\t\treturn 4\n\tcase x < 1<<(7*5):\n\t\treturn 5\n\tcase x < 1<<(7*6):\n\t\treturn 6\n\tcase x < 1<<(7*7):\n\t\treturn 7\n\tcase x < 1<<(7*8):\n\t\treturn 8\n\tcase x < 1<<(7*9):\n\t\treturn 9\n\tdefault:\n\t\treturn 10\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/hysteria/congestion/brutal.go",
    "content": "package congestion\n\nimport (\n\t\"github.com/metacubex/quic-go/congestion\"\n\t\"github.com/metacubex/quic-go/monotime\"\n\n\t\"time\"\n)\n\nconst (\n\tinitMaxDatagramSize = 1252\n\n\tpktInfoSlotCount = 5 // slot index is based on seconds, so this is basically how many seconds we sample\n\tminSampleCount   = 50\n\tminAckRate       = 0.8\n)\n\nvar _ congestion.CongestionControlEx = &BrutalSender{}\n\ntype BrutalSender struct {\n\trttStats        congestion.RTTStatsProvider\n\tbps             congestion.ByteCount\n\tmaxDatagramSize congestion.ByteCount\n\tpacer           *pacer\n\n\tpktInfoSlots [pktInfoSlotCount]pktInfo\n\tackRate      float64\n}\n\ntype pktInfo struct {\n\tTimestamp int64\n\tAckCount  uint64\n\tLossCount uint64\n}\n\nfunc NewBrutalSender(bps congestion.ByteCount) *BrutalSender {\n\tbs := &BrutalSender{\n\t\tbps:             bps,\n\t\tmaxDatagramSize: initMaxDatagramSize,\n\t\tackRate:         1,\n\t}\n\tbs.pacer = newPacer(func() congestion.ByteCount {\n\t\treturn congestion.ByteCount(float64(bs.bps) / bs.ackRate)\n\t})\n\treturn bs\n}\n\nfunc (b *BrutalSender) SetRTTStatsProvider(rttStats congestion.RTTStatsProvider) {\n\tb.rttStats = rttStats\n}\n\nfunc (b *BrutalSender) TimeUntilSend(bytesInFlight congestion.ByteCount) monotime.Time {\n\treturn b.pacer.TimeUntilSend()\n}\n\nfunc (b *BrutalSender) HasPacingBudget(now monotime.Time) bool {\n\treturn b.pacer.Budget(now) >= b.maxDatagramSize\n}\n\nfunc (b *BrutalSender) CanSend(bytesInFlight congestion.ByteCount) bool {\n\treturn bytesInFlight < b.GetCongestionWindow()\n}\n\nfunc (b *BrutalSender) GetCongestionWindow() congestion.ByteCount {\n\trtt := maxDuration(b.rttStats.LatestRTT(), b.rttStats.SmoothedRTT())\n\tif rtt <= 0 {\n\t\treturn 10240\n\t}\n\treturn congestion.ByteCount(float64(b.bps) * rtt.Seconds() * 1.5 / b.ackRate)\n}\n\nfunc (b *BrutalSender) OnPacketSent(sentTime monotime.Time, bytesInFlight congestion.ByteCount,\n\tpacketNumber congestion.PacketNumber, bytes congestion.ByteCount, isRetransmittable bool) {\n\tb.pacer.SentPacket(sentTime, bytes)\n}\n\nfunc (b *BrutalSender) OnPacketAcked(number congestion.PacketNumber, ackedBytes congestion.ByteCount,\n\tpriorInFlight congestion.ByteCount, eventTime monotime.Time) {\n\t// Stub\n}\n\nfunc (b *BrutalSender) OnCongestionEvent(number congestion.PacketNumber, lostBytes congestion.ByteCount,\n\tpriorInFlight congestion.ByteCount) {\n\t// Stub\n}\n\nfunc (b *BrutalSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, eventTime monotime.Time, ackedPackets []congestion.AckedPacketInfo, lostPackets []congestion.LostPacketInfo) {\n\tcurrentTimestamp := int64(time.Duration(eventTime) / time.Second)\n\tslot := currentTimestamp % pktInfoSlotCount\n\tif b.pktInfoSlots[slot].Timestamp == currentTimestamp {\n\t\tb.pktInfoSlots[slot].LossCount += uint64(len(lostPackets))\n\t\tb.pktInfoSlots[slot].AckCount += uint64(len(ackedPackets))\n\t} else {\n\t\t// uninitialized slot or too old, reset\n\t\tb.pktInfoSlots[slot].Timestamp = currentTimestamp\n\t\tb.pktInfoSlots[slot].AckCount = uint64(len(ackedPackets))\n\t\tb.pktInfoSlots[slot].LossCount = uint64(len(lostPackets))\n\t}\n\tb.updateAckRate(currentTimestamp)\n}\n\nfunc (b *BrutalSender) SetMaxDatagramSize(size congestion.ByteCount) {\n\tb.maxDatagramSize = size\n\tb.pacer.SetMaxDatagramSize(size)\n}\n\nfunc (b *BrutalSender) updateAckRate(currentTimestamp int64) {\n\tminTimestamp := currentTimestamp - pktInfoSlotCount\n\tvar ackCount, lossCount uint64\n\tfor _, info := range b.pktInfoSlots {\n\t\tif info.Timestamp < minTimestamp {\n\t\t\tcontinue\n\t\t}\n\t\tackCount += info.AckCount\n\t\tlossCount += info.LossCount\n\t}\n\tif ackCount+lossCount < minSampleCount {\n\t\tb.ackRate = 1\n\t\treturn\n\t}\n\trate := float64(ackCount) / float64(ackCount+lossCount)\n\tif rate < minAckRate {\n\t\tb.ackRate = minAckRate\n\t\treturn\n\t}\n\tb.ackRate = rate\n}\n\nfunc (b *BrutalSender) InSlowStart() bool {\n\treturn false\n}\n\nfunc (b *BrutalSender) InRecovery() bool {\n\treturn false\n}\n\nfunc (b *BrutalSender) MaybeExitSlowStart() {}\n\nfunc (b *BrutalSender) OnRetransmissionTimeout(packetsRetransmitted bool) {}\n\nfunc maxDuration(a, b time.Duration) time.Duration {\n\tif a > b {\n\t\treturn a\n\t}\n\treturn b\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/hysteria/congestion/pacer.go",
    "content": "package congestion\n\nimport (\n\t\"github.com/metacubex/quic-go/congestion\"\n\t\"github.com/metacubex/quic-go/monotime\"\n\n\t\"math\"\n\t\"time\"\n)\n\nconst (\n\tmaxBurstPackets = 10\n\tminPacingDelay  = time.Millisecond\n)\n\n// The pacer implements a token bucket pacing algorithm.\ntype pacer struct {\n\tbudgetAtLastSent congestion.ByteCount\n\tmaxDatagramSize  congestion.ByteCount\n\tlastSentTime     monotime.Time\n\tgetBandwidth     func() congestion.ByteCount // in bytes/s\n}\n\nfunc newPacer(getBandwidth func() congestion.ByteCount) *pacer {\n\tp := &pacer{\n\t\tbudgetAtLastSent: maxBurstPackets * initMaxDatagramSize,\n\t\tmaxDatagramSize:  initMaxDatagramSize,\n\t\tgetBandwidth:     getBandwidth,\n\t}\n\treturn p\n}\n\nfunc (p *pacer) SentPacket(sendTime monotime.Time, size congestion.ByteCount) {\n\tbudget := p.Budget(sendTime)\n\tif size > budget {\n\t\tp.budgetAtLastSent = 0\n\t} else {\n\t\tp.budgetAtLastSent = budget - size\n\t}\n\tp.lastSentTime = sendTime\n}\n\nfunc (p *pacer) Budget(now monotime.Time) congestion.ByteCount {\n\tif p.lastSentTime.IsZero() {\n\t\treturn p.maxBurstSize()\n\t}\n\tbudget := p.budgetAtLastSent + (p.getBandwidth()*congestion.ByteCount(now.Sub(p.lastSentTime).Nanoseconds()))/1e9\n\treturn minByteCount(p.maxBurstSize(), budget)\n}\n\nfunc (p *pacer) maxBurstSize() congestion.ByteCount {\n\treturn maxByteCount(\n\t\tcongestion.ByteCount((minPacingDelay+time.Millisecond).Nanoseconds())*p.getBandwidth()/1e9,\n\t\tmaxBurstPackets*p.maxDatagramSize,\n\t)\n}\n\n// TimeUntilSend returns when the next packet should be sent.\n// It returns the zero value of monotime.Time if a packet can be sent immediately.\nfunc (p *pacer) TimeUntilSend() monotime.Time {\n\tif p.budgetAtLastSent >= p.maxDatagramSize {\n\t\treturn monotime.Time(0)\n\t}\n\treturn p.lastSentTime.Add(maxDuration(\n\t\tminPacingDelay,\n\t\ttime.Duration(math.Ceil(float64(p.maxDatagramSize-p.budgetAtLastSent)*1e9/\n\t\t\tfloat64(p.getBandwidth())))*time.Nanosecond,\n\t))\n}\n\nfunc (p *pacer) SetMaxDatagramSize(s congestion.ByteCount) {\n\tp.maxDatagramSize = s\n}\n\nfunc maxByteCount(a, b congestion.ByteCount) congestion.ByteCount {\n\tif a < b {\n\t\treturn b\n\t}\n\treturn a\n}\n\nfunc minByteCount(a, b congestion.ByteCount) congestion.ByteCount {\n\tif a < b {\n\t\treturn a\n\t}\n\treturn b\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/hysteria/conns/faketcp/LICENSE",
    "content": "Grabbed from https://github.com/xtaci/tcpraw with modifications"
  },
  {
    "path": "core/Clash.Meta/transport/hysteria/conns/faketcp/obfs.go",
    "content": "package faketcp\n\nimport (\n\t\"github.com/metacubex/mihomo/transport/hysteria/obfs\"\n\t\"net\"\n\t\"sync\"\n\t\"syscall\"\n\t\"time\"\n)\n\nconst udpBufferSize = 65535\n\ntype ObfsFakeTCPConn struct {\n\torig       *TCPConn\n\tobfs       obfs.Obfuscator\n\treadBuf    []byte\n\treadMutex  sync.Mutex\n\twriteBuf   []byte\n\twriteMutex sync.Mutex\n}\n\nfunc NewObfsFakeTCPConn(orig *TCPConn, obfs obfs.Obfuscator) *ObfsFakeTCPConn {\n\treturn &ObfsFakeTCPConn{\n\t\torig:     orig,\n\t\tobfs:     obfs,\n\t\treadBuf:  make([]byte, udpBufferSize),\n\t\twriteBuf: make([]byte, udpBufferSize),\n\t}\n}\n\nfunc (c *ObfsFakeTCPConn) ReadFrom(p []byte) (int, net.Addr, error) {\n\tfor {\n\t\tc.readMutex.Lock()\n\t\tn, addr, err := c.orig.ReadFrom(c.readBuf)\n\t\tif n <= 0 {\n\t\t\tc.readMutex.Unlock()\n\t\t\treturn 0, addr, err\n\t\t}\n\t\tnewN := c.obfs.Deobfuscate(c.readBuf[:n], p)\n\t\tc.readMutex.Unlock()\n\t\tif newN > 0 {\n\t\t\t// Valid packet\n\t\t\treturn newN, addr, err\n\t\t} else if err != nil {\n\t\t\t// Not valid and orig.ReadFrom had some error\n\t\t\treturn 0, addr, err\n\t\t}\n\t}\n}\n\nfunc (c *ObfsFakeTCPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {\n\tc.writeMutex.Lock()\n\tbn := c.obfs.Obfuscate(p, c.writeBuf)\n\t_, err = c.orig.WriteTo(c.writeBuf[:bn], addr)\n\tc.writeMutex.Unlock()\n\tif err != nil {\n\t\treturn 0, err\n\t} else {\n\t\treturn len(p), nil\n\t}\n}\n\nfunc (c *ObfsFakeTCPConn) Close() error {\n\treturn c.orig.Close()\n}\n\nfunc (c *ObfsFakeTCPConn) LocalAddr() net.Addr {\n\treturn c.orig.LocalAddr()\n}\n\nfunc (c *ObfsFakeTCPConn) SetDeadline(t time.Time) error {\n\treturn c.orig.SetDeadline(t)\n}\n\nfunc (c *ObfsFakeTCPConn) SetReadDeadline(t time.Time) error {\n\treturn c.orig.SetReadDeadline(t)\n}\n\nfunc (c *ObfsFakeTCPConn) SetWriteDeadline(t time.Time) error {\n\treturn c.orig.SetWriteDeadline(t)\n}\n\nfunc (c *ObfsFakeTCPConn) SetReadBuffer(bytes int) error {\n\treturn c.orig.SetReadBuffer(bytes)\n}\n\nfunc (c *ObfsFakeTCPConn) SetWriteBuffer(bytes int) error {\n\treturn c.orig.SetWriteBuffer(bytes)\n}\n\nfunc (c *ObfsFakeTCPConn) SyscallConn() (syscall.RawConn, error) {\n\treturn c.orig.SyscallConn()\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/hysteria/conns/faketcp/tcp_linux.go",
    "content": "//go:build linux && !no_fake_tcp\n// +build linux,!no_fake_tcp\n\npackage faketcp\n\nimport (\n\t\"crypto/rand\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"io/ioutil\"\n\t\"net\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/coreos/go-iptables/iptables\"\n\t\"github.com/metacubex/gopacket\"\n\t\"github.com/metacubex/gopacket/layers\"\n\n\t\"github.com/metacubex/mihomo/component/dialer\"\n)\n\nvar (\n\terrOpNotImplemented = errors.New(\"operation not implemented\")\n\terrTimeout          = errors.New(\"timeout\")\n\texpire              = time.Minute\n)\n\n// a message from NIC\ntype message struct {\n\tbts  []byte\n\taddr net.Addr\n}\n\n// a tcp flow information of a connection pair\ntype tcpFlow struct {\n\tconn         *net.TCPConn               // the related system TCP connection of this flow\n\thandle       *net.IPConn                // the handle to send packets\n\tseq          uint32                     // TCP sequence number\n\tack          uint32                     // TCP acknowledge number\n\tnetworkLayer gopacket.SerializableLayer // network layer header for tx\n\tts           time.Time                  // last packet incoming time\n\tbuf          gopacket.SerializeBuffer   // a buffer for write\n\ttcpHeader    layers.TCP\n}\n\n// TCPConn defines a TCP-packet oriented connection\ntype TCPConn struct {\n\tdie     chan struct{}\n\tdieOnce sync.Once\n\n\t// the main golang sockets\n\ttcpconn  *net.TCPConn     // from net.Dial\n\tlistener *net.TCPListener // from net.Listen\n\n\t// handles\n\thandles []*net.IPConn\n\n\t// packets captured from all related NICs will be delivered to this channel\n\tchMessage chan message\n\n\t// all TCP flows\n\tflowTable map[string]*tcpFlow\n\tflowsLock sync.Mutex\n\n\t// iptables\n\tiptables *iptables.IPTables\n\tiprule   []string\n\n\tip6tables *iptables.IPTables\n\tip6rule   []string\n\n\t// deadlines\n\treadDeadline  atomic.Value\n\twriteDeadline atomic.Value\n\n\t// serialization\n\topts gopacket.SerializeOptions\n}\n\n// lockflow locks the flow table and apply function `f` to the entry, and create one if not exist\nfunc (conn *TCPConn) lockflow(addr net.Addr, f func(e *tcpFlow)) {\n\tkey := addr.String()\n\tconn.flowsLock.Lock()\n\te := conn.flowTable[key]\n\tif e == nil { // entry first visit\n\t\te = new(tcpFlow)\n\t\te.ts = time.Now()\n\t\te.buf = gopacket.NewSerializeBuffer()\n\t}\n\tf(e)\n\tconn.flowTable[key] = e\n\tconn.flowsLock.Unlock()\n}\n\n// clean expired flows\nfunc (conn *TCPConn) cleaner() {\n\tticker := time.NewTicker(time.Minute)\n\tselect {\n\tcase <-conn.die:\n\t\treturn\n\tcase <-ticker.C:\n\t\tconn.flowsLock.Lock()\n\t\tfor k, v := range conn.flowTable {\n\t\t\tif time.Now().Sub(v.ts) > expire {\n\t\t\t\tif v.conn != nil {\n\t\t\t\t\tsetTTL(v.conn, 64)\n\t\t\t\t\tv.conn.Close()\n\t\t\t\t}\n\t\t\t\tdelete(conn.flowTable, k)\n\t\t\t}\n\t\t}\n\t\tconn.flowsLock.Unlock()\n\t}\n}\n\n// captureFlow capture every inbound packets based on rules of BPF\nfunc (conn *TCPConn) captureFlow(handle *net.IPConn, port int) {\n\tbuf := make([]byte, 2048)\n\topt := gopacket.DecodeOptions{NoCopy: true, Lazy: true}\n\tfor {\n\t\tn, addr, err := handle.ReadFromIP(buf)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\t// try decoding TCP frame from buf[:n]\n\t\tpacket := gopacket.NewPacket(buf[:n], layers.LayerTypeTCP, opt)\n\t\ttransport := packet.TransportLayer()\n\t\ttcp, ok := transport.(*layers.TCP)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\t// port filtering\n\t\tif int(tcp.DstPort) != port {\n\t\t\tcontinue\n\t\t}\n\n\t\t// address building\n\t\tvar src net.TCPAddr\n\t\tsrc.IP = addr.IP\n\t\tsrc.Port = int(tcp.SrcPort)\n\n\t\tvar orphan bool\n\t\t// flow maintaince\n\t\tconn.lockflow(&src, func(e *tcpFlow) {\n\t\t\tif e.conn == nil { // make sure it's related to net.TCPConn\n\t\t\t\torphan = true // mark as orphan if it's not related net.TCPConn\n\t\t\t}\n\n\t\t\t// to keep track of TCP header related to this source\n\t\t\te.ts = time.Now()\n\t\t\tif tcp.ACK {\n\t\t\t\te.seq = tcp.Ack\n\t\t\t}\n\t\t\tif tcp.SYN {\n\t\t\t\te.ack = tcp.Seq + 1\n\t\t\t}\n\t\t\tif tcp.PSH {\n\t\t\t\tif e.ack == tcp.Seq {\n\t\t\t\t\te.ack = tcp.Seq + uint32(len(tcp.Payload))\n\t\t\t\t}\n\t\t\t}\n\t\t\te.handle = handle\n\t\t})\n\n\t\t// push data if it's not orphan\n\t\tif !orphan && tcp.PSH {\n\t\t\tpayload := make([]byte, len(tcp.Payload))\n\t\t\tcopy(payload, tcp.Payload)\n\t\t\tselect {\n\t\t\tcase conn.chMessage <- message{payload, &src}:\n\t\t\tcase <-conn.die:\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n\n// ReadFrom implements the PacketConn ReadFrom method.\nfunc (conn *TCPConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {\n\tvar timer *time.Timer\n\tvar deadline <-chan time.Time\n\tif d, ok := conn.readDeadline.Load().(time.Time); ok && !d.IsZero() {\n\t\ttimer = time.NewTimer(time.Until(d))\n\t\tdefer timer.Stop()\n\t\tdeadline = timer.C\n\t}\n\n\tselect {\n\tcase <-deadline:\n\t\treturn 0, nil, errTimeout\n\tcase <-conn.die:\n\t\treturn 0, nil, io.EOF\n\tcase packet := <-conn.chMessage:\n\t\tn = copy(p, packet.bts)\n\t\treturn n, packet.addr, nil\n\t}\n}\n\n// WriteTo implements the PacketConn WriteTo method.\nfunc (conn *TCPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {\n\tvar deadline <-chan time.Time\n\tif d, ok := conn.writeDeadline.Load().(time.Time); ok && !d.IsZero() {\n\t\ttimer := time.NewTimer(time.Until(d))\n\t\tdefer timer.Stop()\n\t\tdeadline = timer.C\n\t}\n\n\tselect {\n\tcase <-deadline:\n\t\treturn 0, errTimeout\n\tcase <-conn.die:\n\t\treturn 0, io.EOF\n\tdefault:\n\t\traddr, err := net.ResolveTCPAddr(\"tcp\", addr.String())\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\n\t\tvar lport int\n\t\tif conn.tcpconn != nil {\n\t\t\tlport = conn.tcpconn.LocalAddr().(*net.TCPAddr).Port\n\t\t} else {\n\t\t\tlport = conn.listener.Addr().(*net.TCPAddr).Port\n\t\t}\n\n\t\tconn.lockflow(addr, func(e *tcpFlow) {\n\t\t\t// if the flow doesn't have handle , assume this packet has lost, without notification\n\t\t\tif e.handle == nil {\n\t\t\t\tn = len(p)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// build tcp header with local and remote port\n\t\t\te.tcpHeader.SrcPort = layers.TCPPort(lport)\n\t\t\te.tcpHeader.DstPort = layers.TCPPort(raddr.Port)\n\t\t\tbinary.Read(rand.Reader, binary.LittleEndian, &e.tcpHeader.Window)\n\t\t\te.tcpHeader.Window |= 0x8000 // make sure it's larger than 32768\n\t\t\te.tcpHeader.Ack = e.ack\n\t\t\te.tcpHeader.Seq = e.seq\n\t\t\te.tcpHeader.PSH = true\n\t\t\te.tcpHeader.ACK = true\n\n\t\t\t// build IP header with src & dst ip for TCP checksum\n\t\t\tif raddr.IP.To4() != nil {\n\t\t\t\tip := &layers.IPv4{\n\t\t\t\t\tProtocol: layers.IPProtocolTCP,\n\t\t\t\t\tSrcIP:    e.handle.LocalAddr().(*net.IPAddr).IP.To4(),\n\t\t\t\t\tDstIP:    raddr.IP.To4(),\n\t\t\t\t}\n\t\t\t\te.tcpHeader.SetNetworkLayerForChecksum(ip)\n\t\t\t} else {\n\t\t\t\tip := &layers.IPv6{\n\t\t\t\t\tNextHeader: layers.IPProtocolTCP,\n\t\t\t\t\tSrcIP:      e.handle.LocalAddr().(*net.IPAddr).IP.To16(),\n\t\t\t\t\tDstIP:      raddr.IP.To16(),\n\t\t\t\t}\n\t\t\t\te.tcpHeader.SetNetworkLayerForChecksum(ip)\n\t\t\t}\n\n\t\t\te.buf.Clear()\n\t\t\tgopacket.SerializeLayers(e.buf, conn.opts, &e.tcpHeader, gopacket.Payload(p))\n\t\t\tif conn.tcpconn != nil {\n\t\t\t\t_, err = e.handle.Write(e.buf.Bytes())\n\t\t\t} else {\n\t\t\t\t_, err = e.handle.WriteToIP(e.buf.Bytes(), &net.IPAddr{IP: raddr.IP})\n\t\t\t}\n\t\t\t// increase seq in flow\n\t\t\te.seq += uint32(len(p))\n\t\t\tn = len(p)\n\t\t})\n\t}\n\treturn\n}\n\n// Close closes the connection.\nfunc (conn *TCPConn) Close() error {\n\tvar err error\n\tconn.dieOnce.Do(func() {\n\t\t// signal closing\n\t\tclose(conn.die)\n\n\t\t// close all established tcp connections\n\t\tif conn.tcpconn != nil { // client\n\t\t\tsetTTL(conn.tcpconn, 64)\n\t\t\terr = conn.tcpconn.Close()\n\t\t} else if conn.listener != nil {\n\t\t\terr = conn.listener.Close() // server\n\t\t\tconn.flowsLock.Lock()\n\t\t\tfor k, v := range conn.flowTable {\n\t\t\t\tif v.conn != nil {\n\t\t\t\t\tsetTTL(v.conn, 64)\n\t\t\t\t\tv.conn.Close()\n\t\t\t\t}\n\t\t\t\tdelete(conn.flowTable, k)\n\t\t\t}\n\t\t\tconn.flowsLock.Unlock()\n\t\t}\n\n\t\t// close handles\n\t\tfor k := range conn.handles {\n\t\t\tconn.handles[k].Close()\n\t\t}\n\n\t\t// delete iptable\n\t\tif conn.iptables != nil {\n\t\t\tconn.iptables.Delete(\"filter\", \"OUTPUT\", conn.iprule...)\n\t\t}\n\t\tif conn.ip6tables != nil {\n\t\t\tconn.ip6tables.Delete(\"filter\", \"OUTPUT\", conn.ip6rule...)\n\t\t}\n\t})\n\treturn err\n}\n\n// LocalAddr returns the local network address.\nfunc (conn *TCPConn) LocalAddr() net.Addr {\n\tif conn.tcpconn != nil {\n\t\treturn conn.tcpconn.LocalAddr()\n\t} else if conn.listener != nil {\n\t\treturn conn.listener.Addr()\n\t}\n\treturn nil\n}\n\n// SetDeadline implements the Conn SetDeadline method.\nfunc (conn *TCPConn) SetDeadline(t time.Time) error {\n\tif err := conn.SetReadDeadline(t); err != nil {\n\t\treturn err\n\t}\n\tif err := conn.SetWriteDeadline(t); err != nil {\n\t\treturn err\n\t}\n\treturn nil\n}\n\n// SetReadDeadline implements the Conn SetReadDeadline method.\nfunc (conn *TCPConn) SetReadDeadline(t time.Time) error {\n\tconn.readDeadline.Store(t)\n\treturn nil\n}\n\n// SetWriteDeadline implements the Conn SetWriteDeadline method.\nfunc (conn *TCPConn) SetWriteDeadline(t time.Time) error {\n\tconn.writeDeadline.Store(t)\n\treturn nil\n}\n\n// SetDSCP sets the 6bit DSCP field in IPv4 header, or 8bit Traffic Class in IPv6 header.\nfunc (conn *TCPConn) SetDSCP(dscp int) error {\n\tfor k := range conn.handles {\n\t\tif err := setDSCP(conn.handles[k], dscp); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\n// SetReadBuffer sets the size of the operating system's receive buffer associated with the connection.\nfunc (conn *TCPConn) SetReadBuffer(bytes int) error {\n\tvar err error\n\tfor k := range conn.handles {\n\t\tif err := conn.handles[k].SetReadBuffer(bytes); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn err\n}\n\n// SetWriteBuffer sets the size of the operating system's transmit buffer associated with the connection.\nfunc (conn *TCPConn) SetWriteBuffer(bytes int) error {\n\tvar err error\n\tfor k := range conn.handles {\n\t\tif err := conn.handles[k].SetWriteBuffer(bytes); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn err\n}\n\nfunc (conn *TCPConn) SyscallConn() (syscall.RawConn, error) {\n\tif len(conn.handles) == 0 {\n\t\treturn nil, errors.New(\"no handles\")\n\t\t// How is it possible?\n\t}\n\treturn conn.handles[0].SyscallConn()\n}\n\n// Dial connects to the remote TCP port,\n// and returns a single packet-oriented connection\nfunc Dial(network, address string) (*TCPConn, error) {\n\t// init gopacket.layers\n\tlayers.Init()\n\t// remote address resolve\n\traddr, err := net.ResolveTCPAddr(network, address)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar lTcpAddr *net.TCPAddr\n\tvar lIpAddr *net.IPAddr\n\trAddrPort := raddr.AddrPort()\n\tifaceName := dialer.DefaultInterface.Load()\n\tif ifaceName == \"\" {\n\t\tif finder := dialer.DefaultInterfaceFinder.Load(); finder != nil {\n\t\t\tifaceName = finder.FindInterfaceName(rAddrPort.Addr().Unmap())\n\t\t}\n\t}\n\tif len(ifaceName) > 0 {\n\t\taddr, err := dialer.LookupLocalAddrFromIfaceName(ifaceName, network, rAddrPort.Addr(), int(rAddrPort.Port()))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tlTcpAddr = addr.(*net.TCPAddr)\n\t\tlIpAddr = &net.IPAddr{IP: lTcpAddr.IP}\n\t}\n\n\t// AF_INET\n\thandle, err := net.DialIP(\"ip:tcp\", lIpAddr, &net.IPAddr{IP: raddr.IP})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// create an established tcp connection\n\t// will hack this tcp connection for packet transmission\n\ttcpconn, err := net.DialTCP(network, lTcpAddr, raddr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// fields\n\tconn := new(TCPConn)\n\tconn.die = make(chan struct{})\n\tconn.flowTable = make(map[string]*tcpFlow)\n\tconn.tcpconn = tcpconn\n\tconn.chMessage = make(chan message)\n\tconn.lockflow(tcpconn.RemoteAddr(), func(e *tcpFlow) { e.conn = tcpconn })\n\tconn.handles = append(conn.handles, handle)\n\tconn.opts = gopacket.SerializeOptions{\n\t\tFixLengths:       true,\n\t\tComputeChecksums: true,\n\t}\n\tgo conn.captureFlow(handle, tcpconn.LocalAddr().(*net.TCPAddr).Port)\n\tgo conn.cleaner()\n\n\t// iptables\n\terr = setTTL(tcpconn, 1)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4); err == nil {\n\t\trule := []string{\"-m\", \"ttl\", \"--ttl-eq\", \"1\", \"-p\", \"tcp\", \"-d\", raddr.IP.String(), \"--dport\", fmt.Sprint(raddr.Port), \"-j\", \"DROP\"}\n\t\tif exists, err := ipt.Exists(\"filter\", \"OUTPUT\", rule...); err == nil {\n\t\t\tif !exists {\n\t\t\t\tif err = ipt.Append(\"filter\", \"OUTPUT\", rule...); err == nil {\n\t\t\t\t\tconn.iprule = rule\n\t\t\t\t\tconn.iptables = ipt\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv6); err == nil {\n\t\trule := []string{\"-m\", \"hl\", \"--hl-eq\", \"1\", \"-p\", \"tcp\", \"-d\", raddr.IP.String(), \"--dport\", fmt.Sprint(raddr.Port), \"-j\", \"DROP\"}\n\t\tif exists, err := ipt.Exists(\"filter\", \"OUTPUT\", rule...); err == nil {\n\t\t\tif !exists {\n\t\t\t\tif err = ipt.Append(\"filter\", \"OUTPUT\", rule...); err == nil {\n\t\t\t\t\tconn.ip6rule = rule\n\t\t\t\t\tconn.ip6tables = ipt\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// discard everything\n\tgo io.Copy(ioutil.Discard, tcpconn)\n\n\treturn conn, nil\n}\n\n// Listen acts like net.ListenTCP,\n// and returns a single packet-oriented connection\nfunc Listen(network, address string) (*TCPConn, error) {\n\t// init gopacket.layers\n\tlayers.Init()\n\t// fields\n\tconn := new(TCPConn)\n\tconn.flowTable = make(map[string]*tcpFlow)\n\tconn.die = make(chan struct{})\n\tconn.chMessage = make(chan message)\n\tconn.opts = gopacket.SerializeOptions{\n\t\tFixLengths:       true,\n\t\tComputeChecksums: true,\n\t}\n\n\t// resolve address\n\tladdr, err := net.ResolveTCPAddr(network, address)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// AF_INET\n\tifaces, err := net.Interfaces()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif laddr.IP == nil || laddr.IP.IsUnspecified() { // if address is not specified, capture on all ifaces\n\t\tvar lasterr error\n\t\tfor _, iface := range ifaces {\n\t\t\tif addrs, err := iface.Addrs(); err == nil {\n\t\t\t\tfor _, addr := range addrs {\n\t\t\t\t\tif ipaddr, ok := addr.(*net.IPNet); ok {\n\t\t\t\t\t\tif handle, err := net.ListenIP(\"ip:tcp\", &net.IPAddr{IP: ipaddr.IP}); err == nil {\n\t\t\t\t\t\t\tconn.handles = append(conn.handles, handle)\n\t\t\t\t\t\t\tgo conn.captureFlow(handle, laddr.Port)\n\t\t\t\t\t\t} else {\n\t\t\t\t\t\t\tlasterr = err\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t\tif len(conn.handles) == 0 {\n\t\t\treturn nil, lasterr\n\t\t}\n\t} else {\n\t\tif handle, err := net.ListenIP(\"ip:tcp\", &net.IPAddr{IP: laddr.IP}); err == nil {\n\t\t\tconn.handles = append(conn.handles, handle)\n\t\t\tgo conn.captureFlow(handle, laddr.Port)\n\t\t} else {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\t// start listening\n\tl, err := net.ListenTCP(network, laddr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tconn.listener = l\n\n\t// start cleaner\n\tgo conn.cleaner()\n\n\t// iptables drop packets marked with TTL = 1\n\t// TODO: what if iptables is not available, the next hop will send back ICMP Time Exceeded,\n\t// is this still an acceptable behavior?\n\tif ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv4); err == nil {\n\t\trule := []string{\"-m\", \"ttl\", \"--ttl-eq\", \"1\", \"-p\", \"tcp\", \"--sport\", fmt.Sprint(laddr.Port), \"-j\", \"DROP\"}\n\t\tif exists, err := ipt.Exists(\"filter\", \"OUTPUT\", rule...); err == nil {\n\t\t\tif !exists {\n\t\t\t\tif err = ipt.Append(\"filter\", \"OUTPUT\", rule...); err == nil {\n\t\t\t\t\tconn.iprule = rule\n\t\t\t\t\tconn.iptables = ipt\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tif ipt, err := iptables.NewWithProtocol(iptables.ProtocolIPv6); err == nil {\n\t\trule := []string{\"-m\", \"hl\", \"--hl-eq\", \"1\", \"-p\", \"tcp\", \"--sport\", fmt.Sprint(laddr.Port), \"-j\", \"DROP\"}\n\t\tif exists, err := ipt.Exists(\"filter\", \"OUTPUT\", rule...); err == nil {\n\t\t\tif !exists {\n\t\t\t\tif err = ipt.Append(\"filter\", \"OUTPUT\", rule...); err == nil {\n\t\t\t\t\tconn.ip6rule = rule\n\t\t\t\t\tconn.ip6tables = ipt\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t// discard everything in original connection\n\tgo func() {\n\t\tfor {\n\t\t\ttcpconn, err := l.AcceptTCP()\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\t// if we cannot set TTL = 1, the only thing reasonable is panic\n\t\t\tif err := setTTL(tcpconn, 1); err != nil {\n\t\t\t\tpanic(err)\n\t\t\t}\n\n\t\t\t// record net.Conn\n\t\t\tconn.lockflow(tcpconn.RemoteAddr(), func(e *tcpFlow) { e.conn = tcpconn })\n\n\t\t\t// discard everything\n\t\t\tgo io.Copy(ioutil.Discard, tcpconn)\n\t\t}\n\t}()\n\n\treturn conn, nil\n}\n\n// setTTL sets the Time-To-Live field on a given connection\nfunc setTTL(c *net.TCPConn, ttl int) error {\n\traw, err := c.SyscallConn()\n\tif err != nil {\n\t\treturn err\n\t}\n\taddr := c.LocalAddr().(*net.TCPAddr)\n\n\tif addr.IP.To4() == nil {\n\t\traw.Control(func(fd uintptr) {\n\t\t\terr = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS, ttl)\n\t\t})\n\t} else {\n\t\traw.Control(func(fd uintptr) {\n\t\t\terr = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TTL, ttl)\n\t\t})\n\t}\n\treturn err\n}\n\n// setDSCP sets the 6bit DSCP field in IPv4 header, or 8bit Traffic Class in IPv6 header.\nfunc setDSCP(c *net.IPConn, dscp int) error {\n\traw, err := c.SyscallConn()\n\tif err != nil {\n\t\treturn err\n\t}\n\taddr := c.LocalAddr().(*net.IPAddr)\n\n\tif addr.IP.To4() == nil {\n\t\traw.Control(func(fd uintptr) {\n\t\t\terr = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IPV6, syscall.IPV6_TCLASS, dscp)\n\t\t})\n\t} else {\n\t\traw.Control(func(fd uintptr) {\n\t\t\terr = syscall.SetsockoptInt(int(fd), syscall.IPPROTO_IP, syscall.IP_TOS, dscp<<2)\n\t\t})\n\t}\n\treturn err\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/hysteria/conns/faketcp/tcp_stub.go",
    "content": "//go:build !linux || no_fake_tcp\n// +build !linux no_fake_tcp\n\npackage faketcp\n\nimport (\n\t\"errors\"\n\t\"net\"\n)\n\ntype TCPConn struct{ *net.UDPConn }\n\n// Dial connects to the remote TCP port,\n// and returns a single packet-oriented connection\nfunc Dial(network, address string) (*TCPConn, error) {\n\treturn nil, errors.New(\"faketcp is not supported on this platform\")\n}\n\nfunc Listen(network, address string) (*TCPConn, error) {\n\treturn nil, errors.New(\"faketcp is not supported on this platform\")\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/hysteria/conns/udp/hop.go",
    "content": "package udp\n\nimport (\n\t\"errors\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/transport/hysteria/obfs\"\n\t\"github.com/metacubex/mihomo/transport/hysteria/utils\"\n\n\t\"github.com/metacubex/randv2\"\n)\n\nconst (\n\tpacketQueueSize = 1024\n)\n\n// ObfsUDPHopClientPacketConn is the UDP port-hopping packet connection for client side.\n// It hops to a different local & server port every once in a while.\ntype ObfsUDPHopClientPacketConn struct {\n\tserverAddr  net.Addr // Combined udpHopAddr\n\tserverAddrs []net.Addr\n\thopInterval time.Duration\n\n\tobfs obfs.Obfuscator\n\n\tconnMutex   sync.RWMutex\n\tprevConn    net.PacketConn\n\tcurrentConn net.PacketConn\n\taddrIndex   int\n\n\treadBufferSize  int\n\twriteBufferSize int\n\n\trecvQueue chan *udpPacket\n\tcloseChan chan struct{}\n\tclosed    bool\n\n\tbufPool sync.Pool\n}\n\ntype udpHopAddr string\n\nfunc (a *udpHopAddr) Network() string {\n\treturn \"udp-hop\"\n}\n\nfunc (a *udpHopAddr) String() string {\n\treturn string(*a)\n}\n\ntype udpPacket struct {\n\tbuf  []byte\n\tn    int\n\taddr net.Addr\n}\n\nfunc NewObfsUDPHopClientPacketConn(server string, serverPorts string, hopInterval time.Duration, obfs obfs.Obfuscator, dialer utils.PacketDialer) (net.PacketConn, error) {\n\tports, err := parsePorts(serverPorts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t// Resolve the server IP address, then attach the ports to UDP addresses\n\trAddr, err := dialer.RemoteAddr(server)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tip, _, err := net.SplitHostPort(rAddr.String())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tserverAddrs := make([]net.Addr, len(ports))\n\tfor i, port := range ports {\n\t\tserverAddrs[i] = &net.UDPAddr{\n\t\t\tIP:   net.ParseIP(ip),\n\t\t\tPort: int(port),\n\t\t}\n\t}\n\thopAddr := udpHopAddr(server)\n\tconn := &ObfsUDPHopClientPacketConn{\n\t\tserverAddr:  &hopAddr,\n\t\tserverAddrs: serverAddrs,\n\t\thopInterval: hopInterval,\n\t\tobfs:        obfs,\n\t\taddrIndex:   randv2.IntN(len(serverAddrs)),\n\t\trecvQueue:   make(chan *udpPacket, packetQueueSize),\n\t\tcloseChan:   make(chan struct{}),\n\t\tbufPool: sync.Pool{\n\t\t\tNew: func() interface{} {\n\t\t\t\treturn make([]byte, udpBufferSize)\n\t\t\t},\n\t\t},\n\t}\n\tcurConn, err := dialer.ListenPacket(rAddr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif obfs != nil {\n\t\tconn.currentConn = NewObfsUDPConn(curConn, obfs)\n\t} else {\n\t\tconn.currentConn = curConn\n\t}\n\tgo conn.recvRoutine(conn.currentConn)\n\tgo conn.hopRoutine(dialer, rAddr)\n\tif _, ok := conn.currentConn.(syscall.Conn); ok {\n\t\treturn &ObfsUDPHopClientPacketConnWithSyscall{conn}, nil\n\t}\n\treturn conn, nil\n}\n\nfunc (c *ObfsUDPHopClientPacketConn) recvRoutine(conn net.PacketConn) {\n\tfor {\n\t\tbuf := c.bufPool.Get().([]byte)\n\t\tn, addr, err := conn.ReadFrom(buf)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tselect {\n\t\tcase c.recvQueue <- &udpPacket{buf, n, addr}:\n\t\tdefault:\n\t\t\t// Drop the packet if the queue is full\n\t\t\tc.bufPool.Put(buf)\n\t\t}\n\t}\n}\n\nfunc (c *ObfsUDPHopClientPacketConn) hopRoutine(dialer utils.PacketDialer, rAddr net.Addr) {\n\tticker := time.NewTicker(c.hopInterval)\n\tdefer ticker.Stop()\n\tfor {\n\t\tselect {\n\t\tcase <-ticker.C:\n\t\t\tc.hop(dialer, rAddr)\n\t\tcase <-c.closeChan:\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (c *ObfsUDPHopClientPacketConn) hop(dialer utils.PacketDialer, rAddr net.Addr) {\n\tc.connMutex.Lock()\n\tdefer c.connMutex.Unlock()\n\tif c.closed {\n\t\treturn\n\t}\n\tnewConn, err := dialer.ListenPacket(rAddr)\n\tif err != nil {\n\t\t// Skip this hop if failed to listen\n\t\treturn\n\t}\n\t// Close prevConn,\n\t// prevConn <- currentConn\n\t// currentConn <- newConn\n\t// update addrIndex\n\t//\n\t// We need to keep receiving packets from the previous connection,\n\t// because otherwise there will be packet loss due to the time gap\n\t// between we hop to a new port and the server acknowledges this change.\n\tif c.prevConn != nil {\n\t\t_ = c.prevConn.Close() // recvRoutine will exit on error\n\t}\n\tc.prevConn = c.currentConn\n\tif c.obfs != nil {\n\t\tc.currentConn = NewObfsUDPConn(newConn, c.obfs)\n\t} else {\n\t\tc.currentConn = newConn\n\t}\n\t// Set buffer sizes if previously set\n\tif c.readBufferSize > 0 {\n\t\t_ = trySetPacketConnReadBuffer(c.currentConn, c.readBufferSize)\n\t}\n\tif c.writeBufferSize > 0 {\n\t\t_ = trySetPacketConnWriteBuffer(c.currentConn, c.writeBufferSize)\n\t}\n\tgo c.recvRoutine(c.currentConn)\n\tc.addrIndex = randv2.IntN(len(c.serverAddrs))\n}\n\nfunc (c *ObfsUDPHopClientPacketConn) ReadFrom(b []byte) (int, net.Addr, error) {\n\tfor {\n\t\tselect {\n\t\tcase p := <-c.recvQueue:\n\t\t\t/*\n\t\t\t\t// Check if the packet is from one of the server addresses\n\t\t\t\tfor _, addr := range c.serverAddrs {\n\t\t\t\t\tif addr.String() == p.addr.String() {\n\t\t\t\t\t\t// Copy the packet to the buffer\n\t\t\t\t\t\tn := copy(b, p.buf[:p.n])\n\t\t\t\t\t\tc.bufPool.Put(p.buf)\n\t\t\t\t\t\treturn n, c.serverAddr, nil\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Drop the packet, continue\n\t\t\t\tc.bufPool.Put(p.buf)\n\t\t\t*/\n\t\t\t// The above code was causing performance issues when the range is large,\n\t\t\t// so we skip the check for now. Should probably still check by using a map\n\t\t\t// or something in the future.\n\t\t\tn := copy(b, p.buf[:p.n])\n\t\t\tc.bufPool.Put(p.buf)\n\t\t\treturn n, c.serverAddr, nil\n\t\tcase <-c.closeChan:\n\t\t\treturn 0, nil, net.ErrClosed\n\t\t}\n\t\t// Ignore packets from other addresses\n\t}\n}\n\nfunc (c *ObfsUDPHopClientPacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {\n\tc.connMutex.RLock()\n\tdefer c.connMutex.RUnlock()\n\tif c.closed {\n\t\treturn 0, net.ErrClosed\n\t}\n\t/*\n\t\t// Check if the address is the server address\n\t\tif addr.String() != c.serverAddr.String() {\n\t\t\treturn 0, net.ErrWriteToConnected\n\t\t}\n\t*/\n\t// Skip the check for now, always write to the server\n\treturn c.currentConn.WriteTo(b, c.serverAddrs[c.addrIndex])\n}\n\nfunc (c *ObfsUDPHopClientPacketConn) Close() error {\n\tc.connMutex.Lock()\n\tdefer c.connMutex.Unlock()\n\tif c.closed {\n\t\treturn nil\n\t}\n\t// Close prevConn and currentConn\n\t// Close closeChan to unblock ReadFrom & hopRoutine\n\t// Set closed flag to true to prevent double close\n\tif c.prevConn != nil {\n\t\t_ = c.prevConn.Close()\n\t}\n\terr := c.currentConn.Close()\n\tclose(c.closeChan)\n\tc.closed = true\n\tc.serverAddrs = nil // For GC\n\treturn err\n}\n\nfunc (c *ObfsUDPHopClientPacketConn) LocalAddr() net.Addr {\n\tc.connMutex.RLock()\n\tdefer c.connMutex.RUnlock()\n\treturn c.currentConn.LocalAddr()\n}\n\nfunc (c *ObfsUDPHopClientPacketConn) SetReadDeadline(t time.Time) error {\n\t// Not supported\n\treturn nil\n}\n\nfunc (c *ObfsUDPHopClientPacketConn) SetWriteDeadline(t time.Time) error {\n\t// Not supported\n\treturn nil\n}\n\nfunc (c *ObfsUDPHopClientPacketConn) SetDeadline(t time.Time) error {\n\terr := c.SetReadDeadline(t)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn c.SetWriteDeadline(t)\n}\n\nfunc (c *ObfsUDPHopClientPacketConn) SetReadBuffer(bytes int) error {\n\tc.connMutex.Lock()\n\tdefer c.connMutex.Unlock()\n\tc.readBufferSize = bytes\n\tif c.prevConn != nil {\n\t\t_ = trySetPacketConnReadBuffer(c.prevConn, bytes)\n\t}\n\treturn trySetPacketConnReadBuffer(c.currentConn, bytes)\n}\n\nfunc (c *ObfsUDPHopClientPacketConn) SetWriteBuffer(bytes int) error {\n\tc.connMutex.Lock()\n\tdefer c.connMutex.Unlock()\n\tc.writeBufferSize = bytes\n\tif c.prevConn != nil {\n\t\t_ = trySetPacketConnWriteBuffer(c.prevConn, bytes)\n\t}\n\treturn trySetPacketConnWriteBuffer(c.currentConn, bytes)\n}\n\nfunc trySetPacketConnReadBuffer(pc net.PacketConn, bytes int) error {\n\tsc, ok := pc.(interface {\n\t\tSetReadBuffer(bytes int) error\n\t})\n\tif ok {\n\t\treturn sc.SetReadBuffer(bytes)\n\t}\n\treturn nil\n}\n\nfunc trySetPacketConnWriteBuffer(pc net.PacketConn, bytes int) error {\n\tsc, ok := pc.(interface {\n\t\tSetWriteBuffer(bytes int) error\n\t})\n\tif ok {\n\t\treturn sc.SetWriteBuffer(bytes)\n\t}\n\treturn nil\n}\n\ntype ObfsUDPHopClientPacketConnWithSyscall struct {\n\t*ObfsUDPHopClientPacketConn\n}\n\nfunc (c *ObfsUDPHopClientPacketConnWithSyscall) SyscallConn() (syscall.RawConn, error) {\n\tc.connMutex.RLock()\n\tdefer c.connMutex.RUnlock()\n\tsc, ok := c.currentConn.(syscall.Conn)\n\tif !ok {\n\t\treturn nil, errors.New(\"not supported\")\n\t}\n\treturn sc.SyscallConn()\n}\n\n// parsePorts parses the multi-port server address and returns the host and ports.\n// Supports both comma-separated single ports and dash-separated port ranges.\n// Format: \"host:port1,port2-port3,port4\"\nfunc parsePorts(serverPorts string) (ports []uint16, err error) {\n\tportStrs := strings.Split(serverPorts, \",\")\n\tfor _, portStr := range portStrs {\n\t\tif strings.Contains(portStr, \"-\") {\n\t\t\t// Port range\n\t\t\tportRange := strings.Split(portStr, \"-\")\n\t\t\tif len(portRange) != 2 {\n\t\t\t\treturn nil, net.InvalidAddrError(\"invalid port range\")\n\t\t\t}\n\t\t\tstart, err := strconv.ParseUint(portRange[0], 10, 16)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, net.InvalidAddrError(\"invalid port range\")\n\t\t\t}\n\t\t\tend, err := strconv.ParseUint(portRange[1], 10, 16)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, net.InvalidAddrError(\"invalid port range\")\n\t\t\t}\n\t\t\tif start > end {\n\t\t\t\tstart, end = end, start\n\t\t\t}\n\t\t\tfor i := start; i <= end; i++ {\n\t\t\t\tports = append(ports, uint16(i))\n\t\t\t}\n\t\t} else {\n\t\t\t// Single port\n\t\t\tport, err := strconv.ParseUint(portStr, 10, 16)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, net.InvalidAddrError(\"invalid port\")\n\t\t\t}\n\t\t\tports = append(ports, uint16(port))\n\t\t}\n\t}\n\tif len(ports) == 0 {\n\t\treturn nil, net.InvalidAddrError(\"invalid port\")\n\t}\n\treturn ports, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/hysteria/conns/udp/obfs.go",
    "content": "package udp\n\nimport (\n\t\"github.com/metacubex/mihomo/transport/hysteria/obfs\"\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n)\n\nconst udpBufferSize = 65535\n\ntype ObfsUDPConn struct {\n\torig       net.PacketConn\n\tobfs       obfs.Obfuscator\n\treadBuf    []byte\n\treadMutex  sync.Mutex\n\twriteBuf   []byte\n\twriteMutex sync.Mutex\n}\n\nfunc NewObfsUDPConn(orig net.PacketConn, obfs obfs.Obfuscator) *ObfsUDPConn {\n\treturn &ObfsUDPConn{\n\t\torig:     orig,\n\t\tobfs:     obfs,\n\t\treadBuf:  make([]byte, udpBufferSize),\n\t\twriteBuf: make([]byte, udpBufferSize),\n\t}\n}\n\nfunc (c *ObfsUDPConn) ReadFrom(p []byte) (int, net.Addr, error) {\n\tfor {\n\t\tc.readMutex.Lock()\n\t\tn, addr, err := c.orig.ReadFrom(c.readBuf)\n\t\tif n <= 0 {\n\t\t\tc.readMutex.Unlock()\n\t\t\treturn 0, addr, err\n\t\t}\n\t\tnewN := c.obfs.Deobfuscate(c.readBuf[:n], p)\n\t\tc.readMutex.Unlock()\n\t\tif newN > 0 {\n\t\t\t// Valid packet\n\t\t\treturn newN, addr, err\n\t\t} else if err != nil {\n\t\t\t// Not valid and orig.ReadFrom had some error\n\t\t\treturn 0, addr, err\n\t\t}\n\t}\n}\n\nfunc (c *ObfsUDPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {\n\tc.writeMutex.Lock()\n\tbn := c.obfs.Obfuscate(p, c.writeBuf)\n\t_, err = c.orig.WriteTo(c.writeBuf[:bn], addr)\n\tc.writeMutex.Unlock()\n\tif err != nil {\n\t\treturn 0, err\n\t} else {\n\t\treturn len(p), nil\n\t}\n}\n\nfunc (c *ObfsUDPConn) Close() error {\n\treturn c.orig.Close()\n}\n\nfunc (c *ObfsUDPConn) LocalAddr() net.Addr {\n\treturn c.orig.LocalAddr()\n}\n\nfunc (c *ObfsUDPConn) SetDeadline(t time.Time) error {\n\treturn c.orig.SetDeadline(t)\n}\n\nfunc (c *ObfsUDPConn) SetReadDeadline(t time.Time) error {\n\treturn c.orig.SetReadDeadline(t)\n}\n\nfunc (c *ObfsUDPConn) SetWriteDeadline(t time.Time) error {\n\treturn c.orig.SetWriteDeadline(t)\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/hysteria/conns/wechat/obfs.go",
    "content": "package wechat\n\nimport (\n\t\"encoding/binary\"\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/transport/hysteria/obfs\"\n\n\t\"github.com/metacubex/randv2\"\n)\n\nconst udpBufferSize = 65535\n\ntype ObfsWeChatUDPConn struct {\n\torig       net.PacketConn\n\tobfs       obfs.Obfuscator\n\treadBuf    []byte\n\treadMutex  sync.Mutex\n\twriteBuf   []byte\n\twriteMutex sync.Mutex\n\tsn         uint32\n}\n\nfunc NewObfsWeChatUDPConn(orig net.PacketConn, obfs obfs.Obfuscator) *ObfsWeChatUDPConn {\n\tlog.Infoln(\"new wechat\")\n\treturn &ObfsWeChatUDPConn{\n\t\torig:     orig,\n\t\tobfs:     obfs,\n\t\treadBuf:  make([]byte, udpBufferSize),\n\t\twriteBuf: make([]byte, udpBufferSize),\n\t\tsn:       randv2.Uint32() & 0xFFFF,\n\t}\n}\n\nfunc (c *ObfsWeChatUDPConn) ReadFrom(p []byte) (int, net.Addr, error) {\n\tfor {\n\t\tc.readMutex.Lock()\n\t\tn, addr, err := c.orig.ReadFrom(c.readBuf)\n\t\tif n <= 13 {\n\t\t\tc.readMutex.Unlock()\n\t\t\treturn 0, addr, err\n\t\t}\n\t\tnewN := c.obfs.Deobfuscate(c.readBuf[13:n], p)\n\t\tc.readMutex.Unlock()\n\t\tif newN > 0 {\n\t\t\t// Valid packet\n\t\t\treturn newN, addr, err\n\t\t} else if err != nil {\n\t\t\t// Not valid and orig.ReadFrom had some error\n\t\t\treturn 0, addr, err\n\t\t}\n\t}\n}\n\nfunc (c *ObfsWeChatUDPConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {\n\tc.writeMutex.Lock()\n\tc.writeBuf[0] = 0xa1\n\tc.writeBuf[1] = 0x08\n\tbinary.BigEndian.PutUint32(c.writeBuf[2:], c.sn)\n\tc.sn++\n\tc.writeBuf[6] = 0x00\n\tc.writeBuf[7] = 0x10\n\tc.writeBuf[8] = 0x11\n\tc.writeBuf[9] = 0x18\n\tc.writeBuf[10] = 0x30\n\tc.writeBuf[11] = 0x22\n\tc.writeBuf[12] = 0x30\n\tbn := c.obfs.Obfuscate(p, c.writeBuf[13:])\n\t_, err = c.orig.WriteTo(c.writeBuf[:13+bn], addr)\n\tc.writeMutex.Unlock()\n\tif err != nil {\n\t\treturn 0, err\n\t} else {\n\t\treturn len(p), nil\n\t}\n}\n\nfunc (c *ObfsWeChatUDPConn) Close() error {\n\treturn c.orig.Close()\n}\n\nfunc (c *ObfsWeChatUDPConn) LocalAddr() net.Addr {\n\treturn c.orig.LocalAddr()\n}\n\nfunc (c *ObfsWeChatUDPConn) SetDeadline(t time.Time) error {\n\treturn c.orig.SetDeadline(t)\n}\n\nfunc (c *ObfsWeChatUDPConn) SetReadDeadline(t time.Time) error {\n\treturn c.orig.SetReadDeadline(t)\n}\n\nfunc (c *ObfsWeChatUDPConn) SetWriteDeadline(t time.Time) error {\n\treturn c.orig.SetWriteDeadline(t)\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/hysteria/core/client.go",
    "content": "package core\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"strconv\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/transport/hysteria/obfs\"\n\t\"github.com/metacubex/mihomo/transport/hysteria/pmtud_fix\"\n\t\"github.com/metacubex/mihomo/transport/hysteria/transport\"\n\t\"github.com/metacubex/mihomo/transport/hysteria/utils\"\n\n\t\"github.com/metacubex/quic-go\"\n\t\"github.com/metacubex/quic-go/congestion\"\n\t\"github.com/metacubex/randv2\"\n\t\"github.com/metacubex/tls\"\n)\n\nvar (\n\tErrClosed = errors.New(\"closed\")\n)\n\ntype CongestionFactory func(refBPS uint64) congestion.CongestionControl\n\ntype Client struct {\n\ttransport         *transport.ClientTransport\n\tserverAddr        string\n\tserverPorts       string\n\tprotocol          string\n\tsendBPS, recvBPS  uint64\n\tauth              []byte\n\tcongestionFactory CongestionFactory\n\tobfuscator        obfs.Obfuscator\n\n\ttlsConfig  *tls.Config\n\tquicConfig *quic.Config\n\n\tquicSession    *quic.Conn\n\treconnectMutex sync.Mutex\n\tclosed         bool\n\n\tudpSessionMutex sync.RWMutex\n\tudpSessionMap   map[uint32]chan *udpMessage\n\tudpDefragger    defragger\n\thopInterval     time.Duration\n\tfastOpen        bool\n}\n\nfunc NewClient(serverAddr string, serverPorts string, protocol string, auth []byte, tlsConfig *tls.Config, quicConfig *quic.Config,\n\ttransport *transport.ClientTransport, sendBPS uint64, recvBPS uint64, congestionFactory CongestionFactory,\n\tobfuscator obfs.Obfuscator, hopInterval time.Duration, fastOpen bool) (*Client, error) {\n\tquicConfig.DisablePathMTUDiscovery = quicConfig.DisablePathMTUDiscovery || pmtud_fix.DisablePathMTUDiscovery\n\tc := &Client{\n\t\ttransport:         transport,\n\t\tserverAddr:        serverAddr,\n\t\tserverPorts:       serverPorts,\n\t\tprotocol:          protocol,\n\t\tsendBPS:           sendBPS,\n\t\trecvBPS:           recvBPS,\n\t\tauth:              auth,\n\t\tcongestionFactory: congestionFactory,\n\t\tobfuscator:        obfuscator,\n\t\ttlsConfig:         tlsConfig,\n\t\tquicConfig:        quicConfig,\n\t\thopInterval:       hopInterval,\n\t\tfastOpen:          fastOpen,\n\t}\n\treturn c, nil\n}\n\nfunc (c *Client) connectToServer(dialer utils.PacketDialer) error {\n\tqs, err := c.transport.QUICDial(c.protocol, c.serverAddr, c.serverPorts, c.tlsConfig, c.quicConfig, c.obfuscator, c.hopInterval, dialer)\n\tif err != nil {\n\t\treturn err\n\t}\n\t// Control stream\n\tctx, ctxCancel := context.WithTimeout(context.Background(), protocolTimeout)\n\tstream, err := qs.OpenStreamSync(ctx)\n\tctxCancel()\n\tif err != nil {\n\t\t_ = qs.CloseWithError(closeErrorCodeProtocol, \"protocol error\")\n\t\treturn err\n\t}\n\tok, msg, err := c.handleControlStream(qs, stream)\n\tif err != nil {\n\t\t_ = qs.CloseWithError(closeErrorCodeProtocol, \"protocol error\")\n\t\treturn err\n\t}\n\tif !ok {\n\t\t_ = qs.CloseWithError(closeErrorCodeAuth, \"auth error\")\n\t\treturn fmt.Errorf(\"auth error: %s\", msg)\n\t}\n\t// All good\n\tc.udpSessionMap = make(map[uint32]chan *udpMessage)\n\tgo c.handleMessage(qs)\n\tc.quicSession = qs\n\treturn nil\n}\n\nfunc (c *Client) handleControlStream(qs *quic.Conn, stream *quic.Stream) (bool, string, error) {\n\t// Send client hello\n\terr := WriteClientHello(stream, ClientHello{\n\t\tSendBPS: c.sendBPS,\n\t\tRecvBPS: c.recvBPS,\n\t\tAuth:    c.auth,\n\t})\n\tif err != nil {\n\t\treturn false, \"\", err\n\t}\n\t// Receive server hello\n\tsh, err := ReadServerHello(stream)\n\tif err != nil {\n\t\treturn false, \"\", err\n\t}\n\t// Set the congestion accordingly\n\tif sh.OK && c.congestionFactory != nil {\n\t\tqs.SetCongestionControl(c.congestionFactory(sh.RecvBPS))\n\t}\n\treturn sh.OK, sh.Message, nil\n}\n\nfunc (c *Client) handleMessage(qs *quic.Conn) {\n\tfor {\n\t\tmsg, err := qs.ReceiveDatagram(context.Background())\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tvar udpMsg udpMessage\n\t\terr = udpMsg.Unpack(msg)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\tdfMsg := c.udpDefragger.Feed(udpMsg)\n\t\tif dfMsg == nil {\n\t\t\tcontinue\n\t\t}\n\t\tc.udpSessionMutex.RLock()\n\t\tch, ok := c.udpSessionMap[dfMsg.SessionID]\n\t\tif ok {\n\t\t\tselect {\n\t\t\tcase ch <- dfMsg:\n\t\t\t\t// OK\n\t\t\tdefault:\n\t\t\t\t// Silently drop the message when the channel is full\n\t\t\t}\n\t\t}\n\t\tc.udpSessionMutex.RUnlock()\n\t}\n}\n\nfunc (c *Client) openStreamWithReconnect(dialer utils.PacketDialer) (*quic.Conn, *wrappedQUICStream, error) {\n\tc.reconnectMutex.Lock()\n\tdefer c.reconnectMutex.Unlock()\n\tif c.closed {\n\t\treturn nil, nil, ErrClosed\n\t}\n\tif c.quicSession == nil {\n\t\tif err := c.connectToServer(dialer); err != nil {\n\t\t\t// Still error, oops\n\t\t\treturn nil, nil, err\n\t\t}\n\t}\n\tstream, err := c.quicSession.OpenStream()\n\tif err == nil {\n\t\t// All good\n\t\treturn c.quicSession, &wrappedQUICStream{stream}, nil\n\t}\n\t// Something is wrong\n\tif nErr, ok := err.(net.Error); ok && nErr.Temporary() {\n\t\t// Temporary error, just return\n\t\treturn nil, nil, err\n\t}\n\t// Permanent error, need to reconnect\n\tif err := c.connectToServer(dialer); err != nil {\n\t\t// Still error, oops\n\t\treturn nil, nil, err\n\t}\n\t// We are not going to try again even if it still fails the second time\n\tstream, err = c.quicSession.OpenStream()\n\treturn c.quicSession, &wrappedQUICStream{stream}, err\n}\n\nfunc (c *Client) DialTCP(host string, port uint16, dialer utils.PacketDialer) (net.Conn, error) {\n\tsession, stream, err := c.openStreamWithReconnect(dialer)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t// Send request\n\terr = WriteClientRequest(stream, ClientRequest{\n\t\tUDP:  false,\n\t\tHost: host,\n\t\tPort: port,\n\t})\n\tif err != nil {\n\t\t_ = stream.Close()\n\t\treturn nil, err\n\t}\n\t// If fast open is enabled, we return the stream immediately\n\t// and defer the response handling to the first Read() call\n\tif !c.fastOpen {\n\t\t// Read response\n\t\tvar sr *ServerResponse\n\t\tsr, err = ReadServerResponse(stream)\n\t\tif err != nil {\n\t\t\t_ = stream.Close()\n\t\t\treturn nil, err\n\t\t}\n\t\tif !sr.OK {\n\t\t\t_ = stream.Close()\n\t\t\treturn nil, fmt.Errorf(\"connection rejected: %s\", sr.Message)\n\t\t}\n\t}\n\n\treturn &quicConn{\n\t\tOrig:             stream,\n\t\tPseudoLocalAddr:  session.LocalAddr(),\n\t\tPseudoRemoteAddr: session.RemoteAddr(),\n\t\tEstablished:      !c.fastOpen,\n\t}, nil\n}\n\nfunc (c *Client) DialUDP(dialer utils.PacketDialer) (UDPConn, error) {\n\tsession, stream, err := c.openStreamWithReconnect(dialer)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\t// Send request\n\terr = WriteClientRequest(stream, ClientRequest{\n\t\tUDP: false,\n\t})\n\tif err != nil {\n\t\t_ = stream.Close()\n\t\treturn nil, err\n\t}\n\t// Read response\n\tvar sr *ServerResponse\n\tsr, err = ReadServerResponse(stream)\n\tif err != nil {\n\t\t_ = stream.Close()\n\t\treturn nil, err\n\t}\n\tif !sr.OK {\n\t\t_ = stream.Close()\n\t\treturn nil, fmt.Errorf(\"connection rejected: %s\", sr.Message)\n\t}\n\n\t// Create a session in the map\n\tc.udpSessionMutex.Lock()\n\tnCh := make(chan *udpMessage, 1024)\n\t// Store the current session map for CloseFunc below\n\t// to ensures that we are adding and removing sessions on the same map,\n\t// as reconnecting will reassign the map\n\tsessionMap := c.udpSessionMap\n\tsessionMap[sr.UDPSessionID] = nCh\n\tc.udpSessionMutex.Unlock()\n\n\tpktConn := &quicPktConn{\n\t\tSession: session,\n\t\tStream:  stream,\n\t\tCloseFunc: func() {\n\t\t\tc.udpSessionMutex.Lock()\n\t\t\tif ch, ok := sessionMap[sr.UDPSessionID]; ok {\n\t\t\t\tclose(ch)\n\t\t\t\tdelete(sessionMap, sr.UDPSessionID)\n\t\t\t}\n\t\t\tc.udpSessionMutex.Unlock()\n\t\t},\n\t\tUDPSessionID: sr.UDPSessionID,\n\t\tMsgCh:        nCh,\n\t}\n\tgo pktConn.Hold()\n\treturn pktConn, nil\n}\n\nfunc (c *Client) Close() error {\n\tc.reconnectMutex.Lock()\n\tdefer c.reconnectMutex.Unlock()\n\tvar err error\n\tif c.quicSession != nil {\n\t\terr = c.quicSession.CloseWithError(closeErrorCodeGeneric, \"\")\n\t}\n\tc.closed = true\n\treturn err\n}\n\ntype quicConn struct {\n\tOrig             *wrappedQUICStream\n\tPseudoLocalAddr  net.Addr\n\tPseudoRemoteAddr net.Addr\n\tEstablished      bool\n}\n\nfunc (w *quicConn) Read(b []byte) (n int, err error) {\n\tif !w.Established {\n\t\tvar sr *ServerResponse\n\t\tsr, err = ReadServerResponse(w.Orig)\n\t\tif err != nil {\n\t\t\t_ = w.Close()\n\t\t\treturn 0, err\n\t\t}\n\t\tif !sr.OK {\n\t\t\t_ = w.Close()\n\t\t\treturn 0, fmt.Errorf(\"connection rejected: %s\", sr.Message)\n\t\t}\n\t\tw.Established = true\n\t}\n\treturn w.Orig.Read(b)\n}\n\nfunc (w *quicConn) Write(b []byte) (n int, err error) {\n\treturn w.Orig.Write(b)\n}\n\nfunc (w *quicConn) Close() error {\n\treturn w.Orig.Close()\n}\n\nfunc (w *quicConn) LocalAddr() net.Addr {\n\treturn w.PseudoLocalAddr\n}\n\nfunc (w *quicConn) RemoteAddr() net.Addr {\n\treturn w.PseudoRemoteAddr\n}\n\nfunc (w *quicConn) SetDeadline(t time.Time) error {\n\treturn w.Orig.SetDeadline(t)\n}\n\nfunc (w *quicConn) SetReadDeadline(t time.Time) error {\n\treturn w.Orig.SetReadDeadline(t)\n}\n\nfunc (w *quicConn) SetWriteDeadline(t time.Time) error {\n\treturn w.Orig.SetWriteDeadline(t)\n}\n\ntype UDPConn interface {\n\tReadFrom() ([]byte, string, error)\n\tWriteTo([]byte, string) error\n\tClose() error\n\tLocalAddr() net.Addr\n\tSetDeadline(t time.Time) error\n\tSetReadDeadline(t time.Time) error\n\tSetWriteDeadline(t time.Time) error\n}\n\ntype quicPktConn struct {\n\tSession      *quic.Conn\n\tStream       *wrappedQUICStream\n\tCloseFunc    func()\n\tUDPSessionID uint32\n\tMsgCh        <-chan *udpMessage\n}\n\nfunc (c *quicPktConn) Hold() {\n\t// Hold the stream until it's closed\n\tbuf := make([]byte, 1024)\n\tfor {\n\t\t_, err := c.Stream.Read(buf)\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t}\n\t_ = c.Close()\n}\n\nfunc (c *quicPktConn) ReadFrom() ([]byte, string, error) {\n\tmsg := <-c.MsgCh\n\tif msg == nil {\n\t\t// Closed\n\t\treturn nil, \"\", ErrClosed\n\t}\n\treturn msg.Data, net.JoinHostPort(msg.Host, strconv.Itoa(int(msg.Port))), nil\n}\n\nfunc (c *quicPktConn) WriteTo(p []byte, addr string) error {\n\thost, port, err := utils.SplitHostPort(addr)\n\tif err != nil {\n\t\treturn err\n\t}\n\tmsg := udpMessage{\n\t\tSessionID: c.UDPSessionID,\n\t\tHost:      host,\n\t\tPort:      port,\n\t\tFragCount: 1,\n\t\tData:      p,\n\t}\n\t// try no frag first\n\terr = c.Session.SendDatagram(msg.Pack())\n\tif err != nil {\n\t\tvar errSize *quic.DatagramTooLargeError\n\t\tif errors.As(err, &errSize) {\n\t\t\t// need to frag\n\t\t\tmsg.MsgID = uint16(randv2.IntN(0xFFFF)) + 1 // msgID must be > 0 when fragCount > 1\n\t\t\tfragMsgs := fragUDPMessage(msg, int(errSize.MaxDatagramPayloadSize))\n\t\t\tfor _, fragMsg := range fragMsgs {\n\t\t\t\terr = c.Session.SendDatagram(fragMsg.Pack())\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil\n\t\t} else {\n\t\t\t// some other error\n\t\t\treturn err\n\t\t}\n\t} else {\n\t\treturn nil\n\t}\n}\n\nfunc (c *quicPktConn) Close() error {\n\tc.CloseFunc()\n\treturn c.Stream.Close()\n}\n\nfunc (c *quicPktConn) LocalAddr() net.Addr {\n\treturn c.Session.LocalAddr()\n}\n\nfunc (c *quicPktConn) SetDeadline(t time.Time) error {\n\treturn c.Stream.SetDeadline(t)\n}\n\nfunc (c *quicPktConn) SetReadDeadline(t time.Time) error {\n\treturn c.Stream.SetReadDeadline(t)\n}\n\nfunc (c *quicPktConn) SetWriteDeadline(t time.Time) error {\n\treturn c.Stream.SetWriteDeadline(t)\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/hysteria/core/frag.go",
    "content": "package core\n\nfunc fragUDPMessage(m udpMessage, maxSize int) []udpMessage {\n\tif m.Size() <= maxSize {\n\t\treturn []udpMessage{m}\n\t}\n\tfullPayload := m.Data\n\tmaxPayloadSize := maxSize - m.HeaderSize()\n\toff := 0\n\tfragID := uint8(0)\n\tfragCount := uint8((len(fullPayload) + maxPayloadSize - 1) / maxPayloadSize) // round up\n\tvar frags []udpMessage\n\tfor off < len(fullPayload) {\n\t\tpayloadSize := len(fullPayload) - off\n\t\tif payloadSize > maxPayloadSize {\n\t\t\tpayloadSize = maxPayloadSize\n\t\t}\n\t\tfrag := m\n\t\tfrag.FragID = fragID\n\t\tfrag.FragCount = fragCount\n\t\tfrag.Data = fullPayload[off : off+payloadSize]\n\t\tfrags = append(frags, frag)\n\t\toff += payloadSize\n\t\tfragID++\n\t}\n\treturn frags\n}\n\ntype defragger struct {\n\tmsgID uint16\n\tfrags []*udpMessage\n\tcount uint8\n}\n\nfunc (d *defragger) Feed(m udpMessage) *udpMessage {\n\tif m.FragCount <= 1 {\n\t\treturn &m\n\t}\n\tif m.FragID >= m.FragCount {\n\t\t// wtf is this?\n\t\treturn nil\n\t}\n\tif m.MsgID != d.msgID {\n\t\t// new message, clear previous state\n\t\td.msgID = m.MsgID\n\t\td.frags = make([]*udpMessage, m.FragCount)\n\t\td.count = 1\n\t\td.frags[m.FragID] = &m\n\t} else if d.frags[m.FragID] == nil {\n\t\td.frags[m.FragID] = &m\n\t\td.count++\n\t\tif int(d.count) == len(d.frags) {\n\t\t\t// all fragments received, assemble\n\t\t\tvar data []byte\n\t\t\tfor _, frag := range d.frags {\n\t\t\t\tdata = append(data, frag.Data...)\n\t\t\t}\n\t\t\tm.Data = data\n\t\t\tm.FragID = 0\n\t\t\tm.FragCount = 1\n\t\t\treturn &m\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/hysteria/core/frag_test.go",
    "content": "package core\n\nimport (\n\t\"reflect\"\n\t\"testing\"\n)\n\nfunc Test_fragUDPMessage(t *testing.T) {\n\ttype args struct {\n\t\tm       udpMessage\n\t\tmaxSize int\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\twant []udpMessage\n\t}{\n\t\t{\n\t\t\t\"no frag\",\n\t\t\targs{\n\t\t\t\tudpMessage{\n\t\t\t\t\tSessionID: 123,\n\t\t\t\t\tHost:      \"test\",\n\t\t\t\t\tPort:      123,\n\t\t\t\t\tMsgID:     123,\n\t\t\t\t\tFragID:    0,\n\t\t\t\t\tFragCount: 1,\n\t\t\t\t\tData:      []byte(\"hello\"),\n\t\t\t\t},\n\t\t\t\t100,\n\t\t\t},\n\t\t\t[]udpMessage{\n\t\t\t\tudpMessage{\n\t\t\t\t\tSessionID: 123,\n\t\t\t\t\tHost:      \"test\",\n\t\t\t\t\tPort:      123,\n\t\t\t\t\tMsgID:     123,\n\t\t\t\t\tFragID:    0,\n\t\t\t\t\tFragCount: 1,\n\t\t\t\t\tData:      []byte(\"hello\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"2 frags\",\n\t\t\targs{\n\t\t\t\tudpMessage{\n\t\t\t\t\tSessionID: 123,\n\t\t\t\t\tHost:      \"test\",\n\t\t\t\t\tPort:      123,\n\t\t\t\t\tMsgID:     123,\n\t\t\t\t\tFragID:    0,\n\t\t\t\t\tFragCount: 1,\n\t\t\t\t\tData:      []byte(\"hello\"),\n\t\t\t\t},\n\t\t\t\t22,\n\t\t\t},\n\t\t\t[]udpMessage{\n\t\t\t\tudpMessage{\n\t\t\t\t\tSessionID: 123,\n\t\t\t\t\tHost:      \"test\",\n\t\t\t\t\tPort:      123,\n\t\t\t\t\tMsgID:     123,\n\t\t\t\t\tFragID:    0,\n\t\t\t\t\tFragCount: 2,\n\t\t\t\t\tData:      []byte(\"hell\"),\n\t\t\t\t},\n\t\t\t\tudpMessage{\n\t\t\t\t\tSessionID: 123,\n\t\t\t\t\tHost:      \"test\",\n\t\t\t\t\tPort:      123,\n\t\t\t\t\tMsgID:     123,\n\t\t\t\t\tFragID:    1,\n\t\t\t\t\tFragCount: 2,\n\t\t\t\t\tData:      []byte(\"o\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"4 frags\",\n\t\t\targs{\n\t\t\t\tudpMessage{\n\t\t\t\t\tSessionID: 123,\n\t\t\t\t\tHost:      \"test\",\n\t\t\t\t\tPort:      123,\n\t\t\t\t\tMsgID:     123,\n\t\t\t\t\tFragID:    0,\n\t\t\t\t\tFragCount: 1,\n\t\t\t\t\tData:      []byte(\"wow wow wow lol lmao\"),\n\t\t\t\t},\n\t\t\t\t23,\n\t\t\t},\n\t\t\t[]udpMessage{\n\t\t\t\tudpMessage{\n\t\t\t\t\tSessionID: 123,\n\t\t\t\t\tHost:      \"test\",\n\t\t\t\t\tPort:      123,\n\t\t\t\t\tMsgID:     123,\n\t\t\t\t\tFragID:    0,\n\t\t\t\t\tFragCount: 4,\n\t\t\t\t\tData:      []byte(\"wow w\"),\n\t\t\t\t},\n\t\t\t\tudpMessage{\n\t\t\t\t\tSessionID: 123,\n\t\t\t\t\tHost:      \"test\",\n\t\t\t\t\tPort:      123,\n\t\t\t\t\tMsgID:     123,\n\t\t\t\t\tFragID:    1,\n\t\t\t\t\tFragCount: 4,\n\t\t\t\t\tData:      []byte(\"ow wo\"),\n\t\t\t\t},\n\t\t\t\tudpMessage{\n\t\t\t\t\tSessionID: 123,\n\t\t\t\t\tHost:      \"test\",\n\t\t\t\t\tPort:      123,\n\t\t\t\t\tMsgID:     123,\n\t\t\t\t\tFragID:    2,\n\t\t\t\t\tFragCount: 4,\n\t\t\t\t\tData:      []byte(\"w lol\"),\n\t\t\t\t},\n\t\t\t\tudpMessage{\n\t\t\t\t\tSessionID: 123,\n\t\t\t\t\tHost:      \"test\",\n\t\t\t\t\tPort:      123,\n\t\t\t\t\tMsgID:     123,\n\t\t\t\t\tFragID:    3,\n\t\t\t\t\tFragCount: 4,\n\t\t\t\t\tData:      []byte(\" lmao\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := fragUDPMessage(tt.args.m, tt.args.maxSize); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"fragUDPMessage() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc Test_defragger_Feed(t *testing.T) {\n\td := &defragger{}\n\ttype args struct {\n\t\tm udpMessage\n\t}\n\ttests := []struct {\n\t\tname string\n\t\targs args\n\t\twant *udpMessage\n\t}{\n\t\t{\n\t\t\t\"no frag\",\n\t\t\targs{\n\t\t\t\tudpMessage{\n\t\t\t\t\tSessionID: 123,\n\t\t\t\t\tHost:      \"test\",\n\t\t\t\t\tPort:      123,\n\t\t\t\t\tMsgID:     123,\n\t\t\t\t\tFragID:    0,\n\t\t\t\t\tFragCount: 1,\n\t\t\t\t\tData:      []byte(\"hello\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\t&udpMessage{\n\t\t\t\tSessionID: 123,\n\t\t\t\tHost:      \"test\",\n\t\t\t\tPort:      123,\n\t\t\t\tMsgID:     123,\n\t\t\t\tFragID:    0,\n\t\t\t\tFragCount: 1,\n\t\t\t\tData:      []byte(\"hello\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"frag 1 - 1/3\",\n\t\t\targs{\n\t\t\t\tudpMessage{\n\t\t\t\t\tSessionID: 123,\n\t\t\t\t\tHost:      \"test\",\n\t\t\t\t\tPort:      123,\n\t\t\t\t\tMsgID:     666,\n\t\t\t\t\tFragID:    0,\n\t\t\t\t\tFragCount: 3,\n\t\t\t\t\tData:      []byte(\"hello\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"frag 1 - 2/3\",\n\t\t\targs{\n\t\t\t\tudpMessage{\n\t\t\t\t\tSessionID: 123,\n\t\t\t\t\tHost:      \"test\",\n\t\t\t\t\tPort:      123,\n\t\t\t\t\tMsgID:     666,\n\t\t\t\t\tFragID:    1,\n\t\t\t\t\tFragCount: 3,\n\t\t\t\t\tData:      []byte(\" shitty \"),\n\t\t\t\t},\n\t\t\t},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"frag 1 - 3/3\",\n\t\t\targs{\n\t\t\t\tudpMessage{\n\t\t\t\t\tSessionID: 123,\n\t\t\t\t\tHost:      \"test\",\n\t\t\t\t\tPort:      123,\n\t\t\t\t\tMsgID:     666,\n\t\t\t\t\tFragID:    2,\n\t\t\t\t\tFragCount: 3,\n\t\t\t\t\tData:      []byte(\"world!!\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\t&udpMessage{\n\t\t\t\tSessionID: 123,\n\t\t\t\tHost:      \"test\",\n\t\t\t\tPort:      123,\n\t\t\t\tMsgID:     666,\n\t\t\t\tFragID:    0,\n\t\t\t\tFragCount: 1,\n\t\t\t\tData:      []byte(\"hello shitty world!!\"),\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\t\"frag 2 - 1/2\",\n\t\t\targs{\n\t\t\t\tudpMessage{\n\t\t\t\t\tSessionID: 123,\n\t\t\t\t\tHost:      \"test\",\n\t\t\t\t\tPort:      123,\n\t\t\t\t\tMsgID:     777,\n\t\t\t\t\tFragID:    0,\n\t\t\t\t\tFragCount: 2,\n\t\t\t\t\tData:      []byte(\"hello\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"frag 3 - 2/2\",\n\t\t\targs{\n\t\t\t\tudpMessage{\n\t\t\t\t\tSessionID: 123,\n\t\t\t\t\tHost:      \"test\",\n\t\t\t\t\tPort:      123,\n\t\t\t\t\tMsgID:     778,\n\t\t\t\t\tFragID:    1,\n\t\t\t\t\tFragCount: 2,\n\t\t\t\t\tData:      []byte(\" moto\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"frag 2 - 2/2\",\n\t\t\targs{\n\t\t\t\tudpMessage{\n\t\t\t\t\tSessionID: 123,\n\t\t\t\t\tHost:      \"test\",\n\t\t\t\t\tPort:      123,\n\t\t\t\t\tMsgID:     777,\n\t\t\t\t\tFragID:    1,\n\t\t\t\t\tFragCount: 2,\n\t\t\t\t\tData:      []byte(\" moto\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\tnil,\n\t\t},\n\t\t{\n\t\t\t\"frag 2 - 1/2 re\",\n\t\t\targs{\n\t\t\t\tudpMessage{\n\t\t\t\t\tSessionID: 123,\n\t\t\t\t\tHost:      \"test\",\n\t\t\t\t\tPort:      123,\n\t\t\t\t\tMsgID:     777,\n\t\t\t\t\tFragID:    0,\n\t\t\t\t\tFragCount: 2,\n\t\t\t\t\tData:      []byte(\"hello\"),\n\t\t\t\t},\n\t\t\t},\n\t\t\t&udpMessage{\n\t\t\t\tSessionID: 123,\n\t\t\t\tHost:      \"test\",\n\t\t\t\tPort:      123,\n\t\t\t\tMsgID:     777,\n\t\t\t\tFragID:    0,\n\t\t\t\tFragCount: 1,\n\t\t\t\tData:      []byte(\"hello moto\"),\n\t\t\t},\n\t\t},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tif got := d.Feed(tt.args.m); !reflect.DeepEqual(got, tt.want) {\n\t\t\t\tt.Errorf(\"Feed() = %v, want %v\", got, tt.want)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/hysteria/core/protocol.go",
    "content": "package core\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"io\"\n\t\"time\"\n)\n\nconst (\n\tprotocolVersion = uint8(3)\n\tprotocolTimeout = 10 * time.Second\n\n\tcloseErrorCodeGeneric  = 0\n\tcloseErrorCodeProtocol = 1\n\tcloseErrorCodeAuth     = 2\n)\n\ntype ClientHello struct {\n\tSendBPS uint64\n\tRecvBPS uint64\n\tAuth    []byte\n}\n\nfunc WriteClientHello(stream io.Writer, hello ClientHello) error {\n\tvar requestLen int\n\trequestLen += 1 // version\n\trequestLen += 8 // sendBPS\n\trequestLen += 8 // recvBPS\n\trequestLen += 2 // auth len\n\trequestLen += len(hello.Auth)\n\trequest := make([]byte, requestLen)\n\trequest[0] = protocolVersion\n\tbinary.BigEndian.PutUint64(request[1:9], hello.SendBPS)\n\tbinary.BigEndian.PutUint64(request[9:17], hello.RecvBPS)\n\tbinary.BigEndian.PutUint16(request[17:19], uint16(len(hello.Auth)))\n\tcopy(request[19:], hello.Auth)\n\t_, err := stream.Write(request)\n\treturn err\n}\n\nfunc ReadClientHello(stream io.Reader) (*ClientHello, error) {\n\tvar responseLen int\n\tresponseLen += 1 // ok\n\tresponseLen += 8 // sendBPS\n\tresponseLen += 8 // recvBPS\n\tresponseLen += 2 // auth len\n\tresponse := make([]byte, responseLen)\n\t_, err := io.ReadFull(stream, response)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif response[0] != protocolVersion {\n\t\treturn nil, errors.New(\"unsupported client version\")\n\t}\n\tvar clientHello ClientHello\n\tclientHello.SendBPS = binary.BigEndian.Uint64(response[1:9])\n\tclientHello.RecvBPS = binary.BigEndian.Uint64(response[9:17])\n\tauthLen := binary.BigEndian.Uint16(response[17:19])\n\n\tif clientHello.SendBPS == 0 || clientHello.RecvBPS == 0 {\n\t\treturn nil, errors.New(\"invalid rate from client\")\n\t}\n\n\tauthBytes := make([]byte, authLen)\n\t_, err = io.ReadFull(stream, authBytes)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tclientHello.Auth = authBytes\n\treturn &clientHello, nil\n}\n\ntype ServerHello struct {\n\tOK      bool\n\tSendBPS uint64\n\tRecvBPS uint64\n\tMessage string\n}\n\nfunc ReadServerHello(stream io.Reader) (*ServerHello, error) {\n\tvar responseLen int\n\tresponseLen += 1 // ok\n\tresponseLen += 8 // sendBPS\n\tresponseLen += 8 // recvBPS\n\tresponseLen += 2 // message len\n\tresponse := make([]byte, responseLen)\n\t_, err := io.ReadFull(stream, response)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar serverHello ServerHello\n\tserverHello.OK = response[0] == 1\n\tserverHello.SendBPS = binary.BigEndian.Uint64(response[1:9])\n\tserverHello.RecvBPS = binary.BigEndian.Uint64(response[9:17])\n\tmessageLen := binary.BigEndian.Uint16(response[17:19])\n\tif messageLen == 0 {\n\t\treturn &serverHello, nil\n\t}\n\tmessage := make([]byte, messageLen)\n\t_, err = io.ReadFull(stream, message)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tserverHello.Message = string(message)\n\treturn &serverHello, nil\n}\n\nfunc WriteServerHello(stream io.Writer, hello ServerHello) error {\n\tvar responseLen int\n\tresponseLen += 1 // ok\n\tresponseLen += 8 // sendBPS\n\tresponseLen += 8 // recvBPS\n\tresponseLen += 2 // message len\n\tresponseLen += len(hello.Message)\n\tresponse := make([]byte, responseLen)\n\tif hello.OK {\n\t\tresponse[0] = 1\n\t} else {\n\t\tresponse[0] = 0\n\t}\n\tbinary.BigEndian.PutUint64(response[1:9], hello.SendBPS)\n\tbinary.BigEndian.PutUint64(response[9:17], hello.RecvBPS)\n\tbinary.BigEndian.PutUint16(response[17:19], uint16(len(hello.Message)))\n\tcopy(response[19:], hello.Message)\n\t_, err := stream.Write(response)\n\treturn err\n}\n\ntype ClientRequest struct {\n\tUDP  bool\n\tHost string\n\tPort uint16\n}\n\nfunc ReadClientRequest(stream io.Reader) (*ClientRequest, error) {\n\tvar clientRequest ClientRequest\n\terr := binary.Read(stream, binary.BigEndian, &clientRequest.UDP)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar hostLen uint16\n\terr = binary.Read(stream, binary.BigEndian, &hostLen)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\thost := make([]byte, hostLen)\n\t_, err = io.ReadFull(stream, host)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tclientRequest.Host = string(host)\n\terr = binary.Read(stream, binary.BigEndian, &clientRequest.Port)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &clientRequest, nil\n}\n\nfunc WriteClientRequest(stream io.Writer, request ClientRequest) error {\n\tvar requestLen int\n\trequestLen += 1 // udp\n\trequestLen += 2 // host len\n\trequestLen += len(request.Host)\n\trequestLen += 2 // port\n\tbuffer := make([]byte, requestLen)\n\tif request.UDP {\n\t\tbuffer[0] = 1\n\t} else {\n\t\tbuffer[0] = 0\n\t}\n\tbinary.BigEndian.PutUint16(buffer[1:3], uint16(len(request.Host)))\n\tn := copy(buffer[3:], request.Host)\n\tbinary.BigEndian.PutUint16(buffer[3+n:3+n+2], request.Port)\n\t_, err := stream.Write(buffer)\n\treturn err\n}\n\ntype ServerResponse struct {\n\tOK           bool\n\tUDPSessionID uint32\n\tMessage      string\n}\n\nfunc ReadServerResponse(stream io.Reader) (*ServerResponse, error) {\n\tvar responseLen int\n\tresponseLen += 1 // ok\n\tresponseLen += 4 // udp session id\n\tresponseLen += 2 // message len\n\tresponse := make([]byte, responseLen)\n\t_, err := io.ReadFull(stream, response)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tvar serverResponse ServerResponse\n\tserverResponse.OK = response[0] == 1\n\tserverResponse.UDPSessionID = binary.BigEndian.Uint32(response[1:5])\n\tmessageLen := binary.BigEndian.Uint16(response[5:7])\n\tif messageLen == 0 {\n\t\treturn &serverResponse, nil\n\t}\n\tmessage := make([]byte, messageLen)\n\t_, err = io.ReadFull(stream, message)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tserverResponse.Message = string(message)\n\treturn &serverResponse, nil\n}\n\nfunc WriteServerResponse(stream io.Writer, response ServerResponse) error {\n\tvar responseLen int\n\tresponseLen += 1 // ok\n\tresponseLen += 4 // udp session id\n\tresponseLen += 2 // message len\n\tresponseLen += len(response.Message)\n\tbuffer := make([]byte, responseLen)\n\tif response.OK {\n\t\tbuffer[0] = 1\n\t} else {\n\t\tbuffer[0] = 0\n\t}\n\tbinary.BigEndian.PutUint32(buffer[1:5], response.UDPSessionID)\n\tbinary.BigEndian.PutUint16(buffer[5:7], uint16(len(response.Message)))\n\tcopy(buffer[7:], response.Message)\n\t_, err := stream.Write(buffer)\n\treturn err\n}\n\ntype udpMessage struct {\n\tSessionID uint32\n\tHost      string\n\tPort      uint16\n\tMsgID     uint16 // doesn't matter when not fragmented, but must not be 0 when fragmented\n\tFragID    uint8  // doesn't matter when not fragmented, starts at 0 when fragmented\n\tFragCount uint8  // must be 1 when not fragmented\n\tData      []byte\n}\n\nfunc (m udpMessage) HeaderSize() int {\n\treturn 4 + 2 + len(m.Host) + 2 + 2 + 1 + 1 + 2\n}\n\nfunc (m udpMessage) Size() int {\n\treturn m.HeaderSize() + len(m.Data)\n}\n\nfunc (m udpMessage) Pack() []byte {\n\tdata := make([]byte, m.Size())\n\tbuffer := bytes.NewBuffer(data)\n\t_ = binary.Write(buffer, binary.BigEndian, m.SessionID)\n\t_ = binary.Write(buffer, binary.BigEndian, uint16(len(m.Host)))\n\tbuffer.WriteString(m.Host)\n\t_ = binary.Write(buffer, binary.BigEndian, m.Port)\n\t_ = binary.Write(buffer, binary.BigEndian, m.MsgID)\n\t_ = binary.Write(buffer, binary.BigEndian, m.FragID)\n\t_ = binary.Write(buffer, binary.BigEndian, m.FragCount)\n\t_ = binary.Write(buffer, binary.BigEndian, uint16(len(m.Data)))\n\tbuffer.Write(m.Data)\n\treturn buffer.Bytes()\n}\n\nfunc (m *udpMessage) Unpack(data []byte) error {\n\treader := bytes.NewReader(data)\n\terr := binary.Read(reader, binary.BigEndian, &m.SessionID)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar hostLen uint16\n\terr = binary.Read(reader, binary.BigEndian, &hostLen)\n\tif err != nil {\n\t\treturn err\n\t}\n\thostBytes := make([]byte, hostLen)\n\t_, err = io.ReadFull(reader, hostBytes)\n\tif err != nil {\n\t\treturn err\n\t}\n\tm.Host = string(hostBytes)\n\terr = binary.Read(reader, binary.BigEndian, &m.Port)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = binary.Read(reader, binary.BigEndian, &m.MsgID)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = binary.Read(reader, binary.BigEndian, &m.FragID)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = binary.Read(reader, binary.BigEndian, &m.FragCount)\n\tif err != nil {\n\t\treturn err\n\t}\n\tvar dataLen uint16\n\terr = binary.Read(reader, binary.BigEndian, &dataLen)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif reader.Len() != int(dataLen) {\n\t\treturn errors.New(\"invalid data length\")\n\t}\n\tm.Data = data[len(data)-reader.Len():]\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/hysteria/core/stream.go",
    "content": "package core\n\nimport (\n\t\"context\"\n\t\"github.com/metacubex/quic-go\"\n\t\"time\"\n)\n\n// Handle stream close properly\n// Ref: https://github.com/libp2p/go-libp2p-quic-transport/blob/master/stream.go\ntype wrappedQUICStream struct {\n\tStream *quic.Stream\n}\n\nfunc (s *wrappedQUICStream) StreamID() quic.StreamID {\n\treturn s.Stream.StreamID()\n}\n\nfunc (s *wrappedQUICStream) Read(p []byte) (n int, err error) {\n\treturn s.Stream.Read(p)\n}\n\nfunc (s *wrappedQUICStream) CancelRead(code quic.StreamErrorCode) {\n\ts.Stream.CancelRead(code)\n}\n\nfunc (s *wrappedQUICStream) SetReadDeadline(t time.Time) error {\n\treturn s.Stream.SetReadDeadline(t)\n}\n\nfunc (s *wrappedQUICStream) Write(p []byte) (n int, err error) {\n\treturn s.Stream.Write(p)\n}\n\nfunc (s *wrappedQUICStream) Close() error {\n\ts.Stream.CancelRead(0)\n\treturn s.Stream.Close()\n}\n\nfunc (s *wrappedQUICStream) CancelWrite(code quic.StreamErrorCode) {\n\ts.Stream.CancelWrite(code)\n}\n\nfunc (s *wrappedQUICStream) Context() context.Context {\n\treturn s.Stream.Context()\n}\n\nfunc (s *wrappedQUICStream) SetWriteDeadline(t time.Time) error {\n\treturn s.Stream.SetWriteDeadline(t)\n}\n\nfunc (s *wrappedQUICStream) SetDeadline(t time.Time) error {\n\treturn s.Stream.SetDeadline(t)\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/hysteria/obfs/dummy.go",
    "content": "package obfs\n\ntype DummyObfuscator struct{}\n\nfunc NewDummyObfuscator() *DummyObfuscator {\n\treturn &DummyObfuscator{}\n}\n\nfunc (x *DummyObfuscator) Deobfuscate(in []byte, out []byte) int {\n\tif len(out) < len(in) {\n\t\treturn 0\n\t}\n\treturn copy(out, in)\n}\n\nfunc (x *DummyObfuscator) Obfuscate(in []byte, out []byte) int {\n\treturn copy(out, in)\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/hysteria/obfs/obfs.go",
    "content": "package obfs\n\ntype Obfuscator interface {\n\tDeobfuscate(in []byte, out []byte) int\n\tObfuscate(in []byte, out []byte) int\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/hysteria/obfs/xplus.go",
    "content": "package obfs\n\nimport (\n\t\"crypto/rand\"\n\t\"crypto/sha256\"\n)\n\n// [salt][obfuscated payload]\n\nconst saltLen = 16\n\ntype XPlusObfuscator struct {\n\tKey []byte\n}\n\nfunc NewXPlusObfuscator(key []byte) *XPlusObfuscator {\n\treturn &XPlusObfuscator{\n\t\tKey: key,\n\t}\n}\n\nfunc (x *XPlusObfuscator) Deobfuscate(in []byte, out []byte) int {\n\tpLen := len(in) - saltLen\n\tif pLen <= 0 || len(out) < pLen {\n\t\t// Invalid\n\t\treturn 0\n\t}\n\tkey := sha256.Sum256(append(x.Key, in[:saltLen]...))\n\t// Deobfuscate the payload\n\tfor i, c := range in[saltLen:] {\n\t\tout[i] = c ^ key[i%sha256.Size]\n\t}\n\treturn pLen\n}\n\nfunc (x *XPlusObfuscator) Obfuscate(in []byte, out []byte) int {\n\t_, _ = rand.Read(out[:saltLen]) // salt\n\t// Obfuscate the payload\n\tkey := sha256.Sum256(append(x.Key, out[:saltLen]...))\n\tfor i, c := range in {\n\t\tout[i+saltLen] = c ^ key[i%sha256.Size]\n\t}\n\treturn len(in) + saltLen\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/hysteria/obfs/xplus_test.go",
    "content": "package obfs\n\nimport (\n\t\"bytes\"\n\t\"testing\"\n)\n\nfunc TestXPlusObfuscator(t *testing.T) {\n\tx := NewXPlusObfuscator([]byte(\"Vaundy\"))\n\ttests := []struct {\n\t\tname string\n\t\tp    []byte\n\t}{\n\t\t{name: \"1\", p: []byte(\"HelloWorld\")},\n\t\t{name: \"2\", p: []byte(\"Regret is just a horrible attempt at time travel that ends with you feeling like crap\")},\n\t\t{name: \"3\", p: []byte(\"To be, or not to be, that is the question:\\nWhether 'tis nobler in the mind to suffer\\n\" +\n\t\t\t\"The slings and arrows of outrageous fortune,\\nOr to take arms against a sea of troubles\\n\" +\n\t\t\t\"And by opposing end them. To die—to sleep,\\nNo more; and by a sleep to say we end\")},\n\t\t{name: \"empty\", p: []byte(\"\")},\n\t}\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tbuf := make([]byte, 10240)\n\t\t\tn := x.Obfuscate(tt.p, buf)\n\t\t\tn2 := x.Deobfuscate(buf[:n], buf[n:])\n\t\t\tif !bytes.Equal(tt.p, buf[n:n+n2]) {\n\t\t\t\tt.Errorf(\"Inconsistent deobfuscate result: got %v, want %v\", buf[n:n+n2], tt.p)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/hysteria/pmtud_fix/avail.go",
    "content": "//go:build linux || windows || darwin\n\npackage pmtud_fix\n\nconst (\n\tDisablePathMTUDiscovery = false\n)\n"
  },
  {
    "path": "core/Clash.Meta/transport/hysteria/pmtud_fix/unavail.go",
    "content": "//go:build !linux && !windows && !darwin\n\npackage pmtud_fix\n\nconst (\n\tDisablePathMTUDiscovery = true\n)\n"
  },
  {
    "path": "core/Clash.Meta/transport/hysteria/transport/client.go",
    "content": "package transport\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/transport/hysteria/conns/faketcp\"\n\t\"github.com/metacubex/mihomo/transport/hysteria/conns/udp\"\n\t\"github.com/metacubex/mihomo/transport/hysteria/conns/wechat\"\n\tobfsPkg \"github.com/metacubex/mihomo/transport/hysteria/obfs\"\n\t\"github.com/metacubex/mihomo/transport/hysteria/utils\"\n\n\t\"github.com/metacubex/quic-go\"\n\t\"github.com/metacubex/tls\"\n)\n\ntype ClientTransport struct{}\n\nfunc (ct *ClientTransport) quicPacketConn(proto string, rAddr net.Addr, serverPorts string, obfs obfsPkg.Obfuscator, hopInterval time.Duration, dialer utils.PacketDialer) (net.PacketConn, error) {\n\tserver := rAddr.String()\n\tif len(proto) == 0 || proto == \"udp\" {\n\t\tconn, err := dialer.ListenPacket(rAddr)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif obfs != nil {\n\t\t\tif serverPorts != \"\" {\n\t\t\t\treturn udp.NewObfsUDPHopClientPacketConn(server, serverPorts, hopInterval, obfs, dialer)\n\t\t\t}\n\t\t\toc := udp.NewObfsUDPConn(conn, obfs)\n\t\t\treturn oc, nil\n\t\t} else {\n\t\t\tif serverPorts != \"\" {\n\t\t\t\treturn udp.NewObfsUDPHopClientPacketConn(server, serverPorts, hopInterval, nil, dialer)\n\t\t\t}\n\t\t\treturn conn, nil\n\t\t}\n\t} else if proto == \"wechat-video\" {\n\t\tconn, err := dialer.ListenPacket(rAddr)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif obfs == nil {\n\t\t\tobfs = obfsPkg.NewDummyObfuscator()\n\t\t}\n\t\treturn wechat.NewObfsWeChatUDPConn(conn, obfs), nil\n\t} else if proto == \"faketcp\" {\n\t\tvar conn *faketcp.TCPConn\n\t\tconn, err := faketcp.Dial(\"tcp\", server)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif obfs != nil {\n\t\t\toc := faketcp.NewObfsFakeTCPConn(conn, obfs)\n\t\t\treturn oc, nil\n\t\t} else {\n\t\t\treturn conn, nil\n\t\t}\n\t} else {\n\t\treturn nil, fmt.Errorf(\"unsupported protocol: %s\", proto)\n\t}\n}\n\nfunc (ct *ClientTransport) QUICDial(proto string, server string, serverPorts string, tlsConfig *tls.Config, quicConfig *quic.Config, obfs obfsPkg.Obfuscator, hopInterval time.Duration, dialer utils.PacketDialer) (*quic.Conn, error) {\n\tserverUDPAddr, err := dialer.RemoteAddr(server)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tpktConn, err := ct.quicPacketConn(proto, serverUDPAddr, serverPorts, obfs, hopInterval, dialer)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttransport := quic.Transport{Conn: pktConn}\n\ttransport.SetCreatedConn(true) // auto close conn\n\ttransport.SetSingleUse(true)   // auto close transport\n\tqs, err := transport.Dial(dialer.Context(), serverUDPAddr, tlsConfig, quicConfig)\n\tif err != nil {\n\t\t_ = pktConn.Close()\n\t\treturn nil, err\n\t}\n\treturn qs, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/hysteria/utils/misc.go",
    "content": "package utils\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"strconv\"\n)\n\nfunc SplitHostPort(hostport string) (string, uint16, error) {\n\thost, port, err := net.SplitHostPort(hostport)\n\tif err != nil {\n\t\treturn \"\", 0, err\n\t}\n\tportUint, err := strconv.ParseUint(port, 10, 16)\n\tif err != nil {\n\t\treturn \"\", 0, err\n\t}\n\treturn host, uint16(portUint), err\n}\n\nfunc ParseIPZone(s string) (net.IP, string) {\n\ts, zone := splitHostZone(s)\n\treturn net.ParseIP(s), zone\n}\n\nfunc splitHostZone(s string) (host, zone string) {\n\tif i := last(s, '%'); i > 0 {\n\t\thost, zone = s[:i], s[i+1:]\n\t} else {\n\t\thost = s\n\t}\n\treturn\n}\n\nfunc last(s string, b byte) int {\n\ti := len(s)\n\tfor i--; i >= 0; i-- {\n\t\tif s[i] == b {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn i\n}\n\ntype PacketDialer interface {\n\tListenPacket(rAddr net.Addr) (net.PacketConn, error)\n\tContext() context.Context\n\tRemoteAddr(host string) (net.Addr, error)\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/kcptun/client.go",
    "content": "package kcptun\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/metacubex/kcp-go\"\n\t\"github.com/metacubex/randv2\"\n\t\"github.com/metacubex/smux\"\n)\n\nconst Mode = \"kcptun\"\n\ntype DialFn func(ctx context.Context) (net.PacketConn, net.Addr, error)\n\ntype Client struct {\n\tonce   sync.Once\n\tconfig Config\n\tblock  kcp.BlockCrypt\n\n\tctx    context.Context\n\tcancel context.CancelFunc\n\n\tnumconn uint16\n\tmuxes   []timedSession\n\trr      uint16\n\tconnMu  sync.Mutex\n\n\tchScavenger chan timedSession\n}\n\nfunc NewClient(config Config) *Client {\n\tconfig.FillDefaults()\n\tblock := config.NewBlock()\n\n\tctx, cancel := context.WithCancel(context.Background())\n\n\treturn &Client{\n\t\tconfig: config,\n\t\tblock:  block,\n\t\tctx:    ctx,\n\t\tcancel: cancel,\n\t}\n}\n\nfunc (c *Client) Close() error {\n\tc.cancel()\n\treturn nil\n}\n\nfunc (c *Client) createConn(ctx context.Context, dial DialFn) (*smux.Session, error) {\n\tconn, addr, err := dial(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tconfig := c.config\n\tconvid := randv2.Uint32()\n\tkcpconn, err := kcp.NewConn4(convid, addr, c.block, config.DataShard, config.ParityShard, true, conn)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tkcpconn.SetStreamMode(true)\n\tkcpconn.SetWriteDelay(false)\n\tkcpconn.SetNoDelay(config.NoDelay, config.Interval, config.Resend, config.NoCongestion)\n\tkcpconn.SetWindowSize(config.SndWnd, config.RcvWnd)\n\tkcpconn.SetMtu(config.MTU)\n\tkcpconn.SetACKNoDelay(config.AckNodelay)\n\tkcpconn.SetRateLimit(uint32(config.RateLimit))\n\n\t_ = kcpconn.SetDSCP(config.DSCP)\n\t_ = kcpconn.SetReadBuffer(config.SockBuf)\n\t_ = kcpconn.SetWriteBuffer(config.SockBuf)\n\tsmuxConfig := smux.DefaultConfig()\n\tsmuxConfig.Version = config.SmuxVer\n\tsmuxConfig.MaxReceiveBuffer = config.SmuxBuf\n\tsmuxConfig.MaxStreamBuffer = config.StreamBuf\n\tsmuxConfig.MaxFrameSize = config.FrameSize\n\tsmuxConfig.KeepAliveInterval = time.Duration(config.KeepAlive) * time.Second\n\tif smuxConfig.KeepAliveInterval >= smuxConfig.KeepAliveTimeout {\n\t\tsmuxConfig.KeepAliveTimeout = 3 * smuxConfig.KeepAliveInterval\n\t}\n\n\tif err := smux.VerifyConfig(smuxConfig); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar netConn net.Conn = kcpconn\n\tif !config.NoComp {\n\t\tnetConn = NewCompStream(netConn)\n\t}\n\t// stream multiplex\n\treturn smux.Client(netConn, smuxConfig)\n}\n\nfunc (c *Client) OpenStream(ctx context.Context, dial DialFn) (*smux.Stream, error) {\n\tc.once.Do(func() {\n\t\t// start scavenger if autoexpire is set\n\t\tc.chScavenger = make(chan timedSession, 128)\n\t\tif c.config.AutoExpire > 0 {\n\t\t\tgo scavenger(c.ctx, c.chScavenger, &c.config)\n\t\t}\n\n\t\tc.numconn = uint16(c.config.Conn)\n\t\tc.muxes = make([]timedSession, c.config.Conn)\n\t\tc.rr = uint16(0)\n\t})\n\n\tc.connMu.Lock()\n\tidx := c.rr % c.numconn\n\n\t// do auto expiration && reconnection\n\tif c.muxes[idx].session == nil || c.muxes[idx].session.IsClosed() ||\n\t\t(c.config.AutoExpire > 0 && time.Now().After(c.muxes[idx].expiryDate)) {\n\t\tvar err error\n\t\tc.muxes[idx].session, err = c.createConn(ctx, dial)\n\t\tif err != nil {\n\t\t\tc.connMu.Unlock()\n\t\t\treturn nil, err\n\t\t}\n\t\tc.muxes[idx].expiryDate = time.Now().Add(time.Duration(c.config.AutoExpire) * time.Second)\n\t\tif c.config.AutoExpire > 0 { // only when autoexpire set\n\t\t\tc.chScavenger <- c.muxes[idx]\n\t\t}\n\n\t}\n\tc.rr++\n\tsession := c.muxes[idx].session\n\tc.connMu.Unlock()\n\n\treturn session.OpenStream()\n}\n\n// timedSession is a wrapper for smux.Session with expiry date\ntype timedSession struct {\n\tsession    *smux.Session\n\texpiryDate time.Time\n}\n\n// scavenger goroutine is used to close expired sessions\nfunc scavenger(ctx context.Context, ch chan timedSession, config *Config) {\n\tticker := time.NewTicker(scavengePeriod * time.Second)\n\tdefer ticker.Stop()\n\tvar sessionList []timedSession\n\tfor {\n\t\tselect {\n\t\tcase item := <-ch:\n\t\t\tsessionList = append(sessionList, timedSession{\n\t\t\t\titem.session,\n\t\t\t\titem.expiryDate.Add(time.Duration(config.ScavengeTTL) * time.Second)})\n\t\tcase <-ticker.C:\n\t\t\tvar newList []timedSession\n\t\t\tfor k := range sessionList {\n\t\t\t\ts := sessionList[k]\n\t\t\t\tif s.session.IsClosed() {\n\t\t\t\t\tlog.Debugln(\"scavenger: session normally closed: %s\", s.session.LocalAddr())\n\t\t\t\t} else if time.Now().After(s.expiryDate) {\n\t\t\t\t\ts.session.Close()\n\t\t\t\t\tlog.Debugln(\"scavenger: session closed due to ttl: %s\", s.session.LocalAddr())\n\t\t\t\t} else {\n\t\t\t\t\tnewList = append(newList, sessionList[k])\n\t\t\t\t}\n\t\t\t}\n\t\t\tsessionList = newList\n\t\tcase <-ctx.Done():\n\t\t\treturn\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/kcptun/common.go",
    "content": "package kcptun\n\nimport (\n\t\"crypto/sha1\"\n\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/metacubex/kcp-go\"\n\t\"golang.org/x/crypto/pbkdf2\"\n)\n\nconst (\n\t// SALT is use for pbkdf2 key expansion\n\tSALT = \"kcp-go\"\n\t// maximum supported smux version\n\tmaxSmuxVer = 2\n\t// scavenger check period\n\tscavengePeriod = 5\n)\n\ntype Config struct {\n\tKey          string `json:\"key\"`\n\tCrypt        string `json:\"crypt\"`\n\tMode         string `json:\"mode\"`\n\tConn         int    `json:\"conn\"`\n\tAutoExpire   int    `json:\"autoexpire\"`\n\tScavengeTTL  int    `json:\"scavengettl\"`\n\tMTU          int    `json:\"mtu\"`\n\tRateLimit    int    `json:\"ratelimit\"`\n\tSndWnd       int    `json:\"sndwnd\"`\n\tRcvWnd       int    `json:\"rcvwnd\"`\n\tDataShard    int    `json:\"datashard\"`\n\tParityShard  int    `json:\"parityshard\"`\n\tDSCP         int    `json:\"dscp\"`\n\tNoComp       bool   `json:\"nocomp\"`\n\tAckNodelay   bool   `json:\"acknodelay\"`\n\tNoDelay      int    `json:\"nodelay\"`\n\tInterval     int    `json:\"interval\"`\n\tResend       int    `json:\"resend\"`\n\tNoCongestion int    `json:\"nc\"`\n\tSockBuf      int    `json:\"sockbuf\"`\n\tSmuxVer      int    `json:\"smuxver\"`\n\tSmuxBuf      int    `json:\"smuxbuf\"`\n\tFrameSize    int    `json:\"framesize\"`\n\tStreamBuf    int    `json:\"streambuf\"`\n\tKeepAlive    int    `json:\"keepalive\"`\n}\n\nfunc (config *Config) FillDefaults() {\n\tif config.Key == \"\" {\n\t\tconfig.Key = \"it's a secrect\"\n\t}\n\tif config.Crypt == \"\" {\n\t\tconfig.Crypt = \"aes\"\n\t}\n\tif config.Mode == \"\" {\n\t\tconfig.Mode = \"fast\"\n\t}\n\tif config.Conn == 0 {\n\t\tconfig.Conn = 1\n\t}\n\tif config.ScavengeTTL == 0 {\n\t\tconfig.ScavengeTTL = 600\n\t}\n\tif config.MTU == 0 {\n\t\tconfig.MTU = 1350\n\t}\n\tif config.SndWnd == 0 {\n\t\tconfig.SndWnd = 128\n\t}\n\tif config.RcvWnd == 0 {\n\t\tconfig.RcvWnd = 512\n\t}\n\tif config.DataShard == 0 {\n\t\tconfig.DataShard = 10\n\t}\n\tif config.ParityShard == 0 {\n\t\tconfig.ParityShard = 3\n\t}\n\tif config.Interval == 0 {\n\t\tconfig.Interval = 50\n\t}\n\tif config.SockBuf == 0 {\n\t\tconfig.SockBuf = 4194304\n\t}\n\tif config.SmuxVer == 0 {\n\t\tconfig.SmuxVer = 1\n\t}\n\tif config.SmuxBuf == 0 {\n\t\tconfig.SmuxBuf = 4194304\n\t}\n\tif config.FrameSize == 0 {\n\t\tconfig.FrameSize = 8192\n\t}\n\tif config.StreamBuf == 0 {\n\t\tconfig.StreamBuf = 2097152\n\t}\n\tif config.KeepAlive == 0 {\n\t\tconfig.KeepAlive = 10\n\t}\n\tswitch config.Mode {\n\tcase \"normal\":\n\t\tconfig.NoDelay, config.Interval, config.Resend, config.NoCongestion = 0, 40, 2, 1\n\tcase \"fast\":\n\t\tconfig.NoDelay, config.Interval, config.Resend, config.NoCongestion = 0, 30, 2, 1\n\tcase \"fast2\":\n\t\tconfig.NoDelay, config.Interval, config.Resend, config.NoCongestion = 1, 20, 2, 1\n\tcase \"fast3\":\n\t\tconfig.NoDelay, config.Interval, config.Resend, config.NoCongestion = 1, 10, 2, 1\n\t}\n\n\t// SMUX Version check\n\tif config.SmuxVer > maxSmuxVer {\n\t\tlog.Warnln(\"unsupported smux version: %d\", config.SmuxVer)\n\t\tconfig.SmuxVer = maxSmuxVer\n\t}\n\n\t// Scavenge parameters check\n\tif config.AutoExpire != 0 && config.ScavengeTTL > config.AutoExpire {\n\t\tlog.Warnln(\"WARNING: scavengettl is bigger than autoexpire, connections may race hard to use bandwidth.\")\n\t\tlog.Warnln(\"Try limiting scavengettl to a smaller value.\")\n\t}\n}\n\nfunc (config *Config) NewBlock() (block kcp.BlockCrypt) {\n\tpass := pbkdf2.Key([]byte(config.Key), []byte(SALT), 4096, 32, sha1.New)\n\tswitch config.Crypt {\n\tcase \"null\":\n\t\tblock = nil\n\tcase \"tea\":\n\t\tblock, _ = kcp.NewTEABlockCrypt(pass[:16])\n\tcase \"xor\":\n\t\tblock, _ = kcp.NewSimpleXORBlockCrypt(pass)\n\tcase \"none\":\n\t\tblock, _ = kcp.NewNoneBlockCrypt(pass)\n\tcase \"aes-128\":\n\t\tblock, _ = kcp.NewAESBlockCrypt(pass[:16])\n\tcase \"aes-192\":\n\t\tblock, _ = kcp.NewAESBlockCrypt(pass[:24])\n\tcase \"blowfish\":\n\t\tblock, _ = kcp.NewBlowfishBlockCrypt(pass)\n\tcase \"twofish\":\n\t\tblock, _ = kcp.NewTwofishBlockCrypt(pass)\n\tcase \"cast5\":\n\t\tblock, _ = kcp.NewCast5BlockCrypt(pass[:16])\n\tcase \"3des\":\n\t\tblock, _ = kcp.NewTripleDESBlockCrypt(pass[:24])\n\tcase \"xtea\":\n\t\tblock, _ = kcp.NewXTEABlockCrypt(pass[:16])\n\tcase \"salsa20\":\n\t\tblock, _ = kcp.NewSalsa20BlockCrypt(pass)\n\tcase \"aes-128-gcm\":\n\t\tblock, _ = kcp.NewAESGCMCrypt(pass[:16])\n\tdefault:\n\t\tconfig.Crypt = \"aes\"\n\t\tblock, _ = kcp.NewAESBlockCrypt(pass)\n\t}\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/kcptun/comp.go",
    "content": "package kcptun\n\nimport (\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/golang/snappy\"\n)\n\n// CompStream is a net.Conn wrapper that compresses data using snappy\ntype CompStream struct {\n\tconn net.Conn\n\tw    *snappy.Writer\n\tr    *snappy.Reader\n}\n\nfunc (c *CompStream) Read(p []byte) (n int, err error) {\n\treturn c.r.Read(p)\n}\n\nfunc (c *CompStream) Write(p []byte) (n int, err error) {\n\tif _, err := c.w.Write(p); err != nil {\n\t\treturn 0, err\n\t}\n\n\tif err := c.w.Flush(); err != nil {\n\t\treturn 0, err\n\t}\n\treturn len(p), err\n}\n\nfunc (c *CompStream) Close() error {\n\treturn c.conn.Close()\n}\n\nfunc (c *CompStream) LocalAddr() net.Addr {\n\treturn c.conn.LocalAddr()\n}\n\nfunc (c *CompStream) RemoteAddr() net.Addr {\n\treturn c.conn.RemoteAddr()\n}\n\nfunc (c *CompStream) SetDeadline(t time.Time) error {\n\treturn c.conn.SetDeadline(t)\n}\n\nfunc (c *CompStream) SetReadDeadline(t time.Time) error {\n\treturn c.conn.SetReadDeadline(t)\n}\n\nfunc (c *CompStream) SetWriteDeadline(t time.Time) error {\n\treturn c.conn.SetWriteDeadline(t)\n}\n\n// NewCompStream creates a new stream that compresses data using snappy\nfunc NewCompStream(conn net.Conn) *CompStream {\n\tc := new(CompStream)\n\tc.conn = conn\n\tc.w = snappy.NewBufferedWriter(conn)\n\tc.r = snappy.NewReader(conn)\n\treturn c\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/kcptun/doc.go",
    "content": "// Package kcptun copy and modify from:\n// https://github.com/xtaci/kcptun/tree/f54f35175bed6ddda4e47aa35c9d7ae8b7e7eb85\n// adopt for mihomo\n// without SM4,QPP,tcpraw support\npackage kcptun\n"
  },
  {
    "path": "core/Clash.Meta/transport/kcptun/server.go",
    "content": "package kcptun\n\nimport (\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/metacubex/kcp-go\"\n\t\"github.com/metacubex/smux\"\n)\n\ntype Server struct {\n\tconfig Config\n\tblock  kcp.BlockCrypt\n}\n\nfunc NewServer(config Config) *Server {\n\tconfig.FillDefaults()\n\tblock := config.NewBlock()\n\n\treturn &Server{\n\t\tconfig: config,\n\t\tblock:  block,\n\t}\n}\n\nfunc (s *Server) Serve(pc net.PacketConn, handler func(net.Conn)) error {\n\tlis, err := kcp.ServeConn(s.block, s.config.DataShard, s.config.ParityShard, pc)\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer lis.Close()\n\t_ = lis.SetDSCP(s.config.DSCP)\n\t_ = lis.SetReadBuffer(s.config.SockBuf)\n\t_ = lis.SetWriteBuffer(s.config.SockBuf)\n\tfor {\n\t\tconn, err := lis.AcceptKCP()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tconn.SetStreamMode(true)\n\t\tconn.SetWriteDelay(false)\n\t\tconn.SetNoDelay(s.config.NoDelay, s.config.Interval, s.config.Resend, s.config.NoCongestion)\n\t\tconn.SetMtu(s.config.MTU)\n\t\tconn.SetWindowSize(s.config.SndWnd, s.config.RcvWnd)\n\t\tconn.SetACKNoDelay(s.config.AckNodelay)\n\t\tconn.SetRateLimit(uint32(s.config.RateLimit))\n\n\t\tvar netConn net.Conn = conn\n\t\tif !s.config.NoComp {\n\t\t\tnetConn = NewCompStream(netConn)\n\t\t}\n\n\t\tgo func() {\n\t\t\t// stream multiplex\n\t\t\tsmuxConfig := smux.DefaultConfig()\n\t\t\tsmuxConfig.Version = s.config.SmuxVer\n\t\t\tsmuxConfig.MaxReceiveBuffer = s.config.SmuxBuf\n\t\t\tsmuxConfig.MaxStreamBuffer = s.config.StreamBuf\n\t\t\tsmuxConfig.MaxFrameSize = s.config.FrameSize\n\t\t\tsmuxConfig.KeepAliveInterval = time.Duration(s.config.KeepAlive) * time.Second\n\t\t\tif smuxConfig.KeepAliveInterval >= smuxConfig.KeepAliveTimeout {\n\t\t\t\tsmuxConfig.KeepAliveTimeout = 3 * smuxConfig.KeepAliveInterval\n\t\t\t}\n\n\t\t\tmux, err := smux.Server(netConn, smuxConfig)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdefer mux.Close()\n\n\t\t\tfor {\n\t\t\t\tstream, err := mux.AcceptStream()\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tgo handler(stream)\n\t\t\t}\n\t\t}()\n\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/masque/client_h2.go",
    "content": "package masque\n\n// copy and modify from: https://github.com/Diniboy1123/connect-ip-go/blob/8d7bb0a858a2674046a7cb5538749e4c826c3538/client_h2.go\n\nimport (\n\t\"context\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/url\"\n\t\"strings\"\n\t\"sync\"\n\n\t\"github.com/metacubex/mihomo/common/contextutils\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/quic-go/quicvarint\"\n\t\"github.com/yosida95/uritemplate/v3\"\n)\n\nconst h2DatagramCapsuleType uint64 = 0\n\nconst (\n\tipv4HeaderLen = 20\n\tipv6HeaderLen = 40\n)\n\nfunc ConnectTunnelH2(ctx context.Context, h2Transport *http.Transport, connectUri string) (*http.ClientConn, IpConn, error) {\n\tadditionalHeaders := http.Header{\n\t\t\"User-Agent\": []string{\"\"},\n\t}\n\ttemplate := uritemplate.MustNew(connectUri)\n\n\th2Headers := additionalHeaders.Clone()\n\th2Headers.Set(\"cf-connect-proto\", \"cf-connect-ip\")\n\t// TODO: support PQC\n\th2Headers.Set(\"pq-enabled\", \"false\")\n\n\tcc, err := h2Transport.NewClientConn(ctx, \"https\", \":0\")\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"connect-ip: failed to create client connection: %w\", err)\n\t}\n\n\tif err = cc.Reserve(); err != nil {\n\t\t_ = cc.Close()\n\t\treturn nil, nil, fmt.Errorf(\"connect-ip: failed to reserve client connection: %w\", err)\n\t}\n\n\tipConn, rsp, err := dialH2(ctx, cc, template, h2Headers)\n\tif err != nil {\n\t\t_ = cc.Close()\n\t\tif strings.Contains(err.Error(), \"tls: access denied\") {\n\t\t\treturn nil, nil, errors.New(\"login failed! Please double-check if your tls key and cert is enrolled in the Cloudflare Access service\")\n\t\t}\n\t\treturn nil, nil, fmt.Errorf(\"failed to dial connect-ip over HTTP/2: %w\", err)\n\t}\n\n\tif rsp.StatusCode != http.StatusOK {\n\t\t_ = ipConn.Close()\n\t\t_ = cc.Close()\n\t\treturn nil, nil, fmt.Errorf(\"failed to dial connect-ip: %v\", rsp.Status)\n\t}\n\n\treturn cc, ipConn, nil\n}\n\n// dialH2 dials a proxied connection over HTTP/2 CONNECT-IP.\n//\n// This transport carries proxied packets inside HTTP capsule DATAGRAM frames.\nfunc dialH2(ctx context.Context, rt http.RoundTripper, template *uritemplate.Template, additionalHeaders http.Header) (*h2IpConn, *http.Response, error) {\n\tif len(template.Varnames()) > 0 {\n\t\treturn nil, nil, errors.New(\"connect-ip: IP flow forwarding not supported\")\n\t}\n\n\tu, err := url.Parse(template.Raw())\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"connect-ip: failed to parse URI: %w\", err)\n\t}\n\n\treqCtx, cancel := context.WithCancel(context.Background()) // reqCtx must disconnect from ctx, otherwise ctx would close the entire HTTP/2 connection.\n\n\tpr, pw := io.Pipe()\n\treq, err := http.NewRequestWithContext(reqCtx, http.MethodConnect, u.String(), pr)\n\tif err != nil {\n\t\tcancel()\n\t\t_ = pr.Close()\n\t\t_ = pw.Close()\n\t\treturn nil, nil, fmt.Errorf(\"connect-ip: failed to create request: %w\", err)\n\t}\n\treq.Host = authorityFromURL(u)\n\treq.ContentLength = -1\n\treq.Header = make(http.Header)\n\tfor k, v := range additionalHeaders {\n\t\treq.Header[k] = v\n\t}\n\n\tstop := contextutils.AfterFunc(ctx, cancel) // temporarily connect ctx with reqCtx when client.Do\n\trsp, err := rt.RoundTrip(req)\n\tstop() // disconnect ctx with reqCtx after client.Do\n\tif err != nil {\n\t\tcancel()\n\t\t_ = pr.Close()\n\t\t_ = pw.Close()\n\t\treturn nil, nil, fmt.Errorf(\"connect-ip: failed to send request: %w\", err)\n\t}\n\tif rsp.StatusCode < 200 || rsp.StatusCode > 299 {\n\t\tcancel()\n\t\t_ = pr.Close()\n\t\t_ = pw.Close()\n\t\t_ = rsp.Body.Close()\n\t\treturn nil, rsp, fmt.Errorf(\"connect-ip: server responded with %d\", rsp.StatusCode)\n\t}\n\n\tstream := &h2DatagramStream{\n\t\trequestBody:  pw,\n\t\tresponseBody: rsp.Body,\n\t\tcancel:       cancel,\n\t}\n\treturn &h2IpConn{\n\t\tstr:       stream,\n\t\tcloseChan: make(chan struct{}),\n\t}, rsp, nil\n}\n\nfunc authorityFromURL(u *url.URL) string {\n\tif u.Port() != \"\" {\n\t\treturn u.Host\n\t}\n\thost := u.Hostname()\n\tif host == \"\" {\n\t\treturn u.Host\n\t}\n\treturn host + \":443\"\n}\n\ntype h2IpConn struct {\n\tstr *h2DatagramStream\n\n\tmu sync.Mutex\n\n\tcloseChan chan struct{}\n\tcloseErr  error\n}\n\nfunc (c *h2IpConn) ReadPacket() (b []byte, err error) {\nstart:\n\tdata, err := c.str.ReceiveDatagram(context.Background())\n\tif err != nil {\n\t\tdefer func() {\n\t\t\t// There are no errors that can be recovered in h2 mode,\n\t\t\t// so calling Close allows the outer read loop to exit in the next iteration by returning net.ErrClosed.\n\t\t\t_ = c.Close()\n\t\t}()\n\t\tselect {\n\t\tcase <-c.closeChan:\n\t\t\treturn nil, c.closeErr\n\t\tdefault:\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tif err := c.handleIncomingProxiedPacket(data); err != nil {\n\t\tlog.Debugln(\"dropping proxied packet: %s\", err)\n\t\tgoto start\n\t}\n\treturn data, nil\n}\n\nfunc (c *h2IpConn) handleIncomingProxiedPacket(data []byte) error {\n\tif len(data) == 0 {\n\t\treturn errors.New(\"connect-ip: empty packet\")\n\t}\n\tswitch v := ipVersion(data); v {\n\tdefault:\n\t\treturn fmt.Errorf(\"connect-ip: unknown IP versions: %d\", v)\n\tcase 4:\n\t\tif len(data) < ipv4HeaderLen {\n\t\t\treturn fmt.Errorf(\"connect-ip: malformed datagram: too short\")\n\t\t}\n\tcase 6:\n\t\tif len(data) < ipv6HeaderLen {\n\t\t\treturn fmt.Errorf(\"connect-ip: malformed datagram: too short\")\n\t\t}\n\t}\n\treturn nil\n}\n\n// WritePacket writes an IP packet to the stream.\n// If sending the packet fails, it might return an ICMP packet.\n// It is the caller's responsibility to send the ICMP packet to the sender.\nfunc (c *h2IpConn) WritePacket(b []byte) (icmp []byte, err error) {\n\tdata, err := c.composeDatagram(b)\n\tif err != nil {\n\t\tlog.Debugln(\"dropping proxied packet (%d bytes) that can't be proxied: %s\", len(b), err)\n\t\treturn nil, nil\n\t}\n\tif err := c.str.SendDatagram(data); err != nil {\n\t\tselect {\n\t\tcase <-c.closeChan:\n\t\t\treturn nil, c.closeErr\n\t\tdefault:\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn nil, nil\n}\n\nfunc (c *h2IpConn) composeDatagram(b []byte) ([]byte, error) {\n\t// TODO: implement src, dst and ipproto checks\n\tif len(b) == 0 {\n\t\treturn nil, nil\n\t}\n\tswitch v := ipVersion(b); v {\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"connect-ip: unknown IP versions: %d\", v)\n\tcase 4:\n\t\tif len(b) < ipv4HeaderLen {\n\t\t\treturn nil, fmt.Errorf(\"connect-ip: IPv4 packet too short\")\n\t\t}\n\t\tttl := b[8]\n\t\tif ttl <= 1 {\n\t\t\treturn nil, fmt.Errorf(\"connect-ip: datagram TTL too small: %d\", ttl)\n\t\t}\n\t\tb[8]-- // decrement TTL\n\t\t// recalculate the checksum\n\t\tbinary.BigEndian.PutUint16(b[10:12], calculateIPv4Checksum(([ipv4HeaderLen]byte)(b[:ipv4HeaderLen])))\n\tcase 6:\n\t\tif len(b) < ipv6HeaderLen {\n\t\t\treturn nil, fmt.Errorf(\"connect-ip: IPv6 packet too short\")\n\t\t}\n\t\thopLimit := b[7]\n\t\tif hopLimit <= 1 {\n\t\t\treturn nil, fmt.Errorf(\"connect-ip: datagram Hop Limit too small: %d\", hopLimit)\n\t\t}\n\t\tb[7]-- // Decrement Hop Limit\n\t}\n\treturn b, nil\n}\n\nfunc (c *h2IpConn) Close() error {\n\tc.mu.Lock()\n\tif c.closeErr == nil {\n\t\tc.closeErr = net.ErrClosed\n\t\tclose(c.closeChan)\n\t}\n\tc.mu.Unlock()\n\terr := c.str.Close()\n\treturn err\n}\n\nfunc ipVersion(b []byte) uint8 { return b[0] >> 4 }\n\nfunc calculateIPv4Checksum(header [ipv4HeaderLen]byte) uint16 {\n\t// add every 16-bit word in the header, skipping the checksum field (bytes 10 and 11)\n\tvar sum uint32\n\tfor i := 0; i < len(header); i += 2 {\n\t\tif i == 10 {\n\t\t\tcontinue // skip checksum field\n\t\t}\n\t\tsum += uint32(binary.BigEndian.Uint16(header[i : i+2]))\n\t}\n\tfor (sum >> 16) > 0 {\n\t\tsum = (sum & 0xffff) + (sum >> 16)\n\t}\n\treturn ^uint16(sum)\n}\n\ntype h2DatagramStream struct {\n\trequestBody  *io.PipeWriter\n\tresponseBody io.ReadCloser\n\tcancel       context.CancelFunc\n\n\treadMu  sync.Mutex\n\twriteMu sync.Mutex\n}\n\nfunc (s *h2DatagramStream) ReceiveDatagram(_ context.Context) ([]byte, error) {\n\ts.readMu.Lock()\n\tdefer s.readMu.Unlock()\n\n\treader := quicvarint.NewReader(s.responseBody)\n\tfor {\n\t\tcapsuleType, err := quicvarint.Read(reader)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpayloadLen, err := quicvarint.Read(reader)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tpayload := make([]byte, payloadLen)\n\t\t_, err = io.ReadFull(reader, payload)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif capsuleType != h2DatagramCapsuleType {\n\t\t\tcontinue\n\t\t}\n\t\treturn payload, nil\n\t}\n}\n\nfunc (s *h2DatagramStream) SendDatagram(data []byte) error {\n\tframe := make([]byte, 0, quicvarint.Len(h2DatagramCapsuleType)+quicvarint.Len(uint64(len(data)))+len(data))\n\tframe = quicvarint.Append(frame, h2DatagramCapsuleType)\n\tframe = quicvarint.Append(frame, uint64(len(data)))\n\tframe = append(frame, data...)\n\n\ts.writeMu.Lock()\n\tdefer s.writeMu.Unlock()\n\t_, err := s.requestBody.Write(frame)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"connect-ip: failed to send datagram capsule: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc (s *h2DatagramStream) Close() error {\n\t_ = s.requestBody.Close()\n\terr := s.responseBody.Close()\n\ts.cancel()\n\treturn err\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/masque/masque.go",
    "content": "// Package masque\n// copy and modify from https://github.com/Diniboy1123/usque/blob/d0eb96e7e5c56cce6cf34a7f8d75abbedba58fef/api/masque.go\npackage masque\n\nimport (\n\t\"context\"\n\t\"crypto/ecdsa\"\n\t\"crypto/rand\"\n\t\"crypto/x509\"\n\t\"errors\"\n\t\"fmt\"\n\t\"math/big\"\n\t\"net/netip\"\n\t\"net/url\"\n\t\"time\"\n\n\tconnectip \"github.com/metacubex/connect-ip-go\"\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/quic-go\"\n\t\"github.com/metacubex/quic-go/http3\"\n\t\"github.com/metacubex/tls\"\n\t\"github.com/yosida95/uritemplate/v3\"\n)\n\nconst (\n\tConnectSNI = \"consumer-masque.cloudflareclient.com\"\n\tConnectURI = \"https://cloudflareaccess.com\"\n)\n\ntype IpConn interface {\n\tReadPacket() (b []byte, err error)\n\tWritePacket(b []byte) (icmp []byte, err error)\n\tClose() error\n}\n\n// PrepareTlsConfig creates a TLS configuration using the provided certificate and SNI (Server Name Indication).\n// It also verifies the peer's public key against the provided public key.\nfunc PrepareTlsConfig(privKey *ecdsa.PrivateKey, peerPubKey *ecdsa.PublicKey, sni string, insecure bool) (*tls.Config, error) {\n\tverfiyCert := func(cert *x509.Certificate) error {\n\t\tif _, ok := cert.PublicKey.(*ecdsa.PublicKey); !ok {\n\t\t\t// we only support ECDSA\n\t\t\t// TODO: don't hardcode cert type in the future\n\t\t\t// as backend can start using different cert types\n\t\t\treturn x509.ErrUnsupportedAlgorithm\n\t\t}\n\n\t\tif !cert.PublicKey.(*ecdsa.PublicKey).Equal(peerPubKey) {\n\t\t\t// reason is incorrect, but the best I could figure\n\t\t\t// detail explains the actual reason\n\n\t\t\t//10 is NoValidChains, but we support go1.22 where it's not defined\n\t\t\treturn x509.CertificateInvalidError{Cert: cert, Reason: 10, Detail: \"remote endpoint has a different public key than what we trust\"}\n\t\t}\n\n\t\treturn nil\n\t}\n\n\tcert, err := GenerateCert(privKey)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to generate cert: %v\", err)\n\t}\n\n\ttlsConfig := &tls.Config{\n\t\tCertificates: []tls.Certificate{\n\t\t\t{\n\t\t\t\tCertificate: cert,\n\t\t\t\tPrivateKey:  privKey,\n\t\t\t},\n\t\t},\n\t\tServerName: sni,\n\t\tNextProtos: []string{http3.NextProtoH3},\n\t\t// WARN: SNI is usually not for the endpoint, so we must skip verification\n\t\tInsecureSkipVerify: true,\n\t\t// we pin to the endpoint public key\n\t\tVerifyConnection: func(cs tls.ConnectionState) error {\n\t\t\tvar err error\n\t\t\tfor _, cert := range cs.PeerCertificates {\n\t\t\t\tif er := verfiyCert(cert); er != nil {\n\t\t\t\t\terr = errors.Join(err, er)\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn err\n\t\t},\n\t}\n\tif insecure {\n\t\ttlsConfig.VerifyConnection = nil\n\t}\n\n\treturn tlsConfig, nil\n}\n\nfunc GenerateCert(privKey *ecdsa.PrivateKey) ([][]byte, error) {\n\tcert, err := x509.CreateCertificate(rand.Reader, &x509.Certificate{\n\t\tSerialNumber: big.NewInt(0),\n\t\tNotBefore:    time.Now(),\n\t\tNotAfter:     time.Now().Add(1 * 24 * time.Hour),\n\t}, &x509.Certificate{}, &privKey.PublicKey, privKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn [][]byte{cert}, nil\n}\n\n// ConnectTunnel establishes a QUIC connection and sets up a Connect-IP tunnel with the provided endpoint.\n// Endpoint address is used to check whether the authentication/connection is successful or not.\n// Requires modified connect-ip-go for now to support Cloudflare's non RFC compliant implementation.\nfunc ConnectTunnel(ctx context.Context, quicConn *quic.Conn, connectUri string) (*http3.Transport, *connectip.Conn, error) {\n\ttr := &http3.Transport{\n\t\tEnableDatagrams: true,\n\t\tAdditionalSettings: map[uint64]uint64{\n\t\t\t// official client still sends this out as well, even though\n\t\t\t// it's deprecated, see https://datatracker.ietf.org/doc/draft-ietf-masque-h3-datagram/00/\n\t\t\t// SETTINGS_H3_DATAGRAM_00 = 0x0000000000000276\n\t\t\t// https://github.com/cloudflare/quiche/blob/7c66757dbc55b8d0c3653d4b345c6785a181f0b7/quiche/src/h3/frame.rs#L46\n\t\t\t0x276: 1,\n\t\t},\n\t\tDisableCompression: true,\n\t}\n\n\thconn := tr.NewClientConn(quicConn)\n\n\tadditionalHeaders := http.Header{\n\t\t\"User-Agent\": []string{\"\"},\n\t}\n\n\ttemplate := uritemplate.MustNew(connectUri)\n\tipConn, rsp, err := dialEx(ctx, hconn, template, \"cf-connect-ip\", additionalHeaders, true)\n\tif err != nil {\n\t\t_ = tr.Close()\n\t\tif err.Error() == \"CRYPTO_ERROR 0x131 (remote): tls: access denied\" {\n\t\t\treturn nil, nil, errors.New(\"login failed! Please double-check if your tls key and cert is enrolled in the Cloudflare Access service\")\n\t\t}\n\t\treturn nil, nil, fmt.Errorf(\"failed to dial connect-ip: %v\", err)\n\t}\n\n\terr = ipConn.AdvertiseRoute(ctx, []connectip.IPRoute{\n\t\t{\n\t\t\tIPProtocol: 0,\n\t\t\tStartIP:    netip.AddrFrom4([4]byte{}),\n\t\t\tEndIP:      netip.AddrFrom4([4]byte{255, 255, 255, 255}),\n\t\t},\n\t\t{\n\t\t\tIPProtocol: 0,\n\t\t\tStartIP:    netip.AddrFrom16([16]byte{}),\n\t\t\tEndIP: netip.AddrFrom16([16]byte{\n\t\t\t\t255, 255, 255, 255,\n\t\t\t\t255, 255, 255, 255,\n\t\t\t\t255, 255, 255, 255,\n\t\t\t\t255, 255, 255, 255,\n\t\t\t}),\n\t\t},\n\t})\n\tif err != nil {\n\t\t_ = ipConn.Close()\n\t\t_ = tr.Close()\n\t\treturn nil, nil, err\n\t}\n\n\tif rsp.StatusCode != http.StatusOK {\n\t\t_ = ipConn.Close()\n\t\t_ = tr.Close()\n\t\treturn nil, nil, fmt.Errorf(\"failed to dial connect-ip: %v\", rsp.Status)\n\t}\n\n\treturn tr, ipConn, nil\n}\n\n// dialEx dials a proxied connection to a target server.\nfunc dialEx(ctx context.Context, conn *http3.ClientConn, template *uritemplate.Template, requestProtocol string, additionalHeaders http.Header, ignoreExtendedConnect bool) (*connectip.Conn, *http.Response, error) {\n\tif len(template.Varnames()) > 0 {\n\t\treturn nil, nil, errors.New(\"connect-ip: IP flow forwarding not supported\")\n\t}\n\n\tu, err := url.Parse(template.Raw())\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"connect-ip: failed to parse URI: %w\", err)\n\t}\n\n\tselect {\n\tcase <-ctx.Done():\n\t\treturn nil, nil, context.Cause(ctx)\n\tcase <-conn.Context().Done():\n\t\treturn nil, nil, context.Cause(conn.Context())\n\tcase <-conn.ReceivedSettings():\n\t}\n\tsettings := conn.Settings()\n\tif !ignoreExtendedConnect && !settings.EnableExtendedConnect {\n\t\treturn nil, nil, errors.New(\"connect-ip: server didn't enable Extended CONNECT\")\n\t}\n\tif !settings.EnableDatagrams {\n\t\treturn nil, nil, errors.New(\"connect-ip: server didn't enable datagrams\")\n\t}\n\n\tconst capsuleProtocolHeaderValue = \"?1\"\n\theaders := http.Header{http3.CapsuleProtocolHeader: []string{capsuleProtocolHeaderValue}}\n\tfor k, v := range additionalHeaders {\n\t\theaders[k] = v\n\t}\n\n\trstr, err := conn.OpenRequestStream(ctx)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"connect-ip: failed to open request stream: %w\", err)\n\t}\n\tif err := rstr.SendRequestHeader(&http.Request{\n\t\tMethod: http.MethodConnect,\n\t\tProto:  requestProtocol,\n\t\tHost:   u.Host,\n\t\tHeader: headers,\n\t\tURL:    u,\n\t}); err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"connect-ip: failed to send request: %w\", err)\n\t}\n\t// TODO: optimistically return the connection\n\trsp, err := rstr.ReadResponse()\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"connect-ip: failed to read response: %w\", err)\n\t}\n\tif rsp.StatusCode < 200 || rsp.StatusCode > 299 {\n\t\treturn nil, rsp, fmt.Errorf(\"connect-ip: server responded with %d\", rsp.StatusCode)\n\t}\n\treturn connectip.NewProxiedConn(rstr), rsp, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/restls/restls.go",
    "content": "package restls\n\nimport (\n\t\"context\"\n\t\"net\"\n\n\ttls \"github.com/metacubex/restls-client-go\"\n)\n\nconst (\n\tMode string = \"restls\"\n)\n\ntype Restls struct {\n\t*tls.UConn\n}\n\nfunc (r *Restls) Upstream() any {\n\treturn r.UConn.NetConn()\n}\n\ntype Config = tls.Config\n\nvar NewRestlsConfig = tls.NewRestlsConfig\n\n// NewRestls return a Restls Connection\nfunc NewRestls(ctx context.Context, conn net.Conn, config *Config) (net.Conn, error) {\n\tclientHellowID := tls.HelloChrome_Auto\n\tif config != nil {\n\t\tclientIDPtr := config.ClientID.Load()\n\t\tif clientIDPtr != nil {\n\t\t\tclientHellowID = *clientIDPtr\n\t\t}\n\t}\n\trestls := &Restls{\n\t\tUConn: tls.UClient(conn, config, clientHellowID),\n\t}\n\tif err := restls.HandshakeContext(ctx); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn restls, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/shadowsocks/core/cipher.go",
    "content": "package core\n\nimport (\n\t\"crypto/md5\"\n\t\"errors\"\n\t\"net\"\n\t\"sort\"\n\t\"strings\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/transport/shadowsocks/shadowaead\"\n\t\"github.com/metacubex/mihomo/transport/shadowsocks/shadowstream\"\n)\n\ntype Cipher interface {\n\tStreamConnCipher\n\tPacketConnCipher\n}\n\ntype StreamConnCipher interface {\n\tStreamConn(net.Conn) net.Conn\n}\n\ntype PacketConnCipher interface {\n\tPacketConn(N.EnhancePacketConn) N.EnhancePacketConn\n}\n\n// ErrCipherNotSupported occurs when a cipher is not supported (likely because of security concerns).\nvar ErrCipherNotSupported = errors.New(\"cipher not supported\")\n\nconst (\n\taeadAes128Gcm         = \"AEAD_AES_128_GCM\"\n\taeadAes192Gcm         = \"AEAD_AES_192_GCM\"\n\taeadAes256Gcm         = \"AEAD_AES_256_GCM\"\n\taeadChacha20Poly1305  = \"AEAD_CHACHA20_POLY1305\"\n\taeadXChacha20Poly1305 = \"AEAD_XCHACHA20_POLY1305\"\n\taeadChacha8Poly1305   = \"AEAD_CHACHA8_POLY1305\"\n\taeadXChacha8Poly1305  = \"AEAD_XCHACHA8_POLY1305\"\n\taeadAes128Ccm         = \"AEAD_AES_128_CCM\"\n\taeadAes192Ccm         = \"AEAD_AES_192_CCM\"\n\taeadAes256Ccm         = \"AEAD_AES_256_CCM\"\n)\n\n// List of AEAD ciphers: key size in bytes and constructor\nvar aeadList = map[string]struct {\n\tKeySize int\n\tNew     func([]byte) (shadowaead.Cipher, error)\n}{\n\taeadAes128Gcm:         {16, shadowaead.AESGCM},\n\taeadAes192Gcm:         {24, shadowaead.AESGCM},\n\taeadAes256Gcm:         {32, shadowaead.AESGCM},\n\taeadChacha20Poly1305:  {32, shadowaead.Chacha20Poly1305},\n\taeadXChacha20Poly1305: {32, shadowaead.XChacha20Poly1305},\n\taeadChacha8Poly1305:   {32, shadowaead.Chacha8Poly1305},\n\taeadXChacha8Poly1305:  {32, shadowaead.XChacha8Poly1305},\n\taeadAes128Ccm:         {16, shadowaead.AESCCM},\n\taeadAes192Ccm:         {24, shadowaead.AESCCM},\n\taeadAes256Ccm:         {32, shadowaead.AESCCM},\n}\n\n// List of stream ciphers: key size in bytes and constructor\nvar streamList = map[string]struct {\n\tKeySize int\n\tNew     func(key []byte) (shadowstream.Cipher, error)\n}{\n\t\"RC4-MD5\":       {16, shadowstream.RC4MD5},\n\t\"AES-128-CTR\":   {16, shadowstream.AESCTR},\n\t\"AES-192-CTR\":   {24, shadowstream.AESCTR},\n\t\"AES-256-CTR\":   {32, shadowstream.AESCTR},\n\t\"AES-128-CFB\":   {16, shadowstream.AESCFB},\n\t\"AES-192-CFB\":   {24, shadowstream.AESCFB},\n\t\"AES-256-CFB\":   {32, shadowstream.AESCFB},\n\t\"CHACHA20\":      {32, shadowstream.ChaCha20},\n\t\"CHACHA20-IETF\": {32, shadowstream.Chacha20IETF},\n\t\"XCHACHA20\":     {32, shadowstream.Xchacha20},\n}\n\n// ListCipher returns a list of available cipher names sorted alphabetically.\nfunc ListCipher() []string {\n\tvar l []string\n\tfor k := range aeadList {\n\t\tl = append(l, k)\n\t}\n\tfor k := range streamList {\n\t\tl = append(l, k)\n\t}\n\tsort.Strings(l)\n\treturn l\n}\n\n// PickCipher returns a Cipher of the given name. Derive key from password if given key is empty.\nfunc PickCipher(name string, key []byte, password string) (Cipher, error) {\n\tname = strings.ToUpper(name)\n\n\tswitch name {\n\tcase \"DUMMY\":\n\t\treturn &dummy{}, nil\n\tcase \"CHACHA20-IETF-POLY1305\":\n\t\tname = aeadChacha20Poly1305\n\tcase \"XCHACHA20-IETF-POLY1305\":\n\t\tname = aeadXChacha20Poly1305\n\tcase \"AES-128-GCM\":\n\t\tname = aeadAes128Gcm\n\tcase \"AES-192-GCM\":\n\t\tname = aeadAes192Gcm\n\tcase \"AES-256-GCM\":\n\t\tname = aeadAes256Gcm\n\tcase \"CHACHA8-IETF-POLY1305\":\n\t\tname = aeadChacha8Poly1305\n\tcase \"XCHACHA8-IETF-POLY1305\":\n\t\tname = aeadXChacha8Poly1305\n\tcase \"AES-128-CCM\":\n\t\tname = aeadAes128Ccm\n\tcase \"AES-192-CCM\":\n\t\tname = aeadAes192Ccm\n\tcase \"AES-256-CCM\":\n\t\tname = aeadAes256Ccm\n\t}\n\n\tif choice, ok := aeadList[name]; ok {\n\t\tif len(key) == 0 {\n\t\t\tkey = Kdf(password, choice.KeySize)\n\t\t}\n\t\tif len(key) != choice.KeySize {\n\t\t\treturn nil, shadowaead.KeySizeError(choice.KeySize)\n\t\t}\n\t\taead, err := choice.New(key)\n\t\treturn &AeadCipher{Cipher: aead, Key: key}, err\n\t}\n\n\tif choice, ok := streamList[name]; ok {\n\t\tif len(key) == 0 {\n\t\t\tkey = Kdf(password, choice.KeySize)\n\t\t}\n\t\tif len(key) != choice.KeySize {\n\t\t\treturn nil, shadowstream.KeySizeError(choice.KeySize)\n\t\t}\n\t\tciph, err := choice.New(key)\n\t\treturn &StreamCipher{Cipher: ciph, Key: key}, err\n\t}\n\n\treturn nil, ErrCipherNotSupported\n}\n\ntype AeadCipher struct {\n\tshadowaead.Cipher\n\n\tKey []byte\n}\n\nfunc (aead *AeadCipher) StreamConn(c net.Conn) net.Conn { return shadowaead.NewConn(c, aead) }\nfunc (aead *AeadCipher) PacketConn(c N.EnhancePacketConn) N.EnhancePacketConn {\n\treturn shadowaead.NewPacketConn(c, aead)\n}\n\ntype StreamCipher struct {\n\tshadowstream.Cipher\n\n\tKey []byte\n}\n\nfunc (ciph *StreamCipher) StreamConn(c net.Conn) net.Conn { return shadowstream.NewConn(c, ciph) }\nfunc (ciph *StreamCipher) PacketConn(c N.EnhancePacketConn) N.EnhancePacketConn {\n\treturn shadowstream.NewPacketConn(c, ciph)\n}\n\n// dummy cipher does not encrypt\n\ntype dummy struct{}\n\nfunc (dummy) StreamConn(c net.Conn) net.Conn                       { return c }\nfunc (dummy) PacketConn(c N.EnhancePacketConn) N.EnhancePacketConn { return c }\n\n// key-derivation function from original Shadowsocks\nfunc Kdf(password string, keyLen int) []byte {\n\tvar b, prev []byte\n\th := md5.New()\n\tfor len(b) < keyLen {\n\t\th.Write(prev)\n\t\th.Write([]byte(password))\n\t\tb = h.Sum(b)\n\t\tprev = b[len(b)-h.Size():]\n\t\th.Reset()\n\t}\n\treturn b[:keyLen]\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/shadowsocks/shadowaead/cipher.go",
    "content": "package shadowaead\n\nimport (\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\t\"crypto/sha1\"\n\t\"io\"\n\t\"strconv\"\n\n\t\"github.com/metacubex/chacha\"\n\t\"gitlab.com/go-extension/aes-ccm\"\n\t\"golang.org/x/crypto/chacha20poly1305\"\n\t\"golang.org/x/crypto/hkdf\"\n)\n\ntype Cipher interface {\n\tKeySize() int\n\tSaltSize() int\n\tEncrypter(salt []byte) (cipher.AEAD, error)\n\tDecrypter(salt []byte) (cipher.AEAD, error)\n}\n\ntype KeySizeError int\n\nfunc (e KeySizeError) Error() string {\n\treturn \"key size error: need \" + strconv.Itoa(int(e)) + \" bytes\"\n}\n\nfunc hkdfSHA1(secret, salt, info, outkey []byte) {\n\tr := hkdf.New(sha1.New, secret, salt, info)\n\tif _, err := io.ReadFull(r, outkey); err != nil {\n\t\tpanic(err) // should never happen\n\t}\n}\n\ntype metaCipher struct {\n\tpsk      []byte\n\tmakeAEAD func(key []byte) (cipher.AEAD, error)\n}\n\nfunc (a *metaCipher) KeySize() int { return len(a.psk) }\nfunc (a *metaCipher) SaltSize() int {\n\tif ks := a.KeySize(); ks > 16 {\n\t\treturn ks\n\t}\n\treturn 16\n}\n\nfunc (a *metaCipher) Encrypter(salt []byte) (cipher.AEAD, error) {\n\tsubkey := make([]byte, a.KeySize())\n\thkdfSHA1(a.psk, salt, []byte(\"ss-subkey\"), subkey)\n\treturn a.makeAEAD(subkey)\n}\n\nfunc (a *metaCipher) Decrypter(salt []byte) (cipher.AEAD, error) {\n\tsubkey := make([]byte, a.KeySize())\n\thkdfSHA1(a.psk, salt, []byte(\"ss-subkey\"), subkey)\n\treturn a.makeAEAD(subkey)\n}\n\nfunc aesGCM(key []byte) (cipher.AEAD, error) {\n\tblk, err := aes.NewCipher(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn cipher.NewGCM(blk)\n}\n\n// AESGCM creates a new Cipher with a pre-shared key. len(psk) must be\n// one of 16, 24, or 32 to select AES-128/196/256-GCM.\nfunc AESGCM(psk []byte) (Cipher, error) {\n\tswitch l := len(psk); l {\n\tcase 16, 24, 32: // AES 128/196/256\n\tdefault:\n\t\treturn nil, aes.KeySizeError(l)\n\t}\n\treturn &metaCipher{psk: psk, makeAEAD: aesGCM}, nil\n}\n\nfunc aesCCM(key []byte) (cipher.AEAD, error) {\n\tblk, err := aes.NewCipher(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn ccm.NewCCM(blk)\n}\n\n// AESCCM creates a new Cipher with a pre-shared key. len(psk) must be\n// one of 16, 24, or 32 to select AES-128/196/256-GCM.\nfunc AESCCM(psk []byte) (Cipher, error) {\n\tswitch l := len(psk); l {\n\tcase 16, 24, 32: // AES 128/196/256\n\tdefault:\n\t\treturn nil, aes.KeySizeError(l)\n\t}\n\treturn &metaCipher{psk: psk, makeAEAD: aesCCM}, nil\n}\n\n// Chacha20Poly1305 creates a new Cipher with a pre-shared key. len(psk)\n// must be 32.\nfunc Chacha20Poly1305(psk []byte) (Cipher, error) {\n\tif len(psk) != chacha20poly1305.KeySize {\n\t\treturn nil, KeySizeError(chacha20poly1305.KeySize)\n\t}\n\treturn &metaCipher{psk: psk, makeAEAD: chacha20poly1305.New}, nil\n}\n\n// XChacha20Poly1305 creates a new Cipher with a pre-shared key. len(psk)\n// must be 32.\nfunc XChacha20Poly1305(psk []byte) (Cipher, error) {\n\tif len(psk) != chacha20poly1305.KeySize {\n\t\treturn nil, KeySizeError(chacha20poly1305.KeySize)\n\t}\n\treturn &metaCipher{psk: psk, makeAEAD: chacha20poly1305.NewX}, nil\n}\n\n// Chacha8Poly1305 creates a new Cipher with a pre-shared key. len(psk)\n// must be 32.\nfunc Chacha8Poly1305(psk []byte) (Cipher, error) {\n\tif len(psk) != chacha.KeySize {\n\t\treturn nil, KeySizeError(chacha.KeySize)\n\t}\n\treturn &metaCipher{psk: psk, makeAEAD: chacha.NewChaCha8IETFPoly1305}, nil\n}\n\n// XChacha8Poly1305 creates a new Cipher with a pre-shared key. len(psk)\n// must be 32.\nfunc XChacha8Poly1305(psk []byte) (Cipher, error) {\n\tif len(psk) != chacha.KeySize {\n\t\treturn nil, KeySizeError(chacha.KeySize)\n\t}\n\treturn &metaCipher{psk: psk, makeAEAD: chacha.NewXChaCha20IETFPoly1305}, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/shadowsocks/shadowaead/packet.go",
    "content": "package shadowaead\n\nimport (\n\t\"crypto/rand\"\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/pool\"\n)\n\n// ErrShortPacket means that the packet is too short for a valid encrypted packet.\nvar ErrShortPacket = errors.New(\"short packet\")\n\nvar _zerononce [128]byte // read-only. 128 bytes is more than enough.\n\n// Pack encrypts plaintext using Cipher with a randomly generated salt and\n// returns a slice of dst containing the encrypted packet and any error occurred.\n// Ensure len(dst) >= ciph.SaltSize() + len(plaintext) + aead.Overhead().\nfunc Pack(dst, plaintext []byte, ciph Cipher) ([]byte, error) {\n\tsaltSize := ciph.SaltSize()\n\tsalt := dst[:saltSize]\n\tif _, err := rand.Read(salt); err != nil {\n\t\treturn nil, err\n\t}\n\taead, err := ciph.Encrypter(salt)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(dst) < saltSize+len(plaintext)+aead.Overhead() {\n\t\treturn nil, io.ErrShortBuffer\n\t}\n\tb := aead.Seal(dst[saltSize:saltSize], _zerononce[:aead.NonceSize()], plaintext, nil)\n\treturn dst[:saltSize+len(b)], nil\n}\n\n// Unpack decrypts pkt using Cipher and returns a slice of dst containing the decrypted payload and any error occurred.\n// Ensure len(dst) >= len(pkt) - aead.SaltSize() - aead.Overhead().\nfunc Unpack(dst, pkt []byte, ciph Cipher) ([]byte, error) {\n\tsaltSize := ciph.SaltSize()\n\tif len(pkt) < saltSize {\n\t\treturn nil, ErrShortPacket\n\t}\n\tsalt := pkt[:saltSize]\n\taead, err := ciph.Decrypter(salt)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif len(pkt) < saltSize+aead.Overhead() {\n\t\treturn nil, ErrShortPacket\n\t}\n\tif saltSize+len(dst)+aead.Overhead() < len(pkt) {\n\t\treturn nil, io.ErrShortBuffer\n\t}\n\tb, err := aead.Open(dst[:0], _zerononce[:aead.NonceSize()], pkt[saltSize:], nil)\n\treturn b, err\n}\n\ntype PacketConn struct {\n\tN.EnhancePacketConn\n\tCipher\n}\n\nconst maxPacketSize = 64 * 1024\n\n// NewPacketConn wraps an N.EnhancePacketConn with cipher\nfunc NewPacketConn(c N.EnhancePacketConn, ciph Cipher) *PacketConn {\n\treturn &PacketConn{EnhancePacketConn: c, Cipher: ciph}\n}\n\n// WriteTo encrypts b and write to addr using the embedded PacketConn.\nfunc (c *PacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {\n\tbuf := pool.Get(maxPacketSize)\n\tdefer pool.Put(buf)\n\tbuf, err := Pack(buf, b, c)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\t_, err = c.EnhancePacketConn.WriteTo(buf, addr)\n\treturn len(b), err\n}\n\n// ReadFrom reads from the embedded PacketConn and decrypts into b.\nfunc (c *PacketConn) ReadFrom(b []byte) (int, net.Addr, error) {\n\tn, addr, err := c.EnhancePacketConn.ReadFrom(b)\n\tif err != nil {\n\t\treturn n, addr, err\n\t}\n\tbb, err := Unpack(b[c.Cipher.SaltSize():], b[:n], c)\n\tif err != nil {\n\t\treturn n, addr, err\n\t}\n\tcopy(b, bb)\n\treturn len(bb), addr, err\n}\n\nfunc (c *PacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {\n\tdata, put, addr, err = c.EnhancePacketConn.WaitReadFrom()\n\tif err != nil {\n\t\treturn\n\t}\n\tdata, err = Unpack(data[c.Cipher.SaltSize():], data, c)\n\tif err != nil {\n\t\tif put != nil {\n\t\t\tput()\n\t\t}\n\t\tdata = nil\n\t\tput = nil\n\t\treturn\n\t}\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/shadowsocks/shadowaead/stream.go",
    "content": "package shadowaead\n\nimport (\n\t\"crypto/cipher\"\n\t\"crypto/rand\"\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/common/pool\"\n)\n\nconst (\n\t// payloadSizeMask is the maximum size of payload in bytes.\n\tpayloadSizeMask = 0x3FFF    // 16*1024 - 1\n\tbufSize         = 17 * 1024 // >= 2+aead.Overhead()+payloadSizeMask+aead.Overhead()\n)\n\nvar ErrZeroChunk = errors.New(\"zero chunk\")\n\ntype Writer struct {\n\tio.Writer\n\tcipher.AEAD\n\tnonce [32]byte // should be sufficient for most nonce sizes\n}\n\n// NewWriter wraps an io.Writer with authenticated encryption.\nfunc NewWriter(w io.Writer, aead cipher.AEAD) *Writer { return &Writer{Writer: w, AEAD: aead} }\n\n// Write encrypts p and writes to the embedded io.Writer.\nfunc (w *Writer) Write(p []byte) (n int, err error) {\n\tbuf := pool.Get(bufSize)\n\tdefer pool.Put(buf)\n\tnonce := w.nonce[:w.NonceSize()]\n\ttag := w.Overhead()\n\toff := 2 + tag\n\n\t// compatible with snell\n\tif len(p) == 0 {\n\t\tbuf = buf[:off]\n\t\tbuf[0], buf[1] = byte(0), byte(0)\n\t\tw.Seal(buf[:0], nonce, buf[:2], nil)\n\t\tincrement(nonce)\n\t\t_, err = w.Writer.Write(buf)\n\t\treturn\n\t}\n\n\tfor nr := 0; n < len(p) && err == nil; n += nr {\n\t\tnr = payloadSizeMask\n\t\tif n+nr > len(p) {\n\t\t\tnr = len(p) - n\n\t\t}\n\t\tbuf = buf[:off+nr+tag]\n\t\tbuf[0], buf[1] = byte(nr>>8), byte(nr) // big-endian payload size\n\t\tw.Seal(buf[:0], nonce, buf[:2], nil)\n\t\tincrement(nonce)\n\t\tw.Seal(buf[:off], nonce, p[n:n+nr], nil)\n\t\tincrement(nonce)\n\t\t_, err = w.Writer.Write(buf)\n\t}\n\treturn\n}\n\n// ReadFrom reads from the given io.Reader until EOF or error, encrypts and\n// writes to the embedded io.Writer. Returns number of bytes read from r and\n// any error encountered.\nfunc (w *Writer) ReadFrom(r io.Reader) (n int64, err error) {\n\tbuf := pool.Get(bufSize)\n\tdefer pool.Put(buf)\n\tnonce := w.nonce[:w.NonceSize()]\n\ttag := w.Overhead()\n\toff := 2 + tag\n\tfor {\n\t\tnr, er := r.Read(buf[off : off+payloadSizeMask])\n\t\tn += int64(nr)\n\t\tbuf[0], buf[1] = byte(nr>>8), byte(nr)\n\t\tw.Seal(buf[:0], nonce, buf[:2], nil)\n\t\tincrement(nonce)\n\t\tw.Seal(buf[:off], nonce, buf[off:off+nr], nil)\n\t\tincrement(nonce)\n\t\tif _, ew := w.Writer.Write(buf[:off+nr+tag]); ew != nil {\n\t\t\terr = ew\n\t\t\treturn\n\t\t}\n\t\tif er != nil {\n\t\t\tif er != io.EOF { // ignore EOF as per io.ReaderFrom contract\n\t\t\t\terr = er\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}\n}\n\ntype Reader struct {\n\tio.Reader\n\tcipher.AEAD\n\tnonce [32]byte // should be sufficient for most nonce sizes\n\tbuf   []byte   // to be put back into bufPool\n\toff   int      // offset to unconsumed part of buf\n}\n\n// NewReader wraps an io.Reader with authenticated decryption.\nfunc NewReader(r io.Reader, aead cipher.AEAD) *Reader { return &Reader{Reader: r, AEAD: aead} }\n\n// Read and decrypt a record into p. len(p) >= max payload size + AEAD overhead.\nfunc (r *Reader) read(p []byte) (int, error) {\n\tnonce := r.nonce[:r.NonceSize()]\n\ttag := r.Overhead()\n\n\t// decrypt payload size\n\tp = p[:2+tag]\n\tif _, err := io.ReadFull(r.Reader, p); err != nil {\n\t\treturn 0, err\n\t}\n\t_, err := r.Open(p[:0], nonce, p, nil)\n\tincrement(nonce)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\t// decrypt payload\n\tsize := (int(p[0])<<8 + int(p[1])) & payloadSizeMask\n\tif size == 0 {\n\t\treturn 0, ErrZeroChunk\n\t}\n\n\tp = p[:size+tag]\n\tif _, err := io.ReadFull(r.Reader, p); err != nil {\n\t\treturn 0, err\n\t}\n\t_, err = r.Open(p[:0], nonce, p, nil)\n\tincrement(nonce)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn size, nil\n}\n\n// Read reads from the embedded io.Reader, decrypts and writes to p.\nfunc (r *Reader) Read(p []byte) (int, error) {\n\tif r.buf == nil {\n\t\tif len(p) >= payloadSizeMask+r.Overhead() {\n\t\t\treturn r.read(p)\n\t\t}\n\t\tb := pool.Get(bufSize)\n\t\tn, err := r.read(b)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tr.buf = b[:n]\n\t\tr.off = 0\n\t}\n\n\tn := copy(p, r.buf[r.off:])\n\tr.off += n\n\tif r.off == len(r.buf) {\n\t\tpool.Put(r.buf[:cap(r.buf)])\n\t\tr.buf = nil\n\t}\n\treturn n, nil\n}\n\n// WriteTo reads from the embedded io.Reader, decrypts and writes to w until\n// there's no more data to write or when an error occurs. Return number of\n// bytes written to w and any error encountered.\nfunc (r *Reader) WriteTo(w io.Writer) (n int64, err error) {\n\tif r.buf == nil {\n\t\tr.buf = pool.Get(bufSize)\n\t\tr.off = len(r.buf)\n\t}\n\n\tfor {\n\t\tfor r.off < len(r.buf) {\n\t\t\tnw, ew := w.Write(r.buf[r.off:])\n\t\t\tr.off += nw\n\t\t\tn += int64(nw)\n\t\t\tif ew != nil {\n\t\t\t\tif r.off == len(r.buf) {\n\t\t\t\t\tpool.Put(r.buf[:cap(r.buf)])\n\t\t\t\t\tr.buf = nil\n\t\t\t\t}\n\t\t\t\terr = ew\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tnr, er := r.read(r.buf)\n\t\tif er != nil {\n\t\t\tif er != io.EOF {\n\t\t\t\terr = er\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t\tr.buf = r.buf[:nr]\n\t\tr.off = 0\n\t}\n}\n\n// increment little-endian encoded unsigned integer b. Wrap around on overflow.\nfunc increment(b []byte) {\n\tfor i := range b {\n\t\tb[i]++\n\t\tif b[i] != 0 {\n\t\t\treturn\n\t\t}\n\t}\n}\n\ntype Conn struct {\n\tnet.Conn\n\tCipher\n\tr *Reader\n\tw *Writer\n}\n\n// NewConn wraps a stream-oriented net.Conn with cipher.\nfunc NewConn(c net.Conn, ciph Cipher) *Conn { return &Conn{Conn: c, Cipher: ciph} }\n\nfunc (c *Conn) initReader() error {\n\tsalt := make([]byte, c.SaltSize())\n\tif _, err := io.ReadFull(c.Conn, salt); err != nil {\n\t\treturn err\n\t}\n\n\taead, err := c.Decrypter(salt)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tc.r = NewReader(c.Conn, aead)\n\treturn nil\n}\n\nfunc (c *Conn) Read(b []byte) (int, error) {\n\tif c.r == nil {\n\t\tif err := c.initReader(); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\treturn c.r.Read(b)\n}\n\nfunc (c *Conn) WriteTo(w io.Writer) (int64, error) {\n\tif c.r == nil {\n\t\tif err := c.initReader(); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\treturn c.r.WriteTo(w)\n}\n\nfunc (c *Conn) initWriter() error {\n\tsalt := make([]byte, c.SaltSize())\n\tif _, err := rand.Read(salt); err != nil {\n\t\treturn err\n\t}\n\taead, err := c.Encrypter(salt)\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = c.Conn.Write(salt)\n\tif err != nil {\n\t\treturn err\n\t}\n\tc.w = NewWriter(c.Conn, aead)\n\treturn nil\n}\n\nfunc (c *Conn) Write(b []byte) (int, error) {\n\tif c.w == nil {\n\t\tif err := c.initWriter(); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\treturn c.w.Write(b)\n}\n\nfunc (c *Conn) ReadFrom(r io.Reader) (int64, error) {\n\tif c.w == nil {\n\t\tif err := c.initWriter(); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\treturn c.w.ReadFrom(r)\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/shadowsocks/shadowstream/chacha20.go",
    "content": "package shadowstream\n\nimport (\n\t\"crypto/cipher\"\n\n\t\"github.com/metacubex/chacha\"\n)\n\nfunc newChaCha20(nonce, key []byte) cipher.Stream {\n\tc, err := chacha.NewChaCha20IgnoreCounterOverflow(nonce, key)\n\tif err != nil {\n\t\tpanic(err) // should never happen\n\t}\n\treturn c\n}\n\ntype chacha20key []byte\n\nfunc (k chacha20key) IVSize() int                       { return chacha.NonceSize }\nfunc (k chacha20key) Encrypter(iv []byte) cipher.Stream { return newChaCha20(iv, k) }\nfunc (k chacha20key) Decrypter(iv []byte) cipher.Stream { return k.Encrypter(iv) }\n\nfunc ChaCha20(key []byte) (Cipher, error) {\n\tif len(key) != chacha.KeySize {\n\t\treturn nil, KeySizeError(chacha.KeySize)\n\t}\n\treturn chacha20key(key), nil\n}\n\n// IETF-variant of chacha20\ntype chacha20ietfkey []byte\n\nfunc (k chacha20ietfkey) IVSize() int                       { return chacha.INonceSize }\nfunc (k chacha20ietfkey) Decrypter(iv []byte) cipher.Stream { return k.Encrypter(iv) }\nfunc (k chacha20ietfkey) Encrypter(iv []byte) cipher.Stream { return newChaCha20(iv, k) }\n\nfunc Chacha20IETF(key []byte) (Cipher, error) {\n\tif len(key) != chacha.KeySize {\n\t\treturn nil, KeySizeError(chacha.KeySize)\n\t}\n\treturn chacha20ietfkey(key), nil\n}\n\ntype xchacha20key []byte\n\nfunc (k xchacha20key) IVSize() int                       { return chacha.XNonceSize }\nfunc (k xchacha20key) Decrypter(iv []byte) cipher.Stream { return k.Encrypter(iv) }\nfunc (k xchacha20key) Encrypter(iv []byte) cipher.Stream { return newChaCha20(iv, k) }\n\nfunc Xchacha20(key []byte) (Cipher, error) {\n\tif len(key) != chacha.KeySize {\n\t\treturn nil, KeySizeError(chacha.KeySize)\n\t}\n\treturn xchacha20key(key), nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/shadowsocks/shadowstream/cipher.go",
    "content": "package shadowstream\n\nimport (\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\t\"crypto/md5\"\n\t\"crypto/rc4\"\n\t\"strconv\"\n)\n\n// Cipher generates a pair of stream ciphers for encryption and decryption.\ntype Cipher interface {\n\tIVSize() int\n\tEncrypter(iv []byte) cipher.Stream\n\tDecrypter(iv []byte) cipher.Stream\n}\n\ntype KeySizeError int\n\nfunc (e KeySizeError) Error() string {\n\treturn \"key size error: need \" + strconv.Itoa(int(e)) + \" bytes\"\n}\n\n// CTR mode\ntype ctrStream struct{ cipher.Block }\n\nfunc (b *ctrStream) IVSize() int                       { return b.BlockSize() }\nfunc (b *ctrStream) Decrypter(iv []byte) cipher.Stream { return b.Encrypter(iv) }\nfunc (b *ctrStream) Encrypter(iv []byte) cipher.Stream { return cipher.NewCTR(b, iv) }\n\nfunc AESCTR(key []byte) (Cipher, error) {\n\tblk, err := aes.NewCipher(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &ctrStream{blk}, nil\n}\n\n// CFB mode\ntype cfbStream struct{ cipher.Block }\n\nfunc (b *cfbStream) IVSize() int                       { return b.BlockSize() }\nfunc (b *cfbStream) Decrypter(iv []byte) cipher.Stream { return cipher.NewCFBDecrypter(b, iv) }\nfunc (b *cfbStream) Encrypter(iv []byte) cipher.Stream { return cipher.NewCFBEncrypter(b, iv) }\n\nfunc AESCFB(key []byte) (Cipher, error) {\n\tblk, err := aes.NewCipher(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &cfbStream{blk}, nil\n}\n\ntype rc4Md5Key []byte\n\nfunc (k rc4Md5Key) IVSize() int {\n\treturn 16\n}\n\nfunc (k rc4Md5Key) Encrypter(iv []byte) cipher.Stream {\n\th := md5.New()\n\th.Write([]byte(k))\n\th.Write(iv)\n\trc4key := h.Sum(nil)\n\tc, _ := rc4.NewCipher(rc4key)\n\treturn c\n}\n\nfunc (k rc4Md5Key) Decrypter(iv []byte) cipher.Stream {\n\treturn k.Encrypter(iv)\n}\n\nfunc RC4MD5(key []byte) (Cipher, error) {\n\treturn rc4Md5Key(key), nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/shadowsocks/shadowstream/packet.go",
    "content": "package shadowstream\n\nimport (\n\t\"crypto/rand\"\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/pool\"\n)\n\n// ErrShortPacket means the packet is too short to be a valid encrypted packet.\nvar ErrShortPacket = errors.New(\"short packet\")\n\n// Pack encrypts plaintext using stream cipher s and a random IV.\n// Returns a slice of dst containing random IV and ciphertext.\n// Ensure len(dst) >= s.IVSize() + len(plaintext).\nfunc Pack(dst, plaintext []byte, s Cipher) ([]byte, error) {\n\tif len(dst) < s.IVSize()+len(plaintext) {\n\t\treturn nil, io.ErrShortBuffer\n\t}\n\tiv := dst[:s.IVSize()]\n\t_, err := rand.Read(iv)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\ts.Encrypter(iv).XORKeyStream(dst[len(iv):], plaintext)\n\treturn dst[:len(iv)+len(plaintext)], nil\n}\n\n// UnpackInplace decrypts pkt using stream cipher s.\n// Returns a slice of pkt containing decrypted plaintext.\n// Note: The data in the input dst will be changed\nfunc UnpackInplace(pkt []byte, s Cipher) ([]byte, error) {\n\tif len(pkt) < s.IVSize() {\n\t\treturn nil, ErrShortPacket\n\t}\n\tiv, dst := pkt[:s.IVSize()], pkt[s.IVSize():]\n\ts.Decrypter(iv).XORKeyStream(dst, dst)\n\treturn dst, nil\n}\n\n// Unpack decrypts pkt using stream cipher s.\n// Returns a slice of dst containing decrypted plaintext.\nfunc Unpack(dst, pkt []byte, s Cipher) ([]byte, error) {\n\tif len(pkt) < s.IVSize() {\n\t\treturn nil, ErrShortPacket\n\t}\n\tif len(dst) < len(pkt)-s.IVSize() {\n\t\treturn nil, io.ErrShortBuffer\n\t}\n\tiv := pkt[:s.IVSize()]\n\ts.Decrypter(iv).XORKeyStream(dst, pkt[len(iv):])\n\treturn dst[:len(pkt)-len(iv)], nil\n}\n\ntype PacketConn struct {\n\tN.EnhancePacketConn\n\tCipher\n}\n\n// NewPacketConn wraps an N.EnhancePacketConn with stream cipher encryption/decryption.\nfunc NewPacketConn(c N.EnhancePacketConn, ciph Cipher) *PacketConn {\n\treturn &PacketConn{EnhancePacketConn: c, Cipher: ciph}\n}\n\nconst maxPacketSize = 64 * 1024\n\nfunc (c *PacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {\n\tbuf := pool.Get(maxPacketSize)\n\tdefer pool.Put(buf)\n\tbuf, err := Pack(buf, b, c.Cipher)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\t_, err = c.EnhancePacketConn.WriteTo(buf, addr)\n\treturn len(b), err\n}\n\nfunc (c *PacketConn) ReadFrom(b []byte) (int, net.Addr, error) {\n\tn, addr, err := c.EnhancePacketConn.ReadFrom(b)\n\tif err != nil {\n\t\treturn n, addr, err\n\t}\n\tbb, err := UnpackInplace(b[:n], c.Cipher)\n\tif err != nil {\n\t\treturn n, addr, err\n\t}\n\tcopy(b, bb)\n\treturn len(bb), addr, err\n}\n\nfunc (c *PacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {\n\tdata, put, addr, err = c.EnhancePacketConn.WaitReadFrom()\n\tif err != nil {\n\t\treturn\n\t}\n\tdata, err = UnpackInplace(data, c.Cipher)\n\tif err != nil {\n\t\tif put != nil {\n\t\t\tput()\n\t\t}\n\t\tdata = nil\n\t\tput = nil\n\t\treturn\n\t}\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/shadowsocks/shadowstream/stream.go",
    "content": "package shadowstream\n\nimport (\n\t\"crypto/cipher\"\n\t\"crypto/rand\"\n\t\"io\"\n\t\"net\"\n)\n\nconst bufSize = 2048\n\ntype Writer struct {\n\tio.Writer\n\tcipher.Stream\n\tbuf [bufSize]byte\n}\n\n// NewWriter wraps an io.Writer with stream cipher encryption.\nfunc NewWriter(w io.Writer, s cipher.Stream) *Writer { return &Writer{Writer: w, Stream: s} }\n\nfunc (w *Writer) Write(p []byte) (n int, err error) {\n\tbuf := w.buf[:]\n\tfor nw := 0; n < len(p) && err == nil; n += nw {\n\t\tend := n + len(buf)\n\t\tif end > len(p) {\n\t\t\tend = len(p)\n\t\t}\n\t\tw.XORKeyStream(buf, p[n:end])\n\t\tnw, err = w.Writer.Write(buf[:end-n])\n\t}\n\treturn\n}\n\nfunc (w *Writer) ReadFrom(r io.Reader) (n int64, err error) {\n\tbuf := w.buf[:]\n\tfor {\n\t\tnr, er := r.Read(buf)\n\t\tn += int64(nr)\n\t\tb := buf[:nr]\n\t\tw.XORKeyStream(b, b)\n\t\tif _, err = w.Writer.Write(b); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif er != nil {\n\t\t\tif er != io.EOF { // ignore EOF as per io.ReaderFrom contract\n\t\t\t\terr = er\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}\n}\n\ntype Reader struct {\n\tio.Reader\n\tcipher.Stream\n\tbuf [bufSize]byte\n}\n\n// NewReader wraps an io.Reader with stream cipher decryption.\nfunc NewReader(r io.Reader, s cipher.Stream) *Reader { return &Reader{Reader: r, Stream: s} }\n\nfunc (r *Reader) Read(p []byte) (n int, err error) {\n\tn, err = r.Reader.Read(p)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tr.XORKeyStream(p, p[:n])\n\treturn\n}\n\nfunc (r *Reader) WriteTo(w io.Writer) (n int64, err error) {\n\tbuf := r.buf[:]\n\tfor {\n\t\tnr, er := r.Reader.Read(buf)\n\t\tif nr > 0 {\n\t\t\tr.XORKeyStream(buf, buf[:nr])\n\t\t\tnw, ew := w.Write(buf[:nr])\n\t\t\tn += int64(nw)\n\t\t\tif ew != nil {\n\t\t\t\terr = ew\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tif er != nil {\n\t\t\tif er != io.EOF { // ignore EOF as per io.Copy contract (using src.WriteTo shortcut)\n\t\t\t\terr = er\n\t\t\t}\n\t\t\treturn\n\t\t}\n\t}\n}\n\n// A Conn represents a Shadowsocks connection. It implements the net.Conn interface.\ntype Conn struct {\n\tnet.Conn\n\tCipher\n\tr       *Reader\n\tw       *Writer\n\treadIV  []byte\n\twriteIV []byte\n}\n\n// NewConn wraps a stream-oriented net.Conn with stream cipher encryption/decryption.\nfunc NewConn(c net.Conn, ciph Cipher) *Conn { return &Conn{Conn: c, Cipher: ciph} }\n\nfunc (c *Conn) initReader() error {\n\tif c.r == nil {\n\t\tiv, err := c.ObtainReadIV()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tc.r = NewReader(c.Conn, c.Decrypter(iv))\n\t}\n\treturn nil\n}\n\nfunc (c *Conn) Read(b []byte) (int, error) {\n\tif c.r == nil {\n\t\tif err := c.initReader(); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\treturn c.r.Read(b)\n}\n\nfunc (c *Conn) WriteTo(w io.Writer) (int64, error) {\n\tif c.r == nil {\n\t\tif err := c.initReader(); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\treturn c.r.WriteTo(w)\n}\n\nfunc (c *Conn) initWriter() error {\n\tif c.w == nil {\n\t\tiv, err := c.ObtainWriteIV()\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif _, err := c.Conn.Write(iv); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tc.w = NewWriter(c.Conn, c.Encrypter(iv))\n\t}\n\treturn nil\n}\n\nfunc (c *Conn) Write(b []byte) (int, error) {\n\tif c.w == nil {\n\t\tif err := c.initWriter(); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\treturn c.w.Write(b)\n}\n\nfunc (c *Conn) ReadFrom(r io.Reader) (int64, error) {\n\tif c.w == nil {\n\t\tif err := c.initWriter(); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\treturn c.w.ReadFrom(r)\n}\n\nfunc (c *Conn) ObtainWriteIV() ([]byte, error) {\n\tif len(c.writeIV) == c.IVSize() {\n\t\treturn c.writeIV, nil\n\t}\n\n\tiv := make([]byte, c.IVSize())\n\n\tif _, err := rand.Read(iv); err != nil {\n\t\treturn nil, err\n\t}\n\n\tc.writeIV = iv\n\n\treturn iv, nil\n}\n\nfunc (c *Conn) ObtainReadIV() ([]byte, error) {\n\tif len(c.readIV) == c.IVSize() {\n\t\treturn c.readIV, nil\n\t}\n\n\tiv := make([]byte, c.IVSize())\n\n\tif _, err := io.ReadFull(c.Conn, iv); err != nil {\n\t\treturn nil, err\n\t}\n\n\tc.readIV = iv\n\n\treturn iv, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/shadowtls/shadowtls.go",
    "content": "package shadowtls\n\nimport (\n\t\"context\"\n\t\"crypto/hmac\"\n\t\"crypto/sha1\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"hash\"\n\t\"io\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/common/pool\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\n\t\"github.com/metacubex/tls\"\n)\n\nconst (\n\tchunkSize           = 1 << 13\n\tMode         string = \"shadow-tls\"\n\thashLen      int    = 8\n\ttlsHeaderLen int    = 5\n)\n\nvar (\n\tDefaultALPN = []string{\"h2\", \"http/1.1\"}\n)\n\n// ShadowTLS is shadow-tls implementation\ntype ShadowTLS struct {\n\tnet.Conn\n\tpassword     []byte\n\tremain       int\n\tfirstRequest bool\n\ttlsConfig    *tls.Config\n}\n\ntype HashedConn struct {\n\tnet.Conn\n\thasher hash.Hash\n}\n\nfunc newHashedStream(conn net.Conn, password []byte) HashedConn {\n\treturn HashedConn{\n\t\tConn:   conn,\n\t\thasher: hmac.New(sha1.New, password),\n\t}\n}\n\nfunc (h HashedConn) Read(b []byte) (n int, err error) {\n\tn, err = h.Conn.Read(b)\n\th.hasher.Write(b[:n])\n\treturn\n}\n\nfunc (s *ShadowTLS) read(b []byte) (int, error) {\n\tvar buf [tlsHeaderLen]byte\n\t_, err := io.ReadFull(s.Conn, buf[:])\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"shadowtls read failed %w\", err)\n\t}\n\tif buf[0] != 0x17 || buf[1] != 0x3 || buf[2] != 0x3 {\n\t\treturn 0, fmt.Errorf(\"invalid shadowtls header %v\", buf)\n\t}\n\tlength := int(binary.BigEndian.Uint16(buf[3:]))\n\n\tif length > len(b) {\n\t\tn, err := s.Conn.Read(b)\n\t\tif err != nil {\n\t\t\treturn n, err\n\t\t}\n\t\ts.remain = length - n\n\t\treturn n, nil\n\t}\n\n\treturn io.ReadFull(s.Conn, b[:length])\n}\n\nfunc (s *ShadowTLS) Read(b []byte) (int, error) {\n\tif s.remain > 0 {\n\t\tlength := s.remain\n\t\tif length > len(b) {\n\t\t\tlength = len(b)\n\t\t}\n\n\t\tn, err := io.ReadFull(s.Conn, b[:length])\n\t\tif err != nil {\n\t\t\treturn n, fmt.Errorf(\"shadowtls Read failed with %w\", err)\n\t\t}\n\t\ts.remain -= n\n\t\treturn n, nil\n\t}\n\n\treturn s.read(b)\n}\n\nfunc (s *ShadowTLS) Write(b []byte) (int, error) {\n\tlength := len(b)\n\tfor i := 0; i < length; i += chunkSize {\n\t\tend := i + chunkSize\n\t\tif end > length {\n\t\t\tend = length\n\t\t}\n\n\t\tn, err := s.write(b[i:end])\n\t\tif err != nil {\n\t\t\treturn n, fmt.Errorf(\"shadowtls Write failed with %w, i=%d, end=%d, n=%d\", err, i, end, n)\n\t\t}\n\t}\n\treturn length, nil\n}\n\nfunc (s *ShadowTLS) write(b []byte) (int, error) {\n\tvar hashVal []byte\n\tif s.firstRequest {\n\t\thashedConn := newHashedStream(s.Conn, s.password)\n\t\ttlsConn := tls.Client(hashedConn, s.tlsConfig)\n\t\t// fix tls handshake not timeout\n\t\tctx, cancel := context.WithTimeout(context.Background(), C.DefaultTLSTimeout)\n\t\tdefer cancel()\n\t\tif err := tlsConn.HandshakeContext(ctx); err != nil {\n\t\t\treturn 0, fmt.Errorf(\"tls connect failed with %w\", err)\n\t\t}\n\t\thashVal = hashedConn.hasher.Sum(nil)[:hashLen]\n\t\ts.firstRequest = false\n\t}\n\n\tbuf := pool.GetBuffer()\n\tdefer pool.PutBuffer(buf)\n\tbuf.Write([]byte{0x17, 0x03, 0x03})\n\tbinary.Write(buf, binary.BigEndian, uint16(len(b)+len(hashVal)))\n\tbuf.Write(hashVal)\n\tbuf.Write(b)\n\t_, err := s.Conn.Write(buf.Bytes())\n\tif err != nil {\n\t\t// return 0 because errors occur here make the\n\t\t// whole situation irrecoverable\n\t\treturn 0, err\n\t}\n\treturn len(b), nil\n}\n\n// NewShadowTLS return a ShadowTLS\nfunc NewShadowTLS(conn net.Conn, password string, tlsConfig *tls.Config) net.Conn {\n\treturn &ShadowTLS{\n\t\tConn:         conn,\n\t\tpassword:     []byte(password),\n\t\tfirstRequest: true,\n\t\ttlsConfig:    tlsConfig,\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/simple-obfs/http.go",
    "content": "package obfs\n\nimport (\n\t\"bytes\"\n\t\"crypto/rand\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/common/pool\"\n\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/randv2\"\n)\n\n// HTTPObfs is shadowsocks http simple-obfs implementation\ntype HTTPObfs struct {\n\tnet.Conn\n\thost          string\n\tport          string\n\tbuf           []byte\n\toffset        int\n\tfirstRequest  bool\n\tfirstResponse bool\n}\n\nfunc (ho *HTTPObfs) Read(b []byte) (int, error) {\n\tif ho.buf != nil {\n\t\tn := copy(b, ho.buf[ho.offset:])\n\t\tho.offset += n\n\t\tif ho.offset == len(ho.buf) {\n\t\t\tpool.Put(ho.buf)\n\t\t\tho.buf = nil\n\t\t}\n\t\treturn n, nil\n\t}\n\n\tif ho.firstResponse {\n\t\tbuf := pool.Get(pool.RelayBufferSize)\n\t\tn, err := ho.Conn.Read(buf)\n\t\tif err != nil {\n\t\t\tpool.Put(buf)\n\t\t\treturn 0, err\n\t\t}\n\t\tidx := bytes.Index(buf[:n], []byte(\"\\r\\n\\r\\n\"))\n\t\tif idx == -1 {\n\t\t\tpool.Put(buf)\n\t\t\treturn 0, io.EOF\n\t\t}\n\t\tho.firstResponse = false\n\t\tlength := n - (idx + 4)\n\t\tn = copy(b, buf[idx+4:n])\n\t\tif length > n {\n\t\t\tho.buf = buf[:idx+4+length]\n\t\t\tho.offset = idx + 4 + n\n\t\t} else {\n\t\t\tpool.Put(buf)\n\t\t}\n\t\treturn n, nil\n\t}\n\treturn ho.Conn.Read(b)\n}\n\nfunc (ho *HTTPObfs) Write(b []byte) (int, error) {\n\tif ho.firstRequest {\n\t\trandBytes := make([]byte, 16)\n\t\trand.Read(randBytes)\n\t\treq, err := http.NewRequest(\"GET\", fmt.Sprintf(\"http://%s/\", ho.host), bytes.NewBuffer(b[:]))\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\treq.Header.Set(\"User-Agent\", fmt.Sprintf(\"curl/7.%d.%d\", randv2.Int()%54, randv2.Int()%2))\n\t\treq.Header.Set(\"Upgrade\", \"websocket\")\n\t\treq.Header.Set(\"Connection\", \"Upgrade\")\n\t\treq.Host = ho.host\n\t\tif ho.port != \"80\" {\n\t\t\treq.Host = fmt.Sprintf(\"%s:%s\", ho.host, ho.port)\n\t\t}\n\t\treq.Header.Set(\"Sec-WebSocket-Key\", base64.URLEncoding.EncodeToString(randBytes))\n\t\treq.ContentLength = int64(len(b))\n\t\terr = req.Write(ho.Conn)\n\t\tho.firstRequest = false\n\t\treturn len(b), err\n\t}\n\n\treturn ho.Conn.Write(b)\n}\n\n// NewHTTPObfs return a HTTPObfs\nfunc NewHTTPObfs(conn net.Conn, host string, port string) net.Conn {\n\treturn &HTTPObfs{\n\t\tConn:          conn,\n\t\tfirstRequest:  true,\n\t\tfirstResponse: true,\n\t\thost:          host,\n\t\tport:          port,\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/simple-obfs/tls.go",
    "content": "package obfs\n\nimport (\n\t\"bytes\"\n\t\"crypto/rand\"\n\t\"encoding/binary\"\n\t\"io\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/common/pool\"\n\t\"github.com/metacubex/mihomo/ntp\"\n)\n\nconst (\n\tchunkSize = 1 << 14 // 2 ** 14 == 16 * 1024\n)\n\n// TLSObfs is shadowsocks tls simple-obfs implementation\ntype TLSObfs struct {\n\tnet.Conn\n\tserver        string\n\tremain        int\n\tfirstRequest  bool\n\tfirstResponse bool\n}\n\nfunc (to *TLSObfs) read(b []byte, discardN int) (int, error) {\n\tbuf := pool.Get(discardN)\n\t_, err := io.ReadFull(to.Conn, buf)\n\tpool.Put(buf)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tsizeBuf := make([]byte, 2)\n\t_, err = io.ReadFull(to.Conn, sizeBuf)\n\tif err != nil {\n\t\treturn 0, nil\n\t}\n\n\tlength := int(binary.BigEndian.Uint16(sizeBuf))\n\tif length > len(b) {\n\t\tn, err := to.Conn.Read(b)\n\t\tif err != nil {\n\t\t\treturn n, err\n\t\t}\n\t\tto.remain = length - n\n\t\treturn n, nil\n\t}\n\n\treturn io.ReadFull(to.Conn, b[:length])\n}\n\nfunc (to *TLSObfs) Read(b []byte) (int, error) {\n\tif to.remain > 0 {\n\t\tlength := to.remain\n\t\tif length > len(b) {\n\t\t\tlength = len(b)\n\t\t}\n\n\t\tn, err := io.ReadFull(to.Conn, b[:length])\n\t\tto.remain -= n\n\t\treturn n, err\n\t}\n\n\tif to.firstResponse {\n\t\t// type + ver + lensize + 91 = 96\n\t\t// type + ver + lensize + 1 = 6\n\t\t// type + ver = 3\n\t\tto.firstResponse = false\n\t\treturn to.read(b, 105)\n\t}\n\n\t// type + ver = 3\n\treturn to.read(b, 3)\n}\n\nfunc (to *TLSObfs) Write(b []byte) (int, error) {\n\tlength := len(b)\n\tfor i := 0; i < length; i += chunkSize {\n\t\tend := i + chunkSize\n\t\tif end > length {\n\t\t\tend = length\n\t\t}\n\n\t\tn, err := to.write(b[i:end])\n\t\tif err != nil {\n\t\t\treturn n, err\n\t\t}\n\t}\n\treturn length, nil\n}\n\nfunc (to *TLSObfs) write(b []byte) (int, error) {\n\tif to.firstRequest {\n\t\thelloMsg := makeClientHelloMsg(b, to.server)\n\t\t_, err := to.Conn.Write(helloMsg)\n\t\tto.firstRequest = false\n\t\treturn len(b), err\n\t}\n\n\tbuf := pool.GetBuffer()\n\tdefer pool.PutBuffer(buf)\n\tbuf.Write([]byte{0x17, 0x03, 0x03})\n\tbinary.Write(buf, binary.BigEndian, uint16(len(b)))\n\tbuf.Write(b)\n\t_, err := to.Conn.Write(buf.Bytes())\n\tif err != nil {\n\t\t// return 0 because errors occur here make the\n\t\t// whole situation irrecoverable\n\t\treturn 0, err\n\t}\n\treturn len(b), nil\n}\n\n// NewTLSObfs return a SimpleObfs\nfunc NewTLSObfs(conn net.Conn, server string) net.Conn {\n\treturn &TLSObfs{\n\t\tConn:          conn,\n\t\tserver:        server,\n\t\tfirstRequest:  true,\n\t\tfirstResponse: true,\n\t}\n}\n\nfunc makeClientHelloMsg(data []byte, server string) []byte {\n\trandom := make([]byte, 28)\n\tsessionID := make([]byte, 32)\n\trand.Read(random)\n\trand.Read(sessionID)\n\n\tbuf := &bytes.Buffer{}\n\n\t// handshake, TLS 1.0 version, length\n\tbuf.WriteByte(22)\n\tbuf.Write([]byte{0x03, 0x01})\n\tlength := uint16(212 + len(data) + len(server))\n\tbuf.WriteByte(byte(length >> 8))\n\tbuf.WriteByte(byte(length & 0xff))\n\n\t// clientHello, length, TLS 1.2 version\n\tbuf.WriteByte(1)\n\tbuf.WriteByte(0)\n\tbinary.Write(buf, binary.BigEndian, uint16(208+len(data)+len(server)))\n\tbuf.Write([]byte{0x03, 0x03})\n\n\t// random with timestamp, sid len, sid\n\tbinary.Write(buf, binary.BigEndian, uint32(ntp.Now().Unix()))\n\tbuf.Write(random)\n\tbuf.WriteByte(32)\n\tbuf.Write(sessionID)\n\n\t// cipher suites\n\tbuf.Write([]byte{0x00, 0x38})\n\tbuf.Write([]byte{\n\t\t0xc0, 0x2c, 0xc0, 0x30, 0x00, 0x9f, 0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0xaa, 0xc0, 0x2b, 0xc0, 0x2f,\n\t\t0x00, 0x9e, 0xc0, 0x24, 0xc0, 0x28, 0x00, 0x6b, 0xc0, 0x23, 0xc0, 0x27, 0x00, 0x67, 0xc0, 0x0a,\n\t\t0xc0, 0x14, 0x00, 0x39, 0xc0, 0x09, 0xc0, 0x13, 0x00, 0x33, 0x00, 0x9d, 0x00, 0x9c, 0x00, 0x3d,\n\t\t0x00, 0x3c, 0x00, 0x35, 0x00, 0x2f, 0x00, 0xff,\n\t})\n\n\t// compression\n\tbuf.Write([]byte{0x01, 0x00})\n\n\t// extension length\n\tbinary.Write(buf, binary.BigEndian, uint16(79+len(data)+len(server)))\n\n\t// session ticket\n\tbuf.Write([]byte{0x00, 0x23})\n\tbinary.Write(buf, binary.BigEndian, uint16(len(data)))\n\tbuf.Write(data)\n\n\t// server name\n\tbuf.Write([]byte{0x00, 0x00})\n\tbinary.Write(buf, binary.BigEndian, uint16(len(server)+5))\n\tbinary.Write(buf, binary.BigEndian, uint16(len(server)+3))\n\tbuf.WriteByte(0)\n\tbinary.Write(buf, binary.BigEndian, uint16(len(server)))\n\tbuf.Write([]byte(server))\n\n\t// ec_point\n\tbuf.Write([]byte{0x00, 0x0b, 0x00, 0x04, 0x03, 0x01, 0x00, 0x02})\n\n\t// groups\n\tbuf.Write([]byte{0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x19, 0x00, 0x18})\n\n\t// signature\n\tbuf.Write([]byte{\n\t\t0x00, 0x0d, 0x00, 0x20, 0x00, 0x1e, 0x06, 0x01, 0x06, 0x02, 0x06, 0x03, 0x05,\n\t\t0x01, 0x05, 0x02, 0x05, 0x03, 0x04, 0x01, 0x04, 0x02, 0x04, 0x03, 0x03, 0x01,\n\t\t0x03, 0x02, 0x03, 0x03, 0x02, 0x01, 0x02, 0x02, 0x02, 0x03,\n\t})\n\n\t// encrypt then mac\n\tbuf.Write([]byte{0x00, 0x16, 0x00, 0x00})\n\n\t// extended master secret\n\tbuf.Write([]byte{0x00, 0x17, 0x00, 0x00})\n\n\treturn buf.Bytes()\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sing-shadowtls/shadowtls.go",
    "content": "package sing_shadowtls\n\nimport (\n\t\"context\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/component/ca\"\n\ttlsC \"github.com/metacubex/mihomo/component/tls\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/metacubex/sing-shadowtls\"\n\t\"github.com/metacubex/tls\"\n\t\"golang.org/x/exp/slices\"\n)\n\nconst (\n\tMode string = \"shadow-tls\"\n)\n\nvar (\n\tDefaultALPN = []string{\"h2\", \"http/1.1\"}\n\tWsALPN      = []string{\"http/1.1\"}\n)\n\ntype ShadowTLSOption struct {\n\tPassword          string\n\tHost              string\n\tFingerprint       string\n\tCertificate       string\n\tPrivateKey        string\n\tClientFingerprint string\n\tSkipCertVerify    bool\n\tVersion           int\n\tALPN              []string\n}\n\nfunc NewShadowTLS(ctx context.Context, conn net.Conn, option *ShadowTLSOption) (net.Conn, error) {\n\ttlsConfig, err := ca.GetTLSConfig(ca.Option{\n\t\tTLSConfig: &tls.Config{\n\t\t\tNextProtos:         option.ALPN,\n\t\t\tMinVersion:         tls.VersionTLS12,\n\t\t\tInsecureSkipVerify: option.SkipCertVerify,\n\t\t\tServerName:         option.Host,\n\t\t},\n\t\tFingerprint: option.Fingerprint,\n\t\tCertificate: option.Certificate,\n\t\tPrivateKey:  option.PrivateKey,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif option.Version == 1 {\n\t\ttlsConfig.MaxVersion = tls.VersionTLS12 // ShadowTLS v1 only support TLS 1.2\n\t}\n\n\ttlsHandshake := uTLSHandshakeFunc(tlsConfig, option.ClientFingerprint, option.Version)\n\tclient, err := shadowtls.NewClient(shadowtls.ClientConfig{\n\t\tVersion:      option.Version,\n\t\tPassword:     option.Password,\n\t\tTLSHandshake: tlsHandshake,\n\t\tLogger:       log.SingLogger,\n\t})\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn client.DialContextConn(ctx, conn)\n}\n\nfunc uTLSHandshakeFunc(config *tls.Config, clientFingerprint string, version int) shadowtls.TLSHandshakeFunc {\n\treturn func(ctx context.Context, conn net.Conn, sessionIDGenerator shadowtls.TLSSessionIDGeneratorFunc) error {\n\t\ttlsConfig := tlsC.UConfig(config)\n\t\ttlsConfig.SessionIDGenerator = sessionIDGenerator\n\t\tif version == 1 {\n\t\t\ttlsConfig.MaxVersion = tlsC.VersionTLS12 // ShadowTLS v1 only support TLS 1.2\n\t\t\ttlsConn := tlsC.Client(conn, tlsConfig)\n\t\t\treturn tlsConn.HandshakeContext(ctx)\n\t\t}\n\t\tif clientFingerprint, ok := tlsC.GetFingerprint(clientFingerprint); ok {\n\t\t\ttlsConn := tlsC.UClient(conn, tlsConfig, clientFingerprint)\n\t\t\tif slices.Equal(tlsConfig.NextProtos, WsALPN) {\n\t\t\t\terr := tlsC.BuildWebsocketHandshakeState(tlsConn)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t\tif version == 2 { // ShadowTLS v2 not work with X25519MLKEM768\n\t\t\t\terr := tlsC.BuildRemovedX25519MLKEM768HandshakeState(tlsConn)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn tlsConn.HandshakeContext(ctx)\n\t\t}\n\t\ttlsConn := tlsC.Client(conn, tlsConfig)\n\t\treturn tlsConn.HandshakeContext(ctx)\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/snell/cipher.go",
    "content": "package snell\n\nimport (\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\n\t\"github.com/metacubex/mihomo/transport/shadowsocks/shadowaead\"\n\n\t\"golang.org/x/crypto/argon2\"\n\t\"golang.org/x/crypto/chacha20poly1305\"\n)\n\ntype snellCipher struct {\n\tpsk      []byte\n\tkeySize  int\n\tmakeAEAD func(key []byte) (cipher.AEAD, error)\n}\n\nfunc (sc *snellCipher) KeySize() int  { return sc.keySize }\nfunc (sc *snellCipher) SaltSize() int { return 16 }\nfunc (sc *snellCipher) Encrypter(salt []byte) (cipher.AEAD, error) {\n\treturn sc.makeAEAD(snellKDF(sc.psk, salt, sc.KeySize()))\n}\n\nfunc (sc *snellCipher) Decrypter(salt []byte) (cipher.AEAD, error) {\n\treturn sc.makeAEAD(snellKDF(sc.psk, salt, sc.KeySize()))\n}\n\nfunc snellKDF(psk, salt []byte, keySize int) []byte {\n\t// snell use a special kdf function\n\treturn argon2.IDKey(psk, salt, 3, 8, 1, 32)[:keySize]\n}\n\nfunc aesGCM(key []byte) (cipher.AEAD, error) {\n\tblk, err := aes.NewCipher(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn cipher.NewGCM(blk)\n}\n\nfunc NewAES128GCM(psk []byte) shadowaead.Cipher {\n\treturn &snellCipher{\n\t\tpsk:      psk,\n\t\tkeySize:  16,\n\t\tmakeAEAD: aesGCM,\n\t}\n}\n\nfunc NewChacha20Poly1305(psk []byte) shadowaead.Cipher {\n\treturn &snellCipher{\n\t\tpsk:      psk,\n\t\tkeySize:  32,\n\t\tmakeAEAD: chacha20poly1305.New,\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/snell/pool.go",
    "content": "package snell\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/component/pool\"\n\t\"github.com/metacubex/mihomo/transport/shadowsocks/shadowaead\"\n)\n\ntype Pool struct {\n\tpool *pool.Pool[*Snell]\n}\n\nfunc (p *Pool) Get() (net.Conn, error) {\n\treturn p.GetContext(context.Background())\n}\n\nfunc (p *Pool) GetContext(ctx context.Context) (net.Conn, error) {\n\telm, err := p.pool.GetContext(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &PoolConn{elm, p}, nil\n}\n\nfunc (p *Pool) Put(conn *Snell) {\n\tif err := HalfClose(conn); err != nil {\n\t\t_ = conn.Close()\n\t\treturn\n\t}\n\n\tp.pool.Put(conn)\n}\n\ntype PoolConn struct {\n\t*Snell\n\tpool *Pool\n}\n\nfunc (pc *PoolConn) Read(b []byte) (int, error) {\n\t// save old status of reply (it mutable by Read)\n\treply := pc.Snell.reply\n\n\tn, err := pc.Snell.Read(b)\n\tif err == shadowaead.ErrZeroChunk {\n\t\t// if reply is false, it should be client halfclose.\n\t\t// ignore error and read data again.\n\t\tif !reply {\n\t\t\tpc.Snell.reply = false\n\t\t\treturn pc.Snell.Read(b)\n\t\t}\n\t}\n\treturn n, err\n}\n\nfunc (pc *PoolConn) Write(b []byte) (int, error) {\n\treturn pc.Snell.Write(b)\n}\n\nfunc (pc *PoolConn) Close() error {\n\t// mihomo use SetReadDeadline to break bidirectional copy between client and server.\n\t// reset it before reuse connection to avoid io timeout error.\n\t_ = pc.Snell.Conn.SetReadDeadline(time.Time{})\n\tpc.pool.Put(pc.Snell)\n\treturn nil\n}\n\nfunc NewPool(factory func(context.Context) (*Snell, error)) *Pool {\n\tp := pool.New[*Snell](\n\t\tfunc(ctx context.Context) (*Snell, error) {\n\t\t\treturn factory(ctx)\n\t\t},\n\t\tpool.WithAge[*Snell](15000),\n\t\tpool.WithSize[*Snell](10),\n\t\tpool.WithEvict[*Snell](func(item *Snell) {\n\t\t\t_ = item.Close()\n\t\t}),\n\t)\n\n\treturn &Pool{pool: p}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/snell/snell.go",
    "content": "package snell\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"sync\"\n\n\t\"github.com/metacubex/mihomo/common/pool\"\n\t\"github.com/metacubex/mihomo/transport/shadowsocks/shadowaead\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n)\n\nconst (\n\tVersion1            = 1\n\tVersion2            = 2\n\tVersion3            = 3\n\tDefaultSnellVersion = Version1\n\n\t// max packet length\n\tmaxLength = 0x3FFF\n)\n\nconst (\n\tCommandPing       byte = 0\n\tCommandConnect    byte = 1\n\tCommandConnectV2  byte = 5\n\tCommandUDP        byte = 6\n\tCommondUDPForward byte = 1\n\n\tCommandTunnel byte = 0\n\tCommandPong   byte = 1\n\tCommandError  byte = 2\n\n\tVersion byte = 1\n)\n\nvar endSignal = []byte{}\n\ntype Snell struct {\n\tnet.Conn\n\tbuffer [1]byte\n\treply  bool\n}\n\nfunc (s *Snell) Read(b []byte) (int, error) {\n\tif s.reply {\n\t\treturn s.Conn.Read(b)\n\t}\n\n\ts.reply = true\n\tif _, err := io.ReadFull(s.Conn, s.buffer[:]); err != nil {\n\t\treturn 0, err\n\t}\n\n\tif s.buffer[0] == CommandTunnel {\n\t\treturn s.Conn.Read(b)\n\t} else if s.buffer[0] != CommandError {\n\t\treturn 0, errors.New(\"command not support\")\n\t}\n\n\t// CommandError\n\t// 1 byte error code\n\tif _, err := io.ReadFull(s.Conn, s.buffer[:]); err != nil {\n\t\treturn 0, err\n\t}\n\terrcode := int(s.buffer[0])\n\n\t// 1 byte error message length\n\tif _, err := io.ReadFull(s.Conn, s.buffer[:]); err != nil {\n\t\treturn 0, err\n\t}\n\tlength := int(s.buffer[0])\n\tmsg := make([]byte, length)\n\n\tif _, err := io.ReadFull(s.Conn, msg); err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn 0, fmt.Errorf(\"server reported code: %d, message: %s\", errcode, string(msg))\n}\n\nfunc WriteHeader(conn net.Conn, host string, port uint, version int) error {\n\tbuf := pool.GetBuffer()\n\tdefer pool.PutBuffer(buf)\n\tbuf.WriteByte(Version)\n\tif version == Version2 {\n\t\tbuf.WriteByte(CommandConnectV2)\n\t} else {\n\t\tbuf.WriteByte(CommandConnect)\n\t}\n\n\t// clientID length & id\n\tbuf.WriteByte(0)\n\n\t// host & port\n\tbuf.WriteByte(uint8(len(host)))\n\tbuf.WriteString(host)\n\tbinary.Write(buf, binary.BigEndian, uint16(port))\n\n\tif _, err := conn.Write(buf.Bytes()); err != nil {\n\t\treturn err\n\t}\n\n\treturn nil\n}\n\nfunc WriteUDPHeader(conn net.Conn, version int) error {\n\tif version < Version3 {\n\t\treturn errors.New(\"unsupport UDP version\")\n\t}\n\n\t// version, command, clientID length\n\t_, err := conn.Write([]byte{Version, CommandUDP, 0x00})\n\treturn err\n}\n\n// HalfClose works only on version2\nfunc HalfClose(conn net.Conn) error {\n\tif _, err := conn.Write(endSignal); err != nil {\n\t\treturn err\n\t}\n\n\tif s, ok := conn.(*Snell); ok {\n\t\ts.reply = false\n\t}\n\treturn nil\n}\n\nfunc StreamConn(conn net.Conn, psk []byte, version int) *Snell {\n\tvar cipher shadowaead.Cipher\n\tif version != Version1 {\n\t\tcipher = NewAES128GCM(psk)\n\t} else {\n\t\tcipher = NewChacha20Poly1305(psk)\n\t}\n\treturn &Snell{Conn: shadowaead.NewConn(conn, cipher)}\n}\n\nfunc PacketConn(conn net.Conn) net.PacketConn {\n\treturn &packetConn{\n\t\tConn: conn,\n\t}\n}\n\nfunc writePacket(w io.Writer, socks5Addr, payload []byte) (int, error) {\n\tbuf := pool.GetBuffer()\n\tdefer pool.PutBuffer(buf)\n\n\t// compose snell UDP address format (refer: icpz/snell-server-reversed)\n\t// a brand new wheel to replace socks5 address format, well done Yachen\n\tbuf.WriteByte(CommondUDPForward)\n\tswitch socks5Addr[0] {\n\tcase socks5.AtypDomainName:\n\t\thostLen := socks5Addr[1]\n\t\tbuf.Write(socks5Addr[1 : 1+1+hostLen+2])\n\tcase socks5.AtypIPv4:\n\t\tbuf.Write([]byte{0x00, 0x04})\n\t\tbuf.Write(socks5Addr[1 : 1+net.IPv4len+2])\n\tcase socks5.AtypIPv6:\n\t\tbuf.Write([]byte{0x00, 0x06})\n\t\tbuf.Write(socks5Addr[1 : 1+net.IPv6len+2])\n\t}\n\n\tbuf.Write(payload)\n\t_, err := w.Write(buf.Bytes())\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn len(payload), nil\n}\n\nfunc WritePacket(w io.Writer, socks5Addr, payload []byte) (int, error) {\n\tif len(payload) <= maxLength {\n\t\treturn writePacket(w, socks5Addr, payload)\n\t}\n\n\toffset := 0\n\ttotal := len(payload)\n\tfor {\n\t\tcursor := offset + maxLength\n\t\tif cursor > total {\n\t\t\tcursor = total\n\t\t}\n\n\t\tn, err := writePacket(w, socks5Addr, payload[offset:cursor])\n\t\tif err != nil {\n\t\t\treturn offset + n, err\n\t\t}\n\n\t\toffset = cursor\n\t\tif offset == total {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn total, nil\n}\n\nfunc ReadPacket(r io.Reader, payload []byte) (net.Addr, int, error) {\n\tbuf := pool.Get(pool.UDPBufferSize)\n\tdefer pool.Put(buf)\n\n\tn, err := r.Read(buf)\n\theadLen := 1\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\tif n < headLen {\n\t\treturn nil, 0, errors.New(\"insufficient UDP length\")\n\t}\n\n\t// parse snell UDP response address format\n\tswitch buf[0] {\n\tcase 0x04:\n\t\theadLen += net.IPv4len + 2\n\t\tif n < headLen {\n\t\t\terr = errors.New(\"insufficient UDP length\")\n\t\t\tbreak\n\t\t}\n\t\tbuf[0] = socks5.AtypIPv4\n\tcase 0x06:\n\t\theadLen += net.IPv6len + 2\n\t\tif n < headLen {\n\t\t\terr = errors.New(\"insufficient UDP length\")\n\t\t\tbreak\n\t\t}\n\t\tbuf[0] = socks5.AtypIPv6\n\tdefault:\n\t\terr = errors.New(\"ip version invalid\")\n\t}\n\n\tif err != nil {\n\t\treturn nil, 0, err\n\t}\n\n\taddr := socks5.SplitAddr(buf[0:])\n\tif addr == nil {\n\t\treturn nil, 0, errors.New(\"remote address invalid\")\n\t}\n\tuAddr := addr.UDPAddr()\n\tif uAddr == nil {\n\t\treturn nil, 0, errors.New(\"parse addr error\")\n\t}\n\n\tlength := len(payload)\n\tif n-headLen < length {\n\t\tlength = n - headLen\n\t}\n\tcopy(payload[:], buf[headLen:headLen+length])\n\n\treturn uAddr, length, nil\n}\n\ntype packetConn struct {\n\tnet.Conn\n\trMux sync.Mutex\n\twMux sync.Mutex\n}\n\nfunc (pc *packetConn) WriteTo(b []byte, addr net.Addr) (int, error) {\n\tpc.wMux.Lock()\n\tdefer pc.wMux.Unlock()\n\n\treturn WritePacket(pc, socks5.ParseAddrToSocksAddr(addr), b)\n}\n\nfunc (pc *packetConn) ReadFrom(b []byte) (int, net.Addr, error) {\n\tpc.rMux.Lock()\n\tdefer pc.rMux.Unlock()\n\n\taddr, n, err := ReadPacket(pc.Conn, b)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\n\treturn n, addr, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/socks4/socks4.go",
    "content": "package socks4\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\t\"net/netip\"\n\t\"strconv\"\n\n\t\"github.com/metacubex/mihomo/component/auth\"\n)\n\nconst Version = 0x04\n\ntype Command = uint8\n\nconst (\n\tCmdConnect Command = 0x01\n\tCmdBind    Command = 0x02\n)\n\ntype Code = uint8\n\nconst (\n\tRequestGranted          Code = 90\n\tRequestRejected         Code = 91\n\tRequestIdentdFailed     Code = 92\n\tRequestIdentdMismatched Code = 93\n)\n\nvar (\n\terrVersionMismatched   = errors.New(\"version code mismatched\")\n\terrCommandNotSupported = errors.New(\"command not supported\")\n\terrIPv6NotSupported    = errors.New(\"IPv6 not supported\")\n\n\tErrRequestRejected         = errors.New(\"request rejected or failed\")\n\tErrRequestIdentdFailed     = errors.New(\"request rejected because SOCKS server cannot connect to identd on the client\")\n\tErrRequestIdentdMismatched = errors.New(\"request rejected because the client program and identd report different user-ids\")\n\tErrRequestUnknownCode      = errors.New(\"request failed with unknown code\")\n)\n\nvar subnet = netip.PrefixFrom(netip.IPv4Unspecified(), 24)\n\nfunc ServerHandshake(rw io.ReadWriter, authenticator auth.Authenticator) (addr string, command Command, user string, err error) {\n\tvar req [8]byte\n\tif _, err = io.ReadFull(rw, req[:]); err != nil {\n\t\treturn\n\t}\n\n\tif req[0] != Version {\n\t\terr = errVersionMismatched\n\t\treturn\n\t}\n\n\tif command = req[1]; command != CmdConnect {\n\t\terr = errCommandNotSupported\n\t\treturn\n\t}\n\n\tvar (\n\t\tdstIP   = netip.AddrFrom4(*(*[4]byte)(req[4:8])) // [4]byte\n\t\tdstPort = req[2:4]                               // [2]byte\n\t)\n\n\tvar (\n\t\thost   string\n\t\tport   string\n\t\tcode   uint8\n\t\tuserID []byte\n\t)\n\tif userID, err = readUntilNull(rw); err != nil {\n\t\treturn\n\t}\n\tuser = string(userID)\n\n\tif isReservedIP(dstIP) {\n\t\tvar target []byte\n\t\tif target, err = readUntilNull(rw); err != nil {\n\t\t\treturn\n\t\t}\n\t\thost = string(target)\n\t}\n\n\tport = strconv.Itoa(int(binary.BigEndian.Uint16(dstPort)))\n\tif host != \"\" {\n\t\taddr = net.JoinHostPort(host, port)\n\t} else {\n\t\taddr = net.JoinHostPort(dstIP.String(), port)\n\t}\n\n\t// SOCKS4 only support USERID auth.\n\tif authenticator == nil || authenticator.Verify(user, \"\") {\n\t\tcode = RequestGranted\n\t} else {\n\t\tcode = RequestIdentdMismatched\n\t\terr = ErrRequestIdentdMismatched\n\t}\n\n\tvar reply [8]byte\n\treply[0] = 0x00 // reply code\n\treply[1] = code // result code\n\tcopy(reply[4:8], dstIP.AsSlice())\n\tcopy(reply[2:4], dstPort)\n\n\t_, wErr := rw.Write(reply[:])\n\tif err == nil {\n\t\terr = wErr\n\t}\n\treturn\n}\n\nfunc ClientHandshake(rw io.ReadWriter, addr string, command Command, userID string) (err error) {\n\thost, portStr, err := net.SplitHostPort(addr)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tport, err := strconv.ParseUint(portStr, 10, 16)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tdstIP, err := netip.ParseAddr(host)\n\tif err != nil /* HOST */ {\n\t\tdstIP = netip.AddrFrom4([4]byte{0, 0, 0, 1})\n\t} else if dstIP.Is6() /* IPv6 */ {\n\t\treturn errIPv6NotSupported\n\t}\n\n\treq := &bytes.Buffer{}\n\treq.WriteByte(Version)\n\treq.WriteByte(command)\n\t_ = binary.Write(req, binary.BigEndian, uint16(port))\n\treq.Write(dstIP.AsSlice())\n\treq.WriteString(userID)\n\treq.WriteByte(0) /* NULL */\n\n\tif isReservedIP(dstIP) /* SOCKS4A */ {\n\t\treq.WriteString(host)\n\t\treq.WriteByte(0) /* NULL */\n\t}\n\n\tif _, err = rw.Write(req.Bytes()); err != nil {\n\t\treturn err\n\t}\n\n\tvar resp [8]byte\n\tif _, err = io.ReadFull(rw, resp[:]); err != nil {\n\t\treturn err\n\t}\n\n\tif resp[0] != 0x00 {\n\t\treturn errVersionMismatched\n\t}\n\n\tswitch resp[1] {\n\tcase RequestGranted:\n\t\treturn nil\n\tcase RequestRejected:\n\t\treturn ErrRequestRejected\n\tcase RequestIdentdFailed:\n\t\treturn ErrRequestIdentdFailed\n\tcase RequestIdentdMismatched:\n\t\treturn ErrRequestIdentdMismatched\n\tdefault:\n\t\treturn ErrRequestUnknownCode\n\t}\n}\n\n// For version 4A, if the client cannot resolve the destination host's\n// domain name to find its IP address, it should set the first three bytes\n// of DSTIP to NULL and the last byte to a non-zero value. (This corresponds\n// to IP address 0.0.0.x, with x nonzero. As decreed by IANA  -- The\n// Internet Assigned Numbers Authority -- such an address is inadmissible\n// as a destination IP address and thus should never occur if the client\n// can resolve the domain name.)\nfunc isReservedIP(ip netip.Addr) bool {\n\treturn !ip.IsUnspecified() && subnet.Contains(ip)\n}\n\nfunc readUntilNull(r io.Reader) ([]byte, error) {\n\tbuf := &bytes.Buffer{}\n\tvar data [1]byte\n\n\tfor {\n\t\tif _, err := r.Read(data[:]); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif data[0] == 0 {\n\t\t\treturn buf.Bytes(), nil\n\t\t}\n\t\tbuf.WriteByte(data[0])\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/socks5/socks5.go",
    "content": "package socks5\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\t\"net/netip\"\n\t\"strconv\"\n\n\t\"github.com/metacubex/mihomo/component/auth\"\n)\n\n// Error represents a SOCKS error\ntype Error byte\n\nfunc (err Error) Error() string {\n\treturn \"SOCKS error: \" + strconv.Itoa(int(err))\n}\n\n// Command is request commands as defined in RFC 1928 section 4.\ntype Command = uint8\n\nconst Version = 5\n\n// SOCKS request commands as defined in RFC 1928 section 4.\nconst (\n\tCmdConnect      Command = 1\n\tCmdBind         Command = 2\n\tCmdUDPAssociate Command = 3\n)\n\n// SOCKS address types as defined in RFC 1928 section 5.\nconst (\n\tAtypIPv4       = 1\n\tAtypDomainName = 3\n\tAtypIPv6       = 4\n)\n\n// MaxAddrLen is the maximum size of SOCKS address in bytes.\nconst MaxAddrLen = 1 + 1 + 255 + 2\n\n// MaxAuthLen is the maximum size of user/password field in SOCKS5 Auth\nconst MaxAuthLen = 255\n\n// Addr represents a SOCKS address as defined in RFC 1928 section 5.\ntype Addr []byte\n\nfunc (a Addr) String() string {\n\tvar host, port string\n\n\tswitch a[0] {\n\tcase AtypDomainName:\n\t\thostLen := uint16(a[1])\n\t\thost = string(a[2 : 2+hostLen])\n\t\tport = strconv.Itoa((int(a[2+hostLen]) << 8) | int(a[2+hostLen+1]))\n\tcase AtypIPv4:\n\t\thost = net.IP(a[1 : 1+net.IPv4len]).String()\n\t\tport = strconv.Itoa((int(a[1+net.IPv4len]) << 8) | int(a[1+net.IPv4len+1]))\n\tcase AtypIPv6:\n\t\thost = net.IP(a[1 : 1+net.IPv6len]).String()\n\t\tport = strconv.Itoa((int(a[1+net.IPv6len]) << 8) | int(a[1+net.IPv6len+1]))\n\t}\n\n\treturn net.JoinHostPort(host, port)\n}\n\n// UDPAddr converts a socks5.Addr to *net.UDPAddr\nfunc (a Addr) UDPAddr() *net.UDPAddr {\n\tif len(a) == 0 {\n\t\treturn nil\n\t}\n\tswitch a[0] {\n\tcase AtypIPv4:\n\t\tvar ip [net.IPv4len]byte\n\t\tcopy(ip[0:], a[1:1+net.IPv4len])\n\t\treturn &net.UDPAddr{IP: net.IP(ip[:]), Port: int(binary.BigEndian.Uint16(a[1+net.IPv4len : 1+net.IPv4len+2]))}\n\tcase AtypIPv6:\n\t\tvar ip [net.IPv6len]byte\n\t\tcopy(ip[0:], a[1:1+net.IPv6len])\n\t\treturn &net.UDPAddr{IP: net.IP(ip[:]), Port: int(binary.BigEndian.Uint16(a[1+net.IPv6len : 1+net.IPv6len+2]))}\n\t}\n\t// Other Atyp\n\treturn nil\n}\n\n// SOCKS errors as defined in RFC 1928 section 6.\nconst (\n\tErrGeneralFailure       = Error(1)\n\tErrConnectionNotAllowed = Error(2)\n\tErrNetworkUnreachable   = Error(3)\n\tErrHostUnreachable      = Error(4)\n\tErrConnectionRefused    = Error(5)\n\tErrTTLExpired           = Error(6)\n\tErrCommandNotSupported  = Error(7)\n\tErrAddressNotSupported  = Error(8)\n)\n\n// Auth errors used to return a specific \"Auth failed\" error\nvar ErrAuth = errors.New(\"auth failed\")\n\ntype User struct {\n\tUsername string\n\tPassword string\n}\n\n// ServerHandshake fast-tracks SOCKS initialization to get target address to connect on server side.\nfunc ServerHandshake(rw net.Conn, authenticator auth.Authenticator) (addr Addr, command Command, user string, err error) {\n\t// Read RFC 1928 for request and reply structure and sizes.\n\tbuf := make([]byte, MaxAddrLen)\n\t// read VER, NMETHODS, METHODS\n\tif _, err = io.ReadFull(rw, buf[:2]); err != nil {\n\t\treturn\n\t}\n\tnmethods := buf[1]\n\tif _, err = io.ReadFull(rw, buf[:nmethods]); err != nil {\n\t\treturn\n\t}\n\n\tif nmethods == 1 && buf[0] == 0x02 /* will use password */ && authenticator == nil {\n\t\tauthenticator = auth.AlwaysValid\n\t}\n\n\t// write VER METHOD\n\tif authenticator != nil {\n\t\tif _, err = rw.Write([]byte{5, 2}); err != nil {\n\t\t\treturn\n\t\t}\n\n\t\t// Get header\n\t\theader := make([]byte, 2)\n\t\tif _, err = io.ReadFull(rw, header); err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tauthBuf := make([]byte, MaxAuthLen)\n\t\t// Get username\n\t\tuserLen := int(header[1])\n\t\tif userLen <= 0 {\n\t\t\trw.Write([]byte{1, 1})\n\t\t\terr = ErrAuth\n\t\t\treturn\n\t\t}\n\t\tif _, err = io.ReadFull(rw, authBuf[:userLen]); err != nil {\n\t\t\treturn\n\t\t}\n\t\tuser = string(authBuf[:userLen])\n\n\t\t// Get password\n\t\tif _, err = rw.Read(header[:1]); err != nil {\n\t\t\treturn\n\t\t}\n\t\tpassLen := int(header[0])\n\t\tif passLen <= 0 {\n\t\t\trw.Write([]byte{1, 1})\n\t\t\terr = ErrAuth\n\t\t\treturn\n\t\t}\n\t\tif _, err = io.ReadFull(rw, authBuf[:passLen]); err != nil {\n\t\t\treturn\n\t\t}\n\t\tpass := string(authBuf[:passLen])\n\n\t\t// Verify\n\t\tif ok := authenticator.Verify(string(user), string(pass)); !ok {\n\t\t\trw.Write([]byte{1, 1})\n\t\t\terr = ErrAuth\n\t\t\treturn\n\t\t}\n\n\t\t// Response auth state\n\t\tif _, err = rw.Write([]byte{1, 0}); err != nil {\n\t\t\treturn\n\t\t}\n\t} else {\n\t\tif _, err = rw.Write([]byte{5, 0}); err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\t// read VER CMD RSV ATYP DST.ADDR DST.PORT\n\tif _, err = io.ReadFull(rw, buf[:3]); err != nil {\n\t\treturn\n\t}\n\n\tcommand = buf[1]\n\taddr, err = ReadAddr(rw, buf)\n\tif err != nil {\n\t\treturn\n\t}\n\n\tswitch command {\n\tcase CmdConnect, CmdUDPAssociate:\n\t\t// Acquire server listened address info\n\t\tlocalAddr := ParseAddrToSocksAddr(rw.LocalAddr())\n\t\tif localAddr == nil {\n\t\t\terr = ErrAddressNotSupported\n\t\t} else {\n\t\t\t// write VER REP RSV ATYP BND.ADDR BND.PORT\n\t\t\t_, err = rw.Write(bytes.Join([][]byte{{5, 0, 0}, localAddr}, []byte{}))\n\t\t}\n\tcase CmdBind:\n\t\tfallthrough\n\tdefault:\n\t\terr = ErrCommandNotSupported\n\t}\n\n\treturn\n}\n\n// ClientHandshake fast-tracks SOCKS initialization to get target address to connect on client side.\nfunc ClientHandshake(rw io.ReadWriter, addr Addr, command Command, user *User) (Addr, error) {\n\tbuf := make([]byte, MaxAddrLen)\n\tvar err error\n\n\t// VER, NMETHODS, METHODS\n\tif user != nil {\n\t\t_, err = rw.Write([]byte{5, 1, 2})\n\t} else {\n\t\t_, err = rw.Write([]byte{5, 1, 0})\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// VER, METHOD\n\tif _, err := io.ReadFull(rw, buf[:2]); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif buf[0] != 5 {\n\t\treturn nil, errors.New(\"SOCKS version error\")\n\t}\n\n\tif buf[1] == 2 {\n\t\tif user == nil {\n\t\t\treturn nil, ErrAuth\n\t\t}\n\n\t\t// password protocol version\n\t\tauthMsg := &bytes.Buffer{}\n\t\tauthMsg.WriteByte(1)\n\t\tauthMsg.WriteByte(uint8(len(user.Username)))\n\t\tauthMsg.WriteString(user.Username)\n\t\tauthMsg.WriteByte(uint8(len(user.Password)))\n\t\tauthMsg.WriteString(user.Password)\n\n\t\tif _, err := rw.Write(authMsg.Bytes()); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif _, err := io.ReadFull(rw, buf[:2]); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif buf[1] != 0 {\n\t\t\treturn nil, errors.New(\"rejected username/password\")\n\t\t}\n\t} else if buf[1] != 0 {\n\t\treturn nil, errors.New(\"SOCKS need auth\")\n\t}\n\n\t// VER, CMD, RSV, ADDR\n\tif _, err := rw.Write(bytes.Join([][]byte{{5, command, 0}, addr}, []byte{})); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// VER, REP, RSV\n\tif _, err := io.ReadFull(rw, buf[:3]); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn ReadAddr(rw, buf)\n}\n\nfunc ReadAddr(r io.Reader, b []byte) (Addr, error) {\n\tif len(b) < MaxAddrLen {\n\t\treturn nil, io.ErrShortBuffer\n\t}\n\t_, err := io.ReadFull(r, b[:1]) // read 1st byte for address type\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tswitch b[0] {\n\tcase AtypDomainName:\n\t\t_, err = io.ReadFull(r, b[1:2]) // read 2nd byte for domain length\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tdomainLength := uint16(b[1])\n\t\t_, err = io.ReadFull(r, b[2:2+domainLength+2])\n\t\treturn b[:1+1+domainLength+2], err\n\tcase AtypIPv4:\n\t\t_, err = io.ReadFull(r, b[1:1+net.IPv4len+2])\n\t\treturn b[:1+net.IPv4len+2], err\n\tcase AtypIPv6:\n\t\t_, err = io.ReadFull(r, b[1:1+net.IPv6len+2])\n\t\treturn b[:1+net.IPv6len+2], err\n\t}\n\n\treturn nil, ErrAddressNotSupported\n}\n\nfunc ReadAddr0(r io.Reader) (Addr, error) {\n\taType, err := ReadByte(r) // read 1st byte for address type\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tswitch aType {\n\tcase AtypDomainName:\n\t\tvar domainLength byte\n\t\tdomainLength, err = ReadByte(r) // read 2nd byte for domain length\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tb := make([]byte, 1+1+uint16(domainLength)+2)\n\t\t_, err = io.ReadFull(r, b[2:])\n\t\tb[0] = aType\n\t\tb[1] = domainLength\n\t\treturn b, err\n\tcase AtypIPv4:\n\t\tvar b [1 + net.IPv4len + 2]byte\n\t\t_, err = io.ReadFull(r, b[1:])\n\t\tb[0] = aType\n\t\treturn b[:], err\n\tcase AtypIPv6:\n\t\tvar b [1 + net.IPv6len + 2]byte\n\t\t_, err = io.ReadFull(r, b[1:])\n\t\tb[0] = aType\n\t\treturn b[:], err\n\t}\n\n\treturn nil, ErrAddressNotSupported\n}\n\nfunc ReadByte(reader io.Reader) (byte, error) {\n\tif br, isBr := reader.(io.ByteReader); isBr {\n\t\treturn br.ReadByte()\n\t}\n\tvar b [1]byte\n\tif _, err := io.ReadFull(reader, b[:]); err != nil {\n\t\treturn 0, err\n\t}\n\treturn b[0], nil\n}\n\n// SplitAddr slices a SOCKS address from beginning of b. Returns nil if failed.\nfunc SplitAddr(b []byte) Addr {\n\taddrLen := 1\n\tif len(b) < addrLen {\n\t\treturn nil\n\t}\n\n\tswitch b[0] {\n\tcase AtypDomainName:\n\t\tif len(b) < 2 {\n\t\t\treturn nil\n\t\t}\n\t\taddrLen = 1 + 1 + int(b[1]) + 2\n\tcase AtypIPv4:\n\t\taddrLen = 1 + net.IPv4len + 2\n\tcase AtypIPv6:\n\t\taddrLen = 1 + net.IPv6len + 2\n\tdefault:\n\t\treturn nil\n\n\t}\n\n\tif len(b) < addrLen {\n\t\treturn nil\n\t}\n\n\treturn b[:addrLen]\n}\n\n// ParseAddr parses the address in string s. Returns nil if failed.\nfunc ParseAddr(s string) Addr {\n\tvar addr Addr\n\thost, port, err := net.SplitHostPort(s)\n\tif err != nil {\n\t\treturn nil\n\t}\n\tif ip := net.ParseIP(host); ip != nil {\n\t\tif ip4 := ip.To4(); ip4 != nil {\n\t\t\taddr = make([]byte, 1+net.IPv4len+2)\n\t\t\taddr[0] = AtypIPv4\n\t\t\tcopy(addr[1:], ip4)\n\t\t} else {\n\t\t\taddr = make([]byte, 1+net.IPv6len+2)\n\t\t\taddr[0] = AtypIPv6\n\t\t\tcopy(addr[1:], ip)\n\t\t}\n\t} else {\n\t\tif len(host) > 255 {\n\t\t\treturn nil\n\t\t}\n\t\taddr = make([]byte, 1+1+len(host)+2)\n\t\taddr[0] = AtypDomainName\n\t\taddr[1] = byte(len(host))\n\t\tcopy(addr[2:], host)\n\t}\n\n\tportnum, err := strconv.ParseUint(port, 10, 16)\n\tif err != nil {\n\t\treturn nil\n\t}\n\n\taddr[len(addr)-2], addr[len(addr)-1] = byte(portnum>>8), byte(portnum)\n\n\treturn addr\n}\n\n// ParseAddrToSocksAddr parse a socks addr from net.addr\n// This is a fast path of ParseAddr(addr.String())\nfunc ParseAddrToSocksAddr(addr net.Addr) Addr {\n\tvar hostip net.IP\n\tvar port int\n\tswitch addr := addr.(type) {\n\tcase *net.UDPAddr:\n\t\thostip = addr.IP\n\t\tport = addr.Port\n\tcase *net.TCPAddr:\n\t\thostip = addr.IP\n\t\tport = addr.Port\n\tcase nil:\n\t\treturn nil\n\t}\n\n\t// fallback parse\n\tif hostip == nil {\n\t\treturn ParseAddr(addr.String())\n\t}\n\n\tvar parsed Addr\n\tif ip4 := hostip.To4(); ip4.DefaultMask() != nil {\n\t\tparsed = make([]byte, 1+net.IPv4len+2)\n\t\tparsed[0] = AtypIPv4\n\t\tcopy(parsed[1:], ip4)\n\t\tbinary.BigEndian.PutUint16(parsed[1+net.IPv4len:], uint16(port))\n\n\t} else {\n\t\tparsed = make([]byte, 1+net.IPv6len+2)\n\t\tparsed[0] = AtypIPv6\n\t\tcopy(parsed[1:], hostip)\n\t\tbinary.BigEndian.PutUint16(parsed[1+net.IPv6len:], uint16(port))\n\t}\n\treturn parsed\n}\n\nfunc AddrFromStdAddrPort(addrPort netip.AddrPort) Addr {\n\taddr := addrPort.Addr()\n\tif addr.Is4() {\n\t\tip4 := addr.As4()\n\t\treturn []byte{AtypIPv4, ip4[0], ip4[1], ip4[2], ip4[3], byte(addrPort.Port() >> 8), byte(addrPort.Port())}\n\t}\n\n\tbuf := make([]byte, 1+net.IPv6len+2)\n\tbuf[0] = AtypIPv6\n\tcopy(buf[1:], addr.AsSlice())\n\tbuf[1+net.IPv6len] = byte(addrPort.Port() >> 8)\n\tbuf[1+net.IPv6len+1] = byte(addrPort.Port())\n\treturn buf\n}\n\n// DecodeUDPPacket split `packet` to addr payload, and this function is mutable with `packet`\nfunc DecodeUDPPacket(packet []byte) (addr Addr, payload []byte, err error) {\n\tif len(packet) < 5 {\n\t\terr = errors.New(\"insufficient length of packet\")\n\t\treturn\n\t}\n\n\t// packet[0] and packet[1] are reserved\n\tif !bytes.Equal(packet[:2], []byte{0, 0}) {\n\t\terr = errors.New(\"reserved fields should be zero\")\n\t\treturn\n\t}\n\n\tif packet[2] != 0 /* fragments */ {\n\t\terr = errors.New(\"discarding fragmented payload\")\n\t\treturn\n\t}\n\n\taddr = SplitAddr(packet[3:])\n\tif addr == nil {\n\t\terr = errors.New(\"failed to read UDP header\")\n\t}\n\n\tpayload = packet[3+len(addr):]\n\treturn\n}\n\nfunc EncodeUDPPacket(addr Addr, payload []byte) (packet []byte, err error) {\n\tif addr == nil {\n\t\terr = errors.New(\"address is invalid\")\n\t\treturn\n\t}\n\tpacket = bytes.Join([][]byte{{0, 0, 0}, addr, payload}, []byte{})\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/ssr/obfs/base.go",
    "content": "package obfs\n\ntype Base struct {\n\tHost   string\n\tPort   int\n\tKey    []byte\n\tIVSize int\n\tParam  string\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/ssr/obfs/http_post.go",
    "content": "package obfs\n\nfunc init() {\n\tregister(\"http_post\", newHTTPPost, 0)\n}\n\nfunc newHTTPPost(b *Base) Obfs {\n\treturn &httpObfs{Base: b, post: true}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/ssr/obfs/http_simple.go",
    "content": "package obfs\n\nimport (\n\t\"bytes\"\n\t\"encoding/hex\"\n\t\"io\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/common/pool\"\n\n\t\"github.com/metacubex/randv2\"\n)\n\nfunc init() {\n\tregister(\"http_simple\", newHTTPSimple, 0)\n}\n\ntype httpObfs struct {\n\t*Base\n\tpost bool\n}\n\nfunc newHTTPSimple(b *Base) Obfs {\n\treturn &httpObfs{Base: b}\n}\n\ntype httpConn struct {\n\tnet.Conn\n\t*httpObfs\n\thasSentHeader bool\n\thasRecvHeader bool\n\tbuf           []byte\n}\n\nfunc (h *httpObfs) StreamConn(c net.Conn) net.Conn {\n\treturn &httpConn{Conn: c, httpObfs: h}\n}\n\nfunc (c *httpConn) Read(b []byte) (int, error) {\n\tif c.buf != nil {\n\t\tn := copy(b, c.buf)\n\t\tif n == len(c.buf) {\n\t\t\tc.buf = nil\n\t\t} else {\n\t\t\tc.buf = c.buf[n:]\n\t\t}\n\t\treturn n, nil\n\t}\n\n\tif c.hasRecvHeader {\n\t\treturn c.Conn.Read(b)\n\t}\n\n\tbuf := pool.Get(pool.RelayBufferSize)\n\tdefer pool.Put(buf)\n\tn, err := c.Conn.Read(buf)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tpos := bytes.Index(buf[:n], []byte(\"\\r\\n\\r\\n\"))\n\tif pos == -1 {\n\t\treturn 0, io.EOF\n\t}\n\tc.hasRecvHeader = true\n\tdataLength := n - pos - 4\n\tn = copy(b, buf[4+pos:n])\n\tif dataLength > n {\n\t\tc.buf = append(c.buf, buf[4+pos+n:4+pos+dataLength]...)\n\t}\n\treturn n, nil\n}\n\nfunc (c *httpConn) Write(b []byte) (int, error) {\n\tif c.hasSentHeader {\n\t\treturn c.Conn.Write(b)\n\t}\n\t// 30: head length\n\theadLength := c.IVSize + 30\n\n\tbLength := len(b)\n\theadDataLength := bLength\n\tif bLength-headLength > 64 {\n\t\theadDataLength = headLength + randv2.IntN(65)\n\t}\n\theadData := b[:headDataLength]\n\tb = b[headDataLength:]\n\n\tvar body string\n\thost := c.Host\n\tif len(c.Param) > 0 {\n\t\tpos := strings.Index(c.Param, \"#\")\n\t\tif pos != -1 {\n\t\t\tbody = strings.ReplaceAll(c.Param[pos+1:], \"\\n\", \"\\r\\n\")\n\t\t\tbody = strings.ReplaceAll(body, \"\\\\n\", \"\\r\\n\")\n\t\t\thost = c.Param[:pos]\n\t\t} else {\n\t\t\thost = c.Param\n\t\t}\n\t}\n\thosts := strings.Split(host, \",\")\n\thost = hosts[randv2.IntN(len(hosts))]\n\n\tbuf := pool.GetBuffer()\n\tdefer pool.PutBuffer(buf)\n\tif c.post {\n\t\tbuf.WriteString(\"POST /\")\n\t} else {\n\t\tbuf.WriteString(\"GET /\")\n\t}\n\tpackURLEncodedHeadData(buf, headData)\n\tbuf.WriteString(\" HTTP/1.1\\r\\nHost: \" + host)\n\tif c.Port != 80 {\n\t\tbuf.WriteString(\":\" + strconv.Itoa(c.Port))\n\t}\n\tbuf.WriteString(\"\\r\\n\")\n\tif len(body) > 0 {\n\t\tbuf.WriteString(body + \"\\r\\n\\r\\n\")\n\t} else {\n\t\tbuf.WriteString(\"User-Agent: \")\n\t\tbuf.WriteString(userAgent[randv2.IntN(len(userAgent))])\n\t\tbuf.WriteString(\"\\r\\nAccept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\\r\\nAccept-Language: en-US,en;q=0.8\\r\\nAccept-Encoding: gzip, deflate\\r\\n\")\n\t\tif c.post {\n\t\t\tpackBoundary(buf)\n\t\t}\n\t\tbuf.WriteString(\"DNT: 1\\r\\nConnection: keep-alive\\r\\n\\r\\n\")\n\t}\n\tbuf.Write(b)\n\t_, err := c.Conn.Write(buf.Bytes())\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tc.hasSentHeader = true\n\treturn bLength, nil\n}\n\nfunc packURLEncodedHeadData(buf *bytes.Buffer, data []byte) {\n\tdataLength := len(data)\n\tfor i := 0; i < dataLength; i++ {\n\t\tbuf.WriteRune('%')\n\t\tbuf.WriteString(hex.EncodeToString(data[i : i+1]))\n\t}\n}\n\nfunc packBoundary(buf *bytes.Buffer) {\n\tbuf.WriteString(\"Content-Type: multipart/form-data; boundary=\")\n\tset := \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\"\n\tfor i := 0; i < 32; i++ {\n\t\tbuf.WriteByte(set[randv2.IntN(62)])\n\t}\n\tbuf.WriteString(\"\\r\\n\")\n}\n\nvar userAgent = []string{\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.162 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.85 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.0; Moto C Build/NRD90M.059) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0.1; SM-G532M Build/MMB29T; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/55.0.2883.91 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.111 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 5.1.1; SM-J120M Build/LMY47X) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.0; Moto G (5) Build/NPPS25.137-93-14) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.0; SM-G570M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 5.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0; CAM-L03 Build/HUAWEICAM-L03) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.76 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.63 Safari/534.3\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.517.44 Safari/534.7\",\n\t\"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.75 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.3 (KHTML, like Gecko) Chrome/6.0.472.63 Safari/534.3\",\n\t\"Mozilla/5.0 (Linux; Android 8.0.0; FIG-LX3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/40.0.2214.115 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.10 (KHTML, like Gecko) Chrome/8.0.552.237 Safari/534.10\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36\",\n\t\"Mozilla/5.0 (X11; U; Linux x86_64; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/5.0.342.1 Safari/533.2\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.89 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.81 Safari/537.36\",\n\t\"Mozilla/5.0 (X11; Datanyze; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.71 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 5.1.1; SM-J111M Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/37.0.2062.120 Safari/537.36\",\n\t\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/32.0.1700.107 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0.1; SM-J700M Build/MMB29K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.63 Safari/537.36\",\n\t\"Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Slackware/Chrome/12.0.742.100 Safari/534.30\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.167 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.116 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36\",\n\t\"Mozilla/5.0 (X11; Linux i686) AppleWebKit/534.30 (KHTML, like Gecko) Chrome/12.0.742.100 Safari/534.30\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 8.0.0; WAS-LX3 Build/HUAWEIWAS-LX3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.57 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.101 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.1805 Safari/537.36 MVisionPlayer/1.0.0.0\",\n\t\"Mozilla/5.0 (Linux; Android 7.0; TRT-LX3 Build/HUAWEITRT-LX3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.89 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0; vivo 1610 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.124 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 4.4.2; de-de; SAMSUNG GT-I9195 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Version/1.5 Chrome/28.0.1500.94 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\",\n\t\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.110 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 8.0.0; ANE-LX3 Build/HUAWEIANE-LX3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.143 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36\",\n\t\"Mozilla/5.0 (X11; U; Linux i586; en-US) AppleWebKit/533.2 (KHTML, like Gecko) Chrome/5.0.342.1 Safari/533.2\",\n\t\"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.65 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.0; SM-G610M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.80 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0.1; SM-J500M Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/534.7 (KHTML, like Gecko) Chrome/7.0.517.44 Safari/534.7\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.104 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0; vivo 1606 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.124 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36\",\n\t\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.135 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.0; SM-G610M Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.1; vivo 1716 Build/N2G47H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.98 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.93 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.0; SM-G570M Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0; MYA-L22 Build/HUAWEIMYA-L22) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 5.1; A1601 Build/LMY47I) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.98 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.0; TRT-LX2 Build/HUAWEITRT-LX2; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/59.0.3071.125 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 5.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows; U; Windows NT 6.1; en-US) AppleWebKit/534.17 (KHTML, like Gecko) Chrome/10.0.649.0 Safari/534.17\",\n\t\"Mozilla/5.0 (Linux; Android 6.0; CAM-L21 Build/HUAWEICAM-L21; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/62.0.3202.84 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36\",\n\t\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/534.24 (KHTML, like Gecko) Chrome/11.0.696.3 Safari/534.24\",\n\t\"Mozilla/5.0 (Linux; Android 7.1.2; Redmi 4X Build/N2G47H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 4.4.2; SM-G7102 Build/KOT49H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.109 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 5.1; HUAWEI CUN-L22 Build/HUAWEICUN-L22; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/62.0.3202.84 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 5.1.1; A37fw Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.0; SM-J730GM Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.0; SM-G610F Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.1.2; Redmi Note 5A Build/N2G47H; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/63.0.3239.111 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.0; Redmi Note 4 Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.111 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.106 Safari/537.36\",\n\t\"Mozilla/5.0 (Unknown; Linux) AppleWebKit/538.1 (KHTML, like Gecko) Chrome/v1.0.0 Safari/538.1\",\n\t\"Mozilla/5.0 (Linux; Android 7.0; BLL-L22 Build/HUAWEIBLL-L22) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.0; SM-J710F Build/NRD90M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.84 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0.1; SM-G532M Build/MMB29T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.91 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.1.1; CPH1723 Build/N6F26Q) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.98 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.118 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.79 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/45.0.2454.101 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.94 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 8.0.0; FIG-LX3 Build/HUAWEIFIG-LX3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows; U; Windows NT 6.1; de-DE) AppleWebKit/534.17 (KHTML, like Gecko) Chrome/10.0.649.0 Safari/534.17\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.63 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2228.0 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.94 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.65 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 7.1; Mi A1 Build/N2G47H) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US) AppleWebKit/533.4 (KHTML, like Gecko) Chrome/5.0.375.99 Safari/533.4\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.125 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.89 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.111 Safari/537.36 MVisionPlayer/1.0.0.0\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 5.1; A37f Build/LMY47V) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/43.0.2357.93 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/56.0.2924.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.86 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.110 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.76 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 5.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0.1; CPH1607 Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/63.0.3239.111 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.99 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/39.0.2171.95 Safari/537.36\",\n\t\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0.1; vivo 1603 Build/MMB29M) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.83 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0.1; SM-G532M Build/MMB29T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0.1; Redmi 4A Build/MMB29M; wv) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/60.0.3112.116 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.103 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.0) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36\",\n\t\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/44.0.2403.157 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.71 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 5.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.2; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.113 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.2; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/42.0.2311.90 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.132 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3112.90 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.77 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.31 (KHTML, like Gecko) Chrome/26.0.1410.64 Safari/537.31\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.84 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/57.0.2987.133 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.186 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/59.0.3071.115 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/55.0.2883.87 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/36.0.1985.143 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 5.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/49.0.2623.112 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.181 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/67.0.3396.99 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/51.0.2704.106 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.3; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0.1; SM-G532G Build/MMB29T) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/63.0.3239.83 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/48.0.2564.109 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.117 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/66.0.3359.139 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/34.0.1847.131 Safari/537.36\",\n\t\"Mozilla/5.0 (Linux; Android 6.0; vivo 1713 Build/MRA58K) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/53.0.2785.124 Mobile Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/62.0.3202.89 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/46.0.2490.80 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.101 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.67 Safari/537.36\",\n\t\"Mozilla/5.0 (Windows NT 6.1; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36\",\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/ssr/obfs/obfs.go",
    "content": "package obfs\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n)\n\nvar (\n\terrTLS12TicketAuthIncorrectMagicNumber = errors.New(\"tls1.2_ticket_auth incorrect magic number\")\n\terrTLS12TicketAuthTooShortData         = errors.New(\"tls1.2_ticket_auth too short data\")\n\terrTLS12TicketAuthHMACError            = errors.New(\"tls1.2_ticket_auth hmac verifying failed\")\n)\n\ntype authData struct {\n\tclientID [32]byte\n}\n\ntype Obfs interface {\n\tStreamConn(net.Conn) net.Conn\n}\n\ntype obfsCreator func(b *Base) Obfs\n\nvar obfsList = make(map[string]struct {\n\toverhead int\n\tnew      obfsCreator\n})\n\nfunc register(name string, c obfsCreator, o int) {\n\tobfsList[name] = struct {\n\t\toverhead int\n\t\tnew      obfsCreator\n\t}{overhead: o, new: c}\n}\n\nfunc PickObfs(name string, b *Base) (Obfs, int, error) {\n\tif choice, ok := obfsList[name]; ok {\n\t\treturn choice.new(b), choice.overhead, nil\n\t}\n\treturn nil, 0, fmt.Errorf(\"Obfs %s not supported\", name)\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/ssr/obfs/plain.go",
    "content": "package obfs\n\nimport \"net\"\n\ntype plain struct{}\n\nfunc init() {\n\tregister(\"plain\", newPlain, 0)\n}\n\nfunc newPlain(b *Base) Obfs {\n\treturn &plain{}\n}\n\nfunc (p *plain) StreamConn(c net.Conn) net.Conn { return c }\n"
  },
  {
    "path": "core/Clash.Meta/transport/ssr/obfs/random_head.go",
    "content": "package obfs\n\nimport (\n\t\"crypto/rand\"\n\t\"encoding/binary\"\n\t\"hash/crc32\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/common/pool\"\n\n\t\"github.com/metacubex/randv2\"\n)\n\nfunc init() {\n\tregister(\"random_head\", newRandomHead, 0)\n}\n\ntype randomHead struct {\n\t*Base\n}\n\nfunc newRandomHead(b *Base) Obfs {\n\treturn &randomHead{Base: b}\n}\n\ntype randomHeadConn struct {\n\tnet.Conn\n\t*randomHead\n\thasSentHeader bool\n\trawTransSent  bool\n\trawTransRecv  bool\n\tbuf           []byte\n}\n\nfunc (r *randomHead) StreamConn(c net.Conn) net.Conn {\n\treturn &randomHeadConn{Conn: c, randomHead: r}\n}\n\nfunc (c *randomHeadConn) Read(b []byte) (int, error) {\n\tif c.rawTransRecv {\n\t\treturn c.Conn.Read(b)\n\t}\n\tbuf := pool.Get(pool.RelayBufferSize)\n\tdefer pool.Put(buf)\n\tc.Conn.Read(buf)\n\tc.rawTransRecv = true\n\tc.Write(nil)\n\treturn 0, nil\n}\n\nfunc (c *randomHeadConn) Write(b []byte) (int, error) {\n\tif c.rawTransSent {\n\t\treturn c.Conn.Write(b)\n\t}\n\tc.buf = append(c.buf, b...)\n\tif !c.hasSentHeader {\n\t\tc.hasSentHeader = true\n\t\tdataLength := randv2.IntN(96) + 4\n\t\tbuf := pool.Get(dataLength + 4)\n\t\tdefer pool.Put(buf)\n\t\trand.Read(buf[:dataLength])\n\t\tbinary.LittleEndian.PutUint32(buf[dataLength:], 0xffffffff-crc32.ChecksumIEEE(buf[:dataLength]))\n\t\t_, err := c.Conn.Write(buf)\n\t\treturn len(b), err\n\t}\n\tif c.rawTransRecv {\n\t\t_, err := c.Conn.Write(c.buf)\n\t\tc.buf = nil\n\t\tc.rawTransSent = true\n\t\treturn len(b), err\n\t}\n\treturn len(b), nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/ssr/obfs/tls1.2_ticket_auth.go",
    "content": "package obfs\n\nimport (\n\t\"bytes\"\n\t\"crypto/hmac\"\n\t\"crypto/rand\"\n\t\"encoding/binary\"\n\t\"net\"\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/common/pool\"\n\t\"github.com/metacubex/mihomo/ntp\"\n\t\"github.com/metacubex/mihomo/transport/ssr/tools\"\n\n\t\"github.com/metacubex/randv2\"\n)\n\nfunc init() {\n\tregister(\"tls1.2_ticket_auth\", newTLS12Ticket, 5)\n\tregister(\"tls1.2_ticket_fastauth\", newTLS12Ticket, 5)\n}\n\ntype tls12Ticket struct {\n\t*Base\n\t*authData\n}\n\nfunc newTLS12Ticket(b *Base) Obfs {\n\tr := &tls12Ticket{Base: b, authData: &authData{}}\n\trand.Read(r.clientID[:])\n\treturn r\n}\n\ntype tls12TicketConn struct {\n\tnet.Conn\n\t*tls12Ticket\n\thandshakeStatus int\n\tdecoded         bytes.Buffer\n\tunderDecoded    bytes.Buffer\n\tsendBuf         bytes.Buffer\n}\n\nfunc (t *tls12Ticket) StreamConn(c net.Conn) net.Conn {\n\treturn &tls12TicketConn{Conn: c, tls12Ticket: t}\n}\n\nfunc (c *tls12TicketConn) Read(b []byte) (int, error) {\n\tif c.decoded.Len() > 0 {\n\t\treturn c.decoded.Read(b)\n\t}\n\n\tbuf := pool.Get(pool.RelayBufferSize)\n\tdefer pool.Put(buf)\n\tn, err := c.Conn.Read(buf)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tif c.handshakeStatus == 8 {\n\t\tc.underDecoded.Write(buf[:n])\n\t\tfor c.underDecoded.Len() > 5 {\n\t\t\tif !bytes.Equal(c.underDecoded.Bytes()[:3], []byte{0x17, 3, 3}) {\n\t\t\t\tc.underDecoded.Reset()\n\t\t\t\treturn 0, errTLS12TicketAuthIncorrectMagicNumber\n\t\t\t}\n\t\t\tsize := int(binary.BigEndian.Uint16(c.underDecoded.Bytes()[3:5]))\n\t\t\tif c.underDecoded.Len() < 5+size {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\tc.underDecoded.Next(5)\n\t\t\tc.decoded.Write(c.underDecoded.Next(size))\n\t\t}\n\t\tn, _ = c.decoded.Read(b)\n\t\treturn n, nil\n\t}\n\n\tif n < 11+32+1+32 {\n\t\treturn 0, errTLS12TicketAuthTooShortData\n\t}\n\n\tif !hmac.Equal(buf[33:43], c.hmacSHA1(buf[11:33])[:10]) || !hmac.Equal(buf[n-10:n], c.hmacSHA1(buf[:n-10])[:10]) {\n\t\treturn 0, errTLS12TicketAuthHMACError\n\t}\n\n\tc.Write(nil)\n\treturn 0, nil\n}\n\nfunc (c *tls12TicketConn) Write(b []byte) (int, error) {\n\tlength := len(b)\n\tif c.handshakeStatus == 8 {\n\t\tbuf := pool.GetBuffer()\n\t\tdefer pool.PutBuffer(buf)\n\t\tfor len(b) > 2048 {\n\t\t\tsize := randv2.IntN(4096) + 100\n\t\t\tif len(b) < size {\n\t\t\t\tsize = len(b)\n\t\t\t}\n\t\t\tpackData(buf, b[:size])\n\t\t\tb = b[size:]\n\t\t}\n\t\tif len(b) > 0 {\n\t\t\tpackData(buf, b)\n\t\t}\n\t\t_, err := c.Conn.Write(buf.Bytes())\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\treturn length, nil\n\t}\n\n\tif len(b) > 0 {\n\t\tpackData(&c.sendBuf, b)\n\t}\n\n\tif c.handshakeStatus == 0 {\n\t\tc.handshakeStatus = 1\n\n\t\tdata := pool.GetBuffer()\n\t\tdefer pool.PutBuffer(data)\n\n\t\tdata.Write([]byte{3, 3})\n\t\tc.packAuthData(data)\n\t\tdata.WriteByte(0x20)\n\t\tdata.Write(c.clientID[:])\n\t\tdata.Write([]byte{0x00, 0x1c, 0xc0, 0x2b, 0xc0, 0x2f, 0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0x14, 0xcc, 0x13, 0xc0, 0x0a, 0xc0, 0x14, 0xc0, 0x09, 0xc0, 0x13, 0x00, 0x9c, 0x00, 0x35, 0x00, 0x2f, 0x00, 0x0a})\n\t\tdata.Write([]byte{0x1, 0x0})\n\n\t\text := pool.GetBuffer()\n\t\tdefer pool.PutBuffer(ext)\n\n\t\thost := c.getHost()\n\t\text.Write([]byte{0xff, 0x01, 0x00, 0x01, 0x00})\n\t\tpackSNIData(ext, host)\n\t\text.Write([]byte{0, 0x17, 0, 0})\n\t\tc.packTicketBuf(ext, host)\n\t\text.Write([]byte{0x00, 0x0d, 0x00, 0x16, 0x00, 0x14, 0x06, 0x01, 0x06, 0x03, 0x05, 0x01, 0x05, 0x03, 0x04, 0x01, 0x04, 0x03, 0x03, 0x01, 0x03, 0x03, 0x02, 0x01, 0x02, 0x03})\n\t\text.Write([]byte{0x00, 0x05, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00})\n\t\text.Write([]byte{0x00, 0x12, 0x00, 0x00})\n\t\text.Write([]byte{0x75, 0x50, 0x00, 0x00})\n\t\text.Write([]byte{0x00, 0x0b, 0x00, 0x02, 0x01, 0x00})\n\t\text.Write([]byte{0x00, 0x0a, 0x00, 0x06, 0x00, 0x04, 0x00, 0x17, 0x00, 0x18})\n\n\t\tbinary.Write(data, binary.BigEndian, uint16(ext.Len()))\n\t\tdata.ReadFrom(ext)\n\n\t\tret := pool.GetBuffer()\n\t\tdefer pool.PutBuffer(ret)\n\n\t\tret.Write([]byte{0x16, 3, 1})\n\t\tbinary.Write(ret, binary.BigEndian, uint16(data.Len()+4))\n\t\tret.Write([]byte{1, 0})\n\t\tbinary.Write(ret, binary.BigEndian, uint16(data.Len()))\n\t\tret.ReadFrom(data)\n\n\t\t_, err := c.Conn.Write(ret.Bytes())\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\treturn length, nil\n\t} else if c.handshakeStatus == 1 && len(b) == 0 {\n\t\tbuf := pool.GetBuffer()\n\t\tdefer pool.PutBuffer(buf)\n\n\t\tbuf.Write([]byte{0x14, 3, 3, 0, 1, 1, 0x16, 3, 3, 0, 0x20})\n\t\ttools.AppendRandBytes(buf, 22)\n\t\tbuf.Write(c.hmacSHA1(buf.Bytes())[:10])\n\t\tbuf.ReadFrom(&c.sendBuf)\n\n\t\tc.handshakeStatus = 8\n\n\t\t_, err := c.Conn.Write(buf.Bytes())\n\t\treturn 0, err\n\t}\n\treturn length, nil\n}\n\nfunc packData(buf *bytes.Buffer, data []byte) {\n\tbuf.Write([]byte{0x17, 3, 3})\n\tbinary.Write(buf, binary.BigEndian, uint16(len(data)))\n\tbuf.Write(data)\n}\n\nfunc (t *tls12Ticket) packAuthData(buf *bytes.Buffer) {\n\tbinary.Write(buf, binary.BigEndian, uint32(ntp.Now().Unix()))\n\ttools.AppendRandBytes(buf, 18)\n\tbuf.Write(t.hmacSHA1(buf.Bytes()[buf.Len()-22:])[:10])\n}\n\nfunc packSNIData(buf *bytes.Buffer, u string) {\n\tlen := uint16(len(u))\n\tbuf.Write([]byte{0, 0})\n\tbinary.Write(buf, binary.BigEndian, len+5)\n\tbinary.Write(buf, binary.BigEndian, len+3)\n\tbuf.WriteByte(0)\n\tbinary.Write(buf, binary.BigEndian, len)\n\tbuf.WriteString(u)\n}\n\nfunc (c *tls12TicketConn) packTicketBuf(buf *bytes.Buffer, u string) {\n\tlength := 16 * (randv2.IntN(17) + 8)\n\tbuf.Write([]byte{0, 0x23})\n\tbinary.Write(buf, binary.BigEndian, uint16(length))\n\ttools.AppendRandBytes(buf, length)\n}\n\nfunc (t *tls12Ticket) hmacSHA1(data []byte) []byte {\n\tkey := pool.Get(len(t.Key) + 32)\n\tdefer pool.Put(key)\n\tcopy(key, t.Key)\n\tcopy(key[len(t.Key):], t.clientID[:])\n\n\tsha1Data := tools.HmacSHA1(key, data)\n\treturn sha1Data[:10]\n}\n\nfunc (t *tls12Ticket) getHost() string {\n\thost := t.Param\n\tif len(host) == 0 {\n\t\thost = t.Host\n\t}\n\tif len(host) > 0 && host[len(host)-1] >= '0' && host[len(host)-1] <= '9' {\n\t\thost = \"\"\n\t}\n\thosts := strings.Split(host, \",\")\n\thost = hosts[randv2.IntN(len(hosts))]\n\treturn host\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/ssr/protocol/auth_aes128_md5.go",
    "content": "package protocol\n\nimport \"github.com/metacubex/mihomo/transport/ssr/tools\"\n\nfunc init() {\n\tregister(\"auth_aes128_md5\", newAuthAES128MD5, 9)\n}\n\nfunc newAuthAES128MD5(b *Base) Protocol {\n\ta := &authAES128{\n\t\tBase:               b,\n\t\tauthData:           &authData{},\n\t\tauthAES128Function: &authAES128Function{salt: \"auth_aes128_md5\", hmac: tools.HmacMD5, hashDigest: tools.MD5Sum},\n\t\tuserData:           &userData{},\n\t}\n\ta.initUserData()\n\treturn a\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/ssr/protocol/auth_aes128_sha1.go",
    "content": "package protocol\n\nimport (\n\t\"bytes\"\n\t\"crypto/rand\"\n\t\"encoding/binary\"\n\t\"math\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/pool\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/transport/ssr/tools\"\n\n\t\"github.com/metacubex/randv2\"\n)\n\ntype (\n\thmacMethod       func(key, data []byte) []byte\n\thashDigestMethod func([]byte) []byte\n)\n\nfunc init() {\n\tregister(\"auth_aes128_sha1\", newAuthAES128SHA1, 9)\n}\n\ntype authAES128Function struct {\n\tsalt       string\n\thmac       hmacMethod\n\thashDigest hashDigestMethod\n}\n\ntype authAES128 struct {\n\t*Base\n\t*authData\n\t*authAES128Function\n\t*userData\n\tiv            []byte\n\thasSentHeader bool\n\trawTrans      bool\n\tpackID        uint32\n\trecvID        uint32\n}\n\nfunc newAuthAES128SHA1(b *Base) Protocol {\n\ta := &authAES128{\n\t\tBase:               b,\n\t\tauthData:           &authData{},\n\t\tauthAES128Function: &authAES128Function{salt: \"auth_aes128_sha1\", hmac: tools.HmacSHA1, hashDigest: tools.SHA1Sum},\n\t\tuserData:           &userData{},\n\t}\n\ta.initUserData()\n\treturn a\n}\n\nfunc (a *authAES128) initUserData() {\n\tparams := strings.Split(a.Param, \":\")\n\tif len(params) > 1 {\n\t\tif userID, err := strconv.ParseUint(params[0], 10, 32); err == nil {\n\t\t\tbinary.LittleEndian.PutUint32(a.userID[:], uint32(userID))\n\t\t\ta.userKey = a.hashDigest([]byte(params[1]))\n\t\t} else {\n\t\t\tlog.Warnln(\"Wrong protocol-param for %s, only digits are expected before ':'\", a.salt)\n\t\t}\n\t}\n\tif len(a.userKey) == 0 {\n\t\ta.userKey = a.Key\n\t\trand.Read(a.userID[:])\n\t}\n}\n\nfunc (a *authAES128) StreamConn(c net.Conn, iv []byte) net.Conn {\n\tp := &authAES128{\n\t\tBase:               a.Base,\n\t\tauthData:           a.next(),\n\t\tauthAES128Function: a.authAES128Function,\n\t\tuserData:           a.userData,\n\t\tpackID:             1,\n\t\trecvID:             1,\n\t}\n\tp.iv = iv\n\treturn &Conn{Conn: c, Protocol: p}\n}\n\nfunc (a *authAES128) PacketConn(c N.EnhancePacketConn) N.EnhancePacketConn {\n\tp := &authAES128{\n\t\tBase:               a.Base,\n\t\tauthAES128Function: a.authAES128Function,\n\t\tuserData:           a.userData,\n\t}\n\treturn &PacketConn{EnhancePacketConn: c, Protocol: p}\n}\n\nfunc (a *authAES128) Decode(dst, src *bytes.Buffer) error {\n\tif a.rawTrans {\n\t\tdst.ReadFrom(src)\n\t\treturn nil\n\t}\n\tfor src.Len() > 4 {\n\t\tmacKey := pool.Get(len(a.userKey) + 4)\n\t\tdefer pool.Put(macKey)\n\t\tcopy(macKey, a.userKey)\n\t\tbinary.LittleEndian.PutUint32(macKey[len(a.userKey):], a.recvID)\n\t\tif !bytes.Equal(a.hmac(macKey, src.Bytes()[:2])[:2], src.Bytes()[2:4]) {\n\t\t\tsrc.Reset()\n\t\t\treturn errAuthAES128MACError\n\t\t}\n\n\t\tlength := int(binary.LittleEndian.Uint16(src.Bytes()[:2]))\n\t\tif length >= 8192 || length < 7 {\n\t\t\ta.rawTrans = true\n\t\t\tsrc.Reset()\n\t\t\treturn errAuthAES128LengthError\n\t\t}\n\t\tif length > src.Len() {\n\t\t\tbreak\n\t\t}\n\n\t\tif !bytes.Equal(a.hmac(macKey, src.Bytes()[:length-4])[:4], src.Bytes()[length-4:length]) {\n\t\t\ta.rawTrans = true\n\t\t\tsrc.Reset()\n\t\t\treturn errAuthAES128ChksumError\n\t\t}\n\n\t\ta.recvID++\n\n\t\tpos := int(src.Bytes()[4])\n\t\tif pos < 255 {\n\t\t\tpos += 4\n\t\t} else {\n\t\t\tpos = int(binary.LittleEndian.Uint16(src.Bytes()[5:7])) + 4\n\t\t}\n\t\tdst.Write(src.Bytes()[pos : length-4])\n\t\tsrc.Next(length)\n\t}\n\treturn nil\n}\n\nfunc (a *authAES128) Encode(buf *bytes.Buffer, b []byte) error {\n\tfullDataLength := len(b)\n\tif !a.hasSentHeader {\n\t\tdataLength := getDataLength(b)\n\t\ta.packAuthData(buf, b[:dataLength])\n\t\tb = b[dataLength:]\n\t\ta.hasSentHeader = true\n\t}\n\tfor len(b) > 8100 {\n\t\ta.packData(buf, b[:8100], fullDataLength)\n\t\tb = b[8100:]\n\t}\n\tif len(b) > 0 {\n\t\ta.packData(buf, b, fullDataLength)\n\t}\n\treturn nil\n}\n\nfunc (a *authAES128) DecodePacket(b []byte) ([]byte, error) {\n\tif len(b) < 4 {\n\t\treturn nil, errAuthAES128LengthError\n\t}\n\tif !bytes.Equal(a.hmac(a.Key, b[:len(b)-4])[:4], b[len(b)-4:]) {\n\t\treturn nil, errAuthAES128ChksumError\n\t}\n\treturn b[:len(b)-4], nil\n}\n\nfunc (a *authAES128) EncodePacket(buf *bytes.Buffer, b []byte) error {\n\tbuf.Write(b)\n\tbuf.Write(a.userID[:])\n\tbuf.Write(a.hmac(a.userKey, buf.Bytes())[:4])\n\treturn nil\n}\n\nfunc (a *authAES128) packData(poolBuf *bytes.Buffer, data []byte, fullDataLength int) {\n\tdataLength := len(data)\n\trandDataLength := a.getRandDataLengthForPackData(dataLength, fullDataLength)\n\t/*\n\t\t2:\tuint16 LittleEndian packedDataLength\n\t\t2:\thmac of packedDataLength\n\t\t3:\tmaxRandDataLengthPrefix (min:1)\n\t\t4:\thmac of packedData except the last 4 bytes\n\t*/\n\tpackedDataLength := 2 + 2 + 3 + randDataLength + dataLength + 4\n\tif randDataLength < 128 {\n\t\tpackedDataLength -= 2\n\t}\n\n\tmacKey := pool.Get(len(a.userKey) + 4)\n\tdefer pool.Put(macKey)\n\tcopy(macKey, a.userKey)\n\tbinary.LittleEndian.PutUint32(macKey[len(a.userKey):], a.packID)\n\ta.packID++\n\n\tbinary.Write(poolBuf, binary.LittleEndian, uint16(packedDataLength))\n\tpoolBuf.Write(a.hmac(macKey, poolBuf.Bytes()[poolBuf.Len()-2:])[:2])\n\ta.packRandData(poolBuf, randDataLength)\n\tpoolBuf.Write(data)\n\tpoolBuf.Write(a.hmac(macKey, poolBuf.Bytes()[poolBuf.Len()-packedDataLength+4:])[:4])\n}\n\nfunc trapezoidRandom(max int, d float64) int {\n\tbase := randv2.Float64()\n\tif d-0 > 1e-6 {\n\t\ta := 1 - d\n\t\tbase = (math.Sqrt(a*a+4*d*base) - a) / (2 * d)\n\t}\n\treturn int(base * float64(max))\n}\n\nfunc (a *authAES128) getRandDataLengthForPackData(dataLength, fullDataLength int) int {\n\tif fullDataLength >= 32*1024-a.Overhead {\n\t\treturn 0\n\t}\n\t// 1460: tcp_mss\n\trevLength := 1460 - dataLength - 9\n\tif revLength == 0 {\n\t\treturn 0\n\t}\n\tif revLength < 0 {\n\t\tif revLength > -1460 {\n\t\t\treturn trapezoidRandom(revLength+1460, -0.3)\n\t\t}\n\t\treturn randv2.IntN(32)\n\t}\n\tif dataLength > 900 {\n\t\treturn randv2.IntN(revLength)\n\t}\n\treturn trapezoidRandom(revLength, -0.3)\n}\n\nfunc (a *authAES128) packAuthData(poolBuf *bytes.Buffer, data []byte) {\n\tif len(data) == 0 {\n\t\treturn\n\t}\n\tdataLength := len(data)\n\trandDataLength := a.getRandDataLengthForPackAuthData(dataLength)\n\t/*\n\t\t7:\tcheckHead(1) and hmac of checkHead(6)\n\t\t4:\tuserID\n\t\t16:\tencrypted data of authdata(12), uint16 BigEndian packedDataLength(2) and uint16 BigEndian randDataLength(2)\n\t\t4:\thmac of userID and encrypted data\n\t\t4:\thmac of packedAuthData except the last 4 bytes\n\t*/\n\tpackedAuthDataLength := 7 + 4 + 16 + 4 + randDataLength + dataLength + 4\n\n\tmacKey := pool.Get(len(a.iv) + len(a.Key))\n\tdefer pool.Put(macKey)\n\tcopy(macKey, a.iv)\n\tcopy(macKey[len(a.iv):], a.Key)\n\n\tpoolBuf.WriteByte(byte(randv2.IntN(256)))\n\tpoolBuf.Write(a.hmac(macKey, poolBuf.Bytes())[:6])\n\tpoolBuf.Write(a.userID[:])\n\terr := a.authData.putEncryptedData(poolBuf, a.userKey, [2]int{packedAuthDataLength, randDataLength}, a.salt)\n\tif err != nil {\n\t\tpoolBuf.Reset()\n\t\treturn\n\t}\n\tpoolBuf.Write(a.hmac(macKey, poolBuf.Bytes()[7:])[:4])\n\ttools.AppendRandBytes(poolBuf, randDataLength)\n\tpoolBuf.Write(data)\n\tpoolBuf.Write(a.hmac(a.userKey, poolBuf.Bytes())[:4])\n}\n\nfunc (a *authAES128) getRandDataLengthForPackAuthData(size int) int {\n\tif size > 400 {\n\t\treturn randv2.IntN(512)\n\t}\n\treturn randv2.IntN(1024)\n}\n\nfunc (a *authAES128) packRandData(poolBuf *bytes.Buffer, size int) {\n\tif size < 128 {\n\t\tpoolBuf.WriteByte(byte(size + 1))\n\t\ttools.AppendRandBytes(poolBuf, size)\n\t\treturn\n\t}\n\tpoolBuf.WriteByte(255)\n\tbinary.Write(poolBuf, binary.LittleEndian, uint16(size+3))\n\ttools.AppendRandBytes(poolBuf, size)\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/ssr/protocol/auth_chain_a.go",
    "content": "package protocol\n\nimport (\n\t\"bytes\"\n\t\"crypto/cipher\"\n\t\"crypto/rand\"\n\t\"crypto/rc4\"\n\t\"encoding/base64\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/pool\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/transport/shadowsocks/core\"\n\t\"github.com/metacubex/mihomo/transport/ssr/tools\"\n)\n\nfunc init() {\n\tregister(\"auth_chain_a\", newAuthChainA, 4)\n}\n\ntype randDataLengthMethod func(int, []byte, *tools.XorShift128Plus) int\n\ntype authChainA struct {\n\t*Base\n\t*authData\n\t*userData\n\tiv             []byte\n\tsalt           string\n\thasSentHeader  bool\n\trawTrans       bool\n\tlastClientHash []byte\n\tlastServerHash []byte\n\tencrypter      cipher.Stream\n\tdecrypter      cipher.Stream\n\trandomClient   tools.XorShift128Plus\n\trandomServer   tools.XorShift128Plus\n\trandDataLength randDataLengthMethod\n\tpackID         uint32\n\trecvID         uint32\n}\n\nfunc newAuthChainA(b *Base) Protocol {\n\ta := &authChainA{\n\t\tBase:     b,\n\t\tauthData: &authData{},\n\t\tuserData: &userData{},\n\t\tsalt:     \"auth_chain_a\",\n\t}\n\ta.initUserData()\n\treturn a\n}\n\nfunc (a *authChainA) initUserData() {\n\tparams := strings.Split(a.Param, \":\")\n\tif len(params) > 1 {\n\t\tif userID, err := strconv.ParseUint(params[0], 10, 32); err == nil {\n\t\t\tbinary.LittleEndian.PutUint32(a.userID[:], uint32(userID))\n\t\t\ta.userKey = []byte(params[1])\n\t\t} else {\n\t\t\tlog.Warnln(\"Wrong protocol-param for %s, only digits are expected before ':'\", a.salt)\n\t\t}\n\t}\n\tif len(a.userKey) == 0 {\n\t\ta.userKey = a.Key\n\t\trand.Read(a.userID[:])\n\t}\n}\n\nfunc (a *authChainA) StreamConn(c net.Conn, iv []byte) net.Conn {\n\tp := &authChainA{\n\t\tBase:     a.Base,\n\t\tauthData: a.next(),\n\t\tuserData: a.userData,\n\t\tsalt:     a.salt,\n\t\tpackID:   1,\n\t\trecvID:   1,\n\t}\n\tp.iv = iv\n\tp.randDataLength = p.getRandLength\n\treturn &Conn{Conn: c, Protocol: p}\n}\n\nfunc (a *authChainA) PacketConn(c N.EnhancePacketConn) N.EnhancePacketConn {\n\tp := &authChainA{\n\t\tBase:     a.Base,\n\t\tsalt:     a.salt,\n\t\tuserData: a.userData,\n\t}\n\treturn &PacketConn{EnhancePacketConn: c, Protocol: p}\n}\n\nfunc (a *authChainA) Decode(dst, src *bytes.Buffer) error {\n\tif a.rawTrans {\n\t\tdst.ReadFrom(src)\n\t\treturn nil\n\t}\n\tfor src.Len() > 4 {\n\t\tmacKey := pool.Get(len(a.userKey) + 4)\n\t\tdefer pool.Put(macKey)\n\t\tcopy(macKey, a.userKey)\n\t\tbinary.LittleEndian.PutUint32(macKey[len(a.userKey):], a.recvID)\n\n\t\tdataLength := int(binary.LittleEndian.Uint16(src.Bytes()[:2]) ^ binary.LittleEndian.Uint16(a.lastServerHash[14:16]))\n\t\trandDataLength := a.randDataLength(dataLength, a.lastServerHash, &a.randomServer)\n\t\tlength := dataLength + randDataLength\n\t\t// Temporary workaround for https://github.com/metacubex/mihomo/issues/1352\n\t\tif dataLength < 0 || randDataLength < 0 || length < 0 {\n\t\t\treturn errors.New(\"ssr crashing blocked\")\n\t\t}\n\n\t\tif length >= 4096 {\n\t\t\ta.rawTrans = true\n\t\t\tsrc.Reset()\n\t\t\treturn errAuthChainLengthError\n\t\t}\n\n\t\tif 4+length > src.Len() {\n\t\t\tbreak\n\t\t}\n\n\t\tserverHash := tools.HmacMD5(macKey, src.Bytes()[:length+2])\n\t\tif !bytes.Equal(serverHash[:2], src.Bytes()[length+2:length+4]) {\n\t\t\ta.rawTrans = true\n\t\t\tsrc.Reset()\n\t\t\treturn errAuthChainChksumError\n\t\t}\n\t\ta.lastServerHash = serverHash\n\n\t\tpos := 2\n\t\tif dataLength > 0 && randDataLength > 0 {\n\t\t\tpos += getRandStartPos(randDataLength, &a.randomServer)\n\t\t}\n\t\t// Temporary workaround for https://github.com/metacubex/mihomo/issues/1352\n\t\tif pos < 0 || pos+dataLength < 0 || dataLength < 0 {\n\t\t\treturn errors.New(\"ssr crashing blocked\")\n\t\t}\n\n\t\twantedData := src.Bytes()[pos : pos+dataLength]\n\t\ta.decrypter.XORKeyStream(wantedData, wantedData)\n\t\tif a.recvID == 1 {\n\t\t\tdst.Write(wantedData[2:])\n\t\t} else {\n\t\t\tdst.Write(wantedData)\n\t\t}\n\t\ta.recvID++\n\t\tsrc.Next(length + 4)\n\t}\n\treturn nil\n}\n\nfunc (a *authChainA) Encode(buf *bytes.Buffer, b []byte) error {\n\tif !a.hasSentHeader {\n\t\tdataLength := getDataLength(b)\n\t\ta.packAuthData(buf, b[:dataLength])\n\t\tb = b[dataLength:]\n\t\ta.hasSentHeader = true\n\t}\n\tfor len(b) > 2800 {\n\t\ta.packData(buf, b[:2800])\n\t\tb = b[2800:]\n\t}\n\tif len(b) > 0 {\n\t\ta.packData(buf, b)\n\t}\n\treturn nil\n}\n\nfunc (a *authChainA) DecodePacket(b []byte) ([]byte, error) {\n\tif len(b) < 9 {\n\t\treturn nil, errAuthChainLengthError\n\t}\n\tif !bytes.Equal(tools.HmacMD5(a.userKey, b[:len(b)-1])[:1], b[len(b)-1:]) {\n\t\treturn nil, errAuthChainChksumError\n\t}\n\tmd5Data := tools.HmacMD5(a.Key, b[len(b)-8:len(b)-1])\n\n\trandDataLength := udpGetRandLength(md5Data, &a.randomServer)\n\n\tkey := core.Kdf(base64.StdEncoding.EncodeToString(a.userKey)+base64.StdEncoding.EncodeToString(md5Data), 16)\n\trc4Cipher, err := rc4.NewCipher(key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\twantedData := b[:len(b)-8-randDataLength]\n\trc4Cipher.XORKeyStream(wantedData, wantedData)\n\treturn wantedData, nil\n}\n\nfunc (a *authChainA) EncodePacket(buf *bytes.Buffer, b []byte) error {\n\tauthData := pool.Get(3)\n\tdefer pool.Put(authData)\n\trand.Read(authData)\n\n\tmd5Data := tools.HmacMD5(a.Key, authData)\n\n\trandDataLength := udpGetRandLength(md5Data, &a.randomClient)\n\n\tkey := core.Kdf(base64.StdEncoding.EncodeToString(a.userKey)+base64.StdEncoding.EncodeToString(md5Data), 16)\n\trc4Cipher, err := rc4.NewCipher(key)\n\tif err != nil {\n\t\treturn err\n\t}\n\trc4Cipher.XORKeyStream(b, b)\n\n\tbuf.Write(b)\n\ttools.AppendRandBytes(buf, randDataLength)\n\tbuf.Write(authData)\n\tbinary.Write(buf, binary.LittleEndian, binary.LittleEndian.Uint32(a.userID[:])^binary.LittleEndian.Uint32(md5Data[:4]))\n\tbuf.Write(tools.HmacMD5(a.userKey, buf.Bytes())[:1])\n\treturn nil\n}\n\nfunc (a *authChainA) packAuthData(poolBuf *bytes.Buffer, data []byte) {\n\t/*\n\t\tdataLength := len(data)\n\t\t12:\tcheckHead(4) and hmac of checkHead(8)\n\t\t4:\tuint32 LittleEndian uid (uid = userID ^ last client hash)\n\t\t16:\tencrypted data of authdata(12), uint16 LittleEndian overhead(2) and uint16 LittleEndian number zero(2)\n\t\t4:\tlast server hash(4)\n\t\tpackedAuthDataLength := 12 + 4 + 16 + 4 + dataLength\n\t*/\n\n\tmacKey := pool.Get(len(a.iv) + len(a.Key))\n\tdefer pool.Put(macKey)\n\tcopy(macKey, a.iv)\n\tcopy(macKey[len(a.iv):], a.Key)\n\n\t// check head\n\ttools.AppendRandBytes(poolBuf, 4)\n\ta.lastClientHash = tools.HmacMD5(macKey, poolBuf.Bytes())\n\ta.initRC4Cipher()\n\tpoolBuf.Write(a.lastClientHash[:8])\n\t// uid\n\tbinary.Write(poolBuf, binary.LittleEndian, binary.LittleEndian.Uint32(a.userID[:])^binary.LittleEndian.Uint32(a.lastClientHash[8:12]))\n\t// encrypted data\n\terr := a.putEncryptedData(poolBuf, a.userKey, [2]int{a.Overhead, 0}, a.salt)\n\tif err != nil {\n\t\tpoolBuf.Reset()\n\t\treturn\n\t}\n\t// last server hash\n\ta.lastServerHash = tools.HmacMD5(a.userKey, poolBuf.Bytes()[12:])\n\tpoolBuf.Write(a.lastServerHash[:4])\n\t// packed data\n\ta.packData(poolBuf, data)\n}\n\nfunc (a *authChainA) packData(poolBuf *bytes.Buffer, data []byte) {\n\ta.encrypter.XORKeyStream(data, data)\n\n\tmacKey := pool.Get(len(a.userKey) + 4)\n\tdefer pool.Put(macKey)\n\tcopy(macKey, a.userKey)\n\tbinary.LittleEndian.PutUint32(macKey[len(a.userKey):], a.packID)\n\ta.packID++\n\n\tlength := uint16(len(data)) ^ binary.LittleEndian.Uint16(a.lastClientHash[14:16])\n\n\toriginalLength := poolBuf.Len()\n\tbinary.Write(poolBuf, binary.LittleEndian, length)\n\ta.putMixedRandDataAndData(poolBuf, data)\n\ta.lastClientHash = tools.HmacMD5(macKey, poolBuf.Bytes()[originalLength:])\n\tpoolBuf.Write(a.lastClientHash[:2])\n}\n\nfunc (a *authChainA) putMixedRandDataAndData(poolBuf *bytes.Buffer, data []byte) {\n\trandDataLength := a.randDataLength(len(data), a.lastClientHash, &a.randomClient)\n\tif len(data) == 0 {\n\t\ttools.AppendRandBytes(poolBuf, randDataLength)\n\t\treturn\n\t}\n\tif randDataLength > 0 {\n\t\tstartPos := getRandStartPos(randDataLength, &a.randomClient)\n\t\ttools.AppendRandBytes(poolBuf, startPos)\n\t\tpoolBuf.Write(data)\n\t\ttools.AppendRandBytes(poolBuf, randDataLength-startPos)\n\t\treturn\n\t}\n\tpoolBuf.Write(data)\n}\n\nfunc getRandStartPos(length int, random *tools.XorShift128Plus) int {\n\tif length == 0 {\n\t\treturn 0\n\t}\n\treturn int(int64(random.Next()%8589934609) % int64(length))\n}\n\nfunc (a *authChainA) getRandLength(length int, lastHash []byte, random *tools.XorShift128Plus) int {\n\tif length > 1440 {\n\t\treturn 0\n\t}\n\trandom.InitFromBinAndLength(lastHash, length)\n\tif length > 1300 {\n\t\treturn int(random.Next() % 31)\n\t}\n\tif length > 900 {\n\t\treturn int(random.Next() % 127)\n\t}\n\tif length > 400 {\n\t\treturn int(random.Next() % 521)\n\t}\n\treturn int(random.Next() % 1021)\n}\n\nfunc (a *authChainA) initRC4Cipher() {\n\tkey := core.Kdf(base64.StdEncoding.EncodeToString(a.userKey)+base64.StdEncoding.EncodeToString(a.lastClientHash), 16)\n\ta.encrypter, _ = rc4.NewCipher(key)\n\ta.decrypter, _ = rc4.NewCipher(key)\n}\n\nfunc udpGetRandLength(lastHash []byte, random *tools.XorShift128Plus) int {\n\trandom.InitFromBin(lastHash)\n\treturn int(random.Next() % 127)\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/ssr/protocol/auth_chain_b.go",
    "content": "package protocol\n\nimport (\n\t\"net\"\n\t\"sort\"\n\n\t\"github.com/metacubex/mihomo/transport/ssr/tools\"\n)\n\nfunc init() {\n\tregister(\"auth_chain_b\", newAuthChainB, 4)\n}\n\ntype authChainB struct {\n\t*authChainA\n\tdataSizeList  []int\n\tdataSizeList2 []int\n}\n\nfunc newAuthChainB(b *Base) Protocol {\n\ta := &authChainB{\n\t\tauthChainA: &authChainA{\n\t\t\tBase:     b,\n\t\t\tauthData: &authData{},\n\t\t\tuserData: &userData{},\n\t\t\tsalt:     \"auth_chain_b\",\n\t\t},\n\t}\n\ta.initUserData()\n\treturn a\n}\n\nfunc (a *authChainB) StreamConn(c net.Conn, iv []byte) net.Conn {\n\tp := &authChainB{\n\t\tauthChainA: &authChainA{\n\t\t\tBase:     a.Base,\n\t\t\tauthData: a.next(),\n\t\t\tuserData: a.userData,\n\t\t\tsalt:     a.salt,\n\t\t\tpackID:   1,\n\t\t\trecvID:   1,\n\t\t},\n\t}\n\tp.iv = iv\n\tp.randDataLength = p.getRandLength\n\tp.initDataSize()\n\treturn &Conn{Conn: c, Protocol: p}\n}\n\nfunc (a *authChainB) initDataSize() {\n\ta.dataSizeList = a.dataSizeList[:0]\n\ta.dataSizeList2 = a.dataSizeList2[:0]\n\n\ta.randomServer.InitFromBin(a.Key)\n\tlength := a.randomServer.Next()%8 + 4\n\tfor ; length > 0; length-- {\n\t\ta.dataSizeList = append(a.dataSizeList, int(a.randomServer.Next()%2340%2040%1440))\n\t}\n\tsort.Ints(a.dataSizeList)\n\n\tlength = a.randomServer.Next()%16 + 8\n\tfor ; length > 0; length-- {\n\t\ta.dataSizeList2 = append(a.dataSizeList2, int(a.randomServer.Next()%2340%2040%1440))\n\t}\n\tsort.Ints(a.dataSizeList2)\n}\n\nfunc (a *authChainB) getRandLength(length int, lashHash []byte, random *tools.XorShift128Plus) int {\n\tif length >= 1440 {\n\t\treturn 0\n\t}\n\trandom.InitFromBinAndLength(lashHash, length)\n\tpos := sort.Search(len(a.dataSizeList), func(i int) bool { return a.dataSizeList[i] >= length+a.Overhead })\n\tfinalPos := pos + int(random.Next()%uint64(len(a.dataSizeList)))\n\tif finalPos < len(a.dataSizeList) {\n\t\treturn a.dataSizeList[finalPos] - length - a.Overhead\n\t}\n\n\tpos = sort.Search(len(a.dataSizeList2), func(i int) bool { return a.dataSizeList2[i] >= length+a.Overhead })\n\tfinalPos = pos + int(random.Next()%uint64(len(a.dataSizeList2)))\n\tif finalPos < len(a.dataSizeList2) {\n\t\treturn a.dataSizeList2[finalPos] - length - a.Overhead\n\t}\n\tif finalPos < pos+len(a.dataSizeList2)-1 {\n\t\treturn 0\n\t}\n\tif length > 1300 {\n\t\treturn int(random.Next() % 31)\n\t}\n\tif length > 900 {\n\t\treturn int(random.Next() % 127)\n\t}\n\tif length > 400 {\n\t\treturn int(random.Next() % 521)\n\t}\n\treturn int(random.Next() % 1021)\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/ssr/protocol/auth_sha1_v4.go",
    "content": "package protocol\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"hash/adler32\"\n\t\"hash/crc32\"\n\t\"net\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/pool\"\n\t\"github.com/metacubex/mihomo/transport/ssr/tools\"\n\n\t\"github.com/metacubex/randv2\"\n)\n\nfunc init() {\n\tregister(\"auth_sha1_v4\", newAuthSHA1V4, 7)\n}\n\ntype authSHA1V4 struct {\n\t*Base\n\t*authData\n\tiv            []byte\n\thasSentHeader bool\n\trawTrans      bool\n}\n\nfunc newAuthSHA1V4(b *Base) Protocol {\n\treturn &authSHA1V4{Base: b, authData: &authData{}}\n}\n\nfunc (a *authSHA1V4) StreamConn(c net.Conn, iv []byte) net.Conn {\n\tp := &authSHA1V4{Base: a.Base, authData: a.next()}\n\tp.iv = iv\n\treturn &Conn{Conn: c, Protocol: p}\n}\n\nfunc (a *authSHA1V4) PacketConn(c N.EnhancePacketConn) N.EnhancePacketConn {\n\treturn c\n}\n\nfunc (a *authSHA1V4) Decode(dst, src *bytes.Buffer) error {\n\tif a.rawTrans {\n\t\tdst.ReadFrom(src)\n\t\treturn nil\n\t}\n\tfor src.Len() > 4 {\n\t\tif uint16(crc32.ChecksumIEEE(src.Bytes()[:2])&0xffff) != binary.LittleEndian.Uint16(src.Bytes()[2:4]) {\n\t\t\tsrc.Reset()\n\t\t\treturn errAuthSHA1V4CRC32Error\n\t\t}\n\n\t\tlength := int(binary.BigEndian.Uint16(src.Bytes()[:2]))\n\t\tif length >= 8192 || length < 7 {\n\t\t\ta.rawTrans = true\n\t\t\tsrc.Reset()\n\t\t\treturn errAuthSHA1V4LengthError\n\t\t}\n\t\tif length > src.Len() {\n\t\t\tbreak\n\t\t}\n\n\t\tif adler32.Checksum(src.Bytes()[:length-4]) != binary.LittleEndian.Uint32(src.Bytes()[length-4:length]) {\n\t\t\ta.rawTrans = true\n\t\t\tsrc.Reset()\n\t\t\treturn errAuthSHA1V4Adler32Error\n\t\t}\n\n\t\tpos := int(src.Bytes()[4])\n\t\tif pos < 255 {\n\t\t\tpos += 4\n\t\t} else {\n\t\t\tpos = int(binary.BigEndian.Uint16(src.Bytes()[5:7])) + 4\n\t\t}\n\t\tdst.Write(src.Bytes()[pos : length-4])\n\t\tsrc.Next(length)\n\t}\n\treturn nil\n}\n\nfunc (a *authSHA1V4) Encode(buf *bytes.Buffer, b []byte) error {\n\tif !a.hasSentHeader {\n\t\tdataLength := getDataLength(b)\n\n\t\ta.packAuthData(buf, b[:dataLength])\n\t\tb = b[dataLength:]\n\n\t\ta.hasSentHeader = true\n\t}\n\tfor len(b) > 8100 {\n\t\ta.packData(buf, b[:8100])\n\t\tb = b[8100:]\n\t}\n\tif len(b) > 0 {\n\t\ta.packData(buf, b)\n\t}\n\n\treturn nil\n}\n\nfunc (a *authSHA1V4) DecodePacket(b []byte) ([]byte, error) { return b, nil }\n\nfunc (a *authSHA1V4) EncodePacket(buf *bytes.Buffer, b []byte) error {\n\tbuf.Write(b)\n\treturn nil\n}\n\nfunc (a *authSHA1V4) packData(poolBuf *bytes.Buffer, data []byte) {\n\tdataLength := len(data)\n\trandDataLength := a.getRandDataLength(dataLength)\n\t/*\n\t\t2:\tuint16 BigEndian packedDataLength\n\t\t2:\tuint16 LittleEndian crc32Data & 0xffff\n\t\t3:\tmaxRandDataLengthPrefix (min:1)\n\t\t4:\tadler32Data\n\t*/\n\tpackedDataLength := 2 + 2 + 3 + randDataLength + dataLength + 4\n\tif randDataLength < 128 {\n\t\tpackedDataLength -= 2\n\t}\n\n\tbinary.Write(poolBuf, binary.BigEndian, uint16(packedDataLength))\n\tbinary.Write(poolBuf, binary.LittleEndian, uint16(crc32.ChecksumIEEE(poolBuf.Bytes()[poolBuf.Len()-2:])&0xffff))\n\ta.packRandData(poolBuf, randDataLength)\n\tpoolBuf.Write(data)\n\tbinary.Write(poolBuf, binary.LittleEndian, adler32.Checksum(poolBuf.Bytes()[poolBuf.Len()-packedDataLength+4:]))\n}\n\nfunc (a *authSHA1V4) packAuthData(poolBuf *bytes.Buffer, data []byte) {\n\tdataLength := len(data)\n\trandDataLength := a.getRandDataLength(12 + dataLength)\n\t/*\n\t\t2:\tuint16 BigEndian packedAuthDataLength\n\t\t4:\tuint32 LittleEndian crc32Data\n\t\t3:\tmaxRandDataLengthPrefix (min: 1)\n\t\t12:\tauthDataLength\n\t\t10:\thmacSHA1DataLength\n\t*/\n\tpackedAuthDataLength := 2 + 4 + 3 + randDataLength + 12 + dataLength + 10\n\tif randDataLength < 128 {\n\t\tpackedAuthDataLength -= 2\n\t}\n\n\tsalt := []byte(\"auth_sha1_v4\")\n\tcrcData := pool.Get(len(salt) + len(a.Key) + 2)\n\tdefer pool.Put(crcData)\n\tbinary.BigEndian.PutUint16(crcData, uint16(packedAuthDataLength))\n\tcopy(crcData[2:], salt)\n\tcopy(crcData[2+len(salt):], a.Key)\n\n\tkey := pool.Get(len(a.iv) + len(a.Key))\n\tdefer pool.Put(key)\n\tcopy(key, a.iv)\n\tcopy(key[len(a.iv):], a.Key)\n\n\tpoolBuf.Write(crcData[:2])\n\tbinary.Write(poolBuf, binary.LittleEndian, crc32.ChecksumIEEE(crcData))\n\ta.packRandData(poolBuf, randDataLength)\n\ta.putAuthData(poolBuf)\n\tpoolBuf.Write(data)\n\tpoolBuf.Write(tools.HmacSHA1(key, poolBuf.Bytes()[poolBuf.Len()-packedAuthDataLength+10:])[:10])\n}\n\nfunc (a *authSHA1V4) packRandData(poolBuf *bytes.Buffer, size int) {\n\tif size < 128 {\n\t\tpoolBuf.WriteByte(byte(size + 1))\n\t\ttools.AppendRandBytes(poolBuf, size)\n\t\treturn\n\t}\n\tpoolBuf.WriteByte(255)\n\tbinary.Write(poolBuf, binary.BigEndian, uint16(size+3))\n\ttools.AppendRandBytes(poolBuf, size)\n}\n\nfunc (a *authSHA1V4) getRandDataLength(size int) int {\n\tif size > 1200 {\n\t\treturn 0\n\t}\n\tif size > 400 {\n\t\treturn randv2.IntN(256)\n\t}\n\treturn randv2.IntN(512)\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/ssr/protocol/base.go",
    "content": "package protocol\n\nimport (\n\t\"bytes\"\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\t\"crypto/rand\"\n\t\"encoding/base64\"\n\t\"encoding/binary\"\n\t\"sync\"\n\n\t\"github.com/metacubex/mihomo/common/pool\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/ntp\"\n\t\"github.com/metacubex/mihomo/transport/shadowsocks/core\"\n\n\t\"github.com/metacubex/randv2\"\n)\n\ntype Base struct {\n\tKey      []byte\n\tOverhead int\n\tParam    string\n}\n\ntype userData struct {\n\tuserKey []byte\n\tuserID  [4]byte\n}\n\ntype authData struct {\n\tclientID     [4]byte\n\tconnectionID uint32\n\tmutex        sync.Mutex\n}\n\nfunc (a *authData) next() *authData {\n\tr := &authData{}\n\ta.mutex.Lock()\n\tdefer a.mutex.Unlock()\n\tif a.connectionID > 0xff000000 || a.connectionID == 0 {\n\t\trand.Read(a.clientID[:])\n\t\ta.connectionID = randv2.Uint32() & 0xffffff\n\t}\n\ta.connectionID++\n\tcopy(r.clientID[:], a.clientID[:])\n\tr.connectionID = a.connectionID\n\treturn r\n}\n\nfunc (a *authData) putAuthData(buf *bytes.Buffer) {\n\tbinary.Write(buf, binary.LittleEndian, uint32(ntp.Now().Unix()))\n\tbuf.Write(a.clientID[:])\n\tbinary.Write(buf, binary.LittleEndian, a.connectionID)\n}\n\nfunc (a *authData) putEncryptedData(b *bytes.Buffer, userKey []byte, paddings [2]int, salt string) error {\n\tencrypt := pool.Get(16)\n\tdefer pool.Put(encrypt)\n\tbinary.LittleEndian.PutUint32(encrypt, uint32(ntp.Now().Unix()))\n\tcopy(encrypt[4:], a.clientID[:])\n\tbinary.LittleEndian.PutUint32(encrypt[8:], a.connectionID)\n\tbinary.LittleEndian.PutUint16(encrypt[12:], uint16(paddings[0]))\n\tbinary.LittleEndian.PutUint16(encrypt[14:], uint16(paddings[1]))\n\n\tcipherKey := core.Kdf(base64.StdEncoding.EncodeToString(userKey)+salt, 16)\n\tblock, err := aes.NewCipher(cipherKey)\n\tif err != nil {\n\t\tlog.Warnln(\"New cipher error: %s\", err.Error())\n\t\treturn err\n\t}\n\tiv := bytes.Repeat([]byte{0}, 16)\n\tcbcCipher := cipher.NewCBCEncrypter(block, iv)\n\n\tcbcCipher.CryptBlocks(encrypt, encrypt)\n\n\tb.Write(encrypt)\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/ssr/protocol/origin.go",
    "content": "package protocol\n\nimport (\n\t\"bytes\"\n\t\"net\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n)\n\ntype origin struct{}\n\nfunc init() { register(\"origin\", newOrigin, 0) }\n\nfunc newOrigin(b *Base) Protocol { return &origin{} }\n\nfunc (o *origin) StreamConn(c net.Conn, iv []byte) net.Conn { return c }\n\nfunc (o *origin) PacketConn(c N.EnhancePacketConn) N.EnhancePacketConn { return c }\n\nfunc (o *origin) Decode(dst, src *bytes.Buffer) error {\n\tdst.ReadFrom(src)\n\treturn nil\n}\n\nfunc (o *origin) Encode(buf *bytes.Buffer, b []byte) error {\n\tbuf.Write(b)\n\treturn nil\n}\n\nfunc (o *origin) DecodePacket(b []byte) ([]byte, error) { return b, nil }\n\nfunc (o *origin) EncodePacket(buf *bytes.Buffer, b []byte) error {\n\tbuf.Write(b)\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/ssr/protocol/packet.go",
    "content": "package protocol\n\nimport (\n\t\"net\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/pool\"\n)\n\ntype PacketConn struct {\n\tN.EnhancePacketConn\n\tProtocol\n}\n\nfunc (c *PacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {\n\tbuf := pool.GetBuffer()\n\tdefer pool.PutBuffer(buf)\n\terr := c.EncodePacket(buf, b)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\t_, err = c.EnhancePacketConn.WriteTo(buf.Bytes(), addr)\n\treturn len(b), err\n}\n\nfunc (c *PacketConn) ReadFrom(b []byte) (int, net.Addr, error) {\n\tn, addr, err := c.EnhancePacketConn.ReadFrom(b)\n\tif err != nil {\n\t\treturn n, addr, err\n\t}\n\tdecoded, err := c.DecodePacket(b[:n])\n\tif err != nil {\n\t\treturn n, addr, err\n\t}\n\tcopy(b, decoded)\n\treturn len(decoded), addr, nil\n}\n\nfunc (c *PacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {\n\tdata, put, addr, err = c.EnhancePacketConn.WaitReadFrom()\n\tif err != nil {\n\t\treturn\n\t}\n\tdata, err = c.DecodePacket(data)\n\tif err != nil {\n\t\tif put != nil {\n\t\t\tput()\n\t\t}\n\t\tdata = nil\n\t\tput = nil\n\t\treturn\n\t}\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/ssr/protocol/protocol.go",
    "content": "package protocol\n\nimport (\n\t\"bytes\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\n\t\"github.com/metacubex/randv2\"\n)\n\nvar (\n\terrAuthSHA1V4CRC32Error   = errors.New(\"auth_sha1_v4 decode data wrong crc32\")\n\terrAuthSHA1V4LengthError  = errors.New(\"auth_sha1_v4 decode data wrong length\")\n\terrAuthSHA1V4Adler32Error = errors.New(\"auth_sha1_v4 decode data wrong adler32\")\n\terrAuthAES128MACError     = errors.New(\"auth_aes128 decode data wrong mac\")\n\terrAuthAES128LengthError  = errors.New(\"auth_aes128 decode data wrong length\")\n\terrAuthAES128ChksumError  = errors.New(\"auth_aes128 decode data wrong checksum\")\n\terrAuthChainLengthError   = errors.New(\"auth_chain decode data wrong length\")\n\terrAuthChainChksumError   = errors.New(\"auth_chain decode data wrong checksum\")\n)\n\ntype Protocol interface {\n\tStreamConn(net.Conn, []byte) net.Conn\n\tPacketConn(N.EnhancePacketConn) N.EnhancePacketConn\n\tDecode(dst, src *bytes.Buffer) error\n\tEncode(buf *bytes.Buffer, b []byte) error\n\tDecodePacket([]byte) ([]byte, error)\n\tEncodePacket(buf *bytes.Buffer, b []byte) error\n}\n\ntype protocolCreator func(b *Base) Protocol\n\nvar protocolList = make(map[string]struct {\n\toverhead int\n\tnew      protocolCreator\n})\n\nfunc register(name string, c protocolCreator, o int) {\n\tprotocolList[name] = struct {\n\t\toverhead int\n\t\tnew      protocolCreator\n\t}{overhead: o, new: c}\n}\n\nfunc PickProtocol(name string, b *Base) (Protocol, error) {\n\tif choice, ok := protocolList[name]; ok {\n\t\tb.Overhead += choice.overhead\n\t\treturn choice.new(b), nil\n\t}\n\treturn nil, fmt.Errorf(\"protocol %s not supported\", name)\n}\n\nfunc getHeadSize(b []byte, defaultValue int) int {\n\tif len(b) < 2 {\n\t\treturn defaultValue\n\t}\n\theadType := b[0] & 7\n\tswitch headType {\n\tcase 1:\n\t\treturn 7\n\tcase 4:\n\t\treturn 19\n\tcase 3:\n\t\treturn 4 + int(b[1])\n\t}\n\treturn defaultValue\n}\n\nfunc getDataLength(b []byte) int {\n\tbLength := len(b)\n\tdataLength := getHeadSize(b, 30) + randv2.IntN(32)\n\tif bLength < dataLength {\n\t\treturn bLength\n\t}\n\treturn dataLength\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/ssr/protocol/stream.go",
    "content": "package protocol\n\nimport (\n\t\"bytes\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/common/pool\"\n)\n\ntype Conn struct {\n\tnet.Conn\n\tProtocol\n\tdecoded      bytes.Buffer\n\tunderDecoded bytes.Buffer\n}\n\nfunc (c *Conn) Read(b []byte) (int, error) {\n\tif c.decoded.Len() > 0 {\n\t\treturn c.decoded.Read(b)\n\t}\n\n\tbuf := pool.Get(pool.RelayBufferSize)\n\tdefer pool.Put(buf)\n\tn, err := c.Conn.Read(buf)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tc.underDecoded.Write(buf[:n])\n\terr = c.Decode(&c.decoded, &c.underDecoded)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tn, _ = c.decoded.Read(b)\n\treturn n, nil\n}\n\nfunc (c *Conn) Write(b []byte) (int, error) {\n\tbLength := len(b)\n\tbuf := pool.GetBuffer()\n\tdefer pool.PutBuffer(buf)\n\terr := c.Encode(buf, b)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\t_, err = c.Conn.Write(buf.Bytes())\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn bLength, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/ssr/tools/bufPool.go",
    "content": "package tools\n\nimport (\n\t\"bytes\"\n\t\"crypto/rand\"\n\t\"io\"\n)\n\nfunc AppendRandBytes(b *bytes.Buffer, length int) {\n\tb.ReadFrom(io.LimitReader(rand.Reader, int64(length)))\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/ssr/tools/crypto.go",
    "content": "package tools\n\nimport (\n\t\"crypto/hmac\"\n\t\"crypto/md5\"\n\t\"crypto/sha1\"\n)\n\nconst HmacSHA1Len = 10\n\nfunc HmacMD5(key, data []byte) []byte {\n\thmacMD5 := hmac.New(md5.New, key)\n\thmacMD5.Write(data)\n\treturn hmacMD5.Sum(nil)\n}\n\nfunc HmacSHA1(key, data []byte) []byte {\n\thmacSHA1 := hmac.New(sha1.New, key)\n\thmacSHA1.Write(data)\n\treturn hmacSHA1.Sum(nil)\n}\n\nfunc MD5Sum(b []byte) []byte {\n\th := md5.New()\n\th.Write(b)\n\treturn h.Sum(nil)\n}\n\nfunc SHA1Sum(b []byte) []byte {\n\th := sha1.New()\n\th.Write(b)\n\treturn h.Sum(nil)\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/ssr/tools/random.go",
    "content": "package tools\n\nimport (\n\t\"encoding/binary\"\n\n\t\"github.com/metacubex/mihomo/common/pool\"\n)\n\n// XorShift128Plus - a pseudorandom number generator\ntype XorShift128Plus struct {\n\ts [2]uint64\n}\n\nfunc (r *XorShift128Plus) Next() uint64 {\n\tx := r.s[0]\n\ty := r.s[1]\n\tr.s[0] = y\n\tx ^= x << 23\n\tx ^= y ^ (x >> 17) ^ (y >> 26)\n\tr.s[1] = x\n\treturn x + y\n}\n\nfunc (r *XorShift128Plus) InitFromBin(bin []byte) {\n\tvar full []byte\n\tif len(bin) < 16 {\n\t\tfull := pool.Get(16)[:0]\n\t\tdefer pool.Put(full)\n\t\tfull = append(full, bin...)\n\t\tfor len(full) < 16 {\n\t\t\tfull = append(full, 0)\n\t\t}\n\t} else {\n\t\tfull = bin\n\t}\n\tr.s[0] = binary.LittleEndian.Uint64(full[:8])\n\tr.s[1] = binary.LittleEndian.Uint64(full[8:16])\n}\n\nfunc (r *XorShift128Plus) InitFromBinAndLength(bin []byte, length int) {\n\tvar full []byte\n\tif len(bin) < 16 {\n\t\tfull := pool.Get(16)[:0]\n\t\tdefer pool.Put(full)\n\t\tfull = append(full, bin...)\n\t\tfor len(full) < 16 {\n\t\t\tfull = append(full, 0)\n\t\t}\n\t}\n\tfull = bin\n\tbinary.LittleEndian.PutUint16(full, uint16(length))\n\tr.s[0] = binary.LittleEndian.Uint64(full[:8])\n\tr.s[1] = binary.LittleEndian.Uint64(full[8:16])\n\tfor i := 0; i < 4; i++ {\n\t\tr.Next()\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/address.go",
    "content": "package sudoku\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n)\n\nfunc EncodeAddress(rawAddr string) ([]byte, error) {\n\thost, portStr, err := net.SplitHostPort(rawAddr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tportInt, err := strconv.ParseUint(portStr, 10, 16)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar buf []byte\n\tif i := strings.IndexByte(host, '%'); i >= 0 {\n\t\t// Zone identifiers are not representable in SOCKS5 IPv6 address encoding.\n\t\thost = host[:i]\n\t}\n\tif ip := net.ParseIP(host); ip != nil {\n\t\tif ip4 := ip.To4(); ip4 != nil {\n\t\t\tbuf = append(buf, 0x01) // IPv4\n\t\t\tbuf = append(buf, ip4...)\n\t\t} else {\n\t\t\tbuf = append(buf, 0x04) // IPv6\n\t\t\tip16 := ip.To16()\n\t\t\tif ip16 == nil {\n\t\t\t\treturn nil, fmt.Errorf(\"invalid ipv6: %q\", host)\n\t\t\t}\n\t\t\tbuf = append(buf, ip16...)\n\t\t}\n\t} else {\n\t\tif len(host) > 255 {\n\t\t\treturn nil, fmt.Errorf(\"domain too long\")\n\t\t}\n\t\tbuf = append(buf, 0x03) // domain\n\t\tbuf = append(buf, byte(len(host)))\n\t\tbuf = append(buf, host...)\n\t}\n\n\tvar portBytes [2]byte\n\tbinary.BigEndian.PutUint16(portBytes[:], uint16(portInt))\n\tbuf = append(buf, portBytes[:]...)\n\treturn buf, nil\n}\n\nfunc DecodeAddress(r io.Reader) (string, error) {\n\tvar atyp [1]byte\n\tif _, err := io.ReadFull(r, atyp[:]); err != nil {\n\t\treturn \"\", err\n\t}\n\n\tswitch atyp[0] {\n\tcase 0x01: // IPv4\n\t\tvar ipBuf [net.IPv4len]byte\n\t\tif _, err := io.ReadFull(r, ipBuf[:]); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tvar portBuf [2]byte\n\t\tif _, err := io.ReadFull(r, portBuf[:]); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\treturn net.JoinHostPort(net.IP(ipBuf[:]).String(), fmt.Sprint(binary.BigEndian.Uint16(portBuf[:]))), nil\n\tcase 0x04: // IPv6\n\t\tvar ipBuf [net.IPv6len]byte\n\t\tif _, err := io.ReadFull(r, ipBuf[:]); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tvar portBuf [2]byte\n\t\tif _, err := io.ReadFull(r, portBuf[:]); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\treturn net.JoinHostPort(net.IP(ipBuf[:]).String(), fmt.Sprint(binary.BigEndian.Uint16(portBuf[:]))), nil\n\tcase 0x03: // domain\n\t\tvar lengthBuf [1]byte\n\t\tif _, err := io.ReadFull(r, lengthBuf[:]); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tl := int(lengthBuf[0])\n\t\thostBuf := make([]byte, l)\n\t\tif _, err := io.ReadFull(r, hostBuf); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\tvar portBuf [2]byte\n\t\tif _, err := io.ReadFull(r, portBuf[:]); err != nil {\n\t\t\treturn \"\", err\n\t\t}\n\t\treturn net.JoinHostPort(string(hostBuf), fmt.Sprint(binary.BigEndian.Uint16(portBuf[:]))), nil\n\tdefault:\n\t\treturn \"\", fmt.Errorf(\"unknown address type: %d\", atyp[0])\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/config.go",
    "content": "package sudoku\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/transport/sudoku/obfs/sudoku\"\n)\n\n// ProtocolConfig defines the configuration for the Sudoku protocol stack.\n// It is intentionally kept close to the upstream Sudoku project to ensure wire compatibility.\ntype ProtocolConfig struct {\n\t// Client-only: \"host:port\".\n\tServerAddress string\n\n\t// Pre-shared key (or ED25519 key material) used to derive crypto and tables.\n\tKey string\n\n\t// \"aes-128-gcm\", \"chacha20-poly1305\", or \"none\".\n\tAEADMethod string\n\n\t// Table is the single obfuscation table to use when table rotation is disabled.\n\tTable *sudoku.Table\n\n\t// Tables is an optional candidate set for table rotation.\n\t// If provided (len>0), the client will pick one table per connection and the server will\n\t// probe the handshake to detect which one was used, keeping the handshake format unchanged.\n\t// When Tables is set, Table may be nil.\n\tTables []*sudoku.Table\n\n\t// Padding insertion ratio (0-100). Must satisfy PaddingMax >= PaddingMin.\n\tPaddingMin int\n\tPaddingMax int\n\n\t// EnablePureDownlink enables the pure Sudoku downlink mode.\n\t// When false, the connection uses the bandwidth-optimized packed downlink.\n\tEnablePureDownlink bool\n\n\t// Client-only: final target \"host:port\".\n\tTargetAddress string\n\n\t// Server-side handshake timeout (seconds).\n\tHandshakeTimeoutSeconds int\n\n\t// DisableHTTPMask disables all HTTP camouflage layers.\n\tDisableHTTPMask bool\n\n\t// HTTPMaskMode controls how the HTTP layer behaves:\n\t//   - \"legacy\": write a fake HTTP/1.1 header then switch to raw stream (default, not CDN-compatible)\n\t//   - \"stream\": real HTTP tunnel (split-stream), CDN-compatible\n\t//   - \"poll\": plain HTTP tunnel (authorize/push/pull), strong restricted-network pass-through\n\t//   - \"auto\": try stream then fall back to poll\n\t//   - \"ws\": WebSocket tunnel (GET upgrade), CDN-friendly\n\tHTTPMaskMode string\n\n\t// HTTPMaskTLSEnabled enables HTTPS for HTTP tunnel modes (client-side).\n\t// If false, the tunnel uses HTTP (no port-based inference).\n\tHTTPMaskTLSEnabled bool\n\n\t// HTTPMaskHost optionally overrides the HTTP Host header / SNI host for HTTP tunnel modes (client-side).\n\tHTTPMaskHost string\n\n\t// HTTPMaskPathRoot optionally prefixes all HTTP mask paths with a first-level segment.\n\t// Example: \"aabbcc\" => \"/aabbcc/session\", \"/aabbcc/api/v1/upload\", ...\n\tHTTPMaskPathRoot string\n\n\t// HTTPMaskMultiplex controls multiplex behavior when HTTPMask tunnel modes are enabled:\n\t//   - \"off\": disable reuse; each Dial establishes its own HTTPMask tunnel\n\t//   - \"auto\": reuse underlying HTTP connections across multiple tunnel dials (HTTP/1.1 keep-alive / HTTP/2)\n\t//   - \"on\": enable \"single tunnel, multi-target\" mux (Sudoku-level multiplex; Dial behaves like \"auto\" otherwise)\n\tHTTPMaskMultiplex string\n}\n\nfunc (c *ProtocolConfig) Validate() error {\n\tif c.Table == nil && len(c.Tables) == 0 {\n\t\treturn fmt.Errorf(\"table cannot be nil (or provide tables)\")\n\t}\n\tfor i, t := range c.Tables {\n\t\tif t == nil {\n\t\t\treturn fmt.Errorf(\"tables[%d] cannot be nil\", i)\n\t\t}\n\t}\n\n\tif c.Key == \"\" {\n\t\treturn fmt.Errorf(\"key cannot be empty\")\n\t}\n\n\tswitch c.AEADMethod {\n\tcase \"aes-128-gcm\", \"chacha20-poly1305\", \"none\":\n\tdefault:\n\t\treturn fmt.Errorf(\"invalid aead-method: %s, must be one of: aes-128-gcm, chacha20-poly1305, none\", c.AEADMethod)\n\t}\n\n\tif c.PaddingMin < 0 || c.PaddingMin > 100 {\n\t\treturn fmt.Errorf(\"padding-min must be between 0 and 100, got %d\", c.PaddingMin)\n\t}\n\tif c.PaddingMax < 0 || c.PaddingMax > 100 {\n\t\treturn fmt.Errorf(\"padding-max must be between 0 and 100, got %d\", c.PaddingMax)\n\t}\n\tif c.PaddingMax < c.PaddingMin {\n\t\treturn fmt.Errorf(\"padding-max (%d) must be >= padding-min (%d)\", c.PaddingMax, c.PaddingMin)\n\t}\n\n\tif c.HandshakeTimeoutSeconds < 0 {\n\t\treturn fmt.Errorf(\"handshake-timeout must be >= 0, got %d\", c.HandshakeTimeoutSeconds)\n\t}\n\n\tswitch strings.ToLower(strings.TrimSpace(c.HTTPMaskMode)) {\n\tcase \"\", \"legacy\", \"stream\", \"poll\", \"auto\", \"ws\":\n\tdefault:\n\t\treturn fmt.Errorf(\"invalid http-mask-mode: %s, must be one of: legacy, stream, poll, auto, ws\", c.HTTPMaskMode)\n\t}\n\n\tif v := strings.TrimSpace(c.HTTPMaskPathRoot); v != \"\" {\n\t\tv = strings.Trim(v, \"/\")\n\t\tif v == \"\" || strings.Contains(v, \"/\") {\n\t\t\treturn fmt.Errorf(\"invalid http-mask-path-root: must be a single path segment\")\n\t\t}\n\t\tfor i := 0; i < len(v); i++ {\n\t\t\tch := v[i]\n\t\t\tswitch {\n\t\t\tcase ch >= 'a' && ch <= 'z':\n\t\t\tcase ch >= 'A' && ch <= 'Z':\n\t\t\tcase ch >= '0' && ch <= '9':\n\t\t\tcase ch == '_' || ch == '-':\n\t\t\tdefault:\n\t\t\t\treturn fmt.Errorf(\"invalid http-mask-path-root: contains invalid character %q\", ch)\n\t\t\t}\n\t\t}\n\t}\n\n\tswitch strings.ToLower(strings.TrimSpace(c.HTTPMaskMultiplex)) {\n\tcase \"\", \"off\", \"auto\", \"on\":\n\tdefault:\n\t\treturn fmt.Errorf(\"invalid http-mask-multiplex: %s, must be one of: off, auto, on\", c.HTTPMaskMultiplex)\n\t}\n\n\treturn nil\n}\n\nfunc (c *ProtocolConfig) ValidateClient() error {\n\tif err := c.Validate(); err != nil {\n\t\treturn err\n\t}\n\tif c.ServerAddress == \"\" {\n\t\treturn fmt.Errorf(\"server address cannot be empty\")\n\t}\n\tif c.TargetAddress == \"\" {\n\t\treturn fmt.Errorf(\"target address cannot be empty\")\n\t}\n\treturn nil\n}\n\nfunc DefaultConfig() *ProtocolConfig {\n\treturn &ProtocolConfig{\n\t\tAEADMethod:              \"chacha20-poly1305\",\n\t\tPaddingMin:              10,\n\t\tPaddingMax:              30,\n\t\tEnablePureDownlink:      true,\n\t\tHandshakeTimeoutSeconds: 5,\n\t\tHTTPMaskMode:            \"legacy\",\n\t\tHTTPMaskMultiplex:       \"off\",\n\t}\n}\n\nfunc DerefInt(v *int, def int) int {\n\tif v == nil {\n\t\treturn def\n\t}\n\treturn *v\n}\n\nfunc DerefBool(v *bool, def bool) bool {\n\tif v == nil {\n\t\treturn def\n\t}\n\treturn *v\n}\n\n// ResolvePadding applies defaults and keeps min/max consistent when only one side is provided.\nfunc ResolvePadding(min, max *int, defMin, defMax int) (int, int) {\n\tpaddingMin := DerefInt(min, defMin)\n\tpaddingMax := DerefInt(max, defMax)\n\tswitch {\n\tcase min == nil && max != nil && paddingMax < paddingMin:\n\t\tpaddingMin = paddingMax\n\tcase max == nil && min != nil && paddingMax < paddingMin:\n\t\tpaddingMax = paddingMin\n\t}\n\treturn paddingMin, paddingMax\n}\n\nfunc NormalizeTableType(tableType string) (string, error) {\n\tnormalized, err := sudoku.NormalizeASCIIMode(tableType)\n\tif err != nil {\n\t\treturn \"\", fmt.Errorf(\"table-type must be prefer_ascii, prefer_entropy, up_ascii_down_entropy, or up_entropy_down_ascii\")\n\t}\n\treturn normalized, nil\n}\n\nfunc (c *ProtocolConfig) tableCandidates() []*sudoku.Table {\n\tif c == nil {\n\t\treturn nil\n\t}\n\tif len(c.Tables) > 0 {\n\t\treturn c.Tables\n\t}\n\tif c.Table != nil {\n\t\treturn []*sudoku.Table{c.Table}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/crypto/aead.go",
    "content": "package crypto\n\nimport (\n\t\"bytes\"\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\t\"crypto/rand\"\n\t\"crypto/sha256\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\n\t\"golang.org/x/crypto/chacha20poly1305\"\n)\n\ntype AEADConn struct {\n\tnet.Conn\n\taead      cipher.AEAD\n\treadBuf   bytes.Buffer\n\tnonceSize int\n}\n\nfunc (cc *AEADConn) CloseWrite() error {\n\tif cc == nil || cc.Conn == nil {\n\t\treturn nil\n\t}\n\tif cw, ok := cc.Conn.(interface{ CloseWrite() error }); ok {\n\t\treturn cw.CloseWrite()\n\t}\n\treturn nil\n}\n\nfunc (cc *AEADConn) CloseRead() error {\n\tif cc == nil || cc.Conn == nil {\n\t\treturn nil\n\t}\n\tif cr, ok := cc.Conn.(interface{ CloseRead() error }); ok {\n\t\treturn cr.CloseRead()\n\t}\n\treturn nil\n}\n\nfunc NewAEADConn(c net.Conn, key string, method string) (*AEADConn, error) {\n\tif method == \"none\" {\n\t\treturn &AEADConn{Conn: c, aead: nil}, nil\n\t}\n\n\th := sha256.New()\n\th.Write([]byte(key))\n\tkeyBytes := h.Sum(nil)\n\n\tvar aead cipher.AEAD\n\tvar err error\n\n\tswitch method {\n\tcase \"aes-128-gcm\":\n\t\tblock, _ := aes.NewCipher(keyBytes[:16])\n\t\taead, err = cipher.NewGCM(block)\n\tcase \"chacha20-poly1305\":\n\t\taead, err = chacha20poly1305.New(keyBytes)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unsupported cipher: %s\", method)\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &AEADConn{\n\t\tConn:      c,\n\t\taead:      aead,\n\t\tnonceSize: aead.NonceSize(),\n\t}, nil\n}\n\nfunc (cc *AEADConn) Write(p []byte) (int, error) {\n\tif cc.aead == nil {\n\t\treturn cc.Conn.Write(p)\n\t}\n\n\tmaxPayload := 65535 - cc.nonceSize - cc.aead.Overhead()\n\ttotalWritten := 0\n\tvar frameBuf bytes.Buffer\n\theader := make([]byte, 2)\n\tnonce := make([]byte, cc.nonceSize)\n\n\tfor len(p) > 0 {\n\t\tchunkSize := len(p)\n\t\tif chunkSize > maxPayload {\n\t\t\tchunkSize = maxPayload\n\t\t}\n\t\tchunk := p[:chunkSize]\n\t\tp = p[chunkSize:]\n\n\t\tif _, err := io.ReadFull(rand.Reader, nonce); err != nil {\n\t\t\treturn totalWritten, err\n\t\t}\n\n\t\tciphertext := cc.aead.Seal(nil, nonce, chunk, nil)\n\t\tframeLen := len(nonce) + len(ciphertext)\n\t\tbinary.BigEndian.PutUint16(header, uint16(frameLen))\n\n\t\tframeBuf.Reset()\n\t\tframeBuf.Write(header)\n\t\tframeBuf.Write(nonce)\n\t\tframeBuf.Write(ciphertext)\n\n\t\tif _, err := cc.Conn.Write(frameBuf.Bytes()); err != nil {\n\t\t\treturn totalWritten, err\n\t\t}\n\t\ttotalWritten += chunkSize\n\t}\n\treturn totalWritten, nil\n}\n\nfunc (cc *AEADConn) Read(p []byte) (int, error) {\n\tif cc.aead == nil {\n\t\treturn cc.Conn.Read(p)\n\t}\n\n\tif cc.readBuf.Len() > 0 {\n\t\treturn cc.readBuf.Read(p)\n\t}\n\n\theader := make([]byte, 2)\n\tif _, err := io.ReadFull(cc.Conn, header); err != nil {\n\t\treturn 0, err\n\t}\n\tframeLen := int(binary.BigEndian.Uint16(header))\n\n\tbody := make([]byte, frameLen)\n\tif _, err := io.ReadFull(cc.Conn, body); err != nil {\n\t\treturn 0, err\n\t}\n\n\tif len(body) < cc.nonceSize {\n\t\treturn 0, errors.New(\"frame too short\")\n\t}\n\tnonce := body[:cc.nonceSize]\n\tciphertext := body[cc.nonceSize:]\n\n\tplaintext, err := cc.aead.Open(nil, nonce, ciphertext, nil)\n\tif err != nil {\n\t\treturn 0, errors.New(\"decryption failed\")\n\t}\n\n\tcc.readBuf.Write(plaintext)\n\treturn cc.readBuf.Read(p)\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/crypto/ed25519.go",
    "content": "package crypto\n\nimport (\n\t\"crypto/rand\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\n\t\"github.com/metacubex/edwards25519\"\n)\n\n// KeyPair holds the scalar private key and point public key\ntype KeyPair struct {\n\tPrivate *edwards25519.Scalar\n\tPublic  *edwards25519.Point\n}\n\n// GenerateMasterKey generates a random master private key (scalar) and its public key (point)\nfunc GenerateMasterKey() (*KeyPair, error) {\n\t// 1. Generate random scalar x (32 bytes)\n\tvar seed [64]byte\n\tif _, err := rand.Read(seed[:]); err != nil {\n\t\treturn nil, err\n\t}\n\n\tx, err := edwards25519.NewScalar().SetUniformBytes(seed[:])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\t// 2. Calculate Public Key P = x * G\n\tP := new(edwards25519.Point).ScalarBaseMult(x)\n\n\treturn &KeyPair{Private: x, Public: P}, nil\n}\n\n// SplitPrivateKey takes a master private key x and returns a new random split key (r, k)\n// such that x = r + k (mod L).\n// Returns hex encoded string of r || k (64 bytes)\nfunc SplitPrivateKey(x *edwards25519.Scalar) (string, error) {\n\t// 1. Generate random r (32 bytes)\n\tvar seed [64]byte\n\tif _, err := rand.Read(seed[:]); err != nil {\n\t\treturn \"\", err\n\t}\n\tr, err := edwards25519.NewScalar().SetUniformBytes(seed[:])\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\n\t// 2. Calculate k = x - r (mod L)\n\tk := new(edwards25519.Scalar).Subtract(x, r)\n\n\t// 3. Encode r and k\n\trBytes := r.Bytes()\n\tkBytes := k.Bytes()\n\n\tfull := make([]byte, 64)\n\tcopy(full[:32], rBytes)\n\tcopy(full[32:], kBytes)\n\n\treturn hex.EncodeToString(full), nil\n}\n\n// RecoverPublicKey takes a split private key (r, k) or a master private key (x)\n// and returns the public key P.\n// Input can be:\n// - 32 bytes hex (Master Scalar x)\n// - 64 bytes hex (Split Key r || k)\nfunc RecoverPublicKey(keyHex string) (*edwards25519.Point, error) {\n\tkeyBytes, err := hex.DecodeString(keyHex)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid hex: %w\", err)\n\t}\n\n\tif len(keyBytes) == 32 {\n\t\t// Master Key x\n\t\tx, err := edwards25519.NewScalar().SetCanonicalBytes(keyBytes)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid scalar: %w\", err)\n\t\t}\n\t\treturn new(edwards25519.Point).ScalarBaseMult(x), nil\n\t}\n\tif len(keyBytes) == 64 {\n\t\t// Split Key r || k\n\t\trBytes := keyBytes[:32]\n\t\tkBytes := keyBytes[32:]\n\n\t\tr, err := edwards25519.NewScalar().SetCanonicalBytes(rBytes)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid scalar r: %w\", err)\n\t\t}\n\t\tk, err := edwards25519.NewScalar().SetCanonicalBytes(kBytes)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid scalar k: %w\", err)\n\t\t}\n\n\t\t// sum = r + k\n\t\tsum := new(edwards25519.Scalar).Add(r, k)\n\n\t\t// P = sum * G\n\t\treturn new(edwards25519.Point).ScalarBaseMult(sum), nil\n\t}\n\n\treturn nil, errors.New(\"invalid key length: must be 32 bytes (Master) or 64 bytes (Split)\")\n}\n\n// EncodePoint returns the hex string of the compressed point\nfunc EncodePoint(p *edwards25519.Point) string {\n\treturn hex.EncodeToString(p.Bytes())\n}\n\n// EncodeScalar returns the hex string of the scalar\nfunc EncodeScalar(s *edwards25519.Scalar) string {\n\treturn hex.EncodeToString(s.Bytes())\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/crypto/record_conn.go",
    "content": "package crypto\n\nimport (\n\t\"bytes\"\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\t\"crypto/hmac\"\n\t\"crypto/rand\"\n\t\"crypto/sha256\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"sync\"\n\t\"sync/atomic\"\n\n\t\"golang.org/x/crypto/chacha20poly1305\"\n)\n\n// KeyUpdateAfterBytes controls automatic key rotation based on plaintext bytes.\n// It is a package var (not config) to enable targeted tests with smaller thresholds.\nvar KeyUpdateAfterBytes int64 = 32 << 20 // 32 MiB\n\nconst (\n\trecordHeaderSize = 12 // epoch(uint32) + seq(uint64) - also used as nonce+AAD.\n\tmaxFrameBodySize = 65535\n)\n\ntype recordKeys struct {\n\tbaseSend []byte\n\tbaseRecv []byte\n}\n\n// RecordConn is a framed AEAD net.Conn with:\n//   - deterministic per-record nonce (epoch+seq)\n//   - per-direction key rotation (epoch), driven by plaintext byte counters\n//   - replay/out-of-order protection within the connection (strict seq check)\n//\n// Wire format per record:\n//   - uint16 bodyLen\n//   - header[12] = epoch(uint32 BE) || seq(uint64 BE)  (plaintext)\n//   - ciphertext = AEAD(header as nonce, plaintext, header as AAD)\ntype RecordConn struct {\n\tnet.Conn\n\tmethod string\n\n\twriteMu sync.Mutex\n\treadMu  sync.Mutex\n\n\tkeys recordKeys\n\n\tsendAEAD      cipher.AEAD\n\tsendAEADEpoch uint32\n\n\trecvAEAD      cipher.AEAD\n\trecvAEADEpoch uint32\n\n\t// Send direction state.\n\tsendEpoch        uint32\n\tsendSeq          uint64\n\tsendBytes        int64\n\tsendEpochUpdates uint32\n\n\t// Receive direction state.\n\trecvEpoch       uint32\n\trecvSeq         uint64\n\trecvInitialized bool\n\n\treadBuf bytes.Buffer\n\n\t// writeFrame is a reusable buffer for [len||header||ciphertext] on the wire.\n\t// Guarded by writeMu.\n\twriteFrame []byte\n}\n\nfunc (c *RecordConn) CloseWrite() error {\n\tif c == nil {\n\t\treturn nil\n\t}\n\tif cw, ok := c.Conn.(interface{ CloseWrite() error }); ok {\n\t\treturn cw.CloseWrite()\n\t}\n\treturn nil\n}\n\nfunc (c *RecordConn) CloseRead() error {\n\tif c == nil {\n\t\treturn nil\n\t}\n\tif cr, ok := c.Conn.(interface{ CloseRead() error }); ok {\n\t\treturn cr.CloseRead()\n\t}\n\treturn nil\n}\n\nfunc NewRecordConn(conn net.Conn, method string, baseSend, baseRecv []byte) (*RecordConn, error) {\n\tif conn == nil {\n\t\treturn nil, fmt.Errorf(\"nil conn\")\n\t}\n\tmethod = normalizeAEADMethod(method)\n\tif method != \"none\" {\n\t\tif err := validateBaseKey(baseSend); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid send base key: %w\", err)\n\t\t}\n\t\tif err := validateBaseKey(baseRecv); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"invalid recv base key: %w\", err)\n\t\t}\n\t}\n\trc := &RecordConn{Conn: conn, method: method}\n\trc.keys = recordKeys{baseSend: cloneBytes(baseSend), baseRecv: cloneBytes(baseRecv)}\n\tif err := rc.resetTrafficState(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn rc, nil\n}\n\nfunc (c *RecordConn) Rekey(baseSend, baseRecv []byte) error {\n\tif c == nil {\n\t\treturn fmt.Errorf(\"nil conn\")\n\t}\n\tif c.method != \"none\" {\n\t\tif err := validateBaseKey(baseSend); err != nil {\n\t\t\treturn fmt.Errorf(\"invalid send base key: %w\", err)\n\t\t}\n\t\tif err := validateBaseKey(baseRecv); err != nil {\n\t\t\treturn fmt.Errorf(\"invalid recv base key: %w\", err)\n\t\t}\n\t}\n\n\tc.writeMu.Lock()\n\tc.readMu.Lock()\n\tdefer c.readMu.Unlock()\n\tdefer c.writeMu.Unlock()\n\n\tc.keys = recordKeys{baseSend: cloneBytes(baseSend), baseRecv: cloneBytes(baseRecv)}\n\tif err := c.resetTrafficState(); err != nil {\n\t\treturn err\n\t}\n\tc.readBuf.Reset()\n\n\tc.sendAEAD = nil\n\tc.recvAEAD = nil\n\tc.sendAEADEpoch = 0\n\tc.recvAEADEpoch = 0\n\treturn nil\n}\n\nfunc (c *RecordConn) resetTrafficState() error {\n\tsendEpoch, sendSeq, err := randomRecordCounters()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"initialize record counters: %w\", err)\n\t}\n\tc.sendEpoch = sendEpoch\n\tc.sendSeq = sendSeq\n\tc.sendBytes = 0\n\tc.sendEpochUpdates = 0\n\tc.recvEpoch = 0\n\tc.recvSeq = 0\n\tc.recvInitialized = false\n\treturn nil\n}\n\nfunc normalizeAEADMethod(method string) string {\n\tswitch method {\n\tcase \"\", \"chacha20-poly1305\":\n\t\treturn \"chacha20-poly1305\"\n\tcase \"aes-128-gcm\", \"none\":\n\t\treturn method\n\tdefault:\n\t\treturn method\n\t}\n}\n\nfunc validateBaseKey(b []byte) error {\n\tif len(b) < 32 {\n\t\treturn fmt.Errorf(\"need at least 32 bytes, got %d\", len(b))\n\t}\n\treturn nil\n}\n\nfunc cloneBytes(b []byte) []byte {\n\tif len(b) == 0 {\n\t\treturn nil\n\t}\n\treturn append([]byte(nil), b...)\n}\n\nfunc randomRecordCounters() (uint32, uint64, error) {\n\tepoch, err := randomNonZeroUint32()\n\tif err != nil {\n\t\treturn 0, 0, err\n\t}\n\tseq, err := randomNonZeroUint64()\n\tif err != nil {\n\t\treturn 0, 0, err\n\t}\n\treturn epoch, seq, nil\n}\n\nfunc randomNonZeroUint32() (uint32, error) {\n\tvar b [4]byte\n\tfor {\n\t\tif _, err := io.ReadFull(rand.Reader, b[:]); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tv := binary.BigEndian.Uint32(b[:])\n\t\tif v != 0 && v != ^uint32(0) {\n\t\t\treturn v, nil\n\t\t}\n\t}\n}\n\nfunc randomNonZeroUint64() (uint64, error) {\n\tvar b [8]byte\n\tfor {\n\t\tif _, err := io.ReadFull(rand.Reader, b[:]); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tv := binary.BigEndian.Uint64(b[:])\n\t\tif v != 0 && v != ^uint64(0) {\n\t\t\treturn v, nil\n\t\t}\n\t}\n}\n\nfunc (c *RecordConn) newAEADFor(base []byte, epoch uint32) (cipher.AEAD, error) {\n\tif c.method == \"none\" {\n\t\treturn nil, nil\n\t}\n\tkey := deriveEpochKey(base, epoch, c.method)\n\tswitch c.method {\n\tcase \"aes-128-gcm\":\n\t\tblock, err := aes.NewCipher(key[:16])\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ta, err := cipher.NewGCM(block)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif a.NonceSize() != recordHeaderSize {\n\t\t\treturn nil, fmt.Errorf(\"unexpected gcm nonce size: %d\", a.NonceSize())\n\t\t}\n\t\treturn a, nil\n\tcase \"chacha20-poly1305\":\n\t\ta, err := chacha20poly1305.New(key[:32])\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif a.NonceSize() != recordHeaderSize {\n\t\t\treturn nil, fmt.Errorf(\"unexpected chacha nonce size: %d\", a.NonceSize())\n\t\t}\n\t\treturn a, nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unsupported cipher: %s\", c.method)\n\t}\n}\n\nfunc deriveEpochKey(base []byte, epoch uint32, method string) []byte {\n\tvar b [4]byte\n\tbinary.BigEndian.PutUint32(b[:], epoch)\n\tmac := hmac.New(sha256.New, base)\n\t_, _ = mac.Write([]byte(\"sudoku-record:\"))\n\t_, _ = mac.Write([]byte(method))\n\t_, _ = mac.Write(b[:])\n\treturn mac.Sum(nil)\n}\n\nfunc (c *RecordConn) maybeBumpSendEpochLocked(addedPlain int) error {\n\tku := atomic.LoadInt64(&KeyUpdateAfterBytes)\n\tif ku <= 0 || c.method == \"none\" {\n\t\treturn nil\n\t}\n\tc.sendBytes += int64(addedPlain)\n\tthreshold := ku * int64(c.sendEpochUpdates+1)\n\tif c.sendBytes < threshold {\n\t\treturn nil\n\t}\n\tc.sendEpoch++\n\tc.sendEpochUpdates++\n\tnextSeq, err := randomNonZeroUint64()\n\tif err != nil {\n\t\treturn fmt.Errorf(\"rotate record seq: %w\", err)\n\t}\n\tc.sendSeq = nextSeq\n\treturn nil\n}\n\nfunc (c *RecordConn) validateRecvPosition(epoch uint32, seq uint64) error {\n\tif !c.recvInitialized {\n\t\treturn nil\n\t}\n\tif epoch < c.recvEpoch {\n\t\treturn fmt.Errorf(\"replayed epoch: got %d want >=%d\", epoch, c.recvEpoch)\n\t}\n\tif epoch == c.recvEpoch && seq != c.recvSeq {\n\t\treturn fmt.Errorf(\"out of order: epoch=%d got=%d want=%d\", epoch, seq, c.recvSeq)\n\t}\n\tif epoch > c.recvEpoch {\n\t\tconst maxJump = 8\n\t\tif epoch-c.recvEpoch > maxJump {\n\t\t\treturn fmt.Errorf(\"epoch jump too large: got=%d want<=%d\", epoch-c.recvEpoch, maxJump)\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (c *RecordConn) markRecvPosition(epoch uint32, seq uint64) {\n\tc.recvEpoch = epoch\n\tc.recvSeq = seq + 1\n\tc.recvInitialized = true\n}\n\nfunc (c *RecordConn) Write(p []byte) (int, error) {\n\tif c == nil || c.Conn == nil {\n\t\treturn 0, net.ErrClosed\n\t}\n\tif c.method == \"none\" {\n\t\treturn c.Conn.Write(p)\n\t}\n\n\tc.writeMu.Lock()\n\tdefer c.writeMu.Unlock()\n\n\ttotal := 0\n\tfor len(p) > 0 {\n\t\tif c.sendAEAD == nil || c.sendAEADEpoch != c.sendEpoch {\n\t\t\ta, err := c.newAEADFor(c.keys.baseSend, c.sendEpoch)\n\t\t\tif err != nil {\n\t\t\t\treturn total, err\n\t\t\t}\n\t\t\tc.sendAEAD = a\n\t\t\tc.sendAEADEpoch = c.sendEpoch\n\t\t}\n\t\taead := c.sendAEAD\n\n\t\tmaxPlain := maxFrameBodySize - recordHeaderSize - aead.Overhead()\n\t\tif maxPlain <= 0 {\n\t\t\treturn total, errors.New(\"frame size too small\")\n\t\t}\n\t\tn := len(p)\n\t\tif n > maxPlain {\n\t\t\tn = maxPlain\n\t\t}\n\t\tchunk := p[:n]\n\t\tp = p[n:]\n\n\t\tvar header [recordHeaderSize]byte\n\t\tbinary.BigEndian.PutUint32(header[:4], c.sendEpoch)\n\t\tbinary.BigEndian.PutUint64(header[4:], c.sendSeq)\n\t\tc.sendSeq++\n\n\t\tcipherLen := n + aead.Overhead()\n\t\tbodyLen := recordHeaderSize + cipherLen\n\t\tframeLen := 2 + bodyLen\n\t\tif bodyLen > maxFrameBodySize {\n\t\t\treturn total, errors.New(\"frame too large\")\n\t\t}\n\t\tif cap(c.writeFrame) < frameLen {\n\t\t\tc.writeFrame = make([]byte, frameLen)\n\t\t}\n\t\tframe := c.writeFrame[:frameLen]\n\t\tbinary.BigEndian.PutUint16(frame[:2], uint16(bodyLen))\n\t\tcopy(frame[2:2+recordHeaderSize], header[:])\n\n\t\tdst := frame[2+recordHeaderSize : 2+recordHeaderSize : frameLen]\n\t\t_ = aead.Seal(dst[:0], header[:], chunk, header[:])\n\n\t\tif err := writeFull(c.Conn, frame); err != nil {\n\t\t\treturn total, err\n\t\t}\n\n\t\ttotal += n\n\t\tif err := c.maybeBumpSendEpochLocked(n); err != nil {\n\t\t\treturn total, err\n\t\t}\n\t}\n\treturn total, nil\n}\n\nfunc (c *RecordConn) Read(p []byte) (int, error) {\n\tif c == nil || c.Conn == nil {\n\t\treturn 0, net.ErrClosed\n\t}\n\tif c.method == \"none\" {\n\t\treturn c.Conn.Read(p)\n\t}\n\n\tc.readMu.Lock()\n\tdefer c.readMu.Unlock()\n\n\tif c.readBuf.Len() > 0 {\n\t\treturn c.readBuf.Read(p)\n\t}\n\n\tvar lenBuf [2]byte\n\tif _, err := io.ReadFull(c.Conn, lenBuf[:]); err != nil {\n\t\treturn 0, err\n\t}\n\tbodyLen := int(binary.BigEndian.Uint16(lenBuf[:]))\n\tif bodyLen < recordHeaderSize {\n\t\treturn 0, errors.New(\"frame too short\")\n\t}\n\tif bodyLen > maxFrameBodySize {\n\t\treturn 0, errors.New(\"frame too large\")\n\t}\n\n\tbody := make([]byte, bodyLen)\n\tif _, err := io.ReadFull(c.Conn, body); err != nil {\n\t\treturn 0, err\n\t}\n\theader := body[:recordHeaderSize]\n\tciphertext := body[recordHeaderSize:]\n\n\tepoch := binary.BigEndian.Uint32(header[:4])\n\tseq := binary.BigEndian.Uint64(header[4:])\n\n\tif err := c.validateRecvPosition(epoch, seq); err != nil {\n\t\treturn 0, err\n\t}\n\n\tif c.recvAEAD == nil || c.recvAEADEpoch != epoch {\n\t\ta, err := c.newAEADFor(c.keys.baseRecv, epoch)\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tc.recvAEAD = a\n\t\tc.recvAEADEpoch = epoch\n\t}\n\taead := c.recvAEAD\n\n\tplaintext, err := aead.Open(nil, header, ciphertext, header)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"decryption failed: epoch=%d seq=%d: %w\", epoch, seq, err)\n\t}\n\tc.markRecvPosition(epoch, seq)\n\n\tc.readBuf.Write(plaintext)\n\treturn c.readBuf.Read(p)\n}\n\nfunc writeFull(w io.Writer, b []byte) error {\n\tfor len(b) > 0 {\n\t\tn, err := w.Write(b)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tb = b[n:]\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/crypto/record_conn_test.go",
    "content": "package crypto\n\nimport (\n\t\"bytes\"\n\t\"crypto/sha256\"\n\t\"encoding/binary\"\n\t\"io\"\n\t\"net\"\n\t\"testing\"\n\t\"time\"\n)\n\ntype captureConn struct {\n\tbytes.Buffer\n}\n\nfunc (c *captureConn) Read(_ []byte) (int, error)       { return 0, io.EOF }\nfunc (c *captureConn) Write(p []byte) (int, error)      { return c.Buffer.Write(p) }\nfunc (c *captureConn) Close() error                     { return nil }\nfunc (c *captureConn) LocalAddr() net.Addr              { return nil }\nfunc (c *captureConn) RemoteAddr() net.Addr             { return nil }\nfunc (c *captureConn) SetDeadline(time.Time) error      { return nil }\nfunc (c *captureConn) SetReadDeadline(time.Time) error  { return nil }\nfunc (c *captureConn) SetWriteDeadline(time.Time) error { return nil }\n\ntype replayConn struct {\n\treader *bytes.Reader\n}\n\nfunc (c *replayConn) Read(p []byte) (int, error)       { return c.reader.Read(p) }\nfunc (c *replayConn) Write(p []byte) (int, error)      { return len(p), nil }\nfunc (c *replayConn) Close() error                     { return nil }\nfunc (c *replayConn) LocalAddr() net.Addr              { return nil }\nfunc (c *replayConn) RemoteAddr() net.Addr             { return nil }\nfunc (c *replayConn) SetDeadline(time.Time) error      { return nil }\nfunc (c *replayConn) SetReadDeadline(time.Time) error  { return nil }\nfunc (c *replayConn) SetWriteDeadline(time.Time) error { return nil }\n\nfunc TestRecordConn_FirstFrameUsesRandomizedCounters(t *testing.T) {\n\tpskSend := sha256.Sum256([]byte(\"record-send\"))\n\tpskRecv := sha256.Sum256([]byte(\"record-recv\"))\n\n\traw := &captureConn{}\n\twriter, err := NewRecordConn(raw, \"chacha20-poly1305\", pskSend[:], pskRecv[:])\n\tif err != nil {\n\t\tt.Fatalf(\"new writer: %v\", err)\n\t}\n\n\tif writer.sendEpoch == 0 || writer.sendSeq == 0 {\n\t\tt.Fatalf(\"expected non-zero randomized counters, got epoch=%d seq=%d\", writer.sendEpoch, writer.sendSeq)\n\t}\n\n\twant := []byte(\"record prefix camouflage\")\n\tif _, err := writer.Write(want); err != nil {\n\t\tt.Fatalf(\"write: %v\", err)\n\t}\n\n\twire := raw.Bytes()\n\tif len(wire) < 2+recordHeaderSize {\n\t\tt.Fatalf(\"short frame: %d\", len(wire))\n\t}\n\n\tbodyLen := int(binary.BigEndian.Uint16(wire[:2]))\n\tif bodyLen != len(wire)-2 {\n\t\tt.Fatalf(\"body len mismatch: got %d want %d\", bodyLen, len(wire)-2)\n\t}\n\n\tepoch := binary.BigEndian.Uint32(wire[2:6])\n\tseq := binary.BigEndian.Uint64(wire[6:14])\n\tif epoch == 0 || seq == 0 {\n\t\tt.Fatalf(\"wire header still starts from zero: epoch=%d seq=%d\", epoch, seq)\n\t}\n\n\treader, err := NewRecordConn(&replayConn{reader: bytes.NewReader(wire)}, \"chacha20-poly1305\", pskRecv[:], pskSend[:])\n\tif err != nil {\n\t\tt.Fatalf(\"new reader: %v\", err)\n\t}\n\n\tgot := make([]byte, len(want))\n\tif _, err := io.ReadFull(reader, got); err != nil {\n\t\tt.Fatalf(\"read: %v\", err)\n\t}\n\tif !bytes.Equal(got, want) {\n\t\tt.Fatalf(\"plaintext mismatch: got %q want %q\", got, want)\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/directional_hint_test.go",
    "content": "package sudoku\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"net\"\n\t\"testing\"\n\n\t\"github.com/metacubex/mihomo/transport/sudoku/crypto\"\n\tsudokuobfs \"github.com/metacubex/mihomo/transport/sudoku/obfs/sudoku\"\n)\n\nfunc TestDirectionalCustomTableRotationHintRoundTrip(t *testing.T) {\n\tkey := \"directional-rotate-key\"\n\ttarget := \"8.8.8.8:53\"\n\n\tserverTables, err := NewServerTablesWithCustomPatterns(ClientAEADSeed(key), \"up_ascii_down_entropy\", \"\", []string{\"xpxvvpvv\", \"vxpvxvvp\"})\n\tif err != nil {\n\t\tt.Fatalf(\"server tables: %v\", err)\n\t}\n\tif len(serverTables) != 2 {\n\t\tt.Fatalf(\"expected 2 server tables, got %d\", len(serverTables))\n\t}\n\n\tclientTable, err := sudokuobfs.NewTableWithCustom(ClientAEADSeed(key), \"up_ascii_down_entropy\", \"vxpvxvvp\")\n\tif err != nil {\n\t\tt.Fatalf(\"client table: %v\", err)\n\t}\n\n\tserverCfg := DefaultConfig()\n\tserverCfg.Key = key\n\tserverCfg.AEADMethod = \"chacha20-poly1305\"\n\tserverCfg.Tables = serverTables\n\tserverCfg.PaddingMin = 0\n\tserverCfg.PaddingMax = 0\n\tserverCfg.EnablePureDownlink = true\n\tserverCfg.HandshakeTimeoutSeconds = 5\n\tserverCfg.DisableHTTPMask = true\n\n\tclientCfg := DefaultConfig()\n\t*clientCfg = *serverCfg\n\tclientCfg.ServerAddress = \"example.com:443\"\n\n\tserverConn, clientConn := net.Pipe()\n\tdefer clientConn.Close()\n\n\tserverErr := make(chan error, 1)\n\tgo func() {\n\t\tdefer close(serverErr)\n\t\tdefer serverConn.Close()\n\n\t\tc, meta, err := ServerHandshake(serverConn, serverCfg)\n\t\tif err != nil {\n\t\t\tserverErr <- err\n\t\t\treturn\n\t\t}\n\t\tdefer c.Close()\n\n\t\tsession, err := ReadServerSession(c, meta)\n\t\tif err != nil {\n\t\t\tserverErr <- err\n\t\t\treturn\n\t\t}\n\t\tif session.Type != SessionTypeTCP || session.Target != target {\n\t\t\tserverErr <- io.ErrUnexpectedEOF\n\t\t\treturn\n\t\t}\n\n\t\tpayload := make([]byte, len(\"client-payload\"))\n\t\tif _, err := io.ReadFull(session.Conn, payload); err != nil {\n\t\t\tserverErr <- err\n\t\t\treturn\n\t\t}\n\t\tif !bytes.Equal(payload, []byte(\"client-payload\")) {\n\t\t\tserverErr <- io.ErrUnexpectedEOF\n\t\t\treturn\n\t\t}\n\n\t\tif _, err := session.Conn.Write([]byte(\"server-reply\")); err != nil {\n\t\t\tserverErr <- err\n\t\t}\n\t}()\n\n\tseed := ClientAEADSeed(clientCfg.Key)\n\tobfsConn := buildClientObfsConn(clientConn, clientCfg, clientTable)\n\tpskC2S, pskS2C := derivePSKDirectionalBases(seed)\n\tcConn, err := crypto.NewRecordConn(obfsConn, clientCfg.AEADMethod, pskC2S, pskS2C)\n\tif err != nil {\n\t\tt.Fatalf(\"setup crypto: %v\", err)\n\t}\n\tdefer cConn.Close()\n\n\tif _, err := kipHandshakeClient(cConn, seed, kipUserHashFromKey(clientCfg.Key), KIPFeatAll, clientTable.Hint(), true); err != nil {\n\t\tt.Fatalf(\"client handshake: %v\", err)\n\t}\n\n\taddrBuf, err := EncodeAddress(target)\n\tif err != nil {\n\t\tt.Fatalf(\"encode target: %v\", err)\n\t}\n\tif err := WriteKIPMessage(cConn, KIPTypeOpenTCP, addrBuf); err != nil {\n\t\tt.Fatalf(\"write target: %v\", err)\n\t}\n\tif _, err := cConn.Write([]byte(\"client-payload\")); err != nil {\n\t\tt.Fatalf(\"write payload: %v\", err)\n\t}\n\n\treply := make([]byte, len(\"server-reply\"))\n\tif _, err := io.ReadFull(cConn, reply); err != nil {\n\t\tt.Fatalf(\"read reply: %v\", err)\n\t}\n\tif !bytes.Equal(reply, []byte(\"server-reply\")) {\n\t\tt.Fatalf(\"unexpected reply: %q\", reply)\n\t}\n\n\tif err := <-serverErr; err != nil {\n\t\tt.Fatalf(\"server: %v\", err)\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/early_handshake.go",
    "content": "package sudoku\n\nimport (\n\t\"bytes\"\n\t\"crypto/ecdh\"\n\t\"crypto/rand\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/transport/sudoku/crypto\"\n\thttpmaskobfs \"github.com/metacubex/mihomo/transport/sudoku/obfs/httpmask\"\n\tsudokuobfs \"github.com/metacubex/mihomo/transport/sudoku/obfs/sudoku\"\n)\n\nconst earlyKIPHandshakeTTL = 60 * time.Second\n\ntype EarlyCodecConfig struct {\n\tPSK                string\n\tAEAD               string\n\tEnablePureDownlink bool\n\tPaddingMin         int\n\tPaddingMax         int\n}\n\ntype EarlyClientState struct {\n\tRequestPayload []byte\n\n\tcfg         EarlyCodecConfig\n\ttable       *sudokuobfs.Table\n\tnonce       [kipHelloNonceSize]byte\n\tephemeral   *ecdh.PrivateKey\n\tsessionC2S  []byte\n\tsessionS2C  []byte\n\tresponseSet bool\n}\n\ntype EarlyServerState struct {\n\tResponsePayload []byte\n\tUserHash        string\n\n\tcfg        EarlyCodecConfig\n\ttable      *sudokuobfs.Table\n\tsessionC2S []byte\n\tsessionS2C []byte\n}\n\ntype ReplayAllowFunc func(userHash string, nonce [kipHelloNonceSize]byte, now time.Time) bool\n\ntype earlyMemoryConn struct {\n\treader *bytes.Reader\n\twrite  bytes.Buffer\n}\n\nfunc newEarlyMemoryConn(readBuf []byte) *earlyMemoryConn {\n\treturn &earlyMemoryConn{reader: bytes.NewReader(readBuf)}\n}\n\nfunc (c *earlyMemoryConn) Read(p []byte) (int, error) {\n\tif c == nil || c.reader == nil {\n\t\treturn 0, net.ErrClosed\n\t}\n\treturn c.reader.Read(p)\n}\n\nfunc (c *earlyMemoryConn) Write(p []byte) (int, error) {\n\tif c == nil {\n\t\treturn 0, net.ErrClosed\n\t}\n\treturn c.write.Write(p)\n}\n\nfunc (c *earlyMemoryConn) Close() error                     { return nil }\nfunc (c *earlyMemoryConn) LocalAddr() net.Addr              { return earlyDummyAddr(\"local\") }\nfunc (c *earlyMemoryConn) RemoteAddr() net.Addr             { return earlyDummyAddr(\"remote\") }\nfunc (c *earlyMemoryConn) SetDeadline(time.Time) error      { return nil }\nfunc (c *earlyMemoryConn) SetReadDeadline(time.Time) error  { return nil }\nfunc (c *earlyMemoryConn) SetWriteDeadline(time.Time) error { return nil }\nfunc (c *earlyMemoryConn) Written() []byte                  { return append([]byte(nil), c.write.Bytes()...) }\n\ntype earlyDummyAddr string\n\nfunc (a earlyDummyAddr) Network() string { return string(a) }\nfunc (a earlyDummyAddr) String() string  { return string(a) }\n\nfunc buildEarlyClientObfsConn(raw net.Conn, cfg EarlyCodecConfig, table *sudokuobfs.Table) net.Conn {\n\tbase := sudokuobfs.NewConn(raw, table, cfg.PaddingMin, cfg.PaddingMax, false)\n\tdownlinkReader := newClientDownlinkReader(raw, table, cfg.PaddingMin, cfg.PaddingMax, cfg.EnablePureDownlink)\n\tif downlinkReader == nil {\n\t\treturn base\n\t}\n\treturn newDirectionalConn(raw, downlinkReader, base)\n}\n\nfunc buildEarlyServerObfsConn(raw net.Conn, cfg EarlyCodecConfig, table *sudokuobfs.Table) net.Conn {\n\tuplink := sudokuobfs.NewConn(raw, table, cfg.PaddingMin, cfg.PaddingMax, false)\n\tdownlinkWriter, closers := newServerDownlinkWriter(raw, table, cfg.PaddingMin, cfg.PaddingMax, cfg.EnablePureDownlink)\n\tif downlinkWriter == nil {\n\t\treturn uplink\n\t}\n\treturn newDirectionalConn(raw, uplink, downlinkWriter, closers...)\n}\n\nfunc NewEarlyClientState(cfg EarlyCodecConfig, table *sudokuobfs.Table, tableHint uint32, hasTableHint bool, userHash [kipHelloUserHashSize]byte, feats uint32) (*EarlyClientState, error) {\n\tif table == nil {\n\t\treturn nil, fmt.Errorf(\"nil table\")\n\t}\n\n\tcurve := ecdh.X25519()\n\tephemeral, err := curve.GenerateKey(rand.Reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"ecdh generate failed: %w\", err)\n\t}\n\n\tvar nonce [kipHelloNonceSize]byte\n\tif _, err := rand.Read(nonce[:]); err != nil {\n\t\treturn nil, fmt.Errorf(\"nonce generate failed: %w\", err)\n\t}\n\n\tvar clientPub [kipHelloPubSize]byte\n\tcopy(clientPub[:], ephemeral.PublicKey().Bytes())\n\thello := newKIPClientHello(userHash, nonce, clientPub, feats, tableHint, hasTableHint)\n\n\tmem := newEarlyMemoryConn(nil)\n\tobfsConn := buildEarlyClientObfsConn(mem, cfg, table)\n\tpskC2S, pskS2C := derivePSKDirectionalBases(cfg.PSK)\n\trc, err := crypto.NewRecordConn(obfsConn, cfg.AEAD, pskC2S, pskS2C)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"client early crypto setup failed: %w\", err)\n\t}\n\tif err := WriteKIPMessage(rc, KIPTypeClientHello, hello.EncodePayload()); err != nil {\n\t\treturn nil, fmt.Errorf(\"write early client hello failed: %w\", err)\n\t}\n\n\treturn &EarlyClientState{\n\t\tRequestPayload: mem.Written(),\n\t\tcfg:            cfg,\n\t\ttable:          table,\n\t\tnonce:          nonce,\n\t\tephemeral:      ephemeral,\n\t}, nil\n}\n\nfunc (s *EarlyClientState) ProcessResponse(payload []byte) error {\n\tif s == nil {\n\t\treturn fmt.Errorf(\"nil client state\")\n\t}\n\n\tmem := newEarlyMemoryConn(payload)\n\tobfsConn := buildEarlyClientObfsConn(mem, s.cfg, s.table)\n\tpskC2S, pskS2C := derivePSKDirectionalBases(s.cfg.PSK)\n\trc, err := crypto.NewRecordConn(obfsConn, s.cfg.AEAD, pskC2S, pskS2C)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"client early crypto setup failed: %w\", err)\n\t}\n\n\tmsg, err := ReadKIPMessage(rc)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"read early server hello failed: %w\", err)\n\t}\n\tif msg.Type != KIPTypeServerHello {\n\t\treturn fmt.Errorf(\"unexpected early handshake message: %d\", msg.Type)\n\t}\n\tsh, err := DecodeKIPServerHelloPayload(msg.Payload)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"decode early server hello failed: %w\", err)\n\t}\n\tif sh.Nonce != s.nonce {\n\t\treturn fmt.Errorf(\"early handshake nonce mismatch\")\n\t}\n\n\tshared, err := x25519SharedSecret(s.ephemeral, sh.ServerPub[:])\n\tif err != nil {\n\t\treturn fmt.Errorf(\"ecdh failed: %w\", err)\n\t}\n\ts.sessionC2S, s.sessionS2C, err = deriveSessionDirectionalBases(s.cfg.PSK, shared, s.nonce)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"derive session keys failed: %w\", err)\n\t}\n\ts.responseSet = true\n\treturn nil\n}\n\nfunc (s *EarlyClientState) WrapConn(raw net.Conn) (net.Conn, error) {\n\tif s == nil {\n\t\treturn nil, fmt.Errorf(\"nil client state\")\n\t}\n\tif !s.responseSet {\n\t\treturn nil, fmt.Errorf(\"early handshake not completed\")\n\t}\n\n\tobfsConn := buildEarlyClientObfsConn(raw, s.cfg, s.table)\n\trc, err := crypto.NewRecordConn(obfsConn, s.cfg.AEAD, s.sessionC2S, s.sessionS2C)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"setup client session crypto failed: %w\", err)\n\t}\n\treturn rc, nil\n}\n\nfunc (s *EarlyClientState) Ready() bool {\n\treturn s != nil && s.responseSet\n}\n\nfunc NewHTTPMaskClientEarlyHandshake(cfg EarlyCodecConfig, table *sudokuobfs.Table, tableHint uint32, hasTableHint bool, userHash [kipHelloUserHashSize]byte, feats uint32) (*httpmaskobfs.ClientEarlyHandshake, error) {\n\tstate, err := NewEarlyClientState(cfg, table, tableHint, hasTableHint, userHash, feats)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &httpmaskobfs.ClientEarlyHandshake{\n\t\tRequestPayload: state.RequestPayload,\n\t\tHandleResponse: state.ProcessResponse,\n\t\tReady:          state.Ready,\n\t\tWrapConn:       state.WrapConn,\n\t}, nil\n}\n\nfunc ProcessEarlyClientPayload(cfg EarlyCodecConfig, tables []*sudokuobfs.Table, payload []byte, allowReplay ReplayAllowFunc) (*EarlyServerState, error) {\n\tif len(payload) == 0 {\n\t\treturn nil, fmt.Errorf(\"empty early payload\")\n\t}\n\tif len(tables) == 0 {\n\t\treturn nil, fmt.Errorf(\"no tables configured\")\n\t}\n\n\tvar firstErr error\n\tfor _, table := range tables {\n\t\tstate, err := processEarlyClientPayloadForTable(cfg, tables, table, payload, allowReplay)\n\t\tif err == nil {\n\t\t\treturn state, nil\n\t\t}\n\t\tif firstErr == nil {\n\t\t\tfirstErr = err\n\t\t}\n\t}\n\tif firstErr == nil {\n\t\tfirstErr = fmt.Errorf(\"early handshake probe failed\")\n\t}\n\treturn nil, firstErr\n}\n\nfunc processEarlyClientPayloadForTable(cfg EarlyCodecConfig, tables []*sudokuobfs.Table, table *sudokuobfs.Table, payload []byte, allowReplay ReplayAllowFunc) (*EarlyServerState, error) {\n\tmem := newEarlyMemoryConn(payload)\n\tobfsConn := buildEarlyServerObfsConn(mem, cfg, table)\n\tpskC2S, pskS2C := derivePSKDirectionalBases(cfg.PSK)\n\trc, err := crypto.NewRecordConn(obfsConn, cfg.AEAD, pskS2C, pskC2S)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tmsg, err := ReadKIPMessage(rc)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif msg.Type != KIPTypeClientHello {\n\t\treturn nil, fmt.Errorf(\"unexpected handshake message: %d\", msg.Type)\n\t}\n\tch, err := DecodeKIPClientHelloPayload(msg.Payload)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif absInt64(time.Now().Unix()-ch.Timestamp.Unix()) > int64(earlyKIPHandshakeTTL.Seconds()) {\n\t\treturn nil, fmt.Errorf(\"time skew/replay\")\n\t}\n\n\tuserHash := hex.EncodeToString(ch.UserHash[:])\n\tif allowReplay != nil && !allowReplay(userHash, ch.Nonce, time.Now()) {\n\t\treturn nil, fmt.Errorf(\"replay detected\")\n\t}\n\tresolvedTable, err := ResolveClientHelloTable(table, tables, ch)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"resolve table hint failed: %w\", err)\n\t}\n\n\tcurve := ecdh.X25519()\n\tserverEphemeral, err := curve.GenerateKey(rand.Reader)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"ecdh generate failed: %w\", err)\n\t}\n\tshared, err := x25519SharedSecret(serverEphemeral, ch.ClientPub[:])\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"ecdh failed: %w\", err)\n\t}\n\tsessionC2S, sessionS2C, err := deriveSessionDirectionalBases(cfg.PSK, shared, ch.Nonce)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"derive session keys failed: %w\", err)\n\t}\n\n\tvar serverPub [kipHelloPubSize]byte\n\tcopy(serverPub[:], serverEphemeral.PublicKey().Bytes())\n\tserverHello := &KIPServerHello{\n\t\tNonce:         ch.Nonce,\n\t\tServerPub:     serverPub,\n\t\tSelectedFeats: ch.Features & KIPFeatAll,\n\t}\n\n\trespMem := newEarlyMemoryConn(nil)\n\trespObfs := buildEarlyServerObfsConn(respMem, cfg, resolvedTable)\n\trespConn, err := crypto.NewRecordConn(respObfs, cfg.AEAD, pskS2C, pskC2S)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"server early crypto setup failed: %w\", err)\n\t}\n\tif err := WriteKIPMessage(respConn, KIPTypeServerHello, serverHello.EncodePayload()); err != nil {\n\t\treturn nil, fmt.Errorf(\"write early server hello failed: %w\", err)\n\t}\n\n\treturn &EarlyServerState{\n\t\tResponsePayload: respMem.Written(),\n\t\tUserHash:        userHash,\n\t\tcfg:             cfg,\n\t\ttable:           resolvedTable,\n\t\tsessionC2S:      sessionC2S,\n\t\tsessionS2C:      sessionS2C,\n\t}, nil\n}\n\nfunc (s *EarlyServerState) WrapConn(raw net.Conn) (net.Conn, error) {\n\tif s == nil {\n\t\treturn nil, fmt.Errorf(\"nil server state\")\n\t}\n\tobfsConn := buildEarlyServerObfsConn(raw, s.cfg, s.table)\n\trc, err := crypto.NewRecordConn(obfsConn, s.cfg.AEAD, s.sessionS2C, s.sessionC2S)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"setup server session crypto failed: %w\", err)\n\t}\n\treturn rc, nil\n}\n\nfunc NewHTTPMaskServerEarlyHandshake(cfg EarlyCodecConfig, tables []*sudokuobfs.Table, allowReplay ReplayAllowFunc) *httpmaskobfs.TunnelServerEarlyHandshake {\n\treturn &httpmaskobfs.TunnelServerEarlyHandshake{\n\t\tPrepare: func(payload []byte) (*httpmaskobfs.PreparedServerEarlyHandshake, error) {\n\t\t\tstate, err := ProcessEarlyClientPayload(cfg, tables, payload, allowReplay)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn &httpmaskobfs.PreparedServerEarlyHandshake{\n\t\t\t\tResponsePayload: state.ResponsePayload,\n\t\t\t\tWrapConn:        state.WrapConn,\n\t\t\t\tUserHash:        state.UserHash,\n\t\t\t}, nil\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/features_test.go",
    "content": "package sudoku\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"net\"\n\t\"testing\"\n\n\tsudokuobfs \"github.com/metacubex/mihomo/transport/sudoku/obfs/sudoku\"\n)\n\nfunc TestCustomTablesRotation_ProbedByServer(t *testing.T) {\n\tkey := \"rotate-test-key\"\n\ttarget := \"8.8.8.8:53\"\n\n\tt1, err := sudokuobfs.NewTableWithCustom(\"rotate-seed\", \"prefer_entropy\", \"xpxvvpvv\")\n\tif err != nil {\n\t\tt.Fatalf(\"t1: %v\", err)\n\t}\n\tt2, err := sudokuobfs.NewTableWithCustom(\"rotate-seed\", \"prefer_entropy\", \"vxpvxvvp\")\n\tif err != nil {\n\t\tt.Fatalf(\"t2: %v\", err)\n\t}\n\n\tserverCfg := DefaultConfig()\n\tserverCfg.Key = key\n\tserverCfg.AEADMethod = \"chacha20-poly1305\"\n\tserverCfg.Tables = []*sudokuobfs.Table{t1, t2}\n\tserverCfg.PaddingMin = 0\n\tserverCfg.PaddingMax = 0\n\tserverCfg.EnablePureDownlink = true\n\tserverCfg.HandshakeTimeoutSeconds = 5\n\tserverCfg.DisableHTTPMask = true\n\n\tclientCfg := DefaultConfig()\n\t*clientCfg = *serverCfg\n\tclientCfg.ServerAddress = \"example.com:443\"\n\n\tfor i := 0; i < 10; i++ {\n\t\tserverConn, clientConn := net.Pipe()\n\n\t\terrCh := make(chan error, 1)\n\t\tgo func() {\n\t\t\tdefer close(errCh)\n\t\t\tdefer serverConn.Close()\n\t\t\tc, meta, err := ServerHandshake(serverConn, serverCfg)\n\t\t\tif err != nil {\n\t\t\t\terrCh <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t\tsession, err := ReadServerSession(c, meta)\n\t\t\tif err != nil {\n\t\t\t\terrCh <- err\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdefer c.Close()\n\t\t\tif session.Type != SessionTypeTCP {\n\t\t\t\terrCh <- io.ErrUnexpectedEOF\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif session.Target != target {\n\t\t\t\terrCh <- io.ErrClosedPipe\n\t\t\t\treturn\n\t\t\t}\n\t\t\t_, _ = session.Conn.Write([]byte{0xaa, 0xbb, 0xcc})\n\t\t}()\n\n\t\tcConn, err := ClientHandshake(clientConn, clientCfg)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"client handshake: %v\", err)\n\t\t}\n\n\t\taddrBuf, err := EncodeAddress(target)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"encode addr: %v\", err)\n\t\t}\n\t\tif err := WriteKIPMessage(cConn, KIPTypeOpenTCP, addrBuf); err != nil {\n\t\t\tt.Fatalf(\"write addr: %v\", err)\n\t\t}\n\n\t\tbuf := make([]byte, 3)\n\t\tif _, err := io.ReadFull(cConn, buf); err != nil {\n\t\t\tt.Fatalf(\"read: %v\", err)\n\t\t}\n\t\tif !bytes.Equal(buf, []byte{0xaa, 0xbb, 0xcc}) {\n\t\t\tt.Fatalf(\"payload mismatch: %x\", buf)\n\t\t}\n\t\t_ = cConn.Close()\n\n\t\tif err := <-errCh; err != nil {\n\t\t\tt.Fatalf(\"server: %v\", err)\n\t\t}\n\t}\n}\n\nfunc TestDirectionalTrafficRoundTrip(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tmode string\n\t\tpure bool\n\t}{\n\t\t{name: \"UpASCII_DownEntropy_Pure\", mode: \"up_ascii_down_entropy\", pure: true},\n\t\t{name: \"UpASCII_DownEntropy_Packed\", mode: \"up_ascii_down_entropy\", pure: false},\n\t\t{name: \"UpEntropy_DownASCII_Pure\", mode: \"up_entropy_down_ascii\", pure: true},\n\t\t{name: \"UpEntropy_DownASCII_Packed\", mode: \"up_entropy_down_ascii\", pure: false},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tkey := \"directional-test-key-\" + tt.name\n\t\t\ttarget := \"8.8.8.8:53\"\n\n\t\t\ttable, err := sudokuobfs.NewTableWithCustom(ClientAEADSeed(key), tt.mode, \"xpxvvpvv\")\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"table: %v\", err)\n\t\t\t}\n\n\t\t\tserverCfg := DefaultConfig()\n\t\t\tserverCfg.Key = key\n\t\t\tserverCfg.AEADMethod = \"chacha20-poly1305\"\n\t\t\tserverCfg.Table = table\n\t\t\tserverCfg.PaddingMin = 0\n\t\t\tserverCfg.PaddingMax = 0\n\t\t\tserverCfg.EnablePureDownlink = tt.pure\n\t\t\tserverCfg.HandshakeTimeoutSeconds = 5\n\t\t\tserverCfg.DisableHTTPMask = true\n\n\t\t\tclientCfg := DefaultConfig()\n\t\t\t*clientCfg = *serverCfg\n\t\t\tclientCfg.ServerAddress = \"example.com:443\"\n\n\t\t\tserverConn, clientConn := net.Pipe()\n\t\t\tdefer clientConn.Close()\n\n\t\t\tserverErr := make(chan error, 1)\n\t\t\tgo func() {\n\t\t\t\tdefer close(serverErr)\n\t\t\t\tdefer serverConn.Close()\n\n\t\t\t\tc, meta, err := ServerHandshake(serverConn, serverCfg)\n\t\t\t\tif err != nil {\n\t\t\t\t\tserverErr <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tdefer c.Close()\n\n\t\t\t\tsession, err := ReadServerSession(c, meta)\n\t\t\t\tif err != nil {\n\t\t\t\t\tserverErr <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif session.Type != SessionTypeTCP {\n\t\t\t\t\tserverErr <- io.ErrUnexpectedEOF\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif session.Target != target {\n\t\t\t\t\tserverErr <- io.ErrClosedPipe\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\twant := []byte(\"client-payload\")\n\t\t\t\tgot := make([]byte, len(want))\n\t\t\t\tif _, err := io.ReadFull(session.Conn, got); err != nil {\n\t\t\t\t\tserverErr <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif !bytes.Equal(got, want) {\n\t\t\t\t\tserverErr <- io.ErrUnexpectedEOF\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif _, err := session.Conn.Write([]byte(\"server-reply\")); err != nil {\n\t\t\t\t\tserverErr <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}()\n\n\t\t\tcConn, err := ClientHandshake(clientConn, clientCfg)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"client handshake: %v\", err)\n\t\t\t}\n\t\t\tdefer cConn.Close()\n\n\t\t\taddrBuf, err := EncodeAddress(target)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"encode target: %v\", err)\n\t\t\t}\n\t\t\tif err := WriteKIPMessage(cConn, KIPTypeOpenTCP, addrBuf); err != nil {\n\t\t\t\tt.Fatalf(\"write target: %v\", err)\n\t\t\t}\n\t\t\tif _, err := cConn.Write([]byte(\"client-payload\")); err != nil {\n\t\t\t\tt.Fatalf(\"write payload: %v\", err)\n\t\t\t}\n\n\t\t\treply := make([]byte, len(\"server-reply\"))\n\t\t\tif _, err := io.ReadFull(cConn, reply); err != nil {\n\t\t\t\tt.Fatalf(\"read reply: %v\", err)\n\t\t\t}\n\t\t\tif !bytes.Equal(reply, []byte(\"server-reply\")) {\n\t\t\t\tt.Fatalf(\"unexpected reply: %q\", reply)\n\t\t\t}\n\n\t\t\tif err := <-serverErr; err != nil {\n\t\t\t\tt.Fatalf(\"server: %v\", err)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestDirectionalTrafficRoundTripTCP(t *testing.T) {\n\ttests := []struct {\n\t\tname string\n\t\tmode string\n\t\tpure bool\n\t}{\n\t\t{name: \"UpASCII_DownEntropy_Pure_TCP\", mode: \"up_ascii_down_entropy\", pure: true},\n\t\t{name: \"UpEntropy_DownASCII_Packed_TCP\", mode: \"up_entropy_down_ascii\", pure: false},\n\t}\n\n\tfor _, tt := range tests {\n\t\tt.Run(tt.name, func(t *testing.T) {\n\t\t\tkey := \"directional-tcp-test-key-\" + tt.name\n\t\t\ttarget := \"127.0.0.1:18080\"\n\n\t\t\ttable, err := sudokuobfs.NewTableWithCustom(ClientAEADSeed(key), tt.mode, \"xpxvvpvv\")\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"table: %v\", err)\n\t\t\t}\n\n\t\t\tserverCfg := DefaultConfig()\n\t\t\tserverCfg.Key = key\n\t\t\tserverCfg.AEADMethod = \"chacha20-poly1305\"\n\t\t\tserverCfg.Table = table\n\t\t\tserverCfg.PaddingMin = 0\n\t\t\tserverCfg.PaddingMax = 0\n\t\t\tserverCfg.EnablePureDownlink = tt.pure\n\t\t\tserverCfg.HandshakeTimeoutSeconds = 5\n\t\t\tserverCfg.DisableHTTPMask = true\n\n\t\t\tln, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"listen: %v\", err)\n\t\t\t}\n\t\t\tdefer ln.Close()\n\n\t\t\tserverErr := make(chan error, 1)\n\t\t\tgo func() {\n\t\t\t\tdefer close(serverErr)\n\t\t\t\traw, err := ln.Accept()\n\t\t\t\tif err != nil {\n\t\t\t\t\tserverErr <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tdefer raw.Close()\n\n\t\t\t\tc, meta, err := ServerHandshake(raw, serverCfg)\n\t\t\t\tif err != nil {\n\t\t\t\t\tserverErr <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tdefer c.Close()\n\n\t\t\t\tsession, err := ReadServerSession(c, meta)\n\t\t\t\tif err != nil {\n\t\t\t\t\tserverErr <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif session.Type != SessionTypeTCP || session.Target != target {\n\t\t\t\t\tserverErr <- io.ErrUnexpectedEOF\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\twant := []byte(\"client-payload\")\n\t\t\t\tgot := make([]byte, len(want))\n\t\t\t\tif _, err := io.ReadFull(session.Conn, got); err != nil {\n\t\t\t\t\tserverErr <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif !bytes.Equal(got, want) {\n\t\t\t\t\tserverErr <- io.ErrUnexpectedEOF\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif _, err := session.Conn.Write([]byte(\"server-reply\")); err != nil {\n\t\t\t\t\tserverErr <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}()\n\n\t\t\tclientCfg := DefaultConfig()\n\t\t\t*clientCfg = *serverCfg\n\t\t\tclientCfg.ServerAddress = ln.Addr().String()\n\n\t\t\traw, err := net.Dial(\"tcp\", clientCfg.ServerAddress)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"dial: %v\", err)\n\t\t\t}\n\t\t\tdefer raw.Close()\n\n\t\t\tcConn, err := ClientHandshake(raw, clientCfg)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"client handshake: %v\", err)\n\t\t\t}\n\t\t\tdefer cConn.Close()\n\n\t\t\taddrBuf, err := EncodeAddress(target)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"encode target: %v\", err)\n\t\t\t}\n\t\t\tif err := WriteKIPMessage(cConn, KIPTypeOpenTCP, addrBuf); err != nil {\n\t\t\t\tt.Fatalf(\"write target: %v\", err)\n\t\t\t}\n\t\t\tif _, err := cConn.Write([]byte(\"client-payload\")); err != nil {\n\t\t\t\tt.Fatalf(\"write payload: %v\", err)\n\t\t\t}\n\n\t\t\treply := make([]byte, len(\"server-reply\"))\n\t\t\tif _, err := io.ReadFull(cConn, reply); err != nil {\n\t\t\t\tt.Fatalf(\"read reply: %v\", err)\n\t\t\t}\n\t\t\tif !bytes.Equal(reply, []byte(\"server-reply\")) {\n\t\t\t\tt.Fatalf(\"unexpected reply: %q\", reply)\n\t\t\t}\n\n\t\t\tif err := <-serverErr; err != nil {\n\t\t\t\tt.Fatalf(\"server: %v\", err)\n\t\t\t}\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/handshake.go",
    "content": "package sudoku\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"crypto/ecdh\"\n\t\"crypto/rand\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/transport/sudoku/crypto\"\n\t\"github.com/metacubex/mihomo/transport/sudoku/obfs/httpmask\"\n\t\"github.com/metacubex/mihomo/transport/sudoku/obfs/sudoku\"\n)\n\ntype SessionType int\n\nconst (\n\tSessionTypeTCP SessionType = iota\n\tSessionTypeUoT\n\tSessionTypeMultiplex\n)\n\ntype ServerSession struct {\n\tConn   net.Conn\n\tType   SessionType\n\tTarget string\n\n\t// UserHash is a stable per-key identifier derived from the client hello payload.\n\tUserHash string\n}\n\ntype HandshakeMeta struct {\n\tUserHash string\n}\n\n// SuspiciousError indicates a potential probing attempt or protocol violation.\n// When returned, Conn (if non-nil) should contain all bytes already consumed/buffered so the caller\n// can perform a best-effort fallback relay (e.g. to a local web server) without losing the request.\ntype SuspiciousError struct {\n\tErr  error\n\tConn net.Conn\n}\n\nfunc (e *SuspiciousError) Error() string {\n\tif e == nil || e.Err == nil {\n\t\treturn \"\"\n\t}\n\treturn e.Err.Error()\n}\n\nfunc (e *SuspiciousError) Unwrap() error { return e.Err }\n\ntype recordedConn struct {\n\tnet.Conn\n\trecorded []byte\n}\n\nfunc (rc *recordedConn) GetBufferedAndRecorded() []byte { return rc.recorded }\n\ntype prefixedRecorderConn struct {\n\tnet.Conn\n\tprefix []byte\n}\n\nfunc (pc *prefixedRecorderConn) GetBufferedAndRecorded() []byte {\n\tvar rest []byte\n\tif r, ok := pc.Conn.(interface{ GetBufferedAndRecorded() []byte }); ok {\n\t\trest = r.GetBufferedAndRecorded()\n\t}\n\tout := make([]byte, 0, len(pc.prefix)+len(rest))\n\tout = append(out, pc.prefix...)\n\tout = append(out, rest...)\n\treturn out\n}\n\n// bufferedRecorderConn wraps a net.Conn and a shared bufio.Reader so we can expose buffered bytes.\n// This is used for legacy HTTP mask parsing errors so callers can fall back to a real HTTP server.\ntype bufferedRecorderConn struct {\n\tnet.Conn\n\tr        *bufio.Reader\n\trecorder *bytes.Buffer\n\tmu       sync.Mutex\n}\n\nfunc (bc *bufferedRecorderConn) Read(p []byte) (n int, err error) {\n\tn, err = bc.r.Read(p)\n\tif n > 0 && bc.recorder != nil {\n\t\tbc.mu.Lock()\n\t\tbc.recorder.Write(p[:n])\n\t\tbc.mu.Unlock()\n\t}\n\treturn n, err\n}\n\nfunc (bc *bufferedRecorderConn) GetBufferedAndRecorded() []byte {\n\tif bc == nil {\n\t\treturn nil\n\t}\n\tbc.mu.Lock()\n\tdefer bc.mu.Unlock()\n\n\tvar recorded []byte\n\tif bc.recorder != nil {\n\t\trecorded = bc.recorder.Bytes()\n\t}\n\tbuffered := 0\n\tif bc.r != nil {\n\t\tbuffered = bc.r.Buffered()\n\t}\n\tif buffered <= 0 {\n\t\treturn recorded\n\t}\n\tpeeked, _ := bc.r.Peek(buffered)\n\tfull := make([]byte, len(recorded)+len(peeked))\n\tcopy(full, recorded)\n\tcopy(full[len(recorded):], peeked)\n\treturn full\n}\n\ntype preBufferedConn struct {\n\tnet.Conn\n\tbuf []byte\n}\n\nfunc (p *preBufferedConn) Read(b []byte) (int, error) {\n\tif len(p.buf) > 0 {\n\t\tn := copy(b, p.buf)\n\t\tp.buf = p.buf[n:]\n\t\treturn n, nil\n\t}\n\tif p.Conn == nil {\n\t\treturn 0, io.EOF\n\t}\n\treturn p.Conn.Read(b)\n}\n\nfunc (p *preBufferedConn) CloseWrite() error {\n\tif p == nil {\n\t\treturn nil\n\t}\n\tif cw, ok := p.Conn.(interface{ CloseWrite() error }); ok {\n\t\treturn cw.CloseWrite()\n\t}\n\treturn nil\n}\n\nfunc (p *preBufferedConn) CloseRead() error {\n\tif p == nil {\n\t\treturn nil\n\t}\n\tif cr, ok := p.Conn.(interface{ CloseRead() error }); ok {\n\t\treturn cr.CloseRead()\n\t}\n\treturn nil\n}\n\ntype directionalConn struct {\n\tnet.Conn\n\treader  io.Reader\n\twriter  io.Writer\n\tclosers []func() error\n}\n\nfunc newDirectionalConn(base net.Conn, reader io.Reader, writer io.Writer, closers ...func() error) net.Conn {\n\treturn &directionalConn{\n\t\tConn:    base,\n\t\treader:  reader,\n\t\twriter:  writer,\n\t\tclosers: closers,\n\t}\n}\n\nfunc (c *directionalConn) Read(p []byte) (int, error) {\n\treturn c.reader.Read(p)\n}\n\nfunc (c *directionalConn) Write(p []byte) (int, error) {\n\treturn c.writer.Write(p)\n}\n\nfunc (c *directionalConn) ReplaceWriter(writer io.Writer, closers ...func() error) {\n\tif c == nil {\n\t\treturn\n\t}\n\tc.writer = writer\n\tc.closers = closers\n}\n\nfunc (c *directionalConn) Close() error {\n\tvar firstErr error\n\tfor _, fn := range c.closers {\n\t\tif fn == nil {\n\t\t\tcontinue\n\t\t}\n\t\tif err := fn(); err != nil && firstErr == nil {\n\t\t\tfirstErr = err\n\t\t}\n\t}\n\tif err := c.Conn.Close(); err != nil && firstErr == nil {\n\t\tfirstErr = err\n\t}\n\treturn firstErr\n}\n\nfunc (c *directionalConn) CloseWrite() error {\n\tif c == nil {\n\t\treturn nil\n\t}\n\tif cw, ok := c.Conn.(interface{ CloseWrite() error }); ok {\n\t\treturn cw.CloseWrite()\n\t}\n\treturn nil\n}\n\nfunc (c *directionalConn) CloseRead() error {\n\tif c == nil {\n\t\treturn nil\n\t}\n\tif cr, ok := c.Conn.(interface{ CloseRead() error }); ok {\n\t\treturn cr.CloseRead()\n\t}\n\treturn nil\n}\n\nfunc absInt64(v int64) int64 {\n\tif v < 0 {\n\t\treturn -v\n\t}\n\treturn v\n}\n\nfunc oppositeDirectionTable(table *sudoku.Table) *sudoku.Table {\n\tif table == nil {\n\t\treturn nil\n\t}\n\tif other := table.OppositeDirection(); other != nil {\n\t\treturn other\n\t}\n\treturn table\n}\n\nfunc newClientDownlinkReader(raw net.Conn, table *sudoku.Table, paddingMin, paddingMax int, pureDownlink bool) io.Reader {\n\tdownlinkTable := oppositeDirectionTable(table)\n\tif pureDownlink {\n\t\tif downlinkTable == table {\n\t\t\treturn nil\n\t\t}\n\t\treturn sudoku.NewConn(raw, downlinkTable, paddingMin, paddingMax, false)\n\t}\n\treturn sudoku.NewPackedConn(raw, downlinkTable, paddingMin, paddingMax)\n}\n\nfunc newServerDownlinkWriter(raw net.Conn, table *sudoku.Table, paddingMin, paddingMax int, pureDownlink bool) (io.Writer, []func() error) {\n\tdownlinkTable := oppositeDirectionTable(table)\n\tif pureDownlink {\n\t\tif downlinkTable == table {\n\t\t\treturn nil, nil\n\t\t}\n\t\treturn sudoku.NewConn(raw, downlinkTable, paddingMin, paddingMax, false), nil\n\t}\n\tpacked := sudoku.NewPackedConn(raw, downlinkTable, paddingMin, paddingMax)\n\treturn packed, []func() error{packed.Flush}\n}\n\nfunc buildClientObfsConn(raw net.Conn, cfg *ProtocolConfig, table *sudoku.Table) net.Conn {\n\tbaseSudoku := sudoku.NewConn(raw, table, cfg.PaddingMin, cfg.PaddingMax, false)\n\tdownlinkReader := newClientDownlinkReader(raw, table, cfg.PaddingMin, cfg.PaddingMax, cfg.EnablePureDownlink)\n\tif downlinkReader == nil {\n\t\treturn baseSudoku\n\t}\n\treturn newDirectionalConn(raw, downlinkReader, baseSudoku)\n}\n\nfunc buildServerObfsConn(raw net.Conn, cfg *ProtocolConfig, table *sudoku.Table, record bool) (*sudoku.Conn, net.Conn) {\n\tuplinkSudoku := sudoku.NewConn(raw, table, cfg.PaddingMin, cfg.PaddingMax, record)\n\tdownlinkWriter, closers := newServerDownlinkWriter(raw, table, cfg.PaddingMin, cfg.PaddingMax, cfg.EnablePureDownlink)\n\tif downlinkWriter == nil {\n\t\treturn uplinkSudoku, uplinkSudoku\n\t}\n\treturn uplinkSudoku, newDirectionalConn(raw, uplinkSudoku, downlinkWriter, closers...)\n}\n\nfunc isLegacyHTTPMaskMode(mode string) bool {\n\tswitch strings.ToLower(strings.TrimSpace(mode)) {\n\tcase \"\", \"legacy\":\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\n// ClientHandshake performs the client-side Sudoku handshake (no target request).\nfunc ClientHandshake(rawConn net.Conn, cfg *ProtocolConfig) (net.Conn, error) {\n\tif cfg == nil {\n\t\treturn nil, fmt.Errorf(\"config is required\")\n\t}\n\tif err := cfg.Validate(); err != nil {\n\t\treturn nil, fmt.Errorf(\"invalid config: %w\", err)\n\t}\n\n\tif !cfg.DisableHTTPMask && isLegacyHTTPMaskMode(cfg.HTTPMaskMode) {\n\t\tif err := httpmask.WriteRandomRequestHeaderWithPathRoot(rawConn, cfg.ServerAddress, cfg.HTTPMaskPathRoot); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"write http mask failed: %w\", err)\n\t\t}\n\t}\n\n\tchoice, err := pickClientTable(cfg)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tseed := ClientAEADSeed(cfg.Key)\n\tobfsConn := buildClientObfsConn(rawConn, cfg, choice.Table)\n\tpskC2S, pskS2C := derivePSKDirectionalBases(seed)\n\trc, err := crypto.NewRecordConn(obfsConn, cfg.AEADMethod, pskC2S, pskS2C)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"setup crypto failed: %w\", err)\n\t}\n\n\tif _, err := kipHandshakeClient(rc, seed, kipUserHashFromKey(cfg.Key), KIPFeatAll, choice.Hint, choice.HasHint); err != nil {\n\t\t_ = rc.Close()\n\t\treturn nil, err\n\t}\n\n\treturn rc, nil\n}\n\nfunc readFirstSessionMessage(conn net.Conn) (*KIPMessage, error) {\n\tfor {\n\t\tmsg, err := ReadKIPMessage(conn)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif msg.Type == KIPTypeKeepAlive {\n\t\t\tcontinue\n\t\t}\n\t\treturn msg, nil\n\t}\n}\n\nfunc maybeConsumeLegacyHTTPMask(rawConn net.Conn, r *bufio.Reader, cfg *ProtocolConfig) ([]byte, *SuspiciousError) {\n\tif rawConn == nil || r == nil || cfg == nil || cfg.DisableHTTPMask || !isLegacyHTTPMaskMode(cfg.HTTPMaskMode) {\n\t\treturn nil, nil\n\t}\n\n\tpeekBytes, _ := r.Peek(4) // ignore error; subsequent read will handle it\n\tif !httpmask.LooksLikeHTTPRequestStart(peekBytes) {\n\t\treturn nil, nil\n\t}\n\n\tconsumed, err := httpmask.ConsumeHeader(r)\n\tif err == nil {\n\t\treturn consumed, nil\n\t}\n\n\trecorder := new(bytes.Buffer)\n\tif len(consumed) > 0 {\n\t\trecorder.Write(consumed)\n\t}\n\tbadConn := &bufferedRecorderConn{Conn: rawConn, r: r, recorder: recorder}\n\treturn consumed, &SuspiciousError{Err: fmt.Errorf(\"invalid http header: %w\", err), Conn: badConn}\n}\n\n// ServerHandshake performs the server-side KIP handshake.\nfunc ServerHandshake(rawConn net.Conn, cfg *ProtocolConfig) (net.Conn, *HandshakeMeta, error) {\n\tif rawConn == nil {\n\t\treturn nil, nil, fmt.Errorf(\"nil conn\")\n\t}\n\tif cfg == nil {\n\t\treturn nil, nil, fmt.Errorf(\"config is required\")\n\t}\n\tif err := cfg.Validate(); err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"invalid config: %w\", err)\n\t}\n\tif userHash, ok := httpmask.EarlyHandshakeUserHash(rawConn); ok {\n\t\treturn rawConn, &HandshakeMeta{UserHash: userHash}, nil\n\t}\n\n\thandshakeTimeout := time.Duration(cfg.HandshakeTimeoutSeconds) * time.Second\n\tif handshakeTimeout <= 0 {\n\t\thandshakeTimeout = 5 * time.Second\n\t}\n\n\tbufReader := bufio.NewReader(rawConn)\n\t_ = rawConn.SetReadDeadline(time.Now().Add(handshakeTimeout))\n\tdefer func() { _ = rawConn.SetReadDeadline(time.Time{}) }()\n\n\thttpHeaderData, susp := maybeConsumeLegacyHTTPMask(rawConn, bufReader, cfg)\n\tif susp != nil {\n\t\treturn nil, nil, susp\n\t}\n\n\tselectedTable, preRead, err := selectTableByProbe(bufReader, cfg, cfg.tableCandidates())\n\tif err != nil {\n\t\tcombined := make([]byte, 0, len(httpHeaderData)+len(preRead))\n\t\tcombined = append(combined, httpHeaderData...)\n\t\tcombined = append(combined, preRead...)\n\t\treturn nil, nil, &SuspiciousError{Err: err, Conn: &recordedConn{Conn: rawConn, recorded: combined}}\n\t}\n\n\tbaseConn := &preBufferedConn{Conn: rawConn, buf: preRead}\n\tsConn, obfsConn := buildServerObfsConn(baseConn, cfg, selectedTable, true)\n\n\tseed := ServerAEADSeed(cfg.Key)\n\tpskC2S, pskS2C := derivePSKDirectionalBases(seed)\n\t// Server side: recv is client->server, send is server->client.\n\trc, err := crypto.NewRecordConn(obfsConn, cfg.AEADMethod, pskS2C, pskC2S)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"setup crypto failed: %w\", err)\n\t}\n\n\tmsg, err := ReadKIPMessage(rc)\n\tif err != nil {\n\t\treturn nil, nil, &SuspiciousError{Err: fmt.Errorf(\"handshake read failed: %w\", err), Conn: &prefixedRecorderConn{Conn: sConn, prefix: httpHeaderData}}\n\t}\n\tif msg.Type != KIPTypeClientHello {\n\t\treturn nil, nil, &SuspiciousError{Err: fmt.Errorf(\"unexpected handshake message: %d\", msg.Type), Conn: &prefixedRecorderConn{Conn: sConn, prefix: httpHeaderData}}\n\t}\n\tch, err := DecodeKIPClientHelloPayload(msg.Payload)\n\tif err != nil {\n\t\treturn nil, nil, &SuspiciousError{Err: fmt.Errorf(\"decode client hello failed: %w\", err), Conn: &prefixedRecorderConn{Conn: sConn, prefix: httpHeaderData}}\n\t}\n\tif absInt64(time.Now().Unix()-ch.Timestamp.Unix()) > int64(kipHandshakeSkew.Seconds()) {\n\t\treturn nil, nil, &SuspiciousError{Err: fmt.Errorf(\"time skew/replay\"), Conn: &prefixedRecorderConn{Conn: sConn, prefix: httpHeaderData}}\n\t}\n\n\tuserHashHex := hex.EncodeToString(ch.UserHash[:])\n\tif !globalHandshakeReplay.allow(userHashHex, ch.Nonce, time.Now()) {\n\t\treturn nil, nil, &SuspiciousError{Err: fmt.Errorf(\"replay\"), Conn: &prefixedRecorderConn{Conn: sConn, prefix: httpHeaderData}}\n\t}\n\tresolvedTable, err := ResolveClientHelloTable(selectedTable, cfg.tableCandidates(), ch)\n\tif err != nil {\n\t\treturn nil, nil, &SuspiciousError{Err: fmt.Errorf(\"resolve table hint failed: %w\", err), Conn: &prefixedRecorderConn{Conn: sConn, prefix: httpHeaderData}}\n\t}\n\tif resolvedTable != selectedTable {\n\t\tdownlinkWriter, closers := newServerDownlinkWriter(baseConn, resolvedTable, cfg.PaddingMin, cfg.PaddingMax, cfg.EnablePureDownlink)\n\t\tswitchable, ok := obfsConn.(*directionalConn)\n\t\tif !ok {\n\t\t\treturn nil, nil, &SuspiciousError{Err: fmt.Errorf(\"switch downlink writer failed\"), Conn: &prefixedRecorderConn{Conn: sConn, prefix: httpHeaderData}}\n\t\t}\n\t\tswitchable.ReplaceWriter(downlinkWriter, closers...)\n\t}\n\n\tcurve := ecdh.X25519()\n\tserverEphemeral, err := curve.GenerateKey(rand.Reader)\n\tif err != nil {\n\t\treturn nil, nil, &SuspiciousError{Err: fmt.Errorf(\"ecdh generate failed: %w\", err), Conn: &prefixedRecorderConn{Conn: sConn, prefix: httpHeaderData}}\n\t}\n\tshared, err := x25519SharedSecret(serverEphemeral, ch.ClientPub[:])\n\tif err != nil {\n\t\treturn nil, nil, &SuspiciousError{Err: fmt.Errorf(\"ecdh failed: %w\", err), Conn: &prefixedRecorderConn{Conn: sConn, prefix: httpHeaderData}}\n\t}\n\tsessC2S, sessS2C, err := deriveSessionDirectionalBases(seed, shared, ch.Nonce)\n\tif err != nil {\n\t\treturn nil, nil, &SuspiciousError{Err: fmt.Errorf(\"derive session keys failed: %w\", err), Conn: &prefixedRecorderConn{Conn: sConn, prefix: httpHeaderData}}\n\t}\n\n\tvar serverPub [kipHelloPubSize]byte\n\tcopy(serverPub[:], serverEphemeral.PublicKey().Bytes())\n\tsh := &KIPServerHello{\n\t\tNonce:         ch.Nonce,\n\t\tServerPub:     serverPub,\n\t\tSelectedFeats: ch.Features & KIPFeatAll,\n\t}\n\tif err := WriteKIPMessage(rc, KIPTypeServerHello, sh.EncodePayload()); err != nil {\n\t\treturn nil, nil, &SuspiciousError{Err: fmt.Errorf(\"write server hello failed: %w\", err), Conn: &prefixedRecorderConn{Conn: sConn, prefix: httpHeaderData}}\n\t}\n\tif err := rc.Rekey(sessS2C, sessC2S); err != nil {\n\t\treturn nil, nil, &SuspiciousError{Err: fmt.Errorf(\"rekey failed: %w\", err), Conn: &prefixedRecorderConn{Conn: sConn, prefix: httpHeaderData}}\n\t}\n\n\tsConn.StopRecording()\n\treturn rc, &HandshakeMeta{UserHash: userHashHex}, nil\n}\n\n// ReadServerSession consumes the first post-handshake KIP control message and returns the session intent.\nfunc ReadServerSession(conn net.Conn, meta *HandshakeMeta) (*ServerSession, error) {\n\tif conn == nil {\n\t\treturn nil, fmt.Errorf(\"nil conn\")\n\t}\n\tuserHash := \"\"\n\tif meta != nil {\n\t\tuserHash = meta.UserHash\n\t}\n\n\tfirst, err := readFirstSessionMessage(conn)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tswitch first.Type {\n\tcase KIPTypeStartUoT:\n\t\treturn &ServerSession{Conn: conn, Type: SessionTypeUoT, UserHash: userHash}, nil\n\tcase KIPTypeStartMux:\n\t\treturn &ServerSession{Conn: conn, Type: SessionTypeMultiplex, UserHash: userHash}, nil\n\tcase KIPTypeOpenTCP:\n\t\ttarget, err := DecodeAddress(bytes.NewReader(first.Payload))\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"decode target address failed: %w\", err)\n\t\t}\n\t\treturn &ServerSession{Conn: conn, Type: SessionTypeTCP, Target: target, UserHash: userHash}, nil\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown kip message: %d\", first.Type)\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/handshake_kip.go",
    "content": "package sudoku\n\nimport (\n\t\"crypto/ecdh\"\n\t\"crypto/rand\"\n\t\"fmt\"\n\t\"io\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/transport/sudoku/crypto\"\n)\n\nconst kipHandshakeSkew = 60 * time.Second\n\nfunc kipHandshakeClient(rc *crypto.RecordConn, seed string, userHash [kipHelloUserHashSize]byte, feats uint32, tableHint uint32, hasTableHint bool) (uint32, error) {\n\tif rc == nil {\n\t\treturn 0, fmt.Errorf(\"nil conn\")\n\t}\n\n\tcurve := ecdh.X25519()\n\tephemeral, err := curve.GenerateKey(rand.Reader)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"ecdh generate failed: %w\", err)\n\t}\n\n\tvar nonce [kipHelloNonceSize]byte\n\tif _, err := io.ReadFull(rand.Reader, nonce[:]); err != nil {\n\t\treturn 0, fmt.Errorf(\"nonce generate failed: %w\", err)\n\t}\n\n\tvar clientPub [kipHelloPubSize]byte\n\tcopy(clientPub[:], ephemeral.PublicKey().Bytes())\n\n\tch := newKIPClientHello(userHash, nonce, clientPub, feats, tableHint, hasTableHint)\n\tif err := WriteKIPMessage(rc, KIPTypeClientHello, ch.EncodePayload()); err != nil {\n\t\treturn 0, fmt.Errorf(\"write client hello failed: %w\", err)\n\t}\n\n\tmsg, err := ReadKIPMessage(rc)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"read server hello failed: %w\", err)\n\t}\n\tif msg.Type != KIPTypeServerHello {\n\t\treturn 0, fmt.Errorf(\"unexpected handshake message: %d\", msg.Type)\n\t}\n\tsh, err := DecodeKIPServerHelloPayload(msg.Payload)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"decode server hello failed: %w\", err)\n\t}\n\tif sh.Nonce != nonce {\n\t\treturn 0, fmt.Errorf(\"handshake nonce mismatch\")\n\t}\n\n\tshared, err := x25519SharedSecret(ephemeral, sh.ServerPub[:])\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"ecdh failed: %w\", err)\n\t}\n\tsessC2S, sessS2C, err := deriveSessionDirectionalBases(seed, shared, nonce)\n\tif err != nil {\n\t\treturn 0, fmt.Errorf(\"derive session keys failed: %w\", err)\n\t}\n\tif err := rc.Rekey(sessC2S, sessS2C); err != nil {\n\t\treturn 0, fmt.Errorf(\"rekey failed: %w\", err)\n\t}\n\n\treturn sh.SelectedFeats, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/handshake_test.go",
    "content": "package sudoku\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n\n\tsudokuobfs \"github.com/metacubex/mihomo/transport/sudoku/obfs/sudoku\"\n)\n\nfunc TestPackedConnRoundTrip_WithPadding(t *testing.T) {\n\tpayload := []byte{0x3a, 0x1f, 0x71, 0x00, 0xff, 0x10, 0x22}\n\ttableTypes := []string{\"prefer_ascii\", \"prefer_entropy\"}\n\n\tfor _, tt := range tableTypes {\n\t\tt.Run(tt, func(t *testing.T) {\n\t\t\tserverConn, clientConn := net.Pipe()\n\t\t\tdefer serverConn.Close()\n\t\t\tdefer clientConn.Close()\n\n\t\t\ttable := sudokuobfs.NewTable(\"roundtrip-seed\", tt)\n\t\t\twriter := sudokuobfs.NewPackedConn(serverConn, table, 30, 80)\n\t\t\treader := sudokuobfs.NewPackedConn(clientConn, table, 30, 80)\n\n\t\t\twriteErr := make(chan error, 1)\n\t\t\tgo func() {\n\t\t\t\tif _, err := writer.Write(payload); err != nil {\n\t\t\t\t\twriteErr <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif err := writer.Flush(); err != nil {\n\t\t\t\t\twriteErr <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\twriteErr <- serverConn.Close()\n\t\t\t}()\n\n\t\t\tdone := make(chan struct{})\n\t\t\tvar got []byte\n\t\t\tvar readErr error\n\t\t\tgo func() {\n\t\t\t\tgot, readErr = io.ReadAll(reader)\n\t\t\t\tclose(done)\n\t\t\t}()\n\n\t\t\tselect {\n\t\t\tcase <-done:\n\t\t\tcase <-time.After(5 * time.Second):\n\t\t\t\tt.Fatal(\"read timeout\")\n\t\t\t}\n\n\t\t\tif err := <-writeErr; err != nil && err != io.EOF {\n\t\t\t\tt.Fatalf(\"write side error: %v\", err)\n\t\t\t}\n\t\t\tif readErr != nil && readErr != io.EOF {\n\t\t\t\tt.Fatalf(\"read side error: %v\", readErr)\n\t\t\t}\n\t\t\tif !bytes.Equal(got, payload) {\n\t\t\t\tt.Fatalf(\"payload mismatch, want %x got %x\", payload, got)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc newPackedConfig(table *sudokuobfs.Table) *ProtocolConfig {\n\tcfg := DefaultConfig()\n\tcfg.Key = \"sudoku-test-key\"\n\tcfg.Table = table\n\tcfg.PaddingMin = 10\n\tcfg.PaddingMax = 30\n\tcfg.EnablePureDownlink = false\n\tcfg.ServerAddress = \"example.com:443\"\n\tcfg.DisableHTTPMask = true\n\treturn cfg\n}\n\nfunc TestPackedDownlinkSoak(t *testing.T) {\n\tconst sessions = 16\n\n\ttable := sudokuobfs.NewTable(\"soak-seed\", \"prefer_ascii\")\n\tcfg := newPackedConfig(table)\n\n\tvar wg sync.WaitGroup\n\terrCh := make(chan error, sessions*2)\n\n\tfor i := 0; i < sessions; i++ {\n\t\twg.Add(2)\n\t\tgo func(id int) {\n\t\t\tdefer wg.Done()\n\t\t\trunPackedTCPSession(id, cfg, errCh)\n\t\t}(i)\n\t\tgo func(id int) {\n\t\t\tdefer wg.Done()\n\t\t\trunPackedUoTSession(id, cfg, errCh)\n\t\t}(i)\n\t}\n\n\tdone := make(chan struct{})\n\tgo func() {\n\t\twg.Wait()\n\t\tclose(done)\n\t}()\n\n\tselect {\n\tcase <-done:\n\tcase <-time.After(10 * time.Second):\n\t\tt.Fatal(\"soak test timeout\")\n\t}\n\n\tclose(errCh)\n\tfor err := range errCh {\n\t\tt.Fatalf(\"soak error: %v\", err)\n\t}\n}\n\nfunc runPackedTCPSession(id int, cfg *ProtocolConfig, errCh chan<- error) {\n\tserverConn, clientConn := net.Pipe()\n\ttarget := fmt.Sprintf(\"1.1.1.%d:80\", (id%200)+1)\n\tpayload := []byte{0x42, byte(id)}\n\n\t// Server side\n\tgo func() {\n\t\tc, meta, err := ServerHandshake(serverConn, cfg)\n\t\tif err != nil {\n\t\t\terrCh <- fmt.Errorf(\"server handshake tcp: %w\", err)\n\t\t\treturn\n\t\t}\n\t\tdefer c.Close()\n\n\t\tsession, err := ReadServerSession(c, meta)\n\t\tif err != nil {\n\t\t\terrCh <- fmt.Errorf(\"server read session tcp: %w\", err)\n\t\t\treturn\n\t\t}\n\t\tif session.Type != SessionTypeTCP {\n\t\t\terrCh <- fmt.Errorf(\"unexpected session type: %v\", session.Type)\n\t\t\treturn\n\t\t}\n\t\tif session.Target != target {\n\t\t\terrCh <- fmt.Errorf(\"target mismatch want %s got %s\", target, session.Target)\n\t\t\treturn\n\t\t}\n\t\tif _, err := session.Conn.Write(payload); err != nil {\n\t\t\terrCh <- fmt.Errorf(\"server write: %w\", err)\n\t\t\treturn\n\t\t}\n\t}()\n\n\t// Client side\n\tclientCfg := *cfg\n\tcConn, err := ClientHandshake(clientConn, &clientCfg)\n\tif err != nil {\n\t\terrCh <- fmt.Errorf(\"client handshake tcp: %w\", err)\n\t\treturn\n\t}\n\tdefer cConn.Close()\n\n\taddrBuf, err := EncodeAddress(target)\n\tif err != nil {\n\t\terrCh <- fmt.Errorf(\"encode address: %w\", err)\n\t\treturn\n\t}\n\tif err := WriteKIPMessage(cConn, KIPTypeOpenTCP, addrBuf); err != nil {\n\t\terrCh <- fmt.Errorf(\"client send open tcp: %w\", err)\n\t\treturn\n\t}\n\n\tbuf := make([]byte, len(payload))\n\tif _, err := io.ReadFull(cConn, buf); err != nil {\n\t\terrCh <- fmt.Errorf(\"client read: %w\", err)\n\t\treturn\n\t}\n\tif !bytes.Equal(buf, payload) {\n\t\terrCh <- fmt.Errorf(\"payload mismatch want %x got %x\", payload, buf)\n\t\treturn\n\t}\n}\n\nfunc runPackedUoTSession(id int, cfg *ProtocolConfig, errCh chan<- error) {\n\tserverConn, clientConn := net.Pipe()\n\ttarget := \"8.8.8.8:53\"\n\tpayload := []byte{0xaa, byte(id)}\n\n\t// Server side\n\tgo func() {\n\t\tc, meta, err := ServerHandshake(serverConn, cfg)\n\t\tif err != nil {\n\t\t\terrCh <- fmt.Errorf(\"server handshake uot: %w\", err)\n\t\t\treturn\n\t\t}\n\t\tdefer c.Close()\n\n\t\tsession, err := ReadServerSession(c, meta)\n\t\tif err != nil {\n\t\t\terrCh <- fmt.Errorf(\"server read session uot: %w\", err)\n\t\t\treturn\n\t\t}\n\t\tif session.Type != SessionTypeUoT {\n\t\t\terrCh <- fmt.Errorf(\"unexpected session type: %v\", session.Type)\n\t\t\treturn\n\t\t}\n\t\tif err := WriteDatagram(session.Conn, target, payload); err != nil {\n\t\t\terrCh <- fmt.Errorf(\"server write datagram: %w\", err)\n\t\t\treturn\n\t\t}\n\t}()\n\n\t// Client side\n\tclientCfg := *cfg\n\tcConn, err := ClientHandshake(clientConn, &clientCfg)\n\tif err != nil {\n\t\terrCh <- fmt.Errorf(\"client handshake uot: %w\", err)\n\t\treturn\n\t}\n\tdefer cConn.Close()\n\n\tif err := WriteKIPMessage(cConn, KIPTypeStartUoT, nil); err != nil {\n\t\terrCh <- fmt.Errorf(\"client start uot: %w\", err)\n\t\treturn\n\t}\n\n\taddr, data, err := ReadDatagram(cConn)\n\tif err != nil {\n\t\terrCh <- fmt.Errorf(\"client read datagram: %w\", err)\n\t\treturn\n\t}\n\tif addr != target {\n\t\terrCh <- fmt.Errorf(\"uot target mismatch want %s got %s\", target, addr)\n\t\treturn\n\t}\n\tif !bytes.Equal(data, payload) {\n\t\terrCh <- fmt.Errorf(\"uot payload mismatch want %x got %x\", payload, data)\n\t\treturn\n\t}\n}\n\nfunc TestCustomTableHandshake(t *testing.T) {\n\ttable, err := sudokuobfs.NewTableWithCustom(\"custom-seed\", \"prefer_entropy\", \"xpxvvpvv\")\n\tif err != nil {\n\t\tt.Fatalf(\"build custom table: %v\", err)\n\t}\n\tcfg := newPackedConfig(table)\n\terrCh := make(chan error, 2)\n\n\trunPackedTCPSession(42, cfg, errCh)\n\trunPackedUoTSession(43, cfg, errCh)\n\n\tclose(errCh)\n\tfor err := range errCh {\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"custom table handshake failed: %v\", err)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/httpmask_tunnel.go",
    "content": "package sudoku\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/transport/sudoku/obfs/httpmask\"\n)\n\ntype HTTPMaskTunnelServer struct {\n\tcfg *ProtocolConfig\n\tts  *httpmask.TunnelServer\n}\n\nfunc newHTTPMaskEarlyCodecConfig(cfg *ProtocolConfig, psk string) EarlyCodecConfig {\n\treturn EarlyCodecConfig{\n\t\tPSK:                psk,\n\t\tAEAD:               cfg.AEADMethod,\n\t\tEnablePureDownlink: cfg.EnablePureDownlink,\n\t\tPaddingMin:         cfg.PaddingMin,\n\t\tPaddingMax:         cfg.PaddingMax,\n\t}\n}\n\nfunc newClientHTTPMaskEarlyHandshake(cfg *ProtocolConfig) (*httpmask.ClientEarlyHandshake, error) {\n\tchoice, err := pickClientTable(cfg)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn NewHTTPMaskClientEarlyHandshake(\n\t\tnewHTTPMaskEarlyCodecConfig(cfg, ClientAEADSeed(cfg.Key)),\n\t\tchoice.Table,\n\t\tchoice.Hint,\n\t\tchoice.HasHint,\n\t\tkipUserHashFromKey(cfg.Key),\n\t\tKIPFeatAll,\n\t)\n}\n\nfunc NewHTTPMaskTunnelServer(cfg *ProtocolConfig) *HTTPMaskTunnelServer {\n\treturn newHTTPMaskTunnelServer(cfg, false)\n}\n\nfunc NewHTTPMaskTunnelServerWithFallback(cfg *ProtocolConfig) *HTTPMaskTunnelServer {\n\treturn newHTTPMaskTunnelServer(cfg, true)\n}\n\nfunc newHTTPMaskTunnelServer(cfg *ProtocolConfig, passThroughOnReject bool) *HTTPMaskTunnelServer {\n\tif cfg == nil {\n\t\treturn &HTTPMaskTunnelServer{}\n\t}\n\n\tvar ts *httpmask.TunnelServer\n\tif !cfg.DisableHTTPMask {\n\t\tswitch strings.ToLower(strings.TrimSpace(cfg.HTTPMaskMode)) {\n\t\tcase \"stream\", \"poll\", \"auto\", \"ws\":\n\t\t\tts = httpmask.NewTunnelServer(httpmask.TunnelServerOptions{\n\t\t\t\tMode:     cfg.HTTPMaskMode,\n\t\t\t\tPathRoot: cfg.HTTPMaskPathRoot,\n\t\t\t\tAuthKey:  ServerAEADSeed(cfg.Key),\n\t\t\t\tEarlyHandshake: NewHTTPMaskServerEarlyHandshake(\n\t\t\t\t\tnewHTTPMaskEarlyCodecConfig(cfg, ServerAEADSeed(cfg.Key)),\n\t\t\t\t\tcfg.tableCandidates(),\n\t\t\t\t\tglobalHandshakeReplay.allow,\n\t\t\t\t),\n\t\t\t\t// When upstream fallback is enabled, preserve rejected HTTP requests for the caller.\n\t\t\t\tPassThroughOnReject: passThroughOnReject,\n\t\t\t})\n\t\t}\n\t}\n\treturn &HTTPMaskTunnelServer{cfg: cfg, ts: ts}\n}\n\n// WrapConn inspects an accepted TCP connection and upgrades it to an HTTP tunnel stream when needed.\n//\n// Returns:\n//   - done=true: this TCP connection has been fully handled (e.g., stream/poll control request), caller should return\n//   - done=false: handshakeConn+cfg are ready for ServerHandshake\nfunc (s *HTTPMaskTunnelServer) WrapConn(rawConn net.Conn) (handshakeConn net.Conn, cfg *ProtocolConfig, done bool, err error) {\n\tif rawConn == nil {\n\t\treturn nil, nil, true, fmt.Errorf(\"nil conn\")\n\t}\n\tif s == nil {\n\t\treturn rawConn, nil, false, nil\n\t}\n\tif s.ts == nil {\n\t\treturn rawConn, s.cfg, false, nil\n\t}\n\n\tres, c, err := s.ts.HandleConn(rawConn)\n\tif err != nil {\n\t\treturn nil, nil, true, err\n\t}\n\n\tswitch res {\n\tcase httpmask.HandleDone:\n\t\treturn nil, nil, true, nil\n\tcase httpmask.HandlePassThrough:\n\t\treturn c, s.cfg, false, nil\n\tcase httpmask.HandleStartTunnel:\n\t\tinner := *s.cfg\n\t\tinner.DisableHTTPMask = true\n\t\t// HTTPMask tunnel modes (stream/poll/auto/ws) add extra round trips before the first\n\t\t// handshake bytes can reach ServerHandshake, especially under high concurrency.\n\t\t// Bump the handshake timeout for tunneled conns to avoid flaky timeouts while keeping\n\t\t// the default strict for raw TCP handshakes.\n\t\tconst minTunneledHandshakeTimeoutSeconds = 15\n\t\tif inner.HandshakeTimeoutSeconds <= 0 || inner.HandshakeTimeoutSeconds < minTunneledHandshakeTimeoutSeconds {\n\t\t\tinner.HandshakeTimeoutSeconds = minTunneledHandshakeTimeoutSeconds\n\t\t}\n\t\treturn c, &inner, false, nil\n\tdefault:\n\t\treturn nil, nil, true, nil\n\t}\n}\n\ntype TunnelDialer func(ctx context.Context, network, addr string) (net.Conn, error)\n\n// DialHTTPMaskTunnel dials a CDN-capable HTTP tunnel (stream/poll/auto/ws) and returns a stream carrying raw Sudoku bytes.\nfunc DialHTTPMaskTunnel(ctx context.Context, serverAddress string, cfg *ProtocolConfig, dial TunnelDialer, upgrade func(net.Conn) (net.Conn, error)) (net.Conn, error) {\n\tif cfg == nil {\n\t\treturn nil, fmt.Errorf(\"config is required\")\n\t}\n\tif cfg.DisableHTTPMask {\n\t\treturn nil, fmt.Errorf(\"http mask is disabled\")\n\t}\n\tswitch strings.ToLower(strings.TrimSpace(cfg.HTTPMaskMode)) {\n\tcase \"stream\", \"poll\", \"auto\", \"ws\":\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"http-mask-mode=%q does not use http tunnel\", cfg.HTTPMaskMode)\n\t}\n\tvar (\n\t\tearlyHandshake *httpmask.ClientEarlyHandshake\n\t\terr            error\n\t)\n\tif upgrade != nil {\n\t\tearlyHandshake, err = newClientHTTPMaskEarlyHandshake(cfg)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn httpmask.DialTunnel(ctx, serverAddress, httpmask.TunnelDialOptions{\n\t\tMode:           cfg.HTTPMaskMode,\n\t\tTLSEnabled:     cfg.HTTPMaskTLSEnabled,\n\t\tHostOverride:   cfg.HTTPMaskHost,\n\t\tPathRoot:       cfg.HTTPMaskPathRoot,\n\t\tAuthKey:        ClientAEADSeed(cfg.Key),\n\t\tEarlyHandshake: earlyHandshake,\n\t\tUpgrade:        upgrade,\n\t\tMultiplex:      cfg.HTTPMaskMultiplex,\n\t\tDialContext:    dial,\n\t})\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/httpmask_tunnel_test.go",
    "content": "package sudoku\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"strings\"\n\t\"sync\"\n\t\"testing\"\n\t\"time\"\n)\n\nfunc startTunnelServer(t *testing.T, cfg *ProtocolConfig, handle func(*ServerSession) error) (addr string, stop func(), errCh <-chan error) {\n\tt.Helper()\n\n\tln, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\tif err != nil {\n\t\tt.Fatalf(\"listen: %v\", err)\n\t}\n\n\terrC := make(chan error, 128)\n\tdone := make(chan struct{})\n\n\ttunnelSrv := NewHTTPMaskTunnelServer(cfg)\n\tvar wg sync.WaitGroup\n\tvar stopOnce sync.Once\n\n\twg.Add(1)\n\tgo func() {\n\t\tdefer wg.Done()\n\t\tfor {\n\t\t\tc, err := ln.Accept()\n\t\t\tif err != nil {\n\t\t\t\tclose(done)\n\t\t\t\treturn\n\t\t\t}\n\t\t\twg.Add(1)\n\t\t\tgo func(conn net.Conn) {\n\t\t\t\tdefer wg.Done()\n\n\t\t\t\thandshakeConn, handshakeCfg, handled, err := tunnelSrv.WrapConn(conn)\n\t\t\t\tif err != nil {\n\t\t\t\t\t_ = conn.Close()\n\t\t\t\t\tif nerr, ok := err.(net.Error); ok && nerr.Timeout() {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\tif err == io.EOF {\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t\terrC <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif handled {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif handshakeConn == nil || handshakeCfg == nil {\n\t\t\t\t\t_ = conn.Close()\n\t\t\t\t\terrC <- fmt.Errorf(\"wrap conn returned nil\")\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tcConn, meta, err := ServerHandshake(handshakeConn, handshakeCfg)\n\t\t\t\tif err != nil {\n\t\t\t\t\t_ = handshakeConn.Close()\n\t\t\t\t\tif handshakeConn != conn {\n\t\t\t\t\t\t_ = conn.Close()\n\t\t\t\t\t}\n\t\t\t\t\terrC <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tdefer cConn.Close()\n\n\t\t\t\tsession, err := ReadServerSession(cConn, meta)\n\t\t\t\tif err != nil {\n\t\t\t\t\terrC <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif handleErr := handle(session); handleErr != nil {\n\t\t\t\t\terrC <- handleErr\n\t\t\t\t}\n\t\t\t}(c)\n\t\t}\n\t}()\n\n\tstop = func() {\n\t\tstopOnce.Do(func() {\n\t\t\t_ = ln.Close()\n\t\t\tselect {\n\t\t\tcase <-done:\n\t\t\tcase <-time.After(5 * time.Second):\n\t\t\t\tt.Fatalf(\"server did not stop\")\n\t\t\t}\n\n\t\t\tch := make(chan struct{})\n\t\t\tgo func() {\n\t\t\t\twg.Wait()\n\t\t\t\tclose(ch)\n\t\t\t}()\n\t\t\tselect {\n\t\t\tcase <-ch:\n\t\t\tcase <-time.After(10 * time.Second):\n\t\t\t\tt.Fatalf(\"server goroutines did not exit\")\n\t\t\t}\n\t\t\tclose(errC)\n\t\t})\n\t}\n\n\treturn ln.Addr().String(), stop, errC\n}\n\nfunc newTunnelTestTable(t *testing.T, key string) *ProtocolConfig {\n\tt.Helper()\n\n\ttables, err := NewTablesWithCustomPatterns(ClientAEADSeed(key), \"prefer_ascii\", \"\", nil)\n\tif err != nil {\n\t\tt.Fatalf(\"build tables: %v\", err)\n\t}\n\tif len(tables) != 1 {\n\t\tt.Fatalf(\"unexpected tables: %d\", len(tables))\n\t}\n\n\tcfg := DefaultConfig()\n\tcfg.Key = key\n\tcfg.AEADMethod = \"chacha20-poly1305\"\n\tcfg.Table = tables[0]\n\tcfg.PaddingMin = 0\n\tcfg.PaddingMax = 0\n\tcfg.HandshakeTimeoutSeconds = 5\n\tcfg.EnablePureDownlink = true\n\tcfg.DisableHTTPMask = false\n\treturn cfg\n}\n\nfunc TestHTTPMaskTunnel_Stream_TCPRoundTrip(t *testing.T) {\n\tkey := \"tunnel-stream-key\"\n\ttarget := \"1.1.1.1:80\"\n\n\tserverCfg := newTunnelTestTable(t, key)\n\tserverCfg.HTTPMaskMode = \"stream\"\n\n\taddr, stop, errCh := startTunnelServer(t, serverCfg, func(s *ServerSession) error {\n\t\tif s.Type != SessionTypeTCP {\n\t\t\treturn fmt.Errorf(\"unexpected session type: %v\", s.Type)\n\t\t}\n\t\tif s.Target != target {\n\t\t\treturn fmt.Errorf(\"target mismatch: %s\", s.Target)\n\t\t}\n\t\t_, _ = s.Conn.Write([]byte(\"ok\"))\n\t\treturn nil\n\t})\n\tdefer stop()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\tdefer cancel()\n\n\tclientCfg := *serverCfg\n\tclientCfg.ServerAddress = addr\n\tclientCfg.HTTPMaskHost = \"example.com\"\n\n\ttunnelConn, err := DialHTTPMaskTunnel(ctx, clientCfg.ServerAddress, &clientCfg, (&net.Dialer{}).DialContext, nil)\n\tif err != nil {\n\t\tt.Fatalf(\"dial tunnel: %v\", err)\n\t}\n\tdefer tunnelConn.Close()\n\n\thandshakeCfg := clientCfg\n\thandshakeCfg.DisableHTTPMask = true\n\tcConn, err := ClientHandshake(tunnelConn, &handshakeCfg)\n\tif err != nil {\n\t\tt.Fatalf(\"client handshake: %v\", err)\n\t}\n\tdefer cConn.Close()\n\n\taddrBuf, err := EncodeAddress(target)\n\tif err != nil {\n\t\tt.Fatalf(\"encode addr: %v\", err)\n\t}\n\tif err := WriteKIPMessage(cConn, KIPTypeOpenTCP, addrBuf); err != nil {\n\t\tt.Fatalf(\"write addr: %v\", err)\n\t}\n\n\tbuf := make([]byte, 2)\n\tif _, err := io.ReadFull(cConn, buf); err != nil {\n\t\tt.Fatalf(\"read: %v\", err)\n\t}\n\tif string(buf) != \"ok\" {\n\t\tt.Fatalf(\"unexpected payload: %q\", buf)\n\t}\n\n\tstop()\n\tfor err := range errCh {\n\t\tt.Fatalf(\"server error: %v\", err)\n\t}\n}\n\nfunc TestHTTPMaskTunnel_Poll_UoTRoundTrip(t *testing.T) {\n\tkey := \"tunnel-poll-key\"\n\ttarget := \"8.8.8.8:53\"\n\tpayload := []byte{0xaa, 0xbb, 0xcc, 0xdd}\n\n\tserverCfg := newTunnelTestTable(t, key)\n\tserverCfg.HTTPMaskMode = \"poll\"\n\n\taddr, stop, errCh := startTunnelServer(t, serverCfg, func(s *ServerSession) error {\n\t\tif s.Type != SessionTypeUoT {\n\t\t\treturn fmt.Errorf(\"unexpected session type: %v\", s.Type)\n\t\t}\n\t\tgotAddr, gotPayload, err := ReadDatagram(s.Conn)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"server read datagram: %w\", err)\n\t\t}\n\t\tif gotAddr != target {\n\t\t\treturn fmt.Errorf(\"uot target mismatch: %s\", gotAddr)\n\t\t}\n\t\tif !bytes.Equal(gotPayload, payload) {\n\t\t\treturn fmt.Errorf(\"uot payload mismatch: %x\", gotPayload)\n\t\t}\n\t\tif err := WriteDatagram(s.Conn, gotAddr, gotPayload); err != nil {\n\t\t\treturn fmt.Errorf(\"server write datagram: %w\", err)\n\t\t}\n\t\treturn nil\n\t})\n\tdefer stop()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\tdefer cancel()\n\n\tclientCfg := *serverCfg\n\tclientCfg.ServerAddress = addr\n\n\ttunnelConn, err := DialHTTPMaskTunnel(ctx, clientCfg.ServerAddress, &clientCfg, (&net.Dialer{}).DialContext, nil)\n\tif err != nil {\n\t\tt.Fatalf(\"dial tunnel: %v\", err)\n\t}\n\tdefer tunnelConn.Close()\n\n\thandshakeCfg := clientCfg\n\thandshakeCfg.DisableHTTPMask = true\n\tcConn, err := ClientHandshake(tunnelConn, &handshakeCfg)\n\tif err != nil {\n\t\tt.Fatalf(\"client handshake: %v\", err)\n\t}\n\tdefer cConn.Close()\n\n\tif err := WriteKIPMessage(cConn, KIPTypeStartUoT, nil); err != nil {\n\t\tt.Fatalf(\"start uot: %v\", err)\n\t}\n\tif err := WriteDatagram(cConn, target, payload); err != nil {\n\t\tt.Fatalf(\"write datagram: %v\", err)\n\t}\n\tgotAddr, gotPayload, err := ReadDatagram(cConn)\n\tif err != nil {\n\t\tt.Fatalf(\"read datagram: %v\", err)\n\t}\n\tif gotAddr != target {\n\t\tt.Fatalf(\"uot target mismatch: %s\", gotAddr)\n\t}\n\tif !bytes.Equal(gotPayload, payload) {\n\t\tt.Fatalf(\"uot payload mismatch: %x\", gotPayload)\n\t}\n\n\tstop()\n\tfor err := range errCh {\n\t\tt.Fatalf(\"server error: %v\", err)\n\t}\n}\n\nfunc TestHTTPMaskTunnel_Auto_TCPRoundTrip(t *testing.T) {\n\tkey := \"tunnel-auto-key\"\n\ttarget := \"9.9.9.9:443\"\n\n\tserverCfg := newTunnelTestTable(t, key)\n\tserverCfg.HTTPMaskMode = \"auto\"\n\n\taddr, stop, errCh := startTunnelServer(t, serverCfg, func(s *ServerSession) error {\n\t\tif s.Type != SessionTypeTCP {\n\t\t\treturn fmt.Errorf(\"unexpected session type: %v\", s.Type)\n\t\t}\n\t\tif s.Target != target {\n\t\t\treturn fmt.Errorf(\"target mismatch: %s\", s.Target)\n\t\t}\n\t\t_, _ = s.Conn.Write([]byte(\"ok\"))\n\t\treturn nil\n\t})\n\tdefer stop()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\tdefer cancel()\n\n\tclientCfg := *serverCfg\n\tclientCfg.ServerAddress = addr\n\n\ttunnelConn, err := DialHTTPMaskTunnel(ctx, clientCfg.ServerAddress, &clientCfg, (&net.Dialer{}).DialContext, nil)\n\tif err != nil {\n\t\tt.Fatalf(\"dial tunnel: %v\", err)\n\t}\n\tdefer tunnelConn.Close()\n\n\thandshakeCfg := clientCfg\n\thandshakeCfg.DisableHTTPMask = true\n\tcConn, err := ClientHandshake(tunnelConn, &handshakeCfg)\n\tif err != nil {\n\t\tt.Fatalf(\"client handshake: %v\", err)\n\t}\n\tdefer cConn.Close()\n\n\taddrBuf, err := EncodeAddress(target)\n\tif err != nil {\n\t\tt.Fatalf(\"encode addr: %v\", err)\n\t}\n\tif err := WriteKIPMessage(cConn, KIPTypeOpenTCP, addrBuf); err != nil {\n\t\tt.Fatalf(\"write addr: %v\", err)\n\t}\n\n\tbuf := make([]byte, 2)\n\tif _, err := io.ReadFull(cConn, buf); err != nil {\n\t\tt.Fatalf(\"read: %v\", err)\n\t}\n\tif string(buf) != \"ok\" {\n\t\tt.Fatalf(\"unexpected payload: %q\", buf)\n\t}\n\n\tstop()\n\tfor err := range errCh {\n\t\tt.Fatalf(\"server error: %v\", err)\n\t}\n}\n\nfunc TestHTTPMaskTunnel_WS_TCPRoundTrip(t *testing.T) {\n\tkey := \"tunnel-ws-key\"\n\ttarget := \"1.1.1.1:80\"\n\n\tserverCfg := newTunnelTestTable(t, key)\n\tserverCfg.HTTPMaskMode = \"ws\"\n\n\taddr, stop, errCh := startTunnelServer(t, serverCfg, func(s *ServerSession) error {\n\t\tif s.Type != SessionTypeTCP {\n\t\t\treturn fmt.Errorf(\"unexpected session type: %v\", s.Type)\n\t\t}\n\t\tif s.Target != target {\n\t\t\treturn fmt.Errorf(\"target mismatch: %s\", s.Target)\n\t\t}\n\t\t_, _ = s.Conn.Write([]byte(\"ok\"))\n\t\treturn nil\n\t})\n\tdefer stop()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\tdefer cancel()\n\n\tclientCfg := *serverCfg\n\tclientCfg.ServerAddress = addr\n\n\ttunnelConn, err := DialHTTPMaskTunnel(ctx, clientCfg.ServerAddress, &clientCfg, (&net.Dialer{}).DialContext, nil)\n\tif err != nil {\n\t\tt.Fatalf(\"dial tunnel: %v\", err)\n\t}\n\tdefer tunnelConn.Close()\n\n\thandshakeCfg := clientCfg\n\thandshakeCfg.DisableHTTPMask = true\n\tcConn, err := ClientHandshake(tunnelConn, &handshakeCfg)\n\tif err != nil {\n\t\tt.Fatalf(\"client handshake: %v\", err)\n\t}\n\tdefer cConn.Close()\n\n\taddrBuf, err := EncodeAddress(target)\n\tif err != nil {\n\t\tt.Fatalf(\"encode addr: %v\", err)\n\t}\n\tif err := WriteKIPMessage(cConn, KIPTypeOpenTCP, addrBuf); err != nil {\n\t\tt.Fatalf(\"write addr: %v\", err)\n\t}\n\n\tbuf := make([]byte, 2)\n\tif _, err := io.ReadFull(cConn, buf); err != nil {\n\t\tt.Fatalf(\"read: %v\", err)\n\t}\n\tif string(buf) != \"ok\" {\n\t\tt.Fatalf(\"unexpected payload: %q\", buf)\n\t}\n\n\tstop()\n\tfor err := range errCh {\n\t\tt.Fatalf(\"server error: %v\", err)\n\t}\n}\n\nfunc TestHTTPMaskTunnel_EarlyHandshake_TCPRoundTrip(t *testing.T) {\n\tmodes := []string{\"stream\", \"poll\", \"ws\"}\n\tfor _, mode := range modes {\n\t\tt.Run(mode, func(t *testing.T) {\n\t\t\tkey := \"tunnel-early-\" + mode\n\t\t\ttarget := \"1.1.1.1:80\"\n\n\t\t\tserverCfg := newTunnelTestTable(t, key)\n\t\t\tserverCfg.HTTPMaskMode = mode\n\n\t\t\taddr, stop, errCh := startTunnelServer(t, serverCfg, func(s *ServerSession) error {\n\t\t\t\tif s.Type != SessionTypeTCP {\n\t\t\t\t\treturn fmt.Errorf(\"unexpected session type: %v\", s.Type)\n\t\t\t\t}\n\t\t\t\tif s.Target != target {\n\t\t\t\t\treturn fmt.Errorf(\"target mismatch: %s\", s.Target)\n\t\t\t\t}\n\t\t\t\t_, _ = s.Conn.Write([]byte(\"ok\"))\n\t\t\t\treturn nil\n\t\t\t})\n\t\t\tdefer stop()\n\n\t\t\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\t\t\tdefer cancel()\n\n\t\t\tclientCfg := *serverCfg\n\t\t\tclientCfg.ServerAddress = addr\n\n\t\t\thandshakeCfg := clientCfg\n\t\t\thandshakeCfg.DisableHTTPMask = true\n\t\t\ttunnelConn, err := DialHTTPMaskTunnel(ctx, clientCfg.ServerAddress, &clientCfg, (&net.Dialer{}).DialContext, func(raw net.Conn) (net.Conn, error) {\n\t\t\t\treturn ClientHandshake(raw, &handshakeCfg)\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"dial tunnel: %v\", err)\n\t\t\t}\n\t\t\tdefer tunnelConn.Close()\n\n\t\t\taddrBuf, err := EncodeAddress(target)\n\t\t\tif err != nil {\n\t\t\t\tt.Fatalf(\"encode addr: %v\", err)\n\t\t\t}\n\t\t\tif err := WriteKIPMessage(tunnelConn, KIPTypeOpenTCP, addrBuf); err != nil {\n\t\t\t\tt.Fatalf(\"write addr: %v\", err)\n\t\t\t}\n\n\t\t\tbuf := make([]byte, 2)\n\t\t\tif _, err := io.ReadFull(tunnelConn, buf); err != nil {\n\t\t\t\tt.Fatalf(\"read: %v\", err)\n\t\t\t}\n\t\t\tif string(buf) != \"ok\" {\n\t\t\t\tt.Fatalf(\"unexpected payload: %q\", buf)\n\t\t\t}\n\n\t\t\tstop()\n\t\t\tfor err := range errCh {\n\t\t\t\tt.Fatalf(\"server error: %v\", err)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestHTTPMaskTunnel_EarlyHandshake_AutoPathRoot_TCPRoundTrip(t *testing.T) {\n\tkey := \"tunnel-early-auto-pathroot\"\n\ttarget := \"1.1.1.1:80\"\n\n\tserverCfg := newTunnelTestTable(t, key)\n\tserverCfg.HTTPMaskMode = \"auto\"\n\tserverCfg.HTTPMaskPathRoot = \"httpmaskpath\"\n\n\taddr, stop, errCh := startTunnelServer(t, serverCfg, func(s *ServerSession) error {\n\t\tif s.Type != SessionTypeTCP {\n\t\t\treturn fmt.Errorf(\"unexpected session type: %v\", s.Type)\n\t\t}\n\t\tif s.Target != target {\n\t\t\treturn fmt.Errorf(\"target mismatch: %s\", s.Target)\n\t\t}\n\t\t_, _ = s.Conn.Write([]byte(\"ok\"))\n\t\treturn nil\n\t})\n\tdefer stop()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\tdefer cancel()\n\n\tclientCfg := *serverCfg\n\tclientCfg.ServerAddress = addr\n\n\thandshakeCfg := clientCfg\n\thandshakeCfg.DisableHTTPMask = true\n\ttunnelConn, err := DialHTTPMaskTunnel(ctx, clientCfg.ServerAddress, &clientCfg, (&net.Dialer{}).DialContext, func(raw net.Conn) (net.Conn, error) {\n\t\treturn ClientHandshake(raw, &handshakeCfg)\n\t})\n\tif err != nil {\n\t\tt.Fatalf(\"dial tunnel: %v\", err)\n\t}\n\tdefer tunnelConn.Close()\n\n\taddrBuf, err := EncodeAddress(target)\n\tif err != nil {\n\t\tt.Fatalf(\"encode addr: %v\", err)\n\t}\n\tif err := WriteKIPMessage(tunnelConn, KIPTypeOpenTCP, addrBuf); err != nil {\n\t\tt.Fatalf(\"write addr: %v\", err)\n\t}\n\n\tbuf := make([]byte, 2)\n\tif _, err := io.ReadFull(tunnelConn, buf); err != nil {\n\t\tt.Fatalf(\"read: %v\", err)\n\t}\n\tif string(buf) != \"ok\" {\n\t\tt.Fatalf(\"unexpected payload: %q\", buf)\n\t}\n\n\tstop()\n\tfor err := range errCh {\n\t\tt.Fatalf(\"server error: %v\", err)\n\t}\n}\n\nfunc TestHTTPMaskTunnel_Validation(t *testing.T) {\n\tcfg := DefaultConfig()\n\tcfg.Key = \"k\"\n\tcfg.Table = NewTable(\"seed\", \"prefer_ascii\")\n\tcfg.ServerAddress = \"127.0.0.1:1\"\n\n\tcfg.DisableHTTPMask = true\n\tcfg.HTTPMaskMode = \"stream\"\n\tif _, err := DialHTTPMaskTunnel(context.Background(), cfg.ServerAddress, cfg, (&net.Dialer{}).DialContext, nil); err == nil {\n\t\tt.Fatalf(\"expected error for disabled http mask\")\n\t}\n\n\tcfg.DisableHTTPMask = false\n\tcfg.HTTPMaskMode = \"legacy\"\n\tif _, err := DialHTTPMaskTunnel(context.Background(), cfg.ServerAddress, cfg, (&net.Dialer{}).DialContext, nil); err == nil {\n\t\tt.Fatalf(\"expected error for legacy mode\")\n\t}\n}\n\nfunc TestHTTPMaskTunnel_Soak_Concurrent(t *testing.T) {\n\tkey := \"tunnel-soak-key\"\n\ttarget := \"1.0.0.1:80\"\n\n\tserverCfg := newTunnelTestTable(t, key)\n\tserverCfg.HTTPMaskMode = \"stream\"\n\tserverCfg.EnablePureDownlink = false\n\n\tconst (\n\t\tsessions   = 8\n\t\tpayloadLen = 64 * 1024\n\t)\n\n\taddr, stop, errCh := startTunnelServer(t, serverCfg, func(s *ServerSession) error {\n\t\tif s.Type != SessionTypeTCP {\n\t\t\treturn fmt.Errorf(\"unexpected session type: %v\", s.Type)\n\t\t}\n\t\tif s.Target != target {\n\t\t\treturn fmt.Errorf(\"target mismatch: %s\", s.Target)\n\t\t}\n\t\tbuf := make([]byte, payloadLen)\n\t\tif _, err := io.ReadFull(s.Conn, buf); err != nil {\n\t\t\treturn fmt.Errorf(\"server read payload: %w\", err)\n\t\t}\n\t\t_, err := s.Conn.Write(buf)\n\t\treturn err\n\t})\n\tdefer stop()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 30*time.Second)\n\tdefer cancel()\n\n\tvar wg sync.WaitGroup\n\trunErr := make(chan error, sessions)\n\n\tfor i := 0; i < sessions; i++ {\n\t\twg.Add(1)\n\t\tgo func(id int) {\n\t\t\tdefer wg.Done()\n\t\t\tclientCfg := *serverCfg\n\t\t\tclientCfg.ServerAddress = addr\n\t\t\tclientCfg.HTTPMaskHost = strings.TrimSpace(clientCfg.HTTPMaskHost)\n\n\t\t\ttunnelConn, err := DialHTTPMaskTunnel(ctx, clientCfg.ServerAddress, &clientCfg, (&net.Dialer{}).DialContext, nil)\n\t\t\tif err != nil {\n\t\t\t\trunErr <- fmt.Errorf(\"dial: %w\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdefer tunnelConn.Close()\n\n\t\t\thandshakeCfg := clientCfg\n\t\t\thandshakeCfg.DisableHTTPMask = true\n\t\t\tcConn, err := ClientHandshake(tunnelConn, &handshakeCfg)\n\t\t\tif err != nil {\n\t\t\t\trunErr <- fmt.Errorf(\"handshake: %w\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdefer cConn.Close()\n\n\t\t\taddrBuf, err := EncodeAddress(target)\n\t\t\tif err != nil {\n\t\t\t\trunErr <- fmt.Errorf(\"encode addr: %w\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif err := WriteKIPMessage(cConn, KIPTypeOpenTCP, addrBuf); err != nil {\n\t\t\t\trunErr <- fmt.Errorf(\"write addr: %w\", err)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tpayload := bytes.Repeat([]byte{byte(id)}, payloadLen)\n\t\t\tif _, err := cConn.Write(payload); err != nil {\n\t\t\t\trunErr <- fmt.Errorf(\"write payload: %w\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\techo := make([]byte, payloadLen)\n\t\t\tif _, err := io.ReadFull(cConn, echo); err != nil {\n\t\t\t\trunErr <- fmt.Errorf(\"read echo: %w\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif !bytes.Equal(echo, payload) {\n\t\t\t\trunErr <- fmt.Errorf(\"echo mismatch\")\n\t\t\t\treturn\n\t\t\t}\n\t\t\trunErr <- nil\n\t\t}(i)\n\t}\n\n\twg.Wait()\n\tclose(runErr)\n\n\tfor err := range runErr {\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"soak: %v\", err)\n\t\t}\n\t}\n\n\tstop()\n\tfor err := range errCh {\n\t\tt.Fatalf(\"server error: %v\", err)\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/init.go",
    "content": "package sudoku\n\nimport (\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"strings\"\n\n\t\"github.com/metacubex/edwards25519\"\n\t\"github.com/metacubex/mihomo/transport/sudoku/crypto\"\n\t\"github.com/metacubex/mihomo/transport/sudoku/obfs/sudoku\"\n)\n\nfunc NewTable(key string, tableType string) *sudoku.Table {\n\ttable, err := NewTableWithCustom(key, tableType, \"\")\n\tif err != nil {\n\t\tpanic(fmt.Sprintf(\"[Sudoku] failed to init tables: %v\", err))\n\t}\n\treturn table\n}\n\nfunc NewTableWithCustom(key string, tableType string, customTable string) (*sudoku.Table, error) {\n\ttable, err := sudoku.NewTableWithCustom(key, tableType, customTable)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn table, nil\n}\n\n// ClientAEADSeed returns a canonical \"seed\" that is stable between client private key material and server public key.\nfunc ClientAEADSeed(key string) string {\n\tkey = strings.TrimSpace(key)\n\tif key == \"\" {\n\t\treturn \"\"\n\t}\n\n\tb, err := hex.DecodeString(key)\n\tif err != nil {\n\t\treturn key\n\t}\n\n\t// Client-side key material can be:\n\t//   - public key: 32 bytes hex compressed point\n\t//   - split private key: 64 bytes hex (r||k)\n\t//   - master private scalar: 32 bytes hex (x)\n\t//   - PSK string: non-hex\n\t//\n\t// 32-byte hex is ambiguous: it can be either a compressed public key or a\n\t// master private scalar. Official Sudoku runtime accepts public keys directly,\n\t// so when the bytes already decode as a point, preserve that point verbatim.\n\tif len(b) == 32 {\n\t\tif p, err := new(edwards25519.Point).SetBytes(b); err == nil {\n\t\t\treturn hex.EncodeToString(p.Bytes())\n\t\t}\n\t}\n\tif len(b) != 64 && len(b) != 32 {\n\t\treturn key\n\t}\n\tif recovered, err := crypto.RecoverPublicKey(key); err == nil {\n\t\treturn crypto.EncodePoint(recovered)\n\t}\n\treturn key\n}\n\n// ServerAEADSeed returns a canonical seed for server-side configuration.\n//\n// When key is a public key (32-byte compressed point, hex), it returns the canonical point encoding.\n// When key is private key material (split/master scalar), it derives and returns the public key.\nfunc ServerAEADSeed(key string) string {\n\tkey = strings.TrimSpace(key)\n\tif key == \"\" {\n\t\treturn \"\"\n\t}\n\n\tb, err := hex.DecodeString(key)\n\tif err != nil {\n\t\treturn key\n\t}\n\n\t// Prefer interpreting 32-byte hex as a public key point, to avoid accidental scalar parsing.\n\tif len(b) == 32 {\n\t\tif p, err := new(edwards25519.Point).SetBytes(b); err == nil {\n\t\t\treturn hex.EncodeToString(p.Bytes())\n\t\t}\n\t}\n\n\t// Fall back to client-side rules for private key materials / other formats.\n\treturn ClientAEADSeed(key)\n}\n\n// GenKeyPair generates a client \"available private key\" and the corresponding server public key.\nfunc GenKeyPair() (privateKey, publicKey string, err error) {\n\tpair, err := crypto.GenerateMasterKey()\n\tif err != nil {\n\t\treturn \"\", \"\", err\n\t}\n\tavailablePrivateKey, err := crypto.SplitPrivateKey(pair.Private)\n\tif err != nil {\n\t\treturn \"\", \"\", err\n\t}\n\treturn availablePrivateKey, crypto.EncodePoint(pair.Public), nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/init_test.go",
    "content": "package sudoku\n\nimport (\n\t\"crypto/rand\"\n\t\"encoding/hex\"\n\t\"testing\"\n\n\t\"github.com/metacubex/edwards25519\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestClientAEADSeed_IsStableForPrivAndPub(t *testing.T) {\n\tfor i := 0; i < 64; i++ {\n\t\tpriv, pub, err := GenKeyPair()\n\t\trequire.NoError(t, err)\n\n\t\trequire.Equal(t, pub, ClientAEADSeed(priv))\n\t\trequire.Equal(t, pub, ClientAEADSeed(pub))\n\t\trequire.Equal(t, pub, ServerAEADSeed(pub))\n\t\trequire.Equal(t, pub, ServerAEADSeed(priv))\n\t}\n}\n\nfunc TestClientAEADSeed_Supports32ByteMasterScalar(t *testing.T) {\n\tfor i := 0; i < 256; i++ {\n\t\tvar seed [64]byte\n\t\t_, err := rand.Read(seed[:])\n\t\trequire.NoError(t, err)\n\n\t\ts, err := edwards25519.NewScalar().SetUniformBytes(seed[:])\n\t\trequire.NoError(t, err)\n\n\t\tkeyHex := hex.EncodeToString(s.Bytes())\n\t\trequire.Len(t, keyHex, 64)\n\n\t\t// 32-byte hex is ambiguous: it can be either a master scalar or an\n\t\t// already-compressed public key. Public-key encoding wins when both parse.\n\t\tif _, err := new(edwards25519.Point).SetBytes(s.Bytes()); err == nil {\n\t\t\tcontinue\n\t\t}\n\n\t\trequire.NotEqual(t, keyHex, ClientAEADSeed(keyHex))\n\t\trequire.Equal(t, ClientAEADSeed(keyHex), ServerAEADSeed(ClientAEADSeed(keyHex)))\n\t\treturn\n\t}\n\n\tt.Fatal(\"failed to generate an unambiguous 32-byte master scalar\")\n}\n\nfunc TestServerAEADSeed_LeavesPublicKeyAsIs(t *testing.T) {\n\tfor i := 0; i < 64; i++ {\n\t\tpriv, pub, err := GenKeyPair()\n\t\trequire.NoError(t, err)\n\t\trequire.Equal(t, pub, ServerAEADSeed(pub))\n\t\trequire.Equal(t, pub, ServerAEADSeed(priv))\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/kip.go",
    "content": "package sudoku\n\nimport (\n\t\"bytes\"\n\t\"crypto/sha256\"\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"strings\"\n\t\"time\"\n\n\tsudokuobfs \"github.com/metacubex/mihomo/transport/sudoku/obfs/sudoku\"\n)\n\nconst (\n\tkipMagic = \"kip\"\n\n\tKIPTypeClientHello byte = 0x01\n\tKIPTypeServerHello byte = 0x02\n\n\tKIPTypeOpenTCP   byte = 0x10\n\tKIPTypeStartMux  byte = 0x11\n\tKIPTypeStartUoT  byte = 0x12\n\tKIPTypeKeepAlive byte = 0x14\n)\n\n// KIP feature bits are advisory capability flags negotiated during the handshake.\n// They represent control-plane message families.\nconst (\n\tKIPFeatOpenTCP   uint32 = 1 << 0\n\tKIPFeatMux       uint32 = 1 << 1\n\tKIPFeatUoT       uint32 = 1 << 2\n\tKIPFeatKeepAlive uint32 = 1 << 4\n\n\tKIPFeatAll = KIPFeatOpenTCP | KIPFeatMux | KIPFeatUoT | KIPFeatKeepAlive\n)\n\nconst (\n\tkipHelloUserHashSize = 8\n\tkipHelloNonceSize    = 16\n\tkipHelloPubSize      = 32\n\tkipMaxPayload        = 64 * 1024\n)\n\nconst kipClientHelloTableHintSize = 4\n\nvar errKIP = errors.New(\"kip protocol error\")\n\ntype KIPMessage struct {\n\tType    byte\n\tPayload []byte\n}\n\nfunc WriteKIPMessage(w io.Writer, typ byte, payload []byte) error {\n\tif w == nil {\n\t\treturn fmt.Errorf(\"%w: nil writer\", errKIP)\n\t}\n\tif len(payload) > kipMaxPayload {\n\t\treturn fmt.Errorf(\"%w: payload too large: %d\", errKIP, len(payload))\n\t}\n\n\tvar hdr [3 + 1 + 2]byte\n\tcopy(hdr[:3], []byte(kipMagic))\n\thdr[3] = typ\n\tbinary.BigEndian.PutUint16(hdr[4:], uint16(len(payload)))\n\n\treturn writeAllChunks(w, hdr[:], payload)\n}\n\nfunc ReadKIPMessage(r io.Reader) (*KIPMessage, error) {\n\tif r == nil {\n\t\treturn nil, fmt.Errorf(\"%w: nil reader\", errKIP)\n\t}\n\tvar hdr [3 + 1 + 2]byte\n\tif _, err := io.ReadFull(r, hdr[:]); err != nil {\n\t\treturn nil, err\n\t}\n\tif string(hdr[:3]) != kipMagic {\n\t\treturn nil, fmt.Errorf(\"%w: bad magic\", errKIP)\n\t}\n\ttyp := hdr[3]\n\tn := int(binary.BigEndian.Uint16(hdr[4:]))\n\tif n < 0 || n > kipMaxPayload {\n\t\treturn nil, fmt.Errorf(\"%w: invalid payload length: %d\", errKIP, n)\n\t}\n\tvar payload []byte\n\tif n > 0 {\n\t\tpayload = make([]byte, n)\n\t\tif _, err := io.ReadFull(r, payload); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\treturn &KIPMessage{Type: typ, Payload: payload}, nil\n}\n\ntype KIPClientHello struct {\n\tTimestamp    time.Time\n\tUserHash     [kipHelloUserHashSize]byte\n\tNonce        [kipHelloNonceSize]byte\n\tClientPub    [kipHelloPubSize]byte\n\tFeatures     uint32\n\tTableHint    uint32\n\tHasTableHint bool\n}\n\ntype KIPServerHello struct {\n\tNonce         [kipHelloNonceSize]byte\n\tServerPub     [kipHelloPubSize]byte\n\tSelectedFeats uint32\n}\n\nfunc newKIPClientHello(userHash [kipHelloUserHashSize]byte, nonce [kipHelloNonceSize]byte, clientPub [kipHelloPubSize]byte, feats uint32, tableHint uint32, hasTableHint bool) *KIPClientHello {\n\treturn &KIPClientHello{\n\t\tTimestamp:    time.Now(),\n\t\tUserHash:     userHash,\n\t\tNonce:        nonce,\n\t\tClientPub:    clientPub,\n\t\tFeatures:     feats,\n\t\tTableHint:    tableHint,\n\t\tHasTableHint: hasTableHint,\n\t}\n}\n\nfunc kipUserHashFromKey(psk string) [kipHelloUserHashSize]byte {\n\tvar out [kipHelloUserHashSize]byte\n\tpsk = strings.TrimSpace(psk)\n\tif psk == \"\" {\n\t\treturn out\n\t}\n\n\t// Align with upstream: when the client carries private key material (or even just a public key),\n\t// prefer hashing the raw hex bytes so different split/master keys can be distinguished.\n\tif keyBytes, err := hex.DecodeString(psk); err == nil && len(keyBytes) > 0 {\n\t\tsum := sha256.Sum256(keyBytes)\n\t\tcopy(out[:], sum[:kipHelloUserHashSize])\n\t\treturn out\n\t}\n\n\tsum := sha256.Sum256([]byte(psk))\n\tcopy(out[:], sum[:kipHelloUserHashSize])\n\treturn out\n}\n\nfunc KIPUserHashHexFromKey(psk string) string {\n\tuh := kipUserHashFromKey(psk)\n\treturn hex.EncodeToString(uh[:])\n}\n\nfunc (m *KIPClientHello) EncodePayload() []byte {\n\tvar b bytes.Buffer\n\tvar tmp [8]byte\n\tbinary.BigEndian.PutUint64(tmp[:], uint64(m.Timestamp.Unix()))\n\tb.Write(tmp[:])\n\tb.Write(m.UserHash[:])\n\tb.Write(m.Nonce[:])\n\tb.Write(m.ClientPub[:])\n\tvar f [4]byte\n\tbinary.BigEndian.PutUint32(f[:], m.Features)\n\tb.Write(f[:])\n\tif m.HasTableHint {\n\t\tvar hint [kipClientHelloTableHintSize]byte\n\t\tbinary.BigEndian.PutUint32(hint[:], m.TableHint)\n\t\tb.Write(hint[:])\n\t}\n\treturn b.Bytes()\n}\n\nfunc DecodeKIPClientHelloPayload(payload []byte) (*KIPClientHello, error) {\n\tconst minLen = 8 + kipHelloUserHashSize + kipHelloNonceSize + kipHelloPubSize + 4\n\tif len(payload) < minLen {\n\t\treturn nil, fmt.Errorf(\"%w: client hello too short\", errKIP)\n\t}\n\tvar h KIPClientHello\n\tts := int64(binary.BigEndian.Uint64(payload[:8]))\n\th.Timestamp = time.Unix(ts, 0)\n\toff := 8\n\tcopy(h.UserHash[:], payload[off:off+kipHelloUserHashSize])\n\toff += kipHelloUserHashSize\n\tcopy(h.Nonce[:], payload[off:off+kipHelloNonceSize])\n\toff += kipHelloNonceSize\n\tcopy(h.ClientPub[:], payload[off:off+kipHelloPubSize])\n\toff += kipHelloPubSize\n\th.Features = binary.BigEndian.Uint32(payload[off : off+4])\n\toff += 4\n\tif len(payload) >= off+kipClientHelloTableHintSize {\n\t\th.TableHint = binary.BigEndian.Uint32(payload[off : off+kipClientHelloTableHintSize])\n\t\th.HasTableHint = true\n\t}\n\treturn &h, nil\n}\n\nfunc ResolveClientHelloTable(selected *sudokuobfs.Table, candidates []*sudokuobfs.Table, hello *KIPClientHello) (*sudokuobfs.Table, error) {\n\tif selected == nil {\n\t\treturn nil, fmt.Errorf(\"nil selected table\")\n\t}\n\tif hello == nil || !hello.HasTableHint {\n\t\treturn selected, nil\n\t}\n\tif selected.Hint() == hello.TableHint {\n\t\treturn selected, nil\n\t}\n\tif len(candidates) == 0 {\n\t\treturn nil, fmt.Errorf(\"no table candidates\")\n\t}\n\n\tvar hinted *sudokuobfs.Table\n\tfor _, candidate := range candidates {\n\t\tif candidate == nil || candidate.Hint() != hello.TableHint {\n\t\t\tcontinue\n\t\t}\n\t\thinted = candidate\n\t\tbreak\n\t}\n\tif hinted == nil {\n\t\treturn nil, fmt.Errorf(\"unknown table hint: %d\", hello.TableHint)\n\t}\n\tif hinted != selected && (!hinted.IsASCII || !selected.IsASCII) {\n\t\treturn nil, fmt.Errorf(\"table hint %d mismatches probed uplink table\", hello.TableHint)\n\t}\n\treturn hinted, nil\n}\n\nfunc (m *KIPServerHello) EncodePayload() []byte {\n\tvar b bytes.Buffer\n\tb.Write(m.Nonce[:])\n\tb.Write(m.ServerPub[:])\n\tvar f [4]byte\n\tbinary.BigEndian.PutUint32(f[:], m.SelectedFeats)\n\tb.Write(f[:])\n\treturn b.Bytes()\n}\n\nfunc DecodeKIPServerHelloPayload(payload []byte) (*KIPServerHello, error) {\n\tconst want = kipHelloNonceSize + kipHelloPubSize + 4\n\tif len(payload) != want {\n\t\treturn nil, fmt.Errorf(\"%w: server hello bad len: %d\", errKIP, len(payload))\n\t}\n\tvar h KIPServerHello\n\toff := 0\n\tcopy(h.Nonce[:], payload[off:off+kipHelloNonceSize])\n\toff += kipHelloNonceSize\n\tcopy(h.ServerPub[:], payload[off:off+kipHelloPubSize])\n\toff += kipHelloPubSize\n\th.SelectedFeats = binary.BigEndian.Uint32(payload[off : off+4])\n\treturn &h, nil\n}\n\nfunc writeFull(w io.Writer, b []byte) error {\n\tfor len(b) > 0 {\n\t\tn, err := w.Write(b)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tb = b[n:]\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/kip_test.go",
    "content": "package sudoku\n\nimport (\n\t\"testing\"\n\n\tsudokuobfs \"github.com/metacubex/mihomo/transport/sudoku/obfs/sudoku\"\n)\n\nfunc TestKIPClientHelloTableHintRoundTrip(t *testing.T) {\n\thello := &KIPClientHello{\n\t\tFeatures:     KIPFeatAll,\n\t\tTableHint:    0x12345678,\n\t\tHasTableHint: true,\n\t}\n\tdecoded, err := DecodeKIPClientHelloPayload(hello.EncodePayload())\n\tif err != nil {\n\t\tt.Fatalf(\"decode client hello: %v\", err)\n\t}\n\tif !decoded.HasTableHint {\n\t\tt.Fatalf(\"expected decoded hello to carry table hint\")\n\t}\n\tif decoded.TableHint != hello.TableHint {\n\t\tt.Fatalf(\"decoded table hint = %08x, want %08x\", decoded.TableHint, hello.TableHint)\n\t}\n}\n\nfunc TestResolveClientHelloTableAllowsDirectionalASCIIRotation(t *testing.T) {\n\ttables, err := NewClientTablesWithCustomPatterns(\"seed\", \"up_ascii_down_entropy\", \"\", []string{\"xpxvvpvv\", \"vxpvxvvp\"})\n\tif err != nil {\n\t\tt.Fatalf(\"build tables: %v\", err)\n\t}\n\tif len(tables) != 2 {\n\t\tt.Fatalf(\"expected 2 tables, got %d\", len(tables))\n\t}\n\n\tselected, err := ResolveClientHelloTable(tables[0], tables, &KIPClientHello{\n\t\tTableHint:    tables[1].Hint(),\n\t\tHasTableHint: true,\n\t})\n\tif err != nil {\n\t\tt.Fatalf(\"resolve client hello table: %v\", err)\n\t}\n\tif selected != tables[1] {\n\t\tt.Fatalf(\"resolved table mismatch\")\n\t}\n}\n\nfunc TestResolveClientHelloTableRejectsEntropyMismatch(t *testing.T) {\n\ta, err := sudokuobfs.NewTableWithCustom(\"seed\", \"prefer_entropy\", \"xpxvvpvv\")\n\tif err != nil {\n\t\tt.Fatalf(\"table a: %v\", err)\n\t}\n\tb, err := sudokuobfs.NewTableWithCustom(\"seed\", \"prefer_entropy\", \"vxpvxvvp\")\n\tif err != nil {\n\t\tt.Fatalf(\"table b: %v\", err)\n\t}\n\n\tif _, err := ResolveClientHelloTable(a, []*sudokuobfs.Table{a, b}, &KIPClientHello{\n\t\tTableHint:    b.Hint(),\n\t\tHasTableHint: true,\n\t}); err == nil {\n\t\tt.Fatalf(\"expected entropy-table mismatch to fail\")\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/multiplex/session.go",
    "content": "package multiplex\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n)\n\nconst (\n\tframeOpen  byte = 0x01\n\tframeData  byte = 0x02\n\tframeClose byte = 0x03\n\tframeReset byte = 0x04\n)\n\nconst (\n\theaderSize     = 1 + 4 + 4\n\tmaxFrameSize   = 256 * 1024\n\tmaxDataPayload = 32 * 1024\n)\n\ntype acceptEvent struct {\n\tstream  *stream\n\tpayload []byte\n}\n\ntype Session struct {\n\tconn net.Conn\n\n\twriteMu sync.Mutex\n\n\tstreamsMu sync.Mutex\n\tstreams   map[uint32]*stream\n\tnextID    uint32\n\n\tacceptCh chan acceptEvent\n\n\tclosed    chan struct{}\n\tcloseOnce sync.Once\n\tcloseErr  error\n}\n\nfunc NewClientSession(conn net.Conn) (*Session, error) {\n\tif conn == nil {\n\t\treturn nil, fmt.Errorf(\"nil conn\")\n\t}\n\ts := &Session{\n\t\tconn:    conn,\n\t\tstreams: make(map[uint32]*stream),\n\t\tclosed:  make(chan struct{}),\n\t}\n\tgo s.readLoop()\n\treturn s, nil\n}\n\nfunc NewServerSession(conn net.Conn) (*Session, error) {\n\tif conn == nil {\n\t\treturn nil, fmt.Errorf(\"nil conn\")\n\t}\n\ts := &Session{\n\t\tconn:     conn,\n\t\tstreams:  make(map[uint32]*stream),\n\t\tacceptCh: make(chan acceptEvent, 256),\n\t\tclosed:   make(chan struct{}),\n\t}\n\tgo s.readLoop()\n\treturn s, nil\n}\n\nfunc (s *Session) IsClosed() bool {\n\tif s == nil {\n\t\treturn true\n\t}\n\tselect {\n\tcase <-s.closed:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc (s *Session) closedErr() error {\n\ts.streamsMu.Lock()\n\terr := s.closeErr\n\ts.streamsMu.Unlock()\n\tif err == nil {\n\t\treturn io.ErrClosedPipe\n\t}\n\treturn err\n}\n\nfunc (s *Session) closeWithError(err error) {\n\tif err == nil {\n\t\terr = io.ErrClosedPipe\n\t}\n\ts.closeOnce.Do(func() {\n\t\ts.streamsMu.Lock()\n\t\tif s.closeErr == nil {\n\t\t\ts.closeErr = err\n\t\t}\n\t\tstreams := make([]*stream, 0, len(s.streams))\n\t\tfor _, st := range s.streams {\n\t\t\tstreams = append(streams, st)\n\t\t}\n\t\ts.streams = make(map[uint32]*stream)\n\t\ts.streamsMu.Unlock()\n\n\t\tfor _, st := range streams {\n\t\t\tst.closeNoSend(err)\n\t\t}\n\n\t\tclose(s.closed)\n\t\t_ = s.conn.Close()\n\t})\n}\n\nfunc (s *Session) Close() error {\n\tif s == nil {\n\t\treturn nil\n\t}\n\ts.closeWithError(io.ErrClosedPipe)\n\treturn nil\n}\n\nfunc (s *Session) registerStream(st *stream) {\n\ts.streamsMu.Lock()\n\ts.streams[st.id] = st\n\ts.streamsMu.Unlock()\n}\n\nfunc (s *Session) getStream(id uint32) *stream {\n\ts.streamsMu.Lock()\n\tst := s.streams[id]\n\ts.streamsMu.Unlock()\n\treturn st\n}\n\nfunc (s *Session) removeStream(id uint32) {\n\ts.streamsMu.Lock()\n\tdelete(s.streams, id)\n\ts.streamsMu.Unlock()\n}\n\nfunc (s *Session) nextStreamID() uint32 {\n\ts.streamsMu.Lock()\n\ts.nextID++\n\tid := s.nextID\n\tif id == 0 {\n\t\ts.nextID++\n\t\tid = s.nextID\n\t}\n\ts.streamsMu.Unlock()\n\treturn id\n}\n\nfunc (s *Session) sendFrame(frameType byte, streamID uint32, payload []byte) error {\n\tif s.IsClosed() {\n\t\treturn s.closedErr()\n\t}\n\tif len(payload) > maxFrameSize {\n\t\treturn fmt.Errorf(\"mux payload too large: %d\", len(payload))\n\t}\n\n\tvar header [headerSize]byte\n\theader[0] = frameType\n\tbinary.BigEndian.PutUint32(header[1:5], streamID)\n\tbinary.BigEndian.PutUint32(header[5:9], uint32(len(payload)))\n\n\ts.writeMu.Lock()\n\tdefer s.writeMu.Unlock()\n\n\tif err := writeAllChunks(s.conn, header[:], payload); err != nil {\n\t\ts.closeWithError(err)\n\t\treturn err\n\t}\n\treturn nil\n}\n\nfunc (s *Session) sendReset(streamID uint32, msg string) {\n\tif msg == \"\" {\n\t\tmsg = \"reset\"\n\t}\n\t_ = s.sendFrame(frameReset, streamID, []byte(msg))\n\t_ = s.sendFrame(frameClose, streamID, nil)\n}\n\nfunc (s *Session) OpenStream(openPayload []byte) (net.Conn, error) {\n\tif s == nil {\n\t\treturn nil, fmt.Errorf(\"nil session\")\n\t}\n\tif s.IsClosed() {\n\t\treturn nil, s.closedErr()\n\t}\n\n\tstreamID := s.nextStreamID()\n\tst := newStream(s, streamID)\n\ts.registerStream(st)\n\n\tif err := s.sendFrame(frameOpen, streamID, openPayload); err != nil {\n\t\tst.closeNoSend(err)\n\t\ts.removeStream(streamID)\n\t\treturn nil, fmt.Errorf(\"mux open failed: %w\", err)\n\t}\n\treturn st, nil\n}\n\nfunc (s *Session) AcceptStream() (net.Conn, []byte, error) {\n\tif s == nil {\n\t\treturn nil, nil, fmt.Errorf(\"nil session\")\n\t}\n\tif s.acceptCh == nil {\n\t\treturn nil, nil, fmt.Errorf(\"accept is not supported on client sessions\")\n\t}\n\tselect {\n\tcase ev := <-s.acceptCh:\n\t\treturn ev.stream, ev.payload, nil\n\tcase <-s.closed:\n\t\treturn nil, nil, s.closedErr()\n\t}\n}\n\nfunc (s *Session) readLoop() {\n\tvar header [headerSize]byte\n\tfor {\n\t\tif _, err := io.ReadFull(s.conn, header[:]); err != nil {\n\t\t\ts.closeWithError(err)\n\t\t\treturn\n\t\t}\n\t\tframeType := header[0]\n\t\tstreamID := binary.BigEndian.Uint32(header[1:5])\n\t\tn := int(binary.BigEndian.Uint32(header[5:9]))\n\t\tif n < 0 || n > maxFrameSize {\n\t\t\ts.closeWithError(fmt.Errorf(\"invalid mux frame length: %d\", n))\n\t\t\treturn\n\t\t}\n\n\t\tvar payload []byte\n\t\tif n > 0 {\n\t\t\tpayload = make([]byte, n)\n\t\t\tif _, err := io.ReadFull(s.conn, payload); err != nil {\n\t\t\t\ts.closeWithError(err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tswitch frameType {\n\t\tcase frameOpen:\n\t\t\tif s.acceptCh == nil {\n\t\t\t\ts.sendReset(streamID, \"unexpected open\")\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif streamID == 0 {\n\t\t\t\ts.sendReset(streamID, \"invalid stream id\")\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif existing := s.getStream(streamID); existing != nil {\n\t\t\t\ts.sendReset(streamID, \"stream already exists\")\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tst := newStream(s, streamID)\n\t\t\ts.registerStream(st)\n\t\t\tgo func() {\n\t\t\t\tselect {\n\t\t\t\tcase s.acceptCh <- acceptEvent{stream: st, payload: payload}:\n\t\t\t\tcase <-s.closed:\n\t\t\t\t\tst.closeNoSend(io.ErrClosedPipe)\n\t\t\t\t\ts.removeStream(streamID)\n\t\t\t\t}\n\t\t\t}()\n\n\t\tcase frameData:\n\t\t\tst := s.getStream(streamID)\n\t\t\tif st == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif len(payload) == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tst.enqueue(payload)\n\n\t\tcase frameClose:\n\t\t\tst := s.getStream(streamID)\n\t\t\tif st == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tst.closeNoSend(io.EOF)\n\t\t\ts.removeStream(streamID)\n\n\t\tcase frameReset:\n\t\t\tst := s.getStream(streamID)\n\t\t\tif st == nil {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tmsg := trimASCII(payload)\n\t\t\tif msg == \"\" {\n\t\t\t\tmsg = \"reset\"\n\t\t\t}\n\t\t\tst.closeNoSend(errors.New(msg))\n\t\t\ts.removeStream(streamID)\n\n\t\tdefault:\n\t\t\ts.closeWithError(fmt.Errorf(\"unknown mux frame type: %d\", frameType))\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc trimASCII(b []byte) string {\n\ti := 0\n\tj := len(b)\n\tfor i < j {\n\t\tc := b[i]\n\t\tif c != ' ' && c != '\\n' && c != '\\r' && c != '\\t' {\n\t\t\tbreak\n\t\t}\n\t\ti++\n\t}\n\tfor j > i {\n\t\tc := b[j-1]\n\t\tif c != ' ' && c != '\\n' && c != '\\r' && c != '\\t' {\n\t\t\tbreak\n\t\t}\n\t\tj--\n\t}\n\tif i >= j {\n\t\treturn \"\"\n\t}\n\tout := make([]byte, j-i)\n\tcopy(out, b[i:j])\n\treturn string(out)\n}\n\ntype stream struct {\n\tsession *Session\n\tid      uint32\n\n\tmu       sync.Mutex\n\tcond     *sync.Cond\n\tclosed   bool\n\tcloseErr error\n\treadBuf  []byte\n\tqueue    [][]byte\n\n\tlocalAddr  net.Addr\n\tremoteAddr net.Addr\n}\n\nfunc newStream(session *Session, id uint32) *stream {\n\tst := &stream{\n\t\tsession:    session,\n\t\tid:         id,\n\t\tlocalAddr:  &net.TCPAddr{},\n\t\tremoteAddr: &net.TCPAddr{},\n\t}\n\tst.cond = sync.NewCond(&st.mu)\n\treturn st\n}\n\nfunc (c *stream) enqueue(payload []byte) {\n\tc.mu.Lock()\n\tif c.closed {\n\t\tc.mu.Unlock()\n\t\treturn\n\t}\n\tif len(c.readBuf) == 0 && len(c.queue) == 0 {\n\t\tc.readBuf = payload\n\t} else {\n\t\tc.queue = append(c.queue, payload)\n\t}\n\tc.cond.Signal()\n\tc.mu.Unlock()\n}\n\nfunc (c *stream) closeNoSend(err error) {\n\tif err == nil {\n\t\terr = io.EOF\n\t}\n\tc.mu.Lock()\n\tif c.closed {\n\t\tc.mu.Unlock()\n\t\treturn\n\t}\n\tc.closed = true\n\tif c.closeErr == nil {\n\t\tc.closeErr = err\n\t}\n\tc.cond.Broadcast()\n\tc.mu.Unlock()\n}\n\nfunc (c *stream) closedErr() error {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\tif c.closeErr == nil {\n\t\treturn io.ErrClosedPipe\n\t}\n\treturn c.closeErr\n}\n\nfunc (c *stream) Read(p []byte) (int, error) {\n\tif len(p) == 0 {\n\t\treturn 0, nil\n\t}\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tfor len(c.readBuf) == 0 && len(c.queue) == 0 && !c.closed {\n\t\tc.cond.Wait()\n\t}\n\tif len(c.readBuf) == 0 && len(c.queue) > 0 {\n\t\tc.readBuf = c.queue[0]\n\t\tc.queue = c.queue[1:]\n\t}\n\tif len(c.readBuf) == 0 && c.closed {\n\t\tif c.closeErr == nil {\n\t\t\treturn 0, io.ErrClosedPipe\n\t\t}\n\t\treturn 0, c.closeErr\n\t}\n\n\tn := copy(p, c.readBuf)\n\tc.readBuf = c.readBuf[n:]\n\treturn n, nil\n}\n\nfunc (c *stream) Write(p []byte) (int, error) {\n\tif len(p) == 0 {\n\t\treturn 0, nil\n\t}\n\tif c.session == nil || c.session.IsClosed() {\n\t\tif c.session != nil {\n\t\t\treturn 0, c.session.closedErr()\n\t\t}\n\t\treturn 0, io.ErrClosedPipe\n\t}\n\n\tc.mu.Lock()\n\tclosed := c.closed\n\tc.mu.Unlock()\n\tif closed {\n\t\treturn 0, c.closedErr()\n\t}\n\n\twritten := 0\n\tfor len(p) > 0 {\n\t\tchunk := p\n\t\tif len(chunk) > maxDataPayload {\n\t\t\tchunk = p[:maxDataPayload]\n\t\t}\n\t\tif err := c.session.sendFrame(frameData, c.id, chunk); err != nil {\n\t\t\treturn written, err\n\t\t}\n\t\twritten += len(chunk)\n\t\tp = p[len(chunk):]\n\t}\n\treturn written, nil\n}\n\nfunc (c *stream) Close() error {\n\tc.mu.Lock()\n\tif c.closed {\n\t\tc.mu.Unlock()\n\t\treturn nil\n\t}\n\tc.closed = true\n\tif c.closeErr == nil {\n\t\tc.closeErr = io.ErrClosedPipe\n\t}\n\tc.cond.Broadcast()\n\tc.mu.Unlock()\n\n\t_ = c.session.sendFrame(frameClose, c.id, nil)\n\tc.session.removeStream(c.id)\n\treturn nil\n}\n\nfunc (c *stream) CloseWrite() error { return c.Close() }\nfunc (c *stream) CloseRead() error  { return c.Close() }\n\nfunc (c *stream) LocalAddr() net.Addr  { return c.localAddr }\nfunc (c *stream) RemoteAddr() net.Addr { return c.remoteAddr }\n\nfunc (c *stream) SetDeadline(t time.Time) error {\n\t_ = c.SetReadDeadline(t)\n\t_ = c.SetWriteDeadline(t)\n\treturn nil\n}\nfunc (c *stream) SetReadDeadline(time.Time) error  { return nil }\nfunc (c *stream) SetWriteDeadline(time.Time) error { return nil }\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/multiplex/write_chunks.go",
    "content": "package multiplex\n\nimport \"io\"\n\nfunc writeAllChunks(w io.Writer, chunks ...[]byte) error {\n\tfor _, chunk := range chunks {\n\t\tfor len(chunk) > 0 {\n\t\t\tn, err := w.Write(chunk)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif n == 0 {\n\t\t\t\treturn io.ErrShortWrite\n\t\t\t}\n\t\t\tchunk = chunk[n:]\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/multiplex.go",
    "content": "package sudoku\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/transport/sudoku/multiplex\"\n)\n\n// StartMultiplexClient upgrades an already-handshaked Sudoku tunnel into a multiplex session.\nfunc StartMultiplexClient(conn net.Conn) (*MultiplexClient, error) {\n\tif conn == nil {\n\t\treturn nil, fmt.Errorf(\"nil conn\")\n\t}\n\n\tif err := WriteKIPMessage(conn, KIPTypeStartMux, nil); err != nil {\n\t\treturn nil, fmt.Errorf(\"write mux start failed: %w\", err)\n\t}\n\n\tsess, err := multiplex.NewClientSession(conn)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"start multiplex session failed: %w\", err)\n\t}\n\n\treturn &MultiplexClient{sess: sess}, nil\n}\n\ntype MultiplexClient struct {\n\tsess *multiplex.Session\n}\n\n// Dial opens a new logical stream, writes the target address, and returns the stream as net.Conn.\nfunc (c *MultiplexClient) Dial(ctx context.Context, targetAddress string) (net.Conn, error) {\n\tif c == nil || c.sess == nil || c.sess.IsClosed() {\n\t\treturn nil, fmt.Errorf(\"multiplex session is closed\")\n\t}\n\tif strings.TrimSpace(targetAddress) == \"\" {\n\t\treturn nil, fmt.Errorf(\"target address cannot be empty\")\n\t}\n\n\taddrBuf, err := EncodeAddress(targetAddress)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"encode target address failed: %w\", err)\n\t}\n\n\tif ctx != nil && ctx.Err() != nil {\n\t\treturn nil, ctx.Err()\n\t}\n\n\tstream, err := c.sess.OpenStream(addrBuf)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn stream, nil\n}\n\nfunc (c *MultiplexClient) Close() error {\n\tif c == nil || c.sess == nil {\n\t\treturn nil\n\t}\n\treturn c.sess.Close()\n}\n\nfunc (c *MultiplexClient) IsClosed() bool {\n\tif c == nil || c.sess == nil {\n\t\treturn true\n\t}\n\treturn c.sess.IsClosed()\n}\n\n// AcceptMultiplexServer upgrades a server-side, already-handshaked Sudoku connection into a multiplex session.\nfunc AcceptMultiplexServer(conn net.Conn) (*MultiplexServer, error) {\n\tif conn == nil {\n\t\treturn nil, fmt.Errorf(\"nil conn\")\n\t}\n\tsess, err := multiplex.NewServerSession(conn)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &MultiplexServer{sess: sess}, nil\n}\n\n// MultiplexServer wraps a multiplex session created from a handshaked Sudoku tunnel connection.\ntype MultiplexServer struct {\n\tsess *multiplex.Session\n}\n\nfunc (s *MultiplexServer) AcceptStream() (net.Conn, error) {\n\tif s == nil || s.sess == nil {\n\t\treturn nil, fmt.Errorf(\"nil session\")\n\t}\n\tc, _, err := s.sess.AcceptStream()\n\treturn c, err\n}\n\n// AcceptTCP accepts a multiplex stream and returns the target address declared in the open frame.\nfunc (s *MultiplexServer) AcceptTCP() (net.Conn, string, error) {\n\tif s == nil || s.sess == nil {\n\t\treturn nil, \"\", fmt.Errorf(\"nil session\")\n\t}\n\tstream, payload, err := s.sess.AcceptStream()\n\tif err != nil {\n\t\treturn nil, \"\", err\n\t}\n\n\ttarget, err := DecodeAddress(bytes.NewReader(payload))\n\tif err != nil {\n\t\t_ = stream.Close()\n\t\treturn nil, \"\", err\n\t}\n\n\treturn stream, target, nil\n}\n\nfunc (s *MultiplexServer) Close() error {\n\tif s == nil || s.sess == nil {\n\t\treturn nil\n\t}\n\treturn s.sess.Close()\n}\n\nfunc (s *MultiplexServer) IsClosed() bool {\n\tif s == nil || s.sess == nil {\n\t\treturn true\n\t}\n\treturn s.sess.IsClosed()\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/multiplex_test.go",
    "content": "package sudoku\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"io\"\n\t\"net\"\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\tsudokuobfs \"github.com/metacubex/mihomo/transport/sudoku/obfs/sudoku\"\n)\n\nfunc TestUserHash_StableAcrossTableRotation(t *testing.T) {\n\ttables := []*sudokuobfs.Table{\n\t\tsudokuobfs.NewTable(\"seed-a\", \"prefer_ascii\"),\n\t\tsudokuobfs.NewTable(\"seed-b\", \"prefer_ascii\"),\n\t}\n\tkey := \"userhash-stability-key\"\n\n\tserverCfg := DefaultConfig()\n\tserverCfg.Key = key\n\tserverCfg.AEADMethod = \"chacha20-poly1305\"\n\tserverCfg.Tables = tables\n\tserverCfg.PaddingMin = 0\n\tserverCfg.PaddingMax = 0\n\tserverCfg.EnablePureDownlink = true\n\tserverCfg.HandshakeTimeoutSeconds = 5\n\tserverCfg.DisableHTTPMask = true\n\n\tln, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\tif err != nil {\n\t\tt.Fatalf(\"listen: %v\", err)\n\t}\n\tt.Cleanup(func() { _ = ln.Close() })\n\n\tconst attempts = 32\n\thashCh := make(chan string, attempts)\n\terrCh := make(chan error, attempts)\n\n\tgo func() {\n\t\tfor {\n\t\t\tc, err := ln.Accept()\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tgo func(conn net.Conn) {\n\t\t\t\tdefer conn.Close()\n\t\t\t\t_, meta, err := ServerHandshake(conn, serverCfg)\n\t\t\t\tif err != nil {\n\t\t\t\t\terrCh <- err\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif meta == nil || meta.UserHash == \"\" {\n\t\t\t\t\terrCh <- io.ErrUnexpectedEOF\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\thashCh <- meta.UserHash\n\t\t\t}(c)\n\t\t}\n\t}()\n\n\tclientCfg := DefaultConfig()\n\t*clientCfg = *serverCfg\n\tclientCfg.ServerAddress = ln.Addr().String()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\tdefer cancel()\n\n\tfor i := 0; i < attempts; i++ {\n\t\traw, err := (&net.Dialer{}).DialContext(ctx, \"tcp\", clientCfg.ServerAddress)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"dial %d: %v\", i, err)\n\t\t}\n\t\tcConn, err := ClientHandshake(raw, clientCfg)\n\t\tif err != nil {\n\t\t\t_ = raw.Close()\n\t\t\tt.Fatalf(\"handshake %d: %v\", i, err)\n\t\t}\n\n\t\t_ = cConn.Close()\n\t}\n\n\tunique := map[string]struct{}{}\n\tdeadline := time.After(10 * time.Second)\n\tfor i := 0; i < attempts; i++ {\n\t\tselect {\n\t\tcase err := <-errCh:\n\t\t\tt.Fatalf(\"server handshake error: %v\", err)\n\t\tcase h := <-hashCh:\n\t\t\tif h == \"\" {\n\t\t\t\tt.Fatalf(\"empty user hash\")\n\t\t\t}\n\t\t\tif len(h) != 16 {\n\t\t\t\tt.Fatalf(\"unexpected user hash length: %d\", len(h))\n\t\t\t}\n\t\t\tunique[h] = struct{}{}\n\t\tcase <-deadline:\n\t\t\tt.Fatalf(\"timeout waiting for server handshakes\")\n\t\t}\n\t}\n\tif len(unique) != 1 {\n\t\tt.Fatalf(\"user hash should be stable across table rotation; got %d distinct values\", len(unique))\n\t}\n}\n\nfunc TestMultiplex_TCP_Echo(t *testing.T) {\n\ttable := sudokuobfs.NewTable(\"seed\", \"prefer_ascii\")\n\tkey := \"test-key-mux\"\n\ttarget := \"example.com:80\"\n\n\tserverCfg := DefaultConfig()\n\tserverCfg.Key = key\n\tserverCfg.AEADMethod = \"chacha20-poly1305\"\n\tserverCfg.Table = table\n\tserverCfg.PaddingMin = 0\n\tserverCfg.PaddingMax = 0\n\tserverCfg.EnablePureDownlink = true\n\tserverCfg.HandshakeTimeoutSeconds = 5\n\tserverCfg.DisableHTTPMask = true\n\n\tln, err := net.Listen(\"tcp\", \"127.0.0.1:0\")\n\tif err != nil {\n\t\tt.Fatalf(\"listen: %v\", err)\n\t}\n\tt.Cleanup(func() { _ = ln.Close() })\n\n\tvar handshakes int64\n\tvar streams int64\n\tdone := make(chan struct{})\n\n\tgo func() {\n\t\tdefer close(done)\n\t\traw, err := ln.Accept()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tdefer raw.Close()\n\n\t\tc, meta, err := ServerHandshake(raw, serverCfg)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tatomic.AddInt64(&handshakes, 1)\n\n\t\tsession, err := ReadServerSession(c, meta)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif session.Type != SessionTypeMultiplex {\n\t\t\t_ = c.Close()\n\t\t\treturn\n\t\t}\n\n\t\tmux, err := AcceptMultiplexServer(c)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tdefer mux.Close()\n\n\t\tfor {\n\t\t\tstream, dst, err := mux.AcceptTCP()\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif dst != target {\n\t\t\t\t_ = stream.Close()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tatomic.AddInt64(&streams, 1)\n\t\t\tgo func(c net.Conn) {\n\t\t\t\tdefer c.Close()\n\t\t\t\t_, _ = io.Copy(c, c)\n\t\t\t}(stream)\n\t\t}\n\t}()\n\n\tclientCfg := DefaultConfig()\n\t*clientCfg = *serverCfg\n\tclientCfg.ServerAddress = ln.Addr().String()\n\n\tctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)\n\tdefer cancel()\n\n\traw, err := (&net.Dialer{}).DialContext(ctx, \"tcp\", clientCfg.ServerAddress)\n\tif err != nil {\n\t\tt.Fatalf(\"dial: %v\", err)\n\t}\n\tt.Cleanup(func() { _ = raw.Close() })\n\n\tcConn, err := ClientHandshake(raw, clientCfg)\n\tif err != nil {\n\t\tt.Fatalf(\"client handshake: %v\", err)\n\t}\n\n\tmux, err := StartMultiplexClient(cConn)\n\tif err != nil {\n\t\t_ = cConn.Close()\n\t\tt.Fatalf(\"start mux: %v\", err)\n\t}\n\tdefer mux.Close()\n\n\tfor i := 0; i < 6; i++ {\n\t\ts, err := mux.Dial(ctx, target)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"dial stream %d: %v\", i, err)\n\t\t}\n\n\t\tmsg := []byte(\"hello-mux\")\n\t\tif _, err := s.Write(msg); err != nil {\n\t\t\t_ = s.Close()\n\t\t\tt.Fatalf(\"write: %v\", err)\n\t\t}\n\t\tbuf := make([]byte, len(msg))\n\t\tif _, err := io.ReadFull(s, buf); err != nil {\n\t\t\t_ = s.Close()\n\t\t\tt.Fatalf(\"read: %v\", err)\n\t\t}\n\t\t_ = s.Close()\n\t\tif !bytes.Equal(buf, msg) {\n\t\t\tt.Fatalf(\"echo mismatch: got %q\", buf)\n\t\t}\n\t}\n\n\t_ = mux.Close()\n\tselect {\n\tcase <-done:\n\tcase <-time.After(5 * time.Second):\n\t\tt.Fatalf(\"server did not exit\")\n\t}\n\n\tif got := atomic.LoadInt64(&handshakes); got != 1 {\n\t\tt.Fatalf(\"unexpected handshake count: %d\", got)\n\t}\n\tif got := atomic.LoadInt64(&streams); got < 6 {\n\t\tt.Fatalf(\"unexpected stream count: %d\", got)\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/obfs/httpmask/auth.go",
    "content": "package httpmask\n\nimport (\n\t\"crypto/hmac\"\n\t\"crypto/sha256\"\n\t\"crypto/subtle\"\n\t\"encoding/base64\"\n\t\"encoding/binary\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/metacubex/http\"\n)\n\nconst (\n\ttunnelAuthHeaderKey    = \"Authorization\"\n\ttunnelAuthHeaderPrefix = \"Bearer \"\n\ttunnelAuthQueryKey     = \"auth\"\n)\n\ntype tunnelAuth struct {\n\tkey  [32]byte // derived HMAC key\n\tskew time.Duration\n}\n\nfunc newTunnelAuth(key string, skew time.Duration) *tunnelAuth {\n\tkey = strings.TrimSpace(key)\n\tif key == \"\" {\n\t\treturn nil\n\t}\n\tif skew <= 0 {\n\t\tskew = 60 * time.Second\n\t}\n\n\t// Domain separation: keep this HMAC key independent from other uses of cfg.Key.\n\th := sha256.New()\n\t_, _ = h.Write([]byte(\"sudoku-httpmask-auth-v1:\"))\n\t_, _ = h.Write([]byte(key))\n\n\tvar sum [32]byte\n\th.Sum(sum[:0])\n\n\treturn &tunnelAuth{key: sum, skew: skew}\n}\n\nfunc (a *tunnelAuth) token(mode TunnelMode, method, path string, now time.Time) string {\n\tif a == nil {\n\t\treturn \"\"\n\t}\n\n\tts := now.Unix()\n\tsig := a.sign(mode, method, path, ts)\n\n\tvar buf [8 + 16]byte\n\tbinary.BigEndian.PutUint64(buf[:8], uint64(ts))\n\tcopy(buf[8:], sig[:])\n\treturn base64.RawURLEncoding.EncodeToString(buf[:])\n}\n\nfunc (a *tunnelAuth) verify(headers map[string]string, mode TunnelMode, method, path string, now time.Time) bool {\n\tif a == nil {\n\t\treturn true\n\t}\n\tif headers == nil {\n\t\treturn false\n\t}\n\treturn a.verifyValue(headers[\"authorization\"], mode, method, path, now)\n}\n\nfunc (a *tunnelAuth) verifyValue(val string, mode TunnelMode, method, path string, now time.Time) bool {\n\tif a == nil {\n\t\treturn true\n\t}\n\n\tval = strings.TrimSpace(val)\n\tif val == \"\" {\n\t\treturn false\n\t}\n\n\t// Accept both \"Bearer <token>\" and raw token forms (for forward proxies / CDNs that may normalize headers).\n\tif len(val) > len(tunnelAuthHeaderPrefix) && strings.EqualFold(val[:len(tunnelAuthHeaderPrefix)], tunnelAuthHeaderPrefix) {\n\t\tval = strings.TrimSpace(val[len(tunnelAuthHeaderPrefix):])\n\t}\n\tif val == \"\" {\n\t\treturn false\n\t}\n\n\traw, err := base64.RawURLEncoding.DecodeString(val)\n\tif err != nil || len(raw) != 8+16 {\n\t\treturn false\n\t}\n\n\tts := int64(binary.BigEndian.Uint64(raw[:8]))\n\tnowTS := now.Unix()\n\tdelta := nowTS - ts\n\tif delta < 0 {\n\t\tdelta = -delta\n\t}\n\tif delta > int64(a.skew.Seconds()) {\n\t\treturn false\n\t}\n\n\twant := a.sign(mode, method, path, ts)\n\treturn subtle.ConstantTimeCompare(raw[8:], want[:]) == 1\n}\n\nfunc (a *tunnelAuth) sign(mode TunnelMode, method, path string, ts int64) [16]byte {\n\tmethod = strings.ToUpper(strings.TrimSpace(method))\n\tif method == \"\" {\n\t\tmethod = \"GET\"\n\t}\n\tpath = strings.TrimSpace(path)\n\n\tvar tsBuf [8]byte\n\tbinary.BigEndian.PutUint64(tsBuf[:], uint64(ts))\n\n\tmac := hmac.New(sha256.New, a.key[:])\n\t_, _ = mac.Write([]byte(mode))\n\t_, _ = mac.Write([]byte{0})\n\t_, _ = mac.Write([]byte(method))\n\t_, _ = mac.Write([]byte{0})\n\t_, _ = mac.Write([]byte(path))\n\t_, _ = mac.Write([]byte{0})\n\t_, _ = mac.Write(tsBuf[:])\n\n\tvar full [32]byte\n\tmac.Sum(full[:0])\n\n\tvar out [16]byte\n\tcopy(out[:], full[:16])\n\treturn out\n}\n\ntype httpHeaderSetter = http.Header\n\nfunc applyTunnelAuthHeader(h httpHeaderSetter, auth *tunnelAuth, mode TunnelMode, method, path string) {\n\tif auth == nil || h == nil {\n\t\treturn\n\t}\n\ttoken := auth.token(mode, method, path, time.Now())\n\tif token == \"\" {\n\t\treturn\n\t}\n\th.Set(tunnelAuthHeaderKey, tunnelAuthHeaderPrefix+token)\n}\n\nfunc applyTunnelAuth(req *http.Request, auth *tunnelAuth, mode TunnelMode, method, path string) {\n\tif auth == nil || req == nil {\n\t\treturn\n\t}\n\ttoken := auth.token(mode, method, path, time.Now())\n\tif token == \"\" {\n\t\treturn\n\t}\n\treq.Header.Set(tunnelAuthHeaderKey, tunnelAuthHeaderPrefix+token)\n\tif req.URL != nil {\n\t\tq := req.URL.Query()\n\t\tq.Set(tunnelAuthQueryKey, token)\n\t\treq.URL.RawQuery = q.Encode()\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/obfs/httpmask/early_handshake.go",
    "content": "package httpmask\n\nimport (\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/url\"\n\t\"strings\"\n)\n\nconst (\n\ttunnelEarlyDataQueryKey = \"ed\"\n\ttunnelEarlyDataHeader   = \"X-Sudoku-Early\"\n)\n\ntype ClientEarlyHandshake struct {\n\tRequestPayload []byte\n\tHandleResponse func(payload []byte) error\n\tReady          func() bool\n\tWrapConn       func(raw net.Conn) (net.Conn, error)\n}\n\ntype TunnelServerEarlyHandshake struct {\n\tPrepare func(payload []byte) (*PreparedServerEarlyHandshake, error)\n}\n\ntype PreparedServerEarlyHandshake struct {\n\tResponsePayload []byte\n\tWrapConn        func(raw net.Conn) (net.Conn, error)\n\tUserHash        string\n}\n\ntype earlyHandshakeMeta interface {\n\tHTTPMaskEarlyHandshakeUserHash() string\n}\n\ntype earlyHandshakeConn struct {\n\tnet.Conn\n\tuserHash string\n}\n\nfunc (c *earlyHandshakeConn) HTTPMaskEarlyHandshakeUserHash() string {\n\tif c == nil {\n\t\treturn \"\"\n\t}\n\treturn c.userHash\n}\n\nfunc wrapEarlyHandshakeConn(conn net.Conn, userHash string) net.Conn {\n\tif conn == nil {\n\t\treturn nil\n\t}\n\treturn &earlyHandshakeConn{Conn: conn, userHash: userHash}\n}\n\nfunc EarlyHandshakeUserHash(conn net.Conn) (string, bool) {\n\tif conn == nil {\n\t\treturn \"\", false\n\t}\n\tv, ok := conn.(earlyHandshakeMeta)\n\tif !ok {\n\t\treturn \"\", false\n\t}\n\treturn v.HTTPMaskEarlyHandshakeUserHash(), true\n}\n\ntype authorizeResponse struct {\n\ttoken        string\n\tearlyPayload []byte\n}\n\nfunc isTunnelTokenByte(c byte) bool {\n\treturn (c >= 'a' && c <= 'z') ||\n\t\t(c >= 'A' && c <= 'Z') ||\n\t\t(c >= '0' && c <= '9') ||\n\t\tc == '-' ||\n\t\tc == '_'\n}\n\nfunc parseAuthorizeResponse(body []byte) (*authorizeResponse, error) {\n\ts := strings.TrimSpace(string(body))\n\tidx := strings.Index(s, \"token=\")\n\tif idx < 0 {\n\t\treturn nil, errors.New(\"missing token\")\n\t}\n\ts = s[idx+len(\"token=\"):]\n\tif s == \"\" {\n\t\treturn nil, errors.New(\"empty token\")\n\t}\n\n\tvar b strings.Builder\n\tfor i := 0; i < len(s); i++ {\n\t\tc := s[i]\n\t\tif isTunnelTokenByte(c) {\n\t\t\tb.WriteByte(c)\n\t\t\tcontinue\n\t\t}\n\t\tbreak\n\t}\n\ttoken := b.String()\n\tif token == \"\" {\n\t\treturn nil, errors.New(\"empty token\")\n\t}\n\n\tout := &authorizeResponse{token: token}\n\tif earlyLine := findAuthorizeField(body, \"ed=\"); earlyLine != \"\" {\n\t\tdecoded, err := base64.RawURLEncoding.DecodeString(earlyLine)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"decode early authorize payload failed: %w\", err)\n\t\t}\n\t\tout.earlyPayload = decoded\n\t}\n\treturn out, nil\n}\n\nfunc findAuthorizeField(body []byte, prefix string) string {\n\tfor _, line := range strings.Split(strings.TrimSpace(string(body)), \"\\n\") {\n\t\tline = strings.TrimSpace(line)\n\t\tif strings.HasPrefix(line, prefix) {\n\t\t\treturn strings.TrimSpace(strings.TrimPrefix(line, prefix))\n\t\t}\n\t}\n\treturn \"\"\n}\n\nfunc setEarlyDataQuery(rawURL string, payload []byte) (string, error) {\n\tif len(payload) == 0 {\n\t\treturn rawURL, nil\n\t}\n\tu, err := url.Parse(rawURL)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\tq := u.Query()\n\tq.Set(tunnelEarlyDataQueryKey, base64.RawURLEncoding.EncodeToString(payload))\n\tu.RawQuery = q.Encode()\n\treturn u.String(), nil\n}\n\nfunc parseEarlyDataQuery(u *url.URL) ([]byte, error) {\n\tif u == nil {\n\t\treturn nil, nil\n\t}\n\tval := strings.TrimSpace(u.Query().Get(tunnelEarlyDataQueryKey))\n\tif val == \"\" {\n\t\treturn nil, nil\n\t}\n\treturn base64.RawURLEncoding.DecodeString(val)\n}\n\nfunc applyEarlyHandshakeOrUpgrade(raw net.Conn, opts TunnelDialOptions) (net.Conn, error) {\n\tout := raw\n\tif opts.EarlyHandshake != nil && opts.EarlyHandshake.WrapConn != nil && (opts.EarlyHandshake.Ready == nil || opts.EarlyHandshake.Ready()) {\n\t\twrapped, err := opts.EarlyHandshake.WrapConn(raw)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif wrapped != nil {\n\t\t\tout = wrapped\n\t\t}\n\t\treturn out, nil\n\t}\n\tif opts.Upgrade != nil {\n\t\twrapped, err := opts.Upgrade(raw)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tif wrapped != nil {\n\t\t\tout = wrapped\n\t\t}\n\t}\n\treturn out, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/obfs/httpmask/halfpipe.go",
    "content": "package httpmask\n\nimport (\n\t\"io\"\n\t\"net\"\n\t\"os\"\n\t\"sync\"\n\t\"time\"\n)\n\ntype pipeDeadline struct {\n\tmu     sync.Mutex\n\ttimer  *time.Timer\n\tcancel chan struct{}\n}\n\nfunc makePipeDeadline() pipeDeadline {\n\treturn pipeDeadline{cancel: make(chan struct{})}\n}\n\nfunc (d *pipeDeadline) set(t time.Time) {\n\td.mu.Lock()\n\tdefer d.mu.Unlock()\n\n\tif d.timer != nil && !d.timer.Stop() {\n\t\t<-d.cancel\n\t}\n\td.timer = nil\n\n\tclosed := isClosedPipeChan(d.cancel)\n\tif t.IsZero() {\n\t\tif closed {\n\t\t\td.cancel = make(chan struct{})\n\t\t}\n\t\treturn\n\t}\n\n\tif dur := time.Until(t); dur > 0 {\n\t\tif closed {\n\t\t\td.cancel = make(chan struct{})\n\t\t}\n\t\td.timer = time.AfterFunc(dur, func() {\n\t\t\tclose(d.cancel)\n\t\t})\n\t\treturn\n\t}\n\n\tif !closed {\n\t\tclose(d.cancel)\n\t}\n}\n\nfunc (d *pipeDeadline) wait() <-chan struct{} {\n\td.mu.Lock()\n\tch := d.cancel\n\td.mu.Unlock()\n\treturn ch\n}\n\nfunc isClosedPipeChan(ch <-chan struct{}) bool {\n\tselect {\n\tcase <-ch:\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\ntype halfPipeAddr struct{}\n\nfunc (halfPipeAddr) Network() string { return \"pipe\" }\nfunc (halfPipeAddr) String() string  { return \"pipe\" }\n\ntype halfPipeConn struct {\n\twrMu sync.Mutex\n\n\trdRx <-chan []byte\n\trdTx chan<- int\n\n\twrTx chan<- []byte\n\twrRx <-chan int\n\n\treadOnce  sync.Once\n\twriteOnce sync.Once\n\n\tlocalReadDone  chan struct{}\n\tlocalWriteDone chan struct{}\n\n\tremoteReadDone  <-chan struct{}\n\tremoteWriteDone <-chan struct{}\n\n\treadDeadline  pipeDeadline\n\twriteDeadline pipeDeadline\n}\n\nfunc newHalfPipe() (net.Conn, net.Conn) {\n\tcb1 := make(chan []byte)\n\tcb2 := make(chan []byte)\n\tcn1 := make(chan int)\n\tcn2 := make(chan int)\n\n\tr1 := make(chan struct{})\n\tw1 := make(chan struct{})\n\tr2 := make(chan struct{})\n\tw2 := make(chan struct{})\n\n\tc1 := &halfPipeConn{\n\t\trdRx: cb1,\n\t\trdTx: cn1,\n\t\twrTx: cb2,\n\t\twrRx: cn2,\n\n\t\tlocalReadDone:   r1,\n\t\tlocalWriteDone:  w1,\n\t\tremoteReadDone:  r2,\n\t\tremoteWriteDone: w2,\n\n\t\treadDeadline:  makePipeDeadline(),\n\t\twriteDeadline: makePipeDeadline(),\n\t}\n\tc2 := &halfPipeConn{\n\t\trdRx: cb2,\n\t\trdTx: cn2,\n\t\twrTx: cb1,\n\t\twrRx: cn1,\n\n\t\tlocalReadDone:   r2,\n\t\tlocalWriteDone:  w2,\n\t\tremoteReadDone:  r1,\n\t\tremoteWriteDone: w1,\n\n\t\treadDeadline:  makePipeDeadline(),\n\t\twriteDeadline: makePipeDeadline(),\n\t}\n\treturn c1, c2\n}\n\nfunc (*halfPipeConn) LocalAddr() net.Addr  { return halfPipeAddr{} }\nfunc (*halfPipeConn) RemoteAddr() net.Addr { return halfPipeAddr{} }\n\nfunc (c *halfPipeConn) Read(p []byte) (int, error) {\n\tswitch {\n\tcase isClosedPipeChan(c.localReadDone):\n\t\treturn 0, io.ErrClosedPipe\n\tcase isClosedPipeChan(c.remoteWriteDone):\n\t\treturn 0, io.EOF\n\tcase isClosedPipeChan(c.readDeadline.wait()):\n\t\treturn 0, os.ErrDeadlineExceeded\n\t}\n\n\tselect {\n\tcase b := <-c.rdRx:\n\t\tn := copy(p, b)\n\t\tc.rdTx <- n\n\t\treturn n, nil\n\tcase <-c.localReadDone:\n\t\treturn 0, io.ErrClosedPipe\n\tcase <-c.remoteWriteDone:\n\t\treturn 0, io.EOF\n\tcase <-c.readDeadline.wait():\n\t\treturn 0, os.ErrDeadlineExceeded\n\t}\n}\n\nfunc (c *halfPipeConn) Write(p []byte) (int, error) {\n\tswitch {\n\tcase isClosedPipeChan(c.localWriteDone):\n\t\treturn 0, io.ErrClosedPipe\n\tcase isClosedPipeChan(c.remoteReadDone):\n\t\treturn 0, io.ErrClosedPipe\n\tcase isClosedPipeChan(c.writeDeadline.wait()):\n\t\treturn 0, os.ErrDeadlineExceeded\n\t}\n\n\tc.wrMu.Lock()\n\tdefer c.wrMu.Unlock()\n\n\tvar (\n\t\ttotal int\n\t\trest  = p\n\t)\n\tfor once := true; once || len(rest) > 0; once = false {\n\t\tselect {\n\t\tcase c.wrTx <- rest:\n\t\t\tn := <-c.wrRx\n\t\t\trest = rest[n:]\n\t\t\ttotal += n\n\t\tcase <-c.localWriteDone:\n\t\t\treturn total, io.ErrClosedPipe\n\t\tcase <-c.remoteReadDone:\n\t\t\treturn total, io.ErrClosedPipe\n\t\tcase <-c.writeDeadline.wait():\n\t\t\treturn total, os.ErrDeadlineExceeded\n\t\t}\n\t}\n\treturn total, nil\n}\n\nfunc (c *halfPipeConn) CloseWrite() error {\n\tc.writeOnce.Do(func() { close(c.localWriteDone) })\n\treturn nil\n}\n\nfunc (c *halfPipeConn) CloseRead() error {\n\tc.readOnce.Do(func() { close(c.localReadDone) })\n\treturn nil\n}\n\nfunc (c *halfPipeConn) Close() error {\n\t_ = c.CloseRead()\n\t_ = c.CloseWrite()\n\treturn nil\n}\n\nfunc (c *halfPipeConn) SetDeadline(t time.Time) error {\n\tc.readDeadline.set(t)\n\tc.writeDeadline.set(t)\n\treturn nil\n}\n\nfunc (c *halfPipeConn) SetReadDeadline(t time.Time) error {\n\tc.readDeadline.set(t)\n\treturn nil\n}\n\nfunc (c *halfPipeConn) SetWriteDeadline(t time.Time) error {\n\tc.writeDeadline.set(t)\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/obfs/httpmask/masker.go",
    "content": "package httpmask\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io\"\n\t\"math/rand\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n)\n\nvar (\n\tuserAgents = []string{\n\t\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36\",\n\t\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Safari/537.36\",\n\t\t\"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:121.0) Gecko/20100101 Firefox/121.0\",\n\t\t\"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15\",\n\t\t\"Mozilla/5.0 (Macintosh; Intel Mac OS X 14_2_1) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Safari/605.1.15\",\n\t\t\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36\",\n\t\t\"Mozilla/5.0 (iPhone; CPU iPhone OS 17_2 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.2 Mobile/15E148 Safari/604.1\",\n\t\t\"Mozilla/5.0 (Linux; Android 14; Pixel 7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/121.0.0.0 Mobile Safari/537.36\",\n\t}\n\taccepts = []string{\n\t\t\"text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,*/*;q=0.8\",\n\t\t\"application/json, text/plain, */*\",\n\t\t\"application/octet-stream\",\n\t\t\"*/*\",\n\t}\n\tacceptLanguages = []string{\n\t\t\"en-US,en;q=0.9\",\n\t\t\"en-GB,en;q=0.9\",\n\t\t\"zh-CN,zh;q=0.9,en-US;q=0.8,en;q=0.7\",\n\t\t\"ja-JP,ja;q=0.9,en-US;q=0.8,en;q=0.7\",\n\t\t\"de-DE,de;q=0.9,en-US;q=0.8,en;q=0.7\",\n\t}\n\tacceptEncodings = []string{\n\t\t\"gzip, deflate, br\",\n\t\t\"gzip, deflate\",\n\t\t\"br, gzip, deflate\",\n\t}\n\tpaths = []string{\n\t\t\"/api/v1/upload\",\n\t\t\"/data/sync\",\n\t\t\"/uploads/raw\",\n\t\t\"/api/report\",\n\t\t\"/feed/update\",\n\t\t\"/v2/events\",\n\t\t\"/v1/telemetry\",\n\t\t\"/session\",\n\t\t\"/stream\",\n\t\t\"/ws\",\n\t}\n\tcontentTypes = []string{\n\t\t\"application/octet-stream\",\n\t\t\"application/x-protobuf\",\n\t\t\"application/json\",\n\t}\n)\n\nvar (\n\trngPool = sync.Pool{\n\t\tNew: func() interface{} {\n\t\t\treturn rand.New(rand.NewSource(time.Now().UnixNano()))\n\t\t},\n\t}\n\theaderBufPool = sync.Pool{\n\t\tNew: func() interface{} {\n\t\t\tb := make([]byte, 0, 1024)\n\t\t\treturn &b\n\t\t},\n\t}\n)\n\n// LooksLikeHTTPRequestStart reports whether peek4 looks like a supported HTTP/1.x request method prefix.\nfunc LooksLikeHTTPRequestStart(peek4 []byte) bool {\n\tif len(peek4) < 4 {\n\t\treturn false\n\t}\n\t// Common methods: \"GET \", \"POST\", \"HEAD\", \"PUT \", \"OPTI\" (OPTIONS), \"PATC\" (PATCH), \"DELE\" (DELETE)\n\treturn bytes.Equal(peek4, []byte(\"GET \")) ||\n\t\tbytes.Equal(peek4, []byte(\"POST\")) ||\n\t\tbytes.Equal(peek4, []byte(\"HEAD\")) ||\n\t\tbytes.Equal(peek4, []byte(\"PUT \")) ||\n\t\tbytes.Equal(peek4, []byte(\"OPTI\")) ||\n\t\tbytes.Equal(peek4, []byte(\"PATC\")) ||\n\t\tbytes.Equal(peek4, []byte(\"DELE\"))\n}\n\nfunc trimPortForHost(host string) string {\n\tif host == \"\" {\n\t\treturn host\n\t}\n\t// Accept \"example.com:443\" / \"1.2.3.4:443\" / \"[::1]:443\"\n\th, _, err := net.SplitHostPort(host)\n\tif err == nil && h != \"\" {\n\t\treturn h\n\t}\n\t// If it's not in host:port form, keep as-is.\n\treturn host\n}\n\nfunc appendCommonHeaders(buf []byte, host string, r *rand.Rand) []byte {\n\tua := userAgents[r.Intn(len(userAgents))]\n\taccept := accepts[r.Intn(len(accepts))]\n\tlang := acceptLanguages[r.Intn(len(acceptLanguages))]\n\tenc := acceptEncodings[r.Intn(len(acceptEncodings))]\n\n\tbuf = append(buf, \"Host: \"...)\n\tbuf = append(buf, host...)\n\tbuf = append(buf, \"\\r\\nUser-Agent: \"...)\n\tbuf = append(buf, ua...)\n\tbuf = append(buf, \"\\r\\nAccept: \"...)\n\tbuf = append(buf, accept...)\n\tbuf = append(buf, \"\\r\\nAccept-Language: \"...)\n\tbuf = append(buf, lang...)\n\tbuf = append(buf, \"\\r\\nAccept-Encoding: \"...)\n\tbuf = append(buf, enc...)\n\tbuf = append(buf, \"\\r\\nConnection: keep-alive\\r\\n\"...)\n\n\t// A couple of common cache headers; keep them static for simplicity.\n\tbuf = append(buf, \"Cache-Control: no-cache\\r\\nPragma: no-cache\\r\\n\"...)\n\treturn buf\n}\n\n// WriteRandomRequestHeader writes a plausible HTTP/1.1 request header as a mask.\nfunc WriteRandomRequestHeader(w io.Writer, host string) error {\n\treturn WriteRandomRequestHeaderWithPathRoot(w, host, \"\")\n}\n\n// WriteRandomRequestHeaderWithPathRoot is like WriteRandomRequestHeader but prefixes all paths with pathRoot.\n// pathRoot must be a single segment (e.g. \"aabbcc\"); invalid inputs are treated as empty (disabled).\nfunc WriteRandomRequestHeaderWithPathRoot(w io.Writer, host string, pathRoot string) error {\n\t// Get RNG from pool\n\tr := rngPool.Get().(*rand.Rand)\n\tdefer rngPool.Put(r)\n\n\tpath := joinPathRoot(pathRoot, paths[r.Intn(len(paths))])\n\tctype := contentTypes[r.Intn(len(contentTypes))]\n\n\t// Use buffer pool\n\tbufPtr := headerBufPool.Get().(*[]byte)\n\tbuf := *bufPtr\n\tbuf = buf[:0]\n\tdefer func() {\n\t\tif cap(buf) <= 4096 {\n\t\t\t*bufPtr = buf\n\t\t\theaderBufPool.Put(bufPtr)\n\t\t}\n\t}()\n\n\t// Weighted template selection. Keep a conservative default (POST w/ Content-Length),\n\t// but occasionally rotate to other realistic templates (e.g. WebSocket upgrade).\n\tswitch r.Intn(10) {\n\tcase 0, 1: // ~20% WebSocket-like upgrade\n\t\thostNoPort := trimPortForHost(host)\n\t\tvar keyBytes [16]byte\n\t\tfor i := 0; i < len(keyBytes); i++ {\n\t\t\tkeyBytes[i] = byte(r.Intn(256))\n\t\t}\n\t\twsKey := base64.StdEncoding.EncodeToString(keyBytes[:])\n\n\t\tbuf = append(buf, \"GET \"...)\n\t\tbuf = append(buf, path...)\n\t\tbuf = append(buf, \" HTTP/1.1\\r\\n\"...)\n\t\tbuf = appendCommonHeaders(buf, host, r)\n\t\tbuf = append(buf, \"Upgrade: websocket\\r\\nConnection: Upgrade\\r\\nSec-WebSocket-Version: 13\\r\\nSec-WebSocket-Key: \"...)\n\t\tbuf = append(buf, wsKey...)\n\t\tbuf = append(buf, \"\\r\\nOrigin: https://\"...)\n\t\tbuf = append(buf, hostNoPort...)\n\t\tbuf = append(buf, \"\\r\\n\\r\\n\"...)\n\tdefault: // ~80% POST upload\n\t\t// Random Content-Length: 4KB–10MB. Small enough to look plausible, large enough\n\t\t// to justify long-lived writes on keep-alive connections.\n\t\tconst minCL = int64(4 * 1024)\n\t\tconst maxCL = int64(10 * 1024 * 1024)\n\t\tcontentLength := minCL + r.Int63n(maxCL-minCL+1)\n\n\t\tbuf = append(buf, \"POST \"...)\n\t\tbuf = append(buf, path...)\n\t\tbuf = append(buf, \" HTTP/1.1\\r\\n\"...)\n\t\tbuf = appendCommonHeaders(buf, host, r)\n\t\tbuf = append(buf, \"Content-Type: \"...)\n\t\tbuf = append(buf, ctype...)\n\t\tbuf = append(buf, \"\\r\\nContent-Length: \"...)\n\t\tbuf = strconv.AppendInt(buf, contentLength, 10)\n\t\t// A couple of extra headers seen in real clients.\n\t\tif r.Intn(2) == 0 {\n\t\t\tbuf = append(buf, \"\\r\\nX-Requested-With: XMLHttpRequest\"...)\n\t\t}\n\t\tif r.Intn(3) == 0 {\n\t\t\tbuf = append(buf, \"\\r\\nReferer: https://\"...)\n\t\t\tbuf = append(buf, trimPortForHost(host)...)\n\t\t\tbuf = append(buf, \"/\"...)\n\t\t}\n\t\tbuf = append(buf, \"\\r\\n\\r\\n\"...)\n\t}\n\n\t_, err := w.Write(buf)\n\treturn err\n}\n\n// ConsumeHeader 读取并消耗 HTTP 头部，返回消耗的数据和剩余的 reader 数据\n// 如果不是 POST 请求或格式严重错误，返回 error\nfunc ConsumeHeader(r *bufio.Reader) ([]byte, error) {\n\tvar consumed bytes.Buffer\n\n\t// 1. 读取请求行\n\t// Use ReadSlice to avoid allocation if line fits in buffer\n\tline, err := r.ReadSlice('\\n')\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tconsumed.Write(line)\n\n\t// Basic method validation: accept common HTTP/1.x methods used by our masker.\n\t// Keep it strict enough to reject obvious garbage.\n\tswitch {\n\tcase bytes.HasPrefix(line, []byte(\"POST \")),\n\t\tbytes.HasPrefix(line, []byte(\"GET \")),\n\t\tbytes.HasPrefix(line, []byte(\"HEAD \")),\n\t\tbytes.HasPrefix(line, []byte(\"PUT \")),\n\t\tbytes.HasPrefix(line, []byte(\"DELETE \")),\n\t\tbytes.HasPrefix(line, []byte(\"OPTIONS \")),\n\t\tbytes.HasPrefix(line, []byte(\"PATCH \")):\n\tdefault:\n\t\treturn consumed.Bytes(), fmt.Errorf(\"invalid method or garbage: %s\", strings.TrimSpace(string(line)))\n\t}\n\n\t// 2. 循环读取头部，直到遇到空行\n\tfor {\n\t\tline, err = r.ReadSlice('\\n')\n\t\tif err != nil {\n\t\t\treturn consumed.Bytes(), err\n\t\t}\n\t\tconsumed.Write(line)\n\n\t\t// Check for empty line (\\r\\n or \\n)\n\t\t// ReadSlice includes the delimiter\n\t\tn := len(line)\n\t\tif n == 2 && line[0] == '\\r' && line[1] == '\\n' {\n\t\t\treturn consumed.Bytes(), nil\n\t\t}\n\t\tif n == 1 && line[0] == '\\n' {\n\t\t\treturn consumed.Bytes(), nil\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/obfs/httpmask/pathroot.go",
    "content": "package httpmask\n\nimport \"strings\"\n\n// normalizePathRoot normalizes the configured path root into \"/<segment>\" form.\n//\n// It is intentionally strict: only a single path segment is allowed, consisting of\n// [A-Za-z0-9_-]. Invalid inputs are treated as empty (disabled).\nfunc normalizePathRoot(root string) string {\n\troot = strings.TrimSpace(root)\n\troot = strings.Trim(root, \"/\")\n\tif root == \"\" {\n\t\treturn \"\"\n\t}\n\tfor i := 0; i < len(root); i++ {\n\t\tc := root[i]\n\t\tswitch {\n\t\tcase c >= 'a' && c <= 'z':\n\t\tcase c >= 'A' && c <= 'Z':\n\t\tcase c >= '0' && c <= '9':\n\t\tcase c == '_' || c == '-':\n\t\tdefault:\n\t\t\treturn \"\"\n\t\t}\n\t}\n\treturn \"/\" + root\n}\n\nfunc joinPathRoot(root, path string) string {\n\troot = normalizePathRoot(root)\n\tif root == \"\" {\n\t\treturn path\n\t}\n\tif path == \"\" {\n\t\treturn root\n\t}\n\tif !strings.HasPrefix(path, \"/\") {\n\t\tpath = \"/\" + path\n\t}\n\treturn root + path\n}\n\nfunc stripPathRoot(root, fullPath string) (string, bool) {\n\troot = normalizePathRoot(root)\n\tif root == \"\" {\n\t\treturn fullPath, true\n\t}\n\tif !strings.HasPrefix(fullPath, root+\"/\") {\n\t\treturn \"\", false\n\t}\n\treturn strings.TrimPrefix(fullPath, root), true\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/obfs/httpmask/tunnel.go",
    "content": "package httpmask\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\tcrand \"crypto/rand\"\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\tmrand \"math/rand\"\n\t\"net\"\n\t\"net/url\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"syscall\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/component/ca\"\n\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/http/httputil\"\n\t\"github.com/metacubex/tls\"\n)\n\ntype TunnelMode string\n\nconst (\n\tTunnelModeLegacy TunnelMode = \"legacy\"\n\tTunnelModeStream TunnelMode = \"stream\"\n\tTunnelModePoll   TunnelMode = \"poll\"\n\tTunnelModeAuto   TunnelMode = \"auto\"\n\tTunnelModeWS     TunnelMode = \"ws\"\n)\n\nfunc normalizeTunnelMode(mode string) TunnelMode {\n\tswitch strings.ToLower(strings.TrimSpace(mode)) {\n\tcase \"\", string(TunnelModeLegacy):\n\t\treturn TunnelModeLegacy\n\tcase string(TunnelModeStream):\n\t\treturn TunnelModeStream\n\tcase string(TunnelModePoll):\n\t\treturn TunnelModePoll\n\tcase string(TunnelModeAuto):\n\t\treturn TunnelModeAuto\n\tcase string(TunnelModeWS):\n\t\treturn TunnelModeWS\n\tdefault:\n\t\t// Be conservative: unknown => legacy\n\t\treturn TunnelModeLegacy\n\t}\n}\n\ntype HandleResult int\n\nconst (\n\tHandlePassThrough HandleResult = iota\n\tHandleStartTunnel\n\tHandleDone\n)\n\ntype TunnelDialOptions struct {\n\tMode         string\n\tTLSEnabled   bool   // when true, use HTTPS; otherwise, use HTTP (no port-based inference)\n\tHostOverride string // optional Host header / SNI host (without scheme); accepts \"example.com\" or \"example.com:443\"\n\t// PathRoot is an optional first-level path prefix for all HTTP tunnel endpoints.\n\t// Example: \"aabbcc\" => \"/aabbcc/session\", \"/aabbcc/api/v1/upload\", ...\n\tPathRoot string\n\t// AuthKey enables short-term HMAC auth for HTTP tunnel requests (anti-probing).\n\t// When set (non-empty), each HTTP request carries an Authorization bearer token derived from AuthKey.\n\tAuthKey string\n\t// EarlyHandshake folds the protocol handshake into the HTTP/WS setup round trip.\n\t// When the server accepts the early payload, DialTunnel returns a conn that is already post-handshake.\n\t// When the server does not echo early data, DialTunnel falls back to Upgrade.\n\tEarlyHandshake *ClientEarlyHandshake\n\t// Upgrade optionally wraps the raw tunnel conn and/or writes a small prelude before DialTunnel returns.\n\t// It is called with the raw tunnel conn; if it returns a non-nil conn, that conn is returned by DialTunnel.\n\tUpgrade func(raw net.Conn) (net.Conn, error)\n\t// Multiplex controls whether the caller should reuse underlying HTTP connections (HTTP/1.1 keep-alive / HTTP/2).\n\t// To reuse across multiple dials, create a TunnelClient per proxy and reuse it.\n\t// Values: \"off\" disables reuse; \"auto\"/\"on\" enables it.\n\tMultiplex string\n\t// DialContext overrides how the HTTP tunnel dials raw TCP/TLS connections.\n\t// It must not be nil; passing nil is a programming error.\n\tDialContext func(ctx context.Context, network, addr string) (net.Conn, error)\n}\n\ntype TunnelClientOptions struct {\n\tTLSEnabled   bool\n\tHostOverride string\n\tDialContext  func(ctx context.Context, network, addr string) (net.Conn, error)\n\tMaxIdleConns int\n}\n\ntype TunnelClient struct {\n\ttransport *http.Transport\n\ttarget    httpClientTarget\n}\n\nfunc NewTunnelClient(serverAddress string, opts TunnelClientOptions) (*TunnelClient, error) {\n\tmaxIdle := opts.MaxIdleConns\n\tif maxIdle <= 0 {\n\t\tmaxIdle = 32\n\t}\n\n\ttransport, target, err := buildHTTPTransport(serverAddress, opts.TLSEnabled, opts.HostOverride, opts.DialContext, maxIdle)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &TunnelClient{\n\t\ttransport: transport,\n\t\ttarget:    target,\n\t}, nil\n}\n\nfunc (c *TunnelClient) CloseIdleConnections() {\n\tif c == nil || c.transport == nil {\n\t\treturn\n\t}\n\tc.transport.CloseIdleConnections()\n}\n\nfunc (c *TunnelClient) DialTunnel(ctx context.Context, opts TunnelDialOptions) (net.Conn, error) {\n\tif c == nil || c.transport == nil {\n\t\treturn nil, fmt.Errorf(\"nil tunnel client\")\n\t}\n\ttm := normalizeTunnelMode(opts.Mode)\n\tif tm == TunnelModeLegacy {\n\t\treturn nil, fmt.Errorf(\"legacy mode does not use http tunnel\")\n\t}\n\n\t// Create a per-dial client while sharing the underlying Transport for connection reuse.\n\t// This matches upstream behavior and avoids potential client-level concurrency pitfalls.\n\tclient := &http.Client{Transport: c.transport}\n\n\tswitch tm {\n\tcase TunnelModeStream:\n\t\treturn dialStreamWithClient(ctx, client, c.target, opts)\n\tcase TunnelModePoll:\n\t\treturn dialPollWithClient(ctx, client, c.target, opts)\n\tcase TunnelModeWS:\n\t\treturn nil, fmt.Errorf(\"ws mode does not support TunnelClient reuse\")\n\tcase TunnelModeAuto:\n\t\tstreamCtx, cancelX := context.WithTimeout(ctx, 3*time.Second)\n\t\tc1, errX := dialStreamWithClient(streamCtx, client, c.target, opts)\n\t\tcancelX()\n\t\tif errX == nil {\n\t\t\treturn c1, nil\n\t\t}\n\t\tc2, errP := dialPollWithClient(ctx, client, c.target, opts)\n\t\tif errP == nil {\n\t\t\treturn c2, nil\n\t\t}\n\t\treturn nil, fmt.Errorf(\"auto tunnel failed: stream: %v; poll: %w\", errX, errP)\n\tdefault:\n\t\treturn dialStreamWithClient(ctx, client, c.target, opts)\n\t}\n}\n\n// DialTunnel establishes a bidirectional stream over HTTP:\n//   - stream: a single streaming POST (request body uplink, response body downlink)\n//   - poll: authorize + push/pull polling tunnel (base64 framed)\n//   - auto: try stream then fall back to poll\n//\n// The returned net.Conn carries the raw Sudoku stream (no HTTP headers).\nfunc DialTunnel(ctx context.Context, serverAddress string, opts TunnelDialOptions) (net.Conn, error) {\n\tmode := normalizeTunnelMode(opts.Mode)\n\tif mode == TunnelModeLegacy {\n\t\treturn nil, fmt.Errorf(\"legacy mode does not use http tunnel\")\n\t}\n\n\tswitch mode {\n\tcase TunnelModeStream:\n\t\treturn dialStreamFn(ctx, serverAddress, opts)\n\tcase TunnelModePoll:\n\t\treturn dialPollFn(ctx, serverAddress, opts)\n\tcase TunnelModeWS:\n\t\treturn dialWS(ctx, serverAddress, opts)\n\tcase TunnelModeAuto:\n\t\t// \"stream\" can hang on some CDNs that buffer uploads until request body completes.\n\t\t// Keep it on a short leash so we can fall back to poll within the caller's deadline.\n\t\tstreamCtx, cancelX := context.WithTimeout(ctx, 3*time.Second)\n\t\tc, errX := dialStreamFn(streamCtx, serverAddress, opts)\n\t\tcancelX()\n\t\tif errX == nil {\n\t\t\treturn c, nil\n\t\t}\n\t\tc, errP := dialPollFn(ctx, serverAddress, opts)\n\t\tif errP == nil {\n\t\t\treturn c, nil\n\t\t}\n\t\treturn nil, fmt.Errorf(\"auto tunnel failed: stream: %v; poll: %w\", errX, errP)\n\tdefault:\n\t\treturn dialStreamFn(ctx, serverAddress, opts)\n\t}\n}\n\nvar (\n\tdialStreamFn = dialStream\n\tdialPollFn   = dialPoll\n)\n\nfunc canonicalHeaderHost(urlHost, scheme string) string {\n\thost, port, err := net.SplitHostPort(urlHost)\n\tif err != nil {\n\t\treturn urlHost\n\t}\n\n\tdefaultPort := \"\"\n\tswitch scheme {\n\tcase \"https\":\n\t\tdefaultPort = \"443\"\n\tcase \"http\":\n\t\tdefaultPort = \"80\"\n\t}\n\tif defaultPort == \"\" || port != defaultPort {\n\t\treturn urlHost\n\t}\n\n\t// If we strip the port from an IPv6 literal, re-add brackets to keep the Host header valid.\n\tif strings.Contains(host, \":\") {\n\t\treturn \"[\" + host + \"]\"\n\t}\n\treturn host\n}\n\nfunc parseTunnelToken(body []byte) (string, error) {\n\tresp, err := parseAuthorizeResponse(body)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn resp.token, nil\n}\n\ntype httpClientTarget struct {\n\tscheme     string\n\turlHost    string\n\theaderHost string\n}\n\nfunc buildHTTPTransport(serverAddress string, tlsEnabled bool, hostOverride string, dialContext func(ctx context.Context, network, addr string) (net.Conn, error), maxIdleConns int) (*http.Transport, httpClientTarget, error) {\n\tif dialContext == nil {\n\t\tpanic(\"httpmask: DialContext is nil\")\n\t}\n\n\tscheme, urlHost, dialAddr, serverName, err := normalizeHTTPDialTarget(serverAddress, tlsEnabled, hostOverride)\n\tif err != nil {\n\t\treturn nil, httpClientTarget{}, err\n\t}\n\n\ttransport := &http.Transport{\n\t\tForceAttemptHTTP2:     scheme == \"https\",\n\t\tDisableCompression:    true,\n\t\tMaxIdleConns:          maxIdleConns,\n\t\tMaxIdleConnsPerHost:   maxIdleConns,\n\t\tIdleConnTimeout:       30 * time.Second,\n\t\tResponseHeaderTimeout: 20 * time.Second,\n\t\tTLSHandshakeTimeout:   10 * time.Second,\n\t\tDialContext: func(dialCtx context.Context, network, _ string) (net.Conn, error) {\n\t\t\treturn dialContext(dialCtx, network, dialAddr)\n\t\t},\n\t}\n\tif scheme == \"https\" {\n\t\tvar tlsConf *tls.Config\n\t\ttlsConf, err = ca.GetTLSConfig(ca.Option{TLSConfig: &tls.Config{\n\t\t\tServerName: serverName,\n\t\t\tMinVersion: tls.VersionTLS12,\n\t\t}})\n\t\tif err != nil {\n\t\t\treturn nil, httpClientTarget{}, err\n\t\t}\n\t\ttransport.TLSClientConfig = tlsConf\n\t}\n\n\treturn transport, httpClientTarget{\n\t\tscheme:     scheme,\n\t\turlHost:    urlHost,\n\t\theaderHost: canonicalHeaderHost(urlHost, scheme),\n\t}, nil\n}\n\nfunc newHTTPClient(serverAddress string, opts TunnelDialOptions, maxIdleConns int) (*http.Client, httpClientTarget, error) {\n\ttransport, target, err := buildHTTPTransport(serverAddress, opts.TLSEnabled, opts.HostOverride, opts.DialContext, maxIdleConns)\n\tif err != nil {\n\t\treturn nil, httpClientTarget{}, err\n\t}\n\treturn &http.Client{Transport: transport}, target, nil\n}\n\ntype sessionDialInfo struct {\n\tclient     *http.Client\n\tpushURL    string\n\tpullURL    string\n\tfinURL     string\n\tcloseURL   string\n\theaderHost string\n\tauth       *tunnelAuth\n}\n\ntype httpStatusError struct {\n\tcode   int\n\tstatus string\n}\n\nfunc (e *httpStatusError) Error() string {\n\tif e == nil {\n\t\treturn \"bad status\"\n\t}\n\tif e.status != \"\" {\n\t\treturn \"bad status: \" + e.status\n\t}\n\treturn \"bad status\"\n}\n\nfunc isRetryableStatusCode(code int) bool {\n\treturn code == http.StatusRequestTimeout || code == http.StatusTooManyRequests || code >= 500\n}\n\ntype idleConnCloser interface{ CloseIdleConnections() }\n\nfunc closeIdleConnections(client *http.Client) {\n\tif client == nil || client.Transport == nil {\n\t\treturn\n\t}\n\tif c, ok := client.Transport.(idleConnCloser); ok {\n\t\tc.CloseIdleConnections()\n\t}\n}\n\nfunc dialSessionWithClient(ctx context.Context, client *http.Client, target httpClientTarget, mode TunnelMode, opts TunnelDialOptions) (*sessionDialInfo, error) {\n\tif client == nil {\n\t\treturn nil, fmt.Errorf(\"nil http client\")\n\t}\n\n\tauth := newTunnelAuth(opts.AuthKey, 0)\n\tauthorizeURL := (&url.URL{Scheme: target.scheme, Host: target.urlHost, Path: joinPathRoot(opts.PathRoot, \"/session\")}).String()\n\tif opts.EarlyHandshake != nil && len(opts.EarlyHandshake.RequestPayload) > 0 {\n\t\tvar err error\n\t\tauthorizeURL, err = setEarlyDataQuery(authorizeURL, opts.EarlyHandshake.RequestPayload)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tvar bodyBytes []byte\n\tfor attempt := 0; ; attempt++ {\n\t\treq, err := http.NewRequestWithContext(ctx, http.MethodGet, authorizeURL, nil)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treq.Host = target.headerHost\n\t\tapplyTunnelHeaders(req.Header, target.headerHost, mode)\n\t\tapplyTunnelAuth(req, auth, mode, http.MethodGet, \"/session\")\n\n\t\tresp, err := client.Do(req)\n\t\tif err != nil {\n\t\t\t// Transient failure on reused keep-alive conns (multiplex=auto). Retry a few times.\n\t\t\tif attempt < 2 && (isDialError(err) || isRetryableRequestError(err)) {\n\t\t\t\tcloseIdleConnections(client)\n\t\t\t\tselect {\n\t\t\t\tcase <-time.After(25 * time.Millisecond):\n\t\t\t\t\tcontinue\n\t\t\t\tcase <-ctx.Done():\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil, err\n\t\t}\n\n\t\tbodyBytes, err = io.ReadAll(io.LimitReader(resp.Body, 4*1024))\n\t\t_ = resp.Body.Close()\n\t\tif err != nil {\n\t\t\tif attempt < 2 && isRetryableRequestError(err) {\n\t\t\t\tcloseIdleConnections(client)\n\t\t\t\tselect {\n\t\t\t\tcase <-time.After(25 * time.Millisecond):\n\t\t\t\t\tcontinue\n\t\t\t\tcase <-ctx.Done():\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif resp.StatusCode != http.StatusOK {\n\t\t\t// Retry some transient proxy/CDN errors.\n\t\t\tif attempt < 2 && resp.StatusCode >= 500 {\n\t\t\t\tcloseIdleConnections(client)\n\t\t\t\tselect {\n\t\t\t\tcase <-time.After(25 * time.Millisecond):\n\t\t\t\t\tcontinue\n\t\t\t\tcase <-ctx.Done():\n\t\t\t\t\treturn nil, fmt.Errorf(\"%s authorize bad status: %s (%s)\", mode, resp.Status, strings.TrimSpace(string(bodyBytes)))\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn nil, fmt.Errorf(\"%s authorize bad status: %s (%s)\", mode, resp.Status, strings.TrimSpace(string(bodyBytes)))\n\t\t}\n\t\tbreak\n\t}\n\n\tauthResp, err := parseAuthorizeResponse(bodyBytes)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"%s authorize failed: %q\", mode, strings.TrimSpace(string(bodyBytes)))\n\t}\n\ttoken := authResp.token\n\tif token == \"\" {\n\t\treturn nil, fmt.Errorf(\"%s authorize empty token\", mode)\n\t}\n\tif opts.EarlyHandshake != nil && len(authResp.earlyPayload) > 0 && opts.EarlyHandshake.HandleResponse != nil {\n\t\tif err := opts.EarlyHandshake.HandleResponse(authResp.earlyPayload); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tpushURL := (&url.URL{Scheme: target.scheme, Host: target.urlHost, Path: joinPathRoot(opts.PathRoot, \"/api/v1/upload\"), RawQuery: \"token=\" + url.QueryEscape(token)}).String()\n\tpullURL := (&url.URL{Scheme: target.scheme, Host: target.urlHost, Path: joinPathRoot(opts.PathRoot, \"/stream\"), RawQuery: \"token=\" + url.QueryEscape(token)}).String()\n\tfinURL := (&url.URL{Scheme: target.scheme, Host: target.urlHost, Path: joinPathRoot(opts.PathRoot, \"/api/v1/upload\"), RawQuery: \"token=\" + url.QueryEscape(token) + \"&fin=1\"}).String()\n\tcloseURL := (&url.URL{Scheme: target.scheme, Host: target.urlHost, Path: joinPathRoot(opts.PathRoot, \"/api/v1/upload\"), RawQuery: \"token=\" + url.QueryEscape(token) + \"&close=1\"}).String()\n\n\treturn &sessionDialInfo{\n\t\tclient:     client,\n\t\tpushURL:    pushURL,\n\t\tpullURL:    pullURL,\n\t\tfinURL:     finURL,\n\t\tcloseURL:   closeURL,\n\t\theaderHost: target.headerHost,\n\t\tauth:       auth,\n\t}, nil\n}\n\nfunc dialSession(ctx context.Context, serverAddress string, opts TunnelDialOptions, mode TunnelMode) (*sessionDialInfo, error) {\n\tclient, target, err := newHTTPClient(serverAddress, opts, 32)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn dialSessionWithClient(ctx, client, target, mode, opts)\n}\n\nfunc bestEffortCloseSession(client *http.Client, closeURL, headerHost string, mode TunnelMode, auth *tunnelAuth) {\n\tif client == nil || closeURL == \"\" || headerHost == \"\" {\n\t\treturn\n\t}\n\n\tcloseCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\tdefer cancel()\n\n\treq, err := http.NewRequestWithContext(closeCtx, http.MethodPost, closeURL, nil)\n\tif err != nil {\n\t\treturn\n\t}\n\treq.Host = headerHost\n\tapplyTunnelHeaders(req.Header, headerHost, mode)\n\tapplyTunnelAuth(req, auth, mode, http.MethodPost, \"/api/v1/upload\")\n\n\tresp, err := client.Do(req)\n\tif err != nil || resp == nil {\n\t\treturn\n\t}\n\t_, _ = io.Copy(io.Discard, io.LimitReader(resp.Body, 4*1024))\n\t_ = resp.Body.Close()\n}\n\nfunc bestEffortCloseWriteSession(client *http.Client, finURL, headerHost string, mode TunnelMode, auth *tunnelAuth) {\n\tif client == nil || finURL == \"\" || headerHost == \"\" {\n\t\treturn\n\t}\n\n\tcloseCtx, cancel := context.WithTimeout(context.Background(), 5*time.Second)\n\tdefer cancel()\n\n\treq, err := http.NewRequestWithContext(closeCtx, http.MethodPost, finURL, nil)\n\tif err != nil {\n\t\treturn\n\t}\n\treq.Host = headerHost\n\tapplyTunnelHeaders(req.Header, headerHost, mode)\n\tapplyTunnelAuth(req, auth, mode, http.MethodPost, \"/api/v1/upload\")\n\n\tresp, err := client.Do(req)\n\tif err != nil || resp == nil {\n\t\treturn\n\t}\n\t_, _ = io.Copy(io.Discard, io.LimitReader(resp.Body, 4*1024))\n\t_ = resp.Body.Close()\n}\n\nfunc dialStreamWithClient(ctx context.Context, client *http.Client, target httpClientTarget, opts TunnelDialOptions) (net.Conn, error) {\n\t// \"stream\" mode uses split-stream to stay CDN-friendly by default.\n\treturn dialStreamSplitWithClient(ctx, client, target, opts)\n}\n\nfunc dialStream(ctx context.Context, serverAddress string, opts TunnelDialOptions) (net.Conn, error) {\n\t// \"stream\" mode uses split-stream to stay CDN-friendly by default.\n\treturn dialStreamSplit(ctx, serverAddress, opts)\n}\n\ntype queuedConn struct {\n\trxc    chan []byte\n\tclosed chan struct{}\n\n\twriteCh chan []byte\n\t// writeClosed is closed by CloseWrite to stop accepting new payloads.\n\t// When closed, Write returns io.ErrClosedPipe, but Read is unaffected.\n\twriteClosed chan struct{}\n\n\tmu         sync.Mutex\n\treadBuf    []byte\n\tcloseErr   error\n\tlocalAddr  net.Addr\n\tremoteAddr net.Addr\n}\n\nfunc (c *queuedConn) CloseWrite() error {\n\tif c == nil || c.writeClosed == nil {\n\t\treturn nil\n\t}\n\tc.mu.Lock()\n\tif !isClosedPipeChan(c.writeClosed) {\n\t\tclose(c.writeClosed)\n\t}\n\tc.mu.Unlock()\n\treturn nil\n}\n\nfunc (c *queuedConn) closeWithError(err error) error {\n\tc.mu.Lock()\n\tselect {\n\tcase <-c.closed:\n\t\tc.mu.Unlock()\n\t\treturn nil\n\tdefault:\n\t\tif err == nil {\n\t\t\terr = io.ErrClosedPipe\n\t\t}\n\t\tif c.closeErr == nil {\n\t\t\tc.closeErr = err\n\t\t}\n\t\tclose(c.closed)\n\t}\n\tc.mu.Unlock()\n\treturn nil\n}\n\nfunc (c *queuedConn) closedErr() error {\n\tc.mu.Lock()\n\terr := c.closeErr\n\tc.mu.Unlock()\n\tif err == nil {\n\t\treturn io.ErrClosedPipe\n\t}\n\treturn err\n}\n\nfunc (c *queuedConn) Read(b []byte) (n int, err error) {\n\tif len(c.readBuf) == 0 {\n\t\tselect {\n\t\tcase c.readBuf = <-c.rxc:\n\t\tcase <-c.closed:\n\t\t\treturn 0, c.closedErr()\n\t\t}\n\t}\n\tn = copy(b, c.readBuf)\n\tc.readBuf = c.readBuf[n:]\n\treturn n, nil\n}\n\nfunc (c *queuedConn) Write(b []byte) (n int, err error) {\n\tif len(b) == 0 {\n\t\treturn 0, nil\n\t}\n\tc.mu.Lock()\n\tselect {\n\tcase <-c.closed:\n\t\tc.mu.Unlock()\n\t\treturn 0, c.closedErr()\n\tcase <-c.writeClosed:\n\t\tc.mu.Unlock()\n\t\treturn 0, io.ErrClosedPipe\n\tdefault:\n\t}\n\tc.mu.Unlock()\n\n\tpayload := make([]byte, len(b))\n\tcopy(payload, b)\n\tselect {\n\tcase c.writeCh <- payload:\n\t\treturn len(b), nil\n\tcase <-c.closed:\n\t\treturn 0, c.closedErr()\n\tcase <-c.writeClosed:\n\t\treturn 0, io.ErrClosedPipe\n\t}\n}\n\nfunc (c *queuedConn) LocalAddr() net.Addr  { return c.localAddr }\nfunc (c *queuedConn) RemoteAddr() net.Addr { return c.remoteAddr }\n\nfunc (c *queuedConn) SetDeadline(time.Time) error      { return nil }\nfunc (c *queuedConn) SetReadDeadline(time.Time) error  { return nil }\nfunc (c *queuedConn) SetWriteDeadline(time.Time) error { return nil }\n\ntype streamSplitConn struct {\n\tqueuedConn\n\n\tctx    context.Context\n\tcancel context.CancelFunc\n\n\tclient     *http.Client\n\tpushURL    string\n\tpullURL    string\n\tfinURL     string\n\tcloseURL   string\n\theaderHost string\n\tauth       *tunnelAuth\n}\n\nfunc (c *streamSplitConn) closeWithError(err error) error {\n\t_ = c.queuedConn.closeWithError(err)\n\tif c.cancel != nil {\n\t\tc.cancel()\n\t}\n\tbestEffortCloseSession(c.client, c.closeURL, c.headerHost, TunnelModeStream, c.auth)\n\treturn nil\n}\n\nfunc (c *streamSplitConn) Close() error { return c.closeWithError(io.ErrClosedPipe) }\n\nfunc newStreamSplitConnFromInfo(info *sessionDialInfo) *streamSplitConn {\n\tif info == nil {\n\t\treturn nil\n\t}\n\n\tconnCtx, cancel := context.WithCancel(context.Background())\n\tc := &streamSplitConn{\n\t\tctx:        connCtx,\n\t\tcancel:     cancel,\n\t\tclient:     info.client,\n\t\tpushURL:    info.pushURL,\n\t\tpullURL:    info.pullURL,\n\t\tfinURL:     info.finURL,\n\t\tcloseURL:   info.closeURL,\n\t\theaderHost: info.headerHost,\n\t\tauth:       info.auth,\n\t\tqueuedConn: queuedConn{\n\t\t\trxc:         make(chan []byte, 256),\n\t\t\tclosed:      make(chan struct{}),\n\t\t\twriteCh:     make(chan []byte, 256),\n\t\t\twriteClosed: make(chan struct{}),\n\t\t\tlocalAddr:   &net.TCPAddr{},\n\t\t\tremoteAddr:  &net.TCPAddr{},\n\t\t},\n\t}\n\n\tgo c.pullLoop()\n\tgo c.pushLoop()\n\treturn c\n}\n\nfunc dialStreamSplitWithClient(ctx context.Context, client *http.Client, target httpClientTarget, opts TunnelDialOptions) (net.Conn, error) {\n\tinfo, err := dialSessionWithClient(ctx, client, target, TunnelModeStream, opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tc := newStreamSplitConnFromInfo(info)\n\tif c == nil {\n\t\treturn nil, fmt.Errorf(\"failed to build stream split conn\")\n\t}\n\toutConn, err := applyEarlyHandshakeOrUpgrade(c, opts)\n\tif err != nil {\n\t\t_ = c.Close()\n\t\treturn nil, err\n\t}\n\treturn outConn, nil\n}\n\nfunc dialStreamSplit(ctx context.Context, serverAddress string, opts TunnelDialOptions) (net.Conn, error) {\n\tinfo, err := dialSession(ctx, serverAddress, opts, TunnelModeStream)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tc := newStreamSplitConnFromInfo(info)\n\tif c == nil {\n\t\treturn nil, fmt.Errorf(\"failed to build stream split conn\")\n\t}\n\toutConn, err := applyEarlyHandshakeOrUpgrade(c, opts)\n\tif err != nil {\n\t\t_ = c.Close()\n\t\treturn nil, err\n\t}\n\treturn outConn, nil\n}\n\nfunc (c *streamSplitConn) pullLoop() {\n\tconst (\n\t\t// requestTimeout must be long enough for continuous high-throughput streams (e.g. mux + large downloads).\n\t\t// If it is too short, the client cancels the response mid-body and corrupts the byte stream.\n\t\trequestTimeout = 2 * time.Minute\n\t\treadChunkSize  = 32 * 1024\n\t\tidleBackoff    = 25 * time.Millisecond\n\t\tmaxDialRetry   = 12\n\t\tminBackoff     = 10 * time.Millisecond\n\t\tmaxBackoff     = 250 * time.Millisecond\n\t)\n\n\tvar (\n\t\tdialRetry int\n\t\tbackoff   = minBackoff\n\t)\n\tbuf := make([]byte, readChunkSize)\n\tfor {\n\t\tselect {\n\t\tcase <-c.closed:\n\t\t\treturn\n\t\tdefault:\n\t\t}\n\n\t\treqCtx, cancel := context.WithTimeout(c.ctx, requestTimeout)\n\t\treq, err := http.NewRequestWithContext(reqCtx, http.MethodGet, c.pullURL, nil)\n\t\tif err != nil {\n\t\t\tcancel()\n\t\t\t_ = c.closeWithError(fmt.Errorf(\"stream pull build request failed: %w\", err))\n\t\t\treturn\n\t\t}\n\t\treq.Host = c.headerHost\n\t\tapplyTunnelHeaders(req.Header, c.headerHost, TunnelModeStream)\n\t\tapplyTunnelAuth(req, c.auth, TunnelModeStream, http.MethodGet, \"/stream\")\n\n\t\tresp, err := c.client.Do(req)\n\t\tif err != nil {\n\t\t\tcancel()\n\t\t\tif (isDialError(err) || isRetryableRequestError(err)) && dialRetry < maxDialRetry {\n\t\t\t\tdialRetry++\n\t\t\t\tcloseIdleConnections(c.client)\n\t\t\t\tselect {\n\t\t\t\tcase <-time.After(backoff):\n\t\t\t\tcase <-c.closed:\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tbackoff *= 2\n\t\t\t\tif backoff > maxBackoff {\n\t\t\t\t\tbackoff = maxBackoff\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t_ = c.closeWithError(fmt.Errorf(\"stream pull request failed: %w\", err))\n\t\t\treturn\n\t\t}\n\t\tdialRetry = 0\n\t\tbackoff = minBackoff\n\n\t\tif resp.StatusCode != http.StatusOK {\n\t\t\tif isRetryableStatusCode(resp.StatusCode) && dialRetry < maxDialRetry {\n\t\t\t\tdialRetry++\n\t\t\t\t_, _ = io.Copy(io.Discard, io.LimitReader(resp.Body, 4*1024))\n\t\t\t\t_ = resp.Body.Close()\n\t\t\t\tcancel()\n\t\t\t\tcloseIdleConnections(c.client)\n\t\t\t\tselect {\n\t\t\t\tcase <-time.After(backoff):\n\t\t\t\tcase <-c.closed:\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tbackoff *= 2\n\t\t\t\tif backoff > maxBackoff {\n\t\t\t\t\tbackoff = maxBackoff\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t_ = resp.Body.Close()\n\t\t\tcancel()\n\t\t\t_ = c.closeWithError(fmt.Errorf(\"stream pull bad status: %s\", resp.Status))\n\t\t\treturn\n\t\t}\n\n\t\treadAny := false\n\t\tfor {\n\t\t\tn, rerr := resp.Body.Read(buf)\n\t\t\tif n > 0 {\n\t\t\t\treadAny = true\n\t\t\t\tpayload := make([]byte, n)\n\t\t\t\tcopy(payload, buf[:n])\n\t\t\t\tselect {\n\t\t\t\tcase c.rxc <- payload:\n\t\t\t\tcase <-c.closed:\n\t\t\t\t\t_ = resp.Body.Close()\n\t\t\t\t\tcancel()\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\t\tif rerr != nil {\n\t\t\t\t_ = resp.Body.Close()\n\t\t\t\tcancel()\n\t\t\t\tif errors.Is(rerr, io.EOF) {\n\t\t\t\t\t// Long-poll ended; retry.\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\t// Some environments may sporadically reset the HTTP connection under load; treat\n\t\t\t\t// it as an ended long-poll and retry instead of tearing down the whole tunnel.\n\t\t\t\tif errors.Is(rerr, io.ErrUnexpectedEOF) || isRetryableRequestError(rerr) {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\t_ = c.closeWithError(fmt.Errorf(\"stream pull read failed: %w\", rerr))\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tcancel()\n\t\tif !readAny {\n\t\t\t// Avoid tight loop if the server replied quickly with an empty body.\n\t\t\tselect {\n\t\t\tcase <-time.After(idleBackoff):\n\t\t\tcase <-c.closed:\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (c *streamSplitConn) pushLoop() {\n\tconst (\n\t\t// Batching is critical for stability under high concurrency: every flush is a new TCP\n\t\t// connection in HTTP/1.1, and too many tiny uploads can overwhelm the accept backlog,\n\t\t// causing sporadic RSTs (connection reset by peer).\n\t\t//\n\t\t// Keep this below the server-side maxUploadBytes limit in streamPush().\n\t\tmaxBatchBytes  = 512 * 1024\n\t\tflushInterval  = 25 * time.Millisecond\n\t\trequestTimeout = 20 * time.Second\n\t\tmaxDialRetry   = 12\n\t\tminBackoff     = 10 * time.Millisecond\n\t\tmaxBackoff     = 250 * time.Millisecond\n\t)\n\n\tvar (\n\t\tbuf   bytes.Buffer\n\t\ttimer = time.NewTimer(flushInterval)\n\t)\n\tdefer timer.Stop()\n\n\tflush := func() error {\n\t\tif buf.Len() == 0 {\n\t\t\treturn nil\n\t\t}\n\n\t\tpayload := buf.Bytes()\n\t\treqCtx, cancel := context.WithTimeout(c.ctx, requestTimeout)\n\t\treq, err := http.NewRequestWithContext(reqCtx, http.MethodPost, c.pushURL, bytes.NewReader(payload))\n\t\tif err != nil {\n\t\t\tcancel()\n\t\t\treturn err\n\t\t}\n\t\t// Be explicit: some http client forks won't auto-populate GetBody, which makes POST retries on stale\n\t\t// keep-alive connections flaky under multiplex=auto.\n\t\treq.GetBody = func() (io.ReadCloser, error) {\n\t\t\treturn io.NopCloser(bytes.NewReader(payload)), nil\n\t\t}\n\t\treq.Host = c.headerHost\n\t\tapplyTunnelHeaders(req.Header, c.headerHost, TunnelModeStream)\n\t\tapplyTunnelAuth(req, c.auth, TunnelModeStream, http.MethodPost, \"/api/v1/upload\")\n\t\treq.Header.Set(\"Content-Type\", \"application/octet-stream\")\n\n\t\tresp, err := c.client.Do(req)\n\t\tif err != nil {\n\t\t\tcancel()\n\t\t\treturn err\n\t\t}\n\t\t_, _ = io.Copy(io.Discard, io.LimitReader(resp.Body, 4*1024))\n\t\t_ = resp.Body.Close()\n\t\tcancel()\n\t\tif resp.StatusCode != http.StatusOK {\n\t\t\treturn &httpStatusError{code: resp.StatusCode, status: resp.Status}\n\t\t}\n\n\t\tbuf.Reset()\n\t\treturn nil\n\t}\n\n\tflushWithRetry := func() error {\n\t\tdialRetry := 0\n\t\tbackoff := minBackoff\n\t\tfor {\n\t\t\tif err := flush(); err == nil {\n\t\t\t\treturn nil\n\t\t\t} else if se := (*httpStatusError)(nil); errors.As(err, &se) && isRetryableStatusCode(se.code) && dialRetry < maxDialRetry {\n\t\t\t\tdialRetry++\n\t\t\t\tcloseIdleConnections(c.client)\n\t\t\t\tselect {\n\t\t\t\tcase <-time.After(backoff):\n\t\t\t\tcase <-c.closed:\n\t\t\t\t\treturn io.ErrClosedPipe\n\t\t\t\t}\n\t\t\t\tbackoff *= 2\n\t\t\t\tif backoff > maxBackoff {\n\t\t\t\t\tbackoff = maxBackoff\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t} else if (isDialError(err) || isRetryableRequestError(err)) && dialRetry < maxDialRetry {\n\t\t\t\tdialRetry++\n\t\t\t\tcloseIdleConnections(c.client)\n\t\t\t\tselect {\n\t\t\t\tcase <-time.After(backoff):\n\t\t\t\tcase <-c.closed:\n\t\t\t\t\treturn io.ErrClosedPipe\n\t\t\t\t}\n\t\t\t\tbackoff *= 2\n\t\t\t\tif backoff > maxBackoff {\n\t\t\t\t\tbackoff = maxBackoff\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t} else {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\tresetTimer := func() {\n\t\tif !timer.Stop() {\n\t\t\tselect {\n\t\t\tcase <-timer.C:\n\t\t\tdefault:\n\t\t\t}\n\t\t}\n\t\ttimer.Reset(flushInterval)\n\t}\n\n\tresetTimer()\n\n\tfor {\n\t\tselect {\n\t\tcase b, ok := <-c.writeCh:\n\t\t\tif !ok {\n\t\t\t\t_ = flushWithRetry()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif len(b) == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif buf.Len()+len(b) > maxBatchBytes {\n\t\t\t\tif err := flushWithRetry(); err != nil {\n\t\t\t\t\t_ = c.closeWithError(fmt.Errorf(\"stream push flush failed: %w\", err))\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tresetTimer()\n\t\t\t}\n\t\t\t_, _ = buf.Write(b)\n\t\t\tif buf.Len() >= maxBatchBytes {\n\t\t\t\tif err := flushWithRetry(); err != nil {\n\t\t\t\t\t_ = c.closeWithError(fmt.Errorf(\"stream push flush failed: %w\", err))\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tresetTimer()\n\t\t\t}\n\t\tcase <-timer.C:\n\t\t\tif err := flushWithRetry(); err != nil {\n\t\t\t\t_ = c.closeWithError(fmt.Errorf(\"stream push flush failed: %w\", err))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tresetTimer()\n\t\tcase <-c.writeClosed:\n\t\t\t// Drain any already-accepted writes so CloseWrite does not lose data.\n\t\t\tfor {\n\t\t\t\tselect {\n\t\t\t\tcase b := <-c.writeCh:\n\t\t\t\t\tif len(b) == 0 {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tif buf.Len()+len(b) > maxBatchBytes {\n\t\t\t\t\t\tif err := flushWithRetry(); err != nil {\n\t\t\t\t\t\t\t_ = c.closeWithError(fmt.Errorf(\"stream push flush failed: %w\", err))\n\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\t_, _ = buf.Write(b)\n\t\t\t\tdefault:\n\t\t\t\t\t_ = flushWithRetry()\n\t\t\t\t\tbestEffortCloseWriteSession(c.client, c.finURL, c.headerHost, TunnelModeStream, c.auth)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\tcase <-c.closed:\n\t\t\t_ = flushWithRetry()\n\t\t\treturn\n\t\t}\n\t}\n}\n\ntype pollConn struct {\n\tqueuedConn\n\n\tctx    context.Context\n\tcancel context.CancelFunc\n\n\tclient     *http.Client\n\tpushURL    string\n\tpullURL    string\n\tfinURL     string\n\tcloseURL   string\n\theaderHost string\n\tauth       *tunnelAuth\n}\n\nfunc isDialError(err error) bool {\n\tvar urlErr *url.Error\n\tif errors.As(err, &urlErr) {\n\t\treturn isDialError(urlErr.Err)\n\t}\n\tvar opErr *net.OpError\n\tif errors.As(err, &opErr) {\n\t\tif opErr.Op == \"dial\" || opErr.Op == \"connect\" {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc isRetryableRequestError(err error) bool {\n\tif err == nil {\n\t\treturn false\n\t}\n\tif errors.Is(err, context.Canceled) || errors.Is(err, context.DeadlineExceeded) {\n\t\treturn false\n\t}\n\tif errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {\n\t\treturn true\n\t}\n\t// net/http may return this when reusing a keep-alive conn that the peer already closed.\n\t// Treat it as retryable: callers already implement bounded backoff retries.\n\tif strings.Contains(strings.ToLower(err.Error()), \"server closed idle connection\") {\n\t\treturn true\n\t}\n\n\t// Unwrap common wrappers.\n\tvar urlErr *url.Error\n\tif errors.As(err, &urlErr) {\n\t\treturn isRetryableRequestError(urlErr.Err)\n\t}\n\n\t// Connection-level transient failures.\n\tif errors.Is(err, syscall.EPIPE) || errors.Is(err, syscall.ECONNRESET) {\n\t\treturn true\n\t}\n\tif errors.Is(err, io.ErrClosedPipe) || errors.Is(err, net.ErrClosed) {\n\t\treturn true\n\t}\n\n\tvar netErr net.Error\n\tif errors.As(err, &netErr) {\n\t\treturn netErr.Timeout() || netErr.Temporary()\n\t}\n\treturn false\n}\n\nfunc (c *pollConn) closeWithError(err error) error {\n\t_ = c.queuedConn.closeWithError(err)\n\tif c.cancel != nil {\n\t\tc.cancel()\n\t}\n\tbestEffortCloseSession(c.client, c.closeURL, c.headerHost, TunnelModePoll, c.auth)\n\treturn nil\n}\n\nfunc (c *pollConn) Close() error {\n\treturn c.closeWithError(io.ErrClosedPipe)\n}\n\nfunc newPollConnFromInfo(info *sessionDialInfo) *pollConn {\n\tif info == nil {\n\t\treturn nil\n\t}\n\n\tconnCtx, cancel := context.WithCancel(context.Background())\n\tc := &pollConn{\n\t\tctx:        connCtx,\n\t\tcancel:     cancel,\n\t\tclient:     info.client,\n\t\tpushURL:    info.pushURL,\n\t\tpullURL:    info.pullURL,\n\t\tfinURL:     info.finURL,\n\t\tcloseURL:   info.closeURL,\n\t\theaderHost: info.headerHost,\n\t\tauth:       info.auth,\n\t\tqueuedConn: queuedConn{\n\t\t\trxc:         make(chan []byte, 128),\n\t\t\tclosed:      make(chan struct{}),\n\t\t\twriteCh:     make(chan []byte, 256),\n\t\t\twriteClosed: make(chan struct{}),\n\t\t\tlocalAddr:   &net.TCPAddr{},\n\t\t\tremoteAddr:  &net.TCPAddr{},\n\t\t},\n\t}\n\n\tgo c.pullLoop()\n\tgo c.pushLoop()\n\treturn c\n}\n\nfunc dialPollWithClient(ctx context.Context, client *http.Client, target httpClientTarget, opts TunnelDialOptions) (net.Conn, error) {\n\tinfo, err := dialSessionWithClient(ctx, client, target, TunnelModePoll, opts)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tc := newPollConnFromInfo(info)\n\tif c == nil {\n\t\treturn nil, fmt.Errorf(\"failed to build poll conn\")\n\t}\n\toutConn, err := applyEarlyHandshakeOrUpgrade(c, opts)\n\tif err != nil {\n\t\t_ = c.Close()\n\t\treturn nil, err\n\t}\n\treturn outConn, nil\n}\n\nfunc dialPoll(ctx context.Context, serverAddress string, opts TunnelDialOptions) (net.Conn, error) {\n\tinfo, err := dialSession(ctx, serverAddress, opts, TunnelModePoll)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tc := newPollConnFromInfo(info)\n\tif c == nil {\n\t\treturn nil, fmt.Errorf(\"failed to build poll conn\")\n\t}\n\toutConn, err := applyEarlyHandshakeOrUpgrade(c, opts)\n\tif err != nil {\n\t\t_ = c.Close()\n\t\treturn nil, err\n\t}\n\treturn outConn, nil\n}\n\nfunc (c *pollConn) pullLoop() {\n\tconst (\n\t\tmaxDialRetry = 12\n\t\tminBackoff   = 10 * time.Millisecond\n\t\tmaxBackoff   = 250 * time.Millisecond\n\t)\n\tvar (\n\t\tdialRetry int\n\t\tbackoff   = minBackoff\n\t)\n\tfor {\n\t\tselect {\n\t\tcase <-c.closed:\n\t\t\treturn\n\t\tdefault:\n\t\t}\n\n\t\treqCtx, cancel := context.WithTimeout(c.ctx, 30*time.Second)\n\t\treq, err := http.NewRequestWithContext(reqCtx, http.MethodGet, c.pullURL, nil)\n\t\tif err != nil {\n\t\t\tcancel()\n\t\t\t_ = c.Close()\n\t\t\treturn\n\t\t}\n\t\treq.Host = c.headerHost\n\t\tapplyTunnelHeaders(req.Header, c.headerHost, TunnelModePoll)\n\t\tapplyTunnelAuth(req, c.auth, TunnelModePoll, http.MethodGet, \"/stream\")\n\n\t\tresp, err := c.client.Do(req)\n\t\tif err != nil {\n\t\t\tcancel()\n\t\t\tif (isDialError(err) || isRetryableRequestError(err)) && dialRetry < maxDialRetry {\n\t\t\t\tdialRetry++\n\t\t\t\tcloseIdleConnections(c.client)\n\t\t\t\tselect {\n\t\t\t\tcase <-time.After(backoff):\n\t\t\t\tcase <-c.closed:\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tbackoff *= 2\n\t\t\t\tif backoff > maxBackoff {\n\t\t\t\t\tbackoff = maxBackoff\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t_ = c.closeWithError(fmt.Errorf(\"poll pull request failed: %w\", err))\n\t\t\treturn\n\t\t}\n\t\tdialRetry = 0\n\t\tbackoff = minBackoff\n\n\t\tif resp.StatusCode != http.StatusOK {\n\t\t\tif isRetryableStatusCode(resp.StatusCode) && dialRetry < maxDialRetry {\n\t\t\t\tdialRetry++\n\t\t\t\t_, _ = io.Copy(io.Discard, io.LimitReader(resp.Body, 4*1024))\n\t\t\t\t_ = resp.Body.Close()\n\t\t\t\tcancel()\n\t\t\t\tcloseIdleConnections(c.client)\n\t\t\t\tselect {\n\t\t\t\tcase <-time.After(backoff):\n\t\t\t\tcase <-c.closed:\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tbackoff *= 2\n\t\t\t\tif backoff > maxBackoff {\n\t\t\t\t\tbackoff = maxBackoff\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t_ = resp.Body.Close()\n\t\t\tcancel()\n\t\t\t_ = c.closeWithError(fmt.Errorf(\"poll pull bad status: %s\", resp.Status))\n\t\t\treturn\n\t\t}\n\n\t\tscanner := bufio.NewScanner(resp.Body)\n\t\tfor scanner.Scan() {\n\t\t\tline := scanner.Text()\n\t\t\tif line == \"\" {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tpayload, err := base64.StdEncoding.DecodeString(line)\n\t\t\tif err != nil {\n\t\t\t\t_ = resp.Body.Close()\n\t\t\t\t_ = c.closeWithError(fmt.Errorf(\"poll pull decode failed: %w\", err))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tselect {\n\t\t\tcase c.rxc <- payload:\n\t\t\tcase <-c.closed:\n\t\t\t\t_ = resp.Body.Close()\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\t_ = resp.Body.Close()\n\t\tcancel()\n\t\tif err := scanner.Err(); err != nil {\n\t\t\t// Treat transient stream breaks (RST/EOF) as an ended long-poll and retry.\n\t\t\tif errors.Is(err, io.ErrUnexpectedEOF) || isRetryableRequestError(err) {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\t_ = c.closeWithError(fmt.Errorf(\"poll pull scan failed: %w\", err))\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc (c *pollConn) pushLoop() {\n\tconst (\n\t\tmaxBatchBytes   = 512 * 1024\n\t\tflushInterval   = 50 * time.Millisecond\n\t\tmaxLineRawBytes = 16 * 1024\n\t\tmaxDialRetry    = 12\n\t\tminBackoff      = 10 * time.Millisecond\n\t\tmaxBackoff      = 250 * time.Millisecond\n\t)\n\n\tvar (\n\t\tbuf        bytes.Buffer\n\t\tpendingRaw int\n\t\ttimer      = time.NewTimer(flushInterval)\n\t)\n\tdefer timer.Stop()\n\n\tflush := func() error {\n\t\tif buf.Len() == 0 {\n\t\t\treturn nil\n\t\t}\n\n\t\tpayload := buf.Bytes()\n\t\treqCtx, cancel := context.WithTimeout(c.ctx, 20*time.Second)\n\t\treq, err := http.NewRequestWithContext(reqCtx, http.MethodPost, c.pushURL, bytes.NewReader(payload))\n\t\tif err != nil {\n\t\t\tcancel()\n\t\t\treturn err\n\t\t}\n\t\treq.GetBody = func() (io.ReadCloser, error) {\n\t\t\treturn io.NopCloser(bytes.NewReader(payload)), nil\n\t\t}\n\t\treq.Host = c.headerHost\n\t\tapplyTunnelHeaders(req.Header, c.headerHost, TunnelModePoll)\n\t\tapplyTunnelAuth(req, c.auth, TunnelModePoll, http.MethodPost, \"/api/v1/upload\")\n\t\treq.Header.Set(\"Content-Type\", \"text/plain\")\n\n\t\tresp, err := c.client.Do(req)\n\t\tif err != nil {\n\t\t\tcancel()\n\t\t\treturn err\n\t\t}\n\t\t_, _ = io.Copy(io.Discard, io.LimitReader(resp.Body, 4*1024))\n\t\t_ = resp.Body.Close()\n\t\tcancel()\n\t\tif resp.StatusCode != http.StatusOK {\n\t\t\treturn &httpStatusError{code: resp.StatusCode, status: resp.Status}\n\t\t}\n\n\t\tbuf.Reset()\n\t\tpendingRaw = 0\n\t\treturn nil\n\t}\n\n\tflushWithRetry := func() error {\n\t\tdialRetry := 0\n\t\tbackoff := minBackoff\n\t\tfor {\n\t\t\tif err := flush(); err == nil {\n\t\t\t\treturn nil\n\t\t\t} else if se := (*httpStatusError)(nil); errors.As(err, &se) && isRetryableStatusCode(se.code) && dialRetry < maxDialRetry {\n\t\t\t\tdialRetry++\n\t\t\t\tcloseIdleConnections(c.client)\n\t\t\t\tselect {\n\t\t\t\tcase <-time.After(backoff):\n\t\t\t\tcase <-c.closed:\n\t\t\t\t\treturn c.closedErr()\n\t\t\t\t}\n\t\t\t\tbackoff *= 2\n\t\t\t\tif backoff > maxBackoff {\n\t\t\t\t\tbackoff = maxBackoff\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t} else if (isDialError(err) || isRetryableRequestError(err)) && dialRetry < maxDialRetry {\n\t\t\t\tdialRetry++\n\t\t\t\tcloseIdleConnections(c.client)\n\t\t\t\tselect {\n\t\t\t\tcase <-time.After(backoff):\n\t\t\t\tcase <-c.closed:\n\t\t\t\t\treturn c.closedErr()\n\t\t\t\t}\n\t\t\t\tbackoff *= 2\n\t\t\t\tif backoff > maxBackoff {\n\t\t\t\t\tbackoff = maxBackoff\n\t\t\t\t}\n\t\t\t\tcontinue\n\t\t\t} else {\n\t\t\t\treturn err\n\t\t\t}\n\t\t}\n\t}\n\n\tresetTimer := func() {\n\t\tif !timer.Stop() {\n\t\t\tselect {\n\t\t\tcase <-timer.C:\n\t\t\tdefault:\n\t\t\t}\n\t\t}\n\t\ttimer.Reset(flushInterval)\n\t}\n\n\tresetTimer()\n\n\tfor {\n\t\tselect {\n\t\tcase b, ok := <-c.writeCh:\n\t\t\tif !ok {\n\t\t\t\t_ = flushWithRetry()\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif len(b) == 0 {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// Split large writes into multiple base64 lines to cap per-line size.\n\t\t\tfor len(b) > 0 {\n\t\t\t\tchunk := b\n\t\t\t\tif len(chunk) > maxLineRawBytes {\n\t\t\t\t\tchunk = b[:maxLineRawBytes]\n\t\t\t\t}\n\t\t\t\tb = b[len(chunk):]\n\n\t\t\t\tencLen := base64.StdEncoding.EncodedLen(len(chunk))\n\t\t\t\tif pendingRaw+len(chunk) > maxBatchBytes || buf.Len()+encLen+1 > maxBatchBytes*2 {\n\t\t\t\t\tif err := flushWithRetry(); err != nil {\n\t\t\t\t\t\t_ = c.closeWithError(fmt.Errorf(\"poll push flush failed: %w\", err))\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\ttmp := make([]byte, base64.StdEncoding.EncodedLen(len(chunk)))\n\t\t\t\tbase64.StdEncoding.Encode(tmp, chunk)\n\t\t\t\tbuf.Write(tmp)\n\t\t\t\tbuf.WriteByte('\\n')\n\t\t\t\tpendingRaw += len(chunk)\n\t\t\t}\n\n\t\t\tif pendingRaw >= maxBatchBytes {\n\t\t\t\tif err := flushWithRetry(); err != nil {\n\t\t\t\t\t_ = c.closeWithError(fmt.Errorf(\"poll push flush failed: %w\", err))\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tresetTimer()\n\t\t\t}\n\t\tcase <-timer.C:\n\t\t\tif err := flushWithRetry(); err != nil {\n\t\t\t\t_ = c.closeWithError(fmt.Errorf(\"poll push flush failed: %w\", err))\n\t\t\t\treturn\n\t\t\t}\n\t\t\tresetTimer()\n\t\tcase <-c.writeClosed:\n\t\t\t// Drain any already-accepted writes so CloseWrite does not lose data.\n\t\t\tfor {\n\t\t\t\tselect {\n\t\t\t\tcase b := <-c.writeCh:\n\t\t\t\t\tif len(b) == 0 {\n\t\t\t\t\t\tcontinue\n\t\t\t\t\t}\n\t\t\t\t\tfor len(b) > 0 {\n\t\t\t\t\t\tchunk := b\n\t\t\t\t\t\tif len(chunk) > maxLineRawBytes {\n\t\t\t\t\t\t\tchunk = b[:maxLineRawBytes]\n\t\t\t\t\t\t}\n\t\t\t\t\t\tb = b[len(chunk):]\n\n\t\t\t\t\t\tencLen := base64.StdEncoding.EncodedLen(len(chunk))\n\t\t\t\t\t\tif pendingRaw+len(chunk) > maxBatchBytes || buf.Len()+encLen+1 > maxBatchBytes*2 {\n\t\t\t\t\t\t\tif err := flushWithRetry(); err != nil {\n\t\t\t\t\t\t\t\t_ = c.closeWithError(fmt.Errorf(\"poll push flush failed: %w\", err))\n\t\t\t\t\t\t\t\treturn\n\t\t\t\t\t\t\t}\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\ttmp := make([]byte, base64.StdEncoding.EncodedLen(len(chunk)))\n\t\t\t\t\t\tbase64.StdEncoding.Encode(tmp, chunk)\n\t\t\t\t\t\tbuf.Write(tmp)\n\t\t\t\t\t\tbuf.WriteByte('\\n')\n\t\t\t\t\t\tpendingRaw += len(chunk)\n\t\t\t\t\t}\n\t\t\t\tdefault:\n\t\t\t\t\t_ = flushWithRetry()\n\t\t\t\t\tbestEffortCloseWriteSession(c.client, c.finURL, c.headerHost, TunnelModePoll, c.auth)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t}\n\t\tcase <-c.closed:\n\t\t\t_ = flushWithRetry()\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc normalizeHTTPDialTarget(serverAddress string, tlsEnabled bool, hostOverride string) (scheme, urlHost, dialAddr, serverName string, err error) {\n\thost, port, err := net.SplitHostPort(serverAddress)\n\tif err != nil {\n\t\treturn \"\", \"\", \"\", \"\", fmt.Errorf(\"invalid server address %q: %w\", serverAddress, err)\n\t}\n\n\tif hostOverride != \"\" {\n\t\t// Allow \"example.com\" or \"example.com:443\"\n\t\tif h, p, splitErr := net.SplitHostPort(hostOverride); splitErr == nil {\n\t\t\tif h != \"\" {\n\t\t\t\thostOverride = h\n\t\t\t}\n\t\t\tif p != \"\" {\n\t\t\t\tport = p\n\t\t\t}\n\t\t}\n\t\tserverName = hostOverride\n\t\turlHost = net.JoinHostPort(hostOverride, port)\n\t} else {\n\t\tserverName = host\n\t\turlHost = net.JoinHostPort(host, port)\n\t}\n\n\tif tlsEnabled {\n\t\tscheme = \"https\"\n\t} else {\n\t\tscheme = \"http\"\n\t}\n\n\tdialAddr = net.JoinHostPort(host, port)\n\treturn scheme, urlHost, dialAddr, trimPortForHost(serverName), nil\n}\n\nfunc applyTunnelHeaders(h http.Header, host string, mode TunnelMode) {\n\tr := rngPool.Get().(*mrand.Rand)\n\tua := userAgents[r.Intn(len(userAgents))]\n\taccept := accepts[r.Intn(len(accepts))]\n\tlang := acceptLanguages[r.Intn(len(acceptLanguages))]\n\tenc := acceptEncodings[r.Intn(len(acceptEncodings))]\n\trngPool.Put(r)\n\n\th.Set(\"User-Agent\", ua)\n\th.Set(\"Accept\", accept)\n\th.Set(\"Accept-Language\", lang)\n\th.Set(\"Accept-Encoding\", enc)\n\th.Set(\"Cache-Control\", \"no-cache\")\n\th.Set(\"Pragma\", \"no-cache\")\n\th.Set(\"Connection\", \"keep-alive\")\n\th.Set(\"Host\", host)\n\th.Set(\"X-Sudoku-Tunnel\", string(mode))\n\th.Set(\"X-Sudoku-Version\", \"1\")\n}\n\ntype TunnelServerOptions struct {\n\tMode string\n\t// PathRoot is an optional first-level path prefix for all HTTP tunnel endpoints.\n\t// Example: \"aabbcc\" => \"/aabbcc/session\", \"/aabbcc/api/v1/upload\", ...\n\tPathRoot string\n\t// AuthKey enables short-term HMAC auth for HTTP tunnel requests (anti-probing).\n\t// When set (non-empty), the server requires each request to carry a valid Authorization bearer token.\n\tAuthKey string\n\t// AuthSkew controls allowed clock skew / replay window for AuthKey. 0 uses a conservative default.\n\tAuthSkew time.Duration\n\t// PassThroughOnReject controls how the server handles \"recognized but rejected\" tunnel requests\n\t// (e.g., wrong mode / wrong path / invalid token). When true, the request bytes are replayed back\n\t// to the caller as HandlePassThrough to allow higher-level fallback handling.\n\tPassThroughOnReject bool\n\t// PullReadTimeout controls how long the server long-poll waits for tunnel downlink data before replying with a keepalive newline.\n\tPullReadTimeout time.Duration\n\t// SessionTTL is a best-effort TTL to prevent leaked sessions. 0 uses a conservative default.\n\tSessionTTL time.Duration\n\t// EarlyHandshake optionally folds the protocol handshake into the initial HTTP/WS round trip.\n\tEarlyHandshake *TunnelServerEarlyHandshake\n}\n\ntype TunnelServer struct {\n\tmode                TunnelMode\n\tpathRoot            string\n\tpassThroughOnReject bool\n\tauth                *tunnelAuth\n\n\tpullReadTimeout time.Duration\n\tsessionTTL      time.Duration\n\tearlyHandshake  *TunnelServerEarlyHandshake\n\n\tmu       sync.Mutex\n\tsessions map[string]*tunnelSession\n}\n\ntype tunnelSession struct {\n\tconn       net.Conn\n\tlastActive time.Time\n}\n\nfunc NewTunnelServer(opts TunnelServerOptions) *TunnelServer {\n\tmode := normalizeTunnelMode(opts.Mode)\n\tif mode == TunnelModeLegacy {\n\t\t// Server-side \"legacy\" means: don't accept stream/poll tunnels; only passthrough.\n\t}\n\tpathRoot := normalizePathRoot(opts.PathRoot)\n\tauth := newTunnelAuth(opts.AuthKey, opts.AuthSkew)\n\ttimeout := opts.PullReadTimeout\n\tif timeout <= 0 {\n\t\ttimeout = 10 * time.Second\n\t}\n\tttl := opts.SessionTTL\n\tif ttl <= 0 {\n\t\tttl = 2 * time.Minute\n\t}\n\treturn &TunnelServer{\n\t\tmode:                mode,\n\t\tpathRoot:            pathRoot,\n\t\tauth:                auth,\n\t\tpassThroughOnReject: opts.PassThroughOnReject,\n\t\tpullReadTimeout:     timeout,\n\t\tsessionTTL:          ttl,\n\t\tearlyHandshake:      opts.EarlyHandshake,\n\t\tsessions:            make(map[string]*tunnelSession),\n\t}\n}\n\n// HandleConn inspects rawConn. If it is an HTTP tunnel request (X-Sudoku-Tunnel header), it is handled here and:\n//   - returns HandleStartTunnel + a net.Conn that carries the raw Sudoku stream (stream mode or poll session pipe)\n//   - or returns HandleDone if the HTTP request is a poll control request (push/pull) and no Sudoku handshake should run on this TCP conn\n//\n// If it is not an HTTP tunnel request (or server mode is legacy), it returns HandlePassThrough with a conn that replays any pre-read bytes.\nfunc (s *TunnelServer) HandleConn(rawConn net.Conn) (HandleResult, net.Conn, error) {\n\tif rawConn == nil {\n\t\treturn HandleDone, nil, errors.New(\"nil conn\")\n\t}\n\n\t// Small header read deadline to avoid stalling Accept loops. The actual Sudoku handshake has its own deadlines.\n\t_ = rawConn.SetReadDeadline(time.Now().Add(5 * time.Second))\n\tvar first [4]byte\n\tn, err := io.ReadFull(rawConn, first[:])\n\tif err != nil {\n\t\t_ = rawConn.SetReadDeadline(time.Time{})\n\t\t// Even if short-read, preserve bytes for downstream handlers.\n\t\tif n > 0 {\n\t\t\treturn HandlePassThrough, newPreBufferedConn(rawConn, first[:n]), nil\n\t\t}\n\t\treturn HandleDone, nil, err\n\t}\n\tpc := newPreBufferedConn(rawConn, first[:])\n\tbr := bufio.NewReader(pc)\n\n\tif !LooksLikeHTTPRequestStart(first[:]) {\n\t\t_ = rawConn.SetReadDeadline(time.Time{})\n\t\treturn HandlePassThrough, pc, nil\n\t}\n\n\treq, headerBytes, buffered, err := readHTTPHeader(br)\n\t_ = rawConn.SetReadDeadline(time.Time{})\n\tif err != nil {\n\t\t// Not a valid HTTP request; hand it back to the legacy path with replay.\n\t\tprefix := make([]byte, 0, len(first)+len(headerBytes)+len(buffered))\n\t\tif len(headerBytes) == 0 || !bytes.HasPrefix(headerBytes, first[:]) {\n\t\t\tprefix = append(prefix, first[:]...)\n\t\t}\n\t\tprefix = append(prefix, headerBytes...)\n\t\tprefix = append(prefix, buffered...)\n\t\treturn HandlePassThrough, newPreBufferedConn(rawConn, prefix), nil\n\t}\n\n\ttunnelHeader := strings.ToLower(strings.TrimSpace(req.headers[\"x-sudoku-tunnel\"]))\n\tif tunnelHeader == \"\" {\n\t\t// Some CDNs / forward proxies may strip unknown headers. When AuthKey is enabled, we can\n\t\t// safely infer the intended tunnel mode by verifying the Authorization token against\n\t\t// both stream/poll modes and picking the one that matches.\n\t\tif s.auth != nil {\n\t\t\tu, err := url.ParseRequestURI(req.target)\n\t\t\tif err == nil {\n\t\t\t\tpath, ok := stripPathRoot(s.pathRoot, u.Path)\n\t\t\t\tif ok && s.isAllowedBasePath(path) {\n\t\t\t\t\tauthVal := req.headers[\"authorization\"]\n\t\t\t\t\tif authVal == \"\" {\n\t\t\t\t\t\tauthVal = u.Query().Get(tunnelAuthQueryKey)\n\t\t\t\t\t}\n\t\t\t\t\tstreamOK := s.auth.verifyValue(authVal, TunnelModeStream, req.method, path, time.Now())\n\t\t\t\t\tpollOK := s.auth.verifyValue(authVal, TunnelModePoll, req.method, path, time.Now())\n\t\t\t\t\tswitch {\n\t\t\t\t\tcase streamOK && !pollOK:\n\t\t\t\t\t\ttunnelHeader = string(TunnelModeStream)\n\t\t\t\t\tcase pollOK && !streamOK:\n\t\t\t\t\t\ttunnelHeader = string(TunnelModePoll)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif tunnelHeader == \"\" {\n\t\t\t// Not our tunnel; replay full bytes to legacy handler.\n\t\t\tprefix := make([]byte, 0, len(headerBytes)+len(buffered))\n\t\t\tprefix = append(prefix, headerBytes...)\n\t\t\tprefix = append(prefix, buffered...)\n\t\t\treturn HandlePassThrough, newPreBufferedConn(rawConn, prefix), nil\n\t\t}\n\t}\n\n\treject := func() (HandleResult, net.Conn, error) {\n\t\tprefix := make([]byte, 0, len(headerBytes)+len(buffered))\n\t\tprefix = append(prefix, headerBytes...)\n\t\tprefix = append(prefix, buffered...)\n\t\treturn HandlePassThrough, newRejectedPreBufferedConn(rawConn, prefix), nil\n\t}\n\n\tif s.mode == TunnelModeLegacy {\n\t\tif s.passThroughOnReject {\n\t\t\treturn reject()\n\t\t}\n\t\t_ = writeSimpleHTTPResponse(rawConn, http.StatusNotFound, \"not found\")\n\t\t_ = rawConn.Close()\n\t\treturn HandleDone, nil, nil\n\t}\n\n\tswitch TunnelMode(tunnelHeader) {\n\tcase TunnelModeStream:\n\t\tif s.mode != TunnelModeStream && s.mode != TunnelModeAuto {\n\t\t\tif s.passThroughOnReject {\n\t\t\t\treturn reject()\n\t\t\t}\n\t\t\t_ = writeSimpleHTTPResponse(rawConn, http.StatusNotFound, \"not found\")\n\t\t\t_ = rawConn.Close()\n\t\t\treturn HandleDone, nil, nil\n\t\t}\n\t\treturn s.handleStream(rawConn, req, headerBytes, buffered)\n\tcase TunnelModePoll:\n\t\tif s.mode != TunnelModePoll && s.mode != TunnelModeAuto {\n\t\t\tif s.passThroughOnReject {\n\t\t\t\treturn reject()\n\t\t\t}\n\t\t\t_ = writeSimpleHTTPResponse(rawConn, http.StatusNotFound, \"not found\")\n\t\t\t_ = rawConn.Close()\n\t\t\treturn HandleDone, nil, nil\n\t\t}\n\t\treturn s.handlePoll(rawConn, req, headerBytes, buffered)\n\tcase TunnelModeWS:\n\t\tif s.mode != TunnelModeWS && s.mode != TunnelModeAuto {\n\t\t\tif s.passThroughOnReject {\n\t\t\t\treturn reject()\n\t\t\t}\n\t\t\t_ = writeSimpleHTTPResponse(rawConn, http.StatusNotFound, \"not found\")\n\t\t\t_ = rawConn.Close()\n\t\t\treturn HandleDone, nil, nil\n\t\t}\n\t\treturn s.handleWS(rawConn, req, headerBytes, buffered)\n\tdefault:\n\t\tif s.passThroughOnReject {\n\t\t\treturn reject()\n\t\t}\n\t\t_ = writeSimpleHTTPResponse(rawConn, http.StatusNotFound, \"not found\")\n\t\t_ = rawConn.Close()\n\t\treturn HandleDone, nil, nil\n\t}\n}\n\ntype httpRequestHeader struct {\n\tmethod  string\n\ttarget  string // path + query\n\tproto   string\n\theaders map[string]string // lower-case keys\n}\n\nfunc readHTTPHeader(r *bufio.Reader) (*httpRequestHeader, []byte, []byte, error) {\n\tconst maxHeaderBytes = 32 * 1024\n\n\tvar consumed bytes.Buffer\n\treadLine := func() ([]byte, error) {\n\t\tline, err := r.ReadSlice('\\n')\n\t\tif len(line) > 0 {\n\t\t\tif consumed.Len()+len(line) > maxHeaderBytes {\n\t\t\t\treturn line, fmt.Errorf(\"http header too large\")\n\t\t\t}\n\t\t\tconsumed.Write(line)\n\t\t}\n\t\treturn line, err\n\t}\n\n\t// Request line\n\tline, err := readLine()\n\tif err != nil {\n\t\treturn nil, consumed.Bytes(), readAllBuffered(r), err\n\t}\n\tlineStr := strings.TrimRight(string(line), \"\\r\\n\")\n\tparts := strings.SplitN(lineStr, \" \", 3)\n\tif len(parts) != 3 {\n\t\treturn nil, consumed.Bytes(), readAllBuffered(r), fmt.Errorf(\"invalid request line\")\n\t}\n\treq := &httpRequestHeader{\n\t\tmethod:  parts[0],\n\t\ttarget:  parts[1],\n\t\tproto:   parts[2],\n\t\theaders: make(map[string]string),\n\t}\n\n\t// Headers\n\tfor {\n\t\tline, err = readLine()\n\t\tif err != nil {\n\t\t\treturn nil, consumed.Bytes(), readAllBuffered(r), err\n\t\t}\n\t\ttrimmed := strings.TrimRight(string(line), \"\\r\\n\")\n\t\tif trimmed == \"\" {\n\t\t\tbreak\n\t\t}\n\t\tk, v, ok := strings.Cut(trimmed, \":\")\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\t\tk = strings.ToLower(strings.TrimSpace(k))\n\t\tv = strings.TrimSpace(v)\n\t\tif k == \"\" {\n\t\t\tcontinue\n\t\t}\n\t\t// Keep the first value; we only care about a small set.\n\t\tif _, exists := req.headers[k]; !exists {\n\t\t\treq.headers[k] = v\n\t\t}\n\t}\n\n\treturn req, consumed.Bytes(), readAllBuffered(r), nil\n}\n\nfunc readAllBuffered(r *bufio.Reader) []byte {\n\tn := r.Buffered()\n\tif n <= 0 {\n\t\treturn nil\n\t}\n\tb, err := r.Peek(n)\n\tif err != nil {\n\t\treturn nil\n\t}\n\tout := make([]byte, n)\n\tcopy(out, b)\n\treturn out\n}\n\ntype preBufferedConn struct {\n\tnet.Conn\n\tbuf      []byte\n\trecorded []byte\n\trejected bool\n}\n\nfunc (p *preBufferedConn) CloseWrite() error {\n\tif p == nil || p.Conn == nil {\n\t\treturn nil\n\t}\n\tif cw, ok := p.Conn.(interface{ CloseWrite() error }); ok {\n\t\treturn cw.CloseWrite()\n\t}\n\treturn nil\n}\n\nfunc (p *preBufferedConn) CloseRead() error {\n\tif p == nil || p.Conn == nil {\n\t\treturn nil\n\t}\n\tif cr, ok := p.Conn.(interface{ CloseRead() error }); ok {\n\t\treturn cr.CloseRead()\n\t}\n\treturn nil\n}\n\nfunc newPreBufferedConn(conn net.Conn, pre []byte) *preBufferedConn {\n\tcpy := make([]byte, len(pre))\n\tcopy(cpy, pre)\n\treturn &preBufferedConn{Conn: conn, buf: cpy, recorded: cpy}\n}\n\nfunc newRejectedPreBufferedConn(conn net.Conn, pre []byte) *preBufferedConn {\n\tc := newPreBufferedConn(conn, pre)\n\tc.rejected = true\n\treturn c\n}\n\nfunc (p *preBufferedConn) IsHTTPMaskRejected() bool { return p.rejected }\n\nfunc (p *preBufferedConn) GetBufferedAndRecorded() []byte {\n\tif len(p.recorded) == 0 {\n\t\treturn nil\n\t}\n\tout := make([]byte, len(p.recorded))\n\tcopy(out, p.recorded)\n\treturn out\n}\n\nfunc (p *preBufferedConn) Read(b []byte) (int, error) {\n\tif len(p.buf) > 0 {\n\t\tn := copy(b, p.buf)\n\t\tp.buf = p.buf[n:]\n\t\treturn n, nil\n\t}\n\treturn p.Conn.Read(b)\n}\n\ntype bodyConn struct {\n\tnet.Conn\n\treader io.Reader\n\twriter io.WriteCloser\n\ttail   io.Writer\n\tflush  func() error\n}\n\nfunc (c *bodyConn) Read(p []byte) (int, error) { return c.reader.Read(p) }\nfunc (c *bodyConn) Write(p []byte) (int, error) {\n\tn, err := c.writer.Write(p)\n\tif c.flush != nil {\n\t\t_ = c.flush()\n\t}\n\treturn n, err\n}\n\nfunc (c *bodyConn) Close() error {\n\tvar firstErr error\n\tif c.writer != nil {\n\t\tif err := c.writer.Close(); err != nil && firstErr == nil {\n\t\t\tfirstErr = err\n\t\t}\n\t\t// NewChunkedWriter does not write the final CRLF. Ensure a clean terminator.\n\t\tif c.tail != nil {\n\t\t\t_, _ = c.tail.Write([]byte(\"\\r\\n\"))\n\t\t} else {\n\t\t\t_, _ = c.Conn.Write([]byte(\"\\r\\n\"))\n\t\t}\n\t\tif c.flush != nil {\n\t\t\t_ = c.flush()\n\t\t}\n\t}\n\tif err := c.Conn.Close(); err != nil && firstErr == nil {\n\t\tfirstErr = err\n\t}\n\treturn firstErr\n}\n\nfunc (s *TunnelServer) handleStream(rawConn net.Conn, req *httpRequestHeader, headerBytes []byte, buffered []byte) (HandleResult, net.Conn, error) {\n\trejectOrReply := func(code int, body string) (HandleResult, net.Conn, error) {\n\t\tif s.passThroughOnReject {\n\t\t\tprefix := make([]byte, 0, len(headerBytes)+len(buffered))\n\t\t\tprefix = append(prefix, headerBytes...)\n\t\t\tprefix = append(prefix, buffered...)\n\t\t\treturn HandlePassThrough, newRejectedPreBufferedConn(rawConn, prefix), nil\n\t\t}\n\t\t_ = writeSimpleHTTPResponse(rawConn, code, body)\n\t\t_ = rawConn.Close()\n\t\treturn HandleDone, nil, nil\n\t}\n\n\tu, err := url.ParseRequestURI(req.target)\n\tif err != nil {\n\t\treturn rejectOrReply(http.StatusBadRequest, \"bad request\")\n\t}\n\n\t// Only accept plausible paths to reduce accidental exposure.\n\tpath, ok := stripPathRoot(s.pathRoot, u.Path)\n\tif !ok || !s.isAllowedBasePath(path) {\n\t\treturn rejectOrReply(http.StatusNotFound, \"not found\")\n\t}\n\tauthVal := req.headers[\"authorization\"]\n\tif authVal == \"\" {\n\t\tauthVal = u.Query().Get(tunnelAuthQueryKey)\n\t}\n\tif !s.auth.verifyValue(authVal, TunnelModeStream, req.method, path, time.Now()) {\n\t\treturn rejectOrReply(http.StatusNotFound, \"not found\")\n\t}\n\n\ttoken := u.Query().Get(\"token\")\n\tcloseFlag := u.Query().Get(\"close\") == \"1\"\n\tfinFlag := u.Query().Get(\"fin\") == \"1\"\n\n\tswitch strings.ToUpper(req.method) {\n\tcase http.MethodGet:\n\t\tif token == \"\" && path == \"/session\" {\n\t\t\tearlyPayload, err := parseEarlyDataQuery(u)\n\t\t\tif err != nil {\n\t\t\t\treturn rejectOrReply(http.StatusBadRequest, \"bad request\")\n\t\t\t}\n\t\t\treturn s.sessionAuthorize(rawConn, earlyPayload)\n\t\t}\n\t\t// Stream split-session: GET /stream?token=... => downlink poll.\n\t\tif token != \"\" && path == \"/stream\" {\n\t\t\tif s.passThroughOnReject && !s.sessionHas(token) {\n\t\t\t\treturn rejectOrReply(http.StatusNotFound, \"not found\")\n\t\t\t}\n\t\t\treturn s.streamPull(rawConn, token)\n\t\t}\n\t\treturn rejectOrReply(http.StatusBadRequest, \"bad request\")\n\n\tcase http.MethodPost:\n\t\t// Stream split-session: POST /api/v1/upload?token=... => uplink push.\n\t\tif token != \"\" && path == \"/api/v1/upload\" {\n\t\t\tif s.passThroughOnReject && !s.sessionHas(token) {\n\t\t\t\treturn rejectOrReply(http.StatusNotFound, \"not found\")\n\t\t\t}\n\t\t\tif closeFlag {\n\t\t\t\ts.sessionClose(token)\n\t\t\t\t_ = writeSimpleHTTPResponse(rawConn, http.StatusOK, \"\")\n\t\t\t\t_ = rawConn.Close()\n\t\t\t\treturn HandleDone, nil, nil\n\t\t\t}\n\t\t\tif finFlag {\n\t\t\t\ts.sessionCloseWrite(token)\n\t\t\t\t_ = writeSimpleHTTPResponse(rawConn, http.StatusOK, \"\")\n\t\t\t\t_ = rawConn.Close()\n\t\t\t\treturn HandleDone, nil, nil\n\t\t\t}\n\t\t\tbodyReader, err := newRequestBodyReader(newPreBufferedConn(rawConn, buffered), req.headers)\n\t\t\tif err != nil {\n\t\t\t\t_ = writeSimpleHTTPResponse(rawConn, http.StatusBadRequest, \"bad request\")\n\t\t\t\t_ = rawConn.Close()\n\t\t\t\treturn HandleDone, nil, nil\n\t\t\t}\n\t\t\treturn s.streamPush(rawConn, token, bodyReader)\n\t\t}\n\n\t\t// Stream-one: single full-duplex POST.\n\t\tif err := writeTunnelResponseHeader(rawConn); err != nil {\n\t\t\t_ = rawConn.Close()\n\t\t\treturn HandleDone, nil, err\n\t\t}\n\n\t\tbodyReader, err := newRequestBodyReader(newPreBufferedConn(rawConn, buffered), req.headers)\n\t\tif err != nil {\n\t\t\t_ = rawConn.Close()\n\t\t\treturn HandleDone, nil, err\n\t\t}\n\n\t\tbw := bufio.NewWriterSize(rawConn, 32*1024)\n\t\tchunked := httputil.NewChunkedWriter(bw)\n\t\tstream := &bodyConn{\n\t\t\tConn:   rawConn,\n\t\t\treader: bodyReader,\n\t\t\twriter: chunked,\n\t\t\ttail:   bw,\n\t\t\tflush:  bw.Flush,\n\t\t}\n\t\treturn HandleStartTunnel, stream, nil\n\n\tdefault:\n\t\treturn rejectOrReply(http.StatusBadRequest, \"bad request\")\n\t}\n}\n\nfunc (s *TunnelServer) isAllowedBasePath(path string) bool {\n\tfor _, p := range paths {\n\t\tif path == p {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc newRequestBodyReader(conn net.Conn, headers map[string]string) (io.Reader, error) {\n\tbr := bufio.NewReaderSize(conn, 32*1024)\n\n\tte := strings.ToLower(headers[\"transfer-encoding\"])\n\tif strings.Contains(te, \"chunked\") {\n\t\treturn httputil.NewChunkedReader(br), nil\n\t}\n\tif clStr := headers[\"content-length\"]; clStr != \"\" {\n\t\tn, err := strconv.ParseInt(strings.TrimSpace(clStr), 10, 64)\n\t\tif err != nil || n < 0 {\n\t\t\treturn nil, fmt.Errorf(\"invalid content-length\")\n\t\t}\n\t\treturn io.LimitReader(br, n), nil\n\t}\n\treturn br, nil\n}\n\nfunc writeTunnelResponseHeader(w io.Writer) error {\n\t_, err := io.WriteString(w,\n\t\t\"HTTP/1.1 200 OK\\r\\n\"+\n\t\t\t\"Content-Type: application/octet-stream\\r\\n\"+\n\t\t\t\"Transfer-Encoding: chunked\\r\\n\"+\n\t\t\t\"Cache-Control: no-store\\r\\n\"+\n\t\t\t\"Pragma: no-cache\\r\\n\"+\n\t\t\t\"Connection: keep-alive\\r\\n\"+\n\t\t\t\"X-Accel-Buffering: no\\r\\n\"+\n\t\t\t\"\\r\\n\")\n\treturn err\n}\n\nfunc writeSimpleHTTPResponse(w io.Writer, code int, body string) error {\n\tif body == \"\" {\n\t\tbody = http.StatusText(code)\n\t}\n\tbody = strings.TrimRight(body, \"\\r\\n\")\n\t_, err := io.WriteString(w,\n\t\tfmt.Sprintf(\"HTTP/1.1 %d %s\\r\\nContent-Type: text/plain\\r\\nContent-Length: %d\\r\\nConnection: close\\r\\n\\r\\n%s\",\n\t\t\tcode, http.StatusText(code), len(body), body))\n\treturn err\n}\n\nfunc writeTokenHTTPResponse(w io.Writer, token string) error {\n\ttoken = strings.TrimRight(token, \"\\r\\n\")\n\treturn writeTokenHTTPResponseWithEarlyData(w, token, nil)\n}\n\nfunc writeTokenHTTPResponseWithEarlyData(w io.Writer, token string, earlyPayload []byte) error {\n\ttoken = strings.TrimRight(token, \"\\r\\n\")\n\tbody := \"token=\" + token\n\tif len(earlyPayload) > 0 {\n\t\tbody += \"\\ned=\" + base64.RawURLEncoding.EncodeToString(earlyPayload)\n\t}\n\t_, err := io.WriteString(w,\n\t\tfmt.Sprintf(\"HTTP/1.1 200 OK\\r\\nContent-Type: application/octet-stream\\r\\nCache-Control: no-store\\r\\nPragma: no-cache\\r\\nContent-Length: %d\\r\\nConnection: close\\r\\n\\r\\n%s\",\n\t\t\tlen(body), body))\n\treturn err\n}\n\nfunc (s *TunnelServer) handlePoll(rawConn net.Conn, req *httpRequestHeader, headerBytes []byte, buffered []byte) (HandleResult, net.Conn, error) {\n\trejectOrReply := func(code int, body string) (HandleResult, net.Conn, error) {\n\t\tif s.passThroughOnReject {\n\t\t\tprefix := make([]byte, 0, len(headerBytes)+len(buffered))\n\t\t\tprefix = append(prefix, headerBytes...)\n\t\t\tprefix = append(prefix, buffered...)\n\t\t\treturn HandlePassThrough, newRejectedPreBufferedConn(rawConn, prefix), nil\n\t\t}\n\t\t_ = writeSimpleHTTPResponse(rawConn, code, body)\n\t\t_ = rawConn.Close()\n\t\treturn HandleDone, nil, nil\n\t}\n\n\tu, err := url.ParseRequestURI(req.target)\n\tif err != nil {\n\t\treturn rejectOrReply(http.StatusBadRequest, \"bad request\")\n\t}\n\n\tpath, ok := stripPathRoot(s.pathRoot, u.Path)\n\tif !ok || !s.isAllowedBasePath(path) {\n\t\treturn rejectOrReply(http.StatusNotFound, \"not found\")\n\t}\n\tauthVal := req.headers[\"authorization\"]\n\tif authVal == \"\" {\n\t\tauthVal = u.Query().Get(tunnelAuthQueryKey)\n\t}\n\tif !s.auth.verifyValue(authVal, TunnelModePoll, req.method, path, time.Now()) {\n\t\treturn rejectOrReply(http.StatusNotFound, \"not found\")\n\t}\n\n\ttoken := u.Query().Get(\"token\")\n\tcloseFlag := u.Query().Get(\"close\") == \"1\"\n\tfinFlag := u.Query().Get(\"fin\") == \"1\"\n\tswitch strings.ToUpper(req.method) {\n\tcase http.MethodGet:\n\t\tif token == \"\" && path == \"/session\" {\n\t\t\tearlyPayload, err := parseEarlyDataQuery(u)\n\t\t\tif err != nil {\n\t\t\t\treturn rejectOrReply(http.StatusBadRequest, \"bad request\")\n\t\t\t}\n\t\t\treturn s.sessionAuthorize(rawConn, earlyPayload)\n\t\t}\n\t\tif token != \"\" && path == \"/stream\" {\n\t\t\tif s.passThroughOnReject && !s.sessionHas(token) {\n\t\t\t\treturn rejectOrReply(http.StatusNotFound, \"not found\")\n\t\t\t}\n\t\t\treturn s.pollPull(rawConn, token)\n\t\t}\n\t\treturn rejectOrReply(http.StatusBadRequest, \"bad request\")\n\tcase http.MethodPost:\n\t\tif token == \"\" || path != \"/api/v1/upload\" {\n\t\t\treturn rejectOrReply(http.StatusBadRequest, \"bad request\")\n\t\t}\n\t\tif s.passThroughOnReject && !s.sessionHas(token) {\n\t\t\treturn rejectOrReply(http.StatusNotFound, \"not found\")\n\t\t}\n\t\tif closeFlag {\n\t\t\ts.sessionClose(token)\n\t\t\t_ = writeSimpleHTTPResponse(rawConn, http.StatusOK, \"\")\n\t\t\t_ = rawConn.Close()\n\t\t\treturn HandleDone, nil, nil\n\t\t}\n\t\tif finFlag {\n\t\t\ts.sessionCloseWrite(token)\n\t\t\t_ = writeSimpleHTTPResponse(rawConn, http.StatusOK, \"\")\n\t\t\t_ = rawConn.Close()\n\t\t\treturn HandleDone, nil, nil\n\t\t}\n\t\tbodyReader, err := newRequestBodyReader(newPreBufferedConn(rawConn, buffered), req.headers)\n\t\tif err != nil {\n\t\t\t_ = writeSimpleHTTPResponse(rawConn, http.StatusBadRequest, \"bad request\")\n\t\t\t_ = rawConn.Close()\n\t\t\treturn HandleDone, nil, nil\n\t\t}\n\t\treturn s.pollPush(rawConn, token, bodyReader)\n\tdefault:\n\t\treturn rejectOrReply(http.StatusBadRequest, \"bad request\")\n\t}\n}\n\nfunc (s *TunnelServer) sessionAuthorize(rawConn net.Conn, earlyPayload []byte) (HandleResult, net.Conn, error) {\n\ttoken, err := newSessionToken()\n\tif err != nil {\n\t\t_ = writeSimpleHTTPResponse(rawConn, http.StatusInternalServerError, \"internal error\")\n\t\t_ = rawConn.Close()\n\t\treturn HandleDone, nil, nil\n\t}\n\n\tc1, c2 := newHalfPipe()\n\toutConn := net.Conn(c1)\n\tvar responsePayload []byte\n\tvar userHash string\n\tif len(earlyPayload) > 0 && s.earlyHandshake != nil && s.earlyHandshake.Prepare != nil {\n\t\tprepared, err := s.earlyHandshake.Prepare(earlyPayload)\n\t\tif err != nil {\n\t\t\t_ = c1.Close()\n\t\t\t_ = c2.Close()\n\t\t\tif s.passThroughOnReject {\n\t\t\t\treturn HandlePassThrough, newRejectedPreBufferedConn(rawConn, nil), nil\n\t\t\t}\n\t\t\t_ = writeSimpleHTTPResponse(rawConn, http.StatusNotFound, \"not found\")\n\t\t\t_ = rawConn.Close()\n\t\t\treturn HandleDone, nil, nil\n\t\t}\n\t\tresponsePayload = prepared.ResponsePayload\n\t\tuserHash = prepared.UserHash\n\t\tif prepared.WrapConn != nil {\n\t\t\twrapped, err := prepared.WrapConn(c1)\n\t\t\tif err != nil {\n\t\t\t\t_ = c1.Close()\n\t\t\t\t_ = c2.Close()\n\t\t\t\t_ = writeSimpleHTTPResponse(rawConn, http.StatusInternalServerError, \"internal error\")\n\t\t\t\t_ = rawConn.Close()\n\t\t\t\treturn HandleDone, nil, nil\n\t\t\t}\n\t\t\tif wrapped != nil {\n\t\t\t\toutConn = wrapEarlyHandshakeConn(wrapped, userHash)\n\t\t\t}\n\t\t}\n\t}\n\n\ts.mu.Lock()\n\ts.sessions[token] = &tunnelSession{conn: c2, lastActive: time.Now()}\n\ts.mu.Unlock()\n\n\tgo s.reapLater(token)\n\n\t_ = writeTokenHTTPResponseWithEarlyData(rawConn, token, responsePayload)\n\t_ = rawConn.Close()\n\treturn HandleStartTunnel, outConn, nil\n}\n\nfunc newSessionToken() (string, error) {\n\tvar b [16]byte\n\tif _, err := crand.Read(b[:]); err != nil {\n\t\treturn \"\", err\n\t}\n\treturn base64.RawURLEncoding.EncodeToString(b[:]), nil\n}\n\nfunc (s *TunnelServer) reapLater(token string) {\n\tttl := s.sessionTTL\n\tif ttl <= 0 {\n\t\treturn\n\t}\n\n\ttimer := time.NewTimer(ttl)\n\tdefer timer.Stop()\n\n\tfor {\n\t\t<-timer.C\n\n\t\ts.mu.Lock()\n\t\tsess, ok := s.sessions[token]\n\t\tif !ok {\n\t\t\ts.mu.Unlock()\n\t\t\treturn\n\t\t}\n\t\tidle := time.Since(sess.lastActive)\n\t\tif idle >= ttl {\n\t\t\tdelete(s.sessions, token)\n\t\t\ts.mu.Unlock()\n\t\t\t_ = sess.conn.Close()\n\t\t\treturn\n\t\t}\n\t\tnext := ttl - idle\n\t\ts.mu.Unlock()\n\n\t\t// Avoid a tight loop under high-frequency activity; we only need best-effort cleanup.\n\t\tif next < 50*time.Millisecond {\n\t\t\tnext = 50 * time.Millisecond\n\t\t}\n\t\ttimer.Reset(next)\n\t}\n}\n\nfunc (s *TunnelServer) sessionHas(token string) bool {\n\ts.mu.Lock()\n\t_, ok := s.sessions[token]\n\ts.mu.Unlock()\n\treturn ok\n}\n\nfunc (s *TunnelServer) sessionGet(token string) (*tunnelSession, bool) {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\tsess, ok := s.sessions[token]\n\tif !ok {\n\t\treturn nil, false\n\t}\n\tsess.lastActive = time.Now()\n\treturn sess, true\n}\n\nfunc (s *TunnelServer) sessionClose(token string) {\n\ts.mu.Lock()\n\tsess, ok := s.sessions[token]\n\tif ok {\n\t\tdelete(s.sessions, token)\n\t}\n\ts.mu.Unlock()\n\tif ok {\n\t\t_ = sess.conn.Close()\n\t}\n}\n\nfunc (s *TunnelServer) sessionCloseWrite(token string) {\n\tsess, ok := s.sessionGet(token)\n\tif !ok || sess == nil || sess.conn == nil {\n\t\treturn\n\t}\n\tif cw, ok := sess.conn.(interface{ CloseWrite() error }); ok {\n\t\t_ = cw.CloseWrite()\n\t\treturn\n\t}\n\t_ = sess.conn.Close()\n}\n\nfunc (s *TunnelServer) pollPush(rawConn net.Conn, token string, body io.Reader) (HandleResult, net.Conn, error) {\n\tsess, ok := s.sessionGet(token)\n\tif !ok {\n\t\t_ = writeSimpleHTTPResponse(rawConn, http.StatusForbidden, \"forbidden\")\n\t\t_ = rawConn.Close()\n\t\treturn HandleDone, nil, nil\n\t}\n\n\tpayload, err := io.ReadAll(io.LimitReader(body, 1<<20)) // 1MiB per request cap\n\tif err != nil {\n\t\t_ = writeSimpleHTTPResponse(rawConn, http.StatusBadRequest, \"bad request\")\n\t\t_ = rawConn.Close()\n\t\treturn HandleDone, nil, nil\n\t}\n\n\tlines := bytes.Split(payload, []byte{'\\n'})\n\tfor _, line := range lines {\n\t\tline = bytes.TrimSpace(line)\n\t\tif len(line) == 0 {\n\t\t\tcontinue\n\t\t}\n\t\tdecoded := make([]byte, base64.StdEncoding.DecodedLen(len(line)))\n\t\tn, decErr := base64.StdEncoding.Decode(decoded, line)\n\t\tif decErr != nil {\n\t\t\t_ = writeSimpleHTTPResponse(rawConn, http.StatusBadRequest, \"bad request\")\n\t\t\t_ = rawConn.Close()\n\t\t\treturn HandleDone, nil, nil\n\t\t}\n\t\tif n == 0 {\n\t\t\tcontinue\n\t\t}\n\t\t_ = sess.conn.SetWriteDeadline(time.Now().Add(30 * time.Second))\n\t\t_, werr := sess.conn.Write(decoded[:n])\n\t\t_ = sess.conn.SetWriteDeadline(time.Time{})\n\t\tif werr != nil {\n\t\t\ts.sessionClose(token)\n\t\t\t_ = writeSimpleHTTPResponse(rawConn, http.StatusGone, \"gone\")\n\t\t\t_ = rawConn.Close()\n\t\t\treturn HandleDone, nil, nil\n\t\t}\n\t}\n\n\t_ = writeSimpleHTTPResponse(rawConn, http.StatusOK, \"\")\n\t_ = rawConn.Close()\n\treturn HandleDone, nil, nil\n}\n\nfunc (s *TunnelServer) streamPush(rawConn net.Conn, token string, body io.Reader) (HandleResult, net.Conn, error) {\n\tsess, ok := s.sessionGet(token)\n\tif !ok {\n\t\t_ = writeSimpleHTTPResponse(rawConn, http.StatusForbidden, \"forbidden\")\n\t\t_ = rawConn.Close()\n\t\treturn HandleDone, nil, nil\n\t}\n\n\tconst maxUploadBytes = 1 << 20\n\tpayload, err := io.ReadAll(io.LimitReader(body, maxUploadBytes+1))\n\tif err != nil {\n\t\t_ = writeSimpleHTTPResponse(rawConn, http.StatusBadRequest, \"bad request\")\n\t\t_ = rawConn.Close()\n\t\treturn HandleDone, nil, nil\n\t}\n\tif len(payload) > maxUploadBytes {\n\t\t_ = writeSimpleHTTPResponse(rawConn, http.StatusRequestEntityTooLarge, \"too large\")\n\t\t_ = rawConn.Close()\n\t\treturn HandleDone, nil, nil\n\t}\n\n\tif len(payload) > 0 {\n\t\t_ = sess.conn.SetWriteDeadline(time.Now().Add(30 * time.Second))\n\t\t_, werr := sess.conn.Write(payload)\n\t\t_ = sess.conn.SetWriteDeadline(time.Time{})\n\t\tif werr != nil {\n\t\t\ts.sessionClose(token)\n\t\t\t_ = writeSimpleHTTPResponse(rawConn, http.StatusGone, \"gone\")\n\t\t\t_ = rawConn.Close()\n\t\t\treturn HandleDone, nil, nil\n\t\t}\n\t}\n\n\t_ = writeSimpleHTTPResponse(rawConn, http.StatusOK, \"\")\n\t_ = rawConn.Close()\n\treturn HandleDone, nil, nil\n}\n\nfunc (s *TunnelServer) streamPull(rawConn net.Conn, token string) (HandleResult, net.Conn, error) {\n\tsess, ok := s.sessionGet(token)\n\tif !ok {\n\t\t_ = writeSimpleHTTPResponse(rawConn, http.StatusForbidden, \"forbidden\")\n\t\t_ = rawConn.Close()\n\t\treturn HandleDone, nil, nil\n\t}\n\n\t// Streaming response (chunked) with raw bytes (no base64 framing).\n\tif err := writeTunnelResponseHeader(rawConn); err != nil {\n\t\t_ = rawConn.Close()\n\t\treturn HandleDone, nil, err\n\t}\n\n\tbw := bufio.NewWriterSize(rawConn, 32*1024)\n\tcw := httputil.NewChunkedWriter(bw)\n\tdefer func() {\n\t\t_ = cw.Close()\n\t\t_, _ = bw.WriteString(\"\\r\\n\")\n\t\t_ = bw.Flush()\n\t\t_ = rawConn.Close()\n\t}()\n\n\tbuf := make([]byte, 32*1024)\n\tfor {\n\t\t_ = sess.conn.SetReadDeadline(time.Now().Add(s.pullReadTimeout))\n\t\tn, err := sess.conn.Read(buf)\n\t\tif n > 0 {\n\t\t\t_, _ = cw.Write(buf[:n])\n\t\t\t_ = bw.Flush()\n\t\t}\n\t\tif err != nil {\n\t\t\tif errors.Is(err, os.ErrDeadlineExceeded) {\n\t\t\t\t// End this long-poll response; client will re-issue.\n\t\t\t\treturn HandleDone, nil, nil\n\t\t\t}\n\t\t\tif errors.Is(err, io.EOF) || errors.Is(err, io.ErrClosedPipe) || errors.Is(err, net.ErrClosed) {\n\t\t\t\treturn HandleDone, nil, nil\n\t\t\t}\n\t\t\ts.sessionClose(token)\n\t\t\treturn HandleDone, nil, nil\n\t\t}\n\t}\n}\n\nfunc (s *TunnelServer) pollPull(rawConn net.Conn, token string) (HandleResult, net.Conn, error) {\n\tsess, ok := s.sessionGet(token)\n\tif !ok {\n\t\t_ = writeSimpleHTTPResponse(rawConn, http.StatusForbidden, \"forbidden\")\n\t\t_ = rawConn.Close()\n\t\treturn HandleDone, nil, nil\n\t}\n\n\t// Streaming response (chunked) with base64 lines.\n\tif err := writeTunnelResponseHeader(rawConn); err != nil {\n\t\t_ = rawConn.Close()\n\t\treturn HandleDone, nil, err\n\t}\n\n\tbw := bufio.NewWriterSize(rawConn, 32*1024)\n\tcw := httputil.NewChunkedWriter(bw)\n\tdefer func() {\n\t\t_ = cw.Close()\n\t\t_, _ = bw.WriteString(\"\\r\\n\")\n\t\t_ = bw.Flush()\n\t\t_ = rawConn.Close()\n\t}()\n\n\tbuf := make([]byte, 32*1024)\n\tfor {\n\t\t_ = sess.conn.SetReadDeadline(time.Now().Add(s.pullReadTimeout))\n\t\tn, err := sess.conn.Read(buf)\n\t\tif n > 0 {\n\t\t\tline := make([]byte, base64.StdEncoding.EncodedLen(n))\n\t\t\tbase64.StdEncoding.Encode(line, buf[:n])\n\t\t\t_, _ = cw.Write(append(line, '\\n'))\n\t\t\t_ = bw.Flush()\n\t\t}\n\t\tif err != nil {\n\t\t\tif errors.Is(err, os.ErrDeadlineExceeded) {\n\t\t\t\t// Keepalive: send an empty line then end this long-poll response.\n\t\t\t\t_, _ = cw.Write([]byte(\"\\n\"))\n\t\t\t\t_ = bw.Flush()\n\t\t\t\treturn HandleDone, nil, nil\n\t\t\t}\n\t\t\tif errors.Is(err, io.EOF) || errors.Is(err, io.ErrClosedPipe) || errors.Is(err, net.ErrClosed) {\n\t\t\t\treturn HandleDone, nil, nil\n\t\t\t}\n\t\t\ts.sessionClose(token)\n\t\t\treturn HandleDone, nil, nil\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/obfs/httpmask/tunnel_ws.go",
    "content": "package httpmask\n\nimport (\n\t\"context\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io\"\n\tmrand \"math/rand\"\n\t\"net\"\n\tstdhttp \"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gobwas/ws\"\n\t\"github.com/metacubex/tls\"\n)\n\nfunc normalizeWSSchemeFromAddress(serverAddress string, tlsEnabled bool) (string, string) {\n\taddr := strings.TrimSpace(serverAddress)\n\tif strings.Contains(addr, \"://\") {\n\t\tif u, err := url.Parse(addr); err == nil && u != nil {\n\t\t\tswitch strings.ToLower(strings.TrimSpace(u.Scheme)) {\n\t\t\tcase \"ws\":\n\t\t\t\treturn \"ws\", u.Host\n\t\t\tcase \"wss\":\n\t\t\t\treturn \"wss\", u.Host\n\t\t\t}\n\t\t}\n\t}\n\tif tlsEnabled {\n\t\treturn \"wss\", addr\n\t}\n\treturn \"ws\", addr\n}\n\nfunc normalizeWSDialTarget(serverAddress string, tlsEnabled bool, hostOverride string) (scheme, urlHost, dialAddr, serverName string, err error) {\n\tscheme, addr := normalizeWSSchemeFromAddress(serverAddress, tlsEnabled)\n\n\thost, port, err := net.SplitHostPort(addr)\n\tif err != nil {\n\t\t// Allow ws(s)://host without port.\n\t\tif strings.Contains(addr, \":\") {\n\t\t\treturn \"\", \"\", \"\", \"\", fmt.Errorf(\"invalid server address %q: %w\", serverAddress, err)\n\t\t}\n\t\tswitch scheme {\n\t\tcase \"wss\":\n\t\t\tport = \"443\"\n\t\tdefault:\n\t\t\tport = \"80\"\n\t\t}\n\t\thost = addr\n\t}\n\n\tif hostOverride != \"\" {\n\t\t// Allow \"example.com\" or \"example.com:443\"\n\t\tif h, p, splitErr := net.SplitHostPort(hostOverride); splitErr == nil {\n\t\t\tif h != \"\" {\n\t\t\t\thostOverride = h\n\t\t\t}\n\t\t\tif p != \"\" {\n\t\t\t\tport = p\n\t\t\t}\n\t\t}\n\t\tserverName = hostOverride\n\t\turlHost = net.JoinHostPort(hostOverride, port)\n\t} else {\n\t\tserverName = host\n\t\turlHost = net.JoinHostPort(host, port)\n\t}\n\n\tdialAddr = net.JoinHostPort(host, port)\n\treturn scheme, urlHost, dialAddr, trimPortForHost(serverName), nil\n}\n\nfunc applyWSHeaders(h stdhttp.Header, host string) {\n\tif h == nil {\n\t\treturn\n\t}\n\tr := rngPool.Get().(*mrand.Rand)\n\tua := userAgents[r.Intn(len(userAgents))]\n\taccept := accepts[r.Intn(len(accepts))]\n\tlang := acceptLanguages[r.Intn(len(acceptLanguages))]\n\tenc := acceptEncodings[r.Intn(len(acceptEncodings))]\n\trngPool.Put(r)\n\n\th.Set(\"User-Agent\", ua)\n\th.Set(\"Accept\", accept)\n\th.Set(\"Accept-Language\", lang)\n\th.Set(\"Accept-Encoding\", enc)\n\th.Set(\"Cache-Control\", \"no-cache\")\n\th.Set(\"Pragma\", \"no-cache\")\n\th.Set(\"X-Sudoku-Tunnel\", string(TunnelModeWS))\n\th.Set(\"X-Sudoku-Version\", \"1\")\n}\n\nfunc dialWS(ctx context.Context, serverAddress string, opts TunnelDialOptions) (net.Conn, error) {\n\tif opts.DialContext == nil {\n\t\tpanic(\"httpmask: DialContext is nil\")\n\t}\n\n\tscheme, urlHost, dialAddr, serverName, err := normalizeWSDialTarget(serverAddress, opts.TLSEnabled, opts.HostOverride)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\thttpScheme := \"http\"\n\tif scheme == \"wss\" {\n\t\thttpScheme = \"https\"\n\t}\n\theaderHost := canonicalHeaderHost(urlHost, httpScheme)\n\tauth := newTunnelAuth(opts.AuthKey, 0)\n\n\tu := &url.URL{\n\t\tScheme: scheme,\n\t\tHost:   urlHost,\n\t\tPath:   joinPathRoot(opts.PathRoot, \"/ws\"),\n\t}\n\tif opts.EarlyHandshake != nil && len(opts.EarlyHandshake.RequestPayload) > 0 {\n\t\trawURL, err := setEarlyDataQuery(u.String(), opts.EarlyHandshake.RequestPayload)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tu, err = url.Parse(rawURL)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\theader := make(stdhttp.Header)\n\tapplyWSHeaders(header, headerHost)\n\n\tif auth != nil {\n\t\ttoken := auth.token(TunnelModeWS, stdhttp.MethodGet, \"/ws\", time.Now())\n\t\tif token != \"\" {\n\t\t\theader.Set(\"Authorization\", \"Bearer \"+token)\n\t\t\tq := u.Query()\n\t\t\tq.Set(tunnelAuthQueryKey, token)\n\t\t\tu.RawQuery = q.Encode()\n\t\t}\n\t}\n\n\td := ws.Dialer{\n\t\tHost:   headerHost,\n\t\tHeader: ws.HandshakeHeaderHTTP(header),\n\t\tOnHeader: func(key, value []byte) error {\n\t\t\tif !strings.EqualFold(string(key), tunnelEarlyDataHeader) || opts.EarlyHandshake == nil || opts.EarlyHandshake.HandleResponse == nil {\n\t\t\t\treturn nil\n\t\t\t}\n\t\t\tdecoded, err := base64.RawURLEncoding.DecodeString(strings.TrimSpace(string(value)))\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\treturn opts.EarlyHandshake.HandleResponse(decoded)\n\t\t},\n\t\tNetDial: func(dialCtx context.Context, network, addr string) (net.Conn, error) {\n\t\t\tif addr == urlHost {\n\t\t\t\taddr = dialAddr\n\t\t\t}\n\t\t\treturn opts.DialContext(dialCtx, network, addr)\n\t\t},\n\t}\n\tif scheme == \"wss\" {\n\t\ttlsConfig := &tls.Config{\n\t\t\tServerName: serverName,\n\t\t\tMinVersion: tls.VersionTLS12,\n\t\t}\n\t\td.TLSClient = func(conn net.Conn, hostname string) net.Conn {\n\t\t\treturn tls.Client(conn, tlsConfig)\n\t\t}\n\t}\n\n\tconn, br, _, err := d.Dial(ctx, u.String())\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif br != nil && br.Buffered() > 0 {\n\t\tpre := make([]byte, br.Buffered())\n\t\t_, _ = io.ReadFull(br, pre)\n\t\tconn = newPreBufferedConn(conn, pre)\n\t}\n\n\twsConn := newWSStreamConn(conn, ws.StateClientSide)\n\tupgraded, err := applyEarlyHandshakeOrUpgrade(wsConn, opts)\n\tif err != nil {\n\t\t_ = wsConn.Close()\n\t\treturn nil, err\n\t}\n\treturn upgraded, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/obfs/httpmask/tunnel_ws_server.go",
    "content": "package httpmask\n\nimport (\n\t\"encoding/base64\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/gobwas/ws\"\n)\n\nfunc looksLikeWebSocketUpgrade(headers map[string]string) bool {\n\tif headers == nil {\n\t\treturn false\n\t}\n\tif !strings.EqualFold(strings.TrimSpace(headers[\"upgrade\"]), \"websocket\") {\n\t\treturn false\n\t}\n\tconn := headers[\"connection\"]\n\tfor _, part := range strings.Split(conn, \",\") {\n\t\tif strings.EqualFold(strings.TrimSpace(part), \"upgrade\") {\n\t\t\treturn true\n\t\t}\n\t}\n\treturn false\n}\n\nfunc (s *TunnelServer) handleWS(rawConn net.Conn, req *httpRequestHeader, headerBytes []byte, buffered []byte) (HandleResult, net.Conn, error) {\n\trejectOrReply := func(code int, body string) (HandleResult, net.Conn, error) {\n\t\tif s.passThroughOnReject {\n\t\t\tprefix := make([]byte, 0, len(headerBytes)+len(buffered))\n\t\t\tprefix = append(prefix, headerBytes...)\n\t\t\tprefix = append(prefix, buffered...)\n\t\t\treturn HandlePassThrough, newRejectedPreBufferedConn(rawConn, prefix), nil\n\t\t}\n\t\t_ = writeSimpleHTTPResponse(rawConn, code, body)\n\t\t_ = rawConn.Close()\n\t\treturn HandleDone, nil, nil\n\t}\n\n\tu, err := url.ParseRequestURI(req.target)\n\tif err != nil {\n\t\treturn rejectOrReply(http.StatusBadRequest, \"bad request\")\n\t}\n\n\tpath, ok := stripPathRoot(s.pathRoot, u.Path)\n\tif !ok || path != \"/ws\" {\n\t\treturn rejectOrReply(http.StatusNotFound, \"not found\")\n\t}\n\tif strings.ToUpper(strings.TrimSpace(req.method)) != http.MethodGet {\n\t\treturn rejectOrReply(http.StatusBadRequest, \"bad request\")\n\t}\n\tif !looksLikeWebSocketUpgrade(req.headers) {\n\t\treturn rejectOrReply(http.StatusBadRequest, \"bad request\")\n\t}\n\n\tauthVal := req.headers[\"authorization\"]\n\tif authVal == \"\" {\n\t\tauthVal = u.Query().Get(tunnelAuthQueryKey)\n\t}\n\tif !s.auth.verifyValue(authVal, TunnelModeWS, req.method, path, time.Now()) {\n\t\treturn rejectOrReply(http.StatusNotFound, \"not found\")\n\t}\n\n\tearlyPayload, err := parseEarlyDataQuery(u)\n\tif err != nil {\n\t\treturn rejectOrReply(http.StatusBadRequest, \"bad request\")\n\t}\n\tvar prepared *PreparedServerEarlyHandshake\n\tif len(earlyPayload) > 0 && s.earlyHandshake != nil && s.earlyHandshake.Prepare != nil {\n\t\tprepared, err = s.earlyHandshake.Prepare(earlyPayload)\n\t\tif err != nil {\n\t\t\treturn rejectOrReply(http.StatusNotFound, \"not found\")\n\t\t}\n\t}\n\n\tprefix := make([]byte, 0, len(headerBytes)+len(buffered))\n\tprefix = append(prefix, headerBytes...)\n\tprefix = append(prefix, buffered...)\n\twsConnRaw := newPreBufferedConn(rawConn, prefix)\n\n\tupgrader := ws.Upgrader{}\n\tif prepared != nil && len(prepared.ResponsePayload) > 0 {\n\t\tupgrader.OnBeforeUpgrade = func() (ws.HandshakeHeader, error) {\n\t\t\th := http.Header{}\n\t\t\th.Set(tunnelEarlyDataHeader, base64.RawURLEncoding.EncodeToString(prepared.ResponsePayload))\n\t\t\treturn ws.HandshakeHeaderHTTP(h), nil\n\t\t}\n\t}\n\tif _, err := upgrader.Upgrade(wsConnRaw); err != nil {\n\t\t_ = rawConn.Close()\n\t\treturn HandleDone, nil, nil\n\t}\n\n\toutConn := net.Conn(newWSStreamConn(wsConnRaw, ws.StateServerSide))\n\tif prepared != nil && prepared.WrapConn != nil {\n\t\twrapped, err := prepared.WrapConn(outConn)\n\t\tif err != nil {\n\t\t\t_ = outConn.Close()\n\t\t\treturn HandleDone, nil, nil\n\t\t}\n\t\tif wrapped != nil {\n\t\t\toutConn = wrapEarlyHandshakeConn(wrapped, prepared.UserHash)\n\t\t}\n\t}\n\treturn HandleStartTunnel, outConn, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/obfs/httpmask/ws_stream_conn.go",
    "content": "package httpmask\n\nimport (\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\n\t\"github.com/gobwas/ws\"\n\t\"github.com/gobwas/ws/wsutil\"\n)\n\ntype wsStreamConn struct {\n\tnet.Conn\n\tstate          ws.State\n\treader         *wsutil.Reader\n\tcontrolHandler wsutil.FrameHandlerFunc\n}\n\nfunc newWSStreamConn(conn net.Conn, state ws.State) net.Conn {\n\tcontrolHandler := wsutil.ControlFrameHandler(conn, state)\n\treturn &wsStreamConn{\n\t\tConn:  conn,\n\t\tstate: state,\n\t\treader: &wsutil.Reader{\n\t\t\tSource: conn,\n\t\t\tState:  state,\n\t\t},\n\t\tcontrolHandler: controlHandler,\n\t}\n}\n\nfunc (c *wsStreamConn) Read(b []byte) (n int, err error) {\n\tdefer func() {\n\t\tif v := recover(); v != nil {\n\t\t\terr = fmt.Errorf(\"websocket error: %v\", v)\n\t\t}\n\t}()\n\n\tfor {\n\t\tn, err = c.reader.Read(b)\n\t\tif errors.Is(err, io.EOF) {\n\t\t\terr = nil\n\t\t}\n\t\tif !errors.Is(err, wsutil.ErrNoFrameAdvance) {\n\t\t\treturn n, err\n\t\t}\n\n\t\thdr, err2 := c.reader.NextFrame()\n\t\tif err2 != nil {\n\t\t\treturn 0, err2\n\t\t}\n\t\tif hdr.OpCode.IsControl() {\n\t\t\tif err := c.controlHandler(hdr, c.reader); err != nil {\n\t\t\t\treturn 0, err\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tif hdr.OpCode&(ws.OpBinary|ws.OpText) == 0 {\n\t\t\tif err := c.reader.Discard(); err != nil {\n\t\t\t\treturn 0, err\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t}\n}\n\nfunc (c *wsStreamConn) Write(b []byte) (int, error) {\n\tif err := wsutil.WriteMessage(c.Conn, c.state, ws.OpBinary, b); err != nil {\n\t\treturn 0, err\n\t}\n\treturn len(b), nil\n}\n\nfunc (c *wsStreamConn) Close() error {\n\t_ = wsutil.WriteMessage(c.Conn, c.state, ws.OpClose, ws.NewCloseFrameBody(ws.StatusNormalClosure, \"\"))\n\treturn c.Conn.Close()\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/obfs/sudoku/ascii_mode.go",
    "content": "package sudoku\n\nimport (\n\t\"fmt\"\n\t\"strings\"\n)\n\nconst (\n\tasciiModeTokenASCII   = \"ascii\"\n\tasciiModeTokenEntropy = \"entropy\"\n)\n\n// ASCIIMode describes the preferred wire layout for each traffic direction.\n// Uplink is client->server, Downlink is server->client.\ntype ASCIIMode struct {\n\tUplink   string\n\tDownlink string\n}\n\n// ParseASCIIMode accepts legacy symmetric values (\"ascii\"/\"entropy\"/\"prefer_*\")\n// and directional values like \"up_ascii_down_entropy\".\nfunc ParseASCIIMode(mode string) (ASCIIMode, error) {\n\traw := strings.ToLower(strings.TrimSpace(mode))\n\tswitch raw {\n\tcase \"\", \"entropy\", \"prefer_entropy\":\n\t\treturn ASCIIMode{Uplink: asciiModeTokenEntropy, Downlink: asciiModeTokenEntropy}, nil\n\tcase \"ascii\", \"prefer_ascii\":\n\t\treturn ASCIIMode{Uplink: asciiModeTokenASCII, Downlink: asciiModeTokenASCII}, nil\n\t}\n\n\tif !strings.HasPrefix(raw, \"up_\") {\n\t\treturn ASCIIMode{}, fmt.Errorf(\"invalid ascii mode: %s\", mode)\n\t}\n\tparts := strings.SplitN(strings.TrimPrefix(raw, \"up_\"), \"_down_\", 2)\n\tif len(parts) != 2 {\n\t\treturn ASCIIMode{}, fmt.Errorf(\"invalid ascii mode: %s\", mode)\n\t}\n\n\tup, ok := normalizeASCIIModeToken(parts[0])\n\tif !ok {\n\t\treturn ASCIIMode{}, fmt.Errorf(\"invalid ascii mode: %s\", mode)\n\t}\n\tdown, ok := normalizeASCIIModeToken(parts[1])\n\tif !ok {\n\t\treturn ASCIIMode{}, fmt.Errorf(\"invalid ascii mode: %s\", mode)\n\t}\n\treturn ASCIIMode{Uplink: up, Downlink: down}, nil\n}\n\n// NormalizeASCIIMode returns the canonical config string for a supported mode.\nfunc NormalizeASCIIMode(mode string) (string, error) {\n\tparsed, err := ParseASCIIMode(mode)\n\tif err != nil {\n\t\treturn \"\", err\n\t}\n\treturn parsed.Canonical(), nil\n}\n\nfunc (m ASCIIMode) Canonical() string {\n\tif m.Uplink == asciiModeTokenASCII && m.Downlink == asciiModeTokenASCII {\n\t\treturn \"prefer_ascii\"\n\t}\n\tif m.Uplink == asciiModeTokenEntropy && m.Downlink == asciiModeTokenEntropy {\n\t\treturn \"prefer_entropy\"\n\t}\n\treturn \"up_\" + m.Uplink + \"_down_\" + m.Downlink\n}\n\nfunc (m ASCIIMode) uplinkPreference() string {\n\treturn singleDirectionPreference(m.Uplink)\n}\n\nfunc (m ASCIIMode) downlinkPreference() string {\n\treturn singleDirectionPreference(m.Downlink)\n}\n\nfunc normalizeASCIIModeToken(token string) (string, bool) {\n\tswitch strings.ToLower(strings.TrimSpace(token)) {\n\tcase \"ascii\", \"prefer_ascii\":\n\t\treturn asciiModeTokenASCII, true\n\tcase \"entropy\", \"prefer_entropy\", \"\":\n\t\treturn asciiModeTokenEntropy, true\n\tdefault:\n\t\treturn \"\", false\n\t}\n}\n\nfunc singleDirectionPreference(token string) string {\n\tif token == asciiModeTokenASCII {\n\t\treturn \"prefer_ascii\"\n\t}\n\treturn \"prefer_entropy\"\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/obfs/sudoku/ascii_mode_test.go",
    "content": "package sudoku\n\nimport \"testing\"\n\nfunc TestNormalizeASCIIMode(t *testing.T) {\n\ttests := []struct {\n\t\tin   string\n\t\twant string\n\t}{\n\t\t{\"\", \"prefer_entropy\"},\n\t\t{\"entropy\", \"prefer_entropy\"},\n\t\t{\"prefer_ascii\", \"prefer_ascii\"},\n\t\t{\"up_ascii_down_entropy\", \"up_ascii_down_entropy\"},\n\t\t{\"up_entropy_down_ascii\", \"up_entropy_down_ascii\"},\n\t\t{\"up_prefer_ascii_down_prefer_entropy\", \"up_ascii_down_entropy\"},\n\t}\n\n\tfor _, tt := range tests {\n\t\tgot, err := NormalizeASCIIMode(tt.in)\n\t\tif err != nil {\n\t\t\tt.Fatalf(\"NormalizeASCIIMode(%q): %v\", tt.in, err)\n\t\t}\n\t\tif got != tt.want {\n\t\t\tt.Fatalf(\"NormalizeASCIIMode(%q) = %q, want %q\", tt.in, got, tt.want)\n\t\t}\n\t}\n\n\tif _, err := NormalizeASCIIMode(\"up_ascii_down_binary\"); err == nil {\n\t\tt.Fatalf(\"expected invalid directional mode to fail\")\n\t}\n}\n\nfunc TestNewTableWithCustomDirectionalOpposite(t *testing.T) {\n\ttable, err := NewTableWithCustom(\"seed\", \"up_ascii_down_entropy\", \"xpxvvpvv\")\n\tif err != nil {\n\t\tt.Fatalf(\"NewTableWithCustom: %v\", err)\n\t}\n\tif !table.IsASCII {\n\t\tt.Fatalf(\"uplink table should be ascii\")\n\t}\n\topposite := table.OppositeDirection()\n\tif opposite == nil || opposite == table {\n\t\tt.Fatalf(\"expected distinct opposite table\")\n\t}\n\tif opposite.IsASCII {\n\t\tt.Fatalf(\"downlink table should be entropy/custom\")\n\t}\n\n\tsymmetric, err := NewTableWithCustom(\"seed\", \"prefer_ascii\", \"xpxvvpvv\")\n\tif err != nil {\n\t\tt.Fatalf(\"NewTableWithCustom symmetric: %v\", err)\n\t}\n\tif symmetric.OppositeDirection() != symmetric {\n\t\tt.Fatalf(\"symmetric table should point to itself\")\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/obfs/sudoku/conn.go",
    "content": "package sudoku\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"net\"\n\t\"sync\"\n\t\"sync/atomic\"\n)\n\nconst IOBufferSize = 32 * 1024\n\nvar perm4 = [24][4]byte{\n\t{0, 1, 2, 3},\n\t{0, 1, 3, 2},\n\t{0, 2, 1, 3},\n\t{0, 2, 3, 1},\n\t{0, 3, 1, 2},\n\t{0, 3, 2, 1},\n\t{1, 0, 2, 3},\n\t{1, 0, 3, 2},\n\t{1, 2, 0, 3},\n\t{1, 2, 3, 0},\n\t{1, 3, 0, 2},\n\t{1, 3, 2, 0},\n\t{2, 0, 1, 3},\n\t{2, 0, 3, 1},\n\t{2, 1, 0, 3},\n\t{2, 1, 3, 0},\n\t{2, 3, 0, 1},\n\t{2, 3, 1, 0},\n\t{3, 0, 1, 2},\n\t{3, 0, 2, 1},\n\t{3, 1, 0, 2},\n\t{3, 1, 2, 0},\n\t{3, 2, 0, 1},\n\t{3, 2, 1, 0},\n}\n\ntype Conn struct {\n\tnet.Conn\n\ttable      *Table\n\treader     *bufio.Reader\n\trecorder   *bytes.Buffer\n\trecording  atomic.Bool\n\trecordLock sync.Mutex\n\n\trawBuf      []byte\n\tpendingData pendingBuffer\n\thintBuf     [4]byte\n\thintCount   int\n\twriteMu     sync.Mutex\n\twriteBuf    []byte\n\n\trng              randomSource\n\tpaddingThreshold uint64\n}\n\nfunc (sc *Conn) CloseWrite() error {\n\tif sc == nil || sc.Conn == nil {\n\t\treturn nil\n\t}\n\tif cw, ok := sc.Conn.(interface{ CloseWrite() error }); ok {\n\t\treturn cw.CloseWrite()\n\t}\n\treturn nil\n}\n\nfunc (sc *Conn) CloseRead() error {\n\tif sc == nil || sc.Conn == nil {\n\t\treturn nil\n\t}\n\tif cr, ok := sc.Conn.(interface{ CloseRead() error }); ok {\n\t\treturn cr.CloseRead()\n\t}\n\treturn nil\n}\n\nfunc NewConn(c net.Conn, table *Table, pMin, pMax int, record bool) *Conn {\n\tlocalRng := newSeededRand()\n\n\tsc := &Conn{\n\t\tConn:             c,\n\t\ttable:            table,\n\t\treader:           bufio.NewReaderSize(c, IOBufferSize),\n\t\trawBuf:           make([]byte, IOBufferSize),\n\t\tpendingData:      newPendingBuffer(4096),\n\t\twriteBuf:         make([]byte, 0, 4096),\n\t\trng:              localRng,\n\t\tpaddingThreshold: pickPaddingThreshold(localRng, pMin, pMax),\n\t}\n\tif record {\n\t\tsc.recorder = new(bytes.Buffer)\n\t\tsc.recording.Store(true)\n\t}\n\treturn sc\n}\n\nfunc (sc *Conn) StopRecording() {\n\tsc.recordLock.Lock()\n\tsc.recording.Store(false)\n\tsc.recorder = nil\n\tsc.recordLock.Unlock()\n}\n\nfunc (sc *Conn) GetBufferedAndRecorded() []byte {\n\tif sc == nil {\n\t\treturn nil\n\t}\n\n\tsc.recordLock.Lock()\n\tdefer sc.recordLock.Unlock()\n\n\tvar recorded []byte\n\tif sc.recorder != nil {\n\t\trecorded = sc.recorder.Bytes()\n\t}\n\n\tbuffered := sc.reader.Buffered()\n\tif buffered > 0 {\n\t\tpeeked, _ := sc.reader.Peek(buffered)\n\t\tfull := make([]byte, len(recorded)+len(peeked))\n\t\tcopy(full, recorded)\n\t\tcopy(full[len(recorded):], peeked)\n\t\treturn full\n\t}\n\treturn recorded\n}\n\nfunc (sc *Conn) Write(p []byte) (n int, err error) {\n\tif len(p) == 0 {\n\t\treturn 0, nil\n\t}\n\n\tsc.writeMu.Lock()\n\tdefer sc.writeMu.Unlock()\n\n\tsc.writeBuf = encodeSudokuPayload(sc.writeBuf[:0], sc.table, sc.rng, sc.paddingThreshold, p)\n\treturn len(p), writeFull(sc.Conn, sc.writeBuf)\n}\n\nfunc (sc *Conn) Read(p []byte) (n int, err error) {\n\tif n, ok := drainPending(p, &sc.pendingData); ok {\n\t\treturn n, nil\n\t}\n\n\tfor {\n\t\tif sc.pendingData.available() > 0 {\n\t\t\tbreak\n\t\t}\n\n\t\tnr, rErr := sc.reader.Read(sc.rawBuf)\n\t\tif nr > 0 {\n\t\t\tchunk := sc.rawBuf[:nr]\n\t\t\tif sc.recording.Load() {\n\t\t\t\tsc.recordLock.Lock()\n\t\t\t\tif sc.recording.Load() && sc.recorder != nil {\n\t\t\t\t\tsc.recorder.Write(chunk)\n\t\t\t\t}\n\t\t\t\tsc.recordLock.Unlock()\n\t\t\t}\n\n\t\t\tlayout := sc.table.layout\n\t\t\tfor _, b := range chunk {\n\t\t\t\tif !layout.hintTable[b] {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tsc.hintBuf[sc.hintCount] = b\n\t\t\t\tsc.hintCount++\n\t\t\t\tif sc.hintCount == len(sc.hintBuf) {\n\t\t\t\t\tkey := packHintsToKey(sc.hintBuf)\n\t\t\t\t\tval, ok := sc.table.DecodeMap[key]\n\t\t\t\t\tif !ok {\n\t\t\t\t\t\treturn 0, ErrInvalidSudokuMapMiss\n\t\t\t\t\t}\n\t\t\t\t\tsc.pendingData.appendByte(val)\n\t\t\t\t\tsc.hintCount = 0\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\tif rErr != nil {\n\t\t\treturn 0, rErr\n\t\t}\n\t\tif sc.pendingData.available() > 0 {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tn, _ = drainPending(p, &sc.pendingData)\n\treturn n, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/obfs/sudoku/encode.go",
    "content": "package sudoku\n\nfunc encodeSudokuPayload(dst []byte, table *Table, rng randomSource, paddingThreshold uint64, p []byte) []byte {\n\tif len(p) == 0 {\n\t\treturn dst[:0]\n\t}\n\n\toutCapacity := len(p)*6 + 1\n\tif cap(dst) < outCapacity {\n\t\tdst = make([]byte, 0, outCapacity)\n\t}\n\tout := dst[:0]\n\tpads := table.PaddingPool\n\tpadLen := len(pads)\n\n\tfor _, b := range p {\n\t\tif shouldPad(rng, paddingThreshold) {\n\t\t\tout = append(out, pads[rng.Intn(padLen)])\n\t\t}\n\n\t\tpuzzles := table.EncodeTable[b]\n\t\tpuzzle := puzzles[rng.Intn(len(puzzles))]\n\t\tperm := perm4[rng.Intn(len(perm4))]\n\t\tfor _, idx := range perm {\n\t\t\tif shouldPad(rng, paddingThreshold) {\n\t\t\t\tout = append(out, pads[rng.Intn(padLen)])\n\t\t\t}\n\t\t\tout = append(out, puzzle[idx])\n\t\t}\n\t}\n\n\tif shouldPad(rng, paddingThreshold) {\n\t\tout = append(out, pads[rng.Intn(padLen)])\n\t}\n\treturn out\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/obfs/sudoku/grid.go",
    "content": "package sudoku\n\n// Grid represents a 4x4 sudoku grid\ntype Grid [16]uint8\n\n// GenerateAllGrids generates all valid 4x4 Sudoku grids\nfunc GenerateAllGrids() []Grid {\n\tvar grids []Grid\n\tvar g Grid\n\tvar backtrack func(int)\n\n\tbacktrack = func(idx int) {\n\t\tif idx == 16 {\n\t\t\tgrids = append(grids, g)\n\t\t\treturn\n\t\t}\n\t\trow, col := idx/4, idx%4\n\t\tbr, bc := (row/2)*2, (col/2)*2\n\t\tfor num := uint8(1); num <= 4; num++ {\n\t\t\tvalid := true\n\t\t\tfor i := 0; i < 4; i++ {\n\t\t\t\tif g[row*4+i] == num || g[i*4+col] == num {\n\t\t\t\t\tvalid = false\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif valid {\n\t\t\t\tfor r := 0; r < 2; r++ {\n\t\t\t\t\tfor c := 0; c < 2; c++ {\n\t\t\t\t\t\tif g[(br+r)*4+(bc+c)] == num {\n\t\t\t\t\t\t\tvalid = false\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\tif valid {\n\t\t\t\tg[idx] = num\n\t\t\t\tbacktrack(idx + 1)\n\t\t\t\tg[idx] = 0\n\t\t\t}\n\t\t}\n\t}\n\tbacktrack(0)\n\treturn grids\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/obfs/sudoku/layout.go",
    "content": "package sudoku\n\nimport (\n\t\"fmt\"\n\t\"math/bits\"\n\t\"sort\"\n\t\"strings\"\n)\n\ntype byteLayout struct {\n\tname        string\n\thintMask    byte\n\thintValue   byte\n\tpadMarker   byte\n\tpaddingPool []byte\n\n\thintTable   [256]bool\n\tencodeHint  [4][16]byte\n\tencodeGroup [64]byte\n\tdecodeGroup [256]byte\n\tgroupValid  [256]bool\n}\n\nfunc (l *byteLayout) isHint(b byte) bool {\n\treturn l != nil && l.hintTable[b]\n}\n\nfunc (l *byteLayout) hintByte(val, pos byte) byte {\n\treturn l.encodeHint[val&0x03][pos&0x0F]\n}\n\nfunc (l *byteLayout) groupByte(group byte) byte {\n\treturn l.encodeGroup[group&0x3F]\n}\n\nfunc (l *byteLayout) decodePackedGroup(b byte) (byte, bool) {\n\tif l == nil {\n\t\treturn 0, false\n\t}\n\treturn l.decodeGroup[b], l.groupValid[b]\n}\n\n// resolveLayout picks the byte layout for a single traffic direction.\n// ASCII always wins if requested. Custom patterns are ignored when ASCII is preferred.\nfunc resolveLayout(mode string, customPattern string) (*byteLayout, error) {\n\tswitch strings.ToLower(mode) {\n\tcase \"ascii\", \"prefer_ascii\":\n\t\treturn newASCIILayout(), nil\n\tcase \"entropy\", \"prefer_entropy\", \"\":\n\t\t// fallback to entropy unless a custom pattern is provided\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"invalid ascii mode: %s\", mode)\n\t}\n\n\tif strings.TrimSpace(customPattern) != \"\" {\n\t\treturn newCustomLayout(customPattern)\n\t}\n\treturn newEntropyLayout(), nil\n}\n\nfunc newASCIILayout() *byteLayout {\n\tpadding := make([]byte, 0, 32)\n\tfor i := 0; i < 32; i++ {\n\t\tpadding = append(padding, byte(0x20+i))\n\t}\n\n\tlayout := &byteLayout{\n\t\tname:        \"ascii\",\n\t\thintMask:    0x40,\n\t\thintValue:   0x40,\n\t\tpadMarker:   0x3F,\n\t\tpaddingPool: padding,\n\t}\n\n\tfor val := 0; val < 4; val++ {\n\t\tfor pos := 0; pos < 16; pos++ {\n\t\t\tb := byte(0x40 | (byte(val) << 4) | byte(pos))\n\t\t\tif b == 0x7F {\n\t\t\t\tb = '\\n'\n\t\t\t}\n\t\t\tlayout.encodeHint[val][pos] = b\n\t\t}\n\t}\n\tfor group := 0; group < 64; group++ {\n\t\tb := byte(0x40 | byte(group))\n\t\tif b == 0x7F {\n\t\t\tb = '\\n'\n\t\t}\n\t\tlayout.encodeGroup[group] = b\n\t}\n\tfor b := 0; b < 256; b++ {\n\t\twire := byte(b)\n\t\tif (wire & 0x40) == 0x40 {\n\t\t\tlayout.hintTable[wire] = true\n\t\t\tlayout.decodeGroup[wire] = wire & 0x3F\n\t\t\tlayout.groupValid[wire] = true\n\t\t}\n\t}\n\tlayout.hintTable['\\n'] = true\n\tlayout.decodeGroup['\\n'] = 0x3F\n\tlayout.groupValid['\\n'] = true\n\n\treturn layout\n}\n\nfunc newEntropyLayout() *byteLayout {\n\tpadding := make([]byte, 0, 16)\n\tfor i := 0; i < 8; i++ {\n\t\tpadding = append(padding, byte(0x80+i))\n\t\tpadding = append(padding, byte(0x10+i))\n\t}\n\n\tlayout := &byteLayout{\n\t\tname:        \"entropy\",\n\t\thintMask:    0x90,\n\t\thintValue:   0x00,\n\t\tpadMarker:   0x80,\n\t\tpaddingPool: padding,\n\t}\n\n\tfor val := 0; val < 4; val++ {\n\t\tfor pos := 0; pos < 16; pos++ {\n\t\t\tlayout.encodeHint[val][pos] = (byte(val) << 5) | byte(pos)\n\t\t}\n\t}\n\tfor group := 0; group < 64; group++ {\n\t\tv := byte(group)\n\t\tlayout.encodeGroup[group] = ((v & 0x30) << 1) | (v & 0x0F)\n\t}\n\tfor b := 0; b < 256; b++ {\n\t\twire := byte(b)\n\t\tif (wire & 0x90) != 0 {\n\t\t\tcontinue\n\t\t}\n\t\tlayout.hintTable[wire] = true\n\t\tlayout.decodeGroup[wire] = ((wire >> 1) & 0x30) | (wire & 0x0F)\n\t\tlayout.groupValid[wire] = true\n\t}\n\n\treturn layout\n}\n\nfunc newCustomLayout(pattern string) (*byteLayout, error) {\n\tcleaned := strings.ToLower(strings.ReplaceAll(strings.TrimSpace(pattern), \" \", \"\"))\n\tif len(cleaned) != 8 {\n\t\treturn nil, fmt.Errorf(\"custom table must have 8 symbols, got %d\", len(cleaned))\n\t}\n\n\tvar xBits, pBits, vBits []uint8\n\tfor i, c := range cleaned {\n\t\tbit := uint8(7 - i)\n\t\tswitch c {\n\t\tcase 'x':\n\t\t\txBits = append(xBits, bit)\n\t\tcase 'p':\n\t\t\tpBits = append(pBits, bit)\n\t\tcase 'v':\n\t\t\tvBits = append(vBits, bit)\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"invalid char %q in custom table\", c)\n\t\t}\n\t}\n\n\tif len(xBits) != 2 || len(pBits) != 2 || len(vBits) != 4 {\n\t\treturn nil, fmt.Errorf(\"custom table must contain exactly 2 x, 2 p, 4 v\")\n\t}\n\n\txMask := byte(0)\n\tfor _, b := range xBits {\n\t\txMask |= 1 << b\n\t}\n\n\tencodeBits := func(val, pos byte, dropX int) byte {\n\t\tvar out byte\n\t\tout |= xMask\n\t\tif dropX >= 0 {\n\t\t\tout &^= 1 << xBits[dropX]\n\t\t}\n\t\tif (val & 0x02) != 0 {\n\t\t\tout |= 1 << pBits[0]\n\t\t}\n\t\tif (val & 0x01) != 0 {\n\t\t\tout |= 1 << pBits[1]\n\t\t}\n\t\tfor i, bit := range vBits {\n\t\t\tif (pos>>(3-uint8(i)))&0x01 == 1 {\n\t\t\t\tout |= 1 << bit\n\t\t\t}\n\t\t}\n\t\treturn out\n\t}\n\n\tpaddingSet := make(map[byte]struct{})\n\tvar padding []byte\n\tfor drop := range xBits {\n\t\tfor val := 0; val < 4; val++ {\n\t\t\tfor pos := 0; pos < 16; pos++ {\n\t\t\t\tb := encodeBits(byte(val), byte(pos), drop)\n\t\t\t\tif bits.OnesCount8(b) >= 5 {\n\t\t\t\t\tif _, ok := paddingSet[b]; !ok {\n\t\t\t\t\t\tpaddingSet[b] = struct{}{}\n\t\t\t\t\t\tpadding = append(padding, b)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tsort.Slice(padding, func(i, j int) bool { return padding[i] < padding[j] })\n\tif len(padding) == 0 {\n\t\treturn nil, fmt.Errorf(\"custom table produced empty padding pool\")\n\t}\n\n\tlayout := &byteLayout{\n\t\tname:        fmt.Sprintf(\"custom(%s)\", cleaned),\n\t\thintMask:    xMask,\n\t\thintValue:   xMask,\n\t\tpadMarker:   padding[0],\n\t\tpaddingPool: padding,\n\t}\n\n\tfor val := 0; val < 4; val++ {\n\t\tfor pos := 0; pos < 16; pos++ {\n\t\t\tlayout.encodeHint[val][pos] = encodeBits(byte(val), byte(pos), -1)\n\t\t}\n\t}\n\tfor group := 0; group < 64; group++ {\n\t\tval := byte(group>>4) & 0x03\n\t\tpos := byte(group) & 0x0F\n\t\tlayout.encodeGroup[group] = encodeBits(val, pos, -1)\n\t}\n\tfor b := 0; b < 256; b++ {\n\t\twire := byte(b)\n\t\tif (wire & xMask) != xMask {\n\t\t\tcontinue\n\t\t}\n\t\tlayout.hintTable[wire] = true\n\n\t\tvar val, pos byte\n\t\tif wire&(1<<pBits[0]) != 0 {\n\t\t\tval |= 0x02\n\t\t}\n\t\tif wire&(1<<pBits[1]) != 0 {\n\t\t\tval |= 0x01\n\t\t}\n\t\tfor i, bit := range vBits {\n\t\t\tif wire&(1<<bit) != 0 {\n\t\t\t\tpos |= 1 << (3 - uint8(i))\n\t\t\t}\n\t\t}\n\t\tlayout.decodeGroup[wire] = (val << 4) | pos\n\t\tlayout.groupValid[wire] = true\n\t}\n\n\treturn layout, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/obfs/sudoku/packed.go",
    "content": "package sudoku\n\nimport (\n\t\"bufio\"\n\t\"io\"\n\t\"net\"\n\t\"sync\"\n)\n\nconst (\n\tRngBatchSize = 128\n\n\tpackedProtectedPrefixBytes = 14\n)\n\n// PackedConn encodes traffic with the packed Sudoku layout while preserving\n// the same padding model as the regular connection.\ntype PackedConn struct {\n\tnet.Conn\n\ttable  *Table\n\treader *bufio.Reader\n\n\t// Read-side buffers.\n\trawBuf      []byte\n\tpendingData pendingBuffer\n\n\t// Write-side state.\n\twriteMu  sync.Mutex\n\twriteBuf []byte\n\tbitBuf   uint64\n\tbitCount int\n\n\t// Read-side bit accumulator.\n\treadBitBuf uint64\n\treadBits   int\n\n\t// Padding selection matches Conn's threshold-based model.\n\trng              randomSource\n\tpaddingThreshold uint64\n\tpadMarker        byte\n\tpadPool          []byte\n}\n\nfunc (pc *PackedConn) CloseWrite() error {\n\tif pc == nil || pc.Conn == nil {\n\t\treturn nil\n\t}\n\tif cw, ok := pc.Conn.(interface{ CloseWrite() error }); ok {\n\t\treturn cw.CloseWrite()\n\t}\n\treturn nil\n}\n\nfunc (pc *PackedConn) CloseRead() error {\n\tif pc == nil || pc.Conn == nil {\n\t\treturn nil\n\t}\n\tif cr, ok := pc.Conn.(interface{ CloseRead() error }); ok {\n\t\treturn cr.CloseRead()\n\t}\n\treturn nil\n}\n\nfunc NewPackedConn(c net.Conn, table *Table, pMin, pMax int) *PackedConn {\n\tlocalRng := newSeededRand()\n\n\tpc := &PackedConn{\n\t\tConn:             c,\n\t\ttable:            table,\n\t\treader:           bufio.NewReaderSize(c, IOBufferSize),\n\t\trawBuf:           make([]byte, IOBufferSize),\n\t\tpendingData:      newPendingBuffer(4096),\n\t\twriteBuf:         make([]byte, 0, 4096),\n\t\trng:              localRng,\n\t\tpaddingThreshold: pickPaddingThreshold(localRng, pMin, pMax),\n\t}\n\n\tpc.padMarker = table.layout.padMarker\n\tfor _, b := range table.PaddingPool {\n\t\tif b != pc.padMarker {\n\t\t\tpc.padPool = append(pc.padPool, b)\n\t\t}\n\t}\n\tif len(pc.padPool) == 0 {\n\t\tpc.padPool = append(pc.padPool, pc.padMarker)\n\t}\n\treturn pc\n}\n\nfunc (pc *PackedConn) maybeAddPadding(out []byte) []byte {\n\tif shouldPad(pc.rng, pc.paddingThreshold) {\n\t\tout = append(out, pc.getPaddingByte())\n\t}\n\treturn out\n}\n\nfunc (pc *PackedConn) appendGroup(out []byte, group byte) []byte {\n\tout = pc.maybeAddPadding(out)\n\treturn append(out, pc.table.layout.groupByte(group))\n}\n\nfunc (pc *PackedConn) appendForcedPadding(out []byte) []byte {\n\treturn append(out, pc.getPaddingByte())\n}\n\nfunc (pc *PackedConn) nextProtectedPrefixGap() int {\n\treturn 1 + pc.rng.Intn(2)\n}\n\nfunc (pc *PackedConn) writeProtectedPrefix(out []byte, p []byte) ([]byte, int) {\n\tif len(p) == 0 {\n\t\treturn out, 0\n\t}\n\n\tlimit := len(p)\n\tif limit > packedProtectedPrefixBytes {\n\t\tlimit = packedProtectedPrefixBytes\n\t}\n\n\tfor padCount := 0; padCount < 1+pc.rng.Intn(2); padCount++ {\n\t\tout = pc.appendForcedPadding(out)\n\t}\n\n\tgap := pc.nextProtectedPrefixGap()\n\teffective := 0\n\tfor i := 0; i < limit; i++ {\n\t\tpc.bitBuf = (pc.bitBuf << 8) | uint64(p[i])\n\t\tpc.bitCount += 8\n\t\tfor pc.bitCount >= 6 {\n\t\t\tpc.bitCount -= 6\n\t\t\tgroup := byte(pc.bitBuf >> pc.bitCount)\n\t\t\tif pc.bitCount == 0 {\n\t\t\t\tpc.bitBuf = 0\n\t\t\t} else {\n\t\t\t\tpc.bitBuf &= (1 << pc.bitCount) - 1\n\t\t\t}\n\t\t\tout = pc.appendGroup(out, group&0x3F)\n\t\t}\n\n\t\teffective++\n\t\tif effective >= gap {\n\t\t\tout = pc.appendForcedPadding(out)\n\t\t\teffective = 0\n\t\t\tgap = pc.nextProtectedPrefixGap()\n\t\t}\n\t}\n\n\treturn out, limit\n}\n\nfunc (pc *PackedConn) Write(p []byte) (int, error) {\n\tif len(p) == 0 {\n\t\treturn 0, nil\n\t}\n\n\tpc.writeMu.Lock()\n\tdefer pc.writeMu.Unlock()\n\n\tneeded := len(p)*3/2 + 32\n\tif cap(pc.writeBuf) < needed {\n\t\tpc.writeBuf = make([]byte, 0, needed)\n\t}\n\tout := pc.writeBuf[:0]\n\n\tvar prefixN int\n\tout, prefixN = pc.writeProtectedPrefix(out, p)\n\n\ti := prefixN\n\tn := len(p)\n\n\tfor pc.bitCount > 0 && i < n {\n\t\tb := p[i]\n\t\ti++\n\t\tpc.bitBuf = (pc.bitBuf << 8) | uint64(b)\n\t\tpc.bitCount += 8\n\t\tfor pc.bitCount >= 6 {\n\t\t\tpc.bitCount -= 6\n\t\t\tgroup := byte(pc.bitBuf >> pc.bitCount)\n\t\t\tif pc.bitCount == 0 {\n\t\t\t\tpc.bitBuf = 0\n\t\t\t} else {\n\t\t\t\tpc.bitBuf &= (1 << pc.bitCount) - 1\n\t\t\t}\n\t\t\tout = pc.appendGroup(out, group&0x3F)\n\t\t}\n\t}\n\n\tfor i+11 < n {\n\t\tfor batch := 0; batch < 4; batch++ {\n\t\t\tb1, b2, b3 := p[i], p[i+1], p[i+2]\n\t\t\ti += 3\n\n\t\t\tg1 := (b1 >> 2) & 0x3F\n\t\t\tg2 := ((b1 & 0x03) << 4) | ((b2 >> 4) & 0x0F)\n\t\t\tg3 := ((b2 & 0x0F) << 2) | ((b3 >> 6) & 0x03)\n\t\t\tg4 := b3 & 0x3F\n\n\t\t\tout = pc.appendGroup(out, g1)\n\t\t\tout = pc.appendGroup(out, g2)\n\t\t\tout = pc.appendGroup(out, g3)\n\t\t\tout = pc.appendGroup(out, g4)\n\t\t}\n\t}\n\n\tfor i+2 < n {\n\t\tb1, b2, b3 := p[i], p[i+1], p[i+2]\n\t\ti += 3\n\n\t\tg1 := (b1 >> 2) & 0x3F\n\t\tg2 := ((b1 & 0x03) << 4) | ((b2 >> 4) & 0x0F)\n\t\tg3 := ((b2 & 0x0F) << 2) | ((b3 >> 6) & 0x03)\n\t\tg4 := b3 & 0x3F\n\n\t\tout = pc.appendGroup(out, g1)\n\t\tout = pc.appendGroup(out, g2)\n\t\tout = pc.appendGroup(out, g3)\n\t\tout = pc.appendGroup(out, g4)\n\t}\n\n\tfor ; i < n; i++ {\n\t\tb := p[i]\n\t\tpc.bitBuf = (pc.bitBuf << 8) | uint64(b)\n\t\tpc.bitCount += 8\n\t\tfor pc.bitCount >= 6 {\n\t\t\tpc.bitCount -= 6\n\t\t\tgroup := byte(pc.bitBuf >> pc.bitCount)\n\t\t\tif pc.bitCount == 0 {\n\t\t\t\tpc.bitBuf = 0\n\t\t\t} else {\n\t\t\t\tpc.bitBuf &= (1 << pc.bitCount) - 1\n\t\t\t}\n\t\t\tout = pc.appendGroup(out, group&0x3F)\n\t\t}\n\t}\n\n\tif pc.bitCount > 0 {\n\t\tgroup := byte(pc.bitBuf << (6 - pc.bitCount))\n\t\tpc.bitBuf = 0\n\t\tpc.bitCount = 0\n\t\tout = pc.appendGroup(out, group&0x3F)\n\t\tout = append(out, pc.padMarker)\n\t}\n\n\tout = pc.maybeAddPadding(out)\n\n\tif len(out) > 0 {\n\t\tpc.writeBuf = out[:0]\n\t\treturn len(p), writeFull(pc.Conn, out)\n\t}\n\tpc.writeBuf = out[:0]\n\treturn len(p), nil\n}\n\nfunc (pc *PackedConn) Flush() error {\n\tpc.writeMu.Lock()\n\tdefer pc.writeMu.Unlock()\n\n\tout := pc.writeBuf[:0]\n\tif pc.bitCount > 0 {\n\t\tgroup := byte(pc.bitBuf << (6 - pc.bitCount))\n\t\tpc.bitBuf = 0\n\t\tpc.bitCount = 0\n\n\t\tout = append(out, pc.table.layout.groupByte(group&0x3F))\n\t\tout = append(out, pc.padMarker)\n\t}\n\n\tout = pc.maybeAddPadding(out)\n\n\tif len(out) > 0 {\n\t\tpc.writeBuf = out[:0]\n\t\treturn writeFull(pc.Conn, out)\n\t}\n\treturn nil\n}\n\nfunc writeFull(w io.Writer, b []byte) error {\n\tfor len(b) > 0 {\n\t\tn, err := w.Write(b)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif n == 0 {\n\t\t\treturn io.ErrShortWrite\n\t\t}\n\t\tb = b[n:]\n\t}\n\treturn nil\n}\n\nfunc (pc *PackedConn) Read(p []byte) (int, error) {\n\tif n, ok := drainPending(p, &pc.pendingData); ok {\n\t\treturn n, nil\n\t}\n\n\tfor {\n\t\tnr, rErr := pc.reader.Read(pc.rawBuf)\n\t\tif nr > 0 {\n\t\t\trBuf := pc.readBitBuf\n\t\t\trBits := pc.readBits\n\t\t\tpadMarker := pc.padMarker\n\t\t\tlayout := pc.table.layout\n\n\t\t\tfor _, b := range pc.rawBuf[:nr] {\n\t\t\t\tif !layout.hintTable[b] {\n\t\t\t\t\tif b == padMarker {\n\t\t\t\t\t\trBuf = 0\n\t\t\t\t\t\trBits = 0\n\t\t\t\t\t}\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tgroup, ok := layout.decodePackedGroup(b)\n\t\t\t\tif !ok {\n\t\t\t\t\treturn 0, ErrInvalidSudokuMapMiss\n\t\t\t\t}\n\n\t\t\t\trBuf = (rBuf << 6) | uint64(group)\n\t\t\t\trBits += 6\n\n\t\t\t\tif rBits >= 8 {\n\t\t\t\t\trBits -= 8\n\t\t\t\t\tval := byte(rBuf >> rBits)\n\t\t\t\t\tpc.pendingData.appendByte(val)\n\t\t\t\t\tif rBits == 0 {\n\t\t\t\t\t\trBuf = 0\n\t\t\t\t\t} else {\n\t\t\t\t\t\trBuf &= (uint64(1) << rBits) - 1\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tpc.readBitBuf = rBuf\n\t\t\tpc.readBits = rBits\n\t\t}\n\n\t\tif rErr != nil {\n\t\t\tif rErr == io.EOF {\n\t\t\t\tpc.readBitBuf = 0\n\t\t\t\tpc.readBits = 0\n\t\t\t}\n\t\t\tif pc.pendingData.available() > 0 {\n\t\t\t\tbreak\n\t\t\t}\n\t\t\treturn 0, rErr\n\t\t}\n\n\t\tif pc.pendingData.available() > 0 {\n\t\t\tbreak\n\t\t}\n\t}\n\n\tn, _ := drainPending(p, &pc.pendingData)\n\treturn n, nil\n}\n\nfunc (pc *PackedConn) getPaddingByte() byte {\n\treturn pc.padPool[pc.rng.Intn(len(pc.padPool))]\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/obfs/sudoku/packed_prefix_test.go",
    "content": "package sudoku\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"math/rand\"\n\t\"net\"\n\t\"testing\"\n\t\"time\"\n)\n\ntype mockConn struct {\n\treadBuf  []byte\n\twriteBuf []byte\n}\n\nfunc (c *mockConn) Read(p []byte) (int, error) {\n\tif len(c.readBuf) == 0 {\n\t\treturn 0, io.EOF\n\t}\n\tn := copy(p, c.readBuf)\n\tc.readBuf = c.readBuf[n:]\n\treturn n, nil\n}\n\nfunc (c *mockConn) Write(p []byte) (int, error) {\n\tc.writeBuf = append(c.writeBuf, p...)\n\treturn len(p), nil\n}\n\nfunc (c *mockConn) Close() error                     { return nil }\nfunc (c *mockConn) LocalAddr() net.Addr              { return nil }\nfunc (c *mockConn) RemoteAddr() net.Addr             { return nil }\nfunc (c *mockConn) SetDeadline(time.Time) error      { return nil }\nfunc (c *mockConn) SetReadDeadline(time.Time) error  { return nil }\nfunc (c *mockConn) SetWriteDeadline(time.Time) error { return nil }\n\nfunc TestPackedConn_ProtectedPrefixPadding(t *testing.T) {\n\ttable := NewTable(\"packed-prefix-seed\", \"prefer_ascii\")\n\tmock := &mockConn{}\n\twriter := NewPackedConn(mock, table, 0, 0)\n\twriter.rng = rand.New(rand.NewSource(1))\n\n\tpayload := bytes.Repeat([]byte{0}, 32)\n\tif _, err := writer.Write(payload); err != nil {\n\t\tt.Fatalf(\"write: %v\", err)\n\t}\n\n\twire := append([]byte(nil), mock.writeBuf...)\n\tif len(wire) < 20 {\n\t\tt.Fatalf(\"wire too short: %d\", len(wire))\n\t}\n\n\tfirstHint := -1\n\tnonHintCount := 0\n\tmaxHintRun := 0\n\tcurrentHintRun := 0\n\tfor i, b := range wire[:20] {\n\t\tif table.layout.isHint(b) {\n\t\t\tif firstHint == -1 {\n\t\t\t\tfirstHint = i\n\t\t\t}\n\t\t\tcurrentHintRun++\n\t\t\tif currentHintRun > maxHintRun {\n\t\t\t\tmaxHintRun = currentHintRun\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tnonHintCount++\n\t\tcurrentHintRun = 0\n\t}\n\n\tif firstHint < 1 || firstHint > 2 {\n\t\tt.Fatalf(\"expected 1-2 leading padding bytes, first hint index=%d\", firstHint)\n\t}\n\tif nonHintCount < 6 {\n\t\tt.Fatalf(\"expected dense prefix padding, got only %d non-hint bytes in first 20\", nonHintCount)\n\t}\n\tif maxHintRun > 3 {\n\t\tt.Fatalf(\"prefix still exposes long hint run: %d\", maxHintRun)\n\t}\n\n\treader := NewPackedConn(&mockConn{readBuf: wire}, table, 0, 0)\n\tdecoded := make([]byte, len(payload))\n\tif _, err := io.ReadFull(reader, decoded); err != nil {\n\t\tt.Fatalf(\"read back: %v\", err)\n\t}\n\tif !bytes.Equal(decoded, payload) {\n\t\tt.Fatalf(\"roundtrip mismatch\")\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/obfs/sudoku/padding_prob.go",
    "content": "package sudoku\n\nconst probOne = uint64(1) << 32\n\nfunc pickPaddingThreshold(r randomSource, pMin, pMax int) uint64 {\n\tif r == nil {\n\t\treturn 0\n\t}\n\tif pMin < 0 {\n\t\tpMin = 0\n\t}\n\tif pMax < pMin {\n\t\tpMax = pMin\n\t}\n\tif pMax > 100 {\n\t\tpMax = 100\n\t}\n\tif pMin > 100 {\n\t\tpMin = 100\n\t}\n\n\tmin := uint64(pMin) * probOne / 100\n\tmax := uint64(pMax) * probOne / 100\n\tif max <= min {\n\t\treturn min\n\t}\n\tu := uint64(r.Uint32())\n\treturn min + (u * (max - min) >> 32)\n}\n\nfunc shouldPad(r randomSource, threshold uint64) bool {\n\tif threshold == 0 {\n\t\treturn false\n\t}\n\tif threshold >= probOne {\n\t\treturn true\n\t}\n\tif r == nil {\n\t\treturn false\n\t}\n\treturn uint64(r.Uint32()) < threshold\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/obfs/sudoku/pending.go",
    "content": "package sudoku\n\ntype pendingBuffer struct {\n\tdata []byte\n\toff  int\n}\n\nfunc newPendingBuffer(capacity int) pendingBuffer {\n\treturn pendingBuffer{data: make([]byte, 0, capacity)}\n}\n\nfunc (p *pendingBuffer) available() int {\n\tif p == nil {\n\t\treturn 0\n\t}\n\treturn len(p.data) - p.off\n}\n\nfunc (p *pendingBuffer) reset() {\n\tif p == nil {\n\t\treturn\n\t}\n\tp.data = p.data[:0]\n\tp.off = 0\n}\n\nfunc (p *pendingBuffer) ensureAppendCapacity(extra int) {\n\tif p == nil || extra <= 0 || p.off == 0 {\n\t\treturn\n\t}\n\tif cap(p.data)-len(p.data) >= extra {\n\t\treturn\n\t}\n\n\tunread := len(p.data) - p.off\n\tcopy(p.data[:unread], p.data[p.off:])\n\tp.data = p.data[:unread]\n\tp.off = 0\n}\n\nfunc (p *pendingBuffer) appendByte(b byte) {\n\tp.ensureAppendCapacity(1)\n\tp.data = append(p.data, b)\n}\n\nfunc drainPending(dst []byte, pending *pendingBuffer) (int, bool) {\n\tif pending == nil || pending.available() == 0 {\n\t\treturn 0, false\n\t}\n\n\tn := copy(dst, pending.data[pending.off:])\n\tpending.off += n\n\tif pending.off == len(pending.data) {\n\t\tpending.reset()\n\t}\n\treturn n, true\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/obfs/sudoku/rand.go",
    "content": "package sudoku\n\nimport (\n\tcrypto_rand \"crypto/rand\"\n\t\"encoding/binary\"\n\t\"time\"\n)\n\ntype randomSource interface {\n\tUint32() uint32\n\tUint64() uint64\n\tIntn(n int) int\n}\n\ntype sudokuRand struct {\n\tstate uint64\n}\n\nfunc newSeededRand() *sudokuRand {\n\tseed := time.Now().UnixNano()\n\tvar seedBytes [8]byte\n\tif _, err := crypto_rand.Read(seedBytes[:]); err == nil {\n\t\tseed = int64(binary.BigEndian.Uint64(seedBytes[:]))\n\t}\n\treturn newSudokuRand(seed)\n}\n\nfunc newSudokuRand(seed int64) *sudokuRand {\n\tstate := uint64(seed)\n\tif state == 0 {\n\t\tstate = 0x9e3779b97f4a7c15\n\t}\n\treturn &sudokuRand{state: state}\n}\n\nfunc (r *sudokuRand) Uint64() uint64 {\n\tif r == nil {\n\t\treturn 0\n\t}\n\tr.state += 0x9e3779b97f4a7c15\n\tz := r.state\n\tz = (z ^ (z >> 30)) * 0xbf58476d1ce4e5b9\n\tz = (z ^ (z >> 27)) * 0x94d049bb133111eb\n\treturn z ^ (z >> 31)\n}\n\nfunc (r *sudokuRand) Uint32() uint32 {\n\treturn uint32(r.Uint64() >> 32)\n}\n\nfunc (r *sudokuRand) Intn(n int) int {\n\tif n <= 1 {\n\t\treturn 0\n\t}\n\treturn int((uint64(r.Uint32()) * uint64(n)) >> 32)\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/obfs/sudoku/table.go",
    "content": "package sudoku\n\nimport (\n\t\"crypto/sha256\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"math/rand\"\n\t\"strings\"\n)\n\nvar (\n\tErrInvalidSudokuMapMiss = errors.New(\"INVALID_SUDOKU_MAP_MISS\")\n)\n\ntype Table struct {\n\tEncodeTable [256][][4]byte\n\tDecodeMap   map[uint32]byte\n\tPaddingPool []byte\n\tIsASCII     bool // 标记当前模式\n\tlayout      *byteLayout\n\topposite    *Table\n\thint        uint32\n}\n\n// NewTable initializes the obfuscation tables with built-in layouts.\n// Equivalent to calling NewTableWithCustom(key, mode, \"\").\nfunc NewTable(key string, mode string) *Table {\n\tt, err := NewTableWithCustom(key, mode, \"\")\n\tif err != nil {\n\t\tpanic(err)\n\t}\n\treturn t\n}\n\n// NewTableWithCustom initializes the uplink/probe Sudoku table using either predefined\n// or directional layouts. Directional modes such as \"up_ascii_down_entropy\" return the\n// client->server table and internally attach the opposite direction table for runtime use.\n// The customPattern must contain 8 characters with exactly 2 x, 2 p, and 4 v (case-insensitive).\nfunc NewTableWithCustom(key string, mode string, customPattern string) (*Table, error) {\n\tasciiMode, err := ParseASCIIMode(mode)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tuplinkPattern := customPatternForToken(asciiMode.Uplink, customPattern)\n\tdownlinkPattern := customPatternForToken(asciiMode.Downlink, customPattern)\n\thint := tableHintFingerprint(key, asciiMode.Canonical(), uplinkPattern, downlinkPattern)\n\n\tuplink, err := newSingleDirectionTable(key, asciiMode.uplinkPreference(), uplinkPattern)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tuplink.hint = hint\n\tif asciiMode.Uplink == asciiMode.Downlink {\n\t\tuplink.opposite = uplink\n\t\treturn uplink, nil\n\t}\n\n\tdownlink, err := newSingleDirectionTable(key, asciiMode.downlinkPreference(), downlinkPattern)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tdownlink.hint = hint\n\tuplink.opposite = downlink\n\tdownlink.opposite = uplink\n\treturn uplink, nil\n}\n\nfunc newSingleDirectionTable(key string, mode string, customPattern string) (*Table, error) {\n\tlayout, err := resolveLayout(mode, customPattern)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tt := &Table{\n\t\tDecodeMap: make(map[uint32]byte),\n\t\tIsASCII:   layout.name == \"ascii\",\n\t\tlayout:    layout,\n\t}\n\tt.PaddingPool = append(t.PaddingPool, layout.paddingPool...)\n\n\t// 生成数独网格 (逻辑不变)\n\tallGrids := GenerateAllGrids()\n\th := sha256.New()\n\th.Write([]byte(key))\n\tseed := int64(binary.BigEndian.Uint64(h.Sum(nil)[:8]))\n\trng := rand.New(rand.NewSource(seed))\n\n\tshuffledGrids := make([]Grid, 288)\n\tcopy(shuffledGrids, allGrids)\n\trng.Shuffle(len(shuffledGrids), func(i, j int) {\n\t\tshuffledGrids[i], shuffledGrids[j] = shuffledGrids[j], shuffledGrids[i]\n\t})\n\n\t// 预计算组合\n\tvar combinations [][]int\n\tvar combine func(int, int, []int)\n\tcombine = func(s, k int, c []int) {\n\t\tif k == 0 {\n\t\t\ttmp := make([]int, len(c))\n\t\t\tcopy(tmp, c)\n\t\t\tcombinations = append(combinations, tmp)\n\t\t\treturn\n\t\t}\n\t\tfor i := s; i <= 16-k; i++ {\n\t\t\tc = append(c, i)\n\t\t\tcombine(i+1, k-1, c)\n\t\t\tc = c[:len(c)-1]\n\t\t}\n\t}\n\tcombine(0, 4, []int{})\n\n\t// 构建映射表\n\tfor byteVal := 0; byteVal < 256; byteVal++ {\n\t\ttargetGrid := shuffledGrids[byteVal]\n\t\tfor _, positions := range combinations {\n\t\t\tvar currentHints [4]byte\n\n\t\t\t// 1. 计算抽象提示 (Abstract Hints)\n\t\t\t// 我们先计算出 val 和 pos，后面再根据模式编码成 byte\n\t\t\tvar rawParts [4]struct{ val, pos byte }\n\n\t\t\tfor i, pos := range positions {\n\t\t\t\tval := targetGrid[pos] // 1..4\n\t\t\t\trawParts[i] = struct{ val, pos byte }{val, uint8(pos)}\n\t\t\t}\n\n\t\t\t// 检查唯一性 (数独逻辑)\n\t\t\tmatchCount := 0\n\t\t\tfor _, g := range allGrids {\n\t\t\t\tmatch := true\n\t\t\t\tfor _, p := range rawParts {\n\t\t\t\t\tif g[p.pos] != p.val {\n\t\t\t\t\t\tmatch = false\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif match {\n\t\t\t\t\tmatchCount++\n\t\t\t\t\tif matchCount > 1 {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tif matchCount == 1 {\n\t\t\t\t// 唯一确定，生成最终编码字节\n\t\t\t\tfor i, p := range rawParts {\n\t\t\t\t\tcurrentHints[i] = t.layout.hintByte(p.val-1, p.pos)\n\t\t\t\t}\n\n\t\t\t\tt.EncodeTable[byteVal] = append(t.EncodeTable[byteVal], currentHints)\n\t\t\t\t// 生成解码键 (需要对 Hints 进行排序以忽略传输顺序)\n\t\t\t\tkey := packHintsToKey(currentHints)\n\t\t\t\tt.DecodeMap[key] = byte(byteVal)\n\t\t\t}\n\t\t}\n\t}\n\treturn t, nil\n}\n\nfunc customPatternForToken(token string, customPattern string) string {\n\tif token == asciiModeTokenEntropy {\n\t\treturn customPattern\n\t}\n\treturn \"\"\n}\n\nfunc (t *Table) OppositeDirection() *Table {\n\tif t == nil || t.opposite == nil {\n\t\treturn t\n\t}\n\treturn t.opposite\n}\n\nfunc (t *Table) Hint() uint32 {\n\tif t == nil {\n\t\treturn 0\n\t}\n\treturn t.hint\n}\n\nfunc tableHintFingerprint(key string, mode string, uplinkPattern string, downlinkPattern string) uint32 {\n\tsum := sha256.Sum256([]byte(strings.Join([]string{\n\t\t\"sudoku-table-hint\",\n\t\tkey,\n\t\tmode,\n\t\tstrings.ToLower(strings.TrimSpace(uplinkPattern)),\n\t\tstrings.ToLower(strings.TrimSpace(downlinkPattern)),\n\t}, \"\\x00\")))\n\treturn binary.BigEndian.Uint32(sum[:4])\n}\n\nfunc packHintsToKey(hints [4]byte) uint32 {\n\t// Sorting network for 4 elements (Bubble sort unrolled)\n\t// Swap if a > b\n\tif hints[0] > hints[1] {\n\t\thints[0], hints[1] = hints[1], hints[0]\n\t}\n\tif hints[2] > hints[3] {\n\t\thints[2], hints[3] = hints[3], hints[2]\n\t}\n\tif hints[0] > hints[2] {\n\t\thints[0], hints[2] = hints[2], hints[0]\n\t}\n\tif hints[1] > hints[3] {\n\t\thints[1], hints[3] = hints[3], hints[1]\n\t}\n\tif hints[1] > hints[2] {\n\t\thints[1], hints[2] = hints[2], hints[1]\n\t}\n\n\treturn uint32(hints[0])<<24 | uint32(hints[1])<<16 | uint32(hints[2])<<8 | uint32(hints[3])\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/obfs/sudoku/table_set.go",
    "content": "package sudoku\n\nimport \"fmt\"\n\n// TableSet is a small helper for managing multiple Sudoku tables (e.g. for per-connection rotation).\n// It is intentionally decoupled from the tunnel/app layers.\ntype TableSet struct {\n\tTables []*Table\n}\n\n// NewTableSet builds one or more tables from key/mode and a list of custom X/P/V patterns.\n// If patterns is empty, it builds a single default table (customPattern=\"\").\nfunc NewTableSet(key string, mode string, patterns []string) (*TableSet, error) {\n\tif len(patterns) == 0 {\n\t\tt, err := NewTableWithCustom(key, mode, \"\")\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn &TableSet{Tables: []*Table{t}}, nil\n\t}\n\n\ttables := make([]*Table, 0, len(patterns))\n\tfor i, pattern := range patterns {\n\t\tt, err := NewTableWithCustom(key, mode, pattern)\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"build table[%d] (%q): %w\", i, pattern, err)\n\t\t}\n\t\ttables = append(tables, t)\n\t}\n\treturn &TableSet{Tables: tables}, nil\n}\n\nfunc (ts *TableSet) Candidates() []*Table {\n\tif ts == nil {\n\t\treturn nil\n\t}\n\treturn ts.Tables\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/replay.go",
    "content": "package sudoku\n\nimport (\n\t\"sync\"\n\t\"time\"\n)\n\nvar handshakeReplayTTL = 60 * time.Second\n\ntype nonceSet struct {\n\tmu         sync.Mutex\n\tm          map[[kipHelloNonceSize]byte]time.Time\n\tmaxEntries int\n\tlastPrune  time.Time\n}\n\nfunc newNonceSet(maxEntries int) *nonceSet {\n\tif maxEntries <= 0 {\n\t\tmaxEntries = 4096\n\t}\n\treturn &nonceSet{\n\t\tm:          make(map[[kipHelloNonceSize]byte]time.Time),\n\t\tmaxEntries: maxEntries,\n\t}\n}\n\nfunc (s *nonceSet) allow(nonce [kipHelloNonceSize]byte, now time.Time, ttl time.Duration) bool {\n\ts.mu.Lock()\n\tdefer s.mu.Unlock()\n\n\tif ttl <= 0 {\n\t\tttl = 60 * time.Second\n\t}\n\n\tif now.Sub(s.lastPrune) > ttl/2 || len(s.m) > s.maxEntries {\n\t\tfor k, exp := range s.m {\n\t\t\tif !now.Before(exp) {\n\t\t\t\tdelete(s.m, k)\n\t\t\t}\n\t\t}\n\t\ts.lastPrune = now\n\t\tfor len(s.m) > s.maxEntries {\n\t\t\tfor k := range s.m {\n\t\t\t\tdelete(s.m, k)\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\tif exp, ok := s.m[nonce]; ok && now.Before(exp) {\n\t\treturn false\n\t}\n\ts.m[nonce] = now.Add(ttl)\n\treturn true\n}\n\ntype handshakeReplayProtector struct {\n\tusers sync.Map // map[userHash string]*nonceSet\n}\n\nfunc (p *handshakeReplayProtector) allow(userHash string, nonce [kipHelloNonceSize]byte, now time.Time) bool {\n\tif userHash == \"\" {\n\t\tuserHash = \"_\"\n\t}\n\tval, _ := p.users.LoadOrStore(userHash, newNonceSet(4096))\n\tset, ok := val.(*nonceSet)\n\tif !ok || set == nil {\n\t\tset = newNonceSet(4096)\n\t\tp.users.Store(userHash, set)\n\t}\n\treturn set.allow(nonce, now, handshakeReplayTTL)\n}\n\nvar globalHandshakeReplay = &handshakeReplayProtector{}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/session_keys.go",
    "content": "package sudoku\n\nimport (\n\t\"crypto/ecdh\"\n\t\"crypto/sha256\"\n\t\"fmt\"\n\t\"io\"\n\n\t\"golang.org/x/crypto/hkdf\"\n)\n\nfunc derivePSKDirectionalBases(psk string) (c2s, s2c []byte) {\n\tsum := sha256.Sum256([]byte(psk))\n\tc2sKey := make([]byte, 32)\n\ts2cKey := make([]byte, 32)\n\tif _, err := io.ReadFull(hkdf.Expand(sha256.New, sum[:], []byte(\"sudoku-psk-c2s\")), c2sKey); err != nil {\n\t\tpanic(\"sudoku: hkdf expand failed\")\n\t}\n\tif _, err := io.ReadFull(hkdf.Expand(sha256.New, sum[:], []byte(\"sudoku-psk-s2c\")), s2cKey); err != nil {\n\t\tpanic(\"sudoku: hkdf expand failed\")\n\t}\n\treturn c2sKey, s2cKey\n}\n\nfunc deriveSessionDirectionalBases(psk string, shared []byte, nonce [kipHelloNonceSize]byte) (c2s, s2c []byte, err error) {\n\tsum := sha256.Sum256([]byte(psk))\n\tikm := make([]byte, 0, len(shared)+len(nonce))\n\tikm = append(ikm, shared...)\n\tikm = append(ikm, nonce[:]...)\n\n\tprk := hkdf.Extract(sha256.New, ikm, sum[:])\n\n\tc2sKey := make([]byte, 32)\n\ts2cKey := make([]byte, 32)\n\tif _, err := io.ReadFull(hkdf.Expand(sha256.New, prk, []byte(\"sudoku-session-c2s\")), c2sKey); err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"hkdf expand c2s: %w\", err)\n\t}\n\tif _, err := io.ReadFull(hkdf.Expand(sha256.New, prk, []byte(\"sudoku-session-s2c\")), s2cKey); err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"hkdf expand s2c: %w\", err)\n\t}\n\treturn c2sKey, s2cKey, nil\n}\n\nfunc x25519SharedSecret(priv *ecdh.PrivateKey, peerPub []byte) ([]byte, error) {\n\tif priv == nil {\n\t\treturn nil, fmt.Errorf(\"nil priv\")\n\t}\n\tcurve := ecdh.X25519()\n\tpk, err := curve.NewPublicKey(peerPub)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"parse peer pub: %w\", err)\n\t}\n\tsecret, err := priv.ECDH(pk)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"ecdh: %w\", err)\n\t}\n\treturn secret, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/table_probe.go",
    "content": "package sudoku\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\tcrand \"crypto/rand\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/transport/sudoku/crypto\"\n\t\"github.com/metacubex/mihomo/transport/sudoku/obfs/sudoku\"\n)\n\ntype clientTableChoice struct {\n\tTable   *sudoku.Table\n\tHint    uint32\n\tHasHint bool\n}\n\nfunc pickClientTable(cfg *ProtocolConfig) (clientTableChoice, error) {\n\tcandidates := cfg.tableCandidates()\n\tif len(candidates) == 0 {\n\t\treturn clientTableChoice{}, fmt.Errorf(\"no table configured\")\n\t}\n\tif len(candidates) == 1 {\n\t\treturn clientTableChoice{Table: candidates[0], Hint: candidates[0].Hint()}, nil\n\t}\n\tvar b [1]byte\n\tif _, err := crand.Read(b[:]); err != nil {\n\t\treturn clientTableChoice{}, fmt.Errorf(\"random table pick failed: %w\", err)\n\t}\n\tidx := int(b[0]) % len(candidates)\n\treturn clientTableChoice{Table: candidates[idx], Hint: candidates[idx].Hint(), HasHint: true}, nil\n}\n\ntype readOnlyConn struct {\n\t*bytes.Reader\n}\n\nfunc (c *readOnlyConn) Write([]byte) (int, error)        { return 0, io.ErrClosedPipe }\nfunc (c *readOnlyConn) Close() error                     { return nil }\nfunc (c *readOnlyConn) LocalAddr() net.Addr              { return nil }\nfunc (c *readOnlyConn) RemoteAddr() net.Addr             { return nil }\nfunc (c *readOnlyConn) SetDeadline(time.Time) error      { return nil }\nfunc (c *readOnlyConn) SetReadDeadline(time.Time) error  { return nil }\nfunc (c *readOnlyConn) SetWriteDeadline(time.Time) error { return nil }\n\nfunc drainBuffered(r *bufio.Reader) ([]byte, error) {\n\tn := r.Buffered()\n\tif n <= 0 {\n\t\treturn nil, nil\n\t}\n\tout := make([]byte, n)\n\t_, err := io.ReadFull(r, out)\n\treturn out, err\n}\n\nfunc probeHandshakeBytes(probe []byte, cfg *ProtocolConfig, table *sudoku.Table) error {\n\trc := &readOnlyConn{Reader: bytes.NewReader(probe)}\n\t_, obfsConn := buildServerObfsConn(rc, cfg, table, false)\n\tseed := ServerAEADSeed(cfg.Key)\n\tpskC2S, pskS2C := derivePSKDirectionalBases(seed)\n\t// Server side: recv is client->server, send is server->client.\n\tcConn, err := crypto.NewRecordConn(obfsConn, cfg.AEADMethod, pskS2C, pskC2S)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tmsg, err := ReadKIPMessage(cConn)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif msg.Type != KIPTypeClientHello {\n\t\treturn fmt.Errorf(\"unexpected handshake message: %d\", msg.Type)\n\t}\n\tch, err := DecodeKIPClientHelloPayload(msg.Payload)\n\tif err != nil {\n\t\treturn err\n\t}\n\tif absInt64(time.Now().Unix()-ch.Timestamp.Unix()) > int64(kipHandshakeSkew.Seconds()) {\n\t\treturn fmt.Errorf(\"time skew/replay\")\n\t}\n\n\treturn nil\n}\n\nfunc selectTableByProbe(r *bufio.Reader, cfg *ProtocolConfig, tables []*sudoku.Table) (*sudoku.Table, []byte, error) {\n\tconst (\n\t\tmaxProbeBytes = 64 * 1024\n\t\treadChunk     = 4 * 1024\n\t)\n\tif len(tables) == 0 {\n\t\treturn nil, nil, fmt.Errorf(\"no table candidates\")\n\t}\n\tif len(tables) > 255 {\n\t\treturn nil, nil, fmt.Errorf(\"too many table candidates: %d\", len(tables))\n\t}\n\n\t// Copy so we can prune candidates without mutating the caller slice.\n\tcandidates := make([]*sudoku.Table, 0, len(tables))\n\tfor _, t := range tables {\n\t\tif t != nil {\n\t\t\tcandidates = append(candidates, t)\n\t\t}\n\t}\n\tif len(candidates) == 0 {\n\t\treturn nil, nil, fmt.Errorf(\"no table candidates\")\n\t}\n\n\tprobe, err := drainBuffered(r)\n\tif err != nil {\n\t\treturn nil, nil, fmt.Errorf(\"drain buffered bytes failed: %w\", err)\n\t}\n\n\ttmp := make([]byte, readChunk)\n\tfor {\n\t\tif len(candidates) == 1 {\n\t\t\ttail, err := drainBuffered(r)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, nil, fmt.Errorf(\"drain buffered bytes failed: %w\", err)\n\t\t\t}\n\t\t\tprobe = append(probe, tail...)\n\t\t\treturn candidates[0], probe, nil\n\t\t}\n\n\t\tneedMore := false\n\t\tnext := candidates[:0]\n\t\tfor _, table := range candidates {\n\t\t\terr := probeHandshakeBytes(probe, cfg, table)\n\t\t\tif err == nil {\n\t\t\t\ttail, err := drainBuffered(r)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, nil, fmt.Errorf(\"drain buffered bytes failed: %w\", err)\n\t\t\t\t}\n\t\t\t\tprobe = append(probe, tail...)\n\t\t\t\treturn table, probe, nil\n\t\t\t}\n\t\t\tif errors.Is(err, io.EOF) || errors.Is(err, io.ErrUnexpectedEOF) {\n\t\t\t\tneedMore = true\n\t\t\t\tnext = append(next, table)\n\t\t\t}\n\t\t\t// Definitive mismatch: drop table.\n\t\t}\n\t\tcandidates = next\n\n\t\tif len(candidates) == 0 || !needMore {\n\t\t\treturn nil, probe, fmt.Errorf(\"handshake table selection failed\")\n\t\t}\n\t\tif len(probe) >= maxProbeBytes {\n\t\t\treturn nil, probe, fmt.Errorf(\"handshake probe exceeded %d bytes\", maxProbeBytes)\n\t\t}\n\n\t\tn, err := r.Read(tmp)\n\t\tif n > 0 {\n\t\t\tprobe = append(probe, tmp[:n]...)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn nil, probe, fmt.Errorf(\"handshake probe read failed: %w\", err)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/tables.go",
    "content": "package sudoku\n\nimport (\n\t\"strings\"\n\n\t\"github.com/metacubex/mihomo/transport/sudoku/obfs/sudoku\"\n)\n\nfunc normalizeCustomPatterns(customTable string, customTables []string) []string {\n\tpatterns := customTables\n\tif len(patterns) == 0 && strings.TrimSpace(customTable) != \"\" {\n\t\tpatterns = []string{customTable}\n\t}\n\tif len(patterns) == 0 {\n\t\tpatterns = []string{\"\"}\n\t}\n\treturn patterns\n}\n\nfunc normalizeTablePatterns(tableType string, customTable string, customTables []string) ([]string, error) {\n\tpatterns := normalizeCustomPatterns(customTable, customTables)\n\tif _, err := sudoku.ParseASCIIMode(tableType); err != nil {\n\t\treturn nil, err\n\t}\n\treturn patterns, nil\n}\n\n// NewTablesWithCustomPatterns builds one or more obfuscation tables from x/v/p custom patterns.\n// When customTables is non-empty it overrides customTable (matching upstream Sudoku behavior).\n//\n// Deprecated-ish: prefer NewClientTablesWithCustomPatterns / NewServerTablesWithCustomPatterns.\nfunc NewTablesWithCustomPatterns(key string, tableType string, customTable string, customTables []string) ([]*sudoku.Table, error) {\n\tpatterns, err := normalizeTablePatterns(tableType, customTable, customTables)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\ttables := make([]*sudoku.Table, 0, len(patterns))\n\tfor _, pattern := range patterns {\n\t\tpattern = strings.TrimSpace(pattern)\n\t\tt, err := NewTableWithCustom(key, tableType, pattern)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttables = append(tables, t)\n\t}\n\treturn tables, nil\n}\n\nfunc NewClientTablesWithCustomPatterns(key string, tableType string, customTable string, customTables []string) ([]*sudoku.Table, error) {\n\treturn NewTablesWithCustomPatterns(key, tableType, customTable, customTables)\n}\n\n// NewServerTablesWithCustomPatterns matches upstream server behavior: when probeable custom table\n// rotation is enabled, also accept the default table to avoid forcing clients to update in lockstep.\nfunc NewServerTablesWithCustomPatterns(key string, tableType string, customTable string, customTables []string) ([]*sudoku.Table, error) {\n\tpatterns, err := normalizeTablePatterns(tableType, customTable, customTables)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tasciiMode, err := sudoku.ParseASCIIMode(tableType)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif asciiMode.Uplink == \"entropy\" && len(patterns) > 0 && strings.TrimSpace(patterns[0]) != \"\" {\n\t\tpatterns = append([]string{\"\"}, patterns...)\n\t}\n\treturn NewTablesWithCustomPatterns(key, tableType, \"\", patterns)\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/tables_directional_test.go",
    "content": "package sudoku\n\nimport \"testing\"\n\nfunc TestDirectionalCustomTableRotationCollapse(t *testing.T) {\n\tpatterns := []string{\"xpxvvpvv\", \"vxpvxvvp\"}\n\n\tclientTables, err := NewClientTablesWithCustomPatterns(\"seed\", \"up_ascii_down_entropy\", \"\", patterns)\n\tif err != nil {\n\t\tt.Fatalf(\"client tables: %v\", err)\n\t}\n\tif len(clientTables) != 2 {\n\t\tt.Fatalf(\"expected ascii-uplink directional rotation to keep 2 tables, got %d\", len(clientTables))\n\t}\n\tif clientTables[0].Hint() == clientTables[1].Hint() {\n\t\tt.Fatalf(\"expected directional custom tables to carry distinct hints\")\n\t}\n\tif got, want := clientTables[0].EncodeTable[0][0], clientTables[1].EncodeTable[0][0]; got != want {\n\t\tt.Fatalf(\"expected directional ascii uplink tables to share the same probe layout, got %x want %x\", got, want)\n\t}\n\tif got, want := clientTables[0].OppositeDirection().EncodeTable[0][0], clientTables[1].OppositeDirection().EncodeTable[0][0]; got == want {\n\t\tt.Fatalf(\"expected directional downlink custom layouts to differ, both got %x\", got)\n\t}\n\n\tclientTables, err = NewClientTablesWithCustomPatterns(\"seed\", \"up_entropy_down_ascii\", \"\", patterns)\n\tif err != nil {\n\t\tt.Fatalf(\"client tables entropy uplink: %v\", err)\n\t}\n\tif len(clientTables) != 2 {\n\t\tt.Fatalf(\"expected entropy-uplink rotation to keep 2 tables, got %d\", len(clientTables))\n\t}\n\n\tserverTables, err := NewServerTablesWithCustomPatterns(\"seed\", \"up_ascii_down_entropy\", \"\", patterns)\n\tif err != nil {\n\t\tt.Fatalf(\"server tables: %v\", err)\n\t}\n\tif len(serverTables) != 2 {\n\t\tt.Fatalf(\"expected ascii-uplink server directional table set to keep 2 tables, got %d\", len(serverTables))\n\t}\n\tif clientTables, err = NewClientTablesWithCustomPatterns(\"seed\", \"up_ascii_down_entropy\", patterns[0], nil); err != nil {\n\t\tt.Fatalf(\"client table with single custom pattern: %v\", err)\n\t} else if got, want := serverTables[0].OppositeDirection().EncodeTable[0][0], clientTables[0].OppositeDirection().EncodeTable[0][0]; got != want {\n\t\tt.Fatalf(\"expected server directional downlink table to preserve custom pattern, got %x want %x\", got, want)\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/uot.go",
    "content": "package sudoku\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/netip\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/log\"\n)\n\nconst (\n\tmaxUoTPayload = 64 * 1024\n)\n\n// WriteDatagram sends a single UDP datagram frame over a reliable stream.\nfunc WriteDatagram(w io.Writer, addr string, payload []byte) error {\n\taddrBuf, err := EncodeAddress(addr)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"encode address: %w\", err)\n\t}\n\n\tif addrLen := len(addrBuf); addrLen == 0 || addrLen > maxUoTPayload {\n\t\treturn fmt.Errorf(\"address too long: %d\", len(addrBuf))\n\t}\n\tif payloadLen := len(payload); payloadLen > maxUoTPayload {\n\t\treturn fmt.Errorf(\"payload too large: %d\", payloadLen)\n\t}\n\n\tvar header [4]byte\n\tbinary.BigEndian.PutUint16(header[:2], uint16(len(addrBuf)))\n\tbinary.BigEndian.PutUint16(header[2:], uint16(len(payload)))\n\n\treturn writeAllChunks(w, header[:], addrBuf, payload)\n}\n\n// ReadDatagram parses a single UDP datagram frame from the reliable stream.\nfunc ReadDatagram(r io.Reader) (string, []byte, error) {\n\taddr, payloadLen, err := readDatagramHeaderAndAddress(r)\n\tif err != nil {\n\t\treturn \"\", nil, err\n\t}\n\tpayload := make([]byte, payloadLen)\n\tif _, err := io.ReadFull(r, payload); err != nil {\n\t\treturn \"\", nil, err\n\t}\n\n\treturn addr, payload, nil\n}\n\n// UoTPacketConn adapts a net.Conn with the Sudoku UoT framing to net.PacketConn.\ntype UoTPacketConn struct {\n\tconn    net.Conn\n\twriteMu sync.Mutex\n}\n\nfunc NewUoTPacketConn(conn net.Conn) *UoTPacketConn {\n\treturn &UoTPacketConn{conn: conn}\n}\n\nfunc (c *UoTPacketConn) ReadFrom(p []byte) (int, net.Addr, error) {\n\tfor {\n\t\taddrStr, payloadLen, err := readDatagramHeaderAndAddress(c.conn)\n\t\tif err != nil {\n\t\t\treturn 0, nil, err\n\t\t}\n\n\t\tudpAddr, err := parseDatagramUDPAddr(addrStr)\n\t\tif payloadLen > len(p) {\n\t\t\tif discardErr := discardBytes(c.conn, payloadLen); discardErr != nil {\n\t\t\t\treturn 0, nil, discardErr\n\t\t\t}\n\t\t\treturn 0, nil, io.ErrShortBuffer\n\t\t}\n\t\tif err != nil {\n\t\t\tif discardErr := discardBytes(c.conn, payloadLen); discardErr != nil {\n\t\t\t\treturn 0, nil, discardErr\n\t\t\t}\n\t\t\tlog.Debugln(\"[Sudoku][UoT] discard datagram with invalid address %s: %v\", addrStr, err)\n\t\t\tcontinue\n\t\t}\n\t\tif _, err := io.ReadFull(c.conn, p[:payloadLen]); err != nil {\n\t\t\treturn 0, nil, err\n\t\t}\n\t\treturn payloadLen, udpAddr, nil\n\t}\n}\n\nfunc (c *UoTPacketConn) WriteTo(p []byte, addr net.Addr) (int, error) {\n\tif addr == nil {\n\t\treturn 0, errors.New(\"address is nil\")\n\t}\n\tc.writeMu.Lock()\n\tdefer c.writeMu.Unlock()\n\tif err := WriteDatagram(c.conn, addr.String(), p); err != nil {\n\t\treturn 0, err\n\t}\n\treturn len(p), nil\n}\n\nfunc (c *UoTPacketConn) Close() error {\n\treturn c.conn.Close()\n}\n\nfunc (c *UoTPacketConn) LocalAddr() net.Addr {\n\treturn c.conn.LocalAddr()\n}\n\nfunc (c *UoTPacketConn) SetDeadline(t time.Time) error {\n\treturn c.conn.SetDeadline(t)\n}\n\nfunc (c *UoTPacketConn) SetReadDeadline(t time.Time) error {\n\treturn c.conn.SetReadDeadline(t)\n}\n\nfunc (c *UoTPacketConn) SetWriteDeadline(t time.Time) error {\n\treturn c.conn.SetWriteDeadline(t)\n}\n\nfunc readDatagramHeaderAndAddress(r io.Reader) (string, int, error) {\n\tvar header [4]byte\n\tif _, err := io.ReadFull(r, header[:]); err != nil {\n\t\treturn \"\", 0, err\n\t}\n\n\taddrLen := int(binary.BigEndian.Uint16(header[:2]))\n\tpayloadLen := int(binary.BigEndian.Uint16(header[2:]))\n\tif addrLen <= 0 || addrLen > maxUoTPayload {\n\t\treturn \"\", 0, fmt.Errorf(\"invalid address length: %d\", addrLen)\n\t}\n\tif payloadLen < 0 || payloadLen > maxUoTPayload {\n\t\treturn \"\", 0, fmt.Errorf(\"invalid payload length: %d\", payloadLen)\n\t}\n\n\taddrBuf := make([]byte, addrLen)\n\tif _, err := io.ReadFull(r, addrBuf); err != nil {\n\t\treturn \"\", 0, err\n\t}\n\n\taddr, err := DecodeAddress(bytes.NewReader(addrBuf))\n\tif err != nil {\n\t\treturn \"\", 0, fmt.Errorf(\"decode address: %w\", err)\n\t}\n\treturn addr, payloadLen, nil\n}\n\nfunc parseDatagramUDPAddr(addr string) (*net.UDPAddr, error) {\n\taddrPort, err := netip.ParseAddrPort(addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn net.UDPAddrFromAddrPort(netip.AddrPortFrom(addrPort.Addr().Unmap(), addrPort.Port())), nil\n}\n\nfunc discardBytes(r io.Reader, n int) error {\n\tif n <= 0 {\n\t\treturn nil\n\t}\n\t_, err := io.CopyN(io.Discard, r, int64(n))\n\treturn err\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/sudoku/write_chunks.go",
    "content": "package sudoku\n\nimport \"io\"\n\nfunc writeAllChunks(w io.Writer, chunks ...[]byte) error {\n\tfor _, chunk := range chunks {\n\t\tfor len(chunk) > 0 {\n\t\t\tn, err := w.Write(chunk)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif n == 0 {\n\t\t\t\treturn io.ErrShortWrite\n\t\t\t}\n\t\t\tchunk = chunk[n:]\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/trojan/trojan.go",
    "content": "package trojan\n\nimport (\n\t\"crypto/sha256\"\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\t\"sync\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/pool\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n)\n\nconst (\n\t// max packet length\n\tmaxLength = 8192\n)\n\nvar (\n\tDefaultALPN          = []string{\"h2\", \"http/1.1\"}\n\tDefaultWebsocketALPN = []string{\"http/1.1\"}\n\n\tcrlf = []byte{'\\r', '\\n'}\n)\n\ntype Command = byte\n\nconst (\n\tCommandTCP byte = 1\n\tCommandUDP byte = 3\n\tCommandMux byte = 0x7f\n\n\tKeyLength = 56\n)\n\nfunc WriteHeader(w io.Writer, hexPassword [KeyLength]byte, command Command, socks5Addr []byte) error {\n\tbuf := pool.GetBuffer()\n\tdefer pool.PutBuffer(buf)\n\n\tbuf.Write(hexPassword[:])\n\tbuf.Write(crlf)\n\n\tbuf.WriteByte(command)\n\tbuf.Write(socks5Addr)\n\tbuf.Write(crlf)\n\n\t_, err := w.Write(buf.Bytes())\n\treturn err\n}\n\nfunc writePacket(w io.Writer, socks5Addr, payload []byte) (int, error) {\n\tbuf := pool.GetBuffer()\n\tdefer pool.PutBuffer(buf)\n\n\tbuf.Write(socks5Addr)\n\tbinary.Write(buf, binary.BigEndian, uint16(len(payload)))\n\tbuf.Write(crlf)\n\tbuf.Write(payload)\n\n\treturn w.Write(buf.Bytes())\n}\n\nfunc WritePacket(w io.Writer, socks5Addr, payload []byte) (int, error) {\n\tif len(payload) <= maxLength {\n\t\treturn writePacket(w, socks5Addr, payload)\n\t}\n\n\toffset := 0\n\ttotal := len(payload)\n\tfor {\n\t\tcursor := offset + maxLength\n\t\tif cursor > total {\n\t\t\tcursor = total\n\t\t}\n\n\t\tn, err := writePacket(w, socks5Addr, payload[offset:cursor])\n\t\tif err != nil {\n\t\t\treturn offset + n, err\n\t\t}\n\n\t\toffset = cursor\n\t\tif offset == total {\n\t\t\tbreak\n\t\t}\n\t}\n\n\treturn total, nil\n}\n\nfunc ReadPacket(r io.Reader, payload []byte) (net.Addr, int, int, error) {\n\taddr, err := socks5.ReadAddr(r, payload)\n\tif err != nil {\n\t\treturn nil, 0, 0, errors.New(\"read addr error\")\n\t}\n\tuAddr := addr.UDPAddr()\n\tif uAddr == nil {\n\t\treturn nil, 0, 0, errors.New(\"parse addr error\")\n\t}\n\n\tif _, err = io.ReadFull(r, payload[:2]); err != nil {\n\t\treturn nil, 0, 0, errors.New(\"read length error\")\n\t}\n\n\ttotal := int(binary.BigEndian.Uint16(payload[:2]))\n\tif total > maxLength {\n\t\treturn nil, 0, 0, errors.New(\"packet invalid\")\n\t}\n\n\t// read crlf\n\tif _, err = io.ReadFull(r, payload[:2]); err != nil {\n\t\treturn nil, 0, 0, errors.New(\"read crlf error\")\n\t}\n\n\tlength := len(payload)\n\tif total < length {\n\t\tlength = total\n\t}\n\n\tif _, err = io.ReadFull(r, payload[:length]); err != nil {\n\t\treturn nil, 0, 0, errors.New(\"read packet error\")\n\t}\n\n\treturn uAddr, length, total - length, nil\n}\n\nvar _ N.EnhancePacketConn = (*PacketConn)(nil)\n\ntype PacketConn struct {\n\tnet.Conn\n\tremain int\n\trAddr  net.Addr\n\tmux    sync.Mutex\n}\n\nfunc (pc *PacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {\n\treturn WritePacket(pc, socks5.ParseAddrToSocksAddr(addr), b)\n}\n\nfunc (pc *PacketConn) ReadFrom(b []byte) (int, net.Addr, error) {\n\tpc.mux.Lock()\n\tdefer pc.mux.Unlock()\n\tif pc.remain != 0 {\n\t\tlength := len(b)\n\t\tif pc.remain < length {\n\t\t\tlength = pc.remain\n\t\t}\n\n\t\tn, err := pc.Conn.Read(b[:length])\n\t\tif err != nil {\n\t\t\treturn 0, nil, err\n\t\t}\n\n\t\tpc.remain -= n\n\t\taddr := pc.rAddr\n\t\tif pc.remain == 0 {\n\t\t\tpc.rAddr = nil\n\t\t}\n\n\t\treturn n, addr, nil\n\t}\n\n\taddr, n, remain, err := ReadPacket(pc.Conn, b)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\n\tif remain != 0 {\n\t\tpc.remain = remain\n\t\tpc.rAddr = addr\n\t}\n\n\treturn n, addr, nil\n}\n\nfunc (pc *PacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {\n\tpc.mux.Lock()\n\tdefer pc.mux.Unlock()\n\n\tdestination, err := socks5.ReadAddr0(pc.Conn)\n\tif err != nil {\n\t\treturn nil, nil, nil, err\n\t}\n\tudpAddr := destination.UDPAddr()\n\tif udpAddr == nil {\n\t\treturn nil, nil, nil, errors.New(\"parse addr error\")\n\t}\n\taddr = udpAddr\n\n\tdata = pool.Get(pool.UDPBufferSize)\n\tput = func() {\n\t\t_ = pool.Put(data)\n\t}\n\n\t_, err = io.ReadFull(pc.Conn, data[:2+2]) // u16be length + CR LF\n\tif err != nil {\n\t\tif put != nil {\n\t\t\tput()\n\t\t}\n\t\treturn nil, nil, nil, err\n\t}\n\tlength := binary.BigEndian.Uint16(data)\n\n\tif length > 0 {\n\t\tdata = data[:length]\n\t\t_, err = io.ReadFull(pc.Conn, data)\n\t\tif err != nil {\n\t\t\tif put != nil {\n\t\t\t\tput()\n\t\t\t}\n\t\t\treturn nil, nil, nil, err\n\t\t}\n\t} else {\n\t\tif put != nil {\n\t\t\tput()\n\t\t}\n\t\treturn nil, nil, addr, nil\n\t}\n\n\treturn\n}\n\nfunc NewPacketConn(conn net.Conn) *PacketConn {\n\treturn &PacketConn{Conn: conn}\n}\n\nfunc Key(password string) (key [56]byte) {\n\thash := sha256.Sum224([]byte(password))\n\thex.Encode(key[:], hash[:])\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/trusttunnel/client.go",
    "content": "package trusttunnel\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/url\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/httputils\"\n\t\"github.com/metacubex/mihomo/common/once\"\n\t\"github.com/metacubex/mihomo/component/dialer\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/transport/vmess\"\n\n\t\"github.com/metacubex/http\"\n\t\"golang.org/x/exp/slices\"\n)\n\ntype DialOptionsFunc func() []dialer.Option\n\ntype ClientOptions struct {\n\tDialer                C.Dialer\n\tDialOptions           DialOptionsFunc // for quic\n\tServer                string\n\tUsername              string\n\tPassword              string\n\tTLSConfig             *vmess.TLSConfig\n\tQUIC                  bool\n\tQUICCongestionControl string\n\tQUICCwnd              int\n\tQUICBBRProfile        string\n\tHealthCheck           bool\n\tMaxConnections        int\n\tMinStreams            int\n\tMaxStreams            int\n}\n\ntype Client struct {\n\tctx              context.Context\n\tdialer           C.Dialer\n\tdialOptions      DialOptionsFunc\n\tserver           string\n\tauth             string\n\troundTripper     http.RoundTripper\n\tstartOnce        sync.Once\n\thealthCheck      bool\n\thealthCheckTimer *time.Timer\n\tcount            atomic.Int64\n}\n\nfunc NewClient(ctx context.Context, options ClientOptions) (client *Client, err error) {\n\tclient = &Client{\n\t\tctx:         ctx,\n\t\tdialer:      options.Dialer,\n\t\tdialOptions: options.DialOptions,\n\t\tserver:      options.Server,\n\t\tauth:        buildAuth(options.Username, options.Password),\n\t}\n\tif options.QUIC {\n\t\tif len(options.TLSConfig.NextProtos) == 0 {\n\t\t\toptions.TLSConfig.NextProtos = []string{\"h3\"}\n\t\t} else if !slices.Contains(options.TLSConfig.NextProtos, \"h3\") {\n\t\t\treturn nil, errors.New(\"require alpn h3\")\n\t\t}\n\t\terr = client.quicRoundTripper(options.TLSConfig, options.QUICCongestionControl, options.QUICCwnd, options.QUICBBRProfile)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t} else {\n\t\tif len(options.TLSConfig.NextProtos) == 0 {\n\t\t\toptions.TLSConfig.NextProtos = []string{\"h2\"}\n\t\t} else if !slices.Contains(options.TLSConfig.NextProtos, \"h2\") {\n\t\t\treturn nil, errors.New(\"require alpn h2\")\n\t\t}\n\t\tclient.h2RoundTripper(options.TLSConfig)\n\t}\n\tif options.HealthCheck {\n\t\tclient.healthCheck = true\n\t}\n\treturn client, nil\n}\n\nfunc (c *Client) h2RoundTripper(tlsConfig *vmess.TLSConfig) {\n\t// use h2c mode to disallow the net/http fallback to http1.1\n\tprotocols := new(http.Protocols)\n\tprotocols.SetUnencryptedHTTP2(true)\n\tc.roundTripper = &http.Transport{\n\t\tDialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\t\tconn, err := c.dialer.DialContext(ctx, network, c.server)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\ttlsConn, err := vmess.StreamTLSConn(ctx, conn, tlsConfig)\n\t\t\tif err != nil {\n\t\t\t\t_ = conn.Close()\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn tlsConn, nil\n\t\t},\n\t\tProtocols:       protocols,\n\t\tIdleConnTimeout: DefaultSessionTimeout,\n\t}\n}\n\nfunc (c *Client) start() {\n\tif c.healthCheck {\n\t\tc.healthCheckTimer = time.NewTimer(DefaultHealthCheckTimeout)\n\t\tgo c.loopHealthCheck()\n\t}\n}\n\nfunc (c *Client) loopHealthCheck() {\n\tfor {\n\t\tselect {\n\t\tcase <-c.healthCheckTimer.C:\n\t\tcase <-c.ctx.Done():\n\t\t\tc.healthCheckTimer.Stop()\n\t\t\treturn\n\t\t}\n\t\tctx, cancel := context.WithTimeout(c.ctx, DefaultHealthCheckTimeout)\n\t\t_ = c.HealthCheck(ctx)\n\t\tcancel()\n\t}\n}\n\nfunc (c *Client) resetHealthCheckTimer() {\n\tif c.healthCheckTimer == nil {\n\t\treturn\n\t}\n\tc.healthCheckTimer.Reset(DefaultHealthCheckTimeout)\n}\n\nfunc (c *Client) roundTrip(request *http.Request, conn *httpConn) {\n\tc.startOnce.Do(c.start)\n\tpipeReader, pipeWriter := io.Pipe()\n\trequest.Body = pipeReader\n\t*conn = httpConn{\n\t\twriter:  pipeWriter,\n\t\tcreated: make(chan struct{}),\n\t}\n\tc.count.Add(1)\n\tconn.closeFn = once.OnceFunc(func() {\n\t\tc.count.Add(-1)\n\t})\n\tctx, cancel := context.WithCancel(c.ctx) // requestCtx must alive during conn not closed\n\tconn.cancelFn = cancel                   // cancel ctx when conn closed\n\tgo func() {\n\t\ttimeout := time.AfterFunc(C.DefaultTCPTimeout, cancel) // only cancel when RoundTrip timeout\n\t\tdefer timeout.Stop()                                   // RoundTrip already returned, stop the timer\n\t\trequest = request.WithContext(httputils.NewAddrContext(&conn.NetAddr, ctx))\n\t\tresponse, err := c.roundTripper.RoundTrip(request)\n\t\tif err != nil {\n\t\t\t_ = pipeWriter.CloseWithError(err)\n\t\t\t_ = pipeReader.CloseWithError(err)\n\t\t\tconn.setup(nil, err)\n\t\t} else if response.StatusCode != http.StatusOK {\n\t\t\t_ = response.Body.Close()\n\t\t\terr = fmt.Errorf(\"unexpected status code: %d\", response.StatusCode)\n\t\t\t_ = pipeWriter.CloseWithError(err)\n\t\t\t_ = pipeReader.CloseWithError(err)\n\t\t\tconn.setup(nil, err)\n\t\t} else {\n\t\t\tc.resetHealthCheckTimer()\n\t\t\tconn.setup(response.Body, nil)\n\t\t}\n\t}()\n}\n\nfunc (c *Client) newConnectRequest(host, userAgent string) *http.Request {\n\trequest := &http.Request{\n\t\tMethod: http.MethodConnect,\n\t\tURL: &url.URL{\n\t\t\tScheme: \"https\",\n\t\t\tHost:   c.server, // Use the proxy server authority so the pool keys reuse against the actual proxy endpoint.\n\t\t},\n\t\tHeader: make(http.Header),\n\t\tHost:   host, // Send the actual CONNECT target as the Host header (:authority).\n\t}\n\trequest.Header.Add(\"User-Agent\", userAgent)\n\trequest.Header.Add(\"Proxy-Authorization\", c.auth)\n\treturn request\n}\n\nfunc (c *Client) Dial(ctx context.Context, host string) (net.Conn, error) {\n\trequest := c.newConnectRequest(host, TCPUserAgent)\n\tconn := &tcpConn{}\n\tc.roundTrip(request, &conn.httpConn)\n\treturn conn, nil\n}\n\nfunc (c *Client) ListenPacket(ctx context.Context) (net.PacketConn, error) {\n\trequest := c.newConnectRequest(UDPMagicAddress, UDPUserAgent)\n\tconn := &clientPacketConn{}\n\tc.roundTrip(request, &conn.httpConn)\n\treturn conn, nil\n}\n\nfunc (c *Client) ListenICMP(ctx context.Context) (*IcmpConn, error) {\n\trequest := c.newConnectRequest(ICMPMagicAddress, ICMPUserAgent)\n\tconn := &IcmpConn{}\n\tc.roundTrip(request, &conn.httpConn)\n\treturn conn, nil\n}\n\nfunc (c *Client) Close() error {\n\thttputils.CloseTransport(c.roundTripper)\n\tif c.healthCheckTimer != nil {\n\t\tc.healthCheckTimer.Stop()\n\t}\n\treturn nil\n}\n\nfunc (c *Client) ResetConnections() {\n\thttputils.CloseTransport(c.roundTripper)\n\tc.resetHealthCheckTimer()\n}\n\nfunc (c *Client) HealthCheck(ctx context.Context) error {\n\tdefer c.resetHealthCheckTimer()\n\trequest := c.newConnectRequest(HealthCheckMagicAddress, HealthCheckUserAgent)\n\tresponse, err := c.roundTripper.RoundTrip(request.WithContext(ctx))\n\tif err != nil {\n\t\treturn err\n\t}\n\tdefer response.Body.Close()\n\tif response.StatusCode != http.StatusOK {\n\t\treturn fmt.Errorf(\"unexpected status code: %d\", response.StatusCode)\n\t}\n\treturn nil\n}\n\ntype PoolClient struct {\n\tmutex          sync.Mutex\n\tmaxConnections int\n\tminStreams     int\n\tmaxStreams     int\n\tctx            context.Context\n\toptions        ClientOptions\n\tclients        []*Client\n}\n\nfunc NewPoolClient(ctx context.Context, options ClientOptions) (*PoolClient, error) {\n\tmaxConnections := options.MaxConnections\n\tminStreams := options.MinStreams\n\tmaxStreams := options.MaxStreams\n\tif maxConnections == 0 && minStreams == 0 && maxStreams == 0 {\n\t\tmaxConnections = 8\n\t\tminStreams = 5\n\t}\n\tclient, err := NewClient(ctx, options) // reserve one client and verify the configuration\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &PoolClient{\n\t\tmaxConnections: maxConnections,\n\t\tminStreams:     minStreams,\n\t\tmaxStreams:     maxStreams,\n\t\tctx:            ctx,\n\t\toptions:        options,\n\t\tclients:        []*Client{client},\n\t}, nil\n}\n\nfunc (c *PoolClient) Dial(ctx context.Context, host string) (net.Conn, error) {\n\ttransport, err := c.getClient()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn transport.Dial(ctx, host)\n}\n\nfunc (c *PoolClient) ListenPacket(ctx context.Context) (net.PacketConn, error) {\n\ttransport, err := c.getClient()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn transport.ListenPacket(ctx)\n}\n\nfunc (c *PoolClient) ListenICMP(ctx context.Context) (*IcmpConn, error) {\n\ttransport, err := c.getClient()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn transport.ListenICMP(ctx)\n}\n\nfunc (c *PoolClient) Close() error {\n\tc.mutex.Lock()\n\tdefer c.mutex.Unlock()\n\tvar errs []error\n\tfor _, t := range c.clients {\n\t\tif err := t.Close(); err != nil {\n\t\t\terrs = append(errs, err)\n\t\t}\n\t}\n\tc.clients = nil\n\treturn errors.Join(errs...)\n}\n\nfunc (c *PoolClient) getClient() (*Client, error) {\n\tc.mutex.Lock()\n\tdefer c.mutex.Unlock()\n\tvar transport *Client\n\tfor _, t := range c.clients {\n\t\tif transport == nil || t.count.Load() < transport.count.Load() {\n\t\t\ttransport = t\n\t\t}\n\t}\n\tif transport == nil {\n\t\treturn c.newTransportLocked()\n\t}\n\tnumStreams := int(transport.count.Load())\n\tif numStreams == 0 {\n\t\treturn transport, nil\n\t}\n\tif c.maxConnections > 0 {\n\t\tif len(c.clients) >= c.maxConnections || numStreams < c.minStreams {\n\t\t\treturn transport, nil\n\t\t}\n\t} else {\n\t\tif c.maxStreams > 0 && numStreams < c.maxStreams {\n\t\t\treturn transport, nil\n\t\t}\n\t}\n\treturn c.newTransportLocked()\n}\n\nfunc (c *PoolClient) newTransportLocked() (*Client, error) {\n\ttransport, err := NewClient(c.ctx, c.options)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tc.clients = append(c.clients, transport)\n\treturn transport, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/trusttunnel/doc.go",
    "content": "// Package trusttunnel copy and modify from:\n// https://github.com/xchacha20-poly1305/sing-trusttunnel/tree/v0.1.1\n// adopt for mihomo\npackage trusttunnel\n"
  },
  {
    "path": "core/Clash.Meta/transport/trusttunnel/icmp.go",
    "content": "package trusttunnel\n\nimport (\n\t\"encoding/binary\"\n\t\"net/netip\"\n\n\t\"github.com/metacubex/mihomo/common/buf\"\n)\n\ntype IcmpConn struct {\n\thttpConn\n}\n\nfunc (i *IcmpConn) WritePing(id uint16, destination netip.Addr, sequenceNumber uint16, ttl uint8, size uint16) error {\n\trequest := buf.NewSize(2 + 16 + 2 + 1 + 2)\n\tdefer request.Release()\n\tbuf.Must(binary.Write(request, binary.BigEndian, id))\n\tdestinationAddress := buildPaddingIP(destination)\n\tbuf.Must1(request.Write(destinationAddress[:]))\n\tbuf.Must(binary.Write(request, binary.BigEndian, sequenceNumber))\n\tbuf.Must(binary.Write(request, binary.BigEndian, ttl))\n\tbuf.Must(binary.Write(request, binary.BigEndian, size))\n\treturn buf.Error(i.writeFlush(request.Bytes()))\n}\n\nfunc (i *IcmpConn) ReadPing() (id uint16, sourceAddress netip.Addr, icmpType uint8, code uint8, sequenceNumber uint16, err error) {\n\terr = i.waitCreated()\n\tif err != nil {\n\t\treturn\n\t}\n\tresponse := buf.NewSize(2 + 16 + 1 + 1 + 2)\n\tdefer response.Release()\n\t_, err = response.ReadFullFrom(i.body, response.Cap())\n\tif err != nil {\n\t\treturn\n\t}\n\tbuf.Must(binary.Read(response, binary.BigEndian, &id))\n\tvar sourceAddressBuffer [16]byte\n\tbuf.Must1(response.Read(sourceAddressBuffer[:]))\n\tsourceAddress = parse16BytesIP(sourceAddressBuffer)\n\tbuf.Must(binary.Read(response, binary.BigEndian, &icmpType))\n\tbuf.Must(binary.Read(response, binary.BigEndian, &code))\n\tbuf.Must(binary.Read(response, binary.BigEndian, &sequenceNumber))\n\treturn\n}\n\nfunc (i *IcmpConn) Close() error {\n\treturn i.httpConn.Close()\n}\n\nfunc (i *IcmpConn) ReadPingRequest() (id uint16, destination netip.Addr, sequenceNumber uint16, ttl uint8, size uint16, err error) {\n\terr = i.waitCreated()\n\tif err != nil {\n\t\treturn\n\t}\n\trequest := buf.NewSize(2 + 16 + 2 + 1 + 2)\n\tdefer request.Release()\n\t_, err = request.ReadFullFrom(i.body, request.Cap())\n\tif err != nil {\n\t\treturn\n\t}\n\tbuf.Must(binary.Read(request, binary.BigEndian, &id))\n\tvar destinationAddressBuffer [16]byte\n\tbuf.Must1(request.Read(destinationAddressBuffer[:]))\n\tdestination = parse16BytesIP(destinationAddressBuffer)\n\tbuf.Must(binary.Read(request, binary.BigEndian, &sequenceNumber))\n\tbuf.Must(binary.Read(request, binary.BigEndian, &ttl))\n\tbuf.Must(binary.Read(request, binary.BigEndian, &size))\n\treturn\n}\n\nfunc (i *IcmpConn) WritePingResponse(id uint16, sourceAddress netip.Addr, icmpType uint8, code uint8, sequenceNumber uint16) error {\n\tresponse := buf.NewSize(2 + 16 + 1 + 1 + 2)\n\tdefer response.Release()\n\tbuf.Must(binary.Write(response, binary.BigEndian, id))\n\tsourceAddressBytes := buildPaddingIP(sourceAddress)\n\tbuf.Must1(response.Write(sourceAddressBytes[:]))\n\tbuf.Must(binary.Write(response, binary.BigEndian, icmpType))\n\tbuf.Must(binary.Write(response, binary.BigEndian, code))\n\tbuf.Must(binary.Write(response, binary.BigEndian, sequenceNumber))\n\treturn buf.Error(i.writeFlush(response.Bytes()))\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/trusttunnel/packet.go",
    "content": "package trusttunnel\n\nimport (\n\t\"encoding/binary\"\n\t\"math\"\n\t\"net\"\n\n\t\"github.com/metacubex/sing/common\"\n\t\"github.com/metacubex/sing/common/buf\"\n\tE \"github.com/metacubex/sing/common/exceptions\"\n\tM \"github.com/metacubex/sing/common/metadata\"\n\tN \"github.com/metacubex/sing/common/network\"\n\t\"github.com/metacubex/sing/common/rw\"\n)\n\ntype packetConn struct {\n\thttpConn\n\treadWaitOptions N.ReadWaitOptions\n}\n\nfunc (c *packetConn) InitializeReadWaiter(options N.ReadWaitOptions) (needCopy bool) {\n\tc.readWaitOptions = options\n\treturn false\n}\n\nvar (\n\t_ N.NetPacketConn    = (*clientPacketConn)(nil)\n\t_ N.FrontHeadroom    = (*clientPacketConn)(nil)\n\t_ N.PacketReadWaiter = (*clientPacketConn)(nil)\n)\n\ntype clientPacketConn struct {\n\tpacketConn\n}\n\nfunc (u *clientPacketConn) FrontHeadroom() int {\n\treturn 4 + 16 + 2 + 16 + 2 + 1 + math.MaxUint8\n}\n\nfunc (u *clientPacketConn) WaitReadPacket() (buffer *buf.Buffer, destination M.Socksaddr, err error) {\n\tbuffer = u.readWaitOptions.NewPacketBuffer()\n\tdestination, err = u.ReadPacket(buffer)\n\tif err != nil {\n\t\tbuffer.Release()\n\t\treturn nil, M.Socksaddr{}, err\n\t}\n\tu.readWaitOptions.PostReturn(buffer)\n\treturn buffer, destination, nil\n}\n\nfunc (u *clientPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {\n\terr = u.waitCreated()\n\tif err != nil {\n\t\treturn M.Socksaddr{}, err\n\t}\n\treturn u.readPacketFromServer(buffer)\n}\n\nfunc (u *clientPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {\n\tbuffer := buf.With(p)\n\tdestination, err := u.ReadPacket(buffer)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\treturn buffer.Len(), destination.UDPAddr(), nil\n}\n\nfunc (u *clientPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {\n\treturn u.writePacketToServer(buffer, destination)\n}\n\nfunc (u *clientPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {\n\terr = u.WritePacket(buf.As(p), M.SocksaddrFromNet(addr))\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn len(p), nil\n}\n\nfunc (u *clientPacketConn) readPacketFromServer(buffer *buf.Buffer) (destination M.Socksaddr, err error) {\n\theader := buf.NewSize(4 + 16 + 2 + 16 + 2)\n\tdefer header.Release()\n\t_, err = header.ReadFullFrom(u.body, header.Cap())\n\tif err != nil {\n\t\treturn\n\t}\n\tvar length uint32\n\tcommon.Must(binary.Read(header, binary.BigEndian, &length))\n\tvar sourceAddressBuffer [16]byte\n\tcommon.Must1(header.Read(sourceAddressBuffer[:]))\n\tdestination.Addr = parse16BytesIP(sourceAddressBuffer)\n\tcommon.Must(binary.Read(header, binary.BigEndian, &destination.Port))\n\tcommon.Must(rw.SkipN(header, 16+2)) // To local address:port\n\tpayloadLen := int(length) - (16 + 2 + 16 + 2)\n\tif payloadLen < 0 {\n\t\treturn M.Socksaddr{}, E.New(\"invalid udp length: \", length)\n\t}\n\t_, err = buffer.ReadFullFrom(u.body, payloadLen)\n\treturn\n}\n\nfunc (u *clientPacketConn) writePacketToServer(buffer *buf.Buffer, source M.Socksaddr) error {\n\tdefer buffer.Release()\n\tif !source.IsIP() {\n\t\treturn E.New(\"only support IP\")\n\t}\n\tappName := AppName\n\tif len(appName) > math.MaxUint8 {\n\t\tappName = appName[:math.MaxUint8]\n\t}\n\tpayloadLen := buffer.Len()\n\theaderLen := 4 + 16 + 2 + 16 + 2 + 1 + len(appName)\n\tlengthField := uint32(16 + 2 + 16 + 2 + 1 + len(appName) + payloadLen)\n\tdestinationAddress := buildPaddingIP(source.Addr)\n\n\tvar (\n\t\theader         *buf.Buffer\n\t\theaderInBuffer bool\n\t)\n\tif buffer.Start() >= headerLen {\n\t\theaderBytes := buffer.ExtendHeader(headerLen)\n\t\theader = buf.With(headerBytes)\n\t\theaderInBuffer = true\n\t} else {\n\t\theader = buf.NewSize(headerLen)\n\t\tdefer header.Release()\n\t}\n\tcommon.Must(binary.Write(header, binary.BigEndian, lengthField))\n\tcommon.Must(header.WriteZeroN(16 + 2)) // Source address:port (unknown)\n\tcommon.Must1(header.Write(destinationAddress[:]))\n\tcommon.Must(binary.Write(header, binary.BigEndian, source.Port))\n\tcommon.Must(binary.Write(header, binary.BigEndian, uint8(len(appName))))\n\tcommon.Must1(header.WriteString(appName))\n\tif !headerInBuffer {\n\t\t_, err := u.writer.Write(header.Bytes())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\t_, err := u.writer.Write(buffer.Bytes())\n\tif err != nil {\n\t\treturn err\n\t}\n\tif u.flusher != nil {\n\t\tu.flusher.Flush()\n\t}\n\treturn nil\n}\n\nvar (\n\t_ N.NetPacketConn    = (*serverPacketConn)(nil)\n\t_ N.FrontHeadroom    = (*serverPacketConn)(nil)\n\t_ N.PacketReadWaiter = (*serverPacketConn)(nil)\n)\n\ntype serverPacketConn struct {\n\tpacketConn\n}\n\nfunc (u *serverPacketConn) FrontHeadroom() int {\n\treturn 4 + 16 + 2 + 16 + 2\n}\n\nfunc (u *serverPacketConn) WaitReadPacket() (buffer *buf.Buffer, destination M.Socksaddr, err error) {\n\tbuffer = u.readWaitOptions.NewPacketBuffer()\n\tdestination, err = u.ReadPacket(buffer)\n\tif err != nil {\n\t\tbuffer.Release()\n\t\treturn nil, M.Socksaddr{}, err\n\t}\n\tu.readWaitOptions.PostReturn(buffer)\n\treturn buffer, destination, nil\n}\n\nfunc (u *serverPacketConn) ReadPacket(buffer *buf.Buffer) (destination M.Socksaddr, err error) {\n\terr = u.waitCreated()\n\tif err != nil {\n\t\treturn M.Socksaddr{}, err\n\t}\n\treturn u.readPacketFromClient(buffer)\n}\n\nfunc (u *serverPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {\n\tbuffer := buf.With(p)\n\tdestination, err := u.ReadPacket(buffer)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\treturn buffer.Len(), destination.UDPAddr(), nil\n}\n\nfunc (u *serverPacketConn) WritePacket(buffer *buf.Buffer, destination M.Socksaddr) error {\n\treturn u.writePacketToClient(buffer, destination)\n}\n\nfunc (u *serverPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {\n\terr = u.WritePacket(buf.As(p), M.SocksaddrFromNet(addr))\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\treturn len(p), nil\n}\n\nfunc (u *serverPacketConn) readPacketFromClient(buffer *buf.Buffer) (destination M.Socksaddr, err error) {\n\theader := buf.NewSize(4 + 16 + 2 + 16 + 2 + 1)\n\tdefer header.Release()\n\t_, err = header.ReadFullFrom(u.body, header.Cap())\n\tif err != nil {\n\t\treturn\n\t}\n\tvar length uint32\n\tcommon.Must(binary.Read(header, binary.BigEndian, &length))\n\tvar sourceAddressBuffer [16]byte\n\tcommon.Must1(header.Read(sourceAddressBuffer[:]))\n\tvar sourcePort uint16\n\tcommon.Must(binary.Read(header, binary.BigEndian, &sourcePort))\n\t_ = sourcePort\n\tvar destinationAddressBuffer [16]byte\n\tcommon.Must1(header.Read(destinationAddressBuffer[:]))\n\tdestination.Addr = parse16BytesIP(destinationAddressBuffer)\n\tcommon.Must(binary.Read(header, binary.BigEndian, &destination.Port))\n\tvar appNameLen uint8\n\tcommon.Must(binary.Read(header, binary.BigEndian, &appNameLen))\n\tif appNameLen > 0 {\n\t\terr = rw.SkipN(u.body, int(appNameLen))\n\t\tif err != nil {\n\t\t\treturn M.Socksaddr{}, err\n\t\t}\n\t}\n\tpayloadLen := int(length) - (16 + 2 + 16 + 2 + 1 + int(appNameLen))\n\tif payloadLen < 0 {\n\t\treturn M.Socksaddr{}, E.New(\"invalid udp length: \", length)\n\t}\n\t_, err = buffer.ReadFullFrom(u.body, payloadLen)\n\treturn\n}\n\nfunc (u *serverPacketConn) writePacketToClient(buffer *buf.Buffer, source M.Socksaddr) error {\n\tdefer buffer.Release()\n\tif !source.IsIP() {\n\t\treturn E.New(\"only support IP\")\n\t}\n\tpayloadLen := buffer.Len()\n\theaderLen := 4 + 16 + 2 + 16 + 2\n\tlengthField := uint32(16 + 2 + 16 + 2 + payloadLen)\n\tsourceAddress := buildPaddingIP(source.Addr)\n\tvar destinationAddress [16]byte\n\tvar destinationPort uint16\n\tvar (\n\t\theader         *buf.Buffer\n\t\theaderInBuffer bool\n\t)\n\tif buffer.Start() >= headerLen {\n\t\theaderBytes := buffer.ExtendHeader(headerLen)\n\t\theader = buf.With(headerBytes)\n\t\theaderInBuffer = true\n\t} else {\n\t\theader = buf.NewSize(headerLen)\n\t\tdefer header.Release()\n\t}\n\tcommon.Must(binary.Write(header, binary.BigEndian, lengthField))\n\tcommon.Must1(header.Write(sourceAddress[:]))\n\tcommon.Must(binary.Write(header, binary.BigEndian, source.Port))\n\tcommon.Must1(header.Write(destinationAddress[:]))\n\tcommon.Must(binary.Write(header, binary.BigEndian, destinationPort))\n\tif !headerInBuffer {\n\t\t_, err := u.writer.Write(header.Bytes())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\t_, err := u.writer.Write(buffer.Bytes())\n\tif err != nil {\n\t\treturn err\n\t}\n\tif u.flusher != nil {\n\t\tu.flusher.Flush()\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/trusttunnel/protocol.go",
    "content": "package trusttunnel\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\t\"net/http\"\n\t\"net/netip\"\n\t\"runtime\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/httputils\"\n\tC \"github.com/metacubex/mihomo/constant\"\n)\n\nconst (\n\tUDPMagicAddress         = \"_udp2\"\n\tICMPMagicAddress        = \"_icmp\"\n\tHealthCheckMagicAddress = \"_check\"\n\n\tDefaultQuicStreamReceiveWindow = 131072 // Chrome's default\n\tDefaultConnectionTimeout       = 30 * time.Second\n\tDefaultHealthCheckTimeout      = 7 * time.Second\n\tDefaultQuicMaxIdleTimeout      = 2 * (DefaultConnectionTimeout + DefaultHealthCheckTimeout)\n\tDefaultSessionTimeout          = 30 * time.Second\n)\n\nvar (\n\tAppName = C.Name\n\tVersion = C.Version\n\n\t// TCPUserAgent is user-agent for TCP connections.\n\t// Format: <platform> <app_name>\n\tTCPUserAgent = runtime.GOOS + \" \" + AppName + \"/\" + Version\n\n\t// UDPUserAgent is user-agent for UDP multiplexinh.\n\t// Format: <platform> _udp2\n\tUDPUserAgent = runtime.GOOS + \" \" + UDPMagicAddress\n\n\t// ICMPUserAgent is user-agent for ICMP multiplexinh.\n\t// Format: <platform> _icmp\n\tICMPUserAgent = runtime.GOOS + \" \" + ICMPMagicAddress\n\n\tHealthCheckUserAgent = runtime.GOOS\n)\n\nfunc buildAuth(username string, password string) string {\n\treturn \"Basic \" + base64.StdEncoding.EncodeToString([]byte(username+\":\"+password))\n}\n\n// parseBasicAuth parses an HTTP Basic Authentication strinh.\n// \"Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ==\" returns (\"Aladdin\", \"open sesame\", true).\nfunc parseBasicAuth(auth string) (username, password string, ok bool) {\n\tconst prefix = \"Basic \"\n\t// Case insensitive prefix match. See Issue 22736.\n\tif len(auth) < len(prefix) || !strings.EqualFold(auth[:len(prefix)], prefix) {\n\t\treturn \"\", \"\", false\n\t}\n\tc, err := base64.StdEncoding.DecodeString(auth[len(prefix):])\n\tif err != nil {\n\t\treturn \"\", \"\", false\n\t}\n\tcs := string(c)\n\tusername, password, ok = strings.Cut(cs, \":\")\n\tif !ok {\n\t\treturn \"\", \"\", false\n\t}\n\treturn username, password, true\n}\n\nfunc parse16BytesIP(buffer [16]byte) netip.Addr {\n\tvar zeroPrefix [12]byte\n\tisIPv4 := bytes.HasPrefix(buffer[:], zeroPrefix[:])\n\t// Special: check ::1\n\tisIPv4 = isIPv4 && !(buffer[12] == 0 && buffer[13] == 0 && buffer[14] == 0 && buffer[15] == 1)\n\tif isIPv4 {\n\t\treturn netip.AddrFrom4([4]byte(buffer[12:16]))\n\t}\n\treturn netip.AddrFrom16(buffer)\n}\n\nfunc buildPaddingIP(addr netip.Addr) (buffer [16]byte) {\n\tif addr.Is6() {\n\t\treturn addr.As16()\n\t}\n\tipv4 := addr.As4()\n\tcopy(buffer[12:16], ipv4[:])\n\treturn buffer\n}\n\ntype httpConn struct {\n\twriter    io.Writer\n\tflusher   http.Flusher\n\tbody      io.ReadCloser\n\tsetupOnce sync.Once\n\tcreated   chan struct{}\n\tcreateErr error\n\tcancelFn  func()\n\tcloseFn   func()\n\thttputils.NetAddr\n\n\t// deadlines\n\tdeadline *time.Timer\n}\n\nfunc (h *httpConn) setup(body io.ReadCloser, err error) {\n\th.setupOnce.Do(func() {\n\t\th.body = body\n\t\th.createErr = err\n\t\tclose(h.created)\n\t})\n\tif h.createErr != nil && body != nil { // conn already closed before setup\n\t\t_ = body.Close()\n\t}\n}\n\nfunc (h *httpConn) waitCreated() error {\n\t<-h.created\n\tif h.body != nil {\n\t\treturn nil\n\t}\n\treturn h.createErr\n}\n\nfunc (h *httpConn) Close() error {\n\tvar errorArr []error\n\th.setup(nil, net.ErrClosed)\n\tif closer, ok := h.writer.(io.Closer); ok {\n\t\terrorArr = append(errorArr, closer.Close())\n\t}\n\tif h.body != nil {\n\t\terrorArr = append(errorArr, h.body.Close())\n\t}\n\tif h.cancelFn != nil {\n\t\th.cancelFn()\n\t}\n\tif h.closeFn != nil {\n\t\th.closeFn()\n\t}\n\treturn errors.Join(errorArr...)\n}\n\nfunc (h *httpConn) writeFlush(p []byte) (n int, err error) {\n\tn, err = h.writer.Write(p)\n\tif h.flusher != nil {\n\t\th.flusher.Flush()\n\t}\n\treturn n, err\n}\n\nfunc (h *httpConn) SetReadDeadline(t time.Time) error  { return h.SetDeadline(t) }\nfunc (h *httpConn) SetWriteDeadline(t time.Time) error { return h.SetDeadline(t) }\n\nfunc (h *httpConn) SetDeadline(t time.Time) error {\n\tif t.IsZero() {\n\t\tif h.deadline != nil {\n\t\t\th.deadline.Stop()\n\t\t\th.deadline = nil\n\t\t}\n\t\treturn nil\n\t}\n\td := time.Until(t)\n\tif h.deadline != nil {\n\t\th.deadline.Reset(d)\n\t\treturn nil\n\t}\n\th.deadline = time.AfterFunc(d, func() {\n\t\th.Close()\n\t})\n\treturn nil\n}\n\nvar _ net.Conn = (*tcpConn)(nil)\n\ntype tcpConn struct {\n\thttpConn\n}\n\nfunc (t *tcpConn) Read(b []byte) (n int, err error) {\n\terr = t.waitCreated()\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tn, err = t.body.Read(b)\n\treturn\n}\n\nfunc (t *tcpConn) Write(b []byte) (int, error) {\n\treturn t.writeFlush(b)\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/trusttunnel/quic.go",
    "content": "package trusttunnel\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net\"\n\t\"runtime\"\n\n\t\"github.com/metacubex/mihomo/transport/tuic/common\"\n\t\"github.com/metacubex/mihomo/transport/vmess\"\n\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/quic-go\"\n\t\"github.com/metacubex/quic-go/http3\"\n\t\"github.com/metacubex/tls\"\n)\n\nfunc (c *Client) quicRoundTripper(tlsConfig *vmess.TLSConfig, congestionControlName string, cwnd int, bbrProfile string) error {\n\tstdConfig, err := tlsConfig.ToStdConfig()\n\tif err != nil {\n\t\treturn err\n\t}\n\tc.roundTripper = &http3.Transport{\n\t\tTLSClientConfig: stdConfig,\n\t\tQUICConfig: &quic.Config{\n\t\t\tVersions:                   []quic.Version{quic.Version1},\n\t\t\tMaxIdleTimeout:             DefaultQuicMaxIdleTimeout,\n\t\t\tInitialStreamReceiveWindow: DefaultQuicStreamReceiveWindow,\n\t\t\tDisablePathMTUDiscovery:    !(runtime.GOOS == \"windows\" || runtime.GOOS == \"linux\" || runtime.GOOS == \"android\" || runtime.GOOS == \"darwin\"),\n\t\t\tAllow0RTT:                  false,\n\t\t},\n\t\tDial: func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (*quic.Conn, error) {\n\t\t\terr := tlsConfig.ECH.ClientHandle(ctx, tlsCfg)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\t_, quicConn, err := common.DialQuic(ctx, addr, c.dialOptions(), c.dialer, tlsCfg, cfg, true)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tcommon.SetCongestionController(quicConn, congestionControlName, cwnd, bbrProfile)\n\t\t\treturn quicConn, nil\n\t\t},\n\t}\n\treturn nil\n}\n\nfunc (s *Service) configHTTP3Server(tlsConfig *tls.Config, udpConn net.PacketConn) error {\n\ttlsConfig = http3.ConfigureTLSConfig(tlsConfig)\n\tquicListener, err := quic.ListenEarly(udpConn, tlsConfig, &quic.Config{\n\t\tVersions:           []quic.Version{quic.Version1},\n\t\tMaxIdleTimeout:     DefaultQuicMaxIdleTimeout,\n\t\tMaxIncomingStreams: 1 << 60,\n\t\tAllow0RTT:          true,\n\t})\n\tif err != nil {\n\t\treturn err\n\t}\n\th3Server := &http3.Server{\n\t\tHandler:     s,\n\t\tIdleTimeout: DefaultSessionTimeout,\n\t\tConnContext: func(ctx context.Context, conn *quic.Conn) context.Context {\n\t\t\tcommon.SetCongestionController(conn, s.quicCongestionControl, s.quicCwnd, s.quicBBRProfile)\n\t\t\treturn ctx\n\t\t},\n\t}\n\ts.h3Server = h3Server\n\ts.udpConn = udpConn\n\tgo func() {\n\t\tsErr := h3Server.ServeListener(quicListener)\n\t\tif sErr != nil && !errors.Is(sErr, http.ErrServerClosed) {\n\t\t\ts.logger.ErrorContext(s.ctx, \"HTTP3 server close: \", sErr)\n\t\t}\n\t}()\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/trusttunnel/service.go",
    "content": "package trusttunnel\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/httputils\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/quic-go/http3\"\n\t\"github.com/metacubex/sing/common\"\n\t\"github.com/metacubex/sing/common/auth\"\n\t\"github.com/metacubex/sing/common/buf\"\n\t\"github.com/metacubex/sing/common/bufio\"\n\tE \"github.com/metacubex/sing/common/exceptions\"\n\t\"github.com/metacubex/sing/common/logger\"\n\tM \"github.com/metacubex/sing/common/metadata\"\n\t\"github.com/metacubex/sing/common/network\"\n\t\"github.com/metacubex/tls\"\n)\n\ntype Handler interface {\n\tnetwork.TCPConnectionHandler\n\tnetwork.UDPConnectionHandler\n}\n\ntype ICMPHandler interface {\n\tNewICMPConnection(ctx context.Context, conn *IcmpConn)\n}\n\ntype ServiceOptions struct {\n\tCtx                   context.Context\n\tLogger                logger.ContextLogger\n\tHandler               Handler\n\tICMPHandler           ICMPHandler\n\tQUICCongestionControl string\n\tQUICCwnd              int\n\tQUICBBRProfile        string\n}\n\ntype Service struct {\n\tctx                   context.Context\n\tlogger                logger.ContextLogger\n\tusers                 map[string]string\n\thandler               Handler\n\ticmpHandler           ICMPHandler\n\tquicCongestionControl string\n\tquicCwnd              int\n\tquicBBRProfile        string\n\thttpServer            *http.Server\n\th3Server              *http3.Server\n\ttcpListener           net.Listener\n\ttlsListener           net.Listener\n\tudpConn               net.PacketConn\n}\n\nfunc NewService(options ServiceOptions) *Service {\n\treturn &Service{\n\t\tctx:                   options.Ctx,\n\t\tlogger:                options.Logger,\n\t\thandler:               options.Handler,\n\t\ticmpHandler:           options.ICMPHandler,\n\t\tquicCongestionControl: options.QUICCongestionControl,\n\t\tquicCwnd:              options.QUICCwnd,\n\t\tquicBBRProfile:        options.QUICBBRProfile,\n\t}\n}\n\nfunc (s *Service) Start(tcpListener net.Listener, udpConn net.PacketConn, tlsConfig *tls.Config) error {\n\tif tcpListener != nil {\n\t\tprotocols := new(http.Protocols)\n\t\tprotocols.SetHTTP1(true)\n\t\tprotocols.SetHTTP2(true)\n\t\tprotocols.SetUnencryptedHTTP2(true)\n\t\ts.httpServer = &http.Server{\n\t\t\tHandler:     s,\n\t\t\tIdleTimeout: DefaultSessionTimeout,\n\t\t\tBaseContext: func(net.Listener) context.Context {\n\t\t\t\treturn s.ctx\n\t\t\t},\n\t\t\tProtocols: protocols,\n\t\t}\n\t\tlistener := tcpListener\n\t\ts.tcpListener = tcpListener\n\t\tif tlsConfig != nil {\n\t\t\tlistener = tls.NewListener(listener, tlsConfig)\n\t\t\ts.tlsListener = listener\n\t\t}\n\t\tgo func() {\n\t\t\tsErr := s.httpServer.Serve(listener)\n\t\t\tif sErr != nil && !errors.Is(sErr, http.ErrServerClosed) {\n\t\t\t\ts.logger.ErrorContext(s.ctx, \"HTTP server close: \", sErr)\n\t\t\t}\n\t\t}()\n\t}\n\tif udpConn != nil {\n\t\terr := s.configHTTP3Server(tlsConfig, udpConn)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc (s *Service) UpdateUsers(users map[string]string) {\n\ts.users = users\n}\n\nfunc (s *Service) Close() error {\n\tvar shutdownErr error\n\tif s.httpServer != nil {\n\t\tconst shutdownTimeout = 5 * time.Second\n\t\tctx, cancel := context.WithTimeout(s.ctx, shutdownTimeout)\n\t\tshutdownErr = s.httpServer.Shutdown(ctx)\n\t\tcancel()\n\t\tif errors.Is(shutdownErr, http.ErrServerClosed) {\n\t\t\tshutdownErr = nil\n\t\t}\n\t}\n\tcloseErr := common.Close(\n\t\tcommon.PtrOrNil(s.httpServer),\n\t\ts.tlsListener,\n\t\ts.tcpListener,\n\t\tcommon.PtrOrNil(s.h3Server),\n\t\ts.udpConn,\n\t)\n\treturn E.Errors(shutdownErr, closeErr)\n}\n\nfunc (s *Service) ServeHTTP(writer http.ResponseWriter, request *http.Request) {\n\tauthorization := request.Header.Get(\"Proxy-Authorization\")\n\tusername, loaded := s.verify(authorization)\n\tif !loaded {\n\t\twriter.WriteHeader(http.StatusProxyAuthRequired)\n\t\ts.badRequest(request.Context(), request, E.New(\"authorization failed\"))\n\t\treturn\n\t}\n\tif request.Method != http.MethodConnect {\n\t\twriter.WriteHeader(http.StatusMethodNotAllowed)\n\t\ts.badRequest(request.Context(), request, E.New(\"unexpected HTTP method \", request.Method))\n\t\treturn\n\t}\n\tctx := request.Context()\n\tctx = auth.ContextWithUser(ctx, username)\n\ts.logger.DebugContext(ctx, \"[\", username, \"] \", \"request from \", request.RemoteAddr)\n\ts.logger.DebugContext(ctx, \"[\", username, \"] \", \"request to \", request.Host)\n\tswitch request.Host {\n\tcase UDPMagicAddress:\n\t\twriter.WriteHeader(http.StatusOK)\n\t\tflusher, isFlusher := writer.(http.Flusher)\n\t\tif isFlusher {\n\t\t\tflusher.Flush()\n\t\t}\n\t\tconn := &serverPacketConn{\n\t\t\tpacketConn: packetConn{\n\t\t\t\thttpConn: httpConn{\n\t\t\t\t\twriter:  writer,\n\t\t\t\t\tflusher: flusher,\n\t\t\t\t\tcreated: make(chan struct{}),\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\t\thttputils.SetAddrFromRequest(&conn.NetAddr, request)\n\t\tconn.setup(request.Body, nil)\n\t\tfirstPacket := buf.NewPacket()\n\t\tdestination, err := conn.ReadPacket(firstPacket)\n\t\tif err != nil {\n\t\t\tfirstPacket.Release()\n\t\t\t_ = conn.Close()\n\t\t\ts.logger.ErrorContext(ctx, E.Cause(err, \"read first packet of \", request.RemoteAddr))\n\t\t\treturn\n\t\t}\n\t\tdestination = destination.Unwrap()\n\t\tcachedConn := bufio.NewCachedPacketConn(conn, firstPacket, destination)\n\t\t_ = s.handler.NewPacketConnection(ctx, cachedConn, M.Metadata{\n\t\t\tProtocol:    \"trusttunnel\",\n\t\t\tSource:      M.ParseSocksaddr(request.RemoteAddr),\n\t\t\tDestination: destination,\n\t\t})\n\tcase ICMPMagicAddress:\n\t\tflusher, isFlusher := writer.(http.Flusher)\n\t\tif s.icmpHandler == nil {\n\t\t\twriter.WriteHeader(http.StatusNotImplemented)\n\t\t\tif isFlusher {\n\t\t\t\tflusher.Flush()\n\t\t\t}\n\t\t\t_ = request.Body.Close()\n\t\t} else {\n\t\t\twriter.WriteHeader(http.StatusOK)\n\t\t\tif isFlusher {\n\t\t\t\tflusher.Flush()\n\t\t\t}\n\t\t\tconn := &IcmpConn{\n\t\t\t\thttpConn{\n\t\t\t\t\twriter:  writer,\n\t\t\t\t\tflusher: flusher,\n\t\t\t\t\tcreated: make(chan struct{}),\n\t\t\t\t},\n\t\t\t}\n\t\t\thttputils.SetAddrFromRequest(&conn.NetAddr, request)\n\t\t\tconn.setup(request.Body, nil)\n\t\t\ts.icmpHandler.NewICMPConnection(ctx, conn)\n\t\t}\n\tcase HealthCheckMagicAddress:\n\t\twriter.WriteHeader(http.StatusOK)\n\t\tif flusher, isFlusher := writer.(http.Flusher); isFlusher {\n\t\t\tflusher.Flush()\n\t\t}\n\t\t_ = request.Body.Close()\n\tdefault:\n\t\twriter.WriteHeader(http.StatusOK)\n\t\tflusher, isFlusher := writer.(http.Flusher)\n\t\tif isFlusher {\n\t\t\tflusher.Flush()\n\t\t}\n\t\tconn := &tcpConn{\n\t\t\thttpConn{\n\t\t\t\twriter:  writer,\n\t\t\t\tflusher: flusher,\n\t\t\t\tcreated: make(chan struct{}),\n\t\t\t},\n\t\t}\n\t\thttputils.SetAddrFromRequest(&conn.NetAddr, request)\n\t\tconn.setup(request.Body, nil)\n\t\t_ = s.handler.NewConnection(ctx, N.NewDeadlineConn(conn), M.Metadata{\n\t\t\tProtocol:    \"trusttunnel\",\n\t\t\tSource:      M.ParseSocksaddr(request.RemoteAddr),\n\t\t\tDestination: M.ParseSocksaddr(request.Host).Unwrap(),\n\t\t})\n\t}\n}\n\nfunc (s *Service) verify(authorization string) (username string, loaded bool) {\n\tusername, password, loaded := parseBasicAuth(authorization)\n\tif !loaded {\n\t\treturn \"\", false\n\t}\n\trecordedPassword, loaded := s.users[username]\n\tif !loaded {\n\t\treturn \"\", false\n\t}\n\tif password != recordedPassword {\n\t\treturn \"\", false\n\t}\n\treturn username, true\n}\n\nfunc (s *Service) badRequest(ctx context.Context, request *http.Request, err error) {\n\ts.logger.ErrorContext(ctx, E.Cause(err, \"process connection from \", request.RemoteAddr))\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/common/congestion.go",
    "content": "package common\n\nimport (\n\t\"github.com/metacubex/mihomo/transport/tuic/congestion\"\n\tcongestionv2 \"github.com/metacubex/mihomo/transport/tuic/congestion_v2\"\n\n\t\"github.com/metacubex/quic-go\"\n\tc \"github.com/metacubex/quic-go/congestion\"\n)\n\nconst (\n\tDefaultStreamReceiveWindow     = 15728640 // 15 MB/s\n\tDefaultConnectionReceiveWindow = 67108864 // 64 MB/s\n)\n\nfunc SetCongestionController(quicConn *quic.Conn, cc string, cwnd int, profile string) {\n\tif cwnd == 0 {\n\t\tcwnd = 32\n\t}\n\tswitch cc {\n\tcase \"cubic\":\n\t\tquicConn.SetCongestionControl(\n\t\t\tcongestion.NewCubicSender(\n\t\t\t\tcongestion.GetInitialPacketSize(quicConn),\n\t\t\t\tfalse,\n\t\t\t),\n\t\t)\n\tcase \"new_reno\":\n\t\tquicConn.SetCongestionControl(\n\t\t\tcongestion.NewCubicSender(\n\t\t\t\tcongestion.GetInitialPacketSize(quicConn),\n\t\t\t\ttrue,\n\t\t\t),\n\t\t)\n\tcase \"bbr_meta_v1\":\n\t\tquicConn.SetCongestionControl(\n\t\t\tcongestion.NewBBRSender(\n\t\t\t\tcongestion.GetInitialPacketSize(quicConn),\n\t\t\t\tc.ByteCount(cwnd)*congestion.InitialMaxDatagramSize,\n\t\t\t\tcongestion.DefaultBBRMaxCongestionWindow*congestion.InitialMaxDatagramSize,\n\t\t\t),\n\t\t)\n\tcase \"bbr_meta_v2\":\n\t\tfallthrough\n\tcase \"bbr\":\n\t\tquicConn.SetCongestionControl(\n\t\t\tcongestionv2.NewBbrSender(\n\t\t\t\tcongestionv2.GetInitialPacketSize(quicConn),\n\t\t\t\tc.ByteCount(cwnd),\n\t\t\t\tcongestionv2.Profile(profile),\n\t\t\t),\n\t\t)\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/common/dial.go",
    "content": "package common\n\nimport (\n\t\"context\"\n\t\"net\"\n\t\"net/netip\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/component/dialer\"\n\n\t\"github.com/metacubex/quic-go\"\n\t\"github.com/metacubex/tls\"\n)\n\ntype PacketDialer interface {\n\tListenPacket(ctx context.Context, network, address string, rAddrPort netip.AddrPort) (net.PacketConn, error)\n}\n\nfunc DialQuic(ctx context.Context, address string, opts []dialer.Option, pDialer PacketDialer, tlsConf *tls.Config, conf *quic.Config, early bool) (net.PacketConn, *quic.Conn, error) {\n\td := dialer.NewDialer(\n\t\tdialer.WithOptions(opts...),\n\t\tdialer.WithNetDialer(dialer.NetDialerFunc(func(ctx context.Context, network, address string) (net.Conn, error) {\n\t\t\taddrPort, err := netip.ParseAddrPort(address) // the dialer will resolve the domain to ip\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tudpAddr := net.UDPAddrFromAddrPort(addrPort)\n\t\t\tpacketConn, err := pDialer.ListenPacket(ctx, \"udp\", \"\", udpAddr.AddrPort())\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\ttransport := quic.Transport{Conn: packetConn}\n\t\t\ttransport.SetCreatedConn(true) // auto close conn\n\t\t\ttransport.SetSingleUse(true)   // auto close transport\n\n\t\t\tvar quicConn *quic.Conn\n\t\t\tif early {\n\t\t\t\tquicConn, err = transport.DialEarly(ctx, udpAddr, tlsConf, conf)\n\t\t\t} else {\n\t\t\t\tquicConn, err = transport.Dial(ctx, udpAddr, tlsConf, conf)\n\t\t\t}\n\t\t\tif err != nil {\n\t\t\t\t_ = packetConn.Close()\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn quicNetConn{Conn: quicConn, pc: packetConn}, nil\n\t\t})),\n\t)\n\tc, err := d.DialContext(ctx, \"udp\", address)\n\tif err != nil {\n\t\treturn nil, nil, err\n\t}\n\tnc := c.(quicNetConn)\n\treturn nc.pc, nc.Conn, nil\n}\n\ntype quicNetConn struct {\n\t*quic.Conn\n\tpc net.PacketConn\n}\n\nfunc (q quicNetConn) Close() error {\n\terr := q.Conn.CloseWithError(0, \"\")\n\t_ = q.pc.Close() // always close the packetConn\n\treturn err\n}\n\nfunc (q quicNetConn) Read(b []byte) (n int, err error) {\n\tpanic(\"should not call Read on quicNetConn\")\n}\n\nfunc (q quicNetConn) Write(b []byte) (n int, err error) {\n\tpanic(\"should not call Write on quicNetConn\")\n}\n\nfunc (q quicNetConn) SetDeadline(t time.Time) error {\n\tpanic(\"should not call SetDeadline on quicNetConn\")\n}\n\nfunc (q quicNetConn) SetReadDeadline(t time.Time) error {\n\tpanic(\"should not call SetReadDeadline on quicNetConn\")\n}\n\nfunc (q quicNetConn) SetWriteDeadline(t time.Time) error {\n\tpanic(\"should not call SetWriteDeadline on quicNetConn\")\n}\n\nvar _ net.Conn = quicNetConn{}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/congestion/bandwidth.go",
    "content": "package congestion\n\nimport (\n\t\"math\"\n\t\"time\"\n\n\t\"github.com/metacubex/quic-go/congestion\"\n)\n\n// Bandwidth of a connection\ntype Bandwidth uint64\n\nconst infBandwidth Bandwidth = math.MaxUint64\n\nconst (\n\t// BitsPerSecond is 1 bit per second\n\tBitsPerSecond Bandwidth = 1\n\t// BytesPerSecond is 1 byte per second\n\tBytesPerSecond = 8 * BitsPerSecond\n)\n\n// BandwidthFromDelta calculates the bandwidth from a number of bytes and a time delta\nfunc BandwidthFromDelta(bytes congestion.ByteCount, delta time.Duration) Bandwidth {\n\treturn Bandwidth(bytes) * Bandwidth(time.Second) / Bandwidth(delta) * BytesPerSecond\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/congestion/bandwidth_sampler.go",
    "content": "package congestion\n\nimport (\n\t\"math\"\n\t\"time\"\n\n\t\"github.com/metacubex/quic-go/congestion\"\n\t\"github.com/metacubex/quic-go/monotime\"\n)\n\nvar (\n\tInfiniteBandwidth = Bandwidth(math.MaxUint64)\n)\n\n// SendTimeState is a subset of ConnectionStateOnSentPacket which is returned\n// to the caller when the packet is acked or lost.\ntype SendTimeState struct {\n\t// Whether other states in this object is valid.\n\tisValid bool\n\t// Whether the sender is app limited at the time the packet was sent.\n\t// App limited bandwidth sample might be artificially low because the sender\n\t// did not have enough data to send in order to saturate the link.\n\tisAppLimited bool\n\t// Total number of sent bytes at the time the packet was sent.\n\t// Includes the packet itself.\n\ttotalBytesSent congestion.ByteCount\n\t// Total number of acked bytes at the time the packet was sent.\n\ttotalBytesAcked congestion.ByteCount\n\t// Total number of lost bytes at the time the packet was sent.\n\ttotalBytesLost congestion.ByteCount\n}\n\n// ConnectionStateOnSentPacket represents the information about a sent packet\n// and the state of the connection at the moment the packet was sent,\n// specifically the information about the most recently acknowledged packet at\n// that moment.\ntype ConnectionStateOnSentPacket struct {\n\tpacketNumber congestion.PacketNumber\n\t// Time at which the packet is sent.\n\tsendTime monotime.Time\n\t// Size of the packet.\n\tsize congestion.ByteCount\n\t// The value of |totalBytesSentAtLastAckedPacket| at the time the\n\t// packet was sent.\n\ttotalBytesSentAtLastAckedPacket congestion.ByteCount\n\t// The value of |lastAckedPacketSentTime| at the time the packet was\n\t// sent.\n\tlastAckedPacketSentTime monotime.Time\n\t// The value of |lastAckedPacketAckTime| at the time the packet was\n\t// sent.\n\tlastAckedPacketAckTime monotime.Time\n\t// Send time states that are returned to the congestion controller when the\n\t// packet is acked or lost.\n\tsendTimeState SendTimeState\n}\n\n// BandwidthSample\ntype BandwidthSample struct {\n\t// The bandwidth at that particular sample. Zero if no valid bandwidth sample\n\t// is available.\n\tbandwidth Bandwidth\n\t// The RTT measurement at this particular sample.  Zero if no RTT sample is\n\t// available.  Does not correct for delayed ack time.\n\trtt time.Duration\n\t// States captured when the packet was sent.\n\tstateAtSend SendTimeState\n}\n\nfunc NewBandwidthSample() *BandwidthSample {\n\treturn &BandwidthSample{\n\t\t// FIXME: the default value of original code is zero.\n\t\trtt: InfiniteRTT,\n\t}\n}\n\n// BandwidthSampler keeps track of sent and acknowledged packets and outputs a\n// bandwidth sample for every packet acknowledged. The samples are taken for\n// individual packets, and are not filtered; the consumer has to filter the\n// bandwidth samples itself. In certain cases, the sampler will locally severely\n// underestimate the bandwidth, hence a maximum filter with a size of at least\n// one RTT is recommended.\n//\n// This class bases its samples on the slope of two curves: the number of bytes\n// sent over time, and the number of bytes acknowledged as received over time.\n// It produces a sample of both slopes for every packet that gets acknowledged,\n// based on a slope between two points on each of the corresponding curves. Note\n// that due to the packet loss, the number of bytes on each curve might get\n// further and further away from each other, meaning that it is not feasible to\n// compare byte values coming from different curves with each other.\n//\n// The obvious points for measuring slope sample are the ones corresponding to\n// the packet that was just acknowledged. Let us denote them as S_1 (point at\n// which the current packet was sent) and A_1 (point at which the current packet\n// was acknowledged). However, taking a slope requires two points on each line,\n// so estimating bandwidth requires picking a packet in the past with respect to\n// which the slope is measured.\n//\n// For that purpose, BandwidthSampler always keeps track of the most recently\n// acknowledged packet, and records it together with every outgoing packet.\n// When a packet gets acknowledged (A_1), it has not only information about when\n// it itself was sent (S_1), but also the information about the latest\n// acknowledged packet right before it was sent (S_0 and A_0).\n//\n// Based on that data, send and ack rate are estimated as:\n//\n//\tsend_rate = (bytes(S_1) - bytes(S_0)) / (time(S_1) - time(S_0))\n//\tack_rate = (bytes(A_1) - bytes(A_0)) / (time(A_1) - time(A_0))\n//\n// Here, the ack rate is intuitively the rate we want to treat as bandwidth.\n// However, in certain cases (e.g. ack compression) the ack rate at a point may\n// end up higher than the rate at which the data was originally sent, which is\n// not indicative of the real bandwidth. Hence, we use the send rate as an upper\n// bound, and the sample value is\n//\n//\trate_sample = min(send_rate, ack_rate)\n//\n// An important edge case handled by the sampler is tracking the app-limited\n// samples. There are multiple meaning of \"app-limited\" used interchangeably,\n// hence it is important to understand and to be able to distinguish between\n// them.\n//\n// Meaning 1: connection state. The connection is said to be app-limited when\n// there is no outstanding data to send. This means that certain bandwidth\n// samples in the future would not be an accurate indication of the link\n// capacity, and it is important to inform consumer about that. Whenever\n// connection becomes app-limited, the sampler is notified via OnAppLimited()\n// method.\n//\n// Meaning 2: a phase in the bandwidth sampler. As soon as the bandwidth\n// sampler becomes notified about the connection being app-limited, it enters\n// app-limited phase. In that phase, all *sent* packets are marked as\n// app-limited. Note that the connection itself does not have to be\n// app-limited during the app-limited phase, and in fact it will not be\n// (otherwise how would it send packets?). The boolean flag below indicates\n// whether the sampler is in that phase.\n//\n// Meaning 3: a flag on the sent packet and on the sample. If a sent packet is\n// sent during the app-limited phase, the resulting sample related to the\n// packet will be marked as app-limited.\n//\n// With the terminology issue out of the way, let us consider the question of\n// what kind of situation it addresses.\n//\n// Consider a scenario where we first send packets 1 to 20 at a regular\n// bandwidth, and then immediately run out of data. After a few seconds, we send\n// packets 21 to 60, and only receive ack for 21 between sending packets 40 and\n// 41. In this case, when we sample bandwidth for packets 21 to 40, the S_0/A_0\n// we use to compute the slope is going to be packet 20, a few seconds apart\n// from the current packet, hence the resulting estimate would be extremely low\n// and not indicative of anything. Only at packet 41 the S_0/A_0 will become 21,\n// meaning that the bandwidth sample would exclude the quiescence.\n//\n// Based on the analysis of that scenario, we implement the following rule: once\n// OnAppLimited() is called, all sent packets will produce app-limited samples\n// up until an ack for a packet that was sent after OnAppLimited() was called.\n// Note that while the scenario above is not the only scenario when the\n// connection is app-limited, the approach works in other cases too.\ntype BandwidthSampler struct {\n\t// The total number of congestion controlled bytes sent during the connection.\n\ttotalBytesSent congestion.ByteCount\n\t// The total number of congestion controlled bytes which were acknowledged.\n\ttotalBytesAcked congestion.ByteCount\n\t// The total number of congestion controlled bytes which were lost.\n\ttotalBytesLost congestion.ByteCount\n\t// The value of |totalBytesSent| at the time the last acknowledged packet\n\t// was sent. Valid only when |lastAckedPacketSentTime| is valid.\n\ttotalBytesSentAtLastAckedPacket congestion.ByteCount\n\t// The time at which the last acknowledged packet was sent. Set to\n\t// QuicTime::Zero() if no valid timestamp is available.\n\tlastAckedPacketSentTime monotime.Time\n\t// The time at which the most recent packet was acknowledged.\n\tlastAckedPacketAckTime monotime.Time\n\t// The most recently sent packet.\n\tlastSendPacket congestion.PacketNumber\n\t// Indicates whether the bandwidth sampler is currently in an app-limited\n\t// phase.\n\tisAppLimited bool\n\t// The packet that will be acknowledged after this one will cause the sampler\n\t// to exit the app-limited phase.\n\tendOfAppLimitedPhase congestion.PacketNumber\n\t// Record of the connection state at the point where each packet in flight was\n\t// sent, indexed by the packet number.\n\tconnectionStats *ConnectionStates\n}\n\nfunc NewBandwidthSampler() *BandwidthSampler {\n\treturn &BandwidthSampler{\n\t\tconnectionStats: &ConnectionStates{\n\t\t\tstats: make(map[congestion.PacketNumber]*ConnectionStateOnSentPacket),\n\t\t},\n\t}\n}\n\n// OnPacketSent Inputs the sent packet information into the sampler. Assumes that all\n// packets are sent in order. The information about the packet will not be\n// released from the sampler until it the packet is either acknowledged or\n// declared lost.\nfunc (s *BandwidthSampler) OnPacketSent(sentTime monotime.Time, lastSentPacket congestion.PacketNumber, sentBytes, bytesInFlight congestion.ByteCount, hasRetransmittableData bool) {\n\ts.lastSendPacket = lastSentPacket\n\n\tif !hasRetransmittableData {\n\t\treturn\n\t}\n\n\ts.totalBytesSent += sentBytes\n\n\t// If there are no packets in flight, the time at which the new transmission\n\t// opens can be treated as the A_0 point for the purpose of bandwidth\n\t// sampling. This underestimates bandwidth to some extent, and produces some\n\t// artificially low samples for most packets in flight, but it provides with\n\t// samples at important points where we would not have them otherwise, most\n\t// importantly at the beginning of the connection.\n\tif bytesInFlight == 0 {\n\t\ts.lastAckedPacketAckTime = sentTime\n\t\ts.totalBytesSentAtLastAckedPacket = s.totalBytesSent\n\n\t\t// In this situation ack compression is not a concern, set send rate to\n\t\t// effectively infinite.\n\t\ts.lastAckedPacketSentTime = sentTime\n\t}\n\n\ts.connectionStats.Insert(lastSentPacket, sentTime, sentBytes, s)\n}\n\n// OnPacketAcked Notifies the sampler that the |lastAckedPacket| is acknowledged. Returns a\n// bandwidth sample. If no bandwidth sample is available,\n// QuicBandwidth::Zero() is returned.\nfunc (s *BandwidthSampler) OnPacketAcked(ackTime monotime.Time, lastAckedPacket congestion.PacketNumber) *BandwidthSample {\n\tsentPacketState := s.connectionStats.Get(lastAckedPacket)\n\tif sentPacketState == nil {\n\t\treturn NewBandwidthSample()\n\t}\n\n\tsample := s.onPacketAckedInner(ackTime, lastAckedPacket, sentPacketState)\n\ts.connectionStats.Remove(lastAckedPacket)\n\n\treturn sample\n}\n\n// onPacketAckedInner Handles the actual bandwidth calculations, whereas the outer method handles\n// retrieving and removing |sentPacket|.\nfunc (s *BandwidthSampler) onPacketAckedInner(ackTime monotime.Time, lastAckedPacket congestion.PacketNumber, sentPacket *ConnectionStateOnSentPacket) *BandwidthSample {\n\ts.totalBytesAcked += sentPacket.size\n\n\ts.totalBytesSentAtLastAckedPacket = sentPacket.sendTimeState.totalBytesSent\n\ts.lastAckedPacketSentTime = sentPacket.sendTime\n\ts.lastAckedPacketAckTime = ackTime\n\n\t// Exit app-limited phase once a packet that was sent while the connection is\n\t// not app-limited is acknowledged.\n\tif s.isAppLimited && lastAckedPacket > s.endOfAppLimitedPhase {\n\t\ts.isAppLimited = false\n\t}\n\n\t// There might have been no packets acknowledged at the moment when the\n\t// current packet was sent. In that case, there is no bandwidth sample to\n\t// make.\n\tif sentPacket.lastAckedPacketSentTime.IsZero() {\n\t\treturn NewBandwidthSample()\n\t}\n\n\t// Infinite rate indicates that the sampler is supposed to discard the\n\t// current send rate sample and use only the ack rate.\n\tsendRate := InfiniteBandwidth\n\tif sentPacket.sendTime.After(sentPacket.lastAckedPacketSentTime) {\n\t\tsendRate = BandwidthFromDelta(sentPacket.sendTimeState.totalBytesSent-sentPacket.totalBytesSentAtLastAckedPacket, sentPacket.sendTime.Sub(sentPacket.lastAckedPacketSentTime))\n\t}\n\n\t// During the slope calculation, ensure that ack time of the current packet is\n\t// always larger than the time of the previous packet, otherwise division by\n\t// zero or integer underflow can occur.\n\tif !ackTime.After(sentPacket.lastAckedPacketAckTime) {\n\t\t// TODO(wub): Compare this code count before and after fixing clock jitter\n\t\t// issue.\n\t\t// if sentPacket.lastAckedPacketAckTime.Equal(sentPacket.sendTime) {\n\t\t// This is the 1st packet after quiescense.\n\t\t// QUIC_CODE_COUNT_N(quic_prev_ack_time_larger_than_current_ack_time, 1, 2);\n\t\t// } else {\n\t\t//   QUIC_CODE_COUNT_N(quic_prev_ack_time_larger_than_current_ack_time, 2, 2);\n\t\t// }\n\n\t\treturn NewBandwidthSample()\n\t}\n\n\tackRate := BandwidthFromDelta(s.totalBytesAcked-sentPacket.sendTimeState.totalBytesAcked,\n\t\tackTime.Sub(sentPacket.lastAckedPacketAckTime))\n\n\t// Note: this sample does not account for delayed acknowledgement time.  This\n\t// means that the RTT measurements here can be artificially high, especially\n\t// on low bandwidth connections.\n\tsample := &BandwidthSample{\n\t\tbandwidth: minBandwidth(sendRate, ackRate),\n\t\trtt:       ackTime.Sub(sentPacket.sendTime),\n\t}\n\n\tSentPacketToSendTimeState(sentPacket, &sample.stateAtSend)\n\treturn sample\n}\n\n// OnCongestionEvent Informs the sampler that a packet is considered lost and it should no\n// longer keep track of it.\nfunc (s *BandwidthSampler) OnCongestionEvent(packetNumber congestion.PacketNumber) SendTimeState {\n\tok, sentPacket := s.connectionStats.Remove(packetNumber)\n\tsendTimeState := SendTimeState{\n\t\tisValid: ok,\n\t}\n\tif sentPacket != nil {\n\t\ts.totalBytesLost += sentPacket.size\n\t\tSentPacketToSendTimeState(sentPacket, &sendTimeState)\n\t}\n\n\treturn sendTimeState\n}\n\n// OnAppLimited Informs the sampler that the connection is currently app-limited, causing\n// the sampler to enter the app-limited phase.  The phase will expire by\n// itself.\nfunc (s *BandwidthSampler) OnAppLimited() {\n\ts.isAppLimited = true\n\ts.endOfAppLimitedPhase = s.lastSendPacket\n}\n\n// SentPacketToSendTimeState Copy a subset of the (private) ConnectionStateOnSentPacket to the (public)\n// SendTimeState. Always set send_time_state->is_valid to true.\nfunc SentPacketToSendTimeState(sentPacket *ConnectionStateOnSentPacket, sendTimeState *SendTimeState) {\n\tsendTimeState.isAppLimited = sentPacket.sendTimeState.isAppLimited\n\tsendTimeState.totalBytesSent = sentPacket.sendTimeState.totalBytesSent\n\tsendTimeState.totalBytesAcked = sentPacket.sendTimeState.totalBytesAcked\n\tsendTimeState.totalBytesLost = sentPacket.sendTimeState.totalBytesLost\n\tsendTimeState.isValid = true\n}\n\n// ConnectionStates Record of the connection state at the point where each packet in flight was\n// sent, indexed by the packet number.\n// FIXME: using LinkedList replace map to fast remove all the packets lower than the specified packet number.\ntype ConnectionStates struct {\n\tstats map[congestion.PacketNumber]*ConnectionStateOnSentPacket\n}\n\nfunc (s *ConnectionStates) Insert(packetNumber congestion.PacketNumber, sentTime monotime.Time, bytes congestion.ByteCount, sampler *BandwidthSampler) bool {\n\tif _, ok := s.stats[packetNumber]; ok {\n\t\treturn false\n\t}\n\n\ts.stats[packetNumber] = NewConnectionStateOnSentPacket(packetNumber, sentTime, bytes, sampler)\n\treturn true\n}\n\nfunc (s *ConnectionStates) Get(packetNumber congestion.PacketNumber) *ConnectionStateOnSentPacket {\n\treturn s.stats[packetNumber]\n}\n\nfunc (s *ConnectionStates) Remove(packetNumber congestion.PacketNumber) (bool, *ConnectionStateOnSentPacket) {\n\tstate, ok := s.stats[packetNumber]\n\tif ok {\n\t\tdelete(s.stats, packetNumber)\n\t}\n\treturn ok, state\n}\n\nfunc NewConnectionStateOnSentPacket(packetNumber congestion.PacketNumber, sentTime monotime.Time, bytes congestion.ByteCount, sampler *BandwidthSampler) *ConnectionStateOnSentPacket {\n\treturn &ConnectionStateOnSentPacket{\n\t\tpacketNumber:                    packetNumber,\n\t\tsendTime:                        sentTime,\n\t\tsize:                            bytes,\n\t\tlastAckedPacketSentTime:         sampler.lastAckedPacketSentTime,\n\t\tlastAckedPacketAckTime:          sampler.lastAckedPacketAckTime,\n\t\ttotalBytesSentAtLastAckedPacket: sampler.totalBytesSentAtLastAckedPacket,\n\t\tsendTimeState: SendTimeState{\n\t\t\tisValid:         true,\n\t\t\tisAppLimited:    sampler.isAppLimited,\n\t\t\ttotalBytesSent:  sampler.totalBytesSent,\n\t\t\ttotalBytesAcked: sampler.totalBytesAcked,\n\t\t\ttotalBytesLost:  sampler.totalBytesLost,\n\t\t},\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/congestion/bbr_sender.go",
    "content": "package congestion\n\n// src from https://quiche.googlesource.com/quiche.git/+/66dea072431f94095dfc3dd2743cb94ef365f7ef/quic/core/congestion_control/bbr_sender.cc\n\nimport (\n\t\"fmt\"\n\t\"math\"\n\t\"time\"\n\n\t\"github.com/metacubex/quic-go\"\n\t\"github.com/metacubex/quic-go/congestion\"\n\t\"github.com/metacubex/quic-go/monotime\"\n\t\"github.com/metacubex/randv2\"\n)\n\nconst (\n\t// InitialMaxDatagramSize is the default maximum packet size used in QUIC for congestion window computations in bytes.\n\tInitialMaxDatagramSize        = 1280\n\tInitialPacketSize             = 1280\n\tInitialCongestionWindow       = 32\n\tDefaultBBRMaxCongestionWindow = 10000\n)\n\nfunc GetInitialPacketSize(quicConn *quic.Conn) congestion.ByteCount {\n\treturn congestion.ByteCount(quicConn.Config().InitialPacketSize)\n}\n\nvar (\n\n\t// Default initial rtt used before any samples are received.\n\tInitialRtt = 100 * time.Millisecond\n\n\t// The gain used for the STARTUP, equal to  4*ln(2).\n\tDefaultHighGain = 2.77\n\n\t// The gain used in STARTUP after loss has been detected.\n\t// 1.5 is enough to allow for 25% exogenous loss and still observe a 25% growth\n\t// in measured bandwidth.\n\tStartupAfterLossGain = 1.5\n\n\t// The cycle of gains used during the PROBE_BW stage.\n\tPacingGain = []float64{1.25, 0.75, 1, 1, 1, 1, 1, 1}\n\n\t// The length of the gain cycle.\n\tGainCycleLength = len(PacingGain)\n\n\t// The size of the bandwidth filter window, in round-trips.\n\tBandwidthWindowSize = GainCycleLength + 2\n\n\t// The time after which the current min_rtt value expires.\n\tMinRttExpiry = 10 * time.Second\n\n\t// The minimum time the connection can spend in PROBE_RTT mode.\n\tProbeRttTime = time.Millisecond * 200\n\n\t// If the bandwidth does not increase by the factor of |kStartupGrowthTarget|\n\t// within |kRoundTripsWithoutGrowthBeforeExitingStartup| rounds, the connection\n\t// will exit the STARTUP mode.\n\tStartupGrowthTarget                         = 1.25\n\tRoundTripsWithoutGrowthBeforeExitingStartup = int64(3)\n\n\t// Coefficient of target congestion window to use when basing PROBE_RTT on BDP.\n\tModerateProbeRttMultiplier = 0.75\n\n\t// Coefficient to determine if a new RTT is sufficiently similar to min_rtt that\n\t// we don't need to enter PROBE_RTT.\n\tSimilarMinRttThreshold = 1.125\n\n\t// Congestion window gain for QUIC BBR during PROBE_BW phase.\n\tDefaultCongestionWindowGainConst = 2.0\n)\n\ntype bbrMode int\n\nconst (\n\t// Startup phase of the connection.\n\tSTARTUP = iota\n\t// After achieving the highest possible bandwidth during the startup, lower\n\t// the pacing rate in order to drain the queue.\n\tDRAIN\n\t// Cruising mode.\n\tPROBE_BW\n\t// Temporarily slow down sending in order to empty the buffer and measure\n\t// the real minimum RTT.\n\tPROBE_RTT\n)\n\ntype bbrRecoveryState int\n\nconst (\n\t// Do not limit.\n\tNOT_IN_RECOVERY = iota\n\n\t// Allow an extra outstanding byte for each byte acknowledged.\n\tCONSERVATION\n\n\t// Allow two extra outstanding bytes for each byte acknowledged (slow\n\t// start).\n\tGROWTH\n)\n\ntype bbrSender struct {\n\tmode          bbrMode\n\trttStats      congestion.RTTStatsProvider\n\tbytesInFlight congestion.ByteCount\n\t// return total bytes of unacked packets.\n\t//GetBytesInFlight func() congestion.ByteCount\n\t// Bandwidth sampler provides BBR with the bandwidth measurements at\n\t// individual points.\n\tsampler *BandwidthSampler\n\t// The number of the round trips that have occurred during the connection.\n\troundTripCount int64\n\t// The packet number of the most recently sent packet.\n\tlastSendPacket congestion.PacketNumber\n\t// Acknowledgement of any packet after |current_round_trip_end_| will cause\n\t// the round trip counter to advance.\n\tcurrentRoundTripEnd congestion.PacketNumber\n\t// The filter that tracks the maximum bandwidth over the multiple recent\n\t// round-trips.\n\tmaxBandwidth *WindowedFilter\n\t// Tracks the maximum number of bytes acked faster than the sending rate.\n\tmaxAckHeight *WindowedFilter\n\t// The time this aggregation started and the number of bytes acked during it.\n\taggregationEpochStartTime monotime.Time\n\taggregationEpochBytes     congestion.ByteCount\n\t// Minimum RTT estimate.  Automatically expires within 10 seconds (and\n\t// triggers PROBE_RTT mode) if no new value is sampled during that period.\n\tminRtt time.Duration\n\t// The time at which the current value of |min_rtt_| was assigned.\n\tminRttTimestamp monotime.Time\n\t// The maximum allowed number of bytes in flight.\n\tcongestionWindow congestion.ByteCount\n\t// The initial value of the |congestion_window_|.\n\tinitialCongestionWindow congestion.ByteCount\n\t// The largest value the |congestion_window_| can achieve.\n\tinitialMaxCongestionWindow congestion.ByteCount\n\t// The smallest value the |congestion_window_| can achieve.\n\t//minCongestionWindow congestion.ByteCount\n\t// The pacing gain applied during the STARTUP phase.\n\thighGain float64\n\t// The CWND gain applied during the STARTUP phase.\n\thighCwndGain float64\n\t// The pacing gain applied during the DRAIN phase.\n\tdrainGain float64\n\t// The current pacing rate of the connection.\n\tpacingRate Bandwidth\n\t// The gain currently applied to the pacing rate.\n\tpacingGain float64\n\t// The gain currently applied to the congestion window.\n\tcongestionWindowGain float64\n\t// The gain used for the congestion window during PROBE_BW.  Latched from\n\t// quic_bbr_cwnd_gain flag.\n\tcongestionWindowGainConst float64\n\t// The number of RTTs to stay in STARTUP mode.  Defaults to 3.\n\tnumStartupRtts int64\n\t// If true, exit startup if 1RTT has passed with no bandwidth increase and\n\t// the connection is in recovery.\n\texitStartupOnLoss bool\n\t// Number of round-trips in PROBE_BW mode, used for determining the current\n\t// pacing gain cycle.\n\tcycleCurrentOffset int\n\t// The time at which the last pacing gain cycle was started.\n\tlastCycleStart monotime.Time\n\t// Indicates whether the connection has reached the full bandwidth mode.\n\tisAtFullBandwidth bool\n\t// Number of rounds during which there was no significant bandwidth increase.\n\troundsWithoutBandwidthGain int64\n\t// The bandwidth compared to which the increase is measured.\n\tbandwidthAtLastRound Bandwidth\n\t// Set to true upon exiting quiescence.\n\texitingQuiescence bool\n\t// Time at which PROBE_RTT has to be exited.  Setting it to zero indicates\n\t// that the time is yet unknown as the number of packets in flight has not\n\t// reached the required value.\n\texitProbeRttAt monotime.Time\n\t// Indicates whether a round-trip has passed since PROBE_RTT became active.\n\tprobeRttRoundPassed bool\n\t// Indicates whether the most recent bandwidth sample was marked as\n\t// app-limited.\n\tlastSampleIsAppLimited bool\n\t// Indicates whether any non app-limited samples have been recorded.\n\thasNoAppLimitedSample bool\n\t// Indicates app-limited calls should be ignored as long as there's\n\t// enough data inflight to see more bandwidth when necessary.\n\tflexibleAppLimited bool\n\t// Current state of recovery.\n\trecoveryState bbrRecoveryState\n\t// Receiving acknowledgement of a packet after |end_recovery_at_| will cause\n\t// BBR to exit the recovery mode.  A value above zero indicates at least one\n\t// loss has been detected, so it must not be set back to zero.\n\tendRecoveryAt congestion.PacketNumber\n\t// A window used to limit the number of bytes in flight during loss recovery.\n\trecoveryWindow congestion.ByteCount\n\t// If true, consider all samples in recovery app-limited.\n\tisAppLimitedRecovery bool\n\t// When true, pace at 1.5x and disable packet conservation in STARTUP.\n\tslowerStartup bool\n\t// When true, disables packet conservation in STARTUP.\n\trateBasedStartup bool\n\t// When non-zero, decreases the rate in STARTUP by the total number of bytes\n\t// lost in STARTUP divided by CWND.\n\tstartupRateReductionMultiplier int64\n\t// Sum of bytes lost in STARTUP.\n\tstartupBytesLost congestion.ByteCount\n\t// When true, add the most recent ack aggregation measurement during STARTUP.\n\tenableAckAggregationDuringStartup bool\n\t// When true, expire the windowed ack aggregation values in STARTUP when\n\t// bandwidth increases more than 25%.\n\texpireAckAggregationInStartup bool\n\t// If true, will not exit low gain mode until bytes_in_flight drops below BDP\n\t// or it's time for high gain mode.\n\tdrainToTarget bool\n\t// If true, use a CWND of 0.75*BDP during probe_rtt instead of 4 packets.\n\tprobeRttBasedOnBdp bool\n\t// If true, skip probe_rtt and update the timestamp of the existing min_rtt to\n\t// now if min_rtt over the last cycle is within 12.5% of the current min_rtt.\n\t// Even if the min_rtt is 12.5% too low, the 25% gain cycling and 2x CWND gain\n\t// should overcome an overly small min_rtt.\n\tprobeRttSkippedIfSimilarRtt bool\n\t// If true, disable PROBE_RTT entirely as long as the connection was recently\n\t// app limited.\n\tprobeRttDisabledIfAppLimited bool\n\tappLimitedSinceLastProbeRtt  bool\n\tminRttSinceLastProbeRtt      time.Duration\n\t// Latched value of --quic_always_get_bw_sample_when_acked.\n\talwaysGetBwSampleWhenAcked bool\n\n\tpacer *pacer\n\n\tmaxDatagramSize congestion.ByteCount\n}\n\nfunc NewBBRSender(\n\tinitialMaxDatagramSize,\n\tinitialCongestionWindow,\n\tinitialMaxCongestionWindow congestion.ByteCount,\n) *bbrSender {\n\tb := &bbrSender{\n\t\tmode:                      STARTUP,\n\t\tsampler:                   NewBandwidthSampler(),\n\t\tmaxBandwidth:              NewWindowedFilter(int64(BandwidthWindowSize), MaxFilter),\n\t\tmaxAckHeight:              NewWindowedFilter(int64(BandwidthWindowSize), MaxFilter),\n\t\tcongestionWindow:          initialCongestionWindow,\n\t\tinitialCongestionWindow:   initialCongestionWindow,\n\t\thighGain:                  DefaultHighGain,\n\t\thighCwndGain:              DefaultHighGain,\n\t\tdrainGain:                 1.0 / DefaultHighGain,\n\t\tpacingGain:                1.0,\n\t\tcongestionWindowGain:      1.0,\n\t\tcongestionWindowGainConst: DefaultCongestionWindowGainConst,\n\t\tnumStartupRtts:            RoundTripsWithoutGrowthBeforeExitingStartup,\n\t\trecoveryState:             NOT_IN_RECOVERY,\n\t\trecoveryWindow:            initialMaxCongestionWindow,\n\t\tminRttSinceLastProbeRtt:   InfiniteRTT,\n\t\tmaxDatagramSize:           initialMaxDatagramSize,\n\t}\n\tb.pacer = newPacer(b.BandwidthEstimate)\n\treturn b\n}\n\nfunc (b *bbrSender) maxCongestionWindow() congestion.ByteCount {\n\treturn b.maxDatagramSize * DefaultBBRMaxCongestionWindow\n}\n\nfunc (b *bbrSender) minCongestionWindow() congestion.ByteCount {\n\treturn b.maxDatagramSize * b.initialCongestionWindow\n}\n\nfunc (b *bbrSender) SetRTTStatsProvider(provider congestion.RTTStatsProvider) {\n\tb.rttStats = provider\n}\n\nfunc (b *bbrSender) GetBytesInFlight() congestion.ByteCount {\n\treturn b.bytesInFlight\n}\n\n// TimeUntilSend returns when the next packet should be sent.\nfunc (b *bbrSender) TimeUntilSend(bytesInFlight congestion.ByteCount) monotime.Time {\n\tb.bytesInFlight = bytesInFlight\n\treturn b.pacer.TimeUntilSend()\n}\n\nfunc (b *bbrSender) HasPacingBudget(now monotime.Time) bool {\n\treturn b.pacer.Budget(now) >= b.maxDatagramSize\n}\n\nfunc (b *bbrSender) SetMaxDatagramSize(s congestion.ByteCount) {\n\tif s < b.maxDatagramSize {\n\t\tpanic(fmt.Sprintf(\"congestion BUG: decreased max datagram size from %d to %d\", b.maxDatagramSize, s))\n\t}\n\tcwndIsMinCwnd := b.congestionWindow == b.minCongestionWindow()\n\tb.maxDatagramSize = s\n\tif cwndIsMinCwnd {\n\t\tb.congestionWindow = b.minCongestionWindow()\n\t}\n\tb.pacer.SetMaxDatagramSize(s)\n}\n\nfunc (b *bbrSender) OnPacketSent(sentTime monotime.Time, bytesInFlight congestion.ByteCount, packetNumber congestion.PacketNumber, bytes congestion.ByteCount, isRetransmittable bool) {\n\tb.pacer.SentPacket(sentTime, bytes)\n\tb.lastSendPacket = packetNumber\n\n\tb.bytesInFlight = bytesInFlight\n\tif bytesInFlight == 0 && b.sampler.isAppLimited {\n\t\tb.exitingQuiescence = true\n\t}\n\n\tif b.aggregationEpochStartTime.IsZero() {\n\t\tb.aggregationEpochStartTime = sentTime\n\t}\n\n\tb.sampler.OnPacketSent(sentTime, packetNumber, bytes, bytesInFlight, isRetransmittable)\n}\n\nfunc (b *bbrSender) CanSend(bytesInFlight congestion.ByteCount) bool {\n\tb.bytesInFlight = bytesInFlight\n\treturn bytesInFlight < b.GetCongestionWindow()\n}\n\nfunc (b *bbrSender) GetCongestionWindow() congestion.ByteCount {\n\tif b.mode == PROBE_RTT {\n\t\treturn b.ProbeRttCongestionWindow()\n\t}\n\n\tif b.InRecovery() && !(b.rateBasedStartup && b.mode == STARTUP) {\n\t\treturn minByteCount(b.congestionWindow, b.recoveryWindow)\n\t}\n\n\treturn b.congestionWindow\n}\n\nfunc (b *bbrSender) MaybeExitSlowStart() {\n\n}\n\nfunc (b *bbrSender) OnPacketAcked(number congestion.PacketNumber, ackedBytes congestion.ByteCount, priorInFlight congestion.ByteCount, eventTime monotime.Time) {\n\t// Stub\n}\n\nfunc (b *bbrSender) OnCongestionEvent(number congestion.PacketNumber, lostBytes congestion.ByteCount, priorInFlight congestion.ByteCount) {\n\t// Stub\n}\n\nfunc (b *bbrSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, eventTime monotime.Time, ackedPackets []congestion.AckedPacketInfo, lostPackets []congestion.LostPacketInfo) {\n\ttotalBytesAckedBefore := b.sampler.totalBytesAcked\n\tisRoundStart, minRttExpired := false, false\n\n\tif lostPackets != nil {\n\t\tb.DiscardLostPackets(lostPackets)\n\t}\n\n\t// Input the new data into the BBR model of the connection.\n\tvar excessAcked congestion.ByteCount\n\tif len(ackedPackets) > 0 {\n\t\tlastAckedPacket := ackedPackets[len(ackedPackets)-1].PacketNumber\n\t\tisRoundStart = b.UpdateRoundTripCounter(lastAckedPacket)\n\t\tminRttExpired = b.UpdateBandwidthAndMinRtt(eventTime, ackedPackets)\n\t\tb.UpdateRecoveryState(len(lostPackets) > 0, isRoundStart)\n\t\tbytesAcked := b.sampler.totalBytesAcked - totalBytesAckedBefore\n\t\texcessAcked = b.UpdateAckAggregationBytes(eventTime, bytesAcked)\n\t}\n\n\t// Handle logic specific to PROBE_BW mode.\n\tif b.mode == PROBE_BW {\n\t\tb.UpdateGainCyclePhase(eventTime, priorInFlight, len(lostPackets) > 0)\n\t}\n\n\t// Handle logic specific to STARTUP and DRAIN modes.\n\tif isRoundStart && !b.isAtFullBandwidth {\n\t\tb.CheckIfFullBandwidthReached()\n\t}\n\tb.MaybeExitStartupOrDrain(eventTime)\n\n\t// Handle logic specific to PROBE_RTT.\n\tb.MaybeEnterOrExitProbeRtt(eventTime, isRoundStart, minRttExpired)\n\n\t// Calculate number of packets acked and lost.\n\tbytesAcked := b.sampler.totalBytesAcked - totalBytesAckedBefore\n\tbytesLost := congestion.ByteCount(0)\n\tfor _, packet := range lostPackets {\n\t\tbytesLost += packet.BytesLost\n\t}\n\n\t// After the model is updated, recalculate the pacing rate and congestion\n\t// window.\n\tb.CalculatePacingRate()\n\tb.CalculateCongestionWindow(bytesAcked, excessAcked)\n\tb.CalculateRecoveryWindow(bytesAcked, bytesLost)\n}\n\n//func (b *bbrSender) SetNumEmulatedConnections(n int) {\n//\n//}\n\nfunc (b *bbrSender) OnRetransmissionTimeout(packetsRetransmitted bool) {\n\n}\n\n//func (b *bbrSender) OnConnectionMigration() {\n//\n//}\n\n//// Experiments\n//func (b *bbrSender) SetSlowStartLargeReduction(enabled bool) {\n//\n//}\n\n//func (b *bbrSender) BandwidthEstimate() Bandwidth {\n//\treturn Bandwidth(b.maxBandwidth.GetBest())\n//}\n\n// BandwidthEstimate returns the current bandwidth estimate\nfunc (b *bbrSender) BandwidthEstimate() Bandwidth {\n\tif b.rttStats == nil {\n\t\treturn infBandwidth\n\t}\n\tsrtt := b.rttStats.SmoothedRTT()\n\tif srtt == 0 {\n\t\t// If we haven't measured an rtt, the bandwidth estimate is unknown.\n\t\treturn infBandwidth\n\t}\n\treturn BandwidthFromDelta(b.GetCongestionWindow(), srtt)\n}\n\n//func (b *bbrSender) HybridSlowStart() *HybridSlowStart {\n//\treturn nil\n//}\n\n//func (b *bbrSender) SlowstartThreshold() congestion.ByteCount {\n//\treturn 0\n//}\n\n//func (b *bbrSender) RenoBeta() float32 {\n//\treturn 0.0\n//}\n\nfunc (b *bbrSender) InRecovery() bool {\n\treturn b.recoveryState != NOT_IN_RECOVERY\n}\n\nfunc (b *bbrSender) InSlowStart() bool {\n\treturn b.mode == STARTUP\n}\n\n//func (b *bbrSender) ShouldSendProbingPacket() bool {\n//\tif b.pacingGain <= 1 {\n//\t\treturn false\n//\t}\n//\t// TODO(b/77975811): If the pipe is highly under-utilized, consider not\n//\t// sending a probing transmission, because the extra bandwidth is not needed.\n//\t// If flexible_app_limited is enabled, check if the pipe is sufficiently full.\n//\tif b.flexibleAppLimited {\n//\t\treturn !b.IsPipeSufficientlyFull()\n//\t} else {\n//\t\treturn true\n//\t}\n//}\n\n//func (b *bbrSender) IsPipeSufficientlyFull() bool {\n//\t// See if we need more bytes in flight to see more bandwidth.\n//\tif b.mode == STARTUP {\n//\t\t// STARTUP exits if it doesn't observe a 25% bandwidth increase, so the CWND\n//\t\t// must be more than 25% above the target.\n//\t\treturn b.GetBytesInFlight() >= b.GetTargetCongestionWindow(1.5)\n//\t}\n//\tif b.pacingGain > 1 {\n//\t\t// Super-unity PROBE_BW doesn't exit until 1.25 * BDP is achieved.\n//\t\treturn b.GetBytesInFlight() >= b.GetTargetCongestionWindow(b.pacingGain)\n//\t}\n//\t// If bytes_in_flight are above the target congestion window, it should be\n//\t// possible to observe the same or more bandwidth if it's available.\n//\treturn b.GetBytesInFlight() >= b.GetTargetCongestionWindow(1.1)\n//}\n\n//func (b *bbrSender) SetFromConfig() {\n//\t// TODO: not impl.\n//}\n\nfunc (b *bbrSender) UpdateRoundTripCounter(lastAckedPacket congestion.PacketNumber) bool {\n\tif b.currentRoundTripEnd == 0 || lastAckedPacket > b.currentRoundTripEnd {\n\t\tb.currentRoundTripEnd = lastAckedPacket\n\t\tb.roundTripCount++\n\t\t// if b.rttStats != nil && b.InSlowStart() {\n\t\t// TODO: ++stats_->slowstart_num_rtts;\n\t\t// }\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (b *bbrSender) UpdateBandwidthAndMinRtt(now monotime.Time, ackedPackets []congestion.AckedPacketInfo) bool {\n\tsampleMinRtt := InfiniteRTT\n\n\tfor _, packet := range ackedPackets {\n\t\tif !b.alwaysGetBwSampleWhenAcked && packet.BytesAcked == 0 {\n\t\t\t// Skip acked packets with 0 in flight bytes when updating bandwidth.\n\t\t\treturn false\n\t\t}\n\t\tbandwidthSample := b.sampler.OnPacketAcked(now, packet.PacketNumber)\n\t\tif b.alwaysGetBwSampleWhenAcked && !bandwidthSample.stateAtSend.isValid {\n\t\t\t// From the sampler's perspective, the packet has never been sent, or the\n\t\t\t// packet has been acked or marked as lost previously.\n\t\t\treturn false\n\t\t}\n\t\tb.lastSampleIsAppLimited = bandwidthSample.stateAtSend.isAppLimited\n\t\t//     has_non_app_limited_sample_ |=\n\t\t//        !bandwidth_sample.state_at_send.is_app_limited;\n\t\tif !bandwidthSample.stateAtSend.isAppLimited {\n\t\t\tb.hasNoAppLimitedSample = true\n\t\t}\n\t\tif bandwidthSample.rtt > 0 {\n\t\t\tsampleMinRtt = minRtt(sampleMinRtt, bandwidthSample.rtt)\n\t\t}\n\t\tif !bandwidthSample.stateAtSend.isAppLimited || bandwidthSample.bandwidth > b.BandwidthEstimate() {\n\t\t\tb.maxBandwidth.Update(int64(bandwidthSample.bandwidth), b.roundTripCount)\n\t\t}\n\t}\n\n\t// If none of the RTT samples are valid, return immediately.\n\tif sampleMinRtt == InfiniteRTT {\n\t\treturn false\n\t}\n\n\tb.minRttSinceLastProbeRtt = minRtt(b.minRttSinceLastProbeRtt, sampleMinRtt)\n\t// Do not expire min_rtt if none was ever available.\n\tminRttExpired := b.minRtt > 0 && (now.After(b.minRttTimestamp.Add(MinRttExpiry)))\n\tif minRttExpired || sampleMinRtt < b.minRtt || b.minRtt == 0 {\n\t\tif minRttExpired && b.ShouldExtendMinRttExpiry() {\n\t\t\tminRttExpired = false\n\t\t} else {\n\t\t\tb.minRtt = sampleMinRtt\n\t\t}\n\t\tb.minRttTimestamp = now\n\t\t// Reset since_last_probe_rtt fields.\n\t\tb.minRttSinceLastProbeRtt = InfiniteRTT\n\t\tb.appLimitedSinceLastProbeRtt = false\n\t}\n\n\treturn minRttExpired\n}\n\nfunc (b *bbrSender) ShouldExtendMinRttExpiry() bool {\n\tif b.probeRttDisabledIfAppLimited && b.appLimitedSinceLastProbeRtt {\n\t\t// Extend the current min_rtt if we've been app limited recently.\n\t\treturn true\n\t}\n\n\tminRttIncreasedSinceLastProbe := b.minRttSinceLastProbeRtt > time.Duration(float64(b.minRtt)*SimilarMinRttThreshold)\n\tif b.probeRttSkippedIfSimilarRtt && b.appLimitedSinceLastProbeRtt && !minRttIncreasedSinceLastProbe {\n\t\t// Extend the current min_rtt if we've been app limited recently and an rtt\n\t\t// has been measured in that time that's less than 12.5% more than the\n\t\t// current min_rtt.\n\t\treturn true\n\t}\n\n\treturn false\n}\n\nfunc (b *bbrSender) DiscardLostPackets(lostPackets []congestion.LostPacketInfo) {\n\tfor _, packet := range lostPackets {\n\t\tb.sampler.OnCongestionEvent(packet.PacketNumber)\n\t\tif b.mode == STARTUP {\n\t\t\t// if b.rttStats != nil {\n\t\t\t// TODO: slow start.\n\t\t\t// }\n\t\t\tif b.startupRateReductionMultiplier != 0 {\n\t\t\t\tb.startupBytesLost += packet.BytesLost\n\t\t\t}\n\t\t}\n\t}\n}\n\nfunc (b *bbrSender) UpdateRecoveryState(hasLosses, isRoundStart bool) {\n\t// Exit recovery when there are no losses for a round.\n\tif !hasLosses {\n\t\tb.endRecoveryAt = b.lastSendPacket\n\t}\n\tswitch b.recoveryState {\n\tcase NOT_IN_RECOVERY:\n\t\t// Enter conservation on the first loss.\n\t\tif hasLosses {\n\t\t\tb.recoveryState = CONSERVATION\n\t\t\t// This will cause the |recovery_window_| to be set to the correct\n\t\t\t// value in CalculateRecoveryWindow().\n\t\t\tb.recoveryWindow = 0\n\t\t\t// Since the conservation phase is meant to be lasting for a whole\n\t\t\t// round, extend the current round as if it were started right now.\n\t\t\tb.currentRoundTripEnd = b.lastSendPacket\n\t\t\tif false && b.lastSampleIsAppLimited {\n\t\t\t\tb.isAppLimitedRecovery = true\n\t\t\t}\n\t\t}\n\tcase CONSERVATION:\n\t\tif isRoundStart {\n\t\t\tb.recoveryState = GROWTH\n\t\t}\n\t\tfallthrough\n\tcase GROWTH:\n\t\t// Exit recovery if appropriate.\n\t\tif !hasLosses && b.lastSendPacket > b.endRecoveryAt {\n\t\t\tb.recoveryState = NOT_IN_RECOVERY\n\t\t\tb.isAppLimitedRecovery = false\n\t\t}\n\t}\n\n\tif b.recoveryState != NOT_IN_RECOVERY && b.isAppLimitedRecovery {\n\t\tb.sampler.OnAppLimited()\n\t}\n}\n\nfunc (b *bbrSender) UpdateAckAggregationBytes(ackTime monotime.Time, ackedBytes congestion.ByteCount) congestion.ByteCount {\n\t// Compute how many bytes are expected to be delivered, assuming max bandwidth\n\t// is correct.\n\texpectedAckedBytes := congestion.ByteCount(b.maxBandwidth.GetBest()) *\n\t\tcongestion.ByteCount((ackTime.Sub(b.aggregationEpochStartTime)))\n\t// Reset the current aggregation epoch as soon as the ack arrival rate is less\n\t// than or equal to the max bandwidth.\n\tif b.aggregationEpochBytes <= expectedAckedBytes {\n\t\t// Reset to start measuring a new aggregation epoch.\n\t\tb.aggregationEpochBytes = ackedBytes\n\t\tb.aggregationEpochStartTime = ackTime\n\t\treturn 0\n\t}\n\t// Compute how many extra bytes were delivered vs max bandwidth.\n\t// Include the bytes most recently acknowledged to account for stretch acks.\n\tb.aggregationEpochBytes += ackedBytes\n\tb.maxAckHeight.Update(int64(b.aggregationEpochBytes-expectedAckedBytes), b.roundTripCount)\n\treturn b.aggregationEpochBytes - expectedAckedBytes\n}\n\nfunc (b *bbrSender) UpdateGainCyclePhase(now monotime.Time, priorInFlight congestion.ByteCount, hasLosses bool) {\n\tbytesInFlight := b.GetBytesInFlight()\n\t// In most cases, the cycle is advanced after an RTT passes.\n\tshouldAdvanceGainCycling := now.Sub(b.lastCycleStart) > b.GetMinRtt()\n\n\t// If the pacing gain is above 1.0, the connection is trying to probe the\n\t// bandwidth by increasing the number of bytes in flight to at least\n\t// pacing_gain * BDP.  Make sure that it actually reaches the target, as long\n\t// as there are no losses suggesting that the buffers are not able to hold\n\t// that much.\n\tif b.pacingGain > 1.0 && !hasLosses && priorInFlight < b.GetTargetCongestionWindow(b.pacingGain) {\n\t\tshouldAdvanceGainCycling = false\n\t}\n\t// If pacing gain is below 1.0, the connection is trying to drain the extra\n\t// queue which could have been incurred by probing prior to it.  If the number\n\t// of bytes in flight falls down to the estimated BDP value earlier, conclude\n\t// that the queue has been successfully drained and exit this cycle early.\n\tif b.pacingGain < 1.0 && bytesInFlight <= b.GetTargetCongestionWindow(1.0) {\n\t\tshouldAdvanceGainCycling = true\n\t}\n\n\tif shouldAdvanceGainCycling {\n\t\tb.cycleCurrentOffset = (b.cycleCurrentOffset + 1) % GainCycleLength\n\t\tb.lastCycleStart = now\n\t\t// Stay in low gain mode until the target BDP is hit.\n\t\t// Low gain mode will be exited immediately when the target BDP is achieved.\n\t\tif b.drainToTarget && b.pacingGain < 1.0 && PacingGain[b.cycleCurrentOffset] == 1.0 &&\n\t\t\tbytesInFlight > b.GetTargetCongestionWindow(1.0) {\n\t\t\treturn\n\t\t}\n\t\tb.pacingGain = PacingGain[b.cycleCurrentOffset]\n\t}\n}\n\nfunc (b *bbrSender) GetTargetCongestionWindow(gain float64) congestion.ByteCount {\n\tbdp := congestion.ByteCount(b.GetMinRtt()) * congestion.ByteCount(b.BandwidthEstimate())\n\tcongestionWindow := congestion.ByteCount(gain * float64(bdp))\n\n\t// BDP estimate will be zero if no bandwidth samples are available yet.\n\tif congestionWindow == 0 {\n\t\tcongestionWindow = congestion.ByteCount(gain * float64(b.initialCongestionWindow))\n\t}\n\n\treturn maxByteCount(congestionWindow, b.minCongestionWindow())\n}\n\nfunc (b *bbrSender) CheckIfFullBandwidthReached() {\n\tif b.lastSampleIsAppLimited {\n\t\treturn\n\t}\n\n\ttarget := Bandwidth(float64(b.bandwidthAtLastRound) * StartupGrowthTarget)\n\tif b.BandwidthEstimate() >= target {\n\t\tb.bandwidthAtLastRound = b.BandwidthEstimate()\n\t\tb.roundsWithoutBandwidthGain = 0\n\t\tif b.expireAckAggregationInStartup {\n\t\t\t// Expire old excess delivery measurements now that bandwidth increased.\n\t\t\tb.maxAckHeight.Reset(0, b.roundTripCount)\n\t\t}\n\t\treturn\n\t}\n\tb.roundsWithoutBandwidthGain++\n\tif b.roundsWithoutBandwidthGain >= b.numStartupRtts || (b.exitStartupOnLoss && b.InRecovery()) {\n\t\tb.isAtFullBandwidth = true\n\t}\n}\n\nfunc (b *bbrSender) MaybeExitStartupOrDrain(now monotime.Time) {\n\tif b.mode == STARTUP && b.isAtFullBandwidth {\n\t\tb.OnExitStartup(now)\n\t\tb.mode = DRAIN\n\t\tb.pacingGain = b.drainGain\n\t\tb.congestionWindowGain = b.highCwndGain\n\t}\n\tif b.mode == DRAIN && b.GetBytesInFlight() <= b.GetTargetCongestionWindow(1) {\n\t\tb.EnterProbeBandwidthMode(now)\n\t}\n}\n\nfunc (b *bbrSender) EnterProbeBandwidthMode(now monotime.Time) {\n\tb.mode = PROBE_BW\n\tb.congestionWindowGain = b.congestionWindowGainConst\n\n\t// Pick a random offset for the gain cycle out of {0, 2..7} range. 1 is\n\t// excluded because in that case increased gain and decreased gain would not\n\t// follow each other.\n\tb.cycleCurrentOffset = randv2.Int() % (GainCycleLength - 1)\n\tif b.cycleCurrentOffset >= 1 {\n\t\tb.cycleCurrentOffset += 1\n\t}\n\n\tb.lastCycleStart = now\n\tb.pacingGain = PacingGain[b.cycleCurrentOffset]\n}\n\nfunc (b *bbrSender) MaybeEnterOrExitProbeRtt(now monotime.Time, isRoundStart, minRttExpired bool) {\n\tif minRttExpired && !b.exitingQuiescence && b.mode != PROBE_RTT {\n\t\tif b.InSlowStart() {\n\t\t\tb.OnExitStartup(now)\n\t\t}\n\t\tb.mode = PROBE_RTT\n\t\tb.pacingGain = 1.0\n\t\t// Do not decide on the time to exit PROBE_RTT until the |bytes_in_flight|\n\t\t// is at the target small value.\n\t\tb.exitProbeRttAt = monotime.Time(0)\n\t}\n\n\tif b.mode == PROBE_RTT {\n\t\tb.sampler.OnAppLimited()\n\t\tif b.exitProbeRttAt.IsZero() {\n\t\t\t// If the window has reached the appropriate size, schedule exiting\n\t\t\t// PROBE_RTT.  The CWND during PROBE_RTT is kMinimumCongestionWindow, but\n\t\t\t// we allow an extra packet since QUIC checks CWND before sending a\n\t\t\t// packet.\n\t\t\tif b.GetBytesInFlight() < b.ProbeRttCongestionWindow()+b.maxDatagramSize {\n\t\t\t\tb.exitProbeRttAt = now.Add(ProbeRttTime)\n\t\t\t\tb.probeRttRoundPassed = false\n\t\t\t}\n\t\t} else {\n\t\t\tif isRoundStart {\n\t\t\t\tb.probeRttRoundPassed = true\n\t\t\t}\n\t\t\tif !now.Before(b.exitProbeRttAt) && b.probeRttRoundPassed {\n\t\t\t\tb.minRttTimestamp = now\n\t\t\t\tif !b.isAtFullBandwidth {\n\t\t\t\t\tb.EnterStartupMode(now)\n\t\t\t\t} else {\n\t\t\t\t\tb.EnterProbeBandwidthMode(now)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\tb.exitingQuiescence = false\n}\n\nfunc (b *bbrSender) ProbeRttCongestionWindow() congestion.ByteCount {\n\tif b.probeRttBasedOnBdp {\n\t\treturn b.GetTargetCongestionWindow(ModerateProbeRttMultiplier)\n\t} else {\n\t\treturn b.minCongestionWindow()\n\t}\n}\n\nfunc (b *bbrSender) EnterStartupMode(now monotime.Time) {\n\t// if b.rttStats != nil {\n\t// TODO: slow start.\n\t// }\n\tb.mode = STARTUP\n\tb.pacingGain = b.highGain\n\tb.congestionWindowGain = b.highCwndGain\n}\n\nfunc (b *bbrSender) OnExitStartup(now monotime.Time) {\n\tif b.rttStats == nil {\n\t\treturn\n\t}\n\t// TODO: slow start.\n}\n\nfunc (b *bbrSender) CalculatePacingRate() {\n\tif b.BandwidthEstimate() == 0 {\n\t\treturn\n\t}\n\n\ttargetRate := Bandwidth(b.pacingGain * float64(b.BandwidthEstimate()))\n\tif b.isAtFullBandwidth {\n\t\tb.pacingRate = targetRate\n\t\treturn\n\t}\n\n\t// Pace at the rate of initial_window / RTT as soon as RTT measurements are\n\t// available.\n\tif b.pacingRate == 0 && b.rttStats.MinRTT() > 0 {\n\t\tb.pacingRate = BandwidthFromDelta(b.initialCongestionWindow, b.rttStats.MinRTT())\n\t\treturn\n\t}\n\t// Slow the pacing rate in STARTUP once loss has ever been detected.\n\thasEverDetectedLoss := b.endRecoveryAt > 0\n\tif b.slowerStartup && hasEverDetectedLoss && b.hasNoAppLimitedSample {\n\t\tb.pacingRate = Bandwidth(StartupAfterLossGain * float64(b.BandwidthEstimate()))\n\t\treturn\n\t}\n\n\t// Slow the pacing rate in STARTUP by the bytes_lost / CWND.\n\tif b.startupRateReductionMultiplier != 0 && hasEverDetectedLoss && b.hasNoAppLimitedSample {\n\t\tb.pacingRate = Bandwidth((1.0 - (float64(b.startupBytesLost) * float64(b.startupRateReductionMultiplier) / float64(b.congestionWindow))) * float64(targetRate))\n\t\t// Ensure the pacing rate doesn't drop below the startup growth target times\n\t\t// the bandwidth estimate.\n\t\tb.pacingRate = maxBandwidth(b.pacingRate, Bandwidth(StartupGrowthTarget*float64(b.BandwidthEstimate())))\n\t\treturn\n\t}\n\n\t// Do not decrease the pacing rate during startup.\n\tb.pacingRate = maxBandwidth(b.pacingRate, targetRate)\n}\n\nfunc (b *bbrSender) CalculateCongestionWindow(ackedBytes, excessAcked congestion.ByteCount) {\n\tif b.mode == PROBE_RTT {\n\t\treturn\n\t}\n\n\ttargetWindow := b.GetTargetCongestionWindow(b.congestionWindowGain)\n\tif b.isAtFullBandwidth {\n\t\t// Add the max recently measured ack aggregation to CWND.\n\t\ttargetWindow += congestion.ByteCount(b.maxAckHeight.GetBest())\n\t} else if b.enableAckAggregationDuringStartup {\n\t\t// Add the most recent excess acked.  Because CWND never decreases in\n\t\t// STARTUP, this will automatically create a very localized max filter.\n\t\ttargetWindow += excessAcked\n\t}\n\n\t// Instead of immediately setting the target CWND as the new one, BBR grows\n\t// the CWND towards |target_window| by only increasing it |bytes_acked| at a\n\t// time.\n\taddBytesAcked := true || !b.InRecovery()\n\tif b.isAtFullBandwidth {\n\t\tb.congestionWindow = minByteCount(targetWindow, b.congestionWindow+ackedBytes)\n\t} else if addBytesAcked && (b.congestionWindow < targetWindow || b.sampler.totalBytesAcked < b.initialCongestionWindow) {\n\t\t// If the connection is not yet out of startup phase, do not decrease the\n\t\t// window.\n\t\tb.congestionWindow += ackedBytes\n\t}\n\n\t// Enforce the limits on the congestion window.\n\tb.congestionWindow = maxByteCount(b.congestionWindow, b.minCongestionWindow())\n\tb.congestionWindow = minByteCount(b.congestionWindow, b.maxCongestionWindow())\n}\n\nfunc (b *bbrSender) CalculateRecoveryWindow(ackedBytes, lostBytes congestion.ByteCount) {\n\tif b.rateBasedStartup && b.mode == STARTUP {\n\t\treturn\n\t}\n\n\tif b.recoveryState == NOT_IN_RECOVERY {\n\t\treturn\n\t}\n\n\t// Set up the initial recovery window.\n\tif b.recoveryWindow == 0 {\n\t\tb.recoveryWindow = maxByteCount(b.GetBytesInFlight()+ackedBytes, b.minCongestionWindow())\n\t\treturn\n\t}\n\n\t// Remove losses from the recovery window, while accounting for a potential\n\t// integer underflow.\n\tif b.recoveryWindow >= lostBytes {\n\t\tb.recoveryWindow -= lostBytes\n\t} else {\n\t\tb.recoveryWindow = congestion.ByteCount(b.maxDatagramSize)\n\t}\n\t// In CONSERVATION mode, just subtracting losses is sufficient.  In GROWTH,\n\t// release additional |bytes_acked| to achieve a slow-start-like behavior.\n\tif b.recoveryState == GROWTH {\n\t\tb.recoveryWindow += ackedBytes\n\t}\n\t// Sanity checks.  Ensure that we always allow to send at least an MSS or\n\t// |bytes_acked| in response, whichever is larger.\n\tb.recoveryWindow = maxByteCount(b.recoveryWindow, b.GetBytesInFlight()+ackedBytes)\n\tb.recoveryWindow = maxByteCount(b.recoveryWindow, b.minCongestionWindow())\n}\n\nvar _ congestion.CongestionControl = (*bbrSender)(nil)\n\nfunc (b *bbrSender) GetMinRtt() time.Duration {\n\tif b.minRtt > 0 {\n\t\treturn b.minRtt\n\t} else {\n\t\treturn InitialRtt\n\t}\n}\n\nfunc minRtt(a, b time.Duration) time.Duration {\n\tif a < b {\n\t\treturn a\n\t} else {\n\t\treturn b\n\t}\n}\n\nfunc minBandwidth(a, b Bandwidth) Bandwidth {\n\tif a < b {\n\t\treturn a\n\t} else {\n\t\treturn b\n\t}\n}\n\nfunc maxBandwidth(a, b Bandwidth) Bandwidth {\n\tif a > b {\n\t\treturn a\n\t} else {\n\t\treturn b\n\t}\n}\n\nfunc maxByteCount(a, b congestion.ByteCount) congestion.ByteCount {\n\tif a > b {\n\t\treturn a\n\t} else {\n\t\treturn b\n\t}\n}\n\nfunc minByteCount(a, b congestion.ByteCount) congestion.ByteCount {\n\tif a < b {\n\t\treturn a\n\t} else {\n\t\treturn b\n\t}\n}\n\nvar (\n\tInfiniteRTT = time.Duration(math.MaxInt64)\n)\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/congestion/cubic.go",
    "content": "package congestion\n\nimport (\n\t\"math\"\n\t\"time\"\n\n\t\"github.com/metacubex/quic-go/congestion\"\n\t\"github.com/metacubex/quic-go/monotime\"\n)\n\n// This cubic implementation is based on the one found in Chromiums's QUIC\n// implementation, in the files net/quic/congestion_control/cubic.{hh,cc}.\n\n// Constants based on TCP defaults.\n// The following constants are in 2^10 fractions of a second instead of ms to\n// allow a 10 shift right to divide.\n\n// 1024*1024^3 (first 1024 is from 0.100^3)\n// where 0.100 is 100 ms which is the scaling round trip time.\nconst (\n\tcubeScale                                      = 40\n\tcubeCongestionWindowScale                      = 410\n\tcubeFactor                congestion.ByteCount = 1 << cubeScale / cubeCongestionWindowScale / maxDatagramSize\n\t// TODO: when re-enabling cubic, make sure to use the actual packet size here\n\tmaxDatagramSize = congestion.ByteCount(InitialPacketSize)\n)\n\nconst defaultNumConnections = 1\n\n// Default Cubic backoff factor\nconst beta float32 = 0.7\n\n// Additional backoff factor when loss occurs in the concave part of the Cubic\n// curve. This additional backoff factor is expected to give up bandwidth to\n// new concurrent flows and speed up convergence.\nconst betaLastMax float32 = 0.85\n\n// Cubic implements the cubic algorithm from TCP\ntype Cubic struct {\n\t// Number of connections to simulate.\n\tnumConnections int\n\n\t// Time when this cycle started, after last loss event.\n\tepoch monotime.Time\n\n\t// Max congestion window used just before last loss event.\n\t// Note: to improve fairness to other streams an additional back off is\n\t// applied to this value if the new value is below our latest value.\n\tlastMaxCongestionWindow congestion.ByteCount\n\n\t// Number of acked bytes since the cycle started (epoch).\n\tackedBytesCount congestion.ByteCount\n\n\t// TCP Reno equivalent congestion window in packets.\n\testimatedTCPcongestionWindow congestion.ByteCount\n\n\t// Origin point of cubic function.\n\toriginPointCongestionWindow congestion.ByteCount\n\n\t// Time to origin point of cubic function in 2^10 fractions of a second.\n\ttimeToOriginPoint uint32\n\n\t// Last congestion window in packets computed by cubic function.\n\tlastTargetCongestionWindow congestion.ByteCount\n}\n\n// NewCubic returns a new Cubic instance\nfunc NewCubic() *Cubic {\n\tc := &Cubic{\n\t\tnumConnections: defaultNumConnections,\n\t}\n\tc.Reset()\n\treturn c\n}\n\n// Reset is called after a timeout to reset the cubic state\nfunc (c *Cubic) Reset() {\n\tc.epoch = monotime.Time(0)\n\tc.lastMaxCongestionWindow = 0\n\tc.ackedBytesCount = 0\n\tc.estimatedTCPcongestionWindow = 0\n\tc.originPointCongestionWindow = 0\n\tc.timeToOriginPoint = 0\n\tc.lastTargetCongestionWindow = 0\n}\n\nfunc (c *Cubic) alpha() float32 {\n\t// TCPFriendly alpha is described in Section 3.3 of the CUBIC paper. Note that\n\t// beta here is a cwnd multiplier, and is equal to 1-beta from the paper.\n\t// We derive the equivalent alpha for an N-connection emulation as:\n\tb := c.beta()\n\treturn 3 * float32(c.numConnections) * float32(c.numConnections) * (1 - b) / (1 + b)\n}\n\nfunc (c *Cubic) beta() float32 {\n\t// kNConnectionBeta is the backoff factor after loss for our N-connection\n\t// emulation, which emulates the effective backoff of an ensemble of N\n\t// TCP-Reno connections on a single loss event. The effective multiplier is\n\t// computed as:\n\treturn (float32(c.numConnections) - 1 + beta) / float32(c.numConnections)\n}\n\nfunc (c *Cubic) betaLastMax() float32 {\n\t// betaLastMax is the additional backoff factor after loss for our\n\t// N-connection emulation, which emulates the additional backoff of\n\t// an ensemble of N TCP-Reno connections on a single loss event. The\n\t// effective multiplier is computed as:\n\treturn (float32(c.numConnections) - 1 + betaLastMax) / float32(c.numConnections)\n}\n\n// OnApplicationLimited is called on ack arrival when sender is unable to use\n// the available congestion window. Resets Cubic state during quiescence.\nfunc (c *Cubic) OnApplicationLimited() {\n\t// When sender is not using the available congestion window, the window does\n\t// not grow. But to be RTT-independent, Cubic assumes that the sender has been\n\t// using the entire window during the time since the beginning of the current\n\t// \"epoch\" (the end of the last loss recovery period). Since\n\t// application-limited periods break this assumption, we reset the epoch when\n\t// in such a period. This reset effectively freezes congestion window growth\n\t// through application-limited periods and allows Cubic growth to continue\n\t// when the entire window is being used.\n\tc.epoch = monotime.Time(0)\n}\n\n// CongestionWindowAfterPacketLoss computes a new congestion window to use after\n// a loss event. Returns the new congestion window in packets. The new\n// congestion window is a multiplicative decrease of our current window.\nfunc (c *Cubic) CongestionWindowAfterPacketLoss(currentCongestionWindow congestion.ByteCount) congestion.ByteCount {\n\tif currentCongestionWindow+maxDatagramSize < c.lastMaxCongestionWindow {\n\t\t// We never reached the old max, so assume we are competing with another\n\t\t// flow. Use our extra back off factor to allow the other flow to go up.\n\t\tc.lastMaxCongestionWindow = congestion.ByteCount(c.betaLastMax() * float32(currentCongestionWindow))\n\t} else {\n\t\tc.lastMaxCongestionWindow = currentCongestionWindow\n\t}\n\tc.epoch = monotime.Time(0) // Reset time.\n\treturn congestion.ByteCount(float32(currentCongestionWindow) * c.beta())\n}\n\n// CongestionWindowAfterAck computes a new congestion window to use after a received ACK.\n// Returns the new congestion window in packets. The new congestion window\n// follows a cubic function that depends on the time passed since last\n// packet loss.\nfunc (c *Cubic) CongestionWindowAfterAck(\n\tackedBytes congestion.ByteCount,\n\tcurrentCongestionWindow congestion.ByteCount,\n\tdelayMin time.Duration,\n\teventTime monotime.Time,\n) congestion.ByteCount {\n\tc.ackedBytesCount += ackedBytes\n\n\tif c.epoch.IsZero() {\n\t\t// First ACK after a loss event.\n\t\tc.epoch = eventTime            // Start of epoch.\n\t\tc.ackedBytesCount = ackedBytes // Reset count.\n\t\t// Reset estimated_tcp_congestion_window_ to be in sync with cubic.\n\t\tc.estimatedTCPcongestionWindow = currentCongestionWindow\n\t\tif c.lastMaxCongestionWindow <= currentCongestionWindow {\n\t\t\tc.timeToOriginPoint = 0\n\t\t\tc.originPointCongestionWindow = currentCongestionWindow\n\t\t} else {\n\t\t\tc.timeToOriginPoint = uint32(math.Cbrt(float64(cubeFactor * (c.lastMaxCongestionWindow - currentCongestionWindow))))\n\t\t\tc.originPointCongestionWindow = c.lastMaxCongestionWindow\n\t\t}\n\t}\n\n\t// Change the time unit from microseconds to 2^10 fractions per second. Take\n\t// the round trip time in account. This is done to allow us to use shift as a\n\t// divide operator.\n\telapsedTime := int64(eventTime.Add(delayMin).Sub(c.epoch)/time.Microsecond) << 10 / (1000 * 1000)\n\n\t// Right-shifts of negative, signed numbers have implementation-dependent\n\t// behavior, so force the offset to be positive, as is done in the kernel.\n\toffset := int64(c.timeToOriginPoint) - elapsedTime\n\tif offset < 0 {\n\t\toffset = -offset\n\t}\n\n\tdeltaCongestionWindow := congestion.ByteCount(cubeCongestionWindowScale*offset*offset*offset) * maxDatagramSize >> cubeScale\n\tvar targetCongestionWindow congestion.ByteCount\n\tif elapsedTime > int64(c.timeToOriginPoint) {\n\t\ttargetCongestionWindow = c.originPointCongestionWindow + deltaCongestionWindow\n\t} else {\n\t\ttargetCongestionWindow = c.originPointCongestionWindow - deltaCongestionWindow\n\t}\n\t// Limit the CWND increase to half the acked bytes.\n\ttargetCongestionWindow = Min(targetCongestionWindow, currentCongestionWindow+c.ackedBytesCount/2)\n\n\t// Increase the window by approximately Alpha * 1 MSS of bytes every\n\t// time we ack an estimated tcp window of bytes.  For small\n\t// congestion windows (less than 25), the formula below will\n\t// increase slightly slower than linearly per estimated tcp window\n\t// of bytes.\n\tc.estimatedTCPcongestionWindow += congestion.ByteCount(float32(c.ackedBytesCount) * c.alpha() * float32(maxDatagramSize) / float32(c.estimatedTCPcongestionWindow))\n\tc.ackedBytesCount = 0\n\n\t// We have a new cubic congestion window.\n\tc.lastTargetCongestionWindow = targetCongestionWindow\n\n\t// Compute target congestion_window based on cubic target and estimated TCP\n\t// congestion_window, use highest (fastest).\n\tif targetCongestionWindow < c.estimatedTCPcongestionWindow {\n\t\ttargetCongestionWindow = c.estimatedTCPcongestionWindow\n\t}\n\treturn targetCongestionWindow\n}\n\n// SetNumConnections sets the number of emulated connections\nfunc (c *Cubic) SetNumConnections(n int) {\n\tc.numConnections = n\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/congestion/cubic_sender.go",
    "content": "package congestion\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/metacubex/quic-go/congestion\"\n\t\"github.com/metacubex/quic-go/monotime\"\n)\n\nconst (\n\tmaxBurstPackets            = 3\n\trenoBeta                   = 0.7 // Reno backoff factor.\n\tminCongestionWindowPackets = 2\n\tinitialCongestionWindow    = 32\n)\n\nconst InvalidPacketNumber congestion.PacketNumber = -1\nconst MaxCongestionWindowPackets = 20000\nconst MaxByteCount = congestion.ByteCount(1<<62 - 1)\n\ntype cubicSender struct {\n\thybridSlowStart HybridSlowStart\n\trttStats        congestion.RTTStatsProvider\n\tcubic           *Cubic\n\tpacer           *pacer\n\n\treno bool\n\n\t// Track the largest packet that has been sent.\n\tlargestSentPacketNumber congestion.PacketNumber\n\n\t// Track the largest packet that has been acked.\n\tlargestAckedPacketNumber congestion.PacketNumber\n\n\t// Track the largest packet number outstanding when a CWND cutback occurs.\n\tlargestSentAtLastCutback congestion.PacketNumber\n\n\t// Whether the last loss event caused us to exit slowstart.\n\t// Used for stats collection of slowstartPacketsLost\n\tlastCutbackExitedSlowstart bool\n\n\t// Congestion window in bytes.\n\tcongestionWindow congestion.ByteCount\n\n\t// Slow start congestion window in bytes, aka ssthresh.\n\tslowStartThreshold congestion.ByteCount\n\n\t// ACK counter for the Reno implementation.\n\tnumAckedPackets uint64\n\n\tinitialCongestionWindow    congestion.ByteCount\n\tinitialMaxCongestionWindow congestion.ByteCount\n\n\tmaxDatagramSize congestion.ByteCount\n}\n\nvar (\n\t_ congestion.CongestionControl = &cubicSender{}\n)\n\n// NewCubicSender makes a new cubic sender\nfunc NewCubicSender(\n\tinitialMaxDatagramSize congestion.ByteCount,\n\treno bool,\n) *cubicSender {\n\treturn newCubicSender(\n\t\treno,\n\t\tinitialMaxDatagramSize,\n\t\tinitialCongestionWindow*initialMaxDatagramSize,\n\t\tMaxCongestionWindowPackets*initialMaxDatagramSize,\n\t)\n}\n\nfunc newCubicSender(\n\treno bool,\n\tinitialMaxDatagramSize,\n\tinitialCongestionWindow,\n\tinitialMaxCongestionWindow congestion.ByteCount,\n) *cubicSender {\n\tc := &cubicSender{\n\t\tlargestSentPacketNumber:    InvalidPacketNumber,\n\t\tlargestAckedPacketNumber:   InvalidPacketNumber,\n\t\tlargestSentAtLastCutback:   InvalidPacketNumber,\n\t\tinitialCongestionWindow:    initialCongestionWindow,\n\t\tinitialMaxCongestionWindow: initialMaxCongestionWindow,\n\t\tcongestionWindow:           initialCongestionWindow,\n\t\tslowStartThreshold:         MaxByteCount,\n\t\tcubic:                      NewCubic(),\n\t\treno:                       reno,\n\t\tmaxDatagramSize:            initialMaxDatagramSize,\n\t}\n\tc.pacer = newPacer(c.BandwidthEstimate)\n\treturn c\n}\n\nfunc (c *cubicSender) SetRTTStatsProvider(provider congestion.RTTStatsProvider) {\n\tc.rttStats = provider\n}\n\n// TimeUntilSend returns when the next packet should be sent.\nfunc (c *cubicSender) TimeUntilSend(_ congestion.ByteCount) monotime.Time {\n\treturn c.pacer.TimeUntilSend()\n}\n\nfunc (c *cubicSender) HasPacingBudget(now monotime.Time) bool {\n\treturn c.pacer.Budget(now) >= c.maxDatagramSize\n}\n\nfunc (c *cubicSender) maxCongestionWindow() congestion.ByteCount {\n\treturn c.maxDatagramSize * MaxCongestionWindowPackets\n}\n\nfunc (c *cubicSender) minCongestionWindow() congestion.ByteCount {\n\treturn c.maxDatagramSize * minCongestionWindowPackets\n}\n\nfunc (c *cubicSender) OnPacketSent(\n\tsentTime monotime.Time,\n\t_ congestion.ByteCount,\n\tpacketNumber congestion.PacketNumber,\n\tbytes congestion.ByteCount,\n\tisRetransmittable bool,\n) {\n\tc.pacer.SentPacket(sentTime, bytes)\n\tif !isRetransmittable {\n\t\treturn\n\t}\n\tc.largestSentPacketNumber = packetNumber\n\tc.hybridSlowStart.OnPacketSent(packetNumber)\n}\n\nfunc (c *cubicSender) CanSend(bytesInFlight congestion.ByteCount) bool {\n\treturn bytesInFlight < c.GetCongestionWindow()\n}\n\nfunc (c *cubicSender) InRecovery() bool {\n\treturn c.largestAckedPacketNumber != InvalidPacketNumber && c.largestAckedPacketNumber <= c.largestSentAtLastCutback\n}\n\nfunc (c *cubicSender) InSlowStart() bool {\n\treturn c.GetCongestionWindow() < c.slowStartThreshold\n}\n\nfunc (c *cubicSender) GetCongestionWindow() congestion.ByteCount {\n\treturn c.congestionWindow\n}\n\nfunc (c *cubicSender) MaybeExitSlowStart() {\n\tif c.InSlowStart() &&\n\t\tc.hybridSlowStart.ShouldExitSlowStart(c.rttStats.LatestRTT(), c.rttStats.MinRTT(), c.GetCongestionWindow()/c.maxDatagramSize) {\n\t\t// exit slow start\n\t\tc.slowStartThreshold = c.congestionWindow\n\t}\n}\n\nfunc (c *cubicSender) OnPacketAcked(\n\tackedPacketNumber congestion.PacketNumber,\n\tackedBytes congestion.ByteCount,\n\tpriorInFlight congestion.ByteCount,\n\teventTime monotime.Time,\n) {\n\tc.largestAckedPacketNumber = Max(ackedPacketNumber, c.largestAckedPacketNumber)\n\tif c.InRecovery() {\n\t\treturn\n\t}\n\tc.maybeIncreaseCwnd(ackedPacketNumber, ackedBytes, priorInFlight, eventTime)\n\tif c.InSlowStart() {\n\t\tc.hybridSlowStart.OnPacketAcked(ackedPacketNumber)\n\t}\n}\n\nfunc (c *cubicSender) OnCongestionEvent(packetNumber congestion.PacketNumber, lostBytes, priorInFlight congestion.ByteCount) {\n\t// TCP NewReno (RFC6582) says that once a loss occurs, any losses in packets\n\t// already sent should be treated as a single loss event, since it's expected.\n\tif packetNumber <= c.largestSentAtLastCutback {\n\t\treturn\n\t}\n\tc.lastCutbackExitedSlowstart = c.InSlowStart()\n\n\tif c.reno {\n\t\tc.congestionWindow = congestion.ByteCount(float64(c.congestionWindow) * renoBeta)\n\t} else {\n\t\tc.congestionWindow = c.cubic.CongestionWindowAfterPacketLoss(c.congestionWindow)\n\t}\n\tif minCwnd := c.minCongestionWindow(); c.congestionWindow < minCwnd {\n\t\tc.congestionWindow = minCwnd\n\t}\n\tc.slowStartThreshold = c.congestionWindow\n\tc.largestSentAtLastCutback = c.largestSentPacketNumber\n\t// reset packet count from congestion avoidance mode. We start\n\t// counting again when we're out of recovery.\n\tc.numAckedPackets = 0\n}\n\nfunc (b *cubicSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, eventTime monotime.Time, ackedPackets []congestion.AckedPacketInfo, lostPackets []congestion.LostPacketInfo) {\n\t// Stub\n}\n\n// Called when we receive an ack. Normal TCP tracks how many packets one ack\n// represents, but quic has a separate ack for each packet.\nfunc (c *cubicSender) maybeIncreaseCwnd(\n\t_ congestion.PacketNumber,\n\tackedBytes congestion.ByteCount,\n\tpriorInFlight congestion.ByteCount,\n\teventTime monotime.Time,\n) {\n\t// Do not increase the congestion window unless the sender is close to using\n\t// the current window.\n\tif !c.isCwndLimited(priorInFlight) {\n\t\tc.cubic.OnApplicationLimited()\n\t\treturn\n\t}\n\tif c.congestionWindow >= c.maxCongestionWindow() {\n\t\treturn\n\t}\n\tif c.InSlowStart() {\n\t\t// TCP slow start, exponential growth, increase by one for each ACK.\n\t\tc.congestionWindow += c.maxDatagramSize\n\t\treturn\n\t}\n\t// Congestion avoidance\n\tif c.reno {\n\t\t// Classic Reno congestion avoidance.\n\t\tc.numAckedPackets++\n\t\tif c.numAckedPackets >= uint64(c.congestionWindow/c.maxDatagramSize) {\n\t\t\tc.congestionWindow += c.maxDatagramSize\n\t\t\tc.numAckedPackets = 0\n\t\t}\n\t} else {\n\t\tc.congestionWindow = Min(c.maxCongestionWindow(), c.cubic.CongestionWindowAfterAck(ackedBytes, c.congestionWindow, c.rttStats.MinRTT(), eventTime))\n\t}\n}\n\nfunc (c *cubicSender) isCwndLimited(bytesInFlight congestion.ByteCount) bool {\n\tcongestionWindow := c.GetCongestionWindow()\n\tif bytesInFlight >= congestionWindow {\n\t\treturn true\n\t}\n\tavailableBytes := congestionWindow - bytesInFlight\n\tslowStartLimited := c.InSlowStart() && bytesInFlight > congestionWindow/2\n\treturn slowStartLimited || availableBytes <= maxBurstPackets*c.maxDatagramSize\n}\n\n// BandwidthEstimate returns the current bandwidth estimate\nfunc (c *cubicSender) BandwidthEstimate() Bandwidth {\n\tif c.rttStats == nil {\n\t\treturn infBandwidth\n\t}\n\tsrtt := c.rttStats.SmoothedRTT()\n\tif srtt == 0 {\n\t\t// If we haven't measured an rtt, the bandwidth estimate is unknown.\n\t\treturn infBandwidth\n\t}\n\treturn BandwidthFromDelta(c.GetCongestionWindow(), srtt)\n}\n\n// OnRetransmissionTimeout is called on an retransmission timeout\nfunc (c *cubicSender) OnRetransmissionTimeout(packetsRetransmitted bool) {\n\tc.largestSentAtLastCutback = InvalidPacketNumber\n\tif !packetsRetransmitted {\n\t\treturn\n\t}\n\tc.hybridSlowStart.Restart()\n\tc.cubic.Reset()\n\tc.slowStartThreshold = c.congestionWindow / 2\n\tc.congestionWindow = c.minCongestionWindow()\n}\n\n// OnConnectionMigration is called when the connection is migrated (?)\nfunc (c *cubicSender) OnConnectionMigration() {\n\tc.hybridSlowStart.Restart()\n\tc.largestSentPacketNumber = InvalidPacketNumber\n\tc.largestAckedPacketNumber = InvalidPacketNumber\n\tc.largestSentAtLastCutback = InvalidPacketNumber\n\tc.lastCutbackExitedSlowstart = false\n\tc.cubic.Reset()\n\tc.numAckedPackets = 0\n\tc.congestionWindow = c.initialCongestionWindow\n\tc.slowStartThreshold = c.initialMaxCongestionWindow\n}\n\nfunc (c *cubicSender) SetMaxDatagramSize(s congestion.ByteCount) {\n\tif s < c.maxDatagramSize {\n\t\tpanic(fmt.Sprintf(\"congestion BUG: decreased max datagram size from %d to %d\", c.maxDatagramSize, s))\n\t}\n\tcwndIsMinCwnd := c.congestionWindow == c.minCongestionWindow()\n\tc.maxDatagramSize = s\n\tif cwndIsMinCwnd {\n\t\tc.congestionWindow = c.minCongestionWindow()\n\t}\n\tc.pacer.SetMaxDatagramSize(s)\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/congestion/hybrid_slow_start.go",
    "content": "package congestion\n\nimport (\n\t\"time\"\n\n\t\"github.com/metacubex/quic-go/congestion\"\n)\n\n// Note(pwestin): the magic clamping numbers come from the original code in\n// tcp_cubic.c.\nconst hybridStartLowWindow = congestion.ByteCount(16)\n\n// Number of delay samples for detecting the increase of delay.\nconst hybridStartMinSamples = uint32(8)\n\n// Exit slow start if the min rtt has increased by more than 1/8th.\nconst hybridStartDelayFactorExp = 3 // 2^3 = 8\n// The original paper specifies 2 and 8ms, but those have changed over time.\nconst (\n\thybridStartDelayMinThresholdUs = int64(4000)\n\thybridStartDelayMaxThresholdUs = int64(16000)\n)\n\n// HybridSlowStart implements the TCP hybrid slow start algorithm\ntype HybridSlowStart struct {\n\tendPacketNumber      congestion.PacketNumber\n\tlastSentPacketNumber congestion.PacketNumber\n\tstarted              bool\n\tcurrentMinRTT        time.Duration\n\trttSampleCount       uint32\n\thystartFound         bool\n}\n\n// StartReceiveRound is called for the start of each receive round (burst) in the slow start phase.\nfunc (s *HybridSlowStart) StartReceiveRound(lastSent congestion.PacketNumber) {\n\ts.endPacketNumber = lastSent\n\ts.currentMinRTT = 0\n\ts.rttSampleCount = 0\n\ts.started = true\n}\n\n// IsEndOfRound returns true if this ack is the last packet number of our current slow start round.\nfunc (s *HybridSlowStart) IsEndOfRound(ack congestion.PacketNumber) bool {\n\treturn s.endPacketNumber < ack\n}\n\n// ShouldExitSlowStart should be called on every new ack frame, since a new\n// RTT measurement can be made then.\n// rtt: the RTT for this ack packet.\n// minRTT: is the lowest delay (RTT) we have seen during the session.\n// congestionWindow: the congestion window in packets.\nfunc (s *HybridSlowStart) ShouldExitSlowStart(latestRTT time.Duration, minRTT time.Duration, congestionWindow congestion.ByteCount) bool {\n\tif !s.started {\n\t\t// Time to start the hybrid slow start.\n\t\ts.StartReceiveRound(s.lastSentPacketNumber)\n\t}\n\tif s.hystartFound {\n\t\treturn true\n\t}\n\t// Second detection parameter - delay increase detection.\n\t// Compare the minimum delay (s.currentMinRTT) of the current\n\t// burst of packets relative to the minimum delay during the session.\n\t// Note: we only look at the first few(8) packets in each burst, since we\n\t// only want to compare the lowest RTT of the burst relative to previous\n\t// bursts.\n\ts.rttSampleCount++\n\tif s.rttSampleCount <= hybridStartMinSamples {\n\t\tif s.currentMinRTT == 0 || s.currentMinRTT > latestRTT {\n\t\t\ts.currentMinRTT = latestRTT\n\t\t}\n\t}\n\t// We only need to check this once per round.\n\tif s.rttSampleCount == hybridStartMinSamples {\n\t\t// Divide minRTT by 8 to get a rtt increase threshold for exiting.\n\t\tminRTTincreaseThresholdUs := int64(minRTT / time.Microsecond >> hybridStartDelayFactorExp)\n\t\t// Ensure the rtt threshold is never less than 2ms or more than 16ms.\n\t\tminRTTincreaseThresholdUs = Min(minRTTincreaseThresholdUs, hybridStartDelayMaxThresholdUs)\n\t\tminRTTincreaseThreshold := time.Duration(Max(minRTTincreaseThresholdUs, hybridStartDelayMinThresholdUs)) * time.Microsecond\n\n\t\tif s.currentMinRTT > (minRTT + minRTTincreaseThreshold) {\n\t\t\ts.hystartFound = true\n\t\t}\n\t}\n\t// Exit from slow start if the cwnd is greater than 16 and\n\t// increasing delay is found.\n\treturn congestionWindow >= hybridStartLowWindow && s.hystartFound\n}\n\n// OnPacketSent is called when a packet was sent\nfunc (s *HybridSlowStart) OnPacketSent(packetNumber congestion.PacketNumber) {\n\ts.lastSentPacketNumber = packetNumber\n}\n\n// OnPacketAcked gets invoked after ShouldExitSlowStart, so it's best to end\n// the round when the final packet of the burst is received and start it on\n// the next incoming ack.\nfunc (s *HybridSlowStart) OnPacketAcked(ackedPacketNumber congestion.PacketNumber) {\n\tif s.IsEndOfRound(ackedPacketNumber) {\n\t\ts.started = false\n\t}\n}\n\n// Started returns true if started\nfunc (s *HybridSlowStart) Started() bool {\n\treturn s.started\n}\n\n// Restart the slow start phase\nfunc (s *HybridSlowStart) Restart() {\n\ts.started = false\n\ts.hystartFound = false\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/congestion/minmax.go",
    "content": "package congestion\n\nimport (\n\t\"math\"\n\t\"time\"\n)\n\n// InfDuration is a duration of infinite length\nconst InfDuration = time.Duration(math.MaxInt64)\n\n// MinNonZeroDuration return the minimum duration that's not zero.\nfunc MinNonZeroDuration(a, b time.Duration) time.Duration {\n\tif a == 0 {\n\t\treturn b\n\t}\n\tif b == 0 {\n\t\treturn a\n\t}\n\treturn Min(a, b)\n}\n\n// AbsDuration returns the absolute value of a time duration\nfunc AbsDuration(d time.Duration) time.Duration {\n\tif d >= 0 {\n\t\treturn d\n\t}\n\treturn -d\n}\n\n// MinTime returns the earlier time\nfunc MinTime(a, b time.Time) time.Time {\n\tif a.After(b) {\n\t\treturn b\n\t}\n\treturn a\n}\n\n// MinNonZeroTime returns the earlist time that is not time.Time{}\n// If both a and b are time.Time{}, it returns time.Time{}\nfunc MinNonZeroTime(a, b time.Time) time.Time {\n\tif a.IsZero() {\n\t\treturn b\n\t}\n\tif b.IsZero() {\n\t\treturn a\n\t}\n\treturn MinTime(a, b)\n}\n\n// MaxTime returns the later time\nfunc MaxTime(a, b time.Time) time.Time {\n\tif a.After(b) {\n\t\treturn a\n\t}\n\treturn b\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/congestion/minmax_go120.go",
    "content": "//go:build !go1.21\n\npackage congestion\n\nimport \"golang.org/x/exp/constraints\"\n\nfunc Max[T constraints.Ordered](a, b T) T {\n\tif a < b {\n\t\treturn b\n\t}\n\treturn a\n}\n\nfunc Min[T constraints.Ordered](a, b T) T {\n\tif a < b {\n\t\treturn a\n\t}\n\treturn b\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/congestion/minmax_go121.go",
    "content": "//go:build go1.21\n\npackage congestion\n\nimport \"cmp\"\n\nfunc Max[T cmp.Ordered](a, b T) T {\n\treturn max(a, b)\n}\n\nfunc Min[T cmp.Ordered](a, b T) T {\n\treturn min(a, b)\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/congestion/pacer.go",
    "content": "package congestion\n\nimport (\n\t\"math\"\n\t\"time\"\n\n\t\"github.com/metacubex/quic-go/congestion\"\n\t\"github.com/metacubex/quic-go/monotime\"\n)\n\nconst initialMaxDatagramSize = congestion.ByteCount(1252)\nconst MinPacingDelay = time.Millisecond\nconst TimerGranularity = time.Millisecond\nconst maxBurstSizePackets = 10\n\n// The pacer implements a token bucket pacing algorithm.\ntype pacer struct {\n\tbudgetAtLastSent     congestion.ByteCount\n\tmaxDatagramSize      congestion.ByteCount\n\tlastSentTime         monotime.Time\n\tgetAdjustedBandwidth func() uint64 // in bytes/s\n}\n\nfunc newPacer(getBandwidth func() Bandwidth) *pacer {\n\tp := &pacer{\n\t\tmaxDatagramSize: initialMaxDatagramSize,\n\t\tgetAdjustedBandwidth: func() uint64 {\n\t\t\t// Bandwidth is in bits/s. We need the value in bytes/s.\n\t\t\tbw := uint64(getBandwidth() / BytesPerSecond)\n\t\t\t// Use a slightly higher value than the actual measured bandwidth.\n\t\t\t// RTT variations then won't result in under-utilization of the congestion window.\n\t\t\t// Ultimately, this will  result in sending packets as acknowledgments are received rather than when timers fire,\n\t\t\t// provided the congestion window is fully utilized and acknowledgments arrive at regular intervals.\n\t\t\treturn bw * 5 / 4\n\t\t},\n\t}\n\tp.budgetAtLastSent = p.maxBurstSize()\n\treturn p\n}\n\nfunc (p *pacer) SentPacket(sendTime monotime.Time, size congestion.ByteCount) {\n\tbudget := p.Budget(sendTime)\n\tif size > budget {\n\t\tp.budgetAtLastSent = 0\n\t} else {\n\t\tp.budgetAtLastSent = budget - size\n\t}\n\tp.lastSentTime = sendTime\n}\n\nfunc (p *pacer) Budget(now monotime.Time) congestion.ByteCount {\n\tif p.lastSentTime.IsZero() {\n\t\treturn p.maxBurstSize()\n\t}\n\tbudget := p.budgetAtLastSent + (congestion.ByteCount(p.getAdjustedBandwidth())*congestion.ByteCount(now.Sub(p.lastSentTime).Nanoseconds()))/1e9\n\treturn Min(p.maxBurstSize(), budget)\n}\n\nfunc (p *pacer) maxBurstSize() congestion.ByteCount {\n\treturn Max(\n\t\tcongestion.ByteCount(uint64((MinPacingDelay+TimerGranularity).Nanoseconds())*p.getAdjustedBandwidth())/1e9,\n\t\tmaxBurstSizePackets*p.maxDatagramSize,\n\t)\n}\n\n// TimeUntilSend returns when the next packet should be sent.\n// It returns the zero value of monotime.Time if a packet can be sent immediately.\nfunc (p *pacer) TimeUntilSend() monotime.Time {\n\tif p.budgetAtLastSent >= p.maxDatagramSize {\n\t\treturn monotime.Time(0)\n\t}\n\treturn p.lastSentTime.Add(Max(\n\t\tMinPacingDelay,\n\t\ttime.Duration(math.Ceil(float64(p.maxDatagramSize-p.budgetAtLastSent)*1e9/float64(p.getAdjustedBandwidth())))*time.Nanosecond,\n\t))\n}\n\nfunc (p *pacer) SetMaxDatagramSize(s congestion.ByteCount) {\n\tp.maxDatagramSize = s\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/congestion/windowed_filter.go",
    "content": "package congestion\n\n// WindowedFilter Use the following to construct a windowed filter object of type T.\n// For example, a min filter using QuicTime as the time type:\n//\n//\tWindowedFilter<T, MinFilter<T>, QuicTime, QuicTime::Delta> ObjectName;\n//\n// A max filter using 64-bit integers as the time type:\n//\n//\tWindowedFilter<T, MaxFilter<T>, uint64_t, int64_t> ObjectName;\n//\n// Specifically, this template takes four arguments:\n//  1. T -- type of the measurement that is being filtered.\n//  2. Compare -- MinFilter<T> or MaxFilter<T>, depending on the type of filter\n//     desired.\n//  3. TimeT -- the type used to represent timestamps.\n//  4. TimeDeltaT -- the type used to represent continuous time intervals between\n//     two timestamps.  Has to be the type of (a - b) if both |a| and |b| are\n//     of type TimeT.\ntype WindowedFilter struct {\n\t// Time length of window.\n\twindowLength int64\n\testimates    []Sample\n\tcomparator   func(int64, int64) bool\n}\n\ntype Sample struct {\n\tsample int64\n\ttime   int64\n}\n\n// Compares two values and returns true if the first is greater than or equal\n// to the second.\nfunc MaxFilter(a, b int64) bool {\n\treturn a >= b\n}\n\n// Compares two values and returns true if the first is less than or equal\n// to the second.\nfunc MinFilter(a, b int64) bool {\n\treturn a <= b\n}\n\nfunc NewWindowedFilter(windowLength int64, comparator func(int64, int64) bool) *WindowedFilter {\n\treturn &WindowedFilter{\n\t\twindowLength: windowLength,\n\t\testimates:    make([]Sample, 3),\n\t\tcomparator:   comparator,\n\t}\n}\n\n// Changes the window length.  Does not update any current samples.\nfunc (f *WindowedFilter) SetWindowLength(windowLength int64) {\n\tf.windowLength = windowLength\n}\n\nfunc (f *WindowedFilter) GetBest() int64 {\n\treturn f.estimates[0].sample\n}\n\nfunc (f *WindowedFilter) GetSecondBest() int64 {\n\treturn f.estimates[1].sample\n}\n\nfunc (f *WindowedFilter) GetThirdBest() int64 {\n\treturn f.estimates[2].sample\n}\n\nfunc (f *WindowedFilter) Update(sample int64, time int64) {\n\tif f.estimates[0].time == 0 || f.comparator(sample, f.estimates[0].sample) || (time-f.estimates[2].time) > f.windowLength {\n\t\tf.Reset(sample, time)\n\t\treturn\n\t}\n\n\tif f.comparator(sample, f.estimates[1].sample) {\n\t\tf.estimates[1].sample = sample\n\t\tf.estimates[1].time = time\n\t\tf.estimates[2].sample = sample\n\t\tf.estimates[2].time = time\n\t} else if f.comparator(sample, f.estimates[2].sample) {\n\t\tf.estimates[2].sample = sample\n\t\tf.estimates[2].time = time\n\t}\n\n\t// Expire and update estimates as necessary.\n\tif time-f.estimates[0].time > f.windowLength {\n\t\t// The best estimate hasn't been updated for an entire window, so promote\n\t\t// second and third best estimates.\n\t\tf.estimates[0].sample = f.estimates[1].sample\n\t\tf.estimates[0].time = f.estimates[1].time\n\t\tf.estimates[1].sample = f.estimates[2].sample\n\t\tf.estimates[1].time = f.estimates[2].time\n\t\tf.estimates[2].sample = sample\n\t\tf.estimates[2].time = time\n\t\t// Need to iterate one more time. Check if the new best estimate is\n\t\t// outside the window as well, since it may also have been recorded a\n\t\t// long time ago. Don't need to iterate once more since we cover that\n\t\t// case at the beginning of the method.\n\t\tif time-f.estimates[0].time > f.windowLength {\n\t\t\tf.estimates[0].sample = f.estimates[1].sample\n\t\t\tf.estimates[0].time = f.estimates[1].time\n\t\t\tf.estimates[1].sample = f.estimates[2].sample\n\t\t\tf.estimates[1].time = f.estimates[2].time\n\t\t}\n\t\treturn\n\t}\n\tif f.estimates[1].sample == f.estimates[0].sample && time-f.estimates[1].time > f.windowLength>>2 {\n\t\t// A quarter of the window has passed without a better sample, so the\n\t\t// second-best estimate is taken from the second quarter of the window.\n\t\tf.estimates[1].sample = sample\n\t\tf.estimates[1].time = time\n\t\tf.estimates[2].sample = sample\n\t\tf.estimates[2].time = time\n\t\treturn\n\t}\n\n\tif f.estimates[2].sample == f.estimates[1].sample && time-f.estimates[2].time > f.windowLength>>1 {\n\t\t// We've passed a half of the window without a better estimate, so take\n\t\t// a third-best estimate from the second half of the window.\n\t\tf.estimates[2].sample = sample\n\t\tf.estimates[2].time = time\n\t}\n}\n\nfunc (f *WindowedFilter) Reset(newSample int64, newTime int64) {\n\tf.estimates[0].sample = newSample\n\tf.estimates[0].time = newTime\n\tf.estimates[1].sample = newSample\n\tf.estimates[1].time = newTime\n\tf.estimates[2].sample = newSample\n\tf.estimates[2].time = newTime\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/congestion_v2/bandwidth.go",
    "content": "package congestion\n\nimport (\n\t\"math\"\n\t\"time\"\n\n\t\"github.com/metacubex/quic-go/congestion\"\n)\n\nconst (\n\tinfBandwidth = Bandwidth(math.MaxUint64)\n)\n\n// Bandwidth of a connection\ntype Bandwidth uint64\n\nconst (\n\t// BitsPerSecond is 1 bit per second\n\tBitsPerSecond Bandwidth = 1\n\t// BytesPerSecond is 1 byte per second\n\tBytesPerSecond = 8 * BitsPerSecond\n)\n\n// BandwidthFromDelta calculates the bandwidth from a number of bytes and a time delta\nfunc BandwidthFromDelta(bytes congestion.ByteCount, delta time.Duration) Bandwidth {\n\treturn Bandwidth(bytes) * Bandwidth(time.Second) / Bandwidth(delta) * BytesPerSecond\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/congestion_v2/bandwidth_sampler.go",
    "content": "package congestion\n\nimport (\n\t\"math\"\n\t\"time\"\n\n\t\"github.com/metacubex/quic-go/congestion\"\n\t\"github.com/metacubex/quic-go/monotime\"\n)\n\nconst (\n\tinfRTT                             = time.Duration(math.MaxInt64)\n\tdefaultConnectionStateMapQueueSize = 256\n\tdefaultCandidatesBufferSize        = 256\n)\n\ntype roundTripCount uint64\n\n// SendTimeState is a subset of ConnectionStateOnSentPacket which is returned\n// to the caller when the packet is acked or lost.\ntype sendTimeState struct {\n\t// Whether other states in this object is valid.\n\tisValid bool\n\t// Whether the sender is app limited at the time the packet was sent.\n\t// App limited bandwidth sample might be artificially low because the sender\n\t// did not have enough data to send in order to saturate the link.\n\tisAppLimited bool\n\t// Total number of sent bytes at the time the packet was sent.\n\t// Includes the packet itself.\n\ttotalBytesSent congestion.ByteCount\n\t// Total number of acked bytes at the time the packet was sent.\n\ttotalBytesAcked congestion.ByteCount\n\t// Total number of lost bytes at the time the packet was sent.\n\ttotalBytesLost congestion.ByteCount\n\t// Total number of inflight bytes at the time the packet was sent.\n\t// Includes the packet itself.\n\t// It should be equal to |total_bytes_sent| minus the sum of\n\t// |total_bytes_acked|, |total_bytes_lost| and total neutered bytes.\n\tbytesInFlight congestion.ByteCount\n}\n\nfunc newSendTimeState(\n\tisAppLimited bool,\n\ttotalBytesSent congestion.ByteCount,\n\ttotalBytesAcked congestion.ByteCount,\n\ttotalBytesLost congestion.ByteCount,\n\tbytesInFlight congestion.ByteCount,\n) *sendTimeState {\n\treturn &sendTimeState{\n\t\tisValid:         true,\n\t\tisAppLimited:    isAppLimited,\n\t\ttotalBytesSent:  totalBytesSent,\n\t\ttotalBytesAcked: totalBytesAcked,\n\t\ttotalBytesLost:  totalBytesLost,\n\t\tbytesInFlight:   bytesInFlight,\n\t}\n}\n\ntype extraAckedEvent struct {\n\t// The excess bytes acknowlwedged in the time delta for this event.\n\textraAcked congestion.ByteCount\n\n\t// The bytes acknowledged and time delta from the event.\n\tbytesAcked congestion.ByteCount\n\ttimeDelta  time.Duration\n\t// The round trip of the event.\n\tround roundTripCount\n}\n\nfunc maxExtraAckedEventFunc(a, b extraAckedEvent) int {\n\tif a.extraAcked > b.extraAcked {\n\t\treturn 1\n\t} else if a.extraAcked < b.extraAcked {\n\t\treturn -1\n\t}\n\treturn 0\n}\n\n// BandwidthSample\ntype bandwidthSample struct {\n\t// The bandwidth at that particular sample. Zero if no valid bandwidth sample\n\t// is available.\n\tbandwidth Bandwidth\n\t// The RTT measurement at this particular sample.  Zero if no RTT sample is\n\t// available.  Does not correct for delayed ack time.\n\trtt time.Duration\n\t// |send_rate| is computed from the current packet being acked('P') and an\n\t// earlier packet that is acked before P was sent.\n\tsendRate Bandwidth\n\t// States captured when the packet was sent.\n\tstateAtSend sendTimeState\n}\n\nfunc newBandwidthSample() *bandwidthSample {\n\treturn &bandwidthSample{\n\t\tsendRate: infBandwidth,\n\t}\n}\n\n// MaxAckHeightTracker is part of the BandwidthSampler. It is called after every\n// ack event to keep track the degree of ack aggregation(a.k.a \"ack height\").\ntype maxAckHeightTracker struct {\n\t// Tracks the maximum number of bytes acked faster than the estimated\n\t// bandwidth.\n\tmaxAckHeightFilter *WindowedFilter[extraAckedEvent, roundTripCount]\n\t// The time this aggregation started and the number of bytes acked during it.\n\taggregationEpochStartTime monotime.Time\n\taggregationEpochBytes     congestion.ByteCount\n\t// The last sent packet number before the current aggregation epoch started.\n\tlastSentPacketNumberBeforeEpoch congestion.PacketNumber\n\t// The number of ack aggregation epochs ever started, including the ongoing\n\t// one. Stats only.\n\tnumAckAggregationEpochs                uint64\n\tackAggregationBandwidthThreshold       float64\n\tstartNewAggregationEpochAfterFullRound bool\n\treduceExtraAckedOnBandwidthIncrease    bool\n}\n\nfunc newMaxAckHeightTracker(windowLength roundTripCount) *maxAckHeightTracker {\n\treturn &maxAckHeightTracker{\n\t\tmaxAckHeightFilter:               NewWindowedFilter(windowLength, maxExtraAckedEventFunc),\n\t\tlastSentPacketNumberBeforeEpoch:  invalidPacketNumber,\n\t\tackAggregationBandwidthThreshold: 1.0,\n\t}\n}\n\nfunc (m *maxAckHeightTracker) Get() congestion.ByteCount {\n\treturn m.maxAckHeightFilter.GetBest().extraAcked\n}\n\nfunc (m *maxAckHeightTracker) Update(\n\tbandwidthEstimate Bandwidth,\n\tisNewMaxBandwidth bool,\n\troundTripCount roundTripCount,\n\tlastSentPacketNumber congestion.PacketNumber,\n\tlastAckedPacketNumber congestion.PacketNumber,\n\tackTime monotime.Time,\n\tbytesAcked congestion.ByteCount,\n) congestion.ByteCount {\n\tforceNewEpoch := false\n\n\tif m.reduceExtraAckedOnBandwidthIncrease && isNewMaxBandwidth {\n\t\t// Save and clear existing entries.\n\t\tbest := m.maxAckHeightFilter.GetBest()\n\t\tsecondBest := m.maxAckHeightFilter.GetSecondBest()\n\t\tthirdBest := m.maxAckHeightFilter.GetThirdBest()\n\t\tm.maxAckHeightFilter.Clear()\n\n\t\t// Reinsert the heights into the filter after recalculating.\n\t\texpectedBytesAcked := bytesFromBandwidthAndTimeDelta(bandwidthEstimate, best.timeDelta)\n\t\tif expectedBytesAcked < best.bytesAcked {\n\t\t\tbest.extraAcked = best.bytesAcked - expectedBytesAcked\n\t\t\tm.maxAckHeightFilter.Update(best, best.round)\n\t\t}\n\t\texpectedBytesAcked = bytesFromBandwidthAndTimeDelta(bandwidthEstimate, secondBest.timeDelta)\n\t\tif expectedBytesAcked < secondBest.bytesAcked {\n\t\t\tsecondBest.extraAcked = secondBest.bytesAcked - expectedBytesAcked\n\t\t\tm.maxAckHeightFilter.Update(secondBest, secondBest.round)\n\t\t}\n\t\texpectedBytesAcked = bytesFromBandwidthAndTimeDelta(bandwidthEstimate, thirdBest.timeDelta)\n\t\tif expectedBytesAcked < thirdBest.bytesAcked {\n\t\t\tthirdBest.extraAcked = thirdBest.bytesAcked - expectedBytesAcked\n\t\t\tm.maxAckHeightFilter.Update(thirdBest, thirdBest.round)\n\t\t}\n\t}\n\n\t// If any packet sent after the start of the epoch has been acked, start a new\n\t// epoch.\n\tif m.startNewAggregationEpochAfterFullRound &&\n\t\tm.lastSentPacketNumberBeforeEpoch != invalidPacketNumber &&\n\t\tlastAckedPacketNumber != invalidPacketNumber &&\n\t\tlastAckedPacketNumber > m.lastSentPacketNumberBeforeEpoch {\n\t\tforceNewEpoch = true\n\t}\n\tif m.aggregationEpochStartTime.IsZero() || forceNewEpoch {\n\t\tm.aggregationEpochBytes = bytesAcked\n\t\tm.aggregationEpochStartTime = ackTime\n\t\tm.lastSentPacketNumberBeforeEpoch = lastSentPacketNumber\n\t\tm.numAckAggregationEpochs++\n\t\treturn 0\n\t}\n\n\t// Compute how many bytes are expected to be delivered, assuming max bandwidth\n\t// is correct.\n\taggregationDelta := ackTime.Sub(m.aggregationEpochStartTime)\n\texpectedBytesAcked := bytesFromBandwidthAndTimeDelta(bandwidthEstimate, aggregationDelta)\n\t// Reset the current aggregation epoch as soon as the ack arrival rate is less\n\t// than or equal to the max bandwidth.\n\tif m.aggregationEpochBytes <= congestion.ByteCount(m.ackAggregationBandwidthThreshold*float64(expectedBytesAcked)) {\n\t\t// Reset to start measuring a new aggregation epoch.\n\t\tm.aggregationEpochBytes = bytesAcked\n\t\tm.aggregationEpochStartTime = ackTime\n\t\tm.lastSentPacketNumberBeforeEpoch = lastSentPacketNumber\n\t\tm.numAckAggregationEpochs++\n\t\treturn 0\n\t}\n\n\tm.aggregationEpochBytes += bytesAcked\n\n\t// Compute how many extra bytes were delivered vs max bandwidth.\n\textraBytesAcked := m.aggregationEpochBytes - expectedBytesAcked\n\tnewEvent := extraAckedEvent{\n\t\textraAcked: extraBytesAcked,\n\t\tbytesAcked: m.aggregationEpochBytes,\n\t\ttimeDelta:  aggregationDelta,\n\t}\n\tm.maxAckHeightFilter.Update(newEvent, roundTripCount)\n\treturn extraBytesAcked\n}\n\nfunc (m *maxAckHeightTracker) SetFilterWindowLength(length roundTripCount) {\n\tm.maxAckHeightFilter.SetWindowLength(length)\n}\n\nfunc (m *maxAckHeightTracker) Reset(newHeight congestion.ByteCount, newTime roundTripCount) {\n\tnewEvent := extraAckedEvent{\n\t\textraAcked: newHeight,\n\t\tround:      newTime,\n\t}\n\tm.maxAckHeightFilter.Reset(newEvent, newTime)\n}\n\nfunc (m *maxAckHeightTracker) SetAckAggregationBandwidthThreshold(threshold float64) {\n\tm.ackAggregationBandwidthThreshold = threshold\n}\n\nfunc (m *maxAckHeightTracker) SetStartNewAggregationEpochAfterFullRound(value bool) {\n\tm.startNewAggregationEpochAfterFullRound = value\n}\n\nfunc (m *maxAckHeightTracker) SetReduceExtraAckedOnBandwidthIncrease(value bool) {\n\tm.reduceExtraAckedOnBandwidthIncrease = value\n}\n\nfunc (m *maxAckHeightTracker) AckAggregationBandwidthThreshold() float64 {\n\treturn m.ackAggregationBandwidthThreshold\n}\n\nfunc (m *maxAckHeightTracker) NumAckAggregationEpochs() uint64 {\n\treturn m.numAckAggregationEpochs\n}\n\n// AckPoint represents a point on the ack line.\ntype ackPoint struct {\n\tackTime         monotime.Time\n\ttotalBytesAcked congestion.ByteCount\n}\n\n// RecentAckPoints maintains the most recent 2 ack points at distinct times.\ntype recentAckPoints struct {\n\tackPoints [2]ackPoint\n}\n\nfunc (r *recentAckPoints) Update(ackTime monotime.Time, totalBytesAcked congestion.ByteCount) {\n\tif ackTime.Before(r.ackPoints[1].ackTime) {\n\t\tr.ackPoints[1].ackTime = ackTime\n\t} else if ackTime.After(r.ackPoints[1].ackTime) {\n\t\tr.ackPoints[0] = r.ackPoints[1]\n\t\tr.ackPoints[1].ackTime = ackTime\n\t}\n\n\tr.ackPoints[1].totalBytesAcked = totalBytesAcked\n}\n\nfunc (r *recentAckPoints) Clear() {\n\tr.ackPoints[0] = ackPoint{}\n\tr.ackPoints[1] = ackPoint{}\n}\n\nfunc (r *recentAckPoints) MostRecentPoint() *ackPoint {\n\treturn &r.ackPoints[1]\n}\n\nfunc (r *recentAckPoints) LessRecentPoint() *ackPoint {\n\tif r.ackPoints[0].totalBytesAcked != 0 {\n\t\treturn &r.ackPoints[0]\n\t}\n\n\treturn &r.ackPoints[1]\n}\n\n// ConnectionStateOnSentPacket represents the information about a sent packet\n// and the state of the connection at the moment the packet was sent,\n// specifically the information about the most recently acknowledged packet at\n// that moment.\ntype connectionStateOnSentPacket struct {\n\t// Time at which the packet is sent.\n\tsentTime monotime.Time\n\t// Size of the packet.\n\tsize congestion.ByteCount\n\t// The value of |totalBytesSentAtLastAckedPacket| at the time the\n\t// packet was sent.\n\ttotalBytesSentAtLastAckedPacket congestion.ByteCount\n\t// The value of |lastAckedPacketSentTime| at the time the packet was\n\t// sent.\n\tlastAckedPacketSentTime monotime.Time\n\t// The value of |lastAckedPacketAckTime| at the time the packet was\n\t// sent.\n\tlastAckedPacketAckTime monotime.Time\n\t// Send time states that are returned to the congestion controller when the\n\t// packet is acked or lost.\n\tsendTimeState sendTimeState\n}\n\n// Snapshot constructor. Records the current state of the bandwidth\n// sampler.\n// |bytes_in_flight| is the bytes in flight right after the packet is sent.\nfunc newConnectionStateOnSentPacket(\n\tsentTime monotime.Time,\n\tsize congestion.ByteCount,\n\tbytesInFlight congestion.ByteCount,\n\tsampler *bandwidthSampler,\n) *connectionStateOnSentPacket {\n\treturn &connectionStateOnSentPacket{\n\t\tsentTime:                        sentTime,\n\t\tsize:                            size,\n\t\ttotalBytesSentAtLastAckedPacket: sampler.totalBytesSentAtLastAckedPacket,\n\t\tlastAckedPacketSentTime:         sampler.lastAckedPacketSentTime,\n\t\tlastAckedPacketAckTime:          sampler.lastAckedPacketAckTime,\n\t\tsendTimeState: *newSendTimeState(\n\t\t\tsampler.isAppLimited,\n\t\t\tsampler.totalBytesSent,\n\t\t\tsampler.totalBytesAcked,\n\t\t\tsampler.totalBytesLost,\n\t\t\tbytesInFlight,\n\t\t),\n\t}\n}\n\n// BandwidthSampler keeps track of sent and acknowledged packets and outputs a\n// bandwidth sample for every packet acknowledged. The samples are taken for\n// individual packets, and are not filtered; the consumer has to filter the\n// bandwidth samples itself. In certain cases, the sampler will locally severely\n// underestimate the bandwidth, hence a maximum filter with a size of at least\n// one RTT is recommended.\n//\n// This class bases its samples on the slope of two curves: the number of bytes\n// sent over time, and the number of bytes acknowledged as received over time.\n// It produces a sample of both slopes for every packet that gets acknowledged,\n// based on a slope between two points on each of the corresponding curves. Note\n// that due to the packet loss, the number of bytes on each curve might get\n// further and further away from each other, meaning that it is not feasible to\n// compare byte values coming from different curves with each other.\n//\n// The obvious points for measuring slope sample are the ones corresponding to\n// the packet that was just acknowledged. Let us denote them as S_1 (point at\n// which the current packet was sent) and A_1 (point at which the current packet\n// was acknowledged). However, taking a slope requires two points on each line,\n// so estimating bandwidth requires picking a packet in the past with respect to\n// which the slope is measured.\n//\n// For that purpose, BandwidthSampler always keeps track of the most recently\n// acknowledged packet, and records it together with every outgoing packet.\n// When a packet gets acknowledged (A_1), it has not only information about when\n// it itself was sent (S_1), but also the information about the latest\n// acknowledged packet right before it was sent (S_0 and A_0).\n//\n// Based on that data, send and ack rate are estimated as:\n//\n//\tsend_rate = (bytes(S_1) - bytes(S_0)) / (time(S_1) - time(S_0))\n//\tack_rate = (bytes(A_1) - bytes(A_0)) / (time(A_1) - time(A_0))\n//\n// Here, the ack rate is intuitively the rate we want to treat as bandwidth.\n// However, in certain cases (e.g. ack compression) the ack rate at a point may\n// end up higher than the rate at which the data was originally sent, which is\n// not indicative of the real bandwidth. Hence, we use the send rate as an upper\n// bound, and the sample value is\n//\n//\trate_sample = Min(send_rate, ack_rate)\n//\n// An important edge case handled by the sampler is tracking the app-limited\n// samples. There are multiple meaning of \"app-limited\" used interchangeably,\n// hence it is important to understand and to be able to distinguish between\n// them.\n//\n// Meaning 1: connection state. The connection is said to be app-limited when\n// there is no outstanding data to send. This means that certain bandwidth\n// samples in the future would not be an accurate indication of the link\n// capacity, and it is important to inform consumer about that. Whenever\n// connection becomes app-limited, the sampler is notified via OnAppLimited()\n// method.\n//\n// Meaning 2: a phase in the bandwidth sampler. As soon as the bandwidth\n// sampler becomes notified about the connection being app-limited, it enters\n// app-limited phase. In that phase, all *sent* packets are marked as\n// app-limited. Note that the connection itself does not have to be\n// app-limited during the app-limited phase, and in fact it will not be\n// (otherwise how would it send packets?). The boolean flag below indicates\n// whether the sampler is in that phase.\n//\n// Meaning 3: a flag on the sent packet and on the sample. If a sent packet is\n// sent during the app-limited phase, the resulting sample related to the\n// packet will be marked as app-limited.\n//\n// With the terminology issue out of the way, let us consider the question of\n// what kind of situation it addresses.\n//\n// Consider a scenario where we first send packets 1 to 20 at a regular\n// bandwidth, and then immediately run out of data. After a few seconds, we send\n// packets 21 to 60, and only receive ack for 21 between sending packets 40 and\n// 41. In this case, when we sample bandwidth for packets 21 to 40, the S_0/A_0\n// we use to compute the slope is going to be packet 20, a few seconds apart\n// from the current packet, hence the resulting estimate would be extremely low\n// and not indicative of anything. Only at packet 41 the S_0/A_0 will become 21,\n// meaning that the bandwidth sample would exclude the quiescence.\n//\n// Based on the analysis of that scenario, we implement the following rule: once\n// OnAppLimited() is called, all sent packets will produce app-limited samples\n// up until an ack for a packet that was sent after OnAppLimited() was called.\n// Note that while the scenario above is not the only scenario when the\n// connection is app-limited, the approach works in other cases too.\n\ntype congestionEventSample struct {\n\t// The maximum bandwidth sample from all acked packets.\n\t// QuicBandwidth::Zero() if no samples are available.\n\tsampleMaxBandwidth Bandwidth\n\t// Whether |sample_max_bandwidth| is from a app-limited sample.\n\tsampleIsAppLimited bool\n\t// The minimum rtt sample from all acked packets.\n\t// QuicTime::Delta::Infinite() if no samples are available.\n\tsampleRtt time.Duration\n\t// For each packet p in acked packets, this is the max value of INFLIGHT(p),\n\t// where INFLIGHT(p) is the number of bytes acked while p is inflight.\n\tsampleMaxInflight congestion.ByteCount\n\t// The send state of the largest packet in acked_packets, unless it is\n\t// empty. If acked_packets is empty, it's the send state of the largest\n\t// packet in lost_packets.\n\tlastPacketSendState sendTimeState\n\t// The number of extra bytes acked from this ack event, compared to what is\n\t// expected from the flow's bandwidth. Larger value means more ack\n\t// aggregation.\n\textraAcked congestion.ByteCount\n}\n\nfunc newCongestionEventSample() *congestionEventSample {\n\treturn &congestionEventSample{\n\t\tsampleRtt: infRTT,\n\t}\n}\n\ntype bandwidthSampler struct {\n\t// The total number of congestion controlled bytes sent during the connection.\n\ttotalBytesSent congestion.ByteCount\n\n\t// The total number of congestion controlled bytes which were acknowledged.\n\ttotalBytesAcked congestion.ByteCount\n\n\t// The total number of congestion controlled bytes which were lost.\n\ttotalBytesLost congestion.ByteCount\n\n\t// The total number of congestion controlled bytes which have been neutered.\n\ttotalBytesNeutered congestion.ByteCount\n\n\t// The value of |total_bytes_sent_| at the time the last acknowledged packet\n\t// was sent. Valid only when |last_acked_packet_sent_time_| is valid.\n\ttotalBytesSentAtLastAckedPacket congestion.ByteCount\n\n\t// The time at which the last acknowledged packet was sent. Set to\n\t// QuicTime::Zero() if no valid timestamp is available.\n\tlastAckedPacketSentTime monotime.Time\n\n\t// The time at which the most recent packet was acknowledged.\n\tlastAckedPacketAckTime monotime.Time\n\n\t// The most recently sent packet.\n\tlastSentPacket congestion.PacketNumber\n\n\t// The most recently acked packet.\n\tlastAckedPacket congestion.PacketNumber\n\n\t// Indicates whether the bandwidth sampler is currently in an app-limited\n\t// phase.\n\tisAppLimited bool\n\n\t// The packet that will be acknowledged after this one will cause the sampler\n\t// to exit the app-limited phase.\n\tendOfAppLimitedPhase congestion.PacketNumber\n\n\t// Record of the connection state at the point where each packet in flight was\n\t// sent, indexed by the packet number.\n\tconnectionStateMap *packetNumberIndexedQueue[connectionStateOnSentPacket]\n\n\trecentAckPoints recentAckPoints\n\ta0Candidates    RingBuffer[ackPoint]\n\n\t// Maximum number of tracked packets.\n\tmaxTrackedPackets congestion.ByteCount\n\n\tmaxAckHeightTracker              *maxAckHeightTracker\n\ttotalBytesAckedAfterLastAckEvent congestion.ByteCount\n\n\t// True if connection option 'BSAO' is set.\n\toverestimateAvoidance bool\n\n\t// True if connection option 'BBRB' is set.\n\tlimitMaxAckHeightTrackerBySendRate bool\n}\n\nfunc newBandwidthSampler(maxAckHeightTrackerWindowLength roundTripCount) *bandwidthSampler {\n\tb := &bandwidthSampler{\n\t\tmaxAckHeightTracker:  newMaxAckHeightTracker(maxAckHeightTrackerWindowLength),\n\t\tconnectionStateMap:   newPacketNumberIndexedQueue[connectionStateOnSentPacket](defaultConnectionStateMapQueueSize),\n\t\tlastSentPacket:       invalidPacketNumber,\n\t\tlastAckedPacket:      invalidPacketNumber,\n\t\tendOfAppLimitedPhase: invalidPacketNumber,\n\t}\n\n\tb.a0Candidates.Init(defaultCandidatesBufferSize)\n\n\treturn b\n}\n\nfunc (b *bandwidthSampler) MaxAckHeight() congestion.ByteCount {\n\treturn b.maxAckHeightTracker.Get()\n}\n\nfunc (b *bandwidthSampler) NumAckAggregationEpochs() uint64 {\n\treturn b.maxAckHeightTracker.NumAckAggregationEpochs()\n}\n\nfunc (b *bandwidthSampler) SetMaxAckHeightTrackerWindowLength(length roundTripCount) {\n\tb.maxAckHeightTracker.SetFilterWindowLength(length)\n}\n\nfunc (b *bandwidthSampler) ResetMaxAckHeightTracker(newHeight congestion.ByteCount, newTime roundTripCount) {\n\tb.maxAckHeightTracker.Reset(newHeight, newTime)\n}\n\nfunc (b *bandwidthSampler) SetStartNewAggregationEpochAfterFullRound(value bool) {\n\tb.maxAckHeightTracker.SetStartNewAggregationEpochAfterFullRound(value)\n}\n\nfunc (b *bandwidthSampler) SetLimitMaxAckHeightTrackerBySendRate(value bool) {\n\tb.limitMaxAckHeightTrackerBySendRate = value\n}\n\nfunc (b *bandwidthSampler) SetReduceExtraAckedOnBandwidthIncrease(value bool) {\n\tb.maxAckHeightTracker.SetReduceExtraAckedOnBandwidthIncrease(value)\n}\n\nfunc (b *bandwidthSampler) EnableOverestimateAvoidance() {\n\tif b.overestimateAvoidance {\n\t\treturn\n\t}\n\n\tb.overestimateAvoidance = true\n\tb.maxAckHeightTracker.SetAckAggregationBandwidthThreshold(2.0)\n}\n\nfunc (b *bandwidthSampler) IsOverestimateAvoidanceEnabled() bool {\n\treturn b.overestimateAvoidance\n}\n\nfunc (b *bandwidthSampler) OnPacketSent(\n\tsentTime monotime.Time,\n\tpacketNumber congestion.PacketNumber,\n\tbytes congestion.ByteCount,\n\tbytesInFlight congestion.ByteCount,\n\tisRetransmittable bool,\n) {\n\tb.lastSentPacket = packetNumber\n\n\tif !isRetransmittable {\n\t\treturn\n\t}\n\n\tb.totalBytesSent += bytes\n\n\t// If there are no packets in flight, the time at which the new transmission\n\t// opens can be treated as the A_0 point for the purpose of bandwidth\n\t// sampling. This underestimates bandwidth to some extent, and produces some\n\t// artificially low samples for most packets in flight, but it provides with\n\t// samples at important points where we would not have them otherwise, most\n\t// importantly at the beginning of the connection.\n\tif bytesInFlight == 0 {\n\t\tb.lastAckedPacketAckTime = sentTime\n\t\tif b.overestimateAvoidance {\n\t\t\tb.recentAckPoints.Clear()\n\t\t\tb.recentAckPoints.Update(sentTime, b.totalBytesAcked)\n\t\t\tb.a0Candidates.Clear()\n\t\t\tb.a0Candidates.PushBack(*b.recentAckPoints.MostRecentPoint())\n\t\t}\n\t\tb.totalBytesSentAtLastAckedPacket = b.totalBytesSent\n\n\t\t// In this situation ack compression is not a concern, set send rate to\n\t\t// effectively infinite.\n\t\tb.lastAckedPacketSentTime = sentTime\n\t}\n\n\tb.connectionStateMap.Emplace(packetNumber, newConnectionStateOnSentPacket(\n\t\tsentTime,\n\t\tbytes,\n\t\tbytesInFlight+bytes,\n\t\tb,\n\t))\n}\n\nfunc (b *bandwidthSampler) OnCongestionEvent(\n\tackTime monotime.Time,\n\tackedPackets []congestion.AckedPacketInfo,\n\tlostPackets []congestion.LostPacketInfo,\n\tmaxBandwidth Bandwidth,\n\testBandwidthUpperBound Bandwidth,\n\troundTripCount roundTripCount,\n) congestionEventSample {\n\teventSample := newCongestionEventSample()\n\n\tvar lastLostPacketSendState sendTimeState\n\n\tfor _, p := range lostPackets {\n\t\tsendState := b.OnPacketLost(p.PacketNumber, p.BytesLost)\n\t\tif sendState.isValid {\n\t\t\tlastLostPacketSendState = sendState\n\t\t}\n\t}\n\n\tif len(ackedPackets) == 0 {\n\t\t// Only populate send state for a loss-only event.\n\t\teventSample.lastPacketSendState = lastLostPacketSendState\n\t\treturn *eventSample\n\t}\n\n\tvar lastAckedPacketSendState sendTimeState\n\tvar maxSendRate Bandwidth\n\n\tfor _, p := range ackedPackets {\n\t\tsample := b.onPacketAcknowledged(ackTime, p.PacketNumber)\n\t\tif !sample.stateAtSend.isValid {\n\t\t\tcontinue\n\t\t}\n\n\t\tlastAckedPacketSendState = sample.stateAtSend\n\n\t\tif sample.rtt != 0 {\n\t\t\teventSample.sampleRtt = Min(eventSample.sampleRtt, sample.rtt)\n\t\t}\n\t\tif sample.bandwidth > eventSample.sampleMaxBandwidth {\n\t\t\teventSample.sampleMaxBandwidth = sample.bandwidth\n\t\t\teventSample.sampleIsAppLimited = sample.stateAtSend.isAppLimited\n\t\t}\n\t\tif sample.sendRate != infBandwidth {\n\t\t\tmaxSendRate = Max(maxSendRate, sample.sendRate)\n\t\t}\n\t\tinflightSample := b.totalBytesAcked - lastAckedPacketSendState.totalBytesAcked\n\t\tif inflightSample > eventSample.sampleMaxInflight {\n\t\t\teventSample.sampleMaxInflight = inflightSample\n\t\t}\n\t}\n\n\tif !lastLostPacketSendState.isValid {\n\t\teventSample.lastPacketSendState = lastAckedPacketSendState\n\t} else if !lastAckedPacketSendState.isValid {\n\t\teventSample.lastPacketSendState = lastLostPacketSendState\n\t} else {\n\t\t// If two packets are inflight and an alarm is armed to lose a packet and it\n\t\t// wakes up late, then the first of two in flight packets could have been\n\t\t// acknowledged before the wakeup, which re-evaluates loss detection, and\n\t\t// could declare the later of the two lost.\n\t\tif lostPackets[len(lostPackets)-1].PacketNumber > ackedPackets[len(ackedPackets)-1].PacketNumber {\n\t\t\teventSample.lastPacketSendState = lastLostPacketSendState\n\t\t} else {\n\t\t\teventSample.lastPacketSendState = lastAckedPacketSendState\n\t\t}\n\t}\n\n\tisNewMaxBandwidth := eventSample.sampleMaxBandwidth > maxBandwidth\n\tmaxBandwidth = Max(maxBandwidth, eventSample.sampleMaxBandwidth)\n\tif b.limitMaxAckHeightTrackerBySendRate {\n\t\tmaxBandwidth = Max(maxBandwidth, maxSendRate)\n\t}\n\n\teventSample.extraAcked = b.onAckEventEnd(Min(estBandwidthUpperBound, maxBandwidth), isNewMaxBandwidth, roundTripCount)\n\n\treturn *eventSample\n}\n\nfunc (b *bandwidthSampler) OnPacketLost(packetNumber congestion.PacketNumber, bytesLost congestion.ByteCount) (s sendTimeState) {\n\tb.totalBytesLost += bytesLost\n\tif sentPacketPointer := b.connectionStateMap.GetEntry(packetNumber); sentPacketPointer != nil {\n\t\tsentPacketToSendTimeState(sentPacketPointer, &s)\n\t}\n\treturn s\n}\n\nfunc (b *bandwidthSampler) OnPacketNeutered(packetNumber congestion.PacketNumber) {\n\tb.connectionStateMap.Remove(packetNumber, func(sentPacket connectionStateOnSentPacket) {\n\t\tb.totalBytesNeutered += sentPacket.size\n\t})\n}\n\nfunc (b *bandwidthSampler) OnAppLimited() {\n\tb.isAppLimited = true\n\tb.endOfAppLimitedPhase = b.lastSentPacket\n}\n\nfunc (b *bandwidthSampler) RemoveObsoletePackets(leastUnacked congestion.PacketNumber) {\n\t// A packet can become obsolete when it is removed from QuicUnackedPacketMap's\n\t// view of inflight before it is acked or marked as lost. For example, when\n\t// QuicSentPacketManager::RetransmitCryptoPackets retransmits a crypto packet,\n\t// the packet is removed from QuicUnackedPacketMap's inflight, but is not\n\t// marked as acked or lost in the BandwidthSampler.\n\tb.connectionStateMap.RemoveUpTo(leastUnacked)\n}\n\nfunc (b *bandwidthSampler) TotalBytesSent() congestion.ByteCount {\n\treturn b.totalBytesSent\n}\n\nfunc (b *bandwidthSampler) TotalBytesLost() congestion.ByteCount {\n\treturn b.totalBytesLost\n}\n\nfunc (b *bandwidthSampler) TotalBytesAcked() congestion.ByteCount {\n\treturn b.totalBytesAcked\n}\n\nfunc (b *bandwidthSampler) TotalBytesNeutered() congestion.ByteCount {\n\treturn b.totalBytesNeutered\n}\n\nfunc (b *bandwidthSampler) IsAppLimited() bool {\n\treturn b.isAppLimited\n}\n\nfunc (b *bandwidthSampler) EndOfAppLimitedPhase() congestion.PacketNumber {\n\treturn b.endOfAppLimitedPhase\n}\n\nfunc (b *bandwidthSampler) max_ack_height() congestion.ByteCount {\n\treturn b.maxAckHeightTracker.Get()\n}\n\nfunc (b *bandwidthSampler) chooseA0Point(totalBytesAcked congestion.ByteCount, a0 *ackPoint) bool {\n\tif b.a0Candidates.Empty() {\n\t\treturn false\n\t}\n\n\tif b.a0Candidates.Len() == 1 {\n\t\t*a0 = *b.a0Candidates.Front()\n\t\treturn true\n\t}\n\n\tfor i := 1; i < b.a0Candidates.Len(); i++ {\n\t\tif b.a0Candidates.Offset(i).totalBytesAcked > totalBytesAcked {\n\t\t\t*a0 = *b.a0Candidates.Offset(i - 1)\n\t\t\tif i > 1 {\n\t\t\t\tfor j := 0; j < i-1; j++ {\n\t\t\t\t\tb.a0Candidates.PopFront()\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn true\n\t\t}\n\t}\n\n\t*a0 = *b.a0Candidates.Back()\n\tfor k := 0; k < b.a0Candidates.Len()-1; k++ {\n\t\tb.a0Candidates.PopFront()\n\t}\n\treturn true\n}\n\nfunc (b *bandwidthSampler) onPacketAcknowledged(ackTime monotime.Time, packetNumber congestion.PacketNumber) bandwidthSample {\n\tsample := newBandwidthSample()\n\tb.lastAckedPacket = packetNumber\n\tsentPacketPointer := b.connectionStateMap.GetEntry(packetNumber)\n\tif sentPacketPointer == nil {\n\t\treturn *sample\n\t}\n\n\t// OnPacketAcknowledgedInner\n\tb.totalBytesAcked += sentPacketPointer.size\n\tb.totalBytesSentAtLastAckedPacket = sentPacketPointer.sendTimeState.totalBytesSent\n\tb.lastAckedPacketSentTime = sentPacketPointer.sentTime\n\tb.lastAckedPacketAckTime = ackTime\n\tif b.overestimateAvoidance {\n\t\tb.recentAckPoints.Update(ackTime, b.totalBytesAcked)\n\t}\n\n\tif b.isAppLimited {\n\t\t// Exit app-limited phase in two cases:\n\t\t// (1) end_of_app_limited_phase_ is not initialized, i.e., so far all\n\t\t// packets are sent while there are buffered packets or pending data.\n\t\t// (2) The current acked packet is after the sent packet marked as the end\n\t\t// of the app limit phase.\n\t\tif b.endOfAppLimitedPhase == invalidPacketNumber ||\n\t\t\tpacketNumber > b.endOfAppLimitedPhase {\n\t\t\tb.isAppLimited = false\n\t\t}\n\t}\n\n\t// There might have been no packets acknowledged at the moment when the\n\t// current packet was sent. In that case, there is no bandwidth sample to\n\t// make.\n\tif sentPacketPointer.lastAckedPacketSentTime.IsZero() {\n\t\treturn *sample\n\t}\n\n\t// Infinite rate indicates that the sampler is supposed to discard the\n\t// current send rate sample and use only the ack rate.\n\tsendRate := infBandwidth\n\tif sentPacketPointer.sentTime.After(sentPacketPointer.lastAckedPacketSentTime) {\n\t\tsendRate = BandwidthFromDelta(\n\t\t\tsentPacketPointer.sendTimeState.totalBytesSent-sentPacketPointer.totalBytesSentAtLastAckedPacket,\n\t\t\tsentPacketPointer.sentTime.Sub(sentPacketPointer.lastAckedPacketSentTime))\n\t}\n\n\tvar a0 ackPoint\n\tif b.overestimateAvoidance && b.chooseA0Point(sentPacketPointer.sendTimeState.totalBytesAcked, &a0) {\n\t} else {\n\t\ta0.ackTime = sentPacketPointer.lastAckedPacketAckTime\n\t\ta0.totalBytesAcked = sentPacketPointer.sendTimeState.totalBytesAcked\n\t}\n\n\t// During the slope calculation, ensure that ack time of the current packet is\n\t// always larger than the time of the previous packet, otherwise division by\n\t// zero or integer underflow can occur.\n\tif ackTime.Sub(a0.ackTime) <= 0 {\n\t\treturn *sample\n\t}\n\n\tackRate := BandwidthFromDelta(b.totalBytesAcked-a0.totalBytesAcked, ackTime.Sub(a0.ackTime))\n\n\tsample.bandwidth = Min(sendRate, ackRate)\n\t// Note: this sample does not account for delayed acknowledgement time.  This\n\t// means that the RTT measurements here can be artificially high, especially\n\t// on low bandwidth connections.\n\tsample.rtt = ackTime.Sub(sentPacketPointer.sentTime)\n\tsample.sendRate = sendRate\n\tsentPacketToSendTimeState(sentPacketPointer, &sample.stateAtSend)\n\n\treturn *sample\n}\n\nfunc (b *bandwidthSampler) onAckEventEnd(\n\tbandwidthEstimate Bandwidth,\n\tisNewMaxBandwidth bool,\n\troundTripCount roundTripCount,\n) congestion.ByteCount {\n\tnewlyAckedBytes := b.totalBytesAcked - b.totalBytesAckedAfterLastAckEvent\n\tif newlyAckedBytes == 0 {\n\t\treturn 0\n\t}\n\tb.totalBytesAckedAfterLastAckEvent = b.totalBytesAcked\n\textraAcked := b.maxAckHeightTracker.Update(\n\t\tbandwidthEstimate,\n\t\tisNewMaxBandwidth,\n\t\troundTripCount,\n\t\tb.lastSentPacket,\n\t\tb.lastAckedPacket,\n\t\tb.lastAckedPacketAckTime,\n\t\tnewlyAckedBytes)\n\t// If |extra_acked| is zero, i.e. this ack event marks the start of a new ack\n\t// aggregation epoch, save LessRecentPoint, which is the last ack point of the\n\t// previous epoch, as a A0 candidate.\n\tif b.overestimateAvoidance && extraAcked == 0 {\n\t\tb.a0Candidates.PushBack(*b.recentAckPoints.LessRecentPoint())\n\t}\n\treturn extraAcked\n}\n\nfunc sentPacketToSendTimeState(sentPacket *connectionStateOnSentPacket, sendTimeState *sendTimeState) {\n\t*sendTimeState = sentPacket.sendTimeState\n\tsendTimeState.isValid = true\n}\n\n// BytesFromBandwidthAndTimeDelta calculates the bytes\n// from a bandwidth(bits per second) and a time delta\nfunc bytesFromBandwidthAndTimeDelta(bandwidth Bandwidth, delta time.Duration) congestion.ByteCount {\n\treturn (congestion.ByteCount(bandwidth) * congestion.ByteCount(delta)) /\n\t\t(congestion.ByteCount(time.Second) * 8)\n}\n\nfunc timeDeltaFromBytesAndBandwidth(bytes congestion.ByteCount, bandwidth Bandwidth) time.Duration {\n\treturn time.Duration(bytes*8) * time.Second / time.Duration(bandwidth)\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/congestion_v2/bbr_sender.go",
    "content": "package congestion\n\n// src from https://github.com/google/quiche/blob/e7872fc9e12bb1d46a118949c3d4da36de58aa44/quiche/quic/core/congestion_control/bbr_sender.cc\n\nimport (\n\t\"fmt\"\n\t\"time\"\n\n\t\"github.com/metacubex/quic-go\"\n\t\"github.com/metacubex/quic-go/congestion\"\n\t\"github.com/metacubex/quic-go/monotime\"\n\n\t\"github.com/metacubex/randv2\"\n)\n\n// BbrSender implements BBR congestion control algorithm.  BBR aims to estimate\n// the current available Bottleneck Bandwidth and RTT (hence the name), and\n// regulates the pacing rate and the size of the congestion window based on\n// those signals.\n//\n// BBR relies on pacing in order to function properly.  Do not use BBR when\n// pacing is disabled.\n//\n\nconst (\n\tminBps = 65536 // 64 KB/s\n\n\tinvalidPacketNumber            = -1\n\tinitialCongestionWindowPackets = 32\n\tminCongestionWindowPackets     = 4\n\n\t// Constants based on TCP defaults.\n\t// The minimum CWND to ensure delayed acks don't reduce bandwidth measurements.\n\t// Does not inflate the pacing rate.\n\t// The gain used for the STARTUP, equal to 2/ln(2).\n\tdefaultHighGain = 2.885\n\t// The newly derived CWND gain for STARTUP, 2.\n\tderivedHighCWNDGain = 2.0\n)\n\n// The cycle of gains used during the PROBE_BW stage.\nvar pacingGain = [...]float64{1.25, 0.75, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0}\n\nconst (\n\t// The length of the gain cycle.\n\tgainCycleLength = len(pacingGain)\n\t// The size of the bandwidth filter window, in round-trips.\n\tbandwidthWindowSize = gainCycleLength + 2\n\n\t// The time after which the current min_rtt value expires.\n\tminRttExpiry = 10 * time.Second\n\t// The minimum time the connection can spend in PROBE_RTT mode.\n\tprobeRttTime = 200 * time.Millisecond\n\t// If the bandwidth does not increase by the factor of |kStartupGrowthTarget|\n\t// within |kRoundTripsWithoutGrowthBeforeExitingStartup| rounds, the connection\n\t// will exit the STARTUP mode.\n\tstartupGrowthTarget                         = 1.25\n\troundTripsWithoutGrowthBeforeExitingStartup = int64(3)\n\n\t// Flag.\n\tdefaultStartupFullLossCount  = 8\n\tquicBbr2DefaultLossThreshold = 0.02\n)\n\ntype bbrMode int\n\nconst (\n\t// Startup phase of the connection.\n\tbbrModeStartup = iota\n\t// After achieving the highest possible bandwidth during the startup, lower\n\t// the pacing rate in order to drain the queue.\n\tbbrModeDrain\n\t// Cruising mode.\n\tbbrModeProbeBw\n\t// Temporarily slow down sending in order to empty the buffer and measure\n\t// the real minimum RTT.\n\tbbrModeProbeRtt\n)\n\n// Indicates how the congestion control limits the amount of bytes in flight.\ntype bbrRecoveryState int\n\nconst (\n\t// Do not limit.\n\tbbrRecoveryStateNotInRecovery = iota\n\t// Allow an extra outstanding byte for each byte acknowledged.\n\tbbrRecoveryStateConservation\n\t// Allow two extra outstanding bytes for each byte acknowledged (slow\n\t// start).\n\tbbrRecoveryStateGrowth\n)\n\ntype Profile string\n\nconst (\n\tProfileConservative Profile = \"conservative\"\n\tProfileStandard     Profile = \"standard\"\n\tProfileAggressive   Profile = \"aggressive\"\n)\n\ntype profileConfig struct {\n\thighGain                            float64\n\thighCwndGain                        float64\n\tcongestionWindowGainConstant        float64\n\tnumStartupRtts                      int64\n\tdrainToTarget                       bool\n\tdetectOvershooting                  bool\n\tbytesLostMultiplier                 uint8\n\tenableAckAggregationStartup         bool\n\texpireAckAggregationStartup         bool\n\tenableOverestimateAvoidance         bool\n\treduceExtraAckedOnBandwidthIncrease bool\n}\n\nfunc configForProfile(profile Profile) profileConfig {\n\tswitch profile {\n\tcase ProfileConservative:\n\t\treturn profileConfig{\n\t\t\thighGain:                            2.25,\n\t\t\thighCwndGain:                        1.75,\n\t\t\tcongestionWindowGainConstant:        1.75,\n\t\t\tnumStartupRtts:                      2,\n\t\t\tdrainToTarget:                       true,\n\t\t\tdetectOvershooting:                  true,\n\t\t\tbytesLostMultiplier:                 1,\n\t\t\tenableOverestimateAvoidance:         true,\n\t\t\treduceExtraAckedOnBandwidthIncrease: true,\n\t\t}\n\tcase ProfileAggressive:\n\t\treturn profileConfig{\n\t\t\thighGain:                     3.0,\n\t\t\thighCwndGain:                 2.25,\n\t\t\tcongestionWindowGainConstant: 2.5,\n\t\t\tnumStartupRtts:               4,\n\t\t\tbytesLostMultiplier:          2,\n\t\t\tenableAckAggregationStartup:  true,\n\t\t\texpireAckAggregationStartup:  true,\n\t\t}\n\tdefault:\n\t\treturn profileConfig{\n\t\t\thighGain:                     defaultHighGain,\n\t\t\thighCwndGain:                 derivedHighCWNDGain,\n\t\t\tcongestionWindowGainConstant: 2.0,\n\t\t\tnumStartupRtts:               roundTripsWithoutGrowthBeforeExitingStartup,\n\t\t\tbytesLostMultiplier:          2,\n\t\t}\n\t}\n}\n\ntype bbrSender struct {\n\trttStats congestion.RTTStatsProvider\n\tpacer    *Pacer\n\n\tmode bbrMode\n\n\t// Bandwidth sampler provides BBR with the bandwidth measurements at\n\t// individual points.\n\tsampler *bandwidthSampler\n\n\t// The number of the round trips that have occurred during the connection.\n\troundTripCount roundTripCount\n\n\t// The packet number of the most recently sent packet.\n\tlastSentPacket congestion.PacketNumber\n\t// Acknowledgement of any packet after |current_round_trip_end_| will cause\n\t// the round trip counter to advance.\n\tcurrentRoundTripEnd congestion.PacketNumber\n\n\t// Number of congestion events with some losses, in the current round.\n\tnumLossEventsInRound uint64\n\n\t// Number of total bytes lost in the current round.\n\tbytesLostInRound congestion.ByteCount\n\n\t// The filter that tracks the maximum bandwidth over the multiple recent\n\t// round-trips.\n\tmaxBandwidth *WindowedFilter[Bandwidth, roundTripCount]\n\n\t// Minimum RTT estimate.  Automatically expires within 10 seconds (and\n\t// triggers PROBE_RTT mode) if no new value is sampled during that period.\n\tminRtt time.Duration\n\t// The time at which the current value of |min_rtt_| was assigned.\n\tminRttTimestamp monotime.Time\n\n\t// The maximum allowed number of bytes in flight.\n\tcongestionWindow congestion.ByteCount\n\n\t// The initial value of the |congestion_window_|.\n\tinitialCongestionWindow congestion.ByteCount\n\n\t// The largest value the |congestion_window_| can achieve.\n\tmaxCongestionWindow congestion.ByteCount\n\n\t// The smallest value the |congestion_window_| can achieve.\n\tminCongestionWindow congestion.ByteCount\n\n\t// The BBR profile used by the sender.\n\tprofile Profile\n\n\t// The pacing gain applied during the STARTUP phase.\n\thighGain float64\n\n\t// The CWND gain applied during the STARTUP phase.\n\thighCwndGain float64\n\n\t// The pacing gain applied during the DRAIN phase.\n\tdrainGain float64\n\n\t// The current pacing rate of the connection.\n\tpacingRate Bandwidth\n\n\t// The gain currently applied to the pacing rate.\n\tpacingGain float64\n\t// The gain currently applied to the congestion window.\n\tcongestionWindowGain float64\n\n\t// The gain used for the congestion window during PROBE_BW.  Latched from\n\t// quic_bbr_cwnd_gain flag.\n\tcongestionWindowGainConstant float64\n\t// The number of RTTs to stay in STARTUP mode.  Defaults to 3.\n\tnumStartupRtts int64\n\n\t// Number of round-trips in PROBE_BW mode, used for determining the current\n\t// pacing gain cycle.\n\tcycleCurrentOffset int\n\t// The time at which the last pacing gain cycle was started.\n\tlastCycleStart monotime.Time\n\n\t// Indicates whether the connection has reached the full bandwidth mode.\n\tisAtFullBandwidth bool\n\t// Number of rounds during which there was no significant bandwidth increase.\n\troundsWithoutBandwidthGain int64\n\t// The bandwidth compared to which the increase is measured.\n\tbandwidthAtLastRound Bandwidth\n\n\t// Set to true upon exiting quiescence.\n\texitingQuiescence bool\n\n\t// Time at which PROBE_RTT has to be exited.  Setting it to zero indicates\n\t// that the time is yet unknown as the number of packets in flight has not\n\t// reached the required value.\n\texitProbeRttAt monotime.Time\n\t// Indicates whether a round-trip has passed since PROBE_RTT became active.\n\tprobeRttRoundPassed bool\n\n\t// Indicates whether the most recent bandwidth sample was marked as\n\t// app-limited.\n\tlastSampleIsAppLimited bool\n\t// Indicates whether any non app-limited samples have been recorded.\n\thasNoAppLimitedSample bool\n\n\t// Current state of recovery.\n\trecoveryState bbrRecoveryState\n\t// Receiving acknowledgement of a packet after |end_recovery_at_| will cause\n\t// BBR to exit the recovery mode.  A value above zero indicates at least one\n\t// loss has been detected, so it must not be set back to zero.\n\tendRecoveryAt congestion.PacketNumber\n\t// A window used to limit the number of bytes in flight during loss recovery.\n\trecoveryWindow congestion.ByteCount\n\t// If true, consider all samples in recovery app-limited.\n\tisAppLimitedRecovery bool // not used\n\n\t// When true, pace at 1.5x and disable packet conservation in STARTUP.\n\tslowerStartup bool // not used\n\t// When true, disables packet conservation in STARTUP.\n\trateBasedStartup bool // not used\n\n\t// When true, add the most recent ack aggregation measurement during STARTUP.\n\tenableAckAggregationDuringStartup bool\n\t// When true, expire the windowed ack aggregation values in STARTUP when\n\t// bandwidth increases more than 25%.\n\texpireAckAggregationInStartup bool\n\n\t// If true, will not exit low gain mode until bytes_in_flight drops below BDP\n\t// or it's time for high gain mode.\n\tdrainToTarget bool\n\n\t// If true, slow down pacing rate in STARTUP when overshooting is detected.\n\tdetectOvershooting bool\n\t// Bytes lost while detect_overshooting_ is true.\n\tbytesLostWhileDetectingOvershooting congestion.ByteCount\n\t// Slow down pacing rate if\n\t// bytes_lost_while_detecting_overshooting_ *\n\t// bytes_lost_multiplier_while_detecting_overshooting_ > IW.\n\tbytesLostMultiplierWhileDetectingOvershooting uint8\n\t// When overshooting is detected, do not drop pacing_rate_ below this value /\n\t// min_rtt.\n\tcwndToCalculateMinPacingRate congestion.ByteCount\n\n\t// Max congestion window when adjusting network parameters.\n\tmaxCongestionWindowWithNetworkParametersAdjusted congestion.ByteCount // not used\n\n\t// Params.\n\tmaxDatagramSize congestion.ByteCount\n\t// Recorded on packet sent. equivalent |unacked_packets_->bytes_in_flight()|\n\tbytesInFlight congestion.ByteCount\n}\n\nvar _ congestion.CongestionControl = &bbrSender{}\n\nfunc NewBbrSender(\n\tinitialMaxDatagramSize congestion.ByteCount,\n\tinitialCongestionWindowPackets congestion.ByteCount,\n\tprofile Profile,\n) *bbrSender {\n\treturn newBbrSender(\n\t\tinitialMaxDatagramSize,\n\t\tinitialCongestionWindowPackets*initialMaxDatagramSize,\n\t\tcongestion.MaxCongestionWindowPackets*initialMaxDatagramSize,\n\t\tprofile,\n\t)\n}\n\nfunc newBbrSender(\n\tinitialMaxDatagramSize,\n\tinitialCongestionWindow,\n\tinitialMaxCongestionWindow congestion.ByteCount,\n\tprofile Profile,\n) *bbrSender {\n\tb := &bbrSender{\n\t\tmode:                         bbrModeStartup,\n\t\tsampler:                      newBandwidthSampler(roundTripCount(bandwidthWindowSize)),\n\t\tlastSentPacket:               invalidPacketNumber,\n\t\tcurrentRoundTripEnd:          invalidPacketNumber,\n\t\tmaxBandwidth:                 NewWindowedFilter(roundTripCount(bandwidthWindowSize), MaxFilter[Bandwidth]),\n\t\tcongestionWindow:             initialCongestionWindow,\n\t\tinitialCongestionWindow:      initialCongestionWindow,\n\t\tmaxCongestionWindow:          initialMaxCongestionWindow,\n\t\tminCongestionWindow:          minCongestionWindowForMaxDatagramSize(initialMaxDatagramSize),\n\t\tprofile:                      ProfileStandard,\n\t\thighGain:                     defaultHighGain,\n\t\thighCwndGain:                 derivedHighCWNDGain,\n\t\tdrainGain:                    1.0 / defaultHighGain,\n\t\tpacingGain:                   1.0,\n\t\tcongestionWindowGain:         1.0,\n\t\tcongestionWindowGainConstant: 2.0,\n\t\tnumStartupRtts:               roundTripsWithoutGrowthBeforeExitingStartup,\n\t\trecoveryState:                bbrRecoveryStateNotInRecovery,\n\t\tendRecoveryAt:                invalidPacketNumber,\n\t\trecoveryWindow:               initialMaxCongestionWindow,\n\t\tbytesLostMultiplierWhileDetectingOvershooting:    2,\n\t\tcwndToCalculateMinPacingRate:                     initialCongestionWindow,\n\t\tmaxCongestionWindowWithNetworkParametersAdjusted: initialMaxCongestionWindow,\n\t\tmaxDatagramSize: initialMaxDatagramSize,\n\t}\n\tb.pacer = NewPacer(b.bandwidthForPacer)\n\tb.applyProfile(profile)\n\n\tb.enterStartupMode()\n\n\treturn b\n}\n\nfunc (b *bbrSender) applyProfile(profile Profile) {\n\tcfg := configForProfile(profile)\n\tb.profile = profile\n\tb.highGain = cfg.highGain\n\tb.highCwndGain = cfg.highCwndGain\n\tb.drainGain = 1.0 / cfg.highGain\n\tb.congestionWindowGainConstant = cfg.congestionWindowGainConstant\n\tb.numStartupRtts = cfg.numStartupRtts\n\tb.drainToTarget = cfg.drainToTarget\n\tb.detectOvershooting = cfg.detectOvershooting\n\tb.bytesLostMultiplierWhileDetectingOvershooting = cfg.bytesLostMultiplier\n\tb.enableAckAggregationDuringStartup = cfg.enableAckAggregationStartup\n\tb.expireAckAggregationInStartup = cfg.expireAckAggregationStartup\n\tif cfg.enableOverestimateAvoidance {\n\t\tb.sampler.EnableOverestimateAvoidance()\n\t}\n\tb.sampler.SetReduceExtraAckedOnBandwidthIncrease(cfg.reduceExtraAckedOnBandwidthIncrease)\n}\n\nfunc minCongestionWindowForMaxDatagramSize(maxDatagramSize congestion.ByteCount) congestion.ByteCount {\n\treturn minCongestionWindowPackets * maxDatagramSize\n}\n\nfunc scaleByteWindowForDatagramSize(window, oldMaxDatagramSize, newMaxDatagramSize congestion.ByteCount) congestion.ByteCount {\n\tif oldMaxDatagramSize == newMaxDatagramSize {\n\t\treturn window\n\t}\n\treturn congestion.ByteCount(uint64(window) * uint64(newMaxDatagramSize) / uint64(oldMaxDatagramSize))\n}\n\nfunc (b *bbrSender) rescalePacketSizedWindows(maxDatagramSize congestion.ByteCount) {\n\toldMaxDatagramSize := b.maxDatagramSize\n\tb.maxDatagramSize = maxDatagramSize\n\tb.initialCongestionWindow = scaleByteWindowForDatagramSize(b.initialCongestionWindow, oldMaxDatagramSize, maxDatagramSize)\n\tb.maxCongestionWindow = scaleByteWindowForDatagramSize(b.maxCongestionWindow, oldMaxDatagramSize, maxDatagramSize)\n\tb.minCongestionWindow = minCongestionWindowForMaxDatagramSize(maxDatagramSize)\n\tb.cwndToCalculateMinPacingRate = scaleByteWindowForDatagramSize(b.cwndToCalculateMinPacingRate, oldMaxDatagramSize, maxDatagramSize)\n\tb.maxCongestionWindowWithNetworkParametersAdjusted = scaleByteWindowForDatagramSize(\n\t\tb.maxCongestionWindowWithNetworkParametersAdjusted,\n\t\toldMaxDatagramSize,\n\t\tmaxDatagramSize,\n\t)\n}\n\nfunc (b *bbrSender) SetRTTStatsProvider(provider congestion.RTTStatsProvider) {\n\tb.rttStats = provider\n}\n\n// TimeUntilSend implements the SendAlgorithm interface.\nfunc (b *bbrSender) TimeUntilSend(bytesInFlight congestion.ByteCount) monotime.Time {\n\treturn b.pacer.TimeUntilSend()\n}\n\n// HasPacingBudget implements the SendAlgorithm interface.\nfunc (b *bbrSender) HasPacingBudget(now monotime.Time) bool {\n\treturn b.pacer.Budget(now) >= b.maxDatagramSize\n}\n\n// OnPacketSent implements the SendAlgorithm interface.\nfunc (b *bbrSender) OnPacketSent(\n\tsentTime monotime.Time,\n\tbytesInFlight congestion.ByteCount,\n\tpacketNumber congestion.PacketNumber,\n\tbytes congestion.ByteCount,\n\tisRetransmittable bool,\n) {\n\tb.pacer.SentPacket(sentTime, bytes)\n\n\tb.lastSentPacket = packetNumber\n\tb.bytesInFlight = bytesInFlight\n\n\tif bytesInFlight == 0 {\n\t\tb.exitingQuiescence = true\n\t}\n\n\tb.sampler.OnPacketSent(sentTime, packetNumber, bytes, bytesInFlight, isRetransmittable)\n}\n\n// CanSend implements the SendAlgorithm interface.\nfunc (b *bbrSender) CanSend(bytesInFlight congestion.ByteCount) bool {\n\treturn bytesInFlight < b.GetCongestionWindow()\n}\n\n// MaybeExitSlowStart implements the SendAlgorithm interface.\nfunc (b *bbrSender) MaybeExitSlowStart() {\n\t// Do nothing\n}\n\n// OnPacketAcked implements the SendAlgorithm interface.\nfunc (b *bbrSender) OnPacketAcked(number congestion.PacketNumber, ackedBytes, priorInFlight congestion.ByteCount, eventTime monotime.Time) {\n\t// Do nothing.\n}\n\n// OnPacketLost implements the SendAlgorithm interface.\nfunc (b *bbrSender) OnPacketLost(number congestion.PacketNumber, lostBytes, priorInFlight congestion.ByteCount) {\n\t// Do nothing.\n}\n\n// OnRetransmissionTimeout implements the SendAlgorithm interface.\nfunc (b *bbrSender) OnRetransmissionTimeout(packetsRetransmitted bool) {\n\t// Do nothing.\n}\n\n// SetMaxDatagramSize implements the SendAlgorithm interface.\nfunc (b *bbrSender) SetMaxDatagramSize(s congestion.ByteCount) {\n\tif s < b.maxDatagramSize {\n\t\tpanic(fmt.Sprintf(\"congestion BUG: decreased max datagram size from %d to %d\", b.maxDatagramSize, s))\n\t}\n\toldMinCongestionWindow := b.minCongestionWindow\n\toldInitialCongestionWindow := b.initialCongestionWindow\n\tb.rescalePacketSizedWindows(s)\n\tswitch b.congestionWindow {\n\tcase oldMinCongestionWindow:\n\t\tb.congestionWindow = b.minCongestionWindow\n\tcase oldInitialCongestionWindow:\n\t\tb.congestionWindow = b.initialCongestionWindow\n\tdefault:\n\t\tb.congestionWindow = Min(b.maxCongestionWindow, Max(b.congestionWindow, b.minCongestionWindow))\n\t}\n\tb.recoveryWindow = Min(b.maxCongestionWindow, Max(b.recoveryWindow, b.minCongestionWindow))\n\tb.pacer.SetMaxDatagramSize(s)\n}\n\n// InSlowStart implements the SendAlgorithmWithDebugInfos interface.\nfunc (b *bbrSender) InSlowStart() bool {\n\treturn b.mode == bbrModeStartup\n}\n\n// InRecovery implements the SendAlgorithmWithDebugInfos interface.\nfunc (b *bbrSender) InRecovery() bool {\n\treturn b.recoveryState != bbrRecoveryStateNotInRecovery\n}\n\n// GetCongestionWindow implements the SendAlgorithmWithDebugInfos interface.\nfunc (b *bbrSender) GetCongestionWindow() congestion.ByteCount {\n\tif b.mode == bbrModeProbeRtt {\n\t\treturn b.probeRttCongestionWindow()\n\t}\n\n\tif b.InRecovery() {\n\t\treturn Min(b.congestionWindow, b.recoveryWindow)\n\t}\n\n\treturn b.congestionWindow\n}\n\nfunc (b *bbrSender) OnCongestionEvent(number congestion.PacketNumber, lostBytes, priorInFlight congestion.ByteCount) {\n\t// Do nothing.\n}\n\nfunc (b *bbrSender) OnCongestionEventEx(priorInFlight congestion.ByteCount, eventTime monotime.Time, ackedPackets []congestion.AckedPacketInfo, lostPackets []congestion.LostPacketInfo) {\n\ttotalBytesAckedBefore := b.sampler.TotalBytesAcked()\n\ttotalBytesLostBefore := b.sampler.TotalBytesLost()\n\n\tvar isRoundStart, minRttExpired bool\n\tvar excessAcked, bytesLost congestion.ByteCount\n\n\t// The send state of the largest packet in acked_packets, unless it is\n\t// empty. If acked_packets is empty, it's the send state of the largest\n\t// packet in lost_packets.\n\tvar lastPacketSendState sendTimeState\n\n\tb.maybeAppLimited(priorInFlight)\n\n\t// Update bytesInFlight\n\tb.bytesInFlight = priorInFlight\n\tfor _, p := range ackedPackets {\n\t\tb.bytesInFlight -= p.BytesAcked\n\t}\n\tfor _, p := range lostPackets {\n\t\tb.bytesInFlight -= p.BytesLost\n\t}\n\n\tif len(ackedPackets) != 0 {\n\t\tlastAckedPacket := ackedPackets[len(ackedPackets)-1].PacketNumber\n\t\tisRoundStart = b.updateRoundTripCounter(lastAckedPacket)\n\t\tb.updateRecoveryState(lastAckedPacket, len(lostPackets) != 0, isRoundStart)\n\t}\n\n\tsample := b.sampler.OnCongestionEvent(eventTime,\n\t\tackedPackets, lostPackets, b.maxBandwidth.GetBest(), infBandwidth, b.roundTripCount)\n\tif sample.lastPacketSendState.isValid {\n\t\tb.lastSampleIsAppLimited = sample.lastPacketSendState.isAppLimited\n\t\tb.hasNoAppLimitedSample = b.hasNoAppLimitedSample || !b.lastSampleIsAppLimited\n\t}\n\t// Avoid updating |max_bandwidth_| if a) this is a loss-only event, or b) all\n\t// packets in |acked_packets| did not generate valid samples. (e.g. ack of\n\t// ack-only packets). In both cases, sampler_.total_bytes_acked() will not\n\t// change.\n\tif totalBytesAckedBefore != b.sampler.TotalBytesAcked() {\n\t\tif !sample.sampleIsAppLimited || sample.sampleMaxBandwidth > b.maxBandwidth.GetBest() {\n\t\t\tb.maxBandwidth.Update(sample.sampleMaxBandwidth, b.roundTripCount)\n\t\t}\n\t}\n\n\tif sample.sampleRtt != infRTT {\n\t\tminRttExpired = b.maybeUpdateMinRtt(eventTime, sample.sampleRtt)\n\t}\n\tbytesLost = b.sampler.TotalBytesLost() - totalBytesLostBefore\n\n\texcessAcked = sample.extraAcked\n\tlastPacketSendState = sample.lastPacketSendState\n\n\tif len(lostPackets) != 0 {\n\t\tb.numLossEventsInRound++\n\t\tb.bytesLostInRound += bytesLost\n\t}\n\n\t// Handle logic specific to PROBE_BW mode.\n\tif b.mode == bbrModeProbeBw {\n\t\tb.updateGainCyclePhase(eventTime, priorInFlight, len(lostPackets) != 0)\n\t}\n\n\t// Handle logic specific to STARTUP and DRAIN modes.\n\tif isRoundStart && !b.isAtFullBandwidth {\n\t\tb.checkIfFullBandwidthReached(&lastPacketSendState)\n\t}\n\n\tb.maybeExitStartupOrDrain(eventTime)\n\n\t// Handle logic specific to PROBE_RTT.\n\tb.maybeEnterOrExitProbeRtt(eventTime, isRoundStart, minRttExpired)\n\n\t// Calculate number of packets acked and lost.\n\tbytesAcked := b.sampler.TotalBytesAcked() - totalBytesAckedBefore\n\n\t// After the model is updated, recalculate the pacing rate and congestion\n\t// window.\n\tb.calculatePacingRate(bytesLost)\n\tb.calculateCongestionWindow(bytesAcked, excessAcked)\n\tb.calculateRecoveryWindow(bytesAcked, bytesLost)\n\n\t// Cleanup internal state.\n\t// This is where we clean up obsolete (acked or lost) packets from the bandwidth sampler.\n\t// The \"least unacked\" should actually be FirstOutstanding, but since we are not passing\n\t// that through OnCongestionEventEx, we will only do an estimate using acked/lost packets\n\t// for now. Because of fast retransmission, they should differ by no more than 2 packets.\n\t// (this is controlled by packetThreshold in quic-go's sentPacketHandler)\n\tvar leastUnacked congestion.PacketNumber\n\tif len(ackedPackets) != 0 {\n\t\tleastUnacked = ackedPackets[len(ackedPackets)-1].PacketNumber - 2\n\t} else {\n\t\tleastUnacked = lostPackets[len(lostPackets)-1].PacketNumber + 1\n\t}\n\tb.sampler.RemoveObsoletePackets(leastUnacked)\n\n\tif isRoundStart {\n\t\tb.numLossEventsInRound = 0\n\t\tb.bytesLostInRound = 0\n\t}\n}\n\nfunc (b *bbrSender) PacingRate() Bandwidth {\n\tif b.pacingRate == 0 {\n\t\treturn Bandwidth(b.highGain * float64(\n\t\t\tBandwidthFromDelta(b.initialCongestionWindow, b.getMinRtt())))\n\t}\n\n\treturn b.pacingRate\n}\n\n// Sets the CWND gain used in STARTUP.  Must be greater than 1.\nfunc (b *bbrSender) setHighCwndGain(highCwndGain float64) {\n\tb.highCwndGain = highCwndGain\n\tif b.mode == bbrModeStartup {\n\t\tb.congestionWindowGain = highCwndGain\n\t}\n}\n\n// Get the current bandwidth estimate. Note that Bandwidth is in bits per second.\nfunc (b *bbrSender) bandwidthEstimate() Bandwidth {\n\treturn b.maxBandwidth.GetBest()\n}\n\nfunc (b *bbrSender) bandwidthForPacer() congestion.ByteCount {\n\tbps := congestion.ByteCount(float64(b.PacingRate()) / float64(BytesPerSecond))\n\tif bps < minBps {\n\t\t// We need to make sure that the bandwidth value for pacer is never zero,\n\t\t// otherwise it will go into an edge case where HasPacingBudget = false\n\t\t// but TimeUntilSend is before, causing the quic-go send loop to go crazy and get stuck.\n\t\treturn minBps\n\t}\n\treturn bps\n}\n\n// Returns the current estimate of the RTT of the connection.  Outside of the\n// edge cases, this is minimum RTT.\nfunc (b *bbrSender) getMinRtt() time.Duration {\n\tif b.minRtt != 0 {\n\t\treturn b.minRtt\n\t}\n\t// min_rtt could be available if the handshake packet gets neutered then\n\t// gets acknowledged. This could only happen for QUIC crypto where we do not\n\t// drop keys.\n\tminRtt := b.rttStats.MinRTT()\n\tif minRtt == 0 {\n\t\treturn 100 * time.Millisecond\n\t} else {\n\t\treturn minRtt\n\t}\n}\n\n// Computes the target congestion window using the specified gain.\nfunc (b *bbrSender) getTargetCongestionWindow(gain float64) congestion.ByteCount {\n\tbdp := bdpFromRttAndBandwidth(b.getMinRtt(), b.bandwidthEstimate())\n\tcongestionWindow := congestion.ByteCount(gain * float64(bdp))\n\n\t// BDP estimate will be zero if no bandwidth samples are available yet.\n\tif congestionWindow == 0 {\n\t\tcongestionWindow = congestion.ByteCount(gain * float64(b.initialCongestionWindow))\n\t}\n\n\treturn Max(congestionWindow, b.minCongestionWindow)\n}\n\n// The target congestion window during PROBE_RTT.\nfunc (b *bbrSender) probeRttCongestionWindow() congestion.ByteCount {\n\treturn b.minCongestionWindow\n}\n\nfunc (b *bbrSender) maybeUpdateMinRtt(now monotime.Time, sampleMinRtt time.Duration) bool {\n\t// Do not expire min_rtt if none was ever available.\n\tminRttExpired := b.minRtt != 0 && now.After(b.minRttTimestamp.Add(minRttExpiry))\n\tif minRttExpired || sampleMinRtt < b.minRtt || b.minRtt == 0 {\n\t\tb.minRtt = sampleMinRtt\n\t\tb.minRttTimestamp = now\n\t}\n\n\treturn minRttExpired\n}\n\n// Enters the STARTUP mode.\nfunc (b *bbrSender) enterStartupMode() {\n\tb.mode = bbrModeStartup\n\t// b.maybeTraceStateChange(logging.CongestionStateStartup)\n\tb.pacingGain = b.highGain\n\tb.congestionWindowGain = b.highCwndGain\n}\n\n// Enters the PROBE_BW mode.\nfunc (b *bbrSender) enterProbeBandwidthMode(now monotime.Time) {\n\tb.mode = bbrModeProbeBw\n\t// b.maybeTraceStateChange(logging.CongestionStateProbeBw)\n\tb.congestionWindowGain = b.congestionWindowGainConstant\n\n\t// Pick a random offset for the gain cycle out of {0, 2..7} range. 1 is\n\t// excluded because in that case increased gain and decreased gain would not\n\t// follow each other.\n\tb.cycleCurrentOffset = int(randv2.Int32N(congestion.PacketsPerConnectionID)) % (gainCycleLength - 1)\n\tif b.cycleCurrentOffset >= 1 {\n\t\tb.cycleCurrentOffset += 1\n\t}\n\n\tb.lastCycleStart = now\n\tb.pacingGain = pacingGain[b.cycleCurrentOffset]\n}\n\n// Updates the round-trip counter if a round-trip has passed.  Returns true if\n// the counter has been advanced.\nfunc (b *bbrSender) updateRoundTripCounter(lastAckedPacket congestion.PacketNumber) bool {\n\tif b.currentRoundTripEnd == invalidPacketNumber || lastAckedPacket > b.currentRoundTripEnd {\n\t\tb.roundTripCount++\n\t\tb.currentRoundTripEnd = b.lastSentPacket\n\t\treturn true\n\t}\n\treturn false\n}\n\n// Updates the current gain used in PROBE_BW mode.\nfunc (b *bbrSender) updateGainCyclePhase(now monotime.Time, priorInFlight congestion.ByteCount, hasLosses bool) {\n\t// In most cases, the cycle is advanced after an RTT passes.\n\tshouldAdvanceGainCycling := now.After(b.lastCycleStart.Add(b.getMinRtt()))\n\t// If the pacing gain is above 1.0, the connection is trying to probe the\n\t// bandwidth by increasing the number of bytes in flight to at least\n\t// pacing_gain * BDP.  Make sure that it actually reaches the target, as long\n\t// as there are no losses suggesting that the buffers are not able to hold\n\t// that much.\n\tif b.pacingGain > 1.0 && !hasLosses && priorInFlight < b.getTargetCongestionWindow(b.pacingGain) {\n\t\tshouldAdvanceGainCycling = false\n\t}\n\n\t// If pacing gain is below 1.0, the connection is trying to drain the extra\n\t// queue which could have been incurred by probing prior to it.  If the number\n\t// of bytes in flight falls down to the estimated BDP value earlier, conclude\n\t// that the queue has been successfully drained and exit this cycle early.\n\tif b.pacingGain < 1.0 && b.bytesInFlight <= b.getTargetCongestionWindow(1) {\n\t\tshouldAdvanceGainCycling = true\n\t}\n\n\tif shouldAdvanceGainCycling {\n\t\tb.cycleCurrentOffset = (b.cycleCurrentOffset + 1) % gainCycleLength\n\t\tb.lastCycleStart = now\n\t\t// Stay in low gain mode until the target BDP is hit.\n\t\t// Low gain mode will be exited immediately when the target BDP is achieved.\n\t\tif b.drainToTarget && b.pacingGain < 1 &&\n\t\t\tpacingGain[b.cycleCurrentOffset] == 1 &&\n\t\t\tb.bytesInFlight > b.getTargetCongestionWindow(1) {\n\t\t\treturn\n\t\t}\n\t\tb.pacingGain = pacingGain[b.cycleCurrentOffset]\n\t}\n}\n\n// Tracks for how many round-trips the bandwidth has not increased\n// significantly.\nfunc (b *bbrSender) checkIfFullBandwidthReached(lastPacketSendState *sendTimeState) {\n\tif b.lastSampleIsAppLimited {\n\t\treturn\n\t}\n\n\ttarget := Bandwidth(float64(b.bandwidthAtLastRound) * startupGrowthTarget)\n\tif b.bandwidthEstimate() >= target {\n\t\tb.bandwidthAtLastRound = b.bandwidthEstimate()\n\t\tb.roundsWithoutBandwidthGain = 0\n\t\tif b.expireAckAggregationInStartup {\n\t\t\t// Expire old excess delivery measurements now that bandwidth increased.\n\t\t\tb.sampler.ResetMaxAckHeightTracker(0, b.roundTripCount)\n\t\t}\n\t\treturn\n\t}\n\n\tb.roundsWithoutBandwidthGain++\n\tif b.roundsWithoutBandwidthGain >= b.numStartupRtts ||\n\t\tb.shouldExitStartupDueToLoss(lastPacketSendState) {\n\t\tb.isAtFullBandwidth = true\n\t}\n}\n\nfunc (b *bbrSender) maybeAppLimited(bytesInFlight congestion.ByteCount) {\n\tif bytesInFlight < b.getTargetCongestionWindow(1) {\n\t\tb.sampler.OnAppLimited()\n\t}\n}\n\n// Transitions from STARTUP to DRAIN and from DRAIN to PROBE_BW if\n// appropriate.\nfunc (b *bbrSender) maybeExitStartupOrDrain(now monotime.Time) {\n\tif b.mode == bbrModeStartup && b.isAtFullBandwidth {\n\t\tb.mode = bbrModeDrain\n\t\t// b.maybeTraceStateChange(logging.CongestionStateDrain)\n\t\tb.pacingGain = b.drainGain\n\t\tb.congestionWindowGain = b.highCwndGain\n\t}\n\tif b.mode == bbrModeDrain && b.bytesInFlight <= b.getTargetCongestionWindow(1) {\n\t\tb.enterProbeBandwidthMode(now)\n\t}\n}\n\n// Decides whether to enter or exit PROBE_RTT.\nfunc (b *bbrSender) maybeEnterOrExitProbeRtt(now monotime.Time, isRoundStart, minRttExpired bool) {\n\tif minRttExpired && !b.exitingQuiescence && b.mode != bbrModeProbeRtt {\n\t\tb.mode = bbrModeProbeRtt\n\t\t// b.maybeTraceStateChange(logging.CongestionStateProbRtt)\n\t\tb.pacingGain = 1.0\n\t\t// Do not decide on the time to exit PROBE_RTT until the |bytes_in_flight|\n\t\t// is at the target small value.\n\t\tb.exitProbeRttAt = 0\n\t}\n\n\tif b.mode == bbrModeProbeRtt {\n\t\tb.sampler.OnAppLimited()\n\t\t// b.maybeTraceStateChange(logging.CongestionStateApplicationLimited)\n\n\t\tif b.exitProbeRttAt.IsZero() {\n\t\t\t// If the window has reached the appropriate size, schedule exiting\n\t\t\t// PROBE_RTT.  The CWND during PROBE_RTT is kMinimumCongestionWindow, but\n\t\t\t// we allow an extra packet since QUIC checks CWND before sending a\n\t\t\t// packet.\n\t\t\tif b.bytesInFlight < b.probeRttCongestionWindow()+congestion.MaxPacketBufferSize {\n\t\t\t\tb.exitProbeRttAt = now.Add(probeRttTime)\n\t\t\t\tb.probeRttRoundPassed = false\n\t\t\t}\n\t\t} else {\n\t\t\tif isRoundStart {\n\t\t\t\tb.probeRttRoundPassed = true\n\t\t\t}\n\t\t\tif now.Sub(b.exitProbeRttAt) >= 0 && b.probeRttRoundPassed {\n\t\t\t\tb.minRttTimestamp = now\n\t\t\t\tif !b.isAtFullBandwidth {\n\t\t\t\t\tb.enterStartupMode()\n\t\t\t\t} else {\n\t\t\t\t\tb.enterProbeBandwidthMode(now)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tb.exitingQuiescence = false\n}\n\n// Determines whether BBR needs to enter, exit or advance state of the\n// recovery.\nfunc (b *bbrSender) updateRecoveryState(lastAckedPacket congestion.PacketNumber, hasLosses, isRoundStart bool) {\n\t// Disable recovery in startup, if loss-based exit is enabled.\n\tif !b.isAtFullBandwidth {\n\t\treturn\n\t}\n\n\t// Exit recovery when there are no losses for a round.\n\tif hasLosses {\n\t\tb.endRecoveryAt = b.lastSentPacket\n\t}\n\n\tswitch b.recoveryState {\n\tcase bbrRecoveryStateNotInRecovery:\n\t\tif hasLosses {\n\t\t\tb.recoveryState = bbrRecoveryStateConservation\n\t\t\t// This will cause the |recovery_window_| to be set to the correct\n\t\t\t// value in CalculateRecoveryWindow().\n\t\t\tb.recoveryWindow = 0\n\t\t\t// Since the conservation phase is meant to be lasting for a whole\n\t\t\t// round, extend the current round as if it were started right now.\n\t\t\tb.currentRoundTripEnd = b.lastSentPacket\n\t\t}\n\tcase bbrRecoveryStateConservation:\n\t\tif isRoundStart {\n\t\t\tb.recoveryState = bbrRecoveryStateGrowth\n\t\t}\n\t\tfallthrough\n\tcase bbrRecoveryStateGrowth:\n\t\t// Exit recovery if appropriate.\n\t\tif !hasLosses && lastAckedPacket > b.endRecoveryAt {\n\t\t\tb.recoveryState = bbrRecoveryStateNotInRecovery\n\t\t}\n\t}\n}\n\n// Determines the appropriate pacing rate for the connection.\nfunc (b *bbrSender) calculatePacingRate(bytesLost congestion.ByteCount) {\n\tif b.bandwidthEstimate() == 0 {\n\t\treturn\n\t}\n\n\ttargetRate := Bandwidth(b.pacingGain * float64(b.bandwidthEstimate()))\n\tif b.isAtFullBandwidth {\n\t\tb.pacingRate = targetRate\n\t\treturn\n\t}\n\n\t// Pace at the rate of initial_window / RTT as soon as RTT measurements are\n\t// available.\n\tif b.pacingRate == 0 && b.rttStats.MinRTT() != 0 {\n\t\tb.pacingRate = BandwidthFromDelta(b.initialCongestionWindow, b.rttStats.MinRTT())\n\t\treturn\n\t}\n\n\tif b.detectOvershooting {\n\t\tb.bytesLostWhileDetectingOvershooting += bytesLost\n\t\t// Check for overshooting with network parameters adjusted when pacing rate\n\t\t// > target_rate and loss has been detected.\n\t\tif b.pacingRate > targetRate && b.bytesLostWhileDetectingOvershooting > 0 {\n\t\t\tif b.hasNoAppLimitedSample ||\n\t\t\t\tb.bytesLostWhileDetectingOvershooting*congestion.ByteCount(b.bytesLostMultiplierWhileDetectingOvershooting) > b.initialCongestionWindow {\n\t\t\t\t// We are fairly sure overshoot happens if 1) there is at least one\n\t\t\t\t// non app-limited bw sample or 2) half of IW gets lost. Slow pacing\n\t\t\t\t// rate.\n\t\t\t\tb.pacingRate = Max(targetRate, BandwidthFromDelta(b.cwndToCalculateMinPacingRate, b.rttStats.MinRTT()))\n\t\t\t\tb.bytesLostWhileDetectingOvershooting = 0\n\t\t\t\tb.detectOvershooting = false\n\t\t\t}\n\t\t}\n\t}\n\n\t// Do not decrease the pacing rate during startup.\n\tb.pacingRate = Max(b.pacingRate, targetRate)\n}\n\n// Determines the appropriate congestion window for the connection.\nfunc (b *bbrSender) calculateCongestionWindow(bytesAcked, excessAcked congestion.ByteCount) {\n\tif b.mode == bbrModeProbeRtt {\n\t\treturn\n\t}\n\n\ttargetWindow := b.getTargetCongestionWindow(b.congestionWindowGain)\n\tif b.isAtFullBandwidth {\n\t\t// Add the max recently measured ack aggregation to CWND.\n\t\ttargetWindow += b.sampler.MaxAckHeight()\n\t} else if b.enableAckAggregationDuringStartup {\n\t\t// Add the most recent excess acked.  Because CWND never decreases in\n\t\t// STARTUP, this will automatically create a very localized max filter.\n\t\ttargetWindow += excessAcked\n\t}\n\n\t// Instead of immediately setting the target CWND as the new one, BBR grows\n\t// the CWND towards |target_window| by only increasing it |bytes_acked| at a\n\t// time.\n\tif b.isAtFullBandwidth {\n\t\tb.congestionWindow = Min(targetWindow, b.congestionWindow+bytesAcked)\n\t} else if b.congestionWindow < targetWindow ||\n\t\tb.sampler.TotalBytesAcked() < b.initialCongestionWindow {\n\t\t// If the connection is not yet out of startup phase, do not decrease the\n\t\t// window.\n\t\tb.congestionWindow += bytesAcked\n\t}\n\n\t// Enforce the limits on the congestion window.\n\tb.congestionWindow = Max(b.congestionWindow, b.minCongestionWindow)\n\tb.congestionWindow = Min(b.congestionWindow, b.maxCongestionWindow)\n}\n\n// Determines the appropriate window that constrains the in-flight during recovery.\nfunc (b *bbrSender) calculateRecoveryWindow(bytesAcked, bytesLost congestion.ByteCount) {\n\tif b.recoveryState == bbrRecoveryStateNotInRecovery {\n\t\treturn\n\t}\n\n\t// Set up the initial recovery window.\n\tif b.recoveryWindow == 0 {\n\t\tb.recoveryWindow = b.bytesInFlight + bytesAcked\n\t\tb.recoveryWindow = Max(b.minCongestionWindow, b.recoveryWindow)\n\t\treturn\n\t}\n\n\t// Remove losses from the recovery window, while accounting for a potential\n\t// integer underflow.\n\tif b.recoveryWindow >= bytesLost {\n\t\tb.recoveryWindow = b.recoveryWindow - bytesLost\n\t} else {\n\t\tb.recoveryWindow = b.maxDatagramSize\n\t}\n\n\t// In CONSERVATION mode, just subtracting losses is sufficient.  In GROWTH,\n\t// release additional |bytes_acked| to achieve a slow-start-like behavior.\n\tif b.recoveryState == bbrRecoveryStateGrowth {\n\t\tb.recoveryWindow += bytesAcked\n\t}\n\n\t// Always allow sending at least |bytes_acked| in response.\n\tb.recoveryWindow = Max(b.recoveryWindow, b.bytesInFlight+bytesAcked)\n\tb.recoveryWindow = Max(b.minCongestionWindow, b.recoveryWindow)\n}\n\n// Return whether we should exit STARTUP due to excessive loss.\nfunc (b *bbrSender) shouldExitStartupDueToLoss(lastPacketSendState *sendTimeState) bool {\n\tif b.numLossEventsInRound < defaultStartupFullLossCount || !lastPacketSendState.isValid {\n\t\treturn false\n\t}\n\n\tinflightAtSend := lastPacketSendState.bytesInFlight\n\n\tif inflightAtSend > 0 && b.bytesLostInRound > 0 {\n\t\tif b.bytesLostInRound > congestion.ByteCount(float64(inflightAtSend)*quicBbr2DefaultLossThreshold) {\n\t\t\treturn true\n\t\t}\n\t\treturn false\n\t}\n\treturn false\n}\n\nfunc bdpFromRttAndBandwidth(rtt time.Duration, bandwidth Bandwidth) congestion.ByteCount {\n\treturn congestion.ByteCount(rtt) * congestion.ByteCount(bandwidth) / congestion.ByteCount(BytesPerSecond) / congestion.ByteCount(time.Second)\n}\n\nfunc GetInitialPacketSize(quicConn *quic.Conn) congestion.ByteCount {\n\treturn congestion.ByteCount(quicConn.Config().InitialPacketSize)\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/congestion_v2/minmax_go120.go",
    "content": "//go:build !go1.21\n\npackage congestion\n\nimport \"golang.org/x/exp/constraints\"\n\nfunc Max[T constraints.Ordered](a, b T) T {\n\tif a < b {\n\t\treturn b\n\t}\n\treturn a\n}\n\nfunc Min[T constraints.Ordered](a, b T) T {\n\tif a < b {\n\t\treturn a\n\t}\n\treturn b\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/congestion_v2/minmax_go121.go",
    "content": "//go:build go1.21\n\npackage congestion\n\nimport \"cmp\"\n\nfunc Max[T cmp.Ordered](a, b T) T {\n\treturn max(a, b)\n}\n\nfunc Min[T cmp.Ordered](a, b T) T {\n\treturn min(a, b)\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/congestion_v2/pacer.go",
    "content": "package congestion\n\nimport (\n\t\"time\"\n\n\t\"github.com/metacubex/quic-go/congestion\"\n\t\"github.com/metacubex/quic-go/monotime\"\n)\n\nconst (\n\tmaxBurstPackets               = 10\n\tmaxBurstPacingDelayMultiplier = 4\n)\n\n// Pacer implements a token bucket pacing algorithm.\ntype Pacer struct {\n\tbudgetAtLastSent congestion.ByteCount\n\tmaxDatagramSize  congestion.ByteCount\n\tlastSentTime     monotime.Time\n\tgetBandwidth     func() congestion.ByteCount // in bytes/s\n}\n\nfunc NewPacer(getBandwidth func() congestion.ByteCount) *Pacer {\n\tp := &Pacer{\n\t\tbudgetAtLastSent: maxBurstPackets * congestion.InitialPacketSize,\n\t\tmaxDatagramSize:  congestion.InitialPacketSize,\n\t\tgetBandwidth:     getBandwidth,\n\t}\n\treturn p\n}\n\nfunc (p *Pacer) SentPacket(sendTime monotime.Time, size congestion.ByteCount) {\n\tbudget := p.Budget(sendTime)\n\tif size > budget {\n\t\tp.budgetAtLastSent = 0\n\t} else {\n\t\tp.budgetAtLastSent = budget - size\n\t}\n\tp.lastSentTime = sendTime\n}\n\nfunc (p *Pacer) Budget(now monotime.Time) congestion.ByteCount {\n\tif p.lastSentTime.IsZero() {\n\t\treturn p.maxBurstSize()\n\t}\n\tbudget := p.budgetAtLastSent + (p.getBandwidth()*congestion.ByteCount(now.Sub(p.lastSentTime).Nanoseconds()))/1e9\n\tif budget < 0 { // protect against overflows\n\t\tbudget = congestion.ByteCount(1<<62 - 1)\n\t}\n\treturn Min(p.maxBurstSize(), budget)\n}\n\nfunc (p *Pacer) maxBurstSize() congestion.ByteCount {\n\treturn Max(\n\t\tcongestion.ByteCount((maxBurstPacingDelayMultiplier*congestion.MinPacingDelay).Nanoseconds())*p.getBandwidth()/1e9,\n\t\tmaxBurstPackets*p.maxDatagramSize,\n\t)\n}\n\n// TimeUntilSend returns when the next packet should be sent.\n// It returns the zero value if a packet can be sent immediately.\nfunc (p *Pacer) TimeUntilSend() monotime.Time {\n\tif p.budgetAtLastSent >= p.maxDatagramSize {\n\t\treturn 0\n\t}\n\tdiff := 1e9 * uint64(p.maxDatagramSize-p.budgetAtLastSent)\n\tbw := uint64(p.getBandwidth())\n\t// We might need to round up this value.\n\t// Otherwise, we might have a budget (slightly) smaller than the datagram size when the timer expires.\n\td := diff / bw\n\t// this is effectively a math.Ceil, but using only integer math\n\tif diff%bw > 0 {\n\t\td++\n\t}\n\treturn p.lastSentTime.Add(Max(congestion.MinPacingDelay, time.Duration(d)*time.Nanosecond))\n}\n\nfunc (p *Pacer) SetMaxDatagramSize(s congestion.ByteCount) {\n\tp.maxDatagramSize = s\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/congestion_v2/packet_number_indexed_queue.go",
    "content": "package congestion\n\nimport (\n\t\"github.com/metacubex/quic-go/congestion\"\n)\n\n// packetNumberIndexedQueue is a queue of mostly continuous numbered entries\n// which supports the following operations:\n// - adding elements to the end of the queue, or at some point past the end\n// - removing elements in any order\n// - retrieving elements\n// If all elements are inserted in order, all of the operations above are\n// amortized O(1) time.\n//\n// Internally, the data structure is a deque where each element is marked as\n// present or not.  The deque starts at the lowest present index.  Whenever an\n// element is removed, it's marked as not present, and the front of the deque is\n// cleared of elements that are not present.\n//\n// The tail of the queue is not cleared due to the assumption of entries being\n// inserted in order, though removing all elements of the queue will return it\n// to its initial state.\n//\n// Note that this data structure is inherently hazardous, since an addition of\n// just two entries will cause it to consume all of the memory available.\n// Because of that, it is not a general-purpose container and should not be used\n// as one.\n\ntype entryWrapper[T any] struct {\n\tpresent bool\n\tentry   T\n}\n\ntype packetNumberIndexedQueue[T any] struct {\n\tentries                RingBuffer[entryWrapper[T]]\n\tnumberOfPresentEntries int\n\tfirstPacket            congestion.PacketNumber\n}\n\nfunc newPacketNumberIndexedQueue[T any](size int) *packetNumberIndexedQueue[T] {\n\tq := &packetNumberIndexedQueue[T]{\n\t\tfirstPacket: invalidPacketNumber,\n\t}\n\n\tq.entries.Init(size)\n\n\treturn q\n}\n\n// Emplace inserts data associated |packet_number| into (or past) the end of the\n// queue, filling up the missing intermediate entries as necessary.  Returns\n// true if the element has been inserted successfully, false if it was already\n// in the queue or inserted out of order.\nfunc (p *packetNumberIndexedQueue[T]) Emplace(packetNumber congestion.PacketNumber, entry *T) bool {\n\tif packetNumber == invalidPacketNumber || entry == nil {\n\t\treturn false\n\t}\n\n\tif p.IsEmpty() {\n\t\tp.entries.PushBack(entryWrapper[T]{\n\t\t\tpresent: true,\n\t\t\tentry:   *entry,\n\t\t})\n\t\tp.numberOfPresentEntries = 1\n\t\tp.firstPacket = packetNumber\n\t\treturn true\n\t}\n\n\t// Do not allow insertion out-of-order.\n\tif packetNumber <= p.LastPacket() {\n\t\treturn false\n\t}\n\n\t// Handle potentially missing elements.\n\toffset := int(packetNumber - p.FirstPacket())\n\tif gap := offset - p.entries.Len(); gap > 0 {\n\t\tfor i := 0; i < gap; i++ {\n\t\t\tp.entries.PushBack(entryWrapper[T]{})\n\t\t}\n\t}\n\n\tp.entries.PushBack(entryWrapper[T]{\n\t\tpresent: true,\n\t\tentry:   *entry,\n\t})\n\tp.numberOfPresentEntries++\n\treturn true\n}\n\n// GetEntry Retrieve the entry associated with the packet number.  Returns the pointer\n// to the entry in case of success, or nullptr if the entry does not exist.\nfunc (p *packetNumberIndexedQueue[T]) GetEntry(packetNumber congestion.PacketNumber) *T {\n\tew := p.getEntryWraper(packetNumber)\n\tif ew == nil {\n\t\treturn nil\n\t}\n\n\treturn &ew.entry\n}\n\n// Remove, Same as above, but if an entry is present in the queue, also call f(entry)\n// before removing it.\nfunc (p *packetNumberIndexedQueue[T]) Remove(packetNumber congestion.PacketNumber, f func(T)) bool {\n\tew := p.getEntryWraper(packetNumber)\n\tif ew == nil {\n\t\treturn false\n\t}\n\tif f != nil {\n\t\tf(ew.entry)\n\t}\n\tew.present = false\n\tp.numberOfPresentEntries--\n\n\tif packetNumber == p.FirstPacket() {\n\t\tp.clearup()\n\t}\n\n\treturn true\n}\n\n// RemoveUpTo, but not including |packet_number|.\n// Unused slots in the front are also removed, which means when the function\n// returns, |first_packet()| can be larger than |packet_number|.\nfunc (p *packetNumberIndexedQueue[T]) RemoveUpTo(packetNumber congestion.PacketNumber) {\n\tfor !p.entries.Empty() &&\n\t\tp.firstPacket != invalidPacketNumber &&\n\t\tp.firstPacket < packetNumber {\n\t\tif p.entries.Front().present {\n\t\t\tp.numberOfPresentEntries--\n\t\t}\n\t\tp.entries.PopFront()\n\t\tp.firstPacket++\n\t}\n\tp.clearup()\n\n\treturn\n}\n\n// IsEmpty return if queue is empty.\nfunc (p *packetNumberIndexedQueue[T]) IsEmpty() bool {\n\treturn p.numberOfPresentEntries == 0\n}\n\n// NumberOfPresentEntries returns the number of entries in the queue.\nfunc (p *packetNumberIndexedQueue[T]) NumberOfPresentEntries() int {\n\treturn p.numberOfPresentEntries\n}\n\n// EntrySlotsUsed returns the number of entries allocated in the underlying deque.  This is\n// proportional to the memory usage of the queue.\nfunc (p *packetNumberIndexedQueue[T]) EntrySlotsUsed() int {\n\treturn p.entries.Len()\n}\n\n// FirstPacket returns packet number of the first entry in the queue.\nfunc (p *packetNumberIndexedQueue[T]) FirstPacket() (packetNumber congestion.PacketNumber) {\n\treturn p.firstPacket\n}\n\n// LastPacket returns packet number of the last entry ever inserted in the queue.  Note that the\n// entry in question may have already been removed.  Zero if the queue is\n// empty.\nfunc (p *packetNumberIndexedQueue[T]) LastPacket() (packetNumber congestion.PacketNumber) {\n\tif p.IsEmpty() {\n\t\treturn invalidPacketNumber\n\t}\n\n\treturn p.firstPacket + congestion.PacketNumber(p.entries.Len()-1)\n}\n\nfunc (p *packetNumberIndexedQueue[T]) clearup() {\n\tfor !p.entries.Empty() && !p.entries.Front().present {\n\t\tp.entries.PopFront()\n\t\tp.firstPacket++\n\t}\n\tif p.entries.Empty() {\n\t\tp.firstPacket = invalidPacketNumber\n\t}\n}\n\nfunc (p *packetNumberIndexedQueue[T]) getEntryWraper(packetNumber congestion.PacketNumber) *entryWrapper[T] {\n\tif packetNumber == invalidPacketNumber ||\n\t\tp.IsEmpty() ||\n\t\tpacketNumber < p.firstPacket {\n\t\treturn nil\n\t}\n\n\toffset := int(packetNumber - p.firstPacket)\n\tif offset >= p.entries.Len() {\n\t\treturn nil\n\t}\n\n\tew := p.entries.Offset(offset)\n\tif ew == nil || !ew.present {\n\t\treturn nil\n\t}\n\n\treturn ew\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/congestion_v2/ringbuffer.go",
    "content": "package congestion\n\n// A RingBuffer is a ring buffer.\n// It acts as a heap that doesn't cause any allocations.\ntype RingBuffer[T any] struct {\n\tring             []T\n\theadPos, tailPos int\n\tfull             bool\n}\n\n// Init preallocs a buffer with a certain size.\nfunc (r *RingBuffer[T]) Init(size int) {\n\tr.ring = make([]T, size)\n}\n\n// Len returns the number of elements in the ring buffer.\nfunc (r *RingBuffer[T]) Len() int {\n\tif r.full {\n\t\treturn len(r.ring)\n\t}\n\tif r.tailPos >= r.headPos {\n\t\treturn r.tailPos - r.headPos\n\t}\n\treturn r.tailPos - r.headPos + len(r.ring)\n}\n\n// Empty says if the ring buffer is empty.\nfunc (r *RingBuffer[T]) Empty() bool {\n\treturn !r.full && r.headPos == r.tailPos\n}\n\n// PushBack adds a new element.\n// If the ring buffer is full, its capacity is increased first.\nfunc (r *RingBuffer[T]) PushBack(t T) {\n\tif r.full || len(r.ring) == 0 {\n\t\tr.grow()\n\t}\n\tr.ring[r.tailPos] = t\n\tr.tailPos++\n\tif r.tailPos == len(r.ring) {\n\t\tr.tailPos = 0\n\t}\n\tif r.tailPos == r.headPos {\n\t\tr.full = true\n\t}\n}\n\n// PopFront returns the next element.\n// It must not be called when the buffer is empty, that means that\n// callers might need to check if there are elements in the buffer first.\nfunc (r *RingBuffer[T]) PopFront() T {\n\tif r.Empty() {\n\t\tpanic(\"github.com/quic-go/quic-go/internal/utils/ringbuffer: pop from an empty queue\")\n\t}\n\tr.full = false\n\tt := r.ring[r.headPos]\n\tr.ring[r.headPos] = *new(T)\n\tr.headPos++\n\tif r.headPos == len(r.ring) {\n\t\tr.headPos = 0\n\t}\n\treturn t\n}\n\n// Offset returns the offset element.\n// It must not be called when the buffer is empty, that means that\n// callers might need to check if there are elements in the buffer first\n// and check if the index larger than buffer length.\nfunc (r *RingBuffer[T]) Offset(index int) *T {\n\tif r.Empty() || index >= r.Len() {\n\t\tpanic(\"github.com/quic-go/quic-go/internal/utils/ringbuffer: offset from invalid index\")\n\t}\n\toffset := (r.headPos + index) % len(r.ring)\n\treturn &r.ring[offset]\n}\n\n// Front returns the front element.\n// It must not be called when the buffer is empty, that means that\n// callers might need to check if there are elements in the buffer first.\nfunc (r *RingBuffer[T]) Front() *T {\n\tif r.Empty() {\n\t\tpanic(\"github.com/quic-go/quic-go/internal/utils/ringbuffer: front from an empty queue\")\n\t}\n\treturn &r.ring[r.headPos]\n}\n\n// Back returns the back element.\n// It must not be called when the buffer is empty, that means that\n// callers might need to check if there are elements in the buffer first.\nfunc (r *RingBuffer[T]) Back() *T {\n\tif r.Empty() {\n\t\tpanic(\"github.com/quic-go/quic-go/internal/utils/ringbuffer: back from an empty queue\")\n\t}\n\treturn r.Offset(r.Len() - 1)\n}\n\n// Grow the maximum size of the queue.\n// This method assume the queue is full.\nfunc (r *RingBuffer[T]) grow() {\n\toldRing := r.ring\n\tnewSize := len(oldRing) * 2\n\tif newSize == 0 {\n\t\tnewSize = 1\n\t}\n\tr.ring = make([]T, newSize)\n\theadLen := copy(r.ring, oldRing[r.headPos:])\n\tcopy(r.ring[headLen:], oldRing[:r.headPos])\n\tr.headPos, r.tailPos, r.full = 0, len(oldRing), false\n}\n\n// Clear removes all elements.\nfunc (r *RingBuffer[T]) Clear() {\n\tvar zeroValue T\n\tfor i := range r.ring {\n\t\tr.ring[i] = zeroValue\n\t}\n\tr.headPos, r.tailPos, r.full = 0, 0, false\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/congestion_v2/windowed_filter.go",
    "content": "package congestion\n\nimport (\n\t\"golang.org/x/exp/constraints\"\n)\n\n// Implements Kathleen Nichols' algorithm for tracking the minimum (or maximum)\n// estimate of a stream of samples over some fixed time interval. (E.g.,\n// the minimum RTT over the past five minutes.) The algorithm keeps track of\n// the best, second best, and third best min (or max) estimates, maintaining an\n// invariant that the measurement time of the n'th best >= n-1'th best.\n\n// The algorithm works as follows. On a reset, all three estimates are set to\n// the same sample. The second best estimate is then recorded in the second\n// quarter of the window, and a third best estimate is recorded in the second\n// half of the window, bounding the worst case error when the true min is\n// monotonically increasing (or true max is monotonically decreasing) over the\n// window.\n//\n// A new best sample replaces all three estimates, since the new best is lower\n// (or higher) than everything else in the window and it is the most recent.\n// The window thus effectively gets reset on every new min. The same property\n// holds true for second best and third best estimates. Specifically, when a\n// sample arrives that is better than the second best but not better than the\n// best, it replaces the second and third best estimates but not the best\n// estimate. Similarly, a sample that is better than the third best estimate\n// but not the other estimates replaces only the third best estimate.\n//\n// Finally, when the best expires, it is replaced by the second best, which in\n// turn is replaced by the third best. The newest sample replaces the third\n// best.\n\ntype WindowedFilterValue interface {\n\tany\n}\n\ntype WindowedFilterTime interface {\n\tconstraints.Integer | constraints.Float\n}\n\ntype WindowedFilter[V WindowedFilterValue, T WindowedFilterTime] struct {\n\t// Time length of window.\n\twindowLength T\n\testimates    []entry[V, T]\n\tcomparator   func(V, V) int\n}\n\ntype entry[V WindowedFilterValue, T WindowedFilterTime] struct {\n\tsample V\n\ttime   T\n}\n\n// Compares two values and returns true if the first is greater than or equal\n// to the second.\nfunc MaxFilter[O constraints.Ordered](a, b O) int {\n\tif a > b {\n\t\treturn 1\n\t} else if a < b {\n\t\treturn -1\n\t}\n\treturn 0\n}\n\n// Compares two values and returns true if the first is less than or equal\n// to the second.\nfunc MinFilter[O constraints.Ordered](a, b O) int {\n\tif a < b {\n\t\treturn 1\n\t} else if a > b {\n\t\treturn -1\n\t}\n\treturn 0\n}\n\nfunc NewWindowedFilter[V WindowedFilterValue, T WindowedFilterTime](windowLength T, comparator func(V, V) int) *WindowedFilter[V, T] {\n\treturn &WindowedFilter[V, T]{\n\t\twindowLength: windowLength,\n\t\testimates:    make([]entry[V, T], 3, 3),\n\t\tcomparator:   comparator,\n\t}\n}\n\n// Changes the window length.  Does not update any current samples.\nfunc (f *WindowedFilter[V, T]) SetWindowLength(windowLength T) {\n\tf.windowLength = windowLength\n}\n\nfunc (f *WindowedFilter[V, T]) GetBest() V {\n\treturn f.estimates[0].sample\n}\n\nfunc (f *WindowedFilter[V, T]) GetSecondBest() V {\n\treturn f.estimates[1].sample\n}\n\nfunc (f *WindowedFilter[V, T]) GetThirdBest() V {\n\treturn f.estimates[2].sample\n}\n\n// Updates best estimates with |sample|, and expires and updates best\n// estimates as necessary.\nfunc (f *WindowedFilter[V, T]) Update(newSample V, newTime T) {\n\t// Reset all estimates if they have not yet been initialized, if new sample\n\t// is a new best, or if the newest recorded estimate is too old.\n\tif f.comparator(f.estimates[0].sample, *new(V)) == 0 ||\n\t\tf.comparator(newSample, f.estimates[0].sample) >= 0 ||\n\t\tnewTime-f.estimates[2].time > f.windowLength {\n\t\tf.Reset(newSample, newTime)\n\t\treturn\n\t}\n\n\tif f.comparator(newSample, f.estimates[1].sample) >= 0 {\n\t\tf.estimates[1] = entry[V, T]{newSample, newTime}\n\t\tf.estimates[2] = f.estimates[1]\n\t} else if f.comparator(newSample, f.estimates[2].sample) >= 0 {\n\t\tf.estimates[2] = entry[V, T]{newSample, newTime}\n\t}\n\n\t// Expire and update estimates as necessary.\n\tif newTime-f.estimates[0].time > f.windowLength {\n\t\t// The best estimate hasn't been updated for an entire window, so promote\n\t\t// second and third best estimates.\n\t\tf.estimates[0] = f.estimates[1]\n\t\tf.estimates[1] = f.estimates[2]\n\t\tf.estimates[2] = entry[V, T]{newSample, newTime}\n\t\t// Need to iterate one more time. Check if the new best estimate is\n\t\t// outside the window as well, since it may also have been recorded a\n\t\t// long time ago. Don't need to iterate once more since we cover that\n\t\t// case at the beginning of the method.\n\t\tif newTime-f.estimates[0].time > f.windowLength {\n\t\t\tf.estimates[0] = f.estimates[1]\n\t\t\tf.estimates[1] = f.estimates[2]\n\t\t}\n\t\treturn\n\t}\n\tif f.comparator(f.estimates[1].sample, f.estimates[0].sample) == 0 &&\n\t\tnewTime-f.estimates[1].time > f.windowLength/4 {\n\t\t// A quarter of the window has passed without a better sample, so the\n\t\t// second-best estimate is taken from the second quarter of the window.\n\t\tf.estimates[1] = entry[V, T]{newSample, newTime}\n\t\tf.estimates[2] = f.estimates[1]\n\t\treturn\n\t}\n\n\tif f.comparator(f.estimates[2].sample, f.estimates[1].sample) == 0 &&\n\t\tnewTime-f.estimates[2].time > f.windowLength/2 {\n\t\t// We've passed a half of the window without a better estimate, so take\n\t\t// a third-best estimate from the second half of the window.\n\t\tf.estimates[2] = entry[V, T]{newSample, newTime}\n\t}\n}\n\n// Resets all estimates to new sample.\nfunc (f *WindowedFilter[V, T]) Reset(newSample V, newTime T) {\n\tf.estimates[2] = entry[V, T]{newSample, newTime}\n\tf.estimates[1] = f.estimates[2]\n\tf.estimates[0] = f.estimates[1]\n}\n\nfunc (f *WindowedFilter[V, T]) Clear() {\n\tf.estimates = make([]entry[V, T], 3, 3)\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/pool_client.go",
    "content": "package tuic\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\n\tlist \"github.com/bahlo/generic-list-go\"\n)\n\ntype PoolClient struct {\n\tnewClientOptionV4 *ClientOptionV4\n\tnewClientOptionV5 *ClientOptionV5\n\n\tdialFn          DialFunc\n\ttcpClients      list.List[Client]\n\ttcpClientsMutex sync.Mutex\n\tudpClients      list.List[Client]\n\tudpClientsMutex sync.Mutex\n}\n\nfunc (t *PoolClient) DialContext(ctx context.Context, metadata *C.Metadata) (net.Conn, error) {\n\tconn, err := t.getClient(false).DialContext(ctx, metadata)\n\tif errors.Is(err, TooManyOpenStreams) {\n\t\tconn, err = t.newClient(false).DialContext(ctx, metadata)\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn N.NewRefConn(conn, t), err\n}\n\nfunc (t *PoolClient) ListenPacket(ctx context.Context, metadata *C.Metadata) (net.PacketConn, error) {\n\tpc, err := t.getClient(true).ListenPacket(ctx, metadata)\n\tif errors.Is(err, TooManyOpenStreams) {\n\t\tpc, err = t.newClient(true).ListenPacket(ctx, metadata)\n\t}\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn N.NewRefPacketConn(pc, t), nil\n}\n\nfunc (t *PoolClient) newClient(udp bool) (client Client) {\n\tclients := &t.tcpClients\n\tclientsMutex := &t.tcpClientsMutex\n\tif udp {\n\t\tclients = &t.udpClients\n\t\tclientsMutex = &t.udpClientsMutex\n\t}\n\n\tclientsMutex.Lock()\n\tdefer clientsMutex.Unlock()\n\n\tif t.newClientOptionV4 != nil {\n\t\tclient = NewClientV4(t.newClientOptionV4, udp, t.dialFn)\n\t} else {\n\t\tclient = NewClientV5(t.newClientOptionV5, udp, t.dialFn)\n\t}\n\n\tclient.SetLastVisited(time.Now())\n\n\tclients.PushFront(client)\n\treturn client\n}\n\nfunc (t *PoolClient) getClient(udp bool) Client {\n\tclients := &t.tcpClients\n\tclientsMutex := &t.tcpClientsMutex\n\tif udp {\n\t\tclients = &t.udpClients\n\t\tclientsMutex = &t.udpClientsMutex\n\t}\n\tvar bestClient Client\n\n\tfunc() {\n\t\tclientsMutex.Lock()\n\t\tdefer clientsMutex.Unlock()\n\t\tfor it := clients.Front(); it != nil; {\n\t\t\tclient := it.Value\n\t\t\tif client == nil {\n\t\t\t\tnext := it.Next()\n\t\t\t\tclients.Remove(it)\n\t\t\t\tit = next\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tif bestClient == nil {\n\t\t\t\tbestClient = client\n\t\t\t} else {\n\t\t\t\tif client.OpenStreams() < bestClient.OpenStreams() {\n\t\t\t\t\tbestClient = client\n\t\t\t\t}\n\t\t\t}\n\t\t\tit = it.Next()\n\t\t}\n\t\tfor it := clients.Front(); it != nil; {\n\t\t\tclient := it.Value\n\t\t\tif client != bestClient && client.OpenStreams() == 0 && time.Now().Sub(client.LastVisited()) > 30*time.Minute {\n\t\t\t\tclient.Close()\n\t\t\t\tnext := it.Next()\n\t\t\t\tclients.Remove(it)\n\t\t\t\tit = next\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tit = it.Next()\n\t\t}\n\t}()\n\n\tif bestClient == nil {\n\t\treturn t.newClient(udp)\n\t} else {\n\t\tbestClient.SetLastVisited(time.Now())\n\t\treturn bestClient\n\t}\n}\n\nfunc NewPoolClientV4(clientOption *ClientOptionV4, dialFn DialFunc) *PoolClient {\n\tp := &PoolClient{\n\t\tdialFn: dialFn,\n\t}\n\tnewClientOption := *clientOption\n\tp.newClientOptionV4 = &newClientOption\n\treturn p\n}\n\nfunc NewPoolClientV5(clientOption *ClientOptionV5, dialFn DialFunc) *PoolClient {\n\tp := &PoolClient{\n\t\tdialFn: dialFn,\n\t}\n\tnewClientOption := *clientOption\n\tp.newClientOptionV5 = &newClientOption\n\treturn p\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/server.go",
    "content": "package tuic\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n\t\"github.com/metacubex/mihomo/transport/tuic/common\"\n\t\"github.com/metacubex/mihomo/transport/tuic/types\"\n\tv4 \"github.com/metacubex/mihomo/transport/tuic/v4\"\n\tv5 \"github.com/metacubex/mihomo/transport/tuic/v5\"\n\n\t\"github.com/gofrs/uuid/v5\"\n\t\"github.com/metacubex/quic-go\"\n\t\"github.com/metacubex/tls\"\n)\n\ntype ServerOption struct {\n\tHandleTcpFn func(conn net.Conn, addr socks5.Addr, additions ...inbound.Addition) error\n\tHandleUdpFn func(addr socks5.Addr, packet C.UDPPacket, additions ...inbound.Addition) error\n\n\tTlsConfig             *tls.Config\n\tQuicConfig            *quic.Config\n\tTokens                [][32]byte          // V4 special\n\tUsers                 map[[16]byte]string // V5 special\n\tCongestionController  string\n\tAuthenticationTimeout time.Duration\n\tMaxUdpRelayPacketSize int\n\tCWND                  int\n\tBBRProfile            string\n}\n\ntype Server struct {\n\t*ServerOption\n\toptionV4 *v4.ServerOption\n\toptionV5 *v5.ServerOption\n\tlistener *quic.EarlyListener\n}\n\nfunc (s *Server) Serve() error {\n\tfor {\n\t\tconn, err := s.listener.Accept(context.Background())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tcommon.SetCongestionController(conn, s.CongestionController, s.CWND, s.BBRProfile)\n\t\th := &serverHandler{\n\t\t\tServer:   s,\n\t\t\tquicConn: conn,\n\t\t\tuuid:     utils.NewUUIDV4(),\n\t\t}\n\t\tif h.optionV4 != nil {\n\t\t\th.v4Handler = v4.NewServerHandler(h.optionV4, conn, h.uuid)\n\t\t}\n\t\tif h.optionV5 != nil {\n\t\t\th.v5Handler = v5.NewServerHandler(h.optionV5, conn, h.uuid)\n\t\t}\n\t\tgo h.handle()\n\t}\n}\n\nfunc (s *Server) Close() error {\n\treturn s.listener.Close()\n}\n\ntype serverHandler struct {\n\t*Server\n\tquicConn *quic.Conn\n\tuuid     uuid.UUID\n\n\tv4Handler types.ServerHandler\n\tv5Handler types.ServerHandler\n}\n\nfunc (s *serverHandler) handle() {\n\tgo func() {\n\t\t_ = s.handleUniStream()\n\t}()\n\tgo func() {\n\t\t_ = s.handleStream()\n\t}()\n\tgo func() {\n\t\t_ = s.handleMessage()\n\t}()\n\n\tselect {\n\tcase <-s.quicConn.HandshakeComplete(): // this chan maybe not closed if handshake never complete\n\tcase <-time.After(s.quicConn.Config().HandshakeIdleTimeout): // HandshakeIdleTimeout in real conn.Config() never be zero\n\t}\n\n\ttime.AfterFunc(s.AuthenticationTimeout, func() {\n\t\tif s.v4Handler != nil {\n\t\t\tif s.v4Handler.AuthOk() {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tif s.v5Handler != nil {\n\t\t\tif s.v5Handler.AuthOk() {\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tif s.v4Handler != nil {\n\t\t\ts.v4Handler.HandleTimeout()\n\t\t}\n\n\t\tif s.v5Handler != nil {\n\t\t\ts.v5Handler.HandleTimeout()\n\t\t}\n\t})\n}\n\nfunc (s *serverHandler) handleMessage() (err error) {\n\tfor {\n\t\tvar message []byte\n\t\tmessage, err = s.quicConn.ReceiveDatagram(context.Background())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tgo func() (err error) {\n\t\t\tif len(message) > 0 {\n\t\t\t\tswitch message[0] {\n\t\t\t\tcase v4.VER:\n\t\t\t\t\tif s.v4Handler != nil {\n\t\t\t\t\t\treturn s.v4Handler.HandleMessage(message)\n\t\t\t\t\t}\n\t\t\t\tcase v5.VER:\n\t\t\t\t\tif s.v5Handler != nil {\n\t\t\t\t\t\treturn s.v5Handler.HandleMessage(message)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn\n\t\t}()\n\t}\n}\n\nfunc (s *serverHandler) handleStream() (err error) {\n\tfor {\n\t\tvar quicStream *quic.Stream\n\t\tquicStream, err = s.quicConn.AcceptStream(context.Background())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tgo func() (err error) {\n\t\t\tstream := types.NewQuicStreamConn(\n\t\t\t\tquicStream,\n\t\t\t\ts.quicConn.LocalAddr(),\n\t\t\t\ts.quicConn.RemoteAddr(),\n\t\t\t\tnil,\n\t\t\t)\n\t\t\tconn := N.NewBufferedConn(stream)\n\n\t\t\tverBytes, err := conn.Peek(1)\n\t\t\tif err != nil {\n\t\t\t\t_ = conn.Close()\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tswitch verBytes[0] {\n\t\t\tcase v4.VER:\n\t\t\t\tif s.v4Handler != nil {\n\t\t\t\t\treturn s.v4Handler.HandleStream(conn)\n\t\t\t\t}\n\t\t\tcase v5.VER:\n\t\t\t\tif s.v5Handler != nil {\n\t\t\t\t\treturn s.v5Handler.HandleStream(conn)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn\n\t\t}()\n\t}\n}\n\nfunc (s *serverHandler) handleUniStream() (err error) {\n\tfor {\n\t\tvar stream *quic.ReceiveStream\n\t\tstream, err = s.quicConn.AcceptUniStream(context.Background())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tgo func() (err error) {\n\t\t\tdefer func() {\n\t\t\t\tstream.CancelRead(0)\n\t\t\t}()\n\t\t\treader := bufio.NewReader(stream)\n\t\t\tverBytes, err := reader.Peek(1)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\n\t\t\tswitch verBytes[0] {\n\t\t\tcase v4.VER:\n\t\t\t\tif s.v4Handler != nil {\n\t\t\t\t\treturn s.v4Handler.HandleUniStream(reader)\n\t\t\t\t}\n\t\t\tcase v5.VER:\n\t\t\t\tif s.v5Handler != nil {\n\t\t\t\t\treturn s.v5Handler.HandleUniStream(reader)\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn\n\t\t}()\n\t}\n}\n\nfunc NewServer(option *ServerOption, pc net.PacketConn) (*Server, error) {\n\tlistener, err := quic.ListenEarly(pc, option.TlsConfig, option.QuicConfig)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tserver := &Server{\n\t\tServerOption: option,\n\t\tlistener:     listener,\n\t}\n\tif len(option.Tokens) > 0 {\n\t\tserver.optionV4 = &v4.ServerOption{\n\t\t\tHandleTcpFn:           option.HandleTcpFn,\n\t\t\tHandleUdpFn:           option.HandleUdpFn,\n\t\t\tTokens:                option.Tokens,\n\t\t\tMaxUdpRelayPacketSize: option.MaxUdpRelayPacketSize,\n\t\t}\n\t}\n\tif len(option.Users) > 0 {\n\t\tmaxUdpRelayPacketSize := option.MaxUdpRelayPacketSize\n\t\tif maxUdpRelayPacketSize > MaxFragSizeV5 {\n\t\t\tmaxUdpRelayPacketSize = MaxFragSizeV5\n\t\t}\n\t\tserver.optionV5 = &v5.ServerOption{\n\t\t\tHandleTcpFn:           option.HandleTcpFn,\n\t\t\tHandleUdpFn:           option.HandleUdpFn,\n\t\t\tUsers:                 option.Users,\n\t\t\tMaxUdpRelayPacketSize: option.MaxUdpRelayPacketSize,\n\t\t}\n\t}\n\treturn server, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/tuic.go",
    "content": "package tuic\n\nimport (\n\t\"github.com/metacubex/mihomo/transport/tuic/common\"\n\t\"github.com/metacubex/mihomo/transport/tuic/types\"\n\tv4 \"github.com/metacubex/mihomo/transport/tuic/v4\"\n\tv5 \"github.com/metacubex/mihomo/transport/tuic/v5\"\n)\n\ntype ClientOptionV4 = v4.ClientOption\ntype ClientOptionV5 = v5.ClientOption\n\ntype Client = types.Client\n\nfunc NewClientV4(clientOption *ClientOptionV4, udp bool, dialFn DialFunc) Client {\n\treturn v4.NewClient(clientOption, udp, dialFn)\n}\n\nfunc NewClientV5(clientOption *ClientOptionV5, udp bool, dialFn DialFunc) Client {\n\treturn v5.NewClient(clientOption, udp, dialFn)\n}\n\ntype DialFunc = types.DialFunc\n\nvar TooManyOpenStreams = types.TooManyOpenStreams\n\nconst DefaultStreamReceiveWindow = common.DefaultStreamReceiveWindow\nconst DefaultConnectionReceiveWindow = common.DefaultConnectionReceiveWindow\n\nvar GenTKN = v4.GenTKN\nvar PacketOverHeadV4 = v4.PacketOverHead\nvar PacketOverHeadV5 = v5.PacketOverHead\nvar MaxFragSizeV5 = v5.MaxFragSize\n\ntype UdpRelayMode = types.UdpRelayMode\n\nconst (\n\tQUIC   = types.QUIC\n\tNATIVE = types.NATIVE\n)\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/types/stream.go",
    "content": "package types\n\nimport (\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/quic-go\"\n)\n\ntype quicStreamConn struct {\n\t*quic.Stream\n\tlock  sync.Mutex\n\tlAddr net.Addr\n\trAddr net.Addr\n\n\tcloseDeferFn func()\n\n\tcloseOnce sync.Once\n\tcloseErr  error\n}\n\nfunc (q *quicStreamConn) Write(p []byte) (n int, err error) {\n\tq.lock.Lock()\n\tdefer q.lock.Unlock()\n\treturn q.Stream.Write(p)\n}\n\nfunc (q *quicStreamConn) Close() error {\n\tq.closeOnce.Do(func() {\n\t\tq.closeErr = q.close()\n\t})\n\treturn q.closeErr\n}\n\nfunc (q *quicStreamConn) close() error {\n\tif q.closeDeferFn != nil {\n\t\tdefer q.closeDeferFn()\n\t}\n\n\t// https://github.com/cloudflare/cloudflared/commit/ed2bac026db46b239699ac5ce4fcf122d7cab2cd\n\t// Make sure a possible writer does not block the lock forever. We need it, so we can close the writer\n\t// side of the stream safely.\n\t_ = q.Stream.SetWriteDeadline(time.Now())\n\n\t// This lock is eventually acquired despite Write also acquiring it, because we set a deadline to writes.\n\tq.lock.Lock()\n\tdefer q.lock.Unlock()\n\n\t// We have to clean up the receiving stream ourselves since the Close in the bottom does not handle that.\n\tq.Stream.CancelRead(0)\n\treturn q.Stream.Close()\n}\n\nfunc (q *quicStreamConn) LocalAddr() net.Addr {\n\treturn q.lAddr\n}\n\nfunc (q *quicStreamConn) RemoteAddr() net.Addr {\n\treturn q.rAddr\n}\n\nvar _ net.Conn = (*quicStreamConn)(nil)\n\nfunc NewQuicStreamConn(stream *quic.Stream, lAddr, rAddr net.Addr, closeDeferFn func()) net.Conn {\n\treturn &quicStreamConn{Stream: stream, lAddr: lAddr, rAddr: rAddr, closeDeferFn: closeDeferFn}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/types/type.go",
    "content": "package types\n\nimport (\n\t\"bufio\"\n\t\"context\"\n\t\"errors\"\n\t\"net\"\n\t\"time\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\n\t\"github.com/metacubex/quic-go\"\n)\n\nvar (\n\tClientClosed       = errors.New(\"tuic: client closed\")\n\tTooManyOpenStreams = errors.New(\"tuic: too many open streams\")\n)\n\ntype DialFunc func(ctx context.Context) (quicConn *quic.Conn, err error)\n\ntype Client interface {\n\tDialContext(ctx context.Context, metadata *C.Metadata) (net.Conn, error)\n\tListenPacket(ctx context.Context, metadata *C.Metadata) (net.PacketConn, error)\n\tOpenStreams() int64\n\tLastVisited() time.Time\n\tSetLastVisited(last time.Time)\n\tClose()\n}\n\ntype ServerHandler interface {\n\tAuthOk() bool\n\tHandleTimeout()\n\tHandleStream(conn *N.BufferedConn) (err error)\n\tHandleMessage(message []byte) (err error)\n\tHandleUniStream(reader *bufio.Reader) (err error)\n}\n\ntype UdpRelayMode uint8\n\nconst (\n\tQUIC UdpRelayMode = iota\n\tNATIVE\n)\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/v4/client.go",
    "content": "package v4\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"net\"\n\t\"runtime\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\tatomic2 \"github.com/metacubex/mihomo/common/atomic\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/pool\"\n\t\"github.com/metacubex/mihomo/common/xsync\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/transport/tuic/types\"\n\n\t\"github.com/metacubex/quic-go\"\n\t\"github.com/metacubex/randv2\"\n)\n\ntype ClientOption struct {\n\tToken                 [32]byte\n\tUdpRelayMode          types.UdpRelayMode\n\tRequestTimeout        time.Duration\n\tMaxUdpRelayPacketSize int\n\tFastOpen              bool\n\tMaxOpenStreams        int64\n}\n\ntype clientImpl struct {\n\t*ClientOption\n\tdialFn types.DialFunc\n\tudp    bool\n\n\tquicConn  *quic.Conn\n\tconnMutex sync.Mutex\n\n\topenStreams atomic.Int64\n\tclosed      atomic.Bool\n\n\tudpInputMap xsync.Map[uint32, net.Conn]\n\n\t// only ready for PoolClient\n\tlastVisited atomic2.TypedValue[time.Time]\n}\n\nfunc (t *clientImpl) OpenStreams() int64 {\n\treturn t.openStreams.Load()\n}\n\nfunc (t *clientImpl) LastVisited() time.Time {\n\treturn t.lastVisited.Load()\n}\n\nfunc (t *clientImpl) SetLastVisited(last time.Time) {\n\tt.lastVisited.Store(last)\n}\n\nfunc (t *clientImpl) getQuicConn(ctx context.Context) (*quic.Conn, error) {\n\tt.connMutex.Lock()\n\tdefer t.connMutex.Unlock()\n\tif t.quicConn != nil {\n\t\treturn t.quicConn, nil\n\t}\n\tquicConn, err := t.dialFn(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tgo func() {\n\t\t_ = t.sendAuthentication(quicConn)\n\t}()\n\n\tif t.udp {\n\t\tgo func() {\n\t\t\tswitch t.UdpRelayMode {\n\t\t\tcase types.QUIC:\n\t\t\t\t_ = t.handleUniStream(quicConn)\n\t\t\tdefault: // native\n\t\t\t\t_ = t.handleMessage(quicConn)\n\t\t\t}\n\t\t}()\n\t}\n\n\tt.quicConn = quicConn\n\tt.openStreams.Store(0)\n\treturn quicConn, nil\n}\n\nfunc (t *clientImpl) sendAuthentication(quicConn *quic.Conn) (err error) {\n\tdefer func() {\n\t\tt.deferQuicConn(quicConn, err)\n\t}()\n\tstream, err := quicConn.OpenUniStream()\n\tif err != nil {\n\t\treturn err\n\t}\n\tbuf := pool.GetBuffer()\n\tdefer pool.PutBuffer(buf)\n\terr = NewAuthenticate(t.Token).WriteTo(buf)\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = buf.WriteTo(stream)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = stream.Close()\n\tif err != nil {\n\t\treturn\n\t}\n\treturn nil\n}\n\nfunc (t *clientImpl) handleUniStream(quicConn *quic.Conn) (err error) {\n\tdefer func() {\n\t\tt.deferQuicConn(quicConn, err)\n\t}()\n\tfor {\n\t\tvar stream *quic.ReceiveStream\n\t\tstream, err = quicConn.AcceptUniStream(context.Background())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tgo func() (err error) {\n\t\t\tvar assocId uint32\n\t\t\tdefer func() {\n\t\t\t\tt.deferQuicConn(quicConn, err)\n\t\t\t\tif err != nil && assocId != 0 {\n\t\t\t\t\tif val, ok := t.udpInputMap.LoadAndDelete(assocId); ok {\n\t\t\t\t\t\tif conn, ok := val.(net.Conn); ok {\n\t\t\t\t\t\t\t_ = conn.Close()\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tstream.CancelRead(0)\n\t\t\t}()\n\t\t\treader := bufio.NewReader(stream)\n\t\t\tcommandHead, err := ReadCommandHead(reader)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tswitch commandHead.TYPE {\n\t\t\tcase PacketType:\n\t\t\t\tvar packet Packet\n\t\t\t\tpacket, err = ReadPacketWithHead(commandHead, reader)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif t.udp && t.UdpRelayMode == types.QUIC {\n\t\t\t\t\tassocId = packet.ASSOC_ID\n\t\t\t\t\tif val, ok := t.udpInputMap.Load(assocId); ok {\n\t\t\t\t\t\tif conn, ok := val.(net.Conn); ok {\n\t\t\t\t\t\t\twriter := bufio.NewWriterSize(conn, packet.BytesLen())\n\t\t\t\t\t\t\t_ = packet.WriteTo(writer)\n\t\t\t\t\t\t\t_ = writer.Flush()\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn\n\t\t}()\n\t}\n}\n\nfunc (t *clientImpl) handleMessage(quicConn *quic.Conn) (err error) {\n\tdefer func() {\n\t\tt.deferQuicConn(quicConn, err)\n\t}()\n\tfor {\n\t\tvar message []byte\n\t\tmessage, err = quicConn.ReceiveDatagram(context.Background())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tgo func() (err error) {\n\t\t\tvar assocId uint32\n\t\t\tdefer func() {\n\t\t\t\tt.deferQuicConn(quicConn, err)\n\t\t\t\tif err != nil && assocId != 0 {\n\t\t\t\t\tif val, ok := t.udpInputMap.LoadAndDelete(assocId); ok {\n\t\t\t\t\t\tif conn, ok := val.(net.Conn); ok {\n\t\t\t\t\t\t\t_ = conn.Close()\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}()\n\t\t\treader := bytes.NewBuffer(message)\n\t\t\tcommandHead, err := ReadCommandHead(reader)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tswitch commandHead.TYPE {\n\t\t\tcase PacketType:\n\t\t\t\tvar packet Packet\n\t\t\t\tpacket, err = ReadPacketWithHead(commandHead, reader)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif t.udp && t.UdpRelayMode == types.NATIVE {\n\t\t\t\t\tassocId = packet.ASSOC_ID\n\t\t\t\t\tif val, ok := t.udpInputMap.Load(assocId); ok {\n\t\t\t\t\t\tif conn, ok := val.(net.Conn); ok {\n\t\t\t\t\t\t\t_, _ = conn.Write(message)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn\n\t\t}()\n\t}\n}\n\nfunc (t *clientImpl) deferQuicConn(quicConn *quic.Conn, err error) {\n\tvar netError net.Error\n\tif err != nil && errors.As(err, &netError) {\n\t\tt.forceClose(quicConn, err)\n\t}\n}\n\nfunc (t *clientImpl) forceClose(quicConn *quic.Conn, err error) {\n\tt.connMutex.Lock()\n\tdefer t.connMutex.Unlock()\n\tif quicConn == nil {\n\t\tquicConn = t.quicConn\n\t}\n\tif quicConn != nil {\n\t\tif quicConn == t.quicConn {\n\t\t\tt.quicConn = nil\n\t\t}\n\t}\n\terrStr := \"\"\n\tif err != nil {\n\t\terrStr = err.Error()\n\t}\n\tif quicConn != nil {\n\t\t_ = quicConn.CloseWithError(ProtocolError, errStr)\n\t}\n\tudpInputMap := &t.udpInputMap\n\tudpInputMap.Range(func(key uint32, value net.Conn) bool {\n\t\tconn := value\n\t\t_ = conn.Close()\n\t\tudpInputMap.Delete(key)\n\t\treturn true\n\t})\n}\n\nfunc (t *clientImpl) Close() {\n\tt.closed.Store(true)\n\tif t.openStreams.Load() == 0 {\n\t\tt.forceClose(nil, types.ClientClosed)\n\t}\n}\n\nfunc (t *clientImpl) DialContext(ctx context.Context, metadata *C.Metadata) (net.Conn, error) {\n\tquicConn, err := t.getQuicConn(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\topenStreams := t.openStreams.Add(1)\n\tif openStreams >= t.MaxOpenStreams {\n\t\tt.openStreams.Add(-1)\n\t\treturn nil, types.TooManyOpenStreams\n\t}\n\tstream, err := func() (stream net.Conn, err error) {\n\t\tdefer func() {\n\t\t\tt.deferQuicConn(quicConn, err)\n\t\t}()\n\t\tbuf := pool.GetBuffer()\n\t\tdefer pool.PutBuffer(buf)\n\t\terr = NewConnect(NewAddress(metadata)).WriteTo(buf)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tquicStream, err := quicConn.OpenStream()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tstream = types.NewQuicStreamConn(\n\t\t\tquicStream,\n\t\t\tquicConn.LocalAddr(),\n\t\t\tquicConn.RemoteAddr(),\n\t\t\tfunc() {\n\t\t\t\ttime.AfterFunc(C.DefaultTCPTimeout, func() {\n\t\t\t\t\topenStreams := t.openStreams.Add(-1)\n\t\t\t\t\tif openStreams == 0 && t.closed.Load() {\n\t\t\t\t\t\tt.forceClose(quicConn, types.ClientClosed)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t},\n\t\t)\n\t\t_, err = buf.WriteTo(stream)\n\t\tif err != nil {\n\t\t\t_ = stream.Close()\n\t\t\treturn nil, err\n\t\t}\n\t\treturn stream, err\n\t}()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tbufConn := N.NewBufferedConn(stream)\n\tresponse := func() error {\n\t\tif t.RequestTimeout > 0 {\n\t\t\t_ = bufConn.SetReadDeadline(time.Now().Add(t.RequestTimeout))\n\t\t}\n\t\tresponse, err := ReadResponse(bufConn)\n\t\tif err != nil {\n\t\t\t_ = bufConn.Close()\n\t\t\treturn err\n\t\t}\n\t\tif response.IsFailed() {\n\t\t\t_ = bufConn.Close()\n\t\t\treturn errors.New(\"connect failed\")\n\t\t}\n\t\t_ = bufConn.SetReadDeadline(time.Time{})\n\t\treturn nil\n\t}\n\tif t.FastOpen {\n\t\treturn N.NewEarlyConn(bufConn, response), nil\n\t}\n\terr = response()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn bufConn, nil\n}\n\nfunc (t *clientImpl) ListenPacket(ctx context.Context, metadata *C.Metadata) (net.PacketConn, error) {\n\tquicConn, err := t.getQuicConn(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\topenStreams := t.openStreams.Add(1)\n\tif openStreams >= t.MaxOpenStreams {\n\t\tt.openStreams.Add(-1)\n\t\treturn nil, types.TooManyOpenStreams\n\t}\n\n\tpipe1, pipe2 := N.Pipe()\n\tvar connId uint32\n\tfor {\n\t\tconnId = randv2.Uint32()\n\t\t_, loaded := t.udpInputMap.LoadOrStore(connId, pipe1)\n\t\tif !loaded {\n\t\t\tbreak\n\t\t}\n\t}\n\tpc := &quicStreamPacketConn{\n\t\tconnId:                connId,\n\t\tquicConn:              quicConn,\n\t\tinputConn:             N.NewBufferedConn(pipe2),\n\t\tudpRelayMode:          t.UdpRelayMode,\n\t\tmaxUdpRelayPacketSize: t.MaxUdpRelayPacketSize,\n\t\tdeferQuicConnFn:       t.deferQuicConn,\n\t\tcloseDeferFn: func() {\n\t\t\tt.udpInputMap.Delete(connId)\n\t\t\ttime.AfterFunc(C.DefaultUDPTimeout, func() {\n\t\t\t\topenStreams := t.openStreams.Add(-1)\n\t\t\t\tif openStreams == 0 && t.closed.Load() {\n\t\t\t\t\tt.forceClose(quicConn, types.ClientClosed)\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\t}\n\treturn pc, nil\n}\n\ntype Client struct {\n\t*clientImpl // use an independent pointer to let Finalizer can work no matter somewhere handle an influence in clientImpl inner\n}\n\nfunc (t *Client) DialContext(ctx context.Context, metadata *C.Metadata) (net.Conn, error) {\n\tconn, err := t.clientImpl.DialContext(ctx, metadata)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn N.NewRefConn(conn, t), err\n}\n\nfunc (t *Client) ListenPacket(ctx context.Context, metadata *C.Metadata) (net.PacketConn, error) {\n\tpc, err := t.clientImpl.ListenPacket(ctx, metadata)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn N.NewRefPacketConn(pc, t), nil\n}\n\nfunc (t *Client) forceClose() {\n\tt.clientImpl.forceClose(nil, types.ClientClosed)\n}\n\nfunc NewClient(clientOption *ClientOption, udp bool, dialFn types.DialFunc) *Client {\n\tci := &clientImpl{\n\t\tClientOption: clientOption,\n\t\tdialFn:       dialFn,\n\t\tudp:          udp,\n\t}\n\tc := &Client{ci}\n\truntime.SetFinalizer(c, closeClient)\n\tlog.Debugln(\"New TuicV4 Client at %p\", c)\n\treturn c\n}\n\nfunc closeClient(client *Client) {\n\tlog.Debugln(\"Close TuicV4 Client at %p\", client)\n\tclient.forceClose()\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/v4/packet.go",
    "content": "package v4\n\nimport (\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/atomic\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/pool\"\n\t\"github.com/metacubex/mihomo/transport/tuic/types\"\n\n\t\"github.com/metacubex/quic-go\"\n)\n\ntype quicStreamPacketConn struct {\n\tconnId    uint32\n\tquicConn  *quic.Conn\n\tinputConn *N.BufferedConn\n\n\tudpRelayMode          types.UdpRelayMode\n\tmaxUdpRelayPacketSize int\n\n\tdeferQuicConnFn func(quicConn *quic.Conn, err error)\n\tcloseDeferFn    func()\n\twriteClosed     *atomic.Bool\n\n\tcloseOnce sync.Once\n\tcloseErr  error\n\tclosed    bool\n}\n\nfunc (q *quicStreamPacketConn) Close() error {\n\tq.closeOnce.Do(func() {\n\t\tq.closed = true\n\t\tq.closeErr = q.close()\n\t})\n\treturn q.closeErr\n}\n\nfunc (q *quicStreamPacketConn) close() (err error) {\n\tif q.closeDeferFn != nil {\n\t\tdefer q.closeDeferFn()\n\t}\n\tif q.deferQuicConnFn != nil {\n\t\tdefer func() {\n\t\t\tq.deferQuicConnFn(q.quicConn, err)\n\t\t}()\n\t}\n\tif q.inputConn != nil {\n\t\t_ = q.inputConn.Close()\n\t\tq.inputConn = nil\n\n\t\tbuf := pool.GetBuffer()\n\t\tdefer pool.PutBuffer(buf)\n\t\terr = NewDissociate(q.connId).WriteTo(buf)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tvar stream *quic.SendStream\n\t\tstream, err = q.quicConn.OpenUniStream()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\t_, err = buf.WriteTo(stream)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\terr = stream.Close()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\nfunc (q *quicStreamPacketConn) SetDeadline(t time.Time) error {\n\t//TODO implement me\n\treturn nil\n}\n\nfunc (q *quicStreamPacketConn) SetReadDeadline(t time.Time) error {\n\tif q.inputConn != nil {\n\t\treturn q.inputConn.SetReadDeadline(t)\n\t}\n\treturn nil\n}\n\nfunc (q *quicStreamPacketConn) SetWriteDeadline(t time.Time) error {\n\t//TODO implement me\n\treturn nil\n}\n\nfunc (q *quicStreamPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {\n\tif q.inputConn != nil {\n\t\tvar packet Packet\n\t\tpacket, err = ReadPacket(q.inputConn)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tn = copy(p, packet.DATA)\n\t\taddr = packet.ADDR.UDPAddr()\n\t} else {\n\t\terr = net.ErrClosed\n\t}\n\treturn\n}\n\nfunc (q *quicStreamPacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {\n\tif q.inputConn != nil {\n\t\tvar packet Packet\n\t\tpacket, err = ReadPacket(q.inputConn)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tdata = packet.DATA\n\t\taddr = packet.ADDR.UDPAddr()\n\t} else {\n\t\terr = net.ErrClosed\n\t}\n\treturn\n}\n\nfunc (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {\n\tif q.udpRelayMode != types.QUIC && len(p) > q.maxUdpRelayPacketSize {\n\t\treturn 0, &quic.DatagramTooLargeError{MaxDatagramPayloadSize: int64(q.maxUdpRelayPacketSize)}\n\t}\n\tif q.closed {\n\t\treturn 0, net.ErrClosed\n\t}\n\tif q.writeClosed != nil && q.writeClosed.Load() {\n\t\t_ = q.Close()\n\t\treturn 0, net.ErrClosed\n\t}\n\tif q.deferQuicConnFn != nil {\n\t\tdefer func() {\n\t\t\tq.deferQuicConnFn(q.quicConn, err)\n\t\t}()\n\t}\n\tbuf := pool.GetBuffer()\n\tdefer pool.PutBuffer(buf)\n\taddress, err := NewAddressNetAddr(addr)\n\tif err != nil {\n\t\treturn\n\t}\n\terr = NewPacket(q.connId, uint16(len(p)), address, p).WriteTo(buf)\n\tif err != nil {\n\t\treturn\n\t}\n\tswitch q.udpRelayMode {\n\tcase types.QUIC:\n\t\tvar stream *quic.SendStream\n\t\tstream, err = q.quicConn.OpenUniStream()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tdefer stream.Close()\n\t\t_, err = buf.WriteTo(stream)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\tdefault: // native\n\t\tdata := buf.Bytes()\n\t\terr = q.quicConn.SendDatagram(data)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tn = len(p)\n\n\treturn\n}\n\nfunc (q *quicStreamPacketConn) LocalAddr() net.Addr {\n\treturn q.quicConn.LocalAddr()\n}\n\nvar _ net.PacketConn = (*quicStreamPacketConn)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/v4/protocol.go",
    "content": "package v4\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/netip\"\n\t\"strconv\"\n\n\t\"github.com/metacubex/blake3\"\n\t\"github.com/metacubex/quic-go\"\n\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n)\n\ntype BufferedReader interface {\n\tio.Reader\n\tio.ByteReader\n}\n\ntype BufferedWriter interface {\n\tio.Writer\n\tio.ByteWriter\n}\n\ntype CommandType byte\n\nconst (\n\tAuthenticateType = CommandType(0x00)\n\tConnectType      = CommandType(0x01)\n\tPacketType       = CommandType(0x02)\n\tDissociateType   = CommandType(0x03)\n\tHeartbeatType    = CommandType(0x04)\n\tResponseType     = CommandType(0xff)\n)\n\nconst VER byte = 0x04\n\nfunc (c CommandType) String() string {\n\tswitch c {\n\tcase AuthenticateType:\n\t\treturn \"Authenticate\"\n\tcase ConnectType:\n\t\treturn \"Connect\"\n\tcase PacketType:\n\t\treturn \"Packet\"\n\tcase DissociateType:\n\t\treturn \"Dissociate\"\n\tcase HeartbeatType:\n\t\treturn \"Heartbeat\"\n\tcase ResponseType:\n\t\treturn \"Response\"\n\tdefault:\n\t\treturn fmt.Sprintf(\"UnknowCommand: %#x\", byte(c))\n\t}\n}\n\nfunc (c CommandType) BytesLen() int {\n\treturn 1\n}\n\ntype CommandHead struct {\n\tVER  byte\n\tTYPE CommandType\n}\n\nfunc NewCommandHead(TYPE CommandType) CommandHead {\n\treturn CommandHead{\n\t\tVER:  VER,\n\t\tTYPE: TYPE,\n\t}\n}\n\nfunc ReadCommandHead(reader BufferedReader) (c CommandHead, err error) {\n\tc.VER, err = reader.ReadByte()\n\tif err != nil {\n\t\treturn\n\t}\n\tTYPE, err := reader.ReadByte()\n\tif err != nil {\n\t\treturn\n\t}\n\tc.TYPE = CommandType(TYPE)\n\treturn\n}\n\nfunc (c CommandHead) WriteTo(writer BufferedWriter) (err error) {\n\terr = writer.WriteByte(c.VER)\n\tif err != nil {\n\t\treturn\n\t}\n\terr = writer.WriteByte(byte(c.TYPE))\n\tif err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (c CommandHead) BytesLen() int {\n\treturn 1 + c.TYPE.BytesLen()\n}\n\ntype Authenticate struct {\n\tCommandHead\n\tTKN [32]byte\n}\n\nfunc NewAuthenticate(TKN [32]byte) Authenticate {\n\treturn Authenticate{\n\t\tCommandHead: NewCommandHead(AuthenticateType),\n\t\tTKN:         TKN,\n\t}\n}\n\nfunc ReadAuthenticateWithHead(head CommandHead, reader BufferedReader) (c Authenticate, err error) {\n\tc.CommandHead = head\n\tif c.CommandHead.TYPE != AuthenticateType {\n\t\terr = fmt.Errorf(\"error command type: %s\", c.CommandHead.TYPE)\n\t\treturn\n\t}\n\t_, err = io.ReadFull(reader, c.TKN[:])\n\tif err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc ReadAuthenticate(reader BufferedReader) (c Authenticate, err error) {\n\thead, err := ReadCommandHead(reader)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn ReadAuthenticateWithHead(head, reader)\n}\n\nfunc GenTKN(token string) [32]byte {\n\treturn blake3.Sum256([]byte(token))\n}\n\nfunc (c Authenticate) WriteTo(writer BufferedWriter) (err error) {\n\terr = c.CommandHead.WriteTo(writer)\n\tif err != nil {\n\t\treturn\n\t}\n\t_, err = writer.Write(c.TKN[:])\n\tif err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (c Authenticate) BytesLen() int {\n\treturn c.CommandHead.BytesLen() + 32\n}\n\ntype Connect struct {\n\tCommandHead\n\tADDR Address\n}\n\nfunc NewConnect(ADDR Address) Connect {\n\treturn Connect{\n\t\tCommandHead: NewCommandHead(ConnectType),\n\t\tADDR:        ADDR,\n\t}\n}\n\nfunc ReadConnectWithHead(head CommandHead, reader BufferedReader) (c Connect, err error) {\n\tc.CommandHead = head\n\tif c.CommandHead.TYPE != ConnectType {\n\t\terr = fmt.Errorf(\"error command type: %s\", c.CommandHead.TYPE)\n\t\treturn\n\t}\n\tc.ADDR, err = ReadAddress(reader)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc ReadConnect(reader BufferedReader) (c Connect, err error) {\n\thead, err := ReadCommandHead(reader)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn ReadConnectWithHead(head, reader)\n}\n\nfunc (c Connect) WriteTo(writer BufferedWriter) (err error) {\n\terr = c.CommandHead.WriteTo(writer)\n\tif err != nil {\n\t\treturn\n\t}\n\terr = c.ADDR.WriteTo(writer)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (c Connect) BytesLen() int {\n\treturn c.CommandHead.BytesLen() + c.ADDR.BytesLen()\n}\n\ntype Packet struct {\n\tCommandHead\n\tASSOC_ID uint32\n\tLEN      uint16\n\tADDR     Address\n\tDATA     []byte\n}\n\nfunc NewPacket(ASSOC_ID uint32, LEN uint16, ADDR Address, DATA []byte) Packet {\n\treturn Packet{\n\t\tCommandHead: NewCommandHead(PacketType),\n\t\tASSOC_ID:    ASSOC_ID,\n\t\tLEN:         LEN,\n\t\tADDR:        ADDR,\n\t\tDATA:        DATA,\n\t}\n}\n\nfunc ReadPacketWithHead(head CommandHead, reader BufferedReader) (c Packet, err error) {\n\tc.CommandHead = head\n\tif c.CommandHead.TYPE != PacketType {\n\t\terr = fmt.Errorf(\"error command type: %s\", c.CommandHead.TYPE)\n\t\treturn\n\t}\n\terr = binary.Read(reader, binary.BigEndian, &c.ASSOC_ID)\n\tif err != nil {\n\t\treturn\n\t}\n\terr = binary.Read(reader, binary.BigEndian, &c.LEN)\n\tif err != nil {\n\t\treturn\n\t}\n\tc.ADDR, err = ReadAddress(reader)\n\tif err != nil {\n\t\treturn\n\t}\n\tc.DATA = make([]byte, c.LEN)\n\t_, err = io.ReadFull(reader, c.DATA)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc ReadPacket(reader BufferedReader) (c Packet, err error) {\n\thead, err := ReadCommandHead(reader)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn ReadPacketWithHead(head, reader)\n}\n\nfunc (c Packet) WriteTo(writer BufferedWriter) (err error) {\n\terr = c.CommandHead.WriteTo(writer)\n\tif err != nil {\n\t\treturn\n\t}\n\terr = binary.Write(writer, binary.BigEndian, c.ASSOC_ID)\n\tif err != nil {\n\t\treturn\n\t}\n\terr = binary.Write(writer, binary.BigEndian, c.LEN)\n\tif err != nil {\n\t\treturn\n\t}\n\terr = c.ADDR.WriteTo(writer)\n\tif err != nil {\n\t\treturn\n\t}\n\t_, err = writer.Write(c.DATA)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (c Packet) BytesLen() int {\n\treturn c.CommandHead.BytesLen() + 4 + 2 + c.ADDR.BytesLen() + len(c.DATA)\n}\n\nvar PacketOverHead = NewPacket(0, 0, NewAddressAddrPort(netip.AddrPortFrom(netip.IPv6Unspecified(), 0)), nil).BytesLen()\n\ntype Dissociate struct {\n\tCommandHead\n\tASSOC_ID uint32\n}\n\nfunc NewDissociate(ASSOC_ID uint32) Dissociate {\n\treturn Dissociate{\n\t\tCommandHead: NewCommandHead(DissociateType),\n\t\tASSOC_ID:    ASSOC_ID,\n\t}\n}\n\nfunc ReadDissociateWithHead(head CommandHead, reader BufferedReader) (c Dissociate, err error) {\n\tc.CommandHead = head\n\tif c.CommandHead.TYPE != DissociateType {\n\t\terr = fmt.Errorf(\"error command type: %s\", c.CommandHead.TYPE)\n\t\treturn\n\t}\n\terr = binary.Read(reader, binary.BigEndian, &c.ASSOC_ID)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc ReadDissociate(reader BufferedReader) (c Dissociate, err error) {\n\thead, err := ReadCommandHead(reader)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn ReadDissociateWithHead(head, reader)\n}\n\nfunc (c Dissociate) WriteTo(writer BufferedWriter) (err error) {\n\terr = c.CommandHead.WriteTo(writer)\n\tif err != nil {\n\t\treturn\n\t}\n\terr = binary.Write(writer, binary.BigEndian, c.ASSOC_ID)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (c Dissociate) BytesLen() int {\n\treturn c.CommandHead.BytesLen() + 4\n}\n\ntype Heartbeat struct {\n\tCommandHead\n}\n\nfunc NewHeartbeat() Heartbeat {\n\treturn Heartbeat{\n\t\tCommandHead: NewCommandHead(HeartbeatType),\n\t}\n}\n\nfunc ReadHeartbeatWithHead(head CommandHead, reader BufferedReader) (c Heartbeat, err error) {\n\tc.CommandHead = head\n\tif c.CommandHead.TYPE != HeartbeatType {\n\t\terr = fmt.Errorf(\"error command type: %s\", c.CommandHead.TYPE)\n\t\treturn\n\t}\n\treturn\n}\n\nfunc ReadHeartbeat(reader BufferedReader) (c Heartbeat, err error) {\n\thead, err := ReadCommandHead(reader)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn ReadHeartbeatWithHead(head, reader)\n}\n\ntype Response struct {\n\tCommandHead\n\tREP byte\n}\n\nfunc NewResponse(REP byte) Response {\n\treturn Response{\n\t\tCommandHead: NewCommandHead(ResponseType),\n\t\tREP:         REP,\n\t}\n}\n\nfunc NewResponseSucceed() Response {\n\treturn NewResponse(0x00)\n}\n\nfunc NewResponseFailed() Response {\n\treturn NewResponse(0xff)\n}\n\nfunc ReadResponseWithHead(head CommandHead, reader BufferedReader) (c Response, err error) {\n\tc.CommandHead = head\n\tif c.CommandHead.TYPE != ResponseType {\n\t\terr = fmt.Errorf(\"error command type: %s\", c.CommandHead.TYPE)\n\t\treturn\n\t}\n\tc.REP, err = reader.ReadByte()\n\tif err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc ReadResponse(reader BufferedReader) (c Response, err error) {\n\thead, err := ReadCommandHead(reader)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn ReadResponseWithHead(head, reader)\n}\n\nfunc (c Response) WriteTo(writer BufferedWriter) (err error) {\n\terr = c.CommandHead.WriteTo(writer)\n\tif err != nil {\n\t\treturn\n\t}\n\terr = writer.WriteByte(c.REP)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (c Response) IsSucceed() bool {\n\treturn c.REP == 0x00\n}\n\nfunc (c Response) IsFailed() bool {\n\treturn c.REP == 0xff\n}\n\nfunc (c Response) BytesLen() int {\n\treturn c.CommandHead.BytesLen() + 1\n}\n\n// Addr types\nconst (\n\tAtypDomainName byte = 0\n\tAtypIPv4       byte = 1\n\tAtypIPv6       byte = 2\n)\n\ntype Address struct {\n\tTYPE byte\n\tADDR []byte\n\tPORT uint16\n}\n\nfunc NewAddress(metadata *C.Metadata) Address {\n\tvar addrType byte\n\tvar addr []byte\n\tswitch metadata.AddrType() {\n\tcase C.AtypIPv4:\n\t\taddrType = AtypIPv4\n\t\taddr = metadata.DstIP.AsSlice()\n\tcase C.AtypIPv6:\n\t\taddrType = AtypIPv6\n\t\taddr = metadata.DstIP.AsSlice()\n\tcase C.AtypDomainName:\n\t\taddrType = AtypDomainName\n\t\taddr = make([]byte, len(metadata.Host)+1)\n\t\taddr[0] = byte(len(metadata.Host))\n\t\tcopy(addr[1:], metadata.Host)\n\t}\n\n\treturn Address{\n\t\tTYPE: addrType,\n\t\tADDR: addr,\n\t\tPORT: metadata.DstPort,\n\t}\n}\n\nfunc NewAddressNetAddr(addr net.Addr) (Address, error) {\n\tif addr, ok := addr.(interface{ AddrPort() netip.AddrPort }); ok {\n\t\tif addrPort := addr.AddrPort(); addrPort.IsValid() { // sing's M.Socksaddr maybe return an invalid AddrPort if it's a DomainName\n\t\t\treturn NewAddressAddrPort(addrPort), nil\n\t\t}\n\t}\n\taddrStr := addr.String()\n\tif addrPort, err := netip.ParseAddrPort(addrStr); err == nil {\n\t\treturn NewAddressAddrPort(addrPort), nil\n\t}\n\tmetadata := &C.Metadata{}\n\tif err := metadata.SetRemoteAddress(addrStr); err != nil {\n\t\treturn Address{}, err\n\t}\n\treturn NewAddress(metadata), nil\n}\n\nfunc NewAddressAddrPort(addrPort netip.AddrPort) Address {\n\tvar addrType byte\n\tport := addrPort.Port()\n\taddr := addrPort.Addr().Unmap()\n\tif addr.Is4() {\n\t\taddrType = AtypIPv4\n\t} else {\n\t\taddrType = AtypIPv6\n\t}\n\treturn Address{\n\t\tTYPE: addrType,\n\t\tADDR: addr.AsSlice(),\n\t\tPORT: port,\n\t}\n}\n\nfunc ReadAddress(reader BufferedReader) (c Address, err error) {\n\tc.TYPE, err = reader.ReadByte()\n\tif err != nil {\n\t\treturn\n\t}\n\tswitch c.TYPE {\n\tcase AtypIPv4:\n\t\tc.ADDR = make([]byte, net.IPv4len)\n\t\t_, err = io.ReadFull(reader, c.ADDR)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\tcase AtypIPv6:\n\t\tc.ADDR = make([]byte, net.IPv6len)\n\t\t_, err = io.ReadFull(reader, c.ADDR)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\tcase AtypDomainName:\n\t\tvar addrLen byte\n\t\taddrLen, err = reader.ReadByte()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tc.ADDR = make([]byte, addrLen+1)\n\t\tc.ADDR[0] = addrLen\n\t\t_, err = io.ReadFull(reader, c.ADDR[1:])\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\terr = binary.Read(reader, binary.BigEndian, &c.PORT)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (c Address) WriteTo(writer BufferedWriter) (err error) {\n\terr = writer.WriteByte(c.TYPE)\n\tif err != nil {\n\t\treturn\n\t}\n\t_, err = writer.Write(c.ADDR[:])\n\tif err != nil {\n\t\treturn\n\t}\n\terr = binary.Write(writer, binary.BigEndian, c.PORT)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (c Address) String() string {\n\tswitch c.TYPE {\n\tcase AtypDomainName:\n\t\treturn net.JoinHostPort(string(c.ADDR[1:]), strconv.Itoa(int(c.PORT)))\n\tdefault:\n\t\taddr, _ := netip.AddrFromSlice(c.ADDR)\n\t\taddrPort := netip.AddrPortFrom(addr, c.PORT)\n\t\treturn addrPort.String()\n\t}\n}\n\nfunc (c Address) SocksAddr() socks5.Addr {\n\taddr := make([]byte, 1+len(c.ADDR)+2)\n\tswitch c.TYPE {\n\tcase AtypIPv4:\n\t\taddr[0] = socks5.AtypIPv4\n\tcase AtypIPv6:\n\t\taddr[0] = socks5.AtypIPv6\n\tcase AtypDomainName:\n\t\taddr[0] = socks5.AtypDomainName\n\t}\n\tcopy(addr[1:], c.ADDR)\n\tbinary.BigEndian.PutUint16(addr[len(addr)-2:], c.PORT)\n\treturn addr\n}\n\nfunc (c Address) UDPAddr() *net.UDPAddr {\n\treturn &net.UDPAddr{\n\t\tIP:   c.ADDR,\n\t\tPort: int(c.PORT),\n\t\tZone: \"\",\n\t}\n}\n\nfunc (c Address) BytesLen() int {\n\treturn 1 + len(c.ADDR) + 2\n}\n\nconst (\n\tProtocolError         = quic.ApplicationErrorCode(0xfffffff0)\n\tAuthenticationFailed  = quic.ApplicationErrorCode(0xfffffff1)\n\tAuthenticationTimeout = quic.ApplicationErrorCode(0xfffffff2)\n\tBadCommand            = quic.ApplicationErrorCode(0xfffffff3)\n)\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/v4/server.go",
    "content": "package v4\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"fmt\"\n\t\"net\"\n\t\"sync\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\t\"github.com/metacubex/mihomo/common/atomic\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/pool\"\n\t\"github.com/metacubex/mihomo/common/xsync\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n\t\"github.com/metacubex/mihomo/transport/tuic/types\"\n\n\t\"github.com/gofrs/uuid/v5\"\n\t\"github.com/metacubex/quic-go\"\n)\n\ntype ServerOption struct {\n\tHandleTcpFn func(conn net.Conn, addr socks5.Addr, additions ...inbound.Addition) error\n\tHandleUdpFn func(addr socks5.Addr, packet C.UDPPacket, additions ...inbound.Addition) error\n\n\tTokens                [][32]byte\n\tMaxUdpRelayPacketSize int\n}\n\nfunc NewServerHandler(option *ServerOption, quicConn *quic.Conn, uuid uuid.UUID) types.ServerHandler {\n\treturn &serverHandler{\n\t\tServerOption: option,\n\t\tquicConn:     quicConn,\n\t\tuuid:         uuid,\n\t\tauthCh:       make(chan struct{}),\n\t}\n}\n\ntype serverHandler struct {\n\t*ServerOption\n\tquicConn *quic.Conn\n\tuuid     uuid.UUID\n\n\tauthCh   chan struct{}\n\tauthOk   atomic.Bool\n\tauthOnce sync.Once\n\n\tudpInputMap xsync.Map[uint32, *atomic.Bool]\n}\n\nfunc (s *serverHandler) AuthOk() bool {\n\treturn s.authOk.Load()\n}\n\nfunc (s *serverHandler) HandleTimeout() {\n\ts.authOnce.Do(func() {\n\t\t_ = s.quicConn.CloseWithError(AuthenticationTimeout, \"AuthenticationTimeout\")\n\t\ts.authOk.Store(false)\n\t\tclose(s.authCh)\n\t})\n}\n\nfunc (s *serverHandler) HandleMessage(message []byte) (err error) {\n\tbuffer := bytes.NewBuffer(message)\n\tpacket, err := ReadPacket(buffer)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn s.parsePacket(&packet, types.NATIVE)\n}\n\nfunc (s *serverHandler) parsePacket(packet *Packet, udpRelayMode types.UdpRelayMode) (err error) {\n\t<-s.authCh\n\tif !s.authOk.Load() {\n\t\treturn\n\t}\n\tvar assocId uint32\n\n\tassocId = packet.ASSOC_ID\n\n\twriteClosed, _ := s.udpInputMap.LoadOrStoreFn(assocId, func() *atomic.Bool { return &atomic.Bool{} })\n\tif writeClosed.Load() {\n\t\treturn nil\n\t}\n\n\tpc := &quicStreamPacketConn{\n\t\tconnId:                assocId,\n\t\tquicConn:              s.quicConn,\n\t\tinputConn:             nil,\n\t\tudpRelayMode:          udpRelayMode,\n\t\tmaxUdpRelayPacketSize: s.MaxUdpRelayPacketSize,\n\t\tdeferQuicConnFn:       nil,\n\t\tcloseDeferFn:          nil,\n\t\twriteClosed:           writeClosed,\n\t}\n\n\treturn s.HandleUdpFn(packet.ADDR.SocksAddr(), &serverUDPPacket{\n\t\tpc:     pc,\n\t\tpacket: packet,\n\t\trAddr:  N.NewCustomAddr(\"tuic\", fmt.Sprintf(\"tuic-%s-%d\", s.uuid, assocId), s.quicConn.RemoteAddr()), // for tunnel's handleUDPConn\n\t})\n}\n\nfunc (s *serverHandler) HandleStream(conn *N.BufferedConn) (err error) {\n\tconnect, err := ReadConnect(conn)\n\tif err != nil {\n\t\treturn err\n\t}\n\t<-s.authCh\n\tif !s.authOk.Load() {\n\t\treturn conn.Close()\n\t}\n\n\tbuf := pool.GetBuffer()\n\tdefer pool.PutBuffer(buf)\n\terr = s.HandleTcpFn(conn, connect.ADDR.SocksAddr())\n\tif err != nil {\n\t\terr = NewResponseFailed().WriteTo(buf)\n\t\tdefer conn.Close()\n\t} else {\n\t\terr = NewResponseSucceed().WriteTo(buf)\n\t}\n\tif err != nil {\n\t\t_ = conn.Close()\n\t\treturn err\n\t}\n\t_, err = buf.WriteTo(conn)\n\tif err != nil {\n\t\t_ = conn.Close()\n\t\treturn err\n\t}\n\n\treturn\n}\n\nfunc (s *serverHandler) HandleUniStream(reader *bufio.Reader) (err error) {\n\tcommandHead, err := ReadCommandHead(reader)\n\tif err != nil {\n\t\treturn\n\t}\n\tswitch commandHead.TYPE {\n\tcase AuthenticateType:\n\t\tvar authenticate Authenticate\n\t\tauthenticate, err = ReadAuthenticateWithHead(commandHead, reader)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tauthOk := false\n\t\tfor _, tkn := range s.Tokens {\n\t\t\tif authenticate.TKN == tkn {\n\t\t\t\tauthOk = true\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t\ts.authOnce.Do(func() {\n\t\t\tif !authOk {\n\t\t\t\t_ = s.quicConn.CloseWithError(AuthenticationFailed, \"AuthenticationFailed\")\n\t\t\t}\n\t\t\ts.authOk.Store(authOk)\n\t\t\tclose(s.authCh)\n\t\t})\n\tcase PacketType:\n\t\tvar packet Packet\n\t\tpacket, err = ReadPacketWithHead(commandHead, reader)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\treturn s.parsePacket(&packet, types.QUIC)\n\tcase DissociateType:\n\t\tvar disassociate Dissociate\n\t\tdisassociate, err = ReadDissociateWithHead(commandHead, reader)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif writeClosed, loaded := s.udpInputMap.LoadAndDelete(disassociate.ASSOC_ID); loaded {\n\t\t\twriteClosed.Store(true)\n\t\t}\n\tcase HeartbeatType:\n\t\tvar heartbeat Heartbeat\n\t\theartbeat, err = ReadHeartbeatWithHead(commandHead, reader)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\theartbeat.BytesLen()\n\t}\n\treturn\n}\n\ntype serverUDPPacket struct {\n\tpc     *quicStreamPacketConn\n\tpacket *Packet\n\trAddr  net.Addr\n}\n\nfunc (s *serverUDPPacket) InAddr() net.Addr {\n\treturn s.pc.LocalAddr()\n}\n\nfunc (s *serverUDPPacket) LocalAddr() net.Addr {\n\treturn s.rAddr\n}\n\nfunc (s *serverUDPPacket) Data() []byte {\n\treturn s.packet.DATA\n}\n\nfunc (s *serverUDPPacket) WriteBack(b []byte, addr net.Addr) (n int, err error) {\n\treturn s.pc.WriteTo(b, addr)\n}\n\nfunc (s *serverUDPPacket) Drop() {\n\ts.packet.DATA = nil\n}\n\nvar _ C.UDPPacket = (*serverUDPPacket)(nil)\nvar _ C.UDPPacketInAddr = (*serverUDPPacket)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/v5/client.go",
    "content": "package v5\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"net\"\n\t\"runtime\"\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\tatomic2 \"github.com/metacubex/mihomo/common/atomic\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/pool\"\n\t\"github.com/metacubex/mihomo/common/xsync\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/transport/tuic/types\"\n\n\t\"github.com/metacubex/quic-go\"\n\t\"github.com/metacubex/randv2\"\n)\n\ntype ClientOption struct {\n\tUuid                  [16]byte\n\tPassword              string\n\tUdpRelayMode          types.UdpRelayMode\n\tMaxUdpRelayPacketSize int\n\tMaxOpenStreams        int64\n}\n\ntype clientImpl struct {\n\t*ClientOption\n\tdialFn types.DialFunc\n\tudp    bool\n\n\tquicConn  *quic.Conn\n\tconnMutex sync.Mutex\n\n\topenStreams atomic.Int64\n\tclosed      atomic.Bool\n\n\tudpInputMap xsync.Map[uint16, net.Conn]\n\n\t// only ready for PoolClient\n\tlastVisited atomic2.TypedValue[time.Time]\n}\n\nfunc (t *clientImpl) OpenStreams() int64 {\n\treturn t.openStreams.Load()\n}\n\nfunc (t *clientImpl) LastVisited() time.Time {\n\treturn t.lastVisited.Load()\n}\n\nfunc (t *clientImpl) SetLastVisited(last time.Time) {\n\tt.lastVisited.Store(last)\n}\n\nfunc (t *clientImpl) getQuicConn(ctx context.Context) (*quic.Conn, error) {\n\tt.connMutex.Lock()\n\tdefer t.connMutex.Unlock()\n\tif t.quicConn != nil {\n\t\treturn t.quicConn, nil\n\t}\n\tquicConn, err := t.dialFn(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tgo func() {\n\t\t_ = t.sendAuthentication(quicConn)\n\t}()\n\n\tif t.udp && t.UdpRelayMode == types.QUIC {\n\t\tgo func() {\n\t\t\t_ = t.handleUniStream(quicConn)\n\t\t}()\n\t}\n\tgo func() {\n\t\t_ = t.handleMessage(quicConn) // always handleMessage because tuicV5 using datagram to send the Heartbeat\n\t}()\n\n\tt.quicConn = quicConn\n\tt.openStreams.Store(0)\n\treturn quicConn, nil\n}\n\nfunc (t *clientImpl) sendAuthentication(quicConn *quic.Conn) (err error) {\n\tdefer func() {\n\t\tt.deferQuicConn(quicConn, err)\n\t}()\n\tstream, err := quicConn.OpenUniStream()\n\tif err != nil {\n\t\treturn err\n\t}\n\tbuf := pool.GetBuffer()\n\tdefer pool.PutBuffer(buf)\n\ttoken, err := GenToken(quicConn.ConnectionState(), t.Uuid, t.Password)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = NewAuthenticate(t.Uuid, token).WriteTo(buf)\n\tif err != nil {\n\t\treturn err\n\t}\n\t_, err = buf.WriteTo(stream)\n\tif err != nil {\n\t\treturn err\n\t}\n\terr = stream.Close()\n\tif err != nil {\n\t\treturn\n\t}\n\treturn nil\n}\n\nfunc (t *clientImpl) handleUniStream(quicConn *quic.Conn) (err error) {\n\tdefer func() {\n\t\tt.deferQuicConn(quicConn, err)\n\t}()\n\tfor {\n\t\tvar stream *quic.ReceiveStream\n\t\tstream, err = quicConn.AcceptUniStream(context.Background())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tgo func() (err error) {\n\t\t\tvar assocId uint16\n\t\t\tdefer func() {\n\t\t\t\tt.deferQuicConn(quicConn, err)\n\t\t\t\tif err != nil && assocId != 0 {\n\t\t\t\t\tif val, ok := t.udpInputMap.LoadAndDelete(assocId); ok {\n\t\t\t\t\t\tif conn, ok := val.(net.Conn); ok {\n\t\t\t\t\t\t\t_ = conn.Close()\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tstream.CancelRead(0)\n\t\t\t}()\n\t\t\treader := bufio.NewReader(stream)\n\t\t\tcommandHead, err := ReadCommandHead(reader)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tswitch commandHead.TYPE {\n\t\t\tcase PacketType:\n\t\t\t\tvar packet Packet\n\t\t\t\tpacket, err = ReadPacketWithHead(commandHead, reader)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif t.udp && t.UdpRelayMode == types.QUIC {\n\t\t\t\t\tassocId = packet.ASSOC_ID\n\t\t\t\t\tif val, ok := t.udpInputMap.Load(assocId); ok {\n\t\t\t\t\t\tif conn, ok := val.(net.Conn); ok {\n\t\t\t\t\t\t\twriter := bufio.NewWriterSize(conn, packet.BytesLen())\n\t\t\t\t\t\t\t_ = packet.WriteTo(writer)\n\t\t\t\t\t\t\t_ = writer.Flush()\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn\n\t\t}()\n\t}\n}\n\nfunc (t *clientImpl) handleMessage(quicConn *quic.Conn) (err error) {\n\tdefer func() {\n\t\tt.deferQuicConn(quicConn, err)\n\t}()\n\tfor {\n\t\tvar message []byte\n\t\tmessage, err = quicConn.ReceiveDatagram(context.Background())\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tgo func() (err error) {\n\t\t\tvar assocId uint16\n\t\t\tdefer func() {\n\t\t\t\tt.deferQuicConn(quicConn, err)\n\t\t\t\tif err != nil && assocId != 0 {\n\t\t\t\t\tif val, ok := t.udpInputMap.LoadAndDelete(assocId); ok {\n\t\t\t\t\t\tif conn, ok := val.(net.Conn); ok {\n\t\t\t\t\t\t\t_ = conn.Close()\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}()\n\t\t\treader := bytes.NewBuffer(message)\n\t\t\tcommandHead, err := ReadCommandHead(reader)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tswitch commandHead.TYPE {\n\t\t\tcase PacketType:\n\t\t\t\tvar packet Packet\n\t\t\t\tpacket, err = ReadPacketWithHead(commandHead, reader)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif t.udp && t.UdpRelayMode == types.NATIVE {\n\t\t\t\t\tassocId = packet.ASSOC_ID\n\t\t\t\t\tif val, ok := t.udpInputMap.Load(assocId); ok {\n\t\t\t\t\t\tif conn, ok := val.(net.Conn); ok {\n\t\t\t\t\t\t\t_, _ = conn.Write(message)\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\tcase HeartbeatType:\n\t\t\t\tvar heartbeat Heartbeat\n\t\t\t\theartbeat, err = ReadHeartbeatWithHead(commandHead, reader)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\theartbeat.BytesLen()\n\t\t\t}\n\t\t\treturn\n\t\t}()\n\t}\n}\n\nfunc (t *clientImpl) deferQuicConn(quicConn *quic.Conn, err error) {\n\tvar netError net.Error\n\tif err != nil && errors.As(err, &netError) {\n\t\tt.forceClose(quicConn, err)\n\t}\n}\n\nfunc (t *clientImpl) forceClose(quicConn *quic.Conn, err error) {\n\tt.connMutex.Lock()\n\tdefer t.connMutex.Unlock()\n\tif quicConn == nil {\n\t\tquicConn = t.quicConn\n\t}\n\tif quicConn != nil {\n\t\tif quicConn == t.quicConn {\n\t\t\tt.quicConn = nil\n\t\t}\n\t}\n\terrStr := \"\"\n\tif err != nil {\n\t\terrStr = err.Error()\n\t}\n\tif quicConn != nil {\n\t\t_ = quicConn.CloseWithError(ProtocolError, errStr)\n\t}\n\tudpInputMap := &t.udpInputMap\n\tudpInputMap.Range(func(key uint16, value net.Conn) bool {\n\t\tconn := value\n\t\t_ = conn.Close()\n\t\tudpInputMap.Delete(key)\n\t\treturn true\n\t})\n}\n\nfunc (t *clientImpl) Close() {\n\tt.closed.Store(true)\n\tif t.openStreams.Load() == 0 {\n\t\tt.forceClose(nil, types.ClientClosed)\n\t}\n}\n\nfunc (t *clientImpl) DialContext(ctx context.Context, metadata *C.Metadata) (net.Conn, error) {\n\tquicConn, err := t.getQuicConn(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\topenStreams := t.openStreams.Add(1)\n\tif openStreams >= t.MaxOpenStreams {\n\t\tt.openStreams.Add(-1)\n\t\treturn nil, types.TooManyOpenStreams\n\t}\n\tstream, err := func() (stream net.Conn, err error) {\n\t\tdefer func() {\n\t\t\tt.deferQuicConn(quicConn, err)\n\t\t}()\n\t\tbuf := pool.GetBuffer()\n\t\tdefer pool.PutBuffer(buf)\n\t\terr = NewConnect(NewAddress(metadata)).WriteTo(buf)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tquicStream, err := quicConn.OpenStream()\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tstream = types.NewQuicStreamConn(\n\t\t\tquicStream,\n\t\t\tquicConn.LocalAddr(),\n\t\t\tquicConn.RemoteAddr(),\n\t\t\tfunc() {\n\t\t\t\ttime.AfterFunc(C.DefaultTCPTimeout, func() {\n\t\t\t\t\topenStreams := t.openStreams.Add(-1)\n\t\t\t\t\tif openStreams == 0 && t.closed.Load() {\n\t\t\t\t\t\tt.forceClose(quicConn, types.ClientClosed)\n\t\t\t\t\t}\n\t\t\t\t})\n\t\t\t},\n\t\t)\n\t\t_, err = buf.WriteTo(stream)\n\t\tif err != nil {\n\t\t\t_ = stream.Close()\n\t\t\treturn nil, err\n\t\t}\n\t\treturn stream, err\n\t}()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn stream, nil\n}\n\nfunc (t *clientImpl) ListenPacket(ctx context.Context, metadata *C.Metadata) (net.PacketConn, error) {\n\tquicConn, err := t.getQuicConn(ctx)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\topenStreams := t.openStreams.Add(1)\n\tif openStreams >= t.MaxOpenStreams {\n\t\tt.openStreams.Add(-1)\n\t\treturn nil, types.TooManyOpenStreams\n\t}\n\n\tpipe1, pipe2 := N.Pipe()\n\tvar connId uint16\n\tfor {\n\t\tconnId = uint16(randv2.IntN(0xFFFF))\n\t\t_, loaded := t.udpInputMap.LoadOrStore(connId, pipe1)\n\t\tif !loaded {\n\t\t\tbreak\n\t\t}\n\t}\n\tpc := &quicStreamPacketConn{\n\t\tconnId:                connId,\n\t\tquicConn:              quicConn,\n\t\tinputConn:             N.NewBufferedConn(pipe2),\n\t\tudpRelayMode:          t.UdpRelayMode,\n\t\tmaxUdpRelayPacketSize: t.MaxUdpRelayPacketSize,\n\t\tdeferQuicConnFn:       t.deferQuicConn,\n\t\tcloseDeferFn: func() {\n\t\t\tt.udpInputMap.Delete(connId)\n\t\t\ttime.AfterFunc(C.DefaultUDPTimeout, func() {\n\t\t\t\topenStreams := t.openStreams.Add(-1)\n\t\t\t\tif openStreams == 0 && t.closed.Load() {\n\t\t\t\t\tt.forceClose(quicConn, types.ClientClosed)\n\t\t\t\t}\n\t\t\t})\n\t\t},\n\t}\n\treturn pc, nil\n}\n\ntype Client struct {\n\t*clientImpl // use an independent pointer to let Finalizer can work no matter somewhere handle an influence in clientImpl inner\n}\n\nfunc (t *Client) DialContext(ctx context.Context, metadata *C.Metadata) (net.Conn, error) {\n\tconn, err := t.clientImpl.DialContext(ctx, metadata)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn N.NewRefConn(conn, t), err\n}\n\nfunc (t *Client) ListenPacket(ctx context.Context, metadata *C.Metadata) (net.PacketConn, error) {\n\tpc, err := t.clientImpl.ListenPacket(ctx, metadata)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn N.NewRefPacketConn(pc, t), nil\n}\n\nfunc (t *Client) forceClose() {\n\tt.clientImpl.forceClose(nil, types.ClientClosed)\n}\n\nfunc NewClient(clientOption *ClientOption, udp bool, dialFn types.DialFunc) *Client {\n\tci := &clientImpl{\n\t\tClientOption: clientOption,\n\t\tdialFn:       dialFn,\n\t\tudp:          udp,\n\t}\n\tc := &Client{ci}\n\truntime.SetFinalizer(c, closeClient)\n\tlog.Debugln(\"New TuicV5 Client at %p\", c)\n\treturn c\n}\n\nfunc closeClient(client *Client) {\n\tlog.Debugln(\"Close TuicV5 Client at %p\", client)\n\tclient.forceClose()\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/v5/frag.go",
    "content": "package v5\n\nimport (\n\t\"bytes\"\n\t\"sync\"\n\n\t\"github.com/metacubex/mihomo/common/lru\"\n\n\t\"github.com/metacubex/quic-go\"\n)\n\n// MaxFragSize is a safe udp relay packet size\n// because tuicv5 support udp fragment so we unneeded to do a magic modify for quic-go to increase MaxDatagramFrameSize\n// it may not work fine in some platform\n// \"1200\" from quic-go's MaxDatagramSize\n// \"-3\" from quic-go's DatagramFrame.MaxDataLen\nvar MaxFragSize = 1200 - PacketOverHead - 3\n\nfunc fragWriteNative(quicConn *quic.Conn, packet Packet, buf *bytes.Buffer, fragSize int) (err error) {\n\tfullPayload := packet.DATA\n\toff := 0\n\tfragID := uint8(0)\n\tfragCount := uint8((len(fullPayload) + fragSize - 1) / fragSize) // round up\n\tpacket.FRAG_TOTAL = fragCount\n\tfor off < len(fullPayload) {\n\t\tpayloadSize := len(fullPayload) - off\n\t\tif payloadSize > fragSize {\n\t\t\tpayloadSize = fragSize\n\t\t}\n\t\tfrag := packet\n\t\tfrag.FRAG_ID = fragID\n\t\tfrag.SIZE = uint16(payloadSize)\n\t\tfrag.DATA = fullPayload[off : off+payloadSize]\n\t\toff += payloadSize\n\t\tfragID++\n\t\tbuf.Reset()\n\t\terr = frag.WriteTo(buf)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tdata := buf.Bytes()\n\t\terr = quicConn.SendDatagram(data)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tpacket.ADDR.TYPE = AtypNone // avoid \"fragment 2/2: address in non-first fragment\"\n\t}\n\treturn\n}\n\ntype deFragger struct {\n\tlru  *lru.LruCache[uint16, *packetBag]\n\tonce sync.Once\n}\n\ntype packetBag struct {\n\tfrags []*Packet\n\tcount uint8\n\tmutex sync.Mutex\n}\n\nfunc newPacketBag() *packetBag {\n\treturn new(packetBag)\n}\n\nfunc (d *deFragger) init() {\n\tif d.lru == nil {\n\t\td.lru = lru.New(\n\t\t\tlru.WithAge[uint16, *packetBag](10),\n\t\t\tlru.WithUpdateAgeOnGet[uint16, *packetBag](),\n\t\t)\n\t}\n}\n\nfunc (d *deFragger) Feed(m *Packet) *Packet {\n\tif m.FRAG_TOTAL <= 1 {\n\t\treturn m\n\t}\n\tif m.FRAG_ID >= m.FRAG_TOTAL {\n\t\t// wtf is this?\n\t\treturn nil\n\t}\n\td.once.Do(d.init) // lazy init\n\tbag, _ := d.lru.GetOrStore(m.PKT_ID, newPacketBag)\n\tbag.mutex.Lock()\n\tdefer bag.mutex.Unlock()\n\tif int(m.FRAG_TOTAL) != len(bag.frags) {\n\t\t// new message, clear previous state\n\t\tbag.frags = make([]*Packet, m.FRAG_TOTAL)\n\t\tbag.count = 1\n\t\tbag.frags[m.FRAG_ID] = m\n\t\treturn nil\n\t}\n\tif bag.frags[m.FRAG_ID] != nil {\n\t\treturn nil\n\t}\n\tbag.frags[m.FRAG_ID] = m\n\tbag.count++\n\tif int(bag.count) != len(bag.frags) {\n\t\treturn nil\n\t}\n\n\t// all fragments received, assemble\n\tvar data []byte\n\tfor _, frag := range bag.frags {\n\t\tdata = append(data, frag.DATA...)\n\t}\n\tp := *bag.frags[0] // recover from first fragment\n\tp.SIZE = uint16(len(data))\n\tp.DATA = data\n\tp.FRAG_ID = 0\n\tp.FRAG_TOTAL = 1\n\tbag.frags = nil\n\td.lru.Delete(m.PKT_ID)\n\treturn &p\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/v5/packet.go",
    "content": "package v5\n\nimport (\n\t\"errors\"\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/atomic\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/pool\"\n\t\"github.com/metacubex/mihomo/transport/tuic/types\"\n\n\t\"github.com/metacubex/quic-go\"\n\t\"github.com/metacubex/randv2\"\n)\n\ntype quicStreamPacketConn struct {\n\tconnId    uint16\n\tquicConn  *quic.Conn\n\tinputConn *N.BufferedConn\n\n\tudpRelayMode          types.UdpRelayMode\n\tmaxUdpRelayPacketSize int\n\n\tdeferQuicConnFn func(quicConn *quic.Conn, err error)\n\tcloseDeferFn    func()\n\twriteClosed     *atomic.Bool\n\n\tcloseOnce sync.Once\n\tcloseErr  error\n\tclosed    bool\n\n\tdeFragger\n}\n\nfunc (q *quicStreamPacketConn) Close() error {\n\tq.closeOnce.Do(func() {\n\t\tq.closed = true\n\t\tq.closeErr = q.close()\n\t})\n\treturn q.closeErr\n}\n\nfunc (q *quicStreamPacketConn) close() (err error) {\n\tif q.closeDeferFn != nil {\n\t\tdefer q.closeDeferFn()\n\t}\n\tif q.deferQuicConnFn != nil {\n\t\tdefer func() {\n\t\t\tq.deferQuicConnFn(q.quicConn, err)\n\t\t}()\n\t}\n\tif q.inputConn != nil {\n\t\t_ = q.inputConn.Close()\n\t\tq.inputConn = nil\n\n\t\tbuf := pool.GetBuffer()\n\t\tdefer pool.PutBuffer(buf)\n\t\terr = NewDissociate(q.connId).WriteTo(buf)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tvar stream *quic.SendStream\n\t\tstream, err = q.quicConn.OpenUniStream()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\t_, err = buf.WriteTo(stream)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\terr = stream.Close()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\treturn\n}\n\nfunc (q *quicStreamPacketConn) SetDeadline(t time.Time) error {\n\t//TODO implement me\n\treturn nil\n}\n\nfunc (q *quicStreamPacketConn) SetReadDeadline(t time.Time) error {\n\tif q.inputConn != nil {\n\t\treturn q.inputConn.SetReadDeadline(t)\n\t}\n\treturn nil\n}\n\nfunc (q *quicStreamPacketConn) SetWriteDeadline(t time.Time) error {\n\t//TODO implement me\n\treturn nil\n}\n\nfunc (q *quicStreamPacketConn) ReadFrom(p []byte) (n int, addr net.Addr, err error) {\n\tif inputConn := q.inputConn; inputConn != nil { // copy inputConn avoid be nil in for loop\n\t\tfor {\n\t\t\tvar packet Packet\n\t\t\tpacket, err = ReadPacket(inputConn)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif packetPtr := q.deFragger.Feed(&packet); packetPtr != nil {\n\t\t\t\tn = copy(p, packet.DATA)\n\t\t\t\taddr = packetPtr.ADDR.UDPAddr()\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t} else {\n\t\terr = net.ErrClosed\n\t}\n\treturn\n}\n\nfunc (q *quicStreamPacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {\n\tif inputConn := q.inputConn; inputConn != nil { // copy inputConn avoid be nil in for loop\n\t\tfor {\n\t\t\tvar packet Packet\n\t\t\tpacket, err = ReadPacket(inputConn)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif packetPtr := q.deFragger.Feed(&packet); packetPtr != nil {\n\t\t\t\tdata = packetPtr.DATA\n\t\t\t\taddr = packetPtr.ADDR.UDPAddr()\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t} else {\n\t\terr = net.ErrClosed\n\t}\n\treturn\n}\n\nfunc (q *quicStreamPacketConn) WriteTo(p []byte, addr net.Addr) (n int, err error) {\n\tif len(p) > 0xffff { // uint16 max\n\t\treturn 0, &quic.DatagramTooLargeError{MaxDatagramPayloadSize: 0xffff}\n\t}\n\tif q.closed {\n\t\treturn 0, net.ErrClosed\n\t}\n\tif q.writeClosed != nil && q.writeClosed.Load() {\n\t\t_ = q.Close()\n\t\treturn 0, net.ErrClosed\n\t}\n\tif q.deferQuicConnFn != nil {\n\t\tdefer func() {\n\t\t\tq.deferQuicConnFn(q.quicConn, err)\n\t\t}()\n\t}\n\tbuf := pool.GetBuffer()\n\tdefer pool.PutBuffer(buf)\n\taddress, err := NewAddressNetAddr(addr)\n\tif err != nil {\n\t\treturn\n\t}\n\tpktId := uint16(randv2.Uint32())\n\tpacket := NewPacket(q.connId, pktId, 1, 0, uint16(len(p)), address, p)\n\tswitch q.udpRelayMode {\n\tcase types.QUIC:\n\t\terr = packet.WriteTo(buf)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tvar stream *quic.SendStream\n\t\tstream, err = q.quicConn.OpenUniStream()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tdefer stream.Close()\n\t\t_, err = buf.WriteTo(stream)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\tdefault: // native\n\t\tif len(p) > q.maxUdpRelayPacketSize {\n\t\t\terr = fragWriteNative(q.quicConn, packet, buf, q.maxUdpRelayPacketSize)\n\t\t} else {\n\t\t\terr = packet.WriteTo(buf)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tdata := buf.Bytes()\n\t\t\terr = q.quicConn.SendDatagram(data)\n\t\t}\n\n\t\tvar tooLarge *quic.DatagramTooLargeError\n\t\tif errors.As(err, &tooLarge) {\n\t\t\terr = fragWriteNative(q.quicConn, packet, buf, int(tooLarge.MaxDatagramPayloadSize)-PacketOverHead)\n\t\t}\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\tn = len(p)\n\n\treturn\n}\n\nfunc (q *quicStreamPacketConn) LocalAddr() net.Addr {\n\treturn q.quicConn.LocalAddr()\n}\n\nvar _ net.PacketConn = (*quicStreamPacketConn)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/v5/protocol.go",
    "content": "package v5\n\nimport (\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/netip\"\n\t\"strconv\"\n\n\t\"github.com/metacubex/mihomo/common/utils\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n\n\t\"github.com/metacubex/quic-go\"\n)\n\ntype BufferedReader interface {\n\tio.Reader\n\tio.ByteReader\n}\n\ntype BufferedWriter interface {\n\tio.Writer\n\tio.ByteWriter\n}\n\ntype CommandType byte\n\nconst (\n\tAuthenticateType = CommandType(0x00)\n\tConnectType      = CommandType(0x01)\n\tPacketType       = CommandType(0x02)\n\tDissociateType   = CommandType(0x03)\n\tHeartbeatType    = CommandType(0x04)\n)\n\nconst VER byte = 0x05\n\nfunc (c CommandType) String() string {\n\tswitch c {\n\tcase AuthenticateType:\n\t\treturn \"Authenticate\"\n\tcase ConnectType:\n\t\treturn \"Connect\"\n\tcase PacketType:\n\t\treturn \"Packet\"\n\tcase DissociateType:\n\t\treturn \"Dissociate\"\n\tcase HeartbeatType:\n\t\treturn \"Heartbeat\"\n\tdefault:\n\t\treturn fmt.Sprintf(\"UnknowCommand: %#x\", byte(c))\n\t}\n}\n\nfunc (c CommandType) BytesLen() int {\n\treturn 1\n}\n\ntype CommandHead struct {\n\tVER  byte\n\tTYPE CommandType\n}\n\nfunc NewCommandHead(TYPE CommandType) CommandHead {\n\treturn CommandHead{\n\t\tVER:  VER,\n\t\tTYPE: TYPE,\n\t}\n}\n\nfunc ReadCommandHead(reader BufferedReader) (c CommandHead, err error) {\n\tc.VER, err = reader.ReadByte()\n\tif err != nil {\n\t\treturn\n\t}\n\tTYPE, err := reader.ReadByte()\n\tif err != nil {\n\t\treturn\n\t}\n\tc.TYPE = CommandType(TYPE)\n\treturn\n}\n\nfunc (c CommandHead) WriteTo(writer BufferedWriter) (err error) {\n\terr = writer.WriteByte(c.VER)\n\tif err != nil {\n\t\treturn\n\t}\n\terr = writer.WriteByte(byte(c.TYPE))\n\tif err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (c CommandHead) BytesLen() int {\n\treturn 1 + c.TYPE.BytesLen()\n}\n\ntype Authenticate struct {\n\tCommandHead\n\tUUID  [16]byte\n\tTOKEN [32]byte\n}\n\nfunc NewAuthenticate(UUID [16]byte, TOKEN [32]byte) Authenticate {\n\treturn Authenticate{\n\t\tCommandHead: NewCommandHead(AuthenticateType),\n\t\tUUID:        UUID,\n\t\tTOKEN:       TOKEN,\n\t}\n}\n\nfunc ReadAuthenticateWithHead(head CommandHead, reader BufferedReader) (c Authenticate, err error) {\n\tc.CommandHead = head\n\tif c.CommandHead.TYPE != AuthenticateType {\n\t\terr = fmt.Errorf(\"error command type: %s\", c.CommandHead.TYPE)\n\t\treturn\n\t}\n\t_, err = io.ReadFull(reader, c.UUID[:])\n\tif err != nil {\n\t\treturn\n\t}\n\t_, err = io.ReadFull(reader, c.TOKEN[:])\n\tif err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc ReadAuthenticate(reader BufferedReader) (c Authenticate, err error) {\n\thead, err := ReadCommandHead(reader)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn ReadAuthenticateWithHead(head, reader)\n}\n\nfunc GenToken(state quic.ConnectionState, uuid [16]byte, password string) (token [32]byte, err error) {\n\tvar tokenBytes []byte\n\ttokenBytes, err = state.TLS.ExportKeyingMaterial(utils.StringFromImmutableBytes(uuid[:]), utils.ImmutableBytesFromString(password), 32)\n\tif err != nil {\n\t\treturn\n\t}\n\tcopy(token[:], tokenBytes)\n\treturn\n}\n\nfunc (c Authenticate) WriteTo(writer BufferedWriter) (err error) {\n\terr = c.CommandHead.WriteTo(writer)\n\tif err != nil {\n\t\treturn\n\t}\n\t_, err = writer.Write(c.UUID[:])\n\tif err != nil {\n\t\treturn\n\t}\n\t_, err = writer.Write(c.TOKEN[:])\n\tif err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (c Authenticate) BytesLen() int {\n\treturn c.CommandHead.BytesLen() + 16 + 32\n}\n\ntype Connect struct {\n\tCommandHead\n\tADDR Address\n}\n\nfunc NewConnect(ADDR Address) Connect {\n\treturn Connect{\n\t\tCommandHead: NewCommandHead(ConnectType),\n\t\tADDR:        ADDR,\n\t}\n}\n\nfunc ReadConnectWithHead(head CommandHead, reader BufferedReader) (c Connect, err error) {\n\tc.CommandHead = head\n\tif c.CommandHead.TYPE != ConnectType {\n\t\terr = fmt.Errorf(\"error command type: %s\", c.CommandHead.TYPE)\n\t\treturn\n\t}\n\tc.ADDR, err = ReadAddress(reader)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc ReadConnect(reader BufferedReader) (c Connect, err error) {\n\thead, err := ReadCommandHead(reader)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn ReadConnectWithHead(head, reader)\n}\n\nfunc (c Connect) WriteTo(writer BufferedWriter) (err error) {\n\terr = c.CommandHead.WriteTo(writer)\n\tif err != nil {\n\t\treturn\n\t}\n\terr = c.ADDR.WriteTo(writer)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (c Connect) BytesLen() int {\n\treturn c.CommandHead.BytesLen() + c.ADDR.BytesLen()\n}\n\ntype Packet struct {\n\tCommandHead\n\tASSOC_ID   uint16\n\tPKT_ID     uint16\n\tFRAG_TOTAL uint8\n\tFRAG_ID    uint8\n\tSIZE       uint16\n\tADDR       Address\n\tDATA       []byte\n}\n\nfunc NewPacket(ASSOC_ID uint16, PKT_ID uint16, FRGA_TOTAL uint8, FRAG_ID uint8, SIZE uint16, ADDR Address, DATA []byte) Packet {\n\treturn Packet{\n\t\tCommandHead: NewCommandHead(PacketType),\n\t\tASSOC_ID:    ASSOC_ID,\n\t\tPKT_ID:      PKT_ID,\n\t\tFRAG_ID:     FRAG_ID,\n\t\tFRAG_TOTAL:  FRGA_TOTAL,\n\t\tSIZE:        SIZE,\n\t\tADDR:        ADDR,\n\t\tDATA:        DATA,\n\t}\n}\n\nfunc ReadPacketWithHead(head CommandHead, reader BufferedReader) (c Packet, err error) {\n\tc.CommandHead = head\n\tif c.CommandHead.TYPE != PacketType {\n\t\terr = fmt.Errorf(\"error command type: %s\", c.CommandHead.TYPE)\n\t\treturn\n\t}\n\terr = binary.Read(reader, binary.BigEndian, &c.ASSOC_ID)\n\tif err != nil {\n\t\treturn\n\t}\n\terr = binary.Read(reader, binary.BigEndian, &c.PKT_ID)\n\tif err != nil {\n\t\treturn\n\t}\n\terr = binary.Read(reader, binary.BigEndian, &c.FRAG_TOTAL)\n\tif err != nil {\n\t\treturn\n\t}\n\terr = binary.Read(reader, binary.BigEndian, &c.FRAG_ID)\n\tif err != nil {\n\t\treturn\n\t}\n\terr = binary.Read(reader, binary.BigEndian, &c.SIZE)\n\tif err != nil {\n\t\treturn\n\t}\n\tc.ADDR, err = ReadAddress(reader)\n\tif err != nil {\n\t\treturn\n\t}\n\tc.DATA = make([]byte, c.SIZE)\n\t_, err = io.ReadFull(reader, c.DATA)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc ReadPacket(reader BufferedReader) (c Packet, err error) {\n\thead, err := ReadCommandHead(reader)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn ReadPacketWithHead(head, reader)\n}\n\nfunc (c Packet) WriteTo(writer BufferedWriter) (err error) {\n\terr = c.CommandHead.WriteTo(writer)\n\tif err != nil {\n\t\treturn\n\t}\n\terr = binary.Write(writer, binary.BigEndian, c.ASSOC_ID)\n\tif err != nil {\n\t\treturn\n\t}\n\terr = binary.Write(writer, binary.BigEndian, c.PKT_ID)\n\tif err != nil {\n\t\treturn\n\t}\n\terr = binary.Write(writer, binary.BigEndian, c.FRAG_TOTAL)\n\tif err != nil {\n\t\treturn\n\t}\n\terr = binary.Write(writer, binary.BigEndian, c.FRAG_ID)\n\tif err != nil {\n\t\treturn\n\t}\n\terr = binary.Write(writer, binary.BigEndian, c.SIZE)\n\tif err != nil {\n\t\treturn\n\t}\n\terr = c.ADDR.WriteTo(writer)\n\tif err != nil {\n\t\treturn\n\t}\n\t_, err = writer.Write(c.DATA)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (c Packet) BytesLen() int {\n\treturn c.CommandHead.BytesLen() + 4 + 2 + c.ADDR.BytesLen() + len(c.DATA)\n}\n\nvar PacketOverHead = NewPacket(0, 0, 0, 0, 0, NewAddressAddrPort(netip.AddrPortFrom(netip.IPv6Unspecified(), 0)), nil).BytesLen()\n\ntype Dissociate struct {\n\tCommandHead\n\tASSOC_ID uint16\n}\n\nfunc NewDissociate(ASSOC_ID uint16) Dissociate {\n\treturn Dissociate{\n\t\tCommandHead: NewCommandHead(DissociateType),\n\t\tASSOC_ID:    ASSOC_ID,\n\t}\n}\n\nfunc ReadDissociateWithHead(head CommandHead, reader BufferedReader) (c Dissociate, err error) {\n\tc.CommandHead = head\n\tif c.CommandHead.TYPE != DissociateType {\n\t\terr = fmt.Errorf(\"error command type: %s\", c.CommandHead.TYPE)\n\t\treturn\n\t}\n\terr = binary.Read(reader, binary.BigEndian, &c.ASSOC_ID)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc ReadDissociate(reader BufferedReader) (c Dissociate, err error) {\n\thead, err := ReadCommandHead(reader)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn ReadDissociateWithHead(head, reader)\n}\n\nfunc (c Dissociate) WriteTo(writer BufferedWriter) (err error) {\n\terr = c.CommandHead.WriteTo(writer)\n\tif err != nil {\n\t\treturn\n\t}\n\terr = binary.Write(writer, binary.BigEndian, c.ASSOC_ID)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (c Dissociate) BytesLen() int {\n\treturn c.CommandHead.BytesLen() + 4\n}\n\ntype Heartbeat struct {\n\tCommandHead\n}\n\nfunc NewHeartbeat() Heartbeat {\n\treturn Heartbeat{\n\t\tCommandHead: NewCommandHead(HeartbeatType),\n\t}\n}\n\nfunc ReadHeartbeatWithHead(head CommandHead, reader BufferedReader) (c Heartbeat, err error) {\n\tc.CommandHead = head\n\tif c.CommandHead.TYPE != HeartbeatType {\n\t\terr = fmt.Errorf(\"error command type: %s\", c.CommandHead.TYPE)\n\t\treturn\n\t}\n\treturn\n}\n\nfunc ReadHeartbeat(reader BufferedReader) (c Heartbeat, err error) {\n\thead, err := ReadCommandHead(reader)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn ReadHeartbeatWithHead(head, reader)\n}\n\n// Addr types\nconst (\n\tAtypDomainName byte = 0\n\tAtypIPv4       byte = 1\n\tAtypIPv6       byte = 2\n\tAtypNone       byte = 255 // Address type None is used in Packet commands that is not the first fragment of a UDP packet.\n)\n\ntype Address struct {\n\tTYPE byte\n\tADDR []byte\n\tPORT uint16\n}\n\nfunc NewAddress(metadata *C.Metadata) Address {\n\tvar addrType byte\n\tvar addr []byte\n\tswitch metadata.AddrType() {\n\tcase C.AtypIPv4:\n\t\taddrType = AtypIPv4\n\t\taddr = metadata.DstIP.AsSlice()\n\tcase C.AtypIPv6:\n\t\taddrType = AtypIPv6\n\t\taddr = metadata.DstIP.AsSlice()\n\tcase C.AtypDomainName:\n\t\taddrType = AtypDomainName\n\t\taddr = make([]byte, len(metadata.Host)+1)\n\t\taddr[0] = byte(len(metadata.Host))\n\t\tcopy(addr[1:], metadata.Host)\n\t}\n\n\treturn Address{\n\t\tTYPE: addrType,\n\t\tADDR: addr,\n\t\tPORT: metadata.DstPort,\n\t}\n}\n\nfunc NewAddressNetAddr(addr net.Addr) (Address, error) {\n\tif addr, ok := addr.(interface{ AddrPort() netip.AddrPort }); ok {\n\t\tif addrPort := addr.AddrPort(); addrPort.IsValid() { // sing's M.Socksaddr maybe return an invalid AddrPort if it's a DomainName\n\t\t\treturn NewAddressAddrPort(addrPort), nil\n\t\t}\n\t}\n\taddrStr := addr.String()\n\tif addrPort, err := netip.ParseAddrPort(addrStr); err == nil {\n\t\treturn NewAddressAddrPort(addrPort), nil\n\t}\n\tmetadata := &C.Metadata{}\n\tif err := metadata.SetRemoteAddress(addrStr); err != nil {\n\t\treturn Address{}, err\n\t}\n\treturn NewAddress(metadata), nil\n}\n\nfunc NewAddressAddrPort(addrPort netip.AddrPort) Address {\n\tvar addrType byte\n\tport := addrPort.Port()\n\taddr := addrPort.Addr().Unmap()\n\tif addr.Is4() {\n\t\taddrType = AtypIPv4\n\t} else {\n\t\taddrType = AtypIPv6\n\t}\n\treturn Address{\n\t\tTYPE: addrType,\n\t\tADDR: addr.AsSlice(),\n\t\tPORT: port,\n\t}\n}\n\nfunc ReadAddress(reader BufferedReader) (c Address, err error) {\n\tc.TYPE, err = reader.ReadByte()\n\tif err != nil {\n\t\treturn\n\t}\n\tswitch c.TYPE {\n\tcase AtypIPv4:\n\t\tc.ADDR = make([]byte, net.IPv4len)\n\t\t_, err = io.ReadFull(reader, c.ADDR)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\tcase AtypIPv6:\n\t\tc.ADDR = make([]byte, net.IPv6len)\n\t\t_, err = io.ReadFull(reader, c.ADDR)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\tcase AtypDomainName:\n\t\tvar addrLen byte\n\t\taddrLen, err = reader.ReadByte()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tc.ADDR = make([]byte, addrLen+1)\n\t\tc.ADDR[0] = addrLen\n\t\t_, err = io.ReadFull(reader, c.ADDR[1:])\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tif c.TYPE == AtypNone {\n\t\treturn\n\t}\n\terr = binary.Read(reader, binary.BigEndian, &c.PORT)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (c Address) WriteTo(writer BufferedWriter) (err error) {\n\terr = writer.WriteByte(c.TYPE)\n\tif err != nil {\n\t\treturn\n\t}\n\tif c.TYPE == AtypNone {\n\t\treturn\n\t}\n\t_, err = writer.Write(c.ADDR[:])\n\tif err != nil {\n\t\treturn\n\t}\n\terr = binary.Write(writer, binary.BigEndian, c.PORT)\n\tif err != nil {\n\t\treturn\n\t}\n\treturn\n}\n\nfunc (c Address) String() string {\n\tswitch c.TYPE {\n\tcase AtypDomainName:\n\t\treturn net.JoinHostPort(string(c.ADDR[1:]), strconv.Itoa(int(c.PORT)))\n\tdefault:\n\t\taddr, _ := netip.AddrFromSlice(c.ADDR)\n\t\taddrPort := netip.AddrPortFrom(addr, c.PORT)\n\t\treturn addrPort.String()\n\t}\n}\n\nfunc (c Address) SocksAddr() socks5.Addr {\n\taddr := make([]byte, 1+len(c.ADDR)+2)\n\tswitch c.TYPE {\n\tcase AtypIPv4:\n\t\taddr[0] = socks5.AtypIPv4\n\tcase AtypIPv6:\n\t\taddr[0] = socks5.AtypIPv6\n\tcase AtypDomainName:\n\t\taddr[0] = socks5.AtypDomainName\n\t}\n\tcopy(addr[1:], c.ADDR)\n\tbinary.BigEndian.PutUint16(addr[len(addr)-2:], c.PORT)\n\treturn addr\n}\n\nfunc (c Address) UDPAddr() *net.UDPAddr {\n\treturn &net.UDPAddr{\n\t\tIP:   c.ADDR,\n\t\tPort: int(c.PORT),\n\t\tZone: \"\",\n\t}\n}\n\nfunc (c Address) BytesLen() int {\n\treturn 1 + len(c.ADDR) + 2\n}\n\nconst (\n\tProtocolError         = quic.ApplicationErrorCode(0xfffffff0)\n\tAuthenticationFailed  = quic.ApplicationErrorCode(0xfffffff1)\n\tAuthenticationTimeout = quic.ApplicationErrorCode(0xfffffff2)\n\tBadCommand            = quic.ApplicationErrorCode(0xfffffff3)\n)\n"
  },
  {
    "path": "core/Clash.Meta/transport/tuic/v5/server.go",
    "content": "package v5\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"fmt\"\n\t\"net\"\n\t\"sync\"\n\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\t\"github.com/metacubex/mihomo/common/atomic\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/xsync\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/transport/socks5\"\n\t\"github.com/metacubex/mihomo/transport/tuic/types\"\n\n\t\"github.com/gofrs/uuid/v5\"\n\t\"github.com/metacubex/quic-go\"\n)\n\ntype ServerOption struct {\n\tHandleTcpFn func(conn net.Conn, addr socks5.Addr, additions ...inbound.Addition) error\n\tHandleUdpFn func(addr socks5.Addr, packet C.UDPPacket, additions ...inbound.Addition) error\n\n\tUsers                 map[[16]byte]string\n\tMaxUdpRelayPacketSize int\n}\n\nfunc NewServerHandler(option *ServerOption, quicConn *quic.Conn, uuid uuid.UUID) types.ServerHandler {\n\treturn &serverHandler{\n\t\tServerOption: option,\n\t\tquicConn:     quicConn,\n\t\tuuid:         uuid,\n\t\tauthCh:       make(chan struct{}),\n\t}\n}\n\ntype serverHandler struct {\n\t*ServerOption\n\tquicConn *quic.Conn\n\tuuid     uuid.UUID\n\n\tauthCh   chan struct{}\n\tauthOk   atomic.Bool\n\tauthUUID atomic.TypedValue[string]\n\tauthOnce sync.Once\n\n\tudpInputMap xsync.Map[uint16, *serverUDPInput]\n}\n\nfunc (s *serverHandler) AuthOk() bool {\n\treturn s.authOk.Load()\n}\n\nfunc (s *serverHandler) HandleTimeout() {\n\ts.authOnce.Do(func() {\n\t\t_ = s.quicConn.CloseWithError(AuthenticationTimeout, \"AuthenticationTimeout\")\n\t\ts.authOk.Store(false)\n\t\tclose(s.authCh)\n\t})\n}\n\nfunc (s *serverHandler) HandleMessage(message []byte) (err error) {\n\treader := bytes.NewBuffer(message)\n\tcommandHead, err := ReadCommandHead(reader)\n\tif err != nil {\n\t\treturn\n\t}\n\tswitch commandHead.TYPE {\n\tcase PacketType:\n\t\tvar packet Packet\n\t\tpacket, err = ReadPacketWithHead(commandHead, reader)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\treturn s.parsePacket(&packet, types.NATIVE)\n\tcase HeartbeatType:\n\t\tvar heartbeat Heartbeat\n\t\theartbeat, err = ReadHeartbeatWithHead(commandHead, reader)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\theartbeat.BytesLen()\n\t}\n\treturn\n}\n\nfunc (s *serverHandler) parsePacket(packet *Packet, udpRelayMode types.UdpRelayMode) (err error) {\n\t<-s.authCh\n\tif !s.authOk.Load() {\n\t\treturn\n\t}\n\tvar assocId uint16\n\n\tassocId = packet.ASSOC_ID\n\n\tinput, _ := s.udpInputMap.LoadOrStoreFn(assocId, func() *serverUDPInput { return &serverUDPInput{} })\n\tif input.writeClosed.Load() {\n\t\treturn nil\n\t}\n\tpacketPtr := input.Feed(packet)\n\tif packetPtr == nil {\n\t\treturn\n\t}\n\n\tpc := &quicStreamPacketConn{\n\t\tconnId:                assocId,\n\t\tquicConn:              s.quicConn,\n\t\tinputConn:             nil,\n\t\tudpRelayMode:          udpRelayMode,\n\t\tmaxUdpRelayPacketSize: s.MaxUdpRelayPacketSize,\n\t\tdeferQuicConnFn:       nil,\n\t\tcloseDeferFn:          nil,\n\t\twriteClosed:           &input.writeClosed,\n\t}\n\n\treturn s.HandleUdpFn(packetPtr.ADDR.SocksAddr(), &serverUDPPacket{\n\t\tpc:     pc,\n\t\tpacket: packetPtr,\n\t\trAddr:  N.NewCustomAddr(\"tuic\", fmt.Sprintf(\"tuic-%s-%d\", s.uuid, assocId), s.quicConn.RemoteAddr()), // for tunnel's handleUDPConn\n\t}, inbound.WithInUser(s.authUUID.Load()))\n}\n\nfunc (s *serverHandler) HandleStream(conn *N.BufferedConn) (err error) {\n\tconnect, err := ReadConnect(conn)\n\tif err != nil {\n\t\treturn err\n\t}\n\t<-s.authCh\n\tif !s.authOk.Load() {\n\t\treturn conn.Close()\n\t}\n\n\terr = s.HandleTcpFn(conn, connect.ADDR.SocksAddr(), inbound.WithInUser(s.authUUID.Load()))\n\tif err != nil {\n\t\t_ = conn.Close()\n\t\treturn err\n\t}\n\treturn\n}\n\nfunc (s *serverHandler) HandleUniStream(reader *bufio.Reader) (err error) {\n\tcommandHead, err := ReadCommandHead(reader)\n\tif err != nil {\n\t\treturn\n\t}\n\tswitch commandHead.TYPE {\n\tcase AuthenticateType:\n\t\tvar authenticate Authenticate\n\t\tauthenticate, err = ReadAuthenticateWithHead(commandHead, reader)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tauthOk := false\n\t\tvar authUUID uuid.UUID\n\t\tvar token [32]byte\n\t\tif password, ok := s.Users[authenticate.UUID]; ok {\n\t\t\ttoken, err = GenToken(s.quicConn.ConnectionState(), authenticate.UUID, password)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif token == authenticate.TOKEN {\n\t\t\t\tauthOk = true\n\t\t\t\tauthUUID = authenticate.UUID\n\t\t\t}\n\t\t}\n\t\ts.authOnce.Do(func() {\n\t\t\tif !authOk {\n\t\t\t\t_ = s.quicConn.CloseWithError(AuthenticationFailed, \"AuthenticationFailed\")\n\t\t\t}\n\t\t\ts.authOk.Store(authOk)\n\t\t\ts.authUUID.Store(authUUID.String())\n\t\t\tclose(s.authCh)\n\t\t})\n\tcase PacketType:\n\t\tvar packet Packet\n\t\tpacket, err = ReadPacketWithHead(commandHead, reader)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\treturn s.parsePacket(&packet, types.QUIC)\n\tcase DissociateType:\n\t\tvar disassociate Dissociate\n\t\tdisassociate, err = ReadDissociateWithHead(commandHead, reader)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif input, loaded := s.udpInputMap.LoadAndDelete(disassociate.ASSOC_ID); loaded {\n\t\t\tinput.writeClosed.Store(true)\n\t\t}\n\t}\n\treturn\n}\n\ntype serverUDPInput struct {\n\twriteClosed atomic.Bool\n\tdeFragger\n}\n\ntype serverUDPPacket struct {\n\tpc     *quicStreamPacketConn\n\tpacket *Packet\n\trAddr  net.Addr\n}\n\nfunc (s *serverUDPPacket) InAddr() net.Addr {\n\treturn s.pc.LocalAddr()\n}\n\nfunc (s *serverUDPPacket) LocalAddr() net.Addr {\n\treturn s.rAddr\n}\n\nfunc (s *serverUDPPacket) Data() []byte {\n\treturn s.packet.DATA\n}\n\nfunc (s *serverUDPPacket) WriteBack(b []byte, addr net.Addr) (n int, err error) {\n\treturn s.pc.WriteTo(b, addr)\n}\n\nfunc (s *serverUDPPacket) Drop() {\n\ts.packet.DATA = nil\n}\n\nvar _ C.UDPPacket = (*serverUDPPacket)(nil)\nvar _ C.UDPPacketInAddr = (*serverUDPPacket)(nil)\n"
  },
  {
    "path": "core/Clash.Meta/transport/v2ray-plugin/mux.go",
    "content": "package obfs\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\t\"time\"\n)\n\ntype SessionStatus = byte\n\nconst (\n\tSessionStatusNew       SessionStatus = 0x01\n\tSessionStatusKeep      SessionStatus = 0x02\n\tSessionStatusEnd       SessionStatus = 0x03\n\tSessionStatusKeepAlive SessionStatus = 0x04\n)\n\nconst (\n\tOptionNone  = byte(0x00)\n\tOptionData  = byte(0x01)\n\tOptionError = byte(0x02)\n)\n\ntype MuxOption struct {\n\tID   [2]byte\n\tPort uint16\n\tHost string\n\tType string\n}\n\n// Mux is an mux-compatible client for v2ray-plugin, not a complete implementation\ntype Mux struct {\n\tnet.Conn\n\tbuf    bytes.Buffer\n\tid     [2]byte\n\tlength [2]byte\n\tstatus [2]byte\n\tremain int\n}\n\nfunc (m *Mux) Read(b []byte) (int, error) {\n\tif m.remain != 0 {\n\t\tlength := m.remain\n\t\tif len(b) < m.remain {\n\t\t\tlength = len(b)\n\t\t}\n\n\t\tn, err := m.Conn.Read(b[:length])\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tm.remain -= n\n\t\treturn n, nil\n\t}\n\n\tfor {\n\t\t_, err := io.ReadFull(m.Conn, m.length[:])\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tlength := binary.BigEndian.Uint16(m.length[:])\n\t\tif length > 512 {\n\t\t\treturn 0, errors.New(\"invalid metalen\")\n\t\t}\n\n\t\t_, err = io.ReadFull(m.Conn, m.id[:])\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\n\t\t_, err = m.Conn.Read(m.status[:])\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\n\t\topcode := m.status[0]\n\t\tif opcode == SessionStatusKeepAlive {\n\t\t\tcontinue\n\t\t}\n\n\t\topts := m.status[1]\n\n\t\tif opts != OptionData {\n\t\t\tcontinue\n\t\t}\n\n\t\t_, err = io.ReadFull(m.Conn, m.length[:])\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tdataLen := int(binary.BigEndian.Uint16(m.length[:]))\n\t\tm.remain = dataLen\n\t\tif dataLen > len(b) {\n\t\t\tdataLen = len(b)\n\t\t}\n\n\t\tn, err := m.Conn.Read(b[:dataLen])\n\t\tm.remain -= n\n\t\treturn n, err\n\t}\n}\n\nfunc (m *Mux) Write(b []byte) (int, error) {\n\tdefer m.buf.Reset() // reset must after write (keep the data fill in NewMux can be sent)\n\n\tbinary.Write(&m.buf, binary.BigEndian, uint16(4))\n\tm.buf.Write(m.id[:])\n\tm.buf.WriteByte(SessionStatusKeep)\n\tm.buf.WriteByte(OptionData)\n\tbinary.Write(&m.buf, binary.BigEndian, uint16(len(b)))\n\tm.buf.Write(b)\n\n\treturn m.Conn.Write(m.buf.Bytes())\n}\n\nfunc (m *Mux) Close() error {\n\terrChan := make(chan error, 1)\n\tt := time.AfterFunc(time.Second, func() { // maybe conn write too slowly, force close underlay conn after one second\n\t\terrChan <- m.Conn.Close()\n\t})\n\t_, _ = m.Conn.Write([]byte{0x0, 0x4, m.id[0], m.id[1], SessionStatusEnd, OptionNone}) // ignore session end frame write error\n\tif !t.Stop() {\n\t\t// Stop does not wait for f to complete before returning, so we used a chan to know whether f is completed\n\t\treturn <-errChan\n\t}\n\treturn m.Conn.Close()\n}\n\nfunc NewMux(conn net.Conn, option MuxOption) *Mux {\n\tmux := &Mux{\n\t\tConn: conn,\n\t\tid:   option.ID,\n\t}\n\n\t// create a sub connection (in buf)\n\tbuf := &mux.buf\n\n\t// fill empty length\n\tbuf.Write([]byte{0x0, 0x0})\n\tbuf.Write(option.ID[:])\n\tbuf.WriteByte(SessionStatusNew)\n\tbuf.WriteByte(OptionNone)\n\n\t// tcp\n\tnetType := byte(0x1)\n\tif option.Type == \"udp\" {\n\t\tnetType = byte(0x2)\n\t}\n\tbuf.WriteByte(netType)\n\n\t// port\n\tbinary.Write(buf, binary.BigEndian, option.Port)\n\n\t// address\n\tip := net.ParseIP(option.Host)\n\tif ip == nil {\n\t\tbuf.WriteByte(0x2)\n\t\tbuf.WriteString(option.Host)\n\t} else if ipv4 := ip.To4(); ipv4 != nil {\n\t\tbuf.WriteByte(0x1)\n\t\tbuf.Write(ipv4)\n\t} else {\n\t\tbuf.WriteByte(0x3)\n\t\tbuf.Write(ip.To16())\n\t}\n\n\tmetadata := buf.Bytes()\n\tbinary.BigEndian.PutUint16(metadata[:2], uint16(len(metadata)-2))\n\n\treturn mux\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/v2ray-plugin/websocket.go",
    "content": "package obfs\n\nimport (\n\t\"context\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/component/ca\"\n\t\"github.com/metacubex/mihomo/component/ech\"\n\t\"github.com/metacubex/mihomo/transport/vmess\"\n\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/tls\"\n)\n\n// Option is options of websocket obfs\ntype Option struct {\n\tHost                     string\n\tPort                     string\n\tPath                     string\n\tHeaders                  map[string]string\n\tTLS                      bool\n\tECHConfig                *ech.Config\n\tSkipCertVerify           bool\n\tFingerprint              string\n\tCertificate              string\n\tPrivateKey               string\n\tMux                      bool\n\tV2rayHttpUpgrade         bool\n\tV2rayHttpUpgradeFastOpen bool\n}\n\n// NewV2rayObfs return a HTTPObfs\nfunc NewV2rayObfs(ctx context.Context, conn net.Conn, option *Option) (net.Conn, error) {\n\theader := http.Header{}\n\tfor k, v := range option.Headers {\n\t\theader.Add(k, v)\n\t}\n\n\tconfig := &vmess.WebsocketConfig{\n\t\tHost:                     option.Host,\n\t\tPort:                     option.Port,\n\t\tPath:                     option.Path,\n\t\tV2rayHttpUpgrade:         option.V2rayHttpUpgrade,\n\t\tV2rayHttpUpgradeFastOpen: option.V2rayHttpUpgradeFastOpen,\n\t\tECHConfig:                option.ECHConfig,\n\t\tHeaders:                  header,\n\t}\n\n\tvar err error\n\tif option.TLS {\n\t\tconfig.TLS = true\n\t\tconfig.TLSConfig, err = ca.GetTLSConfig(ca.Option{\n\t\t\tTLSConfig: &tls.Config{\n\t\t\t\tServerName:         option.Host,\n\t\t\t\tInsecureSkipVerify: option.SkipCertVerify,\n\t\t\t\tNextProtos:         []string{\"http/1.1\"},\n\t\t\t},\n\t\t\tFingerprint: option.Fingerprint,\n\t\t\tCertificate: option.Certificate,\n\t\t\tPrivateKey:  option.PrivateKey,\n\t\t})\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\tif host := config.Headers.Get(\"Host\"); host != \"\" {\n\t\t\tconfig.TLSConfig.ServerName = host\n\t\t}\n\t}\n\n\tconn, err = vmess.StreamWebsocketConn(ctx, conn, config)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif option.Mux {\n\t\tconn = NewMux(conn, MuxOption{\n\t\t\tID:   [2]byte{0, 0},\n\t\t\tHost: \"127.0.0.1\",\n\t\t\tPort: 0,\n\t\t})\n\t}\n\treturn conn, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vless/addons.go",
    "content": "package vless\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"fmt\"\n\t\"io\"\n)\n\nfunc ReadAddons(data []byte) (*Addons, error) {\n\treader := bytes.NewReader(data)\n\tvar addons Addons\n\tfor reader.Len() > 0 {\n\t\ttag, err := binary.ReadUvarint(reader)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tnumber, typ := int32(tag>>3), int8(tag&7)\n\t\tswitch typ {\n\t\tcase 0: // VARINT\n\t\t\t_, err = binary.ReadUvarint(reader)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tcase 5: // I32\n\t\t\tvar i32 [4]byte\n\t\t\t_, err = io.ReadFull(reader, i32[:])\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tcase 1: // I64\n\t\t\tvar i64 [8]byte\n\t\t\t_, err = io.ReadFull(reader, i64[:])\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\tcase 2: // LEN\n\t\t\tvar bytesLen uint64\n\t\t\tbytesLen, err = binary.ReadUvarint(reader)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tbytesData := make([]byte, bytesLen)\n\t\t\t_, err = io.ReadFull(reader, bytesData)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tswitch number {\n\t\t\tcase 1:\n\t\t\t\taddons.Flow = string(bytesData)\n\t\t\tcase 2:\n\t\t\t\taddons.Seed = bytesData\n\t\t\t}\n\t\tdefault: // group (3,4) has been deprecated we unneeded support\n\t\t\treturn nil, fmt.Errorf(\"unknown protobuf message tag: %v\", tag)\n\t\t}\n\t}\n\treturn &addons, nil\n}\n\nfunc WriteAddons(addons *Addons) []byte {\n\tvar writer bytes.Buffer\n\tif len(addons.Flow) > 0 {\n\t\tWriteUvarint(&writer, (1<<3)|2) // (field << 3) bit-or wire_type encoded as uint32 varint\n\t\tWriteUvarint(&writer, uint64(len(addons.Flow)))\n\t\twriter.WriteString(addons.Flow)\n\t}\n\tif len(addons.Seed) > 0 {\n\t\tWriteUvarint(&writer, (2<<3)|2) // (field << 3) bit-or wire_type encoded as uint32 varint\n\t\tWriteUvarint(&writer, uint64(len(addons.Seed)))\n\t\twriter.Write(addons.Seed)\n\t}\n\treturn writer.Bytes()\n}\n\nfunc WriteUvarint(writer *bytes.Buffer, x uint64) {\n\tfor x >= 0x80 {\n\t\twriter.WriteByte(byte(x) | 0x80)\n\t\tx >>= 7\n\t}\n\twriter.WriteByte(byte(x))\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vless/addons_test.go",
    "content": "package vless\n\nimport (\n\t\"bytes\"\n\t\"strconv\"\n\t\"testing\"\n\n\t\"google.golang.org/protobuf/proto\"\n)\n\nfunc TestAddons(t *testing.T) {\n\tvar tests = []struct {\n\t\tflow string\n\t\tseed []byte\n\t}{\n\t\t{XRV, nil},\n\t\t{XRS, []byte{1, 2, 3}},\n\t\t{\"\", []byte{1, 2, 3}},\n\t\t{\"\", nil},\n\t}\n\n\tfor i, test := range tests {\n\t\tt.Run(strconv.Itoa(i), func(t *testing.T) {\n\t\t\tt.Run(\"proto->handwritten\", func(t *testing.T) {\n\t\t\t\taddons := new(Addons)\n\t\t\t\taddons.Flow = test.flow\n\t\t\t\taddons.Seed = test.seed\n\n\t\t\t\taddonsBytes, err := proto.Marshal(addons)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Errorf(\"error marshalling addons: %v\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\taddons, err = ReadAddons(addonsBytes)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Errorf(\"error reading addons: %v\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif addons.Flow != test.flow {\n\t\t\t\t\tt.Errorf(\"got %v; want %v\", addons.Flow, test.flow)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif !bytes.Equal(addons.Seed, test.seed) {\n\t\t\t\t\tt.Errorf(\"got %v; want %v\", addons.Seed, test.seed)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"handwritten->proto\", func(t *testing.T) {\n\t\t\t\taddons := new(Addons)\n\t\t\t\taddons.Flow = test.flow\n\t\t\t\taddons.Seed = test.seed\n\n\t\t\t\taddonsBytes := WriteAddons(addons)\n\t\t\t\terr := proto.Unmarshal(addonsBytes, addons)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Errorf(\"error reading addons: %v\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif addons.Flow != test.flow {\n\t\t\t\t\tt.Errorf(\"got %v; want %v\", addons.Flow, test.flow)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif !bytes.Equal(addons.Seed, test.seed) {\n\t\t\t\t\tt.Errorf(\"got %v; want %v\", addons.Seed, test.seed)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t})\n\n\t\t\tt.Run(\"handwritten->handwritten\", func(t *testing.T) {\n\t\t\t\taddons := new(Addons)\n\t\t\t\taddons.Flow = test.flow\n\t\t\t\taddons.Seed = test.seed\n\n\t\t\t\taddonsBytes := WriteAddons(addons)\n\t\t\t\taddons, err := ReadAddons(addonsBytes)\n\t\t\t\tif err != nil {\n\t\t\t\t\tt.Errorf(\"error reading addons: %v\", err)\n\t\t\t\t\treturn\n\t\t\t\t}\n\n\t\t\t\tif addons.Flow != test.flow {\n\t\t\t\t\tt.Errorf(\"got %v; want %v\", addons.Flow, test.flow)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tif !bytes.Equal(addons.Seed, test.seed) {\n\t\t\t\t\tt.Errorf(\"got %v; want %v\", addons.Seed, test.seed)\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vless/config.pb.go",
    "content": "// Code generated by protoc-gen-go. DO NOT EDIT.\n// versions:\n// \tprotoc-gen-go v1.28.0\n// \tprotoc        v3.19.1\n// source: transport/vless/config.proto\n\npackage vless\n\nimport (\n\tprotoreflect \"google.golang.org/protobuf/reflect/protoreflect\"\n\tprotoimpl \"google.golang.org/protobuf/runtime/protoimpl\"\n\treflect \"reflect\"\n\tsync \"sync\"\n)\n\nconst (\n\t// Verify that this generated code is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion)\n\t// Verify that runtime/protoimpl is sufficiently up-to-date.\n\t_ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20)\n)\n\ntype Addons struct {\n\tstate         protoimpl.MessageState\n\tsizeCache     protoimpl.SizeCache\n\tunknownFields protoimpl.UnknownFields\n\n\tFlow string `protobuf:\"bytes,1,opt,name=Flow,proto3\" json:\"Flow,omitempty\"`\n\tSeed []byte `protobuf:\"bytes,2,opt,name=Seed,proto3\" json:\"Seed,omitempty\"`\n}\n\nfunc (x *Addons) Reset() {\n\t*x = Addons{}\n\tif protoimpl.UnsafeEnabled {\n\t\tmi := &file_transport_vless_config_proto_msgTypes[0]\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tms.StoreMessageInfo(mi)\n\t}\n}\n\nfunc (x *Addons) String() string {\n\treturn protoimpl.X.MessageStringOf(x)\n}\n\nfunc (*Addons) ProtoMessage() {}\n\nfunc (x *Addons) ProtoReflect() protoreflect.Message {\n\tmi := &file_transport_vless_config_proto_msgTypes[0]\n\tif protoimpl.UnsafeEnabled && x != nil {\n\t\tms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x))\n\t\tif ms.LoadMessageInfo() == nil {\n\t\t\tms.StoreMessageInfo(mi)\n\t\t}\n\t\treturn ms\n\t}\n\treturn mi.MessageOf(x)\n}\n\n// Deprecated: Use Addons.ProtoReflect.Descriptor instead.\nfunc (*Addons) Descriptor() ([]byte, []int) {\n\treturn file_transport_vless_config_proto_rawDescGZIP(), []int{0}\n}\n\nfunc (x *Addons) GetFlow() string {\n\tif x != nil {\n\t\treturn x.Flow\n\t}\n\treturn \"\"\n}\n\nfunc (x *Addons) GetSeed() []byte {\n\tif x != nil {\n\t\treturn x.Seed\n\t}\n\treturn nil\n}\n\nvar File_transport_vless_config_proto protoreflect.FileDescriptor\n\nvar file_transport_vless_config_proto_rawDesc = []byte{\n\t0x0a, 0x1c, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x76, 0x6c, 0x65, 0x73,\n\t0x73, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x15,\n\t0x63, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e,\n\t0x76, 0x6c, 0x65, 0x73, 0x73, 0x22, 0x30, 0x0a, 0x06, 0x41, 0x64, 0x64, 0x6f, 0x6e, 0x73, 0x12,\n\t0x12, 0x0a, 0x04, 0x46, 0x6c, 0x6f, 0x77, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x46,\n\t0x6c, 0x6f, 0x77, 0x12, 0x12, 0x0a, 0x04, 0x53, 0x65, 0x65, 0x64, 0x18, 0x02, 0x20, 0x01, 0x28,\n\t0x0c, 0x52, 0x04, 0x53, 0x65, 0x65, 0x64, 0x42, 0x61, 0x0a, 0x19, 0x63, 0x6f, 0x6d, 0x2e, 0x63,\n\t0x6c, 0x61, 0x73, 0x68, 0x2e, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2e, 0x76,\n\t0x6c, 0x65, 0x73, 0x73, 0x50, 0x01, 0x5a, 0x2a, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63,\n\t0x6f, 0x6d, 0x2f, 0x44, 0x72, 0x65, 0x61, 0x6d, 0x61, 0x63, 0x72, 0x6f, 0x2f, 0x63, 0x6c, 0x61,\n\t0x73, 0x68, 0x2f, 0x74, 0x72, 0x61, 0x6e, 0x73, 0x70, 0x6f, 0x72, 0x74, 0x2f, 0x76, 0x6c, 0x65,\n\t0x73, 0x73, 0xaa, 0x02, 0x15, 0x43, 0x6c, 0x61, 0x73, 0x68, 0x2e, 0x54, 0x72, 0x61, 0x6e, 0x73,\n\t0x70, 0x6f, 0x72, 0x74, 0x2e, 0x56, 0x6c, 0x65, 0x73, 0x73, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,\n\t0x6f, 0x33,\n}\n\nvar (\n\tfile_transport_vless_config_proto_rawDescOnce sync.Once\n\tfile_transport_vless_config_proto_rawDescData = file_transport_vless_config_proto_rawDesc\n)\n\nfunc file_transport_vless_config_proto_rawDescGZIP() []byte {\n\tfile_transport_vless_config_proto_rawDescOnce.Do(func() {\n\t\tfile_transport_vless_config_proto_rawDescData = protoimpl.X.CompressGZIP(file_transport_vless_config_proto_rawDescData)\n\t})\n\treturn file_transport_vless_config_proto_rawDescData\n}\n\nvar file_transport_vless_config_proto_msgTypes = make([]protoimpl.MessageInfo, 1)\nvar file_transport_vless_config_proto_goTypes = []interface{}{\n\t(*Addons)(nil), // 0: mihomo.transport.vless.Addons\n}\nvar file_transport_vless_config_proto_depIdxs = []int32{\n\t0, // [0:0] is the sub-list for method output_type\n\t0, // [0:0] is the sub-list for method input_type\n\t0, // [0:0] is the sub-list for extension type_name\n\t0, // [0:0] is the sub-list for extension extendee\n\t0, // [0:0] is the sub-list for field type_name\n}\n\nfunc init() { file_transport_vless_config_proto_init() }\nfunc file_transport_vless_config_proto_init() {\n\tif File_transport_vless_config_proto != nil {\n\t\treturn\n\t}\n\tif !protoimpl.UnsafeEnabled {\n\t\tfile_transport_vless_config_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} {\n\t\t\tswitch v := v.(*Addons); i {\n\t\t\tcase 0:\n\t\t\t\treturn &v.state\n\t\t\tcase 1:\n\t\t\t\treturn &v.sizeCache\n\t\t\tcase 2:\n\t\t\t\treturn &v.unknownFields\n\t\t\tdefault:\n\t\t\t\treturn nil\n\t\t\t}\n\t\t}\n\t}\n\ttype x struct{}\n\tout := protoimpl.TypeBuilder{\n\t\tFile: protoimpl.DescBuilder{\n\t\t\tGoPackagePath: reflect.TypeOf(x{}).PkgPath(),\n\t\t\tRawDescriptor: file_transport_vless_config_proto_rawDesc,\n\t\t\tNumEnums:      0,\n\t\t\tNumMessages:   1,\n\t\t\tNumExtensions: 0,\n\t\t\tNumServices:   0,\n\t\t},\n\t\tGoTypes:           file_transport_vless_config_proto_goTypes,\n\t\tDependencyIndexes: file_transport_vless_config_proto_depIdxs,\n\t\tMessageInfos:      file_transport_vless_config_proto_msgTypes,\n\t}.Build()\n\tFile_transport_vless_config_proto = out.File\n\tfile_transport_vless_config_proto_rawDesc = nil\n\tfile_transport_vless_config_proto_goTypes = nil\n\tfile_transport_vless_config_proto_depIdxs = nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vless/config.proto",
    "content": "syntax = \"proto3\";\n\npackage mihomo.transport.vless;\noption csharp_namespace = \"Mihomo.Transport.Vless\";\noption go_package = \"github.com/metacubex/mihomo/transport/vless\";\noption java_package = \"com.mihomo.transport.vless\";\noption java_multiple_files = true;\n\nmessage Addons {\n  string Flow = 1;\n  bytes Seed = 2;\n}"
  },
  {
    "path": "core/Clash.Meta/transport/vless/conn.go",
    "content": "package vless\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/common/buf\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/transport/vless/vision\"\n\n\t\"github.com/gofrs/uuid/v5\"\n\t\"google.golang.org/protobuf/proto\"\n)\n\ntype Conn struct {\n\tN.ExtendedConn\n\tdst      *DstAddr\n\tid       uuid.UUID\n\taddons   *Addons\n\treceived bool\n\tsent     bool\n}\n\nfunc (vc *Conn) Read(b []byte) (int, error) {\n\tif !vc.received {\n\t\tif err := vc.recvResponse(); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tvc.received = true\n\t}\n\treturn vc.ExtendedConn.Read(b)\n}\n\nfunc (vc *Conn) ReadBuffer(buffer *buf.Buffer) error {\n\tif !vc.received {\n\t\tif err := vc.recvResponse(); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tvc.received = true\n\t}\n\treturn vc.ExtendedConn.ReadBuffer(buffer)\n}\n\nfunc (vc *Conn) Write(p []byte) (int, error) {\n\tif !vc.sent {\n\t\tif err := vc.sendRequest(p); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tvc.sent = true\n\t\treturn len(p), nil\n\t}\n\n\treturn vc.ExtendedConn.Write(p)\n}\n\nfunc (vc *Conn) WriteBuffer(buffer *buf.Buffer) error {\n\tif !vc.sent {\n\t\tif err := vc.sendRequest(buffer.Bytes()); err != nil {\n\t\t\treturn err\n\t\t}\n\t\tvc.sent = true\n\t\treturn nil\n\t}\n\n\treturn vc.ExtendedConn.WriteBuffer(buffer)\n}\n\nfunc (vc *Conn) sendRequest(p []byte) (err error) {\n\tvar addonsBytes []byte\n\tif vc.addons != nil {\n\t\taddonsBytes, err = proto.Marshal(vc.addons)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\trequestLen := 1  // protocol version\n\trequestLen += 16 // UUID\n\trequestLen += 1  // addons length\n\trequestLen += len(addonsBytes)\n\trequestLen += 1 // command\n\tif !vc.dst.Mux {\n\t\trequestLen += 2 // port\n\t\trequestLen += 1 // addr type\n\t\trequestLen += len(vc.dst.Addr)\n\t}\n\trequestLen += len(p)\n\n\tbuffer := buf.NewSize(requestLen)\n\tdefer buffer.Release()\n\n\tbuf.Must(\n\t\tbuffer.WriteByte(Version),              // protocol version\n\t\tbuf.Error(buffer.Write(vc.id.Bytes())), // 16 bytes of uuid\n\t\tbuffer.WriteByte(byte(len(addonsBytes))),\n\t\tbuf.Error(buffer.Write(addonsBytes)),\n\t)\n\n\tif vc.dst.Mux {\n\t\tbuf.Must(buffer.WriteByte(CommandMux))\n\t} else {\n\t\tif vc.dst.UDP {\n\t\t\tbuf.Must(buffer.WriteByte(CommandUDP))\n\t\t} else {\n\t\t\tbuf.Must(buffer.WriteByte(CommandTCP))\n\t\t}\n\n\t\tbinary.BigEndian.PutUint16(buffer.Extend(2), vc.dst.Port)\n\t\tbuf.Must(\n\t\t\tbuffer.WriteByte(vc.dst.AddrType),\n\t\t\tbuf.Error(buffer.Write(vc.dst.Addr)),\n\t\t)\n\t}\n\n\tbuf.Must(buf.Error(buffer.Write(p)))\n\n\t_, err = vc.ExtendedConn.Write(buffer.Bytes())\n\treturn\n}\n\nfunc (vc *Conn) recvResponse() (err error) {\n\tvar buffer [2]byte\n\t_, err = io.ReadFull(vc.ExtendedConn, buffer[:])\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif buffer[0] != Version {\n\t\treturn errors.New(\"unexpected response version\")\n\t}\n\n\tlength := int64(buffer[1])\n\tif length != 0 { // addon data length > 0\n\t\tio.CopyN(io.Discard, vc.ExtendedConn, length) // just discard\n\t}\n\n\treturn\n}\n\nfunc (vc *Conn) Upstream() any {\n\treturn vc.ExtendedConn\n}\n\nfunc (vc *Conn) ReaderReplaceable() bool {\n\treturn vc.received\n}\n\nfunc (vc *Conn) WriterReplaceable() bool {\n\treturn vc.sent\n}\n\nfunc (vc *Conn) NeedHandshake() bool {\n\treturn !vc.sent\n}\n\n// newConn return a Conn instance\nfunc newConn(conn net.Conn, client *Client, dst *DstAddr) (net.Conn, error) {\n\tc := &Conn{\n\t\tExtendedConn: N.NewExtendedConn(conn),\n\t\tid:           client.uuid,\n\t\taddons:       client.Addons,\n\t\tdst:          dst,\n\t}\n\n\tif client.Addons != nil {\n\t\tswitch client.Addons.Flow {\n\t\tcase XRV:\n\t\t\tvisionConn, err := vision.NewConn(c, conn, c.id)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn visionConn, nil\n\t\t}\n\t}\n\n\treturn c, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vless/encryption/client.go",
    "content": "package encryption\n\nimport (\n\t\"crypto/cipher\"\n\t\"crypto/ecdh\"\n\t\"crypto/rand\"\n\t\"errors\"\n\t\"io\"\n\t\"net\"\n\t\"runtime\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/blake3\"\n\t\"github.com/metacubex/cpu\"\n\t\"github.com/metacubex/mlkem\"\n)\n\nvar (\n\t// Keep in sync with crypto/internal/fips140/aes/gcm.supportsAESGCM.\n\thasGCMAsmAMD64 = cpu.X86.HasAES && cpu.X86.HasPCLMULQDQ && cpu.X86.HasSSE41 && cpu.X86.HasSSSE3\n\thasGCMAsmARM64 = cpu.ARM64.HasAES && cpu.ARM64.HasPMULL\n\thasGCMAsmS390X = cpu.S390X.HasAES && cpu.S390X.HasAESCTR && cpu.S390X.HasGHASH\n\thasGCMAsmPPC64 = runtime.GOARCH == \"ppc64\" || runtime.GOARCH == \"ppc64le\"\n\n\tHasAESGCMHardwareSupport = hasGCMAsmAMD64 || hasGCMAsmARM64 || hasGCMAsmS390X || hasGCMAsmPPC64\n)\n\ntype ClientInstance struct {\n\tNfsPKeys      []any\n\tNfsPKeysBytes [][]byte\n\tHash32s       [][32]byte\n\tRelaysLength  int\n\tXorMode       uint32\n\tSeconds       uint32\n\tPaddingLens   [][3]int\n\tPaddingGaps   [][3]int\n\n\tRWLock sync.RWMutex\n\tExpire time.Time\n\tPfsKey []byte\n\tTicket []byte\n}\n\nfunc (i *ClientInstance) Init(nfsPKeysBytes [][]byte, xorMode, seconds uint32, padding string) (err error) {\n\tif i.NfsPKeys != nil {\n\t\treturn errors.New(\"already initialized\")\n\t}\n\tl := len(nfsPKeysBytes)\n\tif l == 0 {\n\t\treturn errors.New(\"empty nfsPKeysBytes\")\n\t}\n\ti.NfsPKeys = make([]any, l)\n\ti.NfsPKeysBytes = nfsPKeysBytes\n\ti.Hash32s = make([][32]byte, l)\n\tfor j, k := range nfsPKeysBytes {\n\t\tif len(k) == 32 {\n\t\t\tif i.NfsPKeys[j], err = ecdh.X25519().NewPublicKey(k); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ti.RelaysLength += 32 + 32\n\t\t} else {\n\t\t\tif i.NfsPKeys[j], err = mlkem.NewEncapsulationKey768(k); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ti.RelaysLength += 1088 + 32\n\t\t}\n\t\ti.Hash32s[j] = blake3.Sum256(k)\n\t}\n\ti.RelaysLength -= 32\n\ti.XorMode = xorMode\n\ti.Seconds = seconds\n\treturn ParsePadding(padding, &i.PaddingLens, &i.PaddingGaps)\n}\n\nfunc (i *ClientInstance) Handshake(conn net.Conn) (*CommonConn, error) {\n\tif i.NfsPKeys == nil {\n\t\treturn nil, errors.New(\"uninitialized\")\n\t}\n\tc := NewCommonConn(conn, HasAESGCMHardwareSupport)\n\n\tivAndRealysLength := 16 + i.RelaysLength\n\tpfsKeyExchangeLength := 18 + 1184 + 32 + 16\n\tpaddingLength, paddingLens, paddingGaps := CreatPadding(i.PaddingLens, i.PaddingGaps)\n\tclientHello := make([]byte, ivAndRealysLength+pfsKeyExchangeLength+paddingLength)\n\n\tiv := clientHello[:16]\n\trand.Read(iv)\n\trelays := clientHello[16:ivAndRealysLength]\n\tvar nfsKey []byte\n\tvar lastCTR cipher.Stream\n\tfor j, k := range i.NfsPKeys {\n\t\tvar index = 32\n\t\tif k, ok := k.(*ecdh.PublicKey); ok {\n\t\t\tprivateKey, _ := ecdh.X25519().GenerateKey(rand.Reader)\n\t\t\tcopy(relays, privateKey.PublicKey().Bytes())\n\t\t\tvar err error\n\t\t\tnfsKey, err = privateKey.ECDH(k)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\tif k, ok := k.(*mlkem.EncapsulationKey768); ok {\n\t\t\tvar ciphertext []byte\n\t\t\tnfsKey, ciphertext = k.Encapsulate()\n\t\t\tcopy(relays, ciphertext)\n\t\t\tindex = 1088\n\t\t}\n\t\tif i.XorMode > 0 { // this xor can (others can't) be recovered by client's config, revealing an X25519 public key / ML-KEM-768 ciphertext, that's why \"native\" values\n\t\t\tNewCTR(i.NfsPKeysBytes[j], iv).XORKeyStream(relays, relays[:index]) // make X25519 public key / ML-KEM-768 ciphertext distinguishable from random bytes\n\t\t}\n\t\tif lastCTR != nil {\n\t\t\tlastCTR.XORKeyStream(relays, relays[:32]) // make this relay irreplaceable\n\t\t}\n\t\tif j == len(i.NfsPKeys)-1 {\n\t\t\tbreak\n\t\t}\n\t\tlastCTR = NewCTR(nfsKey, iv)\n\t\tlastCTR.XORKeyStream(relays[index:], i.Hash32s[j+1][:])\n\t\trelays = relays[index+32:]\n\t}\n\tnfsAEAD := NewAEAD(iv, nfsKey, c.UseAES)\n\n\tif i.Seconds > 0 {\n\t\ti.RWLock.RLock()\n\t\tif time.Now().Before(i.Expire) {\n\t\t\tc.Client = i\n\t\t\tc.UnitedKey = append(i.PfsKey, nfsKey...) // different unitedKey for each connection\n\t\t\tnfsAEAD.Seal(clientHello[:ivAndRealysLength], nil, EncodeLength(32), nil)\n\t\t\tnfsAEAD.Seal(clientHello[:ivAndRealysLength+18], nil, i.Ticket, nil)\n\t\t\ti.RWLock.RUnlock()\n\t\t\tc.PreWrite = clientHello[:ivAndRealysLength+18+32]\n\t\t\tc.AEAD = NewAEAD(clientHello[ivAndRealysLength+18:ivAndRealysLength+18+32], c.UnitedKey, c.UseAES)\n\t\t\tif i.XorMode == 2 {\n\t\t\t\tc.Conn = NewXorConn(conn, NewCTR(c.UnitedKey, iv), nil, len(c.PreWrite), 16)\n\t\t\t}\n\t\t\treturn c, nil\n\t\t}\n\t\ti.RWLock.RUnlock()\n\t}\n\n\tpfsKeyExchange := clientHello[ivAndRealysLength : ivAndRealysLength+pfsKeyExchangeLength]\n\tnfsAEAD.Seal(pfsKeyExchange[:0], nil, EncodeLength(pfsKeyExchangeLength-18), nil)\n\tmlkem768DKey, _ := mlkem.GenerateKey768()\n\tx25519SKey, _ := ecdh.X25519().GenerateKey(rand.Reader)\n\tpfsPublicKey := append(mlkem768DKey.EncapsulationKey().Bytes(), x25519SKey.PublicKey().Bytes()...)\n\tnfsAEAD.Seal(pfsKeyExchange[:18], nil, pfsPublicKey, nil)\n\n\tpadding := clientHello[ivAndRealysLength+pfsKeyExchangeLength:]\n\tnfsAEAD.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)\n\tnfsAEAD.Seal(padding[:18], nil, padding[18:paddingLength-16], nil)\n\n\tpaddingLens[0] = ivAndRealysLength + pfsKeyExchangeLength + paddingLens[0]\n\tfor i, l := range paddingLens { // sends padding in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control\n\t\tif l > 0 {\n\t\t\tif _, err := conn.Write(clientHello[:l]); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tclientHello = clientHello[l:]\n\t\t}\n\t\tif len(paddingGaps) > i {\n\t\t\ttime.Sleep(paddingGaps[i])\n\t\t}\n\t}\n\n\tencryptedPfsPublicKey := make([]byte, 1088+32+16)\n\tif _, err := io.ReadFull(conn, encryptedPfsPublicKey); err != nil {\n\t\treturn nil, err\n\t}\n\tnfsAEAD.Open(encryptedPfsPublicKey[:0], MaxNonce, encryptedPfsPublicKey, nil)\n\tmlkem768Key, err := mlkem768DKey.Decapsulate(encryptedPfsPublicKey[:1088])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tpeerX25519PKey, err := ecdh.X25519().NewPublicKey(encryptedPfsPublicKey[1088 : 1088+32])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tx25519Key, err := x25519SKey.ECDH(peerX25519PKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tpfsKey := make([]byte, 32+32) // no more capacity\n\tcopy(pfsKey, mlkem768Key)\n\tcopy(pfsKey[32:], x25519Key)\n\tc.UnitedKey = append(pfsKey, nfsKey...)\n\tc.AEAD = NewAEAD(pfsPublicKey, c.UnitedKey, c.UseAES)\n\tc.PeerAEAD = NewAEAD(encryptedPfsPublicKey[:1088+32], c.UnitedKey, c.UseAES)\n\n\tencryptedTicket := make([]byte, 32)\n\tif _, err := io.ReadFull(conn, encryptedTicket); err != nil {\n\t\treturn nil, err\n\t}\n\tif _, err := c.PeerAEAD.Open(encryptedTicket[:0], nil, encryptedTicket, nil); err != nil {\n\t\treturn nil, err\n\t}\n\tseconds := DecodeLength(encryptedTicket)\n\n\tif i.Seconds > 0 && seconds > 0 {\n\t\ti.RWLock.Lock()\n\t\ti.Expire = time.Now().Add(time.Duration(seconds) * time.Second)\n\t\ti.PfsKey = pfsKey\n\t\ti.Ticket = encryptedTicket[:16]\n\t\ti.RWLock.Unlock()\n\t}\n\n\tencryptedLength := make([]byte, 18)\n\tif _, err := io.ReadFull(conn, encryptedLength); err != nil {\n\t\treturn nil, err\n\t}\n\tif _, err := c.PeerAEAD.Open(encryptedLength[:0], nil, encryptedLength, nil); err != nil {\n\t\treturn nil, err\n\t}\n\tlength := DecodeLength(encryptedLength[:2])\n\tc.PeerPadding = make([]byte, length) // important: allows server sends padding slowly, eliminating 1-RTT's traffic pattern\n\n\tif i.XorMode == 2 {\n\t\tc.Conn = NewXorConn(conn, NewCTR(c.UnitedKey, iv), NewCTR(c.UnitedKey, encryptedTicket[:16]), 0, length)\n\t}\n\treturn c, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vless/encryption/client_test.go",
    "content": "package encryption\n\nimport (\n\t\"fmt\"\n\t\"runtime\"\n\t\"testing\"\n)\n\nfunc TestHasAESGCMHardwareSupport(t *testing.T) {\n\tfmt.Println(\"HasAESGCMHardwareSupport:\", HasAESGCMHardwareSupport)\n\n\tif runtime.GOARCH == \"arm64\" && runtime.GOOS == \"darwin\" {\n\t\t// It should be supported starting from Apple Silicon M1\n\t\t// https://github.com/golang/go/blob/go1.25.0/src/internal/cpu/cpu_arm64_darwin.go#L26-L30\n\t\tif !HasAESGCMHardwareSupport {\n\t\t\tt.Errorf(\"For ARM64 Darwin platforms (excluding iOS), AES GCM hardware acceleration should always be available.\")\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vless/encryption/common.go",
    "content": "package encryption\n\nimport (\n\t\"bytes\"\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/pool\"\n\n\t\"github.com/metacubex/blake3\"\n\t\"github.com/metacubex/randv2\"\n\t\"golang.org/x/crypto/chacha20poly1305\"\n)\n\ntype CommonConn struct {\n\tnet.Conn\n\tUseAES      bool\n\tClient      *ClientInstance\n\tUnitedKey   []byte\n\tPreWrite    []byte\n\tAEAD        *AEAD\n\tPeerAEAD    *AEAD\n\tPeerPadding []byte\n\trawInput    bytes.Buffer // PeerInBytes\n\tinput       bytes.Reader // PeerCache\n}\n\nfunc NewCommonConn(conn net.Conn, useAES bool) *CommonConn {\n\treturn &CommonConn{\n\t\tConn:   conn,\n\t\tUseAES: useAES,\n\t}\n}\n\nfunc (c *CommonConn) Write(b []byte) (int, error) {\n\tif len(b) == 0 {\n\t\treturn 0, nil\n\t}\n\toutBytes := pool.Get(5 + 8192 + 16)\n\tdefer pool.Put(outBytes)\n\tfor n := 0; n < len(b); {\n\t\tb := b[n:]\n\t\tif len(b) > 8192 {\n\t\t\tb = b[:8192] // for avoiding another copy() in peer's Read()\n\t\t}\n\t\tn += len(b)\n\t\theaderAndData := outBytes[:5+len(b)+16]\n\t\tEncodeHeader(headerAndData, len(b)+16)\n\t\tmax := false\n\t\tif bytes.Equal(c.AEAD.Nonce[:], MaxNonce) {\n\t\t\tmax = true\n\t\t}\n\t\tc.AEAD.Seal(headerAndData[:5], nil, b, headerAndData[:5])\n\t\tif max {\n\t\t\tc.AEAD = NewAEAD(headerAndData, c.UnitedKey, c.UseAES)\n\t\t}\n\t\tif c.PreWrite != nil {\n\t\t\theaderAndData = append(c.PreWrite, headerAndData...)\n\t\t\tc.PreWrite = nil\n\t\t}\n\t\tif _, err := c.Conn.Write(headerAndData); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t}\n\treturn len(b), nil\n}\n\nfunc (c *CommonConn) Read(b []byte) (int, error) {\n\tif len(b) == 0 {\n\t\treturn 0, nil\n\t}\n\tif c.PeerAEAD == nil { // client's 0-RTT\n\t\tserverRandom := make([]byte, 16)\n\t\tif _, err := io.ReadFull(c.Conn, serverRandom); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tc.PeerAEAD = NewAEAD(serverRandom, c.UnitedKey, c.UseAES)\n\t\tif xorConn, ok := c.Conn.(*XorConn); ok {\n\t\t\txorConn.PeerCTR = NewCTR(c.UnitedKey, serverRandom)\n\t\t}\n\t}\n\tif c.PeerPadding != nil { // client's 1-RTT\n\t\tif _, err := io.ReadFull(c.Conn, c.PeerPadding); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tif _, err := c.PeerAEAD.Open(c.PeerPadding[:0], nil, c.PeerPadding, nil); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\tc.PeerPadding = nil\n\t}\n\tif c.input.Len() > 0 {\n\t\treturn c.input.Read(b)\n\t}\n\tpeerHeader := [5]byte{}\n\tif _, err := io.ReadFull(c.Conn, peerHeader[:]); err != nil {\n\t\treturn 0, err\n\t}\n\tl, err := DecodeHeader(peerHeader[:]) // l: 17~17000\n\tif err != nil {\n\t\tif c.Client != nil && errors.Is(err, ErrInvalidHeader) { // client's 0-RTT\n\t\t\tc.Client.RWLock.Lock()\n\t\t\tif bytes.HasPrefix(c.UnitedKey, c.Client.PfsKey) {\n\t\t\t\tc.Client.Expire = time.Now() // expired\n\t\t\t}\n\t\t\tc.Client.RWLock.Unlock()\n\t\t\treturn 0, errors.New(\"new handshake needed\")\n\t\t}\n\t\treturn 0, err\n\t}\n\tc.Client = nil\n\tif c.rawInput.Cap() < l {\n\t\tc.rawInput.Grow(l) // no need to use sync.Pool, because we are always reading\n\t}\n\tpeerData := c.rawInput.Bytes()[:l]\n\tif _, err := io.ReadFull(c.Conn, peerData); err != nil {\n\t\treturn 0, err\n\t}\n\tdst := peerData[:l-16]\n\tif len(dst) <= len(b) {\n\t\tdst = b[:len(dst)] // avoids another copy()\n\t}\n\tvar newAEAD *AEAD\n\tif bytes.Equal(c.PeerAEAD.Nonce[:], MaxNonce) {\n\t\tnewAEAD = NewAEAD(append(peerHeader[:], peerData...), c.UnitedKey, c.UseAES)\n\t}\n\t_, err = c.PeerAEAD.Open(dst[:0], nil, peerData, peerHeader[:])\n\tif newAEAD != nil {\n\t\tc.PeerAEAD = newAEAD\n\t}\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tif len(dst) > len(b) {\n\t\tc.input.Reset(dst[copy(b, dst):])\n\t\tdst = b // for len(dst)\n\t}\n\treturn len(dst), nil\n}\n\ntype AEAD struct {\n\tcipher.AEAD\n\tNonce [12]byte\n}\n\nfunc NewAEAD(ctx, key []byte, useAES bool) *AEAD {\n\tk := make([]byte, 32)\n\tblake3.DeriveKey(k, string(ctx), key)\n\tvar aead cipher.AEAD\n\tif useAES {\n\t\tblock, _ := aes.NewCipher(k)\n\t\taead, _ = cipher.NewGCM(block)\n\t} else {\n\t\taead, _ = chacha20poly1305.New(k)\n\t}\n\treturn &AEAD{AEAD: aead}\n}\n\nfunc (a *AEAD) Seal(dst, nonce, plaintext, additionalData []byte) []byte {\n\tif nonce == nil {\n\t\tnonce = IncreaseNonce(a.Nonce[:])\n\t}\n\treturn a.AEAD.Seal(dst, nonce, plaintext, additionalData)\n}\n\nfunc (a *AEAD) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {\n\tif nonce == nil {\n\t\tnonce = IncreaseNonce(a.Nonce[:])\n\t}\n\treturn a.AEAD.Open(dst, nonce, ciphertext, additionalData)\n}\n\nfunc IncreaseNonce(nonce []byte) []byte {\n\tfor i := 0; i < 12; i++ {\n\t\tnonce[11-i]++\n\t\tif nonce[11-i] != 0 {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn nonce\n}\n\nvar MaxNonce = bytes.Repeat([]byte{255}, 12)\n\nfunc EncodeLength(l int) []byte {\n\treturn []byte{byte(l >> 8), byte(l)}\n}\n\nfunc DecodeLength(b []byte) int {\n\treturn int(b[0])<<8 | int(b[1])\n}\n\nfunc EncodeHeader(h []byte, l int) {\n\th[0] = 23\n\th[1] = 3\n\th[2] = 3\n\th[3] = byte(l >> 8)\n\th[4] = byte(l)\n}\n\nvar ErrInvalidHeader = errors.New(\"invalid header\")\n\nfunc DecodeHeader(h []byte) (l int, err error) {\n\tl = int(h[3])<<8 | int(h[4])\n\tif h[0] != 23 || h[1] != 3 || h[2] != 3 {\n\t\tl = 0\n\t}\n\tif l < 17 || l > 17000 { // TODO: TLSv1.3 max length\n\t\terr = fmt.Errorf(\"%w: %v\", ErrInvalidHeader, h[:5]) // DO NOT CHANGE: relied by client's Read()\n\t}\n\treturn\n}\n\nfunc ParsePadding(padding string, paddingLens, paddingGaps *[][3]int) (err error) {\n\tif padding == \"\" {\n\t\treturn\n\t}\n\tmaxLen := 0\n\tfor i, s := range strings.Split(padding, \".\") {\n\t\tx := strings.Split(s, \"-\")\n\t\tif len(x) < 3 || x[0] == \"\" || x[1] == \"\" || x[2] == \"\" {\n\t\t\treturn errors.New(\"invalid padding lenth/gap parameter: \" + s)\n\t\t}\n\t\ty := [3]int{}\n\t\tif y[0], err = strconv.Atoi(x[0]); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif y[1], err = strconv.Atoi(x[1]); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif y[2], err = strconv.Atoi(x[2]); err != nil {\n\t\t\treturn\n\t\t}\n\t\tif i == 0 && (y[0] < 100 || y[1] < 18+17 || y[2] < 18+17) {\n\t\t\treturn errors.New(\"first padding length must not be smaller than 35\")\n\t\t}\n\t\tif i%2 == 0 {\n\t\t\t*paddingLens = append(*paddingLens, y)\n\t\t\tmaxLen += max(y[1], y[2])\n\t\t} else {\n\t\t\t*paddingGaps = append(*paddingGaps, y)\n\t\t}\n\t}\n\tif maxLen > 18+65535 {\n\t\treturn errors.New(\"total padding length must not be larger than 65553\")\n\t}\n\treturn\n}\n\nfunc CreatPadding(paddingLens, paddingGaps [][3]int) (length int, lens []int, gaps []time.Duration) {\n\tif len(paddingLens) == 0 {\n\t\tpaddingLens = [][3]int{{100, 111, 1111}, {50, 0, 3333}}\n\t\tpaddingGaps = [][3]int{{75, 0, 111}}\n\t}\n\tfor _, y := range paddingLens {\n\t\tl := 0\n\t\tif y[0] >= int(randBetween(0, 100)) {\n\t\t\tl = int(randBetween(int64(y[1]), int64(y[2])))\n\t\t}\n\t\tlens = append(lens, l)\n\t\tlength += l\n\t}\n\tfor _, y := range paddingGaps {\n\t\tg := 0\n\t\tif y[0] >= int(randBetween(0, 100)) {\n\t\t\tg = int(randBetween(int64(y[1]), int64(y[2])))\n\t\t}\n\t\tgaps = append(gaps, time.Duration(g)*time.Millisecond)\n\t}\n\treturn\n}\n\nfunc max[T ~int | ~uint | ~int64 | ~uint64 | ~int32 | ~uint32 | ~int16 | ~uint16 | ~int8 | ~uint8](a, b T) T {\n\tif a > b {\n\t\treturn a\n\t}\n\treturn b\n}\n\nfunc randBetween(from int64, to int64) int64 {\n\tif from == to {\n\t\treturn from\n\t}\n\n\tif to < from {\n\t\tfrom, to = to, from\n\t}\n\n\treturn from + randv2.Int64N(to-from)\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vless/encryption/doc.go",
    "content": "// Package encryption copy and modify from xray-core\n// https://github.com/XTLS/Xray-core/commit/f61c14e9c63dc41a8a09135db3aea337974f3f37\n// https://github.com/XTLS/Xray-core/commit/3e19bf9233bdd9bafc073a71c65b737cc1ffba5e\n// https://github.com/XTLS/Xray-core/commit/7ffb555fc8ec51bd1e3e60f26f1d6957984dba80\n// https://github.com/XTLS/Xray-core/commit/ec1cc35188c1a5f38a2ff75e88b5d043ffdc59da\n// https://github.com/XTLS/Xray-core/commit/5c611420487a92f931faefc01d4bf03869f477f6\n// https://github.com/XTLS/Xray-core/commit/23d7aad461d232bc5bed52dd6aaa731ecd88ad35\n// https://github.com/XTLS/Xray-core/commit/3c20bddfcfd8999be5f9a2ac180dc959950e4c61\n// https://github.com/XTLS/Xray-core/commit/1720be168fa069332c418503d30341fc6e01df7f\n// https://github.com/XTLS/Xray-core/commit/0fd7691d6b28e05922d7a5a9313d97745a51ea63\n// https://github.com/XTLS/Xray-core/commit/09cc92c61d9067e0d65c1cae9124664ecfc78f43\n// https://github.com/XTLS/Xray-core/commit/2807ee432a1fbeb301815647189eacd650b12a8b\n// https://github.com/XTLS/Xray-core/commit/bfe4820f2f086daf639b1957eb23dc13c843cad1\n// https://github.com/XTLS/Xray-core/commit/d1fb48521271251a8c74bd64fcc2fc8700717a3b\n// https://github.com/XTLS/Xray-core/commit/49580705f6029648399304b816a2737f991582a8\n// https://github.com/XTLS/Xray-core/commit/84835bec7d0d8555d0dd30953ed26a272de814c4\n// https://github.com/XTLS/Xray-core/commit/373558ed7abdbac3de41745cf30ec04c9adde604\n// https://github.com/XTLS/Xray-core/commit/38cc306c955c362f044e074049a5e67b6b9fb389\n// https://github.com/XTLS/Xray-core/commit/b33555cc0a52d0af3c23d2af8fca42f8a685d9af\n// https://github.com/XTLS/Xray-core/commit/ad7140641c44239c9dcdc3d7215ea639b1f0841c\n// https://github.com/XTLS/Xray-core/commit/0199dea39988a1a1b846d0bf8598631bade40902\n// https://github.com/XTLS/Xray-core/commit/fce1195b60f48ca18a953dbd5c7d991869de9a5e\n// https://github.com/XTLS/Xray-core/commit/b0b220985c9c1bc832665458d5fd6e0c287b67ae\n// https://github.com/XTLS/Xray-core/commit/82ea7a3cc5ff23280b87e3052f0f83b04f0267fa\n// https://github.com/XTLS/Xray-core/commit/e8b02cd6649f14889841e8ab8ee6b2acca71dbe6\n// https://github.com/XTLS/Xray-core/commit/6768a22f676c9121cfc9dc4f51181a8a07837c8d\n// https://github.com/XTLS/Xray-core/commit/4c6fd94d97159f5a3e740ba6dd2d9b65e3ed320c\n// https://github.com/XTLS/Xray-core/commit/19f890729656bc923ae3dee8426168c93b8ee9c2\n// https://github.com/XTLS/Xray-core/commit/cbade89ab11af26ba1e480a3688a6c205fa3c3f8\npackage encryption\n"
  },
  {
    "path": "core/Clash.Meta/transport/vless/encryption/factory.go",
    "content": "package encryption\n\nimport (\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"strconv\"\n\t\"strings\"\n)\n\n// NewClient new client from encryption string\n// maybe return a nil *ClientInstance without any error, that means don't need to encrypt\nfunc NewClient(encryption string) (*ClientInstance, error) {\n\tswitch encryption {\n\tcase \"\", \"none\": // We will not reject empty string like xray-core does, because we need to ensure compatibility\n\t\treturn nil, nil\n\t}\n\tif s := strings.Split(encryption, \".\"); len(s) >= 4 && s[0] == \"mlkem768x25519plus\" {\n\t\tvar xorMode uint32\n\t\tswitch s[1] {\n\t\tcase \"native\":\n\t\tcase \"xorpub\":\n\t\t\txorMode = 1\n\t\tcase \"random\":\n\t\t\txorMode = 2\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"invaild vless encryption value: %s\", encryption)\n\t\t}\n\t\tvar seconds uint32\n\t\tswitch s[2] {\n\t\tcase \"1rtt\":\n\t\tcase \"0rtt\":\n\t\t\tseconds = 1\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"invaild vless encryption value: %s\", encryption)\n\t\t}\n\t\tvar nfsPKeysBytes [][]byte\n\t\tvar paddings []string\n\t\tfor _, r := range s[3:] {\n\t\t\tif len(r) < 20 {\n\t\t\t\tpaddings = append(paddings, r)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tb, err := base64.RawURLEncoding.DecodeString(r)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"invaild vless encryption value: %s\", encryption)\n\t\t\t}\n\t\t\tif len(b) != X25519PasswordSize && len(b) != MLKEM768ClientLength {\n\t\t\t\treturn nil, fmt.Errorf(\"invaild vless encryption value: %s\", encryption)\n\t\t\t}\n\t\t\tnfsPKeysBytes = append(nfsPKeysBytes, b)\n\t\t}\n\t\tpadding := strings.Join(paddings, \".\")\n\t\tclient := &ClientInstance{}\n\t\tif err := client.Init(nfsPKeysBytes, xorMode, seconds, padding); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to use encryption: %w\", err)\n\t\t}\n\t\treturn client, nil\n\t}\n\treturn nil, fmt.Errorf(\"invaild vless encryption value: %s\", encryption)\n}\n\n// NewServer new server from decryption string\n// maybe return a nil *ServerInstance without any error, that means don't need to decrypt\nfunc NewServer(decryption string) (*ServerInstance, error) {\n\tswitch decryption {\n\tcase \"\", \"none\": // We will not reject empty string like xray-core does, because we need to ensure compatibility\n\t\treturn nil, nil\n\t}\n\tif s := strings.Split(decryption, \".\"); len(s) >= 4 && s[0] == \"mlkem768x25519plus\" {\n\t\tvar xorMode uint32\n\t\tswitch s[1] {\n\t\tcase \"native\":\n\t\tcase \"xorpub\":\n\t\t\txorMode = 1\n\t\tcase \"random\":\n\t\t\txorMode = 2\n\t\tdefault:\n\t\t\treturn nil, fmt.Errorf(\"invaild vless decryption value: %s\", decryption)\n\t\t}\n\t\tt := strings.SplitN(strings.TrimSuffix(s[2], \"s\"), \"-\", 2)\n\t\ti, err := strconv.Atoi(t[0])\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"invaild vless decryption value: %s\", decryption)\n\t\t}\n\t\tsecondsFrom := int64(i)\n\t\tsecondsTo := int64(0)\n\t\tif len(t) == 2 {\n\t\t\ti, err = strconv.Atoi(t[1])\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"invaild vless decryption value: %s\", decryption)\n\t\t\t}\n\t\t\tsecondsTo = int64(i)\n\t\t}\n\t\tvar nfsSKeysBytes [][]byte\n\t\tvar paddings []string\n\t\tfor _, r := range s[3:] {\n\t\t\tif len(r) < 20 {\n\t\t\t\tpaddings = append(paddings, r)\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tb, err := base64.RawURLEncoding.DecodeString(r)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"invaild vless decryption value: %s\", decryption)\n\t\t\t}\n\t\t\tif len(b) != X25519PrivateKeySize && len(b) != MLKEM768SeedLength {\n\t\t\t\treturn nil, fmt.Errorf(\"invaild vless decryption value: %s\", decryption)\n\t\t\t}\n\t\t\tnfsSKeysBytes = append(nfsSKeysBytes, b)\n\t\t}\n\t\tpadding := strings.Join(paddings, \".\")\n\t\tserver := &ServerInstance{}\n\t\tif err := server.Init(nfsSKeysBytes, xorMode, secondsFrom, secondsTo, padding); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"failed to use decryption: %w\", err)\n\t\t}\n\t\treturn server, nil\n\t}\n\treturn nil, fmt.Errorf(\"invaild vless decryption value: %s\", decryption)\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vless/encryption/key.go",
    "content": "package encryption\n\nimport (\n\t\"crypto/ecdh\"\n\t\"crypto/rand\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\n\t\"github.com/metacubex/blake3\"\n\t\"github.com/metacubex/mlkem\"\n)\n\nconst MLKEM768SeedLength = mlkem.SeedSize\nconst MLKEM768ClientLength = mlkem.EncapsulationKeySize768\nconst X25519PasswordSize = 32\nconst X25519PrivateKeySize = 32\n\nfunc GenMLKEM768(seedStr string) (seedBase64, clientBase64, hash32Base64 string, err error) {\n\tvar seed [MLKEM768SeedLength]byte\n\tif len(seedStr) > 0 {\n\t\ts, _ := base64.RawURLEncoding.DecodeString(seedStr)\n\t\tif len(s) != MLKEM768SeedLength {\n\t\t\terr = fmt.Errorf(\"invalid length of ML-KEM-768 seed: %s\", seedStr)\n\t\t\treturn\n\t\t}\n\t\tseed = [MLKEM768SeedLength]byte(s)\n\t} else {\n\t\t_, err = rand.Read(seed[:])\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\tkey, _ := mlkem.NewDecapsulationKey768(seed[:])\n\tclient := key.EncapsulationKey().Bytes()\n\thash32 := blake3.Sum256(client)\n\tseedBase64 = base64.RawURLEncoding.EncodeToString(seed[:])\n\tclientBase64 = base64.RawURLEncoding.EncodeToString(client)\n\thash32Base64 = base64.RawURLEncoding.EncodeToString(hash32[:])\n\treturn\n}\n\nfunc GenX25519(privateKeyStr string) (privateKeyBase64, passwordBase64, hash32Base64 string, err error) {\n\tvar privateKey [X25519PrivateKeySize]byte\n\tif len(privateKeyStr) > 0 {\n\t\ts, _ := base64.RawURLEncoding.DecodeString(privateKeyStr)\n\t\tif len(s) != X25519PrivateKeySize {\n\t\t\terr = fmt.Errorf(\"invalid length of X25519 private key: %s\", privateKeyStr)\n\t\t\treturn\n\t\t}\n\t\tprivateKey = [X25519PrivateKeySize]byte(s)\n\t} else {\n\t\t_, err = rand.Read(privateKey[:])\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n\n\t// Avoid generating equivalent X25519 private keys\n\t// https://github.com/XTLS/Xray-core/pull/1747\n\t//\n\t// Modify random bytes using algorithm described at:\n\t// https://cr.yp.to/ecdh.html.\n\tprivateKey[0] &= 248\n\tprivateKey[31] &= 127\n\tprivateKey[31] |= 64\n\n\tkey, err := ecdh.X25519().NewPrivateKey(privateKey[:])\n\tif err != nil {\n\t\tfmt.Println(err.Error())\n\t\treturn\n\t}\n\tpassword := key.PublicKey().Bytes()\n\thash32 := blake3.Sum256(password)\n\tprivateKeyBase64 = base64.RawURLEncoding.EncodeToString(privateKey[:])\n\tpasswordBase64 = base64.RawURLEncoding.EncodeToString(password)\n\thash32Base64 = base64.RawURLEncoding.EncodeToString(hash32[:])\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vless/encryption/server.go",
    "content": "package encryption\n\nimport (\n\t\"bytes\"\n\t\"crypto/cipher\"\n\t\"crypto/ecdh\"\n\t\"crypto/rand\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/blake3\"\n\t\"github.com/metacubex/mlkem\"\n)\n\ntype ServerSession struct {\n\tPfsKey  []byte\n\tNfsKeys sync.Map\n}\n\ntype ServerInstance struct {\n\tNfsSKeys      []any\n\tNfsPKeysBytes [][]byte\n\tHash32s       [][32]byte\n\tRelaysLength  int\n\tXorMode       uint32\n\tSecondsFrom   int64\n\tSecondsTo     int64\n\tPaddingLens   [][3]int\n\tPaddingGaps   [][3]int\n\n\tRWLock   sync.RWMutex\n\tClosed   bool\n\tLasts    map[int64][16]byte\n\tTickets  [][16]byte\n\tSessions map[[16]byte]*ServerSession\n}\n\nfunc (i *ServerInstance) Init(nfsSKeysBytes [][]byte, xorMode uint32, secondsFrom, secondsTo int64, padding string) (err error) {\n\tif i.NfsSKeys != nil {\n\t\treturn errors.New(\"already initialized\")\n\t}\n\tl := len(nfsSKeysBytes)\n\tif l == 0 {\n\t\treturn errors.New(\"empty nfsSKeysBytes\")\n\t}\n\ti.NfsSKeys = make([]any, l)\n\ti.NfsPKeysBytes = make([][]byte, l)\n\ti.Hash32s = make([][32]byte, l)\n\tfor j, k := range nfsSKeysBytes {\n\t\tif len(k) == 32 {\n\t\t\tif i.NfsSKeys[j], err = ecdh.X25519().NewPrivateKey(k); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ti.NfsPKeysBytes[j] = i.NfsSKeys[j].(*ecdh.PrivateKey).PublicKey().Bytes()\n\t\t\ti.RelaysLength += 32 + 32\n\t\t} else {\n\t\t\tif i.NfsSKeys[j], err = mlkem.NewDecapsulationKey768(k); err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\ti.NfsPKeysBytes[j] = i.NfsSKeys[j].(*mlkem.DecapsulationKey768).EncapsulationKey().Bytes()\n\t\t\ti.RelaysLength += 1088 + 32\n\t\t}\n\t\ti.Hash32s[j] = blake3.Sum256(i.NfsPKeysBytes[j])\n\t}\n\ti.RelaysLength -= 32\n\ti.XorMode = xorMode\n\ti.SecondsFrom = secondsFrom\n\ti.SecondsTo = secondsTo\n\terr = ParsePadding(padding, &i.PaddingLens, &i.PaddingGaps)\n\tif err != nil {\n\t\treturn\n\t}\n\tif i.SecondsFrom > 0 || i.SecondsTo > 0 {\n\t\ti.Lasts = make(map[int64][16]byte)\n\t\ti.Tickets = make([][16]byte, 0, 1024)\n\t\ti.Sessions = make(map[[16]byte]*ServerSession)\n\t\tgo func() {\n\t\t\tfor {\n\t\t\t\ttime.Sleep(time.Minute)\n\t\t\t\ti.RWLock.Lock()\n\t\t\t\tif i.Closed {\n\t\t\t\t\ti.RWLock.Unlock()\n\t\t\t\t\treturn\n\t\t\t\t}\n\t\t\t\tminute := time.Now().Unix() / 60\n\t\t\t\tlast := i.Lasts[minute]\n\t\t\t\tdelete(i.Lasts, minute)\n\t\t\t\tdelete(i.Lasts, minute-1) // for insurance\n\t\t\t\tif last != [16]byte{} {\n\t\t\t\t\tfor j, ticket := range i.Tickets {\n\t\t\t\t\t\tdelete(i.Sessions, ticket)\n\t\t\t\t\t\tif ticket == last {\n\t\t\t\t\t\t\ti.Tickets = i.Tickets[j+1:]\n\t\t\t\t\t\t\tbreak\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\ti.RWLock.Unlock()\n\t\t\t}\n\t\t}()\n\t}\n\treturn\n}\n\nfunc (i *ServerInstance) Close() (err error) {\n\ti.RWLock.Lock()\n\ti.Closed = true\n\ti.RWLock.Unlock()\n\treturn\n}\n\nfunc (i *ServerInstance) Handshake(conn net.Conn, fallback *[]byte) (*CommonConn, error) {\n\tif i.NfsSKeys == nil {\n\t\treturn nil, errors.New(\"uninitialized\")\n\t}\n\tc := NewCommonConn(conn, true)\n\n\tivAndRelays := make([]byte, 16+i.RelaysLength)\n\tif _, err := io.ReadFull(conn, ivAndRelays); err != nil {\n\t\treturn nil, err\n\t}\n\tif fallback != nil {\n\t\t*fallback = append(*fallback, ivAndRelays...)\n\t}\n\tiv := ivAndRelays[:16]\n\trelays := ivAndRelays[16:]\n\tvar nfsKey []byte\n\tvar lastCTR cipher.Stream\n\tfor j, k := range i.NfsSKeys {\n\t\tif lastCTR != nil {\n\t\t\tlastCTR.XORKeyStream(relays, relays[:32]) // recover this relay\n\t\t}\n\t\tvar index = 32\n\t\tif _, ok := k.(*mlkem.DecapsulationKey768); ok {\n\t\t\tindex = 1088\n\t\t}\n\t\tif i.XorMode > 0 {\n\t\t\tNewCTR(i.NfsPKeysBytes[j], iv).XORKeyStream(relays, relays[:index]) // we don't use buggy elligator2, because we have PSK :)\n\t\t}\n\t\tif k, ok := k.(*ecdh.PrivateKey); ok {\n\t\t\tpublicKey, err := ecdh.X25519().NewPublicKey(relays[:index])\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tif publicKey.Bytes()[31] > 127 { // we just don't want the observer can change even one bit without breaking the connection, though it has nothing to do with security\n\t\t\t\treturn nil, errors.New(\"the highest bit of the last byte of the peer-sent X25519 public key is not 0\")\n\t\t\t}\n\t\t\tnfsKey, err = k.ECDH(publicKey)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\tif k, ok := k.(*mlkem.DecapsulationKey768); ok {\n\t\t\tvar err error\n\t\t\tnfsKey, err = k.Decapsulate(relays[:index])\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\t\tif j == len(i.NfsSKeys)-1 {\n\t\t\tbreak\n\t\t}\n\t\trelays = relays[index:]\n\t\tlastCTR = NewCTR(nfsKey, iv)\n\t\tlastCTR.XORKeyStream(relays, relays[:32])\n\t\tif !bytes.Equal(relays[:32], i.Hash32s[j+1][:]) {\n\t\t\treturn nil, fmt.Errorf(\"unexpected hash32: %v\", relays[:32])\n\t\t}\n\t\trelays = relays[32:]\n\t}\n\tnfsAEAD := NewAEAD(iv, nfsKey, c.UseAES)\n\n\tencryptedLength := make([]byte, 18)\n\tif _, err := io.ReadFull(conn, encryptedLength); err != nil {\n\t\treturn nil, err\n\t}\n\tif fallback != nil {\n\t\t*fallback = append(*fallback, encryptedLength...)\n\t}\n\tdecryptedLength := make([]byte, 2)\n\tif _, err := nfsAEAD.Open(decryptedLength[:0], nil, encryptedLength, nil); err != nil {\n\t\tc.UseAES = !c.UseAES\n\t\tnfsAEAD = NewAEAD(iv, nfsKey, c.UseAES)\n\t\tif _, err := nfsAEAD.Open(decryptedLength[:0], nil, encryptedLength, nil); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\tif fallback != nil {\n\t\t*fallback = nil\n\t}\n\tlength := DecodeLength(decryptedLength)\n\n\tif length == 32 {\n\t\tif i.SecondsFrom == 0 && i.SecondsTo == 0 {\n\t\t\treturn nil, errors.New(\"0-RTT is not allowed\")\n\t\t}\n\t\tencryptedTicket := make([]byte, 32)\n\t\tif _, err := io.ReadFull(conn, encryptedTicket); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tticket, err := nfsAEAD.Open(nil, nil, encryptedTicket, nil)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ti.RWLock.RLock()\n\t\ts := i.Sessions[[16]byte(ticket)]\n\t\ti.RWLock.RUnlock()\n\t\tif s == nil {\n\t\t\tnoises := make([]byte, randBetween(1279, 2279)) // matches 1-RTT's server hello length for \"random\", though it is not important, just for example\n\t\t\tvar err error\n\t\t\tfor err == nil {\n\t\t\t\trand.Read(noises)\n\t\t\t\t_, err = DecodeHeader(noises)\n\t\t\t}\n\t\t\tconn.Write(noises) // make client do new handshake\n\t\t\treturn nil, errors.New(\"expired ticket\")\n\t\t}\n\t\tif _, loaded := s.NfsKeys.LoadOrStore([32]byte(nfsKey), true); loaded { // prevents bad client also\n\t\t\treturn nil, errors.New(\"replay detected\")\n\t\t}\n\t\tc.UnitedKey = append(s.PfsKey, nfsKey...) // the same nfsKey links the upload & download (prevents server -> client's another request)\n\t\tc.PreWrite = make([]byte, 16)\n\t\trand.Read(c.PreWrite) // always trust yourself, not the client (also prevents being parsed as TLS thus causing false interruption for \"native\" and \"xorpub\")\n\t\tc.AEAD = NewAEAD(c.PreWrite, c.UnitedKey, c.UseAES)\n\t\tc.PeerAEAD = NewAEAD(encryptedTicket, c.UnitedKey, c.UseAES) // unchangeable ctx (prevents server -> server), and different ctx length for upload / download (prevents client -> client)\n\t\tif i.XorMode == 2 {\n\t\t\tc.Conn = NewXorConn(conn, NewCTR(c.UnitedKey, c.PreWrite), NewCTR(c.UnitedKey, iv), 16, 0) // it doesn't matter if the attacker sends client's iv back to the client\n\t\t}\n\t\treturn c, nil\n\t}\n\n\tif length < 1184+32+16 { // client may send more public keys in the future's version\n\t\treturn nil, errors.New(\"too short length\")\n\t}\n\tencryptedPfsPublicKey := make([]byte, length)\n\tif _, err := io.ReadFull(conn, encryptedPfsPublicKey); err != nil {\n\t\treturn nil, err\n\t}\n\tif _, err := nfsAEAD.Open(encryptedPfsPublicKey[:0], nil, encryptedPfsPublicKey, nil); err != nil {\n\t\treturn nil, err\n\t}\n\tmlkem768EKey, err := mlkem.NewEncapsulationKey768(encryptedPfsPublicKey[:1184])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tmlkem768Key, encapsulatedPfsKey := mlkem768EKey.Encapsulate()\n\tpeerX25519PKey, err := ecdh.X25519().NewPublicKey(encryptedPfsPublicKey[1184 : 1184+32])\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tx25519SKey, _ := ecdh.X25519().GenerateKey(rand.Reader)\n\tx25519Key, err := x25519SKey.ECDH(peerX25519PKey)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tpfsKey := make([]byte, 32+32) // no more capacity\n\tcopy(pfsKey, mlkem768Key)\n\tcopy(pfsKey[32:], x25519Key)\n\tpfsPublicKey := append(encapsulatedPfsKey, x25519SKey.PublicKey().Bytes()...)\n\tc.UnitedKey = append(pfsKey, nfsKey...)\n\tc.AEAD = NewAEAD(pfsPublicKey, c.UnitedKey, c.UseAES)\n\tc.PeerAEAD = NewAEAD(encryptedPfsPublicKey[:1184+32], c.UnitedKey, c.UseAES)\n\n\tticket := [16]byte{}\n\trand.Read(ticket[:])\n\tvar seconds int64\n\tif i.SecondsTo == 0 {\n\t\tseconds = i.SecondsFrom * randBetween(50, 100) / 100\n\t} else {\n\t\tseconds = randBetween(i.SecondsFrom, i.SecondsTo)\n\t}\n\tcopy(ticket[:], EncodeLength(int(seconds)))\n\tif seconds > 0 {\n\t\ti.RWLock.Lock()\n\t\ti.Lasts[(time.Now().Unix()+max(i.SecondsFrom, i.SecondsTo))/60+2] = ticket\n\t\ti.Tickets = append(i.Tickets, ticket)\n\t\ti.Sessions[ticket] = &ServerSession{PfsKey: pfsKey}\n\t\ti.RWLock.Unlock()\n\t}\n\n\tpfsKeyExchangeLength := 1088 + 32 + 16\n\tencryptedTicketLength := 32\n\tpaddingLength, paddingLens, paddingGaps := CreatPadding(i.PaddingLens, i.PaddingGaps)\n\tserverHello := make([]byte, pfsKeyExchangeLength+encryptedTicketLength+paddingLength)\n\tnfsAEAD.Seal(serverHello[:0], MaxNonce, pfsPublicKey, nil)\n\tc.AEAD.Seal(serverHello[:pfsKeyExchangeLength], nil, ticket[:], nil)\n\tpadding := serverHello[pfsKeyExchangeLength+encryptedTicketLength:]\n\tc.AEAD.Seal(padding[:0], nil, EncodeLength(paddingLength-18), nil)\n\tc.AEAD.Seal(padding[:18], nil, padding[18:paddingLength-16], nil)\n\n\tpaddingLens[0] = pfsKeyExchangeLength + encryptedTicketLength + paddingLens[0]\n\tfor i, l := range paddingLens { // sends padding in a fragmented way, to create variable traffic pattern, before inner VLESS flow takes control\n\t\tif l > 0 {\n\t\t\tif _, err := conn.Write(serverHello[:l]); err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tserverHello = serverHello[l:]\n\t\t}\n\t\tif len(paddingGaps) > i {\n\t\t\ttime.Sleep(paddingGaps[i])\n\t\t}\n\t}\n\n\t// important: allows client sends padding slowly, eliminating 1-RTT's traffic pattern\n\tif _, err := io.ReadFull(conn, encryptedLength); err != nil {\n\t\treturn nil, err\n\t}\n\tif _, err := nfsAEAD.Open(encryptedLength[:0], nil, encryptedLength, nil); err != nil {\n\t\treturn nil, err\n\t}\n\tencryptedPadding := make([]byte, DecodeLength(encryptedLength[:2]))\n\tif _, err := io.ReadFull(conn, encryptedPadding); err != nil {\n\t\treturn nil, err\n\t}\n\tif _, err := nfsAEAD.Open(encryptedPadding[:0], nil, encryptedPadding, nil); err != nil {\n\t\treturn nil, err\n\t}\n\n\tif i.XorMode == 2 {\n\t\tc.Conn = NewXorConn(conn, NewCTR(c.UnitedKey, ticket[:]), NewCTR(c.UnitedKey, iv), 0, 0)\n\t}\n\treturn c, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vless/encryption/xor.go",
    "content": "package encryption\n\nimport (\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\t\"net\"\n\n\t\"github.com/metacubex/blake3\"\n)\n\nfunc NewCTR(key, iv []byte) cipher.Stream {\n\tk := make([]byte, 32)\n\tblake3.DeriveKey(k, \"VLESS\", key) // avoids using key directly\n\tblock, _ := aes.NewCipher(k)\n\treturn cipher.NewCTR(block, iv)\n\t//chacha20.NewUnauthenticatedCipher()\n}\n\ntype XorConn struct {\n\tnet.Conn\n\tCTR       cipher.Stream\n\tPeerCTR   cipher.Stream\n\tOutSkip   int\n\tOutHeader []byte\n\tInSkip    int\n\tInHeader  []byte\n}\n\nfunc NewXorConn(conn net.Conn, ctr, peerCTR cipher.Stream, outSkip, inSkip int) *XorConn {\n\treturn &XorConn{\n\t\tConn:      conn,\n\t\tCTR:       ctr,\n\t\tPeerCTR:   peerCTR,\n\t\tOutSkip:   outSkip,\n\t\tOutHeader: make([]byte, 0, 5), // important\n\t\tInSkip:    inSkip,\n\t\tInHeader:  make([]byte, 0, 5), // important\n\t}\n}\n\nfunc (c *XorConn) Write(b []byte) (int, error) {\n\tif len(b) == 0 {\n\t\treturn 0, nil\n\t}\n\tfor p := b; ; {\n\t\tif len(p) <= c.OutSkip {\n\t\t\tc.OutSkip -= len(p)\n\t\t\tbreak\n\t\t}\n\t\tp = p[c.OutSkip:]\n\t\tc.OutSkip = 0\n\t\tneed := 5 - len(c.OutHeader)\n\t\tif len(p) < need {\n\t\t\tc.OutHeader = append(c.OutHeader, p...)\n\t\t\tc.CTR.XORKeyStream(p, p)\n\t\t\tbreak\n\t\t}\n\t\tc.OutSkip, _ = DecodeHeader(append(c.OutHeader, p[:need]...))\n\t\tc.OutHeader = c.OutHeader[:0]\n\t\tc.CTR.XORKeyStream(p[:need], p[:need])\n\t\tp = p[need:]\n\t}\n\tif _, err := c.Conn.Write(b); err != nil {\n\t\treturn 0, err\n\t}\n\treturn len(b), nil\n}\n\nfunc (c *XorConn) Read(b []byte) (int, error) {\n\tif len(b) == 0 {\n\t\treturn 0, nil\n\t}\n\tn, err := c.Conn.Read(b)\n\tfor p := b[:n]; ; {\n\t\tif len(p) <= c.InSkip {\n\t\t\tc.InSkip -= len(p)\n\t\t\tbreak\n\t\t}\n\t\tp = p[c.InSkip:]\n\t\tc.InSkip = 0\n\t\tneed := 5 - len(c.InHeader)\n\t\tif len(p) < need {\n\t\t\tc.PeerCTR.XORKeyStream(p, p)\n\t\t\tc.InHeader = append(c.InHeader, p...)\n\t\t\tbreak\n\t\t}\n\t\tc.PeerCTR.XORKeyStream(p[:need], p[:need])\n\t\tc.InSkip, _ = DecodeHeader(append(c.InHeader, p[:need]...))\n\t\tc.InHeader = c.InHeader[:0]\n\t\tp = p[need:]\n\t}\n\treturn n, err\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vless/packet.go",
    "content": "package vless\n\nimport (\n\t\"encoding/binary\"\n\t\"io\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/common/pool\"\n)\n\ntype PacketConn struct {\n\tnet.Conn\n\trAddr net.Addr\n}\n\nfunc (c *PacketConn) WriteTo(b []byte, addr net.Addr) (int, error) {\n\terr := binary.Write(c.Conn, binary.BigEndian, uint16(len(b)))\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\treturn c.Conn.Write(b)\n}\n\nfunc (c *PacketConn) ReadFrom(b []byte) (int, net.Addr, error) {\n\tvar length uint16\n\terr := binary.Read(c.Conn, binary.BigEndian, &length)\n\tif err != nil {\n\t\treturn 0, nil, err\n\t}\n\tif len(b) < int(length) {\n\t\treturn 0, nil, io.ErrShortBuffer\n\t}\n\tn, err := io.ReadFull(c.Conn, b[:length])\n\treturn n, c.rAddr, err\n}\n\nfunc (c *PacketConn) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {\n\tvar length uint16\n\terr = binary.Read(c.Conn, binary.BigEndian, &length)\n\tif err != nil {\n\t\treturn\n\t}\n\treadBuf := pool.Get(int(length))\n\tput = func() {\n\t\t_ = pool.Put(readBuf)\n\t}\n\tn, err := io.ReadFull(c.Conn, readBuf)\n\tif err != nil {\n\t\tput()\n\t\tput = nil\n\t\treturn\n\t}\n\tdata = readBuf[:n]\n\taddr = c.rAddr\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vless/vision/conn.go",
    "content": "package vision\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"unsafe\"\n\n\t\"github.com/metacubex/mihomo/common/buf\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/gofrs/uuid/v5\"\n)\n\nvar (\n\t_ N.ExtendedConn = (*Conn)(nil)\n)\n\ntype Conn struct {\n\tnet.Conn // should be *vless.Conn\n\tN.ExtendedReader\n\tN.ExtendedWriter\n\tuserUUID uuid.UUID\n\n\t// [*tls.Conn] or other tls-like [net.Conn]'s internal variables\n\tnetConn  net.Conn      // tlsConn.NetConn()\n\tinput    *bytes.Reader // &tlsConn.input or nil\n\trawInput *bytes.Buffer // &tlsConn.rawInput or nil\n\n\tpacketsToFilter            int\n\tisTLS                      bool\n\tisTLS12orAbove             bool\n\tenableXTLS                 bool\n\tcipher                     uint16\n\tremainingServerHello       uint16\n\treadRemainingBuffer        *buf.Buffer\n\treadRemainingContent       int\n\treadRemainingPadding       int\n\treadProcess                bool\n\treadFilterUUID             bool\n\treadLastCommand            byte\n\twriteFilterApplicationData bool\n\twriteDirect                bool\n\twriteOnceUserUUID          []byte\n}\n\nfunc (vc *Conn) Read(b []byte) (int, error) {\n\tif vc.readProcess {\n\t\tbuffer := buf.With(b)\n\t\terr := vc.ReadBuffer(buffer)\n\t\tif unsafe.SliceData(buffer.Bytes()) != unsafe.SliceData(b) { // buffer.Bytes() not at the beginning of b\n\t\t\tcopy(b, buffer.Bytes())\n\t\t}\n\t\treturn buffer.Len(), err\n\t}\n\treturn vc.ExtendedReader.Read(b)\n}\n\nfunc (vc *Conn) ReadBuffer(buffer *buf.Buffer) error {\n\tif vc.readRemainingBuffer != nil {\n\t\t_, err := buffer.ReadOnceFrom(vc.readRemainingBuffer)\n\t\tif vc.readRemainingBuffer.IsEmpty() {\n\t\t\tvc.readRemainingBuffer.Release()\n\t\t\tvc.readRemainingBuffer = nil\n\t\t}\n\t\treturn err\n\t}\n\tif vc.readRemainingContent > 0 {\n\t\treadSize := xrayBufSize          // at least read xrayBufSize\n\t\tif buffer.FreeLen() > readSize { // input buffer larger than xrayBufSize, read as much as possible\n\t\t\treadSize = buffer.FreeLen()\n\t\t}\n\t\tif readSize > vc.readRemainingContent { // don't read out of bounds\n\t\t\treadSize = vc.readRemainingContent\n\t\t}\n\n\t\treadBuffer := buffer\n\t\tif buffer.FreeLen() < readSize {\n\t\t\treadBuffer = buf.NewSize(readSize)\n\t\t\tvc.readRemainingBuffer = readBuffer\n\t\t}\n\t\tn, err := vc.ExtendedReader.Read(readBuffer.FreeBytes()[:readSize])\n\t\treadBuffer.Truncate(n)\n\t\tvc.readRemainingContent -= n\n\t\tvc.FilterTLS(readBuffer.Bytes())\n\t\tif vc.readRemainingBuffer != nil {\n\t\t\tinnerErr := vc.ReadBuffer(buffer) // back to top but not losing err\n\t\t\tif err != nil {\n\t\t\t\terr = innerErr\n\t\t\t}\n\t\t}\n\t\treturn err\n\t}\n\tif vc.readRemainingPadding > 0 {\n\t\tn, err := io.CopyN(io.Discard, vc.ExtendedReader, int64(vc.readRemainingPadding))\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tvc.readRemainingPadding -= int(n)\n\t}\n\tif vc.readProcess {\n\t\tswitch vc.readLastCommand {\n\t\tcase commandPaddingContinue:\n\t\t\t//if vc.isTLS || vc.packetsToFilter > 0 {\n\t\t\tneed := PaddingHeaderLen\n\t\t\tif !vc.readFilterUUID {\n\t\t\t\tneed = PaddingHeaderLen - uuid.Size\n\t\t\t}\n\t\t\tvar header []byte\n\t\t\tif buffer.FreeLen() < need {\n\t\t\t\theader = make([]byte, need)\n\t\t\t} else {\n\t\t\t\theader = buffer.FreeBytes()[:need]\n\t\t\t}\n\t\t\t_, err := io.ReadFull(vc.ExtendedReader, header)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif vc.readFilterUUID {\n\t\t\t\tvc.readFilterUUID = false\n\t\t\t\tif !bytes.Equal(vc.userUUID.Bytes(), header[:uuid.Size]) {\n\t\t\t\t\terr = fmt.Errorf(\"XTLS Vision server responded unknown UUID: %s\", uuid.FromBytesOrNil(header[:uuid.Size]))\n\t\t\t\t\tlog.Errorln(err.Error())\n\t\t\t\t\treturn err\n\t\t\t\t}\n\t\t\t\theader = header[uuid.Size:]\n\t\t\t}\n\t\t\tvc.readRemainingPadding = int(binary.BigEndian.Uint16(header[3:]))\n\t\t\tvc.readRemainingContent = int(binary.BigEndian.Uint16(header[1:]))\n\t\t\tvc.readLastCommand = header[0]\n\t\t\tlog.Debugln(\"XTLS Vision read padding: command=%d, payloadLen=%d, paddingLen=%d\",\n\t\t\t\tvc.readLastCommand, vc.readRemainingContent, vc.readRemainingPadding)\n\t\t\treturn vc.ReadBuffer(buffer)\n\t\t\t//}\n\t\tcase commandPaddingEnd:\n\t\t\tvc.readProcess = false\n\t\t\treturn vc.ReadBuffer(buffer)\n\t\tcase commandPaddingDirect:\n\t\t\tneedReturn := false\n\t\t\tif vc.input != nil {\n\t\t\t\t_, err := buffer.ReadOnceFrom(vc.input)\n\t\t\t\tif err != nil {\n\t\t\t\t\tif !errors.Is(err, io.EOF) {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif vc.input.Len() == 0 {\n\t\t\t\t\tneedReturn = true\n\t\t\t\t\t*vc.input = bytes.Reader{} // full reset\n\t\t\t\t\tvc.input = nil\n\t\t\t\t} else { // buffer is full\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t}\n\t\t\tif vc.rawInput != nil {\n\t\t\t\t_, err := buffer.ReadOnceFrom(vc.rawInput)\n\t\t\t\tif err != nil {\n\t\t\t\t\tif !errors.Is(err, io.EOF) {\n\t\t\t\t\t\treturn err\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tneedReturn = true\n\t\t\t\tif vc.rawInput.Len() == 0 {\n\t\t\t\t\t*vc.rawInput = bytes.Buffer{} // full reset\n\t\t\t\t\tvc.rawInput = nil\n\t\t\t\t}\n\t\t\t}\n\t\t\tif vc.input == nil && vc.rawInput == nil {\n\t\t\t\tvc.readProcess = false\n\t\t\t\tvc.ExtendedReader = N.NewExtendedReader(vc.netConn)\n\t\t\t\tlog.Debugln(\"XTLS Vision direct read start\")\n\t\t\t}\n\t\t\tif needReturn {\n\t\t\t\treturn nil\n\t\t\t}\n\t\tdefault:\n\t\t\terr := fmt.Errorf(\"XTLS Vision read unknown command: %d\", vc.readLastCommand)\n\t\t\tlog.Debugln(err.Error())\n\t\t\treturn err\n\t\t}\n\t}\n\treturn vc.ExtendedReader.ReadBuffer(buffer)\n}\n\nfunc (vc *Conn) Write(p []byte) (int, error) {\n\tif vc.writeFilterApplicationData {\n\t\treturn N.WriteBuffer(vc, buf.As(p))\n\t}\n\treturn vc.ExtendedWriter.Write(p)\n}\n\nfunc (vc *Conn) WriteBuffer(buffer *buf.Buffer) (err error) {\n\tif vc.writeFilterApplicationData {\n\t\tif buffer.IsEmpty() {\n\t\t\tApplyPadding(buffer, commandPaddingContinue, &vc.writeOnceUserUUID, true) // we do a long padding to hide vless header\n\t\t\treturn vc.ExtendedWriter.WriteBuffer(buffer)\n\t\t}\n\n\t\tvc.FilterTLS(buffer.Bytes())\n\t\tbuffers := vc.ReshapeBuffer(buffer)\n\t\tapplyPadding := true\n\t\tfor i, buffer := range buffers {\n\t\t\tcommand := commandPaddingContinue\n\t\t\tif applyPadding {\n\t\t\t\tif vc.isTLS && buffer.Len() > 6 && bytes.Equal(tlsApplicationDataStart, buffer.To(3)) {\n\t\t\t\t\tcommand = commandPaddingEnd\n\t\t\t\t\tif vc.enableXTLS {\n\t\t\t\t\t\tcommand = commandPaddingDirect\n\t\t\t\t\t\tvc.writeDirect = true\n\t\t\t\t\t}\n\t\t\t\t\tvc.writeFilterApplicationData = false\n\t\t\t\t\tapplyPadding = false\n\t\t\t\t} else if !vc.isTLS12orAbove && vc.packetsToFilter <= 1 {\n\t\t\t\t\tcommand = commandPaddingEnd\n\t\t\t\t\tvc.writeFilterApplicationData = false\n\t\t\t\t\tapplyPadding = false\n\t\t\t\t}\n\t\t\t\tApplyPadding(buffer, command, &vc.writeOnceUserUUID, vc.isTLS)\n\t\t\t}\n\n\t\t\terr = vc.ExtendedWriter.WriteBuffer(buffer)\n\t\t\tif err != nil {\n\t\t\t\tbuf.ReleaseMulti(buffers[i:]) // release unwritten buffers\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif command == commandPaddingDirect {\n\t\t\t\tvc.ExtendedWriter = N.NewExtendedWriter(vc.netConn)\n\t\t\t\tlog.Debugln(\"XTLS Vision direct write start\")\n\t\t\t\t//time.Sleep(5 * time.Millisecond)\n\t\t\t}\n\t\t}\n\t\treturn err\n\t}\n\t/*if vc.writeDirect {\n\t\tlog.Debugln(\"XTLS Vision Direct write, payloadLen=%d\", buffer.Len())\n\t}*/\n\treturn vc.ExtendedWriter.WriteBuffer(buffer)\n}\n\nfunc (vc *Conn) FrontHeadroom() int {\n\tfontHeadroom := PaddingHeaderLen - uuid.Size\n\tif vc.readFilterUUID || vc.writeOnceUserUUID != nil {\n\t\tfontHeadroom = PaddingHeaderLen\n\t}\n\tif vc.writeFilterApplicationData { // The writer may be replaced, add the required value for vc.netConn\n\t\tif abs := N.CalculateFrontHeadroom(vc.netConn) - N.CalculateFrontHeadroom(vc.Conn); abs > 0 {\n\t\t\tfontHeadroom += abs\n\t\t}\n\t}\n\treturn fontHeadroom\n}\n\nfunc (vc *Conn) RearHeadroom() int {\n\trearHeadroom := 500 + 900\n\tif vc.writeFilterApplicationData { // The writer may be replaced, add the required value for vc.netConn\n\t\tif abs := N.CalculateRearHeadroom(vc.netConn) - N.CalculateRearHeadroom(vc.Conn); abs > 0 {\n\t\t\trearHeadroom += abs\n\t\t}\n\t}\n\treturn rearHeadroom\n}\n\nfunc (vc *Conn) NeedHandshake() bool {\n\treturn vc.writeOnceUserUUID != nil\n}\n\nfunc (vc *Conn) NeedAdditionalReadDeadline() bool {\n\treturn true\n}\n\nfunc (vc *Conn) Upstream() any {\n\tif vc.writeDirect ||\n\t\tvc.readLastCommand == commandPaddingDirect {\n\t\treturn vc.netConn\n\t}\n\treturn vc.Conn\n}\n\nfunc (vc *Conn) ReaderPossiblyReplaceable() bool {\n\treturn vc.readProcess\n}\n\nfunc (vc *Conn) ReaderReplaceable() bool {\n\tif !vc.readProcess &&\n\t\tvc.readLastCommand == commandPaddingDirect {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (vc *Conn) WriterPossiblyReplaceable() bool {\n\treturn vc.writeFilterApplicationData\n}\n\nfunc (vc *Conn) WriterReplaceable() bool {\n\tif vc.writeDirect {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc (vc *Conn) Close() error {\n\tif vc.ReaderReplaceable() || vc.WriterReplaceable() { // ignore send closeNotify alert in tls.Conn\n\t\treturn vc.netConn.Close()\n\t}\n\treturn vc.Conn.Close()\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vless/vision/filter.go",
    "content": "package vision\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\n\t\"github.com/metacubex/mihomo/log\"\n)\n\nvar (\n\ttls13SupportedVersions  = []byte{0x00, 0x2b, 0x00, 0x02, 0x03, 0x04}\n\ttlsClientHandshakeStart = []byte{0x16, 0x03}\n\ttlsServerHandshakeStart = []byte{0x16, 0x03, 0x03}\n\ttlsApplicationDataStart = []byte{0x17, 0x03, 0x03}\n\n\ttls13CipherSuiteMap = map[uint16]string{\n\t\t0x1301: \"TLS_AES_128_GCM_SHA256\",\n\t\t0x1302: \"TLS_AES_256_GCM_SHA384\",\n\t\t0x1303: \"TLS_CHACHA20_POLY1305_SHA256\",\n\t\t0x1304: \"TLS_AES_128_CCM_SHA256\",\n\t\t0x1305: \"TLS_AES_128_CCM_8_SHA256\",\n\t}\n)\n\nconst (\n\ttlsHandshakeTypeClientHello byte = 0x01\n\ttlsHandshakeTypeServerHello byte = 0x02\n)\n\nfunc (vc *Conn) FilterTLS(buffer []byte) (index int) {\n\tif vc.packetsToFilter <= 0 {\n\t\treturn 0\n\t}\n\tlenP := len(buffer)\n\tvc.packetsToFilter--\n\tif index = bytes.Index(buffer, tlsServerHandshakeStart); index != -1 {\n\t\tif lenP > index+5 {\n\t\t\tif buffer[0] == 22 && buffer[1] == 3 && buffer[2] == 3 {\n\t\t\t\tvc.isTLS = true\n\t\t\t\tif buffer[5] == tlsHandshakeTypeServerHello {\n\t\t\t\t\t//log.Debugln(\"isTLS12orAbove\")\n\t\t\t\t\tvc.remainingServerHello = binary.BigEndian.Uint16(buffer[index+3:]) + 5\n\t\t\t\t\tvc.isTLS12orAbove = true\n\t\t\t\t\tif lenP-index >= 79 && vc.remainingServerHello >= 79 {\n\t\t\t\t\t\tsessionIDLen := int(buffer[index+43])\n\t\t\t\t\t\tvc.cipher = binary.BigEndian.Uint16(buffer[index+43+sessionIDLen+1:])\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t} else if index = bytes.Index(buffer, tlsClientHandshakeStart); index != -1 {\n\t\tif lenP > index+5 && buffer[index+5] == tlsHandshakeTypeClientHello {\n\t\t\tvc.isTLS = true\n\t\t}\n\t}\n\n\tif vc.remainingServerHello > 0 {\n\t\tend := int(vc.remainingServerHello)\n\t\ti := index\n\t\tif i < 0 {\n\t\t\ti = 0\n\t\t}\n\t\tif i+end > lenP {\n\t\t\tend = lenP\n\t\t\tvc.remainingServerHello -= uint16(end - i)\n\t\t} else {\n\t\t\tvc.remainingServerHello -= uint16(end)\n\t\t\tend += i\n\t\t}\n\t\tif bytes.Contains(buffer[i:end], tls13SupportedVersions) {\n\t\t\t// TLS 1.3 Client Hello\n\t\t\tcs, ok := tls13CipherSuiteMap[vc.cipher]\n\t\t\tif ok && cs != \"TLS_AES_128_CCM_8_SHA256\" {\n\t\t\t\tvc.enableXTLS = true\n\t\t\t}\n\t\t\tlog.Debugln(\"XTLS Vision found TLS 1.3, packetLength=%d， CipherSuite=%s\", lenP, cs)\n\t\t\tvc.packetsToFilter = 0\n\t\t\treturn\n\t\t} else if vc.remainingServerHello <= 0 {\n\t\t\tlog.Debugln(\"XTLS Vision found TLS 1.2, packetLength=%d\", lenP)\n\t\t\tvc.packetsToFilter = 0\n\t\t\treturn\n\t\t}\n\t\tlog.Debugln(\"XTLS Vision found inconclusive server hello, packetLength=%d, remainingServerHelloBytes=%d\", lenP, vc.remainingServerHello)\n\t}\n\tif vc.packetsToFilter <= 0 {\n\t\tlog.Debugln(\"XTLS Vision stop filtering\")\n\t}\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vless/vision/padding.go",
    "content": "package vision\n\nimport (\n\t\"bytes\"\n\t\"encoding/binary\"\n\n\t\"github.com/metacubex/mihomo/common/buf\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/gofrs/uuid/v5\"\n\t\"github.com/metacubex/randv2\"\n)\n\nconst (\n\tPaddingHeaderLen = uuid.Size + 1 + 2 + 2 // =21\n\n\tcommandPaddingContinue byte = 0x00\n\tcommandPaddingEnd      byte = 0x01\n\tcommandPaddingDirect   byte = 0x02\n)\n\nfunc ApplyPadding(buffer *buf.Buffer, command byte, userUUID *[]byte, paddingTLS bool) {\n\tcontentLen := int32(buffer.Len())\n\tvar paddingLen int32\n\tif contentLen < 900 {\n\t\tif paddingTLS {\n\t\t\t//log.Debugln(\"long padding\")\n\t\t\tpaddingLen = randv2.Int32N(500) + 900 - contentLen\n\t\t} else {\n\t\t\tpaddingLen = randv2.Int32N(256)\n\t\t}\n\t}\n\n\tbinary.BigEndian.PutUint16(buffer.ExtendHeader(2), uint16(paddingLen))\n\tbinary.BigEndian.PutUint16(buffer.ExtendHeader(2), uint16(contentLen))\n\tbuffer.ExtendHeader(1)[0] = command\n\tif userUUID != nil && *userUUID != nil {\n\t\tcopy(buffer.ExtendHeader(uuid.Size), *userUUID)\n\t\t*userUUID = nil\n\t}\n\n\tbuffer.Extend(int(paddingLen))\n\tlog.Debugln(\"XTLS Vision write padding: command=%d, payloadLen=%d, paddingLen=%d\", command, contentLen, paddingLen)\n}\n\nconst xrayBufSize = 8192\n\nfunc (vc *Conn) ReshapeBuffer(buffer *buf.Buffer) []*buf.Buffer {\n\tconst bufferLimit = xrayBufSize - PaddingHeaderLen\n\tif buffer.Len() < bufferLimit {\n\t\treturn []*buf.Buffer{buffer}\n\t}\n\toptions := N.NewReadWaitOptions(nil, vc)\n\tvar buffers []*buf.Buffer\n\tfor buffer.Len() >= bufferLimit {\n\t\tcutAt := bytes.LastIndex(buffer.Bytes(), tlsApplicationDataStart)\n\t\tif cutAt < 21 || cutAt > bufferLimit {\n\t\t\tcutAt = xrayBufSize / 2\n\t\t}\n\t\tbuffer2 := options.NewBuffer() // ensure the new buffer can send used in vc.WriteBuffer\n\t\tbuf.Must(buf.Error(buffer2.ReadFullFrom(buffer, cutAt)))\n\t\tbuffers = append(buffers, buffer2)\n\t}\n\tbuffers = append(buffers, buffer)\n\treturn buffers\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vless/vision/vision.go",
    "content": "// Package vision implements VLESS flow `xtls-rprx-vision` introduced by Xray-core.\n//\n// same logic as https://github.com/XTLS/Xray-core/blob/v25.9.11/proxy/proxy.go\npackage vision\n\nimport (\n\t\"bytes\"\n\tgotls \"crypto/tls\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"reflect\"\n\t\"unsafe\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\ttlsC \"github.com/metacubex/mihomo/component/tls\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/transport/vless/encryption\"\n\n\t\"github.com/gofrs/uuid/v5\"\n\t\"github.com/metacubex/tls\"\n)\n\nvar ErrNotHandshakeComplete = errors.New(\"tls connection not handshake complete\")\nvar ErrNotTLS13 = errors.New(\"XTLS Vision based on TLS 1.3 outer connection\")\n\nfunc NewConn(conn net.Conn, tlsConn net.Conn, userUUID uuid.UUID) (*Conn, error) {\n\tc := &Conn{\n\t\tExtendedReader:             N.NewExtendedReader(conn),\n\t\tExtendedWriter:             N.NewExtendedWriter(conn),\n\t\tConn:                       conn,\n\t\tuserUUID:                   userUUID,\n\t\tpacketsToFilter:            8,\n\t\treadProcess:                true,\n\t\treadFilterUUID:             true,\n\t\twriteFilterApplicationData: true,\n\t\twriteOnceUserUUID:          userUUID.Bytes(),\n\t}\n\tvar t reflect.Type\n\tvar p unsafe.Pointer\n\tvar upstream any = tlsConn\n\tfor {\n\t\tswitch underlying := upstream.(type) {\n\t\tcase *gotls.Conn:\n\t\t\t//log.Debugln(\"type tls\")\n\t\t\ttlsConn = underlying\n\t\t\tc.netConn = underlying.NetConn()\n\t\t\tt = reflect.TypeOf(underlying).Elem()\n\t\t\tp = unsafe.Pointer(underlying)\n\t\t\tbreak\n\t\tcase *tls.Conn:\n\t\t\t//log.Debugln(\"type tls\")\n\t\t\ttlsConn = underlying\n\t\t\tc.netConn = underlying.NetConn()\n\t\t\tt = reflect.TypeOf(underlying).Elem()\n\t\t\tp = unsafe.Pointer(underlying)\n\t\t\tbreak\n\t\tcase *tlsC.Conn:\n\t\t\t//log.Debugln(\"type *tlsC.Conn\")\n\t\t\ttlsConn = underlying\n\t\t\tc.netConn = underlying.NetConn()\n\t\t\tt = reflect.TypeOf(underlying).Elem()\n\t\t\tp = unsafe.Pointer(underlying)\n\t\t\tbreak\n\t\tcase *tlsC.UConn:\n\t\t\t//log.Debugln(\"type *tlsC.UConn\")\n\t\t\ttlsConn = underlying\n\t\t\tc.netConn = underlying.NetConn()\n\t\t\tt = reflect.TypeOf(underlying.Conn).Elem()\n\t\t\t//log.Debugln(\"t:%v\", t)\n\t\t\tp = unsafe.Pointer(underlying.Conn)\n\t\t\tbreak\n\t\tcase *encryption.CommonConn:\n\t\t\t//log.Debugln(\"type *encryption.CommonConn\")\n\t\t\ttlsConn = underlying\n\t\t\tc.netConn = underlying.Conn\n\t\t\tt = reflect.TypeOf(underlying).Elem()\n\t\t\tp = unsafe.Pointer(underlying)\n\t\t\tbreak\n\t\t}\n\t\tif u, ok := upstream.(N.ReaderWithUpstream); !ok || !u.ReaderReplaceable() { // must replaceable\n\t\t\tbreak\n\t\t}\n\t\tif u, ok := upstream.(N.WithUpstreamReader); ok {\n\t\t\tupstream = u.UpstreamReader()\n\t\t\tcontinue\n\t\t}\n\t\tif u, ok := upstream.(N.WithUpstream); ok {\n\t\t\tupstream = u.Upstream()\n\t\t\tcontinue\n\t\t}\n\t}\n\tif t == nil || p == nil {\n\t\tlog.Warnln(\"vision: not a valid supported TLS connection: %s\", reflect.TypeOf(tlsConn))\n\t\treturn nil, fmt.Errorf(`failed to use vision, maybe \"tls\" is not enable and \"encryption\" is empty`)\n\t}\n\n\tif err := checkTLSVersion(tlsConn); err != nil {\n\t\tif errors.Is(err, ErrNotHandshakeComplete) {\n\t\t\tlog.Warnln(\"vision: TLS connection not handshake complete: %s\", reflect.TypeOf(tlsConn))\n\t\t} else {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\tif i, ok := t.FieldByName(\"input\"); ok {\n\t\tc.input = (*bytes.Reader)(unsafe.Add(p, i.Offset))\n\t}\n\tif r, ok := t.FieldByName(\"rawInput\"); ok {\n\t\tc.rawInput = (*bytes.Buffer)(unsafe.Add(p, r.Offset))\n\t}\n\treturn c, nil\n}\n\nfunc checkTLSVersion(tlsConn net.Conn) error {\n\tswitch underlying := tlsConn.(type) {\n\tcase *gotls.Conn:\n\t\tstate := underlying.ConnectionState()\n\t\tif !state.HandshakeComplete {\n\t\t\treturn ErrNotHandshakeComplete\n\t\t}\n\t\tif state.Version != gotls.VersionTLS13 {\n\t\t\treturn ErrNotTLS13\n\t\t}\n\tcase *tls.Conn:\n\t\tstate := underlying.ConnectionState()\n\t\tif !state.HandshakeComplete {\n\t\t\treturn ErrNotHandshakeComplete\n\t\t}\n\t\tif state.Version != tls.VersionTLS13 {\n\t\t\treturn ErrNotTLS13\n\t\t}\n\tcase *tlsC.Conn:\n\t\tstate := underlying.ConnectionState()\n\t\tif !state.HandshakeComplete {\n\t\t\treturn ErrNotHandshakeComplete\n\t\t}\n\t\tif state.Version != tlsC.VersionTLS13 {\n\t\t\treturn ErrNotTLS13\n\t\t}\n\tcase *tlsC.UConn:\n\t\tstate := underlying.ConnectionState()\n\t\tif !state.HandshakeComplete {\n\t\t\treturn ErrNotHandshakeComplete\n\t\t}\n\t\tif state.Version != tlsC.VersionTLS13 {\n\t\t\treturn ErrNotTLS13\n\t\t}\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vless/vless.go",
    "content": "package vless\n\nimport (\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/common/utils\"\n\n\t\"github.com/gofrs/uuid/v5\"\n)\n\nconst (\n\tXRO = \"xtls-rprx-origin\"\n\tXRD = \"xtls-rprx-direct\"\n\tXRS = \"xtls-rprx-splice\"\n\tXRV = \"xtls-rprx-vision\"\n\n\tVersion byte = 0 // protocol version. preview version is 0\n)\n\n// Command types\nconst (\n\tCommandTCP byte = 1\n\tCommandUDP byte = 2\n\tCommandMux byte = 3\n)\n\n// Addr types\nconst (\n\tAtypIPv4       byte = 1\n\tAtypDomainName byte = 2\n\tAtypIPv6       byte = 3\n)\n\n// DstAddr store destination address\ntype DstAddr struct {\n\tUDP      bool\n\tAddrType byte\n\tAddr     []byte\n\tPort     uint16\n\tMux      bool // currently used for XUDP only\n}\n\n// Client is vless connection generator\ntype Client struct {\n\tuuid   uuid.UUID\n\tAddons *Addons\n}\n\n// StreamConn return a Conn with net.Conn and DstAddr\nfunc (c *Client) StreamConn(conn net.Conn, dst *DstAddr) (net.Conn, error) {\n\treturn newConn(conn, c, dst)\n}\n\nfunc (c *Client) PacketConn(conn net.Conn, rAddr net.Addr) net.PacketConn {\n\treturn &PacketConn{conn, rAddr}\n}\n\n// NewClient return Client instance\nfunc NewClient(uuidStr string, addons *Addons) (*Client, error) {\n\tuid := utils.UUIDMap(uuidStr)\n\n\treturn &Client{\n\t\tuuid:   uid,\n\t\tAddons: addons,\n\t}, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vmess/aead.go",
    "content": "package vmess\n\nimport (\n\t\"crypto/cipher\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"io\"\n\t\"sync\"\n\n\t\"github.com/metacubex/mihomo/common/pool\"\n)\n\ntype aeadWriter struct {\n\tio.Writer\n\tcipher.AEAD\n\tnonce [32]byte\n\tcount uint16\n\tiv    []byte\n\n\twriteLock sync.Mutex\n}\n\nfunc newAEADWriter(w io.Writer, aead cipher.AEAD, iv []byte) *aeadWriter {\n\treturn &aeadWriter{Writer: w, AEAD: aead, iv: iv}\n}\n\nfunc (w *aeadWriter) Write(b []byte) (n int, err error) {\n\tw.writeLock.Lock()\n\tbuf := pool.Get(pool.RelayBufferSize)\n\tdefer func() {\n\t\tw.writeLock.Unlock()\n\t\tpool.Put(buf)\n\t}()\n\tlength := len(b)\n\tfor {\n\t\tif length == 0 {\n\t\t\tbreak\n\t\t}\n\t\treadLen := chunkSize - w.Overhead()\n\t\tif length < readLen {\n\t\t\treadLen = length\n\t\t}\n\t\tpayloadBuf := buf[lenSize : lenSize+chunkSize-w.Overhead()]\n\t\tcopy(payloadBuf, b[n:n+readLen])\n\n\t\tbinary.BigEndian.PutUint16(buf[:lenSize], uint16(readLen+w.Overhead()))\n\t\tbinary.BigEndian.PutUint16(w.nonce[:2], w.count)\n\t\tcopy(w.nonce[2:], w.iv[2:12])\n\n\t\tw.Seal(payloadBuf[:0], w.nonce[:w.NonceSize()], payloadBuf[:readLen], nil)\n\t\tw.count++\n\n\t\t_, err = w.Writer.Write(buf[:lenSize+readLen+w.Overhead()])\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tn += readLen\n\t\tlength -= readLen\n\t}\n\treturn\n}\n\ntype aeadReader struct {\n\tio.Reader\n\tcipher.AEAD\n\tnonce   [32]byte\n\tbuf     []byte\n\toffset  int\n\tiv      []byte\n\tsizeBuf []byte\n\tcount   uint16\n}\n\nfunc newAEADReader(r io.Reader, aead cipher.AEAD, iv []byte) *aeadReader {\n\treturn &aeadReader{Reader: r, AEAD: aead, iv: iv, sizeBuf: make([]byte, lenSize)}\n}\n\nfunc (r *aeadReader) Read(b []byte) (int, error) {\n\tif r.buf != nil {\n\t\tn := copy(b, r.buf[r.offset:])\n\t\tr.offset += n\n\t\tif r.offset == len(r.buf) {\n\t\t\tpool.Put(r.buf)\n\t\t\tr.buf = nil\n\t\t}\n\t\treturn n, nil\n\t}\n\n\t_, err := io.ReadFull(r.Reader, r.sizeBuf)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tsize := int(binary.BigEndian.Uint16(r.sizeBuf))\n\tif size > maxSize {\n\t\treturn 0, errors.New(\"buffer is larger than standard\")\n\t}\n\n\tbuf := pool.Get(size)\n\t_, err = io.ReadFull(r.Reader, buf[:size])\n\tif err != nil {\n\t\tpool.Put(buf)\n\t\treturn 0, err\n\t}\n\n\tbinary.BigEndian.PutUint16(r.nonce[:2], r.count)\n\tcopy(r.nonce[2:], r.iv[2:12])\n\n\t_, err = r.Open(buf[:0], r.nonce[:r.NonceSize()], buf[:size], nil)\n\tr.count++\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\trealLen := size - r.Overhead()\n\tn := copy(b, buf[:realLen])\n\tif len(b) >= realLen {\n\t\tpool.Put(buf)\n\t\treturn n, nil\n\t}\n\n\tr.offset = n\n\tr.buf = buf[:realLen]\n\treturn n, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vmess/chunk.go",
    "content": "package vmess\n\nimport (\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"io\"\n\n\t\"github.com/metacubex/mihomo/common/pool\"\n)\n\nconst (\n\tlenSize   = 2\n\tchunkSize = 1 << 14   // 2 ** 14 == 16 * 1024\n\tmaxSize   = 17 * 1024 // 2 + chunkSize + aead.Overhead()\n)\n\ntype chunkReader struct {\n\tio.Reader\n\tbuf     []byte\n\tsizeBuf []byte\n\toffset  int\n}\n\nfunc newChunkReader(reader io.Reader) *chunkReader {\n\treturn &chunkReader{Reader: reader, sizeBuf: make([]byte, lenSize)}\n}\n\nfunc newChunkWriter(writer io.WriteCloser) *chunkWriter {\n\treturn &chunkWriter{Writer: writer}\n}\n\nfunc (cr *chunkReader) Read(b []byte) (int, error) {\n\tif cr.buf != nil {\n\t\tn := copy(b, cr.buf[cr.offset:])\n\t\tcr.offset += n\n\t\tif cr.offset == len(cr.buf) {\n\t\t\tpool.Put(cr.buf)\n\t\t\tcr.buf = nil\n\t\t}\n\t\treturn n, nil\n\t}\n\n\t_, err := io.ReadFull(cr.Reader, cr.sizeBuf)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tsize := int(binary.BigEndian.Uint16(cr.sizeBuf))\n\tif size > maxSize {\n\t\treturn 0, errors.New(\"buffer is larger than standard\")\n\t}\n\n\tif len(b) >= size {\n\t\t_, err := io.ReadFull(cr.Reader, b[:size])\n\t\tif err != nil {\n\t\t\treturn 0, err\n\t\t}\n\n\t\treturn size, nil\n\t}\n\n\tbuf := pool.Get(size)\n\t_, err = io.ReadFull(cr.Reader, buf)\n\tif err != nil {\n\t\tpool.Put(buf)\n\t\treturn 0, err\n\t}\n\tn := copy(b, buf)\n\tcr.offset = n\n\tcr.buf = buf\n\treturn n, nil\n}\n\ntype chunkWriter struct {\n\tio.Writer\n}\n\nfunc (cw *chunkWriter) Write(b []byte) (n int, err error) {\n\tbuf := pool.Get(pool.RelayBufferSize)\n\tdefer pool.Put(buf)\n\tlength := len(b)\n\tfor {\n\t\tif length == 0 {\n\t\t\tbreak\n\t\t}\n\t\treadLen := chunkSize\n\t\tif length < chunkSize {\n\t\t\treadLen = length\n\t\t}\n\t\tpayloadBuf := buf[lenSize : lenSize+chunkSize]\n\t\tcopy(payloadBuf, b[n:n+readLen])\n\n\t\tbinary.BigEndian.PutUint16(buf[:lenSize], uint16(readLen))\n\t\t_, err = cw.Writer.Write(buf[:lenSize+readLen])\n\t\tif err != nil {\n\t\t\tbreak\n\t\t}\n\t\tn += readLen\n\t\tlength -= readLen\n\t}\n\treturn\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vmess/conn.go",
    "content": "package vmess\n\nimport (\n\t\"bytes\"\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\t\"crypto/hmac\"\n\t\"crypto/md5\"\n\t\"crypto/rand\"\n\t\"crypto/sha256\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"hash/fnv\"\n\t\"io\"\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/metacubex/randv2\"\n\t\"golang.org/x/crypto/chacha20poly1305\"\n)\n\n// Conn wrapper a net.Conn with vmess protocol\ntype Conn struct {\n\tnet.Conn\n\treader      io.Reader\n\twriter      io.Writer\n\tdst         *DstAddr\n\tid          *ID\n\treqBodyIV   []byte\n\treqBodyKey  []byte\n\trespBodyIV  []byte\n\trespBodyKey []byte\n\trespV       byte\n\tsecurity    byte\n\tisAead      bool\n\n\treceived bool\n}\n\nfunc (vc *Conn) Write(b []byte) (int, error) {\n\treturn vc.writer.Write(b)\n}\n\nfunc (vc *Conn) Read(b []byte) (int, error) {\n\tif vc.received {\n\t\treturn vc.reader.Read(b)\n\t}\n\n\tif err := vc.recvResponse(); err != nil {\n\t\treturn 0, err\n\t}\n\tvc.received = true\n\treturn vc.reader.Read(b)\n}\n\nfunc (vc *Conn) sendRequest() error {\n\ttimestamp := time.Now()\n\n\tmbuf := &bytes.Buffer{}\n\n\tif !vc.isAead {\n\t\th := hmac.New(md5.New, vc.id.UUID.Bytes())\n\t\tbinary.Write(h, binary.BigEndian, uint64(timestamp.Unix()))\n\t\tmbuf.Write(h.Sum(nil))\n\t}\n\n\tbuf := &bytes.Buffer{}\n\n\t// Ver IV Key V Opt\n\tbuf.WriteByte(Version)\n\tbuf.Write(vc.reqBodyIV[:])\n\tbuf.Write(vc.reqBodyKey[:])\n\tbuf.WriteByte(vc.respV)\n\tbuf.WriteByte(OptionChunkStream)\n\n\tp := randv2.IntN(16)\n\t// P Sec Reserve Cmd\n\tbuf.WriteByte(byte(p<<4) | byte(vc.security))\n\tbuf.WriteByte(0)\n\tif vc.dst.UDP {\n\t\tbuf.WriteByte(CommandUDP)\n\t} else {\n\t\tbuf.WriteByte(CommandTCP)\n\t}\n\n\t// Port AddrType Addr\n\tbinary.Write(buf, binary.BigEndian, uint16(vc.dst.Port))\n\tbuf.WriteByte(vc.dst.AddrType)\n\tbuf.Write(vc.dst.Addr)\n\n\t// padding\n\tif p > 0 {\n\t\tpadding := make([]byte, p)\n\t\trand.Read(padding)\n\t\tbuf.Write(padding)\n\t}\n\n\tfnv1a := fnv.New32a()\n\tfnv1a.Write(buf.Bytes())\n\tbuf.Write(fnv1a.Sum(nil))\n\n\tif !vc.isAead {\n\t\tblock, err := aes.NewCipher(vc.id.CmdKey)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tstream := cipher.NewCFBEncrypter(block, hashTimestamp(timestamp))\n\t\tstream.XORKeyStream(buf.Bytes(), buf.Bytes())\n\t\tmbuf.Write(buf.Bytes())\n\t\t_, err = vc.Conn.Write(mbuf.Bytes())\n\t\treturn err\n\t}\n\n\tvar fixedLengthCmdKey [16]byte\n\tcopy(fixedLengthCmdKey[:], vc.id.CmdKey)\n\tvmessout := sealVMessAEADHeader(fixedLengthCmdKey, buf.Bytes(), timestamp)\n\t_, err := vc.Conn.Write(vmessout)\n\treturn err\n}\n\nfunc (vc *Conn) recvResponse() error {\n\tvar buf []byte\n\tif !vc.isAead {\n\t\tblock, err := aes.NewCipher(vc.respBodyKey[:])\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tstream := cipher.NewCFBDecrypter(block, vc.respBodyIV[:])\n\t\tbuf = make([]byte, 4)\n\t\t_, err = io.ReadFull(vc.Conn, buf)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tstream.XORKeyStream(buf, buf)\n\t} else {\n\t\taeadResponseHeaderLengthEncryptionKey := kdf(vc.respBodyKey[:], kdfSaltConstAEADRespHeaderLenKey)[:16]\n\t\taeadResponseHeaderLengthEncryptionIV := kdf(vc.respBodyIV[:], kdfSaltConstAEADRespHeaderLenIV)[:12]\n\n\t\taeadResponseHeaderLengthEncryptionKeyAESBlock, _ := aes.NewCipher(aeadResponseHeaderLengthEncryptionKey)\n\t\taeadResponseHeaderLengthEncryptionAEAD, _ := cipher.NewGCM(aeadResponseHeaderLengthEncryptionKeyAESBlock)\n\n\t\taeadEncryptedResponseHeaderLength := make([]byte, 18)\n\t\tif _, err := io.ReadFull(vc.Conn, aeadEncryptedResponseHeaderLength); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tdecryptedResponseHeaderLengthBinaryBuffer, err := aeadResponseHeaderLengthEncryptionAEAD.Open(nil, aeadResponseHeaderLengthEncryptionIV, aeadEncryptedResponseHeaderLength[:], nil)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tdecryptedResponseHeaderLength := binary.BigEndian.Uint16(decryptedResponseHeaderLengthBinaryBuffer)\n\t\taeadResponseHeaderPayloadEncryptionKey := kdf(vc.respBodyKey[:], kdfSaltConstAEADRespHeaderPayloadKey)[:16]\n\t\taeadResponseHeaderPayloadEncryptionIV := kdf(vc.respBodyIV[:], kdfSaltConstAEADRespHeaderPayloadIV)[:12]\n\t\taeadResponseHeaderPayloadEncryptionKeyAESBlock, _ := aes.NewCipher(aeadResponseHeaderPayloadEncryptionKey)\n\t\taeadResponseHeaderPayloadEncryptionAEAD, _ := cipher.NewGCM(aeadResponseHeaderPayloadEncryptionKeyAESBlock)\n\n\t\tencryptedResponseHeaderBuffer := make([]byte, decryptedResponseHeaderLength+16)\n\t\tif _, err := io.ReadFull(vc.Conn, encryptedResponseHeaderBuffer); err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tbuf, err = aeadResponseHeaderPayloadEncryptionAEAD.Open(nil, aeadResponseHeaderPayloadEncryptionIV, encryptedResponseHeaderBuffer, nil)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif len(buf) < 4 {\n\t\t\treturn errors.New(\"unexpected buffer length\")\n\t\t}\n\t}\n\n\tif buf[0] != vc.respV {\n\t\treturn errors.New(\"unexpected response header\")\n\t}\n\n\tif buf[2] != 0 {\n\t\treturn errors.New(\"dynamic port is not supported now\")\n\t}\n\n\treturn nil\n}\n\nfunc hashTimestamp(t time.Time) []byte {\n\tmd5hash := md5.New()\n\tts := make([]byte, 8)\n\tbinary.BigEndian.PutUint64(ts, uint64(t.Unix()))\n\tmd5hash.Write(ts)\n\tmd5hash.Write(ts)\n\tmd5hash.Write(ts)\n\tmd5hash.Write(ts)\n\treturn md5hash.Sum(nil)\n}\n\n// newConn return a Conn instance\nfunc newConn(conn net.Conn, id *ID, dst *DstAddr, security Security, isAead bool) (*Conn, error) {\n\trandBytes := make([]byte, 33)\n\trand.Read(randBytes)\n\treqBodyIV := make([]byte, 16)\n\treqBodyKey := make([]byte, 16)\n\tcopy(reqBodyIV[:], randBytes[:16])\n\tcopy(reqBodyKey[:], randBytes[16:32])\n\trespV := randBytes[32]\n\n\tvar (\n\t\trespBodyKey []byte\n\t\trespBodyIV  []byte\n\t)\n\n\tif isAead {\n\t\tbodyKey := sha256.Sum256(reqBodyKey)\n\t\tbodyIV := sha256.Sum256(reqBodyIV)\n\t\trespBodyKey = bodyKey[:16]\n\t\trespBodyIV = bodyIV[:16]\n\t} else {\n\t\tbodyKey := md5.Sum(reqBodyKey)\n\t\tbodyIV := md5.Sum(reqBodyIV)\n\t\trespBodyKey = bodyKey[:]\n\t\trespBodyIV = bodyIV[:]\n\t}\n\n\tvar writer io.Writer\n\tvar reader io.Reader\n\tswitch security {\n\tcase SecurityNone:\n\t\treader = newChunkReader(conn)\n\t\twriter = newChunkWriter(conn)\n\tcase SecurityAES128GCM:\n\t\tblock, _ := aes.NewCipher(reqBodyKey[:])\n\t\taead, _ := cipher.NewGCM(block)\n\t\twriter = newAEADWriter(conn, aead, reqBodyIV[:])\n\n\t\tblock, _ = aes.NewCipher(respBodyKey[:])\n\t\taead, _ = cipher.NewGCM(block)\n\t\treader = newAEADReader(conn, aead, respBodyIV[:])\n\tcase SecurityCHACHA20POLY1305:\n\t\tkey := make([]byte, 32)\n\t\tt := md5.Sum(reqBodyKey[:])\n\t\tcopy(key, t[:])\n\t\tt = md5.Sum(key[:16])\n\t\tcopy(key[16:], t[:])\n\t\taead, _ := chacha20poly1305.New(key)\n\t\twriter = newAEADWriter(conn, aead, reqBodyIV[:])\n\n\t\tt = md5.Sum(respBodyKey[:])\n\t\tcopy(key, t[:])\n\t\tt = md5.Sum(key[:16])\n\t\tcopy(key[16:], t[:])\n\t\taead, _ = chacha20poly1305.New(key)\n\t\treader = newAEADReader(conn, aead, respBodyIV[:])\n\t}\n\n\tc := &Conn{\n\t\tConn:        conn,\n\t\tid:          id,\n\t\tdst:         dst,\n\t\treqBodyIV:   reqBodyIV,\n\t\treqBodyKey:  reqBodyKey,\n\t\trespV:       respV,\n\t\trespBodyIV:  respBodyIV[:],\n\t\trespBodyKey: respBodyKey[:],\n\t\treader:      reader,\n\t\twriter:      writer,\n\t\tsecurity:    security,\n\t\tisAead:      isAead,\n\t}\n\tif err := c.sendRequest(); err != nil {\n\t\treturn nil, err\n\t}\n\treturn c, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vmess/h2.go",
    "content": "package vmess\n\nimport (\n\t\"context\"\n\t\"io\"\n\t\"net\"\n\t\"net/url\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/randv2\"\n)\n\ntype h2Conn struct {\n\tnet.Conn\n\t*http.ClientConn\n\tpwriter *io.PipeWriter\n\tres     *http.Response\n\tcfg     *H2Config\n}\n\ntype H2Config struct {\n\tHosts []string\n\tPath  string\n}\n\nfunc (hc *h2Conn) establishConn() error {\n\tpreader, pwriter := io.Pipe()\n\n\thost := hc.cfg.Hosts[randv2.IntN(len(hc.cfg.Hosts))]\n\tpath := hc.cfg.Path\n\t// TODO: connect use VMess Host instead of H2 Host\n\treq := http.Request{\n\t\tMethod: \"PUT\",\n\t\tHost:   host,\n\t\tURL: &url.URL{\n\t\t\tScheme: \"https\",\n\t\t\tHost:   host,\n\t\t\tPath:   path,\n\t\t},\n\t\tProto:      \"HTTP/2\",\n\t\tProtoMajor: 2,\n\t\tProtoMinor: 0,\n\t\tBody:       preader,\n\t\tHeader: map[string][]string{\n\t\t\t\"Accept-Encoding\": {\"identity\"},\n\t\t},\n\t}\n\n\t// it will be close at :  `func (hc *h2Conn) Close() error`\n\tres, err := hc.ClientConn.RoundTrip(&req)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\thc.pwriter = pwriter\n\thc.res = res\n\n\treturn nil\n}\n\n// Read implements net.Conn.Read()\nfunc (hc *h2Conn) Read(b []byte) (int, error) {\n\tif hc.res != nil && !hc.res.Close {\n\t\tn, err := hc.res.Body.Read(b)\n\t\treturn n, err\n\t}\n\n\tif err := hc.establishConn(); err != nil {\n\t\treturn 0, err\n\t}\n\treturn hc.res.Body.Read(b)\n}\n\n// Write implements io.Writer.\nfunc (hc *h2Conn) Write(b []byte) (int, error) {\n\tif hc.pwriter != nil {\n\t\treturn hc.pwriter.Write(b)\n\t}\n\n\tif err := hc.establishConn(); err != nil {\n\t\treturn 0, err\n\t}\n\treturn hc.pwriter.Write(b)\n}\n\nfunc (hc *h2Conn) Close() error {\n\tif hc.pwriter != nil {\n\t\tif err := hc.pwriter.Close(); err != nil {\n\t\t\treturn err\n\t\t}\n\t}\n\treturn hc.Conn.Close()\n}\n\nfunc StreamH2Conn(ctx context.Context, conn net.Conn, cfg *H2Config) (_ net.Conn, err error) {\n\tif ctx.Done() != nil {\n\t\tdone := N.SetupContextForConn(ctx, conn)\n\t\tdefer done(&err)\n\t}\n\n\t// use h2c mode to disallow the net/http fallback to http1.1\n\tprotocols := new(http.Protocols)\n\tprotocols.SetUnencryptedHTTP2(true)\n\ttransport := &http.Transport{\n\t\tDialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\t\treturn conn, nil\n\t\t},\n\t\tProtocols: protocols,\n\t}\n\n\tclientConn, err := transport.NewClientConn(ctx, \"https\", \":0\")\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn &h2Conn{\n\t\tConn:       conn,\n\t\tClientConn: clientConn,\n\t\tcfg:        cfg,\n\t}, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vmess/header.go",
    "content": "package vmess\n\nimport (\n\t\"bytes\"\n\t\"crypto/aes\"\n\t\"crypto/cipher\"\n\t\"crypto/hmac\"\n\t\"crypto/rand\"\n\t\"crypto/sha256\"\n\t\"encoding/binary\"\n\t\"hash\"\n\t\"hash/crc32\"\n\t\"time\"\n)\n\nconst (\n\tkdfSaltConstAuthIDEncryptionKey             = \"AES Auth ID Encryption\"\n\tkdfSaltConstAEADRespHeaderLenKey            = \"AEAD Resp Header Len Key\"\n\tkdfSaltConstAEADRespHeaderLenIV             = \"AEAD Resp Header Len IV\"\n\tkdfSaltConstAEADRespHeaderPayloadKey        = \"AEAD Resp Header Key\"\n\tkdfSaltConstAEADRespHeaderPayloadIV         = \"AEAD Resp Header IV\"\n\tkdfSaltConstVMessAEADKDF                    = \"VMess AEAD KDF\"\n\tkdfSaltConstVMessHeaderPayloadAEADKey       = \"VMess Header AEAD Key\"\n\tkdfSaltConstVMessHeaderPayloadAEADIV        = \"VMess Header AEAD Nonce\"\n\tkdfSaltConstVMessHeaderPayloadLengthAEADKey = \"VMess Header AEAD Key_Length\"\n\tkdfSaltConstVMessHeaderPayloadLengthAEADIV  = \"VMess Header AEAD Nonce_Length\"\n)\n\nfunc kdf(key []byte, path ...string) []byte {\n\thmacCreator := &hMacCreator{value: []byte(kdfSaltConstVMessAEADKDF)}\n\tfor _, v := range path {\n\t\thmacCreator = &hMacCreator{value: []byte(v), parent: hmacCreator}\n\t}\n\thmacf := hmacCreator.Create()\n\thmacf.Write(key)\n\treturn hmacf.Sum(nil)\n}\n\ntype hMacCreator struct {\n\tparent *hMacCreator\n\tvalue  []byte\n}\n\nfunc (h *hMacCreator) Create() hash.Hash {\n\tif h.parent == nil {\n\t\treturn hmac.New(sha256.New, h.value)\n\t}\n\treturn hmac.New(h.parent.Create, h.value)\n}\n\nfunc createAuthID(cmdKey []byte, time int64) [16]byte {\n\tbuf := &bytes.Buffer{}\n\tbinary.Write(buf, binary.BigEndian, time)\n\n\trandom := make([]byte, 4)\n\trand.Read(random)\n\tbuf.Write(random)\n\tzero := crc32.ChecksumIEEE(buf.Bytes())\n\tbinary.Write(buf, binary.BigEndian, zero)\n\n\taesBlock, _ := aes.NewCipher(kdf(cmdKey[:], kdfSaltConstAuthIDEncryptionKey)[:16])\n\tvar result [16]byte\n\taesBlock.Encrypt(result[:], buf.Bytes())\n\treturn result\n}\n\nfunc sealVMessAEADHeader(key [16]byte, data []byte, t time.Time) []byte {\n\tgeneratedAuthID := createAuthID(key[:], t.Unix())\n\tconnectionNonce := make([]byte, 8)\n\trand.Read(connectionNonce)\n\n\taeadPayloadLengthSerializedByte := make([]byte, 2)\n\tbinary.BigEndian.PutUint16(aeadPayloadLengthSerializedByte, uint16(len(data)))\n\n\tvar payloadHeaderLengthAEADEncrypted []byte\n\n\t{\n\t\tpayloadHeaderLengthAEADKey := kdf(key[:], kdfSaltConstVMessHeaderPayloadLengthAEADKey, string(generatedAuthID[:]), string(connectionNonce))[:16]\n\t\tpayloadHeaderLengthAEADNonce := kdf(key[:], kdfSaltConstVMessHeaderPayloadLengthAEADIV, string(generatedAuthID[:]), string(connectionNonce))[:12]\n\t\tpayloadHeaderLengthAEADAESBlock, _ := aes.NewCipher(payloadHeaderLengthAEADKey)\n\t\tpayloadHeaderAEAD, _ := cipher.NewGCM(payloadHeaderLengthAEADAESBlock)\n\t\tpayloadHeaderLengthAEADEncrypted = payloadHeaderAEAD.Seal(nil, payloadHeaderLengthAEADNonce, aeadPayloadLengthSerializedByte, generatedAuthID[:])\n\t}\n\n\tvar payloadHeaderAEADEncrypted []byte\n\n\t{\n\t\tpayloadHeaderAEADKey := kdf(key[:], kdfSaltConstVMessHeaderPayloadAEADKey, string(generatedAuthID[:]), string(connectionNonce))[:16]\n\t\tpayloadHeaderAEADNonce := kdf(key[:], kdfSaltConstVMessHeaderPayloadAEADIV, string(generatedAuthID[:]), string(connectionNonce))[:12]\n\t\tpayloadHeaderAEADAESBlock, _ := aes.NewCipher(payloadHeaderAEADKey)\n\t\tpayloadHeaderAEAD, _ := cipher.NewGCM(payloadHeaderAEADAESBlock)\n\t\tpayloadHeaderAEADEncrypted = payloadHeaderAEAD.Seal(nil, payloadHeaderAEADNonce, data, generatedAuthID[:])\n\t}\n\n\toutputBuffer := &bytes.Buffer{}\n\n\toutputBuffer.Write(generatedAuthID[:])\n\toutputBuffer.Write(payloadHeaderLengthAEADEncrypted)\n\toutputBuffer.Write(connectionNonce)\n\toutputBuffer.Write(payloadHeaderAEADEncrypted)\n\n\treturn outputBuffer.Bytes()\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vmess/http.go",
    "content": "package vmess\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"io\"\n\t\"net\"\n\t\"net/textproto\"\n\t\"net/url\"\n\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/randv2\"\n)\n\ntype httpConn struct {\n\tnet.Conn\n\tcfg        *HTTPConfig\n\treader     *bufio.Reader\n\twhandshake bool\n}\n\ntype HTTPConfig struct {\n\tMethod  string\n\tHost    string\n\tPath    []string\n\tHeaders map[string][]string\n}\n\n// Read implements net.Conn.Read()\nfunc (hc *httpConn) Read(b []byte) (int, error) {\n\tif hc.reader != nil {\n\t\tn, err := hc.reader.Read(b)\n\t\treturn n, err\n\t}\n\n\treader := textproto.NewConn(hc.Conn)\n\t// First line: GET /index.html HTTP/1.0\n\tif _, err := reader.ReadLine(); err != nil {\n\t\treturn 0, err\n\t}\n\n\tif _, err := reader.ReadMIMEHeader(); err != nil {\n\t\treturn 0, err\n\t}\n\n\thc.reader = reader.R\n\treturn reader.R.Read(b)\n}\n\n// Write implements io.Writer.\nfunc (hc *httpConn) Write(b []byte) (int, error) {\n\tif hc.whandshake {\n\t\treturn hc.Conn.Write(b)\n\t}\n\n\tpath := \"/\"\n\tif len(hc.cfg.Path) > 0 {\n\t\tpath = hc.cfg.Path[randv2.IntN(len(hc.cfg.Path))]\n\t}\n\n\thost := hc.cfg.Host\n\tif header := hc.cfg.Headers[\"Host\"]; len(header) != 0 {\n\t\thost = header[randv2.IntN(len(header))]\n\t}\n\n\treq := http.Request{\n\t\tMethod: hc.cfg.Method, // default is GET\n\t\tHost:   host,\n\t\tURL:    &url.URL{Scheme: \"http\", Host: host, Path: path},\n\t\tHeader: make(http.Header),\n\t\tBody:   io.NopCloser(bytes.NewReader(b)),\n\t}\n\tfor key, list := range hc.cfg.Headers {\n\t\treq.Header.Set(key, list[randv2.IntN(len(list))])\n\t}\n\treq.ContentLength = int64(len(b))\n\tif err := req.Write(hc.Conn); err != nil {\n\t\treturn 0, err\n\t}\n\thc.whandshake = true\n\treturn len(b), nil\n}\n\nfunc (hc *httpConn) Close() error {\n\treturn hc.Conn.Close()\n}\n\nfunc StreamHTTPConn(conn net.Conn, cfg *HTTPConfig) net.Conn {\n\treturn &httpConn{\n\t\tConn: conn,\n\t\tcfg:  cfg,\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vmess/tls.go",
    "content": "package vmess\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net\"\n\n\t\"github.com/metacubex/mihomo/component/ca\"\n\t\"github.com/metacubex/mihomo/component/ech\"\n\ttlsC \"github.com/metacubex/mihomo/component/tls\"\n\n\t\"github.com/metacubex/tls\"\n)\n\ntype TLSConfig struct {\n\tHost              string\n\tSkipCertVerify    bool\n\tFingerPrint       string\n\tCertificate       string\n\tPrivateKey        string\n\tClientFingerprint string\n\tNextProtos        []string\n\tECH               *ech.Config\n\tReality           *tlsC.RealityConfig\n}\n\nfunc (cfg *TLSConfig) ToStdConfig() (*tls.Config, error) {\n\treturn ca.GetTLSConfig(ca.Option{\n\t\tTLSConfig: &tls.Config{\n\t\t\tServerName:         cfg.Host,\n\t\t\tInsecureSkipVerify: cfg.SkipCertVerify,\n\t\t\tNextProtos:         cfg.NextProtos,\n\t\t},\n\t\tFingerprint: cfg.FingerPrint,\n\t\tCertificate: cfg.Certificate,\n\t\tPrivateKey:  cfg.PrivateKey,\n\t})\n}\n\nfunc StreamTLSConn(ctx context.Context, conn net.Conn, cfg *TLSConfig) (net.Conn, error) {\n\ttlsConfig, err := cfg.ToStdConfig()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tif clientFingerprint, ok := tlsC.GetFingerprint(cfg.ClientFingerprint); ok {\n\t\tif cfg.Reality != nil {\n\t\t\treturn tlsC.GetRealityConn(ctx, conn, clientFingerprint, tlsConfig.ServerName, cfg.Reality)\n\t\t}\n\t\ttlsConfig := tlsC.UConfig(tlsConfig)\n\t\terr = cfg.ECH.ClientHandleUTLS(ctx, tlsConfig)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\ttlsConn := tlsC.UClient(conn, tlsConfig, clientFingerprint)\n\t\terr = tlsConn.HandshakeContext(ctx)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\treturn tlsConn, nil\n\t}\n\tif cfg.Reality != nil {\n\t\treturn nil, errors.New(\"REALITY is based on uTLS, please set a client-fingerprint\")\n\t}\n\n\terr = cfg.ECH.ClientHandle(ctx, tlsConfig)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\ttlsConn := tls.Client(conn, tlsConfig)\n\n\terr = tlsConn.HandshakeContext(ctx)\n\treturn tlsConn, err\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vmess/user.go",
    "content": "package vmess\n\nimport (\n\t\"bytes\"\n\t\"crypto/md5\"\n\n\t\"github.com/gofrs/uuid/v5\"\n)\n\n// ID cmdKey length\nconst (\n\tIDBytesLen = 16\n)\n\n// The ID of en entity, in the form of a UUID.\ntype ID struct {\n\tUUID   *uuid.UUID\n\tCmdKey []byte\n}\n\n// newID returns an ID with given UUID.\nfunc newID(uuid *uuid.UUID) *ID {\n\tid := &ID{UUID: uuid, CmdKey: make([]byte, IDBytesLen)}\n\tmd5hash := md5.New()\n\tmd5hash.Write(uuid.Bytes())\n\tmd5hash.Write([]byte(\"c48619fe-8f02-49e0-b9e9-edf763e17e21\"))\n\tmd5hash.Sum(id.CmdKey[:0])\n\treturn id\n}\n\nfunc nextID(u *uuid.UUID) *uuid.UUID {\n\tmd5hash := md5.New()\n\tmd5hash.Write(u.Bytes())\n\tmd5hash.Write([]byte(\"16167dc8-16b6-4e6d-b8bb-65dd68113a81\"))\n\tvar newid uuid.UUID\n\tfor {\n\t\tmd5hash.Sum(newid[:0])\n\t\tif !bytes.Equal(newid.Bytes(), u.Bytes()) {\n\t\t\treturn &newid\n\t\t}\n\t\tmd5hash.Write([]byte(\"533eff8a-4113-4b10-b5ce-0f5d76b98cd2\"))\n\t}\n}\n\nfunc newAlterIDs(primary *ID, alterIDCount uint16) []*ID {\n\talterIDs := make([]*ID, alterIDCount)\n\tprevID := primary.UUID\n\tfor idx := range alterIDs {\n\t\tnewid := nextID(prevID)\n\t\talterIDs[idx] = &ID{UUID: newid, CmdKey: primary.CmdKey[:]}\n\t\tprevID = newid\n\t}\n\talterIDs = append(alterIDs, primary)\n\treturn alterIDs\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vmess/vmess.go",
    "content": "package vmess\n\nimport (\n\t\"fmt\"\n\t\"net\"\n\t\"runtime\"\n\n\t\"github.com/metacubex/mihomo/common/utils\"\n\n\t\"github.com/gofrs/uuid/v5\"\n\t\"github.com/metacubex/randv2\"\n)\n\n// Version of vmess\nconst Version byte = 1\n\n// Request Options\nconst (\n\tOptionChunkStream  byte = 1\n\tOptionChunkMasking byte = 4\n)\n\n// Security type vmess\ntype Security = byte\n\n// Cipher types\nconst (\n\tSecurityAES128GCM        Security = 3\n\tSecurityCHACHA20POLY1305 Security = 4\n\tSecurityNone             Security = 5\n)\n\n// CipherMapping return\nvar CipherMapping = map[string]byte{\n\t\"none\":              SecurityNone,\n\t\"aes-128-gcm\":       SecurityAES128GCM,\n\t\"chacha20-poly1305\": SecurityCHACHA20POLY1305,\n}\n\n// Command types\nconst (\n\tCommandTCP byte = 1\n\tCommandUDP byte = 2\n)\n\n// Addr types\nconst (\n\tAtypIPv4       byte = 1\n\tAtypDomainName byte = 2\n\tAtypIPv6       byte = 3\n)\n\n// DstAddr store destination address\ntype DstAddr struct {\n\tUDP      bool\n\tAddrType byte\n\tAddr     []byte\n\tPort     uint\n}\n\n// Client is vmess connection generator\ntype Client struct {\n\tuser     []*ID\n\tuuid     *uuid.UUID\n\tsecurity Security\n\tisAead   bool\n}\n\n// Config of vmess\ntype Config struct {\n\tUUID     string\n\tAlterID  uint16\n\tSecurity string\n\tPort     string\n\tHostName string\n\tIsAead   bool\n}\n\n// StreamConn return a Conn with net.Conn and DstAddr\nfunc (c *Client) StreamConn(conn net.Conn, dst *DstAddr) (net.Conn, error) {\n\tr := randv2.IntN(len(c.user))\n\treturn newConn(conn, c.user[r], dst, c.security, c.isAead)\n}\n\n// NewClient return Client instance\nfunc NewClient(config Config) (*Client, error) {\n\tuid := utils.UUIDMap(config.UUID)\n\n\tvar security Security\n\tswitch config.Security {\n\tcase \"aes-128-gcm\":\n\t\tsecurity = SecurityAES128GCM\n\tcase \"chacha20-poly1305\":\n\t\tsecurity = SecurityCHACHA20POLY1305\n\tcase \"none\":\n\t\tsecurity = SecurityNone\n\tcase \"auto\":\n\t\tsecurity = SecurityCHACHA20POLY1305\n\t\tif runtime.GOARCH == \"amd64\" || runtime.GOARCH == \"s390x\" || runtime.GOARCH == \"arm64\" {\n\t\t\tsecurity = SecurityAES128GCM\n\t\t}\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"unknown security type: %s\", config.Security)\n\t}\n\n\treturn &Client{\n\t\tuser:     newAlterIDs(newID(&uid), config.AlterID),\n\t\tuuid:     &uid,\n\t\tsecurity: security,\n\t\tisAead:   config.IsAead,\n\t}, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vmess/websocket.go",
    "content": "package vmess\n\nimport (\n\t\"bufio\"\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/rand\"\n\t\"encoding/base64\"\n\t\"encoding/binary\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/buf\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/component/ech\"\n\ttlsC \"github.com/metacubex/mihomo/component/tls\"\n\t\"github.com/metacubex/mihomo/log\"\n\n\t\"github.com/gobwas/ws\"\n\t\"github.com/gobwas/ws/wsutil\"\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/randv2\"\n\t\"github.com/metacubex/tls\"\n)\n\ntype websocketConn struct {\n\tnet.Conn\n\tstate          ws.State\n\treader         *wsutil.Reader\n\tcontrolHandler wsutil.FrameHandlerFunc\n\n\trawWriter N.ExtendedWriter\n}\n\ntype websocketWithEarlyDataConn struct {\n\tconn     N.ExtendedConn\n\tunderlay net.Conn\n\tdialed   chan struct{}\n\tcancel   context.CancelFunc\n\tctx      context.Context\n\tconfig   *WebsocketConfig\n}\n\ntype WebsocketConfig struct {\n\tHost                     string\n\tPort                     string\n\tPath                     string\n\tHeaders                  http.Header\n\tTLS                      bool\n\tTLSConfig                *tls.Config\n\tECHConfig                *ech.Config\n\tMaxEarlyData             int\n\tEarlyDataHeaderName      string\n\tClientFingerprint        string\n\tV2rayHttpUpgrade         bool\n\tV2rayHttpUpgradeFastOpen bool\n}\n\n// Read implements net.Conn.Read()\n// modify from gobwas/ws/wsutil.readData\nfunc (wsc *websocketConn) Read(b []byte) (n int, err error) {\n\tdefer func() { // avoid gobwas/ws pbytes.GetLen panic\n\t\tif value := recover(); value != nil {\n\t\t\terr = fmt.Errorf(\"websocket error: %s\", value)\n\t\t}\n\t}()\n\tvar header ws.Header\n\tfor {\n\t\tn, err = wsc.reader.Read(b)\n\t\t// in gobwas/ws: \"The error is io.EOF only if all of message bytes were read.\"\n\t\t// but maybe next frame still have data, so drop it\n\t\tif errors.Is(err, io.EOF) {\n\t\t\terr = nil\n\t\t}\n\t\tif !errors.Is(err, wsutil.ErrNoFrameAdvance) {\n\t\t\treturn\n\t\t}\n\t\theader, err = wsc.reader.NextFrame()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tif header.OpCode.IsControl() {\n\t\t\terr = wsc.controlHandler(header, wsc.reader)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t\tif header.OpCode&(ws.OpBinary|ws.OpText) == 0 {\n\t\t\terr = wsc.reader.Discard()\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tcontinue\n\t\t}\n\t}\n}\n\n// Write implements io.Writer.\nfunc (wsc *websocketConn) Write(b []byte) (n int, err error) {\n\terr = wsutil.WriteMessage(wsc.Conn, wsc.state, ws.OpBinary, b)\n\tif err != nil {\n\t\treturn\n\t}\n\tn = len(b)\n\treturn\n}\n\nfunc (wsc *websocketConn) WriteBuffer(buffer *buf.Buffer) error {\n\tvar payloadBitLength int\n\tdataLen := buffer.Len()\n\tdata := buffer.Bytes()\n\tif dataLen < 126 {\n\t\tpayloadBitLength = 1\n\t} else if dataLen < 65536 {\n\t\tpayloadBitLength = 3\n\t} else {\n\t\tpayloadBitLength = 9\n\t}\n\n\tvar headerLen int\n\theaderLen += 1 // FIN / RSV / OPCODE\n\theaderLen += payloadBitLength\n\tif wsc.state.ClientSide() {\n\t\theaderLen += 4 // MASK KEY\n\t}\n\n\theader := buffer.ExtendHeader(headerLen)\n\theader[0] = byte(ws.OpBinary) | 0x80\n\tif wsc.state.ClientSide() {\n\t\theader[1] = 1 << 7\n\t} else {\n\t\theader[1] = 0\n\t}\n\n\tif dataLen < 126 {\n\t\theader[1] |= byte(dataLen)\n\t} else if dataLen < 65536 {\n\t\theader[1] |= 126\n\t\tbinary.BigEndian.PutUint16(header[2:], uint16(dataLen))\n\t} else {\n\t\theader[1] |= 127\n\t\tbinary.BigEndian.PutUint64(header[2:], uint64(dataLen))\n\t}\n\n\tif wsc.state.ClientSide() {\n\t\tmaskKey := randv2.Uint32()\n\t\tbinary.LittleEndian.PutUint32(header[1+payloadBitLength:], maskKey)\n\t\tN.MaskWebSocket(maskKey, data)\n\t}\n\n\treturn wsc.rawWriter.WriteBuffer(buffer)\n}\n\nfunc (wsc *websocketConn) FrontHeadroom() int {\n\treturn 14\n}\n\nfunc (wsc *websocketConn) Upstream() any {\n\treturn wsc.Conn\n}\n\nfunc (wsc *websocketConn) Close() error {\n\t_ = wsc.Conn.SetWriteDeadline(time.Now().Add(time.Second * 5))\n\t_ = wsutil.WriteMessage(wsc.Conn, wsc.state, ws.OpClose, ws.NewCloseFrameBody(ws.StatusNormalClosure, \"\"))\n\t_ = wsc.Conn.Close()\n\treturn nil\n}\n\nfunc (wsedc *websocketWithEarlyDataConn) dial(earlyData []byte) error {\n\tbase64DataBuf := &bytes.Buffer{}\n\tbase64EarlyDataEncoder := base64.NewEncoder(base64.RawURLEncoding, base64DataBuf)\n\n\tearlyDataBuf := bytes.NewBuffer(earlyData)\n\tif _, err := base64EarlyDataEncoder.Write(earlyDataBuf.Next(wsedc.config.MaxEarlyData)); err != nil {\n\t\treturn fmt.Errorf(\"failed to encode early data: %w\", err)\n\t}\n\n\tif err := base64EarlyDataEncoder.Close(); err != nil {\n\t\treturn fmt.Errorf(\"failed to encode early data tail: %w\", err)\n\t}\n\n\tconn, err := streamWebsocketConn(wsedc.ctx, wsedc.underlay, wsedc.config, base64DataBuf)\n\tif err != nil {\n\t\t_ = wsedc.Close()\n\t\treturn fmt.Errorf(\"failed to dial WebSocket: %w\", err)\n\t}\n\n\twsedc.conn = N.NewExtendedConn(conn)\n\tclose(wsedc.dialed)\n\tif earlyDataBuf.Len() != 0 {\n\t\t_, err = wsedc.conn.Write(earlyDataBuf.Bytes())\n\t}\n\n\treturn err\n}\n\nfunc (wsedc *websocketWithEarlyDataConn) Write(b []byte) (int, error) {\n\tselect {\n\tcase <-wsedc.ctx.Done():\n\t\treturn 0, io.ErrClosedPipe\n\tcase <-wsedc.dialed:\n\t\treturn wsedc.conn.Write(b)\n\tdefault:\n\t\tif err := wsedc.dial(b); err != nil {\n\t\t\treturn 0, err\n\t\t}\n\t\treturn len(b), nil\n\t}\n}\n\nfunc (wsedc *websocketWithEarlyDataConn) WriteBuffer(buffer *buf.Buffer) error {\n\tselect {\n\tcase <-wsedc.ctx.Done():\n\t\treturn io.ErrClosedPipe\n\tcase <-wsedc.dialed:\n\t\treturn wsedc.conn.WriteBuffer(buffer)\n\tdefault:\n\t\tif err := wsedc.dial(buffer.Bytes()); err != nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\t}\n}\n\nfunc (wsedc *websocketWithEarlyDataConn) Read(b []byte) (int, error) {\n\tselect {\n\tcase <-wsedc.ctx.Done():\n\t\treturn 0, io.ErrClosedPipe\n\tcase <-wsedc.dialed:\n\t\treturn wsedc.conn.Read(b)\n\t}\n}\n\nfunc (wsedc *websocketWithEarlyDataConn) Close() error {\n\twsedc.cancel()\n\tselect {\n\tcase <-wsedc.dialed:\n\t\treturn wsedc.conn.Close()\n\tdefault:\n\t\treturn wsedc.underlay.Close()\n\t}\n}\n\nfunc (wsedc *websocketWithEarlyDataConn) LocalAddr() net.Addr {\n\tselect {\n\tcase <-wsedc.dialed:\n\t\treturn wsedc.conn.LocalAddr()\n\tdefault:\n\t\treturn wsedc.underlay.LocalAddr()\n\t}\n}\n\nfunc (wsedc *websocketWithEarlyDataConn) RemoteAddr() net.Addr {\n\tselect {\n\tcase <-wsedc.dialed:\n\t\treturn wsedc.conn.RemoteAddr()\n\tdefault:\n\t\treturn wsedc.underlay.RemoteAddr()\n\t}\n}\n\nfunc (wsedc *websocketWithEarlyDataConn) SetDeadline(t time.Time) error {\n\tif err := wsedc.SetReadDeadline(t); err != nil {\n\t\treturn err\n\t}\n\treturn wsedc.SetWriteDeadline(t)\n}\n\nfunc (wsedc *websocketWithEarlyDataConn) SetReadDeadline(t time.Time) error {\n\tselect {\n\tcase <-wsedc.dialed:\n\t\treturn wsedc.conn.SetReadDeadline(t)\n\tdefault:\n\t\treturn nil\n\t}\n}\n\nfunc (wsedc *websocketWithEarlyDataConn) SetWriteDeadline(t time.Time) error {\n\tselect {\n\tcase <-wsedc.dialed:\n\t\treturn wsedc.conn.SetReadDeadline(t)\n\tdefault:\n\t\treturn nil\n\t}\n}\n\nfunc (wsedc *websocketWithEarlyDataConn) FrontHeadroom() int {\n\treturn 14\n}\n\nfunc (wsedc *websocketWithEarlyDataConn) Upstream() any {\n\treturn wsedc.underlay\n}\n\n//func (wsedc *websocketWithEarlyDataConn) LazyHeadroom() bool {\n//\treturn wsedc.Conn == nil\n//}\n//\n//func (wsedc *websocketWithEarlyDataConn) Upstream() any {\n//\tif wsedc.Conn == nil { // ensure return a nil interface not an interface with nil value\n//\t\treturn nil\n//\t}\n//\treturn wsedc.Conn\n//}\n\nfunc (wsedc *websocketWithEarlyDataConn) NeedHandshake() bool {\n\tselect {\n\tcase <-wsedc.dialed:\n\t\treturn false\n\tdefault:\n\t\treturn true\n\t}\n}\n\nfunc streamWebsocketWithEarlyDataConn(conn net.Conn, c *WebsocketConfig) (net.Conn, error) {\n\tctx, cancel := context.WithCancel(context.Background())\n\tconn = &websocketWithEarlyDataConn{\n\t\tdialed:   make(chan struct{}),\n\t\tcancel:   cancel,\n\t\tctx:      ctx,\n\t\tunderlay: conn,\n\t\tconfig:   c,\n\t}\n\t// websocketWithEarlyDataConn can't correct handle Deadline\n\t// it will not apply the already set Deadline after Dial()\n\t// so call N.NewDeadlineConn to add a safe wrapper\n\treturn N.NewDeadlineConn(conn), nil\n}\n\nfunc streamWebsocketConn(ctx context.Context, conn net.Conn, c *WebsocketConfig, earlyData *bytes.Buffer) (_ net.Conn, err error) {\n\tu, err := url.Parse(c.Path)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"parse url %s error: %w\", c.Path, err)\n\t}\n\n\turi := url.URL{\n\t\tScheme:   \"ws\",\n\t\tHost:     net.JoinHostPort(c.Host, c.Port),\n\t\tPath:     u.Path,\n\t\tRawQuery: u.RawQuery,\n\t}\n\n\tif !strings.HasPrefix(uri.Path, \"/\") {\n\t\turi.Path = \"/\" + uri.Path\n\t}\n\n\tif c.TLS {\n\t\turi.Scheme = \"wss\"\n\t\tconfig := c.TLSConfig\n\t\tif config == nil { // The config cannot be nil\n\t\t\tconfig = &tls.Config{NextProtos: []string{\"http/1.1\"}}\n\t\t}\n\t\tif config.ServerName == \"\" && !config.InsecureSkipVerify { // users must set either ServerName or InsecureSkipVerify in the config.\n\t\t\tconfig = config.Clone()\n\t\t\tconfig.ServerName = c.Host\n\t\t}\n\n\t\tif clientFingerprint, ok := tlsC.GetFingerprint(c.ClientFingerprint); ok {\n\t\t\ttlsConfig := tlsC.UConfig(config)\n\t\t\terr = c.ECHConfig.ClientHandleUTLS(ctx, tlsConfig)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\ttlsConn := tlsC.UClient(conn, tlsConfig, clientFingerprint)\n\t\t\tif err = tlsC.BuildWebsocketHandshakeState(tlsConn); err != nil {\n\t\t\t\treturn nil, fmt.Errorf(\"parse url %s error: %w\", c.Path, err)\n\t\t\t}\n\t\t\terr = tlsConn.HandshakeContext(ctx)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tconn = tlsConn\n\t\t} else {\n\t\t\terr = c.ECHConfig.ClientHandle(ctx, config)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\ttlsConn := tls.Client(conn, config)\n\t\t\terr = tlsConn.HandshakeContext(ctx)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tconn = tlsConn\n\t\t}\n\t}\n\n\trequest := &http.Request{\n\t\tMethod: http.MethodGet,\n\t\tURL:    &uri,\n\t\tHeader: c.Headers.Clone(),\n\t\tHost:   c.Host,\n\t}\n\n\trequest.Header.Set(\"Connection\", \"Upgrade\")\n\trequest.Header.Set(\"Upgrade\", \"websocket\")\n\n\tif host := request.Header.Get(\"Host\"); host != \"\" {\n\t\t// For client requests, Host optionally overrides the Host\n\t\t// header to send. If empty, the Request.Write method uses\n\t\t// the value of URL.Host. Host may contain an international\n\t\t// domain name.\n\t\trequest.Host = host\n\t}\n\trequest.Header.Del(\"Host\")\n\n\tvar secKey string\n\tif !c.V2rayHttpUpgrade {\n\t\tconst nonceKeySize = 16\n\t\t// NOTE: bts does not escape.\n\t\tbts := make([]byte, nonceKeySize)\n\t\tif _, err = rand.Read(bts); err != nil {\n\t\t\treturn nil, fmt.Errorf(\"rand read error: %w\", err)\n\t\t}\n\t\tsecKey = base64.StdEncoding.EncodeToString(bts)\n\t\trequest.Header.Set(\"Sec-WebSocket-Version\", \"13\")\n\t\trequest.Header.Set(\"Sec-WebSocket-Key\", secKey)\n\t}\n\n\tif earlyData != nil {\n\t\tearlyDataString := earlyData.String()\n\t\tif c.EarlyDataHeaderName == \"\" {\n\t\t\turi.Path += earlyDataString\n\t\t} else {\n\t\t\trequest.Header.Set(c.EarlyDataHeaderName, earlyDataString)\n\t\t}\n\t}\n\n\tif ctx.Done() != nil {\n\t\tdone := N.SetupContextForConn(ctx, conn)\n\t\tdefer done(&err)\n\t}\n\n\terr = request.Write(conn)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tbufferedConn := N.NewBufferedConn(conn)\n\n\tif c.V2rayHttpUpgrade && c.V2rayHttpUpgradeFastOpen {\n\t\treturn N.NewEarlyConn(bufferedConn, func() error {\n\t\t\tresponse, err := http.ReadResponse(bufferedConn.Reader(), request)\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tif response.StatusCode != http.StatusSwitchingProtocols ||\n\t\t\t\t!strings.EqualFold(response.Header.Get(\"Connection\"), \"upgrade\") ||\n\t\t\t\t!strings.EqualFold(response.Header.Get(\"Upgrade\"), \"websocket\") {\n\t\t\t\treturn fmt.Errorf(\"unexpected status: %s\", response.Status)\n\t\t\t}\n\t\t\treturn nil\n\t\t}), nil\n\t}\n\n\tresponse, err := http.ReadResponse(bufferedConn.Reader(), request)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif response.StatusCode != http.StatusSwitchingProtocols ||\n\t\t!strings.EqualFold(response.Header.Get(\"Connection\"), \"upgrade\") ||\n\t\t!strings.EqualFold(response.Header.Get(\"Upgrade\"), \"websocket\") {\n\t\treturn nil, fmt.Errorf(\"unexpected status: %s\", response.Status)\n\t}\n\n\tif c.V2rayHttpUpgrade {\n\t\treturn bufferedConn, nil\n\t}\n\n\tif log.Level() == log.DEBUG { // we might not check this for performance\n\t\tsecAccept := response.Header.Get(\"Sec-Websocket-Accept\")\n\t\tconst acceptSize = 28 // base64.StdEncoding.EncodedLen(sha1.Size)\n\t\tif lenSecAccept := len(secAccept); lenSecAccept != acceptSize {\n\t\t\treturn nil, fmt.Errorf(\"unexpected Sec-Websocket-Accept length: %d\", lenSecAccept)\n\t\t}\n\t\tif N.GetWebSocketSecAccept(secKey) != secAccept {\n\t\t\treturn nil, errors.New(\"unexpected Sec-Websocket-Accept\")\n\t\t}\n\t}\n\n\tconn = newWebsocketConn(bufferedConn, ws.StateClientSide)\n\t// websocketConn can't correct handle ReadDeadline\n\t// so call N.NewDeadlineConn to add a safe wrapper\n\treturn N.NewDeadlineConn(conn), nil\n}\n\nfunc StreamWebsocketConn(ctx context.Context, conn net.Conn, c *WebsocketConfig) (net.Conn, error) {\n\tif u, err := url.Parse(c.Path); err == nil {\n\t\tif q := u.Query(); q.Get(\"ed\") != \"\" {\n\t\t\tif ed, err := strconv.Atoi(q.Get(\"ed\")); err == nil {\n\t\t\t\tc.MaxEarlyData = ed\n\t\t\t\tc.EarlyDataHeaderName = \"Sec-WebSocket-Protocol\"\n\t\t\t\tq.Del(\"ed\")\n\t\t\t\tu.RawQuery = q.Encode()\n\t\t\t\tc.Path = u.String()\n\t\t\t}\n\t\t}\n\t}\n\n\tif c.MaxEarlyData > 0 {\n\t\treturn streamWebsocketWithEarlyDataConn(conn, c)\n\t}\n\n\treturn streamWebsocketConn(ctx, conn, c, nil)\n}\n\nfunc newWebsocketConn(conn net.Conn, state ws.State) *websocketConn {\n\tcontrolHandler := wsutil.ControlFrameHandler(conn, state)\n\treturn &websocketConn{\n\t\tConn:  conn,\n\t\tstate: state,\n\t\treader: &wsutil.Reader{\n\t\t\tSource:          conn,\n\t\t\tState:           state,\n\t\t\tSkipHeaderCheck: true,\n\t\t\tCheckUTF8:       false,\n\t\t\tOnIntermediate:  controlHandler,\n\t\t},\n\t\tcontrolHandler: controlHandler,\n\t\trawWriter:      N.NewExtendedWriter(conn),\n\t}\n}\n\nvar replacer = strings.NewReplacer(\"+\", \"-\", \"/\", \"_\", \"=\", \"\")\n\nfunc decodeEd(s string) ([]byte, error) {\n\treturn base64.RawURLEncoding.DecodeString(replacer.Replace(s))\n}\n\nfunc decodeXray0rtt(requestHeader http.Header) []byte {\n\t// read inHeader's `Sec-WebSocket-Protocol` for Xray's 0rtt ws\n\tif secProtocol := requestHeader.Get(\"Sec-WebSocket-Protocol\"); len(secProtocol) > 0 {\n\t\tif edBuf, err := decodeEd(secProtocol); err == nil { // sure could base64 decode\n\t\t\treturn edBuf\n\t\t}\n\t}\n\treturn nil\n}\n\nfunc IsWebSocketUpgrade(r *http.Request) bool {\n\treturn r.Header.Get(\"Upgrade\") == \"websocket\"\n}\n\nfunc IsV2rayHttpUpdate(r *http.Request) bool {\n\treturn IsWebSocketUpgrade(r) && r.Header.Get(\"Sec-WebSocket-Key\") == \"\"\n}\n\nfunc StreamUpgradedWebsocketConn(w http.ResponseWriter, r *http.Request) (net.Conn, error) {\n\tvar conn net.Conn\n\tvar rw *bufio.ReadWriter\n\tvar err error\n\tisRaw := IsV2rayHttpUpdate(r)\n\tw.Header().Set(\"Connection\", \"upgrade\")\n\tw.Header().Set(\"Upgrade\", \"websocket\")\n\tif !isRaw {\n\t\tw.Header().Set(\"Sec-Websocket-Accept\", N.GetWebSocketSecAccept(r.Header.Get(\"Sec-WebSocket-Key\")))\n\t}\n\tw.WriteHeader(http.StatusSwitchingProtocols)\n\tif flusher, isFlusher := w.(interface{ FlushError() error }); isFlusher && writeHeaderShouldFlush {\n\t\terr = flusher.FlushError()\n\t\tif err != nil {\n\t\t\treturn nil, fmt.Errorf(\"flush response: %w\", err)\n\t\t}\n\t}\n\thijacker, canHijack := w.(http.Hijacker)\n\tif !canHijack {\n\t\treturn nil, errors.New(\"invalid connection, maybe HTTP/2\")\n\t}\n\tconn, rw, err = hijacker.Hijack()\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"hijack failed: %w\", err)\n\t}\n\n\t// rw.Writer was flushed, so we only need warp rw.Reader\n\tconn = N.WarpConnWithBioReader(conn, rw.Reader)\n\n\tif !isRaw {\n\t\tconn = newWebsocketConn(conn, ws.StateServerSide)\n\t\t// websocketConn can't correct handle ReadDeadline\n\t\t// so call N.NewDeadlineConn to add a safe wrapper\n\t\tconn = N.NewDeadlineConn(conn)\n\t}\n\n\tif edBuf := decodeXray0rtt(r.Header); len(edBuf) > 0 {\n\t\tappendOk := false\n\t\tif bufConn, ok := conn.(*N.BufferedConn); ok {\n\t\t\tappendOk = bufConn.AppendData(edBuf)\n\t\t}\n\t\tif !appendOk {\n\t\t\tconn = N.NewCachedConn(conn, edBuf)\n\t\t}\n\n\t}\n\n\treturn conn, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/vmess/websocket_go120.go",
    "content": "//go:build !go1.21\n\npackage vmess\n\n// Golang1.20's net.http Flush will mistakenly call w.WriteHeader(StatusOK) internally after w.WriteHeader(http.StatusSwitchingProtocols)\n// https://github.com/golang/go/issues/59564\nconst writeHeaderShouldFlush = false\n"
  },
  {
    "path": "core/Clash.Meta/transport/vmess/websocket_go121.go",
    "content": "//go:build go1.21\n\npackage vmess\n\nconst writeHeaderShouldFlush = true\n"
  },
  {
    "path": "core/Clash.Meta/transport/xhttp/browser.go",
    "content": "package xhttp\n\nimport (\n\t\"math\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/randv2\"\n)\n\n// The Chrome version generator will suffer from deviation of a normal distribution.\n\nfunc ChromeVersion() int {\n\t// Start from Chrome 144, released on 2026.1.13.\n\tvar startVersion int = 144\n\tvar timeStart int64 = time.Date(2026, 1, 13, 0, 0, 0, 0, time.UTC).Unix() / 86400\n\tvar timeCurrent int64 = time.Now().Unix() / 86400\n\tvar timeDiff int = int((timeCurrent - timeStart - 35)) - int(math.Floor(math.Pow(randv2.Float64(), 2)*105))\n\treturn startVersion + (timeDiff / 35) // It's 31.15 currently.\n}\n\nvar safariMinorMap [25]int = [25]int{0, 0, 0, 1, 1,\n\t1, 2, 2, 2, 2, 3, 3, 3, 4, 4,\n\t4, 5, 5, 5, 5, 5, 6, 6, 6, 6}\n\n// The following version generators use deterministic generators, but with the distribution scaled by a curve.\n\nfunc CurlVersion() string {\n\t// curl 8.0.0 was released on 20/03/2023.\n\tvar timeCurrent int64 = time.Now().Unix() / 86400\n\tvar timeStart int64 = time.Date(2023, 3, 20, 0, 0, 0, 0, time.UTC).Unix() / 86400\n\tvar timeDiff int = int((timeCurrent - timeStart - 60)) - int(math.Floor(math.Pow(randv2.Float64(), 2)*165))\n\tvar minorValue int = int(timeDiff / 57) // The release cadence is actually 56.67 days.\n\treturn \"8.\" + strconv.Itoa(minorValue) + \".0\"\n}\nfunc FirefoxVersion() int {\n\t// Firefox 128 ESR was released on 09/07/2023.\n\tvar timeCurrent int64 = time.Now().Unix() / 86400\n\tvar timeStart int64 = time.Date(2024, 7, 29, 0, 0, 0, 0, time.UTC).Unix() / 86400\n\tvar timeDiff = timeCurrent - timeStart - 25 - int64(math.Floor(math.Pow(randv2.Float64(), 2)*50))\n\treturn int(timeDiff/30) + 128\n}\nfunc SafariVersion() string {\n\tvar anchoredTime time.Time = time.Now()\n\tvar releaseYear int = anchoredTime.Year()\n\tvar splitPoint time.Time = time.Date(releaseYear, 9, 23, 0, 0, 0, 0, time.UTC)\n\tvar delayedDays = int(math.Floor(math.Pow(randv2.Float64(), 3) * 75))\n\tsplitPoint = splitPoint.AddDate(0, 0, delayedDays)\n\tif anchoredTime.Compare(splitPoint) < 0 {\n\t\treleaseYear--\n\t\tsplitPoint = time.Date(releaseYear, 9, 23, 0, 0, 0, 0, time.UTC)\n\t\tsplitPoint = splitPoint.AddDate(0, 0, delayedDays)\n\t}\n\tvar minorVersion = safariMinorMap[(anchoredTime.Unix()-splitPoint.Unix())/1296000]\n\treturn strconv.Itoa(releaseYear-1999) + \".\" + strconv.Itoa(minorVersion)\n}\n\n// The full Chromium brand GREASE implementation\nvar clientHintGreaseNA = []string{\" \", \"(\", \":\", \"-\", \".\", \"/\", \")\", \";\", \"=\", \"?\", \"_\"}\nvar clientHintVersionNA = []string{\"8\", \"99\", \"24\"}\nvar clientHintShuffle3 = [][3]int{{0, 1, 2}, {0, 2, 1}, {1, 0, 2}, {1, 2, 0}, {2, 0, 1}, {2, 1, 0}}\nvar clientHintShuffle4 = [][4]int{\n\t{0, 1, 2, 3}, {0, 1, 3, 2}, {0, 2, 1, 3}, {0, 2, 3, 1}, {0, 3, 1, 2}, {0, 3, 2, 1},\n\t{1, 0, 2, 3}, {1, 0, 3, 2}, {1, 2, 0, 3}, {1, 2, 3, 0}, {1, 3, 0, 2}, {1, 3, 2, 0},\n\t{2, 0, 1, 3}, {2, 0, 3, 1}, {2, 1, 0, 3}, {2, 1, 3, 0}, {2, 3, 0, 1}, {2, 3, 1, 0},\n\t{3, 0, 1, 2}, {3, 0, 2, 1}, {3, 1, 0, 2}, {3, 1, 2, 0}, {3, 2, 0, 1}, {3, 2, 1, 0}}\n\nfunc getGreasedChInvalidBrand(seed int) string {\n\treturn \"\\\"Not\" + clientHintGreaseNA[seed%len(clientHintGreaseNA)] + \"A\" + clientHintGreaseNA[(seed+1)%len(clientHintGreaseNA)] + \"Brand\\\";v=\\\"\" + clientHintVersionNA[seed%len(clientHintVersionNA)] + \"\\\"\"\n}\nfunc getGreasedChOrder(brandLength int, seed int) []int {\n\tswitch brandLength {\n\tcase 1:\n\t\treturn []int{0}\n\tcase 2:\n\t\treturn []int{seed % brandLength, (seed + 1) % brandLength}\n\tcase 3:\n\t\treturn clientHintShuffle3[seed%len(clientHintShuffle3)][:]\n\tdefault:\n\t\treturn clientHintShuffle4[seed%len(clientHintShuffle4)][:]\n\t}\n\t//return []int{}\n}\nfunc getUngreasedChUa(majorVersion int, forkName string) []string {\n\t// Set the capacity to 4, the maximum allowed brand size, so Go will never allocate memory twice\n\tbaseChUa := make([]string, 0, 4)\n\tbaseChUa = append(baseChUa, getGreasedChInvalidBrand(majorVersion),\n\t\t\"\\\"Chromium\\\";v=\\\"\"+strconv.Itoa(majorVersion)+\"\\\"\")\n\tswitch forkName {\n\tcase \"chrome\":\n\t\tbaseChUa = append(baseChUa, \"\\\"Google Chrome\\\";v=\\\"\"+strconv.Itoa(majorVersion)+\"\\\"\")\n\tcase \"edge\":\n\t\tbaseChUa = append(baseChUa, \"\\\"Microsoft Edge\\\";v=\\\"\"+strconv.Itoa(majorVersion)+\"\\\"\")\n\t}\n\treturn baseChUa\n}\nfunc getGreasedChUa(majorVersion int, forkName string) string {\n\tungreasedCh := getUngreasedChUa(majorVersion, forkName)\n\tshuffleMap := getGreasedChOrder(len(ungreasedCh), majorVersion)\n\tshuffledCh := make([]string, len(ungreasedCh))\n\tfor i, e := range shuffleMap {\n\t\tshuffledCh[e] = ungreasedCh[i]\n\t}\n\treturn strings.Join(shuffledCh, \", \")\n}\n\n// The code below provides a coherent default browser user agent string based on a CPU-seeded PRNG.\n\nvar CurlUA = \"curl/\" + CurlVersion()\nvar AnchoredFirefoxVersion = strconv.Itoa(FirefoxVersion())\nvar FirefoxUA = \"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:\" + AnchoredFirefoxVersion + \".0) Gecko/20100101 Firefox/\" + AnchoredFirefoxVersion + \".0\"\nvar SafariUA = \"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/\" + SafariVersion() + \" Safari/605.1.15\"\n\n// Chromium browsers.\n\nvar AnchoredChromeVersion = ChromeVersion()\nvar ChromeUA = \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/\" + strconv.Itoa(AnchoredChromeVersion) + \".0.0.0 Safari/537.36\"\nvar ChromeUACH = getGreasedChUa(AnchoredChromeVersion, \"chrome\")\nvar MSEdgeUA = ChromeUA + \"Edg/\" + strconv.Itoa(AnchoredChromeVersion) + \".0.0.0\"\nvar MSEdgeUACH = getGreasedChUa(AnchoredChromeVersion, \"edge\")\n\nfunc applyMasqueradedHeaders(header http.Header, browser string, variant string) {\n\t// Browser-specific.\n\tswitch browser {\n\tcase \"chrome\":\n\t\theader[\"Sec-CH-UA\"] = []string{ChromeUACH}\n\t\theader[\"Sec-CH-UA-Mobile\"] = []string{\"?0\"}\n\t\theader[\"Sec-CH-UA-Platform\"] = []string{\"\\\"Windows\\\"\"}\n\t\theader[\"DNT\"] = []string{\"1\"}\n\t\theader.Set(\"User-Agent\", ChromeUA)\n\t\theader.Set(\"Accept-Language\", \"en-US,en;q=0.9\")\n\tcase \"edge\":\n\t\theader[\"Sec-CH-UA\"] = []string{MSEdgeUACH}\n\t\theader[\"Sec-CH-UA-Mobile\"] = []string{\"?0\"}\n\t\theader[\"Sec-CH-UA-Platform\"] = []string{\"\\\"Windows\\\"\"}\n\t\theader[\"DNT\"] = []string{\"1\"}\n\t\theader.Set(\"User-Agent\", MSEdgeUA)\n\t\theader.Set(\"Accept-Language\", \"en-US,en;q=0.9\")\n\tcase \"firefox\":\n\t\theader.Set(\"User-Agent\", FirefoxUA)\n\t\theader[\"DNT\"] = []string{\"1\"}\n\t\theader.Set(\"Accept-Language\", \"en-US,en;q=0.5\")\n\tcase \"safari\":\n\t\theader.Set(\"User-Agent\", SafariUA)\n\t\theader.Set(\"Accept-Language\", \"en-US,en;q=0.9\")\n\tcase \"golang\":\n\t\t// Expose the default net/http header.\n\t\theader.Del(\"User-Agent\")\n\t\treturn\n\tcase \"curl\":\n\t\theader.Set(\"User-Agent\", CurlUA)\n\t\treturn\n\t}\n\t// Context-specific.\n\tswitch variant {\n\tcase \"nav\":\n\t\tif header.Get(\"Cache-Control\") == \"\" {\n\t\t\tswitch browser {\n\t\t\tcase \"chrome\", \"edge\":\n\t\t\t\theader.Set(\"Cache-Control\", \"max-age=0\")\n\t\t\t}\n\t\t}\n\t\theader.Set(\"Upgrade-Insecure-Requests\", \"1\")\n\t\tif header.Get(\"Accept\") == \"\" {\n\t\t\tswitch browser {\n\t\t\tcase \"chrome\", \"edge\":\n\t\t\t\theader.Set(\"Accept\", \"text/html,application/xhtml+xml,application/xml;q=0.9,image/jxl,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7\")\n\t\t\tcase \"firefox\", \"safari\":\n\t\t\t\theader.Set(\"Accept\", \"text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8\")\n\t\t\t}\n\t\t}\n\t\theader.Set(\"Sec-Fetch-Site\", \"none\")\n\t\theader.Set(\"Sec-Fetch-Mode\", \"navigate\")\n\t\tswitch browser {\n\t\tcase \"safari\":\n\t\tdefault:\n\t\t\theader.Set(\"Sec-Fetch-User\", \"?1\")\n\t\t}\n\t\theader.Set(\"Sec-Fetch-Dest\", \"document\")\n\t\theader.Set(\"Priority\", \"u=0, i\")\n\tcase \"ws\":\n\t\theader.Set(\"Sec-Fetch-Mode\", \"websocket\")\n\t\tswitch browser {\n\t\tcase \"safari\":\n\t\t\t// Safari is NOT web-compliant here!\n\t\t\theader.Set(\"Sec-Fetch-Dest\", \"websocket\")\n\t\tdefault:\n\t\t\theader.Set(\"Sec-Fetch-Dest\", \"empty\")\n\t\t}\n\t\theader.Set(\"Sec-Fetch-Site\", \"same-origin\")\n\t\tif header.Get(\"Cache-Control\") == \"\" {\n\t\t\theader.Set(\"Cache-Control\", \"no-cache\")\n\t\t}\n\t\tif header.Get(\"Pragma\") == \"\" {\n\t\t\theader.Set(\"Pragma\", \"no-cache\")\n\t\t}\n\t\tif header.Get(\"Accept\") == \"\" {\n\t\t\theader.Set(\"Accept\", \"*/*\")\n\t\t}\n\tcase \"fetch\":\n\t\theader.Set(\"Sec-Fetch-Mode\", \"cors\")\n\t\theader.Set(\"Sec-Fetch-Dest\", \"empty\")\n\t\theader.Set(\"Sec-Fetch-Site\", \"same-origin\")\n\t\tif header.Get(\"Priority\") == \"\" {\n\t\t\tswitch browser {\n\t\t\tcase \"chrome\", \"edge\":\n\t\t\t\theader.Set(\"Priority\", \"u=1, i\")\n\t\t\tcase \"firefox\":\n\t\t\t\theader.Set(\"Priority\", \"u=4\")\n\t\t\tcase \"safari\":\n\t\t\t\theader.Set(\"Priority\", \"u=3, i\")\n\t\t\t}\n\t\t}\n\t\tif header.Get(\"Cache-Control\") == \"\" {\n\t\t\theader.Set(\"Cache-Control\", \"no-cache\")\n\t\t}\n\t\tif header.Get(\"Pragma\") == \"\" {\n\t\t\theader.Set(\"Pragma\", \"no-cache\")\n\t\t}\n\t\tif header.Get(\"Accept\") == \"\" {\n\t\t\theader.Set(\"Accept\", \"*/*\")\n\t\t}\n\t}\n}\n\nfunc TryDefaultHeadersWith(header http.Header, variant string) {\n\t// The global UA special value handler for transports. Used to be called HandleTransportUASettings.\n\t// Just a FYI to whoever needing to fix this piece of code after some spontaneous event, I tried to make the two methods separate to let the code be cleaner and more organized.\n\tif len(header.Values(\"User-Agent\")) < 1 {\n\t\tapplyMasqueradedHeaders(header, \"chrome\", variant)\n\t} else {\n\t\tswitch header.Get(\"User-Agent\") {\n\t\tcase \"chrome\":\n\t\t\tapplyMasqueradedHeaders(header, \"chrome\", variant)\n\t\tcase \"firefox\":\n\t\t\tapplyMasqueradedHeaders(header, \"firefox\", variant)\n\t\tcase \"safari\":\n\t\t\tapplyMasqueradedHeaders(header, \"safari\", variant)\n\t\tcase \"edge\":\n\t\t\tapplyMasqueradedHeaders(header, \"edge\", variant)\n\t\tcase \"curl\":\n\t\t\tapplyMasqueradedHeaders(header, \"curl\", variant)\n\t\tcase \"golang\":\n\t\t\tapplyMasqueradedHeaders(header, \"golang\", variant)\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/xhttp/client.go",
    "content": "package xhttp\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"crypto/rand\"\n\t\"encoding/hex\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/contextutils\"\n\t\"github.com/metacubex/mihomo/common/httputils\"\n\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/http/httptrace\"\n\t\"github.com/metacubex/quic-go\"\n\t\"github.com/metacubex/quic-go/http3\"\n\t\"github.com/metacubex/tls\"\n)\n\n// ConnIdleTimeout defines the maximum time an idle TCP session can survive in the tunnel,\n// so it should be consistent across HTTP versions and with other transports.\nconst ConnIdleTimeout = 300 * time.Second\n\n// QuicgoH3KeepAlivePeriod consistent with quic-go\nconst QuicgoH3KeepAlivePeriod = 10 * time.Second\n\n// ChromeH2KeepAlivePeriod consistent with chrome\nconst ChromeH2KeepAlivePeriod = 45 * time.Second\n\ntype DialRawFunc func(ctx context.Context) (net.Conn, error)\ntype WrapTLSFunc func(ctx context.Context, conn net.Conn, isH2 bool) (net.Conn, error)\ntype DialQUICFunc func(ctx context.Context, cfg *quic.Config) (*quic.Conn, error)\n\ntype TransportMaker func() http.RoundTripper\n\ntype PacketUpWriter struct {\n\tctx                  context.Context\n\tcancel               context.CancelFunc\n\tcfg                  *Config\n\tscMaxEachPostBytes   int\n\tscMinPostsIntervalMs Range\n\tsessionID            string\n\ttransport            http.RoundTripper\n\twriteMu              sync.Mutex\n\twriteCond            sync.Cond\n\tseq                  uint64\n\tbuf                  []byte\n\ttimer                *time.Timer\n\tflushErr             error\n}\n\nfunc (c *PacketUpWriter) Write(b []byte) (int, error) {\n\tc.writeMu.Lock()\n\tdefer c.writeMu.Unlock()\n\n\tif err := c.flushErr; err != nil {\n\t\treturn 0, err\n\t}\n\n\tdata := bytes.NewBuffer(b)\n\tfor data.Len() > 0 {\n\t\tif c.timer == nil { // start a timer to flush the buffer\n\t\t\tc.timer = time.AfterFunc(time.Duration(c.scMinPostsIntervalMs.Rand())*time.Millisecond, c.flush)\n\t\t}\n\n\t\tc.buf = append(c.buf, data.Next(c.scMaxEachPostBytes-len(c.buf))...) // let buffer fill up to scMaxEachPostBytes\n\n\t\tif len(c.buf) >= c.scMaxEachPostBytes { // too much data in buffer, wait the flush complete\n\t\t\tc.writeCond.Wait()\n\t\t\tif err := c.flushErr; err != nil {\n\t\t\t\treturn 0, err\n\t\t\t}\n\t\t}\n\t}\n\treturn len(b), nil\n}\n\nfunc (c *PacketUpWriter) flush() {\n\tc.writeMu.Lock()\n\tdefer c.writeMu.Unlock()\n\n\tdefer c.writeCond.Broadcast() // wake up the waited Write() call\n\n\tif c.timer != nil {\n\t\tc.timer.Stop()\n\t\tc.timer = nil\n\t}\n\n\tif c.flushErr != nil {\n\t\treturn\n\t}\n\n\tif len(c.buf) == 0 {\n\t\treturn\n\t}\n\t_, err := c.write(c.buf)\n\tc.buf = c.buf[:0] // reset buffer\n\tif err != nil {\n\t\tc.flushErr = err\n\t\treturn\n\t}\n}\n\nfunc (c *PacketUpWriter) write(b []byte) (int, error) {\n\tu := url.URL{\n\t\tScheme: \"https\",\n\t\tHost:   c.cfg.Host,\n\t\tPath:   c.cfg.NormalizedPath(),\n\t}\n\n\treq, err := http.NewRequestWithContext(c.ctx, c.cfg.GetNormalizedUplinkHTTPMethod(), u.String(), nil)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\n\tseqStr := strconv.FormatUint(c.seq, 10)\n\tc.seq++\n\n\tif err := c.cfg.FillPacketRequest(req, c.sessionID, seqStr, b); err != nil {\n\t\treturn 0, err\n\t}\n\treq.Host = c.cfg.Host\n\n\tresp, err := c.transport.RoundTrip(req)\n\tif err != nil {\n\t\treturn 0, err\n\t}\n\tdefer resp.Body.Close()\n\t_, _ = io.Copy(io.Discard, resp.Body)\n\n\tif resp.StatusCode != http.StatusOK {\n\t\treturn 0, fmt.Errorf(\"xhttp packet-up bad status: %s\", resp.Status)\n\t}\n\n\treturn len(b), nil\n}\n\nfunc (c *PacketUpWriter) Close() error {\n\tch := make(chan struct{})\n\tgo func() { // flush in the background\n\t\tdefer close(ch)\n\t\tc.flush()\n\t}()\n\tselect {\n\tcase <-ch:\n\tcase <-time.After(time.Second):\n\t}\n\tc.cancel()\n\thttputils.CloseTransport(c.transport)\n\treturn nil\n}\n\nfunc NewTransport(dialRaw DialRawFunc, wrapTLS WrapTLSFunc, dialQUIC DialQUICFunc, alpn []string, keepAlivePeriod time.Duration) http.RoundTripper {\n\tif len(alpn) == 1 && alpn[0] == \"h3\" { // `alpn: [h3]` means using h3 mode\n\t\tif keepAlivePeriod == 0 {\n\t\t\tkeepAlivePeriod = QuicgoH3KeepAlivePeriod\n\t\t}\n\t\tif keepAlivePeriod < 0 {\n\t\t\tkeepAlivePeriod = 0\n\t\t}\n\t\treturn &http3.Transport{\n\t\t\tQUICConfig: &quic.Config{\n\t\t\t\tMaxIncomingStreams: -1, // don't allow the server to create bidirectional streams\n\t\t\t\tKeepAlivePeriod:    keepAlivePeriod,\n\t\t\t\tMaxIdleTimeout:     ConnIdleTimeout,\n\t\t\t},\n\t\t\tDial: func(ctx context.Context, addr string, tlsCfg *tls.Config, cfg *quic.Config) (*quic.Conn, error) {\n\t\t\t\treturn dialQUIC(ctx, cfg)\n\t\t\t},\n\t\t}\n\t}\n\tif len(alpn) == 1 && alpn[0] == \"http/1.1\" { // `alpn: [http/1.1]` means using http/1.1 mode\n\t\tdialContext := func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\t\traw, err := dialRaw(ctx)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\twrapped, err := wrapTLS(ctx, raw, false)\n\t\t\tif err != nil {\n\t\t\t\t_ = raw.Close()\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn wrapped, nil\n\t\t}\n\t\treturn &http.Transport{\n\t\t\tDialContext:       dialContext,\n\t\t\tDialTLSContext:    dialContext,\n\t\t\tIdleConnTimeout:   ConnIdleTimeout,\n\t\t\tForceAttemptHTTP2: false, // only http/1.1\n\t\t}\n\t}\n\tif keepAlivePeriod == 0 {\n\t\tkeepAlivePeriod = ChromeH2KeepAlivePeriod\n\t}\n\tif keepAlivePeriod < 0 {\n\t\tkeepAlivePeriod = 0\n\t}\n\t// use h2c mode to disallow the net/http fallback to http1.1\n\tprotocols := new(http.Protocols)\n\tprotocols.SetUnencryptedHTTP2(true)\n\treturn &http.Transport{\n\t\tDialTLSContext: func(ctx context.Context, network, addr string) (net.Conn, error) {\n\t\t\traw, err := dialRaw(ctx)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\twrapped, err := wrapTLS(ctx, raw, true)\n\t\t\tif err != nil {\n\t\t\t\t_ = raw.Close()\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\treturn wrapped, nil\n\t\t},\n\t\tIdleConnTimeout: ConnIdleTimeout,\n\t\tProtocols:       protocols,\n\t\tHTTP2: &http.HTTP2Config{\n\t\t\tSendPingTimeout: keepAlivePeriod,\n\t\t},\n\t}\n}\n\ntype Client struct {\n\tctx                   context.Context\n\tcancel                context.CancelFunc\n\tmode                  string\n\tcfg                   *Config\n\tscMaxEachPostBytes    Range\n\tscMinPostsIntervalMs  Range\n\tmakeTransport         TransportMaker\n\tmakeDownloadTransport TransportMaker\n\tuploadManager         *ReuseManager\n\tdownloadManager       *ReuseManager\n}\n\nfunc NewClient(cfg *Config, makeTransport TransportMaker, makeDownloadTransport TransportMaker, hasReality bool) (*Client, error) {\n\tmode := cfg.EffectiveMode(hasReality)\n\tswitch mode {\n\tcase \"stream-one\", \"stream-up\", \"packet-up\":\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"xhttp mode %s is not implemented yet\", mode)\n\t}\n\tscMaxEachPostBytes, err := cfg.GetNormalizedScMaxEachPostBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tscMinPostsIntervalMs, err := cfg.GetNormalizedScMinPostsIntervalMs()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tctx, cancel := context.WithCancel(context.Background())\n\n\tclient := &Client{\n\t\tmode:                  mode,\n\t\tcfg:                   cfg,\n\t\tscMaxEachPostBytes:    scMaxEachPostBytes,\n\t\tscMinPostsIntervalMs:  scMinPostsIntervalMs,\n\t\tmakeTransport:         makeTransport,\n\t\tmakeDownloadTransport: makeDownloadTransport,\n\t\tctx:                   ctx,\n\t\tcancel:                cancel,\n\t}\n\tif cfg.ReuseConfig != nil {\n\t\tclient.uploadManager, err = NewReuseManager(cfg.ReuseConfig, makeTransport)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tclient.makeTransport = client.uploadManager.GetTransport\n\t\tif cfg.DownloadConfig != nil {\n\t\t\tif makeDownloadTransport == nil {\n\t\t\t\treturn nil, fmt.Errorf(\"xhttp: download manager requires download transport maker\")\n\t\t\t}\n\t\t\tclient.downloadManager, err = NewReuseManager(cfg.DownloadConfig.ReuseConfig, makeDownloadTransport)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tclient.makeDownloadTransport = client.downloadManager.GetTransport\n\t\t}\n\t}\n\treturn client, nil\n}\n\nfunc (c *Client) Close() error {\n\tc.cancel()\n\tvar errs []error\n\tif c.uploadManager != nil {\n\t\terr := c.uploadManager.Close()\n\t\tif err != nil {\n\t\t\terrs = append(errs, err)\n\t\t}\n\t}\n\tif c.downloadManager != nil {\n\t\terr := c.downloadManager.Close()\n\t\tif err != nil {\n\t\t\terrs = append(errs, err)\n\t\t}\n\t}\n\treturn errors.Join(errs...)\n}\n\nfunc (c *Client) Dial(ctx context.Context) (net.Conn, error) {\n\tswitch c.mode {\n\tcase \"stream-one\":\n\t\treturn c.DialStreamOne(ctx)\n\tcase \"stream-up\":\n\t\treturn c.DialStreamUp(ctx)\n\tcase \"packet-up\":\n\t\treturn c.DialPacketUp(ctx)\n\tdefault:\n\t\treturn nil, fmt.Errorf(\"xhttp mode %s is not implemented yet\", c.mode)\n\t}\n}\n\n// onlyRoundTripper is a wrapper that prevents the underlying transport from being closed.\ntype onlyRoundTripper struct {\n\thttp.RoundTripper\n}\n\nfunc (c *Client) getTransport() (uploadTransport http.RoundTripper, downloadTransport http.RoundTripper, err error) {\n\tuploadTransport = c.makeTransport()\n\tdownloadTransport = onlyRoundTripper{uploadTransport}\n\tif c.makeDownloadTransport != nil {\n\t\tdownloadTransport = c.makeDownloadTransport()\n\t}\n\treturn\n}\n\nfunc (c *Client) DialStreamOne(ctx context.Context) (net.Conn, error) {\n\ttransport, _, err := c.getTransport()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\trequestURL := url.URL{\n\t\tScheme: \"https\",\n\t\tHost:   c.cfg.Host,\n\t\tPath:   c.cfg.NormalizedPath(),\n\t}\n\tpr, pw := io.Pipe()\n\n\tconn := &Conn{writer: pw}\n\n\t// Use gotConn to detect when TCP connection is established, so we can\n\t// return the conn immediately without waiting for the HTTP response.\n\t// This breaks the deadlock where CDN buffers response headers until the\n\t// server sends body data, but the server waits for our request body,\n\t// which can't be sent because we haven't returned the conn yet.\n\tgotConn := make(chan bool, 1)\n\n\treqCtx, reqCancel := context.WithCancel(c.ctx) // reqCtx must alive during conn not closed\n\tstop := contextutils.AfterFunc(ctx, reqCancel) // temporarily connect ctx with reqCtx when dialing\n\tdefer stop()                                   // disconnect ctx with reqCtx after dialing\n\n\taddrCtx := httputils.NewAddrContext(&conn.NetAddr, reqCtx)\n\tstreamCtx := httptrace.WithClientTrace(addrCtx, &httptrace.ClientTrace{\n\t\tGotConn: func(info httptrace.GotConnInfo) {\n\t\t\tselect {\n\t\t\tcase gotConn <- true:\n\t\t\tdefault: // GotConn maybe called multiple times, ignore the second and later calls\n\t\t\t}\n\t\t},\n\t})\n\n\treq, err := http.NewRequestWithContext(streamCtx, c.cfg.GetNormalizedUplinkHTTPMethod(), requestURL.String(), pr)\n\tif err != nil {\n\t\t_ = pr.Close()\n\t\t_ = pw.Close()\n\t\thttputils.CloseTransport(transport)\n\t\treqCancel()\n\t\treturn nil, err\n\t}\n\treq.Host = c.cfg.Host\n\n\tif err = c.cfg.FillStreamRequest(req, \"\"); err != nil {\n\t\t_ = pr.Close()\n\t\t_ = pw.Close()\n\t\thttputils.CloseTransport(transport)\n\t\treqCancel()\n\t\treturn nil, err\n\t}\n\n\twrc := NewWaitReadCloser()\n\n\tgo func() {\n\t\tresp, err := transport.RoundTrip(req)\n\t\tif err != nil {\n\t\t\twrc.CloseWithError(err)\n\t\t\tclose(gotConn)\n\t\t\treturn\n\t\t}\n\t\tif resp.StatusCode < 200 || resp.StatusCode >= 300 {\n\t\t\t_ = resp.Body.Close()\n\t\t\twrc.CloseWithError(fmt.Errorf(\"xhttp stream-one bad status: %s\", resp.Status))\n\t\t\treturn\n\t\t}\n\t\twrc.Set(resp.Body)\n\t}()\n\n\tif !<-gotConn {\n\t\t// RoundTrip failed before TCP connected (e.g. DNS failure)\n\t\t_ = pr.Close()\n\t\t_ = pw.Close()\n\t\thttputils.CloseTransport(transport)\n\t\treqCancel()\n\t\tvar buf [0]byte\n\t\t_, err = wrc.Read(buf[:])\n\t\treturn nil, err\n\t}\n\n\tconn.reader = wrc\n\tconn.onClose = func() {\n\t\t_ = pr.Close()\n\t\thttputils.CloseTransport(transport)\n\t\treqCancel()\n\t}\n\n\treturn conn, nil\n}\n\nfunc (c *Client) DialStreamUp(ctx context.Context) (net.Conn, error) {\n\tuploadTransport, downloadTransport, err := c.getTransport()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdownloadCfg := c.cfg\n\tif ds := c.cfg.DownloadConfig; ds != nil {\n\t\tdownloadCfg = ds\n\t}\n\n\tstreamURL := url.URL{\n\t\tScheme: \"https\",\n\t\tHost:   c.cfg.Host,\n\t\tPath:   c.cfg.NormalizedPath(),\n\t}\n\n\tdownloadURL := url.URL{\n\t\tScheme: \"https\",\n\t\tHost:   downloadCfg.Host,\n\t\tPath:   downloadCfg.NormalizedPath(),\n\t}\n\tpr, pw := io.Pipe()\n\n\tconn := &Conn{writer: pw}\n\n\tsessionID := newSessionID()\n\n\t// Async download: avoid blocking on CDN response header buffering\n\tgotConn := make(chan bool, 1)\n\n\treqCtx, reqCancel := context.WithCancel(c.ctx) // reqCtx must alive during conn not closed\n\tstop := contextutils.AfterFunc(ctx, reqCancel) // temporarily connect ctx with reqCtx when dialing\n\tdefer stop()                                   // disconnect ctx with reqCtx after dialing\n\n\taddrCtx := httputils.NewAddrContext(&conn.NetAddr, reqCtx)\n\tdownloadCtx := httptrace.WithClientTrace(addrCtx, &httptrace.ClientTrace{\n\t\tGotConn: func(info httptrace.GotConnInfo) {\n\t\t\tselect {\n\t\t\tcase gotConn <- true:\n\t\t\tdefault: // GotConn maybe called multiple times, ignore the second and later calls\n\t\t\t}\n\t\t},\n\t})\n\n\tdownloadReq, err := http.NewRequestWithContext(\n\t\tdownloadCtx,\n\t\thttp.MethodGet,\n\t\tdownloadURL.String(),\n\t\tnil,\n\t)\n\tif err != nil {\n\t\thttputils.CloseTransport(uploadTransport)\n\t\thttputils.CloseTransport(downloadTransport)\n\t\treqCancel()\n\t\treturn nil, err\n\t}\n\n\tif err := downloadCfg.FillDownloadRequest(downloadReq, sessionID); err != nil {\n\t\thttputils.CloseTransport(uploadTransport)\n\t\thttputils.CloseTransport(downloadTransport)\n\t\treqCancel()\n\t\treturn nil, err\n\t}\n\tdownloadReq.Host = downloadCfg.Host\n\n\tuploadReq, err := http.NewRequestWithContext(\n\t\treqCtx,\n\t\tc.cfg.GetNormalizedUplinkHTTPMethod(),\n\t\tstreamURL.String(),\n\t\tpr,\n\t)\n\tif err != nil {\n\t\thttputils.CloseTransport(uploadTransport)\n\t\thttputils.CloseTransport(downloadTransport)\n\t\treqCancel()\n\t\treturn nil, err\n\t}\n\n\tif err = c.cfg.FillStreamRequest(uploadReq, sessionID); err != nil {\n\t\thttputils.CloseTransport(uploadTransport)\n\t\thttputils.CloseTransport(downloadTransport)\n\t\treqCancel()\n\t\treturn nil, err\n\t}\n\tuploadReq.Host = c.cfg.Host\n\n\twrc := NewWaitReadCloser()\n\n\tgo func() {\n\t\tresp, err := downloadTransport.RoundTrip(downloadReq)\n\t\tif err != nil {\n\t\t\twrc.CloseWithError(err)\n\t\t\tclose(gotConn)\n\t\t\treturn\n\t\t}\n\t\tif resp.StatusCode != http.StatusOK {\n\t\t\t_ = resp.Body.Close()\n\t\t\twrc.CloseWithError(fmt.Errorf(\"xhttp stream-up download bad status: %s\", resp.Status))\n\t\t\treturn\n\t\t}\n\t\twrc.Set(resp.Body)\n\t}()\n\n\tif !<-gotConn {\n\t\t_ = pr.Close()\n\t\t_ = pw.Close()\n\t\thttputils.CloseTransport(uploadTransport)\n\t\thttputils.CloseTransport(downloadTransport)\n\t\treqCancel()\n\t\tvar buf [0]byte\n\t\t_, err = wrc.Read(buf[:])\n\t\treturn nil, err\n\t}\n\n\t// Start upload after download TCP is connected, so the server has likely\n\t// already processed the GET and created the session. This preserves the\n\t// original ordering (download before upload) while still being async.\n\tgo func() {\n\t\tresp, err := uploadTransport.RoundTrip(uploadReq)\n\t\tif err != nil {\n\t\t\t_ = pw.CloseWithError(err)\n\t\t\treturn\n\t\t}\n\t\tdefer resp.Body.Close()\n\t\t_, _ = io.Copy(io.Discard, resp.Body)\n\n\t\tif resp.StatusCode < 200 || resp.StatusCode >= 300 {\n\t\t\t_ = pw.CloseWithError(fmt.Errorf(\"xhttp stream-up upload bad status: %s\", resp.Status))\n\t\t}\n\t}()\n\n\tconn.reader = wrc\n\tconn.onClose = func() {\n\t\t_ = pr.Close()\n\t\thttputils.CloseTransport(uploadTransport)\n\t\thttputils.CloseTransport(downloadTransport)\n\t\treqCancel()\n\t}\n\n\treturn conn, nil\n}\n\nfunc (c *Client) DialPacketUp(ctx context.Context) (net.Conn, error) {\n\tuploadTransport, downloadTransport, err := c.getTransport()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\tdownloadCfg := c.cfg\n\tif ds := c.cfg.DownloadConfig; ds != nil {\n\t\tdownloadCfg = ds\n\t}\n\tsessionID := newSessionID()\n\n\tdownloadURL := url.URL{\n\t\tScheme: \"https\",\n\t\tHost:   downloadCfg.Host,\n\t\tPath:   downloadCfg.NormalizedPath(),\n\t}\n\n\twriterCtx, writerCancel := context.WithCancel(c.ctx)\n\twriter := &PacketUpWriter{\n\t\tctx:                  writerCtx,\n\t\tcancel:               writerCancel,\n\t\tcfg:                  c.cfg,\n\t\tscMaxEachPostBytes:   c.scMaxEachPostBytes.Rand(),\n\t\tscMinPostsIntervalMs: c.scMinPostsIntervalMs,\n\t\tsessionID:            sessionID,\n\t\ttransport:            uploadTransport,\n\t\tseq:                  0,\n\t}\n\twriter.writeCond = sync.Cond{L: &writer.writeMu}\n\tconn := &Conn{writer: writer}\n\n\t// Async download: avoid blocking on CDN response header buffering\n\tgotConn := make(chan bool, 1)\n\n\treqCtx, reqCancel := context.WithCancel(c.ctx) // reqCtx must alive during conn not closed\n\tstop := contextutils.AfterFunc(ctx, reqCancel) // temporarily connect ctx with reqCtx when dialing\n\tdefer stop()                                   // disconnect ctx with reqCtx after dialing\n\n\taddrCtx := httputils.NewAddrContext(&conn.NetAddr, reqCtx)\n\tdownloadCtx := httptrace.WithClientTrace(addrCtx, &httptrace.ClientTrace{\n\t\tGotConn: func(info httptrace.GotConnInfo) {\n\t\t\tselect {\n\t\t\tcase gotConn <- true:\n\t\t\tdefault: // GotConn maybe called multiple times, ignore the second and later calls\n\t\t\t}\n\t\t},\n\t})\n\n\tdownloadReq, err := http.NewRequestWithContext(\n\t\tdownloadCtx,\n\t\thttp.MethodGet,\n\t\tdownloadURL.String(),\n\t\tnil,\n\t)\n\tif err != nil {\n\t\thttputils.CloseTransport(uploadTransport)\n\t\thttputils.CloseTransport(downloadTransport)\n\t\treqCancel()\n\t\treturn nil, err\n\t}\n\tif err = downloadCfg.FillDownloadRequest(downloadReq, sessionID); err != nil {\n\t\thttputils.CloseTransport(uploadTransport)\n\t\thttputils.CloseTransport(downloadTransport)\n\t\treqCancel()\n\t\treturn nil, err\n\t}\n\tdownloadReq.Host = downloadCfg.Host\n\n\twrc := NewWaitReadCloser()\n\n\tgo func() {\n\t\tresp, err := downloadTransport.RoundTrip(downloadReq)\n\t\tif err != nil {\n\t\t\twrc.CloseWithError(err)\n\t\t\tclose(gotConn)\n\t\t\treturn\n\t\t}\n\t\tif resp.StatusCode != http.StatusOK {\n\t\t\t_ = resp.Body.Close()\n\t\t\twrc.CloseWithError(fmt.Errorf(\"xhttp packet-up download bad status: %s\", resp.Status))\n\t\t\treturn\n\t\t}\n\t\twrc.Set(resp.Body)\n\t}()\n\n\tif !<-gotConn {\n\t\thttputils.CloseTransport(uploadTransport)\n\t\thttputils.CloseTransport(downloadTransport)\n\t\treqCancel()\n\t\tvar buf [0]byte\n\t\t_, err = wrc.Read(buf[:])\n\t\treturn nil, err\n\t}\n\n\tconn.reader = wrc\n\tconn.onClose = func() {\n\t\t// uploadTransport already closed by writer\n\t\thttputils.CloseTransport(downloadTransport)\n\t\treqCancel()\n\t}\n\n\treturn conn, nil\n}\n\nfunc newSessionID() string {\n\tvar b [16]byte\n\t_, _ = rand.Read(b[:])\n\treturn hex.EncodeToString(b[:])\n}\n\n// WaitReadCloser is an io.ReadCloser that blocks on Read() until the underlying\n// ReadCloser is provided via Set(). This enables returning a reader immediately\n// while the actual HTTP response body is obtained asynchronously in a goroutine,\n// breaking the synchronous RoundTrip deadlock with CDN header buffering.\ntype WaitReadCloser struct {\n\twait chan struct{}\n\tonce sync.Once\n\trc   io.ReadCloser\n\terr  error\n}\n\nfunc NewWaitReadCloser() *WaitReadCloser {\n\treturn &WaitReadCloser{wait: make(chan struct{})}\n}\n\n// Set provides the underlying ReadCloser and unblocks any pending Read calls.\n// Must be called at most once. If Close was already called, rc is closed to\n// prevent leaks.\nfunc (w *WaitReadCloser) Set(rc io.ReadCloser) {\n\tw.setup(rc, nil)\n}\n\n// CloseWithError records an error and unblocks any pending Read calls.\nfunc (w *WaitReadCloser) CloseWithError(err error) {\n\tw.setup(nil, err)\n}\n\n// setup sets the underlying ReadCloser and error.\nfunc (w *WaitReadCloser) setup(rc io.ReadCloser, err error) {\n\tw.once.Do(func() {\n\t\tw.rc = rc\n\t\tw.err = err\n\t\tclose(w.wait)\n\t})\n\tif w.err != nil && rc != nil {\n\t\t_ = rc.Close()\n\t}\n}\n\nfunc (w *WaitReadCloser) Read(b []byte) (int, error) {\n\t<-w.wait\n\tif w.rc == nil {\n\t\treturn 0, w.err\n\t}\n\treturn w.rc.Read(b)\n}\n\nfunc (w *WaitReadCloser) Close() error {\n\tw.setup(nil, net.ErrClosed)\n\t<-w.wait\n\tif w.rc != nil {\n\t\treturn w.rc.Close()\n\t}\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/xhttp/config.go",
    "content": "package xhttp\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io\"\n\t\"math/rand\"\n\t\"strconv\"\n\t\"strings\"\n\n\t\"github.com/metacubex/http\"\n)\n\nconst (\n\tPlacementQueryInHeader = \"queryInHeader\"\n\tPlacementCookie        = \"cookie\"\n\tPlacementHeader        = \"header\"\n\tPlacementQuery         = \"query\"\n\tPlacementPath          = \"path\"\n\tPlacementBody          = \"body\"\n\tPlacementAuto          = \"auto\"\n)\n\ntype Config struct {\n\tHost                 string\n\tPath                 string\n\tMode                 string\n\tHeaders              map[string]string\n\tNoGRPCHeader         bool\n\tXPaddingBytes        string\n\tXPaddingObfsMode     bool\n\tXPaddingKey          string\n\tXPaddingHeader       string\n\tXPaddingPlacement    string\n\tXPaddingMethod       string\n\tUplinkHTTPMethod     string\n\tSessionPlacement     string\n\tSessionKey           string\n\tSeqPlacement         string\n\tSeqKey               string\n\tUplinkDataPlacement  string\n\tUplinkDataKey        string\n\tUplinkChunkSize      string\n\tNoSSEHeader          bool   // server only\n\tScStreamUpServerSecs string // server only\n\tScMaxBufferedPosts   string // server only\n\tScMaxEachPostBytes   string\n\tScMinPostsIntervalMs string\n\tReuseConfig          *ReuseConfig\n\tDownloadConfig       *Config\n}\n\ntype ReuseConfig struct {\n\tMaxConcurrency   string\n\tMaxConnections   string\n\tCMaxReuseTimes   string\n\tHMaxRequestTimes string\n\tHMaxReusableSecs string\n}\n\nfunc (c *Config) NormalizedMode() string {\n\tif c.Mode == \"\" {\n\t\treturn \"auto\"\n\t}\n\treturn c.Mode\n}\n\nfunc (c *Config) EffectiveMode(hasReality bool) string {\n\tmode := c.NormalizedMode()\n\tif mode != \"auto\" {\n\t\treturn mode\n\t}\n\tif hasReality {\n\t\tif c.DownloadConfig != nil {\n\t\t\treturn \"stream-up\"\n\t\t}\n\t\treturn \"stream-one\"\n\t}\n\treturn \"packet-up\"\n}\n\nfunc (c *Config) NormalizedPath() string {\n\tpath := c.Path\n\tif path == \"\" {\n\t\tpath = \"/\"\n\t}\n\tif !strings.HasPrefix(path, \"/\") {\n\t\tpath = \"/\" + path\n\t}\n\tif !strings.HasSuffix(path, \"/\") {\n\t\tpath += \"/\"\n\t}\n\treturn path\n}\n\nfunc (c *Config) GetRequestHeader() http.Header {\n\th := http.Header{}\n\tfor k, v := range c.Headers {\n\t\th.Set(k, v)\n\t}\n\tTryDefaultHeadersWith(h, \"fetch\")\n\treturn h\n}\n\nfunc (c *Config) GetRequestHeaderWithPayload(payload []byte, uplinkChunkSize Range) http.Header {\n\theader := c.GetRequestHeader()\n\n\tkey := c.UplinkDataKey\n\tencodedData := base64.RawURLEncoding.EncodeToString(payload)\n\n\tfor i := 0; len(encodedData) > 0; i++ {\n\t\tchunkSize := uplinkChunkSize.Rand()\n\t\tif len(encodedData) < chunkSize {\n\t\t\tchunkSize = len(encodedData)\n\t\t}\n\t\tchunk := encodedData[:chunkSize]\n\t\tencodedData = encodedData[chunkSize:]\n\t\theaderKey := fmt.Sprintf(\"%s-%d\", key, i)\n\t\theader.Set(headerKey, chunk)\n\t}\n\n\treturn header\n}\n\nfunc (c *Config) GetRequestCookiesWithPayload(payload []byte, uplinkChunkSize Range) []*http.Cookie {\n\tcookies := []*http.Cookie{}\n\n\tkey := c.UplinkDataKey\n\tencodedData := base64.RawURLEncoding.EncodeToString(payload)\n\n\tfor i := 0; len(encodedData) > 0; i++ {\n\t\tchunkSize := uplinkChunkSize.Rand()\n\t\tif len(encodedData) < chunkSize {\n\t\t\tchunkSize = len(encodedData)\n\t\t}\n\t\tchunk := encodedData[:chunkSize]\n\t\tencodedData = encodedData[chunkSize:]\n\t\tcookieName := fmt.Sprintf(\"%s_%d\", key, i)\n\t\tcookies = append(cookies, &http.Cookie{Name: cookieName, Value: chunk})\n\t}\n\n\treturn cookies\n}\n\nfunc (c *Config) WriteResponseHeader(writer http.ResponseWriter, requestMethod string, requestHeader http.Header) {\n\tif origin := requestHeader.Get(\"Origin\"); origin == \"\" {\n\t\twriter.Header().Set(\"Access-Control-Allow-Origin\", \"*\")\n\t} else {\n\t\t// Chrome says: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'.\n\t\twriter.Header().Set(\"Access-Control-Allow-Origin\", origin)\n\t}\n\n\tif c.GetNormalizedSessionPlacement() == PlacementCookie ||\n\t\tc.GetNormalizedSeqPlacement() == PlacementCookie ||\n\t\tc.XPaddingPlacement == PlacementCookie ||\n\t\tc.GetNormalizedUplinkDataPlacement() == PlacementCookie {\n\t\twriter.Header().Set(\"Access-Control-Allow-Credentials\", \"true\")\n\t}\n\n\tif requestMethod == \"OPTIONS\" {\n\t\trequestedMethod := requestHeader.Get(\"Access-Control-Request-Method\")\n\t\tif requestedMethod != \"\" {\n\t\t\twriter.Header().Set(\"Access-Control-Allow-Methods\", requestedMethod)\n\t\t} else {\n\t\t\twriter.Header().Set(\"Access-Control-Allow-Methods\", \"*\")\n\t\t}\n\n\t\trequestedHeaders := requestHeader.Get(\"Access-Control-Request-Headers\")\n\t\tif requestedHeaders == \"\" {\n\t\t\twriter.Header().Set(\"Access-Control-Allow-Headers\", \"*\")\n\t\t} else {\n\t\t\twriter.Header().Set(\"Access-Control-Allow-Headers\", requestedHeaders)\n\t\t}\n\t}\n}\n\nfunc (c *Config) GetNormalizedUplinkHTTPMethod() string {\n\tif c.UplinkHTTPMethod == \"\" {\n\t\treturn \"POST\"\n\t}\n\treturn c.UplinkHTTPMethod\n}\n\nfunc (c *Config) GetNormalizedScStreamUpServerSecs() (Range, error) {\n\tr, err := ParseRange(c.ScStreamUpServerSecs, \"20-80\")\n\tif err != nil {\n\t\treturn Range{}, fmt.Errorf(\"invalid sc-stream-up-server-secs: %w\", err)\n\t}\n\treturn r, nil\n}\n\nfunc (c *Config) GetNormalizedScMaxBufferedPosts() (Range, error) {\n\tr, err := ParseRange(c.ScMaxBufferedPosts, \"30\")\n\tif err != nil {\n\t\treturn Range{}, fmt.Errorf(\"invalid sc-max-buffered-posts: %w\", err)\n\t}\n\tif r.Max == 0 {\n\t\treturn Range{}, fmt.Errorf(\"invalid sc-max-buffered-posts: must be greater than zero\")\n\t}\n\treturn r, nil\n}\n\nfunc (c *Config) GetNormalizedScMaxEachPostBytes() (Range, error) {\n\tr, err := ParseRange(c.ScMaxEachPostBytes, \"1000000\")\n\tif err != nil {\n\t\treturn Range{}, fmt.Errorf(\"invalid sc-max-each-post-bytes: %w\", err)\n\t}\n\tif r.Max == 0 {\n\t\treturn Range{}, fmt.Errorf(\"invalid sc-max-each-post-bytes: must be greater than zero\")\n\t}\n\treturn r, nil\n}\n\nfunc (c *Config) GetNormalizedScMinPostsIntervalMs() (Range, error) {\n\tr, err := ParseRange(c.ScMinPostsIntervalMs, \"30\")\n\tif err != nil {\n\t\treturn Range{}, fmt.Errorf(\"invalid sc-min-posts-interval-ms: %w\", err)\n\t}\n\tif r.Max == 0 {\n\t\treturn Range{}, fmt.Errorf(\"invalid sc-min-posts-interval-ms: must be greater than zero\")\n\t}\n\treturn r, nil\n}\n\nfunc (c *Config) GetNormalizedUplinkChunkSize() (Range, error) {\n\tuplinkChunkSize, err := ParseRange(c.UplinkChunkSize, \"\")\n\tif err != nil {\n\t\treturn Range{}, fmt.Errorf(\"invalid uplink-chunk-size: %w\", err)\n\t}\n\tif uplinkChunkSize.Max == 0 {\n\t\tswitch c.GetNormalizedUplinkDataPlacement() {\n\t\tcase PlacementCookie:\n\t\t\treturn Range{\n\t\t\t\tMin: 2 * 1024, // 2 KiB\n\t\t\t\tMax: 3 * 1024, // 3 KiB\n\t\t\t}, nil\n\t\tcase PlacementHeader:\n\t\t\treturn Range{\n\t\t\t\tMin: 3 * 1024, // 3 KiB\n\t\t\t\tMax: 4 * 1024, // 4 KiB\n\t\t\t}, nil\n\t\tdefault:\n\t\t\treturn c.GetNormalizedScMaxEachPostBytes()\n\t\t}\n\t} else if uplinkChunkSize.Min < 64 {\n\t\tuplinkChunkSize.Min = 64\n\t\tif uplinkChunkSize.Max < 64 {\n\t\t\tuplinkChunkSize.Max = 64\n\t\t}\n\t}\n\treturn uplinkChunkSize, nil\n}\n\nfunc (c *Config) GetNormalizedSessionPlacement() string {\n\tif c.SessionPlacement == \"\" {\n\t\treturn PlacementPath\n\t}\n\treturn c.SessionPlacement\n}\n\nfunc (c *Config) GetNormalizedSeqPlacement() string {\n\tif c.SeqPlacement == \"\" {\n\t\treturn PlacementPath\n\t}\n\treturn c.SeqPlacement\n}\n\nfunc (c *Config) GetNormalizedUplinkDataPlacement() string {\n\tif c.UplinkDataPlacement == \"\" {\n\t\treturn PlacementBody\n\t}\n\treturn c.UplinkDataPlacement\n}\n\nfunc (c *Config) GetNormalizedSessionKey() string {\n\tif c.SessionKey != \"\" {\n\t\treturn c.SessionKey\n\t}\n\tswitch c.GetNormalizedSessionPlacement() {\n\tcase PlacementHeader:\n\t\treturn \"X-Session\"\n\tcase PlacementCookie, PlacementQuery:\n\t\treturn \"x_session\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\n\nfunc (c *Config) GetNormalizedSeqKey() string {\n\tif c.SeqKey != \"\" {\n\t\treturn c.SeqKey\n\t}\n\tswitch c.GetNormalizedSeqPlacement() {\n\tcase PlacementHeader:\n\t\treturn \"X-Seq\"\n\tcase PlacementCookie, PlacementQuery:\n\t\treturn \"x_seq\"\n\tdefault:\n\t\treturn \"\"\n\t}\n}\n\ntype Range struct {\n\tMin int\n\tMax int\n}\n\nfunc (r Range) Rand() int {\n\tif r.Min == r.Max {\n\t\treturn r.Min\n\t}\n\treturn r.Min + rand.Intn(r.Max-r.Min+1)\n}\n\nfunc ParseRange(s string, fallback string) (Range, error) {\n\tif strings.TrimSpace(s) == \"\" {\n\t\treturn parseRange(fallback)\n\t}\n\treturn parseRange(s)\n}\n\nfunc parseRange(s string) (Range, error) {\n\tparts := strings.Split(strings.TrimSpace(s), \"-\")\n\tif len(parts) == 1 {\n\t\tv, err := strconv.Atoi(parts[0])\n\t\tif err != nil {\n\t\t\treturn Range{}, err\n\t\t}\n\t\treturn Range{v, v}, nil\n\t}\n\tif len(parts) != 2 {\n\t\treturn Range{}, fmt.Errorf(\"invalid range: %s\", s)\n\t}\n\n\tminVal, err := strconv.Atoi(strings.TrimSpace(parts[0]))\n\tif err != nil {\n\t\treturn Range{}, err\n\t}\n\tmaxVal, err := strconv.Atoi(strings.TrimSpace(parts[1]))\n\tif err != nil {\n\t\treturn Range{}, err\n\t}\n\tif minVal < 0 || maxVal < minVal {\n\t\treturn Range{}, fmt.Errorf(\"invalid range: %s\", s)\n\t}\n\treturn Range{minVal, maxVal}, nil\n}\n\nfunc (c *ReuseConfig) ResolveManagerConfig() (Range, Range, error) {\n\tif c == nil {\n\t\treturn Range{}, Range{}, nil\n\t}\n\n\tmaxConcurrency, err := ParseRange(c.MaxConcurrency, \"0\")\n\tif err != nil {\n\t\treturn Range{}, Range{}, fmt.Errorf(\"invalid max-concurrency: %w\", err)\n\t}\n\n\tmaxConnections, err := ParseRange(c.MaxConnections, \"0\")\n\tif err != nil {\n\t\treturn Range{}, Range{}, fmt.Errorf(\"invalid max-connections: %w\", err)\n\t}\n\n\treturn maxConcurrency, maxConnections, nil\n}\n\nfunc (c *ReuseConfig) ResolveEntryConfig() (Range, Range, Range, error) {\n\tif c == nil {\n\t\treturn Range{}, Range{}, Range{}, nil\n\t}\n\n\tcMaxReuseTimes, err := ParseRange(c.CMaxReuseTimes, \"0\")\n\tif err != nil {\n\t\treturn Range{}, Range{}, Range{}, fmt.Errorf(\"invalid c-max-reuse-times: %w\", err)\n\t}\n\n\thMaxRequestTimes, err := ParseRange(c.HMaxRequestTimes, \"0\")\n\tif err != nil {\n\t\treturn Range{}, Range{}, Range{}, fmt.Errorf(\"invalid h-max-request-times: %w\", err)\n\t}\n\n\thMaxReusableSecs, err := ParseRange(c.HMaxReusableSecs, \"0\")\n\tif err != nil {\n\t\treturn Range{}, Range{}, Range{}, fmt.Errorf(\"invalid h-max-reusable-secs: %w\", err)\n\t}\n\n\treturn cMaxReuseTimes, hMaxRequestTimes, hMaxReusableSecs, nil\n}\n\nfunc appendToPath(path, value string) string {\n\tif strings.HasSuffix(path, \"/\") {\n\t\treturn path + value\n\t}\n\treturn path + \"/\" + value\n}\n\nfunc (c *Config) ApplyMetaToRequest(req *http.Request, sessionId string, seqStr string) {\n\tsessionPlacement := c.GetNormalizedSessionPlacement()\n\tseqPlacement := c.GetNormalizedSeqPlacement()\n\tsessionKey := c.GetNormalizedSessionKey()\n\tseqKey := c.GetNormalizedSeqKey()\n\n\tif sessionId != \"\" {\n\t\tswitch sessionPlacement {\n\t\tcase PlacementPath:\n\t\t\treq.URL.Path = appendToPath(req.URL.Path, sessionId)\n\t\tcase PlacementQuery:\n\t\t\tq := req.URL.Query()\n\t\t\tq.Set(sessionKey, sessionId)\n\t\t\treq.URL.RawQuery = q.Encode()\n\t\tcase PlacementHeader:\n\t\t\treq.Header.Set(sessionKey, sessionId)\n\t\tcase PlacementCookie:\n\t\t\treq.AddCookie(&http.Cookie{Name: sessionKey, Value: sessionId})\n\t\t}\n\t}\n\n\tif seqStr != \"\" {\n\t\tswitch seqPlacement {\n\t\tcase PlacementPath:\n\t\t\treq.URL.Path = appendToPath(req.URL.Path, seqStr)\n\t\tcase PlacementQuery:\n\t\t\tq := req.URL.Query()\n\t\t\tq.Set(seqKey, seqStr)\n\t\t\treq.URL.RawQuery = q.Encode()\n\t\tcase PlacementHeader:\n\t\t\treq.Header.Set(seqKey, seqStr)\n\t\tcase PlacementCookie:\n\t\t\treq.AddCookie(&http.Cookie{Name: seqKey, Value: seqStr})\n\t\t}\n\t}\n}\n\nfunc (c *Config) ExtractMetaFromRequest(req *http.Request, path string) (sessionId string, seqStr string) {\n\tsessionPlacement := c.GetNormalizedSessionPlacement()\n\tseqPlacement := c.GetNormalizedSeqPlacement()\n\tsessionKey := c.GetNormalizedSessionKey()\n\tseqKey := c.GetNormalizedSeqKey()\n\n\tvar subpath []string\n\tpathPart := 0\n\tif sessionPlacement == PlacementPath || seqPlacement == PlacementPath {\n\t\tsubpath = strings.Split(req.URL.Path[len(path):], \"/\")\n\t}\n\n\tswitch sessionPlacement {\n\tcase PlacementPath:\n\t\tif len(subpath) > pathPart {\n\t\t\tsessionId = subpath[pathPart]\n\t\t\tpathPart += 1\n\t\t}\n\tcase PlacementQuery:\n\t\tsessionId = req.URL.Query().Get(sessionKey)\n\tcase PlacementHeader:\n\t\tsessionId = req.Header.Get(sessionKey)\n\tcase PlacementCookie:\n\t\tif cookie, e := req.Cookie(sessionKey); e == nil {\n\t\t\tsessionId = cookie.Value\n\t\t}\n\t}\n\n\tswitch seqPlacement {\n\tcase PlacementPath:\n\t\tif len(subpath) > pathPart {\n\t\t\tseqStr = subpath[pathPart]\n\t\t\tpathPart += 1\n\t\t}\n\tcase PlacementQuery:\n\t\tseqStr = req.URL.Query().Get(seqKey)\n\tcase PlacementHeader:\n\t\tseqStr = req.Header.Get(seqKey)\n\tcase PlacementCookie:\n\t\tif cookie, e := req.Cookie(seqKey); e == nil {\n\t\t\tseqStr = cookie.Value\n\t\t}\n\t}\n\n\treturn sessionId, seqStr\n}\n\nfunc (c *Config) FillStreamRequest(req *http.Request, sessionID string) error {\n\treq.Header = c.GetRequestHeader()\n\txPaddingBytes, err := c.GetNormalizedXPaddingBytes()\n\tif err != nil {\n\t\treturn err\n\t}\n\tlength := xPaddingBytes.Rand()\n\tconfig := XPaddingConfig{Length: length}\n\n\tif c.XPaddingObfsMode {\n\t\tconfig.Placement = XPaddingPlacement{\n\t\t\tPlacement: c.XPaddingPlacement,\n\t\t\tKey:       c.XPaddingKey,\n\t\t\tHeader:    c.XPaddingHeader,\n\t\t\tRawURL:    req.URL.String(),\n\t\t}\n\t\tconfig.Method = PaddingMethod(c.XPaddingMethod)\n\t} else {\n\t\tconfig.Placement = XPaddingPlacement{\n\t\t\tPlacement: PlacementQueryInHeader,\n\t\t\tKey:       \"x_padding\",\n\t\t\tHeader:    \"Referer\",\n\t\t\tRawURL:    req.URL.String(),\n\t\t}\n\t}\n\n\tc.ApplyXPaddingToRequest(req, config)\n\tc.ApplyMetaToRequest(req, sessionID, \"\")\n\n\tif req.Body != nil && !c.NoGRPCHeader { // stream-up/one\n\t\treq.Header.Set(\"Content-Type\", \"application/grpc\")\n\t}\n\n\treturn nil\n}\n\nfunc (c *Config) FillDownloadRequest(req *http.Request, sessionID string) error {\n\treturn c.FillStreamRequest(req, sessionID)\n}\n\nfunc (c *Config) FillPacketRequest(request *http.Request, sessionId string, seqStr string, data []byte) error {\n\tdataPlacement := c.GetNormalizedUplinkDataPlacement()\n\n\tif dataPlacement == PlacementBody || dataPlacement == PlacementAuto {\n\t\trequest.Header = c.GetRequestHeader()\n\t\trequest.Body = io.NopCloser(bytes.NewReader(data))\n\t\trequest.ContentLength = int64(len(data))\n\t} else {\n\t\trequest.Body = nil\n\t\trequest.ContentLength = 0\n\t\tswitch dataPlacement {\n\t\tcase PlacementHeader:\n\t\t\tuplinkChunkSize, err := c.GetNormalizedUplinkChunkSize()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\trequest.Header = c.GetRequestHeaderWithPayload(data, uplinkChunkSize)\n\t\tcase PlacementCookie:\n\t\t\trequest.Header = c.GetRequestHeader()\n\t\t\tuplinkChunkSize, err := c.GetNormalizedUplinkChunkSize()\n\t\t\tif err != nil {\n\t\t\t\treturn err\n\t\t\t}\n\t\t\tfor _, cookie := range c.GetRequestCookiesWithPayload(data, uplinkChunkSize) {\n\t\t\t\trequest.AddCookie(cookie)\n\t\t\t}\n\t\t}\n\t}\n\n\txPaddingBytes, err := c.GetNormalizedXPaddingBytes()\n\tif err != nil {\n\t\treturn err\n\t}\n\tlength := xPaddingBytes.Rand()\n\tconfig := XPaddingConfig{Length: length}\n\n\tif c.XPaddingObfsMode {\n\t\tconfig.Placement = XPaddingPlacement{\n\t\t\tPlacement: c.XPaddingPlacement,\n\t\t\tKey:       c.XPaddingKey,\n\t\t\tHeader:    c.XPaddingHeader,\n\t\t\tRawURL:    request.URL.String(),\n\t\t}\n\t\tconfig.Method = PaddingMethod(c.XPaddingMethod)\n\t} else {\n\t\tconfig.Placement = XPaddingPlacement{\n\t\t\tPlacement: PlacementQueryInHeader,\n\t\t\tKey:       \"x_padding\",\n\t\t\tHeader:    \"Referer\",\n\t\t\tRawURL:    request.URL.String(),\n\t\t}\n\t}\n\n\tc.ApplyXPaddingToRequest(request, config)\n\tc.ApplyMetaToRequest(request, sessionId, seqStr)\n\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/xhttp/conn.go",
    "content": "package xhttp\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/httputils\"\n)\n\ntype Conn struct {\n\twriter  io.WriteCloser\n\treader  io.ReadCloser\n\tonClose func()\n\thttputils.NetAddr\n\n\t// deadlines\n\tdeadline *time.Timer\n}\n\nfunc (c *Conn) Write(b []byte) (int, error) {\n\treturn c.writer.Write(b)\n}\n\nfunc (c *Conn) Read(b []byte) (int, error) {\n\treturn c.reader.Read(b)\n}\n\nfunc (c *Conn) Close() error {\n\terr := c.writer.Close()\n\terr2 := c.reader.Close()\n\tif c.onClose != nil {\n\t\tc.onClose()\n\t}\n\treturn errors.Join(err, err2)\n}\n\nfunc (c *Conn) SetReadDeadline(t time.Time) error  { return c.SetDeadline(t) }\nfunc (c *Conn) SetWriteDeadline(t time.Time) error { return c.SetDeadline(t) }\n\nfunc (c *Conn) SetDeadline(t time.Time) error {\n\tif t.IsZero() {\n\t\tif c.deadline != nil {\n\t\t\tc.deadline.Stop()\n\t\t\tc.deadline = nil\n\t\t}\n\t\treturn nil\n\t}\n\td := time.Until(t)\n\tif c.deadline != nil {\n\t\tc.deadline.Reset(d)\n\t\treturn nil\n\t}\n\tc.deadline = time.AfterFunc(d, func() {\n\t\tc.Close()\n\t})\n\treturn nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/xhttp/reuse.go",
    "content": "package xhttp\n\nimport (\n\t\"sync\"\n\t\"sync/atomic\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/httputils\"\n\n\t\"github.com/metacubex/http\"\n)\n\ntype reuseEntry struct {\n\ttransport http.RoundTripper\n\n\topenUsage     atomic.Int32\n\tleftRequests  atomic.Int32\n\treuseCount    atomic.Int32\n\tmaxReuseTimes int32\n\tunreusableAt  time.Time\n\n\tclosed atomic.Bool\n}\n\nfunc (entry *reuseEntry) isClosed() bool {\n\treturn entry.closed.Load()\n}\n\nfunc (entry *reuseEntry) close() {\n\tif !entry.closed.CompareAndSwap(false, true) {\n\t\treturn\n\t}\n\thttputils.CloseTransport(entry.transport)\n}\n\ntype ReuseTransport struct {\n\tentry   *reuseEntry\n\tremoved atomic.Bool\n}\n\nfunc (rt *ReuseTransport) RoundTrip(req *http.Request) (*http.Response, error) {\n\treturn rt.entry.transport.RoundTrip(req)\n}\n\nfunc (rt *ReuseTransport) Close() error {\n\tif !rt.removed.CompareAndSwap(false, true) {\n\t\treturn nil\n\t}\n\trt.entry.release()\n\treturn nil\n}\n\nvar _ http.RoundTripper = (*ReuseTransport)(nil)\n\ntype ReuseManager struct {\n\tmaxConcurrency   int\n\tmaxConnections   int\n\tcMaxReuseTimes   Range\n\thMaxRequestTimes Range\n\thMaxReusableSecs Range\n\tmaker            TransportMaker\n\tmu               sync.Mutex\n\tentries          []*reuseEntry\n}\n\nfunc NewReuseManager(cfg *ReuseConfig, makeTransport TransportMaker) (*ReuseManager, error) {\n\tif cfg == nil {\n\t\treturn nil, nil\n\t}\n\tconcurrency, connections, err := cfg.ResolveManagerConfig()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tcMaxReuseTimes, hMaxRequestTimes, hMaxReusableSecs, err := cfg.ResolveEntryConfig()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &ReuseManager{\n\t\tmaxConcurrency:   concurrency.Rand(),\n\t\tmaxConnections:   connections.Rand(),\n\t\tcMaxReuseTimes:   cMaxReuseTimes,\n\t\thMaxRequestTimes: hMaxRequestTimes,\n\t\thMaxReusableSecs: hMaxReusableSecs,\n\t\tmaker:            makeTransport,\n\t\tentries:          make([]*reuseEntry, 0),\n\t}, nil\n}\n\nfunc (m *ReuseManager) Close() error {\n\tif m == nil {\n\t\treturn nil\n\t}\n\tm.mu.Lock()\n\tdefer m.mu.Unlock()\n\n\tfor _, entry := range m.entries {\n\t\tentry.close()\n\t}\n\tm.entries = nil\n\treturn nil\n}\n\nfunc (m *ReuseManager) cleanupLocked(now time.Time) {\n\tkept := m.entries[:0]\n\tfor _, entry := range m.entries {\n\t\tif entry.isClosed() {\n\t\t\tcontinue\n\t\t}\n\t\tif entry.leftRequests.Load() <= 0 && entry.openUsage.Load() == 0 {\n\t\t\tentry.close()\n\t\t\tcontinue\n\t\t}\n\t\tif !entry.unreusableAt.IsZero() && now.After(entry.unreusableAt) && entry.openUsage.Load() == 0 {\n\t\t\tentry.close()\n\t\t\tcontinue\n\t\t}\n\t\tkept = append(kept, entry)\n\t}\n\tm.entries = kept\n}\n\nfunc (entry *reuseEntry) release() {\n\tif entry == nil {\n\t\treturn\n\t}\n\tremaining := entry.openUsage.Add(-1)\n\tif remaining < 0 {\n\t\tentry.openUsage.Store(0)\n\t\tremaining = 0\n\t}\n\n\tif remaining == 0 {\n\t\tnow := time.Now()\n\t\tif entry.leftRequests.Load() <= 0 ||\n\t\t\t(entry.maxReuseTimes > 0 && entry.reuseCount.Load() >= entry.maxReuseTimes) ||\n\t\t\t(!entry.unreusableAt.IsZero() && now.After(entry.unreusableAt)) {\n\t\t\tentry.close()\n\t\t}\n\t}\n}\n\nfunc (m *ReuseManager) pickLocked() *reuseEntry {\n\tvar best *reuseEntry\n\tfor _, entry := range m.entries {\n\t\tif entry.isClosed() {\n\t\t\tcontinue\n\t\t}\n\t\tif entry.leftRequests.Load() <= 0 {\n\t\t\tcontinue\n\t\t}\n\t\tif entry.maxReuseTimes > 0 && entry.reuseCount.Load() >= entry.maxReuseTimes {\n\t\t\tcontinue\n\t\t}\n\t\tif m.maxConcurrency > 0 && int(entry.openUsage.Load()) >= m.maxConcurrency {\n\t\t\tcontinue\n\t\t}\n\t\tif best == nil || entry.openUsage.Load() < best.openUsage.Load() {\n\t\t\tbest = entry\n\t\t}\n\t}\n\treturn best\n}\n\nfunc (m *ReuseManager) shouldCreateLocked() bool {\n\tif len(m.entries) == 0 {\n\t\treturn true\n\t}\n\tif m.maxConnections > 0 {\n\t\treturn len(m.entries) < m.maxConnections\n\t}\n\treturn false\n}\n\nfunc (m *ReuseManager) newEntryLocked(transport http.RoundTripper, now time.Time) *reuseEntry {\n\tentry := &reuseEntry{transport: transport}\n\n\tif m.hMaxRequestTimes.Max > 0 {\n\t\tentry.leftRequests.Store(int32(m.hMaxRequestTimes.Rand()))\n\t} else {\n\t\tentry.leftRequests.Store(1<<30 - 1)\n\t}\n\tif m.hMaxReusableSecs.Max > 0 {\n\t\tentry.unreusableAt = now.Add(time.Duration(m.hMaxReusableSecs.Rand()) * time.Second)\n\t}\n\tif m.cMaxReuseTimes.Max > 0 {\n\t\tentry.maxReuseTimes = int32(m.cMaxReuseTimes.Rand())\n\t}\n\n\tm.entries = append(m.entries, entry)\n\treturn entry\n}\n\nfunc (m *ReuseManager) GetTransport() http.RoundTripper {\n\tnow := time.Now()\n\n\tm.mu.Lock()\n\tdefer m.mu.Unlock()\n\n\tm.cleanupLocked(now)\n\n\tvar entry *reuseEntry\n\tif !m.shouldCreateLocked() {\n\t\tentry = m.pickLocked()\n\t}\n\treused := entry != nil\n\n\tif entry == nil {\n\t\ttransport := m.maker()\n\t\tentry = m.newEntryLocked(transport, now)\n\t}\n\n\tif reused {\n\t\tentry.reuseCount.Add(1)\n\t}\n\n\tentry.openUsage.Add(1)\n\tif entry.leftRequests.Load() > 0 {\n\t\tentry.leftRequests.Add(-1)\n\t}\n\n\treturn &ReuseTransport{entry: entry}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/xhttp/reuse_test.go",
    "content": "package xhttp\n\nimport (\n\t\"sync/atomic\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/metacubex/http\"\n)\n\ntype testRoundTripper struct {\n\tid int64\n}\n\nfunc (t *testRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {\n\tpanic(\"not used in reuse manager unit tests\")\n}\n\nfunc makeTestTransportFactory(counter *atomic.Int64) TransportMaker {\n\treturn func() http.RoundTripper {\n\t\tid := counter.Add(1)\n\t\treturn &testRoundTripper{id: id}\n\t}\n}\n\nfunc transportID(rt *ReuseTransport) int64 {\n\treturn rt.entry.transport.(*testRoundTripper).id\n}\n\nfunc TestManagerReuseSameEntry(t *testing.T) {\n\tvar created atomic.Int64\n\n\tmanager, err := NewReuseManager(&ReuseConfig{\n\t\tMaxConcurrency:   \"1\",\n\t\tMaxConnections:   \"1\",\n\t\tHMaxRequestTimes: \"10\",\n\t}, makeTestTransportFactory(&created))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\ttransport1 := manager.GetTransport().(*ReuseTransport)\n\tid1 := transportID(transport1)\n\n\ttransport1.Close()\n\n\ttransport2 := manager.GetTransport().(*ReuseTransport)\n\tid2 := transportID(transport2)\n\n\tif id1 != id2 {\n\t\tt.Fatalf(\"expected same transport to be reused, got %d and %d\", id1, id2)\n\t}\n\n\ttransport2.Close()\n\tmanager.Close()\n}\n\nfunc TestManagerRespectMaxConnections(t *testing.T) {\n\tvar created atomic.Int64\n\n\tmanager, err := NewReuseManager(&ReuseConfig{\n\t\tMaxConcurrency:   \"2\",\n\t\tMaxConnections:   \"2\",\n\t\tHMaxRequestTimes: \"100\",\n\t}, makeTestTransportFactory(&created))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\ttransport1 := manager.GetTransport().(*ReuseTransport)\n\tid1 := transportID(transport1)\n\ttransport2 := manager.GetTransport().(*ReuseTransport)\n\tid2 := transportID(transport2)\n\ttransport3 := manager.GetTransport().(*ReuseTransport)\n\tid3 := transportID(transport3)\n\ttransport4 := manager.GetTransport().(*ReuseTransport)\n\tid4 := transportID(transport4)\n\ttransport5 := manager.GetTransport().(*ReuseTransport)\n\tid5 := transportID(transport5)\n\n\tif id1 == id2 {\n\t\tt.Fatal(\"expected the second transport to be new\")\n\t}\n\n\tif id3 != id1 && id3 != id2 {\n\t\tt.Fatal(\"expected the third transport to be reused\")\n\t}\n\n\tif id4 != id1 && id4 != id2 {\n\t\tt.Fatal(\"expected the fourth transport to be reused\")\n\t}\n\n\tif id5 == id1 || id5 == id2 {\n\t\tt.Fatal(\"expected the fifth transport to be new\")\n\t}\n\n\ttransport1.Close()\n\ttransport2.Close()\n\ttransport3.Close()\n\ttransport4.Close()\n\ttransport5.Close()\n\tmanager.Close()\n}\n\nfunc TestManagerRotateOnRequestLimit(t *testing.T) {\n\tvar created atomic.Int64\n\n\tmanager, err := NewReuseManager(&ReuseConfig{\n\t\tMaxConcurrency:   \"1\",\n\t\tMaxConnections:   \"1\",\n\t\tHMaxRequestTimes: \"1\",\n\t}, makeTestTransportFactory(&created))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\ttransport1 := manager.GetTransport().(*ReuseTransport)\n\tid1 := transportID(transport1)\n\n\ttransport1.Close()\n\n\ttransport2 := manager.GetTransport().(*ReuseTransport)\n\tid2 := transportID(transport2)\n\n\tif id1 == id2 {\n\t\tt.Fatalf(\"expected new transport after request limit, got same id %d\", id1)\n\t}\n\n\ttransport2.Close()\n\tmanager.Close()\n}\n\nfunc TestManagerRotateOnReusableSecs(t *testing.T) {\n\tvar created atomic.Int64\n\n\tmanager, err := NewReuseManager(&ReuseConfig{\n\t\tMaxConcurrency:   \"1\",\n\t\tMaxConnections:   \"1\",\n\t\tHMaxRequestTimes: \"100\",\n\t\tHMaxReusableSecs: \"1\",\n\t}, makeTestTransportFactory(&created))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\ttransport1 := manager.GetTransport().(*ReuseTransport)\n\tid1 := transportID(transport1)\n\n\ttime.Sleep(1100 * time.Millisecond)\n\ttransport1.Close()\n\n\ttransport2 := manager.GetTransport().(*ReuseTransport)\n\tid2 := transportID(transport2)\n\n\tif id1 == id2 {\n\t\tt.Fatalf(\"expected new transport after reusable timeout, got same id %d\", id1)\n\t}\n\n\ttransport2.Close()\n\tmanager.Close()\n}\n\nfunc TestManagerRotateOnConnReuseLimit(t *testing.T) {\n\tvar created atomic.Int64\n\n\tmanager, err := NewReuseManager(&ReuseConfig{\n\t\tMaxConcurrency:   \"1\",\n\t\tMaxConnections:   \"1\",\n\t\tCMaxReuseTimes:   \"1\",\n\t\tHMaxRequestTimes: \"100\",\n\t}, makeTestTransportFactory(&created))\n\tif err != nil {\n\t\tt.Fatal(err)\n\t}\n\n\ttransport1 := manager.GetTransport().(*ReuseTransport)\n\tid1 := transportID(transport1)\n\n\ttransport1.Close()\n\n\ttransport2 := manager.GetTransport().(*ReuseTransport)\n\tid2 := transportID(transport2)\n\n\tif id1 != id2 {\n\t\tt.Fatalf(\"expected first reuse to use same transport, got %d and %d\", id1, id2)\n\t}\n\n\ttransport2.Close()\n\n\ttransport3 := manager.GetTransport().(*ReuseTransport)\n\tid3 := transportID(transport3)\n\n\tif id3 == id2 {\n\t\tt.Fatalf(\"expected new transport after c-max-reuse-times limit, got same id %d\", id3)\n\t}\n\n\ttransport3.Close()\n\tmanager.Close()\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/xhttp/server.go",
    "content": "package xhttp\n\nimport (\n\t\"bytes\"\n\t\"encoding/base64\"\n\t\"fmt\"\n\t\"io\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/httputils\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\n\t\"github.com/metacubex/http\"\n)\n\ntype ServerOption struct {\n\tConfig\n\tConnHandler func(net.Conn)\n\tHttpHandler http.Handler\n}\n\ntype httpServerConn struct {\n\tmu      sync.Mutex\n\tw       http.ResponseWriter\n\tflusher http.Flusher\n\treader  io.ReadCloser\n\tclosed  bool\n\tdone    chan struct{}\n\tonce    sync.Once\n}\n\nfunc newHTTPServerConn(w http.ResponseWriter, r io.ReadCloser) *httpServerConn {\n\tflusher, _ := w.(http.Flusher)\n\treturn &httpServerConn{\n\t\tw:       w,\n\t\tflusher: flusher,\n\t\treader:  r,\n\t\tdone:    make(chan struct{}),\n\t}\n}\n\nfunc (c *httpServerConn) Read(b []byte) (int, error) {\n\treturn c.reader.Read(b)\n}\n\nfunc (c *httpServerConn) Write(b []byte) (int, error) {\n\tc.mu.Lock()\n\tdefer c.mu.Unlock()\n\n\tif c.closed {\n\t\treturn 0, io.ErrClosedPipe\n\t}\n\n\tn, err := c.w.Write(b)\n\tif err == nil && c.flusher != nil {\n\t\tc.flusher.Flush()\n\t}\n\treturn n, err\n}\n\nfunc (c *httpServerConn) Close() error {\n\tc.once.Do(func() {\n\t\tc.mu.Lock()\n\t\tc.closed = true\n\t\tc.mu.Unlock()\n\t\tclose(c.done)\n\t})\n\treturn c.reader.Close()\n}\n\nfunc (c *httpServerConn) Wait() <-chan struct{} {\n\treturn c.done\n}\n\ntype httpSession struct {\n\tuploadQueue *UploadQueue\n\tconnected   chan struct{}\n\tonce        sync.Once\n}\n\nfunc newHTTPSession(maxPackets int) *httpSession {\n\treturn &httpSession{\n\t\tuploadQueue: NewUploadQueue(maxPackets),\n\t\tconnected:   make(chan struct{}),\n\t}\n}\n\nfunc (s *httpSession) markConnected() {\n\ts.once.Do(func() {\n\t\tclose(s.connected)\n\t})\n}\n\ntype requestHandler struct {\n\tconfig      Config\n\tconnHandler func(net.Conn)\n\thttpHandler http.Handler\n\n\txPaddingBytes        Range\n\tscMaxEachPostBytes   Range\n\tscStreamUpServerSecs Range\n\tscMaxBufferedPosts   Range\n\n\tmu       sync.Mutex\n\tsessions map[string]*httpSession\n}\n\nfunc NewServerHandler(opt ServerOption) (http.Handler, error) {\n\txPaddingBytes, err := opt.Config.GetNormalizedXPaddingBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tscMaxEachPostBytes, err := opt.Config.GetNormalizedScMaxEachPostBytes()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tscStreamUpServerSecs, err := opt.Config.GetNormalizedScStreamUpServerSecs()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tscMaxBufferedPosts, err := opt.Config.GetNormalizedScMaxBufferedPosts()\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn &requestHandler{\n\t\tconfig:               opt.Config,\n\t\tconnHandler:          opt.ConnHandler,\n\t\thttpHandler:          opt.HttpHandler,\n\t\txPaddingBytes:        xPaddingBytes,\n\t\tscMaxEachPostBytes:   scMaxEachPostBytes,\n\t\tscStreamUpServerSecs: scStreamUpServerSecs,\n\t\tscMaxBufferedPosts:   scMaxBufferedPosts,\n\t\tsessions:             map[string]*httpSession{},\n\t}, nil\n}\n\nfunc (h *requestHandler) upsertSession(sessionID string) *httpSession {\n\th.mu.Lock()\n\tdefer h.mu.Unlock()\n\n\ts, ok := h.sessions[sessionID]\n\tif ok {\n\t\treturn s\n\t}\n\n\ts = newHTTPSession(h.scMaxBufferedPosts.Max)\n\th.sessions[sessionID] = s\n\n\t// Reap orphan sessions that never become fully connected (e.g. from probing).\n\t// Matches Xray-core's 30-second reaper in upsertSession.\n\tgo func() {\n\t\ttimer := time.NewTimer(30 * time.Second)\n\t\tdefer timer.Stop()\n\t\tselect {\n\t\tcase <-timer.C:\n\t\t\th.deleteSession(sessionID)\n\t\tcase <-s.connected:\n\t\t}\n\t}()\n\n\treturn s\n}\n\nfunc (h *requestHandler) deleteSession(sessionID string) {\n\th.mu.Lock()\n\tdefer h.mu.Unlock()\n\n\tif s, ok := h.sessions[sessionID]; ok {\n\t\t_ = s.uploadQueue.Close()\n\t\tdelete(h.sessions, sessionID)\n\t}\n}\n\nfunc (h *requestHandler) getSession(sessionID string) *httpSession {\n\th.mu.Lock()\n\tdefer h.mu.Unlock()\n\treturn h.sessions[sessionID]\n}\n\nfunc (h *requestHandler) normalizedMode() string {\n\tif h.config.Mode == \"\" {\n\t\treturn \"auto\"\n\t}\n\treturn h.config.Mode\n}\n\nfunc (h *requestHandler) allowStreamOne() bool {\n\tswitch h.normalizedMode() {\n\tcase \"auto\", \"stream-one\", \"stream-up\":\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc (h *requestHandler) allowSessionDownload() bool {\n\tswitch h.normalizedMode() {\n\tcase \"auto\", \"stream-up\", \"packet-up\":\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc (h *requestHandler) allowStreamUpUpload() bool {\n\tswitch h.normalizedMode() {\n\tcase \"auto\", \"stream-up\":\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc (h *requestHandler) allowPacketUpUpload() bool {\n\tswitch h.normalizedMode() {\n\tcase \"auto\", \"packet-up\":\n\t\treturn true\n\tdefault:\n\t\treturn false\n\t}\n}\n\nfunc (h *requestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {\n\tpath := h.config.NormalizedPath()\n\tif h.httpHandler != nil && !strings.HasPrefix(r.URL.Path, path) {\n\t\th.httpHandler.ServeHTTP(w, r)\n\t\treturn\n\t}\n\n\tif h.config.Host != \"\" && !equalHost(r.Host, h.config.Host) {\n\t\thttp.NotFound(w, r)\n\t\treturn\n\t}\n\n\tif !strings.HasPrefix(r.URL.Path, path) {\n\t\thttp.NotFound(w, r)\n\t\treturn\n\t}\n\n\th.config.WriteResponseHeader(w, r.Method, r.Header)\n\tlength := h.xPaddingBytes.Rand()\n\tconfig := XPaddingConfig{Length: length}\n\n\tif h.config.XPaddingObfsMode {\n\t\tconfig.Placement = XPaddingPlacement{\n\t\t\tPlacement: h.config.XPaddingPlacement,\n\t\t\tKey:       h.config.XPaddingKey,\n\t\t\tHeader:    h.config.XPaddingHeader,\n\t\t}\n\t\tconfig.Method = PaddingMethod(h.config.XPaddingMethod)\n\t} else {\n\t\tconfig.Placement = XPaddingPlacement{\n\t\t\tPlacement: PlacementHeader,\n\t\t\tHeader:    \"X-Padding\",\n\t\t}\n\t}\n\n\th.config.ApplyXPaddingToResponse(w, config)\n\n\tif r.Method == \"OPTIONS\" {\n\t\tw.WriteHeader(http.StatusOK)\n\t\treturn\n\t}\n\n\tpaddingValue, _ := h.config.ExtractXPaddingFromRequest(r, h.config.XPaddingObfsMode)\n\tif !h.config.IsPaddingValid(paddingValue, h.xPaddingBytes.Min, h.xPaddingBytes.Max, PaddingMethod(h.config.XPaddingMethod)) {\n\t\thttp.Error(w, \"invalid xpadding\", http.StatusBadRequest)\n\t\treturn\n\t}\n\tsessionId, seqStr := h.config.ExtractMetaFromRequest(r, path)\n\n\tvar currentSession *httpSession\n\tif sessionId != \"\" {\n\t\tcurrentSession = h.upsertSession(sessionId)\n\t}\n\n\t// stream-up upload: POST /path/{session}\n\tif r.Method != http.MethodGet && sessionId != \"\" && seqStr == \"\" && h.allowStreamUpUpload() {\n\t\thttpSC := newHTTPServerConn(w, r.Body)\n\t\terr := currentSession.uploadQueue.Push(Packet{\n\t\t\tReader: httpSC,\n\t\t})\n\t\tif err != nil {\n\t\t\thttp.Error(w, err.Error(), http.StatusConflict)\n\t\t\treturn\n\t\t}\n\n\t\t// magic header instructs nginx + apache to not buffer response body\n\t\tw.Header().Set(\"X-Accel-Buffering\", \"no\")\n\t\t// A web-compliant header telling all middleboxes to disable caching.\n\t\t// Should be able to prevent overloading the cache, or stop CDNs from\n\t\t// teeing the response stream into their cache, causing slowdowns.\n\t\tw.Header().Set(\"Cache-Control\", \"no-store\")\n\t\tif !h.config.NoSSEHeader {\n\t\t\t// magic header to make the HTTP middle box consider this as SSE to disable buffer\n\t\t\tw.Header().Set(\"Content-Type\", \"text/event-stream\")\n\t\t}\n\t\tw.WriteHeader(http.StatusOK)\n\n\t\trc := http.NewResponseController(w)\n\t\t_ = rc.EnableFullDuplex() // http1 need to enable full duplex manually\n\t\t_ = rc.Flush()            // force flush the response header\n\n\t\treferrer := r.Header.Get(\"Referer\")\n\t\tif referrer != \"\" && h.scStreamUpServerSecs.Max > 0 {\n\t\t\tgo func() {\n\t\t\t\tfor {\n\t\t\t\t\t_, err := httpSC.Write(bytes.Repeat([]byte{'X'}, int(h.xPaddingBytes.Rand())))\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tbreak\n\t\t\t\t\t}\n\t\t\t\t\ttime.Sleep(time.Duration(h.scStreamUpServerSecs.Rand()) * time.Second)\n\t\t\t\t}\n\t\t\t}()\n\t\t}\n\n\t\tselect {\n\t\tcase <-r.Context().Done():\n\t\tcase <-httpSC.Wait():\n\t\t}\n\n\t\t_ = httpSC.Close()\n\t\treturn\n\t}\n\n\t// packet-up upload: POST /path/{session}/{seq}\n\tif r.Method != http.MethodGet && sessionId != \"\" && seqStr != \"\" && h.allowPacketUpUpload() {\n\t\tscMaxEachPostBytes := h.scMaxEachPostBytes.Max\n\t\tdataPlacement := h.config.GetNormalizedUplinkDataPlacement()\n\t\tuplinkDataKey := h.config.UplinkDataKey\n\t\tvar headerPayload []byte\n\t\tvar err error\n\t\tif dataPlacement == PlacementAuto || dataPlacement == PlacementHeader {\n\t\t\tvar headerPayloadChunks []string\n\t\t\tfor i := 0; true; i++ {\n\t\t\t\tchunk := r.Header.Get(fmt.Sprintf(\"%s-%d\", uplinkDataKey, i))\n\t\t\t\tif chunk == \"\" {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t\theaderPayloadChunks = append(headerPayloadChunks, chunk)\n\t\t\t}\n\t\t\theaderPayloadEncoded := strings.Join(headerPayloadChunks, \"\")\n\t\t\theaderPayload, err = base64.RawURLEncoding.DecodeString(headerPayloadEncoded)\n\t\t\tif err != nil {\n\t\t\t\thttp.Error(w, \"invalid base64 in header's payload\", http.StatusBadRequest)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tvar cookiePayload []byte\n\t\tif dataPlacement == PlacementAuto || dataPlacement == PlacementCookie {\n\t\t\tvar cookiePayloadChunks []string\n\t\t\tfor i := 0; true; i++ {\n\t\t\t\tcookieName := fmt.Sprintf(\"%s_%d\", uplinkDataKey, i)\n\t\t\t\tif c, _ := r.Cookie(cookieName); c != nil {\n\t\t\t\t\tcookiePayloadChunks = append(cookiePayloadChunks, c.Value)\n\t\t\t\t} else {\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tcookiePayloadEncoded := strings.Join(cookiePayloadChunks, \"\")\n\t\t\tcookiePayload, err = base64.RawURLEncoding.DecodeString(cookiePayloadEncoded)\n\t\t\tif err != nil {\n\t\t\t\thttp.Error(w, \"invalid base64 in cookies' payload\", http.StatusBadRequest)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tvar bodyPayload []byte\n\t\tif dataPlacement == PlacementAuto || dataPlacement == PlacementBody {\n\t\t\tif r.ContentLength > int64(scMaxEachPostBytes) {\n\t\t\t\thttp.Error(w, \"body too large\", http.StatusRequestEntityTooLarge)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tbodyPayload, err = io.ReadAll(io.LimitReader(r.Body, int64(scMaxEachPostBytes)+1))\n\t\t\tif err != nil {\n\t\t\t\thttp.Error(w, \"failed to read body\", http.StatusBadRequest)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tvar payload []byte\n\t\tswitch dataPlacement {\n\t\tcase PlacementHeader:\n\t\t\tpayload = headerPayload\n\t\tcase PlacementCookie:\n\t\t\tpayload = cookiePayload\n\t\tcase PlacementBody:\n\t\t\tpayload = bodyPayload\n\t\tcase PlacementAuto:\n\t\t\tpayload = headerPayload\n\t\t\tpayload = append(payload, cookiePayload...)\n\t\t\tpayload = append(payload, bodyPayload...)\n\t\t}\n\n\t\tif len(payload) > h.scMaxEachPostBytes.Max {\n\t\t\thttp.Error(w, \"body too large\", http.StatusRequestEntityTooLarge)\n\t\t\treturn\n\t\t}\n\n\t\tseq, err := strconv.ParseUint(seqStr, 10, 64)\n\t\tif err != nil {\n\t\t\thttp.Error(w, \"invalid xhttp seq\", http.StatusBadRequest)\n\t\t\treturn\n\t\t}\n\n\t\terr = currentSession.uploadQueue.Push(Packet{\n\t\t\tSeq:     seq,\n\t\t\tPayload: payload,\n\t\t})\n\t\tif err != nil {\n\t\t\thttp.Error(w, err.Error(), http.StatusInternalServerError)\n\t\t\treturn\n\t\t}\n\n\t\tif len(payload) == 0 {\n\t\t\t// Methods without a body are usually cached by default.\n\t\t\tw.Header().Set(\"Cache-Control\", \"no-store\")\n\t\t}\n\t\tw.WriteHeader(http.StatusOK)\n\t\treturn\n\t}\n\n\t// stream-up/packet-up download: GET /path/{session}\n\tif r.Method == http.MethodGet && sessionId != \"\" && seqStr == \"\" && h.allowSessionDownload() {\n\t\tcurrentSession.markConnected()\n\n\t\t// magic header instructs nginx + apache to not buffer response body\n\t\tw.Header().Set(\"X-Accel-Buffering\", \"no\")\n\t\t// A web-compliant header telling all middleboxes to disable caching.\n\t\t// Should be able to prevent overloading the cache, or stop CDNs from\n\t\t// teeing the response stream into their cache, causing slowdowns.\n\t\tw.Header().Set(\"Cache-Control\", \"no-store\")\n\t\tif !h.config.NoSSEHeader {\n\t\t\t// magic header to make the HTTP middle box consider this as SSE to disable buffer\n\t\t\tw.Header().Set(\"Content-Type\", \"text/event-stream\")\n\t\t}\n\t\tw.WriteHeader(http.StatusOK)\n\n\t\trc := http.NewResponseController(w)\n\t\t_ = rc.EnableFullDuplex() // http1 need to enable full duplex manually\n\t\t_ = rc.Flush()            // force flush the response header\n\n\t\thttpSC := newHTTPServerConn(w, r.Body)\n\t\tconn := &Conn{\n\t\t\twriter: httpSC,\n\t\t\treader: currentSession.uploadQueue,\n\t\t\tonClose: func() {\n\t\t\t\th.deleteSession(sessionId)\n\t\t\t},\n\t\t}\n\t\thttputils.SetAddrFromRequest(&conn.NetAddr, r)\n\n\t\tgo h.connHandler(N.NewDeadlineConn(conn))\n\n\t\tselect {\n\t\tcase <-r.Context().Done():\n\t\tcase <-httpSC.Wait():\n\t\t}\n\n\t\t_ = conn.Close()\n\t\treturn\n\t}\n\n\t// stream-one: POST /path\n\tif r.Method != http.MethodGet && sessionId == \"\" && seqStr == \"\" && h.allowStreamOne() {\n\t\tw.Header().Set(\"X-Accel-Buffering\", \"no\")\n\t\tw.Header().Set(\"Cache-Control\", \"no-store\")\n\t\tw.WriteHeader(http.StatusOK)\n\n\t\trc := http.NewResponseController(w)\n\t\t_ = rc.EnableFullDuplex() // http1 need to enable full duplex manually\n\t\t_ = rc.Flush()            // force flush the response header\n\n\t\thttpSC := newHTTPServerConn(w, r.Body)\n\t\tconn := &Conn{\n\t\t\twriter: httpSC,\n\t\t\treader: httpSC,\n\t\t}\n\t\thttputils.SetAddrFromRequest(&conn.NetAddr, r)\n\n\t\tgo h.connHandler(N.NewDeadlineConn(conn))\n\n\t\tselect {\n\t\tcase <-r.Context().Done():\n\t\tcase <-httpSC.Wait():\n\t\t}\n\n\t\t_ = conn.Close()\n\t\treturn\n\t}\n\n\thttp.NotFound(w, r)\n}\n\nfunc splitNonEmpty(s string) []string {\n\traw := strings.Split(s, \"/\")\n\tout := make([]string, 0, len(raw))\n\tfor _, v := range raw {\n\t\tif v != \"\" {\n\t\t\tout = append(out, v)\n\t\t}\n\t}\n\treturn out\n}\n\nfunc equalHost(a, b string) bool {\n\ta = strings.ToLower(a)\n\tb = strings.ToLower(b)\n\n\tif ah, _, err := net.SplitHostPort(a); err == nil {\n\t\ta = ah\n\t}\n\tif bh, _, err := net.SplitHostPort(b); err == nil {\n\t\tb = bh\n\t}\n\n\treturn a == b\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/xhttp/server_test.go",
    "content": "package xhttp\n\nimport (\n\t\"io\"\n\t\"net\"\n\t\"testing\"\n\n\t\"github.com/metacubex/http\"\n\t\"github.com/metacubex/http/httptest\"\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestServerHandlerModeRestrictions(t *testing.T) {\n\ttestCases := []struct {\n\t\tname       string\n\t\tmode       string\n\t\tmethod     string\n\t\ttarget     string\n\t\twantStatus int\n\t}{\n\t\t{\n\t\t\tname:       \"StreamOneAcceptsStreamOne\",\n\t\t\tmode:       \"stream-one\",\n\t\t\tmethod:     http.MethodPost,\n\t\t\ttarget:     \"https://example.com/xhttp/\",\n\t\t\twantStatus: http.StatusOK,\n\t\t},\n\t\t{\n\t\t\tname:       \"StreamOneRejectsSessionDownload\",\n\t\t\tmode:       \"stream-one\",\n\t\t\tmethod:     http.MethodGet,\n\t\t\ttarget:     \"https://example.com/xhttp/session\",\n\t\t\twantStatus: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:       \"StreamUpAcceptsStreamOne\",\n\t\t\tmode:       \"stream-up\",\n\t\t\tmethod:     http.MethodPost,\n\t\t\ttarget:     \"https://example.com/xhttp/\",\n\t\t\twantStatus: http.StatusOK,\n\t\t},\n\t\t{\n\t\t\tname:       \"StreamUpAllowsDownloadEndpoint\",\n\t\t\tmode:       \"stream-up\",\n\t\t\tmethod:     http.MethodGet,\n\t\t\ttarget:     \"https://example.com/xhttp/session\",\n\t\t\twantStatus: http.StatusOK,\n\t\t},\n\t\t{\n\t\t\tname:       \"StreamUpRejectsPacketUpload\",\n\t\t\tmode:       \"stream-up\",\n\t\t\tmethod:     http.MethodPost,\n\t\t\ttarget:     \"https://example.com/xhttp/session/0\",\n\t\t\twantStatus: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:       \"PacketUpAllowsDownloadEndpoint\",\n\t\t\tmode:       \"packet-up\",\n\t\t\tmethod:     http.MethodGet,\n\t\t\ttarget:     \"https://example.com/xhttp/session\",\n\t\t\twantStatus: http.StatusOK,\n\t\t},\n\t\t{\n\t\t\tname:       \"PacketUpRejectsStreamOne\",\n\t\t\tmode:       \"packet-up\",\n\t\t\tmethod:     http.MethodPost,\n\t\t\ttarget:     \"https://example.com/xhttp/\",\n\t\t\twantStatus: http.StatusNotFound,\n\t\t},\n\t\t{\n\t\t\tname:       \"PacketUpRejectsStreamUpUpload\",\n\t\t\tmode:       \"packet-up\",\n\t\t\tmethod:     http.MethodPost,\n\t\t\ttarget:     \"https://example.com/xhttp/session\",\n\t\t\twantStatus: http.StatusNotFound,\n\t\t},\n\t}\n\n\tfor _, testCase := range testCases {\n\t\tt.Run(testCase.name, func(t *testing.T) {\n\t\t\tconfig := Config{\n\t\t\t\tPath: \"/xhttp\",\n\t\t\t\tMode: testCase.mode,\n\t\t\t}\n\t\t\thandler, err := NewServerHandler(ServerOption{\n\t\t\t\tConfig: config,\n\t\t\t\tConnHandler: func(conn net.Conn) {\n\t\t\t\t\t_ = conn.Close()\n\t\t\t\t},\n\t\t\t})\n\t\t\tassert.NoError(t, err)\n\n\t\t\treq := httptest.NewRequest(testCase.method, testCase.target, io.NopCloser(http.NoBody))\n\t\t\trecorder := httptest.NewRecorder()\n\n\t\t\terr = config.FillStreamRequest(req, \"\")\n\t\t\tassert.NoError(t, err)\n\n\t\t\thandler.ServeHTTP(recorder, req)\n\n\t\t\tassert.Equal(t, testCase.wantStatus, recorder.Result().StatusCode)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/xhttp/upload_queue.go",
    "content": "package xhttp\n\nimport (\n\t\"errors\"\n\t\"io\"\n\t\"sync\"\n)\n\nvar ErrQueueTooLarge = errors.New(\"packet queue is too large\")\n\ntype Packet struct {\n\tSeq     uint64\n\tPayload []byte // UploadQueue will hold Payload, so never reuse it after UploadQueue.Push\n\tReader  io.ReadCloser\n}\n\ntype UploadQueue struct {\n\tmu         sync.Mutex\n\tcondPushed sync.Cond\n\tcondPopped sync.Cond\n\tpackets    map[uint64][]byte\n\tnextSeq    uint64\n\tbuf        []byte\n\tclosed     bool\n\tmaxPackets int\n\treader     io.ReadCloser\n}\n\nfunc NewUploadQueue(maxPackets int) *UploadQueue {\n\tq := &UploadQueue{\n\t\tpackets:    make(map[uint64][]byte, maxPackets),\n\t\tmaxPackets: maxPackets,\n\t}\n\tq.condPushed = sync.Cond{L: &q.mu}\n\tq.condPopped = sync.Cond{L: &q.mu}\n\treturn q\n}\n\nfunc (q *UploadQueue) Push(p Packet) error {\n\tq.mu.Lock()\n\tdefer q.mu.Unlock()\n\n\tif q.closed {\n\t\treturn io.ErrClosedPipe\n\t}\n\n\tif q.reader != nil {\n\t\treturn errors.New(\"uploadQueue.reader already exists\")\n\t}\n\n\tif p.Reader != nil {\n\t\tq.reader = p.Reader\n\t\tq.condPushed.Broadcast()\n\t\treturn nil\n\t}\n\n\tfor len(q.packets) > q.maxPackets {\n\t\tq.condPopped.Wait() // wait for the reader to read the packets\n\t\tif q.closed {\n\t\t\treturn io.ErrClosedPipe\n\t\t}\n\t}\n\n\tq.packets[p.Seq] = p.Payload\n\tq.condPushed.Broadcast()\n\treturn nil\n}\n\nfunc (q *UploadQueue) Read(b []byte) (int, error) {\n\tq.mu.Lock()\n\n\tfor {\n\t\tif len(q.buf) > 0 {\n\t\t\tn := copy(b, q.buf)\n\t\t\tq.buf = q.buf[n:]\n\t\t\tq.mu.Unlock()\n\t\t\treturn n, nil\n\t\t}\n\n\t\tif payload, ok := q.packets[q.nextSeq]; ok {\n\t\t\tdelete(q.packets, q.nextSeq)\n\t\t\tq.nextSeq++\n\t\t\tq.buf = payload\n\t\t\tq.condPopped.Broadcast()\n\t\t\tcontinue\n\t\t}\n\n\t\tif reader := q.reader; reader != nil {\n\t\t\tq.mu.Unlock() // unlock before calling q.reader.Read\n\t\t\treturn reader.Read(b)\n\t\t}\n\n\t\tif q.closed {\n\t\t\tq.mu.Unlock()\n\t\t\treturn 0, io.EOF\n\t\t}\n\n\t\tif len(q.packets) > q.maxPackets {\n\t\t\tq.mu.Unlock()\n\t\t\t// the \"reassembly buffer\" is too large, and we want to constrain memory usage somehow.\n\t\t\t// let's tear down the connection and hope the application retries.\n\t\t\treturn 0, ErrQueueTooLarge\n\t\t}\n\n\t\tq.condPushed.Wait()\n\t}\n}\n\nfunc (q *UploadQueue) Close() error {\n\tq.mu.Lock()\n\tdefer q.mu.Unlock()\n\n\tvar err error\n\tif q.reader != nil {\n\t\terr = q.reader.Close()\n\t}\n\tq.closed = true\n\tq.condPushed.Broadcast()\n\tq.condPopped.Broadcast()\n\treturn err\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/xhttp/upload_queue_test.go",
    "content": "package xhttp\n\nimport (\n\t\"io\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n)\n\nfunc TestUploadQueueMaxPackets(t *testing.T) {\n\tq := NewUploadQueue(2)\n\tch := make(chan struct{})\n\tgo func() {\n\t\terr := q.Push(Packet{Seq: 0, Payload: []byte{'0'}})\n\t\tassert.NoError(t, err)\n\t\terr = q.Push(Packet{Seq: 1, Payload: []byte{'1'}})\n\t\tassert.NoError(t, err)\n\t\terr = q.Push(Packet{Seq: 2, Payload: []byte{'2'}})\n\t\tassert.NoError(t, err)\n\t\terr = q.Push(Packet{Seq: 4, Payload: []byte{'4'}})\n\t\tassert.NoError(t, err)\n\t\terr = q.Push(Packet{Seq: 5, Payload: []byte{'5'}})\n\t\tassert.NoError(t, err)\n\t\terr = q.Push(Packet{Seq: 6, Payload: []byte{'6'}})\n\t\tassert.NoError(t, err)\n\t\terr = q.Push(Packet{Seq: 7, Payload: []byte{'7'}})\n\t\tassert.ErrorIs(t, err, io.ErrClosedPipe)\n\t\tclose(ch)\n\t}()\n\n\tbuf := make([]byte, 20)\n\tn, err := q.Read(buf)\n\tassert.Equal(t, 1, n)\n\tassert.Equal(t, []byte{'0'}, buf[:n])\n\tassert.NoError(t, err)\n\n\tn, err = q.Read(buf)\n\tassert.Equal(t, 1, n)\n\tassert.Equal(t, []byte{'1'}, buf[:n])\n\n\tn, err = q.Read(buf)\n\tassert.Equal(t, 1, n)\n\tassert.Equal(t, []byte{'2'}, buf[:n])\n\n\tn, err = q.Read(buf)\n\tassert.Equal(t, 0, n)\n\tassert.ErrorIs(t, err, ErrQueueTooLarge)\n\n\terr = q.Close()\n\tassert.NoError(t, err)\n\n\t<-ch\n}\n"
  },
  {
    "path": "core/Clash.Meta/transport/xhttp/xpadding.go",
    "content": "package xhttp\n\nimport (\n\t\"crypto/rand\"\n\t\"fmt\"\n\t\"math\"\n\t\"net/url\"\n\t\"strings\"\n\n\t\"github.com/metacubex/http\"\n\t\"golang.org/x/net/http2/hpack\"\n)\n\ntype PaddingMethod string\n\nconst (\n\tPaddingMethodRepeatX  PaddingMethod = \"repeat-x\"\n\tPaddingMethodTokenish PaddingMethod = \"tokenish\"\n)\n\nconst charsetBase62 = \"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz\"\n\n// Huffman encoding gives ~20% size reduction for base62 sequences\nconst avgHuffmanBytesPerCharBase62 = 0.8\n\nconst validationTolerance = 2\n\ntype XPaddingPlacement struct {\n\tPlacement string\n\tKey       string\n\tHeader    string\n\tRawURL    string\n}\n\ntype XPaddingConfig struct {\n\tLength    int\n\tPlacement XPaddingPlacement\n\tMethod    PaddingMethod\n}\n\nfunc randStringFromCharset(n int, charset string) (string, bool) {\n\tif n <= 0 || len(charset) == 0 {\n\t\treturn \"\", false\n\t}\n\n\tm := len(charset)\n\tlimit := byte(256 - (256 % m))\n\n\tresult := make([]byte, n)\n\ti := 0\n\n\tbuf := make([]byte, 256)\n\tfor i < n {\n\t\tif _, err := rand.Read(buf); err != nil {\n\t\t\treturn \"\", false\n\t\t}\n\t\tfor _, rb := range buf {\n\t\t\tif rb >= limit {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tresult[i] = charset[int(rb)%m]\n\t\t\ti++\n\t\t\tif i == n {\n\t\t\t\tbreak\n\t\t\t}\n\t\t}\n\t}\n\n\treturn string(result), true\n}\n\nfunc absInt(x int) int {\n\tif x < 0 {\n\t\treturn -x\n\t}\n\treturn x\n}\n\nfunc GenerateTokenishPaddingBase62(targetHuffmanBytes int) string {\n\tn := int(math.Ceil(float64(targetHuffmanBytes) / avgHuffmanBytesPerCharBase62))\n\tif n < 1 {\n\t\tn = 1\n\t}\n\n\trandBase62Str, ok := randStringFromCharset(n, charsetBase62)\n\tif !ok {\n\t\treturn \"\"\n\t}\n\n\tconst maxIter = 150\n\tadjustChar := byte('X')\n\n\t// Adjust until close enough\n\tfor iter := 0; iter < maxIter; iter++ {\n\t\tcurrentLength := int(hpack.HuffmanEncodeLength(randBase62Str))\n\t\tdiff := currentLength - targetHuffmanBytes\n\n\t\tif absInt(diff) <= validationTolerance {\n\t\t\treturn randBase62Str\n\t\t}\n\n\t\tif diff < 0 {\n\t\t\t// Too small -> append padding char(s)\n\t\t\trandBase62Str += string(adjustChar)\n\n\t\t\t// Avoid a long run of identical chars\n\t\t\tif adjustChar == 'X' {\n\t\t\t\tadjustChar = 'Z'\n\t\t\t} else {\n\t\t\t\tadjustChar = 'X'\n\t\t\t}\n\t\t} else {\n\t\t\t// Too big -> remove from the end\n\t\t\tif len(randBase62Str) <= 1 {\n\t\t\t\treturn randBase62Str\n\t\t\t}\n\t\t\trandBase62Str = randBase62Str[:len(randBase62Str)-1]\n\t\t}\n\t}\n\n\treturn randBase62Str\n}\n\nfunc GeneratePadding(method PaddingMethod, length int) string {\n\tif length <= 0 {\n\t\treturn \"\"\n\t}\n\n\t// https://www.rfc-editor.org/rfc/rfc7541.html#appendix-B\n\t// h2's HPACK Header Compression feature employs a huffman encoding using a static table.\n\t// 'X' and 'Z' are assigned an 8 bit code, so HPACK compression won't change actual padding length on the wire.\n\t// https://www.rfc-editor.org/rfc/rfc9204.html#section-4.1.2-2\n\t// h3's similar QPACK feature uses the same huffman table.\n\n\tswitch method {\n\tcase PaddingMethodRepeatX:\n\t\treturn strings.Repeat(\"X\", length)\n\tcase PaddingMethodTokenish:\n\t\tpaddingValue := GenerateTokenishPaddingBase62(length)\n\t\tif paddingValue == \"\" {\n\t\t\treturn strings.Repeat(\"X\", length)\n\t\t}\n\t\treturn paddingValue\n\tdefault:\n\t\treturn strings.Repeat(\"X\", length)\n\t}\n}\n\nfunc ApplyPaddingToCookie(req *http.Request, name, value string) {\n\tif req == nil || name == \"\" || value == \"\" {\n\t\treturn\n\t}\n\treq.AddCookie(&http.Cookie{\n\t\tName:  name,\n\t\tValue: value,\n\t\tPath:  \"/\",\n\t})\n}\n\nfunc ApplyPaddingToResponseCookie(writer http.ResponseWriter, name, value string) {\n\tif name == \"\" || value == \"\" {\n\t\treturn\n\t}\n\thttp.SetCookie(writer, &http.Cookie{\n\t\tName:  name,\n\t\tValue: value,\n\t\tPath:  \"/\",\n\t})\n}\n\nfunc ApplyPaddingToQuery(u *url.URL, key, value string) {\n\tif u == nil || key == \"\" || value == \"\" {\n\t\treturn\n\t}\n\tq := u.Query()\n\tq.Set(key, value)\n\tu.RawQuery = q.Encode()\n}\n\nfunc (c *Config) GetNormalizedXPaddingBytes() (Range, error) {\n\tr, err := ParseRange(c.XPaddingBytes, \"100-1000\")\n\tif err != nil {\n\t\treturn Range{}, fmt.Errorf(\"invalid x-padding-bytes: %w\", err)\n\t}\n\treturn r, nil\n}\n\nfunc (c *Config) ApplyXPaddingToHeader(h http.Header, config XPaddingConfig) {\n\tif h == nil {\n\t\treturn\n\t}\n\n\tpaddingValue := GeneratePadding(config.Method, config.Length)\n\n\tswitch p := config.Placement; p.Placement {\n\tcase PlacementHeader:\n\t\th.Set(p.Header, paddingValue)\n\tcase PlacementQueryInHeader:\n\t\tu, err := url.Parse(p.RawURL)\n\t\tif err != nil || u == nil {\n\t\t\treturn\n\t\t}\n\t\tu.RawQuery = p.Key + \"=\" + paddingValue\n\t\th.Set(p.Header, u.String())\n\t}\n}\n\nfunc (c *Config) ApplyXPaddingToRequest(req *http.Request, config XPaddingConfig) {\n\tif req == nil {\n\t\treturn\n\t}\n\tif req.Header == nil {\n\t\treq.Header = make(http.Header)\n\t}\n\n\tplacement := config.Placement.Placement\n\n\tif placement == PlacementHeader || placement == PlacementQueryInHeader {\n\t\tc.ApplyXPaddingToHeader(req.Header, config)\n\t\treturn\n\t}\n\n\tpaddingValue := GeneratePadding(config.Method, config.Length)\n\n\tswitch placement {\n\tcase PlacementCookie:\n\t\tApplyPaddingToCookie(req, config.Placement.Key, paddingValue)\n\tcase PlacementQuery:\n\t\tApplyPaddingToQuery(req.URL, config.Placement.Key, paddingValue)\n\t}\n}\n\nfunc (c *Config) ApplyXPaddingToResponse(writer http.ResponseWriter, config XPaddingConfig) {\n\tplacement := config.Placement.Placement\n\n\tif placement == PlacementHeader || placement == PlacementQueryInHeader {\n\t\tc.ApplyXPaddingToHeader(writer.Header(), config)\n\t\treturn\n\t}\n\n\tpaddingValue := GeneratePadding(config.Method, config.Length)\n\n\tswitch placement {\n\tcase PlacementCookie:\n\t\tApplyPaddingToResponseCookie(writer, config.Placement.Key, paddingValue)\n\t}\n}\n\nfunc (c *Config) ExtractXPaddingFromRequest(req *http.Request, obfsMode bool) (string, string) {\n\tif req == nil {\n\t\treturn \"\", \"\"\n\t}\n\n\tif !obfsMode {\n\t\treferrer := req.Header.Get(\"Referer\")\n\n\t\tif referrer != \"\" {\n\t\t\tif referrerURL, err := url.Parse(referrer); err == nil {\n\t\t\t\tpaddingValue := referrerURL.Query().Get(\"x_padding\")\n\t\t\t\tpaddingPlacement := PlacementQueryInHeader + \"=Referer, key=x_padding\"\n\t\t\t\treturn paddingValue, paddingPlacement\n\t\t\t}\n\t\t} else {\n\t\t\tpaddingValue := req.URL.Query().Get(\"x_padding\")\n\t\t\treturn paddingValue, PlacementQuery + \", key=x_padding\"\n\t\t}\n\t}\n\n\tkey := c.XPaddingKey\n\theader := c.XPaddingHeader\n\n\tif cookie, err := req.Cookie(key); err == nil {\n\t\tif cookie != nil && cookie.Value != \"\" {\n\t\t\tpaddingValue := cookie.Value\n\t\t\tpaddingPlacement := PlacementCookie + \", key=\" + key\n\t\t\treturn paddingValue, paddingPlacement\n\t\t}\n\t}\n\n\theaderValue := req.Header.Get(header)\n\n\tif headerValue != \"\" {\n\t\tif c.XPaddingPlacement == PlacementHeader {\n\t\t\tpaddingPlacement := PlacementHeader + \"=\" + header\n\t\t\treturn headerValue, paddingPlacement\n\t\t}\n\n\t\tif parsedURL, err := url.Parse(headerValue); err == nil {\n\t\t\tpaddingPlacement := PlacementQueryInHeader + \"=\" + header + \", key=\" + key\n\n\t\t\treturn parsedURL.Query().Get(key), paddingPlacement\n\t\t}\n\t}\n\n\tqueryValue := req.URL.Query().Get(key)\n\n\tif queryValue != \"\" {\n\t\tpaddingPlacement := PlacementQuery + \", key=\" + key\n\t\treturn queryValue, paddingPlacement\n\t}\n\n\treturn \"\", \"\"\n}\n\nfunc (c *Config) IsPaddingValid(paddingValue string, from, to int, method PaddingMethod) bool {\n\tif paddingValue == \"\" {\n\t\treturn false\n\t}\n\tif to <= 0 {\n\t\tif r, err := c.GetNormalizedXPaddingBytes(); err == nil {\n\t\t\tfrom, to = r.Min, r.Max\n\t\t}\n\t}\n\n\tswitch method {\n\tcase PaddingMethodRepeatX:\n\t\tn := len(paddingValue)\n\t\treturn n >= from && n <= to\n\tcase PaddingMethodTokenish:\n\t\tconst tolerance = validationTolerance\n\n\t\tn := int(hpack.HuffmanEncodeLength(paddingValue))\n\t\tf := from - tolerance\n\t\tt := to + tolerance\n\t\tif f < 0 {\n\t\t\tf = 0\n\t\t}\n\t\treturn n >= f && n <= t\n\tdefault:\n\t\tn := len(paddingValue)\n\t\treturn n >= from && n <= to\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/tunnel/connection.go",
    "content": "package tunnel\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net\"\n\t\"net/netip\"\n\t\"sync\"\n\t\"time\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n)\n\ntype packetSender struct {\n\tctx    context.Context\n\tcancel context.CancelFunc\n\tch     chan C.PacketAdapter\n\n\t// destination NAT mapping\n\toriginToTarget map[string]netip.Addr\n\ttargetToOrigin map[netip.Addr]netip.Addr\n\tmappingMutex   sync.RWMutex\n}\n\n// newPacketSender return a chan based C.PacketSender\n// It ensures that packets can be sent sequentially and without blocking\nfunc newPacketSender() C.PacketSender {\n\tctx, cancel := context.WithCancel(context.Background())\n\tch := make(chan C.PacketAdapter, senderCapacity)\n\treturn &packetSender{\n\t\tctx:    ctx,\n\t\tcancel: cancel,\n\t\tch:     ch,\n\n\t\toriginToTarget: make(map[string]netip.Addr),\n\t\ttargetToOrigin: make(map[netip.Addr]netip.Addr),\n\t}\n}\n\nfunc (s *packetSender) AddMapping(originMetadata *C.Metadata, metadata *C.Metadata) {\n\ts.mappingMutex.Lock()\n\tdefer s.mappingMutex.Unlock()\n\toriginKey := originMetadata.String()\n\toriginAddr := originMetadata.DstIP\n\ttargetAddr := metadata.DstIP\n\tif addr := s.originToTarget[originKey]; !addr.IsValid() { // overwrite only if the record is illegal\n\t\ts.originToTarget[originKey] = targetAddr\n\t}\n\tif addr := s.targetToOrigin[targetAddr]; !addr.IsValid() { // overwrite only if the record is illegal\n\t\ts.targetToOrigin[targetAddr] = originAddr\n\t}\n}\n\nfunc (s *packetSender) RestoreReadFrom(addr netip.Addr) netip.Addr {\n\ts.mappingMutex.RLock()\n\tdefer s.mappingMutex.RUnlock()\n\tif originAddr := s.targetToOrigin[addr]; originAddr.IsValid() {\n\t\treturn originAddr\n\t}\n\treturn addr\n}\n\nfunc (s *packetSender) processPacket(pc C.PacketConn, packet C.PacketAdapter) {\n\tdefer packet.Drop()\n\tmetadata := packet.Metadata()\n\n\tvar addr *net.UDPAddr\n\n\ts.mappingMutex.RLock()\n\ttargetAddr := s.originToTarget[metadata.String()]\n\ts.mappingMutex.RUnlock()\n\n\tif targetAddr.IsValid() {\n\t\taddr = net.UDPAddrFromAddrPort(netip.AddrPortFrom(targetAddr, metadata.DstPort))\n\t}\n\n\tif addr == nil {\n\t\toriginMetadata := metadata  // save origin metadata\n\t\tmetadata = metadata.Clone() // don't modify PacketAdapter's metadata\n\n\t\t_ = preHandleMetadata(metadata) // error was pre-checked\n\t\tmetadata = metadata.Pure()\n\t\tif metadata.Host != \"\" {\n\t\t\t// TODO: ResolveUDP may take a long time to block the Process loop\n\t\t\t//       but we want keep sequence sending so can't open a new goroutine\n\t\t\tif err := pc.ResolveUDP(s.ctx, metadata); err != nil {\n\t\t\t\tlog.Warnln(\"[UDP] Resolve Ip error: %s\", err)\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\n\t\tif !metadata.DstIP.IsValid() {\n\t\t\tlog.Warnln(\"[UDP] Destination ip not valid: %#v\", metadata)\n\t\t\treturn\n\t\t}\n\t\ts.AddMapping(originMetadata, metadata)\n\t\taddr = metadata.UDPAddr()\n\t}\n\t_ = handleUDPToRemote(packet, pc, addr)\n}\n\nfunc (s *packetSender) Process(pc C.PacketConn, proxy C.WriteBackProxy) {\n\tfor {\n\t\tselect {\n\t\tcase <-s.ctx.Done():\n\t\t\treturn // sender closed\n\t\tcase packet := <-s.ch:\n\t\t\tif proxy != nil {\n\t\t\t\tproxy.UpdateWriteBack(packet)\n\t\t\t}\n\t\t\ts.processPacket(pc, packet)\n\t\t}\n\t}\n}\n\nfunc (s *packetSender) dropAll() {\n\tfor {\n\t\tselect {\n\t\tcase data := <-s.ch:\n\t\t\tdata.Drop() // drop all data still in chan\n\t\tdefault:\n\t\t\treturn // no data, exit goroutine\n\t\t}\n\t}\n}\n\nfunc (s *packetSender) Send(packet C.PacketAdapter) {\n\tselect {\n\tcase <-s.ctx.Done():\n\t\tpacket.Drop() // sender closed before Send()\n\t\treturn\n\tdefault:\n\t}\n\n\tselect {\n\tcase s.ch <- packet:\n\t\t// put ok, so don't drop packet, will process by other side of chan\n\tcase <-s.ctx.Done():\n\t\tpacket.Drop() // sender closed when putting data to chan\n\tdefault:\n\t\tpacket.Drop() // chan is full\n\t}\n}\n\nfunc (s *packetSender) Close() {\n\ts.cancel()\n\ts.dropAll()\n}\n\nfunc (s *packetSender) DoSniff(metadata *C.Metadata) error { return nil }\n\nfunc handleUDPToRemote(packet C.UDPPacket, pc C.PacketConn, addr *net.UDPAddr) error {\n\tif addr == nil {\n\t\treturn errors.New(\"udp addr invalid\")\n\t}\n\n\tif _, err := pc.WriteTo(packet.Data(), addr); err != nil {\n\t\treturn err\n\t}\n\t// reset timeout\n\t_ = pc.SetReadDeadline(time.Now().Add(udpTimeout))\n\n\treturn nil\n}\n\nfunc handleUDPToLocal(writeBack C.WriteBack, pc C.PacketConn, sender C.PacketSender, key string, oAddrPort netip.AddrPort) {\n\tdefer func() {\n\t\tsender.Close()\n\t\t_ = pc.Close()\n\t\tcloseAllLocalCoon(key)\n\t\tnatTable.Delete(key)\n\t}()\n\n\tfor {\n\t\t_ = pc.SetReadDeadline(time.Now().Add(udpTimeout))\n\t\tdata, put, from, err := pc.WaitReadFrom()\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tfromUDPAddr, isUDPAddr := from.(*net.UDPAddr)\n\t\tif !isUDPAddr {\n\t\t\tfromUDPAddr = net.UDPAddrFromAddrPort(oAddrPort) // oAddrPort was Unmapped\n\t\t\tlog.Warnln(\"server return a [%T](%s) which isn't a *net.UDPAddr, force replace to (%s), this may be caused by a wrongly implemented server\", from, from, oAddrPort)\n\t\t} else if fromUDPAddr == nil {\n\t\t\tfromUDPAddr = net.UDPAddrFromAddrPort(oAddrPort) // oAddrPort was Unmapped\n\t\t\tlog.Warnln(\"server return a nil *net.UDPAddr, force replace to (%s), this may be caused by a wrongly implemented server\", oAddrPort)\n\t\t}\n\n\t\tfromAddrPort := fromUDPAddr.AddrPort()\n\t\tfromAddr := fromAddrPort.Addr().Unmap()\n\n\t\t// restore DestinationNAT\n\t\tfromAddr = sender.RestoreReadFrom(fromAddr).Unmap()\n\n\t\tfromAddrPort = netip.AddrPortFrom(fromAddr, fromAddrPort.Port())\n\n\t\t_, err = writeBack.WriteBack(data, net.UDPAddrFromAddrPort(fromAddrPort))\n\t\tif put != nil {\n\t\t\tput()\n\t\t}\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t}\n}\n\nfunc closeAllLocalCoon(lAddr string) {\n\tnatTable.RangeForLocalConn(lAddr, func(key string, value *net.UDPConn) bool {\n\t\tconn := value\n\n\t\tconn.Close()\n\t\tlog.Debugln(\"Closing TProxy local conn... lAddr=%s rAddr=%s\", lAddr, key)\n\t\treturn true\n\t})\n}\n\nfunc handleSocket(inbound, outbound net.Conn) {\n\tN.Relay(inbound, outbound)\n}\n"
  },
  {
    "path": "core/Clash.Meta/tunnel/dns_dialer.go",
    "content": "package tunnel\n\n// WARNING: all function in this file should only be using in dns module\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net\"\n\t\"strings\"\n\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/component/dialer\"\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/tunnel/statistic\"\n)\n\nconst DnsRespectRules = \"RULES\"\n\ntype DNSDialer struct {\n\tr            resolver.Resolver\n\tproxyAdapter C.ProxyAdapter\n\tproxyName    string\n}\n\nfunc NewDNSDialer(r resolver.Resolver, proxyAdapter C.ProxyAdapter, proxyName string) *DNSDialer {\n\treturn &DNSDialer{r: r, proxyAdapter: proxyAdapter, proxyName: proxyName}\n}\n\nfunc (d *DNSDialer) DialContext(ctx context.Context, network, addr string) (net.Conn, error) {\n\tr := d.r\n\tproxyName := d.proxyName\n\tproxyAdapter := d.proxyAdapter\n\tvar opts []dialer.Option\n\tvar rule C.Rule\n\tmetadata := &C.Metadata{\n\t\tNetWork: C.TCP,\n\t\tType:    C.INNER,\n\t}\n\terr := metadata.SetRemoteAddress(addr) // tcp can resolve host by remote\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif !strings.Contains(network, \"tcp\") {\n\t\tmetadata.NetWork = C.UDP\n\t\tif !metadata.Resolved() {\n\t\t\t// udp must resolve host first\n\t\t\tdstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tmetadata.DstIP = dstIP\n\t\t}\n\t}\n\n\tif proxyAdapter == nil && len(proxyName) != 0 {\n\t\tif proxyName == DnsRespectRules {\n\t\t\tif !metadata.Resolved() {\n\t\t\t\t// resolve here before resolveMetadata to avoid its inner resolver.ResolveIP\n\t\t\t\tdstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tmetadata.DstIP = dstIP\n\t\t\t}\n\t\t\tproxyAdapter, rule, err = resolveMetadata(metadata)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t} else {\n\t\t\tvar ok bool\n\t\t\tproxyAdapter, ok = Proxies()[proxyName]\n\t\t\tif ok {\n\t\t\t\tmetadata.SpecialProxy = proxyName // just for log\n\t\t\t} else {\n\t\t\t\topts = append(opts, dialer.WithInterface(proxyName))\n\t\t\t}\n\t\t}\n\t}\n\n\tif metadata.NetWork == C.TCP {\n\t\tif proxyAdapter == nil {\n\t\t\topts = append(opts, dialer.WithResolver(r))\n\t\t\treturn dialer.DialContext(ctx, network, addr, opts...)\n\t\t}\n\n\t\tif proxyAdapter.IsL3Protocol(metadata) { // L3 proxy should resolve domain before to avoid loopback\n\t\t\tif !metadata.Resolved() {\n\t\t\t\tdstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn nil, err\n\t\t\t\t}\n\t\t\t\tmetadata.DstIP = dstIP\n\t\t\t}\n\t\t\tmetadata.Host = \"\" // clear host to avoid double resolve in proxy\n\t\t}\n\n\t\tconn, err := proxyAdapter.DialContext(ctx, metadata)\n\t\tif err != nil {\n\t\t\tlogMetadataErr(metadata, rule, proxyAdapter, err)\n\t\t\treturn nil, err\n\t\t}\n\t\tlogMetadata(metadata, rule, conn)\n\n\t\tconn = statistic.NewTCPTracker(conn, statistic.DefaultManager, metadata, rule, 0, 0, false)\n\n\t\treturn conn, nil\n\t} else {\n\t\tif proxyAdapter == nil {\n\t\t\treturn dialer.DialContext(ctx, network, metadata.AddrPort().String(), opts...)\n\t\t}\n\n\t\tif !proxyAdapter.SupportUDP() {\n\t\t\treturn nil, fmt.Errorf(\"proxy adapter [%s] UDP is not supported\", proxyAdapter)\n\t\t}\n\n\t\tpacketConn, err := proxyAdapter.ListenPacketContext(ctx, metadata)\n\t\tif err != nil {\n\t\t\tlogMetadataErr(metadata, rule, proxyAdapter, err)\n\t\t\treturn nil, err\n\t\t}\n\t\tlogMetadata(metadata, rule, packetConn)\n\n\t\tpacketConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false)\n\n\t\treturn N.NewBindPacketConn(packetConn, metadata.UDPAddr()), nil\n\t}\n\n}\n\nfunc (d *DNSDialer) ListenPacket(ctx context.Context, network, addr string) (net.PacketConn, error) {\n\tr := d.r\n\tproxyAdapter := d.proxyAdapter\n\tproxyName := d.proxyName\n\tvar opts []dialer.Option\n\tmetadata := &C.Metadata{\n\t\tNetWork: C.UDP,\n\t\tType:    C.INNER,\n\t}\n\terr := metadata.SetRemoteAddress(addr)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tif !metadata.Resolved() {\n\t\t// udp must resolve host first\n\t\tdstIP, err := resolver.ResolveIPWithResolver(ctx, metadata.Host, r)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t\tmetadata.DstIP = dstIP\n\t}\n\n\tvar rule C.Rule\n\tif proxyAdapter == nil {\n\t\tif proxyName == DnsRespectRules {\n\t\t\tproxyAdapter, rule, err = resolveMetadata(metadata)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t} else {\n\t\t\tvar ok bool\n\t\t\tproxyAdapter, ok = Proxies()[proxyName]\n\t\t\tif ok {\n\t\t\t\tmetadata.SpecialProxy = proxyName // just for log\n\t\t\t} else {\n\t\t\t\topts = append(opts, dialer.WithInterface(proxyName))\n\t\t\t}\n\t\t}\n\t}\n\n\tif proxyAdapter == nil {\n\t\treturn dialer.NewDialer(opts...).ListenPacket(ctx, network, \"\", metadata.AddrPort())\n\t}\n\n\tif !proxyAdapter.SupportUDP() {\n\t\treturn nil, fmt.Errorf(\"proxy adapter [%s] UDP is not supported\", proxyAdapter)\n\t}\n\n\tpacketConn, err := proxyAdapter.ListenPacketContext(ctx, metadata)\n\tif err != nil {\n\t\tlogMetadataErr(metadata, rule, proxyAdapter, err)\n\t\treturn nil, err\n\t}\n\tlogMetadata(metadata, rule, packetConn)\n\n\tpacketConn = statistic.NewUDPTracker(packetConn, statistic.DefaultManager, metadata, rule, 0, 0, false)\n\n\treturn packetConn, nil\n}\n"
  },
  {
    "path": "core/Clash.Meta/tunnel/mode.go",
    "content": "package tunnel\n\nimport (\n\t\"errors\"\n\t\"strings\"\n)\n\ntype TunnelMode int32\n\n// ModeMapping is a mapping for Mode enum\nvar ModeMapping = map[string]TunnelMode{\n\tGlobal.String(): Global,\n\tRule.String():   Rule,\n\tDirect.String(): Direct,\n}\n\nconst (\n\tGlobal TunnelMode = iota\n\tRule\n\tDirect\n)\n\n// UnmarshalText unserialize Mode\nfunc (m *TunnelMode) UnmarshalText(data []byte) error {\n\tmode, exist := ModeMapping[strings.ToLower(string(data))]\n\tif !exist {\n\t\treturn errors.New(\"invalid mode\")\n\t}\n\t*m = mode\n\treturn nil\n}\n\n// MarshalText serialize Mode\nfunc (m TunnelMode) MarshalText() ([]byte, error) {\n\treturn []byte(m.String()), nil\n}\n\nfunc (m TunnelMode) String() string {\n\tswitch m {\n\tcase Global:\n\t\treturn \"global\"\n\tcase Rule:\n\t\treturn \"rule\"\n\tcase Direct:\n\t\treturn \"direct\"\n\tdefault:\n\t\treturn \"Unknown\"\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/tunnel/statistic/manager.go",
    "content": "package statistic\n\nimport (\n\t\"os\"\n\t\"runtime\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/atomic\"\n\t\"github.com/metacubex/mihomo/common/xsync\"\n)\n\nvar DefaultManager *Manager\n\nfunc init() {\n\tDefaultManager = &Manager{\n\t\tuploadTemp:         atomic.NewInt64(0),\n\t\tdownloadTemp:       atomic.NewInt64(0),\n\t\tuploadBlip:         atomic.NewInt64(0),\n\t\tdownloadBlip:       atomic.NewInt64(0),\n\t\tuploadTotal:        atomic.NewInt64(0),\n\t\tdownloadTotal:      atomic.NewInt64(0),\n\t\tproxyUploadTemp:    atomic.NewInt64(0),\n\t\tproxyDownloadTemp:  atomic.NewInt64(0),\n\t\tproxyUploadBlip:    atomic.NewInt64(0),\n\t\tproxyDownloadBlip:  atomic.NewInt64(0),\n\t\tproxyUploadTotal:   atomic.NewInt64(0),\n\t\tproxyDownloadTotal: atomic.NewInt64(0),\n\t\tpid:                int32(os.Getpid()),\n\t}\n\n\tgo DefaultManager.handle()\n}\n\ntype Manager struct {\n\tconnections        xsync.Map[string, Tracker]\n\tuploadTemp         atomic.Int64\n\tdownloadTemp       atomic.Int64\n\tuploadBlip         atomic.Int64\n\tdownloadBlip       atomic.Int64\n\tuploadTotal        atomic.Int64\n\tdownloadTotal      atomic.Int64\n\tproxyUploadTemp    atomic.Int64\n\tproxyDownloadTemp  atomic.Int64\n\tproxyUploadBlip    atomic.Int64\n\tproxyDownloadBlip  atomic.Int64\n\tproxyUploadTotal   atomic.Int64\n\tproxyDownloadTotal atomic.Int64\n\tpid                int32\n\tmemory             uint64\n}\n\nfunc (m *Manager) Join(c Tracker) {\n\tm.connections.Store(c.ID(), c)\n}\n\nfunc (m *Manager) Leave(c Tracker) {\n\tm.connections.Delete(c.ID())\n\tif DefaultRequestNotify != nil {\n\t\tDefaultRequestNotify(c)\n\t}\n}\n\nfunc (m *Manager) Get(id string) (c Tracker) {\n\tif value, ok := m.connections.Load(id); ok {\n\t\tc = value\n\t}\n\treturn\n}\n\nfunc (m *Manager) Range(f func(c Tracker) bool) {\n\tm.connections.Range(func(key string, value Tracker) bool {\n\t\treturn f(value)\n\t})\n}\n\nfunc (m *Manager) PushUploaded(lastChain string, size int64) {\n\tif lastChain != \"DIRECT\" {\n\t\tm.proxyUploadTemp.Add(size)\n\t\tm.proxyUploadTotal.Add(size)\n\t}\n\tm.uploadTemp.Add(size)\n\tm.uploadTotal.Add(size)\n}\n\nfunc (m *Manager) PushDownloaded(lastChain string, size int64) {\n\tif lastChain != \"DIRECT\" {\n\t\tm.proxyDownloadTemp.Add(size)\n\t\tm.proxyDownloadTotal.Add(size)\n\t}\n\tm.downloadTemp.Add(size)\n\tm.downloadTotal.Add(size)\n}\n\nfunc (m *Manager) Now() (up int64, down int64) {\n\treturn m.uploadBlip.Load(), m.downloadBlip.Load()\n}\n\nfunc (m *Manager) Total() (up, down int64) {\n\treturn m.uploadTotal.Load(), m.downloadTotal.Load()\n}\n\nfunc (m *Manager) Memory() uint64 {\n\tm.updateMemory()\n\treturn m.memory\n}\n\nfunc (m *Manager) ResetStatistic() {\n\tm.uploadTemp.Store(0)\n\tm.uploadBlip.Store(0)\n\tm.uploadTotal.Store(0)\n\tm.downloadTemp.Store(0)\n\tm.downloadBlip.Store(0)\n\tm.downloadTotal.Store(0)\n\n\tm.proxyUploadTemp.Store(0)\n\tm.proxyUploadBlip.Store(0)\n\tm.proxyUploadTotal.Store(0)\n\tm.proxyDownloadTemp.Store(0)\n\tm.proxyDownloadBlip.Store(0)\n\tm.proxyDownloadTotal.Store(0)\n}\n\nfunc (m *Manager) updateMemory() {\n\tvar memStats runtime.MemStats\n\truntime.ReadMemStats(&memStats)\n\tm.memory = memStats.StackInuse + memStats.HeapInuse\n}\n\nfunc (m *Manager) handle() {\n\tticker := time.NewTicker(time.Second)\n\n\tfor range ticker.C {\n\t\tm.uploadBlip.Store(m.uploadTemp.Swap(0))\n\t\tm.downloadBlip.Store(m.downloadTemp.Swap(0))\n\t\tm.proxyUploadBlip.Store(m.proxyUploadTemp.Swap(0))\n\t\tm.proxyDownloadBlip.Store(m.proxyDownloadTemp.Swap(0))\n\t}\n}\n\ntype Snapshot struct {\n\tDownloadTotal   int64         `json:\"downloadTotal\"`\n\tUploadTotal     int64         `json:\"uploadTotal\"`\n\tConnections     []TrackerInfo `json:\"connections\"`\n\tMemory          uint64        `json:\"memory\"`\n}\n\nfunc (m *Manager) Snapshot() *Snapshot {\n\tm.updateMemory()\n\n\tconnections := make([]TrackerInfo, 0)\n\tm.connections.Range(func(key string, value Tracker) bool {\n\t\tconnections = append(connections, *value.Info())\n\t\treturn true\n\t})\n\n\treturn &Snapshot{\n\t\tDownloadTotal:   m.downloadTotal.Load(),\n\t\tUploadTotal:     m.uploadTotal.Load(),\n\t\tConnections:     connections,\n\t\tMemory:          m.memory,\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/tunnel/statistic/patch.go",
    "content": "package statistic\n\ntype RequestNotify func(c Tracker)\n\nvar DefaultRequestNotify RequestNotify\n\nfunc (m *Manager) TotalTraffic(onlyProxy bool) (up, down int64) {\n\tif onlyProxy {\n\t\treturn m.proxyUploadTotal.Load(), m.proxyDownloadTotal.Load()\n\t}\n\treturn m.uploadTotal.Load(), m.downloadTotal.Load()\n}\n\nfunc (m *Manager) NowTraffic(onlyProxy bool) (up, down int64) {\n\tif onlyProxy {\n\t\treturn m.proxyUploadBlip.Load(), m.proxyDownloadBlip.Load()\n\t}\n\treturn m.uploadBlip.Load(), m.downloadBlip.Load()\n}\n"
  },
  {
    "path": "core/Clash.Meta/tunnel/statistic/patch_android.go",
    "content": "//go:build android\n\npackage statistic\n"
  },
  {
    "path": "core/Clash.Meta/tunnel/statistic/tracker.go",
    "content": "package statistic\n\nimport (\n\t\"io\"\n\t\"net\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/atomic\"\n\t\"github.com/metacubex/mihomo/common/buf\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\n\t\"github.com/gofrs/uuid/v5\"\n)\n\ntype Tracker interface {\n\tID() string\n\tClose() error\n\tInfo() *TrackerInfo\n\tC.Connection\n}\n\ntype TrackerInfo struct {\n\tUUID          uuid.UUID    `json:\"id\"`\n\tMetadata      *C.Metadata  `json:\"metadata\"`\n\tUploadTotal   atomic.Int64 `json:\"upload\"`\n\tDownloadTotal atomic.Int64 `json:\"download\"`\n\tStart         time.Time    `json:\"start\"`\n\tChain         C.Chain      `json:\"chains\"`\n\tProviderChain C.Chain      `json:\"providerChains\"`\n\tRule          string       `json:\"rule\"`\n\tRulePayload   string       `json:\"rulePayload\"`\n}\n\ntype tcpTracker struct {\n\tC.Conn `json:\"-\"`\n\t*TrackerInfo\n\tmanager *Manager\n\n\tpushToManager bool        `json:\"-\"`\n\tclosed        atomic.Bool `json:\"-\"`\n}\n\nfunc (tt *tcpTracker) ID() string {\n\treturn tt.UUID.String()\n}\n\nfunc (tt *tcpTracker) Info() *TrackerInfo {\n\treturn tt.TrackerInfo\n}\n\nfunc (tt *tcpTracker) Read(b []byte) (int, error) {\n\tn, err := tt.Conn.Read(b)\n\tdownload := int64(n)\n\tif tt.pushToManager {\n\t\ttt.manager.PushDownloaded(tt.Chain.Last(), download)\n\t}\n\ttt.DownloadTotal.Add(download)\n\treturn n, err\n}\n\nfunc (tt *tcpTracker) ReadBuffer(buffer *buf.Buffer) (err error) {\n\terr = tt.Conn.ReadBuffer(buffer)\n\tdownload := int64(buffer.Len())\n\tif tt.pushToManager {\n\t\ttt.manager.PushDownloaded(tt.Chain.Last(), download)\n\t}\n\ttt.DownloadTotal.Add(download)\n\treturn\n}\n\nfunc (tt *tcpTracker) UnwrapReader() (io.Reader, []N.CountFunc) {\n\treturn tt.Conn, []N.CountFunc{func(download int64) {\n\t\tif tt.pushToManager {\n\t\t\ttt.manager.PushDownloaded(tt.Chain.Last(), download)\n\t\t}\n\t\ttt.DownloadTotal.Add(download)\n\t}}\n}\n\nfunc (tt *tcpTracker) Write(b []byte) (int, error) {\n\tn, err := tt.Conn.Write(b)\n\tupload := int64(n)\n\tif tt.pushToManager {\n\t\ttt.manager.PushUploaded(tt.Chain.Last(), upload)\n\t}\n\ttt.UploadTotal.Add(upload)\n\treturn n, err\n}\n\nfunc (tt *tcpTracker) WriteBuffer(buffer *buf.Buffer) (err error) {\n\tupload := int64(buffer.Len())\n\terr = tt.Conn.WriteBuffer(buffer)\n\tif tt.pushToManager {\n\t\ttt.manager.PushUploaded(tt.Chain.Last(), upload)\n\t}\n\ttt.UploadTotal.Add(upload)\n\treturn\n}\n\nfunc (tt *tcpTracker) UnwrapWriter() (io.Writer, []N.CountFunc) {\n\treturn tt.Conn, []N.CountFunc{func(upload int64) {\n\t\tif tt.pushToManager {\n\t\t\ttt.manager.PushUploaded(tt.Chain.Last(), upload)\n\t\t}\n\t\ttt.UploadTotal.Add(upload)\n\t}}\n}\n\nfunc (tt *tcpTracker) Close() error {\n\tif tt.closed.CompareAndSwap(false, true) {\n\t\ttt.manager.Leave(tt)\n\t}\n\treturn tt.Conn.Close()\n}\n\nfunc (tt *tcpTracker) Upstream() any {\n\treturn tt.Conn\n}\n\nfunc NewTCPTracker(conn C.Conn, manager *Manager, metadata *C.Metadata, rule C.Rule, uploadTotal int64, downloadTotal int64, pushToManager bool) *tcpTracker {\n\tmetadata.RemoteDst = conn.RemoteDestination()\n\n\tchains := conn.Chains()\n\n\ttt := &tcpTracker{\n\t\tConn:    conn,\n\t\tmanager: manager,\n\t\tTrackerInfo: &TrackerInfo{\n\t\t\tUUID:          utils.NewUUIDV4(),\n\t\t\tStart:         time.Now(),\n\t\t\tMetadata:      metadata,\n\t\t\tChain:         chains,\n\t\t\tProviderChain: conn.ProviderChains(),\n\t\t\tRule:          \"\",\n\t\t\tUploadTotal:   atomic.NewInt64(uploadTotal),\n\t\t\tDownloadTotal: atomic.NewInt64(downloadTotal),\n\t\t},\n\t\tpushToManager: pushToManager,\n\t}\n\n\tif pushToManager {\n\t\tif uploadTotal > 0 {\n\t\t\tmanager.PushUploaded(chains.Last(), uploadTotal)\n\t\t}\n\t\tif downloadTotal > 0 {\n\t\t\tmanager.PushDownloaded(chains.Last(), downloadTotal)\n\t\t}\n\t}\n\n\tif rule != nil {\n\t\ttt.TrackerInfo.Rule = rule.RuleType().String()\n\t\ttt.TrackerInfo.RulePayload = rule.Payload()\n\t}\n\n\tmanager.Join(tt)\n\treturn tt\n}\n\ntype udpTracker struct {\n\tC.PacketConn `json:\"-\"`\n\t*TrackerInfo\n\tmanager *Manager\n\n\tpushToManager bool        `json:\"-\"`\n\tclosed        atomic.Bool `json:\"-\"`\n}\n\nfunc (ut *udpTracker) ID() string {\n\treturn ut.UUID.String()\n}\n\nfunc (ut *udpTracker) Info() *TrackerInfo {\n\treturn ut.TrackerInfo\n}\n\nfunc (ut *udpTracker) ReadFrom(b []byte) (int, net.Addr, error) {\n\tn, addr, err := ut.PacketConn.ReadFrom(b)\n\tdownload := int64(n)\n\tif ut.pushToManager {\n\t\tut.manager.PushDownloaded(ut.Chain.Last(), download)\n\t}\n\tut.DownloadTotal.Add(download)\n\treturn n, addr, err\n}\n\nfunc (ut *udpTracker) WaitReadFrom() (data []byte, put func(), addr net.Addr, err error) {\n\tdata, put, addr, err = ut.PacketConn.WaitReadFrom()\n\tdownload := int64(len(data))\n\tif ut.pushToManager {\n\t\tut.manager.PushDownloaded(ut.Chain.Last(), download)\n\t}\n\tut.DownloadTotal.Add(download)\n\treturn\n}\n\nfunc (ut *udpTracker) WriteTo(b []byte, addr net.Addr) (int, error) {\n\tn, err := ut.PacketConn.WriteTo(b, addr)\n\tupload := int64(n)\n\tif ut.pushToManager {\n\t\tut.manager.PushUploaded(ut.Chain.Last(), upload)\n\t}\n\tut.UploadTotal.Add(upload)\n\treturn n, err\n}\n\nfunc (ut *udpTracker) Close() error {\n\tif ut.closed.CompareAndSwap(false, true) {\n\t\tut.manager.Leave(ut)\n\t}\n\treturn ut.PacketConn.Close()\n}\n\nfunc (ut *udpTracker) Upstream() any {\n\treturn ut.PacketConn\n}\n\nfunc NewUDPTracker(conn C.PacketConn, manager *Manager, metadata *C.Metadata, rule C.Rule, uploadTotal int64, downloadTotal int64, pushToManager bool) *udpTracker {\n\tmetadata.RemoteDst = conn.RemoteDestination()\n\n\tchains := conn.Chains()\n\n\tut := &udpTracker{\n\t\tPacketConn: conn,\n\t\tmanager:    manager,\n\t\tTrackerInfo: &TrackerInfo{\n\t\t\tUUID:          utils.NewUUIDV4(),\n\t\t\tStart:         time.Now(),\n\t\t\tMetadata:      metadata,\n\t\t\tChain:         chains,\n\t\t\tProviderChain: conn.ProviderChains(),\n\t\t\tRule:          \"\",\n\t\t\tUploadTotal:   atomic.NewInt64(uploadTotal),\n\t\t\tDownloadTotal: atomic.NewInt64(downloadTotal),\n\t\t},\n\t\tpushToManager: pushToManager,\n\t}\n\n\tif pushToManager {\n\t\tif uploadTotal > 0 {\n\t\t\tmanager.PushUploaded(chains.Last(), uploadTotal)\n\t\t}\n\t\tif downloadTotal > 0 {\n\t\t\tmanager.PushDownloaded(chains.Last(), downloadTotal)\n\t\t}\n\t}\n\n\tif rule != nil {\n\t\tut.TrackerInfo.Rule = rule.RuleType().String()\n\t\tut.TrackerInfo.RulePayload = rule.Payload()\n\t}\n\n\tmanager.Join(ut)\n\treturn ut\n}\n"
  },
  {
    "path": "core/Clash.Meta/tunnel/status.go",
    "content": "package tunnel\n\nimport (\n\t\"errors\"\n\t\"strings\"\n)\n\ntype TunnelStatus int32\n\n// StatusMapping is a mapping for Status enum\nvar StatusMapping = map[string]TunnelStatus{\n\tSuspend.String(): Suspend,\n\tInner.String():   Inner,\n\tRunning.String(): Running,\n}\n\nconst (\n\tSuspend TunnelStatus = iota\n\tInner\n\tRunning\n)\n\n// UnmarshalText unserialize Status\nfunc (s *TunnelStatus) UnmarshalText(data []byte) error {\n\tstatus, exist := StatusMapping[strings.ToLower(string(data))]\n\tif !exist {\n\t\treturn errors.New(\"invalid status\")\n\t}\n\t*s = status\n\treturn nil\n}\n\n// MarshalText serialize Status\nfunc (s TunnelStatus) MarshalText() ([]byte, error) {\n\treturn []byte(s.String()), nil\n}\n\nfunc (s TunnelStatus) String() string {\n\tswitch s {\n\tcase Suspend:\n\t\treturn \"suspend\"\n\tcase Inner:\n\t\treturn \"inner\"\n\tcase Running:\n\t\treturn \"running\"\n\tdefault:\n\t\treturn \"Unknown\"\n\t}\n}\n"
  },
  {
    "path": "core/Clash.Meta/tunnel/tunnel.go",
    "content": "package tunnel\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net\"\n\t\"net/netip\"\n\t\"path/filepath\"\n\t\"runtime\"\n\t\"strings\"\n\t\"sync\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/common/atomic\"\n\tN \"github.com/metacubex/mihomo/common/net\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"github.com/metacubex/mihomo/component/loopback\"\n\t\"github.com/metacubex/mihomo/component/nat\"\n\t\"github.com/metacubex/mihomo/component/process\"\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\t\"github.com/metacubex/mihomo/component/slowdown\"\n\t\"github.com/metacubex/mihomo/component/sniffer\"\n\tC \"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/constant/features\"\n\tP \"github.com/metacubex/mihomo/constant/provider\"\n\ticontext \"github.com/metacubex/mihomo/context\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/tunnel/statistic\"\n)\n\nconst (\n\tqueueCapacity  = 64  // chan capacity tcpQueue and udpQueue\n\tsenderCapacity = 128 // chan capacity of PacketSender\n)\n\nvar (\n\tstatus        = atomic.NewInt32Enum(Suspend)\n\tudpInit       sync.Once\n\tudpQueues     []chan C.PacketAdapter\n\tnatTable      = nat.New()\n\trules         []C.Rule\n\tlisteners     = make(map[string]C.InboundListener)\n\tsubRules      map[string][]C.Rule\n\tproxies       = make(map[string]C.Proxy)\n\tproviders     map[string]P.ProxyProvider\n\truleProviders map[string]P.RuleProvider\n\tconfigMux     sync.RWMutex\n\n\t// for compatibility, lazy init\n\ttcpQueue  chan C.ConnContext\n\ttcpInOnce sync.Once\n\tudpQueue  chan C.PacketAdapter\n\tudpInOnce sync.Once\n\n\t// Outbound Rule\n\tmode = Rule\n\n\t// default timeout for UDP session\n\tudpTimeout = 60 * time.Second\n\n\tfindProcessMode = atomic.NewInt32Enum(process.FindProcessStrict)\n\n\tsnifferDispatcher *sniffer.Dispatcher\n\tsniffingEnable    = false\n\n\truleUpdateCallback = utils.NewCallback[P.RuleProvider]()\n)\n\ntype tunnel struct{}\n\nvar Tunnel = tunnel{}\nvar _ C.Tunnel = Tunnel\nvar _ P.Tunnel = Tunnel\n\nfunc (t tunnel) HandleTCPConn(conn net.Conn, metadata *C.Metadata) {\n\tconnCtx := icontext.NewConnContext(conn, metadata)\n\thandleTCPConn(connCtx)\n}\n\nfunc initUDP() {\n\tnumUDPWorkers := 4\n\tif num := runtime.GOMAXPROCS(0); num > numUDPWorkers {\n\t\tnumUDPWorkers = num\n\t}\n\n\tudpQueues = make([]chan C.PacketAdapter, numUDPWorkers)\n\tfor i := 0; i < numUDPWorkers; i++ {\n\t\tqueue := make(chan C.PacketAdapter, queueCapacity)\n\t\tudpQueues[i] = queue\n\t\tgo processUDP(queue)\n\t}\n}\n\nfunc (t tunnel) HandleUDPPacket(packet C.UDPPacket, metadata *C.Metadata) {\n\tudpInit.Do(initUDP)\n\n\tpacketAdapter := C.NewPacketAdapter(packet, metadata)\n\tkey := packetAdapter.Key()\n\n\thash := utils.MapHash(key)\n\tqueueNo := uint(hash) % uint(len(udpQueues))\n\n\tselect {\n\tcase udpQueues[queueNo] <- packetAdapter:\n\tdefault:\n\t\tpacket.Drop()\n\t}\n}\n\nfunc (t tunnel) NatTable() C.NatTable {\n\treturn natTable\n}\n\nfunc (t tunnel) Providers() map[string]P.ProxyProvider {\n\treturn providers\n}\n\nfunc (t tunnel) RuleProviders() map[string]P.RuleProvider {\n\treturn ruleProviders\n}\n\nfunc (t tunnel) RuleUpdateCallback() *utils.Callback[P.RuleProvider] {\n\treturn ruleUpdateCallback\n}\n\nfunc OnSuspend() {\n\tstatus.Store(Suspend)\n}\n\nfunc OnInnerLoading() {\n\tstatus.Store(Inner)\n}\n\nfunc OnRunning() {\n\tstatus.Store(Running)\n}\n\nfunc Status() TunnelStatus {\n\treturn status.Load()\n}\n\nfunc SetSniffing(b bool) {\n\tif snifferDispatcher.Enable() {\n\t\tconfigMux.Lock()\n\t\tsniffingEnable = b\n\t\tconfigMux.Unlock()\n\t}\n}\n\nfunc IsSniffing() bool {\n\treturn sniffingEnable\n}\n\n// TCPIn return fan-in queue\n// Deprecated: using Tunnel instead\nfunc TCPIn() chan<- C.ConnContext {\n\ttcpInOnce.Do(func() {\n\t\ttcpQueue = make(chan C.ConnContext, queueCapacity)\n\t\tgo func() {\n\t\t\tfor connCtx := range tcpQueue {\n\t\t\t\tgo handleTCPConn(connCtx)\n\t\t\t}\n\t\t}()\n\t})\n\treturn tcpQueue\n}\n\n// UDPIn return fan-in udp queue\n// Deprecated: using Tunnel instead\nfunc UDPIn() chan<- C.PacketAdapter {\n\tudpInOnce.Do(func() {\n\t\tudpQueue = make(chan C.PacketAdapter, queueCapacity)\n\t\tgo func() {\n\t\t\tfor packet := range udpQueue {\n\t\t\t\tTunnel.HandleUDPPacket(packet, packet.Metadata())\n\t\t\t}\n\t\t}()\n\t})\n\treturn udpQueue\n}\n\n// NatTable return nat table\nfunc NatTable() C.NatTable {\n\treturn natTable\n}\n\n// Rules return all rules\nfunc Rules() []C.Rule {\n\treturn rules\n}\n\nfunc Listeners() map[string]C.InboundListener {\n\treturn listeners\n}\n\n// UpdateRules handle update rules\nfunc UpdateRules(newRules []C.Rule, newSubRule map[string][]C.Rule, rp map[string]P.RuleProvider) {\n\tconfigMux.Lock()\n\trules = newRules\n\truleProviders = rp\n\tsubRules = newSubRule\n\tconfigMux.Unlock()\n}\n\n// Proxies return all proxies\nfunc Proxies() map[string]C.Proxy {\n\treturn proxies\n}\n\n// Providers return all compatible providers\nfunc Providers() map[string]P.ProxyProvider {\n\treturn providers\n}\n\n// ProxiesWithProviders return all proxies and providers\nfunc ProxiesWithProviders() map[string]C.Proxy {\n\tallProxies := make(map[string]C.Proxy)\n\tfor name, proxy := range proxies {\n\t\tallProxies[name] = proxy\n\t}\n\tfor _, p := range providers {\n\t\tfor _, proxy := range p.Proxies() {\n\t\t\tname := proxy.Name()\n\t\t\tallProxies[name] = proxy\n\t\t}\n\t}\n\treturn allProxies\n}\n\n// RuleProviders return all loaded rule providers\nfunc RuleProviders() map[string]P.RuleProvider {\n\treturn ruleProviders\n}\n\n// UpdateProxies handle update proxies\nfunc UpdateProxies(newProxies map[string]C.Proxy, newProviders map[string]P.ProxyProvider) {\n\tconfigMux.Lock()\n\tproxies = newProxies\n\tproviders = newProviders\n\tconfigMux.Unlock()\n}\n\nfunc UpdateListeners(newListeners map[string]C.InboundListener) {\n\tconfigMux.Lock()\n\tdefer configMux.Unlock()\n\tlisteners = newListeners\n}\n\nfunc UpdateSniffer(dispatcher *sniffer.Dispatcher) {\n\tconfigMux.Lock()\n\tsnifferDispatcher = dispatcher\n\tsniffingEnable = dispatcher.Enable()\n\tconfigMux.Unlock()\n}\n\n// Mode return current mode\nfunc Mode() TunnelMode {\n\treturn mode\n}\n\n// SetMode change the mode of tunnel\nfunc SetMode(m TunnelMode) {\n\tmode = m\n}\n\nfunc FindProcessMode() process.FindProcessMode {\n\treturn findProcessMode.Load()\n}\n\n// SetFindProcessMode replace SetAlwaysFindProcess\n// always find process info if legacyAlways = true or mode.Always() = true, may be increase many memory\nfunc SetFindProcessMode(mode process.FindProcessMode) {\n\tfindProcessMode.Store(mode)\n}\n\nfunc isHandle(t C.Type) bool {\n\tstatus := status.Load()\n\treturn status == Running || (status == Inner && t == C.INNER)\n}\n\nfunc fixMetadata(metadata *C.Metadata) {\n\t// first unmap dstIP\n\tmetadata.DstIP = metadata.DstIP.Unmap()\n\t// handle IP string on host\n\tif ip, err := netip.ParseAddr(metadata.Host); err == nil {\n\t\tmetadata.DstIP = ip.Unmap()\n\t\tmetadata.Host = \"\"\n\t}\n}\n\nfunc needLookupIP(metadata *C.Metadata) bool {\n\treturn resolver.MappingEnabled() && metadata.Host == \"\" && metadata.DstIP.IsValid()\n}\n\nfunc preHandleMetadata(metadata *C.Metadata) error {\n\t// preprocess enhanced-mode metadata\n\tif needLookupIP(metadata) {\n\t\thost, exist := resolver.FindHostByIP(metadata.DstIP)\n\t\tif exist {\n\t\t\tmetadata.Host = host\n\t\t\tmetadata.DNSMode = C.DNSMapping\n\t\t\tif resolver.IsFakeIP(metadata.DstIP) {\n\t\t\t\t// only clear dstIP if it is confirmed to be a fake IP\n\t\t\t\tmetadata.DstIP = netip.Addr{}\n\t\t\t\tmetadata.DNSMode = C.DNSFakeIP\n\t\t\t} else if node, ok := resolver.DefaultHosts.Search(host, false); ok {\n\t\t\t\t// redir-host should lookup the hosts\n\t\t\t\tmetadata.DstIP, _ = node.RandIP()\n\t\t\t} else if node != nil && node.IsDomain {\n\t\t\t\tmetadata.Host = node.Domain\n\t\t\t}\n\t\t} else if resolver.IsFakeIP(metadata.DstIP) {\n\t\t\treturn fmt.Errorf(\"fake DNS record %s missing\", metadata.DstIP)\n\t\t}\n\t} else if node, ok := resolver.DefaultHosts.Search(metadata.Host, true); ok {\n\t\t// try use domain mapping\n\t\tmetadata.Host = node.Domain\n\t}\n\n\treturn nil\n}\n\nfunc resolveMetadata(metadata *C.Metadata) (proxy C.Proxy, rule C.Rule, err error) {\n\tif metadata.SpecialProxy != \"\" {\n\t\tvar exist bool\n\t\tproxy, exist = proxies[metadata.SpecialProxy]\n\t\tif !exist {\n\t\t\terr = fmt.Errorf(\"proxy %s not found\", metadata.SpecialProxy)\n\t\t}\n\t\treturn\n\t}\n\tvar (\n\t\tresolved             bool\n\t\tattemptProcessLookup = metadata.Type != C.INNER\n\t)\n\n\tif node, ok := resolver.DefaultHosts.Search(metadata.Host, false); ok {\n\t\tmetadata.DstIP, _ = node.RandIP()\n\t\tresolved = true\n\t}\n\n\thelper := C.RuleMatchHelper{\n\t\tResolveIP: func() {\n\t\t\tif !resolved && metadata.Host != \"\" && !metadata.Resolved() {\n\t\t\t\tctx, cancel := context.WithTimeout(context.Background(), resolver.DefaultDNSTimeout)\n\t\t\t\tdefer cancel()\n\t\t\t\tip, err := resolver.ResolveIP(ctx, metadata.Host)\n\t\t\t\tif err != nil {\n\t\t\t\t\tlog.Debugln(\"[DNS] resolve %s error: %s\", metadata.Host, err.Error())\n\t\t\t\t} else {\n\t\t\t\t\tlog.Debugln(\"[DNS] %s --> %s\", metadata.Host, ip.String())\n\t\t\t\t\tmetadata.DstIP = ip\n\t\t\t\t}\n\t\t\t\tresolved = true\n\t\t\t}\n\t\t},\n\t\tFindProcess: func() {\n\t\t\tif attemptProcessLookup {\n\t\t\t\tattemptProcessLookup = false\n\t\t\t\tif !features.Android {\n\t\t\t\t// normal check for process\n\t\t\t\t\tuid, path, err := process.FindProcessName(metadata.NetWork.String(), metadata.SrcIP, int(metadata.SrcPort))\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tlog.Debugln(\"[Process] find process error for %s: %v\", metadata.String(), err)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmetadata.Process = filepath.Base(path)\n\t\t\t\t\t\tmetadata.ProcessPath = path\n\t\t\t\t\t\tmetadata.Uid = uid\n\n\t\t\t\t\t\tif pkg, err := process.FindPackageName(metadata); err == nil { // for android (not CMFA) package names\n\t\t\t\t\t\t\tmetadata.Process = pkg\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t} else {\n\t\t\t\t\t// check package names\n\t\t\t\t\tpkg, err := process.FindPackageName(metadata)\n\t\t\t\t\tif err != nil {\n\t\t\t\t\t\tlog.Debugln(\"[Process] find process error for %s: %v\", metadata.String(), err)\n\t\t\t\t\t} else {\n\t\t\t\t\t\tmetadata.Process = pkg\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\t\t},\n\t}\n\n\tswitch FindProcessMode() {\n\tcase process.FindProcessAlways:\n\t\thelper.FindProcess()\n\t\thelper.FindProcess = nil\n\tcase process.FindProcessOff:\n\t\thelper.FindProcess = nil\n\t}\n\n\tswitch mode {\n\tcase Direct:\n\t\tproxy = proxies[\"DIRECT\"]\n\tcase Global:\n\t\tproxy = proxies[\"GLOBAL\"]\n\t// Rule\n\tdefault:\n\t\tproxy, rule, err = match(metadata, helper)\n\t}\n\treturn\n}\n\n// processUDP starts a loop to handle udp packet\nfunc processUDP(queue chan C.PacketAdapter) {\n\tfor conn := range queue {\n\t\thandleUDPConn(conn)\n\t}\n}\n\nfunc handleUDPConn(packet C.PacketAdapter) {\n\tif !isHandle(packet.Metadata().Type) {\n\t\tpacket.Drop()\n\t\treturn\n\t}\n\n\tmetadata := packet.Metadata()\n\tif !metadata.Valid() {\n\t\tpacket.Drop()\n\t\tlog.Warnln(\"[Metadata] not valid: %#v\", metadata)\n\t\treturn\n\t}\n\tfixMetadata(metadata) // fix some metadata not set via metadata.SetRemoteAddr or metadata.SetRemoteAddress\n\n\tif err := preHandleMetadata(metadata.Clone()); err != nil { // precheck without modify metadata\n\t\tpacket.Drop()\n\t\tlog.Debugln(\"[Metadata PreHandle] error: %s\", err)\n\t\treturn\n\t}\n\n\tkey := packet.Key()\n\tsender, loaded := natTable.GetOrCreate(key, func() C.PacketSender {\n\t\tsender := newPacketSender()\n\t\tif sniffingEnable && snifferDispatcher.Enable() {\n\t\t\treturn snifferDispatcher.UDPSniff(packet, sender)\n\t\t}\n\t\treturn sender\n\t})\n\tif !loaded {\n\t\tdial := func() (C.PacketConn, C.WriteBackProxy, error) {\n\t\t\toriginMetadata := metadata  // save origin metadata\n\t\t\tmetadata = metadata.Clone() // don't modify PacketAdapter's metadata\n\n\t\t\tif err := sender.DoSniff(metadata); err != nil {\n\t\t\t\tlog.Warnln(\"[UDP] DoSniff error: %s\", err.Error())\n\t\t\t\treturn nil, nil, err\n\t\t\t}\n\n\t\t\t_ = preHandleMetadata(metadata) // error was pre-checked\n\n\t\t\tproxy, rule, err := resolveMetadata(metadata)\n\t\t\tif err != nil {\n\t\t\t\tlog.Warnln(\"[UDP] Parse metadata failed: %s\", err.Error())\n\t\t\t\treturn nil, nil, err\n\t\t\t}\n\n\t\t\tdialMetadata := metadata.Pure()\n\t\t\tctx, cancel := context.WithTimeout(context.Background(), C.DefaultUDPTimeout)\n\t\t\tdefer cancel()\n\t\t\trawPc, err := retry(ctx, func(ctx context.Context) (C.PacketConn, error) {\n\t\t\t\treturn proxy.ListenPacketContext(ctx, dialMetadata)\n\t\t\t}, func(err error) {\n\t\t\t\tlogMetadataErr(metadata, rule, proxy, err)\n\t\t\t})\n\t\t\tif err != nil {\n\t\t\t\treturn nil, nil, err\n\t\t\t}\n\t\t\tlogMetadata(metadata, rule, rawPc)\n\n\t\t\tpc := statistic.NewUDPTracker(rawPc, statistic.DefaultManager, metadata, rule, 0, 0, true)\n\n\t\t\tsender.AddMapping(originMetadata, dialMetadata)\n\t\t\toAddrPort := dialMetadata.AddrPort()\n\t\t\twriteBackProxy := nat.NewWriteBackProxy(packet)\n\n\t\t\tgo handleUDPToLocal(writeBackProxy, pc, sender, key, oAddrPort)\n\t\t\treturn pc, writeBackProxy, nil\n\t\t}\n\n\t\tgo func() {\n\t\t\tpc, proxy, err := dial()\n\t\t\tif err != nil {\n\t\t\t\tsender.Close()\n\t\t\t\tnatTable.Delete(key)\n\t\t\t\treturn\n\t\t\t}\n\t\t\tsender.Process(pc, proxy)\n\t\t}()\n\t}\n\tsender.Send(packet) // nonblocking\n}\n\nfunc handleTCPConn(connCtx C.ConnContext) {\n\tif !isHandle(connCtx.Metadata().Type) {\n\t\t_ = connCtx.Conn().Close()\n\t\treturn\n\t}\n\n\tdefer func(conn net.Conn) {\n\t\t_ = conn.Close()\n\t}(connCtx.Conn())\n\n\tmetadata := connCtx.Metadata()\n\tif !metadata.Valid() {\n\t\tlog.Warnln(\"[Metadata] not valid: %#v\", metadata)\n\t\treturn\n\t}\n\tfixMetadata(metadata) // fix some metadata not set via metadata.SetRemoteAddr or metadata.SetRemoteAddress\n\n\tpreHandleFailed := false\n\tif err := preHandleMetadata(metadata); err != nil {\n\t\tlog.Debugln(\"[Metadata PreHandle] error: %s\", err)\n\t\tpreHandleFailed = true\n\t}\n\n\tconn := connCtx.Conn()\n\tconn.ResetPeeked() // reset before sniffer\n\tif sniffingEnable && snifferDispatcher.Enable() {\n\t\t// Try to sniff a domain when `preHandleMetadata` failed, this is usually\n\t\t// caused by a \"Fake DNS record missing\" error when enhanced-mode is fake-ip.\n\t\tif snifferDispatcher.TCPSniff(conn, metadata) {\n\t\t\t// we now have a domain name\n\t\t\tpreHandleFailed = false\n\t\t}\n\t}\n\n\t// If both trials have failed, we can do nothing but give up\n\tif preHandleFailed {\n\t\tlog.Debugln(\"[Metadata PreHandle] failed to sniff a domain for connection %s --> %s, give up\",\n\t\t\tmetadata.SourceDetail(), metadata.RemoteAddress())\n\t\treturn\n\t}\n\n\tpeekMutex := sync.Mutex{}\n\tif !conn.Peeked() {\n\t\tpeekMutex.Lock()\n\t\tgo func() {\n\t\t\tdefer peekMutex.Unlock()\n\t\t\t_ = conn.SetReadDeadline(time.Now().Add(200 * time.Millisecond))\n\t\t\t_, _ = conn.Peek(1)\n\t\t\t_ = conn.SetReadDeadline(time.Time{})\n\t\t}()\n\t}\n\n\tproxy, rule, err := resolveMetadata(metadata)\n\tif err != nil {\n\t\tlog.Warnln(\"[Metadata] parse failed: %s\", err.Error())\n\t\treturn\n\t}\n\n\tdialMetadata := metadata\n\tif len(metadata.Host) > 0 {\n\t\tif node, ok := resolver.DefaultHosts.Search(metadata.Host, false); ok {\n\t\t\tif dstIp, _ := node.RandIP(); !resolver.IsFakeIP(dstIp) {\n\t\t\t\tdialMetadata.DstIP = dstIp\n\t\t\t\tdialMetadata.DNSMode = C.DNSHosts\n\t\t\t\tdialMetadata = dialMetadata.Pure()\n\t\t\t}\n\t\t}\n\t}\n\n\tvar peekBytes []byte\n\tvar peekLen int\n\n\tctx, cancel := context.WithTimeout(context.Background(), C.DefaultTCPTimeout)\n\tdefer cancel()\n\tremoteConn, err := retry(ctx, func(ctx context.Context) (remoteConn C.Conn, err error) {\n\t\tremoteConn, err = proxy.DialContext(ctx, dialMetadata)\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tif N.NeedHandshake(remoteConn) {\n\t\t\tdefer func() {\n\t\t\t\tfor _, chain := range remoteConn.Chains() {\n\t\t\t\t\tif chain == \"REJECT\" {\n\t\t\t\t\t\terr = nil\n\t\t\t\t\t\treturn\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif err != nil {\n\t\t\t\t\tremoteConn = nil\n\t\t\t\t}\n\t\t\t}()\n\t\t\tpeekMutex.Lock()\n\t\t\tdefer peekMutex.Unlock()\n\t\t\tpeekBytes, _ = conn.Peek(conn.Buffered())\n\t\t\t_, err = remoteConn.Write(peekBytes)\n\t\t\tif err != nil {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif peekLen = len(peekBytes); peekLen > 0 {\n\t\t\t\t_, _ = conn.Discard(peekLen)\n\t\t\t}\n\t\t}\n\t\treturn\n\t}, func(err error) {\n\t\tlogMetadataErr(metadata, rule, proxy, err)\n\t})\n\tif err != nil {\n\t\treturn\n\t}\n\tlogMetadata(metadata, rule, remoteConn)\n\n\tremoteConn = statistic.NewTCPTracker(remoteConn, statistic.DefaultManager, metadata, rule, int64(peekLen), 0, true)\n\tdefer func(remoteConn C.Conn) {\n\t\t_ = remoteConn.Close()\n\t}(remoteConn)\n\n\t_ = conn.SetReadDeadline(time.Now()) // stop unfinished peek\n\tpeekMutex.Lock()\n\tdefer peekMutex.Unlock()\n\t_ = conn.SetReadDeadline(time.Time{}) // reset\n\thandleSocket(conn, remoteConn)\n}\n\nfunc logMetadataErr(metadata *C.Metadata, rule C.Rule, proxy C.ProxyAdapter, err error) {\n\tif rule == nil {\n\t\tlog.Warnln(\"[%s] dial %s %s --> %s error: %s\", strings.ToUpper(metadata.NetWork.String()), proxy.Name(), metadata.SourceDetail(), metadata.RemoteAddress(), err.Error())\n\t} else {\n\t\tlog.Warnln(\"[%s] dial %s (match %s/%s) %s --> %s error: %s\", strings.ToUpper(metadata.NetWork.String()), proxy.Name(), rule.RuleType().String(), rule.Payload(), metadata.SourceDetail(), metadata.RemoteAddress(), err.Error())\n\t}\n}\n\nfunc logMetadata(metadata *C.Metadata, rule C.Rule, remoteConn C.Connection) {\n\tswitch {\n\tcase metadata.SpecialProxy != \"\":\n\t\tlog.Infoln(\"[%s] %s --> %s using %s\", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress(), remoteConn.Chains().String())\n\tcase rule != nil:\n\t\tif rule.Payload() != \"\" {\n\t\t\tlog.Infoln(\"[%s] %s --> %s match %s using %s\", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress(), fmt.Sprintf(\"%s(%s)\", rule.RuleType().String(), rule.Payload()), remoteConn.Chains().String())\n\t\t} else {\n\t\t\tlog.Infoln(\"[%s] %s --> %s match %s using %s\", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress(), rule.RuleType().String(), remoteConn.Chains().String())\n\t\t}\n\tcase mode == Global:\n\t\tlog.Infoln(\"[%s] %s --> %s using GLOBAL\", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress())\n\tcase mode == Direct:\n\t\tlog.Infoln(\"[%s] %s --> %s using DIRECT\", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress())\n\tdefault:\n\t\tlog.Infoln(\"[%s] %s --> %s doesn't match any rule using %s\", strings.ToUpper(metadata.NetWork.String()), metadata.SourceDetail(), metadata.RemoteAddress(), remoteConn.Chains().String())\n\t}\n}\n\nfunc match(metadata *C.Metadata, helper C.RuleMatchHelper) (C.Proxy, C.Rule, error) {\n\tconfigMux.RLock()\n\tdefer configMux.RUnlock()\n\n\tfor _, rule := range getRules(metadata) {\n\t\tif matched, ada := rule.Match(metadata, helper); matched {\n\t\t\tadapter, ok := proxies[ada]\n\t\t\tif !ok {\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\t// parse multi-layer nesting\n\t\t\tpassed := false\n\t\t\tfor adapter := adapter; adapter != nil; adapter = adapter.Unwrap(metadata, false) {\n\t\t\t\tif adapter.Type() == C.Pass {\n\t\t\t\t\tpassed = true\n\t\t\t\t\tbreak\n\t\t\t\t}\n\t\t\t}\n\t\t\tif passed {\n\t\t\t\tlog.Debugln(\"%s match Pass rule\", adapter.Name())\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tif metadata.NetWork == C.UDP && !adapter.SupportUDP() {\n\t\t\t\tlog.Debugln(\"%s UDP is not supported\", adapter.Name())\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\treturn adapter, rule, nil\n\t\t}\n\t}\n\n\treturn proxies[\"DIRECT\"], nil, nil\n}\n\nfunc getRules(metadata *C.Metadata) []C.Rule {\n\tif sr, ok := subRules[metadata.SpecialRules]; ok {\n\t\tlog.Debugln(\"[Rule] use %s rules\", metadata.SpecialRules)\n\t\treturn sr\n\t} else {\n\t\tlog.Debugln(\"[Rule] use default rules\")\n\t\treturn rules\n\t}\n}\n\nfunc shouldStopRetry(err error) bool {\n\tif errors.Is(err, resolver.ErrIPNotFound) {\n\t\treturn true\n\t}\n\tif errors.Is(err, resolver.ErrIPVersion) {\n\t\treturn true\n\t}\n\tif errors.Is(err, resolver.ErrIPv6Disabled) {\n\t\treturn true\n\t}\n\tif errors.Is(err, loopback.ErrReject) {\n\t\treturn true\n\t}\n\treturn false\n}\n\nfunc retry[T any](ctx context.Context, ft func(context.Context) (T, error), fe func(err error)) (t T, err error) {\n\ts := slowdown.New()\n\tfor i := 0; i < 10; i++ {\n\t\tt, err = ft(ctx)\n\t\tif err != nil {\n\t\t\tif fe != nil {\n\t\t\t\tfe(err)\n\t\t\t}\n\t\t\tif shouldStopRetry(err) {\n\t\t\t\treturn\n\t\t\t}\n\t\t\tif s.Wait(ctx) == nil {\n\t\t\t\tcontinue\n\t\t\t} else {\n\t\t\t\treturn\n\t\t\t}\n\t\t} else {\n\t\t\tbreak\n\t\t}\n\t}\n\treturn\n}\n"
  },
  {
    "path": "core/action.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n)\n\ntype Action struct {\n\tId     string      `json:\"id\"`\n\tMethod Method      `json:\"method\"`\n\tData   interface{} `json:\"data\"`\n}\n\ntype ActionResult struct {\n\tId     string      `json:\"id\"`\n\tMethod Method      `json:\"method\"`\n\tData   interface{} `json:\"data\"`\n\tCode   int         `json:\"code\"`\n\tPort   int64\n}\n\nfunc (result ActionResult) Json() ([]byte, error) {\n\tdata, err := json.Marshal(result)\n\treturn data, err\n}\n\nfunc (result ActionResult) success(data interface{}) {\n\tresult.Code = 0\n\tresult.Data = data\n\tresult.send()\n}\n\nfunc (result ActionResult) error(data interface{}) {\n\tresult.Code = -1\n\tresult.Data = data\n\tresult.send()\n}\n\nfunc handleAction(action *Action, result ActionResult) {\n\tswitch action.Method {\n\tcase initClashMethod:\n\t\tparamsString := action.Data.(string)\n\t\tresult.success(handleInitClash(paramsString))\n\t\treturn\n\tcase getIsInitMethod:\n\t\tresult.success(handleGetIsInit())\n\t\treturn\n\tcase forceGcMethod:\n\t\thandleForceGc()\n\t\tresult.success(true)\n\t\treturn\n\tcase shutdownMethod:\n\t\tresult.success(handleShutdown())\n\t\treturn\n\tcase validateConfigMethod:\n\t\tdata := []byte(action.Data.(string))\n\t\tresult.success(handleValidateConfig(data))\n\t\treturn\n\tcase updateConfigMethod:\n\t\tdata := []byte(action.Data.(string))\n\t\tresult.success(handleUpdateConfig(data))\n\t\treturn\n\tcase setupConfigMethod:\n\t\tdata := []byte(action.Data.(string))\n\t\tresult.success(handleSetupConfig(data))\n\t\treturn\n\tcase getProxiesMethod:\n\t\tresult.success(handleGetProxies())\n\t\treturn\n\tcase changeProxyMethod:\n\t\tdata := action.Data.(string)\n\t\thandleChangeProxy(data, func(value string) {\n\t\t\tresult.success(value)\n\t\t})\n\t\treturn\n\tcase getTrafficMethod:\n\t\tresult.success(handleGetTraffic())\n\t\treturn\n\tcase getTotalTrafficMethod:\n\t\tresult.success(handleGetTotalTraffic())\n\t\treturn\n\tcase resetTrafficMethod:\n\t\thandleResetTraffic()\n\t\tresult.success(true)\n\t\treturn\n\tcase asyncTestDelayMethod:\n\t\tdata := action.Data.(string)\n\t\thandleAsyncTestDelay(data, func(value string) {\n\t\t\tresult.success(value)\n\t\t})\n\t\treturn\n\tcase getConnectionsMethod:\n\t\tresult.success(handleGetConnections())\n\t\treturn\n\tcase closeConnectionsMethod:\n\t\tresult.success(handleCloseConnections())\n\t\treturn\n\tcase resetConnectionsMethod:\n\t\tresult.success(handleResetConnections())\n\t\treturn\n\tcase getConfigMethod:\n\t\tpath := action.Data.(string)\n\t\tconfig, err := handleGetConfig(path)\n\t\tif err != nil {\n\t\t\tresult.error(err)\n\t\t\treturn\n\t\t}\n\t\tresult.success(config)\n\t\treturn\n\tcase closeConnectionMethod:\n\t\tid := action.Data.(string)\n\t\tresult.success(handleCloseConnection(id))\n\t\treturn\n\tcase getExternalProvidersMethod:\n\t\tresult.success(handleGetExternalProviders())\n\t\treturn\n\tcase getExternalProviderMethod:\n\t\texternalProviderName := action.Data.(string)\n\t\tresult.success(handleGetExternalProvider(externalProviderName))\n\tcase updateGeoDataMethod:\n\t\tparamsString := action.Data.(string)\n\t\tvar params = map[string]string{}\n\t\terr := json.Unmarshal([]byte(paramsString), &params)\n\t\tif err != nil {\n\t\t\tresult.success(err.Error())\n\t\t\treturn\n\t\t}\n\t\tgeoType := params[\"geo-type\"]\n\t\tgeoName := params[\"geo-name\"]\n\t\thandleUpdateGeoData(geoType, geoName, func(value string) {\n\t\t\tresult.success(value)\n\t\t})\n\t\treturn\n\tcase updateExternalProviderMethod:\n\t\tproviderName := action.Data.(string)\n\t\thandleUpdateExternalProvider(providerName, func(value string) {\n\t\t\tresult.success(value)\n\t\t})\n\t\treturn\n\tcase sideLoadExternalProviderMethod:\n\t\tparamsString := action.Data.(string)\n\t\tvar params = map[string]string{}\n\t\terr := json.Unmarshal([]byte(paramsString), &params)\n\t\tif err != nil {\n\t\t\tresult.success(err.Error())\n\t\t\treturn\n\t\t}\n\t\tproviderName := params[\"providerName\"]\n\t\tdata := params[\"data\"]\n\t\thandleSideLoadExternalProvider(providerName, []byte(data), func(value string) {\n\t\t\tresult.success(value)\n\t\t})\n\t\treturn\n\tcase startLogMethod:\n\t\thandleStartLog()\n\t\tresult.success(true)\n\t\treturn\n\tcase stopLogMethod:\n\t\thandleStopLog()\n\t\tresult.success(true)\n\t\treturn\n\tcase startListenerMethod:\n\t\tresult.success(handleStartListener())\n\t\treturn\n\tcase stopListenerMethod:\n\t\tresult.success(handleStopListener())\n\t\treturn\n\tcase getCountryCodeMethod:\n\t\tip := action.Data.(string)\n\t\thandleGetCountryCode(ip, func(value string) {\n\t\t\tresult.success(value)\n\t\t})\n\t\treturn\n\tcase getMemoryMethod:\n\t\thandleGetMemory(func(value string) {\n\t\t\tresult.success(value)\n\t\t})\n\t\treturn\n\tcase setStateMethod:\n\t\tdata := action.Data.(string)\n\t\thandleSetState(data)\n\t\tresult.success(true)\n\tcase flushFakeIPMethod:\n\t\tresult.success(handleFlushFakeIP())\n\t\treturn\n\tcase flushDnsCacheMethod:\n\t\thandleFlushDnsCache()\n\t\tresult.success(true)\n\t\treturn\n\tcase crashMethod:\n\t\tresult.success(true)\n\t\thandleCrash()\n\tdefault:\n\t\tnextHandle(action, result)\n\t}\n}\n"
  },
  {
    "path": "core/android_bride.go",
    "content": "//go:build android && cgo\n\npackage main\n\n/*\n#include <stdlib.h>\n\ntypedef void (*release_object_func)(void *obj);\n\ntypedef void (*protect_func)(void *tun_interface, int fd);\n\ntypedef const char* (*resolve_process_func)(void *tun_interface, int protocol, const char *source, const char *target, int uid);\n\nstatic void protect(protect_func fn, void *tun_interface, int fd) {\n    if (fn) {\n        fn(tun_interface, fd);\n    }\n}\n\nstatic const char* resolve_process(resolve_process_func fn, void *tun_interface, int protocol, const char *source, const char *target, int uid) {\n    if (fn) {\n        return fn(tun_interface, protocol, source, target, uid);\n    }\n    return \"\";\n}\n\nstatic void release_object(release_object_func fn, void *obj) {\n    if (fn) {\n        return fn(obj);\n    }\n}\n*/\nimport \"C\"\nimport (\n\t\"unsafe\"\n)\n\nvar (\n\tglobalCallbacks struct {\n\t\treleaseObjectFunc  C.release_object_func\n\t\tprotectFunc        C.protect_func\n\t\tresolveProcessFunc C.resolve_process_func\n\t}\n)\n\nfunc Protect(callback unsafe.Pointer, fd int) {\n\tif globalCallbacks.protectFunc != nil {\n\t\tC.protect(globalCallbacks.protectFunc, callback, C.int(fd))\n\t}\n}\n\nfunc ResolveProcess(callback unsafe.Pointer, protocol int, source, target string, uid int) string {\n\tif globalCallbacks.resolveProcessFunc == nil {\n\t\treturn \"\"\n\t}\n\ts := C.CString(source)\n\tdefer C.free(unsafe.Pointer(s))\n\tt := C.CString(target)\n\tdefer C.free(unsafe.Pointer(t))\n\tres := C.resolve_process(globalCallbacks.resolveProcessFunc, callback, C.int(protocol), s, t, C.int(uid))\n\tdefer C.free(unsafe.Pointer(res))\n\treturn C.GoString(res)\n}\n\nfunc releaseObject(callback unsafe.Pointer) {\n\tif globalCallbacks.releaseObjectFunc == nil {\n\t\treturn\n\t}\n\tC.release_object(globalCallbacks.releaseObjectFunc, callback)\n}\n\n//export registerCallbacks\nfunc registerCallbacks(markSocketFunc C.protect_func, resolveProcessFunc C.resolve_process_func, releaseObjectFunc C.release_object_func) {\n\tglobalCallbacks.protectFunc = markSocketFunc\n\tglobalCallbacks.resolveProcessFunc = resolveProcessFunc\n\tglobalCallbacks.releaseObjectFunc = releaseObjectFunc\n}\n"
  },
  {
    "path": "core/common.go",
    "content": "package main\n\nimport (\n\tb \"bytes\"\n\t\"context\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"github.com/metacubex/mihomo/adapter\"\n\t\"github.com/metacubex/mihomo/adapter/inbound\"\n\t\"github.com/metacubex/mihomo/adapter/outboundgroup\"\n\t\"github.com/metacubex/mihomo/adapter/provider\"\n\t\"github.com/metacubex/mihomo/common/batch\"\n\t\"github.com/metacubex/mihomo/component/dialer\"\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\t\"github.com/metacubex/mihomo/config\"\n\t\"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/constant/features\"\n\tcp \"github.com/metacubex/mihomo/constant/provider\"\n\t\"github.com/metacubex/mihomo/hub\"\n\t\"github.com/metacubex/mihomo/hub/route\"\n\t\"github.com/metacubex/mihomo/listener\"\n\t\"github.com/metacubex/mihomo/log\"\n\trp \"github.com/metacubex/mihomo/rules/provider\"\n\t\"github.com/metacubex/mihomo/tunnel\"\n\t\"os\"\n\t\"sync\"\n)\n\nvar (\n\tcurrentConfig *config.Config\n\tversion       = 0\n\tisRunning     = false\n\trunLock       sync.Mutex\n\tmBatch, _     = batch.New[bool](context.Background(), batch.WithConcurrencyNum[bool](50))\n)\n\ntype ExternalProviders []ExternalProvider\n\nfunc (a ExternalProviders) Len() int           { return len(a) }\nfunc (a ExternalProviders) Less(i, j int) bool { return a[i].Name < a[j].Name }\nfunc (a ExternalProviders) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }\n\nfunc getExternalProvidersRaw() map[string]cp.Provider {\n\teps := make(map[string]cp.Provider)\n\tfor n, p := range tunnel.Providers() {\n\t\tif p.VehicleType() != cp.Compatible {\n\t\t\teps[n] = p\n\t\t}\n\t}\n\tfor n, p := range tunnel.RuleProviders() {\n\t\tif p.VehicleType() != cp.Compatible {\n\t\t\teps[n] = p\n\t\t}\n\t}\n\treturn eps\n}\n\nfunc toExternalProvider(p cp.Provider) (*ExternalProvider, error) {\n\tswitch p.(type) {\n\tcase *provider.ProxySetProvider:\n\t\tpsp := p.(*provider.ProxySetProvider)\n\t\treturn &ExternalProvider{\n\t\t\tName:             psp.Name(),\n\t\t\tType:             psp.Type().String(),\n\t\t\tVehicleType:      psp.VehicleType().String(),\n\t\t\tCount:            psp.Count(),\n\t\t\tUpdateAt:         psp.UpdatedAt(),\n\t\t\tPath:             psp.Vehicle().Path(),\n\t\t\tSubscriptionInfo: psp.GetSubscriptionInfo(),\n\t\t}, nil\n\tcase *rp.RuleSetProvider:\n\t\trsp := p.(*rp.RuleSetProvider)\n\t\treturn &ExternalProvider{\n\t\t\tName:        rsp.Name(),\n\t\t\tType:        rsp.Type().String(),\n\t\t\tVehicleType: rsp.VehicleType().String(),\n\t\t\tCount:       rsp.Count(),\n\t\t\tUpdateAt:    rsp.UpdatedAt(),\n\t\t\tPath:        rsp.Vehicle().Path(),\n\t\t}, nil\n\tdefault:\n\t\treturn nil, errors.New(\"not external provider\")\n\t}\n}\n\nfunc sideUpdateExternalProvider(p cp.Provider, bytes []byte) error {\n\tswitch p.(type) {\n\tcase *provider.ProxySetProvider:\n\t\tpsp := p.(*provider.ProxySetProvider)\n\t\t_, _, err := psp.SideUpdate(bytes)\n\t\tif err == nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\tcase rp.RuleSetProvider:\n\t\trsp := p.(*rp.RuleSetProvider)\n\t\t_, _, err := rsp.SideUpdate(bytes)\n\t\tif err == nil {\n\t\t\treturn err\n\t\t}\n\t\treturn nil\n\tdefault:\n\t\treturn errors.New(\"not external provider\")\n\t}\n}\n\nfunc updateListeners() {\n\tif !isRunning {\n\t\treturn\n\t}\n\tif currentConfig == nil {\n\t\treturn\n\t}\n\tlisteners := currentConfig.Listeners\n\tgeneral := currentConfig.General\n\tlistener.PatchInboundListeners(listeners, tunnel.Tunnel, true)\n\tlistener.SetAllowLan(general.AllowLan)\n\tinbound.SetSkipAuthPrefixes(general.SkipAuthPrefixes)\n\tinbound.SetAllowedIPs(general.LanAllowedIPs)\n\tinbound.SetDisAllowedIPs(general.LanDisAllowedIPs)\n\tlistener.SetBindAddress(general.BindAddress)\n\tlistener.ReCreateHTTP(general.Port, tunnel.Tunnel)\n\tlistener.ReCreateSocks(general.SocksPort, tunnel.Tunnel)\n\tlistener.ReCreateRedir(general.RedirPort, tunnel.Tunnel)\n\tlistener.ReCreateTProxy(general.TProxyPort, tunnel.Tunnel)\n\tlistener.ReCreateMixed(general.MixedPort, tunnel.Tunnel)\n\tlistener.ReCreateShadowSocks(general.ShadowSocksConfig, tunnel.Tunnel)\n\tlistener.ReCreateVmess(general.VmessConfig, tunnel.Tunnel)\n\tlistener.ReCreateTuic(general.TuicServer, tunnel.Tunnel)\n\tif !features.Android {\n\t\tlistener.ReCreateTun(general.Tun, tunnel.Tunnel)\n\t}\n}\n\nfunc stopListeners() {\n\tlistener.StopListener()\n}\n\nfunc patchSelectGroup(mapping map[string]string) {\n\tfor name, proxy := range tunnel.ProxiesWithProviders() {\n\t\toutbound, ok := proxy.(*adapter.Proxy)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tselector, ok := outbound.ProxyAdapter.(outboundgroup.SelectAble)\n\t\tif !ok {\n\t\t\tcontinue\n\t\t}\n\n\t\tselected, exist := mapping[name]\n\t\tif !exist {\n\t\t\tcontinue\n\t\t}\n\n\t\tselector.ForceSet(selected)\n\t}\n}\n\nfunc defaultSetupParams() *SetupParams {\n\treturn &SetupParams{\n\t\tConfig:      config.DefaultRawConfig(),\n\t\tTestURL:     \"https://g.cn/generate_204\",\n\t\tSelectedMap: map[string]string{},\n\t}\n}\n\nfunc readFile(path string) ([]byte, error) {\n\tif _, err := os.Stat(path); os.IsNotExist(err) {\n\t\treturn nil, err\n\t}\n\tdata, err := os.ReadFile(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn data, err\n}\n\nfunc updateConfig(params *UpdateParams) {\n\trunLock.Lock()\n\tdefer runLock.Unlock()\n\tgeneral := currentConfig.General\n\tif params.MixedPort != nil {\n\t\tgeneral.MixedPort = *params.MixedPort\n\t}\n\tif params.Sniffing != nil {\n\t\tgeneral.Sniffing = *params.Sniffing\n\t\ttunnel.SetSniffing(general.Sniffing)\n\t}\n\tif params.FindProcessMode != nil {\n\t\tgeneral.FindProcessMode = *params.FindProcessMode\n\t\ttunnel.SetFindProcessMode(general.FindProcessMode)\n\t}\n\tif params.TCPConcurrent != nil {\n\t\tgeneral.TCPConcurrent = *params.TCPConcurrent\n\t\tdialer.SetTcpConcurrent(general.TCPConcurrent)\n\t}\n\tif params.Interface != nil {\n\t\tgeneral.Interface = *params.Interface\n\t\tdialer.DefaultInterface.Store(general.Interface)\n\t}\n\tif params.UnifiedDelay != nil {\n\t\tgeneral.UnifiedDelay = *params.UnifiedDelay\n\t\tadapter.UnifiedDelay.Store(general.UnifiedDelay)\n\t}\n\tif params.Mode != nil {\n\t\tgeneral.Mode = *params.Mode\n\t\ttunnel.SetMode(general.Mode)\n\t}\n\tif params.LogLevel != nil {\n\t\tgeneral.LogLevel = *params.LogLevel\n\t\tlog.SetLevel(general.LogLevel)\n\t}\n\tif params.IPv6 != nil {\n\t\tgeneral.IPv6 = *params.IPv6\n\t\tresolver.DisableIPv6 = !general.IPv6\n\t}\n\tif params.ExternalController != nil {\n\t\tcurrentConfig.Controller.ExternalController = *params.ExternalController\n\t\troute.ReCreateServer(&route.Config{\n\t\t\tAddr: currentConfig.Controller.ExternalController,\n\t\t})\n\t}\n\n\tif params.Tun != nil {\n\t\tgeneral.Tun.Enable = params.Tun.Enable\n\t\tgeneral.Tun.AutoRoute = *params.Tun.AutoRoute\n\t\tgeneral.Tun.Device = *params.Tun.Device\n\t\tgeneral.Tun.RouteAddress = *params.Tun.RouteAddress\n\t\tif params.Tun.RouteExcludeAddress != nil {\n\t\t\tgeneral.Tun.RouteExcludeAddress = *params.Tun.RouteExcludeAddress\n\t\t}\n\t\tif params.Tun.StrictRoute != nil {\n\t\t\tgeneral.Tun.StrictRoute = *params.Tun.StrictRoute\n\t\t}\n\t\tgeneral.Tun.DNSHijack = *params.Tun.DNSHijack\n\t\tgeneral.Tun.Stack = *params.Tun.Stack\n\t\tgeneral.Tun.DisableICMPForwarding = *params.Tun.DisableICMPForwarding\n\t}\n\n\tupdateListeners()\n}\n\nfunc setupConfig(params *SetupParams) error {\n\trunLock.Lock()\n\tdefer runLock.Unlock()\n\n\tif params.Config != nil && params.Config.ProxyGroup != nil {\n\t\tfor _, group := range params.Config.ProxyGroup {\n\t\t\tif elm, ok := group[\"tolerance\"]; ok {\n\t\t\t\tswitch v := elm.(type) {\n\t\t\t\tcase json.Number:\n\t\t\t\t\tif i, err := v.Int64(); err == nil {\n\t\t\t\t\t\tgroup[\"tolerance\"] = int(i)\n\t\t\t\t\t}\n\t\t\t\tcase float64:\n\t\t\t\t\tgroup[\"tolerance\"] = int(v)\n\t\t\t\tcase float32:\n\t\t\t\t\tgroup[\"tolerance\"] = int(v)\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\tconstant.DefaultTestURL = params.TestURL\n\tif params.OverrideTestUrl && params.Config != nil {\n\t\tif params.Config.ProxyGroup != nil {\n\t\t\tfor _, group := range params.Config.ProxyGroup {\n\t\t\t\tgroup[\"url\"] = params.TestURL\n\t\t\t}\n\t\t}\n\t}\n\n\tvar err error\n\tcurrentConfig, err = config.ParseRawConfig(params.Config)\n\tif err != nil {\n\t\tcurrentConfig, _ = config.ParseRawConfig(config.DefaultRawConfig())\n\t}\n\thub.ApplyConfig(currentConfig)\n\tpatchSelectGroup(params.SelectedMap)\n\tupdateListeners()\n\treturn err\n}\n\nfunc UnmarshalJson(data []byte, v any) error {\n\tdecoder := json.NewDecoder(b.NewReader(data))\n\tdecoder.UseNumber()\n\terr := decoder.Decode(v)\n\treturn err\n}\n"
  },
  {
    "path": "core/constant.go",
    "content": "package main\n\nimport (\n\t\"encoding/json\"\n\t\"net/netip\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/adapter/provider\"\n\tP \"github.com/metacubex/mihomo/component/process\"\n\t\"github.com/metacubex/mihomo/config\"\n\t\"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/tunnel\"\n)\n\ntype InitParams struct {\n\tHomeDir string `json:\"home-dir\"`\n\tVersion int    `json:\"version\"`\n}\n\ntype SetupParams struct {\n\tConfig          *config.RawConfig `json:\"config\"`\n\tSelectedMap     map[string]string `json:\"selected-map\"`\n\tTestURL         string            `json:\"test-url\"`\n\tOverrideTestUrl bool              `json:\"override-test-url\"`\n}\n\ntype UpdateParams struct {\n\tTun                *tunSchema         `json:\"tun\"`\n\tAllowLan           *bool              `json:\"allow-lan\"`\n\tMixedPort          *int               `json:\"mixed-port\"`\n\tFindProcessMode    *P.FindProcessMode `json:\"find-process-mode\"`\n\tMode               *tunnel.TunnelMode `json:\"mode\"`\n\tLogLevel           *log.LogLevel      `json:\"log-level\"`\n\tIPv6               *bool              `json:\"ipv6\"`\n\tSniffing           *bool              `json:\"sniffing\"`\n\tTCPConcurrent      *bool              `json:\"tcp-concurrent\"`\n\tExternalController *string            `json:\"external-controller\"`\n\tInterface          *string            `json:\"interface-name\"`\n\tUnifiedDelay       *bool              `json:\"unified-delay\"`\n}\n\ntype tunSchema struct {\n\tEnable                bool               `yaml:\"enable\" json:\"enable\"`\n\tDevice                *string            `yaml:\"device\" json:\"device\"`\n\tStack                 *constant.TUNStack `yaml:\"stack\" json:\"stack\"`\n\tDNSHijack             *[]string          `yaml:\"dns-hijack\" json:\"dns-hijack\"`\n\tAutoRoute             *bool              `yaml:\"auto-route\" json:\"auto-route\"`\n\tRouteAddress          *[]netip.Prefix    `yaml:\"route-address\" json:\"route-address,omitempty\"`\n\tRouteExcludeAddress   *[]netip.Prefix    `yaml:\"route-exclude-address\" json:\"route-exclude-address,omitempty\"`\n\tStrictRoute           *bool              `yaml:\"strict-route\" json:\"strict-route,omitempty\"`\n\tDisableICMPForwarding *bool              `yaml:\"disable-icmp-forwarding\" json:\"disable-icmp-forwarding,omitempty\"`\n}\n\ntype ChangeProxyParams struct {\n\tGroupName *string `json:\"group-name\"`\n\tProxyName *string `json:\"proxy-name\"`\n}\n\ntype TestDelayParams struct {\n\tProxyName string `json:\"proxy-name\"`\n\tTestUrl   string `json:\"test-url\"`\n\tTimeout   int64  `json:\"timeout\"`\n}\n\ntype ExternalProvider struct {\n\tName             string                     `json:\"name\"`\n\tType             string                     `json:\"type\"`\n\tVehicleType      string                     `json:\"vehicle-type\"`\n\tCount            int                        `json:\"count\"`\n\tPath             string                     `json:\"path\"`\n\tUpdateAt         time.Time                  `json:\"update-at\"`\n\tSubscriptionInfo *provider.SubscriptionInfo `json:\"subscription-info\"`\n}\n\nconst (\n\tmessageMethod                  Method = \"message\"\n\tinitClashMethod                Method = \"initClash\"\n\tgetIsInitMethod                Method = \"getIsInit\"\n\tforceGcMethod                  Method = \"forceGc\"\n\tshutdownMethod                 Method = \"shutdown\"\n\tvalidateConfigMethod           Method = \"validateConfig\"\n\tupdateConfigMethod             Method = \"updateConfig\"\n\tgetProxiesMethod               Method = \"getProxies\"\n\tchangeProxyMethod              Method = \"changeProxy\"\n\tgetTrafficMethod               Method = \"getTraffic\"\n\tgetTotalTrafficMethod          Method = \"getTotalTraffic\"\n\tresetTrafficMethod             Method = \"resetTraffic\"\n\tasyncTestDelayMethod           Method = \"asyncTestDelay\"\n\tgetConnectionsMethod           Method = \"getConnections\"\n\tcloseConnectionsMethod         Method = \"closeConnections\"\n\tresetConnectionsMethod         Method = \"resetConnectionsMethod\"\n\tcloseConnectionMethod          Method = \"closeConnection\"\n\tgetExternalProvidersMethod     Method = \"getExternalProviders\"\n\tgetExternalProviderMethod      Method = \"getExternalProvider\"\n\tgetCountryCodeMethod           Method = \"getCountryCode\"\n\tgetMemoryMethod                Method = \"getMemory\"\n\tupdateGeoDataMethod            Method = \"updateGeoData\"\n\tupdateExternalProviderMethod   Method = \"updateExternalProvider\"\n\tsideLoadExternalProviderMethod Method = \"sideLoadExternalProvider\"\n\tstartLogMethod                 Method = \"startLog\"\n\tstopLogMethod                  Method = \"stopLog\"\n\tstartListenerMethod            Method = \"startListener\"\n\tstopListenerMethod             Method = \"stopListener\"\n\tupdateDnsMethod                Method = \"updateDns\"\n\tsetStateMethod                 Method = \"setState\"\n\tgetAndroidVpnOptionsMethod     Method = \"getAndroidVpnOptions\"\n\tgetRunTimeMethod               Method = \"getRunTime\"\n\tgetCurrentProfileNameMethod    Method = \"getCurrentProfileName\"\n\tcrashMethod                    Method = \"crash\"\n\tsetupConfigMethod              Method = \"setupConfig\"\n\tgetConfigMethod                Method = \"getConfig\"\n\tflushFakeIPMethod              Method = \"flushFakeIP\"\n\tflushDnsCacheMethod            Method = \"flushDnsCache\"\n)\n\ntype Method string\n\ntype MessageType string\n\ntype Delay struct {\n\tUrl   string `json:\"url\"`\n\tName  string `json:\"name\"`\n\tValue int32  `json:\"value\"`\n}\n\ntype Message struct {\n\tType MessageType `json:\"type\"`\n\tData interface{} `json:\"data\"`\n}\n\nconst (\n\tLogMessage     MessageType = \"log\"\n\tDelayMessage   MessageType = \"delay\"\n\tRequestMessage MessageType = \"request\"\n\tLoadedMessage  MessageType = \"loaded\"\n)\n\nfunc (message *Message) Json() (string, error) {\n\tdata, err := json.Marshal(message)\n\treturn string(data), err\n}\n"
  },
  {
    "path": "core/dart-bridge/include/dart_api.h",
    "content": "/*\n * Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file\n * for details. All rights reserved. Use of this source code is governed by a\n * BSD-style license that can be found in the LICENSE file.\n */\n\n#ifndef RUNTIME_INCLUDE_DART_API_H_\n#define RUNTIME_INCLUDE_DART_API_H_\n\n/** \\mainpage Dart Embedding API Reference\n *\n * This reference describes the Dart Embedding API, which is used to embed the\n * Dart Virtual Machine within C/C++ applications.\n *\n * This reference is generated from the header include/dart_api.h.\n */\n\n/* __STDC_FORMAT_MACROS has to be defined before including <inttypes.h> to\n * enable platform independent printf format specifiers. */\n#ifndef __STDC_FORMAT_MACROS\n#define __STDC_FORMAT_MACROS\n#endif\n\n#include <assert.h>\n#include <inttypes.h>\n#include <stdbool.h>\n\n#if defined(__Fuchsia__)\n#include <zircon/types.h>\n#endif\n\n#ifdef __cplusplus\n#define DART_EXTERN_C extern \"C\"\n#else\n#define DART_EXTERN_C extern\n#endif\n\n#if defined(__CYGWIN__)\n#error Tool chain and platform not supported.\n#elif defined(_WIN32)\n#if defined(DART_SHARED_LIB)\n#define DART_EXPORT DART_EXTERN_C __declspec(dllexport)\n#else\n#define DART_EXPORT DART_EXTERN_C\n#endif\n#else\n#if __GNUC__ >= 4\n#if defined(DART_SHARED_LIB)\n#define DART_EXPORT                                                            \\\n  DART_EXTERN_C __attribute__((visibility(\"default\"))) __attribute((used))\n#else\n#define DART_EXPORT DART_EXTERN_C\n#endif\n#else\n#error Tool chain not supported.\n#endif\n#endif\n\n#if __GNUC__\n#define DART_WARN_UNUSED_RESULT __attribute__((warn_unused_result))\n#elif _MSC_VER\n#define DART_WARN_UNUSED_RESULT _Check_return_\n#else\n#define DART_WARN_UNUSED_RESULT\n#endif\n\n/*\n * =======\n * Handles\n * =======\n */\n\n/**\n * An isolate is the unit of concurrency in Dart. Each isolate has\n * its own memory and thread of control. No state is shared between\n * isolates. Instead, isolates communicate by message passing.\n *\n * Each thread keeps track of its current isolate, which is the\n * isolate which is ready to execute on the current thread. The\n * current isolate may be NULL, in which case no isolate is ready to\n * execute. Most of the Dart apis require there to be a current\n * isolate in order to function without error. The current isolate is\n * set by any call to Dart_CreateIsolateGroup or Dart_EnterIsolate.\n */\ntypedef struct _Dart_Isolate* Dart_Isolate;\ntypedef struct _Dart_IsolateGroup* Dart_IsolateGroup;\n\n/**\n * An object reference managed by the Dart VM garbage collector.\n *\n * Because the garbage collector may move objects, it is unsafe to\n * refer to objects directly. Instead, we refer to objects through\n * handles, which are known to the garbage collector and updated\n * automatically when the object is moved. Handles should be passed\n * by value (except in cases like out-parameters) and should never be\n * allocated on the heap.\n *\n * Most functions in the Dart Embedding API return a handle. When a\n * function completes normally, this will be a valid handle to an\n * object in the Dart VM heap. This handle may represent the result of\n * the operation or it may be a special valid handle used merely to\n * indicate successful completion. Note that a valid handle may in\n * some cases refer to the null object.\n *\n * --- Error handles ---\n *\n * When a function encounters a problem that prevents it from\n * completing normally, it returns an error handle (See Dart_IsError).\n * An error handle has an associated error message that gives more\n * details about the problem (See Dart_GetError).\n *\n * There are four kinds of error handles that can be produced,\n * depending on what goes wrong:\n *\n * - Api error handles are produced when an api function is misused.\n *   This happens when a Dart embedding api function is called with\n *   invalid arguments or in an invalid context.\n *\n * - Unhandled exception error handles are produced when, during the\n *   execution of Dart code, an exception is thrown but not caught.\n *   Prototypically this would occur during a call to Dart_Invoke, but\n *   it can occur in any function which triggers the execution of Dart\n *   code (for example, Dart_ToString).\n *\n *   An unhandled exception error provides access to an exception and\n *   stacktrace via the functions Dart_ErrorGetException and\n *   Dart_ErrorGetStackTrace.\n *\n * - Compilation error handles are produced when, during the execution\n *   of Dart code, a compile-time error occurs.  As above, this can\n *   occur in any function which triggers the execution of Dart code.\n *\n * - Fatal error handles are produced when the system wants to shut\n *   down the current isolate.\n *\n * --- Propagating errors ---\n *\n * When an error handle is returned from the top level invocation of\n * Dart code in a program, the embedder must handle the error as they\n * see fit.  Often, the embedder will print the error message produced\n * by Dart_Error and exit the program.\n *\n * When an error is returned while in the body of a native function,\n * it can be propagated up the call stack by calling\n * Dart_PropagateError, Dart_SetReturnValue, or Dart_ThrowException.\n * Errors should be propagated unless there is a specific reason not\n * to.  If an error is not propagated then it is ignored.  For\n * example, if an unhandled exception error is ignored, that\n * effectively \"catches\" the unhandled exception.  Fatal errors must\n * always be propagated.\n *\n * When an error is propagated, any current scopes created by\n * Dart_EnterScope will be exited.\n *\n * Using Dart_SetReturnValue to propagate an exception is somewhat\n * more convenient than using Dart_PropagateError, and should be\n * preferred for reasons discussed below.\n *\n * Dart_PropagateError and Dart_ThrowException do not return.  Instead\n * they transfer control non-locally using a setjmp-like mechanism.\n * This can be inconvenient if you have resources that you need to\n * clean up before propagating the error.\n *\n * When relying on Dart_PropagateError, we often return error handles\n * rather than propagating them from helper functions.  Consider the\n * following contrived example:\n *\n * 1    Dart_Handle isLongStringHelper(Dart_Handle arg) {\n * 2      intptr_t* length = 0;\n * 3      result = Dart_StringLength(arg, &length);\n * 4      if (Dart_IsError(result)) {\n * 5        return result;\n * 6      }\n * 7      return Dart_NewBoolean(length > 100);\n * 8    }\n * 9\n * 10   void NativeFunction_isLongString(Dart_NativeArguments args) {\n * 11     Dart_EnterScope();\n * 12     AllocateMyResource();\n * 13     Dart_Handle arg = Dart_GetNativeArgument(args, 0);\n * 14     Dart_Handle result = isLongStringHelper(arg);\n * 15     if (Dart_IsError(result)) {\n * 16       FreeMyResource();\n * 17       Dart_PropagateError(result);\n * 18       abort();  // will not reach here\n * 19     }\n * 20     Dart_SetReturnValue(result);\n * 21     FreeMyResource();\n * 22     Dart_ExitScope();\n * 23   }\n *\n * In this example, we have a native function which calls a helper\n * function to do its work.  On line 5, the helper function could call\n * Dart_PropagateError, but that would not give the native function a\n * chance to call FreeMyResource(), causing a leak.  Instead, the\n * helper function returns the error handle to the caller, giving the\n * caller a chance to clean up before propagating the error handle.\n *\n * When an error is propagated by calling Dart_SetReturnValue, the\n * native function will be allowed to complete normally and then the\n * exception will be propagated only once the native call\n * returns. This can be convenient, as it allows the C code to clean\n * up normally.\n *\n * The example can be written more simply using Dart_SetReturnValue to\n * propagate the error.\n *\n * 1    Dart_Handle isLongStringHelper(Dart_Handle arg) {\n * 2      intptr_t* length = 0;\n * 3      result = Dart_StringLength(arg, &length);\n * 4      if (Dart_IsError(result)) {\n * 5        return result\n * 6      }\n * 7      return Dart_NewBoolean(length > 100);\n * 8    }\n * 9\n * 10   void NativeFunction_isLongString(Dart_NativeArguments args) {\n * 11     Dart_EnterScope();\n * 12     AllocateMyResource();\n * 13     Dart_Handle arg = Dart_GetNativeArgument(args, 0);\n * 14     Dart_SetReturnValue(isLongStringHelper(arg));\n * 15     FreeMyResource();\n * 16     Dart_ExitScope();\n * 17   }\n *\n * In this example, the call to Dart_SetReturnValue on line 14 will\n * either return the normal return value or the error (potentially\n * generated on line 3).  The call to FreeMyResource on line 15 will\n * execute in either case.\n *\n * --- Local and persistent handles ---\n *\n * Local handles are allocated within the current scope (see\n * Dart_EnterScope) and go away when the current scope exits. Unless\n * otherwise indicated, callers should assume that all functions in\n * the Dart embedding api return local handles.\n *\n * Persistent handles are allocated within the current isolate. They\n * can be used to store objects across scopes. Persistent handles have\n * the lifetime of the current isolate unless they are explicitly\n * deallocated (see Dart_DeletePersistentHandle).\n * The type Dart_Handle represents a handle (both local and persistent).\n * The type Dart_PersistentHandle is a Dart_Handle and it is used to\n * document that a persistent handle is expected as a parameter to a call\n * or the return value from a call is a persistent handle.\n *\n * FinalizableHandles are persistent handles which are auto deleted when\n * the object is garbage collected. It is never safe to use these handles\n * unless you know the object is still reachable.\n *\n * WeakPersistentHandles are persistent handles which are automatically set\n * to point Dart_Null when the object is garbage collected. They are not auto\n * deleted, so it is safe to use them after the object has become unreachable.\n */\ntypedef struct _Dart_Handle* Dart_Handle;\ntypedef Dart_Handle Dart_PersistentHandle;\ntypedef struct _Dart_WeakPersistentHandle* Dart_WeakPersistentHandle;\ntypedef struct _Dart_FinalizableHandle* Dart_FinalizableHandle;\n// These structs are versioned by DART_API_DL_MAJOR_VERSION, bump the\n// version when changing this struct.\n\ntypedef void (*Dart_HandleFinalizer)(void* isolate_callback_data, void* peer);\n\n/**\n * Is this an error handle?\n *\n * Requires there to be a current isolate.\n */\nDART_EXPORT bool Dart_IsError(Dart_Handle handle);\n\n/**\n * Is this an api error handle?\n *\n * Api error handles are produced when an api function is misused.\n * This happens when a Dart embedding api function is called with\n * invalid arguments or in an invalid context.\n *\n * Requires there to be a current isolate.\n */\nDART_EXPORT bool Dart_IsApiError(Dart_Handle handle);\n\n/**\n * Is this an unhandled exception error handle?\n *\n * Unhandled exception error handles are produced when, during the\n * execution of Dart code, an exception is thrown but not caught.\n * This can occur in any function which triggers the execution of Dart\n * code.\n *\n * See Dart_ErrorGetException and Dart_ErrorGetStackTrace.\n *\n * Requires there to be a current isolate.\n */\nDART_EXPORT bool Dart_IsUnhandledExceptionError(Dart_Handle handle);\n\n/**\n * Is this a compilation error handle?\n *\n * Compilation error handles are produced when, during the execution\n * of Dart code, a compile-time error occurs.  This can occur in any\n * function which triggers the execution of Dart code.\n *\n * Requires there to be a current isolate.\n */\nDART_EXPORT bool Dart_IsCompilationError(Dart_Handle handle);\n\n/**\n * Is this a fatal error handle?\n *\n * Fatal error handles are produced when the system wants to shut down\n * the current isolate.\n *\n * Requires there to be a current isolate.\n */\nDART_EXPORT bool Dart_IsFatalError(Dart_Handle handle);\n\n/**\n * Gets the error message from an error handle.\n *\n * Requires there to be a current isolate.\n *\n * \\return A C string containing an error message if the handle is\n *   error. An empty C string (\"\") if the handle is valid. This C\n *   String is scope allocated and is only valid until the next call\n *   to Dart_ExitScope.\n*/\nDART_EXPORT const char* Dart_GetError(Dart_Handle handle);\n\n/**\n * Is this an error handle for an unhandled exception?\n */\nDART_EXPORT bool Dart_ErrorHasException(Dart_Handle handle);\n\n/**\n * Gets the exception Object from an unhandled exception error handle.\n */\nDART_EXPORT Dart_Handle Dart_ErrorGetException(Dart_Handle handle);\n\n/**\n * Gets the stack trace Object from an unhandled exception error handle.\n */\nDART_EXPORT Dart_Handle Dart_ErrorGetStackTrace(Dart_Handle handle);\n\n/**\n * Produces an api error handle with the provided error message.\n *\n * Requires there to be a current isolate.\n *\n * \\param error the error message.\n */\nDART_EXPORT Dart_Handle Dart_NewApiError(const char* error);\nDART_EXPORT Dart_Handle Dart_NewCompilationError(const char* error);\n\n/**\n * Produces a new unhandled exception error handle.\n *\n * Requires there to be a current isolate.\n *\n * \\param exception An instance of a Dart object to be thrown or\n *        an ApiError or CompilationError handle.\n *        When an ApiError or CompilationError handle is passed in\n *        a string object of the error message is created and it becomes\n *        the Dart object to be thrown.\n */\nDART_EXPORT Dart_Handle Dart_NewUnhandledExceptionError(Dart_Handle exception);\n\n/**\n * Propagates an error.\n *\n * If the provided handle is an unhandled exception error, this\n * function will cause the unhandled exception to be rethrown.  This\n * will proceed in the standard way, walking up Dart frames until an\n * appropriate 'catch' block is found, executing 'finally' blocks,\n * etc.\n *\n * If the error is not an unhandled exception error, we will unwind\n * the stack to the next C frame.  Intervening Dart frames will be\n * discarded; specifically, 'finally' blocks will not execute.  This\n * is the standard way that compilation errors (and the like) are\n * handled by the Dart runtime.\n *\n * In either case, when an error is propagated any current scopes\n * created by Dart_EnterScope will be exited.\n *\n * See the additional discussion under \"Propagating Errors\" at the\n * beginning of this file.\n *\n * \\param handle An error handle (See Dart_IsError)\n *\n * On success, this function does not return.  On failure, the\n * process is terminated.\n */\nDART_EXPORT void Dart_PropagateError(Dart_Handle handle);\n\n/**\n * Converts an object to a string.\n *\n * May generate an unhandled exception error.\n *\n * \\return The converted string if no error occurs during\n *   the conversion. If an error does occur, an error handle is\n *   returned.\n */\nDART_EXPORT Dart_Handle Dart_ToString(Dart_Handle object);\n\n/**\n * Checks to see if two handles refer to identically equal objects.\n *\n * If both handles refer to instances, this is equivalent to using the top-level\n * function identical() from dart:core. Otherwise, returns whether the two\n * argument handles refer to the same object.\n *\n * \\param obj1 An object to be compared.\n * \\param obj2 An object to be compared.\n *\n * \\return True if the objects are identically equal.  False otherwise.\n */\nDART_EXPORT bool Dart_IdentityEquals(Dart_Handle obj1, Dart_Handle obj2);\n\n/**\n * Allocates a handle in the current scope from a persistent handle.\n */\nDART_EXPORT Dart_Handle Dart_HandleFromPersistent(Dart_PersistentHandle object);\n\n/**\n * Allocates a handle in the current scope from a weak persistent handle.\n *\n * This will be a handle to Dart_Null if the object has been garbage collected.\n */\nDART_EXPORT Dart_Handle\nDart_HandleFromWeakPersistent(Dart_WeakPersistentHandle object);\n\n/**\n * Allocates a persistent handle for an object.\n *\n * This handle has the lifetime of the current isolate unless it is\n * explicitly deallocated by calling Dart_DeletePersistentHandle.\n *\n * Requires there to be a current isolate.\n */\nDART_EXPORT Dart_PersistentHandle Dart_NewPersistentHandle(Dart_Handle object);\n\n/**\n * Assign value of local handle to a persistent handle.\n *\n * Requires there to be a current isolate.\n *\n * \\param obj1 A persistent handle whose value needs to be set.\n * \\param obj2 An object whose value needs to be set to the persistent handle.\n */\nDART_EXPORT void Dart_SetPersistentHandle(Dart_PersistentHandle obj1,\n                                          Dart_Handle obj2);\n\n/**\n * Deallocates a persistent handle.\n *\n * Requires there to be a current isolate group.\n */\nDART_EXPORT void Dart_DeletePersistentHandle(Dart_PersistentHandle object);\n\n/**\n * Allocates a weak persistent handle for an object.\n *\n * This handle has the lifetime of the current isolate. The handle can also be\n * explicitly deallocated by calling Dart_DeleteWeakPersistentHandle.\n *\n * If the object becomes unreachable the callback is invoked with the peer as\n * argument. The callback can be executed on any thread, will have a current\n * isolate group, but will not have a current isolate. The callback can only\n * call Dart_DeletePersistentHandle or Dart_DeleteWeakPersistentHandle. This\n * gives the embedder the ability to cleanup data associated with the object.\n * The handle will point to the Dart_Null object after the finalizer has been\n * run. It is illegal to call into the VM with any other Dart_* functions from\n * the callback. If the handle is deleted before the object becomes\n * unreachable, the callback is never invoked.\n *\n * Requires there to be a current isolate.\n *\n * \\param object An object with identity.\n * \\param peer A pointer to a native object or NULL.  This value is\n *   provided to callback when it is invoked.\n * \\param external_allocation_size The number of externally allocated\n *   bytes for peer. Used to inform the garbage collector.\n * \\param callback A function pointer that will be invoked sometime\n *   after the object is garbage collected, unless the handle has been deleted.\n *   A valid callback needs to be specified it cannot be NULL.\n *\n * \\return The weak persistent handle or NULL. NULL is returned in case of bad\n *   parameters.\n */\nDART_EXPORT Dart_WeakPersistentHandle\nDart_NewWeakPersistentHandle(Dart_Handle object,\n                             void* peer,\n                             intptr_t external_allocation_size,\n                             Dart_HandleFinalizer callback);\n\n/**\n * Deletes the given weak persistent [object] handle.\n *\n * Requires there to be a current isolate group.\n */\nDART_EXPORT void Dart_DeleteWeakPersistentHandle(\n    Dart_WeakPersistentHandle object);\n\n/**\n * Updates the external memory size for the given weak persistent handle.\n *\n * May trigger garbage collection.\n */\nDART_EXPORT void Dart_UpdateExternalSize(Dart_WeakPersistentHandle object,\n                                         intptr_t external_allocation_size);\n\n/**\n * Allocates a finalizable handle for an object.\n *\n * This handle has the lifetime of the current isolate group unless the object\n * pointed to by the handle is garbage collected, in this case the VM\n * automatically deletes the handle after invoking the callback associated\n * with the handle. The handle can also be explicitly deallocated by\n * calling Dart_DeleteFinalizableHandle.\n *\n * If the object becomes unreachable the callback is invoked with the\n * the peer as argument. The callback can be executed on any thread, will have\n * an isolate group, but will not have a current isolate. The callback can only\n * call Dart_DeletePersistentHandle or Dart_DeleteWeakPersistentHandle.\n * This gives the embedder the ability to cleanup data associated with the\n * object and clear out any cached references to the handle. All references to\n * this handle after the callback will be invalid. It is illegal to call into\n * the VM with any other Dart_* functions from the callback. If the handle is\n * deleted before the object becomes unreachable, the callback is never\n * invoked.\n *\n * Requires there to be a current isolate.\n *\n * \\param object An object with identity.\n * \\param peer A pointer to a native object or NULL.  This value is\n *   provided to callback when it is invoked.\n * \\param external_allocation_size The number of externally allocated\n *   bytes for peer. Used to inform the garbage collector.\n * \\param callback A function pointer that will be invoked sometime\n *   after the object is garbage collected, unless the handle has been deleted.\n *   A valid callback needs to be specified it cannot be NULL.\n *\n * \\return The finalizable handle or NULL. NULL is returned in case of bad\n *   parameters.\n */\nDART_EXPORT Dart_FinalizableHandle\nDart_NewFinalizableHandle(Dart_Handle object,\n                          void* peer,\n                          intptr_t external_allocation_size,\n                          Dart_HandleFinalizer callback);\n\n/**\n * Deletes the given finalizable [object] handle.\n *\n * The caller has to provide the actual Dart object the handle was created from\n * to prove the object (and therefore the finalizable handle) is still alive.\n *\n * Requires there to be a current isolate.\n */\nDART_EXPORT void Dart_DeleteFinalizableHandle(Dart_FinalizableHandle object,\n                                              Dart_Handle strong_ref_to_object);\n\n/**\n * Updates the external memory size for the given finalizable handle.\n *\n * The caller has to provide the actual Dart object the handle was created from\n * to prove the object (and therefore the finalizable handle) is still alive.\n *\n * May trigger garbage collection.\n */\nDART_EXPORT void Dart_UpdateFinalizableExternalSize(\n    Dart_FinalizableHandle object,\n    Dart_Handle strong_ref_to_object,\n    intptr_t external_allocation_size);\n\n/*\n * ==========================\n * Initialization and Globals\n * ==========================\n */\n\n/**\n * Gets the version string for the Dart VM.\n *\n * The version of the Dart VM can be accessed without initializing the VM.\n *\n * \\return The version string for the embedded Dart VM.\n */\nDART_EXPORT const char* Dart_VersionString(void);\n\n/**\n * Isolate specific flags are set when creating a new isolate using the\n * Dart_IsolateFlags structure.\n *\n * Current version of flags is encoded in a 32-bit integer with 16 bits used\n * for each part.\n */\n\n#define DART_FLAGS_CURRENT_VERSION (0x0000000c)\n\ntypedef struct {\n  int32_t version;\n  bool enable_asserts;\n  bool use_field_guards;\n  bool use_osr;\n  bool obfuscate;\n  bool load_vmservice_library;\n  bool copy_parent_code;\n  bool null_safety;\n  bool is_system_isolate;\n  bool snapshot_is_dontneed_safe;\n  bool branch_coverage;\n} Dart_IsolateFlags;\n\n/**\n * Initialize Dart_IsolateFlags with correct version and default values.\n */\nDART_EXPORT void Dart_IsolateFlagsInitialize(Dart_IsolateFlags* flags);\n\n/**\n * An isolate creation and initialization callback function.\n *\n * This callback, provided by the embedder, is called when the VM\n * needs to create an isolate. The callback should create an isolate\n * by calling Dart_CreateIsolateGroup and load any scripts required for\n * execution.\n *\n * This callback may be called on a different thread than the one\n * running the parent isolate.\n *\n * When the function returns NULL, it is the responsibility of this\n * function to ensure that Dart_ShutdownIsolate has been called if\n * required (for example, if the isolate was created successfully by\n * Dart_CreateIsolateGroup() but the root library fails to load\n * successfully, then the function should call Dart_ShutdownIsolate\n * before returning).\n *\n * When the function returns NULL, the function should set *error to\n * a malloc-allocated buffer containing a useful error message.  The\n * caller of this function (the VM) will make sure that the buffer is\n * freed.\n *\n * \\param script_uri The uri of the main source file or snapshot to load.\n *   Either the URI of the parent isolate set in Dart_CreateIsolateGroup for\n *   Isolate.spawn, or the argument to Isolate.spawnUri canonicalized by the\n *   library tag handler of the parent isolate.\n *   The callback is responsible for loading the program by a call to\n *   Dart_LoadScriptFromKernel.\n * \\param main The name of the main entry point this isolate will\n *   eventually run.  This is provided for advisory purposes only to\n *   improve debugging messages.  The main function is not invoked by\n *   this function.\n * \\param package_root Ignored.\n * \\param package_config Uri of the package configuration file (either in format\n *   of .packages or .dart_tool/package_config.json) for this isolate\n *   to resolve package imports against. If this parameter is not passed the\n *   package resolution of the parent isolate should be used.\n * \\param flags Default flags for this isolate being spawned. Either inherited\n *   from the spawning isolate or passed as parameters when spawning the\n *   isolate from Dart code.\n * \\param isolate_data The isolate data which was passed to the\n *   parent isolate when it was created by calling Dart_CreateIsolateGroup().\n * \\param error A structure into which the embedder can place a\n *   C string containing an error message in the case of failures.\n *\n * \\return The embedder returns NULL if the creation and\n *   initialization was not successful and the isolate if successful.\n */\ntypedef Dart_Isolate (*Dart_IsolateGroupCreateCallback)(\n    const char* script_uri,\n    const char* main,\n    const char* package_root,\n    const char* package_config,\n    Dart_IsolateFlags* flags,\n    void* isolate_data,\n    char** error);\n\n/**\n * An isolate initialization callback function.\n *\n * This callback, provided by the embedder, is called when the VM has created an\n * isolate within an existing isolate group (i.e. from the same source as an\n * existing isolate).\n *\n * The callback should setup native resolvers and might want to set a custom\n * message handler via [Dart_SetMessageNotifyCallback] and mark the isolate as\n * runnable.\n *\n * This callback may be called on a different thread than the one\n * running the parent isolate.\n *\n * When the function returns `false`, it is the responsibility of this\n * function to ensure that `Dart_ShutdownIsolate` has been called.\n *\n * When the function returns `false`, the function should set *error to\n * a malloc-allocated buffer containing a useful error message.  The\n * caller of this function (the VM) will make sure that the buffer is\n * freed.\n *\n * \\param child_isolate_data The callback data to associate with the new\n *        child isolate.\n * \\param error A structure into which the embedder can place a\n *   C string containing an error message in the case the initialization fails.\n *\n * \\return The embedder returns true if the initialization was successful and\n *         false otherwise (in which case the VM will terminate the isolate).\n */\ntypedef bool (*Dart_InitializeIsolateCallback)(void** child_isolate_data,\n                                               char** error);\n\n/**\n * An isolate shutdown callback function.\n *\n * This callback, provided by the embedder, is called before the vm\n * shuts down an isolate.  The isolate being shutdown will be the current\n * isolate. It is safe to run Dart code.\n *\n * This function should be used to dispose of native resources that\n * are allocated to an isolate in order to avoid leaks.\n *\n * \\param isolate_group_data The same callback data which was passed to the\n *   isolate group when it was created.\n * \\param isolate_data The same callback data which was passed to the isolate\n *   when it was created.\n */\ntypedef void (*Dart_IsolateShutdownCallback)(void* isolate_group_data,\n                                             void* isolate_data);\n\n/**\n * An isolate cleanup callback function.\n *\n * This callback, provided by the embedder, is called after the vm\n * shuts down an isolate. There will be no current isolate and it is *not*\n * safe to run Dart code.\n *\n * This function should be used to dispose of native resources that\n * are allocated to an isolate in order to avoid leaks.\n *\n * \\param isolate_group_data The same callback data which was passed to the\n *   isolate group when it was created.\n * \\param isolate_data The same callback data which was passed to the isolate\n *   when it was created.\n */\ntypedef void (*Dart_IsolateCleanupCallback)(void* isolate_group_data,\n                                            void* isolate_data);\n\n/**\n * An isolate group cleanup callback function.\n *\n * This callback, provided by the embedder, is called after the vm\n * shuts down an isolate group.\n *\n * This function should be used to dispose of native resources that\n * are allocated to an isolate in order to avoid leaks.\n *\n * \\param isolate_group_data The same callback data which was passed to the\n *   isolate group when it was created.\n *\n */\ntypedef void (*Dart_IsolateGroupCleanupCallback)(void* isolate_group_data);\n\n/**\n * A thread start callback function.\n * This callback, provided by the embedder, is called after a thread in the\n * vm thread pool starts.\n * This function could be used to adjust thread priority or attach native\n * resources to the thread.\n */\ntypedef void (*Dart_ThreadStartCallback)(void);\n\n/**\n * A thread death callback function.\n * This callback, provided by the embedder, is called before a thread in the\n * vm thread pool exits.\n * This function could be used to dispose of native resources that\n * are associated and attached to the thread, in order to avoid leaks.\n */\ntypedef void (*Dart_ThreadExitCallback)(void);\n\n/**\n * Opens a file for reading or writing.\n *\n * Callback provided by the embedder for file operations. If the\n * embedder does not allow file operations this callback can be\n * NULL.\n *\n * \\param name The name of the file to open.\n * \\param write A boolean variable which indicates if the file is to\n *   opened for writing. If there is an existing file it needs to truncated.\n */\ntypedef void* (*Dart_FileOpenCallback)(const char* name, bool write);\n\n/**\n * Read contents of file.\n *\n * Callback provided by the embedder for file operations. If the\n * embedder does not allow file operations this callback can be\n * NULL.\n *\n * \\param data Buffer allocated in the callback into which the contents\n *   of the file are read into. It is the responsibility of the caller to\n *   free this buffer.\n * \\param file_length A variable into which the length of the file is returned.\n *   In the case of an error this value would be -1.\n * \\param stream Handle to the opened file.\n */\ntypedef void (*Dart_FileReadCallback)(uint8_t** data,\n                                      intptr_t* file_length,\n                                      void* stream);\n\n/**\n * Write data into file.\n *\n * Callback provided by the embedder for file operations. If the\n * embedder does not allow file operations this callback can be\n * NULL.\n *\n * \\param data Buffer which needs to be written into the file.\n * \\param length Length of the buffer.\n * \\param stream Handle to the opened file.\n */\ntypedef void (*Dart_FileWriteCallback)(const void* data,\n                                       intptr_t length,\n                                       void* stream);\n\n/**\n * Closes the opened file.\n *\n * Callback provided by the embedder for file operations. If the\n * embedder does not allow file operations this callback can be\n * NULL.\n *\n * \\param stream Handle to the opened file.\n */\ntypedef void (*Dart_FileCloseCallback)(void* stream);\n\ntypedef bool (*Dart_EntropySource)(uint8_t* buffer, intptr_t length);\n\n/**\n * Callback provided by the embedder that is used by the vmservice isolate\n * to request the asset archive. The asset archive must be an uncompressed tar\n * archive that is stored in a Uint8List.\n *\n * If the embedder has no vmservice isolate assets, the callback can be NULL.\n *\n * \\return The embedder must return a handle to a Uint8List containing an\n *   uncompressed tar archive or null.\n */\ntypedef Dart_Handle (*Dart_GetVMServiceAssetsArchive)(void);\n\n/**\n * The current version of the Dart_InitializeFlags. Should be incremented every\n * time Dart_InitializeFlags changes in a binary incompatible way.\n */\n#define DART_INITIALIZE_PARAMS_CURRENT_VERSION (0x00000008)\n\n/** Forward declaration */\nstruct Dart_CodeObserver;\n\n/**\n * Callback provided by the embedder that is used by the VM to notify on code\n * object creation, *before* it is invoked the first time.\n * This is useful for embedders wanting to e.g. keep track of PCs beyond\n * the lifetime of the garbage collected code objects.\n * Note that an address range may be used by more than one code object over the\n * lifecycle of a process. Clients of this function should record timestamps for\n * these compilation events and when collecting PCs to disambiguate reused\n * address ranges.\n */\ntypedef void (*Dart_OnNewCodeCallback)(struct Dart_CodeObserver* observer,\n                                       const char* name,\n                                       uintptr_t base,\n                                       uintptr_t size);\n\ntypedef struct Dart_CodeObserver {\n  void* data;\n\n  Dart_OnNewCodeCallback on_new_code;\n} Dart_CodeObserver;\n\n/**\n * Optional callback provided by the embedder that is used by the VM to\n * implement registration of kernel blobs for the subsequent Isolate.spawnUri\n * If no callback is provided, the registration of kernel blobs will throw\n * an error.\n * \n * \\param kernel_buffer A buffer which contains a kernel program. Callback\n *                      should copy the contents of `kernel_buffer` as\n *                      it may be freed immediately after registration.\n * \\param kernel_buffer_size The size of `kernel_buffer`.\n *\n * \\return A C string representing URI which can be later used\n *         to spawn a new isolate. This C String should be scope allocated\n *         or owned by the embedder.\n *         Returns NULL if embedder runs out of memory.\n */\ntypedef const char* (*Dart_RegisterKernelBlobCallback)(\n    const uint8_t* kernel_buffer,\n    intptr_t kernel_buffer_size);\n\n/**\n * Optional callback provided by the embedder that is used by the VM to\n * unregister kernel blobs.\n * If no callback is provided, the unregistration of kernel blobs will throw\n * an error.\n * \n * \\param kernel_blob_uri URI of the kernel blob to unregister.\n */\ntypedef void (*Dart_UnregisterKernelBlobCallback)(const char* kernel_blob_uri);\n\n/**\n * Describes how to initialize the VM. Used with Dart_Initialize.\n */\ntypedef struct {\n  /**\n   * Identifies the version of the struct used by the client.\n   * should be initialized to DART_INITIALIZE_PARAMS_CURRENT_VERSION.\n   */\n  int32_t version;\n\n  /**\n   * A buffer containing snapshot data, or NULL if no snapshot is provided.\n   *\n   * If provided, the buffer must remain valid until Dart_Cleanup returns.\n   */\n  const uint8_t* vm_snapshot_data;\n\n  /**\n   * A buffer containing a snapshot of precompiled instructions, or NULL if\n   * no snapshot is provided.\n   *\n   * If provided, the buffer must remain valid until Dart_Cleanup returns.\n   */\n  const uint8_t* vm_snapshot_instructions;\n\n  /**\n   * A function to be called during isolate group creation.\n   * See Dart_IsolateGroupCreateCallback.\n   */\n  Dart_IsolateGroupCreateCallback create_group;\n\n  /**\n   * A function to be called during isolate\n   * initialization inside an existing isolate group.\n   * See Dart_InitializeIsolateCallback.\n   */\n  Dart_InitializeIsolateCallback initialize_isolate;\n\n  /**\n   * A function to be called right before an isolate is shutdown.\n   * See Dart_IsolateShutdownCallback.\n   */\n  Dart_IsolateShutdownCallback shutdown_isolate;\n\n  /**\n   * A function to be called after an isolate was shutdown.\n   * See Dart_IsolateCleanupCallback.\n   */\n  Dart_IsolateCleanupCallback cleanup_isolate;\n\n  /**\n   * A function to be called after an isolate group is\n   * shutdown. See Dart_IsolateGroupCleanupCallback.\n   */\n  Dart_IsolateGroupCleanupCallback cleanup_group;\n\n  Dart_ThreadStartCallback thread_start;\n  Dart_ThreadExitCallback thread_exit;\n  Dart_FileOpenCallback file_open;\n  Dart_FileReadCallback file_read;\n  Dart_FileWriteCallback file_write;\n  Dart_FileCloseCallback file_close;\n  Dart_EntropySource entropy_source;\n\n  /**\n   * A function to be called by the service isolate when it requires the\n   * vmservice assets archive. See Dart_GetVMServiceAssetsArchive.\n   */\n  Dart_GetVMServiceAssetsArchive get_service_assets;\n\n  bool start_kernel_isolate;\n\n  /**\n   * An external code observer callback function. The observer can be invoked\n   * as early as during the Dart_Initialize() call.\n   */\n  Dart_CodeObserver* code_observer;\n\n  /**\n   * Kernel blob registration callback function. See Dart_RegisterKernelBlobCallback.\n   */\n  Dart_RegisterKernelBlobCallback register_kernel_blob;\n\n  /**\n   * Kernel blob unregistration callback function. See Dart_UnregisterKernelBlobCallback.\n   */\n  Dart_UnregisterKernelBlobCallback unregister_kernel_blob;\n\n#if defined(__Fuchsia__)\n  /**\n   * The resource needed to use zx_vmo_replace_as_executable. Can be\n   * ZX_HANDLE_INVALID if the process has ambient-replace-as-executable or if\n   * executable memory is not needed (e.g., this is an AOT runtime).\n   */\n  zx_handle_t vmex_resource;\n#endif\n} Dart_InitializeParams;\n\n/**\n * Initializes the VM.\n *\n * \\param params A struct containing initialization information. The version\n *   field of the struct must be DART_INITIALIZE_PARAMS_CURRENT_VERSION.\n *\n * \\return NULL if initialization is successful. Returns an error message\n *   otherwise. The caller is responsible for freeing the error message.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT char* Dart_Initialize(\n    Dart_InitializeParams* params);\n\n/**\n * Cleanup state in the VM before process termination.\n *\n * \\return NULL if cleanup is successful. Returns an error message otherwise.\n *   The caller is responsible for freeing the error message.\n *\n * NOTE: This function must not be called on a thread that was created by the VM\n * itself.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT char* Dart_Cleanup(void);\n\n/**\n * Sets command line flags. Should be called before Dart_Initialize.\n *\n * \\param argc The length of the arguments array.\n * \\param argv An array of arguments.\n *\n * \\return NULL if successful. Returns an error message otherwise.\n *  The caller is responsible for freeing the error message.\n *\n * NOTE: This call does not store references to the passed in c-strings.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT char* Dart_SetVMFlags(int argc,\n                                                          const char** argv);\n\n/**\n * Returns true if the named VM flag is of boolean type, specified, and set to\n * true.\n *\n * \\param flag_name The name of the flag without leading punctuation\n *                  (example: \"enable_asserts\").\n */\nDART_EXPORT bool Dart_IsVMFlagSet(const char* flag_name);\n\n/*\n * ========\n * Isolates\n * ========\n */\n\n/**\n * Creates a new isolate. The new isolate becomes the current isolate.\n *\n * A snapshot can be used to restore the VM quickly to a saved state\n * and is useful for fast startup. If snapshot data is provided, the\n * isolate will be started using that snapshot data. Requires a core snapshot or\n * an app snapshot created by Dart_CreateSnapshot or\n * Dart_CreatePrecompiledSnapshot* from a VM with the same version.\n *\n * Requires there to be no current isolate.\n *\n * \\param script_uri The main source file or snapshot this isolate will load.\n *   The VM will provide this URI to the Dart_IsolateGroupCreateCallback when a\n *   child isolate is created by Isolate.spawn. The embedder should use a URI\n *   that allows it to load the same program into such a child isolate.\n * \\param name A short name for the isolate to improve debugging messages.\n *   Typically of the format 'foo.dart:main()'.\n * \\param isolate_snapshot_data Buffer containing the snapshot data of the\n *   isolate or NULL if no snapshot is provided. If provided, the buffer must\n *   remain valid until the isolate shuts down.\n * \\param isolate_snapshot_instructions Buffer containing the snapshot\n *   instructions of the isolate or NULL if no snapshot is provided. If\n *   provided, the buffer must remain valid until the isolate shuts down.\n * \\param flags Pointer to VM specific flags or NULL for default flags.\n * \\param isolate_group_data Embedder group data. This data can be obtained\n *   by calling Dart_IsolateGroupData and will be passed to the\n *   Dart_IsolateShutdownCallback, Dart_IsolateCleanupCallback, and\n *   Dart_IsolateGroupCleanupCallback.\n * \\param isolate_data Embedder data.  This data will be passed to\n *   the Dart_IsolateGroupCreateCallback when new isolates are spawned from\n *   this parent isolate.\n * \\param error Returns NULL if creation is successful, an error message\n *   otherwise. The caller is responsible for calling free() on the error\n *   message.\n *\n * \\return The new isolate on success, or NULL if isolate creation failed.\n */\nDART_EXPORT Dart_Isolate\nDart_CreateIsolateGroup(const char* script_uri,\n                        const char* name,\n                        const uint8_t* isolate_snapshot_data,\n                        const uint8_t* isolate_snapshot_instructions,\n                        Dart_IsolateFlags* flags,\n                        void* isolate_group_data,\n                        void* isolate_data,\n                        char** error);\n/**\n * Creates a new isolate inside the isolate group of [group_member].\n *\n * Requires there to be no current isolate.\n *\n * \\param group_member An isolate from the same group into which the newly created\n *   isolate should be born into. Other threads may not have entered / enter this\n *   member isolate.\n * \\param name A short name for the isolate for debugging purposes.\n * \\param shutdown_callback A callback to be called when the isolate is being\n *   shutdown (may be NULL).\n * \\param cleanup_callback A callback to be called when the isolate is being\n *   cleaned up (may be NULL).\n * \\param child_isolate_data The embedder-specific data associated with this isolate.\n * \\param error Set to NULL if creation is successful, set to an error\n *   message otherwise. The caller is responsible for calling free() on the\n *   error message.\n *\n * \\return The newly created isolate on success, or NULL if isolate creation\n *   failed.\n *\n * If successful, the newly created isolate will become the current isolate.\n */\nDART_EXPORT Dart_Isolate\nDart_CreateIsolateInGroup(Dart_Isolate group_member,\n                          const char* name,\n                          Dart_IsolateShutdownCallback shutdown_callback,\n                          Dart_IsolateCleanupCallback cleanup_callback,\n                          void* child_isolate_data,\n                          char** error);\n\n/* TODO(turnidge): Document behavior when there is already a current\n * isolate. */\n\n/**\n * Creates a new isolate from a Dart Kernel file. The new isolate\n * becomes the current isolate.\n *\n * Requires there to be no current isolate.\n *\n * \\param script_uri The main source file or snapshot this isolate will load.\n *   The VM will provide this URI to the Dart_IsolateGroupCreateCallback when a\n * child isolate is created by Isolate.spawn. The embedder should use a URI that\n *   allows it to load the same program into such a child isolate.\n * \\param name A short name for the isolate to improve debugging messages.\n *   Typically of the format 'foo.dart:main()'.\n * \\param kernel_buffer A buffer which contains a kernel/DIL program. Must\n *   remain valid until isolate shutdown.\n * \\param kernel_buffer_size The size of `kernel_buffer`.\n * \\param flags Pointer to VM specific flags or NULL for default flags.\n * \\param isolate_group_data Embedder group data. This data can be obtained\n *   by calling Dart_IsolateGroupData and will be passed to the\n *   Dart_IsolateShutdownCallback, Dart_IsolateCleanupCallback, and\n *   Dart_IsolateGroupCleanupCallback.\n * \\param isolate_data Embedder data.  This data will be passed to\n *   the Dart_IsolateGroupCreateCallback when new isolates are spawned from\n *   this parent isolate.\n * \\param error Returns NULL if creation is successful, an error message\n *   otherwise. The caller is responsible for calling free() on the error\n *   message.\n *\n * \\return The new isolate on success, or NULL if isolate creation failed.\n */\nDART_EXPORT Dart_Isolate\nDart_CreateIsolateGroupFromKernel(const char* script_uri,\n                                  const char* name,\n                                  const uint8_t* kernel_buffer,\n                                  intptr_t kernel_buffer_size,\n                                  Dart_IsolateFlags* flags,\n                                  void* isolate_group_data,\n                                  void* isolate_data,\n                                  char** error);\n/**\n * Shuts down the current isolate. After this call, the current isolate is NULL.\n * Any current scopes created by Dart_EnterScope will be exited. Invokes the\n * shutdown callback and any callbacks of remaining weak persistent handles.\n *\n * Requires there to be a current isolate.\n */\nDART_EXPORT void Dart_ShutdownIsolate(void);\n/* TODO(turnidge): Document behavior when there is no current isolate. */\n\n/**\n * Returns the current isolate. Will return NULL if there is no\n * current isolate.\n */\nDART_EXPORT Dart_Isolate Dart_CurrentIsolate(void);\n\n/**\n * Returns the callback data associated with the current isolate. This\n * data was set when the isolate got created or initialized.\n */\nDART_EXPORT void* Dart_CurrentIsolateData(void);\n\n/**\n * Returns the callback data associated with the given isolate. This\n * data was set when the isolate got created or initialized.\n */\nDART_EXPORT void* Dart_IsolateData(Dart_Isolate isolate);\n\n/**\n * Returns the current isolate group. Will return NULL if there is no\n * current isolate group.\n */\nDART_EXPORT Dart_IsolateGroup Dart_CurrentIsolateGroup(void);\n\n/**\n * Returns the callback data associated with the current isolate group. This\n * data was passed to the isolate group when it was created.\n */\nDART_EXPORT void* Dart_CurrentIsolateGroupData(void);\n\n/**\n * Gets an id that uniquely identifies current isolate group.\n *\n * It is the responsibility of the caller to free the returned ID.\n */\ntypedef int64_t Dart_IsolateGroupId;\nDART_EXPORT Dart_IsolateGroupId Dart_CurrentIsolateGroupId(void);\n\n/**\n * Returns the callback data associated with the specified isolate group. This\n * data was passed to the isolate when it was created.\n * The embedder is responsible for ensuring the consistency of this data\n * with respect to the lifecycle of an isolate group.\n */\nDART_EXPORT void* Dart_IsolateGroupData(Dart_Isolate isolate);\n\n/**\n * Returns the debugging name for the current isolate.\n *\n * This name is unique to each isolate and should only be used to make\n * debugging messages more comprehensible.\n */\nDART_EXPORT Dart_Handle Dart_DebugName(void);\n\n/**\n * Returns the debugging name for the current isolate.\n *\n * This name is unique to each isolate and should only be used to make\n * debugging messages more comprehensible.\n *\n * The returned string is scope allocated and is only valid until the next call\n * to Dart_ExitScope.\n */\nDART_EXPORT const char* Dart_DebugNameToCString(void);\n\n/**\n * Returns the ID for an isolate which is used to query the service protocol.\n *\n * It is the responsibility of the caller to free the returned ID.\n */\nDART_EXPORT const char* Dart_IsolateServiceId(Dart_Isolate isolate);\n\n/**\n * Enters an isolate. After calling this function,\n * the current isolate will be set to the provided isolate.\n *\n * Requires there to be no current isolate. Multiple threads may not be in\n * the same isolate at once.\n */\nDART_EXPORT void Dart_EnterIsolate(Dart_Isolate isolate);\n\n/**\n * Kills the given isolate.\n *\n * This function has the same effect as dart:isolate's\n * Isolate.kill(priority:immediate).\n * It can interrupt ordinary Dart code but not native code. If the isolate is\n * in the middle of a long running native function, the isolate will not be\n * killed until control returns to Dart.\n *\n * Does not require a current isolate. It is safe to kill the current isolate if\n * there is one.\n */\nDART_EXPORT void Dart_KillIsolate(Dart_Isolate isolate);\n\n/**\n * Notifies the VM that the embedder expects to be idle until |deadline|. The VM\n * may use this time to perform garbage collection or other tasks to avoid\n * delays during execution of Dart code in the future.\n *\n * |deadline| is measured in microseconds against the system's monotonic time.\n * This clock can be accessed via Dart_TimelineGetMicros().\n *\n * Requires there to be a current isolate.\n */\nDART_EXPORT void Dart_NotifyIdle(int64_t deadline);\n\ntypedef void (*Dart_HeapSamplingReportCallback)(void* context,\n                                                void* data);\n\ntypedef void* (*Dart_HeapSamplingCreateCallback)(\n    Dart_Isolate isolate,\n    Dart_IsolateGroup isolate_group,\n    const char* cls_name,\n    intptr_t allocation_size);\ntypedef void (*Dart_HeapSamplingDeleteCallback)(void* data);\n\n/**\n * Starts the heap sampling profiler for each thread in the VM.\n */\nDART_EXPORT void Dart_EnableHeapSampling(void);\n\n/*\n * Stops the heap sampling profiler for each thread in the VM.\n */\nDART_EXPORT void Dart_DisableHeapSampling(void);\n\n/* Registers callbacks are invoked once per sampled allocation upon object\n * allocation and garbage collection.\n *\n * |create_callback| can be used to associate additional data with the sampled\n * allocation, such as a stack trace. This data pointer will be passed to\n * |delete_callback| to allow for proper disposal when the object associated\n * with the allocation sample is collected.\n *\n * The provided callbacks must not call into the VM and should do as little\n * work as possible to avoid performance penalities during object allocation and\n * garbage collection.\n *\n * NOTE: It is a fatal error to set either callback to null once they have been\n * initialized.\n */\nDART_EXPORT void Dart_RegisterHeapSamplingCallback(\n    Dart_HeapSamplingCreateCallback create_callback,\n    Dart_HeapSamplingDeleteCallback delete_callback);\n\n/*\n * Reports the surviving allocation samples for all live isolate groups in the\n * VM.\n *\n * When the callback is invoked:\n *  - |context| will be the context object provided when invoking\n *    |Dart_ReportSurvivingAllocations|. This can be safely set to null if not\n *    required.\n *  - |heap_size| will be equal to the size of the allocated object associated\n *    with the sample.\n *  - |cls_name| will be a C String representing\n *    the class name of the allocated object. This string is valid for the\n *    duration of the call to Dart_ReportSurvivingAllocations and can be\n *    freed by the VM at any point after the method returns.\n *  - |data| will be set to the data associated with the sample by\n *    |Dart_HeapSamplingCreateCallback|.\n *\n * If |force_gc| is true, a full GC will be performed before reporting the\n * allocations.\n */\nDART_EXPORT void Dart_ReportSurvivingAllocations(\n    Dart_HeapSamplingReportCallback callback,\n    void* context,\n    bool force_gc);\n\n/*\n * Sets the average heap sampling rate based on a number of |bytes| for each\n * thread.\n *\n * In other words, approximately every |bytes| allocated will create a sample.\n * Defaults to 512 KiB.\n */\nDART_EXPORT void Dart_SetHeapSamplingPeriod(intptr_t bytes);\n\n/**\n * Notifies the VM that the embedder expects the application's working set has\n * recently shrunk significantly and is not expected to rise in the near future.\n * The VM may spend O(heap-size) time performing clean up work.\n *\n * Requires there to be a current isolate.\n */\nDART_EXPORT void Dart_NotifyDestroyed(void);\n\n/**\n * Notifies the VM that the system is running low on memory.\n *\n * Does not require a current isolate. Only valid after calling Dart_Initialize.\n */\nDART_EXPORT void Dart_NotifyLowMemory(void);\n\ntypedef enum {\n  /**\n   * Balanced\n   */\n  Dart_PerformanceMode_Default,\n  /**\n   * Optimize for low latency, at the expense of throughput and memory overhead\n   * by performing work in smaller batches (requiring more overhead) or by\n   * delaying work (requiring more memory). An embedder should not remain in\n   * this mode indefinitely.\n   */\n  Dart_PerformanceMode_Latency,\n  /**\n   * Optimize for high throughput, at the expense of latency and memory overhead\n   * by performing work in larger batches with more intervening growth.\n   */\n  Dart_PerformanceMode_Throughput,\n  /**\n   * Optimize for low memory, at the expensive of throughput and latency by more\n   * frequently performing work.\n   */\n  Dart_PerformanceMode_Memory,\n} Dart_PerformanceMode;\n\n/**\n * Set the desired performance trade-off.\n *\n * Requires a current isolate.\n *\n * Returns the previous performance mode.\n */\nDART_EXPORT Dart_PerformanceMode\nDart_SetPerformanceMode(Dart_PerformanceMode mode);\n\n/**\n * Starts the CPU sampling profiler.\n */\nDART_EXPORT void Dart_StartProfiling(void);\n\n/**\n * Stops the CPU sampling profiler.\n *\n * Note that some profile samples might still be taken after this function\n * returns due to the asynchronous nature of the implementation on some\n * platforms.\n */\nDART_EXPORT void Dart_StopProfiling(void);\n\n/**\n * Notifies the VM that the current thread should not be profiled until a\n * matching call to Dart_ThreadEnableProfiling is made.\n *\n * NOTE: By default, if a thread has entered an isolate it will be profiled.\n * This function should be used when an embedder knows a thread is about\n * to make a blocking call and wants to avoid unnecessary interrupts by\n * the profiler.\n */\nDART_EXPORT void Dart_ThreadDisableProfiling(void);\n\n/**\n * Notifies the VM that the current thread should be profiled.\n *\n * NOTE: It is only legal to call this function *after* calling\n *   Dart_ThreadDisableProfiling.\n *\n * NOTE: By default, if a thread has entered an isolate it will be profiled.\n */\nDART_EXPORT void Dart_ThreadEnableProfiling(void);\n\n/**\n * Register symbol information for the Dart VM's profiler and crash dumps.\n *\n * This consumes the output of //topaz/runtime/dart/profiler_symbols, which\n * should be treated as opaque.\n */\nDART_EXPORT void Dart_AddSymbols(const char* dso_name,\n                                 void* buffer,\n                                 intptr_t buffer_size);\n\n/**\n * Exits an isolate. After this call, Dart_CurrentIsolate will\n * return NULL.\n *\n * Requires there to be a current isolate.\n */\nDART_EXPORT void Dart_ExitIsolate(void);\n/* TODO(turnidge): We don't want users of the api to be able to exit a\n * \"pure\" dart isolate. Implement and document. */\n\n/**\n * Creates a full snapshot of the current isolate heap.\n *\n * A full snapshot is a compact representation of the dart vm isolate heap\n * and dart isolate heap states. These snapshots are used to initialize\n * the vm isolate on startup and fast initialization of an isolate.\n * A Snapshot of the heap is created before any dart code has executed.\n *\n * Requires there to be a current isolate. Not available in the precompiled\n * runtime (check Dart_IsPrecompiledRuntime).\n *\n * \\param vm_snapshot_data_buffer Returns a pointer to a buffer containing the\n *   vm snapshot. This buffer is scope allocated and is only valid\n *   until the next call to Dart_ExitScope.\n * \\param vm_snapshot_data_size Returns the size of vm_snapshot_data_buffer.\n * \\param isolate_snapshot_data_buffer Returns a pointer to a buffer containing\n *   the isolate snapshot. This buffer is scope allocated and is only valid\n *   until the next call to Dart_ExitScope.\n * \\param isolate_snapshot_data_size Returns the size of\n *   isolate_snapshot_data_buffer.\n * \\param is_core Create a snapshot containing core libraries.\n *   Such snapshot should be agnostic to null safety mode.\n *\n * \\return A valid handle if no error occurs during the operation.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle\nDart_CreateSnapshot(uint8_t** vm_snapshot_data_buffer,\n                    intptr_t* vm_snapshot_data_size,\n                    uint8_t** isolate_snapshot_data_buffer,\n                    intptr_t* isolate_snapshot_data_size,\n                    bool is_core);\n\n/**\n * Returns whether the buffer contains a kernel file.\n *\n * \\param buffer Pointer to a buffer that might contain a kernel binary.\n * \\param buffer_size Size of the buffer.\n *\n * \\return Whether the buffer contains a kernel binary (full or partial).\n */\nDART_EXPORT bool Dart_IsKernel(const uint8_t* buffer, intptr_t buffer_size);\n\n/**\n * Make isolate runnable.\n *\n * When isolates are spawned, this function is used to indicate that\n * the creation and initialization (including script loading) of the\n * isolate is complete and the isolate can start.\n * This function expects there to be no current isolate.\n *\n * \\param isolate The isolate to be made runnable.\n *\n * \\return NULL if successful. Returns an error message otherwise. The caller\n * is responsible for freeing the error message.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT char* Dart_IsolateMakeRunnable(\n    Dart_Isolate isolate);\n\n/*\n * ==================\n * Messages and Ports\n * ==================\n */\n\n/**\n * A port is used to send or receive inter-isolate messages\n */\ntypedef int64_t Dart_Port;\n\n/**\n * ILLEGAL_PORT is a port number guaranteed never to be associated with a valid\n * port.\n */\n#define ILLEGAL_PORT ((Dart_Port)0)\n\n/**\n * A message notification callback.\n *\n * This callback allows the embedder to provide a custom wakeup mechanism for\n * the delivery of inter-isolate messages. This function is called once per\n * message on an arbitrary thread. It is the responsibility of the embedder to\n * eventually call Dart_HandleMessage once per callback received with the\n * destination isolate set as the current isolate to process the message.\n */\ntypedef void (*Dart_MessageNotifyCallback)(Dart_Isolate destination_isolate);\n\n/**\n * Allows embedders to provide a custom wakeup mechanism for the delivery of\n * inter-isolate messages. This setting only applies to the current isolate.\n *\n * This mechanism is optional: if not provided, the isolate will be scheduled on\n * a VM-managed thread pool. An embedder should provide this callback if it\n * wants to run an isolate on a specific thread or to interleave handling of\n * inter-isolate messages with other event sources.\n *\n * Most embedders will only call this function once, before isolate\n * execution begins. If this function is called after isolate\n * execution begins, the embedder is responsible for threading issues.\n */\nDART_EXPORT void Dart_SetMessageNotifyCallback(\n    Dart_MessageNotifyCallback message_notify_callback);\n/* TODO(turnidge): Consider moving this to isolate creation so that it\n * is impossible to mess up. */\n\n/**\n * Query the current message notify callback for the isolate.\n *\n * \\return The current message notify callback for the isolate.\n */\nDART_EXPORT Dart_MessageNotifyCallback Dart_GetMessageNotifyCallback(void);\n\n/**\n * The VM's default message handler supports pausing an isolate before it\n * processes the first message and right after the it processes the isolate's\n * final message. This can be controlled for all isolates by two VM flags:\n *\n *   `--pause-isolates-on-start`\n *   `--pause-isolates-on-exit`\n *\n * Additionally, Dart_SetShouldPauseOnStart and Dart_SetShouldPauseOnExit can be\n * used to control this behaviour on a per-isolate basis.\n *\n * When an embedder is using a Dart_MessageNotifyCallback the embedder\n * needs to cooperate with the VM so that the service protocol can report\n * accurate information about isolates and so that tools such as debuggers\n * work reliably.\n *\n * The following functions can be used to implement pausing on start and exit.\n */\n\n/**\n * If the VM flag `--pause-isolates-on-start` was passed this will be true.\n *\n * \\return A boolean value indicating if pause on start was requested.\n */\nDART_EXPORT bool Dart_ShouldPauseOnStart(void);\n\n/**\n * Override the VM flag `--pause-isolates-on-start` for the current isolate.\n *\n * \\param should_pause Should the isolate be paused on start?\n *\n * NOTE: This must be called before Dart_IsolateMakeRunnable.\n */\nDART_EXPORT void Dart_SetShouldPauseOnStart(bool should_pause);\n\n/**\n * Is the current isolate paused on start?\n *\n * \\return A boolean value indicating if the isolate is paused on start.\n */\nDART_EXPORT bool Dart_IsPausedOnStart(void);\n\n/**\n * Called when the embedder has paused the current isolate on start and when\n * the embedder has resumed the isolate.\n *\n * \\param paused Is the isolate paused on start?\n */\nDART_EXPORT void Dart_SetPausedOnStart(bool paused);\n\n/**\n * If the VM flag `--pause-isolates-on-exit` was passed this will be true.\n *\n * \\return A boolean value indicating if pause on exit was requested.\n */\nDART_EXPORT bool Dart_ShouldPauseOnExit(void);\n\n/**\n * Override the VM flag `--pause-isolates-on-exit` for the current isolate.\n *\n * \\param should_pause Should the isolate be paused on exit?\n *\n */\nDART_EXPORT void Dart_SetShouldPauseOnExit(bool should_pause);\n\n/**\n * Is the current isolate paused on exit?\n *\n * \\return A boolean value indicating if the isolate is paused on exit.\n */\nDART_EXPORT bool Dart_IsPausedOnExit(void);\n\n/**\n * Called when the embedder has paused the current isolate on exit and when\n * the embedder has resumed the isolate.\n *\n * \\param paused Is the isolate paused on exit?\n */\nDART_EXPORT void Dart_SetPausedOnExit(bool paused);\n\n/**\n * Called when the embedder has caught a top level unhandled exception error\n * in the current isolate.\n *\n * NOTE: It is illegal to call this twice on the same isolate without first\n * clearing the sticky error to null.\n *\n * \\param error The unhandled exception error.\n */\nDART_EXPORT void Dart_SetStickyError(Dart_Handle error);\n\n/**\n * Does the current isolate have a sticky error?\n */\nDART_EXPORT bool Dart_HasStickyError(void);\n\n/**\n * Gets the sticky error for the current isolate.\n *\n * \\return A handle to the sticky error object or null.\n */\nDART_EXPORT Dart_Handle Dart_GetStickyError(void);\n\n/**\n * Handles the next pending message for the current isolate.\n *\n * May generate an unhandled exception error.\n *\n * \\return A valid handle if no error occurs during the operation.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_HandleMessage(void);\n\n/**\n * Drains the microtask queue, then blocks the calling thread until the current\n * isolate receives a message, then handles all messages.\n *\n * \\param timeout_millis When non-zero, the call returns after the indicated\n          number of milliseconds even if no message was received.\n * \\return A valid handle if no error occurs, otherwise an error handle.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle\nDart_WaitForEvent(int64_t timeout_millis);\n\n/**\n * Handles any pending messages for the vm service for the current\n * isolate.\n *\n * This function may be used by an embedder at a breakpoint to avoid\n * pausing the vm service.\n *\n * This function can indirectly cause the message notify callback to\n * be called.\n *\n * \\return true if the vm service requests the program resume\n * execution, false otherwise\n */\nDART_EXPORT bool Dart_HandleServiceMessages(void);\n\n/**\n * Does the current isolate have pending service messages?\n *\n * \\return true if the isolate has pending service messages, false otherwise.\n */\nDART_EXPORT bool Dart_HasServiceMessages(void);\n\n/**\n * Processes any incoming messages for the current isolate.\n *\n * This function may only be used when the embedder has not provided\n * an alternate message delivery mechanism with\n * Dart_SetMessageCallbacks. It is provided for convenience.\n *\n * This function waits for incoming messages for the current\n * isolate. As new messages arrive, they are handled using\n * Dart_HandleMessage. The routine exits when all ports to the\n * current isolate are closed.\n *\n * \\return A valid handle if the run loop exited successfully.  If an\n *   exception or other error occurs while processing messages, an\n *   error handle is returned.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_RunLoop(void);\n\n/**\n * Lets the VM run message processing for the isolate.\n *\n * This function expects there to a current isolate and the current isolate\n * must not have an active api scope. The VM will take care of making the\n * isolate runnable (if not already), handles its message loop and will take\n * care of shutting the isolate down once it's done.\n *\n * \\param errors_are_fatal Whether uncaught errors should be fatal.\n * \\param on_error_port A port to notify on uncaught errors (or ILLEGAL_PORT).\n * \\param on_exit_port A port to notify on exit (or ILLEGAL_PORT).\n * \\param error A non-NULL pointer which will hold an error message if the call\n *   fails. The error has to be free()ed by the caller.\n *\n * \\return If successful the VM takes ownership of the isolate and takes care\n *   of its message loop. If not successful the caller retains ownership of the\n *   isolate.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT bool Dart_RunLoopAsync(\n    bool errors_are_fatal,\n    Dart_Port on_error_port,\n    Dart_Port on_exit_port,\n    char** error);\n\n/* TODO(turnidge): Should this be removed from the public api? */\n\n/**\n * Gets the main port id for the current isolate.\n */\nDART_EXPORT Dart_Port Dart_GetMainPortId(void);\n\n/**\n * Does the current isolate have live ReceivePorts?\n *\n * A ReceivePort is live when it has not been closed.\n */\nDART_EXPORT bool Dart_HasLivePorts(void);\n\n/**\n * Posts a message for some isolate. The message is a serialized\n * object.\n *\n * Requires there to be a current isolate.\n *\n * For posting messages outside of an isolate see \\ref Dart_PostCObject.\n *\n * \\param port_id The destination port.\n * \\param object An object from the current isolate.\n *\n * \\return True if the message was posted.\n */\nDART_EXPORT bool Dart_Post(Dart_Port port_id, Dart_Handle object);\n\n/**\n * Returns a new SendPort with the provided port id.\n *\n * \\param port_id The destination port.\n *\n * \\return A new SendPort if no errors occurs. Otherwise returns\n *   an error handle.\n */\nDART_EXPORT Dart_Handle Dart_NewSendPort(Dart_Port port_id);\n\n/**\n * Gets the SendPort id for the provided SendPort.\n * \\param port A SendPort object whose id is desired.\n * \\param port_id Returns the id of the SendPort.\n * \\return Success if no error occurs. Otherwise returns\n *   an error handle.\n */\nDART_EXPORT Dart_Handle Dart_SendPortGetId(Dart_Handle port,\n                                           Dart_Port* port_id);\n\n/*\n * ======\n * Scopes\n * ======\n */\n\n/**\n * Enters a new scope.\n *\n * All new local handles will be created in this scope. Additionally,\n * some functions may return \"scope allocated\" memory which is only\n * valid within this scope.\n *\n * Requires there to be a current isolate.\n */\nDART_EXPORT void Dart_EnterScope(void);\n\n/**\n * Exits a scope.\n *\n * The previous scope (if any) becomes the current scope.\n *\n * Requires there to be a current isolate.\n */\nDART_EXPORT void Dart_ExitScope(void);\n\n/**\n * The Dart VM uses \"zone allocation\" for temporary structures. Zones\n * support very fast allocation of small chunks of memory. The chunks\n * cannot be deallocated individually, but instead zones support\n * deallocating all chunks in one fast operation.\n *\n * This function makes it possible for the embedder to allocate\n * temporary data in the VMs zone allocator.\n *\n * Zone allocation is possible:\n *   1. when inside a scope where local handles can be allocated\n *   2. when processing a message from a native port in a native port\n *      handler\n *\n * All the memory allocated this way will be reclaimed either on the\n * next call to Dart_ExitScope or when the native port handler exits.\n *\n * \\param size Size of the memory to allocate.\n *\n * \\return A pointer to the allocated memory. NULL if allocation\n *   failed. Failure might due to is no current VM zone.\n */\nDART_EXPORT uint8_t* Dart_ScopeAllocate(intptr_t size);\n\n/*\n * =======\n * Objects\n * =======\n */\n\n/**\n * Returns the null object.\n *\n * \\return A handle to the null object.\n */\nDART_EXPORT Dart_Handle Dart_Null(void);\n\n/**\n * Is this object null?\n */\nDART_EXPORT bool Dart_IsNull(Dart_Handle object);\n\n/**\n * Returns the empty string object.\n *\n * \\return A handle to the empty string object.\n */\nDART_EXPORT Dart_Handle Dart_EmptyString(void);\n\n/**\n * Returns types that are not classes, and which therefore cannot be looked up\n * as library members by Dart_GetType.\n *\n * \\return A handle to the dynamic, void or Never type.\n */\nDART_EXPORT Dart_Handle Dart_TypeDynamic(void);\nDART_EXPORT Dart_Handle Dart_TypeVoid(void);\nDART_EXPORT Dart_Handle Dart_TypeNever(void);\n\n/**\n * Checks if the two objects are equal.\n *\n * The result of the comparison is returned through the 'equal'\n * parameter. The return value itself is used to indicate success or\n * failure, not equality.\n *\n * May generate an unhandled exception error.\n *\n * \\param obj1 An object to be compared.\n * \\param obj2 An object to be compared.\n * \\param equal Returns the result of the equality comparison.\n *\n * \\return A valid handle if no error occurs during the comparison.\n */\nDART_EXPORT Dart_Handle Dart_ObjectEquals(Dart_Handle obj1,\n                                          Dart_Handle obj2,\n                                          bool* equal);\n\n/**\n * Is this object an instance of some type?\n *\n * The result of the test is returned through the 'instanceof' parameter.\n * The return value itself is used to indicate success or failure.\n *\n * \\param object An object.\n * \\param type A type.\n * \\param instanceof Return true if 'object' is an instance of type 'type'.\n *\n * \\return A valid handle if no error occurs during the operation.\n */\nDART_EXPORT Dart_Handle Dart_ObjectIsType(Dart_Handle object,\n                                          Dart_Handle type,\n                                          bool* instanceof);\n\n/**\n * Query object type.\n *\n * \\param object Some Object.\n *\n * \\return true if Object is of the specified type.\n */\nDART_EXPORT bool Dart_IsInstance(Dart_Handle object);\nDART_EXPORT bool Dart_IsNumber(Dart_Handle object);\nDART_EXPORT bool Dart_IsInteger(Dart_Handle object);\nDART_EXPORT bool Dart_IsDouble(Dart_Handle object);\nDART_EXPORT bool Dart_IsBoolean(Dart_Handle object);\nDART_EXPORT bool Dart_IsString(Dart_Handle object);\nDART_EXPORT bool Dart_IsStringLatin1(Dart_Handle object); /* (ISO-8859-1) */\nDART_EXPORT bool Dart_IsExternalString(Dart_Handle object);\nDART_EXPORT bool Dart_IsList(Dart_Handle object);\nDART_EXPORT bool Dart_IsMap(Dart_Handle object);\nDART_EXPORT bool Dart_IsLibrary(Dart_Handle object);\nDART_EXPORT bool Dart_IsType(Dart_Handle handle);\nDART_EXPORT bool Dart_IsFunction(Dart_Handle handle);\nDART_EXPORT bool Dart_IsVariable(Dart_Handle handle);\nDART_EXPORT bool Dart_IsTypeVariable(Dart_Handle handle);\nDART_EXPORT bool Dart_IsClosure(Dart_Handle object);\nDART_EXPORT bool Dart_IsTypedData(Dart_Handle object);\nDART_EXPORT bool Dart_IsByteBuffer(Dart_Handle object);\nDART_EXPORT bool Dart_IsFuture(Dart_Handle object);\n\n/*\n * =========\n * Instances\n * =========\n */\n\n/*\n * For the purposes of the embedding api, not all objects returned are\n * Dart language objects.  Within the api, we use the term 'Instance'\n * to indicate handles which refer to true Dart language objects.\n *\n * TODO(turnidge): Reorganize the \"Object\" section above, pulling down\n * any functions that more properly belong here. */\n\n/**\n * Gets the type of a Dart language object.\n *\n * \\param instance Some Dart object.\n *\n * \\return If no error occurs, the type is returned. Otherwise an\n *   error handle is returned.\n */\nDART_EXPORT Dart_Handle Dart_InstanceGetType(Dart_Handle instance);\n\n/**\n * Returns the name for the provided class type.\n *\n * \\return A valid string handle if no error occurs during the\n *   operation.\n */\nDART_EXPORT Dart_Handle Dart_ClassName(Dart_Handle cls_type);\n\n/**\n * Returns the name for the provided function or method.\n *\n * \\return A valid string handle if no error occurs during the\n *   operation.\n */\nDART_EXPORT Dart_Handle Dart_FunctionName(Dart_Handle function);\n\n/**\n * Returns a handle to the owner of a function.\n *\n * The owner of an instance method or a static method is its defining\n * class. The owner of a top-level function is its defining\n * library. The owner of the function of a non-implicit closure is the\n * function of the method or closure that defines the non-implicit\n * closure.\n *\n * \\return A valid handle to the owner of the function, or an error\n *   handle if the argument is not a valid handle to a function.\n */\nDART_EXPORT Dart_Handle Dart_FunctionOwner(Dart_Handle function);\n\n/**\n * Determines whether a function handle refers to a static function\n * of method.\n *\n * For the purposes of the embedding API, a top-level function is\n * implicitly declared static.\n *\n * \\param function A handle to a function or method declaration.\n * \\param is_static Returns whether the function or method is declared static.\n *\n * \\return A valid handle if no error occurs during the operation.\n */\nDART_EXPORT Dart_Handle Dart_FunctionIsStatic(Dart_Handle function,\n                                              bool* is_static);\n\n/**\n * Is this object a closure resulting from a tear-off (closurized method)?\n *\n * Returns true for closures produced when an ordinary method is accessed\n * through a getter call. Returns false otherwise, in particular for closures\n * produced from local function declarations.\n *\n * \\param object Some Object.\n *\n * \\return true if Object is a tear-off.\n */\nDART_EXPORT bool Dart_IsTearOff(Dart_Handle object);\n\n/**\n * Retrieves the function of a closure.\n *\n * \\return A handle to the function of the closure, or an error handle if the\n *   argument is not a closure.\n */\nDART_EXPORT Dart_Handle Dart_ClosureFunction(Dart_Handle closure);\n\n/**\n * Returns a handle to the library which contains class.\n *\n * \\return A valid handle to the library with owns class, null if the class\n *   has no library or an error handle if the argument is not a valid handle\n *   to a class type.\n */\nDART_EXPORT Dart_Handle Dart_ClassLibrary(Dart_Handle cls_type);\n\n/*\n * =============================\n * Numbers, Integers and Doubles\n * =============================\n */\n\n/**\n * Does this Integer fit into a 64-bit signed integer?\n *\n * \\param integer An integer.\n * \\param fits Returns true if the integer fits into a 64-bit signed integer.\n *\n * \\return A valid handle if no error occurs during the operation.\n */\nDART_EXPORT Dart_Handle Dart_IntegerFitsIntoInt64(Dart_Handle integer,\n                                                  bool* fits);\n\n/**\n * Does this Integer fit into a 64-bit unsigned integer?\n *\n * \\param integer An integer.\n * \\param fits Returns true if the integer fits into a 64-bit unsigned integer.\n *\n * \\return A valid handle if no error occurs during the operation.\n */\nDART_EXPORT Dart_Handle Dart_IntegerFitsIntoUint64(Dart_Handle integer,\n                                                   bool* fits);\n\n/**\n * Returns an Integer with the provided value.\n *\n * \\param value The value of the integer.\n *\n * \\return The Integer object if no error occurs. Otherwise returns\n *   an error handle.\n */\nDART_EXPORT Dart_Handle Dart_NewInteger(int64_t value);\n\n/**\n * Returns an Integer with the provided value.\n *\n * \\param value The unsigned value of the integer.\n *\n * \\return The Integer object if no error occurs. Otherwise returns\n *   an error handle.\n */\nDART_EXPORT Dart_Handle Dart_NewIntegerFromUint64(uint64_t value);\n\n/**\n * Returns an Integer with the provided value.\n *\n * \\param value The value of the integer represented as a C string\n *   containing a hexadecimal number.\n *\n * \\return The Integer object if no error occurs. Otherwise returns\n *   an error handle.\n */\nDART_EXPORT Dart_Handle Dart_NewIntegerFromHexCString(const char* value);\n\n/**\n * Gets the value of an Integer.\n *\n * The integer must fit into a 64-bit signed integer, otherwise an error occurs.\n *\n * \\param integer An Integer.\n * \\param value Returns the value of the Integer.\n *\n * \\return A valid handle if no error occurs during the operation.\n */\nDART_EXPORT Dart_Handle Dart_IntegerToInt64(Dart_Handle integer,\n                                            int64_t* value);\n\n/**\n * Gets the value of an Integer.\n *\n * The integer must fit into a 64-bit unsigned integer, otherwise an\n * error occurs.\n *\n * \\param integer An Integer.\n * \\param value Returns the value of the Integer.\n *\n * \\return A valid handle if no error occurs during the operation.\n */\nDART_EXPORT Dart_Handle Dart_IntegerToUint64(Dart_Handle integer,\n                                             uint64_t* value);\n\n/**\n * Gets the value of an integer as a hexadecimal C string.\n *\n * \\param integer An Integer.\n * \\param value Returns the value of the Integer as a hexadecimal C\n *   string. This C string is scope allocated and is only valid until\n *   the next call to Dart_ExitScope.\n *\n * \\return A valid handle if no error occurs during the operation.\n */\nDART_EXPORT Dart_Handle Dart_IntegerToHexCString(Dart_Handle integer,\n                                                 const char** value);\n\n/**\n * Returns a Double with the provided value.\n *\n * \\param value A double.\n *\n * \\return The Double object if no error occurs. Otherwise returns\n *   an error handle.\n */\nDART_EXPORT Dart_Handle Dart_NewDouble(double value);\n\n/**\n * Gets the value of a Double\n *\n * \\param double_obj A Double\n * \\param value Returns the value of the Double.\n *\n * \\return A valid handle if no error occurs during the operation.\n */\nDART_EXPORT Dart_Handle Dart_DoubleValue(Dart_Handle double_obj, double* value);\n\n/**\n * Returns a closure of static function 'function_name' in the class 'class_name'\n * in the exported namespace of specified 'library'.\n *\n * \\param library Library object\n * \\param cls_type Type object representing a Class\n * \\param function_name Name of the static function in the class\n *\n * \\return A valid Dart instance if no error occurs during the operation.\n */\nDART_EXPORT Dart_Handle Dart_GetStaticMethodClosure(Dart_Handle library,\n                                                    Dart_Handle cls_type,\n                                                    Dart_Handle function_name);\n\n/*\n * ========\n * Booleans\n * ========\n */\n\n/**\n * Returns the True object.\n *\n * Requires there to be a current isolate.\n *\n * \\return A handle to the True object.\n */\nDART_EXPORT Dart_Handle Dart_True(void);\n\n/**\n * Returns the False object.\n *\n * Requires there to be a current isolate.\n *\n * \\return A handle to the False object.\n */\nDART_EXPORT Dart_Handle Dart_False(void);\n\n/**\n * Returns a Boolean with the provided value.\n *\n * \\param value true or false.\n *\n * \\return The Boolean object if no error occurs. Otherwise returns\n *   an error handle.\n */\nDART_EXPORT Dart_Handle Dart_NewBoolean(bool value);\n\n/**\n * Gets the value of a Boolean\n *\n * \\param boolean_obj A Boolean\n * \\param value Returns the value of the Boolean.\n *\n * \\return A valid handle if no error occurs during the operation.\n */\nDART_EXPORT Dart_Handle Dart_BooleanValue(Dart_Handle boolean_obj, bool* value);\n\n/*\n * =======\n * Strings\n * =======\n */\n\n/**\n * Gets the length of a String.\n *\n * \\param str A String.\n * \\param length Returns the length of the String.\n *\n * \\return A valid handle if no error occurs during the operation.\n */\nDART_EXPORT Dart_Handle Dart_StringLength(Dart_Handle str, intptr_t* length);\n\n/**\n * Returns a String built from the provided C string\n * (There is an implicit assumption that the C string passed in contains\n *  UTF-8 encoded characters and '\\0' is considered as a termination\n *  character).\n *\n * \\param str A C String\n *\n * \\return The String object if no error occurs. Otherwise returns\n *   an error handle.\n */\nDART_EXPORT Dart_Handle Dart_NewStringFromCString(const char* str);\n/* TODO(turnidge): Document what happens when we run out of memory\n * during this call. */\n\n/**\n * Returns a String built from an array of UTF-8 encoded characters.\n *\n * \\param utf8_array An array of UTF-8 encoded characters.\n * \\param length The length of the codepoints array.\n *\n * \\return The String object if no error occurs. Otherwise returns\n *   an error handle.\n */\nDART_EXPORT Dart_Handle Dart_NewStringFromUTF8(const uint8_t* utf8_array,\n                                               intptr_t length);\n\n/**\n * Returns a String built from an array of UTF-16 encoded characters.\n *\n * \\param utf16_array An array of UTF-16 encoded characters.\n * \\param length The length of the codepoints array.\n *\n * \\return The String object if no error occurs. Otherwise returns\n *   an error handle.\n */\nDART_EXPORT Dart_Handle Dart_NewStringFromUTF16(const uint16_t* utf16_array,\n                                                intptr_t length);\n\n/**\n * Returns a String built from an array of UTF-32 encoded characters.\n *\n * \\param utf32_array An array of UTF-32 encoded characters.\n * \\param length The length of the codepoints array.\n *\n * \\return The String object if no error occurs. Otherwise returns\n *   an error handle.\n */\nDART_EXPORT Dart_Handle Dart_NewStringFromUTF32(const int32_t* utf32_array,\n                                                intptr_t length);\n\n/**\n * Returns a String which references an external array of\n * Latin-1 (ISO-8859-1) encoded characters.\n *\n * \\param latin1_array Array of Latin-1 encoded characters. This must not move.\n * \\param length The length of the characters array.\n * \\param peer An external pointer to associate with this string.\n * \\param external_allocation_size The number of externally allocated\n *   bytes for peer. Used to inform the garbage collector.\n * \\param callback A callback to be called when this string is finalized.\n *\n * \\return The String object if no error occurs. Otherwise returns\n *   an error handle.\n */\nDART_EXPORT Dart_Handle\nDart_NewExternalLatin1String(const uint8_t* latin1_array,\n                             intptr_t length,\n                             void* peer,\n                             intptr_t external_allocation_size,\n                             Dart_HandleFinalizer callback);\n\n/**\n * Returns a String which references an external array of UTF-16 encoded\n * characters.\n *\n * \\param utf16_array An array of UTF-16 encoded characters. This must not move.\n * \\param length The length of the characters array.\n * \\param peer An external pointer to associate with this string.\n * \\param external_allocation_size The number of externally allocated\n *   bytes for peer. Used to inform the garbage collector.\n * \\param callback A callback to be called when this string is finalized.\n *\n * \\return The String object if no error occurs. Otherwise returns\n *   an error handle.\n */\nDART_EXPORT Dart_Handle\nDart_NewExternalUTF16String(const uint16_t* utf16_array,\n                            intptr_t length,\n                            void* peer,\n                            intptr_t external_allocation_size,\n                            Dart_HandleFinalizer callback);\n\n/**\n * Gets the C string representation of a String.\n * (It is a sequence of UTF-8 encoded values with a '\\0' termination.)\n *\n * \\param str A string.\n * \\param cstr Returns the String represented as a C string.\n *   This C string is scope allocated and is only valid until\n *   the next call to Dart_ExitScope.\n *\n * \\return A valid handle if no error occurs during the operation.\n */\nDART_EXPORT Dart_Handle Dart_StringToCString(Dart_Handle str,\n                                             const char** cstr);\n\n/**\n * Gets a UTF-8 encoded representation of a String.\n *\n * Any unpaired surrogate code points in the string will be converted as\n * replacement characters (U+FFFD, 0xEF 0xBF 0xBD in UTF-8). If you need\n * to preserve unpaired surrogates, use the Dart_StringToUTF16 function.\n *\n * \\param str A string.\n * \\param utf8_array Returns the String represented as UTF-8 code\n *   units.  This UTF-8 array is scope allocated and is only valid\n *   until the next call to Dart_ExitScope.\n * \\param length Used to return the length of the array which was\n *   actually used.\n *\n * \\return A valid handle if no error occurs during the operation.\n */\nDART_EXPORT Dart_Handle Dart_StringToUTF8(Dart_Handle str,\n                                          uint8_t** utf8_array,\n                                          intptr_t* length);\n\n/**\n * Gets the data corresponding to the string object. This function returns\n * the data only for Latin-1 (ISO-8859-1) string objects. For all other\n * string objects it returns an error.\n *\n * \\param str A string.\n * \\param latin1_array An array allocated by the caller, used to return\n *   the string data.\n * \\param length Used to pass in the length of the provided array.\n *   Used to return the length of the array which was actually used.\n *\n * \\return A valid handle if no error occurs during the operation.\n */\nDART_EXPORT Dart_Handle Dart_StringToLatin1(Dart_Handle str,\n                                            uint8_t* latin1_array,\n                                            intptr_t* length);\n\n/**\n * Gets the UTF-16 encoded representation of a string.\n *\n * \\param str A string.\n * \\param utf16_array An array allocated by the caller, used to return\n *   the array of UTF-16 encoded characters.\n * \\param length Used to pass in the length of the provided array.\n *   Used to return the length of the array which was actually used.\n *\n * \\return A valid handle if no error occurs during the operation.\n */\nDART_EXPORT Dart_Handle Dart_StringToUTF16(Dart_Handle str,\n                                           uint16_t* utf16_array,\n                                           intptr_t* length);\n\n/**\n * Gets the storage size in bytes of a String.\n *\n * \\param str A String.\n * \\param size Returns the storage size in bytes of the String.\n *  This is the size in bytes needed to store the String.\n *\n * \\return A valid handle if no error occurs during the operation.\n */\nDART_EXPORT Dart_Handle Dart_StringStorageSize(Dart_Handle str, intptr_t* size);\n\n/**\n * Retrieves some properties associated with a String.\n * Properties retrieved are:\n * - character size of the string (one or two byte)\n * - length of the string\n * - peer pointer of string if it is an external string.\n * \\param str A String.\n * \\param char_size Returns the character size of the String.\n * \\param str_len Returns the length of the String.\n * \\param peer Returns the peer pointer associated with the String or 0 if\n *   there is no peer pointer for it.\n * \\return Success if no error occurs. Otherwise returns\n *   an error handle.\n */\nDART_EXPORT Dart_Handle Dart_StringGetProperties(Dart_Handle str,\n                                                 intptr_t* char_size,\n                                                 intptr_t* str_len,\n                                                 void** peer);\n\n/*\n * =====\n * Lists\n * =====\n */\n\n/**\n * Returns a List<dynamic> of the desired length.\n *\n * \\param length The length of the list.\n *\n * \\return The List object if no error occurs. Otherwise returns\n *   an error handle.\n */\nDART_EXPORT Dart_Handle Dart_NewList(intptr_t length);\n\ntypedef enum {\n  Dart_CoreType_Dynamic,\n  Dart_CoreType_Int,\n  Dart_CoreType_String,\n} Dart_CoreType_Id;\n\n// TODO(bkonyi): convert this to use nullable types once NNBD is enabled.\n/**\n * Returns a List of the desired length with the desired legacy element type.\n *\n * \\param element_type_id The type of elements of the list.\n * \\param length The length of the list.\n *\n * \\return The List object if no error occurs. Otherwise returns an error\n * handle.\n */\nDART_EXPORT Dart_Handle Dart_NewListOf(Dart_CoreType_Id element_type_id,\n                                       intptr_t length);\n\n/**\n * Returns a List of the desired length with the desired element type.\n *\n * \\param element_type Handle to a nullable type object. E.g., from\n * Dart_GetType or Dart_GetNullableType.\n *\n * \\param length The length of the list.\n *\n * \\return The List object if no error occurs. Otherwise returns\n *   an error handle.\n */\nDART_EXPORT Dart_Handle Dart_NewListOfType(Dart_Handle element_type,\n                                           intptr_t length);\n\n/**\n * Returns a List of the desired length with the desired element type, filled\n * with the provided object.\n *\n * \\param element_type Handle to a type object. E.g., from Dart_GetType.\n *\n * \\param fill_object Handle to an object of type 'element_type' that will be\n * used to populate the list. This parameter can only be Dart_Null() if the\n * length of the list is 0 or 'element_type' is a nullable type.\n *\n * \\param length The length of the list.\n *\n * \\return The List object if no error occurs. Otherwise returns\n *   an error handle.\n */\nDART_EXPORT Dart_Handle Dart_NewListOfTypeFilled(Dart_Handle element_type,\n                                                 Dart_Handle fill_object,\n                                                 intptr_t length);\n\n/**\n * Gets the length of a List.\n *\n * May generate an unhandled exception error.\n *\n * \\param list A List.\n * \\param length Returns the length of the List.\n *\n * \\return A valid handle if no error occurs during the operation.\n */\nDART_EXPORT Dart_Handle Dart_ListLength(Dart_Handle list, intptr_t* length);\n\n/**\n * Gets the Object at some index of a List.\n *\n * If the index is out of bounds, an error occurs.\n *\n * May generate an unhandled exception error.\n *\n * \\param list A List.\n * \\param index A valid index into the List.\n *\n * \\return The Object in the List at the specified index if no error\n *   occurs. Otherwise returns an error handle.\n */\nDART_EXPORT Dart_Handle Dart_ListGetAt(Dart_Handle list, intptr_t index);\n\n/**\n* Gets a range of Objects from a List.\n*\n* If any of the requested index values are out of bounds, an error occurs.\n*\n* May generate an unhandled exception error.\n*\n* \\param list A List.\n* \\param offset The offset of the first item to get.\n* \\param length The number of items to get.\n* \\param result A pointer to fill with the objects.\n*\n* \\return Success if no error occurs during the operation.\n*/\nDART_EXPORT Dart_Handle Dart_ListGetRange(Dart_Handle list,\n                                          intptr_t offset,\n                                          intptr_t length,\n                                          Dart_Handle* result);\n\n/**\n * Sets the Object at some index of a List.\n *\n * If the index is out of bounds, an error occurs.\n *\n * May generate an unhandled exception error.\n *\n * \\param list A List.\n * \\param index A valid index into the List.\n * \\param value The Object to put in the List.\n *\n * \\return A valid handle if no error occurs during the operation.\n */\nDART_EXPORT Dart_Handle Dart_ListSetAt(Dart_Handle list,\n                                       intptr_t index,\n                                       Dart_Handle value);\n\n/**\n * May generate an unhandled exception error.\n */\nDART_EXPORT Dart_Handle Dart_ListGetAsBytes(Dart_Handle list,\n                                            intptr_t offset,\n                                            uint8_t* native_array,\n                                            intptr_t length);\n\n/**\n * May generate an unhandled exception error.\n */\nDART_EXPORT Dart_Handle Dart_ListSetAsBytes(Dart_Handle list,\n                                            intptr_t offset,\n                                            const uint8_t* native_array,\n                                            intptr_t length);\n\n/*\n * ====\n * Maps\n * ====\n */\n\n/**\n * Gets the Object at some key of a Map.\n *\n * May generate an unhandled exception error.\n *\n * \\param map A Map.\n * \\param key An Object.\n *\n * \\return The value in the map at the specified key, null if the map does not\n *   contain the key, or an error handle.\n */\nDART_EXPORT Dart_Handle Dart_MapGetAt(Dart_Handle map, Dart_Handle key);\n\n/**\n * Returns whether the Map contains a given key.\n *\n * May generate an unhandled exception error.\n *\n * \\param map A Map.\n *\n * \\return A handle on a boolean indicating whether map contains the key.\n *   Otherwise returns an error handle.\n */\nDART_EXPORT Dart_Handle Dart_MapContainsKey(Dart_Handle map, Dart_Handle key);\n\n/**\n * Gets the list of keys of a Map.\n *\n * May generate an unhandled exception error.\n *\n * \\param map A Map.\n *\n * \\return The list of key Objects if no error occurs. Otherwise returns an\n *   error handle.\n */\nDART_EXPORT Dart_Handle Dart_MapKeys(Dart_Handle map);\n\n/*\n * ==========\n * Typed Data\n * ==========\n */\n\ntypedef enum {\n  Dart_TypedData_kByteData = 0,\n  Dart_TypedData_kInt8,\n  Dart_TypedData_kUint8,\n  Dart_TypedData_kUint8Clamped,\n  Dart_TypedData_kInt16,\n  Dart_TypedData_kUint16,\n  Dart_TypedData_kInt32,\n  Dart_TypedData_kUint32,\n  Dart_TypedData_kInt64,\n  Dart_TypedData_kUint64,\n  Dart_TypedData_kFloat32,\n  Dart_TypedData_kFloat64,\n  Dart_TypedData_kInt32x4,\n  Dart_TypedData_kFloat32x4,\n  Dart_TypedData_kFloat64x2,\n  Dart_TypedData_kInvalid\n} Dart_TypedData_Type;\n\n/**\n * Return type if this object is a TypedData object.\n *\n * \\return kInvalid if the object is not a TypedData object or the appropriate\n *   Dart_TypedData_Type.\n */\nDART_EXPORT Dart_TypedData_Type Dart_GetTypeOfTypedData(Dart_Handle object);\n\n/**\n * Return type if this object is an external TypedData object.\n *\n * \\return kInvalid if the object is not an external TypedData object or\n *   the appropriate Dart_TypedData_Type.\n */\nDART_EXPORT Dart_TypedData_Type\nDart_GetTypeOfExternalTypedData(Dart_Handle object);\n\n/**\n * Returns a TypedData object of the desired length and type.\n *\n * \\param type The type of the TypedData object.\n * \\param length The length of the TypedData object (length in type units).\n *\n * \\return The TypedData object if no error occurs. Otherwise returns\n *   an error handle.\n */\nDART_EXPORT Dart_Handle Dart_NewTypedData(Dart_TypedData_Type type,\n                                          intptr_t length);\n\n/**\n * Returns a TypedData object which references an external data array.\n *\n * \\param type The type of the data array.\n * \\param data A data array. This array must not move.\n * \\param length The length of the data array (length in type units).\n *\n * \\return The TypedData object if no error occurs. Otherwise returns\n *   an error handle.\n */\nDART_EXPORT Dart_Handle Dart_NewExternalTypedData(Dart_TypedData_Type type,\n                                                  void* data,\n                                                  intptr_t length);\n\n/**\n * Returns a TypedData object which references an external data array.\n *\n * \\param type The type of the data array.\n * \\param data A data array. This array must not move.\n * \\param length The length of the data array (length in type units).\n * \\param peer A pointer to a native object or NULL.  This value is\n *   provided to callback when it is invoked.\n * \\param external_allocation_size The number of externally allocated\n *   bytes for peer. Used to inform the garbage collector.\n * \\param callback A function pointer that will be invoked sometime\n *   after the object is garbage collected, unless the handle has been deleted.\n *   A valid callback needs to be specified it cannot be NULL.\n *\n * \\return The TypedData object if no error occurs. Otherwise returns\n *   an error handle.\n */\nDART_EXPORT Dart_Handle\nDart_NewExternalTypedDataWithFinalizer(Dart_TypedData_Type type,\n                                       void* data,\n                                       intptr_t length,\n                                       void* peer,\n                                       intptr_t external_allocation_size,\n                                       Dart_HandleFinalizer callback);\nDART_EXPORT Dart_Handle Dart_NewUnmodifiableExternalTypedDataWithFinalizer(\n    Dart_TypedData_Type type,\n    const void* data,\n    intptr_t length,\n    void* peer,\n    intptr_t external_allocation_size,\n    Dart_HandleFinalizer callback);\n\n/**\n * Returns a ByteBuffer object for the typed data.\n *\n * \\param typed_data The TypedData object.\n *\n * \\return The ByteBuffer object if no error occurs. Otherwise returns\n *   an error handle.\n */\nDART_EXPORT Dart_Handle Dart_NewByteBuffer(Dart_Handle typed_data);\n\n/**\n * Acquires access to the internal data address of a TypedData object.\n *\n * \\param object The typed data object whose internal data address is to\n *    be accessed.\n * \\param type The type of the object is returned here.\n * \\param data The internal data address is returned here.\n * \\param len Size of the typed array is returned here.\n *\n * Notes:\n *   When the internal address of the object is acquired any calls to a\n *   Dart API function that could potentially allocate an object or run\n *   any Dart code will return an error.\n *\n *   Any Dart API functions for accessing the data should not be called\n *   before the corresponding release. In particular, the object should\n *   not be acquired again before its release. This leads to undefined\n *   behavior.\n *\n * \\return Success if the internal data address is acquired successfully.\n *   Otherwise, returns an error handle.\n */\nDART_EXPORT Dart_Handle Dart_TypedDataAcquireData(Dart_Handle object,\n                                                  Dart_TypedData_Type* type,\n                                                  void** data,\n                                                  intptr_t* len);\n\n/**\n * Releases access to the internal data address that was acquired earlier using\n * Dart_TypedDataAcquireData.\n *\n * \\param object The typed data object whose internal data address is to be\n *   released.\n *\n * \\return Success if the internal data address is released successfully.\n *   Otherwise, returns an error handle.\n */\nDART_EXPORT Dart_Handle Dart_TypedDataReleaseData(Dart_Handle object);\n\n/**\n * Returns the TypedData object associated with the ByteBuffer object.\n *\n * \\param byte_buffer The ByteBuffer object.\n *\n * \\return The TypedData object if no error occurs. Otherwise returns\n *   an error handle.\n */\nDART_EXPORT Dart_Handle Dart_GetDataFromByteBuffer(Dart_Handle byte_buffer);\n\n/*\n * ============================================================\n * Invoking Constructors, Methods, Closures and Field accessors\n * ============================================================\n */\n\n/**\n * Invokes a constructor, creating a new object.\n *\n * This function allows hidden constructors (constructors with leading\n * underscores) to be called.\n *\n * \\param type Type of object to be constructed.\n * \\param constructor_name The name of the constructor to invoke.  Use\n *   Dart_Null() or Dart_EmptyString() to invoke the unnamed constructor.\n *   This name should not include the name of the class.\n * \\param number_of_arguments Size of the arguments array.\n * \\param arguments An array of arguments to the constructor.\n *\n * \\return If the constructor is called and completes successfully,\n *   then the new object. If an error occurs during execution, then an\n *   error handle is returned.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle\nDart_New(Dart_Handle type,\n         Dart_Handle constructor_name,\n         int number_of_arguments,\n         Dart_Handle* arguments);\n\n/**\n * Allocate a new object without invoking a constructor.\n *\n * \\param type The type of an object to be allocated.\n *\n * \\return The new object. If an error occurs during execution, then an\n *   error handle is returned.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_Allocate(Dart_Handle type);\n\n/**\n * Allocate a new object without invoking a constructor, and sets specified\n *  native fields.\n *\n * \\param type The type of an object to be allocated.\n * \\param num_native_fields The number of native fields to set.\n * \\param native_fields An array containing the value of native fields.\n *\n * \\return The new object. If an error occurs during execution, then an\n *   error handle is returned.\n */\nDART_EXPORT Dart_Handle\nDart_AllocateWithNativeFields(Dart_Handle type,\n                              intptr_t num_native_fields,\n                              const intptr_t* native_fields);\n\n/**\n * Invokes a method or function.\n *\n * The 'target' parameter may be an object, type, or library.  If\n * 'target' is an object, then this function will invoke an instance\n * method.  If 'target' is a type, then this function will invoke a\n * static method.  If 'target' is a library, then this function will\n * invoke a top-level function from that library.\n * NOTE: This API call cannot be used to invoke methods of a type object.\n *\n * This function ignores visibility (leading underscores in names).\n *\n * May generate an unhandled exception error.\n *\n * \\param target An object, type, or library.\n * \\param name The name of the function or method to invoke.\n * \\param number_of_arguments Size of the arguments array.\n * \\param arguments An array of arguments to the function.\n *\n * \\return If the function or method is called and completes\n *   successfully, then the return value is returned. If an error\n *   occurs during execution, then an error handle is returned.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle\nDart_Invoke(Dart_Handle target,\n            Dart_Handle name,\n            int number_of_arguments,\n            Dart_Handle* arguments);\n/* TODO(turnidge): Document how to invoke operators. */\n\n/**\n * Invokes a Closure with the given arguments.\n *\n * May generate an unhandled exception error.\n *\n * \\return If no error occurs during execution, then the result of\n *   invoking the closure is returned. If an error occurs during\n *   execution, then an error handle is returned.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle\nDart_InvokeClosure(Dart_Handle closure,\n                   int number_of_arguments,\n                   Dart_Handle* arguments);\n\n/**\n * Invokes a Generative Constructor on an object that was previously\n * allocated using Dart_Allocate/Dart_AllocateWithNativeFields.\n *\n * The 'object' parameter must be an object.\n *\n * This function ignores visibility (leading underscores in names).\n *\n * May generate an unhandled exception error.\n *\n * \\param object An object.\n * \\param name The name of the constructor to invoke.\n *   Use Dart_Null() or Dart_EmptyString() to invoke the unnamed constructor.\n * \\param number_of_arguments Size of the arguments array.\n * \\param arguments An array of arguments to the function.\n *\n * \\return If the constructor is called and completes\n *   successfully, then the object is returned. If an error\n *   occurs during execution, then an error handle is returned.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle\nDart_InvokeConstructor(Dart_Handle object,\n                       Dart_Handle name,\n                       int number_of_arguments,\n                       Dart_Handle* arguments);\n\n/**\n * Gets the value of a field.\n *\n * The 'container' parameter may be an object, type, or library.  If\n * 'container' is an object, then this function will access an\n * instance field.  If 'container' is a type, then this function will\n * access a static field.  If 'container' is a library, then this\n * function will access a top-level variable.\n * NOTE: This API call cannot be used to access fields of a type object.\n *\n * This function ignores field visibility (leading underscores in names).\n *\n * May generate an unhandled exception error.\n *\n * \\param container An object, type, or library.\n * \\param name A field name.\n *\n * \\return If no error occurs, then the value of the field is\n *   returned. Otherwise an error handle is returned.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle\nDart_GetField(Dart_Handle container, Dart_Handle name);\n\n/**\n * Sets the value of a field.\n *\n * The 'container' parameter may actually be an object, type, or\n * library.  If 'container' is an object, then this function will\n * access an instance field.  If 'container' is a type, then this\n * function will access a static field.  If 'container' is a library,\n * then this function will access a top-level variable.\n * NOTE: This API call cannot be used to access fields of a type object.\n *\n * This function ignores field visibility (leading underscores in names).\n *\n * May generate an unhandled exception error.\n *\n * \\param container An object, type, or library.\n * \\param name A field name.\n * \\param value The new field value.\n *\n * \\return A valid handle if no error occurs.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle\nDart_SetField(Dart_Handle container, Dart_Handle name, Dart_Handle value);\n\n/*\n * ==========\n * Exceptions\n * ==========\n */\n\n/*\n * TODO(turnidge): Remove these functions from the api and replace all\n * uses with Dart_NewUnhandledExceptionError. */\n\n/**\n * Throws an exception.\n *\n * This function causes a Dart language exception to be thrown. This\n * will proceed in the standard way, walking up Dart frames until an\n * appropriate 'catch' block is found, executing 'finally' blocks,\n * etc.\n *\n * If an error handle is passed into this function, the error is\n * propagated immediately.  See Dart_PropagateError for a discussion\n * of error propagation.\n *\n * If successful, this function does not return. Note that this means\n * that the destructors of any stack-allocated C++ objects will not be\n * called. If there are no Dart frames on the stack, an error occurs.\n *\n * \\return An error handle if the exception was not thrown.\n *   Otherwise the function does not return.\n */\nDART_EXPORT Dart_Handle Dart_ThrowException(Dart_Handle exception);\n\n/**\n * Rethrows an exception.\n *\n * Rethrows an exception, unwinding all dart frames on the stack. If\n * successful, this function does not return. Note that this means\n * that the destructors of any stack-allocated C++ objects will not be\n * called. If there are no Dart frames on the stack, an error occurs.\n *\n * \\return An error handle if the exception was not thrown.\n *   Otherwise the function does not return.\n */\nDART_EXPORT Dart_Handle Dart_ReThrowException(Dart_Handle exception,\n                                              Dart_Handle stacktrace);\n\n/*\n * ===========================\n * Native fields and functions\n * ===========================\n */\n\n/**\n * Gets the number of native instance fields in an object.\n */\nDART_EXPORT Dart_Handle Dart_GetNativeInstanceFieldCount(Dart_Handle obj,\n                                                         int* count);\n\n/**\n * Gets the value of a native field.\n *\n * TODO(turnidge): Document.\n */\nDART_EXPORT Dart_Handle Dart_GetNativeInstanceField(Dart_Handle obj,\n                                                    int index,\n                                                    intptr_t* value);\n\n/**\n * Sets the value of a native field.\n *\n * TODO(turnidge): Document.\n */\nDART_EXPORT Dart_Handle Dart_SetNativeInstanceField(Dart_Handle obj,\n                                                    int index,\n                                                    intptr_t value);\n\n/**\n * The arguments to a native function.\n *\n * This object is passed to a native function to represent its\n * arguments and return value. It allows access to the arguments to a\n * native function by index. It also allows the return value of a\n * native function to be set.\n */\ntypedef struct _Dart_NativeArguments* Dart_NativeArguments;\n\n/**\n * Extracts current isolate group data from the native arguments structure.\n */\nDART_EXPORT void* Dart_GetNativeIsolateGroupData(Dart_NativeArguments args);\n\ntypedef enum {\n  Dart_NativeArgument_kBool = 0,\n  Dart_NativeArgument_kInt32,\n  Dart_NativeArgument_kUint32,\n  Dart_NativeArgument_kInt64,\n  Dart_NativeArgument_kUint64,\n  Dart_NativeArgument_kDouble,\n  Dart_NativeArgument_kString,\n  Dart_NativeArgument_kInstance,\n  Dart_NativeArgument_kNativeFields,\n} Dart_NativeArgument_Type;\n\ntypedef struct _Dart_NativeArgument_Descriptor {\n  uint8_t type;\n  uint8_t index;\n} Dart_NativeArgument_Descriptor;\n\ntypedef union _Dart_NativeArgument_Value {\n  bool as_bool;\n  int32_t as_int32;\n  uint32_t as_uint32;\n  int64_t as_int64;\n  uint64_t as_uint64;\n  double as_double;\n  struct {\n    Dart_Handle dart_str;\n    void* peer;\n  } as_string;\n  struct {\n    intptr_t num_fields;\n    intptr_t* values;\n  } as_native_fields;\n  Dart_Handle as_instance;\n} Dart_NativeArgument_Value;\n\nenum {\n  kNativeArgNumberPos = 0,\n  kNativeArgNumberSize = 8,\n  kNativeArgTypePos = kNativeArgNumberPos + kNativeArgNumberSize,\n  kNativeArgTypeSize = 8,\n};\n\n#define BITMASK(size) ((1 << size) - 1)\n#define DART_NATIVE_ARG_DESCRIPTOR(type, position)                             \\\n  (((type & BITMASK(kNativeArgTypeSize)) << kNativeArgTypePos) |               \\\n   (position & BITMASK(kNativeArgNumberSize)))\n\n/**\n * Gets the native arguments based on the types passed in and populates\n * the passed arguments buffer with appropriate native values.\n *\n * \\param args the Native arguments block passed into the native call.\n * \\param num_arguments length of argument descriptor array and argument\n *   values array passed in.\n * \\param arg_descriptors an array that describes the arguments that\n *   need to be retrieved. For each argument to be retrieved the descriptor\n *   contains the argument number (0, 1 etc.) and the argument type\n *   described using Dart_NativeArgument_Type, e.g:\n *   DART_NATIVE_ARG_DESCRIPTOR(Dart_NativeArgument_kBool, 1) indicates\n *   that the first argument is to be retrieved and it should be a boolean.\n * \\param arg_values array into which the native arguments need to be\n *   extracted into, the array is allocated by the caller (it could be\n *   stack allocated to avoid the malloc/free performance overhead).\n *\n * \\return Success if all the arguments could be extracted correctly,\n *   returns an error handle if there were any errors while extracting the\n *   arguments (mismatched number of arguments, incorrect types, etc.).\n */\nDART_EXPORT Dart_Handle\nDart_GetNativeArguments(Dart_NativeArguments args,\n                        int num_arguments,\n                        const Dart_NativeArgument_Descriptor* arg_descriptors,\n                        Dart_NativeArgument_Value* arg_values);\n\n/**\n * Gets the native argument at some index.\n */\nDART_EXPORT Dart_Handle Dart_GetNativeArgument(Dart_NativeArguments args,\n                                               int index);\n/* TODO(turnidge): Specify the behavior of an out-of-bounds access. */\n\n/**\n * Gets the number of native arguments.\n */\nDART_EXPORT int Dart_GetNativeArgumentCount(Dart_NativeArguments args);\n\n/**\n * Gets all the native fields of the native argument at some index.\n * \\param args Native arguments structure.\n * \\param arg_index Index of the desired argument in the structure above.\n * \\param num_fields size of the intptr_t array 'field_values' passed in.\n * \\param field_values intptr_t array in which native field values are returned.\n * \\return Success if the native fields where copied in successfully. Otherwise\n *   returns an error handle. On success the native field values are copied\n *   into the 'field_values' array, if the argument at 'arg_index' is a\n *   null object then 0 is copied as the native field values into the\n *   'field_values' array.\n */\nDART_EXPORT Dart_Handle\nDart_GetNativeFieldsOfArgument(Dart_NativeArguments args,\n                               int arg_index,\n                               int num_fields,\n                               intptr_t* field_values);\n\n/**\n * Gets the native field of the receiver.\n */\nDART_EXPORT Dart_Handle Dart_GetNativeReceiver(Dart_NativeArguments args,\n                                               intptr_t* value);\n\n/**\n * Gets a string native argument at some index.\n * \\param args Native arguments structure.\n * \\param arg_index Index of the desired argument in the structure above.\n * \\param peer Returns the peer pointer if the string argument has one.\n * \\return Success if the string argument has a peer, if it does not\n *   have a peer then the String object is returned. Otherwise returns\n *   an error handle (argument is not a String object).\n */\nDART_EXPORT Dart_Handle Dart_GetNativeStringArgument(Dart_NativeArguments args,\n                                                     int arg_index,\n                                                     void** peer);\n\n/**\n * Gets an integer native argument at some index.\n * \\param args Native arguments structure.\n * \\param index Index of the desired argument in the structure above.\n * \\param value Returns the integer value if the argument is an Integer.\n * \\return Success if no error occurs. Otherwise returns an error handle.\n */\nDART_EXPORT Dart_Handle Dart_GetNativeIntegerArgument(Dart_NativeArguments args,\n                                                      int index,\n                                                      int64_t* value);\n\n/**\n * Gets a boolean native argument at some index.\n * \\param args Native arguments structure.\n * \\param index Index of the desired argument in the structure above.\n * \\param value Returns the boolean value if the argument is a Boolean.\n * \\return Success if no error occurs. Otherwise returns an error handle.\n */\nDART_EXPORT Dart_Handle Dart_GetNativeBooleanArgument(Dart_NativeArguments args,\n                                                      int index,\n                                                      bool* value);\n\n/**\n * Gets a double native argument at some index.\n * \\param args Native arguments structure.\n * \\param index Index of the desired argument in the structure above.\n * \\param value Returns the double value if the argument is a double.\n * \\return Success if no error occurs. Otherwise returns an error handle.\n */\nDART_EXPORT Dart_Handle Dart_GetNativeDoubleArgument(Dart_NativeArguments args,\n                                                     int index,\n                                                     double* value);\n\n/**\n * Sets the return value for a native function.\n *\n * If retval is an Error handle, then error will be propagated once\n * the native functions exits. See Dart_PropagateError for a\n * discussion of how different types of errors are propagated.\n */\nDART_EXPORT void Dart_SetReturnValue(Dart_NativeArguments args,\n                                     Dart_Handle retval);\n\nDART_EXPORT void Dart_SetWeakHandleReturnValue(Dart_NativeArguments args,\n                                               Dart_WeakPersistentHandle rval);\n\nDART_EXPORT void Dart_SetBooleanReturnValue(Dart_NativeArguments args,\n                                            bool retval);\n\nDART_EXPORT void Dart_SetIntegerReturnValue(Dart_NativeArguments args,\n                                            int64_t retval);\n\nDART_EXPORT void Dart_SetDoubleReturnValue(Dart_NativeArguments args,\n                                           double retval);\n\n/**\n * A native function.\n */\ntypedef void (*Dart_NativeFunction)(Dart_NativeArguments arguments);\n\n/**\n * Native entry resolution callback.\n *\n * For libraries and scripts which have native functions, the embedder\n * can provide a native entry resolver. This callback is used to map a\n * name/arity to a Dart_NativeFunction. If no function is found, the\n * callback should return NULL.\n *\n * The parameters to the native resolver function are:\n * \\param name a Dart string which is the name of the native function.\n * \\param num_of_arguments is the number of arguments expected by the\n *   native function.\n * \\param auto_setup_scope is a boolean flag that can be set by the resolver\n *   to indicate if this function needs a Dart API scope (see Dart_EnterScope/\n *   Dart_ExitScope) to be setup automatically by the VM before calling into\n *   the native function. By default most native functions would require this\n *   to be true but some light weight native functions which do not call back\n *   into the VM through the Dart API may not require a Dart scope to be\n *   setup automatically.\n *\n * \\return A valid Dart_NativeFunction which resolves to a native entry point\n *   for the native function.\n *\n * See Dart_SetNativeResolver.\n */\ntypedef Dart_NativeFunction (*Dart_NativeEntryResolver)(Dart_Handle name,\n                                                        int num_of_arguments,\n                                                        bool* auto_setup_scope);\n/* TODO(turnidge): Consider renaming to NativeFunctionResolver or\n * NativeResolver. */\n\n/**\n * Native entry symbol lookup callback.\n *\n * For libraries and scripts which have native functions, the embedder\n * can provide a callback for mapping a native entry to a symbol. This callback\n * maps a native function entry PC to the native function name. If no native\n * entry symbol can be found, the callback should return NULL.\n *\n * The parameters to the native reverse resolver function are:\n * \\param nf A Dart_NativeFunction.\n *\n * \\return A const UTF-8 string containing the symbol name or NULL.\n *\n * See Dart_SetNativeResolver.\n */\ntypedef const uint8_t* (*Dart_NativeEntrySymbol)(Dart_NativeFunction nf);\n\n/**\n * FFI Native C function pointer resolver callback.\n *\n * See Dart_SetFfiNativeResolver.\n */\ntypedef void* (*Dart_FfiNativeResolver)(const char* name, uintptr_t args_n);\n\n/*\n * ===========\n * Environment\n * ===========\n */\n\n/**\n * An environment lookup callback function.\n *\n * \\param name The name of the value to lookup in the environment.\n *\n * \\return A valid handle to a string if the name exists in the\n * current environment or Dart_Null() if not.\n */\ntypedef Dart_Handle (*Dart_EnvironmentCallback)(Dart_Handle name);\n\n/**\n * Sets the environment callback for the current isolate. This\n * callback is used to lookup environment values by name in the\n * current environment. This enables the embedder to supply values for\n * the const constructors bool.fromEnvironment, int.fromEnvironment\n * and String.fromEnvironment.\n */\nDART_EXPORT Dart_Handle\nDart_SetEnvironmentCallback(Dart_EnvironmentCallback callback);\n\n/**\n * Sets the callback used to resolve native functions for a library.\n *\n * \\param library A library.\n * \\param resolver A native entry resolver.\n *\n * \\return A valid handle if the native resolver was set successfully.\n */\nDART_EXPORT Dart_Handle\nDart_SetNativeResolver(Dart_Handle library,\n                       Dart_NativeEntryResolver resolver,\n                       Dart_NativeEntrySymbol symbol);\n/* TODO(turnidge): Rename to Dart_LibrarySetNativeResolver? */\n\n/**\n * Returns the callback used to resolve native functions for a library.\n *\n * \\param library A library.\n * \\param resolver a pointer to a Dart_NativeEntryResolver\n *\n * \\return A valid handle if the library was found.\n */\nDART_EXPORT Dart_Handle\nDart_GetNativeResolver(Dart_Handle library, Dart_NativeEntryResolver* resolver);\n\n/**\n * Returns the callback used to resolve native function symbols for a library.\n *\n * \\param library A library.\n * \\param resolver a pointer to a Dart_NativeEntrySymbol.\n *\n * \\return A valid handle if the library was found.\n */\nDART_EXPORT Dart_Handle Dart_GetNativeSymbol(Dart_Handle library,\n                                             Dart_NativeEntrySymbol* resolver);\n\n/**\n * Sets the callback used to resolve FFI native functions for a library.\n * The resolved functions are expected to be a C function pointer of the\n * correct signature (as specified in the `@FfiNative<NFT>()` function\n * annotation in Dart code).\n *\n * NOTE: This is an experimental feature and might change in the future.\n *\n * \\param library A library.\n * \\param resolver A native function resolver.\n *\n * \\return A valid handle if the native resolver was set successfully.\n */\nDART_EXPORT Dart_Handle\nDart_SetFfiNativeResolver(Dart_Handle library, Dart_FfiNativeResolver resolver);\n\n/*\n * =====================\n * Scripts and Libraries\n * =====================\n */\n\ntypedef enum {\n  Dart_kCanonicalizeUrl = 0,\n  Dart_kImportTag,\n  Dart_kKernelTag,\n} Dart_LibraryTag;\n\n/**\n * The library tag handler is a multi-purpose callback provided by the\n * embedder to the Dart VM. The embedder implements the tag handler to\n * provide the ability to load Dart scripts and imports.\n *\n * -- TAGS --\n *\n * Dart_kCanonicalizeUrl\n *\n * This tag indicates that the embedder should canonicalize 'url' with\n * respect to 'library'.  For most embedders, the\n * Dart_DefaultCanonicalizeUrl function is a sufficient implementation\n * of this tag.  The return value should be a string holding the\n * canonicalized url.\n *\n * Dart_kImportTag\n *\n * This tag is used to load a library from IsolateMirror.loadUri. The embedder\n * should call Dart_LoadLibraryFromKernel to provide the library to the VM. The\n * return value should be an error or library (the result from\n * Dart_LoadLibraryFromKernel).\n *\n * Dart_kKernelTag\n *\n * This tag is used to load the intermediate file (kernel) generated by\n * the Dart front end. This tag is typically used when a 'hot-reload'\n * of an application is needed and the VM is 'use dart front end' mode.\n * The dart front end typically compiles all the scripts, imports and part\n * files into one intermediate file hence we don't use the source/import or\n * script tags. The return value should be an error or a TypedData containing\n * the kernel bytes.\n *\n */\ntypedef Dart_Handle (*Dart_LibraryTagHandler)(\n    Dart_LibraryTag tag,\n    Dart_Handle library_or_package_map_url,\n    Dart_Handle url);\n\n/**\n * Sets library tag handler for the current isolate. This handler is\n * used to handle the various tags encountered while loading libraries\n * or scripts in the isolate.\n *\n * \\param handler Handler code to be used for handling the various tags\n *   encountered while loading libraries or scripts in the isolate.\n *\n * \\return If no error occurs, the handler is set for the isolate.\n *   Otherwise an error handle is returned.\n *\n * TODO(turnidge): Document.\n */\nDART_EXPORT Dart_Handle\nDart_SetLibraryTagHandler(Dart_LibraryTagHandler handler);\n\n/**\n * Handles deferred loading requests. When this handler is invoked, it should\n * eventually load the deferred loading unit with the given id and call\n * Dart_DeferredLoadComplete or Dart_DeferredLoadCompleteError. It is\n * recommended that the loading occur asynchronously, but it is permitted to\n * call Dart_DeferredLoadComplete or Dart_DeferredLoadCompleteError before the\n * handler returns.\n *\n * If an error is returned, it will be propagated through\n * `prefix.loadLibrary()`. This is useful for synchronous\n * implementations, which must propagate any unwind errors from\n * Dart_DeferredLoadComplete or Dart_DeferredLoadComplete. Otherwise the handler\n * should return a non-error such as `Dart_Null()`.\n */\ntypedef Dart_Handle (*Dart_DeferredLoadHandler)(intptr_t loading_unit_id);\n\n/**\n * Sets the deferred load handler for the current isolate. This handler is\n * used to handle loading deferred imports in an AppJIT or AppAOT program.\n */\nDART_EXPORT Dart_Handle\nDart_SetDeferredLoadHandler(Dart_DeferredLoadHandler handler);\n\n/**\n * Notifies the VM that a deferred load completed successfully. This function\n * will eventually cause the corresponding `prefix.loadLibrary()` futures to\n * complete.\n *\n * Requires the current isolate to be the same current isolate during the\n * invocation of the Dart_DeferredLoadHandler.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle\nDart_DeferredLoadComplete(intptr_t loading_unit_id,\n                          const uint8_t* snapshot_data,\n                          const uint8_t* snapshot_instructions);\n\n/**\n * Notifies the VM that a deferred load failed. This function\n * will eventually cause the corresponding `prefix.loadLibrary()` futures to\n * complete with an error.\n *\n * If `transient` is true, future invocations of `prefix.loadLibrary()` will\n * trigger new load requests. If false, futures invocation will complete with\n * the same error.\n *\n * Requires the current isolate to be the same current isolate during the\n * invocation of the Dart_DeferredLoadHandler.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle\nDart_DeferredLoadCompleteError(intptr_t loading_unit_id,\n                               const char* error_message,\n                               bool transient);\n\n/**\n * Canonicalizes a url with respect to some library.\n *\n * The url is resolved with respect to the library's url and some url\n * normalizations are performed.\n *\n * This canonicalization function should be sufficient for most\n * embedders to implement the Dart_kCanonicalizeUrl tag.\n *\n * \\param base_url The base url relative to which the url is\n *                being resolved.\n * \\param url The url being resolved and canonicalized.  This\n *            parameter is a string handle.\n *\n * \\return If no error occurs, a String object is returned.  Otherwise\n *   an error handle is returned.\n */\nDART_EXPORT Dart_Handle Dart_DefaultCanonicalizeUrl(Dart_Handle base_url,\n                                                    Dart_Handle url);\n\n/**\n * Loads the root library for the current isolate.\n *\n * Requires there to be no current root library.\n *\n * \\param kernel_buffer A buffer which contains a kernel binary (see\n *     pkg/kernel/binary.md). Must remain valid until isolate group shutdown.\n * \\param kernel_size Length of the passed in buffer.\n *\n * \\return A handle to the root library, or an error.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle\nDart_LoadScriptFromKernel(const uint8_t* kernel_buffer, intptr_t kernel_size);\n\n/**\n * Gets the library for the root script for the current isolate.\n *\n * If the root script has not yet been set for the current isolate,\n * this function returns Dart_Null().  This function never returns an\n * error handle.\n *\n * \\return Returns the root Library for the current isolate or Dart_Null().\n */\nDART_EXPORT Dart_Handle Dart_RootLibrary(void);\n\n/**\n * Sets the root library for the current isolate.\n *\n * \\return Returns an error handle if `library` is not a library handle.\n */\nDART_EXPORT Dart_Handle Dart_SetRootLibrary(Dart_Handle library);\n\n/**\n * Lookup or instantiate a legacy type by name and type arguments from a\n * Library.\n *\n * \\param library The library containing the class or interface.\n * \\param class_name The class name for the type.\n * \\param number_of_type_arguments Number of type arguments.\n *   For non parametric types the number of type arguments would be 0.\n * \\param type_arguments Pointer to an array of type arguments.\n *   For non parametric types a NULL would be passed in for this argument.\n *\n * \\return If no error occurs, the type is returned.\n *   Otherwise an error handle is returned.\n */\nDART_EXPORT Dart_Handle Dart_GetType(Dart_Handle library,\n                                     Dart_Handle class_name,\n                                     intptr_t number_of_type_arguments,\n                                     Dart_Handle* type_arguments);\n\n/**\n * Lookup or instantiate a nullable type by name and type arguments from\n * Library.\n *\n * \\param library The library containing the class or interface.\n * \\param class_name The class name for the type.\n * \\param number_of_type_arguments Number of type arguments.\n *   For non parametric types the number of type arguments would be 0.\n * \\param type_arguments Pointer to an array of type arguments.\n *   For non parametric types a NULL would be passed in for this argument.\n *\n * \\return If no error occurs, the type is returned.\n *   Otherwise an error handle is returned.\n */\nDART_EXPORT Dart_Handle Dart_GetNullableType(Dart_Handle library,\n                                             Dart_Handle class_name,\n                                             intptr_t number_of_type_arguments,\n                                             Dart_Handle* type_arguments);\n\n/**\n * Lookup or instantiate a non-nullable type by name and type arguments from\n * Library.\n *\n * \\param library The library containing the class or interface.\n * \\param class_name The class name for the type.\n * \\param number_of_type_arguments Number of type arguments.\n *   For non parametric types the number of type arguments would be 0.\n * \\param type_arguments Pointer to an array of type arguments.\n *   For non parametric types a NULL would be passed in for this argument.\n *\n * \\return If no error occurs, the type is returned.\n *   Otherwise an error handle is returned.\n */\nDART_EXPORT Dart_Handle\nDart_GetNonNullableType(Dart_Handle library,\n                        Dart_Handle class_name,\n                        intptr_t number_of_type_arguments,\n                        Dart_Handle* type_arguments);\n\n/**\n * Creates a nullable version of the provided type.\n *\n * \\param type The type to be converted to a nullable type.\n *\n * \\return If no error occurs, a nullable type is returned.\n *   Otherwise an error handle is returned.\n */\nDART_EXPORT Dart_Handle Dart_TypeToNullableType(Dart_Handle type);\n\n/**\n * Creates a non-nullable version of the provided type.\n *\n * \\param type The type to be converted to a non-nullable type.\n *\n * \\return If no error occurs, a non-nullable type is returned.\n *   Otherwise an error handle is returned.\n */\nDART_EXPORT Dart_Handle Dart_TypeToNonNullableType(Dart_Handle type);\n\n/**\n * A type's nullability.\n *\n * \\param type A Dart type.\n * \\param result An out parameter containing the result of the check. True if\n * the type is of the specified nullability, false otherwise.\n *\n * \\return Returns an error handle if type is not of type Type.\n */\nDART_EXPORT Dart_Handle Dart_IsNullableType(Dart_Handle type, bool* result);\nDART_EXPORT Dart_Handle Dart_IsNonNullableType(Dart_Handle type, bool* result);\nDART_EXPORT Dart_Handle Dart_IsLegacyType(Dart_Handle type, bool* result);\n\n/**\n * Lookup a class or interface by name from a Library.\n *\n * \\param library The library containing the class or interface.\n * \\param class_name The name of the class or interface.\n *\n * \\return If no error occurs, the class or interface is\n *   returned. Otherwise an error handle is returned.\n */\nDART_EXPORT Dart_Handle Dart_GetClass(Dart_Handle library,\n                                      Dart_Handle class_name);\n/* TODO(asiva): The above method needs to be removed once all uses\n * of it are removed from the embedder code. */\n\n/**\n * Returns an import path to a Library, such as \"file:///test.dart\" or\n * \"dart:core\".\n */\nDART_EXPORT Dart_Handle Dart_LibraryUrl(Dart_Handle library);\n\n/**\n * Returns a URL from which a Library was loaded.\n */\nDART_EXPORT Dart_Handle Dart_LibraryResolvedUrl(Dart_Handle library);\n\n/**\n * \\return An array of libraries.\n */\nDART_EXPORT Dart_Handle Dart_GetLoadedLibraries(void);\n\nDART_EXPORT Dart_Handle Dart_LookupLibrary(Dart_Handle url);\n/* TODO(turnidge): Consider returning Dart_Null() when the library is\n * not found to distinguish that from a true error case. */\n\n/**\n * Report an loading error for the library.\n *\n * \\param library The library that failed to load.\n * \\param error The Dart error instance containing the load error.\n *\n * \\return If the VM handles the error, the return value is\n * a null handle. If it doesn't handle the error, the error\n * object is returned.\n */\nDART_EXPORT Dart_Handle Dart_LibraryHandleError(Dart_Handle library,\n                                                Dart_Handle error);\n\n/**\n * Called by the embedder to load a partial program. Does not set the root\n * library.\n *\n * \\param kernel_buffer A buffer which contains a kernel binary (see\n *     pkg/kernel/binary.md). Must remain valid until isolate shutdown.\n * \\param kernel_buffer_size Length of the passed in buffer.\n *\n * \\return A handle to the main library of the compilation unit, or an error.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle\nDart_LoadLibraryFromKernel(const uint8_t* kernel_buffer,\n                           intptr_t kernel_buffer_size);\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle\nDart_LoadLibrary(Dart_Handle kernel_buffer);\n\n/**\n * Indicates that all outstanding load requests have been satisfied.\n * This finalizes all the new classes loaded and optionally completes\n * deferred library futures.\n *\n * Requires there to be a current isolate.\n *\n * \\param complete_futures Specify true if all deferred library\n *  futures should be completed, false otherwise.\n *\n * \\return Success if all classes have been finalized and deferred library\n *   futures are completed. Otherwise, returns an error.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle\nDart_FinalizeLoading(bool complete_futures);\n\n/*\n * =====\n * Peers\n * =====\n */\n\n/**\n * The peer field is a lazily allocated field intended for storage of\n * an uncommonly used values.  Most instances types can have a peer\n * field allocated.  The exceptions are subtypes of Null, num, and\n * bool.\n */\n\n/**\n * Returns the value of peer field of 'object' in 'peer'.\n *\n * \\param object An object.\n * \\param peer An out parameter that returns the value of the peer\n *   field.\n *\n * \\return Returns an error if 'object' is a subtype of Null, num, or\n *   bool.\n */\nDART_EXPORT Dart_Handle Dart_GetPeer(Dart_Handle object, void** peer);\n\n/**\n * Sets the value of the peer field of 'object' to the value of\n * 'peer'.\n *\n * \\param object An object.\n * \\param peer A value to store in the peer field.\n *\n * \\return Returns an error if 'object' is a subtype of Null, num, or\n *   bool.\n */\nDART_EXPORT Dart_Handle Dart_SetPeer(Dart_Handle object, void* peer);\n\n/*\n * ======\n * Kernel\n * ======\n */\n\n/**\n * Experimental support for Dart to Kernel parser isolate.\n *\n * TODO(hausner): Document finalized interface.\n *\n */\n\n// TODO(33433): Remove kernel service from the embedding API.\n\ntypedef enum {\n  Dart_KernelCompilationStatus_Unknown = -1,\n  Dart_KernelCompilationStatus_Ok = 0,\n  Dart_KernelCompilationStatus_Error = 1,\n  Dart_KernelCompilationStatus_Crash = 2,\n  Dart_KernelCompilationStatus_MsgFailed = 3,\n} Dart_KernelCompilationStatus;\n\ntypedef struct {\n  Dart_KernelCompilationStatus status;\n  bool null_safety;\n  char* error;\n  uint8_t* kernel;\n  intptr_t kernel_size;\n} Dart_KernelCompilationResult;\n\ntypedef enum {\n  Dart_KernelCompilationVerbosityLevel_Error = 0,\n  Dart_KernelCompilationVerbosityLevel_Warning,\n  Dart_KernelCompilationVerbosityLevel_Info,\n  Dart_KernelCompilationVerbosityLevel_All,\n} Dart_KernelCompilationVerbosityLevel;\n\nDART_EXPORT bool Dart_IsKernelIsolate(Dart_Isolate isolate);\nDART_EXPORT bool Dart_KernelIsolateIsRunning(void);\nDART_EXPORT Dart_Port Dart_KernelPort(void);\n\n/**\n * Compiles the given `script_uri` to a kernel file.\n *\n * \\param platform_kernel A buffer containing the kernel of the platform (e.g.\n * `vm_platform_strong.dill`). The VM does not take ownership of this memory.\n *\n * \\param platform_kernel_size The length of the platform_kernel buffer.\n *\n * \\param snapshot_compile Set to `true` when the compilation is for a snapshot.\n * This is used by the frontend to determine if compilation related information\n * should be printed to console (e.g., null safety mode).\n *\n * \\param verbosity Specifies the logging behavior of the kernel compilation\n * service.\n *\n * \\return Returns the result of the compilation.\n *\n * On a successful compilation the returned [Dart_KernelCompilationResult] has\n * a status of [Dart_KernelCompilationStatus_Ok] and the `kernel`/`kernel_size`\n * fields are set. The caller takes ownership of the malloc()ed buffer.\n *\n * On a failed compilation the `error` might be set describing the reason for\n * the failed compilation. The caller takes ownership of the malloc()ed\n * error.\n *\n * Requires there to be a current isolate.\n */\nDART_EXPORT Dart_KernelCompilationResult\nDart_CompileToKernel(const char* script_uri,\n                     const uint8_t* platform_kernel,\n                     const intptr_t platform_kernel_size,\n                     bool incremental_compile,\n                     bool snapshot_compile,\n                     const char* package_config,\n                     Dart_KernelCompilationVerbosityLevel verbosity);\n\ntypedef struct {\n  const char* uri;\n  const char* source;\n} Dart_SourceFile;\n\nDART_EXPORT Dart_KernelCompilationResult Dart_KernelListDependencies(void);\n\n/**\n * Sets the kernel buffer which will be used to load Dart SDK sources\n * dynamically at runtime.\n *\n * \\param platform_kernel A buffer containing kernel which has sources for the\n * Dart SDK populated. Note: The VM does not take ownership of this memory.\n *\n * \\param platform_kernel_size The length of the platform_kernel buffer.\n */\nDART_EXPORT void Dart_SetDartLibrarySourcesKernel(\n    const uint8_t* platform_kernel,\n    const intptr_t platform_kernel_size);\n\n/**\n * Detect the null safety opt-in status.\n *\n * When running from source, it is based on the opt-in status of `script_uri`.\n * When running from a kernel buffer, it is based on the mode used when\n *   generating `kernel_buffer`.\n * When running from an appJIT or AOT snapshot, it is based on the mode used\n *   when generating `snapshot_data`.\n *\n * \\param script_uri Uri of the script that contains the source code\n *\n * \\param package_config Uri of the package configuration file (either in format\n *   of .packages or .dart_tool/package_config.json) for the null safety\n *   detection to resolve package imports against. If this parameter is not\n *   passed the package resolution of the parent isolate should be used.\n *\n * \\param original_working_directory current working directory when the VM\n *   process was launched, this is used to correctly resolve the path specified\n *   for package_config.\n *\n * \\param snapshot_data Buffer containing the snapshot data of the\n *   isolate or NULL if no snapshot is provided. If provided, the buffers must\n *   remain valid until the isolate shuts down.\n *\n * \\param snapshot_instructions Buffer containing the snapshot instructions of\n *   the isolate or NULL if no snapshot is provided. If provided, the buffers\n *   must remain valid until the isolate shuts down.\n *\n * \\param kernel_buffer A buffer which contains a kernel/DIL program. Must\n *   remain valid until isolate shutdown.\n *\n * \\param kernel_buffer_size The size of `kernel_buffer`.\n *\n * \\return Returns true if the null safety is opted in by the input being\n *   run `script_uri`, `snapshot_data` or `kernel_buffer`.\n *\n */\nDART_EXPORT bool Dart_DetectNullSafety(const char* script_uri,\n                                       const char* package_config,\n                                       const char* original_working_directory,\n                                       const uint8_t* snapshot_data,\n                                       const uint8_t* snapshot_instructions,\n                                       const uint8_t* kernel_buffer,\n                                       intptr_t kernel_buffer_size);\n\n#define DART_KERNEL_ISOLATE_NAME \"kernel-service\"\n\n/*\n * =======\n * Service\n * =======\n */\n\n#define DART_VM_SERVICE_ISOLATE_NAME \"vm-service\"\n\n/**\n * Returns true if isolate is the service isolate.\n *\n * \\param isolate An isolate\n *\n * \\return Returns true if 'isolate' is the service isolate.\n */\nDART_EXPORT bool Dart_IsServiceIsolate(Dart_Isolate isolate);\n\n/**\n * Writes the CPU profile to the timeline as a series of 'instant' events.\n *\n * Note that this is an expensive operation.\n *\n * \\param main_port The main port of the Isolate whose profile samples to write.\n * \\param error An optional error, must be free()ed by caller.\n *\n * \\return Returns true if the profile is successfully written and false\n *         otherwise.\n */\nDART_EXPORT bool Dart_WriteProfileToTimeline(Dart_Port main_port, char** error);\n\n/*\n * ==============\n * Precompilation\n * ==============\n */\n\n/**\n * Compiles all functions reachable from entry points and marks\n * the isolate to disallow future compilation.\n *\n * Entry points should be specified using `@pragma(\"vm:entry-point\")`\n * annotation.\n *\n * \\return An error handle if a compilation error or runtime error running const\n * constructors was encountered.\n */\nDART_EXPORT Dart_Handle Dart_Precompile(void);\n\ntypedef void (*Dart_CreateLoadingUnitCallback)(\n    void* callback_data,\n    intptr_t loading_unit_id,\n    void** write_callback_data,\n    void** write_debug_callback_data);\ntypedef void (*Dart_StreamingWriteCallback)(void* callback_data,\n                                            const uint8_t* buffer,\n                                            intptr_t size);\ntypedef void (*Dart_StreamingCloseCallback)(void* callback_data);\n\nDART_EXPORT Dart_Handle Dart_LoadingUnitLibraryUris(intptr_t loading_unit_id);\n\n// On Darwin systems, 'dlsym' adds an '_' to the beginning of the symbol name.\n// Use the '...CSymbol' definitions for resolving through 'dlsym'. The actual\n// symbol names in the objects are given by the '...AsmSymbol' definitions.\n#if defined(__APPLE__)\n#define kSnapshotBuildIdCSymbol \"kDartSnapshotBuildId\"\n#define kVmSnapshotDataCSymbol \"kDartVmSnapshotData\"\n#define kVmSnapshotInstructionsCSymbol \"kDartVmSnapshotInstructions\"\n#define kVmSnapshotBssCSymbol \"kDartVmSnapshotBss\"\n#define kIsolateSnapshotDataCSymbol \"kDartIsolateSnapshotData\"\n#define kIsolateSnapshotInstructionsCSymbol \"kDartIsolateSnapshotInstructions\"\n#define kIsolateSnapshotBssCSymbol \"kDartIsolateSnapshotBss\"\n#else\n#define kSnapshotBuildIdCSymbol \"_kDartSnapshotBuildId\"\n#define kVmSnapshotDataCSymbol \"_kDartVmSnapshotData\"\n#define kVmSnapshotInstructionsCSymbol \"_kDartVmSnapshotInstructions\"\n#define kVmSnapshotBssCSymbol \"_kDartVmSnapshotBss\"\n#define kIsolateSnapshotDataCSymbol \"_kDartIsolateSnapshotData\"\n#define kIsolateSnapshotInstructionsCSymbol \"_kDartIsolateSnapshotInstructions\"\n#define kIsolateSnapshotBssCSymbol \"_kDartIsolateSnapshotBss\"\n#endif\n\n#define kSnapshotBuildIdAsmSymbol \"_kDartSnapshotBuildId\"\n#define kVmSnapshotDataAsmSymbol \"_kDartVmSnapshotData\"\n#define kVmSnapshotInstructionsAsmSymbol \"_kDartVmSnapshotInstructions\"\n#define kVmSnapshotBssAsmSymbol \"_kDartVmSnapshotBss\"\n#define kIsolateSnapshotDataAsmSymbol \"_kDartIsolateSnapshotData\"\n#define kIsolateSnapshotInstructionsAsmSymbol                                  \\\n  \"_kDartIsolateSnapshotInstructions\"\n#define kIsolateSnapshotBssAsmSymbol \"_kDartIsolateSnapshotBss\"\n\n/**\n *  Creates a precompiled snapshot.\n *   - A root library must have been loaded.\n *   - Dart_Precompile must have been called.\n *\n *  Outputs an assembly file defining the symbols listed in the definitions\n *  above.\n *\n *  The assembly should be compiled as a static or shared library and linked or\n *  loaded by the embedder. Running this snapshot requires a VM compiled with\n *  DART_PRECOMPILED_SNAPSHOT. The kDartVmSnapshotData and\n *  kDartVmSnapshotInstructions should be passed to Dart_Initialize. The\n *  kDartIsolateSnapshotData and kDartIsolateSnapshotInstructions should be\n *  passed to Dart_CreateIsolateGroup.\n *\n *  The callback will be invoked one or more times to provide the assembly code.\n *\n *  If stripped is true, then the assembly code will not include DWARF\n *  debugging sections.\n *\n *  If debug_callback_data is provided, debug_callback_data will be used with\n *  the callback to provide separate debugging information.\n *\n *  \\return A valid handle if no error occurs during the operation.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle\nDart_CreateAppAOTSnapshotAsAssembly(Dart_StreamingWriteCallback callback,\n                                    void* callback_data,\n                                    bool stripped,\n                                    void* debug_callback_data);\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle\nDart_CreateAppAOTSnapshotAsAssemblies(\n    Dart_CreateLoadingUnitCallback next_callback,\n    void* next_callback_data,\n    bool stripped,\n    Dart_StreamingWriteCallback write_callback,\n    Dart_StreamingCloseCallback close_callback);\n\n/**\n *  Creates a precompiled snapshot.\n *   - A root library must have been loaded.\n *   - Dart_Precompile must have been called.\n *\n *  Outputs an ELF shared library defining the symbols\n *   - _kDartVmSnapshotData\n *   - _kDartVmSnapshotInstructions\n *   - _kDartIsolateSnapshotData\n *   - _kDartIsolateSnapshotInstructions\n *\n *  The shared library should be dynamically loaded by the embedder.\n *  Running this snapshot requires a VM compiled with DART_PRECOMPILED_SNAPSHOT.\n *  The kDartVmSnapshotData and kDartVmSnapshotInstructions should be passed to\n *  Dart_Initialize. The kDartIsolateSnapshotData and\n *  kDartIsolateSnapshotInstructions should be passed to Dart_CreateIsolate.\n *\n *  The callback will be invoked one or more times to provide the binary output.\n *\n *  If stripped is true, then the binary output will not include DWARF\n *  debugging sections.\n *\n *  If debug_callback_data is provided, debug_callback_data will be used with\n *  the callback to provide separate debugging information.\n *\n * \\return A valid handle if no error occurs during the operation.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle\nDart_CreateAppAOTSnapshotAsElf(Dart_StreamingWriteCallback callback,\n                               void* callback_data,\n                               bool stripped,\n                               void* debug_callback_data);\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle\nDart_CreateAppAOTSnapshotAsElfs(Dart_CreateLoadingUnitCallback next_callback,\n                                void* next_callback_data,\n                                bool stripped,\n                                Dart_StreamingWriteCallback write_callback,\n                                Dart_StreamingCloseCallback close_callback);\n\n/**\n *  Like Dart_CreateAppAOTSnapshotAsAssembly, but only includes\n *  kDartVmSnapshotData and kDartVmSnapshotInstructions. It also does\n *  not strip DWARF information from the generated assembly or allow for\n *  separate debug information.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle\nDart_CreateVMAOTSnapshotAsAssembly(Dart_StreamingWriteCallback callback,\n                                   void* callback_data);\n\n/**\n * Sorts the class-ids in depth first traversal order of the inheritance\n * tree. This is a costly operation, but it can make method dispatch\n * more efficient and is done before writing snapshots.\n *\n * \\return A valid handle if no error occurs during the operation.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_SortClasses(void);\n\n/**\n *  Creates a snapshot that caches compiled code and type feedback for faster\n *  startup and quicker warmup in a subsequent process.\n *\n *  Outputs a snapshot in two pieces. The pieces should be passed to\n *  Dart_CreateIsolateGroup in a VM using the same VM snapshot pieces used in the\n *  current VM. The instructions piece must be loaded with read and execute\n *  permissions; the data piece may be loaded as read-only.\n *\n *   - Requires the VM to have not been started with --precompilation.\n *   - Not supported when targeting IA32.\n *   - The VM writing the snapshot and the VM reading the snapshot must be the\n *     same version, must be built in the same DEBUG/RELEASE/PRODUCT mode, must\n *     be targeting the same architecture, and must both be in checked mode or\n *     both in unchecked mode.\n *\n *  The buffers are scope allocated and are only valid until the next call to\n *  Dart_ExitScope.\n *\n * \\return A valid handle if no error occurs during the operation.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle\nDart_CreateAppJITSnapshotAsBlobs(uint8_t** isolate_snapshot_data_buffer,\n                                 intptr_t* isolate_snapshot_data_size,\n                                 uint8_t** isolate_snapshot_instructions_buffer,\n                                 intptr_t* isolate_snapshot_instructions_size);\n\n/**\n * Like Dart_CreateAppJITSnapshotAsBlobs, but also creates a new VM snapshot.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle\nDart_CreateCoreJITSnapshotAsBlobs(\n    uint8_t** vm_snapshot_data_buffer,\n    intptr_t* vm_snapshot_data_size,\n    uint8_t** vm_snapshot_instructions_buffer,\n    intptr_t* vm_snapshot_instructions_size,\n    uint8_t** isolate_snapshot_data_buffer,\n    intptr_t* isolate_snapshot_data_size,\n    uint8_t** isolate_snapshot_instructions_buffer,\n    intptr_t* isolate_snapshot_instructions_size);\n\n/**\n * Get obfuscation map for precompiled code.\n *\n * Obfuscation map is encoded as a JSON array of pairs (original name,\n * obfuscated name).\n *\n * \\return Returns an error handler if the VM was built in a mode that does not\n * support obfuscation.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle\nDart_GetObfuscationMap(uint8_t** buffer, intptr_t* buffer_length);\n\n/**\n *  Returns whether the VM only supports running from precompiled snapshots and\n *  not from any other kind of snapshot or from source (that is, the VM was\n *  compiled with DART_PRECOMPILED_RUNTIME).\n */\nDART_EXPORT bool Dart_IsPrecompiledRuntime(void);\n\n/**\n *  Print a native stack trace. Used for crash handling.\n *\n *  If context is NULL, prints the current stack trace. Otherwise, context\n *  should be a CONTEXT* (Windows) or ucontext_t* (POSIX) from a signal handler\n *  running on the current thread.\n */\nDART_EXPORT void Dart_DumpNativeStackTrace(void* context);\n\n/**\n *  Indicate that the process is about to abort, and the Dart VM should not\n *  attempt to cleanup resources.\n */\nDART_EXPORT void Dart_PrepareToAbort(void);\n\n/**\n * Callback provided by the embedder that is used by the VM to\n * produce footnotes appended to DWARF stack traces.\n *\n * Whenever VM formats a stack trace as a string it would call this callback\n * passing raw program counters for each frame in the stack trace.\n *\n * Embedder can then return a string which if not-null will be appended to the\n * formatted stack trace.\n *\n * Returned string is expected to be `malloc()` allocated. VM takes ownership\n * of the returned string and will `free()` it.\n *\n * \\param addresses raw program counter addresses for each frame\n * \\param count number of elements in the addresses array\n */\ntypedef char* (*Dart_DwarfStackTraceFootnoteCallback)(void* addresses[],\n                                                      intptr_t count);\n\n/**\n *  Configure DWARF stack trace footnote callback.\n */\nDART_EXPORT void Dart_SetDwarfStackTraceFootnoteCallback(\n    Dart_DwarfStackTraceFootnoteCallback callback);\n\n#endif /* INCLUDE_DART_API_H_ */ /* NOLINT */\n"
  },
  {
    "path": "core/dart-bridge/include/dart_api_dl.c",
    "content": "/*\n * Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file\n * for details. All rights reserved. Use of this source code is governed by a\n * BSD-style license that can be found in the LICENSE file.\n */\n\n#include \"dart_api_dl.h\"               /* NOLINT */\n#include \"dart_version.h\"              /* NOLINT */\n#include \"internal/dart_api_dl_impl.h\" /* NOLINT */\n\n#include <string.h>\n\n#define DART_API_DL_DEFINITIONS(name, R, A) name##_Type name##_DL = NULL;\n\nDART_API_ALL_DL_SYMBOLS(DART_API_DL_DEFINITIONS)\n\n#undef DART_API_DL_DEFINITIONS\n\ntypedef void* DartApiEntry_function;\n\nDartApiEntry_function FindFunctionPointer(const DartApiEntry* entries,\n                                          const char* name) {\n  while (entries->name != NULL) {\n    if (strcmp(entries->name, name) == 0) return entries->function;\n    entries++;\n  }\n  return NULL;\n}\n\nintptr_t Dart_InitializeApiDL(void* data) {\n  DartApi* dart_api_data = (DartApi*)data;\n\n  if (dart_api_data->major != DART_API_DL_MAJOR_VERSION) {\n    // If the DartVM we're running on does not have the same version as this\n    // file was compiled against, refuse to initialize. The symbols are not\n    // compatible.\n    return -1;\n  }\n  // Minor versions are allowed to be different.\n  // If the DartVM has a higher minor version, it will provide more symbols\n  // than we initialize here.\n  // If the DartVM has a lower minor version, it will not provide all symbols.\n  // In that case, we leave the missing symbols un-initialized. Those symbols\n  // should not be used by the Dart and native code. The client is responsible\n  // for checking the minor version number himself based on which symbols it\n  // is using.\n  // (If we would error out on this case, recompiling native code against a\n  // newer SDK would break all uses on older SDKs, which is too strict.)\n\n  const DartApiEntry* dart_api_function_pointers = dart_api_data->functions;\n\n#define DART_API_DL_INIT(name, R, A)                                           \\\n  name##_DL =                                                                  \\\n      (name##_Type)(FindFunctionPointer(dart_api_function_pointers, #name));\n  DART_API_ALL_DL_SYMBOLS(DART_API_DL_INIT)\n#undef DART_API_DL_INIT\n\n  return 0;\n}\n"
  },
  {
    "path": "core/dart-bridge/include/dart_api_dl.h",
    "content": "/*\n * Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file\n * for details. All rights reserved. Use of this source code is governed by a\n * BSD-style license that can be found in the LICENSE file.\n */\n\n#ifndef RUNTIME_INCLUDE_DART_API_DL_H_\n#define RUNTIME_INCLUDE_DART_API_DL_H_\n\n#include \"dart_api.h\"        /* NOLINT */\n#include \"dart_native_api.h\" /* NOLINT */\n\n/** \\mainpage Dynamically Linked Dart API\n *\n * This exposes a subset of symbols from dart_api.h and dart_native_api.h\n * available in every Dart embedder through dynamic linking.\n *\n * All symbols are postfixed with _DL to indicate that they are dynamically\n * linked and to prevent conflicts with the original symbol.\n *\n * Link `dart_api_dl.c` file into your library and invoke\n * `Dart_InitializeApiDL` with `NativeApi.initializeApiDLData`.\n */\n\nDART_EXPORT intptr_t Dart_InitializeApiDL(void* data);\n\n// ============================================================================\n// IMPORTANT! Never update these signatures without properly updating\n// DART_API_DL_MAJOR_VERSION and DART_API_DL_MINOR_VERSION.\n//\n// Verbatim copy of `dart_native_api.h` and `dart_api.h` symbol names and types\n// to trigger compile-time errors if the symbols in those files are updated\n// without updating these.\n//\n// Function return and argument types, and typedefs are carbon copied. Structs\n// are typechecked nominally in C/C++, so they are not copied, instead a\n// comment is added to their definition.\ntypedef int64_t Dart_Port_DL;\n\ntypedef void (*Dart_NativeMessageHandler_DL)(Dart_Port_DL dest_port_id,\n                                             Dart_CObject* message);\n\n// dart_native_api.h symbols can be called on any thread.\n#define DART_NATIVE_API_DL_SYMBOLS(F)                                          \\\n  /***** dart_native_api.h *****/                                              \\\n  /* Dart_Port */                                                              \\\n  F(Dart_PostCObject, bool, (Dart_Port_DL port_id, Dart_CObject * message))    \\\n  F(Dart_PostInteger, bool, (Dart_Port_DL port_id, int64_t message))           \\\n  F(Dart_NewNativePort, Dart_Port_DL,                                          \\\n    (const char* name, Dart_NativeMessageHandler_DL handler,                   \\\n     bool handle_concurrently))                                                \\\n  F(Dart_CloseNativePort, bool, (Dart_Port_DL native_port_id))\n\n// dart_api.h symbols can only be called on Dart threads.\n#define DART_API_DL_SYMBOLS(F)                                                 \\\n  /***** dart_api.h *****/                                                     \\\n  /* Errors */                                                                 \\\n  F(Dart_IsError, bool, (Dart_Handle handle))                                  \\\n  F(Dart_IsApiError, bool, (Dart_Handle handle))                               \\\n  F(Dart_IsUnhandledExceptionError, bool, (Dart_Handle handle))                \\\n  F(Dart_IsCompilationError, bool, (Dart_Handle handle))                       \\\n  F(Dart_IsFatalError, bool, (Dart_Handle handle))                             \\\n  F(Dart_GetError, const char*, (Dart_Handle handle))                          \\\n  F(Dart_ErrorHasException, bool, (Dart_Handle handle))                        \\\n  F(Dart_ErrorGetException, Dart_Handle, (Dart_Handle handle))                 \\\n  F(Dart_ErrorGetStackTrace, Dart_Handle, (Dart_Handle handle))                \\\n  F(Dart_NewApiError, Dart_Handle, (const char* error))                        \\\n  F(Dart_NewCompilationError, Dart_Handle, (const char* error))                \\\n  F(Dart_NewUnhandledExceptionError, Dart_Handle, (Dart_Handle exception))     \\\n  F(Dart_PropagateError, void, (Dart_Handle handle))                           \\\n  /* Dart_Handle, Dart_PersistentHandle, Dart_WeakPersistentHandle */          \\\n  F(Dart_HandleFromPersistent, Dart_Handle, (Dart_PersistentHandle object))    \\\n  F(Dart_HandleFromWeakPersistent, Dart_Handle,                                \\\n    (Dart_WeakPersistentHandle object))                                        \\\n  F(Dart_NewPersistentHandle, Dart_PersistentHandle, (Dart_Handle object))     \\\n  F(Dart_SetPersistentHandle, void,                                            \\\n    (Dart_PersistentHandle obj1, Dart_Handle obj2))                            \\\n  F(Dart_DeletePersistentHandle, void, (Dart_PersistentHandle object))         \\\n  F(Dart_NewWeakPersistentHandle, Dart_WeakPersistentHandle,                   \\\n    (Dart_Handle object, void* peer, intptr_t external_allocation_size,        \\\n     Dart_HandleFinalizer callback))                                           \\\n  F(Dart_DeleteWeakPersistentHandle, void, (Dart_WeakPersistentHandle object)) \\\n  F(Dart_UpdateExternalSize, void,                                             \\\n    (Dart_WeakPersistentHandle object, intptr_t external_allocation_size))     \\\n  F(Dart_NewFinalizableHandle, Dart_FinalizableHandle,                         \\\n    (Dart_Handle object, void* peer, intptr_t external_allocation_size,        \\\n     Dart_HandleFinalizer callback))                                           \\\n  F(Dart_DeleteFinalizableHandle, void,                                        \\\n    (Dart_FinalizableHandle object, Dart_Handle strong_ref_to_object))         \\\n  F(Dart_UpdateFinalizableExternalSize, void,                                  \\\n    (Dart_FinalizableHandle object, Dart_Handle strong_ref_to_object,          \\\n     intptr_t external_allocation_size))                                       \\\n  /* Isolates */                                                               \\\n  F(Dart_CurrentIsolate, Dart_Isolate, (void))                                 \\\n  F(Dart_ExitIsolate, void, (void))                                            \\\n  F(Dart_EnterIsolate, void, (Dart_Isolate))                                   \\\n  /* Dart_Port */                                                              \\\n  F(Dart_Post, bool, (Dart_Port_DL port_id, Dart_Handle object))               \\\n  F(Dart_NewSendPort, Dart_Handle, (Dart_Port_DL port_id))                     \\\n  F(Dart_SendPortGetId, Dart_Handle,                                           \\\n    (Dart_Handle port, Dart_Port_DL * port_id))                                \\\n  /* Scopes */                                                                 \\\n  F(Dart_EnterScope, void, (void))                                             \\\n  F(Dart_ExitScope, void, (void))                                              \\\n  /* Objects */                                                                \\\n  F(Dart_IsNull, bool, (Dart_Handle))\n\n#define DART_API_ALL_DL_SYMBOLS(F)                                             \\\n  DART_NATIVE_API_DL_SYMBOLS(F)                                                \\\n  DART_API_DL_SYMBOLS(F)\n// IMPORTANT! Never update these signatures without properly updating\n// DART_API_DL_MAJOR_VERSION and DART_API_DL_MINOR_VERSION.\n//\n// End of verbatim copy.\n// ============================================================================\n\n// Copy of definition of DART_EXPORT without 'used' attribute.\n//\n// The 'used' attribute cannot be used with DART_API_ALL_DL_SYMBOLS because\n// they are not function declarations, but variable declarations with a\n// function pointer type.\n//\n// The function pointer variables are initialized with the addresses of the\n// functions in the VM. If we were to use function declarations instead, we\n// would need to forward the call to the VM adding indirection.\n#if defined(__CYGWIN__)\n#error Tool chain and platform not supported.\n#elif defined(_WIN32)\n#if defined(DART_SHARED_LIB)\n#define DART_EXPORT_DL DART_EXTERN_C __declspec(dllexport)\n#else\n#define DART_EXPORT_DL DART_EXTERN_C\n#endif\n#else\n#if __GNUC__ >= 4\n#if defined(DART_SHARED_LIB)\n#define DART_EXPORT_DL DART_EXTERN_C __attribute__((visibility(\"default\")))\n#else\n#define DART_EXPORT_DL DART_EXTERN_C\n#endif\n#else\n#error Tool chain not supported.\n#endif\n#endif\n\n#define DART_API_DL_DECLARATIONS(name, R, A)                                   \\\n  typedef R(*name##_Type) A;                                                   \\\n  DART_EXPORT_DL name##_Type name##_DL;\n\nDART_API_ALL_DL_SYMBOLS(DART_API_DL_DECLARATIONS)\n\n#undef DART_API_DL_DECLARATIONS\n\n#undef DART_EXPORT_DL\n\n#endif /* RUNTIME_INCLUDE_DART_API_DL_H_ */ /* NOLINT */\n"
  },
  {
    "path": "core/dart-bridge/include/dart_native_api.h",
    "content": "/*\n * Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file\n * for details. All rights reserved. Use of this source code is governed by a\n * BSD-style license that can be found in the LICENSE file.\n */\n\n#ifndef RUNTIME_INCLUDE_DART_NATIVE_API_H_\n#define RUNTIME_INCLUDE_DART_NATIVE_API_H_\n\n#include \"dart_api.h\" /* NOLINT */\n\n/*\n * ==========================================\n * Message sending/receiving from native code\n * ==========================================\n */\n\n/**\n * A Dart_CObject is used for representing Dart objects as native C\n * data outside the Dart heap. These objects are totally detached from\n * the Dart heap. Only a subset of the Dart objects have a\n * representation as a Dart_CObject.\n *\n * The string encoding in the 'value.as_string' is UTF-8.\n *\n * All the different types from dart:typed_data are exposed as type\n * kTypedData. The specific type from dart:typed_data is in the type\n * field of the as_typed_data structure. The length in the\n * as_typed_data structure is always in bytes.\n *\n * The data for kTypedData is copied on message send and ownership remains with\n * the caller. The ownership of data for kExternalTyped is passed to the VM on\n * message send and returned when the VM invokes the\n * Dart_HandleFinalizer callback; a non-NULL callback must be provided.\n *\n * Note that Dart_CObject_kNativePointer is intended for internal use by\n * dart:io implementation and has no connection to dart:ffi Pointer class.\n * It represents a pointer to a native resource of a known type.\n * The receiving side will only see this pointer as an integer and will not\n * see the specified finalizer.\n * The specified finalizer will only be invoked if the message is not delivered.\n */\ntypedef enum {\n  Dart_CObject_kNull = 0,\n  Dart_CObject_kBool,\n  Dart_CObject_kInt32,\n  Dart_CObject_kInt64,\n  Dart_CObject_kDouble,\n  Dart_CObject_kString,\n  Dart_CObject_kArray,\n  Dart_CObject_kTypedData,\n  Dart_CObject_kExternalTypedData,\n  Dart_CObject_kSendPort,\n  Dart_CObject_kCapability,\n  Dart_CObject_kNativePointer,\n  Dart_CObject_kUnsupported,\n  Dart_CObject_kUnmodifiableExternalTypedData,\n  Dart_CObject_kNumberOfTypes\n} Dart_CObject_Type;\n// This enum is versioned by DART_API_DL_MAJOR_VERSION, only add at the end\n// and bump the DART_API_DL_MINOR_VERSION.\n\ntypedef struct _Dart_CObject {\n  Dart_CObject_Type type;\n  union {\n    bool as_bool;\n    int32_t as_int32;\n    int64_t as_int64;\n    double as_double;\n    const char* as_string;\n    struct {\n      Dart_Port id;\n      Dart_Port origin_id;\n    } as_send_port;\n    struct {\n      int64_t id;\n    } as_capability;\n    struct {\n      intptr_t length;\n      struct _Dart_CObject** values;\n    } as_array;\n    struct {\n      Dart_TypedData_Type type;\n      intptr_t length; /* in elements, not bytes */\n      const uint8_t* values;\n    } as_typed_data;\n    struct {\n      Dart_TypedData_Type type;\n      intptr_t length; /* in elements, not bytes */\n      uint8_t* data;\n      void* peer;\n      Dart_HandleFinalizer callback;\n    } as_external_typed_data;\n    struct {\n      intptr_t ptr;\n      intptr_t size;\n      Dart_HandleFinalizer callback;\n    } as_native_pointer;\n  } value;\n} Dart_CObject;\n// This struct is versioned by DART_API_DL_MAJOR_VERSION, bump the version when\n// changing this struct.\n\n/**\n * Posts a message on some port. The message will contain the Dart_CObject\n * object graph rooted in 'message'.\n *\n * While the message is being sent the state of the graph of Dart_CObject\n * structures rooted in 'message' should not be accessed, as the message\n * generation will make temporary modifications to the data. When the message\n * has been sent the graph will be fully restored.\n *\n * If true is returned, the message was enqueued, and finalizers for external\n * typed data will eventually run, even if the receiving isolate shuts down\n * before processing the message. If false is returned, the message was not\n * enqueued and ownership of external typed data in the message remains with the\n * caller.\n *\n * This function may be called on any thread when the VM is running (that is,\n * after Dart_Initialize has returned and before Dart_Cleanup has been called).\n *\n * \\param port_id The destination port.\n * \\param message The message to send.\n *\n * \\return True if the message was posted.\n */\nDART_EXPORT bool Dart_PostCObject(Dart_Port port_id, Dart_CObject* message);\n\n/**\n * Posts a message on some port. The message will contain the integer 'message'.\n *\n * \\param port_id The destination port.\n * \\param message The message to send.\n *\n * \\return True if the message was posted.\n */\nDART_EXPORT bool Dart_PostInteger(Dart_Port port_id, int64_t message);\n\n/**\n * A native message handler.\n *\n * This handler is associated with a native port by calling\n * Dart_NewNativePort.\n *\n * The message received is decoded into the message structure. The\n * lifetime of the message data is controlled by the caller. All the\n * data references from the message are allocated by the caller and\n * will be reclaimed when returning to it.\n */\ntypedef void (*Dart_NativeMessageHandler)(Dart_Port dest_port_id,\n                                          Dart_CObject* message);\n\n/**\n * Creates a new native port.  When messages are received on this\n * native port, then they will be dispatched to the provided native\n * message handler.\n *\n * \\param name The name of this port in debugging messages.\n * \\param handler The C handler to run when messages arrive on the port.\n * \\param handle_concurrently Is it okay to process requests on this\n *                            native port concurrently?\n *\n * \\return If successful, returns the port id for the native port.  In\n *   case of error, returns ILLEGAL_PORT.\n */\nDART_EXPORT Dart_Port Dart_NewNativePort(const char* name,\n                                         Dart_NativeMessageHandler handler,\n                                         bool handle_concurrently);\n/* TODO(turnidge): Currently handle_concurrently is ignored. */\n\n/**\n * Closes the native port with the given id.\n *\n * The port must have been allocated by a call to Dart_NewNativePort.\n *\n * \\param native_port_id The id of the native port to close.\n *\n * \\return Returns true if the port was closed successfully.\n */\nDART_EXPORT bool Dart_CloseNativePort(Dart_Port native_port_id);\n\n/*\n * ==================\n * Verification Tools\n * ==================\n */\n\n/**\n * Forces all loaded classes and functions to be compiled eagerly in\n * the current isolate..\n *\n * TODO(turnidge): Document.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_CompileAll(void);\n\n/**\n * Finalizes all classes.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT Dart_Handle Dart_FinalizeAllClasses(void);\n\n/*  This function is intentionally undocumented.\n *\n *  It should not be used outside internal tests.\n */\nDART_EXPORT void* Dart_ExecuteInternalCommand(const char* command, void* arg);\n\n#endif /* INCLUDE_DART_NATIVE_API_H_ */ /* NOLINT */\n"
  },
  {
    "path": "core/dart-bridge/include/dart_tools_api.h",
    "content": "// Copyright (c) 2011, the Dart project authors.  Please see the AUTHORS file\n// for details. All rights reserved. Use of this source code is governed by a\n// BSD-style license that can be found in the LICENSE file.\n\n#ifndef RUNTIME_INCLUDE_DART_TOOLS_API_H_\n#define RUNTIME_INCLUDE_DART_TOOLS_API_H_\n\n#include \"dart_api.h\" /* NOLINT */\n\n/** \\mainpage Dart Tools Embedding API Reference\n *\n * This reference describes the Dart embedding API for tools. Tools include\n * a debugger, service protocol, and timeline.\n *\n * NOTE: The APIs described in this file are unstable and subject to change.\n *\n * This reference is generated from the header include/dart_tools_api.h.\n */\n\n/*\n * ========\n * Debugger\n * ========\n */\n\n/**\n * ILLEGAL_ISOLATE_ID is a number guaranteed never to be associated with a\n * valid isolate.\n */\n#define ILLEGAL_ISOLATE_ID ILLEGAL_PORT\n\n/**\n * ILLEGAL_ISOLATE_GROUP_ID is a number guaranteed never to be associated with a\n * valid isolate group.\n */\n#define ILLEGAL_ISOLATE_GROUP_ID 0\n\n/*\n * =======\n * Service\n * =======\n */\n\n/**\n * A service request callback function.\n *\n * These callbacks, registered by the embedder, are called when the VM receives\n * a service request it can't handle and the service request command name\n * matches one of the embedder registered handlers.\n *\n * The return value of the callback indicates whether the response\n * should be used as a regular result or an error result.\n * Specifically, if the callback returns true, a regular JSON-RPC\n * response is built in the following way:\n *\n * {\n *   \"jsonrpc\": \"2.0\",\n *   \"result\": <json_object>,\n *   \"id\": <some sequence id>,\n * }\n *\n * If the callback returns false, a JSON-RPC error is built like this:\n *\n * {\n *   \"jsonrpc\": \"2.0\",\n *   \"error\": <json_object>,\n *   \"id\": <some sequence id>,\n * }\n *\n * \\param method The rpc method name.\n * \\param param_keys Service requests can have key-value pair parameters. The\n *   keys and values are flattened and stored in arrays.\n * \\param param_values The values associated with the keys.\n * \\param num_params The length of the param_keys and param_values arrays.\n * \\param user_data The user_data pointer registered with this handler.\n * \\param result A C string containing a valid JSON object. The returned\n *   pointer will be freed by the VM by calling free.\n *\n * \\return True if the result is a regular JSON-RPC response, false if the\n *   result is a JSON-RPC error.\n */\ntypedef bool (*Dart_ServiceRequestCallback)(const char* method,\n                                            const char** param_keys,\n                                            const char** param_values,\n                                            intptr_t num_params,\n                                            void* user_data,\n                                            const char** json_object);\n\n/**\n * Register a Dart_ServiceRequestCallback to be called to handle\n * requests for the named rpc on a specific isolate. The callback will\n * be invoked with the current isolate set to the request target.\n *\n * \\param method The name of the method that this callback is responsible for.\n * \\param callback The callback to invoke.\n * \\param user_data The user data passed to the callback.\n *\n * NOTE: If multiple callbacks with the same name are registered, only\n * the last callback registered will be remembered.\n */\nDART_EXPORT void Dart_RegisterIsolateServiceRequestCallback(\n    const char* method,\n    Dart_ServiceRequestCallback callback,\n    void* user_data);\n\n/**\n * Register a Dart_ServiceRequestCallback to be called to handle\n * requests for the named rpc. The callback will be invoked without a\n * current isolate.\n *\n * \\param method The name of the command that this callback is responsible for.\n * \\param callback The callback to invoke.\n * \\param user_data The user data passed to the callback.\n *\n * NOTE: If multiple callbacks with the same name are registered, only\n * the last callback registered will be remembered.\n */\nDART_EXPORT void Dart_RegisterRootServiceRequestCallback(\n    const char* method,\n    Dart_ServiceRequestCallback callback,\n    void* user_data);\n\n/**\n * Embedder information which can be requested by the VM for internal or\n * reporting purposes.\n *\n * The pointers in this structure are not going to be cached or freed by the VM.\n */\n\n #define DART_EMBEDDER_INFORMATION_CURRENT_VERSION (0x00000001)\n\ntypedef struct {\n  int32_t version;\n  const char* name;  // [optional] The name of the embedder\n  int64_t current_rss;  // [optional] the current RSS of the embedder\n  int64_t max_rss;  // [optional] the maximum RSS of the embedder\n} Dart_EmbedderInformation;\n\n/**\n * Callback provided by the embedder that is used by the VM to request\n * information.\n *\n * \\return Returns a pointer to a Dart_EmbedderInformation structure.\n * The embedder keeps the ownership of the structure and any field in it.\n * The embedder must ensure that the structure will remain valid until the\n * next invocation of the callback.\n */\ntypedef void (*Dart_EmbedderInformationCallback)(\n    Dart_EmbedderInformation* info);\n\n/**\n * Register a Dart_ServiceRequestCallback to be called to handle\n * requests for the named rpc. The callback will be invoked without a\n * current isolate.\n *\n * \\param method The name of the command that this callback is responsible for.\n * \\param callback The callback to invoke.\n * \\param user_data The user data passed to the callback.\n *\n * NOTE: If multiple callbacks are registered, only the last callback registered\n * will be remembered.\n */\nDART_EXPORT void Dart_SetEmbedderInformationCallback(\n    Dart_EmbedderInformationCallback callback);\n\n/**\n * Invoke a vm-service method and wait for its result.\n *\n * \\param request_json The utf8-encoded json-rpc request.\n * \\param request_json_length The length of the json-rpc request.\n *\n * \\param response_json The returned utf8-encoded json response, must be\n *   free()ed by caller.\n * \\param response_json_length The length of the returned json response.\n * \\param error An optional error, must be free()ed by caller.\n *\n * \\return Whether the call was successfully performed.\n *\n * NOTE: This method does not need a current isolate and must not have the\n * vm-isolate being the current isolate. It must be called after\n * Dart_Initialize() and before Dart_Cleanup().\n */\nDART_EXPORT bool Dart_InvokeVMServiceMethod(uint8_t* request_json,\n                                            intptr_t request_json_length,\n                                            uint8_t** response_json,\n                                            intptr_t* response_json_length,\n                                            char** error);\n\n/*\n * ========\n * Event Streams\n * ========\n */\n\n/**\n * A callback invoked when the VM service gets a request to listen to\n * some stream.\n *\n * \\return Returns true iff the embedder supports the named stream id.\n */\ntypedef bool (*Dart_ServiceStreamListenCallback)(const char* stream_id);\n\n/**\n * A callback invoked when the VM service gets a request to cancel\n * some stream.\n */\ntypedef void (*Dart_ServiceStreamCancelCallback)(const char* stream_id);\n\n/**\n * Adds VM service stream callbacks.\n *\n * \\param listen_callback A function pointer to a listen callback function.\n *   A listen callback function should not be already set when this function\n *   is called. A NULL value removes the existing listen callback function\n *   if any.\n *\n * \\param cancel_callback A function pointer to a cancel callback function.\n *   A cancel callback function should not be already set when this function\n *   is called. A NULL value removes the existing cancel callback function\n *   if any.\n *\n * \\return Success if the callbacks were added.  Otherwise, returns an\n *   error handle.\n */\nDART_EXPORT char* Dart_SetServiceStreamCallbacks(\n    Dart_ServiceStreamListenCallback listen_callback,\n    Dart_ServiceStreamCancelCallback cancel_callback);\n\n/**\n * Sends a data event to clients of the VM Service.\n *\n * A data event is used to pass an array of bytes to subscribed VM\n * Service clients.  For example, in the standalone embedder, this is\n * function used to provide WriteEvents on the Stdout and Stderr\n * streams.\n *\n * If the embedder passes in a stream id for which no client is\n * subscribed, then the event is ignored.\n *\n * \\param stream_id The id of the stream on which to post the event.\n *\n * \\param event_kind A string identifying what kind of event this is.\n *   For example, 'WriteEvent'.\n *\n * \\param bytes A pointer to an array of bytes.\n *\n * \\param bytes_length The length of the byte array.\n *\n * \\return NULL if the arguments are well formed.  Otherwise, returns an\n *   error string. The caller is responsible for freeing the error message.\n */\nDART_EXPORT char* Dart_ServiceSendDataEvent(const char* stream_id,\n                                            const char* event_kind,\n                                            const uint8_t* bytes,\n                                            intptr_t bytes_length);\n\n/*\n * ========\n * Reload support\n * ========\n *\n * These functions are used to implement reloading in the Dart VM.\n * This is an experimental feature, so embedders should be prepared\n * for these functions to change.\n */\n\n/**\n * A callback which determines whether the file at some url has been\n * modified since some time.  If the file cannot be found, true should\n * be returned.\n */\ntypedef bool (*Dart_FileModifiedCallback)(const char* url, int64_t since);\n\nDART_EXPORT char* Dart_SetFileModifiedCallback(\n    Dart_FileModifiedCallback file_modified_callback);\n\n/**\n * Returns true if isolate is currently reloading.\n */\nDART_EXPORT bool Dart_IsReloading();\n\n/*\n * ========\n * Timeline\n * ========\n */\n\n/**\n * Enable tracking of specified timeline category. This is operational\n * only when systrace timeline functionality is turned on.\n *\n * \\param categories A comma separated list of categories that need to\n *   be enabled, the categories are\n *   \"all\" : All categories\n *   \"API\" - Execution of Dart C API functions\n *   \"Compiler\" - Execution of Dart JIT compiler\n *   \"CompilerVerbose\" - More detailed Execution of Dart JIT compiler\n *   \"Dart\" - Execution of Dart code\n *   \"Debugger\" - Execution of Dart debugger\n *   \"Embedder\" - Execution of Dart embedder code\n *   \"GC\" - Execution of Dart Garbage Collector\n *   \"Isolate\" - Dart Isolate lifecycle execution\n *   \"VM\" - Execution in Dart VM runtime code\n *   \"\" - None\n *\n *  When \"all\" is specified all the categories are enabled.\n *  When a comma separated list of categories is specified, the categories\n *   that are specified will be enabled and the rest will be disabled.\n *  When \"\" is specified all the categories are disabled.\n *  The category names are case sensitive.\n *  eg:  Dart_EnableTimelineCategory(\"all\");\n *       Dart_EnableTimelineCategory(\"GC,API,Isolate\");\n *       Dart_EnableTimelineCategory(\"GC,Debugger,Dart\");\n *\n * \\return True if the categories were successfully enabled, False otherwise.\n */\nDART_EXPORT bool Dart_SetEnabledTimelineCategory(const char* categories);\n\n/**\n * Returns a timestamp in microseconds. This timestamp is suitable for\n * passing into the timeline system, and uses the same monotonic clock\n * as dart:developer's Timeline.now.\n *\n * \\return A timestamp that can be passed to the timeline system.\n */\nDART_EXPORT int64_t Dart_TimelineGetMicros();\n\n/**\n * Returns a raw timestamp in from the monotonic clock.\n *\n * \\return A raw timestamp from the monotonic clock.\n */\nDART_EXPORT int64_t Dart_TimelineGetTicks();\n\n/**\n * Returns the frequency of the monotonic clock.\n *\n * \\return The frequency of the monotonic clock.\n */\nDART_EXPORT int64_t Dart_TimelineGetTicksFrequency();\n\ntypedef enum {\n  Dart_Timeline_Event_Begin,          // Phase = 'B'.\n  Dart_Timeline_Event_End,            // Phase = 'E'.\n  Dart_Timeline_Event_Instant,        // Phase = 'i'.\n  Dart_Timeline_Event_Duration,       // Phase = 'X'.\n  Dart_Timeline_Event_Async_Begin,    // Phase = 'b'.\n  Dart_Timeline_Event_Async_End,      // Phase = 'e'.\n  Dart_Timeline_Event_Async_Instant,  // Phase = 'n'.\n  Dart_Timeline_Event_Counter,        // Phase = 'C'.\n  Dart_Timeline_Event_Flow_Begin,     // Phase = 's'.\n  Dart_Timeline_Event_Flow_Step,      // Phase = 't'.\n  Dart_Timeline_Event_Flow_End,       // Phase = 'f'.\n} Dart_Timeline_Event_Type;\n\n/**\n * Add a timeline event to the embedder stream.\n *\n * DEPRECATED: this function will be removed in Dart SDK v3.2.\n *\n * \\param label The name of the event. Its lifetime must extend at least until\n *     Dart_Cleanup.\n * \\param timestamp0 The first timestamp of the event.\n * \\param timestamp1_or_id When reporting an event of type\n *     |Dart_Timeline_Event_Duration|, the second (end) timestamp of the event\n *     should be passed through |timestamp1_or_id|. When reporting an event of\n *     type |Dart_Timeline_Event_Async_Begin|, |Dart_Timeline_Event_Async_End|,\n *     or |Dart_Timeline_Event_Async_Instant|, the async ID associated with the\n *     event should be passed through |timestamp1_or_id|. When reporting an\n *     event of type |Dart_Timeline_Event_Flow_Begin|,\n *     |Dart_Timeline_Event_Flow_Step|, or |Dart_Timeline_Event_Flow_End|, the\n *     flow ID associated with the event should be passed through\n *     |timestamp1_or_id|. When reporting an event of type\n *     |Dart_Timeline_Event_Begin| or |Dart_Timeline_Event_End|, the event ID\n *     associated with the event should be passed through |timestamp1_or_id|.\n *     Note that this event ID will only be used by the MacOS recorder. The\n *     argument to |timestamp1_or_id| will not be used when reporting events of\n *     other types.\n * \\param argument_count The number of argument names and values.\n * \\param argument_names An array of names of the arguments. The lifetime of the\n *     names must extend at least until Dart_Cleanup. The array may be reclaimed\n *     when this call returns.\n * \\param argument_values An array of values of the arguments. The values and\n *     the array may be reclaimed when this call returns.\n */\nDART_EXPORT void Dart_TimelineEvent(const char* label,\n                                    int64_t timestamp0,\n                                    int64_t timestamp1_or_id,\n                                    Dart_Timeline_Event_Type type,\n                                    intptr_t argument_count,\n                                    const char** argument_names,\n                                    const char** argument_values);\n\n/**\n * Add a timeline event to the embedder stream.\n *\n * Note regarding flow events: events must be associated with flow IDs in two\n * different ways to allow flow events to be serialized correctly in both\n * Chrome's JSON trace event format and Perfetto's proto trace format. Events\n * of type |Dart_Timeline_Event_Flow_Begin|, |Dart_Timeline_Event_Flow_Step|,\n * and |Dart_Timeline_Event_Flow_End| must be reported to support serialization\n * in Chrome's trace format. The |flow_ids| argument must be supplied when\n * reporting events of type |Dart_Timeline_Event_Begin|,\n * |Dart_Timeline_Event_Duration|, |Dart_Timeline_Event_Instant|,\n * |Dart_Timeline_Event_Async_Begin|, and |Dart_Timeline_Event_Async_Instant| to\n * support serialization in Perfetto's proto format.\n *\n * \\param label The name of the event. Its lifetime must extend at least until\n *     Dart_Cleanup.\n * \\param timestamp0 The first timestamp of the event.\n * \\param timestamp1_or_id When reporting an event of type\n *     |Dart_Timeline_Event_Duration|, the second (end) timestamp of the event\n *     should be passed through |timestamp1_or_id|. When reporting an event of\n *     type |Dart_Timeline_Event_Async_Begin|, |Dart_Timeline_Event_Async_End|,\n *     or |Dart_Timeline_Event_Async_Instant|, the async ID associated with the\n *     event should be passed through |timestamp1_or_id|. When reporting an\n *     event of type |Dart_Timeline_Event_Flow_Begin|,\n *     |Dart_Timeline_Event_Flow_Step|, or |Dart_Timeline_Event_Flow_End|, the\n *     flow ID associated with the event should be passed through\n *     |timestamp1_or_id|. When reporting an event of type\n *     |Dart_Timeline_Event_Begin| or |Dart_Timeline_Event_End|, the event ID\n *     associated with the event should be passed through |timestamp1_or_id|.\n *     Note that this event ID will only be used by the MacOS recorder. The\n *     argument to |timestamp1_or_id| will not be used when reporting events of\n *     other types.\n * \\param flow_id_count The number of flow IDs associated with this event.\n * \\param flow_ids An array of flow IDs associated with this event. The array\n *     may be reclaimed when this call returns.\n * \\param argument_count The number of argument names and values.\n * \\param argument_names An array of names of the arguments. The lifetime of the\n *     names must extend at least until Dart_Cleanup. The array may be reclaimed\n *     when this call returns.\n * \\param argument_values An array of values of the arguments. The values and\n *     the array may be reclaimed when this call returns.\n */\nDART_EXPORT void Dart_RecordTimelineEvent(const char* label,\n                                          int64_t timestamp0,\n                                          int64_t timestamp1_or_id,\n                                          intptr_t flow_id_count,\n                                          const int64_t* flow_ids,\n                                          Dart_Timeline_Event_Type type,\n                                          intptr_t argument_count,\n                                          const char** argument_names,\n                                          const char** argument_values);\n\n/**\n * Associates a name with the current thread. This name will be used to name\n * threads in the timeline. Can only be called after a call to Dart_Initialize.\n *\n * \\param name The name of the thread.\n */\nDART_EXPORT void Dart_SetThreadName(const char* name);\n\ntypedef struct {\n  const char* name;\n  const char* value;\n} Dart_TimelineRecorderEvent_Argument;\n\n#define DART_TIMELINE_RECORDER_CURRENT_VERSION (0x00000002)\n\ntypedef struct {\n  /* Set to DART_TIMELINE_RECORDER_CURRENT_VERSION */\n  int32_t version;\n\n  /* The event's type / phase. */\n  Dart_Timeline_Event_Type type;\n\n  /* The event's timestamp according to the same clock as\n   * Dart_TimelineGetMicros. For a duration event, this is the beginning time.\n   */\n  int64_t timestamp0;\n\n  /**\n   * For a duration event, this is the end time. For an async event, this is the\n   * async ID. For a flow event, this is the flow ID. For a begin or end event,\n   * this is the event ID (which is only referenced by the MacOS recorder).\n   */\n  int64_t timestamp1_or_id;\n\n  /* The current isolate of the event, as if by Dart_GetMainPortId, or\n   * ILLEGAL_PORT if the event had no current isolate. */\n  Dart_Port isolate;\n\n  /* The current isolate group of the event, as if by\n   * Dart_CurrentIsolateGroupId, or ILLEGAL_PORT if the event had no current\n   * isolate group. */\n  Dart_IsolateGroupId isolate_group;\n\n  /* The callback data associated with the isolate if any. */\n  void* isolate_data;\n\n  /* The callback data associated with the isolate group if any. */\n  void* isolate_group_data;\n\n  /* The name / label of the event. */\n  const char* label;\n\n  /* The stream / category of the event. */\n  const char* stream;\n\n  intptr_t argument_count;\n  Dart_TimelineRecorderEvent_Argument* arguments;\n} Dart_TimelineRecorderEvent;\n\n/**\n * Callback provided by the embedder to handle the completion of timeline\n * events.\n *\n * \\param event A timeline event that has just been completed. The VM keeps\n * ownership of the event and any field in it (i.e., the embedder should copy\n * any values it needs after the callback returns).\n */\ntypedef void (*Dart_TimelineRecorderCallback)(\n    Dart_TimelineRecorderEvent* event);\n\n/**\n * Register a `Dart_TimelineRecorderCallback` to be called as timeline events\n * are completed.\n *\n * The callback will be invoked without a current isolate.\n *\n * The callback will be invoked on the thread completing the event. Because\n * `Dart_TimelineEvent` may be called by any thread, the callback may be called\n * on any thread.\n *\n * The callback may be invoked at any time after `Dart_Initialize` is called and\n * before `Dart_Cleanup` returns.\n *\n * If multiple callbacks are registered, only the last callback registered\n * will be remembered. Providing a NULL callback will clear the registration\n * (i.e., a NULL callback produced a no-op instead of a crash).\n *\n * Setting a callback is insufficient to receive events through the callback. The\n * VM flag `timeline_recorder` must also be set to `callback`.\n */\nDART_EXPORT void Dart_SetTimelineRecorderCallback(\n    Dart_TimelineRecorderCallback callback);\n\n/*\n * =======\n * Metrics\n * =======\n */\n\n/**\n * Return metrics gathered for the VM and individual isolates.\n */\nDART_EXPORT int64_t\nDart_IsolateGroupHeapOldUsedMetric(Dart_IsolateGroup group);  // Byte\nDART_EXPORT int64_t\nDart_IsolateGroupHeapOldCapacityMetric(Dart_IsolateGroup group);  // Byte\nDART_EXPORT int64_t\nDart_IsolateGroupHeapOldExternalMetric(Dart_IsolateGroup group);  // Byte\nDART_EXPORT int64_t\nDart_IsolateGroupHeapNewUsedMetric(Dart_IsolateGroup group);  // Byte\nDART_EXPORT int64_t\nDart_IsolateGroupHeapNewCapacityMetric(Dart_IsolateGroup group);  // Byte\nDART_EXPORT int64_t\nDart_IsolateGroupHeapNewExternalMetric(Dart_IsolateGroup group);  // Byte\n\n/*\n * ========\n * UserTags\n * ========\n */\n\n/*\n * Gets the current isolate's currently set UserTag instance.\n *\n * \\return The currently set UserTag instance.\n */\nDART_EXPORT Dart_Handle Dart_GetCurrentUserTag();\n\n/*\n * Gets the current isolate's default UserTag instance.\n *\n * \\return The default UserTag with label 'Default'\n */\nDART_EXPORT Dart_Handle Dart_GetDefaultUserTag();\n\n/*\n * Creates a new UserTag instance.\n *\n * \\param label The name of the new UserTag.\n *\n * \\return The newly created UserTag instance or an error handle.\n */\nDART_EXPORT Dart_Handle Dart_NewUserTag(const char* label);\n\n/*\n * Updates the current isolate's UserTag to a new value.\n *\n * \\param user_tag The UserTag to be set as the current UserTag.\n *\n * \\return The previously set UserTag instance or an error handle.\n */\nDART_EXPORT Dart_Handle Dart_SetCurrentUserTag(Dart_Handle user_tag);\n\n/*\n * Returns the label of a given UserTag instance.\n *\n * \\param user_tag The UserTag from which the label will be retrieved.\n *\n * \\return The UserTag's label. NULL if the user_tag is invalid. The caller is\n *   responsible for freeing the returned label.\n */\nDART_EXPORT DART_WARN_UNUSED_RESULT char* Dart_GetUserTagLabel(\n    Dart_Handle user_tag);\n\n/*\n * =======\n * Heap Snapshot\n * =======\n */\n\n/**\n * Callback provided by the caller of `Dart_WriteHeapSnapshot` which is\n * used to write out chunks of the requested heap snapshot.\n *\n * \\param context An opaque context which was passed to `Dart_WriteHeapSnapshot`\n *   together with this callback.\n *\n * \\param buffer Pointer to the buffer containing a chunk of the snapshot.\n *   The callback owns the buffer and needs to `free` it.\n *\n * \\param size Number of bytes in the `buffer` to be written.\n *\n * \\param is_last Set to `true` for the last chunk. The callback will not\n *   be invoked again after it was invoked once with `is_last` set to `true`.\n */\ntypedef void (*Dart_HeapSnapshotWriteChunkCallback)(void* context,\n                                                    uint8_t* buffer,\n                                                    intptr_t size,\n                                                    bool is_last);\n\n/**\n * Generate heap snapshot of the current isolate group and stream it into the\n * given `callback`. VM would produce snapshot in chunks and send these chunks\n * one by one back to the embedder by invoking the provided `callback`.\n *\n * This API enables embedder to stream snapshot into a file or socket without\n * allocating a buffer to hold the whole snapshot in memory.\n *\n * The isolate group will be paused for the duration of this operation.\n *\n * \\param write Callback used to write chunks of the heap snapshot.\n *\n * \\param context Opaque context which would be passed on each invocation of\n *   `write` callback.\n *\n * \\returns `nullptr` if the operation is successful otherwise error message.\n *   Caller owns error message string and needs to `free` it.\n */\nDART_EXPORT char* Dart_WriteHeapSnapshot(\n    Dart_HeapSnapshotWriteChunkCallback write,\n    void* context);\n\n#endif  // RUNTIME_INCLUDE_DART_TOOLS_API_H_\n"
  },
  {
    "path": "core/dart-bridge/include/dart_version.h",
    "content": "/*\n * Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file\n * for details. All rights reserved. Use of this source code is governed by a\n * BSD-style license that can be found in the LICENSE file.\n */\n\n#ifndef RUNTIME_INCLUDE_DART_VERSION_H_\n#define RUNTIME_INCLUDE_DART_VERSION_H_\n\n// On breaking changes the major version is increased.\n// On backwards compatible changes the minor version is increased.\n// The versioning covers the symbols exposed in dart_api_dl.h\n#define DART_API_DL_MAJOR_VERSION 2\n#define DART_API_DL_MINOR_VERSION 3\n\n#endif /* RUNTIME_INCLUDE_DART_VERSION_H_ */ /* NOLINT */\n"
  },
  {
    "path": "core/dart-bridge/include/internal/dart_api_dl_impl.h",
    "content": "/*\n * Copyright (c) 2020, the Dart project authors.  Please see the AUTHORS file\n * for details. All rights reserved. Use of this source code is governed by a\n * BSD-style license that can be found in the LICENSE file.\n */\n\n#ifndef RUNTIME_INCLUDE_INTERNAL_DART_API_DL_IMPL_H_\n#define RUNTIME_INCLUDE_INTERNAL_DART_API_DL_IMPL_H_\n\ntypedef struct {\n  const char* name;\n  void (*function)(void);\n} DartApiEntry;\n\ntypedef struct {\n  const int major;\n  const int minor;\n  const DartApiEntry* const functions;\n} DartApi;\n\n#endif /* RUNTIME_INCLUDE_INTERNAL_DART_API_DL_IMPL_H_ */ /* NOLINT */\n"
  },
  {
    "path": "core/dart-bridge/lib.go",
    "content": "//go:build cgo\n\npackage dart_bridge\n\n/*\n#include <stdlib.h>\n#include \"stdint.h\"\n#include \"include/dart_api_dl.h\"\n#include \"include/dart_api_dl.c\"\n#include \"include/dart_native_api.h\"\n\nbool GoDart_PostCObject(Dart_Port_DL port, Dart_CObject* obj) {\n  return Dart_PostCObject_DL(port, obj);\n}\n*/\nimport \"C\"\nimport (\n\t\"fmt\"\n\t\"unsafe\"\n)\n\nfunc InitDartApi(api unsafe.Pointer) {\n\tif C.Dart_InitializeApiDL(api) != 0 {\n\t\tpanic(\"failed to create dart bridge\")\n\t} else {\n\t\tfmt.Println(\"Dart Api DL is initialized\")\n\t}\n}\n\nfunc SendToPort(port int64, msg string) bool {\n\tvar obj C.Dart_CObject\n\tobj._type = C.Dart_CObject_kString\n\tmsgString := C.CString(msg)\n\tdefer C.free(unsafe.Pointer(msgString))\n\tptr := unsafe.Pointer(&obj.value[0])\n\t*(**C.char)(ptr) = msgString\n\tisSuccess := C.GoDart_PostCObject(C.Dart_Port_DL(port), &obj)\n\tif !isSuccess {\n\t\treturn false\n\t}\n\treturn true\n}\n"
  },
  {
    "path": "core/dart-bridge/lib_common.go",
    "content": "//go:build !cgo\n\npackage dart_bridge\n\nfunc SendToPort(port int64, msg string) bool {\n\treturn false\n}\n"
  },
  {
    "path": "core/go.mod",
    "content": "module core\n\ngo 1.20\n\nreplace github.com/metacubex/mihomo => ./Clash.Meta\n\nrequire (\n\tgithub.com/metacubex/mihomo v0.0.0-00010101000000-000000000000\n\tgolang.org/x/sync v0.11.0\n)\n\nrequire (\n\tgithub.com/RyuaNerin/go-krypto v1.3.0 // indirect\n\tgithub.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 // indirect\n\tgithub.com/ajg/form v1.5.1 // indirect\n\tgithub.com/andybalholm/brotli v1.0.6 // indirect\n\tgithub.com/bahlo/generic-list-go v0.2.0 // indirect\n\tgithub.com/coreos/go-iptables v0.8.0 // indirect\n\tgithub.com/dlclark/regexp2 v1.12.0 // indirect\n\tgithub.com/dunglas/httpsfv v1.0.2 // indirect\n\tgithub.com/enfein/mieru/v3 v3.31.0 // indirect\n\tgithub.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358 // indirect\n\tgithub.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 // indirect\n\tgithub.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 // indirect\n\tgithub.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 // indirect\n\tgithub.com/fsnotify/fsnotify v1.9.0 // indirect\n\tgithub.com/gaukas/godicttls v0.0.4 // indirect\n\tgithub.com/go-ole/go-ole v1.3.0 // indirect\n\tgithub.com/gobwas/httphead v0.1.0 // indirect\n\tgithub.com/gobwas/pool v0.2.1 // indirect\n\tgithub.com/gobwas/ws v1.4.0 // indirect\n\tgithub.com/gofrs/uuid/v5 v5.4.0 // indirect\n\tgithub.com/golang/snappy v1.0.0 // indirect\n\tgithub.com/google/btree v1.1.3 // indirect\n\tgithub.com/google/go-cmp v0.6.0 // indirect\n\tgithub.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 // indirect\n\tgithub.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 // indirect\n\tgithub.com/josharian/native v1.1.0 // indirect\n\tgithub.com/klauspost/compress v1.17.9 // indirect\n\tgithub.com/klauspost/cpuid/v2 v2.2.9 // indirect\n\tgithub.com/klauspost/reedsolomon v1.12.3 // indirect\n\tgithub.com/mdlayher/netlink v1.7.2 // indirect\n\tgithub.com/mdlayher/socket v0.5.1 // indirect\n\tgithub.com/metacubex/amneziawg-go v0.0.0-20251104174305-5a0e9f7e361d // indirect\n\tgithub.com/metacubex/ascon v0.1.0 // indirect\n\tgithub.com/metacubex/bart v0.26.0 // indirect\n\tgithub.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b // indirect\n\tgithub.com/metacubex/blake3 v0.1.0 // indirect\n\tgithub.com/metacubex/chacha v0.1.5 // indirect\n\tgithub.com/metacubex/chi v0.1.0 // indirect\n\tgithub.com/metacubex/connect-ip-go v0.0.0-20260412152424-e1625567920a // indirect\n\tgithub.com/metacubex/cpu v0.1.1 // indirect\n\tgithub.com/metacubex/edwards25519 v1.2.0 // indirect\n\tgithub.com/metacubex/fswatch v0.1.1 // indirect\n\tgithub.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 // indirect\n\tgithub.com/metacubex/gvisor v0.0.0-20251227095601-261ec1326fe8 // indirect\n\tgithub.com/metacubex/hkdf v0.1.0 // indirect\n\tgithub.com/metacubex/hpke v0.1.0 // indirect\n\tgithub.com/metacubex/http v0.1.6 // indirect\n\tgithub.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604 // indirect\n\tgithub.com/metacubex/mhurl v0.1.0 // indirect\n\tgithub.com/metacubex/mlkem v0.1.0 // indirect\n\tgithub.com/metacubex/nftables v0.0.0-20260426003805-208c2c1ba2cb // indirect\n\tgithub.com/metacubex/qpack v0.6.0 // indirect\n\tgithub.com/metacubex/quic-go v0.59.1-0.20260413153657-53bb22f2c306 // indirect\n\tgithub.com/metacubex/randv2 v0.2.0 // indirect\n\tgithub.com/metacubex/restls-client-go v0.1.7 // indirect\n\tgithub.com/metacubex/sing v0.5.7 // indirect\n\tgithub.com/metacubex/sing-mux v0.3.9 // indirect\n\tgithub.com/metacubex/sing-quic v0.0.0-20260414034501-3ea3410d197a // indirect\n\tgithub.com/metacubex/sing-shadowsocks v0.2.12 // indirect\n\tgithub.com/metacubex/sing-shadowsocks2 v0.2.7 // indirect\n\tgithub.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 // indirect\n\tgithub.com/metacubex/sing-tun v0.4.18 // indirect\n\tgithub.com/metacubex/sing-vmess v0.2.5 // indirect\n\tgithub.com/metacubex/sing-wireguard v0.0.0-20260507084707-690d479ec947 // indirect\n\tgithub.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141 // indirect\n\tgithub.com/metacubex/ssh v0.1.0 // indirect\n\tgithub.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443 // indirect\n\tgithub.com/metacubex/tls v0.1.5 // indirect\n\tgithub.com/metacubex/utls v1.8.4 // indirect\n\tgithub.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f // indirect\n\tgithub.com/metacubex/yamux v0.0.0-20250918083631-dd5f17c0be49 // indirect\n\tgithub.com/miekg/dns v1.1.63 // indirect\n\tgithub.com/mroth/weightedrand/v2 v2.1.0 // indirect\n\tgithub.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 // indirect\n\tgithub.com/openacid/low v0.1.21 // indirect\n\tgithub.com/oschwald/maxminddb-golang v1.12.0 // indirect\n\tgithub.com/pierrec/lz4/v4 v4.1.14 // indirect\n\tgithub.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a // indirect\n\tgithub.com/samber/lo v1.53.0 // indirect\n\tgithub.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b // indirect\n\tgithub.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c // indirect\n\tgithub.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e // indirect\n\tgithub.com/sirupsen/logrus v1.9.4 // indirect\n\tgithub.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 // indirect\n\tgithub.com/vishvananda/netns v0.0.5 // indirect\n\tgithub.com/vmihailenco/msgpack/v5 v5.4.1 // indirect\n\tgithub.com/vmihailenco/tagparser/v2 v2.0.0 // indirect\n\tgithub.com/yosida95/uritemplate/v3 v3.0.2 // indirect\n\tgitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 // indirect\n\tgitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec // indirect\n\tgo4.org/netipx v0.0.0-20231129151722-fdeea329fbba // indirect\n\tgolang.org/x/crypto v0.33.0 // indirect\n\tgolang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e // indirect\n\tgolang.org/x/mod v0.20.0 // indirect\n\tgolang.org/x/net v0.35.0 // indirect\n\tgolang.org/x/sys v0.30.0 // indirect\n\tgolang.org/x/text v0.22.0 // indirect\n\tgolang.org/x/time v0.10.0 // indirect\n\tgolang.org/x/tools v0.24.0 // indirect\n\tgoogle.golang.org/protobuf v1.34.2 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "core/go.sum",
    "content": "github.com/RyuaNerin/go-krypto v1.3.0 h1:smavTzSMAx8iuVlGb4pEwl9MD2qicqMzuXR2QWp2/Pg=\ngithub.com/RyuaNerin/go-krypto v1.3.0/go.mod h1:9R9TU936laAIqAmjcHo/LsaXYOZlymudOAxjaBf62UM=\ngithub.com/RyuaNerin/testingutil v0.1.0 h1:IYT6JL57RV3U2ml3dLHZsVtPOP6yNK7WUVdzzlpNrss=\ngithub.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344 h1:cDVUiFo+npB0ZASqnw4q90ylaVAbnYyx0JYqK4YcGok=\ngithub.com/Yawning/aez v0.0.0-20211027044916-e49e68abd344/go.mod h1:9pIqrY6SXNL8vjRQE5Hd/OL5GyK/9MrGUWs87z/eFfk=\ngithub.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU=\ngithub.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY=\ngithub.com/andybalholm/brotli v1.0.6 h1:Yf9fFpf49Zrxb9NlQaluyE92/+X7UVHlhMNJN2sxfOI=\ngithub.com/andybalholm/brotli v1.0.6/go.mod h1:fO7iG3H7G2nSZ7m0zPUDn85XEX2GTukHGRSepvi9Eig=\ngithub.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=\ngithub.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=\ngithub.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk=\ngithub.com/coreos/go-iptables v0.8.0 h1:MPc2P89IhuVpLI7ETL/2tx3XZ61VeICZjYqDEgNsPRc=\ngithub.com/coreos/go-iptables v0.8.0/go.mod h1:Qe8Bv2Xik5FyTXwgIbLAnv2sWSBmvWdFETJConOQ//Q=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/dlclark/regexp2 v1.12.0 h1:0j4c5qQmnC6XOWNjP3PIXURXN2gWx76rd3KvgdPkCz8=\ngithub.com/dlclark/regexp2 v1.12.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8=\ngithub.com/dunglas/httpsfv v1.0.2 h1:iERDp/YAfnojSDJ7PW3dj1AReJz4MrwbECSSE59JWL0=\ngithub.com/dunglas/httpsfv v1.0.2/go.mod h1:zID2mqw9mFsnt7YC3vYQ9/cjq30q41W+1AnDwH8TiMg=\ngithub.com/enfein/mieru/v3 v3.31.0 h1:Fl2ocRCRXJzMygzdRjBHgqI996ZuIDHUmyQyovSf9sA=\ngithub.com/enfein/mieru/v3 v3.31.0/go.mod h1:zJBUCsi5rxyvHM8fjFf+GLaEl4OEjjBXr1s5F6Qd3hM=\ngithub.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358 h1:kXYqH/sL8dS/FdoFjr12ePjnLPorPo2FsnrHNuXSDyo=\ngithub.com/ericlagergren/aegis v0.0.0-20250325060835-cd0defd64358/go.mod h1:hkIFzoiIPZYxdFOOLyDho59b7SrDfo+w3h+yWdlg45I=\ngithub.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391 h1:8j2RH289RJplhA6WfdaPqzg1MjH2K8wX5e0uhAxrw2g=\ngithub.com/ericlagergren/polyval v0.0.0-20220411101811-e25bc10ba391/go.mod h1:K2R7GhgxrlJzHw2qiPWsCZXf/kXEJN9PLnQK73Ll0po=\ngithub.com/ericlagergren/saferand v0.0.0-20220206064634-960a4dd2bc5c h1:RUzBDdZ+e/HEe2Nh8lYsduiPAZygUfVXJn0Ncj5sHMg=\ngithub.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1 h1:tlDMEdcPRQKBEz5nGDMvswiajqh7k8ogWRlhRwKy5mY=\ngithub.com/ericlagergren/siv v0.0.0-20220507050439-0b757b3aa5f1/go.mod h1:4RfsapbGx2j/vU5xC/5/9qB3kn9Awp1YDiEnN43QrJ4=\ngithub.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010 h1:fuGucgPk5dN6wzfnxl3D0D3rVLw4v2SbBT9jb4VnxzA=\ngithub.com/ericlagergren/subtle v0.0.0-20220507045147-890d697da010/go.mod h1:JtBcj7sBuTTRupn7c2bFspMDIObMJsVK8TeUvpShPok=\ngithub.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=\ngithub.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=\ngithub.com/gaukas/godicttls v0.0.4 h1:NlRaXb3J6hAnTmWdsEKb9bcSBD6BvcIjdGdeb0zfXbk=\ngithub.com/gaukas/godicttls v0.0.4/go.mod h1:l6EenT4TLWgTdwslVb4sEMOCf7Bv0JAK67deKr9/NCI=\ngithub.com/go-ole/go-ole v1.3.0 h1:Dt6ye7+vXGIKZ7Xtk4s6/xVdGDQynvom7xCFEdWr6uE=\ngithub.com/go-ole/go-ole v1.3.0/go.mod h1:5LS6F96DhAwUc7C+1HLexzMXY1xGRSryjyPPKW6zv78=\ngithub.com/gobwas/httphead v0.1.0 h1:exrUm0f4YX0L7EBwZHuCF4GDp8aJfVeBrlLQrs6NqWU=\ngithub.com/gobwas/httphead v0.1.0/go.mod h1:O/RXo79gxV8G+RqlR/otEwx4Q36zl9rqC5u12GKvMCM=\ngithub.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og=\ngithub.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw=\ngithub.com/gobwas/ws v1.4.0 h1:CTaoG1tojrh4ucGPcoJFiAQUAsEWekEWvLy7GsVNqGs=\ngithub.com/gobwas/ws v1.4.0/go.mod h1:G3gNqMNtPppf5XUz7O4shetPpcZ1VJ7zt18dlUeakrc=\ngithub.com/gofrs/uuid/v5 v5.4.0 h1:EfbpCTjqMuGyq5ZJwxqzn3Cbr2d0rUZU7v5ycAk/e/0=\ngithub.com/gofrs/uuid/v5 v5.4.0/go.mod h1:CDOjlDMVAtN56jqyRUZh58JT31Tiw7/oQyEXZV+9bD8=\ngithub.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=\ngithub.com/golang/snappy v1.0.0 h1:Oy607GVXHs7RtbggtPBnr2RmDArIsAefDwvrdWvRhGs=\ngithub.com/golang/snappy v1.0.0/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=\ngithub.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg=\ngithub.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4=\ngithub.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=\ngithub.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=\ngithub.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8 h1:FKHo8hFI3A+7w0aUQuYXQ+6EN5stWmeY/AZqtM8xk9k=\ngithub.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo=\ngithub.com/google/tink/go v1.6.1 h1:t7JHqO8Ath2w2ig5vjwQYJzhGEZymedQc90lQXUBa4I=\ngithub.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905 h1:q3OEI9RaN/wwcx+qgGo6ZaoJkCiDYe/gjDLfq7lQQF4=\ngithub.com/insomniacslk/dhcp v0.0.0-20250109001534-8abf58130905/go.mod h1:VvGYjkZoJyKqlmT1yzakUs4mfKMNB0XdODP0+rdml6k=\ngithub.com/josharian/native v1.0.1-0.20221213033349-c1e37c09b531/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=\ngithub.com/josharian/native v1.1.0 h1:uuaP0hAbW7Y4l0ZRQ6C9zfb7Mg1mbFKry/xzDAfmtLA=\ngithub.com/josharian/native v1.1.0/go.mod h1:7X/raswPFr05uY3HiLlYeyQntB6OO7E/d2Cu7qoaN2w=\ngithub.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA=\ngithub.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw=\ngithub.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY=\ngithub.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8=\ngithub.com/klauspost/reedsolomon v1.12.3 h1:tzUznbfc3OFwJaTebv/QdhnFf2Xvb7gZ24XaHLBPmdc=\ngithub.com/klauspost/reedsolomon v1.12.3/go.mod h1:3K5rXwABAvzGeR01r6pWZieUALXO/Tq7bFKGIb4m4WI=\ngithub.com/mdlayher/netlink v1.7.2 h1:/UtM3ofJap7Vl4QWCPDGXY8d3GIY2UGSDbK+QWmY8/g=\ngithub.com/mdlayher/netlink v1.7.2/go.mod h1:xraEF7uJbxLhc5fpHL4cPe221LI2bdttWlU+ZGLfQSw=\ngithub.com/mdlayher/socket v0.5.1 h1:VZaqt6RkGkt2OE9l3GcC6nZkqD3xKeQLyfleW/uBcos=\ngithub.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ=\ngithub.com/metacubex/amneziawg-go v0.0.0-20251104174305-5a0e9f7e361d h1:vAJ0ZT4aO803F1uw2roIA9yH7Sxzox34tVVyye1bz6c=\ngithub.com/metacubex/amneziawg-go v0.0.0-20251104174305-5a0e9f7e361d/go.mod h1:MsM/5czONyXMJ3PRr5DbQ4O/BxzAnJWOIcJdLzW6qHY=\ngithub.com/metacubex/ascon v0.1.0 h1:6ZWxmXYszT1XXtwkf6nxfFhc/OTtQ9R3Vyj1jN32lGM=\ngithub.com/metacubex/ascon v0.1.0/go.mod h1:eV5oim4cVPPdEL8/EYaTZ0iIKARH9pnhAK/fcT5Kacc=\ngithub.com/metacubex/bart v0.26.0 h1:d/bBTvVatfVWGfQbiDpYKI1bXUJgjaabB2KpK1Tnk6w=\ngithub.com/metacubex/bart v0.26.0/go.mod h1:DCcyfP4MC+Zy7sLK7XeGuMw+P5K9mIRsYOBgiE8icsI=\ngithub.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b h1:j7dadXD8I2KTmMt8jg1JcaP1ANL3JEObJPdANKcSYPY=\ngithub.com/metacubex/bbolt v0.0.0-20250725135710-010dbbbb7a5b/go.mod h1:+WmP0VJZDkDszvpa83HzfUp6QzARl/IKkMorH4+nODw=\ngithub.com/metacubex/blake3 v0.1.0 h1:KGnjh/56REO7U+cgZA8dnBhxdP7jByrG7hTP+bu6cqY=\ngithub.com/metacubex/blake3 v0.1.0/go.mod h1:CCkLdzFrqf7xmxCdhQFvJsRRV2mwOLDoSPg6vUTB9Uk=\ngithub.com/metacubex/chacha v0.1.5 h1:fKWMb/5c7ZrY8Uoqi79PPFxl+qwR7X/q0OrsAubyX2M=\ngithub.com/metacubex/chacha v0.1.5/go.mod h1:Djn9bPZxLTXbJFSeyo0/qzEzQI+gUSSzttuzZM75GH8=\ngithub.com/metacubex/chi v0.1.0 h1:rjNDyDj50nRpicG43CNkIw4ssiCbmDL8d7wJXKlUCsg=\ngithub.com/metacubex/chi v0.1.0/go.mod h1:zM5u5oMQt8b2DjvDHvzadKrP6B2ztmasL1YHRMbVV+g=\ngithub.com/metacubex/connect-ip-go v0.0.0-20260412152424-e1625567920a h1:Ph5UfTWDsGruZ+v95Df1ycTflQFmpZBFg2LUvj2kx/M=\ngithub.com/metacubex/connect-ip-go v0.0.0-20260412152424-e1625567920a/go.mod h1:xYC8Ik7/rN6no+vTRuWMEziGwm3brA0wNM/zZP9qhOQ=\ngithub.com/metacubex/cpu v0.1.1 h1:rRV5HGmeuGzjiKI3hYbL0dCd0qGwM7VUtk4ICXD06mI=\ngithub.com/metacubex/cpu v0.1.1/go.mod h1:09VEt4dSRLR+bOA8l4w4NDuzGZ8n5dkMv7e8axgEeTU=\ngithub.com/metacubex/edwards25519 v1.2.0 h1:pIQZLBsjQgg3Nl/c86YYFEUAbL5qQRnPq4LrgIw0KK4=\ngithub.com/metacubex/edwards25519 v1.2.0/go.mod h1:NCQF3J/Ki7382FJuokwsywEIIEI/gro/3smyXgQJsx0=\ngithub.com/metacubex/fswatch v0.1.1 h1:jqU7C/v+g0qc2RUFgmAOPoVvfl2BXXUXEumn6oQuxhU=\ngithub.com/metacubex/fswatch v0.1.1/go.mod h1:czrTT7Zlbz7vWft8RQu9Qqh+JoX+Nnb+UabuyN1YsgI=\ngithub.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759 h1:cjd4biTvOzK9ubNCCkQ+ldc4YSH/rILn53l/xGBFHHI=\ngithub.com/metacubex/gopacket v1.1.20-0.20230608035415-7e2f98a3e759/go.mod h1:UHOv2xu+RIgLwpXca7TLrXleEd4oR3sPatW6IF8wU88=\ngithub.com/metacubex/gvisor v0.0.0-20251227095601-261ec1326fe8 h1:hUL81H0Ic/XIDkvtn9M1pmfDdfid7JzYQToY4Ps1TvQ=\ngithub.com/metacubex/gvisor v0.0.0-20251227095601-261ec1326fe8/go.mod h1:8LpS0IJW1VmWzUm3ylb0e2SK5QDm5lO/2qwWLZgRpBU=\ngithub.com/metacubex/hkdf v0.1.0 h1:fPA6VzXK8cU1foc/TOmGCDmSa7pZbxlnqhl3RNsthaA=\ngithub.com/metacubex/hkdf v0.1.0/go.mod h1:3seEfds3smgTAXqUGn+tgEJH3uXdsUjOiduG/2EtvZ4=\ngithub.com/metacubex/hpke v0.1.0 h1:gu2jUNhraehWi0P/z5HX2md3d7L1FhPQE6/Q0E9r9xQ=\ngithub.com/metacubex/hpke v0.1.0/go.mod h1:vfDm6gfgrwlXUxKDkWbcE44hXtmc1uxLDm2BcR11b3U=\ngithub.com/metacubex/http v0.1.6 h1:xvXuvXMCMxCWMF5nEJF4yiKvXL+p2atWMzs37e80m1I=\ngithub.com/metacubex/http v0.1.6/go.mod h1:Nxx0zZAo2AhRfanyL+fmmK6ACMtVsfpwIl1aFAik2Eg=\ngithub.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604 h1:hJwCVlE3ojViC35MGHB+FBr8TuIf3BUFn2EQ1VIamsI=\ngithub.com/metacubex/kcp-go v0.0.0-20260105040817-550693377604/go.mod h1:lpmN3m269b3V5jFCWtffqBLS4U3QQoIid9ugtO+OhVc=\ngithub.com/metacubex/mhurl v0.1.0 h1:ZdW4Zxe3j3uJ89gNytOazHu6kbHn5owutN/VfXOI8GE=\ngithub.com/metacubex/mhurl v0.1.0/go.mod h1:2qpQImCbXoUs6GwJrjuEXKelPyoimsIXr07eNKZdS00=\ngithub.com/metacubex/mlkem v0.1.0 h1:wFClitonSFcmipzzQvax75beLQU+D7JuC+VK1RzSL8I=\ngithub.com/metacubex/mlkem v0.1.0/go.mod h1:amhaXZVeYNShuy9BILcR7P0gbeo/QLZsnqCdL8U2PDQ=\ngithub.com/metacubex/nftables v0.0.0-20260426003805-208c2c1ba2cb h1:wk6mHYPURSUvWcUv72gNP79oiylFsscBSDPJ6ieV6Iw=\ngithub.com/metacubex/nftables v0.0.0-20260426003805-208c2c1ba2cb/go.mod h1:73ZrCfhdkW4F2E2GAlta3km/S2RHhFNogCMtWZV2anQ=\ngithub.com/metacubex/qpack v0.6.0 h1:YqClGIMOpiRYLjV1qOs483Od08MdPgRnHjt90FuaAKw=\ngithub.com/metacubex/qpack v0.6.0/go.mod h1:lKGSi7Xk94IMvHGOmxS9eIei3bvIqpOAImEBsaOwTkA=\ngithub.com/metacubex/quic-go v0.59.1-0.20260413153657-53bb22f2c306 h1:HlGLmLsWJMLSu0CMI9z/BmEnithB4oXM5Rom6/0Qxtg=\ngithub.com/metacubex/quic-go v0.59.1-0.20260413153657-53bb22f2c306/go.mod h1:oNzMrmylS897M3zSMuapIdwSwfq6F2qW01Z3NhVRJhk=\ngithub.com/metacubex/randv2 v0.2.0 h1:uP38uBvV2SxYfLj53kuvAjbND4RUDfFJjwr4UigMiLs=\ngithub.com/metacubex/randv2 v0.2.0/go.mod h1:kFi2SzrQ5WuneuoLLCMkABtiBu6VRrMrWFqSPyj2cxY=\ngithub.com/metacubex/restls-client-go v0.1.7 h1:eCwiXCTQb5WJu9IlgYvDBA1OgrINv58dEe7hcN5H15k=\ngithub.com/metacubex/restls-client-go v0.1.7/go.mod h1:BN/U52vPw7j8VTSh2vleD/MnmVKCov84mS5VcjVHH4g=\ngithub.com/metacubex/sing v0.5.7 h1:8OC+fhKFSv/l9ehEhJRaZZAOuthfZo68SteBVLe8QqM=\ngithub.com/metacubex/sing v0.5.7/go.mod h1:ypf0mjwlZm0sKdQSY+yQvmsbWa0hNPtkeqyRMGgoN+w=\ngithub.com/metacubex/sing-mux v0.3.9 h1:/aoBD2+sK2qsXDlNDe3hkR0GZuFDtwIZhOeGUx9W0Yk=\ngithub.com/metacubex/sing-mux v0.3.9/go.mod h1:8bT7ZKT3clRrJjYc/x5CRYibC1TX/bK73a3r3+2E+Fc=\ngithub.com/metacubex/sing-quic v0.0.0-20260414034501-3ea3410d197a h1:977o0ZYYbiQAGuOxql7Q6UN3rEy59OyAE0tELq4gZfI=\ngithub.com/metacubex/sing-quic v0.0.0-20260414034501-3ea3410d197a/go.mod h1:6ayFGfzzBE85csgQkM3gf4neFq6s0losHlPRSxY+nuk=\ngithub.com/metacubex/sing-shadowsocks v0.2.12 h1:Wqzo8bYXrK5aWqxu/TjlTnYZzAKtKsaFQBdr6IHFaBE=\ngithub.com/metacubex/sing-shadowsocks v0.2.12/go.mod h1:2e5EIaw0rxKrm1YTRmiMnDulwbGxH9hAFlrwQLQMQkU=\ngithub.com/metacubex/sing-shadowsocks2 v0.2.7 h1:hSuuc0YpsfiqYqt1o+fP4m34BQz4e6wVj3PPBVhor3A=\ngithub.com/metacubex/sing-shadowsocks2 v0.2.7/go.mod h1:vOEbfKC60txi0ca+yUlqEwOGc3Obl6cnSgx9Gf45KjE=\ngithub.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2 h1:gXU+MYPm7Wme3/OAY2FFzVq9d9GxPHOqu5AQfg/ddhI=\ngithub.com/metacubex/sing-shadowtls v0.0.0-20250503063515-5d9f966d17a2/go.mod h1:mbfboaXauKJNIHJYxQRa+NJs4JU9NZfkA+I33dS2+9E=\ngithub.com/metacubex/sing-tun v0.4.18 h1:WRzAosG0YkT3aZq5RJWtF+RdCgeJ8EpooS5ZM1lkXo0=\ngithub.com/metacubex/sing-tun v0.4.18/go.mod h1:g4I/JNplDBhXLF+aQWgFbhNeJPSXQOWS9HvLeNvkgeA=\ngithub.com/metacubex/sing-vmess v0.2.5 h1:m9Zt5I27lB9fmLMZfism9sH2LcnAfShZfwSkf6/KJoE=\ngithub.com/metacubex/sing-vmess v0.2.5/go.mod h1:AwtlzUgf8COe9tRYAKqWZ+leDH7p5U98a0ZUpYehl8Q=\ngithub.com/metacubex/sing-wireguard v0.0.0-20260507084707-690d479ec947 h1:IB03BvRQtvjWScyOK5jSQVJYY8osmZXHL+4VCEFMWcM=\ngithub.com/metacubex/sing-wireguard v0.0.0-20260507084707-690d479ec947/go.mod h1:jpAkVLPnCpGSfNyVmj6Cq4YbuZsFepm/Dc+9BAOcR80=\ngithub.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141 h1:DK2l6m2Fc85H2BhiAPgbJygiWhesPlfGmF+9Vw6ARdk=\ngithub.com/metacubex/smux v0.0.0-20260105030934-d0c8756d3141/go.mod h1:/yI4OiGOSn0SURhZdJF3CbtPg3nwK700bG8TZLMBvAg=\ngithub.com/metacubex/ssh v0.1.0 h1:iGfr99qk/eMHzUnQ/0bTxXT8+8SWqLSHBWDHoAhngzw=\ngithub.com/metacubex/ssh v0.1.0/go.mod h1:NUtl0d+/f2cG9ECEpMM8iCVOpmggQlC13oLeDUONDlU=\ngithub.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443 h1:H6TnfM12tOoTizYE/qBHH3nEuibIelmHI+BVSxVJr8o=\ngithub.com/metacubex/tfo-go v0.0.0-20251130171125-413e892ac443/go.mod h1:l9oLnLoEXyGZ5RVLsh7QCC5XsouTUyKk4F2nLm2DHLw=\ngithub.com/metacubex/tls v0.1.5 h1:ECcB83dj+zadnhlKcLnUUf1Sq6+vU0f/zoyU0+9oPTc=\ngithub.com/metacubex/tls v0.1.5/go.mod h1:0XeVdL0cBw+8i5Hqy3lVeP9IyD/LFTq02ExvHM6rzEM=\ngithub.com/metacubex/utls v1.8.4 h1:HmL9nUApDdWSkgUyodfwF6hSjtiwCGGdyhaSpEejKpg=\ngithub.com/metacubex/utls v1.8.4/go.mod h1:kncGGVhFaoGn5M3pFe3SXhZCzsbCJayNOH4UEqTKTko=\ngithub.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f h1:FGBPRb1zUabhPhDrlKEjQ9lgIwQ6cHL4x8M9lrERhbk=\ngithub.com/metacubex/wireguard-go v0.0.0-20250820062549-a6cecdd7f57f/go.mod h1:oPGcV994OGJedmmxrcK9+ni7jUEMGhR+uVQAdaduIP4=\ngithub.com/metacubex/yamux v0.0.0-20250918083631-dd5f17c0be49 h1:lhlqpYHopuTLx9xQt22kSA9HtnyTDmk5XjjQVCGHe2E=\ngithub.com/metacubex/yamux v0.0.0-20250918083631-dd5f17c0be49/go.mod h1:MBeEa9IVBphH7vc3LNtW6ZujVXFizotPo3OEiHQ+TNU=\ngithub.com/miekg/dns v1.1.63 h1:8M5aAw6OMZfFXTT7K5V0Eu5YiiL8l7nUAkyN6C9YwaY=\ngithub.com/miekg/dns v1.1.63/go.mod h1:6NGHfjhpmr5lt3XPLuyfDJi5AXbNIPM9PY6H6sF1Nfs=\ngithub.com/mroth/weightedrand/v2 v2.1.0 h1:o1ascnB1CIVzsqlfArQQjeMy1U0NcIbBO5rfd5E/OeU=\ngithub.com/mroth/weightedrand/v2 v2.1.0/go.mod h1:f2faGsfOGOwc1p94wzHKKZyTpcJUW7OJ/9U4yfiNAOU=\ngithub.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7 h1:1102pQc2SEPp5+xrS26wEaeb26sZy6k9/ZXlZN+eXE4=\ngithub.com/oasisprotocol/deoxysii v0.0.0-20220228165953-2091330c22b7/go.mod h1:UqoUn6cHESlliMhOnKLWr+CBH+e3bazUPvFj1XZwAjs=\ngithub.com/openacid/errors v0.8.1/go.mod h1:GUQEJJOJE3W9skHm8E8Y4phdl2LLEN8iD7c5gcGgdx0=\ngithub.com/openacid/low v0.1.21 h1:Tr2GNu4N/+rGRYdOsEHOE89cxUIaDViZbVmKz29uKGo=\ngithub.com/openacid/low v0.1.21/go.mod h1:q+MsKI6Pz2xsCkzV4BLj7NR5M4EX0sGz5AqotpZDVh0=\ngithub.com/openacid/must v0.1.3/go.mod h1:luPiXCuJlEo3UUFQngVQokV0MPGryeYvtCbQPs3U1+I=\ngithub.com/openacid/testkeys v0.1.6/go.mod h1:MfA7cACzBpbiwekivj8StqX0WIRmqlMsci1c37CA3Do=\ngithub.com/oschwald/maxminddb-golang v1.12.0 h1:9FnTOD0YOhP7DGxGsq4glzpGy5+w7pq50AS6wALUMYs=\ngithub.com/oschwald/maxminddb-golang v1.12.0/go.mod h1:q0Nob5lTCqyQ8WT6FYgS1L7PXKVVbgiymefNwIjPzgY=\ngithub.com/pierrec/lz4/v4 v4.1.14 h1:+fL8AQEZtz/ijeNnpduH0bROTu0O3NZAlPjQxGn8LwE=\ngithub.com/pierrec/lz4/v4 v4.1.14/go.mod h1:gZWDp/Ze/IJXGXf23ltt2EXimqmTUXEy0GFuRQyBid4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a h1:ObwtHN2VpqE0ZNjr6sGeT00J8uU7JF4cNUdb44/Duis=\ngithub.com/sagernet/netlink v0.0.0-20240612041022-b9a21c07ac6a/go.mod h1:xLnfdiJbSp8rNqYEdIW/6eDO4mVoogml14Bh2hSiFpM=\ngithub.com/samber/lo v1.53.0 h1:t975lj2py4kJPQ6haz1QMgtId2gtmfktACxIXArw3HM=\ngithub.com/samber/lo v1.53.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0=\ngithub.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b h1:rXHg9GrUEtWZhEkrykicdND3VPjlVbYiLdX9J7gimS8=\ngithub.com/sina-ghaderi/poly1305 v0.0.0-20220724002748-c5926b03988b/go.mod h1:X7qrxNQViEaAN9LNZOPl9PfvQtp3V3c7LTo0dvGi0fM=\ngithub.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c h1:DjKMC30y6yjG3IxDaeAj3PCoRr+IsO+bzyT+Se2m2Hk=\ngithub.com/sina-ghaderi/rabaead v0.0.0-20220730151906-ab6e06b96e8c/go.mod h1:NV/a66PhhWYVmUMaotlXJ8fIEFB98u+c8l/CQIEFLrU=\ngithub.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e h1:ur8uMsPIFG3i4Gi093BQITvwH9znsz2VUZmnmwHvpIo=\ngithub.com/sina-ghaderi/rabbitio v0.0.0-20220730151941-9ce26f4f872e/go.mod h1:+e5fBW3bpPyo+3uLo513gIUblc03egGjMM0+5GKbzK8=\ngithub.com/sirupsen/logrus v1.9.4 h1:TsZE7l11zFCLZnZ+teH4Umoq5BhEIfIzfRDZ1Uzql2w=\ngithub.com/sirupsen/logrus v1.9.4/go.mod h1:ftWc9WdOfJ0a92nsE2jF5u5ZwH8Bv2zdeOC42RjbV2g=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=\ngithub.com/u-root/uio v0.0.0-20230220225925-ffce2a382923 h1:tHNk7XK9GkmKUR6Gh8gVBKXc2MVSZ4G/NnWLtzw4gNA=\ngithub.com/u-root/uio v0.0.0-20230220225925-ffce2a382923/go.mod h1:eLL9Nub3yfAho7qB0MzZizFhTU2QkLeoVsWdHtDW264=\ngithub.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=\ngithub.com/vishvananda/netns v0.0.0-20191106174202-0a2b9b5464df/go.mod h1:JP3t17pCcGlemwknint6hfoeCVQrEMVwxRLRjXpq+BU=\ngithub.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f/go.mod h1:DD4vA1DwXk04H54A1oHXtwZmA0grkVMdPxx/VGLCah0=\ngithub.com/vishvananda/netns v0.0.5 h1:DfiHV+j8bA32MFM7bfEunvT8IAqQ/NzSJHtcmW5zdEY=\ngithub.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1 h1:cQriyiUvjTwOHg8QZaPihLWeRAAVoCpE00IUPn0Bjt8=\ngithub.com/vmihailenco/msgpack/v5 v5.4.1/go.mod h1:GaZTsDaehaPpQVyxrf5mtQlH+pc21PIudVV/E3rRQok=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0 h1:y09buUbR+b5aycVFQs/g70pqKVZNBmxwAhO7/IwNM9g=\ngithub.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=\ngithub.com/xtaci/lossyconn v0.0.0-20190602105132-8df528c0c9ae h1:J0GxkO96kL4WF+AIT3M4mfUVinOCPgf2uUWYFUzN0sM=\ngithub.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=\ngithub.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=\ngitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7 h1:UNrDfkQqiEYzdMlNsVvBYOAJWZjdktqFE9tQh5BT2+4=\ngitlab.com/go-extension/aes-ccm v0.0.0-20230221065045-e58665ef23c7/go.mod h1:E+rxHvJG9H6PUdzq9NRG6csuLN3XUx98BfGOVWNYnXs=\ngitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec h1:FpfFs4EhNehiVfzQttTuxanPIT43FtkkCFypIod8LHo=\ngitlab.com/yawning/bsaes.git v0.0.0-20190805113838-0a714cd429ec/go.mod h1:BZ1RAoRPbCxum9Grlv5aeksu2H8BiKehBYooU2LFiOQ=\ngo.uber.org/mock v0.4.0 h1:VcM4ZOtdbR4f6VXfiOpwpVJDL6lCReaZ6mw31wqh7KU=\ngo4.org/netipx v0.0.0-20231129151722-fdeea329fbba h1:0b9z3AuHCjxk0x/opv64kcgZLBseWJUpBw5I82+2U4M=\ngo4.org/netipx v0.0.0-20231129151722-fdeea329fbba/go.mod h1:PLyyIXexvUFg3Owu6p/WfdlivPbZJsZdgWZlrGope/Y=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.33.0 h1:IOBPskki6Lysi0lo9qQvbxiQ+FvsCC/YWOecCHAixus=\ngolang.org/x/crypto v0.33.0/go.mod h1:bVdXmD7IV/4GdElGPozy6U7lWdRXA4qyRVGJV57uQ5M=\ngolang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e h1:I88y4caeGeuDQxgdoFPUq097j7kNfw6uvuiNxUBfcBk=\ngolang.org/x/exp v0.0.0-20240904232852-e7e105dedf7e/go.mod h1:akd2r19cwCdwSwWeIdzYQGa/EZZyqcOdwWiwj5L5eKQ=\ngolang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY=\ngolang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg=\ngolang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0=\ngolang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.35.0 h1:T5GQRQb2y08kTAByq9L4/bz8cipCdA8FbRTXewonqY8=\ngolang.org/x/net v0.35.0/go.mod h1:EglIi67kWsHKlRzzVMUD93VMSWGFOMSZgxFjparz1Qk=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w=\ngolang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190606203320-7fc4e5ec1444/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20190804053845-51ab0e2deafa/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200217220822-9197077df867/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20220622161953-175b2fd9d664/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=\ngolang.org/x/sys v0.30.0 h1:QjkSwP/36a20jFYWkSue1YwXzLmsV5Gfq7Eiy72C1uc=\ngolang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=\ngolang.org/x/term v0.29.0 h1:L6pJp37ocefwRRtYPKSWOWzOtWSxVajvz2ldH/xi3iU=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.22.0 h1:bofq7m3/HAFvbF51jz3Q9wLg3jkvSPuiZu/pD1XwgtM=\ngolang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY=\ngolang.org/x/time v0.10.0 h1:3usCWA8tQn0L8+hFJQNgzpWbd89begxN66o1Ojdn5L4=\ngolang.org/x/time v0.10.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=\ngolang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28=\ngolang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24=\ngolang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngoogle.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg=\ngoogle.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\n"
  },
  {
    "path": "core/hub.go",
    "content": "package main\n\nimport (\n\t\"context\"\n\t\"core/state\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net\"\n\t\"runtime\"\n\t\"sort\"\n\t\"strconv\"\n\t\"time\"\n\n\t\"github.com/metacubex/mihomo/adapter\"\n\t\"github.com/metacubex/mihomo/adapter/outboundgroup\"\n\t\"github.com/metacubex/mihomo/common/observable\"\n\t\"github.com/metacubex/mihomo/common/utils\"\n\t\"github.com/metacubex/mihomo/component/mmdb\"\n\t\"github.com/metacubex/mihomo/component/resolver\"\n\t\"github.com/metacubex/mihomo/component/updater\"\n\t\"github.com/metacubex/mihomo/config\"\n\t\"github.com/metacubex/mihomo/constant\"\n\tcp \"github.com/metacubex/mihomo/constant/provider\"\n\t\"github.com/metacubex/mihomo/hub/executor\"\n\t\"github.com/metacubex/mihomo/listener\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/tunnel\"\n\t\"github.com/metacubex/mihomo/tunnel/statistic\"\n)\n\nvar (\n\tisInit            = false\n\texternalProviders = map[string]cp.Provider{}\n\tlogSubscriber     observable.Subscription[log.Event]\n)\n\nfunc handleInitClash(paramsString string) bool {\n\tvar params = InitParams{}\n\terr := json.Unmarshal([]byte(paramsString), &params)\n\tif err != nil {\n\t\treturn false\n\t}\n\tversion = params.Version\n\tif !isInit {\n\t\tconstant.SetHomeDir(params.HomeDir)\n\t\tisInit = true\n\t}\n\treturn isInit\n}\n\nfunc handleStartListener() bool {\n\trunLock.Lock()\n\tdefer runLock.Unlock()\n\tisRunning = true\n\tupdateListeners()\n\tresolver.ResetConnection()\n\treturn true\n}\n\nfunc handleStopListener() bool {\n\trunLock.Lock()\n\tdefer runLock.Unlock()\n\tisRunning = false\n\tlistener.StopListener()\n\treturn true\n}\n\nfunc handleGetIsInit() bool {\n\treturn isInit\n}\n\nfunc handleForceGc() {\n\tgo func() {\n\t\tlog.Infoln(\"[APP] request force GC\")\n\t\truntime.GC()\n\t}()\n}\n\nfunc handleShutdown() bool {\n\tstopListeners()\n\texecutor.Shutdown()\n\truntime.GC()\n\tisInit = false\n\treturn true\n}\n\nfunc handleValidateConfig(bytes []byte) string {\n\t_, err := config.UnmarshalRawConfig(bytes)\n\tif err != nil {\n\t\treturn err.Error()\n\t}\n\treturn \"\"\n}\n\nfunc handleGetProxies() map[string]constant.Proxy {\n\trunLock.Lock()\n\tdefer runLock.Unlock()\n\treturn tunnel.ProxiesWithProviders()\n}\n\nfunc handleChangeProxy(data string, fn func(string string)) {\n\trunLock.Lock()\n\tgo func() {\n\t\tdefer runLock.Unlock()\n\t\tvar params = &ChangeProxyParams{}\n\t\terr := json.Unmarshal([]byte(data), params)\n\t\tif err != nil {\n\t\t\tfn(err.Error())\n\t\t\treturn\n\t\t}\n\t\tgroupName := *params.GroupName\n\t\tproxyName := *params.ProxyName\n\t\tproxies := tunnel.ProxiesWithProviders()\n\t\tgroup, ok := proxies[groupName]\n\t\tif !ok {\n\t\t\tfn(\"Not found group\")\n\t\t\treturn\n\t\t}\n\t\tadapterProxy := group.(*adapter.Proxy)\n\t\tselector, ok := adapterProxy.ProxyAdapter.(outboundgroup.SelectAble)\n\t\tif !ok {\n\t\t\tfn(\"Group is not selectable\")\n\t\t\treturn\n\t\t}\n\t\tif proxyName == \"\" {\n\t\t\tselector.ForceSet(proxyName)\n\t\t} else {\n\t\t\terr = selector.Set(proxyName)\n\t\t}\n\t\tif err != nil {\n\t\t\tfn(err.Error())\n\t\t\treturn\n\t\t}\n\n\t\tfn(\"\")\n\t\treturn\n\t}()\n}\n\nfunc handleGetTraffic() string {\n\tup, down := statistic.DefaultManager.NowTraffic(state.CurrentState.OnlyStatisticsProxy)\n\ttraffic := map[string]int64{\n\t\t\"up\":   up,\n\t\t\"down\": down,\n\t}\n\tdata, err := json.Marshal(traffic)\n\tif err != nil {\n\t\tfmt.Println(\"Error:\", err)\n\t\treturn \"\"\n\t}\n\treturn string(data)\n}\n\nfunc handleGetTotalTraffic() string {\n\tup, down := statistic.DefaultManager.TotalTraffic(state.CurrentState.OnlyStatisticsProxy)\n\ttraffic := map[string]int64{\n\t\t\"up\":   up,\n\t\t\"down\": down,\n\t}\n\tdata, err := json.Marshal(traffic)\n\tif err != nil {\n\t\tfmt.Println(\"Error:\", err)\n\t\treturn \"\"\n\t}\n\treturn string(data)\n}\n\nfunc handleResetTraffic() {\n\tstatistic.DefaultManager.ResetStatistic()\n}\n\nfunc handleAsyncTestDelay(paramsString string, fn func(string)) {\n\tmBatch.Go(paramsString, func() (bool, error) {\n\t\tvar params = &TestDelayParams{}\n\t\terr := json.Unmarshal([]byte(paramsString), params)\n\t\tif err != nil {\n\t\t\tfn(\"\")\n\t\t\treturn false, nil\n\t\t}\n\n\t\texpectedStatus, err := utils.NewUnsignedRanges[uint16](\"\")\n\t\tif err != nil {\n\t\t\tfn(\"\")\n\t\t\treturn false, nil\n\t\t}\n\n\t\tctx, cancel := context.WithTimeout(context.Background(), time.Millisecond*time.Duration(params.Timeout))\n\t\tdefer cancel()\n\n\t\tproxies := tunnel.ProxiesWithProviders()\n\t\tproxy := proxies[params.ProxyName]\n\n\t\tdelayData := &Delay{\n\t\t\tName: params.ProxyName,\n\t\t}\n\n\t\tif proxy == nil {\n\t\t\tdelayData.Value = -1\n\t\t\tdata, _ := json.Marshal(delayData)\n\t\t\tfn(string(data))\n\t\t\treturn false, nil\n\t\t}\n\n\t\ttestUrl := constant.DefaultTestURL\n\n\t\tif params.TestUrl != \"\" {\n\t\t\ttestUrl = params.TestUrl\n\t\t}\n\t\tdelayData.Url = testUrl\n\n\t\tdelay, err := proxy.URLTest(ctx, testUrl, expectedStatus)\n\t\tif err != nil || delay == 0 {\n\t\t\tdelayData.Value = -1\n\t\t\tdata, _ := json.Marshal(delayData)\n\t\t\tfn(string(data))\n\t\t\treturn false, nil\n\t\t}\n\n\t\tdelayData.Value = int32(delay)\n\t\tdata, _ := json.Marshal(delayData)\n\t\tfn(string(data))\n\t\treturn false, nil\n\t})\n}\n\nfunc handleGetConnections() string {\n\trunLock.Lock()\n\tdefer runLock.Unlock()\n\tsnapshot := statistic.DefaultManager.Snapshot()\n\tdata, err := json.Marshal(snapshot)\n\tif err != nil {\n\t\tfmt.Println(\"Error:\", err)\n\t\treturn \"\"\n\t}\n\treturn string(data)\n}\n\nfunc handleCloseConnections() bool {\n\trunLock.Lock()\n\tdefer runLock.Unlock()\n\tcloseConnections()\n\treturn true\n}\n\nfunc closeConnections() {\n\tstatistic.DefaultManager.Range(func(c statistic.Tracker) bool {\n\t\terr := c.Close()\n\t\tif err != nil {\n\t\t\treturn false\n\t\t}\n\t\treturn true\n\t})\n}\n\nfunc handleResetConnections() bool {\n\trunLock.Lock()\n\tdefer runLock.Unlock()\n\tresolver.ResetConnection()\n\treturn true\n}\n\nfunc handleCloseConnection(connectionId string) bool {\n\trunLock.Lock()\n\tdefer runLock.Unlock()\n\tc := statistic.DefaultManager.Get(connectionId)\n\tif c == nil {\n\t\treturn false\n\t}\n\t_ = c.Close()\n\treturn true\n}\n\nfunc handleGetExternalProviders() string {\n\trunLock.Lock()\n\tdefer runLock.Unlock()\n\texternalProviders = getExternalProvidersRaw()\n\teps := make([]ExternalProvider, 0)\n\tfor _, p := range externalProviders {\n\t\texternalProvider, err := toExternalProvider(p)\n\t\tif err != nil {\n\t\t\tcontinue\n\t\t}\n\t\teps = append(eps, *externalProvider)\n\t}\n\tsort.Sort(ExternalProviders(eps))\n\tdata, err := json.Marshal(eps)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn string(data)\n}\n\nfunc handleGetExternalProvider(externalProviderName string) string {\n\trunLock.Lock()\n\tdefer runLock.Unlock()\n\texternalProvider, exist := externalProviders[externalProviderName]\n\tif !exist {\n\t\treturn \"\"\n\t}\n\te, err := toExternalProvider(externalProvider)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\tdata, err := json.Marshal(e)\n\tif err != nil {\n\t\treturn \"\"\n\t}\n\treturn string(data)\n}\n\nfunc handleUpdateGeoData(geoType string, geoName string, fn func(value string)) {\n\tgo func() {\n\t\tpath := constant.Path.Resolve(geoName)\n\t\tswitch geoType {\n\t\tcase \"MMDB\":\n\t\t\terr := updater.UpdateMMDBWithPath(path)\n\t\t\tif err != nil {\n\t\t\t\tfn(err.Error())\n\t\t\t\treturn\n\t\t\t}\n\t\tcase \"ASN\":\n\t\t\terr := updater.UpdateASNWithPath(path)\n\t\t\tif err != nil {\n\t\t\t\tfn(err.Error())\n\t\t\t\treturn\n\t\t\t}\n\t\tcase \"GeoIp\":\n\t\t\terr := updater.UpdateGeoIpWithPath(path)\n\t\t\tif err != nil {\n\t\t\t\tfn(err.Error())\n\t\t\t\treturn\n\t\t\t}\n\t\tcase \"GeoSite\":\n\t\t\terr := updater.UpdateGeoSiteWithPath(path)\n\t\t\tif err != nil {\n\t\t\t\tfn(err.Error())\n\t\t\t\treturn\n\t\t\t}\n\t\t}\n\t\tfn(\"\")\n\t}()\n}\n\nfunc handleUpdateExternalProvider(providerName string, fn func(value string)) {\n\tgo func() {\n\t\texternalProvider, exist := externalProviders[providerName]\n\t\tif !exist {\n\t\t\tfn(\"external provider is not exist\")\n\t\t\treturn\n\t\t}\n\t\terr := externalProvider.Update()\n\t\tif err != nil {\n\t\t\tfn(err.Error())\n\t\t\treturn\n\t\t}\n\t\tfn(\"\")\n\t}()\n}\n\nfunc handleSideLoadExternalProvider(providerName string, data []byte, fn func(value string)) {\n\tgo func() {\n\t\trunLock.Lock()\n\t\tdefer runLock.Unlock()\n\t\texternalProvider, exist := externalProviders[providerName]\n\t\tif !exist {\n\t\t\tfn(\"external provider is not exist\")\n\t\t\treturn\n\t\t}\n\t\terr := sideUpdateExternalProvider(externalProvider, data)\n\t\tif err != nil {\n\t\t\tfn(err.Error())\n\t\t\treturn\n\t\t}\n\t\tfn(\"\")\n\t}()\n}\n\nfunc handleStartLog() {\n\tif logSubscriber != nil {\n\t\tlog.UnSubscribe(logSubscriber)\n\t\tlogSubscriber = nil\n\t}\n\tlogSubscriber = log.Subscribe()\n\tgo func() {\n\t\tfor logData := range logSubscriber {\n\t\t\tif logData.LogLevel < log.Level() {\n\t\t\t\tcontinue\n\t\t\t}\n\t\t\tmessage := &Message{\n\t\t\t\tType: LogMessage,\n\t\t\t\tData: logData,\n\t\t\t}\n\t\t\tsendMessage(*message)\n\t\t}\n\t}()\n}\n\nfunc handleStopLog() {\n\tif logSubscriber != nil {\n\t\tlog.UnSubscribe(logSubscriber)\n\t\tlogSubscriber = nil\n\t}\n}\n\nfunc handleGetCountryCode(ip string, fn func(value string)) {\n\tgo func() {\n\t\trunLock.Lock()\n\t\tdefer runLock.Unlock()\n\t\tcodes := mmdb.IPInstance().LookupCode(net.ParseIP(ip))\n\t\tif len(codes) == 0 {\n\t\t\tfn(\"\")\n\t\t\treturn\n\t\t}\n\t\tfn(codes[0])\n\t}()\n}\n\nfunc handleGetMemory(fn func(value string)) {\n\tgo func() {\n\t\tfn(strconv.FormatUint(statistic.DefaultManager.Memory(), 10))\n\t}()\n}\n\nfunc handleSetState(params string) {\n\t_ = json.Unmarshal([]byte(params), state.CurrentState)\n}\n\nfunc handleGetConfig(path string) (*config.RawConfig, error) {\n\tbytes, err := readFile(path)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\tprof, err := config.UnmarshalRawConfig(bytes)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\treturn prof, nil\n}\n\nfunc handleFlushFakeIP() bool {\n\terr := resolver.FlushFakeIP()\n\tif err != nil {\n\t\tlog.Errorln(\"[APP] Flush FakeIP error: %v\", err)\n\t\treturn false\n\t}\n\tlog.Infoln(\"[APP] FakeIP pool flushed\")\n\treturn true\n}\n\nfunc handleFlushDnsCache() {\n\tresolver.ClearCache()\n\tlog.Infoln(\"[APP] DNS cache flushed\")\n}\n\nfunc handleCrash() {\n\tpanic(\"handle invoke crash\")\n}\n\nfunc handleUpdateConfig(bytes []byte) string {\n\tvar params = &UpdateParams{}\n\terr := json.Unmarshal(bytes, params)\n\tif err != nil {\n\t\treturn err.Error()\n\t}\n\tupdateConfig(params)\n\treturn \"\"\n}\n\nfunc handleSetupConfig(bytes []byte) string {\n\tvar params = defaultSetupParams()\n\terr := UnmarshalJson(bytes, params)\n\tif err != nil {\n\t\tlog.Errorln(\"unmarshalRawConfig error %v\", err)\n\t\t_ = setupConfig(defaultSetupParams())\n\t\treturn err.Error()\n\t}\n\terr = setupConfig(params)\n\tif err != nil {\n\t\treturn err.Error()\n\t}\n\treturn \"\"\n}\n\nfunc handleSuspend(suspended bool) bool {\n\tif suspended {\n\t\tlog.Infoln(\"[APP] Suspend mode enabled\")\n\t\ttunnel.OnSuspend()\n\t} else {\n\t\tlog.Infoln(\"[APP] Resume from suspend\")\n\t\ttunnel.OnRunning()\n\t}\n\treturn true\n}\n\nfunc init() {\n\tadapter.UrlTestHook = func(url string, name string, delay uint16) {\n\t\tdelayData := &Delay{\n\t\t\tUrl:  url,\n\t\t\tName: name,\n\t\t}\n\t\tif delay == 0 {\n\t\t\tdelayData.Value = -1\n\t\t} else {\n\t\t\tdelayData.Value = int32(delay)\n\t\t}\n\t\tsendMessage(Message{\n\t\t\tType: DelayMessage,\n\t\t\tData: delayData,\n\t\t})\n\t}\n\tstatistic.DefaultRequestNotify = func(c statistic.Tracker) {\n\t\tsendMessage(Message{\n\t\t\tType: RequestMessage,\n\t\t\tData: c.Info(),\n\t\t})\n\t}\n\texecutor.DefaultProviderLoadedHook = func(providerName string) {\n\t\tsendMessage(Message{\n\t\t\tType: LoadedMessage,\n\t\t\tData: providerName,\n\t\t})\n\t}\n}"
  },
  {
    "path": "core/lib.go",
    "content": "//go:build cgo\n\npackage main\n\n/*\n#include <stdlib.h>\n*/\nimport \"C\"\nimport (\n\tbridge \"core/dart-bridge\"\n\t\"encoding/json\"\n\t\"unsafe\"\n)\n\nvar messagePort int64 = -1\n\n//export initNativeApiBridge\nfunc initNativeApiBridge(api unsafe.Pointer) {\n\tbridge.InitDartApi(api)\n}\n\n//export attachMessagePort\nfunc attachMessagePort(mPort C.longlong) {\n\tmessagePort = int64(mPort)\n}\n\n//export getTraffic\nfunc getTraffic() *C.char {\n\treturn C.CString(handleGetTraffic())\n}\n\n//export getTotalTraffic\nfunc getTotalTraffic() *C.char {\n\treturn C.CString(handleGetTotalTraffic())\n}\n\n//export freeCString\nfunc freeCString(s *C.char) {\n\tC.free(unsafe.Pointer(s))\n}\n\nfunc (result ActionResult) send() {\n\tdata, err := result.Json()\n\tif err != nil {\n\t\treturn\n\t}\n\tbridge.SendToPort(result.Port, string(data))\n}\n\n//export invokeAction\nfunc invokeAction(paramsChar *C.char, port C.longlong) {\n\tparams := C.GoString(paramsChar)\n\ti := int64(port)\n\tvar action = &Action{}\n\terr := json.Unmarshal([]byte(params), action)\n\tif err != nil {\n\t\tbridge.SendToPort(i, err.Error())\n\t\treturn\n\t}\n\tresult := ActionResult{\n\t\tId:     action.Id,\n\t\tMethod: action.Method,\n\t\tPort:   i,\n\t}\n\tgo handleAction(action, result)\n}\n\nfunc sendMessage(message Message) {\n\tif messagePort == -1 {\n\t\treturn\n\t}\n\tresult := ActionResult{\n\t\tMethod: messageMethod,\n\t\tPort:   messagePort,\n\t\tData:   message,\n\t}\n\tresult.send()\n}\n\n//export getConfig\nfunc getConfig(s *C.char) *C.char {\n\tpath := C.GoString(s)\n\tconfig, err := handleGetConfig(path)\n\tif err != nil {\n\t\treturn C.CString(\"\")\n\t}\n\tmarshal, err := json.Marshal(config)\n\tif err != nil {\n\t\treturn C.CString(\"\")\n\t}\n\treturn C.CString(string(marshal))\n}\n\n//export startListener\nfunc startListener() {\n\thandleStartListener()\n}\n\n//export stopListener\nfunc stopListener() {\n\thandleStopListener()\n}\n\n//export suspend\nfunc suspend(suspended C.int) {\n\thandleSuspend(suspended != 0)\n}\n"
  },
  {
    "path": "core/lib_android.go",
    "content": "//go:build android && cgo\n\npackage main\n\nimport \"C\"\nimport (\n\t\"context\"\n\tbridge \"core/dart-bridge\"\n\t\"core/platform\"\n\t\"core/state\"\n\tt \"core/tun\"\n\t\"encoding/json\"\n\t\"errors\"\n\t\"fmt\"\n\t\"github.com/metacubex/mihomo/component/dialer\"\n\t\"github.com/metacubex/mihomo/component/process\"\n\t\"github.com/metacubex/mihomo/constant\"\n\t\"github.com/metacubex/mihomo/dns\"\n\t\"github.com/metacubex/mihomo/listener/sing_tun\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"golang.org/x/sync/semaphore\"\n\t\"net\"\n\t\"strconv\"\n\t\"strings\"\n\t\"sync\"\n\t\"syscall\"\n\t\"time\"\n\t\"unsafe\"\n)\n\ntype TunHandler struct {\n\tlistener *sing_tun.Listener\n\tcallback unsafe.Pointer\n\n\tlimit *semaphore.Weighted\n}\n\nfunc (t *TunHandler) close() {\n\t_ = t.limit.Acquire(context.TODO(), 4)\n\tdefer t.limit.Release(4)\n\tremoveTunHook()\n\tif t.listener != nil {\n\t\t_ = t.listener.Close()\n\t}\n\n\tif t.callback != nil {\n\t\treleaseObject(t.callback)\n\t}\n\tt.callback = nil\n\tt.listener = nil\n}\n\nfunc (t *TunHandler) handleProtect(fd int) {\n\t_ = t.limit.Acquire(context.Background(), 1)\n\tdefer t.limit.Release(1)\n\n\tif t.listener == nil {\n\t\treturn\n\t}\n\n\tProtect(t.callback, fd)\n}\n\nfunc (t *TunHandler) handleResolveProcess(source, target net.Addr) string {\n\t_ = t.limit.Acquire(context.Background(), 1)\n\tdefer t.limit.Release(1)\n\n\tif t.listener == nil {\n\t\treturn \"\"\n\t}\n\tvar protocol int\n\tuid := -1\n\tswitch source.Network() {\n\tcase \"udp\", \"udp4\", \"udp6\":\n\t\tprotocol = syscall.IPPROTO_UDP\n\tcase \"tcp\", \"tcp4\", \"tcp6\":\n\t\tprotocol = syscall.IPPROTO_TCP\n\t}\n\tif version < 29 {\n\t\tuid = platform.QuerySocketUidFromProcFs(source, target)\n\t}\n\treturn ResolveProcess(t.callback, protocol, source.String(), target.String(), uid)\n}\n\nvar (\n\ttunLock    sync.Mutex\n\trunTime    *time.Time\n\terrBlocked = errors.New(\"blocked\")\n\ttunHandler *TunHandler\n)\n\nfunc handleStopTun() {\n\ttunLock.Lock()\n\tdefer tunLock.Unlock()\n\trunTime = nil\n\tif tunHandler != nil {\n\t\ttunHandler.close()\n\t}\n}\n\nfunc handleStartTun(fd int, callback unsafe.Pointer) {\n\thandleStopTun()\n\ttunLock.Lock()\n\tdefer tunLock.Unlock()\n\tnow := time.Now()\n\trunTime = &now\n\tif fd != 0 {\n\t\ttunHandler = &TunHandler{\n\t\t\tcallback: callback,\n\t\t\tlimit:    semaphore.NewWeighted(4),\n\t\t}\n\t\tinitTunHook()\n\t\ttunListener, _ := t.Start(fd, currentConfig.General.Tun.Device, currentConfig.General.Tun.Stack, currentConfig.General.Tun.DisableICMPForwarding, uint32(currentConfig.General.Tun.MTU), currentConfig.General.IPv6)\n\t\tif tunListener != nil {\n\t\t\tlog.Infoln(\"TUN address: %v\", tunListener.Address())\n\t\t\ttunHandler.listener = tunListener\n\t\t} else {\n\t\t\tremoveTunHook()\n\t\t}\n\t}\n}\n\nfunc handleGetRunTime() string {\n\tif runTime == nil {\n\t\treturn \"\"\n\t}\n\treturn strconv.FormatInt(runTime.UnixMilli(), 10)\n}\n\nfunc initTunHook() {\n\tdialer.DefaultSocketHook = func(network, address string, conn syscall.RawConn) error {\n\t\tif platform.ShouldBlockConnection() {\n\t\t\treturn errBlocked\n\t\t}\n\t\treturn conn.Control(func(fd uintptr) {\n\t\t\ttunHandler.handleProtect(int(fd))\n\t\t})\n\t}\n\tprocess.DefaultPackageNameResolver = func(metadata *constant.Metadata) (string, error) {\n\t\tsrc, dst := metadata.RawSrcAddr, metadata.RawDstAddr\n\t\tif src == nil || dst == nil {\n\t\t\treturn \"\", process.ErrInvalidNetwork\n\t\t}\n\t\treturn tunHandler.handleResolveProcess(src, dst), nil\n\t}\n}\n\nfunc removeTunHook() {\n\tdialer.DefaultSocketHook = nil\n\tprocess.DefaultPackageNameResolver = nil\n}\n\nfunc handleGetAndroidVpnOptions() string {\n\ttunLock.Lock()\n\tdefer tunLock.Unlock()\n\tipv6Address := \"\"\n\tif currentConfig.General.IPv6 {\n\t\tipv6Address = state.DefaultIpv6Address\n\t}\n\toptions := state.AndroidVpnOptions{\n\t\tEnable:                state.CurrentState.VpnProps.Enable,\n\t\tPort:                  currentConfig.General.MixedPort,\n\t\tIpv4Address:           state.DefaultIpv4Address,\n\t\tIpv6Address:           ipv6Address,\n\t\tAccessControl:         state.CurrentState.VpnProps.AccessControl,\n\t\tSystemProxy:           state.CurrentState.VpnProps.SystemProxy,\n\t\tAllowBypass:           state.CurrentState.VpnProps.AllowBypass,\n\t\tRouteAddress:          currentConfig.General.Tun.RouteAddress,\n\t\tRouteMode:             state.CurrentState.VpnProps.RouteMode,\n\t\tBypassDomain:          state.CurrentState.BypassDomain,\n\t\tDnsServerAddress:      state.GetDnsServerAddress(),\n\t\tDozeSuspend:           state.CurrentState.VpnProps.DozeSuspend,\n\t\tDisableIcmpForwarding: currentConfig.General.Tun.DisableICMPForwarding,\n\t\tMtu:                   uint32(currentConfig.General.Tun.MTU),\n\t}\n\tdata, err := json.Marshal(options)\n\tif err != nil {\n\t\tfmt.Println(\"Error:\", err)\n\t\treturn \"\"\n\t}\n\treturn string(data)\n}\n\nfunc handleUpdateDns(value string) {\n\tgo func() {\n\t\tlog.Infoln(\"[DNS] updateDns %s\", value)\n\t\tdns.UpdateSystemDNS(strings.Split(value, \",\"))\n\t\tdns.FlushCacheWithDefaultResolver()\n\t}()\n}\n\nfunc handleGetCurrentProfileName() string {\n\tif state.CurrentState == nil {\n\t\treturn \"\"\n\t}\n\treturn state.CurrentState.CurrentProfileName\n}\n\nfunc nextHandle(action *Action, result ActionResult) bool {\n\tswitch action.Method {\n\tcase getAndroidVpnOptionsMethod:\n\t\tresult.success(handleGetAndroidVpnOptions())\n\t\treturn true\n\tcase updateDnsMethod:\n\t\tdata := action.Data.(string)\n\t\thandleUpdateDns(data)\n\t\tresult.success(true)\n\t\treturn true\n\tcase getRunTimeMethod:\n\t\tresult.success(handleGetRunTime())\n\t\treturn true\n\tcase getCurrentProfileNameMethod:\n\t\tresult.success(handleGetCurrentProfileName())\n\t\treturn true\n\t}\n\treturn false\n}\n\n//export quickStart\nfunc quickStart(initParamsChar *C.char, paramsChar *C.char, stateParamsChar *C.char, port C.longlong) {\n\ti := int64(port)\n\tparamsString := C.GoString(initParamsChar)\n\tbytes := []byte(C.GoString(paramsChar))\n\tstateParams := C.GoString(stateParamsChar)\n\tgo func() {\n\t\tres := handleInitClash(paramsString)\n\t\tif res == false {\n\t\t\tbridge.SendToPort(i, \"init error\")\n\t\t}\n\t\thandleSetState(stateParams)\n\t\tbridge.SendToPort(i, handleSetupConfig(bytes))\n\t}()\n}\n\n//export startTUN\nfunc startTUN(fd C.int, callback unsafe.Pointer) bool {\n\tgo func() {\n\t\thandleStartTun(int(fd), callback)\n\t}()\n\treturn true\n}\n\n//export getRunTime\nfunc getRunTime() *C.char {\n\treturn C.CString(handleGetRunTime())\n}\n\n//export stopTun\nfunc stopTun() {\n\tgo func() {\n\t\thandleStopTun()\n\t}()\n}\n\n//export getCurrentProfileName\nfunc getCurrentProfileName() *C.char {\n\treturn C.CString(handleGetCurrentProfileName())\n}\n\n//export getAndroidVpnOptions\nfunc getAndroidVpnOptions() *C.char {\n\treturn C.CString(handleGetAndroidVpnOptions())\n}\n\n//export setState\nfunc setState(s *C.char) {\n\tparamsString := C.GoString(s)\n\thandleSetState(paramsString)\n}\n\n//export updateDns\nfunc updateDns(s *C.char) {\n\tdnsList := C.GoString(s)\n\thandleUpdateDns(dnsList)\n}\n"
  },
  {
    "path": "core/lib_no_android.go",
    "content": "//go:build !android && cgo\n\npackage main\n\nfunc nextHandle(action *Action, result ActionResult) bool {\n\treturn false\n}\n"
  },
  {
    "path": "core/main.go",
    "content": "//go:build !cgo\n\npackage main\n\nimport (\n\t\"fmt\"\n\t\"os\"\n)\n\nfunc main() {\n\targs := os.Args\n\tif len(args) <= 1 {\n\t\tfmt.Println(\"Arguments error\")\n\t\tos.Exit(1)\n\t}\n\tstartServer(args[1])\n}\n"
  },
  {
    "path": "core/main_cgo.go",
    "content": "//go:build cgo\n\npackage main\n\nimport \"C\"\n\nfunc main() {\n}\n"
  },
  {
    "path": "core/platform/limit.go",
    "content": "//go:build android && cgo\n\npackage platform\n\nimport \"syscall\"\n\nvar nullFd int\nvar maxFdCount int\n\nfunc init() {\n\tfd, err := syscall.Open(\"/dev/null\", syscall.O_WRONLY, 0644)\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\tnullFd = fd\n\n\tvar limit syscall.Rlimit\n\n\tif err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &limit); err != nil {\n\t\tmaxFdCount = 1024\n\t} else {\n\t\tmaxFdCount = int(limit.Cur)\n\t}\n\n\tmaxFdCount = maxFdCount / 4 * 3\n}\n\nfunc ShouldBlockConnection() bool {\n\tfd, err := syscall.Dup(nullFd)\n\tif err != nil {\n\t\treturn true\n\t}\n\n\t_ = syscall.Close(fd)\n\n\tif fd > maxFdCount {\n\t\treturn true\n\t}\n\n\treturn false\n}\n"
  },
  {
    "path": "core/platform/procfs.go",
    "content": "//go:build linux\n// +build linux\n\npackage platform\n\nimport (\n\t\"bufio\"\n\t\"encoding/binary\"\n\t\"encoding/hex\"\n\t\"fmt\"\n\t\"net\"\n\t\"os\"\n\t\"strconv\"\n\t\"strings\"\n\t\"unsafe\"\n)\n\nvar netIndexOfLocal = -1\nvar netIndexOfUid = -1\n\nvar nativeEndian binary.ByteOrder\n\nfunc QuerySocketUidFromProcFs(source, _ net.Addr) int {\n\tif netIndexOfLocal < 0 || netIndexOfUid < 0 {\n\t\treturn -1\n\t}\n\n\tnetwork := source.Network()\n\n\tif strings.HasSuffix(network, \"4\") || strings.HasSuffix(network, \"6\") {\n\t\tnetwork = network[:len(network)-1]\n\t}\n\n\tpath := \"/proc/net/\" + network\n\n\tvar sIP net.IP\n\tvar sPort int\n\n\tswitch s := source.(type) {\n\tcase *net.TCPAddr:\n\t\tsIP = s.IP\n\t\tsPort = s.Port\n\tcase *net.UDPAddr:\n\t\tsIP = s.IP\n\t\tsPort = s.Port\n\tdefault:\n\t\treturn -1\n\t}\n\n\tsIP = sIP.To16()\n\tif sIP == nil {\n\t\treturn -1\n\t}\n\n\tuid := doQuery(path+\"6\", sIP, sPort)\n\tif uid == -1 {\n\t\tsIP = sIP.To4()\n\t\tif sIP == nil {\n\t\t\treturn -1\n\t\t}\n\t\tuid = doQuery(path, sIP, sPort)\n\t}\n\n\treturn uid\n}\n\nfunc doQuery(path string, sIP net.IP, sPort int) int {\n\tfile, err := os.Open(path)\n\tif err != nil {\n\t\treturn -1\n\t}\n\n\tdefer func(file *os.File) {\n\t\t_ = file.Close()\n\t}(file)\n\n\treader := bufio.NewReader(file)\n\n\tvar bytes [2]byte\n\n\tbinary.BigEndian.PutUint16(bytes[:], uint16(sPort))\n\n\tlocal := fmt.Sprintf(\"%s:%s\", hex.EncodeToString(nativeEndianIP(sIP)), hex.EncodeToString(bytes[:]))\n\n\tfor {\n\t\trow, _, err := reader.ReadLine()\n\t\tif err != nil {\n\t\t\treturn -1\n\t\t}\n\n\t\tfields := strings.Fields(string(row))\n\n\t\tif len(fields) <= netIndexOfLocal || len(fields) <= netIndexOfUid {\n\t\t\tcontinue\n\t\t}\n\n\t\tif strings.EqualFold(local, fields[netIndexOfLocal]) {\n\t\t\tuid, err := strconv.Atoi(fields[netIndexOfUid])\n\t\t\tif err != nil {\n\t\t\t\treturn -1\n\t\t\t}\n\n\t\t\treturn uid\n\t\t}\n\t}\n}\n\nfunc nativeEndianIP(ip net.IP) []byte {\n\tresult := make([]byte, len(ip))\n\n\tfor i := 0; i < len(ip); i += 4 {\n\t\tvalue := binary.BigEndian.Uint32(ip[i:])\n\n\t\tnativeEndian.PutUint32(result[i:], value)\n\t}\n\n\treturn result\n}\n\nfunc init() {\n\tfile, err := os.Open(\"/proc/net/tcp\")\n\tif err != nil {\n\t\treturn\n\t}\n\n\tdefer func(file *os.File) {\n\t\t_ = file.Close()\n\t}(file)\n\n\treader := bufio.NewReader(file)\n\n\theader, _, err := reader.ReadLine()\n\tif err != nil {\n\t\treturn\n\t}\n\n\tcolumns := strings.Fields(string(header))\n\n\tvar txQueue, rxQueue, tr, tmWhen bool\n\n\tfor idx, col := range columns {\n\t\toffset := 0\n\n\t\tif txQueue && rxQueue {\n\t\t\toffset--\n\t\t}\n\n\t\tif tr && tmWhen {\n\t\t\toffset--\n\t\t}\n\n\t\tswitch col {\n\t\tcase \"tx_queue\":\n\t\t\ttxQueue = true\n\t\tcase \"rx_queue\":\n\t\t\trxQueue = true\n\t\tcase \"tr\":\n\t\t\ttr = true\n\t\tcase \"tm->when\":\n\t\t\ttmWhen = true\n\t\tcase \"local_address\":\n\t\t\tnetIndexOfLocal = idx + offset\n\t\tcase \"uid\":\n\t\t\tnetIndexOfUid = idx + offset\n\t\t}\n\t}\n}\n\nfunc init() {\n\tvar x uint32 = 0x01020304\n\tif *(*byte)(unsafe.Pointer(&x)) == 0x01 {\n\t\tnativeEndian = binary.BigEndian\n\t} else {\n\t\tnativeEndian = binary.LittleEndian\n\t}\n}\n"
  },
  {
    "path": "core/server.go",
    "content": "//go:build !cgo\n\npackage main\n\nimport (\n\t\"bufio\"\n\t\"encoding/json\"\n\t\"fmt\"\n\t\"net\"\n\t\"strconv\"\n)\n\nvar conn net.Conn\n\nfunc (result ActionResult) send() {\n\tdata, err := result.Json()\n\tif err != nil {\n\t\treturn\n\t}\n\tsend(data)\n}\n\nfunc sendMessage(message Message) {\n\tresult := ActionResult{\n\t\tMethod: messageMethod,\n\t\tData:   message,\n\t}\n\tresult.send()\n}\n\nfunc send(data []byte) {\n\tif conn == nil {\n\t\treturn\n\t}\n\t_, _ = conn.Write(append(data, []byte(\"\\n\")...))\n}\n\nfunc startServer(arg string) {\n\n\t_, err := strconv.Atoi(arg)\n\n\tif err != nil {\n\t\tconn, err = net.Dial(\"unix\", arg)\n\t} else {\n\t\tconn, err = net.Dial(\"tcp\", fmt.Sprintf(\"127.0.0.1:%s\", arg))\n\t}\n\tif err != nil {\n\t\tpanic(err.Error())\n\t}\n\n\tdefer func(conn net.Conn) {\n\t\t_ = conn.Close()\n\t}(conn)\n\n\treader := bufio.NewReader(conn)\n\n\tfor {\n\t\tdata, err := reader.ReadString('\\n')\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\t\tvar action = &Action{}\n\n\t\terr = json.Unmarshal([]byte(data), action)\n\n\t\tif err != nil {\n\t\t\treturn\n\t\t}\n\n\t\tresult := ActionResult{\n\t\t\tId:     action.Id,\n\t\t\tMethod: action.Method,\n\t\t}\n\n\t\tgo handleAction(action, result)\n\t}\n}\n\nfunc nextHandle(action *Action, result ActionResult) bool {\n\treturn false\n}\n"
  },
  {
    "path": "core/state/state.go",
    "content": "package state\n\nimport \"net/netip\"\n\nvar DefaultIpv4Address = \"198.51.100.1/30\"\nvar DefaultDnsAddress = \"198.51.100.2\"\nvar DefaultIpv6Address = \"fdfe:dcba:9876::1/126\"\n\ntype AndroidVpnOptions struct {\n\tEnable                bool           `json:\"enable\"`\n\tPort                  int            `json:\"port\"`\n\tAccessControl         *AccessControl `json:\"accessControl\"`\n\tAllowBypass           bool           `json:\"allowBypass\"`\n\tSystemProxy           bool           `json:\"systemProxy\"`\n\tBypassDomain          []string       `json:\"bypassDomain\"`\n\tRouteAddress          []netip.Prefix `json:\"routeAddress\"`\n\tRouteMode             string         `json:\"routeMode\"`\n\tIpv4Address           string         `json:\"ipv4Address\"`\n\tIpv6Address           string         `json:\"ipv6Address\"`\n\tDnsServerAddress      string         `json:\"dnsServerAddress\"`\n\tDozeSuspend           bool           `json:\"dozeSuspend\"`\n\tDisableIcmpForwarding bool           `json:\"disableIcmpForwarding\"`\n\tMtu                   uint32         `json:\"mtu\"`\n}\n\ntype AccessControl struct {\n\tEnable            bool     `json:\"enable\"`\n\tMode              string   `json:\"mode\"`\n\tAcceptList        []string `json:\"acceptList\"`\n\tRejectList        []string `json:\"rejectList\"`\n}\n\ntype AndroidVpnRawOptions struct {\n\tEnable        bool           `json:\"enable\"`\n\tAccessControl *AccessControl `json:\"accessControl\"`\n\tAllowBypass   bool           `json:\"allowBypass\"`\n\tSystemProxy   bool           `json:\"systemProxy\"`\n\tRouteMode     string         `json:\"routeMode\"`\n\tDozeSuspend   bool           `json:\"dozeSuspend\"`\n}\n\ntype State struct {\n\tVpnProps            AndroidVpnRawOptions `json:\"vpn-props\"`\n\tCurrentProfileName  string               `json:\"current-profile-name\"`\n\tOnlyStatisticsProxy bool                 `json:\"only-statistics-proxy\"`\n\tBypassDomain        []string             `json:\"bypass-domain\"`\n}\n\nvar CurrentState = &State{\n\tOnlyStatisticsProxy: false,\n\tCurrentProfileName:  \"\",\n}\n\n\n\nfunc GetDnsServerAddress() string {\n\treturn DefaultDnsAddress\n}\n"
  },
  {
    "path": "core/tun/tun.go",
    "content": "//go:build android && cgo\n\npackage tun\n\nimport \"C\"\nimport (\n\t\"core/state\"\n\t\"github.com/metacubex/mihomo/constant\"\n\tLC \"github.com/metacubex/mihomo/listener/config\"\n\t\"github.com/metacubex/mihomo/listener/sing_tun\"\n\t\"github.com/metacubex/mihomo/log\"\n\t\"github.com/metacubex/mihomo/tunnel\"\n\t\"net\"\n\t\"net/netip\"\n)\n\ntype Props struct {\n\tFd       int    `json:\"fd\"`\n\tGateway  string `json:\"gateway\"`\n\tGateway6 string `json:\"gateway6\"`\n\tPortal   string `json:\"portal\"`\n\tPortal6  string `json:\"portal6\"`\n\tDns      string `json:\"dns\"`\n\tDns6     string `json:\"dns6\"`\n}\n\nfunc Start(fd int, device string, stack constant.TUNStack, disableIcmpForwarding bool, mtu uint32, ipv6Enabled bool) (*sing_tun.Listener, error) {\n\tvar prefix4 []netip.Prefix\n\ttempPrefix4, err := netip.ParsePrefix(state.DefaultIpv4Address)\n\tif err != nil {\n\t\tlog.Errorln(\"startTUN error:\", err)\n\t\treturn nil, err\n\t}\n\tprefix4 = append(prefix4, tempPrefix4)\n\tvar prefix6 []netip.Prefix\n\tif ipv6Enabled {\n\t\ttempPrefix6, err := netip.ParsePrefix(state.DefaultIpv6Address)\n\t\tif err != nil {\n\t\t\tlog.Errorln(\"startTUN error:\", err)\n\t\t\treturn nil, err\n\t\t}\n\t\tprefix6 = append(prefix6, tempPrefix6)\n\t}\n\n\tvar dnsHijack []string\n\tdnsHijack = append(dnsHijack, net.JoinHostPort(state.GetDnsServerAddress(), \"53\"))\n\n\tvalidMtu := mtu\n\tif validMtu < 1280 || validMtu > 65535 {\n\t\tvalidMtu = 1480\n\t}\n\n\toptions := LC.Tun{\n\t\tEnable:                true,\n\t\tDevice:                device,\n\t\tStack:                 stack,\n\t\tDNSHijack:             dnsHijack,\n\t\tAutoRoute:             false,\n\t\tAutoDetectInterface:   false,\n\t\tInet4Address:          prefix4,\n\t\tInet6Address:          prefix6,\n\t\tMTU:                   validMtu,\n\t\tFileDescriptor:        fd,\n\t\tDisableICMPForwarding: disableIcmpForwarding,\n\t}\n\n\tlistener, err := sing_tun.New(options, tunnel.Tunnel)\n\n\tif err != nil {\n\t\tlog.Errorln(\"startTUN error:\", err)\n\t\treturn nil, err\n\t}\n\n\treturn listener, nil\n}\n"
  },
  {
    "path": "devtools_options.yaml",
    "content": "description: This file stores settings for Dart & Flutter DevTools.\ndocumentation: https://docs.flutter.dev/tools/devtools/extensions#configure-extension-enablement-states\nextensions:\n"
  },
  {
    "path": "distribute_options.yaml",
    "content": "app_name: 'Bettbox'\noutput: 'dist/'\n"
  },
  {
    "path": "lib/application.dart",
    "content": "import 'dart:async';\n\nimport 'package:connectivity_plus/connectivity_plus.dart';\nimport 'package:bett_box/clash/clash.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/l10n/l10n.dart';\nimport 'package:bett_box/manager/hotkey_manager.dart';\nimport 'package:bett_box/manager/manager.dart';\nimport 'package:bett_box/plugins/app.dart';\nimport 'package:bett_box/providers/providers.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_displaymode/flutter_displaymode.dart';\nimport 'package:flutter_localizations/flutter_localizations.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nimport 'controller.dart';\nimport 'pages/pages.dart';\n\nclass Application extends ConsumerStatefulWidget {\n  const Application({super.key});\n\n  @override\n  ConsumerState<Application> createState() => ApplicationState();\n}\n\nclass ApplicationState extends ConsumerState<Application>\n    with WidgetsBindingObserver {\n  Timer? _autoUpdateGroupTaskTimer;\n  Timer? _autoUpdateProfilesTaskTimer;\n\n  final _pageTransitionsTheme = const PageTransitionsTheme(\n    builders: <TargetPlatform, PageTransitionsBuilder>{\n      TargetPlatform.android: CupertinoPageTransitionsBuilder(),\n      TargetPlatform.windows: CupertinoPageTransitionsBuilder(),\n      TargetPlatform.linux: CupertinoPageTransitionsBuilder(),\n      TargetPlatform.macOS: CupertinoPageTransitionsBuilder(),\n    },\n  );\n\n  ColorScheme _getAppColorScheme({\n    required Brightness brightness,\n    int? primaryColor,\n  }) {\n    return ref.read(genColorSchemeProvider(brightness));\n  }\n\n  @override\n  void initState() {\n    super.initState();\n    WidgetsBinding.instance.addObserver(this);\n    globalState.backgroundMode.addListener(_syncAutoUpdateTasks);\n    _syncAutoUpdateTasks();\n    globalState.appController = AppController(context, ref);\n    WidgetsBinding.instance.addPostFrameCallback((_) {\n      unawaited(_initApp());\n    });\n  }\n\n  bool get _isForeground {\n    final lifecycleState = WidgetsBinding.instance.lifecycleState;\n    return lifecycleState == null ||\n        lifecycleState == AppLifecycleState.resumed;\n  }\n\n  Future<void> _initApp() async {\n    final currentContext = globalState.navigatorKey.currentContext;\n    if (currentContext != null && currentContext != context) {\n      globalState.appController = AppController(currentContext, ref);\n    }\n    await globalState.appController.init();\n    globalState.appController.initLink();\n    if (system.isAndroid) {\n      app.initShortcuts();\n    }\n  }\n\n  @override\n  void didChangeAppLifecycleState(AppLifecycleState state) {\n    _syncAutoUpdateTasks();\n    if (state == AppLifecycleState.resumed) {\n      if (system.isAndroid &&\n          globalState.config.appSetting.enableHighRefreshRate) {\n        _restoreHighRefreshRate();\n      }\n    }\n  }\n\n  void _syncAutoUpdateTasks() {\n    final shouldRun = _isForeground && !globalState.backgroundMode.value;\n    if (!shouldRun) {\n      _autoUpdateGroupTaskTimer?.cancel();\n      _autoUpdateGroupTaskTimer = null;\n      return;\n    }\n    if (_autoUpdateGroupTaskTimer == null) {\n      _autoUpdateGroupTask();\n    }\n    if (_autoUpdateProfilesTaskTimer == null) {\n      _autoUpdateProfilesTask();\n    }\n  }\n\n  Future<void> _restoreHighRefreshRate() async {\n    try {\n      await FlutterDisplayMode.setHighRefreshRate();\n    } catch (e) {\n      commonPrint.log('Failed to restore high refresh rate: $e');\n    }\n  }\n\n  void _autoUpdateGroupTask() {\n    _autoUpdateGroupTaskTimer = Timer.periodic(\n      const Duration(seconds: 60),\n      (_) => globalState.appController.updateGroupsDebounce(),\n    );\n  }\n\n  void _autoUpdateProfilesTask() {\n    _autoUpdateProfilesTaskTimer = Timer.periodic(\n      const Duration(hours: 24),\n      (_) => unawaited(globalState.appController.autoUpdateProfiles()),\n    );\n  }\n\n  Widget _buildPlatformState(Widget child) {\n    if (system.isDesktop) {\n      return WindowManager(\n        child: TrayManager(\n          child: HotKeyManager(\n            child: ProxyManager(child: SmartAutoStopManager(child: child)),\n          ),\n        ),\n      );\n    }\n    return AndroidManager(\n      child: TileManager(child: SmartAutoStopManager(child: child)),\n    );\n  }\n\n  Widget _buildState(Widget child) {\n    return AppStateManager(\n      child: ClashManager(\n        child: ConnectivityManager(\n          onConnectivityChanged: (results) async {\n            if (!results.contains(ConnectivityResult.vpn)) {\n              clashCore.closeConnections();\n            }\n            globalState.appController.updateLocalIp();\n            globalState.appController.addCheckIpNumDebounce();\n          },\n          child: child,\n        ),\n      ),\n    );\n  }\n\n  Widget _buildPlatformApp(Widget child) {\n    if (system.isDesktop) {\n      return WindowHeaderContainer(child: child);\n    }\n    return VpnManager(child: child);\n  }\n\n  Widget _buildApp(Widget child) {\n    return MessageManager(child: ThemeManager(child: child));\n  }\n\n  @override\n  Widget build(context) {\n    return _buildPlatformState(\n      _buildState(\n        Consumer(\n          builder: (_, ref, child) {\n            final locale = ref.watch(\n              appSettingProvider.select((state) => state.locale),\n            );\n            final themeProps = ref.watch(themeSettingProvider);\n            final fontFamily = themeProps.useHarmonyFont\n                ? 'HarmonyOS_Sans'\n                : null;\n\n            return MaterialApp(\n              debugShowCheckedModeBanner: false,\n              navigatorKey: globalState.navigatorKey,\n              localizationsDelegates: const [\n                AppLocalizations.delegate,\n                GlobalMaterialLocalizations.delegate,\n                GlobalCupertinoLocalizations.delegate,\n                GlobalWidgetsLocalizations.delegate,\n              ],\n              builder: (_, child) {\n                return ValueListenableBuilder<bool>(\n                  valueListenable: globalState.animationEnabled,\n                  builder: (_, enabled, _) {\n                    return TickerMode(\n                      enabled: enabled,\n                      child: AppEnvManager(\n                        child: _buildApp(\n                          AppSidebarContainer(child: _buildPlatformApp(child!)),\n                        ),\n                      ),\n                    );\n                  },\n                );\n              },\n              scrollBehavior: BaseScrollBehavior(),\n              title: appName,\n              locale:\n                  utils.getLocaleForString(locale) ?? utils.getSystemLocale(),\n              supportedLocales: AppLocalizations.delegate.supportedLocales,\n              themeMode: themeProps.themeMode,\n              theme: ThemeData(\n                useMaterial3: true,\n                pageTransitionsTheme: _pageTransitionsTheme,\n                colorScheme: _getAppColorScheme(\n                  brightness: Brightness.light,\n                  primaryColor: themeProps.primaryColor,\n                ),\n                fontFamily: fontFamily,\n              ),\n              darkTheme: ThemeData(\n                useMaterial3: true,\n                pageTransitionsTheme: _pageTransitionsTheme,\n                colorScheme: _getAppColorScheme(\n                  brightness: Brightness.dark,\n                  primaryColor: themeProps.primaryColor,\n                ).toPureBlack(themeProps.pureBlack),\n                fontFamily: fontFamily,\n              ),\n              home: child!,\n            );\n          },\n          child: const HomePage(),\n        ),\n      ),\n    );\n  }\n\n  @override\n  void dispose() {\n    globalState.backgroundMode.removeListener(_syncAutoUpdateTasks);\n    WidgetsBinding.instance.removeObserver(this);\n    linkManager.destroy();\n    _autoUpdateGroupTaskTimer?.cancel();\n    _autoUpdateProfilesTaskTimer?.cancel();\n    if (!system.isAndroid && !globalState.isExiting) {\n      unawaited(globalState.appController.handleExit());\n    }\n    super.dispose();\n  }\n}\n"
  },
  {
    "path": "lib/clash/clash.dart",
    "content": "export 'core.dart';\nexport 'lib.dart';\nexport 'message.dart';\nexport 'service.dart';\n"
  },
  {
    "path": "lib/clash/core.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\nimport 'dart:io';\nimport 'dart:isolate';\n\nimport 'package:bett_box/clash/clash.dart';\nimport 'package:bett_box/clash/interface.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:flutter/services.dart';\nimport 'package:path/path.dart';\n\nclass ClashCore {\n  static ClashCore? _instance;\n  late ClashHandlerInterface clashInterface;\n\n  ClashCore._internal() {\n    if (system.isAndroid) {\n      clashInterface = clashLib!;\n    } else {\n      clashInterface = clashService!;\n    }\n  }\n\n  factory ClashCore() {\n    _instance ??= ClashCore._internal();\n    return _instance!;\n  }\n\n  Future<bool> preload() {\n    return clashInterface.preload();\n  }\n\n  static Future<void> initGeo() async {\n    final homePath = await appPath.homeDirPath;\n    final homeDir = Directory(homePath);\n    if (!await homeDir.exists()) {\n      await homeDir.create(recursive: true);\n    }\n    const geoFileNameList = [mmdbFileName, geoSiteFileName, asnFileName];\n    try {\n      for (final geoFileName in geoFileNameList) {\n        final geoFile = File(join(homePath, geoFileName));\n        if (await geoFile.exists()) continue;\n        final data = await rootBundle.load('assets/data/$geoFileName');\n        await geoFile.writeAsBytes(data.buffer.asUint8List(), flush: true);\n      }\n    } catch (e) {\n      exit(0);\n    }\n  }\n\n  Future<bool> init() async {\n    await initGeo();\n    if (globalState.config.appSetting.openLogs) {\n      clashCore.startLog();\n    } else {\n      clashCore.stopLog();\n    }\n    final homeDirPath = await appPath.homeDirPath;\n    return await clashInterface.init(\n      InitParams(homeDir: homeDirPath, version: globalState.appState.version),\n    );\n  }\n\n  Future<bool> setState(CoreState state) async {\n    return await clashInterface.setState(state);\n  }\n\n  Future<void> shutdown() async {\n    await clashInterface.shutdown();\n  }\n\n  FutureOr<bool> get isInit => clashInterface.isInit;\n\n  FutureOr<String> validateConfig(String data) {\n    return clashInterface.validateConfig(data);\n  }\n\n  Future<String> updateConfig(UpdateParams updateParams) async {\n    return await clashInterface.updateConfig(updateParams);\n  }\n\n  Future<String> setupConfig(SetupParams setupParams) async {\n    return await clashInterface.setupConfig(setupParams);\n  }\n\n  Future<List<Group>> getProxiesGroups() async {\n    final proxies = await clashInterface.getProxies();\n    if (proxies.isEmpty) return [];\n\n    return Isolate.run<List<Group>>(() {\n      final groupNames = [\n        UsedProxy.GLOBAL.name,\n        ...(proxies[UsedProxy.GLOBAL.name]['all'] as List).where((e) {\n          final proxy = proxies[e] as Map<String, dynamic>?;\n          return GroupTypeExtension.valueList.contains(proxy?['type']);\n        }),\n      ];\n      final groupsRaw = groupNames.map((groupName) {\n        final group = Map<String, dynamic>.from(\n          (proxies[groupName] as Map).cast<String, dynamic>(),\n        );\n        group['all'] = ((group['all'] ?? []) as List)\n            .map((name) => proxies[name])\n            .whereType<Map<String, dynamic>>()\n            .toList();\n        return group;\n      }).toList();\n      return groupsRaw.map((e) => Group.fromJson(e)).toList();\n    });\n  }\n\n  FutureOr<String> changeProxy(ChangeProxyParams changeProxyParams) async {\n    return await clashInterface.changeProxy(changeProxyParams);\n  }\n\n  Future<List<TrackerInfo>> getConnections() async {\n    final res = await clashInterface.getConnections();\n    if (res.isEmpty) {\n      return [];\n    }\n    try {\n      final connectionsData = json.decode(res) as Map;\n      final connectionsRaw = connectionsData['connections'] as List? ?? [];\n      return connectionsRaw.map((e) => TrackerInfo.fromJson(e)).toList();\n    } catch (e) {\n      commonPrint.log('Failed to parse connections: $e');\n      return [];\n    }\n  }\n\n  void closeConnection(String id) {\n    clashInterface.closeConnection(id);\n  }\n\n  void closeConnections() {\n    clashInterface.closeConnections();\n  }\n\n  void resetConnections() {\n    clashInterface.resetConnections();\n  }\n\n  Future<List<ExternalProvider>> getExternalProviders() async {\n    final externalProvidersRawString = await clashInterface\n        .getExternalProviders();\n    if (externalProvidersRawString.isEmpty) {\n      return [];\n    }\n    try {\n      return Isolate.run<List<ExternalProvider>>(() {\n        final externalProviders =\n            (json.decode(externalProvidersRawString) as List<dynamic>)\n                .map((item) => ExternalProvider.fromJson(item))\n                .toList();\n        return externalProviders;\n      });\n    } catch (e) {\n      commonPrint.log('Failed to parse external providers: $e');\n      return [];\n    }\n  }\n\n  Future<ExternalProvider?> getExternalProvider(\n    String externalProviderName,\n  ) async {\n    final externalProvidersRawString = await clashInterface.getExternalProvider(\n      externalProviderName,\n    );\n    if (externalProvidersRawString.isEmpty) {\n      return null;\n    }\n    try {\n      return ExternalProvider.fromJson(json.decode(externalProvidersRawString));\n    } catch (e) {\n      commonPrint.log('Failed to parse external provider: $e');\n      return null;\n    }\n  }\n\n  Future<String> updateGeoData(UpdateGeoDataParams params) {\n    return clashInterface.updateGeoData(params);\n  }\n\n  Future<String> sideLoadExternalProvider({\n    required String providerName,\n    required String data,\n  }) {\n    return clashInterface.sideLoadExternalProvider(\n      providerName: providerName,\n      data: data,\n    );\n  }\n\n  Future<String> updateExternalProvider({required String providerName}) async {\n    return clashInterface.updateExternalProvider(providerName);\n  }\n\n  Future<void> startListener() async {\n    await clashInterface.startListener();\n  }\n\n  Future<void> stopListener() async {\n    await clashInterface.stopListener();\n  }\n\n  Future<Delay> getDelay(String url, String proxyName) async {\n    final data = await clashInterface.asyncTestDelay(url, proxyName);\n    if (data.isEmpty) {\n      throw Exception('Empty delay response');\n    }\n    try {\n      return Delay.fromJson(json.decode(data));\n    } catch (e) {\n      commonPrint.log('Failed to parse delay: $e');\n      rethrow;\n    }\n  }\n\n  Future<Map<String, dynamic>> getConfig(String id) async {\n    final profilePath = await appPath.getProfilePath(id);\n    final res = await clashInterface.getConfig(profilePath);\n    if (res.isSuccess) {\n      return res.data as Map<String, dynamic>;\n    } else {\n      throw res.message;\n    }\n  }\n\n  Future<Traffic> getTraffic() async {\n    final trafficString = await clashInterface.getTraffic();\n    if (trafficString.isEmpty) {\n      return Traffic();\n    }\n    try {\n      return Traffic.fromMap(json.decode(trafficString));\n    } catch (e) {\n      commonPrint.log('Failed to parse traffic: $e');\n      return Traffic();\n    }\n  }\n\n  Future<IpInfo?> getCountryCode(String ip) async {\n    final countryCode = await clashInterface.getCountryCode(ip);\n    if (countryCode.isEmpty) {\n      return null;\n    }\n    return IpInfo(ip: ip, countryCode: countryCode);\n  }\n\n  Future<Traffic> getTotalTraffic() async {\n    final totalTrafficString = await clashInterface.getTotalTraffic();\n    if (totalTrafficString.isEmpty) {\n      return Traffic();\n    }\n    try {\n      return Traffic.fromMap(json.decode(totalTrafficString));\n    } catch (e) {\n      commonPrint.log('Failed to parse total traffic: $e');\n      return Traffic();\n    }\n  }\n\n  Future<int> getMemory() async {\n    final value = await clashInterface.getMemory();\n    if (value.isEmpty) {\n      return 0;\n    }\n    return int.parse(value);\n  }\n\n  void resetTraffic() {\n    clashInterface.resetTraffic();\n  }\n\n  void startLog() {\n    clashInterface.startLog();\n  }\n\n  void stopLog() {\n    clashInterface.stopLog();\n  }\n\n  Future<void> requestGc() async {\n    await clashInterface.forceGc();\n  }\n\n  Future<void> flushFakeIP() async {\n    await clashInterface.flushFakeIP();\n  }\n\n  Future<void> flushDnsCache() async {\n    await clashInterface.flushDnsCache();\n  }\n\n  Future<void> destroy() async {\n    await clashInterface.destroy();\n  }\n}\n\nfinal clashCore = ClashCore();"
  },
  {
    "path": "lib/clash/generated/clash_ffi.dart",
    "content": "// AUTO GENERATED FILE, DO NOT EDIT.\n//\n// Generated by `package:ffigen`.\n// ignore_for_file: type=lint\nimport 'dart:ffi' as ffi;\n\nclass ClashFFI {\n  /// Holds the symbol lookup function.\n  final ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName)\n  _lookup;\n\n  /// The symbols are looked up in [dynamicLibrary].\n  ClashFFI(ffi.DynamicLibrary dynamicLibrary) : _lookup = dynamicLibrary.lookup;\n\n  /// The symbols are looked up with [lookup].\n  ClashFFI.fromLookup(\n    ffi.Pointer<T> Function<T extends ffi.NativeType>(String symbolName) lookup,\n  ) : _lookup = lookup;\n\n  ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Int)>> signal(\n    int arg0,\n    ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Int)>> arg1,\n  ) {\n    return _signal(arg0, arg1);\n  }\n\n  late final _signalPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Int)>> Function(\n            ffi.Int,\n            ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Int)>>,\n          )\n        >\n      >('signal');\n  late final _signal = _signalPtr\n      .asFunction<\n        ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Int)>> Function(\n          int,\n          ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Int)>>,\n        )\n      >();\n\n  int getpriority(int arg0, int arg1) {\n    return _getpriority(arg0, arg1);\n  }\n\n  late final _getpriorityPtr =\n      _lookup<ffi.NativeFunction<ffi.Int Function(ffi.Int, id_t)>>(\n        'getpriority',\n      );\n  late final _getpriority = _getpriorityPtr\n      .asFunction<int Function(int, int)>();\n\n  int getiopolicy_np(int arg0, int arg1) {\n    return _getiopolicy_np(arg0, arg1);\n  }\n\n  late final _getiopolicy_npPtr =\n      _lookup<ffi.NativeFunction<ffi.Int Function(ffi.Int, ffi.Int)>>(\n        'getiopolicy_np',\n      );\n  late final _getiopolicy_np = _getiopolicy_npPtr\n      .asFunction<int Function(int, int)>();\n\n  int getrlimit(int arg0, ffi.Pointer<rlimit> arg1) {\n    return _getrlimit(arg0, arg1);\n  }\n\n  late final _getrlimitPtr =\n      _lookup<\n        ffi.NativeFunction<ffi.Int Function(ffi.Int, ffi.Pointer<rlimit>)>\n      >('getrlimit');\n  late final _getrlimit = _getrlimitPtr\n      .asFunction<int Function(int, ffi.Pointer<rlimit>)>();\n\n  int getrusage(int arg0, ffi.Pointer<rusage> arg1) {\n    return _getrusage(arg0, arg1);\n  }\n\n  late final _getrusagePtr =\n      _lookup<\n        ffi.NativeFunction<ffi.Int Function(ffi.Int, ffi.Pointer<rusage>)>\n      >('getrusage');\n  late final _getrusage = _getrusagePtr\n      .asFunction<int Function(int, ffi.Pointer<rusage>)>();\n\n  int setpriority(int arg0, int arg1, int arg2) {\n    return _setpriority(arg0, arg1, arg2);\n  }\n\n  late final _setpriorityPtr =\n      _lookup<ffi.NativeFunction<ffi.Int Function(ffi.Int, id_t, ffi.Int)>>(\n        'setpriority',\n      );\n  late final _setpriority = _setpriorityPtr\n      .asFunction<int Function(int, int, int)>();\n\n  int setiopolicy_np(int arg0, int arg1, int arg2) {\n    return _setiopolicy_np(arg0, arg1, arg2);\n  }\n\n  late final _setiopolicy_npPtr =\n      _lookup<ffi.NativeFunction<ffi.Int Function(ffi.Int, ffi.Int, ffi.Int)>>(\n        'setiopolicy_np',\n      );\n  late final _setiopolicy_np = _setiopolicy_npPtr\n      .asFunction<int Function(int, int, int)>();\n\n  int setrlimit(int arg0, ffi.Pointer<rlimit> arg1) {\n    return _setrlimit(arg0, arg1);\n  }\n\n  late final _setrlimitPtr =\n      _lookup<\n        ffi.NativeFunction<ffi.Int Function(ffi.Int, ffi.Pointer<rlimit>)>\n      >('setrlimit');\n  late final _setrlimit = _setrlimitPtr\n      .asFunction<int Function(int, ffi.Pointer<rlimit>)>();\n\n  int wait$1(ffi.Pointer<ffi.Int> arg0) {\n    return _wait$1(arg0);\n  }\n\n  late final _wait$1Ptr =\n      _lookup<ffi.NativeFunction<pid_t Function(ffi.Pointer<ffi.Int>)>>('wait');\n  late final _wait$1 = _wait$1Ptr\n      .asFunction<int Function(ffi.Pointer<ffi.Int>)>();\n\n  int waitpid(int arg0, ffi.Pointer<ffi.Int> arg1, int arg2) {\n    return _waitpid(arg0, arg1, arg2);\n  }\n\n  late final _waitpidPtr =\n      _lookup<\n        ffi.NativeFunction<pid_t Function(pid_t, ffi.Pointer<ffi.Int>, ffi.Int)>\n      >('waitpid');\n  late final _waitpid = _waitpidPtr\n      .asFunction<int Function(int, ffi.Pointer<ffi.Int>, int)>();\n\n  int waitid(\n    idtype_t arg0,\n    Dart__uint32_t arg1,\n    ffi.Pointer<siginfo_t> arg2,\n    int arg3,\n  ) {\n    return _waitid(arg0.value, arg1, arg2, arg3);\n  }\n\n  late final _waitidPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Int Function(\n            ffi.UnsignedInt,\n            id_t,\n            ffi.Pointer<siginfo_t>,\n            ffi.Int,\n          )\n        >\n      >('waitid');\n  late final _waitid = _waitidPtr\n      .asFunction<int Function(int, int, ffi.Pointer<siginfo_t>, int)>();\n\n  int wait3(ffi.Pointer<ffi.Int> arg0, int arg1, ffi.Pointer<rusage> arg2) {\n    return _wait3(arg0, arg1, arg2);\n  }\n\n  late final _wait3Ptr =\n      _lookup<\n        ffi.NativeFunction<\n          pid_t Function(ffi.Pointer<ffi.Int>, ffi.Int, ffi.Pointer<rusage>)\n        >\n      >('wait3');\n  late final _wait3 = _wait3Ptr\n      .asFunction<\n        int Function(ffi.Pointer<ffi.Int>, int, ffi.Pointer<rusage>)\n      >();\n\n  int wait4(\n    int arg0,\n    ffi.Pointer<ffi.Int> arg1,\n    int arg2,\n    ffi.Pointer<rusage> arg3,\n  ) {\n    return _wait4(arg0, arg1, arg2, arg3);\n  }\n\n  late final _wait4Ptr =\n      _lookup<\n        ffi.NativeFunction<\n          pid_t Function(\n            pid_t,\n            ffi.Pointer<ffi.Int>,\n            ffi.Int,\n            ffi.Pointer<rusage>,\n          )\n        >\n      >('wait4');\n  late final _wait4 = _wait4Ptr\n      .asFunction<\n        int Function(int, ffi.Pointer<ffi.Int>, int, ffi.Pointer<rusage>)\n      >();\n\n  ffi.Pointer<ffi.Void> alloca(int arg0) {\n    return _alloca(arg0);\n  }\n\n  late final _allocaPtr =\n      _lookup<ffi.NativeFunction<ffi.Pointer<ffi.Void> Function(ffi.Size)>>(\n        'alloca',\n      );\n  late final _alloca = _allocaPtr\n      .asFunction<ffi.Pointer<ffi.Void> Function(int)>();\n\n  late final ffi.Pointer<ffi.Int> ___mb_cur_max = _lookup<ffi.Int>(\n    '__mb_cur_max',\n  );\n\n  int get __mb_cur_max => ___mb_cur_max.value;\n\n  set __mb_cur_max(int value) => ___mb_cur_max.value = value;\n\n  ffi.Pointer<ffi.Void> malloc_type_malloc(int size, int type_id) {\n    return _malloc_type_malloc(size, type_id);\n  }\n\n  late final _malloc_type_mallocPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.Void> Function(ffi.Size, malloc_type_id_t)\n        >\n      >('malloc_type_malloc');\n  late final _malloc_type_malloc = _malloc_type_mallocPtr\n      .asFunction<ffi.Pointer<ffi.Void> Function(int, int)>();\n\n  ffi.Pointer<ffi.Void> malloc_type_calloc(int count, int size, int type_id) {\n    return _malloc_type_calloc(count, size, type_id);\n  }\n\n  late final _malloc_type_callocPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.Void> Function(ffi.Size, ffi.Size, malloc_type_id_t)\n        >\n      >('malloc_type_calloc');\n  late final _malloc_type_calloc = _malloc_type_callocPtr\n      .asFunction<ffi.Pointer<ffi.Void> Function(int, int, int)>();\n\n  void malloc_type_free(ffi.Pointer<ffi.Void> ptr, int type_id) {\n    return _malloc_type_free(ptr, type_id);\n  }\n\n  late final _malloc_type_freePtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Void Function(ffi.Pointer<ffi.Void>, malloc_type_id_t)\n        >\n      >('malloc_type_free');\n  late final _malloc_type_free = _malloc_type_freePtr\n      .asFunction<void Function(ffi.Pointer<ffi.Void>, int)>();\n\n  ffi.Pointer<ffi.Void> malloc_type_realloc(\n    ffi.Pointer<ffi.Void> ptr,\n    int size,\n    int type_id,\n  ) {\n    return _malloc_type_realloc(ptr, size, type_id);\n  }\n\n  late final _malloc_type_reallocPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.Void> Function(\n            ffi.Pointer<ffi.Void>,\n            ffi.Size,\n            malloc_type_id_t,\n          )\n        >\n      >('malloc_type_realloc');\n  late final _malloc_type_realloc = _malloc_type_reallocPtr\n      .asFunction<\n        ffi.Pointer<ffi.Void> Function(ffi.Pointer<ffi.Void>, int, int)\n      >();\n\n  ffi.Pointer<ffi.Void> malloc_type_valloc(int size, int type_id) {\n    return _malloc_type_valloc(size, type_id);\n  }\n\n  late final _malloc_type_vallocPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.Void> Function(ffi.Size, malloc_type_id_t)\n        >\n      >('malloc_type_valloc');\n  late final _malloc_type_valloc = _malloc_type_vallocPtr\n      .asFunction<ffi.Pointer<ffi.Void> Function(int, int)>();\n\n  ffi.Pointer<ffi.Void> malloc_type_aligned_alloc(\n    int alignment,\n    int size,\n    int type_id,\n  ) {\n    return _malloc_type_aligned_alloc(alignment, size, type_id);\n  }\n\n  late final _malloc_type_aligned_allocPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.Void> Function(ffi.Size, ffi.Size, malloc_type_id_t)\n        >\n      >('malloc_type_aligned_alloc');\n  late final _malloc_type_aligned_alloc = _malloc_type_aligned_allocPtr\n      .asFunction<ffi.Pointer<ffi.Void> Function(int, int, int)>();\n\n  int malloc_type_posix_memalign(\n    ffi.Pointer<ffi.Pointer<ffi.Void>> memptr,\n    int alignment,\n    int size,\n    int type_id,\n  ) {\n    return _malloc_type_posix_memalign(memptr, alignment, size, type_id);\n  }\n\n  late final _malloc_type_posix_memalignPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Int Function(\n            ffi.Pointer<ffi.Pointer<ffi.Void>>,\n            ffi.Size,\n            ffi.Size,\n            malloc_type_id_t,\n          )\n        >\n      >('malloc_type_posix_memalign');\n  late final _malloc_type_posix_memalign = _malloc_type_posix_memalignPtr\n      .asFunction<\n        int Function(ffi.Pointer<ffi.Pointer<ffi.Void>>, int, int, int)\n      >();\n\n  ffi.Pointer<ffi.Void> malloc_type_zone_malloc(\n    ffi.Pointer<malloc_zone_t> zone,\n    int size,\n    int type_id,\n  ) {\n    return _malloc_type_zone_malloc(zone, size, type_id);\n  }\n\n  late final _malloc_type_zone_mallocPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.Void> Function(\n            ffi.Pointer<malloc_zone_t>,\n            ffi.Size,\n            malloc_type_id_t,\n          )\n        >\n      >('malloc_type_zone_malloc');\n  late final _malloc_type_zone_malloc = _malloc_type_zone_mallocPtr\n      .asFunction<\n        ffi.Pointer<ffi.Void> Function(ffi.Pointer<malloc_zone_t>, int, int)\n      >();\n\n  ffi.Pointer<ffi.Void> malloc_type_zone_calloc(\n    ffi.Pointer<malloc_zone_t> zone,\n    int count,\n    int size,\n    int type_id,\n  ) {\n    return _malloc_type_zone_calloc(zone, count, size, type_id);\n  }\n\n  late final _malloc_type_zone_callocPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.Void> Function(\n            ffi.Pointer<malloc_zone_t>,\n            ffi.Size,\n            ffi.Size,\n            malloc_type_id_t,\n          )\n        >\n      >('malloc_type_zone_calloc');\n  late final _malloc_type_zone_calloc = _malloc_type_zone_callocPtr\n      .asFunction<\n        ffi.Pointer<ffi.Void> Function(\n          ffi.Pointer<malloc_zone_t>,\n          int,\n          int,\n          int,\n        )\n      >();\n\n  void malloc_type_zone_free(\n    ffi.Pointer<malloc_zone_t> zone,\n    ffi.Pointer<ffi.Void> ptr,\n    int type_id,\n  ) {\n    return _malloc_type_zone_free(zone, ptr, type_id);\n  }\n\n  late final _malloc_type_zone_freePtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Void Function(\n            ffi.Pointer<malloc_zone_t>,\n            ffi.Pointer<ffi.Void>,\n            malloc_type_id_t,\n          )\n        >\n      >('malloc_type_zone_free');\n  late final _malloc_type_zone_free = _malloc_type_zone_freePtr\n      .asFunction<\n        void Function(ffi.Pointer<malloc_zone_t>, ffi.Pointer<ffi.Void>, int)\n      >();\n\n  ffi.Pointer<ffi.Void> malloc_type_zone_realloc(\n    ffi.Pointer<malloc_zone_t> zone,\n    ffi.Pointer<ffi.Void> ptr,\n    int size,\n    int type_id,\n  ) {\n    return _malloc_type_zone_realloc(zone, ptr, size, type_id);\n  }\n\n  late final _malloc_type_zone_reallocPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.Void> Function(\n            ffi.Pointer<malloc_zone_t>,\n            ffi.Pointer<ffi.Void>,\n            ffi.Size,\n            malloc_type_id_t,\n          )\n        >\n      >('malloc_type_zone_realloc');\n  late final _malloc_type_zone_realloc = _malloc_type_zone_reallocPtr\n      .asFunction<\n        ffi.Pointer<ffi.Void> Function(\n          ffi.Pointer<malloc_zone_t>,\n          ffi.Pointer<ffi.Void>,\n          int,\n          int,\n        )\n      >();\n\n  ffi.Pointer<ffi.Void> malloc_type_zone_valloc(\n    ffi.Pointer<malloc_zone_t> zone,\n    int size,\n    int type_id,\n  ) {\n    return _malloc_type_zone_valloc(zone, size, type_id);\n  }\n\n  late final _malloc_type_zone_vallocPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.Void> Function(\n            ffi.Pointer<malloc_zone_t>,\n            ffi.Size,\n            malloc_type_id_t,\n          )\n        >\n      >('malloc_type_zone_valloc');\n  late final _malloc_type_zone_valloc = _malloc_type_zone_vallocPtr\n      .asFunction<\n        ffi.Pointer<ffi.Void> Function(ffi.Pointer<malloc_zone_t>, int, int)\n      >();\n\n  ffi.Pointer<ffi.Void> malloc_type_zone_memalign(\n    ffi.Pointer<malloc_zone_t> zone,\n    int alignment,\n    int size,\n    int type_id,\n  ) {\n    return _malloc_type_zone_memalign(zone, alignment, size, type_id);\n  }\n\n  late final _malloc_type_zone_memalignPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.Void> Function(\n            ffi.Pointer<malloc_zone_t>,\n            ffi.Size,\n            ffi.Size,\n            malloc_type_id_t,\n          )\n        >\n      >('malloc_type_zone_memalign');\n  late final _malloc_type_zone_memalign = _malloc_type_zone_memalignPtr\n      .asFunction<\n        ffi.Pointer<ffi.Void> Function(\n          ffi.Pointer<malloc_zone_t>,\n          int,\n          int,\n          int,\n        )\n      >();\n\n  ffi.Pointer<ffi.Void> malloc(int __size) {\n    return _malloc(__size);\n  }\n\n  late final _mallocPtr =\n      _lookup<ffi.NativeFunction<ffi.Pointer<ffi.Void> Function(ffi.Size)>>(\n        'malloc',\n      );\n  late final _malloc = _mallocPtr\n      .asFunction<ffi.Pointer<ffi.Void> Function(int)>();\n\n  ffi.Pointer<ffi.Void> calloc(int __count, int __size) {\n    return _calloc(__count, __size);\n  }\n\n  late final _callocPtr =\n      _lookup<\n        ffi.NativeFunction<ffi.Pointer<ffi.Void> Function(ffi.Size, ffi.Size)>\n      >('calloc');\n  late final _calloc = _callocPtr\n      .asFunction<ffi.Pointer<ffi.Void> Function(int, int)>();\n\n  void free(ffi.Pointer<ffi.Void> arg0) {\n    return _free(arg0);\n  }\n\n  late final _freePtr =\n      _lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>(\n        'free',\n      );\n  late final _free = _freePtr\n      .asFunction<void Function(ffi.Pointer<ffi.Void>)>();\n\n  ffi.Pointer<ffi.Void> realloc(ffi.Pointer<ffi.Void> __ptr, int __size) {\n    return _realloc(__ptr, __size);\n  }\n\n  late final _reallocPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.Void> Function(ffi.Pointer<ffi.Void>, ffi.Size)\n        >\n      >('realloc');\n  late final _realloc = _reallocPtr\n      .asFunction<ffi.Pointer<ffi.Void> Function(ffi.Pointer<ffi.Void>, int)>();\n\n  ffi.Pointer<ffi.Void> reallocf(ffi.Pointer<ffi.Void> __ptr, int __size) {\n    return _reallocf(__ptr, __size);\n  }\n\n  late final _reallocfPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.Void> Function(ffi.Pointer<ffi.Void>, ffi.Size)\n        >\n      >('reallocf');\n  late final _reallocf = _reallocfPtr\n      .asFunction<ffi.Pointer<ffi.Void> Function(ffi.Pointer<ffi.Void>, int)>();\n\n  ffi.Pointer<ffi.Void> valloc(int __size) {\n    return _valloc(__size);\n  }\n\n  late final _vallocPtr =\n      _lookup<ffi.NativeFunction<ffi.Pointer<ffi.Void> Function(ffi.Size)>>(\n        'valloc',\n      );\n  late final _valloc = _vallocPtr\n      .asFunction<ffi.Pointer<ffi.Void> Function(int)>();\n\n  ffi.Pointer<ffi.Void> aligned_alloc(int __alignment, int __size) {\n    return _aligned_alloc(__alignment, __size);\n  }\n\n  late final _aligned_allocPtr =\n      _lookup<\n        ffi.NativeFunction<ffi.Pointer<ffi.Void> Function(ffi.Size, ffi.Size)>\n      >('aligned_alloc');\n  late final _aligned_alloc = _aligned_allocPtr\n      .asFunction<ffi.Pointer<ffi.Void> Function(int, int)>();\n\n  int posix_memalign(\n    ffi.Pointer<ffi.Pointer<ffi.Void>> __memptr,\n    int __alignment,\n    int __size,\n  ) {\n    return _posix_memalign(__memptr, __alignment, __size);\n  }\n\n  late final _posix_memalignPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Int Function(\n            ffi.Pointer<ffi.Pointer<ffi.Void>>,\n            ffi.Size,\n            ffi.Size,\n          )\n        >\n      >('posix_memalign');\n  late final _posix_memalign = _posix_memalignPtr\n      .asFunction<int Function(ffi.Pointer<ffi.Pointer<ffi.Void>>, int, int)>();\n\n  void abort() {\n    return _abort();\n  }\n\n  late final _abortPtr = _lookup<ffi.NativeFunction<ffi.Void Function()>>(\n    'abort',\n  );\n  late final _abort = _abortPtr.asFunction<void Function()>();\n\n  int abs(int arg0) {\n    return _abs(arg0);\n  }\n\n  late final _absPtr = _lookup<ffi.NativeFunction<ffi.Int Function(ffi.Int)>>(\n    'abs',\n  );\n  late final _abs = _absPtr.asFunction<int Function(int)>();\n\n  int atexit(ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> arg0) {\n    return _atexit(arg0);\n  }\n\n  late final _atexitPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Int Function(ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)\n        >\n      >('atexit');\n  late final _atexit = _atexitPtr\n      .asFunction<\n        int Function(ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)\n      >();\n\n  int at_quick_exit(ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>> arg0) {\n    return _at_quick_exit(arg0);\n  }\n\n  late final _at_quick_exitPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Int Function(ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)\n        >\n      >('at_quick_exit');\n  late final _at_quick_exit = _at_quick_exitPtr\n      .asFunction<\n        int Function(ffi.Pointer<ffi.NativeFunction<ffi.Void Function()>>)\n      >();\n\n  double atof(ffi.Pointer<ffi.Char> arg0) {\n    return _atof(arg0);\n  }\n\n  late final _atofPtr =\n      _lookup<ffi.NativeFunction<ffi.Double Function(ffi.Pointer<ffi.Char>)>>(\n        'atof',\n      );\n  late final _atof = _atofPtr\n      .asFunction<double Function(ffi.Pointer<ffi.Char>)>();\n\n  int atoi(ffi.Pointer<ffi.Char> arg0) {\n    return _atoi(arg0);\n  }\n\n  late final _atoiPtr =\n      _lookup<ffi.NativeFunction<ffi.Int Function(ffi.Pointer<ffi.Char>)>>(\n        'atoi',\n      );\n  late final _atoi = _atoiPtr.asFunction<int Function(ffi.Pointer<ffi.Char>)>();\n\n  int atol(ffi.Pointer<ffi.Char> arg0) {\n    return _atol(arg0);\n  }\n\n  late final _atolPtr =\n      _lookup<ffi.NativeFunction<ffi.Long Function(ffi.Pointer<ffi.Char>)>>(\n        'atol',\n      );\n  late final _atol = _atolPtr.asFunction<int Function(ffi.Pointer<ffi.Char>)>();\n\n  int atoll(ffi.Pointer<ffi.Char> arg0) {\n    return _atoll(arg0);\n  }\n\n  late final _atollPtr =\n      _lookup<ffi.NativeFunction<ffi.LongLong Function(ffi.Pointer<ffi.Char>)>>(\n        'atoll',\n      );\n  late final _atoll = _atollPtr\n      .asFunction<int Function(ffi.Pointer<ffi.Char>)>();\n\n  ffi.Pointer<ffi.Void> bsearch(\n    ffi.Pointer<ffi.Void> __key,\n    ffi.Pointer<ffi.Void> __base,\n    int __nel,\n    int __width,\n    ffi.Pointer<\n      ffi.NativeFunction<\n        ffi.Int Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)\n      >\n    >\n    __compar,\n  ) {\n    return _bsearch(__key, __base, __nel, __width, __compar);\n  }\n\n  late final _bsearchPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.Void> Function(\n            ffi.Pointer<ffi.Void>,\n            ffi.Pointer<ffi.Void>,\n            ffi.Size,\n            ffi.Size,\n            ffi.Pointer<\n              ffi.NativeFunction<\n                ffi.Int Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)\n              >\n            >,\n          )\n        >\n      >('bsearch');\n  late final _bsearch = _bsearchPtr\n      .asFunction<\n        ffi.Pointer<ffi.Void> Function(\n          ffi.Pointer<ffi.Void>,\n          ffi.Pointer<ffi.Void>,\n          int,\n          int,\n          ffi.Pointer<\n            ffi.NativeFunction<\n              ffi.Int Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)\n            >\n          >,\n        )\n      >();\n\n  div_t div(int arg0, int arg1) {\n    return _div(arg0, arg1);\n  }\n\n  late final _divPtr =\n      _lookup<ffi.NativeFunction<div_t Function(ffi.Int, ffi.Int)>>('div');\n  late final _div = _divPtr.asFunction<div_t Function(int, int)>();\n\n  void exit(int arg0) {\n    return _exit(arg0);\n  }\n\n  late final _exitPtr = _lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int)>>(\n    'exit',\n  );\n  late final _exit = _exitPtr.asFunction<void Function(int)>();\n\n  ffi.Pointer<ffi.Char> getenv(ffi.Pointer<ffi.Char> arg0) {\n    return _getenv(arg0);\n  }\n\n  late final _getenvPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.Char> Function(ffi.Pointer<ffi.Char>)\n        >\n      >('getenv');\n  late final _getenv = _getenvPtr\n      .asFunction<ffi.Pointer<ffi.Char> Function(ffi.Pointer<ffi.Char>)>();\n\n  int labs(int arg0) {\n    return _labs(arg0);\n  }\n\n  late final _labsPtr =\n      _lookup<ffi.NativeFunction<ffi.Long Function(ffi.Long)>>('labs');\n  late final _labs = _labsPtr.asFunction<int Function(int)>();\n\n  ldiv_t ldiv(int arg0, int arg1) {\n    return _ldiv(arg0, arg1);\n  }\n\n  late final _ldivPtr =\n      _lookup<ffi.NativeFunction<ldiv_t Function(ffi.Long, ffi.Long)>>('ldiv');\n  late final _ldiv = _ldivPtr.asFunction<ldiv_t Function(int, int)>();\n\n  int llabs(int arg0) {\n    return _llabs(arg0);\n  }\n\n  late final _llabsPtr =\n      _lookup<ffi.NativeFunction<ffi.LongLong Function(ffi.LongLong)>>('llabs');\n  late final _llabs = _llabsPtr.asFunction<int Function(int)>();\n\n  lldiv_t lldiv(int arg0, int arg1) {\n    return _lldiv(arg0, arg1);\n  }\n\n  late final _lldivPtr =\n      _lookup<ffi.NativeFunction<lldiv_t Function(ffi.LongLong, ffi.LongLong)>>(\n        'lldiv',\n      );\n  late final _lldiv = _lldivPtr.asFunction<lldiv_t Function(int, int)>();\n\n  int mblen(ffi.Pointer<ffi.Char> __s, int __n) {\n    return _mblen(__s, __n);\n  }\n\n  late final _mblenPtr =\n      _lookup<\n        ffi.NativeFunction<ffi.Int Function(ffi.Pointer<ffi.Char>, ffi.Size)>\n      >('mblen');\n  late final _mblen = _mblenPtr\n      .asFunction<int Function(ffi.Pointer<ffi.Char>, int)>();\n\n  int mbstowcs(\n    ffi.Pointer<ffi.WChar> arg0,\n    ffi.Pointer<ffi.Char> arg1,\n    int arg2,\n  ) {\n    return _mbstowcs(arg0, arg1, arg2);\n  }\n\n  late final _mbstowcsPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Size Function(\n            ffi.Pointer<ffi.WChar>,\n            ffi.Pointer<ffi.Char>,\n            ffi.Size,\n          )\n        >\n      >('mbstowcs');\n  late final _mbstowcs = _mbstowcsPtr\n      .asFunction<\n        int Function(ffi.Pointer<ffi.WChar>, ffi.Pointer<ffi.Char>, int)\n      >();\n\n  int mbtowc(\n    ffi.Pointer<ffi.WChar> arg0,\n    ffi.Pointer<ffi.Char> arg1,\n    int arg2,\n  ) {\n    return _mbtowc(arg0, arg1, arg2);\n  }\n\n  late final _mbtowcPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Int Function(\n            ffi.Pointer<ffi.WChar>,\n            ffi.Pointer<ffi.Char>,\n            ffi.Size,\n          )\n        >\n      >('mbtowc');\n  late final _mbtowc = _mbtowcPtr\n      .asFunction<\n        int Function(ffi.Pointer<ffi.WChar>, ffi.Pointer<ffi.Char>, int)\n      >();\n\n  void qsort(\n    ffi.Pointer<ffi.Void> __base,\n    int __nel,\n    int __width,\n    ffi.Pointer<\n      ffi.NativeFunction<\n        ffi.Int Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)\n      >\n    >\n    __compar,\n  ) {\n    return _qsort(__base, __nel, __width, __compar);\n  }\n\n  late final _qsortPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Void Function(\n            ffi.Pointer<ffi.Void>,\n            ffi.Size,\n            ffi.Size,\n            ffi.Pointer<\n              ffi.NativeFunction<\n                ffi.Int Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)\n              >\n            >,\n          )\n        >\n      >('qsort');\n  late final _qsort = _qsortPtr\n      .asFunction<\n        void Function(\n          ffi.Pointer<ffi.Void>,\n          int,\n          int,\n          ffi.Pointer<\n            ffi.NativeFunction<\n              ffi.Int Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)\n            >\n          >,\n        )\n      >();\n\n  void quick_exit(int arg0) {\n    return _quick_exit(arg0);\n  }\n\n  late final _quick_exitPtr =\n      _lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int)>>('quick_exit');\n  late final _quick_exit = _quick_exitPtr.asFunction<void Function(int)>();\n\n  int rand() {\n    return _rand();\n  }\n\n  late final _randPtr = _lookup<ffi.NativeFunction<ffi.Int Function()>>('rand');\n  late final _rand = _randPtr.asFunction<int Function()>();\n\n  void srand(int arg0) {\n    return _srand(arg0);\n  }\n\n  late final _srandPtr =\n      _lookup<ffi.NativeFunction<ffi.Void Function(ffi.UnsignedInt)>>('srand');\n  late final _srand = _srandPtr.asFunction<void Function(int)>();\n\n  double strtod(\n    ffi.Pointer<ffi.Char> arg0,\n    ffi.Pointer<ffi.Pointer<ffi.Char>> arg1,\n  ) {\n    return _strtod(arg0, arg1);\n  }\n\n  late final _strtodPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Double Function(\n            ffi.Pointer<ffi.Char>,\n            ffi.Pointer<ffi.Pointer<ffi.Char>>,\n          )\n        >\n      >('strtod');\n  late final _strtod = _strtodPtr\n      .asFunction<\n        double Function(\n          ffi.Pointer<ffi.Char>,\n          ffi.Pointer<ffi.Pointer<ffi.Char>>,\n        )\n      >();\n\n  double strtof(\n    ffi.Pointer<ffi.Char> arg0,\n    ffi.Pointer<ffi.Pointer<ffi.Char>> arg1,\n  ) {\n    return _strtof(arg0, arg1);\n  }\n\n  late final _strtofPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Float Function(\n            ffi.Pointer<ffi.Char>,\n            ffi.Pointer<ffi.Pointer<ffi.Char>>,\n          )\n        >\n      >('strtof');\n  late final _strtof = _strtofPtr\n      .asFunction<\n        double Function(\n          ffi.Pointer<ffi.Char>,\n          ffi.Pointer<ffi.Pointer<ffi.Char>>,\n        )\n      >();\n\n  int strtol(\n    ffi.Pointer<ffi.Char> __str,\n    ffi.Pointer<ffi.Pointer<ffi.Char>> __endptr,\n    int __base,\n  ) {\n    return _strtol(__str, __endptr, __base);\n  }\n\n  late final _strtolPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Long Function(\n            ffi.Pointer<ffi.Char>,\n            ffi.Pointer<ffi.Pointer<ffi.Char>>,\n            ffi.Int,\n          )\n        >\n      >('strtol');\n  late final _strtol = _strtolPtr\n      .asFunction<\n        int Function(\n          ffi.Pointer<ffi.Char>,\n          ffi.Pointer<ffi.Pointer<ffi.Char>>,\n          int,\n        )\n      >();\n\n  int strtoll(\n    ffi.Pointer<ffi.Char> __str,\n    ffi.Pointer<ffi.Pointer<ffi.Char>> __endptr,\n    int __base,\n  ) {\n    return _strtoll(__str, __endptr, __base);\n  }\n\n  late final _strtollPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.LongLong Function(\n            ffi.Pointer<ffi.Char>,\n            ffi.Pointer<ffi.Pointer<ffi.Char>>,\n            ffi.Int,\n          )\n        >\n      >('strtoll');\n  late final _strtoll = _strtollPtr\n      .asFunction<\n        int Function(\n          ffi.Pointer<ffi.Char>,\n          ffi.Pointer<ffi.Pointer<ffi.Char>>,\n          int,\n        )\n      >();\n\n  int strtoul(\n    ffi.Pointer<ffi.Char> __str,\n    ffi.Pointer<ffi.Pointer<ffi.Char>> __endptr,\n    int __base,\n  ) {\n    return _strtoul(__str, __endptr, __base);\n  }\n\n  late final _strtoulPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.UnsignedLong Function(\n            ffi.Pointer<ffi.Char>,\n            ffi.Pointer<ffi.Pointer<ffi.Char>>,\n            ffi.Int,\n          )\n        >\n      >('strtoul');\n  late final _strtoul = _strtoulPtr\n      .asFunction<\n        int Function(\n          ffi.Pointer<ffi.Char>,\n          ffi.Pointer<ffi.Pointer<ffi.Char>>,\n          int,\n        )\n      >();\n\n  int strtoull(\n    ffi.Pointer<ffi.Char> __str,\n    ffi.Pointer<ffi.Pointer<ffi.Char>> __endptr,\n    int __base,\n  ) {\n    return _strtoull(__str, __endptr, __base);\n  }\n\n  late final _strtoullPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.UnsignedLongLong Function(\n            ffi.Pointer<ffi.Char>,\n            ffi.Pointer<ffi.Pointer<ffi.Char>>,\n            ffi.Int,\n          )\n        >\n      >('strtoull');\n  late final _strtoull = _strtoullPtr\n      .asFunction<\n        int Function(\n          ffi.Pointer<ffi.Char>,\n          ffi.Pointer<ffi.Pointer<ffi.Char>>,\n          int,\n        )\n      >();\n\n  int system(ffi.Pointer<ffi.Char> arg0) {\n    return _system(arg0);\n  }\n\n  late final _systemPtr =\n      _lookup<ffi.NativeFunction<ffi.Int Function(ffi.Pointer<ffi.Char>)>>(\n        'system',\n      );\n  late final _system = _systemPtr\n      .asFunction<int Function(ffi.Pointer<ffi.Char>)>();\n\n  int wcstombs(\n    ffi.Pointer<ffi.Char> arg0,\n    ffi.Pointer<ffi.WChar> arg1,\n    int arg2,\n  ) {\n    return _wcstombs(arg0, arg1, arg2);\n  }\n\n  late final _wcstombsPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Size Function(\n            ffi.Pointer<ffi.Char>,\n            ffi.Pointer<ffi.WChar>,\n            ffi.Size,\n          )\n        >\n      >('wcstombs');\n  late final _wcstombs = _wcstombsPtr\n      .asFunction<\n        int Function(ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.WChar>, int)\n      >();\n\n  int wctomb(ffi.Pointer<ffi.Char> arg0, int arg1) {\n    return _wctomb(arg0, arg1);\n  }\n\n  late final _wctombPtr =\n      _lookup<\n        ffi.NativeFunction<ffi.Int Function(ffi.Pointer<ffi.Char>, ffi.WChar)>\n      >('wctomb');\n  late final _wctomb = _wctombPtr\n      .asFunction<int Function(ffi.Pointer<ffi.Char>, int)>();\n\n  void _Exit(int arg0) {\n    return __Exit(arg0);\n  }\n\n  late final __ExitPtr =\n      _lookup<ffi.NativeFunction<ffi.Void Function(ffi.Int)>>('_Exit');\n  late final __Exit = __ExitPtr.asFunction<void Function(int)>();\n\n  int a64l(ffi.Pointer<ffi.Char> arg0) {\n    return _a64l(arg0);\n  }\n\n  late final _a64lPtr =\n      _lookup<ffi.NativeFunction<ffi.Long Function(ffi.Pointer<ffi.Char>)>>(\n        'a64l',\n      );\n  late final _a64l = _a64lPtr.asFunction<int Function(ffi.Pointer<ffi.Char>)>();\n\n  double drand48() {\n    return _drand48();\n  }\n\n  late final _drand48Ptr = _lookup<ffi.NativeFunction<ffi.Double Function()>>(\n    'drand48',\n  );\n  late final _drand48 = _drand48Ptr.asFunction<double Function()>();\n\n  ffi.Pointer<ffi.Char> ecvt(\n    double arg0,\n    int arg1,\n    ffi.Pointer<ffi.Int> arg2,\n    ffi.Pointer<ffi.Int> arg3,\n  ) {\n    return _ecvt(arg0, arg1, arg2, arg3);\n  }\n\n  late final _ecvtPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.Char> Function(\n            ffi.Double,\n            ffi.Int,\n            ffi.Pointer<ffi.Int>,\n            ffi.Pointer<ffi.Int>,\n          )\n        >\n      >('ecvt');\n  late final _ecvt = _ecvtPtr\n      .asFunction<\n        ffi.Pointer<ffi.Char> Function(\n          double,\n          int,\n          ffi.Pointer<ffi.Int>,\n          ffi.Pointer<ffi.Int>,\n        )\n      >();\n\n  double erand48(ffi.Pointer<ffi.UnsignedShort> arg0) {\n    return _erand48(arg0);\n  }\n\n  late final _erand48Ptr =\n      _lookup<\n        ffi.NativeFunction<ffi.Double Function(ffi.Pointer<ffi.UnsignedShort>)>\n      >('erand48');\n  late final _erand48 = _erand48Ptr\n      .asFunction<double Function(ffi.Pointer<ffi.UnsignedShort>)>();\n\n  ffi.Pointer<ffi.Char> fcvt(\n    double arg0,\n    int arg1,\n    ffi.Pointer<ffi.Int> arg2,\n    ffi.Pointer<ffi.Int> arg3,\n  ) {\n    return _fcvt(arg0, arg1, arg2, arg3);\n  }\n\n  late final _fcvtPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.Char> Function(\n            ffi.Double,\n            ffi.Int,\n            ffi.Pointer<ffi.Int>,\n            ffi.Pointer<ffi.Int>,\n          )\n        >\n      >('fcvt');\n  late final _fcvt = _fcvtPtr\n      .asFunction<\n        ffi.Pointer<ffi.Char> Function(\n          double,\n          int,\n          ffi.Pointer<ffi.Int>,\n          ffi.Pointer<ffi.Int>,\n        )\n      >();\n\n  ffi.Pointer<ffi.Char> gcvt(\n    double arg0,\n    int arg1,\n    ffi.Pointer<ffi.Char> arg2,\n  ) {\n    return _gcvt(arg0, arg1, arg2);\n  }\n\n  late final _gcvtPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.Char> Function(\n            ffi.Double,\n            ffi.Int,\n            ffi.Pointer<ffi.Char>,\n          )\n        >\n      >('gcvt');\n  late final _gcvt = _gcvtPtr\n      .asFunction<\n        ffi.Pointer<ffi.Char> Function(double, int, ffi.Pointer<ffi.Char>)\n      >();\n\n  int getsubopt(\n    ffi.Pointer<ffi.Pointer<ffi.Char>> arg0,\n    ffi.Pointer<ffi.Pointer<ffi.Char>> arg1,\n    ffi.Pointer<ffi.Pointer<ffi.Char>> arg2,\n  ) {\n    return _getsubopt(arg0, arg1, arg2);\n  }\n\n  late final _getsuboptPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Int Function(\n            ffi.Pointer<ffi.Pointer<ffi.Char>>,\n            ffi.Pointer<ffi.Pointer<ffi.Char>>,\n            ffi.Pointer<ffi.Pointer<ffi.Char>>,\n          )\n        >\n      >('getsubopt');\n  late final _getsubopt = _getsuboptPtr\n      .asFunction<\n        int Function(\n          ffi.Pointer<ffi.Pointer<ffi.Char>>,\n          ffi.Pointer<ffi.Pointer<ffi.Char>>,\n          ffi.Pointer<ffi.Pointer<ffi.Char>>,\n        )\n      >();\n\n  int grantpt(int arg0) {\n    return _grantpt(arg0);\n  }\n\n  late final _grantptPtr =\n      _lookup<ffi.NativeFunction<ffi.Int Function(ffi.Int)>>('grantpt');\n  late final _grantpt = _grantptPtr.asFunction<int Function(int)>();\n\n  ffi.Pointer<ffi.Char> initstate(\n    int arg0,\n    ffi.Pointer<ffi.Char> arg1,\n    int arg2,\n  ) {\n    return _initstate(arg0, arg1, arg2);\n  }\n\n  late final _initstatePtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.Char> Function(\n            ffi.UnsignedInt,\n            ffi.Pointer<ffi.Char>,\n            ffi.Size,\n          )\n        >\n      >('initstate');\n  late final _initstate = _initstatePtr\n      .asFunction<\n        ffi.Pointer<ffi.Char> Function(int, ffi.Pointer<ffi.Char>, int)\n      >();\n\n  int jrand48(ffi.Pointer<ffi.UnsignedShort> arg0) {\n    return _jrand48(arg0);\n  }\n\n  late final _jrand48Ptr =\n      _lookup<\n        ffi.NativeFunction<ffi.Long Function(ffi.Pointer<ffi.UnsignedShort>)>\n      >('jrand48');\n  late final _jrand48 = _jrand48Ptr\n      .asFunction<int Function(ffi.Pointer<ffi.UnsignedShort>)>();\n\n  ffi.Pointer<ffi.Char> l64a(int arg0) {\n    return _l64a(arg0);\n  }\n\n  late final _l64aPtr =\n      _lookup<ffi.NativeFunction<ffi.Pointer<ffi.Char> Function(ffi.Long)>>(\n        'l64a',\n      );\n  late final _l64a = _l64aPtr.asFunction<ffi.Pointer<ffi.Char> Function(int)>();\n\n  void lcong48(ffi.Pointer<ffi.UnsignedShort> arg0) {\n    return _lcong48(arg0);\n  }\n\n  late final _lcong48Ptr =\n      _lookup<\n        ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.UnsignedShort>)>\n      >('lcong48');\n  late final _lcong48 = _lcong48Ptr\n      .asFunction<void Function(ffi.Pointer<ffi.UnsignedShort>)>();\n\n  int lrand48() {\n    return _lrand48();\n  }\n\n  late final _lrand48Ptr = _lookup<ffi.NativeFunction<ffi.Long Function()>>(\n    'lrand48',\n  );\n  late final _lrand48 = _lrand48Ptr.asFunction<int Function()>();\n\n  ffi.Pointer<ffi.Char> mktemp(ffi.Pointer<ffi.Char> arg0) {\n    return _mktemp(arg0);\n  }\n\n  late final _mktempPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.Char> Function(ffi.Pointer<ffi.Char>)\n        >\n      >('mktemp');\n  late final _mktemp = _mktempPtr\n      .asFunction<ffi.Pointer<ffi.Char> Function(ffi.Pointer<ffi.Char>)>();\n\n  int mkstemp(ffi.Pointer<ffi.Char> arg0) {\n    return _mkstemp(arg0);\n  }\n\n  late final _mkstempPtr =\n      _lookup<ffi.NativeFunction<ffi.Int Function(ffi.Pointer<ffi.Char>)>>(\n        'mkstemp',\n      );\n  late final _mkstemp = _mkstempPtr\n      .asFunction<int Function(ffi.Pointer<ffi.Char>)>();\n\n  int mrand48() {\n    return _mrand48();\n  }\n\n  late final _mrand48Ptr = _lookup<ffi.NativeFunction<ffi.Long Function()>>(\n    'mrand48',\n  );\n  late final _mrand48 = _mrand48Ptr.asFunction<int Function()>();\n\n  int nrand48(ffi.Pointer<ffi.UnsignedShort> arg0) {\n    return _nrand48(arg0);\n  }\n\n  late final _nrand48Ptr =\n      _lookup<\n        ffi.NativeFunction<ffi.Long Function(ffi.Pointer<ffi.UnsignedShort>)>\n      >('nrand48');\n  late final _nrand48 = _nrand48Ptr\n      .asFunction<int Function(ffi.Pointer<ffi.UnsignedShort>)>();\n\n  int posix_openpt(int arg0) {\n    return _posix_openpt(arg0);\n  }\n\n  late final _posix_openptPtr =\n      _lookup<ffi.NativeFunction<ffi.Int Function(ffi.Int)>>('posix_openpt');\n  late final _posix_openpt = _posix_openptPtr.asFunction<int Function(int)>();\n\n  ffi.Pointer<ffi.Char> ptsname(int arg0) {\n    return _ptsname(arg0);\n  }\n\n  late final _ptsnamePtr =\n      _lookup<ffi.NativeFunction<ffi.Pointer<ffi.Char> Function(ffi.Int)>>(\n        'ptsname',\n      );\n  late final _ptsname = _ptsnamePtr\n      .asFunction<ffi.Pointer<ffi.Char> Function(int)>();\n\n  int ptsname_r(int fildes, ffi.Pointer<ffi.Char> buffer, int buflen) {\n    return _ptsname_r(fildes, buffer, buflen);\n  }\n\n  late final _ptsname_rPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Int Function(ffi.Int, ffi.Pointer<ffi.Char>, ffi.Size)\n        >\n      >('ptsname_r');\n  late final _ptsname_r = _ptsname_rPtr\n      .asFunction<int Function(int, ffi.Pointer<ffi.Char>, int)>();\n\n  int putenv(ffi.Pointer<ffi.Char> arg0) {\n    return _putenv(arg0);\n  }\n\n  late final _putenvPtr =\n      _lookup<ffi.NativeFunction<ffi.Int Function(ffi.Pointer<ffi.Char>)>>(\n        'putenv',\n      );\n  late final _putenv = _putenvPtr\n      .asFunction<int Function(ffi.Pointer<ffi.Char>)>();\n\n  int random() {\n    return _random();\n  }\n\n  late final _randomPtr = _lookup<ffi.NativeFunction<ffi.Long Function()>>(\n    'random',\n  );\n  late final _random = _randomPtr.asFunction<int Function()>();\n\n  int rand_r(ffi.Pointer<ffi.UnsignedInt> arg0) {\n    return _rand_r(arg0);\n  }\n\n  late final _rand_rPtr =\n      _lookup<\n        ffi.NativeFunction<ffi.Int Function(ffi.Pointer<ffi.UnsignedInt>)>\n      >('rand_r');\n  late final _rand_r = _rand_rPtr\n      .asFunction<int Function(ffi.Pointer<ffi.UnsignedInt>)>();\n\n  ffi.Pointer<ffi.Char> realpath(\n    ffi.Pointer<ffi.Char> arg0,\n    ffi.Pointer<ffi.Char> arg1,\n  ) {\n    return _realpath(arg0, arg1);\n  }\n\n  late final _realpathPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.Char> Function(\n            ffi.Pointer<ffi.Char>,\n            ffi.Pointer<ffi.Char>,\n          )\n        >\n      >('realpath');\n  late final _realpath = _realpathPtr\n      .asFunction<\n        ffi.Pointer<ffi.Char> Function(\n          ffi.Pointer<ffi.Char>,\n          ffi.Pointer<ffi.Char>,\n        )\n      >();\n\n  ffi.Pointer<ffi.UnsignedShort> seed48(ffi.Pointer<ffi.UnsignedShort> arg0) {\n    return _seed48(arg0);\n  }\n\n  late final _seed48Ptr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.UnsignedShort> Function(\n            ffi.Pointer<ffi.UnsignedShort>,\n          )\n        >\n      >('seed48');\n  late final _seed48 = _seed48Ptr\n      .asFunction<\n        ffi.Pointer<ffi.UnsignedShort> Function(ffi.Pointer<ffi.UnsignedShort>)\n      >();\n\n  int setenv(\n    ffi.Pointer<ffi.Char> __name,\n    ffi.Pointer<ffi.Char> __value,\n    int __overwrite,\n  ) {\n    return _setenv(__name, __value, __overwrite);\n  }\n\n  late final _setenvPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Int Function(\n            ffi.Pointer<ffi.Char>,\n            ffi.Pointer<ffi.Char>,\n            ffi.Int,\n          )\n        >\n      >('setenv');\n  late final _setenv = _setenvPtr\n      .asFunction<\n        int Function(ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Char>, int)\n      >();\n\n  void setkey(ffi.Pointer<ffi.Char> arg0) {\n    return _setkey(arg0);\n  }\n\n  late final _setkeyPtr =\n      _lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Char>)>>(\n        'setkey',\n      );\n  late final _setkey = _setkeyPtr\n      .asFunction<void Function(ffi.Pointer<ffi.Char>)>();\n\n  ffi.Pointer<ffi.Char> setstate(ffi.Pointer<ffi.Char> arg0) {\n    return _setstate(arg0);\n  }\n\n  late final _setstatePtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.Char> Function(ffi.Pointer<ffi.Char>)\n        >\n      >('setstate');\n  late final _setstate = _setstatePtr\n      .asFunction<ffi.Pointer<ffi.Char> Function(ffi.Pointer<ffi.Char>)>();\n\n  void srand48(int arg0) {\n    return _srand48(arg0);\n  }\n\n  late final _srand48Ptr =\n      _lookup<ffi.NativeFunction<ffi.Void Function(ffi.Long)>>('srand48');\n  late final _srand48 = _srand48Ptr.asFunction<void Function(int)>();\n\n  void srandom(int arg0) {\n    return _srandom(arg0);\n  }\n\n  late final _srandomPtr =\n      _lookup<ffi.NativeFunction<ffi.Void Function(ffi.UnsignedInt)>>(\n        'srandom',\n      );\n  late final _srandom = _srandomPtr.asFunction<void Function(int)>();\n\n  int unlockpt(int arg0) {\n    return _unlockpt(arg0);\n  }\n\n  late final _unlockptPtr =\n      _lookup<ffi.NativeFunction<ffi.Int Function(ffi.Int)>>('unlockpt');\n  late final _unlockpt = _unlockptPtr.asFunction<int Function(int)>();\n\n  int unsetenv(ffi.Pointer<ffi.Char> arg0) {\n    return _unsetenv(arg0);\n  }\n\n  late final _unsetenvPtr =\n      _lookup<ffi.NativeFunction<ffi.Int Function(ffi.Pointer<ffi.Char>)>>(\n        'unsetenv',\n      );\n  late final _unsetenv = _unsetenvPtr\n      .asFunction<int Function(ffi.Pointer<ffi.Char>)>();\n\n  int arc4random() {\n    return _arc4random();\n  }\n\n  late final _arc4randomPtr =\n      _lookup<ffi.NativeFunction<ffi.Uint32 Function()>>('arc4random');\n  late final _arc4random = _arc4randomPtr.asFunction<int Function()>();\n\n  void arc4random_addrandom(ffi.Pointer<ffi.UnsignedChar> arg0, int arg1) {\n    return _arc4random_addrandom(arg0, arg1);\n  }\n\n  late final _arc4random_addrandomPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Void Function(ffi.Pointer<ffi.UnsignedChar>, ffi.Int)\n        >\n      >('arc4random_addrandom');\n  late final _arc4random_addrandom = _arc4random_addrandomPtr\n      .asFunction<void Function(ffi.Pointer<ffi.UnsignedChar>, int)>();\n\n  void arc4random_buf(ffi.Pointer<ffi.Void> __buf, int __nbytes) {\n    return _arc4random_buf(__buf, __nbytes);\n  }\n\n  late final _arc4random_bufPtr =\n      _lookup<\n        ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>, ffi.Size)>\n      >('arc4random_buf');\n  late final _arc4random_buf = _arc4random_bufPtr\n      .asFunction<void Function(ffi.Pointer<ffi.Void>, int)>();\n\n  void arc4random_stir() {\n    return _arc4random_stir();\n  }\n\n  late final _arc4random_stirPtr =\n      _lookup<ffi.NativeFunction<ffi.Void Function()>>('arc4random_stir');\n  late final _arc4random_stir = _arc4random_stirPtr\n      .asFunction<void Function()>();\n\n  int arc4random_uniform(int __upper_bound) {\n    return _arc4random_uniform(__upper_bound);\n  }\n\n  late final _arc4random_uniformPtr =\n      _lookup<ffi.NativeFunction<ffi.Uint32 Function(ffi.Uint32)>>(\n        'arc4random_uniform',\n      );\n  late final _arc4random_uniform = _arc4random_uniformPtr\n      .asFunction<int Function(int)>();\n\n  ffi.Pointer<ffi.Char> cgetcap(\n    ffi.Pointer<ffi.Char> arg0,\n    ffi.Pointer<ffi.Char> arg1,\n    int arg2,\n  ) {\n    return _cgetcap(arg0, arg1, arg2);\n  }\n\n  late final _cgetcapPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.Char> Function(\n            ffi.Pointer<ffi.Char>,\n            ffi.Pointer<ffi.Char>,\n            ffi.Int,\n          )\n        >\n      >('cgetcap');\n  late final _cgetcap = _cgetcapPtr\n      .asFunction<\n        ffi.Pointer<ffi.Char> Function(\n          ffi.Pointer<ffi.Char>,\n          ffi.Pointer<ffi.Char>,\n          int,\n        )\n      >();\n\n  int cgetclose() {\n    return _cgetclose();\n  }\n\n  late final _cgetclosePtr = _lookup<ffi.NativeFunction<ffi.Int Function()>>(\n    'cgetclose',\n  );\n  late final _cgetclose = _cgetclosePtr.asFunction<int Function()>();\n\n  int cgetent(\n    ffi.Pointer<ffi.Pointer<ffi.Char>> arg0,\n    ffi.Pointer<ffi.Pointer<ffi.Char>> arg1,\n    ffi.Pointer<ffi.Char> arg2,\n  ) {\n    return _cgetent(arg0, arg1, arg2);\n  }\n\n  late final _cgetentPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Int Function(\n            ffi.Pointer<ffi.Pointer<ffi.Char>>,\n            ffi.Pointer<ffi.Pointer<ffi.Char>>,\n            ffi.Pointer<ffi.Char>,\n          )\n        >\n      >('cgetent');\n  late final _cgetent = _cgetentPtr\n      .asFunction<\n        int Function(\n          ffi.Pointer<ffi.Pointer<ffi.Char>>,\n          ffi.Pointer<ffi.Pointer<ffi.Char>>,\n          ffi.Pointer<ffi.Char>,\n        )\n      >();\n\n  int cgetfirst(\n    ffi.Pointer<ffi.Pointer<ffi.Char>> arg0,\n    ffi.Pointer<ffi.Pointer<ffi.Char>> arg1,\n  ) {\n    return _cgetfirst(arg0, arg1);\n  }\n\n  late final _cgetfirstPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Int Function(\n            ffi.Pointer<ffi.Pointer<ffi.Char>>,\n            ffi.Pointer<ffi.Pointer<ffi.Char>>,\n          )\n        >\n      >('cgetfirst');\n  late final _cgetfirst = _cgetfirstPtr\n      .asFunction<\n        int Function(\n          ffi.Pointer<ffi.Pointer<ffi.Char>>,\n          ffi.Pointer<ffi.Pointer<ffi.Char>>,\n        )\n      >();\n\n  int cgetmatch(ffi.Pointer<ffi.Char> arg0, ffi.Pointer<ffi.Char> arg1) {\n    return _cgetmatch(arg0, arg1);\n  }\n\n  late final _cgetmatchPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Int Function(ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Char>)\n        >\n      >('cgetmatch');\n  late final _cgetmatch = _cgetmatchPtr\n      .asFunction<int Function(ffi.Pointer<ffi.Char>, ffi.Pointer<ffi.Char>)>();\n\n  int cgetnext(\n    ffi.Pointer<ffi.Pointer<ffi.Char>> arg0,\n    ffi.Pointer<ffi.Pointer<ffi.Char>> arg1,\n  ) {\n    return _cgetnext(arg0, arg1);\n  }\n\n  late final _cgetnextPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Int Function(\n            ffi.Pointer<ffi.Pointer<ffi.Char>>,\n            ffi.Pointer<ffi.Pointer<ffi.Char>>,\n          )\n        >\n      >('cgetnext');\n  late final _cgetnext = _cgetnextPtr\n      .asFunction<\n        int Function(\n          ffi.Pointer<ffi.Pointer<ffi.Char>>,\n          ffi.Pointer<ffi.Pointer<ffi.Char>>,\n        )\n      >();\n\n  int cgetnum(\n    ffi.Pointer<ffi.Char> arg0,\n    ffi.Pointer<ffi.Char> arg1,\n    ffi.Pointer<ffi.Long> arg2,\n  ) {\n    return _cgetnum(arg0, arg1, arg2);\n  }\n\n  late final _cgetnumPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Int Function(\n            ffi.Pointer<ffi.Char>,\n            ffi.Pointer<ffi.Char>,\n            ffi.Pointer<ffi.Long>,\n          )\n        >\n      >('cgetnum');\n  late final _cgetnum = _cgetnumPtr\n      .asFunction<\n        int Function(\n          ffi.Pointer<ffi.Char>,\n          ffi.Pointer<ffi.Char>,\n          ffi.Pointer<ffi.Long>,\n        )\n      >();\n\n  int cgetset(ffi.Pointer<ffi.Char> arg0) {\n    return _cgetset(arg0);\n  }\n\n  late final _cgetsetPtr =\n      _lookup<ffi.NativeFunction<ffi.Int Function(ffi.Pointer<ffi.Char>)>>(\n        'cgetset',\n      );\n  late final _cgetset = _cgetsetPtr\n      .asFunction<int Function(ffi.Pointer<ffi.Char>)>();\n\n  int cgetstr(\n    ffi.Pointer<ffi.Char> arg0,\n    ffi.Pointer<ffi.Char> arg1,\n    ffi.Pointer<ffi.Pointer<ffi.Char>> arg2,\n  ) {\n    return _cgetstr(arg0, arg1, arg2);\n  }\n\n  late final _cgetstrPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Int Function(\n            ffi.Pointer<ffi.Char>,\n            ffi.Pointer<ffi.Char>,\n            ffi.Pointer<ffi.Pointer<ffi.Char>>,\n          )\n        >\n      >('cgetstr');\n  late final _cgetstr = _cgetstrPtr\n      .asFunction<\n        int Function(\n          ffi.Pointer<ffi.Char>,\n          ffi.Pointer<ffi.Char>,\n          ffi.Pointer<ffi.Pointer<ffi.Char>>,\n        )\n      >();\n\n  int cgetustr(\n    ffi.Pointer<ffi.Char> arg0,\n    ffi.Pointer<ffi.Char> arg1,\n    ffi.Pointer<ffi.Pointer<ffi.Char>> arg2,\n  ) {\n    return _cgetustr(arg0, arg1, arg2);\n  }\n\n  late final _cgetustrPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Int Function(\n            ffi.Pointer<ffi.Char>,\n            ffi.Pointer<ffi.Char>,\n            ffi.Pointer<ffi.Pointer<ffi.Char>>,\n          )\n        >\n      >('cgetustr');\n  late final _cgetustr = _cgetustrPtr\n      .asFunction<\n        int Function(\n          ffi.Pointer<ffi.Char>,\n          ffi.Pointer<ffi.Char>,\n          ffi.Pointer<ffi.Pointer<ffi.Char>>,\n        )\n      >();\n\n  int daemon(int arg0, int arg1) {\n    return _daemon(arg0, arg1);\n  }\n\n  late final _daemonPtr =\n      _lookup<ffi.NativeFunction<ffi.Int Function(ffi.Int, ffi.Int)>>('daemon');\n  late final _daemon = _daemonPtr.asFunction<int Function(int, int)>();\n\n  ffi.Pointer<ffi.Char> devname(int arg0, int arg1) {\n    return _devname(arg0, arg1);\n  }\n\n  late final _devnamePtr =\n      _lookup<\n        ffi.NativeFunction<ffi.Pointer<ffi.Char> Function(dev_t, mode_t)>\n      >('devname');\n  late final _devname = _devnamePtr\n      .asFunction<ffi.Pointer<ffi.Char> Function(int, int)>();\n\n  ffi.Pointer<ffi.Char> devname_r(\n    int arg0,\n    int arg1,\n    ffi.Pointer<ffi.Char> buf,\n    int len,\n  ) {\n    return _devname_r(arg0, arg1, buf, len);\n  }\n\n  late final _devname_rPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.Char> Function(\n            dev_t,\n            mode_t,\n            ffi.Pointer<ffi.Char>,\n            ffi.Int,\n          )\n        >\n      >('devname_r');\n  late final _devname_r = _devname_rPtr\n      .asFunction<\n        ffi.Pointer<ffi.Char> Function(int, int, ffi.Pointer<ffi.Char>, int)\n      >();\n\n  ffi.Pointer<ffi.Char> getbsize(\n    ffi.Pointer<ffi.Int> arg0,\n    ffi.Pointer<ffi.Long> arg1,\n  ) {\n    return _getbsize(arg0, arg1);\n  }\n\n  late final _getbsizePtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.Char> Function(\n            ffi.Pointer<ffi.Int>,\n            ffi.Pointer<ffi.Long>,\n          )\n        >\n      >('getbsize');\n  late final _getbsize = _getbsizePtr\n      .asFunction<\n        ffi.Pointer<ffi.Char> Function(\n          ffi.Pointer<ffi.Int>,\n          ffi.Pointer<ffi.Long>,\n        )\n      >();\n\n  int getloadavg(ffi.Pointer<ffi.Double> arg0, int arg1) {\n    return _getloadavg(arg0, arg1);\n  }\n\n  late final _getloadavgPtr =\n      _lookup<\n        ffi.NativeFunction<ffi.Int Function(ffi.Pointer<ffi.Double>, ffi.Int)>\n      >('getloadavg');\n  late final _getloadavg = _getloadavgPtr\n      .asFunction<int Function(ffi.Pointer<ffi.Double>, int)>();\n\n  ffi.Pointer<ffi.Char> getprogname() {\n    return _getprogname();\n  }\n\n  late final _getprognamePtr =\n      _lookup<ffi.NativeFunction<ffi.Pointer<ffi.Char> Function()>>(\n        'getprogname',\n      );\n  late final _getprogname = _getprognamePtr\n      .asFunction<ffi.Pointer<ffi.Char> Function()>();\n\n  void setprogname(ffi.Pointer<ffi.Char> arg0) {\n    return _setprogname(arg0);\n  }\n\n  late final _setprognamePtr =\n      _lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Char>)>>(\n        'setprogname',\n      );\n  late final _setprogname = _setprognamePtr\n      .asFunction<void Function(ffi.Pointer<ffi.Char>)>();\n\n  int heapsort(\n    ffi.Pointer<ffi.Void> __base,\n    int __nel,\n    int __width,\n    ffi.Pointer<\n      ffi.NativeFunction<\n        ffi.Int Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)\n      >\n    >\n    __compar,\n  ) {\n    return _heapsort(__base, __nel, __width, __compar);\n  }\n\n  late final _heapsortPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Int Function(\n            ffi.Pointer<ffi.Void>,\n            ffi.Size,\n            ffi.Size,\n            ffi.Pointer<\n              ffi.NativeFunction<\n                ffi.Int Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)\n              >\n            >,\n          )\n        >\n      >('heapsort');\n  late final _heapsort = _heapsortPtr\n      .asFunction<\n        int Function(\n          ffi.Pointer<ffi.Void>,\n          int,\n          int,\n          ffi.Pointer<\n            ffi.NativeFunction<\n              ffi.Int Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)\n            >\n          >,\n        )\n      >();\n\n  int mergesort(\n    ffi.Pointer<ffi.Void> __base,\n    int __nel,\n    int __width,\n    ffi.Pointer<\n      ffi.NativeFunction<\n        ffi.Int Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)\n      >\n    >\n    __compar,\n  ) {\n    return _mergesort(__base, __nel, __width, __compar);\n  }\n\n  late final _mergesortPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Int Function(\n            ffi.Pointer<ffi.Void>,\n            ffi.Size,\n            ffi.Size,\n            ffi.Pointer<\n              ffi.NativeFunction<\n                ffi.Int Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)\n              >\n            >,\n          )\n        >\n      >('mergesort');\n  late final _mergesort = _mergesortPtr\n      .asFunction<\n        int Function(\n          ffi.Pointer<ffi.Void>,\n          int,\n          int,\n          ffi.Pointer<\n            ffi.NativeFunction<\n              ffi.Int Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)\n            >\n          >,\n        )\n      >();\n\n  void psort(\n    ffi.Pointer<ffi.Void> __base,\n    int __nel,\n    int __width,\n    ffi.Pointer<\n      ffi.NativeFunction<\n        ffi.Int Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)\n      >\n    >\n    __compar,\n  ) {\n    return _psort(__base, __nel, __width, __compar);\n  }\n\n  late final _psortPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Void Function(\n            ffi.Pointer<ffi.Void>,\n            ffi.Size,\n            ffi.Size,\n            ffi.Pointer<\n              ffi.NativeFunction<\n                ffi.Int Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)\n              >\n            >,\n          )\n        >\n      >('psort');\n  late final _psort = _psortPtr\n      .asFunction<\n        void Function(\n          ffi.Pointer<ffi.Void>,\n          int,\n          int,\n          ffi.Pointer<\n            ffi.NativeFunction<\n              ffi.Int Function(ffi.Pointer<ffi.Void>, ffi.Pointer<ffi.Void>)\n            >\n          >,\n        )\n      >();\n\n  void psort_r(\n    ffi.Pointer<ffi.Void> __base,\n    int __nel,\n    int __width,\n    ffi.Pointer<ffi.Void> arg3,\n    ffi.Pointer<\n      ffi.NativeFunction<\n        ffi.Int Function(\n          ffi.Pointer<ffi.Void>,\n          ffi.Pointer<ffi.Void>,\n          ffi.Pointer<ffi.Void>,\n        )\n      >\n    >\n    __compar,\n  ) {\n    return _psort_r(__base, __nel, __width, arg3, __compar);\n  }\n\n  late final _psort_rPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Void Function(\n            ffi.Pointer<ffi.Void>,\n            ffi.Size,\n            ffi.Size,\n            ffi.Pointer<ffi.Void>,\n            ffi.Pointer<\n              ffi.NativeFunction<\n                ffi.Int Function(\n                  ffi.Pointer<ffi.Void>,\n                  ffi.Pointer<ffi.Void>,\n                  ffi.Pointer<ffi.Void>,\n                )\n              >\n            >,\n          )\n        >\n      >('psort_r');\n  late final _psort_r = _psort_rPtr\n      .asFunction<\n        void Function(\n          ffi.Pointer<ffi.Void>,\n          int,\n          int,\n          ffi.Pointer<ffi.Void>,\n          ffi.Pointer<\n            ffi.NativeFunction<\n              ffi.Int Function(\n                ffi.Pointer<ffi.Void>,\n                ffi.Pointer<ffi.Void>,\n                ffi.Pointer<ffi.Void>,\n              )\n            >\n          >,\n        )\n      >();\n\n  void qsort_r(\n    ffi.Pointer<ffi.Void> __base,\n    int __nel,\n    int __width,\n    ffi.Pointer<ffi.Void> arg3,\n    ffi.Pointer<\n      ffi.NativeFunction<\n        ffi.Int Function(\n          ffi.Pointer<ffi.Void>,\n          ffi.Pointer<ffi.Void>,\n          ffi.Pointer<ffi.Void>,\n        )\n      >\n    >\n    __compar,\n  ) {\n    return _qsort_r(__base, __nel, __width, arg3, __compar);\n  }\n\n  late final _qsort_rPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Void Function(\n            ffi.Pointer<ffi.Void>,\n            ffi.Size,\n            ffi.Size,\n            ffi.Pointer<ffi.Void>,\n            ffi.Pointer<\n              ffi.NativeFunction<\n                ffi.Int Function(\n                  ffi.Pointer<ffi.Void>,\n                  ffi.Pointer<ffi.Void>,\n                  ffi.Pointer<ffi.Void>,\n                )\n              >\n            >,\n          )\n        >\n      >('qsort_r');\n  late final _qsort_r = _qsort_rPtr\n      .asFunction<\n        void Function(\n          ffi.Pointer<ffi.Void>,\n          int,\n          int,\n          ffi.Pointer<ffi.Void>,\n          ffi.Pointer<\n            ffi.NativeFunction<\n              ffi.Int Function(\n                ffi.Pointer<ffi.Void>,\n                ffi.Pointer<ffi.Void>,\n                ffi.Pointer<ffi.Void>,\n              )\n            >\n          >,\n        )\n      >();\n\n  int radixsort(\n    ffi.Pointer<ffi.Pointer<ffi.UnsignedChar>> __base,\n    int __nel,\n    ffi.Pointer<ffi.UnsignedChar> __table,\n    int __endbyte,\n  ) {\n    return _radixsort(__base, __nel, __table, __endbyte);\n  }\n\n  late final _radixsortPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Int Function(\n            ffi.Pointer<ffi.Pointer<ffi.UnsignedChar>>,\n            ffi.Int,\n            ffi.Pointer<ffi.UnsignedChar>,\n            ffi.UnsignedInt,\n          )\n        >\n      >('radixsort');\n  late final _radixsort = _radixsortPtr\n      .asFunction<\n        int Function(\n          ffi.Pointer<ffi.Pointer<ffi.UnsignedChar>>,\n          int,\n          ffi.Pointer<ffi.UnsignedChar>,\n          int,\n        )\n      >();\n\n  int rpmatch(ffi.Pointer<ffi.Char> arg0) {\n    return _rpmatch(arg0);\n  }\n\n  late final _rpmatchPtr =\n      _lookup<ffi.NativeFunction<ffi.Int Function(ffi.Pointer<ffi.Char>)>>(\n        'rpmatch',\n      );\n  late final _rpmatch = _rpmatchPtr\n      .asFunction<int Function(ffi.Pointer<ffi.Char>)>();\n\n  int sradixsort(\n    ffi.Pointer<ffi.Pointer<ffi.UnsignedChar>> __base,\n    int __nel,\n    ffi.Pointer<ffi.UnsignedChar> __table,\n    int __endbyte,\n  ) {\n    return _sradixsort(__base, __nel, __table, __endbyte);\n  }\n\n  late final _sradixsortPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Int Function(\n            ffi.Pointer<ffi.Pointer<ffi.UnsignedChar>>,\n            ffi.Int,\n            ffi.Pointer<ffi.UnsignedChar>,\n            ffi.UnsignedInt,\n          )\n        >\n      >('sradixsort');\n  late final _sradixsort = _sradixsortPtr\n      .asFunction<\n        int Function(\n          ffi.Pointer<ffi.Pointer<ffi.UnsignedChar>>,\n          int,\n          ffi.Pointer<ffi.UnsignedChar>,\n          int,\n        )\n      >();\n\n  void sranddev() {\n    return _sranddev();\n  }\n\n  late final _sranddevPtr = _lookup<ffi.NativeFunction<ffi.Void Function()>>(\n    'sranddev',\n  );\n  late final _sranddev = _sranddevPtr.asFunction<void Function()>();\n\n  void srandomdev() {\n    return _srandomdev();\n  }\n\n  late final _srandomdevPtr = _lookup<ffi.NativeFunction<ffi.Void Function()>>(\n    'srandomdev',\n  );\n  late final _srandomdev = _srandomdevPtr.asFunction<void Function()>();\n\n  int strtonum(\n    ffi.Pointer<ffi.Char> __numstr,\n    int __minval,\n    int __maxval,\n    ffi.Pointer<ffi.Pointer<ffi.Char>> __errstrp,\n  ) {\n    return _strtonum(__numstr, __minval, __maxval, __errstrp);\n  }\n\n  late final _strtonumPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.LongLong Function(\n            ffi.Pointer<ffi.Char>,\n            ffi.LongLong,\n            ffi.LongLong,\n            ffi.Pointer<ffi.Pointer<ffi.Char>>,\n          )\n        >\n      >('strtonum');\n  late final _strtonum = _strtonumPtr\n      .asFunction<\n        int Function(\n          ffi.Pointer<ffi.Char>,\n          int,\n          int,\n          ffi.Pointer<ffi.Pointer<ffi.Char>>,\n        )\n      >();\n\n  int strtoq(\n    ffi.Pointer<ffi.Char> __str,\n    ffi.Pointer<ffi.Pointer<ffi.Char>> __endptr,\n    int __base,\n  ) {\n    return _strtoq(__str, __endptr, __base);\n  }\n\n  late final _strtoqPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.LongLong Function(\n            ffi.Pointer<ffi.Char>,\n            ffi.Pointer<ffi.Pointer<ffi.Char>>,\n            ffi.Int,\n          )\n        >\n      >('strtoq');\n  late final _strtoq = _strtoqPtr\n      .asFunction<\n        int Function(\n          ffi.Pointer<ffi.Char>,\n          ffi.Pointer<ffi.Pointer<ffi.Char>>,\n          int,\n        )\n      >();\n\n  int strtouq(\n    ffi.Pointer<ffi.Char> __str,\n    ffi.Pointer<ffi.Pointer<ffi.Char>> __endptr,\n    int __base,\n  ) {\n    return _strtouq(__str, __endptr, __base);\n  }\n\n  late final _strtouqPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.UnsignedLongLong Function(\n            ffi.Pointer<ffi.Char>,\n            ffi.Pointer<ffi.Pointer<ffi.Char>>,\n            ffi.Int,\n          )\n        >\n      >('strtouq');\n  late final _strtouq = _strtouqPtr\n      .asFunction<\n        int Function(\n          ffi.Pointer<ffi.Char>,\n          ffi.Pointer<ffi.Pointer<ffi.Char>>,\n          int,\n        )\n      >();\n\n  late final ffi.Pointer<ffi.Pointer<ffi.Char>> _suboptarg =\n      _lookup<ffi.Pointer<ffi.Char>>('suboptarg');\n\n  ffi.Pointer<ffi.Char> get suboptarg => _suboptarg.value;\n\n  set suboptarg(ffi.Pointer<ffi.Char> value) => _suboptarg.value = value;\n\n  void protect(protect_func fn, ffi.Pointer<ffi.Void> tun_interface, int fd) {\n    return _protect(fn, tun_interface, fd);\n  }\n\n  late final _protectPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Void Function(protect_func, ffi.Pointer<ffi.Void>, ffi.Int)\n        >\n      >('protect');\n  late final _protect = _protectPtr\n      .asFunction<void Function(protect_func, ffi.Pointer<ffi.Void>, int)>();\n\n  ffi.Pointer<ffi.Char> resolve_process(\n    resolve_process_func fn,\n    ffi.Pointer<ffi.Void> tun_interface,\n    int protocol,\n    ffi.Pointer<ffi.Char> source,\n    ffi.Pointer<ffi.Char> target,\n    int uid,\n  ) {\n    return _resolve_process(fn, tun_interface, protocol, source, target, uid);\n  }\n\n  late final _resolve_processPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.Char> Function(\n            resolve_process_func,\n            ffi.Pointer<ffi.Void>,\n            ffi.Int,\n            ffi.Pointer<ffi.Char>,\n            ffi.Pointer<ffi.Char>,\n            ffi.Int,\n          )\n        >\n      >('resolve_process');\n  late final _resolve_process = _resolve_processPtr\n      .asFunction<\n        ffi.Pointer<ffi.Char> Function(\n          resolve_process_func,\n          ffi.Pointer<ffi.Void>,\n          int,\n          ffi.Pointer<ffi.Char>,\n          ffi.Pointer<ffi.Char>,\n          int,\n        )\n      >();\n\n  void release_object(release_object_func fn, ffi.Pointer<ffi.Void> obj) {\n    return _release_object(fn, obj);\n  }\n\n  late final _release_objectPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Void Function(release_object_func, ffi.Pointer<ffi.Void>)\n        >\n      >('release_object');\n  late final _release_object = _release_objectPtr\n      .asFunction<void Function(release_object_func, ffi.Pointer<ffi.Void>)>();\n\n  void registerCallbacks(\n    protect_func markSocketFunc,\n    resolve_process_func resolveProcessFunc,\n    release_object_func releaseObjectFunc,\n  ) {\n    return _registerCallbacks(\n      markSocketFunc,\n      resolveProcessFunc,\n      releaseObjectFunc,\n    );\n  }\n\n  late final _registerCallbacksPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Void Function(\n            protect_func,\n            resolve_process_func,\n            release_object_func,\n          )\n        >\n      >('registerCallbacks');\n  late final _registerCallbacks = _registerCallbacksPtr\n      .asFunction<\n        void Function(protect_func, resolve_process_func, release_object_func)\n      >();\n\n  void initNativeApiBridge(ffi.Pointer<ffi.Void> api) {\n    return _initNativeApiBridge(api);\n  }\n\n  late final _initNativeApiBridgePtr =\n      _lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>>(\n        'initNativeApiBridge',\n      );\n  late final _initNativeApiBridge = _initNativeApiBridgePtr\n      .asFunction<void Function(ffi.Pointer<ffi.Void>)>();\n\n  void attachMessagePort(int mPort) {\n    return _attachMessagePort(mPort);\n  }\n\n  late final _attachMessagePortPtr =\n      _lookup<ffi.NativeFunction<ffi.Void Function(ffi.LongLong)>>(\n        'attachMessagePort',\n      );\n  late final _attachMessagePort = _attachMessagePortPtr\n      .asFunction<void Function(int)>();\n\n  ffi.Pointer<ffi.Char> getTraffic() {\n    return _getTraffic();\n  }\n\n  late final _getTrafficPtr =\n      _lookup<ffi.NativeFunction<ffi.Pointer<ffi.Char> Function()>>(\n        'getTraffic',\n      );\n  late final _getTraffic = _getTrafficPtr\n      .asFunction<ffi.Pointer<ffi.Char> Function()>();\n\n  ffi.Pointer<ffi.Char> getTotalTraffic() {\n    return _getTotalTraffic();\n  }\n\n  late final _getTotalTrafficPtr =\n      _lookup<ffi.NativeFunction<ffi.Pointer<ffi.Char> Function()>>(\n        'getTotalTraffic',\n      );\n  late final _getTotalTraffic = _getTotalTrafficPtr\n      .asFunction<ffi.Pointer<ffi.Char> Function()>();\n\n  void freeCString(ffi.Pointer<ffi.Char> s) {\n    return _freeCString(s);\n  }\n\n  late final _freeCStringPtr =\n      _lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Char>)>>(\n        'freeCString',\n      );\n  late final _freeCString = _freeCStringPtr\n      .asFunction<void Function(ffi.Pointer<ffi.Char>)>();\n\n  void invokeAction(ffi.Pointer<ffi.Char> paramsChar, int port) {\n    return _invokeAction(paramsChar, port);\n  }\n\n  late final _invokeActionPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Void Function(ffi.Pointer<ffi.Char>, ffi.LongLong)\n        >\n      >('invokeAction');\n  late final _invokeAction = _invokeActionPtr\n      .asFunction<void Function(ffi.Pointer<ffi.Char>, int)>();\n\n  ffi.Pointer<ffi.Char> getConfig(ffi.Pointer<ffi.Char> s) {\n    return _getConfig(s);\n  }\n\n  late final _getConfigPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Pointer<ffi.Char> Function(ffi.Pointer<ffi.Char>)\n        >\n      >('getConfig');\n  late final _getConfig = _getConfigPtr\n      .asFunction<ffi.Pointer<ffi.Char> Function(ffi.Pointer<ffi.Char>)>();\n\n  void startListener() {\n    return _startListener();\n  }\n\n  late final _startListenerPtr =\n      _lookup<ffi.NativeFunction<ffi.Void Function()>>('startListener');\n  late final _startListener = _startListenerPtr.asFunction<void Function()>();\n\n  void stopListener() {\n    return _stopListener();\n  }\n\n  late final _stopListenerPtr =\n      _lookup<ffi.NativeFunction<ffi.Void Function()>>('stopListener');\n  late final _stopListener = _stopListenerPtr.asFunction<void Function()>();\n\n  void quickStart(\n    ffi.Pointer<ffi.Char> initParamsChar,\n    ffi.Pointer<ffi.Char> paramsChar,\n    ffi.Pointer<ffi.Char> stateParamsChar,\n    int port,\n  ) {\n    return _quickStart(initParamsChar, paramsChar, stateParamsChar, port);\n  }\n\n  late final _quickStartPtr =\n      _lookup<\n        ffi.NativeFunction<\n          ffi.Void Function(\n            ffi.Pointer<ffi.Char>,\n            ffi.Pointer<ffi.Char>,\n            ffi.Pointer<ffi.Char>,\n            ffi.LongLong,\n          )\n        >\n      >('quickStart');\n  late final _quickStart = _quickStartPtr\n      .asFunction<\n        void Function(\n          ffi.Pointer<ffi.Char>,\n          ffi.Pointer<ffi.Char>,\n          ffi.Pointer<ffi.Char>,\n          int,\n        )\n      >();\n\n  int startTUN(int fd, ffi.Pointer<ffi.Void> callback) {\n    return _startTUN(fd, callback);\n  }\n\n  late final _startTUNPtr =\n      _lookup<\n        ffi.NativeFunction<GoUint8 Function(ffi.Int, ffi.Pointer<ffi.Void>)>\n      >('startTUN');\n  late final _startTUN = _startTUNPtr\n      .asFunction<int Function(int, ffi.Pointer<ffi.Void>)>();\n\n  ffi.Pointer<ffi.Char> getRunTime() {\n    return _getRunTime();\n  }\n\n  late final _getRunTimePtr =\n      _lookup<ffi.NativeFunction<ffi.Pointer<ffi.Char> Function()>>(\n        'getRunTime',\n      );\n  late final _getRunTime = _getRunTimePtr\n      .asFunction<ffi.Pointer<ffi.Char> Function()>();\n\n  void stopTun() {\n    return _stopTun();\n  }\n\n  late final _stopTunPtr = _lookup<ffi.NativeFunction<ffi.Void Function()>>(\n    'stopTun',\n  );\n  late final _stopTun = _stopTunPtr.asFunction<void Function()>();\n\n  ffi.Pointer<ffi.Char> getCurrentProfileName() {\n    return _getCurrentProfileName();\n  }\n\n  late final _getCurrentProfileNamePtr =\n      _lookup<ffi.NativeFunction<ffi.Pointer<ffi.Char> Function()>>(\n        'getCurrentProfileName',\n      );\n  late final _getCurrentProfileName = _getCurrentProfileNamePtr\n      .asFunction<ffi.Pointer<ffi.Char> Function()>();\n\n  ffi.Pointer<ffi.Char> getAndroidVpnOptions() {\n    return _getAndroidVpnOptions();\n  }\n\n  late final _getAndroidVpnOptionsPtr =\n      _lookup<ffi.NativeFunction<ffi.Pointer<ffi.Char> Function()>>(\n        'getAndroidVpnOptions',\n      );\n  late final _getAndroidVpnOptions = _getAndroidVpnOptionsPtr\n      .asFunction<ffi.Pointer<ffi.Char> Function()>();\n\n  void setState(ffi.Pointer<ffi.Char> s) {\n    return _setState(s);\n  }\n\n  late final _setStatePtr =\n      _lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Char>)>>(\n        'setState',\n      );\n  late final _setState = _setStatePtr\n      .asFunction<void Function(ffi.Pointer<ffi.Char>)>();\n\n  void updateDns(ffi.Pointer<ffi.Char> s) {\n    return _updateDns(s);\n  }\n\n  late final _updateDnsPtr =\n      _lookup<ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Char>)>>(\n        'updateDns',\n      );\n  late final _updateDns = _updateDnsPtr\n      .asFunction<void Function(ffi.Pointer<ffi.Char>)>();\n}\n\ntypedef __int8_t = ffi.SignedChar;\ntypedef Dart__int8_t = int;\ntypedef __uint8_t = ffi.UnsignedChar;\ntypedef Dart__uint8_t = int;\ntypedef __int16_t = ffi.Short;\ntypedef Dart__int16_t = int;\ntypedef __uint16_t = ffi.UnsignedShort;\ntypedef Dart__uint16_t = int;\ntypedef __int32_t = ffi.Int;\ntypedef Dart__int32_t = int;\ntypedef __uint32_t = ffi.UnsignedInt;\ntypedef Dart__uint32_t = int;\ntypedef __int64_t = ffi.LongLong;\ntypedef Dart__int64_t = int;\ntypedef __uint64_t = ffi.UnsignedLongLong;\ntypedef Dart__uint64_t = int;\ntypedef __darwin_intptr_t = ffi.Long;\ntypedef Dart__darwin_intptr_t = int;\ntypedef __darwin_natural_t = ffi.UnsignedInt;\ntypedef Dart__darwin_natural_t = int;\ntypedef __darwin_ct_rune_t = ffi.Int;\ntypedef Dart__darwin_ct_rune_t = int;\n\nfinal class __mbstate_t extends ffi.Union {\n  @ffi.Array.multi([128])\n  external ffi.Array<ffi.Char> __mbstate8;\n\n  @ffi.LongLong()\n  external int _mbstateL;\n}\n\ntypedef __darwin_mbstate_t = __mbstate_t;\ntypedef __darwin_ptrdiff_t = ffi.Long;\ntypedef Dart__darwin_ptrdiff_t = int;\ntypedef __darwin_size_t = ffi.UnsignedLong;\ntypedef Dart__darwin_size_t = int;\ntypedef __builtin_va_list = ffi.Pointer<ffi.Char>;\ntypedef __darwin_va_list = __builtin_va_list;\ntypedef __darwin_wchar_t = ffi.Int;\ntypedef Dart__darwin_wchar_t = int;\ntypedef __darwin_rune_t = __darwin_wchar_t;\ntypedef __darwin_wint_t = ffi.Int;\ntypedef Dart__darwin_wint_t = int;\ntypedef __darwin_clock_t = ffi.UnsignedLong;\ntypedef Dart__darwin_clock_t = int;\ntypedef __darwin_socklen_t = __uint32_t;\ntypedef __darwin_ssize_t = ffi.Long;\ntypedef Dart__darwin_ssize_t = int;\ntypedef __darwin_time_t = ffi.Long;\ntypedef Dart__darwin_time_t = int;\ntypedef __darwin_blkcnt_t = __int64_t;\ntypedef __darwin_blksize_t = __int32_t;\ntypedef __darwin_dev_t = __int32_t;\ntypedef __darwin_fsblkcnt_t = ffi.UnsignedInt;\ntypedef Dart__darwin_fsblkcnt_t = int;\ntypedef __darwin_fsfilcnt_t = ffi.UnsignedInt;\ntypedef Dart__darwin_fsfilcnt_t = int;\ntypedef __darwin_gid_t = __uint32_t;\ntypedef __darwin_id_t = __uint32_t;\ntypedef __darwin_ino64_t = __uint64_t;\ntypedef __darwin_ino_t = __darwin_ino64_t;\ntypedef __darwin_mach_port_name_t = __darwin_natural_t;\ntypedef __darwin_mach_port_t = __darwin_mach_port_name_t;\ntypedef __darwin_mode_t = __uint16_t;\ntypedef __darwin_off_t = __int64_t;\ntypedef __darwin_pid_t = __int32_t;\ntypedef __darwin_sigset_t = __uint32_t;\ntypedef __darwin_suseconds_t = __int32_t;\ntypedef __darwin_uid_t = __uint32_t;\ntypedef __darwin_useconds_t = __uint32_t;\n\nfinal class __darwin_pthread_handler_rec extends ffi.Struct {\n  external ffi.Pointer<\n    ffi.NativeFunction<ffi.Void Function(ffi.Pointer<ffi.Void>)>\n  >\n  __routine;\n\n  external ffi.Pointer<ffi.Void> __arg;\n\n  external ffi.Pointer<__darwin_pthread_handler_rec> __next;\n}\n\nfinal class _opaque_pthread_attr_t extends ffi.Struct {\n  @ffi.Long()\n  external int __sig;\n\n  @ffi.Array.multi([56])\n  external ffi.Array<ffi.Char> __opaque;\n}\n\nfinal class _opaque_pthread_cond_t extends ffi.Struct {\n  @ffi.Long()\n  external int __sig;\n\n  @ffi.Array.multi([40])\n  external ffi.Array<ffi.Char> __opaque;\n}\n\nfinal class _opaque_pthread_condattr_t extends ffi.Struct {\n  @ffi.Long()\n  external int __sig;\n\n  @ffi.Array.multi([8])\n  external ffi.Array<ffi.Char> __opaque;\n}\n\nfinal class _opaque_pthread_mutex_t extends ffi.Struct {\n  @ffi.Long()\n  external int __sig;\n\n  @ffi.Array.multi([56])\n  external ffi.Array<ffi.Char> __opaque;\n}\n\nfinal class _opaque_pthread_mutexattr_t extends ffi.Struct {\n  @ffi.Long()\n  external int __sig;\n\n  @ffi.Array.multi([8])\n  external ffi.Array<ffi.Char> __opaque;\n}\n\nfinal class _opaque_pthread_once_t extends ffi.Struct {\n  @ffi.Long()\n  external int __sig;\n\n  @ffi.Array.multi([8])\n  external ffi.Array<ffi.Char> __opaque;\n}\n\nfinal class _opaque_pthread_rwlock_t extends ffi.Struct {\n  @ffi.Long()\n  external int __sig;\n\n  @ffi.Array.multi([192])\n  external ffi.Array<ffi.Char> __opaque;\n}\n\nfinal class _opaque_pthread_rwlockattr_t extends ffi.Struct {\n  @ffi.Long()\n  external int __sig;\n\n  @ffi.Array.multi([16])\n  external ffi.Array<ffi.Char> __opaque;\n}\n\nfinal class _opaque_pthread_t extends ffi.Struct {\n  @ffi.Long()\n  external int __sig;\n\n  external ffi.Pointer<__darwin_pthread_handler_rec> __cleanup_stack;\n\n  @ffi.Array.multi([8176])\n  external ffi.Array<ffi.Char> __opaque;\n}\n\ntypedef __darwin_pthread_attr_t = _opaque_pthread_attr_t;\ntypedef __darwin_pthread_cond_t = _opaque_pthread_cond_t;\ntypedef __darwin_pthread_condattr_t = _opaque_pthread_condattr_t;\ntypedef __darwin_pthread_key_t = ffi.UnsignedLong;\ntypedef Dart__darwin_pthread_key_t = int;\ntypedef __darwin_pthread_mutex_t = _opaque_pthread_mutex_t;\ntypedef __darwin_pthread_mutexattr_t = _opaque_pthread_mutexattr_t;\ntypedef __darwin_pthread_once_t = _opaque_pthread_once_t;\ntypedef __darwin_pthread_rwlock_t = _opaque_pthread_rwlock_t;\ntypedef __darwin_pthread_rwlockattr_t = _opaque_pthread_rwlockattr_t;\ntypedef __darwin_pthread_t = ffi.Pointer<_opaque_pthread_t>;\ntypedef __darwin_nl_item = ffi.Int;\ntypedef Dart__darwin_nl_item = int;\ntypedef __darwin_wctrans_t = ffi.Int;\ntypedef Dart__darwin_wctrans_t = int;\ntypedef __darwin_wctype_t = __uint32_t;\ntypedef u_int8_t = ffi.UnsignedChar;\ntypedef Dartu_int8_t = int;\ntypedef u_int16_t = ffi.UnsignedShort;\ntypedef Dartu_int16_t = int;\ntypedef u_int32_t = ffi.UnsignedInt;\ntypedef Dartu_int32_t = int;\ntypedef u_int64_t = ffi.UnsignedLongLong;\ntypedef Dartu_int64_t = int;\ntypedef register_t = ffi.Int64;\ntypedef Dartregister_t = int;\ntypedef user_addr_t = u_int64_t;\ntypedef user_size_t = u_int64_t;\ntypedef user_ssize_t = ffi.Int64;\ntypedef Dartuser_ssize_t = int;\ntypedef user_long_t = ffi.Int64;\ntypedef Dartuser_long_t = int;\ntypedef user_ulong_t = u_int64_t;\ntypedef user_time_t = ffi.Int64;\ntypedef Dartuser_time_t = int;\ntypedef user_off_t = ffi.Int64;\ntypedef Dartuser_off_t = int;\ntypedef syscall_arg_t = u_int64_t;\ntypedef ptrdiff_t = __darwin_ptrdiff_t;\ntypedef rsize_t = __darwin_size_t;\ntypedef wint_t = __darwin_wint_t;\n\nfinal class _GoString_ extends ffi.Struct {\n  external ffi.Pointer<ffi.Char> p;\n\n  @ptrdiff_t()\n  external int n;\n}\n\nenum idtype_t {\n  P_ALL(0),\n  P_PID(1),\n  P_PGID(2);\n\n  final int value;\n  const idtype_t(this.value);\n\n  static idtype_t fromValue(int value) => switch (value) {\n    0 => P_ALL,\n    1 => P_PID,\n    2 => P_PGID,\n    _ => throw ArgumentError('Unknown value for idtype_t: $value'),\n  };\n}\n\ntypedef pid_t = __darwin_pid_t;\ntypedef id_t = __darwin_id_t;\ntypedef sig_atomic_t = ffi.Int;\ntypedef Dartsig_atomic_t = int;\n\nfinal class __darwin_arm_exception_state extends ffi.Struct {\n  @__uint32_t()\n  external int __exception;\n\n  @__uint32_t()\n  external int __fsr;\n\n  @__uint32_t()\n  external int __far;\n}\n\nfinal class __darwin_arm_exception_state64 extends ffi.Struct {\n  @__uint64_t()\n  external int __far;\n\n  @__uint32_t()\n  external int __esr;\n\n  @__uint32_t()\n  external int __exception;\n}\n\nfinal class __darwin_arm_exception_state64_v2 extends ffi.Struct {\n  @__uint64_t()\n  external int __far;\n\n  @__uint64_t()\n  external int __esr;\n}\n\nfinal class __darwin_arm_thread_state extends ffi.Struct {\n  @ffi.Array.multi([13])\n  external ffi.Array<__uint32_t> __r;\n\n  @__uint32_t()\n  external int __sp;\n\n  @__uint32_t()\n  external int __lr;\n\n  @__uint32_t()\n  external int __pc;\n\n  @__uint32_t()\n  external int __cpsr;\n}\n\nfinal class __darwin_arm_thread_state64 extends ffi.Struct {\n  @ffi.Array.multi([29])\n  external ffi.Array<__uint64_t> __x;\n\n  @__uint64_t()\n  external int __fp;\n\n  @__uint64_t()\n  external int __lr;\n\n  @__uint64_t()\n  external int __sp;\n\n  @__uint64_t()\n  external int __pc;\n\n  @__uint32_t()\n  external int __cpsr;\n\n  @__uint32_t()\n  external int __pad;\n}\n\nfinal class __darwin_arm_vfp_state extends ffi.Struct {\n  @ffi.Array.multi([64])\n  external ffi.Array<__uint32_t> __r;\n\n  @__uint32_t()\n  external int __fpscr;\n}\n\nfinal class __darwin_arm_neon_state64 extends ffi.Opaque {}\n\nfinal class __darwin_arm_neon_state extends ffi.Opaque {}\n\nfinal class __arm_pagein_state extends ffi.Struct {\n  @ffi.Int()\n  external int __pagein_error;\n}\n\nfinal class __arm_legacy_debug_state extends ffi.Struct {\n  @ffi.Array.multi([16])\n  external ffi.Array<__uint32_t> __bvr;\n\n  @ffi.Array.multi([16])\n  external ffi.Array<__uint32_t> __bcr;\n\n  @ffi.Array.multi([16])\n  external ffi.Array<__uint32_t> __wvr;\n\n  @ffi.Array.multi([16])\n  external ffi.Array<__uint32_t> __wcr;\n}\n\nfinal class __darwin_arm_debug_state32 extends ffi.Struct {\n  @ffi.Array.multi([16])\n  external ffi.Array<__uint32_t> __bvr;\n\n  @ffi.Array.multi([16])\n  external ffi.Array<__uint32_t> __bcr;\n\n  @ffi.Array.multi([16])\n  external ffi.Array<__uint32_t> __wvr;\n\n  @ffi.Array.multi([16])\n  external ffi.Array<__uint32_t> __wcr;\n\n  @__uint64_t()\n  external int __mdscr_el1;\n}\n\nfinal class __darwin_arm_debug_state64 extends ffi.Struct {\n  @ffi.Array.multi([16])\n  external ffi.Array<__uint64_t> __bvr;\n\n  @ffi.Array.multi([16])\n  external ffi.Array<__uint64_t> __bcr;\n\n  @ffi.Array.multi([16])\n  external ffi.Array<__uint64_t> __wvr;\n\n  @ffi.Array.multi([16])\n  external ffi.Array<__uint64_t> __wcr;\n\n  @__uint64_t()\n  external int __mdscr_el1;\n}\n\nfinal class __darwin_arm_cpmu_state64 extends ffi.Struct {\n  @ffi.Array.multi([16])\n  external ffi.Array<__uint64_t> __ctrs;\n}\n\nfinal class __darwin_mcontext32 extends ffi.Struct {\n  external __darwin_arm_exception_state __es;\n\n  external __darwin_arm_thread_state __ss;\n\n  external __darwin_arm_vfp_state __fs;\n}\n\nfinal class __darwin_mcontext64 extends ffi.Opaque {}\n\ntypedef mcontext_t = ffi.Pointer<__darwin_mcontext64>;\ntypedef pthread_attr_t = __darwin_pthread_attr_t;\n\nfinal class __darwin_sigaltstack extends ffi.Struct {\n  external ffi.Pointer<ffi.Void> ss_sp;\n\n  @__darwin_size_t()\n  external int ss_size;\n\n  @ffi.Int()\n  external int ss_flags;\n}\n\ntypedef stack_t = __darwin_sigaltstack;\n\nfinal class __darwin_ucontext extends ffi.Struct {\n  @ffi.Int()\n  external int uc_onstack;\n\n  @__darwin_sigset_t()\n  external int uc_sigmask;\n\n  external __darwin_sigaltstack uc_stack;\n\n  external ffi.Pointer<__darwin_ucontext> uc_link;\n\n  @__darwin_size_t()\n  external int uc_mcsize;\n\n  external ffi.Pointer<__darwin_mcontext64> uc_mcontext;\n}\n\ntypedef ucontext_t = __darwin_ucontext;\ntypedef sigset_t = __darwin_sigset_t;\ntypedef uid_t = __darwin_uid_t;\n\nfinal class sigval extends ffi.Union {\n  @ffi.Int()\n  external int sival_int;\n\n  external ffi.Pointer<ffi.Void> sival_ptr;\n}\n\nfinal class sigevent extends ffi.Struct {\n  @ffi.Int()\n  external int sigev_notify;\n\n  @ffi.Int()\n  external int sigev_signo;\n\n  external sigval sigev_value;\n\n  external ffi.Pointer<ffi.NativeFunction<ffi.Void Function(sigval)>>\n  sigev_notify_function;\n\n  external ffi.Pointer<pthread_attr_t> sigev_notify_attributes;\n}\n\nfinal class __siginfo extends ffi.Struct {\n  @ffi.Int()\n  external int si_signo;\n\n  @ffi.Int()\n  external int si_errno;\n\n  @ffi.Int()\n  external int si_code;\n\n  @pid_t()\n  external int si_pid;\n\n  @uid_t()\n  external int si_uid;\n\n  @ffi.Int()\n  external int si_status;\n\n  external ffi.Pointer<ffi.Void> si_addr;\n\n  external sigval si_value;\n\n  @ffi.Long()\n  external int si_band;\n\n  @ffi.Array.multi([7])\n  external ffi.Array<ffi.UnsignedLong> __pad;\n}\n\ntypedef siginfo_t = __siginfo;\n\nfinal class __sigaction_u extends ffi.Union {\n  external ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Int)>>\n  __sa_handler;\n\n  external ffi.Pointer<\n    ffi.NativeFunction<\n      ffi.Void Function(ffi.Int, ffi.Pointer<__siginfo>, ffi.Pointer<ffi.Void>)\n    >\n  >\n  __sa_sigaction;\n}\n\nfinal class __sigaction extends ffi.Struct {\n  external __sigaction_u __sigaction_u$1;\n\n  external ffi.Pointer<\n    ffi.NativeFunction<\n      ffi.Void Function(\n        ffi.Pointer<ffi.Void>,\n        ffi.Int,\n        ffi.Int,\n        ffi.Pointer<siginfo_t>,\n        ffi.Pointer<ffi.Void>,\n      )\n    >\n  >\n  sa_tramp;\n\n  @sigset_t()\n  external int sa_mask;\n\n  @ffi.Int()\n  external int sa_flags;\n}\n\nfinal class sigaction extends ffi.Struct {\n  external __sigaction_u __sigaction_u$1;\n\n  @sigset_t()\n  external int sa_mask;\n\n  @ffi.Int()\n  external int sa_flags;\n}\n\ntypedef sig_tFunction = ffi.Void Function(ffi.Int);\ntypedef Dartsig_tFunction = void Function(int);\ntypedef sig_t = ffi.Pointer<ffi.NativeFunction<sig_tFunction>>;\n\nfinal class sigvec extends ffi.Struct {\n  external ffi.Pointer<ffi.NativeFunction<ffi.Void Function(ffi.Int)>>\n  sv_handler;\n\n  @ffi.Int()\n  external int sv_mask;\n\n  @ffi.Int()\n  external int sv_flags;\n}\n\nfinal class sigstack extends ffi.Struct {\n  external ffi.Pointer<ffi.Char> ss_sp;\n\n  @ffi.Int()\n  external int ss_onstack;\n}\n\ntypedef int_least8_t = ffi.Int8;\ntypedef Dartint_least8_t = int;\ntypedef int_least16_t = ffi.Int16;\ntypedef Dartint_least16_t = int;\ntypedef int_least32_t = ffi.Int32;\ntypedef Dartint_least32_t = int;\ntypedef int_least64_t = ffi.Int64;\ntypedef Dartint_least64_t = int;\ntypedef uint_least8_t = ffi.Uint8;\ntypedef Dartuint_least8_t = int;\ntypedef uint_least16_t = ffi.Uint16;\ntypedef Dartuint_least16_t = int;\ntypedef uint_least32_t = ffi.Uint32;\ntypedef Dartuint_least32_t = int;\ntypedef uint_least64_t = ffi.Uint64;\ntypedef Dartuint_least64_t = int;\ntypedef int_fast8_t = ffi.Int8;\ntypedef Dartint_fast8_t = int;\ntypedef int_fast16_t = ffi.Int16;\ntypedef Dartint_fast16_t = int;\ntypedef int_fast32_t = ffi.Int32;\ntypedef Dartint_fast32_t = int;\ntypedef int_fast64_t = ffi.Int64;\ntypedef Dartint_fast64_t = int;\ntypedef uint_fast8_t = ffi.Uint8;\ntypedef Dartuint_fast8_t = int;\ntypedef uint_fast16_t = ffi.Uint16;\ntypedef Dartuint_fast16_t = int;\ntypedef uint_fast32_t = ffi.Uint32;\ntypedef Dartuint_fast32_t = int;\ntypedef uint_fast64_t = ffi.Uint64;\ntypedef Dartuint_fast64_t = int;\ntypedef intmax_t = ffi.Long;\ntypedef Dartintmax_t = int;\ntypedef uintmax_t = ffi.UnsignedLong;\ntypedef Dartuintmax_t = int;\n\nfinal class timeval extends ffi.Struct {\n  @__darwin_time_t()\n  external int tv_sec;\n\n  @__darwin_suseconds_t()\n  external int tv_usec;\n}\n\ntypedef rlim_t = __uint64_t;\n\nfinal class rusage extends ffi.Struct {\n  external timeval ru_utime;\n\n  external timeval ru_stime;\n\n  @ffi.Long()\n  external int ru_maxrss;\n\n  @ffi.Long()\n  external int ru_ixrss;\n\n  @ffi.Long()\n  external int ru_idrss;\n\n  @ffi.Long()\n  external int ru_isrss;\n\n  @ffi.Long()\n  external int ru_minflt;\n\n  @ffi.Long()\n  external int ru_majflt;\n\n  @ffi.Long()\n  external int ru_nswap;\n\n  @ffi.Long()\n  external int ru_inblock;\n\n  @ffi.Long()\n  external int ru_oublock;\n\n  @ffi.Long()\n  external int ru_msgsnd;\n\n  @ffi.Long()\n  external int ru_msgrcv;\n\n  @ffi.Long()\n  external int ru_nsignals;\n\n  @ffi.Long()\n  external int ru_nvcsw;\n\n  @ffi.Long()\n  external int ru_nivcsw;\n}\n\ntypedef rusage_info_t = ffi.Pointer<ffi.Void>;\n\nfinal class rusage_info_v0 extends ffi.Struct {\n  @ffi.Array.multi([16])\n  external ffi.Array<ffi.Uint8> ri_uuid;\n\n  @ffi.Uint64()\n  external int ri_user_time;\n\n  @ffi.Uint64()\n  external int ri_system_time;\n\n  @ffi.Uint64()\n  external int ri_pkg_idle_wkups;\n\n  @ffi.Uint64()\n  external int ri_interrupt_wkups;\n\n  @ffi.Uint64()\n  external int ri_pageins;\n\n  @ffi.Uint64()\n  external int ri_wired_size;\n\n  @ffi.Uint64()\n  external int ri_resident_size;\n\n  @ffi.Uint64()\n  external int ri_phys_footprint;\n\n  @ffi.Uint64()\n  external int ri_proc_start_abstime;\n\n  @ffi.Uint64()\n  external int ri_proc_exit_abstime;\n}\n\nfinal class rusage_info_v1 extends ffi.Struct {\n  @ffi.Array.multi([16])\n  external ffi.Array<ffi.Uint8> ri_uuid;\n\n  @ffi.Uint64()\n  external int ri_user_time;\n\n  @ffi.Uint64()\n  external int ri_system_time;\n\n  @ffi.Uint64()\n  external int ri_pkg_idle_wkups;\n\n  @ffi.Uint64()\n  external int ri_interrupt_wkups;\n\n  @ffi.Uint64()\n  external int ri_pageins;\n\n  @ffi.Uint64()\n  external int ri_wired_size;\n\n  @ffi.Uint64()\n  external int ri_resident_size;\n\n  @ffi.Uint64()\n  external int ri_phys_footprint;\n\n  @ffi.Uint64()\n  external int ri_proc_start_abstime;\n\n  @ffi.Uint64()\n  external int ri_proc_exit_abstime;\n\n  @ffi.Uint64()\n  external int ri_child_user_time;\n\n  @ffi.Uint64()\n  external int ri_child_system_time;\n\n  @ffi.Uint64()\n  external int ri_child_pkg_idle_wkups;\n\n  @ffi.Uint64()\n  external int ri_child_interrupt_wkups;\n\n  @ffi.Uint64()\n  external int ri_child_pageins;\n\n  @ffi.Uint64()\n  external int ri_child_elapsed_abstime;\n}\n\nfinal class rusage_info_v2 extends ffi.Struct {\n  @ffi.Array.multi([16])\n  external ffi.Array<ffi.Uint8> ri_uuid;\n\n  @ffi.Uint64()\n  external int ri_user_time;\n\n  @ffi.Uint64()\n  external int ri_system_time;\n\n  @ffi.Uint64()\n  external int ri_pkg_idle_wkups;\n\n  @ffi.Uint64()\n  external int ri_interrupt_wkups;\n\n  @ffi.Uint64()\n  external int ri_pageins;\n\n  @ffi.Uint64()\n  external int ri_wired_size;\n\n  @ffi.Uint64()\n  external int ri_resident_size;\n\n  @ffi.Uint64()\n  external int ri_phys_footprint;\n\n  @ffi.Uint64()\n  external int ri_proc_start_abstime;\n\n  @ffi.Uint64()\n  external int ri_proc_exit_abstime;\n\n  @ffi.Uint64()\n  external int ri_child_user_time;\n\n  @ffi.Uint64()\n  external int ri_child_system_time;\n\n  @ffi.Uint64()\n  external int ri_child_pkg_idle_wkups;\n\n  @ffi.Uint64()\n  external int ri_child_interrupt_wkups;\n\n  @ffi.Uint64()\n  external int ri_child_pageins;\n\n  @ffi.Uint64()\n  external int ri_child_elapsed_abstime;\n\n  @ffi.Uint64()\n  external int ri_diskio_bytesread;\n\n  @ffi.Uint64()\n  external int ri_diskio_byteswritten;\n}\n\nfinal class rusage_info_v3 extends ffi.Struct {\n  @ffi.Array.multi([16])\n  external ffi.Array<ffi.Uint8> ri_uuid;\n\n  @ffi.Uint64()\n  external int ri_user_time;\n\n  @ffi.Uint64()\n  external int ri_system_time;\n\n  @ffi.Uint64()\n  external int ri_pkg_idle_wkups;\n\n  @ffi.Uint64()\n  external int ri_interrupt_wkups;\n\n  @ffi.Uint64()\n  external int ri_pageins;\n\n  @ffi.Uint64()\n  external int ri_wired_size;\n\n  @ffi.Uint64()\n  external int ri_resident_size;\n\n  @ffi.Uint64()\n  external int ri_phys_footprint;\n\n  @ffi.Uint64()\n  external int ri_proc_start_abstime;\n\n  @ffi.Uint64()\n  external int ri_proc_exit_abstime;\n\n  @ffi.Uint64()\n  external int ri_child_user_time;\n\n  @ffi.Uint64()\n  external int ri_child_system_time;\n\n  @ffi.Uint64()\n  external int ri_child_pkg_idle_wkups;\n\n  @ffi.Uint64()\n  external int ri_child_interrupt_wkups;\n\n  @ffi.Uint64()\n  external int ri_child_pageins;\n\n  @ffi.Uint64()\n  external int ri_child_elapsed_abstime;\n\n  @ffi.Uint64()\n  external int ri_diskio_bytesread;\n\n  @ffi.Uint64()\n  external int ri_diskio_byteswritten;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_default;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_maintenance;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_background;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_utility;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_legacy;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_user_initiated;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_user_interactive;\n\n  @ffi.Uint64()\n  external int ri_billed_system_time;\n\n  @ffi.Uint64()\n  external int ri_serviced_system_time;\n}\n\nfinal class rusage_info_v4 extends ffi.Struct {\n  @ffi.Array.multi([16])\n  external ffi.Array<ffi.Uint8> ri_uuid;\n\n  @ffi.Uint64()\n  external int ri_user_time;\n\n  @ffi.Uint64()\n  external int ri_system_time;\n\n  @ffi.Uint64()\n  external int ri_pkg_idle_wkups;\n\n  @ffi.Uint64()\n  external int ri_interrupt_wkups;\n\n  @ffi.Uint64()\n  external int ri_pageins;\n\n  @ffi.Uint64()\n  external int ri_wired_size;\n\n  @ffi.Uint64()\n  external int ri_resident_size;\n\n  @ffi.Uint64()\n  external int ri_phys_footprint;\n\n  @ffi.Uint64()\n  external int ri_proc_start_abstime;\n\n  @ffi.Uint64()\n  external int ri_proc_exit_abstime;\n\n  @ffi.Uint64()\n  external int ri_child_user_time;\n\n  @ffi.Uint64()\n  external int ri_child_system_time;\n\n  @ffi.Uint64()\n  external int ri_child_pkg_idle_wkups;\n\n  @ffi.Uint64()\n  external int ri_child_interrupt_wkups;\n\n  @ffi.Uint64()\n  external int ri_child_pageins;\n\n  @ffi.Uint64()\n  external int ri_child_elapsed_abstime;\n\n  @ffi.Uint64()\n  external int ri_diskio_bytesread;\n\n  @ffi.Uint64()\n  external int ri_diskio_byteswritten;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_default;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_maintenance;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_background;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_utility;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_legacy;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_user_initiated;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_user_interactive;\n\n  @ffi.Uint64()\n  external int ri_billed_system_time;\n\n  @ffi.Uint64()\n  external int ri_serviced_system_time;\n\n  @ffi.Uint64()\n  external int ri_logical_writes;\n\n  @ffi.Uint64()\n  external int ri_lifetime_max_phys_footprint;\n\n  @ffi.Uint64()\n  external int ri_instructions;\n\n  @ffi.Uint64()\n  external int ri_cycles;\n\n  @ffi.Uint64()\n  external int ri_billed_energy;\n\n  @ffi.Uint64()\n  external int ri_serviced_energy;\n\n  @ffi.Uint64()\n  external int ri_interval_max_phys_footprint;\n\n  @ffi.Uint64()\n  external int ri_runnable_time;\n}\n\nfinal class rusage_info_v5 extends ffi.Struct {\n  @ffi.Array.multi([16])\n  external ffi.Array<ffi.Uint8> ri_uuid;\n\n  @ffi.Uint64()\n  external int ri_user_time;\n\n  @ffi.Uint64()\n  external int ri_system_time;\n\n  @ffi.Uint64()\n  external int ri_pkg_idle_wkups;\n\n  @ffi.Uint64()\n  external int ri_interrupt_wkups;\n\n  @ffi.Uint64()\n  external int ri_pageins;\n\n  @ffi.Uint64()\n  external int ri_wired_size;\n\n  @ffi.Uint64()\n  external int ri_resident_size;\n\n  @ffi.Uint64()\n  external int ri_phys_footprint;\n\n  @ffi.Uint64()\n  external int ri_proc_start_abstime;\n\n  @ffi.Uint64()\n  external int ri_proc_exit_abstime;\n\n  @ffi.Uint64()\n  external int ri_child_user_time;\n\n  @ffi.Uint64()\n  external int ri_child_system_time;\n\n  @ffi.Uint64()\n  external int ri_child_pkg_idle_wkups;\n\n  @ffi.Uint64()\n  external int ri_child_interrupt_wkups;\n\n  @ffi.Uint64()\n  external int ri_child_pageins;\n\n  @ffi.Uint64()\n  external int ri_child_elapsed_abstime;\n\n  @ffi.Uint64()\n  external int ri_diskio_bytesread;\n\n  @ffi.Uint64()\n  external int ri_diskio_byteswritten;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_default;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_maintenance;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_background;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_utility;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_legacy;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_user_initiated;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_user_interactive;\n\n  @ffi.Uint64()\n  external int ri_billed_system_time;\n\n  @ffi.Uint64()\n  external int ri_serviced_system_time;\n\n  @ffi.Uint64()\n  external int ri_logical_writes;\n\n  @ffi.Uint64()\n  external int ri_lifetime_max_phys_footprint;\n\n  @ffi.Uint64()\n  external int ri_instructions;\n\n  @ffi.Uint64()\n  external int ri_cycles;\n\n  @ffi.Uint64()\n  external int ri_billed_energy;\n\n  @ffi.Uint64()\n  external int ri_serviced_energy;\n\n  @ffi.Uint64()\n  external int ri_interval_max_phys_footprint;\n\n  @ffi.Uint64()\n  external int ri_runnable_time;\n\n  @ffi.Uint64()\n  external int ri_flags;\n}\n\nfinal class rusage_info_v6 extends ffi.Struct {\n  @ffi.Array.multi([16])\n  external ffi.Array<ffi.Uint8> ri_uuid;\n\n  @ffi.Uint64()\n  external int ri_user_time;\n\n  @ffi.Uint64()\n  external int ri_system_time;\n\n  @ffi.Uint64()\n  external int ri_pkg_idle_wkups;\n\n  @ffi.Uint64()\n  external int ri_interrupt_wkups;\n\n  @ffi.Uint64()\n  external int ri_pageins;\n\n  @ffi.Uint64()\n  external int ri_wired_size;\n\n  @ffi.Uint64()\n  external int ri_resident_size;\n\n  @ffi.Uint64()\n  external int ri_phys_footprint;\n\n  @ffi.Uint64()\n  external int ri_proc_start_abstime;\n\n  @ffi.Uint64()\n  external int ri_proc_exit_abstime;\n\n  @ffi.Uint64()\n  external int ri_child_user_time;\n\n  @ffi.Uint64()\n  external int ri_child_system_time;\n\n  @ffi.Uint64()\n  external int ri_child_pkg_idle_wkups;\n\n  @ffi.Uint64()\n  external int ri_child_interrupt_wkups;\n\n  @ffi.Uint64()\n  external int ri_child_pageins;\n\n  @ffi.Uint64()\n  external int ri_child_elapsed_abstime;\n\n  @ffi.Uint64()\n  external int ri_diskio_bytesread;\n\n  @ffi.Uint64()\n  external int ri_diskio_byteswritten;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_default;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_maintenance;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_background;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_utility;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_legacy;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_user_initiated;\n\n  @ffi.Uint64()\n  external int ri_cpu_time_qos_user_interactive;\n\n  @ffi.Uint64()\n  external int ri_billed_system_time;\n\n  @ffi.Uint64()\n  external int ri_serviced_system_time;\n\n  @ffi.Uint64()\n  external int ri_logical_writes;\n\n  @ffi.Uint64()\n  external int ri_lifetime_max_phys_footprint;\n\n  @ffi.Uint64()\n  external int ri_instructions;\n\n  @ffi.Uint64()\n  external int ri_cycles;\n\n  @ffi.Uint64()\n  external int ri_billed_energy;\n\n  @ffi.Uint64()\n  external int ri_serviced_energy;\n\n  @ffi.Uint64()\n  external int ri_interval_max_phys_footprint;\n\n  @ffi.Uint64()\n  external int ri_runnable_time;\n\n  @ffi.Uint64()\n  external int ri_flags;\n\n  @ffi.Uint64()\n  external int ri_user_ptime;\n\n  @ffi.Uint64()\n  external int ri_system_ptime;\n\n  @ffi.Uint64()\n  external int ri_pinstructions;\n\n  @ffi.Uint64()\n  external int ri_pcycles;\n\n  @ffi.Uint64()\n  external int ri_energy_nj;\n\n  @ffi.Uint64()\n  external int ri_penergy_nj;\n\n  @ffi.Uint64()\n  external int ri_secure_time_in_system;\n\n  @ffi.Uint64()\n  external int ri_secure_ptime_in_system;\n\n  @ffi.Uint64()\n  external int ri_neural_footprint;\n\n  @ffi.Uint64()\n  external int ri_lifetime_max_neural_footprint;\n\n  @ffi.Uint64()\n  external int ri_interval_max_neural_footprint;\n\n  @ffi.Array.multi([9])\n  external ffi.Array<ffi.Uint64> ri_reserved;\n}\n\ntypedef rusage_info_current = rusage_info_v6;\n\nfinal class rlimit extends ffi.Struct {\n  @rlim_t()\n  external int rlim_cur;\n\n  @rlim_t()\n  external int rlim_max;\n}\n\nfinal class proc_rlimit_control_wakeupmon extends ffi.Struct {\n  @ffi.Uint32()\n  external int wm_flags;\n\n  @ffi.Int32()\n  external int wm_rate;\n}\n\nfinal class wait extends ffi.Opaque {}\n\ntypedef ct_rune_t = __darwin_ct_rune_t;\ntypedef rune_t = __darwin_rune_t;\n\nfinal class div_t extends ffi.Struct {\n  @ffi.Int()\n  external int quot;\n\n  @ffi.Int()\n  external int rem;\n}\n\nfinal class ldiv_t extends ffi.Struct {\n  @ffi.Long()\n  external int quot;\n\n  @ffi.Long()\n  external int rem;\n}\n\nfinal class lldiv_t extends ffi.Struct {\n  @ffi.LongLong()\n  external int quot;\n\n  @ffi.LongLong()\n  external int rem;\n}\n\ntypedef malloc_type_id_t = ffi.UnsignedLongLong;\ntypedef Dartmalloc_type_id_t = int;\n\nfinal class _malloc_zone_t extends ffi.Opaque {}\n\ntypedef malloc_zone_t = _malloc_zone_t;\ntypedef dev_t = __darwin_dev_t;\ntypedef mode_t = __darwin_mode_t;\ntypedef release_object_funcFunction =\n    ffi.Void Function(ffi.Pointer<ffi.Void> obj);\ntypedef Dartrelease_object_funcFunction =\n    void Function(ffi.Pointer<ffi.Void> obj);\ntypedef release_object_func =\n    ffi.Pointer<ffi.NativeFunction<release_object_funcFunction>>;\ntypedef protect_funcFunction =\n    ffi.Void Function(ffi.Pointer<ffi.Void> tun_interface, ffi.Int fd);\ntypedef Dartprotect_funcFunction =\n    void Function(ffi.Pointer<ffi.Void> tun_interface, int fd);\ntypedef protect_func = ffi.Pointer<ffi.NativeFunction<protect_funcFunction>>;\ntypedef resolve_process_funcFunction =\n    ffi.Pointer<ffi.Char> Function(\n      ffi.Pointer<ffi.Void> tun_interface,\n      ffi.Int protocol,\n      ffi.Pointer<ffi.Char> source,\n      ffi.Pointer<ffi.Char> target,\n      ffi.Int uid,\n    );\ntypedef Dartresolve_process_funcFunction =\n    ffi.Pointer<ffi.Char> Function(\n      ffi.Pointer<ffi.Void> tun_interface,\n      int protocol,\n      ffi.Pointer<ffi.Char> source,\n      ffi.Pointer<ffi.Char> target,\n      int uid,\n    );\ntypedef resolve_process_func =\n    ffi.Pointer<ffi.NativeFunction<resolve_process_funcFunction>>;\ntypedef GoInt8 = ffi.SignedChar;\ntypedef DartGoInt8 = int;\ntypedef GoUint8 = ffi.UnsignedChar;\ntypedef DartGoUint8 = int;\ntypedef GoInt16 = ffi.Short;\ntypedef DartGoInt16 = int;\ntypedef GoUint16 = ffi.UnsignedShort;\ntypedef DartGoUint16 = int;\ntypedef GoInt32 = ffi.Int;\ntypedef DartGoInt32 = int;\ntypedef GoUint32 = ffi.UnsignedInt;\ntypedef DartGoUint32 = int;\ntypedef GoInt64 = ffi.LongLong;\ntypedef DartGoInt64 = int;\ntypedef GoUint64 = ffi.UnsignedLongLong;\ntypedef DartGoUint64 = int;\ntypedef GoInt = GoInt64;\ntypedef GoUint = GoUint64;\ntypedef GoUintptr = ffi.Size;\ntypedef DartGoUintptr = int;\ntypedef GoFloat32 = ffi.Float;\ntypedef DartGoFloat32 = double;\ntypedef GoFloat64 = ffi.Double;\ntypedef DartGoFloat64 = double;\ntypedef GoString = _GoString_;\ntypedef GoMap = ffi.Pointer<ffi.Void>;\ntypedef GoChan = ffi.Pointer<ffi.Void>;\n\nfinal class GoInterface extends ffi.Struct {\n  external ffi.Pointer<ffi.Void> t;\n\n  external ffi.Pointer<ffi.Void> v;\n}\n\nfinal class GoSlice extends ffi.Struct {\n  external ffi.Pointer<ffi.Void> data;\n\n  @GoInt()\n  external int len;\n\n  @GoInt()\n  external int cap;\n}\n\nconst int __has_safe_buffers = 1;\n\nconst int __DARWIN_ONLY_64_BIT_INO_T = 1;\n\nconst int __DARWIN_ONLY_UNIX_CONFORMANCE = 1;\n\nconst int __DARWIN_ONLY_VERS_1050 = 1;\n\nconst int __DARWIN_UNIX03 = 1;\n\nconst int __DARWIN_64_BIT_INO_T = 1;\n\nconst int __DARWIN_VERS_1050 = 1;\n\nconst int __DARWIN_NON_CANCELABLE = 0;\n\nconst String __DARWIN_SUF_EXTSN = '\\$DARWIN_EXTSN';\n\nconst int __DARWIN_C_ANSI = 4096;\n\nconst int __DARWIN_C_FULL = 900000;\n\nconst int __DARWIN_C_LEVEL = 900000;\n\nconst int __STDC_WANT_LIB_EXT1__ = 1;\n\nconst int __DARWIN_NO_LONG_LONG = 0;\n\nconst int _DARWIN_FEATURE_64_BIT_INODE = 1;\n\nconst int _DARWIN_FEATURE_ONLY_64_BIT_INODE = 1;\n\nconst int _DARWIN_FEATURE_ONLY_VERS_1050 = 1;\n\nconst int _DARWIN_FEATURE_ONLY_UNIX_CONFORMANCE = 1;\n\nconst int _DARWIN_FEATURE_UNIX_CONFORMANCE = 3;\n\nconst int __has_ptrcheck = 0;\n\nconst int __DARWIN_NULL = 0;\n\nconst int __PTHREAD_SIZE__ = 8176;\n\nconst int __PTHREAD_ATTR_SIZE__ = 56;\n\nconst int __PTHREAD_MUTEXATTR_SIZE__ = 8;\n\nconst int __PTHREAD_MUTEX_SIZE__ = 56;\n\nconst int __PTHREAD_CONDATTR_SIZE__ = 8;\n\nconst int __PTHREAD_COND_SIZE__ = 40;\n\nconst int __PTHREAD_ONCE_SIZE__ = 8;\n\nconst int __PTHREAD_RWLOCK_SIZE__ = 192;\n\nconst int __PTHREAD_RWLOCKATTR_SIZE__ = 16;\n\nconst int __DARWIN_WCHAR_MAX = 2147483647;\n\nconst int __DARWIN_WCHAR_MIN = -2147483648;\n\nconst int __DARWIN_WEOF = -1;\n\nconst int _FORTIFY_SOURCE = 2;\n\nconst int NULL = 0;\n\nconst int USER_ADDR_NULL = 0;\n\nconst int __API_TO_BE_DEPRECATED = 100000;\n\nconst int __API_TO_BE_DEPRECATED_MACOS = 100000;\n\nconst int __API_TO_BE_DEPRECATED_IOS = 100000;\n\nconst int __API_TO_BE_DEPRECATED_MACCATALYST = 100000;\n\nconst int __API_TO_BE_DEPRECATED_WATCHOS = 100000;\n\nconst int __API_TO_BE_DEPRECATED_TVOS = 100000;\n\nconst int __API_TO_BE_DEPRECATED_DRIVERKIT = 100000;\n\nconst int __API_TO_BE_DEPRECATED_VISIONOS = 100000;\n\nconst int __MAC_10_0 = 1000;\n\nconst int __MAC_10_1 = 1010;\n\nconst int __MAC_10_2 = 1020;\n\nconst int __MAC_10_3 = 1030;\n\nconst int __MAC_10_4 = 1040;\n\nconst int __MAC_10_5 = 1050;\n\nconst int __MAC_10_6 = 1060;\n\nconst int __MAC_10_7 = 1070;\n\nconst int __MAC_10_8 = 1080;\n\nconst int __MAC_10_9 = 1090;\n\nconst int __MAC_10_10 = 101000;\n\nconst int __MAC_10_10_2 = 101002;\n\nconst int __MAC_10_10_3 = 101003;\n\nconst int __MAC_10_11 = 101100;\n\nconst int __MAC_10_11_2 = 101102;\n\nconst int __MAC_10_11_3 = 101103;\n\nconst int __MAC_10_11_4 = 101104;\n\nconst int __MAC_10_12 = 101200;\n\nconst int __MAC_10_12_1 = 101201;\n\nconst int __MAC_10_12_2 = 101202;\n\nconst int __MAC_10_12_4 = 101204;\n\nconst int __MAC_10_13 = 101300;\n\nconst int __MAC_10_13_1 = 101301;\n\nconst int __MAC_10_13_2 = 101302;\n\nconst int __MAC_10_13_4 = 101304;\n\nconst int __MAC_10_14 = 101400;\n\nconst int __MAC_10_14_1 = 101401;\n\nconst int __MAC_10_14_4 = 101404;\n\nconst int __MAC_10_14_5 = 101405;\n\nconst int __MAC_10_14_6 = 101406;\n\nconst int __MAC_10_15 = 101500;\n\nconst int __MAC_10_15_1 = 101501;\n\nconst int __MAC_10_15_4 = 101504;\n\nconst int __MAC_10_16 = 101600;\n\nconst int __MAC_11_0 = 110000;\n\nconst int __MAC_11_1 = 110100;\n\nconst int __MAC_11_3 = 110300;\n\nconst int __MAC_11_4 = 110400;\n\nconst int __MAC_11_5 = 110500;\n\nconst int __MAC_11_6 = 110600;\n\nconst int __MAC_12_0 = 120000;\n\nconst int __MAC_12_1 = 120100;\n\nconst int __MAC_12_2 = 120200;\n\nconst int __MAC_12_3 = 120300;\n\nconst int __MAC_12_4 = 120400;\n\nconst int __MAC_12_5 = 120500;\n\nconst int __MAC_12_6 = 120600;\n\nconst int __MAC_12_7 = 120700;\n\nconst int __MAC_13_0 = 130000;\n\nconst int __MAC_13_1 = 130100;\n\nconst int __MAC_13_2 = 130200;\n\nconst int __MAC_13_3 = 130300;\n\nconst int __MAC_13_4 = 130400;\n\nconst int __MAC_13_5 = 130500;\n\nconst int __MAC_13_6 = 130600;\n\nconst int __MAC_14_0 = 140000;\n\nconst int __MAC_14_1 = 140100;\n\nconst int __MAC_14_2 = 140200;\n\nconst int __MAC_14_3 = 140300;\n\nconst int __MAC_14_4 = 140400;\n\nconst int __MAC_14_5 = 140500;\n\nconst int __MAC_15_0 = 150000;\n\nconst int __MAC_15_1 = 150100;\n\nconst int __MAC_15_2 = 150200;\n\nconst int __IPHONE_2_0 = 20000;\n\nconst int __IPHONE_2_1 = 20100;\n\nconst int __IPHONE_2_2 = 20200;\n\nconst int __IPHONE_3_0 = 30000;\n\nconst int __IPHONE_3_1 = 30100;\n\nconst int __IPHONE_3_2 = 30200;\n\nconst int __IPHONE_4_0 = 40000;\n\nconst int __IPHONE_4_1 = 40100;\n\nconst int __IPHONE_4_2 = 40200;\n\nconst int __IPHONE_4_3 = 40300;\n\nconst int __IPHONE_5_0 = 50000;\n\nconst int __IPHONE_5_1 = 50100;\n\nconst int __IPHONE_6_0 = 60000;\n\nconst int __IPHONE_6_1 = 60100;\n\nconst int __IPHONE_7_0 = 70000;\n\nconst int __IPHONE_7_1 = 70100;\n\nconst int __IPHONE_8_0 = 80000;\n\nconst int __IPHONE_8_1 = 80100;\n\nconst int __IPHONE_8_2 = 80200;\n\nconst int __IPHONE_8_3 = 80300;\n\nconst int __IPHONE_8_4 = 80400;\n\nconst int __IPHONE_9_0 = 90000;\n\nconst int __IPHONE_9_1 = 90100;\n\nconst int __IPHONE_9_2 = 90200;\n\nconst int __IPHONE_9_3 = 90300;\n\nconst int __IPHONE_10_0 = 100000;\n\nconst int __IPHONE_10_1 = 100100;\n\nconst int __IPHONE_10_2 = 100200;\n\nconst int __IPHONE_10_3 = 100300;\n\nconst int __IPHONE_11_0 = 110000;\n\nconst int __IPHONE_11_1 = 110100;\n\nconst int __IPHONE_11_2 = 110200;\n\nconst int __IPHONE_11_3 = 110300;\n\nconst int __IPHONE_11_4 = 110400;\n\nconst int __IPHONE_12_0 = 120000;\n\nconst int __IPHONE_12_1 = 120100;\n\nconst int __IPHONE_12_2 = 120200;\n\nconst int __IPHONE_12_3 = 120300;\n\nconst int __IPHONE_12_4 = 120400;\n\nconst int __IPHONE_13_0 = 130000;\n\nconst int __IPHONE_13_1 = 130100;\n\nconst int __IPHONE_13_2 = 130200;\n\nconst int __IPHONE_13_3 = 130300;\n\nconst int __IPHONE_13_4 = 130400;\n\nconst int __IPHONE_13_5 = 130500;\n\nconst int __IPHONE_13_6 = 130600;\n\nconst int __IPHONE_13_7 = 130700;\n\nconst int __IPHONE_14_0 = 140000;\n\nconst int __IPHONE_14_1 = 140100;\n\nconst int __IPHONE_14_2 = 140200;\n\nconst int __IPHONE_14_3 = 140300;\n\nconst int __IPHONE_14_5 = 140500;\n\nconst int __IPHONE_14_4 = 140400;\n\nconst int __IPHONE_14_6 = 140600;\n\nconst int __IPHONE_14_7 = 140700;\n\nconst int __IPHONE_14_8 = 140800;\n\nconst int __IPHONE_15_0 = 150000;\n\nconst int __IPHONE_15_1 = 150100;\n\nconst int __IPHONE_15_2 = 150200;\n\nconst int __IPHONE_15_3 = 150300;\n\nconst int __IPHONE_15_4 = 150400;\n\nconst int __IPHONE_15_5 = 150500;\n\nconst int __IPHONE_15_6 = 150600;\n\nconst int __IPHONE_15_7 = 150700;\n\nconst int __IPHONE_15_8 = 150800;\n\nconst int __IPHONE_16_0 = 160000;\n\nconst int __IPHONE_16_1 = 160100;\n\nconst int __IPHONE_16_2 = 160200;\n\nconst int __IPHONE_16_3 = 160300;\n\nconst int __IPHONE_16_4 = 160400;\n\nconst int __IPHONE_16_5 = 160500;\n\nconst int __IPHONE_16_6 = 160600;\n\nconst int __IPHONE_16_7 = 160700;\n\nconst int __IPHONE_17_0 = 170000;\n\nconst int __IPHONE_17_1 = 170100;\n\nconst int __IPHONE_17_2 = 170200;\n\nconst int __IPHONE_17_3 = 170300;\n\nconst int __IPHONE_17_4 = 170400;\n\nconst int __IPHONE_17_5 = 170500;\n\nconst int __IPHONE_18_0 = 180000;\n\nconst int __IPHONE_18_1 = 180100;\n\nconst int __IPHONE_18_2 = 180200;\n\nconst int __WATCHOS_1_0 = 10000;\n\nconst int __WATCHOS_2_0 = 20000;\n\nconst int __WATCHOS_2_1 = 20100;\n\nconst int __WATCHOS_2_2 = 20200;\n\nconst int __WATCHOS_3_0 = 30000;\n\nconst int __WATCHOS_3_1 = 30100;\n\nconst int __WATCHOS_3_1_1 = 30101;\n\nconst int __WATCHOS_3_2 = 30200;\n\nconst int __WATCHOS_4_0 = 40000;\n\nconst int __WATCHOS_4_1 = 40100;\n\nconst int __WATCHOS_4_2 = 40200;\n\nconst int __WATCHOS_4_3 = 40300;\n\nconst int __WATCHOS_5_0 = 50000;\n\nconst int __WATCHOS_5_1 = 50100;\n\nconst int __WATCHOS_5_2 = 50200;\n\nconst int __WATCHOS_5_3 = 50300;\n\nconst int __WATCHOS_6_0 = 60000;\n\nconst int __WATCHOS_6_1 = 60100;\n\nconst int __WATCHOS_6_2 = 60200;\n\nconst int __WATCHOS_7_0 = 70000;\n\nconst int __WATCHOS_7_1 = 70100;\n\nconst int __WATCHOS_7_2 = 70200;\n\nconst int __WATCHOS_7_3 = 70300;\n\nconst int __WATCHOS_7_4 = 70400;\n\nconst int __WATCHOS_7_5 = 70500;\n\nconst int __WATCHOS_7_6 = 70600;\n\nconst int __WATCHOS_8_0 = 80000;\n\nconst int __WATCHOS_8_1 = 80100;\n\nconst int __WATCHOS_8_3 = 80300;\n\nconst int __WATCHOS_8_4 = 80400;\n\nconst int __WATCHOS_8_5 = 80500;\n\nconst int __WATCHOS_8_6 = 80600;\n\nconst int __WATCHOS_8_7 = 80700;\n\nconst int __WATCHOS_8_8 = 80800;\n\nconst int __WATCHOS_9_0 = 90000;\n\nconst int __WATCHOS_9_1 = 90100;\n\nconst int __WATCHOS_9_2 = 90200;\n\nconst int __WATCHOS_9_3 = 90300;\n\nconst int __WATCHOS_9_4 = 90400;\n\nconst int __WATCHOS_9_5 = 90500;\n\nconst int __WATCHOS_9_6 = 90600;\n\nconst int __WATCHOS_10_0 = 100000;\n\nconst int __WATCHOS_10_1 = 100100;\n\nconst int __WATCHOS_10_2 = 100200;\n\nconst int __WATCHOS_10_3 = 100300;\n\nconst int __WATCHOS_10_4 = 100400;\n\nconst int __WATCHOS_10_5 = 100500;\n\nconst int __WATCHOS_11_0 = 110000;\n\nconst int __WATCHOS_11_1 = 110100;\n\nconst int __WATCHOS_11_2 = 110200;\n\nconst int __TVOS_9_0 = 90000;\n\nconst int __TVOS_9_1 = 90100;\n\nconst int __TVOS_9_2 = 90200;\n\nconst int __TVOS_10_0 = 100000;\n\nconst int __TVOS_10_0_1 = 100001;\n\nconst int __TVOS_10_1 = 100100;\n\nconst int __TVOS_10_2 = 100200;\n\nconst int __TVOS_11_0 = 110000;\n\nconst int __TVOS_11_1 = 110100;\n\nconst int __TVOS_11_2 = 110200;\n\nconst int __TVOS_11_3 = 110300;\n\nconst int __TVOS_11_4 = 110400;\n\nconst int __TVOS_12_0 = 120000;\n\nconst int __TVOS_12_1 = 120100;\n\nconst int __TVOS_12_2 = 120200;\n\nconst int __TVOS_12_3 = 120300;\n\nconst int __TVOS_12_4 = 120400;\n\nconst int __TVOS_13_0 = 130000;\n\nconst int __TVOS_13_2 = 130200;\n\nconst int __TVOS_13_3 = 130300;\n\nconst int __TVOS_13_4 = 130400;\n\nconst int __TVOS_14_0 = 140000;\n\nconst int __TVOS_14_1 = 140100;\n\nconst int __TVOS_14_2 = 140200;\n\nconst int __TVOS_14_3 = 140300;\n\nconst int __TVOS_14_5 = 140500;\n\nconst int __TVOS_14_6 = 140600;\n\nconst int __TVOS_14_7 = 140700;\n\nconst int __TVOS_15_0 = 150000;\n\nconst int __TVOS_15_1 = 150100;\n\nconst int __TVOS_15_2 = 150200;\n\nconst int __TVOS_15_3 = 150300;\n\nconst int __TVOS_15_4 = 150400;\n\nconst int __TVOS_15_5 = 150500;\n\nconst int __TVOS_15_6 = 150600;\n\nconst int __TVOS_16_0 = 160000;\n\nconst int __TVOS_16_1 = 160100;\n\nconst int __TVOS_16_2 = 160200;\n\nconst int __TVOS_16_3 = 160300;\n\nconst int __TVOS_16_4 = 160400;\n\nconst int __TVOS_16_5 = 160500;\n\nconst int __TVOS_16_6 = 160600;\n\nconst int __TVOS_17_0 = 170000;\n\nconst int __TVOS_17_1 = 170100;\n\nconst int __TVOS_17_2 = 170200;\n\nconst int __TVOS_17_3 = 170300;\n\nconst int __TVOS_17_4 = 170400;\n\nconst int __TVOS_17_5 = 170500;\n\nconst int __TVOS_18_0 = 180000;\n\nconst int __TVOS_18_1 = 180100;\n\nconst int __TVOS_18_2 = 180200;\n\nconst int __BRIDGEOS_2_0 = 20000;\n\nconst int __BRIDGEOS_3_0 = 30000;\n\nconst int __BRIDGEOS_3_1 = 30100;\n\nconst int __BRIDGEOS_3_4 = 30400;\n\nconst int __BRIDGEOS_4_0 = 40000;\n\nconst int __BRIDGEOS_4_1 = 40100;\n\nconst int __BRIDGEOS_5_0 = 50000;\n\nconst int __BRIDGEOS_5_1 = 50100;\n\nconst int __BRIDGEOS_5_3 = 50300;\n\nconst int __BRIDGEOS_6_0 = 60000;\n\nconst int __BRIDGEOS_6_2 = 60200;\n\nconst int __BRIDGEOS_6_4 = 60400;\n\nconst int __BRIDGEOS_6_5 = 60500;\n\nconst int __BRIDGEOS_6_6 = 60600;\n\nconst int __BRIDGEOS_7_0 = 70000;\n\nconst int __BRIDGEOS_7_1 = 70100;\n\nconst int __BRIDGEOS_7_2 = 70200;\n\nconst int __BRIDGEOS_7_3 = 70300;\n\nconst int __BRIDGEOS_7_4 = 70400;\n\nconst int __BRIDGEOS_7_6 = 70600;\n\nconst int __BRIDGEOS_8_0 = 80000;\n\nconst int __BRIDGEOS_8_1 = 80100;\n\nconst int __BRIDGEOS_8_2 = 80200;\n\nconst int __BRIDGEOS_8_3 = 80300;\n\nconst int __BRIDGEOS_8_4 = 80400;\n\nconst int __BRIDGEOS_8_5 = 80500;\n\nconst int __BRIDGEOS_9_0 = 90000;\n\nconst int __BRIDGEOS_9_1 = 90100;\n\nconst int __BRIDGEOS_9_2 = 90200;\n\nconst int __DRIVERKIT_19_0 = 190000;\n\nconst int __DRIVERKIT_20_0 = 200000;\n\nconst int __DRIVERKIT_21_0 = 210000;\n\nconst int __DRIVERKIT_22_0 = 220000;\n\nconst int __DRIVERKIT_22_4 = 220400;\n\nconst int __DRIVERKIT_22_5 = 220500;\n\nconst int __DRIVERKIT_22_6 = 220600;\n\nconst int __DRIVERKIT_23_0 = 230000;\n\nconst int __DRIVERKIT_23_1 = 230100;\n\nconst int __DRIVERKIT_23_2 = 230200;\n\nconst int __DRIVERKIT_23_3 = 230300;\n\nconst int __DRIVERKIT_23_4 = 230400;\n\nconst int __DRIVERKIT_23_5 = 230500;\n\nconst int __DRIVERKIT_24_0 = 240000;\n\nconst int __DRIVERKIT_24_1 = 240100;\n\nconst int __DRIVERKIT_24_2 = 240200;\n\nconst int __VISIONOS_1_0 = 10000;\n\nconst int __VISIONOS_1_1 = 10100;\n\nconst int __VISIONOS_1_2 = 10200;\n\nconst int __VISIONOS_2_0 = 20000;\n\nconst int __VISIONOS_2_1 = 20100;\n\nconst int __VISIONOS_2_2 = 20200;\n\nconst int MAC_OS_X_VERSION_10_0 = 1000;\n\nconst int MAC_OS_X_VERSION_10_1 = 1010;\n\nconst int MAC_OS_X_VERSION_10_2 = 1020;\n\nconst int MAC_OS_X_VERSION_10_3 = 1030;\n\nconst int MAC_OS_X_VERSION_10_4 = 1040;\n\nconst int MAC_OS_X_VERSION_10_5 = 1050;\n\nconst int MAC_OS_X_VERSION_10_6 = 1060;\n\nconst int MAC_OS_X_VERSION_10_7 = 1070;\n\nconst int MAC_OS_X_VERSION_10_8 = 1080;\n\nconst int MAC_OS_X_VERSION_10_9 = 1090;\n\nconst int MAC_OS_X_VERSION_10_10 = 101000;\n\nconst int MAC_OS_X_VERSION_10_10_2 = 101002;\n\nconst int MAC_OS_X_VERSION_10_10_3 = 101003;\n\nconst int MAC_OS_X_VERSION_10_11 = 101100;\n\nconst int MAC_OS_X_VERSION_10_11_2 = 101102;\n\nconst int MAC_OS_X_VERSION_10_11_3 = 101103;\n\nconst int MAC_OS_X_VERSION_10_11_4 = 101104;\n\nconst int MAC_OS_X_VERSION_10_12 = 101200;\n\nconst int MAC_OS_X_VERSION_10_12_1 = 101201;\n\nconst int MAC_OS_X_VERSION_10_12_2 = 101202;\n\nconst int MAC_OS_X_VERSION_10_12_4 = 101204;\n\nconst int MAC_OS_X_VERSION_10_13 = 101300;\n\nconst int MAC_OS_X_VERSION_10_13_1 = 101301;\n\nconst int MAC_OS_X_VERSION_10_13_2 = 101302;\n\nconst int MAC_OS_X_VERSION_10_13_4 = 101304;\n\nconst int MAC_OS_X_VERSION_10_14 = 101400;\n\nconst int MAC_OS_X_VERSION_10_14_1 = 101401;\n\nconst int MAC_OS_X_VERSION_10_14_4 = 101404;\n\nconst int MAC_OS_X_VERSION_10_14_5 = 101405;\n\nconst int MAC_OS_X_VERSION_10_14_6 = 101406;\n\nconst int MAC_OS_X_VERSION_10_15 = 101500;\n\nconst int MAC_OS_X_VERSION_10_15_1 = 101501;\n\nconst int MAC_OS_X_VERSION_10_15_4 = 101504;\n\nconst int MAC_OS_X_VERSION_10_16 = 101600;\n\nconst int MAC_OS_VERSION_11_0 = 110000;\n\nconst int MAC_OS_VERSION_11_1 = 110100;\n\nconst int MAC_OS_VERSION_11_3 = 110300;\n\nconst int MAC_OS_VERSION_11_4 = 110400;\n\nconst int MAC_OS_VERSION_11_5 = 110500;\n\nconst int MAC_OS_VERSION_11_6 = 110600;\n\nconst int MAC_OS_VERSION_12_0 = 120000;\n\nconst int MAC_OS_VERSION_12_1 = 120100;\n\nconst int MAC_OS_VERSION_12_2 = 120200;\n\nconst int MAC_OS_VERSION_12_3 = 120300;\n\nconst int MAC_OS_VERSION_12_4 = 120400;\n\nconst int MAC_OS_VERSION_12_5 = 120500;\n\nconst int MAC_OS_VERSION_12_6 = 120600;\n\nconst int MAC_OS_VERSION_12_7 = 120700;\n\nconst int MAC_OS_VERSION_13_0 = 130000;\n\nconst int MAC_OS_VERSION_13_1 = 130100;\n\nconst int MAC_OS_VERSION_13_2 = 130200;\n\nconst int MAC_OS_VERSION_13_3 = 130300;\n\nconst int MAC_OS_VERSION_13_4 = 130400;\n\nconst int MAC_OS_VERSION_13_5 = 130500;\n\nconst int MAC_OS_VERSION_13_6 = 130600;\n\nconst int MAC_OS_VERSION_14_0 = 140000;\n\nconst int MAC_OS_VERSION_14_1 = 140100;\n\nconst int MAC_OS_VERSION_14_2 = 140200;\n\nconst int MAC_OS_VERSION_14_3 = 140300;\n\nconst int MAC_OS_VERSION_14_4 = 140400;\n\nconst int MAC_OS_VERSION_14_5 = 140500;\n\nconst int MAC_OS_VERSION_15_0 = 150000;\n\nconst int MAC_OS_VERSION_15_1 = 150100;\n\nconst int MAC_OS_VERSION_15_2 = 150200;\n\nconst int __MAC_OS_X_VERSION_MIN_REQUIRED = 150000;\n\nconst int __MAC_OS_X_VERSION_MAX_ALLOWED = 150200;\n\nconst int __ENABLE_LEGACY_MAC_AVAILABILITY = 1;\n\nconst int __DARWIN_NSIG = 32;\n\nconst int NSIG = 32;\n\nconst int _ARM_SIGNAL_ = 1;\n\nconst int SIGHUP = 1;\n\nconst int SIGINT = 2;\n\nconst int SIGQUIT = 3;\n\nconst int SIGILL = 4;\n\nconst int SIGTRAP = 5;\n\nconst int SIGABRT = 6;\n\nconst int SIGIOT = 6;\n\nconst int SIGEMT = 7;\n\nconst int SIGFPE = 8;\n\nconst int SIGKILL = 9;\n\nconst int SIGBUS = 10;\n\nconst int SIGSEGV = 11;\n\nconst int SIGSYS = 12;\n\nconst int SIGPIPE = 13;\n\nconst int SIGALRM = 14;\n\nconst int SIGTERM = 15;\n\nconst int SIGURG = 16;\n\nconst int SIGSTOP = 17;\n\nconst int SIGTSTP = 18;\n\nconst int SIGCONT = 19;\n\nconst int SIGCHLD = 20;\n\nconst int SIGTTIN = 21;\n\nconst int SIGTTOU = 22;\n\nconst int SIGIO = 23;\n\nconst int SIGXCPU = 24;\n\nconst int SIGXFSZ = 25;\n\nconst int SIGVTALRM = 26;\n\nconst int SIGPROF = 27;\n\nconst int SIGWINCH = 28;\n\nconst int SIGINFO = 29;\n\nconst int SIGUSR1 = 30;\n\nconst int SIGUSR2 = 31;\n\nconst int __DARWIN_OPAQUE_ARM_THREAD_STATE64 = 0;\n\nconst int SIGEV_NONE = 0;\n\nconst int SIGEV_SIGNAL = 1;\n\nconst int SIGEV_THREAD = 3;\n\nconst int ILL_NOOP = 0;\n\nconst int ILL_ILLOPC = 1;\n\nconst int ILL_ILLTRP = 2;\n\nconst int ILL_PRVOPC = 3;\n\nconst int ILL_ILLOPN = 4;\n\nconst int ILL_ILLADR = 5;\n\nconst int ILL_PRVREG = 6;\n\nconst int ILL_COPROC = 7;\n\nconst int ILL_BADSTK = 8;\n\nconst int FPE_NOOP = 0;\n\nconst int FPE_FLTDIV = 1;\n\nconst int FPE_FLTOVF = 2;\n\nconst int FPE_FLTUND = 3;\n\nconst int FPE_FLTRES = 4;\n\nconst int FPE_FLTINV = 5;\n\nconst int FPE_FLTSUB = 6;\n\nconst int FPE_INTDIV = 7;\n\nconst int FPE_INTOVF = 8;\n\nconst int SEGV_NOOP = 0;\n\nconst int SEGV_MAPERR = 1;\n\nconst int SEGV_ACCERR = 2;\n\nconst int BUS_NOOP = 0;\n\nconst int BUS_ADRALN = 1;\n\nconst int BUS_ADRERR = 2;\n\nconst int BUS_OBJERR = 3;\n\nconst int TRAP_BRKPT = 1;\n\nconst int TRAP_TRACE = 2;\n\nconst int CLD_NOOP = 0;\n\nconst int CLD_EXITED = 1;\n\nconst int CLD_KILLED = 2;\n\nconst int CLD_DUMPED = 3;\n\nconst int CLD_TRAPPED = 4;\n\nconst int CLD_STOPPED = 5;\n\nconst int CLD_CONTINUED = 6;\n\nconst int POLL_IN = 1;\n\nconst int POLL_OUT = 2;\n\nconst int POLL_MSG = 3;\n\nconst int POLL_ERR = 4;\n\nconst int POLL_PRI = 5;\n\nconst int POLL_HUP = 6;\n\nconst int SA_ONSTACK = 1;\n\nconst int SA_RESTART = 2;\n\nconst int SA_RESETHAND = 4;\n\nconst int SA_NOCLDSTOP = 8;\n\nconst int SA_NODEFER = 16;\n\nconst int SA_NOCLDWAIT = 32;\n\nconst int SA_SIGINFO = 64;\n\nconst int SA_USERTRAMP = 256;\n\nconst int SA_64REGSET = 512;\n\nconst int SA_USERSPACE_MASK = 127;\n\nconst int SIG_BLOCK = 1;\n\nconst int SIG_UNBLOCK = 2;\n\nconst int SIG_SETMASK = 3;\n\nconst int SI_USER = 65537;\n\nconst int SI_QUEUE = 65538;\n\nconst int SI_TIMER = 65539;\n\nconst int SI_ASYNCIO = 65540;\n\nconst int SI_MESGQ = 65541;\n\nconst int SS_ONSTACK = 1;\n\nconst int SS_DISABLE = 4;\n\nconst int MINSIGSTKSZ = 32768;\n\nconst int SIGSTKSZ = 131072;\n\nconst int SV_ONSTACK = 1;\n\nconst int SV_INTERRUPT = 2;\n\nconst int SV_RESETHAND = 4;\n\nconst int SV_NODEFER = 16;\n\nconst int SV_NOCLDSTOP = 8;\n\nconst int SV_SIGINFO = 64;\n\nconst int __WORDSIZE = 64;\n\nconst int INT8_MAX = 127;\n\nconst int INT16_MAX = 32767;\n\nconst int INT32_MAX = 2147483647;\n\nconst int INT64_MAX = 9223372036854775807;\n\nconst int INT8_MIN = -128;\n\nconst int INT16_MIN = -32768;\n\nconst int INT32_MIN = -2147483648;\n\nconst int INT64_MIN = -9223372036854775808;\n\nconst int UINT8_MAX = 255;\n\nconst int UINT16_MAX = 65535;\n\nconst int UINT32_MAX = 4294967295;\n\nconst int UINT64_MAX = -1;\n\nconst int INT_LEAST8_MIN = -128;\n\nconst int INT_LEAST16_MIN = -32768;\n\nconst int INT_LEAST32_MIN = -2147483648;\n\nconst int INT_LEAST64_MIN = -9223372036854775808;\n\nconst int INT_LEAST8_MAX = 127;\n\nconst int INT_LEAST16_MAX = 32767;\n\nconst int INT_LEAST32_MAX = 2147483647;\n\nconst int INT_LEAST64_MAX = 9223372036854775807;\n\nconst int UINT_LEAST8_MAX = 255;\n\nconst int UINT_LEAST16_MAX = 65535;\n\nconst int UINT_LEAST32_MAX = 4294967295;\n\nconst int UINT_LEAST64_MAX = -1;\n\nconst int INT_FAST8_MIN = -128;\n\nconst int INT_FAST16_MIN = -32768;\n\nconst int INT_FAST32_MIN = -2147483648;\n\nconst int INT_FAST64_MIN = -9223372036854775808;\n\nconst int INT_FAST8_MAX = 127;\n\nconst int INT_FAST16_MAX = 32767;\n\nconst int INT_FAST32_MAX = 2147483647;\n\nconst int INT_FAST64_MAX = 9223372036854775807;\n\nconst int UINT_FAST8_MAX = 255;\n\nconst int UINT_FAST16_MAX = 65535;\n\nconst int UINT_FAST32_MAX = 4294967295;\n\nconst int UINT_FAST64_MAX = -1;\n\nconst int INTPTR_MAX = 9223372036854775807;\n\nconst int INTPTR_MIN = -9223372036854775808;\n\nconst int UINTPTR_MAX = -1;\n\nconst int INTMAX_MAX = 9223372036854775807;\n\nconst int UINTMAX_MAX = -1;\n\nconst int INTMAX_MIN = -9223372036854775808;\n\nconst int PTRDIFF_MIN = -9223372036854775808;\n\nconst int PTRDIFF_MAX = 9223372036854775807;\n\nconst int SIZE_MAX = -1;\n\nconst int RSIZE_MAX = 9223372036854775807;\n\nconst int WCHAR_MAX = 2147483647;\n\nconst int WCHAR_MIN = -2147483648;\n\nconst int WINT_MIN = -2147483648;\n\nconst int WINT_MAX = 2147483647;\n\nconst int SIG_ATOMIC_MIN = -2147483648;\n\nconst int SIG_ATOMIC_MAX = 2147483647;\n\nconst int PRIO_PROCESS = 0;\n\nconst int PRIO_PGRP = 1;\n\nconst int PRIO_USER = 2;\n\nconst int PRIO_DARWIN_THREAD = 3;\n\nconst int PRIO_DARWIN_PROCESS = 4;\n\nconst int PRIO_MIN = -20;\n\nconst int PRIO_MAX = 20;\n\nconst int PRIO_DARWIN_BG = 4096;\n\nconst int PRIO_DARWIN_NONUI = 4097;\n\nconst int RUSAGE_SELF = 0;\n\nconst int RUSAGE_CHILDREN = -1;\n\nconst int RUSAGE_INFO_V0 = 0;\n\nconst int RUSAGE_INFO_V1 = 1;\n\nconst int RUSAGE_INFO_V2 = 2;\n\nconst int RUSAGE_INFO_V3 = 3;\n\nconst int RUSAGE_INFO_V4 = 4;\n\nconst int RUSAGE_INFO_V5 = 5;\n\nconst int RUSAGE_INFO_V6 = 6;\n\nconst int RUSAGE_INFO_CURRENT = 6;\n\nconst int RU_PROC_RUNS_RESLIDE = 1;\n\nconst int RLIM_INFINITY = 9223372036854775807;\n\nconst int RLIM_SAVED_MAX = 9223372036854775807;\n\nconst int RLIM_SAVED_CUR = 9223372036854775807;\n\nconst int RLIMIT_CPU = 0;\n\nconst int RLIMIT_FSIZE = 1;\n\nconst int RLIMIT_DATA = 2;\n\nconst int RLIMIT_STACK = 3;\n\nconst int RLIMIT_CORE = 4;\n\nconst int RLIMIT_AS = 5;\n\nconst int RLIMIT_RSS = 5;\n\nconst int RLIMIT_MEMLOCK = 6;\n\nconst int RLIMIT_NPROC = 7;\n\nconst int RLIMIT_NOFILE = 8;\n\nconst int RLIM_NLIMITS = 9;\n\nconst int _RLIMIT_POSIX_FLAG = 4096;\n\nconst int RLIMIT_WAKEUPS_MONITOR = 1;\n\nconst int RLIMIT_CPU_USAGE_MONITOR = 2;\n\nconst int RLIMIT_THREAD_CPULIMITS = 3;\n\nconst int RLIMIT_FOOTPRINT_INTERVAL = 4;\n\nconst int WAKEMON_ENABLE = 1;\n\nconst int WAKEMON_DISABLE = 2;\n\nconst int WAKEMON_GET_PARAMS = 4;\n\nconst int WAKEMON_SET_DEFAULTS = 8;\n\nconst int WAKEMON_MAKE_FATAL = 16;\n\nconst int CPUMON_MAKE_FATAL = 4096;\n\nconst int FOOTPRINT_INTERVAL_RESET = 1;\n\nconst int IOPOL_TYPE_DISK = 0;\n\nconst int IOPOL_TYPE_VFS_ATIME_UPDATES = 2;\n\nconst int IOPOL_TYPE_VFS_MATERIALIZE_DATALESS_FILES = 3;\n\nconst int IOPOL_TYPE_VFS_STATFS_NO_DATA_VOLUME = 4;\n\nconst int IOPOL_TYPE_VFS_TRIGGER_RESOLVE = 5;\n\nconst int IOPOL_TYPE_VFS_IGNORE_CONTENT_PROTECTION = 6;\n\nconst int IOPOL_TYPE_VFS_IGNORE_PERMISSIONS = 7;\n\nconst int IOPOL_TYPE_VFS_SKIP_MTIME_UPDATE = 8;\n\nconst int IOPOL_TYPE_VFS_ALLOW_LOW_SPACE_WRITES = 9;\n\nconst int IOPOL_TYPE_VFS_DISALLOW_RW_FOR_O_EVTONLY = 10;\n\nconst int IOPOL_SCOPE_PROCESS = 0;\n\nconst int IOPOL_SCOPE_THREAD = 1;\n\nconst int IOPOL_SCOPE_DARWIN_BG = 2;\n\nconst int IOPOL_DEFAULT = 0;\n\nconst int IOPOL_IMPORTANT = 1;\n\nconst int IOPOL_PASSIVE = 2;\n\nconst int IOPOL_THROTTLE = 3;\n\nconst int IOPOL_UTILITY = 4;\n\nconst int IOPOL_STANDARD = 5;\n\nconst int IOPOL_APPLICATION = 5;\n\nconst int IOPOL_NORMAL = 1;\n\nconst int IOPOL_ATIME_UPDATES_DEFAULT = 0;\n\nconst int IOPOL_ATIME_UPDATES_OFF = 1;\n\nconst int IOPOL_MATERIALIZE_DATALESS_FILES_DEFAULT = 0;\n\nconst int IOPOL_MATERIALIZE_DATALESS_FILES_OFF = 1;\n\nconst int IOPOL_MATERIALIZE_DATALESS_FILES_ON = 2;\n\nconst int IOPOL_VFS_STATFS_NO_DATA_VOLUME_DEFAULT = 0;\n\nconst int IOPOL_VFS_STATFS_FORCE_NO_DATA_VOLUME = 1;\n\nconst int IOPOL_VFS_TRIGGER_RESOLVE_DEFAULT = 0;\n\nconst int IOPOL_VFS_TRIGGER_RESOLVE_OFF = 1;\n\nconst int IOPOL_VFS_CONTENT_PROTECTION_DEFAULT = 0;\n\nconst int IOPOL_VFS_CONTENT_PROTECTION_IGNORE = 1;\n\nconst int IOPOL_VFS_IGNORE_PERMISSIONS_OFF = 0;\n\nconst int IOPOL_VFS_IGNORE_PERMISSIONS_ON = 1;\n\nconst int IOPOL_VFS_SKIP_MTIME_UPDATE_OFF = 0;\n\nconst int IOPOL_VFS_SKIP_MTIME_UPDATE_ON = 1;\n\nconst int IOPOL_VFS_ALLOW_LOW_SPACE_WRITES_OFF = 0;\n\nconst int IOPOL_VFS_ALLOW_LOW_SPACE_WRITES_ON = 1;\n\nconst int IOPOL_VFS_DISALLOW_RW_FOR_O_EVTONLY_DEFAULT = 0;\n\nconst int IOPOL_VFS_DISALLOW_RW_FOR_O_EVTONLY_ON = 1;\n\nconst int IOPOL_VFS_NOCACHE_WRITE_FS_BLKSIZE_DEFAULT = 0;\n\nconst int IOPOL_VFS_NOCACHE_WRITE_FS_BLKSIZE_ON = 1;\n\nconst int WNOHANG = 1;\n\nconst int WUNTRACED = 2;\n\nconst int WCOREFLAG = 128;\n\nconst int _WSTOPPED = 127;\n\nconst int WEXITED = 4;\n\nconst int WSTOPPED = 8;\n\nconst int WCONTINUED = 16;\n\nconst int WNOWAIT = 32;\n\nconst int WAIT_ANY = -1;\n\nconst int WAIT_MYPGRP = 0;\n\nconst int _QUAD_HIGHWORD = 1;\n\nconst int _QUAD_LOWWORD = 0;\n\nconst int __DARWIN_LITTLE_ENDIAN = 1234;\n\nconst int __DARWIN_BIG_ENDIAN = 4321;\n\nconst int __DARWIN_PDP_ENDIAN = 3412;\n\nconst int LITTLE_ENDIAN = 1234;\n\nconst int BIG_ENDIAN = 4321;\n\nconst int PDP_ENDIAN = 3412;\n\nconst int __DARWIN_BYTE_ORDER = 1234;\n\nconst int BYTE_ORDER = 1234;\n\nconst int EXIT_FAILURE = 1;\n\nconst int EXIT_SUCCESS = 0;\n\nconst int RAND_MAX = 2147483647;\n"
  },
  {
    "path": "lib/clash/interface.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\nimport 'dart:isolate';\n\nimport 'package:bett_box/clash/message.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\n\nmixin ClashInterface {\n  Future<bool> init(InitParams params);\n\n  Future<bool> preload();\n\n  Future<bool> shutdown();\n\n  Future<bool> get isInit;\n\n  Future<bool> forceGc();\n\n  FutureOr<String> validateConfig(String data);\n\n  FutureOr<Result> getConfig(String path);\n\n  Future<String> asyncTestDelay(String url, String proxyName);\n\n  FutureOr<String> updateConfig(UpdateParams updateParams);\n\n  FutureOr<String> setupConfig(SetupParams setupParams);\n\n  FutureOr<Map> getProxies();\n\n  FutureOr<String> changeProxy(ChangeProxyParams changeProxyParams);\n\n  Future<bool> startListener();\n\n  Future<bool> stopListener();\n\n  FutureOr<String> getExternalProviders();\n\n  FutureOr<String>? getExternalProvider(String externalProviderName);\n\n  Future<String> updateGeoData(UpdateGeoDataParams params);\n\n  Future<String> sideLoadExternalProvider({\n    required String providerName,\n    required String data,\n  });\n\n  Future<String> updateExternalProvider(String providerName);\n\n  FutureOr<String> getTraffic();\n\n  FutureOr<String> getTotalTraffic();\n\n  FutureOr<String> getCountryCode(String ip);\n\n  FutureOr<String> getMemory();\n\n  FutureOr<void> resetTraffic();\n\n  FutureOr<void> startLog();\n\n  FutureOr<void> stopLog();\n\n  Future<bool> crash();\n\n  FutureOr<String> getConnections();\n\n  FutureOr<bool> closeConnection(String id);\n\n  FutureOr<bool> closeConnections();\n\n  FutureOr<bool> resetConnections();\n\n  Future<bool> setState(CoreState state);\n\n  FutureOr<bool> flushFakeIP();\n\n  FutureOr<bool> flushDnsCache();\n}\n\nmixin AndroidClashInterface {\n  Future<bool> updateDns(String value);\n\n  Future<AndroidVpnOptions?> getAndroidVpnOptions();\n\n  Future<String> getCurrentProfileName();\n\n  Future<DateTime?> getRunTime();\n}\n\nabstract class ClashHandlerInterface with ClashInterface {\n  Map<String, Completer> callbackCompleterMap = {};\n\n  Future<void> handleResult(ActionResult result) async {\n    final completer = callbackCompleterMap[result.id];\n    try {\n      switch (result.method) {\n        case ActionMethod.message:\n          clashMessage.controller.add(result.data);\n          completer?.complete(true);\n          return;\n        case ActionMethod.getConfig:\n          completer?.complete(result.toResult);\n          return;\n        default:\n          completer?.complete(result.data);\n          return;\n      }\n    } catch (e) {\n      commonPrint.log('${result.id} error $e');\n    }\n  }\n\n  void sendMessage(String message);\n\n  FutureOr<void> reStart();\n\n  FutureOr<bool> destroy();\n\n  Future<T> invoke<T>({\n    required ActionMethod method,\n    dynamic data,\n    Duration? timeout,\n    FutureOr<T> Function()? onTimeout,\n    T? defaultValue,\n  }) async {\n    final id = '${method.name}#${utils.id}';\n\n    callbackCompleterMap[id] = Completer<T>();\n\n    dynamic mDefaultValue = defaultValue;\n    if (mDefaultValue == null) {\n      if (T == String) {\n        mDefaultValue = '';\n      } else if (T == bool) {\n        mDefaultValue = false;\n      } else if (T == Map) {\n        mDefaultValue = {};\n      }\n    }\n\n    sendMessage(json.encode(Action(id: id, method: method, data: data)));\n\n    return (callbackCompleterMap[id] as Completer<T>).safeFuture(\n      timeout: timeout,\n      onLast: () {\n        callbackCompleterMap.remove(id);\n      },\n      onTimeout:\n          onTimeout ??\n          () {\n            return mDefaultValue;\n          },\n      functionName: id,\n    );\n  }\n\n  @override\n  Future<bool> init(InitParams params) {\n    return invoke<bool>(\n      method: ActionMethod.initClash,\n      data: json.encode(params),\n    );\n  }\n\n  @override\n  Future<bool> setState(CoreState state) {\n    return invoke<bool>(\n      method: ActionMethod.setState,\n      data: json.encode(state),\n    );\n  }\n\n  @override\n  shutdown() async {\n    return await invoke<bool>(\n      method: ActionMethod.shutdown,\n      timeout: Duration(seconds: 1),\n    );\n  }\n\n  @override\n  Future<bool> get isInit {\n    return invoke<bool>(method: ActionMethod.getIsInit);\n  }\n\n  @override\n  Future<bool> forceGc() {\n    return invoke<bool>(method: ActionMethod.forceGc);\n  }\n\n  @override\n  FutureOr<String> validateConfig(String data) {\n    return invoke<String>(method: ActionMethod.validateConfig, data: data);\n  }\n\n  @override\n  Future<String> updateConfig(UpdateParams updateParams) async {\n    return await invoke<String>(\n      method: ActionMethod.updateConfig,\n      data: json.encode(updateParams),\n      timeout: Duration(minutes: 2),\n    );\n  }\n\n  @override\n  Future<Result> getConfig(String path) async {\n    final res = await invoke<Result>(\n      method: ActionMethod.getConfig,\n      data: path,\n      timeout: Duration(minutes: 2),\n      defaultValue: Result.success({}),\n    );\n    return res;\n  }\n\n  @override\n  Future<String> setupConfig(SetupParams setupParams) async {\n    final data = await Isolate.run(() => json.encode(setupParams));\n    return await invoke<String>(\n      method: ActionMethod.setupConfig,\n      data: data,\n      timeout: Duration(minutes: 2),\n    );\n  }\n\n  @override\n  Future<bool> crash() {\n    return invoke<bool>(method: ActionMethod.crash);\n  }\n\n  @override\n  Future<Map> getProxies() {\n    return invoke<Map>(\n      method: ActionMethod.getProxies,\n      timeout: Duration(seconds: 5),\n    );\n  }\n\n  @override\n  FutureOr<String> changeProxy(ChangeProxyParams changeProxyParams) {\n    return invoke<String>(\n      method: ActionMethod.changeProxy,\n      data: json.encode(changeProxyParams),\n    );\n  }\n\n  @override\n  FutureOr<String> getExternalProviders() {\n    return invoke<String>(method: ActionMethod.getExternalProviders);\n  }\n\n  @override\n  FutureOr<String> getExternalProvider(String externalProviderName) {\n    return invoke<String>(\n      method: ActionMethod.getExternalProvider,\n      data: externalProviderName,\n    );\n  }\n\n  @override\n  Future<String> updateGeoData(UpdateGeoDataParams params) {\n    return invoke<String>(\n      method: ActionMethod.updateGeoData,\n      data: json.encode(params),\n      timeout: Duration(minutes: 1),\n    );\n  }\n\n  @override\n  Future<String> sideLoadExternalProvider({\n    required String providerName,\n    required String data,\n  }) {\n    return invoke<String>(\n      method: ActionMethod.sideLoadExternalProvider,\n      data: json.encode({'providerName': providerName, 'data': data}),\n    );\n  }\n\n  @override\n  Future<String> updateExternalProvider(String providerName) {\n    return invoke<String>(\n      method: ActionMethod.updateExternalProvider,\n      data: providerName,\n      timeout: Duration(minutes: 1),\n    );\n  }\n\n  @override\n  FutureOr<String> getConnections() {\n    return invoke<String>(method: ActionMethod.getConnections);\n  }\n\n  @override\n  Future<bool> closeConnections() {\n    return invoke<bool>(method: ActionMethod.closeConnections);\n  }\n\n  @override\n  Future<bool> resetConnections() {\n    return invoke<bool>(method: ActionMethod.resetConnections);\n  }\n\n  @override\n  Future<bool> closeConnection(String id) {\n    return invoke<bool>(method: ActionMethod.closeConnection, data: id);\n  }\n\n  @override\n  FutureOr<String> getTotalTraffic() {\n    return invoke<String>(method: ActionMethod.getTotalTraffic);\n  }\n\n  @override\n  FutureOr<String> getTraffic() {\n    return invoke<String>(method: ActionMethod.getTraffic);\n  }\n\n  @override\n  resetTraffic() {\n    invoke(method: ActionMethod.resetTraffic);\n  }\n\n  @override\n  startLog() {\n    invoke(method: ActionMethod.startLog);\n  }\n\n  @override\n  stopLog() {\n    invoke<bool>(method: ActionMethod.stopLog);\n  }\n\n  @override\n  Future<bool> startListener() {\n    return invoke<bool>(method: ActionMethod.startListener);\n  }\n\n  @override\n  stopListener() {\n    return invoke<bool>(method: ActionMethod.stopListener);\n  }\n\n  @override\n  Future<String> asyncTestDelay(String url, String proxyName) {\n    final delayParams = {\n      'proxy-name': proxyName,\n      'timeout': httpTimeoutDuration.inMilliseconds,\n      'test-url': url,\n    };\n    return invoke<String>(\n      method: ActionMethod.asyncTestDelay,\n      data: json.encode(delayParams),\n      timeout: Duration(milliseconds: 6000),\n      onTimeout: () {\n        return json.encode(Delay(name: proxyName, value: -1, url: url));\n      },\n    );\n  }\n\n  @override\n  FutureOr<String> getCountryCode(String ip) {\n    return invoke<String>(method: ActionMethod.getCountryCode, data: ip);\n  }\n\n  @override\n  FutureOr<String> getMemory() {\n    return invoke<String>(method: ActionMethod.getMemory);\n  }\n\n  @override\n  FutureOr<bool> flushFakeIP() {\n    return invoke<bool>(method: ActionMethod.flushFakeIP);\n  }\n\n  @override\n  FutureOr<bool> flushDnsCache() {\n    return invoke<bool>(method: ActionMethod.flushDnsCache);\n  }\n}\n"
  },
  {
    "path": "lib/clash/lib.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\nimport 'dart:ffi';\nimport 'dart:isolate';\nimport 'dart:ui';\n\nimport 'package:ffi/ffi.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/plugins/service.dart';\nimport 'package:bett_box/state.dart';\n\nimport 'generated/clash_ffi.dart';\nimport 'interface.dart';\n\nclass ClashLib extends ClashHandlerInterface with AndroidClashInterface {\n  static ClashLib? _instance;\n  Completer<bool> _canSendCompleter = Completer();\n  SendPort? sendPort;\n  final receiverPort = ReceivePort();\n\n  ClashLib._internal() {\n    _initService();\n  }\n\n  @override\n  preload() {\n    return _canSendCompleter.future;\n  }\n\n  Future<void> _initService() async {\n    _registerMainPort(receiverPort.sendPort);\n    receiverPort.listen((message) {\n      if (message is SendPort) {\n        if (_canSendCompleter.isCompleted) {\n          sendPort = null;\n          _canSendCompleter = Completer();\n        }\n        sendPort = message;\n        _canSendCompleter.complete(true);\n      } else {\n        handleResult(ActionResult.fromJson(json.decode(message)));\n      }\n    });\n    final alreadyRunning = await service?.isServiceEngineRunning() ?? false;\n    if (alreadyRunning) {\n      await service?.reconnectIpc();\n    } else {\n      await service?.destroy();\n      await service?.init();\n    }\n    await _waitForIpc();\n  }\n\n  Future<void> _waitForIpc() async {\n    for (var attempt = 0; attempt < 3; attempt++) {\n      final connected = await _canSendCompleter.future\n          .timeout(const Duration(seconds: 2), onTimeout: () => false);\n      if (connected) return;\n      commonPrint.log('ClashLib: IPC attempt ${attempt + 1}/3 failed, retrying...');\n      _canSendCompleter = Completer();\n      await service?.reconnectIpc();\n    }\n    commonPrint.log('ClashLib: IPC failed after 3 attempts');\n  }\n\n  void _registerMainPort(SendPort sendPort) {\n    IsolateNameServer.removePortNameMapping(mainIsolate);\n    IsolateNameServer.registerPortWithName(sendPort, mainIsolate);\n  }\n\n  factory ClashLib() {\n    _instance ??= ClashLib._internal();\n    return _instance!;\n  }\n\n  @override\n  destroy() async {\n    await service?.destroy();\n    return true;\n  }\n\n  @override\n  reStart() {\n    _initService();\n  }\n\n  @override\n  Future<bool> shutdown() async {\n    await super.shutdown();\n    destroy();\n    return true;\n  }\n\n  @override\n  sendMessage(String message) async {\n    await _canSendCompleter.future;\n    try {\n      sendPort?.send(message);\n    } catch (e) {\n      commonPrint.log('ClashLib: sendMessage failed: $e, reconnecting IPC');\n      sendPort = null;\n      _canSendCompleter = Completer();\n      await service?.reconnectIpc();\n      await _waitForIpc();\n      sendPort?.send(message);\n    }\n  }\n\n  // @override\n  // Future<bool> stopTun() {\n  //   return invoke<bool>(\n  //     method: ActionMethod.stopTun,\n  //   );\n  // }\n\n  @override\n  Future<AndroidVpnOptions?> getAndroidVpnOptions() async {\n    final res = await invoke<String>(method: ActionMethod.getAndroidVpnOptions);\n    if (res.isEmpty) return null;\n    return AndroidVpnOptions.fromJson(json.decode(res));\n  }\n\n  @override\n  Future<bool> updateDns(String value) {\n    return invoke<bool>(method: ActionMethod.updateDns, data: value);\n  }\n\n  @override\n  Future<DateTime?> getRunTime() async {\n    final runTimeString = await invoke<String>(method: ActionMethod.getRunTime);\n    if (runTimeString.isEmpty) return null;\n    return DateTime.fromMillisecondsSinceEpoch(int.parse(runTimeString));\n  }\n\n  @override\n  Future<String> getCurrentProfileName() {\n    return invoke<String>(method: ActionMethod.getCurrentProfileName);\n  }\n}\n\nclass ClashLibHandler {\n  static ClashLibHandler? _instance;\n\n  late final ClashFFI clashFFI;\n\n  late final DynamicLibrary lib;\n\n  ClashLibHandler._internal() {\n    lib = DynamicLibrary.open('libclash.so');\n    clashFFI = ClashFFI(lib);\n    clashFFI.initNativeApiBridge(NativeApi.initializeApiDLData);\n  }\n\n  factory ClashLibHandler() {\n    _instance ??= ClashLibHandler._internal();\n    return _instance!;\n  }\n\n  Future<String> invokeAction(String actionParams) {\n    final completer = Completer<String>();\n    final receiver = ReceivePort();\n    receiver.listen((message) {\n      if (!completer.isCompleted) {\n        completer.complete(message);\n        receiver.close();\n      }\n    });\n    final actionParamsChar = actionParams.toNativeUtf8().cast<Char>();\n    clashFFI.invokeAction(actionParamsChar, receiver.sendPort.nativePort);\n    malloc.free(actionParamsChar);\n    return completer.future;\n  }\n\n  void attachMessagePort(int messagePort) {\n    clashFFI.attachMessagePort(messagePort);\n  }\n\n  void updateDns(String dns) {\n    final dnsChar = dns.toNativeUtf8().cast<Char>();\n    clashFFI.updateDns(dnsChar);\n    malloc.free(dnsChar);\n  }\n\n  void setState(CoreState state) {\n    final stateChar = json.encode(state).toNativeUtf8().cast<Char>();\n    clashFFI.setState(stateChar);\n    malloc.free(stateChar);\n  }\n\n  String getCurrentProfileName() {\n    final currentProfileRaw = clashFFI.getCurrentProfileName();\n    final currentProfile = currentProfileRaw.cast<Utf8>().toDartString();\n    clashFFI.freeCString(currentProfileRaw);\n    return currentProfile;\n  }\n\n  AndroidVpnOptions getAndroidVpnOptions() {\n    final vpnOptionsRaw = clashFFI.getAndroidVpnOptions();\n    final vpnOptions = json.decode(vpnOptionsRaw.cast<Utf8>().toDartString());\n    clashFFI.freeCString(vpnOptionsRaw);\n    return AndroidVpnOptions.fromJson(vpnOptions);\n  }\n\n  Traffic getTraffic() {\n    final trafficRaw = clashFFI.getTraffic();\n    final trafficString = trafficRaw.cast<Utf8>().toDartString();\n    clashFFI.freeCString(trafficRaw);\n    if (trafficString.isEmpty) return Traffic();\n    return Traffic.fromMap(json.decode(trafficString));\n  }\n\n  Traffic getTotalTraffic(bool value) {\n    final trafficRaw = clashFFI.getTotalTraffic();\n    final trafficString = trafficRaw.cast<Utf8>().toDartString();\n    clashFFI.freeCString(trafficRaw);\n    if (trafficString.isEmpty) return Traffic();\n    return Traffic.fromMap(json.decode(trafficString));\n  }\n\n  Future<bool> startListener() async {\n    clashFFI.startListener();\n    return true;\n  }\n\n  Future<bool> stopListener() async {\n    clashFFI.stopListener();\n    return true;\n  }\n\n  DateTime? getRunTime() {\n    final runTimeRaw = clashFFI.getRunTime();\n    final runTimeString = runTimeRaw.cast<Utf8>().toDartString();\n    clashFFI.freeCString(runTimeRaw);\n    if (runTimeString.isEmpty) {\n      return null;\n    }\n    return DateTime.fromMillisecondsSinceEpoch(int.parse(runTimeString));\n  }\n\n  Future<Map<String, dynamic>> getConfig(String id) async {\n    final path = await appPath.getProfilePath(id);\n    final pathChar = path.toNativeUtf8().cast<Char>();\n    final configRaw = clashFFI.getConfig(pathChar);\n    final configString = configRaw.cast<Utf8>().toDartString();\n    malloc.free(pathChar);\n    clashFFI.freeCString(configRaw);\n    if (configString.isEmpty) return {};\n    return json.decode(configString);\n  }\n\n  Future<String> quickStart(\n    InitParams initParams,\n    SetupParams setupParams,\n    CoreState state,\n  ) {\n    final completer = Completer<String>();\n    final receiver = ReceivePort();\n    receiver.listen((message) {\n      if (!completer.isCompleted) {\n        completer.complete(message);\n        receiver.close();\n      }\n    });\n    final params = json.encode(setupParams);\n    final initValue = json.encode(initParams);\n    final stateParams = json.encode(state);\n    final initParamsChar = initValue.toNativeUtf8().cast<Char>();\n    final paramsChar = params.toNativeUtf8().cast<Char>();\n    final stateParamsChar = stateParams.toNativeUtf8().cast<Char>();\n    clashFFI.quickStart(\n      initParamsChar,\n      paramsChar,\n      stateParamsChar,\n      receiver.sendPort.nativePort,\n    );\n    malloc.free(initParamsChar);\n    malloc.free(paramsChar);\n    malloc.free(stateParamsChar);\n    return completer.future;\n  }\n}\n\nClashLib? get clashLib =>\n    system.isAndroid && !globalState.isService ? ClashLib() : null;\n\nClashLibHandler? get clashLibHandler =>\n    system.isAndroid && globalState.isService ? ClashLibHandler() : null;\n"
  },
  {
    "path": "lib/clash/message.dart",
    "content": "import 'dart:async';\n\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:flutter/foundation.dart';\n\nclass ClashMessage {\n  final controller = StreamController<Map<String, Object?>>.broadcast();\n\n  ClashMessage._() {\n    controller.stream.listen((message) {\n      if (message.isEmpty) return;\n      final m = AppMessage.fromJson(message);\n      for (final AppMessageListener listener in _listeners) {\n        switch (m.type) {\n          case AppMessageType.log:\n            listener.onLog(Log.fromJson(m.data));\n          case AppMessageType.delay:\n            listener.onDelay(Delay.fromJson(m.data));\n          case AppMessageType.request:\n            listener.onRequest(TrackerInfo.fromJson(m.data));\n          case AppMessageType.loaded:\n            listener.onLoaded(m.data);\n        }\n      }\n    });\n  }\n\n  static final ClashMessage instance = ClashMessage._();\n\n  final ObserverList<AppMessageListener> _listeners =\n      ObserverList<AppMessageListener>();\n\n  bool get hasListeners {\n    return _listeners.isNotEmpty;\n  }\n\n  void addListener(AppMessageListener listener) {\n    _listeners.add(listener);\n  }\n\n  void removeListener(AppMessageListener listener) {\n    _listeners.remove(listener);\n  }\n}\n\nfinal clashMessage = ClashMessage.instance;\n"
  },
  {
    "path": "lib/clash/service.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\nimport 'dart:io';\n\nimport 'package:bett_box/clash/interface.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/models/core.dart';\nimport 'package:bett_box/state.dart';\n\nclass ClashService extends ClashHandlerInterface {\n  static ClashService? _instance;\n\n  Completer<ServerSocket> serverCompleter = Completer();\n\n  Completer<Socket> socketCompleter = Completer();\n\n  bool isStarting = false;\n  bool _isDestroying = false;\n\n  Process? process;\n\n  factory ClashService() {\n    _instance ??= ClashService._internal();\n    return _instance!;\n  }\n\n  ClashService._internal() {\n    _initServer();\n    reStart();\n  }\n\n  Future<void> _initServer() async {\n    runZonedGuarded(\n      () async {\n        final address = !system.isWindows\n            ? InternetAddress(unixSocketPath, type: InternetAddressType.unix)\n            : InternetAddress(localhost, type: InternetAddressType.IPv4);\n        await _deleteSocketFile();\n        final server = await ServerSocket.bind(address, 0, shared: true);\n        serverCompleter.complete(server);\n        await for (final socket in server) {\n          await _destroySocket();\n          socketCompleter.complete(socket);\n          socket\n              .transform(uint8ListToListIntConverter)\n              .transform(utf8.decoder)\n              .transform(LineSplitter())\n              .listen((data) {\n                handleResult(ActionResult.fromJson(json.decode(data.trim())));\n              });\n        }\n      },\n      (error, stack) {\n        commonPrint.log(error.toString());\n        if (error is SocketException &&\n            !_isDestroying &&\n            !globalState.isExiting) {\n          globalState.showNotifier(error.toString());\n          // globalState.appController.restartCore();\n        }\n      },\n    );\n  }\n\n  @override\n  reStart() async {\n    if (isStarting) return;\n    isStarting = true;\n    _isDestroying = false;\n\n    await _destroySocket();\n\n    process?.kill();\n    for (var i = 0; i < 5; i++) {\n      if (process == null) break;\n      try {\n        process!.exitCode;\n        break;\n      } on StateError {\n        await Future.delayed(const Duration(milliseconds: 100));\n      }\n    }\n    process = null;\n\n    socketCompleter = Completer();\n\n    final serverSocket = await serverCompleter.future;\n    final arg = system.isWindows\n        ? '${serverSocket.port}'\n        : serverSocket.address.address;\n\n    if (system.isWindows) {\n      final serviceOk = await windows?.registerService() ?? false;\n      if (serviceOk) {\n        final isSuccess = await request.startCoreByHelper(arg);\n        if (isSuccess) {\n          await _waitForCoreReady();\n          isStarting = false;\n          return;\n        }\n      }\n    }\n\n    final homeDirPath = await appPath.homeDirPath;\n    final environment = Map<String, String>.from(Platform.environment);\n    environment['SAFE_PATHS'] = homeDirPath;\n\n    process = await Process.start(appPath.corePath, [\n      arg,\n    ], environment: environment);\n    process?.stdout.listen((_) {});\n    process?.stderr.listen((e) {\n      final error = utf8.decode(e);\n      if (error.isNotEmpty) commonPrint.log(error);\n    });\n    await _waitForCoreReady();\n    isStarting = false;\n  }\n\n  Future<void> _waitForCoreReady() async {\n    const maxAttempts = 10;\n    const interval = Duration(milliseconds: 500);\n\n    for (var attempt = 1; attempt <= maxAttempts; attempt++) {\n      if (socketCompleter.isCompleted) return;\n      await Future.delayed(interval);\n    }\n    commonPrint.log(\n      'Core ready timeout after ${maxAttempts * interval.inMilliseconds}ms',\n    );\n  }\n\n  @override\n  destroy() async {\n    _isDestroying = true;\n    final server = await serverCompleter.future;\n    await server.close();\n    await _deleteSocketFile();\n    return true;\n  }\n\n  @override\n  sendMessage(String message) async {\n    if (_isDestroying || globalState.isExiting) {\n      return;\n    }\n    final socket = await socketCompleter.future;\n    try {\n      socket.writeln(message);\n    } on SocketException catch (e) {\n      if (_isDestroying || globalState.isExiting) {\n        commonPrint.log('Ignore socket error during shutdown: $e');\n        return;\n      }\n      rethrow;\n    }\n  }\n\n  Future<void> _deleteSocketFile() async {\n    if (!system.isWindows) {\n      final file = File(unixSocketPath);\n      if (await file.exists()) {\n        await file.delete();\n      }\n    }\n  }\n\n  Future<void> _destroySocket() async {\n    if (socketCompleter.isCompleted) {\n      final lastSocket = await socketCompleter.future;\n      await lastSocket.close();\n      socketCompleter = Completer();\n    }\n  }\n\n  @override\n  shutdown() async {\n    _isDestroying = true;\n    if (system.isWindows) {\n      await request.stopCoreByHelper();\n    }\n    await _destroySocket();\n    process?.kill();\n    process = null;\n    return true;\n  }\n\n  @override\n  Future<bool> preload() async {\n    await serverCompleter.future;\n    return true;\n  }\n}\n\nfinal clashService = system.isDesktop ? ClashService() : null;\n"
  },
  {
    "path": "lib/common/android.dart",
    "content": "import 'package:bett_box/plugins/app.dart';\nimport 'package:bett_box/state.dart';\n\nimport 'system.dart';\n\nclass Android {\n  Future<void> init() async {\n    app.onExit = () async {\n      await globalState.appController.savePreferences();\n    };\n  }\n}\n\nfinal android = system.isAndroid ? Android() : null;\n"
  },
  {
    "path": "lib/common/app_localizations.dart",
    "content": "import 'package:bett_box/l10n/l10n.dart';\n\nfinal appLocalizations = AppLocalizations.current;\n"
  },
  {
    "path": "lib/common/archive.dart",
    "content": "import 'dart:convert';\nimport 'dart:io';\n\nimport 'package:archive/archive_io.dart';\nimport 'package:path/path.dart';\n\nextension ArchiveExt on Archive {\n  Future<void> addDirectoryToArchive(String dirPath, String parentPath) async {\n    final dir = Directory(dirPath);\n    final entities = await dir.list(recursive: false).toList();\n    for (final entity in entities) {\n      final relativePath = relative(entity.path, from: parentPath).replaceAll('\\\\', '/');\n      if (entity is File) {\n        final data = await entity.readAsBytes();\n        final archiveFile = ArchiveFile(relativePath, data.length, data);\n        addFile(archiveFile);\n      } else if (entity is Directory) {\n        await addDirectoryToArchive(entity.path, parentPath);\n      }\n    }\n  }\n\n  void add<T>(String name, T raw) {\n    final data = json.encode(raw);\n    addFile(ArchiveFile(name, data.length, utf8.encode(data)));\n  }\n}\n"
  },
  {
    "path": "lib/common/color.dart",
    "content": "import 'dart:math';\n\nimport 'package:flutter/material.dart';\n\nextension ColorExtension on Color {\n  Color get opacity80 {\n    return withAlpha(204);\n  }\n\n  Color get opacity60 {\n    return withAlpha(153);\n  }\n\n  Color get opacity50 {\n    return withAlpha(128);\n  }\n\n  Color get opacity38 {\n    return withAlpha(97);\n  }\n\n  Color get opacity30 {\n    return withAlpha(77);\n  }\n\n  Color get opacity12 {\n    return withAlpha(31);\n  }\n\n  Color get opacity15 {\n    return withAlpha(38);\n  }\n\n  Color get opacity10 {\n    return withAlpha(15);\n  }\n\n  Color get opacity3 {\n    return withAlpha(76);\n  }\n\n  Color get opacity0 {\n    return withAlpha(0);\n  }\n\n  int get value32bit {\n    return _floatToInt8(a) << 24 |\n        _floatToInt8(r) << 16 |\n        _floatToInt8(g) << 8 |\n        _floatToInt8(b) << 0;\n  }\n\n  int get alpha8bit => (0xff000000 & value32bit) >> 24;\n\n  int get red8bit => (0x00ff0000 & value32bit) >> 16;\n\n  int get green8bit => (0x0000ff00 & value32bit) >> 8;\n\n  int get blue8bit => (0x000000ff & value32bit) >> 0;\n\n  int _floatToInt8(double x) {\n    return (x * 255.0).round() & 0xff;\n  }\n\n  Color lighten([double amount = 10]) {\n    if (amount <= 0) return this;\n    if (amount > 100) return Colors.white;\n    final HSLColor hsl = this == const Color(0xFF000000)\n        ? HSLColor.fromColor(this).withSaturation(0)\n        : HSLColor.fromColor(this);\n    return hsl\n        .withLightness(min(1, max(0, hsl.lightness + amount / 100)))\n        .toColor();\n  }\n\n  String get hex {\n    final value = toARGB32();\n    final red = (value >> 16) & 0xFF;\n    final green = (value >> 8) & 0xFF;\n    final blue = value & 0xFF;\n    return '#${red.toRadixString(16).padLeft(2, '0')}'\n            '${green.toRadixString(16).padLeft(2, '0')}'\n            '${blue.toRadixString(16).padLeft(2, '0')}'\n        .toUpperCase();\n  }\n\n  Color darken([final int amount = 10]) {\n    if (amount <= 0) return this;\n    if (amount > 100) return Colors.black;\n    final HSLColor hsl = HSLColor.fromColor(this);\n    return hsl\n        .withLightness(min(1, max(0, hsl.lightness - amount / 100)))\n        .toColor();\n  }\n\n  Color blendDarken(BuildContext context, {double factor = 0.1}) {\n    final brightness = Theme.of(context).brightness;\n    return Color.lerp(\n      this,\n      brightness == Brightness.dark ? Colors.white : Colors.black,\n      factor,\n    )!;\n  }\n\n  Color blendLighten(BuildContext context, {double factor = 0.1}) {\n    final brightness = Theme.of(context).brightness;\n    return Color.lerp(\n      this,\n      brightness == Brightness.dark ? Colors.black : Colors.white,\n      factor,\n    )!;\n  }\n}\n\nextension ColorSchemeExtension on ColorScheme {\n  ColorScheme toPureBlack(bool isPrueBlack) => isPrueBlack\n      ? copyWith(\n          surface: Colors.black,\n          surfaceContainer: surfaceContainer.darken(5),\n        )\n      : this;\n}\n"
  },
  {
    "path": "lib/common/common.dart",
    "content": "export 'android.dart';\nexport 'app_localizations.dart';\nexport 'color.dart';\nexport 'constant.dart';\nexport 'context.dart';\nexport 'converter.dart';\nexport 'datetime.dart';\nexport 'fixed.dart';\nexport 'function.dart';\nexport 'future.dart';\nexport 'http.dart';\nexport 'icons.dart';\nexport 'iterable.dart';\nexport 'js_runtime_manager.dart';\nexport 'keyboard.dart';\nexport 'launch.dart';\nexport 'link.dart';\nexport 'lock.dart';\nexport 'measure.dart';\nexport 'mixin.dart';\nexport 'navigation.dart';\nexport 'navigator.dart';\nexport 'network.dart';\nexport 'num.dart';\nexport 'package.dart';\nexport 'path.dart';\nexport 'picker.dart';\nexport 'preferences.dart';\nexport 'print.dart';\nexport 'protocol.dart';\nexport 'proxy.dart';\nexport 'render.dart';\nexport 'request.dart';\nexport 'scroll.dart';\nexport 'string.dart';\nexport 'system.dart';\nexport 'task.dart';\nexport 'text.dart';\nexport 'tray.dart';\nexport 'ui_manager.dart';\nexport 'utils.dart';\nexport 'window.dart';\n"
  },
  {
    "path": "lib/common/constant.dart",
    "content": "import 'dart:math';\nimport 'dart:ui';\n\nimport 'package:collection/collection.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:flutter/material.dart';\n\nconst appName = 'Bettbox';\nconst appHelperService = 'BettboxHelperService';\nconst coreName = 'clash.meta';\nconst tunDeviceName = 'Bettbox';\nconst browserUa =\n    'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/143.0.0.0 Safari/537.36';\nconst packageName = 'com.appshub.bettbox';\nfinal unixSocketPath = '/tmp/BettboxSocket_${Random().nextInt(10000)}.sock';\nconst helperPort = 45678;\nconst maxTextScale = 1.4;\nconst minTextScale = 0.8;\nfinal baseInfoEdgeInsets = EdgeInsets.symmetric(\n  vertical: 16.ap,\n  horizontal: 16.ap,\n);\n\nfinal defaultTextScaleFactor =\n    WidgetsBinding.instance.platformDispatcher.textScaleFactor;\nconst httpTimeoutDuration = Duration(milliseconds: 5000);\nconst moreDuration = Duration(milliseconds: 100);\nconst animateDuration = Duration(milliseconds: 100);\nconst midDuration = Duration(milliseconds: 200);\nconst commonDuration = Duration(milliseconds: 300);\nconst defaultUpdateDuration = Duration(days: 1);\nconst mmdbFileName = 'geoip.metadb';\nconst asnFileName = 'ASN.mmdb';\nconst geoIpFileName = 'GeoIP.dat';\nconst geoSiteFileName = 'GeoSite.dat';\nfinal double kHeaderHeight = system.isDesktop\n    ? !system.isMacOS\n          ? 40\n          : 28\n    : 0;\nconst profilesDirectoryName = 'profiles';\nconst localhost = '127.0.0.1';\nconst clashConfigKey = 'clash_config';\nconst configKey = 'config';\nconst customSidebarIconKey = 'custom_sidebar_icon';\nconst customDashboardTitleKey = 'custom_dashboard_title';\nconst double dialogCommonWidth = 300;\nconst repository = 'appshubcc/Bettbox';\nconst defaultExternalController = '127.0.0.1:9090';\nconst maxMobileWidth = 600;\nconst maxLaptopWidth = 840;\nconst defaultTestUrl = 'https://g.cn/generate_204';\n\n// Preset test URLs\nconst presetTestUrls = [\n  'https://g.cn/generate_204',\n  'https://www.gstatic.com/generate_204',\n  'https://www.google.com/generate_204',\n  'https://cp.cloudflare.com/generate_204',\n  'https://www.apple.com/library/test/success.html',\n];\n\n// Preset NTP servers\nconst defaultNtpServer = 'ntp.aliyun.com';\nconst presetNtpServers = [\n  'ntp.aliyun.com',\n  'time.apple.com',\n  'ntp.tencent.com',\n  'time.windows.com',\n  'time.cloudflare.com',\n];\n\nclass CommonFilters {\n  static final ImageFilter blur = ImageFilter.blur(\n    sigmaX: 2,\n    sigmaY: 2,\n    tileMode: TileMode.mirror,\n  );\n}\n\nfinal commonFilter = CommonFilters.blur;\n\nconst navigationItemListEquality = ListEquality<NavigationItem>();\nconst trackerInfoListEquality = ListEquality<TrackerInfo>();\nconst stringListEquality = ListEquality<String>();\nconst intListEquality = ListEquality<int>();\nconst logListEquality = ListEquality<Log>();\nconst groupListEquality = ListEquality<Group>();\nconst externalProviderListEquality = ListEquality<ExternalProvider>();\nconst packageListEquality = ListEquality<Package>();\nconst hotKeyActionListEquality = ListEquality<HotKeyAction>();\nconst stringAndStringMapEquality = MapEquality<String, String>();\nconst stringAndStringMapEntryIterableEquality =\n    IterableEquality<MapEntry<String, String>>();\nconst delayMapEquality = MapEquality<String, Map<String, int?>>();\nconst stringSetEquality = SetEquality<String>();\nconst keyboardModifierListEquality = SetEquality<KeyboardModifier>();\n\nconst viewModeColumnsMap = {\n  ViewMode.mobile: [2, 1],\n  ViewMode.laptop: [3, 2],\n  ViewMode.desktop: [4, 3],\n};\n\nconst proxiesListStoreKey = PageStorageKey<String>('proxies_list');\nconst toolsStoreKey = PageStorageKey<String>('tools');\nconst profilesStoreKey = PageStorageKey<String>('profiles');\n\nconst defaultPrimaryColor = 0xFF00796B;\n\ndouble getWidgetHeight(num lines) {\n  return max(lines * 84 + (lines - 1) * 16, 0).ap;\n}\n\nconst maxLength = 256;\n\nfinal mainIsolate = 'BettboxMainIsolate';\n\nfinal serviceIsolate = 'BettboxServiceIsolate';\n\nconst defaultPrimaryColors = [\n  0xFF191919,\n  0xFF1976D2,\n  defaultPrimaryColor,\n  0xFFE91E63,\n  0xFF7B1FA2,\n  0xFFD97706,\n  0xFF455A64,\n];\n\nconst scriptTemplate = '''\nconst main = (config) => {\n  return config;\n}''';\n"
  },
  {
    "path": "lib/common/context.dart",
    "content": "import 'package:bett_box/manager/message_manager.dart';\nimport 'package:bett_box/widgets/scaffold.dart';\nimport 'package:flutter/material.dart';\n\nextension BuildContextExtension on BuildContext {\n  CommonScaffoldState? get commonScaffoldState {\n    return findAncestorStateOfType<CommonScaffoldState>();\n  }\n\n  Future<void>? showNotifier(String text,\n      {VoidCallback? onAction, String? actionLabel, bool showCountdown = false}) {\n    return findAncestorStateOfType<MessageManagerState>()\n        ?.message(text, onAction: onAction, actionLabel: actionLabel, showCountdown: showCountdown);\n  }\n\n  void showSnackBar(String message, {SnackBarAction? action}) {\n    final width = viewWidth;\n    EdgeInsets margin;\n    if (width < 600) {\n      margin = const EdgeInsets.only(bottom: 16, right: 16, left: 16);\n    } else {\n      margin = EdgeInsets.only(bottom: 16, left: 16, right: width - 316);\n    }\n    ScaffoldMessenger.of(this).showSnackBar(\n      SnackBar(\n        action: action,\n        content: Text(message),\n        behavior: SnackBarBehavior.floating,\n        duration: const Duration(milliseconds: 1500),\n        margin: margin,\n      ),\n    );\n  }\n\n  Size get appSize {\n    return MediaQuery.of(this).size;\n  }\n\n  double get viewWidth {\n    return appSize.width;\n  }\n\n  ColorScheme get colorScheme => Theme.of(this).colorScheme;\n\n  TextTheme get textTheme => Theme.of(this).textTheme;\n\n  T? findLastStateOfType<T extends State>() {\n    T? state;\n\n    visitor(Element element) {\n      if (!element.mounted) {\n        return;\n      }\n      if (element is StatefulElement) {\n        if (element.state is T) {\n          state = element.state as T;\n        }\n      }\n      element.visitChildren(visitor);\n    }\n\n    visitor(this as Element);\n    return state;\n  }\n}\n\nclass BackHandleInherited extends InheritedWidget {\n  final Function handleBack;\n\n  const BackHandleInherited({\n    super.key,\n    required this.handleBack,\n    required super.child,\n  });\n\n  static BackHandleInherited? of(BuildContext context) =>\n      context.dependOnInheritedWidgetOfExactType<BackHandleInherited>();\n\n  @override\n  bool updateShouldNotify(BackHandleInherited oldWidget) {\n    return handleBack != oldWidget.handleBack;\n  }\n}\n"
  },
  {
    "path": "lib/common/converter.dart",
    "content": "import 'dart:convert';\nimport 'dart:typed_data';\n\nclass Uint8ListToListIntConverter extends Converter<Uint8List, List<int>> {\n  @override\n  List<int> convert(Uint8List input) {\n    return input.toList();\n  }\n\n  @override\n  Sink<Uint8List> startChunkedConversion(Sink<List<int>> sink) {\n    return _Uint8ListToListIntConverterSink(sink);\n  }\n}\n\nclass _Uint8ListToListIntConverterSink implements Sink<Uint8List> {\n  const _Uint8ListToListIntConverterSink(this._target);\n\n  final Sink<List<int>> _target;\n\n  @override\n  void add(Uint8List data) {\n    _target.add(data.toList());\n  }\n\n  @override\n  void close() {\n    _target.close();\n  }\n}\n\nfinal uint8ListToListIntConverter = Uint8ListToListIntConverter();\n"
  },
  {
    "path": "lib/common/datetime.dart",
    "content": "import 'package:bett_box/common/app_localizations.dart';\n\nextension DateTimeExtension on DateTime {\n  bool get isBeforeNow {\n    return isBefore(DateTime.now());\n  }\n\n  bool isBeforeSecure(DateTime? dateTime) {\n    if (dateTime == null) {\n      return false;\n    }\n    return true;\n  }\n\n  String get lastUpdateTimeDesc {\n    final currentDateTime = DateTime.now();\n    final difference = currentDateTime.difference(this);\n    final days = difference.inDays;\n    if (days >= 365) {\n      return '${(days / 365).floor()} ${appLocalizations.years}${appLocalizations.ago}';\n    }\n    if (days >= 30) {\n      return '${(days / 30).floor()} ${appLocalizations.months}${appLocalizations.ago}';\n    }\n    if (days >= 1) {\n      return '$days ${appLocalizations.days}${appLocalizations.ago}';\n    }\n    final hours = difference.inHours;\n    if (hours >= 1) {\n      return '$hours ${appLocalizations.hours}${appLocalizations.ago}';\n    }\n    final minutes = difference.inMinutes;\n    if (minutes >= 1) {\n      return '$minutes ${appLocalizations.minutes}${appLocalizations.ago}';\n    }\n    return appLocalizations.just;\n  }\n\n  String get show {\n    return toLocal().toString().substring(0, 10);\n  }\n\n  String get showFull {\n    return toLocal().toString().substring(0, 19);\n  }\n\n  String get showTime {\n    return toLocal().toString().substring(10, 19);\n  }\n}\n"
  },
  {
    "path": "lib/common/dav_client.dart",
    "content": "import 'dart:async';\nimport 'dart:typed_data';\n\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:webdav_client/webdav_client.dart';\n\nclass DAVClient {\n  late Client client;\n  Completer<bool> pingCompleter = Completer();\n  late String fileName;\n\n  DAVClient(DAV dav) {\n    client = newClient(dav.uri, user: dav.user, password: dav.password);\n    fileName = dav.fileName;\n    client.setHeaders({'accept-charset': 'utf-8', 'Content-Type': 'text/xml'});\n    // 增加超时时间以适应慢速网络\n    client.setConnectTimeout(15000); // 15秒连接超时\n    client.setSendTimeout(120000); // 2分钟发送超时（大文件）\n    client.setReceiveTimeout(120000); // 2分钟接收超时（大文件）\n    pingCompleter.complete(_ping());\n  }\n\n  Future<bool> _ping() async {\n    try {\n      await client.ping();\n      commonPrint.log('WebDAV ping successful');\n      return true;\n    } catch (e) {\n      commonPrint.log('WebDAV ping failed: $e');\n      return false;\n    }\n  }\n\n  String get root => '/$appName';\n\n  String get backupFile => '$root/$fileName';\n\n  /// 备份数据到 WebDAV（带重试机制）\n  Future<bool> backup(Uint8List data) async {\n    return await _retryOperation(() async {\n      commonPrint.log(\n        'WebDAV backup: uploading ${data.length} bytes to $backupFile',\n      );\n\n      // 确保目录存在\n      try {\n        await client.mkdir(root);\n      } catch (e) {\n        // 目录可能已存在，忽略错误\n        commonPrint.log('WebDAV mkdir warning (may already exist): $e');\n      }\n\n      // 上传文件\n      await client.write(backupFile, data);\n      commonPrint.log('WebDAV backup successful');\n      return true;\n    }, operationName: 'backup');\n  }\n\n  /// 从 WebDAV 恢复数据（带重试机制）\n  Future<List<int>> recovery() async {\n    return await _retryOperation(() async {\n      commonPrint.log('WebDAV recovery: downloading from $backupFile');\n\n      // 确保目录存在\n      try {\n        await client.mkdir(root);\n      } catch (e) {\n        commonPrint.log('WebDAV mkdir warning: $e');\n      }\n\n      // 下载文件\n      final data = await client.read(backupFile);\n      commonPrint.log('WebDAV recovery successful: ${data.length} bytes');\n      return data;\n    }, operationName: 'recovery');\n  }\n\n  /// 重试机制：最多重试3次，指数退避\n  Future<T> _retryOperation<T>(\n    Future<T> Function() operation, {\n    required String operationName,\n    int maxAttempts = 3,\n  }) async {\n    int attempt = 0;\n    Duration delay = const Duration(seconds: 2);\n\n    while (attempt < maxAttempts) {\n      attempt++;\n\n      try {\n        return await operation();\n      } catch (e) {\n        final isLastAttempt = attempt >= maxAttempts;\n\n        if (isLastAttempt) {\n          // 最后一次尝试失败，抛出详细错误\n          commonPrint.log(\n            'WebDAV $operationName failed after $maxAttempts attempts: $e',\n          );\n          throw 'WebDAV $operationName failed: ${_formatError(e)}';\n        }\n\n        // 非最后一次尝试，等待后重试\n        commonPrint.log(\n          'WebDAV $operationName attempt $attempt failed: $e, retrying in ${delay.inSeconds}s...',\n        );\n        await Future.delayed(delay);\n\n        // 指数退避：2s -> 4s -> 8s\n        delay *= 2;\n      }\n    }\n\n    throw 'WebDAV $operationName failed: unexpected error';\n  }\n\n  /// 格式化错误信息，提供更友好的提示\n  String _formatError(dynamic error) {\n    final errorStr = error.toString();\n\n    // 常见错误的友好提示\n    if (errorStr.contains('SocketException') ||\n        errorStr.contains('Connection')) {\n      return 'Network connection failed. Please check your internet connection and WebDAV server address.';\n    }\n\n    if (errorStr.contains('401') || errorStr.contains('Unauthorized')) {\n      return 'Authentication failed. Please check your username and password.';\n    }\n\n    if (errorStr.contains('403') || errorStr.contains('Forbidden')) {\n      return 'Access denied. Please check your account permissions.';\n    }\n\n    if (errorStr.contains('404') || errorStr.contains('Not Found')) {\n      return 'Backup file not found on server.';\n    }\n\n    if (errorStr.contains('timeout') || errorStr.contains('Timeout')) {\n      return 'Operation timed out. Please check your network connection or try again later.';\n    }\n\n    if (errorStr.contains('507') || errorStr.contains('Insufficient Storage')) {\n      return 'Server storage is full. Please free up space on your WebDAV server.';\n    }\n\n    // 返回原始错误（如果无法识别）\n    return errorStr;\n  }\n}\n"
  },
  {
    "path": "lib/common/fixed.dart",
    "content": "import 'iterable.dart';\n\ntypedef ValueCallback<T> = T Function();\n\nclass FixedList<T> {\n  final int maxLength;\n  final List<T> _list;\n\n  FixedList(this.maxLength, {List<T>? list})\n    : _list = (list ?? [])..truncate(maxLength);\n\n  void add(T item) {\n    _list.add(item);\n    _list.truncate(maxLength);\n  }\n\n  void clear() {\n    _list.clear();\n  }\n\n  List<T> get list => List.unmodifiable(_list);\n\n  int get length => _list.length;\n\n  T operator [](int index) => _list[index];\n\n  FixedList<T> copyWith() {\n    return FixedList(maxLength, list: _list);\n  }\n}\n\nclass FixedMap<K, V> {\n  int maxLength;\n  late Map<K, V> _map;\n\n  FixedMap(this.maxLength, {Map<K, V>? map}) {\n    _map = map ?? {};\n  }\n\n  V updateCacheValue(K key, ValueCallback<V> callback) {\n    final realValue = _map.updateCacheValue(key, callback);\n    _adjustMap();\n    return realValue;\n  }\n\n  void clear() {\n    _map.clear();\n  }\n\n  void updateMaxLength(int size) {\n    maxLength = size;\n    _adjustMap();\n  }\n\n  void updateMap(Map<K, V> map) {\n    _map = map;\n    _adjustMap();\n  }\n\n  void _adjustMap() {\n    if (_map.length > maxLength) {\n      _map = Map.fromEntries(map.entries.toList()..truncate(maxLength));\n    }\n  }\n\n  V? get(K key) => _map[key];\n\n  bool containsKey(K key) => _map.containsKey(key);\n\n  int get length => _map.length;\n\n  Map<K, V> get map => Map.unmodifiable(_map);\n}\n"
  },
  {
    "path": "lib/common/flclash_database_extractor.dart",
    "content": "import 'dart:convert';\n\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:sqflite_common_ffi/sqflite_ffi.dart';\n\n/// FlClash 数据库提取工具\nclass FlClashDatabaseExtractor {\n  /// 从 FlClash 数据库文件提取 Profiles\n  static Future<List<Profile>> extractProfiles(String dbPath) async {\n    Database? db;\n    try {\n      if (system.isDesktop) {\n        sqfliteFfiInit();\n        databaseFactory = databaseFactoryFfi;\n      }\n\n      db = await openDatabase(dbPath, readOnly: true, singleInstance: false);\n      final List<Map<String, dynamic>> results = await db.query('profiles');\n\n      final profiles = <Profile>[];\n      for (final row in results) {\n        try {\n          profiles.add(_convertRowToProfile(row));\n        } catch (e) {\n          commonPrint.log('Failed to convert profile row: $e, row=$row');\n        }\n      }\n      return profiles;\n    } catch (e) {\n      commonPrint.log('Failed to extract profiles from database: $e');\n      rethrow;\n    } finally {\n      await db?.close();\n    }\n  }\n\n  /// 将数据库行转换为 Profile 对象\n  static Profile _convertRowToProfile(Map<String, dynamic> row) {\n    final id = row['id'].toString();\n    final label = (row['label'] as String?) ?? 'Subscription $id';\n    final url = (row['url'] as String?) ?? '';\n    final currentGroupName = row['current_group_name'] as String?;\n\n    // 解析更新时间 (Drift 默认存储为秒)\n    final lastUpdateDateSecs = row['last_update_date'];\n    DateTime? lastUpdateDate;\n    if (lastUpdateDateSecs != null) {\n      final secs = lastUpdateDateSecs is int\n          ? lastUpdateDateSecs\n          : int.tryParse(lastUpdateDateSecs.toString());\n      if (secs != null) {\n        lastUpdateDate = DateTime.fromMillisecondsSinceEpoch(secs * 1000);\n      }\n    }\n\n    final autoUpdateRaw = row['auto_update'];\n    final autoUpdate = autoUpdateRaw == 1 || autoUpdateRaw == true;\n    final autoUpdateDurationMillis = (row['auto_update_duration_millis'] as int?) ?? 0;\n    final autoUpdateDuration = Duration(milliseconds: autoUpdateDurationMillis);\n\n    final subscriptionInfo = _parseSubscriptionInfo(row['subscription_info']);\n    final selectedMap = _parseSelectedMap(row['selected_map']);\n    final unfoldSet = _parseUnfoldSet(row['unfold_set']);\n\n    final overwriteTypeStr = row['overwrite_type'] as String?;\n    final overwriteType = _parseOverwriteType(overwriteTypeStr);\n\n    commonPrint.log('Converted profile: id=$id, label=$label');\n\n    return Profile(\n      id: id,\n      label: label,\n      url: url,\n      currentGroupName: currentGroupName,\n      lastUpdateDate: lastUpdateDate,\n      autoUpdate: autoUpdate,\n      autoUpdateDuration: autoUpdateDuration,\n      subscriptionInfo: subscriptionInfo,\n      selectedMap: selectedMap,\n      unfoldSet: unfoldSet,\n      overrideData: OverrideData(\n        enable: false,\n        rule: OverrideRule(type: overwriteType),\n      ),\n    );\n  }\n\n  static SubscriptionInfo? _parseSubscriptionInfo(dynamic jsonStr) {\n    if (jsonStr == null || jsonStr is! String || jsonStr.isEmpty) return null;\n    try {\n      return SubscriptionInfo.fromJson(json.decode(jsonStr) as Map<String, dynamic>);\n    } catch (e) {\n      commonPrint.log('Failed to parse subscription_info: $e');\n      return null;\n    }\n  }\n\n  static Map<String, String> _parseSelectedMap(dynamic jsonStr) {\n    if (jsonStr == null || jsonStr is! String || jsonStr.isEmpty) return {};\n    try {\n      return Map<String, String>.from(json.decode(jsonStr) as Map);\n    } catch (e) {\n      commonPrint.log('Failed to parse selected_map: $e');\n      return {};\n    }\n  }\n\n  static Set<String> _parseUnfoldSet(dynamic jsonStr) {\n    if (jsonStr == null || jsonStr is! String || jsonStr.isEmpty) return {};\n    try {\n      return Set<String>.from(json.decode(jsonStr) as List);\n    } catch (e) {\n      commonPrint.log('Failed to parse unfold_set: $e');\n      return {};\n    }\n  }\n\n  static OverrideRuleType _parseOverwriteType(String? typeStr) {\n    return typeStr == 'script' ? OverrideRuleType.override : OverrideRuleType.added;\n  }\n}\n"
  },
  {
    "path": "lib/common/function.dart",
    "content": "import 'dart:async';\n\nimport 'package:bett_box/enum/enum.dart';\n\nclass Debouncer {\n  final Map<FunctionTag, Timer?> _operations = {};\n\n  void call(\n    FunctionTag tag,\n    Function func, {\n    List<dynamic>? args,\n    Duration duration = const Duration(milliseconds: 600),\n  }) {\n    final timer = _operations[tag];\n    if (timer != null) {\n      timer.cancel();\n    }\n    _operations[tag] = Timer(duration, () {\n      _operations[tag]?.cancel();\n      _operations.remove(tag);\n      Function.apply(func, args);\n    });\n  }\n\n  void cancel(dynamic tag) {\n    _operations[tag]?.cancel();\n    _operations[tag] = null;\n  }\n}\n\nclass Throttler {\n  final Map<FunctionTag, Timer?> _operations = {};\n\n  bool call(\n    FunctionTag tag,\n    Function func, {\n    List<dynamic>? args,\n    Duration duration = const Duration(milliseconds: 600),\n  }) {\n    final timer = _operations[tag];\n    if (timer != null) {\n      return true;\n    }\n    _operations[tag] = Timer(duration, () {\n      _operations[tag]?.cancel();\n      _operations.remove(tag);\n      Function.apply(func, args);\n    });\n    return false;\n  }\n\n  void cancel(dynamic tag) {\n    _operations[tag]?.cancel();\n    _operations[tag] = null;\n  }\n}\n\nFuture<T> retry<T>({\n  required Future<T> Function() task,\n  int maxAttempts = 3,\n  required bool Function(T res) retryIf,\n  Duration delay = Duration.zero,\n}) async {\n  int attempts = 0;\n  while (attempts < maxAttempts) {\n    final res = await task();\n    if (!retryIf(res) || attempts >= maxAttempts) {\n      return res;\n    }\n    attempts++;\n  }\n  throw 'unknown error';\n}\n\nfinal debouncer = Debouncer();\n\nfinal throttler = Throttler();\n"
  },
  {
    "path": "lib/common/future.dart",
    "content": "import 'dart:async';\nimport 'dart:ui';\n\nimport 'package:bett_box/common/common.dart';\n\nextension CompleterExt<T> on Completer<T> {\n  Future<T> safeFuture({\n    Duration? timeout,\n    VoidCallback? onLast,\n    FutureOr<T> Function()? onTimeout,\n    required String functionName,\n  }) {\n    final realTimeout = timeout ?? const Duration(seconds: 30);\n    Timer(realTimeout + commonDuration, () {\n      if (onLast != null) {\n        onLast();\n      }\n    });\n    return future.withTimeout(\n      timeout: realTimeout,\n      functionName: functionName,\n      onTimeout: onTimeout,\n    );\n  }\n}\n\nextension FutureExt<T> on Future<T> {\n  Future<T> withTimeout({\n    required Duration timeout,\n    required String functionName,\n    FutureOr<T> Function()? onTimeout,\n  }) {\n    return this.timeout(\n      timeout,\n      onTimeout: () async {\n        if (onTimeout != null) {\n          return onTimeout();\n        } else {\n          throw TimeoutException('$functionName timeout');\n        }\n      },\n    );\n  }\n}\n"
  },
  {
    "path": "lib/common/helper_auth.dart",
    "content": "import 'dart:convert';\nimport 'dart:ffi';\nimport 'dart:io';\nimport 'dart:math';\nimport 'dart:typed_data';\n\nimport 'package:crypto/crypto.dart';\nimport 'package:ffi/ffi.dart';\nimport 'package:win32/win32.dart';\n\nimport 'path.dart';\n\nconst _cryptProtectUiForbidden = 0x1;\n\nclass HelperAuthManager {\n  static String? _authKey;\n\n  static String? getAuthKey() => _authKey;\n\n  static Future<bool> ensureAuthKey() async {\n    if (_authKey != null) {\n      return false;\n    }\n\n    final file = File(await appPath.helperAuthKeyPath);\n    final existingKey = await _readPersistedAuthKey(file);\n    if (existingKey != null) {\n      _authKey = existingKey;\n      return false;\n    }\n\n    _authKey = _generateRandomKey();\n    await _persistAuthKey(file, _authKey!);\n    return true;\n  }\n\n  static Map<String, String> generateAuthHeaders(String body) {\n    if (_authKey == null) {\n      return {};\n    }\n\n    final timestamp = DateTime.now().millisecondsSinceEpoch ~/ 1000;\n    final message = '$timestamp:$body';\n\n    final keyBytes = _hexToBytes(_authKey!);\n    final messageBytes = utf8.encode(message);\n    final hmacSha256 = Hmac(sha256, keyBytes);\n    final digest = hmacSha256.convert(messageBytes);\n    final signature = digest.toString();\n\n    return {'X-Timestamp': timestamp.toString(), 'X-Signature': signature};\n  }\n\n  static Future<void> clearAuthKey() async {\n    _authKey = null;\n    final file = File(await appPath.helperAuthKeyPath);\n    if (await file.exists()) {\n      await file.delete();\n    }\n  }\n\n  static String _generateRandomKey() {\n    final random = Random.secure();\n    final bytes = List<int>.generate(32, (_) => random.nextInt(256));\n    return bytes.map((b) => b.toRadixString(16).padLeft(2, '0')).join();\n  }\n\n  static Future<String?> _readPersistedAuthKey(File file) async {\n    if (!await file.exists()) {\n      return null;\n    }\n\n    try {\n      final encoded = await file.readAsString();\n      if (encoded.isEmpty) {\n        return null;\n      }\n      final encrypted = base64Decode(encoded);\n      final decrypted = Platform.isWindows\n          ? _unprotectForCurrentUser(Uint8List.fromList(encrypted))\n          : Uint8List.fromList(encrypted);\n      final key = utf8.decode(decrypted);\n      if (!_isValidHexKey(key)) {\n        return null;\n      }\n      return key;\n    } catch (_) {\n      return null;\n    }\n  }\n\n  static Future<void> _persistAuthKey(File file, String key) async {\n    final raw = Uint8List.fromList(utf8.encode(key));\n    final encrypted = Platform.isWindows ? _protectForCurrentUser(raw) : raw;\n    await file.parent.create(recursive: true);\n    await file.writeAsString(base64Encode(encrypted), flush: true);\n  }\n\n  static bool _isValidHexKey(String value) {\n    return RegExp(r'^[0-9a-f]{64}$').hasMatch(value);\n  }\n\n  static List<int> _hexToBytes(String hex) {\n    final result = <int>[];\n    for (var i = 0; i < hex.length; i += 2) {\n      result.add(int.parse(hex.substring(i, i + 2), radix: 16));\n    }\n    return result;\n  }\n\n  static Uint8List _protectForCurrentUser(Uint8List input) {\n    final dataIn = calloc<CRYPT_INTEGER_BLOB>();\n    final dataOut = calloc<CRYPT_INTEGER_BLOB>();\n    final inputBuffer = calloc<Uint8>(input.length);\n\n    try {\n      inputBuffer.asTypedList(input.length).setAll(0, input);\n      dataIn.ref.cbData = input.length;\n      dataIn.ref.pbData = inputBuffer;\n\n      final result = CryptProtectData(\n        dataIn,\n        nullptr,\n        nullptr,\n        nullptr,\n        nullptr,\n        _cryptProtectUiForbidden,\n        dataOut,\n      );\n      if (result == 0) {\n        throw WindowsException(HRESULT_FROM_WIN32(GetLastError()));\n      }\n\n      return Uint8List.fromList(\n        dataOut.ref.pbData.asTypedList(dataOut.ref.cbData),\n      );\n    } finally {\n      if (dataOut.ref.pbData != nullptr) {\n        LocalFree(dataOut.ref.pbData);\n      }\n      calloc.free(inputBuffer);\n      calloc.free(dataIn);\n      calloc.free(dataOut);\n    }\n  }\n\n  static Uint8List _unprotectForCurrentUser(Uint8List input) {\n    final dataIn = calloc<CRYPT_INTEGER_BLOB>();\n    final dataOut = calloc<CRYPT_INTEGER_BLOB>();\n    final inputBuffer = calloc<Uint8>(input.length);\n\n    try {\n      inputBuffer.asTypedList(input.length).setAll(0, input);\n      dataIn.ref.cbData = input.length;\n      dataIn.ref.pbData = inputBuffer;\n\n      final result = CryptUnprotectData(\n        dataIn,\n        nullptr,\n        nullptr,\n        nullptr,\n        nullptr,\n        _cryptProtectUiForbidden,\n        dataOut,\n      );\n      if (result == 0) {\n        throw WindowsException(HRESULT_FROM_WIN32(GetLastError()));\n      }\n\n      return Uint8List.fromList(\n        dataOut.ref.pbData.asTypedList(dataOut.ref.cbData),\n      );\n    } finally {\n      if (dataOut.ref.pbData != nullptr) {\n        LocalFree(dataOut.ref.pbData);\n      }\n      calloc.free(inputBuffer);\n      calloc.free(dataIn);\n      calloc.free(dataOut);\n    }\n  }\n}\n"
  },
  {
    "path": "lib/common/http.dart",
    "content": "import 'dart:io';\n\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/state.dart';\n\nclass BettboxHttpOverrides extends HttpOverrides {\n  static String handleFindProxy(Uri url) {\n    if ([localhost].contains(url.host)) {\n      return 'DIRECT';\n    }\n    final port = globalState.config.patchClashConfig.mixedPort;\n    final isStart = globalState.appState.runTime != null;\n    if (!isStart) return 'DIRECT';\n    return 'PROXY localhost:$port';\n  }\n\n  @override\n  HttpClient createHttpClient(SecurityContext? context) {\n    final client = super.createHttpClient(context);\n    client.badCertificateCallback = (_, _, _) => true;\n    client.findProxy = handleFindProxy;\n    return client;\n  }\n}\n"
  },
  {
    "path": "lib/common/icons.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass IconsExt {\n  static const IconData target = IconData(0xe900, fontFamily: 'Icons');\n}\n"
  },
  {
    "path": "lib/common/iterable.dart",
    "content": "extension IterableExt<T> on Iterable<T> {\n  Iterable<T> separated(T separator) sync* {\n    final iterator = this.iterator;\n    if (!iterator.moveNext()) return;\n\n    yield iterator.current;\n\n    while (iterator.moveNext()) {\n      yield separator;\n      yield iterator.current;\n    }\n  }\n\n  Iterable<List<T>> chunks(int size) sync* {\n    if (length == 0) return;\n    var iterator = this.iterator;\n    while (iterator.moveNext()) {\n      var chunk = [iterator.current];\n      for (var i = 1; i < size && iterator.moveNext(); i++) {\n        chunk.add(iterator.current);\n      }\n      yield chunk;\n    }\n  }\n\n  Iterable<T> fill(int length, {required T Function(int count) filler}) sync* {\n    int count = 0;\n    for (var item in this) {\n      yield item;\n      count++;\n      if (count >= length) return;\n    }\n    while (count < length) {\n      yield filler(count);\n      count++;\n    }\n  }\n\n  Iterable<T> takeLast({int count = 50}) {\n    if (count <= 0) return Iterable.empty();\n    return count >= length ? this : toList().skip(length - count);\n  }\n}\n\nextension ListExt<T> on List<T> {\n  void truncate(int maxLength) {\n    if (maxLength == 0) {\n      return;\n    }\n    if (length > maxLength) {\n      removeRange(0, length - maxLength);\n    }\n  }\n\n  List<T> intersection(List<T> list) {\n    return where((item) => list.contains(item)).toList();\n  }\n\n  List<List<T>> batch(int maxConcurrent) {\n    final batches = (length / maxConcurrent).ceil();\n    final List<List<T>> res = [];\n    for (int i = 0; i < batches; i++) {\n      if (i != batches - 1) {\n        res.add(sublist(i * maxConcurrent, maxConcurrent * (i + 1)));\n      } else {\n        res.add(sublist(i * maxConcurrent, length));\n      }\n    }\n    return res;\n  }\n\n  List<T> safeSublist(int start, [int? end]) {\n    if (start <= 0) return this;\n    if (start > length) return [];\n    if (end != null) {\n      return sublist(start, end.clamp(start, length));\n    }\n    return sublist(start);\n  }\n\n  T safeGet(int index) {\n    if (length > index) return this[index];\n    return last;\n  }\n}\n\nextension DoubleListExt on List<double> {\n  int findInterval(num target) {\n    if (isEmpty) return -1;\n    if (target < first) return -1;\n    if (target >= last) return length - 1;\n\n    int left = 0;\n    int right = length - 1;\n\n    while (left <= right) {\n      int mid = left + (right - left) ~/ 2;\n\n      if (mid == length - 1 ||\n          (this[mid] <= target && target < this[mid + 1])) {\n        return mid;\n      } else if (target < this[mid]) {\n        right = mid - 1;\n      } else {\n        left = mid + 1;\n      }\n    }\n\n    return -1;\n  }\n}\n\nextension MapExt<K, V> on Map<K, V> {\n  V updateCacheValue(K key, V Function() callback) {\n    if (this[key] == null) {\n      this[key] = callback();\n    }\n    return this[key]!;\n  }\n}\n"
  },
  {
    "path": "lib/common/js_runtime_manager.dart",
    "content": "import 'dart:async';\n\nimport 'package:flutter_js/flutter_js.dart';\nimport 'package:synchronized/synchronized.dart';\n\nclass JavaScriptRuntimeManager {\n  static JavascriptRuntime? _instance;\n  static final Lock _lock = Lock();\n  static int _activeCount = 0;\n  static bool _isDisposing = false;\n  static Timer? _disposeTimer;\n  static const Duration _disposeDelay = Duration(seconds: 10);\n\n  static Future<T> execute<T>(\n    Future<T> Function(JavascriptRuntime runtime) task,\n  ) async {\n    final runtime = await _acquire();\n    try {\n      return await task(runtime);\n    } finally {\n      await _release();\n    }\n  }\n\n  static Future<JavascriptRuntime> _acquire() async {\n    return _lock.synchronized(() async {\n      while (_isDisposing) {\n        await Future.delayed(const Duration(milliseconds: 10));\n      }\n      _disposeTimer?.cancel();\n      _disposeTimer = null;\n      _activeCount++;\n      _instance ??= getJavascriptRuntime();\n      return _instance!;\n    });\n  }\n\n  static Future<void> _release() async {\n    await _lock.synchronized(() async {\n      _activeCount--;\n      if (_activeCount <= 0 && _instance != null) {\n        _disposeTimer?.cancel();\n        _disposeTimer = Timer(_disposeDelay, () {\n          dispose();\n        });\n      }\n    });\n  }\n\n  static Future<void> dispose() async {\n    return _lock.synchronized(() async {\n      if (_activeCount > 0) return;\n      if (_instance != null) {\n        _isDisposing = true;\n        try {\n          _instance!.dispose();\n        } catch (_) {}\n        _instance = null;\n        _isDisposing = false;\n      }\n    });\n  }\n}\n"
  },
  {
    "path": "lib/common/keyboard.dart",
    "content": "import 'package:flutter/services.dart';\nimport 'package:uni_platform/uni_platform.dart';\n\nimport 'system.dart';\n\nfinal Map<PhysicalKeyboardKey, String> _knownKeyLabels =\n    <PhysicalKeyboardKey, String>{\n      PhysicalKeyboardKey.keyA: 'A',\n      PhysicalKeyboardKey.keyB: 'B',\n      PhysicalKeyboardKey.keyC: 'C',\n      PhysicalKeyboardKey.keyD: 'D',\n      PhysicalKeyboardKey.keyE: 'E',\n      PhysicalKeyboardKey.keyF: 'F',\n      PhysicalKeyboardKey.keyG: 'G',\n      PhysicalKeyboardKey.keyH: 'H',\n      PhysicalKeyboardKey.keyI: 'I',\n      PhysicalKeyboardKey.keyJ: 'J',\n      PhysicalKeyboardKey.keyK: 'K',\n      PhysicalKeyboardKey.keyL: 'L',\n      PhysicalKeyboardKey.keyM: 'M',\n      PhysicalKeyboardKey.keyN: 'N',\n      PhysicalKeyboardKey.keyO: 'O',\n      PhysicalKeyboardKey.keyP: 'P',\n      PhysicalKeyboardKey.keyQ: 'Q',\n      PhysicalKeyboardKey.keyR: 'R',\n      PhysicalKeyboardKey.keyS: 'S',\n      PhysicalKeyboardKey.keyT: 'T',\n      PhysicalKeyboardKey.keyU: 'U',\n      PhysicalKeyboardKey.keyV: 'V',\n      PhysicalKeyboardKey.keyW: 'W',\n      PhysicalKeyboardKey.keyX: 'X',\n      PhysicalKeyboardKey.keyY: 'Y',\n      PhysicalKeyboardKey.keyZ: 'Z',\n      PhysicalKeyboardKey.digit1: '1',\n      PhysicalKeyboardKey.digit2: '2',\n      PhysicalKeyboardKey.digit3: '3',\n      PhysicalKeyboardKey.digit4: '4',\n      PhysicalKeyboardKey.digit5: '5',\n      PhysicalKeyboardKey.digit6: '6',\n      PhysicalKeyboardKey.digit7: '7',\n      PhysicalKeyboardKey.digit8: '8',\n      PhysicalKeyboardKey.digit9: '9',\n      PhysicalKeyboardKey.digit0: '0',\n      PhysicalKeyboardKey.enter: 'ENTER',\n      PhysicalKeyboardKey.escape: 'ESCAPE',\n      PhysicalKeyboardKey.backspace: 'BACKSPACE',\n      PhysicalKeyboardKey.tab: 'TAB',\n      PhysicalKeyboardKey.space: 'SPACE',\n      PhysicalKeyboardKey.minus: '-',\n      PhysicalKeyboardKey.equal: '=',\n      PhysicalKeyboardKey.bracketLeft: '[',\n      PhysicalKeyboardKey.bracketRight: ']',\n      PhysicalKeyboardKey.backslash: '\\\\',\n      PhysicalKeyboardKey.semicolon: ';',\n      PhysicalKeyboardKey.quote: '\"',\n      PhysicalKeyboardKey.backquote: '`',\n      PhysicalKeyboardKey.comma: ',',\n      PhysicalKeyboardKey.period: '.',\n      PhysicalKeyboardKey.slash: '/',\n      PhysicalKeyboardKey.capsLock: 'CAPSLOCK',\n      PhysicalKeyboardKey.f1: 'F1',\n      PhysicalKeyboardKey.f2: 'F2',\n      PhysicalKeyboardKey.f3: 'F3',\n      PhysicalKeyboardKey.f4: 'F4',\n      PhysicalKeyboardKey.f5: 'F5',\n      PhysicalKeyboardKey.f6: 'F6',\n      PhysicalKeyboardKey.f7: 'F7',\n      PhysicalKeyboardKey.f8: 'F8',\n      PhysicalKeyboardKey.f9: 'F9',\n      PhysicalKeyboardKey.f10: 'F10',\n      PhysicalKeyboardKey.f11: 'F11',\n      PhysicalKeyboardKey.f12: 'F12',\n      PhysicalKeyboardKey.home: 'HOME',\n      PhysicalKeyboardKey.pageUp: 'PAGEUP',\n      PhysicalKeyboardKey.delete: 'DELETE',\n      PhysicalKeyboardKey.end: 'END',\n      PhysicalKeyboardKey.pageDown: 'PAGEDOWN',\n      PhysicalKeyboardKey.arrowRight: '→',\n      PhysicalKeyboardKey.arrowLeft: '←',\n      PhysicalKeyboardKey.arrowDown: '↓',\n      PhysicalKeyboardKey.arrowUp: '↑',\n      PhysicalKeyboardKey.controlLeft: 'CTRL',\n      PhysicalKeyboardKey.shiftLeft: 'SHIFT',\n      PhysicalKeyboardKey.altLeft: 'ALT',\n      PhysicalKeyboardKey.metaLeft: system.isMacOS ? '⌘' : 'WIN',\n      PhysicalKeyboardKey.controlRight: 'CTRL',\n      PhysicalKeyboardKey.shiftRight: 'SHIFT',\n      PhysicalKeyboardKey.altRight: 'ALT',\n      PhysicalKeyboardKey.metaRight: system.isMacOS ? '⌘' : 'WIN',\n      PhysicalKeyboardKey.fn: 'FN',\n    };\n\nextension KeyboardKeyExt on KeyboardKey {\n  String get label {\n    PhysicalKeyboardKey? physicalKey;\n    if (this is LogicalKeyboardKey) {\n      physicalKey = (this as LogicalKeyboardKey).physicalKey;\n    } else if (this is PhysicalKeyboardKey) {\n      physicalKey = this as PhysicalKeyboardKey;\n    }\n    return _knownKeyLabels[physicalKey] ?? physicalKey?.debugName ?? 'Unknown';\n  }\n}\n"
  },
  {
    "path": "lib/common/launch.dart",
    "content": "import 'dart:async';\nimport 'dart:io';\n\nimport 'package:flutter/foundation.dart';\nimport 'package:launch_at_startup/launch_at_startup.dart';\n\nimport 'constant.dart';\nimport 'system.dart';\n\nclass AutoLaunch {\n  static AutoLaunch? _instance;\n\n  AutoLaunch._internal() {\n    launchAtStartup.setup(\n      appName: appName,\n      appPath: Platform.resolvedExecutable,\n    );\n  }\n\n  factory AutoLaunch() {\n    _instance ??= AutoLaunch._internal();\n    return _instance!;\n  }\n\n  Future<bool> get isEnable async {\n    if (system.isWindows) {\n      // Windows 上改为通过任务计划实现开机自启动\n      try {\n        final result = await Process.run('schtasks', [\n          '/Query',\n          '/TN',\n          appName,\n        ]);\n        return result.exitCode == 0;\n      } catch (_) {\n        return false;\n      }\n    }\n    return await launchAtStartup.isEnabled();\n  }\n\n  Future<bool> enable({bool requireNetwork = true}) async {\n    if (system.isWindows) {\n      // 使用任务计划实现 Windows 开机自启动（管理员模式）\n      return await windows?.registerTask(\n            appName,\n            requireNetwork: requireNetwork,\n          ) ??\n          false;\n    }\n    return await launchAtStartup.enable();\n  }\n\n  Future<bool> disable() async {\n    if (system.isWindows) {\n      return await windows?.unregisterTask(appName) ?? false;\n    }\n    return await launchAtStartup.disable();\n  }\n\n  Future<void> updateStatus(\n    bool isAutoLaunch, {\n    bool requireNetwork = true,\n  }) async {\n    if (kDebugMode) {\n      return;\n    }\n    if (await isEnable == isAutoLaunch) return;\n\n    // 异步执行，避免阻塞 UI\n    if (isAutoLaunch == true) {\n      unawaited(enable(requireNetwork: requireNetwork));\n    } else {\n      unawaited(disable());\n    }\n  }\n}\n\nfinal autoLaunch = system.isDesktop ? AutoLaunch() : null;\n"
  },
  {
    "path": "lib/common/link.dart",
    "content": "import 'dart:async';\n\nimport 'package:app_links/app_links.dart';\n\nimport 'print.dart';\n\ntypedef InstallConfigCallBack = void Function(String url);\n\nclass LinkManager {\n  static LinkManager? _instance;\n  late AppLinks _appLinks;\n  StreamSubscription? subscription;\n\n  LinkManager._internal() {\n    _appLinks = AppLinks();\n  }\n\n  Future<void> initAppLinksListen(\n    Function(String url) installConfigCallBack,\n  ) async {\n    commonPrint.log('initAppLinksListen');\n    destroy();\n    subscription = _appLinks.uriLinkStream.listen((uri) {\n      commonPrint.log('onAppLink: $uri');\n      if (uri.host == 'install-config') {\n        final parameters = uri.queryParameters;\n        final url = parameters['url'];\n        if (url != null) {\n          installConfigCallBack(url);\n        }\n      }\n    });\n  }\n\n  void destroy() {\n    subscription?.cancel();\n    subscription = null;\n  }\n\n  factory LinkManager() {\n    _instance ??= LinkManager._internal();\n    return _instance!;\n  }\n}\n\nfinal linkManager = LinkManager();\n"
  },
  {
    "path": "lib/common/lock.dart",
    "content": "import 'dart:io';\n\nimport 'package:bett_box/common/common.dart';\n\nclass SingleInstanceLock {\n  static SingleInstanceLock? _instance;\n  RandomAccessFile? _accessFile;\n\n  SingleInstanceLock._internal();\n\n  factory SingleInstanceLock() {\n    _instance ??= SingleInstanceLock._internal();\n    return _instance!;\n  }\n\n  Future<bool> acquire() async {\n    try {\n      final lockFilePath = await appPath.lockFilePath;\n      final lockFile = File(lockFilePath);\n      await lockFile.create();\n      _accessFile = await lockFile.open(mode: FileMode.write);\n      await _accessFile?.lock();\n      return true;\n    } catch (_) {\n      return false;\n    }\n  }\n}\n\nfinal singleInstanceLock = SingleInstanceLock();\n"
  },
  {
    "path": "lib/common/measure.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:flutter/cupertino.dart';\nimport 'package:flutter/material.dart';\n\nclass Measure {\n  final TextScaler _textScaler;\n  final BuildContext context;\n  final Map<String, dynamic> _measureMap;\n\n  Measure.of(this.context, double textScaleFactor)\n    : _measureMap = {},\n      _textScaler = TextScaler.linear(textScaleFactor);\n\n  Size computeTextSize(Text text, {double maxWidth = double.infinity}) {\n    final textPainter = TextPainter(\n      text: TextSpan(text: text.data, style: text.style),\n      maxLines: text.maxLines,\n      textScaler: _textScaler,\n      textDirection: text.textDirection ?? TextDirection.ltr,\n    )..layout(maxWidth: maxWidth);\n    return textPainter.size;\n  }\n\n  double get bodyMediumHeight {\n    return _measureMap.updateCacheValue(\n      'bodyMediumHeight',\n      () => computeTextSize(\n        Text('X', style: context.textTheme.bodyMedium),\n      ).height,\n    );\n  }\n\n  double get bodyLargeHeight {\n    return _measureMap.updateCacheValue(\n      'bodyLargeHeight',\n      () =>\n          computeTextSize(Text('X', style: context.textTheme.bodyLarge)).height,\n    );\n  }\n\n  double get bodySmallHeight {\n    return _measureMap.updateCacheValue(\n      'bodySmallHeight',\n      () =>\n          computeTextSize(Text('X', style: context.textTheme.bodySmall)).height,\n    );\n  }\n\n  double get labelSmallHeight {\n    return _measureMap.updateCacheValue(\n      'labelSmallHeight',\n      () => computeTextSize(\n        Text('X', style: context.textTheme.labelSmall),\n      ).height,\n    );\n  }\n\n  double get labelMediumHeight {\n    return _measureMap.updateCacheValue(\n      'labelMediumHeight',\n      () => computeTextSize(\n        Text('X', style: context.textTheme.labelMedium),\n      ).height,\n    );\n  }\n\n  double get titleLargeHeight {\n    return _measureMap.updateCacheValue(\n      'titleLargeHeight',\n      () => computeTextSize(\n        Text('X', style: context.textTheme.titleLarge),\n      ).height,\n    );\n  }\n\n  double get titleMediumHeight {\n    return _measureMap.updateCacheValue(\n      'titleMediumHeight',\n      () => computeTextSize(\n        Text('X', style: context.textTheme.titleMedium),\n      ).height,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/common/mixin.dart",
    "content": "import 'package:riverpod/riverpod.dart';\n\nmixin AutoDisposeNotifierMixin<T> on AutoDisposeNotifier<T> {\n  set value(T value) {\n    state = value;\n  }\n\n  @override\n  bool updateShouldNotify(previous, next) {\n    final res = super.updateShouldNotify(previous, next);\n    if (res) {\n      onUpdate(next);\n    }\n    return res;\n  }\n\n  void onUpdate(T value) {}\n}"
  },
  {
    "path": "lib/common/navigation.dart",
    "content": "import 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/providers/providers.dart';\nimport 'package:bett_box/views/views.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass Navigation {\n  static Navigation? _instance;\n\n  List<NavigationItem> getItems({\n    bool openLogs = false,\n    bool hasProxies = false,\n  }) {\n    return [\n      NavigationItem(\n        keep: false,\n        icon: Icon(Icons.space_dashboard),\n        label: PageLabel.dashboard,\n        builder: (_) =>\n            const DashboardView(key: GlobalObjectKey(PageLabel.dashboard)),\n      ),\n      NavigationItem(\n        icon: const Icon(Icons.article),\n        label: PageLabel.proxies,\n        builder: (_) => ProviderScope(\n          overrides: [queryProvider.overrideWith(() => Query())],\n          child: const ProxiesView(key: GlobalObjectKey(PageLabel.proxies)),\n        ),\n        modes: hasProxies\n            ? [NavigationItemMode.mobile, NavigationItemMode.desktop]\n            : [],\n      ),\n      NavigationItem(\n        icon: Icon(Icons.folder),\n        label: PageLabel.profiles,\n        builder: (_) =>\n            const ProfilesView(key: GlobalObjectKey(PageLabel.profiles)),\n      ),\n      NavigationItem(\n        icon: Icon(Icons.view_timeline),\n        label: PageLabel.requests,\n        builder: (_) =>\n            const RequestsView(key: GlobalObjectKey(PageLabel.requests)),\n        description: 'requestsDesc',\n        modes: [NavigationItemMode.desktop, NavigationItemMode.more],\n      ),\n      NavigationItem(\n        icon: Icon(Icons.ballot),\n        label: PageLabel.connections,\n        builder: (_) =>\n            const ConnectionsView(key: GlobalObjectKey(PageLabel.connections)),\n        description: 'connectionsDesc',\n        modes: [NavigationItemMode.desktop, NavigationItemMode.more],\n      ),\n      NavigationItem(\n        icon: Icon(Icons.storage),\n        label: PageLabel.resources,\n        description: 'resourcesDesc',\n        builder: (_) =>\n            const ResourcesView(key: GlobalObjectKey(PageLabel.resources)),\n        modes: [NavigationItemMode.more],\n      ),\n      NavigationItem(\n        icon: Icon(Icons.functions),\n        label: PageLabel.script,\n        description: 'scriptDesc',\n        builder: (_) =>\n            const ScriptsView(key: GlobalObjectKey(PageLabel.script)),\n        modes: [NavigationItemMode.more],\n      ),\n      NavigationItem(\n        icon: const Icon(Icons.adb),\n        label: PageLabel.logs,\n        builder: (_) => const LogsView(key: GlobalObjectKey(PageLabel.logs)),\n        description: 'logsDesc',\n        modes: [NavigationItemMode.desktop, NavigationItemMode.more],\n      ),\n      NavigationItem(\n        icon: Icon(Icons.construction),\n        label: PageLabel.tools,\n        builder: (_) => const ToolsView(key: GlobalObjectKey(PageLabel.tools)),\n        modes: [NavigationItemMode.desktop, NavigationItemMode.mobile],\n      ),\n    ];\n  }\n\n  Navigation._internal();\n\n  factory Navigation() {\n    _instance ??= Navigation._internal();\n    return _instance!;\n  }\n}\n\nfinal navigation = Navigation();\n"
  },
  {
    "path": "lib/common/navigator.dart",
    "content": "import 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/app.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:flutter/material.dart';\n\nclass BaseNavigator {\n  static Future<T?> push<T>(BuildContext context, Widget child) async {\n    if (globalState.appState.viewMode != ViewMode.mobile) {\n      return await Navigator.of(\n        context,\n      ).push<T>(CommonDesktopRoute(builder: (context) => child));\n    }\n    return await Navigator.of(\n      context,\n    ).push<T>(MaterialPageRoute(builder: (context) => child));\n  }\n}\n\nclass CommonDesktopRoute<T> extends PageRoute<T> {\n  final Widget Function(BuildContext context) builder;\n\n  CommonDesktopRoute({required this.builder});\n\n  @override\n  Color? get barrierColor => null;\n\n  @override\n  String? get barrierLabel => null;\n\n  @override\n  Widget buildPage(\n    BuildContext context,\n    Animation<double> animation,\n    Animation<double> secondaryAnimation,\n  ) {\n    final Widget result = builder(context);\n    return Semantics(\n      scopesRoute: true,\n      explicitChildNodes: true,\n      child: FadeTransition(opacity: animation, child: result),\n    );\n  }\n\n  @override\n  bool get maintainState => true;\n\n  @override\n  Duration get transitionDuration => Duration(milliseconds: 200);\n\n  @override\n  Duration get reverseTransitionDuration => Duration(milliseconds: 200);\n}\n"
  },
  {
    "path": "lib/common/network.dart",
    "content": "import 'dart:io';\n\nextension NetworkInterfaceExt on NetworkInterface {\n  bool get isWifi {\n    final nameLowCase = name.toLowerCase();\n    if (nameLowCase.contains('wlan') ||\n        nameLowCase.contains('wi-fi') ||\n        nameLowCase == 'en0' ||\n        nameLowCase == 'eth0') {\n      return true;\n    }\n\n    return false;\n  }\n\n  bool get includesIPv4 {\n    return addresses.any((addr) => addr.isIPv4);\n  }\n}\n\nextension InternetAddressExt on InternetAddress {\n  bool get isIPv4 {\n    return type == InternetAddressType.IPv4;\n  }\n}\n"
  },
  {
    "path": "lib/common/network_matcher.dart",
    "content": "class NetworkMatcher {\n  static int? parseIPv4(String ip) {\n    final parts = ip.trim().split('.');\n    if (parts.length != 4) return null;\n\n    int result = 0;\n    for (final part in parts) {\n      final value = int.tryParse(part);\n      if (value == null || value < 0 || value > 255) return null;\n      result = (result << 8) | value;\n    }\n    return result;\n  }\n\n  static String formatIPv4(int ip) {\n    return '${(ip >> 24) & 0xFF}.${(ip >> 16) & 0xFF}.${(ip >> 8) & 0xFF}.${ip & 0xFF}';\n  }\n\n  static (int, int)? parseCIDR(String cidr) {\n    final parts = cidr.trim().split('/');\n\n    if (parts.length == 1) {\n      final ip = parseIPv4(parts[0]);\n      return ip != null ? (ip, 32) : null;\n    }\n\n    if (parts.length != 2) return null;\n\n    final ip = parseIPv4(parts[0]);\n    if (ip == null) return null;\n\n    final prefix = int.tryParse(parts[1]);\n    if (prefix == null || prefix < 0 || prefix > 32) return null;\n\n    final mask = prefix == 0 ? 0 : (0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF;\n    return (ip & mask, prefix);\n  }\n\n  static bool isIPInCIDR(String ip, String cidr) {\n    final ipInt = parseIPv4(ip);\n    if (ipInt == null) return false;\n\n    final parsed = parseCIDR(cidr);\n    if (parsed == null) return false;\n\n    final (network, prefix) = parsed;\n    if (prefix == 0) return true;\n\n    final mask = (0xFFFFFFFF << (32 - prefix)) & 0xFFFFFFFF;\n    return (ipInt & mask) == network;\n  }\n\n  static bool matchRule(String ip, String rule) {\n    final trimmed = rule.trim();\n    if (trimmed.isEmpty) return false;\n\n    if (trimmed.contains('/')) {\n      return isIPInCIDR(ip, trimmed);\n    }\n\n    final ipInt = parseIPv4(ip);\n    final ruleInt = parseIPv4(trimmed);\n    return ipInt != null && ruleInt != null && ipInt == ruleInt;\n  }\n\n  static bool matchAny(String? ip, String rules) {\n    if (ip == null || ip.isEmpty || rules.isEmpty) return false;\n\n    return rules.split(',').any((rule) => matchRule(ip, rule));\n  }\n\n  static bool isValidRule(String rule) {\n    final trimmed = rule.trim();\n    if (trimmed.isEmpty) return false;\n\n    return trimmed.contains('/') ? parseCIDR(trimmed) != null : parseIPv4(trimmed) != null;\n  }\n\n  static bool isValidRules(String rules) {\n    if (rules.isEmpty) return true;\n\n    final ruleList = rules.split(',');\n    if (ruleList.length > 2) return false;\n\n    return ruleList.every((rule) => rule.trim().isEmpty || isValidRule(rule));\n  }\n\n  static String? getValidationError(\n    String rules, {\n    String invalidFormatMsg = 'Invalid IP or CIDR format',\n    String tooManyRulesMsg = 'Maximum 2 rules allowed',\n  }) {\n    if (rules.isEmpty) return null;\n\n    final ruleList = rules.split(',');\n    if (ruleList.length > 2) return tooManyRulesMsg;\n\n    for (final rule in ruleList) {\n      if (rule.trim().isNotEmpty && !isValidRule(rule)) {\n        return invalidFormatMsg;\n      }\n    }\n    return null;\n  }\n}\n"
  },
  {
    "path": "lib/common/num.dart",
    "content": "import 'package:bett_box/state.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:flutter/material.dart';\n\nextension NumExt on num {\n  String fixed({int decimals = 2}) {\n    return toStringAsFixed(decimals);\n  }\n\n  double get ap {\n    return this * (1 + (globalState.theme.textScaleFactor - 1) * 0.5);\n  }\n}\n\nextension DoubleExt on double {\n  bool moreOrEqual(double value) {\n    return this > value || (value - this).abs() < precisionErrorTolerance + 1;\n  }\n}\n\nextension OffsetExt on Offset {\n  double getCrossAxisOffset(Axis direction) {\n    return direction == Axis.vertical ? dx : dy;\n  }\n\n  double getMainAxisOffset(Axis direction) {\n    return direction == Axis.vertical ? dy : dx;\n  }\n\n  bool less(Offset offset) {\n    if (dy < offset.dy) {\n      return true;\n    }\n    if (dy == offset.dy && dx < offset.dx) {\n      return true;\n    }\n    return false;\n  }\n}\n\nextension RectExt on Rect {\n  bool doRectIntersect(Rect rect) {\n    return left < rect.right &&\n        right > rect.left &&\n        top < rect.bottom &&\n        bottom > rect.top;\n  }\n}\n"
  },
  {
    "path": "lib/common/package.dart",
    "content": "import 'package:package_info_plus/package_info_plus.dart';\n\nextension PackageInfoExtension on PackageInfo {\n  String get ua =>\n      ['Clash.Meta/ClashMetaForAndroid/5.0'].join(' ');\n}\n"
  },
  {
    "path": "lib/common/path.dart",
    "content": "import 'dart:async';\nimport 'dart:io';\n\nimport 'package:bett_box/common/common.dart';\nimport 'package:path/path.dart';\nimport 'package:path_provider/path_provider.dart';\n\nclass AppPath {\n  static AppPath? _instance;\n  Completer<Directory> dataDir = Completer();\n  Completer<Directory> downloadDir = Completer();\n  Completer<Directory> tempDir = Completer();\n  late String appDirPath;\n\n  AppPath._internal() {\n    appDirPath = join(dirname(Platform.resolvedExecutable));\n    getApplicationSupportDirectory().then((value) {\n      dataDir.complete(value);\n    });\n    getTemporaryDirectory().then((value) {\n      tempDir.complete(value);\n    });\n    getDownloadsDirectory().then((value) {\n      downloadDir.complete(value);\n    });\n  }\n\n  factory AppPath() {\n    _instance ??= AppPath._internal();\n    return _instance!;\n  }\n\n  String get executableExtension {\n    return system.isWindows ? '.exe' : '';\n  }\n\n  String get executableDirPath {\n    final currentExecutablePath = Platform.resolvedExecutable;\n    return dirname(currentExecutablePath);\n  }\n\n  String get corePath {\n    return join(executableDirPath, 'BettboxCore$executableExtension');\n  }\n\n  String get helperPath {\n    return join(executableDirPath, '$appHelperService$executableExtension');\n  }\n\n  Future<String> get downloadDirPath async {\n    final directory = await downloadDir.future;\n    return directory.path;\n  }\n\n  Future<String> get homeDirPath async {\n    final directory = await dataDir.future;\n    return directory.path;\n  }\n\n  Future<String> get lockFilePath async {\n    final directory = await dataDir.future;\n    return join(directory.path, 'Bettbox.lock');\n  }\n\n  Future<String> get sharedPreferencesPath async {\n    final directory = await dataDir.future;\n    return join(directory.path, 'shared_preferences.json');\n  }\n\n  Future<String> get helperAuthKeyPath async {\n    final directory = await dataDir.future;\n    return join(directory.path, 'helper_auth.key');\n  }\n\n  Future<String> get profilesPath async {\n    final directory = await dataDir.future;\n    return join(directory.path, profilesDirectoryName);\n  }\n\n  Future<String> getProfilePath(String id) async {\n    final directory = await profilesPath;\n    return join(directory, '$id.yaml');\n  }\n\n  Future<String> getProvidersDirPath(String id) async {\n    final directory = await profilesPath;\n    return join(directory, 'providers', id);\n  }\n\n  Future<String> getProvidersFilePath(\n    String id,\n    String type,\n    String url,\n  ) async {\n    final directory = await profilesPath;\n    return join(directory, 'providers', id, type, url.toMd5());\n  }\n\n  Future<String> get tempPath async {\n    final directory = await tempDir.future;\n    return directory.path;\n  }\n\n  Future<String> get uiPath async {\n    final directory = await dataDir.future;\n    return join(directory.path, 'ui');\n  }\n}\n\nfinal appPath = AppPath();\n"
  },
  {
    "path": "lib/common/picker.dart",
    "content": "import 'dart:io';\nimport 'dart:typed_data';\n\nimport 'package:file_picker/file_picker.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:image_picker/image_picker.dart';\nimport 'package:mobile_scanner/mobile_scanner.dart';\n\nclass Picker {\n  Future<PlatformFile?> pickerFile({bool withData = true}) async {\n    final filePickerResult = await FilePicker.platform.pickFiles(\n      withData: withData,\n      allowMultiple: false,\n      initialDirectory: await appPath.downloadDirPath,\n    );\n    return filePickerResult?.files.first;\n  }\n\n  Future<String?> saveFile(String fileName, Uint8List bytes) async {\n    final path = await FilePicker.platform.saveFile(\n      fileName: fileName,\n      initialDirectory: await appPath.downloadDirPath,\n      bytes: system.isAndroid ? bytes : null,\n    );\n    if (!system.isAndroid && path != null) {\n      final file = await File(path).create(recursive: true);\n      await file.writeAsBytes(bytes);\n    }\n    return path;\n  }\n\n  Future<String?> pickerConfigQRCode() async {\n    final xFile = await ImagePicker().pickImage(source: ImageSource.gallery);\n    if (xFile == null) {\n      return null;\n    }\n    final controller = MobileScannerController();\n    final capture = await controller.analyzeImage(\n      xFile.path,\n      formats: [BarcodeFormat.qrCode],\n    );\n    final result = capture?.barcodes.first.rawValue;\n    if (result == null || !result.isUrl) {\n      throw appLocalizations.pleaseUploadValidQrcode;\n    }\n    return result;\n  }\n}\n\nfinal picker = Picker();\n"
  },
  {
    "path": "lib/common/preferences.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\n\nimport 'package:bett_box/models/models.dart';\nimport 'package:shared_preferences/shared_preferences.dart';\n\nimport 'constant.dart';\n\nclass Preferences {\n  static Preferences? _instance;\n  Completer<SharedPreferences?> sharedPreferencesCompleter = Completer();\n\n  Future<bool> get isInit async => await sharedPreferencesCompleter.future != null;\n\n  Preferences._internal() {\n    SharedPreferences.getInstance()\n        .then((value) => sharedPreferencesCompleter.complete(value))\n        .onError((_, _) => sharedPreferencesCompleter.complete(null));\n  }\n\n  factory Preferences() {\n    _instance ??= Preferences._internal();\n    return _instance!;\n  }\n\n  Future<ClashConfig?> getClashConfig() async {\n    final preferences = await sharedPreferencesCompleter.future;\n    final clashConfigString = preferences?.getString(clashConfigKey);\n    if (clashConfigString == null) return null;\n    final clashConfigMap = json.decode(clashConfigString);\n    return ClashConfig.fromJson(clashConfigMap);\n  }\n\n  Future<Config?> getConfig() async {\n    final preferences = await sharedPreferencesCompleter.future;\n    final configString = preferences?.getString(configKey);\n    if (configString == null) return null;\n    final configMap = json.decode(configString);\n    return Config.compatibleFromJson(configMap);\n  }\n\n  Future<bool> saveConfig(Config config) async {\n    final preferences = await sharedPreferencesCompleter.future;\n    return await preferences?.setString(configKey, json.encode(config)) ??\n        false;\n  }\n\n  Future<void> clearClashConfig() async {\n    final preferences = await sharedPreferencesCompleter.future;\n    preferences?.remove(clashConfigKey);\n  }\n\n  Future<void> clearPreferences() async {\n    final sharedPreferencesIns = await sharedPreferencesCompleter.future;\n    sharedPreferencesIns?.clear();\n  }\n}\n\nfinal preferences = Preferences();\n"
  },
  {
    "path": "lib/common/print.dart",
    "content": "import 'package:bett_box/models/models.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:flutter/cupertino.dart';\n\nclass CommonPrint {\n  static CommonPrint? _instance;\n\n  CommonPrint._internal();\n\n  factory CommonPrint() {\n    _instance ??= CommonPrint._internal();\n    return _instance!;\n  }\n\n  void log(String? text) {\n    final payload = '[APP] $text';\n    debugPrint(payload);\n    if (!globalState.isInit) {\n      return;\n    }\n    globalState.appController.addLog(Log.app(payload));\n  }\n}\n\nfinal commonPrint = CommonPrint();\n"
  },
  {
    "path": "lib/common/protocol.dart",
    "content": "import 'dart:io';\n\nimport 'package:win32_registry/win32_registry.dart';\n\nclass Protocol {\n  static Protocol? _instance;\n\n  Protocol._internal();\n\n  factory Protocol() {\n    _instance ??= Protocol._internal();\n    return _instance!;\n  }\n\n  void register(String scheme) {\n    String protocolRegKey = 'Software\\\\Classes\\\\$scheme';\n    RegistryValue protocolRegValue = RegistryValue.string('URL Protocol', '');\n    String protocolCmdRegKey = 'shell\\\\open\\\\command';\n    RegistryValue protocolCmdRegValue = RegistryValue.string(\n      '',\n      '\"${Platform.resolvedExecutable}\" \"%1\"',\n    );\n    final regKey = Registry.currentUser.createKey(protocolRegKey);\n    regKey.createValue(protocolRegValue);\n    regKey.createKey(protocolCmdRegKey).createValue(protocolCmdRegValue);\n  }\n}\n\nfinal protocol = Protocol();\n"
  },
  {
    "path": "lib/common/proxy.dart",
    "content": "import 'package:bett_box/common/system.dart';\nimport 'package:proxy/proxy.dart';\n\nfinal proxy = system.isDesktop ? Proxy() : null;\n"
  },
  {
    "path": "lib/common/render.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:flutter/scheduler.dart';\n\nclass Render {\n  static Render? _instance;\n  bool _isPaused = false;\n  final _dispatcher = SchedulerBinding.instance.platformDispatcher;\n  FrameCallback? _beginFrame;\n  VoidCallback? _drawFrame;\n\n  Render._internal();\n\n  factory Render() {\n    _instance ??= Render._internal();\n    return _instance!;\n  }\n\n  void pause() {\n    _pause();\n  }\n\n  void resume() {\n    _resume();\n  }\n\n  void _pause() async {\n    if (!system.isWindows) return;\n    if (_isPaused) return;\n    _isPaused = true;\n    _beginFrame = _dispatcher.onBeginFrame;\n    _drawFrame = _dispatcher.onDrawFrame;\n    _dispatcher.onBeginFrame = null;\n    _dispatcher.onDrawFrame = null;\n  }\n\n  void _resume() {\n    if (!_isPaused) return;\n    _isPaused = false;\n    _dispatcher.onBeginFrame = _beginFrame;\n    _dispatcher.onDrawFrame = _drawFrame;\n    _dispatcher.scheduleFrame();\n  }\n}\n\nfinal Render? render = system.isDesktop ? Render() : null;\n"
  },
  {
    "path": "lib/common/request.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\nimport 'dart:io';\nimport 'dart:typed_data';\n\nimport 'package:dio/dio.dart';\nimport 'package:dio/io.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/common/helper_auth.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:flutter/cupertino.dart';\n\nclass Request {\n  late final Dio _dio;\n  late final Dio _clashDio;\n  String? userAgent;\n\n  Request() {\n    _dio = Dio(BaseOptions(headers: {'User-Agent': browserUa}));\n    _clashDio = Dio();\n    _clashDio.httpClientAdapter = IOHttpClientAdapter(\n      createHttpClient: () {\n        final client = HttpClient();\n        client.findProxy = (Uri uri) {\n          client.userAgent = globalState.ua;\n          return BettboxHttpOverrides.handleFindProxy(uri);\n        };\n        return client;\n      },\n    );\n  }\n\n  Future<Response> _getResponseForUrl(String url, ResponseType responseType) async {\n    final uri = Uri.parse(url);\n    final userInfo = uri.userInfo;\n\n    Options? options;\n    if (userInfo.isNotEmpty) {\n      final auth = base64Encode(utf8.encode(userInfo));\n      options = Options(\n        responseType: responseType,\n        headers: {'Authorization': 'Basic $auth'},\n      );\n      url = uri.replace(userInfo: '').toString();\n    }\n\n    final response = await _clashDio.get(\n      url,\n      options: options ?? Options(responseType: responseType),\n    );\n    return response;\n  }\n\n  Future<Response> getFileResponseForUrl(String url) async {\n    return _getResponseForUrl(url, ResponseType.bytes);\n  }\n\n  Future<Response> getTextResponseForUrl(String url) async {\n    return _getResponseForUrl(url, ResponseType.plain);\n  }\n\n  Future<MemoryImage?> getImage(String url) async {\n    if (url.isEmpty) return null;\n    final response = await _dio.get<Uint8List>(\n      url,\n      options: Options(responseType: ResponseType.bytes),\n    );\n    final data = response.data;\n    if (data == null) return null;\n    return MemoryImage(data);\n  }\n\n  Future<Map<String, dynamic>?> checkForUpdate() async {\n    try {\n      final response = await _dio.get(\n        'https://api.github.com/repos/$repository/releases/latest',\n        options: Options(responseType: ResponseType.json),\n      );\n      if (response.statusCode != 200) return null;\n      final data = response.data as Map<String, dynamic>;\n      final remoteVersion = data['tag_name'];\n      final version = globalState.packageInfo.version;\n      final hasUpdate =\n          utils.compareVersions(remoteVersion.replaceAll('v', ''), version) > 0;\n      if (!hasUpdate) return null;\n      return data;\n    } on DioException catch (e) {\n      commonPrint.log('Check update failed: ${e.message}');\n      return null;\n    } catch (e) {\n      commonPrint.log('Check update error: $e');\n      return null;\n    }\n  }\n\n  final List<String> _ipInfoSources = [\n    'https://api.cloudflare.com/cdn-cgi/trace',\n    'https://cp.cloudflare.com/cdn-cgi/trace',\n  ];\n\n  final List<String> _domesticIpSources = [\n    'https://www.teamviewer.cn/cdn-cgi/trace',\n    'https://www.cloudflare-cn.com/cdn-cgi/trace',\n  ];\n\n  Future<Result<IpInfo?>> _checkIpFromSources(\n    List<String> sources,\n    CancelToken? cancelToken,\n    Duration? timeout,\n  ) async {\n    final effectiveTimeout = timeout ?? const Duration(seconds: 5);\n\n    final dio = Dio(\n      BaseOptions(\n        receiveTimeout: effectiveTimeout,\n        connectTimeout: effectiveTimeout,\n      ),\n    );\n\n    final Completer<Result<IpInfo?>> resultCompleter = Completer();\n    int failureCount = 0;\n\n    void handleFailure() {\n      if (resultCompleter.isCompleted) return;\n      failureCount++;\n      if (failureCount == sources.length) {\n        resultCompleter.complete(Result.success(null));\n      }\n    }\n\n    for (final url in sources) {\n      dio.get<String>(\n        url,\n        cancelToken: cancelToken,\n        options: Options(responseType: ResponseType.plain),\n      ).then((res) {\n        if (resultCompleter.isCompleted) return;\n        if (res.statusCode == HttpStatus.ok && res.data != null) {\n          try {\n            resultCompleter.complete(\n              Result.success(IpInfo.fromCloudflareTrace(res.data!)),\n            );\n          } catch (_) {\n            handleFailure();\n          }\n        } else {\n          handleFailure();\n        }\n      }).catchError((e) {\n        if (resultCompleter.isCompleted) return;\n        if (e is DioException && e.type == DioExceptionType.cancel) {\n          resultCompleter.complete(Result.error('cancelled'));\n          return;\n        }\n        handleFailure();\n      });\n    }\n\n    try {\n      return await resultCompleter.future.timeout(\n        effectiveTimeout,\n        onTimeout: () => Result.success(null),\n      );\n    } finally {\n      dio.close(force: true);\n    }\n  }\n\n  Future<Result<IpInfo?>> checkIp({\n    CancelToken? cancelToken,\n    Duration? timeout,\n  }) async {\n    return _checkIpFromSources(_ipInfoSources, cancelToken, timeout);\n  }\n\n  Future<Result<IpInfo?>> checkIpDomestic({\n    CancelToken? cancelToken,\n    Duration? timeout,\n  }) async {\n    return _checkIpFromSources(_domesticIpSources, cancelToken, timeout);\n  }\n\n  Future<bool> quickPingHelper() async {\n    try {\n      final response = await _dio\n          .get(\n            'http://$localhost:$helperPort/ping',\n            options: Options(responseType: ResponseType.plain),\n          )\n          .timeout(const Duration(milliseconds: 500));\n      if (response.statusCode != HttpStatus.ok) {\n        return false;\n      }\n      return (response.data as String) == globalState.coreSHA256;\n    } catch (_) {\n      return false;\n    }\n  }\n\n  Future<bool> startCoreByHelper(String arg) async {\n    final helperAlive = await quickPingHelper();\n    if (!helperAlive) {\n      commonPrint.log('Helper service is not reachable, skipping startCoreByHelper');\n      return false;\n    }\n\n    final homeDirPath = await appPath.homeDirPath;\n    final body = json.encode({\n      'path': appPath.corePath,\n      'arg': arg,\n      'home_dir': homeDirPath,\n    });\n    final authHeaders = HelperAuthManager.generateAuthHeaders(body);\n\n    const maxAttempts = 4;\n    const interval = Duration(milliseconds: 500);\n    const requestTimeout = Duration(seconds: 5);\n\n    for (var attempt = 1; attempt <= maxAttempts; attempt++) {\n      try {\n        final response = await _dio\n            .post(\n              'http://$localhost:$helperPort/start',\n              data: body,\n              options: Options(\n                responseType: ResponseType.plain,\n                headers: authHeaders,\n              ),\n            )\n            .timeout(requestTimeout);\n        if (response.statusCode == HttpStatus.ok) {\n          final data = response.data as String;\n          if (data.isEmpty) return true;\n        }\n      } catch (e) {\n        if (attempt == maxAttempts) {\n          commonPrint.log('Failed to start core by helper after $maxAttempts attempts: $e');\n          return false;\n        }\n      }\n      await Future.delayed(interval);\n    }\n    return false;\n  }\n\n  Future<bool> stopCoreByHelper() async {\n    try {\n      final authHeaders = HelperAuthManager.generateAuthHeaders('');\n\n      final response = await _dio\n          .post(\n            'http://$localhost:$helperPort/stop',\n            options: Options(\n              responseType: ResponseType.plain,\n              headers: authHeaders,\n            ),\n          )\n          .timeout(const Duration(milliseconds: 2000));\n      if (response.statusCode != HttpStatus.ok) {\n        return false;\n      }\n      return true;\n    } catch (e) {\n      commonPrint.log('Failed to stop core by helper: $e');\n      return false;\n    }\n  }\n}\n\nfinal request = Request();\n"
  },
  {
    "path": "lib/common/scroll.dart",
    "content": "import 'dart:math';\nimport 'dart:ui';\n\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/widgets/scroll.dart';\nimport 'package:flutter/material.dart';\n\nclass BaseScrollBehavior extends MaterialScrollBehavior {\n  @override\n  Set<PointerDeviceKind> get dragDevices => {\n    PointerDeviceKind.touch,\n    PointerDeviceKind.stylus,\n    PointerDeviceKind.invertedStylus,\n    PointerDeviceKind.trackpad,\n    if (system.isDesktop) PointerDeviceKind.mouse,\n    PointerDeviceKind.unknown,\n  };\n}\n\nclass HiddenBarScrollBehavior extends BaseScrollBehavior {\n  @override\n  Widget buildScrollbar(\n    BuildContext context,\n    Widget child,\n    ScrollableDetails details,\n  ) {\n    return child;\n  }\n}\n\nclass ShowBarScrollBehavior extends BaseScrollBehavior {\n  @override\n  Widget buildScrollbar(\n    BuildContext context,\n    Widget child,\n    ScrollableDetails details,\n  ) {\n    return CommonScrollBar(controller: details.controller, child: child);\n  }\n}\n\nclass NextClampingScrollPhysics extends ClampingScrollPhysics {\n  const NextClampingScrollPhysics({super.parent});\n\n  @override\n  NextClampingScrollPhysics applyTo(ScrollPhysics? ancestor) {\n    return NextClampingScrollPhysics(parent: buildParent(ancestor));\n  }\n\n  @override\n  Simulation? createBallisticSimulation(\n    ScrollMetrics position,\n    double velocity,\n  ) {\n    final Tolerance tolerance = toleranceFor(position);\n    if (position.outOfRange) {\n      double? end;\n      if (position.pixels > position.maxScrollExtent) {\n        end = position.maxScrollExtent;\n      }\n      if (position.pixels < position.minScrollExtent) {\n        end = position.minScrollExtent;\n      }\n      assert(end != null);\n      return ScrollSpringSimulation(\n        spring,\n        end!,\n        end,\n        min(0.0, velocity),\n        tolerance: tolerance,\n      );\n    }\n    if (velocity.abs() < tolerance.velocity) {\n      return null;\n    }\n    if (velocity > 0.0 && position.pixels >= position.maxScrollExtent) {\n      return null;\n    }\n    if (velocity < 0.0 && position.pixels <= position.minScrollExtent) {\n      return null;\n    }\n    return ClampingScrollSimulation(\n      position: position.pixels,\n      velocity: velocity,\n      tolerance: tolerance,\n    );\n  }\n}\n\nclass ReverseScrollController extends ScrollController {\n  ReverseScrollController({\n    super.initialScrollOffset,\n    super.keepScrollOffset,\n    super.debugLabel,\n  });\n\n  @override\n  ScrollPosition createScrollPosition(\n    ScrollPhysics physics,\n    ScrollContext context,\n    ScrollPosition? oldPosition,\n  ) {\n    return ReverseScrollPosition(\n      physics: physics,\n      context: context,\n      initialPixels: initialScrollOffset,\n      keepScrollOffset: keepScrollOffset,\n      oldPosition: oldPosition,\n      debugLabel: debugLabel,\n    );\n  }\n}\n\nclass ReverseScrollPosition extends ScrollPositionWithSingleContext {\n  ReverseScrollPosition({\n    required super.physics,\n    required super.context,\n    super.initialPixels = 0.0,\n    super.keepScrollOffset,\n    super.oldPosition,\n    super.debugLabel,\n  });\n\n  bool _isInit = false;\n\n  @override\n  bool applyContentDimensions(double minScrollExtent, double maxScrollExtent) {\n    if (!_isInit) {\n      correctPixels(maxScrollExtent);\n      _isInit = true;\n    }\n    return super.applyContentDimensions(minScrollExtent, maxScrollExtent);\n  }\n}\n"
  },
  {
    "path": "lib/common/state.dart",
    "content": "\n"
  },
  {
    "path": "lib/common/string.dart",
    "content": "import 'dart:convert';\nimport 'dart:typed_data';\n\nimport 'package:crypto/crypto.dart';\n\nimport 'print.dart';\n\nextension StringExtension on String {\n  bool get isUrl {\n    return RegExp(r'^(http|https|ftp)://').hasMatch(this);\n  }\n\n  dynamic get splitByMultipleSeparators {\n    final parts = split(\n      RegExp(r'[, ;]+'),\n    ).where((part) => part.isNotEmpty).toList();\n\n    return parts.length > 1 ? parts : this;\n  }\n\n  int compareToLower(String other) {\n    return toLowerCase().compareTo(other.toLowerCase());\n  }\n\n  List<int> get encodeUtf16LeWithBom {\n    final byteData = ByteData(length * 2);\n    final bom = [0xFF, 0xFE];\n    for (int i = 0; i < length; i++) {\n      int charCode = codeUnitAt(i);\n      byteData.setUint16(i * 2, charCode, Endian.little);\n    }\n    return bom + byteData.buffer.asUint8List();\n  }\n\n  Uint8List? get getBase64 {\n    final regExp = RegExp(r'base64,(.*)');\n    final match = regExp.firstMatch(this);\n    final realValue = match?.group(1) ?? '';\n    if (realValue.isEmpty) {\n      return null;\n    }\n    try {\n      return base64.decode(realValue);\n    } catch (e) {\n      return null;\n    }\n  }\n\n  bool get isSvg {\n    return endsWith('.svg');\n  }\n\n  bool get isRegex {\n    try {\n      RegExp(this);\n      return true;\n    } catch (e) {\n      commonPrint.log(e.toString());\n      return false;\n    }\n  }\n\n  String toMd5() {\n    final bytes = utf8.encode(this);\n    return md5.convert(bytes).toString();\n  }\n}\n\nextension StringExtensionSafe on String? {\n  String getSafeValue(String defaultValue) {\n    return this?.isEmpty != false ? defaultValue : this!;\n  }\n}\n"
  },
  {
    "path": "lib/common/system.dart",
    "content": "import 'dart:ffi';\nimport 'dart:io';\n\nimport 'package:device_info_plus/device_info_plus.dart';\nimport 'package:ffi/ffi.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/common/helper_auth.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/plugins/app.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/input.dart';\nimport 'package:flutter/services.dart';\nimport 'package:path/path.dart';\n\nclass System {\n  static System? _instance;\n\n  System._internal();\n\n  factory System() {\n    _instance ??= System._internal();\n    return _instance!;\n  }\n\n  bool get isDesktop => isWindows || isMacOS || isLinux;\n\n  bool get isWindows => Platform.isWindows;\n\n  bool get isMacOS => Platform.isMacOS;\n\n  bool get isAndroid => Platform.isAndroid;\n\n  bool get isLinux => Platform.isLinux;\n\n  Future<int> get version async {\n    final deviceInfo = await DeviceInfoPlugin().deviceInfo;\n    return switch (Platform.operatingSystem) {\n      'macos' => (deviceInfo as MacOsDeviceInfo).majorVersion,\n      'android' => (deviceInfo as AndroidDeviceInfo).version.sdkInt,\n      'windows' => (deviceInfo as WindowsDeviceInfo).majorVersion,\n      String() => 0,\n    };\n  }\n\n  Future<bool> checkIsAdmin() async {\n    final corePath = appPath.corePath.replaceAll(' ', '\\\\\\\\ ');\n    if (system.isWindows) {\n      final result = await windows?.checkService();\n      return result == WindowsHelperServiceStatus.running;\n    }\n\n    if (system.isMacOS) {\n      final result = await Process.run('stat', ['-f', '%Su:%Sg %Sp', corePath]);\n      final output = result.stdout.trim();\n      return output.startsWith('root:admin') && output.contains('rws');\n    }\n\n    if (Platform.isLinux) {\n      final result = await Process.run('stat', ['-c', '%U:%G %A', corePath]);\n      final output = result.stdout.trim();\n      return output.startsWith('root:') && output.contains('rws');\n    }\n\n    return true;\n  }\n\n  Future<AuthorizeCode> authorizeCore() async {\n    if (system.isAndroid) return AuthorizeCode.error;\n\n    final corePath = appPath.corePath.replaceAll(' ', '\\\\\\\\ ');\n    if (await checkIsAdmin()) return AuthorizeCode.none;\n\n    if (system.isWindows) {\n      final result = await windows?.registerService();\n      return result == true ? AuthorizeCode.success : AuthorizeCode.error;\n    }\n\n    if (system.isMacOS) {\n      final shell = 'chown root:admin $corePath; chmod +sx $corePath';\n      final result = await Process.run('osascript', [\n        '-e',\n        'do shell script \"$shell\" with administrator privileges',\n      ]);\n      return result.exitCode == 0 ? AuthorizeCode.success : AuthorizeCode.error;\n    }\n\n    if (Platform.isLinux) {\n      final shell = Platform.environment['SHELL'] ?? 'bash';\n      final password = await globalState.showCommonDialog<String>(\n        child: InputDialog(\n          obscureText: true,\n          title: appLocalizations.pleaseInputAdminPassword,\n          value: '',\n        ),\n      );\n      final result = await Process.run(shell, [\n        '-c',\n        'echo \"$password\" | sudo -S chown root:root \"$corePath\" && echo \"$password\" | sudo -S chmod +sx \"$corePath\"',\n      ]);\n      return result.exitCode == 0 ? AuthorizeCode.success : AuthorizeCode.error;\n    }\n\n    return AuthorizeCode.error;\n  }\n\n  Future<void> back() async {\n    if (system.isAndroid) await app.moveTaskToBack();\n    await window?.hide();\n  }\n\n  Future<void> exit() async {\n    if (system.isAndroid) await SystemNavigator.pop();\n    await window?.close();\n  }\n}\n\nfinal system = System();\n\nclass Windows {\n  static Windows? _instance;\n  late DynamicLibrary _shell32;\n\n  Windows._internal() {\n    _shell32 = DynamicLibrary.open('shell32.dll');\n  }\n\n  factory Windows() {\n    _instance ??= Windows._internal();\n    return _instance!;\n  }\n\n  bool runas(String command, String arguments, {bool showWindow = false}) {\n    final commandPtr = command.toNativeUtf16();\n    final argumentsPtr = arguments.toNativeUtf16();\n    final operationPtr = 'runas'.toNativeUtf16();\n\n    final shellExecute = _shell32\n        .lookupFunction<\n          Int32 Function(\n            Pointer<Utf16> hwnd,\n            Pointer<Utf16> lpOperation,\n            Pointer<Utf16> lpFile,\n            Pointer<Utf16> lpParameters,\n            Pointer<Utf16> lpDirectory,\n            Int32 nShowCmd,\n          ),\n          int Function(\n            Pointer<Utf16> hwnd,\n            Pointer<Utf16> lpOperation,\n            Pointer<Utf16> lpFile,\n            Pointer<Utf16> lpParameters,\n            Pointer<Utf16> lpDirectory,\n            int nShowCmd,\n          )\n        >('ShellExecuteW');\n\n    // 0 = hide, 1 = show\n    final result = shellExecute(\n      nullptr,\n      operationPtr,\n      commandPtr,\n      argumentsPtr,\n      nullptr,\n      showWindow ? 1 : 0,\n    );\n\n    calloc.free(commandPtr);\n    calloc.free(argumentsPtr);\n    calloc.free(operationPtr);\n\n    commonPrint.log('windows runas: [command masked] resultCode:$result');\n\n    return result > 32;\n  }\n\n  Future<void> _killProcess(int port) async {\n    final result = await Process.run('netstat', ['-ano']);\n    final lines = result.stdout.toString().trim().split('\\n');\n    for (final line in lines) {\n      if (!line.contains(':$port') || !line.contains('LISTENING')) continue;\n      final parts = line.trim().split(RegExp(r'\\s+'));\n      final pid = int.tryParse(parts.last);\n      if (pid != null) {\n        await Process.run('taskkill', ['/PID', pid.toString(), '/F']);\n      }\n    }\n  }\n\n  Future<WindowsHelperServiceStatus> checkService() async {\n    final result = await Process.run('sc', ['query', appHelperService]);\n    if (result.exitCode != 0) return WindowsHelperServiceStatus.none;\n\n    final output = result.stdout.toString();\n    if (!output.contains('RUNNING')) return WindowsHelperServiceStatus.presence;\n\n    final isReachable = await request.quickPingHelper();\n    return isReachable\n        ? WindowsHelperServiceStatus.running\n        : WindowsHelperServiceStatus.presence;\n  }\n\n  Future<bool> registerService() async {\n    final createdNewKey = await HelperAuthManager.ensureAuthKey();\n    final authKey = HelperAuthManager.getAuthKey();\n\n    final quickCheck = await Process.run('sc', ['query', appHelperService]);\n    if (quickCheck.exitCode == 0 &&\n        quickCheck.stdout.toString().contains('RUNNING')) {\n      final isReachable = await request.quickPingHelper();\n      if (isReachable) {\n        if (createdNewKey && authKey != null) {\n          await _restartServiceWithAuthKey(authKey);\n        }\n        return true;\n      }\n    }\n\n    final status = await checkService();\n    if (status == WindowsHelperServiceStatus.running) {\n      if (createdNewKey && authKey != null) {\n        await _restartServiceWithAuthKey(authKey);\n      }\n      return true;\n    }\n\n    await _killProcess(helperPort);\n\n    final command = [\n      '/c',\n      if (status == WindowsHelperServiceStatus.presence) ...[\n        'sc',\n        'delete',\n        appHelperService,\n        '/force',\n        '&&',\n      ],\n      'sc',\n      'create',\n      appHelperService,\n      'binPath= \"${appPath.helperPath}\"',\n      'start= auto',\n      '&&',\n      if (authKey != null) ...[\n        'sc',\n        'config',\n        appHelperService,\n        'Environment= HELPER_AUTH_KEY=$authKey',\n        '&&',\n      ],\n      'sc',\n      'start',\n      appHelperService,\n    ].join(' ');\n\n    final res = runas('cmd.exe', command);\n\n    for (int i = 0; i < 10; i++) {\n      await Future.delayed(const Duration(milliseconds: 200));\n      if (await request.quickPingHelper()) return true;\n      if (i > 0 && i % 4 == 0) {\n        final check = await Process.run('sc', ['query', appHelperService]);\n        final out = check.stdout.toString();\n        if (out.contains('STOPPED') || out.contains('FAILED')) {\n          commonPrint.log('Helper service stopped/failed, skipping wait');\n          break;\n        }\n      }\n    }\n\n    return res;\n  }\n\n  Future<void> _restartServiceWithAuthKey(String authKey) async {\n    try {\n      await Process.run('sc', ['stop', appHelperService]);\n      await Future.delayed(Duration(milliseconds: 500));\n      await Process.run('sc', [\n        'config',\n        appHelperService,\n        'Environment= HELPER_AUTH_KEY=$authKey',\n      ]);\n      await Process.run('sc', ['start', appHelperService]);\n      await Future.delayed(Duration(milliseconds: 500));\n    } catch (e) {\n      commonPrint.log('Failed to restart service with auth key: $e');\n    }\n  }\n\n  Future<bool> registerTask(\n    String appName, {\n    bool requireNetwork = true,\n  }) async {\n    final executablePath = Platform.resolvedExecutable;\n    final workingDirectory = dirname(executablePath);\n\n    final taskXml = '''\n<?xml version=\"1.0\" encoding=\"UTF-16\"?>\n<Task version=\"1.3\" xmlns=\"http://schemas.microsoft.com/windows/2004/02/mit/task\">\n  <RegistrationInfo>\n    <Description>开机自动启动代理服务</Description>\n    <URI>\\\\$appName</URI>\n  </RegistrationInfo>\n  <Principals>\n    <Principal id=\"Author\">\n      <LogonType>InteractiveToken</LogonType>\n      <RunLevel>HighestAvailable</RunLevel>\n    </Principal>\n  </Principals>\n  <Triggers>\n    <LogonTrigger>\n      <Enabled>true</Enabled>\n    </LogonTrigger>\n  </Triggers>\n  <Settings>\n    <MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>\n    <DisallowStartIfOnBatteries>false</DisallowStartIfOnBatteries>\n    <StopIfGoingOnBatteries>false</StopIfGoingOnBatteries>\n    <AllowHardTerminate>false</AllowHardTerminate>\n    <StartWhenAvailable>true</StartWhenAvailable>\n    <RunOnlyIfNetworkAvailable>$requireNetwork</RunOnlyIfNetworkAvailable>\n    <IdleSettings>\n      <StopOnIdleEnd>false</StopOnIdleEnd>\n      <RestartOnIdle>false</RestartOnIdle>\n    </IdleSettings>\n    <AllowStartOnDemand>true</AllowStartOnDemand>\n    <Enabled>true</Enabled>\n    <Hidden>false</Hidden>\n    <RunOnlyIfIdle>false</RunOnlyIfIdle>\n    <WakeToRun>false</WakeToRun>\n    <ExecutionTimeLimit>PT0S</ExecutionTimeLimit>\n    <Priority>6</Priority>\n  </Settings>\n  <Actions Context=\"Author\">\n    <Exec>\n      <Command>\"$executablePath\"</Command>\n      <WorkingDirectory>$workingDirectory</WorkingDirectory>\n    </Exec>\n  </Actions>\n</Task>''';\n    final taskPath = join(await appPath.tempPath, 'task.xml');\n    await File(taskPath).create(recursive: true);\n    await File(\n      taskPath,\n    ).writeAsBytes(taskXml.encodeUtf16LeWithBom, flush: true);\n    final commandLine = [\n      '/Create',\n      '/TN',\n      appName,\n      '/XML',\n      '%s',\n      '/F',\n    ].join(' ');\n    return runas('schtasks', commandLine.replaceFirst('%s', taskPath));\n  }\n\n  Future<bool> unregisterTask(String appName) async {\n    final commandLine = ['/Delete', '/TN', appName, '/F'].join(' ');\n    return runas('schtasks', commandLine);\n  }\n}\n\nfinal windows = system.isWindows ? Windows() : null;\n\nclass MacOS {\n  static MacOS? _instance;\n\n  List<String>? originDns;\n\n  MacOS._internal();\n\n  factory MacOS() {\n    _instance ??= MacOS._internal();\n    return _instance!;\n  }\n\n  Future<String?> get defaultServiceName async {\n    final result = await Process.run('route', ['-n', 'get', 'default']);\n    final output = result.stdout.toString();\n    final deviceLine = output\n        .split('\\n')\n        .firstWhere((s) => s.contains('interface:'), orElse: () => '');\n    final parts = deviceLine.trim().split(' ');\n    if (parts.length != 2) return null;\n\n    final device = parts[1];\n    final serviceResult = await Process.run('networksetup', [\n      '-listnetworkserviceorder',\n    ]);\n    final serviceOutput = serviceResult.stdout.toString();\n    final currentService = serviceOutput\n        .split('\\n\\n')\n        .firstWhere((s) => s.contains('Device: $device'), orElse: () => '');\n    if (currentService.isEmpty) return null;\n\n    final serviceNameLine = currentService\n        .split('\\n')\n        .firstWhere(\n          (line) => RegExp(r'^\\(\\d+\\).*').hasMatch(line),\n          orElse: () => '',\n        );\n    final nameParts = serviceNameLine.trim().split(' ');\n    if (nameParts.length < 2) return null;\n    return nameParts[1];\n  }\n\n  Future<List<String>?> get systemDns async {\n    final deviceServiceName = await defaultServiceName;\n    if (deviceServiceName == null) return null;\n\n    final result = await Process.run('networksetup', [\n      '-getdnsservers',\n      deviceServiceName,\n    ]);\n    final output = result.stdout.toString().trim();\n    originDns = output.startsWith(\"There aren't any DNS Servers set on\")\n        ? []\n        : output.split('\\n');\n    return originDns;\n  }\n\n  Future<void> updateDns(bool restore) async {\n    final serviceName = await defaultServiceName;\n    if (serviceName == null) return;\n\n    List<String>? nextDns;\n    if (restore) {\n      nextDns = originDns;\n    } else {\n      final currentDns = await systemDns;\n      if (currentDns == null) return;\n      const needAddDns = '223.5.5.5';\n      if (currentDns.contains(needAddDns)) return;\n      nextDns = List.from(currentDns)..add(needAddDns);\n    }\n    if (nextDns == null) return;\n\n    await Process.run('networksetup', [\n      '-setdnsservers',\n      serviceName,\n      if (nextDns.isNotEmpty) ...nextDns,\n      if (nextDns.isEmpty) 'Empty',\n    ]);\n  }\n}\n\nfinal macOS = system.isMacOS ? MacOS() : null;\n"
  },
  {
    "path": "lib/common/task.dart",
    "content": "import 'dart:convert';\nimport 'package:flutter/foundation.dart';\n\n/// Encode data to YAML string in a separate isolate to avoid blocking UI\n/// Note: This uses JSON encoding which is a valid subset of YAML\nFuture<String> encodeYamlTask<T>(T data) async {\n  return await compute<T, String>(_encodeYaml, data);\n}\n\n/// Internal function to encode YAML (runs in isolate)\n/// Uses JSON encoding as it's a valid YAML subset and more readable for config preview\nFuture<String> _encodeYaml<T>(T content) async {\n  // Use pretty-printed JSON which is valid YAML and more readable\n  const encoder = JsonEncoder.withIndent('  ');\n  return encoder.convert(content);\n}\n"
  },
  {
    "path": "lib/common/text.dart",
    "content": "import 'package:bett_box/enum/enum.dart';\nimport 'package:flutter/material.dart';\nimport 'color.dart';\n\nextension TextStyleExtension on TextStyle {\n  TextStyle get toLight => copyWith(color: color?.opacity80);\n\n  TextStyle get toLighter => copyWith(color: color?.opacity60);\n\n  TextStyle get toSoftBold => copyWith(fontWeight: FontWeight.w500);\n\n  TextStyle get toBold => copyWith(fontWeight: FontWeight.bold);\n\n  TextStyle get toJetBrainsMono =>\n      copyWith(fontFamily: FontFamily.jetBrainsMono.value);\n\n  TextStyle adjustSize(int size) => copyWith(fontSize: fontSize! + size);\n}\n"
  },
  {
    "path": "lib/common/theme.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:flutter/material.dart';\n\nclass CommonTheme {\n  final BuildContext context;\n  final Map<String, Color> _colorMap;\n  final double textScaleFactor;\n\n  CommonTheme.of(this.context, this.textScaleFactor) : _colorMap = {};\n\n  Color get darkenSecondaryContainer {\n    return _colorMap.updateCacheValue(\n      'darkenSecondaryContainer',\n      () => context.colorScheme.secondaryContainer.blendDarken(\n        context,\n        factor: 0.1,\n      ),\n    );\n  }\n\n  Color get darkenSecondaryContainerLighter {\n    return _colorMap.updateCacheValue(\n      'darkenSecondaryContainerLighter',\n      () => context.colorScheme.secondaryContainer\n          .blendDarken(context, factor: 0.1)\n          .opacity60,\n    );\n  }\n\n  Color get darken2SecondaryContainer {\n    return _colorMap.updateCacheValue(\n      'darken2SecondaryContainer',\n      () => context.colorScheme.secondaryContainer.blendDarken(\n        context,\n        factor: 0.2,\n      ),\n    );\n  }\n\n  Color get darken3PrimaryContainer {\n    return _colorMap.updateCacheValue(\n      'darken3PrimaryContainer',\n      () => context.colorScheme.primaryContainer.blendDarken(\n        context,\n        factor: 0.3,\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/common/tray.dart",
    "content": "import 'dart:async';\nimport 'dart:io';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:intl/intl.dart';\nimport 'package:tray_manager/tray_manager.dart';\nimport 'package:wakelock_plus/wakelock_plus.dart';\n\nimport 'common.dart';\n\nclass Tray {\n  Timer? _debounceTimer;\n  TrayState? _pendingState;\n  bool _isUpdating = false;\n\n  static const _debounceDelay = Duration(milliseconds: 300);\n  Future _updateSystemTray({\n    required Brightness? brightness,\n    required bool isStart,\n    bool force = false,\n  }) async {\n    if (system.isAndroid) {\n      return;\n    }\n    if (force) {\n      await trayManager.destroy();\n    }\n    await trayManager.setIcon(\n      utils.getTrayIconPath(\n        brightness:\n            brightness ??\n            WidgetsBinding.instance.platformDispatcher.platformBrightness,\n        isStart: isStart,\n      ),\n      isTemplate: system.isMacOS,\n    );\n    if (!Platform.isLinux) {\n      await trayManager.setToolTip(appName);\n    }\n  }\n\n  Future<void> update({\n    required TrayState trayState,\n    bool focus = false,\n  }) async {\n    if (system.isAndroid) {\n      return;\n    }\n\n    _debounceTimer?.cancel();\n\n    if (_isUpdating) {\n      _pendingState = trayState;\n      return;\n    }\n\n    if (focus) {\n      await _doUpdate(trayState: trayState, focus: focus);\n    } else {\n      _debounceTimer = Timer(_debounceDelay, () async {\n        await _doUpdate(trayState: trayState, focus: focus);\n      });\n    }\n  }\n\n  Future<void> _doUpdate({\n    required TrayState trayState,\n    bool focus = false,\n  }) async {\n    if (_isUpdating) return;\n    _isUpdating = true;\n\n    try {\n      if (!Platform.isLinux) {\n        await _updateSystemTray(\n          brightness: trayState.brightness,\n          isStart: trayState.isStart,\n          force: focus,\n        );\n      }\n    List<MenuItem> menuItems = [];\n    final showMenuItem = MenuItem(\n      label: appLocalizations.show,\n      onClick: (_) {\n        window?.show();\n      },\n    );\n    menuItems.add(showMenuItem);\n    final startMenuItem = MenuItem.checkbox(\n      label: trayState.isStart ? appLocalizations.stop : appLocalizations.start,\n      onClick: (_) async {\n        globalState.appController.updateStart();\n      },\n      checked: false,\n    );\n    menuItems.add(startMenuItem);\n    menuItems.add(MenuItem.separator());\n    for (final mode in Mode.values) {\n      menuItems.add(\n        MenuItem.checkbox(\n          label: Intl.message(mode.name),\n          onClick: (_) {\n            globalState.appController.changeMode(mode);\n          },\n          checked: mode == trayState.mode,\n        ),\n      );\n    }\n    menuItems.add(MenuItem.separator());\n    for (final group in trayState.groups) {\n      List<MenuItem> subMenuItems = [];\n      for (final proxy in group.all) {\n        subMenuItems.add(\n          MenuItem.checkbox(\n            label: proxy.name,\n            checked: trayState.selectedMap[group.name] == proxy.name,\n            onClick: (_) {\n              final appController = globalState.appController;\n              appController.updateCurrentSelectedMap(group.name, proxy.name);\n              appController.changeProxy(\n                groupName: group.name,\n                proxyName: proxy.name,\n              );\n            },\n          ),\n        );\n      }\n      menuItems.add(\n        MenuItem.submenu(\n          label: group.name,\n          submenu: Menu(items: subMenuItems),\n        ),\n      );\n    }\n    if (trayState.groups.isNotEmpty) {\n      menuItems.add(MenuItem.separator());\n    }\n    if (trayState.isStart) {\n      menuItems.add(\n        MenuItem.checkbox(\n          label: appLocalizations.tun,\n          onClick: (_) {\n            globalState.appController.updateTun();\n          },\n          checked: trayState.tunEnable,\n        ),\n      );\n      menuItems.add(\n        MenuItem.checkbox(\n          label: appLocalizations.systemProxy,\n          onClick: (_) {\n            globalState.appController.updateSystemProxy();\n          },\n          checked: trayState.systemProxy,\n        ),\n      );\n      menuItems.add(MenuItem.separator());\n    }\n    final autoStartMenuItem = MenuItem.checkbox(\n      label: appLocalizations.autoLaunch,\n      onClick: (_) async {\n        globalState.appController.updateAutoLaunch();\n      },\n      checked: trayState.autoLaunch,\n    );\n    final copyEnvVarMenuItem = MenuItem(\n      label: appLocalizations.copyEnvVar,\n      onClick: (_) async {\n        await _copyEnv(trayState.port);\n      },\n    );\n    menuItems.add(autoStartMenuItem);\n    menuItems.add(copyEnvVarMenuItem);\n\n    if (!system.isAndroid) {\n      final wakelockMenuItem = MenuItem.checkbox(\n        label: appLocalizations.wakelock,\n        onClick: (_) async {\n          await _toggleWakelock();\n        },\n        checked: trayState.wakelockEnabled,\n      );\n      menuItems.add(wakelockMenuItem);\n    }\n\n    menuItems.add(MenuItem.separator());\n    final exitMenuItem = MenuItem(\n      label: appLocalizations.exit,\n      onClick: (_) async {\n        await globalState.appController.handleExit();\n      },\n    );\n    menuItems.add(exitMenuItem);\n    final menu = Menu(items: menuItems);\n    await trayManager.setContextMenu(menu);\n    if (Platform.isLinux) {\n      await _updateSystemTray(\n        brightness: trayState.brightness,\n        isStart: trayState.isStart,\n        force: focus,\n      );\n    }\n    } finally {\n      _isUpdating = false;\n\n      if (_pendingState != null) {\n        final pending = _pendingState;\n        _pendingState = null;\n        await _doUpdate(trayState: pending!, focus: false);\n      }\n    }\n  }\n\n  Future<void> _copyEnv(int port) async {\n    final url = 'http://127.0.0.1:$port';\n\n    final cmdline = system.isWindows\n        ? 'set \\$env:all_proxy=$url'\n        : 'export all_proxy=$url';\n\n    await Clipboard.setData(ClipboardData(text: cmdline));\n  }\n\n  Future<void> _toggleWakelock() async {\n    try {\n      final enabled = await WakelockPlus.enabled;\n      if (enabled) {\n        await WakelockPlus.disable();\n        globalState.appController.stopWakelockAutoRecovery();\n      } else {\n        await WakelockPlus.enable();\n        globalState.appController.startWakelockAutoRecovery();\n      }\n      globalState.updateWakelockState(!enabled);\n      await globalState.appController.updateTray();\n    } catch (e) {\n      commonPrint.log('WakeLock toggle error: $e');\n    }\n  }\n}\n\nfinal tray = Tray();\n"
  },
  {
    "path": "lib/common/ui_manager.dart",
    "content": "import 'dart:io';\nimport 'dart:isolate';\n\nimport 'package:archive/archive_io.dart';\nimport 'package:flutter/services.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:path/path.dart';\n\nclass UiManager {\n  static UiManager? _instance;\n\n  UiManager._internal();\n\n  factory UiManager() {\n    _instance ??= UiManager._internal();\n    return _instance!;\n  }\n\n  Future<void> initializeUI() async {\n    try {\n      final uiPath = await appPath.uiPath;\n      final uiDir = Directory(uiPath);\n      final versionFile = File(join(uiPath, '.ui_version'));\n      final currentVersion = globalState.packageInfo.version;\n\n      if (await uiDir.exists()) {\n        if (await versionFile.exists()) {\n          final existingVersion = await versionFile.readAsString();\n          if (existingVersion.trim() == currentVersion) {\n            commonPrint.log('UI already up to date (v$currentVersion)');\n            return;\n          }\n          commonPrint.log('UI version mismatch: $existingVersion -> $currentVersion');\n        }\n        await clearUI();\n      }\n\n      commonPrint.log('Extracting UI from assets...');\n\n      await uiDir.create(recursive: true);\n\n      final zipData = await rootBundle.load('assets/data/zash.zip');\n      \n      final bytes = Uint8List.fromList(zipData.buffer.asUint8List());\n\n      final tempPath = await appPath.tempPath;\n      final tempExtractPath = join(\n        tempPath,\n        'ui_extract_${DateTime.now().millisecondsSinceEpoch}',\n      );\n\n      await Isolate.run(() async {\n        final tempExtractDir = Directory(tempExtractPath);\n        await tempExtractDir.create(recursive: true);\n\n        try {\n          final archive = ZipDecoder().decodeBytes(bytes);\n\n          for (final file in archive) {\n            final filename = file.name;\n            final filePath = join(tempExtractPath, filename);\n\n            if (file.isFile) {\n              final outFile = File(filePath);\n              await outFile.create(recursive: true);\n              await outFile.writeAsBytes(file.content as List<int>);\n            } else {\n              await Directory(filePath).create(recursive: true);\n            }\n          }\n\n          final extractedFiles = await tempExtractDir.list().toList();\n          String sourceDir = tempExtractPath;\n\n          if (extractedFiles.length == 1 && extractedFiles.first is Directory) {\n            sourceDir = extractedFiles.first.path;\n          }\n\n          await _copyDirectory(Directory(sourceDir), Directory(uiPath));\n\n          final vFile = File(join(uiPath, '.ui_version'));\n          await vFile.writeAsString(currentVersion);\n        } finally {\n          if (await tempExtractDir.exists()) {\n            await tempExtractDir.delete(recursive: true);\n          }\n        }\n      });\n\n      commonPrint.log('UI extracted successfully to: $uiPath (v$currentVersion)');\n    } catch (e) {\n      commonPrint.log('Error extracting UI: $e');\n      rethrow;\n    }\n  }\n\n  static Future<void> _copyDirectory(Directory source, Directory destination) async {\n    await for (final entity in source.list(recursive: false)) {\n      if (entity is Directory) {\n        final newDirectory = Directory(\n          join(destination.path, basename(entity.path)),\n        );\n        await newDirectory.create(recursive: true);\n        await _copyDirectory(entity, newDirectory);\n      } else if (entity is File) {\n        final newFile = File(join(destination.path, basename(entity.path)));\n        await entity.copy(newFile.path);\n      }\n    }\n  }\n\n  Future<void> clearUI() async {\n    try {\n      final uiPath = await appPath.uiPath;\n      final uiDir = Directory(uiPath);\n\n      if (await uiDir.exists()) {\n        await uiDir.delete(recursive: true);\n        commonPrint.log('UI cleared successfully');\n      }\n    } catch (e) {\n      commonPrint.log('Error clearing UI: $e');\n    }\n  }\n}\n\nfinal uiManager = UiManager();"
  },
  {
    "path": "lib/common/utils.dart",
    "content": "import 'dart:async';\nimport 'dart:io';\nimport 'dart:math';\nimport 'dart:ui';\n\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:bett_box/l10n/l10n.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:lpinyin/lpinyin.dart';\n\nclass Utils {\n  Color? getDelayColor(int? delay) {\n    if (delay == null) return null;\n    if (delay < 0) return Colors.red;\n    if (delay < 600) return Colors.green;\n    return const Color(0xFFC57F0A);\n  }\n\n  String get id {\n    final timestamp = DateTime.now().microsecondsSinceEpoch;\n    final random = Random();\n    final randomStr = String.fromCharCodes(\n      List.generate(8, (_) => random.nextInt(26) + 97),\n    );\n    return '$timestamp$randomStr';\n  }\n\n  String getDateStringLast2(int value) {\n    var valueRaw = '0$value';\n    return valueRaw.substring(valueRaw.length - 2);\n  }\n\n  String generateRandomString({int minLength = 10, int maxLength = 100}) {\n    const latinChars =\n        'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';\n    final random = Random();\n\n    int length = minLength + random.nextInt(maxLength - minLength + 1);\n\n    String result = '';\n    for (int i = 0; i < length; i++) {\n      if (random.nextBool()) {\n        result += String.fromCharCode(\n          0x4E00 + random.nextInt(0x9FA5 - 0x4E00 + 1),\n        );\n      } else {\n        result += latinChars[random.nextInt(latinChars.length)];\n      }\n    }\n\n    return result;\n  }\n\n  String get uuidV4 {\n    final Random random = Random();\n    final bytes = List.generate(16, (_) => random.nextInt(256));\n\n    bytes[6] = (bytes[6] & 0x0F) | 0x40;\n    bytes[8] = (bytes[8] & 0x3F) | 0x80;\n\n    final hex = bytes\n        .map((byte) => byte.toRadixString(16).padLeft(2, '0'))\n        .join();\n\n    return '${hex.substring(0, 8)}-${hex.substring(8, 12)}-${hex.substring(12, 16)}-${hex.substring(16, 20)}-${hex.substring(20, 32)}';\n  }\n\n  String getTimeDifference(DateTime dateTime) {\n    var currentDateTime = DateTime.now();\n    var difference = currentDateTime.difference(dateTime);\n    var inHours = difference.inHours;\n    var inMinutes = difference.inMinutes;\n    var inSeconds = difference.inSeconds;\n\n    return '${getDateStringLast2(inHours)}:${getDateStringLast2(inMinutes)}:${getDateStringLast2(inSeconds)}';\n  }\n\n  String getTimeText(int? timeStamp) {\n    if (timeStamp == null) {\n      return '00:00:00';\n    }\n    final diff = timeStamp / 1000;\n    final inHours = (diff / 3600).floor();\n    if (inHours > 99) {\n      return '99:59:59';\n    }\n    final inMinutes = (diff / 60 % 60).floor();\n    final inSeconds = (diff % 60).floor();\n\n    return '${getDateStringLast2(inHours)}:${getDateStringLast2(inMinutes)}:${getDateStringLast2(inSeconds)}';\n  }\n\n  Locale getSystemLocale() {\n    final platformLocale = WidgetsBinding.instance.platformDispatcher.locale;\n    final supportedLocales = AppLocalizations.delegate.supportedLocales;\n    \n    if (platformLocale.languageCode.toLowerCase() == 'zh') {\n      final isTraditional = \n        (platformLocale.countryCode?.toUpperCase() == 'TW') ||\n        (platformLocale.countryCode?.toUpperCase() == 'HK') ||\n        (platformLocale.countryCode?.toUpperCase() == 'MO') ||\n        (platformLocale.scriptCode?.toLowerCase() == 'hant');\n      return isTraditional ? const Locale('zh', 'TC') : const Locale('zh', 'CN');\n    }\n    \n    for (final locale in supportedLocales) {\n      if (locale.languageCode == platformLocale.languageCode) {\n        return locale;\n      }\n    }\n    \n    return const Locale('zh', 'CN');\n  }\n\n  Locale? getLocaleForString(String? localString) {\n    if (localString == null) return null;\n    var localSplit = localString.split('_');\n    if (localSplit.length == 1) {\n      return Locale(localSplit[0]);\n    }\n    if (localSplit.length == 2) {\n      return Locale(localSplit[0], localSplit[1]);\n    }\n    if (localSplit.length == 3) {\n      return Locale.fromSubtags(\n        languageCode: localSplit[0],\n        scriptCode: localSplit[1],\n        countryCode: localSplit[2],\n      );\n    }\n    return null;\n  }\n\n  int sortByChar(String a, String b) {\n    if (a.isEmpty && b.isEmpty) {\n      return 0;\n    }\n    if (a.isEmpty) {\n      return -1;\n    }\n    if (b.isEmpty) {\n      return 1;\n    }\n    final charA = a[0];\n    final charB = b[0];\n\n    if (charA == charB) {\n      return sortByChar(a.substring(1), b.substring(1));\n    } else {\n      return charA.compareToLower(charB);\n    }\n  }\n\n  String getOverwriteLabel(String label) {\n    final reg = RegExp(r'\\((\\d+)\\)$');\n    final matches = reg.allMatches(label);\n    if (matches.isNotEmpty) {\n      final match = matches.last;\n      final number = int.parse(match[1] ?? '0') + 1;\n      return label.replaceFirst(reg, '($number)', label.length - 3 - 1);\n    } else {\n      return '$label(1)';\n    }\n  }\n\n  String getTrayIconPath({\n    required Brightness brightness,\n    bool isStart = false,\n  }) {\n    if (system.isMacOS) {\n      return 'assets/images/icon_template.png';\n    }\n\n    if (system.isLinux) {\n      return 'assets/images/icon.png';\n    }\n\n    final suffix = system.isWindows ? 'ico' : 'png';\n\n    return switch (brightness) {\n      Brightness.dark =>\n        !isStart\n            ? 'assets/images/icon.$suffix'\n            : 'assets/images/icon_white.$suffix',\n      Brightness.light =>\n        !isStart\n            ? 'assets/images/icon_light.$suffix'\n            : 'assets/images/icon_black.$suffix',\n    };\n  }\n\n  int compareVersions(String version1, String version2) {\n    List<String> v1 = version1.split('+')[0].split('.');\n    List<String> v2 = version2.split('+')[0].split('.');\n    int major1 = int.parse(v1[0]);\n    int major2 = int.parse(v2[0]);\n    if (major1 != major2) {\n      return major1.compareTo(major2);\n    }\n    int minor1 = v1.length > 1 ? int.parse(v1[1]) : 0;\n    int minor2 = v2.length > 1 ? int.parse(v2[1]) : 0;\n    if (minor1 != minor2) {\n      return minor1.compareTo(minor2);\n    }\n    int patch1 = v1.length > 2 ? int.parse(v1[2]) : 0;\n    int patch2 = v2.length > 2 ? int.parse(v2[2]) : 0;\n    if (patch1 != patch2) {\n      return patch1.compareTo(patch2);\n    }\n    int build1 = version1.contains('+') ? int.parse(version1.split('+')[1]) : 0;\n    int build2 = version2.contains('+') ? int.parse(version2.split('+')[1]) : 0;\n    return build1.compareTo(build2);\n  }\n\n  String getPinyin(String value) {\n    return value.isNotEmpty\n        ? PinyinHelper.getFirstWordPinyin(value.substring(0, 1))\n        : '';\n  }\n\n  String? getFileNameForDisposition(String? disposition) {\n    if (disposition == null) return null;\n    final parseValue = HeaderValue.parse(disposition);\n    final parameters = parseValue.parameters;\n    final fileNamePointKey = parameters.keys.firstWhere(\n      (key) => key == 'filename*',\n      orElse: () => '',\n    );\n    if (fileNamePointKey.isNotEmpty) {\n      final res = parameters[fileNamePointKey]?.split(\"''\") ?? [];\n      if (res.length >= 2) {\n        return Uri.decodeComponent(res[1]);\n      }\n    }\n    final fileNameKey = parameters.keys.firstWhere(\n      (key) => key == 'filename',\n      orElse: () => '',\n    );\n    if (fileNameKey.isEmpty) return null;\n    return parameters[fileNameKey];\n  }\n\n  FlutterView getScreen() {\n    return WidgetsBinding.instance.platformDispatcher.views.first;\n  }\n\n  List<String> parseReleaseBody(String? body) {\n    if (body == null) return [];\n    const pattern = r'- \\s*(.*)';\n    final regex = RegExp(pattern);\n    return regex\n        .allMatches(body)\n        .map((match) => match.group(1) ?? '')\n        .where((item) => item.isNotEmpty)\n        .toList();\n  }\n\n  ViewMode getViewMode(double viewWidth) {\n    if (viewWidth <= maxMobileWidth) return ViewMode.mobile;\n    if (viewWidth <= maxLaptopWidth) return ViewMode.laptop;\n    return ViewMode.desktop;\n  }\n\n  int getProxiesColumns(double viewWidth, ProxiesLayout proxiesLayout) {\n    final columns = max((viewWidth / 300).ceil(), 2);\n    return switch (proxiesLayout) {\n      ProxiesLayout.tight => columns + 1,\n      ProxiesLayout.standard => columns,\n      ProxiesLayout.loose => columns - 1,\n    };\n  }\n\n  int getProfilesColumns(double viewWidth) {\n    return min(max((viewWidth / 320).floor(), 1), 3);\n  }\n\n  final _indexPrimary = [50, 100, 200, 300, 400, 500, 600, 700, 800, 850, 900];\n\n  MaterialColor _createPrimarySwatch(Color color) {\n    final Map<int, Color> swatch = <int, Color>{};\n    final int a = color.alpha8bit;\n    final int r = color.red8bit;\n    final int g = color.green8bit;\n    final int b = color.blue8bit;\n    for (final int strength in _indexPrimary) {\n      final double ds = 0.5 - strength / 1000;\n      swatch[strength] = Color.fromARGB(\n        a,\n        r + ((ds < 0 ? r : (255 - r)) * ds).round(),\n        g + ((ds < 0 ? g : (255 - g)) * ds).round(),\n        b + ((ds < 0 ? b : (255 - b)) * ds).round(),\n      );\n    }\n    swatch[50] = swatch[50]!.lighten(18);\n    swatch[100] = swatch[100]!.lighten(16);\n    swatch[200] = swatch[200]!.lighten(14);\n    swatch[300] = swatch[300]!.lighten(10);\n    swatch[400] = swatch[400]!.lighten(6);\n    swatch[700] = swatch[700]!.darken(2);\n    swatch[800] = swatch[800]!.darken(3);\n    swatch[900] = swatch[900]!.darken(4);\n    return MaterialColor(color.value32bit, swatch);\n  }\n\n  List<Color> getMaterialColorShades(Color color) {\n    final swatch = _createPrimarySwatch(color);\n    return <Color>[\n      if (swatch[50] != null) swatch[50]!,\n      if (swatch[100] != null) swatch[100]!,\n      if (swatch[200] != null) swatch[200]!,\n      if (swatch[300] != null) swatch[300]!,\n      if (swatch[400] != null) swatch[400]!,\n      if (swatch[500] != null) swatch[500]!,\n      if (swatch[600] != null) swatch[600]!,\n      if (swatch[700] != null) swatch[700]!,\n      if (swatch[800] != null) swatch[800]!,\n      if (swatch[850] != null) swatch[850]!,\n      if (swatch[900] != null) swatch[900]!,\n    ];\n  }\n\n  String getBackupFileName() {\n    return '${appName}_backup_${DateTime.now().show}.zip';\n  }\n\n  String get logFile {\n    return '${appName}_${DateTime.now().show}.log';\n  }\n\n  Future<String?> getLocalIpAddress() async {\n    List<NetworkInterface> interfaces =\n        await NetworkInterface.list(includeLoopback: false)\n          ..sort((a, b) {\n            if (a.isWifi && !b.isWifi) return -1;\n            if (!a.isWifi && b.isWifi) return 1;\n            if (a.includesIPv4 && !b.includesIPv4) return -1;\n            if (!a.includesIPv4 && b.includesIPv4) return 1;\n            return 0;\n          });\n    for (final interface in interfaces) {\n      final addresses = interface.addresses;\n      if (addresses.isEmpty) {\n        continue;\n      }\n      addresses.sort((a, b) {\n        if (a.isIPv4 && !b.isIPv4) return -1;\n        if (!a.isIPv4 && b.isIPv4) return 1;\n        return 0;\n      });\n      return addresses.first.address;\n    }\n    return '';\n  }\n\n  SingleActivator controlSingleActivator(LogicalKeyboardKey trigger) {\n    final control = system.isMacOS ? false : true;\n    return SingleActivator(trigger, control: control, meta: !control);\n  }\n\n\n  FutureOr<T> handleWatch<T>(Function function) async {\n    if (kDebugMode) {\n      final stopwatch = Stopwatch()..start();\n      final res = await function();\n      stopwatch.stop();\n      commonPrint.log('Time：${stopwatch.elapsedMilliseconds} ms');\n      return res;\n    }\n    return await function();\n  }\n\n  String generateSecret() {\n    final random = Random();\n    // Generate an 8-digit number (10000000 to 99999999)\n    final secret = 10000000 + random.nextInt(90000000);\n    return secret.toString();\n  }\n}\n\nfinal utils = Utils();\n"
  },
  {
    "path": "lib/common/window.dart",
    "content": "import 'dart:io';\n\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_acrylic/flutter_acrylic.dart' as acrylic;\nimport 'package:screen_retriever/screen_retriever.dart';\nimport 'package:tray_manager/tray_manager.dart';\nimport 'package:window_manager/window_manager.dart';\n\nclass Window {\n  Future<void> init(int version) async {\n    final props = globalState.config.windowProps;\n    final acquire = await singleInstanceLock.acquire();\n    if (!acquire) {\n      exit(0);\n    }\n    if (system.isWindows) {\n      protocol.register('clash');\n      protocol.register('clashmeta');\n      protocol.register('bettbox');\n    }\n    if ((version > 10 && system.isMacOS)) {\n      await acrylic.Window.initialize();\n    }\n    await windowManager.ensureInitialized();\n    WindowOptions windowOptions = WindowOptions(\n      size: Size(props.width, props.height),\n      minimumSize: const Size(380, 400),\n    );\n    if (!system.isMacOS || version > 10) {\n      await windowManager.setTitleBarStyle(TitleBarStyle.hidden);\n    }\n    if (!system.isMacOS) {\n      final left = props.left ?? 0;\n      final top = props.top ?? 0;\n      final right = left + props.width;\n      final bottom = top + props.height;\n      if (left == 0 && top == 0) {\n        await windowManager.setAlignment(Alignment.center);\n      } else {\n        final displays = await screenRetriever.getAllDisplays();\n        final isPositionValid = displays.any((display) {\n          final displayBounds = Rect.fromLTWH(\n            display.visiblePosition!.dx,\n            display.visiblePosition!.dy,\n            display.size.width,\n            display.size.height,\n          );\n          return displayBounds.contains(Offset(left, top)) ||\n              displayBounds.contains(Offset(right, bottom));\n        });\n        if (isPositionValid) {\n          await windowManager.setPosition(Offset(left, top));\n        }\n      }\n    }\n    await windowManager.waitUntilReadyToShow(windowOptions, () async {\n      await windowManager.setPreventClose(true);\n    });\n\n    if (props.isLocked) {\n      try {\n        await windowManager.setResizable(false);\n      } catch (e) {\n        commonPrint.log('Failed to apply the locked state: $e');\n      }\n    }\n  }\n\n  void updateMacOSBrightness(Brightness brightness) {\n    if (!system.isMacOS) {\n      return;\n    }\n    acrylic.Window.overrideMacOSBrightness(dark: brightness == Brightness.dark);\n  }\n\n  Future<void> show() async {\n    globalState.handleForeground();\n    render?.resume();\n    await windowManager.show();\n    await windowManager.focus();\n    await windowManager.setSkipTaskbar(false);\n    await globalState.resumeForegroundUpdates();\n    await globalState.appController.syncWakelockIfNeeded();\n  }\n\n  Future<bool> get isVisible async {\n    return await windowManager.isVisible();\n  }\n\n  Future<bool> get isMinimized async {\n    return await windowManager.isMinimized();\n  }\n\n  Future<void> close() async {\n    try {\n      await trayManager.destroy();\n      commonPrint.log('The tray icon has been destroyed.');\n    } catch (e) {\n      commonPrint.log('Failed to destroy the tray icon: $e');\n    }\n\n    exit(0);\n  }\n\n  Future<void> hide() async {\n    await windowManager.hide();\n    await windowManager.setSkipTaskbar(true);\n    await globalState.handleBackground();\n  }\n}\n\nfinal window = system.isDesktop ? Window() : null;\n"
  },
  {
    "path": "lib/controller.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\nimport 'dart:io';\nimport 'dart:isolate';\n\nimport 'package:archive/archive_io.dart';\nimport 'package:bett_box/clash/clash.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/plugins/app.dart';\nimport 'package:bett_box/plugins/service.dart' as vpn_service;\nimport 'package:bett_box/providers/providers.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/dialog.dart';\nimport 'package:collection/collection.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:path/path.dart';\nimport 'package:url_launcher/url_launcher.dart';\nimport 'package:wakelock_plus/wakelock_plus.dart';\nimport 'package:yaml/yaml.dart';\n\nimport 'common/common.dart';\nimport 'common/flclash_database_extractor.dart';\nimport 'models/models.dart';\nimport 'views/profiles/override_profile.dart';\n\nclass AppController {\n  int? lastProfileModified;\n\n  final BuildContext context;\n  final WidgetRef _ref;\n\n  Timer? _wakelockSyncTimer;\n  Completer<void>? _restartLock;\n  Completer<void>? _exitLock;\n  int _backgroundLoadVersion = 0;\n\n  int _updateGroupsRetryCount = 0;\n  bool _isUpdatingGroups = false;\n\n  AppController(this.context, WidgetRef ref) : _ref = ref;\n\n  void setupClashConfigDebounce() {\n    debouncer.call(FunctionTag.setupClashConfig, () async {\n      await setupClashConfig();\n    });\n  }\n\n  void updateClashConfigDebounce() {\n    debouncer.call(FunctionTag.updateClashConfig, () async {\n      await updateClashConfig();\n    });\n  }\n\n  void updateGroupsDebounce() {\n    debouncer.call(FunctionTag.updateGroups, updateGroups);\n  }\n\n  void addCheckIpNumDebounce() {\n    debouncer.call(FunctionTag.addCheckIpNum, () {\n      _ref.read(checkIpNumProvider.notifier).add();\n    });\n  }\n\n  void addCheckIp() {\n    _ref.read(checkIpNumProvider.notifier).add();\n  }\n\n  void applyProfileDebounce({bool silence = false}) {\n    debouncer.call(FunctionTag.applyProfile, (silence) {\n      applyProfile(silence: silence);\n    }, args: [silence]);\n  }\n\n  void savePreferencesDebounce() {\n    debouncer.call(FunctionTag.savePreferences, savePreferences);\n  }\n\n  void changeProxyDebounce(String groupName, String proxyName) {\n    debouncer.call(FunctionTag.changeProxy, (\n      String groupName,\n      String proxyName,\n    ) async {\n      await changeProxy(groupName: groupName, proxyName: proxyName);\n      await updateGroups();\n      addCheckIp();\n    }, args: [groupName, proxyName]);\n  }\n\n  Future<void> restartCore() async {\n    if (_restartLock != null) {\n      return _restartLock!.future;\n    }\n\n    _restartLock = Completer<void>();\n    commonPrint.log('restart core');\n\n    try {\n      final wasRunning = _ref.read(runTimeProvider.notifier).isStart;\n      if (wasRunning) {\n        await globalState.handleStop();\n      }\n      await Future.delayed(const Duration(milliseconds: 500));\n      await _initCore();\n      if (wasRunning) {\n        if (system.isDesktop) {\n          await _fastStart();\n        } else {\n          await globalState.handleStart();\n        }\n      }\n    } finally {\n      _restartLock?.complete();\n      _restartLock = null;\n    }\n  }\n\n  Future<void> updateStatus(bool isStart) async {\n    if (isStart) {\n      await _fastStart();\n    } else {\n      await globalState.handleStop();\n      clashCore.resetTraffic();\n      _ref.read(trafficsProvider.notifier).clear();\n      _ref.read(totalTrafficProvider.notifier).value = Traffic();\n      _ref.read(runTimeProvider.notifier).value = null;\n      addCheckIpNumDebounce();\n    }\n  }\n\n  Future<void> _fastStart() async {\n    final patchConfig = _ref.read(patchClashConfigProvider);\n    final isDesktop = system.isDesktop;\n\n    if (isDesktop && patchConfig.tun.enable) {\n      await _quickSetupConfig(enableTun: false);\n      await globalState.handleStart([updateRunTime, updateTraffic]);\n\n      Future.microtask(() async {\n        final res = await _requestAdmin(true);\n        if (res.needRestart) {\n          await restartCore();\n        } else if (!res.isError) {\n          await _updateClashConfig();\n        }\n        _backgroundLoad();\n      });\n\n      _scheduleCheckIpRefresh();\n      return;\n    }\n\n    final needReapply = await _checkIfNeedReapply();\n    if (needReapply) {\n      await _quickSetupConfig();\n    }\n\n    await globalState.handleStart([updateRunTime, updateTraffic]);\n\n    _scheduleCheckIpRefresh();\n\n    _backgroundLoad();\n  }\n\n\n\n  void _scheduleCheckIpRefresh() {\n    Future.delayed(const Duration(seconds: 1), () {\n      addCheckIpNumDebounce();\n    });\n  }\n\n  void _backgroundLoad() {\n    final version = ++_backgroundLoadVersion;\n\n    Future.microtask(() async {\n      try {\n        final groups = await clashCore.getProxiesGroups();\n        if (version != _backgroundLoadVersion) return;\n\n        final providers = await clashCore.getExternalProviders();\n        if (version != _backgroundLoadVersion) return;\n\n        _ref.read(groupsProvider.notifier).value = groups;\n        _ref.read(providersProvider.notifier).value = providers;\n\n        await Future.delayed(const Duration(seconds: 2));\n        if (version != _backgroundLoadVersion) return;\n        await clashCore.requestGc();\n      } catch (e) {\n        commonPrint.log('Background load error: $e');\n      }\n    });\n  }\n\n  Future<bool> _checkIfNeedReapply() async {\n    final currentLastModified = await _ref\n        .read(currentProfileProvider)\n        ?.profileLastModified;\n    if (currentLastModified != null &&\n        lastProfileModified != null &&\n        currentLastModified <= lastProfileModified!) {\n      return false;\n    }\n    return true;\n  }\n\n  Future<void> _setupCoreConfig({bool? enableTun}) async {\n    await _ref.read(currentProfileProvider)?.checkAndUpdate();\n    final patchConfig = _ref.read(patchClashConfigProvider);\n    final targetTun = enableTun ?? patchConfig.tun.enable;\n\n    final realTunEnable = await _prepareTun(targetTun);\n    if (realTunEnable == null) return;\n\n    final realPatchConfig = patchConfig.copyWith.tun(enable: realTunEnable);\n    final params = await globalState.getSetupParams(\n      pathConfig: realPatchConfig,\n    );\n    final message = await clashCore.setupConfig(params);\n    if (message.isNotEmpty) {\n      await _rollbackConfig();\n      throw message;\n    }\n    globalState.backupSuccessfulConfig(params);\n    lastProfileModified = await _ref.read(\n      currentProfileProvider.select((state) => state?.profileLastModified),\n    );\n  }\n\n  Future<void> _quickSetupConfig({bool? enableTun}) async {\n    await safeRun(() async {\n      await _setupCoreConfig(enableTun: enableTun);\n    }, needLoading: false);\n  }\n\n  Future<void> updateRunTime() async {\n    final startTime = globalState.startTime;\n    if (startTime == null) {\n      if (_ref.read(runTimeProvider) != null) {\n        _ref.read(runTimeProvider.notifier).value = null;\n      }\n      return;\n    }\n\n    final startTimeStamp = startTime.millisecondsSinceEpoch;\n    final nowTimeStamp = DateTime.now().millisecondsSinceEpoch;\n    final elapsed = nowTimeStamp - startTimeStamp;\n\n    final current = _ref.read(runTimeProvider);\n    if (current == null) {\n      _ref.read(runTimeProvider.notifier).value = elapsed;\n      return;\n    }\n    _ref.read(runTimeProvider.notifier).value = elapsed;\n  }\n\n  Future<bool> _shouldUpdateDashboardTick() async {\n    final lifecycleState = WidgetsBinding.instance.lifecycleState;\n    if (lifecycleState != AppLifecycleState.resumed) return false;\n\n    if (system.isDesktop) {\n      if (await window?.isVisible == false) return false;\n      if (await window?.isMinimized == true) return false;\n    }\n\n    return true;\n  }\n\n  Future<void> updateTraffic() async {\n    _ref.read(totalTrafficProvider.notifier).value = await clashCore\n        .getTotalTraffic();\n    if (!await _shouldUpdateDashboardTick()) {\n      return;\n    }\n    final traffic = await clashCore.getTraffic();\n    _ref.read(trafficsProvider.notifier).addTraffic(traffic);\n  }\n\n  Future<void> addProfile(Profile profile) async {\n    _ref.read(profilesProvider.notifier).setProfile(profile);\n    if (_ref.read(currentProfileIdProvider) != null) return;\n    _ref.read(currentProfileIdProvider.notifier).value = profile.id;\n  }\n\n  Future<void> deleteProfile(String id) async {\n    _ref.read(profilesProvider.notifier).deleteProfileById(id);\n    await clearEffect(id);\n    if (globalState.config.currentProfileId == id) {\n      final profiles = globalState.config.profiles;\n      final currentProfileId = _ref.read(currentProfileIdProvider.notifier);\n      if (profiles.isNotEmpty) {\n        final updateId = profiles.first.id;\n        currentProfileId.value = updateId;\n      } else {\n        currentProfileId.value = null;\n        updateStatus(false);\n      }\n    }\n  }\n\n  Future<void> updateProviders() async {\n    _ref.read(providersProvider.notifier).value = await clashCore\n        .getExternalProviders();\n  }\n\n  Future<void> updateLocalIp() async {\n    _ref.read(localIpProvider.notifier).value = null;\n    await Future.delayed(commonDuration);\n    _ref.read(localIpProvider.notifier).value = await utils.getLocalIpAddress();\n  }\n\n  Future<void> updateProfile(Profile profile) async {\n    final newProfile = await profile.update();\n    _ref\n        .read(profilesProvider.notifier)\n        .setProfile(newProfile.copyWith(isUpdating: false));\n    if (profile.id == _ref.read(currentProfileIdProvider)) {\n      applyProfileDebounce(silence: true);\n    }\n  }\n\n  void setProfile(Profile profile) {\n    _ref.read(profilesProvider.notifier).setProfile(profile);\n  }\n\n  void setProfileAndAutoApply(Profile profile) {\n    _ref.read(profilesProvider.notifier).setProfile(profile);\n    if (profile.id == _ref.read(currentProfileIdProvider)) {\n      applyProfileDebounce(silence: true);\n    }\n  }\n\n  void setProfiles(List<Profile> profiles) {\n    _ref.read(profilesProvider.notifier).value = profiles;\n  }\n\n  void addLog(Log log) {\n    _ref.read(logsProvider).add(log);\n  }\n\n  void updateOrAddHotKeyAction(HotKeyAction hotKeyAction) {\n    final hotKeyActions = _ref.read(hotKeyActionsProvider);\n    final index = hotKeyActions.indexWhere(\n      (item) => item.action == hotKeyAction.action,\n    );\n\n    final newList = List.of(hotKeyActions);\n    if (index == -1) {\n      newList.add(hotKeyAction);\n    } else {\n      newList[index] = hotKeyAction;\n    }\n\n    _ref.read(hotKeyActionsProvider.notifier).value = newList;\n  }\n\n  List<Group> getCurrentGroups() {\n    return _ref.read(currentGroupsStateProvider.select((state) => state.value));\n  }\n\n  String getRealTestUrl(String? url) {\n    return _ref.read(getRealTestUrlProvider(url));\n  }\n\n  int getProxiesColumns() {\n    return _ref.read(getProxiesColumnsProvider);\n  }\n\n  dynamic addSortNum() {\n    return _ref.read(sortNumProvider.notifier).add();\n  }\n\n  String? getCurrentGroupName() {\n    final currentGroupName = _ref.read(\n      currentProfileProvider.select((state) => state?.currentGroupName),\n    );\n    return currentGroupName;\n  }\n\n  ProxyCardState getProxyCardState(String proxyName) {\n    return _ref.read(getProxyCardStateProvider(proxyName));\n  }\n\n  String? getSelectedProxyName(String groupName) {\n    return _ref.read(getSelectedProxyNameProvider(groupName));\n  }\n\n  void updateCurrentGroupName(String groupName) {\n    final profile = _ref.read(currentProfileProvider);\n    if (profile == null || profile.currentGroupName == groupName) {\n      return;\n    }\n    setProfile(profile.copyWith(currentGroupName: groupName));\n  }\n\n  Future<void> updateClashConfig() async {\n    await safeRun(() async {\n      await _updateClashConfig();\n    }, needLoading: true);\n  }\n\n  Future<bool?> _prepareTun(bool targetTun) async {\n    final res = await _requestAdmin(targetTun);\n    if (res.needRestart) {\n      await restartCore();\n    } else if (res.isError) {\n      return null;\n    }\n    return _ref.read(realTunEnableProvider);\n  }\n\n  Future<void> _updateClashConfig() async {\n    final updateParams = _ref.read(updateParamsProvider);\n    final realTunEnable = await _prepareTun(updateParams.tun.enable);\n    if (realTunEnable == null) return;\n\n    final message = await clashCore.updateConfig(\n      updateParams.copyWith.tun(enable: realTunEnable),\n    );\n    if (message.isNotEmpty) throw message;\n  }\n\n  Future<Result<bool>> _requestAdmin(bool enableTun) async {\n    if (system.isWindows && kDebugMode) {\n      return Result.success(false);\n    }\n    final realTunEnable = _ref.read(realTunEnableProvider);\n    if (enableTun != realTunEnable && realTunEnable == false) {\n      final code = await system.authorizeCore();\n      switch (code) {\n        case AuthorizeCode.success:\n          return Result.success(true, needRestart: true);\n        case AuthorizeCode.none:\n          break;\n        case AuthorizeCode.error:\n          if (system.isWindows) {\n            globalState.showNotifier(appLocalizations.tunEnableRequireAdmin);\n          }\n          enableTun = false;\n          break;\n      }\n    }\n    _ref.read(realTunEnableProvider.notifier).value = enableTun;\n    return Result.success(enableTun);\n  }\n\n  Future<void> setupClashConfig() async {\n    await safeRun(() async {\n      await _setupCoreConfig();\n    }, needLoading: true);\n  }\n\n  Future _applyProfile() async {\n    await clashCore.requestGc();\n    await setupClashConfig();\n    await updateGroups();\n    await updateProviders();\n  }\n\n  Future applyProfile({bool silence = false}) async {\n    if (silence) {\n      await _applyProfile();\n    } else {\n      await safeRun(() async {\n        await _applyProfile();\n      }, needLoading: true);\n    }\n    addCheckIpNumDebounce();\n  }\n\n  void handleChangeProfile() {\n    _ref.read(delayDataSourceProvider.notifier).value = {};\n    applyProfile();\n    _ref.read(logsProvider.notifier).value = FixedList(maxLength);\n    _ref.read(requestsProvider.notifier).value = FixedList(maxLength);\n    globalState.computeHeightMapCache = {};\n  }\n\n  void updateBrightness() {\n    _ref.read(systemBrightnessProvider.notifier).value =\n        WidgetsBinding.instance.platformDispatcher.platformBrightness;\n  }\n\n  Future<void> autoUpdateProfiles() async {\n    for (final profile in _ref.read(profilesProvider)) {\n      if (!profile.autoUpdate) continue;\n      final isNotNeedUpdate = profile.lastUpdateDate\n          ?.add(profile.autoUpdateDuration)\n          .isBeforeNow;\n      if (isNotNeedUpdate == false || profile.type == ProfileType.file) {\n        continue;\n      }\n      try {\n        await updateProfile(profile);\n      } catch (e) {\n        commonPrint.log(e.toString());\n      }\n    }\n  }\n\n  Future<void> checkAndUpdateMissedProfiles() async {\n    final now = DateTime.now();\n    final profilesToUpdate = <Profile>[];\n    for (final profile in _ref.read(profilesProvider)) {\n      if (!profile.autoUpdate) continue;\n      if (profile.type == ProfileType.file) continue;\n      if (profile.isUpdating) continue;\n      final lastUpdate = profile.lastUpdateDate;\n      if (lastUpdate == null) continue;\n      final expectedNextUpdate = lastUpdate.add(profile.autoUpdateDuration);\n      final isOverdue = now.difference(expectedNextUpdate) > const Duration(minutes: 1);\n      if (isOverdue) {\n        profilesToUpdate.add(profile);\n      }\n    }\n    if (profilesToUpdate.isEmpty) return;\n    for (final profile in profilesToUpdate) {\n      try {\n        commonPrint.log('[MissedUpdate] Updating profile: ${profile.label ?? profile.id}');\n        await updateProfile(profile);\n      } catch (e) {\n        commonPrint.log('[MissedUpdate] Failed to update ${profile.id}: $e');\n      }\n      if (profilesToUpdate.length > 1) {\n        await Future.delayed(const Duration(seconds: 2));\n      }\n    }\n  }\n\n  Future<void> updateGroups() async {\n    if (_isUpdatingGroups) {\n      commonPrint.log('updateGroups already in progress, skipping');\n      return;\n    }\n    _isUpdatingGroups = true;\n\n    try {\n      final currentGroups = _ref.read(groupsProvider);\n      final isInitialDesktopLoad = system.isDesktop && currentGroups.isEmpty;\n      final maxAttempts = isInitialDesktopLoad ? 6 : 4;\n\n      final newGroups = await retry(\n        task: clashCore.getProxiesGroups,\n        retryIf: (res) => res.isEmpty,\n        maxAttempts: maxAttempts,\n      );\n      _ref.read(groupsProvider.notifier).value = newGroups;\n      _updateGroupsRetryCount = 0;\n      return;\n    } catch (e) {\n      final currentGroups = _ref.read(groupsProvider);\n      final isInitialDesktopLoad = system.isDesktop && currentGroups.isEmpty;\n      final maxRetryRounds = isInitialDesktopLoad ? 8 : 4;\n      final retryDelay = isInitialDesktopLoad\n          ? const Duration(seconds: 1)\n          : const Duration(seconds: 2);\n      if (currentGroups.isNotEmpty) {\n        commonPrint.log('updateGroups error, keeping existing groups: $e');\n        return;\n      }\n\n      if (_updateGroupsRetryCount >= maxRetryRounds) {\n        commonPrint.log(\n          'updateGroups max retries ($maxRetryRounds) reached, giving up',\n        );\n        _updateGroupsRetryCount = 0;\n        return;\n      }\n      _updateGroupsRetryCount++;\n\n      commonPrint.log(\n        'updateGroups initial load failed ($_updateGroupsRetryCount/$maxRetryRounds), scheduling retry in ${retryDelay.inSeconds}s: $e',\n      );\n      Future.delayed(retryDelay, () {\n        updateGroups();\n      });\n    } finally {\n      _isUpdatingGroups = false;\n    }\n  }\n\n  Future<void> updateProfiles() async {\n    for (final profile in _ref.read(profilesProvider)) {\n      if (profile.type == ProfileType.file) {\n        continue;\n      }\n      await updateProfile(profile);\n    }\n  }\n\n  Future<void> savePreferences() async {\n    await preferences.saveConfig(globalState.config);\n  }\n\n  Future<void> changeProxy({\n    required String groupName,\n    required String proxyName,\n  }) async {\n    await clashCore.changeProxy(\n      ChangeProxyParams(groupName: groupName, proxyName: proxyName),\n    );\n    if (_ref.read(appSettingProvider).closeConnections) {\n      clashCore.closeConnections();\n    }\n    addCheckIp();\n  }\n\n  Future<void> handleBackOrExit() async {\n    if (_ref.read(backBlockProvider)) {\n      return;\n    }\n    if (system.isDesktop) {\n      await savePreferences();\n    }\n    await system.back();\n  }\n\n  void backBlock() {\n    _ref.read(backBlockProvider.notifier).value = true;\n  }\n\n  void unBackBlock() {\n    _ref.read(backBlockProvider.notifier).value = false;\n  }\n\n  Future<void> handleExit() async {\n    if (_exitLock != null) {\n      return _exitLock!.future;\n    }\n\n    final exitLock = Completer<void>();\n    _exitLock = exitLock;\n    globalState.isExiting = true;\n\n    try {\n      stopWakelockAutoRecovery();\n      await globalState.handleBackground();\n      await savePreferences();\n      if (macOS != null) {\n        await macOS!.updateDns(true);\n      }\n      if (proxy != null) {\n        await proxy!.stopProxy();\n      }\n      await clashCore.shutdown();\n      if (clashService != null) {\n        await clashService!.destroy();\n      }\n    } catch (e) {\n      commonPrint.log('handleExit error: $e');\n    } finally {\n      if (!exitLock.isCompleted) {\n        exitLock.complete();\n      }\n      system.exit();\n    }\n  }\n\n  Future handleClear() async {\n    await preferences.clearPreferences();\n    commonPrint.log('clear preferences');\n    globalState.config = Config(themeProps: defaultThemeProps);\n  }\n\n  Future<void> autoCheckUpdate() async {\n    final prefs = await preferences.sharedPreferencesCompleter.future;\n    final lastCheckTime = prefs?.getInt('last_check_update_time') ?? 0;\n    final now = DateTime.now().millisecondsSinceEpoch;\n    final isAutoCheck = _ref.read(appSettingProvider).autoCheckUpdate;\n\n    final forceCheck =\n        (now - lastCheckTime) > const Duration(days: 28).inMilliseconds;\n\n    if (!isAutoCheck && !forceCheck) return;\n\n    final res = await request.checkForUpdate();\n    if (res != null) {\n      checkUpdateResultHandle(data: res);\n    }\n\n    await prefs?.setInt('last_check_update_time', now);\n  }\n\n  Future<void> checkUpdateResultHandle({\n    Map<String, dynamic>? data,\n    bool handleError = false,\n  }) async {\n    if (globalState.isPre && !handleError) {\n      return;\n    }\n    if (data != null) {\n      final tagName = data['tag_name'];\n      final body = data['body'];\n      final submits = utils.parseReleaseBody(body);\n      final textTheme = context.textTheme;\n      final res = await globalState.showMessage(\n        title: appLocalizations.discoverNewVersion,\n        message: TextSpan(\n          text: '$tagName \\n',\n          style: textTheme.headlineSmall,\n          children: [\n            TextSpan(text: '\\n', style: textTheme.bodyMedium),\n            for (final submit in submits)\n              TextSpan(text: '- $submit \\n', style: textTheme.bodyMedium),\n          ],\n        ),\n        confirmText: appLocalizations.goDownload,\n      );\n      if (res != true) {\n        return;\n      }\n      launchUrl(\n        Uri.parse('https://github.com/$repository/releases/latest'),\n        mode: LaunchMode.externalApplication,\n      );\n    } else if (handleError) {\n      globalState.showMessage(\n        title: appLocalizations.checkUpdate,\n        message: TextSpan(text: appLocalizations.checkUpdateError),\n      );\n    }\n  }\n\n  Future<void> _handlePreference() async {\n    if (await preferences.isInit) {\n      return;\n    }\n    final res = await globalState.showMessage(\n      title: appLocalizations.tip,\n      message: TextSpan(text: appLocalizations.cacheCorrupt),\n    );\n    if (res == true) {\n      final file = File(await appPath.sharedPreferencesPath);\n      final isExists = await file.exists();\n      if (isExists) {\n        await file.delete();\n      }\n    }\n    await handleExit();\n  }\n\n  Future<void> _initCore() async {\n    final isInit = await clashCore.isInit;\n    if (!isInit) {\n      await clashCore.init();\n      await clashCore.setState(globalState.getCoreState());\n    }\n  }\n\n  void startWakelockAutoRecovery() {\n    _wakelockSyncTimer?.cancel();\n    _wakelockSyncTimer = Timer.periodic(const Duration(seconds: 168), (\n      _,\n    ) async {\n      try {\n        final userEnabled = _ref.read(wakelockStateProvider);\n\n        if (!userEnabled) {\n          stopWakelockAutoRecovery();\n          return;\n        }\n\n        await syncWakelockIfNeeded();\n      } catch (_) {}\n    });\n  }\n\n  void stopWakelockAutoRecovery() {\n    _wakelockSyncTimer?.cancel();\n    _wakelockSyncTimer = null;\n  }\n\n  Future<void> syncWakelockIfNeeded() async {\n    final userEnabled = _ref.read(wakelockStateProvider);\n    if (!userEnabled) {\n      stopWakelockAutoRecovery();\n      return;\n    }\n    final actualState = await WakelockPlus.enabled;\n    if (actualState) {\n      return;\n    }\n    await WakelockPlus.enable();\n  }\n\n  Future<void> _initHighRefreshRateDefault() async {\n    try {\n      final androidVersion = await system.version;\n      final currentSetting = _ref.read(appSettingProvider);\n\n      final bool shouldEnableHighRefreshRate =\n          androidVersion >= 31; // Android 12+\n\n      if (currentSetting.enableHighRefreshRate != shouldEnableHighRefreshRate) {\n        _ref\n            .read(appSettingProvider.notifier)\n            .updateState(\n              (state) => state.copyWith(\n                enableHighRefreshRate: shouldEnableHighRefreshRate,\n              ),\n            );\n      }\n    } catch (e) {\n      commonPrint.log('Failed to initialize high refresh rate default: $e');\n    }\n  }\n\n  Future<void> init() async {\n    FlutterError.onError = (details) {\n      if (kDebugMode) {\n        commonPrint.log(details.stack.toString());\n      }\n    };\n\n    vpn_service.service?.addNativeEventCallback((method, arguments) async {\n      if (method == 'vpnStartFailed') {\n        globalState.showNotifier('Failed, Please try again later');\n        await updateStatus(false);\n      }\n    });\n\n    if (system.isAndroid) {\n      await _initHighRefreshRateDefault();\n    }\n\n    try {\n      final wakelockEnabled = await WakelockPlus.enabled;\n      _ref.read(wakelockStateProvider.notifier).state = wakelockEnabled;\n\n      if (wakelockEnabled) {\n        startWakelockAutoRecovery();\n      }\n    } catch (e) {\n      commonPrint.log('Failed to check wake lock status: $e');\n    }\n\n    await updateTray(true);\n\n    await _initCore();\n    try {\n      await _initStatus();\n    } catch (e) {\n      commonPrint.log('_initStatus failed, falling back to basic startup: $e');\n      try {\n        await applyProfile(silence: true);\n      } catch (e2) {\n        commonPrint.log('Fallback applyProfile also failed: $e2');\n      }\n    }\n    autoLaunch?.updateStatus(_ref.read(appSettingProvider).autoLaunch);\n    autoUpdateProfiles();\n    autoCheckUpdate();\n\n    final isWindowVisible = await window?.isVisible ?? false;\n    if (isWindowVisible) {\n      window?.show();\n    } else {\n      if (!_ref.read(appSettingProvider).silentLaunch) {\n        window?.show();\n      } else {\n        window?.hide();\n      }\n    }\n    await syncDesktopRuntimeState(preferCurrentState: true);\n    await updateTray(true);\n\n    await _handlePreference();\n    await _handlerDisclaimer();\n    _ref.read(initProvider.notifier).value = true;\n  }\n\n  Future<void> _initStatus() async {\n    if (system.isAndroid) {\n      await globalState.updateStartTime();\n      if (globalState.isStart && _ref.read(runTimeProvider) == null) {\n        _ref.read(runTimeProvider.notifier).value = 0;\n      }\n    } else if (system.isDesktop) {\n      await syncDesktopRuntimeState();\n    }\n\n    final needRecovery = await _detectAbnormalExit();\n\n    if (needRecovery) {\n      commonPrint.log('Abnormal exit detected');\n      if (system.isAndroid) {\n        try {\n          await applyProfile(silence: true);\n        } catch (e) {\n          commonPrint.log('Recovery failed: $e');\n        }\n      }\n    }\n    final shouldStart =\n        globalState.isStart || _ref.read(appSettingProvider).autoRun;\n\n    if (shouldStart) {\n      try {\n        await updateStatus(true);\n      } catch (e) {\n        commonPrint.log('Auto start failed: $e');\n        await applyProfile();\n        addCheckIpNumDebounce();\n      }\n    } else {\n      await applyProfile();\n      addCheckIpNumDebounce();\n    }\n  }\n\n  Future<void> syncDesktopRuntimeState({\n    bool preferCurrentState = false,\n  }) async {\n    if (!system.isDesktop) return;\n    if (!preferCurrentState || !globalState.isStart) {\n      await globalState.updateStartTime();\n    }\n\n    if (globalState.isStart) {\n      if (_ref.read(runTimeProvider) == null) {\n        _ref.read(runTimeProvider.notifier).value = 0;\n      }\n      await globalState.startUpdateTasks([updateTraffic]);\n      return;\n    }\n\n    if (_ref.read(runTimeProvider) != null) {\n      _ref.read(runTimeProvider.notifier).value = null;\n    }\n    globalState.stopUpdateTasks();\n  }\n\n  Future<bool> _detectAbnormalExit() async {\n    final prefs = await preferences.sharedPreferencesCompleter.future;\n\n    if (system.isAndroid) {\n      final isVpnRunningFlag = prefs?.getBool('is_vpn_running') ?? false;\n      return !globalState.isStart && isVpnRunningFlag;\n    }\n\n    if (system.isDesktop) {\n      final wasTunRunning = prefs?.getBool('is_tun_running') ?? false;\n      return !globalState.isStart && wasTunRunning;\n    }\n\n    return false;\n  }\n\n  void setDelay(Delay delay) {\n    _ref.read(delayDataSourceProvider.notifier).setDelay(delay);\n  }\n\n  void toPage(PageLabel pageLabel) {\n    final context = globalState.navigatorKey.currentState?.context;\n    if (context != null && context.mounted) {\n      Navigator.of(context, rootNavigator: true).popUntil((route) => route.isFirst);\n    }\n    _ref.read(currentPageLabelProvider.notifier).value = pageLabel;\n  }\n\n  void toProfiles() {\n    toPage(PageLabel.profiles);\n  }\n\n  void initLink() {\n    linkManager.initAppLinksListen((url) async {\n      final res = await globalState.showMessage(\n        title: '${appLocalizations.add}${appLocalizations.profile}',\n        message: TextSpan(\n          children: [\n            TextSpan(text: appLocalizations.doYouWantToPass),\n            TextSpan(\n              text: ' $url ',\n              style: TextStyle(\n                color: Theme.of(context).colorScheme.primary,\n                decoration: TextDecoration.underline,\n                decorationColor: Theme.of(context).colorScheme.primary,\n              ),\n            ),\n            TextSpan(\n              text: '${appLocalizations.create}${appLocalizations.profile}',\n            ),\n          ],\n        ),\n      );\n\n      if (res != true) {\n        return;\n      }\n      addProfileFormURL(url);\n    });\n  }\n\n  Future<bool> showDisclaimer() async {\n    return await globalState.showCommonDialog<bool>(\n          dismissible: false,\n          child: CommonDialog(\n            title: appLocalizations.disclaimer,\n            actions: [\n              TextButton(\n                onPressed: () {\n                  Navigator.of(context).pop<bool>(false);\n                },\n                child: Text(appLocalizations.exit),\n              ),\n              TextButton(\n                onPressed: () {\n                  _ref\n                      .read(appSettingProvider.notifier)\n                      .updateState(\n                        (state) => state.copyWith(disclaimerAccepted: true),\n                      );\n                  Navigator.of(context).pop<bool>(true);\n                },\n                child: Text(appLocalizations.agree),\n              ),\n            ],\n            child: SelectableText(appLocalizations.disclaimerDesc),\n          ),\n        ) ??\n        false;\n  }\n\n  Future<void> _handlerDisclaimer() async {\n    if (_ref.read(appSettingProvider).disclaimerAccepted) {\n      return;\n    }\n    final isDisclaimerAccepted = await showDisclaimer();\n    if (!isDisclaimerAccepted) {\n      await handleExit();\n    }\n    return;\n  }\n\n  Future<void> addProfileFormURL(String url) async {\n    if (globalState.navigatorKey.currentState?.canPop() ?? false) {\n      globalState.navigatorKey.currentState?.popUntil((route) => route.isFirst);\n    }\n    toProfiles();\n\n    final profile = await safeRun(\n      () async {\n        return await Profile.normal(url: url).update();\n      },\n      needLoading: true,\n      title: '${appLocalizations.add}${appLocalizations.profile}',\n    );\n    if (profile != null) {\n      await addProfile(profile);\n    }\n  }\n\n  Future<void> addProfileFormFile() async {\n    final platformFile = await safeRun(picker.pickerFile);\n    final bytes = platformFile?.bytes;\n    if (bytes == null) {\n      return;\n    }\n    if (!context.mounted) return;\n    globalState.navigatorKey.currentState?.popUntil((route) => route.isFirst);\n    toProfiles();\n\n    final profile = await safeRun(\n      () async {\n        await Future.delayed(const Duration(milliseconds: 500));\n        return await Profile.normal(label: platformFile?.name).saveFile(bytes);\n      },\n      needLoading: true,\n      title: '${appLocalizations.add}${appLocalizations.profile}',\n    );\n    if (profile != null) {\n      await addProfile(profile);\n    }\n  }\n\n  Future<void> addProfileFormQrCode() async {\n    final url = await safeRun(picker.pickerConfigQRCode);\n    if (url == null) return;\n    addProfileFormURL(url);\n  }\n\n  void updateViewSize(Size size) {\n    WidgetsBinding.instance.addPostFrameCallback((_) {\n      _ref.read(viewSizeProvider.notifier).value = size;\n    });\n  }\n\n  void setProvider(ExternalProvider? provider) {\n    _ref.read(providersProvider.notifier).setProvider(provider);\n  }\n\n  List<Proxy> _sortOfName(List<Proxy> proxies) {\n    return List.of(proxies)..sort(\n      (a, b) =>\n          utils.sortByChar(utils.getPinyin(a.name), utils.getPinyin(b.name)),\n    );\n  }\n\n  int _delayValue(int? delay) =>\n      (delay == null || delay == -1) ? 1 << 30 : delay;\n\n  List<Proxy> _sortOfDelay({required List<Proxy> proxies, String? testUrl}) {\n    return List.of(proxies)..sort((a, b) {\n      final aDelay = _ref.read(\n        getDelayProvider(proxyName: a.name, testUrl: testUrl),\n      );\n      final bDelay = _ref.read(\n        getDelayProvider(proxyName: b.name, testUrl: testUrl),\n      );\n      return _delayValue(aDelay).compareTo(_delayValue(bDelay));\n    });\n  }\n\n  List<Proxy> getSortProxies({\n    required List<Proxy> proxies,\n    required ProxiesSortType sortType,\n    String? testUrl,\n  }) {\n    return switch (sortType) {\n      ProxiesSortType.none => proxies,\n      ProxiesSortType.delay => _sortOfDelay(proxies: proxies, testUrl: testUrl),\n      ProxiesSortType.name => _sortOfName(proxies),\n    };\n  }\n\n  Future<void> clearEffect(String profileId) async {\n    final profilePath = await appPath.getProfilePath(profileId);\n    final providersDirPath = await appPath.getProvidersDirPath(profileId);\n    await Isolate.run(() async {\n      final profileFile = File(profilePath);\n      final isExists = await profileFile.exists();\n      if (isExists) {\n        await profileFile.delete(recursive: true);\n      }\n      final providersFileDir = Directory(providersDirPath);\n      final providersFileIsExists = await providersFileDir.exists();\n      if (providersFileIsExists) {\n        await providersFileDir.delete(recursive: true);\n      }\n    });\n  }\n\n  void updateTun() {\n    _ref\n        .read(patchClashConfigProvider.notifier)\n        .updateState((state) => state.copyWith.tun(enable: !state.tun.enable));\n  }\n\n  void updateSystemProxy() {\n    _ref\n        .read(networkSettingProvider.notifier)\n        .updateState(\n          (state) => state.copyWith(systemProxy: !state.systemProxy),\n        );\n  }\n\n  Future<List<Package>> getPackages({bool forceRefresh = false}) async {\n    final cached = _ref.read(packagesProvider);\n    if (!forceRefresh && cached.isNotEmpty) return cached;\n\n    final packages = await app.getPackages(forceRefresh: forceRefresh);\n    _ref.read(packagesProvider.notifier).value = packages;\n    return packages;\n  }\n\n  void updateStart() {\n    updateStatus(!_ref.read(runTimeProvider.notifier).isStart);\n  }\n\n  void updateCurrentSelectedMap(String groupName, String proxyName) {\n    final currentProfile = _ref.read(currentProfileProvider);\n    if (currentProfile != null &&\n        currentProfile.selectedMap[groupName] != proxyName) {\n      final SelectedMap selectedMap = Map.from(currentProfile.selectedMap)\n        ..[groupName] = proxyName;\n      _ref\n          .read(profilesProvider.notifier)\n          .setProfile(currentProfile.copyWith(selectedMap: selectedMap));\n    }\n  }\n\n  void updateCurrentUnfoldSet(Set<String> value) {\n    final currentProfile = _ref.read(currentProfileProvider);\n    if (currentProfile == null) {\n      return;\n    }\n    _ref\n        .read(profilesProvider.notifier)\n        .setProfile(currentProfile.copyWith(unfoldSet: value));\n  }\n\n  void changeMode(Mode mode) {\n    _ref\n        .read(patchClashConfigProvider.notifier)\n        .updateState((state) => state.copyWith(mode: mode));\n    if (mode == Mode.global) {\n      updateCurrentGroupName(GroupName.GLOBAL.name);\n    }\n    updateGroupsDebounce();\n    addCheckIpNumDebounce();\n  }\n\n  void updateAutoLaunch() {\n    _ref\n        .read(appSettingProvider.notifier)\n        .updateState((state) => state.copyWith(autoLaunch: !state.autoLaunch));\n  }\n\n  Future<void> updateVisible() async {\n    final visible = await window?.isVisible;\n    if (visible != null && !visible) {\n      window?.show();\n    } else {\n      window?.hide();\n    }\n  }\n\n  void updateMode() {\n    _ref.read(patchClashConfigProvider.notifier).updateState((state) {\n      final index = Mode.values.indexWhere((item) => item == state.mode);\n      if (index == -1) {\n        return null;\n      }\n      final nextIndex = index + 1 > Mode.values.length - 1 ? 0 : index + 1;\n      return state.copyWith(mode: Mode.values[nextIndex]);\n    });\n  }\n\n  Future<void> handleAddOrUpdate(WidgetRef ref, [Rule? rule]) async {\n    final res = await globalState.showCommonDialog<Rule>(\n      child: AddRuleDialog(\n        rule: rule,\n        snippet: ref.read(\n          profileOverrideStateProvider.select((state) => state.snippet!),\n        ),\n      ),\n    );\n    if (res == null) {\n      return;\n    }\n    ref.read(profileOverrideStateProvider.notifier).updateState((state) {\n      final model = state.copyWith.overrideData!(\n        rule: state.overrideData!.rule.updateRules((rules) {\n          final index = rules.indexWhere((item) => item.id == res.id);\n          if (index == -1) {\n            return List.from([res, ...rules]);\n          }\n          return List.from(rules)..[index] = res;\n        }),\n      );\n      return model;\n    });\n  }\n\n  Future<bool> exportLogs() async {\n    final logsRaw = _ref.read(logsProvider).list.map((item) => item.toString());\n    final data = await Isolate.run<List<int>>(() async {\n      final logsRawString = logsRaw.join('\\n');\n      return utf8.encode(logsRawString);\n    });\n    return await picker.saveFile(utils.logFile, Uint8List.fromList(data)) !=\n        null;\n  }\n\n  Future<List<int>> backupData() async {\n    final homeDirPath = await appPath.homeDirPath;\n    final profilesPath = await appPath.profilesPath;\n    final configJson = globalState.config.toJson();\n\n    // Get valid profile IDs\n    final validProfileIds = globalState.config.profiles\n        .map((p) => p.id)\n        .toSet();\n    final currentProfileId = globalState.config.currentProfileId;\n\n    commonPrint.log(\n      'Starting backup: ${validProfileIds.length} profiles, current: $currentProfileId',\n    );\n\n    return Isolate.run<List<int>>(() async {\n      // Use ZipFileEncoder like FLClash - more reliable than ZipEncoder + Archive\n      final tempDir = Directory.systemTemp;\n      final tempZipPath = join(\n        tempDir.path,\n        'bettbox_backup_${DateTime.now().millisecondsSinceEpoch}.zip',\n      );\n      final encoder = ZipFileEncoder();\n      encoder.create(tempZipPath);\n\n      // Add marker file\n      final markerData = json.encode({\n        'app': 'Bettbox',\n        'version': '1.0',\n        'timestamp': DateTime.now().millisecondsSinceEpoch,\n      });\n      final markerBytes = utf8.encode(markerData);\n      final tempMarkerFile = File(\n        join(\n          tempDir.path,\n          'bettbox_marker_${DateTime.now().millisecondsSinceEpoch}.tmp',\n        ),\n      );\n      await tempMarkerFile.writeAsBytes(markerBytes);\n      await encoder.addFile(tempMarkerFile, '.bettbox_marker');\n      await tempMarkerFile.delete();\n\n      // Add config file\n      final configStr = json.encode(configJson);\n      final tempConfigFile = File(\n        join(\n          tempDir.path,\n          'bettbox_config_${DateTime.now().millisecondsSinceEpoch}.tmp',\n        ),\n      );\n      await tempConfigFile.writeAsString(configStr);\n      await encoder.addFile(tempConfigFile, 'config.json');\n      await tempConfigFile.delete();\n\n      // Add profiles dir (valid subscriptions only)\n      final profilesDir = Directory(profilesPath);\n      if (await profilesDir.exists()) {\n        final files = await profilesDir\n            .list(recursive: false)\n            .toList(); // First level only\n\n        for (final file in files) {\n          if (file is File) {\n            // Check if valid subscription config\n            final fileName = basename(file.path);\n            final profileId = fileName.replaceAll(RegExp(r'\\.(yaml|yml)$'), '');\n\n            if (validProfileIds.contains(profileId)) {\n              // Normalize path: use Unix-style / separator\n              final relativePath = relative(\n                file.path,\n                from: homeDirPath,\n              ).replaceAll('\\\\', '/');\n              await encoder.addFile(file, relativePath);\n            }\n          }\n        }\n\n        // Add current active subscription Providers\n        if (currentProfileId != null &&\n            validProfileIds.contains(currentProfileId)) {\n          final providersDir = Directory(\n            join(profilesPath, 'providers', currentProfileId),\n          );\n\n          if (await providersDir.exists()) {\n            final providerFiles = await providersDir\n                .list(recursive: true)\n                .toList();\n\n            for (final providerFile in providerFiles) {\n              if (providerFile is File) {\n                final relativePath = relative(\n                  providerFile.path,\n                  from: homeDirPath,\n                ).replaceAll('\\\\', '/');\n                await encoder.addFile(providerFile, relativePath);\n              }\n            }\n          }\n        }\n      }\n\n      encoder.close();\n\n      // Read the zip file and return bytes\n      final zipFile = File(tempZipPath);\n      final bytes = await zipFile.readAsBytes();\n      await zipFile.delete();\n      return bytes;\n    });\n  }\n\n  Future<void> updateTray([bool focus = false]) async {\n    final trayState = _ref.read(trayStateProvider);\n    await tray.update(trayState: trayState, focus: focus);\n  }\n\n  Future<void> _processRecoveryArchive(\n    Future<Archive> Function() getArchive,\n    RecoveryOption recoveryOption,\n  ) async {\n    try {\n      final archive = await getArchive();\n      commonPrint.log('Archive decoded: ${archive.files.length} files');\n      await _recoveryFromArchive(archive, recoveryOption);\n    } catch (e) {\n      commonPrint.log('Recovery failed: $e');\n      throw 'Backup file is corrupted or invalid: $e';\n    }\n  }\n\n  /// Restore data from bytes\n  Future<void> recoveryData(\n    List<int> data,\n    RecoveryOption recoveryOption,\n  ) async {\n    commonPrint.log('Starting recovery from bytes: ${data.length} bytes');\n    await _processRecoveryArchive(() => Isolate.run<Archive>(() {\n      final zipDecoder = ZipDecoder();\n      return zipDecoder.decodeBytes(data);\n    }), recoveryOption);\n  }\n\n  /// Restore data from file path\n  Future<void> recoveryDataFromFile(\n    String path,\n    RecoveryOption recoveryOption,\n  ) async {\n    commonPrint.log('Starting recovery from file: $path');\n    await _processRecoveryArchive(() => Isolate.run<Archive>(() {\n      try {\n        final input = InputFileStream(path);\n        final zipDecoder = ZipDecoder();\n        final result = zipDecoder.decodeStream(input);\n        input.close();\n        if (result.files.isNotEmpty) {\n          return result;\n        }\n      } catch (e) {\n        commonPrint.log('Stream decoding failed: $e');\n      }\n\n      final bytes = File(path).readAsBytesSync();\n      final zipDecoder = ZipDecoder();\n      return zipDecoder.decodeBytes(bytes);\n    }), recoveryOption);\n  }\n\n  /// Unified recovery entry: check marker and dispatch to recovery logic\n  Future<void> _recoveryFromArchive(\n    Archive archive,\n    RecoveryOption recoveryOption,\n  ) async {\n    if (archive.files.isEmpty) {\n      throw 'Backup file is empty or corrupted';\n    }\n\n    final homeDirPath = await appPath.homeDirPath;\n\n    // Check for Bettbox marker\n    final hasBettboxMarker = archive.files.any(\n      (file) => file.name == '.bettbox_marker',\n    );\n\n    if (hasBettboxMarker) {\n      // Bettbox backup\n      await _recoveryBettboxBackup(archive, recoveryOption, homeDirPath);\n    } else {\n      // Legacy backup\n      await _recoveryLegacyBackup(archive, recoveryOption, homeDirPath);\n    }\n  }\n\n  /// Restore Bettbox\n  Future<void> _recoveryBettboxBackup(\n    Archive archive,\n    RecoveryOption recoveryOption,\n    String homeDirPath,\n  ) async {\n    // Separate config and profile files\n    final configs = archive.files\n        .where(\n          (item) =>\n              item.name.endsWith('.json') && item.name != '.bettbox_marker',\n        )\n        .toList();\n    final profiles = archive.files.where(\n      (item) => !item.name.endsWith('.json') && item.name != '.bettbox_marker',\n    );\n\n    // Find config.json\n    final configIndex = configs.indexWhere(\n      (config) => config.name == 'config.json',\n    );\n    if (configIndex == -1) throw 'invalid backup file';\n\n    // Parse config\n    final configFile = configs[configIndex];\n    final configContent = configFile.content;\n    if (configContent.isEmpty) {\n      throw 'Config file is empty or corrupted';\n    }\n    var tempConfig = Config.compatibleFromJson(\n      json.decode(utf8.decode(configContent)),\n    );\n\n    // Restore profile files to disk\n    for (final profile in profiles) {\n      final filePath = join(homeDirPath, profile.name);\n      final file = File(filePath);\n      await file.create(recursive: true);\n      await file.writeAsBytes(profile.content);\n    }\n\n    // Apply recovery logic\n    _recovery(tempConfig, recoveryOption);\n  }\n\n  /// Restore legacy\n  Future<void> _recoveryLegacyBackup(\n    Archive archive,\n    RecoveryOption recoveryOption,\n    String homeDirPath,\n  ) async {\n    // Separate config and profile files\n    final configs = archive.files\n        .where((item) => item.name.endsWith('.json'))\n        .toList();\n    final profileFiles = archive.files\n        .where(\n          (item) =>\n              !item.name.endsWith('.json') && !item.name.endsWith('.sqlite'),\n        )\n        .toList();\n\n    // Find config.json\n    final configIndex = configs.indexWhere(\n      (config) => config.name == 'config.json',\n    );\n    if (configIndex == -1) throw 'invalid backup file';\n\n    // Parse backup config\n    final configFile = configs[configIndex];\n    final configContent = configFile.content;\n    if (configContent.isEmpty) {\n      throw 'Config file is empty or corrupted';\n    }\n    final backupConfig = Config.compatibleFromJson(\n      json.decode(utf8.decode(configContent)),\n    );\n\n    // Restore profile files to disk\n    for (final profile in profileFiles) {\n      final filePath = join(homeDirPath, profile.name);\n      final file = File(filePath);\n      await file.create(recursive: true);\n      await file.writeAsBytes(profile.content);\n    }\n\n    // Extract profiles from backup\n    List<Profile> profiles = [];\n    bool extractedFromDatabase = false;\n\n    // 1. Try SQLite database first (FlClash backup)\n    final dbFile = archive.files.firstWhereOrNull(\n      (file) => file.name.endsWith('database.sqlite'),\n    );\n\n    if (dbFile != null && dbFile.content.isNotEmpty) {\n      try {\n        // Save database temporarily\n        final tempDbPath = join(await appPath.tempPath, 'temp_flclash.db');\n        final tempDb = File(tempDbPath);\n        await tempDb.writeAsBytes(dbFile.content);\n\n        // Extract profiles from database\n        profiles = await FlClashDatabaseExtractor.extractProfiles(tempDbPath);\n        extractedFromDatabase = true;\n\n        // Clean up temp file\n        if (await tempDb.exists()) {\n          await tempDb.delete();\n        }\n\n        commonPrint.log(\n          'Extracted ${profiles.length} profiles from FlClash database',\n        );\n      } catch (e) {\n        commonPrint.log(\n          'Failed to extract from database, fallback to file names: $e',\n        );\n        profiles = [];\n        extractedFromDatabase = false;\n      }\n    }\n\n    // 2. Fallback if database extraction failed\n    if (profiles.isEmpty) {\n      // Get from config.json\n      if (backupConfig.profiles.isNotEmpty) {\n        profiles = backupConfig.profiles;\n      } else {\n        // Extract ID from profile file names (FlClash mode)\n        for (final profileFile in profileFiles) {\n          final fileName = profileFile.name.split('/').last;\n          if (fileName.endsWith('.yaml') || fileName.endsWith('.yml')) {\n            final id = fileName.replaceAll(RegExp(r'\\.(yaml|yml)$'), '');\n\n            // Try to extract friendly label from YAML\n            final label = await _extractLabelFromYaml(profileFile) ?? id;\n\n            // Create basic Profile object\n            profiles.add(\n              Profile(\n                id: id,\n                label: label,\n                autoUpdateDuration: defaultUpdateDuration,\n                url: '', // Mark empty, user needs to add\n              ),\n            );\n          }\n        }\n      }\n    }\n\n    // Create limited recovery config (subscriptions only)\n    Config limitedConfig = globalState.config.copyWith(profiles: profiles);\n\n    // Android: also restore app list\n    if (system.isAndroid) {\n      // FlClash uses accessControlProps instead of accessControl\n      final vpnProps = backupConfig.vpnProps;\n      AccessControl? accessControl;\n\n      // Try to get from vpnProps.accessControl\n      try {\n        accessControl = vpnProps.accessControl;\n      } catch (_) {\n        // Fallback: try accessControlProps from raw JSON\n        try {\n          final configJson = json.decode(utf8.decode(configFile.content));\n          final vpnPropsJson = configJson['vpnProps'];\n          if (vpnPropsJson != null && vpnPropsJson is Map) {\n            final accessControlPropsJson = vpnPropsJson['accessControlProps'];\n            if (accessControlPropsJson != null) {\n              accessControl = AccessControl.fromJson(\n                accessControlPropsJson as Map<String, dynamic>,\n              );\n            }\n          }\n        } catch (_) {}\n      }\n\n      if (accessControl != null) {\n        limitedConfig = limitedConfig.copyWith.vpnProps(\n          accessControl: accessControl,\n        );\n      }\n    }\n\n    // Apply limited recovery\n    _recoveryLimited(limitedConfig, recoveryOption);\n\n    // Show recovery result message\n    _showRecoveryResultMessage(profiles, extractedFromDatabase);\n  }\n\n  /// Extract label\n  Future<String?> _extractLabelFromYaml(ArchiveFile profileFile) async {\n    try {\n      final yamlContent = utf8.decode(profileFile.content);\n\n      // Try to extract from comments\n      final lines = yamlContent.split('\\n');\n      for (final line in lines) {\n        if (line.trim().startsWith('#')) {\n          final comment = line.trim().substring(1).trim();\n          if (comment.isNotEmpty &&\n              comment.length < 50 &&\n              !comment.startsWith('!')) {\n            return comment;\n          }\n        }\n      }\n\n      // Try to extract from first proxy name\n      final yamlMap = loadYaml(yamlContent);\n      if (yamlMap is Map && yamlMap['proxies'] is List) {\n        final proxies = yamlMap['proxies'] as List;\n        if (proxies.isNotEmpty && proxies[0] is Map) {\n          final firstProxy = proxies[0] as Map;\n          final name = firstProxy['name'];\n          if (name != null && name.toString().isNotEmpty) {\n            return 'Sub - $name';\n          }\n        }\n      }\n    } catch (e) {\n      commonPrint.log('Failed to extract label from YAML: $e');\n    }\n    return null;\n  }\n\n  /// Show results\n  void _showRecoveryResultMessage(\n    List<Profile> profiles,\n    bool extractedFromDatabase,\n  ) {\n    if (profiles.isEmpty) return;\n\n    final hasEmptyUrl = profiles.any((p) => p.url.isEmpty);\n\n    String message;\n    if (extractedFromDatabase) {\n      // Successfully extracted from database\n      message = 'Restored ${profiles.length} subscriptions with URLs.';\n    } else if (hasEmptyUrl) {\n      // Partial recovery, missing URLs\n      message =\n          'Restored ${profiles.length} subscriptions.\\n\\n'\n          'Warning: URLs not included. Edit subscriptions to add URLs for auto-update.';\n    } else {\n      // Complete recovery\n      message = 'Restored ${profiles.length} subscriptions.';\n    }\n\n    globalState.showMessage(\n      title: appLocalizations.recoverySuccess,\n      message: TextSpan(text: message),\n    );\n  }\n\n  void _restoreProfiles(List<Profile> profiles) {\n    final recoveryStrategy = _ref.read(\n      appSettingProvider.select((state) => state.recoveryStrategy),\n    );\n    if (recoveryStrategy == RecoveryStrategy.override) {\n      _ref.read(profilesProvider.notifier).value = profiles;\n    } else {\n      for (final profile in profiles) {\n        _ref.read(profilesProvider.notifier).setProfile(profile);\n      }\n    }\n  }\n\n  void _ensureCurrentProfile(List<Profile> profiles) {\n    final currentProfile = _ref.read(currentProfileProvider);\n    if (currentProfile == null && profiles.isNotEmpty) {\n      _ref.read(currentProfileIdProvider.notifier).value = profiles.first.id;\n    }\n  }\n\n  /// Partial restore\n  void _recoveryLimited(Config config, RecoveryOption recoveryOption) {\n    final profiles = config.profiles;\n\n    // Restore subscriptions\n    _restoreProfiles(profiles);\n\n    // Android: restore app list\n    if (system.isAndroid) {\n      _ref\n          .read(vpnSettingProvider.notifier)\n          .updateState(\n            (state) =>\n                state.copyWith(accessControl: config.vpnProps.accessControl),\n          );\n    }\n\n    // Ensure current profile exists\n    _ensureCurrentProfile(profiles);\n  }\n\n  /// Full restore\n  void _recovery(Config config, RecoveryOption recoveryOption) {\n    final profiles = config.profiles;\n\n    // Restore subscriptions\n    _restoreProfiles(profiles);\n\n    final onlyProfiles = recoveryOption == RecoveryOption.onlyProfiles;\n    if (!onlyProfiles) {\n      // Restore settings\n\n      // 1. Clash config\n      if (system.isDesktop) {\n        // Desktop: preserve current TUN state, avoid mobile backup override\n        final currentTunEnable = _ref.read(patchClashConfigProvider).tun.enable;\n        _ref.read(patchClashConfigProvider.notifier).value = config\n            .patchClashConfig\n            .copyWith\n            .tun(enable: currentTunEnable);\n      } else {\n        // Mobile: restore directly\n        _ref.read(patchClashConfigProvider.notifier).value =\n            config.patchClashConfig;\n      }\n\n      // 2. App settings\n      final currentAppSetting = _ref.read(appSettingProvider);\n      final backupAppSetting = config.appSetting;\n\n      // Merge dashboardWidgets: preserve platform-specific widgets\n      final currentWidgets = currentAppSetting.dashboardWidgets;\n      final backupWidgets = backupAppSetting.dashboardWidgets;\n      final mergedWidgets = _mergeDashboardWidgets(\n        currentWidgets,\n        backupWidgets,\n      );\n\n      _ref.read(appSettingProvider.notifier).value = backupAppSetting.copyWith(\n        dashboardWidgets: mergedWidgets,\n      );\n\n      // 3. Restore current profile ID\n      _ref.read(currentProfileIdProvider.notifier).value =\n          config.currentProfileId;\n\n      // 4. Restore WebDAV settings\n      _ref.read(appDAVSettingProvider.notifier).value = config.dav;\n\n      // 5. Restore theme settings\n      _ref.read(themeSettingProvider.notifier).value = config.themeProps;\n\n      // 6. Restore window settings (desktop only)\n      if (system.isDesktop) {\n        _ref.read(windowSettingProvider.notifier).value = config.windowProps;\n      }\n\n      // 7. VPN settings\n      if (system.isAndroid) {\n        // Android: restore VPN settings\n        _ref.read(vpnSettingProvider.notifier).value = config.vpnProps;\n      } else if (system.isDesktop) {\n        // Desktop: restore network settings, preserve TUN state\n        final currentVpnProps = _ref.read(vpnSettingProvider);\n        _ref.read(networkSettingProvider.notifier).value = config.networkProps;\n\n        // Only restore non-platform-specific VPN settings\n        _ref.read(vpnSettingProvider.notifier).value = config.vpnProps.copyWith(\n          enable: currentVpnProps.enable, // Preserve current TUN state\n        );\n      }\n\n      // 8. Restore proxy style\n      _ref.read(proxiesStyleSettingProvider.notifier).value =\n          config.proxiesStyle;\n\n      // 9. Restore DNS override settings\n      _ref.read(overrideDnsProvider.notifier).value = config.overrideDns;\n\n      // 10. Restore hotkey settings (desktop only)\n      if (system.isDesktop) {\n        _ref.read(hotKeyActionsProvider.notifier).value = config.hotKeyActions;\n      }\n\n      // 11. Restore script settings\n      _ref.read(scriptStateProvider.notifier).value = config.scriptProps;\n    }\n\n    // Ensure current profile exists\n    _ensureCurrentProfile(profiles);\n  }\n\n  /// Merge widgets\n  List<DashboardWidget> _mergeDashboardWidgets(\n    List<DashboardWidget> currentWidgets,\n    List<DashboardWidget> backupWidgets,\n  ) {\n    // Platform widgets\n    final Set<DashboardWidget> androidOnlyWidgets = {\n      // Android-specific widgets (if any)\n    };\n\n    final Set<DashboardWidget> desktopOnlyWidgets = {\n      DashboardWidget.tunButton, // TUN button (desktop-specific)\n      DashboardWidget\n          .systemProxyButton, // System proxy button (more common on desktop)\n    };\n\n    // Determine platform-specific widgets\n    final platformSpecificWidgets = system.isAndroid\n        ? androidOnlyWidgets\n        : desktopOnlyWidgets;\n\n    // Build position map for platform-specific widgets\n    final platformWidgetPositions = <DashboardWidget, int>{};\n    for (var i = 0; i < currentWidgets.length; i++) {\n      final widget = currentWidgets[i];\n      if (platformSpecificWidgets.contains(widget)) {\n        platformWidgetPositions[widget] = i;\n      }\n    }\n\n    // Get non-platform-specific widgets from backup\n    final backupCommonWidgets = backupWidgets\n        .where((widget) => !platformSpecificWidgets.contains(widget))\n        .toList();\n\n    // Merge strategy: insert platform-specific widgets at original positions\n    final mergedWidgets = <DashboardWidget>[...backupCommonWidgets];\n\n    // Insert platform-specific widgets by position (smallest first)\n    final sortedEntries = platformWidgetPositions.entries.toList()\n      ..sort((a, b) => a.value.compareTo(b.value));\n\n    for (final entry in sortedEntries) {\n      final widget = entry.key;\n      final originalPosition = entry.value;\n\n      // Insert position cannot exceed list length\n      final insertPosition = originalPosition.clamp(0, mergedWidgets.length);\n      mergedWidgets.insert(insertPosition, widget);\n    }\n\n    // Use default widgets if merged is empty\n    return mergedWidgets.isNotEmpty ? mergedWidgets : defaultDashboardWidgets;\n  }\n\n  /// Rollback\n  Future<void> _rollbackConfig() async {\n    final lastConfig = globalState.getLastSuccessfulConfig();\n    if (lastConfig == null) {\n      commonPrint.log('No backup config available for rollback');\n      return;\n    }\n\n    try {\n      commonPrint.log('Rolling back to last successful config');\n      await clashCore.setupConfig(lastConfig);\n      commonPrint.log('Config rollback successful');\n    } catch (e) {\n      commonPrint.log('Config rollback failed: $e');\n    }\n  }\n\n  Future<T?> safeRun<T>(\n    FutureOr<T> Function() futureFunction, {\n    String? title,\n    bool needLoading = false,\n    bool silence = true,\n  }) async {\n    final realSilence = needLoading == true ? true : silence;\n    try {\n      if (needLoading) {\n        _ref.read(loadingProvider.notifier).value = true;\n      }\n      final res = await futureFunction();\n      return res;\n    } catch (e) {\n      commonPrint.log('$e');\n      if (realSilence) {\n        globalState.showNotifier(e.toString());\n      } else {\n        globalState.showMessage(\n          title: title ?? appLocalizations.tip,\n          message: TextSpan(text: e.toString()),\n        );\n      }\n      return null;\n    } finally {\n      _ref.read(loadingProvider.notifier).value = false;\n    }\n  }\n}\n"
  },
  {
    "path": "lib/enum/enum.dart",
    "content": "// ignore_for_file: constant_identifier_names\n\nimport 'dart:io';\n\nimport 'package:bett_box/common/system.dart';\nimport 'package:bett_box/views/dashboard/widgets/widgets.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:freezed_annotation/freezed_annotation.dart';\nimport 'package:hotkey_manager/hotkey_manager.dart';\n\nenum SupportPlatform {\n  Windows,\n  MacOS,\n  Linux,\n  Android;\n\n  static SupportPlatform get currentPlatform {\n    if (system.isWindows) {\n      return SupportPlatform.Windows;\n    } else if (system.isMacOS) {\n      return SupportPlatform.MacOS;\n    } else if (Platform.isLinux) {\n      return SupportPlatform.Linux;\n    } else if (system.isAndroid) {\n      return SupportPlatform.Android;\n    }\n    throw 'invalid platform';\n  }\n}\n\nconst desktopPlatforms = [\n  SupportPlatform.Linux,\n  SupportPlatform.MacOS,\n  SupportPlatform.Windows,\n];\n\nenum GroupType {\n  Selector,\n  URLTest,\n  Fallback,\n  LoadBalance;\n\n  static GroupType parseProfileType(String type) {\n    return switch (type) {\n      'url-test' => URLTest,\n      'select' => Selector,\n      'fallback' => Fallback,\n      'load-balance' => LoadBalance,\n      String() => throw UnimplementedError(),\n    };\n  }\n}\n\nenum GroupName { GLOBAL, Proxy, Auto, Fallback }\n\nextension GroupTypeExtension on GroupType {\n  static List<String> get valueList =>\n      GroupType.values.map((e) => e.toString().split('.').last).toList();\n\n  bool get isComputedSelected {\n    return [GroupType.URLTest, GroupType.Fallback].contains(this);\n  }\n\n  static GroupType? getGroupType(String value) {\n    final index = GroupTypeExtension.valueList.indexOf(value);\n    if (index == -1) return null;\n    return GroupType.values[index];\n  }\n\n  String get value => GroupTypeExtension.valueList[index];\n}\n\nenum UsedProxy { GLOBAL, DIRECT, REJECT }\n\nextension UsedProxyExtension on UsedProxy {\n  static List<String> get valueList =>\n      UsedProxy.values.map((e) => e.toString().split('.').last).toList();\n\n  String get value => UsedProxyExtension.valueList[index];\n}\n\nenum Mode { rule, global, direct }\n\nenum IpClickBehavior { privacyProtection, manualRefresh, switchDomestic }\n\nenum ViewMode { mobile, laptop, desktop }\n\nenum LogLevel { debug, info, warning, error, silent }\n\nextension LogLevelExt on LogLevel {\n  Color? get color {\n    return switch (this) {\n      LogLevel.silent => Colors.grey.shade700,\n      LogLevel.debug => Colors.grey.shade400,\n      LogLevel.info => null,\n      LogLevel.warning => const Color.fromARGB(230, 255, 166, 0),\n      LogLevel.error => Colors.redAccent,\n    };\n  }\n}\n\nenum TransportProtocol { udp, tcp }\n\nenum TrafficUnit { B, KB, MB, GB, TB }\n\nenum NavigationItemMode { mobile, desktop, more }\n\nenum Network { tcp, udp }\n\nenum ProxiesSortType { none, delay, name }\n\nenum TunStack { gvisor, system, mixed }\n\nenum AccessControlMode { acceptSelected, rejectSelected }\n\nenum AccessSortType { none, name, time }\n\nenum ProfileType { file, url }\n\nenum ResultType {\n  @JsonValue(0)\n  success,\n  @JsonValue(-1)\n  error,\n}\n\nenum AppMessageType { log, delay, request, loaded }\n\nenum InvokeMessageType { protect, process }\n\nenum FindProcessMode { always, off }\n\nenum RecoveryOption { all, onlyProfiles }\n\nenum ChipType { action, delete }\n\nenum CommonCardType { plain, filled }\n//\n// extension CommonCardTypeExt on CommonCardType {\n//   CommonCardType get variant => CommonCardType.plain;\n// }\n\nenum ProxiesType { tab, list }\n\nenum ProxiesLayout { loose, standard, tight }\n\nenum ProxyCardType { expand, shrink, min }\n\nenum DnsMode {\n  normal,\n  @JsonValue('fake-ip')\n  fakeIp,\n  @JsonValue('redir-host')\n  redirHost,\n  hosts,\n}\n\nenum CacheAlgorithm { arc, lru }\n\nenum FilterMode { blacklist, whitelist, rule }\n\nenum ExternalControllerStatus {\n  @JsonValue('')\n  close(''),\n  @JsonValue('127.0.0.1:9090')\n  open('127.0.0.1:9090');\n\n  final String value;\n\n  const ExternalControllerStatus(this.value);\n}\n\nenum KeyboardModifier {\n  alt([PhysicalKeyboardKey.altLeft, PhysicalKeyboardKey.altRight]),\n  capsLock([PhysicalKeyboardKey.capsLock]),\n  control([PhysicalKeyboardKey.controlLeft, PhysicalKeyboardKey.controlRight]),\n  fn([PhysicalKeyboardKey.fn]),\n  meta([PhysicalKeyboardKey.metaLeft, PhysicalKeyboardKey.metaRight]),\n  shift([PhysicalKeyboardKey.shiftLeft, PhysicalKeyboardKey.shiftRight]);\n\n  final List<PhysicalKeyboardKey> physicalKeys;\n\n  const KeyboardModifier(this.physicalKeys);\n}\n\nextension KeyboardModifierExt on KeyboardModifier {\n  HotKeyModifier toHotKeyModifier() {\n    return switch (this) {\n      KeyboardModifier.alt => HotKeyModifier.alt,\n      KeyboardModifier.capsLock => HotKeyModifier.capsLock,\n      KeyboardModifier.control => HotKeyModifier.control,\n      KeyboardModifier.fn => HotKeyModifier.fn,\n      KeyboardModifier.meta => HotKeyModifier.meta,\n      KeyboardModifier.shift => HotKeyModifier.shift,\n    };\n  }\n}\n\nenum HotAction { start, view, mode, proxy, tun }\n\nenum ProxiesIconStyle { standard, none, icon }\n\nenum FontFamily {\n  twEmoji('Twemoji'),\n  jetBrainsMono('JetBrainsMono'),\n  icon('Icons');\n\n  final String value;\n\n  const FontFamily(this.value);\n}\n\nenum RouteMode { bypassPrivate, config }\n\nenum ActionMethod {\n  message,\n  initClash,\n  getIsInit,\n  forceGc,\n  shutdown,\n  validateConfig,\n  updateConfig,\n  getConfig,\n  getProxies,\n  changeProxy,\n  getTraffic,\n  getTotalTraffic,\n  resetTraffic,\n  asyncTestDelay,\n  getConnections,\n  closeConnections,\n  resetConnections,\n  closeConnection,\n  getExternalProviders,\n  getExternalProvider,\n  updateGeoData,\n  updateExternalProvider,\n  sideLoadExternalProvider,\n  startLog,\n  stopLog,\n  startListener,\n  stopListener,\n  getCountryCode,\n  getMemory,\n  crash,\n  setupConfig,\n  flushFakeIP,\n  flushDnsCache,\n\n  ///Android,\n  setState,\n  startTun,\n  stopTun,\n  getRunTime,\n  updateDns,\n  getAndroidVpnOptions,\n  getCurrentProfileName,\n}\n\nenum AuthorizeCode { none, success, error }\n\nenum WindowsHelperServiceStatus { none, presence, running }\n\nenum FunctionTag {\n  updateClashConfig,\n  setupClashConfig,\n  updateStatus,\n  updateGroups,\n  addCheckIpNum,\n  applyProfile,\n  savePreferences,\n  changeProxy,\n  checkIp,\n  handleWill,\n  updateDelay,\n  vpnTip,\n  autoLaunch,\n  renderPause,\n  updatePageIndex,\n  pageChange,\n  proxiesTabChange,\n  logs,\n  requests,\n  autoScrollToEnd,\n}\n\nenum DashboardWidget {\n  networkSpeed(GridItem(crossAxisCellCount: 8, child: NetworkSpeed())),\n  networkSpeedSmall(\n    GridItem(crossAxisCellCount: 4, child: NetworkSpeedSmall()),\n  ),\n  outboundModeV2(GridItem(crossAxisCellCount: 8, child: OutboundModeV2())),\n  outboundMode(GridItem(crossAxisCellCount: 4, child: OutboundMode())),\n  trafficUsage(GridItem(crossAxisCellCount: 4, child: TrafficUsage())),\n  networkDetection(GridItem(crossAxisCellCount: 4, child: NetworkDetection())),\n  tunButton(\n    GridItem(crossAxisCellCount: 4, child: TUNButton()),\n    platforms: desktopPlatforms,\n  ),\n  vpnButton(\n    GridItem(crossAxisCellCount: 4, child: VpnButton()),\n    platforms: [SupportPlatform.Android],\n  ),\n  systemProxyButton(\n    GridItem(crossAxisCellCount: 4, child: SystemProxyButton()),\n    platforms: desktopPlatforms,\n  ),\n  intranetIp(GridItem(crossAxisCellCount: 4, child: IntranetIP())),\n  memoryInfo(GridItem(crossAxisCellCount: 4, child: MemoryInfo())),\n  connectionsCount(GridItem(crossAxisCellCount: 4, child: ConnectionsCount())),\n  ipv6Switch(GridItem(crossAxisCellCount: 4, child: Ipv6Switch())),\n  wakelockSwitch(\n    GridItem(crossAxisCellCount: 4, child: WakelockSwitch()),\n    platforms: desktopPlatforms,\n  ),\n  dnsOverride(GridItem(crossAxisCellCount: 4, child: DnsOverride())),\n  snifferOverride(GridItem(crossAxisCellCount: 4, child: SnifferOverride())),\n  ntpOverride(GridItem(crossAxisCellCount: 4, child: NtpOverride())),\n  providersInfo(GridItem(crossAxisCellCount: 4, child: ProvidersInfo())),\n  fcmStatus(GridItem(crossAxisCellCount: 4, child: FcmStatus())),\n  onlinePanel(GridItem(crossAxisCellCount: 4, child: OnlinePanel())),\n  startButton(\n    GridItem(crossAxisCellCount: 4, isDeletable: false, child: StartButton()),\n  );\n\n  final GridItem widget;\n  final List<SupportPlatform> platforms;\n\n  const DashboardWidget(this.widget, {this.platforms = SupportPlatform.values});\n\n  static DashboardWidget getDashboardWidget(GridItem gridItem) {\n    final dashboardWidgets = DashboardWidget.values;\n    final index = dashboardWidgets.indexWhere(\n      (item) => item.widget == gridItem,\n    );\n    return dashboardWidgets[index];\n  }\n}\n\nenum GeodataLoader { standard, memconservative }\n\nenum PageLabel {\n  dashboard,\n  proxies,\n  profiles,\n  tools,\n  logs,\n  requests,\n  resources,\n  script,\n  connections,\n}\n\nenum RuleAction {\n  DOMAIN('DOMAIN'),\n  DOMAIN_SUFFIX('DOMAIN-SUFFIX'),\n  DOMAIN_KEYWORD('DOMAIN-KEYWORD'),\n  DOMAIN_REGEX('DOMAIN-REGEX'),\n  GEOSITE('GEOSITE'),\n  IP_CIDR('IP-CIDR'),\n  IP_CIDR6('IP-CIDR6'),\n  IP_SUFFIX('IP-SUFFIX'),\n  IP_ASN('IP-ASN'),\n  GEOIP('GEOIP'),\n  SRC_GEOIP('SRC-GEOIP'),\n  SRC_IP_ASN('SRC-IP-ASN'),\n  SRC_IP_CIDR('SRC-IP-CIDR'),\n  SRC_IP_SUFFIX('SRC-IP-SUFFIX'),\n  DST_PORT('DST-PORT'),\n  SRC_PORT('SRC-PORT'),\n  IN_PORT('IN-PORT'),\n  IN_TYPE('IN-TYPE'),\n  IN_USER('IN-USER'),\n  IN_NAME('IN-NAME'),\n  PROCESS_PATH('PROCESS-PATH'),\n  PROCESS_PATH_REGEX('PROCESS-PATH-REGEX'),\n  PROCESS_NAME('PROCESS-NAME'),\n  PROCESS_NAME_REGEX('PROCESS-NAME-REGEX'),\n  UID('UID'),\n  NETWORK('NETWORK'),\n  DSCP('DSCP'),\n  RULE_SET('RULE-SET'),\n  AND('AND'),\n  OR('OR'),\n  NOT('NOT'),\n  SUB_RULE('SUB-RULE'),\n  MATCH('MATCH');\n\n  final String value;\n\n  const RuleAction(this.value);\n}\n\nextension RuleActionExt on RuleAction {\n  bool get hasParams => [\n    RuleAction.GEOIP,\n    RuleAction.IP_ASN,\n    RuleAction.SRC_IP_ASN,\n    RuleAction.IP_CIDR,\n    RuleAction.IP_CIDR6,\n    RuleAction.IP_SUFFIX,\n    RuleAction.RULE_SET,\n  ].contains(this);\n}\n\nenum OverrideRuleType { override, added }\n\nenum RuleTarget { DIRECT, REJECT }\n\nenum RecoveryStrategy { compatible, override }\n\nenum CacheTag { logs, rules, requests, proxiesList }\n\nenum Language { yaml, javaScript }\n\nenum ImportOption { code, url, file }\n\nenum ScrollPositionCacheKeys { tools, profiles, proxiesList, proxiesTabList }\n\nenum DelayAnimationType {\n  none,\n  rotatingCircle,\n  pulse,\n  spinningLines,\n  threeInOut,\n  threeBounce,\n  circle,\n  fadingCircle,\n  fadingFour,\n  wave,\n  doubleBounce,\n}\n"
  },
  {
    "path": "lib/l10n/intl/messages_all.dart",
    "content": "// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart\n// This is a library that looks up messages for specific locales by\n// delegating to the appropriate library.\n\n// Ignore issues from commonly used lints in this file.\n// ignore_for_file:implementation_imports, file_names, unnecessary_new\n// ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering\n// ignore_for_file:argument_type_not_assignable, invalid_assignment\n// ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases\n// ignore_for_file:comment_references\n\nimport 'dart:async';\n\nimport 'package:flutter/foundation.dart';\nimport 'package:intl/intl.dart';\nimport 'package:intl/message_lookup_by_library.dart';\nimport 'package:intl/src/intl_helpers.dart';\n\nimport 'messages_en.dart' as messages_en;\nimport 'messages_ru.dart' as messages_ru;\nimport 'messages_zh_CN.dart' as messages_zh_cn;\nimport 'messages_zh_TC.dart' as messages_zh_tc;\n\ntypedef Future<dynamic> LibraryLoader();\nMap<String, LibraryLoader> _deferredLibraries = {\n  'en': () => new SynchronousFuture(null),\n  'ru': () => new SynchronousFuture(null),\n  'zh_CN': () => new SynchronousFuture(null),\n  'zh_TC': () => new SynchronousFuture(null),\n};\n\nMessageLookupByLibrary? _findExact(String localeName) {\n  switch (localeName) {\n    case 'en':\n      return messages_en.messages;\n    case 'ru':\n      return messages_ru.messages;\n    case 'zh_CN':\n      return messages_zh_cn.messages;\n    case 'zh_TC':\n      return messages_zh_tc.messages;\n    default:\n      return null;\n  }\n}\n\n/// User programs should call this before using [localeName] for messages.\nFuture<bool> initializeMessages(String localeName) {\n  var availableLocale = Intl.verifiedLocale(\n    localeName,\n    (locale) => _deferredLibraries[locale] != null,\n    onFailure: (_) => null,\n  );\n  if (availableLocale == null) {\n    return new SynchronousFuture(false);\n  }\n  var lib = _deferredLibraries[availableLocale];\n  lib == null ? new SynchronousFuture(false) : lib();\n  initializeInternalMessageLookup(() => new CompositeMessageLookup());\n  messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor);\n  return new SynchronousFuture(true);\n}\n\nbool _messagesExistFor(String locale) {\n  try {\n    return _findExact(locale) != null;\n  } catch (e) {\n    return false;\n  }\n}\n\nMessageLookupByLibrary? _findGeneratedMessagesFor(String locale) {\n  var actualLocale = Intl.verifiedLocale(\n    locale,\n    _messagesExistFor,\n    onFailure: (_) => null,\n  );\n  if (actualLocale == null) return null;\n  return _findExact(actualLocale);\n}\n"
  },
  {
    "path": "lib/l10n/intl/messages_en.dart",
    "content": "// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart\n// This is a library that provides messages for a en locale. All the\n// messages from the main program should be duplicated here with the same\n// function name.\n\n// Ignore issues from commonly used lints in this file.\n// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new\n// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering\n// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases\n// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes\n// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes\n\nimport 'package:intl/intl.dart';\nimport 'package:intl/message_lookup_by_library.dart';\n\nfinal messages = new MessageLookup();\n\ntypedef String MessageIfAbsent(String messageStr, List<dynamic> args);\n\nclass MessageLookup extends MessageLookupByLibrary {\n  String get localeName => 'en';\n\n  static String m0(label) => \"Delete selected ${label}?\";\n\n  static String m1(label) => \"Delete current ${label}?\";\n\n  static String m2(label) => \"${label} Details\";\n\n  static String m3(label) => \"${label} cannot be empty\";\n\n  static String m4(label) => \"${label} already exists\";\n\n  static String m5(label) => \"No ${label}\";\n\n  static String m6(label) => \"${label} must be a number\";\n\n  static String m7(label) => \"${label} must be between 1024 and 49151\";\n\n  static String m8(count) => \"${count} items selected\";\n\n  static String m9(label) => \"${label} must be a URL\";\n\n  final messages = _notInlinedMessages(_notInlinedMessages);\n  static Map<String, Function> _notInlinedMessages(_) => <String, Function>{\n    \"about\": MessageLookupByLibrary.simpleMessage(\"About\"),\n    \"accessControl\": MessageLookupByLibrary.simpleMessage(\"Access Control\"),\n    \"accessControlAllowDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Only route selected apps through VPN\",\n    ),\n    \"accessControlDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Configure per-app proxy access\",\n    ),\n    \"accessControlNotAllowDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Exclude selected apps from VPN\",\n    ),\n    \"account\": MessageLookupByLibrary.simpleMessage(\"Account\"),\n    \"action\": MessageLookupByLibrary.simpleMessage(\"Action\"),\n    \"action_mode\": MessageLookupByLibrary.simpleMessage(\"Switch Mode\"),\n    \"action_proxy\": MessageLookupByLibrary.simpleMessage(\"System Proxy\"),\n    \"action_start\": MessageLookupByLibrary.simpleMessage(\"Start/Stop\"),\n    \"action_tun\": MessageLookupByLibrary.simpleMessage(\"TUN\"),\n    \"action_view\": MessageLookupByLibrary.simpleMessage(\"Show/Hide\"),\n    \"add\": MessageLookupByLibrary.simpleMessage(\"Add\"),\n    \"addProfile\": MessageLookupByLibrary.simpleMessage(\"Add Profile\"),\n    \"addRule\": MessageLookupByLibrary.simpleMessage(\"Add Rule\"),\n    \"addTunnel\": MessageLookupByLibrary.simpleMessage(\"Add Forwarding\"),\n    \"addedOriginRules\": MessageLookupByLibrary.simpleMessage(\n      \"Append to Original Rules\",\n    ),\n    \"address\": MessageLookupByLibrary.simpleMessage(\"Address\"),\n    \"addressHelp\": MessageLookupByLibrary.simpleMessage(\n      \"WebDAV server address\",\n    ),\n    \"addressTip\": MessageLookupByLibrary.simpleMessage(\n      \"Please enter a valid WebDAV address\",\n    ),\n    \"adminAutoLaunch\": MessageLookupByLibrary.simpleMessage(\n      \"Admin Auto-Launch\",\n    ),\n    \"adminAutoLaunchDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Auto-start with admin privileges\",\n    ),\n    \"advancedSettings\": MessageLookupByLibrary.simpleMessage(\n      \"Advanced Settings\",\n    ),\n    \"ago\": MessageLookupByLibrary.simpleMessage(\" Ago\"),\n    \"agree\": MessageLookupByLibrary.simpleMessage(\"Agree\"),\n    \"allApps\": MessageLookupByLibrary.simpleMessage(\"All Apps\"),\n    \"allowBypass\": MessageLookupByLibrary.simpleMessage(\"Allow Bypassing VPN\"),\n    \"allowBypassDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Allow specific apps to bypass VPN\",\n    ),\n    \"allowLan\": MessageLookupByLibrary.simpleMessage(\"Allow LAN\"),\n    \"allowLanDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Allow LAN access to proxy\",\n    ),\n    \"alreadyInWhitelist\": MessageLookupByLibrary.simpleMessage(\n      \"Already in whitelist\",\n    ),\n    \"app\": MessageLookupByLibrary.simpleMessage(\"App\"),\n    \"appAccessControl\": MessageLookupByLibrary.simpleMessage(\n      \"App Access Control\",\n    ),\n    \"appDesc\": MessageLookupByLibrary.simpleMessage(\"App-related settings\"),\n    \"application\": MessageLookupByLibrary.simpleMessage(\"Application\"),\n    \"applicationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Modify application settings\",\n    ),\n    \"auto\": MessageLookupByLibrary.simpleMessage(\"Auto\"),\n    \"autoCheckUpdate\": MessageLookupByLibrary.simpleMessage(\n      \"Auto Check Updates\",\n    ),\n    \"autoCheckUpdateDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Check updates on app launch\",\n    ),\n    \"autoCloseConnections\": MessageLookupByLibrary.simpleMessage(\n      \"Auto-Close Connections\",\n    ),\n    \"autoCloseConnectionsDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Close connections when switching nodes\",\n    ),\n    \"autoLaunch\": MessageLookupByLibrary.simpleMessage(\"Auto Launch\"),\n    \"autoLaunchDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Launch on system startup\",\n    ),\n    \"autoRun\": MessageLookupByLibrary.simpleMessage(\"Auto Run\"),\n    \"autoRunDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Connect on app launch\",\n    ),\n    \"autoSetSystemDns\": MessageLookupByLibrary.simpleMessage(\n      \"Auto Set System DNS\",\n    ),\n    \"autoUpdate\": MessageLookupByLibrary.simpleMessage(\"Auto Update\"),\n    \"autoUpdateInterval\": MessageLookupByLibrary.simpleMessage(\n      \"Auto update interval (min)\",\n    ),\n    \"backup\": MessageLookupByLibrary.simpleMessage(\"Backup\"),\n    \"backupAndRecovery\": MessageLookupByLibrary.simpleMessage(\n      \"Backup & Restore\",\n    ),\n    \"backupAndRecoveryDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Sync data via WebDAV or local files\",\n    ),\n    \"backupSuccess\": MessageLookupByLibrary.simpleMessage(\"Backup Successful\"),\n    \"basicConfig\": MessageLookupByLibrary.simpleMessage(\"Core Configuration\"),\n    \"basicConfigDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Global core settings\",\n    ),\n    \"batteryOptimization\": MessageLookupByLibrary.simpleMessage(\n      \"Battery Optimization\",\n    ),\n    \"batteryOptimizationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Request battery optimization whitelist\",\n    ),\n    \"bind\": MessageLookupByLibrary.simpleMessage(\"Bind\"),\n    \"blacklist\": MessageLookupByLibrary.simpleMessage(\"Blacklist\"),\n    \"blacklistMode\": MessageLookupByLibrary.simpleMessage(\"Blacklist Mode\"),\n    \"bypassDomain\": MessageLookupByLibrary.simpleMessage(\"Bypass Domain\"),\n    \"bypassDomainDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Active only when System Proxy is on\",\n    ),\n    \"bypassPrivateRoute\": MessageLookupByLibrary.simpleMessage(\n      \"Bypass Private Network\",\n    ),\n    \"bypassPrivateRouteDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Automatically bypass private network IP addresses\",\n    ),\n    \"cacheAlgorithm\": MessageLookupByLibrary.simpleMessage(\"Cache Algorithm\"),\n    \"cacheCorrupt\": MessageLookupByLibrary.simpleMessage(\n      \"Cache corrupted. Clear it?\",\n    ),\n    \"cameraPermissionDenied\": MessageLookupByLibrary.simpleMessage(\n      \"Camera Permission Denied\",\n    ),\n    \"cameraPermissionDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Camera permission is required to scan QR codes. Please grant it in settings.\",\n    ),\n    \"cancel\": MessageLookupByLibrary.simpleMessage(\"Cancel\"),\n    \"cancelFilterSystemApp\": MessageLookupByLibrary.simpleMessage(\n      \"Show System Apps\",\n    ),\n    \"cancelSelectAll\": MessageLookupByLibrary.simpleMessage(\"Deselect All\"),\n    \"checkError\": MessageLookupByLibrary.simpleMessage(\"Check Failed\"),\n    \"checkOrAddProfile\": MessageLookupByLibrary.simpleMessage(\n      \"Please add a profile first\",\n    ),\n    \"checkUpdate\": MessageLookupByLibrary.simpleMessage(\"Check for Updates\"),\n    \"checkUpdateError\": MessageLookupByLibrary.simpleMessage(\n      \"Already on the latest version\",\n    ),\n    \"checking\": MessageLookupByLibrary.simpleMessage(\"Checking...\"),\n    \"circle\": MessageLookupByLibrary.simpleMessage(\"Circle\"),\n    \"clearCacheDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Clear FakeIP and DNS cache?\",\n    ),\n    \"clearCacheTitle\": MessageLookupByLibrary.simpleMessage(\"Clear Cache\"),\n    \"clearData\": MessageLookupByLibrary.simpleMessage(\"Clear Data\"),\n    \"clipboard\": MessageLookupByLibrary.simpleMessage(\"Clipboard\"),\n    \"clipboardDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Get profile link from clipboard\",\n    ),\n    \"clipboardExport\": MessageLookupByLibrary.simpleMessage(\n      \"Export to Clipboard\",\n    ),\n    \"clipboardImport\": MessageLookupByLibrary.simpleMessage(\n      \"Import from Clipboard\",\n    ),\n    \"color\": MessageLookupByLibrary.simpleMessage(\"Color\"),\n    \"colorSchemes\": MessageLookupByLibrary.simpleMessage(\"Color Schemes\"),\n    \"columns\": MessageLookupByLibrary.simpleMessage(\"Columns\"),\n    \"compatible\": MessageLookupByLibrary.simpleMessage(\"Compatible Mode\"),\n    \"compatibleDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Reduces some features for full Clash compatibility\",\n    ),\n    \"concurrencyLimit\": MessageLookupByLibrary.simpleMessage(\n      \"Concurrency Limit\",\n    ),\n    \"concurrencyLimitDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Maximum concurrent delay tests\",\n    ),\n    \"confirm\": MessageLookupByLibrary.simpleMessage(\"Confirm\"),\n    \"connection\": MessageLookupByLibrary.simpleMessage(\"Active Connections\"),\n    \"connections\": MessageLookupByLibrary.simpleMessage(\"Connections\"),\n    \"connectionsDesc\": MessageLookupByLibrary.simpleMessage(\n      \"View active connections\",\n    ),\n    \"connectivity\": MessageLookupByLibrary.simpleMessage(\"Connectivity:\"),\n    \"contactMe\": MessageLookupByLibrary.simpleMessage(\"Contact Me\"),\n    \"content\": MessageLookupByLibrary.simpleMessage(\"Content\"),\n    \"contentScheme\": MessageLookupByLibrary.simpleMessage(\"Content\"),\n    \"controlSecret\": MessageLookupByLibrary.simpleMessage(\"Control Secret\"),\n    \"controlSecretDesc\": MessageLookupByLibrary.simpleMessage(\n      \"RESTful API access password\",\n    ),\n    \"copy\": MessageLookupByLibrary.simpleMessage(\"Copy\"),\n    \"copyEnvVar\": MessageLookupByLibrary.simpleMessage(\n      \"Copy Environment Variable\",\n    ),\n    \"copyLink\": MessageLookupByLibrary.simpleMessage(\"Copy Link\"),\n    \"copySuccess\": MessageLookupByLibrary.simpleMessage(\"Copy Successful\"),\n    \"core\": MessageLookupByLibrary.simpleMessage(\"Core\"),\n    \"coreConnected\": MessageLookupByLibrary.simpleMessage(\"Connected\"),\n    \"coreInfo\": MessageLookupByLibrary.simpleMessage(\"Core Info\"),\n    \"coreSuspended\": MessageLookupByLibrary.simpleMessage(\"Suspended\"),\n    \"country\": MessageLookupByLibrary.simpleMessage(\"Country\"),\n    \"crashTest\": MessageLookupByLibrary.simpleMessage(\"Crash Test\"),\n    \"create\": MessageLookupByLibrary.simpleMessage(\"Create\"),\n    \"creationTime\": MessageLookupByLibrary.simpleMessage(\"Creation Time\"),\n    \"custom\": MessageLookupByLibrary.simpleMessage(\"Custom\"),\n    \"customUrl\": MessageLookupByLibrary.simpleMessage(\"Custom URL\"),\n    \"cut\": MessageLookupByLibrary.simpleMessage(\"Cut\"),\n    \"dark\": MessageLookupByLibrary.simpleMessage(\"Dark\"),\n    \"dashboard\": MessageLookupByLibrary.simpleMessage(\"Dashboard\"),\n    \"days\": MessageLookupByLibrary.simpleMessage(\"Days\"),\n    \"defaultNameserver\": MessageLookupByLibrary.simpleMessage(\n      \"Default Nameserver\",\n    ),\n    \"defaultNameserverDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Used to resolve DNS servers\",\n    ),\n    \"defaultSort\": MessageLookupByLibrary.simpleMessage(\"Default Sort\"),\n    \"defaultText\": MessageLookupByLibrary.simpleMessage(\"Default\"),\n    \"delay\": MessageLookupByLibrary.simpleMessage(\"Delay\"),\n    \"delayAnimation\": MessageLookupByLibrary.simpleMessage(\"Delay Animation\"),\n    \"delayAnimationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Customize animation during delay testing\",\n    ),\n    \"delaySort\": MessageLookupByLibrary.simpleMessage(\"Sort by Delay\"),\n    \"delete\": MessageLookupByLibrary.simpleMessage(\"Delete\"),\n    \"deleteMultipTip\": m0,\n    \"deleteTip\": m1,\n    \"deleteTunnel\": MessageLookupByLibrary.simpleMessage(\"Delete Forwarding\"),\n    \"desc\": MessageLookupByLibrary.simpleMessage(\n      \"Bettbox is based on the powerful and flexible Mihomo (Clash.Meta) proxy kernel, dedicated to a superior user experience. Forked from FlClash: Better Experience, Out of the box\",\n    ),\n    \"destination\": MessageLookupByLibrary.simpleMessage(\"Destination\"),\n    \"destinationGeoIP\": MessageLookupByLibrary.simpleMessage(\n      \"Destination GeoIP\",\n    ),\n    \"destinationIPASN\": MessageLookupByLibrary.simpleMessage(\n      \"Destination IP ASN\",\n    ),\n    \"details\": m2,\n    \"detectionTip\": MessageLookupByLibrary.simpleMessage(\n      \"Third-party API result (for reference only)\",\n    ),\n    \"developerMode\": MessageLookupByLibrary.simpleMessage(\"Developer Mode\"),\n    \"developerModeEnableTip\": MessageLookupByLibrary.simpleMessage(\n      \"Developer mode is enabled.\",\n    ),\n    \"dialerIp4pConvert\": MessageLookupByLibrary.simpleMessage(\n      \"Enable Dialer IP4P Conversion\",\n    ),\n    \"dialerIp4pConvertDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Enable dialer IP4P address conversion feature\",\n    ),\n    \"direct\": MessageLookupByLibrary.simpleMessage(\"Direct\"),\n    \"directNameserver\": MessageLookupByLibrary.simpleMessage(\n      \"Direct Nameserver\",\n    ),\n    \"directNameserverDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Used to resolve direct domains\",\n    ),\n    \"directNameserverFollowPolicy\": MessageLookupByLibrary.simpleMessage(\n      \"Direct DNS Follows Policy\",\n    ),\n    \"disableQuic\": MessageLookupByLibrary.simpleMessage(\"Disable QUIC\"),\n    \"disableQuicDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Disable QUIC to resolve specific network issues\",\n    ),\n    \"disclaimer\": MessageLookupByLibrary.simpleMessage(\"Disclaimer\"),\n    \"disclaimerDesc\": MessageLookupByLibrary.simpleMessage(\n      \"This free open-source software is for non-commercial learning and personal use only. Proxy services are independent of this software. By agreeing, you acknowledge this; otherwise, please exit.\",\n    ),\n    \"discoverNewVersion\": MessageLookupByLibrary.simpleMessage(\n      \"New Version Available\",\n    ),\n    \"discovery\": MessageLookupByLibrary.simpleMessage(\"New Version Found\"),\n    \"dnsDesc\": MessageLookupByLibrary.simpleMessage(\"DNS-related settings\"),\n    \"dnsHijack\": MessageLookupByLibrary.simpleMessage(\"DNS Hijack\"),\n    \"dnsHijackDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Redirect DNS queries to internal DNS module\",\n    ),\n    \"dnsMode\": MessageLookupByLibrary.simpleMessage(\"DNS Mode\"),\n    \"doYouWantToPass\": MessageLookupByLibrary.simpleMessage(\n      \"Do you want to pass\",\n    ),\n    \"domain\": MessageLookupByLibrary.simpleMessage(\"Domain\"),\n    \"doubleBounce\": MessageLookupByLibrary.simpleMessage(\"Double Bounce\"),\n    \"download\": MessageLookupByLibrary.simpleMessage(\"Download\"),\n    \"dozeSuspend\": MessageLookupByLibrary.simpleMessage(\"Doze Support\"),\n    \"dozeSuspendDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Sync with system Doze mode\",\n    ),\n    \"edit\": MessageLookupByLibrary.simpleMessage(\"Edit\"),\n    \"editTunnel\": MessageLookupByLibrary.simpleMessage(\"Edit Forwarding\"),\n    \"emptyTip\": m3,\n    \"en\": MessageLookupByLibrary.simpleMessage(\"English\"),\n    \"enableCrashReport\": MessageLookupByLibrary.simpleMessage(\n      \"Crash Analytics\",\n    ),\n    \"enableCrashReportDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Upload crash logs when needed\",\n    ),\n    \"enableOverride\": MessageLookupByLibrary.simpleMessage(\"Enable Override\"),\n    \"endpointIndependentNat\": MessageLookupByLibrary.simpleMessage(\n      \"NAT Enhancement\",\n    ),\n    \"endpointIndependentNatDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Enable endpoint-independent NAT\",\n    ),\n    \"entries\": MessageLookupByLibrary.simpleMessage(\" entries\"),\n    \"exclude\": MessageLookupByLibrary.simpleMessage(\"Hide from Recents\"),\n    \"excludeChina\": MessageLookupByLibrary.simpleMessage(\"Exclude China\"),\n    \"excludeChinaDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Allow China QUIC traffic instead of blocking all\",\n    ),\n    \"excludeDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Hide app from recent tasks list\",\n    ),\n    \"existsTip\": m4,\n    \"exit\": MessageLookupByLibrary.simpleMessage(\"Exit\"),\n    \"expand\": MessageLookupByLibrary.simpleMessage(\"Standard\"),\n    \"experimental\": MessageLookupByLibrary.simpleMessage(\"Experimental\"),\n    \"experimentalDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Use with caution\",\n    ),\n    \"expirationTime\": MessageLookupByLibrary.simpleMessage(\"Expiration Time\"),\n    \"exportFile\": MessageLookupByLibrary.simpleMessage(\"Export File\"),\n    \"exportLogs\": MessageLookupByLibrary.simpleMessage(\"Export Logs\"),\n    \"exportSuccess\": MessageLookupByLibrary.simpleMessage(\"Export Successful\"),\n    \"expressiveScheme\": MessageLookupByLibrary.simpleMessage(\"Expressive\"),\n    \"externalController\": MessageLookupByLibrary.simpleMessage(\n      \"External Controller\",\n    ),\n    \"externalControllerDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Control core via online port\",\n    ),\n    \"externalLink\": MessageLookupByLibrary.simpleMessage(\"External Link\"),\n    \"externalResources\": MessageLookupByLibrary.simpleMessage(\n      \"External Resources\",\n    ),\n    \"fadingCircle\": MessageLookupByLibrary.simpleMessage(\"Fading Circle\"),\n    \"fadingFour\": MessageLookupByLibrary.simpleMessage(\"Fading Four\"),\n    \"fakeIpFilterMode\": MessageLookupByLibrary.simpleMessage(\n      \"FakeIP Filter Mode\",\n    ),\n    \"fakeIpFilterModeDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Specify FakeIP filter mode\",\n    ),\n    \"fakeipFilter\": MessageLookupByLibrary.simpleMessage(\"FakeIP Filter\"),\n    \"fakeipRange\": MessageLookupByLibrary.simpleMessage(\"FakeIP Range\"),\n    \"fakeipRangeV6\": MessageLookupByLibrary.simpleMessage(\"FakeIPv6 Range\"),\n    \"fakeipTtl\": MessageLookupByLibrary.simpleMessage(\"FakeIP TTL\"),\n    \"fallback\": MessageLookupByLibrary.simpleMessage(\"Fallback\"),\n    \"fallbackDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Usually offshore DNS\",\n    ),\n    \"fallbackFilter\": MessageLookupByLibrary.simpleMessage(\"Fallback Filter\"),\n    \"fcmOptimization\": MessageLookupByLibrary.simpleMessage(\"FCM Optimization\"),\n    \"fcmOptimizationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Enhance FCM connection stability\",\n    ),\n    \"fcmTip\": MessageLookupByLibrary.simpleMessage(\n      \"FCM support depends on your device; results are for reference. Disable \\'Allow Bypass VPN\\' in network settings for accurate results.\",\n    ),\n    \"fidelityScheme\": MessageLookupByLibrary.simpleMessage(\"Fidelity\"),\n    \"file\": MessageLookupByLibrary.simpleMessage(\"File\"),\n    \"fileDesc\": MessageLookupByLibrary.simpleMessage(\"Upload profile file\"),\n    \"fileIsUpdate\": MessageLookupByLibrary.simpleMessage(\n      \"File modified. Save changes?\",\n    ),\n    \"filterSystemApp\": MessageLookupByLibrary.simpleMessage(\n      \"Filter System Apps\",\n    ),\n    \"findProcessMode\": MessageLookupByLibrary.simpleMessage(\"Find Process\"),\n    \"findProcessModeDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Enable process matching\",\n    ),\n    \"fontFamily\": MessageLookupByLibrary.simpleMessage(\"Font\"),\n    \"forceDnsMapping\": MessageLookupByLibrary.simpleMessage(\n      \"Force DNS Mapping\",\n    ),\n    \"forceDnsMappingDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Force mapping DNS results to connections\",\n    ),\n    \"forceDomain\": MessageLookupByLibrary.simpleMessage(\"Force Sniff Domain\"),\n    \"forceGCDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Force kernel garbage collection? Experimental, use with caution.\",\n    ),\n    \"forceGCTitle\": MessageLookupByLibrary.simpleMessage(\n      \"Force Garbage Collection\",\n    ),\n    \"formatError\": MessageLookupByLibrary.simpleMessage(\n      \"Please check the format\",\n    ),\n    \"fourColumns\": MessageLookupByLibrary.simpleMessage(\"4 Columns\"),\n    \"fruitSaladScheme\": MessageLookupByLibrary.simpleMessage(\"Fruit Salad\"),\n    \"general\": MessageLookupByLibrary.simpleMessage(\"General\"),\n    \"generalDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Modify general settings\",\n    ),\n    \"generateSecret\": MessageLookupByLibrary.simpleMessage(\"Generate\"),\n    \"geoData\": MessageLookupByLibrary.simpleMessage(\"GeoData\"),\n    \"geodataLoader\": MessageLookupByLibrary.simpleMessage(\"GEO Low Memory\"),\n    \"geodataLoaderDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Use GEO low memory loader\",\n    ),\n    \"geoipCode\": MessageLookupByLibrary.simpleMessage(\"GeoIP Code\"),\n    \"getOriginRules\": MessageLookupByLibrary.simpleMessage(\"Original Rules\"),\n    \"global\": MessageLookupByLibrary.simpleMessage(\"Global\"),\n    \"go\": MessageLookupByLibrary.simpleMessage(\"Go\"),\n    \"goDownload\": MessageLookupByLibrary.simpleMessage(\"Download Now\"),\n    \"harmonyFont\": MessageLookupByLibrary.simpleMessage(\"HarmonyOS Font\"),\n    \"harmonyFontDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Use optimized HarmonyOS Sans font\",\n    ),\n    \"hasCacheChange\": MessageLookupByLibrary.simpleMessage(\n      \"Cache modifications?\",\n    ),\n    \"healthCheckTimeout\": MessageLookupByLibrary.simpleMessage(\"Timeout\"),\n    \"healthCheckTimeoutDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Node health check timeout\",\n    ),\n    \"highRefreshRate\": MessageLookupByLibrary.simpleMessage(\n      \"High Refresh Rate\",\n    ),\n    \"highRefreshRateDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Enable highest refresh rate support\",\n    ),\n    \"host\": MessageLookupByLibrary.simpleMessage(\"Host\"),\n    \"hostsDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Append hosts to current config\",\n    ),\n    \"hotkeyConflict\": MessageLookupByLibrary.simpleMessage(\"Hotkey Conflict\"),\n    \"hotkeyManagement\": MessageLookupByLibrary.simpleMessage(\n      \"Hotkey Management\",\n    ),\n    \"hotkeyManagementDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Control app via keyboard\",\n    ),\n    \"hours\": MessageLookupByLibrary.simpleMessage(\"Hours\"),\n    \"httpPortSniffer\": MessageLookupByLibrary.simpleMessage(\n      \"HTTP Port Sniffing\",\n    ),\n    \"icmpForwarding\": MessageLookupByLibrary.simpleMessage(\"ICMP Forwarding\"),\n    \"icmpForwardingDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Enable ICMP Ping\",\n    ),\n    \"icon\": MessageLookupByLibrary.simpleMessage(\"Icon\"),\n    \"iconConfiguration\": MessageLookupByLibrary.simpleMessage(\n      \"Icon Configuration\",\n    ),\n    \"iconStyle\": MessageLookupByLibrary.simpleMessage(\"Icon Style\"),\n    \"import\": MessageLookupByLibrary.simpleMessage(\"Import\"),\n    \"importFailed\": MessageLookupByLibrary.simpleMessage(\"Import failed\"),\n    \"importFile\": MessageLookupByLibrary.simpleMessage(\"Import from File\"),\n    \"importFromCode\": MessageLookupByLibrary.simpleMessage(\"Import from Code\"),\n    \"importFromURL\": MessageLookupByLibrary.simpleMessage(\"Import from URL\"),\n    \"importUrl\": MessageLookupByLibrary.simpleMessage(\"Import from URL\"),\n    \"infiniteTime\": MessageLookupByLibrary.simpleMessage(\"Never Expires\"),\n    \"init\": MessageLookupByLibrary.simpleMessage(\"Init\"),\n    \"inputCorrectHotkey\": MessageLookupByLibrary.simpleMessage(\n      \"Enter a valid hotkey\",\n    ),\n    \"intelligentSelected\": MessageLookupByLibrary.simpleMessage(\"Smart Select\"),\n    \"internet\": MessageLookupByLibrary.simpleMessage(\"Internet\"),\n    \"interval\": MessageLookupByLibrary.simpleMessage(\"Interval\"),\n    \"intranetIP\": MessageLookupByLibrary.simpleMessage(\"Intranet IP\"),\n    \"invalidIpFormat\": MessageLookupByLibrary.simpleMessage(\n      \"Invalid IP or CIDR format\",\n    ),\n    \"ipClickBehavior\": MessageLookupByLibrary.simpleMessage(\"Toggle Display\"),\n    \"ipPrivacyProtection\": MessageLookupByLibrary.simpleMessage(\n      \"Hide IP Display\",\n    ),\n    \"ipcidr\": MessageLookupByLibrary.simpleMessage(\"IP/CIDR\"),\n    \"ipv6Desc\": MessageLookupByLibrary.simpleMessage(\n      \"Enable IPv6 traffic routing\",\n    ),\n    \"ipv6InboundDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Allow IPv6 inbound\",\n    ),\n    \"ja\": MessageLookupByLibrary.simpleMessage(\"Japanese\"),\n    \"just\": MessageLookupByLibrary.simpleMessage(\"Just now\"),\n    \"keepAliveIntervalDesc\": MessageLookupByLibrary.simpleMessage(\n      \"TCP keep-alive interval\",\n    ),\n    \"key\": MessageLookupByLibrary.simpleMessage(\"Key\"),\n    \"language\": MessageLookupByLibrary.simpleMessage(\"Language\"),\n    \"layout\": MessageLookupByLibrary.simpleMessage(\"Layout\"),\n    \"light\": MessageLookupByLibrary.simpleMessage(\"Light\"),\n    \"lightIcon\": MessageLookupByLibrary.simpleMessage(\"Light Icon\"),\n    \"lightIconDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Manually switch light desktop app icon\",\n    ),\n    \"list\": MessageLookupByLibrary.simpleMessage(\"List\"),\n    \"listen\": MessageLookupByLibrary.simpleMessage(\"Listen\"),\n    \"local\": MessageLookupByLibrary.simpleMessage(\"Local\"),\n    \"localBackupDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Backup data locally\",\n    ),\n    \"localRecoveryDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Restore data from file\",\n    ),\n    \"log\": MessageLookupByLibrary.simpleMessage(\"Log\"),\n    \"logLevel\": MessageLookupByLibrary.simpleMessage(\"Log Level\"),\n    \"logcat\": MessageLookupByLibrary.simpleMessage(\"Log Capture\"),\n    \"logcatDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Show log capture entry\",\n    ),\n    \"logs\": MessageLookupByLibrary.simpleMessage(\"Logs\"),\n    \"logsDesc\": MessageLookupByLibrary.simpleMessage(\"View captured logs\"),\n    \"logsTest\": MessageLookupByLibrary.simpleMessage(\"Logs Test\"),\n    \"loopback\": MessageLookupByLibrary.simpleMessage(\"Loopback Unlock\"),\n    \"loopbackDesc\": MessageLookupByLibrary.simpleMessage(\n      \"UWP loopback unlocking tool\",\n    ),\n    \"loose\": MessageLookupByLibrary.simpleMessage(\"Loose\"),\n    \"manualRefreshIp\": MessageLookupByLibrary.simpleMessage(\"Refresh IP\"),\n    \"memoryInfo\": MessageLookupByLibrary.simpleMessage(\"Memory Info\"),\n    \"messageTest\": MessageLookupByLibrary.simpleMessage(\"Message Test\"),\n    \"messageTestTip\": MessageLookupByLibrary.simpleMessage(\n      \"This is a message.\",\n    ),\n    \"min\": MessageLookupByLibrary.simpleMessage(\"Min\"),\n    \"minimizeOnExit\": MessageLookupByLibrary.simpleMessage(\"Minimize on Exit\"),\n    \"minimizeOnExitDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Override default exit behavior\",\n    ),\n    \"minutes\": MessageLookupByLibrary.simpleMessage(\"Minutes\"),\n    \"mixedPort\": MessageLookupByLibrary.simpleMessage(\"Mixed Port\"),\n    \"mode\": MessageLookupByLibrary.simpleMessage(\"Mode\"),\n    \"monochromeScheme\": MessageLookupByLibrary.simpleMessage(\"Monochrome\"),\n    \"months\": MessageLookupByLibrary.simpleMessage(\"Months\"),\n    \"more\": MessageLookupByLibrary.simpleMessage(\"More\"),\n    \"name\": MessageLookupByLibrary.simpleMessage(\"Name\"),\n    \"nameSort\": MessageLookupByLibrary.simpleMessage(\"Sort by Name\"),\n    \"nameserver\": MessageLookupByLibrary.simpleMessage(\"Nameserver\"),\n    \"nameserverDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Used to resolve domains\",\n    ),\n    \"nameserverPolicy\": MessageLookupByLibrary.simpleMessage(\n      \"Nameserver Policy\",\n    ),\n    \"nameserverPolicyDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Specify domain-specific nameservers\",\n    ),\n    \"navBarHapticFeedback\": MessageLookupByLibrary.simpleMessage(\n      \"Haptic Feedback\",\n    ),\n    \"navBarHapticFeedbackDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Vibrate on navigation tab switch\",\n    ),\n    \"network\": MessageLookupByLibrary.simpleMessage(\"Network\"),\n    \"networkDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Modify network-related settings\",\n    ),\n    \"networkDetection\": MessageLookupByLibrary.simpleMessage(\n      \"Network Detection\",\n    ),\n    \"networkFix\": MessageLookupByLibrary.simpleMessage(\"Network Fix\"),\n    \"networkFixDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Fix Windows network globe icon issue\",\n    ),\n    \"networkMatch\": MessageLookupByLibrary.simpleMessage(\"Network Match\"),\n    \"networkMatchHint\": MessageLookupByLibrary.simpleMessage(\n      \"Max 2 IPs/CIDRs, comma-separated\",\n    ),\n    \"networkSpeed\": MessageLookupByLibrary.simpleMessage(\"Network Speed\"),\n    \"networkType\": MessageLookupByLibrary.simpleMessage(\"Network Type\"),\n    \"neutralScheme\": MessageLookupByLibrary.simpleMessage(\"Neutral\"),\n    \"noAnimation\": MessageLookupByLibrary.simpleMessage(\"Default\"),\n    \"noData\": MessageLookupByLibrary.simpleMessage(\"No Data\"),\n    \"noHotKey\": MessageLookupByLibrary.simpleMessage(\"No Hotkeys\"),\n    \"noIcon\": MessageLookupByLibrary.simpleMessage(\"No Icon\"),\n    \"noInfo\": MessageLookupByLibrary.simpleMessage(\"No Info\"),\n    \"noMoreInfoDesc\": MessageLookupByLibrary.simpleMessage(\"No more info\"),\n    \"noNetwork\": MessageLookupByLibrary.simpleMessage(\"No Network\"),\n    \"noNetworkApp\": MessageLookupByLibrary.simpleMessage(\"No Network App\"),\n    \"noProxy\": MessageLookupByLibrary.simpleMessage(\"No Proxy\"),\n    \"noProxyDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Please create or add a valid profile\",\n    ),\n    \"noResolve\": MessageLookupByLibrary.simpleMessage(\"No Resolve\"),\n    \"noStatusAvailable\": MessageLookupByLibrary.simpleMessage(\"No Status\"),\n    \"nodeExclusion\": MessageLookupByLibrary.simpleMessage(\"Node Exclusion\"),\n    \"nodeExclusionDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Exclude all matched nodes\",\n    ),\n    \"nodeExclusionPlaceholder\": MessageLookupByLibrary.simpleMessage(\n      \"HK|Hong Kong|🇭🇰\",\n    ),\n    \"none\": MessageLookupByLibrary.simpleMessage(\"None\"),\n    \"notRecommended\": MessageLookupByLibrary.simpleMessage(\"Not Recommended\"),\n    \"notSelectedTip\": MessageLookupByLibrary.simpleMessage(\n      \"Current proxy group cannot be selected.\",\n    ),\n    \"ntp\": MessageLookupByLibrary.simpleMessage(\"NTP\"),\n    \"ntpDesc\": MessageLookupByLibrary.simpleMessage(\"Use NTP time service\"),\n    \"ntpInterval\": MessageLookupByLibrary.simpleMessage(\"Update Interval\"),\n    \"ntpPort\": MessageLookupByLibrary.simpleMessage(\"Port\"),\n    \"ntpServer\": MessageLookupByLibrary.simpleMessage(\"Server\"),\n    \"ntpStatus\": MessageLookupByLibrary.simpleMessage(\"Status\"),\n    \"ntpStatusDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Enable NTP time service\",\n    ),\n    \"nullProfileDesc\": MessageLookupByLibrary.simpleMessage(\n      \"No profile. Please add one.\",\n    ),\n    \"nullTip\": m5,\n    \"numberTip\": m6,\n    \"oneColumn\": MessageLookupByLibrary.simpleMessage(\"1 Column\"),\n    \"onlinePanel\": MessageLookupByLibrary.simpleMessage(\"Online Panel\"),\n    \"onlyIcon\": MessageLookupByLibrary.simpleMessage(\"Icon Only\"),\n    \"onlyOtherApps\": MessageLookupByLibrary.simpleMessage(\n      \"Third-Party Apps Only\",\n    ),\n    \"onlyStatisticsProxy\": MessageLookupByLibrary.simpleMessage(\n      \"Proxy Traffic Only\",\n    ),\n    \"onlyStatisticsProxyDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Only record proxy traffic\",\n    ),\n    \"openDashboard\": MessageLookupByLibrary.simpleMessage(\"Open Zashboard\"),\n    \"openSettings\": MessageLookupByLibrary.simpleMessage(\"Open Settings\"),\n    \"options\": MessageLookupByLibrary.simpleMessage(\"Options\"),\n    \"other\": MessageLookupByLibrary.simpleMessage(\"Other\"),\n    \"otherContributors\": MessageLookupByLibrary.simpleMessage(\n      \"Other Contributors\",\n    ),\n    \"otherSettings\": MessageLookupByLibrary.simpleMessage(\"Enhanced Tools\"),\n    \"otherSettingsDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Modify enhanced tool settings\",\n    ),\n    \"outboundMode\": MessageLookupByLibrary.simpleMessage(\"Outbound Mode\"),\n    \"override\": MessageLookupByLibrary.simpleMessage(\"Override\"),\n    \"overrideDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Override proxy configurations\",\n    ),\n    \"overrideDestination\": MessageLookupByLibrary.simpleMessage(\n      \"Override Destination\",\n    ),\n    \"overrideDestinationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Override destination with sniffed result\",\n    ),\n    \"overrideDns\": MessageLookupByLibrary.simpleMessage(\"Override DNS\"),\n    \"overrideDnsDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Override profile\\'s DNS settings\",\n    ),\n    \"overrideExperimental\": MessageLookupByLibrary.simpleMessage(\n      \"Override Experimental\",\n    ),\n    \"overrideExperimentalDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Override profile\\'s Experimental settings\",\n    ),\n    \"overrideInvalidTip\": MessageLookupByLibrary.simpleMessage(\n      \"Inactive in script mode\",\n    ),\n    \"overrideNtp\": MessageLookupByLibrary.simpleMessage(\"Override NTP\"),\n    \"overrideNtpDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Override profile\\'s NTP settings\",\n    ),\n    \"overrideOriginRules\": MessageLookupByLibrary.simpleMessage(\n      \"Override Original Rules\",\n    ),\n    \"overrideSniffer\": MessageLookupByLibrary.simpleMessage(\"Override Sniffer\"),\n    \"overrideSnifferDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Override profile\\'s Sniffer settings\",\n    ),\n    \"overrideTestUrl\": MessageLookupByLibrary.simpleMessage(\"Override Config\"),\n    \"overrideTunnel\": MessageLookupByLibrary.simpleMessage(\"Override Tunnel\"),\n    \"overrideTunnelDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Override profile\\'s Tunnel settings\",\n    ),\n    \"packageListPermissionDenied\": MessageLookupByLibrary.simpleMessage(\n      \"Permission denied. Cannot access app list.\",\n    ),\n    \"packageListPermissionRequired\": MessageLookupByLibrary.simpleMessage(\n      \"Permission to access installed apps is required. Grant now?\",\n    ),\n    \"palette\": MessageLookupByLibrary.simpleMessage(\"Palette\"),\n    \"parsePureIp\": MessageLookupByLibrary.simpleMessage(\"Parse Pure IP\"),\n    \"parsePureIpDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Parse pure IP connections\",\n    ),\n    \"password\": MessageLookupByLibrary.simpleMessage(\"Password\"),\n    \"paste\": MessageLookupByLibrary.simpleMessage(\"Paste\"),\n    \"pleaseBindWebDAV\": MessageLookupByLibrary.simpleMessage(\n      \"Please bind WebDAV\",\n    ),\n    \"pleaseCloseSystemProxyFirst\": MessageLookupByLibrary.simpleMessage(\n      \"Please close System Proxy first\",\n    ),\n    \"pleaseCloseTunFirst\": MessageLookupByLibrary.simpleMessage(\n      \"Please close TUN first\",\n    ),\n    \"pleaseEnterScriptName\": MessageLookupByLibrary.simpleMessage(\n      \"Please enter a script name\",\n    ),\n    \"pleaseInputAdminPassword\": MessageLookupByLibrary.simpleMessage(\n      \"Please enter the admin password\",\n    ),\n    \"pleaseUploadFile\": MessageLookupByLibrary.simpleMessage(\n      \"Please upload a file\",\n    ),\n    \"pleaseUploadValidQrcode\": MessageLookupByLibrary.simpleMessage(\n      \"Please upload a valid QR code\",\n    ),\n    \"port\": MessageLookupByLibrary.simpleMessage(\"Port\"),\n    \"portConflictTip\": MessageLookupByLibrary.simpleMessage(\n      \"Please enter a different port\",\n    ),\n    \"portTip\": m7,\n    \"powerSwitch\": MessageLookupByLibrary.simpleMessage(\"Power Switch\"),\n    \"preferH3Desc\": MessageLookupByLibrary.simpleMessage(\n      \"Prioritize DoH HTTP/3\",\n    ),\n    \"pressKeyboard\": MessageLookupByLibrary.simpleMessage(\"Press a key\"),\n    \"preview\": MessageLookupByLibrary.simpleMessage(\"Preview\"),\n    \"profile\": MessageLookupByLibrary.simpleMessage(\"Profile\"),\n    \"profileAutoUpdateIntervalInvalidValidationDesc\":\n        MessageLookupByLibrary.simpleMessage(\"Please enter a valid interval\"),\n    \"profileAutoUpdateIntervalNullValidationDesc\":\n        MessageLookupByLibrary.simpleMessage(\"Please enter update interval\"),\n    \"profileHasUpdate\": MessageLookupByLibrary.simpleMessage(\n      \"Profile modified. Disable auto-update?\",\n    ),\n    \"profileNameNullValidationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Please enter a profile name\",\n    ),\n    \"profileParseErrorDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Profile parse error\",\n    ),\n    \"profileUrlInvalidValidationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Please enter a valid URL\",\n    ),\n    \"profileUrlNullValidationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Please enter a profile URL\",\n    ),\n    \"profiles\": MessageLookupByLibrary.simpleMessage(\"Profiles\"),\n    \"profilesSort\": MessageLookupByLibrary.simpleMessage(\"Profile Sorting\"),\n    \"progress\": MessageLookupByLibrary.simpleMessage(\"Progress\"),\n    \"project\": MessageLookupByLibrary.simpleMessage(\"Project\"),\n    \"providers\": MessageLookupByLibrary.simpleMessage(\"Providers\"),\n    \"proxies\": MessageLookupByLibrary.simpleMessage(\"Proxies\"),\n    \"proxiesSetting\": MessageLookupByLibrary.simpleMessage(\"Proxy Settings\"),\n    \"proxyChains\": MessageLookupByLibrary.simpleMessage(\"Proxy Chains\"),\n    \"proxyGroup\": MessageLookupByLibrary.simpleMessage(\"Proxy Group\"),\n    \"proxyNameserver\": MessageLookupByLibrary.simpleMessage(\"Proxy Nameserver\"),\n    \"proxyNameserverDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Used to resolve proxy nodes\",\n    ),\n    \"proxyPort\": MessageLookupByLibrary.simpleMessage(\"Proxy Port\"),\n    \"proxyPortDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Set the Clash listening port\",\n    ),\n    \"proxyProviders\": MessageLookupByLibrary.simpleMessage(\"Proxy Providers\"),\n    \"pulse\": MessageLookupByLibrary.simpleMessage(\"Pulse\"),\n    \"pureBlackMode\": MessageLookupByLibrary.simpleMessage(\"Pure Black Mode\"),\n    \"qrcode\": MessageLookupByLibrary.simpleMessage(\"QR Code\"),\n    \"qrcodeDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Scan QR code to get profile\",\n    ),\n    \"quicGoDisableEcn\": MessageLookupByLibrary.simpleMessage(\n      \"Disable QUIC ECN\",\n    ),\n    \"quicGoDisableEcnDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Disable QUIC Explicit Congestion Notification\",\n    ),\n    \"quicGoDisableGso\": MessageLookupByLibrary.simpleMessage(\n      \"Disable QUIC GSO\",\n    ),\n    \"quicGoDisableGsoDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Disable QUIC Generic Segmentation Offload\",\n    ),\n    \"quicPortSniffer\": MessageLookupByLibrary.simpleMessage(\n      \"QUIC Port Sniffing\",\n    ),\n    \"quickResponse\": MessageLookupByLibrary.simpleMessage(\"Quick Response\"),\n    \"quickResponseDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Disconnect on network change (WiFi/Mobile)\",\n    ),\n    \"rainbowScheme\": MessageLookupByLibrary.simpleMessage(\"Rainbow\"),\n    \"recovery\": MessageLookupByLibrary.simpleMessage(\"Restore\"),\n    \"recoveryAll\": MessageLookupByLibrary.simpleMessage(\"Restore All Data\"),\n    \"recoveryProfiles\": MessageLookupByLibrary.simpleMessage(\n      \"Restore Profiles Only\",\n    ),\n    \"recoveryStrategy\": MessageLookupByLibrary.simpleMessage(\n      \"Recovery Strategy\",\n    ),\n    \"recoveryStrategy_compatible\": MessageLookupByLibrary.simpleMessage(\n      \"Compatible\",\n    ),\n    \"recoveryStrategy_override\": MessageLookupByLibrary.simpleMessage(\n      \"Override\",\n    ),\n    \"recoverySuccess\": MessageLookupByLibrary.simpleMessage(\n      \"Restore Successful\",\n    ),\n    \"redirPort\": MessageLookupByLibrary.simpleMessage(\"Redir Port\"),\n    \"redo\": MessageLookupByLibrary.simpleMessage(\"Redo\"),\n    \"refreshAppList\": MessageLookupByLibrary.simpleMessage(\"Refresh App List\"),\n    \"refreshAppListConfirm\": MessageLookupByLibrary.simpleMessage(\n      \"Refresh the app list?\",\n    ),\n    \"regExp\": MessageLookupByLibrary.simpleMessage(\"RegExp\"),\n    \"remote\": MessageLookupByLibrary.simpleMessage(\"Remote\"),\n    \"remoteBackupDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Backup data to WebDAV\",\n    ),\n    \"remoteDestination\": MessageLookupByLibrary.simpleMessage(\n      \"Remote Destination\",\n    ),\n    \"remoteRecoveryDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Restore data from WebDAV\",\n    ),\n    \"remove\": MessageLookupByLibrary.simpleMessage(\"Remove\"),\n    \"rename\": MessageLookupByLibrary.simpleMessage(\"Rename\"),\n    \"request\": MessageLookupByLibrary.simpleMessage(\"Request\"),\n    \"requests\": MessageLookupByLibrary.simpleMessage(\"Requests\"),\n    \"requestsDesc\": MessageLookupByLibrary.simpleMessage(\n      \"View recent request logs\",\n    ),\n    \"reset\": MessageLookupByLibrary.simpleMessage(\"Reset\"),\n    \"resetTip\": MessageLookupByLibrary.simpleMessage(\n      \"Are you sure you want to reset?\",\n    ),\n    \"resources\": MessageLookupByLibrary.simpleMessage(\"Resources\"),\n    \"resourcesDesc\": MessageLookupByLibrary.simpleMessage(\n      \"External resource info\",\n    ),\n    \"respectRules\": MessageLookupByLibrary.simpleMessage(\"Respect Rules\"),\n    \"respectRulesDesc\": MessageLookupByLibrary.simpleMessage(\n      \"DNS connections follow Rules\",\n    ),\n    \"restart\": MessageLookupByLibrary.simpleMessage(\"Restart\"),\n    \"restartCoreDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Manually restart the core?\",\n    ),\n    \"restartCoreTitle\": MessageLookupByLibrary.simpleMessage(\"Restart Core\"),\n    \"restartTip\": MessageLookupByLibrary.simpleMessage(\n      \"Restart TUN for changes to take effect\",\n    ),\n    \"retry\": MessageLookupByLibrary.simpleMessage(\"Retry\"),\n    \"rotatingCircle\": MessageLookupByLibrary.simpleMessage(\"Rotating Circle\"),\n    \"ru\": MessageLookupByLibrary.simpleMessage(\"Russian\"),\n    \"rule\": MessageLookupByLibrary.simpleMessage(\"Rule\"),\n    \"ruleName\": MessageLookupByLibrary.simpleMessage(\"Rule Name\"),\n    \"ruleProviders\": MessageLookupByLibrary.simpleMessage(\"Rule Providers\"),\n    \"ruleTarget\": MessageLookupByLibrary.simpleMessage(\"Rule Target\"),\n    \"runTime\": MessageLookupByLibrary.simpleMessage(\"Uptime\"),\n    \"runtimeConfig\": MessageLookupByLibrary.simpleMessage(\"Runtime Config\"),\n    \"save\": MessageLookupByLibrary.simpleMessage(\"Save\"),\n    \"saveChanges\": MessageLookupByLibrary.simpleMessage(\"Save changes?\"),\n    \"saveTip\": MessageLookupByLibrary.simpleMessage(\n      \"Are you sure you want to save?\",\n    ),\n    \"script\": MessageLookupByLibrary.simpleMessage(\"Script\"),\n    \"scriptDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Global override script config\",\n    ),\n    \"search\": MessageLookupByLibrary.simpleMessage(\"Search\"),\n    \"seconds\": MessageLookupByLibrary.simpleMessage(\"Seconds\"),\n    \"secretCopied\": MessageLookupByLibrary.simpleMessage(\n      \"Secret copied to clipboard\",\n    ),\n    \"selectAll\": MessageLookupByLibrary.simpleMessage(\"Select All\"),\n    \"selected\": MessageLookupByLibrary.simpleMessage(\"Selected\"),\n    \"selectedCountTitle\": m8,\n    \"serviceReady\": MessageLookupByLibrary.simpleMessage(\"Service Ready\"),\n    \"serviceRunning\": MessageLookupByLibrary.simpleMessage(\"Service Running\"),\n    \"settings\": MessageLookupByLibrary.simpleMessage(\"Settings\"),\n    \"show\": MessageLookupByLibrary.simpleMessage(\"Show\"),\n    \"shrink\": MessageLookupByLibrary.simpleMessage(\"Compact\"),\n    \"silentLaunch\": MessageLookupByLibrary.simpleMessage(\"Silent Launch\"),\n    \"silentLaunchDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Start in the background\",\n    ),\n    \"size\": MessageLookupByLibrary.simpleMessage(\"Size\"),\n    \"skipDomain\": MessageLookupByLibrary.simpleMessage(\"Skip Domain\"),\n    \"skipDstAddress\": MessageLookupByLibrary.simpleMessage(\n      \"Skip Destination IP\",\n    ),\n    \"skipSrcAddress\": MessageLookupByLibrary.simpleMessage(\"Skip Source IP\"),\n    \"smartAutoStop\": MessageLookupByLibrary.simpleMessage(\"Smart Auto-Stop\"),\n    \"smartAutoStopDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Stop VPN on specific networks\",\n    ),\n    \"smartAutoStopServiceRunning\": MessageLookupByLibrary.simpleMessage(\n      \"Smart Auto-Stop running\",\n    ),\n    \"smartDelayLaunch\": MessageLookupByLibrary.simpleMessage(\"Smart Delay\"),\n    \"smartDelayLaunchDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Launch after network connected\",\n    ),\n    \"sniffer\": MessageLookupByLibrary.simpleMessage(\"Sniffer\"),\n    \"snifferAddressHint\": MessageLookupByLibrary.simpleMessage(\n      \"One address per line\",\n    ),\n    \"snifferDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Modify domain sniffer config\",\n    ),\n    \"snifferDomainHint\": MessageLookupByLibrary.simpleMessage(\n      \"One domain per line\",\n    ),\n    \"snifferPorts\": MessageLookupByLibrary.simpleMessage(\"Ports\"),\n    \"snifferPortsHint\": MessageLookupByLibrary.simpleMessage(\n      \"e.g.: 80, 8080-8880\",\n    ),\n    \"snifferStatus\": MessageLookupByLibrary.simpleMessage(\"Status\"),\n    \"snifferStatusDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Enable Sniffer service\",\n    ),\n    \"socksPort\": MessageLookupByLibrary.simpleMessage(\"Socks Port\"),\n    \"sort\": MessageLookupByLibrary.simpleMessage(\"Sort\"),\n    \"source\": MessageLookupByLibrary.simpleMessage(\"Source\"),\n    \"sourceIp\": MessageLookupByLibrary.simpleMessage(\"Source IP\"),\n    \"specialProxy\": MessageLookupByLibrary.simpleMessage(\"Special Proxy\"),\n    \"specialRules\": MessageLookupByLibrary.simpleMessage(\"Special Rules\"),\n    \"spinningLines\": MessageLookupByLibrary.simpleMessage(\"Spinning Lines\"),\n    \"stackMode\": MessageLookupByLibrary.simpleMessage(\"Stack Mode\"),\n    \"standard\": MessageLookupByLibrary.simpleMessage(\"Standard\"),\n    \"start\": MessageLookupByLibrary.simpleMessage(\"Start\"),\n    \"startTest\": MessageLookupByLibrary.simpleMessage(\"Start Test\"),\n    \"startVpn\": MessageLookupByLibrary.simpleMessage(\"Starting...\"),\n    \"status\": MessageLookupByLibrary.simpleMessage(\"Status\"),\n    \"statusDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Uses system DNS when disabled\",\n    ),\n    \"stop\": MessageLookupByLibrary.simpleMessage(\"Stop\"),\n    \"stopVpn\": MessageLookupByLibrary.simpleMessage(\"Stopping...\"),\n    \"storeFix\": MessageLookupByLibrary.simpleMessage(\"Store Fix\"),\n    \"storeFixDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Fix Play Store download issues\",\n    ),\n    \"strictRoute\": MessageLookupByLibrary.simpleMessage(\"Strict Route\"),\n    \"strictRouteDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Use TUN strict routing mode\",\n    ),\n    \"style\": MessageLookupByLibrary.simpleMessage(\"Style\"),\n    \"subRule\": MessageLookupByLibrary.simpleMessage(\"Sub Rule\"),\n    \"submit\": MessageLookupByLibrary.simpleMessage(\"Submit\"),\n    \"success\": MessageLookupByLibrary.simpleMessage(\"Success\"),\n    \"switchLabel\": MessageLookupByLibrary.simpleMessage(\"Switch\"),\n    \"switchToDomesticIp\": MessageLookupByLibrary.simpleMessage(\n      \"Get Domestic IP\",\n    ),\n    \"sync\": MessageLookupByLibrary.simpleMessage(\"Sync\"),\n    \"syncAll\": MessageLookupByLibrary.simpleMessage(\"Sync All\"),\n    \"syncFailed\": MessageLookupByLibrary.simpleMessage(\"Sync Failed\"),\n    \"system\": MessageLookupByLibrary.simpleMessage(\"System\"),\n    \"systemApp\": MessageLookupByLibrary.simpleMessage(\"System App\"),\n    \"systemFont\": MessageLookupByLibrary.simpleMessage(\"System Font\"),\n    \"systemProxy\": MessageLookupByLibrary.simpleMessage(\"System Proxy\"),\n    \"systemProxyDesc\": MessageLookupByLibrary.simpleMessage(\"Set system proxy\"),\n    \"tab\": MessageLookupByLibrary.simpleMessage(\"Tab\"),\n    \"tabAnimation\": MessageLookupByLibrary.simpleMessage(\"Tab Animation\"),\n    \"tabAnimationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Effective only in mobile view\",\n    ),\n    \"tcpConcurrent\": MessageLookupByLibrary.simpleMessage(\"TCP Concurrent\"),\n    \"tcpConcurrentDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Allow concurrent TCP connections\",\n    ),\n    \"testUrl\": MessageLookupByLibrary.simpleMessage(\"Test URL\"),\n    \"textScale\": MessageLookupByLibrary.simpleMessage(\"Text Scaling\"),\n    \"theme\": MessageLookupByLibrary.simpleMessage(\"Theme\"),\n    \"themeColor\": MessageLookupByLibrary.simpleMessage(\"Theme Color\"),\n    \"themeDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Set theme color and icon\",\n    ),\n    \"themeMode\": MessageLookupByLibrary.simpleMessage(\"Theme Mode\"),\n    \"threeBounce\": MessageLookupByLibrary.simpleMessage(\"Three Bounce\"),\n    \"threeColumns\": MessageLookupByLibrary.simpleMessage(\"3 Columns\"),\n    \"threeInOut\": MessageLookupByLibrary.simpleMessage(\"Three In Out\"),\n    \"tight\": MessageLookupByLibrary.simpleMessage(\"Compact\"),\n    \"time\": MessageLookupByLibrary.simpleMessage(\"Time\"),\n    \"tip\": MessageLookupByLibrary.simpleMessage(\"Tip\"),\n    \"tlsPortSniffer\": MessageLookupByLibrary.simpleMessage(\"TLS Port Sniffing\"),\n    \"toggle\": MessageLookupByLibrary.simpleMessage(\"Toggle\"),\n    \"tonalSpotScheme\": MessageLookupByLibrary.simpleMessage(\"Tonal Spot\"),\n    \"tooManyRules\": MessageLookupByLibrary.simpleMessage(\"Max 2 rules allowed\"),\n    \"tools\": MessageLookupByLibrary.simpleMessage(\"Tools\"),\n    \"tproxyPort\": MessageLookupByLibrary.simpleMessage(\"Tproxy Port\"),\n    \"trafficUsage\": MessageLookupByLibrary.simpleMessage(\"Traffic Usage\"),\n    \"tryManualRefresh\": MessageLookupByLibrary.simpleMessage(\n      \"Please try manual refresh\",\n    ),\n    \"tun\": MessageLookupByLibrary.simpleMessage(\"TUN\"),\n    \"tunDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Take over global device traffic\",\n    ),\n    \"tunEnableRequireAdmin\": MessageLookupByLibrary.simpleMessage(\n      \"TUN requires admin privileges. Please run as Administrator.\",\n    ),\n    \"tunnel\": MessageLookupByLibrary.simpleMessage(\"Tunnel\"),\n    \"tunnelAddress\": MessageLookupByLibrary.simpleMessage(\"Listen Address\"),\n    \"tunnelAddressHint\": MessageLookupByLibrary.simpleMessage(\n      \"e.g.: 127.0.0.1:6553\",\n    ),\n    \"tunnelDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Use traffic forwarding tunnel\",\n    ),\n    \"tunnelList\": MessageLookupByLibrary.simpleMessage(\"Forwarding List\"),\n    \"tunnelNetwork\": MessageLookupByLibrary.simpleMessage(\"Network Protocol\"),\n    \"tunnelNetworkHint\": MessageLookupByLibrary.simpleMessage(\"e.g.: tcp, udp\"),\n    \"tunnelProxy\": MessageLookupByLibrary.simpleMessage(\"Proxy Name\"),\n    \"tunnelProxyHint\": MessageLookupByLibrary.simpleMessage(\n      \"e.g.: proxy (optional)\",\n    ),\n    \"tunnelTarget\": MessageLookupByLibrary.simpleMessage(\"Target Address\"),\n    \"tunnelTargetHint\": MessageLookupByLibrary.simpleMessage(\n      \"e.g.: 114.114.114.114:53\",\n    ),\n    \"twoColumns\": MessageLookupByLibrary.simpleMessage(\"2 Columns\"),\n    \"unableToUpdateCurrentProfileDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Unable to update current profile\",\n    ),\n    \"undo\": MessageLookupByLibrary.simpleMessage(\"Undo\"),\n    \"unifiedDelay\": MessageLookupByLibrary.simpleMessage(\"Unified Delay\"),\n    \"unifiedDelayDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Exclude handshake delays from testing\",\n    ),\n    \"unknown\": MessageLookupByLibrary.simpleMessage(\"Unknown\"),\n    \"unnamed\": MessageLookupByLibrary.simpleMessage(\"Unnamed\"),\n    \"update\": MessageLookupByLibrary.simpleMessage(\"Update\"),\n    \"upload\": MessageLookupByLibrary.simpleMessage(\"Upload\"),\n    \"url\": MessageLookupByLibrary.simpleMessage(\"URL\"),\n    \"urlDesc\": MessageLookupByLibrary.simpleMessage(\"Get profile via URL\"),\n    \"urlTip\": m9,\n    \"useHosts\": MessageLookupByLibrary.simpleMessage(\"Use Hosts\"),\n    \"useSystemHosts\": MessageLookupByLibrary.simpleMessage(\"Use System Hosts\"),\n    \"value\": MessageLookupByLibrary.simpleMessage(\"Value\"),\n    \"vibrantScheme\": MessageLookupByLibrary.simpleMessage(\"Vibrant\"),\n    \"view\": MessageLookupByLibrary.simpleMessage(\"View\"),\n    \"vpnDesc\": MessageLookupByLibrary.simpleMessage(\"VPN-related settings\"),\n    \"vpnEnableDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Route all system traffic via VpnService\",\n    ),\n    \"vpnSystemProxyDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Attach HTTP proxy to VpnService\",\n    ),\n    \"vpnTip\": MessageLookupByLibrary.simpleMessage(\n      \"Restart VPN to apply changes\",\n    ),\n    \"wakelock\": MessageLookupByLibrary.simpleMessage(\"Wakelock\"),\n    \"wakelockDescription\": MessageLookupByLibrary.simpleMessage(\n      \"Keeps the screen on and app active in the background without requiring special CPU wakelock permissions.\",\n    ),\n    \"wave\": MessageLookupByLibrary.simpleMessage(\"Wave\"),\n    \"webDAVConfiguration\": MessageLookupByLibrary.simpleMessage(\n      \"WebDAV Configuration\",\n    ),\n    \"whitelist\": MessageLookupByLibrary.simpleMessage(\"Whitelist\"),\n    \"whitelistMode\": MessageLookupByLibrary.simpleMessage(\"Whitelist Mode\"),\n    \"writeToSystem\": MessageLookupByLibrary.simpleMessage(\"Write to System\"),\n    \"writeToSystemDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Requires administrator privileges\",\n    ),\n    \"years\": MessageLookupByLibrary.simpleMessage(\"Years\"),\n    \"zh_CN\": MessageLookupByLibrary.simpleMessage(\"Simplified Chinese\"),\n    \"zh_TC\": MessageLookupByLibrary.simpleMessage(\"Traditional Chinese\"),\n  };\n}\n"
  },
  {
    "path": "lib/l10n/intl/messages_ru.dart",
    "content": "// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart\n// This is a library that provides messages for a ru locale. All the\n// messages from the main program should be duplicated here with the same\n// function name.\n\n// Ignore issues from commonly used lints in this file.\n// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new\n// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering\n// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases\n// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes\n// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes\n\nimport 'package:intl/intl.dart';\nimport 'package:intl/message_lookup_by_library.dart';\n\nfinal messages = new MessageLookup();\n\ntypedef String MessageIfAbsent(String messageStr, List<dynamic> args);\n\nclass MessageLookup extends MessageLookupByLibrary {\n  String get localeName => 'ru';\n\n  static String m0(label) => \"Удалить выбранные ${label}?\";\n\n  static String m1(label) => \"Удалить текущий ${label}?\";\n\n  static String m2(label) => \"Подробности ${label}\";\n\n  static String m3(label) => \"${label} не может быть пустым\";\n\n  static String m4(label) => \"${label} уже существует\";\n\n  static String m5(label) => \"${label} отсутствует\";\n\n  static String m6(label) => \"${label} должен быть числом\";\n\n  static String m7(label) => \"${label} должен быть от 1024 до 49151\";\n\n  static String m8(count) => \"Выбрано: ${count}\";\n\n  static String m9(label) => \"${label} должен быть URL\";\n\n  final messages = _notInlinedMessages(_notInlinedMessages);\n  static Map<String, Function> _notInlinedMessages(_) => <String, Function>{\n    \"about\": MessageLookupByLibrary.simpleMessage(\"О программе\"),\n    \"accessControl\": MessageLookupByLibrary.simpleMessage(\"Контроль доступа\"),\n    \"accessControlAllowDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Только выбранные приложения используют VPN\",\n    ),\n    \"accessControlDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Настройка доступа приложений к прокси\",\n    ),\n    \"accessControlNotAllowDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Выбранные приложения исключены из VPN\",\n    ),\n    \"account\": MessageLookupByLibrary.simpleMessage(\"Аккаунт\"),\n    \"action\": MessageLookupByLibrary.simpleMessage(\"Действие\"),\n    \"action_mode\": MessageLookupByLibrary.simpleMessage(\"Сменить режим\"),\n    \"action_proxy\": MessageLookupByLibrary.simpleMessage(\"Системный прокси\"),\n    \"action_start\": MessageLookupByLibrary.simpleMessage(\"Запуск/Остановка\"),\n    \"action_tun\": MessageLookupByLibrary.simpleMessage(\"Виртуальный адаптер\"),\n    \"action_view\": MessageLookupByLibrary.simpleMessage(\"Показать/Скрыть\"),\n    \"add\": MessageLookupByLibrary.simpleMessage(\"Добавить\"),\n    \"addProfile\": MessageLookupByLibrary.simpleMessage(\"Добавить профиль\"),\n    \"addRule\": MessageLookupByLibrary.simpleMessage(\"Добавить правило\"),\n    \"addTunnel\": MessageLookupByLibrary.simpleMessage(\n      \"Добавить перенаправление\",\n    ),\n    \"addedOriginRules\": MessageLookupByLibrary.simpleMessage(\n      \"Добавить к исходным\",\n    ),\n    \"address\": MessageLookupByLibrary.simpleMessage(\"Адрес\"),\n    \"addressHelp\": MessageLookupByLibrary.simpleMessage(\"Адрес сервера WebDAV\"),\n    \"addressTip\": MessageLookupByLibrary.simpleMessage(\n      \"Введите корректный адрес WebDAV\",\n    ),\n    \"adminAutoLaunch\": MessageLookupByLibrary.simpleMessage(\n      \"Автозапуск от администратора\",\n    ),\n    \"adminAutoLaunchDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Автозапуск с правами администратора\",\n    ),\n    \"advancedSettings\": MessageLookupByLibrary.simpleMessage(\n      \"Расширенные настройки\",\n    ),\n    \"ago\": MessageLookupByLibrary.simpleMessage(\"назад\"),\n    \"agree\": MessageLookupByLibrary.simpleMessage(\"Согласен\"),\n    \"allApps\": MessageLookupByLibrary.simpleMessage(\"Все приложения\"),\n    \"allowBypass\": MessageLookupByLibrary.simpleMessage(\"Разрешить обход VPN\"),\n    \"allowBypassDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Некоторые приложения смогут обходить VPN\",\n    ),\n    \"allowLan\": MessageLookupByLibrary.simpleMessage(\"LAN доступ\"),\n    \"allowLanDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Разрешить доступ из локальной сети\",\n    ),\n    \"alreadyInWhitelist\": MessageLookupByLibrary.simpleMessage(\n      \"Уже в белом списке\",\n    ),\n    \"app\": MessageLookupByLibrary.simpleMessage(\"Приложение\"),\n    \"appAccessControl\": MessageLookupByLibrary.simpleMessage(\n      \"Контроль доступа приложений\",\n    ),\n    \"appDesc\": MessageLookupByLibrary.simpleMessage(\"Настройки приложения\"),\n    \"application\": MessageLookupByLibrary.simpleMessage(\"Приложение\"),\n    \"applicationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Настройки приложения\",\n    ),\n    \"auto\": MessageLookupByLibrary.simpleMessage(\"Авто\"),\n    \"autoCheckUpdate\": MessageLookupByLibrary.simpleMessage(\"Автообновление\"),\n    \"autoCheckUpdateDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Проверка обновлений при запуске\",\n    ),\n    \"autoCloseConnections\": MessageLookupByLibrary.simpleMessage(\n      \"Автозакрытие соединений\",\n    ),\n    \"autoCloseConnectionsDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Закрывать соединения при смене узла\",\n    ),\n    \"autoLaunch\": MessageLookupByLibrary.simpleMessage(\"Автозапуск\"),\n    \"autoLaunchDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Запуск при старте системы\",\n    ),\n    \"autoRun\": MessageLookupByLibrary.simpleMessage(\"Автоподключение\"),\n    \"autoRunDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Подключаться при запуске приложения\",\n    ),\n    \"autoSetSystemDns\": MessageLookupByLibrary.simpleMessage(\n      \"Автоматически настроить системный DNS\",\n    ),\n    \"autoUpdate\": MessageLookupByLibrary.simpleMessage(\"Автообновление\"),\n    \"autoUpdateInterval\": MessageLookupByLibrary.simpleMessage(\n      \"Интервал автообновления (минуты)\",\n    ),\n    \"backup\": MessageLookupByLibrary.simpleMessage(\"Создать копию\"),\n    \"backupAndRecovery\": MessageLookupByLibrary.simpleMessage(\n      \"Резервное копирование\",\n    ),\n    \"backupAndRecoveryDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Синхронизация данных через WebDAV или локально\",\n    ),\n    \"backupSuccess\": MessageLookupByLibrary.simpleMessage(\n      \"Резервное копирование успешно\",\n    ),\n    \"basicConfig\": MessageLookupByLibrary.simpleMessage(\n      \"Базовая конфигурация ядра\",\n    ),\n    \"basicConfigDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Глобальное изменение конфигурации ядра\",\n    ),\n    \"batteryOptimization\": MessageLookupByLibrary.simpleMessage(\n      \"Оптимизация батареи\",\n    ),\n    \"batteryOptimizationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Запросить добавление в белый список энергосбережения\",\n    ),\n    \"bind\": MessageLookupByLibrary.simpleMessage(\"Привязать\"),\n    \"blacklist\": MessageLookupByLibrary.simpleMessage(\"Чёрный список\"),\n    \"blacklistMode\": MessageLookupByLibrary.simpleMessage(\n      \"Режим чёрного списка\",\n    ),\n    \"bypassDomain\": MessageLookupByLibrary.simpleMessage(\"Исключить домены\"),\n    \"bypassDomainDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Работает только при включённом системном прокси\",\n    ),\n    \"bypassPrivateRoute\": MessageLookupByLibrary.simpleMessage(\n      \"Обход частной сети\",\n    ),\n    \"bypassPrivateRouteDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Автоматически обходить IP-адреса частной сети\",\n    ),\n    \"cacheAlgorithm\": MessageLookupByLibrary.simpleMessage(\"Алгоритм кэша\"),\n    \"cacheCorrupt\": MessageLookupByLibrary.simpleMessage(\n      \"Кэш повреждён. Очистить?\",\n    ),\n    \"cameraPermissionDenied\": MessageLookupByLibrary.simpleMessage(\n      \"Доступ к камере запрещён\",\n    ),\n    \"cameraPermissionDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Для сканирования QR-кода требуется доступ к камере. Пожалуйста, предоставьте разрешение в настройках.\",\n    ),\n    \"cancel\": MessageLookupByLibrary.simpleMessage(\"Отмена\"),\n    \"cancelFilterSystemApp\": MessageLookupByLibrary.simpleMessage(\n      \"Показать системные приложения\",\n    ),\n    \"cancelSelectAll\": MessageLookupByLibrary.simpleMessage(\"Отменить выбор\"),\n    \"checkError\": MessageLookupByLibrary.simpleMessage(\"Ошибка проверки\"),\n    \"checkOrAddProfile\": MessageLookupByLibrary.simpleMessage(\n      \"Добавьте профиль\",\n    ),\n    \"checkUpdate\": MessageLookupByLibrary.simpleMessage(\"Проверить обновление\"),\n    \"checkUpdateError\": MessageLookupByLibrary.simpleMessage(\n      \"Установлена последняя версия\",\n    ),\n    \"checking\": MessageLookupByLibrary.simpleMessage(\"Проверка...\"),\n    \"circle\": MessageLookupByLibrary.simpleMessage(\"Круг\"),\n    \"clearCacheDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Очистить кэш FakeIP и DNS?\",\n    ),\n    \"clearCacheTitle\": MessageLookupByLibrary.simpleMessage(\"Очистить кэш\"),\n    \"clearData\": MessageLookupByLibrary.simpleMessage(\"Очистить данные\"),\n    \"clipboard\": MessageLookupByLibrary.simpleMessage(\"Буфер обмена\"),\n    \"clipboardDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Автоматически получать ссылки из буфера обмена\",\n    ),\n    \"clipboardExport\": MessageLookupByLibrary.simpleMessage(\"Экспорт в буфер\"),\n    \"clipboardImport\": MessageLookupByLibrary.simpleMessage(\"Импорт из буфера\"),\n    \"color\": MessageLookupByLibrary.simpleMessage(\"Цвет\"),\n    \"colorSchemes\": MessageLookupByLibrary.simpleMessage(\"Цветовые схемы\"),\n    \"columns\": MessageLookupByLibrary.simpleMessage(\"Колонки\"),\n    \"compatible\": MessageLookupByLibrary.simpleMessage(\"Режим совместимости\"),\n    \"compatibleDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Включает полную поддержку Clash с потерей некоторых функций\",\n    ),\n    \"concurrencyLimit\": MessageLookupByLibrary.simpleMessage(\n      \"Лимит параллелизма\",\n    ),\n    \"concurrencyLimitDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Максимальное количество параллельных тестов задержки\",\n    ),\n    \"confirm\": MessageLookupByLibrary.simpleMessage(\"Подтвердить\"),\n    \"connection\": MessageLookupByLibrary.simpleMessage(\"Активные соединения\"),\n    \"connections\": MessageLookupByLibrary.simpleMessage(\"Соединения\"),\n    \"connectionsDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Просмотр текущих соединений\",\n    ),\n    \"connectivity\": MessageLookupByLibrary.simpleMessage(\"Подключение:\"),\n    \"contactMe\": MessageLookupByLibrary.simpleMessage(\"Связаться со мной\"),\n    \"content\": MessageLookupByLibrary.simpleMessage(\"Содержимое\"),\n    \"contentScheme\": MessageLookupByLibrary.simpleMessage(\"Контентная тема\"),\n    \"controlSecret\": MessageLookupByLibrary.simpleMessage(\"Пароль управления\"),\n    \"controlSecretDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Пароль для доступа к RESTful API\",\n    ),\n    \"copy\": MessageLookupByLibrary.simpleMessage(\"Копировать\"),\n    \"copyEnvVar\": MessageLookupByLibrary.simpleMessage(\n      \"Копировать переменные окружения\",\n    ),\n    \"copyLink\": MessageLookupByLibrary.simpleMessage(\"Копировать ссылку\"),\n    \"copySuccess\": MessageLookupByLibrary.simpleMessage(\"Скопировано\"),\n    \"core\": MessageLookupByLibrary.simpleMessage(\"Ядро\"),\n    \"coreConnected\": MessageLookupByLibrary.simpleMessage(\"Подключено\"),\n    \"coreInfo\": MessageLookupByLibrary.simpleMessage(\"Информация о ядре\"),\n    \"coreSuspended\": MessageLookupByLibrary.simpleMessage(\"Приостановлено\"),\n    \"country\": MessageLookupByLibrary.simpleMessage(\"Регион\"),\n    \"crashTest\": MessageLookupByLibrary.simpleMessage(\"Тест сбоя\"),\n    \"create\": MessageLookupByLibrary.simpleMessage(\"Создать\"),\n    \"creationTime\": MessageLookupByLibrary.simpleMessage(\"Время создания\"),\n    \"custom\": MessageLookupByLibrary.simpleMessage(\"Пользовательский\"),\n    \"customUrl\": MessageLookupByLibrary.simpleMessage(\"Пользовательский URL\"),\n    \"cut\": MessageLookupByLibrary.simpleMessage(\"Вырезать\"),\n    \"dark\": MessageLookupByLibrary.simpleMessage(\"Тёмная\"),\n    \"dashboard\": MessageLookupByLibrary.simpleMessage(\"Главная\"),\n    \"days\": MessageLookupByLibrary.simpleMessage(\"дней\"),\n    \"defaultNameserver\": MessageLookupByLibrary.simpleMessage(\n      \"DNS по умолчанию\",\n    ),\n    \"defaultNameserverDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Используется для разрешения DNS-серверов\",\n    ),\n    \"defaultSort\": MessageLookupByLibrary.simpleMessage(\"По умолчанию\"),\n    \"defaultText\": MessageLookupByLibrary.simpleMessage(\"По умолчанию\"),\n    \"delay\": MessageLookupByLibrary.simpleMessage(\"Задержка\"),\n    \"delayAnimation\": MessageLookupByLibrary.simpleMessage(\"Анимация задержки\"),\n    \"delayAnimationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Настройка анимации при тестировании\",\n    ),\n    \"delaySort\": MessageLookupByLibrary.simpleMessage(\"По задержке\"),\n    \"delete\": MessageLookupByLibrary.simpleMessage(\"Удалить\"),\n    \"deleteMultipTip\": m0,\n    \"deleteTip\": m1,\n    \"deleteTunnel\": MessageLookupByLibrary.simpleMessage(\n      \"Удалить перенаправление\",\n    ),\n    \"desc\": MessageLookupByLibrary.simpleMessage(\n      \"Bettbox основан на мощном и гибком прокси-ядре Mihomo (Clash.Meta) и стремится к созданию лучшего пользовательского опыта. Форк от FlClash: Улучшенный опыт, готов к работе «из коробки»\",\n    ),\n    \"destination\": MessageLookupByLibrary.simpleMessage(\"Адрес назначения\"),\n    \"destinationGeoIP\": MessageLookupByLibrary.simpleMessage(\n      \"Геолокация назначения\",\n    ),\n    \"destinationIPASN\": MessageLookupByLibrary.simpleMessage(\n      \"IP ASN назначения\",\n    ),\n    \"details\": m2,\n    \"detectionTip\": MessageLookupByLibrary.simpleMessage(\n      \"Зависит от сторонних API, только для справки\",\n    ),\n    \"developerMode\": MessageLookupByLibrary.simpleMessage(\"Режим разработчика\"),\n    \"developerModeEnableTip\": MessageLookupByLibrary.simpleMessage(\n      \"Режим разработчика включён.\",\n    ),\n    \"dialerIp4pConvert\": MessageLookupByLibrary.simpleMessage(\n      \"Включить преобразование IP4P\",\n    ),\n    \"dialerIp4pConvertDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Включить преобразование IP4P в диалере\",\n    ),\n    \"direct\": MessageLookupByLibrary.simpleMessage(\"Напрямую\"),\n    \"directNameserver\": MessageLookupByLibrary.simpleMessage(\"DNS для прямых\"),\n    \"directNameserverDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Используется для разрешения прямых доменов\",\n    ),\n    \"directNameserverFollowPolicy\": MessageLookupByLibrary.simpleMessage(\n      \"Прямой DNS следует правилам\",\n    ),\n    \"disableQuic\": MessageLookupByLibrary.simpleMessage(\"Отключить QUIC\"),\n    \"disableQuicDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Отключить QUIC для решения сетевых проблем\",\n    ),\n    \"disclaimer\": MessageLookupByLibrary.simpleMessage(\n      \"Отказ от ответственности\",\n    ),\n    \"disclaimerDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Это бесплатное ПО с открытым исходным кодом, предназначенное только для обучения и личного тестирования. Действия прокси-провайдеров не связаны с этим ПО. Соглашаясь, вы подтверждаете, что полностью осведомлены об этом. Если не согласны, пожалуйста, выйдите!\",\n    ),\n    \"discoverNewVersion\": MessageLookupByLibrary.simpleMessage(\n      \"Доступна новая версия\",\n    ),\n    \"discovery\": MessageLookupByLibrary.simpleMessage(\"Доступно обновление\"),\n    \"dnsDesc\": MessageLookupByLibrary.simpleMessage(\"Настройки DNS\"),\n    \"dnsHijack\": MessageLookupByLibrary.simpleMessage(\"Перехват DNS\"),\n    \"dnsHijackDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Перенаправить разбор в модуль DNS\",\n    ),\n    \"dnsMode\": MessageLookupByLibrary.simpleMessage(\"Режим DNS\"),\n    \"doYouWantToPass\": MessageLookupByLibrary.simpleMessage(\"Пропустить\"),\n    \"domain\": MessageLookupByLibrary.simpleMessage(\"Домен\"),\n    \"doubleBounce\": MessageLookupByLibrary.simpleMessage(\"Двойной отскок\"),\n    \"download\": MessageLookupByLibrary.simpleMessage(\"Загрузка\"),\n    \"dozeSuspend\": MessageLookupByLibrary.simpleMessage(\"Поддержка Doze\"),\n    \"dozeSuspendDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Синхронизация с режимом сна Android\",\n    ),\n    \"edit\": MessageLookupByLibrary.simpleMessage(\"Редактировать\"),\n    \"editTunnel\": MessageLookupByLibrary.simpleMessage(\n      \"Изменить перенаправление\",\n    ),\n    \"emptyTip\": m3,\n    \"en\": MessageLookupByLibrary.simpleMessage(\"Английский\"),\n    \"enableCrashReport\": MessageLookupByLibrary.simpleMessage(\"Анализ сбоев\"),\n    \"enableCrashReportDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Отправка отчётов о сбоях при необходимости\",\n    ),\n    \"enableOverride\": MessageLookupByLibrary.simpleMessage(\n      \"Включить переопределение\",\n    ),\n    \"endpointIndependentNat\": MessageLookupByLibrary.simpleMessage(\n      \"Улучшенный NAT\",\n    ),\n    \"endpointIndependentNatDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Включить NAT независимый от конечной точки\",\n    ),\n    \"entries\": MessageLookupByLibrary.simpleMessage(\"записей\"),\n    \"exclude\": MessageLookupByLibrary.simpleMessage(\"Скрыть из недавних\"),\n    \"excludeChina\": MessageLookupByLibrary.simpleMessage(\"Исключить Китай\"),\n    \"excludeChinaDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Разрешить QUIC-трафик Китая вместо полной блокировки\",\n    ),\n    \"excludeDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Скрыть приложение из недавних задач\",\n    ),\n    \"existsTip\": m4,\n    \"exit\": MessageLookupByLibrary.simpleMessage(\"Выход\"),\n    \"expand\": MessageLookupByLibrary.simpleMessage(\"Стандартный\"),\n    \"experimental\": MessageLookupByLibrary.simpleMessage(\"Экспериментальное\"),\n    \"experimentalDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Экспериментальные настройки, используйте с осторожностью\",\n    ),\n    \"expirationTime\": MessageLookupByLibrary.simpleMessage(\"Срок действия\"),\n    \"exportFile\": MessageLookupByLibrary.simpleMessage(\"Экспорт файла\"),\n    \"exportLogs\": MessageLookupByLibrary.simpleMessage(\"Экспорт логов\"),\n    \"exportSuccess\": MessageLookupByLibrary.simpleMessage(\"Экспорт успешен\"),\n    \"expressiveScheme\": MessageLookupByLibrary.simpleMessage(\"Экспрессивный\"),\n    \"externalController\": MessageLookupByLibrary.simpleMessage(\n      \"Внешнее управление\",\n    ),\n    \"externalControllerDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Управление ядром через REST API\",\n    ),\n    \"externalLink\": MessageLookupByLibrary.simpleMessage(\"Внешняя ссылка\"),\n    \"externalResources\": MessageLookupByLibrary.simpleMessage(\n      \"Внешние ресурсы\",\n    ),\n    \"fadingCircle\": MessageLookupByLibrary.simpleMessage(\"Затухающий круг\"),\n    \"fadingFour\": MessageLookupByLibrary.simpleMessage(\"Затухающие точки\"),\n    \"fakeIpFilterMode\": MessageLookupByLibrary.simpleMessage(\n      \"Режим фильтрации FakeIP\",\n    ),\n    \"fakeIpFilterModeDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Указать режим фильтрации FakeIP\",\n    ),\n    \"fakeipFilter\": MessageLookupByLibrary.simpleMessage(\"Фильтр FakeIP\"),\n    \"fakeipRange\": MessageLookupByLibrary.simpleMessage(\"Диапазон FakeIP\"),\n    \"fakeipRangeV6\": MessageLookupByLibrary.simpleMessage(\"Диапазон FakeIPv6\"),\n    \"fakeipTtl\": MessageLookupByLibrary.simpleMessage(\"Время жизни FakeIP\"),\n    \"fallback\": MessageLookupByLibrary.simpleMessage(\"Fallback\"),\n    \"fallbackDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Обычно используются зарубежные DNS\",\n    ),\n    \"fallbackFilter\": MessageLookupByLibrary.simpleMessage(\"Фильтр fallback\"),\n    \"fcmOptimization\": MessageLookupByLibrary.simpleMessage(\"Оптимизация FCM\"),\n    \"fcmOptimizationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Повышает стабильность FCM при прямом подключении\",\n    ),\n    \"fcmTip\": MessageLookupByLibrary.simpleMessage(\n      \"FCM зависит от устройства. Для точных результатов отключите \\'Разрешить обход VPN\\'\",\n    ),\n    \"fidelityScheme\": MessageLookupByLibrary.simpleMessage(\"Высокая точность\"),\n    \"file\": MessageLookupByLibrary.simpleMessage(\"Файл\"),\n    \"fileDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Загрузить файл конфигурации\",\n    ),\n    \"fileIsUpdate\": MessageLookupByLibrary.simpleMessage(\n      \"Файл изменён. Сохранить изменения?\",\n    ),\n    \"filterSystemApp\": MessageLookupByLibrary.simpleMessage(\n      \"Скрыть системные приложения\",\n    ),\n    \"findProcessMode\": MessageLookupByLibrary.simpleMessage(\"Поиск процесса\"),\n    \"findProcessModeDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Включить поиск процесса\",\n    ),\n    \"fontFamily\": MessageLookupByLibrary.simpleMessage(\"Шрифт\"),\n    \"forceDnsMapping\": MessageLookupByLibrary.simpleMessage(\n      \"Принудительное DNS-отображение\",\n    ),\n    \"forceDnsMappingDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Принудительно отображать результаты DNS на соединение\",\n    ),\n    \"forceDomain\": MessageLookupByLibrary.simpleMessage(\n      \"Принудительный сниффинг доменов\",\n    ),\n    \"forceGCDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Выполнить сборку мусора ядра? Экспериментально, используйте с осторожностью\",\n    ),\n    \"forceGCTitle\": MessageLookupByLibrary.simpleMessage(\"Принудительный GC\"),\n    \"formatError\": MessageLookupByLibrary.simpleMessage(\"Проверьте формат\"),\n    \"fourColumns\": MessageLookupByLibrary.simpleMessage(\"4 колонки\"),\n    \"fruitSaladScheme\": MessageLookupByLibrary.simpleMessage(\"Фруктовый салат\"),\n    \"general\": MessageLookupByLibrary.simpleMessage(\"Общие\"),\n    \"generalDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Изменить общие настройки\",\n    ),\n    \"generateSecret\": MessageLookupByLibrary.simpleMessage(\"Сгенерировать\"),\n    \"geoData\": MessageLookupByLibrary.simpleMessage(\"Геоданные\"),\n    \"geodataLoader\": MessageLookupByLibrary.simpleMessage(\n      \"Экономия памяти GEO\",\n    ),\n    \"geodataLoaderDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Использовать загрузчик GEO с низким потреблением памяти\",\n    ),\n    \"geoipCode\": MessageLookupByLibrary.simpleMessage(\"Код GeoIP\"),\n    \"getOriginRules\": MessageLookupByLibrary.simpleMessage(\n      \"Получить исходные правила\",\n    ),\n    \"global\": MessageLookupByLibrary.simpleMessage(\"Глобально\"),\n    \"go\": MessageLookupByLibrary.simpleMessage(\"Перейти\"),\n    \"goDownload\": MessageLookupByLibrary.simpleMessage(\"Перейти к загрузке\"),\n    \"harmonyFont\": MessageLookupByLibrary.simpleMessage(\"Шрифт Harmony\"),\n    \"harmonyFontDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Использовать оптимизированный HarmonyOS Sans\",\n    ),\n    \"hasCacheChange\": MessageLookupByLibrary.simpleMessage(\n      \"Сохранить изменения кэша?\",\n    ),\n    \"healthCheckTimeout\": MessageLookupByLibrary.simpleMessage(\n      \"Таймаут проверки\",\n    ),\n    \"healthCheckTimeoutDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Таймаут проверки работоспособности узлов\",\n    ),\n    \"highRefreshRate\": MessageLookupByLibrary.simpleMessage(\n      \"Высокая частота обновления\",\n    ),\n    \"highRefreshRateDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Включить поддержку максимальной частоты обновления устройства\",\n    ),\n    \"host\": MessageLookupByLibrary.simpleMessage(\"Хост\"),\n    \"hostsDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Добавить hosts к текущей конфигурации\",\n    ),\n    \"hotkeyConflict\": MessageLookupByLibrary.simpleMessage(\n      \"Конфликт горячих клавиш\",\n    ),\n    \"hotkeyManagement\": MessageLookupByLibrary.simpleMessage(\n      \"Управление горячими клавишами\",\n    ),\n    \"hotkeyManagementDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Управление приложением с клавиатуры\",\n    ),\n    \"hours\": MessageLookupByLibrary.simpleMessage(\"часов\"),\n    \"httpPortSniffer\": MessageLookupByLibrary.simpleMessage(\n      \"HTTP порты сниффера\",\n    ),\n    \"icmpForwarding\": MessageLookupByLibrary.simpleMessage(\"ICMP forwarding\"),\n    \"icmpForwardingDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Включить поддержку ICMP Ping\",\n    ),\n    \"icon\": MessageLookupByLibrary.simpleMessage(\"Иконка\"),\n    \"iconConfiguration\": MessageLookupByLibrary.simpleMessage(\n      \"Настройка иконки\",\n    ),\n    \"iconStyle\": MessageLookupByLibrary.simpleMessage(\"Стиль иконок\"),\n    \"import\": MessageLookupByLibrary.simpleMessage(\"Импорт\"),\n    \"importFailed\": MessageLookupByLibrary.simpleMessage(\"Ошибка импорта\"),\n    \"importFile\": MessageLookupByLibrary.simpleMessage(\"Импорт из файла\"),\n    \"importFromCode\": MessageLookupByLibrary.simpleMessage(\"Импорт из кода\"),\n    \"importFromURL\": MessageLookupByLibrary.simpleMessage(\"Импорт из URL\"),\n    \"importUrl\": MessageLookupByLibrary.simpleMessage(\"Импорт по URL\"),\n    \"infiniteTime\": MessageLookupByLibrary.simpleMessage(\"Бессрочно\"),\n    \"init\": MessageLookupByLibrary.simpleMessage(\"Инициализация\"),\n    \"inputCorrectHotkey\": MessageLookupByLibrary.simpleMessage(\n      \"Введите корректное сочетание клавиш\",\n    ),\n    \"intelligentSelected\": MessageLookupByLibrary.simpleMessage(\"Умный выбор\"),\n    \"internet\": MessageLookupByLibrary.simpleMessage(\"Интернет\"),\n    \"interval\": MessageLookupByLibrary.simpleMessage(\"Интервал\"),\n    \"intranetIP\": MessageLookupByLibrary.simpleMessage(\"Локальный IP\"),\n    \"invalidIpFormat\": MessageLookupByLibrary.simpleMessage(\n      \"Неверный формат IP или CIDR\",\n    ),\n    \"ipClickBehavior\": MessageLookupByLibrary.simpleMessage(\n      \"Переключение отображения\",\n    ),\n    \"ipPrivacyProtection\": MessageLookupByLibrary.simpleMessage(\"Скрыть IP\"),\n    \"ipcidr\": MessageLookupByLibrary.simpleMessage(\"IP/маска\"),\n    \"ipv6Desc\": MessageLookupByLibrary.simpleMessage(\"Включить поддержку IPv6\"),\n    \"ipv6InboundDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Разрешить входящие IPv6\",\n    ),\n    \"ja\": MessageLookupByLibrary.simpleMessage(\"Японский\"),\n    \"just\": MessageLookupByLibrary.simpleMessage(\"только что\"),\n    \"keepAliveIntervalDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Интервал TCP keep-alive\",\n    ),\n    \"key\": MessageLookupByLibrary.simpleMessage(\"Ключ\"),\n    \"language\": MessageLookupByLibrary.simpleMessage(\"Язык\"),\n    \"layout\": MessageLookupByLibrary.simpleMessage(\"Макет\"),\n    \"light\": MessageLookupByLibrary.simpleMessage(\"Светлая\"),\n    \"lightIcon\": MessageLookupByLibrary.simpleMessage(\"Лёгкая иконка\"),\n    \"lightIconDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Переключить на светлый стиль рабочего стола вручную\",\n    ),\n    \"list\": MessageLookupByLibrary.simpleMessage(\"Список\"),\n    \"listen\": MessageLookupByLibrary.simpleMessage(\"Прослушивание\"),\n    \"local\": MessageLookupByLibrary.simpleMessage(\"Локально\"),\n    \"localBackupDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Локальное резервное копирование\",\n    ),\n    \"localRecoveryDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Восстановление из файла\",\n    ),\n    \"log\": MessageLookupByLibrary.simpleMessage(\"Лог\"),\n    \"logLevel\": MessageLookupByLibrary.simpleMessage(\"Уровень логов\"),\n    \"logcat\": MessageLookupByLibrary.simpleMessage(\"Сбор логов\"),\n    \"logcatDesc\": MessageLookupByLibrary.simpleMessage(\"Показать раздел логов\"),\n    \"logs\": MessageLookupByLibrary.simpleMessage(\"Логи\"),\n    \"logsDesc\": MessageLookupByLibrary.simpleMessage(\"Просмотр журналов\"),\n    \"logsTest\": MessageLookupByLibrary.simpleMessage(\"Тест логов\"),\n    \"loopback\": MessageLookupByLibrary.simpleMessage(\"Разблокировка UWP\"),\n    \"loopbackDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Инструмент для разблокировки UWP loopback\",\n    ),\n    \"loose\": MessageLookupByLibrary.simpleMessage(\"Свободный\"),\n    \"manualRefreshIp\": MessageLookupByLibrary.simpleMessage(\"Обновить IP\"),\n    \"memoryInfo\": MessageLookupByLibrary.simpleMessage(\"Информация о памяти\"),\n    \"messageTest\": MessageLookupByLibrary.simpleMessage(\"Тест сообщения\"),\n    \"messageTestTip\": MessageLookupByLibrary.simpleMessage(\n      \"Это тестовое сообщение.\",\n    ),\n    \"min\": MessageLookupByLibrary.simpleMessage(\"Минимальный\"),\n    \"minimizeOnExit\": MessageLookupByLibrary.simpleMessage(\n      \"Сворачивать при выходе\",\n    ),\n    \"minimizeOnExitDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Изменить поведение при выходе\",\n    ),\n    \"minutes\": MessageLookupByLibrary.simpleMessage(\"минут\"),\n    \"mixedPort\": MessageLookupByLibrary.simpleMessage(\"Смешанный порт\"),\n    \"mode\": MessageLookupByLibrary.simpleMessage(\"Режим\"),\n    \"monochromeScheme\": MessageLookupByLibrary.simpleMessage(\"Монохром\"),\n    \"months\": MessageLookupByLibrary.simpleMessage(\"месяцев\"),\n    \"more\": MessageLookupByLibrary.simpleMessage(\"Подробнее\"),\n    \"name\": MessageLookupByLibrary.simpleMessage(\"Имя\"),\n    \"nameSort\": MessageLookupByLibrary.simpleMessage(\"По имени\"),\n    \"nameserver\": MessageLookupByLibrary.simpleMessage(\"Серверы имён\"),\n    \"nameserverDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Используется для разрешения доменов\",\n    ),\n    \"nameserverPolicy\": MessageLookupByLibrary.simpleMessage(\"Политика DNS\"),\n    \"nameserverPolicyDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Указать политику DNS для конкретных доменов\",\n    ),\n    \"navBarHapticFeedback\": MessageLookupByLibrary.simpleMessage(\n      \"Тактильная отдача\",\n    ),\n    \"navBarHapticFeedbackDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Вибрация при переключении нижней панели навигации\",\n    ),\n    \"network\": MessageLookupByLibrary.simpleMessage(\"Сеть\"),\n    \"networkDesc\": MessageLookupByLibrary.simpleMessage(\"Настройки сети\"),\n    \"networkDetection\": MessageLookupByLibrary.simpleMessage(\"Проверка сети\"),\n    \"networkFix\": MessageLookupByLibrary.simpleMessage(\"Исправление сети\"),\n    \"networkFixDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Исправляет значок сети Windows\",\n    ),\n    \"networkMatch\": MessageLookupByLibrary.simpleMessage(\"Сопоставление сети\"),\n    \"networkMatchHint\": MessageLookupByLibrary.simpleMessage(\n      \"Введите IP или CIDR, максимум 2, через запятую\",\n    ),\n    \"networkSpeed\": MessageLookupByLibrary.simpleMessage(\"Скорость сети\"),\n    \"networkType\": MessageLookupByLibrary.simpleMessage(\"Тип сети\"),\n    \"neutralScheme\": MessageLookupByLibrary.simpleMessage(\"Нейтральный\"),\n    \"noAnimation\": MessageLookupByLibrary.simpleMessage(\"По умолчанию\"),\n    \"noData\": MessageLookupByLibrary.simpleMessage(\"Нет данных\"),\n    \"noHotKey\": MessageLookupByLibrary.simpleMessage(\"Нет горячих клавиш\"),\n    \"noIcon\": MessageLookupByLibrary.simpleMessage(\"Без иконок\"),\n    \"noInfo\": MessageLookupByLibrary.simpleMessage(\"Нет информации\"),\n    \"noMoreInfoDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Нет дополнительной информации\",\n    ),\n    \"noNetwork\": MessageLookupByLibrary.simpleMessage(\"Нет сети\"),\n    \"noNetworkApp\": MessageLookupByLibrary.simpleMessage(\"Приложения без сети\"),\n    \"noProxy\": MessageLookupByLibrary.simpleMessage(\"Нет прокси\"),\n    \"noProxyDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Создайте или добавьте профиль\",\n    ),\n    \"noResolve\": MessageLookupByLibrary.simpleMessage(\"Не разрешать IP\"),\n    \"noStatusAvailable\": MessageLookupByLibrary.simpleMessage(\n      \"Статус недоступен\",\n    ),\n    \"nodeExclusion\": MessageLookupByLibrary.simpleMessage(\"Исключение узлов\"),\n    \"nodeExclusionDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Исключить все узлы, соответствующие шаблону\",\n    ),\n    \"nodeExclusionPlaceholder\": MessageLookupByLibrary.simpleMessage(\n      \"HK|Гонконг|🇭🇰\",\n    ),\n    \"none\": MessageLookupByLibrary.simpleMessage(\"Нет\"),\n    \"notRecommended\": MessageLookupByLibrary.simpleMessage(\"Не рекомендуется\"),\n    \"notSelectedTip\": MessageLookupByLibrary.simpleMessage(\n      \"Невозможно выбрать эту группу прокси\",\n    ),\n    \"ntp\": MessageLookupByLibrary.simpleMessage(\"NTP\"),\n    \"ntpDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Использовать службу времени NTP\",\n    ),\n    \"ntpInterval\": MessageLookupByLibrary.simpleMessage(\"Интервал обновления\"),\n    \"ntpPort\": MessageLookupByLibrary.simpleMessage(\"Порт NTP\"),\n    \"ntpServer\": MessageLookupByLibrary.simpleMessage(\"Сервер NTP\"),\n    \"ntpStatus\": MessageLookupByLibrary.simpleMessage(\"Статус NTP\"),\n    \"ntpStatusDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Включить службу времени NTP\",\n    ),\n    \"nullProfileDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Нет профиля, добавьте его\",\n    ),\n    \"nullTip\": m5,\n    \"numberTip\": m6,\n    \"oneColumn\": MessageLookupByLibrary.simpleMessage(\"1 колонка\"),\n    \"onlinePanel\": MessageLookupByLibrary.simpleMessage(\"Онлайн-панель\"),\n    \"onlyIcon\": MessageLookupByLibrary.simpleMessage(\"Только иконки\"),\n    \"onlyOtherApps\": MessageLookupByLibrary.simpleMessage(\"Только сторонние\"),\n    \"onlyStatisticsProxy\": MessageLookupByLibrary.simpleMessage(\n      \"Только прокси-трафик\",\n    ),\n    \"onlyStatisticsProxyDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Считать только трафик через прокси\",\n    ),\n    \"openDashboard\": MessageLookupByLibrary.simpleMessage(\"Открыть Zashboard\"),\n    \"openSettings\": MessageLookupByLibrary.simpleMessage(\"Открыть настройки\"),\n    \"options\": MessageLookupByLibrary.simpleMessage(\"Опции\"),\n    \"other\": MessageLookupByLibrary.simpleMessage(\"Другое\"),\n    \"otherContributors\": MessageLookupByLibrary.simpleMessage(\n      \"Другие участники\",\n    ),\n    \"otherSettings\": MessageLookupByLibrary.simpleMessage(\n      \"Расширенные инструменты\",\n    ),\n    \"otherSettingsDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Настройка расширенных функций\",\n    ),\n    \"outboundMode\": MessageLookupByLibrary.simpleMessage(\"Режим выхода\"),\n    \"override\": MessageLookupByLibrary.simpleMessage(\"Переопределение\"),\n    \"overrideDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Переопределение конфигурации прокси\",\n    ),\n    \"overrideDestination\": MessageLookupByLibrary.simpleMessage(\n      \"Переопределить назначение\",\n    ),\n    \"overrideDestinationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Использовать результаты сниффинга для переопределения целевого адреса\",\n    ),\n    \"overrideDns\": MessageLookupByLibrary.simpleMessage(\"Переопределить DNS\"),\n    \"overrideDnsDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Включить переопределение настроек DNS в конфигурации\",\n    ),\n    \"overrideExperimental\": MessageLookupByLibrary.simpleMessage(\n      \"Переопределить экспериментальное\",\n    ),\n    \"overrideExperimentalDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Включить переопределение экспериментальных настроек в конфигурации\",\n    ),\n    \"overrideInvalidTip\": MessageLookupByLibrary.simpleMessage(\n      \"Не действует в режиме скрипта\",\n    ),\n    \"overrideNtp\": MessageLookupByLibrary.simpleMessage(\"Переопределить NTP\"),\n    \"overrideNtpDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Включить переопределение настроек NTP в конфигурации\",\n    ),\n    \"overrideOriginRules\": MessageLookupByLibrary.simpleMessage(\n      \"Переопределить исходные\",\n    ),\n    \"overrideSniffer\": MessageLookupByLibrary.simpleMessage(\n      \"Переопределить Sniffer\",\n    ),\n    \"overrideSnifferDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Включить переопределение настроек Sniffer в конфигурации\",\n    ),\n    \"overrideTestUrl\": MessageLookupByLibrary.simpleMessage(\n      \"Переопределить URL теста\",\n    ),\n    \"overrideTunnel\": MessageLookupByLibrary.simpleMessage(\n      \"Переопределить туннель\",\n    ),\n    \"overrideTunnelDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Включить переопределение настроек туннеля в конфигурации\",\n    ),\n    \"packageListPermissionDenied\": MessageLookupByLibrary.simpleMessage(\n      \"Разрешение отклонено. Без доступа невозможно получить список приложений.\",\n    ),\n    \"packageListPermissionRequired\": MessageLookupByLibrary.simpleMessage(\n      \"Эта функция требует доступа к списку установленных приложений. Предоставить разрешение?\",\n    ),\n    \"palette\": MessageLookupByLibrary.simpleMessage(\"Палитра\"),\n    \"parsePureIp\": MessageLookupByLibrary.simpleMessage(\"Разбор чистых IP\"),\n    \"parsePureIpDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Разбирать соединения по чистому IP\",\n    ),\n    \"password\": MessageLookupByLibrary.simpleMessage(\"Пароль\"),\n    \"paste\": MessageLookupByLibrary.simpleMessage(\"Вставить\"),\n    \"pleaseBindWebDAV\": MessageLookupByLibrary.simpleMessage(\n      \"Привяжите WebDAV\",\n    ),\n    \"pleaseCloseSystemProxyFirst\": MessageLookupByLibrary.simpleMessage(\n      \"Сначала отключите системный прокси\",\n    ),\n    \"pleaseCloseTunFirst\": MessageLookupByLibrary.simpleMessage(\n      \"Сначала отключите виртуальный адаптер\",\n    ),\n    \"pleaseEnterScriptName\": MessageLookupByLibrary.simpleMessage(\n      \"Введите название скрипта\",\n    ),\n    \"pleaseInputAdminPassword\": MessageLookupByLibrary.simpleMessage(\n      \"Введите пароль администратора\",\n    ),\n    \"pleaseUploadFile\": MessageLookupByLibrary.simpleMessage(\"Загрузите файл\"),\n    \"pleaseUploadValidQrcode\": MessageLookupByLibrary.simpleMessage(\n      \"Загрузите корректный QR-код\",\n    ),\n    \"port\": MessageLookupByLibrary.simpleMessage(\"Порт\"),\n    \"portConflictTip\": MessageLookupByLibrary.simpleMessage(\n      \"Введите разные порты\",\n    ),\n    \"portTip\": m7,\n    \"powerSwitch\": MessageLookupByLibrary.simpleMessage(\"Переключатель\"),\n    \"preferH3Desc\": MessageLookupByLibrary.simpleMessage(\n      \"Приоритет HTTP/3 для DoH\",\n    ),\n    \"pressKeyboard\": MessageLookupByLibrary.simpleMessage(\"Нажмите клавиши\"),\n    \"preview\": MessageLookupByLibrary.simpleMessage(\"Предпросмотр\"),\n    \"profile\": MessageLookupByLibrary.simpleMessage(\"Профиль\"),\n    \"profileAutoUpdateIntervalInvalidValidationDesc\":\n        MessageLookupByLibrary.simpleMessage(\n          \"Введите корректный формат интервала\",\n        ),\n    \"profileAutoUpdateIntervalNullValidationDesc\":\n        MessageLookupByLibrary.simpleMessage(\"Введите интервал автообновления\"),\n    \"profileHasUpdate\": MessageLookupByLibrary.simpleMessage(\n      \"Конфигурация изменена. Отключить автообновление?\",\n    ),\n    \"profileNameNullValidationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Введите имя профиля\",\n    ),\n    \"profileParseErrorDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Ошибка разбора профиля\",\n    ),\n    \"profileUrlInvalidValidationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Введите корректный URL профиля\",\n    ),\n    \"profileUrlNullValidationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Введите URL профиля\",\n    ),\n    \"profiles\": MessageLookupByLibrary.simpleMessage(\"Профили\"),\n    \"profilesSort\": MessageLookupByLibrary.simpleMessage(\"Сортировка профилей\"),\n    \"progress\": MessageLookupByLibrary.simpleMessage(\"Прогресс\"),\n    \"project\": MessageLookupByLibrary.simpleMessage(\"Проект\"),\n    \"providers\": MessageLookupByLibrary.simpleMessage(\"Провайдеры\"),\n    \"proxies\": MessageLookupByLibrary.simpleMessage(\"Прокси\"),\n    \"proxiesSetting\": MessageLookupByLibrary.simpleMessage(\"Настройки прокси\"),\n    \"proxyChains\": MessageLookupByLibrary.simpleMessage(\"Цепочка прокси\"),\n    \"proxyGroup\": MessageLookupByLibrary.simpleMessage(\"Группа прокси\"),\n    \"proxyNameserver\": MessageLookupByLibrary.simpleMessage(\"DNS для прокси\"),\n    \"proxyNameserverDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Используется для разрешения доменов прокси\",\n    ),\n    \"proxyPort\": MessageLookupByLibrary.simpleMessage(\"Порт прокси\"),\n    \"proxyPortDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Установить порт прослушивания Clash\",\n    ),\n    \"proxyProviders\": MessageLookupByLibrary.simpleMessage(\"Провайдеры прокси\"),\n    \"pulse\": MessageLookupByLibrary.simpleMessage(\"Пульсация\"),\n    \"pureBlackMode\": MessageLookupByLibrary.simpleMessage(\"Чистый чёрный\"),\n    \"qrcode\": MessageLookupByLibrary.simpleMessage(\"QR-код\"),\n    \"qrcodeDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Сканировать QR для получения профиля\",\n    ),\n    \"quicGoDisableEcn\": MessageLookupByLibrary.simpleMessage(\n      \"Отключить ECN QUIC\",\n    ),\n    \"quicGoDisableEcnDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Отключить Explicit Congestion Notification для QUIC\",\n    ),\n    \"quicGoDisableGso\": MessageLookupByLibrary.simpleMessage(\n      \"Отключить GSO QUIC\",\n    ),\n    \"quicGoDisableGsoDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Отключить Generic Segmentation Offload для QUIC\",\n    ),\n    \"quicPortSniffer\": MessageLookupByLibrary.simpleMessage(\n      \"QUIC порты сниффера\",\n    ),\n    \"quickResponse\": MessageLookupByLibrary.simpleMessage(\"Быстрый отклик\"),\n    \"quickResponseDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Активно отключать соединения при изменении сети\",\n    ),\n    \"rainbowScheme\": MessageLookupByLibrary.simpleMessage(\"Радуга\"),\n    \"recovery\": MessageLookupByLibrary.simpleMessage(\"Восстановить\"),\n    \"recoveryAll\": MessageLookupByLibrary.simpleMessage(\"Все данные\"),\n    \"recoveryProfiles\": MessageLookupByLibrary.simpleMessage(\"Только профили\"),\n    \"recoveryStrategy\": MessageLookupByLibrary.simpleMessage(\n      \"Стратегия восстановления\",\n    ),\n    \"recoveryStrategy_compatible\": MessageLookupByLibrary.simpleMessage(\n      \"Совместимость\",\n    ),\n    \"recoveryStrategy_override\": MessageLookupByLibrary.simpleMessage(\n      \"Перезаписать\",\n    ),\n    \"recoverySuccess\": MessageLookupByLibrary.simpleMessage(\n      \"Восстановление успешно\",\n    ),\n    \"redirPort\": MessageLookupByLibrary.simpleMessage(\"Порт перенаправления\"),\n    \"redo\": MessageLookupByLibrary.simpleMessage(\"Повторить\"),\n    \"refreshAppList\": MessageLookupByLibrary.simpleMessage(\n      \"Обновить список приложений\",\n    ),\n    \"refreshAppListConfirm\": MessageLookupByLibrary.simpleMessage(\n      \"Обновить список приложений?\",\n    ),\n    \"regExp\": MessageLookupByLibrary.simpleMessage(\"Регулярное выражение\"),\n    \"remote\": MessageLookupByLibrary.simpleMessage(\"Удалённо\"),\n    \"remoteBackupDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Резервное копирование на WebDAV\",\n    ),\n    \"remoteDestination\": MessageLookupByLibrary.simpleMessage(\n      \"Удалённое назначение\",\n    ),\n    \"remoteRecoveryDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Восстановление с WebDAV\",\n    ),\n    \"remove\": MessageLookupByLibrary.simpleMessage(\"Удалить\"),\n    \"rename\": MessageLookupByLibrary.simpleMessage(\"Переименовать\"),\n    \"request\": MessageLookupByLibrary.simpleMessage(\"Запрос\"),\n    \"requests\": MessageLookupByLibrary.simpleMessage(\"Запросы\"),\n    \"requestsDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Просмотр недавних запросов\",\n    ),\n    \"reset\": MessageLookupByLibrary.simpleMessage(\"Сброс\"),\n    \"resetTip\": MessageLookupByLibrary.simpleMessage(\"Сбросить настройки?\"),\n    \"resources\": MessageLookupByLibrary.simpleMessage(\"Ресурсы\"),\n    \"resourcesDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Информация о внешних ресурсах\",\n    ),\n    \"respectRules\": MessageLookupByLibrary.simpleMessage(\"Следовать правилам\"),\n    \"respectRulesDesc\": MessageLookupByLibrary.simpleMessage(\n      \"DNS-соединения следуют правилам\",\n    ),\n    \"restart\": MessageLookupByLibrary.simpleMessage(\"Перезапуск\"),\n    \"restartCoreDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Перезапустить ядро вручную?\",\n    ),\n    \"restartCoreTitle\": MessageLookupByLibrary.simpleMessage(\"Перезапуск ядра\"),\n    \"restartTip\": MessageLookupByLibrary.simpleMessage(\n      \"Изменения вступят в силу после перезапуска TUN\",\n    ),\n    \"retry\": MessageLookupByLibrary.simpleMessage(\"Повторить\"),\n    \"rotatingCircle\": MessageLookupByLibrary.simpleMessage(\"Вращающийся круг\"),\n    \"ru\": MessageLookupByLibrary.simpleMessage(\"Русский\"),\n    \"rule\": MessageLookupByLibrary.simpleMessage(\"Правила\"),\n    \"ruleName\": MessageLookupByLibrary.simpleMessage(\"Имя правила\"),\n    \"ruleProviders\": MessageLookupByLibrary.simpleMessage(\"Провайдеры правил\"),\n    \"ruleTarget\": MessageLookupByLibrary.simpleMessage(\"Цель правила\"),\n    \"runTime\": MessageLookupByLibrary.simpleMessage(\"Время работы\"),\n    \"runtimeConfig\": MessageLookupByLibrary.simpleMessage(\"Конфигурация\"),\n    \"save\": MessageLookupByLibrary.simpleMessage(\"Сохранить\"),\n    \"saveChanges\": MessageLookupByLibrary.simpleMessage(\"Сохранить изменения?\"),\n    \"saveTip\": MessageLookupByLibrary.simpleMessage(\"Сохранить изменения?\"),\n    \"script\": MessageLookupByLibrary.simpleMessage(\"Скрипт\"),\n    \"scriptDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Настройка глобального скрипта переопределения\",\n    ),\n    \"search\": MessageLookupByLibrary.simpleMessage(\"Поиск\"),\n    \"seconds\": MessageLookupByLibrary.simpleMessage(\"секунд\"),\n    \"secretCopied\": MessageLookupByLibrary.simpleMessage(\n      \"Пароль скопирован в буфер обмена\",\n    ),\n    \"selectAll\": MessageLookupByLibrary.simpleMessage(\"Выбрать все\"),\n    \"selected\": MessageLookupByLibrary.simpleMessage(\"Выбрано\"),\n    \"selectedCountTitle\": m8,\n    \"serviceReady\": MessageLookupByLibrary.simpleMessage(\"Служба готова\"),\n    \"serviceRunning\": MessageLookupByLibrary.simpleMessage(\"Служба запущена\"),\n    \"settings\": MessageLookupByLibrary.simpleMessage(\"Настройки\"),\n    \"show\": MessageLookupByLibrary.simpleMessage(\"Показать\"),\n    \"shrink\": MessageLookupByLibrary.simpleMessage(\"Компактный\"),\n    \"silentLaunch\": MessageLookupByLibrary.simpleMessage(\"Тихий запуск\"),\n    \"silentLaunchDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Запуск в фоне без открытия окна\",\n    ),\n    \"size\": MessageLookupByLibrary.simpleMessage(\"Размер\"),\n    \"skipDomain\": MessageLookupByLibrary.simpleMessage(\"Пропустить домены\"),\n    \"skipDstAddress\": MessageLookupByLibrary.simpleMessage(\n      \"Пропустить IP назначения\",\n    ),\n    \"skipSrcAddress\": MessageLookupByLibrary.simpleMessage(\n      \"Пропустить IP источника\",\n    ),\n    \"smartAutoStop\": MessageLookupByLibrary.simpleMessage(\"Умная остановка\"),\n    \"smartAutoStopDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Останавливать прокси при подключении к заданной сети\",\n    ),\n    \"smartAutoStopServiceRunning\": MessageLookupByLibrary.simpleMessage(\n      \"Служба умной остановки работает\",\n    ),\n    \"smartDelayLaunch\": MessageLookupByLibrary.simpleMessage(\"Умная задержка\"),\n    \"smartDelayLaunchDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Запуск после успешного подключения к сети\",\n    ),\n    \"sniffer\": MessageLookupByLibrary.simpleMessage(\"Sniffer\"),\n    \"snifferAddressHint\": MessageLookupByLibrary.simpleMessage(\n      \"Один адрес на строку\",\n    ),\n    \"snifferDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Настройка сниффинга доменов\",\n    ),\n    \"snifferDomainHint\": MessageLookupByLibrary.simpleMessage(\n      \"Один домен на строку\",\n    ),\n    \"snifferPorts\": MessageLookupByLibrary.simpleMessage(\"Порты\"),\n    \"snifferPortsHint\": MessageLookupByLibrary.simpleMessage(\n      \"Например: 80, 8080-8880\",\n    ),\n    \"snifferStatus\": MessageLookupByLibrary.simpleMessage(\"Статус сниффера\"),\n    \"snifferStatusDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Включить службу сниффинга\",\n    ),\n    \"socksPort\": MessageLookupByLibrary.simpleMessage(\"Порт Socks\"),\n    \"sort\": MessageLookupByLibrary.simpleMessage(\"Сортировка\"),\n    \"source\": MessageLookupByLibrary.simpleMessage(\"Источник\"),\n    \"sourceIp\": MessageLookupByLibrary.simpleMessage(\"IP источника\"),\n    \"specialProxy\": MessageLookupByLibrary.simpleMessage(\"Специальный прокси\"),\n    \"specialRules\": MessageLookupByLibrary.simpleMessage(\"Специальные правила\"),\n    \"spinningLines\": MessageLookupByLibrary.simpleMessage(\"Вращающиеся линии\"),\n    \"stackMode\": MessageLookupByLibrary.simpleMessage(\"Режим стека\"),\n    \"standard\": MessageLookupByLibrary.simpleMessage(\"Стандартный\"),\n    \"start\": MessageLookupByLibrary.simpleMessage(\"Запуск\"),\n    \"startTest\": MessageLookupByLibrary.simpleMessage(\"Тест задержки\"),\n    \"startVpn\": MessageLookupByLibrary.simpleMessage(\"Запуск VPN\"),\n    \"status\": MessageLookupByLibrary.simpleMessage(\"Статус\"),\n    \"statusDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Использовать системный DNS при выключении\",\n    ),\n    \"stop\": MessageLookupByLibrary.simpleMessage(\"Остановка\"),\n    \"stopVpn\": MessageLookupByLibrary.simpleMessage(\"Остановка VPN\"),\n    \"storeFix\": MessageLookupByLibrary.simpleMessage(\"Исправление магазина\"),\n    \"storeFixDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Исправляет проблемы загрузки Google Play\",\n    ),\n    \"strictRoute\": MessageLookupByLibrary.simpleMessage(\n      \"Строгая маршрутизация\",\n    ),\n    \"strictRouteDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Использовать строгий режим маршрутизации TUN\",\n    ),\n    \"style\": MessageLookupByLibrary.simpleMessage(\"Стиль\"),\n    \"subRule\": MessageLookupByLibrary.simpleMessage(\"Подправило\"),\n    \"submit\": MessageLookupByLibrary.simpleMessage(\"Отправить\"),\n    \"success\": MessageLookupByLibrary.simpleMessage(\"Успех\"),\n    \"switchLabel\": MessageLookupByLibrary.simpleMessage(\"Переключатель\"),\n    \"switchToDomesticIp\": MessageLookupByLibrary.simpleMessage(\n      \"Получить локальный IP\",\n    ),\n    \"sync\": MessageLookupByLibrary.simpleMessage(\"Синхронизировать\"),\n    \"syncAll\": MessageLookupByLibrary.simpleMessage(\"Синхронизировать всё\"),\n    \"syncFailed\": MessageLookupByLibrary.simpleMessage(\"Ошибка синхронизации\"),\n    \"system\": MessageLookupByLibrary.simpleMessage(\"Система\"),\n    \"systemApp\": MessageLookupByLibrary.simpleMessage(\"Системные приложения\"),\n    \"systemFont\": MessageLookupByLibrary.simpleMessage(\"Системный шрифт\"),\n    \"systemProxy\": MessageLookupByLibrary.simpleMessage(\"Системный прокси\"),\n    \"systemProxyDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Настроить системный прокси\",\n    ),\n    \"tab\": MessageLookupByLibrary.simpleMessage(\"Вкладки\"),\n    \"tabAnimation\": MessageLookupByLibrary.simpleMessage(\"Анимация вкладок\"),\n    \"tabAnimationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Только для некоторых мобильных представлений\",\n    ),\n    \"tcpConcurrent\": MessageLookupByLibrary.simpleMessage(\"TCP параллельно\"),\n    \"tcpConcurrentDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Разрешить параллельные TCP-соединения\",\n    ),\n    \"testUrl\": MessageLookupByLibrary.simpleMessage(\"URL теста\"),\n    \"textScale\": MessageLookupByLibrary.simpleMessage(\"Масштаб текста\"),\n    \"theme\": MessageLookupByLibrary.simpleMessage(\"Тема\"),\n    \"themeColor\": MessageLookupByLibrary.simpleMessage(\"Цвет темы\"),\n    \"themeDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Настройка темы и иконок\",\n    ),\n    \"themeMode\": MessageLookupByLibrary.simpleMessage(\"Режим темы\"),\n    \"threeBounce\": MessageLookupByLibrary.simpleMessage(\"Прыгающие точки\"),\n    \"threeColumns\": MessageLookupByLibrary.simpleMessage(\"3 колонки\"),\n    \"threeInOut\": MessageLookupByLibrary.simpleMessage(\"Три точки\"),\n    \"tight\": MessageLookupByLibrary.simpleMessage(\"Компактный\"),\n    \"time\": MessageLookupByLibrary.simpleMessage(\"Время\"),\n    \"tip\": MessageLookupByLibrary.simpleMessage(\"Подсказка\"),\n    \"tlsPortSniffer\": MessageLookupByLibrary.simpleMessage(\n      \"TLS порты сниффера\",\n    ),\n    \"toggle\": MessageLookupByLibrary.simpleMessage(\"Переключить\"),\n    \"tonalSpotScheme\": MessageLookupByLibrary.simpleMessage(\"Тональный акцент\"),\n    \"tooManyRules\": MessageLookupByLibrary.simpleMessage(\"Максимум 2 правила\"),\n    \"tools\": MessageLookupByLibrary.simpleMessage(\"Дополнительно\"),\n    \"tproxyPort\": MessageLookupByLibrary.simpleMessage(\"Порт Tproxy\"),\n    \"trafficUsage\": MessageLookupByLibrary.simpleMessage(\"Трафик\"),\n    \"tryManualRefresh\": MessageLookupByLibrary.simpleMessage(\n      \"Попробуйте обновить вручную\",\n    ),\n    \"tun\": MessageLookupByLibrary.simpleMessage(\"Виртуальный адаптер\"),\n    \"tunDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Перехват всего трафика устройства\",\n    ),\n    \"tunEnableRequireAdmin\": MessageLookupByLibrary.simpleMessage(\n      \"Для включения виртуального адаптера требуются права администратора. Запустите программу от имени администратора.\",\n    ),\n    \"tunnel\": MessageLookupByLibrary.simpleMessage(\"Туннель\"),\n    \"tunnelAddress\": MessageLookupByLibrary.simpleMessage(\n      \"Адрес прослушивания\",\n    ),\n    \"tunnelAddressHint\": MessageLookupByLibrary.simpleMessage(\n      \"Например: 127.0.0.1:6553\",\n    ),\n    \"tunnelDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Использовать туннель перенаправления трафика\",\n    ),\n    \"tunnelList\": MessageLookupByLibrary.simpleMessage(\n      \"Список перенаправлений\",\n    ),\n    \"tunnelNetwork\": MessageLookupByLibrary.simpleMessage(\"Сетевой протокол\"),\n    \"tunnelNetworkHint\": MessageLookupByLibrary.simpleMessage(\n      \"Например: tcp, udp\",\n    ),\n    \"tunnelProxy\": MessageLookupByLibrary.simpleMessage(\"Имя прокси\"),\n    \"tunnelProxyHint\": MessageLookupByLibrary.simpleMessage(\n      \"Например: proxy (опционально)\",\n    ),\n    \"tunnelTarget\": MessageLookupByLibrary.simpleMessage(\"Целевой адрес\"),\n    \"tunnelTargetHint\": MessageLookupByLibrary.simpleMessage(\n      \"Например: 114.114.114.114:53\",\n    ),\n    \"twoColumns\": MessageLookupByLibrary.simpleMessage(\"2 колонки\"),\n    \"unableToUpdateCurrentProfileDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Невозможно обновить текущий профиль\",\n    ),\n    \"undo\": MessageLookupByLibrary.simpleMessage(\"Отменить\"),\n    \"unifiedDelay\": MessageLookupByLibrary.simpleMessage(\n      \"Унифицированная задержка\",\n    ),\n    \"unifiedDelayDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Убрать задержку рукопожатия и разбора\",\n    ),\n    \"unknown\": MessageLookupByLibrary.simpleMessage(\"Неизвестно\"),\n    \"unnamed\": MessageLookupByLibrary.simpleMessage(\"Без имени\"),\n    \"update\": MessageLookupByLibrary.simpleMessage(\"Обновить\"),\n    \"upload\": MessageLookupByLibrary.simpleMessage(\"Отправка\"),\n    \"url\": MessageLookupByLibrary.simpleMessage(\"URL\"),\n    \"urlDesc\": MessageLookupByLibrary.simpleMessage(\"Получить профиль по URL\"),\n    \"urlTip\": m9,\n    \"useHosts\": MessageLookupByLibrary.simpleMessage(\"Использовать hosts\"),\n    \"useSystemHosts\": MessageLookupByLibrary.simpleMessage(\n      \"Использовать системные hosts\",\n    ),\n    \"value\": MessageLookupByLibrary.simpleMessage(\"Значение\"),\n    \"vibrantScheme\": MessageLookupByLibrary.simpleMessage(\"Яркий\"),\n    \"view\": MessageLookupByLibrary.simpleMessage(\"Просмотр\"),\n    \"vpnDesc\": MessageLookupByLibrary.simpleMessage(\"Настройки VPN\"),\n    \"vpnEnableDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Автоматическая маршрутизация всего трафика через VpnService\",\n    ),\n    \"vpnSystemProxyDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Добавить HTTP-прокси к VPN\",\n    ),\n    \"vpnTip\": MessageLookupByLibrary.simpleMessage(\n      \"Перезапустите VPN для применения изменений\",\n    ),\n    \"wakelock\": MessageLookupByLibrary.simpleMessage(\"Блокировка сна\"),\n    \"wakelockDescription\": MessageLookupByLibrary.simpleMessage(\n      \"Эта функция не требует специальных разрешений, так как использует только блокировку пробуждения экрана, а не CPU. Приложение остаётся активным в фоне, экран не гаснет автоматически, что полезно в некоторых сценариях.\",\n    ),\n    \"wave\": MessageLookupByLibrary.simpleMessage(\"Волна\"),\n    \"webDAVConfiguration\": MessageLookupByLibrary.simpleMessage(\n      \"Настройки WebDAV\",\n    ),\n    \"whitelist\": MessageLookupByLibrary.simpleMessage(\"Белый список\"),\n    \"whitelistMode\": MessageLookupByLibrary.simpleMessage(\n      \"Режим белого списка\",\n    ),\n    \"writeToSystem\": MessageLookupByLibrary.simpleMessage(\"Записать в систему\"),\n    \"writeToSystemDesc\": MessageLookupByLibrary.simpleMessage(\n      \"Требуются права администратора\",\n    ),\n    \"years\": MessageLookupByLibrary.simpleMessage(\"лет\"),\n    \"zh_CN\": MessageLookupByLibrary.simpleMessage(\"Китайский (упрощённый)\"),\n    \"zh_TC\": MessageLookupByLibrary.simpleMessage(\"Китайский (традиционный)\"),\n  };\n}\n"
  },
  {
    "path": "lib/l10n/intl/messages_zh_CN.dart",
    "content": "// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart\n// This is a library that provides messages for a zh_CN locale. All the\n// messages from the main program should be duplicated here with the same\n// function name.\n\n// Ignore issues from commonly used lints in this file.\n// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new\n// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering\n// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases\n// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes\n// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes\n\nimport 'package:intl/intl.dart';\nimport 'package:intl/message_lookup_by_library.dart';\n\nfinal messages = new MessageLookup();\n\ntypedef String MessageIfAbsent(String messageStr, List<dynamic> args);\n\nclass MessageLookup extends MessageLookupByLibrary {\n  String get localeName => 'zh_CN';\n\n  static String m0(label) => \"确定删除选中的${label}吗？\";\n\n  static String m1(label) => \"确定删除当前${label}吗？\";\n\n  static String m2(label) => \"${label}详情\";\n\n  static String m3(label) => \"${label}不能为空\";\n\n  static String m4(label) => \"${label}当前已存在\";\n\n  static String m5(label) => \"暂无${label}\";\n\n  static String m6(label) => \"${label}必须为数字\";\n\n  static String m7(label) => \"${label} 必须在 1024 到 49151 之间\";\n\n  static String m8(count) => \"已选择 ${count} 项\";\n\n  static String m9(label) => \"${label}必须为URL\";\n\n  final messages = _notInlinedMessages(_notInlinedMessages);\n  static Map<String, Function> _notInlinedMessages(_) => <String, Function>{\n    \"about\": MessageLookupByLibrary.simpleMessage(\"关于\"),\n    \"accessControl\": MessageLookupByLibrary.simpleMessage(\"访问控制\"),\n    \"accessControlAllowDesc\": MessageLookupByLibrary.simpleMessage(\n      \"只允许选中的应用进入VPN\",\n    ),\n    \"accessControlDesc\": MessageLookupByLibrary.simpleMessage(\"配置应用访问代理\"),\n    \"accessControlNotAllowDesc\": MessageLookupByLibrary.simpleMessage(\n      \"选中的应用将被排除在VPN之外\",\n    ),\n    \"account\": MessageLookupByLibrary.simpleMessage(\"账号\"),\n    \"action\": MessageLookupByLibrary.simpleMessage(\"操作\"),\n    \"action_mode\": MessageLookupByLibrary.simpleMessage(\"切换模式\"),\n    \"action_proxy\": MessageLookupByLibrary.simpleMessage(\"系统代理\"),\n    \"action_start\": MessageLookupByLibrary.simpleMessage(\"启动/停止\"),\n    \"action_tun\": MessageLookupByLibrary.simpleMessage(\"虚拟网卡\"),\n    \"action_view\": MessageLookupByLibrary.simpleMessage(\"显示/隐藏\"),\n    \"add\": MessageLookupByLibrary.simpleMessage(\"添加\"),\n    \"addProfile\": MessageLookupByLibrary.simpleMessage(\"添加配置\"),\n    \"addRule\": MessageLookupByLibrary.simpleMessage(\"添加规则\"),\n    \"addTunnel\": MessageLookupByLibrary.simpleMessage(\"添加转发\"),\n    \"addedOriginRules\": MessageLookupByLibrary.simpleMessage(\"附加到原始规则\"),\n    \"address\": MessageLookupByLibrary.simpleMessage(\"地址\"),\n    \"addressHelp\": MessageLookupByLibrary.simpleMessage(\"WebDAV服务器地址\"),\n    \"addressTip\": MessageLookupByLibrary.simpleMessage(\"请输入有效的WebDAV地址\"),\n    \"adminAutoLaunch\": MessageLookupByLibrary.simpleMessage(\"管理员自启动\"),\n    \"adminAutoLaunchDesc\": MessageLookupByLibrary.simpleMessage(\"使用管理员模式开机自启动\"),\n    \"advancedSettings\": MessageLookupByLibrary.simpleMessage(\"进阶设置\"),\n    \"ago\": MessageLookupByLibrary.simpleMessage(\"前\"),\n    \"agree\": MessageLookupByLibrary.simpleMessage(\"同意\"),\n    \"allApps\": MessageLookupByLibrary.simpleMessage(\"所有应用\"),\n    \"allowBypass\": MessageLookupByLibrary.simpleMessage(\"允许绕过VPN\"),\n    \"allowBypassDesc\": MessageLookupByLibrary.simpleMessage(\"开启后部分应用可绕过VPN\"),\n    \"allowLan\": MessageLookupByLibrary.simpleMessage(\"局域网代理\"),\n    \"allowLanDesc\": MessageLookupByLibrary.simpleMessage(\"允许通过局域网访问代理\"),\n    \"alreadyInWhitelist\": MessageLookupByLibrary.simpleMessage(\"当前应用已在白名单内\"),\n    \"app\": MessageLookupByLibrary.simpleMessage(\"应用\"),\n    \"appAccessControl\": MessageLookupByLibrary.simpleMessage(\"应用访问控制\"),\n    \"appDesc\": MessageLookupByLibrary.simpleMessage(\"处理应用相关设置\"),\n    \"application\": MessageLookupByLibrary.simpleMessage(\"应用程序\"),\n    \"applicationDesc\": MessageLookupByLibrary.simpleMessage(\"修改应用程序设置\"),\n    \"auto\": MessageLookupByLibrary.simpleMessage(\"自动\"),\n    \"autoCheckUpdate\": MessageLookupByLibrary.simpleMessage(\"自动检查更新\"),\n    \"autoCheckUpdateDesc\": MessageLookupByLibrary.simpleMessage(\"应用启动时自动检查更新\"),\n    \"autoCloseConnections\": MessageLookupByLibrary.simpleMessage(\"自动关闭连接\"),\n    \"autoCloseConnectionsDesc\": MessageLookupByLibrary.simpleMessage(\n      \"切换节点后自动关闭连接\",\n    ),\n    \"autoLaunch\": MessageLookupByLibrary.simpleMessage(\"开机启动\"),\n    \"autoLaunchDesc\": MessageLookupByLibrary.simpleMessage(\"跟随系统自启动\"),\n    \"autoRun\": MessageLookupByLibrary.simpleMessage(\"自动连接\"),\n    \"autoRunDesc\": MessageLookupByLibrary.simpleMessage(\"应用打开后自动连接\"),\n    \"autoSetSystemDns\": MessageLookupByLibrary.simpleMessage(\"自动设置系统DNS\"),\n    \"autoUpdate\": MessageLookupByLibrary.simpleMessage(\"自动更新\"),\n    \"autoUpdateInterval\": MessageLookupByLibrary.simpleMessage(\"自动更新间隔（分钟）\"),\n    \"backup\": MessageLookupByLibrary.simpleMessage(\"备份\"),\n    \"backupAndRecovery\": MessageLookupByLibrary.simpleMessage(\"备份与恢复\"),\n    \"backupAndRecoveryDesc\": MessageLookupByLibrary.simpleMessage(\n      \"通过在线或本地文件同步数据\",\n    ),\n    \"backupSuccess\": MessageLookupByLibrary.simpleMessage(\"备份成功\"),\n    \"basicConfig\": MessageLookupByLibrary.simpleMessage(\"内核配置\"),\n    \"basicConfigDesc\": MessageLookupByLibrary.simpleMessage(\"全局修改内核配置\"),\n    \"batteryOptimization\": MessageLookupByLibrary.simpleMessage(\"电池优化\"),\n    \"batteryOptimizationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"请求安卓电池优化白名单权限\",\n    ),\n    \"bind\": MessageLookupByLibrary.simpleMessage(\"绑定\"),\n    \"blacklist\": MessageLookupByLibrary.simpleMessage(\"黑名单\"),\n    \"blacklistMode\": MessageLookupByLibrary.simpleMessage(\"黑名单模式\"),\n    \"bypassDomain\": MessageLookupByLibrary.simpleMessage(\"排除域名\"),\n    \"bypassDomainDesc\": MessageLookupByLibrary.simpleMessage(\"仅在系统代理启用时生效\"),\n    \"bypassPrivateRoute\": MessageLookupByLibrary.simpleMessage(\"绕过私有网络\"),\n    \"bypassPrivateRouteDesc\": MessageLookupByLibrary.simpleMessage(\n      \"自动绕过私有网络IP地址\",\n    ),\n    \"cacheAlgorithm\": MessageLookupByLibrary.simpleMessage(\"缓存算法\"),\n    \"cacheCorrupt\": MessageLookupByLibrary.simpleMessage(\"缓存已损坏，是否清空？\"),\n    \"cameraPermissionDenied\": MessageLookupByLibrary.simpleMessage(\"相机权限被拒绝\"),\n    \"cameraPermissionDesc\": MessageLookupByLibrary.simpleMessage(\n      \"扫描二维码需要相机权限，请在设置中授予相机权限。\",\n    ),\n    \"cancel\": MessageLookupByLibrary.simpleMessage(\"取消\"),\n    \"cancelFilterSystemApp\": MessageLookupByLibrary.simpleMessage(\"取消过滤系统应用\"),\n    \"cancelSelectAll\": MessageLookupByLibrary.simpleMessage(\"取消全选\"),\n    \"checkError\": MessageLookupByLibrary.simpleMessage(\"检测失败\"),\n    \"checkOrAddProfile\": MessageLookupByLibrary.simpleMessage(\"请先添加配置\"),\n    \"checkUpdate\": MessageLookupByLibrary.simpleMessage(\"检查更新\"),\n    \"checkUpdateError\": MessageLookupByLibrary.simpleMessage(\"当前应用已经是最新版了\"),\n    \"checking\": MessageLookupByLibrary.simpleMessage(\"检测中...\"),\n    \"circle\": MessageLookupByLibrary.simpleMessage(\"圆环流转\"),\n    \"clearCacheDesc\": MessageLookupByLibrary.simpleMessage(\n      \"是否需要清理FakeIP&DNS缓存？\",\n    ),\n    \"clearCacheTitle\": MessageLookupByLibrary.simpleMessage(\"清理缓存\"),\n    \"clearData\": MessageLookupByLibrary.simpleMessage(\"清除数据\"),\n    \"clipboard\": MessageLookupByLibrary.simpleMessage(\"剪切板\"),\n    \"clipboardDesc\": MessageLookupByLibrary.simpleMessage(\"自动获取剪切板订阅链接\"),\n    \"clipboardExport\": MessageLookupByLibrary.simpleMessage(\"导出剪贴板\"),\n    \"clipboardImport\": MessageLookupByLibrary.simpleMessage(\"剪贴板导入\"),\n    \"color\": MessageLookupByLibrary.simpleMessage(\"颜色\"),\n    \"colorSchemes\": MessageLookupByLibrary.simpleMessage(\"配色方案\"),\n    \"columns\": MessageLookupByLibrary.simpleMessage(\"列数\"),\n    \"compatible\": MessageLookupByLibrary.simpleMessage(\"兼容模式\"),\n    \"compatibleDesc\": MessageLookupByLibrary.simpleMessage(\n      \"开启将失去部分应用能力，获得全量的Clash的支持\",\n    ),\n    \"concurrencyLimit\": MessageLookupByLibrary.simpleMessage(\"并发限制\"),\n    \"concurrencyLimitDesc\": MessageLookupByLibrary.simpleMessage(\"延迟测试的最大并发数量\"),\n    \"confirm\": MessageLookupByLibrary.simpleMessage(\"确定\"),\n    \"connection\": MessageLookupByLibrary.simpleMessage(\"活跃连接\"),\n    \"connections\": MessageLookupByLibrary.simpleMessage(\"连接\"),\n    \"connectionsDesc\": MessageLookupByLibrary.simpleMessage(\"查看当前连接数据\"),\n    \"connectivity\": MessageLookupByLibrary.simpleMessage(\"连通性：\"),\n    \"contactMe\": MessageLookupByLibrary.simpleMessage(\"联系我\"),\n    \"content\": MessageLookupByLibrary.simpleMessage(\"内容\"),\n    \"contentScheme\": MessageLookupByLibrary.simpleMessage(\"内容主题\"),\n    \"controlSecret\": MessageLookupByLibrary.simpleMessage(\"控制密码\"),\n    \"controlSecretDesc\": MessageLookupByLibrary.simpleMessage(\n      \"RESTful API的访问密码\",\n    ),\n    \"copy\": MessageLookupByLibrary.simpleMessage(\"复制\"),\n    \"copyEnvVar\": MessageLookupByLibrary.simpleMessage(\"复制环境变量\"),\n    \"copyLink\": MessageLookupByLibrary.simpleMessage(\"复制链接\"),\n    \"copySuccess\": MessageLookupByLibrary.simpleMessage(\"复制成功\"),\n    \"core\": MessageLookupByLibrary.simpleMessage(\"内核\"),\n    \"coreConnected\": MessageLookupByLibrary.simpleMessage(\"已连接\"),\n    \"coreInfo\": MessageLookupByLibrary.simpleMessage(\"内核信息\"),\n    \"coreSuspended\": MessageLookupByLibrary.simpleMessage(\"已挂起\"),\n    \"country\": MessageLookupByLibrary.simpleMessage(\"区域\"),\n    \"crashTest\": MessageLookupByLibrary.simpleMessage(\"崩溃测试\"),\n    \"create\": MessageLookupByLibrary.simpleMessage(\"创建\"),\n    \"creationTime\": MessageLookupByLibrary.simpleMessage(\"创建时间\"),\n    \"custom\": MessageLookupByLibrary.simpleMessage(\"自定义\"),\n    \"customUrl\": MessageLookupByLibrary.simpleMessage(\"自定义URL\"),\n    \"cut\": MessageLookupByLibrary.simpleMessage(\"剪切\"),\n    \"dark\": MessageLookupByLibrary.simpleMessage(\"深色\"),\n    \"dashboard\": MessageLookupByLibrary.simpleMessage(\"首页\"),\n    \"days\": MessageLookupByLibrary.simpleMessage(\"天\"),\n    \"defaultNameserver\": MessageLookupByLibrary.simpleMessage(\"默认域名服务器\"),\n    \"defaultNameserverDesc\": MessageLookupByLibrary.simpleMessage(\"用于解析DNS服务器\"),\n    \"defaultSort\": MessageLookupByLibrary.simpleMessage(\"按默认排序\"),\n    \"defaultText\": MessageLookupByLibrary.simpleMessage(\"默认\"),\n    \"delay\": MessageLookupByLibrary.simpleMessage(\"延迟\"),\n    \"delayAnimation\": MessageLookupByLibrary.simpleMessage(\"延迟动画\"),\n    \"delayAnimationDesc\": MessageLookupByLibrary.simpleMessage(\"自定义测试过程中的动画显示\"),\n    \"delaySort\": MessageLookupByLibrary.simpleMessage(\"按延迟排序\"),\n    \"delete\": MessageLookupByLibrary.simpleMessage(\"删除\"),\n    \"deleteMultipTip\": m0,\n    \"deleteTip\": m1,\n    \"deleteTunnel\": MessageLookupByLibrary.simpleMessage(\"删除转发\"),\n    \"desc\": MessageLookupByLibrary.simpleMessage(\n      \"Bettbox基于强大灵活的Mihomo(Clash.Meta)代理内核，致力于更好的体验，Forked form FlClash，Better Experience, Out of the box\",\n    ),\n    \"destination\": MessageLookupByLibrary.simpleMessage(\"目标地址\"),\n    \"destinationGeoIP\": MessageLookupByLibrary.simpleMessage(\"目标地理定位\"),\n    \"destinationIPASN\": MessageLookupByLibrary.simpleMessage(\"目标IP ASN\"),\n    \"details\": m2,\n    \"detectionTip\": MessageLookupByLibrary.simpleMessage(\"依赖第三方api，仅供参考\"),\n    \"developerMode\": MessageLookupByLibrary.simpleMessage(\"开发者模式\"),\n    \"developerModeEnableTip\": MessageLookupByLibrary.simpleMessage(\"开发者模式已启用。\"),\n    \"dialerIp4pConvert\": MessageLookupByLibrary.simpleMessage(\"启用拨号IP4P地址转换\"),\n    \"dialerIp4pConvertDesc\": MessageLookupByLibrary.simpleMessage(\n      \"启用拨号器的 IP4P 地址转换功能\",\n    ),\n    \"direct\": MessageLookupByLibrary.simpleMessage(\"直连\"),\n    \"directNameserver\": MessageLookupByLibrary.simpleMessage(\"直连域名服务器\"),\n    \"directNameserverDesc\": MessageLookupByLibrary.simpleMessage(\"用于解析直连出口的域名\"),\n    \"directNameserverFollowPolicy\": MessageLookupByLibrary.simpleMessage(\n      \"直连DNS遵循规则\",\n    ),\n    \"disableQuic\": MessageLookupByLibrary.simpleMessage(\"禁用QUIC\"),\n    \"disableQuicDesc\": MessageLookupByLibrary.simpleMessage(\"禁用QUIC以解决特定网络问题\"),\n    \"disclaimer\": MessageLookupByLibrary.simpleMessage(\"免责声明\"),\n    \"disclaimerDesc\": MessageLookupByLibrary.simpleMessage(\n      \"本软件为开源免费软件，仅供学习交流等非商业性质的个人测试使用，代理服务商的行为均与本软件无关，同意声明代表您已完全知晓并确认了这一点，如不同意，请选择退出！\",\n    ),\n    \"discoverNewVersion\": MessageLookupByLibrary.simpleMessage(\"发现新版本\"),\n    \"discovery\": MessageLookupByLibrary.simpleMessage(\"发现新版本\"),\n    \"dnsDesc\": MessageLookupByLibrary.simpleMessage(\"更新DNS相关设置\"),\n    \"dnsHijack\": MessageLookupByLibrary.simpleMessage(\"DNS劫持\"),\n    \"dnsHijackDesc\": MessageLookupByLibrary.simpleMessage(\"将解析导入内部DNS模块\"),\n    \"dnsMode\": MessageLookupByLibrary.simpleMessage(\"DNS模式\"),\n    \"doYouWantToPass\": MessageLookupByLibrary.simpleMessage(\"是否要通过\"),\n    \"domain\": MessageLookupByLibrary.simpleMessage(\"域名\"),\n    \"doubleBounce\": MessageLookupByLibrary.simpleMessage(\"双重弹奏\"),\n    \"download\": MessageLookupByLibrary.simpleMessage(\"下载\"),\n    \"dozeSuspend\": MessageLookupByLibrary.simpleMessage(\"休眠支持\"),\n    \"dozeSuspendDesc\": MessageLookupByLibrary.simpleMessage(\"开启后同步系统Doze休眠模式\"),\n    \"edit\": MessageLookupByLibrary.simpleMessage(\"编辑\"),\n    \"editTunnel\": MessageLookupByLibrary.simpleMessage(\"编辑转发\"),\n    \"emptyTip\": m3,\n    \"en\": MessageLookupByLibrary.simpleMessage(\"英语\"),\n    \"enableCrashReport\": MessageLookupByLibrary.simpleMessage(\"应用崩溃分析\"),\n    \"enableCrashReportDesc\": MessageLookupByLibrary.simpleMessage(\n      \"必要时上传应用崩溃日志\",\n    ),\n    \"enableOverride\": MessageLookupByLibrary.simpleMessage(\"启用覆写\"),\n    \"endpointIndependentNat\": MessageLookupByLibrary.simpleMessage(\"NAT增强\"),\n    \"endpointIndependentNatDesc\": MessageLookupByLibrary.simpleMessage(\n      \"启用独立于端点的NAT\",\n    ),\n    \"entries\": MessageLookupByLibrary.simpleMessage(\"个条目\"),\n    \"exclude\": MessageLookupByLibrary.simpleMessage(\"后台隐藏\"),\n    \"excludeChina\": MessageLookupByLibrary.simpleMessage(\"排除国内\"),\n    \"excludeChinaDesc\": MessageLookupByLibrary.simpleMessage(\n      \"放行中国QUIC流量而非全部禁用\",\n    ),\n    \"excludeDesc\": MessageLookupByLibrary.simpleMessage(\"从最近任务中隐藏应用\"),\n    \"existsTip\": m4,\n    \"exit\": MessageLookupByLibrary.simpleMessage(\"退出\"),\n    \"expand\": MessageLookupByLibrary.simpleMessage(\"标准\"),\n    \"experimental\": MessageLookupByLibrary.simpleMessage(\"Experimental\"),\n    \"experimentalDesc\": MessageLookupByLibrary.simpleMessage(\"实验性配置请谨慎使用\"),\n    \"expirationTime\": MessageLookupByLibrary.simpleMessage(\"到期时间\"),\n    \"exportFile\": MessageLookupByLibrary.simpleMessage(\"导出文件\"),\n    \"exportLogs\": MessageLookupByLibrary.simpleMessage(\"导出日志\"),\n    \"exportSuccess\": MessageLookupByLibrary.simpleMessage(\"导出成功\"),\n    \"expressiveScheme\": MessageLookupByLibrary.simpleMessage(\"表现力\"),\n    \"externalController\": MessageLookupByLibrary.simpleMessage(\"外部控制\"),\n    \"externalControllerDesc\": MessageLookupByLibrary.simpleMessage(\n      \"通过在线端口控制内核\",\n    ),\n    \"externalLink\": MessageLookupByLibrary.simpleMessage(\"外部链接\"),\n    \"externalResources\": MessageLookupByLibrary.simpleMessage(\"外部资源\"),\n    \"fadingCircle\": MessageLookupByLibrary.simpleMessage(\"环影隐渐\"),\n    \"fadingFour\": MessageLookupByLibrary.simpleMessage(\"四方烁动\"),\n    \"fakeIpFilterMode\": MessageLookupByLibrary.simpleMessage(\"FakeIP过滤模式\"),\n    \"fakeIpFilterModeDesc\": MessageLookupByLibrary.simpleMessage(\n      \"指定FakeIP过滤模式\",\n    ),\n    \"fakeipFilter\": MessageLookupByLibrary.simpleMessage(\"FakeIP过滤列表\"),\n    \"fakeipRange\": MessageLookupByLibrary.simpleMessage(\"FakeIP范围\"),\n    \"fakeipRangeV6\": MessageLookupByLibrary.simpleMessage(\"FakeIPv6范围\"),\n    \"fakeipTtl\": MessageLookupByLibrary.simpleMessage(\"FakeIP有效时间\"),\n    \"fallback\": MessageLookupByLibrary.simpleMessage(\"Fallback\"),\n    \"fallbackDesc\": MessageLookupByLibrary.simpleMessage(\"一般情况下使用境外DNS\"),\n    \"fallbackFilter\": MessageLookupByLibrary.simpleMessage(\"Fallback过滤\"),\n    \"fcmOptimization\": MessageLookupByLibrary.simpleMessage(\"FCM优化\"),\n    \"fcmOptimizationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"增强FCM直连时的网络稳定性\",\n    ),\n    \"fcmTip\": MessageLookupByLibrary.simpleMessage(\n      \"FCM连接和支持取决于设备本身，显示结果仅供参考。因系统权限原因，您需要关闭网络中的\\\"允许绕过VPN\\\"选项，以获得更加准确的结果\",\n    ),\n    \"fidelityScheme\": MessageLookupByLibrary.simpleMessage(\"高保真\"),\n    \"file\": MessageLookupByLibrary.simpleMessage(\"文件\"),\n    \"fileDesc\": MessageLookupByLibrary.simpleMessage(\"直接上传配置文件\"),\n    \"fileIsUpdate\": MessageLookupByLibrary.simpleMessage(\"文件有修改，是否保存修改\"),\n    \"filterSystemApp\": MessageLookupByLibrary.simpleMessage(\"过滤系统应用\"),\n    \"findProcessMode\": MessageLookupByLibrary.simpleMessage(\"查找进程\"),\n    \"findProcessModeDesc\": MessageLookupByLibrary.simpleMessage(\"开启后会将可以查找进程\"),\n    \"fontFamily\": MessageLookupByLibrary.simpleMessage(\"字体\"),\n    \"forceDnsMapping\": MessageLookupByLibrary.simpleMessage(\"强制 DNS 映射\"),\n    \"forceDnsMappingDesc\": MessageLookupByLibrary.simpleMessage(\n      \"强制将 DNS 查询结果映射到连接\",\n    ),\n    \"forceDomain\": MessageLookupByLibrary.simpleMessage(\"强制嗅探域名\"),\n    \"forceGCDesc\": MessageLookupByLibrary.simpleMessage(\n      \"是否进行强制内核垃圾回收？实验性功能，请谨慎使用\",\n    ),\n    \"forceGCTitle\": MessageLookupByLibrary.simpleMessage(\"强制垃圾回收\"),\n    \"formatError\": MessageLookupByLibrary.simpleMessage(\"请检查格式是否正确\"),\n    \"fourColumns\": MessageLookupByLibrary.simpleMessage(\"四列\"),\n    \"fruitSaladScheme\": MessageLookupByLibrary.simpleMessage(\"果缤纷\"),\n    \"general\": MessageLookupByLibrary.simpleMessage(\"常规\"),\n    \"generalDesc\": MessageLookupByLibrary.simpleMessage(\"修改通用设置\"),\n    \"generateSecret\": MessageLookupByLibrary.simpleMessage(\"生成\"),\n    \"geoData\": MessageLookupByLibrary.simpleMessage(\"地理数据\"),\n    \"geodataLoader\": MessageLookupByLibrary.simpleMessage(\"GEO节能\"),\n    \"geodataLoaderDesc\": MessageLookupByLibrary.simpleMessage(\"开启后使用GEO低内存加载器\"),\n    \"geoipCode\": MessageLookupByLibrary.simpleMessage(\"Geoip代码\"),\n    \"getOriginRules\": MessageLookupByLibrary.simpleMessage(\"获取原始规则\"),\n    \"global\": MessageLookupByLibrary.simpleMessage(\"全局\"),\n    \"go\": MessageLookupByLibrary.simpleMessage(\"前往\"),\n    \"goDownload\": MessageLookupByLibrary.simpleMessage(\"前往下载\"),\n    \"harmonyFont\": MessageLookupByLibrary.simpleMessage(\"鸿蒙字体\"),\n    \"harmonyFontDesc\": MessageLookupByLibrary.simpleMessage(\n      \"使用优化的HarmonyOS Sans\",\n    ),\n    \"hasCacheChange\": MessageLookupByLibrary.simpleMessage(\"是否缓存修改\"),\n    \"healthCheckTimeout\": MessageLookupByLibrary.simpleMessage(\"超时时间\"),\n    \"healthCheckTimeoutDesc\": MessageLookupByLibrary.simpleMessage(\n      \"节点健康检查超时时间\",\n    ),\n    \"highRefreshRate\": MessageLookupByLibrary.simpleMessage(\"高刷新率\"),\n    \"highRefreshRateDesc\": MessageLookupByLibrary.simpleMessage(\"启用设备最高刷新率支持\"),\n    \"host\": MessageLookupByLibrary.simpleMessage(\"主机\"),\n    \"hostsDesc\": MessageLookupByLibrary.simpleMessage(\"追加当前配置Hosts\"),\n    \"hotkeyConflict\": MessageLookupByLibrary.simpleMessage(\"快捷键冲突\"),\n    \"hotkeyManagement\": MessageLookupByLibrary.simpleMessage(\"快捷键管理\"),\n    \"hotkeyManagementDesc\": MessageLookupByLibrary.simpleMessage(\"使用键盘控制应用程序\"),\n    \"hours\": MessageLookupByLibrary.simpleMessage(\"小时\"),\n    \"httpPortSniffer\": MessageLookupByLibrary.simpleMessage(\"HTTP 端口嗅探\"),\n    \"icmpForwarding\": MessageLookupByLibrary.simpleMessage(\"ICMP转发\"),\n    \"icmpForwardingDesc\": MessageLookupByLibrary.simpleMessage(\n      \"开启后将支持ICMP Ping\",\n    ),\n    \"icon\": MessageLookupByLibrary.simpleMessage(\"图片\"),\n    \"iconConfiguration\": MessageLookupByLibrary.simpleMessage(\"图片配置\"),\n    \"iconStyle\": MessageLookupByLibrary.simpleMessage(\"图标样式\"),\n    \"import\": MessageLookupByLibrary.simpleMessage(\"导入\"),\n    \"importFailed\": MessageLookupByLibrary.simpleMessage(\"导入失败\"),\n    \"importFile\": MessageLookupByLibrary.simpleMessage(\"通过文件导入\"),\n    \"importFromCode\": MessageLookupByLibrary.simpleMessage(\"通过代码导入\"),\n    \"importFromURL\": MessageLookupByLibrary.simpleMessage(\"从URL导入\"),\n    \"importUrl\": MessageLookupByLibrary.simpleMessage(\"通过URL导入\"),\n    \"infiniteTime\": MessageLookupByLibrary.simpleMessage(\"长期有效\"),\n    \"init\": MessageLookupByLibrary.simpleMessage(\"初始化\"),\n    \"inputCorrectHotkey\": MessageLookupByLibrary.simpleMessage(\"请输入正确的快捷键\"),\n    \"intelligentSelected\": MessageLookupByLibrary.simpleMessage(\"智能选择\"),\n    \"internet\": MessageLookupByLibrary.simpleMessage(\"互联网\"),\n    \"interval\": MessageLookupByLibrary.simpleMessage(\"间隔\"),\n    \"intranetIP\": MessageLookupByLibrary.simpleMessage(\"内网 IP\"),\n    \"invalidIpFormat\": MessageLookupByLibrary.simpleMessage(\"无效的IP或CIDR格式\"),\n    \"ipClickBehavior\": MessageLookupByLibrary.simpleMessage(\"显示切换\"),\n    \"ipPrivacyProtection\": MessageLookupByLibrary.simpleMessage(\"隐藏IP显示\"),\n    \"ipcidr\": MessageLookupByLibrary.simpleMessage(\"IP/掩码\"),\n    \"ipv6Desc\": MessageLookupByLibrary.simpleMessage(\"开启后将可以接收IPv6流量\"),\n    \"ipv6InboundDesc\": MessageLookupByLibrary.simpleMessage(\"允许IPv6入站\"),\n    \"ja\": MessageLookupByLibrary.simpleMessage(\"日语\"),\n    \"just\": MessageLookupByLibrary.simpleMessage(\"刚刚\"),\n    \"keepAliveIntervalDesc\": MessageLookupByLibrary.simpleMessage(\"TCP保持活动间隔\"),\n    \"key\": MessageLookupByLibrary.simpleMessage(\"键\"),\n    \"language\": MessageLookupByLibrary.simpleMessage(\"语言\"),\n    \"layout\": MessageLookupByLibrary.simpleMessage(\"布局\"),\n    \"light\": MessageLookupByLibrary.simpleMessage(\"浅色\"),\n    \"lightIcon\": MessageLookupByLibrary.simpleMessage(\"丹青留白\"),\n    \"lightIconDesc\": MessageLookupByLibrary.simpleMessage(\"手动切换浅色系桌面应用图标\"),\n    \"list\": MessageLookupByLibrary.simpleMessage(\"列表\"),\n    \"listen\": MessageLookupByLibrary.simpleMessage(\"监听\"),\n    \"local\": MessageLookupByLibrary.simpleMessage(\"本地\"),\n    \"localBackupDesc\": MessageLookupByLibrary.simpleMessage(\"备份数据到本地\"),\n    \"localRecoveryDesc\": MessageLookupByLibrary.simpleMessage(\"通过文件恢复数据\"),\n    \"log\": MessageLookupByLibrary.simpleMessage(\"日志\"),\n    \"logLevel\": MessageLookupByLibrary.simpleMessage(\"日志等级\"),\n    \"logcat\": MessageLookupByLibrary.simpleMessage(\"日志捕获\"),\n    \"logcatDesc\": MessageLookupByLibrary.simpleMessage(\"开启后将会显示日志入口\"),\n    \"logs\": MessageLookupByLibrary.simpleMessage(\"日志\"),\n    \"logsDesc\": MessageLookupByLibrary.simpleMessage(\"查看日志捕获记录\"),\n    \"logsTest\": MessageLookupByLibrary.simpleMessage(\"日志测试\"),\n    \"loopback\": MessageLookupByLibrary.simpleMessage(\"回环解锁工具\"),\n    \"loopbackDesc\": MessageLookupByLibrary.simpleMessage(\"用于UWP回环解锁\"),\n    \"loose\": MessageLookupByLibrary.simpleMessage(\"宽松\"),\n    \"manualRefreshIp\": MessageLookupByLibrary.simpleMessage(\"重新获取IP\"),\n    \"memoryInfo\": MessageLookupByLibrary.simpleMessage(\"内存信息\"),\n    \"messageTest\": MessageLookupByLibrary.simpleMessage(\"消息测试\"),\n    \"messageTestTip\": MessageLookupByLibrary.simpleMessage(\"这是一条消息。\"),\n    \"min\": MessageLookupByLibrary.simpleMessage(\"最小\"),\n    \"minimizeOnExit\": MessageLookupByLibrary.simpleMessage(\"退出最小化\"),\n    \"minimizeOnExitDesc\": MessageLookupByLibrary.simpleMessage(\"修改系统默认退出事件\"),\n    \"minutes\": MessageLookupByLibrary.simpleMessage(\"分钟\"),\n    \"mixedPort\": MessageLookupByLibrary.simpleMessage(\"混合端口\"),\n    \"mode\": MessageLookupByLibrary.simpleMessage(\"模式\"),\n    \"monochromeScheme\": MessageLookupByLibrary.simpleMessage(\"单色\"),\n    \"months\": MessageLookupByLibrary.simpleMessage(\"月\"),\n    \"more\": MessageLookupByLibrary.simpleMessage(\"查看\"),\n    \"name\": MessageLookupByLibrary.simpleMessage(\"名称\"),\n    \"nameSort\": MessageLookupByLibrary.simpleMessage(\"按名称排序\"),\n    \"nameserver\": MessageLookupByLibrary.simpleMessage(\"域名服务器\"),\n    \"nameserverDesc\": MessageLookupByLibrary.simpleMessage(\"用于解析域名\"),\n    \"nameserverPolicy\": MessageLookupByLibrary.simpleMessage(\"域名服务器策略\"),\n    \"nameserverPolicyDesc\": MessageLookupByLibrary.simpleMessage(\"指定对应域名服务器策略\"),\n    \"navBarHapticFeedback\": MessageLookupByLibrary.simpleMessage(\"触感反馈\"),\n    \"navBarHapticFeedbackDesc\": MessageLookupByLibrary.simpleMessage(\n      \"底部导航栏切换震动反馈\",\n    ),\n    \"network\": MessageLookupByLibrary.simpleMessage(\"网络\"),\n    \"networkDesc\": MessageLookupByLibrary.simpleMessage(\"修改网络相关设置\"),\n    \"networkDetection\": MessageLookupByLibrary.simpleMessage(\"网络检测\"),\n    \"networkFix\": MessageLookupByLibrary.simpleMessage(\"网络修复\"),\n    \"networkFixDesc\": MessageLookupByLibrary.simpleMessage(\n      \"修复Windows网络检测地球图标问题\",\n    ),\n    \"networkMatch\": MessageLookupByLibrary.simpleMessage(\"网络匹配\"),\n    \"networkMatchHint\": MessageLookupByLibrary.simpleMessage(\n      \"输入IP或CIDR，最多2个，以逗号分隔\",\n    ),\n    \"networkSpeed\": MessageLookupByLibrary.simpleMessage(\"网络速度\"),\n    \"networkType\": MessageLookupByLibrary.simpleMessage(\"网络类型\"),\n    \"neutralScheme\": MessageLookupByLibrary.simpleMessage(\"中性\"),\n    \"noAnimation\": MessageLookupByLibrary.simpleMessage(\"默认\"),\n    \"noData\": MessageLookupByLibrary.simpleMessage(\"暂无数据\"),\n    \"noHotKey\": MessageLookupByLibrary.simpleMessage(\"暂无快捷键\"),\n    \"noIcon\": MessageLookupByLibrary.simpleMessage(\"无图标\"),\n    \"noInfo\": MessageLookupByLibrary.simpleMessage(\"暂无信息\"),\n    \"noMoreInfoDesc\": MessageLookupByLibrary.simpleMessage(\"暂无更多信息\"),\n    \"noNetwork\": MessageLookupByLibrary.simpleMessage(\"无网络\"),\n    \"noNetworkApp\": MessageLookupByLibrary.simpleMessage(\"无网络应用\"),\n    \"noProxy\": MessageLookupByLibrary.simpleMessage(\"暂无代理\"),\n    \"noProxyDesc\": MessageLookupByLibrary.simpleMessage(\"请创建配置或者添加有效配置文件\"),\n    \"noResolve\": MessageLookupByLibrary.simpleMessage(\"不解析IP\"),\n    \"noStatusAvailable\": MessageLookupByLibrary.simpleMessage(\"未获取到状态\"),\n    \"nodeExclusion\": MessageLookupByLibrary.simpleMessage(\"节点排除\"),\n    \"nodeExclusionDesc\": MessageLookupByLibrary.simpleMessage(\"排除所有匹配到的节点\"),\n    \"nodeExclusionPlaceholder\": MessageLookupByLibrary.simpleMessage(\n      \"HK|香港|🇭🇰\",\n    ),\n    \"none\": MessageLookupByLibrary.simpleMessage(\"无\"),\n    \"notRecommended\": MessageLookupByLibrary.simpleMessage(\"不推荐\"),\n    \"notSelectedTip\": MessageLookupByLibrary.simpleMessage(\"当前代理组无法选中\"),\n    \"ntp\": MessageLookupByLibrary.simpleMessage(\"NTP\"),\n    \"ntpDesc\": MessageLookupByLibrary.simpleMessage(\"使用NTP时间服务\"),\n    \"ntpInterval\": MessageLookupByLibrary.simpleMessage(\"更新时间\"),\n    \"ntpPort\": MessageLookupByLibrary.simpleMessage(\"端口\"),\n    \"ntpServer\": MessageLookupByLibrary.simpleMessage(\"服务器\"),\n    \"ntpStatus\": MessageLookupByLibrary.simpleMessage(\"状态\"),\n    \"ntpStatusDesc\": MessageLookupByLibrary.simpleMessage(\"开启NTP时间服务\"),\n    \"nullProfileDesc\": MessageLookupByLibrary.simpleMessage(\"没有配置文件,请先添加配置文件\"),\n    \"nullTip\": m5,\n    \"numberTip\": m6,\n    \"oneColumn\": MessageLookupByLibrary.simpleMessage(\"一列\"),\n    \"onlinePanel\": MessageLookupByLibrary.simpleMessage(\"在线面板\"),\n    \"onlyIcon\": MessageLookupByLibrary.simpleMessage(\"仅图标\"),\n    \"onlyOtherApps\": MessageLookupByLibrary.simpleMessage(\"仅第三方应用\"),\n    \"onlyStatisticsProxy\": MessageLookupByLibrary.simpleMessage(\"代理流量统计\"),\n    \"onlyStatisticsProxyDesc\": MessageLookupByLibrary.simpleMessage(\n      \"开启后将只统计代理流量\",\n    ),\n    \"openDashboard\": MessageLookupByLibrary.simpleMessage(\"打开 Zashboard\"),\n    \"openSettings\": MessageLookupByLibrary.simpleMessage(\"打开设置\"),\n    \"options\": MessageLookupByLibrary.simpleMessage(\"选项\"),\n    \"other\": MessageLookupByLibrary.simpleMessage(\"其他\"),\n    \"otherContributors\": MessageLookupByLibrary.simpleMessage(\"其他贡献者\"),\n    \"otherSettings\": MessageLookupByLibrary.simpleMessage(\"增强工具\"),\n    \"otherSettingsDesc\": MessageLookupByLibrary.simpleMessage(\"修改增强工具设置\"),\n    \"outboundMode\": MessageLookupByLibrary.simpleMessage(\"出站模式\"),\n    \"override\": MessageLookupByLibrary.simpleMessage(\"覆写\"),\n    \"overrideDesc\": MessageLookupByLibrary.simpleMessage(\"覆写代理相关配置\"),\n    \"overrideDestination\": MessageLookupByLibrary.simpleMessage(\"覆盖目标地址\"),\n    \"overrideDestinationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"使用嗅探结果覆盖连接目标地址\",\n    ),\n    \"overrideDns\": MessageLookupByLibrary.simpleMessage(\"覆写DNS\"),\n    \"overrideDnsDesc\": MessageLookupByLibrary.simpleMessage(\"开启后将覆盖配置中的DNS选项\"),\n    \"overrideExperimental\": MessageLookupByLibrary.simpleMessage(\n      \"覆写Experimental\",\n    ),\n    \"overrideExperimentalDesc\": MessageLookupByLibrary.simpleMessage(\n      \"开启后将覆盖配置中的实验性配置\",\n    ),\n    \"overrideInvalidTip\": MessageLookupByLibrary.simpleMessage(\"在脚本模式下不生效\"),\n    \"overrideNtp\": MessageLookupByLibrary.simpleMessage(\"覆写NTP\"),\n    \"overrideNtpDesc\": MessageLookupByLibrary.simpleMessage(\"开启后将覆盖配置中的NTP选项\"),\n    \"overrideOriginRules\": MessageLookupByLibrary.simpleMessage(\"覆盖原始规则\"),\n    \"overrideSniffer\": MessageLookupByLibrary.simpleMessage(\"覆写Sniffer\"),\n    \"overrideSnifferDesc\": MessageLookupByLibrary.simpleMessage(\n      \"开启后将覆盖配置中的Sniffer选项\",\n    ),\n    \"overrideTestUrl\": MessageLookupByLibrary.simpleMessage(\"覆盖配置\"),\n    \"overrideTunnel\": MessageLookupByLibrary.simpleMessage(\"覆写Tunnel\"),\n    \"overrideTunnelDesc\": MessageLookupByLibrary.simpleMessage(\n      \"开启后将覆盖配置中的Tunnel选项\",\n    ),\n    \"packageListPermissionDenied\": MessageLookupByLibrary.simpleMessage(\n      \"权限被拒绝。没有权限无法访问应用列表。\",\n    ),\n    \"packageListPermissionRequired\": MessageLookupByLibrary.simpleMessage(\n      \"此功能需要访问已安装应用列表的权限。是否授予此权限？\",\n    ),\n    \"palette\": MessageLookupByLibrary.simpleMessage(\"调色板\"),\n    \"parsePureIp\": MessageLookupByLibrary.simpleMessage(\"解析纯 IP 连接\"),\n    \"parsePureIpDesc\": MessageLookupByLibrary.simpleMessage(\"解析纯 IP 连接\"),\n    \"password\": MessageLookupByLibrary.simpleMessage(\"密码\"),\n    \"paste\": MessageLookupByLibrary.simpleMessage(\"粘贴\"),\n    \"pleaseBindWebDAV\": MessageLookupByLibrary.simpleMessage(\"请绑定WebDAV\"),\n    \"pleaseCloseSystemProxyFirst\": MessageLookupByLibrary.simpleMessage(\n      \"请先关闭系统代理\",\n    ),\n    \"pleaseCloseTunFirst\": MessageLookupByLibrary.simpleMessage(\"请先关闭虚拟网卡\"),\n    \"pleaseEnterScriptName\": MessageLookupByLibrary.simpleMessage(\"请输入脚本名称\"),\n    \"pleaseInputAdminPassword\": MessageLookupByLibrary.simpleMessage(\n      \"请输入管理员密码\",\n    ),\n    \"pleaseUploadFile\": MessageLookupByLibrary.simpleMessage(\"请上传文件\"),\n    \"pleaseUploadValidQrcode\": MessageLookupByLibrary.simpleMessage(\n      \"请上传有效的二维码\",\n    ),\n    \"port\": MessageLookupByLibrary.simpleMessage(\"端口\"),\n    \"portConflictTip\": MessageLookupByLibrary.simpleMessage(\"请输入不同的端口\"),\n    \"portTip\": m7,\n    \"powerSwitch\": MessageLookupByLibrary.simpleMessage(\"启动开关\"),\n    \"preferH3Desc\": MessageLookupByLibrary.simpleMessage(\"优先使用DOH的http/3\"),\n    \"pressKeyboard\": MessageLookupByLibrary.simpleMessage(\"请按下按键\"),\n    \"preview\": MessageLookupByLibrary.simpleMessage(\"预览\"),\n    \"profile\": MessageLookupByLibrary.simpleMessage(\"配置\"),\n    \"profileAutoUpdateIntervalInvalidValidationDesc\":\n        MessageLookupByLibrary.simpleMessage(\"请输入有效间隔时间格式\"),\n    \"profileAutoUpdateIntervalNullValidationDesc\":\n        MessageLookupByLibrary.simpleMessage(\"请输入自动更新间隔时间\"),\n    \"profileHasUpdate\": MessageLookupByLibrary.simpleMessage(\n      \"配置文件已经修改,是否关闭自动更新 \",\n    ),\n    \"profileNameNullValidationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"请输入配置名称\",\n    ),\n    \"profileParseErrorDesc\": MessageLookupByLibrary.simpleMessage(\"配置文件解析错误\"),\n    \"profileUrlInvalidValidationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"请输入有效配置URL\",\n    ),\n    \"profileUrlNullValidationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"请输入配置URL\",\n    ),\n    \"profiles\": MessageLookupByLibrary.simpleMessage(\"配置\"),\n    \"profilesSort\": MessageLookupByLibrary.simpleMessage(\"配置排序\"),\n    \"progress\": MessageLookupByLibrary.simpleMessage(\"进程\"),\n    \"project\": MessageLookupByLibrary.simpleMessage(\"项目\"),\n    \"providers\": MessageLookupByLibrary.simpleMessage(\"提供者\"),\n    \"proxies\": MessageLookupByLibrary.simpleMessage(\"代理\"),\n    \"proxiesSetting\": MessageLookupByLibrary.simpleMessage(\"代理设置\"),\n    \"proxyChains\": MessageLookupByLibrary.simpleMessage(\"代理链\"),\n    \"proxyGroup\": MessageLookupByLibrary.simpleMessage(\"代理组\"),\n    \"proxyNameserver\": MessageLookupByLibrary.simpleMessage(\"代理域名服务器\"),\n    \"proxyNameserverDesc\": MessageLookupByLibrary.simpleMessage(\"用于解析代理节点的域名\"),\n    \"proxyPort\": MessageLookupByLibrary.simpleMessage(\"代理端口\"),\n    \"proxyPortDesc\": MessageLookupByLibrary.simpleMessage(\"设置Clash监听端口\"),\n    \"proxyProviders\": MessageLookupByLibrary.simpleMessage(\"代理提供者\"),\n    \"pulse\": MessageLookupByLibrary.simpleMessage(\"脉冲律动\"),\n    \"pureBlackMode\": MessageLookupByLibrary.simpleMessage(\"纯黑模式\"),\n    \"qrcode\": MessageLookupByLibrary.simpleMessage(\"二维码\"),\n    \"qrcodeDesc\": MessageLookupByLibrary.simpleMessage(\"扫描二维码获取配置文件\"),\n    \"quicGoDisableEcn\": MessageLookupByLibrary.simpleMessage(\"禁用QUIC显式拥塞通知\"),\n    \"quicGoDisableEcnDesc\": MessageLookupByLibrary.simpleMessage(\n      \"禁用 QUIC 的显式拥塞通知功能\",\n    ),\n    \"quicGoDisableGso\": MessageLookupByLibrary.simpleMessage(\"禁用QUIC通用分段卸载\"),\n    \"quicGoDisableGsoDesc\": MessageLookupByLibrary.simpleMessage(\n      \"禁用 QUIC 的通用分段卸载功能\",\n    ),\n    \"quicPortSniffer\": MessageLookupByLibrary.simpleMessage(\"QUIC 端口嗅探\"),\n    \"quickResponse\": MessageLookupByLibrary.simpleMessage(\"快速响应\"),\n    \"quickResponseDesc\": MessageLookupByLibrary.simpleMessage(\"网络发生变化时主动断开连接\"),\n    \"rainbowScheme\": MessageLookupByLibrary.simpleMessage(\"彩虹\"),\n    \"recovery\": MessageLookupByLibrary.simpleMessage(\"恢复\"),\n    \"recoveryAll\": MessageLookupByLibrary.simpleMessage(\"恢复所有数据\"),\n    \"recoveryProfiles\": MessageLookupByLibrary.simpleMessage(\"仅恢复配置文件\"),\n    \"recoveryStrategy\": MessageLookupByLibrary.simpleMessage(\"恢复策略\"),\n    \"recoveryStrategy_compatible\": MessageLookupByLibrary.simpleMessage(\"兼容\"),\n    \"recoveryStrategy_override\": MessageLookupByLibrary.simpleMessage(\"覆盖\"),\n    \"recoverySuccess\": MessageLookupByLibrary.simpleMessage(\"恢复成功\"),\n    \"redirPort\": MessageLookupByLibrary.simpleMessage(\"Redir端口\"),\n    \"redo\": MessageLookupByLibrary.simpleMessage(\"重做\"),\n    \"refreshAppList\": MessageLookupByLibrary.simpleMessage(\"刷新应用列表\"),\n    \"refreshAppListConfirm\": MessageLookupByLibrary.simpleMessage(\"是否刷新应用列表？\"),\n    \"regExp\": MessageLookupByLibrary.simpleMessage(\"正则\"),\n    \"remote\": MessageLookupByLibrary.simpleMessage(\"远程\"),\n    \"remoteBackupDesc\": MessageLookupByLibrary.simpleMessage(\"备份数据到WebDAV\"),\n    \"remoteDestination\": MessageLookupByLibrary.simpleMessage(\"远程目标\"),\n    \"remoteRecoveryDesc\": MessageLookupByLibrary.simpleMessage(\"通过WebDAV恢复数据\"),\n    \"remove\": MessageLookupByLibrary.simpleMessage(\"移除\"),\n    \"rename\": MessageLookupByLibrary.simpleMessage(\"重命名\"),\n    \"request\": MessageLookupByLibrary.simpleMessage(\"请求\"),\n    \"requests\": MessageLookupByLibrary.simpleMessage(\"请求\"),\n    \"requestsDesc\": MessageLookupByLibrary.simpleMessage(\"查看最近请求记录\"),\n    \"reset\": MessageLookupByLibrary.simpleMessage(\"重置\"),\n    \"resetTip\": MessageLookupByLibrary.simpleMessage(\"确定要重置吗?\"),\n    \"resources\": MessageLookupByLibrary.simpleMessage(\"资源\"),\n    \"resourcesDesc\": MessageLookupByLibrary.simpleMessage(\"外部资源相关信息\"),\n    \"respectRules\": MessageLookupByLibrary.simpleMessage(\"遵守规则\"),\n    \"respectRulesDesc\": MessageLookupByLibrary.simpleMessage(\"DNS连接跟随Rules\"),\n    \"restart\": MessageLookupByLibrary.simpleMessage(\"重启\"),\n    \"restartCoreDesc\": MessageLookupByLibrary.simpleMessage(\"是否手动重启内核？\"),\n    \"restartCoreTitle\": MessageLookupByLibrary.simpleMessage(\"重启内核\"),\n    \"restartTip\": MessageLookupByLibrary.simpleMessage(\"重启TUN后改变生效\"),\n    \"retry\": MessageLookupByLibrary.simpleMessage(\"重试\"),\n    \"rotatingCircle\": MessageLookupByLibrary.simpleMessage(\"单圆自转\"),\n    \"ru\": MessageLookupByLibrary.simpleMessage(\"俄语\"),\n    \"rule\": MessageLookupByLibrary.simpleMessage(\"规则\"),\n    \"ruleName\": MessageLookupByLibrary.simpleMessage(\"规则名称\"),\n    \"ruleProviders\": MessageLookupByLibrary.simpleMessage(\"规则提供者\"),\n    \"ruleTarget\": MessageLookupByLibrary.simpleMessage(\"规则目标\"),\n    \"runTime\": MessageLookupByLibrary.simpleMessage(\"启动时间\"),\n    \"runtimeConfig\": MessageLookupByLibrary.simpleMessage(\"运行时配置\"),\n    \"save\": MessageLookupByLibrary.simpleMessage(\"保存\"),\n    \"saveChanges\": MessageLookupByLibrary.simpleMessage(\"是否保存更改？\"),\n    \"saveTip\": MessageLookupByLibrary.simpleMessage(\"确定要保存吗？\"),\n    \"script\": MessageLookupByLibrary.simpleMessage(\"脚本\"),\n    \"scriptDesc\": MessageLookupByLibrary.simpleMessage(\"配置全局覆写脚本\"),\n    \"search\": MessageLookupByLibrary.simpleMessage(\"搜索\"),\n    \"seconds\": MessageLookupByLibrary.simpleMessage(\"秒\"),\n    \"secretCopied\": MessageLookupByLibrary.simpleMessage(\"密码已复制到剪贴板\"),\n    \"selectAll\": MessageLookupByLibrary.simpleMessage(\"全选\"),\n    \"selected\": MessageLookupByLibrary.simpleMessage(\"已选择\"),\n    \"selectedCountTitle\": m8,\n    \"serviceReady\": MessageLookupByLibrary.simpleMessage(\"服务已就绪\"),\n    \"serviceRunning\": MessageLookupByLibrary.simpleMessage(\"服务正在运行中\"),\n    \"settings\": MessageLookupByLibrary.simpleMessage(\"设置\"),\n    \"show\": MessageLookupByLibrary.simpleMessage(\"显示\"),\n    \"shrink\": MessageLookupByLibrary.simpleMessage(\"紧凑\"),\n    \"silentLaunch\": MessageLookupByLibrary.simpleMessage(\"静默启动\"),\n    \"silentLaunchDesc\": MessageLookupByLibrary.simpleMessage(\"不打开软件直接在后台启动\"),\n    \"size\": MessageLookupByLibrary.simpleMessage(\"尺寸\"),\n    \"skipDomain\": MessageLookupByLibrary.simpleMessage(\"跳过域名\"),\n    \"skipDstAddress\": MessageLookupByLibrary.simpleMessage(\"跳过目标 IP\"),\n    \"skipSrcAddress\": MessageLookupByLibrary.simpleMessage(\"跳过来源 IP\"),\n    \"smartAutoStop\": MessageLookupByLibrary.simpleMessage(\"智能启停\"),\n    \"smartAutoStopDesc\": MessageLookupByLibrary.simpleMessage(\"连接指定网络后停止代理服务\"),\n    \"smartAutoStopServiceRunning\": MessageLookupByLibrary.simpleMessage(\n      \"智能启停服务运行中\",\n    ),\n    \"smartDelayLaunch\": MessageLookupByLibrary.simpleMessage(\"智能延迟\"),\n    \"smartDelayLaunchDesc\": MessageLookupByLibrary.simpleMessage(\"在网络成功连接以后启动\"),\n    \"sniffer\": MessageLookupByLibrary.simpleMessage(\"Sniffer\"),\n    \"snifferAddressHint\": MessageLookupByLibrary.simpleMessage(\"每行一个地址\"),\n    \"snifferDesc\": MessageLookupByLibrary.simpleMessage(\"修改域名嗅探配置\"),\n    \"snifferDomainHint\": MessageLookupByLibrary.simpleMessage(\"每行一个域名\"),\n    \"snifferPorts\": MessageLookupByLibrary.simpleMessage(\"端口\"),\n    \"snifferPortsHint\": MessageLookupByLibrary.simpleMessage(\n      \"例如: 80, 8080-8880\",\n    ),\n    \"snifferStatus\": MessageLookupByLibrary.simpleMessage(\"状态\"),\n    \"snifferStatusDesc\": MessageLookupByLibrary.simpleMessage(\"开启嗅探服务设置\"),\n    \"socksPort\": MessageLookupByLibrary.simpleMessage(\"Socks端口\"),\n    \"sort\": MessageLookupByLibrary.simpleMessage(\"排序\"),\n    \"source\": MessageLookupByLibrary.simpleMessage(\"来源\"),\n    \"sourceIp\": MessageLookupByLibrary.simpleMessage(\"源IP\"),\n    \"specialProxy\": MessageLookupByLibrary.simpleMessage(\"特殊代理\"),\n    \"specialRules\": MessageLookupByLibrary.simpleMessage(\"特殊规则\"),\n    \"spinningLines\": MessageLookupByLibrary.simpleMessage(\"流光旋绕\"),\n    \"stackMode\": MessageLookupByLibrary.simpleMessage(\"栈模式\"),\n    \"standard\": MessageLookupByLibrary.simpleMessage(\"标准\"),\n    \"start\": MessageLookupByLibrary.simpleMessage(\"启动\"),\n    \"startTest\": MessageLookupByLibrary.simpleMessage(\"延迟测试\"),\n    \"startVpn\": MessageLookupByLibrary.simpleMessage(\"正在启动\"),\n    \"status\": MessageLookupByLibrary.simpleMessage(\"状态\"),\n    \"statusDesc\": MessageLookupByLibrary.simpleMessage(\"关闭后将使用系统DNS\"),\n    \"stop\": MessageLookupByLibrary.simpleMessage(\"停止\"),\n    \"stopVpn\": MessageLookupByLibrary.simpleMessage(\"正在停止\"),\n    \"storeFix\": MessageLookupByLibrary.simpleMessage(\"商店修复\"),\n    \"storeFixDesc\": MessageLookupByLibrary.simpleMessage(\"修复Google Play商店下载异常\"),\n    \"strictRoute\": MessageLookupByLibrary.simpleMessage(\"严格路由\"),\n    \"strictRouteDesc\": MessageLookupByLibrary.simpleMessage(\"使用TUN严格路由模式\"),\n    \"style\": MessageLookupByLibrary.simpleMessage(\"风格\"),\n    \"subRule\": MessageLookupByLibrary.simpleMessage(\"子规则\"),\n    \"submit\": MessageLookupByLibrary.simpleMessage(\"提交\"),\n    \"success\": MessageLookupByLibrary.simpleMessage(\"Success\"),\n    \"switchLabel\": MessageLookupByLibrary.simpleMessage(\"开关\"),\n    \"switchToDomesticIp\": MessageLookupByLibrary.simpleMessage(\"获取国内IP\"),\n    \"sync\": MessageLookupByLibrary.simpleMessage(\"同步\"),\n    \"syncAll\": MessageLookupByLibrary.simpleMessage(\"全部同步\"),\n    \"syncFailed\": MessageLookupByLibrary.simpleMessage(\"同步失败\"),\n    \"system\": MessageLookupByLibrary.simpleMessage(\"系统\"),\n    \"systemApp\": MessageLookupByLibrary.simpleMessage(\"系统应用\"),\n    \"systemFont\": MessageLookupByLibrary.simpleMessage(\"系统字体\"),\n    \"systemProxy\": MessageLookupByLibrary.simpleMessage(\"系统代理\"),\n    \"systemProxyDesc\": MessageLookupByLibrary.simpleMessage(\"设置系统代理\"),\n    \"tab\": MessageLookupByLibrary.simpleMessage(\"标签页\"),\n    \"tabAnimation\": MessageLookupByLibrary.simpleMessage(\"切换动画\"),\n    \"tabAnimationDesc\": MessageLookupByLibrary.simpleMessage(\"仅在部分移动视图中有效\"),\n    \"tcpConcurrent\": MessageLookupByLibrary.simpleMessage(\"TCP并发\"),\n    \"tcpConcurrentDesc\": MessageLookupByLibrary.simpleMessage(\"开启后允许TCP并发连接\"),\n    \"testUrl\": MessageLookupByLibrary.simpleMessage(\"测试链接\"),\n    \"textScale\": MessageLookupByLibrary.simpleMessage(\"文本缩放\"),\n    \"theme\": MessageLookupByLibrary.simpleMessage(\"主题\"),\n    \"themeColor\": MessageLookupByLibrary.simpleMessage(\"主题色彩\"),\n    \"themeDesc\": MessageLookupByLibrary.simpleMessage(\"设置主题色彩及图标\"),\n    \"themeMode\": MessageLookupByLibrary.simpleMessage(\"主题模式\"),\n    \"threeBounce\": MessageLookupByLibrary.simpleMessage(\"灵珠跃动\"),\n    \"threeColumns\": MessageLookupByLibrary.simpleMessage(\"三列\"),\n    \"threeInOut\": MessageLookupByLibrary.simpleMessage(\"三星舒合\"),\n    \"tight\": MessageLookupByLibrary.simpleMessage(\"紧凑\"),\n    \"time\": MessageLookupByLibrary.simpleMessage(\"时间\"),\n    \"tip\": MessageLookupByLibrary.simpleMessage(\"提示\"),\n    \"tlsPortSniffer\": MessageLookupByLibrary.simpleMessage(\"TLS 端口嗅探\"),\n    \"toggle\": MessageLookupByLibrary.simpleMessage(\"切换\"),\n    \"tonalSpotScheme\": MessageLookupByLibrary.simpleMessage(\"调性点缀\"),\n    \"tooManyRules\": MessageLookupByLibrary.simpleMessage(\"最多允许2个规则\"),\n    \"tools\": MessageLookupByLibrary.simpleMessage(\"更多\"),\n    \"tproxyPort\": MessageLookupByLibrary.simpleMessage(\"Tproxy端口\"),\n    \"trafficUsage\": MessageLookupByLibrary.simpleMessage(\"流量统计\"),\n    \"tryManualRefresh\": MessageLookupByLibrary.simpleMessage(\"请尝试手动刷新\"),\n    \"tun\": MessageLookupByLibrary.simpleMessage(\"虚拟网卡\"),\n    \"tunDesc\": MessageLookupByLibrary.simpleMessage(\"接管当前设备全局流量\"),\n    \"tunEnableRequireAdmin\": MessageLookupByLibrary.simpleMessage(\n      \"启用虚拟网卡需要管理员权限，请以管理员身份运行程序\",\n    ),\n    \"tunnel\": MessageLookupByLibrary.simpleMessage(\"Tunnel\"),\n    \"tunnelAddress\": MessageLookupByLibrary.simpleMessage(\"监听地址\"),\n    \"tunnelAddressHint\": MessageLookupByLibrary.simpleMessage(\n      \"例如: 127.0.0.1:6553\",\n    ),\n    \"tunnelDesc\": MessageLookupByLibrary.simpleMessage(\"使用流量转发隧道\"),\n    \"tunnelList\": MessageLookupByLibrary.simpleMessage(\"转发列表\"),\n    \"tunnelNetwork\": MessageLookupByLibrary.simpleMessage(\"网络协议\"),\n    \"tunnelNetworkHint\": MessageLookupByLibrary.simpleMessage(\"例如: tcp, udp\"),\n    \"tunnelProxy\": MessageLookupByLibrary.simpleMessage(\"代理名称\"),\n    \"tunnelProxyHint\": MessageLookupByLibrary.simpleMessage(\"例如: proxy (可选)\"),\n    \"tunnelTarget\": MessageLookupByLibrary.simpleMessage(\"目标地址\"),\n    \"tunnelTargetHint\": MessageLookupByLibrary.simpleMessage(\n      \"例如: 114.114.114.114:53\",\n    ),\n    \"twoColumns\": MessageLookupByLibrary.simpleMessage(\"两列\"),\n    \"unableToUpdateCurrentProfileDesc\": MessageLookupByLibrary.simpleMessage(\n      \"无法更新当前配置文件\",\n    ),\n    \"undo\": MessageLookupByLibrary.simpleMessage(\"撤销\"),\n    \"unifiedDelay\": MessageLookupByLibrary.simpleMessage(\"统一延迟\"),\n    \"unifiedDelayDesc\": MessageLookupByLibrary.simpleMessage(\"去除握手解析等额外延迟\"),\n    \"unknown\": MessageLookupByLibrary.simpleMessage(\"未知\"),\n    \"unnamed\": MessageLookupByLibrary.simpleMessage(\"未命名\"),\n    \"update\": MessageLookupByLibrary.simpleMessage(\"更新\"),\n    \"upload\": MessageLookupByLibrary.simpleMessage(\"上传\"),\n    \"url\": MessageLookupByLibrary.simpleMessage(\"URL\"),\n    \"urlDesc\": MessageLookupByLibrary.simpleMessage(\"通过URL获取配置文件\"),\n    \"urlTip\": m9,\n    \"useHosts\": MessageLookupByLibrary.simpleMessage(\"使用Hosts\"),\n    \"useSystemHosts\": MessageLookupByLibrary.simpleMessage(\"使用系统Hosts\"),\n    \"value\": MessageLookupByLibrary.simpleMessage(\"值\"),\n    \"vibrantScheme\": MessageLookupByLibrary.simpleMessage(\"活力\"),\n    \"view\": MessageLookupByLibrary.simpleMessage(\"查看\"),\n    \"vpnDesc\": MessageLookupByLibrary.simpleMessage(\"修改VPN相关设置\"),\n    \"vpnEnableDesc\": MessageLookupByLibrary.simpleMessage(\n      \"通过VpnService自动路由系统所有流量\",\n    ),\n    \"vpnSystemProxyDesc\": MessageLookupByLibrary.simpleMessage(\n      \"为VpnService附加HTTP代理\",\n    ),\n    \"vpnTip\": MessageLookupByLibrary.simpleMessage(\"重启VPN后改变生效\"),\n    \"wakelock\": MessageLookupByLibrary.simpleMessage(\"亮屏锁\"),\n    \"wakelockDescription\": MessageLookupByLibrary.simpleMessage(\n      \"本功能不需要任何特殊权限，因为它仅启用屏幕唤醒锁，而不是任何CPU唤醒锁，应用会在后台保持必要的活跃，且屏幕不会自动熄灭，这在一些场景下会很有用\",\n    ),\n    \"wave\": MessageLookupByLibrary.simpleMessage(\"波浪起伏\"),\n    \"webDAVConfiguration\": MessageLookupByLibrary.simpleMessage(\"WebDAV配置\"),\n    \"whitelist\": MessageLookupByLibrary.simpleMessage(\"白名单\"),\n    \"whitelistMode\": MessageLookupByLibrary.simpleMessage(\"白名单模式\"),\n    \"writeToSystem\": MessageLookupByLibrary.simpleMessage(\"写入系统\"),\n    \"writeToSystemDesc\": MessageLookupByLibrary.simpleMessage(\"需要管理员权限\"),\n    \"years\": MessageLookupByLibrary.simpleMessage(\"年\"),\n    \"zh_CN\": MessageLookupByLibrary.simpleMessage(\"简体中文\"),\n    \"zh_TC\": MessageLookupByLibrary.simpleMessage(\"繁体中文\"),\n  };\n}\n"
  },
  {
    "path": "lib/l10n/intl/messages_zh_TC.dart",
    "content": "// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart\n// This is a library that provides messages for a zh_TC locale. All the\n// messages from the main program should be duplicated here with the same\n// function name.\n\n// Ignore issues from commonly used lints in this file.\n// ignore_for_file:unnecessary_brace_in_string_interps, unnecessary_new\n// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering\n// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases\n// ignore_for_file:unused_import, file_names, avoid_escaping_inner_quotes\n// ignore_for_file:unnecessary_string_interpolations, unnecessary_string_escapes\n\nimport 'package:intl/intl.dart';\nimport 'package:intl/message_lookup_by_library.dart';\n\nfinal messages = new MessageLookup();\n\ntypedef String MessageIfAbsent(String messageStr, List<dynamic> args);\n\nclass MessageLookup extends MessageLookupByLibrary {\n  String get localeName => 'zh_TC';\n\n  static String m0(label) => \"確定刪除選取的 ${label} 嗎？\";\n\n  static String m1(label) => \"確定刪除目前的 ${label} 嗎？\";\n\n  static String m2(label) => \"${label}詳情\";\n\n  static String m3(label) => \"${label}不能為空\";\n\n  static String m4(label) => \"${label}目前已存在\";\n\n  static String m5(label) => \"暫無 ${label}\";\n\n  static String m6(label) => \"${label}必須為數字\";\n\n  static String m7(label) => \"${label} 必須在 1024 到 49151 之間\";\n\n  static String m8(count) => \"已選擇 ${count} 項\";\n\n  static String m9(label) => \"${label}必須為 URL\";\n\n  final messages = _notInlinedMessages(_notInlinedMessages);\n  static Map<String, Function> _notInlinedMessages(_) => <String, Function>{\n    \"about\": MessageLookupByLibrary.simpleMessage(\"關於\"),\n    \"accessControl\": MessageLookupByLibrary.simpleMessage(\"存取控制\"),\n    \"accessControlAllowDesc\": MessageLookupByLibrary.simpleMessage(\n      \"只允許選取的應用程式進入 VPN\",\n    ),\n    \"accessControlDesc\": MessageLookupByLibrary.simpleMessage(\"設定應用存取代理\"),\n    \"accessControlNotAllowDesc\": MessageLookupByLibrary.simpleMessage(\n      \"選取的應用程式將被排除在VPN之外\",\n    ),\n    \"account\": MessageLookupByLibrary.simpleMessage(\"帳號\"),\n    \"action\": MessageLookupByLibrary.simpleMessage(\"操作\"),\n    \"action_mode\": MessageLookupByLibrary.simpleMessage(\"切換模式\"),\n    \"action_proxy\": MessageLookupByLibrary.simpleMessage(\"系統代理\"),\n    \"action_start\": MessageLookupByLibrary.simpleMessage(\"啟動 / 停止\"),\n    \"action_tun\": MessageLookupByLibrary.simpleMessage(\"虛擬網卡\"),\n    \"action_view\": MessageLookupByLibrary.simpleMessage(\"顯示 / 隱藏\"),\n    \"add\": MessageLookupByLibrary.simpleMessage(\"新增\"),\n    \"addProfile\": MessageLookupByLibrary.simpleMessage(\"新增配置\"),\n    \"addRule\": MessageLookupByLibrary.simpleMessage(\"新增規則\"),\n    \"addTunnel\": MessageLookupByLibrary.simpleMessage(\"新增轉發\"),\n    \"addedOriginRules\": MessageLookupByLibrary.simpleMessage(\"附加到原始規則\"),\n    \"address\": MessageLookupByLibrary.simpleMessage(\"地址\"),\n    \"addressHelp\": MessageLookupByLibrary.simpleMessage(\"WebDAV 伺服器地址\"),\n    \"addressTip\": MessageLookupByLibrary.simpleMessage(\"請輸入有效的 WebDAV 地址\"),\n    \"adminAutoLaunch\": MessageLookupByLibrary.simpleMessage(\"管理員自啟動\"),\n    \"adminAutoLaunchDesc\": MessageLookupByLibrary.simpleMessage(\n      \"使用管理員模式開機自動啟動\",\n    ),\n    \"advancedSettings\": MessageLookupByLibrary.simpleMessage(\"進階設定\"),\n    \"ago\": MessageLookupByLibrary.simpleMessage(\"前\"),\n    \"agree\": MessageLookupByLibrary.simpleMessage(\"同意\"),\n    \"allApps\": MessageLookupByLibrary.simpleMessage(\"所有應用程式\"),\n    \"allowBypass\": MessageLookupByLibrary.simpleMessage(\"允許繞過 VPN\"),\n    \"allowBypassDesc\": MessageLookupByLibrary.simpleMessage(\"開啟後部分應用程式可繞過 VPN\"),\n    \"allowLan\": MessageLookupByLibrary.simpleMessage(\"區域網路代理\"),\n    \"allowLanDesc\": MessageLookupByLibrary.simpleMessage(\"允許透過區域網路存取代理\"),\n    \"alreadyInWhitelist\": MessageLookupByLibrary.simpleMessage(\"目前應用程式已在白名單內\"),\n    \"app\": MessageLookupByLibrary.simpleMessage(\"應用\"),\n    \"appAccessControl\": MessageLookupByLibrary.simpleMessage(\"應用存取控制\"),\n    \"appDesc\": MessageLookupByLibrary.simpleMessage(\"處理應用相關設定\"),\n    \"application\": MessageLookupByLibrary.simpleMessage(\"應用程式\"),\n    \"applicationDesc\": MessageLookupByLibrary.simpleMessage(\"修改應用程式設定\"),\n    \"auto\": MessageLookupByLibrary.simpleMessage(\"自動\"),\n    \"autoCheckUpdate\": MessageLookupByLibrary.simpleMessage(\"自動檢查更新\"),\n    \"autoCheckUpdateDesc\": MessageLookupByLibrary.simpleMessage(\"應用啟動時自動檢查更新\"),\n    \"autoCloseConnections\": MessageLookupByLibrary.simpleMessage(\"自動關閉連線\"),\n    \"autoCloseConnectionsDesc\": MessageLookupByLibrary.simpleMessage(\n      \"切換節點後自動關閉連線\",\n    ),\n    \"autoLaunch\": MessageLookupByLibrary.simpleMessage(\"開機啟動\"),\n    \"autoLaunchDesc\": MessageLookupByLibrary.simpleMessage(\"跟隨系統自動啟動\"),\n    \"autoRun\": MessageLookupByLibrary.simpleMessage(\"自動連線\"),\n    \"autoRunDesc\": MessageLookupByLibrary.simpleMessage(\"應用打開後自動連線\"),\n    \"autoSetSystemDns\": MessageLookupByLibrary.simpleMessage(\"自動設定系統 DNS\"),\n    \"autoUpdate\": MessageLookupByLibrary.simpleMessage(\"自動更新\"),\n    \"autoUpdateInterval\": MessageLookupByLibrary.simpleMessage(\"自動更新間隔（分鐘）\"),\n    \"backup\": MessageLookupByLibrary.simpleMessage(\"備份\"),\n    \"backupAndRecovery\": MessageLookupByLibrary.simpleMessage(\"備份與還原\"),\n    \"backupAndRecoveryDesc\": MessageLookupByLibrary.simpleMessage(\n      \"透過線上或本機檔案同步資料\",\n    ),\n    \"backupSuccess\": MessageLookupByLibrary.simpleMessage(\"備份成功\"),\n    \"basicConfig\": MessageLookupByLibrary.simpleMessage(\"內核配置\"),\n    \"basicConfigDesc\": MessageLookupByLibrary.simpleMessage(\"全域修改內核配置\"),\n    \"batteryOptimization\": MessageLookupByLibrary.simpleMessage(\"電池最佳化\"),\n    \"batteryOptimizationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"請求 Android 電池最佳化白名單權限\",\n    ),\n    \"bind\": MessageLookupByLibrary.simpleMessage(\"綁定\"),\n    \"blacklist\": MessageLookupByLibrary.simpleMessage(\"黑名單\"),\n    \"blacklistMode\": MessageLookupByLibrary.simpleMessage(\"黑名單模式\"),\n    \"bypassDomain\": MessageLookupByLibrary.simpleMessage(\"排除網域\"),\n    \"bypassDomainDesc\": MessageLookupByLibrary.simpleMessage(\"僅在系統代理啟用時生效\"),\n    \"bypassPrivateRoute\": MessageLookupByLibrary.simpleMessage(\"繞過私有網路\"),\n    \"bypassPrivateRouteDesc\": MessageLookupByLibrary.simpleMessage(\n      \"自動繞過私有網路IP位址\",\n    ),\n    \"cacheAlgorithm\": MessageLookupByLibrary.simpleMessage(\"快取演算法\"),\n    \"cacheCorrupt\": MessageLookupByLibrary.simpleMessage(\"快取已損壞，是否清空？\"),\n    \"cameraPermissionDenied\": MessageLookupByLibrary.simpleMessage(\"相機權限被拒絕\"),\n    \"cameraPermissionDesc\": MessageLookupByLibrary.simpleMessage(\n      \"掃描二維碼需要相機權限，請在設定中授予相機權限。\",\n    ),\n    \"cancel\": MessageLookupByLibrary.simpleMessage(\"取消\"),\n    \"cancelFilterSystemApp\": MessageLookupByLibrary.simpleMessage(\"取消過濾系統應用程式\"),\n    \"cancelSelectAll\": MessageLookupByLibrary.simpleMessage(\"取消全選\"),\n    \"checkError\": MessageLookupByLibrary.simpleMessage(\"檢測失敗\"),\n    \"checkOrAddProfile\": MessageLookupByLibrary.simpleMessage(\"請先新增配置\"),\n    \"checkUpdate\": MessageLookupByLibrary.simpleMessage(\"檢查更新\"),\n    \"checkUpdateError\": MessageLookupByLibrary.simpleMessage(\"目前的應用程式已經是最新版了\"),\n    \"checking\": MessageLookupByLibrary.simpleMessage(\"檢測中...\"),\n    \"circle\": MessageLookupByLibrary.simpleMessage(\"圓環流轉\"),\n    \"clearCacheDesc\": MessageLookupByLibrary.simpleMessage(\n      \"是否需要清理 FakeIP & DNS 快取？\",\n    ),\n    \"clearCacheTitle\": MessageLookupByLibrary.simpleMessage(\"清理快取\"),\n    \"clearData\": MessageLookupByLibrary.simpleMessage(\"清除資料\"),\n    \"clipboard\": MessageLookupByLibrary.simpleMessage(\"剪貼簿\"),\n    \"clipboardDesc\": MessageLookupByLibrary.simpleMessage(\"自動獲取剪貼簿訂閱連結\"),\n    \"clipboardExport\": MessageLookupByLibrary.simpleMessage(\"匯出剪貼簿\"),\n    \"clipboardImport\": MessageLookupByLibrary.simpleMessage(\"剪貼簿匯入\"),\n    \"color\": MessageLookupByLibrary.simpleMessage(\"顏色\"),\n    \"colorSchemes\": MessageLookupByLibrary.simpleMessage(\"配色方案\"),\n    \"columns\": MessageLookupByLibrary.simpleMessage(\"列數\"),\n    \"compatible\": MessageLookupByLibrary.simpleMessage(\"相容模式\"),\n    \"compatibleDesc\": MessageLookupByLibrary.simpleMessage(\n      \"開啟將失去部分應用能力，獲得全量的 Clash 支援\",\n    ),\n    \"concurrencyLimit\": MessageLookupByLibrary.simpleMessage(\"並發限制\"),\n    \"concurrencyLimitDesc\": MessageLookupByLibrary.simpleMessage(\"延遲測試的最大並發數量\"),\n    \"confirm\": MessageLookupByLibrary.simpleMessage(\"確定\"),\n    \"connection\": MessageLookupByLibrary.simpleMessage(\"活躍連線\"),\n    \"connections\": MessageLookupByLibrary.simpleMessage(\"連線\"),\n    \"connectionsDesc\": MessageLookupByLibrary.simpleMessage(\"查看目前連線資料\"),\n    \"connectivity\": MessageLookupByLibrary.simpleMessage(\"連通性：\"),\n    \"contactMe\": MessageLookupByLibrary.simpleMessage(\"聯絡我\"),\n    \"content\": MessageLookupByLibrary.simpleMessage(\"內容\"),\n    \"contentScheme\": MessageLookupByLibrary.simpleMessage(\"內容主題\"),\n    \"controlSecret\": MessageLookupByLibrary.simpleMessage(\"控制密碼\"),\n    \"controlSecretDesc\": MessageLookupByLibrary.simpleMessage(\n      \"RESTful API 的存取密碼\",\n    ),\n    \"copy\": MessageLookupByLibrary.simpleMessage(\"複製\"),\n    \"copyEnvVar\": MessageLookupByLibrary.simpleMessage(\"複製環境變數\"),\n    \"copyLink\": MessageLookupByLibrary.simpleMessage(\"複製連結\"),\n    \"copySuccess\": MessageLookupByLibrary.simpleMessage(\"複製成功\"),\n    \"core\": MessageLookupByLibrary.simpleMessage(\"內核\"),\n    \"coreConnected\": MessageLookupByLibrary.simpleMessage(\"已連線\"),\n    \"coreInfo\": MessageLookupByLibrary.simpleMessage(\"內核資訊\"),\n    \"coreSuspended\": MessageLookupByLibrary.simpleMessage(\"已掛起\"),\n    \"country\": MessageLookupByLibrary.simpleMessage(\"區域\"),\n    \"crashTest\": MessageLookupByLibrary.simpleMessage(\"崩潰測試\"),\n    \"create\": MessageLookupByLibrary.simpleMessage(\"建立\"),\n    \"creationTime\": MessageLookupByLibrary.simpleMessage(\"建立時間\"),\n    \"custom\": MessageLookupByLibrary.simpleMessage(\"自訂\"),\n    \"customUrl\": MessageLookupByLibrary.simpleMessage(\"自訂 URL\"),\n    \"cut\": MessageLookupByLibrary.simpleMessage(\"剪下\"),\n    \"dark\": MessageLookupByLibrary.simpleMessage(\"深色\"),\n    \"dashboard\": MessageLookupByLibrary.simpleMessage(\"首頁\"),\n    \"days\": MessageLookupByLibrary.simpleMessage(\"天\"),\n    \"defaultNameserver\": MessageLookupByLibrary.simpleMessage(\"預設網域名稱伺服器\"),\n    \"defaultNameserverDesc\": MessageLookupByLibrary.simpleMessage(\n      \"用於解析 DNS 伺服器\",\n    ),\n    \"defaultSort\": MessageLookupByLibrary.simpleMessage(\"按預設排序\"),\n    \"defaultText\": MessageLookupByLibrary.simpleMessage(\"預設\"),\n    \"delay\": MessageLookupByLibrary.simpleMessage(\"延遲\"),\n    \"delayAnimation\": MessageLookupByLibrary.simpleMessage(\"延遲動畫\"),\n    \"delayAnimationDesc\": MessageLookupByLibrary.simpleMessage(\"自訂測試過程中的動畫顯示\"),\n    \"delaySort\": MessageLookupByLibrary.simpleMessage(\"按延遲排序\"),\n    \"delete\": MessageLookupByLibrary.simpleMessage(\"刪除\"),\n    \"deleteMultipTip\": m0,\n    \"deleteTip\": m1,\n    \"deleteTunnel\": MessageLookupByLibrary.simpleMessage(\"刪除轉發\"),\n    \"desc\": MessageLookupByLibrary.simpleMessage(\n      \"Bettbox 基於強大靈活的 Mihomo (Clash.Meta) 代理內核，致力於提供更好的體驗，Forked from FlClash，Better Experience, Out of the box\",\n    ),\n    \"destination\": MessageLookupByLibrary.simpleMessage(\"目標地址\"),\n    \"destinationGeoIP\": MessageLookupByLibrary.simpleMessage(\"目標地理定位\"),\n    \"destinationIPASN\": MessageLookupByLibrary.simpleMessage(\"目標 IP ASN\"),\n    \"details\": m2,\n    \"detectionTip\": MessageLookupByLibrary.simpleMessage(\"依賴第三方 API，僅供參考\"),\n    \"developerMode\": MessageLookupByLibrary.simpleMessage(\"開發者模式\"),\n    \"developerModeEnableTip\": MessageLookupByLibrary.simpleMessage(\"開發者模式已啟用。\"),\n    \"dialerIp4pConvert\": MessageLookupByLibrary.simpleMessage(\"啟用撥號 IP4P 地址轉換\"),\n    \"dialerIp4pConvertDesc\": MessageLookupByLibrary.simpleMessage(\n      \"啟用撥號器的 IP4P 地址轉換功能\",\n    ),\n    \"direct\": MessageLookupByLibrary.simpleMessage(\"直連\"),\n    \"directNameserver\": MessageLookupByLibrary.simpleMessage(\"直連網域名稱伺服器\"),\n    \"directNameserverDesc\": MessageLookupByLibrary.simpleMessage(\"用於解析直連出口的網域\"),\n    \"directNameserverFollowPolicy\": MessageLookupByLibrary.simpleMessage(\n      \"直連 DNS 遵循規則\",\n    ),\n    \"disableQuic\": MessageLookupByLibrary.simpleMessage(\"禁用QUIC\"),\n    \"disableQuicDesc\": MessageLookupByLibrary.simpleMessage(\"禁用QUIC以解決特定網路問題\"),\n    \"disclaimer\": MessageLookupByLibrary.simpleMessage(\"免責聲明\"),\n    \"disclaimerDesc\": MessageLookupByLibrary.simpleMessage(\n      \"本軟體為開源免費軟體，僅供學習交流等非商業性質的個人測試使用，代理服務商的行為均與本軟體無關，同意聲明代表您已完全知曉並確認了這一點，如不同意，請選擇退出！\",\n    ),\n    \"discoverNewVersion\": MessageLookupByLibrary.simpleMessage(\"發現新版本\"),\n    \"discovery\": MessageLookupByLibrary.simpleMessage(\"發現新版本\"),\n    \"dnsDesc\": MessageLookupByLibrary.simpleMessage(\"更新 DNS 相關設定\"),\n    \"dnsHijack\": MessageLookupByLibrary.simpleMessage(\"DNS 劫持\"),\n    \"dnsHijackDesc\": MessageLookupByLibrary.simpleMessage(\"將解析匯入內部 DNS 模組\"),\n    \"dnsMode\": MessageLookupByLibrary.simpleMessage(\"DNS 模式\"),\n    \"doYouWantToPass\": MessageLookupByLibrary.simpleMessage(\"是否要通過\"),\n    \"domain\": MessageLookupByLibrary.simpleMessage(\"網域\"),\n    \"doubleBounce\": MessageLookupByLibrary.simpleMessage(\"雙重彈奏\"),\n    \"download\": MessageLookupByLibrary.simpleMessage(\"下載\"),\n    \"dozeSuspend\": MessageLookupByLibrary.simpleMessage(\"休眠支援\"),\n    \"dozeSuspendDesc\": MessageLookupByLibrary.simpleMessage(\n      \"開啟後同步系統 Doze 休眠模式\",\n    ),\n    \"edit\": MessageLookupByLibrary.simpleMessage(\"編輯\"),\n    \"editTunnel\": MessageLookupByLibrary.simpleMessage(\"編輯轉發\"),\n    \"emptyTip\": m3,\n    \"en\": MessageLookupByLibrary.simpleMessage(\"英語\"),\n    \"enableCrashReport\": MessageLookupByLibrary.simpleMessage(\"應用崩潰分析\"),\n    \"enableCrashReportDesc\": MessageLookupByLibrary.simpleMessage(\n      \"必要時上傳應用崩潰日誌\",\n    ),\n    \"enableOverride\": MessageLookupByLibrary.simpleMessage(\"啟用覆寫\"),\n    \"endpointIndependentNat\": MessageLookupByLibrary.simpleMessage(\"NAT 增強\"),\n    \"endpointIndependentNatDesc\": MessageLookupByLibrary.simpleMessage(\n      \"啟用獨立於端點的 NAT\",\n    ),\n    \"entries\": MessageLookupByLibrary.simpleMessage(\"個項目\"),\n    \"exclude\": MessageLookupByLibrary.simpleMessage(\"背景隱藏\"),\n    \"excludeChina\": MessageLookupByLibrary.simpleMessage(\"排除國內\"),\n    \"excludeChinaDesc\": MessageLookupByLibrary.simpleMessage(\n      \"放行中國QUIC流量而非全部禁用\",\n    ),\n    \"excludeDesc\": MessageLookupByLibrary.simpleMessage(\"從最近任務中隱藏應用程式\"),\n    \"existsTip\": m4,\n    \"exit\": MessageLookupByLibrary.simpleMessage(\"退出\"),\n    \"expand\": MessageLookupByLibrary.simpleMessage(\"標準\"),\n    \"experimental\": MessageLookupByLibrary.simpleMessage(\"Experimental\"),\n    \"experimentalDesc\": MessageLookupByLibrary.simpleMessage(\"實驗性配置請謹慎使用\"),\n    \"expirationTime\": MessageLookupByLibrary.simpleMessage(\"到期時間\"),\n    \"exportFile\": MessageLookupByLibrary.simpleMessage(\"匯出檔案\"),\n    \"exportLogs\": MessageLookupByLibrary.simpleMessage(\"匯出日誌\"),\n    \"exportSuccess\": MessageLookupByLibrary.simpleMessage(\"匯出成功\"),\n    \"expressiveScheme\": MessageLookupByLibrary.simpleMessage(\"表現力\"),\n    \"externalController\": MessageLookupByLibrary.simpleMessage(\"外部控制\"),\n    \"externalControllerDesc\": MessageLookupByLibrary.simpleMessage(\n      \"透過線上連接埠控制內核\",\n    ),\n    \"externalLink\": MessageLookupByLibrary.simpleMessage(\"外部連結\"),\n    \"externalResources\": MessageLookupByLibrary.simpleMessage(\"外部資源\"),\n    \"fadingCircle\": MessageLookupByLibrary.simpleMessage(\"環影隱漸\"),\n    \"fadingFour\": MessageLookupByLibrary.simpleMessage(\"四方爍動\"),\n    \"fakeIpFilterMode\": MessageLookupByLibrary.simpleMessage(\"FakeIP 過濾模式\"),\n    \"fakeIpFilterModeDesc\": MessageLookupByLibrary.simpleMessage(\n      \"指定 FakeIP 過濾模式\",\n    ),\n    \"fakeipFilter\": MessageLookupByLibrary.simpleMessage(\"FakeIP 過濾清單\"),\n    \"fakeipRange\": MessageLookupByLibrary.simpleMessage(\"FakeIP 範圍\"),\n    \"fakeipRangeV6\": MessageLookupByLibrary.simpleMessage(\"FakeIPv6 範圍\"),\n    \"fakeipTtl\": MessageLookupByLibrary.simpleMessage(\"FakeIP 有效時間\"),\n    \"fallback\": MessageLookupByLibrary.simpleMessage(\"Fallback\"),\n    \"fallbackDesc\": MessageLookupByLibrary.simpleMessage(\"一般情況下使用境外 DNS\"),\n    \"fallbackFilter\": MessageLookupByLibrary.simpleMessage(\"Fallback 過濾\"),\n    \"fcmOptimization\": MessageLookupByLibrary.simpleMessage(\"FCM 優化\"),\n    \"fcmOptimizationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"增強 FCM 直連時的網路穩定性\",\n    ),\n    \"fcmTip\": MessageLookupByLibrary.simpleMessage(\n      \"FCM 連線和支援取決於裝置本身，顯示結果僅供參考。因系統權限原因，您需要關閉網路中的 \\\"允許繞過 VPN\\\" 選項，以獲得更加準確的結果\",\n    ),\n    \"fidelityScheme\": MessageLookupByLibrary.simpleMessage(\"高保真\"),\n    \"file\": MessageLookupByLibrary.simpleMessage(\"檔案\"),\n    \"fileDesc\": MessageLookupByLibrary.simpleMessage(\"直接上傳設定檔\"),\n    \"fileIsUpdate\": MessageLookupByLibrary.simpleMessage(\"檔案有修改，是否儲存修改\"),\n    \"filterSystemApp\": MessageLookupByLibrary.simpleMessage(\"過濾系統應用程式\"),\n    \"findProcessMode\": MessageLookupByLibrary.simpleMessage(\"尋找處理程序\"),\n    \"findProcessModeDesc\": MessageLookupByLibrary.simpleMessage(\"開啟後將可以尋找處理程序\"),\n    \"fontFamily\": MessageLookupByLibrary.simpleMessage(\"字體\"),\n    \"forceDnsMapping\": MessageLookupByLibrary.simpleMessage(\"強制 DNS 映射\"),\n    \"forceDnsMappingDesc\": MessageLookupByLibrary.simpleMessage(\n      \"強制將 DNS 查詢結果映射到連線\",\n    ),\n    \"forceDomain\": MessageLookupByLibrary.simpleMessage(\"強制嗅探網域\"),\n    \"forceGCDesc\": MessageLookupByLibrary.simpleMessage(\n      \"是否進行強制內核垃圾回收？實驗性功能，請謹慎使用\",\n    ),\n    \"forceGCTitle\": MessageLookupByLibrary.simpleMessage(\"強制垃圾回收\"),\n    \"formatError\": MessageLookupByLibrary.simpleMessage(\"請檢查格式是否正確\"),\n    \"fourColumns\": MessageLookupByLibrary.simpleMessage(\"四列\"),\n    \"fruitSaladScheme\": MessageLookupByLibrary.simpleMessage(\"果繽紛\"),\n    \"general\": MessageLookupByLibrary.simpleMessage(\"一般\"),\n    \"generalDesc\": MessageLookupByLibrary.simpleMessage(\"修改一般設定\"),\n    \"generateSecret\": MessageLookupByLibrary.simpleMessage(\"產生\"),\n    \"geoData\": MessageLookupByLibrary.simpleMessage(\"地理資料\"),\n    \"geodataLoader\": MessageLookupByLibrary.simpleMessage(\"GEO 節能\"),\n    \"geodataLoaderDesc\": MessageLookupByLibrary.simpleMessage(\n      \"開啟後使用 GEO 低記憶體載入器\",\n    ),\n    \"geoipCode\": MessageLookupByLibrary.simpleMessage(\"GeoIP 代碼\"),\n    \"getOriginRules\": MessageLookupByLibrary.simpleMessage(\"獲取原始規則\"),\n    \"global\": MessageLookupByLibrary.simpleMessage(\"全域\"),\n    \"go\": MessageLookupByLibrary.simpleMessage(\"前往\"),\n    \"goDownload\": MessageLookupByLibrary.simpleMessage(\"前往下載\"),\n    \"harmonyFont\": MessageLookupByLibrary.simpleMessage(\"鴻蒙字體\"),\n    \"harmonyFontDesc\": MessageLookupByLibrary.simpleMessage(\n      \"使用最佳化的 HarmonyOS Sans\",\n    ),\n    \"hasCacheChange\": MessageLookupByLibrary.simpleMessage(\"是否快取修改\"),\n    \"healthCheckTimeout\": MessageLookupByLibrary.simpleMessage(\"超時時間\"),\n    \"healthCheckTimeoutDesc\": MessageLookupByLibrary.simpleMessage(\n      \"節點健康檢查超時時間\",\n    ),\n    \"highRefreshRate\": MessageLookupByLibrary.simpleMessage(\"高重新整理率\"),\n    \"highRefreshRateDesc\": MessageLookupByLibrary.simpleMessage(\n      \"啟用裝置最高重新整理率支援\",\n    ),\n    \"host\": MessageLookupByLibrary.simpleMessage(\"主機\"),\n    \"hostsDesc\": MessageLookupByLibrary.simpleMessage(\"附加目前配置 Hosts\"),\n    \"hotkeyConflict\": MessageLookupByLibrary.simpleMessage(\"快捷鍵衝突\"),\n    \"hotkeyManagement\": MessageLookupByLibrary.simpleMessage(\"快捷鍵管理\"),\n    \"hotkeyManagementDesc\": MessageLookupByLibrary.simpleMessage(\"使用鍵盤控制應用程式\"),\n    \"hours\": MessageLookupByLibrary.simpleMessage(\"小時\"),\n    \"httpPortSniffer\": MessageLookupByLibrary.simpleMessage(\"HTTP 連接埠嗅探\"),\n    \"icmpForwarding\": MessageLookupByLibrary.simpleMessage(\"ICMP 轉發\"),\n    \"icmpForwardingDesc\": MessageLookupByLibrary.simpleMessage(\n      \"開啟後將支援 ICMP Ping\",\n    ),\n    \"icon\": MessageLookupByLibrary.simpleMessage(\"圖片\"),\n    \"iconConfiguration\": MessageLookupByLibrary.simpleMessage(\"圖片設定\"),\n    \"iconStyle\": MessageLookupByLibrary.simpleMessage(\"圖示樣式\"),\n    \"import\": MessageLookupByLibrary.simpleMessage(\"匯入\"),\n    \"importFailed\": MessageLookupByLibrary.simpleMessage(\"匯入失敗\"),\n    \"importFile\": MessageLookupByLibrary.simpleMessage(\"透過檔案匯入\"),\n    \"importFromCode\": MessageLookupByLibrary.simpleMessage(\"透過程式碼匯入\"),\n    \"importFromURL\": MessageLookupByLibrary.simpleMessage(\"從 URL 匯入\"),\n    \"importUrl\": MessageLookupByLibrary.simpleMessage(\"透過 URL 匯入\"),\n    \"infiniteTime\": MessageLookupByLibrary.simpleMessage(\"長期有效\"),\n    \"init\": MessageLookupByLibrary.simpleMessage(\"初始化\"),\n    \"inputCorrectHotkey\": MessageLookupByLibrary.simpleMessage(\"請輸入正確的快捷鍵\"),\n    \"intelligentSelected\": MessageLookupByLibrary.simpleMessage(\"智慧選擇\"),\n    \"internet\": MessageLookupByLibrary.simpleMessage(\"網際網路\"),\n    \"interval\": MessageLookupByLibrary.simpleMessage(\"間隔\"),\n    \"intranetIP\": MessageLookupByLibrary.simpleMessage(\"內網 IP\"),\n    \"invalidIpFormat\": MessageLookupByLibrary.simpleMessage(\"無效的 IP 或 CIDR 格式\"),\n    \"ipClickBehavior\": MessageLookupByLibrary.simpleMessage(\"顯示切換\"),\n    \"ipPrivacyProtection\": MessageLookupByLibrary.simpleMessage(\"隱藏 IP 顯示\"),\n    \"ipcidr\": MessageLookupByLibrary.simpleMessage(\"IP / 遮罩\"),\n    \"ipv6Desc\": MessageLookupByLibrary.simpleMessage(\"開啟後將可以接收 IPv6 流量\"),\n    \"ipv6InboundDesc\": MessageLookupByLibrary.simpleMessage(\"允許 IPv6 入站\"),\n    \"ja\": MessageLookupByLibrary.simpleMessage(\"日語\"),\n    \"just\": MessageLookupByLibrary.simpleMessage(\"剛剛\"),\n    \"keepAliveIntervalDesc\": MessageLookupByLibrary.simpleMessage(\"TCP 保持活動間隔\"),\n    \"key\": MessageLookupByLibrary.simpleMessage(\"鍵\"),\n    \"language\": MessageLookupByLibrary.simpleMessage(\"語言\"),\n    \"layout\": MessageLookupByLibrary.simpleMessage(\"版面配置\"),\n    \"light\": MessageLookupByLibrary.simpleMessage(\"淺色\"),\n    \"lightIcon\": MessageLookupByLibrary.simpleMessage(\"丹青留白\"),\n    \"lightIconDesc\": MessageLookupByLibrary.simpleMessage(\"手動切換淺色系桌面應用程式圖示\"),\n    \"list\": MessageLookupByLibrary.simpleMessage(\"清單\"),\n    \"listen\": MessageLookupByLibrary.simpleMessage(\"監聽\"),\n    \"local\": MessageLookupByLibrary.simpleMessage(\"本機\"),\n    \"localBackupDesc\": MessageLookupByLibrary.simpleMessage(\"備份資料到本機\"),\n    \"localRecoveryDesc\": MessageLookupByLibrary.simpleMessage(\"透過檔案還原資料\"),\n    \"log\": MessageLookupByLibrary.simpleMessage(\"日誌\"),\n    \"logLevel\": MessageLookupByLibrary.simpleMessage(\"日誌等級\"),\n    \"logcat\": MessageLookupByLibrary.simpleMessage(\"日誌捕獲\"),\n    \"logcatDesc\": MessageLookupByLibrary.simpleMessage(\"開啟後將會顯示日誌入口\"),\n    \"logs\": MessageLookupByLibrary.simpleMessage(\"日誌\"),\n    \"logsDesc\": MessageLookupByLibrary.simpleMessage(\"查看日誌擷取記錄\"),\n    \"logsTest\": MessageLookupByLibrary.simpleMessage(\"日誌測試\"),\n    \"loopback\": MessageLookupByLibrary.simpleMessage(\"迴環解鎖工具\"),\n    \"loopbackDesc\": MessageLookupByLibrary.simpleMessage(\"用於 UWP 迴環解鎖\"),\n    \"loose\": MessageLookupByLibrary.simpleMessage(\"寬鬆\"),\n    \"manualRefreshIp\": MessageLookupByLibrary.simpleMessage(\"重新取得 IP\"),\n    \"memoryInfo\": MessageLookupByLibrary.simpleMessage(\"記憶體資訊\"),\n    \"messageTest\": MessageLookupByLibrary.simpleMessage(\"訊息測試\"),\n    \"messageTestTip\": MessageLookupByLibrary.simpleMessage(\"這是一條訊息。\"),\n    \"min\": MessageLookupByLibrary.simpleMessage(\"最小\"),\n    \"minimizeOnExit\": MessageLookupByLibrary.simpleMessage(\"退出最小化\"),\n    \"minimizeOnExitDesc\": MessageLookupByLibrary.simpleMessage(\"修改系統預設退出事件\"),\n    \"minutes\": MessageLookupByLibrary.simpleMessage(\"分鐘\"),\n    \"mixedPort\": MessageLookupByLibrary.simpleMessage(\"混合連接埠\"),\n    \"mode\": MessageLookupByLibrary.simpleMessage(\"模式\"),\n    \"monochromeScheme\": MessageLookupByLibrary.simpleMessage(\"單色\"),\n    \"months\": MessageLookupByLibrary.simpleMessage(\"月\"),\n    \"more\": MessageLookupByLibrary.simpleMessage(\"查看\"),\n    \"name\": MessageLookupByLibrary.simpleMessage(\"名稱\"),\n    \"nameSort\": MessageLookupByLibrary.simpleMessage(\"按名稱排序\"),\n    \"nameserver\": MessageLookupByLibrary.simpleMessage(\"網域名稱伺服器\"),\n    \"nameserverDesc\": MessageLookupByLibrary.simpleMessage(\"用於解析網域\"),\n    \"nameserverPolicy\": MessageLookupByLibrary.simpleMessage(\"網域名稱伺服器策略\"),\n    \"nameserverPolicyDesc\": MessageLookupByLibrary.simpleMessage(\n      \"指定對應網域名稱伺服器策略\",\n    ),\n    \"navBarHapticFeedback\": MessageLookupByLibrary.simpleMessage(\"觸覺回饋\"),\n    \"navBarHapticFeedbackDesc\": MessageLookupByLibrary.simpleMessage(\n      \"底部導覽列切換震動回饋\",\n    ),\n    \"network\": MessageLookupByLibrary.simpleMessage(\"網路\"),\n    \"networkDesc\": MessageLookupByLibrary.simpleMessage(\"修改網路相關設定\"),\n    \"networkDetection\": MessageLookupByLibrary.simpleMessage(\"網路檢測\"),\n    \"networkFix\": MessageLookupByLibrary.simpleMessage(\"網路修復\"),\n    \"networkFixDesc\": MessageLookupByLibrary.simpleMessage(\n      \"修復 Windows 網路檢測地球圖示問題\",\n    ),\n    \"networkMatch\": MessageLookupByLibrary.simpleMessage(\"網路匹配\"),\n    \"networkMatchHint\": MessageLookupByLibrary.simpleMessage(\n      \"輸入 IP 或 CIDR，最多 2 個，以逗號分隔\",\n    ),\n    \"networkSpeed\": MessageLookupByLibrary.simpleMessage(\"網路速度\"),\n    \"networkType\": MessageLookupByLibrary.simpleMessage(\"網路類型\"),\n    \"neutralScheme\": MessageLookupByLibrary.simpleMessage(\"中性\"),\n    \"noAnimation\": MessageLookupByLibrary.simpleMessage(\"預設\"),\n    \"noData\": MessageLookupByLibrary.simpleMessage(\"暫無資料\"),\n    \"noHotKey\": MessageLookupByLibrary.simpleMessage(\"暫無快捷鍵\"),\n    \"noIcon\": MessageLookupByLibrary.simpleMessage(\"無圖示\"),\n    \"noInfo\": MessageLookupByLibrary.simpleMessage(\"暫無資訊\"),\n    \"noMoreInfoDesc\": MessageLookupByLibrary.simpleMessage(\"暫無更多資訊\"),\n    \"noNetwork\": MessageLookupByLibrary.simpleMessage(\"無網路\"),\n    \"noNetworkApp\": MessageLookupByLibrary.simpleMessage(\"無網路應用程式\"),\n    \"noProxy\": MessageLookupByLibrary.simpleMessage(\"暫無代理\"),\n    \"noProxyDesc\": MessageLookupByLibrary.simpleMessage(\"請建立配置或新增有效的設定檔\"),\n    \"noResolve\": MessageLookupByLibrary.simpleMessage(\"不解析 IP\"),\n    \"noStatusAvailable\": MessageLookupByLibrary.simpleMessage(\"未獲取到狀態\"),\n    \"nodeExclusion\": MessageLookupByLibrary.simpleMessage(\"節點排除\"),\n    \"nodeExclusionDesc\": MessageLookupByLibrary.simpleMessage(\"排除所有匹配到的節點\"),\n    \"nodeExclusionPlaceholder\": MessageLookupByLibrary.simpleMessage(\n      \"HK|香港|🇭🇰\",\n    ),\n    \"none\": MessageLookupByLibrary.simpleMessage(\"無\"),\n    \"notRecommended\": MessageLookupByLibrary.simpleMessage(\"不推薦\"),\n    \"notSelectedTip\": MessageLookupByLibrary.simpleMessage(\"目前的代理群組無法選取\"),\n    \"ntp\": MessageLookupByLibrary.simpleMessage(\"NTP\"),\n    \"ntpDesc\": MessageLookupByLibrary.simpleMessage(\"使用 NTP 時間服務\"),\n    \"ntpInterval\": MessageLookupByLibrary.simpleMessage(\"更新時間\"),\n    \"ntpPort\": MessageLookupByLibrary.simpleMessage(\"連接埠\"),\n    \"ntpServer\": MessageLookupByLibrary.simpleMessage(\"伺服器\"),\n    \"ntpStatus\": MessageLookupByLibrary.simpleMessage(\"狀態\"),\n    \"ntpStatusDesc\": MessageLookupByLibrary.simpleMessage(\"開啟 NTP 時間服務\"),\n    \"nullProfileDesc\": MessageLookupByLibrary.simpleMessage(\"沒有設定檔，請先新增設定檔\"),\n    \"nullTip\": m5,\n    \"numberTip\": m6,\n    \"oneColumn\": MessageLookupByLibrary.simpleMessage(\"一列\"),\n    \"onlinePanel\": MessageLookupByLibrary.simpleMessage(\"線上面板\"),\n    \"onlyIcon\": MessageLookupByLibrary.simpleMessage(\"僅圖示\"),\n    \"onlyOtherApps\": MessageLookupByLibrary.simpleMessage(\"僅第三方應用程式\"),\n    \"onlyStatisticsProxy\": MessageLookupByLibrary.simpleMessage(\"代理流量統計\"),\n    \"onlyStatisticsProxyDesc\": MessageLookupByLibrary.simpleMessage(\n      \"開啟後將只統計代理流量\",\n    ),\n    \"openDashboard\": MessageLookupByLibrary.simpleMessage(\"打開 Zashboard\"),\n    \"openSettings\": MessageLookupByLibrary.simpleMessage(\"打開設定\"),\n    \"options\": MessageLookupByLibrary.simpleMessage(\"選項\"),\n    \"other\": MessageLookupByLibrary.simpleMessage(\"其他\"),\n    \"otherContributors\": MessageLookupByLibrary.simpleMessage(\"其他貢獻者\"),\n    \"otherSettings\": MessageLookupByLibrary.simpleMessage(\"增強工具\"),\n    \"otherSettingsDesc\": MessageLookupByLibrary.simpleMessage(\"修改增強工具設定\"),\n    \"outboundMode\": MessageLookupByLibrary.simpleMessage(\"出站模式\"),\n    \"override\": MessageLookupByLibrary.simpleMessage(\"覆寫\"),\n    \"overrideDesc\": MessageLookupByLibrary.simpleMessage(\"覆寫代理相關配置\"),\n    \"overrideDestination\": MessageLookupByLibrary.simpleMessage(\"覆蓋目標地址\"),\n    \"overrideDestinationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"使用嗅探結果覆蓋連線目標地址\",\n    ),\n    \"overrideDns\": MessageLookupByLibrary.simpleMessage(\"覆寫 DNS\"),\n    \"overrideDnsDesc\": MessageLookupByLibrary.simpleMessage(\n      \"開啟後將覆蓋配置中的 DNS 選項\",\n    ),\n    \"overrideExperimental\": MessageLookupByLibrary.simpleMessage(\n      \"覆寫 Experimental\",\n    ),\n    \"overrideExperimentalDesc\": MessageLookupByLibrary.simpleMessage(\n      \"開啟後將覆蓋配置中的實驗性配置\",\n    ),\n    \"overrideInvalidTip\": MessageLookupByLibrary.simpleMessage(\"在腳本模式下不生效\"),\n    \"overrideNtp\": MessageLookupByLibrary.simpleMessage(\"覆寫 NTP\"),\n    \"overrideNtpDesc\": MessageLookupByLibrary.simpleMessage(\n      \"開啟後將覆蓋配置中的 NTP 選項\",\n    ),\n    \"overrideOriginRules\": MessageLookupByLibrary.simpleMessage(\"覆蓋原始規則\"),\n    \"overrideSniffer\": MessageLookupByLibrary.simpleMessage(\"覆寫 Sniffer\"),\n    \"overrideSnifferDesc\": MessageLookupByLibrary.simpleMessage(\n      \"開啟後將覆蓋配置中的 Sniffer 選項\",\n    ),\n    \"overrideTestUrl\": MessageLookupByLibrary.simpleMessage(\"覆蓋配置\"),\n    \"overrideTunnel\": MessageLookupByLibrary.simpleMessage(\"覆寫 Tunnel\"),\n    \"overrideTunnelDesc\": MessageLookupByLibrary.simpleMessage(\n      \"開啟後將覆蓋配置中的 Tunnel 選項\",\n    ),\n    \"packageListPermissionDenied\": MessageLookupByLibrary.simpleMessage(\n      \"權限被拒絕。沒有權限無法存取應用程式清單。\",\n    ),\n    \"packageListPermissionRequired\": MessageLookupByLibrary.simpleMessage(\n      \"此功能需要存取已安裝應用程式清單的權限。是否授予此權限？\",\n    ),\n    \"palette\": MessageLookupByLibrary.simpleMessage(\"調色盤\"),\n    \"parsePureIp\": MessageLookupByLibrary.simpleMessage(\"解析純 IP 連線\"),\n    \"parsePureIpDesc\": MessageLookupByLibrary.simpleMessage(\"解析純 IP 連線\"),\n    \"password\": MessageLookupByLibrary.simpleMessage(\"密碼\"),\n    \"paste\": MessageLookupByLibrary.simpleMessage(\"貼上\"),\n    \"pleaseBindWebDAV\": MessageLookupByLibrary.simpleMessage(\"請綁定 WebDAV\"),\n    \"pleaseCloseSystemProxyFirst\": MessageLookupByLibrary.simpleMessage(\n      \"請先關閉系統代理\",\n    ),\n    \"pleaseCloseTunFirst\": MessageLookupByLibrary.simpleMessage(\"請先關閉虛擬網卡\"),\n    \"pleaseEnterScriptName\": MessageLookupByLibrary.simpleMessage(\"請輸入腳本名稱\"),\n    \"pleaseInputAdminPassword\": MessageLookupByLibrary.simpleMessage(\n      \"請輸入管理員密碼\",\n    ),\n    \"pleaseUploadFile\": MessageLookupByLibrary.simpleMessage(\"請上傳檔案\"),\n    \"pleaseUploadValidQrcode\": MessageLookupByLibrary.simpleMessage(\n      \"請上傳有效的二維碼\",\n    ),\n    \"port\": MessageLookupByLibrary.simpleMessage(\"連接埠\"),\n    \"portConflictTip\": MessageLookupByLibrary.simpleMessage(\"請輸入不同的連接埠\"),\n    \"portTip\": m7,\n    \"powerSwitch\": MessageLookupByLibrary.simpleMessage(\"啟動開關\"),\n    \"preferH3Desc\": MessageLookupByLibrary.simpleMessage(\"優先使用 DOH 的 http/3\"),\n    \"pressKeyboard\": MessageLookupByLibrary.simpleMessage(\"請按下按鍵\"),\n    \"preview\": MessageLookupByLibrary.simpleMessage(\"預覽\"),\n    \"profile\": MessageLookupByLibrary.simpleMessage(\"配置\"),\n    \"profileAutoUpdateIntervalInvalidValidationDesc\":\n        MessageLookupByLibrary.simpleMessage(\"請輸入有效間隔時間格式\"),\n    \"profileAutoUpdateIntervalNullValidationDesc\":\n        MessageLookupByLibrary.simpleMessage(\"請輸入自動更新間隔時間\"),\n    \"profileHasUpdate\": MessageLookupByLibrary.simpleMessage(\n      \"設定檔已經修改，是否關閉自動更新 \",\n    ),\n    \"profileNameNullValidationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"請輸入配置名稱\",\n    ),\n    \"profileParseErrorDesc\": MessageLookupByLibrary.simpleMessage(\"設定檔解析錯誤\"),\n    \"profileUrlInvalidValidationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"請輸入有效配置 URL\",\n    ),\n    \"profileUrlNullValidationDesc\": MessageLookupByLibrary.simpleMessage(\n      \"請輸入配置 URL\",\n    ),\n    \"profiles\": MessageLookupByLibrary.simpleMessage(\"配置\"),\n    \"profilesSort\": MessageLookupByLibrary.simpleMessage(\"配置排序\"),\n    \"progress\": MessageLookupByLibrary.simpleMessage(\"處理程序\"),\n    \"project\": MessageLookupByLibrary.simpleMessage(\"專案\"),\n    \"providers\": MessageLookupByLibrary.simpleMessage(\"提供者\"),\n    \"proxies\": MessageLookupByLibrary.simpleMessage(\"代理\"),\n    \"proxiesSetting\": MessageLookupByLibrary.simpleMessage(\"代理設定\"),\n    \"proxyChains\": MessageLookupByLibrary.simpleMessage(\"代理鏈\"),\n    \"proxyGroup\": MessageLookupByLibrary.simpleMessage(\"代理群組\"),\n    \"proxyNameserver\": MessageLookupByLibrary.simpleMessage(\"代理網域名稱伺服器\"),\n    \"proxyNameserverDesc\": MessageLookupByLibrary.simpleMessage(\"用於解析代理節點的網域\"),\n    \"proxyPort\": MessageLookupByLibrary.simpleMessage(\"代理連接埠\"),\n    \"proxyPortDesc\": MessageLookupByLibrary.simpleMessage(\"設定 Clash 監聽連接埠\"),\n    \"proxyProviders\": MessageLookupByLibrary.simpleMessage(\"代理提供者\"),\n    \"pulse\": MessageLookupByLibrary.simpleMessage(\"脈衝律動\"),\n    \"pureBlackMode\": MessageLookupByLibrary.simpleMessage(\"純黑模式\"),\n    \"qrcode\": MessageLookupByLibrary.simpleMessage(\"二維碼\"),\n    \"qrcodeDesc\": MessageLookupByLibrary.simpleMessage(\"掃描二維碼獲取設定檔\"),\n    \"quicGoDisableEcn\": MessageLookupByLibrary.simpleMessage(\"停用 QUIC 顯式擁塞通知\"),\n    \"quicGoDisableEcnDesc\": MessageLookupByLibrary.simpleMessage(\n      \"停用 QUIC 的顯式擁塞通知功能\",\n    ),\n    \"quicGoDisableGso\": MessageLookupByLibrary.simpleMessage(\"停用 QUIC 通用分段卸載\"),\n    \"quicGoDisableGsoDesc\": MessageLookupByLibrary.simpleMessage(\n      \"停用 QUIC 的通用分段卸載功能\",\n    ),\n    \"quicPortSniffer\": MessageLookupByLibrary.simpleMessage(\"QUIC 連接埠嗅探\"),\n    \"quickResponse\": MessageLookupByLibrary.simpleMessage(\"快速響應\"),\n    \"quickResponseDesc\": MessageLookupByLibrary.simpleMessage(\"網路發生變化時主動斷開連接\"),\n    \"rainbowScheme\": MessageLookupByLibrary.simpleMessage(\"彩虹\"),\n    \"recovery\": MessageLookupByLibrary.simpleMessage(\"還原\"),\n    \"recoveryAll\": MessageLookupByLibrary.simpleMessage(\"還原所有資料\"),\n    \"recoveryProfiles\": MessageLookupByLibrary.simpleMessage(\"僅還原設定檔\"),\n    \"recoveryStrategy\": MessageLookupByLibrary.simpleMessage(\"還原策略\"),\n    \"recoveryStrategy_compatible\": MessageLookupByLibrary.simpleMessage(\"相容\"),\n    \"recoveryStrategy_override\": MessageLookupByLibrary.simpleMessage(\"覆蓋\"),\n    \"recoverySuccess\": MessageLookupByLibrary.simpleMessage(\"還原成功\"),\n    \"redirPort\": MessageLookupByLibrary.simpleMessage(\"Redir 連接埠\"),\n    \"redo\": MessageLookupByLibrary.simpleMessage(\"重做\"),\n    \"refreshAppList\": MessageLookupByLibrary.simpleMessage(\"重新整理應用程式清單\"),\n    \"refreshAppListConfirm\": MessageLookupByLibrary.simpleMessage(\n      \"是否重新整理應用程式清單？\",\n    ),\n    \"regExp\": MessageLookupByLibrary.simpleMessage(\"正規表示式\"),\n    \"remote\": MessageLookupByLibrary.simpleMessage(\"遠端\"),\n    \"remoteBackupDesc\": MessageLookupByLibrary.simpleMessage(\"備份資料到 WebDAV\"),\n    \"remoteDestination\": MessageLookupByLibrary.simpleMessage(\"遠端目標\"),\n    \"remoteRecoveryDesc\": MessageLookupByLibrary.simpleMessage(\n      \"透過 WebDAV 還原資料\",\n    ),\n    \"remove\": MessageLookupByLibrary.simpleMessage(\"移除\"),\n    \"rename\": MessageLookupByLibrary.simpleMessage(\"重新命名\"),\n    \"request\": MessageLookupByLibrary.simpleMessage(\"請求\"),\n    \"requests\": MessageLookupByLibrary.simpleMessage(\"請求\"),\n    \"requestsDesc\": MessageLookupByLibrary.simpleMessage(\"查看最近請求記錄\"),\n    \"reset\": MessageLookupByLibrary.simpleMessage(\"重設\"),\n    \"resetTip\": MessageLookupByLibrary.simpleMessage(\"確定要重設嗎？\"),\n    \"resources\": MessageLookupByLibrary.simpleMessage(\"資源\"),\n    \"resourcesDesc\": MessageLookupByLibrary.simpleMessage(\"外部資源相關資訊\"),\n    \"respectRules\": MessageLookupByLibrary.simpleMessage(\"遵守規則\"),\n    \"respectRulesDesc\": MessageLookupByLibrary.simpleMessage(\"DNS 連線跟隨 Rules\"),\n    \"restart\": MessageLookupByLibrary.simpleMessage(\"重啟\"),\n    \"restartCoreDesc\": MessageLookupByLibrary.simpleMessage(\"是否手動重啟內核？\"),\n    \"restartCoreTitle\": MessageLookupByLibrary.simpleMessage(\"重啟內核\"),\n    \"restartTip\": MessageLookupByLibrary.simpleMessage(\"重啟TUN後改變生效\"),\n    \"retry\": MessageLookupByLibrary.simpleMessage(\"重試\"),\n    \"rotatingCircle\": MessageLookupByLibrary.simpleMessage(\"單圓自轉\"),\n    \"ru\": MessageLookupByLibrary.simpleMessage(\"俄語\"),\n    \"rule\": MessageLookupByLibrary.simpleMessage(\"規則\"),\n    \"ruleName\": MessageLookupByLibrary.simpleMessage(\"規則名稱\"),\n    \"ruleProviders\": MessageLookupByLibrary.simpleMessage(\"規則提供者\"),\n    \"ruleTarget\": MessageLookupByLibrary.simpleMessage(\"規則目標\"),\n    \"runTime\": MessageLookupByLibrary.simpleMessage(\"啟動時間\"),\n    \"runtimeConfig\": MessageLookupByLibrary.simpleMessage(\"執行時配置\"),\n    \"save\": MessageLookupByLibrary.simpleMessage(\"儲存\"),\n    \"saveChanges\": MessageLookupByLibrary.simpleMessage(\"是否儲存更改？\"),\n    \"saveTip\": MessageLookupByLibrary.simpleMessage(\"確定要儲存嗎？\"),\n    \"script\": MessageLookupByLibrary.simpleMessage(\"腳本\"),\n    \"scriptDesc\": MessageLookupByLibrary.simpleMessage(\"配置全局覆寫腳本\"),\n    \"search\": MessageLookupByLibrary.simpleMessage(\"搜尋\"),\n    \"seconds\": MessageLookupByLibrary.simpleMessage(\"秒\"),\n    \"secretCopied\": MessageLookupByLibrary.simpleMessage(\"密碼已複製到剪貼簿\"),\n    \"selectAll\": MessageLookupByLibrary.simpleMessage(\"全選\"),\n    \"selected\": MessageLookupByLibrary.simpleMessage(\"已選擇\"),\n    \"selectedCountTitle\": m8,\n    \"serviceReady\": MessageLookupByLibrary.simpleMessage(\"服務已就緒\"),\n    \"serviceRunning\": MessageLookupByLibrary.simpleMessage(\"服務正在執行中\"),\n    \"settings\": MessageLookupByLibrary.simpleMessage(\"設定\"),\n    \"show\": MessageLookupByLibrary.simpleMessage(\"顯示\"),\n    \"shrink\": MessageLookupByLibrary.simpleMessage(\"緊湊\"),\n    \"silentLaunch\": MessageLookupByLibrary.simpleMessage(\"靜默啟動\"),\n    \"silentLaunchDesc\": MessageLookupByLibrary.simpleMessage(\"不打開軟體直接在背景啟動\"),\n    \"size\": MessageLookupByLibrary.simpleMessage(\"尺寸\"),\n    \"skipDomain\": MessageLookupByLibrary.simpleMessage(\"跳過網域\"),\n    \"skipDstAddress\": MessageLookupByLibrary.simpleMessage(\"跳過目標 IP\"),\n    \"skipSrcAddress\": MessageLookupByLibrary.simpleMessage(\"跳過來源 IP\"),\n    \"smartAutoStop\": MessageLookupByLibrary.simpleMessage(\"智慧啟停\"),\n    \"smartAutoStopDesc\": MessageLookupByLibrary.simpleMessage(\"連線指定網路後停止代理服務\"),\n    \"smartAutoStopServiceRunning\": MessageLookupByLibrary.simpleMessage(\n      \"智慧啟停服務執行中\",\n    ),\n    \"smartDelayLaunch\": MessageLookupByLibrary.simpleMessage(\"智慧延遲\"),\n    \"smartDelayLaunchDesc\": MessageLookupByLibrary.simpleMessage(\"在網路成功連線以後啟動\"),\n    \"sniffer\": MessageLookupByLibrary.simpleMessage(\"Sniffer\"),\n    \"snifferAddressHint\": MessageLookupByLibrary.simpleMessage(\"每行一個地址\"),\n    \"snifferDesc\": MessageLookupByLibrary.simpleMessage(\"修改網域嗅探配置\"),\n    \"snifferDomainHint\": MessageLookupByLibrary.simpleMessage(\"每行一個網域\"),\n    \"snifferPorts\": MessageLookupByLibrary.simpleMessage(\"連接埠\"),\n    \"snifferPortsHint\": MessageLookupByLibrary.simpleMessage(\n      \"例如: 80, 8080-8880\",\n    ),\n    \"snifferStatus\": MessageLookupByLibrary.simpleMessage(\"狀態\"),\n    \"snifferStatusDesc\": MessageLookupByLibrary.simpleMessage(\"開啟嗅探服務設定\"),\n    \"socksPort\": MessageLookupByLibrary.simpleMessage(\"Socks 連接埠\"),\n    \"sort\": MessageLookupByLibrary.simpleMessage(\"排序\"),\n    \"source\": MessageLookupByLibrary.simpleMessage(\"來源\"),\n    \"sourceIp\": MessageLookupByLibrary.simpleMessage(\"來源 IP\"),\n    \"specialProxy\": MessageLookupByLibrary.simpleMessage(\"特殊代理\"),\n    \"specialRules\": MessageLookupByLibrary.simpleMessage(\"特殊規則\"),\n    \"spinningLines\": MessageLookupByLibrary.simpleMessage(\"流光旋繞\"),\n    \"stackMode\": MessageLookupByLibrary.simpleMessage(\"堆疊模式\"),\n    \"standard\": MessageLookupByLibrary.simpleMessage(\"標準\"),\n    \"start\": MessageLookupByLibrary.simpleMessage(\"啟動\"),\n    \"startTest\": MessageLookupByLibrary.simpleMessage(\"延遲測試\"),\n    \"startVpn\": MessageLookupByLibrary.simpleMessage(\"正在啟動\"),\n    \"status\": MessageLookupByLibrary.simpleMessage(\"狀態\"),\n    \"statusDesc\": MessageLookupByLibrary.simpleMessage(\"關閉後將使用系統 DNS\"),\n    \"stop\": MessageLookupByLibrary.simpleMessage(\"停止\"),\n    \"stopVpn\": MessageLookupByLibrary.simpleMessage(\"正在停止\"),\n    \"storeFix\": MessageLookupByLibrary.simpleMessage(\"商店修復\"),\n    \"storeFixDesc\": MessageLookupByLibrary.simpleMessage(\n      \"修復 Google Play 商店下載異常\",\n    ),\n    \"strictRoute\": MessageLookupByLibrary.simpleMessage(\"嚴格路由\"),\n    \"strictRouteDesc\": MessageLookupByLibrary.simpleMessage(\"使用 TUN 嚴格路由模式\"),\n    \"style\": MessageLookupByLibrary.simpleMessage(\"風格\"),\n    \"subRule\": MessageLookupByLibrary.simpleMessage(\"子規則\"),\n    \"submit\": MessageLookupByLibrary.simpleMessage(\"提交\"),\n    \"success\": MessageLookupByLibrary.simpleMessage(\"Success\"),\n    \"switchLabel\": MessageLookupByLibrary.simpleMessage(\"開關\"),\n    \"switchToDomesticIp\": MessageLookupByLibrary.simpleMessage(\"取得國內 IP\"),\n    \"sync\": MessageLookupByLibrary.simpleMessage(\"同步\"),\n    \"syncAll\": MessageLookupByLibrary.simpleMessage(\"全部同步\"),\n    \"syncFailed\": MessageLookupByLibrary.simpleMessage(\"同步失敗\"),\n    \"system\": MessageLookupByLibrary.simpleMessage(\"系統\"),\n    \"systemApp\": MessageLookupByLibrary.simpleMessage(\"系統應用程式\"),\n    \"systemFont\": MessageLookupByLibrary.simpleMessage(\"系統字體\"),\n    \"systemProxy\": MessageLookupByLibrary.simpleMessage(\"系統代理\"),\n    \"systemProxyDesc\": MessageLookupByLibrary.simpleMessage(\"設定系統代理\"),\n    \"tab\": MessageLookupByLibrary.simpleMessage(\"分頁\"),\n    \"tabAnimation\": MessageLookupByLibrary.simpleMessage(\"切換動畫\"),\n    \"tabAnimationDesc\": MessageLookupByLibrary.simpleMessage(\"僅在部分行動檢視中有效\"),\n    \"tcpConcurrent\": MessageLookupByLibrary.simpleMessage(\"TCP 並發\"),\n    \"tcpConcurrentDesc\": MessageLookupByLibrary.simpleMessage(\"開啟後允許 TCP 並發連線\"),\n    \"testUrl\": MessageLookupByLibrary.simpleMessage(\"測試連結\"),\n    \"textScale\": MessageLookupByLibrary.simpleMessage(\"文字縮放\"),\n    \"theme\": MessageLookupByLibrary.simpleMessage(\"主題\"),\n    \"themeColor\": MessageLookupByLibrary.simpleMessage(\"主題色彩\"),\n    \"themeDesc\": MessageLookupByLibrary.simpleMessage(\"設定主題色彩及圖示\"),\n    \"themeMode\": MessageLookupByLibrary.simpleMessage(\"主題模式\"),\n    \"threeBounce\": MessageLookupByLibrary.simpleMessage(\"靈珠躍動\"),\n    \"threeColumns\": MessageLookupByLibrary.simpleMessage(\"三列\"),\n    \"threeInOut\": MessageLookupByLibrary.simpleMessage(\"三星舒合\"),\n    \"tight\": MessageLookupByLibrary.simpleMessage(\"緊湊\"),\n    \"time\": MessageLookupByLibrary.simpleMessage(\"時間\"),\n    \"tip\": MessageLookupByLibrary.simpleMessage(\"提示\"),\n    \"tlsPortSniffer\": MessageLookupByLibrary.simpleMessage(\"TLS 連接埠嗅探\"),\n    \"toggle\": MessageLookupByLibrary.simpleMessage(\"切換\"),\n    \"tonalSpotScheme\": MessageLookupByLibrary.simpleMessage(\"調性點綴\"),\n    \"tooManyRules\": MessageLookupByLibrary.simpleMessage(\"最多允許 2 個規則\"),\n    \"tools\": MessageLookupByLibrary.simpleMessage(\"更多\"),\n    \"tproxyPort\": MessageLookupByLibrary.simpleMessage(\"Tproxy 連接埠\"),\n    \"trafficUsage\": MessageLookupByLibrary.simpleMessage(\"流量統計\"),\n    \"tryManualRefresh\": MessageLookupByLibrary.simpleMessage(\"請嘗試手動重新整理\"),\n    \"tun\": MessageLookupByLibrary.simpleMessage(\"虛擬網卡\"),\n    \"tunDesc\": MessageLookupByLibrary.simpleMessage(\"接管目前裝置全域流量\"),\n    \"tunEnableRequireAdmin\": MessageLookupByLibrary.simpleMessage(\n      \"啟用虛擬網卡需要管理員權限，請以管理員身分執行程式\",\n    ),\n    \"tunnel\": MessageLookupByLibrary.simpleMessage(\"Tunnel\"),\n    \"tunnelAddress\": MessageLookupByLibrary.simpleMessage(\"監聽地址\"),\n    \"tunnelAddressHint\": MessageLookupByLibrary.simpleMessage(\n      \"例如: 127.0.0.1:6553\",\n    ),\n    \"tunnelDesc\": MessageLookupByLibrary.simpleMessage(\"使用流量轉發隧道\"),\n    \"tunnelList\": MessageLookupByLibrary.simpleMessage(\"轉發清單\"),\n    \"tunnelNetwork\": MessageLookupByLibrary.simpleMessage(\"網路協定\"),\n    \"tunnelNetworkHint\": MessageLookupByLibrary.simpleMessage(\"例如: tcp, udp\"),\n    \"tunnelProxy\": MessageLookupByLibrary.simpleMessage(\"代理名稱\"),\n    \"tunnelProxyHint\": MessageLookupByLibrary.simpleMessage(\"例如: proxy (可選)\"),\n    \"tunnelTarget\": MessageLookupByLibrary.simpleMessage(\"目標地址\"),\n    \"tunnelTargetHint\": MessageLookupByLibrary.simpleMessage(\n      \"例如: 114.114.114.114:53\",\n    ),\n    \"twoColumns\": MessageLookupByLibrary.simpleMessage(\"兩列\"),\n    \"unableToUpdateCurrentProfileDesc\": MessageLookupByLibrary.simpleMessage(\n      \"無法更新目前的設定檔\",\n    ),\n    \"undo\": MessageLookupByLibrary.simpleMessage(\"復原\"),\n    \"unifiedDelay\": MessageLookupByLibrary.simpleMessage(\"統一延遲\"),\n    \"unifiedDelayDesc\": MessageLookupByLibrary.simpleMessage(\"去除交握解析等額外延遲\"),\n    \"unknown\": MessageLookupByLibrary.simpleMessage(\"未知\"),\n    \"unnamed\": MessageLookupByLibrary.simpleMessage(\"未命名\"),\n    \"update\": MessageLookupByLibrary.simpleMessage(\"更新\"),\n    \"upload\": MessageLookupByLibrary.simpleMessage(\"上傳\"),\n    \"url\": MessageLookupByLibrary.simpleMessage(\"URL\"),\n    \"urlDesc\": MessageLookupByLibrary.simpleMessage(\"透過 URL 獲取設定檔\"),\n    \"urlTip\": m9,\n    \"useHosts\": MessageLookupByLibrary.simpleMessage(\"使用 Hosts\"),\n    \"useSystemHosts\": MessageLookupByLibrary.simpleMessage(\"使用系統 Hosts\"),\n    \"value\": MessageLookupByLibrary.simpleMessage(\"值\"),\n    \"vibrantScheme\": MessageLookupByLibrary.simpleMessage(\"活力\"),\n    \"view\": MessageLookupByLibrary.simpleMessage(\"查看\"),\n    \"vpnDesc\": MessageLookupByLibrary.simpleMessage(\"修改VPN相關設定\"),\n    \"vpnEnableDesc\": MessageLookupByLibrary.simpleMessage(\n      \"透過 VpnService 自動路由系統所有流量\",\n    ),\n    \"vpnSystemProxyDesc\": MessageLookupByLibrary.simpleMessage(\n      \"為 VpnService 附加 HTTP 代理\",\n    ),\n    \"vpnTip\": MessageLookupByLibrary.simpleMessage(\"重啟VPN後改變生效\"),\n    \"wakelock\": MessageLookupByLibrary.simpleMessage(\"亮螢幕鎖\"),\n    \"wakelockDescription\": MessageLookupByLibrary.simpleMessage(\n      \"本功能不需要任何特殊權限，因為它僅啟用螢幕喚醒鎖，而不是任何 CPU 喚醒鎖，應用程式會在背景保持必要的活躍，且螢幕不會自動熄滅，這在一些場景下會很有用\",\n    ),\n    \"wave\": MessageLookupByLibrary.simpleMessage(\"波浪起伏\"),\n    \"webDAVConfiguration\": MessageLookupByLibrary.simpleMessage(\"WebDAV 設定\"),\n    \"whitelist\": MessageLookupByLibrary.simpleMessage(\"白名單\"),\n    \"whitelistMode\": MessageLookupByLibrary.simpleMessage(\"白名單模式\"),\n    \"writeToSystem\": MessageLookupByLibrary.simpleMessage(\"寫入系統\"),\n    \"writeToSystemDesc\": MessageLookupByLibrary.simpleMessage(\"需要管理員權限\"),\n    \"years\": MessageLookupByLibrary.simpleMessage(\"年\"),\n    \"zh_CN\": MessageLookupByLibrary.simpleMessage(\"簡體中文\"),\n    \"zh_TC\": MessageLookupByLibrary.simpleMessage(\"繁體中文\"),\n  };\n}\n"
  },
  {
    "path": "lib/l10n/l10n.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\nimport 'package:flutter/material.dart';\nimport 'package:intl/intl.dart';\nimport 'intl/messages_all.dart';\n\n// **************************************************************************\n// Generator: Flutter Intl IDE plugin\n// Made by Localizely\n// **************************************************************************\n\n// ignore_for_file: non_constant_identifier_names, lines_longer_than_80_chars\n// ignore_for_file: join_return_with_assignment, prefer_final_in_for_each\n// ignore_for_file: avoid_redundant_argument_values, avoid_escaping_inner_quotes\n\nclass AppLocalizations {\n  AppLocalizations();\n\n  static AppLocalizations? _current;\n\n  static AppLocalizations get current {\n    assert(\n      _current != null,\n      'No instance of AppLocalizations was loaded. Try to initialize the AppLocalizations delegate before accessing AppLocalizations.current.',\n    );\n    return _current!;\n  }\n\n  static const AppLocalizationDelegate delegate = AppLocalizationDelegate();\n\n  static Future<AppLocalizations> load(Locale locale) {\n    final name = (locale.countryCode?.isEmpty ?? false)\n        ? locale.languageCode\n        : locale.toString();\n    final localeName = Intl.canonicalizedLocale(name);\n    return initializeMessages(localeName).then((_) {\n      Intl.defaultLocale = localeName;\n      final instance = AppLocalizations();\n      AppLocalizations._current = instance;\n\n      return instance;\n    });\n  }\n\n  static AppLocalizations of(BuildContext context) {\n    final instance = AppLocalizations.maybeOf(context);\n    assert(\n      instance != null,\n      'No instance of AppLocalizations present in the widget tree. Did you add AppLocalizations.delegate in localizationsDelegates?',\n    );\n    return instance!;\n  }\n\n  static AppLocalizations? maybeOf(BuildContext context) {\n    return Localizations.of<AppLocalizations>(context, AppLocalizations);\n  }\n\n  /// `Rule`\n  String get rule {\n    return Intl.message('Rule', name: 'rule', desc: '', args: []);\n  }\n\n  /// `Global`\n  String get global {\n    return Intl.message('Global', name: 'global', desc: '', args: []);\n  }\n\n  /// `Direct`\n  String get direct {\n    return Intl.message('Direct', name: 'direct', desc: '', args: []);\n  }\n\n  /// `Dashboard`\n  String get dashboard {\n    return Intl.message('Dashboard', name: 'dashboard', desc: '', args: []);\n  }\n\n  /// `Proxies`\n  String get proxies {\n    return Intl.message('Proxies', name: 'proxies', desc: '', args: []);\n  }\n\n  /// `Profile`\n  String get profile {\n    return Intl.message('Profile', name: 'profile', desc: '', args: []);\n  }\n\n  /// `Profiles`\n  String get profiles {\n    return Intl.message('Profiles', name: 'profiles', desc: '', args: []);\n  }\n\n  /// `Tools`\n  String get tools {\n    return Intl.message('Tools', name: 'tools', desc: '', args: []);\n  }\n\n  /// `Logs`\n  String get logs {\n    return Intl.message('Logs', name: 'logs', desc: '', args: []);\n  }\n\n  /// `View captured logs`\n  String get logsDesc {\n    return Intl.message(\n      'View captured logs',\n      name: 'logsDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Resources`\n  String get resources {\n    return Intl.message('Resources', name: 'resources', desc: '', args: []);\n  }\n\n  /// `Sync All`\n  String get syncAll {\n    return Intl.message('Sync All', name: 'syncAll', desc: '', args: []);\n  }\n\n  /// `Sync Failed`\n  String get syncFailed {\n    return Intl.message('Sync Failed', name: 'syncFailed', desc: '', args: []);\n  }\n\n  /// `External resource info`\n  String get resourcesDesc {\n    return Intl.message(\n      'External resource info',\n      name: 'resourcesDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Global override script config`\n  String get scriptDesc {\n    return Intl.message(\n      'Global override script config',\n      name: 'scriptDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Traffic Usage`\n  String get trafficUsage {\n    return Intl.message(\n      'Traffic Usage',\n      name: 'trafficUsage',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Core Info`\n  String get coreInfo {\n    return Intl.message('Core Info', name: 'coreInfo', desc: '', args: []);\n  }\n\n  /// `Network Speed`\n  String get networkSpeed {\n    return Intl.message(\n      'Network Speed',\n      name: 'networkSpeed',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Outbound Mode`\n  String get outboundMode {\n    return Intl.message(\n      'Outbound Mode',\n      name: 'outboundMode',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Network Detection`\n  String get networkDetection {\n    return Intl.message(\n      'Network Detection',\n      name: 'networkDetection',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Upload`\n  String get upload {\n    return Intl.message('Upload', name: 'upload', desc: '', args: []);\n  }\n\n  /// `Download`\n  String get download {\n    return Intl.message('Download', name: 'download', desc: '', args: []);\n  }\n\n  /// `No Proxy`\n  String get noProxy {\n    return Intl.message('No Proxy', name: 'noProxy', desc: '', args: []);\n  }\n\n  /// `Please create or add a valid profile`\n  String get noProxyDesc {\n    return Intl.message(\n      'Please create or add a valid profile',\n      name: 'noProxyDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `No profile. Please add one.`\n  String get nullProfileDesc {\n    return Intl.message(\n      'No profile. Please add one.',\n      name: 'nullProfileDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Settings`\n  String get settings {\n    return Intl.message('Settings', name: 'settings', desc: '', args: []);\n  }\n\n  /// `Language`\n  String get language {\n    return Intl.message('Language', name: 'language', desc: '', args: []);\n  }\n\n  /// `Default`\n  String get defaultText {\n    return Intl.message('Default', name: 'defaultText', desc: '', args: []);\n  }\n\n  /// `More`\n  String get more {\n    return Intl.message('More', name: 'more', desc: '', args: []);\n  }\n\n  /// `Other`\n  String get other {\n    return Intl.message('Other', name: 'other', desc: '', args: []);\n  }\n\n  /// `Enhanced Tools`\n  String get otherSettings {\n    return Intl.message(\n      'Enhanced Tools',\n      name: 'otherSettings',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Modify enhanced tool settings`\n  String get otherSettingsDesc {\n    return Intl.message(\n      'Modify enhanced tool settings',\n      name: 'otherSettingsDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Smart Auto-Stop`\n  String get smartAutoStop {\n    return Intl.message(\n      'Smart Auto-Stop',\n      name: 'smartAutoStop',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Stop VPN on specific networks`\n  String get smartAutoStopDesc {\n    return Intl.message(\n      'Stop VPN on specific networks',\n      name: 'smartAutoStopDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Network Match`\n  String get networkMatch {\n    return Intl.message(\n      'Network Match',\n      name: 'networkMatch',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Max 2 IPs/CIDRs, comma-separated`\n  String get networkMatchHint {\n    return Intl.message(\n      'Max 2 IPs/CIDRs, comma-separated',\n      name: 'networkMatchHint',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Smart Auto-Stop running`\n  String get smartAutoStopServiceRunning {\n    return Intl.message(\n      'Smart Auto-Stop running',\n      name: 'smartAutoStopServiceRunning',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Service Running`\n  String get serviceRunning {\n    return Intl.message(\n      'Service Running',\n      name: 'serviceRunning',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Connected`\n  String get coreConnected {\n    return Intl.message('Connected', name: 'coreConnected', desc: '', args: []);\n  }\n\n  /// `Suspended`\n  String get coreSuspended {\n    return Intl.message('Suspended', name: 'coreSuspended', desc: '', args: []);\n  }\n\n  /// `Invalid IP or CIDR format`\n  String get invalidIpFormat {\n    return Intl.message(\n      'Invalid IP or CIDR format',\n      name: 'invalidIpFormat',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Max 2 rules allowed`\n  String get tooManyRules {\n    return Intl.message(\n      'Max 2 rules allowed',\n      name: 'tooManyRules',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Doze Support`\n  String get dozeSuspend {\n    return Intl.message(\n      'Doze Support',\n      name: 'dozeSuspend',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Sync with system Doze mode`\n  String get dozeSuspendDesc {\n    return Intl.message(\n      'Sync with system Doze mode',\n      name: 'dozeSuspendDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Store Fix`\n  String get storeFix {\n    return Intl.message('Store Fix', name: 'storeFix', desc: '', args: []);\n  }\n\n  /// `Fix Play Store download issues`\n  String get storeFixDesc {\n    return Intl.message(\n      'Fix Play Store download issues',\n      name: 'storeFixDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Disable QUIC`\n  String get disableQuic {\n    return Intl.message(\n      'Disable QUIC',\n      name: 'disableQuic',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Disable QUIC to resolve specific network issues`\n  String get disableQuicDesc {\n    return Intl.message(\n      'Disable QUIC to resolve specific network issues',\n      name: 'disableQuicDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Exclude China`\n  String get excludeChina {\n    return Intl.message(\n      'Exclude China',\n      name: 'excludeChina',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Allow China QUIC traffic instead of blocking all`\n  String get excludeChinaDesc {\n    return Intl.message(\n      'Allow China QUIC traffic instead of blocking all',\n      name: 'excludeChinaDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `FCM Optimization`\n  String get fcmOptimization {\n    return Intl.message(\n      'FCM Optimization',\n      name: 'fcmOptimization',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Enhance FCM connection stability`\n  String get fcmOptimizationDesc {\n    return Intl.message(\n      'Enhance FCM connection stability',\n      name: 'fcmOptimizationDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Quick Response`\n  String get quickResponse {\n    return Intl.message(\n      'Quick Response',\n      name: 'quickResponse',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Disconnect on network change (WiFi/Mobile)`\n  String get quickResponseDesc {\n    return Intl.message(\n      'Disconnect on network change (WiFi/Mobile)',\n      name: 'quickResponseDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Network Fix`\n  String get networkFix {\n    return Intl.message('Network Fix', name: 'networkFix', desc: '', args: []);\n  }\n\n  /// `Fix Windows network globe icon issue`\n  String get networkFixDesc {\n    return Intl.message(\n      'Fix Windows network globe icon issue',\n      name: 'networkFixDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Battery Optimization`\n  String get batteryOptimization {\n    return Intl.message(\n      'Battery Optimization',\n      name: 'batteryOptimization',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Request battery optimization whitelist`\n  String get batteryOptimizationDesc {\n    return Intl.message(\n      'Request battery optimization whitelist',\n      name: 'batteryOptimizationDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Already in whitelist`\n  String get alreadyInWhitelist {\n    return Intl.message(\n      'Already in whitelist',\n      name: 'alreadyInWhitelist',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `About`\n  String get about {\n    return Intl.message('About', name: 'about', desc: '', args: []);\n  }\n\n  /// `English`\n  String get en {\n    return Intl.message('English', name: 'en', desc: '', args: []);\n  }\n\n  /// `Japanese`\n  String get ja {\n    return Intl.message('Japanese', name: 'ja', desc: '', args: []);\n  }\n\n  /// `Russian`\n  String get ru {\n    return Intl.message('Russian', name: 'ru', desc: '', args: []);\n  }\n\n  /// `Simplified Chinese`\n  String get zh_CN {\n    return Intl.message(\n      'Simplified Chinese',\n      name: 'zh_CN',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Traditional Chinese`\n  String get zh_TC {\n    return Intl.message(\n      'Traditional Chinese',\n      name: 'zh_TC',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Theme`\n  String get theme {\n    return Intl.message('Theme', name: 'theme', desc: '', args: []);\n  }\n\n  /// `Set theme color and icon`\n  String get themeDesc {\n    return Intl.message(\n      'Set theme color and icon',\n      name: 'themeDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Override`\n  String get override {\n    return Intl.message('Override', name: 'override', desc: '', args: []);\n  }\n\n  /// `Override proxy configurations`\n  String get overrideDesc {\n    return Intl.message(\n      'Override proxy configurations',\n      name: 'overrideDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Allow LAN`\n  String get allowLan {\n    return Intl.message('Allow LAN', name: 'allowLan', desc: '', args: []);\n  }\n\n  /// `Allow LAN access to proxy`\n  String get allowLanDesc {\n    return Intl.message(\n      'Allow LAN access to proxy',\n      name: 'allowLanDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `TUN`\n  String get tun {\n    return Intl.message('TUN', name: 'tun', desc: '', args: []);\n  }\n\n  /// `Take over global device traffic`\n  String get tunDesc {\n    return Intl.message(\n      'Take over global device traffic',\n      name: 'tunDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Minimize on Exit`\n  String get minimizeOnExit {\n    return Intl.message(\n      'Minimize on Exit',\n      name: 'minimizeOnExit',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Override default exit behavior`\n  String get minimizeOnExitDesc {\n    return Intl.message(\n      'Override default exit behavior',\n      name: 'minimizeOnExitDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Auto Launch`\n  String get autoLaunch {\n    return Intl.message('Auto Launch', name: 'autoLaunch', desc: '', args: []);\n  }\n\n  /// `Launch on system startup`\n  String get autoLaunchDesc {\n    return Intl.message(\n      'Launch on system startup',\n      name: 'autoLaunchDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Smart Delay`\n  String get smartDelayLaunch {\n    return Intl.message(\n      'Smart Delay',\n      name: 'smartDelayLaunch',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Launch after network connected`\n  String get smartDelayLaunchDesc {\n    return Intl.message(\n      'Launch after network connected',\n      name: 'smartDelayLaunchDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Silent Launch`\n  String get silentLaunch {\n    return Intl.message(\n      'Silent Launch',\n      name: 'silentLaunch',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Start in the background`\n  String get silentLaunchDesc {\n    return Intl.message(\n      'Start in the background',\n      name: 'silentLaunchDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Auto Run`\n  String get autoRun {\n    return Intl.message('Auto Run', name: 'autoRun', desc: '', args: []);\n  }\n\n  /// `Connect on app launch`\n  String get autoRunDesc {\n    return Intl.message(\n      'Connect on app launch',\n      name: 'autoRunDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Log Capture`\n  String get logcat {\n    return Intl.message('Log Capture', name: 'logcat', desc: '', args: []);\n  }\n\n  /// `Show log capture entry`\n  String get logcatDesc {\n    return Intl.message(\n      'Show log capture entry',\n      name: 'logcatDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Crash Analytics`\n  String get enableCrashReport {\n    return Intl.message(\n      'Crash Analytics',\n      name: 'enableCrashReport',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Upload crash logs when needed`\n  String get enableCrashReportDesc {\n    return Intl.message(\n      'Upload crash logs when needed',\n      name: 'enableCrashReportDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Auto Check Updates`\n  String get autoCheckUpdate {\n    return Intl.message(\n      'Auto Check Updates',\n      name: 'autoCheckUpdate',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Check updates on app launch`\n  String get autoCheckUpdateDesc {\n    return Intl.message(\n      'Check updates on app launch',\n      name: 'autoCheckUpdateDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Access Control`\n  String get accessControl {\n    return Intl.message(\n      'Access Control',\n      name: 'accessControl',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Configure per-app proxy access`\n  String get accessControlDesc {\n    return Intl.message(\n      'Configure per-app proxy access',\n      name: 'accessControlDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Clear Cache`\n  String get clearCacheTitle {\n    return Intl.message(\n      'Clear Cache',\n      name: 'clearCacheTitle',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Clear FakeIP and DNS cache?`\n  String get clearCacheDesc {\n    return Intl.message(\n      'Clear FakeIP and DNS cache?',\n      name: 'clearCacheDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Force Garbage Collection`\n  String get forceGCTitle {\n    return Intl.message(\n      'Force Garbage Collection',\n      name: 'forceGCTitle',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Force kernel garbage collection? Experimental, use with caution.`\n  String get forceGCDesc {\n    return Intl.message(\n      'Force kernel garbage collection? Experimental, use with caution.',\n      name: 'forceGCDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `FCM support depends on your device; results are for reference. Disable 'Allow Bypass VPN' in network settings for accurate results.`\n  String get fcmTip {\n    return Intl.message(\n      'FCM support depends on your device; results are for reference. Disable \\'Allow Bypass VPN\\' in network settings for accurate results.',\n      name: 'fcmTip',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Application`\n  String get application {\n    return Intl.message('Application', name: 'application', desc: '', args: []);\n  }\n\n  /// `Modify application settings`\n  String get applicationDesc {\n    return Intl.message(\n      'Modify application settings',\n      name: 'applicationDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Edit`\n  String get edit {\n    return Intl.message('Edit', name: 'edit', desc: '', args: []);\n  }\n\n  /// `Confirm`\n  String get confirm {\n    return Intl.message('Confirm', name: 'confirm', desc: '', args: []);\n  }\n\n  /// `Update`\n  String get update {\n    return Intl.message('Update', name: 'update', desc: '', args: []);\n  }\n\n  /// `Add`\n  String get add {\n    return Intl.message('Add', name: 'add', desc: '', args: []);\n  }\n\n  /// `Save`\n  String get save {\n    return Intl.message('Save', name: 'save', desc: '', args: []);\n  }\n\n  /// `Delete`\n  String get delete {\n    return Intl.message('Delete', name: 'delete', desc: '', args: []);\n  }\n\n  /// `Years`\n  String get years {\n    return Intl.message('Years', name: 'years', desc: '', args: []);\n  }\n\n  /// `Months`\n  String get months {\n    return Intl.message('Months', name: 'months', desc: '', args: []);\n  }\n\n  /// `Hours`\n  String get hours {\n    return Intl.message('Hours', name: 'hours', desc: '', args: []);\n  }\n\n  /// `Days`\n  String get days {\n    return Intl.message('Days', name: 'days', desc: '', args: []);\n  }\n\n  /// `Minutes`\n  String get minutes {\n    return Intl.message('Minutes', name: 'minutes', desc: '', args: []);\n  }\n\n  /// `Seconds`\n  String get seconds {\n    return Intl.message('Seconds', name: 'seconds', desc: '', args: []);\n  }\n\n  /// ` Ago`\n  String get ago {\n    return Intl.message(' Ago', name: 'ago', desc: '', args: []);\n  }\n\n  /// `Please close TUN first`\n  String get pleaseCloseTunFirst {\n    return Intl.message(\n      'Please close TUN first',\n      name: 'pleaseCloseTunFirst',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Please close System Proxy first`\n  String get pleaseCloseSystemProxyFirst {\n    return Intl.message(\n      'Please close System Proxy first',\n      name: 'pleaseCloseSystemProxyFirst',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Just now`\n  String get just {\n    return Intl.message('Just now', name: 'just', desc: '', args: []);\n  }\n\n  /// `QR Code`\n  String get qrcode {\n    return Intl.message('QR Code', name: 'qrcode', desc: '', args: []);\n  }\n\n  /// `Scan QR code to get profile`\n  String get qrcodeDesc {\n    return Intl.message(\n      'Scan QR code to get profile',\n      name: 'qrcodeDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Clipboard`\n  String get clipboard {\n    return Intl.message('Clipboard', name: 'clipboard', desc: '', args: []);\n  }\n\n  /// `Get profile link from clipboard`\n  String get clipboardDesc {\n    return Intl.message(\n      'Get profile link from clipboard',\n      name: 'clipboardDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `URL`\n  String get url {\n    return Intl.message('URL', name: 'url', desc: '', args: []);\n  }\n\n  /// `Get profile via URL`\n  String get urlDesc {\n    return Intl.message(\n      'Get profile via URL',\n      name: 'urlDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `File`\n  String get file {\n    return Intl.message('File', name: 'file', desc: '', args: []);\n  }\n\n  /// `Upload profile file`\n  String get fileDesc {\n    return Intl.message(\n      'Upload profile file',\n      name: 'fileDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Name`\n  String get name {\n    return Intl.message('Name', name: 'name', desc: '', args: []);\n  }\n\n  /// `Please enter a profile name`\n  String get profileNameNullValidationDesc {\n    return Intl.message(\n      'Please enter a profile name',\n      name: 'profileNameNullValidationDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Please enter a profile URL`\n  String get profileUrlNullValidationDesc {\n    return Intl.message(\n      'Please enter a profile URL',\n      name: 'profileUrlNullValidationDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Please enter a valid URL`\n  String get profileUrlInvalidValidationDesc {\n    return Intl.message(\n      'Please enter a valid URL',\n      name: 'profileUrlInvalidValidationDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Auto Update`\n  String get autoUpdate {\n    return Intl.message('Auto Update', name: 'autoUpdate', desc: '', args: []);\n  }\n\n  /// `Auto update interval (min)`\n  String get autoUpdateInterval {\n    return Intl.message(\n      'Auto update interval (min)',\n      name: 'autoUpdateInterval',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Please enter update interval`\n  String get profileAutoUpdateIntervalNullValidationDesc {\n    return Intl.message(\n      'Please enter update interval',\n      name: 'profileAutoUpdateIntervalNullValidationDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Please enter a valid interval`\n  String get profileAutoUpdateIntervalInvalidValidationDesc {\n    return Intl.message(\n      'Please enter a valid interval',\n      name: 'profileAutoUpdateIntervalInvalidValidationDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Theme Mode`\n  String get themeMode {\n    return Intl.message('Theme Mode', name: 'themeMode', desc: '', args: []);\n  }\n\n  /// `Theme Color`\n  String get themeColor {\n    return Intl.message('Theme Color', name: 'themeColor', desc: '', args: []);\n  }\n\n  /// `Preview`\n  String get preview {\n    return Intl.message('Preview', name: 'preview', desc: '', args: []);\n  }\n\n  /// `Runtime Config`\n  String get runtimeConfig {\n    return Intl.message(\n      'Runtime Config',\n      name: 'runtimeConfig',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Camera Permission Denied`\n  String get cameraPermissionDenied {\n    return Intl.message(\n      'Camera Permission Denied',\n      name: 'cameraPermissionDenied',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Camera permission is required to scan QR codes. Please grant it in settings.`\n  String get cameraPermissionDesc {\n    return Intl.message(\n      'Camera permission is required to scan QR codes. Please grant it in settings.',\n      name: 'cameraPermissionDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Open Settings`\n  String get openSettings {\n    return Intl.message(\n      'Open Settings',\n      name: 'openSettings',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Retry`\n  String get retry {\n    return Intl.message('Retry', name: 'retry', desc: '', args: []);\n  }\n\n  /// `Permission to access installed apps is required. Grant now?`\n  String get packageListPermissionRequired {\n    return Intl.message(\n      'Permission to access installed apps is required. Grant now?',\n      name: 'packageListPermissionRequired',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Permission denied. Cannot access app list.`\n  String get packageListPermissionDenied {\n    return Intl.message(\n      'Permission denied. Cannot access app list.',\n      name: 'packageListPermissionDenied',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Auto`\n  String get auto {\n    return Intl.message('Auto', name: 'auto', desc: '', args: []);\n  }\n\n  /// `Light`\n  String get light {\n    return Intl.message('Light', name: 'light', desc: '', args: []);\n  }\n\n  /// `Dark`\n  String get dark {\n    return Intl.message('Dark', name: 'dark', desc: '', args: []);\n  }\n\n  /// `Import from URL`\n  String get importFromURL {\n    return Intl.message(\n      'Import from URL',\n      name: 'importFromURL',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Submit`\n  String get submit {\n    return Intl.message('Submit', name: 'submit', desc: '', args: []);\n  }\n\n  /// `Do you want to pass`\n  String get doYouWantToPass {\n    return Intl.message(\n      'Do you want to pass',\n      name: 'doYouWantToPass',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Create`\n  String get create {\n    return Intl.message('Create', name: 'create', desc: '', args: []);\n  }\n\n  /// `Default Sort`\n  String get defaultSort {\n    return Intl.message(\n      'Default Sort',\n      name: 'defaultSort',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Sort by Delay`\n  String get delaySort {\n    return Intl.message('Sort by Delay', name: 'delaySort', desc: '', args: []);\n  }\n\n  /// `Sort by Name`\n  String get nameSort {\n    return Intl.message('Sort by Name', name: 'nameSort', desc: '', args: []);\n  }\n\n  /// `Please upload a file`\n  String get pleaseUploadFile {\n    return Intl.message(\n      'Please upload a file',\n      name: 'pleaseUploadFile',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Please upload a valid QR code`\n  String get pleaseUploadValidQrcode {\n    return Intl.message(\n      'Please upload a valid QR code',\n      name: 'pleaseUploadValidQrcode',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Blacklist Mode`\n  String get blacklistMode {\n    return Intl.message(\n      'Blacklist Mode',\n      name: 'blacklistMode',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Whitelist Mode`\n  String get whitelistMode {\n    return Intl.message(\n      'Whitelist Mode',\n      name: 'whitelistMode',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Filter System Apps`\n  String get filterSystemApp {\n    return Intl.message(\n      'Filter System Apps',\n      name: 'filterSystemApp',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Show System Apps`\n  String get cancelFilterSystemApp {\n    return Intl.message(\n      'Show System Apps',\n      name: 'cancelFilterSystemApp',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Select All`\n  String get selectAll {\n    return Intl.message('Select All', name: 'selectAll', desc: '', args: []);\n  }\n\n  /// `Deselect All`\n  String get cancelSelectAll {\n    return Intl.message(\n      'Deselect All',\n      name: 'cancelSelectAll',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `App Access Control`\n  String get appAccessControl {\n    return Intl.message(\n      'App Access Control',\n      name: 'appAccessControl',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Only route selected apps through VPN`\n  String get accessControlAllowDesc {\n    return Intl.message(\n      'Only route selected apps through VPN',\n      name: 'accessControlAllowDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Exclude selected apps from VPN`\n  String get accessControlNotAllowDesc {\n    return Intl.message(\n      'Exclude selected apps from VPN',\n      name: 'accessControlNotAllowDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Selected`\n  String get selected {\n    return Intl.message('Selected', name: 'selected', desc: '', args: []);\n  }\n\n  /// `Unable to update current profile`\n  String get unableToUpdateCurrentProfileDesc {\n    return Intl.message(\n      'Unable to update current profile',\n      name: 'unableToUpdateCurrentProfileDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `No more info`\n  String get noMoreInfoDesc {\n    return Intl.message(\n      'No more info',\n      name: 'noMoreInfoDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Profile parse error`\n  String get profileParseErrorDesc {\n    return Intl.message(\n      'Profile parse error',\n      name: 'profileParseErrorDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Proxy Port`\n  String get proxyPort {\n    return Intl.message('Proxy Port', name: 'proxyPort', desc: '', args: []);\n  }\n\n  /// `Set the Clash listening port`\n  String get proxyPortDesc {\n    return Intl.message(\n      'Set the Clash listening port',\n      name: 'proxyPortDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Port`\n  String get port {\n    return Intl.message('Port', name: 'port', desc: '', args: []);\n  }\n\n  /// `Log Level`\n  String get logLevel {\n    return Intl.message('Log Level', name: 'logLevel', desc: '', args: []);\n  }\n\n  /// `Show`\n  String get show {\n    return Intl.message('Show', name: 'show', desc: '', args: []);\n  }\n\n  /// `Exit`\n  String get exit {\n    return Intl.message('Exit', name: 'exit', desc: '', args: []);\n  }\n\n  /// `System Proxy`\n  String get systemProxy {\n    return Intl.message(\n      'System Proxy',\n      name: 'systemProxy',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Project`\n  String get project {\n    return Intl.message('Project', name: 'project', desc: '', args: []);\n  }\n\n  /// `Core`\n  String get core {\n    return Intl.message('Core', name: 'core', desc: '', args: []);\n  }\n\n  /// `Tab Animation`\n  String get tabAnimation {\n    return Intl.message(\n      'Tab Animation',\n      name: 'tabAnimation',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Bettbox is based on the powerful and flexible Mihomo (Clash.Meta) proxy kernel, dedicated to a superior user experience. Forked from FlClash: Better Experience, Out of the box`\n  String get desc {\n    return Intl.message(\n      'Bettbox is based on the powerful and flexible Mihomo (Clash.Meta) proxy kernel, dedicated to a superior user experience. Forked from FlClash: Better Experience, Out of the box',\n      name: 'desc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Starting...`\n  String get startVpn {\n    return Intl.message('Starting...', name: 'startVpn', desc: '', args: []);\n  }\n\n  /// `Stopping...`\n  String get stopVpn {\n    return Intl.message('Stopping...', name: 'stopVpn', desc: '', args: []);\n  }\n\n  /// `New Version Found`\n  String get discovery {\n    return Intl.message(\n      'New Version Found',\n      name: 'discovery',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Compatible Mode`\n  String get compatible {\n    return Intl.message(\n      'Compatible Mode',\n      name: 'compatible',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Reduces some features for full Clash compatibility`\n  String get compatibleDesc {\n    return Intl.message(\n      'Reduces some features for full Clash compatibility',\n      name: 'compatibleDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Current proxy group cannot be selected.`\n  String get notSelectedTip {\n    return Intl.message(\n      'Current proxy group cannot be selected.',\n      name: 'notSelectedTip',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Tip`\n  String get tip {\n    return Intl.message('Tip', name: 'tip', desc: '', args: []);\n  }\n\n  /// `Backup & Restore`\n  String get backupAndRecovery {\n    return Intl.message(\n      'Backup & Restore',\n      name: 'backupAndRecovery',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Sync data via WebDAV or local files`\n  String get backupAndRecoveryDesc {\n    return Intl.message(\n      'Sync data via WebDAV or local files',\n      name: 'backupAndRecoveryDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Account`\n  String get account {\n    return Intl.message('Account', name: 'account', desc: '', args: []);\n  }\n\n  /// `Backup`\n  String get backup {\n    return Intl.message('Backup', name: 'backup', desc: '', args: []);\n  }\n\n  /// `Restore`\n  String get recovery {\n    return Intl.message('Restore', name: 'recovery', desc: '', args: []);\n  }\n\n  /// `Restore Profiles Only`\n  String get recoveryProfiles {\n    return Intl.message(\n      'Restore Profiles Only',\n      name: 'recoveryProfiles',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Restore All Data`\n  String get recoveryAll {\n    return Intl.message(\n      'Restore All Data',\n      name: 'recoveryAll',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Restore Successful`\n  String get recoverySuccess {\n    return Intl.message(\n      'Restore Successful',\n      name: 'recoverySuccess',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Backup Successful`\n  String get backupSuccess {\n    return Intl.message(\n      'Backup Successful',\n      name: 'backupSuccess',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `No Info`\n  String get noInfo {\n    return Intl.message('No Info', name: 'noInfo', desc: '', args: []);\n  }\n\n  /// `Please bind WebDAV`\n  String get pleaseBindWebDAV {\n    return Intl.message(\n      'Please bind WebDAV',\n      name: 'pleaseBindWebDAV',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Bind`\n  String get bind {\n    return Intl.message('Bind', name: 'bind', desc: '', args: []);\n  }\n\n  /// `Connectivity:`\n  String get connectivity {\n    return Intl.message(\n      'Connectivity:',\n      name: 'connectivity',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `WebDAV Configuration`\n  String get webDAVConfiguration {\n    return Intl.message(\n      'WebDAV Configuration',\n      name: 'webDAVConfiguration',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Address`\n  String get address {\n    return Intl.message('Address', name: 'address', desc: '', args: []);\n  }\n\n  /// `WebDAV server address`\n  String get addressHelp {\n    return Intl.message(\n      'WebDAV server address',\n      name: 'addressHelp',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Please enter a valid WebDAV address`\n  String get addressTip {\n    return Intl.message(\n      'Please enter a valid WebDAV address',\n      name: 'addressTip',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Password`\n  String get password {\n    return Intl.message('Password', name: 'password', desc: '', args: []);\n  }\n\n  /// `Check for Updates`\n  String get checkUpdate {\n    return Intl.message(\n      'Check for Updates',\n      name: 'checkUpdate',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `New Version Available`\n  String get discoverNewVersion {\n    return Intl.message(\n      'New Version Available',\n      name: 'discoverNewVersion',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Already on the latest version`\n  String get checkUpdateError {\n    return Intl.message(\n      'Already on the latest version',\n      name: 'checkUpdateError',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Download Now`\n  String get goDownload {\n    return Intl.message('Download Now', name: 'goDownload', desc: '', args: []);\n  }\n\n  /// `Unknown`\n  String get unknown {\n    return Intl.message('Unknown', name: 'unknown', desc: '', args: []);\n  }\n\n  /// `GeoData`\n  String get geoData {\n    return Intl.message('GeoData', name: 'geoData', desc: '', args: []);\n  }\n\n  /// `External Resources`\n  String get externalResources {\n    return Intl.message(\n      'External Resources',\n      name: 'externalResources',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Checking...`\n  String get checking {\n    return Intl.message('Checking...', name: 'checking', desc: '', args: []);\n  }\n\n  /// `Country`\n  String get country {\n    return Intl.message('Country', name: 'country', desc: '', args: []);\n  }\n\n  /// `Check Failed`\n  String get checkError {\n    return Intl.message('Check Failed', name: 'checkError', desc: '', args: []);\n  }\n\n  /// `Search`\n  String get search {\n    return Intl.message('Search', name: 'search', desc: '', args: []);\n  }\n\n  /// `Allow Bypassing VPN`\n  String get allowBypass {\n    return Intl.message(\n      'Allow Bypassing VPN',\n      name: 'allowBypass',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Allow specific apps to bypass VPN`\n  String get allowBypassDesc {\n    return Intl.message(\n      'Allow specific apps to bypass VPN',\n      name: 'allowBypassDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `External Controller`\n  String get externalController {\n    return Intl.message(\n      'External Controller',\n      name: 'externalController',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Control core via online port`\n  String get externalControllerDesc {\n    return Intl.message(\n      'Control core via online port',\n      name: 'externalControllerDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Control Secret`\n  String get controlSecret {\n    return Intl.message(\n      'Control Secret',\n      name: 'controlSecret',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `RESTful API access password`\n  String get controlSecretDesc {\n    return Intl.message(\n      'RESTful API access password',\n      name: 'controlSecretDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Generate`\n  String get generateSecret {\n    return Intl.message('Generate', name: 'generateSecret', desc: '', args: []);\n  }\n\n  /// `Secret copied to clipboard`\n  String get secretCopied {\n    return Intl.message(\n      'Secret copied to clipboard',\n      name: 'secretCopied',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Enable IPv6 traffic routing`\n  String get ipv6Desc {\n    return Intl.message(\n      'Enable IPv6 traffic routing',\n      name: 'ipv6Desc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `App`\n  String get app {\n    return Intl.message('App', name: 'app', desc: '', args: []);\n  }\n\n  /// `General`\n  String get general {\n    return Intl.message('General', name: 'general', desc: '', args: []);\n  }\n\n  /// `Attach HTTP proxy to VpnService`\n  String get vpnSystemProxyDesc {\n    return Intl.message(\n      'Attach HTTP proxy to VpnService',\n      name: 'vpnSystemProxyDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Set system proxy`\n  String get systemProxyDesc {\n    return Intl.message(\n      'Set system proxy',\n      name: 'systemProxyDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Unified Delay`\n  String get unifiedDelay {\n    return Intl.message(\n      'Unified Delay',\n      name: 'unifiedDelay',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Exclude handshake delays from testing`\n  String get unifiedDelayDesc {\n    return Intl.message(\n      'Exclude handshake delays from testing',\n      name: 'unifiedDelayDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `TCP Concurrent`\n  String get tcpConcurrent {\n    return Intl.message(\n      'TCP Concurrent',\n      name: 'tcpConcurrent',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Allow concurrent TCP connections`\n  String get tcpConcurrentDesc {\n    return Intl.message(\n      'Allow concurrent TCP connections',\n      name: 'tcpConcurrentDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `GEO Low Memory`\n  String get geodataLoader {\n    return Intl.message(\n      'GEO Low Memory',\n      name: 'geodataLoader',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Use GEO low memory loader`\n  String get geodataLoaderDesc {\n    return Intl.message(\n      'Use GEO low memory loader',\n      name: 'geodataLoaderDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Requests`\n  String get requests {\n    return Intl.message('Requests', name: 'requests', desc: '', args: []);\n  }\n\n  /// `View recent request logs`\n  String get requestsDesc {\n    return Intl.message(\n      'View recent request logs',\n      name: 'requestsDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Find Process`\n  String get findProcessMode {\n    return Intl.message(\n      'Find Process',\n      name: 'findProcessMode',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Init`\n  String get init {\n    return Intl.message('Init', name: 'init', desc: '', args: []);\n  }\n\n  /// `Never Expires`\n  String get infiniteTime {\n    return Intl.message(\n      'Never Expires',\n      name: 'infiniteTime',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Expiration Time`\n  String get expirationTime {\n    return Intl.message(\n      'Expiration Time',\n      name: 'expirationTime',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Connections`\n  String get connections {\n    return Intl.message('Connections', name: 'connections', desc: '', args: []);\n  }\n\n  /// `View active connections`\n  String get connectionsDesc {\n    return Intl.message(\n      'View active connections',\n      name: 'connectionsDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Intranet IP`\n  String get intranetIP {\n    return Intl.message('Intranet IP', name: 'intranetIP', desc: '', args: []);\n  }\n\n  /// `View`\n  String get view {\n    return Intl.message('View', name: 'view', desc: '', args: []);\n  }\n\n  /// `Cut`\n  String get cut {\n    return Intl.message('Cut', name: 'cut', desc: '', args: []);\n  }\n\n  /// `Copy`\n  String get copy {\n    return Intl.message('Copy', name: 'copy', desc: '', args: []);\n  }\n\n  /// `Paste`\n  String get paste {\n    return Intl.message('Paste', name: 'paste', desc: '', args: []);\n  }\n\n  /// `Test URL`\n  String get testUrl {\n    return Intl.message('Test URL', name: 'testUrl', desc: '', args: []);\n  }\n\n  /// `Start Test`\n  String get startTest {\n    return Intl.message('Start Test', name: 'startTest', desc: '', args: []);\n  }\n\n  /// `Add Profile`\n  String get addProfile {\n    return Intl.message('Add Profile', name: 'addProfile', desc: '', args: []);\n  }\n\n  /// `Custom URL`\n  String get customUrl {\n    return Intl.message('Custom URL', name: 'customUrl', desc: '', args: []);\n  }\n\n  /// `Sync`\n  String get sync {\n    return Intl.message('Sync', name: 'sync', desc: '', args: []);\n  }\n\n  /// `Hide from Recents`\n  String get exclude {\n    return Intl.message(\n      'Hide from Recents',\n      name: 'exclude',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Hide app from recent tasks list`\n  String get excludeDesc {\n    return Intl.message(\n      'Hide app from recent tasks list',\n      name: 'excludeDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `1 Column`\n  String get oneColumn {\n    return Intl.message('1 Column', name: 'oneColumn', desc: '', args: []);\n  }\n\n  /// `2 Columns`\n  String get twoColumns {\n    return Intl.message('2 Columns', name: 'twoColumns', desc: '', args: []);\n  }\n\n  /// `3 Columns`\n  String get threeColumns {\n    return Intl.message('3 Columns', name: 'threeColumns', desc: '', args: []);\n  }\n\n  /// `4 Columns`\n  String get fourColumns {\n    return Intl.message('4 Columns', name: 'fourColumns', desc: '', args: []);\n  }\n\n  /// `Standard`\n  String get expand {\n    return Intl.message('Standard', name: 'expand', desc: '', args: []);\n  }\n\n  /// `Compact`\n  String get shrink {\n    return Intl.message('Compact', name: 'shrink', desc: '', args: []);\n  }\n\n  /// `Min`\n  String get min {\n    return Intl.message('Min', name: 'min', desc: '', args: []);\n  }\n\n  /// `Tab`\n  String get tab {\n    return Intl.message('Tab', name: 'tab', desc: '', args: []);\n  }\n\n  /// `List`\n  String get list {\n    return Intl.message('List', name: 'list', desc: '', args: []);\n  }\n\n  /// `Delay`\n  String get delay {\n    return Intl.message('Delay', name: 'delay', desc: '', args: []);\n  }\n\n  /// `Style`\n  String get style {\n    return Intl.message('Style', name: 'style', desc: '', args: []);\n  }\n\n  /// `Size`\n  String get size {\n    return Intl.message('Size', name: 'size', desc: '', args: []);\n  }\n\n  /// `Delay Animation`\n  String get delayAnimation {\n    return Intl.message(\n      'Delay Animation',\n      name: 'delayAnimation',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Customize animation during delay testing`\n  String get delayAnimationDesc {\n    return Intl.message(\n      'Customize animation during delay testing',\n      name: 'delayAnimationDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Default`\n  String get noAnimation {\n    return Intl.message('Default', name: 'noAnimation', desc: '', args: []);\n  }\n\n  /// `Rotating Circle`\n  String get rotatingCircle {\n    return Intl.message(\n      'Rotating Circle',\n      name: 'rotatingCircle',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Pulse`\n  String get pulse {\n    return Intl.message('Pulse', name: 'pulse', desc: '', args: []);\n  }\n\n  /// `Spinning Lines`\n  String get spinningLines {\n    return Intl.message(\n      'Spinning Lines',\n      name: 'spinningLines',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Three In Out`\n  String get threeInOut {\n    return Intl.message('Three In Out', name: 'threeInOut', desc: '', args: []);\n  }\n\n  /// `Three Bounce`\n  String get threeBounce {\n    return Intl.message(\n      'Three Bounce',\n      name: 'threeBounce',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Circle`\n  String get circle {\n    return Intl.message('Circle', name: 'circle', desc: '', args: []);\n  }\n\n  /// `Fading Circle`\n  String get fadingCircle {\n    return Intl.message(\n      'Fading Circle',\n      name: 'fadingCircle',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Fading Four`\n  String get fadingFour {\n    return Intl.message('Fading Four', name: 'fadingFour', desc: '', args: []);\n  }\n\n  /// `Wave`\n  String get wave {\n    return Intl.message('Wave', name: 'wave', desc: '', args: []);\n  }\n\n  /// `Double Bounce`\n  String get doubleBounce {\n    return Intl.message(\n      'Double Bounce',\n      name: 'doubleBounce',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Sort`\n  String get sort {\n    return Intl.message('Sort', name: 'sort', desc: '', args: []);\n  }\n\n  /// `Columns`\n  String get columns {\n    return Intl.message('Columns', name: 'columns', desc: '', args: []);\n  }\n\n  /// `Proxy Settings`\n  String get proxiesSetting {\n    return Intl.message(\n      'Proxy Settings',\n      name: 'proxiesSetting',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Proxy Group`\n  String get proxyGroup {\n    return Intl.message('Proxy Group', name: 'proxyGroup', desc: '', args: []);\n  }\n\n  /// `Go`\n  String get go {\n    return Intl.message('Go', name: 'go', desc: '', args: []);\n  }\n\n  /// `External Link`\n  String get externalLink {\n    return Intl.message(\n      'External Link',\n      name: 'externalLink',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Other Contributors`\n  String get otherContributors {\n    return Intl.message(\n      'Other Contributors',\n      name: 'otherContributors',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Auto-Close Connections`\n  String get autoCloseConnections {\n    return Intl.message(\n      'Auto-Close Connections',\n      name: 'autoCloseConnections',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Close connections when switching nodes`\n  String get autoCloseConnectionsDesc {\n    return Intl.message(\n      'Close connections when switching nodes',\n      name: 'autoCloseConnectionsDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Proxy Traffic Only`\n  String get onlyStatisticsProxy {\n    return Intl.message(\n      'Proxy Traffic Only',\n      name: 'onlyStatisticsProxy',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Only record proxy traffic`\n  String get onlyStatisticsProxyDesc {\n    return Intl.message(\n      'Only record proxy traffic',\n      name: 'onlyStatisticsProxyDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Pure Black Mode`\n  String get pureBlackMode {\n    return Intl.message(\n      'Pure Black Mode',\n      name: 'pureBlackMode',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `TCP keep-alive interval`\n  String get keepAliveIntervalDesc {\n    return Intl.message(\n      'TCP keep-alive interval',\n      name: 'keepAliveIntervalDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// ` entries`\n  String get entries {\n    return Intl.message(' entries', name: 'entries', desc: '', args: []);\n  }\n\n  /// `Local`\n  String get local {\n    return Intl.message('Local', name: 'local', desc: '', args: []);\n  }\n\n  /// `Remote`\n  String get remote {\n    return Intl.message('Remote', name: 'remote', desc: '', args: []);\n  }\n\n  /// `Backup data to WebDAV`\n  String get remoteBackupDesc {\n    return Intl.message(\n      'Backup data to WebDAV',\n      name: 'remoteBackupDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Restore data from WebDAV`\n  String get remoteRecoveryDesc {\n    return Intl.message(\n      'Restore data from WebDAV',\n      name: 'remoteRecoveryDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Backup data locally`\n  String get localBackupDesc {\n    return Intl.message(\n      'Backup data locally',\n      name: 'localBackupDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Restore data from file`\n  String get localRecoveryDesc {\n    return Intl.message(\n      'Restore data from file',\n      name: 'localRecoveryDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Mode`\n  String get mode {\n    return Intl.message('Mode', name: 'mode', desc: '', args: []);\n  }\n\n  /// `Time`\n  String get time {\n    return Intl.message('Time', name: 'time', desc: '', args: []);\n  }\n\n  /// `Source`\n  String get source {\n    return Intl.message('Source', name: 'source', desc: '', args: []);\n  }\n\n  /// `All Apps`\n  String get allApps {\n    return Intl.message('All Apps', name: 'allApps', desc: '', args: []);\n  }\n\n  /// `Third-Party Apps Only`\n  String get onlyOtherApps {\n    return Intl.message(\n      'Third-Party Apps Only',\n      name: 'onlyOtherApps',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Action`\n  String get action {\n    return Intl.message('Action', name: 'action', desc: '', args: []);\n  }\n\n  /// `Smart Select`\n  String get intelligentSelected {\n    return Intl.message(\n      'Smart Select',\n      name: 'intelligentSelected',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Import from Clipboard`\n  String get clipboardImport {\n    return Intl.message(\n      'Import from Clipboard',\n      name: 'clipboardImport',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Export to Clipboard`\n  String get clipboardExport {\n    return Intl.message(\n      'Export to Clipboard',\n      name: 'clipboardExport',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Layout`\n  String get layout {\n    return Intl.message('Layout', name: 'layout', desc: '', args: []);\n  }\n\n  /// `Compact`\n  String get tight {\n    return Intl.message('Compact', name: 'tight', desc: '', args: []);\n  }\n\n  /// `Standard`\n  String get standard {\n    return Intl.message('Standard', name: 'standard', desc: '', args: []);\n  }\n\n  /// `Loose`\n  String get loose {\n    return Intl.message('Loose', name: 'loose', desc: '', args: []);\n  }\n\n  /// `Profile Sorting`\n  String get profilesSort {\n    return Intl.message(\n      'Profile Sorting',\n      name: 'profilesSort',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Start`\n  String get start {\n    return Intl.message('Start', name: 'start', desc: '', args: []);\n  }\n\n  /// `Stop`\n  String get stop {\n    return Intl.message('Stop', name: 'stop', desc: '', args: []);\n  }\n\n  /// `Power Switch`\n  String get powerSwitch {\n    return Intl.message(\n      'Power Switch',\n      name: 'powerSwitch',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Uptime`\n  String get runTime {\n    return Intl.message('Uptime', name: 'runTime', desc: '', args: []);\n  }\n\n  /// `Please add a profile first`\n  String get checkOrAddProfile {\n    return Intl.message(\n      'Please add a profile first',\n      name: 'checkOrAddProfile',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Service Ready`\n  String get serviceReady {\n    return Intl.message(\n      'Service Ready',\n      name: 'serviceReady',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `App-related settings`\n  String get appDesc {\n    return Intl.message(\n      'App-related settings',\n      name: 'appDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `VPN-related settings`\n  String get vpnDesc {\n    return Intl.message(\n      'VPN-related settings',\n      name: 'vpnDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `DNS-related settings`\n  String get dnsDesc {\n    return Intl.message(\n      'DNS-related settings',\n      name: 'dnsDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Key`\n  String get key {\n    return Intl.message('Key', name: 'key', desc: '', args: []);\n  }\n\n  /// `Value`\n  String get value {\n    return Intl.message('Value', name: 'value', desc: '', args: []);\n  }\n\n  /// `Append hosts to current config`\n  String get hostsDesc {\n    return Intl.message(\n      'Append hosts to current config',\n      name: 'hostsDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Restart VPN to apply changes`\n  String get vpnTip {\n    return Intl.message(\n      'Restart VPN to apply changes',\n      name: 'vpnTip',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Route all system traffic via VpnService`\n  String get vpnEnableDesc {\n    return Intl.message(\n      'Route all system traffic via VpnService',\n      name: 'vpnEnableDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Options`\n  String get options {\n    return Intl.message('Options', name: 'options', desc: '', args: []);\n  }\n\n  /// `Loopback Unlock`\n  String get loopback {\n    return Intl.message(\n      'Loopback Unlock',\n      name: 'loopback',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `UWP loopback unlocking tool`\n  String get loopbackDesc {\n    return Intl.message(\n      'UWP loopback unlocking tool',\n      name: 'loopbackDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Providers`\n  String get providers {\n    return Intl.message('Providers', name: 'providers', desc: '', args: []);\n  }\n\n  /// `Proxy Providers`\n  String get proxyProviders {\n    return Intl.message(\n      'Proxy Providers',\n      name: 'proxyProviders',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Rule Providers`\n  String get ruleProviders {\n    return Intl.message(\n      'Rule Providers',\n      name: 'ruleProviders',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Advanced Settings`\n  String get advancedSettings {\n    return Intl.message(\n      'Advanced Settings',\n      name: 'advancedSettings',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Node Exclusion`\n  String get nodeExclusion {\n    return Intl.message(\n      'Node Exclusion',\n      name: 'nodeExclusion',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Exclude all matched nodes`\n  String get nodeExclusionDesc {\n    return Intl.message(\n      'Exclude all matched nodes',\n      name: 'nodeExclusionDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `HK|Hong Kong|🇭🇰`\n  String get nodeExclusionPlaceholder {\n    return Intl.message(\n      'HK|Hong Kong|🇭🇰',\n      name: 'nodeExclusionPlaceholder',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Please check the format`\n  String get formatError {\n    return Intl.message(\n      'Please check the format',\n      name: 'formatError',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Timeout`\n  String get healthCheckTimeout {\n    return Intl.message(\n      'Timeout',\n      name: 'healthCheckTimeout',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Node health check timeout`\n  String get healthCheckTimeoutDesc {\n    return Intl.message(\n      'Node health check timeout',\n      name: 'healthCheckTimeoutDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Concurrency Limit`\n  String get concurrencyLimit {\n    return Intl.message(\n      'Concurrency Limit',\n      name: 'concurrencyLimit',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Maximum concurrent delay tests`\n  String get concurrencyLimitDesc {\n    return Intl.message(\n      'Maximum concurrent delay tests',\n      name: 'concurrencyLimitDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Not Recommended`\n  String get notRecommended {\n    return Intl.message(\n      'Not Recommended',\n      name: 'notRecommended',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Override DNS`\n  String get overrideDns {\n    return Intl.message(\n      'Override DNS',\n      name: 'overrideDns',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Override profile's DNS settings`\n  String get overrideDnsDesc {\n    return Intl.message(\n      'Override profile\\'s DNS settings',\n      name: 'overrideDnsDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Override Config`\n  String get overrideTestUrl {\n    return Intl.message(\n      'Override Config',\n      name: 'overrideTestUrl',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `NTP`\n  String get ntp {\n    return Intl.message('NTP', name: 'ntp', desc: '', args: []);\n  }\n\n  /// `Use NTP time service`\n  String get ntpDesc {\n    return Intl.message(\n      'Use NTP time service',\n      name: 'ntpDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Override NTP`\n  String get overrideNtp {\n    return Intl.message(\n      'Override NTP',\n      name: 'overrideNtp',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Override profile's NTP settings`\n  String get overrideNtpDesc {\n    return Intl.message(\n      'Override profile\\'s NTP settings',\n      name: 'overrideNtpDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Status`\n  String get ntpStatus {\n    return Intl.message('Status', name: 'ntpStatus', desc: '', args: []);\n  }\n\n  /// `Enable NTP time service`\n  String get ntpStatusDesc {\n    return Intl.message(\n      'Enable NTP time service',\n      name: 'ntpStatusDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Write to System`\n  String get writeToSystem {\n    return Intl.message(\n      'Write to System',\n      name: 'writeToSystem',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Requires administrator privileges`\n  String get writeToSystemDesc {\n    return Intl.message(\n      'Requires administrator privileges',\n      name: 'writeToSystemDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Server`\n  String get ntpServer {\n    return Intl.message('Server', name: 'ntpServer', desc: '', args: []);\n  }\n\n  /// `Port`\n  String get ntpPort {\n    return Intl.message('Port', name: 'ntpPort', desc: '', args: []);\n  }\n\n  /// `Update Interval`\n  String get ntpInterval {\n    return Intl.message(\n      'Update Interval',\n      name: 'ntpInterval',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Sniffer`\n  String get sniffer {\n    return Intl.message('Sniffer', name: 'sniffer', desc: '', args: []);\n  }\n\n  /// `Modify domain sniffer config`\n  String get snifferDesc {\n    return Intl.message(\n      'Modify domain sniffer config',\n      name: 'snifferDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Override Sniffer`\n  String get overrideSniffer {\n    return Intl.message(\n      'Override Sniffer',\n      name: 'overrideSniffer',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Override profile's Sniffer settings`\n  String get overrideSnifferDesc {\n    return Intl.message(\n      'Override profile\\'s Sniffer settings',\n      name: 'overrideSnifferDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Status`\n  String get snifferStatus {\n    return Intl.message('Status', name: 'snifferStatus', desc: '', args: []);\n  }\n\n  /// `Enable Sniffer service`\n  String get snifferStatusDesc {\n    return Intl.message(\n      'Enable Sniffer service',\n      name: 'snifferStatusDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Force DNS Mapping`\n  String get forceDnsMapping {\n    return Intl.message(\n      'Force DNS Mapping',\n      name: 'forceDnsMapping',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Force mapping DNS results to connections`\n  String get forceDnsMappingDesc {\n    return Intl.message(\n      'Force mapping DNS results to connections',\n      name: 'forceDnsMappingDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Parse Pure IP`\n  String get parsePureIp {\n    return Intl.message(\n      'Parse Pure IP',\n      name: 'parsePureIp',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Parse pure IP connections`\n  String get parsePureIpDesc {\n    return Intl.message(\n      'Parse pure IP connections',\n      name: 'parsePureIpDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Override Destination`\n  String get overrideDestination {\n    return Intl.message(\n      'Override Destination',\n      name: 'overrideDestination',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Override destination with sniffed result`\n  String get overrideDestinationDesc {\n    return Intl.message(\n      'Override destination with sniffed result',\n      name: 'overrideDestinationDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `HTTP Port Sniffing`\n  String get httpPortSniffer {\n    return Intl.message(\n      'HTTP Port Sniffing',\n      name: 'httpPortSniffer',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `TLS Port Sniffing`\n  String get tlsPortSniffer {\n    return Intl.message(\n      'TLS Port Sniffing',\n      name: 'tlsPortSniffer',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `QUIC Port Sniffing`\n  String get quicPortSniffer {\n    return Intl.message(\n      'QUIC Port Sniffing',\n      name: 'quicPortSniffer',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Force Sniff Domain`\n  String get forceDomain {\n    return Intl.message(\n      'Force Sniff Domain',\n      name: 'forceDomain',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Skip Domain`\n  String get skipDomain {\n    return Intl.message('Skip Domain', name: 'skipDomain', desc: '', args: []);\n  }\n\n  /// `Skip Source IP`\n  String get skipSrcAddress {\n    return Intl.message(\n      'Skip Source IP',\n      name: 'skipSrcAddress',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Skip Destination IP`\n  String get skipDstAddress {\n    return Intl.message(\n      'Skip Destination IP',\n      name: 'skipDstAddress',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Ports`\n  String get snifferPorts {\n    return Intl.message('Ports', name: 'snifferPorts', desc: '', args: []);\n  }\n\n  /// `e.g.: 80, 8080-8880`\n  String get snifferPortsHint {\n    return Intl.message(\n      'e.g.: 80, 8080-8880',\n      name: 'snifferPortsHint',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `One domain per line`\n  String get snifferDomainHint {\n    return Intl.message(\n      'One domain per line',\n      name: 'snifferDomainHint',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `One address per line`\n  String get snifferAddressHint {\n    return Intl.message(\n      'One address per line',\n      name: 'snifferAddressHint',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Tunnel`\n  String get tunnel {\n    return Intl.message('Tunnel', name: 'tunnel', desc: '', args: []);\n  }\n\n  /// `Use traffic forwarding tunnel`\n  String get tunnelDesc {\n    return Intl.message(\n      'Use traffic forwarding tunnel',\n      name: 'tunnelDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Override Tunnel`\n  String get overrideTunnel {\n    return Intl.message(\n      'Override Tunnel',\n      name: 'overrideTunnel',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Override profile's Tunnel settings`\n  String get overrideTunnelDesc {\n    return Intl.message(\n      'Override profile\\'s Tunnel settings',\n      name: 'overrideTunnelDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Forwarding List`\n  String get tunnelList {\n    return Intl.message(\n      'Forwarding List',\n      name: 'tunnelList',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Add Forwarding`\n  String get addTunnel {\n    return Intl.message(\n      'Add Forwarding',\n      name: 'addTunnel',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Edit Forwarding`\n  String get editTunnel {\n    return Intl.message(\n      'Edit Forwarding',\n      name: 'editTunnel',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Delete Forwarding`\n  String get deleteTunnel {\n    return Intl.message(\n      'Delete Forwarding',\n      name: 'deleteTunnel',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Network Protocol`\n  String get tunnelNetwork {\n    return Intl.message(\n      'Network Protocol',\n      name: 'tunnelNetwork',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `e.g.: tcp, udp`\n  String get tunnelNetworkHint {\n    return Intl.message(\n      'e.g.: tcp, udp',\n      name: 'tunnelNetworkHint',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Listen Address`\n  String get tunnelAddress {\n    return Intl.message(\n      'Listen Address',\n      name: 'tunnelAddress',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `e.g.: 127.0.0.1:6553`\n  String get tunnelAddressHint {\n    return Intl.message(\n      'e.g.: 127.0.0.1:6553',\n      name: 'tunnelAddressHint',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Target Address`\n  String get tunnelTarget {\n    return Intl.message(\n      'Target Address',\n      name: 'tunnelTarget',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `e.g.: 114.114.114.114:53`\n  String get tunnelTargetHint {\n    return Intl.message(\n      'e.g.: 114.114.114.114:53',\n      name: 'tunnelTargetHint',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Proxy Name`\n  String get tunnelProxy {\n    return Intl.message('Proxy Name', name: 'tunnelProxy', desc: '', args: []);\n  }\n\n  /// `e.g.: proxy (optional)`\n  String get tunnelProxyHint {\n    return Intl.message(\n      'e.g.: proxy (optional)',\n      name: 'tunnelProxyHint',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Experimental`\n  String get experimental {\n    return Intl.message(\n      'Experimental',\n      name: 'experimental',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Use with caution`\n  String get experimentalDesc {\n    return Intl.message(\n      'Use with caution',\n      name: 'experimentalDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Override Experimental`\n  String get overrideExperimental {\n    return Intl.message(\n      'Override Experimental',\n      name: 'overrideExperimental',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Override profile's Experimental settings`\n  String get overrideExperimentalDesc {\n    return Intl.message(\n      'Override profile\\'s Experimental settings',\n      name: 'overrideExperimentalDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Disable QUIC GSO`\n  String get quicGoDisableGso {\n    return Intl.message(\n      'Disable QUIC GSO',\n      name: 'quicGoDisableGso',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Disable QUIC Generic Segmentation Offload`\n  String get quicGoDisableGsoDesc {\n    return Intl.message(\n      'Disable QUIC Generic Segmentation Offload',\n      name: 'quicGoDisableGsoDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Disable QUIC ECN`\n  String get quicGoDisableEcn {\n    return Intl.message(\n      'Disable QUIC ECN',\n      name: 'quicGoDisableEcn',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Disable QUIC Explicit Congestion Notification`\n  String get quicGoDisableEcnDesc {\n    return Intl.message(\n      'Disable QUIC Explicit Congestion Notification',\n      name: 'quicGoDisableEcnDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Enable Dialer IP4P Conversion`\n  String get dialerIp4pConvert {\n    return Intl.message(\n      'Enable Dialer IP4P Conversion',\n      name: 'dialerIp4pConvert',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Enable dialer IP4P address conversion feature`\n  String get dialerIp4pConvertDesc {\n    return Intl.message(\n      'Enable dialer IP4P address conversion feature',\n      name: 'dialerIp4pConvertDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Status`\n  String get status {\n    return Intl.message('Status', name: 'status', desc: '', args: []);\n  }\n\n  /// `Uses system DNS when disabled`\n  String get statusDesc {\n    return Intl.message(\n      'Uses system DNS when disabled',\n      name: 'statusDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Prioritize DoH HTTP/3`\n  String get preferH3Desc {\n    return Intl.message(\n      'Prioritize DoH HTTP/3',\n      name: 'preferH3Desc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Cache Algorithm`\n  String get cacheAlgorithm {\n    return Intl.message(\n      'Cache Algorithm',\n      name: 'cacheAlgorithm',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Respect Rules`\n  String get respectRules {\n    return Intl.message(\n      'Respect Rules',\n      name: 'respectRules',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `DNS connections follow Rules`\n  String get respectRulesDesc {\n    return Intl.message(\n      'DNS connections follow Rules',\n      name: 'respectRulesDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `DNS Mode`\n  String get dnsMode {\n    return Intl.message('DNS Mode', name: 'dnsMode', desc: '', args: []);\n  }\n\n  /// `FakeIP Range`\n  String get fakeipRange {\n    return Intl.message(\n      'FakeIP Range',\n      name: 'fakeipRange',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `FakeIPv6 Range`\n  String get fakeipRangeV6 {\n    return Intl.message(\n      'FakeIPv6 Range',\n      name: 'fakeipRangeV6',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `FakeIP Filter Mode`\n  String get fakeIpFilterMode {\n    return Intl.message(\n      'FakeIP Filter Mode',\n      name: 'fakeIpFilterMode',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Specify FakeIP filter mode`\n  String get fakeIpFilterModeDesc {\n    return Intl.message(\n      'Specify FakeIP filter mode',\n      name: 'fakeIpFilterModeDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Blacklist`\n  String get blacklist {\n    return Intl.message('Blacklist', name: 'blacklist', desc: '', args: []);\n  }\n\n  /// `Whitelist`\n  String get whitelist {\n    return Intl.message('Whitelist', name: 'whitelist', desc: '', args: []);\n  }\n\n  /// `FakeIP Filter`\n  String get fakeipFilter {\n    return Intl.message(\n      'FakeIP Filter',\n      name: 'fakeipFilter',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `FakeIP TTL`\n  String get fakeipTtl {\n    return Intl.message('FakeIP TTL', name: 'fakeipTtl', desc: '', args: []);\n  }\n\n  /// `Default Nameserver`\n  String get defaultNameserver {\n    return Intl.message(\n      'Default Nameserver',\n      name: 'defaultNameserver',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Used to resolve DNS servers`\n  String get defaultNameserverDesc {\n    return Intl.message(\n      'Used to resolve DNS servers',\n      name: 'defaultNameserverDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Nameserver`\n  String get nameserver {\n    return Intl.message('Nameserver', name: 'nameserver', desc: '', args: []);\n  }\n\n  /// `Used to resolve domains`\n  String get nameserverDesc {\n    return Intl.message(\n      'Used to resolve domains',\n      name: 'nameserverDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Use Hosts`\n  String get useHosts {\n    return Intl.message('Use Hosts', name: 'useHosts', desc: '', args: []);\n  }\n\n  /// `Use System Hosts`\n  String get useSystemHosts {\n    return Intl.message(\n      'Use System Hosts',\n      name: 'useSystemHosts',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Nameserver Policy`\n  String get nameserverPolicy {\n    return Intl.message(\n      'Nameserver Policy',\n      name: 'nameserverPolicy',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Specify domain-specific nameservers`\n  String get nameserverPolicyDesc {\n    return Intl.message(\n      'Specify domain-specific nameservers',\n      name: 'nameserverPolicyDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Proxy Nameserver`\n  String get proxyNameserver {\n    return Intl.message(\n      'Proxy Nameserver',\n      name: 'proxyNameserver',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Used to resolve proxy nodes`\n  String get proxyNameserverDesc {\n    return Intl.message(\n      'Used to resolve proxy nodes',\n      name: 'proxyNameserverDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Direct Nameserver`\n  String get directNameserver {\n    return Intl.message(\n      'Direct Nameserver',\n      name: 'directNameserver',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Used to resolve direct domains`\n  String get directNameserverDesc {\n    return Intl.message(\n      'Used to resolve direct domains',\n      name: 'directNameserverDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Direct DNS Follows Policy`\n  String get directNameserverFollowPolicy {\n    return Intl.message(\n      'Direct DNS Follows Policy',\n      name: 'directNameserverFollowPolicy',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Fallback`\n  String get fallback {\n    return Intl.message('Fallback', name: 'fallback', desc: '', args: []);\n  }\n\n  /// `Usually offshore DNS`\n  String get fallbackDesc {\n    return Intl.message(\n      'Usually offshore DNS',\n      name: 'fallbackDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Fallback Filter`\n  String get fallbackFilter {\n    return Intl.message(\n      'Fallback Filter',\n      name: 'fallbackFilter',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `GeoIP Code`\n  String get geoipCode {\n    return Intl.message('GeoIP Code', name: 'geoipCode', desc: '', args: []);\n  }\n\n  /// `IP/CIDR`\n  String get ipcidr {\n    return Intl.message('IP/CIDR', name: 'ipcidr', desc: '', args: []);\n  }\n\n  /// `Domain`\n  String get domain {\n    return Intl.message('Domain', name: 'domain', desc: '', args: []);\n  }\n\n  /// `Reset`\n  String get reset {\n    return Intl.message('Reset', name: 'reset', desc: '', args: []);\n  }\n\n  /// `Show/Hide`\n  String get action_view {\n    return Intl.message('Show/Hide', name: 'action_view', desc: '', args: []);\n  }\n\n  /// `Start/Stop`\n  String get action_start {\n    return Intl.message('Start/Stop', name: 'action_start', desc: '', args: []);\n  }\n\n  /// `Switch Mode`\n  String get action_mode {\n    return Intl.message('Switch Mode', name: 'action_mode', desc: '', args: []);\n  }\n\n  /// `System Proxy`\n  String get action_proxy {\n    return Intl.message(\n      'System Proxy',\n      name: 'action_proxy',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `TUN`\n  String get action_tun {\n    return Intl.message('TUN', name: 'action_tun', desc: '', args: []);\n  }\n\n  /// `Disclaimer`\n  String get disclaimer {\n    return Intl.message('Disclaimer', name: 'disclaimer', desc: '', args: []);\n  }\n\n  /// `This free open-source software is for non-commercial learning and personal use only. Proxy services are independent of this software. By agreeing, you acknowledge this; otherwise, please exit.`\n  String get disclaimerDesc {\n    return Intl.message(\n      'This free open-source software is for non-commercial learning and personal use only. Proxy services are independent of this software. By agreeing, you acknowledge this; otherwise, please exit.',\n      name: 'disclaimerDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Agree`\n  String get agree {\n    return Intl.message('Agree', name: 'agree', desc: '', args: []);\n  }\n\n  /// `Hotkey Management`\n  String get hotkeyManagement {\n    return Intl.message(\n      'Hotkey Management',\n      name: 'hotkeyManagement',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Control app via keyboard`\n  String get hotkeyManagementDesc {\n    return Intl.message(\n      'Control app via keyboard',\n      name: 'hotkeyManagementDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Press a key`\n  String get pressKeyboard {\n    return Intl.message(\n      'Press a key',\n      name: 'pressKeyboard',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Enter a valid hotkey`\n  String get inputCorrectHotkey {\n    return Intl.message(\n      'Enter a valid hotkey',\n      name: 'inputCorrectHotkey',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Hotkey Conflict`\n  String get hotkeyConflict {\n    return Intl.message(\n      'Hotkey Conflict',\n      name: 'hotkeyConflict',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Remove`\n  String get remove {\n    return Intl.message('Remove', name: 'remove', desc: '', args: []);\n  }\n\n  /// `No Hotkeys`\n  String get noHotKey {\n    return Intl.message('No Hotkeys', name: 'noHotKey', desc: '', args: []);\n  }\n\n  /// `No Network`\n  String get noNetwork {\n    return Intl.message('No Network', name: 'noNetwork', desc: '', args: []);\n  }\n\n  /// `Allow IPv6 inbound`\n  String get ipv6InboundDesc {\n    return Intl.message(\n      'Allow IPv6 inbound',\n      name: 'ipv6InboundDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Export Logs`\n  String get exportLogs {\n    return Intl.message('Export Logs', name: 'exportLogs', desc: '', args: []);\n  }\n\n  /// `Export Successful`\n  String get exportSuccess {\n    return Intl.message(\n      'Export Successful',\n      name: 'exportSuccess',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Icon Style`\n  String get iconStyle {\n    return Intl.message('Icon Style', name: 'iconStyle', desc: '', args: []);\n  }\n\n  /// `Icon Only`\n  String get onlyIcon {\n    return Intl.message('Icon Only', name: 'onlyIcon', desc: '', args: []);\n  }\n\n  /// `No Icon`\n  String get noIcon {\n    return Intl.message('No Icon', name: 'noIcon', desc: '', args: []);\n  }\n\n  /// `Stack Mode`\n  String get stackMode {\n    return Intl.message('Stack Mode', name: 'stackMode', desc: '', args: []);\n  }\n\n  /// `Strict Route`\n  String get strictRoute {\n    return Intl.message(\n      'Strict Route',\n      name: 'strictRoute',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Use TUN strict routing mode`\n  String get strictRouteDesc {\n    return Intl.message(\n      'Use TUN strict routing mode',\n      name: 'strictRouteDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `ICMP Forwarding`\n  String get icmpForwarding {\n    return Intl.message(\n      'ICMP Forwarding',\n      name: 'icmpForwarding',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Enable ICMP Ping`\n  String get icmpForwardingDesc {\n    return Intl.message(\n      'Enable ICMP Ping',\n      name: 'icmpForwardingDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `DNS Hijack`\n  String get dnsHijack {\n    return Intl.message('DNS Hijack', name: 'dnsHijack', desc: '', args: []);\n  }\n\n  /// `Redirect DNS queries to internal DNS module`\n  String get dnsHijackDesc {\n    return Intl.message(\n      'Redirect DNS queries to internal DNS module',\n      name: 'dnsHijackDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `NAT Enhancement`\n  String get endpointIndependentNat {\n    return Intl.message(\n      'NAT Enhancement',\n      name: 'endpointIndependentNat',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Enable endpoint-independent NAT`\n  String get endpointIndependentNatDesc {\n    return Intl.message(\n      'Enable endpoint-independent NAT',\n      name: 'endpointIndependentNatDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Network`\n  String get network {\n    return Intl.message('Network', name: 'network', desc: '', args: []);\n  }\n\n  /// `Modify network-related settings`\n  String get networkDesc {\n    return Intl.message(\n      'Modify network-related settings',\n      name: 'networkDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Bypass Domain`\n  String get bypassDomain {\n    return Intl.message(\n      'Bypass Domain',\n      name: 'bypassDomain',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Active only when System Proxy is on`\n  String get bypassDomainDesc {\n    return Intl.message(\n      'Active only when System Proxy is on',\n      name: 'bypassDomainDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Are you sure you want to reset?`\n  String get resetTip {\n    return Intl.message(\n      'Are you sure you want to reset?',\n      name: 'resetTip',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `RegExp`\n  String get regExp {\n    return Intl.message('RegExp', name: 'regExp', desc: '', args: []);\n  }\n\n  /// `Icon`\n  String get icon {\n    return Intl.message('Icon', name: 'icon', desc: '', args: []);\n  }\n\n  /// `Icon Configuration`\n  String get iconConfiguration {\n    return Intl.message(\n      'Icon Configuration',\n      name: 'iconConfiguration',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `No Data`\n  String get noData {\n    return Intl.message('No Data', name: 'noData', desc: '', args: []);\n  }\n\n  /// `Admin Auto-Launch`\n  String get adminAutoLaunch {\n    return Intl.message(\n      'Admin Auto-Launch',\n      name: 'adminAutoLaunch',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Auto-start with admin privileges`\n  String get adminAutoLaunchDesc {\n    return Intl.message(\n      'Auto-start with admin privileges',\n      name: 'adminAutoLaunchDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Font`\n  String get fontFamily {\n    return Intl.message('Font', name: 'fontFamily', desc: '', args: []);\n  }\n\n  /// `System Font`\n  String get systemFont {\n    return Intl.message('System Font', name: 'systemFont', desc: '', args: []);\n  }\n\n  /// `Toggle`\n  String get toggle {\n    return Intl.message('Toggle', name: 'toggle', desc: '', args: []);\n  }\n\n  /// `System`\n  String get system {\n    return Intl.message('System', name: 'system', desc: '', args: []);\n  }\n\n  /// `Bypass Private Network`\n  String get bypassPrivateRoute {\n    return Intl.message(\n      'Bypass Private Network',\n      name: 'bypassPrivateRoute',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Automatically bypass private network IP addresses`\n  String get bypassPrivateRouteDesc {\n    return Intl.message(\n      'Automatically bypass private network IP addresses',\n      name: 'bypassPrivateRouteDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Please enter the admin password`\n  String get pleaseInputAdminPassword {\n    return Intl.message(\n      'Please enter the admin password',\n      name: 'pleaseInputAdminPassword',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Copy Environment Variable`\n  String get copyEnvVar {\n    return Intl.message(\n      'Copy Environment Variable',\n      name: 'copyEnvVar',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Memory Info`\n  String get memoryInfo {\n    return Intl.message('Memory Info', name: 'memoryInfo', desc: '', args: []);\n  }\n\n  /// `Cancel`\n  String get cancel {\n    return Intl.message('Cancel', name: 'cancel', desc: '', args: []);\n  }\n\n  /// `File modified. Save changes?`\n  String get fileIsUpdate {\n    return Intl.message(\n      'File modified. Save changes?',\n      name: 'fileIsUpdate',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Profile modified. Disable auto-update?`\n  String get profileHasUpdate {\n    return Intl.message(\n      'Profile modified. Disable auto-update?',\n      name: 'profileHasUpdate',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Cache modifications?`\n  String get hasCacheChange {\n    return Intl.message(\n      'Cache modifications?',\n      name: 'hasCacheChange',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Copy Successful`\n  String get copySuccess {\n    return Intl.message(\n      'Copy Successful',\n      name: 'copySuccess',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Success`\n  String get success {\n    return Intl.message('Success', name: 'success', desc: '', args: []);\n  }\n\n  /// `Copy Link`\n  String get copyLink {\n    return Intl.message('Copy Link', name: 'copyLink', desc: '', args: []);\n  }\n\n  /// `Export File`\n  String get exportFile {\n    return Intl.message('Export File', name: 'exportFile', desc: '', args: []);\n  }\n\n  /// `Cache corrupted. Clear it?`\n  String get cacheCorrupt {\n    return Intl.message(\n      'Cache corrupted. Clear it?',\n      name: 'cacheCorrupt',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Third-party API result (for reference only)`\n  String get detectionTip {\n    return Intl.message(\n      'Third-party API result (for reference only)',\n      name: 'detectionTip',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Toggle Display`\n  String get ipClickBehavior {\n    return Intl.message(\n      'Toggle Display',\n      name: 'ipClickBehavior',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Hide IP Display`\n  String get ipPrivacyProtection {\n    return Intl.message(\n      'Hide IP Display',\n      name: 'ipPrivacyProtection',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Refresh IP`\n  String get manualRefreshIp {\n    return Intl.message(\n      'Refresh IP',\n      name: 'manualRefreshIp',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Please try manual refresh`\n  String get tryManualRefresh {\n    return Intl.message(\n      'Please try manual refresh',\n      name: 'tryManualRefresh',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Refresh App List`\n  String get refreshAppList {\n    return Intl.message(\n      'Refresh App List',\n      name: 'refreshAppList',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Refresh the app list?`\n  String get refreshAppListConfirm {\n    return Intl.message(\n      'Refresh the app list?',\n      name: 'refreshAppListConfirm',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Get Domestic IP`\n  String get switchToDomesticIp {\n    return Intl.message(\n      'Get Domestic IP',\n      name: 'switchToDomesticIp',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Listen`\n  String get listen {\n    return Intl.message('Listen', name: 'listen', desc: '', args: []);\n  }\n\n  /// `Undo`\n  String get undo {\n    return Intl.message('Undo', name: 'undo', desc: '', args: []);\n  }\n\n  /// `Redo`\n  String get redo {\n    return Intl.message('Redo', name: 'redo', desc: '', args: []);\n  }\n\n  /// `None`\n  String get none {\n    return Intl.message('None', name: 'none', desc: '', args: []);\n  }\n\n  /// `Core Configuration`\n  String get basicConfig {\n    return Intl.message(\n      'Core Configuration',\n      name: 'basicConfig',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Global core settings`\n  String get basicConfigDesc {\n    return Intl.message(\n      'Global core settings',\n      name: 'basicConfigDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `{count} items selected`\n  String selectedCountTitle(Object count) {\n    return Intl.message(\n      '$count items selected',\n      name: 'selectedCountTitle',\n      desc: '',\n      args: [count],\n    );\n  }\n\n  /// `Add Rule`\n  String get addRule {\n    return Intl.message('Add Rule', name: 'addRule', desc: '', args: []);\n  }\n\n  /// `Rule Name`\n  String get ruleName {\n    return Intl.message('Rule Name', name: 'ruleName', desc: '', args: []);\n  }\n\n  /// `Content`\n  String get content {\n    return Intl.message('Content', name: 'content', desc: '', args: []);\n  }\n\n  /// `Sub Rule`\n  String get subRule {\n    return Intl.message('Sub Rule', name: 'subRule', desc: '', args: []);\n  }\n\n  /// `Rule Target`\n  String get ruleTarget {\n    return Intl.message('Rule Target', name: 'ruleTarget', desc: '', args: []);\n  }\n\n  /// `Source IP`\n  String get sourceIp {\n    return Intl.message('Source IP', name: 'sourceIp', desc: '', args: []);\n  }\n\n  /// `No Resolve`\n  String get noResolve {\n    return Intl.message('No Resolve', name: 'noResolve', desc: '', args: []);\n  }\n\n  /// `Original Rules`\n  String get getOriginRules {\n    return Intl.message(\n      'Original Rules',\n      name: 'getOriginRules',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Override Original Rules`\n  String get overrideOriginRules {\n    return Intl.message(\n      'Override Original Rules',\n      name: 'overrideOriginRules',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Append to Original Rules`\n  String get addedOriginRules {\n    return Intl.message(\n      'Append to Original Rules',\n      name: 'addedOriginRules',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Enable Override`\n  String get enableOverride {\n    return Intl.message(\n      'Enable Override',\n      name: 'enableOverride',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Save changes?`\n  String get saveChanges {\n    return Intl.message(\n      'Save changes?',\n      name: 'saveChanges',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Modify general settings`\n  String get generalDesc {\n    return Intl.message(\n      'Modify general settings',\n      name: 'generalDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Enable process matching`\n  String get findProcessModeDesc {\n    return Intl.message(\n      'Enable process matching',\n      name: 'findProcessModeDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Effective only in mobile view`\n  String get tabAnimationDesc {\n    return Intl.message(\n      'Effective only in mobile view',\n      name: 'tabAnimationDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Haptic Feedback`\n  String get navBarHapticFeedback {\n    return Intl.message(\n      'Haptic Feedback',\n      name: 'navBarHapticFeedback',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Vibrate on navigation tab switch`\n  String get navBarHapticFeedbackDesc {\n    return Intl.message(\n      'Vibrate on navigation tab switch',\n      name: 'navBarHapticFeedbackDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Are you sure you want to save?`\n  String get saveTip {\n    return Intl.message(\n      'Are you sure you want to save?',\n      name: 'saveTip',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Color Schemes`\n  String get colorSchemes {\n    return Intl.message(\n      'Color Schemes',\n      name: 'colorSchemes',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Palette`\n  String get palette {\n    return Intl.message('Palette', name: 'palette', desc: '', args: []);\n  }\n\n  /// `Tonal Spot`\n  String get tonalSpotScheme {\n    return Intl.message(\n      'Tonal Spot',\n      name: 'tonalSpotScheme',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Fidelity`\n  String get fidelityScheme {\n    return Intl.message('Fidelity', name: 'fidelityScheme', desc: '', args: []);\n  }\n\n  /// `Monochrome`\n  String get monochromeScheme {\n    return Intl.message(\n      'Monochrome',\n      name: 'monochromeScheme',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Neutral`\n  String get neutralScheme {\n    return Intl.message('Neutral', name: 'neutralScheme', desc: '', args: []);\n  }\n\n  /// `Vibrant`\n  String get vibrantScheme {\n    return Intl.message('Vibrant', name: 'vibrantScheme', desc: '', args: []);\n  }\n\n  /// `Expressive`\n  String get expressiveScheme {\n    return Intl.message(\n      'Expressive',\n      name: 'expressiveScheme',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Content`\n  String get contentScheme {\n    return Intl.message('Content', name: 'contentScheme', desc: '', args: []);\n  }\n\n  /// `Rainbow`\n  String get rainbowScheme {\n    return Intl.message('Rainbow', name: 'rainbowScheme', desc: '', args: []);\n  }\n\n  /// `Fruit Salad`\n  String get fruitSaladScheme {\n    return Intl.message(\n      'Fruit Salad',\n      name: 'fruitSaladScheme',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Developer Mode`\n  String get developerMode {\n    return Intl.message(\n      'Developer Mode',\n      name: 'developerMode',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Developer mode is enabled.`\n  String get developerModeEnableTip {\n    return Intl.message(\n      'Developer mode is enabled.',\n      name: 'developerModeEnableTip',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Message Test`\n  String get messageTest {\n    return Intl.message(\n      'Message Test',\n      name: 'messageTest',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `This is a message.`\n  String get messageTestTip {\n    return Intl.message(\n      'This is a message.',\n      name: 'messageTestTip',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Crash Test`\n  String get crashTest {\n    return Intl.message('Crash Test', name: 'crashTest', desc: '', args: []);\n  }\n\n  /// `Clear Data`\n  String get clearData {\n    return Intl.message('Clear Data', name: 'clearData', desc: '', args: []);\n  }\n\n  /// `Text Scaling`\n  String get textScale {\n    return Intl.message('Text Scaling', name: 'textScale', desc: '', args: []);\n  }\n\n  /// `Light Icon`\n  String get lightIcon {\n    return Intl.message('Light Icon', name: 'lightIcon', desc: '', args: []);\n  }\n\n  /// `Manually switch light desktop app icon`\n  String get lightIconDesc {\n    return Intl.message(\n      'Manually switch light desktop app icon',\n      name: 'lightIconDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `HarmonyOS Font`\n  String get harmonyFont {\n    return Intl.message(\n      'HarmonyOS Font',\n      name: 'harmonyFont',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Use optimized HarmonyOS Sans font`\n  String get harmonyFontDesc {\n    return Intl.message(\n      'Use optimized HarmonyOS Sans font',\n      name: 'harmonyFontDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Internet`\n  String get internet {\n    return Intl.message('Internet', name: 'internet', desc: '', args: []);\n  }\n\n  /// `System App`\n  String get systemApp {\n    return Intl.message('System App', name: 'systemApp', desc: '', args: []);\n  }\n\n  /// `No Network App`\n  String get noNetworkApp {\n    return Intl.message(\n      'No Network App',\n      name: 'noNetworkApp',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Contact Me`\n  String get contactMe {\n    return Intl.message('Contact Me', name: 'contactMe', desc: '', args: []);\n  }\n\n  /// `Recovery Strategy`\n  String get recoveryStrategy {\n    return Intl.message(\n      'Recovery Strategy',\n      name: 'recoveryStrategy',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Override`\n  String get recoveryStrategy_override {\n    return Intl.message(\n      'Override',\n      name: 'recoveryStrategy_override',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Compatible`\n  String get recoveryStrategy_compatible {\n    return Intl.message(\n      'Compatible',\n      name: 'recoveryStrategy_compatible',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Logs Test`\n  String get logsTest {\n    return Intl.message('Logs Test', name: 'logsTest', desc: '', args: []);\n  }\n\n  /// `{label} cannot be empty`\n  String emptyTip(Object label) {\n    return Intl.message(\n      '$label cannot be empty',\n      name: 'emptyTip',\n      desc: '',\n      args: [label],\n    );\n  }\n\n  /// `{label} must be a URL`\n  String urlTip(Object label) {\n    return Intl.message(\n      '$label must be a URL',\n      name: 'urlTip',\n      desc: '',\n      args: [label],\n    );\n  }\n\n  /// `{label} must be a number`\n  String numberTip(Object label) {\n    return Intl.message(\n      '$label must be a number',\n      name: 'numberTip',\n      desc: '',\n      args: [label],\n    );\n  }\n\n  /// `Interval`\n  String get interval {\n    return Intl.message('Interval', name: 'interval', desc: '', args: []);\n  }\n\n  /// `{label} already exists`\n  String existsTip(Object label) {\n    return Intl.message(\n      '$label already exists',\n      name: 'existsTip',\n      desc: '',\n      args: [label],\n    );\n  }\n\n  /// `Delete current {label}?`\n  String deleteTip(Object label) {\n    return Intl.message(\n      'Delete current $label?',\n      name: 'deleteTip',\n      desc: '',\n      args: [label],\n    );\n  }\n\n  /// `Delete selected {label}?`\n  String deleteMultipTip(Object label) {\n    return Intl.message(\n      'Delete selected $label?',\n      name: 'deleteMultipTip',\n      desc: '',\n      args: [label],\n    );\n  }\n\n  /// `No {label}`\n  String nullTip(Object label) {\n    return Intl.message('No $label', name: 'nullTip', desc: '', args: [label]);\n  }\n\n  /// `Script`\n  String get script {\n    return Intl.message('Script', name: 'script', desc: '', args: []);\n  }\n\n  /// `Color`\n  String get color {\n    return Intl.message('Color', name: 'color', desc: '', args: []);\n  }\n\n  /// `Rename`\n  String get rename {\n    return Intl.message('Rename', name: 'rename', desc: '', args: []);\n  }\n\n  /// `Unnamed`\n  String get unnamed {\n    return Intl.message('Unnamed', name: 'unnamed', desc: '', args: []);\n  }\n\n  /// `Please enter a script name`\n  String get pleaseEnterScriptName {\n    return Intl.message(\n      'Please enter a script name',\n      name: 'pleaseEnterScriptName',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Inactive in script mode`\n  String get overrideInvalidTip {\n    return Intl.message(\n      'Inactive in script mode',\n      name: 'overrideInvalidTip',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Mixed Port`\n  String get mixedPort {\n    return Intl.message('Mixed Port', name: 'mixedPort', desc: '', args: []);\n  }\n\n  /// `Socks Port`\n  String get socksPort {\n    return Intl.message('Socks Port', name: 'socksPort', desc: '', args: []);\n  }\n\n  /// `Redir Port`\n  String get redirPort {\n    return Intl.message('Redir Port', name: 'redirPort', desc: '', args: []);\n  }\n\n  /// `Tproxy Port`\n  String get tproxyPort {\n    return Intl.message('Tproxy Port', name: 'tproxyPort', desc: '', args: []);\n  }\n\n  /// `{label} must be between 1024 and 49151`\n  String portTip(Object label) {\n    return Intl.message(\n      '$label must be between 1024 and 49151',\n      name: 'portTip',\n      desc: '',\n      args: [label],\n    );\n  }\n\n  /// `Please enter a different port`\n  String get portConflictTip {\n    return Intl.message(\n      'Please enter a different port',\n      name: 'portConflictTip',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Import`\n  String get import {\n    return Intl.message('Import', name: 'import', desc: '', args: []);\n  }\n\n  /// `Import from Code`\n  String get importFromCode {\n    return Intl.message(\n      'Import from Code',\n      name: 'importFromCode',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Import failed`\n  String get importFailed {\n    return Intl.message(\n      'Import failed',\n      name: 'importFailed',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Import from File`\n  String get importFile {\n    return Intl.message(\n      'Import from File',\n      name: 'importFile',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Import from URL`\n  String get importUrl {\n    return Intl.message(\n      'Import from URL',\n      name: 'importUrl',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Auto Set System DNS`\n  String get autoSetSystemDns {\n    return Intl.message(\n      'Auto Set System DNS',\n      name: 'autoSetSystemDns',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `{label} Details`\n  String details(Object label) {\n    return Intl.message(\n      '$label Details',\n      name: 'details',\n      desc: '',\n      args: [label],\n    );\n  }\n\n  /// `Creation Time`\n  String get creationTime {\n    return Intl.message(\n      'Creation Time',\n      name: 'creationTime',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Progress`\n  String get progress {\n    return Intl.message('Progress', name: 'progress', desc: '', args: []);\n  }\n\n  /// `Host`\n  String get host {\n    return Intl.message('Host', name: 'host', desc: '', args: []);\n  }\n\n  /// `Destination`\n  String get destination {\n    return Intl.message('Destination', name: 'destination', desc: '', args: []);\n  }\n\n  /// `Destination GeoIP`\n  String get destinationGeoIP {\n    return Intl.message(\n      'Destination GeoIP',\n      name: 'destinationGeoIP',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Destination IP ASN`\n  String get destinationIPASN {\n    return Intl.message(\n      'Destination IP ASN',\n      name: 'destinationIPASN',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Special Proxy`\n  String get specialProxy {\n    return Intl.message(\n      'Special Proxy',\n      name: 'specialProxy',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Special Rules`\n  String get specialRules {\n    return Intl.message(\n      'Special Rules',\n      name: 'specialRules',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Remote Destination`\n  String get remoteDestination {\n    return Intl.message(\n      'Remote Destination',\n      name: 'remoteDestination',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Network Type`\n  String get networkType {\n    return Intl.message(\n      'Network Type',\n      name: 'networkType',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Proxy Chains`\n  String get proxyChains {\n    return Intl.message(\n      'Proxy Chains',\n      name: 'proxyChains',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Log`\n  String get log {\n    return Intl.message('Log', name: 'log', desc: '', args: []);\n  }\n\n  /// `Active Connections`\n  String get connection {\n    return Intl.message(\n      'Active Connections',\n      name: 'connection',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Request`\n  String get request {\n    return Intl.message('Request', name: 'request', desc: '', args: []);\n  }\n\n  /// `Switch`\n  String get switchLabel {\n    return Intl.message('Switch', name: 'switchLabel', desc: '', args: []);\n  }\n\n  /// `No Status`\n  String get noStatusAvailable {\n    return Intl.message(\n      'No Status',\n      name: 'noStatusAvailable',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Online Panel`\n  String get onlinePanel {\n    return Intl.message(\n      'Online Panel',\n      name: 'onlinePanel',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Open Zashboard`\n  String get openDashboard {\n    return Intl.message(\n      'Open Zashboard',\n      name: 'openDashboard',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Custom`\n  String get custom {\n    return Intl.message('Custom', name: 'custom', desc: '', args: []);\n  }\n\n  /// `Wakelock`\n  String get wakelock {\n    return Intl.message('Wakelock', name: 'wakelock', desc: '', args: []);\n  }\n\n  /// `Keeps the screen on and app active in the background without requiring special CPU wakelock permissions.`\n  String get wakelockDescription {\n    return Intl.message(\n      'Keeps the screen on and app active in the background without requiring special CPU wakelock permissions.',\n      name: 'wakelockDescription',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `TUN requires admin privileges. Please run as Administrator.`\n  String get tunEnableRequireAdmin {\n    return Intl.message(\n      'TUN requires admin privileges. Please run as Administrator.',\n      name: 'tunEnableRequireAdmin',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Restart TUN for changes to take effect`\n  String get restartTip {\n    return Intl.message(\n      'Restart TUN for changes to take effect',\n      name: 'restartTip',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Restart`\n  String get restart {\n    return Intl.message('Restart', name: 'restart', desc: '', args: []);\n  }\n\n  /// `Restart Core`\n  String get restartCoreTitle {\n    return Intl.message(\n      'Restart Core',\n      name: 'restartCoreTitle',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Manually restart the core?`\n  String get restartCoreDesc {\n    return Intl.message(\n      'Manually restart the core?',\n      name: 'restartCoreDesc',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `High Refresh Rate`\n  String get highRefreshRate {\n    return Intl.message(\n      'High Refresh Rate',\n      name: 'highRefreshRate',\n      desc: '',\n      args: [],\n    );\n  }\n\n  /// `Enable highest refresh rate support`\n  String get highRefreshRateDesc {\n    return Intl.message(\n      'Enable highest refresh rate support',\n      name: 'highRefreshRateDesc',\n      desc: '',\n      args: [],\n    );\n  }\n}\n\nclass AppLocalizationDelegate extends LocalizationsDelegate<AppLocalizations> {\n  const AppLocalizationDelegate();\n\n  List<Locale> get supportedLocales {\n    return const <Locale>[\n      Locale.fromSubtags(languageCode: 'en'),\n      Locale.fromSubtags(languageCode: 'ru'),\n      Locale.fromSubtags(languageCode: 'zh', countryCode: 'CN'),\n      Locale.fromSubtags(languageCode: 'zh', countryCode: 'TC'),\n    ];\n  }\n\n  @override\n  bool isSupported(Locale locale) => _isSupported(locale);\n  @override\n  Future<AppLocalizations> load(Locale locale) => AppLocalizations.load(locale);\n  @override\n  bool shouldReload(AppLocalizationDelegate old) => false;\n\n  bool _isSupported(Locale locale) {\n    for (var supportedLocale in supportedLocales) {\n      if (supportedLocale.languageCode == locale.languageCode) {\n        return true;\n      }\n    }\n    return false;\n  }\n}\n"
  },
  {
    "path": "lib/main.dart",
    "content": "import 'dart:ffi';\nimport 'dart:io';\nimport 'dart:isolate';\nimport 'dart:ui';\n\nimport 'package:bett_box/plugins/app.dart';\nimport 'package:bett_box/plugins/tile.dart';\nimport 'package:bett_box/plugins/vpn.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:flutter_displaymode/flutter_displaymode.dart';\n\nimport 'application.dart';\nimport 'clash/core.dart';\nimport 'clash/lib.dart';\nimport 'common/common.dart';\nimport 'models/models.dart';\n\nReceivePort? _serviceReceiverPort;\nReceivePort? _messageReceiverPort;\n\nFuture<void> main() async {\n  globalState.isService = false;\n  WidgetsFlutterBinding.ensureInitialized();\n\n  PaintingBinding.instance.imageCache.maximumSizeBytes = 50 * 1024 * 1024;\n\n  final version = await system.version;\n  await clashCore.preload();\n  await globalState.initApp(version);\n\n  try {\n    await uiManager.initializeUI();\n  } catch (e) {\n    commonPrint.log('Failed to initialize UI: $e');\n  }\n\n  await _runApp(version);\n}\n\nFuture<void> _runApp(int version) async {\n  if (system.isAndroid && globalState.config.appSetting.enableHighRefreshRate) {\n    try {\n      await FlutterDisplayMode.setHighRefreshRate();\n    } catch (e) {\n      commonPrint.log('Failed to set high refresh rate: $e');\n    }\n  }\n  await android?.init();\n  \n  await window?.init(version);\n  HttpOverrides.global = BettboxHttpOverrides();\n  runApp(ProviderScope(child: const Application()));\n}\n\n@pragma('vm:entry-point')\nFuture<void> _service(List<String> flags) async {\n  globalState.isService = true;\n  WidgetsFlutterBinding.ensureInitialized();\n  await globalState.init();\n\n  {\n    final quickStart = flags.contains('quick');\n    final bootStart = flags.contains('boot');\n    final clashLibHandler = ClashLibHandler();\n\n    tile?.addListener(\n      _TileListenerWithService(\n        onStart: () async {\n          await app.tip(appLocalizations.startVpn);\n          await globalState.handleStart();\n        },\n        onStop: () async {\n          await app.tip(appLocalizations.stopVpn);\n          clashLibHandler.stopListener();\n          await vpn?.stop();\n        },\n        onReconnectIpc: () {\n          commonPrint.log(\n            'Service: reconnectIpc requested, re-establishing IPC',\n          );\n          _handleMainIpc(clashLibHandler);\n        },\n      ),\n    );\n\n    vpn?.addListener(\n      _VpnListenerWithService(\n        onDnsChanged: (String dns) {\n          clashLibHandler.updateDns(dns);\n        },\n      ),\n    );\n\n    if (!quickStart && !bootStart) {\n      _handleMainIpc(clashLibHandler);\n      return;\n    }\n\n    if (bootStart && !globalState.config.appSetting.autoRun) {\n      commonPrint.log('Silent boot detected, but autoRun is disabled. Staying idle.');\n      _handleMainIpc(clashLibHandler);\n      return;\n    }\n\n    commonPrint.log('Executing ${bootStart ? \"boot\" : \"quick\"} start sequence');\n    await ClashCore.initGeo();\n    app.tip(appLocalizations.startVpn);\n    final homeDirPath = await appPath.homeDirPath;\n    final version = await system.version;\n    final clashConfig = globalState.config.patchClashConfig.copyWith.tun(\n      enable: false,\n    );\n\n    final params = await globalState.getSetupParams(pathConfig: clashConfig);\n    Future(() async {\n      try {\n        final profileId = globalState.config.currentProfileId;\n        if (profileId == null) {\n          return;\n        }\n        final res = await clashLibHandler.quickStart(\n          InitParams(homeDir: homeDirPath, version: version),\n          params,\n          globalState.getCoreState(),\n        );\n        debugPrint(res);\n        if (res.isNotEmpty) {\n          commonPrint.log('QuickStart failed with error: $res');\n          await vpn?.stop();\n          return;\n        }\n        await vpn?.start(clashLibHandler.getAndroidVpnOptions());\n\n        if (globalState.config.appSetting.openLogs) {\n          await clashLibHandler.invokeAction('{\"id\": \"quickStartLog\", \"method\": \"startLog\"}');\n        } else {\n          await clashLibHandler.invokeAction('{\"id\": \"quickStopLog\", \"method\": \"stopLog\"}');\n        }\n\n        clashLibHandler.startListener();\n      } catch (e) {\n        commonPrint.log('Fatal error during service background start: $e');\n        await vpn?.stop();\n      }\n    });\n  }\n}\n\nvoid _handleMainIpc(ClashLibHandler clashLibHandler) {\n  final sendPort = IsolateNameServer.lookupPortByName(mainIsolate);\n  if (sendPort == null) {\n    commonPrint.log('Service: mainIsolate sendPort not found, IPC unavailable');\n    return;\n  }\n\n  _serviceReceiverPort?.close();\n  _messageReceiverPort?.close();\n\n  _serviceReceiverPort = ReceivePort();\n  _serviceReceiverPort!.listen((message) async {\n    final res = await clashLibHandler.invokeAction(message);\n    _safeSend(sendPort, res);\n  });\n  _safeSend(sendPort, _serviceReceiverPort!.sendPort);\n\n  _messageReceiverPort = ReceivePort();\n  clashLibHandler.attachMessagePort(_messageReceiverPort!.sendPort.nativePort);\n  _messageReceiverPort!.listen((message) {\n    _safeSend(sendPort, message);\n  });\n\n  clashLibHandler.startListener();\n}\n\nvoid _safeSend(SendPort sendPort, dynamic message) {\n  try {\n    sendPort.send(message);\n  } catch (e) {\n    commonPrint.log('Service: IPC send failed: $e');\n    final retryPort = IsolateNameServer.lookupPortByName(mainIsolate);\n    if (retryPort != null) {\n      try {\n        retryPort.send(message);\n      } catch (_) {}\n    }\n  }\n}\n\n@immutable\nclass _TileListenerWithService with TileListener {\n  final Function() _onStart;\n  final Function() _onStop;\n  final Function() _onReconnectIpc;\n\n  const _TileListenerWithService({\n    required Function() onStart,\n    required Function() onStop,\n    required Function() onReconnectIpc,\n  }) : _onStart = onStart,\n       _onStop = onStop,\n       _onReconnectIpc = onReconnectIpc;\n\n  @override\n  void onStart() => _onStart();\n\n  @override\n  void onStop() => _onStop();\n\n  @override\n  void onReconnectIpc() => _onReconnectIpc();\n}\n\n@immutable\nclass _VpnListenerWithService with VpnListener {\n  final Function(String dns) _onDnsChanged;\n\n  const _VpnListenerWithService({required Function(String dns) onDnsChanged})\n    : _onDnsChanged = onDnsChanged;\n\n  @override\n  void onDnsChanged(String dns) {\n    super.onDnsChanged(dns);\n    _onDnsChanged(dns);\n  }\n}\n"
  },
  {
    "path": "lib/manager/android_manager.dart",
    "content": "import 'package:bett_box/plugins/app.dart';\nimport 'package:bett_box/providers/providers.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass AndroidManager extends ConsumerStatefulWidget {\n  final Widget child;\n\n  const AndroidManager({super.key, required this.child});\n\n  @override\n  ConsumerState<AndroidManager> createState() => _AndroidContainerState();\n}\n\nclass _AndroidContainerState extends ConsumerState<AndroidManager> {\n  @override\n  void initState() {\n    super.initState();\n    ref.listenManual(appSettingProvider.select((state) => state.hidden), (\n      prev,\n      next,\n    ) {\n      app.updateExcludeFromRecents(next);\n    }, fireImmediately: true);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return widget.child;\n  }\n}"
  },
  {
    "path": "lib/manager/app_manager.dart",
    "content": "import 'dart:async';\n\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/manager/window_manager.dart';\nimport 'package:bett_box/plugins/app.dart';\nimport 'package:bett_box/providers/providers.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_acrylic/widgets/transparent_macos_sidebar.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:intl/intl.dart';\nimport 'package:window_manager/window_manager.dart';\n\nclass AppStateManager extends ConsumerStatefulWidget {\n  final Widget child;\n\n  const AppStateManager({super.key, required this.child});\n\n  @override\n  ConsumerState<AppStateManager> createState() => _AppStateManagerState();\n}\n\nclass _AppStateManagerState extends ConsumerState<AppStateManager>\n    with WidgetsBindingObserver {\n  bool _isRefreshActive = false;\n  Timer? _dashboardRefreshDebounceTimer;\n  Timer? _missedUpdateCheckTimer;\n  DateTime? _lastMissedUpdateCheck;\n  late final VoidCallback _dashboardTickListener;\n\n  static const _missedUpdateCheckDelay = Duration(seconds: 5);\n  static const _missedUpdateCheckThrottle = Duration(seconds: 60);\n\n  @override\n  void initState() {\n    super.initState();\n    WidgetsBinding.instance.addObserver(this);\n    _dashboardTickListener = () {\n      if (!globalState.isStart) {\n        return;\n      }\n      unawaited(globalState.appController.updateRunTime());\n    };\n    dashboardRefreshManager.tick1s.addListener(_dashboardTickListener);\n    ref.listenManual(layoutChangeProvider, (prev, next) {\n      WidgetsBinding.instance.addPostFrameCallback((_) {\n        if (prev != next) {\n          globalState.computeHeightMapCache = {};\n        }\n      });\n    });\n    ref.listenManual(checkIpProvider, (prev, next) {\n      if (next.b && (prev?.a != next.a)) {\n        detectionState.startCheck();\n      }\n    });\n    ref.listenManual(configStateProvider, (prev, next) {\n      if (prev != next) {\n        globalState.appController.savePreferencesDebounce();\n      }\n    });\n    WidgetsBinding.instance.addPostFrameCallback((_) {\n      _updateDashboardRefreshState();\n      detectionState.tryStartCheck();\n    });\n    if (window == null) {\n      return;\n    }\n    ref.listenManual(autoSetSystemDnsStateProvider, (prev, next) async {\n      if (prev == next) {\n        return;\n      }\n      if (next.a == true && next.b == true) {\n        macOS?.updateDns(false);\n      } else {\n        macOS?.updateDns(true);\n      }\n    });\n    ref.listenManual(currentBrightnessProvider, (prev, next) {\n      if (prev == next) {\n        return;\n      }\n      window?.updateMacOSBrightness(next);\n    }, fireImmediately: true);\n  }\n\n  @override\n  void dispose() {\n    _dashboardRefreshDebounceTimer?.cancel();\n    _missedUpdateCheckTimer?.cancel();\n    dashboardRefreshManager.tick1s.removeListener(_dashboardTickListener);\n    WidgetsBinding.instance.removeObserver(this);\n    super.dispose();\n  }\n\n  Future<void> _updateDashboardRefreshState() async {\n    final lifecycleState = WidgetsBinding.instance.lifecycleState;\n    final isForeground =\n        lifecycleState == null || lifecycleState == AppLifecycleState.resumed;\n    var isVisible = true;\n    var isMinimized = false;\n    if (system.isDesktop) {\n      final visible = await window?.isVisible;\n      if (visible == false) {\n        isVisible = false;\n      }\n      isMinimized = await window?.isMinimized ?? false;\n    }\n    final shouldRun = isForeground && isVisible && !isMinimized;\n\n    if (!shouldRun) {\n      _dashboardRefreshDebounceTimer?.cancel();\n      _dashboardRefreshDebounceTimer = null;\n      if (_isRefreshActive) {\n        dashboardRefreshManager.stop();\n        _isRefreshActive = false;\n      }\n      return;\n    }\n\n    if (_isRefreshActive) {\n      return;\n    }\n\n    _dashboardRefreshDebounceTimer?.cancel();\n    _dashboardRefreshDebounceTimer = Timer(\n      const Duration(milliseconds: 1000),\n      () {\n        if (!mounted) return;\n        if (_isRefreshActive) return;\n        dashboardRefreshManager.start();\n        _isRefreshActive = true;\n      },\n    );\n  }\n\n  bool get _shouldCheckMissedUpdates {\n    if (_lastMissedUpdateCheck == null) return true;\n    return DateTime.now().difference(_lastMissedUpdateCheck!) > _missedUpdateCheckThrottle;\n  }\n\n  void _scheduleMissedUpdateCheck() {\n    if (!_shouldCheckMissedUpdates) return;\n    _missedUpdateCheckTimer?.cancel();\n    _missedUpdateCheckTimer = Timer(_missedUpdateCheckDelay, () {\n      _lastMissedUpdateCheck = DateTime.now();\n      globalState.appController.checkAndUpdateMissedProfiles();\n    });\n  }\n\n  @override\n  Future<void> didChangeAppLifecycleState(AppLifecycleState state) async {\n    final isBackgroundState = state == AppLifecycleState.paused ||\n        state == AppLifecycleState.hidden ||\n        (state == AppLifecycleState.inactive && !system.isDesktop);\n\n    if (isBackgroundState) {\n      _missedUpdateCheckTimer?.cancel();\n      globalState.appController.savePreferences();\n      await globalState.handleBackground();\n    } else if (state == AppLifecycleState.resumed) {\n      globalState.handleForeground();\n      render?.resume();\n      await globalState.resumeForegroundUpdates();\n      await globalState.appController.syncWakelockIfNeeded();\n      _scheduleMissedUpdateCheck();\n\n      final hasDetection = ref\n          .read(dashboardStateProvider)\n          .dashboardWidgets\n          .contains(DashboardWidget.networkDetection);\n      if (hasDetection) {\n        detectionState.startCheck(immediate: true);\n      }\n    }\n    if (state == AppLifecycleState.resumed && system.isAndroid) {\n      final hidden = ref.read(appSettingProvider.select((s) => s.hidden));\n      app.updateExcludeFromRecents(hidden);\n      SystemChrome.setSystemUIOverlayStyle(globalState.appState.systemUiOverlayStyle);\n    }\n    if (state == AppLifecycleState.inactive) {\n      WidgetsBinding.instance.addPostFrameCallback((_) {\n        detectionState.tryStartCheck();\n      });\n    }\n    _updateDashboardRefreshState();\n  }\n\n  @override\n  void didChangePlatformBrightness() {\n    globalState.appController.updateBrightness();\n    globalState.appController.updateTray();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return widget.child;\n  }\n}\n\nclass AppEnvManager extends StatelessWidget {\n  final Widget child;\n\n  const AppEnvManager({super.key, required this.child});\n\n  @override\n  Widget build(BuildContext context) {\n    if (kDebugMode) {\n      if (globalState.isPre) {\n        return Banner(\n          message: 'DEBUG',\n          location: BannerLocation.topEnd,\n          child: child,\n        );\n      }\n    }\n    if (globalState.isPre) {\n      return Banner(\n        message: 'PRE',\n        location: BannerLocation.topEnd,\n        child: child,\n      );\n    }\n    return child;\n  }\n}\n\nclass AppSidebarContainer extends ConsumerWidget {\n  final Widget child;\n\n  const AppSidebarContainer({super.key, required this.child});\n\n  Widget _buildLoading() {\n    return Consumer(\n      builder: (_, ref, _) {\n        final loading = ref.watch(loadingProvider);\n        final isMobileView = ref.watch(isMobileViewProvider);\n        return loading && !isMobileView\n            ? RotatedBox(\n                quarterTurns: 1,\n                child: const LinearProgressIndicator(),\n              )\n            : Container();\n      },\n    );\n  }\n\n  Widget _buildBackground({\n    required BuildContext context,\n    required Widget child,\n  }) {\n    if (!system.isMacOS) {\n      return Material(\n        color: context.colorScheme.surfaceContainer,\n        child: child,\n      );\n    }\n    return TransparentMacOSSidebar(\n      child: Material(color: Colors.transparent, child: child),\n    );\n  }\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final navigationState = ref.watch(navigationStateProvider);\n    final navigationItems = navigationState.navigationItems;\n    final isMobileView = navigationState.viewMode == ViewMode.mobile;\n    if (isMobileView) {\n      return child;\n    }\n    final currentIndex = navigationState.currentIndex;\n    final showLabel = ref.watch(appSettingProvider).showLabel;\n    return Row(\n      children: [\n        Stack(\n          alignment: Alignment.topRight,\n          children: [\n            _buildBackground(\n              context: context,\n              child: SafeArea(\n                left: !system.isAndroid,\n                top: true,\n                right: false,\n                bottom: false,\n                child: Column(\n                  children: [\n                    if (system.isMacOS) const SizedBox(height: 22),\n                    const SizedBox(height: 16),\n                    if (!system.isMacOS) ...[const AppIcon(), const SizedBox(height: 12)],\n                    Expanded(\n                      child: ScrollConfiguration(\n                        behavior: HiddenBarScrollBehavior(),\n                        child: CallbackShortcuts(\n                          bindings: <ShortcutActivator, VoidCallback>{\n                            const SingleActivator(LogicalKeyboardKey.arrowUp): () {\n                              if (currentIndex > 0) {\n                                globalState.appController.toPage(\n                                  navigationItems[currentIndex - 1].label,\n                                );\n                              }\n                            },\n                            const SingleActivator(LogicalKeyboardKey.arrowDown): () {\n                              if (currentIndex < navigationItems.length - 1) {\n                                globalState.appController.toPage(\n                                  navigationItems[currentIndex + 1].label,\n                                );\n                              }\n                            },\n                            const SingleActivator(LogicalKeyboardKey.select): () {},\n                            const SingleActivator(LogicalKeyboardKey.enter): () {},\n                          },\n                          child: Focus(\n                            autofocus: true,\n                            child: NavigationRail(\n                              backgroundColor: Colors.transparent,\n                              selectedLabelTextStyle: context\n                                  .textTheme\n                                  .labelLarge!\n                                  .copyWith(\n                                    color: context.colorScheme.onSurface,\n                                  ),\n                              unselectedLabelTextStyle: context\n                                  .textTheme\n                                  .labelLarge!\n                                  .copyWith(\n                                    color: context.colorScheme.onSurface,\n                                  ),\n                              destinations: navigationItems\n                                  .map(\n                                    (e) => NavigationRailDestination(\n                                      icon: e.icon,\n                                      label: Text(Intl.message(e.label.name)),\n                                    ),\n                                  )\n                                  .toList(),\n                              onDestinationSelected: (index) {\n                                globalState.appController.toPage(\n                                  navigationItems[index].label,\n                                );\n                              },\n                              extended: showLabel,\n                              selectedIndex: currentIndex,\n                              labelType: showLabel\n                                  ? NavigationRailLabelType.none\n                                  : NavigationRailLabelType.all,\n                            ),\n                          ),\n                        ),\n                      ),\n                    ),\n                    const SizedBox(height: 16),\n                    if (window != null) const WindowLockButton(),\n                    const SizedBox(height: 16),\n                  ],\n                ),\n              ),\n            ),\n            _buildLoading(),\n          ],\n        ),\n        Expanded(flex: 1, child: ClipRect(child: child)),\n      ],\n    );\n  }\n}\n\nclass WindowLockButton extends ConsumerWidget {\n  const WindowLockButton({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final isLocked = ref.watch(\n      windowSettingProvider.select((state) => state.isLocked),\n    );\n\n    return IconButton(\n      onPressed: () async {\n        try {\n          final currentLocked = ref.read(\n            windowSettingProvider.select((state) => state.isLocked),\n          );\n          final newLocked = !currentLocked;\n\n          await windowManager.setResizable(!newLocked);\n\n          ref\n              .read(windowSettingProvider.notifier)\n              .updateState((state) => state.copyWith(isLocked: newLocked));\n        } catch (e) {\n          commonPrint.log('Window Lock Failed: $e');\n        }\n      },\n      icon: Icon(\n        isLocked ? Icons.lock : Icons.lock_open,\n        color: context.colorScheme.onSurfaceVariant,\n      ),\n    );\n  }\n}"
  },
  {
    "path": "lib/manager/clash_manager.dart",
    "content": "import 'package:bett_box/clash/clash.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/providers/app.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:bett_box/providers/state.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass ClashManager extends ConsumerStatefulWidget {\n  final Widget child;\n\n  const ClashManager({super.key, required this.child});\n\n  @override\n  ConsumerState<ClashManager> createState() => _ClashContainerState();\n}\n\nclass _ClashContainerState extends ConsumerState<ClashManager>\n    with AppMessageListener {\n  @override\n  Widget build(BuildContext context) {\n    return widget.child;\n  }\n\n  @override\n  void initState() {\n    super.initState();\n    clashMessage.addListener(this);\n    ref.listenManual(needSetupProvider, (prev, next) {\n      if (prev != next) {\n        globalState.appController.handleChangeProfile();\n      }\n    });\n    ref.listenManual(coreStateProvider, (prev, next) async {\n      if (prev != next) {\n        await clashCore.setState(next);\n      }\n    });\n    ref.listenManual(updateParamsProvider, (prev, next) {\n      if (prev != next) {\n        globalState.appController.updateClashConfigDebounce();\n      }\n    });\n\n    ref.listenManual(appSettingProvider.select((state) => state.openLogs), (\n      prev,\n      next,\n    ) {\n      if (next) {\n        clashCore.startLog();\n      } else {\n        clashCore.stopLog();\n      }\n    });\n  }\n\n  @override\n  Future<void> dispose() async {\n    clashMessage.removeListener(this);\n    super.dispose();\n  }\n\n  @override\n  Future<void> onDelay(Delay delay) async {\n    super.onDelay(delay);\n    final appController = globalState.appController;\n    appController.setDelay(delay);\n    debouncer.call(FunctionTag.updateDelay, () async {\n      appController.updateGroupsDebounce();\n    }, duration: const Duration(milliseconds: 5000));\n  }\n\n  @override\n  void onLog(Log log) {\n    ref.read(logsProvider.notifier).addLog(log);\n    if (log.logLevel == LogLevel.error) {\n      globalState.showNotifier(log.payload);\n    }\n    super.onLog(log);\n  }\n\n  @override\n  void onRequest(TrackerInfo trackerInfo) async {\n    ref.read(requestsProvider.notifier).addRequest(trackerInfo);\n    super.onRequest(trackerInfo);\n  }\n\n  @override\n  Future<void> onLoaded(String providerName) async {\n    ref\n        .read(providersProvider.notifier)\n        .setProvider(await clashCore.getExternalProvider(providerName));\n    globalState.appController.updateGroupsDebounce();\n    super.onLoaded(providerName);\n  }\n}\n"
  },
  {
    "path": "lib/manager/connectivity_manager.dart",
    "content": "import 'dart:async';\n\nimport 'package:connectivity_plus/connectivity_plus.dart';\nimport 'package:flutter/material.dart';\n\nclass ConnectivityManager extends StatefulWidget {\n  final Function(List<ConnectivityResult> results)? onConnectivityChanged;\n  final Widget child;\n\n  const ConnectivityManager({\n    super.key,\n    this.onConnectivityChanged,\n    required this.child,\n  });\n\n  @override\n  State<ConnectivityManager> createState() => _ConnectivityManagerState();\n}\n\nclass _ConnectivityManagerState extends State<ConnectivityManager> {\n  late StreamSubscription subscription;\n\n  @override\n  void initState() {\n    super.initState();\n    subscription = Connectivity().onConnectivityChanged.listen((results) async {\n      widget.onConnectivityChanged?.call(results);\n    });\n  }\n\n  @override\n  void dispose() {\n    subscription.cancel();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return widget.child;\n  }\n}\n"
  },
  {
    "path": "lib/manager/hotkey_manager.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/common.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:hotkey_manager/hotkey_manager.dart';\n\nclass HotKeyManager extends ConsumerStatefulWidget {\n  final Widget child;\n\n  const HotKeyManager({super.key, required this.child});\n\n  @override\n  ConsumerState<HotKeyManager> createState() => _HotKeyManagerState();\n}\n\nclass _HotKeyManagerState extends ConsumerState<HotKeyManager> {\n  @override\n  void initState() {\n    super.initState();\n    ref.listenManual(hotKeyActionsProvider, (prev, next) {\n      if (!hotKeyActionListEquality.equals(prev, next)) {\n        _updateHotKeys(hotKeyActions: next);\n      }\n    }, fireImmediately: true);\n  }\n\n  Future<void> _handleHotKeyAction(HotAction action) async {\n    switch (action) {\n      case HotAction.mode:\n        globalState.appController.updateMode();\n      case HotAction.start:\n        globalState.appController.updateStart();\n      case HotAction.view:\n        globalState.appController.updateVisible();\n      case HotAction.proxy:\n        globalState.appController.updateSystemProxy();\n      case HotAction.tun:\n        globalState.appController.updateTun();\n    }\n  }\n\n  Future<void> _updateHotKeys({\n    required List<HotKeyAction> hotKeyActions,\n  }) async {\n    await hotKeyManager.unregisterAll();\n    final hotkeyActionHandles = hotKeyActions\n        .where((hotKeyAction) {\n          return hotKeyAction.key != null && hotKeyAction.modifiers.isNotEmpty;\n        })\n        .map<Future>((hotKeyAction) async {\n          final modifiers = hotKeyAction.modifiers\n              .map((item) => item.toHotKeyModifier())\n              .toList();\n          final hotKey = HotKey(\n            key: PhysicalKeyboardKey(hotKeyAction.key!),\n            modifiers: modifiers,\n          );\n          return await hotKeyManager.register(\n            hotKey,\n            keyDownHandler: (_) {\n              _handleHotKeyAction(hotKeyAction.action);\n            },\n          );\n        });\n    await Future.wait(hotkeyActionHandles);\n  }\n\n  Shortcuts _buildShortcuts(Widget child) {\n    return Shortcuts(\n      shortcuts: {\n        utils.controlSingleActivator(LogicalKeyboardKey.keyW):\n            CloseWindowIntent(),\n      },\n      child: Actions(\n        actions: {\n          CloseWindowIntent: CallbackAction<CloseWindowIntent>(\n            onInvoke: (_) => globalState.appController.handleBackOrExit(),\n          ),\n          DoNothingIntent: CallbackAction<DoNothingIntent>(\n            onInvoke: (_) => null,\n          ),\n        },\n        child: child,\n      ),\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return _buildShortcuts(widget.child);\n  }\n}\n"
  },
  {
    "path": "lib/manager/manager.dart",
    "content": "export 'android_manager.dart';\nexport 'app_manager.dart';\nexport 'clash_manager.dart';\nexport 'connectivity_manager.dart';\nexport 'message_manager.dart';\nexport 'proxy_manager.dart';\nexport 'smart_auto_stop_manager.dart';\nexport 'theme_manager.dart';\nexport 'tile_manager.dart';\nexport 'tray_manager.dart';\nexport 'vpn_manager.dart';\nexport 'window_manager.dart';\n"
  },
  {
    "path": "lib/manager/message_manager.dart",
    "content": "import 'dart:async';\nimport 'dart:math';\n\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/widgets/fade_box.dart';\nimport 'package:flutter/material.dart';\n\nclass MessageManager extends StatefulWidget {\n  final Widget child;\n\n  const MessageManager({super.key, required this.child});\n\n  @override\n  State<MessageManager> createState() => MessageManagerState();\n}\n\nclass MessageManagerState extends State<MessageManager> {\n  final _messagesNotifier = ValueNotifier<List<CommonMessage>>([]);\n  final List<CommonMessage> _bufferMessages = [];\n  bool _pushing = false;\n\n  @override\n  void initState() {\n    super.initState();\n  }\n\n  @override\n  void dispose() {\n    _messagesNotifier.dispose();\n    super.dispose();\n  }\n\n  Future<void> message(\n    String text, {\n    VoidCallback? onAction,\n    String? actionLabel,\n    bool showCountdown = false,\n  }) async {\n    if (_messagesNotifier.value.any((m) => m.text == text) ||\n        _bufferMessages.any((m) => m.text == text)) {\n      return;\n    }\n\n    final commonMessage = CommonMessage(\n      id: utils.uuidV4,\n      text: text,\n      onAction: onAction,\n      actionLabel: actionLabel,\n      showCountdown: showCountdown,\n    );\n    _bufferMessages.add(commonMessage);\n    await _showMessage();\n  }\n\n  Future<void> _showMessage() async {\n    if (_pushing) return;\n    _pushing = true;\n    while (_bufferMessages.isNotEmpty) {\n      final commonMessage = _bufferMessages.removeAt(0);\n      _messagesNotifier.value = List.from(_messagesNotifier.value)..add(commonMessage);\n      await Future.delayed(const Duration(seconds: 1));\n      Future.delayed(commonMessage.duration, () => _handleRemove(commonMessage));\n      if (_bufferMessages.isEmpty) _pushing = false;\n    }\n  }\n\n  void _handleRemove(CommonMessage commonMessage) {\n    _messagesNotifier.value = List<CommonMessage>.from(_messagesNotifier.value)\n      ..remove(commonMessage);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Stack(\n      children: [\n        widget.child,\n        ValueListenableBuilder(\n          valueListenable: _messagesNotifier,\n          builder: (_, messages, _) {\n            return FadeThroughBox(\n              margin: EdgeInsets.only(\n                top: kToolbarHeight + 8,\n                left: 12,\n                right: 12,\n              ),\n              alignment: Alignment.topCenter,\n              child: messages.isEmpty\n                  ? SizedBox()\n                  : LayoutBuilder(\n                      key: Key(messages.last.id),\n                      builder: (_, constraints) {\n                        final message = messages.last;\n                        return Card(\n                          shape: const RoundedRectangleBorder(\n                            borderRadius: BorderRadius.all(\n                              Radius.circular(12.0),\n                            ),\n                          ),\n                          elevation: 10,\n                          color: context.colorScheme.surfaceContainerHigh,\n                          child: Container(\n                            width: min(constraints.maxWidth, 500),\n                            padding: EdgeInsets.symmetric(\n                              horizontal: 12,\n                              vertical: 12,\n                            ),\n                            child: Row(\n                              mainAxisSize: MainAxisSize.min,\n                              mainAxisAlignment: MainAxisAlignment.center,\n                              children: [\n                                if (message.showCountdown)\n                                  _CountdownWidget(\n                                    duration: message.duration,\n                                  ),\n                                Expanded(\n                                  child: Text(\n                                    message.text,\n                                    textAlign: TextAlign.center,\n                                  ),\n                                ),\n                                if (message.actionLabel != null &&\n                                    message.onAction != null) ...[\n                                  const SizedBox(width: 8),\n                                  TextButton(\n                                    onPressed: () {\n                                      _handleRemove(message);\n                                      message.onAction?.call();\n                                    },\n                                    style: TextButton.styleFrom(\n                                      minimumSize: Size.zero,\n                                      tapTargetSize:\n                                          MaterialTapTargetSize.shrinkWrap,\n                                      visualDensity: VisualDensity.compact,\n                                      padding: const EdgeInsets.symmetric(\n                                        horizontal: 8,\n                                        vertical: 4,\n                                      ),\n                                      foregroundColor:\n                                          context.colorScheme.primary,\n                                    ),\n                                    child: Text(message.actionLabel!),\n                                  ),\n                                ],\n                              ],\n                            ),\n                          ),\n                        );\n                      },\n                    ),\n            );\n          },\n        ),\n      ],\n    );\n  }\n}\n\nclass _CountdownWidget extends StatefulWidget {\n  final Duration duration;\n\n  const _CountdownWidget({required this.duration});\n\n  @override\n  State<_CountdownWidget> createState() => _CountdownWidgetState();\n}\n\nclass _CountdownWidgetState extends State<_CountdownWidget>\n    with SingleTickerProviderStateMixin {\n  late AnimationController _controller;\n  late int _totalSeconds;\n\n  @override\n  void initState() {\n    super.initState();\n    _totalSeconds = widget.duration.inSeconds;\n    _controller = AnimationController(\n      vsync: this,\n      duration: widget.duration,\n    );\n    _controller.forward();\n  }\n\n  @override\n  void dispose() {\n    _controller.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final primaryColor = context.colorScheme.primary;\n\n    return Padding(\n      padding: const EdgeInsets.only(right: 8),\n      child: SizedBox(\n        width: 24,\n        height: 24,\n        child: AnimatedBuilder(\n          animation: _controller,\n          builder: (context, child) {\n            final remaining = (_totalSeconds * (1 - _controller.value)).ceil();\n            final currentSecond = remaining > 0 ? remaining : 1;\n\n            return Stack(\n              alignment: Alignment.center,\n              children: [\n                CircularProgressIndicator(\n                  value: 1 - _controller.value,\n                  strokeWidth: 2,\n                  backgroundColor: primaryColor.withAlpha(26),\n                  valueColor: AlwaysStoppedAnimation<Color>(primaryColor),\n                ),\n                Text(\n                  '$currentSecond',\n                  style: TextStyle(\n                    fontSize: 10,\n                    fontWeight: FontWeight.bold,\n                    color: primaryColor,\n                  ),\n                ),\n              ],\n            );\n          },\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/manager/proxy_manager.dart",
    "content": "import 'package:bett_box/common/proxy.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/providers/state.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass ProxyManager extends ConsumerStatefulWidget {\n  final Widget child;\n\n  const ProxyManager({super.key, required this.child});\n\n  @override\n  ConsumerState createState() => _ProxyManagerState();\n}\n\nclass _ProxyManagerState extends ConsumerState<ProxyManager> {\n  Future<void> _updateProxy(ProxyState proxyState) async {\n    final isStart = proxyState.isStart;\n    final systemProxy = proxyState.systemProxy;\n    final port = proxyState.port;\n    if (isStart && systemProxy) {\n      proxy?.startProxy(port, proxyState.bassDomain);\n    } else {\n      proxy?.stopProxy();\n    }\n  }\n\n  @override\n  void initState() {\n    super.initState();\n    ref.listenManual(proxyStateProvider, (prev, next) {\n      if (prev != next) {\n        _updateProxy(next);\n      }\n    }, fireImmediately: true);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return widget.child;\n  }\n}\n"
  },
  {
    "path": "lib/manager/smart_auto_stop_manager.dart",
    "content": "import 'dart:async';\n\nimport 'package:connectivity_plus/connectivity_plus.dart';\nimport 'package:bett_box/clash/clash.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/common/network_matcher.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/plugins/service.dart';\nimport 'package:bett_box/providers/providers.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:synchronized/synchronized.dart';\n\n/// Smart Auto Stop Manager\nclass SmartAutoStopManager extends ConsumerStatefulWidget {\n  final Widget child;\n\n  const SmartAutoStopManager({super.key, required this.child});\n\n  @override\n  ConsumerState<SmartAutoStopManager> createState() =>\n      _SmartAutoStopManagerState();\n}\n\nclass _SmartAutoStopManagerState extends ConsumerState<SmartAutoStopManager> {\n  StreamSubscription<List<ConnectivityResult>>? _connectivitySubscription;\n\n  final _checkLock = Lock();\n\n  int _checkSequence = 0;\n\n  late final NativeEventCallback _nativeEventCallback;\n\n  @override\n  void initState() {\n    super.initState();\n    _initConnectivityListener();\n    _initNativeNetworkListener();\n  }\n\n  void _initNativeNetworkListener() {\n    _nativeEventCallback = (String method, dynamic arguments) async {\n      if (method == 'networkChanged') {\n        _onNativeNetworkChanged();\n      } else if (method == 'quickResponse') {\n        final vpnProps = ref.read(vpnSettingProvider);\n        if (vpnProps.quickResponse) {\n          commonPrint.log(\n            'Network change: Closing connections.',\n          );\n          clashCore.closeConnections();\n        }\n      }\n    };\n    service?.addNativeEventCallback(_nativeEventCallback);\n  }\n\n  void _onNativeNetworkChanged() {\n    final vpnProps = ref.read(vpnSettingProvider);\n    if (!vpnProps.smartAutoStop) return;\n    _debouncedCheckCurrentNetwork();\n  }\n\n  @override\n  void didChangeDependencies() {\n    super.didChangeDependencies();\n    ref.listenManual(vpnSettingProvider, (prev, next) {\n      if (prev?.smartAutoStop != next.smartAutoStop ||\n          prev?.smartAutoStopNetworks != next.smartAutoStopNetworks) {\n        _onSettingsChanged();\n      }\n    });\n  }\n\n  void _initConnectivityListener() {\n    _connectivitySubscription = Connectivity().onConnectivityChanged.listen((\n      results,\n    ) {\n      _onConnectivityChanged(results);\n    });\n  }\n\n  void _onSettingsChanged() {\n    final vpnProps = ref.read(vpnSettingProvider);\n    if (!vpnProps.smartAutoStop) {\n      // Feature disabled, if we were smart-stopped, resume.\n      final isSmartStopped = ref.read(isSmartStoppedProvider);\n      if (isSmartStopped) {\n        ref.read(isSmartStoppedProvider.notifier).set(false);\n        _restartVpn();\n      }\n      return;\n    }\n    // Re-check current network\n    _checkCurrentNetwork();\n  }\n\n  Future<void> _onConnectivityChanged(List<ConnectivityResult> results) async {\n    final vpnProps = ref.read(vpnSettingProvider);\n    if (!vpnProps.smartAutoStop) return;\n\n    _debouncedCheckCurrentNetwork();\n  }\n\n  void _debouncedCheckCurrentNetwork() {\n    final currentSequence = ++_checkSequence;\n\n    Future.delayed(const Duration(milliseconds: 1000), () async {\n      if (currentSequence != _checkSequence) {\n        commonPrint.log('Smart Auto Stop: Skipping outdated network check');\n        return;\n      }\n      await _checkCurrentNetwork();\n    });\n  }\n\n  Future<void> _checkCurrentNetwork() async {\n    await _checkLock.synchronized(() async {\n      final vpnProps = ref.read(vpnSettingProvider);\n      if (!vpnProps.smartAutoStop) return;\n\n      final networks = vpnProps.smartAutoStopNetworks;\n      if (networks.isEmpty) return;\n\n      final isSmartStopped = ref.read(isSmartStoppedProvider);\n\n      // Get current IP(s) — always from native on Android for consistency\n      List<String> candidateIps;\n      if (system.isAndroid) {\n        candidateIps = await _getNativeLocalIpAddresses();\n      } else {\n        final ip = await _getLocalIpAddress();\n        candidateIps = ip != null ? [ip] : [];\n      }\n\n      if (candidateIps.isEmpty) {\n        commonPrint.log('Smart Auto Stop: No IP found. Skipping.');\n        return;\n      }\n\n      // Match: any IP matches any rule = should stop\n      final shouldStop = candidateIps.any(\n        (ip) => NetworkMatcher.matchAny(ip, networks),\n      );\n\n      commonPrint.log(\n        'SmartAutoStop: IPs=${candidateIps.join(\",\")}, RuleMatch=$shouldStop, SmartStopped=$isSmartStopped',\n      );\n\n      // Dedup: only act on state transitions\n      if (shouldStop && !isSmartStopped) {\n        // Need to stop, but only if VPN is actually running\n        final isRunning = ref.read(runTimeProvider) != null || globalState.isStart;\n        if (isRunning) {\n          ref.read(isSmartStoppedProvider.notifier).set(true);\n          commonPrint.log('Smart Auto Stop: Stopping ...');\n          await _stopVpn();\n        }\n      } else if (!shouldStop && isSmartStopped) {\n        // Need to resume\n        ref.read(isSmartStoppedProvider.notifier).set(false);\n        commonPrint.log('Smart Auto Stop: Restarting ...');\n        await _restartVpn();\n      }\n    });\n  }\n\n\n\n  Future<List<String>> _getNativeLocalIpAddresses() async {\n    try {\n      final serviceInstance = service;\n      if (serviceInstance != null) {\n        final ips = await serviceInstance.getLocalIpAddresses();\n        if (ips.isNotEmpty) return ips;\n      }\n    } catch (e) {\n      commonPrint.log('Smart Auto Stop: Native IP error: $e');\n    }\n    // Fallback to Flutter layer\n    final ip = await _getLocalIpAddress();\n    return ip != null ? [ip] : [];\n  }\n\n  Future<String?> _getLocalIpAddress() async {\n    return await utils.getLocalIpAddress();\n  }\n\n  Future<void> _stopVpn() async {\n    if (system.isAndroid) {\n      // Android: Enable smart-stop mode (Blank notification)\n      // This keeps the service alive but stops the VPN logic\n      await service?.setSmartStopped(true);\n      await service?.smartStop();\n\n      // Update Dart state to look \"stopped\"\n      globalState.startTime = null;\n      clashCore.resetTraffic();\n      ref.read(trafficsProvider.notifier).clear();\n      ref.read(totalTrafficProvider.notifier).value = Traffic();\n      ref.read(runTimeProvider.notifier).value = null;\n    } else {\n      // Desktop: Full stop\n      await globalState.appController.updateStatus(false);\n    }\n  }\n\n  Future<void> _restartVpn() async {\n    if (system.isAndroid) {\n      // Android: Resume from smart-stop mode\n      await service?.setSmartStopped(false);\n      await service?.smartResume();\n\n      globalState.startTime = DateTime.now();\n      ref.read(runTimeProvider.notifier).value = 0;\n      globalState.appController.addCheckIpNumDebounce();\n    } else {\n      // Desktop: Full start\n      await globalState.appController.updateStatus(true);\n    }\n  }\n\n  @override\n  void dispose() {\n    _connectivitySubscription?.cancel();\n    service?.removeNativeEventCallback(_nativeEventCallback);\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return widget.child;\n  }\n}\n"
  },
  {
    "path": "lib/manager/theme_manager.dart",
    "content": "import 'dart:math';\n\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/common/theme.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nimport '../providers/state.dart';\n\nclass ThemeManager extends ConsumerWidget {\n  final Widget child;\n\n  const ThemeManager({super.key, required this.child});\n\n  Widget _buildSystemUi(Widget child) {\n    if (!system.isAndroid) {\n      return child;\n    }\n    return AnnotatedRegion<SystemUiMode>(\n      sized: false,\n      value: SystemUiMode.edgeToEdge,\n      child: Consumer(\n        builder: (context, ref, _) {\n          final brightness = ref.watch(currentBrightnessProvider);\n          final iconBrightness = brightness == Brightness.light\n              ? Brightness.dark\n              : Brightness.light;\n          globalState.appState = globalState.appState.copyWith(\n            systemUiOverlayStyle: SystemUiOverlayStyle(\n              statusBarColor: Colors.transparent,\n              statusBarIconBrightness: iconBrightness,\n              systemNavigationBarIconBrightness: iconBrightness,\n              systemNavigationBarColor: context.colorScheme.surface,\n              systemNavigationBarDividerColor: Colors.transparent,\n            ),\n          );\n          return AnnotatedRegion<SystemUiOverlayStyle>(\n            value: globalState.appState.systemUiOverlayStyle,\n            sized: false,\n            child: child,\n          );\n        },\n      ),\n    );\n  }\n\n  // _buildScrollbar(Widget child) {\n  //   return Consumer(\n  //     builder: (_, ref, child) {\n  //       final isMobileView = ref.read(isMobileViewProvider);\n  //       if (isMobileView) {\n  //         return ScrollConfiguration(\n  //           behavior: HiddenBarScrollBehavior(),\n  //           child: child!,\n  //         );\n  //       }\n  //       return child!;\n  //     },\n  //     child: child,\n  //   );\n  // }\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final textScale = ref.read(\n      themeSettingProvider.select((state) => state.textScale),\n    );\n    final double textScaleFactor = max(\n      min(\n        textScale.enable ? textScale.scale : defaultTextScaleFactor,\n        maxTextScale,\n      ),\n      minTextScale,\n    );\n\n    globalState.measure = Measure.of(context, textScaleFactor);\n    globalState.theme = CommonTheme.of(context, textScaleFactor);\n    final padding = MediaQuery.of(context).padding;\n    final height = MediaQuery.of(context).size.height;\n    return MediaQuery(\n      data: MediaQuery.of(context).copyWith(\n        textScaler: TextScaler.linear(textScaleFactor),\n        padding: padding.copyWith(\n          top: padding.top > height * 0.3 ? 20.0 : padding.top,\n        ),\n      ),\n      child: LayoutBuilder(\n        builder: (_, container) {\n          globalState.appController.updateViewSize(\n            Size(container.maxWidth, container.maxHeight),\n          );\n          return _buildSystemUi(child);\n        },\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/manager/tile_manager.dart",
    "content": "import 'package:bett_box/plugins/tile.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:flutter/material.dart';\n\nclass TileManager extends StatefulWidget {\n  final Widget child;\n\n  const TileManager({super.key, required this.child});\n\n  @override\n  State<TileManager> createState() => _TileContainerState();\n}\n\nclass _TileContainerState extends State<TileManager> with TileListener {\n  @override\n  Widget build(BuildContext context) {\n    return widget.child;\n  }\n\n  @override\n  void onStart() {\n    globalState.appController.updateStatus(true);\n    super.onStart();\n  }\n\n  @override\n  Future<void> onStop() async {\n    globalState.appController.updateStatus(false);\n    super.onStop();\n  }\n\n  @override\n  void initState() {\n    super.initState();\n    tile?.addListener(this);\n  }\n\n  @override\n  void dispose() {\n    tile?.removeListener(this);\n    super.dispose();\n  }\n}\n"
  },
  {
    "path": "lib/manager/tray_manager.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/providers/state.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:tray_manager/tray_manager.dart';\n\nclass TrayManager extends ConsumerStatefulWidget {\n  final Widget child;\n\n  const TrayManager({super.key, required this.child});\n\n  @override\n  ConsumerState<TrayManager> createState() => _TrayContainerState();\n}\n\nclass _TrayContainerState extends ConsumerState<TrayManager> with TrayListener {\n  @override\n  void initState() {\n    super.initState();\n    trayManager.addListener(this);\n    ref.listenManual(trayStateProvider, (prev, next) {\n      if (prev != next) {\n        globalState.appController.updateTray();\n      }\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return widget.child;\n  }\n\n  @override\n  void onTrayIconRightMouseDown() {\n    // ignore: deprecated_member_use\n    trayManager.popUpContextMenu(bringAppToFront: true);\n  }\n\n\n  @override\n  onTrayIconMouseDown() {\n    window?.show();\n  }\n\n  @override\n  dispose() {\n    trayManager.removeListener(this);\n    super.dispose();\n  }\n}\n"
  },
  {
    "path": "lib/manager/vpn_manager.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/providers/app.dart';\nimport 'package:bett_box/providers/state.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass VpnManager extends ConsumerStatefulWidget {\n  final Widget child;\n\n  const VpnManager({super.key, required this.child});\n\n  @override\n  ConsumerState<VpnManager> createState() => _VpnContainerState();\n}\n\nclass _VpnContainerState extends ConsumerState<VpnManager> {\n  @override\n  void initState() {\n    super.initState();\n    ref.listenManual(vpnStateProvider, (prev, next) {\n      // Skip tip\n      if (prev == null || prev == next) return;\n      \n      final prevProps = prev.vpnProps;\n      final nextProps = next.vpnProps;\n      \n      // Check\n      final onlySmartAutoStopChanged = prevProps.copyWith(\n        smartAutoStop: nextProps.smartAutoStop,\n        smartAutoStopNetworks: nextProps.smartAutoStopNetworks,\n      ) == nextProps;\n      \n      final onlyQuickResponseChanged = prevProps.copyWith(\n        quickResponse: nextProps.quickResponse,\n      ) == nextProps;\n      \n      if (onlySmartAutoStopChanged || onlyQuickResponseChanged) {\n        return; // No tip needed\n      }\n      \n      showTip();\n    });\n  }\n\n  void showTip() {\n    debouncer.call(FunctionTag.vpnTip, () {\n      if (ref.read(runTimeProvider.notifier).isStart) {\n        globalState.showNotifier(\n          appLocalizations.vpnTip,\n          onAction: () async {\n            await globalState.appController.updateStatus(false);\n            await Future.delayed(const Duration(milliseconds: 500));\n            await globalState.appController.updateStatus(true);\n          },\n          actionLabel: appLocalizations.restart,\n          showCountdown: true,\n        );\n      }\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return widget.child;\n  }\n}\n"
  },
  {
    "path": "lib/manager/window_manager.dart",
    "content": "import 'dart:async';\nimport 'dart:io';\n\nimport 'package:file_picker/file_picker.dart';\n\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/providers/providers.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:window_ext/window_ext.dart';\nimport 'package:window_manager/window_manager.dart';\n\nclass WindowManager extends ConsumerStatefulWidget {\n  final Widget child;\n\n  const WindowManager({super.key, required this.child});\n\n  @override\n  ConsumerState<WindowManager> createState() => _WindowContainerState();\n}\n\nclass _WindowContainerState extends ConsumerState<WindowManager>\n    with WindowListener, WindowExtListener {\n  Timer? _renderToggleTimer;\n  bool? _pendingRenderResume;\n\n  void _scheduleRenderToggle(bool resume) {\n    _pendingRenderResume = resume;\n    _renderToggleTimer?.cancel();\n    _renderToggleTimer = Timer(const Duration(milliseconds: 500), () {\n      if (_pendingRenderResume == true) {\n        render?.resume();\n      } else {\n        render?.pause();\n      }\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return widget.child;\n  }\n\n  ProviderSubscription? _autoLaunchSub;\n  ProviderSubscription? _smartDelaySub;\n\n  @override\n  void initState() {\n    super.initState();\n    _autoLaunchSub =\n        ref.listenManual(appSettingProvider.select((state) => state.autoLaunch), (\n      prev,\n      next,\n    ) {\n      if (prev != next) {\n        final smartDelayLaunch = ref.read(appSettingProvider).smartDelayLaunch;\n        debouncer.call(FunctionTag.autoLaunch, () {\n          autoLaunch?.updateStatus(next, requireNetwork: smartDelayLaunch);\n        });\n      }\n    });\n\n    _smartDelaySub = ref.listenManual(\n      appSettingProvider.select((state) => state.smartDelayLaunch),\n      (prev, next) {\n        if (prev != next) {\n          final autoLaunchEnabled = ref.read(appSettingProvider).autoLaunch;\n          if (autoLaunchEnabled) {\n            autoLaunch?.updateStatus(true, requireNetwork: next);\n          }\n        }\n      },\n    );\n    windowExtManager.addListener(this);\n    windowManager.addListener(this);\n  }\n\n  @override\n  void onWindowClose() async {\n    globalState.appController.unBackBlock();\n    await globalState.appController.handleBackOrExit();\n  }\n\n  @override\n  Future<void> onShouldTerminate() async {\n    await globalState.appController.handleExit();\n    super.onShouldTerminate();\n  }\n\n  @override\n  Future<void> onWindowMoved() async {\n    super.onWindowMoved();\n    final offset = await windowManager.getPosition();\n    ref\n        .read(windowSettingProvider.notifier)\n        .updateState(\n          (state) => state.copyWith(top: offset.dy, left: offset.dx),\n        );\n  }\n\n  @override\n  Future<void> onWindowResized() async {\n    super.onWindowResized();\n    final size = await windowManager.getSize();\n    ref\n        .read(windowSettingProvider.notifier)\n        .updateState(\n          (state) => state.copyWith(width: size.width, height: size.height),\n        );\n  }\n\n  @override\n  void onWindowMinimize() async {\n    globalState.appController.savePreferencesDebounce();\n    _renderToggleTimer?.cancel();\n    await globalState.handleBackground();\n    super.onWindowMinimize();\n  }\n\n  @override\n  void onWindowRestore() {\n    globalState.handleForeground();\n    _scheduleRenderToggle(true);\n    unawaited(globalState.resumeForegroundUpdates());\n    unawaited(globalState.appController.syncWakelockIfNeeded());\n    super.onWindowRestore();\n  }\n\n  @override\n  Future<void> dispose() async {\n    _autoLaunchSub?.close();\n    _smartDelaySub?.close();\n    windowManager.removeListener(this);\n    windowExtManager.removeListener(this);\n    _renderToggleTimer?.cancel();\n    super.dispose();\n  }\n}\n\nclass WindowHeaderContainer extends StatelessWidget {\n  final Widget child;\n\n  const WindowHeaderContainer({super.key, required this.child});\n\n  @override\n  Widget build(BuildContext context) {\n    return Consumer(\n      builder: (_, ref, child) {\n        final isMobileView = ref.watch(isMobileViewProvider);\n        final version = ref.watch(versionProvider);\n        if ((version <= 10 || !isMobileView) && system.isMacOS) {\n          return child!;\n        }\n        return Stack(\n          children: [\n            Column(\n              children: [\n                SizedBox(height: kHeaderHeight),\n                Expanded(flex: 1, child: child!),\n              ],\n            ),\n            const WindowHeader(),\n          ],\n        );\n      },\n      child: child,\n    );\n  }\n}\n\nclass WindowHeader extends StatefulWidget {\n  const WindowHeader({super.key});\n\n  @override\n  State<WindowHeader> createState() => _WindowHeaderState();\n}\n\nclass _WindowHeaderState extends State<WindowHeader> {\n  final isMaximizedNotifier = ValueNotifier<bool>(false);\n  final isPinNotifier = ValueNotifier<bool>(false);\n  final isHoveringNotifier = ValueNotifier<bool>(false); // 新增：鼠标悬停状态\n\n  @override\n  void initState() {\n    super.initState();\n    _initNotifier();\n  }\n\n  Future<void> _initNotifier() async {\n    isMaximizedNotifier.value = await windowManager.isMaximized();\n    isPinNotifier.value = await windowManager.isAlwaysOnTop();\n  }\n\n  @override\n  void dispose() {\n    isMaximizedNotifier.dispose();\n    isPinNotifier.dispose();\n    isHoveringNotifier.dispose(); // 新增：释放资源\n    super.dispose();\n  }\n\n  Future<void> _updateMaximized() async {\n    final isMaximized = await windowManager.isMaximized();\n    switch (isMaximized) {\n      case true:\n        await windowManager.unmaximize();\n        break;\n      case false:\n        await windowManager.maximize();\n        break;\n    }\n    isMaximizedNotifier.value = await windowManager.isMaximized();\n  }\n\n  Future<void> _updatePin() async {\n    final isAlwaysOnTop = await windowManager.isAlwaysOnTop();\n    await windowManager.setAlwaysOnTop(!isAlwaysOnTop);\n    isPinNotifier.value = await windowManager.isAlwaysOnTop();\n  }\n\n  Widget _buildActions() {\n    final shouldUseHoverEffect = system.isWindows || system.isLinux;\n\n    return MouseRegion(\n      onEnter: shouldUseHoverEffect\n          ? (_) => isHoveringNotifier.value = true\n          : null,\n      onExit: shouldUseHoverEffect\n          ? (_) {\n              Future.delayed(const Duration(milliseconds: 100), () {\n                if (mounted) {\n                  isHoveringNotifier.value = false;\n                }\n              });\n            }\n          : null,\n      child: ValueListenableBuilder<bool>(\n        valueListenable: isHoveringNotifier,\n        builder: (_, isHovering, _) {\n          final showButtons = !shouldUseHoverEffect || isHovering;\n          return Opacity(\n            opacity: showButtons ? 1.0 : 0.0,\n            child: IgnorePointer(\n              ignoring: !showButtons,\n              child: Row(\n                children: [\n                  IconButton(\n                    onPressed: () async {\n                      _updatePin();\n                    },\n                    icon: ValueListenableBuilder(\n                      valueListenable: isPinNotifier,\n                      builder: (_, value, _) {\n                        return value\n                            ? const Icon(Icons.push_pin)\n                            : const Icon(Icons.push_pin_outlined);\n                      },\n                    ),\n                  ),\n                  IconButton(\n                    onPressed: () {\n                      windowManager.minimize();\n                    },\n                    icon: const Icon(Icons.remove),\n                  ),\n                  IconButton(\n                    onPressed: () async {\n                      _updateMaximized();\n                    },\n                    icon: ValueListenableBuilder(\n                      valueListenable: isMaximizedNotifier,\n                      builder: (_, value, _) {\n                        return value\n                            ? const Icon(Icons.filter_none, size: 20)\n                            : const Icon(Icons.crop_square);\n                      },\n                    ),\n                  ),\n                  IconButton(\n                    onPressed: () {\n                      FocusManager.instance.primaryFocus?.unfocus();\n                      globalState.appController.unBackBlock();\n                      isHoveringNotifier.value = true;\n                      globalState.appController.handleBackOrExit();\n                    },\n                    icon: const Icon(Icons.close),\n                  ),\n                ],\n              ),\n            ),\n          );\n        },\n      ),\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Material(\n      child: Stack(\n        alignment: AlignmentDirectional.center,\n        children: [\n          Positioned(\n            child: GestureDetector(\n              onPanStart: (_) {\n                windowManager.startDragging();\n              },\n              onDoubleTap: () {\n                _updateMaximized();\n              },\n              child: Container(\n                color: context.colorScheme.secondary.opacity15,\n                alignment: Alignment.centerLeft,\n                height: kHeaderHeight,\n              ),\n            ),\n          ),\n          if (system.isMacOS)\n            const Text(appName)\n          else ...[\n            Positioned(right: 0, child: _buildActions()),\n          ],\n        ],\n      ),\n    );\n  }\n}\n\nfinal sidebarIconPathProvider =\n    StateNotifierProvider<SidebarIconPathNotifier, String?>((ref) {\n      return SidebarIconPathNotifier();\n    });\n\nclass SidebarIconPathNotifier extends StateNotifier<String?> {\n  SidebarIconPathNotifier() : super(null) {\n    _init();\n  }\n\n  Future<void> _init() async {\n    final prefs = await preferences.sharedPreferencesCompleter.future;\n    state = prefs?.getString(customSidebarIconKey);\n  }\n\n  Future<void> updatePath(String? path) async {\n    state = path;\n    final prefs = await preferences.sharedPreferencesCompleter.future;\n    if (path == null) {\n      prefs?.remove(customSidebarIconKey);\n    } else {\n      prefs?.setString(customSidebarIconKey, path);\n    }\n  }\n}\n\nclass AppIcon extends ConsumerWidget {\n  const AppIcon({super.key});\n\n  Future<void> _handlePickImage(BuildContext context, WidgetRef ref) async {\n    final result = await FilePicker.platform.pickFiles(type: FileType.image);\n\n    if (result != null && result.files.single.path != null) {\n      final path = result.files.single.path!;\n      final file = File(path);\n      final size = await file.length();\n      if (size > 1024 * 1024) {\n        if (context.mounted) {\n          globalState.showNotifier('Image size exceeds 1MB');\n        }\n        return;\n      }\n      ref.read(sidebarIconPathProvider.notifier).updatePath(path);\n    }\n  }\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final isDark = Theme.of(context).brightness == Brightness.dark;\n    final customIconPath = ref.watch(sidebarIconPathProvider);\n\n    Widget icon;\n    if (customIconPath != null && customIconPath.isNotEmpty) {\n      icon = ClipOval(\n        child: Image.file(\n          File(customIconPath),\n          width: 32,\n          height: 32,\n          fit: BoxFit.cover,\n          cacheWidth: 64,\n          cacheHeight: 64,\n          errorBuilder: (_, _, _) {\n            // Fallback if file load fails\n            return Image.asset(\n              isDark\n                  ? 'assets/images/icon.png'\n                  : 'assets/images/icon_light.png',\n              fit: BoxFit.contain,\n            );\n          },\n        ),\n      );\n    } else {\n      icon = Image.asset(\n        isDark ? 'assets/images/icon.png' : 'assets/images/icon_light.png',\n        fit: BoxFit.contain,\n      );\n    }\n\n    return GestureDetector(\n      onLongPress: () => _handlePickImage(context, ref),\n      child: SizedBox(width: 40, height: 40, child: icon),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/models/app.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:flutter/services.dart';\nimport 'package:freezed_annotation/freezed_annotation.dart';\n\nimport 'common.dart';\nimport 'core.dart';\n\npart 'generated/app.freezed.dart';\n\ntypedef DelayMap = Map<String, Map<String, int?>>;\n\n@freezed\nabstract class AppState with _$AppState {\n  const factory AppState({\n    @Default(false) bool isInit,\n    @Default(false) bool backBlock,\n    @Default(PageLabel.dashboard) PageLabel pageLabel,\n    @Default([]) List<Package> packages,\n    @Default(0) int sortNum,\n    required Size viewSize,\n    @Default({}) DelayMap delayMap,\n    @Default([]) List<Group> groups,\n    @Default(0) int checkIpNum,\n    required Brightness brightness,\n    int? runTime,\n    @Default([]) List<ExternalProvider> providers,\n    String? localIp,\n    required FixedList<TrackerInfo> requests,\n    required int version,\n    required FixedList<Log> logs,\n    required FixedList<Traffic> traffics,\n    required Traffic totalTraffic,\n    @Default(false) bool realTunEnable,\n    @Default(false) bool loading,\n    required SystemUiOverlayStyle systemUiOverlayStyle,\n  }) = _AppState;\n}\n\nextension AppStateExt on AppState {\n  ViewMode get viewMode => utils.getViewMode(viewSize.width);\n\n  bool get isStart => runTime != null;\n}\n"
  },
  {
    "path": "lib/models/clash_config.dart",
    "content": "// ignore_for_file: invalid_annotation_target\n\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'generated/clash_config.freezed.dart';\npart 'generated/clash_config.g.dart';\n\ntypedef HostsMap = Map<String, String>;\n\nconst defaultClashConfig = ClashConfig();\n\nconst defaultTun = Tun();\nconst defaultDns = Dns();\nconst defaultNtp = Ntp();\nconst defaultSniffer = Sniffer();\nconst defaultTunnel = <TunnelEntry>[];\nconst defaultExperimental = Experimental();\nconst defaultGeoXUrl = GeoXUrl();\n\nconst defaultMixedPort = 7890;\nconst defaultKeepAliveInterval = 30;\n\nconst defaultBypassPrivateRouteAddress = [\n  '198.51.100.0/30',\n  '1.0.0.0/8',\n  '2.0.0.0/7',\n  '4.0.0.0/6',\n  '8.0.0.0/7',\n  '11.0.0.0/8',\n  '12.0.0.0/6',\n  '16.0.0.0/4',\n  '32.0.0.0/3',\n  '64.0.0.0/3',\n  '96.0.0.0/4',\n  '112.0.0.0/5',\n  '120.0.0.0/6',\n  '124.0.0.0/7',\n  '126.0.0.0/8',\n  '128.0.0.0/3',\n  '160.0.0.0/5',\n  '168.0.0.0/8',\n  '169.0.0.0/9',\n  '169.128.0.0/10',\n  '169.192.0.0/11',\n  '169.224.0.0/12',\n  '169.240.0.0/13',\n  '169.248.0.0/14',\n  '169.252.0.0/15',\n  '169.255.0.0/16',\n  '170.0.0.0/7',\n  '172.0.0.0/12',\n  '172.32.0.0/11',\n  '172.64.0.0/10',\n  '172.128.0.0/9',\n  '173.0.0.0/8',\n  '174.0.0.0/7',\n  '176.0.0.0/4',\n  '192.0.0.0/9',\n  '192.128.0.0/11',\n  '192.160.0.0/13',\n  '192.169.0.0/16',\n  '192.170.0.0/15',\n  '192.172.0.0/14',\n  '192.176.0.0/12',\n  '192.192.0.0/10',\n  '193.0.0.0/8',\n  '194.0.0.0/7',\n  '196.0.0.0/6',\n  '200.0.0.0/5',\n  '208.0.0.0/4',\n  '2000::/3',\n];\n\nint? _parseInt(dynamic value) {\n  if (value == null) return null;\n  if (value is num) return value.toInt();\n  if (value is String) return int.tryParse(value);\n  return null;\n}\n\nbool? _parseBool(dynamic value) {\n  if (value == null) return null;\n  if (value is bool) return value;\n  if (value is String) {\n    if (value.toLowerCase() == 'true') return true;\n    if (value.toLowerCase() == 'false') return false;\n  }\n  return null;\n}\n\nList<String>? _parseStringList(dynamic value) {\n  if (value == null) return null;\n  if (value is List) {\n    return value.whereType<String>().toList();\n  }\n  return null;\n}\n\n@freezed\nabstract class ProxyGroup with _$ProxyGroup {\n  const factory ProxyGroup({\n    required String name,\n    @JsonKey(fromJson: GroupType.parseProfileType) required GroupType type,\n    @JsonKey(fromJson: _parseStringList) List<String>? proxies,\n    @JsonKey(fromJson: _parseStringList) List<String>? use,\n    @JsonKey(fromJson: _parseInt) int? interval,\n    @JsonKey(fromJson: _parseBool) bool? lazy,\n    String? url,\n    @JsonKey(fromJson: _parseInt) int? timeout,\n    @JsonKey(name: 'max-failed-times', fromJson: _parseInt) int? maxFailedTimes,\n    String? filter,\n    @JsonKey(name: 'expected-filter') String? excludeFilter,\n    @JsonKey(name: 'exclude-type') String? excludeType,\n    @JsonKey(name: 'expected-status') dynamic expectedStatus,\n    @JsonKey(fromJson: _parseBool) bool? hidden,\n    String? icon,\n    @JsonKey(fromJson: _parseInt) int? tolerance,\n  }) = _ProxyGroup;\n\n  factory ProxyGroup.fromJson(Map<String, Object?> json) =>\n      _$ProxyGroupFromJson(json);\n}\n\n@freezed\nabstract class RuleProvider with _$RuleProvider {\n  const factory RuleProvider({required String name}) = _RuleProvider;\n\n  factory RuleProvider.fromJson(Map<String, Object?> json) =>\n      _$RuleProviderFromJson(json);\n}\n\n@freezed\nabstract class Sniffer with _$Sniffer {\n  const factory Sniffer({\n    @Default(true) bool enable,\n    @Default(false) @JsonKey(name: 'override-destination') bool overrideDest,\n    @Default([]) List<String> sniffing,\n    @Default(['+.v2ex.com'])\n    @JsonKey(name: 'force-domain')\n    List<String> forceDomain,\n    @Default(['192.168.0.3/32'])\n    @JsonKey(name: 'skip-src-address')\n    List<String> skipSrcAddress,\n    @Default([\n      '91.108.56.0/22',\n      '91.108.4.0/22',\n      '91.108.8.0/22',\n      '91.108.16.0/22',\n      '91.108.12.0/22',\n      '149.154.160.0/20',\n      '91.105.192.0/23',\n      '91.108.20.0/22',\n      '185.76.151.0/24',\n      '2001:b28:f23d::/48',\n      '2001:b28:f23f::/48',\n      '2001:67c:4e8::/48',\n      '2001:b28:f23c::/48',\n      '2a0a:f280::/32',\n    ])\n    @JsonKey(name: 'skip-dst-address')\n    List<String> skipDstAddress,\n    @Default(['Mijia Cloud', '+.push.apple.com'])\n    @JsonKey(name: 'skip-domain')\n    List<String> skipDomain,\n    @Default([]) @JsonKey(name: 'port-whitelist') List<String> port,\n    @Default(true) @JsonKey(name: 'force-dns-mapping') bool forceDnsMapping,\n    @Default(true) @JsonKey(name: 'parse-pure-ip') bool parsePureIp,\n    @Default({\n      'HTTP': SnifferConfig(ports: ['80', '8080-8880'], overrideDest: true),\n      'TLS': SnifferConfig(ports: ['443', '8443']),\n      'QUIC': SnifferConfig(ports: ['443', '8443']),\n    })\n    Map<String, SnifferConfig> sniff,\n  }) = _Sniffer;\n\n  factory Sniffer.fromJson(Map<String, Object?> json) =>\n      _$SnifferFromJson(json);\n\n  factory Sniffer.safeSnifferFromJson(Map<String, Object?> json) {\n    try {\n      return Sniffer.fromJson(json);\n    } catch (_) {\n      return const Sniffer();\n    }\n  }\n}\n\nList<String> _formJsonPorts(List? ports) {\n  return ports?.map((item) => item.toString()).toList() ?? [];\n}\n\n@freezed\nabstract class TunnelEntry with _$TunnelEntry {\n  const factory TunnelEntry({\n    required String id,\n    List<String>? network,\n    String? address,\n    String? target,\n    String? proxyName,\n  }) = _TunnelEntry;\n\n  factory TunnelEntry.fromJson(Map<String, Object?> json) =>\n      _$TunnelEntryFromJson(json);\n\n  factory TunnelEntry.fromString(String value) {\n    final id = utils.uuidV4;\n    // Parse simple format: tcp/udp,127.0.0.1:6553,114.114.114.114:53,proxy\n    final parts = value.split(',').map((e) => e.trim()).toList();\n    if (parts.length >= 3) {\n      return TunnelEntry(\n        id: id,\n        network: parts[0].split('/').map((e) => e.trim()).toList(),\n        address: parts[1],\n        target: parts[2],\n        proxyName: parts.length > 3 ? parts[3] : null,\n      );\n    }\n    return TunnelEntry(id: id);\n  }\n}\n\nextension TunnelEntryExt on TunnelEntry {\n  String get displayValue {\n    final parts = <String>[];\n    if (network != null && network!.isNotEmpty) {\n      parts.add(network!.join('/'));\n    }\n    if (address != null && address!.isNotEmpty) {\n      parts.add(address!);\n    }\n    if (target != null && target!.isNotEmpty) {\n      parts.add(target!);\n    }\n    if (proxyName != null && proxyName!.isNotEmpty) {\n      parts.add(proxyName!);\n    }\n    return parts.join(', ');\n  }\n\n  Map<String, dynamic> toClashJson() {\n    final map = <String, dynamic>{};\n    if (network != null && network!.isNotEmpty) {\n      map['network'] = network;\n    }\n    if (address != null && address!.isNotEmpty) {\n      map['address'] = address;\n    }\n    if (target != null && target!.isNotEmpty) {\n      map['target'] = target;\n    }\n    if (proxyName != null && proxyName!.isNotEmpty) {\n      map['proxy'] = proxyName;\n    }\n    return map;\n  }\n}\n\n@freezed\nabstract class SnifferConfig with _$SnifferConfig {\n  const factory SnifferConfig({\n    @Default([]) @JsonKey(fromJson: _formJsonPorts) List<String> ports,\n    @JsonKey(name: 'override-destination') bool? overrideDest,\n  }) = _SnifferConfig;\n\n  factory SnifferConfig.fromJson(Map<String, Object?> json) =>\n      _$SnifferConfigFromJson(json);\n}\n\n@freezed\nabstract class Tun with _$Tun {\n  const factory Tun({\n    @Default(false) bool enable,\n    @Default(tunDeviceName) String device,\n    @JsonKey(name: 'auto-route') @Default(false) bool autoRoute,\n    @Default(TunStack.system) TunStack stack,\n    @JsonKey(name: 'dns-hijack') @Default(['any:53']) List<String> dnsHijack,\n    @JsonKey(name: 'route-address') @Default([]) List<String> routeAddress,\n    @JsonKey(name: 'route-exclude-address')\n    @Default([])\n    List<String> routeExcludeAddress,\n    @JsonKey(name: 'strict-route') @Default(false) bool strictRoute,\n    @JsonKey(name: 'disable-icmp-forwarding')\n    @Default(true)\n    bool disableIcmpForwarding,\n    @Default(4064) int mtu,\n    @JsonKey(name: 'endpoint-independent-nat')\n    @Default(false)\n    bool endpointIndependentNat,\n  }) = _Tun;\n\n  factory Tun.fromJson(Map<String, Object?> json) => _$TunFromJson(json);\n\n  factory Tun.safeFormJson(Map<String, Object?>? json) {\n    if (json == null) {\n      return defaultTun;\n    }\n    try {\n      return Tun.fromJson(json);\n    } catch (_) {\n      return defaultTun;\n    }\n  }\n}\n\nextension TunExt on Tun {\n  Tun getRealTun(\n    bool bypassPrivateRoute, {\n    String? fakeIpRange,\n    String? fakeIpRangeV6,\n  }) {\n    if (system.isDesktop) {\n      if (bypassPrivateRoute) {\n        return copyWith(\n          autoRoute: true,\n          routeAddress: [],\n          routeExcludeAddress: [\n            '127.0.0.0/8',\n            '::1/128',\n            '10.0.0.0/8',\n            '172.16.0.0/12',\n            '192.168.0.0/16',\n            '169.254.0.0/16',\n            'fd00::/8',\n            'fe80::/10',\n          ],\n        );\n      }\n      return copyWith(\n        autoRoute: true,\n        routeAddress: [],\n        routeExcludeAddress: [],\n      );\n    }\n\n    if (bypassPrivateRoute) {\n      return copyWith(\n        autoRoute: true,\n        routeAddress: List<String>.from(defaultBypassPrivateRouteAddress),\n      );\n    }\n\n    return copyWith(\n      autoRoute: true,\n      routeAddress: [],\n    );\n  }\n}\n\n@freezed\nabstract class FallbackFilter with _$FallbackFilter {\n  const factory FallbackFilter({\n    @Default(false) bool geoip,\n    @Default('CN') @JsonKey(name: 'geoip-code') String geoipCode,\n    @Default([]) List<String> geosite,\n    @Default([]) List<String> ipcidr,\n    @Default([]) List<String> domain,\n  }) = _FallbackFilter;\n  factory FallbackFilter.fromJson(Map<String, Object?> json) =>\n      _$FallbackFilterFromJson(json);\n}\n\n@freezed\nabstract class Dns with _$Dns {\n  const factory Dns({\n    @Default(true) bool enable,\n    @Default('0.0.0.0:1053') String listen,\n    @Default(false) @JsonKey(name: 'prefer-h3') bool preferH3,\n    @Default(CacheAlgorithm.arc)\n    @JsonKey(name: 'cache-algorithm')\n    CacheAlgorithm cacheAlgorithm,\n    @Default(true) @JsonKey(name: 'use-hosts') bool useHosts,\n    @Default(true) @JsonKey(name: 'use-system-hosts') bool useSystemHosts,\n    @Default(false) @JsonKey(name: 'respect-rules') bool respectRules,\n    @Default(false) bool ipv6,\n    @Default(['114.114.114.114'])\n    @JsonKey(name: 'default-nameserver')\n    List<String> defaultNameserver,\n    @Default(DnsMode.fakeIp)\n    @JsonKey(name: 'enhanced-mode')\n    DnsMode enhancedMode,\n    @Default('198.18.0.1/15')\n    @JsonKey(name: 'fake-ip-range')\n    String fakeIpRange,\n    @Default('fc00::/18')\n    @JsonKey(name: 'fake-ip-range-v6')\n    String fakeIpRangeV6,\n    @Default(FilterMode.blacklist)\n    @JsonKey(name: 'fake-ip-filter-mode')\n    FilterMode fakeIpFilterMode,\n    @Default([\n      '*',\n      'geosite:private',\n      'geosite:category-ntp',\n      'geosite:geolocation-cn',\n      'geosite:connectivity-check',\n    ])\n    @JsonKey(name: 'fake-ip-filter')\n    List<String> fakeIpFilter,\n    @Default(1) @JsonKey(name: 'fake-ip-ttl') int fakeIpTtl,\n    @Default({\n      '+.internal.crop.com': '10.0.0.1',\n      'geosite:cn': '119.29.29.29',\n      'geosite:private': 'system',\n      '*': 'system',\n    })\n    @JsonKey(name: 'nameserver-policy')\n    Map<String, String> nameserverPolicy,\n    @Default(['1.1.1.1']) List<String> nameserver,\n    @Default([]) List<String> fallback,\n    @Default(['https://doh.pub/dns-query#DIRECT'])\n    @JsonKey(name: 'proxy-server-nameserver')\n    List<String> proxyServerNameserver,\n    @Default([])\n    @JsonKey(name: 'direct-nameserver')\n    List<String> directNameserver,\n    @Default(false)\n    @JsonKey(name: 'direct-nameserver-follow-policy')\n    bool directNameserverFollowPolicy,\n    @Default(FallbackFilter())\n    @JsonKey(name: 'fallback-filter')\n    FallbackFilter fallbackFilter,\n  }) = _Dns;\n\n  factory Dns.fromJson(Map<String, Object?> json) => _$DnsFromJson(json);\n\n  factory Dns.safeDnsFromJson(Map<String, Object?> json) {\n    try {\n      return Dns.fromJson(json);\n    } catch (_) {\n      return const Dns();\n    }\n  }\n}\n\n@freezed\nabstract class Ntp with _$Ntp {\n  const factory Ntp({\n    @Default(true) bool enable,\n    @Default(false) @JsonKey(name: 'write-to-system') bool writeToSystem,\n    @Default('ntp.aliyun.com') String server,\n    @Default(123) int port,\n    @Default(60) int interval,\n  }) = _Ntp;\n\n  factory Ntp.fromJson(Map<String, Object?> json) => _$NtpFromJson(json);\n\n  factory Ntp.safeNtpFromJson(Map<String, Object?> json) {\n    try {\n      return Ntp.fromJson(json);\n    } catch (_) {\n      return const Ntp();\n    }\n  }\n}\n\n@freezed\nabstract class Experimental with _$Experimental {\n  const factory Experimental({\n    @Default(true) @JsonKey(name: 'quic-go-disable-gso') bool quicGoDisableGso,\n    @Default(true) @JsonKey(name: 'quic-go-disable-ecn') bool quicGoDisableEcn,\n    @Default(false)\n    @JsonKey(name: 'dialer-ip4p-convert')\n    bool dialerIp4pConvert,\n  }) = _Experimental;\n\n  factory Experimental.fromJson(Map<String, Object?> json) =>\n      _$ExperimentalFromJson(json);\n\n  factory Experimental.safeExperimentalFromJson(Map<String, Object?> json) {\n    try {\n      return Experimental.fromJson(json);\n    } catch (_) {\n      return const Experimental();\n    }\n  }\n}\n\n@freezed\nabstract class GeoXUrl with _$GeoXUrl {\n  const factory GeoXUrl({\n    @Default(\n      'https://fastly.jsdelivr.net/gh/appshubcc/bett-rules@release/geoip.metadb',\n    )\n    String mmdb,\n    @Default(\n      'https://fastly.jsdelivr.net/gh/appshubcc/bett-rules@release/GeoLite2-ASN.mmdb',\n    )\n    String asn,\n    @Default(\n      'https://fastly.jsdelivr.net/gh/appshubcc/bett-rules@release/geoip.dat',\n    )\n    String geoip,\n    @Default(\n      'https://fastly.jsdelivr.net/gh/appshubcc/bett-rules@release/geosite.dat',\n    )\n    String geosite,\n  }) = _GeoXUrl;\n\n  factory GeoXUrl.fromJson(Map<String, Object?> json) =>\n      _$GeoXUrlFromJson(json);\n\n  factory GeoXUrl.safeFormJson(Map<String, Object?>? json) {\n    if (json == null) {\n      return defaultGeoXUrl;\n    }\n    try {\n      return GeoXUrl.fromJson(json);\n    } catch (_) {\n      return defaultGeoXUrl;\n    }\n  }\n}\n\n@freezed\nabstract class ParsedRule with _$ParsedRule {\n  const factory ParsedRule({\n    required RuleAction ruleAction,\n    String? content,\n    String? ruleTarget,\n    String? ruleProvider,\n    String? subRule,\n    @Default(false) bool noResolve,\n    @Default(false) bool src,\n  }) = _ParsedRule;\n\n  factory ParsedRule.parseString(String value) {\n    final splits = value.split(',');\n    final shortSplits = splits\n        .where((item) => !item.contains('src') && !item.contains('no-resolve'))\n        .toList();\n    final ruleAction = RuleAction.values.firstWhere(\n      (item) => item.value == shortSplits.first,\n      orElse: () => RuleAction.DOMAIN,\n    );\n    String? subRule;\n    String? ruleTarget;\n\n    if (ruleAction == RuleAction.SUB_RULE) {\n      subRule = shortSplits.last;\n    } else {\n      ruleTarget = shortSplits.last;\n    }\n\n    String? content;\n    String? ruleProvider;\n\n    if (ruleAction == RuleAction.RULE_SET) {\n      ruleProvider = shortSplits.sublist(1, shortSplits.length - 1).join(',');\n    } else {\n      content = shortSplits.sublist(1, shortSplits.length - 1).join(',');\n    }\n\n    return ParsedRule(\n      ruleAction: ruleAction,\n      content: content,\n      src: splits.contains('src'),\n      ruleProvider: ruleProvider,\n      noResolve: splits.contains('no-resolve'),\n      subRule: subRule,\n      ruleTarget: ruleTarget,\n    );\n  }\n}\n\nextension ParsedRuleExt on ParsedRule {\n  String get value {\n    if (ruleAction == RuleAction.MATCH) {\n      return [\n        ruleAction.value,\n        ruleTarget,\n      ].join(',');\n    }\n    return [\n      ruleAction.value,\n      ruleAction == RuleAction.RULE_SET ? ruleProvider : content,\n      ruleAction == RuleAction.SUB_RULE ? subRule : ruleTarget,\n      if (ruleAction.hasParams) ...[\n        if (src) 'src',\n        if (noResolve) 'no-resolve',\n      ],\n    ].join(',');\n  }\n}\n\n@freezed\nabstract class Rule with _$Rule {\n  const factory Rule({required String id, required String value}) = _Rule;\n\n  factory Rule.value(String value) {\n    return Rule(value: value, id: utils.uuidV4);\n  }\n\n  factory Rule.fromJson(Map<String, Object?> json) => _$RuleFromJson(json);\n}\n\n@freezed\nabstract class SubRule with _$SubRule {\n  const factory SubRule({required String name}) = _SubRule;\n\n  factory SubRule.fromJson(Map<String, Object?> json) =>\n      _$SubRuleFromJson(json);\n}\n\nList<Rule> _genRule(List<dynamic>? rules) {\n  if (rules == null) {\n    return [];\n  }\n  return rules.map((item) => Rule.value(item)).toList();\n}\n\nList<RuleProvider> _genRuleProviders(Map<String, dynamic> json) {\n  return json.entries.map((entry) => RuleProvider(name: entry.key)).toList();\n}\n\nList<SubRule> _genSubRules(Map<String, dynamic> json) {\n  return json.entries.map((entry) => SubRule(name: entry.key)).toList();\n}\n\n@freezed\nabstract class ClashConfigSnippet with _$ClashConfigSnippet {\n  const factory ClashConfigSnippet({\n    @Default([]) @JsonKey(name: 'proxy-groups') List<ProxyGroup> proxyGroups,\n    @JsonKey(fromJson: _genRule, name: 'rules') @Default([]) List<Rule> rule,\n    @JsonKey(name: 'rule-providers', fromJson: _genRuleProviders)\n    @Default([])\n    List<RuleProvider> ruleProvider,\n    @JsonKey(name: 'sub-rules', fromJson: _genSubRules)\n    @Default([])\n    List<SubRule> subRules,\n  }) = _ClashConfigSnippet;\n\n  factory ClashConfigSnippet.fromJson(Map<String, Object?> json) =>\n      _$ClashConfigSnippetFromJson(json);\n}\n\n@freezed\nabstract class ClashConfig with _$ClashConfig {\n  const factory ClashConfig({\n    @Default(defaultMixedPort) @JsonKey(name: 'mixed-port') int mixedPort,\n    @Default(0) @JsonKey(name: 'socks-port') int socksPort,\n    @Default(0) @JsonKey(name: 'port') int port,\n    @Default(0) @JsonKey(name: 'redir-port') int redirPort,\n    @Default(0) @JsonKey(name: 'tproxy-port') int tproxyPort,\n    @Default(Mode.rule) Mode mode,\n    @Default(false) @JsonKey(name: 'allow-lan') bool allowLan,\n    @Default(LogLevel.error) @JsonKey(name: 'log-level') LogLevel logLevel,\n    @Default(false) bool ipv6,\n    @Default(FindProcessMode.off)\n    @JsonKey(\n      name: 'find-process-mode',\n      unknownEnumValue: FindProcessMode.always,\n    )\n    FindProcessMode findProcessMode,\n    @Default(defaultKeepAliveInterval)\n    @JsonKey(name: 'keep-alive-interval')\n    int keepAliveInterval,\n    @Default(true) @JsonKey(name: 'unified-delay') bool unifiedDelay,\n    @Default(true) @JsonKey(name: 'tcp-concurrent') bool tcpConcurrent,\n    @Default(defaultTun) @JsonKey(fromJson: Tun.safeFormJson) Tun tun,\n    @Default(defaultDns) @JsonKey(fromJson: Dns.safeDnsFromJson) Dns dns,\n    @Default(defaultNtp) @JsonKey(fromJson: Ntp.safeNtpFromJson) Ntp ntp,\n    @Default(defaultSniffer)\n    @JsonKey(fromJson: Sniffer.safeSnifferFromJson)\n    Sniffer sniffer,\n    @Default(defaultTunnel) List<TunnelEntry> tunnels,\n    @Default(defaultExperimental)\n    @JsonKey(fromJson: Experimental.safeExperimentalFromJson)\n    Experimental experimental,\n    @Default(defaultGeoXUrl)\n    @JsonKey(name: 'geox-url', fromJson: GeoXUrl.safeFormJson)\n    GeoXUrl geoXUrl,\n    @Default(GeodataLoader.memconservative)\n    @JsonKey(name: 'geodata-loader')\n    GeodataLoader geodataLoader,\n    @Default([]) @JsonKey(name: 'proxy-groups') List<ProxyGroup> proxyGroups,\n    @Default([]) List<String> rule,\n    @JsonKey(name: 'global-ua') String? globalUa,\n    @Default(ExternalControllerStatus.close)\n    @JsonKey(name: 'external-controller')\n    ExternalControllerStatus externalController,\n    String? secret,\n    @JsonKey(name: 'external-ui-name') String? externalUiName,\n    @JsonKey(name: 'external-ui-url') String? externalUiUrl,\n    @Default({}) HostsMap hosts,\n  }) = _ClashConfig;\n\n  factory ClashConfig.fromJson(Map<String, Object?> json) =>\n      _$ClashConfigFromJson(json);\n\n  factory ClashConfig.safeFormJson(Map<String, Object?>? json) {\n    if (json == null) {\n      return defaultClashConfig;\n    }\n    try {\n      return ClashConfig.fromJson(json);\n    } catch (_) {\n      return defaultClashConfig;\n    }\n  }\n}\n"
  },
  {
    "path": "lib/models/common.dart",
    "content": "// ignore_for_file: invalid_annotation_target\n\nimport 'dart:math';\n\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:flutter/material.dart';\nimport 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'generated/common.freezed.dart';\npart 'generated/common.g.dart';\n\n@freezed\nabstract class NavigationItem with _$NavigationItem {\n  const factory NavigationItem({\n    required Icon icon,\n    required PageLabel label,\n    final String? description,\n    required WidgetBuilder builder,\n    @Default(true) bool keep,\n    String? path,\n    @Default([NavigationItemMode.mobile, NavigationItemMode.desktop])\n    List<NavigationItemMode> modes,\n  }) = _NavigationItem;\n}\n\n@freezed\nabstract class Package with _$Package {\n  const factory Package({\n    required String packageName,\n    required String label,\n    required bool system,\n    required bool internet,\n    required int lastUpdateTime,\n  }) = _Package;\n\n  factory Package.fromJson(Map<String, Object?> json) =>\n      _$PackageFromJson(json);\n}\n\n@freezed\nabstract class Metadata with _$Metadata {\n  const factory Metadata({\n    @Default(0) int uid,\n    @Default('') String network,\n    @Default('') String sourceIP,\n    @Default('') String sourcePort,\n    @Default('') String destinationIP,\n    @Default('') String destinationPort,\n    @Default('') String host,\n    DnsMode? dnsMode,\n    @Default('') String process,\n    @Default('') String processPath,\n    @Default('') String remoteDestination,\n    @Default([]) List<String> sourceGeoIP,\n    @Default([]) List<String> destinationGeoIP,\n    @Default('') String destinationIPASN,\n    @Default('') String sourceIPASN,\n    @Default('') String specialRules,\n    @Default('') String specialProxy,\n  }) = _Metadata;\n\n  factory Metadata.fromJson(Map<String, Object?> json) =>\n      _$MetadataFromJson(json);\n}\n\n@freezed\nabstract class TrackerInfo with _$TrackerInfo {\n  const factory TrackerInfo({\n    required String id,\n    @Default(0) int upload,\n    @Default(0) int download,\n    required DateTime start,\n    required Metadata metadata,\n    required List<String> chains,\n    required String rule,\n    required String rulePayload,\n    int? downloadSpeed,\n    int? uploadSpeed,\n  }) = _TrackerInfo;\n\n  factory TrackerInfo.fromJson(Map<String, Object?> json) =>\n      _$TrackerInfoFromJson(json);\n}\n\nextension TrackerInfoExt on TrackerInfo {\n  String get desc {\n    var text = '${metadata.network}://';\n    final ips = [\n      metadata.host,\n      metadata.destinationIP,\n    ].where((ip) => ip.isNotEmpty);\n    text += ips.join('/');\n    text += ':${metadata.destinationPort}';\n    return text;\n  }\n\n  String get progressText {\n    final process = metadata.process;\n    final uid = metadata.uid;\n    if (uid != 0) {\n      return '$process($uid)';\n    }\n    return process;\n  }\n}\n\nString _logDateTime(dynamic _) {\n  return DateTime.now().showFull;\n}\n\n// String _logId(_) {\n//   return utils.id;\n// }\n\n@freezed\nabstract class Log with _$Log {\n  const factory Log({\n    // @JsonKey(fromJson: _logId) required String id,\n    @JsonKey(name: 'LogLevel') @Default(LogLevel.error) LogLevel logLevel,\n    @JsonKey(name: 'Payload') @Default('') String payload,\n    @JsonKey(fromJson: _logDateTime) required String dateTime,\n  }) = _Log;\n\n  factory Log.app(String payload) {\n    return Log(\n      logLevel: LogLevel.info,\n      payload: payload,\n      dateTime: _logDateTime(null),\n      // id: _logId(null),\n    );\n  }\n\n  factory Log.fromJson(Map<String, Object?> json) => _$LogFromJson(json);\n}\n\n@freezed\nabstract class LogsState with _$LogsState {\n  const factory LogsState({\n    @Default([]) List<Log> logs,\n    @Default([]) List<String> keywords,\n    @Default('') String query,\n    @Default(false) bool autoScrollToEnd,\n  }) = _LogsState;\n}\n\nextension LogsStateExt on LogsState {\n  List<Log> get list {\n    final lowQuery = query.toLowerCase();\n    return logs.where((log) {\n      final logLevelName = log.logLevel.name;\n      return {logLevelName}.containsAll(keywords) &&\n          ((log.payload.toLowerCase().contains(lowQuery)) ||\n              logLevelName.contains(lowQuery));\n    }).toList();\n  }\n}\n\n@freezed\nabstract class TrackerInfosState with _$TrackerInfosState {\n  const factory TrackerInfosState({\n    @Default([]) List<TrackerInfo> trackerInfos,\n    @Default([]) List<String> keywords,\n    @Default('') String query,\n    @Default(false) bool autoScrollToEnd,\n  }) = _TrackerInfosState;\n}\n\nextension TrackerInfosStateExt on TrackerInfosState {\n  List<TrackerInfo> get list {\n    final lowerQuery = query.toLowerCase().trim();\n    final lowQuery = query.toLowerCase();\n    return trackerInfos.where((trackerInfo) {\n      final chains = trackerInfo.chains;\n      final process = trackerInfo.metadata.process;\n      final networkText = trackerInfo.metadata.network.toLowerCase();\n      final hostText = trackerInfo.metadata.host.toLowerCase();\n      final destinationIPText = trackerInfo.metadata.destinationIP\n          .toLowerCase();\n      final processText = trackerInfo.metadata.process.toLowerCase();\n      final chainsText = chains.join('').toLowerCase();\n      return {...chains, process}.containsAll(keywords) &&\n          (networkText.contains(lowerQuery) ||\n              hostText.contains(lowerQuery) ||\n              destinationIPText.contains(lowQuery) ||\n              processText.contains(lowerQuery) ||\n              chainsText.contains(lowerQuery));\n    }).toList();\n  }\n}\n\nconst defaultDavFileName = 'backup.zip';\n\n@freezed\nabstract class DAV with _$DAV {\n  const factory DAV({\n    required String uri,\n    required String user,\n    required String password,\n    @Default(defaultDavFileName) String fileName,\n  }) = _DAV;\n\n  factory DAV.fromJson(Map<String, Object?> json) => _$DAVFromJson(json);\n}\n\n@freezed\nabstract class FileInfo with _$FileInfo {\n  const factory FileInfo({required int size, required DateTime lastModified}) =\n      _FileInfo;\n}\n\nextension FileInfoExt on FileInfo {\n  String get desc =>\n      '${TrafficValue(value: size).show}  ·  ${lastModified.lastUpdateTimeDesc}';\n}\n\n@freezed\nabstract class VersionInfo with _$VersionInfo {\n  const factory VersionInfo({\n    @Default('') String clashName,\n    @Default('') String version,\n  }) = _VersionInfo;\n\n  factory VersionInfo.fromJson(Map<String, Object?> json) =>\n      _$VersionInfoFromJson(json);\n}\n\nclass Traffic {\n  int id;\n  TrafficValue up;\n  TrafficValue down;\n\n  Traffic({int? up, int? down})\n    : id = DateTime.now().millisecondsSinceEpoch,\n      up = TrafficValue(value: up),\n      down = TrafficValue(value: down);\n\n  num get speed => up.value + down.value;\n\n  factory Traffic.fromMap(Map<String, dynamic> map) {\n    return Traffic(up: map['up'], down: map['down']);\n  }\n\n  String toSpeedText() {\n    return '↑ $up/s   ↓ $down/s';\n  }\n\n  @override\n  String toString() {\n    return '$up↑ $down↓';\n  }\n\n  @override\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is Traffic &&\n          runtimeType == other.runtimeType &&\n          id == other.id &&\n          up == other.up &&\n          down == other.down;\n\n  @override\n  int get hashCode => id.hashCode ^ up.hashCode ^ down.hashCode;\n}\n\n@immutable\nclass TrafficValueShow {\n  final double value;\n  final TrafficUnit unit;\n\n  const TrafficValueShow({required this.value, required this.unit});\n}\n\n@freezed\nabstract class Proxy with _$Proxy {\n  const factory Proxy({\n    required String name,\n    required String type,\n    String? now,\n  }) = _Proxy;\n\n  factory Proxy.fromJson(Map<String, Object?> json) => _$ProxyFromJson(json);\n}\n\n@freezed\nabstract class Group with _$Group {\n  const factory Group({\n    required GroupType type,\n    @Default([]) List<Proxy> all,\n    String? now,\n    bool? hidden,\n    String? testUrl,\n    @Default('') String icon,\n    required String name,\n  }) = _Group;\n\n  factory Group.fromJson(Map<String, Object?> json) => _$GroupFromJson(json);\n}\n\nextension GroupsExt on List<Group> {\n  Group? getGroup(String groupName) {\n    final index = indexWhere((element) => element.name == groupName);\n    return index != -1 ? this[index] : null;\n  }\n}\n\nextension GroupExt on Group {\n  String get realNow => now ?? '';\n\n  String getCurrentSelectedName(String proxyName) {\n    if (type.isComputedSelected) {\n      return realNow.isNotEmpty ? realNow : proxyName;\n    }\n    return proxyName.isNotEmpty ? proxyName : realNow;\n  }\n}\n\n@immutable\nclass TrafficValue {\n  final int _value;\n\n  const TrafficValue({int? value}) : _value = value ?? 0;\n\n  int get value => _value;\n\n  String get show => '$showValue $showUnit';\n\n  String get shortShow =>\n      '${trafficValueShow.value.fixed(decimals: 1)} $showUnit';\n\n  String get showValue => trafficValueShow.value.fixed();\n\n  String get showUnit => trafficValueShow.unit.name;\n\n  TrafficValueShow get trafficValueShow {\n    if (_value > pow(1024, 4)) {\n      return TrafficValueShow(\n        value: _value / pow(1024, 4),\n        unit: TrafficUnit.TB,\n      );\n    }\n    if (_value > pow(1024, 3)) {\n      return TrafficValueShow(\n        value: _value / pow(1024, 3),\n        unit: TrafficUnit.GB,\n      );\n    }\n    if (_value > pow(1024, 2)) {\n      return TrafficValueShow(\n        value: _value / pow(1024, 2),\n        unit: TrafficUnit.MB,\n      );\n    }\n    if (_value > pow(1024, 1)) {\n      return TrafficValueShow(\n        value: _value / pow(1024, 1),\n        unit: TrafficUnit.KB,\n      );\n    }\n    return TrafficValueShow(value: _value.toDouble(), unit: TrafficUnit.B);\n  }\n\n  @override\n  String toString() {\n    return '$showValue$showUnit';\n  }\n\n  @override\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is TrafficValue &&\n          runtimeType == other.runtimeType &&\n          _value == other._value;\n\n  @override\n  int get hashCode => _value.hashCode;\n}\n\n@freezed\nabstract class ColorSchemes with _$ColorSchemes {\n  const factory ColorSchemes({\n    ColorScheme? lightColorScheme,\n    ColorScheme? darkColorScheme,\n  }) = _ColorSchemes;\n}\n\nextension ColorSchemesExt on ColorSchemes {\n  ColorScheme getColorSchemeForBrightness(\n    Brightness brightness,\n    DynamicSchemeVariant schemeVariant,\n  ) {\n    if (brightness == Brightness.dark) {\n      return darkColorScheme != null\n          ? ColorScheme.fromSeed(\n              seedColor: darkColorScheme!.primary,\n              brightness: Brightness.dark,\n              dynamicSchemeVariant: schemeVariant,\n            )\n          : ColorScheme.fromSeed(\n              seedColor: Color(defaultPrimaryColor),\n              brightness: Brightness.dark,\n              dynamicSchemeVariant: schemeVariant,\n            );\n    }\n    return lightColorScheme != null\n        ? ColorScheme.fromSeed(\n            seedColor: lightColorScheme!.primary,\n            dynamicSchemeVariant: schemeVariant,\n          )\n        : ColorScheme.fromSeed(\n            seedColor: Color(defaultPrimaryColor),\n            dynamicSchemeVariant: schemeVariant,\n          );\n  }\n}\n\nclass IpInfo {\n  final String ip;\n  final String countryCode;\n\n  const IpInfo({required this.ip, required this.countryCode});\n\n  static IpInfo fromCloudflareTrace(String traceText) {\n    // Cloudflare trace格式示例:\n    // fl=...\n    // h=...\n    // ip=1.2.3.4\n    // ts=...\n    // visit_scheme=https\n    // uag=...\n    // colo=...\n    // sliver=none\n    // http=http/2\n    // loc=US\n    // tls=TLSv1.3\n    // sni=plaintext\n    // warp=off\n    // gateway=off\n    // rbi=off\n    // kex=X25519\n\n    final lines = traceText.split('\\n');\n    String? ip;\n    String? countryCode;\n\n    for (final line in lines) {\n      final parts = line.split('=');\n      if (parts.length == 2) {\n        final key = parts[0].trim();\n        final value = parts[1].trim();\n\n        if (key == 'ip') {\n          ip = value;\n        } else if (key == 'loc') {\n          countryCode = value;\n        }\n      }\n    }\n\n    if (ip != null && countryCode != null) {\n      return IpInfo(ip: ip, countryCode: countryCode);\n    }\n\n    throw const FormatException('invalid cloudflare trace format');\n  }\n\n  IpInfo copyWith({String? ip, String? countryCode}) {\n    return IpInfo(\n      ip: ip ?? this.ip,\n      countryCode: countryCode ?? this.countryCode,\n    );\n  }\n\n  @override\n  String toString() {\n    return 'IpInfo{ip: $ip, countryCode: $countryCode}';\n  }\n}\n\n@freezed\nabstract class HotKeyAction with _$HotKeyAction {\n  const factory HotKeyAction({\n    required HotAction action,\n    int? key,\n    @Default({}) Set<KeyboardModifier> modifiers,\n  }) = _HotKeyAction;\n\n  factory HotKeyAction.fromJson(Map<String, Object?> json) =>\n      _$HotKeyActionFromJson(json);\n}\n\ntypedef Validator = String? Function(String? value);\n\n@freezed\nabstract class Field with _$Field {\n  const factory Field({\n    required String label,\n    required String value,\n    Validator? validator,\n  }) = _Field;\n}\n\nenum PopupMenuItemType { primary, danger }\n\nclass PopupMenuItemData {\n  const PopupMenuItemData({\n    this.icon,\n    required this.label,\n    required this.onPressed,\n  });\n\n  final String label;\n  final VoidCallback? onPressed;\n  final IconData? icon;\n}\n\n@freezed\nabstract class TextPainterParams with _$TextPainterParams {\n  const factory TextPainterParams({\n    required String? text,\n    required double? fontSize,\n    required double textScaleFactor,\n    @Default(double.infinity) double maxWidth,\n    int? maxLines,\n  }) = _TextPainterParams;\n\n  factory TextPainterParams.fromJson(Map<String, Object?> json) =>\n      _$TextPainterParamsFromJson(json);\n}\n\nclass CloseWindowIntent extends Intent {\n  const CloseWindowIntent();\n}\n\n@freezed\nabstract class Result<T> with _$Result<T> {\n  const factory Result({\n    required T? data,\n    required ResultType type,\n    required String message,\n    @Default(false) bool needRestart,\n  }) = _Result;\n\n  factory Result.success(T data, {bool needRestart = false}) =>\n      Result(data: data, type: ResultType.success, message: '', needRestart: needRestart);\n\n  factory Result.error(String message) =>\n      Result(data: null, type: ResultType.error, message: message);\n}\n\nextension ResultExt on Result {\n  bool get isError => type == ResultType.error;\n\n  bool get isSuccess => type == ResultType.success;\n}\n\n@freezed\nabstract class Script with _$Script {\n  const factory Script({\n    required String id,\n    required String label,\n    required String content,\n    String? url,\n  }) = _Script;\n\n  factory Script.create({required String label, required String content, String? url}) {\n    return Script(id: utils.uuidV4, label: label, content: content, url: url);\n  }\n\n  factory Script.fromJson(Map<String, Object?> json) => _$ScriptFromJson(json);\n}\n"
  },
  {
    "path": "lib/models/config.dart",
    "content": "// ignore_for_file: invalid_annotation_target\n\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:flutter/material.dart';\nimport 'package:freezed_annotation/freezed_annotation.dart';\n\nimport 'models.dart';\n\npart 'generated/config.freezed.dart';\npart 'generated/config.g.dart';\n\nconst defaultBypassDomain = [\n  '*jd.com',\n  '*zhihu.com',\n  '*zhimg.com',\n  '*360buyimg.com',\n  'localhost',\n  '*.local',\n  '127.*',\n  '10.*',\n  '172.16.*',\n  '172.17.*',\n  '172.18.*',\n  '172.19.*',\n  '172.2*',\n  '172.30.*',\n  '172.31.*',\n  '192.168.*',\n];\n\nconst defaultAppSettingProps = AppSettingProps();\nconst defaultVpnProps = VpnProps();\nconst defaultNetworkProps = NetworkProps();\nconst defaultProxiesStyle = ProxiesStyle();\nconst defaultWindowProps = WindowProps();\nconst defaultAccessControl = AccessControl();\nfinal defaultThemeProps = ThemeProps(primaryColor: defaultPrimaryColor);\n\nconst List<DashboardWidget> defaultDashboardWidgets = [\n  DashboardWidget.networkSpeed,\n  DashboardWidget.systemProxyButton,\n  DashboardWidget.tunButton,\n  DashboardWidget.outboundMode,\n  DashboardWidget.networkDetection,\n  DashboardWidget.trafficUsage,\n  DashboardWidget.intranetIp,\n  DashboardWidget.memoryInfo,\n  DashboardWidget.startButton,\n];\n\nList<DashboardWidget> dashboardWidgetsSafeFormJson(\n  List<dynamic>? dashboardWidgets,\n) {\n  try {\n    return dashboardWidgets\n            ?.map((e) => $enumDecode(_$DashboardWidgetEnumMap, e))\n            .toList() ??\n        defaultDashboardWidgets;\n  } catch (_) {\n    return defaultDashboardWidgets;\n  }\n}\n\n@freezed\nabstract class AppSettingProps with _$AppSettingProps {\n  const factory AppSettingProps({\n    String? locale,\n    @Default(defaultDashboardWidgets)\n    @JsonKey(fromJson: dashboardWidgetsSafeFormJson)\n    List<DashboardWidget> dashboardWidgets,\n    @Default(false) bool onlyStatisticsProxy,\n    @Default(false) bool autoLaunch,\n    @Default(false) bool silentLaunch,\n    @Default(true) bool smartDelayLaunch,\n    @Default(false) bool autoRun,\n    @Default(true) bool openLogs,\n    @Default(true) bool closeConnections,\n    @Default(defaultTestUrl) String testUrl,\n    @Default(true) bool isAnimateToPage,\n    @Default(false) bool enableNavBarHapticFeedback,\n    @Default(true) bool autoCheckUpdate,\n    @Default(false) bool showLabel,\n    @Default(false) bool disclaimerAccepted,\n    @Default(true) bool minimizeOnExit,\n    @Default(false) bool hidden,\n    @Default(false) bool developerMode,\n    @Default(false) bool enableHighRefreshRate,\n    @Default(RecoveryStrategy.compatible) RecoveryStrategy recoveryStrategy,\n  }) = _AppSettingProps;\n\n  factory AppSettingProps.fromJson(Map<String, Object?> json) =>\n      _$AppSettingPropsFromJson(json);\n\n  factory AppSettingProps.safeFromJson(Map<String, Object?>? json) {\n    final props = json == null\n        ? defaultAppSettingProps\n        : AppSettingProps.fromJson(json);\n\n    return props.copyWith(\n      minimizeOnExit: true,\n      openLogs: true,\n    );\n  }\n}\n\n@freezed\nabstract class AccessControl with _$AccessControl {\n  const factory AccessControl({\n    @Default(false) bool enable,\n    @Default(AccessControlMode.rejectSelected) AccessControlMode mode,\n    @Default([]) List<String> acceptList,\n    @Default([]) List<String> rejectList,\n    @Default(AccessSortType.none) AccessSortType sort,\n    @Default(true) bool isFilterSystemApp,\n    @Default(true) bool isFilterNonInternetApp,\n  }) = _AccessControl;\n\n  factory AccessControl.fromJson(Map<String, Object?> json) =>\n      _$AccessControlFromJson(json);\n}\n\nextension AccessControlExt on AccessControl {\n  List<String> get currentList => switch (mode) {\n    AccessControlMode.acceptSelected => acceptList,\n    AccessControlMode.rejectSelected => rejectList,\n  };\n}\n\n@freezed\nabstract class WindowProps with _$WindowProps {\n  const factory WindowProps({\n    @Default(750) double width,\n    @Default(600) double height,\n    double? top,\n    double? left,\n    @Default(false) bool isLocked,\n  }) = _WindowProps;\n\n  factory WindowProps.fromJson(Map<String, Object?>? json) =>\n      json == null ? const WindowProps() : _$WindowPropsFromJson(json);\n}\n\n@freezed\nabstract class VpnProps with _$VpnProps {\n  const factory VpnProps({\n    @Default(true) bool enable,\n    @Default(false) bool systemProxy,\n    @Default(false) bool allowBypass,\n    @Default(true) bool bypassPrivateRoute,\n    @Default(true) bool dozeSuspend,\n    @Default(false) bool smartAutoStop,\n    @Default('') String smartAutoStopNetworks,\n    @Default(false) bool storeFix,\n    @Default(false) bool networkFix,\n    @Default(false) bool disableQuic,\n    @Default(false) bool excludeChina,\n    @Default(false) bool fcmOptimization,\n    @Default(false) bool quickResponse,\n    @Default(defaultAccessControl) AccessControl accessControl,\n  }) = _VpnProps;\n\n  factory VpnProps.fromJson(Map<String, Object?> json) =>\n      _$VpnPropsFromJson(json);\n\n  factory VpnProps.safeFromJson(Map<String, Object?>? json) {\n    final props = json == null ? defaultVpnProps : VpnProps.fromJson(json);\n    var safeProps = props;\n\n    if (system.isAndroid) {\n      safeProps = safeProps.copyWith(systemProxy: false);\n    }\n\n    if (safeProps.fcmOptimization && system.isAndroid) {\n      safeProps = safeProps.copyWith(allowBypass: false);\n    }\n\n    if (safeProps.smartAutoStop && safeProps.quickResponse) {\n      safeProps = safeProps.copyWith(quickResponse: false);\n    }\n\n    return safeProps;\n  }\n}\n\n@freezed\nabstract class NetworkProps with _$NetworkProps {\n  const factory NetworkProps({\n    @Default(false) bool systemProxy,\n    @Default(defaultBypassDomain) List<String> bypassDomain,\n    @Default(true) bool bypassPrivateRoute,\n    @Default(true) bool autoSetSystemDns,\n  }) = _NetworkProps;\n\n  factory NetworkProps.fromJson(Map<String, Object?>? json) =>\n      json == null ? const NetworkProps() : _$NetworkPropsFromJson(json);\n}\n\n@freezed\nabstract class ProxiesStyle with _$ProxiesStyle {\n  const factory ProxiesStyle({\n    @Default(ProxiesType.tab) ProxiesType type,\n    @Default(ProxiesSortType.none) ProxiesSortType sortType,\n    @Default(ProxiesLayout.standard) ProxiesLayout layout,\n    @Default(ProxiesIconStyle.none) ProxiesIconStyle iconStyle,\n    @Default(ProxyCardType.shrink) ProxyCardType cardType,\n    @Default(DelayAnimationType.none) DelayAnimationType delayAnimation,\n    @Default({}) Map<String, String> iconMap,\n    @Default(16) int concurrencyLimit,\n  }) = _ProxiesStyle;\n\n  factory ProxiesStyle.fromJson(Map<String, Object?>? json) =>\n      json == null ? defaultProxiesStyle : _$ProxiesStyleFromJson(json);\n}\n\n@freezed\nabstract class TextScale with _$TextScale {\n  const factory TextScale({\n    @Default(false) bool enable,\n    @Default(1.0) double scale,\n  }) = _TextScale;\n\n  factory TextScale.fromJson(Map<String, Object?> json) =>\n      _$TextScaleFromJson(json);\n}\n\n@freezed\nabstract class ThemeProps with _$ThemeProps {\n  const factory ThemeProps({\n    int? primaryColor,\n    @Default(defaultPrimaryColors) List<int> primaryColors,\n    @Default(ThemeMode.system) ThemeMode themeMode,\n    @Default(DynamicSchemeVariant.content) DynamicSchemeVariant schemeVariant,\n    @Default(false) bool pureBlack,\n    @Default(TextScale()) TextScale textScale,\n    @Default(false) bool useLightIcon,\n    @Default(false) bool useHarmonyFont,\n  }) = _ThemeProps;\n\n  factory ThemeProps.fromJson(Map<String, Object?> json) =>\n      _$ThemePropsFromJson(json);\n\n  factory ThemeProps.safeFromJson(Map<String, Object?>? json) {\n    if (json == null) {\n      return defaultThemeProps;\n    }\n    try {\n      return ThemeProps.fromJson(json);\n    } catch (_) {\n      return defaultThemeProps;\n    }\n  }\n}\n\n@freezed\nabstract class ScriptProps with _$ScriptProps {\n  const factory ScriptProps({\n    String? currentId,\n    @Default([]) List<Script> scripts,\n  }) = _ScriptProps;\n\n  factory ScriptProps.fromJson(Map<String, Object?> json) =>\n      _$ScriptPropsFromJson(json);\n}\n\nextension ScriptPropsExt on ScriptProps {\n  String? get realId {\n    final index = scripts.indexWhere((script) => script.id == currentId);\n    if (index != -1) {\n      return currentId;\n    }\n    return null;\n  }\n\n  Script? get currentScript {\n    final index = scripts.indexWhere((script) => script.id == currentId);\n    if (index != -1) {\n      return scripts[index];\n    }\n    return null;\n  }\n}\n\n@freezed\nabstract class Config with _$Config {\n  const factory Config({\n    @JsonKey(fromJson: AppSettingProps.safeFromJson)\n    @Default(defaultAppSettingProps)\n    AppSettingProps appSetting,\n    @Default([]) List<Profile> profiles,\n    @Default([]) List<HotKeyAction> hotKeyActions,\n    String? currentProfileId,\n    @Default(false) bool overrideDns,\n    @Default(false) bool overrideNtp,\n    @Default(false) bool overrideSniffer,\n    @Default(false) bool overrideTunnel,\n    @Default(false) bool overrideExperimental,\n    @Default(true) bool overrideTestUrl,\n    DAV? dav,\n    @Default(defaultNetworkProps) NetworkProps networkProps,\n    @JsonKey(fromJson: VpnProps.safeFromJson)\n    @Default(defaultVpnProps)\n    VpnProps vpnProps,\n    @JsonKey(fromJson: ThemeProps.safeFromJson) required ThemeProps themeProps,\n    @Default(defaultProxiesStyle) ProxiesStyle proxiesStyle,\n    @Default(defaultWindowProps) WindowProps windowProps,\n    @Default(defaultClashConfig) ClashConfig patchClashConfig,\n    @Default(ScriptProps()) ScriptProps scriptProps,\n    @Default('') String nodeExcludeFilter,\n    @Default(5000) int healthCheckTimeout,\n  }) = _Config;\n\n  factory Config.fromJson(Map<String, Object?> json) => _$ConfigFromJson(json);\n\n  factory Config.compatibleFromJson(Map<String, Object?> json) {\n    try {\n      final accessControlMap = json['accessControl'];\n      final isAccessControl = json['isAccessControl'];\n      if (accessControlMap != null) {\n        (accessControlMap as Map)['enable'] = isAccessControl;\n        if (json['vpnProps'] != null) {\n          (json['vpnProps'] as Map)['accessControl'] = accessControlMap;\n        }\n      }\n    } catch (_) {}\n\n    // 兼容 FlClash：currentProfileId 可能是 int 类型，需要转换为 String\n    try {\n      final currentProfileId = json['currentProfileId'];\n      if (currentProfileId != null && currentProfileId is int) {\n        json['currentProfileId'] = currentProfileId.toString();\n      }\n    } catch (_) {}\n\n    // 兼容 FlClash：profiles 中的 id 可能是 int 类型，需要转换为 String\n    try {\n      final profiles = json['profiles'];\n      if (profiles != null && profiles is List) {\n        for (final profile in profiles) {\n          if (profile is Map) {\n            final id = profile['id'];\n            if (id != null && id is int) {\n              profile['id'] = id.toString();\n            }\n          }\n        }\n      }\n    } catch (_) {}\n\n    return Config.fromJson(json);\n  }\n}\n\nextension ConfigExt on Config {\n  Profile? get currentProfile {\n    return profiles.getProfile(currentProfileId);\n  }\n}\n"
  },
  {
    "path": "lib/models/core.dart",
    "content": "// ignore_for_file: invalid_annotation_target\n\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'generated/core.freezed.dart';\npart 'generated/core.g.dart';\n\nabstract mixin class AppMessageListener {\n  void onLog(Log log) {}\n\n  void onDelay(Delay delay) {}\n\n  void onRequest(TrackerInfo connection) {}\n\n  void onLoaded(String providerName) {}\n}\n\n// abstract mixin class ServiceMessageListener {\n//   onProtect(Fd fd) {}\n//\n//   onProcess(ProcessData process) {}\n// }\n\n@freezed\nabstract class SetupParams with _$SetupParams {\n  const factory SetupParams({\n    @JsonKey(name: 'config') required Map<String, dynamic> config,\n    @JsonKey(name: 'selected-map') required Map<String, String> selectedMap,\n    @JsonKey(name: 'test-url') required String testUrl,\n    @JsonKey(name: 'override-test-url') @Default(true) bool overrideTestUrl,\n  }) = _SetupParams;\n\n  factory SetupParams.fromJson(Map<String, dynamic> json) =>\n      _$SetupParamsFromJson(json);\n}\n\n// extension SetupParamsExt on SetupParams {\n//   Map<String, dynamic> get json {\n//     final json = Map<String, dynamic>.from(config);\n//     json[\"selected-map\"] = selectedMap;\n//     json[\"test-url\"] = testUrl;\n//     return json;\n//   }\n// }\n\n@freezed\nabstract class UpdateParams with _$UpdateParams {\n  const factory UpdateParams({\n    required Tun tun,\n    @JsonKey(name: 'mixed-port') required int mixedPort,\n    @JsonKey(name: 'allow-lan') required bool allowLan,\n    @JsonKey(name: 'find-process-mode')\n    required FindProcessMode findProcessMode,\n    required Mode mode,\n    @JsonKey(name: 'log-level') required LogLevel logLevel,\n    required bool ipv6,\n    @JsonKey(name: 'tcp-concurrent') required bool tcpConcurrent,\n    @JsonKey(name: 'external-controller')\n    required ExternalControllerStatus externalController,\n    @JsonKey(name: 'unified-delay') required bool unifiedDelay,\n  }) = _UpdateParams;\n\n  factory UpdateParams.fromJson(Map<String, dynamic> json) =>\n      _$UpdateParamsFromJson(json);\n}\n\n@freezed\nabstract class CoreState with _$CoreState {\n  const factory CoreState({\n    @JsonKey(name: 'vpn-props') required VpnProps vpnProps,\n    @JsonKey(name: 'only-statistics-proxy') required bool onlyStatisticsProxy,\n    @JsonKey(name: 'current-profile-name') required String currentProfileName,\n    @JsonKey(name: 'bypass-domain') @Default([]) List<String> bypassDomain,\n  }) = _CoreState;\n\n  factory CoreState.fromJson(Map<String, Object?> json) =>\n      _$CoreStateFromJson(json);\n}\n\n@freezed\nabstract class AndroidVpnOptions with _$AndroidVpnOptions {\n  const factory AndroidVpnOptions({\n    required bool enable,\n    required int port,\n    required AccessControl? accessControl,\n    required bool allowBypass,\n    required bool systemProxy,\n    required List<String> bypassDomain,\n    required String ipv4Address,\n    required String ipv6Address,\n    @Default([]) List<String> routeAddress,\n    @Default('config') String routeMode,\n    required String dnsServerAddress,\n    @Default(false) bool dozeSuspend,\n  }) = _AndroidVpnOptions;\n\n  factory AndroidVpnOptions.fromJson(Map<String, Object?> json) =>\n      _$AndroidVpnOptionsFromJson(json);\n}\n\n@freezed\nabstract class InitParams with _$InitParams {\n  const factory InitParams({\n    @JsonKey(name: 'home-dir') required String homeDir,\n    required int version,\n  }) = _InitParams;\n\n  factory InitParams.fromJson(Map<String, Object?> json) =>\n      _$InitParamsFromJson(json);\n}\n\n@freezed\nabstract class ChangeProxyParams with _$ChangeProxyParams {\n  const factory ChangeProxyParams({\n    @JsonKey(name: 'group-name') required String groupName,\n    @JsonKey(name: 'proxy-name') required String proxyName,\n  }) = _ChangeProxyParams;\n\n  factory ChangeProxyParams.fromJson(Map<String, Object?> json) =>\n      _$ChangeProxyParamsFromJson(json);\n}\n\n@freezed\nabstract class UpdateGeoDataParams with _$UpdateGeoDataParams {\n  const factory UpdateGeoDataParams({\n    @JsonKey(name: 'geo-type') required String geoType,\n    @JsonKey(name: 'geo-name') required String geoName,\n  }) = _UpdateGeoDataParams;\n\n  factory UpdateGeoDataParams.fromJson(Map<String, Object?> json) =>\n      _$UpdateGeoDataParamsFromJson(json);\n}\n\n@freezed\nabstract class AppMessage with _$AppMessage {\n  const factory AppMessage({required AppMessageType type, dynamic data}) =\n      _AppMessage;\n\n  factory AppMessage.fromJson(Map<String, Object?> json) =>\n      _$AppMessageFromJson(json);\n}\n\n@freezed\nabstract class InvokeMessage with _$InvokeMessage {\n  const factory InvokeMessage({required InvokeMessageType type, dynamic data}) =\n      _InvokeMessage;\n\n  factory InvokeMessage.fromJson(Map<String, Object?> json) =>\n      _$InvokeMessageFromJson(json);\n}\n\n@freezed\nabstract class Delay with _$Delay {\n  const factory Delay({required String name, required String url, int? value}) =\n      _Delay;\n\n  factory Delay.fromJson(Map<String, Object?> json) => _$DelayFromJson(json);\n}\n\n@freezed\nabstract class Now with _$Now {\n  const factory Now({required String name, required String value}) = _Now;\n\n  factory Now.fromJson(Map<String, Object?> json) => _$NowFromJson(json);\n}\n\n// @freezed\n// class ProcessData with _$ProcessData {\n//   const factory ProcessData({\n//     required String id,\n//     required Metadata metadata,\n//   }) = _ProcessData;\n//\n//   factory ProcessData.fromJson(Map<String, Object?> json) =>\n//       _$ProcessDataFromJson(json);\n// }\n//\n// @freezed\n// class Fd with _$Fd {\n//   const factory Fd({\n//     required String id,\n//     required int value,\n//   }) = _Fd;\n//\n//   factory Fd.fromJson(Map<String, Object?> json) => _$FdFromJson(json);\n// }\n\n@freezed\nabstract class ProviderSubscriptionInfo with _$ProviderSubscriptionInfo {\n  const factory ProviderSubscriptionInfo({\n    @JsonKey(name: 'UPLOAD') @Default(0) int upload,\n    @JsonKey(name: 'DOWNLOAD') @Default(0) int download,\n    @JsonKey(name: 'TOTAL') @Default(0) int total,\n    @JsonKey(name: 'EXPIRE') @Default(0) int expire,\n  }) = _ProviderSubscriptionInfo;\n\n  factory ProviderSubscriptionInfo.fromJson(Map<String, Object?> json) =>\n      _$ProviderSubscriptionInfoFromJson(json);\n}\n\nSubscriptionInfo? subscriptionInfoFormCore(Map<String, Object?>? json) {\n  if (json == null) return null;\n  return SubscriptionInfo(\n    upload: (json['Upload'] as num?)?.toInt() ?? 0,\n    download: (json['Download'] as num?)?.toInt() ?? 0,\n    total: (json['Total'] as num?)?.toInt() ?? 0,\n    expire: (json['Expire'] as num?)?.toInt() ?? 0,\n  );\n}\n\n@freezed\nabstract class ExternalProvider with _$ExternalProvider {\n  const factory ExternalProvider({\n    required String name,\n    required String type,\n    String? path,\n    required int count,\n    @JsonKey(name: 'subscription-info', fromJson: subscriptionInfoFormCore)\n    SubscriptionInfo? subscriptionInfo,\n    @Default(false) bool isUpdating,\n    @JsonKey(name: 'vehicle-type') required String vehicleType,\n    @JsonKey(name: 'update-at') required DateTime updateAt,\n  }) = _ExternalProvider;\n\n  factory ExternalProvider.fromJson(Map<String, Object?> json) =>\n      _$ExternalProviderFromJson(json);\n}\n\n@freezed\nabstract class Action with _$Action {\n  const factory Action({\n    required ActionMethod method,\n    required dynamic data,\n    required String id,\n  }) = _Action;\n\n  factory Action.fromJson(Map<String, Object?> json) => _$ActionFromJson(json);\n}\n\n@freezed\nabstract class ActionResult with _$ActionResult {\n  const factory ActionResult({\n    required ActionMethod method,\n    required dynamic data,\n    String? id,\n    @Default(ResultType.success) ResultType code,\n  }) = _ActionResult;\n\n  factory ActionResult.fromJson(Map<String, Object?> json) =>\n      _$ActionResultFromJson(json);\n}\n\nextension ActionResultExt on ActionResult {\n  Result get toResult {\n    if (code == ResultType.success) {\n      return Result.success(data);\n    } else {\n      return Result.error(data);\n    }\n  }\n}\n"
  },
  {
    "path": "lib/models/generated/app.freezed.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n// coverage:ignore-file\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of '../app.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\n// dart format off\nT _$identity<T>(T value) => value;\n/// @nodoc\nmixin _$AppState {\n\n bool get isInit; bool get backBlock; PageLabel get pageLabel; List<Package> get packages; int get sortNum; Size get viewSize; DelayMap get delayMap; List<Group> get groups; int get checkIpNum; Brightness get brightness; int? get runTime; List<ExternalProvider> get providers; String? get localIp; FixedList<TrackerInfo> get requests; int get version; FixedList<Log> get logs; FixedList<Traffic> get traffics; Traffic get totalTraffic; bool get realTunEnable; bool get loading; SystemUiOverlayStyle get systemUiOverlayStyle;\n/// Create a copy of AppState\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$AppStateCopyWith<AppState> get copyWith => _$AppStateCopyWithImpl<AppState>(this as AppState, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is AppState&&(identical(other.isInit, isInit) || other.isInit == isInit)&&(identical(other.backBlock, backBlock) || other.backBlock == backBlock)&&(identical(other.pageLabel, pageLabel) || other.pageLabel == pageLabel)&&const DeepCollectionEquality().equals(other.packages, packages)&&(identical(other.sortNum, sortNum) || other.sortNum == sortNum)&&(identical(other.viewSize, viewSize) || other.viewSize == viewSize)&&const DeepCollectionEquality().equals(other.delayMap, delayMap)&&const DeepCollectionEquality().equals(other.groups, groups)&&(identical(other.checkIpNum, checkIpNum) || other.checkIpNum == checkIpNum)&&(identical(other.brightness, brightness) || other.brightness == brightness)&&(identical(other.runTime, runTime) || other.runTime == runTime)&&const DeepCollectionEquality().equals(other.providers, providers)&&(identical(other.localIp, localIp) || other.localIp == localIp)&&(identical(other.requests, requests) || other.requests == requests)&&(identical(other.version, version) || other.version == version)&&(identical(other.logs, logs) || other.logs == logs)&&(identical(other.traffics, traffics) || other.traffics == traffics)&&(identical(other.totalTraffic, totalTraffic) || other.totalTraffic == totalTraffic)&&(identical(other.realTunEnable, realTunEnable) || other.realTunEnable == realTunEnable)&&(identical(other.loading, loading) || other.loading == loading)&&(identical(other.systemUiOverlayStyle, systemUiOverlayStyle) || other.systemUiOverlayStyle == systemUiOverlayStyle));\n}\n\n\n@override\nint get hashCode => Object.hashAll([runtimeType,isInit,backBlock,pageLabel,const DeepCollectionEquality().hash(packages),sortNum,viewSize,const DeepCollectionEquality().hash(delayMap),const DeepCollectionEquality().hash(groups),checkIpNum,brightness,runTime,const DeepCollectionEquality().hash(providers),localIp,requests,version,logs,traffics,totalTraffic,realTunEnable,loading,systemUiOverlayStyle]);\n\n@override\nString toString() {\n  return 'AppState(isInit: $isInit, backBlock: $backBlock, pageLabel: $pageLabel, packages: $packages, sortNum: $sortNum, viewSize: $viewSize, delayMap: $delayMap, groups: $groups, checkIpNum: $checkIpNum, brightness: $brightness, runTime: $runTime, providers: $providers, localIp: $localIp, requests: $requests, version: $version, logs: $logs, traffics: $traffics, totalTraffic: $totalTraffic, realTunEnable: $realTunEnable, loading: $loading, systemUiOverlayStyle: $systemUiOverlayStyle)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $AppStateCopyWith<$Res>  {\n  factory $AppStateCopyWith(AppState value, $Res Function(AppState) _then) = _$AppStateCopyWithImpl;\n@useResult\n$Res call({\n bool isInit, bool backBlock, PageLabel pageLabel, List<Package> packages, int sortNum, Size viewSize, DelayMap delayMap, List<Group> groups, int checkIpNum, Brightness brightness, int? runTime, List<ExternalProvider> providers, String? localIp, FixedList<TrackerInfo> requests, int version, FixedList<Log> logs, FixedList<Traffic> traffics, Traffic totalTraffic, bool realTunEnable, bool loading, SystemUiOverlayStyle systemUiOverlayStyle\n});\n\n\n\n\n}\n/// @nodoc\nclass _$AppStateCopyWithImpl<$Res>\n    implements $AppStateCopyWith<$Res> {\n  _$AppStateCopyWithImpl(this._self, this._then);\n\n  final AppState _self;\n  final $Res Function(AppState) _then;\n\n/// Create a copy of AppState\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? isInit = null,Object? backBlock = null,Object? pageLabel = null,Object? packages = null,Object? sortNum = null,Object? viewSize = null,Object? delayMap = null,Object? groups = null,Object? checkIpNum = null,Object? brightness = null,Object? runTime = freezed,Object? providers = null,Object? localIp = freezed,Object? requests = null,Object? version = null,Object? logs = null,Object? traffics = null,Object? totalTraffic = null,Object? realTunEnable = null,Object? loading = null,Object? systemUiOverlayStyle = null,}) {\n  return _then(_self.copyWith(\nisInit: null == isInit ? _self.isInit : isInit // ignore: cast_nullable_to_non_nullable\nas bool,backBlock: null == backBlock ? _self.backBlock : backBlock // ignore: cast_nullable_to_non_nullable\nas bool,pageLabel: null == pageLabel ? _self.pageLabel : pageLabel // ignore: cast_nullable_to_non_nullable\nas PageLabel,packages: null == packages ? _self.packages : packages // ignore: cast_nullable_to_non_nullable\nas List<Package>,sortNum: null == sortNum ? _self.sortNum : sortNum // ignore: cast_nullable_to_non_nullable\nas int,viewSize: null == viewSize ? _self.viewSize : viewSize // ignore: cast_nullable_to_non_nullable\nas Size,delayMap: null == delayMap ? _self.delayMap : delayMap // ignore: cast_nullable_to_non_nullable\nas DelayMap,groups: null == groups ? _self.groups : groups // ignore: cast_nullable_to_non_nullable\nas List<Group>,checkIpNum: null == checkIpNum ? _self.checkIpNum : checkIpNum // ignore: cast_nullable_to_non_nullable\nas int,brightness: null == brightness ? _self.brightness : brightness // ignore: cast_nullable_to_non_nullable\nas Brightness,runTime: freezed == runTime ? _self.runTime : runTime // ignore: cast_nullable_to_non_nullable\nas int?,providers: null == providers ? _self.providers : providers // ignore: cast_nullable_to_non_nullable\nas List<ExternalProvider>,localIp: freezed == localIp ? _self.localIp : localIp // ignore: cast_nullable_to_non_nullable\nas String?,requests: null == requests ? _self.requests : requests // ignore: cast_nullable_to_non_nullable\nas FixedList<TrackerInfo>,version: null == version ? _self.version : version // ignore: cast_nullable_to_non_nullable\nas int,logs: null == logs ? _self.logs : logs // ignore: cast_nullable_to_non_nullable\nas FixedList<Log>,traffics: null == traffics ? _self.traffics : traffics // ignore: cast_nullable_to_non_nullable\nas FixedList<Traffic>,totalTraffic: null == totalTraffic ? _self.totalTraffic : totalTraffic // ignore: cast_nullable_to_non_nullable\nas Traffic,realTunEnable: null == realTunEnable ? _self.realTunEnable : realTunEnable // ignore: cast_nullable_to_non_nullable\nas bool,loading: null == loading ? _self.loading : loading // ignore: cast_nullable_to_non_nullable\nas bool,systemUiOverlayStyle: null == systemUiOverlayStyle ? _self.systemUiOverlayStyle : systemUiOverlayStyle // ignore: cast_nullable_to_non_nullable\nas SystemUiOverlayStyle,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [AppState].\nextension AppStatePatterns on AppState {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _AppState value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _AppState() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _AppState value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _AppState():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _AppState value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _AppState() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isInit,  bool backBlock,  PageLabel pageLabel,  List<Package> packages,  int sortNum,  Size viewSize,  DelayMap delayMap,  List<Group> groups,  int checkIpNum,  Brightness brightness,  int? runTime,  List<ExternalProvider> providers,  String? localIp,  FixedList<TrackerInfo> requests,  int version,  FixedList<Log> logs,  FixedList<Traffic> traffics,  Traffic totalTraffic,  bool realTunEnable,  bool loading,  SystemUiOverlayStyle systemUiOverlayStyle)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _AppState() when $default != null:\nreturn $default(_that.isInit,_that.backBlock,_that.pageLabel,_that.packages,_that.sortNum,_that.viewSize,_that.delayMap,_that.groups,_that.checkIpNum,_that.brightness,_that.runTime,_that.providers,_that.localIp,_that.requests,_that.version,_that.logs,_that.traffics,_that.totalTraffic,_that.realTunEnable,_that.loading,_that.systemUiOverlayStyle);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool isInit,  bool backBlock,  PageLabel pageLabel,  List<Package> packages,  int sortNum,  Size viewSize,  DelayMap delayMap,  List<Group> groups,  int checkIpNum,  Brightness brightness,  int? runTime,  List<ExternalProvider> providers,  String? localIp,  FixedList<TrackerInfo> requests,  int version,  FixedList<Log> logs,  FixedList<Traffic> traffics,  Traffic totalTraffic,  bool realTunEnable,  bool loading,  SystemUiOverlayStyle systemUiOverlayStyle)  $default,) {final _that = this;\nswitch (_that) {\ncase _AppState():\nreturn $default(_that.isInit,_that.backBlock,_that.pageLabel,_that.packages,_that.sortNum,_that.viewSize,_that.delayMap,_that.groups,_that.checkIpNum,_that.brightness,_that.runTime,_that.providers,_that.localIp,_that.requests,_that.version,_that.logs,_that.traffics,_that.totalTraffic,_that.realTunEnable,_that.loading,_that.systemUiOverlayStyle);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool isInit,  bool backBlock,  PageLabel pageLabel,  List<Package> packages,  int sortNum,  Size viewSize,  DelayMap delayMap,  List<Group> groups,  int checkIpNum,  Brightness brightness,  int? runTime,  List<ExternalProvider> providers,  String? localIp,  FixedList<TrackerInfo> requests,  int version,  FixedList<Log> logs,  FixedList<Traffic> traffics,  Traffic totalTraffic,  bool realTunEnable,  bool loading,  SystemUiOverlayStyle systemUiOverlayStyle)?  $default,) {final _that = this;\nswitch (_that) {\ncase _AppState() when $default != null:\nreturn $default(_that.isInit,_that.backBlock,_that.pageLabel,_that.packages,_that.sortNum,_that.viewSize,_that.delayMap,_that.groups,_that.checkIpNum,_that.brightness,_that.runTime,_that.providers,_that.localIp,_that.requests,_that.version,_that.logs,_that.traffics,_that.totalTraffic,_that.realTunEnable,_that.loading,_that.systemUiOverlayStyle);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _AppState implements AppState {\n  const _AppState({this.isInit = false, this.backBlock = false, this.pageLabel = PageLabel.dashboard, final  List<Package> packages = const [], this.sortNum = 0, required this.viewSize, final  DelayMap delayMap = const {}, final  List<Group> groups = const [], this.checkIpNum = 0, required this.brightness, this.runTime, final  List<ExternalProvider> providers = const [], this.localIp, required this.requests, required this.version, required this.logs, required this.traffics, required this.totalTraffic, this.realTunEnable = false, this.loading = false, required this.systemUiOverlayStyle}): _packages = packages,_delayMap = delayMap,_groups = groups,_providers = providers;\n  \n\n@override@JsonKey() final  bool isInit;\n@override@JsonKey() final  bool backBlock;\n@override@JsonKey() final  PageLabel pageLabel;\n final  List<Package> _packages;\n@override@JsonKey() List<Package> get packages {\n  if (_packages is EqualUnmodifiableListView) return _packages;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_packages);\n}\n\n@override@JsonKey() final  int sortNum;\n@override final  Size viewSize;\n final  DelayMap _delayMap;\n@override@JsonKey() DelayMap get delayMap {\n  if (_delayMap is EqualUnmodifiableMapView) return _delayMap;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableMapView(_delayMap);\n}\n\n final  List<Group> _groups;\n@override@JsonKey() List<Group> get groups {\n  if (_groups is EqualUnmodifiableListView) return _groups;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_groups);\n}\n\n@override@JsonKey() final  int checkIpNum;\n@override final  Brightness brightness;\n@override final  int? runTime;\n final  List<ExternalProvider> _providers;\n@override@JsonKey() List<ExternalProvider> get providers {\n  if (_providers is EqualUnmodifiableListView) return _providers;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_providers);\n}\n\n@override final  String? localIp;\n@override final  FixedList<TrackerInfo> requests;\n@override final  int version;\n@override final  FixedList<Log> logs;\n@override final  FixedList<Traffic> traffics;\n@override final  Traffic totalTraffic;\n@override@JsonKey() final  bool realTunEnable;\n@override@JsonKey() final  bool loading;\n@override final  SystemUiOverlayStyle systemUiOverlayStyle;\n\n/// Create a copy of AppState\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$AppStateCopyWith<_AppState> get copyWith => __$AppStateCopyWithImpl<_AppState>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppState&&(identical(other.isInit, isInit) || other.isInit == isInit)&&(identical(other.backBlock, backBlock) || other.backBlock == backBlock)&&(identical(other.pageLabel, pageLabel) || other.pageLabel == pageLabel)&&const DeepCollectionEquality().equals(other._packages, _packages)&&(identical(other.sortNum, sortNum) || other.sortNum == sortNum)&&(identical(other.viewSize, viewSize) || other.viewSize == viewSize)&&const DeepCollectionEquality().equals(other._delayMap, _delayMap)&&const DeepCollectionEquality().equals(other._groups, _groups)&&(identical(other.checkIpNum, checkIpNum) || other.checkIpNum == checkIpNum)&&(identical(other.brightness, brightness) || other.brightness == brightness)&&(identical(other.runTime, runTime) || other.runTime == runTime)&&const DeepCollectionEquality().equals(other._providers, _providers)&&(identical(other.localIp, localIp) || other.localIp == localIp)&&(identical(other.requests, requests) || other.requests == requests)&&(identical(other.version, version) || other.version == version)&&(identical(other.logs, logs) || other.logs == logs)&&(identical(other.traffics, traffics) || other.traffics == traffics)&&(identical(other.totalTraffic, totalTraffic) || other.totalTraffic == totalTraffic)&&(identical(other.realTunEnable, realTunEnable) || other.realTunEnable == realTunEnable)&&(identical(other.loading, loading) || other.loading == loading)&&(identical(other.systemUiOverlayStyle, systemUiOverlayStyle) || other.systemUiOverlayStyle == systemUiOverlayStyle));\n}\n\n\n@override\nint get hashCode => Object.hashAll([runtimeType,isInit,backBlock,pageLabel,const DeepCollectionEquality().hash(_packages),sortNum,viewSize,const DeepCollectionEquality().hash(_delayMap),const DeepCollectionEquality().hash(_groups),checkIpNum,brightness,runTime,const DeepCollectionEquality().hash(_providers),localIp,requests,version,logs,traffics,totalTraffic,realTunEnable,loading,systemUiOverlayStyle]);\n\n@override\nString toString() {\n  return 'AppState(isInit: $isInit, backBlock: $backBlock, pageLabel: $pageLabel, packages: $packages, sortNum: $sortNum, viewSize: $viewSize, delayMap: $delayMap, groups: $groups, checkIpNum: $checkIpNum, brightness: $brightness, runTime: $runTime, providers: $providers, localIp: $localIp, requests: $requests, version: $version, logs: $logs, traffics: $traffics, totalTraffic: $totalTraffic, realTunEnable: $realTunEnable, loading: $loading, systemUiOverlayStyle: $systemUiOverlayStyle)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$AppStateCopyWith<$Res> implements $AppStateCopyWith<$Res> {\n  factory _$AppStateCopyWith(_AppState value, $Res Function(_AppState) _then) = __$AppStateCopyWithImpl;\n@override @useResult\n$Res call({\n bool isInit, bool backBlock, PageLabel pageLabel, List<Package> packages, int sortNum, Size viewSize, DelayMap delayMap, List<Group> groups, int checkIpNum, Brightness brightness, int? runTime, List<ExternalProvider> providers, String? localIp, FixedList<TrackerInfo> requests, int version, FixedList<Log> logs, FixedList<Traffic> traffics, Traffic totalTraffic, bool realTunEnable, bool loading, SystemUiOverlayStyle systemUiOverlayStyle\n});\n\n\n\n\n}\n/// @nodoc\nclass __$AppStateCopyWithImpl<$Res>\n    implements _$AppStateCopyWith<$Res> {\n  __$AppStateCopyWithImpl(this._self, this._then);\n\n  final _AppState _self;\n  final $Res Function(_AppState) _then;\n\n/// Create a copy of AppState\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? isInit = null,Object? backBlock = null,Object? pageLabel = null,Object? packages = null,Object? sortNum = null,Object? viewSize = null,Object? delayMap = null,Object? groups = null,Object? checkIpNum = null,Object? brightness = null,Object? runTime = freezed,Object? providers = null,Object? localIp = freezed,Object? requests = null,Object? version = null,Object? logs = null,Object? traffics = null,Object? totalTraffic = null,Object? realTunEnable = null,Object? loading = null,Object? systemUiOverlayStyle = null,}) {\n  return _then(_AppState(\nisInit: null == isInit ? _self.isInit : isInit // ignore: cast_nullable_to_non_nullable\nas bool,backBlock: null == backBlock ? _self.backBlock : backBlock // ignore: cast_nullable_to_non_nullable\nas bool,pageLabel: null == pageLabel ? _self.pageLabel : pageLabel // ignore: cast_nullable_to_non_nullable\nas PageLabel,packages: null == packages ? _self._packages : packages // ignore: cast_nullable_to_non_nullable\nas List<Package>,sortNum: null == sortNum ? _self.sortNum : sortNum // ignore: cast_nullable_to_non_nullable\nas int,viewSize: null == viewSize ? _self.viewSize : viewSize // ignore: cast_nullable_to_non_nullable\nas Size,delayMap: null == delayMap ? _self._delayMap : delayMap // ignore: cast_nullable_to_non_nullable\nas DelayMap,groups: null == groups ? _self._groups : groups // ignore: cast_nullable_to_non_nullable\nas List<Group>,checkIpNum: null == checkIpNum ? _self.checkIpNum : checkIpNum // ignore: cast_nullable_to_non_nullable\nas int,brightness: null == brightness ? _self.brightness : brightness // ignore: cast_nullable_to_non_nullable\nas Brightness,runTime: freezed == runTime ? _self.runTime : runTime // ignore: cast_nullable_to_non_nullable\nas int?,providers: null == providers ? _self._providers : providers // ignore: cast_nullable_to_non_nullable\nas List<ExternalProvider>,localIp: freezed == localIp ? _self.localIp : localIp // ignore: cast_nullable_to_non_nullable\nas String?,requests: null == requests ? _self.requests : requests // ignore: cast_nullable_to_non_nullable\nas FixedList<TrackerInfo>,version: null == version ? _self.version : version // ignore: cast_nullable_to_non_nullable\nas int,logs: null == logs ? _self.logs : logs // ignore: cast_nullable_to_non_nullable\nas FixedList<Log>,traffics: null == traffics ? _self.traffics : traffics // ignore: cast_nullable_to_non_nullable\nas FixedList<Traffic>,totalTraffic: null == totalTraffic ? _self.totalTraffic : totalTraffic // ignore: cast_nullable_to_non_nullable\nas Traffic,realTunEnable: null == realTunEnable ? _self.realTunEnable : realTunEnable // ignore: cast_nullable_to_non_nullable\nas bool,loading: null == loading ? _self.loading : loading // ignore: cast_nullable_to_non_nullable\nas bool,systemUiOverlayStyle: null == systemUiOverlayStyle ? _self.systemUiOverlayStyle : systemUiOverlayStyle // ignore: cast_nullable_to_non_nullable\nas SystemUiOverlayStyle,\n  ));\n}\n\n\n}\n\n// dart format on\n"
  },
  {
    "path": "lib/models/generated/clash_config.freezed.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n// coverage:ignore-file\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of '../clash_config.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\n// dart format off\nT _$identity<T>(T value) => value;\n\n/// @nodoc\nmixin _$ProxyGroup {\n\n String get name;@JsonKey(fromJson: GroupType.parseProfileType) GroupType get type;@JsonKey(fromJson: _parseStringList) List<String>? get proxies;@JsonKey(fromJson: _parseStringList) List<String>? get use;@JsonKey(fromJson: _parseInt) int? get interval;@JsonKey(fromJson: _parseBool) bool? get lazy; String? get url;@JsonKey(fromJson: _parseInt) int? get timeout;@JsonKey(name: 'max-failed-times', fromJson: _parseInt) int? get maxFailedTimes; String? get filter;@JsonKey(name: 'expected-filter') String? get excludeFilter;@JsonKey(name: 'exclude-type') String? get excludeType;@JsonKey(name: 'expected-status') dynamic get expectedStatus;@JsonKey(fromJson: _parseBool) bool? get hidden; String? get icon;@JsonKey(fromJson: _parseInt) int? get tolerance;\n/// Create a copy of ProxyGroup\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ProxyGroupCopyWith<ProxyGroup> get copyWith => _$ProxyGroupCopyWithImpl<ProxyGroup>(this as ProxyGroup, _$identity);\n\n  /// Serializes this ProxyGroup to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is ProxyGroup&&(identical(other.name, name) || other.name == name)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other.proxies, proxies)&&const DeepCollectionEquality().equals(other.use, use)&&(identical(other.interval, interval) || other.interval == interval)&&(identical(other.lazy, lazy) || other.lazy == lazy)&&(identical(other.url, url) || other.url == url)&&(identical(other.timeout, timeout) || other.timeout == timeout)&&(identical(other.maxFailedTimes, maxFailedTimes) || other.maxFailedTimes == maxFailedTimes)&&(identical(other.filter, filter) || other.filter == filter)&&(identical(other.excludeFilter, excludeFilter) || other.excludeFilter == excludeFilter)&&(identical(other.excludeType, excludeType) || other.excludeType == excludeType)&&const DeepCollectionEquality().equals(other.expectedStatus, expectedStatus)&&(identical(other.hidden, hidden) || other.hidden == hidden)&&(identical(other.icon, icon) || other.icon == icon)&&(identical(other.tolerance, tolerance) || other.tolerance == tolerance));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,name,type,const DeepCollectionEquality().hash(proxies),const DeepCollectionEquality().hash(use),interval,lazy,url,timeout,maxFailedTimes,filter,excludeFilter,excludeType,const DeepCollectionEquality().hash(expectedStatus),hidden,icon,tolerance);\n\n@override\nString toString() {\n  return 'ProxyGroup(name: $name, type: $type, proxies: $proxies, use: $use, interval: $interval, lazy: $lazy, url: $url, timeout: $timeout, maxFailedTimes: $maxFailedTimes, filter: $filter, excludeFilter: $excludeFilter, excludeType: $excludeType, expectedStatus: $expectedStatus, hidden: $hidden, icon: $icon, tolerance: $tolerance)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ProxyGroupCopyWith<$Res>  {\n  factory $ProxyGroupCopyWith(ProxyGroup value, $Res Function(ProxyGroup) _then) = _$ProxyGroupCopyWithImpl;\n@useResult\n$Res call({\n String name,@JsonKey(fromJson: GroupType.parseProfileType) GroupType type,@JsonKey(fromJson: _parseStringList) List<String>? proxies,@JsonKey(fromJson: _parseStringList) List<String>? use,@JsonKey(fromJson: _parseInt) int? interval,@JsonKey(fromJson: _parseBool) bool? lazy, String? url,@JsonKey(fromJson: _parseInt) int? timeout,@JsonKey(name: 'max-failed-times', fromJson: _parseInt) int? maxFailedTimes, String? filter,@JsonKey(name: 'expected-filter') String? excludeFilter,@JsonKey(name: 'exclude-type') String? excludeType,@JsonKey(name: 'expected-status') dynamic expectedStatus,@JsonKey(fromJson: _parseBool) bool? hidden, String? icon,@JsonKey(fromJson: _parseInt) int? tolerance\n});\n\n\n\n\n}\n/// @nodoc\nclass _$ProxyGroupCopyWithImpl<$Res>\n    implements $ProxyGroupCopyWith<$Res> {\n  _$ProxyGroupCopyWithImpl(this._self, this._then);\n\n  final ProxyGroup _self;\n  final $Res Function(ProxyGroup) _then;\n\n/// Create a copy of ProxyGroup\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? name = null,Object? type = null,Object? proxies = freezed,Object? use = freezed,Object? interval = freezed,Object? lazy = freezed,Object? url = freezed,Object? timeout = freezed,Object? maxFailedTimes = freezed,Object? filter = freezed,Object? excludeFilter = freezed,Object? excludeType = freezed,Object? expectedStatus = freezed,Object? hidden = freezed,Object? icon = freezed,Object? tolerance = freezed,}) {\n  return _then(_self.copyWith(\nname: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable\nas String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable\nas GroupType,proxies: freezed == proxies ? _self.proxies : proxies // ignore: cast_nullable_to_non_nullable\nas List<String>?,use: freezed == use ? _self.use : use // ignore: cast_nullable_to_non_nullable\nas List<String>?,interval: freezed == interval ? _self.interval : interval // ignore: cast_nullable_to_non_nullable\nas int?,lazy: freezed == lazy ? _self.lazy : lazy // ignore: cast_nullable_to_non_nullable\nas bool?,url: freezed == url ? _self.url : url // ignore: cast_nullable_to_non_nullable\nas String?,timeout: freezed == timeout ? _self.timeout : timeout // ignore: cast_nullable_to_non_nullable\nas int?,maxFailedTimes: freezed == maxFailedTimes ? _self.maxFailedTimes : maxFailedTimes // ignore: cast_nullable_to_non_nullable\nas int?,filter: freezed == filter ? _self.filter : filter // ignore: cast_nullable_to_non_nullable\nas String?,excludeFilter: freezed == excludeFilter ? _self.excludeFilter : excludeFilter // ignore: cast_nullable_to_non_nullable\nas String?,excludeType: freezed == excludeType ? _self.excludeType : excludeType // ignore: cast_nullable_to_non_nullable\nas String?,expectedStatus: freezed == expectedStatus ? _self.expectedStatus : expectedStatus // ignore: cast_nullable_to_non_nullable\nas dynamic,hidden: freezed == hidden ? _self.hidden : hidden // ignore: cast_nullable_to_non_nullable\nas bool?,icon: freezed == icon ? _self.icon : icon // ignore: cast_nullable_to_non_nullable\nas String?,tolerance: freezed == tolerance ? _self.tolerance : tolerance // ignore: cast_nullable_to_non_nullable\nas int?,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [ProxyGroup].\nextension ProxyGroupPatterns on ProxyGroup {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ProxyGroup value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _ProxyGroup() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ProxyGroup value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ProxyGroup():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ProxyGroup value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ProxyGroup() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String name, @JsonKey(fromJson: GroupType.parseProfileType)  GroupType type, @JsonKey(fromJson: _parseStringList)  List<String>? proxies, @JsonKey(fromJson: _parseStringList)  List<String>? use, @JsonKey(fromJson: _parseInt)  int? interval, @JsonKey(fromJson: _parseBool)  bool? lazy,  String? url, @JsonKey(fromJson: _parseInt)  int? timeout, @JsonKey(name: 'max-failed-times', fromJson: _parseInt)  int? maxFailedTimes,  String? filter, @JsonKey(name: 'expected-filter')  String? excludeFilter, @JsonKey(name: 'exclude-type')  String? excludeType, @JsonKey(name: 'expected-status')  dynamic expectedStatus, @JsonKey(fromJson: _parseBool)  bool? hidden,  String? icon, @JsonKey(fromJson: _parseInt)  int? tolerance)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _ProxyGroup() when $default != null:\nreturn $default(_that.name,_that.type,_that.proxies,_that.use,_that.interval,_that.lazy,_that.url,_that.timeout,_that.maxFailedTimes,_that.filter,_that.excludeFilter,_that.excludeType,_that.expectedStatus,_that.hidden,_that.icon,_that.tolerance);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String name, @JsonKey(fromJson: GroupType.parseProfileType)  GroupType type, @JsonKey(fromJson: _parseStringList)  List<String>? proxies, @JsonKey(fromJson: _parseStringList)  List<String>? use, @JsonKey(fromJson: _parseInt)  int? interval, @JsonKey(fromJson: _parseBool)  bool? lazy,  String? url, @JsonKey(fromJson: _parseInt)  int? timeout, @JsonKey(name: 'max-failed-times', fromJson: _parseInt)  int? maxFailedTimes,  String? filter, @JsonKey(name: 'expected-filter')  String? excludeFilter, @JsonKey(name: 'exclude-type')  String? excludeType, @JsonKey(name: 'expected-status')  dynamic expectedStatus, @JsonKey(fromJson: _parseBool)  bool? hidden,  String? icon, @JsonKey(fromJson: _parseInt)  int? tolerance)  $default,) {final _that = this;\nswitch (_that) {\ncase _ProxyGroup():\nreturn $default(_that.name,_that.type,_that.proxies,_that.use,_that.interval,_that.lazy,_that.url,_that.timeout,_that.maxFailedTimes,_that.filter,_that.excludeFilter,_that.excludeType,_that.expectedStatus,_that.hidden,_that.icon,_that.tolerance);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String name, @JsonKey(fromJson: GroupType.parseProfileType)  GroupType type, @JsonKey(fromJson: _parseStringList)  List<String>? proxies, @JsonKey(fromJson: _parseStringList)  List<String>? use, @JsonKey(fromJson: _parseInt)  int? interval, @JsonKey(fromJson: _parseBool)  bool? lazy,  String? url, @JsonKey(fromJson: _parseInt)  int? timeout, @JsonKey(name: 'max-failed-times', fromJson: _parseInt)  int? maxFailedTimes,  String? filter, @JsonKey(name: 'expected-filter')  String? excludeFilter, @JsonKey(name: 'exclude-type')  String? excludeType, @JsonKey(name: 'expected-status')  dynamic expectedStatus, @JsonKey(fromJson: _parseBool)  bool? hidden,  String? icon, @JsonKey(fromJson: _parseInt)  int? tolerance)?  $default,) {final _that = this;\nswitch (_that) {\ncase _ProxyGroup() when $default != null:\nreturn $default(_that.name,_that.type,_that.proxies,_that.use,_that.interval,_that.lazy,_that.url,_that.timeout,_that.maxFailedTimes,_that.filter,_that.excludeFilter,_that.excludeType,_that.expectedStatus,_that.hidden,_that.icon,_that.tolerance);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _ProxyGroup implements ProxyGroup {\n  const _ProxyGroup({required this.name, @JsonKey(fromJson: GroupType.parseProfileType) required this.type, @JsonKey(fromJson: _parseStringList) final  List<String>? proxies, @JsonKey(fromJson: _parseStringList) final  List<String>? use, @JsonKey(fromJson: _parseInt) this.interval, @JsonKey(fromJson: _parseBool) this.lazy, this.url, @JsonKey(fromJson: _parseInt) this.timeout, @JsonKey(name: 'max-failed-times', fromJson: _parseInt) this.maxFailedTimes, this.filter, @JsonKey(name: 'expected-filter') this.excludeFilter, @JsonKey(name: 'exclude-type') this.excludeType, @JsonKey(name: 'expected-status') this.expectedStatus, @JsonKey(fromJson: _parseBool) this.hidden, this.icon, @JsonKey(fromJson: _parseInt) this.tolerance}): _proxies = proxies,_use = use;\n  factory _ProxyGroup.fromJson(Map<String, dynamic> json) => _$ProxyGroupFromJson(json);\n\n@override final  String name;\n@override@JsonKey(fromJson: GroupType.parseProfileType) final  GroupType type;\n final  List<String>? _proxies;\n@override@JsonKey(fromJson: _parseStringList) List<String>? get proxies {\n  final value = _proxies;\n  if (value == null) return null;\n  if (_proxies is EqualUnmodifiableListView) return _proxies;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(value);\n}\n\n final  List<String>? _use;\n@override@JsonKey(fromJson: _parseStringList) List<String>? get use {\n  final value = _use;\n  if (value == null) return null;\n  if (_use is EqualUnmodifiableListView) return _use;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(value);\n}\n\n@override@JsonKey(fromJson: _parseInt) final  int? interval;\n@override@JsonKey(fromJson: _parseBool) final  bool? lazy;\n@override final  String? url;\n@override@JsonKey(fromJson: _parseInt) final  int? timeout;\n@override@JsonKey(name: 'max-failed-times', fromJson: _parseInt) final  int? maxFailedTimes;\n@override final  String? filter;\n@override@JsonKey(name: 'expected-filter') final  String? excludeFilter;\n@override@JsonKey(name: 'exclude-type') final  String? excludeType;\n@override@JsonKey(name: 'expected-status') final  dynamic expectedStatus;\n@override@JsonKey(fromJson: _parseBool) final  bool? hidden;\n@override final  String? icon;\n@override@JsonKey(fromJson: _parseInt) final  int? tolerance;\n\n/// Create a copy of ProxyGroup\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ProxyGroupCopyWith<_ProxyGroup> get copyWith => __$ProxyGroupCopyWithImpl<_ProxyGroup>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$ProxyGroupToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProxyGroup&&(identical(other.name, name) || other.name == name)&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other._proxies, _proxies)&&const DeepCollectionEquality().equals(other._use, _use)&&(identical(other.interval, interval) || other.interval == interval)&&(identical(other.lazy, lazy) || other.lazy == lazy)&&(identical(other.url, url) || other.url == url)&&(identical(other.timeout, timeout) || other.timeout == timeout)&&(identical(other.maxFailedTimes, maxFailedTimes) || other.maxFailedTimes == maxFailedTimes)&&(identical(other.filter, filter) || other.filter == filter)&&(identical(other.excludeFilter, excludeFilter) || other.excludeFilter == excludeFilter)&&(identical(other.excludeType, excludeType) || other.excludeType == excludeType)&&const DeepCollectionEquality().equals(other.expectedStatus, expectedStatus)&&(identical(other.hidden, hidden) || other.hidden == hidden)&&(identical(other.icon, icon) || other.icon == icon)&&(identical(other.tolerance, tolerance) || other.tolerance == tolerance));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,name,type,const DeepCollectionEquality().hash(_proxies),const DeepCollectionEquality().hash(_use),interval,lazy,url,timeout,maxFailedTimes,filter,excludeFilter,excludeType,const DeepCollectionEquality().hash(expectedStatus),hidden,icon,tolerance);\n\n@override\nString toString() {\n  return 'ProxyGroup(name: $name, type: $type, proxies: $proxies, use: $use, interval: $interval, lazy: $lazy, url: $url, timeout: $timeout, maxFailedTimes: $maxFailedTimes, filter: $filter, excludeFilter: $excludeFilter, excludeType: $excludeType, expectedStatus: $expectedStatus, hidden: $hidden, icon: $icon, tolerance: $tolerance)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ProxyGroupCopyWith<$Res> implements $ProxyGroupCopyWith<$Res> {\n  factory _$ProxyGroupCopyWith(_ProxyGroup value, $Res Function(_ProxyGroup) _then) = __$ProxyGroupCopyWithImpl;\n@override @useResult\n$Res call({\n String name,@JsonKey(fromJson: GroupType.parseProfileType) GroupType type,@JsonKey(fromJson: _parseStringList) List<String>? proxies,@JsonKey(fromJson: _parseStringList) List<String>? use,@JsonKey(fromJson: _parseInt) int? interval,@JsonKey(fromJson: _parseBool) bool? lazy, String? url,@JsonKey(fromJson: _parseInt) int? timeout,@JsonKey(name: 'max-failed-times', fromJson: _parseInt) int? maxFailedTimes, String? filter,@JsonKey(name: 'expected-filter') String? excludeFilter,@JsonKey(name: 'exclude-type') String? excludeType,@JsonKey(name: 'expected-status') dynamic expectedStatus,@JsonKey(fromJson: _parseBool) bool? hidden, String? icon,@JsonKey(fromJson: _parseInt) int? tolerance\n});\n\n\n\n\n}\n/// @nodoc\nclass __$ProxyGroupCopyWithImpl<$Res>\n    implements _$ProxyGroupCopyWith<$Res> {\n  __$ProxyGroupCopyWithImpl(this._self, this._then);\n\n  final _ProxyGroup _self;\n  final $Res Function(_ProxyGroup) _then;\n\n/// Create a copy of ProxyGroup\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? name = null,Object? type = null,Object? proxies = freezed,Object? use = freezed,Object? interval = freezed,Object? lazy = freezed,Object? url = freezed,Object? timeout = freezed,Object? maxFailedTimes = freezed,Object? filter = freezed,Object? excludeFilter = freezed,Object? excludeType = freezed,Object? expectedStatus = freezed,Object? hidden = freezed,Object? icon = freezed,Object? tolerance = freezed,}) {\n  return _then(_ProxyGroup(\nname: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable\nas String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable\nas GroupType,proxies: freezed == proxies ? _self._proxies : proxies // ignore: cast_nullable_to_non_nullable\nas List<String>?,use: freezed == use ? _self._use : use // ignore: cast_nullable_to_non_nullable\nas List<String>?,interval: freezed == interval ? _self.interval : interval // ignore: cast_nullable_to_non_nullable\nas int?,lazy: freezed == lazy ? _self.lazy : lazy // ignore: cast_nullable_to_non_nullable\nas bool?,url: freezed == url ? _self.url : url // ignore: cast_nullable_to_non_nullable\nas String?,timeout: freezed == timeout ? _self.timeout : timeout // ignore: cast_nullable_to_non_nullable\nas int?,maxFailedTimes: freezed == maxFailedTimes ? _self.maxFailedTimes : maxFailedTimes // ignore: cast_nullable_to_non_nullable\nas int?,filter: freezed == filter ? _self.filter : filter // ignore: cast_nullable_to_non_nullable\nas String?,excludeFilter: freezed == excludeFilter ? _self.excludeFilter : excludeFilter // ignore: cast_nullable_to_non_nullable\nas String?,excludeType: freezed == excludeType ? _self.excludeType : excludeType // ignore: cast_nullable_to_non_nullable\nas String?,expectedStatus: freezed == expectedStatus ? _self.expectedStatus : expectedStatus // ignore: cast_nullable_to_non_nullable\nas dynamic,hidden: freezed == hidden ? _self.hidden : hidden // ignore: cast_nullable_to_non_nullable\nas bool?,icon: freezed == icon ? _self.icon : icon // ignore: cast_nullable_to_non_nullable\nas String?,tolerance: freezed == tolerance ? _self.tolerance : tolerance // ignore: cast_nullable_to_non_nullable\nas int?,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$RuleProvider {\n\n String get name;\n/// Create a copy of RuleProvider\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$RuleProviderCopyWith<RuleProvider> get copyWith => _$RuleProviderCopyWithImpl<RuleProvider>(this as RuleProvider, _$identity);\n\n  /// Serializes this RuleProvider to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is RuleProvider&&(identical(other.name, name) || other.name == name));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,name);\n\n@override\nString toString() {\n  return 'RuleProvider(name: $name)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $RuleProviderCopyWith<$Res>  {\n  factory $RuleProviderCopyWith(RuleProvider value, $Res Function(RuleProvider) _then) = _$RuleProviderCopyWithImpl;\n@useResult\n$Res call({\n String name\n});\n\n\n\n\n}\n/// @nodoc\nclass _$RuleProviderCopyWithImpl<$Res>\n    implements $RuleProviderCopyWith<$Res> {\n  _$RuleProviderCopyWithImpl(this._self, this._then);\n\n  final RuleProvider _self;\n  final $Res Function(RuleProvider) _then;\n\n/// Create a copy of RuleProvider\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? name = null,}) {\n  return _then(_self.copyWith(\nname: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable\nas String,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [RuleProvider].\nextension RuleProviderPatterns on RuleProvider {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _RuleProvider value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _RuleProvider() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _RuleProvider value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _RuleProvider():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _RuleProvider value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _RuleProvider() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String name)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _RuleProvider() when $default != null:\nreturn $default(_that.name);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String name)  $default,) {final _that = this;\nswitch (_that) {\ncase _RuleProvider():\nreturn $default(_that.name);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String name)?  $default,) {final _that = this;\nswitch (_that) {\ncase _RuleProvider() when $default != null:\nreturn $default(_that.name);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _RuleProvider implements RuleProvider {\n  const _RuleProvider({required this.name});\n  factory _RuleProvider.fromJson(Map<String, dynamic> json) => _$RuleProviderFromJson(json);\n\n@override final  String name;\n\n/// Create a copy of RuleProvider\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$RuleProviderCopyWith<_RuleProvider> get copyWith => __$RuleProviderCopyWithImpl<_RuleProvider>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$RuleProviderToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _RuleProvider&&(identical(other.name, name) || other.name == name));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,name);\n\n@override\nString toString() {\n  return 'RuleProvider(name: $name)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$RuleProviderCopyWith<$Res> implements $RuleProviderCopyWith<$Res> {\n  factory _$RuleProviderCopyWith(_RuleProvider value, $Res Function(_RuleProvider) _then) = __$RuleProviderCopyWithImpl;\n@override @useResult\n$Res call({\n String name\n});\n\n\n\n\n}\n/// @nodoc\nclass __$RuleProviderCopyWithImpl<$Res>\n    implements _$RuleProviderCopyWith<$Res> {\n  __$RuleProviderCopyWithImpl(this._self, this._then);\n\n  final _RuleProvider _self;\n  final $Res Function(_RuleProvider) _then;\n\n/// Create a copy of RuleProvider\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? name = null,}) {\n  return _then(_RuleProvider(\nname: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable\nas String,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$Sniffer {\n\n bool get enable;@JsonKey(name: 'override-destination') bool get overrideDest; List<String> get sniffing;@JsonKey(name: 'force-domain') List<String> get forceDomain;@JsonKey(name: 'skip-src-address') List<String> get skipSrcAddress;@JsonKey(name: 'skip-dst-address') List<String> get skipDstAddress;@JsonKey(name: 'skip-domain') List<String> get skipDomain;@JsonKey(name: 'port-whitelist') List<String> get port;@JsonKey(name: 'force-dns-mapping') bool get forceDnsMapping;@JsonKey(name: 'parse-pure-ip') bool get parsePureIp; Map<String, SnifferConfig> get sniff;\n/// Create a copy of Sniffer\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$SnifferCopyWith<Sniffer> get copyWith => _$SnifferCopyWithImpl<Sniffer>(this as Sniffer, _$identity);\n\n  /// Serializes this Sniffer to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is Sniffer&&(identical(other.enable, enable) || other.enable == enable)&&(identical(other.overrideDest, overrideDest) || other.overrideDest == overrideDest)&&const DeepCollectionEquality().equals(other.sniffing, sniffing)&&const DeepCollectionEquality().equals(other.forceDomain, forceDomain)&&const DeepCollectionEquality().equals(other.skipSrcAddress, skipSrcAddress)&&const DeepCollectionEquality().equals(other.skipDstAddress, skipDstAddress)&&const DeepCollectionEquality().equals(other.skipDomain, skipDomain)&&const DeepCollectionEquality().equals(other.port, port)&&(identical(other.forceDnsMapping, forceDnsMapping) || other.forceDnsMapping == forceDnsMapping)&&(identical(other.parsePureIp, parsePureIp) || other.parsePureIp == parsePureIp)&&const DeepCollectionEquality().equals(other.sniff, sniff));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,enable,overrideDest,const DeepCollectionEquality().hash(sniffing),const DeepCollectionEquality().hash(forceDomain),const DeepCollectionEquality().hash(skipSrcAddress),const DeepCollectionEquality().hash(skipDstAddress),const DeepCollectionEquality().hash(skipDomain),const DeepCollectionEquality().hash(port),forceDnsMapping,parsePureIp,const DeepCollectionEquality().hash(sniff));\n\n@override\nString toString() {\n  return 'Sniffer(enable: $enable, overrideDest: $overrideDest, sniffing: $sniffing, forceDomain: $forceDomain, skipSrcAddress: $skipSrcAddress, skipDstAddress: $skipDstAddress, skipDomain: $skipDomain, port: $port, forceDnsMapping: $forceDnsMapping, parsePureIp: $parsePureIp, sniff: $sniff)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $SnifferCopyWith<$Res>  {\n  factory $SnifferCopyWith(Sniffer value, $Res Function(Sniffer) _then) = _$SnifferCopyWithImpl;\n@useResult\n$Res call({\n bool enable,@JsonKey(name: 'override-destination') bool overrideDest, List<String> sniffing,@JsonKey(name: 'force-domain') List<String> forceDomain,@JsonKey(name: 'skip-src-address') List<String> skipSrcAddress,@JsonKey(name: 'skip-dst-address') List<String> skipDstAddress,@JsonKey(name: 'skip-domain') List<String> skipDomain,@JsonKey(name: 'port-whitelist') List<String> port,@JsonKey(name: 'force-dns-mapping') bool forceDnsMapping,@JsonKey(name: 'parse-pure-ip') bool parsePureIp, Map<String, SnifferConfig> sniff\n});\n\n\n\n\n}\n/// @nodoc\nclass _$SnifferCopyWithImpl<$Res>\n    implements $SnifferCopyWith<$Res> {\n  _$SnifferCopyWithImpl(this._self, this._then);\n\n  final Sniffer _self;\n  final $Res Function(Sniffer) _then;\n\n/// Create a copy of Sniffer\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? enable = null,Object? overrideDest = null,Object? sniffing = null,Object? forceDomain = null,Object? skipSrcAddress = null,Object? skipDstAddress = null,Object? skipDomain = null,Object? port = null,Object? forceDnsMapping = null,Object? parsePureIp = null,Object? sniff = null,}) {\n  return _then(_self.copyWith(\nenable: null == enable ? _self.enable : enable // ignore: cast_nullable_to_non_nullable\nas bool,overrideDest: null == overrideDest ? _self.overrideDest : overrideDest // ignore: cast_nullable_to_non_nullable\nas bool,sniffing: null == sniffing ? _self.sniffing : sniffing // ignore: cast_nullable_to_non_nullable\nas List<String>,forceDomain: null == forceDomain ? _self.forceDomain : forceDomain // ignore: cast_nullable_to_non_nullable\nas List<String>,skipSrcAddress: null == skipSrcAddress ? _self.skipSrcAddress : skipSrcAddress // ignore: cast_nullable_to_non_nullable\nas List<String>,skipDstAddress: null == skipDstAddress ? _self.skipDstAddress : skipDstAddress // ignore: cast_nullable_to_non_nullable\nas List<String>,skipDomain: null == skipDomain ? _self.skipDomain : skipDomain // ignore: cast_nullable_to_non_nullable\nas List<String>,port: null == port ? _self.port : port // ignore: cast_nullable_to_non_nullable\nas List<String>,forceDnsMapping: null == forceDnsMapping ? _self.forceDnsMapping : forceDnsMapping // ignore: cast_nullable_to_non_nullable\nas bool,parsePureIp: null == parsePureIp ? _self.parsePureIp : parsePureIp // ignore: cast_nullable_to_non_nullable\nas bool,sniff: null == sniff ? _self.sniff : sniff // ignore: cast_nullable_to_non_nullable\nas Map<String, SnifferConfig>,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [Sniffer].\nextension SnifferPatterns on Sniffer {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _Sniffer value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _Sniffer() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _Sniffer value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Sniffer():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _Sniffer value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Sniffer() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool enable, @JsonKey(name: 'override-destination')  bool overrideDest,  List<String> sniffing, @JsonKey(name: 'force-domain')  List<String> forceDomain, @JsonKey(name: 'skip-src-address')  List<String> skipSrcAddress, @JsonKey(name: 'skip-dst-address')  List<String> skipDstAddress, @JsonKey(name: 'skip-domain')  List<String> skipDomain, @JsonKey(name: 'port-whitelist')  List<String> port, @JsonKey(name: 'force-dns-mapping')  bool forceDnsMapping, @JsonKey(name: 'parse-pure-ip')  bool parsePureIp,  Map<String, SnifferConfig> sniff)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _Sniffer() when $default != null:\nreturn $default(_that.enable,_that.overrideDest,_that.sniffing,_that.forceDomain,_that.skipSrcAddress,_that.skipDstAddress,_that.skipDomain,_that.port,_that.forceDnsMapping,_that.parsePureIp,_that.sniff);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool enable, @JsonKey(name: 'override-destination')  bool overrideDest,  List<String> sniffing, @JsonKey(name: 'force-domain')  List<String> forceDomain, @JsonKey(name: 'skip-src-address')  List<String> skipSrcAddress, @JsonKey(name: 'skip-dst-address')  List<String> skipDstAddress, @JsonKey(name: 'skip-domain')  List<String> skipDomain, @JsonKey(name: 'port-whitelist')  List<String> port, @JsonKey(name: 'force-dns-mapping')  bool forceDnsMapping, @JsonKey(name: 'parse-pure-ip')  bool parsePureIp,  Map<String, SnifferConfig> sniff)  $default,) {final _that = this;\nswitch (_that) {\ncase _Sniffer():\nreturn $default(_that.enable,_that.overrideDest,_that.sniffing,_that.forceDomain,_that.skipSrcAddress,_that.skipDstAddress,_that.skipDomain,_that.port,_that.forceDnsMapping,_that.parsePureIp,_that.sniff);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool enable, @JsonKey(name: 'override-destination')  bool overrideDest,  List<String> sniffing, @JsonKey(name: 'force-domain')  List<String> forceDomain, @JsonKey(name: 'skip-src-address')  List<String> skipSrcAddress, @JsonKey(name: 'skip-dst-address')  List<String> skipDstAddress, @JsonKey(name: 'skip-domain')  List<String> skipDomain, @JsonKey(name: 'port-whitelist')  List<String> port, @JsonKey(name: 'force-dns-mapping')  bool forceDnsMapping, @JsonKey(name: 'parse-pure-ip')  bool parsePureIp,  Map<String, SnifferConfig> sniff)?  $default,) {final _that = this;\nswitch (_that) {\ncase _Sniffer() when $default != null:\nreturn $default(_that.enable,_that.overrideDest,_that.sniffing,_that.forceDomain,_that.skipSrcAddress,_that.skipDstAddress,_that.skipDomain,_that.port,_that.forceDnsMapping,_that.parsePureIp,_that.sniff);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _Sniffer implements Sniffer {\n  const _Sniffer({this.enable = true, @JsonKey(name: 'override-destination') this.overrideDest = false, final  List<String> sniffing = const [], @JsonKey(name: 'force-domain') final  List<String> forceDomain = const ['+.v2ex.com'], @JsonKey(name: 'skip-src-address') final  List<String> skipSrcAddress = const ['192.168.0.3/32'], @JsonKey(name: 'skip-dst-address') final  List<String> skipDstAddress = const ['91.108.56.0/22', '91.108.4.0/22', '91.108.8.0/22', '91.108.16.0/22', '91.108.12.0/22', '149.154.160.0/20', '91.105.192.0/23', '91.108.20.0/22', '185.76.151.0/24', '2001:b28:f23d::/48', '2001:b28:f23f::/48', '2001:67c:4e8::/48', '2001:b28:f23c::/48', '2a0a:f280::/32'], @JsonKey(name: 'skip-domain') final  List<String> skipDomain = const ['Mijia Cloud', '+.push.apple.com'], @JsonKey(name: 'port-whitelist') final  List<String> port = const [], @JsonKey(name: 'force-dns-mapping') this.forceDnsMapping = true, @JsonKey(name: 'parse-pure-ip') this.parsePureIp = true, final  Map<String, SnifferConfig> sniff = const {'HTTP' : SnifferConfig(ports: ['80', '8080-8880'], overrideDest: true), 'TLS' : SnifferConfig(ports: ['443', '8443']), 'QUIC' : SnifferConfig(ports: ['443', '8443'])}}): _sniffing = sniffing,_forceDomain = forceDomain,_skipSrcAddress = skipSrcAddress,_skipDstAddress = skipDstAddress,_skipDomain = skipDomain,_port = port,_sniff = sniff;\n  factory _Sniffer.fromJson(Map<String, dynamic> json) => _$SnifferFromJson(json);\n\n@override@JsonKey() final  bool enable;\n@override@JsonKey(name: 'override-destination') final  bool overrideDest;\n final  List<String> _sniffing;\n@override@JsonKey() List<String> get sniffing {\n  if (_sniffing is EqualUnmodifiableListView) return _sniffing;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_sniffing);\n}\n\n final  List<String> _forceDomain;\n@override@JsonKey(name: 'force-domain') List<String> get forceDomain {\n  if (_forceDomain is EqualUnmodifiableListView) return _forceDomain;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_forceDomain);\n}\n\n final  List<String> _skipSrcAddress;\n@override@JsonKey(name: 'skip-src-address') List<String> get skipSrcAddress {\n  if (_skipSrcAddress is EqualUnmodifiableListView) return _skipSrcAddress;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_skipSrcAddress);\n}\n\n final  List<String> _skipDstAddress;\n@override@JsonKey(name: 'skip-dst-address') List<String> get skipDstAddress {\n  if (_skipDstAddress is EqualUnmodifiableListView) return _skipDstAddress;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_skipDstAddress);\n}\n\n final  List<String> _skipDomain;\n@override@JsonKey(name: 'skip-domain') List<String> get skipDomain {\n  if (_skipDomain is EqualUnmodifiableListView) return _skipDomain;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_skipDomain);\n}\n\n final  List<String> _port;\n@override@JsonKey(name: 'port-whitelist') List<String> get port {\n  if (_port is EqualUnmodifiableListView) return _port;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_port);\n}\n\n@override@JsonKey(name: 'force-dns-mapping') final  bool forceDnsMapping;\n@override@JsonKey(name: 'parse-pure-ip') final  bool parsePureIp;\n final  Map<String, SnifferConfig> _sniff;\n@override@JsonKey() Map<String, SnifferConfig> get sniff {\n  if (_sniff is EqualUnmodifiableMapView) return _sniff;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableMapView(_sniff);\n}\n\n\n/// Create a copy of Sniffer\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$SnifferCopyWith<_Sniffer> get copyWith => __$SnifferCopyWithImpl<_Sniffer>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$SnifferToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _Sniffer&&(identical(other.enable, enable) || other.enable == enable)&&(identical(other.overrideDest, overrideDest) || other.overrideDest == overrideDest)&&const DeepCollectionEquality().equals(other._sniffing, _sniffing)&&const DeepCollectionEquality().equals(other._forceDomain, _forceDomain)&&const DeepCollectionEquality().equals(other._skipSrcAddress, _skipSrcAddress)&&const DeepCollectionEquality().equals(other._skipDstAddress, _skipDstAddress)&&const DeepCollectionEquality().equals(other._skipDomain, _skipDomain)&&const DeepCollectionEquality().equals(other._port, _port)&&(identical(other.forceDnsMapping, forceDnsMapping) || other.forceDnsMapping == forceDnsMapping)&&(identical(other.parsePureIp, parsePureIp) || other.parsePureIp == parsePureIp)&&const DeepCollectionEquality().equals(other._sniff, _sniff));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,enable,overrideDest,const DeepCollectionEquality().hash(_sniffing),const DeepCollectionEquality().hash(_forceDomain),const DeepCollectionEquality().hash(_skipSrcAddress),const DeepCollectionEquality().hash(_skipDstAddress),const DeepCollectionEquality().hash(_skipDomain),const DeepCollectionEquality().hash(_port),forceDnsMapping,parsePureIp,const DeepCollectionEquality().hash(_sniff));\n\n@override\nString toString() {\n  return 'Sniffer(enable: $enable, overrideDest: $overrideDest, sniffing: $sniffing, forceDomain: $forceDomain, skipSrcAddress: $skipSrcAddress, skipDstAddress: $skipDstAddress, skipDomain: $skipDomain, port: $port, forceDnsMapping: $forceDnsMapping, parsePureIp: $parsePureIp, sniff: $sniff)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$SnifferCopyWith<$Res> implements $SnifferCopyWith<$Res> {\n  factory _$SnifferCopyWith(_Sniffer value, $Res Function(_Sniffer) _then) = __$SnifferCopyWithImpl;\n@override @useResult\n$Res call({\n bool enable,@JsonKey(name: 'override-destination') bool overrideDest, List<String> sniffing,@JsonKey(name: 'force-domain') List<String> forceDomain,@JsonKey(name: 'skip-src-address') List<String> skipSrcAddress,@JsonKey(name: 'skip-dst-address') List<String> skipDstAddress,@JsonKey(name: 'skip-domain') List<String> skipDomain,@JsonKey(name: 'port-whitelist') List<String> port,@JsonKey(name: 'force-dns-mapping') bool forceDnsMapping,@JsonKey(name: 'parse-pure-ip') bool parsePureIp, Map<String, SnifferConfig> sniff\n});\n\n\n\n\n}\n/// @nodoc\nclass __$SnifferCopyWithImpl<$Res>\n    implements _$SnifferCopyWith<$Res> {\n  __$SnifferCopyWithImpl(this._self, this._then);\n\n  final _Sniffer _self;\n  final $Res Function(_Sniffer) _then;\n\n/// Create a copy of Sniffer\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? enable = null,Object? overrideDest = null,Object? sniffing = null,Object? forceDomain = null,Object? skipSrcAddress = null,Object? skipDstAddress = null,Object? skipDomain = null,Object? port = null,Object? forceDnsMapping = null,Object? parsePureIp = null,Object? sniff = null,}) {\n  return _then(_Sniffer(\nenable: null == enable ? _self.enable : enable // ignore: cast_nullable_to_non_nullable\nas bool,overrideDest: null == overrideDest ? _self.overrideDest : overrideDest // ignore: cast_nullable_to_non_nullable\nas bool,sniffing: null == sniffing ? _self._sniffing : sniffing // ignore: cast_nullable_to_non_nullable\nas List<String>,forceDomain: null == forceDomain ? _self._forceDomain : forceDomain // ignore: cast_nullable_to_non_nullable\nas List<String>,skipSrcAddress: null == skipSrcAddress ? _self._skipSrcAddress : skipSrcAddress // ignore: cast_nullable_to_non_nullable\nas List<String>,skipDstAddress: null == skipDstAddress ? _self._skipDstAddress : skipDstAddress // ignore: cast_nullable_to_non_nullable\nas List<String>,skipDomain: null == skipDomain ? _self._skipDomain : skipDomain // ignore: cast_nullable_to_non_nullable\nas List<String>,port: null == port ? _self._port : port // ignore: cast_nullable_to_non_nullable\nas List<String>,forceDnsMapping: null == forceDnsMapping ? _self.forceDnsMapping : forceDnsMapping // ignore: cast_nullable_to_non_nullable\nas bool,parsePureIp: null == parsePureIp ? _self.parsePureIp : parsePureIp // ignore: cast_nullable_to_non_nullable\nas bool,sniff: null == sniff ? _self._sniff : sniff // ignore: cast_nullable_to_non_nullable\nas Map<String, SnifferConfig>,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$TunnelEntry {\n\n String get id; List<String>? get network; String? get address; String? get target; String? get proxyName;\n/// Create a copy of TunnelEntry\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$TunnelEntryCopyWith<TunnelEntry> get copyWith => _$TunnelEntryCopyWithImpl<TunnelEntry>(this as TunnelEntry, _$identity);\n\n  /// Serializes this TunnelEntry to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is TunnelEntry&&(identical(other.id, id) || other.id == id)&&const DeepCollectionEquality().equals(other.network, network)&&(identical(other.address, address) || other.address == address)&&(identical(other.target, target) || other.target == target)&&(identical(other.proxyName, proxyName) || other.proxyName == proxyName));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,id,const DeepCollectionEquality().hash(network),address,target,proxyName);\n\n@override\nString toString() {\n  return 'TunnelEntry(id: $id, network: $network, address: $address, target: $target, proxyName: $proxyName)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $TunnelEntryCopyWith<$Res>  {\n  factory $TunnelEntryCopyWith(TunnelEntry value, $Res Function(TunnelEntry) _then) = _$TunnelEntryCopyWithImpl;\n@useResult\n$Res call({\n String id, List<String>? network, String? address, String? target, String? proxyName\n});\n\n\n\n\n}\n/// @nodoc\nclass _$TunnelEntryCopyWithImpl<$Res>\n    implements $TunnelEntryCopyWith<$Res> {\n  _$TunnelEntryCopyWithImpl(this._self, this._then);\n\n  final TunnelEntry _self;\n  final $Res Function(TunnelEntry) _then;\n\n/// Create a copy of TunnelEntry\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? network = freezed,Object? address = freezed,Object? target = freezed,Object? proxyName = freezed,}) {\n  return _then(_self.copyWith(\nid: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable\nas String,network: freezed == network ? _self.network : network // ignore: cast_nullable_to_non_nullable\nas List<String>?,address: freezed == address ? _self.address : address // ignore: cast_nullable_to_non_nullable\nas String?,target: freezed == target ? _self.target : target // ignore: cast_nullable_to_non_nullable\nas String?,proxyName: freezed == proxyName ? _self.proxyName : proxyName // ignore: cast_nullable_to_non_nullable\nas String?,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [TunnelEntry].\nextension TunnelEntryPatterns on TunnelEntry {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _TunnelEntry value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _TunnelEntry() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _TunnelEntry value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _TunnelEntry():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _TunnelEntry value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _TunnelEntry() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id,  List<String>? network,  String? address,  String? target,  String? proxyName)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _TunnelEntry() when $default != null:\nreturn $default(_that.id,_that.network,_that.address,_that.target,_that.proxyName);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id,  List<String>? network,  String? address,  String? target,  String? proxyName)  $default,) {final _that = this;\nswitch (_that) {\ncase _TunnelEntry():\nreturn $default(_that.id,_that.network,_that.address,_that.target,_that.proxyName);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id,  List<String>? network,  String? address,  String? target,  String? proxyName)?  $default,) {final _that = this;\nswitch (_that) {\ncase _TunnelEntry() when $default != null:\nreturn $default(_that.id,_that.network,_that.address,_that.target,_that.proxyName);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _TunnelEntry implements TunnelEntry {\n  const _TunnelEntry({required this.id, final  List<String>? network, this.address, this.target, this.proxyName}): _network = network;\n  factory _TunnelEntry.fromJson(Map<String, dynamic> json) => _$TunnelEntryFromJson(json);\n\n@override final  String id;\n final  List<String>? _network;\n@override List<String>? get network {\n  final value = _network;\n  if (value == null) return null;\n  if (_network is EqualUnmodifiableListView) return _network;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(value);\n}\n\n@override final  String? address;\n@override final  String? target;\n@override final  String? proxyName;\n\n/// Create a copy of TunnelEntry\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$TunnelEntryCopyWith<_TunnelEntry> get copyWith => __$TunnelEntryCopyWithImpl<_TunnelEntry>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$TunnelEntryToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _TunnelEntry&&(identical(other.id, id) || other.id == id)&&const DeepCollectionEquality().equals(other._network, _network)&&(identical(other.address, address) || other.address == address)&&(identical(other.target, target) || other.target == target)&&(identical(other.proxyName, proxyName) || other.proxyName == proxyName));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,id,const DeepCollectionEquality().hash(_network),address,target,proxyName);\n\n@override\nString toString() {\n  return 'TunnelEntry(id: $id, network: $network, address: $address, target: $target, proxyName: $proxyName)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$TunnelEntryCopyWith<$Res> implements $TunnelEntryCopyWith<$Res> {\n  factory _$TunnelEntryCopyWith(_TunnelEntry value, $Res Function(_TunnelEntry) _then) = __$TunnelEntryCopyWithImpl;\n@override @useResult\n$Res call({\n String id, List<String>? network, String? address, String? target, String? proxyName\n});\n\n\n\n\n}\n/// @nodoc\nclass __$TunnelEntryCopyWithImpl<$Res>\n    implements _$TunnelEntryCopyWith<$Res> {\n  __$TunnelEntryCopyWithImpl(this._self, this._then);\n\n  final _TunnelEntry _self;\n  final $Res Function(_TunnelEntry) _then;\n\n/// Create a copy of TunnelEntry\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? network = freezed,Object? address = freezed,Object? target = freezed,Object? proxyName = freezed,}) {\n  return _then(_TunnelEntry(\nid: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable\nas String,network: freezed == network ? _self._network : network // ignore: cast_nullable_to_non_nullable\nas List<String>?,address: freezed == address ? _self.address : address // ignore: cast_nullable_to_non_nullable\nas String?,target: freezed == target ? _self.target : target // ignore: cast_nullable_to_non_nullable\nas String?,proxyName: freezed == proxyName ? _self.proxyName : proxyName // ignore: cast_nullable_to_non_nullable\nas String?,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$SnifferConfig {\n\n@JsonKey(fromJson: _formJsonPorts) List<String> get ports;@JsonKey(name: 'override-destination') bool? get overrideDest;\n/// Create a copy of SnifferConfig\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$SnifferConfigCopyWith<SnifferConfig> get copyWith => _$SnifferConfigCopyWithImpl<SnifferConfig>(this as SnifferConfig, _$identity);\n\n  /// Serializes this SnifferConfig to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is SnifferConfig&&const DeepCollectionEquality().equals(other.ports, ports)&&(identical(other.overrideDest, overrideDest) || other.overrideDest == overrideDest));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(ports),overrideDest);\n\n@override\nString toString() {\n  return 'SnifferConfig(ports: $ports, overrideDest: $overrideDest)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $SnifferConfigCopyWith<$Res>  {\n  factory $SnifferConfigCopyWith(SnifferConfig value, $Res Function(SnifferConfig) _then) = _$SnifferConfigCopyWithImpl;\n@useResult\n$Res call({\n@JsonKey(fromJson: _formJsonPorts) List<String> ports,@JsonKey(name: 'override-destination') bool? overrideDest\n});\n\n\n\n\n}\n/// @nodoc\nclass _$SnifferConfigCopyWithImpl<$Res>\n    implements $SnifferConfigCopyWith<$Res> {\n  _$SnifferConfigCopyWithImpl(this._self, this._then);\n\n  final SnifferConfig _self;\n  final $Res Function(SnifferConfig) _then;\n\n/// Create a copy of SnifferConfig\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? ports = null,Object? overrideDest = freezed,}) {\n  return _then(_self.copyWith(\nports: null == ports ? _self.ports : ports // ignore: cast_nullable_to_non_nullable\nas List<String>,overrideDest: freezed == overrideDest ? _self.overrideDest : overrideDest // ignore: cast_nullable_to_non_nullable\nas bool?,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [SnifferConfig].\nextension SnifferConfigPatterns on SnifferConfig {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SnifferConfig value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _SnifferConfig() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SnifferConfig value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _SnifferConfig():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SnifferConfig value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _SnifferConfig() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(@JsonKey(fromJson: _formJsonPorts)  List<String> ports, @JsonKey(name: 'override-destination')  bool? overrideDest)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _SnifferConfig() when $default != null:\nreturn $default(_that.ports,_that.overrideDest);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(@JsonKey(fromJson: _formJsonPorts)  List<String> ports, @JsonKey(name: 'override-destination')  bool? overrideDest)  $default,) {final _that = this;\nswitch (_that) {\ncase _SnifferConfig():\nreturn $default(_that.ports,_that.overrideDest);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(@JsonKey(fromJson: _formJsonPorts)  List<String> ports, @JsonKey(name: 'override-destination')  bool? overrideDest)?  $default,) {final _that = this;\nswitch (_that) {\ncase _SnifferConfig() when $default != null:\nreturn $default(_that.ports,_that.overrideDest);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _SnifferConfig implements SnifferConfig {\n  const _SnifferConfig({@JsonKey(fromJson: _formJsonPorts) final  List<String> ports = const [], @JsonKey(name: 'override-destination') this.overrideDest}): _ports = ports;\n  factory _SnifferConfig.fromJson(Map<String, dynamic> json) => _$SnifferConfigFromJson(json);\n\n final  List<String> _ports;\n@override@JsonKey(fromJson: _formJsonPorts) List<String> get ports {\n  if (_ports is EqualUnmodifiableListView) return _ports;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_ports);\n}\n\n@override@JsonKey(name: 'override-destination') final  bool? overrideDest;\n\n/// Create a copy of SnifferConfig\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$SnifferConfigCopyWith<_SnifferConfig> get copyWith => __$SnifferConfigCopyWithImpl<_SnifferConfig>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$SnifferConfigToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _SnifferConfig&&const DeepCollectionEquality().equals(other._ports, _ports)&&(identical(other.overrideDest, overrideDest) || other.overrideDest == overrideDest));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_ports),overrideDest);\n\n@override\nString toString() {\n  return 'SnifferConfig(ports: $ports, overrideDest: $overrideDest)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$SnifferConfigCopyWith<$Res> implements $SnifferConfigCopyWith<$Res> {\n  factory _$SnifferConfigCopyWith(_SnifferConfig value, $Res Function(_SnifferConfig) _then) = __$SnifferConfigCopyWithImpl;\n@override @useResult\n$Res call({\n@JsonKey(fromJson: _formJsonPorts) List<String> ports,@JsonKey(name: 'override-destination') bool? overrideDest\n});\n\n\n\n\n}\n/// @nodoc\nclass __$SnifferConfigCopyWithImpl<$Res>\n    implements _$SnifferConfigCopyWith<$Res> {\n  __$SnifferConfigCopyWithImpl(this._self, this._then);\n\n  final _SnifferConfig _self;\n  final $Res Function(_SnifferConfig) _then;\n\n/// Create a copy of SnifferConfig\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? ports = null,Object? overrideDest = freezed,}) {\n  return _then(_SnifferConfig(\nports: null == ports ? _self._ports : ports // ignore: cast_nullable_to_non_nullable\nas List<String>,overrideDest: freezed == overrideDest ? _self.overrideDest : overrideDest // ignore: cast_nullable_to_non_nullable\nas bool?,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$Tun {\n\n bool get enable; String get device;@JsonKey(name: 'auto-route') bool get autoRoute; TunStack get stack;@JsonKey(name: 'dns-hijack') List<String> get dnsHijack;@JsonKey(name: 'route-address') List<String> get routeAddress;@JsonKey(name: 'route-exclude-address') List<String> get routeExcludeAddress;@JsonKey(name: 'strict-route') bool get strictRoute;@JsonKey(name: 'disable-icmp-forwarding') bool get disableIcmpForwarding; int get mtu;@JsonKey(name: 'endpoint-independent-nat') bool get endpointIndependentNat;\n/// Create a copy of Tun\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$TunCopyWith<Tun> get copyWith => _$TunCopyWithImpl<Tun>(this as Tun, _$identity);\n\n  /// Serializes this Tun to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is Tun&&(identical(other.enable, enable) || other.enable == enable)&&(identical(other.device, device) || other.device == device)&&(identical(other.autoRoute, autoRoute) || other.autoRoute == autoRoute)&&(identical(other.stack, stack) || other.stack == stack)&&const DeepCollectionEquality().equals(other.dnsHijack, dnsHijack)&&const DeepCollectionEquality().equals(other.routeAddress, routeAddress)&&const DeepCollectionEquality().equals(other.routeExcludeAddress, routeExcludeAddress)&&(identical(other.strictRoute, strictRoute) || other.strictRoute == strictRoute)&&(identical(other.disableIcmpForwarding, disableIcmpForwarding) || other.disableIcmpForwarding == disableIcmpForwarding)&&(identical(other.mtu, mtu) || other.mtu == mtu)&&(identical(other.endpointIndependentNat, endpointIndependentNat) || other.endpointIndependentNat == endpointIndependentNat));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,enable,device,autoRoute,stack,const DeepCollectionEquality().hash(dnsHijack),const DeepCollectionEquality().hash(routeAddress),const DeepCollectionEquality().hash(routeExcludeAddress),strictRoute,disableIcmpForwarding,mtu,endpointIndependentNat);\n\n@override\nString toString() {\n  return 'Tun(enable: $enable, device: $device, autoRoute: $autoRoute, stack: $stack, dnsHijack: $dnsHijack, routeAddress: $routeAddress, routeExcludeAddress: $routeExcludeAddress, strictRoute: $strictRoute, disableIcmpForwarding: $disableIcmpForwarding, mtu: $mtu, endpointIndependentNat: $endpointIndependentNat)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $TunCopyWith<$Res>  {\n  factory $TunCopyWith(Tun value, $Res Function(Tun) _then) = _$TunCopyWithImpl;\n@useResult\n$Res call({\n bool enable, String device,@JsonKey(name: 'auto-route') bool autoRoute, TunStack stack,@JsonKey(name: 'dns-hijack') List<String> dnsHijack,@JsonKey(name: 'route-address') List<String> routeAddress,@JsonKey(name: 'route-exclude-address') List<String> routeExcludeAddress,@JsonKey(name: 'strict-route') bool strictRoute,@JsonKey(name: 'disable-icmp-forwarding') bool disableIcmpForwarding, int mtu,@JsonKey(name: 'endpoint-independent-nat') bool endpointIndependentNat\n});\n\n\n\n\n}\n/// @nodoc\nclass _$TunCopyWithImpl<$Res>\n    implements $TunCopyWith<$Res> {\n  _$TunCopyWithImpl(this._self, this._then);\n\n  final Tun _self;\n  final $Res Function(Tun) _then;\n\n/// Create a copy of Tun\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? enable = null,Object? device = null,Object? autoRoute = null,Object? stack = null,Object? dnsHijack = null,Object? routeAddress = null,Object? routeExcludeAddress = null,Object? strictRoute = null,Object? disableIcmpForwarding = null,Object? mtu = null,Object? endpointIndependentNat = null,}) {\n  return _then(_self.copyWith(\nenable: null == enable ? _self.enable : enable // ignore: cast_nullable_to_non_nullable\nas bool,device: null == device ? _self.device : device // ignore: cast_nullable_to_non_nullable\nas String,autoRoute: null == autoRoute ? _self.autoRoute : autoRoute // ignore: cast_nullable_to_non_nullable\nas bool,stack: null == stack ? _self.stack : stack // ignore: cast_nullable_to_non_nullable\nas TunStack,dnsHijack: null == dnsHijack ? _self.dnsHijack : dnsHijack // ignore: cast_nullable_to_non_nullable\nas List<String>,routeAddress: null == routeAddress ? _self.routeAddress : routeAddress // ignore: cast_nullable_to_non_nullable\nas List<String>,routeExcludeAddress: null == routeExcludeAddress ? _self.routeExcludeAddress : routeExcludeAddress // ignore: cast_nullable_to_non_nullable\nas List<String>,strictRoute: null == strictRoute ? _self.strictRoute : strictRoute // ignore: cast_nullable_to_non_nullable\nas bool,disableIcmpForwarding: null == disableIcmpForwarding ? _self.disableIcmpForwarding : disableIcmpForwarding // ignore: cast_nullable_to_non_nullable\nas bool,mtu: null == mtu ? _self.mtu : mtu // ignore: cast_nullable_to_non_nullable\nas int,endpointIndependentNat: null == endpointIndependentNat ? _self.endpointIndependentNat : endpointIndependentNat // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [Tun].\nextension TunPatterns on Tun {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _Tun value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _Tun() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _Tun value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Tun():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _Tun value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Tun() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool enable,  String device, @JsonKey(name: 'auto-route')  bool autoRoute,  TunStack stack, @JsonKey(name: 'dns-hijack')  List<String> dnsHijack, @JsonKey(name: 'route-address')  List<String> routeAddress, @JsonKey(name: 'route-exclude-address')  List<String> routeExcludeAddress, @JsonKey(name: 'strict-route')  bool strictRoute, @JsonKey(name: 'disable-icmp-forwarding')  bool disableIcmpForwarding,  int mtu, @JsonKey(name: 'endpoint-independent-nat')  bool endpointIndependentNat)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _Tun() when $default != null:\nreturn $default(_that.enable,_that.device,_that.autoRoute,_that.stack,_that.dnsHijack,_that.routeAddress,_that.routeExcludeAddress,_that.strictRoute,_that.disableIcmpForwarding,_that.mtu,_that.endpointIndependentNat);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool enable,  String device, @JsonKey(name: 'auto-route')  bool autoRoute,  TunStack stack, @JsonKey(name: 'dns-hijack')  List<String> dnsHijack, @JsonKey(name: 'route-address')  List<String> routeAddress, @JsonKey(name: 'route-exclude-address')  List<String> routeExcludeAddress, @JsonKey(name: 'strict-route')  bool strictRoute, @JsonKey(name: 'disable-icmp-forwarding')  bool disableIcmpForwarding,  int mtu, @JsonKey(name: 'endpoint-independent-nat')  bool endpointIndependentNat)  $default,) {final _that = this;\nswitch (_that) {\ncase _Tun():\nreturn $default(_that.enable,_that.device,_that.autoRoute,_that.stack,_that.dnsHijack,_that.routeAddress,_that.routeExcludeAddress,_that.strictRoute,_that.disableIcmpForwarding,_that.mtu,_that.endpointIndependentNat);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool enable,  String device, @JsonKey(name: 'auto-route')  bool autoRoute,  TunStack stack, @JsonKey(name: 'dns-hijack')  List<String> dnsHijack, @JsonKey(name: 'route-address')  List<String> routeAddress, @JsonKey(name: 'route-exclude-address')  List<String> routeExcludeAddress, @JsonKey(name: 'strict-route')  bool strictRoute, @JsonKey(name: 'disable-icmp-forwarding')  bool disableIcmpForwarding,  int mtu, @JsonKey(name: 'endpoint-independent-nat')  bool endpointIndependentNat)?  $default,) {final _that = this;\nswitch (_that) {\ncase _Tun() when $default != null:\nreturn $default(_that.enable,_that.device,_that.autoRoute,_that.stack,_that.dnsHijack,_that.routeAddress,_that.routeExcludeAddress,_that.strictRoute,_that.disableIcmpForwarding,_that.mtu,_that.endpointIndependentNat);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _Tun implements Tun {\n  const _Tun({this.enable = false, this.device = tunDeviceName, @JsonKey(name: 'auto-route') this.autoRoute = false, this.stack = TunStack.system, @JsonKey(name: 'dns-hijack') final  List<String> dnsHijack = const ['any:53'], @JsonKey(name: 'route-address') final  List<String> routeAddress = const [], @JsonKey(name: 'route-exclude-address') final  List<String> routeExcludeAddress = const [], @JsonKey(name: 'strict-route') this.strictRoute = false, @JsonKey(name: 'disable-icmp-forwarding') this.disableIcmpForwarding = true, this.mtu = 4064, @JsonKey(name: 'endpoint-independent-nat') this.endpointIndependentNat = false}): _dnsHijack = dnsHijack,_routeAddress = routeAddress,_routeExcludeAddress = routeExcludeAddress;\n  factory _Tun.fromJson(Map<String, dynamic> json) => _$TunFromJson(json);\n\n@override@JsonKey() final  bool enable;\n@override@JsonKey() final  String device;\n@override@JsonKey(name: 'auto-route') final  bool autoRoute;\n@override@JsonKey() final  TunStack stack;\n final  List<String> _dnsHijack;\n@override@JsonKey(name: 'dns-hijack') List<String> get dnsHijack {\n  if (_dnsHijack is EqualUnmodifiableListView) return _dnsHijack;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_dnsHijack);\n}\n\n final  List<String> _routeAddress;\n@override@JsonKey(name: 'route-address') List<String> get routeAddress {\n  if (_routeAddress is EqualUnmodifiableListView) return _routeAddress;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_routeAddress);\n}\n\n final  List<String> _routeExcludeAddress;\n@override@JsonKey(name: 'route-exclude-address') List<String> get routeExcludeAddress {\n  if (_routeExcludeAddress is EqualUnmodifiableListView) return _routeExcludeAddress;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_routeExcludeAddress);\n}\n\n@override@JsonKey(name: 'strict-route') final  bool strictRoute;\n@override@JsonKey(name: 'disable-icmp-forwarding') final  bool disableIcmpForwarding;\n@override@JsonKey() final  int mtu;\n@override@JsonKey(name: 'endpoint-independent-nat') final  bool endpointIndependentNat;\n\n/// Create a copy of Tun\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$TunCopyWith<_Tun> get copyWith => __$TunCopyWithImpl<_Tun>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$TunToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _Tun&&(identical(other.enable, enable) || other.enable == enable)&&(identical(other.device, device) || other.device == device)&&(identical(other.autoRoute, autoRoute) || other.autoRoute == autoRoute)&&(identical(other.stack, stack) || other.stack == stack)&&const DeepCollectionEquality().equals(other._dnsHijack, _dnsHijack)&&const DeepCollectionEquality().equals(other._routeAddress, _routeAddress)&&const DeepCollectionEquality().equals(other._routeExcludeAddress, _routeExcludeAddress)&&(identical(other.strictRoute, strictRoute) || other.strictRoute == strictRoute)&&(identical(other.disableIcmpForwarding, disableIcmpForwarding) || other.disableIcmpForwarding == disableIcmpForwarding)&&(identical(other.mtu, mtu) || other.mtu == mtu)&&(identical(other.endpointIndependentNat, endpointIndependentNat) || other.endpointIndependentNat == endpointIndependentNat));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,enable,device,autoRoute,stack,const DeepCollectionEquality().hash(_dnsHijack),const DeepCollectionEquality().hash(_routeAddress),const DeepCollectionEquality().hash(_routeExcludeAddress),strictRoute,disableIcmpForwarding,mtu,endpointIndependentNat);\n\n@override\nString toString() {\n  return 'Tun(enable: $enable, device: $device, autoRoute: $autoRoute, stack: $stack, dnsHijack: $dnsHijack, routeAddress: $routeAddress, routeExcludeAddress: $routeExcludeAddress, strictRoute: $strictRoute, disableIcmpForwarding: $disableIcmpForwarding, mtu: $mtu, endpointIndependentNat: $endpointIndependentNat)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$TunCopyWith<$Res> implements $TunCopyWith<$Res> {\n  factory _$TunCopyWith(_Tun value, $Res Function(_Tun) _then) = __$TunCopyWithImpl;\n@override @useResult\n$Res call({\n bool enable, String device,@JsonKey(name: 'auto-route') bool autoRoute, TunStack stack,@JsonKey(name: 'dns-hijack') List<String> dnsHijack,@JsonKey(name: 'route-address') List<String> routeAddress,@JsonKey(name: 'route-exclude-address') List<String> routeExcludeAddress,@JsonKey(name: 'strict-route') bool strictRoute,@JsonKey(name: 'disable-icmp-forwarding') bool disableIcmpForwarding, int mtu,@JsonKey(name: 'endpoint-independent-nat') bool endpointIndependentNat\n});\n\n\n\n\n}\n/// @nodoc\nclass __$TunCopyWithImpl<$Res>\n    implements _$TunCopyWith<$Res> {\n  __$TunCopyWithImpl(this._self, this._then);\n\n  final _Tun _self;\n  final $Res Function(_Tun) _then;\n\n/// Create a copy of Tun\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? enable = null,Object? device = null,Object? autoRoute = null,Object? stack = null,Object? dnsHijack = null,Object? routeAddress = null,Object? routeExcludeAddress = null,Object? strictRoute = null,Object? disableIcmpForwarding = null,Object? mtu = null,Object? endpointIndependentNat = null,}) {\n  return _then(_Tun(\nenable: null == enable ? _self.enable : enable // ignore: cast_nullable_to_non_nullable\nas bool,device: null == device ? _self.device : device // ignore: cast_nullable_to_non_nullable\nas String,autoRoute: null == autoRoute ? _self.autoRoute : autoRoute // ignore: cast_nullable_to_non_nullable\nas bool,stack: null == stack ? _self.stack : stack // ignore: cast_nullable_to_non_nullable\nas TunStack,dnsHijack: null == dnsHijack ? _self._dnsHijack : dnsHijack // ignore: cast_nullable_to_non_nullable\nas List<String>,routeAddress: null == routeAddress ? _self._routeAddress : routeAddress // ignore: cast_nullable_to_non_nullable\nas List<String>,routeExcludeAddress: null == routeExcludeAddress ? _self._routeExcludeAddress : routeExcludeAddress // ignore: cast_nullable_to_non_nullable\nas List<String>,strictRoute: null == strictRoute ? _self.strictRoute : strictRoute // ignore: cast_nullable_to_non_nullable\nas bool,disableIcmpForwarding: null == disableIcmpForwarding ? _self.disableIcmpForwarding : disableIcmpForwarding // ignore: cast_nullable_to_non_nullable\nas bool,mtu: null == mtu ? _self.mtu : mtu // ignore: cast_nullable_to_non_nullable\nas int,endpointIndependentNat: null == endpointIndependentNat ? _self.endpointIndependentNat : endpointIndependentNat // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$FallbackFilter {\n\n bool get geoip;@JsonKey(name: 'geoip-code') String get geoipCode; List<String> get geosite; List<String> get ipcidr; List<String> get domain;\n/// Create a copy of FallbackFilter\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$FallbackFilterCopyWith<FallbackFilter> get copyWith => _$FallbackFilterCopyWithImpl<FallbackFilter>(this as FallbackFilter, _$identity);\n\n  /// Serializes this FallbackFilter to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is FallbackFilter&&(identical(other.geoip, geoip) || other.geoip == geoip)&&(identical(other.geoipCode, geoipCode) || other.geoipCode == geoipCode)&&const DeepCollectionEquality().equals(other.geosite, geosite)&&const DeepCollectionEquality().equals(other.ipcidr, ipcidr)&&const DeepCollectionEquality().equals(other.domain, domain));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,geoip,geoipCode,const DeepCollectionEquality().hash(geosite),const DeepCollectionEquality().hash(ipcidr),const DeepCollectionEquality().hash(domain));\n\n@override\nString toString() {\n  return 'FallbackFilter(geoip: $geoip, geoipCode: $geoipCode, geosite: $geosite, ipcidr: $ipcidr, domain: $domain)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $FallbackFilterCopyWith<$Res>  {\n  factory $FallbackFilterCopyWith(FallbackFilter value, $Res Function(FallbackFilter) _then) = _$FallbackFilterCopyWithImpl;\n@useResult\n$Res call({\n bool geoip,@JsonKey(name: 'geoip-code') String geoipCode, List<String> geosite, List<String> ipcidr, List<String> domain\n});\n\n\n\n\n}\n/// @nodoc\nclass _$FallbackFilterCopyWithImpl<$Res>\n    implements $FallbackFilterCopyWith<$Res> {\n  _$FallbackFilterCopyWithImpl(this._self, this._then);\n\n  final FallbackFilter _self;\n  final $Res Function(FallbackFilter) _then;\n\n/// Create a copy of FallbackFilter\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? geoip = null,Object? geoipCode = null,Object? geosite = null,Object? ipcidr = null,Object? domain = null,}) {\n  return _then(_self.copyWith(\ngeoip: null == geoip ? _self.geoip : geoip // ignore: cast_nullable_to_non_nullable\nas bool,geoipCode: null == geoipCode ? _self.geoipCode : geoipCode // ignore: cast_nullable_to_non_nullable\nas String,geosite: null == geosite ? _self.geosite : geosite // ignore: cast_nullable_to_non_nullable\nas List<String>,ipcidr: null == ipcidr ? _self.ipcidr : ipcidr // ignore: cast_nullable_to_non_nullable\nas List<String>,domain: null == domain ? _self.domain : domain // ignore: cast_nullable_to_non_nullable\nas List<String>,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [FallbackFilter].\nextension FallbackFilterPatterns on FallbackFilter {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _FallbackFilter value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _FallbackFilter() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _FallbackFilter value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _FallbackFilter():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _FallbackFilter value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _FallbackFilter() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool geoip, @JsonKey(name: 'geoip-code')  String geoipCode,  List<String> geosite,  List<String> ipcidr,  List<String> domain)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _FallbackFilter() when $default != null:\nreturn $default(_that.geoip,_that.geoipCode,_that.geosite,_that.ipcidr,_that.domain);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool geoip, @JsonKey(name: 'geoip-code')  String geoipCode,  List<String> geosite,  List<String> ipcidr,  List<String> domain)  $default,) {final _that = this;\nswitch (_that) {\ncase _FallbackFilter():\nreturn $default(_that.geoip,_that.geoipCode,_that.geosite,_that.ipcidr,_that.domain);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool geoip, @JsonKey(name: 'geoip-code')  String geoipCode,  List<String> geosite,  List<String> ipcidr,  List<String> domain)?  $default,) {final _that = this;\nswitch (_that) {\ncase _FallbackFilter() when $default != null:\nreturn $default(_that.geoip,_that.geoipCode,_that.geosite,_that.ipcidr,_that.domain);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _FallbackFilter implements FallbackFilter {\n  const _FallbackFilter({this.geoip = false, @JsonKey(name: 'geoip-code') this.geoipCode = 'CN', final  List<String> geosite = const [], final  List<String> ipcidr = const [], final  List<String> domain = const []}): _geosite = geosite,_ipcidr = ipcidr,_domain = domain;\n  factory _FallbackFilter.fromJson(Map<String, dynamic> json) => _$FallbackFilterFromJson(json);\n\n@override@JsonKey() final  bool geoip;\n@override@JsonKey(name: 'geoip-code') final  String geoipCode;\n final  List<String> _geosite;\n@override@JsonKey() List<String> get geosite {\n  if (_geosite is EqualUnmodifiableListView) return _geosite;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_geosite);\n}\n\n final  List<String> _ipcidr;\n@override@JsonKey() List<String> get ipcidr {\n  if (_ipcidr is EqualUnmodifiableListView) return _ipcidr;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_ipcidr);\n}\n\n final  List<String> _domain;\n@override@JsonKey() List<String> get domain {\n  if (_domain is EqualUnmodifiableListView) return _domain;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_domain);\n}\n\n\n/// Create a copy of FallbackFilter\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$FallbackFilterCopyWith<_FallbackFilter> get copyWith => __$FallbackFilterCopyWithImpl<_FallbackFilter>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$FallbackFilterToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _FallbackFilter&&(identical(other.geoip, geoip) || other.geoip == geoip)&&(identical(other.geoipCode, geoipCode) || other.geoipCode == geoipCode)&&const DeepCollectionEquality().equals(other._geosite, _geosite)&&const DeepCollectionEquality().equals(other._ipcidr, _ipcidr)&&const DeepCollectionEquality().equals(other._domain, _domain));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,geoip,geoipCode,const DeepCollectionEquality().hash(_geosite),const DeepCollectionEquality().hash(_ipcidr),const DeepCollectionEquality().hash(_domain));\n\n@override\nString toString() {\n  return 'FallbackFilter(geoip: $geoip, geoipCode: $geoipCode, geosite: $geosite, ipcidr: $ipcidr, domain: $domain)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$FallbackFilterCopyWith<$Res> implements $FallbackFilterCopyWith<$Res> {\n  factory _$FallbackFilterCopyWith(_FallbackFilter value, $Res Function(_FallbackFilter) _then) = __$FallbackFilterCopyWithImpl;\n@override @useResult\n$Res call({\n bool geoip,@JsonKey(name: 'geoip-code') String geoipCode, List<String> geosite, List<String> ipcidr, List<String> domain\n});\n\n\n\n\n}\n/// @nodoc\nclass __$FallbackFilterCopyWithImpl<$Res>\n    implements _$FallbackFilterCopyWith<$Res> {\n  __$FallbackFilterCopyWithImpl(this._self, this._then);\n\n  final _FallbackFilter _self;\n  final $Res Function(_FallbackFilter) _then;\n\n/// Create a copy of FallbackFilter\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? geoip = null,Object? geoipCode = null,Object? geosite = null,Object? ipcidr = null,Object? domain = null,}) {\n  return _then(_FallbackFilter(\ngeoip: null == geoip ? _self.geoip : geoip // ignore: cast_nullable_to_non_nullable\nas bool,geoipCode: null == geoipCode ? _self.geoipCode : geoipCode // ignore: cast_nullable_to_non_nullable\nas String,geosite: null == geosite ? _self._geosite : geosite // ignore: cast_nullable_to_non_nullable\nas List<String>,ipcidr: null == ipcidr ? _self._ipcidr : ipcidr // ignore: cast_nullable_to_non_nullable\nas List<String>,domain: null == domain ? _self._domain : domain // ignore: cast_nullable_to_non_nullable\nas List<String>,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$Dns {\n\n bool get enable; String get listen;@JsonKey(name: 'prefer-h3') bool get preferH3;@JsonKey(name: 'cache-algorithm') CacheAlgorithm get cacheAlgorithm;@JsonKey(name: 'use-hosts') bool get useHosts;@JsonKey(name: 'use-system-hosts') bool get useSystemHosts;@JsonKey(name: 'respect-rules') bool get respectRules; bool get ipv6;@JsonKey(name: 'default-nameserver') List<String> get defaultNameserver;@JsonKey(name: 'enhanced-mode') DnsMode get enhancedMode;@JsonKey(name: 'fake-ip-range') String get fakeIpRange;@JsonKey(name: 'fake-ip-range-v6') String get fakeIpRangeV6;@JsonKey(name: 'fake-ip-filter-mode') FilterMode get fakeIpFilterMode;@JsonKey(name: 'fake-ip-filter') List<String> get fakeIpFilter;@JsonKey(name: 'fake-ip-ttl') int get fakeIpTtl;@JsonKey(name: 'nameserver-policy') Map<String, String> get nameserverPolicy; List<String> get nameserver; List<String> get fallback;@JsonKey(name: 'proxy-server-nameserver') List<String> get proxyServerNameserver;@JsonKey(name: 'direct-nameserver') List<String> get directNameserver;@JsonKey(name: 'direct-nameserver-follow-policy') bool get directNameserverFollowPolicy;@JsonKey(name: 'fallback-filter') FallbackFilter get fallbackFilter;\n/// Create a copy of Dns\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$DnsCopyWith<Dns> get copyWith => _$DnsCopyWithImpl<Dns>(this as Dns, _$identity);\n\n  /// Serializes this Dns to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is Dns&&(identical(other.enable, enable) || other.enable == enable)&&(identical(other.listen, listen) || other.listen == listen)&&(identical(other.preferH3, preferH3) || other.preferH3 == preferH3)&&(identical(other.cacheAlgorithm, cacheAlgorithm) || other.cacheAlgorithm == cacheAlgorithm)&&(identical(other.useHosts, useHosts) || other.useHosts == useHosts)&&(identical(other.useSystemHosts, useSystemHosts) || other.useSystemHosts == useSystemHosts)&&(identical(other.respectRules, respectRules) || other.respectRules == respectRules)&&(identical(other.ipv6, ipv6) || other.ipv6 == ipv6)&&const DeepCollectionEquality().equals(other.defaultNameserver, defaultNameserver)&&(identical(other.enhancedMode, enhancedMode) || other.enhancedMode == enhancedMode)&&(identical(other.fakeIpRange, fakeIpRange) || other.fakeIpRange == fakeIpRange)&&(identical(other.fakeIpRangeV6, fakeIpRangeV6) || other.fakeIpRangeV6 == fakeIpRangeV6)&&(identical(other.fakeIpFilterMode, fakeIpFilterMode) || other.fakeIpFilterMode == fakeIpFilterMode)&&const DeepCollectionEquality().equals(other.fakeIpFilter, fakeIpFilter)&&(identical(other.fakeIpTtl, fakeIpTtl) || other.fakeIpTtl == fakeIpTtl)&&const DeepCollectionEquality().equals(other.nameserverPolicy, nameserverPolicy)&&const DeepCollectionEquality().equals(other.nameserver, nameserver)&&const DeepCollectionEquality().equals(other.fallback, fallback)&&const DeepCollectionEquality().equals(other.proxyServerNameserver, proxyServerNameserver)&&const DeepCollectionEquality().equals(other.directNameserver, directNameserver)&&(identical(other.directNameserverFollowPolicy, directNameserverFollowPolicy) || other.directNameserverFollowPolicy == directNameserverFollowPolicy)&&(identical(other.fallbackFilter, fallbackFilter) || other.fallbackFilter == fallbackFilter));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hashAll([runtimeType,enable,listen,preferH3,cacheAlgorithm,useHosts,useSystemHosts,respectRules,ipv6,const DeepCollectionEquality().hash(defaultNameserver),enhancedMode,fakeIpRange,fakeIpRangeV6,fakeIpFilterMode,const DeepCollectionEquality().hash(fakeIpFilter),fakeIpTtl,const DeepCollectionEquality().hash(nameserverPolicy),const DeepCollectionEquality().hash(nameserver),const DeepCollectionEquality().hash(fallback),const DeepCollectionEquality().hash(proxyServerNameserver),const DeepCollectionEquality().hash(directNameserver),directNameserverFollowPolicy,fallbackFilter]);\n\n@override\nString toString() {\n  return 'Dns(enable: $enable, listen: $listen, preferH3: $preferH3, cacheAlgorithm: $cacheAlgorithm, useHosts: $useHosts, useSystemHosts: $useSystemHosts, respectRules: $respectRules, ipv6: $ipv6, defaultNameserver: $defaultNameserver, enhancedMode: $enhancedMode, fakeIpRange: $fakeIpRange, fakeIpRangeV6: $fakeIpRangeV6, fakeIpFilterMode: $fakeIpFilterMode, fakeIpFilter: $fakeIpFilter, fakeIpTtl: $fakeIpTtl, nameserverPolicy: $nameserverPolicy, nameserver: $nameserver, fallback: $fallback, proxyServerNameserver: $proxyServerNameserver, directNameserver: $directNameserver, directNameserverFollowPolicy: $directNameserverFollowPolicy, fallbackFilter: $fallbackFilter)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $DnsCopyWith<$Res>  {\n  factory $DnsCopyWith(Dns value, $Res Function(Dns) _then) = _$DnsCopyWithImpl;\n@useResult\n$Res call({\n bool enable, String listen,@JsonKey(name: 'prefer-h3') bool preferH3,@JsonKey(name: 'cache-algorithm') CacheAlgorithm cacheAlgorithm,@JsonKey(name: 'use-hosts') bool useHosts,@JsonKey(name: 'use-system-hosts') bool useSystemHosts,@JsonKey(name: 'respect-rules') bool respectRules, bool ipv6,@JsonKey(name: 'default-nameserver') List<String> defaultNameserver,@JsonKey(name: 'enhanced-mode') DnsMode enhancedMode,@JsonKey(name: 'fake-ip-range') String fakeIpRange,@JsonKey(name: 'fake-ip-range-v6') String fakeIpRangeV6,@JsonKey(name: 'fake-ip-filter-mode') FilterMode fakeIpFilterMode,@JsonKey(name: 'fake-ip-filter') List<String> fakeIpFilter,@JsonKey(name: 'fake-ip-ttl') int fakeIpTtl,@JsonKey(name: 'nameserver-policy') Map<String, String> nameserverPolicy, List<String> nameserver, List<String> fallback,@JsonKey(name: 'proxy-server-nameserver') List<String> proxyServerNameserver,@JsonKey(name: 'direct-nameserver') List<String> directNameserver,@JsonKey(name: 'direct-nameserver-follow-policy') bool directNameserverFollowPolicy,@JsonKey(name: 'fallback-filter') FallbackFilter fallbackFilter\n});\n\n\n$FallbackFilterCopyWith<$Res> get fallbackFilter;\n\n}\n/// @nodoc\nclass _$DnsCopyWithImpl<$Res>\n    implements $DnsCopyWith<$Res> {\n  _$DnsCopyWithImpl(this._self, this._then);\n\n  final Dns _self;\n  final $Res Function(Dns) _then;\n\n/// Create a copy of Dns\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? enable = null,Object? listen = null,Object? preferH3 = null,Object? cacheAlgorithm = null,Object? useHosts = null,Object? useSystemHosts = null,Object? respectRules = null,Object? ipv6 = null,Object? defaultNameserver = null,Object? enhancedMode = null,Object? fakeIpRange = null,Object? fakeIpRangeV6 = null,Object? fakeIpFilterMode = null,Object? fakeIpFilter = null,Object? fakeIpTtl = null,Object? nameserverPolicy = null,Object? nameserver = null,Object? fallback = null,Object? proxyServerNameserver = null,Object? directNameserver = null,Object? directNameserverFollowPolicy = null,Object? fallbackFilter = null,}) {\n  return _then(_self.copyWith(\nenable: null == enable ? _self.enable : enable // ignore: cast_nullable_to_non_nullable\nas bool,listen: null == listen ? _self.listen : listen // ignore: cast_nullable_to_non_nullable\nas String,preferH3: null == preferH3 ? _self.preferH3 : preferH3 // ignore: cast_nullable_to_non_nullable\nas bool,cacheAlgorithm: null == cacheAlgorithm ? _self.cacheAlgorithm : cacheAlgorithm // ignore: cast_nullable_to_non_nullable\nas CacheAlgorithm,useHosts: null == useHosts ? _self.useHosts : useHosts // ignore: cast_nullable_to_non_nullable\nas bool,useSystemHosts: null == useSystemHosts ? _self.useSystemHosts : useSystemHosts // ignore: cast_nullable_to_non_nullable\nas bool,respectRules: null == respectRules ? _self.respectRules : respectRules // ignore: cast_nullable_to_non_nullable\nas bool,ipv6: null == ipv6 ? _self.ipv6 : ipv6 // ignore: cast_nullable_to_non_nullable\nas bool,defaultNameserver: null == defaultNameserver ? _self.defaultNameserver : defaultNameserver // ignore: cast_nullable_to_non_nullable\nas List<String>,enhancedMode: null == enhancedMode ? _self.enhancedMode : enhancedMode // ignore: cast_nullable_to_non_nullable\nas DnsMode,fakeIpRange: null == fakeIpRange ? _self.fakeIpRange : fakeIpRange // ignore: cast_nullable_to_non_nullable\nas String,fakeIpRangeV6: null == fakeIpRangeV6 ? _self.fakeIpRangeV6 : fakeIpRangeV6 // ignore: cast_nullable_to_non_nullable\nas String,fakeIpFilterMode: null == fakeIpFilterMode ? _self.fakeIpFilterMode : fakeIpFilterMode // ignore: cast_nullable_to_non_nullable\nas FilterMode,fakeIpFilter: null == fakeIpFilter ? _self.fakeIpFilter : fakeIpFilter // ignore: cast_nullable_to_non_nullable\nas List<String>,fakeIpTtl: null == fakeIpTtl ? _self.fakeIpTtl : fakeIpTtl // ignore: cast_nullable_to_non_nullable\nas int,nameserverPolicy: null == nameserverPolicy ? _self.nameserverPolicy : nameserverPolicy // ignore: cast_nullable_to_non_nullable\nas Map<String, String>,nameserver: null == nameserver ? _self.nameserver : nameserver // ignore: cast_nullable_to_non_nullable\nas List<String>,fallback: null == fallback ? _self.fallback : fallback // ignore: cast_nullable_to_non_nullable\nas List<String>,proxyServerNameserver: null == proxyServerNameserver ? _self.proxyServerNameserver : proxyServerNameserver // ignore: cast_nullable_to_non_nullable\nas List<String>,directNameserver: null == directNameserver ? _self.directNameserver : directNameserver // ignore: cast_nullable_to_non_nullable\nas List<String>,directNameserverFollowPolicy: null == directNameserverFollowPolicy ? _self.directNameserverFollowPolicy : directNameserverFollowPolicy // ignore: cast_nullable_to_non_nullable\nas bool,fallbackFilter: null == fallbackFilter ? _self.fallbackFilter : fallbackFilter // ignore: cast_nullable_to_non_nullable\nas FallbackFilter,\n  ));\n}\n/// Create a copy of Dns\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$FallbackFilterCopyWith<$Res> get fallbackFilter {\n  \n  return $FallbackFilterCopyWith<$Res>(_self.fallbackFilter, (value) {\n    return _then(_self.copyWith(fallbackFilter: value));\n  });\n}\n}\n\n\n/// Adds pattern-matching-related methods to [Dns].\nextension DnsPatterns on Dns {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _Dns value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _Dns() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _Dns value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Dns():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _Dns value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Dns() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool enable,  String listen, @JsonKey(name: 'prefer-h3')  bool preferH3, @JsonKey(name: 'cache-algorithm')  CacheAlgorithm cacheAlgorithm, @JsonKey(name: 'use-hosts')  bool useHosts, @JsonKey(name: 'use-system-hosts')  bool useSystemHosts, @JsonKey(name: 'respect-rules')  bool respectRules,  bool ipv6, @JsonKey(name: 'default-nameserver')  List<String> defaultNameserver, @JsonKey(name: 'enhanced-mode')  DnsMode enhancedMode, @JsonKey(name: 'fake-ip-range')  String fakeIpRange, @JsonKey(name: 'fake-ip-range-v6')  String fakeIpRangeV6, @JsonKey(name: 'fake-ip-filter-mode')  FilterMode fakeIpFilterMode, @JsonKey(name: 'fake-ip-filter')  List<String> fakeIpFilter, @JsonKey(name: 'fake-ip-ttl')  int fakeIpTtl, @JsonKey(name: 'nameserver-policy')  Map<String, String> nameserverPolicy,  List<String> nameserver,  List<String> fallback, @JsonKey(name: 'proxy-server-nameserver')  List<String> proxyServerNameserver, @JsonKey(name: 'direct-nameserver')  List<String> directNameserver, @JsonKey(name: 'direct-nameserver-follow-policy')  bool directNameserverFollowPolicy, @JsonKey(name: 'fallback-filter')  FallbackFilter fallbackFilter)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _Dns() when $default != null:\nreturn $default(_that.enable,_that.listen,_that.preferH3,_that.cacheAlgorithm,_that.useHosts,_that.useSystemHosts,_that.respectRules,_that.ipv6,_that.defaultNameserver,_that.enhancedMode,_that.fakeIpRange,_that.fakeIpRangeV6,_that.fakeIpFilterMode,_that.fakeIpFilter,_that.fakeIpTtl,_that.nameserverPolicy,_that.nameserver,_that.fallback,_that.proxyServerNameserver,_that.directNameserver,_that.directNameserverFollowPolicy,_that.fallbackFilter);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool enable,  String listen, @JsonKey(name: 'prefer-h3')  bool preferH3, @JsonKey(name: 'cache-algorithm')  CacheAlgorithm cacheAlgorithm, @JsonKey(name: 'use-hosts')  bool useHosts, @JsonKey(name: 'use-system-hosts')  bool useSystemHosts, @JsonKey(name: 'respect-rules')  bool respectRules,  bool ipv6, @JsonKey(name: 'default-nameserver')  List<String> defaultNameserver, @JsonKey(name: 'enhanced-mode')  DnsMode enhancedMode, @JsonKey(name: 'fake-ip-range')  String fakeIpRange, @JsonKey(name: 'fake-ip-range-v6')  String fakeIpRangeV6, @JsonKey(name: 'fake-ip-filter-mode')  FilterMode fakeIpFilterMode, @JsonKey(name: 'fake-ip-filter')  List<String> fakeIpFilter, @JsonKey(name: 'fake-ip-ttl')  int fakeIpTtl, @JsonKey(name: 'nameserver-policy')  Map<String, String> nameserverPolicy,  List<String> nameserver,  List<String> fallback, @JsonKey(name: 'proxy-server-nameserver')  List<String> proxyServerNameserver, @JsonKey(name: 'direct-nameserver')  List<String> directNameserver, @JsonKey(name: 'direct-nameserver-follow-policy')  bool directNameserverFollowPolicy, @JsonKey(name: 'fallback-filter')  FallbackFilter fallbackFilter)  $default,) {final _that = this;\nswitch (_that) {\ncase _Dns():\nreturn $default(_that.enable,_that.listen,_that.preferH3,_that.cacheAlgorithm,_that.useHosts,_that.useSystemHosts,_that.respectRules,_that.ipv6,_that.defaultNameserver,_that.enhancedMode,_that.fakeIpRange,_that.fakeIpRangeV6,_that.fakeIpFilterMode,_that.fakeIpFilter,_that.fakeIpTtl,_that.nameserverPolicy,_that.nameserver,_that.fallback,_that.proxyServerNameserver,_that.directNameserver,_that.directNameserverFollowPolicy,_that.fallbackFilter);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool enable,  String listen, @JsonKey(name: 'prefer-h3')  bool preferH3, @JsonKey(name: 'cache-algorithm')  CacheAlgorithm cacheAlgorithm, @JsonKey(name: 'use-hosts')  bool useHosts, @JsonKey(name: 'use-system-hosts')  bool useSystemHosts, @JsonKey(name: 'respect-rules')  bool respectRules,  bool ipv6, @JsonKey(name: 'default-nameserver')  List<String> defaultNameserver, @JsonKey(name: 'enhanced-mode')  DnsMode enhancedMode, @JsonKey(name: 'fake-ip-range')  String fakeIpRange, @JsonKey(name: 'fake-ip-range-v6')  String fakeIpRangeV6, @JsonKey(name: 'fake-ip-filter-mode')  FilterMode fakeIpFilterMode, @JsonKey(name: 'fake-ip-filter')  List<String> fakeIpFilter, @JsonKey(name: 'fake-ip-ttl')  int fakeIpTtl, @JsonKey(name: 'nameserver-policy')  Map<String, String> nameserverPolicy,  List<String> nameserver,  List<String> fallback, @JsonKey(name: 'proxy-server-nameserver')  List<String> proxyServerNameserver, @JsonKey(name: 'direct-nameserver')  List<String> directNameserver, @JsonKey(name: 'direct-nameserver-follow-policy')  bool directNameserverFollowPolicy, @JsonKey(name: 'fallback-filter')  FallbackFilter fallbackFilter)?  $default,) {final _that = this;\nswitch (_that) {\ncase _Dns() when $default != null:\nreturn $default(_that.enable,_that.listen,_that.preferH3,_that.cacheAlgorithm,_that.useHosts,_that.useSystemHosts,_that.respectRules,_that.ipv6,_that.defaultNameserver,_that.enhancedMode,_that.fakeIpRange,_that.fakeIpRangeV6,_that.fakeIpFilterMode,_that.fakeIpFilter,_that.fakeIpTtl,_that.nameserverPolicy,_that.nameserver,_that.fallback,_that.proxyServerNameserver,_that.directNameserver,_that.directNameserverFollowPolicy,_that.fallbackFilter);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _Dns implements Dns {\n  const _Dns({this.enable = true, this.listen = '0.0.0.0:1053', @JsonKey(name: 'prefer-h3') this.preferH3 = false, @JsonKey(name: 'cache-algorithm') this.cacheAlgorithm = CacheAlgorithm.arc, @JsonKey(name: 'use-hosts') this.useHosts = true, @JsonKey(name: 'use-system-hosts') this.useSystemHosts = true, @JsonKey(name: 'respect-rules') this.respectRules = false, this.ipv6 = false, @JsonKey(name: 'default-nameserver') final  List<String> defaultNameserver = const ['114.114.114.114'], @JsonKey(name: 'enhanced-mode') this.enhancedMode = DnsMode.fakeIp, @JsonKey(name: 'fake-ip-range') this.fakeIpRange = '198.18.0.1/15', @JsonKey(name: 'fake-ip-range-v6') this.fakeIpRangeV6 = 'fc00::/18', @JsonKey(name: 'fake-ip-filter-mode') this.fakeIpFilterMode = FilterMode.blacklist, @JsonKey(name: 'fake-ip-filter') final  List<String> fakeIpFilter = const ['*', 'geosite:private', 'geosite:category-ntp', 'geosite:geolocation-cn', 'geosite:connectivity-check'], @JsonKey(name: 'fake-ip-ttl') this.fakeIpTtl = 1, @JsonKey(name: 'nameserver-policy') final  Map<String, String> nameserverPolicy = const {'+.internal.crop.com' : '10.0.0.1', 'geosite:cn' : '119.29.29.29', 'geosite:private' : 'system', '*' : 'system'}, final  List<String> nameserver = const ['1.1.1.1'], final  List<String> fallback = const [], @JsonKey(name: 'proxy-server-nameserver') final  List<String> proxyServerNameserver = const ['https://doh.pub/dns-query#DIRECT'], @JsonKey(name: 'direct-nameserver') final  List<String> directNameserver = const [], @JsonKey(name: 'direct-nameserver-follow-policy') this.directNameserverFollowPolicy = false, @JsonKey(name: 'fallback-filter') this.fallbackFilter = const FallbackFilter()}): _defaultNameserver = defaultNameserver,_fakeIpFilter = fakeIpFilter,_nameserverPolicy = nameserverPolicy,_nameserver = nameserver,_fallback = fallback,_proxyServerNameserver = proxyServerNameserver,_directNameserver = directNameserver;\n  factory _Dns.fromJson(Map<String, dynamic> json) => _$DnsFromJson(json);\n\n@override@JsonKey() final  bool enable;\n@override@JsonKey() final  String listen;\n@override@JsonKey(name: 'prefer-h3') final  bool preferH3;\n@override@JsonKey(name: 'cache-algorithm') final  CacheAlgorithm cacheAlgorithm;\n@override@JsonKey(name: 'use-hosts') final  bool useHosts;\n@override@JsonKey(name: 'use-system-hosts') final  bool useSystemHosts;\n@override@JsonKey(name: 'respect-rules') final  bool respectRules;\n@override@JsonKey() final  bool ipv6;\n final  List<String> _defaultNameserver;\n@override@JsonKey(name: 'default-nameserver') List<String> get defaultNameserver {\n  if (_defaultNameserver is EqualUnmodifiableListView) return _defaultNameserver;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_defaultNameserver);\n}\n\n@override@JsonKey(name: 'enhanced-mode') final  DnsMode enhancedMode;\n@override@JsonKey(name: 'fake-ip-range') final  String fakeIpRange;\n@override@JsonKey(name: 'fake-ip-range-v6') final  String fakeIpRangeV6;\n@override@JsonKey(name: 'fake-ip-filter-mode') final  FilterMode fakeIpFilterMode;\n final  List<String> _fakeIpFilter;\n@override@JsonKey(name: 'fake-ip-filter') List<String> get fakeIpFilter {\n  if (_fakeIpFilter is EqualUnmodifiableListView) return _fakeIpFilter;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_fakeIpFilter);\n}\n\n@override@JsonKey(name: 'fake-ip-ttl') final  int fakeIpTtl;\n final  Map<String, String> _nameserverPolicy;\n@override@JsonKey(name: 'nameserver-policy') Map<String, String> get nameserverPolicy {\n  if (_nameserverPolicy is EqualUnmodifiableMapView) return _nameserverPolicy;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableMapView(_nameserverPolicy);\n}\n\n final  List<String> _nameserver;\n@override@JsonKey() List<String> get nameserver {\n  if (_nameserver is EqualUnmodifiableListView) return _nameserver;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_nameserver);\n}\n\n final  List<String> _fallback;\n@override@JsonKey() List<String> get fallback {\n  if (_fallback is EqualUnmodifiableListView) return _fallback;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_fallback);\n}\n\n final  List<String> _proxyServerNameserver;\n@override@JsonKey(name: 'proxy-server-nameserver') List<String> get proxyServerNameserver {\n  if (_proxyServerNameserver is EqualUnmodifiableListView) return _proxyServerNameserver;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_proxyServerNameserver);\n}\n\n final  List<String> _directNameserver;\n@override@JsonKey(name: 'direct-nameserver') List<String> get directNameserver {\n  if (_directNameserver is EqualUnmodifiableListView) return _directNameserver;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_directNameserver);\n}\n\n@override@JsonKey(name: 'direct-nameserver-follow-policy') final  bool directNameserverFollowPolicy;\n@override@JsonKey(name: 'fallback-filter') final  FallbackFilter fallbackFilter;\n\n/// Create a copy of Dns\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$DnsCopyWith<_Dns> get copyWith => __$DnsCopyWithImpl<_Dns>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$DnsToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _Dns&&(identical(other.enable, enable) || other.enable == enable)&&(identical(other.listen, listen) || other.listen == listen)&&(identical(other.preferH3, preferH3) || other.preferH3 == preferH3)&&(identical(other.cacheAlgorithm, cacheAlgorithm) || other.cacheAlgorithm == cacheAlgorithm)&&(identical(other.useHosts, useHosts) || other.useHosts == useHosts)&&(identical(other.useSystemHosts, useSystemHosts) || other.useSystemHosts == useSystemHosts)&&(identical(other.respectRules, respectRules) || other.respectRules == respectRules)&&(identical(other.ipv6, ipv6) || other.ipv6 == ipv6)&&const DeepCollectionEquality().equals(other._defaultNameserver, _defaultNameserver)&&(identical(other.enhancedMode, enhancedMode) || other.enhancedMode == enhancedMode)&&(identical(other.fakeIpRange, fakeIpRange) || other.fakeIpRange == fakeIpRange)&&(identical(other.fakeIpRangeV6, fakeIpRangeV6) || other.fakeIpRangeV6 == fakeIpRangeV6)&&(identical(other.fakeIpFilterMode, fakeIpFilterMode) || other.fakeIpFilterMode == fakeIpFilterMode)&&const DeepCollectionEquality().equals(other._fakeIpFilter, _fakeIpFilter)&&(identical(other.fakeIpTtl, fakeIpTtl) || other.fakeIpTtl == fakeIpTtl)&&const DeepCollectionEquality().equals(other._nameserverPolicy, _nameserverPolicy)&&const DeepCollectionEquality().equals(other._nameserver, _nameserver)&&const DeepCollectionEquality().equals(other._fallback, _fallback)&&const DeepCollectionEquality().equals(other._proxyServerNameserver, _proxyServerNameserver)&&const DeepCollectionEquality().equals(other._directNameserver, _directNameserver)&&(identical(other.directNameserverFollowPolicy, directNameserverFollowPolicy) || other.directNameserverFollowPolicy == directNameserverFollowPolicy)&&(identical(other.fallbackFilter, fallbackFilter) || other.fallbackFilter == fallbackFilter));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hashAll([runtimeType,enable,listen,preferH3,cacheAlgorithm,useHosts,useSystemHosts,respectRules,ipv6,const DeepCollectionEquality().hash(_defaultNameserver),enhancedMode,fakeIpRange,fakeIpRangeV6,fakeIpFilterMode,const DeepCollectionEquality().hash(_fakeIpFilter),fakeIpTtl,const DeepCollectionEquality().hash(_nameserverPolicy),const DeepCollectionEquality().hash(_nameserver),const DeepCollectionEquality().hash(_fallback),const DeepCollectionEquality().hash(_proxyServerNameserver),const DeepCollectionEquality().hash(_directNameserver),directNameserverFollowPolicy,fallbackFilter]);\n\n@override\nString toString() {\n  return 'Dns(enable: $enable, listen: $listen, preferH3: $preferH3, cacheAlgorithm: $cacheAlgorithm, useHosts: $useHosts, useSystemHosts: $useSystemHosts, respectRules: $respectRules, ipv6: $ipv6, defaultNameserver: $defaultNameserver, enhancedMode: $enhancedMode, fakeIpRange: $fakeIpRange, fakeIpRangeV6: $fakeIpRangeV6, fakeIpFilterMode: $fakeIpFilterMode, fakeIpFilter: $fakeIpFilter, fakeIpTtl: $fakeIpTtl, nameserverPolicy: $nameserverPolicy, nameserver: $nameserver, fallback: $fallback, proxyServerNameserver: $proxyServerNameserver, directNameserver: $directNameserver, directNameserverFollowPolicy: $directNameserverFollowPolicy, fallbackFilter: $fallbackFilter)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$DnsCopyWith<$Res> implements $DnsCopyWith<$Res> {\n  factory _$DnsCopyWith(_Dns value, $Res Function(_Dns) _then) = __$DnsCopyWithImpl;\n@override @useResult\n$Res call({\n bool enable, String listen,@JsonKey(name: 'prefer-h3') bool preferH3,@JsonKey(name: 'cache-algorithm') CacheAlgorithm cacheAlgorithm,@JsonKey(name: 'use-hosts') bool useHosts,@JsonKey(name: 'use-system-hosts') bool useSystemHosts,@JsonKey(name: 'respect-rules') bool respectRules, bool ipv6,@JsonKey(name: 'default-nameserver') List<String> defaultNameserver,@JsonKey(name: 'enhanced-mode') DnsMode enhancedMode,@JsonKey(name: 'fake-ip-range') String fakeIpRange,@JsonKey(name: 'fake-ip-range-v6') String fakeIpRangeV6,@JsonKey(name: 'fake-ip-filter-mode') FilterMode fakeIpFilterMode,@JsonKey(name: 'fake-ip-filter') List<String> fakeIpFilter,@JsonKey(name: 'fake-ip-ttl') int fakeIpTtl,@JsonKey(name: 'nameserver-policy') Map<String, String> nameserverPolicy, List<String> nameserver, List<String> fallback,@JsonKey(name: 'proxy-server-nameserver') List<String> proxyServerNameserver,@JsonKey(name: 'direct-nameserver') List<String> directNameserver,@JsonKey(name: 'direct-nameserver-follow-policy') bool directNameserverFollowPolicy,@JsonKey(name: 'fallback-filter') FallbackFilter fallbackFilter\n});\n\n\n@override $FallbackFilterCopyWith<$Res> get fallbackFilter;\n\n}\n/// @nodoc\nclass __$DnsCopyWithImpl<$Res>\n    implements _$DnsCopyWith<$Res> {\n  __$DnsCopyWithImpl(this._self, this._then);\n\n  final _Dns _self;\n  final $Res Function(_Dns) _then;\n\n/// Create a copy of Dns\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? enable = null,Object? listen = null,Object? preferH3 = null,Object? cacheAlgorithm = null,Object? useHosts = null,Object? useSystemHosts = null,Object? respectRules = null,Object? ipv6 = null,Object? defaultNameserver = null,Object? enhancedMode = null,Object? fakeIpRange = null,Object? fakeIpRangeV6 = null,Object? fakeIpFilterMode = null,Object? fakeIpFilter = null,Object? fakeIpTtl = null,Object? nameserverPolicy = null,Object? nameserver = null,Object? fallback = null,Object? proxyServerNameserver = null,Object? directNameserver = null,Object? directNameserverFollowPolicy = null,Object? fallbackFilter = null,}) {\n  return _then(_Dns(\nenable: null == enable ? _self.enable : enable // ignore: cast_nullable_to_non_nullable\nas bool,listen: null == listen ? _self.listen : listen // ignore: cast_nullable_to_non_nullable\nas String,preferH3: null == preferH3 ? _self.preferH3 : preferH3 // ignore: cast_nullable_to_non_nullable\nas bool,cacheAlgorithm: null == cacheAlgorithm ? _self.cacheAlgorithm : cacheAlgorithm // ignore: cast_nullable_to_non_nullable\nas CacheAlgorithm,useHosts: null == useHosts ? _self.useHosts : useHosts // ignore: cast_nullable_to_non_nullable\nas bool,useSystemHosts: null == useSystemHosts ? _self.useSystemHosts : useSystemHosts // ignore: cast_nullable_to_non_nullable\nas bool,respectRules: null == respectRules ? _self.respectRules : respectRules // ignore: cast_nullable_to_non_nullable\nas bool,ipv6: null == ipv6 ? _self.ipv6 : ipv6 // ignore: cast_nullable_to_non_nullable\nas bool,defaultNameserver: null == defaultNameserver ? _self._defaultNameserver : defaultNameserver // ignore: cast_nullable_to_non_nullable\nas List<String>,enhancedMode: null == enhancedMode ? _self.enhancedMode : enhancedMode // ignore: cast_nullable_to_non_nullable\nas DnsMode,fakeIpRange: null == fakeIpRange ? _self.fakeIpRange : fakeIpRange // ignore: cast_nullable_to_non_nullable\nas String,fakeIpRangeV6: null == fakeIpRangeV6 ? _self.fakeIpRangeV6 : fakeIpRangeV6 // ignore: cast_nullable_to_non_nullable\nas String,fakeIpFilterMode: null == fakeIpFilterMode ? _self.fakeIpFilterMode : fakeIpFilterMode // ignore: cast_nullable_to_non_nullable\nas FilterMode,fakeIpFilter: null == fakeIpFilter ? _self._fakeIpFilter : fakeIpFilter // ignore: cast_nullable_to_non_nullable\nas List<String>,fakeIpTtl: null == fakeIpTtl ? _self.fakeIpTtl : fakeIpTtl // ignore: cast_nullable_to_non_nullable\nas int,nameserverPolicy: null == nameserverPolicy ? _self._nameserverPolicy : nameserverPolicy // ignore: cast_nullable_to_non_nullable\nas Map<String, String>,nameserver: null == nameserver ? _self._nameserver : nameserver // ignore: cast_nullable_to_non_nullable\nas List<String>,fallback: null == fallback ? _self._fallback : fallback // ignore: cast_nullable_to_non_nullable\nas List<String>,proxyServerNameserver: null == proxyServerNameserver ? _self._proxyServerNameserver : proxyServerNameserver // ignore: cast_nullable_to_non_nullable\nas List<String>,directNameserver: null == directNameserver ? _self._directNameserver : directNameserver // ignore: cast_nullable_to_non_nullable\nas List<String>,directNameserverFollowPolicy: null == directNameserverFollowPolicy ? _self.directNameserverFollowPolicy : directNameserverFollowPolicy // ignore: cast_nullable_to_non_nullable\nas bool,fallbackFilter: null == fallbackFilter ? _self.fallbackFilter : fallbackFilter // ignore: cast_nullable_to_non_nullable\nas FallbackFilter,\n  ));\n}\n\n/// Create a copy of Dns\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$FallbackFilterCopyWith<$Res> get fallbackFilter {\n  \n  return $FallbackFilterCopyWith<$Res>(_self.fallbackFilter, (value) {\n    return _then(_self.copyWith(fallbackFilter: value));\n  });\n}\n}\n\n\n/// @nodoc\nmixin _$Ntp {\n\n bool get enable;@JsonKey(name: 'write-to-system') bool get writeToSystem; String get server; int get port; int get interval;\n/// Create a copy of Ntp\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$NtpCopyWith<Ntp> get copyWith => _$NtpCopyWithImpl<Ntp>(this as Ntp, _$identity);\n\n  /// Serializes this Ntp to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is Ntp&&(identical(other.enable, enable) || other.enable == enable)&&(identical(other.writeToSystem, writeToSystem) || other.writeToSystem == writeToSystem)&&(identical(other.server, server) || other.server == server)&&(identical(other.port, port) || other.port == port)&&(identical(other.interval, interval) || other.interval == interval));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,enable,writeToSystem,server,port,interval);\n\n@override\nString toString() {\n  return 'Ntp(enable: $enable, writeToSystem: $writeToSystem, server: $server, port: $port, interval: $interval)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $NtpCopyWith<$Res>  {\n  factory $NtpCopyWith(Ntp value, $Res Function(Ntp) _then) = _$NtpCopyWithImpl;\n@useResult\n$Res call({\n bool enable,@JsonKey(name: 'write-to-system') bool writeToSystem, String server, int port, int interval\n});\n\n\n\n\n}\n/// @nodoc\nclass _$NtpCopyWithImpl<$Res>\n    implements $NtpCopyWith<$Res> {\n  _$NtpCopyWithImpl(this._self, this._then);\n\n  final Ntp _self;\n  final $Res Function(Ntp) _then;\n\n/// Create a copy of Ntp\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? enable = null,Object? writeToSystem = null,Object? server = null,Object? port = null,Object? interval = null,}) {\n  return _then(_self.copyWith(\nenable: null == enable ? _self.enable : enable // ignore: cast_nullable_to_non_nullable\nas bool,writeToSystem: null == writeToSystem ? _self.writeToSystem : writeToSystem // ignore: cast_nullable_to_non_nullable\nas bool,server: null == server ? _self.server : server // ignore: cast_nullable_to_non_nullable\nas String,port: null == port ? _self.port : port // ignore: cast_nullable_to_non_nullable\nas int,interval: null == interval ? _self.interval : interval // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [Ntp].\nextension NtpPatterns on Ntp {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _Ntp value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _Ntp() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _Ntp value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Ntp():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _Ntp value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Ntp() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool enable, @JsonKey(name: 'write-to-system')  bool writeToSystem,  String server,  int port,  int interval)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _Ntp() when $default != null:\nreturn $default(_that.enable,_that.writeToSystem,_that.server,_that.port,_that.interval);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool enable, @JsonKey(name: 'write-to-system')  bool writeToSystem,  String server,  int port,  int interval)  $default,) {final _that = this;\nswitch (_that) {\ncase _Ntp():\nreturn $default(_that.enable,_that.writeToSystem,_that.server,_that.port,_that.interval);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool enable, @JsonKey(name: 'write-to-system')  bool writeToSystem,  String server,  int port,  int interval)?  $default,) {final _that = this;\nswitch (_that) {\ncase _Ntp() when $default != null:\nreturn $default(_that.enable,_that.writeToSystem,_that.server,_that.port,_that.interval);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _Ntp implements Ntp {\n  const _Ntp({this.enable = true, @JsonKey(name: 'write-to-system') this.writeToSystem = false, this.server = 'ntp.aliyun.com', this.port = 123, this.interval = 60});\n  factory _Ntp.fromJson(Map<String, dynamic> json) => _$NtpFromJson(json);\n\n@override@JsonKey() final  bool enable;\n@override@JsonKey(name: 'write-to-system') final  bool writeToSystem;\n@override@JsonKey() final  String server;\n@override@JsonKey() final  int port;\n@override@JsonKey() final  int interval;\n\n/// Create a copy of Ntp\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$NtpCopyWith<_Ntp> get copyWith => __$NtpCopyWithImpl<_Ntp>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$NtpToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _Ntp&&(identical(other.enable, enable) || other.enable == enable)&&(identical(other.writeToSystem, writeToSystem) || other.writeToSystem == writeToSystem)&&(identical(other.server, server) || other.server == server)&&(identical(other.port, port) || other.port == port)&&(identical(other.interval, interval) || other.interval == interval));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,enable,writeToSystem,server,port,interval);\n\n@override\nString toString() {\n  return 'Ntp(enable: $enable, writeToSystem: $writeToSystem, server: $server, port: $port, interval: $interval)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$NtpCopyWith<$Res> implements $NtpCopyWith<$Res> {\n  factory _$NtpCopyWith(_Ntp value, $Res Function(_Ntp) _then) = __$NtpCopyWithImpl;\n@override @useResult\n$Res call({\n bool enable,@JsonKey(name: 'write-to-system') bool writeToSystem, String server, int port, int interval\n});\n\n\n\n\n}\n/// @nodoc\nclass __$NtpCopyWithImpl<$Res>\n    implements _$NtpCopyWith<$Res> {\n  __$NtpCopyWithImpl(this._self, this._then);\n\n  final _Ntp _self;\n  final $Res Function(_Ntp) _then;\n\n/// Create a copy of Ntp\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? enable = null,Object? writeToSystem = null,Object? server = null,Object? port = null,Object? interval = null,}) {\n  return _then(_Ntp(\nenable: null == enable ? _self.enable : enable // ignore: cast_nullable_to_non_nullable\nas bool,writeToSystem: null == writeToSystem ? _self.writeToSystem : writeToSystem // ignore: cast_nullable_to_non_nullable\nas bool,server: null == server ? _self.server : server // ignore: cast_nullable_to_non_nullable\nas String,port: null == port ? _self.port : port // ignore: cast_nullable_to_non_nullable\nas int,interval: null == interval ? _self.interval : interval // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$Experimental {\n\n@JsonKey(name: 'quic-go-disable-gso') bool get quicGoDisableGso;@JsonKey(name: 'quic-go-disable-ecn') bool get quicGoDisableEcn;@JsonKey(name: 'dialer-ip4p-convert') bool get dialerIp4pConvert;\n/// Create a copy of Experimental\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ExperimentalCopyWith<Experimental> get copyWith => _$ExperimentalCopyWithImpl<Experimental>(this as Experimental, _$identity);\n\n  /// Serializes this Experimental to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is Experimental&&(identical(other.quicGoDisableGso, quicGoDisableGso) || other.quicGoDisableGso == quicGoDisableGso)&&(identical(other.quicGoDisableEcn, quicGoDisableEcn) || other.quicGoDisableEcn == quicGoDisableEcn)&&(identical(other.dialerIp4pConvert, dialerIp4pConvert) || other.dialerIp4pConvert == dialerIp4pConvert));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,quicGoDisableGso,quicGoDisableEcn,dialerIp4pConvert);\n\n@override\nString toString() {\n  return 'Experimental(quicGoDisableGso: $quicGoDisableGso, quicGoDisableEcn: $quicGoDisableEcn, dialerIp4pConvert: $dialerIp4pConvert)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ExperimentalCopyWith<$Res>  {\n  factory $ExperimentalCopyWith(Experimental value, $Res Function(Experimental) _then) = _$ExperimentalCopyWithImpl;\n@useResult\n$Res call({\n@JsonKey(name: 'quic-go-disable-gso') bool quicGoDisableGso,@JsonKey(name: 'quic-go-disable-ecn') bool quicGoDisableEcn,@JsonKey(name: 'dialer-ip4p-convert') bool dialerIp4pConvert\n});\n\n\n\n\n}\n/// @nodoc\nclass _$ExperimentalCopyWithImpl<$Res>\n    implements $ExperimentalCopyWith<$Res> {\n  _$ExperimentalCopyWithImpl(this._self, this._then);\n\n  final Experimental _self;\n  final $Res Function(Experimental) _then;\n\n/// Create a copy of Experimental\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? quicGoDisableGso = null,Object? quicGoDisableEcn = null,Object? dialerIp4pConvert = null,}) {\n  return _then(_self.copyWith(\nquicGoDisableGso: null == quicGoDisableGso ? _self.quicGoDisableGso : quicGoDisableGso // ignore: cast_nullable_to_non_nullable\nas bool,quicGoDisableEcn: null == quicGoDisableEcn ? _self.quicGoDisableEcn : quicGoDisableEcn // ignore: cast_nullable_to_non_nullable\nas bool,dialerIp4pConvert: null == dialerIp4pConvert ? _self.dialerIp4pConvert : dialerIp4pConvert // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [Experimental].\nextension ExperimentalPatterns on Experimental {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _Experimental value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _Experimental() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _Experimental value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Experimental():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _Experimental value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Experimental() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(@JsonKey(name: 'quic-go-disable-gso')  bool quicGoDisableGso, @JsonKey(name: 'quic-go-disable-ecn')  bool quicGoDisableEcn, @JsonKey(name: 'dialer-ip4p-convert')  bool dialerIp4pConvert)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _Experimental() when $default != null:\nreturn $default(_that.quicGoDisableGso,_that.quicGoDisableEcn,_that.dialerIp4pConvert);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(@JsonKey(name: 'quic-go-disable-gso')  bool quicGoDisableGso, @JsonKey(name: 'quic-go-disable-ecn')  bool quicGoDisableEcn, @JsonKey(name: 'dialer-ip4p-convert')  bool dialerIp4pConvert)  $default,) {final _that = this;\nswitch (_that) {\ncase _Experimental():\nreturn $default(_that.quicGoDisableGso,_that.quicGoDisableEcn,_that.dialerIp4pConvert);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(@JsonKey(name: 'quic-go-disable-gso')  bool quicGoDisableGso, @JsonKey(name: 'quic-go-disable-ecn')  bool quicGoDisableEcn, @JsonKey(name: 'dialer-ip4p-convert')  bool dialerIp4pConvert)?  $default,) {final _that = this;\nswitch (_that) {\ncase _Experimental() when $default != null:\nreturn $default(_that.quicGoDisableGso,_that.quicGoDisableEcn,_that.dialerIp4pConvert);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _Experimental implements Experimental {\n  const _Experimental({@JsonKey(name: 'quic-go-disable-gso') this.quicGoDisableGso = true, @JsonKey(name: 'quic-go-disable-ecn') this.quicGoDisableEcn = true, @JsonKey(name: 'dialer-ip4p-convert') this.dialerIp4pConvert = false});\n  factory _Experimental.fromJson(Map<String, dynamic> json) => _$ExperimentalFromJson(json);\n\n@override@JsonKey(name: 'quic-go-disable-gso') final  bool quicGoDisableGso;\n@override@JsonKey(name: 'quic-go-disable-ecn') final  bool quicGoDisableEcn;\n@override@JsonKey(name: 'dialer-ip4p-convert') final  bool dialerIp4pConvert;\n\n/// Create a copy of Experimental\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ExperimentalCopyWith<_Experimental> get copyWith => __$ExperimentalCopyWithImpl<_Experimental>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$ExperimentalToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _Experimental&&(identical(other.quicGoDisableGso, quicGoDisableGso) || other.quicGoDisableGso == quicGoDisableGso)&&(identical(other.quicGoDisableEcn, quicGoDisableEcn) || other.quicGoDisableEcn == quicGoDisableEcn)&&(identical(other.dialerIp4pConvert, dialerIp4pConvert) || other.dialerIp4pConvert == dialerIp4pConvert));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,quicGoDisableGso,quicGoDisableEcn,dialerIp4pConvert);\n\n@override\nString toString() {\n  return 'Experimental(quicGoDisableGso: $quicGoDisableGso, quicGoDisableEcn: $quicGoDisableEcn, dialerIp4pConvert: $dialerIp4pConvert)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ExperimentalCopyWith<$Res> implements $ExperimentalCopyWith<$Res> {\n  factory _$ExperimentalCopyWith(_Experimental value, $Res Function(_Experimental) _then) = __$ExperimentalCopyWithImpl;\n@override @useResult\n$Res call({\n@JsonKey(name: 'quic-go-disable-gso') bool quicGoDisableGso,@JsonKey(name: 'quic-go-disable-ecn') bool quicGoDisableEcn,@JsonKey(name: 'dialer-ip4p-convert') bool dialerIp4pConvert\n});\n\n\n\n\n}\n/// @nodoc\nclass __$ExperimentalCopyWithImpl<$Res>\n    implements _$ExperimentalCopyWith<$Res> {\n  __$ExperimentalCopyWithImpl(this._self, this._then);\n\n  final _Experimental _self;\n  final $Res Function(_Experimental) _then;\n\n/// Create a copy of Experimental\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? quicGoDisableGso = null,Object? quicGoDisableEcn = null,Object? dialerIp4pConvert = null,}) {\n  return _then(_Experimental(\nquicGoDisableGso: null == quicGoDisableGso ? _self.quicGoDisableGso : quicGoDisableGso // ignore: cast_nullable_to_non_nullable\nas bool,quicGoDisableEcn: null == quicGoDisableEcn ? _self.quicGoDisableEcn : quicGoDisableEcn // ignore: cast_nullable_to_non_nullable\nas bool,dialerIp4pConvert: null == dialerIp4pConvert ? _self.dialerIp4pConvert : dialerIp4pConvert // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$GeoXUrl {\n\n String get mmdb; String get asn; String get geoip; String get geosite;\n/// Create a copy of GeoXUrl\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$GeoXUrlCopyWith<GeoXUrl> get copyWith => _$GeoXUrlCopyWithImpl<GeoXUrl>(this as GeoXUrl, _$identity);\n\n  /// Serializes this GeoXUrl to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is GeoXUrl&&(identical(other.mmdb, mmdb) || other.mmdb == mmdb)&&(identical(other.asn, asn) || other.asn == asn)&&(identical(other.geoip, geoip) || other.geoip == geoip)&&(identical(other.geosite, geosite) || other.geosite == geosite));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,mmdb,asn,geoip,geosite);\n\n@override\nString toString() {\n  return 'GeoXUrl(mmdb: $mmdb, asn: $asn, geoip: $geoip, geosite: $geosite)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $GeoXUrlCopyWith<$Res>  {\n  factory $GeoXUrlCopyWith(GeoXUrl value, $Res Function(GeoXUrl) _then) = _$GeoXUrlCopyWithImpl;\n@useResult\n$Res call({\n String mmdb, String asn, String geoip, String geosite\n});\n\n\n\n\n}\n/// @nodoc\nclass _$GeoXUrlCopyWithImpl<$Res>\n    implements $GeoXUrlCopyWith<$Res> {\n  _$GeoXUrlCopyWithImpl(this._self, this._then);\n\n  final GeoXUrl _self;\n  final $Res Function(GeoXUrl) _then;\n\n/// Create a copy of GeoXUrl\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? mmdb = null,Object? asn = null,Object? geoip = null,Object? geosite = null,}) {\n  return _then(_self.copyWith(\nmmdb: null == mmdb ? _self.mmdb : mmdb // ignore: cast_nullable_to_non_nullable\nas String,asn: null == asn ? _self.asn : asn // ignore: cast_nullable_to_non_nullable\nas String,geoip: null == geoip ? _self.geoip : geoip // ignore: cast_nullable_to_non_nullable\nas String,geosite: null == geosite ? _self.geosite : geosite // ignore: cast_nullable_to_non_nullable\nas String,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [GeoXUrl].\nextension GeoXUrlPatterns on GeoXUrl {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _GeoXUrl value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _GeoXUrl() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _GeoXUrl value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _GeoXUrl():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _GeoXUrl value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _GeoXUrl() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String mmdb,  String asn,  String geoip,  String geosite)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _GeoXUrl() when $default != null:\nreturn $default(_that.mmdb,_that.asn,_that.geoip,_that.geosite);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String mmdb,  String asn,  String geoip,  String geosite)  $default,) {final _that = this;\nswitch (_that) {\ncase _GeoXUrl():\nreturn $default(_that.mmdb,_that.asn,_that.geoip,_that.geosite);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String mmdb,  String asn,  String geoip,  String geosite)?  $default,) {final _that = this;\nswitch (_that) {\ncase _GeoXUrl() when $default != null:\nreturn $default(_that.mmdb,_that.asn,_that.geoip,_that.geosite);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _GeoXUrl implements GeoXUrl {\n  const _GeoXUrl({this.mmdb = 'https://fastly.jsdelivr.net/gh/appshubcc/bett-rules@release/geoip.metadb', this.asn = 'https://fastly.jsdelivr.net/gh/appshubcc/bett-rules@release/GeoLite2-ASN.mmdb', this.geoip = 'https://fastly.jsdelivr.net/gh/appshubcc/bett-rules@release/geoip.dat', this.geosite = 'https://fastly.jsdelivr.net/gh/appshubcc/bett-rules@release/geosite.dat'});\n  factory _GeoXUrl.fromJson(Map<String, dynamic> json) => _$GeoXUrlFromJson(json);\n\n@override@JsonKey() final  String mmdb;\n@override@JsonKey() final  String asn;\n@override@JsonKey() final  String geoip;\n@override@JsonKey() final  String geosite;\n\n/// Create a copy of GeoXUrl\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$GeoXUrlCopyWith<_GeoXUrl> get copyWith => __$GeoXUrlCopyWithImpl<_GeoXUrl>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$GeoXUrlToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _GeoXUrl&&(identical(other.mmdb, mmdb) || other.mmdb == mmdb)&&(identical(other.asn, asn) || other.asn == asn)&&(identical(other.geoip, geoip) || other.geoip == geoip)&&(identical(other.geosite, geosite) || other.geosite == geosite));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,mmdb,asn,geoip,geosite);\n\n@override\nString toString() {\n  return 'GeoXUrl(mmdb: $mmdb, asn: $asn, geoip: $geoip, geosite: $geosite)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$GeoXUrlCopyWith<$Res> implements $GeoXUrlCopyWith<$Res> {\n  factory _$GeoXUrlCopyWith(_GeoXUrl value, $Res Function(_GeoXUrl) _then) = __$GeoXUrlCopyWithImpl;\n@override @useResult\n$Res call({\n String mmdb, String asn, String geoip, String geosite\n});\n\n\n\n\n}\n/// @nodoc\nclass __$GeoXUrlCopyWithImpl<$Res>\n    implements _$GeoXUrlCopyWith<$Res> {\n  __$GeoXUrlCopyWithImpl(this._self, this._then);\n\n  final _GeoXUrl _self;\n  final $Res Function(_GeoXUrl) _then;\n\n/// Create a copy of GeoXUrl\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? mmdb = null,Object? asn = null,Object? geoip = null,Object? geosite = null,}) {\n  return _then(_GeoXUrl(\nmmdb: null == mmdb ? _self.mmdb : mmdb // ignore: cast_nullable_to_non_nullable\nas String,asn: null == asn ? _self.asn : asn // ignore: cast_nullable_to_non_nullable\nas String,geoip: null == geoip ? _self.geoip : geoip // ignore: cast_nullable_to_non_nullable\nas String,geosite: null == geosite ? _self.geosite : geosite // ignore: cast_nullable_to_non_nullable\nas String,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$ParsedRule {\n\n RuleAction get ruleAction; String? get content; String? get ruleTarget; String? get ruleProvider; String? get subRule; bool get noResolve; bool get src;\n/// Create a copy of ParsedRule\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ParsedRuleCopyWith<ParsedRule> get copyWith => _$ParsedRuleCopyWithImpl<ParsedRule>(this as ParsedRule, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is ParsedRule&&(identical(other.ruleAction, ruleAction) || other.ruleAction == ruleAction)&&(identical(other.content, content) || other.content == content)&&(identical(other.ruleTarget, ruleTarget) || other.ruleTarget == ruleTarget)&&(identical(other.ruleProvider, ruleProvider) || other.ruleProvider == ruleProvider)&&(identical(other.subRule, subRule) || other.subRule == subRule)&&(identical(other.noResolve, noResolve) || other.noResolve == noResolve)&&(identical(other.src, src) || other.src == src));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,ruleAction,content,ruleTarget,ruleProvider,subRule,noResolve,src);\n\n@override\nString toString() {\n  return 'ParsedRule(ruleAction: $ruleAction, content: $content, ruleTarget: $ruleTarget, ruleProvider: $ruleProvider, subRule: $subRule, noResolve: $noResolve, src: $src)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ParsedRuleCopyWith<$Res>  {\n  factory $ParsedRuleCopyWith(ParsedRule value, $Res Function(ParsedRule) _then) = _$ParsedRuleCopyWithImpl;\n@useResult\n$Res call({\n RuleAction ruleAction, String? content, String? ruleTarget, String? ruleProvider, String? subRule, bool noResolve, bool src\n});\n\n\n\n\n}\n/// @nodoc\nclass _$ParsedRuleCopyWithImpl<$Res>\n    implements $ParsedRuleCopyWith<$Res> {\n  _$ParsedRuleCopyWithImpl(this._self, this._then);\n\n  final ParsedRule _self;\n  final $Res Function(ParsedRule) _then;\n\n/// Create a copy of ParsedRule\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? ruleAction = null,Object? content = freezed,Object? ruleTarget = freezed,Object? ruleProvider = freezed,Object? subRule = freezed,Object? noResolve = null,Object? src = null,}) {\n  return _then(_self.copyWith(\nruleAction: null == ruleAction ? _self.ruleAction : ruleAction // ignore: cast_nullable_to_non_nullable\nas RuleAction,content: freezed == content ? _self.content : content // ignore: cast_nullable_to_non_nullable\nas String?,ruleTarget: freezed == ruleTarget ? _self.ruleTarget : ruleTarget // ignore: cast_nullable_to_non_nullable\nas String?,ruleProvider: freezed == ruleProvider ? _self.ruleProvider : ruleProvider // ignore: cast_nullable_to_non_nullable\nas String?,subRule: freezed == subRule ? _self.subRule : subRule // ignore: cast_nullable_to_non_nullable\nas String?,noResolve: null == noResolve ? _self.noResolve : noResolve // ignore: cast_nullable_to_non_nullable\nas bool,src: null == src ? _self.src : src // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [ParsedRule].\nextension ParsedRulePatterns on ParsedRule {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ParsedRule value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _ParsedRule() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ParsedRule value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ParsedRule():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ParsedRule value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ParsedRule() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( RuleAction ruleAction,  String? content,  String? ruleTarget,  String? ruleProvider,  String? subRule,  bool noResolve,  bool src)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _ParsedRule() when $default != null:\nreturn $default(_that.ruleAction,_that.content,_that.ruleTarget,_that.ruleProvider,_that.subRule,_that.noResolve,_that.src);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( RuleAction ruleAction,  String? content,  String? ruleTarget,  String? ruleProvider,  String? subRule,  bool noResolve,  bool src)  $default,) {final _that = this;\nswitch (_that) {\ncase _ParsedRule():\nreturn $default(_that.ruleAction,_that.content,_that.ruleTarget,_that.ruleProvider,_that.subRule,_that.noResolve,_that.src);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( RuleAction ruleAction,  String? content,  String? ruleTarget,  String? ruleProvider,  String? subRule,  bool noResolve,  bool src)?  $default,) {final _that = this;\nswitch (_that) {\ncase _ParsedRule() when $default != null:\nreturn $default(_that.ruleAction,_that.content,_that.ruleTarget,_that.ruleProvider,_that.subRule,_that.noResolve,_that.src);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _ParsedRule implements ParsedRule {\n  const _ParsedRule({required this.ruleAction, this.content, this.ruleTarget, this.ruleProvider, this.subRule, this.noResolve = false, this.src = false});\n  \n\n@override final  RuleAction ruleAction;\n@override final  String? content;\n@override final  String? ruleTarget;\n@override final  String? ruleProvider;\n@override final  String? subRule;\n@override@JsonKey() final  bool noResolve;\n@override@JsonKey() final  bool src;\n\n/// Create a copy of ParsedRule\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ParsedRuleCopyWith<_ParsedRule> get copyWith => __$ParsedRuleCopyWithImpl<_ParsedRule>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _ParsedRule&&(identical(other.ruleAction, ruleAction) || other.ruleAction == ruleAction)&&(identical(other.content, content) || other.content == content)&&(identical(other.ruleTarget, ruleTarget) || other.ruleTarget == ruleTarget)&&(identical(other.ruleProvider, ruleProvider) || other.ruleProvider == ruleProvider)&&(identical(other.subRule, subRule) || other.subRule == subRule)&&(identical(other.noResolve, noResolve) || other.noResolve == noResolve)&&(identical(other.src, src) || other.src == src));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,ruleAction,content,ruleTarget,ruleProvider,subRule,noResolve,src);\n\n@override\nString toString() {\n  return 'ParsedRule(ruleAction: $ruleAction, content: $content, ruleTarget: $ruleTarget, ruleProvider: $ruleProvider, subRule: $subRule, noResolve: $noResolve, src: $src)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ParsedRuleCopyWith<$Res> implements $ParsedRuleCopyWith<$Res> {\n  factory _$ParsedRuleCopyWith(_ParsedRule value, $Res Function(_ParsedRule) _then) = __$ParsedRuleCopyWithImpl;\n@override @useResult\n$Res call({\n RuleAction ruleAction, String? content, String? ruleTarget, String? ruleProvider, String? subRule, bool noResolve, bool src\n});\n\n\n\n\n}\n/// @nodoc\nclass __$ParsedRuleCopyWithImpl<$Res>\n    implements _$ParsedRuleCopyWith<$Res> {\n  __$ParsedRuleCopyWithImpl(this._self, this._then);\n\n  final _ParsedRule _self;\n  final $Res Function(_ParsedRule) _then;\n\n/// Create a copy of ParsedRule\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? ruleAction = null,Object? content = freezed,Object? ruleTarget = freezed,Object? ruleProvider = freezed,Object? subRule = freezed,Object? noResolve = null,Object? src = null,}) {\n  return _then(_ParsedRule(\nruleAction: null == ruleAction ? _self.ruleAction : ruleAction // ignore: cast_nullable_to_non_nullable\nas RuleAction,content: freezed == content ? _self.content : content // ignore: cast_nullable_to_non_nullable\nas String?,ruleTarget: freezed == ruleTarget ? _self.ruleTarget : ruleTarget // ignore: cast_nullable_to_non_nullable\nas String?,ruleProvider: freezed == ruleProvider ? _self.ruleProvider : ruleProvider // ignore: cast_nullable_to_non_nullable\nas String?,subRule: freezed == subRule ? _self.subRule : subRule // ignore: cast_nullable_to_non_nullable\nas String?,noResolve: null == noResolve ? _self.noResolve : noResolve // ignore: cast_nullable_to_non_nullable\nas bool,src: null == src ? _self.src : src // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$Rule {\n\n String get id; String get value;\n/// Create a copy of Rule\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$RuleCopyWith<Rule> get copyWith => _$RuleCopyWithImpl<Rule>(this as Rule, _$identity);\n\n  /// Serializes this Rule to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is Rule&&(identical(other.id, id) || other.id == id)&&(identical(other.value, value) || other.value == value));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,id,value);\n\n@override\nString toString() {\n  return 'Rule(id: $id, value: $value)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $RuleCopyWith<$Res>  {\n  factory $RuleCopyWith(Rule value, $Res Function(Rule) _then) = _$RuleCopyWithImpl;\n@useResult\n$Res call({\n String id, String value\n});\n\n\n\n\n}\n/// @nodoc\nclass _$RuleCopyWithImpl<$Res>\n    implements $RuleCopyWith<$Res> {\n  _$RuleCopyWithImpl(this._self, this._then);\n\n  final Rule _self;\n  final $Res Function(Rule) _then;\n\n/// Create a copy of Rule\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? value = null,}) {\n  return _then(_self.copyWith(\nid: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable\nas String,value: null == value ? _self.value : value // ignore: cast_nullable_to_non_nullable\nas String,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [Rule].\nextension RulePatterns on Rule {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _Rule value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _Rule() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _Rule value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Rule():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _Rule value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Rule() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id,  String value)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _Rule() when $default != null:\nreturn $default(_that.id,_that.value);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id,  String value)  $default,) {final _that = this;\nswitch (_that) {\ncase _Rule():\nreturn $default(_that.id,_that.value);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id,  String value)?  $default,) {final _that = this;\nswitch (_that) {\ncase _Rule() when $default != null:\nreturn $default(_that.id,_that.value);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _Rule implements Rule {\n  const _Rule({required this.id, required this.value});\n  factory _Rule.fromJson(Map<String, dynamic> json) => _$RuleFromJson(json);\n\n@override final  String id;\n@override final  String value;\n\n/// Create a copy of Rule\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$RuleCopyWith<_Rule> get copyWith => __$RuleCopyWithImpl<_Rule>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$RuleToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _Rule&&(identical(other.id, id) || other.id == id)&&(identical(other.value, value) || other.value == value));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,id,value);\n\n@override\nString toString() {\n  return 'Rule(id: $id, value: $value)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$RuleCopyWith<$Res> implements $RuleCopyWith<$Res> {\n  factory _$RuleCopyWith(_Rule value, $Res Function(_Rule) _then) = __$RuleCopyWithImpl;\n@override @useResult\n$Res call({\n String id, String value\n});\n\n\n\n\n}\n/// @nodoc\nclass __$RuleCopyWithImpl<$Res>\n    implements _$RuleCopyWith<$Res> {\n  __$RuleCopyWithImpl(this._self, this._then);\n\n  final _Rule _self;\n  final $Res Function(_Rule) _then;\n\n/// Create a copy of Rule\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? value = null,}) {\n  return _then(_Rule(\nid: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable\nas String,value: null == value ? _self.value : value // ignore: cast_nullable_to_non_nullable\nas String,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$SubRule {\n\n String get name;\n/// Create a copy of SubRule\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$SubRuleCopyWith<SubRule> get copyWith => _$SubRuleCopyWithImpl<SubRule>(this as SubRule, _$identity);\n\n  /// Serializes this SubRule to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is SubRule&&(identical(other.name, name) || other.name == name));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,name);\n\n@override\nString toString() {\n  return 'SubRule(name: $name)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $SubRuleCopyWith<$Res>  {\n  factory $SubRuleCopyWith(SubRule value, $Res Function(SubRule) _then) = _$SubRuleCopyWithImpl;\n@useResult\n$Res call({\n String name\n});\n\n\n\n\n}\n/// @nodoc\nclass _$SubRuleCopyWithImpl<$Res>\n    implements $SubRuleCopyWith<$Res> {\n  _$SubRuleCopyWithImpl(this._self, this._then);\n\n  final SubRule _self;\n  final $Res Function(SubRule) _then;\n\n/// Create a copy of SubRule\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? name = null,}) {\n  return _then(_self.copyWith(\nname: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable\nas String,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [SubRule].\nextension SubRulePatterns on SubRule {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SubRule value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _SubRule() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SubRule value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _SubRule():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SubRule value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _SubRule() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String name)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _SubRule() when $default != null:\nreturn $default(_that.name);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String name)  $default,) {final _that = this;\nswitch (_that) {\ncase _SubRule():\nreturn $default(_that.name);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String name)?  $default,) {final _that = this;\nswitch (_that) {\ncase _SubRule() when $default != null:\nreturn $default(_that.name);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _SubRule implements SubRule {\n  const _SubRule({required this.name});\n  factory _SubRule.fromJson(Map<String, dynamic> json) => _$SubRuleFromJson(json);\n\n@override final  String name;\n\n/// Create a copy of SubRule\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$SubRuleCopyWith<_SubRule> get copyWith => __$SubRuleCopyWithImpl<_SubRule>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$SubRuleToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _SubRule&&(identical(other.name, name) || other.name == name));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,name);\n\n@override\nString toString() {\n  return 'SubRule(name: $name)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$SubRuleCopyWith<$Res> implements $SubRuleCopyWith<$Res> {\n  factory _$SubRuleCopyWith(_SubRule value, $Res Function(_SubRule) _then) = __$SubRuleCopyWithImpl;\n@override @useResult\n$Res call({\n String name\n});\n\n\n\n\n}\n/// @nodoc\nclass __$SubRuleCopyWithImpl<$Res>\n    implements _$SubRuleCopyWith<$Res> {\n  __$SubRuleCopyWithImpl(this._self, this._then);\n\n  final _SubRule _self;\n  final $Res Function(_SubRule) _then;\n\n/// Create a copy of SubRule\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? name = null,}) {\n  return _then(_SubRule(\nname: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable\nas String,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$ClashConfigSnippet {\n\n@JsonKey(name: 'proxy-groups') List<ProxyGroup> get proxyGroups;@JsonKey(fromJson: _genRule, name: 'rules') List<Rule> get rule;@JsonKey(name: 'rule-providers', fromJson: _genRuleProviders) List<RuleProvider> get ruleProvider;@JsonKey(name: 'sub-rules', fromJson: _genSubRules) List<SubRule> get subRules;\n/// Create a copy of ClashConfigSnippet\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ClashConfigSnippetCopyWith<ClashConfigSnippet> get copyWith => _$ClashConfigSnippetCopyWithImpl<ClashConfigSnippet>(this as ClashConfigSnippet, _$identity);\n\n  /// Serializes this ClashConfigSnippet to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is ClashConfigSnippet&&const DeepCollectionEquality().equals(other.proxyGroups, proxyGroups)&&const DeepCollectionEquality().equals(other.rule, rule)&&const DeepCollectionEquality().equals(other.ruleProvider, ruleProvider)&&const DeepCollectionEquality().equals(other.subRules, subRules));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(proxyGroups),const DeepCollectionEquality().hash(rule),const DeepCollectionEquality().hash(ruleProvider),const DeepCollectionEquality().hash(subRules));\n\n@override\nString toString() {\n  return 'ClashConfigSnippet(proxyGroups: $proxyGroups, rule: $rule, ruleProvider: $ruleProvider, subRules: $subRules)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ClashConfigSnippetCopyWith<$Res>  {\n  factory $ClashConfigSnippetCopyWith(ClashConfigSnippet value, $Res Function(ClashConfigSnippet) _then) = _$ClashConfigSnippetCopyWithImpl;\n@useResult\n$Res call({\n@JsonKey(name: 'proxy-groups') List<ProxyGroup> proxyGroups,@JsonKey(fromJson: _genRule, name: 'rules') List<Rule> rule,@JsonKey(name: 'rule-providers', fromJson: _genRuleProviders) List<RuleProvider> ruleProvider,@JsonKey(name: 'sub-rules', fromJson: _genSubRules) List<SubRule> subRules\n});\n\n\n\n\n}\n/// @nodoc\nclass _$ClashConfigSnippetCopyWithImpl<$Res>\n    implements $ClashConfigSnippetCopyWith<$Res> {\n  _$ClashConfigSnippetCopyWithImpl(this._self, this._then);\n\n  final ClashConfigSnippet _self;\n  final $Res Function(ClashConfigSnippet) _then;\n\n/// Create a copy of ClashConfigSnippet\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? proxyGroups = null,Object? rule = null,Object? ruleProvider = null,Object? subRules = null,}) {\n  return _then(_self.copyWith(\nproxyGroups: null == proxyGroups ? _self.proxyGroups : proxyGroups // ignore: cast_nullable_to_non_nullable\nas List<ProxyGroup>,rule: null == rule ? _self.rule : rule // ignore: cast_nullable_to_non_nullable\nas List<Rule>,ruleProvider: null == ruleProvider ? _self.ruleProvider : ruleProvider // ignore: cast_nullable_to_non_nullable\nas List<RuleProvider>,subRules: null == subRules ? _self.subRules : subRules // ignore: cast_nullable_to_non_nullable\nas List<SubRule>,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [ClashConfigSnippet].\nextension ClashConfigSnippetPatterns on ClashConfigSnippet {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ClashConfigSnippet value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _ClashConfigSnippet() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ClashConfigSnippet value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ClashConfigSnippet():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ClashConfigSnippet value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ClashConfigSnippet() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(@JsonKey(name: 'proxy-groups')  List<ProxyGroup> proxyGroups, @JsonKey(fromJson: _genRule, name: 'rules')  List<Rule> rule, @JsonKey(name: 'rule-providers', fromJson: _genRuleProviders)  List<RuleProvider> ruleProvider, @JsonKey(name: 'sub-rules', fromJson: _genSubRules)  List<SubRule> subRules)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _ClashConfigSnippet() when $default != null:\nreturn $default(_that.proxyGroups,_that.rule,_that.ruleProvider,_that.subRules);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(@JsonKey(name: 'proxy-groups')  List<ProxyGroup> proxyGroups, @JsonKey(fromJson: _genRule, name: 'rules')  List<Rule> rule, @JsonKey(name: 'rule-providers', fromJson: _genRuleProviders)  List<RuleProvider> ruleProvider, @JsonKey(name: 'sub-rules', fromJson: _genSubRules)  List<SubRule> subRules)  $default,) {final _that = this;\nswitch (_that) {\ncase _ClashConfigSnippet():\nreturn $default(_that.proxyGroups,_that.rule,_that.ruleProvider,_that.subRules);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(@JsonKey(name: 'proxy-groups')  List<ProxyGroup> proxyGroups, @JsonKey(fromJson: _genRule, name: 'rules')  List<Rule> rule, @JsonKey(name: 'rule-providers', fromJson: _genRuleProviders)  List<RuleProvider> ruleProvider, @JsonKey(name: 'sub-rules', fromJson: _genSubRules)  List<SubRule> subRules)?  $default,) {final _that = this;\nswitch (_that) {\ncase _ClashConfigSnippet() when $default != null:\nreturn $default(_that.proxyGroups,_that.rule,_that.ruleProvider,_that.subRules);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _ClashConfigSnippet implements ClashConfigSnippet {\n  const _ClashConfigSnippet({@JsonKey(name: 'proxy-groups') final  List<ProxyGroup> proxyGroups = const [], @JsonKey(fromJson: _genRule, name: 'rules') final  List<Rule> rule = const [], @JsonKey(name: 'rule-providers', fromJson: _genRuleProviders) final  List<RuleProvider> ruleProvider = const [], @JsonKey(name: 'sub-rules', fromJson: _genSubRules) final  List<SubRule> subRules = const []}): _proxyGroups = proxyGroups,_rule = rule,_ruleProvider = ruleProvider,_subRules = subRules;\n  factory _ClashConfigSnippet.fromJson(Map<String, dynamic> json) => _$ClashConfigSnippetFromJson(json);\n\n final  List<ProxyGroup> _proxyGroups;\n@override@JsonKey(name: 'proxy-groups') List<ProxyGroup> get proxyGroups {\n  if (_proxyGroups is EqualUnmodifiableListView) return _proxyGroups;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_proxyGroups);\n}\n\n final  List<Rule> _rule;\n@override@JsonKey(fromJson: _genRule, name: 'rules') List<Rule> get rule {\n  if (_rule is EqualUnmodifiableListView) return _rule;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_rule);\n}\n\n final  List<RuleProvider> _ruleProvider;\n@override@JsonKey(name: 'rule-providers', fromJson: _genRuleProviders) List<RuleProvider> get ruleProvider {\n  if (_ruleProvider is EqualUnmodifiableListView) return _ruleProvider;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_ruleProvider);\n}\n\n final  List<SubRule> _subRules;\n@override@JsonKey(name: 'sub-rules', fromJson: _genSubRules) List<SubRule> get subRules {\n  if (_subRules is EqualUnmodifiableListView) return _subRules;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_subRules);\n}\n\n\n/// Create a copy of ClashConfigSnippet\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ClashConfigSnippetCopyWith<_ClashConfigSnippet> get copyWith => __$ClashConfigSnippetCopyWithImpl<_ClashConfigSnippet>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$ClashConfigSnippetToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _ClashConfigSnippet&&const DeepCollectionEquality().equals(other._proxyGroups, _proxyGroups)&&const DeepCollectionEquality().equals(other._rule, _rule)&&const DeepCollectionEquality().equals(other._ruleProvider, _ruleProvider)&&const DeepCollectionEquality().equals(other._subRules, _subRules));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_proxyGroups),const DeepCollectionEquality().hash(_rule),const DeepCollectionEquality().hash(_ruleProvider),const DeepCollectionEquality().hash(_subRules));\n\n@override\nString toString() {\n  return 'ClashConfigSnippet(proxyGroups: $proxyGroups, rule: $rule, ruleProvider: $ruleProvider, subRules: $subRules)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ClashConfigSnippetCopyWith<$Res> implements $ClashConfigSnippetCopyWith<$Res> {\n  factory _$ClashConfigSnippetCopyWith(_ClashConfigSnippet value, $Res Function(_ClashConfigSnippet) _then) = __$ClashConfigSnippetCopyWithImpl;\n@override @useResult\n$Res call({\n@JsonKey(name: 'proxy-groups') List<ProxyGroup> proxyGroups,@JsonKey(fromJson: _genRule, name: 'rules') List<Rule> rule,@JsonKey(name: 'rule-providers', fromJson: _genRuleProviders) List<RuleProvider> ruleProvider,@JsonKey(name: 'sub-rules', fromJson: _genSubRules) List<SubRule> subRules\n});\n\n\n\n\n}\n/// @nodoc\nclass __$ClashConfigSnippetCopyWithImpl<$Res>\n    implements _$ClashConfigSnippetCopyWith<$Res> {\n  __$ClashConfigSnippetCopyWithImpl(this._self, this._then);\n\n  final _ClashConfigSnippet _self;\n  final $Res Function(_ClashConfigSnippet) _then;\n\n/// Create a copy of ClashConfigSnippet\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? proxyGroups = null,Object? rule = null,Object? ruleProvider = null,Object? subRules = null,}) {\n  return _then(_ClashConfigSnippet(\nproxyGroups: null == proxyGroups ? _self._proxyGroups : proxyGroups // ignore: cast_nullable_to_non_nullable\nas List<ProxyGroup>,rule: null == rule ? _self._rule : rule // ignore: cast_nullable_to_non_nullable\nas List<Rule>,ruleProvider: null == ruleProvider ? _self._ruleProvider : ruleProvider // ignore: cast_nullable_to_non_nullable\nas List<RuleProvider>,subRules: null == subRules ? _self._subRules : subRules // ignore: cast_nullable_to_non_nullable\nas List<SubRule>,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$ClashConfig {\n\n@JsonKey(name: 'mixed-port') int get mixedPort;@JsonKey(name: 'socks-port') int get socksPort;@JsonKey(name: 'port') int get port;@JsonKey(name: 'redir-port') int get redirPort;@JsonKey(name: 'tproxy-port') int get tproxyPort; Mode get mode;@JsonKey(name: 'allow-lan') bool get allowLan;@JsonKey(name: 'log-level') LogLevel get logLevel; bool get ipv6;@JsonKey(name: 'find-process-mode', unknownEnumValue: FindProcessMode.always) FindProcessMode get findProcessMode;@JsonKey(name: 'keep-alive-interval') int get keepAliveInterval;@JsonKey(name: 'unified-delay') bool get unifiedDelay;@JsonKey(name: 'tcp-concurrent') bool get tcpConcurrent;@JsonKey(fromJson: Tun.safeFormJson) Tun get tun;@JsonKey(fromJson: Dns.safeDnsFromJson) Dns get dns;@JsonKey(fromJson: Ntp.safeNtpFromJson) Ntp get ntp;@JsonKey(fromJson: Sniffer.safeSnifferFromJson) Sniffer get sniffer; List<TunnelEntry> get tunnels;@JsonKey(fromJson: Experimental.safeExperimentalFromJson) Experimental get experimental;@JsonKey(name: 'geox-url', fromJson: GeoXUrl.safeFormJson) GeoXUrl get geoXUrl;@JsonKey(name: 'geodata-loader') GeodataLoader get geodataLoader;@JsonKey(name: 'proxy-groups') List<ProxyGroup> get proxyGroups; List<String> get rule;@JsonKey(name: 'global-ua') String? get globalUa;@JsonKey(name: 'external-controller') ExternalControllerStatus get externalController; String? get secret;@JsonKey(name: 'external-ui-name') String? get externalUiName;@JsonKey(name: 'external-ui-url') String? get externalUiUrl; HostsMap get hosts;\n/// Create a copy of ClashConfig\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ClashConfigCopyWith<ClashConfig> get copyWith => _$ClashConfigCopyWithImpl<ClashConfig>(this as ClashConfig, _$identity);\n\n  /// Serializes this ClashConfig to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is ClashConfig&&(identical(other.mixedPort, mixedPort) || other.mixedPort == mixedPort)&&(identical(other.socksPort, socksPort) || other.socksPort == socksPort)&&(identical(other.port, port) || other.port == port)&&(identical(other.redirPort, redirPort) || other.redirPort == redirPort)&&(identical(other.tproxyPort, tproxyPort) || other.tproxyPort == tproxyPort)&&(identical(other.mode, mode) || other.mode == mode)&&(identical(other.allowLan, allowLan) || other.allowLan == allowLan)&&(identical(other.logLevel, logLevel) || other.logLevel == logLevel)&&(identical(other.ipv6, ipv6) || other.ipv6 == ipv6)&&(identical(other.findProcessMode, findProcessMode) || other.findProcessMode == findProcessMode)&&(identical(other.keepAliveInterval, keepAliveInterval) || other.keepAliveInterval == keepAliveInterval)&&(identical(other.unifiedDelay, unifiedDelay) || other.unifiedDelay == unifiedDelay)&&(identical(other.tcpConcurrent, tcpConcurrent) || other.tcpConcurrent == tcpConcurrent)&&(identical(other.tun, tun) || other.tun == tun)&&(identical(other.dns, dns) || other.dns == dns)&&(identical(other.ntp, ntp) || other.ntp == ntp)&&(identical(other.sniffer, sniffer) || other.sniffer == sniffer)&&const DeepCollectionEquality().equals(other.tunnels, tunnels)&&(identical(other.experimental, experimental) || other.experimental == experimental)&&(identical(other.geoXUrl, geoXUrl) || other.geoXUrl == geoXUrl)&&(identical(other.geodataLoader, geodataLoader) || other.geodataLoader == geodataLoader)&&const DeepCollectionEquality().equals(other.proxyGroups, proxyGroups)&&const DeepCollectionEquality().equals(other.rule, rule)&&(identical(other.globalUa, globalUa) || other.globalUa == globalUa)&&(identical(other.externalController, externalController) || other.externalController == externalController)&&(identical(other.secret, secret) || other.secret == secret)&&(identical(other.externalUiName, externalUiName) || other.externalUiName == externalUiName)&&(identical(other.externalUiUrl, externalUiUrl) || other.externalUiUrl == externalUiUrl)&&const DeepCollectionEquality().equals(other.hosts, hosts));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hashAll([runtimeType,mixedPort,socksPort,port,redirPort,tproxyPort,mode,allowLan,logLevel,ipv6,findProcessMode,keepAliveInterval,unifiedDelay,tcpConcurrent,tun,dns,ntp,sniffer,const DeepCollectionEquality().hash(tunnels),experimental,geoXUrl,geodataLoader,const DeepCollectionEquality().hash(proxyGroups),const DeepCollectionEquality().hash(rule),globalUa,externalController,secret,externalUiName,externalUiUrl,const DeepCollectionEquality().hash(hosts)]);\n\n@override\nString toString() {\n  return 'ClashConfig(mixedPort: $mixedPort, socksPort: $socksPort, port: $port, redirPort: $redirPort, tproxyPort: $tproxyPort, mode: $mode, allowLan: $allowLan, logLevel: $logLevel, ipv6: $ipv6, findProcessMode: $findProcessMode, keepAliveInterval: $keepAliveInterval, unifiedDelay: $unifiedDelay, tcpConcurrent: $tcpConcurrent, tun: $tun, dns: $dns, ntp: $ntp, sniffer: $sniffer, tunnels: $tunnels, experimental: $experimental, geoXUrl: $geoXUrl, geodataLoader: $geodataLoader, proxyGroups: $proxyGroups, rule: $rule, globalUa: $globalUa, externalController: $externalController, secret: $secret, externalUiName: $externalUiName, externalUiUrl: $externalUiUrl, hosts: $hosts)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ClashConfigCopyWith<$Res>  {\n  factory $ClashConfigCopyWith(ClashConfig value, $Res Function(ClashConfig) _then) = _$ClashConfigCopyWithImpl;\n@useResult\n$Res call({\n@JsonKey(name: 'mixed-port') int mixedPort,@JsonKey(name: 'socks-port') int socksPort,@JsonKey(name: 'port') int port,@JsonKey(name: 'redir-port') int redirPort,@JsonKey(name: 'tproxy-port') int tproxyPort, Mode mode,@JsonKey(name: 'allow-lan') bool allowLan,@JsonKey(name: 'log-level') LogLevel logLevel, bool ipv6,@JsonKey(name: 'find-process-mode', unknownEnumValue: FindProcessMode.always) FindProcessMode findProcessMode,@JsonKey(name: 'keep-alive-interval') int keepAliveInterval,@JsonKey(name: 'unified-delay') bool unifiedDelay,@JsonKey(name: 'tcp-concurrent') bool tcpConcurrent,@JsonKey(fromJson: Tun.safeFormJson) Tun tun,@JsonKey(fromJson: Dns.safeDnsFromJson) Dns dns,@JsonKey(fromJson: Ntp.safeNtpFromJson) Ntp ntp,@JsonKey(fromJson: Sniffer.safeSnifferFromJson) Sniffer sniffer, List<TunnelEntry> tunnels,@JsonKey(fromJson: Experimental.safeExperimentalFromJson) Experimental experimental,@JsonKey(name: 'geox-url', fromJson: GeoXUrl.safeFormJson) GeoXUrl geoXUrl,@JsonKey(name: 'geodata-loader') GeodataLoader geodataLoader,@JsonKey(name: 'proxy-groups') List<ProxyGroup> proxyGroups, List<String> rule,@JsonKey(name: 'global-ua') String? globalUa,@JsonKey(name: 'external-controller') ExternalControllerStatus externalController, String? secret,@JsonKey(name: 'external-ui-name') String? externalUiName,@JsonKey(name: 'external-ui-url') String? externalUiUrl, HostsMap hosts\n});\n\n\n$TunCopyWith<$Res> get tun;$DnsCopyWith<$Res> get dns;$NtpCopyWith<$Res> get ntp;$SnifferCopyWith<$Res> get sniffer;$ExperimentalCopyWith<$Res> get experimental;$GeoXUrlCopyWith<$Res> get geoXUrl;\n\n}\n/// @nodoc\nclass _$ClashConfigCopyWithImpl<$Res>\n    implements $ClashConfigCopyWith<$Res> {\n  _$ClashConfigCopyWithImpl(this._self, this._then);\n\n  final ClashConfig _self;\n  final $Res Function(ClashConfig) _then;\n\n/// Create a copy of ClashConfig\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? mixedPort = null,Object? socksPort = null,Object? port = null,Object? redirPort = null,Object? tproxyPort = null,Object? mode = null,Object? allowLan = null,Object? logLevel = null,Object? ipv6 = null,Object? findProcessMode = null,Object? keepAliveInterval = null,Object? unifiedDelay = null,Object? tcpConcurrent = null,Object? tun = null,Object? dns = null,Object? ntp = null,Object? sniffer = null,Object? tunnels = null,Object? experimental = null,Object? geoXUrl = null,Object? geodataLoader = null,Object? proxyGroups = null,Object? rule = null,Object? globalUa = freezed,Object? externalController = null,Object? secret = freezed,Object? externalUiName = freezed,Object? externalUiUrl = freezed,Object? hosts = null,}) {\n  return _then(_self.copyWith(\nmixedPort: null == mixedPort ? _self.mixedPort : mixedPort // ignore: cast_nullable_to_non_nullable\nas int,socksPort: null == socksPort ? _self.socksPort : socksPort // ignore: cast_nullable_to_non_nullable\nas int,port: null == port ? _self.port : port // ignore: cast_nullable_to_non_nullable\nas int,redirPort: null == redirPort ? _self.redirPort : redirPort // ignore: cast_nullable_to_non_nullable\nas int,tproxyPort: null == tproxyPort ? _self.tproxyPort : tproxyPort // ignore: cast_nullable_to_non_nullable\nas int,mode: null == mode ? _self.mode : mode // ignore: cast_nullable_to_non_nullable\nas Mode,allowLan: null == allowLan ? _self.allowLan : allowLan // ignore: cast_nullable_to_non_nullable\nas bool,logLevel: null == logLevel ? _self.logLevel : logLevel // ignore: cast_nullable_to_non_nullable\nas LogLevel,ipv6: null == ipv6 ? _self.ipv6 : ipv6 // ignore: cast_nullable_to_non_nullable\nas bool,findProcessMode: null == findProcessMode ? _self.findProcessMode : findProcessMode // ignore: cast_nullable_to_non_nullable\nas FindProcessMode,keepAliveInterval: null == keepAliveInterval ? _self.keepAliveInterval : keepAliveInterval // ignore: cast_nullable_to_non_nullable\nas int,unifiedDelay: null == unifiedDelay ? _self.unifiedDelay : unifiedDelay // ignore: cast_nullable_to_non_nullable\nas bool,tcpConcurrent: null == tcpConcurrent ? _self.tcpConcurrent : tcpConcurrent // ignore: cast_nullable_to_non_nullable\nas bool,tun: null == tun ? _self.tun : tun // ignore: cast_nullable_to_non_nullable\nas Tun,dns: null == dns ? _self.dns : dns // ignore: cast_nullable_to_non_nullable\nas Dns,ntp: null == ntp ? _self.ntp : ntp // ignore: cast_nullable_to_non_nullable\nas Ntp,sniffer: null == sniffer ? _self.sniffer : sniffer // ignore: cast_nullable_to_non_nullable\nas Sniffer,tunnels: null == tunnels ? _self.tunnels : tunnels // ignore: cast_nullable_to_non_nullable\nas List<TunnelEntry>,experimental: null == experimental ? _self.experimental : experimental // ignore: cast_nullable_to_non_nullable\nas Experimental,geoXUrl: null == geoXUrl ? _self.geoXUrl : geoXUrl // ignore: cast_nullable_to_non_nullable\nas GeoXUrl,geodataLoader: null == geodataLoader ? _self.geodataLoader : geodataLoader // ignore: cast_nullable_to_non_nullable\nas GeodataLoader,proxyGroups: null == proxyGroups ? _self.proxyGroups : proxyGroups // ignore: cast_nullable_to_non_nullable\nas List<ProxyGroup>,rule: null == rule ? _self.rule : rule // ignore: cast_nullable_to_non_nullable\nas List<String>,globalUa: freezed == globalUa ? _self.globalUa : globalUa // ignore: cast_nullable_to_non_nullable\nas String?,externalController: null == externalController ? _self.externalController : externalController // ignore: cast_nullable_to_non_nullable\nas ExternalControllerStatus,secret: freezed == secret ? _self.secret : secret // ignore: cast_nullable_to_non_nullable\nas String?,externalUiName: freezed == externalUiName ? _self.externalUiName : externalUiName // ignore: cast_nullable_to_non_nullable\nas String?,externalUiUrl: freezed == externalUiUrl ? _self.externalUiUrl : externalUiUrl // ignore: cast_nullable_to_non_nullable\nas String?,hosts: null == hosts ? _self.hosts : hosts // ignore: cast_nullable_to_non_nullable\nas HostsMap,\n  ));\n}\n/// Create a copy of ClashConfig\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$TunCopyWith<$Res> get tun {\n  \n  return $TunCopyWith<$Res>(_self.tun, (value) {\n    return _then(_self.copyWith(tun: value));\n  });\n}/// Create a copy of ClashConfig\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$DnsCopyWith<$Res> get dns {\n  \n  return $DnsCopyWith<$Res>(_self.dns, (value) {\n    return _then(_self.copyWith(dns: value));\n  });\n}/// Create a copy of ClashConfig\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$NtpCopyWith<$Res> get ntp {\n  \n  return $NtpCopyWith<$Res>(_self.ntp, (value) {\n    return _then(_self.copyWith(ntp: value));\n  });\n}/// Create a copy of ClashConfig\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$SnifferCopyWith<$Res> get sniffer {\n  \n  return $SnifferCopyWith<$Res>(_self.sniffer, (value) {\n    return _then(_self.copyWith(sniffer: value));\n  });\n}/// Create a copy of ClashConfig\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$ExperimentalCopyWith<$Res> get experimental {\n  \n  return $ExperimentalCopyWith<$Res>(_self.experimental, (value) {\n    return _then(_self.copyWith(experimental: value));\n  });\n}/// Create a copy of ClashConfig\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$GeoXUrlCopyWith<$Res> get geoXUrl {\n  \n  return $GeoXUrlCopyWith<$Res>(_self.geoXUrl, (value) {\n    return _then(_self.copyWith(geoXUrl: value));\n  });\n}\n}\n\n\n/// Adds pattern-matching-related methods to [ClashConfig].\nextension ClashConfigPatterns on ClashConfig {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ClashConfig value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _ClashConfig() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ClashConfig value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ClashConfig():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ClashConfig value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ClashConfig() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(@JsonKey(name: 'mixed-port')  int mixedPort, @JsonKey(name: 'socks-port')  int socksPort, @JsonKey(name: 'port')  int port, @JsonKey(name: 'redir-port')  int redirPort, @JsonKey(name: 'tproxy-port')  int tproxyPort,  Mode mode, @JsonKey(name: 'allow-lan')  bool allowLan, @JsonKey(name: 'log-level')  LogLevel logLevel,  bool ipv6, @JsonKey(name: 'find-process-mode', unknownEnumValue: FindProcessMode.always)  FindProcessMode findProcessMode, @JsonKey(name: 'keep-alive-interval')  int keepAliveInterval, @JsonKey(name: 'unified-delay')  bool unifiedDelay, @JsonKey(name: 'tcp-concurrent')  bool tcpConcurrent, @JsonKey(fromJson: Tun.safeFormJson)  Tun tun, @JsonKey(fromJson: Dns.safeDnsFromJson)  Dns dns, @JsonKey(fromJson: Ntp.safeNtpFromJson)  Ntp ntp, @JsonKey(fromJson: Sniffer.safeSnifferFromJson)  Sniffer sniffer,  List<TunnelEntry> tunnels, @JsonKey(fromJson: Experimental.safeExperimentalFromJson)  Experimental experimental, @JsonKey(name: 'geox-url', fromJson: GeoXUrl.safeFormJson)  GeoXUrl geoXUrl, @JsonKey(name: 'geodata-loader')  GeodataLoader geodataLoader, @JsonKey(name: 'proxy-groups')  List<ProxyGroup> proxyGroups,  List<String> rule, @JsonKey(name: 'global-ua')  String? globalUa, @JsonKey(name: 'external-controller')  ExternalControllerStatus externalController,  String? secret, @JsonKey(name: 'external-ui-name')  String? externalUiName, @JsonKey(name: 'external-ui-url')  String? externalUiUrl,  HostsMap hosts)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _ClashConfig() when $default != null:\nreturn $default(_that.mixedPort,_that.socksPort,_that.port,_that.redirPort,_that.tproxyPort,_that.mode,_that.allowLan,_that.logLevel,_that.ipv6,_that.findProcessMode,_that.keepAliveInterval,_that.unifiedDelay,_that.tcpConcurrent,_that.tun,_that.dns,_that.ntp,_that.sniffer,_that.tunnels,_that.experimental,_that.geoXUrl,_that.geodataLoader,_that.proxyGroups,_that.rule,_that.globalUa,_that.externalController,_that.secret,_that.externalUiName,_that.externalUiUrl,_that.hosts);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(@JsonKey(name: 'mixed-port')  int mixedPort, @JsonKey(name: 'socks-port')  int socksPort, @JsonKey(name: 'port')  int port, @JsonKey(name: 'redir-port')  int redirPort, @JsonKey(name: 'tproxy-port')  int tproxyPort,  Mode mode, @JsonKey(name: 'allow-lan')  bool allowLan, @JsonKey(name: 'log-level')  LogLevel logLevel,  bool ipv6, @JsonKey(name: 'find-process-mode', unknownEnumValue: FindProcessMode.always)  FindProcessMode findProcessMode, @JsonKey(name: 'keep-alive-interval')  int keepAliveInterval, @JsonKey(name: 'unified-delay')  bool unifiedDelay, @JsonKey(name: 'tcp-concurrent')  bool tcpConcurrent, @JsonKey(fromJson: Tun.safeFormJson)  Tun tun, @JsonKey(fromJson: Dns.safeDnsFromJson)  Dns dns, @JsonKey(fromJson: Ntp.safeNtpFromJson)  Ntp ntp, @JsonKey(fromJson: Sniffer.safeSnifferFromJson)  Sniffer sniffer,  List<TunnelEntry> tunnels, @JsonKey(fromJson: Experimental.safeExperimentalFromJson)  Experimental experimental, @JsonKey(name: 'geox-url', fromJson: GeoXUrl.safeFormJson)  GeoXUrl geoXUrl, @JsonKey(name: 'geodata-loader')  GeodataLoader geodataLoader, @JsonKey(name: 'proxy-groups')  List<ProxyGroup> proxyGroups,  List<String> rule, @JsonKey(name: 'global-ua')  String? globalUa, @JsonKey(name: 'external-controller')  ExternalControllerStatus externalController,  String? secret, @JsonKey(name: 'external-ui-name')  String? externalUiName, @JsonKey(name: 'external-ui-url')  String? externalUiUrl,  HostsMap hosts)  $default,) {final _that = this;\nswitch (_that) {\ncase _ClashConfig():\nreturn $default(_that.mixedPort,_that.socksPort,_that.port,_that.redirPort,_that.tproxyPort,_that.mode,_that.allowLan,_that.logLevel,_that.ipv6,_that.findProcessMode,_that.keepAliveInterval,_that.unifiedDelay,_that.tcpConcurrent,_that.tun,_that.dns,_that.ntp,_that.sniffer,_that.tunnels,_that.experimental,_that.geoXUrl,_that.geodataLoader,_that.proxyGroups,_that.rule,_that.globalUa,_that.externalController,_that.secret,_that.externalUiName,_that.externalUiUrl,_that.hosts);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(@JsonKey(name: 'mixed-port')  int mixedPort, @JsonKey(name: 'socks-port')  int socksPort, @JsonKey(name: 'port')  int port, @JsonKey(name: 'redir-port')  int redirPort, @JsonKey(name: 'tproxy-port')  int tproxyPort,  Mode mode, @JsonKey(name: 'allow-lan')  bool allowLan, @JsonKey(name: 'log-level')  LogLevel logLevel,  bool ipv6, @JsonKey(name: 'find-process-mode', unknownEnumValue: FindProcessMode.always)  FindProcessMode findProcessMode, @JsonKey(name: 'keep-alive-interval')  int keepAliveInterval, @JsonKey(name: 'unified-delay')  bool unifiedDelay, @JsonKey(name: 'tcp-concurrent')  bool tcpConcurrent, @JsonKey(fromJson: Tun.safeFormJson)  Tun tun, @JsonKey(fromJson: Dns.safeDnsFromJson)  Dns dns, @JsonKey(fromJson: Ntp.safeNtpFromJson)  Ntp ntp, @JsonKey(fromJson: Sniffer.safeSnifferFromJson)  Sniffer sniffer,  List<TunnelEntry> tunnels, @JsonKey(fromJson: Experimental.safeExperimentalFromJson)  Experimental experimental, @JsonKey(name: 'geox-url', fromJson: GeoXUrl.safeFormJson)  GeoXUrl geoXUrl, @JsonKey(name: 'geodata-loader')  GeodataLoader geodataLoader, @JsonKey(name: 'proxy-groups')  List<ProxyGroup> proxyGroups,  List<String> rule, @JsonKey(name: 'global-ua')  String? globalUa, @JsonKey(name: 'external-controller')  ExternalControllerStatus externalController,  String? secret, @JsonKey(name: 'external-ui-name')  String? externalUiName, @JsonKey(name: 'external-ui-url')  String? externalUiUrl,  HostsMap hosts)?  $default,) {final _that = this;\nswitch (_that) {\ncase _ClashConfig() when $default != null:\nreturn $default(_that.mixedPort,_that.socksPort,_that.port,_that.redirPort,_that.tproxyPort,_that.mode,_that.allowLan,_that.logLevel,_that.ipv6,_that.findProcessMode,_that.keepAliveInterval,_that.unifiedDelay,_that.tcpConcurrent,_that.tun,_that.dns,_that.ntp,_that.sniffer,_that.tunnels,_that.experimental,_that.geoXUrl,_that.geodataLoader,_that.proxyGroups,_that.rule,_that.globalUa,_that.externalController,_that.secret,_that.externalUiName,_that.externalUiUrl,_that.hosts);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _ClashConfig implements ClashConfig {\n  const _ClashConfig({@JsonKey(name: 'mixed-port') this.mixedPort = defaultMixedPort, @JsonKey(name: 'socks-port') this.socksPort = 0, @JsonKey(name: 'port') this.port = 0, @JsonKey(name: 'redir-port') this.redirPort = 0, @JsonKey(name: 'tproxy-port') this.tproxyPort = 0, this.mode = Mode.rule, @JsonKey(name: 'allow-lan') this.allowLan = false, @JsonKey(name: 'log-level') this.logLevel = LogLevel.error, this.ipv6 = false, @JsonKey(name: 'find-process-mode', unknownEnumValue: FindProcessMode.always) this.findProcessMode = FindProcessMode.off, @JsonKey(name: 'keep-alive-interval') this.keepAliveInterval = defaultKeepAliveInterval, @JsonKey(name: 'unified-delay') this.unifiedDelay = true, @JsonKey(name: 'tcp-concurrent') this.tcpConcurrent = true, @JsonKey(fromJson: Tun.safeFormJson) this.tun = defaultTun, @JsonKey(fromJson: Dns.safeDnsFromJson) this.dns = defaultDns, @JsonKey(fromJson: Ntp.safeNtpFromJson) this.ntp = defaultNtp, @JsonKey(fromJson: Sniffer.safeSnifferFromJson) this.sniffer = defaultSniffer, final  List<TunnelEntry> tunnels = defaultTunnel, @JsonKey(fromJson: Experimental.safeExperimentalFromJson) this.experimental = defaultExperimental, @JsonKey(name: 'geox-url', fromJson: GeoXUrl.safeFormJson) this.geoXUrl = defaultGeoXUrl, @JsonKey(name: 'geodata-loader') this.geodataLoader = GeodataLoader.memconservative, @JsonKey(name: 'proxy-groups') final  List<ProxyGroup> proxyGroups = const [], final  List<String> rule = const [], @JsonKey(name: 'global-ua') this.globalUa, @JsonKey(name: 'external-controller') this.externalController = ExternalControllerStatus.close, this.secret, @JsonKey(name: 'external-ui-name') this.externalUiName, @JsonKey(name: 'external-ui-url') this.externalUiUrl, final  HostsMap hosts = const {}}): _tunnels = tunnels,_proxyGroups = proxyGroups,_rule = rule,_hosts = hosts;\n  factory _ClashConfig.fromJson(Map<String, dynamic> json) => _$ClashConfigFromJson(json);\n\n@override@JsonKey(name: 'mixed-port') final  int mixedPort;\n@override@JsonKey(name: 'socks-port') final  int socksPort;\n@override@JsonKey(name: 'port') final  int port;\n@override@JsonKey(name: 'redir-port') final  int redirPort;\n@override@JsonKey(name: 'tproxy-port') final  int tproxyPort;\n@override@JsonKey() final  Mode mode;\n@override@JsonKey(name: 'allow-lan') final  bool allowLan;\n@override@JsonKey(name: 'log-level') final  LogLevel logLevel;\n@override@JsonKey() final  bool ipv6;\n@override@JsonKey(name: 'find-process-mode', unknownEnumValue: FindProcessMode.always) final  FindProcessMode findProcessMode;\n@override@JsonKey(name: 'keep-alive-interval') final  int keepAliveInterval;\n@override@JsonKey(name: 'unified-delay') final  bool unifiedDelay;\n@override@JsonKey(name: 'tcp-concurrent') final  bool tcpConcurrent;\n@override@JsonKey(fromJson: Tun.safeFormJson) final  Tun tun;\n@override@JsonKey(fromJson: Dns.safeDnsFromJson) final  Dns dns;\n@override@JsonKey(fromJson: Ntp.safeNtpFromJson) final  Ntp ntp;\n@override@JsonKey(fromJson: Sniffer.safeSnifferFromJson) final  Sniffer sniffer;\n final  List<TunnelEntry> _tunnels;\n@override@JsonKey() List<TunnelEntry> get tunnels {\n  if (_tunnels is EqualUnmodifiableListView) return _tunnels;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_tunnels);\n}\n\n@override@JsonKey(fromJson: Experimental.safeExperimentalFromJson) final  Experimental experimental;\n@override@JsonKey(name: 'geox-url', fromJson: GeoXUrl.safeFormJson) final  GeoXUrl geoXUrl;\n@override@JsonKey(name: 'geodata-loader') final  GeodataLoader geodataLoader;\n final  List<ProxyGroup> _proxyGroups;\n@override@JsonKey(name: 'proxy-groups') List<ProxyGroup> get proxyGroups {\n  if (_proxyGroups is EqualUnmodifiableListView) return _proxyGroups;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_proxyGroups);\n}\n\n final  List<String> _rule;\n@override@JsonKey() List<String> get rule {\n  if (_rule is EqualUnmodifiableListView) return _rule;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_rule);\n}\n\n@override@JsonKey(name: 'global-ua') final  String? globalUa;\n@override@JsonKey(name: 'external-controller') final  ExternalControllerStatus externalController;\n@override final  String? secret;\n@override@JsonKey(name: 'external-ui-name') final  String? externalUiName;\n@override@JsonKey(name: 'external-ui-url') final  String? externalUiUrl;\n final  HostsMap _hosts;\n@override@JsonKey() HostsMap get hosts {\n  if (_hosts is EqualUnmodifiableMapView) return _hosts;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableMapView(_hosts);\n}\n\n\n/// Create a copy of ClashConfig\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ClashConfigCopyWith<_ClashConfig> get copyWith => __$ClashConfigCopyWithImpl<_ClashConfig>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$ClashConfigToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _ClashConfig&&(identical(other.mixedPort, mixedPort) || other.mixedPort == mixedPort)&&(identical(other.socksPort, socksPort) || other.socksPort == socksPort)&&(identical(other.port, port) || other.port == port)&&(identical(other.redirPort, redirPort) || other.redirPort == redirPort)&&(identical(other.tproxyPort, tproxyPort) || other.tproxyPort == tproxyPort)&&(identical(other.mode, mode) || other.mode == mode)&&(identical(other.allowLan, allowLan) || other.allowLan == allowLan)&&(identical(other.logLevel, logLevel) || other.logLevel == logLevel)&&(identical(other.ipv6, ipv6) || other.ipv6 == ipv6)&&(identical(other.findProcessMode, findProcessMode) || other.findProcessMode == findProcessMode)&&(identical(other.keepAliveInterval, keepAliveInterval) || other.keepAliveInterval == keepAliveInterval)&&(identical(other.unifiedDelay, unifiedDelay) || other.unifiedDelay == unifiedDelay)&&(identical(other.tcpConcurrent, tcpConcurrent) || other.tcpConcurrent == tcpConcurrent)&&(identical(other.tun, tun) || other.tun == tun)&&(identical(other.dns, dns) || other.dns == dns)&&(identical(other.ntp, ntp) || other.ntp == ntp)&&(identical(other.sniffer, sniffer) || other.sniffer == sniffer)&&const DeepCollectionEquality().equals(other._tunnels, _tunnels)&&(identical(other.experimental, experimental) || other.experimental == experimental)&&(identical(other.geoXUrl, geoXUrl) || other.geoXUrl == geoXUrl)&&(identical(other.geodataLoader, geodataLoader) || other.geodataLoader == geodataLoader)&&const DeepCollectionEquality().equals(other._proxyGroups, _proxyGroups)&&const DeepCollectionEquality().equals(other._rule, _rule)&&(identical(other.globalUa, globalUa) || other.globalUa == globalUa)&&(identical(other.externalController, externalController) || other.externalController == externalController)&&(identical(other.secret, secret) || other.secret == secret)&&(identical(other.externalUiName, externalUiName) || other.externalUiName == externalUiName)&&(identical(other.externalUiUrl, externalUiUrl) || other.externalUiUrl == externalUiUrl)&&const DeepCollectionEquality().equals(other._hosts, _hosts));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hashAll([runtimeType,mixedPort,socksPort,port,redirPort,tproxyPort,mode,allowLan,logLevel,ipv6,findProcessMode,keepAliveInterval,unifiedDelay,tcpConcurrent,tun,dns,ntp,sniffer,const DeepCollectionEquality().hash(_tunnels),experimental,geoXUrl,geodataLoader,const DeepCollectionEquality().hash(_proxyGroups),const DeepCollectionEquality().hash(_rule),globalUa,externalController,secret,externalUiName,externalUiUrl,const DeepCollectionEquality().hash(_hosts)]);\n\n@override\nString toString() {\n  return 'ClashConfig(mixedPort: $mixedPort, socksPort: $socksPort, port: $port, redirPort: $redirPort, tproxyPort: $tproxyPort, mode: $mode, allowLan: $allowLan, logLevel: $logLevel, ipv6: $ipv6, findProcessMode: $findProcessMode, keepAliveInterval: $keepAliveInterval, unifiedDelay: $unifiedDelay, tcpConcurrent: $tcpConcurrent, tun: $tun, dns: $dns, ntp: $ntp, sniffer: $sniffer, tunnels: $tunnels, experimental: $experimental, geoXUrl: $geoXUrl, geodataLoader: $geodataLoader, proxyGroups: $proxyGroups, rule: $rule, globalUa: $globalUa, externalController: $externalController, secret: $secret, externalUiName: $externalUiName, externalUiUrl: $externalUiUrl, hosts: $hosts)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ClashConfigCopyWith<$Res> implements $ClashConfigCopyWith<$Res> {\n  factory _$ClashConfigCopyWith(_ClashConfig value, $Res Function(_ClashConfig) _then) = __$ClashConfigCopyWithImpl;\n@override @useResult\n$Res call({\n@JsonKey(name: 'mixed-port') int mixedPort,@JsonKey(name: 'socks-port') int socksPort,@JsonKey(name: 'port') int port,@JsonKey(name: 'redir-port') int redirPort,@JsonKey(name: 'tproxy-port') int tproxyPort, Mode mode,@JsonKey(name: 'allow-lan') bool allowLan,@JsonKey(name: 'log-level') LogLevel logLevel, bool ipv6,@JsonKey(name: 'find-process-mode', unknownEnumValue: FindProcessMode.always) FindProcessMode findProcessMode,@JsonKey(name: 'keep-alive-interval') int keepAliveInterval,@JsonKey(name: 'unified-delay') bool unifiedDelay,@JsonKey(name: 'tcp-concurrent') bool tcpConcurrent,@JsonKey(fromJson: Tun.safeFormJson) Tun tun,@JsonKey(fromJson: Dns.safeDnsFromJson) Dns dns,@JsonKey(fromJson: Ntp.safeNtpFromJson) Ntp ntp,@JsonKey(fromJson: Sniffer.safeSnifferFromJson) Sniffer sniffer, List<TunnelEntry> tunnels,@JsonKey(fromJson: Experimental.safeExperimentalFromJson) Experimental experimental,@JsonKey(name: 'geox-url', fromJson: GeoXUrl.safeFormJson) GeoXUrl geoXUrl,@JsonKey(name: 'geodata-loader') GeodataLoader geodataLoader,@JsonKey(name: 'proxy-groups') List<ProxyGroup> proxyGroups, List<String> rule,@JsonKey(name: 'global-ua') String? globalUa,@JsonKey(name: 'external-controller') ExternalControllerStatus externalController, String? secret,@JsonKey(name: 'external-ui-name') String? externalUiName,@JsonKey(name: 'external-ui-url') String? externalUiUrl, HostsMap hosts\n});\n\n\n@override $TunCopyWith<$Res> get tun;@override $DnsCopyWith<$Res> get dns;@override $NtpCopyWith<$Res> get ntp;@override $SnifferCopyWith<$Res> get sniffer;@override $ExperimentalCopyWith<$Res> get experimental;@override $GeoXUrlCopyWith<$Res> get geoXUrl;\n\n}\n/// @nodoc\nclass __$ClashConfigCopyWithImpl<$Res>\n    implements _$ClashConfigCopyWith<$Res> {\n  __$ClashConfigCopyWithImpl(this._self, this._then);\n\n  final _ClashConfig _self;\n  final $Res Function(_ClashConfig) _then;\n\n/// Create a copy of ClashConfig\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? mixedPort = null,Object? socksPort = null,Object? port = null,Object? redirPort = null,Object? tproxyPort = null,Object? mode = null,Object? allowLan = null,Object? logLevel = null,Object? ipv6 = null,Object? findProcessMode = null,Object? keepAliveInterval = null,Object? unifiedDelay = null,Object? tcpConcurrent = null,Object? tun = null,Object? dns = null,Object? ntp = null,Object? sniffer = null,Object? tunnels = null,Object? experimental = null,Object? geoXUrl = null,Object? geodataLoader = null,Object? proxyGroups = null,Object? rule = null,Object? globalUa = freezed,Object? externalController = null,Object? secret = freezed,Object? externalUiName = freezed,Object? externalUiUrl = freezed,Object? hosts = null,}) {\n  return _then(_ClashConfig(\nmixedPort: null == mixedPort ? _self.mixedPort : mixedPort // ignore: cast_nullable_to_non_nullable\nas int,socksPort: null == socksPort ? _self.socksPort : socksPort // ignore: cast_nullable_to_non_nullable\nas int,port: null == port ? _self.port : port // ignore: cast_nullable_to_non_nullable\nas int,redirPort: null == redirPort ? _self.redirPort : redirPort // ignore: cast_nullable_to_non_nullable\nas int,tproxyPort: null == tproxyPort ? _self.tproxyPort : tproxyPort // ignore: cast_nullable_to_non_nullable\nas int,mode: null == mode ? _self.mode : mode // ignore: cast_nullable_to_non_nullable\nas Mode,allowLan: null == allowLan ? _self.allowLan : allowLan // ignore: cast_nullable_to_non_nullable\nas bool,logLevel: null == logLevel ? _self.logLevel : logLevel // ignore: cast_nullable_to_non_nullable\nas LogLevel,ipv6: null == ipv6 ? _self.ipv6 : ipv6 // ignore: cast_nullable_to_non_nullable\nas bool,findProcessMode: null == findProcessMode ? _self.findProcessMode : findProcessMode // ignore: cast_nullable_to_non_nullable\nas FindProcessMode,keepAliveInterval: null == keepAliveInterval ? _self.keepAliveInterval : keepAliveInterval // ignore: cast_nullable_to_non_nullable\nas int,unifiedDelay: null == unifiedDelay ? _self.unifiedDelay : unifiedDelay // ignore: cast_nullable_to_non_nullable\nas bool,tcpConcurrent: null == tcpConcurrent ? _self.tcpConcurrent : tcpConcurrent // ignore: cast_nullable_to_non_nullable\nas bool,tun: null == tun ? _self.tun : tun // ignore: cast_nullable_to_non_nullable\nas Tun,dns: null == dns ? _self.dns : dns // ignore: cast_nullable_to_non_nullable\nas Dns,ntp: null == ntp ? _self.ntp : ntp // ignore: cast_nullable_to_non_nullable\nas Ntp,sniffer: null == sniffer ? _self.sniffer : sniffer // ignore: cast_nullable_to_non_nullable\nas Sniffer,tunnels: null == tunnels ? _self._tunnels : tunnels // ignore: cast_nullable_to_non_nullable\nas List<TunnelEntry>,experimental: null == experimental ? _self.experimental : experimental // ignore: cast_nullable_to_non_nullable\nas Experimental,geoXUrl: null == geoXUrl ? _self.geoXUrl : geoXUrl // ignore: cast_nullable_to_non_nullable\nas GeoXUrl,geodataLoader: null == geodataLoader ? _self.geodataLoader : geodataLoader // ignore: cast_nullable_to_non_nullable\nas GeodataLoader,proxyGroups: null == proxyGroups ? _self._proxyGroups : proxyGroups // ignore: cast_nullable_to_non_nullable\nas List<ProxyGroup>,rule: null == rule ? _self._rule : rule // ignore: cast_nullable_to_non_nullable\nas List<String>,globalUa: freezed == globalUa ? _self.globalUa : globalUa // ignore: cast_nullable_to_non_nullable\nas String?,externalController: null == externalController ? _self.externalController : externalController // ignore: cast_nullable_to_non_nullable\nas ExternalControllerStatus,secret: freezed == secret ? _self.secret : secret // ignore: cast_nullable_to_non_nullable\nas String?,externalUiName: freezed == externalUiName ? _self.externalUiName : externalUiName // ignore: cast_nullable_to_non_nullable\nas String?,externalUiUrl: freezed == externalUiUrl ? _self.externalUiUrl : externalUiUrl // ignore: cast_nullable_to_non_nullable\nas String?,hosts: null == hosts ? _self._hosts : hosts // ignore: cast_nullable_to_non_nullable\nas HostsMap,\n  ));\n}\n\n/// Create a copy of ClashConfig\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$TunCopyWith<$Res> get tun {\n  \n  return $TunCopyWith<$Res>(_self.tun, (value) {\n    return _then(_self.copyWith(tun: value));\n  });\n}/// Create a copy of ClashConfig\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$DnsCopyWith<$Res> get dns {\n  \n  return $DnsCopyWith<$Res>(_self.dns, (value) {\n    return _then(_self.copyWith(dns: value));\n  });\n}/// Create a copy of ClashConfig\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$NtpCopyWith<$Res> get ntp {\n  \n  return $NtpCopyWith<$Res>(_self.ntp, (value) {\n    return _then(_self.copyWith(ntp: value));\n  });\n}/// Create a copy of ClashConfig\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$SnifferCopyWith<$Res> get sniffer {\n  \n  return $SnifferCopyWith<$Res>(_self.sniffer, (value) {\n    return _then(_self.copyWith(sniffer: value));\n  });\n}/// Create a copy of ClashConfig\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$ExperimentalCopyWith<$Res> get experimental {\n  \n  return $ExperimentalCopyWith<$Res>(_self.experimental, (value) {\n    return _then(_self.copyWith(experimental: value));\n  });\n}/// Create a copy of ClashConfig\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$GeoXUrlCopyWith<$Res> get geoXUrl {\n  \n  return $GeoXUrlCopyWith<$Res>(_self.geoXUrl, (value) {\n    return _then(_self.copyWith(geoXUrl: value));\n  });\n}\n}\n\n// dart format on\n"
  },
  {
    "path": "lib/models/generated/clash_config.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of '../clash_config.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_ProxyGroup _$ProxyGroupFromJson(Map<String, dynamic> json) => _ProxyGroup(\n  name: json['name'] as String,\n  type: GroupType.parseProfileType(json['type'] as String),\n  proxies: _parseStringList(json['proxies']),\n  use: _parseStringList(json['use']),\n  interval: _parseInt(json['interval']),\n  lazy: _parseBool(json['lazy']),\n  url: json['url'] as String?,\n  timeout: _parseInt(json['timeout']),\n  maxFailedTimes: _parseInt(json['max-failed-times']),\n  filter: json['filter'] as String?,\n  excludeFilter: json['expected-filter'] as String?,\n  excludeType: json['exclude-type'] as String?,\n  expectedStatus: json['expected-status'],\n  hidden: _parseBool(json['hidden']),\n  icon: json['icon'] as String?,\n  tolerance: _parseInt(json['tolerance']),\n);\n\nMap<String, dynamic> _$ProxyGroupToJson(_ProxyGroup instance) =>\n    <String, dynamic>{\n      'name': instance.name,\n      'type': _$GroupTypeEnumMap[instance.type]!,\n      'proxies': instance.proxies,\n      'use': instance.use,\n      'interval': instance.interval,\n      'lazy': instance.lazy,\n      'url': instance.url,\n      'timeout': instance.timeout,\n      'max-failed-times': instance.maxFailedTimes,\n      'filter': instance.filter,\n      'expected-filter': instance.excludeFilter,\n      'exclude-type': instance.excludeType,\n      'expected-status': instance.expectedStatus,\n      'hidden': instance.hidden,\n      'icon': instance.icon,\n      'tolerance': instance.tolerance,\n    };\n\nconst _$GroupTypeEnumMap = {\n  GroupType.Selector: 'Selector',\n  GroupType.URLTest: 'URLTest',\n  GroupType.Fallback: 'Fallback',\n  GroupType.LoadBalance: 'LoadBalance',\n};\n\n_RuleProvider _$RuleProviderFromJson(Map<String, dynamic> json) =>\n    _RuleProvider(name: json['name'] as String);\n\nMap<String, dynamic> _$RuleProviderToJson(_RuleProvider instance) =>\n    <String, dynamic>{'name': instance.name};\n\n_Sniffer _$SnifferFromJson(Map<String, dynamic> json) => _Sniffer(\n  enable: json['enable'] as bool? ?? true,\n  overrideDest: json['override-destination'] as bool? ?? false,\n  sniffing:\n      (json['sniffing'] as List<dynamic>?)?.map((e) => e as String).toList() ??\n      const [],\n  forceDomain:\n      (json['force-domain'] as List<dynamic>?)\n          ?.map((e) => e as String)\n          .toList() ??\n      const ['+.v2ex.com'],\n  skipSrcAddress:\n      (json['skip-src-address'] as List<dynamic>?)\n          ?.map((e) => e as String)\n          .toList() ??\n      const ['192.168.0.3/32'],\n  skipDstAddress:\n      (json['skip-dst-address'] as List<dynamic>?)\n          ?.map((e) => e as String)\n          .toList() ??\n      const [\n        '91.108.56.0/22',\n        '91.108.4.0/22',\n        '91.108.8.0/22',\n        '91.108.16.0/22',\n        '91.108.12.0/22',\n        '149.154.160.0/20',\n        '91.105.192.0/23',\n        '91.108.20.0/22',\n        '185.76.151.0/24',\n        '2001:b28:f23d::/48',\n        '2001:b28:f23f::/48',\n        '2001:67c:4e8::/48',\n        '2001:b28:f23c::/48',\n        '2a0a:f280::/32',\n      ],\n  skipDomain:\n      (json['skip-domain'] as List<dynamic>?)\n          ?.map((e) => e as String)\n          .toList() ??\n      const ['Mijia Cloud', '+.push.apple.com'],\n  port:\n      (json['port-whitelist'] as List<dynamic>?)\n          ?.map((e) => e as String)\n          .toList() ??\n      const [],\n  forceDnsMapping: json['force-dns-mapping'] as bool? ?? true,\n  parsePureIp: json['parse-pure-ip'] as bool? ?? true,\n  sniff:\n      (json['sniff'] as Map<String, dynamic>?)?.map(\n        (k, e) =>\n            MapEntry(k, SnifferConfig.fromJson(e as Map<String, dynamic>)),\n      ) ??\n      const {\n        'HTTP': SnifferConfig(ports: ['80', '8080-8880'], overrideDest: true),\n        'TLS': SnifferConfig(ports: ['443', '8443']),\n        'QUIC': SnifferConfig(ports: ['443', '8443']),\n      },\n);\n\nMap<String, dynamic> _$SnifferToJson(_Sniffer instance) => <String, dynamic>{\n  'enable': instance.enable,\n  'override-destination': instance.overrideDest,\n  'sniffing': instance.sniffing,\n  'force-domain': instance.forceDomain,\n  'skip-src-address': instance.skipSrcAddress,\n  'skip-dst-address': instance.skipDstAddress,\n  'skip-domain': instance.skipDomain,\n  'port-whitelist': instance.port,\n  'force-dns-mapping': instance.forceDnsMapping,\n  'parse-pure-ip': instance.parsePureIp,\n  'sniff': instance.sniff,\n};\n\n_TunnelEntry _$TunnelEntryFromJson(Map<String, dynamic> json) => _TunnelEntry(\n  id: json['id'] as String,\n  network: (json['network'] as List<dynamic>?)\n      ?.map((e) => e as String)\n      .toList(),\n  address: json['address'] as String?,\n  target: json['target'] as String?,\n  proxyName: json['proxyName'] as String?,\n);\n\nMap<String, dynamic> _$TunnelEntryToJson(_TunnelEntry instance) =>\n    <String, dynamic>{\n      'id': instance.id,\n      'network': instance.network,\n      'address': instance.address,\n      'target': instance.target,\n      'proxyName': instance.proxyName,\n    };\n\n_SnifferConfig _$SnifferConfigFromJson(Map<String, dynamic> json) =>\n    _SnifferConfig(\n      ports: json['ports'] == null\n          ? const []\n          : _formJsonPorts(json['ports'] as List?),\n      overrideDest: json['override-destination'] as bool?,\n    );\n\nMap<String, dynamic> _$SnifferConfigToJson(_SnifferConfig instance) =>\n    <String, dynamic>{\n      'ports': instance.ports,\n      'override-destination': instance.overrideDest,\n    };\n\n_Tun _$TunFromJson(Map<String, dynamic> json) => _Tun(\n  enable: json['enable'] as bool? ?? false,\n  device: json['device'] as String? ?? tunDeviceName,\n  autoRoute: json['auto-route'] as bool? ?? false,\n  stack:\n      $enumDecodeNullable(_$TunStackEnumMap, json['stack']) ?? TunStack.system,\n  dnsHijack:\n      (json['dns-hijack'] as List<dynamic>?)\n          ?.map((e) => e as String)\n          .toList() ??\n      const ['any:53'],\n  routeAddress:\n      (json['route-address'] as List<dynamic>?)\n          ?.map((e) => e as String)\n          .toList() ??\n      const [],\n  routeExcludeAddress:\n      (json['route-exclude-address'] as List<dynamic>?)\n          ?.map((e) => e as String)\n          .toList() ??\n      const [],\n  strictRoute: json['strict-route'] as bool? ?? false,\n  disableIcmpForwarding: json['disable-icmp-forwarding'] as bool? ?? true,\n  mtu: (json['mtu'] as num?)?.toInt() ?? 4064,\n  endpointIndependentNat: json['endpoint-independent-nat'] as bool? ?? false,\n);\n\nMap<String, dynamic> _$TunToJson(_Tun instance) => <String, dynamic>{\n  'enable': instance.enable,\n  'device': instance.device,\n  'auto-route': instance.autoRoute,\n  'stack': _$TunStackEnumMap[instance.stack]!,\n  'dns-hijack': instance.dnsHijack,\n  'route-address': instance.routeAddress,\n  'route-exclude-address': instance.routeExcludeAddress,\n  'strict-route': instance.strictRoute,\n  'disable-icmp-forwarding': instance.disableIcmpForwarding,\n  'mtu': instance.mtu,\n  'endpoint-independent-nat': instance.endpointIndependentNat,\n};\n\nconst _$TunStackEnumMap = {\n  TunStack.gvisor: 'gvisor',\n  TunStack.system: 'system',\n  TunStack.mixed: 'mixed',\n};\n\n_FallbackFilter _$FallbackFilterFromJson(\n  Map<String, dynamic> json,\n) => _FallbackFilter(\n  geoip: json['geoip'] as bool? ?? false,\n  geoipCode: json['geoip-code'] as String? ?? 'CN',\n  geosite:\n      (json['geosite'] as List<dynamic>?)?.map((e) => e as String).toList() ??\n      const [],\n  ipcidr:\n      (json['ipcidr'] as List<dynamic>?)?.map((e) => e as String).toList() ??\n      const [],\n  domain:\n      (json['domain'] as List<dynamic>?)?.map((e) => e as String).toList() ??\n      const [],\n);\n\nMap<String, dynamic> _$FallbackFilterToJson(_FallbackFilter instance) =>\n    <String, dynamic>{\n      'geoip': instance.geoip,\n      'geoip-code': instance.geoipCode,\n      'geosite': instance.geosite,\n      'ipcidr': instance.ipcidr,\n      'domain': instance.domain,\n    };\n\n_Dns _$DnsFromJson(Map<String, dynamic> json) => _Dns(\n  enable: json['enable'] as bool? ?? true,\n  listen: json['listen'] as String? ?? '0.0.0.0:1053',\n  preferH3: json['prefer-h3'] as bool? ?? false,\n  cacheAlgorithm:\n      $enumDecodeNullable(_$CacheAlgorithmEnumMap, json['cache-algorithm']) ??\n      CacheAlgorithm.arc,\n  useHosts: json['use-hosts'] as bool? ?? true,\n  useSystemHosts: json['use-system-hosts'] as bool? ?? true,\n  respectRules: json['respect-rules'] as bool? ?? false,\n  ipv6: json['ipv6'] as bool? ?? false,\n  defaultNameserver:\n      (json['default-nameserver'] as List<dynamic>?)\n          ?.map((e) => e as String)\n          .toList() ??\n      const ['114.114.114.114'],\n  enhancedMode:\n      $enumDecodeNullable(_$DnsModeEnumMap, json['enhanced-mode']) ??\n      DnsMode.fakeIp,\n  fakeIpRange: json['fake-ip-range'] as String? ?? '198.18.0.1/15',\n  fakeIpRangeV6: json['fake-ip-range-v6'] as String? ?? 'fc00::/18',\n  fakeIpFilterMode:\n      $enumDecodeNullable(_$FilterModeEnumMap, json['fake-ip-filter-mode']) ??\n      FilterMode.blacklist,\n  fakeIpFilter:\n      (json['fake-ip-filter'] as List<dynamic>?)\n          ?.map((e) => e as String)\n          .toList() ??\n      const [\n        '*',\n        'geosite:private',\n        'geosite:category-ntp',\n        'geosite:geolocation-cn',\n        'geosite:connectivity-check',\n      ],\n  fakeIpTtl: (json['fake-ip-ttl'] as num?)?.toInt() ?? 1,\n  nameserverPolicy:\n      (json['nameserver-policy'] as Map<String, dynamic>?)?.map(\n        (k, e) => MapEntry(k, e as String),\n      ) ??\n      const {\n        '+.internal.crop.com': '10.0.0.1',\n        'geosite:cn': '119.29.29.29',\n        'geosite:private': 'system',\n        '*': 'system',\n      },\n  nameserver:\n      (json['nameserver'] as List<dynamic>?)\n          ?.map((e) => e as String)\n          .toList() ??\n      const ['1.1.1.1'],\n  fallback:\n      (json['fallback'] as List<dynamic>?)?.map((e) => e as String).toList() ??\n      const [],\n  proxyServerNameserver:\n      (json['proxy-server-nameserver'] as List<dynamic>?)\n          ?.map((e) => e as String)\n          .toList() ??\n      const ['https://doh.pub/dns-query#DIRECT'],\n  directNameserver:\n      (json['direct-nameserver'] as List<dynamic>?)\n          ?.map((e) => e as String)\n          .toList() ??\n      const [],\n  directNameserverFollowPolicy:\n      json['direct-nameserver-follow-policy'] as bool? ?? false,\n  fallbackFilter: json['fallback-filter'] == null\n      ? const FallbackFilter()\n      : FallbackFilter.fromJson(\n          json['fallback-filter'] as Map<String, dynamic>,\n        ),\n);\n\nMap<String, dynamic> _$DnsToJson(_Dns instance) => <String, dynamic>{\n  'enable': instance.enable,\n  'listen': instance.listen,\n  'prefer-h3': instance.preferH3,\n  'cache-algorithm': _$CacheAlgorithmEnumMap[instance.cacheAlgorithm]!,\n  'use-hosts': instance.useHosts,\n  'use-system-hosts': instance.useSystemHosts,\n  'respect-rules': instance.respectRules,\n  'ipv6': instance.ipv6,\n  'default-nameserver': instance.defaultNameserver,\n  'enhanced-mode': _$DnsModeEnumMap[instance.enhancedMode]!,\n  'fake-ip-range': instance.fakeIpRange,\n  'fake-ip-range-v6': instance.fakeIpRangeV6,\n  'fake-ip-filter-mode': _$FilterModeEnumMap[instance.fakeIpFilterMode]!,\n  'fake-ip-filter': instance.fakeIpFilter,\n  'fake-ip-ttl': instance.fakeIpTtl,\n  'nameserver-policy': instance.nameserverPolicy,\n  'nameserver': instance.nameserver,\n  'fallback': instance.fallback,\n  'proxy-server-nameserver': instance.proxyServerNameserver,\n  'direct-nameserver': instance.directNameserver,\n  'direct-nameserver-follow-policy': instance.directNameserverFollowPolicy,\n  'fallback-filter': instance.fallbackFilter,\n};\n\nconst _$CacheAlgorithmEnumMap = {\n  CacheAlgorithm.arc: 'arc',\n  CacheAlgorithm.lru: 'lru',\n};\n\nconst _$DnsModeEnumMap = {\n  DnsMode.normal: 'normal',\n  DnsMode.fakeIp: 'fake-ip',\n  DnsMode.redirHost: 'redir-host',\n  DnsMode.hosts: 'hosts',\n};\n\nconst _$FilterModeEnumMap = {\n  FilterMode.blacklist: 'blacklist',\n  FilterMode.whitelist: 'whitelist',\n  FilterMode.rule: 'rule',\n};\n\n_Ntp _$NtpFromJson(Map<String, dynamic> json) => _Ntp(\n  enable: json['enable'] as bool? ?? true,\n  writeToSystem: json['write-to-system'] as bool? ?? false,\n  server: json['server'] as String? ?? 'ntp.aliyun.com',\n  port: (json['port'] as num?)?.toInt() ?? 123,\n  interval: (json['interval'] as num?)?.toInt() ?? 60,\n);\n\nMap<String, dynamic> _$NtpToJson(_Ntp instance) => <String, dynamic>{\n  'enable': instance.enable,\n  'write-to-system': instance.writeToSystem,\n  'server': instance.server,\n  'port': instance.port,\n  'interval': instance.interval,\n};\n\n_Experimental _$ExperimentalFromJson(Map<String, dynamic> json) =>\n    _Experimental(\n      quicGoDisableGso: json['quic-go-disable-gso'] as bool? ?? true,\n      quicGoDisableEcn: json['quic-go-disable-ecn'] as bool? ?? true,\n      dialerIp4pConvert: json['dialer-ip4p-convert'] as bool? ?? false,\n    );\n\nMap<String, dynamic> _$ExperimentalToJson(_Experimental instance) =>\n    <String, dynamic>{\n      'quic-go-disable-gso': instance.quicGoDisableGso,\n      'quic-go-disable-ecn': instance.quicGoDisableEcn,\n      'dialer-ip4p-convert': instance.dialerIp4pConvert,\n    };\n\n_GeoXUrl _$GeoXUrlFromJson(Map<String, dynamic> json) => _GeoXUrl(\n  mmdb:\n      json['mmdb'] as String? ??\n      'https://fastly.jsdelivr.net/gh/appshubcc/bett-rules@release/geoip.metadb',\n  asn:\n      json['asn'] as String? ??\n      'https://fastly.jsdelivr.net/gh/appshubcc/bett-rules@release/GeoLite2-ASN.mmdb',\n  geoip:\n      json['geoip'] as String? ??\n      'https://fastly.jsdelivr.net/gh/appshubcc/bett-rules@release/geoip.dat',\n  geosite:\n      json['geosite'] as String? ??\n      'https://fastly.jsdelivr.net/gh/appshubcc/bett-rules@release/geosite.dat',\n);\n\nMap<String, dynamic> _$GeoXUrlToJson(_GeoXUrl instance) => <String, dynamic>{\n  'mmdb': instance.mmdb,\n  'asn': instance.asn,\n  'geoip': instance.geoip,\n  'geosite': instance.geosite,\n};\n\n_Rule _$RuleFromJson(Map<String, dynamic> json) =>\n    _Rule(id: json['id'] as String, value: json['value'] as String);\n\nMap<String, dynamic> _$RuleToJson(_Rule instance) => <String, dynamic>{\n  'id': instance.id,\n  'value': instance.value,\n};\n\n_SubRule _$SubRuleFromJson(Map<String, dynamic> json) =>\n    _SubRule(name: json['name'] as String);\n\nMap<String, dynamic> _$SubRuleToJson(_SubRule instance) => <String, dynamic>{\n  'name': instance.name,\n};\n\n_ClashConfigSnippet _$ClashConfigSnippetFromJson(Map<String, dynamic> json) =>\n    _ClashConfigSnippet(\n      proxyGroups:\n          (json['proxy-groups'] as List<dynamic>?)\n              ?.map((e) => ProxyGroup.fromJson(e as Map<String, dynamic>))\n              .toList() ??\n          const [],\n      rule: json['rules'] == null ? const [] : _genRule(json['rules'] as List?),\n      ruleProvider: json['rule-providers'] == null\n          ? const []\n          : _genRuleProviders(json['rule-providers'] as Map<String, dynamic>),\n      subRules: json['sub-rules'] == null\n          ? const []\n          : _genSubRules(json['sub-rules'] as Map<String, dynamic>),\n    );\n\nMap<String, dynamic> _$ClashConfigSnippetToJson(_ClashConfigSnippet instance) =>\n    <String, dynamic>{\n      'proxy-groups': instance.proxyGroups,\n      'rules': instance.rule,\n      'rule-providers': instance.ruleProvider,\n      'sub-rules': instance.subRules,\n    };\n\n_ClashConfig _$ClashConfigFromJson(Map<String, dynamic> json) => _ClashConfig(\n  mixedPort: (json['mixed-port'] as num?)?.toInt() ?? defaultMixedPort,\n  socksPort: (json['socks-port'] as num?)?.toInt() ?? 0,\n  port: (json['port'] as num?)?.toInt() ?? 0,\n  redirPort: (json['redir-port'] as num?)?.toInt() ?? 0,\n  tproxyPort: (json['tproxy-port'] as num?)?.toInt() ?? 0,\n  mode: $enumDecodeNullable(_$ModeEnumMap, json['mode']) ?? Mode.rule,\n  allowLan: json['allow-lan'] as bool? ?? false,\n  logLevel:\n      $enumDecodeNullable(_$LogLevelEnumMap, json['log-level']) ??\n      LogLevel.error,\n  ipv6: json['ipv6'] as bool? ?? false,\n  findProcessMode:\n      $enumDecodeNullable(\n        _$FindProcessModeEnumMap,\n        json['find-process-mode'],\n        unknownValue: FindProcessMode.always,\n      ) ??\n      FindProcessMode.off,\n  keepAliveInterval:\n      (json['keep-alive-interval'] as num?)?.toInt() ??\n      defaultKeepAliveInterval,\n  unifiedDelay: json['unified-delay'] as bool? ?? true,\n  tcpConcurrent: json['tcp-concurrent'] as bool? ?? true,\n  tun: json['tun'] == null\n      ? defaultTun\n      : Tun.safeFormJson(json['tun'] as Map<String, Object?>?),\n  dns: json['dns'] == null\n      ? defaultDns\n      : Dns.safeDnsFromJson(json['dns'] as Map<String, Object?>),\n  ntp: json['ntp'] == null\n      ? defaultNtp\n      : Ntp.safeNtpFromJson(json['ntp'] as Map<String, Object?>),\n  sniffer: json['sniffer'] == null\n      ? defaultSniffer\n      : Sniffer.safeSnifferFromJson(json['sniffer'] as Map<String, Object?>),\n  tunnels:\n      (json['tunnels'] as List<dynamic>?)\n          ?.map((e) => TunnelEntry.fromJson(e as Map<String, dynamic>))\n          .toList() ??\n      defaultTunnel,\n  experimental: json['experimental'] == null\n      ? defaultExperimental\n      : Experimental.safeExperimentalFromJson(\n          json['experimental'] as Map<String, Object?>,\n        ),\n  geoXUrl: json['geox-url'] == null\n      ? defaultGeoXUrl\n      : GeoXUrl.safeFormJson(json['geox-url'] as Map<String, Object?>?),\n  geodataLoader:\n      $enumDecodeNullable(_$GeodataLoaderEnumMap, json['geodata-loader']) ??\n      GeodataLoader.memconservative,\n  proxyGroups:\n      (json['proxy-groups'] as List<dynamic>?)\n          ?.map((e) => ProxyGroup.fromJson(e as Map<String, dynamic>))\n          .toList() ??\n      const [],\n  rule:\n      (json['rule'] as List<dynamic>?)?.map((e) => e as String).toList() ??\n      const [],\n  globalUa: json['global-ua'] as String?,\n  externalController:\n      $enumDecodeNullable(\n        _$ExternalControllerStatusEnumMap,\n        json['external-controller'],\n      ) ??\n      ExternalControllerStatus.close,\n  secret: json['secret'] as String?,\n  externalUiName: json['external-ui-name'] as String?,\n  externalUiUrl: json['external-ui-url'] as String?,\n  hosts:\n      (json['hosts'] as Map<String, dynamic>?)?.map(\n        (k, e) => MapEntry(k, e as String),\n      ) ??\n      const {},\n);\n\nMap<String, dynamic> _$ClashConfigToJson(_ClashConfig instance) =>\n    <String, dynamic>{\n      'mixed-port': instance.mixedPort,\n      'socks-port': instance.socksPort,\n      'port': instance.port,\n      'redir-port': instance.redirPort,\n      'tproxy-port': instance.tproxyPort,\n      'mode': _$ModeEnumMap[instance.mode]!,\n      'allow-lan': instance.allowLan,\n      'log-level': _$LogLevelEnumMap[instance.logLevel]!,\n      'ipv6': instance.ipv6,\n      'find-process-mode': _$FindProcessModeEnumMap[instance.findProcessMode]!,\n      'keep-alive-interval': instance.keepAliveInterval,\n      'unified-delay': instance.unifiedDelay,\n      'tcp-concurrent': instance.tcpConcurrent,\n      'tun': instance.tun,\n      'dns': instance.dns,\n      'ntp': instance.ntp,\n      'sniffer': instance.sniffer,\n      'tunnels': instance.tunnels,\n      'experimental': instance.experimental,\n      'geox-url': instance.geoXUrl,\n      'geodata-loader': _$GeodataLoaderEnumMap[instance.geodataLoader]!,\n      'proxy-groups': instance.proxyGroups,\n      'rule': instance.rule,\n      'global-ua': instance.globalUa,\n      'external-controller':\n          _$ExternalControllerStatusEnumMap[instance.externalController]!,\n      'secret': instance.secret,\n      'external-ui-name': instance.externalUiName,\n      'external-ui-url': instance.externalUiUrl,\n      'hosts': instance.hosts,\n    };\n\nconst _$ModeEnumMap = {\n  Mode.rule: 'rule',\n  Mode.global: 'global',\n  Mode.direct: 'direct',\n};\n\nconst _$LogLevelEnumMap = {\n  LogLevel.debug: 'debug',\n  LogLevel.info: 'info',\n  LogLevel.warning: 'warning',\n  LogLevel.error: 'error',\n  LogLevel.silent: 'silent',\n};\n\nconst _$FindProcessModeEnumMap = {\n  FindProcessMode.always: 'always',\n  FindProcessMode.off: 'off',\n};\n\nconst _$GeodataLoaderEnumMap = {\n  GeodataLoader.standard: 'standard',\n  GeodataLoader.memconservative: 'memconservative',\n};\n\nconst _$ExternalControllerStatusEnumMap = {\n  ExternalControllerStatus.close: '',\n  ExternalControllerStatus.open: '127.0.0.1:9090',\n};\n"
  },
  {
    "path": "lib/models/generated/common.freezed.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n// coverage:ignore-file\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of '../common.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\n// dart format off\nT _$identity<T>(T value) => value;\n/// @nodoc\nmixin _$NavigationItem {\n\n Icon get icon; PageLabel get label; String? get description; WidgetBuilder get builder; bool get keep; String? get path; List<NavigationItemMode> get modes;\n/// Create a copy of NavigationItem\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$NavigationItemCopyWith<NavigationItem> get copyWith => _$NavigationItemCopyWithImpl<NavigationItem>(this as NavigationItem, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is NavigationItem&&(identical(other.icon, icon) || other.icon == icon)&&(identical(other.label, label) || other.label == label)&&(identical(other.description, description) || other.description == description)&&(identical(other.builder, builder) || other.builder == builder)&&(identical(other.keep, keep) || other.keep == keep)&&(identical(other.path, path) || other.path == path)&&const DeepCollectionEquality().equals(other.modes, modes));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,icon,label,description,builder,keep,path,const DeepCollectionEquality().hash(modes));\n\n@override\nString toString() {\n  return 'NavigationItem(icon: $icon, label: $label, description: $description, builder: $builder, keep: $keep, path: $path, modes: $modes)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $NavigationItemCopyWith<$Res>  {\n  factory $NavigationItemCopyWith(NavigationItem value, $Res Function(NavigationItem) _then) = _$NavigationItemCopyWithImpl;\n@useResult\n$Res call({\n Icon icon, PageLabel label, String? description, WidgetBuilder builder, bool keep, String? path, List<NavigationItemMode> modes\n});\n\n\n\n\n}\n/// @nodoc\nclass _$NavigationItemCopyWithImpl<$Res>\n    implements $NavigationItemCopyWith<$Res> {\n  _$NavigationItemCopyWithImpl(this._self, this._then);\n\n  final NavigationItem _self;\n  final $Res Function(NavigationItem) _then;\n\n/// Create a copy of NavigationItem\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? icon = null,Object? label = null,Object? description = freezed,Object? builder = null,Object? keep = null,Object? path = freezed,Object? modes = null,}) {\n  return _then(_self.copyWith(\nicon: null == icon ? _self.icon : icon // ignore: cast_nullable_to_non_nullable\nas Icon,label: null == label ? _self.label : label // ignore: cast_nullable_to_non_nullable\nas PageLabel,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable\nas String?,builder: null == builder ? _self.builder : builder // ignore: cast_nullable_to_non_nullable\nas WidgetBuilder,keep: null == keep ? _self.keep : keep // ignore: cast_nullable_to_non_nullable\nas bool,path: freezed == path ? _self.path : path // ignore: cast_nullable_to_non_nullable\nas String?,modes: null == modes ? _self.modes : modes // ignore: cast_nullable_to_non_nullable\nas List<NavigationItemMode>,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [NavigationItem].\nextension NavigationItemPatterns on NavigationItem {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _NavigationItem value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _NavigationItem() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _NavigationItem value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _NavigationItem():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _NavigationItem value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _NavigationItem() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( Icon icon,  PageLabel label,  String? description,  WidgetBuilder builder,  bool keep,  String? path,  List<NavigationItemMode> modes)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _NavigationItem() when $default != null:\nreturn $default(_that.icon,_that.label,_that.description,_that.builder,_that.keep,_that.path,_that.modes);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( Icon icon,  PageLabel label,  String? description,  WidgetBuilder builder,  bool keep,  String? path,  List<NavigationItemMode> modes)  $default,) {final _that = this;\nswitch (_that) {\ncase _NavigationItem():\nreturn $default(_that.icon,_that.label,_that.description,_that.builder,_that.keep,_that.path,_that.modes);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( Icon icon,  PageLabel label,  String? description,  WidgetBuilder builder,  bool keep,  String? path,  List<NavigationItemMode> modes)?  $default,) {final _that = this;\nswitch (_that) {\ncase _NavigationItem() when $default != null:\nreturn $default(_that.icon,_that.label,_that.description,_that.builder,_that.keep,_that.path,_that.modes);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _NavigationItem implements NavigationItem {\n  const _NavigationItem({required this.icon, required this.label, this.description, required this.builder, this.keep = true, this.path, final  List<NavigationItemMode> modes = const [NavigationItemMode.mobile, NavigationItemMode.desktop]}): _modes = modes;\n  \n\n@override final  Icon icon;\n@override final  PageLabel label;\n@override final  String? description;\n@override final  WidgetBuilder builder;\n@override@JsonKey() final  bool keep;\n@override final  String? path;\n final  List<NavigationItemMode> _modes;\n@override@JsonKey() List<NavigationItemMode> get modes {\n  if (_modes is EqualUnmodifiableListView) return _modes;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_modes);\n}\n\n\n/// Create a copy of NavigationItem\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$NavigationItemCopyWith<_NavigationItem> get copyWith => __$NavigationItemCopyWithImpl<_NavigationItem>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _NavigationItem&&(identical(other.icon, icon) || other.icon == icon)&&(identical(other.label, label) || other.label == label)&&(identical(other.description, description) || other.description == description)&&(identical(other.builder, builder) || other.builder == builder)&&(identical(other.keep, keep) || other.keep == keep)&&(identical(other.path, path) || other.path == path)&&const DeepCollectionEquality().equals(other._modes, _modes));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,icon,label,description,builder,keep,path,const DeepCollectionEquality().hash(_modes));\n\n@override\nString toString() {\n  return 'NavigationItem(icon: $icon, label: $label, description: $description, builder: $builder, keep: $keep, path: $path, modes: $modes)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$NavigationItemCopyWith<$Res> implements $NavigationItemCopyWith<$Res> {\n  factory _$NavigationItemCopyWith(_NavigationItem value, $Res Function(_NavigationItem) _then) = __$NavigationItemCopyWithImpl;\n@override @useResult\n$Res call({\n Icon icon, PageLabel label, String? description, WidgetBuilder builder, bool keep, String? path, List<NavigationItemMode> modes\n});\n\n\n\n\n}\n/// @nodoc\nclass __$NavigationItemCopyWithImpl<$Res>\n    implements _$NavigationItemCopyWith<$Res> {\n  __$NavigationItemCopyWithImpl(this._self, this._then);\n\n  final _NavigationItem _self;\n  final $Res Function(_NavigationItem) _then;\n\n/// Create a copy of NavigationItem\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? icon = null,Object? label = null,Object? description = freezed,Object? builder = null,Object? keep = null,Object? path = freezed,Object? modes = null,}) {\n  return _then(_NavigationItem(\nicon: null == icon ? _self.icon : icon // ignore: cast_nullable_to_non_nullable\nas Icon,label: null == label ? _self.label : label // ignore: cast_nullable_to_non_nullable\nas PageLabel,description: freezed == description ? _self.description : description // ignore: cast_nullable_to_non_nullable\nas String?,builder: null == builder ? _self.builder : builder // ignore: cast_nullable_to_non_nullable\nas WidgetBuilder,keep: null == keep ? _self.keep : keep // ignore: cast_nullable_to_non_nullable\nas bool,path: freezed == path ? _self.path : path // ignore: cast_nullable_to_non_nullable\nas String?,modes: null == modes ? _self._modes : modes // ignore: cast_nullable_to_non_nullable\nas List<NavigationItemMode>,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$Package {\n\n String get packageName; String get label; bool get system; bool get internet; int get lastUpdateTime;\n/// Create a copy of Package\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$PackageCopyWith<Package> get copyWith => _$PackageCopyWithImpl<Package>(this as Package, _$identity);\n\n  /// Serializes this Package to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is Package&&(identical(other.packageName, packageName) || other.packageName == packageName)&&(identical(other.label, label) || other.label == label)&&(identical(other.system, system) || other.system == system)&&(identical(other.internet, internet) || other.internet == internet)&&(identical(other.lastUpdateTime, lastUpdateTime) || other.lastUpdateTime == lastUpdateTime));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,packageName,label,system,internet,lastUpdateTime);\n\n@override\nString toString() {\n  return 'Package(packageName: $packageName, label: $label, system: $system, internet: $internet, lastUpdateTime: $lastUpdateTime)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $PackageCopyWith<$Res>  {\n  factory $PackageCopyWith(Package value, $Res Function(Package) _then) = _$PackageCopyWithImpl;\n@useResult\n$Res call({\n String packageName, String label, bool system, bool internet, int lastUpdateTime\n});\n\n\n\n\n}\n/// @nodoc\nclass _$PackageCopyWithImpl<$Res>\n    implements $PackageCopyWith<$Res> {\n  _$PackageCopyWithImpl(this._self, this._then);\n\n  final Package _self;\n  final $Res Function(Package) _then;\n\n/// Create a copy of Package\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? packageName = null,Object? label = null,Object? system = null,Object? internet = null,Object? lastUpdateTime = null,}) {\n  return _then(_self.copyWith(\npackageName: null == packageName ? _self.packageName : packageName // ignore: cast_nullable_to_non_nullable\nas String,label: null == label ? _self.label : label // ignore: cast_nullable_to_non_nullable\nas String,system: null == system ? _self.system : system // ignore: cast_nullable_to_non_nullable\nas bool,internet: null == internet ? _self.internet : internet // ignore: cast_nullable_to_non_nullable\nas bool,lastUpdateTime: null == lastUpdateTime ? _self.lastUpdateTime : lastUpdateTime // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [Package].\nextension PackagePatterns on Package {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _Package value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _Package() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _Package value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Package():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _Package value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Package() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String packageName,  String label,  bool system,  bool internet,  int lastUpdateTime)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _Package() when $default != null:\nreturn $default(_that.packageName,_that.label,_that.system,_that.internet,_that.lastUpdateTime);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String packageName,  String label,  bool system,  bool internet,  int lastUpdateTime)  $default,) {final _that = this;\nswitch (_that) {\ncase _Package():\nreturn $default(_that.packageName,_that.label,_that.system,_that.internet,_that.lastUpdateTime);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String packageName,  String label,  bool system,  bool internet,  int lastUpdateTime)?  $default,) {final _that = this;\nswitch (_that) {\ncase _Package() when $default != null:\nreturn $default(_that.packageName,_that.label,_that.system,_that.internet,_that.lastUpdateTime);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _Package implements Package {\n  const _Package({required this.packageName, required this.label, required this.system, required this.internet, required this.lastUpdateTime});\n  factory _Package.fromJson(Map<String, dynamic> json) => _$PackageFromJson(json);\n\n@override final  String packageName;\n@override final  String label;\n@override final  bool system;\n@override final  bool internet;\n@override final  int lastUpdateTime;\n\n/// Create a copy of Package\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$PackageCopyWith<_Package> get copyWith => __$PackageCopyWithImpl<_Package>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$PackageToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _Package&&(identical(other.packageName, packageName) || other.packageName == packageName)&&(identical(other.label, label) || other.label == label)&&(identical(other.system, system) || other.system == system)&&(identical(other.internet, internet) || other.internet == internet)&&(identical(other.lastUpdateTime, lastUpdateTime) || other.lastUpdateTime == lastUpdateTime));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,packageName,label,system,internet,lastUpdateTime);\n\n@override\nString toString() {\n  return 'Package(packageName: $packageName, label: $label, system: $system, internet: $internet, lastUpdateTime: $lastUpdateTime)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$PackageCopyWith<$Res> implements $PackageCopyWith<$Res> {\n  factory _$PackageCopyWith(_Package value, $Res Function(_Package) _then) = __$PackageCopyWithImpl;\n@override @useResult\n$Res call({\n String packageName, String label, bool system, bool internet, int lastUpdateTime\n});\n\n\n\n\n}\n/// @nodoc\nclass __$PackageCopyWithImpl<$Res>\n    implements _$PackageCopyWith<$Res> {\n  __$PackageCopyWithImpl(this._self, this._then);\n\n  final _Package _self;\n  final $Res Function(_Package) _then;\n\n/// Create a copy of Package\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? packageName = null,Object? label = null,Object? system = null,Object? internet = null,Object? lastUpdateTime = null,}) {\n  return _then(_Package(\npackageName: null == packageName ? _self.packageName : packageName // ignore: cast_nullable_to_non_nullable\nas String,label: null == label ? _self.label : label // ignore: cast_nullable_to_non_nullable\nas String,system: null == system ? _self.system : system // ignore: cast_nullable_to_non_nullable\nas bool,internet: null == internet ? _self.internet : internet // ignore: cast_nullable_to_non_nullable\nas bool,lastUpdateTime: null == lastUpdateTime ? _self.lastUpdateTime : lastUpdateTime // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$Metadata {\n\n int get uid; String get network; String get sourceIP; String get sourcePort; String get destinationIP; String get destinationPort; String get host; DnsMode? get dnsMode; String get process; String get processPath; String get remoteDestination; List<String> get sourceGeoIP; List<String> get destinationGeoIP; String get destinationIPASN; String get sourceIPASN; String get specialRules; String get specialProxy;\n/// Create a copy of Metadata\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$MetadataCopyWith<Metadata> get copyWith => _$MetadataCopyWithImpl<Metadata>(this as Metadata, _$identity);\n\n  /// Serializes this Metadata to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is Metadata&&(identical(other.uid, uid) || other.uid == uid)&&(identical(other.network, network) || other.network == network)&&(identical(other.sourceIP, sourceIP) || other.sourceIP == sourceIP)&&(identical(other.sourcePort, sourcePort) || other.sourcePort == sourcePort)&&(identical(other.destinationIP, destinationIP) || other.destinationIP == destinationIP)&&(identical(other.destinationPort, destinationPort) || other.destinationPort == destinationPort)&&(identical(other.host, host) || other.host == host)&&(identical(other.dnsMode, dnsMode) || other.dnsMode == dnsMode)&&(identical(other.process, process) || other.process == process)&&(identical(other.processPath, processPath) || other.processPath == processPath)&&(identical(other.remoteDestination, remoteDestination) || other.remoteDestination == remoteDestination)&&const DeepCollectionEquality().equals(other.sourceGeoIP, sourceGeoIP)&&const DeepCollectionEquality().equals(other.destinationGeoIP, destinationGeoIP)&&(identical(other.destinationIPASN, destinationIPASN) || other.destinationIPASN == destinationIPASN)&&(identical(other.sourceIPASN, sourceIPASN) || other.sourceIPASN == sourceIPASN)&&(identical(other.specialRules, specialRules) || other.specialRules == specialRules)&&(identical(other.specialProxy, specialProxy) || other.specialProxy == specialProxy));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,uid,network,sourceIP,sourcePort,destinationIP,destinationPort,host,dnsMode,process,processPath,remoteDestination,const DeepCollectionEquality().hash(sourceGeoIP),const DeepCollectionEquality().hash(destinationGeoIP),destinationIPASN,sourceIPASN,specialRules,specialProxy);\n\n@override\nString toString() {\n  return 'Metadata(uid: $uid, network: $network, sourceIP: $sourceIP, sourcePort: $sourcePort, destinationIP: $destinationIP, destinationPort: $destinationPort, host: $host, dnsMode: $dnsMode, process: $process, processPath: $processPath, remoteDestination: $remoteDestination, sourceGeoIP: $sourceGeoIP, destinationGeoIP: $destinationGeoIP, destinationIPASN: $destinationIPASN, sourceIPASN: $sourceIPASN, specialRules: $specialRules, specialProxy: $specialProxy)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $MetadataCopyWith<$Res>  {\n  factory $MetadataCopyWith(Metadata value, $Res Function(Metadata) _then) = _$MetadataCopyWithImpl;\n@useResult\n$Res call({\n int uid, String network, String sourceIP, String sourcePort, String destinationIP, String destinationPort, String host, DnsMode? dnsMode, String process, String processPath, String remoteDestination, List<String> sourceGeoIP, List<String> destinationGeoIP, String destinationIPASN, String sourceIPASN, String specialRules, String specialProxy\n});\n\n\n\n\n}\n/// @nodoc\nclass _$MetadataCopyWithImpl<$Res>\n    implements $MetadataCopyWith<$Res> {\n  _$MetadataCopyWithImpl(this._self, this._then);\n\n  final Metadata _self;\n  final $Res Function(Metadata) _then;\n\n/// Create a copy of Metadata\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? uid = null,Object? network = null,Object? sourceIP = null,Object? sourcePort = null,Object? destinationIP = null,Object? destinationPort = null,Object? host = null,Object? dnsMode = freezed,Object? process = null,Object? processPath = null,Object? remoteDestination = null,Object? sourceGeoIP = null,Object? destinationGeoIP = null,Object? destinationIPASN = null,Object? sourceIPASN = null,Object? specialRules = null,Object? specialProxy = null,}) {\n  return _then(_self.copyWith(\nuid: null == uid ? _self.uid : uid // ignore: cast_nullable_to_non_nullable\nas int,network: null == network ? _self.network : network // ignore: cast_nullable_to_non_nullable\nas String,sourceIP: null == sourceIP ? _self.sourceIP : sourceIP // ignore: cast_nullable_to_non_nullable\nas String,sourcePort: null == sourcePort ? _self.sourcePort : sourcePort // ignore: cast_nullable_to_non_nullable\nas String,destinationIP: null == destinationIP ? _self.destinationIP : destinationIP // ignore: cast_nullable_to_non_nullable\nas String,destinationPort: null == destinationPort ? _self.destinationPort : destinationPort // ignore: cast_nullable_to_non_nullable\nas String,host: null == host ? _self.host : host // ignore: cast_nullable_to_non_nullable\nas String,dnsMode: freezed == dnsMode ? _self.dnsMode : dnsMode // ignore: cast_nullable_to_non_nullable\nas DnsMode?,process: null == process ? _self.process : process // ignore: cast_nullable_to_non_nullable\nas String,processPath: null == processPath ? _self.processPath : processPath // ignore: cast_nullable_to_non_nullable\nas String,remoteDestination: null == remoteDestination ? _self.remoteDestination : remoteDestination // ignore: cast_nullable_to_non_nullable\nas String,sourceGeoIP: null == sourceGeoIP ? _self.sourceGeoIP : sourceGeoIP // ignore: cast_nullable_to_non_nullable\nas List<String>,destinationGeoIP: null == destinationGeoIP ? _self.destinationGeoIP : destinationGeoIP // ignore: cast_nullable_to_non_nullable\nas List<String>,destinationIPASN: null == destinationIPASN ? _self.destinationIPASN : destinationIPASN // ignore: cast_nullable_to_non_nullable\nas String,sourceIPASN: null == sourceIPASN ? _self.sourceIPASN : sourceIPASN // ignore: cast_nullable_to_non_nullable\nas String,specialRules: null == specialRules ? _self.specialRules : specialRules // ignore: cast_nullable_to_non_nullable\nas String,specialProxy: null == specialProxy ? _self.specialProxy : specialProxy // ignore: cast_nullable_to_non_nullable\nas String,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [Metadata].\nextension MetadataPatterns on Metadata {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _Metadata value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _Metadata() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _Metadata value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Metadata():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _Metadata value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Metadata() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( int uid,  String network,  String sourceIP,  String sourcePort,  String destinationIP,  String destinationPort,  String host,  DnsMode? dnsMode,  String process,  String processPath,  String remoteDestination,  List<String> sourceGeoIP,  List<String> destinationGeoIP,  String destinationIPASN,  String sourceIPASN,  String specialRules,  String specialProxy)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _Metadata() when $default != null:\nreturn $default(_that.uid,_that.network,_that.sourceIP,_that.sourcePort,_that.destinationIP,_that.destinationPort,_that.host,_that.dnsMode,_that.process,_that.processPath,_that.remoteDestination,_that.sourceGeoIP,_that.destinationGeoIP,_that.destinationIPASN,_that.sourceIPASN,_that.specialRules,_that.specialProxy);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( int uid,  String network,  String sourceIP,  String sourcePort,  String destinationIP,  String destinationPort,  String host,  DnsMode? dnsMode,  String process,  String processPath,  String remoteDestination,  List<String> sourceGeoIP,  List<String> destinationGeoIP,  String destinationIPASN,  String sourceIPASN,  String specialRules,  String specialProxy)  $default,) {final _that = this;\nswitch (_that) {\ncase _Metadata():\nreturn $default(_that.uid,_that.network,_that.sourceIP,_that.sourcePort,_that.destinationIP,_that.destinationPort,_that.host,_that.dnsMode,_that.process,_that.processPath,_that.remoteDestination,_that.sourceGeoIP,_that.destinationGeoIP,_that.destinationIPASN,_that.sourceIPASN,_that.specialRules,_that.specialProxy);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( int uid,  String network,  String sourceIP,  String sourcePort,  String destinationIP,  String destinationPort,  String host,  DnsMode? dnsMode,  String process,  String processPath,  String remoteDestination,  List<String> sourceGeoIP,  List<String> destinationGeoIP,  String destinationIPASN,  String sourceIPASN,  String specialRules,  String specialProxy)?  $default,) {final _that = this;\nswitch (_that) {\ncase _Metadata() when $default != null:\nreturn $default(_that.uid,_that.network,_that.sourceIP,_that.sourcePort,_that.destinationIP,_that.destinationPort,_that.host,_that.dnsMode,_that.process,_that.processPath,_that.remoteDestination,_that.sourceGeoIP,_that.destinationGeoIP,_that.destinationIPASN,_that.sourceIPASN,_that.specialRules,_that.specialProxy);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _Metadata implements Metadata {\n  const _Metadata({this.uid = 0, this.network = '', this.sourceIP = '', this.sourcePort = '', this.destinationIP = '', this.destinationPort = '', this.host = '', this.dnsMode, this.process = '', this.processPath = '', this.remoteDestination = '', final  List<String> sourceGeoIP = const [], final  List<String> destinationGeoIP = const [], this.destinationIPASN = '', this.sourceIPASN = '', this.specialRules = '', this.specialProxy = ''}): _sourceGeoIP = sourceGeoIP,_destinationGeoIP = destinationGeoIP;\n  factory _Metadata.fromJson(Map<String, dynamic> json) => _$MetadataFromJson(json);\n\n@override@JsonKey() final  int uid;\n@override@JsonKey() final  String network;\n@override@JsonKey() final  String sourceIP;\n@override@JsonKey() final  String sourcePort;\n@override@JsonKey() final  String destinationIP;\n@override@JsonKey() final  String destinationPort;\n@override@JsonKey() final  String host;\n@override final  DnsMode? dnsMode;\n@override@JsonKey() final  String process;\n@override@JsonKey() final  String processPath;\n@override@JsonKey() final  String remoteDestination;\n final  List<String> _sourceGeoIP;\n@override@JsonKey() List<String> get sourceGeoIP {\n  if (_sourceGeoIP is EqualUnmodifiableListView) return _sourceGeoIP;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_sourceGeoIP);\n}\n\n final  List<String> _destinationGeoIP;\n@override@JsonKey() List<String> get destinationGeoIP {\n  if (_destinationGeoIP is EqualUnmodifiableListView) return _destinationGeoIP;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_destinationGeoIP);\n}\n\n@override@JsonKey() final  String destinationIPASN;\n@override@JsonKey() final  String sourceIPASN;\n@override@JsonKey() final  String specialRules;\n@override@JsonKey() final  String specialProxy;\n\n/// Create a copy of Metadata\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$MetadataCopyWith<_Metadata> get copyWith => __$MetadataCopyWithImpl<_Metadata>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$MetadataToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _Metadata&&(identical(other.uid, uid) || other.uid == uid)&&(identical(other.network, network) || other.network == network)&&(identical(other.sourceIP, sourceIP) || other.sourceIP == sourceIP)&&(identical(other.sourcePort, sourcePort) || other.sourcePort == sourcePort)&&(identical(other.destinationIP, destinationIP) || other.destinationIP == destinationIP)&&(identical(other.destinationPort, destinationPort) || other.destinationPort == destinationPort)&&(identical(other.host, host) || other.host == host)&&(identical(other.dnsMode, dnsMode) || other.dnsMode == dnsMode)&&(identical(other.process, process) || other.process == process)&&(identical(other.processPath, processPath) || other.processPath == processPath)&&(identical(other.remoteDestination, remoteDestination) || other.remoteDestination == remoteDestination)&&const DeepCollectionEquality().equals(other._sourceGeoIP, _sourceGeoIP)&&const DeepCollectionEquality().equals(other._destinationGeoIP, _destinationGeoIP)&&(identical(other.destinationIPASN, destinationIPASN) || other.destinationIPASN == destinationIPASN)&&(identical(other.sourceIPASN, sourceIPASN) || other.sourceIPASN == sourceIPASN)&&(identical(other.specialRules, specialRules) || other.specialRules == specialRules)&&(identical(other.specialProxy, specialProxy) || other.specialProxy == specialProxy));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,uid,network,sourceIP,sourcePort,destinationIP,destinationPort,host,dnsMode,process,processPath,remoteDestination,const DeepCollectionEquality().hash(_sourceGeoIP),const DeepCollectionEquality().hash(_destinationGeoIP),destinationIPASN,sourceIPASN,specialRules,specialProxy);\n\n@override\nString toString() {\n  return 'Metadata(uid: $uid, network: $network, sourceIP: $sourceIP, sourcePort: $sourcePort, destinationIP: $destinationIP, destinationPort: $destinationPort, host: $host, dnsMode: $dnsMode, process: $process, processPath: $processPath, remoteDestination: $remoteDestination, sourceGeoIP: $sourceGeoIP, destinationGeoIP: $destinationGeoIP, destinationIPASN: $destinationIPASN, sourceIPASN: $sourceIPASN, specialRules: $specialRules, specialProxy: $specialProxy)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$MetadataCopyWith<$Res> implements $MetadataCopyWith<$Res> {\n  factory _$MetadataCopyWith(_Metadata value, $Res Function(_Metadata) _then) = __$MetadataCopyWithImpl;\n@override @useResult\n$Res call({\n int uid, String network, String sourceIP, String sourcePort, String destinationIP, String destinationPort, String host, DnsMode? dnsMode, String process, String processPath, String remoteDestination, List<String> sourceGeoIP, List<String> destinationGeoIP, String destinationIPASN, String sourceIPASN, String specialRules, String specialProxy\n});\n\n\n\n\n}\n/// @nodoc\nclass __$MetadataCopyWithImpl<$Res>\n    implements _$MetadataCopyWith<$Res> {\n  __$MetadataCopyWithImpl(this._self, this._then);\n\n  final _Metadata _self;\n  final $Res Function(_Metadata) _then;\n\n/// Create a copy of Metadata\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? uid = null,Object? network = null,Object? sourceIP = null,Object? sourcePort = null,Object? destinationIP = null,Object? destinationPort = null,Object? host = null,Object? dnsMode = freezed,Object? process = null,Object? processPath = null,Object? remoteDestination = null,Object? sourceGeoIP = null,Object? destinationGeoIP = null,Object? destinationIPASN = null,Object? sourceIPASN = null,Object? specialRules = null,Object? specialProxy = null,}) {\n  return _then(_Metadata(\nuid: null == uid ? _self.uid : uid // ignore: cast_nullable_to_non_nullable\nas int,network: null == network ? _self.network : network // ignore: cast_nullable_to_non_nullable\nas String,sourceIP: null == sourceIP ? _self.sourceIP : sourceIP // ignore: cast_nullable_to_non_nullable\nas String,sourcePort: null == sourcePort ? _self.sourcePort : sourcePort // ignore: cast_nullable_to_non_nullable\nas String,destinationIP: null == destinationIP ? _self.destinationIP : destinationIP // ignore: cast_nullable_to_non_nullable\nas String,destinationPort: null == destinationPort ? _self.destinationPort : destinationPort // ignore: cast_nullable_to_non_nullable\nas String,host: null == host ? _self.host : host // ignore: cast_nullable_to_non_nullable\nas String,dnsMode: freezed == dnsMode ? _self.dnsMode : dnsMode // ignore: cast_nullable_to_non_nullable\nas DnsMode?,process: null == process ? _self.process : process // ignore: cast_nullable_to_non_nullable\nas String,processPath: null == processPath ? _self.processPath : processPath // ignore: cast_nullable_to_non_nullable\nas String,remoteDestination: null == remoteDestination ? _self.remoteDestination : remoteDestination // ignore: cast_nullable_to_non_nullable\nas String,sourceGeoIP: null == sourceGeoIP ? _self._sourceGeoIP : sourceGeoIP // ignore: cast_nullable_to_non_nullable\nas List<String>,destinationGeoIP: null == destinationGeoIP ? _self._destinationGeoIP : destinationGeoIP // ignore: cast_nullable_to_non_nullable\nas List<String>,destinationIPASN: null == destinationIPASN ? _self.destinationIPASN : destinationIPASN // ignore: cast_nullable_to_non_nullable\nas String,sourceIPASN: null == sourceIPASN ? _self.sourceIPASN : sourceIPASN // ignore: cast_nullable_to_non_nullable\nas String,specialRules: null == specialRules ? _self.specialRules : specialRules // ignore: cast_nullable_to_non_nullable\nas String,specialProxy: null == specialProxy ? _self.specialProxy : specialProxy // ignore: cast_nullable_to_non_nullable\nas String,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$TrackerInfo {\n\n String get id; int get upload; int get download; DateTime get start; Metadata get metadata; List<String> get chains; String get rule; String get rulePayload; int? get downloadSpeed; int? get uploadSpeed;\n/// Create a copy of TrackerInfo\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$TrackerInfoCopyWith<TrackerInfo> get copyWith => _$TrackerInfoCopyWithImpl<TrackerInfo>(this as TrackerInfo, _$identity);\n\n  /// Serializes this TrackerInfo to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is TrackerInfo&&(identical(other.id, id) || other.id == id)&&(identical(other.upload, upload) || other.upload == upload)&&(identical(other.download, download) || other.download == download)&&(identical(other.start, start) || other.start == start)&&(identical(other.metadata, metadata) || other.metadata == metadata)&&const DeepCollectionEquality().equals(other.chains, chains)&&(identical(other.rule, rule) || other.rule == rule)&&(identical(other.rulePayload, rulePayload) || other.rulePayload == rulePayload)&&(identical(other.downloadSpeed, downloadSpeed) || other.downloadSpeed == downloadSpeed)&&(identical(other.uploadSpeed, uploadSpeed) || other.uploadSpeed == uploadSpeed));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,id,upload,download,start,metadata,const DeepCollectionEquality().hash(chains),rule,rulePayload,downloadSpeed,uploadSpeed);\n\n@override\nString toString() {\n  return 'TrackerInfo(id: $id, upload: $upload, download: $download, start: $start, metadata: $metadata, chains: $chains, rule: $rule, rulePayload: $rulePayload, downloadSpeed: $downloadSpeed, uploadSpeed: $uploadSpeed)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $TrackerInfoCopyWith<$Res>  {\n  factory $TrackerInfoCopyWith(TrackerInfo value, $Res Function(TrackerInfo) _then) = _$TrackerInfoCopyWithImpl;\n@useResult\n$Res call({\n String id, int upload, int download, DateTime start, Metadata metadata, List<String> chains, String rule, String rulePayload, int? downloadSpeed, int? uploadSpeed\n});\n\n\n$MetadataCopyWith<$Res> get metadata;\n\n}\n/// @nodoc\nclass _$TrackerInfoCopyWithImpl<$Res>\n    implements $TrackerInfoCopyWith<$Res> {\n  _$TrackerInfoCopyWithImpl(this._self, this._then);\n\n  final TrackerInfo _self;\n  final $Res Function(TrackerInfo) _then;\n\n/// Create a copy of TrackerInfo\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? upload = null,Object? download = null,Object? start = null,Object? metadata = null,Object? chains = null,Object? rule = null,Object? rulePayload = null,Object? downloadSpeed = freezed,Object? uploadSpeed = freezed,}) {\n  return _then(_self.copyWith(\nid: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable\nas String,upload: null == upload ? _self.upload : upload // ignore: cast_nullable_to_non_nullable\nas int,download: null == download ? _self.download : download // ignore: cast_nullable_to_non_nullable\nas int,start: null == start ? _self.start : start // ignore: cast_nullable_to_non_nullable\nas DateTime,metadata: null == metadata ? _self.metadata : metadata // ignore: cast_nullable_to_non_nullable\nas Metadata,chains: null == chains ? _self.chains : chains // ignore: cast_nullable_to_non_nullable\nas List<String>,rule: null == rule ? _self.rule : rule // ignore: cast_nullable_to_non_nullable\nas String,rulePayload: null == rulePayload ? _self.rulePayload : rulePayload // ignore: cast_nullable_to_non_nullable\nas String,downloadSpeed: freezed == downloadSpeed ? _self.downloadSpeed : downloadSpeed // ignore: cast_nullable_to_non_nullable\nas int?,uploadSpeed: freezed == uploadSpeed ? _self.uploadSpeed : uploadSpeed // ignore: cast_nullable_to_non_nullable\nas int?,\n  ));\n}\n/// Create a copy of TrackerInfo\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$MetadataCopyWith<$Res> get metadata {\n  \n  return $MetadataCopyWith<$Res>(_self.metadata, (value) {\n    return _then(_self.copyWith(metadata: value));\n  });\n}\n}\n\n\n/// Adds pattern-matching-related methods to [TrackerInfo].\nextension TrackerInfoPatterns on TrackerInfo {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _TrackerInfo value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _TrackerInfo() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _TrackerInfo value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _TrackerInfo():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _TrackerInfo value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _TrackerInfo() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id,  int upload,  int download,  DateTime start,  Metadata metadata,  List<String> chains,  String rule,  String rulePayload,  int? downloadSpeed,  int? uploadSpeed)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _TrackerInfo() when $default != null:\nreturn $default(_that.id,_that.upload,_that.download,_that.start,_that.metadata,_that.chains,_that.rule,_that.rulePayload,_that.downloadSpeed,_that.uploadSpeed);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id,  int upload,  int download,  DateTime start,  Metadata metadata,  List<String> chains,  String rule,  String rulePayload,  int? downloadSpeed,  int? uploadSpeed)  $default,) {final _that = this;\nswitch (_that) {\ncase _TrackerInfo():\nreturn $default(_that.id,_that.upload,_that.download,_that.start,_that.metadata,_that.chains,_that.rule,_that.rulePayload,_that.downloadSpeed,_that.uploadSpeed);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id,  int upload,  int download,  DateTime start,  Metadata metadata,  List<String> chains,  String rule,  String rulePayload,  int? downloadSpeed,  int? uploadSpeed)?  $default,) {final _that = this;\nswitch (_that) {\ncase _TrackerInfo() when $default != null:\nreturn $default(_that.id,_that.upload,_that.download,_that.start,_that.metadata,_that.chains,_that.rule,_that.rulePayload,_that.downloadSpeed,_that.uploadSpeed);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _TrackerInfo implements TrackerInfo {\n  const _TrackerInfo({required this.id, this.upload = 0, this.download = 0, required this.start, required this.metadata, required final  List<String> chains, required this.rule, required this.rulePayload, this.downloadSpeed, this.uploadSpeed}): _chains = chains;\n  factory _TrackerInfo.fromJson(Map<String, dynamic> json) => _$TrackerInfoFromJson(json);\n\n@override final  String id;\n@override@JsonKey() final  int upload;\n@override@JsonKey() final  int download;\n@override final  DateTime start;\n@override final  Metadata metadata;\n final  List<String> _chains;\n@override List<String> get chains {\n  if (_chains is EqualUnmodifiableListView) return _chains;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_chains);\n}\n\n@override final  String rule;\n@override final  String rulePayload;\n@override final  int? downloadSpeed;\n@override final  int? uploadSpeed;\n\n/// Create a copy of TrackerInfo\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$TrackerInfoCopyWith<_TrackerInfo> get copyWith => __$TrackerInfoCopyWithImpl<_TrackerInfo>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$TrackerInfoToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _TrackerInfo&&(identical(other.id, id) || other.id == id)&&(identical(other.upload, upload) || other.upload == upload)&&(identical(other.download, download) || other.download == download)&&(identical(other.start, start) || other.start == start)&&(identical(other.metadata, metadata) || other.metadata == metadata)&&const DeepCollectionEquality().equals(other._chains, _chains)&&(identical(other.rule, rule) || other.rule == rule)&&(identical(other.rulePayload, rulePayload) || other.rulePayload == rulePayload)&&(identical(other.downloadSpeed, downloadSpeed) || other.downloadSpeed == downloadSpeed)&&(identical(other.uploadSpeed, uploadSpeed) || other.uploadSpeed == uploadSpeed));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,id,upload,download,start,metadata,const DeepCollectionEquality().hash(_chains),rule,rulePayload,downloadSpeed,uploadSpeed);\n\n@override\nString toString() {\n  return 'TrackerInfo(id: $id, upload: $upload, download: $download, start: $start, metadata: $metadata, chains: $chains, rule: $rule, rulePayload: $rulePayload, downloadSpeed: $downloadSpeed, uploadSpeed: $uploadSpeed)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$TrackerInfoCopyWith<$Res> implements $TrackerInfoCopyWith<$Res> {\n  factory _$TrackerInfoCopyWith(_TrackerInfo value, $Res Function(_TrackerInfo) _then) = __$TrackerInfoCopyWithImpl;\n@override @useResult\n$Res call({\n String id, int upload, int download, DateTime start, Metadata metadata, List<String> chains, String rule, String rulePayload, int? downloadSpeed, int? uploadSpeed\n});\n\n\n@override $MetadataCopyWith<$Res> get metadata;\n\n}\n/// @nodoc\nclass __$TrackerInfoCopyWithImpl<$Res>\n    implements _$TrackerInfoCopyWith<$Res> {\n  __$TrackerInfoCopyWithImpl(this._self, this._then);\n\n  final _TrackerInfo _self;\n  final $Res Function(_TrackerInfo) _then;\n\n/// Create a copy of TrackerInfo\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? upload = null,Object? download = null,Object? start = null,Object? metadata = null,Object? chains = null,Object? rule = null,Object? rulePayload = null,Object? downloadSpeed = freezed,Object? uploadSpeed = freezed,}) {\n  return _then(_TrackerInfo(\nid: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable\nas String,upload: null == upload ? _self.upload : upload // ignore: cast_nullable_to_non_nullable\nas int,download: null == download ? _self.download : download // ignore: cast_nullable_to_non_nullable\nas int,start: null == start ? _self.start : start // ignore: cast_nullable_to_non_nullable\nas DateTime,metadata: null == metadata ? _self.metadata : metadata // ignore: cast_nullable_to_non_nullable\nas Metadata,chains: null == chains ? _self._chains : chains // ignore: cast_nullable_to_non_nullable\nas List<String>,rule: null == rule ? _self.rule : rule // ignore: cast_nullable_to_non_nullable\nas String,rulePayload: null == rulePayload ? _self.rulePayload : rulePayload // ignore: cast_nullable_to_non_nullable\nas String,downloadSpeed: freezed == downloadSpeed ? _self.downloadSpeed : downloadSpeed // ignore: cast_nullable_to_non_nullable\nas int?,uploadSpeed: freezed == uploadSpeed ? _self.uploadSpeed : uploadSpeed // ignore: cast_nullable_to_non_nullable\nas int?,\n  ));\n}\n\n/// Create a copy of TrackerInfo\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$MetadataCopyWith<$Res> get metadata {\n  \n  return $MetadataCopyWith<$Res>(_self.metadata, (value) {\n    return _then(_self.copyWith(metadata: value));\n  });\n}\n}\n\n\n/// @nodoc\nmixin _$Log {\n\n// @JsonKey(fromJson: _logId) required String id,\n@JsonKey(name: 'LogLevel') LogLevel get logLevel;@JsonKey(name: 'Payload') String get payload;@JsonKey(fromJson: _logDateTime) String get dateTime;\n/// Create a copy of Log\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$LogCopyWith<Log> get copyWith => _$LogCopyWithImpl<Log>(this as Log, _$identity);\n\n  /// Serializes this Log to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is Log&&(identical(other.logLevel, logLevel) || other.logLevel == logLevel)&&(identical(other.payload, payload) || other.payload == payload)&&(identical(other.dateTime, dateTime) || other.dateTime == dateTime));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,logLevel,payload,dateTime);\n\n@override\nString toString() {\n  return 'Log(logLevel: $logLevel, payload: $payload, dateTime: $dateTime)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $LogCopyWith<$Res>  {\n  factory $LogCopyWith(Log value, $Res Function(Log) _then) = _$LogCopyWithImpl;\n@useResult\n$Res call({\n@JsonKey(name: 'LogLevel') LogLevel logLevel,@JsonKey(name: 'Payload') String payload,@JsonKey(fromJson: _logDateTime) String dateTime\n});\n\n\n\n\n}\n/// @nodoc\nclass _$LogCopyWithImpl<$Res>\n    implements $LogCopyWith<$Res> {\n  _$LogCopyWithImpl(this._self, this._then);\n\n  final Log _self;\n  final $Res Function(Log) _then;\n\n/// Create a copy of Log\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? logLevel = null,Object? payload = null,Object? dateTime = null,}) {\n  return _then(_self.copyWith(\nlogLevel: null == logLevel ? _self.logLevel : logLevel // ignore: cast_nullable_to_non_nullable\nas LogLevel,payload: null == payload ? _self.payload : payload // ignore: cast_nullable_to_non_nullable\nas String,dateTime: null == dateTime ? _self.dateTime : dateTime // ignore: cast_nullable_to_non_nullable\nas String,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [Log].\nextension LogPatterns on Log {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _Log value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _Log() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _Log value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Log():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _Log value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Log() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(@JsonKey(name: 'LogLevel')  LogLevel logLevel, @JsonKey(name: 'Payload')  String payload, @JsonKey(fromJson: _logDateTime)  String dateTime)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _Log() when $default != null:\nreturn $default(_that.logLevel,_that.payload,_that.dateTime);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(@JsonKey(name: 'LogLevel')  LogLevel logLevel, @JsonKey(name: 'Payload')  String payload, @JsonKey(fromJson: _logDateTime)  String dateTime)  $default,) {final _that = this;\nswitch (_that) {\ncase _Log():\nreturn $default(_that.logLevel,_that.payload,_that.dateTime);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(@JsonKey(name: 'LogLevel')  LogLevel logLevel, @JsonKey(name: 'Payload')  String payload, @JsonKey(fromJson: _logDateTime)  String dateTime)?  $default,) {final _that = this;\nswitch (_that) {\ncase _Log() when $default != null:\nreturn $default(_that.logLevel,_that.payload,_that.dateTime);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _Log implements Log {\n  const _Log({@JsonKey(name: 'LogLevel') this.logLevel = LogLevel.error, @JsonKey(name: 'Payload') this.payload = '', @JsonKey(fromJson: _logDateTime) required this.dateTime});\n  factory _Log.fromJson(Map<String, dynamic> json) => _$LogFromJson(json);\n\n// @JsonKey(fromJson: _logId) required String id,\n@override@JsonKey(name: 'LogLevel') final  LogLevel logLevel;\n@override@JsonKey(name: 'Payload') final  String payload;\n@override@JsonKey(fromJson: _logDateTime) final  String dateTime;\n\n/// Create a copy of Log\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$LogCopyWith<_Log> get copyWith => __$LogCopyWithImpl<_Log>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$LogToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _Log&&(identical(other.logLevel, logLevel) || other.logLevel == logLevel)&&(identical(other.payload, payload) || other.payload == payload)&&(identical(other.dateTime, dateTime) || other.dateTime == dateTime));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,logLevel,payload,dateTime);\n\n@override\nString toString() {\n  return 'Log(logLevel: $logLevel, payload: $payload, dateTime: $dateTime)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$LogCopyWith<$Res> implements $LogCopyWith<$Res> {\n  factory _$LogCopyWith(_Log value, $Res Function(_Log) _then) = __$LogCopyWithImpl;\n@override @useResult\n$Res call({\n@JsonKey(name: 'LogLevel') LogLevel logLevel,@JsonKey(name: 'Payload') String payload,@JsonKey(fromJson: _logDateTime) String dateTime\n});\n\n\n\n\n}\n/// @nodoc\nclass __$LogCopyWithImpl<$Res>\n    implements _$LogCopyWith<$Res> {\n  __$LogCopyWithImpl(this._self, this._then);\n\n  final _Log _self;\n  final $Res Function(_Log) _then;\n\n/// Create a copy of Log\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? logLevel = null,Object? payload = null,Object? dateTime = null,}) {\n  return _then(_Log(\nlogLevel: null == logLevel ? _self.logLevel : logLevel // ignore: cast_nullable_to_non_nullable\nas LogLevel,payload: null == payload ? _self.payload : payload // ignore: cast_nullable_to_non_nullable\nas String,dateTime: null == dateTime ? _self.dateTime : dateTime // ignore: cast_nullable_to_non_nullable\nas String,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$LogsState {\n\n List<Log> get logs; List<String> get keywords; String get query; bool get autoScrollToEnd;\n/// Create a copy of LogsState\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$LogsStateCopyWith<LogsState> get copyWith => _$LogsStateCopyWithImpl<LogsState>(this as LogsState, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is LogsState&&const DeepCollectionEquality().equals(other.logs, logs)&&const DeepCollectionEquality().equals(other.keywords, keywords)&&(identical(other.query, query) || other.query == query)&&(identical(other.autoScrollToEnd, autoScrollToEnd) || other.autoScrollToEnd == autoScrollToEnd));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(logs),const DeepCollectionEquality().hash(keywords),query,autoScrollToEnd);\n\n@override\nString toString() {\n  return 'LogsState(logs: $logs, keywords: $keywords, query: $query, autoScrollToEnd: $autoScrollToEnd)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $LogsStateCopyWith<$Res>  {\n  factory $LogsStateCopyWith(LogsState value, $Res Function(LogsState) _then) = _$LogsStateCopyWithImpl;\n@useResult\n$Res call({\n List<Log> logs, List<String> keywords, String query, bool autoScrollToEnd\n});\n\n\n\n\n}\n/// @nodoc\nclass _$LogsStateCopyWithImpl<$Res>\n    implements $LogsStateCopyWith<$Res> {\n  _$LogsStateCopyWithImpl(this._self, this._then);\n\n  final LogsState _self;\n  final $Res Function(LogsState) _then;\n\n/// Create a copy of LogsState\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? logs = null,Object? keywords = null,Object? query = null,Object? autoScrollToEnd = null,}) {\n  return _then(_self.copyWith(\nlogs: null == logs ? _self.logs : logs // ignore: cast_nullable_to_non_nullable\nas List<Log>,keywords: null == keywords ? _self.keywords : keywords // ignore: cast_nullable_to_non_nullable\nas List<String>,query: null == query ? _self.query : query // ignore: cast_nullable_to_non_nullable\nas String,autoScrollToEnd: null == autoScrollToEnd ? _self.autoScrollToEnd : autoScrollToEnd // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [LogsState].\nextension LogsStatePatterns on LogsState {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _LogsState value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _LogsState() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _LogsState value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _LogsState():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _LogsState value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _LogsState() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<Log> logs,  List<String> keywords,  String query,  bool autoScrollToEnd)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _LogsState() when $default != null:\nreturn $default(_that.logs,_that.keywords,_that.query,_that.autoScrollToEnd);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<Log> logs,  List<String> keywords,  String query,  bool autoScrollToEnd)  $default,) {final _that = this;\nswitch (_that) {\ncase _LogsState():\nreturn $default(_that.logs,_that.keywords,_that.query,_that.autoScrollToEnd);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<Log> logs,  List<String> keywords,  String query,  bool autoScrollToEnd)?  $default,) {final _that = this;\nswitch (_that) {\ncase _LogsState() when $default != null:\nreturn $default(_that.logs,_that.keywords,_that.query,_that.autoScrollToEnd);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _LogsState implements LogsState {\n  const _LogsState({final  List<Log> logs = const [], final  List<String> keywords = const [], this.query = '', this.autoScrollToEnd = false}): _logs = logs,_keywords = keywords;\n  \n\n final  List<Log> _logs;\n@override@JsonKey() List<Log> get logs {\n  if (_logs is EqualUnmodifiableListView) return _logs;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_logs);\n}\n\n final  List<String> _keywords;\n@override@JsonKey() List<String> get keywords {\n  if (_keywords is EqualUnmodifiableListView) return _keywords;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_keywords);\n}\n\n@override@JsonKey() final  String query;\n@override@JsonKey() final  bool autoScrollToEnd;\n\n/// Create a copy of LogsState\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$LogsStateCopyWith<_LogsState> get copyWith => __$LogsStateCopyWithImpl<_LogsState>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _LogsState&&const DeepCollectionEquality().equals(other._logs, _logs)&&const DeepCollectionEquality().equals(other._keywords, _keywords)&&(identical(other.query, query) || other.query == query)&&(identical(other.autoScrollToEnd, autoScrollToEnd) || other.autoScrollToEnd == autoScrollToEnd));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_logs),const DeepCollectionEquality().hash(_keywords),query,autoScrollToEnd);\n\n@override\nString toString() {\n  return 'LogsState(logs: $logs, keywords: $keywords, query: $query, autoScrollToEnd: $autoScrollToEnd)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$LogsStateCopyWith<$Res> implements $LogsStateCopyWith<$Res> {\n  factory _$LogsStateCopyWith(_LogsState value, $Res Function(_LogsState) _then) = __$LogsStateCopyWithImpl;\n@override @useResult\n$Res call({\n List<Log> logs, List<String> keywords, String query, bool autoScrollToEnd\n});\n\n\n\n\n}\n/// @nodoc\nclass __$LogsStateCopyWithImpl<$Res>\n    implements _$LogsStateCopyWith<$Res> {\n  __$LogsStateCopyWithImpl(this._self, this._then);\n\n  final _LogsState _self;\n  final $Res Function(_LogsState) _then;\n\n/// Create a copy of LogsState\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? logs = null,Object? keywords = null,Object? query = null,Object? autoScrollToEnd = null,}) {\n  return _then(_LogsState(\nlogs: null == logs ? _self._logs : logs // ignore: cast_nullable_to_non_nullable\nas List<Log>,keywords: null == keywords ? _self._keywords : keywords // ignore: cast_nullable_to_non_nullable\nas List<String>,query: null == query ? _self.query : query // ignore: cast_nullable_to_non_nullable\nas String,autoScrollToEnd: null == autoScrollToEnd ? _self.autoScrollToEnd : autoScrollToEnd // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$TrackerInfosState {\n\n List<TrackerInfo> get trackerInfos; List<String> get keywords; String get query; bool get autoScrollToEnd;\n/// Create a copy of TrackerInfosState\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$TrackerInfosStateCopyWith<TrackerInfosState> get copyWith => _$TrackerInfosStateCopyWithImpl<TrackerInfosState>(this as TrackerInfosState, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is TrackerInfosState&&const DeepCollectionEquality().equals(other.trackerInfos, trackerInfos)&&const DeepCollectionEquality().equals(other.keywords, keywords)&&(identical(other.query, query) || other.query == query)&&(identical(other.autoScrollToEnd, autoScrollToEnd) || other.autoScrollToEnd == autoScrollToEnd));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(trackerInfos),const DeepCollectionEquality().hash(keywords),query,autoScrollToEnd);\n\n@override\nString toString() {\n  return 'TrackerInfosState(trackerInfos: $trackerInfos, keywords: $keywords, query: $query, autoScrollToEnd: $autoScrollToEnd)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $TrackerInfosStateCopyWith<$Res>  {\n  factory $TrackerInfosStateCopyWith(TrackerInfosState value, $Res Function(TrackerInfosState) _then) = _$TrackerInfosStateCopyWithImpl;\n@useResult\n$Res call({\n List<TrackerInfo> trackerInfos, List<String> keywords, String query, bool autoScrollToEnd\n});\n\n\n\n\n}\n/// @nodoc\nclass _$TrackerInfosStateCopyWithImpl<$Res>\n    implements $TrackerInfosStateCopyWith<$Res> {\n  _$TrackerInfosStateCopyWithImpl(this._self, this._then);\n\n  final TrackerInfosState _self;\n  final $Res Function(TrackerInfosState) _then;\n\n/// Create a copy of TrackerInfosState\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? trackerInfos = null,Object? keywords = null,Object? query = null,Object? autoScrollToEnd = null,}) {\n  return _then(_self.copyWith(\ntrackerInfos: null == trackerInfos ? _self.trackerInfos : trackerInfos // ignore: cast_nullable_to_non_nullable\nas List<TrackerInfo>,keywords: null == keywords ? _self.keywords : keywords // ignore: cast_nullable_to_non_nullable\nas List<String>,query: null == query ? _self.query : query // ignore: cast_nullable_to_non_nullable\nas String,autoScrollToEnd: null == autoScrollToEnd ? _self.autoScrollToEnd : autoScrollToEnd // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [TrackerInfosState].\nextension TrackerInfosStatePatterns on TrackerInfosState {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _TrackerInfosState value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _TrackerInfosState() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _TrackerInfosState value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _TrackerInfosState():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _TrackerInfosState value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _TrackerInfosState() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<TrackerInfo> trackerInfos,  List<String> keywords,  String query,  bool autoScrollToEnd)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _TrackerInfosState() when $default != null:\nreturn $default(_that.trackerInfos,_that.keywords,_that.query,_that.autoScrollToEnd);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<TrackerInfo> trackerInfos,  List<String> keywords,  String query,  bool autoScrollToEnd)  $default,) {final _that = this;\nswitch (_that) {\ncase _TrackerInfosState():\nreturn $default(_that.trackerInfos,_that.keywords,_that.query,_that.autoScrollToEnd);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<TrackerInfo> trackerInfos,  List<String> keywords,  String query,  bool autoScrollToEnd)?  $default,) {final _that = this;\nswitch (_that) {\ncase _TrackerInfosState() when $default != null:\nreturn $default(_that.trackerInfos,_that.keywords,_that.query,_that.autoScrollToEnd);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _TrackerInfosState implements TrackerInfosState {\n  const _TrackerInfosState({final  List<TrackerInfo> trackerInfos = const [], final  List<String> keywords = const [], this.query = '', this.autoScrollToEnd = false}): _trackerInfos = trackerInfos,_keywords = keywords;\n  \n\n final  List<TrackerInfo> _trackerInfos;\n@override@JsonKey() List<TrackerInfo> get trackerInfos {\n  if (_trackerInfos is EqualUnmodifiableListView) return _trackerInfos;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_trackerInfos);\n}\n\n final  List<String> _keywords;\n@override@JsonKey() List<String> get keywords {\n  if (_keywords is EqualUnmodifiableListView) return _keywords;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_keywords);\n}\n\n@override@JsonKey() final  String query;\n@override@JsonKey() final  bool autoScrollToEnd;\n\n/// Create a copy of TrackerInfosState\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$TrackerInfosStateCopyWith<_TrackerInfosState> get copyWith => __$TrackerInfosStateCopyWithImpl<_TrackerInfosState>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _TrackerInfosState&&const DeepCollectionEquality().equals(other._trackerInfos, _trackerInfos)&&const DeepCollectionEquality().equals(other._keywords, _keywords)&&(identical(other.query, query) || other.query == query)&&(identical(other.autoScrollToEnd, autoScrollToEnd) || other.autoScrollToEnd == autoScrollToEnd));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_trackerInfos),const DeepCollectionEquality().hash(_keywords),query,autoScrollToEnd);\n\n@override\nString toString() {\n  return 'TrackerInfosState(trackerInfos: $trackerInfos, keywords: $keywords, query: $query, autoScrollToEnd: $autoScrollToEnd)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$TrackerInfosStateCopyWith<$Res> implements $TrackerInfosStateCopyWith<$Res> {\n  factory _$TrackerInfosStateCopyWith(_TrackerInfosState value, $Res Function(_TrackerInfosState) _then) = __$TrackerInfosStateCopyWithImpl;\n@override @useResult\n$Res call({\n List<TrackerInfo> trackerInfos, List<String> keywords, String query, bool autoScrollToEnd\n});\n\n\n\n\n}\n/// @nodoc\nclass __$TrackerInfosStateCopyWithImpl<$Res>\n    implements _$TrackerInfosStateCopyWith<$Res> {\n  __$TrackerInfosStateCopyWithImpl(this._self, this._then);\n\n  final _TrackerInfosState _self;\n  final $Res Function(_TrackerInfosState) _then;\n\n/// Create a copy of TrackerInfosState\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? trackerInfos = null,Object? keywords = null,Object? query = null,Object? autoScrollToEnd = null,}) {\n  return _then(_TrackerInfosState(\ntrackerInfos: null == trackerInfos ? _self._trackerInfos : trackerInfos // ignore: cast_nullable_to_non_nullable\nas List<TrackerInfo>,keywords: null == keywords ? _self._keywords : keywords // ignore: cast_nullable_to_non_nullable\nas List<String>,query: null == query ? _self.query : query // ignore: cast_nullable_to_non_nullable\nas String,autoScrollToEnd: null == autoScrollToEnd ? _self.autoScrollToEnd : autoScrollToEnd // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$DAV {\n\n String get uri; String get user; String get password; String get fileName;\n/// Create a copy of DAV\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$DAVCopyWith<DAV> get copyWith => _$DAVCopyWithImpl<DAV>(this as DAV, _$identity);\n\n  /// Serializes this DAV to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is DAV&&(identical(other.uri, uri) || other.uri == uri)&&(identical(other.user, user) || other.user == user)&&(identical(other.password, password) || other.password == password)&&(identical(other.fileName, fileName) || other.fileName == fileName));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,uri,user,password,fileName);\n\n@override\nString toString() {\n  return 'DAV(uri: $uri, user: $user, password: $password, fileName: $fileName)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $DAVCopyWith<$Res>  {\n  factory $DAVCopyWith(DAV value, $Res Function(DAV) _then) = _$DAVCopyWithImpl;\n@useResult\n$Res call({\n String uri, String user, String password, String fileName\n});\n\n\n\n\n}\n/// @nodoc\nclass _$DAVCopyWithImpl<$Res>\n    implements $DAVCopyWith<$Res> {\n  _$DAVCopyWithImpl(this._self, this._then);\n\n  final DAV _self;\n  final $Res Function(DAV) _then;\n\n/// Create a copy of DAV\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? uri = null,Object? user = null,Object? password = null,Object? fileName = null,}) {\n  return _then(_self.copyWith(\nuri: null == uri ? _self.uri : uri // ignore: cast_nullable_to_non_nullable\nas String,user: null == user ? _self.user : user // ignore: cast_nullable_to_non_nullable\nas String,password: null == password ? _self.password : password // ignore: cast_nullable_to_non_nullable\nas String,fileName: null == fileName ? _self.fileName : fileName // ignore: cast_nullable_to_non_nullable\nas String,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [DAV].\nextension DAVPatterns on DAV {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _DAV value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _DAV() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _DAV value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _DAV():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _DAV value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _DAV() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String uri,  String user,  String password,  String fileName)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _DAV() when $default != null:\nreturn $default(_that.uri,_that.user,_that.password,_that.fileName);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String uri,  String user,  String password,  String fileName)  $default,) {final _that = this;\nswitch (_that) {\ncase _DAV():\nreturn $default(_that.uri,_that.user,_that.password,_that.fileName);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String uri,  String user,  String password,  String fileName)?  $default,) {final _that = this;\nswitch (_that) {\ncase _DAV() when $default != null:\nreturn $default(_that.uri,_that.user,_that.password,_that.fileName);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _DAV implements DAV {\n  const _DAV({required this.uri, required this.user, required this.password, this.fileName = defaultDavFileName});\n  factory _DAV.fromJson(Map<String, dynamic> json) => _$DAVFromJson(json);\n\n@override final  String uri;\n@override final  String user;\n@override final  String password;\n@override@JsonKey() final  String fileName;\n\n/// Create a copy of DAV\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$DAVCopyWith<_DAV> get copyWith => __$DAVCopyWithImpl<_DAV>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$DAVToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _DAV&&(identical(other.uri, uri) || other.uri == uri)&&(identical(other.user, user) || other.user == user)&&(identical(other.password, password) || other.password == password)&&(identical(other.fileName, fileName) || other.fileName == fileName));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,uri,user,password,fileName);\n\n@override\nString toString() {\n  return 'DAV(uri: $uri, user: $user, password: $password, fileName: $fileName)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$DAVCopyWith<$Res> implements $DAVCopyWith<$Res> {\n  factory _$DAVCopyWith(_DAV value, $Res Function(_DAV) _then) = __$DAVCopyWithImpl;\n@override @useResult\n$Res call({\n String uri, String user, String password, String fileName\n});\n\n\n\n\n}\n/// @nodoc\nclass __$DAVCopyWithImpl<$Res>\n    implements _$DAVCopyWith<$Res> {\n  __$DAVCopyWithImpl(this._self, this._then);\n\n  final _DAV _self;\n  final $Res Function(_DAV) _then;\n\n/// Create a copy of DAV\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? uri = null,Object? user = null,Object? password = null,Object? fileName = null,}) {\n  return _then(_DAV(\nuri: null == uri ? _self.uri : uri // ignore: cast_nullable_to_non_nullable\nas String,user: null == user ? _self.user : user // ignore: cast_nullable_to_non_nullable\nas String,password: null == password ? _self.password : password // ignore: cast_nullable_to_non_nullable\nas String,fileName: null == fileName ? _self.fileName : fileName // ignore: cast_nullable_to_non_nullable\nas String,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$FileInfo {\n\n int get size; DateTime get lastModified;\n/// Create a copy of FileInfo\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$FileInfoCopyWith<FileInfo> get copyWith => _$FileInfoCopyWithImpl<FileInfo>(this as FileInfo, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is FileInfo&&(identical(other.size, size) || other.size == size)&&(identical(other.lastModified, lastModified) || other.lastModified == lastModified));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,size,lastModified);\n\n@override\nString toString() {\n  return 'FileInfo(size: $size, lastModified: $lastModified)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $FileInfoCopyWith<$Res>  {\n  factory $FileInfoCopyWith(FileInfo value, $Res Function(FileInfo) _then) = _$FileInfoCopyWithImpl;\n@useResult\n$Res call({\n int size, DateTime lastModified\n});\n\n\n\n\n}\n/// @nodoc\nclass _$FileInfoCopyWithImpl<$Res>\n    implements $FileInfoCopyWith<$Res> {\n  _$FileInfoCopyWithImpl(this._self, this._then);\n\n  final FileInfo _self;\n  final $Res Function(FileInfo) _then;\n\n/// Create a copy of FileInfo\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? size = null,Object? lastModified = null,}) {\n  return _then(_self.copyWith(\nsize: null == size ? _self.size : size // ignore: cast_nullable_to_non_nullable\nas int,lastModified: null == lastModified ? _self.lastModified : lastModified // ignore: cast_nullable_to_non_nullable\nas DateTime,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [FileInfo].\nextension FileInfoPatterns on FileInfo {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _FileInfo value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _FileInfo() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _FileInfo value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _FileInfo():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _FileInfo value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _FileInfo() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( int size,  DateTime lastModified)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _FileInfo() when $default != null:\nreturn $default(_that.size,_that.lastModified);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( int size,  DateTime lastModified)  $default,) {final _that = this;\nswitch (_that) {\ncase _FileInfo():\nreturn $default(_that.size,_that.lastModified);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( int size,  DateTime lastModified)?  $default,) {final _that = this;\nswitch (_that) {\ncase _FileInfo() when $default != null:\nreturn $default(_that.size,_that.lastModified);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _FileInfo implements FileInfo {\n  const _FileInfo({required this.size, required this.lastModified});\n  \n\n@override final  int size;\n@override final  DateTime lastModified;\n\n/// Create a copy of FileInfo\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$FileInfoCopyWith<_FileInfo> get copyWith => __$FileInfoCopyWithImpl<_FileInfo>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _FileInfo&&(identical(other.size, size) || other.size == size)&&(identical(other.lastModified, lastModified) || other.lastModified == lastModified));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,size,lastModified);\n\n@override\nString toString() {\n  return 'FileInfo(size: $size, lastModified: $lastModified)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$FileInfoCopyWith<$Res> implements $FileInfoCopyWith<$Res> {\n  factory _$FileInfoCopyWith(_FileInfo value, $Res Function(_FileInfo) _then) = __$FileInfoCopyWithImpl;\n@override @useResult\n$Res call({\n int size, DateTime lastModified\n});\n\n\n\n\n}\n/// @nodoc\nclass __$FileInfoCopyWithImpl<$Res>\n    implements _$FileInfoCopyWith<$Res> {\n  __$FileInfoCopyWithImpl(this._self, this._then);\n\n  final _FileInfo _self;\n  final $Res Function(_FileInfo) _then;\n\n/// Create a copy of FileInfo\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? size = null,Object? lastModified = null,}) {\n  return _then(_FileInfo(\nsize: null == size ? _self.size : size // ignore: cast_nullable_to_non_nullable\nas int,lastModified: null == lastModified ? _self.lastModified : lastModified // ignore: cast_nullable_to_non_nullable\nas DateTime,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$VersionInfo {\n\n String get clashName; String get version;\n/// Create a copy of VersionInfo\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$VersionInfoCopyWith<VersionInfo> get copyWith => _$VersionInfoCopyWithImpl<VersionInfo>(this as VersionInfo, _$identity);\n\n  /// Serializes this VersionInfo to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is VersionInfo&&(identical(other.clashName, clashName) || other.clashName == clashName)&&(identical(other.version, version) || other.version == version));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,clashName,version);\n\n@override\nString toString() {\n  return 'VersionInfo(clashName: $clashName, version: $version)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $VersionInfoCopyWith<$Res>  {\n  factory $VersionInfoCopyWith(VersionInfo value, $Res Function(VersionInfo) _then) = _$VersionInfoCopyWithImpl;\n@useResult\n$Res call({\n String clashName, String version\n});\n\n\n\n\n}\n/// @nodoc\nclass _$VersionInfoCopyWithImpl<$Res>\n    implements $VersionInfoCopyWith<$Res> {\n  _$VersionInfoCopyWithImpl(this._self, this._then);\n\n  final VersionInfo _self;\n  final $Res Function(VersionInfo) _then;\n\n/// Create a copy of VersionInfo\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? clashName = null,Object? version = null,}) {\n  return _then(_self.copyWith(\nclashName: null == clashName ? _self.clashName : clashName // ignore: cast_nullable_to_non_nullable\nas String,version: null == version ? _self.version : version // ignore: cast_nullable_to_non_nullable\nas String,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [VersionInfo].\nextension VersionInfoPatterns on VersionInfo {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _VersionInfo value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _VersionInfo() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _VersionInfo value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _VersionInfo():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _VersionInfo value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _VersionInfo() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String clashName,  String version)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _VersionInfo() when $default != null:\nreturn $default(_that.clashName,_that.version);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String clashName,  String version)  $default,) {final _that = this;\nswitch (_that) {\ncase _VersionInfo():\nreturn $default(_that.clashName,_that.version);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String clashName,  String version)?  $default,) {final _that = this;\nswitch (_that) {\ncase _VersionInfo() when $default != null:\nreturn $default(_that.clashName,_that.version);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _VersionInfo implements VersionInfo {\n  const _VersionInfo({this.clashName = '', this.version = ''});\n  factory _VersionInfo.fromJson(Map<String, dynamic> json) => _$VersionInfoFromJson(json);\n\n@override@JsonKey() final  String clashName;\n@override@JsonKey() final  String version;\n\n/// Create a copy of VersionInfo\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$VersionInfoCopyWith<_VersionInfo> get copyWith => __$VersionInfoCopyWithImpl<_VersionInfo>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$VersionInfoToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _VersionInfo&&(identical(other.clashName, clashName) || other.clashName == clashName)&&(identical(other.version, version) || other.version == version));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,clashName,version);\n\n@override\nString toString() {\n  return 'VersionInfo(clashName: $clashName, version: $version)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$VersionInfoCopyWith<$Res> implements $VersionInfoCopyWith<$Res> {\n  factory _$VersionInfoCopyWith(_VersionInfo value, $Res Function(_VersionInfo) _then) = __$VersionInfoCopyWithImpl;\n@override @useResult\n$Res call({\n String clashName, String version\n});\n\n\n\n\n}\n/// @nodoc\nclass __$VersionInfoCopyWithImpl<$Res>\n    implements _$VersionInfoCopyWith<$Res> {\n  __$VersionInfoCopyWithImpl(this._self, this._then);\n\n  final _VersionInfo _self;\n  final $Res Function(_VersionInfo) _then;\n\n/// Create a copy of VersionInfo\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? clashName = null,Object? version = null,}) {\n  return _then(_VersionInfo(\nclashName: null == clashName ? _self.clashName : clashName // ignore: cast_nullable_to_non_nullable\nas String,version: null == version ? _self.version : version // ignore: cast_nullable_to_non_nullable\nas String,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$Proxy {\n\n String get name; String get type; String? get now;\n/// Create a copy of Proxy\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ProxyCopyWith<Proxy> get copyWith => _$ProxyCopyWithImpl<Proxy>(this as Proxy, _$identity);\n\n  /// Serializes this Proxy to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is Proxy&&(identical(other.name, name) || other.name == name)&&(identical(other.type, type) || other.type == type)&&(identical(other.now, now) || other.now == now));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,name,type,now);\n\n@override\nString toString() {\n  return 'Proxy(name: $name, type: $type, now: $now)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ProxyCopyWith<$Res>  {\n  factory $ProxyCopyWith(Proxy value, $Res Function(Proxy) _then) = _$ProxyCopyWithImpl;\n@useResult\n$Res call({\n String name, String type, String? now\n});\n\n\n\n\n}\n/// @nodoc\nclass _$ProxyCopyWithImpl<$Res>\n    implements $ProxyCopyWith<$Res> {\n  _$ProxyCopyWithImpl(this._self, this._then);\n\n  final Proxy _self;\n  final $Res Function(Proxy) _then;\n\n/// Create a copy of Proxy\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? name = null,Object? type = null,Object? now = freezed,}) {\n  return _then(_self.copyWith(\nname: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable\nas String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable\nas String,now: freezed == now ? _self.now : now // ignore: cast_nullable_to_non_nullable\nas String?,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [Proxy].\nextension ProxyPatterns on Proxy {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _Proxy value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _Proxy() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _Proxy value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Proxy():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _Proxy value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Proxy() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String name,  String type,  String? now)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _Proxy() when $default != null:\nreturn $default(_that.name,_that.type,_that.now);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String name,  String type,  String? now)  $default,) {final _that = this;\nswitch (_that) {\ncase _Proxy():\nreturn $default(_that.name,_that.type,_that.now);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String name,  String type,  String? now)?  $default,) {final _that = this;\nswitch (_that) {\ncase _Proxy() when $default != null:\nreturn $default(_that.name,_that.type,_that.now);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _Proxy implements Proxy {\n  const _Proxy({required this.name, required this.type, this.now});\n  factory _Proxy.fromJson(Map<String, dynamic> json) => _$ProxyFromJson(json);\n\n@override final  String name;\n@override final  String type;\n@override final  String? now;\n\n/// Create a copy of Proxy\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ProxyCopyWith<_Proxy> get copyWith => __$ProxyCopyWithImpl<_Proxy>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$ProxyToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _Proxy&&(identical(other.name, name) || other.name == name)&&(identical(other.type, type) || other.type == type)&&(identical(other.now, now) || other.now == now));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,name,type,now);\n\n@override\nString toString() {\n  return 'Proxy(name: $name, type: $type, now: $now)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ProxyCopyWith<$Res> implements $ProxyCopyWith<$Res> {\n  factory _$ProxyCopyWith(_Proxy value, $Res Function(_Proxy) _then) = __$ProxyCopyWithImpl;\n@override @useResult\n$Res call({\n String name, String type, String? now\n});\n\n\n\n\n}\n/// @nodoc\nclass __$ProxyCopyWithImpl<$Res>\n    implements _$ProxyCopyWith<$Res> {\n  __$ProxyCopyWithImpl(this._self, this._then);\n\n  final _Proxy _self;\n  final $Res Function(_Proxy) _then;\n\n/// Create a copy of Proxy\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? name = null,Object? type = null,Object? now = freezed,}) {\n  return _then(_Proxy(\nname: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable\nas String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable\nas String,now: freezed == now ? _self.now : now // ignore: cast_nullable_to_non_nullable\nas String?,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$Group {\n\n GroupType get type; List<Proxy> get all; String? get now; bool? get hidden; String? get testUrl; String get icon; String get name;\n/// Create a copy of Group\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$GroupCopyWith<Group> get copyWith => _$GroupCopyWithImpl<Group>(this as Group, _$identity);\n\n  /// Serializes this Group to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is Group&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other.all, all)&&(identical(other.now, now) || other.now == now)&&(identical(other.hidden, hidden) || other.hidden == hidden)&&(identical(other.testUrl, testUrl) || other.testUrl == testUrl)&&(identical(other.icon, icon) || other.icon == icon)&&(identical(other.name, name) || other.name == name));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,type,const DeepCollectionEquality().hash(all),now,hidden,testUrl,icon,name);\n\n@override\nString toString() {\n  return 'Group(type: $type, all: $all, now: $now, hidden: $hidden, testUrl: $testUrl, icon: $icon, name: $name)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $GroupCopyWith<$Res>  {\n  factory $GroupCopyWith(Group value, $Res Function(Group) _then) = _$GroupCopyWithImpl;\n@useResult\n$Res call({\n GroupType type, List<Proxy> all, String? now, bool? hidden, String? testUrl, String icon, String name\n});\n\n\n\n\n}\n/// @nodoc\nclass _$GroupCopyWithImpl<$Res>\n    implements $GroupCopyWith<$Res> {\n  _$GroupCopyWithImpl(this._self, this._then);\n\n  final Group _self;\n  final $Res Function(Group) _then;\n\n/// Create a copy of Group\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? type = null,Object? all = null,Object? now = freezed,Object? hidden = freezed,Object? testUrl = freezed,Object? icon = null,Object? name = null,}) {\n  return _then(_self.copyWith(\ntype: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable\nas GroupType,all: null == all ? _self.all : all // ignore: cast_nullable_to_non_nullable\nas List<Proxy>,now: freezed == now ? _self.now : now // ignore: cast_nullable_to_non_nullable\nas String?,hidden: freezed == hidden ? _self.hidden : hidden // ignore: cast_nullable_to_non_nullable\nas bool?,testUrl: freezed == testUrl ? _self.testUrl : testUrl // ignore: cast_nullable_to_non_nullable\nas String?,icon: null == icon ? _self.icon : icon // ignore: cast_nullable_to_non_nullable\nas String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable\nas String,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [Group].\nextension GroupPatterns on Group {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _Group value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _Group() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _Group value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Group():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _Group value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Group() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( GroupType type,  List<Proxy> all,  String? now,  bool? hidden,  String? testUrl,  String icon,  String name)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _Group() when $default != null:\nreturn $default(_that.type,_that.all,_that.now,_that.hidden,_that.testUrl,_that.icon,_that.name);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( GroupType type,  List<Proxy> all,  String? now,  bool? hidden,  String? testUrl,  String icon,  String name)  $default,) {final _that = this;\nswitch (_that) {\ncase _Group():\nreturn $default(_that.type,_that.all,_that.now,_that.hidden,_that.testUrl,_that.icon,_that.name);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( GroupType type,  List<Proxy> all,  String? now,  bool? hidden,  String? testUrl,  String icon,  String name)?  $default,) {final _that = this;\nswitch (_that) {\ncase _Group() when $default != null:\nreturn $default(_that.type,_that.all,_that.now,_that.hidden,_that.testUrl,_that.icon,_that.name);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _Group implements Group {\n  const _Group({required this.type, final  List<Proxy> all = const [], this.now, this.hidden, this.testUrl, this.icon = '', required this.name}): _all = all;\n  factory _Group.fromJson(Map<String, dynamic> json) => _$GroupFromJson(json);\n\n@override final  GroupType type;\n final  List<Proxy> _all;\n@override@JsonKey() List<Proxy> get all {\n  if (_all is EqualUnmodifiableListView) return _all;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_all);\n}\n\n@override final  String? now;\n@override final  bool? hidden;\n@override final  String? testUrl;\n@override@JsonKey() final  String icon;\n@override final  String name;\n\n/// Create a copy of Group\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$GroupCopyWith<_Group> get copyWith => __$GroupCopyWithImpl<_Group>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$GroupToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _Group&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other._all, _all)&&(identical(other.now, now) || other.now == now)&&(identical(other.hidden, hidden) || other.hidden == hidden)&&(identical(other.testUrl, testUrl) || other.testUrl == testUrl)&&(identical(other.icon, icon) || other.icon == icon)&&(identical(other.name, name) || other.name == name));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,type,const DeepCollectionEquality().hash(_all),now,hidden,testUrl,icon,name);\n\n@override\nString toString() {\n  return 'Group(type: $type, all: $all, now: $now, hidden: $hidden, testUrl: $testUrl, icon: $icon, name: $name)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$GroupCopyWith<$Res> implements $GroupCopyWith<$Res> {\n  factory _$GroupCopyWith(_Group value, $Res Function(_Group) _then) = __$GroupCopyWithImpl;\n@override @useResult\n$Res call({\n GroupType type, List<Proxy> all, String? now, bool? hidden, String? testUrl, String icon, String name\n});\n\n\n\n\n}\n/// @nodoc\nclass __$GroupCopyWithImpl<$Res>\n    implements _$GroupCopyWith<$Res> {\n  __$GroupCopyWithImpl(this._self, this._then);\n\n  final _Group _self;\n  final $Res Function(_Group) _then;\n\n/// Create a copy of Group\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? type = null,Object? all = null,Object? now = freezed,Object? hidden = freezed,Object? testUrl = freezed,Object? icon = null,Object? name = null,}) {\n  return _then(_Group(\ntype: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable\nas GroupType,all: null == all ? _self._all : all // ignore: cast_nullable_to_non_nullable\nas List<Proxy>,now: freezed == now ? _self.now : now // ignore: cast_nullable_to_non_nullable\nas String?,hidden: freezed == hidden ? _self.hidden : hidden // ignore: cast_nullable_to_non_nullable\nas bool?,testUrl: freezed == testUrl ? _self.testUrl : testUrl // ignore: cast_nullable_to_non_nullable\nas String?,icon: null == icon ? _self.icon : icon // ignore: cast_nullable_to_non_nullable\nas String,name: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable\nas String,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$ColorSchemes {\n\n ColorScheme? get lightColorScheme; ColorScheme? get darkColorScheme;\n/// Create a copy of ColorSchemes\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ColorSchemesCopyWith<ColorSchemes> get copyWith => _$ColorSchemesCopyWithImpl<ColorSchemes>(this as ColorSchemes, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is ColorSchemes&&(identical(other.lightColorScheme, lightColorScheme) || other.lightColorScheme == lightColorScheme)&&(identical(other.darkColorScheme, darkColorScheme) || other.darkColorScheme == darkColorScheme));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,lightColorScheme,darkColorScheme);\n\n@override\nString toString() {\n  return 'ColorSchemes(lightColorScheme: $lightColorScheme, darkColorScheme: $darkColorScheme)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ColorSchemesCopyWith<$Res>  {\n  factory $ColorSchemesCopyWith(ColorSchemes value, $Res Function(ColorSchemes) _then) = _$ColorSchemesCopyWithImpl;\n@useResult\n$Res call({\n ColorScheme? lightColorScheme, ColorScheme? darkColorScheme\n});\n\n\n\n\n}\n/// @nodoc\nclass _$ColorSchemesCopyWithImpl<$Res>\n    implements $ColorSchemesCopyWith<$Res> {\n  _$ColorSchemesCopyWithImpl(this._self, this._then);\n\n  final ColorSchemes _self;\n  final $Res Function(ColorSchemes) _then;\n\n/// Create a copy of ColorSchemes\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? lightColorScheme = freezed,Object? darkColorScheme = freezed,}) {\n  return _then(_self.copyWith(\nlightColorScheme: freezed == lightColorScheme ? _self.lightColorScheme : lightColorScheme // ignore: cast_nullable_to_non_nullable\nas ColorScheme?,darkColorScheme: freezed == darkColorScheme ? _self.darkColorScheme : darkColorScheme // ignore: cast_nullable_to_non_nullable\nas ColorScheme?,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [ColorSchemes].\nextension ColorSchemesPatterns on ColorSchemes {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ColorSchemes value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _ColorSchemes() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ColorSchemes value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ColorSchemes():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ColorSchemes value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ColorSchemes() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( ColorScheme? lightColorScheme,  ColorScheme? darkColorScheme)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _ColorSchemes() when $default != null:\nreturn $default(_that.lightColorScheme,_that.darkColorScheme);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( ColorScheme? lightColorScheme,  ColorScheme? darkColorScheme)  $default,) {final _that = this;\nswitch (_that) {\ncase _ColorSchemes():\nreturn $default(_that.lightColorScheme,_that.darkColorScheme);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( ColorScheme? lightColorScheme,  ColorScheme? darkColorScheme)?  $default,) {final _that = this;\nswitch (_that) {\ncase _ColorSchemes() when $default != null:\nreturn $default(_that.lightColorScheme,_that.darkColorScheme);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _ColorSchemes implements ColorSchemes {\n  const _ColorSchemes({this.lightColorScheme, this.darkColorScheme});\n  \n\n@override final  ColorScheme? lightColorScheme;\n@override final  ColorScheme? darkColorScheme;\n\n/// Create a copy of ColorSchemes\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ColorSchemesCopyWith<_ColorSchemes> get copyWith => __$ColorSchemesCopyWithImpl<_ColorSchemes>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _ColorSchemes&&(identical(other.lightColorScheme, lightColorScheme) || other.lightColorScheme == lightColorScheme)&&(identical(other.darkColorScheme, darkColorScheme) || other.darkColorScheme == darkColorScheme));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,lightColorScheme,darkColorScheme);\n\n@override\nString toString() {\n  return 'ColorSchemes(lightColorScheme: $lightColorScheme, darkColorScheme: $darkColorScheme)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ColorSchemesCopyWith<$Res> implements $ColorSchemesCopyWith<$Res> {\n  factory _$ColorSchemesCopyWith(_ColorSchemes value, $Res Function(_ColorSchemes) _then) = __$ColorSchemesCopyWithImpl;\n@override @useResult\n$Res call({\n ColorScheme? lightColorScheme, ColorScheme? darkColorScheme\n});\n\n\n\n\n}\n/// @nodoc\nclass __$ColorSchemesCopyWithImpl<$Res>\n    implements _$ColorSchemesCopyWith<$Res> {\n  __$ColorSchemesCopyWithImpl(this._self, this._then);\n\n  final _ColorSchemes _self;\n  final $Res Function(_ColorSchemes) _then;\n\n/// Create a copy of ColorSchemes\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? lightColorScheme = freezed,Object? darkColorScheme = freezed,}) {\n  return _then(_ColorSchemes(\nlightColorScheme: freezed == lightColorScheme ? _self.lightColorScheme : lightColorScheme // ignore: cast_nullable_to_non_nullable\nas ColorScheme?,darkColorScheme: freezed == darkColorScheme ? _self.darkColorScheme : darkColorScheme // ignore: cast_nullable_to_non_nullable\nas ColorScheme?,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$HotKeyAction {\n\n HotAction get action; int? get key; Set<KeyboardModifier> get modifiers;\n/// Create a copy of HotKeyAction\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$HotKeyActionCopyWith<HotKeyAction> get copyWith => _$HotKeyActionCopyWithImpl<HotKeyAction>(this as HotKeyAction, _$identity);\n\n  /// Serializes this HotKeyAction to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is HotKeyAction&&(identical(other.action, action) || other.action == action)&&(identical(other.key, key) || other.key == key)&&const DeepCollectionEquality().equals(other.modifiers, modifiers));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,action,key,const DeepCollectionEquality().hash(modifiers));\n\n@override\nString toString() {\n  return 'HotKeyAction(action: $action, key: $key, modifiers: $modifiers)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $HotKeyActionCopyWith<$Res>  {\n  factory $HotKeyActionCopyWith(HotKeyAction value, $Res Function(HotKeyAction) _then) = _$HotKeyActionCopyWithImpl;\n@useResult\n$Res call({\n HotAction action, int? key, Set<KeyboardModifier> modifiers\n});\n\n\n\n\n}\n/// @nodoc\nclass _$HotKeyActionCopyWithImpl<$Res>\n    implements $HotKeyActionCopyWith<$Res> {\n  _$HotKeyActionCopyWithImpl(this._self, this._then);\n\n  final HotKeyAction _self;\n  final $Res Function(HotKeyAction) _then;\n\n/// Create a copy of HotKeyAction\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? action = null,Object? key = freezed,Object? modifiers = null,}) {\n  return _then(_self.copyWith(\naction: null == action ? _self.action : action // ignore: cast_nullable_to_non_nullable\nas HotAction,key: freezed == key ? _self.key : key // ignore: cast_nullable_to_non_nullable\nas int?,modifiers: null == modifiers ? _self.modifiers : modifiers // ignore: cast_nullable_to_non_nullable\nas Set<KeyboardModifier>,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [HotKeyAction].\nextension HotKeyActionPatterns on HotKeyAction {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _HotKeyAction value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _HotKeyAction() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _HotKeyAction value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _HotKeyAction():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _HotKeyAction value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _HotKeyAction() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( HotAction action,  int? key,  Set<KeyboardModifier> modifiers)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _HotKeyAction() when $default != null:\nreturn $default(_that.action,_that.key,_that.modifiers);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( HotAction action,  int? key,  Set<KeyboardModifier> modifiers)  $default,) {final _that = this;\nswitch (_that) {\ncase _HotKeyAction():\nreturn $default(_that.action,_that.key,_that.modifiers);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( HotAction action,  int? key,  Set<KeyboardModifier> modifiers)?  $default,) {final _that = this;\nswitch (_that) {\ncase _HotKeyAction() when $default != null:\nreturn $default(_that.action,_that.key,_that.modifiers);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _HotKeyAction implements HotKeyAction {\n  const _HotKeyAction({required this.action, this.key, final  Set<KeyboardModifier> modifiers = const {}}): _modifiers = modifiers;\n  factory _HotKeyAction.fromJson(Map<String, dynamic> json) => _$HotKeyActionFromJson(json);\n\n@override final  HotAction action;\n@override final  int? key;\n final  Set<KeyboardModifier> _modifiers;\n@override@JsonKey() Set<KeyboardModifier> get modifiers {\n  if (_modifiers is EqualUnmodifiableSetView) return _modifiers;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableSetView(_modifiers);\n}\n\n\n/// Create a copy of HotKeyAction\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$HotKeyActionCopyWith<_HotKeyAction> get copyWith => __$HotKeyActionCopyWithImpl<_HotKeyAction>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$HotKeyActionToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _HotKeyAction&&(identical(other.action, action) || other.action == action)&&(identical(other.key, key) || other.key == key)&&const DeepCollectionEquality().equals(other._modifiers, _modifiers));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,action,key,const DeepCollectionEquality().hash(_modifiers));\n\n@override\nString toString() {\n  return 'HotKeyAction(action: $action, key: $key, modifiers: $modifiers)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$HotKeyActionCopyWith<$Res> implements $HotKeyActionCopyWith<$Res> {\n  factory _$HotKeyActionCopyWith(_HotKeyAction value, $Res Function(_HotKeyAction) _then) = __$HotKeyActionCopyWithImpl;\n@override @useResult\n$Res call({\n HotAction action, int? key, Set<KeyboardModifier> modifiers\n});\n\n\n\n\n}\n/// @nodoc\nclass __$HotKeyActionCopyWithImpl<$Res>\n    implements _$HotKeyActionCopyWith<$Res> {\n  __$HotKeyActionCopyWithImpl(this._self, this._then);\n\n  final _HotKeyAction _self;\n  final $Res Function(_HotKeyAction) _then;\n\n/// Create a copy of HotKeyAction\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? action = null,Object? key = freezed,Object? modifiers = null,}) {\n  return _then(_HotKeyAction(\naction: null == action ? _self.action : action // ignore: cast_nullable_to_non_nullable\nas HotAction,key: freezed == key ? _self.key : key // ignore: cast_nullable_to_non_nullable\nas int?,modifiers: null == modifiers ? _self._modifiers : modifiers // ignore: cast_nullable_to_non_nullable\nas Set<KeyboardModifier>,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$Field {\n\n String get label; String get value; Validator? get validator;\n/// Create a copy of Field\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$FieldCopyWith<Field> get copyWith => _$FieldCopyWithImpl<Field>(this as Field, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is Field&&(identical(other.label, label) || other.label == label)&&(identical(other.value, value) || other.value == value)&&(identical(other.validator, validator) || other.validator == validator));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,label,value,validator);\n\n@override\nString toString() {\n  return 'Field(label: $label, value: $value, validator: $validator)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $FieldCopyWith<$Res>  {\n  factory $FieldCopyWith(Field value, $Res Function(Field) _then) = _$FieldCopyWithImpl;\n@useResult\n$Res call({\n String label, String value, Validator? validator\n});\n\n\n\n\n}\n/// @nodoc\nclass _$FieldCopyWithImpl<$Res>\n    implements $FieldCopyWith<$Res> {\n  _$FieldCopyWithImpl(this._self, this._then);\n\n  final Field _self;\n  final $Res Function(Field) _then;\n\n/// Create a copy of Field\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? label = null,Object? value = null,Object? validator = freezed,}) {\n  return _then(_self.copyWith(\nlabel: null == label ? _self.label : label // ignore: cast_nullable_to_non_nullable\nas String,value: null == value ? _self.value : value // ignore: cast_nullable_to_non_nullable\nas String,validator: freezed == validator ? _self.validator : validator // ignore: cast_nullable_to_non_nullable\nas Validator?,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [Field].\nextension FieldPatterns on Field {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _Field value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _Field() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _Field value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Field():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _Field value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Field() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String label,  String value,  Validator? validator)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _Field() when $default != null:\nreturn $default(_that.label,_that.value,_that.validator);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String label,  String value,  Validator? validator)  $default,) {final _that = this;\nswitch (_that) {\ncase _Field():\nreturn $default(_that.label,_that.value,_that.validator);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String label,  String value,  Validator? validator)?  $default,) {final _that = this;\nswitch (_that) {\ncase _Field() when $default != null:\nreturn $default(_that.label,_that.value,_that.validator);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _Field implements Field {\n  const _Field({required this.label, required this.value, this.validator});\n  \n\n@override final  String label;\n@override final  String value;\n@override final  Validator? validator;\n\n/// Create a copy of Field\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$FieldCopyWith<_Field> get copyWith => __$FieldCopyWithImpl<_Field>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _Field&&(identical(other.label, label) || other.label == label)&&(identical(other.value, value) || other.value == value)&&(identical(other.validator, validator) || other.validator == validator));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,label,value,validator);\n\n@override\nString toString() {\n  return 'Field(label: $label, value: $value, validator: $validator)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$FieldCopyWith<$Res> implements $FieldCopyWith<$Res> {\n  factory _$FieldCopyWith(_Field value, $Res Function(_Field) _then) = __$FieldCopyWithImpl;\n@override @useResult\n$Res call({\n String label, String value, Validator? validator\n});\n\n\n\n\n}\n/// @nodoc\nclass __$FieldCopyWithImpl<$Res>\n    implements _$FieldCopyWith<$Res> {\n  __$FieldCopyWithImpl(this._self, this._then);\n\n  final _Field _self;\n  final $Res Function(_Field) _then;\n\n/// Create a copy of Field\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? label = null,Object? value = null,Object? validator = freezed,}) {\n  return _then(_Field(\nlabel: null == label ? _self.label : label // ignore: cast_nullable_to_non_nullable\nas String,value: null == value ? _self.value : value // ignore: cast_nullable_to_non_nullable\nas String,validator: freezed == validator ? _self.validator : validator // ignore: cast_nullable_to_non_nullable\nas Validator?,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$TextPainterParams {\n\n String? get text; double? get fontSize; double get textScaleFactor; double get maxWidth; int? get maxLines;\n/// Create a copy of TextPainterParams\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$TextPainterParamsCopyWith<TextPainterParams> get copyWith => _$TextPainterParamsCopyWithImpl<TextPainterParams>(this as TextPainterParams, _$identity);\n\n  /// Serializes this TextPainterParams to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is TextPainterParams&&(identical(other.text, text) || other.text == text)&&(identical(other.fontSize, fontSize) || other.fontSize == fontSize)&&(identical(other.textScaleFactor, textScaleFactor) || other.textScaleFactor == textScaleFactor)&&(identical(other.maxWidth, maxWidth) || other.maxWidth == maxWidth)&&(identical(other.maxLines, maxLines) || other.maxLines == maxLines));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,text,fontSize,textScaleFactor,maxWidth,maxLines);\n\n@override\nString toString() {\n  return 'TextPainterParams(text: $text, fontSize: $fontSize, textScaleFactor: $textScaleFactor, maxWidth: $maxWidth, maxLines: $maxLines)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $TextPainterParamsCopyWith<$Res>  {\n  factory $TextPainterParamsCopyWith(TextPainterParams value, $Res Function(TextPainterParams) _then) = _$TextPainterParamsCopyWithImpl;\n@useResult\n$Res call({\n String? text, double? fontSize, double textScaleFactor, double maxWidth, int? maxLines\n});\n\n\n\n\n}\n/// @nodoc\nclass _$TextPainterParamsCopyWithImpl<$Res>\n    implements $TextPainterParamsCopyWith<$Res> {\n  _$TextPainterParamsCopyWithImpl(this._self, this._then);\n\n  final TextPainterParams _self;\n  final $Res Function(TextPainterParams) _then;\n\n/// Create a copy of TextPainterParams\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? text = freezed,Object? fontSize = freezed,Object? textScaleFactor = null,Object? maxWidth = null,Object? maxLines = freezed,}) {\n  return _then(_self.copyWith(\ntext: freezed == text ? _self.text : text // ignore: cast_nullable_to_non_nullable\nas String?,fontSize: freezed == fontSize ? _self.fontSize : fontSize // ignore: cast_nullable_to_non_nullable\nas double?,textScaleFactor: null == textScaleFactor ? _self.textScaleFactor : textScaleFactor // ignore: cast_nullable_to_non_nullable\nas double,maxWidth: null == maxWidth ? _self.maxWidth : maxWidth // ignore: cast_nullable_to_non_nullable\nas double,maxLines: freezed == maxLines ? _self.maxLines : maxLines // ignore: cast_nullable_to_non_nullable\nas int?,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [TextPainterParams].\nextension TextPainterParamsPatterns on TextPainterParams {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _TextPainterParams value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _TextPainterParams() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _TextPainterParams value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _TextPainterParams():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _TextPainterParams value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _TextPainterParams() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String? text,  double? fontSize,  double textScaleFactor,  double maxWidth,  int? maxLines)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _TextPainterParams() when $default != null:\nreturn $default(_that.text,_that.fontSize,_that.textScaleFactor,_that.maxWidth,_that.maxLines);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String? text,  double? fontSize,  double textScaleFactor,  double maxWidth,  int? maxLines)  $default,) {final _that = this;\nswitch (_that) {\ncase _TextPainterParams():\nreturn $default(_that.text,_that.fontSize,_that.textScaleFactor,_that.maxWidth,_that.maxLines);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String? text,  double? fontSize,  double textScaleFactor,  double maxWidth,  int? maxLines)?  $default,) {final _that = this;\nswitch (_that) {\ncase _TextPainterParams() when $default != null:\nreturn $default(_that.text,_that.fontSize,_that.textScaleFactor,_that.maxWidth,_that.maxLines);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _TextPainterParams implements TextPainterParams {\n  const _TextPainterParams({required this.text, required this.fontSize, required this.textScaleFactor, this.maxWidth = double.infinity, this.maxLines});\n  factory _TextPainterParams.fromJson(Map<String, dynamic> json) => _$TextPainterParamsFromJson(json);\n\n@override final  String? text;\n@override final  double? fontSize;\n@override final  double textScaleFactor;\n@override@JsonKey() final  double maxWidth;\n@override final  int? maxLines;\n\n/// Create a copy of TextPainterParams\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$TextPainterParamsCopyWith<_TextPainterParams> get copyWith => __$TextPainterParamsCopyWithImpl<_TextPainterParams>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$TextPainterParamsToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _TextPainterParams&&(identical(other.text, text) || other.text == text)&&(identical(other.fontSize, fontSize) || other.fontSize == fontSize)&&(identical(other.textScaleFactor, textScaleFactor) || other.textScaleFactor == textScaleFactor)&&(identical(other.maxWidth, maxWidth) || other.maxWidth == maxWidth)&&(identical(other.maxLines, maxLines) || other.maxLines == maxLines));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,text,fontSize,textScaleFactor,maxWidth,maxLines);\n\n@override\nString toString() {\n  return 'TextPainterParams(text: $text, fontSize: $fontSize, textScaleFactor: $textScaleFactor, maxWidth: $maxWidth, maxLines: $maxLines)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$TextPainterParamsCopyWith<$Res> implements $TextPainterParamsCopyWith<$Res> {\n  factory _$TextPainterParamsCopyWith(_TextPainterParams value, $Res Function(_TextPainterParams) _then) = __$TextPainterParamsCopyWithImpl;\n@override @useResult\n$Res call({\n String? text, double? fontSize, double textScaleFactor, double maxWidth, int? maxLines\n});\n\n\n\n\n}\n/// @nodoc\nclass __$TextPainterParamsCopyWithImpl<$Res>\n    implements _$TextPainterParamsCopyWith<$Res> {\n  __$TextPainterParamsCopyWithImpl(this._self, this._then);\n\n  final _TextPainterParams _self;\n  final $Res Function(_TextPainterParams) _then;\n\n/// Create a copy of TextPainterParams\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? text = freezed,Object? fontSize = freezed,Object? textScaleFactor = null,Object? maxWidth = null,Object? maxLines = freezed,}) {\n  return _then(_TextPainterParams(\ntext: freezed == text ? _self.text : text // ignore: cast_nullable_to_non_nullable\nas String?,fontSize: freezed == fontSize ? _self.fontSize : fontSize // ignore: cast_nullable_to_non_nullable\nas double?,textScaleFactor: null == textScaleFactor ? _self.textScaleFactor : textScaleFactor // ignore: cast_nullable_to_non_nullable\nas double,maxWidth: null == maxWidth ? _self.maxWidth : maxWidth // ignore: cast_nullable_to_non_nullable\nas double,maxLines: freezed == maxLines ? _self.maxLines : maxLines // ignore: cast_nullable_to_non_nullable\nas int?,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$Result<T> {\n\n T? get data; ResultType get type; String get message; bool get needRestart;\n/// Create a copy of Result\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ResultCopyWith<T, Result<T>> get copyWith => _$ResultCopyWithImpl<T, Result<T>>(this as Result<T>, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is Result<T>&&const DeepCollectionEquality().equals(other.data, data)&&(identical(other.type, type) || other.type == type)&&(identical(other.message, message) || other.message == message)&&(identical(other.needRestart, needRestart) || other.needRestart == needRestart));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(data),type,message,needRestart);\n\n@override\nString toString() {\n  return 'Result<$T>(data: $data, type: $type, message: $message, needRestart: $needRestart)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ResultCopyWith<T,$Res>  {\n  factory $ResultCopyWith(Result<T> value, $Res Function(Result<T>) _then) = _$ResultCopyWithImpl;\n@useResult\n$Res call({\n T? data, ResultType type, String message, bool needRestart\n});\n\n\n\n\n}\n/// @nodoc\nclass _$ResultCopyWithImpl<T,$Res>\n    implements $ResultCopyWith<T, $Res> {\n  _$ResultCopyWithImpl(this._self, this._then);\n\n  final Result<T> _self;\n  final $Res Function(Result<T>) _then;\n\n/// Create a copy of Result\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? data = freezed,Object? type = null,Object? message = null,Object? needRestart = null,}) {\n  return _then(_self.copyWith(\ndata: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable\nas T?,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable\nas ResultType,message: null == message ? _self.message : message // ignore: cast_nullable_to_non_nullable\nas String,needRestart: null == needRestart ? _self.needRestart : needRestart // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [Result].\nextension ResultPatterns<T> on Result<T> {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _Result<T> value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _Result() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _Result<T> value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Result():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _Result<T> value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Result() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( T? data,  ResultType type,  String message,  bool needRestart)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _Result() when $default != null:\nreturn $default(_that.data,_that.type,_that.message,_that.needRestart);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( T? data,  ResultType type,  String message,  bool needRestart)  $default,) {final _that = this;\nswitch (_that) {\ncase _Result():\nreturn $default(_that.data,_that.type,_that.message,_that.needRestart);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( T? data,  ResultType type,  String message,  bool needRestart)?  $default,) {final _that = this;\nswitch (_that) {\ncase _Result() when $default != null:\nreturn $default(_that.data,_that.type,_that.message,_that.needRestart);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _Result<T> implements Result<T> {\n  const _Result({required this.data, required this.type, required this.message, this.needRestart = false});\n  \n\n@override final  T? data;\n@override final  ResultType type;\n@override final  String message;\n@override@JsonKey() final  bool needRestart;\n\n/// Create a copy of Result\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ResultCopyWith<T, _Result<T>> get copyWith => __$ResultCopyWithImpl<T, _Result<T>>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _Result<T>&&const DeepCollectionEquality().equals(other.data, data)&&(identical(other.type, type) || other.type == type)&&(identical(other.message, message) || other.message == message)&&(identical(other.needRestart, needRestart) || other.needRestart == needRestart));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(data),type,message,needRestart);\n\n@override\nString toString() {\n  return 'Result<$T>(data: $data, type: $type, message: $message, needRestart: $needRestart)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ResultCopyWith<T,$Res> implements $ResultCopyWith<T, $Res> {\n  factory _$ResultCopyWith(_Result<T> value, $Res Function(_Result<T>) _then) = __$ResultCopyWithImpl;\n@override @useResult\n$Res call({\n T? data, ResultType type, String message, bool needRestart\n});\n\n\n\n\n}\n/// @nodoc\nclass __$ResultCopyWithImpl<T,$Res>\n    implements _$ResultCopyWith<T, $Res> {\n  __$ResultCopyWithImpl(this._self, this._then);\n\n  final _Result<T> _self;\n  final $Res Function(_Result<T>) _then;\n\n/// Create a copy of Result\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? data = freezed,Object? type = null,Object? message = null,Object? needRestart = null,}) {\n  return _then(_Result<T>(\ndata: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable\nas T?,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable\nas ResultType,message: null == message ? _self.message : message // ignore: cast_nullable_to_non_nullable\nas String,needRestart: null == needRestart ? _self.needRestart : needRestart // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$Script {\n\n String get id; String get label; String get content; String? get url;\n/// Create a copy of Script\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ScriptCopyWith<Script> get copyWith => _$ScriptCopyWithImpl<Script>(this as Script, _$identity);\n\n  /// Serializes this Script to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is Script&&(identical(other.id, id) || other.id == id)&&(identical(other.label, label) || other.label == label)&&(identical(other.content, content) || other.content == content)&&(identical(other.url, url) || other.url == url));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,id,label,content,url);\n\n@override\nString toString() {\n  return 'Script(id: $id, label: $label, content: $content, url: $url)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ScriptCopyWith<$Res>  {\n  factory $ScriptCopyWith(Script value, $Res Function(Script) _then) = _$ScriptCopyWithImpl;\n@useResult\n$Res call({\n String id, String label, String content, String? url\n});\n\n\n\n\n}\n/// @nodoc\nclass _$ScriptCopyWithImpl<$Res>\n    implements $ScriptCopyWith<$Res> {\n  _$ScriptCopyWithImpl(this._self, this._then);\n\n  final Script _self;\n  final $Res Function(Script) _then;\n\n/// Create a copy of Script\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? label = null,Object? content = null,Object? url = freezed,}) {\n  return _then(_self.copyWith(\nid: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable\nas String,label: null == label ? _self.label : label // ignore: cast_nullable_to_non_nullable\nas String,content: null == content ? _self.content : content // ignore: cast_nullable_to_non_nullable\nas String,url: freezed == url ? _self.url : url // ignore: cast_nullable_to_non_nullable\nas String?,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [Script].\nextension ScriptPatterns on Script {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _Script value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _Script() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _Script value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Script():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _Script value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Script() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id,  String label,  String content,  String? url)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _Script() when $default != null:\nreturn $default(_that.id,_that.label,_that.content,_that.url);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id,  String label,  String content,  String? url)  $default,) {final _that = this;\nswitch (_that) {\ncase _Script():\nreturn $default(_that.id,_that.label,_that.content,_that.url);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id,  String label,  String content,  String? url)?  $default,) {final _that = this;\nswitch (_that) {\ncase _Script() when $default != null:\nreturn $default(_that.id,_that.label,_that.content,_that.url);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _Script implements Script {\n  const _Script({required this.id, required this.label, required this.content, this.url});\n  factory _Script.fromJson(Map<String, dynamic> json) => _$ScriptFromJson(json);\n\n@override final  String id;\n@override final  String label;\n@override final  String content;\n@override final  String? url;\n\n/// Create a copy of Script\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ScriptCopyWith<_Script> get copyWith => __$ScriptCopyWithImpl<_Script>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$ScriptToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _Script&&(identical(other.id, id) || other.id == id)&&(identical(other.label, label) || other.label == label)&&(identical(other.content, content) || other.content == content)&&(identical(other.url, url) || other.url == url));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,id,label,content,url);\n\n@override\nString toString() {\n  return 'Script(id: $id, label: $label, content: $content, url: $url)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ScriptCopyWith<$Res> implements $ScriptCopyWith<$Res> {\n  factory _$ScriptCopyWith(_Script value, $Res Function(_Script) _then) = __$ScriptCopyWithImpl;\n@override @useResult\n$Res call({\n String id, String label, String content, String? url\n});\n\n\n\n\n}\n/// @nodoc\nclass __$ScriptCopyWithImpl<$Res>\n    implements _$ScriptCopyWith<$Res> {\n  __$ScriptCopyWithImpl(this._self, this._then);\n\n  final _Script _self;\n  final $Res Function(_Script) _then;\n\n/// Create a copy of Script\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? label = null,Object? content = null,Object? url = freezed,}) {\n  return _then(_Script(\nid: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable\nas String,label: null == label ? _self.label : label // ignore: cast_nullable_to_non_nullable\nas String,content: null == content ? _self.content : content // ignore: cast_nullable_to_non_nullable\nas String,url: freezed == url ? _self.url : url // ignore: cast_nullable_to_non_nullable\nas String?,\n  ));\n}\n\n\n}\n\n// dart format on\n"
  },
  {
    "path": "lib/models/generated/common.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of '../common.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_Package _$PackageFromJson(Map<String, dynamic> json) => _Package(\n  packageName: json['packageName'] as String,\n  label: json['label'] as String,\n  system: json['system'] as bool,\n  internet: json['internet'] as bool,\n  lastUpdateTime: (json['lastUpdateTime'] as num).toInt(),\n);\n\nMap<String, dynamic> _$PackageToJson(_Package instance) => <String, dynamic>{\n  'packageName': instance.packageName,\n  'label': instance.label,\n  'system': instance.system,\n  'internet': instance.internet,\n  'lastUpdateTime': instance.lastUpdateTime,\n};\n\n_Metadata _$MetadataFromJson(Map<String, dynamic> json) => _Metadata(\n  uid: (json['uid'] as num?)?.toInt() ?? 0,\n  network: json['network'] as String? ?? '',\n  sourceIP: json['sourceIP'] as String? ?? '',\n  sourcePort: json['sourcePort'] as String? ?? '',\n  destinationIP: json['destinationIP'] as String? ?? '',\n  destinationPort: json['destinationPort'] as String? ?? '',\n  host: json['host'] as String? ?? '',\n  dnsMode: $enumDecodeNullable(_$DnsModeEnumMap, json['dnsMode']),\n  process: json['process'] as String? ?? '',\n  processPath: json['processPath'] as String? ?? '',\n  remoteDestination: json['remoteDestination'] as String? ?? '',\n  sourceGeoIP:\n      (json['sourceGeoIP'] as List<dynamic>?)\n          ?.map((e) => e as String)\n          .toList() ??\n      const [],\n  destinationGeoIP:\n      (json['destinationGeoIP'] as List<dynamic>?)\n          ?.map((e) => e as String)\n          .toList() ??\n      const [],\n  destinationIPASN: json['destinationIPASN'] as String? ?? '',\n  sourceIPASN: json['sourceIPASN'] as String? ?? '',\n  specialRules: json['specialRules'] as String? ?? '',\n  specialProxy: json['specialProxy'] as String? ?? '',\n);\n\nMap<String, dynamic> _$MetadataToJson(_Metadata instance) => <String, dynamic>{\n  'uid': instance.uid,\n  'network': instance.network,\n  'sourceIP': instance.sourceIP,\n  'sourcePort': instance.sourcePort,\n  'destinationIP': instance.destinationIP,\n  'destinationPort': instance.destinationPort,\n  'host': instance.host,\n  'dnsMode': _$DnsModeEnumMap[instance.dnsMode],\n  'process': instance.process,\n  'processPath': instance.processPath,\n  'remoteDestination': instance.remoteDestination,\n  'sourceGeoIP': instance.sourceGeoIP,\n  'destinationGeoIP': instance.destinationGeoIP,\n  'destinationIPASN': instance.destinationIPASN,\n  'sourceIPASN': instance.sourceIPASN,\n  'specialRules': instance.specialRules,\n  'specialProxy': instance.specialProxy,\n};\n\nconst _$DnsModeEnumMap = {\n  DnsMode.normal: 'normal',\n  DnsMode.fakeIp: 'fake-ip',\n  DnsMode.redirHost: 'redir-host',\n  DnsMode.hosts: 'hosts',\n};\n\n_TrackerInfo _$TrackerInfoFromJson(Map<String, dynamic> json) => _TrackerInfo(\n  id: json['id'] as String,\n  upload: (json['upload'] as num?)?.toInt() ?? 0,\n  download: (json['download'] as num?)?.toInt() ?? 0,\n  start: DateTime.parse(json['start'] as String),\n  metadata: Metadata.fromJson(json['metadata'] as Map<String, dynamic>),\n  chains: (json['chains'] as List<dynamic>).map((e) => e as String).toList(),\n  rule: json['rule'] as String,\n  rulePayload: json['rulePayload'] as String,\n  downloadSpeed: (json['downloadSpeed'] as num?)?.toInt(),\n  uploadSpeed: (json['uploadSpeed'] as num?)?.toInt(),\n);\n\nMap<String, dynamic> _$TrackerInfoToJson(_TrackerInfo instance) =>\n    <String, dynamic>{\n      'id': instance.id,\n      'upload': instance.upload,\n      'download': instance.download,\n      'start': instance.start.toIso8601String(),\n      'metadata': instance.metadata,\n      'chains': instance.chains,\n      'rule': instance.rule,\n      'rulePayload': instance.rulePayload,\n      'downloadSpeed': instance.downloadSpeed,\n      'uploadSpeed': instance.uploadSpeed,\n    };\n\n_Log _$LogFromJson(Map<String, dynamic> json) => _Log(\n  logLevel:\n      $enumDecodeNullable(_$LogLevelEnumMap, json['LogLevel']) ??\n      LogLevel.error,\n  payload: json['Payload'] as String? ?? '',\n  dateTime: _logDateTime(json['dateTime']),\n);\n\nMap<String, dynamic> _$LogToJson(_Log instance) => <String, dynamic>{\n  'LogLevel': _$LogLevelEnumMap[instance.logLevel]!,\n  'Payload': instance.payload,\n  'dateTime': instance.dateTime,\n};\n\nconst _$LogLevelEnumMap = {\n  LogLevel.debug: 'debug',\n  LogLevel.info: 'info',\n  LogLevel.warning: 'warning',\n  LogLevel.error: 'error',\n  LogLevel.silent: 'silent',\n};\n\n_DAV _$DAVFromJson(Map<String, dynamic> json) => _DAV(\n  uri: json['uri'] as String,\n  user: json['user'] as String,\n  password: json['password'] as String,\n  fileName: json['fileName'] as String? ?? defaultDavFileName,\n);\n\nMap<String, dynamic> _$DAVToJson(_DAV instance) => <String, dynamic>{\n  'uri': instance.uri,\n  'user': instance.user,\n  'password': instance.password,\n  'fileName': instance.fileName,\n};\n\n_VersionInfo _$VersionInfoFromJson(Map<String, dynamic> json) => _VersionInfo(\n  clashName: json['clashName'] as String? ?? '',\n  version: json['version'] as String? ?? '',\n);\n\nMap<String, dynamic> _$VersionInfoToJson(_VersionInfo instance) =>\n    <String, dynamic>{\n      'clashName': instance.clashName,\n      'version': instance.version,\n    };\n\n_Proxy _$ProxyFromJson(Map<String, dynamic> json) => _Proxy(\n  name: json['name'] as String,\n  type: json['type'] as String,\n  now: json['now'] as String?,\n);\n\nMap<String, dynamic> _$ProxyToJson(_Proxy instance) => <String, dynamic>{\n  'name': instance.name,\n  'type': instance.type,\n  'now': instance.now,\n};\n\n_Group _$GroupFromJson(Map<String, dynamic> json) => _Group(\n  type: $enumDecode(_$GroupTypeEnumMap, json['type']),\n  all:\n      (json['all'] as List<dynamic>?)\n          ?.map((e) => Proxy.fromJson(e as Map<String, dynamic>))\n          .toList() ??\n      const [],\n  now: json['now'] as String?,\n  hidden: json['hidden'] as bool?,\n  testUrl: json['testUrl'] as String?,\n  icon: json['icon'] as String? ?? '',\n  name: json['name'] as String,\n);\n\nMap<String, dynamic> _$GroupToJson(_Group instance) => <String, dynamic>{\n  'type': _$GroupTypeEnumMap[instance.type]!,\n  'all': instance.all,\n  'now': instance.now,\n  'hidden': instance.hidden,\n  'testUrl': instance.testUrl,\n  'icon': instance.icon,\n  'name': instance.name,\n};\n\nconst _$GroupTypeEnumMap = {\n  GroupType.Selector: 'Selector',\n  GroupType.URLTest: 'URLTest',\n  GroupType.Fallback: 'Fallback',\n  GroupType.LoadBalance: 'LoadBalance',\n};\n\n_HotKeyAction _$HotKeyActionFromJson(Map<String, dynamic> json) =>\n    _HotKeyAction(\n      action: $enumDecode(_$HotActionEnumMap, json['action']),\n      key: (json['key'] as num?)?.toInt(),\n      modifiers:\n          (json['modifiers'] as List<dynamic>?)\n              ?.map((e) => $enumDecode(_$KeyboardModifierEnumMap, e))\n              .toSet() ??\n          const {},\n    );\n\nMap<String, dynamic> _$HotKeyActionToJson(_HotKeyAction instance) =>\n    <String, dynamic>{\n      'action': _$HotActionEnumMap[instance.action]!,\n      'key': instance.key,\n      'modifiers': instance.modifiers\n          .map((e) => _$KeyboardModifierEnumMap[e]!)\n          .toList(),\n    };\n\nconst _$HotActionEnumMap = {\n  HotAction.start: 'start',\n  HotAction.view: 'view',\n  HotAction.mode: 'mode',\n  HotAction.proxy: 'proxy',\n  HotAction.tun: 'tun',\n};\n\nconst _$KeyboardModifierEnumMap = {\n  KeyboardModifier.alt: 'alt',\n  KeyboardModifier.capsLock: 'capsLock',\n  KeyboardModifier.control: 'control',\n  KeyboardModifier.fn: 'fn',\n  KeyboardModifier.meta: 'meta',\n  KeyboardModifier.shift: 'shift',\n};\n\n_TextPainterParams _$TextPainterParamsFromJson(Map<String, dynamic> json) =>\n    _TextPainterParams(\n      text: json['text'] as String?,\n      fontSize: (json['fontSize'] as num?)?.toDouble(),\n      textScaleFactor: (json['textScaleFactor'] as num).toDouble(),\n      maxWidth: (json['maxWidth'] as num?)?.toDouble() ?? double.infinity,\n      maxLines: (json['maxLines'] as num?)?.toInt(),\n    );\n\nMap<String, dynamic> _$TextPainterParamsToJson(_TextPainterParams instance) =>\n    <String, dynamic>{\n      'text': instance.text,\n      'fontSize': instance.fontSize,\n      'textScaleFactor': instance.textScaleFactor,\n      'maxWidth': instance.maxWidth,\n      'maxLines': instance.maxLines,\n    };\n\n_Script _$ScriptFromJson(Map<String, dynamic> json) => _Script(\n  id: json['id'] as String,\n  label: json['label'] as String,\n  content: json['content'] as String,\n  url: json['url'] as String?,\n);\n\nMap<String, dynamic> _$ScriptToJson(_Script instance) => <String, dynamic>{\n  'id': instance.id,\n  'label': instance.label,\n  'content': instance.content,\n  'url': instance.url,\n};\n"
  },
  {
    "path": "lib/models/generated/config.freezed.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n// coverage:ignore-file\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of '../config.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\n// dart format off\nT _$identity<T>(T value) => value;\n\n/// @nodoc\nmixin _$AppSettingProps {\n\n String? get locale;@JsonKey(fromJson: dashboardWidgetsSafeFormJson) List<DashboardWidget> get dashboardWidgets; bool get onlyStatisticsProxy; bool get autoLaunch; bool get silentLaunch; bool get smartDelayLaunch; bool get autoRun; bool get openLogs; bool get closeConnections; String get testUrl; bool get isAnimateToPage; bool get enableNavBarHapticFeedback; bool get autoCheckUpdate; bool get showLabel; bool get disclaimerAccepted; bool get minimizeOnExit; bool get hidden; bool get developerMode; bool get enableHighRefreshRate; RecoveryStrategy get recoveryStrategy;\n/// Create a copy of AppSettingProps\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$AppSettingPropsCopyWith<AppSettingProps> get copyWith => _$AppSettingPropsCopyWithImpl<AppSettingProps>(this as AppSettingProps, _$identity);\n\n  /// Serializes this AppSettingProps to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is AppSettingProps&&(identical(other.locale, locale) || other.locale == locale)&&const DeepCollectionEquality().equals(other.dashboardWidgets, dashboardWidgets)&&(identical(other.onlyStatisticsProxy, onlyStatisticsProxy) || other.onlyStatisticsProxy == onlyStatisticsProxy)&&(identical(other.autoLaunch, autoLaunch) || other.autoLaunch == autoLaunch)&&(identical(other.silentLaunch, silentLaunch) || other.silentLaunch == silentLaunch)&&(identical(other.smartDelayLaunch, smartDelayLaunch) || other.smartDelayLaunch == smartDelayLaunch)&&(identical(other.autoRun, autoRun) || other.autoRun == autoRun)&&(identical(other.openLogs, openLogs) || other.openLogs == openLogs)&&(identical(other.closeConnections, closeConnections) || other.closeConnections == closeConnections)&&(identical(other.testUrl, testUrl) || other.testUrl == testUrl)&&(identical(other.isAnimateToPage, isAnimateToPage) || other.isAnimateToPage == isAnimateToPage)&&(identical(other.enableNavBarHapticFeedback, enableNavBarHapticFeedback) || other.enableNavBarHapticFeedback == enableNavBarHapticFeedback)&&(identical(other.autoCheckUpdate, autoCheckUpdate) || other.autoCheckUpdate == autoCheckUpdate)&&(identical(other.showLabel, showLabel) || other.showLabel == showLabel)&&(identical(other.disclaimerAccepted, disclaimerAccepted) || other.disclaimerAccepted == disclaimerAccepted)&&(identical(other.minimizeOnExit, minimizeOnExit) || other.minimizeOnExit == minimizeOnExit)&&(identical(other.hidden, hidden) || other.hidden == hidden)&&(identical(other.developerMode, developerMode) || other.developerMode == developerMode)&&(identical(other.enableHighRefreshRate, enableHighRefreshRate) || other.enableHighRefreshRate == enableHighRefreshRate)&&(identical(other.recoveryStrategy, recoveryStrategy) || other.recoveryStrategy == recoveryStrategy));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hashAll([runtimeType,locale,const DeepCollectionEquality().hash(dashboardWidgets),onlyStatisticsProxy,autoLaunch,silentLaunch,smartDelayLaunch,autoRun,openLogs,closeConnections,testUrl,isAnimateToPage,enableNavBarHapticFeedback,autoCheckUpdate,showLabel,disclaimerAccepted,minimizeOnExit,hidden,developerMode,enableHighRefreshRate,recoveryStrategy]);\n\n@override\nString toString() {\n  return 'AppSettingProps(locale: $locale, dashboardWidgets: $dashboardWidgets, onlyStatisticsProxy: $onlyStatisticsProxy, autoLaunch: $autoLaunch, silentLaunch: $silentLaunch, smartDelayLaunch: $smartDelayLaunch, autoRun: $autoRun, openLogs: $openLogs, closeConnections: $closeConnections, testUrl: $testUrl, isAnimateToPage: $isAnimateToPage, enableNavBarHapticFeedback: $enableNavBarHapticFeedback, autoCheckUpdate: $autoCheckUpdate, showLabel: $showLabel, disclaimerAccepted: $disclaimerAccepted, minimizeOnExit: $minimizeOnExit, hidden: $hidden, developerMode: $developerMode, enableHighRefreshRate: $enableHighRefreshRate, recoveryStrategy: $recoveryStrategy)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $AppSettingPropsCopyWith<$Res>  {\n  factory $AppSettingPropsCopyWith(AppSettingProps value, $Res Function(AppSettingProps) _then) = _$AppSettingPropsCopyWithImpl;\n@useResult\n$Res call({\n String? locale,@JsonKey(fromJson: dashboardWidgetsSafeFormJson) List<DashboardWidget> dashboardWidgets, bool onlyStatisticsProxy, bool autoLaunch, bool silentLaunch, bool smartDelayLaunch, bool autoRun, bool openLogs, bool closeConnections, String testUrl, bool isAnimateToPage, bool enableNavBarHapticFeedback, bool autoCheckUpdate, bool showLabel, bool disclaimerAccepted, bool minimizeOnExit, bool hidden, bool developerMode, bool enableHighRefreshRate, RecoveryStrategy recoveryStrategy\n});\n\n\n\n\n}\n/// @nodoc\nclass _$AppSettingPropsCopyWithImpl<$Res>\n    implements $AppSettingPropsCopyWith<$Res> {\n  _$AppSettingPropsCopyWithImpl(this._self, this._then);\n\n  final AppSettingProps _self;\n  final $Res Function(AppSettingProps) _then;\n\n/// Create a copy of AppSettingProps\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? locale = freezed,Object? dashboardWidgets = null,Object? onlyStatisticsProxy = null,Object? autoLaunch = null,Object? silentLaunch = null,Object? smartDelayLaunch = null,Object? autoRun = null,Object? openLogs = null,Object? closeConnections = null,Object? testUrl = null,Object? isAnimateToPage = null,Object? enableNavBarHapticFeedback = null,Object? autoCheckUpdate = null,Object? showLabel = null,Object? disclaimerAccepted = null,Object? minimizeOnExit = null,Object? hidden = null,Object? developerMode = null,Object? enableHighRefreshRate = null,Object? recoveryStrategy = null,}) {\n  return _then(_self.copyWith(\nlocale: freezed == locale ? _self.locale : locale // ignore: cast_nullable_to_non_nullable\nas String?,dashboardWidgets: null == dashboardWidgets ? _self.dashboardWidgets : dashboardWidgets // ignore: cast_nullable_to_non_nullable\nas List<DashboardWidget>,onlyStatisticsProxy: null == onlyStatisticsProxy ? _self.onlyStatisticsProxy : onlyStatisticsProxy // ignore: cast_nullable_to_non_nullable\nas bool,autoLaunch: null == autoLaunch ? _self.autoLaunch : autoLaunch // ignore: cast_nullable_to_non_nullable\nas bool,silentLaunch: null == silentLaunch ? _self.silentLaunch : silentLaunch // ignore: cast_nullable_to_non_nullable\nas bool,smartDelayLaunch: null == smartDelayLaunch ? _self.smartDelayLaunch : smartDelayLaunch // ignore: cast_nullable_to_non_nullable\nas bool,autoRun: null == autoRun ? _self.autoRun : autoRun // ignore: cast_nullable_to_non_nullable\nas bool,openLogs: null == openLogs ? _self.openLogs : openLogs // ignore: cast_nullable_to_non_nullable\nas bool,closeConnections: null == closeConnections ? _self.closeConnections : closeConnections // ignore: cast_nullable_to_non_nullable\nas bool,testUrl: null == testUrl ? _self.testUrl : testUrl // ignore: cast_nullable_to_non_nullable\nas String,isAnimateToPage: null == isAnimateToPage ? _self.isAnimateToPage : isAnimateToPage // ignore: cast_nullable_to_non_nullable\nas bool,enableNavBarHapticFeedback: null == enableNavBarHapticFeedback ? _self.enableNavBarHapticFeedback : enableNavBarHapticFeedback // ignore: cast_nullable_to_non_nullable\nas bool,autoCheckUpdate: null == autoCheckUpdate ? _self.autoCheckUpdate : autoCheckUpdate // ignore: cast_nullable_to_non_nullable\nas bool,showLabel: null == showLabel ? _self.showLabel : showLabel // ignore: cast_nullable_to_non_nullable\nas bool,disclaimerAccepted: null == disclaimerAccepted ? _self.disclaimerAccepted : disclaimerAccepted // ignore: cast_nullable_to_non_nullable\nas bool,minimizeOnExit: null == minimizeOnExit ? _self.minimizeOnExit : minimizeOnExit // ignore: cast_nullable_to_non_nullable\nas bool,hidden: null == hidden ? _self.hidden : hidden // ignore: cast_nullable_to_non_nullable\nas bool,developerMode: null == developerMode ? _self.developerMode : developerMode // ignore: cast_nullable_to_non_nullable\nas bool,enableHighRefreshRate: null == enableHighRefreshRate ? _self.enableHighRefreshRate : enableHighRefreshRate // ignore: cast_nullable_to_non_nullable\nas bool,recoveryStrategy: null == recoveryStrategy ? _self.recoveryStrategy : recoveryStrategy // ignore: cast_nullable_to_non_nullable\nas RecoveryStrategy,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [AppSettingProps].\nextension AppSettingPropsPatterns on AppSettingProps {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _AppSettingProps value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _AppSettingProps() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _AppSettingProps value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _AppSettingProps():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _AppSettingProps value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _AppSettingProps() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String? locale, @JsonKey(fromJson: dashboardWidgetsSafeFormJson)  List<DashboardWidget> dashboardWidgets,  bool onlyStatisticsProxy,  bool autoLaunch,  bool silentLaunch,  bool smartDelayLaunch,  bool autoRun,  bool openLogs,  bool closeConnections,  String testUrl,  bool isAnimateToPage,  bool enableNavBarHapticFeedback,  bool autoCheckUpdate,  bool showLabel,  bool disclaimerAccepted,  bool minimizeOnExit,  bool hidden,  bool developerMode,  bool enableHighRefreshRate,  RecoveryStrategy recoveryStrategy)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _AppSettingProps() when $default != null:\nreturn $default(_that.locale,_that.dashboardWidgets,_that.onlyStatisticsProxy,_that.autoLaunch,_that.silentLaunch,_that.smartDelayLaunch,_that.autoRun,_that.openLogs,_that.closeConnections,_that.testUrl,_that.isAnimateToPage,_that.enableNavBarHapticFeedback,_that.autoCheckUpdate,_that.showLabel,_that.disclaimerAccepted,_that.minimizeOnExit,_that.hidden,_that.developerMode,_that.enableHighRefreshRate,_that.recoveryStrategy);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String? locale, @JsonKey(fromJson: dashboardWidgetsSafeFormJson)  List<DashboardWidget> dashboardWidgets,  bool onlyStatisticsProxy,  bool autoLaunch,  bool silentLaunch,  bool smartDelayLaunch,  bool autoRun,  bool openLogs,  bool closeConnections,  String testUrl,  bool isAnimateToPage,  bool enableNavBarHapticFeedback,  bool autoCheckUpdate,  bool showLabel,  bool disclaimerAccepted,  bool minimizeOnExit,  bool hidden,  bool developerMode,  bool enableHighRefreshRate,  RecoveryStrategy recoveryStrategy)  $default,) {final _that = this;\nswitch (_that) {\ncase _AppSettingProps():\nreturn $default(_that.locale,_that.dashboardWidgets,_that.onlyStatisticsProxy,_that.autoLaunch,_that.silentLaunch,_that.smartDelayLaunch,_that.autoRun,_that.openLogs,_that.closeConnections,_that.testUrl,_that.isAnimateToPage,_that.enableNavBarHapticFeedback,_that.autoCheckUpdate,_that.showLabel,_that.disclaimerAccepted,_that.minimizeOnExit,_that.hidden,_that.developerMode,_that.enableHighRefreshRate,_that.recoveryStrategy);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String? locale, @JsonKey(fromJson: dashboardWidgetsSafeFormJson)  List<DashboardWidget> dashboardWidgets,  bool onlyStatisticsProxy,  bool autoLaunch,  bool silentLaunch,  bool smartDelayLaunch,  bool autoRun,  bool openLogs,  bool closeConnections,  String testUrl,  bool isAnimateToPage,  bool enableNavBarHapticFeedback,  bool autoCheckUpdate,  bool showLabel,  bool disclaimerAccepted,  bool minimizeOnExit,  bool hidden,  bool developerMode,  bool enableHighRefreshRate,  RecoveryStrategy recoveryStrategy)?  $default,) {final _that = this;\nswitch (_that) {\ncase _AppSettingProps() when $default != null:\nreturn $default(_that.locale,_that.dashboardWidgets,_that.onlyStatisticsProxy,_that.autoLaunch,_that.silentLaunch,_that.smartDelayLaunch,_that.autoRun,_that.openLogs,_that.closeConnections,_that.testUrl,_that.isAnimateToPage,_that.enableNavBarHapticFeedback,_that.autoCheckUpdate,_that.showLabel,_that.disclaimerAccepted,_that.minimizeOnExit,_that.hidden,_that.developerMode,_that.enableHighRefreshRate,_that.recoveryStrategy);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _AppSettingProps implements AppSettingProps {\n  const _AppSettingProps({this.locale, @JsonKey(fromJson: dashboardWidgetsSafeFormJson) final  List<DashboardWidget> dashboardWidgets = defaultDashboardWidgets, this.onlyStatisticsProxy = false, this.autoLaunch = false, this.silentLaunch = false, this.smartDelayLaunch = true, this.autoRun = false, this.openLogs = true, this.closeConnections = true, this.testUrl = defaultTestUrl, this.isAnimateToPage = true, this.enableNavBarHapticFeedback = false, this.autoCheckUpdate = true, this.showLabel = false, this.disclaimerAccepted = false, this.minimizeOnExit = true, this.hidden = false, this.developerMode = false, this.enableHighRefreshRate = false, this.recoveryStrategy = RecoveryStrategy.compatible}): _dashboardWidgets = dashboardWidgets;\n  factory _AppSettingProps.fromJson(Map<String, dynamic> json) => _$AppSettingPropsFromJson(json);\n\n@override final  String? locale;\n final  List<DashboardWidget> _dashboardWidgets;\n@override@JsonKey(fromJson: dashboardWidgetsSafeFormJson) List<DashboardWidget> get dashboardWidgets {\n  if (_dashboardWidgets is EqualUnmodifiableListView) return _dashboardWidgets;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_dashboardWidgets);\n}\n\n@override@JsonKey() final  bool onlyStatisticsProxy;\n@override@JsonKey() final  bool autoLaunch;\n@override@JsonKey() final  bool silentLaunch;\n@override@JsonKey() final  bool smartDelayLaunch;\n@override@JsonKey() final  bool autoRun;\n@override@JsonKey() final  bool openLogs;\n@override@JsonKey() final  bool closeConnections;\n@override@JsonKey() final  String testUrl;\n@override@JsonKey() final  bool isAnimateToPage;\n@override@JsonKey() final  bool enableNavBarHapticFeedback;\n@override@JsonKey() final  bool autoCheckUpdate;\n@override@JsonKey() final  bool showLabel;\n@override@JsonKey() final  bool disclaimerAccepted;\n@override@JsonKey() final  bool minimizeOnExit;\n@override@JsonKey() final  bool hidden;\n@override@JsonKey() final  bool developerMode;\n@override@JsonKey() final  bool enableHighRefreshRate;\n@override@JsonKey() final  RecoveryStrategy recoveryStrategy;\n\n/// Create a copy of AppSettingProps\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$AppSettingPropsCopyWith<_AppSettingProps> get copyWith => __$AppSettingPropsCopyWithImpl<_AppSettingProps>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$AppSettingPropsToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppSettingProps&&(identical(other.locale, locale) || other.locale == locale)&&const DeepCollectionEquality().equals(other._dashboardWidgets, _dashboardWidgets)&&(identical(other.onlyStatisticsProxy, onlyStatisticsProxy) || other.onlyStatisticsProxy == onlyStatisticsProxy)&&(identical(other.autoLaunch, autoLaunch) || other.autoLaunch == autoLaunch)&&(identical(other.silentLaunch, silentLaunch) || other.silentLaunch == silentLaunch)&&(identical(other.smartDelayLaunch, smartDelayLaunch) || other.smartDelayLaunch == smartDelayLaunch)&&(identical(other.autoRun, autoRun) || other.autoRun == autoRun)&&(identical(other.openLogs, openLogs) || other.openLogs == openLogs)&&(identical(other.closeConnections, closeConnections) || other.closeConnections == closeConnections)&&(identical(other.testUrl, testUrl) || other.testUrl == testUrl)&&(identical(other.isAnimateToPage, isAnimateToPage) || other.isAnimateToPage == isAnimateToPage)&&(identical(other.enableNavBarHapticFeedback, enableNavBarHapticFeedback) || other.enableNavBarHapticFeedback == enableNavBarHapticFeedback)&&(identical(other.autoCheckUpdate, autoCheckUpdate) || other.autoCheckUpdate == autoCheckUpdate)&&(identical(other.showLabel, showLabel) || other.showLabel == showLabel)&&(identical(other.disclaimerAccepted, disclaimerAccepted) || other.disclaimerAccepted == disclaimerAccepted)&&(identical(other.minimizeOnExit, minimizeOnExit) || other.minimizeOnExit == minimizeOnExit)&&(identical(other.hidden, hidden) || other.hidden == hidden)&&(identical(other.developerMode, developerMode) || other.developerMode == developerMode)&&(identical(other.enableHighRefreshRate, enableHighRefreshRate) || other.enableHighRefreshRate == enableHighRefreshRate)&&(identical(other.recoveryStrategy, recoveryStrategy) || other.recoveryStrategy == recoveryStrategy));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hashAll([runtimeType,locale,const DeepCollectionEquality().hash(_dashboardWidgets),onlyStatisticsProxy,autoLaunch,silentLaunch,smartDelayLaunch,autoRun,openLogs,closeConnections,testUrl,isAnimateToPage,enableNavBarHapticFeedback,autoCheckUpdate,showLabel,disclaimerAccepted,minimizeOnExit,hidden,developerMode,enableHighRefreshRate,recoveryStrategy]);\n\n@override\nString toString() {\n  return 'AppSettingProps(locale: $locale, dashboardWidgets: $dashboardWidgets, onlyStatisticsProxy: $onlyStatisticsProxy, autoLaunch: $autoLaunch, silentLaunch: $silentLaunch, smartDelayLaunch: $smartDelayLaunch, autoRun: $autoRun, openLogs: $openLogs, closeConnections: $closeConnections, testUrl: $testUrl, isAnimateToPage: $isAnimateToPage, enableNavBarHapticFeedback: $enableNavBarHapticFeedback, autoCheckUpdate: $autoCheckUpdate, showLabel: $showLabel, disclaimerAccepted: $disclaimerAccepted, minimizeOnExit: $minimizeOnExit, hidden: $hidden, developerMode: $developerMode, enableHighRefreshRate: $enableHighRefreshRate, recoveryStrategy: $recoveryStrategy)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$AppSettingPropsCopyWith<$Res> implements $AppSettingPropsCopyWith<$Res> {\n  factory _$AppSettingPropsCopyWith(_AppSettingProps value, $Res Function(_AppSettingProps) _then) = __$AppSettingPropsCopyWithImpl;\n@override @useResult\n$Res call({\n String? locale,@JsonKey(fromJson: dashboardWidgetsSafeFormJson) List<DashboardWidget> dashboardWidgets, bool onlyStatisticsProxy, bool autoLaunch, bool silentLaunch, bool smartDelayLaunch, bool autoRun, bool openLogs, bool closeConnections, String testUrl, bool isAnimateToPage, bool enableNavBarHapticFeedback, bool autoCheckUpdate, bool showLabel, bool disclaimerAccepted, bool minimizeOnExit, bool hidden, bool developerMode, bool enableHighRefreshRate, RecoveryStrategy recoveryStrategy\n});\n\n\n\n\n}\n/// @nodoc\nclass __$AppSettingPropsCopyWithImpl<$Res>\n    implements _$AppSettingPropsCopyWith<$Res> {\n  __$AppSettingPropsCopyWithImpl(this._self, this._then);\n\n  final _AppSettingProps _self;\n  final $Res Function(_AppSettingProps) _then;\n\n/// Create a copy of AppSettingProps\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? locale = freezed,Object? dashboardWidgets = null,Object? onlyStatisticsProxy = null,Object? autoLaunch = null,Object? silentLaunch = null,Object? smartDelayLaunch = null,Object? autoRun = null,Object? openLogs = null,Object? closeConnections = null,Object? testUrl = null,Object? isAnimateToPage = null,Object? enableNavBarHapticFeedback = null,Object? autoCheckUpdate = null,Object? showLabel = null,Object? disclaimerAccepted = null,Object? minimizeOnExit = null,Object? hidden = null,Object? developerMode = null,Object? enableHighRefreshRate = null,Object? recoveryStrategy = null,}) {\n  return _then(_AppSettingProps(\nlocale: freezed == locale ? _self.locale : locale // ignore: cast_nullable_to_non_nullable\nas String?,dashboardWidgets: null == dashboardWidgets ? _self._dashboardWidgets : dashboardWidgets // ignore: cast_nullable_to_non_nullable\nas List<DashboardWidget>,onlyStatisticsProxy: null == onlyStatisticsProxy ? _self.onlyStatisticsProxy : onlyStatisticsProxy // ignore: cast_nullable_to_non_nullable\nas bool,autoLaunch: null == autoLaunch ? _self.autoLaunch : autoLaunch // ignore: cast_nullable_to_non_nullable\nas bool,silentLaunch: null == silentLaunch ? _self.silentLaunch : silentLaunch // ignore: cast_nullable_to_non_nullable\nas bool,smartDelayLaunch: null == smartDelayLaunch ? _self.smartDelayLaunch : smartDelayLaunch // ignore: cast_nullable_to_non_nullable\nas bool,autoRun: null == autoRun ? _self.autoRun : autoRun // ignore: cast_nullable_to_non_nullable\nas bool,openLogs: null == openLogs ? _self.openLogs : openLogs // ignore: cast_nullable_to_non_nullable\nas bool,closeConnections: null == closeConnections ? _self.closeConnections : closeConnections // ignore: cast_nullable_to_non_nullable\nas bool,testUrl: null == testUrl ? _self.testUrl : testUrl // ignore: cast_nullable_to_non_nullable\nas String,isAnimateToPage: null == isAnimateToPage ? _self.isAnimateToPage : isAnimateToPage // ignore: cast_nullable_to_non_nullable\nas bool,enableNavBarHapticFeedback: null == enableNavBarHapticFeedback ? _self.enableNavBarHapticFeedback : enableNavBarHapticFeedback // ignore: cast_nullable_to_non_nullable\nas bool,autoCheckUpdate: null == autoCheckUpdate ? _self.autoCheckUpdate : autoCheckUpdate // ignore: cast_nullable_to_non_nullable\nas bool,showLabel: null == showLabel ? _self.showLabel : showLabel // ignore: cast_nullable_to_non_nullable\nas bool,disclaimerAccepted: null == disclaimerAccepted ? _self.disclaimerAccepted : disclaimerAccepted // ignore: cast_nullable_to_non_nullable\nas bool,minimizeOnExit: null == minimizeOnExit ? _self.minimizeOnExit : minimizeOnExit // ignore: cast_nullable_to_non_nullable\nas bool,hidden: null == hidden ? _self.hidden : hidden // ignore: cast_nullable_to_non_nullable\nas bool,developerMode: null == developerMode ? _self.developerMode : developerMode // ignore: cast_nullable_to_non_nullable\nas bool,enableHighRefreshRate: null == enableHighRefreshRate ? _self.enableHighRefreshRate : enableHighRefreshRate // ignore: cast_nullable_to_non_nullable\nas bool,recoveryStrategy: null == recoveryStrategy ? _self.recoveryStrategy : recoveryStrategy // ignore: cast_nullable_to_non_nullable\nas RecoveryStrategy,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$AccessControl {\n\n bool get enable; AccessControlMode get mode; List<String> get acceptList; List<String> get rejectList; AccessSortType get sort; bool get isFilterSystemApp; bool get isFilterNonInternetApp;\n/// Create a copy of AccessControl\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$AccessControlCopyWith<AccessControl> get copyWith => _$AccessControlCopyWithImpl<AccessControl>(this as AccessControl, _$identity);\n\n  /// Serializes this AccessControl to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is AccessControl&&(identical(other.enable, enable) || other.enable == enable)&&(identical(other.mode, mode) || other.mode == mode)&&const DeepCollectionEquality().equals(other.acceptList, acceptList)&&const DeepCollectionEquality().equals(other.rejectList, rejectList)&&(identical(other.sort, sort) || other.sort == sort)&&(identical(other.isFilterSystemApp, isFilterSystemApp) || other.isFilterSystemApp == isFilterSystemApp)&&(identical(other.isFilterNonInternetApp, isFilterNonInternetApp) || other.isFilterNonInternetApp == isFilterNonInternetApp));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,enable,mode,const DeepCollectionEquality().hash(acceptList),const DeepCollectionEquality().hash(rejectList),sort,isFilterSystemApp,isFilterNonInternetApp);\n\n@override\nString toString() {\n  return 'AccessControl(enable: $enable, mode: $mode, acceptList: $acceptList, rejectList: $rejectList, sort: $sort, isFilterSystemApp: $isFilterSystemApp, isFilterNonInternetApp: $isFilterNonInternetApp)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $AccessControlCopyWith<$Res>  {\n  factory $AccessControlCopyWith(AccessControl value, $Res Function(AccessControl) _then) = _$AccessControlCopyWithImpl;\n@useResult\n$Res call({\n bool enable, AccessControlMode mode, List<String> acceptList, List<String> rejectList, AccessSortType sort, bool isFilterSystemApp, bool isFilterNonInternetApp\n});\n\n\n\n\n}\n/// @nodoc\nclass _$AccessControlCopyWithImpl<$Res>\n    implements $AccessControlCopyWith<$Res> {\n  _$AccessControlCopyWithImpl(this._self, this._then);\n\n  final AccessControl _self;\n  final $Res Function(AccessControl) _then;\n\n/// Create a copy of AccessControl\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? enable = null,Object? mode = null,Object? acceptList = null,Object? rejectList = null,Object? sort = null,Object? isFilterSystemApp = null,Object? isFilterNonInternetApp = null,}) {\n  return _then(_self.copyWith(\nenable: null == enable ? _self.enable : enable // ignore: cast_nullable_to_non_nullable\nas bool,mode: null == mode ? _self.mode : mode // ignore: cast_nullable_to_non_nullable\nas AccessControlMode,acceptList: null == acceptList ? _self.acceptList : acceptList // ignore: cast_nullable_to_non_nullable\nas List<String>,rejectList: null == rejectList ? _self.rejectList : rejectList // ignore: cast_nullable_to_non_nullable\nas List<String>,sort: null == sort ? _self.sort : sort // ignore: cast_nullable_to_non_nullable\nas AccessSortType,isFilterSystemApp: null == isFilterSystemApp ? _self.isFilterSystemApp : isFilterSystemApp // ignore: cast_nullable_to_non_nullable\nas bool,isFilterNonInternetApp: null == isFilterNonInternetApp ? _self.isFilterNonInternetApp : isFilterNonInternetApp // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [AccessControl].\nextension AccessControlPatterns on AccessControl {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _AccessControl value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _AccessControl() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _AccessControl value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _AccessControl():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _AccessControl value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _AccessControl() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool enable,  AccessControlMode mode,  List<String> acceptList,  List<String> rejectList,  AccessSortType sort,  bool isFilterSystemApp,  bool isFilterNonInternetApp)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _AccessControl() when $default != null:\nreturn $default(_that.enable,_that.mode,_that.acceptList,_that.rejectList,_that.sort,_that.isFilterSystemApp,_that.isFilterNonInternetApp);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool enable,  AccessControlMode mode,  List<String> acceptList,  List<String> rejectList,  AccessSortType sort,  bool isFilterSystemApp,  bool isFilterNonInternetApp)  $default,) {final _that = this;\nswitch (_that) {\ncase _AccessControl():\nreturn $default(_that.enable,_that.mode,_that.acceptList,_that.rejectList,_that.sort,_that.isFilterSystemApp,_that.isFilterNonInternetApp);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool enable,  AccessControlMode mode,  List<String> acceptList,  List<String> rejectList,  AccessSortType sort,  bool isFilterSystemApp,  bool isFilterNonInternetApp)?  $default,) {final _that = this;\nswitch (_that) {\ncase _AccessControl() when $default != null:\nreturn $default(_that.enable,_that.mode,_that.acceptList,_that.rejectList,_that.sort,_that.isFilterSystemApp,_that.isFilterNonInternetApp);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _AccessControl implements AccessControl {\n  const _AccessControl({this.enable = false, this.mode = AccessControlMode.rejectSelected, final  List<String> acceptList = const [], final  List<String> rejectList = const [], this.sort = AccessSortType.none, this.isFilterSystemApp = true, this.isFilterNonInternetApp = true}): _acceptList = acceptList,_rejectList = rejectList;\n  factory _AccessControl.fromJson(Map<String, dynamic> json) => _$AccessControlFromJson(json);\n\n@override@JsonKey() final  bool enable;\n@override@JsonKey() final  AccessControlMode mode;\n final  List<String> _acceptList;\n@override@JsonKey() List<String> get acceptList {\n  if (_acceptList is EqualUnmodifiableListView) return _acceptList;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_acceptList);\n}\n\n final  List<String> _rejectList;\n@override@JsonKey() List<String> get rejectList {\n  if (_rejectList is EqualUnmodifiableListView) return _rejectList;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_rejectList);\n}\n\n@override@JsonKey() final  AccessSortType sort;\n@override@JsonKey() final  bool isFilterSystemApp;\n@override@JsonKey() final  bool isFilterNonInternetApp;\n\n/// Create a copy of AccessControl\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$AccessControlCopyWith<_AccessControl> get copyWith => __$AccessControlCopyWithImpl<_AccessControl>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$AccessControlToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _AccessControl&&(identical(other.enable, enable) || other.enable == enable)&&(identical(other.mode, mode) || other.mode == mode)&&const DeepCollectionEquality().equals(other._acceptList, _acceptList)&&const DeepCollectionEquality().equals(other._rejectList, _rejectList)&&(identical(other.sort, sort) || other.sort == sort)&&(identical(other.isFilterSystemApp, isFilterSystemApp) || other.isFilterSystemApp == isFilterSystemApp)&&(identical(other.isFilterNonInternetApp, isFilterNonInternetApp) || other.isFilterNonInternetApp == isFilterNonInternetApp));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,enable,mode,const DeepCollectionEquality().hash(_acceptList),const DeepCollectionEquality().hash(_rejectList),sort,isFilterSystemApp,isFilterNonInternetApp);\n\n@override\nString toString() {\n  return 'AccessControl(enable: $enable, mode: $mode, acceptList: $acceptList, rejectList: $rejectList, sort: $sort, isFilterSystemApp: $isFilterSystemApp, isFilterNonInternetApp: $isFilterNonInternetApp)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$AccessControlCopyWith<$Res> implements $AccessControlCopyWith<$Res> {\n  factory _$AccessControlCopyWith(_AccessControl value, $Res Function(_AccessControl) _then) = __$AccessControlCopyWithImpl;\n@override @useResult\n$Res call({\n bool enable, AccessControlMode mode, List<String> acceptList, List<String> rejectList, AccessSortType sort, bool isFilterSystemApp, bool isFilterNonInternetApp\n});\n\n\n\n\n}\n/// @nodoc\nclass __$AccessControlCopyWithImpl<$Res>\n    implements _$AccessControlCopyWith<$Res> {\n  __$AccessControlCopyWithImpl(this._self, this._then);\n\n  final _AccessControl _self;\n  final $Res Function(_AccessControl) _then;\n\n/// Create a copy of AccessControl\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? enable = null,Object? mode = null,Object? acceptList = null,Object? rejectList = null,Object? sort = null,Object? isFilterSystemApp = null,Object? isFilterNonInternetApp = null,}) {\n  return _then(_AccessControl(\nenable: null == enable ? _self.enable : enable // ignore: cast_nullable_to_non_nullable\nas bool,mode: null == mode ? _self.mode : mode // ignore: cast_nullable_to_non_nullable\nas AccessControlMode,acceptList: null == acceptList ? _self._acceptList : acceptList // ignore: cast_nullable_to_non_nullable\nas List<String>,rejectList: null == rejectList ? _self._rejectList : rejectList // ignore: cast_nullable_to_non_nullable\nas List<String>,sort: null == sort ? _self.sort : sort // ignore: cast_nullable_to_non_nullable\nas AccessSortType,isFilterSystemApp: null == isFilterSystemApp ? _self.isFilterSystemApp : isFilterSystemApp // ignore: cast_nullable_to_non_nullable\nas bool,isFilterNonInternetApp: null == isFilterNonInternetApp ? _self.isFilterNonInternetApp : isFilterNonInternetApp // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$WindowProps {\n\n double get width; double get height; double? get top; double? get left; bool get isLocked;\n/// Create a copy of WindowProps\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$WindowPropsCopyWith<WindowProps> get copyWith => _$WindowPropsCopyWithImpl<WindowProps>(this as WindowProps, _$identity);\n\n  /// Serializes this WindowProps to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is WindowProps&&(identical(other.width, width) || other.width == width)&&(identical(other.height, height) || other.height == height)&&(identical(other.top, top) || other.top == top)&&(identical(other.left, left) || other.left == left)&&(identical(other.isLocked, isLocked) || other.isLocked == isLocked));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,width,height,top,left,isLocked);\n\n@override\nString toString() {\n  return 'WindowProps(width: $width, height: $height, top: $top, left: $left, isLocked: $isLocked)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $WindowPropsCopyWith<$Res>  {\n  factory $WindowPropsCopyWith(WindowProps value, $Res Function(WindowProps) _then) = _$WindowPropsCopyWithImpl;\n@useResult\n$Res call({\n double width, double height, double? top, double? left, bool isLocked\n});\n\n\n\n\n}\n/// @nodoc\nclass _$WindowPropsCopyWithImpl<$Res>\n    implements $WindowPropsCopyWith<$Res> {\n  _$WindowPropsCopyWithImpl(this._self, this._then);\n\n  final WindowProps _self;\n  final $Res Function(WindowProps) _then;\n\n/// Create a copy of WindowProps\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? width = null,Object? height = null,Object? top = freezed,Object? left = freezed,Object? isLocked = null,}) {\n  return _then(_self.copyWith(\nwidth: null == width ? _self.width : width // ignore: cast_nullable_to_non_nullable\nas double,height: null == height ? _self.height : height // ignore: cast_nullable_to_non_nullable\nas double,top: freezed == top ? _self.top : top // ignore: cast_nullable_to_non_nullable\nas double?,left: freezed == left ? _self.left : left // ignore: cast_nullable_to_non_nullable\nas double?,isLocked: null == isLocked ? _self.isLocked : isLocked // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [WindowProps].\nextension WindowPropsPatterns on WindowProps {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _WindowProps value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _WindowProps() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _WindowProps value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _WindowProps():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _WindowProps value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _WindowProps() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( double width,  double height,  double? top,  double? left,  bool isLocked)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _WindowProps() when $default != null:\nreturn $default(_that.width,_that.height,_that.top,_that.left,_that.isLocked);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( double width,  double height,  double? top,  double? left,  bool isLocked)  $default,) {final _that = this;\nswitch (_that) {\ncase _WindowProps():\nreturn $default(_that.width,_that.height,_that.top,_that.left,_that.isLocked);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( double width,  double height,  double? top,  double? left,  bool isLocked)?  $default,) {final _that = this;\nswitch (_that) {\ncase _WindowProps() when $default != null:\nreturn $default(_that.width,_that.height,_that.top,_that.left,_that.isLocked);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _WindowProps implements WindowProps {\n  const _WindowProps({this.width = 750, this.height = 600, this.top, this.left, this.isLocked = false});\n  factory _WindowProps.fromJson(Map<String, dynamic> json) => _$WindowPropsFromJson(json);\n\n@override@JsonKey() final  double width;\n@override@JsonKey() final  double height;\n@override final  double? top;\n@override final  double? left;\n@override@JsonKey() final  bool isLocked;\n\n/// Create a copy of WindowProps\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$WindowPropsCopyWith<_WindowProps> get copyWith => __$WindowPropsCopyWithImpl<_WindowProps>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$WindowPropsToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _WindowProps&&(identical(other.width, width) || other.width == width)&&(identical(other.height, height) || other.height == height)&&(identical(other.top, top) || other.top == top)&&(identical(other.left, left) || other.left == left)&&(identical(other.isLocked, isLocked) || other.isLocked == isLocked));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,width,height,top,left,isLocked);\n\n@override\nString toString() {\n  return 'WindowProps(width: $width, height: $height, top: $top, left: $left, isLocked: $isLocked)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$WindowPropsCopyWith<$Res> implements $WindowPropsCopyWith<$Res> {\n  factory _$WindowPropsCopyWith(_WindowProps value, $Res Function(_WindowProps) _then) = __$WindowPropsCopyWithImpl;\n@override @useResult\n$Res call({\n double width, double height, double? top, double? left, bool isLocked\n});\n\n\n\n\n}\n/// @nodoc\nclass __$WindowPropsCopyWithImpl<$Res>\n    implements _$WindowPropsCopyWith<$Res> {\n  __$WindowPropsCopyWithImpl(this._self, this._then);\n\n  final _WindowProps _self;\n  final $Res Function(_WindowProps) _then;\n\n/// Create a copy of WindowProps\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? width = null,Object? height = null,Object? top = freezed,Object? left = freezed,Object? isLocked = null,}) {\n  return _then(_WindowProps(\nwidth: null == width ? _self.width : width // ignore: cast_nullable_to_non_nullable\nas double,height: null == height ? _self.height : height // ignore: cast_nullable_to_non_nullable\nas double,top: freezed == top ? _self.top : top // ignore: cast_nullable_to_non_nullable\nas double?,left: freezed == left ? _self.left : left // ignore: cast_nullable_to_non_nullable\nas double?,isLocked: null == isLocked ? _self.isLocked : isLocked // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$VpnProps {\n\n bool get enable; bool get systemProxy; bool get allowBypass; bool get bypassPrivateRoute; bool get dozeSuspend; bool get smartAutoStop; String get smartAutoStopNetworks; bool get storeFix; bool get networkFix; bool get disableQuic; bool get excludeChina; bool get fcmOptimization; bool get quickResponse; AccessControl get accessControl;\n/// Create a copy of VpnProps\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$VpnPropsCopyWith<VpnProps> get copyWith => _$VpnPropsCopyWithImpl<VpnProps>(this as VpnProps, _$identity);\n\n  /// Serializes this VpnProps to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is VpnProps&&(identical(other.enable, enable) || other.enable == enable)&&(identical(other.systemProxy, systemProxy) || other.systemProxy == systemProxy)&&(identical(other.allowBypass, allowBypass) || other.allowBypass == allowBypass)&&(identical(other.bypassPrivateRoute, bypassPrivateRoute) || other.bypassPrivateRoute == bypassPrivateRoute)&&(identical(other.dozeSuspend, dozeSuspend) || other.dozeSuspend == dozeSuspend)&&(identical(other.smartAutoStop, smartAutoStop) || other.smartAutoStop == smartAutoStop)&&(identical(other.smartAutoStopNetworks, smartAutoStopNetworks) || other.smartAutoStopNetworks == smartAutoStopNetworks)&&(identical(other.storeFix, storeFix) || other.storeFix == storeFix)&&(identical(other.networkFix, networkFix) || other.networkFix == networkFix)&&(identical(other.disableQuic, disableQuic) || other.disableQuic == disableQuic)&&(identical(other.excludeChina, excludeChina) || other.excludeChina == excludeChina)&&(identical(other.fcmOptimization, fcmOptimization) || other.fcmOptimization == fcmOptimization)&&(identical(other.quickResponse, quickResponse) || other.quickResponse == quickResponse)&&(identical(other.accessControl, accessControl) || other.accessControl == accessControl));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,enable,systemProxy,allowBypass,bypassPrivateRoute,dozeSuspend,smartAutoStop,smartAutoStopNetworks,storeFix,networkFix,disableQuic,excludeChina,fcmOptimization,quickResponse,accessControl);\n\n@override\nString toString() {\n  return 'VpnProps(enable: $enable, systemProxy: $systemProxy, allowBypass: $allowBypass, bypassPrivateRoute: $bypassPrivateRoute, dozeSuspend: $dozeSuspend, smartAutoStop: $smartAutoStop, smartAutoStopNetworks: $smartAutoStopNetworks, storeFix: $storeFix, networkFix: $networkFix, disableQuic: $disableQuic, excludeChina: $excludeChina, fcmOptimization: $fcmOptimization, quickResponse: $quickResponse, accessControl: $accessControl)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $VpnPropsCopyWith<$Res>  {\n  factory $VpnPropsCopyWith(VpnProps value, $Res Function(VpnProps) _then) = _$VpnPropsCopyWithImpl;\n@useResult\n$Res call({\n bool enable, bool systemProxy, bool allowBypass, bool bypassPrivateRoute, bool dozeSuspend, bool smartAutoStop, String smartAutoStopNetworks, bool storeFix, bool networkFix, bool disableQuic, bool excludeChina, bool fcmOptimization, bool quickResponse, AccessControl accessControl\n});\n\n\n$AccessControlCopyWith<$Res> get accessControl;\n\n}\n/// @nodoc\nclass _$VpnPropsCopyWithImpl<$Res>\n    implements $VpnPropsCopyWith<$Res> {\n  _$VpnPropsCopyWithImpl(this._self, this._then);\n\n  final VpnProps _self;\n  final $Res Function(VpnProps) _then;\n\n/// Create a copy of VpnProps\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? enable = null,Object? systemProxy = null,Object? allowBypass = null,Object? bypassPrivateRoute = null,Object? dozeSuspend = null,Object? smartAutoStop = null,Object? smartAutoStopNetworks = null,Object? storeFix = null,Object? networkFix = null,Object? disableQuic = null,Object? excludeChina = null,Object? fcmOptimization = null,Object? quickResponse = null,Object? accessControl = null,}) {\n  return _then(_self.copyWith(\nenable: null == enable ? _self.enable : enable // ignore: cast_nullable_to_non_nullable\nas bool,systemProxy: null == systemProxy ? _self.systemProxy : systemProxy // ignore: cast_nullable_to_non_nullable\nas bool,allowBypass: null == allowBypass ? _self.allowBypass : allowBypass // ignore: cast_nullable_to_non_nullable\nas bool,bypassPrivateRoute: null == bypassPrivateRoute ? _self.bypassPrivateRoute : bypassPrivateRoute // ignore: cast_nullable_to_non_nullable\nas bool,dozeSuspend: null == dozeSuspend ? _self.dozeSuspend : dozeSuspend // ignore: cast_nullable_to_non_nullable\nas bool,smartAutoStop: null == smartAutoStop ? _self.smartAutoStop : smartAutoStop // ignore: cast_nullable_to_non_nullable\nas bool,smartAutoStopNetworks: null == smartAutoStopNetworks ? _self.smartAutoStopNetworks : smartAutoStopNetworks // ignore: cast_nullable_to_non_nullable\nas String,storeFix: null == storeFix ? _self.storeFix : storeFix // ignore: cast_nullable_to_non_nullable\nas bool,networkFix: null == networkFix ? _self.networkFix : networkFix // ignore: cast_nullable_to_non_nullable\nas bool,disableQuic: null == disableQuic ? _self.disableQuic : disableQuic // ignore: cast_nullable_to_non_nullable\nas bool,excludeChina: null == excludeChina ? _self.excludeChina : excludeChina // ignore: cast_nullable_to_non_nullable\nas bool,fcmOptimization: null == fcmOptimization ? _self.fcmOptimization : fcmOptimization // ignore: cast_nullable_to_non_nullable\nas bool,quickResponse: null == quickResponse ? _self.quickResponse : quickResponse // ignore: cast_nullable_to_non_nullable\nas bool,accessControl: null == accessControl ? _self.accessControl : accessControl // ignore: cast_nullable_to_non_nullable\nas AccessControl,\n  ));\n}\n/// Create a copy of VpnProps\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$AccessControlCopyWith<$Res> get accessControl {\n  \n  return $AccessControlCopyWith<$Res>(_self.accessControl, (value) {\n    return _then(_self.copyWith(accessControl: value));\n  });\n}\n}\n\n\n/// Adds pattern-matching-related methods to [VpnProps].\nextension VpnPropsPatterns on VpnProps {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _VpnProps value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _VpnProps() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _VpnProps value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _VpnProps():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _VpnProps value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _VpnProps() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool enable,  bool systemProxy,  bool allowBypass,  bool bypassPrivateRoute,  bool dozeSuspend,  bool smartAutoStop,  String smartAutoStopNetworks,  bool storeFix,  bool networkFix,  bool disableQuic,  bool excludeChina,  bool fcmOptimization,  bool quickResponse,  AccessControl accessControl)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _VpnProps() when $default != null:\nreturn $default(_that.enable,_that.systemProxy,_that.allowBypass,_that.bypassPrivateRoute,_that.dozeSuspend,_that.smartAutoStop,_that.smartAutoStopNetworks,_that.storeFix,_that.networkFix,_that.disableQuic,_that.excludeChina,_that.fcmOptimization,_that.quickResponse,_that.accessControl);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool enable,  bool systemProxy,  bool allowBypass,  bool bypassPrivateRoute,  bool dozeSuspend,  bool smartAutoStop,  String smartAutoStopNetworks,  bool storeFix,  bool networkFix,  bool disableQuic,  bool excludeChina,  bool fcmOptimization,  bool quickResponse,  AccessControl accessControl)  $default,) {final _that = this;\nswitch (_that) {\ncase _VpnProps():\nreturn $default(_that.enable,_that.systemProxy,_that.allowBypass,_that.bypassPrivateRoute,_that.dozeSuspend,_that.smartAutoStop,_that.smartAutoStopNetworks,_that.storeFix,_that.networkFix,_that.disableQuic,_that.excludeChina,_that.fcmOptimization,_that.quickResponse,_that.accessControl);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool enable,  bool systemProxy,  bool allowBypass,  bool bypassPrivateRoute,  bool dozeSuspend,  bool smartAutoStop,  String smartAutoStopNetworks,  bool storeFix,  bool networkFix,  bool disableQuic,  bool excludeChina,  bool fcmOptimization,  bool quickResponse,  AccessControl accessControl)?  $default,) {final _that = this;\nswitch (_that) {\ncase _VpnProps() when $default != null:\nreturn $default(_that.enable,_that.systemProxy,_that.allowBypass,_that.bypassPrivateRoute,_that.dozeSuspend,_that.smartAutoStop,_that.smartAutoStopNetworks,_that.storeFix,_that.networkFix,_that.disableQuic,_that.excludeChina,_that.fcmOptimization,_that.quickResponse,_that.accessControl);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _VpnProps implements VpnProps {\n  const _VpnProps({this.enable = true, this.systemProxy = false, this.allowBypass = false, this.bypassPrivateRoute = true, this.dozeSuspend = true, this.smartAutoStop = false, this.smartAutoStopNetworks = '', this.storeFix = false, this.networkFix = false, this.disableQuic = false, this.excludeChina = false, this.fcmOptimization = false, this.quickResponse = false, this.accessControl = defaultAccessControl});\n  factory _VpnProps.fromJson(Map<String, dynamic> json) => _$VpnPropsFromJson(json);\n\n@override@JsonKey() final  bool enable;\n@override@JsonKey() final  bool systemProxy;\n@override@JsonKey() final  bool allowBypass;\n@override@JsonKey() final  bool bypassPrivateRoute;\n@override@JsonKey() final  bool dozeSuspend;\n@override@JsonKey() final  bool smartAutoStop;\n@override@JsonKey() final  String smartAutoStopNetworks;\n@override@JsonKey() final  bool storeFix;\n@override@JsonKey() final  bool networkFix;\n@override@JsonKey() final  bool disableQuic;\n@override@JsonKey() final  bool excludeChina;\n@override@JsonKey() final  bool fcmOptimization;\n@override@JsonKey() final  bool quickResponse;\n@override@JsonKey() final  AccessControl accessControl;\n\n/// Create a copy of VpnProps\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$VpnPropsCopyWith<_VpnProps> get copyWith => __$VpnPropsCopyWithImpl<_VpnProps>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$VpnPropsToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _VpnProps&&(identical(other.enable, enable) || other.enable == enable)&&(identical(other.systemProxy, systemProxy) || other.systemProxy == systemProxy)&&(identical(other.allowBypass, allowBypass) || other.allowBypass == allowBypass)&&(identical(other.bypassPrivateRoute, bypassPrivateRoute) || other.bypassPrivateRoute == bypassPrivateRoute)&&(identical(other.dozeSuspend, dozeSuspend) || other.dozeSuspend == dozeSuspend)&&(identical(other.smartAutoStop, smartAutoStop) || other.smartAutoStop == smartAutoStop)&&(identical(other.smartAutoStopNetworks, smartAutoStopNetworks) || other.smartAutoStopNetworks == smartAutoStopNetworks)&&(identical(other.storeFix, storeFix) || other.storeFix == storeFix)&&(identical(other.networkFix, networkFix) || other.networkFix == networkFix)&&(identical(other.disableQuic, disableQuic) || other.disableQuic == disableQuic)&&(identical(other.excludeChina, excludeChina) || other.excludeChina == excludeChina)&&(identical(other.fcmOptimization, fcmOptimization) || other.fcmOptimization == fcmOptimization)&&(identical(other.quickResponse, quickResponse) || other.quickResponse == quickResponse)&&(identical(other.accessControl, accessControl) || other.accessControl == accessControl));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,enable,systemProxy,allowBypass,bypassPrivateRoute,dozeSuspend,smartAutoStop,smartAutoStopNetworks,storeFix,networkFix,disableQuic,excludeChina,fcmOptimization,quickResponse,accessControl);\n\n@override\nString toString() {\n  return 'VpnProps(enable: $enable, systemProxy: $systemProxy, allowBypass: $allowBypass, bypassPrivateRoute: $bypassPrivateRoute, dozeSuspend: $dozeSuspend, smartAutoStop: $smartAutoStop, smartAutoStopNetworks: $smartAutoStopNetworks, storeFix: $storeFix, networkFix: $networkFix, disableQuic: $disableQuic, excludeChina: $excludeChina, fcmOptimization: $fcmOptimization, quickResponse: $quickResponse, accessControl: $accessControl)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$VpnPropsCopyWith<$Res> implements $VpnPropsCopyWith<$Res> {\n  factory _$VpnPropsCopyWith(_VpnProps value, $Res Function(_VpnProps) _then) = __$VpnPropsCopyWithImpl;\n@override @useResult\n$Res call({\n bool enable, bool systemProxy, bool allowBypass, bool bypassPrivateRoute, bool dozeSuspend, bool smartAutoStop, String smartAutoStopNetworks, bool storeFix, bool networkFix, bool disableQuic, bool excludeChina, bool fcmOptimization, bool quickResponse, AccessControl accessControl\n});\n\n\n@override $AccessControlCopyWith<$Res> get accessControl;\n\n}\n/// @nodoc\nclass __$VpnPropsCopyWithImpl<$Res>\n    implements _$VpnPropsCopyWith<$Res> {\n  __$VpnPropsCopyWithImpl(this._self, this._then);\n\n  final _VpnProps _self;\n  final $Res Function(_VpnProps) _then;\n\n/// Create a copy of VpnProps\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? enable = null,Object? systemProxy = null,Object? allowBypass = null,Object? bypassPrivateRoute = null,Object? dozeSuspend = null,Object? smartAutoStop = null,Object? smartAutoStopNetworks = null,Object? storeFix = null,Object? networkFix = null,Object? disableQuic = null,Object? excludeChina = null,Object? fcmOptimization = null,Object? quickResponse = null,Object? accessControl = null,}) {\n  return _then(_VpnProps(\nenable: null == enable ? _self.enable : enable // ignore: cast_nullable_to_non_nullable\nas bool,systemProxy: null == systemProxy ? _self.systemProxy : systemProxy // ignore: cast_nullable_to_non_nullable\nas bool,allowBypass: null == allowBypass ? _self.allowBypass : allowBypass // ignore: cast_nullable_to_non_nullable\nas bool,bypassPrivateRoute: null == bypassPrivateRoute ? _self.bypassPrivateRoute : bypassPrivateRoute // ignore: cast_nullable_to_non_nullable\nas bool,dozeSuspend: null == dozeSuspend ? _self.dozeSuspend : dozeSuspend // ignore: cast_nullable_to_non_nullable\nas bool,smartAutoStop: null == smartAutoStop ? _self.smartAutoStop : smartAutoStop // ignore: cast_nullable_to_non_nullable\nas bool,smartAutoStopNetworks: null == smartAutoStopNetworks ? _self.smartAutoStopNetworks : smartAutoStopNetworks // ignore: cast_nullable_to_non_nullable\nas String,storeFix: null == storeFix ? _self.storeFix : storeFix // ignore: cast_nullable_to_non_nullable\nas bool,networkFix: null == networkFix ? _self.networkFix : networkFix // ignore: cast_nullable_to_non_nullable\nas bool,disableQuic: null == disableQuic ? _self.disableQuic : disableQuic // ignore: cast_nullable_to_non_nullable\nas bool,excludeChina: null == excludeChina ? _self.excludeChina : excludeChina // ignore: cast_nullable_to_non_nullable\nas bool,fcmOptimization: null == fcmOptimization ? _self.fcmOptimization : fcmOptimization // ignore: cast_nullable_to_non_nullable\nas bool,quickResponse: null == quickResponse ? _self.quickResponse : quickResponse // ignore: cast_nullable_to_non_nullable\nas bool,accessControl: null == accessControl ? _self.accessControl : accessControl // ignore: cast_nullable_to_non_nullable\nas AccessControl,\n  ));\n}\n\n/// Create a copy of VpnProps\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$AccessControlCopyWith<$Res> get accessControl {\n  \n  return $AccessControlCopyWith<$Res>(_self.accessControl, (value) {\n    return _then(_self.copyWith(accessControl: value));\n  });\n}\n}\n\n\n/// @nodoc\nmixin _$NetworkProps {\n\n bool get systemProxy; List<String> get bypassDomain; bool get bypassPrivateRoute; bool get autoSetSystemDns;\n/// Create a copy of NetworkProps\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$NetworkPropsCopyWith<NetworkProps> get copyWith => _$NetworkPropsCopyWithImpl<NetworkProps>(this as NetworkProps, _$identity);\n\n  /// Serializes this NetworkProps to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is NetworkProps&&(identical(other.systemProxy, systemProxy) || other.systemProxy == systemProxy)&&const DeepCollectionEquality().equals(other.bypassDomain, bypassDomain)&&(identical(other.bypassPrivateRoute, bypassPrivateRoute) || other.bypassPrivateRoute == bypassPrivateRoute)&&(identical(other.autoSetSystemDns, autoSetSystemDns) || other.autoSetSystemDns == autoSetSystemDns));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,systemProxy,const DeepCollectionEquality().hash(bypassDomain),bypassPrivateRoute,autoSetSystemDns);\n\n@override\nString toString() {\n  return 'NetworkProps(systemProxy: $systemProxy, bypassDomain: $bypassDomain, bypassPrivateRoute: $bypassPrivateRoute, autoSetSystemDns: $autoSetSystemDns)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $NetworkPropsCopyWith<$Res>  {\n  factory $NetworkPropsCopyWith(NetworkProps value, $Res Function(NetworkProps) _then) = _$NetworkPropsCopyWithImpl;\n@useResult\n$Res call({\n bool systemProxy, List<String> bypassDomain, bool bypassPrivateRoute, bool autoSetSystemDns\n});\n\n\n\n\n}\n/// @nodoc\nclass _$NetworkPropsCopyWithImpl<$Res>\n    implements $NetworkPropsCopyWith<$Res> {\n  _$NetworkPropsCopyWithImpl(this._self, this._then);\n\n  final NetworkProps _self;\n  final $Res Function(NetworkProps) _then;\n\n/// Create a copy of NetworkProps\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? systemProxy = null,Object? bypassDomain = null,Object? bypassPrivateRoute = null,Object? autoSetSystemDns = null,}) {\n  return _then(_self.copyWith(\nsystemProxy: null == systemProxy ? _self.systemProxy : systemProxy // ignore: cast_nullable_to_non_nullable\nas bool,bypassDomain: null == bypassDomain ? _self.bypassDomain : bypassDomain // ignore: cast_nullable_to_non_nullable\nas List<String>,bypassPrivateRoute: null == bypassPrivateRoute ? _self.bypassPrivateRoute : bypassPrivateRoute // ignore: cast_nullable_to_non_nullable\nas bool,autoSetSystemDns: null == autoSetSystemDns ? _self.autoSetSystemDns : autoSetSystemDns // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [NetworkProps].\nextension NetworkPropsPatterns on NetworkProps {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _NetworkProps value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _NetworkProps() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _NetworkProps value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _NetworkProps():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _NetworkProps value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _NetworkProps() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool systemProxy,  List<String> bypassDomain,  bool bypassPrivateRoute,  bool autoSetSystemDns)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _NetworkProps() when $default != null:\nreturn $default(_that.systemProxy,_that.bypassDomain,_that.bypassPrivateRoute,_that.autoSetSystemDns);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool systemProxy,  List<String> bypassDomain,  bool bypassPrivateRoute,  bool autoSetSystemDns)  $default,) {final _that = this;\nswitch (_that) {\ncase _NetworkProps():\nreturn $default(_that.systemProxy,_that.bypassDomain,_that.bypassPrivateRoute,_that.autoSetSystemDns);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool systemProxy,  List<String> bypassDomain,  bool bypassPrivateRoute,  bool autoSetSystemDns)?  $default,) {final _that = this;\nswitch (_that) {\ncase _NetworkProps() when $default != null:\nreturn $default(_that.systemProxy,_that.bypassDomain,_that.bypassPrivateRoute,_that.autoSetSystemDns);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _NetworkProps implements NetworkProps {\n  const _NetworkProps({this.systemProxy = false, final  List<String> bypassDomain = defaultBypassDomain, this.bypassPrivateRoute = true, this.autoSetSystemDns = true}): _bypassDomain = bypassDomain;\n  factory _NetworkProps.fromJson(Map<String, dynamic> json) => _$NetworkPropsFromJson(json);\n\n@override@JsonKey() final  bool systemProxy;\n final  List<String> _bypassDomain;\n@override@JsonKey() List<String> get bypassDomain {\n  if (_bypassDomain is EqualUnmodifiableListView) return _bypassDomain;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_bypassDomain);\n}\n\n@override@JsonKey() final  bool bypassPrivateRoute;\n@override@JsonKey() final  bool autoSetSystemDns;\n\n/// Create a copy of NetworkProps\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$NetworkPropsCopyWith<_NetworkProps> get copyWith => __$NetworkPropsCopyWithImpl<_NetworkProps>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$NetworkPropsToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _NetworkProps&&(identical(other.systemProxy, systemProxy) || other.systemProxy == systemProxy)&&const DeepCollectionEquality().equals(other._bypassDomain, _bypassDomain)&&(identical(other.bypassPrivateRoute, bypassPrivateRoute) || other.bypassPrivateRoute == bypassPrivateRoute)&&(identical(other.autoSetSystemDns, autoSetSystemDns) || other.autoSetSystemDns == autoSetSystemDns));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,systemProxy,const DeepCollectionEquality().hash(_bypassDomain),bypassPrivateRoute,autoSetSystemDns);\n\n@override\nString toString() {\n  return 'NetworkProps(systemProxy: $systemProxy, bypassDomain: $bypassDomain, bypassPrivateRoute: $bypassPrivateRoute, autoSetSystemDns: $autoSetSystemDns)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$NetworkPropsCopyWith<$Res> implements $NetworkPropsCopyWith<$Res> {\n  factory _$NetworkPropsCopyWith(_NetworkProps value, $Res Function(_NetworkProps) _then) = __$NetworkPropsCopyWithImpl;\n@override @useResult\n$Res call({\n bool systemProxy, List<String> bypassDomain, bool bypassPrivateRoute, bool autoSetSystemDns\n});\n\n\n\n\n}\n/// @nodoc\nclass __$NetworkPropsCopyWithImpl<$Res>\n    implements _$NetworkPropsCopyWith<$Res> {\n  __$NetworkPropsCopyWithImpl(this._self, this._then);\n\n  final _NetworkProps _self;\n  final $Res Function(_NetworkProps) _then;\n\n/// Create a copy of NetworkProps\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? systemProxy = null,Object? bypassDomain = null,Object? bypassPrivateRoute = null,Object? autoSetSystemDns = null,}) {\n  return _then(_NetworkProps(\nsystemProxy: null == systemProxy ? _self.systemProxy : systemProxy // ignore: cast_nullable_to_non_nullable\nas bool,bypassDomain: null == bypassDomain ? _self._bypassDomain : bypassDomain // ignore: cast_nullable_to_non_nullable\nas List<String>,bypassPrivateRoute: null == bypassPrivateRoute ? _self.bypassPrivateRoute : bypassPrivateRoute // ignore: cast_nullable_to_non_nullable\nas bool,autoSetSystemDns: null == autoSetSystemDns ? _self.autoSetSystemDns : autoSetSystemDns // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$ProxiesStyle {\n\n ProxiesType get type; ProxiesSortType get sortType; ProxiesLayout get layout; ProxiesIconStyle get iconStyle; ProxyCardType get cardType; DelayAnimationType get delayAnimation; Map<String, String> get iconMap; int get concurrencyLimit;\n/// Create a copy of ProxiesStyle\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ProxiesStyleCopyWith<ProxiesStyle> get copyWith => _$ProxiesStyleCopyWithImpl<ProxiesStyle>(this as ProxiesStyle, _$identity);\n\n  /// Serializes this ProxiesStyle to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is ProxiesStyle&&(identical(other.type, type) || other.type == type)&&(identical(other.sortType, sortType) || other.sortType == sortType)&&(identical(other.layout, layout) || other.layout == layout)&&(identical(other.iconStyle, iconStyle) || other.iconStyle == iconStyle)&&(identical(other.cardType, cardType) || other.cardType == cardType)&&(identical(other.delayAnimation, delayAnimation) || other.delayAnimation == delayAnimation)&&const DeepCollectionEquality().equals(other.iconMap, iconMap)&&(identical(other.concurrencyLimit, concurrencyLimit) || other.concurrencyLimit == concurrencyLimit));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,type,sortType,layout,iconStyle,cardType,delayAnimation,const DeepCollectionEquality().hash(iconMap),concurrencyLimit);\n\n@override\nString toString() {\n  return 'ProxiesStyle(type: $type, sortType: $sortType, layout: $layout, iconStyle: $iconStyle, cardType: $cardType, delayAnimation: $delayAnimation, iconMap: $iconMap, concurrencyLimit: $concurrencyLimit)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ProxiesStyleCopyWith<$Res>  {\n  factory $ProxiesStyleCopyWith(ProxiesStyle value, $Res Function(ProxiesStyle) _then) = _$ProxiesStyleCopyWithImpl;\n@useResult\n$Res call({\n ProxiesType type, ProxiesSortType sortType, ProxiesLayout layout, ProxiesIconStyle iconStyle, ProxyCardType cardType, DelayAnimationType delayAnimation, Map<String, String> iconMap, int concurrencyLimit\n});\n\n\n\n\n}\n/// @nodoc\nclass _$ProxiesStyleCopyWithImpl<$Res>\n    implements $ProxiesStyleCopyWith<$Res> {\n  _$ProxiesStyleCopyWithImpl(this._self, this._then);\n\n  final ProxiesStyle _self;\n  final $Res Function(ProxiesStyle) _then;\n\n/// Create a copy of ProxiesStyle\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? type = null,Object? sortType = null,Object? layout = null,Object? iconStyle = null,Object? cardType = null,Object? delayAnimation = null,Object? iconMap = null,Object? concurrencyLimit = null,}) {\n  return _then(_self.copyWith(\ntype: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable\nas ProxiesType,sortType: null == sortType ? _self.sortType : sortType // ignore: cast_nullable_to_non_nullable\nas ProxiesSortType,layout: null == layout ? _self.layout : layout // ignore: cast_nullable_to_non_nullable\nas ProxiesLayout,iconStyle: null == iconStyle ? _self.iconStyle : iconStyle // ignore: cast_nullable_to_non_nullable\nas ProxiesIconStyle,cardType: null == cardType ? _self.cardType : cardType // ignore: cast_nullable_to_non_nullable\nas ProxyCardType,delayAnimation: null == delayAnimation ? _self.delayAnimation : delayAnimation // ignore: cast_nullable_to_non_nullable\nas DelayAnimationType,iconMap: null == iconMap ? _self.iconMap : iconMap // ignore: cast_nullable_to_non_nullable\nas Map<String, String>,concurrencyLimit: null == concurrencyLimit ? _self.concurrencyLimit : concurrencyLimit // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [ProxiesStyle].\nextension ProxiesStylePatterns on ProxiesStyle {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ProxiesStyle value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _ProxiesStyle() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ProxiesStyle value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ProxiesStyle():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ProxiesStyle value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ProxiesStyle() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( ProxiesType type,  ProxiesSortType sortType,  ProxiesLayout layout,  ProxiesIconStyle iconStyle,  ProxyCardType cardType,  DelayAnimationType delayAnimation,  Map<String, String> iconMap,  int concurrencyLimit)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _ProxiesStyle() when $default != null:\nreturn $default(_that.type,_that.sortType,_that.layout,_that.iconStyle,_that.cardType,_that.delayAnimation,_that.iconMap,_that.concurrencyLimit);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( ProxiesType type,  ProxiesSortType sortType,  ProxiesLayout layout,  ProxiesIconStyle iconStyle,  ProxyCardType cardType,  DelayAnimationType delayAnimation,  Map<String, String> iconMap,  int concurrencyLimit)  $default,) {final _that = this;\nswitch (_that) {\ncase _ProxiesStyle():\nreturn $default(_that.type,_that.sortType,_that.layout,_that.iconStyle,_that.cardType,_that.delayAnimation,_that.iconMap,_that.concurrencyLimit);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( ProxiesType type,  ProxiesSortType sortType,  ProxiesLayout layout,  ProxiesIconStyle iconStyle,  ProxyCardType cardType,  DelayAnimationType delayAnimation,  Map<String, String> iconMap,  int concurrencyLimit)?  $default,) {final _that = this;\nswitch (_that) {\ncase _ProxiesStyle() when $default != null:\nreturn $default(_that.type,_that.sortType,_that.layout,_that.iconStyle,_that.cardType,_that.delayAnimation,_that.iconMap,_that.concurrencyLimit);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _ProxiesStyle implements ProxiesStyle {\n  const _ProxiesStyle({this.type = ProxiesType.tab, this.sortType = ProxiesSortType.none, this.layout = ProxiesLayout.standard, this.iconStyle = ProxiesIconStyle.none, this.cardType = ProxyCardType.shrink, this.delayAnimation = DelayAnimationType.none, final  Map<String, String> iconMap = const {}, this.concurrencyLimit = 16}): _iconMap = iconMap;\n  factory _ProxiesStyle.fromJson(Map<String, dynamic> json) => _$ProxiesStyleFromJson(json);\n\n@override@JsonKey() final  ProxiesType type;\n@override@JsonKey() final  ProxiesSortType sortType;\n@override@JsonKey() final  ProxiesLayout layout;\n@override@JsonKey() final  ProxiesIconStyle iconStyle;\n@override@JsonKey() final  ProxyCardType cardType;\n@override@JsonKey() final  DelayAnimationType delayAnimation;\n final  Map<String, String> _iconMap;\n@override@JsonKey() Map<String, String> get iconMap {\n  if (_iconMap is EqualUnmodifiableMapView) return _iconMap;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableMapView(_iconMap);\n}\n\n@override@JsonKey() final  int concurrencyLimit;\n\n/// Create a copy of ProxiesStyle\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ProxiesStyleCopyWith<_ProxiesStyle> get copyWith => __$ProxiesStyleCopyWithImpl<_ProxiesStyle>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$ProxiesStyleToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProxiesStyle&&(identical(other.type, type) || other.type == type)&&(identical(other.sortType, sortType) || other.sortType == sortType)&&(identical(other.layout, layout) || other.layout == layout)&&(identical(other.iconStyle, iconStyle) || other.iconStyle == iconStyle)&&(identical(other.cardType, cardType) || other.cardType == cardType)&&(identical(other.delayAnimation, delayAnimation) || other.delayAnimation == delayAnimation)&&const DeepCollectionEquality().equals(other._iconMap, _iconMap)&&(identical(other.concurrencyLimit, concurrencyLimit) || other.concurrencyLimit == concurrencyLimit));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,type,sortType,layout,iconStyle,cardType,delayAnimation,const DeepCollectionEquality().hash(_iconMap),concurrencyLimit);\n\n@override\nString toString() {\n  return 'ProxiesStyle(type: $type, sortType: $sortType, layout: $layout, iconStyle: $iconStyle, cardType: $cardType, delayAnimation: $delayAnimation, iconMap: $iconMap, concurrencyLimit: $concurrencyLimit)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ProxiesStyleCopyWith<$Res> implements $ProxiesStyleCopyWith<$Res> {\n  factory _$ProxiesStyleCopyWith(_ProxiesStyle value, $Res Function(_ProxiesStyle) _then) = __$ProxiesStyleCopyWithImpl;\n@override @useResult\n$Res call({\n ProxiesType type, ProxiesSortType sortType, ProxiesLayout layout, ProxiesIconStyle iconStyle, ProxyCardType cardType, DelayAnimationType delayAnimation, Map<String, String> iconMap, int concurrencyLimit\n});\n\n\n\n\n}\n/// @nodoc\nclass __$ProxiesStyleCopyWithImpl<$Res>\n    implements _$ProxiesStyleCopyWith<$Res> {\n  __$ProxiesStyleCopyWithImpl(this._self, this._then);\n\n  final _ProxiesStyle _self;\n  final $Res Function(_ProxiesStyle) _then;\n\n/// Create a copy of ProxiesStyle\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? type = null,Object? sortType = null,Object? layout = null,Object? iconStyle = null,Object? cardType = null,Object? delayAnimation = null,Object? iconMap = null,Object? concurrencyLimit = null,}) {\n  return _then(_ProxiesStyle(\ntype: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable\nas ProxiesType,sortType: null == sortType ? _self.sortType : sortType // ignore: cast_nullable_to_non_nullable\nas ProxiesSortType,layout: null == layout ? _self.layout : layout // ignore: cast_nullable_to_non_nullable\nas ProxiesLayout,iconStyle: null == iconStyle ? _self.iconStyle : iconStyle // ignore: cast_nullable_to_non_nullable\nas ProxiesIconStyle,cardType: null == cardType ? _self.cardType : cardType // ignore: cast_nullable_to_non_nullable\nas ProxyCardType,delayAnimation: null == delayAnimation ? _self.delayAnimation : delayAnimation // ignore: cast_nullable_to_non_nullable\nas DelayAnimationType,iconMap: null == iconMap ? _self._iconMap : iconMap // ignore: cast_nullable_to_non_nullable\nas Map<String, String>,concurrencyLimit: null == concurrencyLimit ? _self.concurrencyLimit : concurrencyLimit // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$TextScale {\n\n bool get enable; double get scale;\n/// Create a copy of TextScale\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$TextScaleCopyWith<TextScale> get copyWith => _$TextScaleCopyWithImpl<TextScale>(this as TextScale, _$identity);\n\n  /// Serializes this TextScale to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is TextScale&&(identical(other.enable, enable) || other.enable == enable)&&(identical(other.scale, scale) || other.scale == scale));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,enable,scale);\n\n@override\nString toString() {\n  return 'TextScale(enable: $enable, scale: $scale)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $TextScaleCopyWith<$Res>  {\n  factory $TextScaleCopyWith(TextScale value, $Res Function(TextScale) _then) = _$TextScaleCopyWithImpl;\n@useResult\n$Res call({\n bool enable, double scale\n});\n\n\n\n\n}\n/// @nodoc\nclass _$TextScaleCopyWithImpl<$Res>\n    implements $TextScaleCopyWith<$Res> {\n  _$TextScaleCopyWithImpl(this._self, this._then);\n\n  final TextScale _self;\n  final $Res Function(TextScale) _then;\n\n/// Create a copy of TextScale\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? enable = null,Object? scale = null,}) {\n  return _then(_self.copyWith(\nenable: null == enable ? _self.enable : enable // ignore: cast_nullable_to_non_nullable\nas bool,scale: null == scale ? _self.scale : scale // ignore: cast_nullable_to_non_nullable\nas double,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [TextScale].\nextension TextScalePatterns on TextScale {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _TextScale value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _TextScale() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _TextScale value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _TextScale():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _TextScale value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _TextScale() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool enable,  double scale)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _TextScale() when $default != null:\nreturn $default(_that.enable,_that.scale);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool enable,  double scale)  $default,) {final _that = this;\nswitch (_that) {\ncase _TextScale():\nreturn $default(_that.enable,_that.scale);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool enable,  double scale)?  $default,) {final _that = this;\nswitch (_that) {\ncase _TextScale() when $default != null:\nreturn $default(_that.enable,_that.scale);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _TextScale implements TextScale {\n  const _TextScale({this.enable = false, this.scale = 1.0});\n  factory _TextScale.fromJson(Map<String, dynamic> json) => _$TextScaleFromJson(json);\n\n@override@JsonKey() final  bool enable;\n@override@JsonKey() final  double scale;\n\n/// Create a copy of TextScale\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$TextScaleCopyWith<_TextScale> get copyWith => __$TextScaleCopyWithImpl<_TextScale>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$TextScaleToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _TextScale&&(identical(other.enable, enable) || other.enable == enable)&&(identical(other.scale, scale) || other.scale == scale));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,enable,scale);\n\n@override\nString toString() {\n  return 'TextScale(enable: $enable, scale: $scale)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$TextScaleCopyWith<$Res> implements $TextScaleCopyWith<$Res> {\n  factory _$TextScaleCopyWith(_TextScale value, $Res Function(_TextScale) _then) = __$TextScaleCopyWithImpl;\n@override @useResult\n$Res call({\n bool enable, double scale\n});\n\n\n\n\n}\n/// @nodoc\nclass __$TextScaleCopyWithImpl<$Res>\n    implements _$TextScaleCopyWith<$Res> {\n  __$TextScaleCopyWithImpl(this._self, this._then);\n\n  final _TextScale _self;\n  final $Res Function(_TextScale) _then;\n\n/// Create a copy of TextScale\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? enable = null,Object? scale = null,}) {\n  return _then(_TextScale(\nenable: null == enable ? _self.enable : enable // ignore: cast_nullable_to_non_nullable\nas bool,scale: null == scale ? _self.scale : scale // ignore: cast_nullable_to_non_nullable\nas double,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$ThemeProps {\n\n int? get primaryColor; List<int> get primaryColors; ThemeMode get themeMode; DynamicSchemeVariant get schemeVariant; bool get pureBlack; TextScale get textScale; bool get useLightIcon; bool get useHarmonyFont;\n/// Create a copy of ThemeProps\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ThemePropsCopyWith<ThemeProps> get copyWith => _$ThemePropsCopyWithImpl<ThemeProps>(this as ThemeProps, _$identity);\n\n  /// Serializes this ThemeProps to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is ThemeProps&&(identical(other.primaryColor, primaryColor) || other.primaryColor == primaryColor)&&const DeepCollectionEquality().equals(other.primaryColors, primaryColors)&&(identical(other.themeMode, themeMode) || other.themeMode == themeMode)&&(identical(other.schemeVariant, schemeVariant) || other.schemeVariant == schemeVariant)&&(identical(other.pureBlack, pureBlack) || other.pureBlack == pureBlack)&&(identical(other.textScale, textScale) || other.textScale == textScale)&&(identical(other.useLightIcon, useLightIcon) || other.useLightIcon == useLightIcon)&&(identical(other.useHarmonyFont, useHarmonyFont) || other.useHarmonyFont == useHarmonyFont));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,primaryColor,const DeepCollectionEquality().hash(primaryColors),themeMode,schemeVariant,pureBlack,textScale,useLightIcon,useHarmonyFont);\n\n@override\nString toString() {\n  return 'ThemeProps(primaryColor: $primaryColor, primaryColors: $primaryColors, themeMode: $themeMode, schemeVariant: $schemeVariant, pureBlack: $pureBlack, textScale: $textScale, useLightIcon: $useLightIcon, useHarmonyFont: $useHarmonyFont)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ThemePropsCopyWith<$Res>  {\n  factory $ThemePropsCopyWith(ThemeProps value, $Res Function(ThemeProps) _then) = _$ThemePropsCopyWithImpl;\n@useResult\n$Res call({\n int? primaryColor, List<int> primaryColors, ThemeMode themeMode, DynamicSchemeVariant schemeVariant, bool pureBlack, TextScale textScale, bool useLightIcon, bool useHarmonyFont\n});\n\n\n$TextScaleCopyWith<$Res> get textScale;\n\n}\n/// @nodoc\nclass _$ThemePropsCopyWithImpl<$Res>\n    implements $ThemePropsCopyWith<$Res> {\n  _$ThemePropsCopyWithImpl(this._self, this._then);\n\n  final ThemeProps _self;\n  final $Res Function(ThemeProps) _then;\n\n/// Create a copy of ThemeProps\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? primaryColor = freezed,Object? primaryColors = null,Object? themeMode = null,Object? schemeVariant = null,Object? pureBlack = null,Object? textScale = null,Object? useLightIcon = null,Object? useHarmonyFont = null,}) {\n  return _then(_self.copyWith(\nprimaryColor: freezed == primaryColor ? _self.primaryColor : primaryColor // ignore: cast_nullable_to_non_nullable\nas int?,primaryColors: null == primaryColors ? _self.primaryColors : primaryColors // ignore: cast_nullable_to_non_nullable\nas List<int>,themeMode: null == themeMode ? _self.themeMode : themeMode // ignore: cast_nullable_to_non_nullable\nas ThemeMode,schemeVariant: null == schemeVariant ? _self.schemeVariant : schemeVariant // ignore: cast_nullable_to_non_nullable\nas DynamicSchemeVariant,pureBlack: null == pureBlack ? _self.pureBlack : pureBlack // ignore: cast_nullable_to_non_nullable\nas bool,textScale: null == textScale ? _self.textScale : textScale // ignore: cast_nullable_to_non_nullable\nas TextScale,useLightIcon: null == useLightIcon ? _self.useLightIcon : useLightIcon // ignore: cast_nullable_to_non_nullable\nas bool,useHarmonyFont: null == useHarmonyFont ? _self.useHarmonyFont : useHarmonyFont // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n/// Create a copy of ThemeProps\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$TextScaleCopyWith<$Res> get textScale {\n  \n  return $TextScaleCopyWith<$Res>(_self.textScale, (value) {\n    return _then(_self.copyWith(textScale: value));\n  });\n}\n}\n\n\n/// Adds pattern-matching-related methods to [ThemeProps].\nextension ThemePropsPatterns on ThemeProps {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ThemeProps value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _ThemeProps() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ThemeProps value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ThemeProps():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ThemeProps value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ThemeProps() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( int? primaryColor,  List<int> primaryColors,  ThemeMode themeMode,  DynamicSchemeVariant schemeVariant,  bool pureBlack,  TextScale textScale,  bool useLightIcon,  bool useHarmonyFont)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _ThemeProps() when $default != null:\nreturn $default(_that.primaryColor,_that.primaryColors,_that.themeMode,_that.schemeVariant,_that.pureBlack,_that.textScale,_that.useLightIcon,_that.useHarmonyFont);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( int? primaryColor,  List<int> primaryColors,  ThemeMode themeMode,  DynamicSchemeVariant schemeVariant,  bool pureBlack,  TextScale textScale,  bool useLightIcon,  bool useHarmonyFont)  $default,) {final _that = this;\nswitch (_that) {\ncase _ThemeProps():\nreturn $default(_that.primaryColor,_that.primaryColors,_that.themeMode,_that.schemeVariant,_that.pureBlack,_that.textScale,_that.useLightIcon,_that.useHarmonyFont);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( int? primaryColor,  List<int> primaryColors,  ThemeMode themeMode,  DynamicSchemeVariant schemeVariant,  bool pureBlack,  TextScale textScale,  bool useLightIcon,  bool useHarmonyFont)?  $default,) {final _that = this;\nswitch (_that) {\ncase _ThemeProps() when $default != null:\nreturn $default(_that.primaryColor,_that.primaryColors,_that.themeMode,_that.schemeVariant,_that.pureBlack,_that.textScale,_that.useLightIcon,_that.useHarmonyFont);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _ThemeProps implements ThemeProps {\n  const _ThemeProps({this.primaryColor, final  List<int> primaryColors = defaultPrimaryColors, this.themeMode = ThemeMode.system, this.schemeVariant = DynamicSchemeVariant.content, this.pureBlack = false, this.textScale = const TextScale(), this.useLightIcon = false, this.useHarmonyFont = false}): _primaryColors = primaryColors;\n  factory _ThemeProps.fromJson(Map<String, dynamic> json) => _$ThemePropsFromJson(json);\n\n@override final  int? primaryColor;\n final  List<int> _primaryColors;\n@override@JsonKey() List<int> get primaryColors {\n  if (_primaryColors is EqualUnmodifiableListView) return _primaryColors;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_primaryColors);\n}\n\n@override@JsonKey() final  ThemeMode themeMode;\n@override@JsonKey() final  DynamicSchemeVariant schemeVariant;\n@override@JsonKey() final  bool pureBlack;\n@override@JsonKey() final  TextScale textScale;\n@override@JsonKey() final  bool useLightIcon;\n@override@JsonKey() final  bool useHarmonyFont;\n\n/// Create a copy of ThemeProps\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ThemePropsCopyWith<_ThemeProps> get copyWith => __$ThemePropsCopyWithImpl<_ThemeProps>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$ThemePropsToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _ThemeProps&&(identical(other.primaryColor, primaryColor) || other.primaryColor == primaryColor)&&const DeepCollectionEquality().equals(other._primaryColors, _primaryColors)&&(identical(other.themeMode, themeMode) || other.themeMode == themeMode)&&(identical(other.schemeVariant, schemeVariant) || other.schemeVariant == schemeVariant)&&(identical(other.pureBlack, pureBlack) || other.pureBlack == pureBlack)&&(identical(other.textScale, textScale) || other.textScale == textScale)&&(identical(other.useLightIcon, useLightIcon) || other.useLightIcon == useLightIcon)&&(identical(other.useHarmonyFont, useHarmonyFont) || other.useHarmonyFont == useHarmonyFont));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,primaryColor,const DeepCollectionEquality().hash(_primaryColors),themeMode,schemeVariant,pureBlack,textScale,useLightIcon,useHarmonyFont);\n\n@override\nString toString() {\n  return 'ThemeProps(primaryColor: $primaryColor, primaryColors: $primaryColors, themeMode: $themeMode, schemeVariant: $schemeVariant, pureBlack: $pureBlack, textScale: $textScale, useLightIcon: $useLightIcon, useHarmonyFont: $useHarmonyFont)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ThemePropsCopyWith<$Res> implements $ThemePropsCopyWith<$Res> {\n  factory _$ThemePropsCopyWith(_ThemeProps value, $Res Function(_ThemeProps) _then) = __$ThemePropsCopyWithImpl;\n@override @useResult\n$Res call({\n int? primaryColor, List<int> primaryColors, ThemeMode themeMode, DynamicSchemeVariant schemeVariant, bool pureBlack, TextScale textScale, bool useLightIcon, bool useHarmonyFont\n});\n\n\n@override $TextScaleCopyWith<$Res> get textScale;\n\n}\n/// @nodoc\nclass __$ThemePropsCopyWithImpl<$Res>\n    implements _$ThemePropsCopyWith<$Res> {\n  __$ThemePropsCopyWithImpl(this._self, this._then);\n\n  final _ThemeProps _self;\n  final $Res Function(_ThemeProps) _then;\n\n/// Create a copy of ThemeProps\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? primaryColor = freezed,Object? primaryColors = null,Object? themeMode = null,Object? schemeVariant = null,Object? pureBlack = null,Object? textScale = null,Object? useLightIcon = null,Object? useHarmonyFont = null,}) {\n  return _then(_ThemeProps(\nprimaryColor: freezed == primaryColor ? _self.primaryColor : primaryColor // ignore: cast_nullable_to_non_nullable\nas int?,primaryColors: null == primaryColors ? _self._primaryColors : primaryColors // ignore: cast_nullable_to_non_nullable\nas List<int>,themeMode: null == themeMode ? _self.themeMode : themeMode // ignore: cast_nullable_to_non_nullable\nas ThemeMode,schemeVariant: null == schemeVariant ? _self.schemeVariant : schemeVariant // ignore: cast_nullable_to_non_nullable\nas DynamicSchemeVariant,pureBlack: null == pureBlack ? _self.pureBlack : pureBlack // ignore: cast_nullable_to_non_nullable\nas bool,textScale: null == textScale ? _self.textScale : textScale // ignore: cast_nullable_to_non_nullable\nas TextScale,useLightIcon: null == useLightIcon ? _self.useLightIcon : useLightIcon // ignore: cast_nullable_to_non_nullable\nas bool,useHarmonyFont: null == useHarmonyFont ? _self.useHarmonyFont : useHarmonyFont // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n/// Create a copy of ThemeProps\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$TextScaleCopyWith<$Res> get textScale {\n  \n  return $TextScaleCopyWith<$Res>(_self.textScale, (value) {\n    return _then(_self.copyWith(textScale: value));\n  });\n}\n}\n\n\n/// @nodoc\nmixin _$ScriptProps {\n\n String? get currentId; List<Script> get scripts;\n/// Create a copy of ScriptProps\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ScriptPropsCopyWith<ScriptProps> get copyWith => _$ScriptPropsCopyWithImpl<ScriptProps>(this as ScriptProps, _$identity);\n\n  /// Serializes this ScriptProps to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is ScriptProps&&(identical(other.currentId, currentId) || other.currentId == currentId)&&const DeepCollectionEquality().equals(other.scripts, scripts));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,currentId,const DeepCollectionEquality().hash(scripts));\n\n@override\nString toString() {\n  return 'ScriptProps(currentId: $currentId, scripts: $scripts)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ScriptPropsCopyWith<$Res>  {\n  factory $ScriptPropsCopyWith(ScriptProps value, $Res Function(ScriptProps) _then) = _$ScriptPropsCopyWithImpl;\n@useResult\n$Res call({\n String? currentId, List<Script> scripts\n});\n\n\n\n\n}\n/// @nodoc\nclass _$ScriptPropsCopyWithImpl<$Res>\n    implements $ScriptPropsCopyWith<$Res> {\n  _$ScriptPropsCopyWithImpl(this._self, this._then);\n\n  final ScriptProps _self;\n  final $Res Function(ScriptProps) _then;\n\n/// Create a copy of ScriptProps\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? currentId = freezed,Object? scripts = null,}) {\n  return _then(_self.copyWith(\ncurrentId: freezed == currentId ? _self.currentId : currentId // ignore: cast_nullable_to_non_nullable\nas String?,scripts: null == scripts ? _self.scripts : scripts // ignore: cast_nullable_to_non_nullable\nas List<Script>,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [ScriptProps].\nextension ScriptPropsPatterns on ScriptProps {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ScriptProps value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _ScriptProps() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ScriptProps value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ScriptProps():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ScriptProps value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ScriptProps() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String? currentId,  List<Script> scripts)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _ScriptProps() when $default != null:\nreturn $default(_that.currentId,_that.scripts);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String? currentId,  List<Script> scripts)  $default,) {final _that = this;\nswitch (_that) {\ncase _ScriptProps():\nreturn $default(_that.currentId,_that.scripts);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String? currentId,  List<Script> scripts)?  $default,) {final _that = this;\nswitch (_that) {\ncase _ScriptProps() when $default != null:\nreturn $default(_that.currentId,_that.scripts);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _ScriptProps implements ScriptProps {\n  const _ScriptProps({this.currentId, final  List<Script> scripts = const []}): _scripts = scripts;\n  factory _ScriptProps.fromJson(Map<String, dynamic> json) => _$ScriptPropsFromJson(json);\n\n@override final  String? currentId;\n final  List<Script> _scripts;\n@override@JsonKey() List<Script> get scripts {\n  if (_scripts is EqualUnmodifiableListView) return _scripts;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_scripts);\n}\n\n\n/// Create a copy of ScriptProps\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ScriptPropsCopyWith<_ScriptProps> get copyWith => __$ScriptPropsCopyWithImpl<_ScriptProps>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$ScriptPropsToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _ScriptProps&&(identical(other.currentId, currentId) || other.currentId == currentId)&&const DeepCollectionEquality().equals(other._scripts, _scripts));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,currentId,const DeepCollectionEquality().hash(_scripts));\n\n@override\nString toString() {\n  return 'ScriptProps(currentId: $currentId, scripts: $scripts)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ScriptPropsCopyWith<$Res> implements $ScriptPropsCopyWith<$Res> {\n  factory _$ScriptPropsCopyWith(_ScriptProps value, $Res Function(_ScriptProps) _then) = __$ScriptPropsCopyWithImpl;\n@override @useResult\n$Res call({\n String? currentId, List<Script> scripts\n});\n\n\n\n\n}\n/// @nodoc\nclass __$ScriptPropsCopyWithImpl<$Res>\n    implements _$ScriptPropsCopyWith<$Res> {\n  __$ScriptPropsCopyWithImpl(this._self, this._then);\n\n  final _ScriptProps _self;\n  final $Res Function(_ScriptProps) _then;\n\n/// Create a copy of ScriptProps\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? currentId = freezed,Object? scripts = null,}) {\n  return _then(_ScriptProps(\ncurrentId: freezed == currentId ? _self.currentId : currentId // ignore: cast_nullable_to_non_nullable\nas String?,scripts: null == scripts ? _self._scripts : scripts // ignore: cast_nullable_to_non_nullable\nas List<Script>,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$Config {\n\n@JsonKey(fromJson: AppSettingProps.safeFromJson) AppSettingProps get appSetting; List<Profile> get profiles; List<HotKeyAction> get hotKeyActions; String? get currentProfileId; bool get overrideDns; bool get overrideNtp; bool get overrideSniffer; bool get overrideTunnel; bool get overrideExperimental; bool get overrideTestUrl; DAV? get dav; NetworkProps get networkProps;@JsonKey(fromJson: VpnProps.safeFromJson) VpnProps get vpnProps;@JsonKey(fromJson: ThemeProps.safeFromJson) ThemeProps get themeProps; ProxiesStyle get proxiesStyle; WindowProps get windowProps; ClashConfig get patchClashConfig; ScriptProps get scriptProps; String get nodeExcludeFilter; int get healthCheckTimeout;\n/// Create a copy of Config\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ConfigCopyWith<Config> get copyWith => _$ConfigCopyWithImpl<Config>(this as Config, _$identity);\n\n  /// Serializes this Config to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is Config&&(identical(other.appSetting, appSetting) || other.appSetting == appSetting)&&const DeepCollectionEquality().equals(other.profiles, profiles)&&const DeepCollectionEquality().equals(other.hotKeyActions, hotKeyActions)&&(identical(other.currentProfileId, currentProfileId) || other.currentProfileId == currentProfileId)&&(identical(other.overrideDns, overrideDns) || other.overrideDns == overrideDns)&&(identical(other.overrideNtp, overrideNtp) || other.overrideNtp == overrideNtp)&&(identical(other.overrideSniffer, overrideSniffer) || other.overrideSniffer == overrideSniffer)&&(identical(other.overrideTunnel, overrideTunnel) || other.overrideTunnel == overrideTunnel)&&(identical(other.overrideExperimental, overrideExperimental) || other.overrideExperimental == overrideExperimental)&&(identical(other.overrideTestUrl, overrideTestUrl) || other.overrideTestUrl == overrideTestUrl)&&(identical(other.dav, dav) || other.dav == dav)&&(identical(other.networkProps, networkProps) || other.networkProps == networkProps)&&(identical(other.vpnProps, vpnProps) || other.vpnProps == vpnProps)&&(identical(other.themeProps, themeProps) || other.themeProps == themeProps)&&(identical(other.proxiesStyle, proxiesStyle) || other.proxiesStyle == proxiesStyle)&&(identical(other.windowProps, windowProps) || other.windowProps == windowProps)&&(identical(other.patchClashConfig, patchClashConfig) || other.patchClashConfig == patchClashConfig)&&(identical(other.scriptProps, scriptProps) || other.scriptProps == scriptProps)&&(identical(other.nodeExcludeFilter, nodeExcludeFilter) || other.nodeExcludeFilter == nodeExcludeFilter)&&(identical(other.healthCheckTimeout, healthCheckTimeout) || other.healthCheckTimeout == healthCheckTimeout));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hashAll([runtimeType,appSetting,const DeepCollectionEquality().hash(profiles),const DeepCollectionEquality().hash(hotKeyActions),currentProfileId,overrideDns,overrideNtp,overrideSniffer,overrideTunnel,overrideExperimental,overrideTestUrl,dav,networkProps,vpnProps,themeProps,proxiesStyle,windowProps,patchClashConfig,scriptProps,nodeExcludeFilter,healthCheckTimeout]);\n\n@override\nString toString() {\n  return 'Config(appSetting: $appSetting, profiles: $profiles, hotKeyActions: $hotKeyActions, currentProfileId: $currentProfileId, overrideDns: $overrideDns, overrideNtp: $overrideNtp, overrideSniffer: $overrideSniffer, overrideTunnel: $overrideTunnel, overrideExperimental: $overrideExperimental, overrideTestUrl: $overrideTestUrl, dav: $dav, networkProps: $networkProps, vpnProps: $vpnProps, themeProps: $themeProps, proxiesStyle: $proxiesStyle, windowProps: $windowProps, patchClashConfig: $patchClashConfig, scriptProps: $scriptProps, nodeExcludeFilter: $nodeExcludeFilter, healthCheckTimeout: $healthCheckTimeout)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ConfigCopyWith<$Res>  {\n  factory $ConfigCopyWith(Config value, $Res Function(Config) _then) = _$ConfigCopyWithImpl;\n@useResult\n$Res call({\n@JsonKey(fromJson: AppSettingProps.safeFromJson) AppSettingProps appSetting, List<Profile> profiles, List<HotKeyAction> hotKeyActions, String? currentProfileId, bool overrideDns, bool overrideNtp, bool overrideSniffer, bool overrideTunnel, bool overrideExperimental, bool overrideTestUrl, DAV? dav, NetworkProps networkProps,@JsonKey(fromJson: VpnProps.safeFromJson) VpnProps vpnProps,@JsonKey(fromJson: ThemeProps.safeFromJson) ThemeProps themeProps, ProxiesStyle proxiesStyle, WindowProps windowProps, ClashConfig patchClashConfig, ScriptProps scriptProps, String nodeExcludeFilter, int healthCheckTimeout\n});\n\n\n$AppSettingPropsCopyWith<$Res> get appSetting;$DAVCopyWith<$Res>? get dav;$NetworkPropsCopyWith<$Res> get networkProps;$VpnPropsCopyWith<$Res> get vpnProps;$ThemePropsCopyWith<$Res> get themeProps;$ProxiesStyleCopyWith<$Res> get proxiesStyle;$WindowPropsCopyWith<$Res> get windowProps;$ClashConfigCopyWith<$Res> get patchClashConfig;$ScriptPropsCopyWith<$Res> get scriptProps;\n\n}\n/// @nodoc\nclass _$ConfigCopyWithImpl<$Res>\n    implements $ConfigCopyWith<$Res> {\n  _$ConfigCopyWithImpl(this._self, this._then);\n\n  final Config _self;\n  final $Res Function(Config) _then;\n\n/// Create a copy of Config\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? appSetting = null,Object? profiles = null,Object? hotKeyActions = null,Object? currentProfileId = freezed,Object? overrideDns = null,Object? overrideNtp = null,Object? overrideSniffer = null,Object? overrideTunnel = null,Object? overrideExperimental = null,Object? overrideTestUrl = null,Object? dav = freezed,Object? networkProps = null,Object? vpnProps = null,Object? themeProps = null,Object? proxiesStyle = null,Object? windowProps = null,Object? patchClashConfig = null,Object? scriptProps = null,Object? nodeExcludeFilter = null,Object? healthCheckTimeout = null,}) {\n  return _then(_self.copyWith(\nappSetting: null == appSetting ? _self.appSetting : appSetting // ignore: cast_nullable_to_non_nullable\nas AppSettingProps,profiles: null == profiles ? _self.profiles : profiles // ignore: cast_nullable_to_non_nullable\nas List<Profile>,hotKeyActions: null == hotKeyActions ? _self.hotKeyActions : hotKeyActions // ignore: cast_nullable_to_non_nullable\nas List<HotKeyAction>,currentProfileId: freezed == currentProfileId ? _self.currentProfileId : currentProfileId // ignore: cast_nullable_to_non_nullable\nas String?,overrideDns: null == overrideDns ? _self.overrideDns : overrideDns // ignore: cast_nullable_to_non_nullable\nas bool,overrideNtp: null == overrideNtp ? _self.overrideNtp : overrideNtp // ignore: cast_nullable_to_non_nullable\nas bool,overrideSniffer: null == overrideSniffer ? _self.overrideSniffer : overrideSniffer // ignore: cast_nullable_to_non_nullable\nas bool,overrideTunnel: null == overrideTunnel ? _self.overrideTunnel : overrideTunnel // ignore: cast_nullable_to_non_nullable\nas bool,overrideExperimental: null == overrideExperimental ? _self.overrideExperimental : overrideExperimental // ignore: cast_nullable_to_non_nullable\nas bool,overrideTestUrl: null == overrideTestUrl ? _self.overrideTestUrl : overrideTestUrl // ignore: cast_nullable_to_non_nullable\nas bool,dav: freezed == dav ? _self.dav : dav // ignore: cast_nullable_to_non_nullable\nas DAV?,networkProps: null == networkProps ? _self.networkProps : networkProps // ignore: cast_nullable_to_non_nullable\nas NetworkProps,vpnProps: null == vpnProps ? _self.vpnProps : vpnProps // ignore: cast_nullable_to_non_nullable\nas VpnProps,themeProps: null == themeProps ? _self.themeProps : themeProps // ignore: cast_nullable_to_non_nullable\nas ThemeProps,proxiesStyle: null == proxiesStyle ? _self.proxiesStyle : proxiesStyle // ignore: cast_nullable_to_non_nullable\nas ProxiesStyle,windowProps: null == windowProps ? _self.windowProps : windowProps // ignore: cast_nullable_to_non_nullable\nas WindowProps,patchClashConfig: null == patchClashConfig ? _self.patchClashConfig : patchClashConfig // ignore: cast_nullable_to_non_nullable\nas ClashConfig,scriptProps: null == scriptProps ? _self.scriptProps : scriptProps // ignore: cast_nullable_to_non_nullable\nas ScriptProps,nodeExcludeFilter: null == nodeExcludeFilter ? _self.nodeExcludeFilter : nodeExcludeFilter // ignore: cast_nullable_to_non_nullable\nas String,healthCheckTimeout: null == healthCheckTimeout ? _self.healthCheckTimeout : healthCheckTimeout // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n/// Create a copy of Config\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$AppSettingPropsCopyWith<$Res> get appSetting {\n  \n  return $AppSettingPropsCopyWith<$Res>(_self.appSetting, (value) {\n    return _then(_self.copyWith(appSetting: value));\n  });\n}/// Create a copy of Config\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$DAVCopyWith<$Res>? get dav {\n    if (_self.dav == null) {\n    return null;\n  }\n\n  return $DAVCopyWith<$Res>(_self.dav!, (value) {\n    return _then(_self.copyWith(dav: value));\n  });\n}/// Create a copy of Config\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$NetworkPropsCopyWith<$Res> get networkProps {\n  \n  return $NetworkPropsCopyWith<$Res>(_self.networkProps, (value) {\n    return _then(_self.copyWith(networkProps: value));\n  });\n}/// Create a copy of Config\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$VpnPropsCopyWith<$Res> get vpnProps {\n  \n  return $VpnPropsCopyWith<$Res>(_self.vpnProps, (value) {\n    return _then(_self.copyWith(vpnProps: value));\n  });\n}/// Create a copy of Config\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$ThemePropsCopyWith<$Res> get themeProps {\n  \n  return $ThemePropsCopyWith<$Res>(_self.themeProps, (value) {\n    return _then(_self.copyWith(themeProps: value));\n  });\n}/// Create a copy of Config\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$ProxiesStyleCopyWith<$Res> get proxiesStyle {\n  \n  return $ProxiesStyleCopyWith<$Res>(_self.proxiesStyle, (value) {\n    return _then(_self.copyWith(proxiesStyle: value));\n  });\n}/// Create a copy of Config\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$WindowPropsCopyWith<$Res> get windowProps {\n  \n  return $WindowPropsCopyWith<$Res>(_self.windowProps, (value) {\n    return _then(_self.copyWith(windowProps: value));\n  });\n}/// Create a copy of Config\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$ClashConfigCopyWith<$Res> get patchClashConfig {\n  \n  return $ClashConfigCopyWith<$Res>(_self.patchClashConfig, (value) {\n    return _then(_self.copyWith(patchClashConfig: value));\n  });\n}/// Create a copy of Config\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$ScriptPropsCopyWith<$Res> get scriptProps {\n  \n  return $ScriptPropsCopyWith<$Res>(_self.scriptProps, (value) {\n    return _then(_self.copyWith(scriptProps: value));\n  });\n}\n}\n\n\n/// Adds pattern-matching-related methods to [Config].\nextension ConfigPatterns on Config {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _Config value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _Config() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _Config value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Config():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _Config value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Config() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(@JsonKey(fromJson: AppSettingProps.safeFromJson)  AppSettingProps appSetting,  List<Profile> profiles,  List<HotKeyAction> hotKeyActions,  String? currentProfileId,  bool overrideDns,  bool overrideNtp,  bool overrideSniffer,  bool overrideTunnel,  bool overrideExperimental,  bool overrideTestUrl,  DAV? dav,  NetworkProps networkProps, @JsonKey(fromJson: VpnProps.safeFromJson)  VpnProps vpnProps, @JsonKey(fromJson: ThemeProps.safeFromJson)  ThemeProps themeProps,  ProxiesStyle proxiesStyle,  WindowProps windowProps,  ClashConfig patchClashConfig,  ScriptProps scriptProps,  String nodeExcludeFilter,  int healthCheckTimeout)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _Config() when $default != null:\nreturn $default(_that.appSetting,_that.profiles,_that.hotKeyActions,_that.currentProfileId,_that.overrideDns,_that.overrideNtp,_that.overrideSniffer,_that.overrideTunnel,_that.overrideExperimental,_that.overrideTestUrl,_that.dav,_that.networkProps,_that.vpnProps,_that.themeProps,_that.proxiesStyle,_that.windowProps,_that.patchClashConfig,_that.scriptProps,_that.nodeExcludeFilter,_that.healthCheckTimeout);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(@JsonKey(fromJson: AppSettingProps.safeFromJson)  AppSettingProps appSetting,  List<Profile> profiles,  List<HotKeyAction> hotKeyActions,  String? currentProfileId,  bool overrideDns,  bool overrideNtp,  bool overrideSniffer,  bool overrideTunnel,  bool overrideExperimental,  bool overrideTestUrl,  DAV? dav,  NetworkProps networkProps, @JsonKey(fromJson: VpnProps.safeFromJson)  VpnProps vpnProps, @JsonKey(fromJson: ThemeProps.safeFromJson)  ThemeProps themeProps,  ProxiesStyle proxiesStyle,  WindowProps windowProps,  ClashConfig patchClashConfig,  ScriptProps scriptProps,  String nodeExcludeFilter,  int healthCheckTimeout)  $default,) {final _that = this;\nswitch (_that) {\ncase _Config():\nreturn $default(_that.appSetting,_that.profiles,_that.hotKeyActions,_that.currentProfileId,_that.overrideDns,_that.overrideNtp,_that.overrideSniffer,_that.overrideTunnel,_that.overrideExperimental,_that.overrideTestUrl,_that.dav,_that.networkProps,_that.vpnProps,_that.themeProps,_that.proxiesStyle,_that.windowProps,_that.patchClashConfig,_that.scriptProps,_that.nodeExcludeFilter,_that.healthCheckTimeout);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(@JsonKey(fromJson: AppSettingProps.safeFromJson)  AppSettingProps appSetting,  List<Profile> profiles,  List<HotKeyAction> hotKeyActions,  String? currentProfileId,  bool overrideDns,  bool overrideNtp,  bool overrideSniffer,  bool overrideTunnel,  bool overrideExperimental,  bool overrideTestUrl,  DAV? dav,  NetworkProps networkProps, @JsonKey(fromJson: VpnProps.safeFromJson)  VpnProps vpnProps, @JsonKey(fromJson: ThemeProps.safeFromJson)  ThemeProps themeProps,  ProxiesStyle proxiesStyle,  WindowProps windowProps,  ClashConfig patchClashConfig,  ScriptProps scriptProps,  String nodeExcludeFilter,  int healthCheckTimeout)?  $default,) {final _that = this;\nswitch (_that) {\ncase _Config() when $default != null:\nreturn $default(_that.appSetting,_that.profiles,_that.hotKeyActions,_that.currentProfileId,_that.overrideDns,_that.overrideNtp,_that.overrideSniffer,_that.overrideTunnel,_that.overrideExperimental,_that.overrideTestUrl,_that.dav,_that.networkProps,_that.vpnProps,_that.themeProps,_that.proxiesStyle,_that.windowProps,_that.patchClashConfig,_that.scriptProps,_that.nodeExcludeFilter,_that.healthCheckTimeout);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _Config implements Config {\n  const _Config({@JsonKey(fromJson: AppSettingProps.safeFromJson) this.appSetting = defaultAppSettingProps, final  List<Profile> profiles = const [], final  List<HotKeyAction> hotKeyActions = const [], this.currentProfileId, this.overrideDns = false, this.overrideNtp = false, this.overrideSniffer = false, this.overrideTunnel = false, this.overrideExperimental = false, this.overrideTestUrl = true, this.dav, this.networkProps = defaultNetworkProps, @JsonKey(fromJson: VpnProps.safeFromJson) this.vpnProps = defaultVpnProps, @JsonKey(fromJson: ThemeProps.safeFromJson) required this.themeProps, this.proxiesStyle = defaultProxiesStyle, this.windowProps = defaultWindowProps, this.patchClashConfig = defaultClashConfig, this.scriptProps = const ScriptProps(), this.nodeExcludeFilter = '', this.healthCheckTimeout = 5000}): _profiles = profiles,_hotKeyActions = hotKeyActions;\n  factory _Config.fromJson(Map<String, dynamic> json) => _$ConfigFromJson(json);\n\n@override@JsonKey(fromJson: AppSettingProps.safeFromJson) final  AppSettingProps appSetting;\n final  List<Profile> _profiles;\n@override@JsonKey() List<Profile> get profiles {\n  if (_profiles is EqualUnmodifiableListView) return _profiles;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_profiles);\n}\n\n final  List<HotKeyAction> _hotKeyActions;\n@override@JsonKey() List<HotKeyAction> get hotKeyActions {\n  if (_hotKeyActions is EqualUnmodifiableListView) return _hotKeyActions;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_hotKeyActions);\n}\n\n@override final  String? currentProfileId;\n@override@JsonKey() final  bool overrideDns;\n@override@JsonKey() final  bool overrideNtp;\n@override@JsonKey() final  bool overrideSniffer;\n@override@JsonKey() final  bool overrideTunnel;\n@override@JsonKey() final  bool overrideExperimental;\n@override@JsonKey() final  bool overrideTestUrl;\n@override final  DAV? dav;\n@override@JsonKey() final  NetworkProps networkProps;\n@override@JsonKey(fromJson: VpnProps.safeFromJson) final  VpnProps vpnProps;\n@override@JsonKey(fromJson: ThemeProps.safeFromJson) final  ThemeProps themeProps;\n@override@JsonKey() final  ProxiesStyle proxiesStyle;\n@override@JsonKey() final  WindowProps windowProps;\n@override@JsonKey() final  ClashConfig patchClashConfig;\n@override@JsonKey() final  ScriptProps scriptProps;\n@override@JsonKey() final  String nodeExcludeFilter;\n@override@JsonKey() final  int healthCheckTimeout;\n\n/// Create a copy of Config\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ConfigCopyWith<_Config> get copyWith => __$ConfigCopyWithImpl<_Config>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$ConfigToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _Config&&(identical(other.appSetting, appSetting) || other.appSetting == appSetting)&&const DeepCollectionEquality().equals(other._profiles, _profiles)&&const DeepCollectionEquality().equals(other._hotKeyActions, _hotKeyActions)&&(identical(other.currentProfileId, currentProfileId) || other.currentProfileId == currentProfileId)&&(identical(other.overrideDns, overrideDns) || other.overrideDns == overrideDns)&&(identical(other.overrideNtp, overrideNtp) || other.overrideNtp == overrideNtp)&&(identical(other.overrideSniffer, overrideSniffer) || other.overrideSniffer == overrideSniffer)&&(identical(other.overrideTunnel, overrideTunnel) || other.overrideTunnel == overrideTunnel)&&(identical(other.overrideExperimental, overrideExperimental) || other.overrideExperimental == overrideExperimental)&&(identical(other.overrideTestUrl, overrideTestUrl) || other.overrideTestUrl == overrideTestUrl)&&(identical(other.dav, dav) || other.dav == dav)&&(identical(other.networkProps, networkProps) || other.networkProps == networkProps)&&(identical(other.vpnProps, vpnProps) || other.vpnProps == vpnProps)&&(identical(other.themeProps, themeProps) || other.themeProps == themeProps)&&(identical(other.proxiesStyle, proxiesStyle) || other.proxiesStyle == proxiesStyle)&&(identical(other.windowProps, windowProps) || other.windowProps == windowProps)&&(identical(other.patchClashConfig, patchClashConfig) || other.patchClashConfig == patchClashConfig)&&(identical(other.scriptProps, scriptProps) || other.scriptProps == scriptProps)&&(identical(other.nodeExcludeFilter, nodeExcludeFilter) || other.nodeExcludeFilter == nodeExcludeFilter)&&(identical(other.healthCheckTimeout, healthCheckTimeout) || other.healthCheckTimeout == healthCheckTimeout));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hashAll([runtimeType,appSetting,const DeepCollectionEquality().hash(_profiles),const DeepCollectionEquality().hash(_hotKeyActions),currentProfileId,overrideDns,overrideNtp,overrideSniffer,overrideTunnel,overrideExperimental,overrideTestUrl,dav,networkProps,vpnProps,themeProps,proxiesStyle,windowProps,patchClashConfig,scriptProps,nodeExcludeFilter,healthCheckTimeout]);\n\n@override\nString toString() {\n  return 'Config(appSetting: $appSetting, profiles: $profiles, hotKeyActions: $hotKeyActions, currentProfileId: $currentProfileId, overrideDns: $overrideDns, overrideNtp: $overrideNtp, overrideSniffer: $overrideSniffer, overrideTunnel: $overrideTunnel, overrideExperimental: $overrideExperimental, overrideTestUrl: $overrideTestUrl, dav: $dav, networkProps: $networkProps, vpnProps: $vpnProps, themeProps: $themeProps, proxiesStyle: $proxiesStyle, windowProps: $windowProps, patchClashConfig: $patchClashConfig, scriptProps: $scriptProps, nodeExcludeFilter: $nodeExcludeFilter, healthCheckTimeout: $healthCheckTimeout)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ConfigCopyWith<$Res> implements $ConfigCopyWith<$Res> {\n  factory _$ConfigCopyWith(_Config value, $Res Function(_Config) _then) = __$ConfigCopyWithImpl;\n@override @useResult\n$Res call({\n@JsonKey(fromJson: AppSettingProps.safeFromJson) AppSettingProps appSetting, List<Profile> profiles, List<HotKeyAction> hotKeyActions, String? currentProfileId, bool overrideDns, bool overrideNtp, bool overrideSniffer, bool overrideTunnel, bool overrideExperimental, bool overrideTestUrl, DAV? dav, NetworkProps networkProps,@JsonKey(fromJson: VpnProps.safeFromJson) VpnProps vpnProps,@JsonKey(fromJson: ThemeProps.safeFromJson) ThemeProps themeProps, ProxiesStyle proxiesStyle, WindowProps windowProps, ClashConfig patchClashConfig, ScriptProps scriptProps, String nodeExcludeFilter, int healthCheckTimeout\n});\n\n\n@override $AppSettingPropsCopyWith<$Res> get appSetting;@override $DAVCopyWith<$Res>? get dav;@override $NetworkPropsCopyWith<$Res> get networkProps;@override $VpnPropsCopyWith<$Res> get vpnProps;@override $ThemePropsCopyWith<$Res> get themeProps;@override $ProxiesStyleCopyWith<$Res> get proxiesStyle;@override $WindowPropsCopyWith<$Res> get windowProps;@override $ClashConfigCopyWith<$Res> get patchClashConfig;@override $ScriptPropsCopyWith<$Res> get scriptProps;\n\n}\n/// @nodoc\nclass __$ConfigCopyWithImpl<$Res>\n    implements _$ConfigCopyWith<$Res> {\n  __$ConfigCopyWithImpl(this._self, this._then);\n\n  final _Config _self;\n  final $Res Function(_Config) _then;\n\n/// Create a copy of Config\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? appSetting = null,Object? profiles = null,Object? hotKeyActions = null,Object? currentProfileId = freezed,Object? overrideDns = null,Object? overrideNtp = null,Object? overrideSniffer = null,Object? overrideTunnel = null,Object? overrideExperimental = null,Object? overrideTestUrl = null,Object? dav = freezed,Object? networkProps = null,Object? vpnProps = null,Object? themeProps = null,Object? proxiesStyle = null,Object? windowProps = null,Object? patchClashConfig = null,Object? scriptProps = null,Object? nodeExcludeFilter = null,Object? healthCheckTimeout = null,}) {\n  return _then(_Config(\nappSetting: null == appSetting ? _self.appSetting : appSetting // ignore: cast_nullable_to_non_nullable\nas AppSettingProps,profiles: null == profiles ? _self._profiles : profiles // ignore: cast_nullable_to_non_nullable\nas List<Profile>,hotKeyActions: null == hotKeyActions ? _self._hotKeyActions : hotKeyActions // ignore: cast_nullable_to_non_nullable\nas List<HotKeyAction>,currentProfileId: freezed == currentProfileId ? _self.currentProfileId : currentProfileId // ignore: cast_nullable_to_non_nullable\nas String?,overrideDns: null == overrideDns ? _self.overrideDns : overrideDns // ignore: cast_nullable_to_non_nullable\nas bool,overrideNtp: null == overrideNtp ? _self.overrideNtp : overrideNtp // ignore: cast_nullable_to_non_nullable\nas bool,overrideSniffer: null == overrideSniffer ? _self.overrideSniffer : overrideSniffer // ignore: cast_nullable_to_non_nullable\nas bool,overrideTunnel: null == overrideTunnel ? _self.overrideTunnel : overrideTunnel // ignore: cast_nullable_to_non_nullable\nas bool,overrideExperimental: null == overrideExperimental ? _self.overrideExperimental : overrideExperimental // ignore: cast_nullable_to_non_nullable\nas bool,overrideTestUrl: null == overrideTestUrl ? _self.overrideTestUrl : overrideTestUrl // ignore: cast_nullable_to_non_nullable\nas bool,dav: freezed == dav ? _self.dav : dav // ignore: cast_nullable_to_non_nullable\nas DAV?,networkProps: null == networkProps ? _self.networkProps : networkProps // ignore: cast_nullable_to_non_nullable\nas NetworkProps,vpnProps: null == vpnProps ? _self.vpnProps : vpnProps // ignore: cast_nullable_to_non_nullable\nas VpnProps,themeProps: null == themeProps ? _self.themeProps : themeProps // ignore: cast_nullable_to_non_nullable\nas ThemeProps,proxiesStyle: null == proxiesStyle ? _self.proxiesStyle : proxiesStyle // ignore: cast_nullable_to_non_nullable\nas ProxiesStyle,windowProps: null == windowProps ? _self.windowProps : windowProps // ignore: cast_nullable_to_non_nullable\nas WindowProps,patchClashConfig: null == patchClashConfig ? _self.patchClashConfig : patchClashConfig // ignore: cast_nullable_to_non_nullable\nas ClashConfig,scriptProps: null == scriptProps ? _self.scriptProps : scriptProps // ignore: cast_nullable_to_non_nullable\nas ScriptProps,nodeExcludeFilter: null == nodeExcludeFilter ? _self.nodeExcludeFilter : nodeExcludeFilter // ignore: cast_nullable_to_non_nullable\nas String,healthCheckTimeout: null == healthCheckTimeout ? _self.healthCheckTimeout : healthCheckTimeout // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n\n/// Create a copy of Config\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$AppSettingPropsCopyWith<$Res> get appSetting {\n  \n  return $AppSettingPropsCopyWith<$Res>(_self.appSetting, (value) {\n    return _then(_self.copyWith(appSetting: value));\n  });\n}/// Create a copy of Config\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$DAVCopyWith<$Res>? get dav {\n    if (_self.dav == null) {\n    return null;\n  }\n\n  return $DAVCopyWith<$Res>(_self.dav!, (value) {\n    return _then(_self.copyWith(dav: value));\n  });\n}/// Create a copy of Config\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$NetworkPropsCopyWith<$Res> get networkProps {\n  \n  return $NetworkPropsCopyWith<$Res>(_self.networkProps, (value) {\n    return _then(_self.copyWith(networkProps: value));\n  });\n}/// Create a copy of Config\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$VpnPropsCopyWith<$Res> get vpnProps {\n  \n  return $VpnPropsCopyWith<$Res>(_self.vpnProps, (value) {\n    return _then(_self.copyWith(vpnProps: value));\n  });\n}/// Create a copy of Config\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$ThemePropsCopyWith<$Res> get themeProps {\n  \n  return $ThemePropsCopyWith<$Res>(_self.themeProps, (value) {\n    return _then(_self.copyWith(themeProps: value));\n  });\n}/// Create a copy of Config\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$ProxiesStyleCopyWith<$Res> get proxiesStyle {\n  \n  return $ProxiesStyleCopyWith<$Res>(_self.proxiesStyle, (value) {\n    return _then(_self.copyWith(proxiesStyle: value));\n  });\n}/// Create a copy of Config\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$WindowPropsCopyWith<$Res> get windowProps {\n  \n  return $WindowPropsCopyWith<$Res>(_self.windowProps, (value) {\n    return _then(_self.copyWith(windowProps: value));\n  });\n}/// Create a copy of Config\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$ClashConfigCopyWith<$Res> get patchClashConfig {\n  \n  return $ClashConfigCopyWith<$Res>(_self.patchClashConfig, (value) {\n    return _then(_self.copyWith(patchClashConfig: value));\n  });\n}/// Create a copy of Config\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$ScriptPropsCopyWith<$Res> get scriptProps {\n  \n  return $ScriptPropsCopyWith<$Res>(_self.scriptProps, (value) {\n    return _then(_self.copyWith(scriptProps: value));\n  });\n}\n}\n\n// dart format on\n"
  },
  {
    "path": "lib/models/generated/config.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of '../config.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_AppSettingProps _$AppSettingPropsFromJson(Map<String, dynamic> json) =>\n    _AppSettingProps(\n      locale: json['locale'] as String?,\n      dashboardWidgets: json['dashboardWidgets'] == null\n          ? defaultDashboardWidgets\n          : dashboardWidgetsSafeFormJson(json['dashboardWidgets'] as List?),\n      onlyStatisticsProxy: json['onlyStatisticsProxy'] as bool? ?? false,\n      autoLaunch: json['autoLaunch'] as bool? ?? false,\n      silentLaunch: json['silentLaunch'] as bool? ?? false,\n      smartDelayLaunch: json['smartDelayLaunch'] as bool? ?? true,\n      autoRun: json['autoRun'] as bool? ?? false,\n      openLogs: json['openLogs'] as bool? ?? true,\n      closeConnections: json['closeConnections'] as bool? ?? true,\n      testUrl: json['testUrl'] as String? ?? defaultTestUrl,\n      isAnimateToPage: json['isAnimateToPage'] as bool? ?? true,\n      enableNavBarHapticFeedback:\n          json['enableNavBarHapticFeedback'] as bool? ?? false,\n      autoCheckUpdate: json['autoCheckUpdate'] as bool? ?? true,\n      showLabel: json['showLabel'] as bool? ?? false,\n      disclaimerAccepted: json['disclaimerAccepted'] as bool? ?? false,\n      minimizeOnExit: json['minimizeOnExit'] as bool? ?? true,\n      hidden: json['hidden'] as bool? ?? false,\n      developerMode: json['developerMode'] as bool? ?? false,\n      enableHighRefreshRate: json['enableHighRefreshRate'] as bool? ?? false,\n      recoveryStrategy:\n          $enumDecodeNullable(\n            _$RecoveryStrategyEnumMap,\n            json['recoveryStrategy'],\n          ) ??\n          RecoveryStrategy.compatible,\n    );\n\nMap<String, dynamic> _$AppSettingPropsToJson(_AppSettingProps instance) =>\n    <String, dynamic>{\n      'locale': instance.locale,\n      'dashboardWidgets': instance.dashboardWidgets\n          .map((e) => _$DashboardWidgetEnumMap[e]!)\n          .toList(),\n      'onlyStatisticsProxy': instance.onlyStatisticsProxy,\n      'autoLaunch': instance.autoLaunch,\n      'silentLaunch': instance.silentLaunch,\n      'smartDelayLaunch': instance.smartDelayLaunch,\n      'autoRun': instance.autoRun,\n      'openLogs': instance.openLogs,\n      'closeConnections': instance.closeConnections,\n      'testUrl': instance.testUrl,\n      'isAnimateToPage': instance.isAnimateToPage,\n      'enableNavBarHapticFeedback': instance.enableNavBarHapticFeedback,\n      'autoCheckUpdate': instance.autoCheckUpdate,\n      'showLabel': instance.showLabel,\n      'disclaimerAccepted': instance.disclaimerAccepted,\n      'minimizeOnExit': instance.minimizeOnExit,\n      'hidden': instance.hidden,\n      'developerMode': instance.developerMode,\n      'enableHighRefreshRate': instance.enableHighRefreshRate,\n      'recoveryStrategy': _$RecoveryStrategyEnumMap[instance.recoveryStrategy]!,\n    };\n\nconst _$RecoveryStrategyEnumMap = {\n  RecoveryStrategy.compatible: 'compatible',\n  RecoveryStrategy.override: 'override',\n};\n\nconst _$DashboardWidgetEnumMap = {\n  DashboardWidget.networkSpeed: 'networkSpeed',\n  DashboardWidget.networkSpeedSmall: 'networkSpeedSmall',\n  DashboardWidget.outboundModeV2: 'outboundModeV2',\n  DashboardWidget.outboundMode: 'outboundMode',\n  DashboardWidget.trafficUsage: 'trafficUsage',\n  DashboardWidget.networkDetection: 'networkDetection',\n  DashboardWidget.tunButton: 'tunButton',\n  DashboardWidget.vpnButton: 'vpnButton',\n  DashboardWidget.systemProxyButton: 'systemProxyButton',\n  DashboardWidget.intranetIp: 'intranetIp',\n  DashboardWidget.memoryInfo: 'memoryInfo',\n  DashboardWidget.connectionsCount: 'connectionsCount',\n  DashboardWidget.ipv6Switch: 'ipv6Switch',\n  DashboardWidget.wakelockSwitch: 'wakelockSwitch',\n  DashboardWidget.dnsOverride: 'dnsOverride',\n  DashboardWidget.snifferOverride: 'snifferOverride',\n  DashboardWidget.ntpOverride: 'ntpOverride',\n  DashboardWidget.providersInfo: 'providersInfo',\n  DashboardWidget.fcmStatus: 'fcmStatus',\n  DashboardWidget.onlinePanel: 'onlinePanel',\n  DashboardWidget.startButton: 'startButton',\n};\n\n_AccessControl _$AccessControlFromJson(Map<String, dynamic> json) =>\n    _AccessControl(\n      enable: json['enable'] as bool? ?? false,\n      mode:\n          $enumDecodeNullable(_$AccessControlModeEnumMap, json['mode']) ??\n          AccessControlMode.rejectSelected,\n      acceptList:\n          (json['acceptList'] as List<dynamic>?)\n              ?.map((e) => e as String)\n              .toList() ??\n          const [],\n      rejectList:\n          (json['rejectList'] as List<dynamic>?)\n              ?.map((e) => e as String)\n              .toList() ??\n          const [],\n      sort:\n          $enumDecodeNullable(_$AccessSortTypeEnumMap, json['sort']) ??\n          AccessSortType.none,\n      isFilterSystemApp: json['isFilterSystemApp'] as bool? ?? true,\n      isFilterNonInternetApp: json['isFilterNonInternetApp'] as bool? ?? true,\n    );\n\nMap<String, dynamic> _$AccessControlToJson(_AccessControl instance) =>\n    <String, dynamic>{\n      'enable': instance.enable,\n      'mode': _$AccessControlModeEnumMap[instance.mode]!,\n      'acceptList': instance.acceptList,\n      'rejectList': instance.rejectList,\n      'sort': _$AccessSortTypeEnumMap[instance.sort]!,\n      'isFilterSystemApp': instance.isFilterSystemApp,\n      'isFilterNonInternetApp': instance.isFilterNonInternetApp,\n    };\n\nconst _$AccessControlModeEnumMap = {\n  AccessControlMode.acceptSelected: 'acceptSelected',\n  AccessControlMode.rejectSelected: 'rejectSelected',\n};\n\nconst _$AccessSortTypeEnumMap = {\n  AccessSortType.none: 'none',\n  AccessSortType.name: 'name',\n  AccessSortType.time: 'time',\n};\n\n_WindowProps _$WindowPropsFromJson(Map<String, dynamic> json) => _WindowProps(\n  width: (json['width'] as num?)?.toDouble() ?? 750,\n  height: (json['height'] as num?)?.toDouble() ?? 600,\n  top: (json['top'] as num?)?.toDouble(),\n  left: (json['left'] as num?)?.toDouble(),\n  isLocked: json['isLocked'] as bool? ?? false,\n);\n\nMap<String, dynamic> _$WindowPropsToJson(_WindowProps instance) =>\n    <String, dynamic>{\n      'width': instance.width,\n      'height': instance.height,\n      'top': instance.top,\n      'left': instance.left,\n      'isLocked': instance.isLocked,\n    };\n\n_VpnProps _$VpnPropsFromJson(Map<String, dynamic> json) => _VpnProps(\n  enable: json['enable'] as bool? ?? true,\n  systemProxy: json['systemProxy'] as bool? ?? false,\n  allowBypass: json['allowBypass'] as bool? ?? false,\n  bypassPrivateRoute: json['bypassPrivateRoute'] as bool? ?? true,\n  dozeSuspend: json['dozeSuspend'] as bool? ?? true,\n  smartAutoStop: json['smartAutoStop'] as bool? ?? false,\n  smartAutoStopNetworks: json['smartAutoStopNetworks'] as String? ?? '',\n  storeFix: json['storeFix'] as bool? ?? false,\n  networkFix: json['networkFix'] as bool? ?? false,\n  disableQuic: json['disableQuic'] as bool? ?? false,\n  excludeChina: json['excludeChina'] as bool? ?? false,\n  fcmOptimization: json['fcmOptimization'] as bool? ?? false,\n  quickResponse: json['quickResponse'] as bool? ?? false,\n  accessControl: json['accessControl'] == null\n      ? defaultAccessControl\n      : AccessControl.fromJson(json['accessControl'] as Map<String, dynamic>),\n);\n\nMap<String, dynamic> _$VpnPropsToJson(_VpnProps instance) => <String, dynamic>{\n  'enable': instance.enable,\n  'systemProxy': instance.systemProxy,\n  'allowBypass': instance.allowBypass,\n  'bypassPrivateRoute': instance.bypassPrivateRoute,\n  'dozeSuspend': instance.dozeSuspend,\n  'smartAutoStop': instance.smartAutoStop,\n  'smartAutoStopNetworks': instance.smartAutoStopNetworks,\n  'storeFix': instance.storeFix,\n  'networkFix': instance.networkFix,\n  'disableQuic': instance.disableQuic,\n  'excludeChina': instance.excludeChina,\n  'fcmOptimization': instance.fcmOptimization,\n  'quickResponse': instance.quickResponse,\n  'accessControl': instance.accessControl,\n};\n\n_NetworkProps _$NetworkPropsFromJson(Map<String, dynamic> json) =>\n    _NetworkProps(\n      systemProxy: json['systemProxy'] as bool? ?? false,\n      bypassDomain:\n          (json['bypassDomain'] as List<dynamic>?)\n              ?.map((e) => e as String)\n              .toList() ??\n          defaultBypassDomain,\n      bypassPrivateRoute: json['bypassPrivateRoute'] as bool? ?? true,\n      autoSetSystemDns: json['autoSetSystemDns'] as bool? ?? true,\n    );\n\nMap<String, dynamic> _$NetworkPropsToJson(_NetworkProps instance) =>\n    <String, dynamic>{\n      'systemProxy': instance.systemProxy,\n      'bypassDomain': instance.bypassDomain,\n      'bypassPrivateRoute': instance.bypassPrivateRoute,\n      'autoSetSystemDns': instance.autoSetSystemDns,\n    };\n\n_ProxiesStyle _$ProxiesStyleFromJson(Map<String, dynamic> json) =>\n    _ProxiesStyle(\n      type:\n          $enumDecodeNullable(_$ProxiesTypeEnumMap, json['type']) ??\n          ProxiesType.tab,\n      sortType:\n          $enumDecodeNullable(_$ProxiesSortTypeEnumMap, json['sortType']) ??\n          ProxiesSortType.none,\n      layout:\n          $enumDecodeNullable(_$ProxiesLayoutEnumMap, json['layout']) ??\n          ProxiesLayout.standard,\n      iconStyle:\n          $enumDecodeNullable(_$ProxiesIconStyleEnumMap, json['iconStyle']) ??\n          ProxiesIconStyle.none,\n      cardType:\n          $enumDecodeNullable(_$ProxyCardTypeEnumMap, json['cardType']) ??\n          ProxyCardType.shrink,\n      delayAnimation:\n          $enumDecodeNullable(\n            _$DelayAnimationTypeEnumMap,\n            json['delayAnimation'],\n          ) ??\n          DelayAnimationType.none,\n      iconMap:\n          (json['iconMap'] as Map<String, dynamic>?)?.map(\n            (k, e) => MapEntry(k, e as String),\n          ) ??\n          const {},\n      concurrencyLimit: (json['concurrencyLimit'] as num?)?.toInt() ?? 16,\n    );\n\nMap<String, dynamic> _$ProxiesStyleToJson(_ProxiesStyle instance) =>\n    <String, dynamic>{\n      'type': _$ProxiesTypeEnumMap[instance.type]!,\n      'sortType': _$ProxiesSortTypeEnumMap[instance.sortType]!,\n      'layout': _$ProxiesLayoutEnumMap[instance.layout]!,\n      'iconStyle': _$ProxiesIconStyleEnumMap[instance.iconStyle]!,\n      'cardType': _$ProxyCardTypeEnumMap[instance.cardType]!,\n      'delayAnimation': _$DelayAnimationTypeEnumMap[instance.delayAnimation]!,\n      'iconMap': instance.iconMap,\n      'concurrencyLimit': instance.concurrencyLimit,\n    };\n\nconst _$ProxiesTypeEnumMap = {ProxiesType.tab: 'tab', ProxiesType.list: 'list'};\n\nconst _$ProxiesSortTypeEnumMap = {\n  ProxiesSortType.none: 'none',\n  ProxiesSortType.delay: 'delay',\n  ProxiesSortType.name: 'name',\n};\n\nconst _$ProxiesLayoutEnumMap = {\n  ProxiesLayout.loose: 'loose',\n  ProxiesLayout.standard: 'standard',\n  ProxiesLayout.tight: 'tight',\n};\n\nconst _$ProxiesIconStyleEnumMap = {\n  ProxiesIconStyle.standard: 'standard',\n  ProxiesIconStyle.none: 'none',\n  ProxiesIconStyle.icon: 'icon',\n};\n\nconst _$ProxyCardTypeEnumMap = {\n  ProxyCardType.expand: 'expand',\n  ProxyCardType.shrink: 'shrink',\n  ProxyCardType.min: 'min',\n};\n\nconst _$DelayAnimationTypeEnumMap = {\n  DelayAnimationType.none: 'none',\n  DelayAnimationType.rotatingCircle: 'rotatingCircle',\n  DelayAnimationType.pulse: 'pulse',\n  DelayAnimationType.spinningLines: 'spinningLines',\n  DelayAnimationType.threeInOut: 'threeInOut',\n  DelayAnimationType.threeBounce: 'threeBounce',\n  DelayAnimationType.circle: 'circle',\n  DelayAnimationType.fadingCircle: 'fadingCircle',\n  DelayAnimationType.fadingFour: 'fadingFour',\n  DelayAnimationType.wave: 'wave',\n  DelayAnimationType.doubleBounce: 'doubleBounce',\n};\n\n_TextScale _$TextScaleFromJson(Map<String, dynamic> json) => _TextScale(\n  enable: json['enable'] as bool? ?? false,\n  scale: (json['scale'] as num?)?.toDouble() ?? 1.0,\n);\n\nMap<String, dynamic> _$TextScaleToJson(_TextScale instance) =>\n    <String, dynamic>{'enable': instance.enable, 'scale': instance.scale};\n\n_ThemeProps _$ThemePropsFromJson(Map<String, dynamic> json) => _ThemeProps(\n  primaryColor: (json['primaryColor'] as num?)?.toInt(),\n  primaryColors:\n      (json['primaryColors'] as List<dynamic>?)\n          ?.map((e) => (e as num).toInt())\n          .toList() ??\n      defaultPrimaryColors,\n  themeMode:\n      $enumDecodeNullable(_$ThemeModeEnumMap, json['themeMode']) ??\n      ThemeMode.system,\n  schemeVariant:\n      $enumDecodeNullable(\n        _$DynamicSchemeVariantEnumMap,\n        json['schemeVariant'],\n      ) ??\n      DynamicSchemeVariant.content,\n  pureBlack: json['pureBlack'] as bool? ?? false,\n  textScale: json['textScale'] == null\n      ? const TextScale()\n      : TextScale.fromJson(json['textScale'] as Map<String, dynamic>),\n  useLightIcon: json['useLightIcon'] as bool? ?? false,\n  useHarmonyFont: json['useHarmonyFont'] as bool? ?? false,\n);\n\nMap<String, dynamic> _$ThemePropsToJson(_ThemeProps instance) =>\n    <String, dynamic>{\n      'primaryColor': instance.primaryColor,\n      'primaryColors': instance.primaryColors,\n      'themeMode': _$ThemeModeEnumMap[instance.themeMode]!,\n      'schemeVariant': _$DynamicSchemeVariantEnumMap[instance.schemeVariant]!,\n      'pureBlack': instance.pureBlack,\n      'textScale': instance.textScale,\n      'useLightIcon': instance.useLightIcon,\n      'useHarmonyFont': instance.useHarmonyFont,\n    };\n\nconst _$ThemeModeEnumMap = {\n  ThemeMode.system: 'system',\n  ThemeMode.light: 'light',\n  ThemeMode.dark: 'dark',\n};\n\nconst _$DynamicSchemeVariantEnumMap = {\n  DynamicSchemeVariant.tonalSpot: 'tonalSpot',\n  DynamicSchemeVariant.fidelity: 'fidelity',\n  DynamicSchemeVariant.monochrome: 'monochrome',\n  DynamicSchemeVariant.neutral: 'neutral',\n  DynamicSchemeVariant.vibrant: 'vibrant',\n  DynamicSchemeVariant.expressive: 'expressive',\n  DynamicSchemeVariant.content: 'content',\n  DynamicSchemeVariant.rainbow: 'rainbow',\n  DynamicSchemeVariant.fruitSalad: 'fruitSalad',\n};\n\n_ScriptProps _$ScriptPropsFromJson(Map<String, dynamic> json) => _ScriptProps(\n  currentId: json['currentId'] as String?,\n  scripts:\n      (json['scripts'] as List<dynamic>?)\n          ?.map((e) => Script.fromJson(e as Map<String, dynamic>))\n          .toList() ??\n      const [],\n);\n\nMap<String, dynamic> _$ScriptPropsToJson(_ScriptProps instance) =>\n    <String, dynamic>{\n      'currentId': instance.currentId,\n      'scripts': instance.scripts,\n    };\n\n_Config _$ConfigFromJson(Map<String, dynamic> json) => _Config(\n  appSetting: json['appSetting'] == null\n      ? defaultAppSettingProps\n      : AppSettingProps.safeFromJson(\n          json['appSetting'] as Map<String, Object?>?,\n        ),\n  profiles:\n      (json['profiles'] as List<dynamic>?)\n          ?.map((e) => Profile.fromJson(e as Map<String, dynamic>))\n          .toList() ??\n      const [],\n  hotKeyActions:\n      (json['hotKeyActions'] as List<dynamic>?)\n          ?.map((e) => HotKeyAction.fromJson(e as Map<String, dynamic>))\n          .toList() ??\n      const [],\n  currentProfileId: json['currentProfileId'] as String?,\n  overrideDns: json['overrideDns'] as bool? ?? false,\n  overrideNtp: json['overrideNtp'] as bool? ?? false,\n  overrideSniffer: json['overrideSniffer'] as bool? ?? false,\n  overrideTunnel: json['overrideTunnel'] as bool? ?? false,\n  overrideExperimental: json['overrideExperimental'] as bool? ?? false,\n  overrideTestUrl: json['overrideTestUrl'] as bool? ?? true,\n  dav: json['dav'] == null\n      ? null\n      : DAV.fromJson(json['dav'] as Map<String, dynamic>),\n  networkProps: json['networkProps'] == null\n      ? defaultNetworkProps\n      : NetworkProps.fromJson(json['networkProps'] as Map<String, dynamic>?),\n  vpnProps: json['vpnProps'] == null\n      ? defaultVpnProps\n      : VpnProps.safeFromJson(json['vpnProps'] as Map<String, Object?>?),\n  themeProps: ThemeProps.safeFromJson(\n    json['themeProps'] as Map<String, Object?>?,\n  ),\n  proxiesStyle: json['proxiesStyle'] == null\n      ? defaultProxiesStyle\n      : ProxiesStyle.fromJson(json['proxiesStyle'] as Map<String, dynamic>?),\n  windowProps: json['windowProps'] == null\n      ? defaultWindowProps\n      : WindowProps.fromJson(json['windowProps'] as Map<String, dynamic>?),\n  patchClashConfig: json['patchClashConfig'] == null\n      ? defaultClashConfig\n      : ClashConfig.fromJson(json['patchClashConfig'] as Map<String, dynamic>),\n  scriptProps: json['scriptProps'] == null\n      ? const ScriptProps()\n      : ScriptProps.fromJson(json['scriptProps'] as Map<String, dynamic>),\n  nodeExcludeFilter: json['nodeExcludeFilter'] as String? ?? '',\n  healthCheckTimeout: (json['healthCheckTimeout'] as num?)?.toInt() ?? 5000,\n);\n\nMap<String, dynamic> _$ConfigToJson(_Config instance) => <String, dynamic>{\n  'appSetting': instance.appSetting,\n  'profiles': instance.profiles,\n  'hotKeyActions': instance.hotKeyActions,\n  'currentProfileId': instance.currentProfileId,\n  'overrideDns': instance.overrideDns,\n  'overrideNtp': instance.overrideNtp,\n  'overrideSniffer': instance.overrideSniffer,\n  'overrideTunnel': instance.overrideTunnel,\n  'overrideExperimental': instance.overrideExperimental,\n  'overrideTestUrl': instance.overrideTestUrl,\n  'dav': instance.dav,\n  'networkProps': instance.networkProps,\n  'vpnProps': instance.vpnProps,\n  'themeProps': instance.themeProps,\n  'proxiesStyle': instance.proxiesStyle,\n  'windowProps': instance.windowProps,\n  'patchClashConfig': instance.patchClashConfig,\n  'scriptProps': instance.scriptProps,\n  'nodeExcludeFilter': instance.nodeExcludeFilter,\n  'healthCheckTimeout': instance.healthCheckTimeout,\n};\n"
  },
  {
    "path": "lib/models/generated/core.freezed.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n// coverage:ignore-file\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of '../core.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\n// dart format off\nT _$identity<T>(T value) => value;\n\n/// @nodoc\nmixin _$SetupParams {\n\n@JsonKey(name: 'config') Map<String, dynamic> get config;@JsonKey(name: 'selected-map') Map<String, String> get selectedMap;@JsonKey(name: 'test-url') String get testUrl;@JsonKey(name: 'override-test-url') bool get overrideTestUrl;\n/// Create a copy of SetupParams\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$SetupParamsCopyWith<SetupParams> get copyWith => _$SetupParamsCopyWithImpl<SetupParams>(this as SetupParams, _$identity);\n\n  /// Serializes this SetupParams to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is SetupParams&&const DeepCollectionEquality().equals(other.config, config)&&const DeepCollectionEquality().equals(other.selectedMap, selectedMap)&&(identical(other.testUrl, testUrl) || other.testUrl == testUrl)&&(identical(other.overrideTestUrl, overrideTestUrl) || other.overrideTestUrl == overrideTestUrl));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(config),const DeepCollectionEquality().hash(selectedMap),testUrl,overrideTestUrl);\n\n@override\nString toString() {\n  return 'SetupParams(config: $config, selectedMap: $selectedMap, testUrl: $testUrl, overrideTestUrl: $overrideTestUrl)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $SetupParamsCopyWith<$Res>  {\n  factory $SetupParamsCopyWith(SetupParams value, $Res Function(SetupParams) _then) = _$SetupParamsCopyWithImpl;\n@useResult\n$Res call({\n@JsonKey(name: 'config') Map<String, dynamic> config,@JsonKey(name: 'selected-map') Map<String, String> selectedMap,@JsonKey(name: 'test-url') String testUrl,@JsonKey(name: 'override-test-url') bool overrideTestUrl\n});\n\n\n\n\n}\n/// @nodoc\nclass _$SetupParamsCopyWithImpl<$Res>\n    implements $SetupParamsCopyWith<$Res> {\n  _$SetupParamsCopyWithImpl(this._self, this._then);\n\n  final SetupParams _self;\n  final $Res Function(SetupParams) _then;\n\n/// Create a copy of SetupParams\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? config = null,Object? selectedMap = null,Object? testUrl = null,Object? overrideTestUrl = null,}) {\n  return _then(_self.copyWith(\nconfig: null == config ? _self.config : config // ignore: cast_nullable_to_non_nullable\nas Map<String, dynamic>,selectedMap: null == selectedMap ? _self.selectedMap : selectedMap // ignore: cast_nullable_to_non_nullable\nas Map<String, String>,testUrl: null == testUrl ? _self.testUrl : testUrl // ignore: cast_nullable_to_non_nullable\nas String,overrideTestUrl: null == overrideTestUrl ? _self.overrideTestUrl : overrideTestUrl // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [SetupParams].\nextension SetupParamsPatterns on SetupParams {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SetupParams value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _SetupParams() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SetupParams value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _SetupParams():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SetupParams value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _SetupParams() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(@JsonKey(name: 'config')  Map<String, dynamic> config, @JsonKey(name: 'selected-map')  Map<String, String> selectedMap, @JsonKey(name: 'test-url')  String testUrl, @JsonKey(name: 'override-test-url')  bool overrideTestUrl)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _SetupParams() when $default != null:\nreturn $default(_that.config,_that.selectedMap,_that.testUrl,_that.overrideTestUrl);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(@JsonKey(name: 'config')  Map<String, dynamic> config, @JsonKey(name: 'selected-map')  Map<String, String> selectedMap, @JsonKey(name: 'test-url')  String testUrl, @JsonKey(name: 'override-test-url')  bool overrideTestUrl)  $default,) {final _that = this;\nswitch (_that) {\ncase _SetupParams():\nreturn $default(_that.config,_that.selectedMap,_that.testUrl,_that.overrideTestUrl);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(@JsonKey(name: 'config')  Map<String, dynamic> config, @JsonKey(name: 'selected-map')  Map<String, String> selectedMap, @JsonKey(name: 'test-url')  String testUrl, @JsonKey(name: 'override-test-url')  bool overrideTestUrl)?  $default,) {final _that = this;\nswitch (_that) {\ncase _SetupParams() when $default != null:\nreturn $default(_that.config,_that.selectedMap,_that.testUrl,_that.overrideTestUrl);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _SetupParams implements SetupParams {\n  const _SetupParams({@JsonKey(name: 'config') required final  Map<String, dynamic> config, @JsonKey(name: 'selected-map') required final  Map<String, String> selectedMap, @JsonKey(name: 'test-url') required this.testUrl, @JsonKey(name: 'override-test-url') this.overrideTestUrl = true}): _config = config,_selectedMap = selectedMap;\n  factory _SetupParams.fromJson(Map<String, dynamic> json) => _$SetupParamsFromJson(json);\n\n final  Map<String, dynamic> _config;\n@override@JsonKey(name: 'config') Map<String, dynamic> get config {\n  if (_config is EqualUnmodifiableMapView) return _config;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableMapView(_config);\n}\n\n final  Map<String, String> _selectedMap;\n@override@JsonKey(name: 'selected-map') Map<String, String> get selectedMap {\n  if (_selectedMap is EqualUnmodifiableMapView) return _selectedMap;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableMapView(_selectedMap);\n}\n\n@override@JsonKey(name: 'test-url') final  String testUrl;\n@override@JsonKey(name: 'override-test-url') final  bool overrideTestUrl;\n\n/// Create a copy of SetupParams\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$SetupParamsCopyWith<_SetupParams> get copyWith => __$SetupParamsCopyWithImpl<_SetupParams>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$SetupParamsToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _SetupParams&&const DeepCollectionEquality().equals(other._config, _config)&&const DeepCollectionEquality().equals(other._selectedMap, _selectedMap)&&(identical(other.testUrl, testUrl) || other.testUrl == testUrl)&&(identical(other.overrideTestUrl, overrideTestUrl) || other.overrideTestUrl == overrideTestUrl));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_config),const DeepCollectionEquality().hash(_selectedMap),testUrl,overrideTestUrl);\n\n@override\nString toString() {\n  return 'SetupParams(config: $config, selectedMap: $selectedMap, testUrl: $testUrl, overrideTestUrl: $overrideTestUrl)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$SetupParamsCopyWith<$Res> implements $SetupParamsCopyWith<$Res> {\n  factory _$SetupParamsCopyWith(_SetupParams value, $Res Function(_SetupParams) _then) = __$SetupParamsCopyWithImpl;\n@override @useResult\n$Res call({\n@JsonKey(name: 'config') Map<String, dynamic> config,@JsonKey(name: 'selected-map') Map<String, String> selectedMap,@JsonKey(name: 'test-url') String testUrl,@JsonKey(name: 'override-test-url') bool overrideTestUrl\n});\n\n\n\n\n}\n/// @nodoc\nclass __$SetupParamsCopyWithImpl<$Res>\n    implements _$SetupParamsCopyWith<$Res> {\n  __$SetupParamsCopyWithImpl(this._self, this._then);\n\n  final _SetupParams _self;\n  final $Res Function(_SetupParams) _then;\n\n/// Create a copy of SetupParams\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? config = null,Object? selectedMap = null,Object? testUrl = null,Object? overrideTestUrl = null,}) {\n  return _then(_SetupParams(\nconfig: null == config ? _self._config : config // ignore: cast_nullable_to_non_nullable\nas Map<String, dynamic>,selectedMap: null == selectedMap ? _self._selectedMap : selectedMap // ignore: cast_nullable_to_non_nullable\nas Map<String, String>,testUrl: null == testUrl ? _self.testUrl : testUrl // ignore: cast_nullable_to_non_nullable\nas String,overrideTestUrl: null == overrideTestUrl ? _self.overrideTestUrl : overrideTestUrl // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$UpdateParams {\n\n Tun get tun;@JsonKey(name: 'mixed-port') int get mixedPort;@JsonKey(name: 'allow-lan') bool get allowLan;@JsonKey(name: 'find-process-mode') FindProcessMode get findProcessMode; Mode get mode;@JsonKey(name: 'log-level') LogLevel get logLevel; bool get ipv6;@JsonKey(name: 'tcp-concurrent') bool get tcpConcurrent;@JsonKey(name: 'external-controller') ExternalControllerStatus get externalController;@JsonKey(name: 'unified-delay') bool get unifiedDelay;\n/// Create a copy of UpdateParams\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$UpdateParamsCopyWith<UpdateParams> get copyWith => _$UpdateParamsCopyWithImpl<UpdateParams>(this as UpdateParams, _$identity);\n\n  /// Serializes this UpdateParams to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is UpdateParams&&(identical(other.tun, tun) || other.tun == tun)&&(identical(other.mixedPort, mixedPort) || other.mixedPort == mixedPort)&&(identical(other.allowLan, allowLan) || other.allowLan == allowLan)&&(identical(other.findProcessMode, findProcessMode) || other.findProcessMode == findProcessMode)&&(identical(other.mode, mode) || other.mode == mode)&&(identical(other.logLevel, logLevel) || other.logLevel == logLevel)&&(identical(other.ipv6, ipv6) || other.ipv6 == ipv6)&&(identical(other.tcpConcurrent, tcpConcurrent) || other.tcpConcurrent == tcpConcurrent)&&(identical(other.externalController, externalController) || other.externalController == externalController)&&(identical(other.unifiedDelay, unifiedDelay) || other.unifiedDelay == unifiedDelay));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,tun,mixedPort,allowLan,findProcessMode,mode,logLevel,ipv6,tcpConcurrent,externalController,unifiedDelay);\n\n@override\nString toString() {\n  return 'UpdateParams(tun: $tun, mixedPort: $mixedPort, allowLan: $allowLan, findProcessMode: $findProcessMode, mode: $mode, logLevel: $logLevel, ipv6: $ipv6, tcpConcurrent: $tcpConcurrent, externalController: $externalController, unifiedDelay: $unifiedDelay)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $UpdateParamsCopyWith<$Res>  {\n  factory $UpdateParamsCopyWith(UpdateParams value, $Res Function(UpdateParams) _then) = _$UpdateParamsCopyWithImpl;\n@useResult\n$Res call({\n Tun tun,@JsonKey(name: 'mixed-port') int mixedPort,@JsonKey(name: 'allow-lan') bool allowLan,@JsonKey(name: 'find-process-mode') FindProcessMode findProcessMode, Mode mode,@JsonKey(name: 'log-level') LogLevel logLevel, bool ipv6,@JsonKey(name: 'tcp-concurrent') bool tcpConcurrent,@JsonKey(name: 'external-controller') ExternalControllerStatus externalController,@JsonKey(name: 'unified-delay') bool unifiedDelay\n});\n\n\n$TunCopyWith<$Res> get tun;\n\n}\n/// @nodoc\nclass _$UpdateParamsCopyWithImpl<$Res>\n    implements $UpdateParamsCopyWith<$Res> {\n  _$UpdateParamsCopyWithImpl(this._self, this._then);\n\n  final UpdateParams _self;\n  final $Res Function(UpdateParams) _then;\n\n/// Create a copy of UpdateParams\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? tun = null,Object? mixedPort = null,Object? allowLan = null,Object? findProcessMode = null,Object? mode = null,Object? logLevel = null,Object? ipv6 = null,Object? tcpConcurrent = null,Object? externalController = null,Object? unifiedDelay = null,}) {\n  return _then(_self.copyWith(\ntun: null == tun ? _self.tun : tun // ignore: cast_nullable_to_non_nullable\nas Tun,mixedPort: null == mixedPort ? _self.mixedPort : mixedPort // ignore: cast_nullable_to_non_nullable\nas int,allowLan: null == allowLan ? _self.allowLan : allowLan // ignore: cast_nullable_to_non_nullable\nas bool,findProcessMode: null == findProcessMode ? _self.findProcessMode : findProcessMode // ignore: cast_nullable_to_non_nullable\nas FindProcessMode,mode: null == mode ? _self.mode : mode // ignore: cast_nullable_to_non_nullable\nas Mode,logLevel: null == logLevel ? _self.logLevel : logLevel // ignore: cast_nullable_to_non_nullable\nas LogLevel,ipv6: null == ipv6 ? _self.ipv6 : ipv6 // ignore: cast_nullable_to_non_nullable\nas bool,tcpConcurrent: null == tcpConcurrent ? _self.tcpConcurrent : tcpConcurrent // ignore: cast_nullable_to_non_nullable\nas bool,externalController: null == externalController ? _self.externalController : externalController // ignore: cast_nullable_to_non_nullable\nas ExternalControllerStatus,unifiedDelay: null == unifiedDelay ? _self.unifiedDelay : unifiedDelay // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n/// Create a copy of UpdateParams\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$TunCopyWith<$Res> get tun {\n  \n  return $TunCopyWith<$Res>(_self.tun, (value) {\n    return _then(_self.copyWith(tun: value));\n  });\n}\n}\n\n\n/// Adds pattern-matching-related methods to [UpdateParams].\nextension UpdateParamsPatterns on UpdateParams {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _UpdateParams value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _UpdateParams() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _UpdateParams value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _UpdateParams():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _UpdateParams value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _UpdateParams() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( Tun tun, @JsonKey(name: 'mixed-port')  int mixedPort, @JsonKey(name: 'allow-lan')  bool allowLan, @JsonKey(name: 'find-process-mode')  FindProcessMode findProcessMode,  Mode mode, @JsonKey(name: 'log-level')  LogLevel logLevel,  bool ipv6, @JsonKey(name: 'tcp-concurrent')  bool tcpConcurrent, @JsonKey(name: 'external-controller')  ExternalControllerStatus externalController, @JsonKey(name: 'unified-delay')  bool unifiedDelay)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _UpdateParams() when $default != null:\nreturn $default(_that.tun,_that.mixedPort,_that.allowLan,_that.findProcessMode,_that.mode,_that.logLevel,_that.ipv6,_that.tcpConcurrent,_that.externalController,_that.unifiedDelay);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( Tun tun, @JsonKey(name: 'mixed-port')  int mixedPort, @JsonKey(name: 'allow-lan')  bool allowLan, @JsonKey(name: 'find-process-mode')  FindProcessMode findProcessMode,  Mode mode, @JsonKey(name: 'log-level')  LogLevel logLevel,  bool ipv6, @JsonKey(name: 'tcp-concurrent')  bool tcpConcurrent, @JsonKey(name: 'external-controller')  ExternalControllerStatus externalController, @JsonKey(name: 'unified-delay')  bool unifiedDelay)  $default,) {final _that = this;\nswitch (_that) {\ncase _UpdateParams():\nreturn $default(_that.tun,_that.mixedPort,_that.allowLan,_that.findProcessMode,_that.mode,_that.logLevel,_that.ipv6,_that.tcpConcurrent,_that.externalController,_that.unifiedDelay);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( Tun tun, @JsonKey(name: 'mixed-port')  int mixedPort, @JsonKey(name: 'allow-lan')  bool allowLan, @JsonKey(name: 'find-process-mode')  FindProcessMode findProcessMode,  Mode mode, @JsonKey(name: 'log-level')  LogLevel logLevel,  bool ipv6, @JsonKey(name: 'tcp-concurrent')  bool tcpConcurrent, @JsonKey(name: 'external-controller')  ExternalControllerStatus externalController, @JsonKey(name: 'unified-delay')  bool unifiedDelay)?  $default,) {final _that = this;\nswitch (_that) {\ncase _UpdateParams() when $default != null:\nreturn $default(_that.tun,_that.mixedPort,_that.allowLan,_that.findProcessMode,_that.mode,_that.logLevel,_that.ipv6,_that.tcpConcurrent,_that.externalController,_that.unifiedDelay);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _UpdateParams implements UpdateParams {\n  const _UpdateParams({required this.tun, @JsonKey(name: 'mixed-port') required this.mixedPort, @JsonKey(name: 'allow-lan') required this.allowLan, @JsonKey(name: 'find-process-mode') required this.findProcessMode, required this.mode, @JsonKey(name: 'log-level') required this.logLevel, required this.ipv6, @JsonKey(name: 'tcp-concurrent') required this.tcpConcurrent, @JsonKey(name: 'external-controller') required this.externalController, @JsonKey(name: 'unified-delay') required this.unifiedDelay});\n  factory _UpdateParams.fromJson(Map<String, dynamic> json) => _$UpdateParamsFromJson(json);\n\n@override final  Tun tun;\n@override@JsonKey(name: 'mixed-port') final  int mixedPort;\n@override@JsonKey(name: 'allow-lan') final  bool allowLan;\n@override@JsonKey(name: 'find-process-mode') final  FindProcessMode findProcessMode;\n@override final  Mode mode;\n@override@JsonKey(name: 'log-level') final  LogLevel logLevel;\n@override final  bool ipv6;\n@override@JsonKey(name: 'tcp-concurrent') final  bool tcpConcurrent;\n@override@JsonKey(name: 'external-controller') final  ExternalControllerStatus externalController;\n@override@JsonKey(name: 'unified-delay') final  bool unifiedDelay;\n\n/// Create a copy of UpdateParams\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$UpdateParamsCopyWith<_UpdateParams> get copyWith => __$UpdateParamsCopyWithImpl<_UpdateParams>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$UpdateParamsToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _UpdateParams&&(identical(other.tun, tun) || other.tun == tun)&&(identical(other.mixedPort, mixedPort) || other.mixedPort == mixedPort)&&(identical(other.allowLan, allowLan) || other.allowLan == allowLan)&&(identical(other.findProcessMode, findProcessMode) || other.findProcessMode == findProcessMode)&&(identical(other.mode, mode) || other.mode == mode)&&(identical(other.logLevel, logLevel) || other.logLevel == logLevel)&&(identical(other.ipv6, ipv6) || other.ipv6 == ipv6)&&(identical(other.tcpConcurrent, tcpConcurrent) || other.tcpConcurrent == tcpConcurrent)&&(identical(other.externalController, externalController) || other.externalController == externalController)&&(identical(other.unifiedDelay, unifiedDelay) || other.unifiedDelay == unifiedDelay));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,tun,mixedPort,allowLan,findProcessMode,mode,logLevel,ipv6,tcpConcurrent,externalController,unifiedDelay);\n\n@override\nString toString() {\n  return 'UpdateParams(tun: $tun, mixedPort: $mixedPort, allowLan: $allowLan, findProcessMode: $findProcessMode, mode: $mode, logLevel: $logLevel, ipv6: $ipv6, tcpConcurrent: $tcpConcurrent, externalController: $externalController, unifiedDelay: $unifiedDelay)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$UpdateParamsCopyWith<$Res> implements $UpdateParamsCopyWith<$Res> {\n  factory _$UpdateParamsCopyWith(_UpdateParams value, $Res Function(_UpdateParams) _then) = __$UpdateParamsCopyWithImpl;\n@override @useResult\n$Res call({\n Tun tun,@JsonKey(name: 'mixed-port') int mixedPort,@JsonKey(name: 'allow-lan') bool allowLan,@JsonKey(name: 'find-process-mode') FindProcessMode findProcessMode, Mode mode,@JsonKey(name: 'log-level') LogLevel logLevel, bool ipv6,@JsonKey(name: 'tcp-concurrent') bool tcpConcurrent,@JsonKey(name: 'external-controller') ExternalControllerStatus externalController,@JsonKey(name: 'unified-delay') bool unifiedDelay\n});\n\n\n@override $TunCopyWith<$Res> get tun;\n\n}\n/// @nodoc\nclass __$UpdateParamsCopyWithImpl<$Res>\n    implements _$UpdateParamsCopyWith<$Res> {\n  __$UpdateParamsCopyWithImpl(this._self, this._then);\n\n  final _UpdateParams _self;\n  final $Res Function(_UpdateParams) _then;\n\n/// Create a copy of UpdateParams\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? tun = null,Object? mixedPort = null,Object? allowLan = null,Object? findProcessMode = null,Object? mode = null,Object? logLevel = null,Object? ipv6 = null,Object? tcpConcurrent = null,Object? externalController = null,Object? unifiedDelay = null,}) {\n  return _then(_UpdateParams(\ntun: null == tun ? _self.tun : tun // ignore: cast_nullable_to_non_nullable\nas Tun,mixedPort: null == mixedPort ? _self.mixedPort : mixedPort // ignore: cast_nullable_to_non_nullable\nas int,allowLan: null == allowLan ? _self.allowLan : allowLan // ignore: cast_nullable_to_non_nullable\nas bool,findProcessMode: null == findProcessMode ? _self.findProcessMode : findProcessMode // ignore: cast_nullable_to_non_nullable\nas FindProcessMode,mode: null == mode ? _self.mode : mode // ignore: cast_nullable_to_non_nullable\nas Mode,logLevel: null == logLevel ? _self.logLevel : logLevel // ignore: cast_nullable_to_non_nullable\nas LogLevel,ipv6: null == ipv6 ? _self.ipv6 : ipv6 // ignore: cast_nullable_to_non_nullable\nas bool,tcpConcurrent: null == tcpConcurrent ? _self.tcpConcurrent : tcpConcurrent // ignore: cast_nullable_to_non_nullable\nas bool,externalController: null == externalController ? _self.externalController : externalController // ignore: cast_nullable_to_non_nullable\nas ExternalControllerStatus,unifiedDelay: null == unifiedDelay ? _self.unifiedDelay : unifiedDelay // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n/// Create a copy of UpdateParams\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$TunCopyWith<$Res> get tun {\n  \n  return $TunCopyWith<$Res>(_self.tun, (value) {\n    return _then(_self.copyWith(tun: value));\n  });\n}\n}\n\n\n/// @nodoc\nmixin _$CoreState {\n\n@JsonKey(name: 'vpn-props') VpnProps get vpnProps;@JsonKey(name: 'only-statistics-proxy') bool get onlyStatisticsProxy;@JsonKey(name: 'current-profile-name') String get currentProfileName;@JsonKey(name: 'bypass-domain') List<String> get bypassDomain;\n/// Create a copy of CoreState\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$CoreStateCopyWith<CoreState> get copyWith => _$CoreStateCopyWithImpl<CoreState>(this as CoreState, _$identity);\n\n  /// Serializes this CoreState to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is CoreState&&(identical(other.vpnProps, vpnProps) || other.vpnProps == vpnProps)&&(identical(other.onlyStatisticsProxy, onlyStatisticsProxy) || other.onlyStatisticsProxy == onlyStatisticsProxy)&&(identical(other.currentProfileName, currentProfileName) || other.currentProfileName == currentProfileName)&&const DeepCollectionEquality().equals(other.bypassDomain, bypassDomain));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,vpnProps,onlyStatisticsProxy,currentProfileName,const DeepCollectionEquality().hash(bypassDomain));\n\n@override\nString toString() {\n  return 'CoreState(vpnProps: $vpnProps, onlyStatisticsProxy: $onlyStatisticsProxy, currentProfileName: $currentProfileName, bypassDomain: $bypassDomain)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $CoreStateCopyWith<$Res>  {\n  factory $CoreStateCopyWith(CoreState value, $Res Function(CoreState) _then) = _$CoreStateCopyWithImpl;\n@useResult\n$Res call({\n@JsonKey(name: 'vpn-props') VpnProps vpnProps,@JsonKey(name: 'only-statistics-proxy') bool onlyStatisticsProxy,@JsonKey(name: 'current-profile-name') String currentProfileName,@JsonKey(name: 'bypass-domain') List<String> bypassDomain\n});\n\n\n$VpnPropsCopyWith<$Res> get vpnProps;\n\n}\n/// @nodoc\nclass _$CoreStateCopyWithImpl<$Res>\n    implements $CoreStateCopyWith<$Res> {\n  _$CoreStateCopyWithImpl(this._self, this._then);\n\n  final CoreState _self;\n  final $Res Function(CoreState) _then;\n\n/// Create a copy of CoreState\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? vpnProps = null,Object? onlyStatisticsProxy = null,Object? currentProfileName = null,Object? bypassDomain = null,}) {\n  return _then(_self.copyWith(\nvpnProps: null == vpnProps ? _self.vpnProps : vpnProps // ignore: cast_nullable_to_non_nullable\nas VpnProps,onlyStatisticsProxy: null == onlyStatisticsProxy ? _self.onlyStatisticsProxy : onlyStatisticsProxy // ignore: cast_nullable_to_non_nullable\nas bool,currentProfileName: null == currentProfileName ? _self.currentProfileName : currentProfileName // ignore: cast_nullable_to_non_nullable\nas String,bypassDomain: null == bypassDomain ? _self.bypassDomain : bypassDomain // ignore: cast_nullable_to_non_nullable\nas List<String>,\n  ));\n}\n/// Create a copy of CoreState\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$VpnPropsCopyWith<$Res> get vpnProps {\n  \n  return $VpnPropsCopyWith<$Res>(_self.vpnProps, (value) {\n    return _then(_self.copyWith(vpnProps: value));\n  });\n}\n}\n\n\n/// Adds pattern-matching-related methods to [CoreState].\nextension CoreStatePatterns on CoreState {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _CoreState value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _CoreState() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _CoreState value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _CoreState():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _CoreState value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _CoreState() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(@JsonKey(name: 'vpn-props')  VpnProps vpnProps, @JsonKey(name: 'only-statistics-proxy')  bool onlyStatisticsProxy, @JsonKey(name: 'current-profile-name')  String currentProfileName, @JsonKey(name: 'bypass-domain')  List<String> bypassDomain)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _CoreState() when $default != null:\nreturn $default(_that.vpnProps,_that.onlyStatisticsProxy,_that.currentProfileName,_that.bypassDomain);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(@JsonKey(name: 'vpn-props')  VpnProps vpnProps, @JsonKey(name: 'only-statistics-proxy')  bool onlyStatisticsProxy, @JsonKey(name: 'current-profile-name')  String currentProfileName, @JsonKey(name: 'bypass-domain')  List<String> bypassDomain)  $default,) {final _that = this;\nswitch (_that) {\ncase _CoreState():\nreturn $default(_that.vpnProps,_that.onlyStatisticsProxy,_that.currentProfileName,_that.bypassDomain);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(@JsonKey(name: 'vpn-props')  VpnProps vpnProps, @JsonKey(name: 'only-statistics-proxy')  bool onlyStatisticsProxy, @JsonKey(name: 'current-profile-name')  String currentProfileName, @JsonKey(name: 'bypass-domain')  List<String> bypassDomain)?  $default,) {final _that = this;\nswitch (_that) {\ncase _CoreState() when $default != null:\nreturn $default(_that.vpnProps,_that.onlyStatisticsProxy,_that.currentProfileName,_that.bypassDomain);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _CoreState implements CoreState {\n  const _CoreState({@JsonKey(name: 'vpn-props') required this.vpnProps, @JsonKey(name: 'only-statistics-proxy') required this.onlyStatisticsProxy, @JsonKey(name: 'current-profile-name') required this.currentProfileName, @JsonKey(name: 'bypass-domain') final  List<String> bypassDomain = const []}): _bypassDomain = bypassDomain;\n  factory _CoreState.fromJson(Map<String, dynamic> json) => _$CoreStateFromJson(json);\n\n@override@JsonKey(name: 'vpn-props') final  VpnProps vpnProps;\n@override@JsonKey(name: 'only-statistics-proxy') final  bool onlyStatisticsProxy;\n@override@JsonKey(name: 'current-profile-name') final  String currentProfileName;\n final  List<String> _bypassDomain;\n@override@JsonKey(name: 'bypass-domain') List<String> get bypassDomain {\n  if (_bypassDomain is EqualUnmodifiableListView) return _bypassDomain;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_bypassDomain);\n}\n\n\n/// Create a copy of CoreState\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$CoreStateCopyWith<_CoreState> get copyWith => __$CoreStateCopyWithImpl<_CoreState>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$CoreStateToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _CoreState&&(identical(other.vpnProps, vpnProps) || other.vpnProps == vpnProps)&&(identical(other.onlyStatisticsProxy, onlyStatisticsProxy) || other.onlyStatisticsProxy == onlyStatisticsProxy)&&(identical(other.currentProfileName, currentProfileName) || other.currentProfileName == currentProfileName)&&const DeepCollectionEquality().equals(other._bypassDomain, _bypassDomain));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,vpnProps,onlyStatisticsProxy,currentProfileName,const DeepCollectionEquality().hash(_bypassDomain));\n\n@override\nString toString() {\n  return 'CoreState(vpnProps: $vpnProps, onlyStatisticsProxy: $onlyStatisticsProxy, currentProfileName: $currentProfileName, bypassDomain: $bypassDomain)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$CoreStateCopyWith<$Res> implements $CoreStateCopyWith<$Res> {\n  factory _$CoreStateCopyWith(_CoreState value, $Res Function(_CoreState) _then) = __$CoreStateCopyWithImpl;\n@override @useResult\n$Res call({\n@JsonKey(name: 'vpn-props') VpnProps vpnProps,@JsonKey(name: 'only-statistics-proxy') bool onlyStatisticsProxy,@JsonKey(name: 'current-profile-name') String currentProfileName,@JsonKey(name: 'bypass-domain') List<String> bypassDomain\n});\n\n\n@override $VpnPropsCopyWith<$Res> get vpnProps;\n\n}\n/// @nodoc\nclass __$CoreStateCopyWithImpl<$Res>\n    implements _$CoreStateCopyWith<$Res> {\n  __$CoreStateCopyWithImpl(this._self, this._then);\n\n  final _CoreState _self;\n  final $Res Function(_CoreState) _then;\n\n/// Create a copy of CoreState\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? vpnProps = null,Object? onlyStatisticsProxy = null,Object? currentProfileName = null,Object? bypassDomain = null,}) {\n  return _then(_CoreState(\nvpnProps: null == vpnProps ? _self.vpnProps : vpnProps // ignore: cast_nullable_to_non_nullable\nas VpnProps,onlyStatisticsProxy: null == onlyStatisticsProxy ? _self.onlyStatisticsProxy : onlyStatisticsProxy // ignore: cast_nullable_to_non_nullable\nas bool,currentProfileName: null == currentProfileName ? _self.currentProfileName : currentProfileName // ignore: cast_nullable_to_non_nullable\nas String,bypassDomain: null == bypassDomain ? _self._bypassDomain : bypassDomain // ignore: cast_nullable_to_non_nullable\nas List<String>,\n  ));\n}\n\n/// Create a copy of CoreState\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$VpnPropsCopyWith<$Res> get vpnProps {\n  \n  return $VpnPropsCopyWith<$Res>(_self.vpnProps, (value) {\n    return _then(_self.copyWith(vpnProps: value));\n  });\n}\n}\n\n\n/// @nodoc\nmixin _$AndroidVpnOptions {\n\n bool get enable; int get port; AccessControl? get accessControl; bool get allowBypass; bool get systemProxy; List<String> get bypassDomain; String get ipv4Address; String get ipv6Address; List<String> get routeAddress; String get routeMode; String get dnsServerAddress; bool get dozeSuspend;\n/// Create a copy of AndroidVpnOptions\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$AndroidVpnOptionsCopyWith<AndroidVpnOptions> get copyWith => _$AndroidVpnOptionsCopyWithImpl<AndroidVpnOptions>(this as AndroidVpnOptions, _$identity);\n\n  /// Serializes this AndroidVpnOptions to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is AndroidVpnOptions&&(identical(other.enable, enable) || other.enable == enable)&&(identical(other.port, port) || other.port == port)&&(identical(other.accessControl, accessControl) || other.accessControl == accessControl)&&(identical(other.allowBypass, allowBypass) || other.allowBypass == allowBypass)&&(identical(other.systemProxy, systemProxy) || other.systemProxy == systemProxy)&&const DeepCollectionEquality().equals(other.bypassDomain, bypassDomain)&&(identical(other.ipv4Address, ipv4Address) || other.ipv4Address == ipv4Address)&&(identical(other.ipv6Address, ipv6Address) || other.ipv6Address == ipv6Address)&&const DeepCollectionEquality().equals(other.routeAddress, routeAddress)&&(identical(other.routeMode, routeMode) || other.routeMode == routeMode)&&(identical(other.dnsServerAddress, dnsServerAddress) || other.dnsServerAddress == dnsServerAddress)&&(identical(other.dozeSuspend, dozeSuspend) || other.dozeSuspend == dozeSuspend));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,enable,port,accessControl,allowBypass,systemProxy,const DeepCollectionEquality().hash(bypassDomain),ipv4Address,ipv6Address,const DeepCollectionEquality().hash(routeAddress),routeMode,dnsServerAddress,dozeSuspend);\n\n@override\nString toString() {\n  return 'AndroidVpnOptions(enable: $enable, port: $port, accessControl: $accessControl, allowBypass: $allowBypass, systemProxy: $systemProxy, bypassDomain: $bypassDomain, ipv4Address: $ipv4Address, ipv6Address: $ipv6Address, routeAddress: $routeAddress, routeMode: $routeMode, dnsServerAddress: $dnsServerAddress, dozeSuspend: $dozeSuspend)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $AndroidVpnOptionsCopyWith<$Res>  {\n  factory $AndroidVpnOptionsCopyWith(AndroidVpnOptions value, $Res Function(AndroidVpnOptions) _then) = _$AndroidVpnOptionsCopyWithImpl;\n@useResult\n$Res call({\n bool enable, int port, AccessControl? accessControl, bool allowBypass, bool systemProxy, List<String> bypassDomain, String ipv4Address, String ipv6Address, List<String> routeAddress, String routeMode, String dnsServerAddress, bool dozeSuspend\n});\n\n\n$AccessControlCopyWith<$Res>? get accessControl;\n\n}\n/// @nodoc\nclass _$AndroidVpnOptionsCopyWithImpl<$Res>\n    implements $AndroidVpnOptionsCopyWith<$Res> {\n  _$AndroidVpnOptionsCopyWithImpl(this._self, this._then);\n\n  final AndroidVpnOptions _self;\n  final $Res Function(AndroidVpnOptions) _then;\n\n/// Create a copy of AndroidVpnOptions\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? enable = null,Object? port = null,Object? accessControl = freezed,Object? allowBypass = null,Object? systemProxy = null,Object? bypassDomain = null,Object? ipv4Address = null,Object? ipv6Address = null,Object? routeAddress = null,Object? routeMode = null,Object? dnsServerAddress = null,Object? dozeSuspend = null,}) {\n  return _then(_self.copyWith(\nenable: null == enable ? _self.enable : enable // ignore: cast_nullable_to_non_nullable\nas bool,port: null == port ? _self.port : port // ignore: cast_nullable_to_non_nullable\nas int,accessControl: freezed == accessControl ? _self.accessControl : accessControl // ignore: cast_nullable_to_non_nullable\nas AccessControl?,allowBypass: null == allowBypass ? _self.allowBypass : allowBypass // ignore: cast_nullable_to_non_nullable\nas bool,systemProxy: null == systemProxy ? _self.systemProxy : systemProxy // ignore: cast_nullable_to_non_nullable\nas bool,bypassDomain: null == bypassDomain ? _self.bypassDomain : bypassDomain // ignore: cast_nullable_to_non_nullable\nas List<String>,ipv4Address: null == ipv4Address ? _self.ipv4Address : ipv4Address // ignore: cast_nullable_to_non_nullable\nas String,ipv6Address: null == ipv6Address ? _self.ipv6Address : ipv6Address // ignore: cast_nullable_to_non_nullable\nas String,routeAddress: null == routeAddress ? _self.routeAddress : routeAddress // ignore: cast_nullable_to_non_nullable\nas List<String>,routeMode: null == routeMode ? _self.routeMode : routeMode // ignore: cast_nullable_to_non_nullable\nas String,dnsServerAddress: null == dnsServerAddress ? _self.dnsServerAddress : dnsServerAddress // ignore: cast_nullable_to_non_nullable\nas String,dozeSuspend: null == dozeSuspend ? _self.dozeSuspend : dozeSuspend // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n/// Create a copy of AndroidVpnOptions\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$AccessControlCopyWith<$Res>? get accessControl {\n    if (_self.accessControl == null) {\n    return null;\n  }\n\n  return $AccessControlCopyWith<$Res>(_self.accessControl!, (value) {\n    return _then(_self.copyWith(accessControl: value));\n  });\n}\n}\n\n\n/// Adds pattern-matching-related methods to [AndroidVpnOptions].\nextension AndroidVpnOptionsPatterns on AndroidVpnOptions {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _AndroidVpnOptions value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _AndroidVpnOptions() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _AndroidVpnOptions value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _AndroidVpnOptions():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _AndroidVpnOptions value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _AndroidVpnOptions() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool enable,  int port,  AccessControl? accessControl,  bool allowBypass,  bool systemProxy,  List<String> bypassDomain,  String ipv4Address,  String ipv6Address,  List<String> routeAddress,  String routeMode,  String dnsServerAddress,  bool dozeSuspend)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _AndroidVpnOptions() when $default != null:\nreturn $default(_that.enable,_that.port,_that.accessControl,_that.allowBypass,_that.systemProxy,_that.bypassDomain,_that.ipv4Address,_that.ipv6Address,_that.routeAddress,_that.routeMode,_that.dnsServerAddress,_that.dozeSuspend);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool enable,  int port,  AccessControl? accessControl,  bool allowBypass,  bool systemProxy,  List<String> bypassDomain,  String ipv4Address,  String ipv6Address,  List<String> routeAddress,  String routeMode,  String dnsServerAddress,  bool dozeSuspend)  $default,) {final _that = this;\nswitch (_that) {\ncase _AndroidVpnOptions():\nreturn $default(_that.enable,_that.port,_that.accessControl,_that.allowBypass,_that.systemProxy,_that.bypassDomain,_that.ipv4Address,_that.ipv6Address,_that.routeAddress,_that.routeMode,_that.dnsServerAddress,_that.dozeSuspend);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool enable,  int port,  AccessControl? accessControl,  bool allowBypass,  bool systemProxy,  List<String> bypassDomain,  String ipv4Address,  String ipv6Address,  List<String> routeAddress,  String routeMode,  String dnsServerAddress,  bool dozeSuspend)?  $default,) {final _that = this;\nswitch (_that) {\ncase _AndroidVpnOptions() when $default != null:\nreturn $default(_that.enable,_that.port,_that.accessControl,_that.allowBypass,_that.systemProxy,_that.bypassDomain,_that.ipv4Address,_that.ipv6Address,_that.routeAddress,_that.routeMode,_that.dnsServerAddress,_that.dozeSuspend);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _AndroidVpnOptions implements AndroidVpnOptions {\n  const _AndroidVpnOptions({required this.enable, required this.port, required this.accessControl, required this.allowBypass, required this.systemProxy, required final  List<String> bypassDomain, required this.ipv4Address, required this.ipv6Address, final  List<String> routeAddress = const [], this.routeMode = 'config', required this.dnsServerAddress, this.dozeSuspend = false}): _bypassDomain = bypassDomain,_routeAddress = routeAddress;\n  factory _AndroidVpnOptions.fromJson(Map<String, dynamic> json) => _$AndroidVpnOptionsFromJson(json);\n\n@override final  bool enable;\n@override final  int port;\n@override final  AccessControl? accessControl;\n@override final  bool allowBypass;\n@override final  bool systemProxy;\n final  List<String> _bypassDomain;\n@override List<String> get bypassDomain {\n  if (_bypassDomain is EqualUnmodifiableListView) return _bypassDomain;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_bypassDomain);\n}\n\n@override final  String ipv4Address;\n@override final  String ipv6Address;\n final  List<String> _routeAddress;\n@override@JsonKey() List<String> get routeAddress {\n  if (_routeAddress is EqualUnmodifiableListView) return _routeAddress;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_routeAddress);\n}\n\n@override@JsonKey() final  String routeMode;\n@override final  String dnsServerAddress;\n@override@JsonKey() final  bool dozeSuspend;\n\n/// Create a copy of AndroidVpnOptions\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$AndroidVpnOptionsCopyWith<_AndroidVpnOptions> get copyWith => __$AndroidVpnOptionsCopyWithImpl<_AndroidVpnOptions>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$AndroidVpnOptionsToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _AndroidVpnOptions&&(identical(other.enable, enable) || other.enable == enable)&&(identical(other.port, port) || other.port == port)&&(identical(other.accessControl, accessControl) || other.accessControl == accessControl)&&(identical(other.allowBypass, allowBypass) || other.allowBypass == allowBypass)&&(identical(other.systemProxy, systemProxy) || other.systemProxy == systemProxy)&&const DeepCollectionEquality().equals(other._bypassDomain, _bypassDomain)&&(identical(other.ipv4Address, ipv4Address) || other.ipv4Address == ipv4Address)&&(identical(other.ipv6Address, ipv6Address) || other.ipv6Address == ipv6Address)&&const DeepCollectionEquality().equals(other._routeAddress, _routeAddress)&&(identical(other.routeMode, routeMode) || other.routeMode == routeMode)&&(identical(other.dnsServerAddress, dnsServerAddress) || other.dnsServerAddress == dnsServerAddress)&&(identical(other.dozeSuspend, dozeSuspend) || other.dozeSuspend == dozeSuspend));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,enable,port,accessControl,allowBypass,systemProxy,const DeepCollectionEquality().hash(_bypassDomain),ipv4Address,ipv6Address,const DeepCollectionEquality().hash(_routeAddress),routeMode,dnsServerAddress,dozeSuspend);\n\n@override\nString toString() {\n  return 'AndroidVpnOptions(enable: $enable, port: $port, accessControl: $accessControl, allowBypass: $allowBypass, systemProxy: $systemProxy, bypassDomain: $bypassDomain, ipv4Address: $ipv4Address, ipv6Address: $ipv6Address, routeAddress: $routeAddress, routeMode: $routeMode, dnsServerAddress: $dnsServerAddress, dozeSuspend: $dozeSuspend)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$AndroidVpnOptionsCopyWith<$Res> implements $AndroidVpnOptionsCopyWith<$Res> {\n  factory _$AndroidVpnOptionsCopyWith(_AndroidVpnOptions value, $Res Function(_AndroidVpnOptions) _then) = __$AndroidVpnOptionsCopyWithImpl;\n@override @useResult\n$Res call({\n bool enable, int port, AccessControl? accessControl, bool allowBypass, bool systemProxy, List<String> bypassDomain, String ipv4Address, String ipv6Address, List<String> routeAddress, String routeMode, String dnsServerAddress, bool dozeSuspend\n});\n\n\n@override $AccessControlCopyWith<$Res>? get accessControl;\n\n}\n/// @nodoc\nclass __$AndroidVpnOptionsCopyWithImpl<$Res>\n    implements _$AndroidVpnOptionsCopyWith<$Res> {\n  __$AndroidVpnOptionsCopyWithImpl(this._self, this._then);\n\n  final _AndroidVpnOptions _self;\n  final $Res Function(_AndroidVpnOptions) _then;\n\n/// Create a copy of AndroidVpnOptions\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? enable = null,Object? port = null,Object? accessControl = freezed,Object? allowBypass = null,Object? systemProxy = null,Object? bypassDomain = null,Object? ipv4Address = null,Object? ipv6Address = null,Object? routeAddress = null,Object? routeMode = null,Object? dnsServerAddress = null,Object? dozeSuspend = null,}) {\n  return _then(_AndroidVpnOptions(\nenable: null == enable ? _self.enable : enable // ignore: cast_nullable_to_non_nullable\nas bool,port: null == port ? _self.port : port // ignore: cast_nullable_to_non_nullable\nas int,accessControl: freezed == accessControl ? _self.accessControl : accessControl // ignore: cast_nullable_to_non_nullable\nas AccessControl?,allowBypass: null == allowBypass ? _self.allowBypass : allowBypass // ignore: cast_nullable_to_non_nullable\nas bool,systemProxy: null == systemProxy ? _self.systemProxy : systemProxy // ignore: cast_nullable_to_non_nullable\nas bool,bypassDomain: null == bypassDomain ? _self._bypassDomain : bypassDomain // ignore: cast_nullable_to_non_nullable\nas List<String>,ipv4Address: null == ipv4Address ? _self.ipv4Address : ipv4Address // ignore: cast_nullable_to_non_nullable\nas String,ipv6Address: null == ipv6Address ? _self.ipv6Address : ipv6Address // ignore: cast_nullable_to_non_nullable\nas String,routeAddress: null == routeAddress ? _self._routeAddress : routeAddress // ignore: cast_nullable_to_non_nullable\nas List<String>,routeMode: null == routeMode ? _self.routeMode : routeMode // ignore: cast_nullable_to_non_nullable\nas String,dnsServerAddress: null == dnsServerAddress ? _self.dnsServerAddress : dnsServerAddress // ignore: cast_nullable_to_non_nullable\nas String,dozeSuspend: null == dozeSuspend ? _self.dozeSuspend : dozeSuspend // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n/// Create a copy of AndroidVpnOptions\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$AccessControlCopyWith<$Res>? get accessControl {\n    if (_self.accessControl == null) {\n    return null;\n  }\n\n  return $AccessControlCopyWith<$Res>(_self.accessControl!, (value) {\n    return _then(_self.copyWith(accessControl: value));\n  });\n}\n}\n\n\n/// @nodoc\nmixin _$InitParams {\n\n@JsonKey(name: 'home-dir') String get homeDir; int get version;\n/// Create a copy of InitParams\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$InitParamsCopyWith<InitParams> get copyWith => _$InitParamsCopyWithImpl<InitParams>(this as InitParams, _$identity);\n\n  /// Serializes this InitParams to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is InitParams&&(identical(other.homeDir, homeDir) || other.homeDir == homeDir)&&(identical(other.version, version) || other.version == version));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,homeDir,version);\n\n@override\nString toString() {\n  return 'InitParams(homeDir: $homeDir, version: $version)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $InitParamsCopyWith<$Res>  {\n  factory $InitParamsCopyWith(InitParams value, $Res Function(InitParams) _then) = _$InitParamsCopyWithImpl;\n@useResult\n$Res call({\n@JsonKey(name: 'home-dir') String homeDir, int version\n});\n\n\n\n\n}\n/// @nodoc\nclass _$InitParamsCopyWithImpl<$Res>\n    implements $InitParamsCopyWith<$Res> {\n  _$InitParamsCopyWithImpl(this._self, this._then);\n\n  final InitParams _self;\n  final $Res Function(InitParams) _then;\n\n/// Create a copy of InitParams\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? homeDir = null,Object? version = null,}) {\n  return _then(_self.copyWith(\nhomeDir: null == homeDir ? _self.homeDir : homeDir // ignore: cast_nullable_to_non_nullable\nas String,version: null == version ? _self.version : version // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [InitParams].\nextension InitParamsPatterns on InitParams {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _InitParams value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _InitParams() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _InitParams value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _InitParams():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _InitParams value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _InitParams() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(@JsonKey(name: 'home-dir')  String homeDir,  int version)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _InitParams() when $default != null:\nreturn $default(_that.homeDir,_that.version);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(@JsonKey(name: 'home-dir')  String homeDir,  int version)  $default,) {final _that = this;\nswitch (_that) {\ncase _InitParams():\nreturn $default(_that.homeDir,_that.version);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(@JsonKey(name: 'home-dir')  String homeDir,  int version)?  $default,) {final _that = this;\nswitch (_that) {\ncase _InitParams() when $default != null:\nreturn $default(_that.homeDir,_that.version);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _InitParams implements InitParams {\n  const _InitParams({@JsonKey(name: 'home-dir') required this.homeDir, required this.version});\n  factory _InitParams.fromJson(Map<String, dynamic> json) => _$InitParamsFromJson(json);\n\n@override@JsonKey(name: 'home-dir') final  String homeDir;\n@override final  int version;\n\n/// Create a copy of InitParams\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$InitParamsCopyWith<_InitParams> get copyWith => __$InitParamsCopyWithImpl<_InitParams>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$InitParamsToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _InitParams&&(identical(other.homeDir, homeDir) || other.homeDir == homeDir)&&(identical(other.version, version) || other.version == version));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,homeDir,version);\n\n@override\nString toString() {\n  return 'InitParams(homeDir: $homeDir, version: $version)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$InitParamsCopyWith<$Res> implements $InitParamsCopyWith<$Res> {\n  factory _$InitParamsCopyWith(_InitParams value, $Res Function(_InitParams) _then) = __$InitParamsCopyWithImpl;\n@override @useResult\n$Res call({\n@JsonKey(name: 'home-dir') String homeDir, int version\n});\n\n\n\n\n}\n/// @nodoc\nclass __$InitParamsCopyWithImpl<$Res>\n    implements _$InitParamsCopyWith<$Res> {\n  __$InitParamsCopyWithImpl(this._self, this._then);\n\n  final _InitParams _self;\n  final $Res Function(_InitParams) _then;\n\n/// Create a copy of InitParams\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? homeDir = null,Object? version = null,}) {\n  return _then(_InitParams(\nhomeDir: null == homeDir ? _self.homeDir : homeDir // ignore: cast_nullable_to_non_nullable\nas String,version: null == version ? _self.version : version // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$ChangeProxyParams {\n\n@JsonKey(name: 'group-name') String get groupName;@JsonKey(name: 'proxy-name') String get proxyName;\n/// Create a copy of ChangeProxyParams\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ChangeProxyParamsCopyWith<ChangeProxyParams> get copyWith => _$ChangeProxyParamsCopyWithImpl<ChangeProxyParams>(this as ChangeProxyParams, _$identity);\n\n  /// Serializes this ChangeProxyParams to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is ChangeProxyParams&&(identical(other.groupName, groupName) || other.groupName == groupName)&&(identical(other.proxyName, proxyName) || other.proxyName == proxyName));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,groupName,proxyName);\n\n@override\nString toString() {\n  return 'ChangeProxyParams(groupName: $groupName, proxyName: $proxyName)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ChangeProxyParamsCopyWith<$Res>  {\n  factory $ChangeProxyParamsCopyWith(ChangeProxyParams value, $Res Function(ChangeProxyParams) _then) = _$ChangeProxyParamsCopyWithImpl;\n@useResult\n$Res call({\n@JsonKey(name: 'group-name') String groupName,@JsonKey(name: 'proxy-name') String proxyName\n});\n\n\n\n\n}\n/// @nodoc\nclass _$ChangeProxyParamsCopyWithImpl<$Res>\n    implements $ChangeProxyParamsCopyWith<$Res> {\n  _$ChangeProxyParamsCopyWithImpl(this._self, this._then);\n\n  final ChangeProxyParams _self;\n  final $Res Function(ChangeProxyParams) _then;\n\n/// Create a copy of ChangeProxyParams\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? groupName = null,Object? proxyName = null,}) {\n  return _then(_self.copyWith(\ngroupName: null == groupName ? _self.groupName : groupName // ignore: cast_nullable_to_non_nullable\nas String,proxyName: null == proxyName ? _self.proxyName : proxyName // ignore: cast_nullable_to_non_nullable\nas String,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [ChangeProxyParams].\nextension ChangeProxyParamsPatterns on ChangeProxyParams {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ChangeProxyParams value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _ChangeProxyParams() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ChangeProxyParams value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ChangeProxyParams():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ChangeProxyParams value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ChangeProxyParams() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(@JsonKey(name: 'group-name')  String groupName, @JsonKey(name: 'proxy-name')  String proxyName)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _ChangeProxyParams() when $default != null:\nreturn $default(_that.groupName,_that.proxyName);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(@JsonKey(name: 'group-name')  String groupName, @JsonKey(name: 'proxy-name')  String proxyName)  $default,) {final _that = this;\nswitch (_that) {\ncase _ChangeProxyParams():\nreturn $default(_that.groupName,_that.proxyName);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(@JsonKey(name: 'group-name')  String groupName, @JsonKey(name: 'proxy-name')  String proxyName)?  $default,) {final _that = this;\nswitch (_that) {\ncase _ChangeProxyParams() when $default != null:\nreturn $default(_that.groupName,_that.proxyName);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _ChangeProxyParams implements ChangeProxyParams {\n  const _ChangeProxyParams({@JsonKey(name: 'group-name') required this.groupName, @JsonKey(name: 'proxy-name') required this.proxyName});\n  factory _ChangeProxyParams.fromJson(Map<String, dynamic> json) => _$ChangeProxyParamsFromJson(json);\n\n@override@JsonKey(name: 'group-name') final  String groupName;\n@override@JsonKey(name: 'proxy-name') final  String proxyName;\n\n/// Create a copy of ChangeProxyParams\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ChangeProxyParamsCopyWith<_ChangeProxyParams> get copyWith => __$ChangeProxyParamsCopyWithImpl<_ChangeProxyParams>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$ChangeProxyParamsToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _ChangeProxyParams&&(identical(other.groupName, groupName) || other.groupName == groupName)&&(identical(other.proxyName, proxyName) || other.proxyName == proxyName));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,groupName,proxyName);\n\n@override\nString toString() {\n  return 'ChangeProxyParams(groupName: $groupName, proxyName: $proxyName)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ChangeProxyParamsCopyWith<$Res> implements $ChangeProxyParamsCopyWith<$Res> {\n  factory _$ChangeProxyParamsCopyWith(_ChangeProxyParams value, $Res Function(_ChangeProxyParams) _then) = __$ChangeProxyParamsCopyWithImpl;\n@override @useResult\n$Res call({\n@JsonKey(name: 'group-name') String groupName,@JsonKey(name: 'proxy-name') String proxyName\n});\n\n\n\n\n}\n/// @nodoc\nclass __$ChangeProxyParamsCopyWithImpl<$Res>\n    implements _$ChangeProxyParamsCopyWith<$Res> {\n  __$ChangeProxyParamsCopyWithImpl(this._self, this._then);\n\n  final _ChangeProxyParams _self;\n  final $Res Function(_ChangeProxyParams) _then;\n\n/// Create a copy of ChangeProxyParams\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? groupName = null,Object? proxyName = null,}) {\n  return _then(_ChangeProxyParams(\ngroupName: null == groupName ? _self.groupName : groupName // ignore: cast_nullable_to_non_nullable\nas String,proxyName: null == proxyName ? _self.proxyName : proxyName // ignore: cast_nullable_to_non_nullable\nas String,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$UpdateGeoDataParams {\n\n@JsonKey(name: 'geo-type') String get geoType;@JsonKey(name: 'geo-name') String get geoName;\n/// Create a copy of UpdateGeoDataParams\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$UpdateGeoDataParamsCopyWith<UpdateGeoDataParams> get copyWith => _$UpdateGeoDataParamsCopyWithImpl<UpdateGeoDataParams>(this as UpdateGeoDataParams, _$identity);\n\n  /// Serializes this UpdateGeoDataParams to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is UpdateGeoDataParams&&(identical(other.geoType, geoType) || other.geoType == geoType)&&(identical(other.geoName, geoName) || other.geoName == geoName));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,geoType,geoName);\n\n@override\nString toString() {\n  return 'UpdateGeoDataParams(geoType: $geoType, geoName: $geoName)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $UpdateGeoDataParamsCopyWith<$Res>  {\n  factory $UpdateGeoDataParamsCopyWith(UpdateGeoDataParams value, $Res Function(UpdateGeoDataParams) _then) = _$UpdateGeoDataParamsCopyWithImpl;\n@useResult\n$Res call({\n@JsonKey(name: 'geo-type') String geoType,@JsonKey(name: 'geo-name') String geoName\n});\n\n\n\n\n}\n/// @nodoc\nclass _$UpdateGeoDataParamsCopyWithImpl<$Res>\n    implements $UpdateGeoDataParamsCopyWith<$Res> {\n  _$UpdateGeoDataParamsCopyWithImpl(this._self, this._then);\n\n  final UpdateGeoDataParams _self;\n  final $Res Function(UpdateGeoDataParams) _then;\n\n/// Create a copy of UpdateGeoDataParams\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? geoType = null,Object? geoName = null,}) {\n  return _then(_self.copyWith(\ngeoType: null == geoType ? _self.geoType : geoType // ignore: cast_nullable_to_non_nullable\nas String,geoName: null == geoName ? _self.geoName : geoName // ignore: cast_nullable_to_non_nullable\nas String,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [UpdateGeoDataParams].\nextension UpdateGeoDataParamsPatterns on UpdateGeoDataParams {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _UpdateGeoDataParams value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _UpdateGeoDataParams() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _UpdateGeoDataParams value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _UpdateGeoDataParams():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _UpdateGeoDataParams value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _UpdateGeoDataParams() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(@JsonKey(name: 'geo-type')  String geoType, @JsonKey(name: 'geo-name')  String geoName)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _UpdateGeoDataParams() when $default != null:\nreturn $default(_that.geoType,_that.geoName);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(@JsonKey(name: 'geo-type')  String geoType, @JsonKey(name: 'geo-name')  String geoName)  $default,) {final _that = this;\nswitch (_that) {\ncase _UpdateGeoDataParams():\nreturn $default(_that.geoType,_that.geoName);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(@JsonKey(name: 'geo-type')  String geoType, @JsonKey(name: 'geo-name')  String geoName)?  $default,) {final _that = this;\nswitch (_that) {\ncase _UpdateGeoDataParams() when $default != null:\nreturn $default(_that.geoType,_that.geoName);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _UpdateGeoDataParams implements UpdateGeoDataParams {\n  const _UpdateGeoDataParams({@JsonKey(name: 'geo-type') required this.geoType, @JsonKey(name: 'geo-name') required this.geoName});\n  factory _UpdateGeoDataParams.fromJson(Map<String, dynamic> json) => _$UpdateGeoDataParamsFromJson(json);\n\n@override@JsonKey(name: 'geo-type') final  String geoType;\n@override@JsonKey(name: 'geo-name') final  String geoName;\n\n/// Create a copy of UpdateGeoDataParams\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$UpdateGeoDataParamsCopyWith<_UpdateGeoDataParams> get copyWith => __$UpdateGeoDataParamsCopyWithImpl<_UpdateGeoDataParams>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$UpdateGeoDataParamsToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _UpdateGeoDataParams&&(identical(other.geoType, geoType) || other.geoType == geoType)&&(identical(other.geoName, geoName) || other.geoName == geoName));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,geoType,geoName);\n\n@override\nString toString() {\n  return 'UpdateGeoDataParams(geoType: $geoType, geoName: $geoName)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$UpdateGeoDataParamsCopyWith<$Res> implements $UpdateGeoDataParamsCopyWith<$Res> {\n  factory _$UpdateGeoDataParamsCopyWith(_UpdateGeoDataParams value, $Res Function(_UpdateGeoDataParams) _then) = __$UpdateGeoDataParamsCopyWithImpl;\n@override @useResult\n$Res call({\n@JsonKey(name: 'geo-type') String geoType,@JsonKey(name: 'geo-name') String geoName\n});\n\n\n\n\n}\n/// @nodoc\nclass __$UpdateGeoDataParamsCopyWithImpl<$Res>\n    implements _$UpdateGeoDataParamsCopyWith<$Res> {\n  __$UpdateGeoDataParamsCopyWithImpl(this._self, this._then);\n\n  final _UpdateGeoDataParams _self;\n  final $Res Function(_UpdateGeoDataParams) _then;\n\n/// Create a copy of UpdateGeoDataParams\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? geoType = null,Object? geoName = null,}) {\n  return _then(_UpdateGeoDataParams(\ngeoType: null == geoType ? _self.geoType : geoType // ignore: cast_nullable_to_non_nullable\nas String,geoName: null == geoName ? _self.geoName : geoName // ignore: cast_nullable_to_non_nullable\nas String,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$AppMessage {\n\n AppMessageType get type; dynamic get data;\n/// Create a copy of AppMessage\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$AppMessageCopyWith<AppMessage> get copyWith => _$AppMessageCopyWithImpl<AppMessage>(this as AppMessage, _$identity);\n\n  /// Serializes this AppMessage to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is AppMessage&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other.data, data));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,type,const DeepCollectionEquality().hash(data));\n\n@override\nString toString() {\n  return 'AppMessage(type: $type, data: $data)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $AppMessageCopyWith<$Res>  {\n  factory $AppMessageCopyWith(AppMessage value, $Res Function(AppMessage) _then) = _$AppMessageCopyWithImpl;\n@useResult\n$Res call({\n AppMessageType type, dynamic data\n});\n\n\n\n\n}\n/// @nodoc\nclass _$AppMessageCopyWithImpl<$Res>\n    implements $AppMessageCopyWith<$Res> {\n  _$AppMessageCopyWithImpl(this._self, this._then);\n\n  final AppMessage _self;\n  final $Res Function(AppMessage) _then;\n\n/// Create a copy of AppMessage\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? type = null,Object? data = freezed,}) {\n  return _then(_self.copyWith(\ntype: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable\nas AppMessageType,data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable\nas dynamic,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [AppMessage].\nextension AppMessagePatterns on AppMessage {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _AppMessage value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _AppMessage() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _AppMessage value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _AppMessage():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _AppMessage value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _AppMessage() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( AppMessageType type,  dynamic data)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _AppMessage() when $default != null:\nreturn $default(_that.type,_that.data);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( AppMessageType type,  dynamic data)  $default,) {final _that = this;\nswitch (_that) {\ncase _AppMessage():\nreturn $default(_that.type,_that.data);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( AppMessageType type,  dynamic data)?  $default,) {final _that = this;\nswitch (_that) {\ncase _AppMessage() when $default != null:\nreturn $default(_that.type,_that.data);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _AppMessage implements AppMessage {\n  const _AppMessage({required this.type, this.data});\n  factory _AppMessage.fromJson(Map<String, dynamic> json) => _$AppMessageFromJson(json);\n\n@override final  AppMessageType type;\n@override final  dynamic data;\n\n/// Create a copy of AppMessage\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$AppMessageCopyWith<_AppMessage> get copyWith => __$AppMessageCopyWithImpl<_AppMessage>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$AppMessageToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppMessage&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other.data, data));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,type,const DeepCollectionEquality().hash(data));\n\n@override\nString toString() {\n  return 'AppMessage(type: $type, data: $data)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$AppMessageCopyWith<$Res> implements $AppMessageCopyWith<$Res> {\n  factory _$AppMessageCopyWith(_AppMessage value, $Res Function(_AppMessage) _then) = __$AppMessageCopyWithImpl;\n@override @useResult\n$Res call({\n AppMessageType type, dynamic data\n});\n\n\n\n\n}\n/// @nodoc\nclass __$AppMessageCopyWithImpl<$Res>\n    implements _$AppMessageCopyWith<$Res> {\n  __$AppMessageCopyWithImpl(this._self, this._then);\n\n  final _AppMessage _self;\n  final $Res Function(_AppMessage) _then;\n\n/// Create a copy of AppMessage\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? type = null,Object? data = freezed,}) {\n  return _then(_AppMessage(\ntype: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable\nas AppMessageType,data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable\nas dynamic,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$InvokeMessage {\n\n InvokeMessageType get type; dynamic get data;\n/// Create a copy of InvokeMessage\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$InvokeMessageCopyWith<InvokeMessage> get copyWith => _$InvokeMessageCopyWithImpl<InvokeMessage>(this as InvokeMessage, _$identity);\n\n  /// Serializes this InvokeMessage to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is InvokeMessage&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other.data, data));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,type,const DeepCollectionEquality().hash(data));\n\n@override\nString toString() {\n  return 'InvokeMessage(type: $type, data: $data)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $InvokeMessageCopyWith<$Res>  {\n  factory $InvokeMessageCopyWith(InvokeMessage value, $Res Function(InvokeMessage) _then) = _$InvokeMessageCopyWithImpl;\n@useResult\n$Res call({\n InvokeMessageType type, dynamic data\n});\n\n\n\n\n}\n/// @nodoc\nclass _$InvokeMessageCopyWithImpl<$Res>\n    implements $InvokeMessageCopyWith<$Res> {\n  _$InvokeMessageCopyWithImpl(this._self, this._then);\n\n  final InvokeMessage _self;\n  final $Res Function(InvokeMessage) _then;\n\n/// Create a copy of InvokeMessage\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? type = null,Object? data = freezed,}) {\n  return _then(_self.copyWith(\ntype: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable\nas InvokeMessageType,data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable\nas dynamic,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [InvokeMessage].\nextension InvokeMessagePatterns on InvokeMessage {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _InvokeMessage value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _InvokeMessage() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _InvokeMessage value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _InvokeMessage():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _InvokeMessage value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _InvokeMessage() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( InvokeMessageType type,  dynamic data)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _InvokeMessage() when $default != null:\nreturn $default(_that.type,_that.data);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( InvokeMessageType type,  dynamic data)  $default,) {final _that = this;\nswitch (_that) {\ncase _InvokeMessage():\nreturn $default(_that.type,_that.data);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( InvokeMessageType type,  dynamic data)?  $default,) {final _that = this;\nswitch (_that) {\ncase _InvokeMessage() when $default != null:\nreturn $default(_that.type,_that.data);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _InvokeMessage implements InvokeMessage {\n  const _InvokeMessage({required this.type, this.data});\n  factory _InvokeMessage.fromJson(Map<String, dynamic> json) => _$InvokeMessageFromJson(json);\n\n@override final  InvokeMessageType type;\n@override final  dynamic data;\n\n/// Create a copy of InvokeMessage\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$InvokeMessageCopyWith<_InvokeMessage> get copyWith => __$InvokeMessageCopyWithImpl<_InvokeMessage>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$InvokeMessageToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _InvokeMessage&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other.data, data));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,type,const DeepCollectionEquality().hash(data));\n\n@override\nString toString() {\n  return 'InvokeMessage(type: $type, data: $data)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$InvokeMessageCopyWith<$Res> implements $InvokeMessageCopyWith<$Res> {\n  factory _$InvokeMessageCopyWith(_InvokeMessage value, $Res Function(_InvokeMessage) _then) = __$InvokeMessageCopyWithImpl;\n@override @useResult\n$Res call({\n InvokeMessageType type, dynamic data\n});\n\n\n\n\n}\n/// @nodoc\nclass __$InvokeMessageCopyWithImpl<$Res>\n    implements _$InvokeMessageCopyWith<$Res> {\n  __$InvokeMessageCopyWithImpl(this._self, this._then);\n\n  final _InvokeMessage _self;\n  final $Res Function(_InvokeMessage) _then;\n\n/// Create a copy of InvokeMessage\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? type = null,Object? data = freezed,}) {\n  return _then(_InvokeMessage(\ntype: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable\nas InvokeMessageType,data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable\nas dynamic,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$Delay {\n\n String get name; String get url; int? get value;\n/// Create a copy of Delay\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$DelayCopyWith<Delay> get copyWith => _$DelayCopyWithImpl<Delay>(this as Delay, _$identity);\n\n  /// Serializes this Delay to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is Delay&&(identical(other.name, name) || other.name == name)&&(identical(other.url, url) || other.url == url)&&(identical(other.value, value) || other.value == value));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,name,url,value);\n\n@override\nString toString() {\n  return 'Delay(name: $name, url: $url, value: $value)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $DelayCopyWith<$Res>  {\n  factory $DelayCopyWith(Delay value, $Res Function(Delay) _then) = _$DelayCopyWithImpl;\n@useResult\n$Res call({\n String name, String url, int? value\n});\n\n\n\n\n}\n/// @nodoc\nclass _$DelayCopyWithImpl<$Res>\n    implements $DelayCopyWith<$Res> {\n  _$DelayCopyWithImpl(this._self, this._then);\n\n  final Delay _self;\n  final $Res Function(Delay) _then;\n\n/// Create a copy of Delay\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? name = null,Object? url = null,Object? value = freezed,}) {\n  return _then(_self.copyWith(\nname: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable\nas String,url: null == url ? _self.url : url // ignore: cast_nullable_to_non_nullable\nas String,value: freezed == value ? _self.value : value // ignore: cast_nullable_to_non_nullable\nas int?,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [Delay].\nextension DelayPatterns on Delay {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _Delay value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _Delay() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _Delay value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Delay():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _Delay value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Delay() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String name,  String url,  int? value)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _Delay() when $default != null:\nreturn $default(_that.name,_that.url,_that.value);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String name,  String url,  int? value)  $default,) {final _that = this;\nswitch (_that) {\ncase _Delay():\nreturn $default(_that.name,_that.url,_that.value);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String name,  String url,  int? value)?  $default,) {final _that = this;\nswitch (_that) {\ncase _Delay() when $default != null:\nreturn $default(_that.name,_that.url,_that.value);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _Delay implements Delay {\n  const _Delay({required this.name, required this.url, this.value});\n  factory _Delay.fromJson(Map<String, dynamic> json) => _$DelayFromJson(json);\n\n@override final  String name;\n@override final  String url;\n@override final  int? value;\n\n/// Create a copy of Delay\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$DelayCopyWith<_Delay> get copyWith => __$DelayCopyWithImpl<_Delay>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$DelayToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _Delay&&(identical(other.name, name) || other.name == name)&&(identical(other.url, url) || other.url == url)&&(identical(other.value, value) || other.value == value));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,name,url,value);\n\n@override\nString toString() {\n  return 'Delay(name: $name, url: $url, value: $value)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$DelayCopyWith<$Res> implements $DelayCopyWith<$Res> {\n  factory _$DelayCopyWith(_Delay value, $Res Function(_Delay) _then) = __$DelayCopyWithImpl;\n@override @useResult\n$Res call({\n String name, String url, int? value\n});\n\n\n\n\n}\n/// @nodoc\nclass __$DelayCopyWithImpl<$Res>\n    implements _$DelayCopyWith<$Res> {\n  __$DelayCopyWithImpl(this._self, this._then);\n\n  final _Delay _self;\n  final $Res Function(_Delay) _then;\n\n/// Create a copy of Delay\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? name = null,Object? url = null,Object? value = freezed,}) {\n  return _then(_Delay(\nname: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable\nas String,url: null == url ? _self.url : url // ignore: cast_nullable_to_non_nullable\nas String,value: freezed == value ? _self.value : value // ignore: cast_nullable_to_non_nullable\nas int?,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$Now {\n\n String get name; String get value;\n/// Create a copy of Now\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$NowCopyWith<Now> get copyWith => _$NowCopyWithImpl<Now>(this as Now, _$identity);\n\n  /// Serializes this Now to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is Now&&(identical(other.name, name) || other.name == name)&&(identical(other.value, value) || other.value == value));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,name,value);\n\n@override\nString toString() {\n  return 'Now(name: $name, value: $value)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $NowCopyWith<$Res>  {\n  factory $NowCopyWith(Now value, $Res Function(Now) _then) = _$NowCopyWithImpl;\n@useResult\n$Res call({\n String name, String value\n});\n\n\n\n\n}\n/// @nodoc\nclass _$NowCopyWithImpl<$Res>\n    implements $NowCopyWith<$Res> {\n  _$NowCopyWithImpl(this._self, this._then);\n\n  final Now _self;\n  final $Res Function(Now) _then;\n\n/// Create a copy of Now\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? name = null,Object? value = null,}) {\n  return _then(_self.copyWith(\nname: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable\nas String,value: null == value ? _self.value : value // ignore: cast_nullable_to_non_nullable\nas String,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [Now].\nextension NowPatterns on Now {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _Now value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _Now() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _Now value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Now():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _Now value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Now() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String name,  String value)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _Now() when $default != null:\nreturn $default(_that.name,_that.value);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String name,  String value)  $default,) {final _that = this;\nswitch (_that) {\ncase _Now():\nreturn $default(_that.name,_that.value);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String name,  String value)?  $default,) {final _that = this;\nswitch (_that) {\ncase _Now() when $default != null:\nreturn $default(_that.name,_that.value);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _Now implements Now {\n  const _Now({required this.name, required this.value});\n  factory _Now.fromJson(Map<String, dynamic> json) => _$NowFromJson(json);\n\n@override final  String name;\n@override final  String value;\n\n/// Create a copy of Now\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$NowCopyWith<_Now> get copyWith => __$NowCopyWithImpl<_Now>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$NowToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _Now&&(identical(other.name, name) || other.name == name)&&(identical(other.value, value) || other.value == value));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,name,value);\n\n@override\nString toString() {\n  return 'Now(name: $name, value: $value)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$NowCopyWith<$Res> implements $NowCopyWith<$Res> {\n  factory _$NowCopyWith(_Now value, $Res Function(_Now) _then) = __$NowCopyWithImpl;\n@override @useResult\n$Res call({\n String name, String value\n});\n\n\n\n\n}\n/// @nodoc\nclass __$NowCopyWithImpl<$Res>\n    implements _$NowCopyWith<$Res> {\n  __$NowCopyWithImpl(this._self, this._then);\n\n  final _Now _self;\n  final $Res Function(_Now) _then;\n\n/// Create a copy of Now\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? name = null,Object? value = null,}) {\n  return _then(_Now(\nname: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable\nas String,value: null == value ? _self.value : value // ignore: cast_nullable_to_non_nullable\nas String,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$ProviderSubscriptionInfo {\n\n@JsonKey(name: 'UPLOAD') int get upload;@JsonKey(name: 'DOWNLOAD') int get download;@JsonKey(name: 'TOTAL') int get total;@JsonKey(name: 'EXPIRE') int get expire;\n/// Create a copy of ProviderSubscriptionInfo\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ProviderSubscriptionInfoCopyWith<ProviderSubscriptionInfo> get copyWith => _$ProviderSubscriptionInfoCopyWithImpl<ProviderSubscriptionInfo>(this as ProviderSubscriptionInfo, _$identity);\n\n  /// Serializes this ProviderSubscriptionInfo to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is ProviderSubscriptionInfo&&(identical(other.upload, upload) || other.upload == upload)&&(identical(other.download, download) || other.download == download)&&(identical(other.total, total) || other.total == total)&&(identical(other.expire, expire) || other.expire == expire));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,upload,download,total,expire);\n\n@override\nString toString() {\n  return 'ProviderSubscriptionInfo(upload: $upload, download: $download, total: $total, expire: $expire)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ProviderSubscriptionInfoCopyWith<$Res>  {\n  factory $ProviderSubscriptionInfoCopyWith(ProviderSubscriptionInfo value, $Res Function(ProviderSubscriptionInfo) _then) = _$ProviderSubscriptionInfoCopyWithImpl;\n@useResult\n$Res call({\n@JsonKey(name: 'UPLOAD') int upload,@JsonKey(name: 'DOWNLOAD') int download,@JsonKey(name: 'TOTAL') int total,@JsonKey(name: 'EXPIRE') int expire\n});\n\n\n\n\n}\n/// @nodoc\nclass _$ProviderSubscriptionInfoCopyWithImpl<$Res>\n    implements $ProviderSubscriptionInfoCopyWith<$Res> {\n  _$ProviderSubscriptionInfoCopyWithImpl(this._self, this._then);\n\n  final ProviderSubscriptionInfo _self;\n  final $Res Function(ProviderSubscriptionInfo) _then;\n\n/// Create a copy of ProviderSubscriptionInfo\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? upload = null,Object? download = null,Object? total = null,Object? expire = null,}) {\n  return _then(_self.copyWith(\nupload: null == upload ? _self.upload : upload // ignore: cast_nullable_to_non_nullable\nas int,download: null == download ? _self.download : download // ignore: cast_nullable_to_non_nullable\nas int,total: null == total ? _self.total : total // ignore: cast_nullable_to_non_nullable\nas int,expire: null == expire ? _self.expire : expire // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [ProviderSubscriptionInfo].\nextension ProviderSubscriptionInfoPatterns on ProviderSubscriptionInfo {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ProviderSubscriptionInfo value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _ProviderSubscriptionInfo() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ProviderSubscriptionInfo value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ProviderSubscriptionInfo():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ProviderSubscriptionInfo value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ProviderSubscriptionInfo() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(@JsonKey(name: 'UPLOAD')  int upload, @JsonKey(name: 'DOWNLOAD')  int download, @JsonKey(name: 'TOTAL')  int total, @JsonKey(name: 'EXPIRE')  int expire)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _ProviderSubscriptionInfo() when $default != null:\nreturn $default(_that.upload,_that.download,_that.total,_that.expire);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(@JsonKey(name: 'UPLOAD')  int upload, @JsonKey(name: 'DOWNLOAD')  int download, @JsonKey(name: 'TOTAL')  int total, @JsonKey(name: 'EXPIRE')  int expire)  $default,) {final _that = this;\nswitch (_that) {\ncase _ProviderSubscriptionInfo():\nreturn $default(_that.upload,_that.download,_that.total,_that.expire);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(@JsonKey(name: 'UPLOAD')  int upload, @JsonKey(name: 'DOWNLOAD')  int download, @JsonKey(name: 'TOTAL')  int total, @JsonKey(name: 'EXPIRE')  int expire)?  $default,) {final _that = this;\nswitch (_that) {\ncase _ProviderSubscriptionInfo() when $default != null:\nreturn $default(_that.upload,_that.download,_that.total,_that.expire);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _ProviderSubscriptionInfo implements ProviderSubscriptionInfo {\n  const _ProviderSubscriptionInfo({@JsonKey(name: 'UPLOAD') this.upload = 0, @JsonKey(name: 'DOWNLOAD') this.download = 0, @JsonKey(name: 'TOTAL') this.total = 0, @JsonKey(name: 'EXPIRE') this.expire = 0});\n  factory _ProviderSubscriptionInfo.fromJson(Map<String, dynamic> json) => _$ProviderSubscriptionInfoFromJson(json);\n\n@override@JsonKey(name: 'UPLOAD') final  int upload;\n@override@JsonKey(name: 'DOWNLOAD') final  int download;\n@override@JsonKey(name: 'TOTAL') final  int total;\n@override@JsonKey(name: 'EXPIRE') final  int expire;\n\n/// Create a copy of ProviderSubscriptionInfo\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ProviderSubscriptionInfoCopyWith<_ProviderSubscriptionInfo> get copyWith => __$ProviderSubscriptionInfoCopyWithImpl<_ProviderSubscriptionInfo>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$ProviderSubscriptionInfoToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProviderSubscriptionInfo&&(identical(other.upload, upload) || other.upload == upload)&&(identical(other.download, download) || other.download == download)&&(identical(other.total, total) || other.total == total)&&(identical(other.expire, expire) || other.expire == expire));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,upload,download,total,expire);\n\n@override\nString toString() {\n  return 'ProviderSubscriptionInfo(upload: $upload, download: $download, total: $total, expire: $expire)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ProviderSubscriptionInfoCopyWith<$Res> implements $ProviderSubscriptionInfoCopyWith<$Res> {\n  factory _$ProviderSubscriptionInfoCopyWith(_ProviderSubscriptionInfo value, $Res Function(_ProviderSubscriptionInfo) _then) = __$ProviderSubscriptionInfoCopyWithImpl;\n@override @useResult\n$Res call({\n@JsonKey(name: 'UPLOAD') int upload,@JsonKey(name: 'DOWNLOAD') int download,@JsonKey(name: 'TOTAL') int total,@JsonKey(name: 'EXPIRE') int expire\n});\n\n\n\n\n}\n/// @nodoc\nclass __$ProviderSubscriptionInfoCopyWithImpl<$Res>\n    implements _$ProviderSubscriptionInfoCopyWith<$Res> {\n  __$ProviderSubscriptionInfoCopyWithImpl(this._self, this._then);\n\n  final _ProviderSubscriptionInfo _self;\n  final $Res Function(_ProviderSubscriptionInfo) _then;\n\n/// Create a copy of ProviderSubscriptionInfo\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? upload = null,Object? download = null,Object? total = null,Object? expire = null,}) {\n  return _then(_ProviderSubscriptionInfo(\nupload: null == upload ? _self.upload : upload // ignore: cast_nullable_to_non_nullable\nas int,download: null == download ? _self.download : download // ignore: cast_nullable_to_non_nullable\nas int,total: null == total ? _self.total : total // ignore: cast_nullable_to_non_nullable\nas int,expire: null == expire ? _self.expire : expire // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$ExternalProvider {\n\n String get name; String get type; String? get path; int get count;@JsonKey(name: 'subscription-info', fromJson: subscriptionInfoFormCore) SubscriptionInfo? get subscriptionInfo; bool get isUpdating;@JsonKey(name: 'vehicle-type') String get vehicleType;@JsonKey(name: 'update-at') DateTime get updateAt;\n/// Create a copy of ExternalProvider\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ExternalProviderCopyWith<ExternalProvider> get copyWith => _$ExternalProviderCopyWithImpl<ExternalProvider>(this as ExternalProvider, _$identity);\n\n  /// Serializes this ExternalProvider to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is ExternalProvider&&(identical(other.name, name) || other.name == name)&&(identical(other.type, type) || other.type == type)&&(identical(other.path, path) || other.path == path)&&(identical(other.count, count) || other.count == count)&&(identical(other.subscriptionInfo, subscriptionInfo) || other.subscriptionInfo == subscriptionInfo)&&(identical(other.isUpdating, isUpdating) || other.isUpdating == isUpdating)&&(identical(other.vehicleType, vehicleType) || other.vehicleType == vehicleType)&&(identical(other.updateAt, updateAt) || other.updateAt == updateAt));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,name,type,path,count,subscriptionInfo,isUpdating,vehicleType,updateAt);\n\n@override\nString toString() {\n  return 'ExternalProvider(name: $name, type: $type, path: $path, count: $count, subscriptionInfo: $subscriptionInfo, isUpdating: $isUpdating, vehicleType: $vehicleType, updateAt: $updateAt)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ExternalProviderCopyWith<$Res>  {\n  factory $ExternalProviderCopyWith(ExternalProvider value, $Res Function(ExternalProvider) _then) = _$ExternalProviderCopyWithImpl;\n@useResult\n$Res call({\n String name, String type, String? path, int count,@JsonKey(name: 'subscription-info', fromJson: subscriptionInfoFormCore) SubscriptionInfo? subscriptionInfo, bool isUpdating,@JsonKey(name: 'vehicle-type') String vehicleType,@JsonKey(name: 'update-at') DateTime updateAt\n});\n\n\n$SubscriptionInfoCopyWith<$Res>? get subscriptionInfo;\n\n}\n/// @nodoc\nclass _$ExternalProviderCopyWithImpl<$Res>\n    implements $ExternalProviderCopyWith<$Res> {\n  _$ExternalProviderCopyWithImpl(this._self, this._then);\n\n  final ExternalProvider _self;\n  final $Res Function(ExternalProvider) _then;\n\n/// Create a copy of ExternalProvider\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? name = null,Object? type = null,Object? path = freezed,Object? count = null,Object? subscriptionInfo = freezed,Object? isUpdating = null,Object? vehicleType = null,Object? updateAt = null,}) {\n  return _then(_self.copyWith(\nname: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable\nas String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable\nas String,path: freezed == path ? _self.path : path // ignore: cast_nullable_to_non_nullable\nas String?,count: null == count ? _self.count : count // ignore: cast_nullable_to_non_nullable\nas int,subscriptionInfo: freezed == subscriptionInfo ? _self.subscriptionInfo : subscriptionInfo // ignore: cast_nullable_to_non_nullable\nas SubscriptionInfo?,isUpdating: null == isUpdating ? _self.isUpdating : isUpdating // ignore: cast_nullable_to_non_nullable\nas bool,vehicleType: null == vehicleType ? _self.vehicleType : vehicleType // ignore: cast_nullable_to_non_nullable\nas String,updateAt: null == updateAt ? _self.updateAt : updateAt // ignore: cast_nullable_to_non_nullable\nas DateTime,\n  ));\n}\n/// Create a copy of ExternalProvider\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$SubscriptionInfoCopyWith<$Res>? get subscriptionInfo {\n    if (_self.subscriptionInfo == null) {\n    return null;\n  }\n\n  return $SubscriptionInfoCopyWith<$Res>(_self.subscriptionInfo!, (value) {\n    return _then(_self.copyWith(subscriptionInfo: value));\n  });\n}\n}\n\n\n/// Adds pattern-matching-related methods to [ExternalProvider].\nextension ExternalProviderPatterns on ExternalProvider {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ExternalProvider value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _ExternalProvider() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ExternalProvider value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ExternalProvider():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ExternalProvider value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ExternalProvider() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String name,  String type,  String? path,  int count, @JsonKey(name: 'subscription-info', fromJson: subscriptionInfoFormCore)  SubscriptionInfo? subscriptionInfo,  bool isUpdating, @JsonKey(name: 'vehicle-type')  String vehicleType, @JsonKey(name: 'update-at')  DateTime updateAt)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _ExternalProvider() when $default != null:\nreturn $default(_that.name,_that.type,_that.path,_that.count,_that.subscriptionInfo,_that.isUpdating,_that.vehicleType,_that.updateAt);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String name,  String type,  String? path,  int count, @JsonKey(name: 'subscription-info', fromJson: subscriptionInfoFormCore)  SubscriptionInfo? subscriptionInfo,  bool isUpdating, @JsonKey(name: 'vehicle-type')  String vehicleType, @JsonKey(name: 'update-at')  DateTime updateAt)  $default,) {final _that = this;\nswitch (_that) {\ncase _ExternalProvider():\nreturn $default(_that.name,_that.type,_that.path,_that.count,_that.subscriptionInfo,_that.isUpdating,_that.vehicleType,_that.updateAt);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String name,  String type,  String? path,  int count, @JsonKey(name: 'subscription-info', fromJson: subscriptionInfoFormCore)  SubscriptionInfo? subscriptionInfo,  bool isUpdating, @JsonKey(name: 'vehicle-type')  String vehicleType, @JsonKey(name: 'update-at')  DateTime updateAt)?  $default,) {final _that = this;\nswitch (_that) {\ncase _ExternalProvider() when $default != null:\nreturn $default(_that.name,_that.type,_that.path,_that.count,_that.subscriptionInfo,_that.isUpdating,_that.vehicleType,_that.updateAt);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _ExternalProvider implements ExternalProvider {\n  const _ExternalProvider({required this.name, required this.type, this.path, required this.count, @JsonKey(name: 'subscription-info', fromJson: subscriptionInfoFormCore) this.subscriptionInfo, this.isUpdating = false, @JsonKey(name: 'vehicle-type') required this.vehicleType, @JsonKey(name: 'update-at') required this.updateAt});\n  factory _ExternalProvider.fromJson(Map<String, dynamic> json) => _$ExternalProviderFromJson(json);\n\n@override final  String name;\n@override final  String type;\n@override final  String? path;\n@override final  int count;\n@override@JsonKey(name: 'subscription-info', fromJson: subscriptionInfoFormCore) final  SubscriptionInfo? subscriptionInfo;\n@override@JsonKey() final  bool isUpdating;\n@override@JsonKey(name: 'vehicle-type') final  String vehicleType;\n@override@JsonKey(name: 'update-at') final  DateTime updateAt;\n\n/// Create a copy of ExternalProvider\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ExternalProviderCopyWith<_ExternalProvider> get copyWith => __$ExternalProviderCopyWithImpl<_ExternalProvider>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$ExternalProviderToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _ExternalProvider&&(identical(other.name, name) || other.name == name)&&(identical(other.type, type) || other.type == type)&&(identical(other.path, path) || other.path == path)&&(identical(other.count, count) || other.count == count)&&(identical(other.subscriptionInfo, subscriptionInfo) || other.subscriptionInfo == subscriptionInfo)&&(identical(other.isUpdating, isUpdating) || other.isUpdating == isUpdating)&&(identical(other.vehicleType, vehicleType) || other.vehicleType == vehicleType)&&(identical(other.updateAt, updateAt) || other.updateAt == updateAt));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,name,type,path,count,subscriptionInfo,isUpdating,vehicleType,updateAt);\n\n@override\nString toString() {\n  return 'ExternalProvider(name: $name, type: $type, path: $path, count: $count, subscriptionInfo: $subscriptionInfo, isUpdating: $isUpdating, vehicleType: $vehicleType, updateAt: $updateAt)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ExternalProviderCopyWith<$Res> implements $ExternalProviderCopyWith<$Res> {\n  factory _$ExternalProviderCopyWith(_ExternalProvider value, $Res Function(_ExternalProvider) _then) = __$ExternalProviderCopyWithImpl;\n@override @useResult\n$Res call({\n String name, String type, String? path, int count,@JsonKey(name: 'subscription-info', fromJson: subscriptionInfoFormCore) SubscriptionInfo? subscriptionInfo, bool isUpdating,@JsonKey(name: 'vehicle-type') String vehicleType,@JsonKey(name: 'update-at') DateTime updateAt\n});\n\n\n@override $SubscriptionInfoCopyWith<$Res>? get subscriptionInfo;\n\n}\n/// @nodoc\nclass __$ExternalProviderCopyWithImpl<$Res>\n    implements _$ExternalProviderCopyWith<$Res> {\n  __$ExternalProviderCopyWithImpl(this._self, this._then);\n\n  final _ExternalProvider _self;\n  final $Res Function(_ExternalProvider) _then;\n\n/// Create a copy of ExternalProvider\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? name = null,Object? type = null,Object? path = freezed,Object? count = null,Object? subscriptionInfo = freezed,Object? isUpdating = null,Object? vehicleType = null,Object? updateAt = null,}) {\n  return _then(_ExternalProvider(\nname: null == name ? _self.name : name // ignore: cast_nullable_to_non_nullable\nas String,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable\nas String,path: freezed == path ? _self.path : path // ignore: cast_nullable_to_non_nullable\nas String?,count: null == count ? _self.count : count // ignore: cast_nullable_to_non_nullable\nas int,subscriptionInfo: freezed == subscriptionInfo ? _self.subscriptionInfo : subscriptionInfo // ignore: cast_nullable_to_non_nullable\nas SubscriptionInfo?,isUpdating: null == isUpdating ? _self.isUpdating : isUpdating // ignore: cast_nullable_to_non_nullable\nas bool,vehicleType: null == vehicleType ? _self.vehicleType : vehicleType // ignore: cast_nullable_to_non_nullable\nas String,updateAt: null == updateAt ? _self.updateAt : updateAt // ignore: cast_nullable_to_non_nullable\nas DateTime,\n  ));\n}\n\n/// Create a copy of ExternalProvider\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$SubscriptionInfoCopyWith<$Res>? get subscriptionInfo {\n    if (_self.subscriptionInfo == null) {\n    return null;\n  }\n\n  return $SubscriptionInfoCopyWith<$Res>(_self.subscriptionInfo!, (value) {\n    return _then(_self.copyWith(subscriptionInfo: value));\n  });\n}\n}\n\n\n/// @nodoc\nmixin _$Action {\n\n ActionMethod get method; dynamic get data; String get id;\n/// Create a copy of Action\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ActionCopyWith<Action> get copyWith => _$ActionCopyWithImpl<Action>(this as Action, _$identity);\n\n  /// Serializes this Action to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is Action&&(identical(other.method, method) || other.method == method)&&const DeepCollectionEquality().equals(other.data, data)&&(identical(other.id, id) || other.id == id));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,method,const DeepCollectionEquality().hash(data),id);\n\n@override\nString toString() {\n  return 'Action(method: $method, data: $data, id: $id)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ActionCopyWith<$Res>  {\n  factory $ActionCopyWith(Action value, $Res Function(Action) _then) = _$ActionCopyWithImpl;\n@useResult\n$Res call({\n ActionMethod method, dynamic data, String id\n});\n\n\n\n\n}\n/// @nodoc\nclass _$ActionCopyWithImpl<$Res>\n    implements $ActionCopyWith<$Res> {\n  _$ActionCopyWithImpl(this._self, this._then);\n\n  final Action _self;\n  final $Res Function(Action) _then;\n\n/// Create a copy of Action\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? method = null,Object? data = freezed,Object? id = null,}) {\n  return _then(_self.copyWith(\nmethod: null == method ? _self.method : method // ignore: cast_nullable_to_non_nullable\nas ActionMethod,data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable\nas dynamic,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable\nas String,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [Action].\nextension ActionPatterns on Action {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _Action value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _Action() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _Action value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Action():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _Action value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Action() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( ActionMethod method,  dynamic data,  String id)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _Action() when $default != null:\nreturn $default(_that.method,_that.data,_that.id);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( ActionMethod method,  dynamic data,  String id)  $default,) {final _that = this;\nswitch (_that) {\ncase _Action():\nreturn $default(_that.method,_that.data,_that.id);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( ActionMethod method,  dynamic data,  String id)?  $default,) {final _that = this;\nswitch (_that) {\ncase _Action() when $default != null:\nreturn $default(_that.method,_that.data,_that.id);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _Action implements Action {\n  const _Action({required this.method, required this.data, required this.id});\n  factory _Action.fromJson(Map<String, dynamic> json) => _$ActionFromJson(json);\n\n@override final  ActionMethod method;\n@override final  dynamic data;\n@override final  String id;\n\n/// Create a copy of Action\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ActionCopyWith<_Action> get copyWith => __$ActionCopyWithImpl<_Action>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$ActionToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _Action&&(identical(other.method, method) || other.method == method)&&const DeepCollectionEquality().equals(other.data, data)&&(identical(other.id, id) || other.id == id));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,method,const DeepCollectionEquality().hash(data),id);\n\n@override\nString toString() {\n  return 'Action(method: $method, data: $data, id: $id)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ActionCopyWith<$Res> implements $ActionCopyWith<$Res> {\n  factory _$ActionCopyWith(_Action value, $Res Function(_Action) _then) = __$ActionCopyWithImpl;\n@override @useResult\n$Res call({\n ActionMethod method, dynamic data, String id\n});\n\n\n\n\n}\n/// @nodoc\nclass __$ActionCopyWithImpl<$Res>\n    implements _$ActionCopyWith<$Res> {\n  __$ActionCopyWithImpl(this._self, this._then);\n\n  final _Action _self;\n  final $Res Function(_Action) _then;\n\n/// Create a copy of Action\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? method = null,Object? data = freezed,Object? id = null,}) {\n  return _then(_Action(\nmethod: null == method ? _self.method : method // ignore: cast_nullable_to_non_nullable\nas ActionMethod,data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable\nas dynamic,id: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable\nas String,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$ActionResult {\n\n ActionMethod get method; dynamic get data; String? get id; ResultType get code;\n/// Create a copy of ActionResult\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ActionResultCopyWith<ActionResult> get copyWith => _$ActionResultCopyWithImpl<ActionResult>(this as ActionResult, _$identity);\n\n  /// Serializes this ActionResult to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is ActionResult&&(identical(other.method, method) || other.method == method)&&const DeepCollectionEquality().equals(other.data, data)&&(identical(other.id, id) || other.id == id)&&(identical(other.code, code) || other.code == code));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,method,const DeepCollectionEquality().hash(data),id,code);\n\n@override\nString toString() {\n  return 'ActionResult(method: $method, data: $data, id: $id, code: $code)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ActionResultCopyWith<$Res>  {\n  factory $ActionResultCopyWith(ActionResult value, $Res Function(ActionResult) _then) = _$ActionResultCopyWithImpl;\n@useResult\n$Res call({\n ActionMethod method, dynamic data, String? id, ResultType code\n});\n\n\n\n\n}\n/// @nodoc\nclass _$ActionResultCopyWithImpl<$Res>\n    implements $ActionResultCopyWith<$Res> {\n  _$ActionResultCopyWithImpl(this._self, this._then);\n\n  final ActionResult _self;\n  final $Res Function(ActionResult) _then;\n\n/// Create a copy of ActionResult\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? method = null,Object? data = freezed,Object? id = freezed,Object? code = null,}) {\n  return _then(_self.copyWith(\nmethod: null == method ? _self.method : method // ignore: cast_nullable_to_non_nullable\nas ActionMethod,data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable\nas dynamic,id: freezed == id ? _self.id : id // ignore: cast_nullable_to_non_nullable\nas String?,code: null == code ? _self.code : code // ignore: cast_nullable_to_non_nullable\nas ResultType,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [ActionResult].\nextension ActionResultPatterns on ActionResult {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ActionResult value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _ActionResult() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ActionResult value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ActionResult():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ActionResult value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ActionResult() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( ActionMethod method,  dynamic data,  String? id,  ResultType code)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _ActionResult() when $default != null:\nreturn $default(_that.method,_that.data,_that.id,_that.code);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( ActionMethod method,  dynamic data,  String? id,  ResultType code)  $default,) {final _that = this;\nswitch (_that) {\ncase _ActionResult():\nreturn $default(_that.method,_that.data,_that.id,_that.code);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( ActionMethod method,  dynamic data,  String? id,  ResultType code)?  $default,) {final _that = this;\nswitch (_that) {\ncase _ActionResult() when $default != null:\nreturn $default(_that.method,_that.data,_that.id,_that.code);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _ActionResult implements ActionResult {\n  const _ActionResult({required this.method, required this.data, this.id, this.code = ResultType.success});\n  factory _ActionResult.fromJson(Map<String, dynamic> json) => _$ActionResultFromJson(json);\n\n@override final  ActionMethod method;\n@override final  dynamic data;\n@override final  String? id;\n@override@JsonKey() final  ResultType code;\n\n/// Create a copy of ActionResult\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ActionResultCopyWith<_ActionResult> get copyWith => __$ActionResultCopyWithImpl<_ActionResult>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$ActionResultToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _ActionResult&&(identical(other.method, method) || other.method == method)&&const DeepCollectionEquality().equals(other.data, data)&&(identical(other.id, id) || other.id == id)&&(identical(other.code, code) || other.code == code));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,method,const DeepCollectionEquality().hash(data),id,code);\n\n@override\nString toString() {\n  return 'ActionResult(method: $method, data: $data, id: $id, code: $code)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ActionResultCopyWith<$Res> implements $ActionResultCopyWith<$Res> {\n  factory _$ActionResultCopyWith(_ActionResult value, $Res Function(_ActionResult) _then) = __$ActionResultCopyWithImpl;\n@override @useResult\n$Res call({\n ActionMethod method, dynamic data, String? id, ResultType code\n});\n\n\n\n\n}\n/// @nodoc\nclass __$ActionResultCopyWithImpl<$Res>\n    implements _$ActionResultCopyWith<$Res> {\n  __$ActionResultCopyWithImpl(this._self, this._then);\n\n  final _ActionResult _self;\n  final $Res Function(_ActionResult) _then;\n\n/// Create a copy of ActionResult\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? method = null,Object? data = freezed,Object? id = freezed,Object? code = null,}) {\n  return _then(_ActionResult(\nmethod: null == method ? _self.method : method // ignore: cast_nullable_to_non_nullable\nas ActionMethod,data: freezed == data ? _self.data : data // ignore: cast_nullable_to_non_nullable\nas dynamic,id: freezed == id ? _self.id : id // ignore: cast_nullable_to_non_nullable\nas String?,code: null == code ? _self.code : code // ignore: cast_nullable_to_non_nullable\nas ResultType,\n  ));\n}\n\n\n}\n\n// dart format on\n"
  },
  {
    "path": "lib/models/generated/core.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of '../core.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_SetupParams _$SetupParamsFromJson(Map<String, dynamic> json) => _SetupParams(\n  config: json['config'] as Map<String, dynamic>,\n  selectedMap: Map<String, String>.from(json['selected-map'] as Map),\n  testUrl: json['test-url'] as String,\n  overrideTestUrl: json['override-test-url'] as bool? ?? true,\n);\n\nMap<String, dynamic> _$SetupParamsToJson(_SetupParams instance) =>\n    <String, dynamic>{\n      'config': instance.config,\n      'selected-map': instance.selectedMap,\n      'test-url': instance.testUrl,\n      'override-test-url': instance.overrideTestUrl,\n    };\n\n_UpdateParams _$UpdateParamsFromJson(Map<String, dynamic> json) =>\n    _UpdateParams(\n      tun: Tun.fromJson(json['tun'] as Map<String, dynamic>),\n      mixedPort: (json['mixed-port'] as num).toInt(),\n      allowLan: json['allow-lan'] as bool,\n      findProcessMode: $enumDecode(\n        _$FindProcessModeEnumMap,\n        json['find-process-mode'],\n      ),\n      mode: $enumDecode(_$ModeEnumMap, json['mode']),\n      logLevel: $enumDecode(_$LogLevelEnumMap, json['log-level']),\n      ipv6: json['ipv6'] as bool,\n      tcpConcurrent: json['tcp-concurrent'] as bool,\n      externalController: $enumDecode(\n        _$ExternalControllerStatusEnumMap,\n        json['external-controller'],\n      ),\n      unifiedDelay: json['unified-delay'] as bool,\n    );\n\nMap<String, dynamic> _$UpdateParamsToJson(_UpdateParams instance) =>\n    <String, dynamic>{\n      'tun': instance.tun,\n      'mixed-port': instance.mixedPort,\n      'allow-lan': instance.allowLan,\n      'find-process-mode': _$FindProcessModeEnumMap[instance.findProcessMode]!,\n      'mode': _$ModeEnumMap[instance.mode]!,\n      'log-level': _$LogLevelEnumMap[instance.logLevel]!,\n      'ipv6': instance.ipv6,\n      'tcp-concurrent': instance.tcpConcurrent,\n      'external-controller':\n          _$ExternalControllerStatusEnumMap[instance.externalController]!,\n      'unified-delay': instance.unifiedDelay,\n    };\n\nconst _$FindProcessModeEnumMap = {\n  FindProcessMode.always: 'always',\n  FindProcessMode.off: 'off',\n};\n\nconst _$ModeEnumMap = {\n  Mode.rule: 'rule',\n  Mode.global: 'global',\n  Mode.direct: 'direct',\n};\n\nconst _$LogLevelEnumMap = {\n  LogLevel.debug: 'debug',\n  LogLevel.info: 'info',\n  LogLevel.warning: 'warning',\n  LogLevel.error: 'error',\n  LogLevel.silent: 'silent',\n};\n\nconst _$ExternalControllerStatusEnumMap = {\n  ExternalControllerStatus.close: '',\n  ExternalControllerStatus.open: '127.0.0.1:9090',\n};\n\n_CoreState _$CoreStateFromJson(Map<String, dynamic> json) => _CoreState(\n  vpnProps: VpnProps.fromJson(json['vpn-props'] as Map<String, dynamic>),\n  onlyStatisticsProxy: json['only-statistics-proxy'] as bool,\n  currentProfileName: json['current-profile-name'] as String,\n  bypassDomain:\n      (json['bypass-domain'] as List<dynamic>?)\n          ?.map((e) => e as String)\n          .toList() ??\n      const [],\n);\n\nMap<String, dynamic> _$CoreStateToJson(_CoreState instance) =>\n    <String, dynamic>{\n      'vpn-props': instance.vpnProps,\n      'only-statistics-proxy': instance.onlyStatisticsProxy,\n      'current-profile-name': instance.currentProfileName,\n      'bypass-domain': instance.bypassDomain,\n    };\n\n_AndroidVpnOptions _$AndroidVpnOptionsFromJson(Map<String, dynamic> json) =>\n    _AndroidVpnOptions(\n      enable: json['enable'] as bool,\n      port: (json['port'] as num).toInt(),\n      accessControl: json['accessControl'] == null\n          ? null\n          : AccessControl.fromJson(\n              json['accessControl'] as Map<String, dynamic>,\n            ),\n      allowBypass: json['allowBypass'] as bool,\n      systemProxy: json['systemProxy'] as bool,\n      bypassDomain: (json['bypassDomain'] as List<dynamic>)\n          .map((e) => e as String)\n          .toList(),\n      ipv4Address: json['ipv4Address'] as String,\n      ipv6Address: json['ipv6Address'] as String,\n      routeAddress:\n          (json['routeAddress'] as List<dynamic>?)\n              ?.map((e) => e as String)\n              .toList() ??\n          const [],\n      routeMode: json['routeMode'] as String? ?? 'config',\n      dnsServerAddress: json['dnsServerAddress'] as String,\n      dozeSuspend: json['dozeSuspend'] as bool? ?? false,\n    );\n\nMap<String, dynamic> _$AndroidVpnOptionsToJson(_AndroidVpnOptions instance) =>\n    <String, dynamic>{\n      'enable': instance.enable,\n      'port': instance.port,\n      'accessControl': instance.accessControl,\n      'allowBypass': instance.allowBypass,\n      'systemProxy': instance.systemProxy,\n      'bypassDomain': instance.bypassDomain,\n      'ipv4Address': instance.ipv4Address,\n      'ipv6Address': instance.ipv6Address,\n      'routeAddress': instance.routeAddress,\n      'routeMode': instance.routeMode,\n      'dnsServerAddress': instance.dnsServerAddress,\n      'dozeSuspend': instance.dozeSuspend,\n    };\n\n_InitParams _$InitParamsFromJson(Map<String, dynamic> json) => _InitParams(\n  homeDir: json['home-dir'] as String,\n  version: (json['version'] as num).toInt(),\n);\n\nMap<String, dynamic> _$InitParamsToJson(_InitParams instance) =>\n    <String, dynamic>{\n      'home-dir': instance.homeDir,\n      'version': instance.version,\n    };\n\n_ChangeProxyParams _$ChangeProxyParamsFromJson(Map<String, dynamic> json) =>\n    _ChangeProxyParams(\n      groupName: json['group-name'] as String,\n      proxyName: json['proxy-name'] as String,\n    );\n\nMap<String, dynamic> _$ChangeProxyParamsToJson(_ChangeProxyParams instance) =>\n    <String, dynamic>{\n      'group-name': instance.groupName,\n      'proxy-name': instance.proxyName,\n    };\n\n_UpdateGeoDataParams _$UpdateGeoDataParamsFromJson(Map<String, dynamic> json) =>\n    _UpdateGeoDataParams(\n      geoType: json['geo-type'] as String,\n      geoName: json['geo-name'] as String,\n    );\n\nMap<String, dynamic> _$UpdateGeoDataParamsToJson(\n  _UpdateGeoDataParams instance,\n) => <String, dynamic>{\n  'geo-type': instance.geoType,\n  'geo-name': instance.geoName,\n};\n\n_AppMessage _$AppMessageFromJson(Map<String, dynamic> json) => _AppMessage(\n  type: $enumDecode(_$AppMessageTypeEnumMap, json['type']),\n  data: json['data'],\n);\n\nMap<String, dynamic> _$AppMessageToJson(_AppMessage instance) =>\n    <String, dynamic>{\n      'type': _$AppMessageTypeEnumMap[instance.type]!,\n      'data': instance.data,\n    };\n\nconst _$AppMessageTypeEnumMap = {\n  AppMessageType.log: 'log',\n  AppMessageType.delay: 'delay',\n  AppMessageType.request: 'request',\n  AppMessageType.loaded: 'loaded',\n};\n\n_InvokeMessage _$InvokeMessageFromJson(Map<String, dynamic> json) =>\n    _InvokeMessage(\n      type: $enumDecode(_$InvokeMessageTypeEnumMap, json['type']),\n      data: json['data'],\n    );\n\nMap<String, dynamic> _$InvokeMessageToJson(_InvokeMessage instance) =>\n    <String, dynamic>{\n      'type': _$InvokeMessageTypeEnumMap[instance.type]!,\n      'data': instance.data,\n    };\n\nconst _$InvokeMessageTypeEnumMap = {\n  InvokeMessageType.protect: 'protect',\n  InvokeMessageType.process: 'process',\n};\n\n_Delay _$DelayFromJson(Map<String, dynamic> json) => _Delay(\n  name: json['name'] as String,\n  url: json['url'] as String,\n  value: (json['value'] as num?)?.toInt(),\n);\n\nMap<String, dynamic> _$DelayToJson(_Delay instance) => <String, dynamic>{\n  'name': instance.name,\n  'url': instance.url,\n  'value': instance.value,\n};\n\n_Now _$NowFromJson(Map<String, dynamic> json) =>\n    _Now(name: json['name'] as String, value: json['value'] as String);\n\nMap<String, dynamic> _$NowToJson(_Now instance) => <String, dynamic>{\n  'name': instance.name,\n  'value': instance.value,\n};\n\n_ProviderSubscriptionInfo _$ProviderSubscriptionInfoFromJson(\n  Map<String, dynamic> json,\n) => _ProviderSubscriptionInfo(\n  upload: (json['UPLOAD'] as num?)?.toInt() ?? 0,\n  download: (json['DOWNLOAD'] as num?)?.toInt() ?? 0,\n  total: (json['TOTAL'] as num?)?.toInt() ?? 0,\n  expire: (json['EXPIRE'] as num?)?.toInt() ?? 0,\n);\n\nMap<String, dynamic> _$ProviderSubscriptionInfoToJson(\n  _ProviderSubscriptionInfo instance,\n) => <String, dynamic>{\n  'UPLOAD': instance.upload,\n  'DOWNLOAD': instance.download,\n  'TOTAL': instance.total,\n  'EXPIRE': instance.expire,\n};\n\n_ExternalProvider _$ExternalProviderFromJson(Map<String, dynamic> json) =>\n    _ExternalProvider(\n      name: json['name'] as String,\n      type: json['type'] as String,\n      path: json['path'] as String?,\n      count: (json['count'] as num).toInt(),\n      subscriptionInfo: subscriptionInfoFormCore(\n        json['subscription-info'] as Map<String, Object?>?,\n      ),\n      isUpdating: json['isUpdating'] as bool? ?? false,\n      vehicleType: json['vehicle-type'] as String,\n      updateAt: DateTime.parse(json['update-at'] as String),\n    );\n\nMap<String, dynamic> _$ExternalProviderToJson(_ExternalProvider instance) =>\n    <String, dynamic>{\n      'name': instance.name,\n      'type': instance.type,\n      'path': instance.path,\n      'count': instance.count,\n      'subscription-info': instance.subscriptionInfo,\n      'isUpdating': instance.isUpdating,\n      'vehicle-type': instance.vehicleType,\n      'update-at': instance.updateAt.toIso8601String(),\n    };\n\n_Action _$ActionFromJson(Map<String, dynamic> json) => _Action(\n  method: $enumDecode(_$ActionMethodEnumMap, json['method']),\n  data: json['data'],\n  id: json['id'] as String,\n);\n\nMap<String, dynamic> _$ActionToJson(_Action instance) => <String, dynamic>{\n  'method': _$ActionMethodEnumMap[instance.method]!,\n  'data': instance.data,\n  'id': instance.id,\n};\n\nconst _$ActionMethodEnumMap = {\n  ActionMethod.message: 'message',\n  ActionMethod.initClash: 'initClash',\n  ActionMethod.getIsInit: 'getIsInit',\n  ActionMethod.forceGc: 'forceGc',\n  ActionMethod.shutdown: 'shutdown',\n  ActionMethod.validateConfig: 'validateConfig',\n  ActionMethod.updateConfig: 'updateConfig',\n  ActionMethod.getConfig: 'getConfig',\n  ActionMethod.getProxies: 'getProxies',\n  ActionMethod.changeProxy: 'changeProxy',\n  ActionMethod.getTraffic: 'getTraffic',\n  ActionMethod.getTotalTraffic: 'getTotalTraffic',\n  ActionMethod.resetTraffic: 'resetTraffic',\n  ActionMethod.asyncTestDelay: 'asyncTestDelay',\n  ActionMethod.getConnections: 'getConnections',\n  ActionMethod.closeConnections: 'closeConnections',\n  ActionMethod.resetConnections: 'resetConnections',\n  ActionMethod.closeConnection: 'closeConnection',\n  ActionMethod.getExternalProviders: 'getExternalProviders',\n  ActionMethod.getExternalProvider: 'getExternalProvider',\n  ActionMethod.updateGeoData: 'updateGeoData',\n  ActionMethod.updateExternalProvider: 'updateExternalProvider',\n  ActionMethod.sideLoadExternalProvider: 'sideLoadExternalProvider',\n  ActionMethod.startLog: 'startLog',\n  ActionMethod.stopLog: 'stopLog',\n  ActionMethod.startListener: 'startListener',\n  ActionMethod.stopListener: 'stopListener',\n  ActionMethod.getCountryCode: 'getCountryCode',\n  ActionMethod.getMemory: 'getMemory',\n  ActionMethod.crash: 'crash',\n  ActionMethod.setupConfig: 'setupConfig',\n  ActionMethod.flushFakeIP: 'flushFakeIP',\n  ActionMethod.flushDnsCache: 'flushDnsCache',\n  ActionMethod.setState: 'setState',\n  ActionMethod.startTun: 'startTun',\n  ActionMethod.stopTun: 'stopTun',\n  ActionMethod.getRunTime: 'getRunTime',\n  ActionMethod.updateDns: 'updateDns',\n  ActionMethod.getAndroidVpnOptions: 'getAndroidVpnOptions',\n  ActionMethod.getCurrentProfileName: 'getCurrentProfileName',\n};\n\n_ActionResult _$ActionResultFromJson(Map<String, dynamic> json) =>\n    _ActionResult(\n      method: $enumDecode(_$ActionMethodEnumMap, json['method']),\n      data: json['data'],\n      id: json['id'] as String?,\n      code:\n          $enumDecodeNullable(_$ResultTypeEnumMap, json['code']) ??\n          ResultType.success,\n    );\n\nMap<String, dynamic> _$ActionResultToJson(_ActionResult instance) =>\n    <String, dynamic>{\n      'method': _$ActionMethodEnumMap[instance.method]!,\n      'data': instance.data,\n      'id': instance.id,\n      'code': _$ResultTypeEnumMap[instance.code]!,\n    };\n\nconst _$ResultTypeEnumMap = {ResultType.success: 0, ResultType.error: -1};\n"
  },
  {
    "path": "lib/models/generated/profile.freezed.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n// coverage:ignore-file\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of '../profile.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\n// dart format off\nT _$identity<T>(T value) => value;\n\n/// @nodoc\nmixin _$SubscriptionInfo {\n\n int get upload; int get download; int get total; int get expire;\n/// Create a copy of SubscriptionInfo\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$SubscriptionInfoCopyWith<SubscriptionInfo> get copyWith => _$SubscriptionInfoCopyWithImpl<SubscriptionInfo>(this as SubscriptionInfo, _$identity);\n\n  /// Serializes this SubscriptionInfo to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is SubscriptionInfo&&(identical(other.upload, upload) || other.upload == upload)&&(identical(other.download, download) || other.download == download)&&(identical(other.total, total) || other.total == total)&&(identical(other.expire, expire) || other.expire == expire));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,upload,download,total,expire);\n\n@override\nString toString() {\n  return 'SubscriptionInfo(upload: $upload, download: $download, total: $total, expire: $expire)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $SubscriptionInfoCopyWith<$Res>  {\n  factory $SubscriptionInfoCopyWith(SubscriptionInfo value, $Res Function(SubscriptionInfo) _then) = _$SubscriptionInfoCopyWithImpl;\n@useResult\n$Res call({\n int upload, int download, int total, int expire\n});\n\n\n\n\n}\n/// @nodoc\nclass _$SubscriptionInfoCopyWithImpl<$Res>\n    implements $SubscriptionInfoCopyWith<$Res> {\n  _$SubscriptionInfoCopyWithImpl(this._self, this._then);\n\n  final SubscriptionInfo _self;\n  final $Res Function(SubscriptionInfo) _then;\n\n/// Create a copy of SubscriptionInfo\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? upload = null,Object? download = null,Object? total = null,Object? expire = null,}) {\n  return _then(_self.copyWith(\nupload: null == upload ? _self.upload : upload // ignore: cast_nullable_to_non_nullable\nas int,download: null == download ? _self.download : download // ignore: cast_nullable_to_non_nullable\nas int,total: null == total ? _self.total : total // ignore: cast_nullable_to_non_nullable\nas int,expire: null == expire ? _self.expire : expire // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [SubscriptionInfo].\nextension SubscriptionInfoPatterns on SubscriptionInfo {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _SubscriptionInfo value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _SubscriptionInfo() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _SubscriptionInfo value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _SubscriptionInfo():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _SubscriptionInfo value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _SubscriptionInfo() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( int upload,  int download,  int total,  int expire)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _SubscriptionInfo() when $default != null:\nreturn $default(_that.upload,_that.download,_that.total,_that.expire);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( int upload,  int download,  int total,  int expire)  $default,) {final _that = this;\nswitch (_that) {\ncase _SubscriptionInfo():\nreturn $default(_that.upload,_that.download,_that.total,_that.expire);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( int upload,  int download,  int total,  int expire)?  $default,) {final _that = this;\nswitch (_that) {\ncase _SubscriptionInfo() when $default != null:\nreturn $default(_that.upload,_that.download,_that.total,_that.expire);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _SubscriptionInfo implements SubscriptionInfo {\n  const _SubscriptionInfo({this.upload = 0, this.download = 0, this.total = 0, this.expire = 0});\n  factory _SubscriptionInfo.fromJson(Map<String, dynamic> json) => _$SubscriptionInfoFromJson(json);\n\n@override@JsonKey() final  int upload;\n@override@JsonKey() final  int download;\n@override@JsonKey() final  int total;\n@override@JsonKey() final  int expire;\n\n/// Create a copy of SubscriptionInfo\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$SubscriptionInfoCopyWith<_SubscriptionInfo> get copyWith => __$SubscriptionInfoCopyWithImpl<_SubscriptionInfo>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$SubscriptionInfoToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _SubscriptionInfo&&(identical(other.upload, upload) || other.upload == upload)&&(identical(other.download, download) || other.download == download)&&(identical(other.total, total) || other.total == total)&&(identical(other.expire, expire) || other.expire == expire));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,upload,download,total,expire);\n\n@override\nString toString() {\n  return 'SubscriptionInfo(upload: $upload, download: $download, total: $total, expire: $expire)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$SubscriptionInfoCopyWith<$Res> implements $SubscriptionInfoCopyWith<$Res> {\n  factory _$SubscriptionInfoCopyWith(_SubscriptionInfo value, $Res Function(_SubscriptionInfo) _then) = __$SubscriptionInfoCopyWithImpl;\n@override @useResult\n$Res call({\n int upload, int download, int total, int expire\n});\n\n\n\n\n}\n/// @nodoc\nclass __$SubscriptionInfoCopyWithImpl<$Res>\n    implements _$SubscriptionInfoCopyWith<$Res> {\n  __$SubscriptionInfoCopyWithImpl(this._self, this._then);\n\n  final _SubscriptionInfo _self;\n  final $Res Function(_SubscriptionInfo) _then;\n\n/// Create a copy of SubscriptionInfo\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? upload = null,Object? download = null,Object? total = null,Object? expire = null,}) {\n  return _then(_SubscriptionInfo(\nupload: null == upload ? _self.upload : upload // ignore: cast_nullable_to_non_nullable\nas int,download: null == download ? _self.download : download // ignore: cast_nullable_to_non_nullable\nas int,total: null == total ? _self.total : total // ignore: cast_nullable_to_non_nullable\nas int,expire: null == expire ? _self.expire : expire // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n\n\n}\n\n\n/// @nodoc\nmixin _$Profile {\n\n String get id; String? get label; String? get currentGroupName; String get url; DateTime? get lastUpdateDate; Duration get autoUpdateDuration; SubscriptionInfo? get subscriptionInfo; bool get autoUpdate; SelectedMap get selectedMap; Set<String> get unfoldSet; OverrideData get overrideData;@JsonKey(includeToJson: false, includeFromJson: false) bool get isUpdating;\n/// Create a copy of Profile\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ProfileCopyWith<Profile> get copyWith => _$ProfileCopyWithImpl<Profile>(this as Profile, _$identity);\n\n  /// Serializes this Profile to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is Profile&&(identical(other.id, id) || other.id == id)&&(identical(other.label, label) || other.label == label)&&(identical(other.currentGroupName, currentGroupName) || other.currentGroupName == currentGroupName)&&(identical(other.url, url) || other.url == url)&&(identical(other.lastUpdateDate, lastUpdateDate) || other.lastUpdateDate == lastUpdateDate)&&(identical(other.autoUpdateDuration, autoUpdateDuration) || other.autoUpdateDuration == autoUpdateDuration)&&(identical(other.subscriptionInfo, subscriptionInfo) || other.subscriptionInfo == subscriptionInfo)&&(identical(other.autoUpdate, autoUpdate) || other.autoUpdate == autoUpdate)&&const DeepCollectionEquality().equals(other.selectedMap, selectedMap)&&const DeepCollectionEquality().equals(other.unfoldSet, unfoldSet)&&(identical(other.overrideData, overrideData) || other.overrideData == overrideData)&&(identical(other.isUpdating, isUpdating) || other.isUpdating == isUpdating));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,id,label,currentGroupName,url,lastUpdateDate,autoUpdateDuration,subscriptionInfo,autoUpdate,const DeepCollectionEquality().hash(selectedMap),const DeepCollectionEquality().hash(unfoldSet),overrideData,isUpdating);\n\n@override\nString toString() {\n  return 'Profile(id: $id, label: $label, currentGroupName: $currentGroupName, url: $url, lastUpdateDate: $lastUpdateDate, autoUpdateDuration: $autoUpdateDuration, subscriptionInfo: $subscriptionInfo, autoUpdate: $autoUpdate, selectedMap: $selectedMap, unfoldSet: $unfoldSet, overrideData: $overrideData, isUpdating: $isUpdating)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ProfileCopyWith<$Res>  {\n  factory $ProfileCopyWith(Profile value, $Res Function(Profile) _then) = _$ProfileCopyWithImpl;\n@useResult\n$Res call({\n String id, String? label, String? currentGroupName, String url, DateTime? lastUpdateDate, Duration autoUpdateDuration, SubscriptionInfo? subscriptionInfo, bool autoUpdate, SelectedMap selectedMap, Set<String> unfoldSet, OverrideData overrideData,@JsonKey(includeToJson: false, includeFromJson: false) bool isUpdating\n});\n\n\n$SubscriptionInfoCopyWith<$Res>? get subscriptionInfo;$OverrideDataCopyWith<$Res> get overrideData;\n\n}\n/// @nodoc\nclass _$ProfileCopyWithImpl<$Res>\n    implements $ProfileCopyWith<$Res> {\n  _$ProfileCopyWithImpl(this._self, this._then);\n\n  final Profile _self;\n  final $Res Function(Profile) _then;\n\n/// Create a copy of Profile\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? label = freezed,Object? currentGroupName = freezed,Object? url = null,Object? lastUpdateDate = freezed,Object? autoUpdateDuration = null,Object? subscriptionInfo = freezed,Object? autoUpdate = null,Object? selectedMap = null,Object? unfoldSet = null,Object? overrideData = null,Object? isUpdating = null,}) {\n  return _then(_self.copyWith(\nid: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable\nas String,label: freezed == label ? _self.label : label // ignore: cast_nullable_to_non_nullable\nas String?,currentGroupName: freezed == currentGroupName ? _self.currentGroupName : currentGroupName // ignore: cast_nullable_to_non_nullable\nas String?,url: null == url ? _self.url : url // ignore: cast_nullable_to_non_nullable\nas String,lastUpdateDate: freezed == lastUpdateDate ? _self.lastUpdateDate : lastUpdateDate // ignore: cast_nullable_to_non_nullable\nas DateTime?,autoUpdateDuration: null == autoUpdateDuration ? _self.autoUpdateDuration : autoUpdateDuration // ignore: cast_nullable_to_non_nullable\nas Duration,subscriptionInfo: freezed == subscriptionInfo ? _self.subscriptionInfo : subscriptionInfo // ignore: cast_nullable_to_non_nullable\nas SubscriptionInfo?,autoUpdate: null == autoUpdate ? _self.autoUpdate : autoUpdate // ignore: cast_nullable_to_non_nullable\nas bool,selectedMap: null == selectedMap ? _self.selectedMap : selectedMap // ignore: cast_nullable_to_non_nullable\nas SelectedMap,unfoldSet: null == unfoldSet ? _self.unfoldSet : unfoldSet // ignore: cast_nullable_to_non_nullable\nas Set<String>,overrideData: null == overrideData ? _self.overrideData : overrideData // ignore: cast_nullable_to_non_nullable\nas OverrideData,isUpdating: null == isUpdating ? _self.isUpdating : isUpdating // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n/// Create a copy of Profile\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$SubscriptionInfoCopyWith<$Res>? get subscriptionInfo {\n    if (_self.subscriptionInfo == null) {\n    return null;\n  }\n\n  return $SubscriptionInfoCopyWith<$Res>(_self.subscriptionInfo!, (value) {\n    return _then(_self.copyWith(subscriptionInfo: value));\n  });\n}/// Create a copy of Profile\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$OverrideDataCopyWith<$Res> get overrideData {\n  \n  return $OverrideDataCopyWith<$Res>(_self.overrideData, (value) {\n    return _then(_self.copyWith(overrideData: value));\n  });\n}\n}\n\n\n/// Adds pattern-matching-related methods to [Profile].\nextension ProfilePatterns on Profile {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _Profile value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _Profile() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _Profile value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Profile():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _Profile value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _Profile() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id,  String? label,  String? currentGroupName,  String url,  DateTime? lastUpdateDate,  Duration autoUpdateDuration,  SubscriptionInfo? subscriptionInfo,  bool autoUpdate,  SelectedMap selectedMap,  Set<String> unfoldSet,  OverrideData overrideData, @JsonKey(includeToJson: false, includeFromJson: false)  bool isUpdating)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _Profile() when $default != null:\nreturn $default(_that.id,_that.label,_that.currentGroupName,_that.url,_that.lastUpdateDate,_that.autoUpdateDuration,_that.subscriptionInfo,_that.autoUpdate,_that.selectedMap,_that.unfoldSet,_that.overrideData,_that.isUpdating);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id,  String? label,  String? currentGroupName,  String url,  DateTime? lastUpdateDate,  Duration autoUpdateDuration,  SubscriptionInfo? subscriptionInfo,  bool autoUpdate,  SelectedMap selectedMap,  Set<String> unfoldSet,  OverrideData overrideData, @JsonKey(includeToJson: false, includeFromJson: false)  bool isUpdating)  $default,) {final _that = this;\nswitch (_that) {\ncase _Profile():\nreturn $default(_that.id,_that.label,_that.currentGroupName,_that.url,_that.lastUpdateDate,_that.autoUpdateDuration,_that.subscriptionInfo,_that.autoUpdate,_that.selectedMap,_that.unfoldSet,_that.overrideData,_that.isUpdating);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id,  String? label,  String? currentGroupName,  String url,  DateTime? lastUpdateDate,  Duration autoUpdateDuration,  SubscriptionInfo? subscriptionInfo,  bool autoUpdate,  SelectedMap selectedMap,  Set<String> unfoldSet,  OverrideData overrideData, @JsonKey(includeToJson: false, includeFromJson: false)  bool isUpdating)?  $default,) {final _that = this;\nswitch (_that) {\ncase _Profile() when $default != null:\nreturn $default(_that.id,_that.label,_that.currentGroupName,_that.url,_that.lastUpdateDate,_that.autoUpdateDuration,_that.subscriptionInfo,_that.autoUpdate,_that.selectedMap,_that.unfoldSet,_that.overrideData,_that.isUpdating);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _Profile implements Profile {\n  const _Profile({required this.id, this.label, this.currentGroupName, this.url = '', this.lastUpdateDate, required this.autoUpdateDuration, this.subscriptionInfo, this.autoUpdate = true, final  SelectedMap selectedMap = const {}, final  Set<String> unfoldSet = const {}, this.overrideData = const OverrideData(), @JsonKey(includeToJson: false, includeFromJson: false) this.isUpdating = false}): _selectedMap = selectedMap,_unfoldSet = unfoldSet;\n  factory _Profile.fromJson(Map<String, dynamic> json) => _$ProfileFromJson(json);\n\n@override final  String id;\n@override final  String? label;\n@override final  String? currentGroupName;\n@override@JsonKey() final  String url;\n@override final  DateTime? lastUpdateDate;\n@override final  Duration autoUpdateDuration;\n@override final  SubscriptionInfo? subscriptionInfo;\n@override@JsonKey() final  bool autoUpdate;\n final  SelectedMap _selectedMap;\n@override@JsonKey() SelectedMap get selectedMap {\n  if (_selectedMap is EqualUnmodifiableMapView) return _selectedMap;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableMapView(_selectedMap);\n}\n\n final  Set<String> _unfoldSet;\n@override@JsonKey() Set<String> get unfoldSet {\n  if (_unfoldSet is EqualUnmodifiableSetView) return _unfoldSet;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableSetView(_unfoldSet);\n}\n\n@override@JsonKey() final  OverrideData overrideData;\n@override@JsonKey(includeToJson: false, includeFromJson: false) final  bool isUpdating;\n\n/// Create a copy of Profile\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ProfileCopyWith<_Profile> get copyWith => __$ProfileCopyWithImpl<_Profile>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$ProfileToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _Profile&&(identical(other.id, id) || other.id == id)&&(identical(other.label, label) || other.label == label)&&(identical(other.currentGroupName, currentGroupName) || other.currentGroupName == currentGroupName)&&(identical(other.url, url) || other.url == url)&&(identical(other.lastUpdateDate, lastUpdateDate) || other.lastUpdateDate == lastUpdateDate)&&(identical(other.autoUpdateDuration, autoUpdateDuration) || other.autoUpdateDuration == autoUpdateDuration)&&(identical(other.subscriptionInfo, subscriptionInfo) || other.subscriptionInfo == subscriptionInfo)&&(identical(other.autoUpdate, autoUpdate) || other.autoUpdate == autoUpdate)&&const DeepCollectionEquality().equals(other._selectedMap, _selectedMap)&&const DeepCollectionEquality().equals(other._unfoldSet, _unfoldSet)&&(identical(other.overrideData, overrideData) || other.overrideData == overrideData)&&(identical(other.isUpdating, isUpdating) || other.isUpdating == isUpdating));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,id,label,currentGroupName,url,lastUpdateDate,autoUpdateDuration,subscriptionInfo,autoUpdate,const DeepCollectionEquality().hash(_selectedMap),const DeepCollectionEquality().hash(_unfoldSet),overrideData,isUpdating);\n\n@override\nString toString() {\n  return 'Profile(id: $id, label: $label, currentGroupName: $currentGroupName, url: $url, lastUpdateDate: $lastUpdateDate, autoUpdateDuration: $autoUpdateDuration, subscriptionInfo: $subscriptionInfo, autoUpdate: $autoUpdate, selectedMap: $selectedMap, unfoldSet: $unfoldSet, overrideData: $overrideData, isUpdating: $isUpdating)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ProfileCopyWith<$Res> implements $ProfileCopyWith<$Res> {\n  factory _$ProfileCopyWith(_Profile value, $Res Function(_Profile) _then) = __$ProfileCopyWithImpl;\n@override @useResult\n$Res call({\n String id, String? label, String? currentGroupName, String url, DateTime? lastUpdateDate, Duration autoUpdateDuration, SubscriptionInfo? subscriptionInfo, bool autoUpdate, SelectedMap selectedMap, Set<String> unfoldSet, OverrideData overrideData,@JsonKey(includeToJson: false, includeFromJson: false) bool isUpdating\n});\n\n\n@override $SubscriptionInfoCopyWith<$Res>? get subscriptionInfo;@override $OverrideDataCopyWith<$Res> get overrideData;\n\n}\n/// @nodoc\nclass __$ProfileCopyWithImpl<$Res>\n    implements _$ProfileCopyWith<$Res> {\n  __$ProfileCopyWithImpl(this._self, this._then);\n\n  final _Profile _self;\n  final $Res Function(_Profile) _then;\n\n/// Create a copy of Profile\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? label = freezed,Object? currentGroupName = freezed,Object? url = null,Object? lastUpdateDate = freezed,Object? autoUpdateDuration = null,Object? subscriptionInfo = freezed,Object? autoUpdate = null,Object? selectedMap = null,Object? unfoldSet = null,Object? overrideData = null,Object? isUpdating = null,}) {\n  return _then(_Profile(\nid: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable\nas String,label: freezed == label ? _self.label : label // ignore: cast_nullable_to_non_nullable\nas String?,currentGroupName: freezed == currentGroupName ? _self.currentGroupName : currentGroupName // ignore: cast_nullable_to_non_nullable\nas String?,url: null == url ? _self.url : url // ignore: cast_nullable_to_non_nullable\nas String,lastUpdateDate: freezed == lastUpdateDate ? _self.lastUpdateDate : lastUpdateDate // ignore: cast_nullable_to_non_nullable\nas DateTime?,autoUpdateDuration: null == autoUpdateDuration ? _self.autoUpdateDuration : autoUpdateDuration // ignore: cast_nullable_to_non_nullable\nas Duration,subscriptionInfo: freezed == subscriptionInfo ? _self.subscriptionInfo : subscriptionInfo // ignore: cast_nullable_to_non_nullable\nas SubscriptionInfo?,autoUpdate: null == autoUpdate ? _self.autoUpdate : autoUpdate // ignore: cast_nullable_to_non_nullable\nas bool,selectedMap: null == selectedMap ? _self._selectedMap : selectedMap // ignore: cast_nullable_to_non_nullable\nas SelectedMap,unfoldSet: null == unfoldSet ? _self._unfoldSet : unfoldSet // ignore: cast_nullable_to_non_nullable\nas Set<String>,overrideData: null == overrideData ? _self.overrideData : overrideData // ignore: cast_nullable_to_non_nullable\nas OverrideData,isUpdating: null == isUpdating ? _self.isUpdating : isUpdating // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n/// Create a copy of Profile\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$SubscriptionInfoCopyWith<$Res>? get subscriptionInfo {\n    if (_self.subscriptionInfo == null) {\n    return null;\n  }\n\n  return $SubscriptionInfoCopyWith<$Res>(_self.subscriptionInfo!, (value) {\n    return _then(_self.copyWith(subscriptionInfo: value));\n  });\n}/// Create a copy of Profile\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$OverrideDataCopyWith<$Res> get overrideData {\n  \n  return $OverrideDataCopyWith<$Res>(_self.overrideData, (value) {\n    return _then(_self.copyWith(overrideData: value));\n  });\n}\n}\n\n\n/// @nodoc\nmixin _$OverrideData {\n\n bool get enable; OverrideRule get rule;\n/// Create a copy of OverrideData\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$OverrideDataCopyWith<OverrideData> get copyWith => _$OverrideDataCopyWithImpl<OverrideData>(this as OverrideData, _$identity);\n\n  /// Serializes this OverrideData to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is OverrideData&&(identical(other.enable, enable) || other.enable == enable)&&(identical(other.rule, rule) || other.rule == rule));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,enable,rule);\n\n@override\nString toString() {\n  return 'OverrideData(enable: $enable, rule: $rule)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $OverrideDataCopyWith<$Res>  {\n  factory $OverrideDataCopyWith(OverrideData value, $Res Function(OverrideData) _then) = _$OverrideDataCopyWithImpl;\n@useResult\n$Res call({\n bool enable, OverrideRule rule\n});\n\n\n$OverrideRuleCopyWith<$Res> get rule;\n\n}\n/// @nodoc\nclass _$OverrideDataCopyWithImpl<$Res>\n    implements $OverrideDataCopyWith<$Res> {\n  _$OverrideDataCopyWithImpl(this._self, this._then);\n\n  final OverrideData _self;\n  final $Res Function(OverrideData) _then;\n\n/// Create a copy of OverrideData\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? enable = null,Object? rule = null,}) {\n  return _then(_self.copyWith(\nenable: null == enable ? _self.enable : enable // ignore: cast_nullable_to_non_nullable\nas bool,rule: null == rule ? _self.rule : rule // ignore: cast_nullable_to_non_nullable\nas OverrideRule,\n  ));\n}\n/// Create a copy of OverrideData\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$OverrideRuleCopyWith<$Res> get rule {\n  \n  return $OverrideRuleCopyWith<$Res>(_self.rule, (value) {\n    return _then(_self.copyWith(rule: value));\n  });\n}\n}\n\n\n/// Adds pattern-matching-related methods to [OverrideData].\nextension OverrideDataPatterns on OverrideData {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _OverrideData value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _OverrideData() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _OverrideData value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _OverrideData():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _OverrideData value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _OverrideData() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool enable,  OverrideRule rule)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _OverrideData() when $default != null:\nreturn $default(_that.enable,_that.rule);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool enable,  OverrideRule rule)  $default,) {final _that = this;\nswitch (_that) {\ncase _OverrideData():\nreturn $default(_that.enable,_that.rule);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool enable,  OverrideRule rule)?  $default,) {final _that = this;\nswitch (_that) {\ncase _OverrideData() when $default != null:\nreturn $default(_that.enable,_that.rule);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _OverrideData implements OverrideData {\n  const _OverrideData({this.enable = false, this.rule = const OverrideRule()});\n  factory _OverrideData.fromJson(Map<String, dynamic> json) => _$OverrideDataFromJson(json);\n\n@override@JsonKey() final  bool enable;\n@override@JsonKey() final  OverrideRule rule;\n\n/// Create a copy of OverrideData\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$OverrideDataCopyWith<_OverrideData> get copyWith => __$OverrideDataCopyWithImpl<_OverrideData>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$OverrideDataToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _OverrideData&&(identical(other.enable, enable) || other.enable == enable)&&(identical(other.rule, rule) || other.rule == rule));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,enable,rule);\n\n@override\nString toString() {\n  return 'OverrideData(enable: $enable, rule: $rule)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$OverrideDataCopyWith<$Res> implements $OverrideDataCopyWith<$Res> {\n  factory _$OverrideDataCopyWith(_OverrideData value, $Res Function(_OverrideData) _then) = __$OverrideDataCopyWithImpl;\n@override @useResult\n$Res call({\n bool enable, OverrideRule rule\n});\n\n\n@override $OverrideRuleCopyWith<$Res> get rule;\n\n}\n/// @nodoc\nclass __$OverrideDataCopyWithImpl<$Res>\n    implements _$OverrideDataCopyWith<$Res> {\n  __$OverrideDataCopyWithImpl(this._self, this._then);\n\n  final _OverrideData _self;\n  final $Res Function(_OverrideData) _then;\n\n/// Create a copy of OverrideData\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? enable = null,Object? rule = null,}) {\n  return _then(_OverrideData(\nenable: null == enable ? _self.enable : enable // ignore: cast_nullable_to_non_nullable\nas bool,rule: null == rule ? _self.rule : rule // ignore: cast_nullable_to_non_nullable\nas OverrideRule,\n  ));\n}\n\n/// Create a copy of OverrideData\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$OverrideRuleCopyWith<$Res> get rule {\n  \n  return $OverrideRuleCopyWith<$Res>(_self.rule, (value) {\n    return _then(_self.copyWith(rule: value));\n  });\n}\n}\n\n\n/// @nodoc\nmixin _$OverrideRule {\n\n OverrideRuleType get type; List<Rule> get overrideRules; List<Rule> get addedRules;\n/// Create a copy of OverrideRule\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$OverrideRuleCopyWith<OverrideRule> get copyWith => _$OverrideRuleCopyWithImpl<OverrideRule>(this as OverrideRule, _$identity);\n\n  /// Serializes this OverrideRule to a JSON map.\n  Map<String, dynamic> toJson();\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is OverrideRule&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other.overrideRules, overrideRules)&&const DeepCollectionEquality().equals(other.addedRules, addedRules));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,type,const DeepCollectionEquality().hash(overrideRules),const DeepCollectionEquality().hash(addedRules));\n\n@override\nString toString() {\n  return 'OverrideRule(type: $type, overrideRules: $overrideRules, addedRules: $addedRules)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $OverrideRuleCopyWith<$Res>  {\n  factory $OverrideRuleCopyWith(OverrideRule value, $Res Function(OverrideRule) _then) = _$OverrideRuleCopyWithImpl;\n@useResult\n$Res call({\n OverrideRuleType type, List<Rule> overrideRules, List<Rule> addedRules\n});\n\n\n\n\n}\n/// @nodoc\nclass _$OverrideRuleCopyWithImpl<$Res>\n    implements $OverrideRuleCopyWith<$Res> {\n  _$OverrideRuleCopyWithImpl(this._self, this._then);\n\n  final OverrideRule _self;\n  final $Res Function(OverrideRule) _then;\n\n/// Create a copy of OverrideRule\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? type = null,Object? overrideRules = null,Object? addedRules = null,}) {\n  return _then(_self.copyWith(\ntype: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable\nas OverrideRuleType,overrideRules: null == overrideRules ? _self.overrideRules : overrideRules // ignore: cast_nullable_to_non_nullable\nas List<Rule>,addedRules: null == addedRules ? _self.addedRules : addedRules // ignore: cast_nullable_to_non_nullable\nas List<Rule>,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [OverrideRule].\nextension OverrideRulePatterns on OverrideRule {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _OverrideRule value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _OverrideRule() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _OverrideRule value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _OverrideRule():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _OverrideRule value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _OverrideRule() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( OverrideRuleType type,  List<Rule> overrideRules,  List<Rule> addedRules)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _OverrideRule() when $default != null:\nreturn $default(_that.type,_that.overrideRules,_that.addedRules);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( OverrideRuleType type,  List<Rule> overrideRules,  List<Rule> addedRules)  $default,) {final _that = this;\nswitch (_that) {\ncase _OverrideRule():\nreturn $default(_that.type,_that.overrideRules,_that.addedRules);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( OverrideRuleType type,  List<Rule> overrideRules,  List<Rule> addedRules)?  $default,) {final _that = this;\nswitch (_that) {\ncase _OverrideRule() when $default != null:\nreturn $default(_that.type,_that.overrideRules,_that.addedRules);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n@JsonSerializable()\n\nclass _OverrideRule implements OverrideRule {\n  const _OverrideRule({this.type = OverrideRuleType.override, final  List<Rule> overrideRules = const [], final  List<Rule> addedRules = const []}): _overrideRules = overrideRules,_addedRules = addedRules;\n  factory _OverrideRule.fromJson(Map<String, dynamic> json) => _$OverrideRuleFromJson(json);\n\n@override@JsonKey() final  OverrideRuleType type;\n final  List<Rule> _overrideRules;\n@override@JsonKey() List<Rule> get overrideRules {\n  if (_overrideRules is EqualUnmodifiableListView) return _overrideRules;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_overrideRules);\n}\n\n final  List<Rule> _addedRules;\n@override@JsonKey() List<Rule> get addedRules {\n  if (_addedRules is EqualUnmodifiableListView) return _addedRules;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_addedRules);\n}\n\n\n/// Create a copy of OverrideRule\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$OverrideRuleCopyWith<_OverrideRule> get copyWith => __$OverrideRuleCopyWithImpl<_OverrideRule>(this, _$identity);\n\n@override\nMap<String, dynamic> toJson() {\n  return _$OverrideRuleToJson(this, );\n}\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _OverrideRule&&(identical(other.type, type) || other.type == type)&&const DeepCollectionEquality().equals(other._overrideRules, _overrideRules)&&const DeepCollectionEquality().equals(other._addedRules, _addedRules));\n}\n\n@JsonKey(includeFromJson: false, includeToJson: false)\n@override\nint get hashCode => Object.hash(runtimeType,type,const DeepCollectionEquality().hash(_overrideRules),const DeepCollectionEquality().hash(_addedRules));\n\n@override\nString toString() {\n  return 'OverrideRule(type: $type, overrideRules: $overrideRules, addedRules: $addedRules)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$OverrideRuleCopyWith<$Res> implements $OverrideRuleCopyWith<$Res> {\n  factory _$OverrideRuleCopyWith(_OverrideRule value, $Res Function(_OverrideRule) _then) = __$OverrideRuleCopyWithImpl;\n@override @useResult\n$Res call({\n OverrideRuleType type, List<Rule> overrideRules, List<Rule> addedRules\n});\n\n\n\n\n}\n/// @nodoc\nclass __$OverrideRuleCopyWithImpl<$Res>\n    implements _$OverrideRuleCopyWith<$Res> {\n  __$OverrideRuleCopyWithImpl(this._self, this._then);\n\n  final _OverrideRule _self;\n  final $Res Function(_OverrideRule) _then;\n\n/// Create a copy of OverrideRule\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? type = null,Object? overrideRules = null,Object? addedRules = null,}) {\n  return _then(_OverrideRule(\ntype: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable\nas OverrideRuleType,overrideRules: null == overrideRules ? _self._overrideRules : overrideRules // ignore: cast_nullable_to_non_nullable\nas List<Rule>,addedRules: null == addedRules ? _self._addedRules : addedRules // ignore: cast_nullable_to_non_nullable\nas List<Rule>,\n  ));\n}\n\n\n}\n\n// dart format on\n"
  },
  {
    "path": "lib/models/generated/profile.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of '../profile.dart';\n\n// **************************************************************************\n// JsonSerializableGenerator\n// **************************************************************************\n\n_SubscriptionInfo _$SubscriptionInfoFromJson(Map<String, dynamic> json) =>\n    _SubscriptionInfo(\n      upload: (json['upload'] as num?)?.toInt() ?? 0,\n      download: (json['download'] as num?)?.toInt() ?? 0,\n      total: (json['total'] as num?)?.toInt() ?? 0,\n      expire: (json['expire'] as num?)?.toInt() ?? 0,\n    );\n\nMap<String, dynamic> _$SubscriptionInfoToJson(_SubscriptionInfo instance) =>\n    <String, dynamic>{\n      'upload': instance.upload,\n      'download': instance.download,\n      'total': instance.total,\n      'expire': instance.expire,\n    };\n\n_Profile _$ProfileFromJson(Map<String, dynamic> json) => _Profile(\n  id: json['id'] as String,\n  label: json['label'] as String?,\n  currentGroupName: json['currentGroupName'] as String?,\n  url: json['url'] as String? ?? '',\n  lastUpdateDate: json['lastUpdateDate'] == null\n      ? null\n      : DateTime.parse(json['lastUpdateDate'] as String),\n  autoUpdateDuration: Duration(\n    microseconds: (json['autoUpdateDuration'] as num).toInt(),\n  ),\n  subscriptionInfo: json['subscriptionInfo'] == null\n      ? null\n      : SubscriptionInfo.fromJson(\n          json['subscriptionInfo'] as Map<String, dynamic>,\n        ),\n  autoUpdate: json['autoUpdate'] as bool? ?? true,\n  selectedMap:\n      (json['selectedMap'] as Map<String, dynamic>?)?.map(\n        (k, e) => MapEntry(k, e as String),\n      ) ??\n      const {},\n  unfoldSet:\n      (json['unfoldSet'] as List<dynamic>?)?.map((e) => e as String).toSet() ??\n      const {},\n  overrideData: json['overrideData'] == null\n      ? const OverrideData()\n      : OverrideData.fromJson(json['overrideData'] as Map<String, dynamic>),\n);\n\nMap<String, dynamic> _$ProfileToJson(_Profile instance) => <String, dynamic>{\n  'id': instance.id,\n  'label': instance.label,\n  'currentGroupName': instance.currentGroupName,\n  'url': instance.url,\n  'lastUpdateDate': instance.lastUpdateDate?.toIso8601String(),\n  'autoUpdateDuration': instance.autoUpdateDuration.inMicroseconds,\n  'subscriptionInfo': instance.subscriptionInfo,\n  'autoUpdate': instance.autoUpdate,\n  'selectedMap': instance.selectedMap,\n  'unfoldSet': instance.unfoldSet.toList(),\n  'overrideData': instance.overrideData,\n};\n\n_OverrideData _$OverrideDataFromJson(Map<String, dynamic> json) =>\n    _OverrideData(\n      enable: json['enable'] as bool? ?? false,\n      rule: json['rule'] == null\n          ? const OverrideRule()\n          : OverrideRule.fromJson(json['rule'] as Map<String, dynamic>),\n    );\n\nMap<String, dynamic> _$OverrideDataToJson(_OverrideData instance) =>\n    <String, dynamic>{'enable': instance.enable, 'rule': instance.rule};\n\n_OverrideRule _$OverrideRuleFromJson(Map<String, dynamic> json) =>\n    _OverrideRule(\n      type:\n          $enumDecodeNullable(_$OverrideRuleTypeEnumMap, json['type']) ??\n          OverrideRuleType.override,\n      overrideRules:\n          (json['overrideRules'] as List<dynamic>?)\n              ?.map((e) => Rule.fromJson(e as Map<String, dynamic>))\n              .toList() ??\n          const [],\n      addedRules:\n          (json['addedRules'] as List<dynamic>?)\n              ?.map((e) => Rule.fromJson(e as Map<String, dynamic>))\n              .toList() ??\n          const [],\n    );\n\nMap<String, dynamic> _$OverrideRuleToJson(_OverrideRule instance) =>\n    <String, dynamic>{\n      'type': _$OverrideRuleTypeEnumMap[instance.type]!,\n      'overrideRules': instance.overrideRules,\n      'addedRules': instance.addedRules,\n    };\n\nconst _$OverrideRuleTypeEnumMap = {\n  OverrideRuleType.override: 'override',\n  OverrideRuleType.added: 'added',\n};\n"
  },
  {
    "path": "lib/models/generated/selector.freezed.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n// coverage:ignore-file\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of '../selector.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\n// dart format off\nT _$identity<T>(T value) => value;\n/// @nodoc\nmixin _$VM2<A,B> {\n\n A get a; B get b;\n/// Create a copy of VM2\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$VM2CopyWith<A, B, VM2<A, B>> get copyWith => _$VM2CopyWithImpl<A, B, VM2<A, B>>(this as VM2<A, B>, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is VM2<A, B>&&const DeepCollectionEquality().equals(other.a, a)&&const DeepCollectionEquality().equals(other.b, b));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(a),const DeepCollectionEquality().hash(b));\n\n@override\nString toString() {\n  return 'VM2<$A, $B>(a: $a, b: $b)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $VM2CopyWith<A,B,$Res>  {\n  factory $VM2CopyWith(VM2<A, B> value, $Res Function(VM2<A, B>) _then) = _$VM2CopyWithImpl;\n@useResult\n$Res call({\n A a, B b\n});\n\n\n\n\n}\n/// @nodoc\nclass _$VM2CopyWithImpl<A,B,$Res>\n    implements $VM2CopyWith<A, B, $Res> {\n  _$VM2CopyWithImpl(this._self, this._then);\n\n  final VM2<A, B> _self;\n  final $Res Function(VM2<A, B>) _then;\n\n/// Create a copy of VM2\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? a = freezed,Object? b = freezed,}) {\n  return _then(_self.copyWith(\na: freezed == a ? _self.a : a // ignore: cast_nullable_to_non_nullable\nas A,b: freezed == b ? _self.b : b // ignore: cast_nullable_to_non_nullable\nas B,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [VM2].\nextension VM2Patterns<A,B> on VM2<A, B> {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _VM2<A, B> value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _VM2() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _VM2<A, B> value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _VM2():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _VM2<A, B> value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _VM2() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( A a,  B b)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _VM2() when $default != null:\nreturn $default(_that.a,_that.b);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( A a,  B b)  $default,) {final _that = this;\nswitch (_that) {\ncase _VM2():\nreturn $default(_that.a,_that.b);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( A a,  B b)?  $default,) {final _that = this;\nswitch (_that) {\ncase _VM2() when $default != null:\nreturn $default(_that.a,_that.b);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _VM2<A,B> implements VM2<A, B> {\n  const _VM2({required this.a, required this.b});\n  \n\n@override final  A a;\n@override final  B b;\n\n/// Create a copy of VM2\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$VM2CopyWith<A, B, _VM2<A, B>> get copyWith => __$VM2CopyWithImpl<A, B, _VM2<A, B>>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _VM2<A, B>&&const DeepCollectionEquality().equals(other.a, a)&&const DeepCollectionEquality().equals(other.b, b));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(a),const DeepCollectionEquality().hash(b));\n\n@override\nString toString() {\n  return 'VM2<$A, $B>(a: $a, b: $b)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$VM2CopyWith<A,B,$Res> implements $VM2CopyWith<A, B, $Res> {\n  factory _$VM2CopyWith(_VM2<A, B> value, $Res Function(_VM2<A, B>) _then) = __$VM2CopyWithImpl;\n@override @useResult\n$Res call({\n A a, B b\n});\n\n\n\n\n}\n/// @nodoc\nclass __$VM2CopyWithImpl<A,B,$Res>\n    implements _$VM2CopyWith<A, B, $Res> {\n  __$VM2CopyWithImpl(this._self, this._then);\n\n  final _VM2<A, B> _self;\n  final $Res Function(_VM2<A, B>) _then;\n\n/// Create a copy of VM2\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? a = freezed,Object? b = freezed,}) {\n  return _then(_VM2<A, B>(\na: freezed == a ? _self.a : a // ignore: cast_nullable_to_non_nullable\nas A,b: freezed == b ? _self.b : b // ignore: cast_nullable_to_non_nullable\nas B,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$VM3<A,B,C> {\n\n A get a; B get b; C get c;\n/// Create a copy of VM3\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$VM3CopyWith<A, B, C, VM3<A, B, C>> get copyWith => _$VM3CopyWithImpl<A, B, C, VM3<A, B, C>>(this as VM3<A, B, C>, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is VM3<A, B, C>&&const DeepCollectionEquality().equals(other.a, a)&&const DeepCollectionEquality().equals(other.b, b)&&const DeepCollectionEquality().equals(other.c, c));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(a),const DeepCollectionEquality().hash(b),const DeepCollectionEquality().hash(c));\n\n@override\nString toString() {\n  return 'VM3<$A, $B, $C>(a: $a, b: $b, c: $c)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $VM3CopyWith<A,B,C,$Res>  {\n  factory $VM3CopyWith(VM3<A, B, C> value, $Res Function(VM3<A, B, C>) _then) = _$VM3CopyWithImpl;\n@useResult\n$Res call({\n A a, B b, C c\n});\n\n\n\n\n}\n/// @nodoc\nclass _$VM3CopyWithImpl<A,B,C,$Res>\n    implements $VM3CopyWith<A, B, C, $Res> {\n  _$VM3CopyWithImpl(this._self, this._then);\n\n  final VM3<A, B, C> _self;\n  final $Res Function(VM3<A, B, C>) _then;\n\n/// Create a copy of VM3\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? a = freezed,Object? b = freezed,Object? c = freezed,}) {\n  return _then(_self.copyWith(\na: freezed == a ? _self.a : a // ignore: cast_nullable_to_non_nullable\nas A,b: freezed == b ? _self.b : b // ignore: cast_nullable_to_non_nullable\nas B,c: freezed == c ? _self.c : c // ignore: cast_nullable_to_non_nullable\nas C,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [VM3].\nextension VM3Patterns<A,B,C> on VM3<A, B, C> {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _VM3<A, B, C> value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _VM3() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _VM3<A, B, C> value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _VM3():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _VM3<A, B, C> value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _VM3() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( A a,  B b,  C c)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _VM3() when $default != null:\nreturn $default(_that.a,_that.b,_that.c);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( A a,  B b,  C c)  $default,) {final _that = this;\nswitch (_that) {\ncase _VM3():\nreturn $default(_that.a,_that.b,_that.c);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( A a,  B b,  C c)?  $default,) {final _that = this;\nswitch (_that) {\ncase _VM3() when $default != null:\nreturn $default(_that.a,_that.b,_that.c);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _VM3<A,B,C> implements VM3<A, B, C> {\n  const _VM3({required this.a, required this.b, required this.c});\n  \n\n@override final  A a;\n@override final  B b;\n@override final  C c;\n\n/// Create a copy of VM3\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$VM3CopyWith<A, B, C, _VM3<A, B, C>> get copyWith => __$VM3CopyWithImpl<A, B, C, _VM3<A, B, C>>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _VM3<A, B, C>&&const DeepCollectionEquality().equals(other.a, a)&&const DeepCollectionEquality().equals(other.b, b)&&const DeepCollectionEquality().equals(other.c, c));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(a),const DeepCollectionEquality().hash(b),const DeepCollectionEquality().hash(c));\n\n@override\nString toString() {\n  return 'VM3<$A, $B, $C>(a: $a, b: $b, c: $c)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$VM3CopyWith<A,B,C,$Res> implements $VM3CopyWith<A, B, C, $Res> {\n  factory _$VM3CopyWith(_VM3<A, B, C> value, $Res Function(_VM3<A, B, C>) _then) = __$VM3CopyWithImpl;\n@override @useResult\n$Res call({\n A a, B b, C c\n});\n\n\n\n\n}\n/// @nodoc\nclass __$VM3CopyWithImpl<A,B,C,$Res>\n    implements _$VM3CopyWith<A, B, C, $Res> {\n  __$VM3CopyWithImpl(this._self, this._then);\n\n  final _VM3<A, B, C> _self;\n  final $Res Function(_VM3<A, B, C>) _then;\n\n/// Create a copy of VM3\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? a = freezed,Object? b = freezed,Object? c = freezed,}) {\n  return _then(_VM3<A, B, C>(\na: freezed == a ? _self.a : a // ignore: cast_nullable_to_non_nullable\nas A,b: freezed == b ? _self.b : b // ignore: cast_nullable_to_non_nullable\nas B,c: freezed == c ? _self.c : c // ignore: cast_nullable_to_non_nullable\nas C,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$VM4<A,B,C,D> {\n\n A get a; B get b; C get c; D get d;\n/// Create a copy of VM4\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$VM4CopyWith<A, B, C, D, VM4<A, B, C, D>> get copyWith => _$VM4CopyWithImpl<A, B, C, D, VM4<A, B, C, D>>(this as VM4<A, B, C, D>, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is VM4<A, B, C, D>&&const DeepCollectionEquality().equals(other.a, a)&&const DeepCollectionEquality().equals(other.b, b)&&const DeepCollectionEquality().equals(other.c, c)&&const DeepCollectionEquality().equals(other.d, d));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(a),const DeepCollectionEquality().hash(b),const DeepCollectionEquality().hash(c),const DeepCollectionEquality().hash(d));\n\n@override\nString toString() {\n  return 'VM4<$A, $B, $C, $D>(a: $a, b: $b, c: $c, d: $d)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $VM4CopyWith<A,B,C,D,$Res>  {\n  factory $VM4CopyWith(VM4<A, B, C, D> value, $Res Function(VM4<A, B, C, D>) _then) = _$VM4CopyWithImpl;\n@useResult\n$Res call({\n A a, B b, C c, D d\n});\n\n\n\n\n}\n/// @nodoc\nclass _$VM4CopyWithImpl<A,B,C,D,$Res>\n    implements $VM4CopyWith<A, B, C, D, $Res> {\n  _$VM4CopyWithImpl(this._self, this._then);\n\n  final VM4<A, B, C, D> _self;\n  final $Res Function(VM4<A, B, C, D>) _then;\n\n/// Create a copy of VM4\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? a = freezed,Object? b = freezed,Object? c = freezed,Object? d = freezed,}) {\n  return _then(_self.copyWith(\na: freezed == a ? _self.a : a // ignore: cast_nullable_to_non_nullable\nas A,b: freezed == b ? _self.b : b // ignore: cast_nullable_to_non_nullable\nas B,c: freezed == c ? _self.c : c // ignore: cast_nullable_to_non_nullable\nas C,d: freezed == d ? _self.d : d // ignore: cast_nullable_to_non_nullable\nas D,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [VM4].\nextension VM4Patterns<A,B,C,D> on VM4<A, B, C, D> {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _VM4<A, B, C, D> value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _VM4() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _VM4<A, B, C, D> value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _VM4():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _VM4<A, B, C, D> value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _VM4() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( A a,  B b,  C c,  D d)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _VM4() when $default != null:\nreturn $default(_that.a,_that.b,_that.c,_that.d);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( A a,  B b,  C c,  D d)  $default,) {final _that = this;\nswitch (_that) {\ncase _VM4():\nreturn $default(_that.a,_that.b,_that.c,_that.d);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( A a,  B b,  C c,  D d)?  $default,) {final _that = this;\nswitch (_that) {\ncase _VM4() when $default != null:\nreturn $default(_that.a,_that.b,_that.c,_that.d);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _VM4<A,B,C,D> implements VM4<A, B, C, D> {\n  const _VM4({required this.a, required this.b, required this.c, required this.d});\n  \n\n@override final  A a;\n@override final  B b;\n@override final  C c;\n@override final  D d;\n\n/// Create a copy of VM4\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$VM4CopyWith<A, B, C, D, _VM4<A, B, C, D>> get copyWith => __$VM4CopyWithImpl<A, B, C, D, _VM4<A, B, C, D>>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _VM4<A, B, C, D>&&const DeepCollectionEquality().equals(other.a, a)&&const DeepCollectionEquality().equals(other.b, b)&&const DeepCollectionEquality().equals(other.c, c)&&const DeepCollectionEquality().equals(other.d, d));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(a),const DeepCollectionEquality().hash(b),const DeepCollectionEquality().hash(c),const DeepCollectionEquality().hash(d));\n\n@override\nString toString() {\n  return 'VM4<$A, $B, $C, $D>(a: $a, b: $b, c: $c, d: $d)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$VM4CopyWith<A,B,C,D,$Res> implements $VM4CopyWith<A, B, C, D, $Res> {\n  factory _$VM4CopyWith(_VM4<A, B, C, D> value, $Res Function(_VM4<A, B, C, D>) _then) = __$VM4CopyWithImpl;\n@override @useResult\n$Res call({\n A a, B b, C c, D d\n});\n\n\n\n\n}\n/// @nodoc\nclass __$VM4CopyWithImpl<A,B,C,D,$Res>\n    implements _$VM4CopyWith<A, B, C, D, $Res> {\n  __$VM4CopyWithImpl(this._self, this._then);\n\n  final _VM4<A, B, C, D> _self;\n  final $Res Function(_VM4<A, B, C, D>) _then;\n\n/// Create a copy of VM4\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? a = freezed,Object? b = freezed,Object? c = freezed,Object? d = freezed,}) {\n  return _then(_VM4<A, B, C, D>(\na: freezed == a ? _self.a : a // ignore: cast_nullable_to_non_nullable\nas A,b: freezed == b ? _self.b : b // ignore: cast_nullable_to_non_nullable\nas B,c: freezed == c ? _self.c : c // ignore: cast_nullable_to_non_nullable\nas C,d: freezed == d ? _self.d : d // ignore: cast_nullable_to_non_nullable\nas D,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$VM5<A,B,C,D,E> {\n\n A get a; B get b; C get c; D get d; E get e;\n/// Create a copy of VM5\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$VM5CopyWith<A, B, C, D, E, VM5<A, B, C, D, E>> get copyWith => _$VM5CopyWithImpl<A, B, C, D, E, VM5<A, B, C, D, E>>(this as VM5<A, B, C, D, E>, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is VM5<A, B, C, D, E>&&const DeepCollectionEquality().equals(other.a, a)&&const DeepCollectionEquality().equals(other.b, b)&&const DeepCollectionEquality().equals(other.c, c)&&const DeepCollectionEquality().equals(other.d, d)&&const DeepCollectionEquality().equals(other.e, e));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(a),const DeepCollectionEquality().hash(b),const DeepCollectionEquality().hash(c),const DeepCollectionEquality().hash(d),const DeepCollectionEquality().hash(e));\n\n@override\nString toString() {\n  return 'VM5<$A, $B, $C, $D, $E>(a: $a, b: $b, c: $c, d: $d, e: $e)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $VM5CopyWith<A,B,C,D,E,$Res>  {\n  factory $VM5CopyWith(VM5<A, B, C, D, E> value, $Res Function(VM5<A, B, C, D, E>) _then) = _$VM5CopyWithImpl;\n@useResult\n$Res call({\n A a, B b, C c, D d, E e\n});\n\n\n\n\n}\n/// @nodoc\nclass _$VM5CopyWithImpl<A,B,C,D,E,$Res>\n    implements $VM5CopyWith<A, B, C, D, E, $Res> {\n  _$VM5CopyWithImpl(this._self, this._then);\n\n  final VM5<A, B, C, D, E> _self;\n  final $Res Function(VM5<A, B, C, D, E>) _then;\n\n/// Create a copy of VM5\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? a = freezed,Object? b = freezed,Object? c = freezed,Object? d = freezed,Object? e = freezed,}) {\n  return _then(_self.copyWith(\na: freezed == a ? _self.a : a // ignore: cast_nullable_to_non_nullable\nas A,b: freezed == b ? _self.b : b // ignore: cast_nullable_to_non_nullable\nas B,c: freezed == c ? _self.c : c // ignore: cast_nullable_to_non_nullable\nas C,d: freezed == d ? _self.d : d // ignore: cast_nullable_to_non_nullable\nas D,e: freezed == e ? _self.e : e // ignore: cast_nullable_to_non_nullable\nas E,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [VM5].\nextension VM5Patterns<A,B,C,D,E> on VM5<A, B, C, D, E> {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _VM5<A, B, C, D, E> value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _VM5() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _VM5<A, B, C, D, E> value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _VM5():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _VM5<A, B, C, D, E> value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _VM5() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( A a,  B b,  C c,  D d,  E e)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _VM5() when $default != null:\nreturn $default(_that.a,_that.b,_that.c,_that.d,_that.e);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( A a,  B b,  C c,  D d,  E e)  $default,) {final _that = this;\nswitch (_that) {\ncase _VM5():\nreturn $default(_that.a,_that.b,_that.c,_that.d,_that.e);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( A a,  B b,  C c,  D d,  E e)?  $default,) {final _that = this;\nswitch (_that) {\ncase _VM5() when $default != null:\nreturn $default(_that.a,_that.b,_that.c,_that.d,_that.e);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _VM5<A,B,C,D,E> implements VM5<A, B, C, D, E> {\n  const _VM5({required this.a, required this.b, required this.c, required this.d, required this.e});\n  \n\n@override final  A a;\n@override final  B b;\n@override final  C c;\n@override final  D d;\n@override final  E e;\n\n/// Create a copy of VM5\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$VM5CopyWith<A, B, C, D, E, _VM5<A, B, C, D, E>> get copyWith => __$VM5CopyWithImpl<A, B, C, D, E, _VM5<A, B, C, D, E>>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _VM5<A, B, C, D, E>&&const DeepCollectionEquality().equals(other.a, a)&&const DeepCollectionEquality().equals(other.b, b)&&const DeepCollectionEquality().equals(other.c, c)&&const DeepCollectionEquality().equals(other.d, d)&&const DeepCollectionEquality().equals(other.e, e));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(a),const DeepCollectionEquality().hash(b),const DeepCollectionEquality().hash(c),const DeepCollectionEquality().hash(d),const DeepCollectionEquality().hash(e));\n\n@override\nString toString() {\n  return 'VM5<$A, $B, $C, $D, $E>(a: $a, b: $b, c: $c, d: $d, e: $e)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$VM5CopyWith<A,B,C,D,E,$Res> implements $VM5CopyWith<A, B, C, D, E, $Res> {\n  factory _$VM5CopyWith(_VM5<A, B, C, D, E> value, $Res Function(_VM5<A, B, C, D, E>) _then) = __$VM5CopyWithImpl;\n@override @useResult\n$Res call({\n A a, B b, C c, D d, E e\n});\n\n\n\n\n}\n/// @nodoc\nclass __$VM5CopyWithImpl<A,B,C,D,E,$Res>\n    implements _$VM5CopyWith<A, B, C, D, E, $Res> {\n  __$VM5CopyWithImpl(this._self, this._then);\n\n  final _VM5<A, B, C, D, E> _self;\n  final $Res Function(_VM5<A, B, C, D, E>) _then;\n\n/// Create a copy of VM5\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? a = freezed,Object? b = freezed,Object? c = freezed,Object? d = freezed,Object? e = freezed,}) {\n  return _then(_VM5<A, B, C, D, E>(\na: freezed == a ? _self.a : a // ignore: cast_nullable_to_non_nullable\nas A,b: freezed == b ? _self.b : b // ignore: cast_nullable_to_non_nullable\nas B,c: freezed == c ? _self.c : c // ignore: cast_nullable_to_non_nullable\nas C,d: freezed == d ? _self.d : d // ignore: cast_nullable_to_non_nullable\nas D,e: freezed == e ? _self.e : e // ignore: cast_nullable_to_non_nullable\nas E,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$StartButtonSelectorState {\n\n bool get isInit; bool get hasProfile;\n/// Create a copy of StartButtonSelectorState\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$StartButtonSelectorStateCopyWith<StartButtonSelectorState> get copyWith => _$StartButtonSelectorStateCopyWithImpl<StartButtonSelectorState>(this as StartButtonSelectorState, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is StartButtonSelectorState&&(identical(other.isInit, isInit) || other.isInit == isInit)&&(identical(other.hasProfile, hasProfile) || other.hasProfile == hasProfile));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,isInit,hasProfile);\n\n@override\nString toString() {\n  return 'StartButtonSelectorState(isInit: $isInit, hasProfile: $hasProfile)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $StartButtonSelectorStateCopyWith<$Res>  {\n  factory $StartButtonSelectorStateCopyWith(StartButtonSelectorState value, $Res Function(StartButtonSelectorState) _then) = _$StartButtonSelectorStateCopyWithImpl;\n@useResult\n$Res call({\n bool isInit, bool hasProfile\n});\n\n\n\n\n}\n/// @nodoc\nclass _$StartButtonSelectorStateCopyWithImpl<$Res>\n    implements $StartButtonSelectorStateCopyWith<$Res> {\n  _$StartButtonSelectorStateCopyWithImpl(this._self, this._then);\n\n  final StartButtonSelectorState _self;\n  final $Res Function(StartButtonSelectorState) _then;\n\n/// Create a copy of StartButtonSelectorState\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? isInit = null,Object? hasProfile = null,}) {\n  return _then(_self.copyWith(\nisInit: null == isInit ? _self.isInit : isInit // ignore: cast_nullable_to_non_nullable\nas bool,hasProfile: null == hasProfile ? _self.hasProfile : hasProfile // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [StartButtonSelectorState].\nextension StartButtonSelectorStatePatterns on StartButtonSelectorState {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _StartButtonSelectorState value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _StartButtonSelectorState() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _StartButtonSelectorState value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _StartButtonSelectorState():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _StartButtonSelectorState value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _StartButtonSelectorState() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isInit,  bool hasProfile)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _StartButtonSelectorState() when $default != null:\nreturn $default(_that.isInit,_that.hasProfile);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool isInit,  bool hasProfile)  $default,) {final _that = this;\nswitch (_that) {\ncase _StartButtonSelectorState():\nreturn $default(_that.isInit,_that.hasProfile);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool isInit,  bool hasProfile)?  $default,) {final _that = this;\nswitch (_that) {\ncase _StartButtonSelectorState() when $default != null:\nreturn $default(_that.isInit,_that.hasProfile);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _StartButtonSelectorState implements StartButtonSelectorState {\n  const _StartButtonSelectorState({required this.isInit, required this.hasProfile});\n  \n\n@override final  bool isInit;\n@override final  bool hasProfile;\n\n/// Create a copy of StartButtonSelectorState\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$StartButtonSelectorStateCopyWith<_StartButtonSelectorState> get copyWith => __$StartButtonSelectorStateCopyWithImpl<_StartButtonSelectorState>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _StartButtonSelectorState&&(identical(other.isInit, isInit) || other.isInit == isInit)&&(identical(other.hasProfile, hasProfile) || other.hasProfile == hasProfile));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,isInit,hasProfile);\n\n@override\nString toString() {\n  return 'StartButtonSelectorState(isInit: $isInit, hasProfile: $hasProfile)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$StartButtonSelectorStateCopyWith<$Res> implements $StartButtonSelectorStateCopyWith<$Res> {\n  factory _$StartButtonSelectorStateCopyWith(_StartButtonSelectorState value, $Res Function(_StartButtonSelectorState) _then) = __$StartButtonSelectorStateCopyWithImpl;\n@override @useResult\n$Res call({\n bool isInit, bool hasProfile\n});\n\n\n\n\n}\n/// @nodoc\nclass __$StartButtonSelectorStateCopyWithImpl<$Res>\n    implements _$StartButtonSelectorStateCopyWith<$Res> {\n  __$StartButtonSelectorStateCopyWithImpl(this._self, this._then);\n\n  final _StartButtonSelectorState _self;\n  final $Res Function(_StartButtonSelectorState) _then;\n\n/// Create a copy of StartButtonSelectorState\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? isInit = null,Object? hasProfile = null,}) {\n  return _then(_StartButtonSelectorState(\nisInit: null == isInit ? _self.isInit : isInit // ignore: cast_nullable_to_non_nullable\nas bool,hasProfile: null == hasProfile ? _self.hasProfile : hasProfile // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$ProfilesSelectorState {\n\n List<Profile> get profiles; String? get currentProfileId; int get columns;\n/// Create a copy of ProfilesSelectorState\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ProfilesSelectorStateCopyWith<ProfilesSelectorState> get copyWith => _$ProfilesSelectorStateCopyWithImpl<ProfilesSelectorState>(this as ProfilesSelectorState, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is ProfilesSelectorState&&const DeepCollectionEquality().equals(other.profiles, profiles)&&(identical(other.currentProfileId, currentProfileId) || other.currentProfileId == currentProfileId)&&(identical(other.columns, columns) || other.columns == columns));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(profiles),currentProfileId,columns);\n\n@override\nString toString() {\n  return 'ProfilesSelectorState(profiles: $profiles, currentProfileId: $currentProfileId, columns: $columns)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ProfilesSelectorStateCopyWith<$Res>  {\n  factory $ProfilesSelectorStateCopyWith(ProfilesSelectorState value, $Res Function(ProfilesSelectorState) _then) = _$ProfilesSelectorStateCopyWithImpl;\n@useResult\n$Res call({\n List<Profile> profiles, String? currentProfileId, int columns\n});\n\n\n\n\n}\n/// @nodoc\nclass _$ProfilesSelectorStateCopyWithImpl<$Res>\n    implements $ProfilesSelectorStateCopyWith<$Res> {\n  _$ProfilesSelectorStateCopyWithImpl(this._self, this._then);\n\n  final ProfilesSelectorState _self;\n  final $Res Function(ProfilesSelectorState) _then;\n\n/// Create a copy of ProfilesSelectorState\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? profiles = null,Object? currentProfileId = freezed,Object? columns = null,}) {\n  return _then(_self.copyWith(\nprofiles: null == profiles ? _self.profiles : profiles // ignore: cast_nullable_to_non_nullable\nas List<Profile>,currentProfileId: freezed == currentProfileId ? _self.currentProfileId : currentProfileId // ignore: cast_nullable_to_non_nullable\nas String?,columns: null == columns ? _self.columns : columns // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [ProfilesSelectorState].\nextension ProfilesSelectorStatePatterns on ProfilesSelectorState {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ProfilesSelectorState value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _ProfilesSelectorState() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ProfilesSelectorState value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ProfilesSelectorState():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ProfilesSelectorState value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ProfilesSelectorState() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<Profile> profiles,  String? currentProfileId,  int columns)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _ProfilesSelectorState() when $default != null:\nreturn $default(_that.profiles,_that.currentProfileId,_that.columns);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<Profile> profiles,  String? currentProfileId,  int columns)  $default,) {final _that = this;\nswitch (_that) {\ncase _ProfilesSelectorState():\nreturn $default(_that.profiles,_that.currentProfileId,_that.columns);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<Profile> profiles,  String? currentProfileId,  int columns)?  $default,) {final _that = this;\nswitch (_that) {\ncase _ProfilesSelectorState() when $default != null:\nreturn $default(_that.profiles,_that.currentProfileId,_that.columns);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _ProfilesSelectorState implements ProfilesSelectorState {\n  const _ProfilesSelectorState({required final  List<Profile> profiles, required this.currentProfileId, required this.columns}): _profiles = profiles;\n  \n\n final  List<Profile> _profiles;\n@override List<Profile> get profiles {\n  if (_profiles is EqualUnmodifiableListView) return _profiles;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_profiles);\n}\n\n@override final  String? currentProfileId;\n@override final  int columns;\n\n/// Create a copy of ProfilesSelectorState\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ProfilesSelectorStateCopyWith<_ProfilesSelectorState> get copyWith => __$ProfilesSelectorStateCopyWithImpl<_ProfilesSelectorState>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProfilesSelectorState&&const DeepCollectionEquality().equals(other._profiles, _profiles)&&(identical(other.currentProfileId, currentProfileId) || other.currentProfileId == currentProfileId)&&(identical(other.columns, columns) || other.columns == columns));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_profiles),currentProfileId,columns);\n\n@override\nString toString() {\n  return 'ProfilesSelectorState(profiles: $profiles, currentProfileId: $currentProfileId, columns: $columns)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ProfilesSelectorStateCopyWith<$Res> implements $ProfilesSelectorStateCopyWith<$Res> {\n  factory _$ProfilesSelectorStateCopyWith(_ProfilesSelectorState value, $Res Function(_ProfilesSelectorState) _then) = __$ProfilesSelectorStateCopyWithImpl;\n@override @useResult\n$Res call({\n List<Profile> profiles, String? currentProfileId, int columns\n});\n\n\n\n\n}\n/// @nodoc\nclass __$ProfilesSelectorStateCopyWithImpl<$Res>\n    implements _$ProfilesSelectorStateCopyWith<$Res> {\n  __$ProfilesSelectorStateCopyWithImpl(this._self, this._then);\n\n  final _ProfilesSelectorState _self;\n  final $Res Function(_ProfilesSelectorState) _then;\n\n/// Create a copy of ProfilesSelectorState\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? profiles = null,Object? currentProfileId = freezed,Object? columns = null,}) {\n  return _then(_ProfilesSelectorState(\nprofiles: null == profiles ? _self._profiles : profiles // ignore: cast_nullable_to_non_nullable\nas List<Profile>,currentProfileId: freezed == currentProfileId ? _self.currentProfileId : currentProfileId // ignore: cast_nullable_to_non_nullable\nas String?,columns: null == columns ? _self.columns : columns // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$NetworkDetectionState {\n\n bool get isLoading; IpInfo? get ipInfo; String? get errorMessage;\n/// Create a copy of NetworkDetectionState\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$NetworkDetectionStateCopyWith<NetworkDetectionState> get copyWith => _$NetworkDetectionStateCopyWithImpl<NetworkDetectionState>(this as NetworkDetectionState, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is NetworkDetectionState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.ipInfo, ipInfo) || other.ipInfo == ipInfo)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,isLoading,ipInfo,errorMessage);\n\n@override\nString toString() {\n  return 'NetworkDetectionState(isLoading: $isLoading, ipInfo: $ipInfo, errorMessage: $errorMessage)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $NetworkDetectionStateCopyWith<$Res>  {\n  factory $NetworkDetectionStateCopyWith(NetworkDetectionState value, $Res Function(NetworkDetectionState) _then) = _$NetworkDetectionStateCopyWithImpl;\n@useResult\n$Res call({\n bool isLoading, IpInfo? ipInfo, String? errorMessage\n});\n\n\n\n\n}\n/// @nodoc\nclass _$NetworkDetectionStateCopyWithImpl<$Res>\n    implements $NetworkDetectionStateCopyWith<$Res> {\n  _$NetworkDetectionStateCopyWithImpl(this._self, this._then);\n\n  final NetworkDetectionState _self;\n  final $Res Function(NetworkDetectionState) _then;\n\n/// Create a copy of NetworkDetectionState\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? isLoading = null,Object? ipInfo = freezed,Object? errorMessage = freezed,}) {\n  return _then(_self.copyWith(\nisLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable\nas bool,ipInfo: freezed == ipInfo ? _self.ipInfo : ipInfo // ignore: cast_nullable_to_non_nullable\nas IpInfo?,errorMessage: freezed == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable\nas String?,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [NetworkDetectionState].\nextension NetworkDetectionStatePatterns on NetworkDetectionState {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _NetworkDetectionState value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _NetworkDetectionState() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _NetworkDetectionState value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _NetworkDetectionState():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _NetworkDetectionState value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _NetworkDetectionState() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isLoading,  IpInfo? ipInfo,  String? errorMessage)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _NetworkDetectionState() when $default != null:\nreturn $default(_that.isLoading,_that.ipInfo,_that.errorMessage);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool isLoading,  IpInfo? ipInfo,  String? errorMessage)  $default,) {final _that = this;\nswitch (_that) {\ncase _NetworkDetectionState():\nreturn $default(_that.isLoading,_that.ipInfo,_that.errorMessage);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool isLoading,  IpInfo? ipInfo,  String? errorMessage)?  $default,) {final _that = this;\nswitch (_that) {\ncase _NetworkDetectionState() when $default != null:\nreturn $default(_that.isLoading,_that.ipInfo,_that.errorMessage);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _NetworkDetectionState implements NetworkDetectionState {\n  const _NetworkDetectionState({required this.isLoading, required this.ipInfo, this.errorMessage});\n  \n\n@override final  bool isLoading;\n@override final  IpInfo? ipInfo;\n@override final  String? errorMessage;\n\n/// Create a copy of NetworkDetectionState\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$NetworkDetectionStateCopyWith<_NetworkDetectionState> get copyWith => __$NetworkDetectionStateCopyWithImpl<_NetworkDetectionState>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _NetworkDetectionState&&(identical(other.isLoading, isLoading) || other.isLoading == isLoading)&&(identical(other.ipInfo, ipInfo) || other.ipInfo == ipInfo)&&(identical(other.errorMessage, errorMessage) || other.errorMessage == errorMessage));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,isLoading,ipInfo,errorMessage);\n\n@override\nString toString() {\n  return 'NetworkDetectionState(isLoading: $isLoading, ipInfo: $ipInfo, errorMessage: $errorMessage)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$NetworkDetectionStateCopyWith<$Res> implements $NetworkDetectionStateCopyWith<$Res> {\n  factory _$NetworkDetectionStateCopyWith(_NetworkDetectionState value, $Res Function(_NetworkDetectionState) _then) = __$NetworkDetectionStateCopyWithImpl;\n@override @useResult\n$Res call({\n bool isLoading, IpInfo? ipInfo, String? errorMessage\n});\n\n\n\n\n}\n/// @nodoc\nclass __$NetworkDetectionStateCopyWithImpl<$Res>\n    implements _$NetworkDetectionStateCopyWith<$Res> {\n  __$NetworkDetectionStateCopyWithImpl(this._self, this._then);\n\n  final _NetworkDetectionState _self;\n  final $Res Function(_NetworkDetectionState) _then;\n\n/// Create a copy of NetworkDetectionState\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? isLoading = null,Object? ipInfo = freezed,Object? errorMessage = freezed,}) {\n  return _then(_NetworkDetectionState(\nisLoading: null == isLoading ? _self.isLoading : isLoading // ignore: cast_nullable_to_non_nullable\nas bool,ipInfo: freezed == ipInfo ? _self.ipInfo : ipInfo // ignore: cast_nullable_to_non_nullable\nas IpInfo?,errorMessage: freezed == errorMessage ? _self.errorMessage : errorMessage // ignore: cast_nullable_to_non_nullable\nas String?,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$TrayState {\n\n Mode get mode; int get port; bool get autoLaunch; bool get systemProxy; bool get tunEnable; bool get isStart; String? get locale; Brightness? get brightness; List<Group> get groups; SelectedMap get selectedMap; bool get wakelockEnabled;\n/// Create a copy of TrayState\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$TrayStateCopyWith<TrayState> get copyWith => _$TrayStateCopyWithImpl<TrayState>(this as TrayState, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is TrayState&&(identical(other.mode, mode) || other.mode == mode)&&(identical(other.port, port) || other.port == port)&&(identical(other.autoLaunch, autoLaunch) || other.autoLaunch == autoLaunch)&&(identical(other.systemProxy, systemProxy) || other.systemProxy == systemProxy)&&(identical(other.tunEnable, tunEnable) || other.tunEnable == tunEnable)&&(identical(other.isStart, isStart) || other.isStart == isStart)&&(identical(other.locale, locale) || other.locale == locale)&&(identical(other.brightness, brightness) || other.brightness == brightness)&&const DeepCollectionEquality().equals(other.groups, groups)&&const DeepCollectionEquality().equals(other.selectedMap, selectedMap)&&(identical(other.wakelockEnabled, wakelockEnabled) || other.wakelockEnabled == wakelockEnabled));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,mode,port,autoLaunch,systemProxy,tunEnable,isStart,locale,brightness,const DeepCollectionEquality().hash(groups),const DeepCollectionEquality().hash(selectedMap),wakelockEnabled);\n\n@override\nString toString() {\n  return 'TrayState(mode: $mode, port: $port, autoLaunch: $autoLaunch, systemProxy: $systemProxy, tunEnable: $tunEnable, isStart: $isStart, locale: $locale, brightness: $brightness, groups: $groups, selectedMap: $selectedMap, wakelockEnabled: $wakelockEnabled)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $TrayStateCopyWith<$Res>  {\n  factory $TrayStateCopyWith(TrayState value, $Res Function(TrayState) _then) = _$TrayStateCopyWithImpl;\n@useResult\n$Res call({\n Mode mode, int port, bool autoLaunch, bool systemProxy, bool tunEnable, bool isStart, String? locale, Brightness? brightness, List<Group> groups, SelectedMap selectedMap, bool wakelockEnabled\n});\n\n\n\n\n}\n/// @nodoc\nclass _$TrayStateCopyWithImpl<$Res>\n    implements $TrayStateCopyWith<$Res> {\n  _$TrayStateCopyWithImpl(this._self, this._then);\n\n  final TrayState _self;\n  final $Res Function(TrayState) _then;\n\n/// Create a copy of TrayState\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? mode = null,Object? port = null,Object? autoLaunch = null,Object? systemProxy = null,Object? tunEnable = null,Object? isStart = null,Object? locale = freezed,Object? brightness = freezed,Object? groups = null,Object? selectedMap = null,Object? wakelockEnabled = null,}) {\n  return _then(_self.copyWith(\nmode: null == mode ? _self.mode : mode // ignore: cast_nullable_to_non_nullable\nas Mode,port: null == port ? _self.port : port // ignore: cast_nullable_to_non_nullable\nas int,autoLaunch: null == autoLaunch ? _self.autoLaunch : autoLaunch // ignore: cast_nullable_to_non_nullable\nas bool,systemProxy: null == systemProxy ? _self.systemProxy : systemProxy // ignore: cast_nullable_to_non_nullable\nas bool,tunEnable: null == tunEnable ? _self.tunEnable : tunEnable // ignore: cast_nullable_to_non_nullable\nas bool,isStart: null == isStart ? _self.isStart : isStart // ignore: cast_nullable_to_non_nullable\nas bool,locale: freezed == locale ? _self.locale : locale // ignore: cast_nullable_to_non_nullable\nas String?,brightness: freezed == brightness ? _self.brightness : brightness // ignore: cast_nullable_to_non_nullable\nas Brightness?,groups: null == groups ? _self.groups : groups // ignore: cast_nullable_to_non_nullable\nas List<Group>,selectedMap: null == selectedMap ? _self.selectedMap : selectedMap // ignore: cast_nullable_to_non_nullable\nas SelectedMap,wakelockEnabled: null == wakelockEnabled ? _self.wakelockEnabled : wakelockEnabled // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [TrayState].\nextension TrayStatePatterns on TrayState {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _TrayState value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _TrayState() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _TrayState value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _TrayState():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _TrayState value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _TrayState() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( Mode mode,  int port,  bool autoLaunch,  bool systemProxy,  bool tunEnable,  bool isStart,  String? locale,  Brightness? brightness,  List<Group> groups,  SelectedMap selectedMap,  bool wakelockEnabled)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _TrayState() when $default != null:\nreturn $default(_that.mode,_that.port,_that.autoLaunch,_that.systemProxy,_that.tunEnable,_that.isStart,_that.locale,_that.brightness,_that.groups,_that.selectedMap,_that.wakelockEnabled);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( Mode mode,  int port,  bool autoLaunch,  bool systemProxy,  bool tunEnable,  bool isStart,  String? locale,  Brightness? brightness,  List<Group> groups,  SelectedMap selectedMap,  bool wakelockEnabled)  $default,) {final _that = this;\nswitch (_that) {\ncase _TrayState():\nreturn $default(_that.mode,_that.port,_that.autoLaunch,_that.systemProxy,_that.tunEnable,_that.isStart,_that.locale,_that.brightness,_that.groups,_that.selectedMap,_that.wakelockEnabled);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( Mode mode,  int port,  bool autoLaunch,  bool systemProxy,  bool tunEnable,  bool isStart,  String? locale,  Brightness? brightness,  List<Group> groups,  SelectedMap selectedMap,  bool wakelockEnabled)?  $default,) {final _that = this;\nswitch (_that) {\ncase _TrayState() when $default != null:\nreturn $default(_that.mode,_that.port,_that.autoLaunch,_that.systemProxy,_that.tunEnable,_that.isStart,_that.locale,_that.brightness,_that.groups,_that.selectedMap,_that.wakelockEnabled);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _TrayState implements TrayState {\n  const _TrayState({required this.mode, required this.port, required this.autoLaunch, required this.systemProxy, required this.tunEnable, required this.isStart, required this.locale, required this.brightness, required final  List<Group> groups, required final  SelectedMap selectedMap, this.wakelockEnabled = false}): _groups = groups,_selectedMap = selectedMap;\n  \n\n@override final  Mode mode;\n@override final  int port;\n@override final  bool autoLaunch;\n@override final  bool systemProxy;\n@override final  bool tunEnable;\n@override final  bool isStart;\n@override final  String? locale;\n@override final  Brightness? brightness;\n final  List<Group> _groups;\n@override List<Group> get groups {\n  if (_groups is EqualUnmodifiableListView) return _groups;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_groups);\n}\n\n final  SelectedMap _selectedMap;\n@override SelectedMap get selectedMap {\n  if (_selectedMap is EqualUnmodifiableMapView) return _selectedMap;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableMapView(_selectedMap);\n}\n\n@override@JsonKey() final  bool wakelockEnabled;\n\n/// Create a copy of TrayState\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$TrayStateCopyWith<_TrayState> get copyWith => __$TrayStateCopyWithImpl<_TrayState>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _TrayState&&(identical(other.mode, mode) || other.mode == mode)&&(identical(other.port, port) || other.port == port)&&(identical(other.autoLaunch, autoLaunch) || other.autoLaunch == autoLaunch)&&(identical(other.systemProxy, systemProxy) || other.systemProxy == systemProxy)&&(identical(other.tunEnable, tunEnable) || other.tunEnable == tunEnable)&&(identical(other.isStart, isStart) || other.isStart == isStart)&&(identical(other.locale, locale) || other.locale == locale)&&(identical(other.brightness, brightness) || other.brightness == brightness)&&const DeepCollectionEquality().equals(other._groups, _groups)&&const DeepCollectionEquality().equals(other._selectedMap, _selectedMap)&&(identical(other.wakelockEnabled, wakelockEnabled) || other.wakelockEnabled == wakelockEnabled));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,mode,port,autoLaunch,systemProxy,tunEnable,isStart,locale,brightness,const DeepCollectionEquality().hash(_groups),const DeepCollectionEquality().hash(_selectedMap),wakelockEnabled);\n\n@override\nString toString() {\n  return 'TrayState(mode: $mode, port: $port, autoLaunch: $autoLaunch, systemProxy: $systemProxy, tunEnable: $tunEnable, isStart: $isStart, locale: $locale, brightness: $brightness, groups: $groups, selectedMap: $selectedMap, wakelockEnabled: $wakelockEnabled)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$TrayStateCopyWith<$Res> implements $TrayStateCopyWith<$Res> {\n  factory _$TrayStateCopyWith(_TrayState value, $Res Function(_TrayState) _then) = __$TrayStateCopyWithImpl;\n@override @useResult\n$Res call({\n Mode mode, int port, bool autoLaunch, bool systemProxy, bool tunEnable, bool isStart, String? locale, Brightness? brightness, List<Group> groups, SelectedMap selectedMap, bool wakelockEnabled\n});\n\n\n\n\n}\n/// @nodoc\nclass __$TrayStateCopyWithImpl<$Res>\n    implements _$TrayStateCopyWith<$Res> {\n  __$TrayStateCopyWithImpl(this._self, this._then);\n\n  final _TrayState _self;\n  final $Res Function(_TrayState) _then;\n\n/// Create a copy of TrayState\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? mode = null,Object? port = null,Object? autoLaunch = null,Object? systemProxy = null,Object? tunEnable = null,Object? isStart = null,Object? locale = freezed,Object? brightness = freezed,Object? groups = null,Object? selectedMap = null,Object? wakelockEnabled = null,}) {\n  return _then(_TrayState(\nmode: null == mode ? _self.mode : mode // ignore: cast_nullable_to_non_nullable\nas Mode,port: null == port ? _self.port : port // ignore: cast_nullable_to_non_nullable\nas int,autoLaunch: null == autoLaunch ? _self.autoLaunch : autoLaunch // ignore: cast_nullable_to_non_nullable\nas bool,systemProxy: null == systemProxy ? _self.systemProxy : systemProxy // ignore: cast_nullable_to_non_nullable\nas bool,tunEnable: null == tunEnable ? _self.tunEnable : tunEnable // ignore: cast_nullable_to_non_nullable\nas bool,isStart: null == isStart ? _self.isStart : isStart // ignore: cast_nullable_to_non_nullable\nas bool,locale: freezed == locale ? _self.locale : locale // ignore: cast_nullable_to_non_nullable\nas String?,brightness: freezed == brightness ? _self.brightness : brightness // ignore: cast_nullable_to_non_nullable\nas Brightness?,groups: null == groups ? _self._groups : groups // ignore: cast_nullable_to_non_nullable\nas List<Group>,selectedMap: null == selectedMap ? _self._selectedMap : selectedMap // ignore: cast_nullable_to_non_nullable\nas SelectedMap,wakelockEnabled: null == wakelockEnabled ? _self.wakelockEnabled : wakelockEnabled // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$NavigationState {\n\n PageLabel get pageLabel; List<NavigationItem> get navigationItems; ViewMode get viewMode; String? get locale; int get currentIndex;\n/// Create a copy of NavigationState\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$NavigationStateCopyWith<NavigationState> get copyWith => _$NavigationStateCopyWithImpl<NavigationState>(this as NavigationState, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is NavigationState&&(identical(other.pageLabel, pageLabel) || other.pageLabel == pageLabel)&&const DeepCollectionEquality().equals(other.navigationItems, navigationItems)&&(identical(other.viewMode, viewMode) || other.viewMode == viewMode)&&(identical(other.locale, locale) || other.locale == locale)&&(identical(other.currentIndex, currentIndex) || other.currentIndex == currentIndex));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,pageLabel,const DeepCollectionEquality().hash(navigationItems),viewMode,locale,currentIndex);\n\n@override\nString toString() {\n  return 'NavigationState(pageLabel: $pageLabel, navigationItems: $navigationItems, viewMode: $viewMode, locale: $locale, currentIndex: $currentIndex)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $NavigationStateCopyWith<$Res>  {\n  factory $NavigationStateCopyWith(NavigationState value, $Res Function(NavigationState) _then) = _$NavigationStateCopyWithImpl;\n@useResult\n$Res call({\n PageLabel pageLabel, List<NavigationItem> navigationItems, ViewMode viewMode, String? locale, int currentIndex\n});\n\n\n\n\n}\n/// @nodoc\nclass _$NavigationStateCopyWithImpl<$Res>\n    implements $NavigationStateCopyWith<$Res> {\n  _$NavigationStateCopyWithImpl(this._self, this._then);\n\n  final NavigationState _self;\n  final $Res Function(NavigationState) _then;\n\n/// Create a copy of NavigationState\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? pageLabel = null,Object? navigationItems = null,Object? viewMode = null,Object? locale = freezed,Object? currentIndex = null,}) {\n  return _then(_self.copyWith(\npageLabel: null == pageLabel ? _self.pageLabel : pageLabel // ignore: cast_nullable_to_non_nullable\nas PageLabel,navigationItems: null == navigationItems ? _self.navigationItems : navigationItems // ignore: cast_nullable_to_non_nullable\nas List<NavigationItem>,viewMode: null == viewMode ? _self.viewMode : viewMode // ignore: cast_nullable_to_non_nullable\nas ViewMode,locale: freezed == locale ? _self.locale : locale // ignore: cast_nullable_to_non_nullable\nas String?,currentIndex: null == currentIndex ? _self.currentIndex : currentIndex // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [NavigationState].\nextension NavigationStatePatterns on NavigationState {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _NavigationState value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _NavigationState() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _NavigationState value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _NavigationState():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _NavigationState value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _NavigationState() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( PageLabel pageLabel,  List<NavigationItem> navigationItems,  ViewMode viewMode,  String? locale,  int currentIndex)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _NavigationState() when $default != null:\nreturn $default(_that.pageLabel,_that.navigationItems,_that.viewMode,_that.locale,_that.currentIndex);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( PageLabel pageLabel,  List<NavigationItem> navigationItems,  ViewMode viewMode,  String? locale,  int currentIndex)  $default,) {final _that = this;\nswitch (_that) {\ncase _NavigationState():\nreturn $default(_that.pageLabel,_that.navigationItems,_that.viewMode,_that.locale,_that.currentIndex);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( PageLabel pageLabel,  List<NavigationItem> navigationItems,  ViewMode viewMode,  String? locale,  int currentIndex)?  $default,) {final _that = this;\nswitch (_that) {\ncase _NavigationState() when $default != null:\nreturn $default(_that.pageLabel,_that.navigationItems,_that.viewMode,_that.locale,_that.currentIndex);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _NavigationState implements NavigationState {\n  const _NavigationState({required this.pageLabel, required final  List<NavigationItem> navigationItems, required this.viewMode, required this.locale, required this.currentIndex}): _navigationItems = navigationItems;\n  \n\n@override final  PageLabel pageLabel;\n final  List<NavigationItem> _navigationItems;\n@override List<NavigationItem> get navigationItems {\n  if (_navigationItems is EqualUnmodifiableListView) return _navigationItems;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_navigationItems);\n}\n\n@override final  ViewMode viewMode;\n@override final  String? locale;\n@override final  int currentIndex;\n\n/// Create a copy of NavigationState\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$NavigationStateCopyWith<_NavigationState> get copyWith => __$NavigationStateCopyWithImpl<_NavigationState>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _NavigationState&&(identical(other.pageLabel, pageLabel) || other.pageLabel == pageLabel)&&const DeepCollectionEquality().equals(other._navigationItems, _navigationItems)&&(identical(other.viewMode, viewMode) || other.viewMode == viewMode)&&(identical(other.locale, locale) || other.locale == locale)&&(identical(other.currentIndex, currentIndex) || other.currentIndex == currentIndex));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,pageLabel,const DeepCollectionEquality().hash(_navigationItems),viewMode,locale,currentIndex);\n\n@override\nString toString() {\n  return 'NavigationState(pageLabel: $pageLabel, navigationItems: $navigationItems, viewMode: $viewMode, locale: $locale, currentIndex: $currentIndex)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$NavigationStateCopyWith<$Res> implements $NavigationStateCopyWith<$Res> {\n  factory _$NavigationStateCopyWith(_NavigationState value, $Res Function(_NavigationState) _then) = __$NavigationStateCopyWithImpl;\n@override @useResult\n$Res call({\n PageLabel pageLabel, List<NavigationItem> navigationItems, ViewMode viewMode, String? locale, int currentIndex\n});\n\n\n\n\n}\n/// @nodoc\nclass __$NavigationStateCopyWithImpl<$Res>\n    implements _$NavigationStateCopyWith<$Res> {\n  __$NavigationStateCopyWithImpl(this._self, this._then);\n\n  final _NavigationState _self;\n  final $Res Function(_NavigationState) _then;\n\n/// Create a copy of NavigationState\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? pageLabel = null,Object? navigationItems = null,Object? viewMode = null,Object? locale = freezed,Object? currentIndex = null,}) {\n  return _then(_NavigationState(\npageLabel: null == pageLabel ? _self.pageLabel : pageLabel // ignore: cast_nullable_to_non_nullable\nas PageLabel,navigationItems: null == navigationItems ? _self._navigationItems : navigationItems // ignore: cast_nullable_to_non_nullable\nas List<NavigationItem>,viewMode: null == viewMode ? _self.viewMode : viewMode // ignore: cast_nullable_to_non_nullable\nas ViewMode,locale: freezed == locale ? _self.locale : locale // ignore: cast_nullable_to_non_nullable\nas String?,currentIndex: null == currentIndex ? _self.currentIndex : currentIndex // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$GroupsState {\n\n List<Group> get value;\n/// Create a copy of GroupsState\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$GroupsStateCopyWith<GroupsState> get copyWith => _$GroupsStateCopyWithImpl<GroupsState>(this as GroupsState, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is GroupsState&&const DeepCollectionEquality().equals(other.value, value));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(value));\n\n@override\nString toString() {\n  return 'GroupsState(value: $value)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $GroupsStateCopyWith<$Res>  {\n  factory $GroupsStateCopyWith(GroupsState value, $Res Function(GroupsState) _then) = _$GroupsStateCopyWithImpl;\n@useResult\n$Res call({\n List<Group> value\n});\n\n\n\n\n}\n/// @nodoc\nclass _$GroupsStateCopyWithImpl<$Res>\n    implements $GroupsStateCopyWith<$Res> {\n  _$GroupsStateCopyWithImpl(this._self, this._then);\n\n  final GroupsState _self;\n  final $Res Function(GroupsState) _then;\n\n/// Create a copy of GroupsState\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? value = null,}) {\n  return _then(_self.copyWith(\nvalue: null == value ? _self.value : value // ignore: cast_nullable_to_non_nullable\nas List<Group>,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [GroupsState].\nextension GroupsStatePatterns on GroupsState {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _GroupsState value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _GroupsState() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _GroupsState value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _GroupsState():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _GroupsState value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _GroupsState() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<Group> value)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _GroupsState() when $default != null:\nreturn $default(_that.value);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<Group> value)  $default,) {final _that = this;\nswitch (_that) {\ncase _GroupsState():\nreturn $default(_that.value);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<Group> value)?  $default,) {final _that = this;\nswitch (_that) {\ncase _GroupsState() when $default != null:\nreturn $default(_that.value);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _GroupsState implements GroupsState {\n  const _GroupsState({required final  List<Group> value}): _value = value;\n  \n\n final  List<Group> _value;\n@override List<Group> get value {\n  if (_value is EqualUnmodifiableListView) return _value;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_value);\n}\n\n\n/// Create a copy of GroupsState\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$GroupsStateCopyWith<_GroupsState> get copyWith => __$GroupsStateCopyWithImpl<_GroupsState>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _GroupsState&&const DeepCollectionEquality().equals(other._value, _value));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_value));\n\n@override\nString toString() {\n  return 'GroupsState(value: $value)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$GroupsStateCopyWith<$Res> implements $GroupsStateCopyWith<$Res> {\n  factory _$GroupsStateCopyWith(_GroupsState value, $Res Function(_GroupsState) _then) = __$GroupsStateCopyWithImpl;\n@override @useResult\n$Res call({\n List<Group> value\n});\n\n\n\n\n}\n/// @nodoc\nclass __$GroupsStateCopyWithImpl<$Res>\n    implements _$GroupsStateCopyWith<$Res> {\n  __$GroupsStateCopyWithImpl(this._self, this._then);\n\n  final _GroupsState _self;\n  final $Res Function(_GroupsState) _then;\n\n/// Create a copy of GroupsState\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? value = null,}) {\n  return _then(_GroupsState(\nvalue: null == value ? _self._value : value // ignore: cast_nullable_to_non_nullable\nas List<Group>,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$NavigationItemsState {\n\n List<NavigationItem> get value;\n/// Create a copy of NavigationItemsState\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$NavigationItemsStateCopyWith<NavigationItemsState> get copyWith => _$NavigationItemsStateCopyWithImpl<NavigationItemsState>(this as NavigationItemsState, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is NavigationItemsState&&const DeepCollectionEquality().equals(other.value, value));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(value));\n\n@override\nString toString() {\n  return 'NavigationItemsState(value: $value)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $NavigationItemsStateCopyWith<$Res>  {\n  factory $NavigationItemsStateCopyWith(NavigationItemsState value, $Res Function(NavigationItemsState) _then) = _$NavigationItemsStateCopyWithImpl;\n@useResult\n$Res call({\n List<NavigationItem> value\n});\n\n\n\n\n}\n/// @nodoc\nclass _$NavigationItemsStateCopyWithImpl<$Res>\n    implements $NavigationItemsStateCopyWith<$Res> {\n  _$NavigationItemsStateCopyWithImpl(this._self, this._then);\n\n  final NavigationItemsState _self;\n  final $Res Function(NavigationItemsState) _then;\n\n/// Create a copy of NavigationItemsState\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? value = null,}) {\n  return _then(_self.copyWith(\nvalue: null == value ? _self.value : value // ignore: cast_nullable_to_non_nullable\nas List<NavigationItem>,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [NavigationItemsState].\nextension NavigationItemsStatePatterns on NavigationItemsState {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _NavigationItemsState value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _NavigationItemsState() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _NavigationItemsState value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _NavigationItemsState():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _NavigationItemsState value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _NavigationItemsState() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<NavigationItem> value)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _NavigationItemsState() when $default != null:\nreturn $default(_that.value);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<NavigationItem> value)  $default,) {final _that = this;\nswitch (_that) {\ncase _NavigationItemsState():\nreturn $default(_that.value);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<NavigationItem> value)?  $default,) {final _that = this;\nswitch (_that) {\ncase _NavigationItemsState() when $default != null:\nreturn $default(_that.value);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _NavigationItemsState implements NavigationItemsState {\n  const _NavigationItemsState({required final  List<NavigationItem> value}): _value = value;\n  \n\n final  List<NavigationItem> _value;\n@override List<NavigationItem> get value {\n  if (_value is EqualUnmodifiableListView) return _value;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_value);\n}\n\n\n/// Create a copy of NavigationItemsState\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$NavigationItemsStateCopyWith<_NavigationItemsState> get copyWith => __$NavigationItemsStateCopyWithImpl<_NavigationItemsState>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _NavigationItemsState&&const DeepCollectionEquality().equals(other._value, _value));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_value));\n\n@override\nString toString() {\n  return 'NavigationItemsState(value: $value)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$NavigationItemsStateCopyWith<$Res> implements $NavigationItemsStateCopyWith<$Res> {\n  factory _$NavigationItemsStateCopyWith(_NavigationItemsState value, $Res Function(_NavigationItemsState) _then) = __$NavigationItemsStateCopyWithImpl;\n@override @useResult\n$Res call({\n List<NavigationItem> value\n});\n\n\n\n\n}\n/// @nodoc\nclass __$NavigationItemsStateCopyWithImpl<$Res>\n    implements _$NavigationItemsStateCopyWith<$Res> {\n  __$NavigationItemsStateCopyWithImpl(this._self, this._then);\n\n  final _NavigationItemsState _self;\n  final $Res Function(_NavigationItemsState) _then;\n\n/// Create a copy of NavigationItemsState\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? value = null,}) {\n  return _then(_NavigationItemsState(\nvalue: null == value ? _self._value : value // ignore: cast_nullable_to_non_nullable\nas List<NavigationItem>,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$ProxiesListState {\n\n List<Group> get groups; Set<String> get currentUnfoldSet; ProxiesSortType get proxiesSortType; ProxyCardType get proxyCardType; num get sortNum; int get columns;\n/// Create a copy of ProxiesListState\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ProxiesListStateCopyWith<ProxiesListState> get copyWith => _$ProxiesListStateCopyWithImpl<ProxiesListState>(this as ProxiesListState, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is ProxiesListState&&const DeepCollectionEquality().equals(other.groups, groups)&&const DeepCollectionEquality().equals(other.currentUnfoldSet, currentUnfoldSet)&&(identical(other.proxiesSortType, proxiesSortType) || other.proxiesSortType == proxiesSortType)&&(identical(other.proxyCardType, proxyCardType) || other.proxyCardType == proxyCardType)&&(identical(other.sortNum, sortNum) || other.sortNum == sortNum)&&(identical(other.columns, columns) || other.columns == columns));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(groups),const DeepCollectionEquality().hash(currentUnfoldSet),proxiesSortType,proxyCardType,sortNum,columns);\n\n@override\nString toString() {\n  return 'ProxiesListState(groups: $groups, currentUnfoldSet: $currentUnfoldSet, proxiesSortType: $proxiesSortType, proxyCardType: $proxyCardType, sortNum: $sortNum, columns: $columns)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ProxiesListStateCopyWith<$Res>  {\n  factory $ProxiesListStateCopyWith(ProxiesListState value, $Res Function(ProxiesListState) _then) = _$ProxiesListStateCopyWithImpl;\n@useResult\n$Res call({\n List<Group> groups, Set<String> currentUnfoldSet, ProxiesSortType proxiesSortType, ProxyCardType proxyCardType, num sortNum, int columns\n});\n\n\n\n\n}\n/// @nodoc\nclass _$ProxiesListStateCopyWithImpl<$Res>\n    implements $ProxiesListStateCopyWith<$Res> {\n  _$ProxiesListStateCopyWithImpl(this._self, this._then);\n\n  final ProxiesListState _self;\n  final $Res Function(ProxiesListState) _then;\n\n/// Create a copy of ProxiesListState\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? groups = null,Object? currentUnfoldSet = null,Object? proxiesSortType = null,Object? proxyCardType = null,Object? sortNum = null,Object? columns = null,}) {\n  return _then(_self.copyWith(\ngroups: null == groups ? _self.groups : groups // ignore: cast_nullable_to_non_nullable\nas List<Group>,currentUnfoldSet: null == currentUnfoldSet ? _self.currentUnfoldSet : currentUnfoldSet // ignore: cast_nullable_to_non_nullable\nas Set<String>,proxiesSortType: null == proxiesSortType ? _self.proxiesSortType : proxiesSortType // ignore: cast_nullable_to_non_nullable\nas ProxiesSortType,proxyCardType: null == proxyCardType ? _self.proxyCardType : proxyCardType // ignore: cast_nullable_to_non_nullable\nas ProxyCardType,sortNum: null == sortNum ? _self.sortNum : sortNum // ignore: cast_nullable_to_non_nullable\nas num,columns: null == columns ? _self.columns : columns // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [ProxiesListState].\nextension ProxiesListStatePatterns on ProxiesListState {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ProxiesListState value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _ProxiesListState() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ProxiesListState value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ProxiesListState():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ProxiesListState value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ProxiesListState() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<Group> groups,  Set<String> currentUnfoldSet,  ProxiesSortType proxiesSortType,  ProxyCardType proxyCardType,  num sortNum,  int columns)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _ProxiesListState() when $default != null:\nreturn $default(_that.groups,_that.currentUnfoldSet,_that.proxiesSortType,_that.proxyCardType,_that.sortNum,_that.columns);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<Group> groups,  Set<String> currentUnfoldSet,  ProxiesSortType proxiesSortType,  ProxyCardType proxyCardType,  num sortNum,  int columns)  $default,) {final _that = this;\nswitch (_that) {\ncase _ProxiesListState():\nreturn $default(_that.groups,_that.currentUnfoldSet,_that.proxiesSortType,_that.proxyCardType,_that.sortNum,_that.columns);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<Group> groups,  Set<String> currentUnfoldSet,  ProxiesSortType proxiesSortType,  ProxyCardType proxyCardType,  num sortNum,  int columns)?  $default,) {final _that = this;\nswitch (_that) {\ncase _ProxiesListState() when $default != null:\nreturn $default(_that.groups,_that.currentUnfoldSet,_that.proxiesSortType,_that.proxyCardType,_that.sortNum,_that.columns);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _ProxiesListState implements ProxiesListState {\n  const _ProxiesListState({required final  List<Group> groups, required final  Set<String> currentUnfoldSet, required this.proxiesSortType, required this.proxyCardType, required this.sortNum, required this.columns}): _groups = groups,_currentUnfoldSet = currentUnfoldSet;\n  \n\n final  List<Group> _groups;\n@override List<Group> get groups {\n  if (_groups is EqualUnmodifiableListView) return _groups;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_groups);\n}\n\n final  Set<String> _currentUnfoldSet;\n@override Set<String> get currentUnfoldSet {\n  if (_currentUnfoldSet is EqualUnmodifiableSetView) return _currentUnfoldSet;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableSetView(_currentUnfoldSet);\n}\n\n@override final  ProxiesSortType proxiesSortType;\n@override final  ProxyCardType proxyCardType;\n@override final  num sortNum;\n@override final  int columns;\n\n/// Create a copy of ProxiesListState\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ProxiesListStateCopyWith<_ProxiesListState> get copyWith => __$ProxiesListStateCopyWithImpl<_ProxiesListState>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProxiesListState&&const DeepCollectionEquality().equals(other._groups, _groups)&&const DeepCollectionEquality().equals(other._currentUnfoldSet, _currentUnfoldSet)&&(identical(other.proxiesSortType, proxiesSortType) || other.proxiesSortType == proxiesSortType)&&(identical(other.proxyCardType, proxyCardType) || other.proxyCardType == proxyCardType)&&(identical(other.sortNum, sortNum) || other.sortNum == sortNum)&&(identical(other.columns, columns) || other.columns == columns));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_groups),const DeepCollectionEquality().hash(_currentUnfoldSet),proxiesSortType,proxyCardType,sortNum,columns);\n\n@override\nString toString() {\n  return 'ProxiesListState(groups: $groups, currentUnfoldSet: $currentUnfoldSet, proxiesSortType: $proxiesSortType, proxyCardType: $proxyCardType, sortNum: $sortNum, columns: $columns)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ProxiesListStateCopyWith<$Res> implements $ProxiesListStateCopyWith<$Res> {\n  factory _$ProxiesListStateCopyWith(_ProxiesListState value, $Res Function(_ProxiesListState) _then) = __$ProxiesListStateCopyWithImpl;\n@override @useResult\n$Res call({\n List<Group> groups, Set<String> currentUnfoldSet, ProxiesSortType proxiesSortType, ProxyCardType proxyCardType, num sortNum, int columns\n});\n\n\n\n\n}\n/// @nodoc\nclass __$ProxiesListStateCopyWithImpl<$Res>\n    implements _$ProxiesListStateCopyWith<$Res> {\n  __$ProxiesListStateCopyWithImpl(this._self, this._then);\n\n  final _ProxiesListState _self;\n  final $Res Function(_ProxiesListState) _then;\n\n/// Create a copy of ProxiesListState\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? groups = null,Object? currentUnfoldSet = null,Object? proxiesSortType = null,Object? proxyCardType = null,Object? sortNum = null,Object? columns = null,}) {\n  return _then(_ProxiesListState(\ngroups: null == groups ? _self._groups : groups // ignore: cast_nullable_to_non_nullable\nas List<Group>,currentUnfoldSet: null == currentUnfoldSet ? _self._currentUnfoldSet : currentUnfoldSet // ignore: cast_nullable_to_non_nullable\nas Set<String>,proxiesSortType: null == proxiesSortType ? _self.proxiesSortType : proxiesSortType // ignore: cast_nullable_to_non_nullable\nas ProxiesSortType,proxyCardType: null == proxyCardType ? _self.proxyCardType : proxyCardType // ignore: cast_nullable_to_non_nullable\nas ProxyCardType,sortNum: null == sortNum ? _self.sortNum : sortNum // ignore: cast_nullable_to_non_nullable\nas num,columns: null == columns ? _self.columns : columns // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$ProxiesTabState {\n\n List<Group> get groups; String? get currentGroupName; ProxiesSortType get proxiesSortType; ProxyCardType get proxyCardType; num get sortNum; int get columns;\n/// Create a copy of ProxiesTabState\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ProxiesTabStateCopyWith<ProxiesTabState> get copyWith => _$ProxiesTabStateCopyWithImpl<ProxiesTabState>(this as ProxiesTabState, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is ProxiesTabState&&const DeepCollectionEquality().equals(other.groups, groups)&&(identical(other.currentGroupName, currentGroupName) || other.currentGroupName == currentGroupName)&&(identical(other.proxiesSortType, proxiesSortType) || other.proxiesSortType == proxiesSortType)&&(identical(other.proxyCardType, proxyCardType) || other.proxyCardType == proxyCardType)&&(identical(other.sortNum, sortNum) || other.sortNum == sortNum)&&(identical(other.columns, columns) || other.columns == columns));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(groups),currentGroupName,proxiesSortType,proxyCardType,sortNum,columns);\n\n@override\nString toString() {\n  return 'ProxiesTabState(groups: $groups, currentGroupName: $currentGroupName, proxiesSortType: $proxiesSortType, proxyCardType: $proxyCardType, sortNum: $sortNum, columns: $columns)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ProxiesTabStateCopyWith<$Res>  {\n  factory $ProxiesTabStateCopyWith(ProxiesTabState value, $Res Function(ProxiesTabState) _then) = _$ProxiesTabStateCopyWithImpl;\n@useResult\n$Res call({\n List<Group> groups, String? currentGroupName, ProxiesSortType proxiesSortType, ProxyCardType proxyCardType, num sortNum, int columns\n});\n\n\n\n\n}\n/// @nodoc\nclass _$ProxiesTabStateCopyWithImpl<$Res>\n    implements $ProxiesTabStateCopyWith<$Res> {\n  _$ProxiesTabStateCopyWithImpl(this._self, this._then);\n\n  final ProxiesTabState _self;\n  final $Res Function(ProxiesTabState) _then;\n\n/// Create a copy of ProxiesTabState\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? groups = null,Object? currentGroupName = freezed,Object? proxiesSortType = null,Object? proxyCardType = null,Object? sortNum = null,Object? columns = null,}) {\n  return _then(_self.copyWith(\ngroups: null == groups ? _self.groups : groups // ignore: cast_nullable_to_non_nullable\nas List<Group>,currentGroupName: freezed == currentGroupName ? _self.currentGroupName : currentGroupName // ignore: cast_nullable_to_non_nullable\nas String?,proxiesSortType: null == proxiesSortType ? _self.proxiesSortType : proxiesSortType // ignore: cast_nullable_to_non_nullable\nas ProxiesSortType,proxyCardType: null == proxyCardType ? _self.proxyCardType : proxyCardType // ignore: cast_nullable_to_non_nullable\nas ProxyCardType,sortNum: null == sortNum ? _self.sortNum : sortNum // ignore: cast_nullable_to_non_nullable\nas num,columns: null == columns ? _self.columns : columns // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [ProxiesTabState].\nextension ProxiesTabStatePatterns on ProxiesTabState {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ProxiesTabState value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _ProxiesTabState() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ProxiesTabState value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ProxiesTabState():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ProxiesTabState value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ProxiesTabState() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<Group> groups,  String? currentGroupName,  ProxiesSortType proxiesSortType,  ProxyCardType proxyCardType,  num sortNum,  int columns)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _ProxiesTabState() when $default != null:\nreturn $default(_that.groups,_that.currentGroupName,_that.proxiesSortType,_that.proxyCardType,_that.sortNum,_that.columns);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<Group> groups,  String? currentGroupName,  ProxiesSortType proxiesSortType,  ProxyCardType proxyCardType,  num sortNum,  int columns)  $default,) {final _that = this;\nswitch (_that) {\ncase _ProxiesTabState():\nreturn $default(_that.groups,_that.currentGroupName,_that.proxiesSortType,_that.proxyCardType,_that.sortNum,_that.columns);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<Group> groups,  String? currentGroupName,  ProxiesSortType proxiesSortType,  ProxyCardType proxyCardType,  num sortNum,  int columns)?  $default,) {final _that = this;\nswitch (_that) {\ncase _ProxiesTabState() when $default != null:\nreturn $default(_that.groups,_that.currentGroupName,_that.proxiesSortType,_that.proxyCardType,_that.sortNum,_that.columns);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _ProxiesTabState implements ProxiesTabState {\n  const _ProxiesTabState({required final  List<Group> groups, required this.currentGroupName, required this.proxiesSortType, required this.proxyCardType, required this.sortNum, required this.columns}): _groups = groups;\n  \n\n final  List<Group> _groups;\n@override List<Group> get groups {\n  if (_groups is EqualUnmodifiableListView) return _groups;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_groups);\n}\n\n@override final  String? currentGroupName;\n@override final  ProxiesSortType proxiesSortType;\n@override final  ProxyCardType proxyCardType;\n@override final  num sortNum;\n@override final  int columns;\n\n/// Create a copy of ProxiesTabState\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ProxiesTabStateCopyWith<_ProxiesTabState> get copyWith => __$ProxiesTabStateCopyWithImpl<_ProxiesTabState>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProxiesTabState&&const DeepCollectionEquality().equals(other._groups, _groups)&&(identical(other.currentGroupName, currentGroupName) || other.currentGroupName == currentGroupName)&&(identical(other.proxiesSortType, proxiesSortType) || other.proxiesSortType == proxiesSortType)&&(identical(other.proxyCardType, proxyCardType) || other.proxyCardType == proxyCardType)&&(identical(other.sortNum, sortNum) || other.sortNum == sortNum)&&(identical(other.columns, columns) || other.columns == columns));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_groups),currentGroupName,proxiesSortType,proxyCardType,sortNum,columns);\n\n@override\nString toString() {\n  return 'ProxiesTabState(groups: $groups, currentGroupName: $currentGroupName, proxiesSortType: $proxiesSortType, proxyCardType: $proxyCardType, sortNum: $sortNum, columns: $columns)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ProxiesTabStateCopyWith<$Res> implements $ProxiesTabStateCopyWith<$Res> {\n  factory _$ProxiesTabStateCopyWith(_ProxiesTabState value, $Res Function(_ProxiesTabState) _then) = __$ProxiesTabStateCopyWithImpl;\n@override @useResult\n$Res call({\n List<Group> groups, String? currentGroupName, ProxiesSortType proxiesSortType, ProxyCardType proxyCardType, num sortNum, int columns\n});\n\n\n\n\n}\n/// @nodoc\nclass __$ProxiesTabStateCopyWithImpl<$Res>\n    implements _$ProxiesTabStateCopyWith<$Res> {\n  __$ProxiesTabStateCopyWithImpl(this._self, this._then);\n\n  final _ProxiesTabState _self;\n  final $Res Function(_ProxiesTabState) _then;\n\n/// Create a copy of ProxiesTabState\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? groups = null,Object? currentGroupName = freezed,Object? proxiesSortType = null,Object? proxyCardType = null,Object? sortNum = null,Object? columns = null,}) {\n  return _then(_ProxiesTabState(\ngroups: null == groups ? _self._groups : groups // ignore: cast_nullable_to_non_nullable\nas List<Group>,currentGroupName: freezed == currentGroupName ? _self.currentGroupName : currentGroupName // ignore: cast_nullable_to_non_nullable\nas String?,proxiesSortType: null == proxiesSortType ? _self.proxiesSortType : proxiesSortType // ignore: cast_nullable_to_non_nullable\nas ProxiesSortType,proxyCardType: null == proxyCardType ? _self.proxyCardType : proxyCardType // ignore: cast_nullable_to_non_nullable\nas ProxyCardType,sortNum: null == sortNum ? _self.sortNum : sortNum // ignore: cast_nullable_to_non_nullable\nas num,columns: null == columns ? _self.columns : columns // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$ProxyGroupSelectorState {\n\n String? get testUrl; ProxiesSortType get proxiesSortType; ProxyCardType get proxyCardType; num get sortNum; GroupType get groupType; List<Proxy> get proxies; int get columns;\n/// Create a copy of ProxyGroupSelectorState\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ProxyGroupSelectorStateCopyWith<ProxyGroupSelectorState> get copyWith => _$ProxyGroupSelectorStateCopyWithImpl<ProxyGroupSelectorState>(this as ProxyGroupSelectorState, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is ProxyGroupSelectorState&&(identical(other.testUrl, testUrl) || other.testUrl == testUrl)&&(identical(other.proxiesSortType, proxiesSortType) || other.proxiesSortType == proxiesSortType)&&(identical(other.proxyCardType, proxyCardType) || other.proxyCardType == proxyCardType)&&(identical(other.sortNum, sortNum) || other.sortNum == sortNum)&&(identical(other.groupType, groupType) || other.groupType == groupType)&&const DeepCollectionEquality().equals(other.proxies, proxies)&&(identical(other.columns, columns) || other.columns == columns));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,testUrl,proxiesSortType,proxyCardType,sortNum,groupType,const DeepCollectionEquality().hash(proxies),columns);\n\n@override\nString toString() {\n  return 'ProxyGroupSelectorState(testUrl: $testUrl, proxiesSortType: $proxiesSortType, proxyCardType: $proxyCardType, sortNum: $sortNum, groupType: $groupType, proxies: $proxies, columns: $columns)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ProxyGroupSelectorStateCopyWith<$Res>  {\n  factory $ProxyGroupSelectorStateCopyWith(ProxyGroupSelectorState value, $Res Function(ProxyGroupSelectorState) _then) = _$ProxyGroupSelectorStateCopyWithImpl;\n@useResult\n$Res call({\n String? testUrl, ProxiesSortType proxiesSortType, ProxyCardType proxyCardType, num sortNum, GroupType groupType, List<Proxy> proxies, int columns\n});\n\n\n\n\n}\n/// @nodoc\nclass _$ProxyGroupSelectorStateCopyWithImpl<$Res>\n    implements $ProxyGroupSelectorStateCopyWith<$Res> {\n  _$ProxyGroupSelectorStateCopyWithImpl(this._self, this._then);\n\n  final ProxyGroupSelectorState _self;\n  final $Res Function(ProxyGroupSelectorState) _then;\n\n/// Create a copy of ProxyGroupSelectorState\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? testUrl = freezed,Object? proxiesSortType = null,Object? proxyCardType = null,Object? sortNum = null,Object? groupType = null,Object? proxies = null,Object? columns = null,}) {\n  return _then(_self.copyWith(\ntestUrl: freezed == testUrl ? _self.testUrl : testUrl // ignore: cast_nullable_to_non_nullable\nas String?,proxiesSortType: null == proxiesSortType ? _self.proxiesSortType : proxiesSortType // ignore: cast_nullable_to_non_nullable\nas ProxiesSortType,proxyCardType: null == proxyCardType ? _self.proxyCardType : proxyCardType // ignore: cast_nullable_to_non_nullable\nas ProxyCardType,sortNum: null == sortNum ? _self.sortNum : sortNum // ignore: cast_nullable_to_non_nullable\nas num,groupType: null == groupType ? _self.groupType : groupType // ignore: cast_nullable_to_non_nullable\nas GroupType,proxies: null == proxies ? _self.proxies : proxies // ignore: cast_nullable_to_non_nullable\nas List<Proxy>,columns: null == columns ? _self.columns : columns // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [ProxyGroupSelectorState].\nextension ProxyGroupSelectorStatePatterns on ProxyGroupSelectorState {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ProxyGroupSelectorState value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _ProxyGroupSelectorState() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ProxyGroupSelectorState value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ProxyGroupSelectorState():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ProxyGroupSelectorState value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ProxyGroupSelectorState() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String? testUrl,  ProxiesSortType proxiesSortType,  ProxyCardType proxyCardType,  num sortNum,  GroupType groupType,  List<Proxy> proxies,  int columns)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _ProxyGroupSelectorState() when $default != null:\nreturn $default(_that.testUrl,_that.proxiesSortType,_that.proxyCardType,_that.sortNum,_that.groupType,_that.proxies,_that.columns);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String? testUrl,  ProxiesSortType proxiesSortType,  ProxyCardType proxyCardType,  num sortNum,  GroupType groupType,  List<Proxy> proxies,  int columns)  $default,) {final _that = this;\nswitch (_that) {\ncase _ProxyGroupSelectorState():\nreturn $default(_that.testUrl,_that.proxiesSortType,_that.proxyCardType,_that.sortNum,_that.groupType,_that.proxies,_that.columns);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String? testUrl,  ProxiesSortType proxiesSortType,  ProxyCardType proxyCardType,  num sortNum,  GroupType groupType,  List<Proxy> proxies,  int columns)?  $default,) {final _that = this;\nswitch (_that) {\ncase _ProxyGroupSelectorState() when $default != null:\nreturn $default(_that.testUrl,_that.proxiesSortType,_that.proxyCardType,_that.sortNum,_that.groupType,_that.proxies,_that.columns);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _ProxyGroupSelectorState implements ProxyGroupSelectorState {\n  const _ProxyGroupSelectorState({required this.testUrl, required this.proxiesSortType, required this.proxyCardType, required this.sortNum, required this.groupType, required final  List<Proxy> proxies, required this.columns}): _proxies = proxies;\n  \n\n@override final  String? testUrl;\n@override final  ProxiesSortType proxiesSortType;\n@override final  ProxyCardType proxyCardType;\n@override final  num sortNum;\n@override final  GroupType groupType;\n final  List<Proxy> _proxies;\n@override List<Proxy> get proxies {\n  if (_proxies is EqualUnmodifiableListView) return _proxies;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_proxies);\n}\n\n@override final  int columns;\n\n/// Create a copy of ProxyGroupSelectorState\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ProxyGroupSelectorStateCopyWith<_ProxyGroupSelectorState> get copyWith => __$ProxyGroupSelectorStateCopyWithImpl<_ProxyGroupSelectorState>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProxyGroupSelectorState&&(identical(other.testUrl, testUrl) || other.testUrl == testUrl)&&(identical(other.proxiesSortType, proxiesSortType) || other.proxiesSortType == proxiesSortType)&&(identical(other.proxyCardType, proxyCardType) || other.proxyCardType == proxyCardType)&&(identical(other.sortNum, sortNum) || other.sortNum == sortNum)&&(identical(other.groupType, groupType) || other.groupType == groupType)&&const DeepCollectionEquality().equals(other._proxies, _proxies)&&(identical(other.columns, columns) || other.columns == columns));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,testUrl,proxiesSortType,proxyCardType,sortNum,groupType,const DeepCollectionEquality().hash(_proxies),columns);\n\n@override\nString toString() {\n  return 'ProxyGroupSelectorState(testUrl: $testUrl, proxiesSortType: $proxiesSortType, proxyCardType: $proxyCardType, sortNum: $sortNum, groupType: $groupType, proxies: $proxies, columns: $columns)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ProxyGroupSelectorStateCopyWith<$Res> implements $ProxyGroupSelectorStateCopyWith<$Res> {\n  factory _$ProxyGroupSelectorStateCopyWith(_ProxyGroupSelectorState value, $Res Function(_ProxyGroupSelectorState) _then) = __$ProxyGroupSelectorStateCopyWithImpl;\n@override @useResult\n$Res call({\n String? testUrl, ProxiesSortType proxiesSortType, ProxyCardType proxyCardType, num sortNum, GroupType groupType, List<Proxy> proxies, int columns\n});\n\n\n\n\n}\n/// @nodoc\nclass __$ProxyGroupSelectorStateCopyWithImpl<$Res>\n    implements _$ProxyGroupSelectorStateCopyWith<$Res> {\n  __$ProxyGroupSelectorStateCopyWithImpl(this._self, this._then);\n\n  final _ProxyGroupSelectorState _self;\n  final $Res Function(_ProxyGroupSelectorState) _then;\n\n/// Create a copy of ProxyGroupSelectorState\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? testUrl = freezed,Object? proxiesSortType = null,Object? proxyCardType = null,Object? sortNum = null,Object? groupType = null,Object? proxies = null,Object? columns = null,}) {\n  return _then(_ProxyGroupSelectorState(\ntestUrl: freezed == testUrl ? _self.testUrl : testUrl // ignore: cast_nullable_to_non_nullable\nas String?,proxiesSortType: null == proxiesSortType ? _self.proxiesSortType : proxiesSortType // ignore: cast_nullable_to_non_nullable\nas ProxiesSortType,proxyCardType: null == proxyCardType ? _self.proxyCardType : proxyCardType // ignore: cast_nullable_to_non_nullable\nas ProxyCardType,sortNum: null == sortNum ? _self.sortNum : sortNum // ignore: cast_nullable_to_non_nullable\nas num,groupType: null == groupType ? _self.groupType : groupType // ignore: cast_nullable_to_non_nullable\nas GroupType,proxies: null == proxies ? _self._proxies : proxies // ignore: cast_nullable_to_non_nullable\nas List<Proxy>,columns: null == columns ? _self.columns : columns // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$MoreToolsSelectorState {\n\n List<NavigationItem> get navigationItems;\n/// Create a copy of MoreToolsSelectorState\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$MoreToolsSelectorStateCopyWith<MoreToolsSelectorState> get copyWith => _$MoreToolsSelectorStateCopyWithImpl<MoreToolsSelectorState>(this as MoreToolsSelectorState, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is MoreToolsSelectorState&&const DeepCollectionEquality().equals(other.navigationItems, navigationItems));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(navigationItems));\n\n@override\nString toString() {\n  return 'MoreToolsSelectorState(navigationItems: $navigationItems)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $MoreToolsSelectorStateCopyWith<$Res>  {\n  factory $MoreToolsSelectorStateCopyWith(MoreToolsSelectorState value, $Res Function(MoreToolsSelectorState) _then) = _$MoreToolsSelectorStateCopyWithImpl;\n@useResult\n$Res call({\n List<NavigationItem> navigationItems\n});\n\n\n\n\n}\n/// @nodoc\nclass _$MoreToolsSelectorStateCopyWithImpl<$Res>\n    implements $MoreToolsSelectorStateCopyWith<$Res> {\n  _$MoreToolsSelectorStateCopyWithImpl(this._self, this._then);\n\n  final MoreToolsSelectorState _self;\n  final $Res Function(MoreToolsSelectorState) _then;\n\n/// Create a copy of MoreToolsSelectorState\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? navigationItems = null,}) {\n  return _then(_self.copyWith(\nnavigationItems: null == navigationItems ? _self.navigationItems : navigationItems // ignore: cast_nullable_to_non_nullable\nas List<NavigationItem>,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [MoreToolsSelectorState].\nextension MoreToolsSelectorStatePatterns on MoreToolsSelectorState {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _MoreToolsSelectorState value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _MoreToolsSelectorState() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _MoreToolsSelectorState value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _MoreToolsSelectorState():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _MoreToolsSelectorState value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _MoreToolsSelectorState() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<NavigationItem> navigationItems)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _MoreToolsSelectorState() when $default != null:\nreturn $default(_that.navigationItems);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<NavigationItem> navigationItems)  $default,) {final _that = this;\nswitch (_that) {\ncase _MoreToolsSelectorState():\nreturn $default(_that.navigationItems);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<NavigationItem> navigationItems)?  $default,) {final _that = this;\nswitch (_that) {\ncase _MoreToolsSelectorState() when $default != null:\nreturn $default(_that.navigationItems);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _MoreToolsSelectorState implements MoreToolsSelectorState {\n  const _MoreToolsSelectorState({required final  List<NavigationItem> navigationItems}): _navigationItems = navigationItems;\n  \n\n final  List<NavigationItem> _navigationItems;\n@override List<NavigationItem> get navigationItems {\n  if (_navigationItems is EqualUnmodifiableListView) return _navigationItems;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_navigationItems);\n}\n\n\n/// Create a copy of MoreToolsSelectorState\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$MoreToolsSelectorStateCopyWith<_MoreToolsSelectorState> get copyWith => __$MoreToolsSelectorStateCopyWithImpl<_MoreToolsSelectorState>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _MoreToolsSelectorState&&const DeepCollectionEquality().equals(other._navigationItems, _navigationItems));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_navigationItems));\n\n@override\nString toString() {\n  return 'MoreToolsSelectorState(navigationItems: $navigationItems)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$MoreToolsSelectorStateCopyWith<$Res> implements $MoreToolsSelectorStateCopyWith<$Res> {\n  factory _$MoreToolsSelectorStateCopyWith(_MoreToolsSelectorState value, $Res Function(_MoreToolsSelectorState) _then) = __$MoreToolsSelectorStateCopyWithImpl;\n@override @useResult\n$Res call({\n List<NavigationItem> navigationItems\n});\n\n\n\n\n}\n/// @nodoc\nclass __$MoreToolsSelectorStateCopyWithImpl<$Res>\n    implements _$MoreToolsSelectorStateCopyWith<$Res> {\n  __$MoreToolsSelectorStateCopyWithImpl(this._self, this._then);\n\n  final _MoreToolsSelectorState _self;\n  final $Res Function(_MoreToolsSelectorState) _then;\n\n/// Create a copy of MoreToolsSelectorState\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? navigationItems = null,}) {\n  return _then(_MoreToolsSelectorState(\nnavigationItems: null == navigationItems ? _self._navigationItems : navigationItems // ignore: cast_nullable_to_non_nullable\nas List<NavigationItem>,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$PackageListSelectorState {\n\n List<Package> get packages; AccessControl get accessControl;\n/// Create a copy of PackageListSelectorState\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$PackageListSelectorStateCopyWith<PackageListSelectorState> get copyWith => _$PackageListSelectorStateCopyWithImpl<PackageListSelectorState>(this as PackageListSelectorState, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is PackageListSelectorState&&const DeepCollectionEquality().equals(other.packages, packages)&&(identical(other.accessControl, accessControl) || other.accessControl == accessControl));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(packages),accessControl);\n\n@override\nString toString() {\n  return 'PackageListSelectorState(packages: $packages, accessControl: $accessControl)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $PackageListSelectorStateCopyWith<$Res>  {\n  factory $PackageListSelectorStateCopyWith(PackageListSelectorState value, $Res Function(PackageListSelectorState) _then) = _$PackageListSelectorStateCopyWithImpl;\n@useResult\n$Res call({\n List<Package> packages, AccessControl accessControl\n});\n\n\n$AccessControlCopyWith<$Res> get accessControl;\n\n}\n/// @nodoc\nclass _$PackageListSelectorStateCopyWithImpl<$Res>\n    implements $PackageListSelectorStateCopyWith<$Res> {\n  _$PackageListSelectorStateCopyWithImpl(this._self, this._then);\n\n  final PackageListSelectorState _self;\n  final $Res Function(PackageListSelectorState) _then;\n\n/// Create a copy of PackageListSelectorState\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? packages = null,Object? accessControl = null,}) {\n  return _then(_self.copyWith(\npackages: null == packages ? _self.packages : packages // ignore: cast_nullable_to_non_nullable\nas List<Package>,accessControl: null == accessControl ? _self.accessControl : accessControl // ignore: cast_nullable_to_non_nullable\nas AccessControl,\n  ));\n}\n/// Create a copy of PackageListSelectorState\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$AccessControlCopyWith<$Res> get accessControl {\n  \n  return $AccessControlCopyWith<$Res>(_self.accessControl, (value) {\n    return _then(_self.copyWith(accessControl: value));\n  });\n}\n}\n\n\n/// Adds pattern-matching-related methods to [PackageListSelectorState].\nextension PackageListSelectorStatePatterns on PackageListSelectorState {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _PackageListSelectorState value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _PackageListSelectorState() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _PackageListSelectorState value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _PackageListSelectorState():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _PackageListSelectorState value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _PackageListSelectorState() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<Package> packages,  AccessControl accessControl)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _PackageListSelectorState() when $default != null:\nreturn $default(_that.packages,_that.accessControl);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<Package> packages,  AccessControl accessControl)  $default,) {final _that = this;\nswitch (_that) {\ncase _PackageListSelectorState():\nreturn $default(_that.packages,_that.accessControl);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<Package> packages,  AccessControl accessControl)?  $default,) {final _that = this;\nswitch (_that) {\ncase _PackageListSelectorState() when $default != null:\nreturn $default(_that.packages,_that.accessControl);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _PackageListSelectorState implements PackageListSelectorState {\n  const _PackageListSelectorState({required final  List<Package> packages, required this.accessControl}): _packages = packages;\n  \n\n final  List<Package> _packages;\n@override List<Package> get packages {\n  if (_packages is EqualUnmodifiableListView) return _packages;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_packages);\n}\n\n@override final  AccessControl accessControl;\n\n/// Create a copy of PackageListSelectorState\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$PackageListSelectorStateCopyWith<_PackageListSelectorState> get copyWith => __$PackageListSelectorStateCopyWithImpl<_PackageListSelectorState>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _PackageListSelectorState&&const DeepCollectionEquality().equals(other._packages, _packages)&&(identical(other.accessControl, accessControl) || other.accessControl == accessControl));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_packages),accessControl);\n\n@override\nString toString() {\n  return 'PackageListSelectorState(packages: $packages, accessControl: $accessControl)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$PackageListSelectorStateCopyWith<$Res> implements $PackageListSelectorStateCopyWith<$Res> {\n  factory _$PackageListSelectorStateCopyWith(_PackageListSelectorState value, $Res Function(_PackageListSelectorState) _then) = __$PackageListSelectorStateCopyWithImpl;\n@override @useResult\n$Res call({\n List<Package> packages, AccessControl accessControl\n});\n\n\n@override $AccessControlCopyWith<$Res> get accessControl;\n\n}\n/// @nodoc\nclass __$PackageListSelectorStateCopyWithImpl<$Res>\n    implements _$PackageListSelectorStateCopyWith<$Res> {\n  __$PackageListSelectorStateCopyWithImpl(this._self, this._then);\n\n  final _PackageListSelectorState _self;\n  final $Res Function(_PackageListSelectorState) _then;\n\n/// Create a copy of PackageListSelectorState\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? packages = null,Object? accessControl = null,}) {\n  return _then(_PackageListSelectorState(\npackages: null == packages ? _self._packages : packages // ignore: cast_nullable_to_non_nullable\nas List<Package>,accessControl: null == accessControl ? _self.accessControl : accessControl // ignore: cast_nullable_to_non_nullable\nas AccessControl,\n  ));\n}\n\n/// Create a copy of PackageListSelectorState\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$AccessControlCopyWith<$Res> get accessControl {\n  \n  return $AccessControlCopyWith<$Res>(_self.accessControl, (value) {\n    return _then(_self.copyWith(accessControl: value));\n  });\n}\n}\n\n/// @nodoc\nmixin _$ProxiesListHeaderSelectorState {\n\n double get offset; int get currentIndex;\n/// Create a copy of ProxiesListHeaderSelectorState\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ProxiesListHeaderSelectorStateCopyWith<ProxiesListHeaderSelectorState> get copyWith => _$ProxiesListHeaderSelectorStateCopyWithImpl<ProxiesListHeaderSelectorState>(this as ProxiesListHeaderSelectorState, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is ProxiesListHeaderSelectorState&&(identical(other.offset, offset) || other.offset == offset)&&(identical(other.currentIndex, currentIndex) || other.currentIndex == currentIndex));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,offset,currentIndex);\n\n@override\nString toString() {\n  return 'ProxiesListHeaderSelectorState(offset: $offset, currentIndex: $currentIndex)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ProxiesListHeaderSelectorStateCopyWith<$Res>  {\n  factory $ProxiesListHeaderSelectorStateCopyWith(ProxiesListHeaderSelectorState value, $Res Function(ProxiesListHeaderSelectorState) _then) = _$ProxiesListHeaderSelectorStateCopyWithImpl;\n@useResult\n$Res call({\n double offset, int currentIndex\n});\n\n\n\n\n}\n/// @nodoc\nclass _$ProxiesListHeaderSelectorStateCopyWithImpl<$Res>\n    implements $ProxiesListHeaderSelectorStateCopyWith<$Res> {\n  _$ProxiesListHeaderSelectorStateCopyWithImpl(this._self, this._then);\n\n  final ProxiesListHeaderSelectorState _self;\n  final $Res Function(ProxiesListHeaderSelectorState) _then;\n\n/// Create a copy of ProxiesListHeaderSelectorState\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? offset = null,Object? currentIndex = null,}) {\n  return _then(_self.copyWith(\noffset: null == offset ? _self.offset : offset // ignore: cast_nullable_to_non_nullable\nas double,currentIndex: null == currentIndex ? _self.currentIndex : currentIndex // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [ProxiesListHeaderSelectorState].\nextension ProxiesListHeaderSelectorStatePatterns on ProxiesListHeaderSelectorState {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ProxiesListHeaderSelectorState value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _ProxiesListHeaderSelectorState() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ProxiesListHeaderSelectorState value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ProxiesListHeaderSelectorState():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ProxiesListHeaderSelectorState value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ProxiesListHeaderSelectorState() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( double offset,  int currentIndex)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _ProxiesListHeaderSelectorState() when $default != null:\nreturn $default(_that.offset,_that.currentIndex);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( double offset,  int currentIndex)  $default,) {final _that = this;\nswitch (_that) {\ncase _ProxiesListHeaderSelectorState():\nreturn $default(_that.offset,_that.currentIndex);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( double offset,  int currentIndex)?  $default,) {final _that = this;\nswitch (_that) {\ncase _ProxiesListHeaderSelectorState() when $default != null:\nreturn $default(_that.offset,_that.currentIndex);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _ProxiesListHeaderSelectorState implements ProxiesListHeaderSelectorState {\n  const _ProxiesListHeaderSelectorState({required this.offset, required this.currentIndex});\n  \n\n@override final  double offset;\n@override final  int currentIndex;\n\n/// Create a copy of ProxiesListHeaderSelectorState\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ProxiesListHeaderSelectorStateCopyWith<_ProxiesListHeaderSelectorState> get copyWith => __$ProxiesListHeaderSelectorStateCopyWithImpl<_ProxiesListHeaderSelectorState>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProxiesListHeaderSelectorState&&(identical(other.offset, offset) || other.offset == offset)&&(identical(other.currentIndex, currentIndex) || other.currentIndex == currentIndex));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,offset,currentIndex);\n\n@override\nString toString() {\n  return 'ProxiesListHeaderSelectorState(offset: $offset, currentIndex: $currentIndex)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ProxiesListHeaderSelectorStateCopyWith<$Res> implements $ProxiesListHeaderSelectorStateCopyWith<$Res> {\n  factory _$ProxiesListHeaderSelectorStateCopyWith(_ProxiesListHeaderSelectorState value, $Res Function(_ProxiesListHeaderSelectorState) _then) = __$ProxiesListHeaderSelectorStateCopyWithImpl;\n@override @useResult\n$Res call({\n double offset, int currentIndex\n});\n\n\n\n\n}\n/// @nodoc\nclass __$ProxiesListHeaderSelectorStateCopyWithImpl<$Res>\n    implements _$ProxiesListHeaderSelectorStateCopyWith<$Res> {\n  __$ProxiesListHeaderSelectorStateCopyWithImpl(this._self, this._then);\n\n  final _ProxiesListHeaderSelectorState _self;\n  final $Res Function(_ProxiesListHeaderSelectorState) _then;\n\n/// Create a copy of ProxiesListHeaderSelectorState\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? offset = null,Object? currentIndex = null,}) {\n  return _then(_ProxiesListHeaderSelectorState(\noffset: null == offset ? _self.offset : offset // ignore: cast_nullable_to_non_nullable\nas double,currentIndex: null == currentIndex ? _self.currentIndex : currentIndex // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$ProxiesActionsState {\n\n PageLabel get pageLabel; ProxiesType get type; bool get hasProviders;\n/// Create a copy of ProxiesActionsState\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ProxiesActionsStateCopyWith<ProxiesActionsState> get copyWith => _$ProxiesActionsStateCopyWithImpl<ProxiesActionsState>(this as ProxiesActionsState, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is ProxiesActionsState&&(identical(other.pageLabel, pageLabel) || other.pageLabel == pageLabel)&&(identical(other.type, type) || other.type == type)&&(identical(other.hasProviders, hasProviders) || other.hasProviders == hasProviders));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,pageLabel,type,hasProviders);\n\n@override\nString toString() {\n  return 'ProxiesActionsState(pageLabel: $pageLabel, type: $type, hasProviders: $hasProviders)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ProxiesActionsStateCopyWith<$Res>  {\n  factory $ProxiesActionsStateCopyWith(ProxiesActionsState value, $Res Function(ProxiesActionsState) _then) = _$ProxiesActionsStateCopyWithImpl;\n@useResult\n$Res call({\n PageLabel pageLabel, ProxiesType type, bool hasProviders\n});\n\n\n\n\n}\n/// @nodoc\nclass _$ProxiesActionsStateCopyWithImpl<$Res>\n    implements $ProxiesActionsStateCopyWith<$Res> {\n  _$ProxiesActionsStateCopyWithImpl(this._self, this._then);\n\n  final ProxiesActionsState _self;\n  final $Res Function(ProxiesActionsState) _then;\n\n/// Create a copy of ProxiesActionsState\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? pageLabel = null,Object? type = null,Object? hasProviders = null,}) {\n  return _then(_self.copyWith(\npageLabel: null == pageLabel ? _self.pageLabel : pageLabel // ignore: cast_nullable_to_non_nullable\nas PageLabel,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable\nas ProxiesType,hasProviders: null == hasProviders ? _self.hasProviders : hasProviders // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [ProxiesActionsState].\nextension ProxiesActionsStatePatterns on ProxiesActionsState {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ProxiesActionsState value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _ProxiesActionsState() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ProxiesActionsState value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ProxiesActionsState():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ProxiesActionsState value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ProxiesActionsState() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( PageLabel pageLabel,  ProxiesType type,  bool hasProviders)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _ProxiesActionsState() when $default != null:\nreturn $default(_that.pageLabel,_that.type,_that.hasProviders);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( PageLabel pageLabel,  ProxiesType type,  bool hasProviders)  $default,) {final _that = this;\nswitch (_that) {\ncase _ProxiesActionsState():\nreturn $default(_that.pageLabel,_that.type,_that.hasProviders);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( PageLabel pageLabel,  ProxiesType type,  bool hasProviders)?  $default,) {final _that = this;\nswitch (_that) {\ncase _ProxiesActionsState() when $default != null:\nreturn $default(_that.pageLabel,_that.type,_that.hasProviders);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _ProxiesActionsState implements ProxiesActionsState {\n  const _ProxiesActionsState({required this.pageLabel, required this.type, required this.hasProviders});\n  \n\n@override final  PageLabel pageLabel;\n@override final  ProxiesType type;\n@override final  bool hasProviders;\n\n/// Create a copy of ProxiesActionsState\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ProxiesActionsStateCopyWith<_ProxiesActionsState> get copyWith => __$ProxiesActionsStateCopyWithImpl<_ProxiesActionsState>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProxiesActionsState&&(identical(other.pageLabel, pageLabel) || other.pageLabel == pageLabel)&&(identical(other.type, type) || other.type == type)&&(identical(other.hasProviders, hasProviders) || other.hasProviders == hasProviders));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,pageLabel,type,hasProviders);\n\n@override\nString toString() {\n  return 'ProxiesActionsState(pageLabel: $pageLabel, type: $type, hasProviders: $hasProviders)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ProxiesActionsStateCopyWith<$Res> implements $ProxiesActionsStateCopyWith<$Res> {\n  factory _$ProxiesActionsStateCopyWith(_ProxiesActionsState value, $Res Function(_ProxiesActionsState) _then) = __$ProxiesActionsStateCopyWithImpl;\n@override @useResult\n$Res call({\n PageLabel pageLabel, ProxiesType type, bool hasProviders\n});\n\n\n\n\n}\n/// @nodoc\nclass __$ProxiesActionsStateCopyWithImpl<$Res>\n    implements _$ProxiesActionsStateCopyWith<$Res> {\n  __$ProxiesActionsStateCopyWithImpl(this._self, this._then);\n\n  final _ProxiesActionsState _self;\n  final $Res Function(_ProxiesActionsState) _then;\n\n/// Create a copy of ProxiesActionsState\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? pageLabel = null,Object? type = null,Object? hasProviders = null,}) {\n  return _then(_ProxiesActionsState(\npageLabel: null == pageLabel ? _self.pageLabel : pageLabel // ignore: cast_nullable_to_non_nullable\nas PageLabel,type: null == type ? _self.type : type // ignore: cast_nullable_to_non_nullable\nas ProxiesType,hasProviders: null == hasProviders ? _self.hasProviders : hasProviders // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$ProxyState {\n\n bool get isStart; bool get systemProxy; List<String> get bassDomain; int get port;\n/// Create a copy of ProxyState\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ProxyStateCopyWith<ProxyState> get copyWith => _$ProxyStateCopyWithImpl<ProxyState>(this as ProxyState, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is ProxyState&&(identical(other.isStart, isStart) || other.isStart == isStart)&&(identical(other.systemProxy, systemProxy) || other.systemProxy == systemProxy)&&const DeepCollectionEquality().equals(other.bassDomain, bassDomain)&&(identical(other.port, port) || other.port == port));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,isStart,systemProxy,const DeepCollectionEquality().hash(bassDomain),port);\n\n@override\nString toString() {\n  return 'ProxyState(isStart: $isStart, systemProxy: $systemProxy, bassDomain: $bassDomain, port: $port)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ProxyStateCopyWith<$Res>  {\n  factory $ProxyStateCopyWith(ProxyState value, $Res Function(ProxyState) _then) = _$ProxyStateCopyWithImpl;\n@useResult\n$Res call({\n bool isStart, bool systemProxy, List<String> bassDomain, int port\n});\n\n\n\n\n}\n/// @nodoc\nclass _$ProxyStateCopyWithImpl<$Res>\n    implements $ProxyStateCopyWith<$Res> {\n  _$ProxyStateCopyWithImpl(this._self, this._then);\n\n  final ProxyState _self;\n  final $Res Function(ProxyState) _then;\n\n/// Create a copy of ProxyState\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? isStart = null,Object? systemProxy = null,Object? bassDomain = null,Object? port = null,}) {\n  return _then(_self.copyWith(\nisStart: null == isStart ? _self.isStart : isStart // ignore: cast_nullable_to_non_nullable\nas bool,systemProxy: null == systemProxy ? _self.systemProxy : systemProxy // ignore: cast_nullable_to_non_nullable\nas bool,bassDomain: null == bassDomain ? _self.bassDomain : bassDomain // ignore: cast_nullable_to_non_nullable\nas List<String>,port: null == port ? _self.port : port // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [ProxyState].\nextension ProxyStatePatterns on ProxyState {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ProxyState value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _ProxyState() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ProxyState value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ProxyState():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ProxyState value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ProxyState() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool isStart,  bool systemProxy,  List<String> bassDomain,  int port)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _ProxyState() when $default != null:\nreturn $default(_that.isStart,_that.systemProxy,_that.bassDomain,_that.port);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool isStart,  bool systemProxy,  List<String> bassDomain,  int port)  $default,) {final _that = this;\nswitch (_that) {\ncase _ProxyState():\nreturn $default(_that.isStart,_that.systemProxy,_that.bassDomain,_that.port);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool isStart,  bool systemProxy,  List<String> bassDomain,  int port)?  $default,) {final _that = this;\nswitch (_that) {\ncase _ProxyState() when $default != null:\nreturn $default(_that.isStart,_that.systemProxy,_that.bassDomain,_that.port);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _ProxyState implements ProxyState {\n  const _ProxyState({required this.isStart, required this.systemProxy, required final  List<String> bassDomain, required this.port}): _bassDomain = bassDomain;\n  \n\n@override final  bool isStart;\n@override final  bool systemProxy;\n final  List<String> _bassDomain;\n@override List<String> get bassDomain {\n  if (_bassDomain is EqualUnmodifiableListView) return _bassDomain;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_bassDomain);\n}\n\n@override final  int port;\n\n/// Create a copy of ProxyState\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ProxyStateCopyWith<_ProxyState> get copyWith => __$ProxyStateCopyWithImpl<_ProxyState>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProxyState&&(identical(other.isStart, isStart) || other.isStart == isStart)&&(identical(other.systemProxy, systemProxy) || other.systemProxy == systemProxy)&&const DeepCollectionEquality().equals(other._bassDomain, _bassDomain)&&(identical(other.port, port) || other.port == port));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,isStart,systemProxy,const DeepCollectionEquality().hash(_bassDomain),port);\n\n@override\nString toString() {\n  return 'ProxyState(isStart: $isStart, systemProxy: $systemProxy, bassDomain: $bassDomain, port: $port)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ProxyStateCopyWith<$Res> implements $ProxyStateCopyWith<$Res> {\n  factory _$ProxyStateCopyWith(_ProxyState value, $Res Function(_ProxyState) _then) = __$ProxyStateCopyWithImpl;\n@override @useResult\n$Res call({\n bool isStart, bool systemProxy, List<String> bassDomain, int port\n});\n\n\n\n\n}\n/// @nodoc\nclass __$ProxyStateCopyWithImpl<$Res>\n    implements _$ProxyStateCopyWith<$Res> {\n  __$ProxyStateCopyWithImpl(this._self, this._then);\n\n  final _ProxyState _self;\n  final $Res Function(_ProxyState) _then;\n\n/// Create a copy of ProxyState\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? isStart = null,Object? systemProxy = null,Object? bassDomain = null,Object? port = null,}) {\n  return _then(_ProxyState(\nisStart: null == isStart ? _self.isStart : isStart // ignore: cast_nullable_to_non_nullable\nas bool,systemProxy: null == systemProxy ? _self.systemProxy : systemProxy // ignore: cast_nullable_to_non_nullable\nas bool,bassDomain: null == bassDomain ? _self._bassDomain : bassDomain // ignore: cast_nullable_to_non_nullable\nas List<String>,port: null == port ? _self.port : port // ignore: cast_nullable_to_non_nullable\nas int,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$ClashConfigState {\n\n bool get overrideDns; ClashConfig get clashConfig; OverrideData get overrideData; bool get bypassPrivateRoute;\n/// Create a copy of ClashConfigState\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ClashConfigStateCopyWith<ClashConfigState> get copyWith => _$ClashConfigStateCopyWithImpl<ClashConfigState>(this as ClashConfigState, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is ClashConfigState&&(identical(other.overrideDns, overrideDns) || other.overrideDns == overrideDns)&&(identical(other.clashConfig, clashConfig) || other.clashConfig == clashConfig)&&(identical(other.overrideData, overrideData) || other.overrideData == overrideData)&&(identical(other.bypassPrivateRoute, bypassPrivateRoute) || other.bypassPrivateRoute == bypassPrivateRoute));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,overrideDns,clashConfig,overrideData,bypassPrivateRoute);\n\n@override\nString toString() {\n  return 'ClashConfigState(overrideDns: $overrideDns, clashConfig: $clashConfig, overrideData: $overrideData, bypassPrivateRoute: $bypassPrivateRoute)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ClashConfigStateCopyWith<$Res>  {\n  factory $ClashConfigStateCopyWith(ClashConfigState value, $Res Function(ClashConfigState) _then) = _$ClashConfigStateCopyWithImpl;\n@useResult\n$Res call({\n bool overrideDns, ClashConfig clashConfig, OverrideData overrideData, bool bypassPrivateRoute\n});\n\n\n$ClashConfigCopyWith<$Res> get clashConfig;$OverrideDataCopyWith<$Res> get overrideData;\n\n}\n/// @nodoc\nclass _$ClashConfigStateCopyWithImpl<$Res>\n    implements $ClashConfigStateCopyWith<$Res> {\n  _$ClashConfigStateCopyWithImpl(this._self, this._then);\n\n  final ClashConfigState _self;\n  final $Res Function(ClashConfigState) _then;\n\n/// Create a copy of ClashConfigState\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? overrideDns = null,Object? clashConfig = null,Object? overrideData = null,Object? bypassPrivateRoute = null,}) {\n  return _then(_self.copyWith(\noverrideDns: null == overrideDns ? _self.overrideDns : overrideDns // ignore: cast_nullable_to_non_nullable\nas bool,clashConfig: null == clashConfig ? _self.clashConfig : clashConfig // ignore: cast_nullable_to_non_nullable\nas ClashConfig,overrideData: null == overrideData ? _self.overrideData : overrideData // ignore: cast_nullable_to_non_nullable\nas OverrideData,bypassPrivateRoute: null == bypassPrivateRoute ? _self.bypassPrivateRoute : bypassPrivateRoute // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n/// Create a copy of ClashConfigState\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$ClashConfigCopyWith<$Res> get clashConfig {\n  \n  return $ClashConfigCopyWith<$Res>(_self.clashConfig, (value) {\n    return _then(_self.copyWith(clashConfig: value));\n  });\n}/// Create a copy of ClashConfigState\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$OverrideDataCopyWith<$Res> get overrideData {\n  \n  return $OverrideDataCopyWith<$Res>(_self.overrideData, (value) {\n    return _then(_self.copyWith(overrideData: value));\n  });\n}\n}\n\n\n/// Adds pattern-matching-related methods to [ClashConfigState].\nextension ClashConfigStatePatterns on ClashConfigState {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ClashConfigState value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _ClashConfigState() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ClashConfigState value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ClashConfigState():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ClashConfigState value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ClashConfigState() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool overrideDns,  ClashConfig clashConfig,  OverrideData overrideData,  bool bypassPrivateRoute)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _ClashConfigState() when $default != null:\nreturn $default(_that.overrideDns,_that.clashConfig,_that.overrideData,_that.bypassPrivateRoute);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool overrideDns,  ClashConfig clashConfig,  OverrideData overrideData,  bool bypassPrivateRoute)  $default,) {final _that = this;\nswitch (_that) {\ncase _ClashConfigState():\nreturn $default(_that.overrideDns,_that.clashConfig,_that.overrideData,_that.bypassPrivateRoute);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool overrideDns,  ClashConfig clashConfig,  OverrideData overrideData,  bool bypassPrivateRoute)?  $default,) {final _that = this;\nswitch (_that) {\ncase _ClashConfigState() when $default != null:\nreturn $default(_that.overrideDns,_that.clashConfig,_that.overrideData,_that.bypassPrivateRoute);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _ClashConfigState implements ClashConfigState {\n  const _ClashConfigState({required this.overrideDns, required this.clashConfig, required this.overrideData, required this.bypassPrivateRoute});\n  \n\n@override final  bool overrideDns;\n@override final  ClashConfig clashConfig;\n@override final  OverrideData overrideData;\n@override final  bool bypassPrivateRoute;\n\n/// Create a copy of ClashConfigState\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ClashConfigStateCopyWith<_ClashConfigState> get copyWith => __$ClashConfigStateCopyWithImpl<_ClashConfigState>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _ClashConfigState&&(identical(other.overrideDns, overrideDns) || other.overrideDns == overrideDns)&&(identical(other.clashConfig, clashConfig) || other.clashConfig == clashConfig)&&(identical(other.overrideData, overrideData) || other.overrideData == overrideData)&&(identical(other.bypassPrivateRoute, bypassPrivateRoute) || other.bypassPrivateRoute == bypassPrivateRoute));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,overrideDns,clashConfig,overrideData,bypassPrivateRoute);\n\n@override\nString toString() {\n  return 'ClashConfigState(overrideDns: $overrideDns, clashConfig: $clashConfig, overrideData: $overrideData, bypassPrivateRoute: $bypassPrivateRoute)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ClashConfigStateCopyWith<$Res> implements $ClashConfigStateCopyWith<$Res> {\n  factory _$ClashConfigStateCopyWith(_ClashConfigState value, $Res Function(_ClashConfigState) _then) = __$ClashConfigStateCopyWithImpl;\n@override @useResult\n$Res call({\n bool overrideDns, ClashConfig clashConfig, OverrideData overrideData, bool bypassPrivateRoute\n});\n\n\n@override $ClashConfigCopyWith<$Res> get clashConfig;@override $OverrideDataCopyWith<$Res> get overrideData;\n\n}\n/// @nodoc\nclass __$ClashConfigStateCopyWithImpl<$Res>\n    implements _$ClashConfigStateCopyWith<$Res> {\n  __$ClashConfigStateCopyWithImpl(this._self, this._then);\n\n  final _ClashConfigState _self;\n  final $Res Function(_ClashConfigState) _then;\n\n/// Create a copy of ClashConfigState\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? overrideDns = null,Object? clashConfig = null,Object? overrideData = null,Object? bypassPrivateRoute = null,}) {\n  return _then(_ClashConfigState(\noverrideDns: null == overrideDns ? _self.overrideDns : overrideDns // ignore: cast_nullable_to_non_nullable\nas bool,clashConfig: null == clashConfig ? _self.clashConfig : clashConfig // ignore: cast_nullable_to_non_nullable\nas ClashConfig,overrideData: null == overrideData ? _self.overrideData : overrideData // ignore: cast_nullable_to_non_nullable\nas OverrideData,bypassPrivateRoute: null == bypassPrivateRoute ? _self.bypassPrivateRoute : bypassPrivateRoute // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n/// Create a copy of ClashConfigState\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$ClashConfigCopyWith<$Res> get clashConfig {\n  \n  return $ClashConfigCopyWith<$Res>(_self.clashConfig, (value) {\n    return _then(_self.copyWith(clashConfig: value));\n  });\n}/// Create a copy of ClashConfigState\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$OverrideDataCopyWith<$Res> get overrideData {\n  \n  return $OverrideDataCopyWith<$Res>(_self.overrideData, (value) {\n    return _then(_self.copyWith(overrideData: value));\n  });\n}\n}\n\n/// @nodoc\nmixin _$DashboardState {\n\n List<DashboardWidget> get dashboardWidgets; double get viewWidth;\n/// Create a copy of DashboardState\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$DashboardStateCopyWith<DashboardState> get copyWith => _$DashboardStateCopyWithImpl<DashboardState>(this as DashboardState, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is DashboardState&&const DeepCollectionEquality().equals(other.dashboardWidgets, dashboardWidgets)&&(identical(other.viewWidth, viewWidth) || other.viewWidth == viewWidth));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(dashboardWidgets),viewWidth);\n\n@override\nString toString() {\n  return 'DashboardState(dashboardWidgets: $dashboardWidgets, viewWidth: $viewWidth)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $DashboardStateCopyWith<$Res>  {\n  factory $DashboardStateCopyWith(DashboardState value, $Res Function(DashboardState) _then) = _$DashboardStateCopyWithImpl;\n@useResult\n$Res call({\n List<DashboardWidget> dashboardWidgets, double viewWidth\n});\n\n\n\n\n}\n/// @nodoc\nclass _$DashboardStateCopyWithImpl<$Res>\n    implements $DashboardStateCopyWith<$Res> {\n  _$DashboardStateCopyWithImpl(this._self, this._then);\n\n  final DashboardState _self;\n  final $Res Function(DashboardState) _then;\n\n/// Create a copy of DashboardState\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? dashboardWidgets = null,Object? viewWidth = null,}) {\n  return _then(_self.copyWith(\ndashboardWidgets: null == dashboardWidgets ? _self.dashboardWidgets : dashboardWidgets // ignore: cast_nullable_to_non_nullable\nas List<DashboardWidget>,viewWidth: null == viewWidth ? _self.viewWidth : viewWidth // ignore: cast_nullable_to_non_nullable\nas double,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [DashboardState].\nextension DashboardStatePatterns on DashboardState {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _DashboardState value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _DashboardState() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _DashboardState value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _DashboardState():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _DashboardState value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _DashboardState() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<DashboardWidget> dashboardWidgets,  double viewWidth)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _DashboardState() when $default != null:\nreturn $default(_that.dashboardWidgets,_that.viewWidth);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<DashboardWidget> dashboardWidgets,  double viewWidth)  $default,) {final _that = this;\nswitch (_that) {\ncase _DashboardState():\nreturn $default(_that.dashboardWidgets,_that.viewWidth);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<DashboardWidget> dashboardWidgets,  double viewWidth)?  $default,) {final _that = this;\nswitch (_that) {\ncase _DashboardState() when $default != null:\nreturn $default(_that.dashboardWidgets,_that.viewWidth);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _DashboardState implements DashboardState {\n  const _DashboardState({required final  List<DashboardWidget> dashboardWidgets, required this.viewWidth}): _dashboardWidgets = dashboardWidgets;\n  \n\n final  List<DashboardWidget> _dashboardWidgets;\n@override List<DashboardWidget> get dashboardWidgets {\n  if (_dashboardWidgets is EqualUnmodifiableListView) return _dashboardWidgets;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_dashboardWidgets);\n}\n\n@override final  double viewWidth;\n\n/// Create a copy of DashboardState\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$DashboardStateCopyWith<_DashboardState> get copyWith => __$DashboardStateCopyWithImpl<_DashboardState>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _DashboardState&&const DeepCollectionEquality().equals(other._dashboardWidgets, _dashboardWidgets)&&(identical(other.viewWidth, viewWidth) || other.viewWidth == viewWidth));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_dashboardWidgets),viewWidth);\n\n@override\nString toString() {\n  return 'DashboardState(dashboardWidgets: $dashboardWidgets, viewWidth: $viewWidth)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$DashboardStateCopyWith<$Res> implements $DashboardStateCopyWith<$Res> {\n  factory _$DashboardStateCopyWith(_DashboardState value, $Res Function(_DashboardState) _then) = __$DashboardStateCopyWithImpl;\n@override @useResult\n$Res call({\n List<DashboardWidget> dashboardWidgets, double viewWidth\n});\n\n\n\n\n}\n/// @nodoc\nclass __$DashboardStateCopyWithImpl<$Res>\n    implements _$DashboardStateCopyWith<$Res> {\n  __$DashboardStateCopyWithImpl(this._self, this._then);\n\n  final _DashboardState _self;\n  final $Res Function(_DashboardState) _then;\n\n/// Create a copy of DashboardState\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? dashboardWidgets = null,Object? viewWidth = null,}) {\n  return _then(_DashboardState(\ndashboardWidgets: null == dashboardWidgets ? _self._dashboardWidgets : dashboardWidgets // ignore: cast_nullable_to_non_nullable\nas List<DashboardWidget>,viewWidth: null == viewWidth ? _self.viewWidth : viewWidth // ignore: cast_nullable_to_non_nullable\nas double,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$ProxyCardState {\n\n String get proxyName; String? get testUrl;\n/// Create a copy of ProxyCardState\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ProxyCardStateCopyWith<ProxyCardState> get copyWith => _$ProxyCardStateCopyWithImpl<ProxyCardState>(this as ProxyCardState, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is ProxyCardState&&(identical(other.proxyName, proxyName) || other.proxyName == proxyName)&&(identical(other.testUrl, testUrl) || other.testUrl == testUrl));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,proxyName,testUrl);\n\n@override\nString toString() {\n  return 'ProxyCardState(proxyName: $proxyName, testUrl: $testUrl)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ProxyCardStateCopyWith<$Res>  {\n  factory $ProxyCardStateCopyWith(ProxyCardState value, $Res Function(ProxyCardState) _then) = _$ProxyCardStateCopyWithImpl;\n@useResult\n$Res call({\n String proxyName, String? testUrl\n});\n\n\n\n\n}\n/// @nodoc\nclass _$ProxyCardStateCopyWithImpl<$Res>\n    implements $ProxyCardStateCopyWith<$Res> {\n  _$ProxyCardStateCopyWithImpl(this._self, this._then);\n\n  final ProxyCardState _self;\n  final $Res Function(ProxyCardState) _then;\n\n/// Create a copy of ProxyCardState\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? proxyName = null,Object? testUrl = freezed,}) {\n  return _then(_self.copyWith(\nproxyName: null == proxyName ? _self.proxyName : proxyName // ignore: cast_nullable_to_non_nullable\nas String,testUrl: freezed == testUrl ? _self.testUrl : testUrl // ignore: cast_nullable_to_non_nullable\nas String?,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [ProxyCardState].\nextension ProxyCardStatePatterns on ProxyCardState {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ProxyCardState value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _ProxyCardState() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ProxyCardState value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ProxyCardState():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ProxyCardState value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ProxyCardState() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String proxyName,  String? testUrl)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _ProxyCardState() when $default != null:\nreturn $default(_that.proxyName,_that.testUrl);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String proxyName,  String? testUrl)  $default,) {final _that = this;\nswitch (_that) {\ncase _ProxyCardState():\nreturn $default(_that.proxyName,_that.testUrl);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String proxyName,  String? testUrl)?  $default,) {final _that = this;\nswitch (_that) {\ncase _ProxyCardState() when $default != null:\nreturn $default(_that.proxyName,_that.testUrl);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _ProxyCardState implements ProxyCardState {\n  const _ProxyCardState({required this.proxyName, this.testUrl});\n  \n\n@override final  String proxyName;\n@override final  String? testUrl;\n\n/// Create a copy of ProxyCardState\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ProxyCardStateCopyWith<_ProxyCardState> get copyWith => __$ProxyCardStateCopyWithImpl<_ProxyCardState>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProxyCardState&&(identical(other.proxyName, proxyName) || other.proxyName == proxyName)&&(identical(other.testUrl, testUrl) || other.testUrl == testUrl));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,proxyName,testUrl);\n\n@override\nString toString() {\n  return 'ProxyCardState(proxyName: $proxyName, testUrl: $testUrl)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ProxyCardStateCopyWith<$Res> implements $ProxyCardStateCopyWith<$Res> {\n  factory _$ProxyCardStateCopyWith(_ProxyCardState value, $Res Function(_ProxyCardState) _then) = __$ProxyCardStateCopyWithImpl;\n@override @useResult\n$Res call({\n String proxyName, String? testUrl\n});\n\n\n\n\n}\n/// @nodoc\nclass __$ProxyCardStateCopyWithImpl<$Res>\n    implements _$ProxyCardStateCopyWith<$Res> {\n  __$ProxyCardStateCopyWithImpl(this._self, this._then);\n\n  final _ProxyCardState _self;\n  final $Res Function(_ProxyCardState) _then;\n\n/// Create a copy of ProxyCardState\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? proxyName = null,Object? testUrl = freezed,}) {\n  return _then(_ProxyCardState(\nproxyName: null == proxyName ? _self.proxyName : proxyName // ignore: cast_nullable_to_non_nullable\nas String,testUrl: freezed == testUrl ? _self.testUrl : testUrl // ignore: cast_nullable_to_non_nullable\nas String?,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$VpnState {\n\n TunStack get stack; VpnProps get vpnProps;\n/// Create a copy of VpnState\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$VpnStateCopyWith<VpnState> get copyWith => _$VpnStateCopyWithImpl<VpnState>(this as VpnState, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is VpnState&&(identical(other.stack, stack) || other.stack == stack)&&(identical(other.vpnProps, vpnProps) || other.vpnProps == vpnProps));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,stack,vpnProps);\n\n@override\nString toString() {\n  return 'VpnState(stack: $stack, vpnProps: $vpnProps)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $VpnStateCopyWith<$Res>  {\n  factory $VpnStateCopyWith(VpnState value, $Res Function(VpnState) _then) = _$VpnStateCopyWithImpl;\n@useResult\n$Res call({\n TunStack stack, VpnProps vpnProps\n});\n\n\n$VpnPropsCopyWith<$Res> get vpnProps;\n\n}\n/// @nodoc\nclass _$VpnStateCopyWithImpl<$Res>\n    implements $VpnStateCopyWith<$Res> {\n  _$VpnStateCopyWithImpl(this._self, this._then);\n\n  final VpnState _self;\n  final $Res Function(VpnState) _then;\n\n/// Create a copy of VpnState\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? stack = null,Object? vpnProps = null,}) {\n  return _then(_self.copyWith(\nstack: null == stack ? _self.stack : stack // ignore: cast_nullable_to_non_nullable\nas TunStack,vpnProps: null == vpnProps ? _self.vpnProps : vpnProps // ignore: cast_nullable_to_non_nullable\nas VpnProps,\n  ));\n}\n/// Create a copy of VpnState\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$VpnPropsCopyWith<$Res> get vpnProps {\n  \n  return $VpnPropsCopyWith<$Res>(_self.vpnProps, (value) {\n    return _then(_self.copyWith(vpnProps: value));\n  });\n}\n}\n\n\n/// Adds pattern-matching-related methods to [VpnState].\nextension VpnStatePatterns on VpnState {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _VpnState value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _VpnState() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _VpnState value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _VpnState():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _VpnState value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _VpnState() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( TunStack stack,  VpnProps vpnProps)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _VpnState() when $default != null:\nreturn $default(_that.stack,_that.vpnProps);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( TunStack stack,  VpnProps vpnProps)  $default,) {final _that = this;\nswitch (_that) {\ncase _VpnState():\nreturn $default(_that.stack,_that.vpnProps);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( TunStack stack,  VpnProps vpnProps)?  $default,) {final _that = this;\nswitch (_that) {\ncase _VpnState() when $default != null:\nreturn $default(_that.stack,_that.vpnProps);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _VpnState implements VpnState {\n  const _VpnState({required this.stack, required this.vpnProps});\n  \n\n@override final  TunStack stack;\n@override final  VpnProps vpnProps;\n\n/// Create a copy of VpnState\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$VpnStateCopyWith<_VpnState> get copyWith => __$VpnStateCopyWithImpl<_VpnState>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _VpnState&&(identical(other.stack, stack) || other.stack == stack)&&(identical(other.vpnProps, vpnProps) || other.vpnProps == vpnProps));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,stack,vpnProps);\n\n@override\nString toString() {\n  return 'VpnState(stack: $stack, vpnProps: $vpnProps)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$VpnStateCopyWith<$Res> implements $VpnStateCopyWith<$Res> {\n  factory _$VpnStateCopyWith(_VpnState value, $Res Function(_VpnState) _then) = __$VpnStateCopyWithImpl;\n@override @useResult\n$Res call({\n TunStack stack, VpnProps vpnProps\n});\n\n\n@override $VpnPropsCopyWith<$Res> get vpnProps;\n\n}\n/// @nodoc\nclass __$VpnStateCopyWithImpl<$Res>\n    implements _$VpnStateCopyWith<$Res> {\n  __$VpnStateCopyWithImpl(this._self, this._then);\n\n  final _VpnState _self;\n  final $Res Function(_VpnState) _then;\n\n/// Create a copy of VpnState\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? stack = null,Object? vpnProps = null,}) {\n  return _then(_VpnState(\nstack: null == stack ? _self.stack : stack // ignore: cast_nullable_to_non_nullable\nas TunStack,vpnProps: null == vpnProps ? _self.vpnProps : vpnProps // ignore: cast_nullable_to_non_nullable\nas VpnProps,\n  ));\n}\n\n/// Create a copy of VpnState\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$VpnPropsCopyWith<$Res> get vpnProps {\n  \n  return $VpnPropsCopyWith<$Res>(_self.vpnProps, (value) {\n    return _then(_self.copyWith(vpnProps: value));\n  });\n}\n}\n\n/// @nodoc\nmixin _$ProfileOverrideStateModel {\n\n ClashConfigSnippet? get snippet; Set<String> get selectedRules; OverrideData? get overrideData;\n/// Create a copy of ProfileOverrideStateModel\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ProfileOverrideStateModelCopyWith<ProfileOverrideStateModel> get copyWith => _$ProfileOverrideStateModelCopyWithImpl<ProfileOverrideStateModel>(this as ProfileOverrideStateModel, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is ProfileOverrideStateModel&&(identical(other.snippet, snippet) || other.snippet == snippet)&&const DeepCollectionEquality().equals(other.selectedRules, selectedRules)&&(identical(other.overrideData, overrideData) || other.overrideData == overrideData));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,snippet,const DeepCollectionEquality().hash(selectedRules),overrideData);\n\n@override\nString toString() {\n  return 'ProfileOverrideStateModel(snippet: $snippet, selectedRules: $selectedRules, overrideData: $overrideData)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ProfileOverrideStateModelCopyWith<$Res>  {\n  factory $ProfileOverrideStateModelCopyWith(ProfileOverrideStateModel value, $Res Function(ProfileOverrideStateModel) _then) = _$ProfileOverrideStateModelCopyWithImpl;\n@useResult\n$Res call({\n ClashConfigSnippet? snippet, Set<String> selectedRules, OverrideData? overrideData\n});\n\n\n$ClashConfigSnippetCopyWith<$Res>? get snippet;$OverrideDataCopyWith<$Res>? get overrideData;\n\n}\n/// @nodoc\nclass _$ProfileOverrideStateModelCopyWithImpl<$Res>\n    implements $ProfileOverrideStateModelCopyWith<$Res> {\n  _$ProfileOverrideStateModelCopyWithImpl(this._self, this._then);\n\n  final ProfileOverrideStateModel _self;\n  final $Res Function(ProfileOverrideStateModel) _then;\n\n/// Create a copy of ProfileOverrideStateModel\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? snippet = freezed,Object? selectedRules = null,Object? overrideData = freezed,}) {\n  return _then(_self.copyWith(\nsnippet: freezed == snippet ? _self.snippet : snippet // ignore: cast_nullable_to_non_nullable\nas ClashConfigSnippet?,selectedRules: null == selectedRules ? _self.selectedRules : selectedRules // ignore: cast_nullable_to_non_nullable\nas Set<String>,overrideData: freezed == overrideData ? _self.overrideData : overrideData // ignore: cast_nullable_to_non_nullable\nas OverrideData?,\n  ));\n}\n/// Create a copy of ProfileOverrideStateModel\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$ClashConfigSnippetCopyWith<$Res>? get snippet {\n    if (_self.snippet == null) {\n    return null;\n  }\n\n  return $ClashConfigSnippetCopyWith<$Res>(_self.snippet!, (value) {\n    return _then(_self.copyWith(snippet: value));\n  });\n}/// Create a copy of ProfileOverrideStateModel\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$OverrideDataCopyWith<$Res>? get overrideData {\n    if (_self.overrideData == null) {\n    return null;\n  }\n\n  return $OverrideDataCopyWith<$Res>(_self.overrideData!, (value) {\n    return _then(_self.copyWith(overrideData: value));\n  });\n}\n}\n\n\n/// Adds pattern-matching-related methods to [ProfileOverrideStateModel].\nextension ProfileOverrideStateModelPatterns on ProfileOverrideStateModel {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ProfileOverrideStateModel value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _ProfileOverrideStateModel() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ProfileOverrideStateModel value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ProfileOverrideStateModel():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ProfileOverrideStateModel value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ProfileOverrideStateModel() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( ClashConfigSnippet? snippet,  Set<String> selectedRules,  OverrideData? overrideData)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _ProfileOverrideStateModel() when $default != null:\nreturn $default(_that.snippet,_that.selectedRules,_that.overrideData);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( ClashConfigSnippet? snippet,  Set<String> selectedRules,  OverrideData? overrideData)  $default,) {final _that = this;\nswitch (_that) {\ncase _ProfileOverrideStateModel():\nreturn $default(_that.snippet,_that.selectedRules,_that.overrideData);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( ClashConfigSnippet? snippet,  Set<String> selectedRules,  OverrideData? overrideData)?  $default,) {final _that = this;\nswitch (_that) {\ncase _ProfileOverrideStateModel() when $default != null:\nreturn $default(_that.snippet,_that.selectedRules,_that.overrideData);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _ProfileOverrideStateModel implements ProfileOverrideStateModel {\n  const _ProfileOverrideStateModel({this.snippet, required final  Set<String> selectedRules, this.overrideData}): _selectedRules = selectedRules;\n  \n\n@override final  ClashConfigSnippet? snippet;\n final  Set<String> _selectedRules;\n@override Set<String> get selectedRules {\n  if (_selectedRules is EqualUnmodifiableSetView) return _selectedRules;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableSetView(_selectedRules);\n}\n\n@override final  OverrideData? overrideData;\n\n/// Create a copy of ProfileOverrideStateModel\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ProfileOverrideStateModelCopyWith<_ProfileOverrideStateModel> get copyWith => __$ProfileOverrideStateModelCopyWithImpl<_ProfileOverrideStateModel>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _ProfileOverrideStateModel&&(identical(other.snippet, snippet) || other.snippet == snippet)&&const DeepCollectionEquality().equals(other._selectedRules, _selectedRules)&&(identical(other.overrideData, overrideData) || other.overrideData == overrideData));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,snippet,const DeepCollectionEquality().hash(_selectedRules),overrideData);\n\n@override\nString toString() {\n  return 'ProfileOverrideStateModel(snippet: $snippet, selectedRules: $selectedRules, overrideData: $overrideData)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ProfileOverrideStateModelCopyWith<$Res> implements $ProfileOverrideStateModelCopyWith<$Res> {\n  factory _$ProfileOverrideStateModelCopyWith(_ProfileOverrideStateModel value, $Res Function(_ProfileOverrideStateModel) _then) = __$ProfileOverrideStateModelCopyWithImpl;\n@override @useResult\n$Res call({\n ClashConfigSnippet? snippet, Set<String> selectedRules, OverrideData? overrideData\n});\n\n\n@override $ClashConfigSnippetCopyWith<$Res>? get snippet;@override $OverrideDataCopyWith<$Res>? get overrideData;\n\n}\n/// @nodoc\nclass __$ProfileOverrideStateModelCopyWithImpl<$Res>\n    implements _$ProfileOverrideStateModelCopyWith<$Res> {\n  __$ProfileOverrideStateModelCopyWithImpl(this._self, this._then);\n\n  final _ProfileOverrideStateModel _self;\n  final $Res Function(_ProfileOverrideStateModel) _then;\n\n/// Create a copy of ProfileOverrideStateModel\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? snippet = freezed,Object? selectedRules = null,Object? overrideData = freezed,}) {\n  return _then(_ProfileOverrideStateModel(\nsnippet: freezed == snippet ? _self.snippet : snippet // ignore: cast_nullable_to_non_nullable\nas ClashConfigSnippet?,selectedRules: null == selectedRules ? _self._selectedRules : selectedRules // ignore: cast_nullable_to_non_nullable\nas Set<String>,overrideData: freezed == overrideData ? _self.overrideData : overrideData // ignore: cast_nullable_to_non_nullable\nas OverrideData?,\n  ));\n}\n\n/// Create a copy of ProfileOverrideStateModel\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$ClashConfigSnippetCopyWith<$Res>? get snippet {\n    if (_self.snippet == null) {\n    return null;\n  }\n\n  return $ClashConfigSnippetCopyWith<$Res>(_self.snippet!, (value) {\n    return _then(_self.copyWith(snippet: value));\n  });\n}/// Create a copy of ProfileOverrideStateModel\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$OverrideDataCopyWith<$Res>? get overrideData {\n    if (_self.overrideData == null) {\n    return null;\n  }\n\n  return $OverrideDataCopyWith<$Res>(_self.overrideData!, (value) {\n    return _then(_self.copyWith(overrideData: value));\n  });\n}\n}\n\n// dart format on\n"
  },
  {
    "path": "lib/models/generated/widget.freezed.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n// coverage:ignore-file\n// ignore_for_file: type=lint\n// ignore_for_file: unused_element, deprecated_member_use, deprecated_member_use_from_same_package, use_function_type_syntax_for_parameters, unnecessary_const, avoid_init_to_null, invalid_override_different_default_values_named, prefer_expression_function_bodies, annotate_overrides, invalid_annotation_target, unnecessary_question_mark\n\npart of '../widget.dart';\n\n// **************************************************************************\n// FreezedGenerator\n// **************************************************************************\n\n// dart format off\nT _$identity<T>(T value) => value;\n/// @nodoc\nmixin _$ActivateState {\n\n bool get active;\n/// Create a copy of ActivateState\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$ActivateStateCopyWith<ActivateState> get copyWith => _$ActivateStateCopyWithImpl<ActivateState>(this as ActivateState, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is ActivateState&&(identical(other.active, active) || other.active == active));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,active);\n\n@override\nString toString() {\n  return 'ActivateState(active: $active)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $ActivateStateCopyWith<$Res>  {\n  factory $ActivateStateCopyWith(ActivateState value, $Res Function(ActivateState) _then) = _$ActivateStateCopyWithImpl;\n@useResult\n$Res call({\n bool active\n});\n\n\n\n\n}\n/// @nodoc\nclass _$ActivateStateCopyWithImpl<$Res>\n    implements $ActivateStateCopyWith<$Res> {\n  _$ActivateStateCopyWithImpl(this._self, this._then);\n\n  final ActivateState _self;\n  final $Res Function(ActivateState) _then;\n\n/// Create a copy of ActivateState\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? active = null,}) {\n  return _then(_self.copyWith(\nactive: null == active ? _self.active : active // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [ActivateState].\nextension ActivateStatePatterns on ActivateState {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _ActivateState value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _ActivateState() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _ActivateState value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ActivateState():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _ActivateState value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _ActivateState() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( bool active)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _ActivateState() when $default != null:\nreturn $default(_that.active);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( bool active)  $default,) {final _that = this;\nswitch (_that) {\ncase _ActivateState():\nreturn $default(_that.active);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( bool active)?  $default,) {final _that = this;\nswitch (_that) {\ncase _ActivateState() when $default != null:\nreturn $default(_that.active);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _ActivateState implements ActivateState {\n  const _ActivateState({required this.active});\n  \n\n@override final  bool active;\n\n/// Create a copy of ActivateState\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$ActivateStateCopyWith<_ActivateState> get copyWith => __$ActivateStateCopyWithImpl<_ActivateState>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _ActivateState&&(identical(other.active, active) || other.active == active));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,active);\n\n@override\nString toString() {\n  return 'ActivateState(active: $active)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$ActivateStateCopyWith<$Res> implements $ActivateStateCopyWith<$Res> {\n  factory _$ActivateStateCopyWith(_ActivateState value, $Res Function(_ActivateState) _then) = __$ActivateStateCopyWithImpl;\n@override @useResult\n$Res call({\n bool active\n});\n\n\n\n\n}\n/// @nodoc\nclass __$ActivateStateCopyWithImpl<$Res>\n    implements _$ActivateStateCopyWith<$Res> {\n  __$ActivateStateCopyWithImpl(this._self, this._then);\n\n  final _ActivateState _self;\n  final $Res Function(_ActivateState) _then;\n\n/// Create a copy of ActivateState\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? active = null,}) {\n  return _then(_ActivateState(\nactive: null == active ? _self.active : active // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$CommonMessage {\n\n String get id; String get text; Duration get duration; VoidCallback? get onAction; String? get actionLabel; bool get showCountdown;\n/// Create a copy of CommonMessage\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$CommonMessageCopyWith<CommonMessage> get copyWith => _$CommonMessageCopyWithImpl<CommonMessage>(this as CommonMessage, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is CommonMessage&&(identical(other.id, id) || other.id == id)&&(identical(other.text, text) || other.text == text)&&(identical(other.duration, duration) || other.duration == duration)&&(identical(other.onAction, onAction) || other.onAction == onAction)&&(identical(other.actionLabel, actionLabel) || other.actionLabel == actionLabel)&&(identical(other.showCountdown, showCountdown) || other.showCountdown == showCountdown));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,id,text,duration,onAction,actionLabel,showCountdown);\n\n@override\nString toString() {\n  return 'CommonMessage(id: $id, text: $text, duration: $duration, onAction: $onAction, actionLabel: $actionLabel, showCountdown: $showCountdown)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $CommonMessageCopyWith<$Res>  {\n  factory $CommonMessageCopyWith(CommonMessage value, $Res Function(CommonMessage) _then) = _$CommonMessageCopyWithImpl;\n@useResult\n$Res call({\n String id, String text, Duration duration, VoidCallback? onAction, String? actionLabel, bool showCountdown\n});\n\n\n\n\n}\n/// @nodoc\nclass _$CommonMessageCopyWithImpl<$Res>\n    implements $CommonMessageCopyWith<$Res> {\n  _$CommonMessageCopyWithImpl(this._self, this._then);\n\n  final CommonMessage _self;\n  final $Res Function(CommonMessage) _then;\n\n/// Create a copy of CommonMessage\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? id = null,Object? text = null,Object? duration = null,Object? onAction = freezed,Object? actionLabel = freezed,Object? showCountdown = null,}) {\n  return _then(_self.copyWith(\nid: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable\nas String,text: null == text ? _self.text : text // ignore: cast_nullable_to_non_nullable\nas String,duration: null == duration ? _self.duration : duration // ignore: cast_nullable_to_non_nullable\nas Duration,onAction: freezed == onAction ? _self.onAction : onAction // ignore: cast_nullable_to_non_nullable\nas VoidCallback?,actionLabel: freezed == actionLabel ? _self.actionLabel : actionLabel // ignore: cast_nullable_to_non_nullable\nas String?,showCountdown: null == showCountdown ? _self.showCountdown : showCountdown // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [CommonMessage].\nextension CommonMessagePatterns on CommonMessage {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _CommonMessage value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _CommonMessage() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _CommonMessage value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _CommonMessage():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _CommonMessage value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _CommonMessage() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( String id,  String text,  Duration duration,  VoidCallback? onAction,  String? actionLabel,  bool showCountdown)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _CommonMessage() when $default != null:\nreturn $default(_that.id,_that.text,_that.duration,_that.onAction,_that.actionLabel,_that.showCountdown);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( String id,  String text,  Duration duration,  VoidCallback? onAction,  String? actionLabel,  bool showCountdown)  $default,) {final _that = this;\nswitch (_that) {\ncase _CommonMessage():\nreturn $default(_that.id,_that.text,_that.duration,_that.onAction,_that.actionLabel,_that.showCountdown);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( String id,  String text,  Duration duration,  VoidCallback? onAction,  String? actionLabel,  bool showCountdown)?  $default,) {final _that = this;\nswitch (_that) {\ncase _CommonMessage() when $default != null:\nreturn $default(_that.id,_that.text,_that.duration,_that.onAction,_that.actionLabel,_that.showCountdown);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _CommonMessage implements CommonMessage {\n  const _CommonMessage({required this.id, required this.text, this.duration = const Duration(seconds: 3), this.onAction, this.actionLabel, this.showCountdown = false});\n  \n\n@override final  String id;\n@override final  String text;\n@override@JsonKey() final  Duration duration;\n@override final  VoidCallback? onAction;\n@override final  String? actionLabel;\n@override@JsonKey() final  bool showCountdown;\n\n/// Create a copy of CommonMessage\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$CommonMessageCopyWith<_CommonMessage> get copyWith => __$CommonMessageCopyWithImpl<_CommonMessage>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _CommonMessage&&(identical(other.id, id) || other.id == id)&&(identical(other.text, text) || other.text == text)&&(identical(other.duration, duration) || other.duration == duration)&&(identical(other.onAction, onAction) || other.onAction == onAction)&&(identical(other.actionLabel, actionLabel) || other.actionLabel == actionLabel)&&(identical(other.showCountdown, showCountdown) || other.showCountdown == showCountdown));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,id,text,duration,onAction,actionLabel,showCountdown);\n\n@override\nString toString() {\n  return 'CommonMessage(id: $id, text: $text, duration: $duration, onAction: $onAction, actionLabel: $actionLabel, showCountdown: $showCountdown)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$CommonMessageCopyWith<$Res> implements $CommonMessageCopyWith<$Res> {\n  factory _$CommonMessageCopyWith(_CommonMessage value, $Res Function(_CommonMessage) _then) = __$CommonMessageCopyWithImpl;\n@override @useResult\n$Res call({\n String id, String text, Duration duration, VoidCallback? onAction, String? actionLabel, bool showCountdown\n});\n\n\n\n\n}\n/// @nodoc\nclass __$CommonMessageCopyWithImpl<$Res>\n    implements _$CommonMessageCopyWith<$Res> {\n  __$CommonMessageCopyWithImpl(this._self, this._then);\n\n  final _CommonMessage _self;\n  final $Res Function(_CommonMessage) _then;\n\n/// Create a copy of CommonMessage\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? id = null,Object? text = null,Object? duration = null,Object? onAction = freezed,Object? actionLabel = freezed,Object? showCountdown = null,}) {\n  return _then(_CommonMessage(\nid: null == id ? _self.id : id // ignore: cast_nullable_to_non_nullable\nas String,text: null == text ? _self.text : text // ignore: cast_nullable_to_non_nullable\nas String,duration: null == duration ? _self.duration : duration // ignore: cast_nullable_to_non_nullable\nas Duration,onAction: freezed == onAction ? _self.onAction : onAction // ignore: cast_nullable_to_non_nullable\nas VoidCallback?,actionLabel: freezed == actionLabel ? _self.actionLabel : actionLabel // ignore: cast_nullable_to_non_nullable\nas String?,showCountdown: null == showCountdown ? _self.showCountdown : showCountdown // ignore: cast_nullable_to_non_nullable\nas bool,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$AppBarState {\n\n List<Widget> get actions; AppBarSearchState? get searchState; AppBarEditState? get editState;\n/// Create a copy of AppBarState\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$AppBarStateCopyWith<AppBarState> get copyWith => _$AppBarStateCopyWithImpl<AppBarState>(this as AppBarState, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is AppBarState&&const DeepCollectionEquality().equals(other.actions, actions)&&(identical(other.searchState, searchState) || other.searchState == searchState)&&(identical(other.editState, editState) || other.editState == editState));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(actions),searchState,editState);\n\n@override\nString toString() {\n  return 'AppBarState(actions: $actions, searchState: $searchState, editState: $editState)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $AppBarStateCopyWith<$Res>  {\n  factory $AppBarStateCopyWith(AppBarState value, $Res Function(AppBarState) _then) = _$AppBarStateCopyWithImpl;\n@useResult\n$Res call({\n List<Widget> actions, AppBarSearchState? searchState, AppBarEditState? editState\n});\n\n\n$AppBarSearchStateCopyWith<$Res>? get searchState;$AppBarEditStateCopyWith<$Res>? get editState;\n\n}\n/// @nodoc\nclass _$AppBarStateCopyWithImpl<$Res>\n    implements $AppBarStateCopyWith<$Res> {\n  _$AppBarStateCopyWithImpl(this._self, this._then);\n\n  final AppBarState _self;\n  final $Res Function(AppBarState) _then;\n\n/// Create a copy of AppBarState\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? actions = null,Object? searchState = freezed,Object? editState = freezed,}) {\n  return _then(_self.copyWith(\nactions: null == actions ? _self.actions : actions // ignore: cast_nullable_to_non_nullable\nas List<Widget>,searchState: freezed == searchState ? _self.searchState : searchState // ignore: cast_nullable_to_non_nullable\nas AppBarSearchState?,editState: freezed == editState ? _self.editState : editState // ignore: cast_nullable_to_non_nullable\nas AppBarEditState?,\n  ));\n}\n/// Create a copy of AppBarState\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$AppBarSearchStateCopyWith<$Res>? get searchState {\n    if (_self.searchState == null) {\n    return null;\n  }\n\n  return $AppBarSearchStateCopyWith<$Res>(_self.searchState!, (value) {\n    return _then(_self.copyWith(searchState: value));\n  });\n}/// Create a copy of AppBarState\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$AppBarEditStateCopyWith<$Res>? get editState {\n    if (_self.editState == null) {\n    return null;\n  }\n\n  return $AppBarEditStateCopyWith<$Res>(_self.editState!, (value) {\n    return _then(_self.copyWith(editState: value));\n  });\n}\n}\n\n\n/// Adds pattern-matching-related methods to [AppBarState].\nextension AppBarStatePatterns on AppBarState {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _AppBarState value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _AppBarState() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _AppBarState value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _AppBarState():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _AppBarState value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _AppBarState() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( List<Widget> actions,  AppBarSearchState? searchState,  AppBarEditState? editState)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _AppBarState() when $default != null:\nreturn $default(_that.actions,_that.searchState,_that.editState);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( List<Widget> actions,  AppBarSearchState? searchState,  AppBarEditState? editState)  $default,) {final _that = this;\nswitch (_that) {\ncase _AppBarState():\nreturn $default(_that.actions,_that.searchState,_that.editState);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( List<Widget> actions,  AppBarSearchState? searchState,  AppBarEditState? editState)?  $default,) {final _that = this;\nswitch (_that) {\ncase _AppBarState() when $default != null:\nreturn $default(_that.actions,_that.searchState,_that.editState);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _AppBarState implements AppBarState {\n  const _AppBarState({final  List<Widget> actions = const [], this.searchState, this.editState}): _actions = actions;\n  \n\n final  List<Widget> _actions;\n@override@JsonKey() List<Widget> get actions {\n  if (_actions is EqualUnmodifiableListView) return _actions;\n  // ignore: implicit_dynamic_type\n  return EqualUnmodifiableListView(_actions);\n}\n\n@override final  AppBarSearchState? searchState;\n@override final  AppBarEditState? editState;\n\n/// Create a copy of AppBarState\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$AppBarStateCopyWith<_AppBarState> get copyWith => __$AppBarStateCopyWithImpl<_AppBarState>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppBarState&&const DeepCollectionEquality().equals(other._actions, _actions)&&(identical(other.searchState, searchState) || other.searchState == searchState)&&(identical(other.editState, editState) || other.editState == editState));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,const DeepCollectionEquality().hash(_actions),searchState,editState);\n\n@override\nString toString() {\n  return 'AppBarState(actions: $actions, searchState: $searchState, editState: $editState)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$AppBarStateCopyWith<$Res> implements $AppBarStateCopyWith<$Res> {\n  factory _$AppBarStateCopyWith(_AppBarState value, $Res Function(_AppBarState) _then) = __$AppBarStateCopyWithImpl;\n@override @useResult\n$Res call({\n List<Widget> actions, AppBarSearchState? searchState, AppBarEditState? editState\n});\n\n\n@override $AppBarSearchStateCopyWith<$Res>? get searchState;@override $AppBarEditStateCopyWith<$Res>? get editState;\n\n}\n/// @nodoc\nclass __$AppBarStateCopyWithImpl<$Res>\n    implements _$AppBarStateCopyWith<$Res> {\n  __$AppBarStateCopyWithImpl(this._self, this._then);\n\n  final _AppBarState _self;\n  final $Res Function(_AppBarState) _then;\n\n/// Create a copy of AppBarState\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? actions = null,Object? searchState = freezed,Object? editState = freezed,}) {\n  return _then(_AppBarState(\nactions: null == actions ? _self._actions : actions // ignore: cast_nullable_to_non_nullable\nas List<Widget>,searchState: freezed == searchState ? _self.searchState : searchState // ignore: cast_nullable_to_non_nullable\nas AppBarSearchState?,editState: freezed == editState ? _self.editState : editState // ignore: cast_nullable_to_non_nullable\nas AppBarEditState?,\n  ));\n}\n\n/// Create a copy of AppBarState\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$AppBarSearchStateCopyWith<$Res>? get searchState {\n    if (_self.searchState == null) {\n    return null;\n  }\n\n  return $AppBarSearchStateCopyWith<$Res>(_self.searchState!, (value) {\n    return _then(_self.copyWith(searchState: value));\n  });\n}/// Create a copy of AppBarState\n/// with the given fields replaced by the non-null parameter values.\n@override\n@pragma('vm:prefer-inline')\n$AppBarEditStateCopyWith<$Res>? get editState {\n    if (_self.editState == null) {\n    return null;\n  }\n\n  return $AppBarEditStateCopyWith<$Res>(_self.editState!, (value) {\n    return _then(_self.copyWith(editState: value));\n  });\n}\n}\n\n/// @nodoc\nmixin _$AppBarSearchState {\n\n  Function(String) get onSearch; String? get query;\n/// Create a copy of AppBarSearchState\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$AppBarSearchStateCopyWith<AppBarSearchState> get copyWith => _$AppBarSearchStateCopyWithImpl<AppBarSearchState>(this as AppBarSearchState, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is AppBarSearchState&&(identical(other.onSearch, onSearch) || other.onSearch == onSearch)&&(identical(other.query, query) || other.query == query));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,onSearch,query);\n\n@override\nString toString() {\n  return 'AppBarSearchState(onSearch: $onSearch, query: $query)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $AppBarSearchStateCopyWith<$Res>  {\n  factory $AppBarSearchStateCopyWith(AppBarSearchState value, $Res Function(AppBarSearchState) _then) = _$AppBarSearchStateCopyWithImpl;\n@useResult\n$Res call({\n  Function(String) onSearch, String? query\n});\n\n\n\n\n}\n/// @nodoc\nclass _$AppBarSearchStateCopyWithImpl<$Res>\n    implements $AppBarSearchStateCopyWith<$Res> {\n  _$AppBarSearchStateCopyWithImpl(this._self, this._then);\n\n  final AppBarSearchState _self;\n  final $Res Function(AppBarSearchState) _then;\n\n/// Create a copy of AppBarSearchState\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? onSearch = null,Object? query = freezed,}) {\n  return _then(_self.copyWith(\nonSearch: null == onSearch ? _self.onSearch : onSearch // ignore: cast_nullable_to_non_nullable\nas  Function(String),query: freezed == query ? _self.query : query // ignore: cast_nullable_to_non_nullable\nas String?,\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [AppBarSearchState].\nextension AppBarSearchStatePatterns on AppBarSearchState {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _AppBarSearchState value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _AppBarSearchState() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _AppBarSearchState value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _AppBarSearchState():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _AppBarSearchState value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _AppBarSearchState() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function(  Function(String) onSearch,  String? query)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _AppBarSearchState() when $default != null:\nreturn $default(_that.onSearch,_that.query);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function(  Function(String) onSearch,  String? query)  $default,) {final _that = this;\nswitch (_that) {\ncase _AppBarSearchState():\nreturn $default(_that.onSearch,_that.query);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function(  Function(String) onSearch,  String? query)?  $default,) {final _that = this;\nswitch (_that) {\ncase _AppBarSearchState() when $default != null:\nreturn $default(_that.onSearch,_that.query);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _AppBarSearchState implements AppBarSearchState {\n  const _AppBarSearchState({required this.onSearch, this.query});\n  \n\n@override final   Function(String) onSearch;\n@override final  String? query;\n\n/// Create a copy of AppBarSearchState\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$AppBarSearchStateCopyWith<_AppBarSearchState> get copyWith => __$AppBarSearchStateCopyWithImpl<_AppBarSearchState>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppBarSearchState&&(identical(other.onSearch, onSearch) || other.onSearch == onSearch)&&(identical(other.query, query) || other.query == query));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,onSearch,query);\n\n@override\nString toString() {\n  return 'AppBarSearchState(onSearch: $onSearch, query: $query)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$AppBarSearchStateCopyWith<$Res> implements $AppBarSearchStateCopyWith<$Res> {\n  factory _$AppBarSearchStateCopyWith(_AppBarSearchState value, $Res Function(_AppBarSearchState) _then) = __$AppBarSearchStateCopyWithImpl;\n@override @useResult\n$Res call({\n  Function(String) onSearch, String? query\n});\n\n\n\n\n}\n/// @nodoc\nclass __$AppBarSearchStateCopyWithImpl<$Res>\n    implements _$AppBarSearchStateCopyWith<$Res> {\n  __$AppBarSearchStateCopyWithImpl(this._self, this._then);\n\n  final _AppBarSearchState _self;\n  final $Res Function(_AppBarSearchState) _then;\n\n/// Create a copy of AppBarSearchState\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? onSearch = null,Object? query = freezed,}) {\n  return _then(_AppBarSearchState(\nonSearch: null == onSearch ? _self.onSearch : onSearch // ignore: cast_nullable_to_non_nullable\nas  Function(String),query: freezed == query ? _self.query : query // ignore: cast_nullable_to_non_nullable\nas String?,\n  ));\n}\n\n\n}\n\n/// @nodoc\nmixin _$AppBarEditState {\n\n int get editCount;  Function() get onExit;\n/// Create a copy of AppBarEditState\n/// with the given fields replaced by the non-null parameter values.\n@JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n$AppBarEditStateCopyWith<AppBarEditState> get copyWith => _$AppBarEditStateCopyWithImpl<AppBarEditState>(this as AppBarEditState, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is AppBarEditState&&(identical(other.editCount, editCount) || other.editCount == editCount)&&(identical(other.onExit, onExit) || other.onExit == onExit));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,editCount,onExit);\n\n@override\nString toString() {\n  return 'AppBarEditState(editCount: $editCount, onExit: $onExit)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class $AppBarEditStateCopyWith<$Res>  {\n  factory $AppBarEditStateCopyWith(AppBarEditState value, $Res Function(AppBarEditState) _then) = _$AppBarEditStateCopyWithImpl;\n@useResult\n$Res call({\n int editCount,  Function() onExit\n});\n\n\n\n\n}\n/// @nodoc\nclass _$AppBarEditStateCopyWithImpl<$Res>\n    implements $AppBarEditStateCopyWith<$Res> {\n  _$AppBarEditStateCopyWithImpl(this._self, this._then);\n\n  final AppBarEditState _self;\n  final $Res Function(AppBarEditState) _then;\n\n/// Create a copy of AppBarEditState\n/// with the given fields replaced by the non-null parameter values.\n@pragma('vm:prefer-inline') @override $Res call({Object? editCount = null,Object? onExit = null,}) {\n  return _then(_self.copyWith(\neditCount: null == editCount ? _self.editCount : editCount // ignore: cast_nullable_to_non_nullable\nas int,onExit: null == onExit ? _self.onExit : onExit // ignore: cast_nullable_to_non_nullable\nas  Function(),\n  ));\n}\n\n}\n\n\n/// Adds pattern-matching-related methods to [AppBarEditState].\nextension AppBarEditStatePatterns on AppBarEditState {\n/// A variant of `map` that fallback to returning `orElse`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeMap<TResult extends Object?>(TResult Function( _AppBarEditState value)?  $default,{required TResult orElse(),}){\nfinal _that = this;\nswitch (_that) {\ncase _AppBarEditState() when $default != null:\nreturn $default(_that);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// Callbacks receives the raw object, upcasted.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case final Subclass2 value:\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult map<TResult extends Object?>(TResult Function( _AppBarEditState value)  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _AppBarEditState():\nreturn $default(_that);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `map` that fallback to returning `null`.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case final Subclass value:\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? mapOrNull<TResult extends Object?>(TResult? Function( _AppBarEditState value)?  $default,){\nfinal _that = this;\nswitch (_that) {\ncase _AppBarEditState() when $default != null:\nreturn $default(_that);case _:\n  return null;\n\n}\n}\n/// A variant of `when` that fallback to an `orElse` callback.\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return orElse();\n/// }\n/// ```\n\n@optionalTypeArgs TResult maybeWhen<TResult extends Object?>(TResult Function( int editCount,   Function() onExit)?  $default,{required TResult orElse(),}) {final _that = this;\nswitch (_that) {\ncase _AppBarEditState() when $default != null:\nreturn $default(_that.editCount,_that.onExit);case _:\n  return orElse();\n\n}\n}\n/// A `switch`-like method, using callbacks.\n///\n/// As opposed to `map`, this offers destructuring.\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case Subclass2(:final field2):\n///     return ...;\n/// }\n/// ```\n\n@optionalTypeArgs TResult when<TResult extends Object?>(TResult Function( int editCount,   Function() onExit)  $default,) {final _that = this;\nswitch (_that) {\ncase _AppBarEditState():\nreturn $default(_that.editCount,_that.onExit);case _:\n  throw StateError('Unexpected subclass');\n\n}\n}\n/// A variant of `when` that fallback to returning `null`\n///\n/// It is equivalent to doing:\n/// ```dart\n/// switch (sealedClass) {\n///   case Subclass(:final field):\n///     return ...;\n///   case _:\n///     return null;\n/// }\n/// ```\n\n@optionalTypeArgs TResult? whenOrNull<TResult extends Object?>(TResult? Function( int editCount,   Function() onExit)?  $default,) {final _that = this;\nswitch (_that) {\ncase _AppBarEditState() when $default != null:\nreturn $default(_that.editCount,_that.onExit);case _:\n  return null;\n\n}\n}\n\n}\n\n/// @nodoc\n\n\nclass _AppBarEditState implements AppBarEditState {\n  const _AppBarEditState({this.editCount = 0, required this.onExit});\n  \n\n@override@JsonKey() final  int editCount;\n@override final   Function() onExit;\n\n/// Create a copy of AppBarEditState\n/// with the given fields replaced by the non-null parameter values.\n@override @JsonKey(includeFromJson: false, includeToJson: false)\n@pragma('vm:prefer-inline')\n_$AppBarEditStateCopyWith<_AppBarEditState> get copyWith => __$AppBarEditStateCopyWithImpl<_AppBarEditState>(this, _$identity);\n\n\n\n@override\nbool operator ==(Object other) {\n  return identical(this, other) || (other.runtimeType == runtimeType&&other is _AppBarEditState&&(identical(other.editCount, editCount) || other.editCount == editCount)&&(identical(other.onExit, onExit) || other.onExit == onExit));\n}\n\n\n@override\nint get hashCode => Object.hash(runtimeType,editCount,onExit);\n\n@override\nString toString() {\n  return 'AppBarEditState(editCount: $editCount, onExit: $onExit)';\n}\n\n\n}\n\n/// @nodoc\nabstract mixin class _$AppBarEditStateCopyWith<$Res> implements $AppBarEditStateCopyWith<$Res> {\n  factory _$AppBarEditStateCopyWith(_AppBarEditState value, $Res Function(_AppBarEditState) _then) = __$AppBarEditStateCopyWithImpl;\n@override @useResult\n$Res call({\n int editCount,  Function() onExit\n});\n\n\n\n\n}\n/// @nodoc\nclass __$AppBarEditStateCopyWithImpl<$Res>\n    implements _$AppBarEditStateCopyWith<$Res> {\n  __$AppBarEditStateCopyWithImpl(this._self, this._then);\n\n  final _AppBarEditState _self;\n  final $Res Function(_AppBarEditState) _then;\n\n/// Create a copy of AppBarEditState\n/// with the given fields replaced by the non-null parameter values.\n@override @pragma('vm:prefer-inline') $Res call({Object? editCount = null,Object? onExit = null,}) {\n  return _then(_AppBarEditState(\neditCount: null == editCount ? _self.editCount : editCount // ignore: cast_nullable_to_non_nullable\nas int,onExit: null == onExit ? _self.onExit : onExit // ignore: cast_nullable_to_non_nullable\nas  Function(),\n  ));\n}\n\n\n}\n\n// dart format on\n"
  },
  {
    "path": "lib/models/models.dart",
    "content": "export 'app.dart';\nexport 'clash_config.dart';\nexport 'common.dart';\nexport 'config.dart';\nexport 'core.dart';\nexport 'profile.dart';\nexport 'selector.dart';\nexport 'widget.dart';\n"
  },
  {
    "path": "lib/models/profile.dart",
    "content": "// ignore_for_file: invalid_annotation_target\nimport 'dart:convert';\nimport 'dart:io';\nimport 'dart:typed_data';\n\nimport 'package:bett_box/clash/core.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:freezed_annotation/freezed_annotation.dart';\n\nimport 'clash_config.dart';\n\npart 'generated/profile.freezed.dart';\npart 'generated/profile.g.dart';\n\ntypedef SelectedMap = Map<String, String>;\n\n@freezed\nabstract class SubscriptionInfo with _$SubscriptionInfo {\n  const factory SubscriptionInfo({\n    @Default(0) int upload,\n    @Default(0) int download,\n    @Default(0) int total,\n    @Default(0) int expire,\n  }) = _SubscriptionInfo;\n\n  factory SubscriptionInfo.fromJson(Map<String, Object?> json) =>\n      _$SubscriptionInfoFromJson(json);\n\n  factory SubscriptionInfo.formHString(String? info) {\n    if (info == null) return const SubscriptionInfo();\n    final list = info.split(';');\n    Map<String, int?> map = {};\n    for (final i in list) {\n      final keyValue = i.trim().split('=');\n      map[keyValue[0]] = int.tryParse(keyValue[1]);\n    }\n    return SubscriptionInfo(\n      upload: map['upload'] ?? 0,\n      download: map['download'] ?? 0,\n      total: map['total'] ?? 0,\n      expire: map['expire'] ?? 0,\n    );\n  }\n}\n\n@freezed\nabstract class Profile with _$Profile {\n  const factory Profile({\n    required String id,\n    String? label,\n    String? currentGroupName,\n    @Default('') String url,\n    DateTime? lastUpdateDate,\n    required Duration autoUpdateDuration,\n    SubscriptionInfo? subscriptionInfo,\n    @Default(true) bool autoUpdate,\n    @Default({}) SelectedMap selectedMap,\n    @Default({}) Set<String> unfoldSet,\n    @Default(OverrideData()) OverrideData overrideData,\n    @JsonKey(includeToJson: false, includeFromJson: false)\n    @Default(false)\n    bool isUpdating,\n  }) = _Profile;\n\n  factory Profile.fromJson(Map<String, Object?> json) =>\n      _$ProfileFromJson(json);\n\n  factory Profile.normal({String? label, String url = ''}) {\n    return Profile(\n      label: label,\n      url: url,\n      id: DateTime.now().millisecondsSinceEpoch.toString(),\n      autoUpdateDuration: defaultUpdateDuration,\n    );\n  }\n}\n\n@freezed\nabstract class OverrideData with _$OverrideData {\n  const factory OverrideData({\n    @Default(false) bool enable,\n    @Default(OverrideRule()) OverrideRule rule,\n  }) = _OverrideData;\n\n  factory OverrideData.fromJson(Map<String, Object?> json) =>\n      _$OverrideDataFromJson(json);\n}\n\nextension OverrideDataExt on OverrideData {\n  List<String> get runningRule {\n    if (!enable) {\n      return [];\n    }\n    return rule.rules.map((item) => item.value).toList();\n  }\n}\n\n@freezed\nabstract class OverrideRule with _$OverrideRule {\n  const factory OverrideRule({\n    @Default(OverrideRuleType.override) OverrideRuleType type,\n    @Default([]) List<Rule> overrideRules,\n    @Default([]) List<Rule> addedRules,\n  }) = _OverrideRule;\n\n  factory OverrideRule.fromJson(Map<String, Object?> json) =>\n      _$OverrideRuleFromJson(json);\n}\n\nextension OverrideRuleExt on OverrideRule {\n  List<Rule> get rules => switch (type == OverrideRuleType.override) {\n    true => overrideRules,\n    false => addedRules,\n  };\n\n  OverrideRule updateRules(List<Rule> Function(List<Rule> rules) builder) {\n    if (type == OverrideRuleType.added) {\n      return copyWith(addedRules: builder(addedRules));\n    }\n    return copyWith(overrideRules: builder(overrideRules));\n  }\n}\n\nextension ProfilesExt on List<Profile> {\n  Profile? getProfile(String? profileId) {\n    final index = indexWhere((profile) => profile.id == profileId);\n    return index == -1 ? null : this[index];\n  }\n}\n\nextension ProfileExtension on Profile {\n  ProfileType get type =>\n      url.isEmpty == true ? ProfileType.file : ProfileType.url;\n\n  bool get realAutoUpdate => url.isEmpty == true ? false : autoUpdate;\n\n  Future<void> checkAndUpdate() async {\n    final isExists = await check();\n    if (!isExists) {\n      if (url.isNotEmpty) {\n        await update();\n      }\n    }\n  }\n\n  Future<bool> check() async {\n    final profilePath = await appPath.getProfilePath(id);\n    return await File(profilePath).exists();\n  }\n\n  Future<File> getFile() async {\n    final path = await appPath.getProfilePath(id);\n    final file = File(path);\n    final isExists = await file.exists();\n    if (!isExists) {\n      await file.create(recursive: true);\n    }\n    return file;\n  }\n\n  Future<int> get profileLastModified async {\n    final file = await getFile();\n    return (await file.lastModified()).microsecondsSinceEpoch;\n  }\n\n  Future<Profile> update() async {\n    final response = await request.getFileResponseForUrl(url);\n    final disposition = response.headers.value('content-disposition');\n    final userinfo = response.headers.value('subscription-userinfo');\n    return await copyWith(\n      label: label ?? utils.getFileNameForDisposition(disposition) ?? id,\n      subscriptionInfo: SubscriptionInfo.formHString(userinfo),\n    ).saveFile(response.data);\n  }\n\n  Future<Profile> saveFile(Uint8List bytes) async {\n    final message = await clashCore.validateConfig(utf8.decode(bytes));\n    if (message.isNotEmpty) {\n      throw message;\n    }\n    final file = await getFile();\n    await file.writeAsBytes(bytes);\n    return copyWith(lastUpdateDate: DateTime.now());\n  }\n\n  Future<Profile> saveFileWithString(String value) async {\n    final message = await clashCore.validateConfig(value);\n    if (message.isNotEmpty) {\n      throw message;\n    }\n    final file = await getFile();\n    await file.writeAsString(value);\n    return copyWith(lastUpdateDate: DateTime.now());\n  }\n}\n"
  },
  {
    "path": "lib/models/selector.dart",
    "content": "import 'package:collection/collection.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:flutter/material.dart';\nimport 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'generated/selector.freezed.dart';\n\n@freezed\nabstract class VM2<A, B> with _$VM2<A, B> {\n  const factory VM2({required A a, required B b}) = _VM2;\n}\n\n@freezed\nabstract class VM3<A, B, C> with _$VM3<A, B, C> {\n  const factory VM3({required A a, required B b, required C c}) = _VM3;\n}\n\n@freezed\nabstract class VM4<A, B, C, D> with _$VM4<A, B, C, D> {\n  const factory VM4({required A a, required B b, required C c, required D d}) =\n      _VM4;\n}\n\n@freezed\nabstract class VM5<A, B, C, D, E> with _$VM5<A, B, C, D, E> {\n  const factory VM5({\n    required A a,\n    required B b,\n    required C c,\n    required D d,\n    required E e,\n  }) = _VM5;\n}\n\n@freezed\nabstract class StartButtonSelectorState with _$StartButtonSelectorState {\n  const factory StartButtonSelectorState({\n    required bool isInit,\n    required bool hasProfile,\n  }) = _StartButtonSelectorState;\n}\n\n@freezed\nabstract class ProfilesSelectorState with _$ProfilesSelectorState {\n  const factory ProfilesSelectorState({\n    required List<Profile> profiles,\n    required String? currentProfileId,\n    required int columns,\n  }) = _ProfilesSelectorState;\n}\n\n@freezed\nabstract class NetworkDetectionState with _$NetworkDetectionState {\n  const factory NetworkDetectionState({\n    required bool isLoading,\n    required IpInfo? ipInfo,\n    String? errorMessage, // 错误消息，用于显示\"请尝试手动刷新\"\n  }) = _NetworkDetectionState;\n}\n\n@freezed\nabstract class TrayState with _$TrayState {\n  const factory TrayState({\n    required Mode mode,\n    required int port,\n    required bool autoLaunch,\n    required bool systemProxy,\n    required bool tunEnable,\n    required bool isStart,\n    required String? locale,\n    required Brightness? brightness,\n    required List<Group> groups,\n    required SelectedMap selectedMap,\n    @Default(false) bool wakelockEnabled,\n  }) = _TrayState;\n}\n\n@freezed\nabstract class NavigationState with _$NavigationState {\n  const factory NavigationState({\n    required PageLabel pageLabel,\n    required List<NavigationItem> navigationItems,\n    required ViewMode viewMode,\n    required String? locale,\n    required int currentIndex,\n  }) = _NavigationState;\n}\n\n@freezed\nabstract class GroupsState with _$GroupsState {\n  const factory GroupsState({required List<Group> value}) = _GroupsState;\n}\n\n@freezed\nabstract class NavigationItemsState with _$NavigationItemsState {\n  const factory NavigationItemsState({required List<NavigationItem> value}) =\n      _NavigationItemsState;\n}\n\n@freezed\nabstract class ProxiesListState with _$ProxiesListState {\n  const factory ProxiesListState({\n    required List<Group> groups,\n    required Set<String> currentUnfoldSet,\n    required ProxiesSortType proxiesSortType,\n    required ProxyCardType proxyCardType,\n    required num sortNum,\n    required int columns,\n  }) = _ProxiesListState;\n}\n\n@freezed\nabstract class ProxiesTabState with _$ProxiesTabState {\n  const factory ProxiesTabState({\n    required List<Group> groups,\n    required String? currentGroupName,\n    required ProxiesSortType proxiesSortType,\n    required ProxyCardType proxyCardType,\n    required num sortNum,\n    required int columns,\n  }) = _ProxiesTabState;\n}\n\n@freezed\nabstract class ProxyGroupSelectorState with _$ProxyGroupSelectorState {\n  const factory ProxyGroupSelectorState({\n    required String? testUrl,\n    required ProxiesSortType proxiesSortType,\n    required ProxyCardType proxyCardType,\n    required num sortNum,\n    required GroupType groupType,\n    required List<Proxy> proxies,\n    required int columns,\n  }) = _ProxyGroupSelectorState;\n}\n\n@freezed\nabstract class MoreToolsSelectorState with _$MoreToolsSelectorState {\n  const factory MoreToolsSelectorState({\n    required List<NavigationItem> navigationItems,\n  }) = _MoreToolsSelectorState;\n}\n\n@freezed\nabstract class PackageListSelectorState with _$PackageListSelectorState {\n  const factory PackageListSelectorState({\n    required List<Package> packages,\n    required AccessControl accessControl,\n  }) = _PackageListSelectorState;\n}\n\nextension PackageListSelectorStateExt on PackageListSelectorState {\n  List<Package> get list {\n    final isFilterSystemApp = accessControl.isFilterSystemApp;\n    final isFilterNonInternetApp = accessControl.isFilterNonInternetApp;\n    return packages\n        .where(\n          (item) =>\n              (isFilterSystemApp ? item.system == false : true) &&\n              (isFilterNonInternetApp ? item.internet == true : true),\n        )\n        .toList();\n  }\n\n  List<Package> getSortList(List<String> selectedList) {\n    final sort = accessControl.sort;\n    return list\n        .sorted((a, b) {\n          return switch (sort) {\n            AccessSortType.none => 0,\n            AccessSortType.name => utils.sortByChar(\n              utils.getPinyin(a.label),\n              utils.getPinyin(b.label),\n            ),\n            AccessSortType.time => b.lastUpdateTime.compareTo(a.lastUpdateTime),\n          };\n        })\n        .sorted((a, b) {\n          final isSelectA = selectedList.contains(a.packageName);\n          final isSelectB = selectedList.contains(b.packageName);\n          if (isSelectA && isSelectB) return 0;\n          if (isSelectA) return -1;\n          if (isSelectB) return 1;\n          return 0;\n        });\n  }\n}\n\n@freezed\nabstract class ProxiesListHeaderSelectorState with _$ProxiesListHeaderSelectorState {\n  const factory ProxiesListHeaderSelectorState({\n    required double offset,\n    required int currentIndex,\n  }) = _ProxiesListHeaderSelectorState;\n}\n\n@freezed\nabstract class ProxiesActionsState with _$ProxiesActionsState {\n  const factory ProxiesActionsState({\n    required PageLabel pageLabel,\n    required ProxiesType type,\n    required bool hasProviders,\n  }) = _ProxiesActionsState;\n}\n\n@freezed\nabstract class ProxyState with _$ProxyState {\n  const factory ProxyState({\n    required bool isStart,\n    required bool systemProxy,\n    required List<String> bassDomain,\n    required int port,\n  }) = _ProxyState;\n}\n\n@freezed\nabstract class ClashConfigState with _$ClashConfigState {\n  const factory ClashConfigState({\n    required bool overrideDns,\n    required ClashConfig clashConfig,\n    required OverrideData overrideData,\n    required bool bypassPrivateRoute,\n  }) = _ClashConfigState;\n}\n\n@freezed\nabstract class DashboardState with _$DashboardState {\n  const factory DashboardState({\n    required List<DashboardWidget> dashboardWidgets,\n    required double viewWidth,\n  }) = _DashboardState;\n}\n\n@freezed\nabstract class ProxyCardState with _$ProxyCardState {\n  const factory ProxyCardState({required String proxyName, String? testUrl}) =\n      _ProxyCardState;\n}\n\n@freezed\nabstract class VpnState with _$VpnState {\n  const factory VpnState({\n    required TunStack stack,\n    required VpnProps vpnProps,\n  }) = _VpnState;\n}\n\n@freezed\nabstract class ProfileOverrideStateModel with _$ProfileOverrideStateModel {\n  const factory ProfileOverrideStateModel({\n    ClashConfigSnippet? snippet,\n    required Set<String> selectedRules,\n    OverrideData? overrideData,\n  }) = _ProfileOverrideStateModel;\n}\n"
  },
  {
    "path": "lib/models/widget.dart",
    "content": "import 'package:flutter/widgets.dart';\nimport 'package:freezed_annotation/freezed_annotation.dart';\n\npart 'generated/widget.freezed.dart';\n\n@freezed\nabstract class ActivateState with _$ActivateState {\n  const factory ActivateState({required bool active}) = _ActivateState;\n}\n\n@freezed\nabstract class CommonMessage with _$CommonMessage {\n  const factory CommonMessage({\n    required String id,\n    required String text,\n    @Default(Duration(seconds: 3)) Duration duration,\n    VoidCallback? onAction,\n    String? actionLabel,\n    @Default(false) bool showCountdown,\n  }) = _CommonMessage;\n}\n\n@freezed\nabstract class AppBarState with _$AppBarState {\n  const factory AppBarState({\n    @Default([]) List<Widget> actions,\n    AppBarSearchState? searchState,\n    AppBarEditState? editState,\n  }) = _AppBarState;\n}\n\n@freezed\nabstract class AppBarSearchState with _$AppBarSearchState {\n  const factory AppBarSearchState({\n    required Function(String) onSearch,\n    String? query,\n  }) = _AppBarSearchState;\n}\n\n@freezed\nabstract class AppBarEditState with _$AppBarEditState {\n  const factory AppBarEditState({\n    @Default(0) int editCount,\n    required Function() onExit,\n  }) = _AppBarEditState;\n}\n"
  },
  {
    "path": "lib/pages/editor.dart",
    "content": "import 'dart:convert';\n\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/common.dart';\nimport 'package:bett_box/providers/app.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:re_editor/re_editor.dart';\nimport 'package:re_highlight/languages/javascript.dart';\nimport 'package:re_highlight/languages/yaml.dart';\nimport 'package:re_highlight/styles/atom-one-light.dart';\n\ntypedef EditingValueChangeBuilder = Widget Function(CodeLineEditingValue value);\ntypedef TextEditingValueChangeBuilder = Widget Function(TextEditingValue value);\n\nclass EditorPage extends ConsumerStatefulWidget {\n  final String title;\n  final String content;\n  final List<Language> languages;\n  final bool supportRemoteDownload;\n  final bool titleEditable;\n  final bool readOnly;\n  final Function(BuildContext context, String title, String content)? onSave;\n  final Future<bool> Function(\n    BuildContext context,\n    String title,\n    String content,\n  )?\n  onPop;\n  final void Function(String url)? onUrlImport;\n\n  const EditorPage({\n    super.key,\n    required this.title,\n    required this.content,\n    this.titleEditable = false,\n    this.readOnly = false,\n    this.onSave,\n    this.onPop,\n    this.onUrlImport,\n    this.supportRemoteDownload = false,\n    this.languages = const [Language.yaml],\n  });\n\n  @override\n  ConsumerState<EditorPage> createState() => _EditorPageState();\n}\n\nclass _EditorPageState extends ConsumerState<EditorPage> {\n  late CodeLineEditingController _controller;\n  late CodeFindController _findController;\n  late TextEditingController _titleController;\n  final _focusNode = FocusNode();\n\n  @override\n  void initState() {\n    super.initState();\n    _controller = CodeLineEditingController.fromText(widget.content);\n    _findController = CodeFindController(_controller);\n    _titleController = TextEditingController(text: widget.title);\n    if (system.isDesktop) {\n      return;\n    }\n    _focusNode.onKeyEvent = ((_, event) {\n      final keys = HardwareKeyboard.instance.logicalKeysPressed;\n      final key = event.logicalKey;\n      if (!keys.contains(key)) {\n        return KeyEventResult.ignored;\n      }\n      if (key == LogicalKeyboardKey.arrowUp) {\n        _controller.moveCursor(AxisDirection.up);\n        return KeyEventResult.handled;\n      } else if (key == LogicalKeyboardKey.arrowDown) {\n        _controller.moveCursor(AxisDirection.down);\n        return KeyEventResult.handled;\n      } else if (key == LogicalKeyboardKey.arrowLeft) {\n        _controller.selection.endIndex;\n        _controller.moveCursor(AxisDirection.left);\n        return KeyEventResult.handled;\n      } else if (key == LogicalKeyboardKey.arrowRight) {\n        _controller.moveCursor(AxisDirection.right);\n        return KeyEventResult.handled;\n      }\n      return KeyEventResult.ignored;\n    });\n  }\n\n  @override\n  void dispose() {\n    _findController.dispose();\n    _controller.dispose();\n    _focusNode.dispose();\n    super.dispose();\n  }\n\n  Widget _wrapController(EditingValueChangeBuilder builder) {\n    return ValueListenableBuilder(\n      valueListenable: _controller,\n      builder: (_, value, _) {\n        return builder(value);\n      },\n    );\n  }\n\n  Widget _wrapTitleController(TextEditingValueChangeBuilder builder) {\n    return ValueListenableBuilder(\n      valueListenable: _titleController,\n      builder: (_, value, _) {\n        return builder(value);\n      },\n    );\n  }\n\n  void _handleSearch() {\n    _findController.findMode();\n  }\n\n  Future<void> _handleImport() async {\n    final option = await globalState.showCommonDialog<ImportOption>(\n      child: _ImportOptionsDialog(),\n    );\n    if (option == null) {\n      return;\n    }\n    if (option == ImportOption.file) {\n      final file = await picker.pickerFile();\n      if (file == null) {\n        return;\n      }\n      final bytes = file.bytes;\n      if (bytes == null) {\n        return;\n      }\n      final res = utf8.decode(bytes);\n      _controller.text = res;\n      return;\n    }\n    final url = await globalState.showCommonDialog(\n      child: InputDialog(\n        title: '导入',\n        value: '',\n        labelText: appLocalizations.url,\n        validator: (value) {\n          if (value == null || value.isEmpty) {\n            return appLocalizations.emptyTip(appLocalizations.value);\n          }\n          if (!value.isUrl) {\n            return appLocalizations.urlTip(appLocalizations.value);\n          }\n          return null;\n        },\n      ),\n    );\n    if (url == null) {\n      return;\n    }\n    final res = await request.getTextResponseForUrl(url);\n    _controller.text = res.data;\n    widget.onUrlImport?.call(url);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final isMobileView = ref.watch(isMobileViewProvider);\n    return CommonPopScope(\n      onPop: () async {\n        if (widget.onPop == null) {\n          return true;\n        }\n        final res = await widget.onPop!(\n          context,\n          _titleController.text,\n          _controller.text,\n        );\n        if (res && context.mounted) {\n          return true;\n        }\n        return false;\n      },\n      child: CommonScaffold(\n        appBar: AppBar(\n          title: TextField(\n            enabled: widget.titleEditable && !widget.readOnly,\n            controller: _titleController,\n            decoration: InputDecoration(\n              border: _NoInputBorder(),\n              hintText: appLocalizations.unnamed,\n            ),\n            style: context.textTheme.titleLarge,\n            autofocus: false,\n          ),\n          actions: genActions([\n            if (widget.onSave != null && !widget.readOnly)\n              _wrapController(\n                (value) => _wrapTitleController(\n                  (value) => IconButton(\n                    onPressed:\n                        _controller.text != widget.content ||\n                            _titleController.text != widget.title\n                        ? () {\n                            widget.onSave!(\n                              context,\n                              _titleController.text,\n                              _controller.text,\n                            );\n                          }\n                        : null,\n                    icon: const Icon(Icons.save_sharp),\n                  ),\n                ),\n              ),\n            if (widget.supportRemoteDownload && !widget.readOnly)\n              IconButton(\n                onPressed: _handleImport,\n                icon: Icon(Icons.arrow_downward),\n              ),\n            _wrapController(\n              (value) => CommonPopupBox(\n                targetBuilder: (open) {\n                  return IconButton(\n                    onPressed: () {\n                      open(offset: Offset(-20, 20));\n                    },\n                    icon: const Icon(Icons.more_vert),\n                  );\n                },\n                popup: CommonPopupMenu(\n                  items: [\n                    PopupMenuItemData(\n                      icon: Icons.search,\n                      label: appLocalizations.search,\n                      onPressed: _handleSearch,\n                    ),\n                    PopupMenuItemData(\n                      icon: Icons.undo,\n                      label: appLocalizations.undo,\n                      onPressed: widget.readOnly\n                          ? null\n                          : (_controller.canUndo ? _controller.undo : null),\n                    ),\n                    PopupMenuItemData(\n                      icon: Icons.redo,\n                      label: appLocalizations.redo,\n                      onPressed: widget.readOnly\n                          ? null\n                          : (_controller.canRedo ? _controller.redo : null),\n                    ),\n                  ],\n                ),\n              ),\n            ),\n          ]),\n        ),\n        body: CodeEditor(\n          findController: _findController,\n          findBuilder: (context, controller, readOnly) => FindPanel(\n            controller: controller,\n            readOnly: readOnly,\n            isMobileView: isMobileView,\n          ),\n          padding: EdgeInsets.only(right: 16),\n          autocompleteSymbols: true,\n          focusNode: _focusNode,\n          readOnly: widget.readOnly,\n          scrollbarBuilder: (context, child, details) {\n            return CommonScrollBar(\n              controller: details.controller,\n              child: child,\n            );\n          },\n          toolbarController: ContextMenuControllerImpl(),\n          indicatorBuilder:\n              (context, editingController, chunkController, notifier) {\n                return Row(\n                  children: [\n                    DefaultCodeLineNumber(\n                      controller: editingController,\n                      notifier: notifier,\n                    ),\n                    DefaultCodeChunkIndicator(\n                      width: 20,\n                      controller: chunkController,\n                      notifier: notifier,\n                    ),\n                  ],\n                );\n              },\n          shortcutsActivatorsBuilder: DefaultCodeShortcutsActivatorsBuilder(),\n          controller: _controller,\n          style: CodeEditorStyle(\n            fontSize: context.textTheme.bodyLarge?.fontSize?.ap,\n            fontFamily: FontFamily.jetBrainsMono.value,\n            codeTheme: CodeHighlightTheme(\n              languages: {\n                if (widget.languages.contains(Language.yaml))\n                  'yaml': CodeHighlightThemeMode(mode: langYaml),\n                if (widget.languages.contains(Language.javaScript))\n                  'javascript': CodeHighlightThemeMode(mode: langJavascript),\n              },\n              theme: atomOneLightTheme,\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n\nconst double _kDefaultFindPanelHeight = 52;\n\nclass FindPanel extends StatelessWidget implements PreferredSizeWidget {\n  final CodeFindController controller;\n  final bool readOnly;\n  final bool isMobileView;\n  final double height;\n\n  const FindPanel({\n    super.key,\n    required this.controller,\n    required this.readOnly,\n    required this.isMobileView,\n  }) : height =\n           (isMobileView\n               ? _kDefaultFindPanelHeight * 2\n               : _kDefaultFindPanelHeight) +\n           8;\n\n  @override\n  Size get preferredSize =>\n      Size(double.infinity, controller.value == null ? 0 : height);\n\n  @override\n  Widget build(BuildContext context) {\n    if (controller.value == null) {\n      return const SizedBox(width: 0, height: 0);\n    }\n    return Container(\n      padding: EdgeInsets.symmetric(vertical: 12, horizontal: 16),\n      margin: EdgeInsets.only(bottom: 8),\n      color: context.colorScheme.surface,\n      alignment: Alignment.centerLeft,\n      height: height,\n      child: _buildFindInputView(context),\n    );\n  }\n\n  Widget _buildFindInputView(BuildContext context) {\n    final CodeFindValue value = controller.value!;\n    final String result;\n    if (value.result == null) {\n      result = appLocalizations.none;\n    } else {\n      result = '${value.result!.index + 1}/${value.result!.matches.length}';\n    }\n    final bar = Row(\n      crossAxisAlignment: CrossAxisAlignment.center,\n      children: [\n        if (!isMobileView) ...[\n          ConstrainedBox(\n            constraints: BoxConstraints(maxWidth: 360),\n            child: _buildFindInput(context, value),\n          ),\n          SizedBox(width: 12),\n        ],\n        Text(result, style: context.textTheme.bodyMedium),\n        Expanded(\n          child: Row(\n            mainAxisAlignment: MainAxisAlignment.end,\n            spacing: 8,\n            children: [\n              _buildIconButton(\n                onPressed: value.result == null\n                    ? null\n                    : () {\n                        controller.previousMatch();\n                      },\n                icon: Icons.arrow_upward,\n              ),\n              _buildIconButton(\n                onPressed: value.result == null\n                    ? null\n                    : () {\n                        controller.nextMatch();\n                      },\n                icon: Icons.arrow_downward,\n              ),\n              SizedBox(width: 2),\n              IconButton.filledTonal(\n                visualDensity: VisualDensity.compact,\n                onPressed: controller.close,\n                style: ButtonStyle(\n                  padding: WidgetStatePropertyAll(EdgeInsets.all(0)),\n                ),\n                icon: Icon(Icons.close, size: 16),\n              ),\n            ],\n          ),\n        ),\n      ],\n    );\n    if (isMobileView) {\n      return Column(\n        mainAxisAlignment: MainAxisAlignment.spaceBetween,\n        children: [bar, SizedBox(height: 4), _buildFindInput(context, value)],\n      );\n    }\n    return bar;\n  }\n\n  Stack _buildFindInput(BuildContext context, CodeFindValue value) {\n    return Stack(\n      alignment: Alignment.center,\n      children: [\n        _buildTextField(\n          context: context,\n          onSubmitted: () {\n            if (value.result == null) {\n              return;\n            }\n            controller.nextMatch();\n            controller.findInputFocusNode.requestFocus();\n          },\n          controller: controller.findInputController,\n          focusNode: controller.findInputFocusNode,\n        ),\n        Row(\n          mainAxisAlignment: MainAxisAlignment.end,\n          spacing: 8,\n          children: [\n            _buildCheckText(\n              context: context,\n              text: 'Aa',\n              isSelected: value.option.caseSensitive,\n              onPressed: () {\n                controller.toggleCaseSensitive();\n              },\n            ),\n            _buildCheckText(\n              context: context,\n              text: '.*',\n              isSelected: value.option.regex,\n              onPressed: () {\n                controller.toggleRegex();\n              },\n            ),\n            SizedBox(width: 4),\n          ],\n        ),\n      ],\n    );\n  }\n\n  Widget _buildTextField({\n    required BuildContext context,\n    required TextEditingController controller,\n    required FocusNode focusNode,\n    required VoidCallback onSubmitted,\n  }) {\n    return TextField(\n      maxLines: 1,\n      focusNode: focusNode,\n      style: context.textTheme.bodyMedium,\n      decoration: InputDecoration(\n        border: OutlineInputBorder(),\n        contentPadding: EdgeInsets.symmetric(horizontal: 12),\n      ),\n      onSubmitted: (_) {\n        onSubmitted();\n      },\n      controller: controller,\n    );\n  }\n\n  Widget _buildCheckText({\n    required BuildContext context,\n    required String text,\n    required bool isSelected,\n    required VoidCallback onPressed,\n  }) {\n    return SizedBox(\n      width: 28,\n      height: 28,\n      child: MouseRegion(\n        cursor: SystemMouseCursors.click,\n        child: isSelected\n            ? IconButton.filledTonal(\n                onPressed: onPressed,\n                padding: EdgeInsets.all(2),\n                icon: Text(text, style: context.textTheme.bodySmall),\n              )\n            : IconButton(\n                onPressed: onPressed,\n                padding: EdgeInsets.all(2),\n                icon: Text(text, style: context.textTheme.bodySmall),\n              ),\n      ),\n    );\n  }\n\n  Widget _buildIconButton({required IconData icon, VoidCallback? onPressed}) {\n    return IconButton(\n      visualDensity: VisualDensity.compact,\n      onPressed: onPressed,\n      style: ButtonStyle(padding: WidgetStatePropertyAll(EdgeInsets.all(0))),\n      icon: Icon(icon, size: 16),\n    );\n  }\n}\n\nclass ContextMenuControllerImpl implements SelectionToolbarController {\n  OverlayEntry? _overlayEntry;\n  bool _isFirstRender = true;\n\n  void _removeOverLayEntry() {\n    _overlayEntry?.remove();\n    _overlayEntry = null;\n    _isFirstRender = true;\n  }\n\n  @override\n  void hide(BuildContext context) {\n    _removeOverLayEntry();\n  }\n\n  @override\n  void show({\n    required context,\n    required controller,\n    required anchors,\n    renderRect,\n    required layerLink,\n    required ValueNotifier<bool> visibility,\n  }) {\n    _removeOverLayEntry();\n    _overlayEntry ??= OverlayEntry(\n      builder: (context) => CodeEditorTapRegion(\n        child: ValueListenableBuilder(\n          valueListenable: controller,\n          builder: (_, _, child) {\n            final isNotEmpty = controller.selectedText.isNotEmpty;\n            final isAllSelected = controller.isAllSelected;\n            final hasSelected = controller.selectedText.isNotEmpty;\n            List<PopupMenuItemData> menus = [\n              if (isNotEmpty)\n                PopupMenuItemData(\n                  label: appLocalizations.copy,\n                  onPressed: controller.copy,\n                ),\n              PopupMenuItemData(\n                label: appLocalizations.paste,\n                onPressed: controller.paste,\n              ),\n              if (isNotEmpty)\n                PopupMenuItemData(\n                  label: appLocalizations.cut,\n                  onPressed: controller.cut,\n                ),\n              if (hasSelected && !isAllSelected)\n                PopupMenuItemData(\n                  label: appLocalizations.selectAll,\n                  onPressed: controller.selectAll,\n                ),\n            ];\n            if (_isFirstRender) {\n              _isFirstRender = false;\n            } else if (controller.selectedText.isEmpty) {\n              _removeOverLayEntry();\n            }\n            return TextSelectionToolbar(\n              anchorAbove: anchors.primaryAnchor,\n              anchorBelow: anchors.secondaryAnchor ?? Offset.zero,\n              children: menus.asMap().entries.map((\n                MapEntry<int, PopupMenuItemData> entry,\n              ) {\n                return TextSelectionToolbarTextButton(\n                  padding: TextSelectionToolbarTextButton.getPadding(\n                    entry.key,\n                    menus.length,\n                  ),\n                  alignment: AlignmentDirectional.centerStart,\n                  onPressed: () {\n                    if (entry.value.onPressed == null) {\n                      return;\n                    }\n                    entry.value.onPressed!();\n                    _removeOverLayEntry();\n                  },\n                  child: Text(entry.value.label),\n                );\n              }).toList(),\n            );\n          },\n        ),\n      ),\n    );\n    Overlay.of(context).insert(_overlayEntry!);\n  }\n}\n\nclass _NoInputBorder extends InputBorder {\n  const _NoInputBorder() : super(borderSide: BorderSide.none);\n\n  @override\n  _NoInputBorder copyWith({BorderSide? borderSide}) => const _NoInputBorder();\n\n  @override\n  bool get isOutline => false;\n\n  @override\n  EdgeInsetsGeometry get dimensions => EdgeInsets.zero;\n\n  @override\n  _NoInputBorder scale(double t) => const _NoInputBorder();\n\n  @override\n  Path getInnerPath(Rect rect, {TextDirection? textDirection}) {\n    return Path()..addRect(rect);\n  }\n\n  @override\n  Path getOuterPath(Rect rect, {TextDirection? textDirection}) {\n    return Path()..addRect(rect);\n  }\n\n  @override\n  void paintInterior(\n    Canvas canvas,\n    Rect rect,\n    Paint paint, {\n    TextDirection? textDirection,\n  }) {\n    canvas.drawRect(rect, paint);\n  }\n\n  @override\n  bool get preferPaintInterior => true;\n\n  @override\n  void paint(\n    Canvas canvas,\n    Rect rect, {\n    double? gapStart,\n    double gapExtent = 0.0,\n    double gapPercentage = 0.0,\n    TextDirection? textDirection,\n  }) {}\n}\n\nclass _ImportOptionsDialog extends StatefulWidget {\n  const _ImportOptionsDialog();\n\n  @override\n  State<_ImportOptionsDialog> createState() => _ImportOptionsDialogState();\n}\n\nclass _ImportOptionsDialogState extends State<_ImportOptionsDialog> {\n  void _handleOnTab(ImportOption value) {\n    Navigator.of(context).pop(value);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return CommonDialog(\n      title: appLocalizations.import,\n      padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16),\n      child: Wrap(\n        children: [\n          ListItem(\n            onTap: () {\n              _handleOnTab(ImportOption.url);\n            },\n            title: Text(appLocalizations.importUrl),\n          ),\n          ListItem(\n            onTap: () {\n              _handleOnTab(ImportOption.file);\n            },\n            title: Text(appLocalizations.importFile),\n          ),\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/pages/home.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/providers/providers.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\ntypedef OnSelected = void Function(int index);\n\nclass HomePage extends StatefulWidget {\n  const HomePage({super.key});\n\n  @override\n  State<HomePage> createState() => _HomePageState();\n}\n\nclass _HomePageState extends State<HomePage> {\n  final Map<int, FocusNode> _navFocusNodes = {};\n\n  FocusNode _getNavFocusNode(int index) {\n    return _navFocusNodes.putIfAbsent(index, () => FocusNode());\n  }\n\n  @override\n  void dispose() {\n    for (final node in _navFocusNodes.values) {\n      node.dispose();\n    }\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return HomeBackScope(\n      child: Material(\n        color: context.colorScheme.surface,\n        child: Consumer(\n          builder: (context, ref, child) {\n            final state = ref.watch(navigationStateProvider);\n            final isMobile = state.viewMode == ViewMode.mobile;\n            final navigationItems = state.navigationItems;\n            final currentIndex = state.currentIndex;\n            final bottomNavigationBar = globalState.isAndroidTV\n                ? _buildTVBottomNavBar(\n                    context,\n                    navigationItems: navigationItems,\n                    currentIndex: currentIndex,\n                  )\n                : GoogleBottomNavBar(\n                    navigationItems: navigationItems,\n                    selectedIndex: currentIndex,\n                    onTabChange: (index) {\n                      globalState.appController.toPage(navigationItems[index].label);\n                    },\n                  );\n            if (isMobile) {\n              return AnnotatedRegion<SystemUiOverlayStyle>(\n                value: globalState.appState.systemUiOverlayStyle.copyWith(\n                  systemNavigationBarColor:\n                      context.colorScheme.surfaceContainer,\n                ),\n                child: Column(\n                  children: [\n                    Flexible(\n                      flex: 1,\n                      child: MediaQuery.removePadding(\n                        removeTop: false,\n                        removeBottom: true,\n                        removeLeft: true,\n                        removeRight: true,\n                        context: context,\n                        child: child!,\n                      ),\n                    ),\n                    MediaQuery.removePadding(\n                      removeTop: true,\n                      removeBottom: false,\n                      removeLeft: true,\n                      removeRight: true,\n                      context: context,\n                      child: bottomNavigationBar,\n                    ),\n                  ],\n                ),\n              );\n            } else {\n              return child!;\n            }\n          },\n          child: Consumer(\n            builder: (_, ref, _) {\n              final navigationItems = ref\n                  .watch(currentNavigationItemsStateProvider)\n                  .value;\n              final isMobile = ref.watch(isMobileViewProvider);\n              return _HomePageView(\n                navigationItems: navigationItems,\n                pageBuilder: (_, index) {\n                  final navigationItem = navigationItems[index];\n                  final navigationView = navigationItem.builder(context);\n                  return KeepScope(\n                    key: ValueKey(navigationItem.label),\n                    keep: navigationItem.keep,\n                    child: isMobile\n                        ? navigationView\n                        : Navigator(\n                            pages: [MaterialPage(child: navigationView)],\n                            onDidRemovePage: (_) {},\n                          ),\n                  );\n                },\n              );\n            },\n          ),\n        ),\n      ),\n    );\n  }\n\n  Widget _buildTVBottomNavBar(\n    BuildContext context, {\n    required List<NavigationItem> navigationItems,\n    required int currentIndex,\n  }) {\n    return Container(\n      decoration: BoxDecoration(\n        color: context.colorScheme.surfaceContainer,\n        boxShadow: [\n          BoxShadow(\n            blurRadius: 20,\n            color: Colors.black.withValues(alpha: 0.15),\n          ),\n        ],\n      ),\n      child: SafeArea(\n        child: FocusTraversalGroup(\n          policy: WidgetOrderTraversalPolicy(),\n          child: Row(\n            mainAxisAlignment: MainAxisAlignment.spaceAround,\n            children: navigationItems.asMap().entries.map((entry) {\n              final index = entry.key;\n              final item = entry.value;\n              final isSelected = index == currentIndex;\n              final focusNode = _getNavFocusNode(index);\n              return FocusTraversalOrder(\n                order: NumericFocusOrder(index.toDouble()),\n                child: AnimatedBuilder(\n                  animation: focusNode,\n                  builder: (context, child) {\n                    final isFocused = focusNode.hasFocus;\n                    return InkWell(\n                      focusNode: focusNode,\n                      onTap: () {\n                        globalState.appController.toPage(item.label);\n                      },\n                      onFocusChange: (hasFocus) {\n                        if (hasFocus && !isSelected) {\n                          globalState.appController.toPage(item.label);\n                        }\n                      },\n                      child: Container(\n                        padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 8),\n                        decoration: BoxDecoration(\n                          color: isSelected\n                              ? context.colorScheme.secondaryContainer\n                              : Colors.transparent,\n                          borderRadius: BorderRadius.circular(12),\n                          border: isFocused\n                              ? Border.all(\n                                  color: context.colorScheme.primary,\n                                  width: 2,\n                                )\n                              : Border.all(\n                                  color: Colors.transparent,\n                                  width: 2,\n                                ),\n                        ),\n                          child: Column(\n                            mainAxisSize: MainAxisSize.min,\n                            children: [\n                              IconTheme(\n                                data: IconThemeData(\n                                  color: isSelected\n                                      ? context.colorScheme.onSecondaryContainer\n                                      : context.colorScheme.onSurfaceVariant,\n                                  size: 24,\n                                ),\n                                child: item.icon,\n                              ),\n                              const SizedBox(height: 4),\n                              Text(\n                                _getLocalizedLabel(item.label),\n                                style: TextStyle(\n                                  color: isSelected\n                                      ? context.colorScheme.onSecondaryContainer\n                                      : context.colorScheme.onSurfaceVariant,\n                                  fontSize: 12,\n                                ),\n                              ),\n                            ],\n                          ),\n                        ),\n                      );\n                    },\n                  ),\n              );\n            }).toList(),\n          ),\n        ),\n      ),\n    );\n  }\n\n  String _getLocalizedLabel(PageLabel label) {\n    switch (label) {\n      case PageLabel.dashboard:\n        return appLocalizations.dashboard;\n      case PageLabel.proxies:\n        return appLocalizations.proxies;\n      case PageLabel.profiles:\n        return appLocalizations.profiles;\n      case PageLabel.tools:\n        return appLocalizations.tools;\n      case PageLabel.logs:\n        return appLocalizations.logs;\n      case PageLabel.requests:\n        return appLocalizations.requests;\n      case PageLabel.resources:\n        return appLocalizations.resources;\n      case PageLabel.script:\n        return appLocalizations.script;\n      case PageLabel.connections:\n        return appLocalizations.connections;\n    }\n  }\n}\n\nclass _HomePageView extends ConsumerStatefulWidget {\n  final IndexedWidgetBuilder pageBuilder;\n  final List<NavigationItem> navigationItems;\n\n  const _HomePageView({\n    required this.pageBuilder,\n    required this.navigationItems,\n  });\n\n  @override\n  ConsumerState createState() => _HomePageViewState();\n}\n\nclass _HomePageViewState extends ConsumerState<_HomePageView> {\n  late PageController _pageController;\n  late final ProviderSubscription<PageLabel> _pageLabelSubscription;\n\n  @override\n  initState() {\n    super.initState();\n    _pageController = PageController(initialPage: _pageIndex);\n    _pageLabelSubscription = ref.listenManual(currentPageLabelProvider, (\n      prev,\n      next,\n    ) {\n      if (prev != next) {\n        _toPage(next);\n      }\n    });\n  }\n\n  @override\n  void didUpdateWidget(covariant _HomePageView oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    if (oldWidget.navigationItems.length != widget.navigationItems.length) {\n      _updatePageController();\n    }\n  }\n\n  int get _pageIndex {\n    return widget.navigationItems.indexWhere(\n      (item) => item.label == globalState.appState.pageLabel,\n    );\n  }\n\n  Future<void> _toPage(\n    PageLabel pageLabel, [\n    bool ignoreAnimateTo = false,\n  ]) async {\n    if (!mounted) {\n      return;\n    }\n    final index = widget.navigationItems.indexWhere(\n      (item) => item.label == pageLabel,\n    );\n    if (index == -1) {\n      return;\n    }\n    \n    FocusManager.instance.primaryFocus?.unfocus();\n    \n    final isAnimateToPage = ref.read(appSettingProvider).isAnimateToPage;\n    final isMobile = ref.read(isMobileViewProvider);\n    if (isAnimateToPage && isMobile && !ignoreAnimateTo) {\n      await _pageController.animateToPage(\n        index,\n        duration: kTabScrollDuration,\n        curve: Curves.easeOut,\n      );\n    } else {\n      _pageController.jumpToPage(index);\n    }\n  }\n\n  void _updatePageController() {\n    final pageLabel = ref.read(currentPageLabelProvider);\n    _toPage(pageLabel, true);\n  }\n\n  @override\n  void dispose() {\n    _pageLabelSubscription.close();\n    _pageController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return PageView.builder(\n      controller: _pageController,\n      physics: const NeverScrollableScrollPhysics(),\n      itemCount: widget.navigationItems.length,\n      itemBuilder: (context, index) {\n        return widget.pageBuilder(context, index);\n      },\n    );\n  }\n}\n\nclass HomeBackScope extends ConsumerStatefulWidget {\n  final Widget child;\n\n  const HomeBackScope({super.key, required this.child});\n\n  @override\n  ConsumerState<HomeBackScope> createState() => _HomeBackScopeState();\n}\n\nclass _HomeBackScopeState extends ConsumerState<HomeBackScope> {\n  int? sdkInt;\n\n  @override\n  void initState() {\n    super.initState();\n    if (system.isAndroid) {\n      system.version.then((value) {\n        if (mounted) {\n          setState(() {\n            sdkInt = value;\n          });\n        }\n      });\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    if (system.isAndroid) {\n      if (sdkInt == null) {\n        return widget.child;\n      }\n\n      final backBlock = ref.watch(backBlockProvider);\n      final currentPage = ref.watch(currentPageLabelProvider);\n      final rootPageLabels = ref.watch(\n        currentNavigationItemsStateProvider.select(\n          (state) => state.value.map((item) => item.label).toSet(),\n        ),\n      );\n      final isCurrentRootPage = rootPageLabels.contains(currentPage);\n\n      if (sdkInt! >= 31) {\n        return PopScope(\n          canPop: !backBlock && isCurrentRootPage,\n          onPopInvokedWithResult: (didPop, _) async {\n            if (didPop || backBlock) return;\n            if (!isCurrentRootPage) {\n              globalState.appController.toPage(PageLabel.dashboard);\n            }\n          },\n          child: widget.child,\n        );\n      }\n\n      return PopScope(\n        canPop: false,\n        onPopInvokedWithResult: (didPop, _) async {\n          if (didPop || backBlock) return;\n          if (!isCurrentRootPage) {\n            globalState.appController.toPage(PageLabel.dashboard);\n            return;\n          }\n          final canPop = Navigator.canPop(context);\n          if (canPop) {\n            Navigator.pop(context);\n          } else {\n            await globalState.appController.handleBackOrExit();\n          }\n        },\n        child: widget.child,\n      );\n    }\n    return widget.child;\n  }\n}\n"
  },
  {
    "path": "lib/pages/pages.dart",
    "content": "export 'home.dart';\nexport 'scan.dart';\nexport 'editor.dart';\n"
  },
  {
    "path": "lib/pages/scan.dart",
    "content": "import 'dart:async';\nimport 'dart:math';\n\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/plugins/app.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/activate_box.dart';\nimport 'package:flutter/material.dart';\nimport 'package:mobile_scanner/mobile_scanner.dart';\n\nclass ScanPage extends StatefulWidget {\n  const ScanPage({super.key});\n\n  @override\n  State<ScanPage> createState() => _ScanPageState();\n}\n\nclass _ScanPageState extends State<ScanPage> with WidgetsBindingObserver {\n  MobileScannerController controller = MobileScannerController(\n    detectionSpeed: DetectionSpeed.noDuplicates,\n    formats: const [BarcodeFormat.qrCode],\n    autoStart: false, // Disable autoStart to manually control initialization\n  );\n\n  StreamSubscription<Object?>? _subscription;\n  bool _permissionDenied = false;\n  bool _permissionChecking = false;\n\n  @override\n  void initState() {\n    super.initState();\n    WidgetsBinding.instance.addObserver(this);\n    _subscription = controller.barcodes.listen(_handleBarcode);\n    // Check permission and start camera\n    WidgetsBinding.instance.addPostFrameCallback((_) {\n      _checkCameraPermission();\n    });\n  }\n\n  void _handleBarcode(BarcodeCapture barcodeCapture) {\n    final barcode = barcodeCapture.barcodes.first;\n    if (barcode.type == BarcodeType.url) {\n      Navigator.pop<String>(context, barcode.rawValue);\n    } else {\n      Navigator.pop(context);\n    }\n  }\n\n  @override\n  void didChangeAppLifecycleState(AppLifecycleState state) {\n    super.didChangeAppLifecycleState(state);\n    switch (state) {\n      case AppLifecycleState.detached:\n      case AppLifecycleState.hidden:\n      case AppLifecycleState.paused:\n        return;\n      case AppLifecycleState.resumed:\n        _subscription ??= controller.barcodes.listen(_handleBarcode);\n        // Recheck permission when returning from settings\n        _checkCameraPermission();\n        return;\n      case AppLifecycleState.inactive:\n        unawaited(_subscription?.cancel());\n        _subscription = null;\n        if (controller.value.isRunning) {\n          unawaited(controller.stop());\n        }\n        return;\n    }\n  }\n\n  Future<void> _checkCameraPermission() async {\n    if (_permissionChecking) return; // Prevent concurrent checks\n    \n    setState(() {\n      _permissionChecking = true;\n    });\n    \n    final granted = await app.hasCameraPermission();\n    if (!mounted) return;\n    \n    setState(() {\n      _permissionDenied = !granted;\n      _permissionChecking = false;\n    });\n    \n    if (!granted) {\n      if (controller.value.isRunning) {\n        await controller.stop();\n      }\n    } else {\n      // Start camera only if not already running\n      if (!controller.value.isRunning && !controller.value.isInitialized) {\n        try {\n          await controller.start();\n        } catch (e) {\n          // Handle start error silently\n          commonPrint.log('Camera start error: $e');\n        }\n      }\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    double sideLength = min(400, MediaQuery.of(context).size.width * 0.67);\n    final scanWindow = Rect.fromCenter(\n      center: MediaQuery.sizeOf(context).center(Offset.zero),\n      width: sideLength,\n      height: sideLength,\n    );\n    return Scaffold(\n      body: Stack(\n        children: [\n          Center(\n            child: _permissionChecking\n                ? Container(color: Colors.black)\n                : (_permissionDenied\n                      ? _buildPermissionDeniedView(context)\n                      : MobileScanner(\n                          controller: controller,\n                          scanWindow: scanWindow,\n                          errorBuilder: (context, error) {\n                            if (error.errorCode ==\n                                MobileScannerErrorCode.permissionDenied) {\n                              if (!_permissionDenied && mounted) {\n                                WidgetsBinding.instance.addPostFrameCallback((\n                                  _,\n                                ) {\n                                  if (!mounted) return;\n                                  setState(() {\n                                    _permissionDenied = true;\n                                  });\n                                });\n                              }\n                              unawaited(controller.stop());\n                              return _buildPermissionDeniedView(context);\n                            }\n                            return _buildErrorView(context, error);\n                          },\n                        )),\n          ),\n          if (!_permissionDenied)\n            CustomPaint(painter: ScannerOverlay(scanWindow: scanWindow)),\n          AppBar(\n            backgroundColor: Colors.transparent,\n            automaticallyImplyLeading: false,\n            leading: IconButton(\n              style: const ButtonStyle(\n                iconSize: WidgetStatePropertyAll(32),\n                foregroundColor: WidgetStatePropertyAll(Colors.white),\n              ),\n              onPressed: () {\n                Navigator.of(context).pop();\n              },\n              icon: const Icon(Icons.close),\n            ),\n            actions: [\n              if (!_permissionDenied)\n                ValueListenableBuilder<MobileScannerState>(\n                  valueListenable: controller,\n                  builder: (context, state, _) {\n                    var icon = const Icon(Icons.flash_off);\n                    var backgroundColor = Colors.black12;\n                    switch (state.torchState) {\n                      case TorchState.off:\n                        icon = const Icon(Icons.flash_off);\n                        backgroundColor = Colors.black12;\n                      case TorchState.on:\n                        icon = const Icon(Icons.flash_on);\n                        backgroundColor = Colors.orange;\n                      case TorchState.unavailable:\n                        icon = const Icon(Icons.flash_off);\n                        backgroundColor = Colors.transparent;\n                      case TorchState.auto:\n                        icon = const Icon(Icons.flash_auto);\n                        backgroundColor = Colors.orange;\n                    }\n                    return Container(\n                      margin: const EdgeInsets.symmetric(horizontal: 8),\n                      child: ActivateBox(\n                        active: state.torchState != TorchState.unavailable,\n                        child: IconButton(\n                          color: Colors.white,\n                          icon: icon,\n                          style: ButtonStyle(\n                            foregroundColor: const WidgetStatePropertyAll(\n                              Colors.white,\n                            ),\n                            backgroundColor: WidgetStatePropertyAll(\n                              backgroundColor,\n                            ),\n                          ),\n                          onPressed: () => controller.toggleTorch(),\n                        ),\n                      ),\n                    );\n                  },\n                ),\n            ],\n          ),\n          if (!_permissionDenied)\n            Container(\n              margin: const EdgeInsets.only(bottom: 32),\n              alignment: Alignment.bottomCenter,\n              child: IconButton(\n                color: Colors.white,\n                style: const ButtonStyle(\n                  foregroundColor: WidgetStatePropertyAll(Colors.white),\n                  backgroundColor: WidgetStatePropertyAll(Colors.grey),\n                ),\n                padding: const EdgeInsets.all(16),\n                iconSize: 32.0,\n                onPressed: globalState.appController.addProfileFormQrCode,\n                icon: const Icon(Icons.photo_camera_back),\n              ),\n            ),\n        ],\n      ),\n    );\n  }\n\n  /// 构建权限被拒绝的视图\n  Widget _buildPermissionDeniedView(BuildContext context) {\n    return Container(\n      color: Colors.black,\n      child: Center(\n        child: Padding(\n          padding: const EdgeInsets.all(24.0),\n          child: Column(\n            mainAxisAlignment: MainAxisAlignment.center,\n            children: [\n              const Icon(\n                Icons.camera_alt_outlined,\n                size: 80,\n                color: Colors.white54,\n              ),\n              const SizedBox(height: 24),\n              Text(\n                appLocalizations.cameraPermissionDenied,\n                style: const TextStyle(\n                  color: Colors.white,\n                  fontSize: 20,\n                  fontWeight: FontWeight.bold,\n                ),\n              ),\n              const SizedBox(height: 12),\n              Text(\n                appLocalizations.cameraPermissionDesc,\n                style: const TextStyle(color: Colors.white70, fontSize: 14),\n                textAlign: TextAlign.center,\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n\n  /// Build error view\n  Widget _buildErrorView(BuildContext context, MobileScannerException error) {\n    String errorMessage = 'Camera init failed';\n    switch (error.errorCode) {\n      case MobileScannerErrorCode.controllerUninitialized:\n        errorMessage = 'Camera uninitialized';\n        break;\n      case MobileScannerErrorCode.genericError:\n        errorMessage = 'Camera error';\n        break;\n      case MobileScannerErrorCode.unsupported:\n        errorMessage = 'Scan not supported';\n        break;\n      default:\n        errorMessage = error.errorDetails?.message ?? 'Unknown error';\n    }\n\n    return Container(\n      color: Colors.black,\n      child: Center(\n        child: Padding(\n          padding: const EdgeInsets.all(24.0),\n          child: Column(\n            mainAxisAlignment: MainAxisAlignment.center,\n            children: [\n              const Icon(Icons.error_outline, size: 80, color: Colors.red),\n              const SizedBox(height: 24),\n              Text(\n                errorMessage,\n                style: const TextStyle(color: Colors.white, fontSize: 16),\n                textAlign: TextAlign.center,\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n\n  @override\n  void dispose() {\n    WidgetsBinding.instance.removeObserver(this);\n    unawaited(_subscription?.cancel());\n    _subscription = null;\n    unawaited(controller.dispose());\n    super.dispose();\n  }\n}\n\nclass ScannerOverlay extends CustomPainter {\n  const ScannerOverlay({required this.scanWindow, this.borderRadius = 12.0});\n\n  final Rect scanWindow;\n  final double borderRadius;\n\n  @override\n  void paint(Canvas canvas, Size size) {\n    final backgroundPath = Path()..addRect(Rect.largest);\n\n    final cutoutPath = Path()\n      ..addRRect(\n        RRect.fromRectAndCorners(\n          scanWindow,\n          topLeft: Radius.circular(borderRadius),\n          topRight: Radius.circular(borderRadius),\n          bottomLeft: Radius.circular(borderRadius),\n          bottomRight: Radius.circular(borderRadius),\n        ),\n      );\n\n    final backgroundPaint = Paint()\n      ..color = Colors.black.opacity50\n      ..style = PaintingStyle.fill\n      ..blendMode = BlendMode.dstOut;\n\n    final backgroundWithCutout = Path.combine(\n      PathOperation.difference,\n      backgroundPath,\n      cutoutPath,\n    );\n\n    final borderPaint = Paint()\n      ..color = Colors.white\n      ..style = PaintingStyle.stroke\n      ..strokeWidth = 4.0;\n\n    final borderRect = RRect.fromRectAndCorners(\n      scanWindow,\n      topLeft: Radius.circular(borderRadius),\n      topRight: Radius.circular(borderRadius),\n      bottomLeft: Radius.circular(borderRadius),\n      bottomRight: Radius.circular(borderRadius),\n    );\n\n    canvas.drawPath(backgroundWithCutout, backgroundPaint);\n    canvas.drawRRect(borderRect, borderPaint);\n  }\n\n  @override\n  bool shouldRepaint(ScannerOverlay oldDelegate) {\n    return scanWindow != oldDelegate.scanWindow ||\n        borderRadius != oldDelegate.borderRadius;\n  }\n}\n"
  },
  {
    "path": "lib/plugins/app.dart",
    "content": "import 'dart:async';\n\nimport 'package:bett_box/common/app_localizations.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:flutter/services.dart';\nimport 'package:intl/intl.dart';\n\nclass App {\n  static App? _instance;\n  late MethodChannel methodChannel;\n  Function()? onExit;\n\n  App._internal() {\n    methodChannel = const MethodChannel('app');\n    methodChannel.setMethodCallHandler((call) async {\n      switch (call.method) {\n        case 'exit':\n          await onExit?.call();\n        case 'getText':\n          try {\n            return Intl.message(call.arguments as String);\n          } catch (_) {\n            return '';\n          }\n        default:\n          throw MissingPluginException();\n      }\n    });\n  }\n\n  factory App() {\n    _instance ??= App._internal();\n    return _instance!;\n  }\n\n  Future<bool?> moveTaskToBack() async {\n    return await methodChannel.invokeMethod<bool>('moveTaskToBack');\n  }\n\n  Future<List<Package>> getPackages({bool forceRefresh = false}) async {\n    final packagesRaw = await methodChannel.invokeListMethod<Map<dynamic, dynamic>>(\n      'getPackages',\n      {'forceRefresh': forceRefresh},\n    ) ?? const [];\n    return packagesRaw\n        .map((e) => Package.fromJson(Map<String, Object?>.from(e)))\n        .toList();\n  }\n\n  Future<bool> hasPackageListPermission() async {\n    final result = await methodChannel.invokeMethod<bool>(\n      'hasPackageListPermission',\n    );\n    return result ?? true;\n  }\n\n  Future<bool> hasCameraPermission() async {\n    final result = await methodChannel.invokeMethod<bool>(\n      'hasCameraPermission',\n    );\n    return result ?? true;\n  }\n\n  Future<void> requestPackageListPermission() async {\n    await methodChannel.invokeMethod<void>('requestPackageListPermission');\n  }\n\n  Future<void> openAppSettings() async {\n    await methodChannel.invokeMethod<void>('openAppSettings');\n  }\n\n  Future<List<String>> getChinaPackageNames() async {\n    final packageNamesRaw =\n        await methodChannel.invokeListMethod<String>('getChinaPackageNames') ??\n        const [];\n    return packageNamesRaw.map((e) => e.toString()).toList();\n  }\n\n  Future<bool> openFile(String path) async {\n    return await methodChannel.invokeMethod<bool>('openFile', {'path': path}) ??\n        false;\n  }\n\n  Future<Uint8List?> getPackageIcon(\n    String packageName, {\n    bool forceRefresh = false,\n  }) async {\n    return await methodChannel.invokeMethod<Uint8List>('getPackageIcon', {\n      'packageName': packageName,\n      'forceRefresh': forceRefresh,\n    });\n  }\n\n  Future<bool?> tip(String? message) async {\n    if (message == null || message.isEmpty) return false;\n    return await methodChannel.invokeMethod<bool>('tip', {\n      'message': message,\n    });\n  }\n\n  Future<bool?> initShortcuts() async {\n    return await methodChannel.invokeMethod<bool>(\n      'initShortcuts',\n      appLocalizations.toggle,\n    );\n  }\n\n  Future<bool?> updateExcludeFromRecents(bool value) async {\n    return await methodChannel.invokeMethod<bool>('updateExcludeFromRecents', {\n      'value': value,\n    });\n  }\n\n  Future<int> getSelfLastUpdateTime() async {\n    final result = await methodChannel.invokeMethod<int>(\n      'getSelfLastUpdateTime',\n    );\n    return result ?? 0;\n  }\n\n  Future<bool> isIgnoringBatteryOptimizations() async {\n    final result = await methodChannel.invokeMethod<bool>(\n      'isIgnoringBatteryOptimizations',\n    );\n    return result ?? false;\n  }\n\n  Future<void> requestIgnoreBatteryOptimizations() async {\n    await methodChannel.invokeMethod<void>('requestIgnoreBatteryOptimizations');\n  }\n\n  Future<bool> setLauncherIcon(bool useLightIcon) async {\n    final result = await methodChannel.invokeMethod<bool>('setLauncherIcon', {\n      'useLightIcon': useLightIcon,\n    });\n    return result ?? false;\n  }\n\n  Future<bool> isAndroidTV() async {\n    final result = await methodChannel.invokeMethod<bool>('isAndroidTV');\n    return result ?? false;\n  }\n}\n\nfinal app = App();\n"
  },
  {
    "path": "lib/plugins/service.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\n\nimport 'package:bett_box/common/system.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:flutter/services.dart';\n\nimport '../clash/lib.dart';\n\ntypedef NativeEventCallback = Future<void> Function(String method, dynamic arguments);\n\nclass Service {\n  static final Service _instance = Service._internal();\n  final MethodChannel methodChannel = const MethodChannel('service');\n\n  final List<NativeEventCallback> _nativeEventCallbacks = [];\n\n  Service._internal() {\n    methodChannel.setMethodCallHandler(_handleMethodCall);\n  }\n\n  factory Service() => _instance;\n\n  Future<dynamic> _handleMethodCall(MethodCall call) async {\n    if (call.method == 'runStateChanged') {\n      final state = call.arguments as String?;\n      if (state == 'STOP') {\n        globalState.startTime = null;\n        globalState.appState = globalState.appState.copyWith(runTime: null);\n      }\n    }\n    for (final callback in _nativeEventCallbacks) {\n      await callback(call.method, call.arguments);\n    }\n  }\n\n  void addNativeEventCallback(NativeEventCallback callback) {\n    _nativeEventCallbacks.add(callback);\n  }\n\n  void removeNativeEventCallback(NativeEventCallback callback) {\n    _nativeEventCallbacks.remove(callback);\n  }\n\n  Future<bool?> init() => methodChannel.invokeMethod<bool>('init');\n\n  Future<bool?> destroy() => methodChannel.invokeMethod<bool>('destroy');\n\n  Future<bool?> startVpn() async {\n    final options = await clashLib?.getAndroidVpnOptions();\n    return await methodChannel.invokeMethod<bool>('startVpn', {\n      'data': json.encode(options),\n    });\n  }\n\n  Future<bool?> stopVpn() => methodChannel.invokeMethod<bool>('stopVpn');\n\n  Future<bool?> smartStop() => methodChannel.invokeMethod<bool>('smartStop');\n\n  Future<bool?> smartResume() async {\n    final options = await clashLib?.getAndroidVpnOptions();\n    return await methodChannel.invokeMethod<bool>('smartResume', {\n      'data': json.encode(options),\n    });\n  }\n\n  Future<void> setSmartStopped(bool value) async {\n    await methodChannel.invokeMethod<bool>('setSmartStopped', {'value': value});\n  }\n\n  Future<bool> isSmartStopped() async {\n    return await methodChannel.invokeMethod<bool>('isSmartStopped') ?? false;\n  }\n\n  Future<List<String>> getLocalIpAddresses() async {\n    return await methodChannel.invokeListMethod<String>(\n          'getLocalIpAddresses',\n        ) ??\n        const [];\n  }\n\n  Future<bool?> setQuickResponse(bool enabled) async {\n    return await methodChannel.invokeMethod<bool>('setQuickResponse', {\n      'enabled': enabled,\n    });\n  }\n\n  Future<bool> isServiceEngineRunning() async {\n    return await methodChannel.invokeMethod<bool>('isServiceEngineRunning') ?? false;\n  }\n\n  Future<bool> getStatus() async {\n    return await methodChannel.invokeMethod<bool>('status') ?? false;\n  }\n\n  Future<bool?> reconnectIpc() => methodChannel.invokeMethod<bool>('reconnectIpc');\n}\n\nService? get service =>\n    system.isAndroid && !globalState.isService ? Service() : null;\n"
  },
  {
    "path": "lib/plugins/tile.dart",
    "content": "import 'package:bett_box/common/system.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:flutter/services.dart';\n\nabstract mixin class TileListener {\n  void onStart() {}\n\n  void onStop() {}\n\n  void onDetached() {}\n\n  void onReconnectIpc() {}\n}\n\nclass Tile {\n  static final Tile instance = Tile._();\n\n  final _channel = const MethodChannel('tile');\n  final _listeners = ObserverList<TileListener>();\n\n  Tile._() {\n    _channel.setMethodCallHandler(_methodCallHandler);\n  }\n\n  Future<void> _methodCallHandler(MethodCall call) async {\n    switch (call.method) {\n      case 'start':\n        for (final l in _listeners) { l.onStart(); }\n      case 'stop':\n        for (final l in _listeners) { l.onStop(); }\n      case 'detached':\n        for (final l in _listeners) { l.onDetached(); }\n      case 'reconnectIpc':\n        for (final l in _listeners) { l.onReconnectIpc(); }\n    }\n  }\n\n  bool get hasListeners => _listeners.isNotEmpty;\n\n  void addListener(TileListener listener) => _listeners.add(listener);\n\n  void removeListener(TileListener listener) => _listeners.remove(listener);\n}\n\nfinal tile = system.isAndroid ? Tile.instance : null;\n"
  },
  {
    "path": "lib/plugins/vpn.dart",
    "content": "import 'dart:convert';\n\nimport 'package:bett_box/clash/clash.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:flutter/services.dart';\n\nabstract mixin class VpnListener {\n  void onDnsChanged(String dns) {}\n}\n\nclass Vpn {\n  static final Vpn _instance = Vpn._internal();\n  final MethodChannel methodChannel = const MethodChannel('vpn');\n\n  Vpn._internal() {\n    methodChannel.setMethodCallHandler((call) async {\n      switch (call.method) {\n        case 'gc':\n          clashCore.requestGc();\n        case 'closeConnections':\n          clashCore.closeConnections();\n        case 'status':\n          return clashLibHandler?.getRunTime() != null;\n        case 'dnsChanged':\n          for (final listener in _listeners) {\n            listener.onDnsChanged(call.arguments as String);\n          }\n        default:\n      }\n    });\n  }\n\n  factory Vpn() => _instance;\n\n  final ObserverList<VpnListener> _listeners = ObserverList<VpnListener>();\n\n  Future<bool?> start(AndroidVpnOptions options) async {\n    return await methodChannel.invokeMethod<bool>('start', {\n      'data': jsonEncode(options),\n    });\n  }\n\n  Future<bool?> stop() => methodChannel.invokeMethod<bool>('stop');\n\n  Future<List<String>> getLocalIpAddresses() async {\n    return await methodChannel.invokeListMethod<String>(\n          'getLocalIpAddresses',\n        ) ??\n        const [];\n  }\n\n  Future<void> setSmartStopped(bool value) async {\n    await methodChannel.invokeMethod<bool>('setSmartStopped', {'value': value});\n  }\n\n  Future<bool> isSmartStopped() async {\n    return await methodChannel.invokeMethod<bool>('isSmartStopped') ?? false;\n  }\n\n  Future<bool?> smartStop() => methodChannel.invokeMethod<bool>('smartStop');\n\n  Future<bool?> smartResume(AndroidVpnOptions options) async {\n    return await methodChannel.invokeMethod<bool>('smartResume', {\n      'data': jsonEncode(options),\n    });\n  }\n\n  Future<bool> getStatus() async {\n    return await methodChannel.invokeMethod<bool>('status') ?? false;\n  }\n\n  void addListener(VpnListener listener) => _listeners.add(listener);\n\n  void removeListener(VpnListener listener) => _listeners.remove(listener);\n}\n\nVpn? get vpn => globalState.isService ? Vpn() : null;\n"
  },
  {
    "path": "lib/providers/app.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:riverpod_annotation/riverpod_annotation.dart';\n\npart 'generated/app.g.dart';\n\n@riverpod\nclass RealTunEnable extends _$RealTunEnable with AutoDisposeNotifierMixin {\n  @override\n  bool build() {\n    return globalState.appState.realTunEnable;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.appState = globalState.appState.copyWith(realTunEnable: value);\n  }\n}\n\n@riverpod\nclass Logs extends _$Logs with AutoDisposeNotifierMixin {\n  @override\n  FixedList<Log> build() {\n    return globalState.appState.logs;\n  }\n\n  void addLog(Log value) {\n    state = state.copyWith()..add(value);\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.appState = globalState.appState.copyWith(logs: value);\n  }\n\n  void clearLogs() {\n    state = FixedList(maxLength);\n  }\n}\n\n// Search and filter providers for logs\nfinal logsSearchProvider = StateProvider<String>((ref) => '');\nfinal logsKeywordsProvider = StateProvider<List<String>>((ref) => []);\n\nfinal filteredLogsProvider = Provider<List<Log>>((ref) {\n  final logs = ref.watch(logsProvider.select((s) => s.list));\n  final query = ref.watch(logsSearchProvider).toLowerCase();\n  final keywords = ref.watch(logsKeywordsProvider);\n\n  return logs.where((item) {\n    if (query.isNotEmpty) {\n      final matchesQuery = item.payload.toLowerCase().contains(query) ||\n          item.logLevel.name.toLowerCase().contains(query) ||\n          item.dateTime.toLowerCase().contains(query);\n      if (!matchesQuery) return false;\n    }\n    if (keywords.isNotEmpty) {\n      final itemStr = '${item.payload} ${item.logLevel.name} ${item.dateTime}'.toLowerCase();\n      final matchesKeywords = keywords.every((keyword) => itemStr.contains(keyword.toLowerCase()));\n      if (!matchesKeywords) return false;\n    }\n    return true;\n  }).toList();\n});\n\n@riverpod\nclass Requests extends _$Requests with AutoDisposeNotifierMixin {\n  @override\n  FixedList<TrackerInfo> build() {\n    return globalState.appState.requests;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.appState = globalState.appState.copyWith(requests: value);\n  }\n\n  void addRequest(TrackerInfo value) {\n    state = state.copyWith()..add(value);\n  }\n\n  void clearRequests() {\n    state = FixedList(maxLength);\n  }\n}\n\n// Search and filter providers for requests\nfinal requestsSearchProvider = StateProvider<String>((ref) => '');\nfinal requestsKeywordsProvider = StateProvider<List<String>>((ref) => []);\n\nfinal filteredRequestsProvider = Provider<List<TrackerInfo>>((ref) {\n  final requests = ref.watch(requestsProvider.select((s) => s.list));\n  final query = ref.watch(requestsSearchProvider).toLowerCase().trim();\n  final keywords = ref.watch(requestsKeywordsProvider);\n\n  return requests.where((item) {\n    if (query.isNotEmpty) {\n      final networkText = item.metadata.network.toLowerCase();\n      final hostText = item.metadata.host.toLowerCase();\n      final destinationIPText = item.metadata.destinationIP.toLowerCase();\n      final processText = item.metadata.process.toLowerCase();\n      final chainsText = item.chains.join('').toLowerCase();\n      final matchesQuery = networkText.contains(query) ||\n          hostText.contains(query) ||\n          destinationIPText.contains(query) ||\n          processText.contains(query) ||\n          chainsText.contains(query);\n      if (!matchesQuery) return false;\n    }\n    if (keywords.isNotEmpty) {\n      final chains = item.chains;\n      final process = item.metadata.process;\n      final matchesKeywords = {...chains, process}.containsAll(keywords);\n      if (!matchesKeywords) return false;\n    }\n    return true;\n  }).toList();\n});\n\n@riverpod\nclass Providers extends _$Providers with AutoDisposeNotifierMixin {\n  @override\n  List<ExternalProvider> build() {\n    return globalState.appState.providers;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.appState = globalState.appState.copyWith(providers: value);\n  }\n\n  void setProvider(ExternalProvider? provider) {\n    if (provider == null) return;\n    final index = state.indexWhere((item) => item.name == provider.name);\n    if (index == -1) return;\n    state = List.from(state)..[index] = provider;\n  }\n}\n\n@riverpod\nclass Packages extends _$Packages with AutoDisposeNotifierMixin {\n  @override\n  List<Package> build() {\n    return globalState.appState.packages;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.appState = globalState.appState.copyWith(packages: value);\n  }\n}\n\n@riverpod\nclass SystemBrightness extends _$SystemBrightness\n    with AutoDisposeNotifierMixin {\n  @override\n  Brightness build() {\n    return globalState.appState.brightness;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.appState = globalState.appState.copyWith(brightness: value);\n  }\n\n  void setState(Brightness value) {\n    state = value;\n  }\n}\n\n@riverpod\nclass Traffics extends _$Traffics with AutoDisposeNotifierMixin {\n  @override\n  FixedList<Traffic> build() {\n    return globalState.appState.traffics;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.appState = globalState.appState.copyWith(traffics: value);\n  }\n\n  void addTraffic(Traffic value) {\n    state = state.copyWith()..add(value);\n  }\n\n  void clear() {\n    state = state.copyWith()..clear();\n  }\n}\n\n@riverpod\nclass TotalTraffic extends _$TotalTraffic with AutoDisposeNotifierMixin {\n  @override\n  Traffic build() {\n    return globalState.appState.totalTraffic;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.appState = globalState.appState.copyWith(totalTraffic: value);\n  }\n}\n\n@riverpod\nclass LocalIp extends _$LocalIp with AutoDisposeNotifierMixin {\n  @override\n  String? build() {\n    return globalState.appState.localIp;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.appState = globalState.appState.copyWith(localIp: value);\n  }\n\n  @override\n  set state(String? value) {\n    super.state = value;\n    globalState.appState = globalState.appState.copyWith(localIp: state);\n  }\n}\n\n@riverpod\nclass RunTime extends _$RunTime with AutoDisposeNotifierMixin {\n  @override\n  int? build() {\n    return globalState.appState.runTime;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.appState = globalState.appState.copyWith(runTime: value);\n  }\n\n  bool get isStart {\n    return state != null;\n  }\n}\n\n@riverpod\nclass ViewSize extends _$ViewSize with AutoDisposeNotifierMixin {\n  @override\n  Size build() {\n    return globalState.appState.viewSize;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.appState = globalState.appState.copyWith(viewSize: value);\n  }\n\n  ViewMode get viewMode => utils.getViewMode(state.width);\n\n  bool get isMobileView => viewMode == ViewMode.mobile;\n}\n\n@riverpod\ndouble viewWidth(Ref ref) {\n  return ref.watch(viewSizeProvider).width;\n}\n\n@riverpod\nViewMode viewMode(Ref ref) {\n  if (globalState.isAndroidTV) {\n    return ViewMode.mobile;\n  }\n  return utils.getViewMode(ref.watch(viewWidthProvider));\n}\n\n@riverpod\nbool isMobileView(Ref ref) {\n  if (globalState.isAndroidTV) {\n    return true;\n  }\n  return ref.watch(viewModeProvider) == ViewMode.mobile;\n}\n\n@riverpod\ndouble viewHeight(Ref ref) {\n  return ref.watch(viewSizeProvider).height;\n}\n\n@riverpod\nclass Init extends _$Init with AutoDisposeNotifierMixin {\n  @override\n  bool build() {\n    return globalState.appState.isInit;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.appState = globalState.appState.copyWith(isInit: value);\n  }\n}\n\n@riverpod\nclass CurrentPageLabel extends _$CurrentPageLabel\n    with AutoDisposeNotifierMixin {\n  @override\n  PageLabel build() {\n    return globalState.appState.pageLabel;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.appState = globalState.appState.copyWith(pageLabel: value);\n  }\n}\n\n@riverpod\nclass SortNum extends _$SortNum with AutoDisposeNotifierMixin {\n  @override\n  int build() {\n    return globalState.appState.sortNum;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.appState = globalState.appState.copyWith(sortNum: value);\n  }\n\n  int add() => state++;\n}\n\n@riverpod\nclass CheckIpNum extends _$CheckIpNum with AutoDisposeNotifierMixin {\n  @override\n  int build() {\n    return globalState.appState.checkIpNum;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.appState = globalState.appState.copyWith(checkIpNum: value);\n  }\n\n  int add() => state++;\n}\n\n@riverpod\nclass BackBlock extends _$BackBlock with AutoDisposeNotifierMixin {\n  @override\n  bool build() {\n    return globalState.appState.backBlock;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.appState = globalState.appState.copyWith(backBlock: value);\n  }\n}\n\n@riverpod\nclass Loading extends _$Loading with AutoDisposeNotifierMixin {\n  @override\n  bool build() {\n    return globalState.appState.loading;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.appState = globalState.appState.copyWith(loading: value);\n  }\n}\n\n@riverpod\nclass Version extends _$Version with AutoDisposeNotifierMixin {\n  @override\n  int build() {\n    return globalState.appState.version;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.appState = globalState.appState.copyWith(version: value);\n  }\n}\n\n@riverpod\nclass Groups extends _$Groups with AutoDisposeNotifierMixin {\n  @override\n  List<Group> build() {\n    return globalState.appState.groups;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.appState = globalState.appState.copyWith(groups: value);\n  }\n}\n\n@riverpod\nclass DelayDataSource extends _$DelayDataSource with AutoDisposeNotifierMixin {\n  @override\n  DelayMap build() {\n    return globalState.appState.delayMap;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.appState = globalState.appState.copyWith(delayMap: value);\n  }\n\n  void setDelay(Delay delay) {\n    if (state[delay.url]?[delay.name] != delay.value) {\n      final DelayMap newDelayMap = Map.from(state);\n      if (newDelayMap[delay.url] == null) {\n        newDelayMap[delay.url] = {};\n      }\n      newDelayMap[delay.url]![delay.name] = delay.value;\n      state = newDelayMap;\n    }\n  }\n}\n\n@riverpod\nclass SystemUiOverlayStyleState extends _$SystemUiOverlayStyleState\n    with AutoDisposeNotifierMixin {\n  @override\n  SystemUiOverlayStyle build() {\n    return globalState.appState.systemUiOverlayStyle;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.appState = globalState.appState.copyWith(\n      systemUiOverlayStyle: value,\n    );\n  }\n}\n\n/// Provider to track if VPN was stopped by Smart Auto Stop feature.\n/// This is used to show different notification content when smart-stopped.\n@Riverpod(keepAlive: true)\nclass IsSmartStopped extends _$IsSmartStopped {\n  @override\n  bool build() {\n    return false;\n  }\n\n  void set(bool value) {\n    state = value;\n  }\n}\n\n// Connections providers\nfinal connectionsProvider = StateProvider<List<TrackerInfo>>((ref) => []);\nfinal connectionsSearchProvider = StateProvider<String>((ref) => '');\nfinal connectionsKeywordsProvider = StateProvider<List<String>>((ref) => []);\n\nfinal filteredConnectionsProvider = Provider<List<TrackerInfo>>((ref) {\n  final connections = ref.watch(connectionsProvider);\n  final query = ref.watch(connectionsSearchProvider).toLowerCase().trim();\n  final keywords = ref.watch(connectionsKeywordsProvider);\n\n  return connections.where((item) {\n    if (query.isNotEmpty) {\n      final networkText = item.metadata.network.toLowerCase();\n      final hostText = item.metadata.host.toLowerCase();\n      final destinationIPText = item.metadata.destinationIP.toLowerCase();\n      final processText = item.metadata.process.toLowerCase();\n      final chainsText = item.chains.join('').toLowerCase();\n      final matchesQuery = networkText.contains(query) ||\n          hostText.contains(query) ||\n          destinationIPText.contains(query) ||\n          processText.contains(query) ||\n          chainsText.contains(query);\n      if (!matchesQuery) return false;\n    }\n    if (keywords.isNotEmpty) {\n      final chains = item.chains;\n      final process = item.metadata.process;\n      final matchesKeywords = {...chains, process}.containsAll(keywords);\n      if (!matchesKeywords) return false;\n    }\n    return true;\n  }).toList();\n});\n"
  },
  {
    "path": "lib/providers/config.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:riverpod_annotation/riverpod_annotation.dart';\n\npart 'generated/config.g.dart';\n\n@riverpod\nclass AppSetting extends _$AppSetting with AutoDisposeNotifierMixin {\n  @override\n  AppSettingProps build() {\n    return globalState.config.appSetting;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.config = globalState.config.copyWith(appSetting: value);\n  }\n\n  void updateState(AppSettingProps Function(AppSettingProps state) builder) {\n    state = builder(state).copyWith(minimizeOnExit: true);\n  }\n}\n\n@riverpod\nclass WindowSetting extends _$WindowSetting with AutoDisposeNotifierMixin {\n  @override\n  WindowProps build() {\n    return globalState.config.windowProps;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.config = globalState.config.copyWith(windowProps: value);\n  }\n\n  void updateState(WindowProps Function(WindowProps state) builder) {\n    state = builder(state);\n  }\n}\n\n@riverpod\nclass VpnSetting extends _$VpnSetting with AutoDisposeNotifierMixin {\n  @override\n  VpnProps build() {\n    return globalState.config.vpnProps;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.config = globalState.config.copyWith(vpnProps: value);\n  }\n\n  void updateState(VpnProps Function(VpnProps state) builder) {\n    state = builder(state);\n  }\n}\n\n@riverpod\nclass NetworkSetting extends _$NetworkSetting with AutoDisposeNotifierMixin {\n  @override\n  NetworkProps build() {\n    return globalState.config.networkProps;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.config = globalState.config.copyWith(networkProps: value);\n  }\n\n  void updateState(NetworkProps Function(NetworkProps state) builder) {\n    state = builder(state);\n  }\n}\n\n@riverpod\nclass ThemeSetting extends _$ThemeSetting with AutoDisposeNotifierMixin {\n  @override\n  ThemeProps build() {\n    return globalState.config.themeProps;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.config = globalState.config.copyWith(themeProps: value);\n  }\n\n  void updateState(ThemeProps Function(ThemeProps state) builder) {\n    state = builder(state);\n  }\n}\n\n@riverpod\nclass Profiles extends _$Profiles with AutoDisposeNotifierMixin {\n  @override\n  List<Profile> build() {\n    return globalState.config.profiles;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.config = globalState.config.copyWith(profiles: value);\n  }\n\n  String? _getLabel(String? label, String id) {\n    final realLabel = label ?? id;\n    final hasDup =\n        state.indexWhere(\n          (element) => element.label == realLabel && element.id != id,\n        ) !=\n        -1;\n    if (hasDup) {\n      return _getLabel(utils.getOverwriteLabel(realLabel), id);\n    } else {\n      return label;\n    }\n  }\n\n  void setProfile(Profile profile) {\n    final List<Profile> profilesTemp = List.from(state);\n    final index = profilesTemp.indexWhere(\n      (element) => element.id == profile.id,\n    );\n    final updateProfile = profile.copyWith(\n      label: _getLabel(profile.label, profile.id),\n    );\n    if (index == -1) {\n      profilesTemp.add(updateProfile);\n    } else {\n      profilesTemp[index] = updateProfile;\n    }\n    state = profilesTemp;\n  }\n\n  void updateProfile(\n    String profileId,\n    Profile Function(Profile profile) builder,\n  ) {\n    final List<Profile> profilesTemp = List.from(state);\n    final index = profilesTemp.indexWhere((element) => element.id == profileId);\n    if (index != -1) {\n      profilesTemp[index] = builder(profilesTemp[index]);\n    }\n    state = profilesTemp;\n  }\n\n  void deleteProfileById(String id) {\n    state = state.where((element) => element.id != id).toList();\n  }\n}\n\n@riverpod\nclass CurrentProfileId extends _$CurrentProfileId\n    with AutoDisposeNotifierMixin {\n  @override\n  String? build() {\n    return globalState.config.currentProfileId;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.config = globalState.config.copyWith(currentProfileId: value);\n  }\n}\n\n@riverpod\nclass AppDAVSetting extends _$AppDAVSetting with AutoDisposeNotifierMixin {\n  @override\n  DAV? build() {\n    return globalState.config.dav;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.config = globalState.config.copyWith(dav: value);\n  }\n\n  void updateState(DAV? Function(DAV? state) builder) {\n    state = builder(state);\n  }\n}\n\n@riverpod\nclass OverrideDns extends _$OverrideDns with AutoDisposeNotifierMixin {\n  @override\n  bool build() {\n    return globalState.config.overrideDns;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.config = globalState.config.copyWith(overrideDns: value);\n  }\n}\n\n@riverpod\nclass OverrideTestUrl extends _$OverrideTestUrl with AutoDisposeNotifierMixin {\n  @override\n  bool build() {\n    return globalState.config.overrideTestUrl;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.config = globalState.config.copyWith(overrideTestUrl: value);\n  }\n}\n\n@riverpod\nclass OverrideNtp extends _$OverrideNtp with AutoDisposeNotifierMixin {\n  @override\n  bool build() {\n    return globalState.config.overrideNtp;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.config = globalState.config.copyWith(overrideNtp: value);\n  }\n}\n\n@riverpod\nclass OverrideSniffer extends _$OverrideSniffer with AutoDisposeNotifierMixin {\n  @override\n  bool build() {\n    return globalState.config.overrideSniffer;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.config = globalState.config.copyWith(overrideSniffer: value);\n  }\n}\n\n@riverpod\nclass OverrideTunnel extends _$OverrideTunnel with AutoDisposeNotifierMixin {\n  @override\n  bool build() {\n    return globalState.config.overrideTunnel;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.config = globalState.config.copyWith(overrideTunnel: value);\n  }\n}\n\n@riverpod\nclass OverrideExperimental extends _$OverrideExperimental\n    with AutoDisposeNotifierMixin {\n  @override\n  bool build() {\n    return globalState.config.overrideExperimental;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.config = globalState.config.copyWith(\n      overrideExperimental: value,\n    );\n  }\n}\n\n@riverpod\nclass HotKeyActions extends _$HotKeyActions with AutoDisposeNotifierMixin {\n  @override\n  List<HotKeyAction> build() {\n    return globalState.config.hotKeyActions;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.config = globalState.config.copyWith(hotKeyActions: value);\n  }\n}\n\n@riverpod\nclass ProxiesStyleSetting extends _$ProxiesStyleSetting\n    with AutoDisposeNotifierMixin {\n  @override\n  ProxiesStyle build() {\n    return globalState.config.proxiesStyle;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.config = globalState.config.copyWith(proxiesStyle: value);\n  }\n\n  void updateState(ProxiesStyle Function(ProxiesStyle state) builder) {\n    state = builder(state);\n  }\n}\n\n@riverpod\nclass ScriptState extends _$ScriptState with AutoDisposeNotifierMixin {\n  @override\n  ScriptProps build() {\n    return globalState.config.scriptProps;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.config = globalState.config.copyWith(scriptProps: value);\n  }\n\n  void setScript(Script script) {\n    final list = List<Script>.from(state.scripts);\n    final index = list.indexWhere((item) => item.id == script.id);\n    if (index != -1) {\n      list[index] = script;\n    } else {\n      list.add(script);\n    }\n    state = state.copyWith(scripts: list);\n  }\n\n  void setId(String id) {\n    state = state.copyWith(currentId: state.currentId != id ? id : null);\n  }\n\n  void del(String id) {\n    final list = List<Script>.from(state.scripts);\n    final index = list.indexWhere((item) => item.label == id);\n    if (index != -1) {\n      list.removeAt(index);\n    }\n    final nextId = id == state.currentId ? null : state.currentId;\n    state = state.copyWith(scripts: list, currentId: nextId);\n  }\n\n  bool isExits(String label) {\n    return state.scripts.indexWhere((item) => item.label == label) != -1;\n  }\n\n  Future<void> syncScript(String id) async {\n    final script = state.scripts.firstWhere((item) => item.id == id);\n    final url = script.url;\n    if (url == null || url.isEmpty) return;\n    final res = await request.getTextResponseForUrl(url);\n    final updated = script.copyWith(content: res.data);\n    setScript(updated);\n  }\n}\n\n@riverpod\nclass PatchClashConfig extends _$PatchClashConfig\n    with AutoDisposeNotifierMixin {\n  @override\n  ClashConfig build() {\n    return globalState.config.patchClashConfig;\n  }\n\n  void updateState(ClashConfig? Function(ClashConfig state) builder) {\n    final newState = builder(state);\n    if (newState == null) {\n      return;\n    }\n    state = newState;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.config = globalState.config.copyWith(patchClashConfig: value);\n  }\n}\n\n@riverpod\nclass WindowLocked extends _$WindowLocked with AutoDisposeNotifierMixin {\n  @override\n  bool build() {\n    return globalState.config.windowProps.isLocked;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.config = globalState.config.copyWith(\n      windowProps: globalState.config.windowProps.copyWith(isLocked: value),\n    );\n  }\n}\n\n@riverpod\nclass NodeExcludeFilter extends _$NodeExcludeFilter\n    with AutoDisposeNotifierMixin {\n  @override\n  String build() {\n    return globalState.config.nodeExcludeFilter;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.config = globalState.config.copyWith(nodeExcludeFilter: value);\n  }\n\n  void updateState(String Function(String state) builder) {\n    state = builder(state);\n  }\n}\n\n@riverpod\nclass HealthCheckTimeout extends _$HealthCheckTimeout\n    with AutoDisposeNotifierMixin {\n  @override\n  int build() {\n    return globalState.config.healthCheckTimeout;\n  }\n\n  @override\n  onUpdate(value) {\n    globalState.config = globalState.config.copyWith(healthCheckTimeout: value);\n  }\n\n  void updateState(int Function(int state) builder) {\n    state = builder(state);\n  }\n}\n\n"
  },
  {
    "path": "lib/providers/generated/app.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of '../app.dart';\n\n// **************************************************************************\n// RiverpodGenerator\n// **************************************************************************\n\nString _$viewWidthHash() => r'a469c3414170a6616ff3264962e7f160b2edceca';\n\n/// See also [viewWidth].\n@ProviderFor(viewWidth)\nfinal viewWidthProvider = AutoDisposeProvider<double>.internal(\n  viewWidth,\n  name: r'viewWidthProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$viewWidthHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef ViewWidthRef = AutoDisposeProviderRef<double>;\nString _$viewModeHash() => r'72c09e2e81bd930b7842fa78b8cf2376fd2b72c0';\n\n/// See also [viewMode].\n@ProviderFor(viewMode)\nfinal viewModeProvider = AutoDisposeProvider<ViewMode>.internal(\n  viewMode,\n  name: r'viewModeProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$viewModeHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef ViewModeRef = AutoDisposeProviderRef<ViewMode>;\nString _$isMobileViewHash() => r'110d5cbf01a55981c95a434c796600757118ec79';\n\n/// See also [isMobileView].\n@ProviderFor(isMobileView)\nfinal isMobileViewProvider = AutoDisposeProvider<bool>.internal(\n  isMobileView,\n  name: r'isMobileViewProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$isMobileViewHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef IsMobileViewRef = AutoDisposeProviderRef<bool>;\nString _$viewHeightHash() => r'410aee5b41388226ab16737f0e85a56f7e9fe801';\n\n/// See also [viewHeight].\n@ProviderFor(viewHeight)\nfinal viewHeightProvider = AutoDisposeProvider<double>.internal(\n  viewHeight,\n  name: r'viewHeightProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$viewHeightHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef ViewHeightRef = AutoDisposeProviderRef<double>;\nString _$realTunEnableHash() => r'a4e995c86deca4c8307966470e69d93d64a40df6';\n\n/// See also [RealTunEnable].\n@ProviderFor(RealTunEnable)\nfinal realTunEnableProvider =\n    AutoDisposeNotifierProvider<RealTunEnable, bool>.internal(\n      RealTunEnable.new,\n      name: r'realTunEnableProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$realTunEnableHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$RealTunEnable = AutoDisposeNotifier<bool>;\nString _$logsHash() => r'87cb2e7f7ba8c33f417f7cd9e7e5413f63821b4c';\n\n/// See also [Logs].\n@ProviderFor(Logs)\nfinal logsProvider = AutoDisposeNotifierProvider<Logs, FixedList<Log>>.internal(\n  Logs.new,\n  name: r'logsProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$logsHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\ntypedef _$Logs = AutoDisposeNotifier<FixedList<Log>>;\nString _$requestsHash() => r'189635f139a05d1d3e8a23350386bba666badea4';\n\n/// See also [Requests].\n@ProviderFor(Requests)\nfinal requestsProvider =\n    AutoDisposeNotifierProvider<Requests, FixedList<TrackerInfo>>.internal(\n      Requests.new,\n      name: r'requestsProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$requestsHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$Requests = AutoDisposeNotifier<FixedList<TrackerInfo>>;\nString _$providersHash() => r'4292240629a99470b2e72426dde3b9049b9b57e0';\n\n/// See also [Providers].\n@ProviderFor(Providers)\nfinal providersProvider =\n    AutoDisposeNotifierProvider<Providers, List<ExternalProvider>>.internal(\n      Providers.new,\n      name: r'providersProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$providersHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$Providers = AutoDisposeNotifier<List<ExternalProvider>>;\nString _$packagesHash() => r'84bff9f5271622ed4199ecafacda8e74fa444fe2';\n\n/// See also [Packages].\n@ProviderFor(Packages)\nfinal packagesProvider =\n    AutoDisposeNotifierProvider<Packages, List<Package>>.internal(\n      Packages.new,\n      name: r'packagesProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$packagesHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$Packages = AutoDisposeNotifier<List<Package>>;\nString _$systemBrightnessHash() => r'46eb2d23b05405723efc29480e8f258bf2d8138b';\n\n/// See also [SystemBrightness].\n@ProviderFor(SystemBrightness)\nfinal systemBrightnessProvider =\n    AutoDisposeNotifierProvider<SystemBrightness, Brightness>.internal(\n      SystemBrightness.new,\n      name: r'systemBrightnessProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$systemBrightnessHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$SystemBrightness = AutoDisposeNotifier<Brightness>;\nString _$trafficsHash() => r'8b86eb718fed5776de174c51fd5b231957011fe6';\n\n/// See also [Traffics].\n@ProviderFor(Traffics)\nfinal trafficsProvider =\n    AutoDisposeNotifierProvider<Traffics, FixedList<Traffic>>.internal(\n      Traffics.new,\n      name: r'trafficsProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$trafficsHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$Traffics = AutoDisposeNotifier<FixedList<Traffic>>;\nString _$totalTrafficHash() => r'cc993ec58fa4c8ee0dbbf2e8a146f7039e818d7e';\n\n/// See also [TotalTraffic].\n@ProviderFor(TotalTraffic)\nfinal totalTrafficProvider =\n    AutoDisposeNotifierProvider<TotalTraffic, Traffic>.internal(\n      TotalTraffic.new,\n      name: r'totalTrafficProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$totalTrafficHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$TotalTraffic = AutoDisposeNotifier<Traffic>;\nString _$localIpHash() => r'2dd4afdb29db4791ebd80d976f9ea31c62959199';\n\n/// See also [LocalIp].\n@ProviderFor(LocalIp)\nfinal localIpProvider = AutoDisposeNotifierProvider<LocalIp, String?>.internal(\n  LocalIp.new,\n  name: r'localIpProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$localIpHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\ntypedef _$LocalIp = AutoDisposeNotifier<String?>;\nString _$runTimeHash() => r'9aab44f2234590a70cbf0ff7394e496c2c97c00e';\n\n/// See also [RunTime].\n@ProviderFor(RunTime)\nfinal runTimeProvider = AutoDisposeNotifierProvider<RunTime, int?>.internal(\n  RunTime.new,\n  name: r'runTimeProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$runTimeHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\ntypedef _$RunTime = AutoDisposeNotifier<int?>;\nString _$viewSizeHash() => r'07f9cce28a69d1496ba4643ef72a739312f6fc28';\n\n/// See also [ViewSize].\n@ProviderFor(ViewSize)\nfinal viewSizeProvider = AutoDisposeNotifierProvider<ViewSize, Size>.internal(\n  ViewSize.new,\n  name: r'viewSizeProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$viewSizeHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\ntypedef _$ViewSize = AutoDisposeNotifier<Size>;\nString _$initHash() => r'7d3f11c8aff7a1924c5ec8886b2cd2cbdda57c3f';\n\n/// See also [Init].\n@ProviderFor(Init)\nfinal initProvider = AutoDisposeNotifierProvider<Init, bool>.internal(\n  Init.new,\n  name: r'initProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$initHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\ntypedef _$Init = AutoDisposeNotifier<bool>;\nString _$currentPageLabelHash() => r'a4ed13348bcd406ec3be52138cf1083106d31215';\n\n/// See also [CurrentPageLabel].\n@ProviderFor(CurrentPageLabel)\nfinal currentPageLabelProvider =\n    AutoDisposeNotifierProvider<CurrentPageLabel, PageLabel>.internal(\n      CurrentPageLabel.new,\n      name: r'currentPageLabelProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$currentPageLabelHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$CurrentPageLabel = AutoDisposeNotifier<PageLabel>;\nString _$sortNumHash() => r'b67bee9fdfbb74b484190fbc6e5c3da7d773bed0';\n\n/// See also [SortNum].\n@ProviderFor(SortNum)\nfinal sortNumProvider = AutoDisposeNotifierProvider<SortNum, int>.internal(\n  SortNum.new,\n  name: r'sortNumProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$sortNumHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\ntypedef _$SortNum = AutoDisposeNotifier<int>;\nString _$checkIpNumHash() => r'4d8b32ed9c0013c056f90c9d5483f11fa5fddec5';\n\n/// See also [CheckIpNum].\n@ProviderFor(CheckIpNum)\nfinal checkIpNumProvider =\n    AutoDisposeNotifierProvider<CheckIpNum, int>.internal(\n      CheckIpNum.new,\n      name: r'checkIpNumProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$checkIpNumHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$CheckIpNum = AutoDisposeNotifier<int>;\nString _$backBlockHash() => r'c0223e0776b72d3a8c8842fc32fdb5287353999f';\n\n/// See also [BackBlock].\n@ProviderFor(BackBlock)\nfinal backBlockProvider = AutoDisposeNotifierProvider<BackBlock, bool>.internal(\n  BackBlock.new,\n  name: r'backBlockProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$backBlockHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\ntypedef _$BackBlock = AutoDisposeNotifier<bool>;\nString _$loadingHash() => r'a0a09132a78495616785461cdc2a8b412c19b51b';\n\n/// See also [Loading].\n@ProviderFor(Loading)\nfinal loadingProvider = AutoDisposeNotifierProvider<Loading, bool>.internal(\n  Loading.new,\n  name: r'loadingProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$loadingHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\ntypedef _$Loading = AutoDisposeNotifier<bool>;\nString _$versionHash() => r'8c0ee019d20df3f112c38ae4dc4abd61148d3809';\n\n/// See also [Version].\n@ProviderFor(Version)\nfinal versionProvider = AutoDisposeNotifierProvider<Version, int>.internal(\n  Version.new,\n  name: r'versionProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$versionHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\ntypedef _$Version = AutoDisposeNotifier<int>;\nString _$groupsHash() => r'fbff504e0bcdb5a2770a902f2867aabd921fbadc';\n\n/// See also [Groups].\n@ProviderFor(Groups)\nfinal groupsProvider =\n    AutoDisposeNotifierProvider<Groups, List<Group>>.internal(\n      Groups.new,\n      name: r'groupsProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$groupsHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$Groups = AutoDisposeNotifier<List<Group>>;\nString _$delayDataSourceHash() => r'1b94dcfdb9e1eb4c0b7ca69d933f2299d1f94ed5';\n\n/// See also [DelayDataSource].\n@ProviderFor(DelayDataSource)\nfinal delayDataSourceProvider =\n    AutoDisposeNotifierProvider<DelayDataSource, DelayMap>.internal(\n      DelayDataSource.new,\n      name: r'delayDataSourceProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$delayDataSourceHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$DelayDataSource = AutoDisposeNotifier<DelayMap>;\nString _$systemUiOverlayStyleStateHash() =>\n    r'4420d92227ae617ce685c8943dda64f29f57d5d1';\n\n/// See also [SystemUiOverlayStyleState].\n@ProviderFor(SystemUiOverlayStyleState)\nfinal systemUiOverlayStyleStateProvider =\n    AutoDisposeNotifierProvider<\n      SystemUiOverlayStyleState,\n      SystemUiOverlayStyle\n    >.internal(\n      SystemUiOverlayStyleState.new,\n      name: r'systemUiOverlayStyleStateProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$systemUiOverlayStyleStateHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$SystemUiOverlayStyleState = AutoDisposeNotifier<SystemUiOverlayStyle>;\nString _$isSmartStoppedHash() => r'3f053dad10b19a0ee35a9bbbd5391395f0dcb5f7';\n\n/// Provider to track if VPN was stopped by Smart Auto Stop feature.\n/// This is used to show different notification content when smart-stopped.\n///\n/// Copied from [IsSmartStopped].\n@ProviderFor(IsSmartStopped)\nfinal isSmartStoppedProvider = NotifierProvider<IsSmartStopped, bool>.internal(\n  IsSmartStopped.new,\n  name: r'isSmartStoppedProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$isSmartStoppedHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\ntypedef _$IsSmartStopped = Notifier<bool>;\n// ignore_for_file: type=lint\n// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package\n"
  },
  {
    "path": "lib/providers/generated/config.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of '../config.dart';\n\n// **************************************************************************\n// RiverpodGenerator\n// **************************************************************************\n\nString _$appSettingHash() => r'2a3e6c9e8f289fa07bc8d6205b0994a59aa09ad3';\n\n/// See also [AppSetting].\n@ProviderFor(AppSetting)\nfinal appSettingProvider =\n    AutoDisposeNotifierProvider<AppSetting, AppSettingProps>.internal(\n      AppSetting.new,\n      name: r'appSettingProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$appSettingHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$AppSetting = AutoDisposeNotifier<AppSettingProps>;\nString _$windowSettingHash() => r'9bf31c7e08fab84213f31e249270f9d730bdf711';\n\n/// See also [WindowSetting].\n@ProviderFor(WindowSetting)\nfinal windowSettingProvider =\n    AutoDisposeNotifierProvider<WindowSetting, WindowProps>.internal(\n      WindowSetting.new,\n      name: r'windowSettingProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$windowSettingHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$WindowSetting = AutoDisposeNotifier<WindowProps>;\nString _$vpnSettingHash() => r'3dae8b56504bfb906aca546c5a5389d79d259a5e';\n\n/// See also [VpnSetting].\n@ProviderFor(VpnSetting)\nfinal vpnSettingProvider =\n    AutoDisposeNotifierProvider<VpnSetting, VpnProps>.internal(\n      VpnSetting.new,\n      name: r'vpnSettingProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$vpnSettingHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$VpnSetting = AutoDisposeNotifier<VpnProps>;\nString _$networkSettingHash() => r'5a30d4cbfaba94cc29ad08dc1771ebb368b4ba14';\n\n/// See also [NetworkSetting].\n@ProviderFor(NetworkSetting)\nfinal networkSettingProvider =\n    AutoDisposeNotifierProvider<NetworkSetting, NetworkProps>.internal(\n      NetworkSetting.new,\n      name: r'networkSettingProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$networkSettingHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$NetworkSetting = AutoDisposeNotifier<NetworkProps>;\nString _$themeSettingHash() => r'0b5620b696d73260d94f63cbfb65857acd2000f0';\n\n/// See also [ThemeSetting].\n@ProviderFor(ThemeSetting)\nfinal themeSettingProvider =\n    AutoDisposeNotifierProvider<ThemeSetting, ThemeProps>.internal(\n      ThemeSetting.new,\n      name: r'themeSettingProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$themeSettingHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$ThemeSetting = AutoDisposeNotifier<ThemeProps>;\nString _$profilesHash() => r'3203cc7de88b91fff86b79c75c2cacd8116fffb7';\n\n/// See also [Profiles].\n@ProviderFor(Profiles)\nfinal profilesProvider =\n    AutoDisposeNotifierProvider<Profiles, List<Profile>>.internal(\n      Profiles.new,\n      name: r'profilesProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$profilesHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$Profiles = AutoDisposeNotifier<List<Profile>>;\nString _$currentProfileIdHash() => r'0c3e324e751aac1164da479e1796e826615bdcbe';\n\n/// See also [CurrentProfileId].\n@ProviderFor(CurrentProfileId)\nfinal currentProfileIdProvider =\n    AutoDisposeNotifierProvider<CurrentProfileId, String?>.internal(\n      CurrentProfileId.new,\n      name: r'currentProfileIdProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$currentProfileIdHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$CurrentProfileId = AutoDisposeNotifier<String?>;\nString _$appDAVSettingHash() => r'4bf293ac0d1fba157f60df920b7ffd5afefaab26';\n\n/// See also [AppDAVSetting].\n@ProviderFor(AppDAVSetting)\nfinal appDAVSettingProvider =\n    AutoDisposeNotifierProvider<AppDAVSetting, DAV?>.internal(\n      AppDAVSetting.new,\n      name: r'appDAVSettingProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$appDAVSettingHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$AppDAVSetting = AutoDisposeNotifier<DAV?>;\nString _$overrideDnsHash() => r'1fc914de471319bf1e003edf9627b8c646b641bf';\n\n/// See also [OverrideDns].\n@ProviderFor(OverrideDns)\nfinal overrideDnsProvider =\n    AutoDisposeNotifierProvider<OverrideDns, bool>.internal(\n      OverrideDns.new,\n      name: r'overrideDnsProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$overrideDnsHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$OverrideDns = AutoDisposeNotifier<bool>;\nString _$overrideTestUrlHash() => r'8a5a63f5abc3eee0293442891c92a5062c09206e';\n\n/// See also [OverrideTestUrl].\n@ProviderFor(OverrideTestUrl)\nfinal overrideTestUrlProvider =\n    AutoDisposeNotifierProvider<OverrideTestUrl, bool>.internal(\n      OverrideTestUrl.new,\n      name: r'overrideTestUrlProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$overrideTestUrlHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$OverrideTestUrl = AutoDisposeNotifier<bool>;\nString _$overrideNtpHash() => r'da4620088529c14a7ceeeaffb1c528aa4202ea46';\n\n/// See also [OverrideNtp].\n@ProviderFor(OverrideNtp)\nfinal overrideNtpProvider =\n    AutoDisposeNotifierProvider<OverrideNtp, bool>.internal(\n      OverrideNtp.new,\n      name: r'overrideNtpProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$overrideNtpHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$OverrideNtp = AutoDisposeNotifier<bool>;\nString _$overrideSnifferHash() => r'c52e23041ec9b85e394bea22442d66a1c2c5e1bb';\n\n/// See also [OverrideSniffer].\n@ProviderFor(OverrideSniffer)\nfinal overrideSnifferProvider =\n    AutoDisposeNotifierProvider<OverrideSniffer, bool>.internal(\n      OverrideSniffer.new,\n      name: r'overrideSnifferProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$overrideSnifferHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$OverrideSniffer = AutoDisposeNotifier<bool>;\nString _$overrideTunnelHash() => r'bef45c0504f3c2b831025b8b1ecca7bf0214f67a';\n\n/// See also [OverrideTunnel].\n@ProviderFor(OverrideTunnel)\nfinal overrideTunnelProvider =\n    AutoDisposeNotifierProvider<OverrideTunnel, bool>.internal(\n      OverrideTunnel.new,\n      name: r'overrideTunnelProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$overrideTunnelHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$OverrideTunnel = AutoDisposeNotifier<bool>;\nString _$overrideExperimentalHash() =>\n    r'92073890c35d9993a7a038128a5945dff8cc6097';\n\n/// See also [OverrideExperimental].\n@ProviderFor(OverrideExperimental)\nfinal overrideExperimentalProvider =\n    AutoDisposeNotifierProvider<OverrideExperimental, bool>.internal(\n      OverrideExperimental.new,\n      name: r'overrideExperimentalProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$overrideExperimentalHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$OverrideExperimental = AutoDisposeNotifier<bool>;\nString _$hotKeyActionsHash() => r'1d308d61b74accebbb11b1771a55975760503691';\n\n/// See also [HotKeyActions].\n@ProviderFor(HotKeyActions)\nfinal hotKeyActionsProvider =\n    AutoDisposeNotifierProvider<HotKeyActions, List<HotKeyAction>>.internal(\n      HotKeyActions.new,\n      name: r'hotKeyActionsProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$hotKeyActionsHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$HotKeyActions = AutoDisposeNotifier<List<HotKeyAction>>;\nString _$proxiesStyleSettingHash() =>\n    r'54ebf20a8d4455b2d7a65824f375c4c02a5fba28';\n\n/// See also [ProxiesStyleSetting].\n@ProviderFor(ProxiesStyleSetting)\nfinal proxiesStyleSettingProvider =\n    AutoDisposeNotifierProvider<ProxiesStyleSetting, ProxiesStyle>.internal(\n      ProxiesStyleSetting.new,\n      name: r'proxiesStyleSettingProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$proxiesStyleSettingHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$ProxiesStyleSetting = AutoDisposeNotifier<ProxiesStyle>;\nString _$scriptStateHash() => r'dc958461c4af0e41a47baea4a4d984e5abc19a9c';\n\n/// See also [ScriptState].\n@ProviderFor(ScriptState)\nfinal scriptStateProvider =\n    AutoDisposeNotifierProvider<ScriptState, ScriptProps>.internal(\n      ScriptState.new,\n      name: r'scriptStateProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$scriptStateHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$ScriptState = AutoDisposeNotifier<ScriptProps>;\nString _$patchClashConfigHash() => r'd9acdd0ace673fc1c1460b63d7a27c5787713c14';\n\n/// See also [PatchClashConfig].\n@ProviderFor(PatchClashConfig)\nfinal patchClashConfigProvider =\n    AutoDisposeNotifierProvider<PatchClashConfig, ClashConfig>.internal(\n      PatchClashConfig.new,\n      name: r'patchClashConfigProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$patchClashConfigHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$PatchClashConfig = AutoDisposeNotifier<ClashConfig>;\nString _$windowLockedHash() => r'3dc075afbc2a00dae7dec528accbed76afc206e4';\n\n/// See also [WindowLocked].\n@ProviderFor(WindowLocked)\nfinal windowLockedProvider =\n    AutoDisposeNotifierProvider<WindowLocked, bool>.internal(\n      WindowLocked.new,\n      name: r'windowLockedProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$windowLockedHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$WindowLocked = AutoDisposeNotifier<bool>;\nString _$nodeExcludeFilterHash() => r'5fc26cba4cd5c9dfdb57fcd4eb4bf59d39abafc3';\n\n/// See also [NodeExcludeFilter].\n@ProviderFor(NodeExcludeFilter)\nfinal nodeExcludeFilterProvider =\n    AutoDisposeNotifierProvider<NodeExcludeFilter, String>.internal(\n      NodeExcludeFilter.new,\n      name: r'nodeExcludeFilterProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$nodeExcludeFilterHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$NodeExcludeFilter = AutoDisposeNotifier<String>;\nString _$healthCheckTimeoutHash() =>\n    r'5d0b9e74e17b4304b5bc724b6823df870449d627';\n\n/// See also [HealthCheckTimeout].\n@ProviderFor(HealthCheckTimeout)\nfinal healthCheckTimeoutProvider =\n    AutoDisposeNotifierProvider<HealthCheckTimeout, int>.internal(\n      HealthCheckTimeout.new,\n      name: r'healthCheckTimeoutProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$healthCheckTimeoutHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$HealthCheckTimeout = AutoDisposeNotifier<int>;\n// ignore_for_file: type=lint\n// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package\n"
  },
  {
    "path": "lib/providers/generated/state.g.dart",
    "content": "// GENERATED CODE - DO NOT MODIFY BY HAND\n\npart of '../state.dart';\n\n// **************************************************************************\n// RiverpodGenerator\n// **************************************************************************\n\nString _$configStateHash() => r'1f4ea3cc8f6461ba734e7e0c5d7295bfa4fd5afb';\n\n/// See also [configState].\n@ProviderFor(configState)\nfinal configStateProvider = AutoDisposeProvider<Config>.internal(\n  configState,\n  name: r'configStateProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$configStateHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef ConfigStateRef = AutoDisposeProviderRef<Config>;\nString _$currentGroupsStateHash() =>\n    r'bde3f468dc3a39b68c1d1f908c8a7f40896cb777';\n\n/// See also [currentGroupsState].\n@ProviderFor(currentGroupsState)\nfinal currentGroupsStateProvider = AutoDisposeProvider<GroupsState>.internal(\n  currentGroupsState,\n  name: r'currentGroupsStateProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$currentGroupsStateHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef CurrentGroupsStateRef = AutoDisposeProviderRef<GroupsState>;\nString _$navigationItemsStateHash() =>\n    r'011892d5b7bdb324c51a1b29e448ee9dff8ae0ff';\n\n/// See also [navigationItemsState].\n@ProviderFor(navigationItemsState)\nfinal navigationItemsStateProvider =\n    AutoDisposeProvider<NavigationItemsState>.internal(\n      navigationItemsState,\n      name: r'navigationItemsStateProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$navigationItemsStateHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef NavigationItemsStateRef = AutoDisposeProviderRef<NavigationItemsState>;\nString _$currentNavigationItemsStateHash() =>\n    r'06fbdc194f4527b945695fe3b72b16e0585fa440';\n\n/// See also [currentNavigationItemsState].\n@ProviderFor(currentNavigationItemsState)\nfinal currentNavigationItemsStateProvider =\n    AutoDisposeProvider<NavigationItemsState>.internal(\n      currentNavigationItemsState,\n      name: r'currentNavigationItemsStateProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$currentNavigationItemsStateHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef CurrentNavigationItemsStateRef =\n    AutoDisposeProviderRef<NavigationItemsState>;\nString _$coreStateHash() => r'0fec30a6b2b78e720d08824a11e1cc945c1873bb';\n\n/// See also [coreState].\n@ProviderFor(coreState)\nfinal coreStateProvider = AutoDisposeProvider<CoreState>.internal(\n  coreState,\n  name: r'coreStateProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$coreStateHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef CoreStateRef = AutoDisposeProviderRef<CoreState>;\nString _$updateParamsHash() => r'aef760656c0e937600e2b8e34d5286123d0ce121';\n\n/// See also [updateParams].\n@ProviderFor(updateParams)\nfinal updateParamsProvider = AutoDisposeProvider<UpdateParams>.internal(\n  updateParams,\n  name: r'updateParamsProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$updateParamsHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef UpdateParamsRef = AutoDisposeProviderRef<UpdateParams>;\nString _$proxyStateHash() => r'22478fb593aaca11dfe2cf64472013190475a5bc';\n\n/// See also [proxyState].\n@ProviderFor(proxyState)\nfinal proxyStateProvider = AutoDisposeProvider<ProxyState>.internal(\n  proxyState,\n  name: r'proxyStateProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$proxyStateHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef ProxyStateRef = AutoDisposeProviderRef<ProxyState>;\nString _$trayStateHash() => r'c132e2625c51db486c392c1f5c09bac8748509b0';\n\n/// See also [trayState].\n@ProviderFor(trayState)\nfinal trayStateProvider = AutoDisposeProvider<TrayState>.internal(\n  trayState,\n  name: r'trayStateProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$trayStateHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef TrayStateRef = AutoDisposeProviderRef<TrayState>;\nString _$vpnStateHash() => r'128ddad03ce045ad1f8204e47aec3cb6cfa29f6e';\n\n/// See also [vpnState].\n@ProviderFor(vpnState)\nfinal vpnStateProvider = AutoDisposeProvider<VpnState>.internal(\n  vpnState,\n  name: r'vpnStateProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$vpnStateHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef VpnStateRef = AutoDisposeProviderRef<VpnState>;\nString _$navigationStateHash() => r'657dc47ecc35ba0807b58cb37e7f1baa14f6c2f9';\n\n/// See also [navigationState].\n@ProviderFor(navigationState)\nfinal navigationStateProvider = AutoDisposeProvider<NavigationState>.internal(\n  navigationState,\n  name: r'navigationStateProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$navigationStateHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef NavigationStateRef = AutoDisposeProviderRef<NavigationState>;\nString _$dashboardStateHash() => r'6bd5c98196d99af7f198fe1a636f157df5d25c90';\n\n/// See also [dashboardState].\n@ProviderFor(dashboardState)\nfinal dashboardStateProvider = AutoDisposeProvider<DashboardState>.internal(\n  dashboardState,\n  name: r'dashboardStateProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$dashboardStateHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef DashboardStateRef = AutoDisposeProviderRef<DashboardState>;\nString _$proxiesActionsStateHash() =>\n    r'84f8a94706233ff5d4b8a456291a4e66c1381c62';\n\n/// See also [proxiesActionsState].\n@ProviderFor(proxiesActionsState)\nfinal proxiesActionsStateProvider =\n    AutoDisposeProvider<ProxiesActionsState>.internal(\n      proxiesActionsState,\n      name: r'proxiesActionsStateProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$proxiesActionsStateHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef ProxiesActionsStateRef = AutoDisposeProviderRef<ProxiesActionsState>;\nString _$startButtonSelectorStateHash() =>\n    r'537aff93c98b0a689cf8cabd080c610c9c58e611';\n\n/// See also [startButtonSelectorState].\n@ProviderFor(startButtonSelectorState)\nfinal startButtonSelectorStateProvider =\n    AutoDisposeProvider<StartButtonSelectorState>.internal(\n      startButtonSelectorState,\n      name: r'startButtonSelectorStateProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$startButtonSelectorStateHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef StartButtonSelectorStateRef =\n    AutoDisposeProviderRef<StartButtonSelectorState>;\nString _$profilesSelectorStateHash() =>\n    r'aac2deee6e747eceaf62cb5f279ec99ce9227a5a';\n\n/// See also [profilesSelectorState].\n@ProviderFor(profilesSelectorState)\nfinal profilesSelectorStateProvider =\n    AutoDisposeProvider<ProfilesSelectorState>.internal(\n      profilesSelectorState,\n      name: r'profilesSelectorStateProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$profilesSelectorStateHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef ProfilesSelectorStateRef =\n    AutoDisposeProviderRef<ProfilesSelectorState>;\nString _$filterGroupsStateHash() => r'c50aafbb50f98a66e21fc069d22031351d93a0ab';\n\n/// Copied from Dart SDK\nclass _SystemHash {\n  _SystemHash._();\n\n  static int combine(int hash, int value) {\n    // ignore: parameter_assignments\n    hash = 0x1fffffff & (hash + value);\n    // ignore: parameter_assignments\n    hash = 0x1fffffff & (hash + ((0x0007ffff & hash) << 10));\n    return hash ^ (hash >> 6);\n  }\n\n  static int finish(int hash) {\n    // ignore: parameter_assignments\n    hash = 0x1fffffff & (hash + ((0x03ffffff & hash) << 3));\n    // ignore: parameter_assignments\n    hash = hash ^ (hash >> 11);\n    return 0x1fffffff & (hash + ((0x00003fff & hash) << 15));\n  }\n}\n\n/// See also [filterGroupsState].\n@ProviderFor(filterGroupsState)\nconst filterGroupsStateProvider = FilterGroupsStateFamily();\n\n/// See also [filterGroupsState].\nclass FilterGroupsStateFamily extends Family<GroupsState> {\n  /// See also [filterGroupsState].\n  const FilterGroupsStateFamily();\n\n  /// See also [filterGroupsState].\n  FilterGroupsStateProvider call(String query) {\n    return FilterGroupsStateProvider(query);\n  }\n\n  @override\n  FilterGroupsStateProvider getProviderOverride(\n    covariant FilterGroupsStateProvider provider,\n  ) {\n    return call(provider.query);\n  }\n\n  static const Iterable<ProviderOrFamily>? _dependencies = null;\n\n  @override\n  Iterable<ProviderOrFamily>? get dependencies => _dependencies;\n\n  static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;\n\n  @override\n  Iterable<ProviderOrFamily>? get allTransitiveDependencies =>\n      _allTransitiveDependencies;\n\n  @override\n  String? get name => r'filterGroupsStateProvider';\n}\n\n/// See also [filterGroupsState].\nclass FilterGroupsStateProvider extends AutoDisposeProvider<GroupsState> {\n  /// See also [filterGroupsState].\n  FilterGroupsStateProvider(String query)\n    : this._internal(\n        (ref) => filterGroupsState(ref as FilterGroupsStateRef, query),\n        from: filterGroupsStateProvider,\n        name: r'filterGroupsStateProvider',\n        debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n            ? null\n            : _$filterGroupsStateHash,\n        dependencies: FilterGroupsStateFamily._dependencies,\n        allTransitiveDependencies:\n            FilterGroupsStateFamily._allTransitiveDependencies,\n        query: query,\n      );\n\n  FilterGroupsStateProvider._internal(\n    super._createNotifier, {\n    required super.name,\n    required super.dependencies,\n    required super.allTransitiveDependencies,\n    required super.debugGetCreateSourceHash,\n    required super.from,\n    required this.query,\n  }) : super.internal();\n\n  final String query;\n\n  @override\n  Override overrideWith(\n    GroupsState Function(FilterGroupsStateRef provider) create,\n  ) {\n    return ProviderOverride(\n      origin: this,\n      override: FilterGroupsStateProvider._internal(\n        (ref) => create(ref as FilterGroupsStateRef),\n        from: from,\n        name: null,\n        dependencies: null,\n        allTransitiveDependencies: null,\n        debugGetCreateSourceHash: null,\n        query: query,\n      ),\n    );\n  }\n\n  @override\n  AutoDisposeProviderElement<GroupsState> createElement() {\n    return _FilterGroupsStateProviderElement(this);\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return other is FilterGroupsStateProvider && other.query == query;\n  }\n\n  @override\n  int get hashCode {\n    var hash = _SystemHash.combine(0, runtimeType.hashCode);\n    hash = _SystemHash.combine(hash, query.hashCode);\n\n    return _SystemHash.finish(hash);\n  }\n}\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\nmixin FilterGroupsStateRef on AutoDisposeProviderRef<GroupsState> {\n  /// The parameter `query` of this provider.\n  String get query;\n}\n\nclass _FilterGroupsStateProviderElement\n    extends AutoDisposeProviderElement<GroupsState>\n    with FilterGroupsStateRef {\n  _FilterGroupsStateProviderElement(super.provider);\n\n  @override\n  String get query => (origin as FilterGroupsStateProvider).query;\n}\n\nString _$proxiesListStateHash() => r'68f712bdbed5be9f9ba7709ec7c861e1d321f8fc';\n\n/// See also [proxiesListState].\n@ProviderFor(proxiesListState)\nfinal proxiesListStateProvider = AutoDisposeProvider<ProxiesListState>.internal(\n  proxiesListState,\n  name: r'proxiesListStateProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$proxiesListStateHash,\n  dependencies: <ProviderOrFamily>[queryProvider],\n  allTransitiveDependencies: <ProviderOrFamily>{\n    queryProvider,\n    ...?queryProvider.allTransitiveDependencies,\n  },\n);\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef ProxiesListStateRef = AutoDisposeProviderRef<ProxiesListState>;\nString _$proxiesTabStateHash() => r'4b6d355c2892208f67bc843ead7687a5816c18e3';\n\n/// See also [proxiesTabState].\n@ProviderFor(proxiesTabState)\nfinal proxiesTabStateProvider = AutoDisposeProvider<ProxiesTabState>.internal(\n  proxiesTabState,\n  name: r'proxiesTabStateProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$proxiesTabStateHash,\n  dependencies: <ProviderOrFamily>[queryProvider],\n  allTransitiveDependencies: <ProviderOrFamily>{\n    queryProvider,\n    ...?queryProvider.allTransitiveDependencies,\n  },\n);\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef ProxiesTabStateRef = AutoDisposeProviderRef<ProxiesTabState>;\nString _$proxiesTabControllerStateHash() =>\n    r'696c680f6aebe856752c31ce3d961753aaad75ca';\n\n/// See also [proxiesTabControllerState].\n@ProviderFor(proxiesTabControllerState)\nfinal proxiesTabControllerStateProvider =\n    AutoDisposeProvider<VM2<List<String>, String?>>.internal(\n      proxiesTabControllerState,\n      name: r'proxiesTabControllerStateProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$proxiesTabControllerStateHash,\n      dependencies: <ProviderOrFamily>[proxiesTabStateProvider],\n      allTransitiveDependencies: <ProviderOrFamily>{\n        proxiesTabStateProvider,\n        ...?proxiesTabStateProvider.allTransitiveDependencies,\n      },\n    );\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef ProxiesTabControllerStateRef =\n    AutoDisposeProviderRef<VM2<List<String>, String?>>;\nString _$proxyGroupSelectorStateHash() =>\n    r'c2a059873a38907071a2664409bacfe21b7d6c3c';\n\n/// See also [proxyGroupSelectorState].\n@ProviderFor(proxyGroupSelectorState)\nconst proxyGroupSelectorStateProvider = ProxyGroupSelectorStateFamily();\n\n/// See also [proxyGroupSelectorState].\nclass ProxyGroupSelectorStateFamily extends Family<ProxyGroupSelectorState> {\n  /// See also [proxyGroupSelectorState].\n  const ProxyGroupSelectorStateFamily();\n\n  /// See also [proxyGroupSelectorState].\n  ProxyGroupSelectorStateProvider call(String groupName, String query) {\n    return ProxyGroupSelectorStateProvider(groupName, query);\n  }\n\n  @override\n  ProxyGroupSelectorStateProvider getProviderOverride(\n    covariant ProxyGroupSelectorStateProvider provider,\n  ) {\n    return call(provider.groupName, provider.query);\n  }\n\n  static const Iterable<ProviderOrFamily>? _dependencies = null;\n\n  @override\n  Iterable<ProviderOrFamily>? get dependencies => _dependencies;\n\n  static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;\n\n  @override\n  Iterable<ProviderOrFamily>? get allTransitiveDependencies =>\n      _allTransitiveDependencies;\n\n  @override\n  String? get name => r'proxyGroupSelectorStateProvider';\n}\n\n/// See also [proxyGroupSelectorState].\nclass ProxyGroupSelectorStateProvider\n    extends AutoDisposeProvider<ProxyGroupSelectorState> {\n  /// See also [proxyGroupSelectorState].\n  ProxyGroupSelectorStateProvider(String groupName, String query)\n    : this._internal(\n        (ref) => proxyGroupSelectorState(\n          ref as ProxyGroupSelectorStateRef,\n          groupName,\n          query,\n        ),\n        from: proxyGroupSelectorStateProvider,\n        name: r'proxyGroupSelectorStateProvider',\n        debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n            ? null\n            : _$proxyGroupSelectorStateHash,\n        dependencies: ProxyGroupSelectorStateFamily._dependencies,\n        allTransitiveDependencies:\n            ProxyGroupSelectorStateFamily._allTransitiveDependencies,\n        groupName: groupName,\n        query: query,\n      );\n\n  ProxyGroupSelectorStateProvider._internal(\n    super._createNotifier, {\n    required super.name,\n    required super.dependencies,\n    required super.allTransitiveDependencies,\n    required super.debugGetCreateSourceHash,\n    required super.from,\n    required this.groupName,\n    required this.query,\n  }) : super.internal();\n\n  final String groupName;\n  final String query;\n\n  @override\n  Override overrideWith(\n    ProxyGroupSelectorState Function(ProxyGroupSelectorStateRef provider)\n    create,\n  ) {\n    return ProviderOverride(\n      origin: this,\n      override: ProxyGroupSelectorStateProvider._internal(\n        (ref) => create(ref as ProxyGroupSelectorStateRef),\n        from: from,\n        name: null,\n        dependencies: null,\n        allTransitiveDependencies: null,\n        debugGetCreateSourceHash: null,\n        groupName: groupName,\n        query: query,\n      ),\n    );\n  }\n\n  @override\n  AutoDisposeProviderElement<ProxyGroupSelectorState> createElement() {\n    return _ProxyGroupSelectorStateProviderElement(this);\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return other is ProxyGroupSelectorStateProvider &&\n        other.groupName == groupName &&\n        other.query == query;\n  }\n\n  @override\n  int get hashCode {\n    var hash = _SystemHash.combine(0, runtimeType.hashCode);\n    hash = _SystemHash.combine(hash, groupName.hashCode);\n    hash = _SystemHash.combine(hash, query.hashCode);\n\n    return _SystemHash.finish(hash);\n  }\n}\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\nmixin ProxyGroupSelectorStateRef\n    on AutoDisposeProviderRef<ProxyGroupSelectorState> {\n  /// The parameter `groupName` of this provider.\n  String get groupName;\n\n  /// The parameter `query` of this provider.\n  String get query;\n}\n\nclass _ProxyGroupSelectorStateProviderElement\n    extends AutoDisposeProviderElement<ProxyGroupSelectorState>\n    with ProxyGroupSelectorStateRef {\n  _ProxyGroupSelectorStateProviderElement(super.provider);\n\n  @override\n  String get groupName => (origin as ProxyGroupSelectorStateProvider).groupName;\n  @override\n  String get query => (origin as ProxyGroupSelectorStateProvider).query;\n}\n\nString _$packageListSelectorStateHash() =>\n    r'26ad58fec2cb0136ece373c7f3ec89b5aafd9324';\n\n/// See also [packageListSelectorState].\n@ProviderFor(packageListSelectorState)\nfinal packageListSelectorStateProvider =\n    AutoDisposeProvider<PackageListSelectorState>.internal(\n      packageListSelectorState,\n      name: r'packageListSelectorStateProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$packageListSelectorStateHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef PackageListSelectorStateRef =\n    AutoDisposeProviderRef<PackageListSelectorState>;\nString _$moreToolsSelectorStateHash() =>\n    r'6329d92652135e2af009dddeac590b4369d2fa04';\n\n/// See also [moreToolsSelectorState].\n@ProviderFor(moreToolsSelectorState)\nfinal moreToolsSelectorStateProvider =\n    AutoDisposeProvider<MoreToolsSelectorState>.internal(\n      moreToolsSelectorState,\n      name: r'moreToolsSelectorStateProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$moreToolsSelectorStateHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef MoreToolsSelectorStateRef =\n    AutoDisposeProviderRef<MoreToolsSelectorState>;\nString _$isCurrentPageHash() => r'7c300770aef90da23109d9fcfc3bf26140d8cd08';\n\n/// See also [isCurrentPage].\n@ProviderFor(isCurrentPage)\nconst isCurrentPageProvider = IsCurrentPageFamily();\n\n/// See also [isCurrentPage].\nclass IsCurrentPageFamily extends Family<bool> {\n  /// See also [isCurrentPage].\n  const IsCurrentPageFamily();\n\n  /// See also [isCurrentPage].\n  IsCurrentPageProvider call(\n    PageLabel pageLabel, {\n    bool Function(PageLabel, ViewMode)? handler,\n  }) {\n    return IsCurrentPageProvider(pageLabel, handler: handler);\n  }\n\n  @override\n  IsCurrentPageProvider getProviderOverride(\n    covariant IsCurrentPageProvider provider,\n  ) {\n    return call(provider.pageLabel, handler: provider.handler);\n  }\n\n  static const Iterable<ProviderOrFamily>? _dependencies = null;\n\n  @override\n  Iterable<ProviderOrFamily>? get dependencies => _dependencies;\n\n  static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;\n\n  @override\n  Iterable<ProviderOrFamily>? get allTransitiveDependencies =>\n      _allTransitiveDependencies;\n\n  @override\n  String? get name => r'isCurrentPageProvider';\n}\n\n/// See also [isCurrentPage].\nclass IsCurrentPageProvider extends AutoDisposeProvider<bool> {\n  /// See also [isCurrentPage].\n  IsCurrentPageProvider(\n    PageLabel pageLabel, {\n    bool Function(PageLabel, ViewMode)? handler,\n  }) : this._internal(\n         (ref) => isCurrentPage(\n           ref as IsCurrentPageRef,\n           pageLabel,\n           handler: handler,\n         ),\n         from: isCurrentPageProvider,\n         name: r'isCurrentPageProvider',\n         debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n             ? null\n             : _$isCurrentPageHash,\n         dependencies: IsCurrentPageFamily._dependencies,\n         allTransitiveDependencies:\n             IsCurrentPageFamily._allTransitiveDependencies,\n         pageLabel: pageLabel,\n         handler: handler,\n       );\n\n  IsCurrentPageProvider._internal(\n    super._createNotifier, {\n    required super.name,\n    required super.dependencies,\n    required super.allTransitiveDependencies,\n    required super.debugGetCreateSourceHash,\n    required super.from,\n    required this.pageLabel,\n    required this.handler,\n  }) : super.internal();\n\n  final PageLabel pageLabel;\n  final bool Function(PageLabel, ViewMode)? handler;\n\n  @override\n  Override overrideWith(bool Function(IsCurrentPageRef provider) create) {\n    return ProviderOverride(\n      origin: this,\n      override: IsCurrentPageProvider._internal(\n        (ref) => create(ref as IsCurrentPageRef),\n        from: from,\n        name: null,\n        dependencies: null,\n        allTransitiveDependencies: null,\n        debugGetCreateSourceHash: null,\n        pageLabel: pageLabel,\n        handler: handler,\n      ),\n    );\n  }\n\n  @override\n  AutoDisposeProviderElement<bool> createElement() {\n    return _IsCurrentPageProviderElement(this);\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return other is IsCurrentPageProvider &&\n        other.pageLabel == pageLabel &&\n        other.handler == handler;\n  }\n\n  @override\n  int get hashCode {\n    var hash = _SystemHash.combine(0, runtimeType.hashCode);\n    hash = _SystemHash.combine(hash, pageLabel.hashCode);\n    hash = _SystemHash.combine(hash, handler.hashCode);\n\n    return _SystemHash.finish(hash);\n  }\n}\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\nmixin IsCurrentPageRef on AutoDisposeProviderRef<bool> {\n  /// The parameter `pageLabel` of this provider.\n  PageLabel get pageLabel;\n\n  /// The parameter `handler` of this provider.\n  bool Function(PageLabel, ViewMode)? get handler;\n}\n\nclass _IsCurrentPageProviderElement extends AutoDisposeProviderElement<bool>\n    with IsCurrentPageRef {\n  _IsCurrentPageProviderElement(super.provider);\n\n  @override\n  PageLabel get pageLabel => (origin as IsCurrentPageProvider).pageLabel;\n  @override\n  bool Function(PageLabel, ViewMode)? get handler =>\n      (origin as IsCurrentPageProvider).handler;\n}\n\nString _$getRealTestUrlHash() => r'f2ac58afa6b99fbdab493e730e771ffd80440198';\n\n/// See also [getRealTestUrl].\n@ProviderFor(getRealTestUrl)\nconst getRealTestUrlProvider = GetRealTestUrlFamily();\n\n/// See also [getRealTestUrl].\nclass GetRealTestUrlFamily extends Family<String> {\n  /// See also [getRealTestUrl].\n  const GetRealTestUrlFamily();\n\n  /// See also [getRealTestUrl].\n  GetRealTestUrlProvider call([String? testUrl]) {\n    return GetRealTestUrlProvider(testUrl);\n  }\n\n  @override\n  GetRealTestUrlProvider getProviderOverride(\n    covariant GetRealTestUrlProvider provider,\n  ) {\n    return call(provider.testUrl);\n  }\n\n  static const Iterable<ProviderOrFamily>? _dependencies = null;\n\n  @override\n  Iterable<ProviderOrFamily>? get dependencies => _dependencies;\n\n  static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;\n\n  @override\n  Iterable<ProviderOrFamily>? get allTransitiveDependencies =>\n      _allTransitiveDependencies;\n\n  @override\n  String? get name => r'getRealTestUrlProvider';\n}\n\n/// See also [getRealTestUrl].\nclass GetRealTestUrlProvider extends AutoDisposeProvider<String> {\n  /// See also [getRealTestUrl].\n  GetRealTestUrlProvider([String? testUrl])\n    : this._internal(\n        (ref) => getRealTestUrl(ref as GetRealTestUrlRef, testUrl),\n        from: getRealTestUrlProvider,\n        name: r'getRealTestUrlProvider',\n        debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n            ? null\n            : _$getRealTestUrlHash,\n        dependencies: GetRealTestUrlFamily._dependencies,\n        allTransitiveDependencies:\n            GetRealTestUrlFamily._allTransitiveDependencies,\n        testUrl: testUrl,\n      );\n\n  GetRealTestUrlProvider._internal(\n    super._createNotifier, {\n    required super.name,\n    required super.dependencies,\n    required super.allTransitiveDependencies,\n    required super.debugGetCreateSourceHash,\n    required super.from,\n    required this.testUrl,\n  }) : super.internal();\n\n  final String? testUrl;\n\n  @override\n  Override overrideWith(String Function(GetRealTestUrlRef provider) create) {\n    return ProviderOverride(\n      origin: this,\n      override: GetRealTestUrlProvider._internal(\n        (ref) => create(ref as GetRealTestUrlRef),\n        from: from,\n        name: null,\n        dependencies: null,\n        allTransitiveDependencies: null,\n        debugGetCreateSourceHash: null,\n        testUrl: testUrl,\n      ),\n    );\n  }\n\n  @override\n  AutoDisposeProviderElement<String> createElement() {\n    return _GetRealTestUrlProviderElement(this);\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return other is GetRealTestUrlProvider && other.testUrl == testUrl;\n  }\n\n  @override\n  int get hashCode {\n    var hash = _SystemHash.combine(0, runtimeType.hashCode);\n    hash = _SystemHash.combine(hash, testUrl.hashCode);\n\n    return _SystemHash.finish(hash);\n  }\n}\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\nmixin GetRealTestUrlRef on AutoDisposeProviderRef<String> {\n  /// The parameter `testUrl` of this provider.\n  String? get testUrl;\n}\n\nclass _GetRealTestUrlProviderElement extends AutoDisposeProviderElement<String>\n    with GetRealTestUrlRef {\n  _GetRealTestUrlProviderElement(super.provider);\n\n  @override\n  String? get testUrl => (origin as GetRealTestUrlProvider).testUrl;\n}\n\nString _$getDelayHash() => r'541f25ef7ee423966a5e0d33a8522231c6ca12b1';\n\n/// See also [getDelay].\n@ProviderFor(getDelay)\nconst getDelayProvider = GetDelayFamily();\n\n/// See also [getDelay].\nclass GetDelayFamily extends Family<int?> {\n  /// See also [getDelay].\n  const GetDelayFamily();\n\n  /// See also [getDelay].\n  GetDelayProvider call({required String proxyName, String? testUrl}) {\n    return GetDelayProvider(proxyName: proxyName, testUrl: testUrl);\n  }\n\n  @override\n  GetDelayProvider getProviderOverride(covariant GetDelayProvider provider) {\n    return call(proxyName: provider.proxyName, testUrl: provider.testUrl);\n  }\n\n  static const Iterable<ProviderOrFamily>? _dependencies = null;\n\n  @override\n  Iterable<ProviderOrFamily>? get dependencies => _dependencies;\n\n  static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;\n\n  @override\n  Iterable<ProviderOrFamily>? get allTransitiveDependencies =>\n      _allTransitiveDependencies;\n\n  @override\n  String? get name => r'getDelayProvider';\n}\n\n/// See also [getDelay].\nclass GetDelayProvider extends AutoDisposeProvider<int?> {\n  /// See also [getDelay].\n  GetDelayProvider({required String proxyName, String? testUrl})\n    : this._internal(\n        (ref) => getDelay(\n          ref as GetDelayRef,\n          proxyName: proxyName,\n          testUrl: testUrl,\n        ),\n        from: getDelayProvider,\n        name: r'getDelayProvider',\n        debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n            ? null\n            : _$getDelayHash,\n        dependencies: GetDelayFamily._dependencies,\n        allTransitiveDependencies: GetDelayFamily._allTransitiveDependencies,\n        proxyName: proxyName,\n        testUrl: testUrl,\n      );\n\n  GetDelayProvider._internal(\n    super._createNotifier, {\n    required super.name,\n    required super.dependencies,\n    required super.allTransitiveDependencies,\n    required super.debugGetCreateSourceHash,\n    required super.from,\n    required this.proxyName,\n    required this.testUrl,\n  }) : super.internal();\n\n  final String proxyName;\n  final String? testUrl;\n\n  @override\n  Override overrideWith(int? Function(GetDelayRef provider) create) {\n    return ProviderOverride(\n      origin: this,\n      override: GetDelayProvider._internal(\n        (ref) => create(ref as GetDelayRef),\n        from: from,\n        name: null,\n        dependencies: null,\n        allTransitiveDependencies: null,\n        debugGetCreateSourceHash: null,\n        proxyName: proxyName,\n        testUrl: testUrl,\n      ),\n    );\n  }\n\n  @override\n  AutoDisposeProviderElement<int?> createElement() {\n    return _GetDelayProviderElement(this);\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return other is GetDelayProvider &&\n        other.proxyName == proxyName &&\n        other.testUrl == testUrl;\n  }\n\n  @override\n  int get hashCode {\n    var hash = _SystemHash.combine(0, runtimeType.hashCode);\n    hash = _SystemHash.combine(hash, proxyName.hashCode);\n    hash = _SystemHash.combine(hash, testUrl.hashCode);\n\n    return _SystemHash.finish(hash);\n  }\n}\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\nmixin GetDelayRef on AutoDisposeProviderRef<int?> {\n  /// The parameter `proxyName` of this provider.\n  String get proxyName;\n\n  /// The parameter `testUrl` of this provider.\n  String? get testUrl;\n}\n\nclass _GetDelayProviderElement extends AutoDisposeProviderElement<int?>\n    with GetDelayRef {\n  _GetDelayProviderElement(super.provider);\n\n  @override\n  String get proxyName => (origin as GetDelayProvider).proxyName;\n  @override\n  String? get testUrl => (origin as GetDelayProvider).testUrl;\n}\n\nString _$selectedMapHash() => r'0d7a3610d9005e74e1a88595d7e22897dc8240a5';\n\n/// See also [selectedMap].\n@ProviderFor(selectedMap)\nfinal selectedMapProvider = AutoDisposeProvider<SelectedMap>.internal(\n  selectedMap,\n  name: r'selectedMapProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$selectedMapHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef SelectedMapRef = AutoDisposeProviderRef<SelectedMap>;\nString _$unfoldSetHash() => r'59a5b417611533069462ddf31eca080ab2f74ac9';\n\n/// See also [unfoldSet].\n@ProviderFor(unfoldSet)\nfinal unfoldSetProvider = AutoDisposeProvider<Set<String>>.internal(\n  unfoldSet,\n  name: r'unfoldSetProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$unfoldSetHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef UnfoldSetRef = AutoDisposeProviderRef<Set<String>>;\nString _$getHotKeyActionHash() => r'4dc74ea7ffb25624ce70c7c8214806f3ef022223';\n\n/// See also [getHotKeyAction].\n@ProviderFor(getHotKeyAction)\nconst getHotKeyActionProvider = GetHotKeyActionFamily();\n\n/// See also [getHotKeyAction].\nclass GetHotKeyActionFamily extends Family<HotKeyAction> {\n  /// See also [getHotKeyAction].\n  const GetHotKeyActionFamily();\n\n  /// See also [getHotKeyAction].\n  GetHotKeyActionProvider call(HotAction hotAction) {\n    return GetHotKeyActionProvider(hotAction);\n  }\n\n  @override\n  GetHotKeyActionProvider getProviderOverride(\n    covariant GetHotKeyActionProvider provider,\n  ) {\n    return call(provider.hotAction);\n  }\n\n  static const Iterable<ProviderOrFamily>? _dependencies = null;\n\n  @override\n  Iterable<ProviderOrFamily>? get dependencies => _dependencies;\n\n  static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;\n\n  @override\n  Iterable<ProviderOrFamily>? get allTransitiveDependencies =>\n      _allTransitiveDependencies;\n\n  @override\n  String? get name => r'getHotKeyActionProvider';\n}\n\n/// See also [getHotKeyAction].\nclass GetHotKeyActionProvider extends AutoDisposeProvider<HotKeyAction> {\n  /// See also [getHotKeyAction].\n  GetHotKeyActionProvider(HotAction hotAction)\n    : this._internal(\n        (ref) => getHotKeyAction(ref as GetHotKeyActionRef, hotAction),\n        from: getHotKeyActionProvider,\n        name: r'getHotKeyActionProvider',\n        debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n            ? null\n            : _$getHotKeyActionHash,\n        dependencies: GetHotKeyActionFamily._dependencies,\n        allTransitiveDependencies:\n            GetHotKeyActionFamily._allTransitiveDependencies,\n        hotAction: hotAction,\n      );\n\n  GetHotKeyActionProvider._internal(\n    super._createNotifier, {\n    required super.name,\n    required super.dependencies,\n    required super.allTransitiveDependencies,\n    required super.debugGetCreateSourceHash,\n    required super.from,\n    required this.hotAction,\n  }) : super.internal();\n\n  final HotAction hotAction;\n\n  @override\n  Override overrideWith(\n    HotKeyAction Function(GetHotKeyActionRef provider) create,\n  ) {\n    return ProviderOverride(\n      origin: this,\n      override: GetHotKeyActionProvider._internal(\n        (ref) => create(ref as GetHotKeyActionRef),\n        from: from,\n        name: null,\n        dependencies: null,\n        allTransitiveDependencies: null,\n        debugGetCreateSourceHash: null,\n        hotAction: hotAction,\n      ),\n    );\n  }\n\n  @override\n  AutoDisposeProviderElement<HotKeyAction> createElement() {\n    return _GetHotKeyActionProviderElement(this);\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return other is GetHotKeyActionProvider && other.hotAction == hotAction;\n  }\n\n  @override\n  int get hashCode {\n    var hash = _SystemHash.combine(0, runtimeType.hashCode);\n    hash = _SystemHash.combine(hash, hotAction.hashCode);\n\n    return _SystemHash.finish(hash);\n  }\n}\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\nmixin GetHotKeyActionRef on AutoDisposeProviderRef<HotKeyAction> {\n  /// The parameter `hotAction` of this provider.\n  HotAction get hotAction;\n}\n\nclass _GetHotKeyActionProviderElement\n    extends AutoDisposeProviderElement<HotKeyAction>\n    with GetHotKeyActionRef {\n  _GetHotKeyActionProviderElement(super.provider);\n\n  @override\n  HotAction get hotAction => (origin as GetHotKeyActionProvider).hotAction;\n}\n\nString _$currentProfileHash() => r'55f3cb9570a0aa6b9e0b83a36693b69d52e753ab';\n\n/// See also [currentProfile].\n@ProviderFor(currentProfile)\nfinal currentProfileProvider = AutoDisposeProvider<Profile?>.internal(\n  currentProfile,\n  name: r'currentProfileProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$currentProfileHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef CurrentProfileRef = AutoDisposeProviderRef<Profile?>;\nString _$getProxiesColumnsHash() => r'725066b5fc21f590a4c2656a1fd5e14ab7079079';\n\n/// See also [getProxiesColumns].\n@ProviderFor(getProxiesColumns)\nfinal getProxiesColumnsProvider = AutoDisposeProvider<int>.internal(\n  getProxiesColumns,\n  name: r'getProxiesColumnsProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$getProxiesColumnsHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef GetProxiesColumnsRef = AutoDisposeProviderRef<int>;\nString _$getProxyCardStateHash() => r'0f131148cb5ed60c9c4c4f31fbe32f114ac346bb';\n\n/// See also [getProxyCardState].\n@ProviderFor(getProxyCardState)\nconst getProxyCardStateProvider = GetProxyCardStateFamily();\n\n/// See also [getProxyCardState].\nclass GetProxyCardStateFamily extends Family<ProxyCardState> {\n  /// See also [getProxyCardState].\n  const GetProxyCardStateFamily();\n\n  /// See also [getProxyCardState].\n  GetProxyCardStateProvider call(String proxyName) {\n    return GetProxyCardStateProvider(proxyName);\n  }\n\n  @override\n  GetProxyCardStateProvider getProviderOverride(\n    covariant GetProxyCardStateProvider provider,\n  ) {\n    return call(provider.proxyName);\n  }\n\n  static const Iterable<ProviderOrFamily>? _dependencies = null;\n\n  @override\n  Iterable<ProviderOrFamily>? get dependencies => _dependencies;\n\n  static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;\n\n  @override\n  Iterable<ProviderOrFamily>? get allTransitiveDependencies =>\n      _allTransitiveDependencies;\n\n  @override\n  String? get name => r'getProxyCardStateProvider';\n}\n\n/// See also [getProxyCardState].\nclass GetProxyCardStateProvider extends AutoDisposeProvider<ProxyCardState> {\n  /// See also [getProxyCardState].\n  GetProxyCardStateProvider(String proxyName)\n    : this._internal(\n        (ref) => getProxyCardState(ref as GetProxyCardStateRef, proxyName),\n        from: getProxyCardStateProvider,\n        name: r'getProxyCardStateProvider',\n        debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n            ? null\n            : _$getProxyCardStateHash,\n        dependencies: GetProxyCardStateFamily._dependencies,\n        allTransitiveDependencies:\n            GetProxyCardStateFamily._allTransitiveDependencies,\n        proxyName: proxyName,\n      );\n\n  GetProxyCardStateProvider._internal(\n    super._createNotifier, {\n    required super.name,\n    required super.dependencies,\n    required super.allTransitiveDependencies,\n    required super.debugGetCreateSourceHash,\n    required super.from,\n    required this.proxyName,\n  }) : super.internal();\n\n  final String proxyName;\n\n  @override\n  Override overrideWith(\n    ProxyCardState Function(GetProxyCardStateRef provider) create,\n  ) {\n    return ProviderOverride(\n      origin: this,\n      override: GetProxyCardStateProvider._internal(\n        (ref) => create(ref as GetProxyCardStateRef),\n        from: from,\n        name: null,\n        dependencies: null,\n        allTransitiveDependencies: null,\n        debugGetCreateSourceHash: null,\n        proxyName: proxyName,\n      ),\n    );\n  }\n\n  @override\n  AutoDisposeProviderElement<ProxyCardState> createElement() {\n    return _GetProxyCardStateProviderElement(this);\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return other is GetProxyCardStateProvider && other.proxyName == proxyName;\n  }\n\n  @override\n  int get hashCode {\n    var hash = _SystemHash.combine(0, runtimeType.hashCode);\n    hash = _SystemHash.combine(hash, proxyName.hashCode);\n\n    return _SystemHash.finish(hash);\n  }\n}\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\nmixin GetProxyCardStateRef on AutoDisposeProviderRef<ProxyCardState> {\n  /// The parameter `proxyName` of this provider.\n  String get proxyName;\n}\n\nclass _GetProxyCardStateProviderElement\n    extends AutoDisposeProviderElement<ProxyCardState>\n    with GetProxyCardStateRef {\n  _GetProxyCardStateProviderElement(super.provider);\n\n  @override\n  String get proxyName => (origin as GetProxyCardStateProvider).proxyName;\n}\n\nString _$getProxyNameHash() => r'204a477ea18c8e1eeef55b3efd3d47e45b0d2350';\n\n/// See also [getProxyName].\n@ProviderFor(getProxyName)\nconst getProxyNameProvider = GetProxyNameFamily();\n\n/// See also [getProxyName].\nclass GetProxyNameFamily extends Family<String?> {\n  /// See also [getProxyName].\n  const GetProxyNameFamily();\n\n  /// See also [getProxyName].\n  GetProxyNameProvider call(String groupName) {\n    return GetProxyNameProvider(groupName);\n  }\n\n  @override\n  GetProxyNameProvider getProviderOverride(\n    covariant GetProxyNameProvider provider,\n  ) {\n    return call(provider.groupName);\n  }\n\n  static const Iterable<ProviderOrFamily>? _dependencies = null;\n\n  @override\n  Iterable<ProviderOrFamily>? get dependencies => _dependencies;\n\n  static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;\n\n  @override\n  Iterable<ProviderOrFamily>? get allTransitiveDependencies =>\n      _allTransitiveDependencies;\n\n  @override\n  String? get name => r'getProxyNameProvider';\n}\n\n/// See also [getProxyName].\nclass GetProxyNameProvider extends AutoDisposeProvider<String?> {\n  /// See also [getProxyName].\n  GetProxyNameProvider(String groupName)\n    : this._internal(\n        (ref) => getProxyName(ref as GetProxyNameRef, groupName),\n        from: getProxyNameProvider,\n        name: r'getProxyNameProvider',\n        debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n            ? null\n            : _$getProxyNameHash,\n        dependencies: GetProxyNameFamily._dependencies,\n        allTransitiveDependencies:\n            GetProxyNameFamily._allTransitiveDependencies,\n        groupName: groupName,\n      );\n\n  GetProxyNameProvider._internal(\n    super._createNotifier, {\n    required super.name,\n    required super.dependencies,\n    required super.allTransitiveDependencies,\n    required super.debugGetCreateSourceHash,\n    required super.from,\n    required this.groupName,\n  }) : super.internal();\n\n  final String groupName;\n\n  @override\n  Override overrideWith(String? Function(GetProxyNameRef provider) create) {\n    return ProviderOverride(\n      origin: this,\n      override: GetProxyNameProvider._internal(\n        (ref) => create(ref as GetProxyNameRef),\n        from: from,\n        name: null,\n        dependencies: null,\n        allTransitiveDependencies: null,\n        debugGetCreateSourceHash: null,\n        groupName: groupName,\n      ),\n    );\n  }\n\n  @override\n  AutoDisposeProviderElement<String?> createElement() {\n    return _GetProxyNameProviderElement(this);\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return other is GetProxyNameProvider && other.groupName == groupName;\n  }\n\n  @override\n  int get hashCode {\n    var hash = _SystemHash.combine(0, runtimeType.hashCode);\n    hash = _SystemHash.combine(hash, groupName.hashCode);\n\n    return _SystemHash.finish(hash);\n  }\n}\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\nmixin GetProxyNameRef on AutoDisposeProviderRef<String?> {\n  /// The parameter `groupName` of this provider.\n  String get groupName;\n}\n\nclass _GetProxyNameProviderElement extends AutoDisposeProviderElement<String?>\n    with GetProxyNameRef {\n  _GetProxyNameProviderElement(super.provider);\n\n  @override\n  String get groupName => (origin as GetProxyNameProvider).groupName;\n}\n\nString _$getSelectedProxyNameHash() =>\n    r'13aeae1fede234983d262d824a85c7375f9e4e78';\n\n/// See also [getSelectedProxyName].\n@ProviderFor(getSelectedProxyName)\nconst getSelectedProxyNameProvider = GetSelectedProxyNameFamily();\n\n/// See also [getSelectedProxyName].\nclass GetSelectedProxyNameFamily extends Family<String?> {\n  /// See also [getSelectedProxyName].\n  const GetSelectedProxyNameFamily();\n\n  /// See also [getSelectedProxyName].\n  GetSelectedProxyNameProvider call(String groupName) {\n    return GetSelectedProxyNameProvider(groupName);\n  }\n\n  @override\n  GetSelectedProxyNameProvider getProviderOverride(\n    covariant GetSelectedProxyNameProvider provider,\n  ) {\n    return call(provider.groupName);\n  }\n\n  static const Iterable<ProviderOrFamily>? _dependencies = null;\n\n  @override\n  Iterable<ProviderOrFamily>? get dependencies => _dependencies;\n\n  static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;\n\n  @override\n  Iterable<ProviderOrFamily>? get allTransitiveDependencies =>\n      _allTransitiveDependencies;\n\n  @override\n  String? get name => r'getSelectedProxyNameProvider';\n}\n\n/// See also [getSelectedProxyName].\nclass GetSelectedProxyNameProvider extends AutoDisposeProvider<String?> {\n  /// See also [getSelectedProxyName].\n  GetSelectedProxyNameProvider(String groupName)\n    : this._internal(\n        (ref) =>\n            getSelectedProxyName(ref as GetSelectedProxyNameRef, groupName),\n        from: getSelectedProxyNameProvider,\n        name: r'getSelectedProxyNameProvider',\n        debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n            ? null\n            : _$getSelectedProxyNameHash,\n        dependencies: GetSelectedProxyNameFamily._dependencies,\n        allTransitiveDependencies:\n            GetSelectedProxyNameFamily._allTransitiveDependencies,\n        groupName: groupName,\n      );\n\n  GetSelectedProxyNameProvider._internal(\n    super._createNotifier, {\n    required super.name,\n    required super.dependencies,\n    required super.allTransitiveDependencies,\n    required super.debugGetCreateSourceHash,\n    required super.from,\n    required this.groupName,\n  }) : super.internal();\n\n  final String groupName;\n\n  @override\n  Override overrideWith(\n    String? Function(GetSelectedProxyNameRef provider) create,\n  ) {\n    return ProviderOverride(\n      origin: this,\n      override: GetSelectedProxyNameProvider._internal(\n        (ref) => create(ref as GetSelectedProxyNameRef),\n        from: from,\n        name: null,\n        dependencies: null,\n        allTransitiveDependencies: null,\n        debugGetCreateSourceHash: null,\n        groupName: groupName,\n      ),\n    );\n  }\n\n  @override\n  AutoDisposeProviderElement<String?> createElement() {\n    return _GetSelectedProxyNameProviderElement(this);\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return other is GetSelectedProxyNameProvider &&\n        other.groupName == groupName;\n  }\n\n  @override\n  int get hashCode {\n    var hash = _SystemHash.combine(0, runtimeType.hashCode);\n    hash = _SystemHash.combine(hash, groupName.hashCode);\n\n    return _SystemHash.finish(hash);\n  }\n}\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\nmixin GetSelectedProxyNameRef on AutoDisposeProviderRef<String?> {\n  /// The parameter `groupName` of this provider.\n  String get groupName;\n}\n\nclass _GetSelectedProxyNameProviderElement\n    extends AutoDisposeProviderElement<String?>\n    with GetSelectedProxyNameRef {\n  _GetSelectedProxyNameProviderElement(super.provider);\n\n  @override\n  String get groupName => (origin as GetSelectedProxyNameProvider).groupName;\n}\n\nString _$getProxyDescHash() => r'c173fe2393d9c4f5d5d17480e69f9126bb76a17d';\n\n/// See also [getProxyDesc].\n@ProviderFor(getProxyDesc)\nconst getProxyDescProvider = GetProxyDescFamily();\n\n/// See also [getProxyDesc].\nclass GetProxyDescFamily extends Family<String> {\n  /// See also [getProxyDesc].\n  const GetProxyDescFamily();\n\n  /// See also [getProxyDesc].\n  GetProxyDescProvider call(Proxy proxy) {\n    return GetProxyDescProvider(proxy);\n  }\n\n  @override\n  GetProxyDescProvider getProviderOverride(\n    covariant GetProxyDescProvider provider,\n  ) {\n    return call(provider.proxy);\n  }\n\n  static const Iterable<ProviderOrFamily>? _dependencies = null;\n\n  @override\n  Iterable<ProviderOrFamily>? get dependencies => _dependencies;\n\n  static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;\n\n  @override\n  Iterable<ProviderOrFamily>? get allTransitiveDependencies =>\n      _allTransitiveDependencies;\n\n  @override\n  String? get name => r'getProxyDescProvider';\n}\n\n/// See also [getProxyDesc].\nclass GetProxyDescProvider extends AutoDisposeProvider<String> {\n  /// See also [getProxyDesc].\n  GetProxyDescProvider(Proxy proxy)\n    : this._internal(\n        (ref) => getProxyDesc(ref as GetProxyDescRef, proxy),\n        from: getProxyDescProvider,\n        name: r'getProxyDescProvider',\n        debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n            ? null\n            : _$getProxyDescHash,\n        dependencies: GetProxyDescFamily._dependencies,\n        allTransitiveDependencies:\n            GetProxyDescFamily._allTransitiveDependencies,\n        proxy: proxy,\n      );\n\n  GetProxyDescProvider._internal(\n    super._createNotifier, {\n    required super.name,\n    required super.dependencies,\n    required super.allTransitiveDependencies,\n    required super.debugGetCreateSourceHash,\n    required super.from,\n    required this.proxy,\n  }) : super.internal();\n\n  final Proxy proxy;\n\n  @override\n  Override overrideWith(String Function(GetProxyDescRef provider) create) {\n    return ProviderOverride(\n      origin: this,\n      override: GetProxyDescProvider._internal(\n        (ref) => create(ref as GetProxyDescRef),\n        from: from,\n        name: null,\n        dependencies: null,\n        allTransitiveDependencies: null,\n        debugGetCreateSourceHash: null,\n        proxy: proxy,\n      ),\n    );\n  }\n\n  @override\n  AutoDisposeProviderElement<String> createElement() {\n    return _GetProxyDescProviderElement(this);\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return other is GetProxyDescProvider && other.proxy == proxy;\n  }\n\n  @override\n  int get hashCode {\n    var hash = _SystemHash.combine(0, runtimeType.hashCode);\n    hash = _SystemHash.combine(hash, proxy.hashCode);\n\n    return _SystemHash.finish(hash);\n  }\n}\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\nmixin GetProxyDescRef on AutoDisposeProviderRef<String> {\n  /// The parameter `proxy` of this provider.\n  Proxy get proxy;\n}\n\nclass _GetProxyDescProviderElement extends AutoDisposeProviderElement<String>\n    with GetProxyDescRef {\n  _GetProxyDescProviderElement(super.provider);\n\n  @override\n  Proxy get proxy => (origin as GetProxyDescProvider).proxy;\n}\n\nString _$getProfileOverrideDataHash() =>\n    r'a17ec085f1733b63b123ac08aa7737588c048c5f';\n\n/// See also [getProfileOverrideData].\n@ProviderFor(getProfileOverrideData)\nconst getProfileOverrideDataProvider = GetProfileOverrideDataFamily();\n\n/// See also [getProfileOverrideData].\nclass GetProfileOverrideDataFamily extends Family<OverrideData?> {\n  /// See also [getProfileOverrideData].\n  const GetProfileOverrideDataFamily();\n\n  /// See also [getProfileOverrideData].\n  GetProfileOverrideDataProvider call(String profileId) {\n    return GetProfileOverrideDataProvider(profileId);\n  }\n\n  @override\n  GetProfileOverrideDataProvider getProviderOverride(\n    covariant GetProfileOverrideDataProvider provider,\n  ) {\n    return call(provider.profileId);\n  }\n\n  static const Iterable<ProviderOrFamily>? _dependencies = null;\n\n  @override\n  Iterable<ProviderOrFamily>? get dependencies => _dependencies;\n\n  static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;\n\n  @override\n  Iterable<ProviderOrFamily>? get allTransitiveDependencies =>\n      _allTransitiveDependencies;\n\n  @override\n  String? get name => r'getProfileOverrideDataProvider';\n}\n\n/// See also [getProfileOverrideData].\nclass GetProfileOverrideDataProvider\n    extends AutoDisposeProvider<OverrideData?> {\n  /// See also [getProfileOverrideData].\n  GetProfileOverrideDataProvider(String profileId)\n    : this._internal(\n        (ref) =>\n            getProfileOverrideData(ref as GetProfileOverrideDataRef, profileId),\n        from: getProfileOverrideDataProvider,\n        name: r'getProfileOverrideDataProvider',\n        debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n            ? null\n            : _$getProfileOverrideDataHash,\n        dependencies: GetProfileOverrideDataFamily._dependencies,\n        allTransitiveDependencies:\n            GetProfileOverrideDataFamily._allTransitiveDependencies,\n        profileId: profileId,\n      );\n\n  GetProfileOverrideDataProvider._internal(\n    super._createNotifier, {\n    required super.name,\n    required super.dependencies,\n    required super.allTransitiveDependencies,\n    required super.debugGetCreateSourceHash,\n    required super.from,\n    required this.profileId,\n  }) : super.internal();\n\n  final String profileId;\n\n  @override\n  Override overrideWith(\n    OverrideData? Function(GetProfileOverrideDataRef provider) create,\n  ) {\n    return ProviderOverride(\n      origin: this,\n      override: GetProfileOverrideDataProvider._internal(\n        (ref) => create(ref as GetProfileOverrideDataRef),\n        from: from,\n        name: null,\n        dependencies: null,\n        allTransitiveDependencies: null,\n        debugGetCreateSourceHash: null,\n        profileId: profileId,\n      ),\n    );\n  }\n\n  @override\n  AutoDisposeProviderElement<OverrideData?> createElement() {\n    return _GetProfileOverrideDataProviderElement(this);\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return other is GetProfileOverrideDataProvider &&\n        other.profileId == profileId;\n  }\n\n  @override\n  int get hashCode {\n    var hash = _SystemHash.combine(0, runtimeType.hashCode);\n    hash = _SystemHash.combine(hash, profileId.hashCode);\n\n    return _SystemHash.finish(hash);\n  }\n}\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\nmixin GetProfileOverrideDataRef on AutoDisposeProviderRef<OverrideData?> {\n  /// The parameter `profileId` of this provider.\n  String get profileId;\n}\n\nclass _GetProfileOverrideDataProviderElement\n    extends AutoDisposeProviderElement<OverrideData?>\n    with GetProfileOverrideDataRef {\n  _GetProfileOverrideDataProviderElement(super.provider);\n\n  @override\n  String get profileId => (origin as GetProfileOverrideDataProvider).profileId;\n}\n\nString _$layoutChangeHash() => r'f25182e1dfaf3c70000404d7635bb1e1db09efbb';\n\n/// See also [layoutChange].\n@ProviderFor(layoutChange)\nfinal layoutChangeProvider = AutoDisposeProvider<VM2?>.internal(\n  layoutChange,\n  name: r'layoutChangeProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$layoutChangeHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef LayoutChangeRef = AutoDisposeProviderRef<VM2?>;\nString _$checkIpHash() => r'07ebf8d032349e2b3adda483e68b1936ffbed68d';\n\n/// See also [checkIp].\n@ProviderFor(checkIp)\nfinal checkIpProvider = AutoDisposeProvider<VM2<int, bool>>.internal(\n  checkIp,\n  name: r'checkIpProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$checkIpHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef CheckIpRef = AutoDisposeProviderRef<VM2<int, bool>>;\nString _$genColorSchemeHash() => r'b18f15c938a8132ee4ed02cdfc02f3b9f01724e2';\n\n/// See also [genColorScheme].\n@ProviderFor(genColorScheme)\nconst genColorSchemeProvider = GenColorSchemeFamily();\n\n/// See also [genColorScheme].\nclass GenColorSchemeFamily extends Family<ColorScheme> {\n  /// See also [genColorScheme].\n  const GenColorSchemeFamily();\n\n  /// See also [genColorScheme].\n  GenColorSchemeProvider call(\n    Brightness brightness, {\n    Color? color,\n    bool ignoreConfig = false,\n  }) {\n    return GenColorSchemeProvider(\n      brightness,\n      color: color,\n      ignoreConfig: ignoreConfig,\n    );\n  }\n\n  @override\n  GenColorSchemeProvider getProviderOverride(\n    covariant GenColorSchemeProvider provider,\n  ) {\n    return call(\n      provider.brightness,\n      color: provider.color,\n      ignoreConfig: provider.ignoreConfig,\n    );\n  }\n\n  static const Iterable<ProviderOrFamily>? _dependencies = null;\n\n  @override\n  Iterable<ProviderOrFamily>? get dependencies => _dependencies;\n\n  static const Iterable<ProviderOrFamily>? _allTransitiveDependencies = null;\n\n  @override\n  Iterable<ProviderOrFamily>? get allTransitiveDependencies =>\n      _allTransitiveDependencies;\n\n  @override\n  String? get name => r'genColorSchemeProvider';\n}\n\n/// See also [genColorScheme].\nclass GenColorSchemeProvider extends AutoDisposeProvider<ColorScheme> {\n  /// See also [genColorScheme].\n  GenColorSchemeProvider(\n    Brightness brightness, {\n    Color? color,\n    bool ignoreConfig = false,\n  }) : this._internal(\n         (ref) => genColorScheme(\n           ref as GenColorSchemeRef,\n           brightness,\n           color: color,\n           ignoreConfig: ignoreConfig,\n         ),\n         from: genColorSchemeProvider,\n         name: r'genColorSchemeProvider',\n         debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n             ? null\n             : _$genColorSchemeHash,\n         dependencies: GenColorSchemeFamily._dependencies,\n         allTransitiveDependencies:\n             GenColorSchemeFamily._allTransitiveDependencies,\n         brightness: brightness,\n         color: color,\n         ignoreConfig: ignoreConfig,\n       );\n\n  GenColorSchemeProvider._internal(\n    super._createNotifier, {\n    required super.name,\n    required super.dependencies,\n    required super.allTransitiveDependencies,\n    required super.debugGetCreateSourceHash,\n    required super.from,\n    required this.brightness,\n    required this.color,\n    required this.ignoreConfig,\n  }) : super.internal();\n\n  final Brightness brightness;\n  final Color? color;\n  final bool ignoreConfig;\n\n  @override\n  Override overrideWith(\n    ColorScheme Function(GenColorSchemeRef provider) create,\n  ) {\n    return ProviderOverride(\n      origin: this,\n      override: GenColorSchemeProvider._internal(\n        (ref) => create(ref as GenColorSchemeRef),\n        from: from,\n        name: null,\n        dependencies: null,\n        allTransitiveDependencies: null,\n        debugGetCreateSourceHash: null,\n        brightness: brightness,\n        color: color,\n        ignoreConfig: ignoreConfig,\n      ),\n    );\n  }\n\n  @override\n  AutoDisposeProviderElement<ColorScheme> createElement() {\n    return _GenColorSchemeProviderElement(this);\n  }\n\n  @override\n  bool operator ==(Object other) {\n    return other is GenColorSchemeProvider &&\n        other.brightness == brightness &&\n        other.color == color &&\n        other.ignoreConfig == ignoreConfig;\n  }\n\n  @override\n  int get hashCode {\n    var hash = _SystemHash.combine(0, runtimeType.hashCode);\n    hash = _SystemHash.combine(hash, brightness.hashCode);\n    hash = _SystemHash.combine(hash, color.hashCode);\n    hash = _SystemHash.combine(hash, ignoreConfig.hashCode);\n\n    return _SystemHash.finish(hash);\n  }\n}\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\nmixin GenColorSchemeRef on AutoDisposeProviderRef<ColorScheme> {\n  /// The parameter `brightness` of this provider.\n  Brightness get brightness;\n\n  /// The parameter `color` of this provider.\n  Color? get color;\n\n  /// The parameter `ignoreConfig` of this provider.\n  bool get ignoreConfig;\n}\n\nclass _GenColorSchemeProviderElement\n    extends AutoDisposeProviderElement<ColorScheme>\n    with GenColorSchemeRef {\n  _GenColorSchemeProviderElement(super.provider);\n\n  @override\n  Brightness get brightness => (origin as GenColorSchemeProvider).brightness;\n  @override\n  Color? get color => (origin as GenColorSchemeProvider).color;\n  @override\n  bool get ignoreConfig => (origin as GenColorSchemeProvider).ignoreConfig;\n}\n\nString _$needSetupHash() => r'3668e8dc9f40a9bea45c94321804eb3afa0e7c51';\n\n/// See also [needSetup].\n@ProviderFor(needSetup)\nfinal needSetupProvider =\n    AutoDisposeProvider<VM3<String?, String?, Dns?>>.internal(\n      needSetup,\n      name: r'needSetupProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$needSetupHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef NeedSetupRef = AutoDisposeProviderRef<VM3<String?, String?, Dns?>>;\nString _$currentBrightnessHash() => r'ab56c47af4fcae773c8f9f81c91800c1e1890b70';\n\n/// See also [currentBrightness].\n@ProviderFor(currentBrightness)\nfinal currentBrightnessProvider = AutoDisposeProvider<Brightness>.internal(\n  currentBrightness,\n  name: r'currentBrightnessProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$currentBrightnessHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef CurrentBrightnessRef = AutoDisposeProviderRef<Brightness>;\nString _$autoSetSystemDnsStateHash() =>\n    r'2e0976e079100325b1ca797285df48a94c2c066c';\n\n/// See also [autoSetSystemDnsState].\n@ProviderFor(autoSetSystemDnsState)\nfinal autoSetSystemDnsStateProvider =\n    AutoDisposeProvider<VM2<bool, bool>>.internal(\n      autoSetSystemDnsState,\n      name: r'autoSetSystemDnsStateProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$autoSetSystemDnsStateHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\n@Deprecated('Will be removed in 3.0. Use Ref instead')\n// ignore: unused_element\ntypedef AutoSetSystemDnsStateRef = AutoDisposeProviderRef<VM2<bool, bool>>;\nString _$profileOverrideStateHash() =>\n    r'0a1657ff3b4657fcc481a4cdd1deda5b353bf845';\n\n/// See also [ProfileOverrideState].\n@ProviderFor(ProfileOverrideState)\nfinal profileOverrideStateProvider =\n    AutoDisposeNotifierProvider<\n      ProfileOverrideState,\n      ProfileOverrideStateModel\n    >.internal(\n      ProfileOverrideState.new,\n      name: r'profileOverrideStateProvider',\n      debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n          ? null\n          : _$profileOverrideStateHash,\n      dependencies: null,\n      allTransitiveDependencies: null,\n    );\n\ntypedef _$ProfileOverrideState = AutoDisposeNotifier<ProfileOverrideStateModel>;\nString _$queryHash() => r'e99b2a2439872f88f09fee8d63f0cc7fb4852186';\n\n/// See also [Query].\n@ProviderFor(Query)\nfinal queryProvider = AutoDisposeNotifierProvider<Query, String>.internal(\n  Query.new,\n  name: r'queryProvider',\n  debugGetCreateSourceHash: const bool.fromEnvironment('dart.vm.product')\n      ? null\n      : _$queryHash,\n  dependencies: null,\n  allTransitiveDependencies: null,\n);\n\ntypedef _$Query = AutoDisposeNotifier<String>;\n// ignore_for_file: type=lint\n// ignore_for_file: subtype_of_sealed_class, invalid_use_of_internal_member, invalid_use_of_visible_for_testing_member, deprecated_member_use_from_same_package\n"
  },
  {
    "path": "lib/providers/providers.dart",
    "content": "export 'app.dart';\nexport 'config.dart';\nexport 'state.dart';\n"
  },
  {
    "path": "lib/providers/state.dart",
    "content": "import 'package:dynamic_color/dynamic_color.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:riverpod_annotation/riverpod_annotation.dart';\n\nimport 'app.dart';\nimport 'config.dart';\n\npart 'generated/state.g.dart';\n\n@riverpod\nConfig configState(Ref ref) {\n  final themeProps = ref.watch(themeSettingProvider);\n  final patchClashConfig = ref.watch(patchClashConfigProvider);\n  final appSetting = ref.watch(appSettingProvider);\n  final profiles = ref.watch(profilesProvider);\n  final currentProfileId = ref.watch(currentProfileIdProvider);\n  final overrideDns = ref.watch(overrideDnsProvider);\n  final networkProps = ref.watch(networkSettingProvider);\n  final vpnProps = ref.watch(vpnSettingProvider);\n  final proxiesStyle = ref.watch(proxiesStyleSettingProvider);\n  final scriptProps = ref.watch(scriptStateProvider);\n  final hotKeyActions = ref.watch(hotKeyActionsProvider);\n  final dav = ref.watch(appDAVSettingProvider);\n  final windowProps = ref.watch(windowSettingProvider);\n  return Config(\n    dav: dav,\n    windowProps: windowProps,\n    hotKeyActions: hotKeyActions,\n    scriptProps: scriptProps,\n    proxiesStyle: proxiesStyle,\n    vpnProps: vpnProps,\n    networkProps: networkProps,\n    overrideDns: overrideDns,\n    currentProfileId: currentProfileId,\n    profiles: profiles,\n    appSetting: appSetting,\n    themeProps: themeProps,\n    patchClashConfig: patchClashConfig,\n  );\n}\n\n@riverpod\nGroupsState currentGroupsState(Ref ref) {\n  final mode = ref.watch(\n    patchClashConfigProvider.select((state) => state.mode),\n  );\n  final groups = ref.watch(groupsProvider);\n  return GroupsState(\n    value: switch (mode) {\n      Mode.direct => [],\n      Mode.global => groups.toList(),\n      Mode.rule =>\n        groups\n            .where((item) => item.hidden != true)\n            .where((element) => element.name != GroupName.GLOBAL.name)\n            .toList(),\n    },\n  );\n}\n\n@riverpod\nNavigationItemsState navigationItemsState(Ref ref) {\n  final openLogs = ref.watch(appSettingProvider).openLogs;\n  return NavigationItemsState(\n    value: navigation.getItems(openLogs: openLogs, hasProxies: true),\n  );\n}\n\n@riverpod\nNavigationItemsState currentNavigationItemsState(Ref ref) {\n  final viewWidth = ref.watch(viewWidthProvider);\n  final navigationItemsState = ref.watch(navigationItemsStateProvider);\n  final navigationItemMode = switch (viewWidth <= maxMobileWidth) {\n    true => NavigationItemMode.mobile,\n    false => NavigationItemMode.desktop,\n  };\n  return NavigationItemsState(\n    value: navigationItemsState.value\n        .where((element) => element.modes.contains(navigationItemMode))\n        .toList(),\n  );\n}\n\n@riverpod\nCoreState coreState(Ref ref) {\n  final vpnProps = ref.watch(vpnSettingProvider);\n  final bypassPrivateRoute = ref.watch(\n    networkSettingProvider.select((state) => state.bypassPrivateRoute),\n  );\n  final currentProfile = ref.watch(currentProfileProvider);\n  final onlyStatisticsProxy = ref.watch(appSettingProvider).onlyStatisticsProxy;\n  return CoreState(\n    vpnProps: vpnProps.copyWith(bypassPrivateRoute: bypassPrivateRoute),\n    onlyStatisticsProxy: onlyStatisticsProxy,\n    currentProfileName: currentProfile?.label ?? currentProfile?.id ?? '',\n  );\n}\n\n@riverpod\nUpdateParams updateParams(Ref ref) {\n  final bypassPrivateRoute = ref.watch(\n    networkSettingProvider.select((state) => state.bypassPrivateRoute),\n  );\n  return ref.watch(\n    patchClashConfigProvider.select(\n      (state) => UpdateParams(\n        tun: state.tun.getRealTun(\n          bypassPrivateRoute,\n          fakeIpRange: state.dns.fakeIpRange,\n          fakeIpRangeV6: state.dns.fakeIpRangeV6,\n        ),\n        allowLan: state.allowLan,\n        findProcessMode: state.findProcessMode,\n        mode: state.mode,\n        logLevel: state.logLevel,\n        ipv6: state.ipv6,\n        tcpConcurrent: state.tcpConcurrent,\n        externalController: state.externalController,\n        unifiedDelay: state.unifiedDelay,\n        mixedPort: state.mixedPort,\n      ),\n    ),\n  );\n}\n\n@riverpod\nProxyState proxyState(Ref ref) {\n  final isStart = ref.watch(runTimeProvider.select((state) => state != null));\n  final vm2 = ref.watch(\n    networkSettingProvider.select(\n      (state) => VM2(a: state.systemProxy, b: state.bypassDomain),\n    ),\n  );\n  final mixedPort = ref.watch(\n    patchClashConfigProvider.select((state) => state.mixedPort),\n  );\n  return ProxyState(\n    isStart: isStart,\n    systemProxy: vm2.a,\n    bassDomain: vm2.b,\n    port: mixedPort,\n  );\n}\n\nfinal wakelockStateProvider = StateProvider<bool>((ref) => false);\n\n@riverpod\nTrayState trayState(Ref ref) {\n  final isStart = ref.watch(runTimeProvider.select((state) => state != null));\n  final networkProps = ref.watch(networkSettingProvider);\n  final clashConfig = ref.watch(patchClashConfigProvider);\n  final appSetting = ref.watch(appSettingProvider);\n  final groups = ref.watch(currentGroupsStateProvider).value;\n  final brightness = ref.watch(systemBrightnessProvider);\n\n  final selectedMap = ref.watch(selectedMapProvider);\n\n  final wakelockEnabled = ref.watch(wakelockStateProvider);\n\n  return TrayState(\n    mode: clashConfig.mode,\n    port: clashConfig.mixedPort,\n    autoLaunch: appSetting.autoLaunch,\n    systemProxy: networkProps.systemProxy,\n    tunEnable: clashConfig.tun.enable,\n    isStart: isStart,\n    locale: appSetting.locale,\n    brightness: brightness,\n    groups: groups,\n    selectedMap: selectedMap,\n    wakelockEnabled: wakelockEnabled,\n  );\n}\n\n@riverpod\nVpnState vpnState(Ref ref) {\n  final vpnProps = ref.watch(vpnSettingProvider);\n  final stack = ref.watch(\n    patchClashConfigProvider.select((state) => state.tun.stack),\n  );\n\n  return VpnState(stack: stack, vpnProps: vpnProps);\n}\n\n@riverpod\nNavigationState navigationState(Ref ref) {\n  final pageLabel = ref.watch(currentPageLabelProvider);\n  final navigationItems = ref.watch(currentNavigationItemsStateProvider).value;\n  final viewMode = ref.watch(viewModeProvider);\n  final locale = ref.watch(appSettingProvider).locale;\n  final index = navigationItems.lastIndexWhere(\n    (element) => element.label == pageLabel,\n  );\n  final currentIndex = index == -1 ? 0 : index;\n  return NavigationState(\n    pageLabel: pageLabel,\n    navigationItems: navigationItems,\n    viewMode: viewMode,\n    locale: locale,\n    currentIndex: currentIndex,\n  );\n}\n\n@riverpod\nDashboardState dashboardState(Ref ref) {\n  final dashboardWidgets = ref.watch(\n    appSettingProvider.select((state) => state.dashboardWidgets),\n  );\n  final viewWidth = ref.watch(viewWidthProvider);\n  final widgets = dashboardWidgets.contains(DashboardWidget.startButton)\n      ? dashboardWidgets\n      : [...dashboardWidgets, DashboardWidget.startButton];\n  return DashboardState(dashboardWidgets: widgets, viewWidth: viewWidth);\n}\n\n@riverpod\nProxiesActionsState proxiesActionsState(Ref ref) {\n  final pageLabel = ref.watch(currentPageLabelProvider);\n  final hasProviders = ref.watch(\n    providersProvider.select((state) => state.isNotEmpty),\n  );\n  final type = ref.watch(\n    proxiesStyleSettingProvider.select((state) => state.type),\n  );\n  return ProxiesActionsState(\n    pageLabel: pageLabel,\n    hasProviders: hasProviders,\n    type: type,\n  );\n}\n\n@riverpod\nStartButtonSelectorState startButtonSelectorState(Ref ref) {\n  final isInit = ref.watch(initProvider);\n  final hasProfile = ref.watch(\n    profilesProvider.select((state) => state.isNotEmpty),\n  );\n  return StartButtonSelectorState(isInit: isInit, hasProfile: hasProfile);\n}\n\n@riverpod\nProfilesSelectorState profilesSelectorState(Ref ref) {\n  final currentProfileId = ref.watch(currentProfileIdProvider);\n  final profiles = ref.watch(profilesProvider);\n  final columns = ref.watch(\n    viewWidthProvider.select((state) => utils.getProfilesColumns(state)),\n  );\n  return ProfilesSelectorState(\n    profiles: profiles,\n    currentProfileId: currentProfileId,\n    columns: columns,\n  );\n}\n\n@riverpod\nGroupsState filterGroupsState(Ref ref, String query) {\n  final currentGroups = ref.watch(currentGroupsStateProvider);\n  if (query.isEmpty) {\n    return currentGroups;\n  }\n  final lowQuery = query.toLowerCase();\n  final groups = currentGroups.value\n      .map((group) {\n        return group.copyWith(\n          all: group.all\n              .where((proxy) => proxy.name.toLowerCase().contains(lowQuery))\n              .toList(),\n        );\n      })\n      .where((group) => group.all.isNotEmpty)\n      .toList();\n  return GroupsState(value: groups);\n}\n\n@Riverpod(dependencies: [Query])\nProxiesListState proxiesListState(Ref ref) {\n  final query = ref.watch(queryProvider);\n  final currentGroups = ref.watch(filterGroupsStateProvider(query));\n  final currentUnfoldSet = ref.watch(unfoldSetProvider);\n  final vm2 = ref.watch(\n    proxiesStyleSettingProvider.select(\n      (state) => VM2(a: state.sortType, b: state.cardType),\n    ),\n  );\n\n  final sortNum = ref.watch(sortNumProvider);\n  final columns = ref.watch(getProxiesColumnsProvider);\n  return ProxiesListState(\n    groups: currentGroups.value,\n    currentUnfoldSet: currentUnfoldSet,\n    proxiesSortType: vm2.a,\n    proxyCardType: vm2.b,\n    sortNum: sortNum,\n    columns: columns,\n  );\n}\n\n@Riverpod(dependencies: [Query])\nProxiesTabState proxiesTabState(Ref ref) {\n  final query = ref.watch(queryProvider);\n  final currentGroups = ref.watch(filterGroupsStateProvider(query));\n  final currentGroupName = ref.watch(\n    currentProfileProvider.select((state) => state?.currentGroupName),\n  );\n  final vm2 = ref.watch(\n    proxiesStyleSettingProvider.select(\n      (state) => VM2(a: state.sortType, b: state.cardType),\n    ),\n  );\n  final sortNum = ref.watch(sortNumProvider);\n  final columns = ref.watch(getProxiesColumnsProvider);\n  return ProxiesTabState(\n    groups: currentGroups.value,\n    currentGroupName: currentGroupName,\n    proxiesSortType: vm2.a,\n    proxyCardType: vm2.b,\n    sortNum: sortNum,\n    columns: columns,\n  );\n}\n\n@Riverpod(dependencies: [proxiesTabState])\nVM2<List<String>, String?> proxiesTabControllerState(Ref ref) {\n  return ref.watch(\n    proxiesTabStateProvider.select(\n      (state) => VM2(\n        a: state.groups.map((group) => group.name).toList(),\n        b: state.currentGroupName,\n      ),\n    ),\n  );\n}\n\n@riverpod\nProxyGroupSelectorState proxyGroupSelectorState(\n  Ref ref,\n  String groupName,\n  String query,\n) {\n  final proxiesStyle = ref.watch(proxiesStyleSettingProvider);\n  final group = ref.watch(\n    currentGroupsStateProvider.select(\n      (state) => state.value.getGroup(groupName),\n    ),\n  );\n  final sortNum = ref.watch(sortNumProvider);\n  final columns = ref.watch(getProxiesColumnsProvider);\n  final lowQuery = query.toLowerCase();\n  final proxies =\n      group?.all.where((item) {\n        return item.name.toLowerCase().contains(lowQuery);\n      }).toList() ??\n      [];\n  return ProxyGroupSelectorState(\n    testUrl: group?.testUrl,\n    proxiesSortType: proxiesStyle.sortType,\n    proxyCardType: proxiesStyle.cardType,\n    sortNum: sortNum,\n    groupType: group?.type ?? GroupType.Selector,\n    proxies: proxies,\n    columns: columns,\n  );\n}\n\n@riverpod\nPackageListSelectorState packageListSelectorState(Ref ref) {\n  final packages = ref.watch(packagesProvider);\n  final accessControl = ref.watch(\n    vpnSettingProvider.select((state) => state.accessControl),\n  );\n  return PackageListSelectorState(\n    packages: packages,\n    accessControl: accessControl,\n  );\n}\n\n@riverpod\nMoreToolsSelectorState moreToolsSelectorState(Ref ref) {\n  final viewMode = ref.watch(viewModeProvider);\n  final navigationItems = ref.watch(\n    navigationItemsStateProvider.select((state) {\n      return state.value.where((element) {\n        final isMore = element.modes.contains(NavigationItemMode.more);\n        final isDesktop = element.modes.contains(NavigationItemMode.desktop);\n        if (isMore && !isDesktop) return true;\n        if (viewMode != ViewMode.mobile || !isMore) {\n          return false;\n        }\n        return true;\n      }).toList();\n    }),\n  );\n\n  return MoreToolsSelectorState(navigationItems: navigationItems);\n}\n\n@riverpod\nbool isCurrentPage(\n  Ref ref,\n  PageLabel pageLabel, {\n  bool Function(PageLabel pageLabel, ViewMode viewMode)? handler,\n}) {\n  final currentPageLabel = ref.watch(currentPageLabelProvider);\n  if (pageLabel == currentPageLabel) {\n    return true;\n  }\n  if (handler != null) {\n    final viewMode = ref.watch(viewModeProvider);\n    return handler(currentPageLabel, viewMode);\n  }\n  return false;\n}\n\n@riverpod\nString getRealTestUrl(Ref ref, [String? testUrl]) {\n  final overrideTestUrl = ref.watch(overrideTestUrlProvider);\n  final currentTestUrl = ref.watch(appSettingProvider).testUrl;\n\n  // If override is enabled or testUrl is null or empty, use client setting\n  if (overrideTestUrl || testUrl == null || testUrl.isEmpty) {\n    return currentTestUrl;\n  }\n\n  // Otherwise use the provided testUrl from config file\n  return testUrl;\n}\n\n@riverpod\nint? getDelay(Ref ref, {required String proxyName, String? testUrl}) {\n  final proxyCardState = ref.watch(getProxyCardStateProvider(proxyName));\n  final currentTestUrl = ref.watch(\n    getRealTestUrlProvider(proxyCardState.testUrl.getSafeValue(testUrl ?? '')),\n  );\n  final delay = ref.watch(\n    delayDataSourceProvider.select((state) {\n      final delayMap = state[currentTestUrl];\n      return delayMap?[proxyCardState.proxyName];\n    }),\n  );\n  return delay;\n}\n\n@riverpod\nSelectedMap selectedMap(Ref ref) {\n  final selectedMap = ref.watch(\n    currentProfileProvider.select((state) => state?.selectedMap ?? {}),\n  );\n  return selectedMap;\n}\n\n@riverpod\nSet<String> unfoldSet(Ref ref) {\n  final unfoldSet = ref.watch(\n    currentProfileProvider.select((state) => state?.unfoldSet ?? {}),\n  );\n  return unfoldSet;\n}\n\n@riverpod\nHotKeyAction getHotKeyAction(Ref ref, HotAction hotAction) {\n  return ref.watch(\n    hotKeyActionsProvider.select((state) {\n      final index = state.indexWhere((item) => item.action == hotAction);\n      return index != -1 ? state[index] : HotKeyAction(action: hotAction);\n    }),\n  );\n}\n\n@riverpod\nProfile? currentProfile(Ref ref) {\n  final profileId = ref.watch(currentProfileIdProvider);\n  return ref.watch(\n    profilesProvider.select((state) => state.getProfile(profileId)),\n  );\n}\n\n@riverpod\nint getProxiesColumns(Ref ref) {\n  final viewWidth = ref.watch(viewWidthProvider);\n  final proxiesLayout = ref.watch(\n    proxiesStyleSettingProvider.select((state) => state.layout),\n  );\n  return utils.getProxiesColumns(viewWidth, proxiesLayout);\n}\n\nProxyCardState _getProxyCardState(\n  List<Group> groups,\n  SelectedMap selectedMap,\n  ProxyCardState proxyDelayState,\n) {\n  if (proxyDelayState.proxyName.isEmpty) return proxyDelayState;\n  final index = groups.indexWhere(\n    (element) => element.name == proxyDelayState.proxyName,\n  );\n  if (index == -1) return proxyDelayState;\n  final group = groups[index];\n  final currentSelectedName = group.getCurrentSelectedName(\n    selectedMap[proxyDelayState.proxyName] ?? '',\n  );\n  if (currentSelectedName.isEmpty) {\n    return proxyDelayState;\n  }\n  return _getProxyCardState(\n    groups,\n    selectedMap,\n    proxyDelayState.copyWith(\n      proxyName: currentSelectedName,\n      testUrl: group.testUrl,\n    ),\n  );\n}\n\n@riverpod\nProxyCardState getProxyCardState(Ref ref, String proxyName) {\n  final groups = ref.watch(groupsProvider);\n  final selectedMap = ref.watch(selectedMapProvider);\n  return _getProxyCardState(\n    groups,\n    selectedMap,\n    ProxyCardState(proxyName: proxyName),\n  );\n}\n\n@riverpod\nString? getProxyName(Ref ref, String groupName) {\n  final proxyName = ref.watch(\n    selectedMapProvider.select((state) => state[groupName]),\n  );\n  return proxyName;\n}\n\n@riverpod\nString? getSelectedProxyName(Ref ref, String groupName) {\n  final proxyName = ref.watch(getProxyNameProvider(groupName));\n  final group = ref.watch(\n    groupsProvider.select((state) => state.getGroup(groupName)),\n  );\n  return group?.getCurrentSelectedName(proxyName ?? '');\n}\n\n@riverpod\nString getProxyDesc(Ref ref, Proxy proxy) {\n  final groupTypeNamesList = GroupType.values.map((e) => e.name).toList();\n  if (!groupTypeNamesList.contains(proxy.type)) {\n    return proxy.type;\n  } else {\n    final groups = ref.watch(groupsProvider);\n    final index = groups.indexWhere((element) => element.name == proxy.name);\n    if (index == -1) return proxy.type;\n    final state = ref.watch(getProxyCardStateProvider(proxy.name));\n    return \"${proxy.type}(${state.proxyName.isNotEmpty ? state.proxyName : '*'})\";\n  }\n}\n\n@riverpod\nclass ProfileOverrideState extends _$ProfileOverrideState {\n  @override\n  ProfileOverrideStateModel build() {\n    return ProfileOverrideStateModel(selectedRules: {});\n  }\n\n  void updateState(\n    ProfileOverrideStateModel? Function(ProfileOverrideStateModel state)\n    builder,\n  ) {\n    final value = builder(state);\n    if (value == null) {\n      return;\n    }\n    state = value;\n  }\n}\n\n@riverpod\nOverrideData? getProfileOverrideData(Ref ref, String profileId) {\n  return ref.watch(\n    profilesProvider.select(\n      (state) => state.getProfile(profileId)?.overrideData,\n    ),\n  );\n}\n\n@riverpod\nVM2? layoutChange(Ref ref) {\n  final viewWidth = ref.watch(viewWidthProvider);\n  final textScale = ref.watch(\n    themeSettingProvider.select((state) => state.textScale),\n  );\n  return VM2(a: viewWidth, b: textScale);\n}\n\n@riverpod\nVM2<int, bool> checkIp(Ref ref) {\n  final checkIpNum = ref.watch(checkIpNumProvider);\n  final containsDetection = ref.watch(\n    dashboardStateProvider.select(\n      (state) =>\n          state.dashboardWidgets.contains(DashboardWidget.networkDetection),\n    ),\n  );\n  return VM2(a: checkIpNum, b: containsDetection);\n}\n\n@riverpod\nColorScheme genColorScheme(\n  Ref ref,\n  Brightness brightness, {\n  Color? color,\n  bool ignoreConfig = false,\n}) {\n  final vm2 = ref.watch(\n    themeSettingProvider.select(\n      (state) => VM2(a: state.primaryColor, b: state.schemeVariant),\n    ),\n  );\n  if (color == null && (ignoreConfig == true || vm2.a == null)) {\n    // if (globalState.corePalette != null) {\n    //   return globalState.corePalette!.toColorScheme(brightness: brightness);\n    // }\n    return ColorScheme.fromSeed(\n      seedColor:\n          globalState.corePalette\n              ?.toColorScheme(brightness: brightness)\n              .primary ??\n          globalState.accentColor,\n      brightness: brightness,\n      dynamicSchemeVariant: vm2.b,\n    );\n  }\n  return ColorScheme.fromSeed(\n    seedColor: color ?? Color(vm2.a!),\n    brightness: brightness,\n    dynamicSchemeVariant: vm2.b,\n  );\n}\n\n@riverpod\nVM3<String?, String?, Dns?> needSetup(Ref ref) {\n  final profileId = ref.watch(currentProfileIdProvider);\n  final content = ref.watch(\n    scriptStateProvider.select((state) => state.currentScript?.content),\n  );\n  final overrideDns = ref.watch(overrideDnsProvider);\n  final dns = overrideDns == true\n      ? ref.watch(patchClashConfigProvider.select((state) => state.dns))\n      : null;\n  return VM3(a: profileId, b: content, c: dns);\n}\n\n@riverpod\nBrightness currentBrightness(Ref ref) {\n  final themeMode = ref.watch(\n    themeSettingProvider.select((state) => state.themeMode),\n  );\n  final systemBrightness = ref.watch(systemBrightnessProvider);\n  return switch (themeMode) {\n    ThemeMode.system => systemBrightness,\n    ThemeMode.light => Brightness.light,\n    ThemeMode.dark => Brightness.dark,\n  };\n}\n\n@riverpod\nVM2<bool, bool> autoSetSystemDnsState(Ref ref) {\n  final isStart = ref.watch(runTimeProvider.select((state) => state != null));\n  final realTunEnable = ref.watch(realTunEnableProvider);\n  final autoSetSystemDns = ref.watch(\n    networkSettingProvider.select((state) => state.autoSetSystemDns),\n  );\n  return VM2(a: isStart ? realTunEnable : false, b: autoSetSystemDns);\n}\n\n@riverpod\nclass Query extends _$Query with AutoDisposeNotifierMixin {\n  @override\n  String build() => '';\n}\n"
  },
  {
    "path": "lib/state.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\nimport 'dart:ffi' show Pointer;\n\nimport 'package:animations/animations.dart';\nimport 'package:dio/dio.dart';\nimport 'package:dynamic_color/dynamic_color.dart';\nimport 'package:bett_box/clash/clash.dart';\nimport 'package:bett_box/common/theme.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/l10n/l10n.dart';\nimport 'package:bett_box/plugins/app.dart';\nimport 'package:bett_box/plugins/service.dart';\nimport 'package:bett_box/providers/state.dart' as providers_state;\n\nimport 'package:bett_box/widgets/dialog.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart' as flutter_riverpod;\nimport 'package:material_color_utilities/palettes/core_palette.dart';\nimport 'package:package_info_plus/package_info_plus.dart';\nimport 'package:synchronized/synchronized.dart';\nimport 'package:url_launcher/url_launcher.dart';\n\nimport 'common/common.dart';\nimport 'controller.dart';\nimport 'models/models.dart';\n\ntypedef UpdateTasks = List<FutureOr Function()>;\n\nclass GlobalState {\n  static GlobalState? _instance;\n  Map<CacheTag, FixedMap<String, double>> computeHeightMapCache = {};\n  bool isService = false;\n  bool isExiting = false;\n  Timer? timer;\n  Timer? groupsUpdateTimer;\n  late Config config;\n  late AppState appState;\n  bool isPre = true;\n  String? coreSHA256;\n  late PackageInfo packageInfo;\n  Function? updateCurrentDelayDebounce;\n  late Measure measure;\n  late CommonTheme theme;\n  late Color accentColor;\n  CorePalette? corePalette;\n  DateTime? startTime;\n  UpdateTasks tasks = [];\n  final navigatorKey = GlobalKey<NavigatorState>();\n  final backgroundMode = ValueNotifier<bool>(false);\n  final animationEnabled = ValueNotifier<bool>(true);\n  AppController? _appController;\n  bool? _isAndroidTV;\n  int _taskLoopToken = 0;\n  bool _isExecutingTasks = false;\n  bool _needsTaskRestart = false;\n  Timer? _backgroundCleanupTimer;\n  final Lock _scriptEvaluateLock = Lock();\n\n  SetupParams? _lastSuccessfulSetupParams;\n\n  bool isInit = false;\n\n  bool get isStart => startTime != null && startTime!.isBeforeNow;\n\n  AppController get appController => _appController!;\n\n  set appController(AppController appController) {\n    _appController = appController;\n    isInit = true;\n  }\n\n  GlobalState._internal();\n\n  factory GlobalState() {\n    _instance ??= GlobalState._internal();\n    return _instance!;\n  }\n\n  Future<void> initApp(int version) async {\n    isExiting = false;\n    coreSHA256 = const String.fromEnvironment('CORE_SHA256');\n    isPre = const String.fromEnvironment('APP_ENV') != 'stable';\n    appState = AppState(\n      brightness: WidgetsBinding.instance.platformDispatcher.platformBrightness,\n      version: version,\n      viewSize: Size.zero,\n      requests: FixedList(maxLength),\n      logs: FixedList(maxLength),\n      traffics: FixedList(30),\n      totalTraffic: Traffic(),\n      systemUiOverlayStyle: const SystemUiOverlayStyle(),\n    );\n    await _initDynamicColor();\n    await init();\n  }\n\n  Future<void> _initDynamicColor() async {\n    try {\n      corePalette = await DynamicColorPlugin.getCorePalette();\n      accentColor =\n          await DynamicColorPlugin.getAccentColor() ??\n          Color(defaultPrimaryColor);\n    } catch (_) {}\n  }\n\n  Future<void> init() async {\n    packageInfo = await PackageInfo.fromPlatform();\n    config =\n        await preferences.getConfig() ?? Config(themeProps: defaultThemeProps);\n    await globalState.migrateOldData(config);\n    final locale =\n        utils.getLocaleForString(config.appSetting.locale) ??\n        utils.getSystemLocale();\n    await AppLocalizations.load(locale);\n    if (system.isAndroid) {\n      _isAndroidTV = await app.isAndroidTV();\n    }\n  }\n\n  bool get isAndroidTV => _isAndroidTV ?? false;\n\n  String get ua => config.patchClashConfig.globalUa ?? packageInfo.ua;\n\n  Future<void> startUpdateTasks([UpdateTasks? tasks]) async {\n    if (tasks != null) {\n      this.tasks = tasks;\n    }\n    final token = ++_taskLoopToken;\n    timer?.cancel();\n    timer = null;\n    if (_isExecutingTasks) {\n      _needsTaskRestart = true;\n      return;\n    }\n    await _runUpdateLoop(token);\n  }\n\n  Future<void> _runUpdateLoop(int token) async {\n    if (token != _taskLoopToken) return;\n    _isExecutingTasks = true;\n    try {\n      await executorUpdateTask();\n    } finally {\n      _isExecutingTasks = false;\n    }\n    if (_needsTaskRestart) {\n      _needsTaskRestart = false;\n      await _runUpdateLoop(_taskLoopToken);\n      return;\n    }\n    if (token != _taskLoopToken) return;\n    timer = Timer(const Duration(seconds: 1), () {\n      unawaited(_runUpdateLoop(token));\n    });\n  }\n\n  Future<void> executorUpdateTask() async {\n    for (final task in tasks) {\n      try {\n        await task();\n      } catch (e) {\n        commonPrint.log('Background task failed: $e');\n      }\n    }\n    timer = null;\n  }\n\n  void stopUpdateTasks() {\n    _taskLoopToken++;\n    _needsTaskRestart = false;\n    timer?.cancel();\n    timer = null;\n  }\n\n  Future<void> handleBackground() async {\n    if (system.isDesktop) {\n      final isMinimized = await window?.isMinimized ?? false;\n      final isVisible = await window?.isVisible ?? true;\n      if (!isMinimized && isVisible) {\n        return;\n      }\n      animationEnabled.value = false;\n      if (!backgroundMode.value) {\n        backgroundMode.value = true;\n        _scheduleBackgroundCleanup();\n      }\n      render?.pause();\n      stopUpdateTasks();\n      dashboardRefreshManager.stop();\n      return;\n    }\n    if (!backgroundMode.value) {\n      backgroundMode.value = true;\n      _scheduleBackgroundCleanup();\n    }\n    render?.pause();\n    stopUpdateTasks();\n    dashboardRefreshManager.stop();\n  }\n\n  void handleForeground() {\n    if (system.isDesktop) {\n      animationEnabled.value = true;\n    }\n    if (!backgroundMode.value) {\n      return;\n    }\n    backgroundMode.value = false;\n    _backgroundCleanupTimer?.cancel();\n    _backgroundCleanupTimer = null;\n    _syncVpnState();\n  }\n\n  Future<void> _syncVpnState() async {\n    if (!system.isAndroid) return;\n    try {\n      final actuallyRunning = await service?.getStatus() ?? false;\n      final flutterState = appState.runTime != null;\n\n      if (actuallyRunning && !flutterState) {\n        await updateStartTime();\n        if (startTime != null) {\n          appState = appState.copyWith(runTime: 0);\n          await startUpdateTasks([appController.updateTraffic]);\n        }\n      } else if (!actuallyRunning && flutterState) {\n        appState = appState.copyWith(runTime: null);\n        startTime = null;\n      }\n    } catch (e) {\n      commonPrint.log('Sync VPN state error: $e');\n    }\n  }\n\n  Future<void> resumeForegroundUpdates() async {\n    dashboardRefreshManager.start();\n    if (!isStart) {\n      return;\n    }\n    await appController.updateRunTime();\n    await appController.updateTraffic();\n    await startUpdateTasks([appController.updateTraffic]);\n  }\n\n  void _scheduleBackgroundCleanup() {\n    _backgroundCleanupTimer?.cancel();\n    _backgroundCleanupTimer = Timer(const Duration(minutes: 3), () {\n      _backgroundCleanupTimer = null;\n      if (!backgroundMode.value) {\n        return;\n      }\n      cleanupBackgroundResources();\n    });\n  }\n\n  void cleanupBackgroundResources() async {\n    final imageCache = PaintingBinding.instance.imageCache;\n    imageCache.clearLiveImages();\n    await Future.delayed(const Duration(milliseconds: 500));\n    WidgetsBinding.instance.handleMemoryPressure();\n    await Future.delayed(const Duration(milliseconds: 500));\n    await clashCore.requestGc();\n  }\n\n  Future<void> handleStart([UpdateTasks? tasks]) async {\n    startTime ??= DateTime.now();\n    if (system.isAndroid && isService) {\n      await clashLibHandler?.startListener();\n    } else {\n      await clashCore.startListener();\n    }\n    await service?.startVpn();\n    final prefs = await preferences.sharedPreferencesCompleter.future;\n    await prefs?.setBool('is_vpn_running', true);\n    if (system.isDesktop) {\n      final tunEnabled = config.patchClashConfig.tun.enable;\n      await prefs?.setBool('is_tun_running', tunEnabled);\n    }\n    if (system.isAndroid) {\n      final conflictFreeQuickResponse =\n          config.vpnProps.quickResponse && !config.vpnProps.smartAutoStop;\n      await service?.setQuickResponse(conflictFreeQuickResponse);\n    }\n    await startUpdateTasks(tasks);\n  }\n\n  Future updateStartTime() async {\n    startTime = await clashLib?.getRunTime();\n  }\n\n  void updateWakelockState(bool enabled) {\n    if (_appController != null) {\n      final container = _appController!.context;\n      if (container.mounted) {\n        final providerContainer = flutter_riverpod.ProviderScope.containerOf(\n          container,\n          listen: false,\n        );\n        providerContainer\n                .read(providers_state.wakelockStateProvider.notifier)\n                .state =\n            enabled;\n      }\n    }\n  }\n\n  Future handleStop() async {\n    startTime = null;\n    if (system.isAndroid && isService) {\n      await clashLibHandler?.stopListener();\n    } else {\n      await clashCore.stopListener();\n    }\n    await service?.stopVpn();\n    final prefs = await preferences.sharedPreferencesCompleter.future;\n    await prefs?.setBool('is_vpn_running', false);\n    if (system.isDesktop) {\n      await prefs?.setBool('is_tun_running', false);\n    }\n    stopUpdateTasks();\n  }\n\n  Future<bool?> showMessage({\n    String? title,\n    required InlineSpan message,\n    String? confirmText,\n    bool cancelable = true,\n  }) async {\n    return await showCommonDialog<bool>(\n      child: Builder(\n        builder: (context) {\n          return CommonDialog(\n            title: title ?? appLocalizations.tip,\n            actions: [\n              if (cancelable)\n                TextButton(\n                  onPressed: () {\n                    Navigator.of(context).pop(false);\n                  },\n                  child: Text(appLocalizations.cancel),\n                ),\n              TextButton(\n                onPressed: () {\n                  Navigator.of(context).pop(true);\n                },\n                child: Text(confirmText ?? appLocalizations.confirm),\n              ),\n            ],\n            child: Container(\n              width: 300,\n              constraints: const BoxConstraints(maxHeight: 200),\n              child: SingleChildScrollView(\n                child: SelectableText.rich(\n                  TextSpan(\n                    style: Theme.of(context).textTheme.labelLarge,\n                    children: [message],\n                  ),\n                  style: const TextStyle(overflow: TextOverflow.visible),\n                ),\n              ),\n            ),\n          );\n        },\n      ),\n    );\n  }\n\n  Future<T?> showCommonDialog<T>({\n    required Widget child,\n    bool dismissible = true,\n  }) async {\n    final context = navigatorKey.currentState!.context;\n    final isDark = Theme.of(context).brightness == Brightness.dark;\n    return await showModal<T>(\n      context: context,\n      configuration: FadeScaleTransitionConfiguration(\n        barrierColor: isDark\n            ? const Color(0xCC000000)\n            : const Color(0x99000000),\n        barrierDismissible: dismissible,\n      ),\n      builder: (_) => child,\n    );\n  }\n\n  void showNotifier(\n    String text, {\n    VoidCallback? onAction,\n    String? actionLabel,\n    bool showCountdown = false,\n  }) {\n    if (text.isEmpty) return;\n    navigatorKey.currentContext?.showNotifier(\n      text,\n      onAction: onAction,\n      actionLabel: actionLabel,\n      showCountdown: showCountdown,\n    );\n  }\n\n  Future<void> openUrl(String url, {bool needConfirm = false}) async {\n    if (needConfirm) {\n      final res = await showMessage(\n        message: TextSpan(text: url),\n        title: appLocalizations.externalLink,\n        confirmText: appLocalizations.go,\n      );\n      if (res != true) {\n        return;\n      }\n    }\n    launchUrl(Uri.parse(url), mode: LaunchMode.externalApplication);\n  }\n\n  Future<void> migrateOldData(Config config) async {\n    final clashConfig = await preferences.getClashConfig();\n    if (clashConfig != null) {\n      config = config.copyWith(patchClashConfig: clashConfig);\n      preferences.clearClashConfig();\n      preferences.saveConfig(config);\n    }\n\n    if (config.appSetting.locale == null) {\n      final systemLocale = utils.getSystemLocale();\n      config = config.copyWith(\n        appSetting: config.appSetting.copyWith(locale: systemLocale.toString()),\n      );\n      preferences.saveConfig(config);\n      this.config = config;\n    }\n  }\n\n  CoreState getCoreState() {\n    final currentProfile = config.currentProfile;\n    return CoreState(\n      vpnProps: config.vpnProps,\n      onlyStatisticsProxy: config.appSetting.onlyStatisticsProxy,\n      currentProfileName: currentProfile?.label ?? currentProfile?.id ?? '',\n      bypassDomain: config.networkProps.bypassDomain,\n    );\n  }\n\n  Future<SetupParams> getSetupParams({required ClashConfig pathConfig}) async {\n    final clashConfig = await patchRawConfig(patchConfig: pathConfig);\n    final params = SetupParams(\n      config: clashConfig,\n      selectedMap: config.currentProfile?.selectedMap ?? {},\n      testUrl: config.appSetting.testUrl,\n      overrideTestUrl: config.overrideTestUrl,\n    );\n    return params;\n  }\n\n  void backupSuccessfulConfig(SetupParams params) {\n    if (_lastSuccessfulSetupParams == params) {\n      return;\n    }\n    _lastSuccessfulSetupParams = params;\n    commonPrint.log('Current config protected');\n  }\n\n  SetupParams? getLastSuccessfulConfig() {\n    return _lastSuccessfulSetupParams;\n  }\n\n  Future<Map<String, dynamic>> patchRawConfig({\n    required ClashConfig patchConfig,\n  }) async {\n    final profile = config.currentProfile;\n    if (profile == null) {\n      return {};\n    }\n    final profileId = profile.id;\n    final configMap = await getProfileConfig(profileId);\n    final rawConfig = await handleEvaluate(configMap);\n    final originalProxyGroups = rawConfig['proxy-groups'];\n\n    final realPatchConfig = patchConfig.copyWith(\n      tun: patchConfig.tun.getRealTun(\n        config.networkProps.bypassPrivateRoute,\n        fakeIpRange: patchConfig.dns.fakeIpRange,\n        fakeIpRangeV6: patchConfig.dns.fakeIpRangeV6,\n      ),\n    );\n    rawConfig['external-controller'] = realPatchConfig.externalController.value;\n    if (realPatchConfig.externalController == ExternalControllerStatus.open) {\n      final secret = realPatchConfig.secret;\n      if (secret != null && secret.isNotEmpty) {\n        rawConfig['secret'] = secret;\n      }\n    }\n    rawConfig['external-ui'] = await appPath.uiPath;\n    rawConfig['interface-name'] = '';\n    rawConfig['tcp-concurrent'] = realPatchConfig.tcpConcurrent;\n    rawConfig['unified-delay'] = realPatchConfig.unifiedDelay;\n    rawConfig['ipv6'] = realPatchConfig.ipv6;\n    rawConfig['log-level'] = realPatchConfig.logLevel.name;\n    rawConfig['port'] = 0;\n    rawConfig['socks-port'] = 0;\n    rawConfig['keep-alive-interval'] = realPatchConfig.keepAliveInterval;\n    rawConfig['mixed-port'] = realPatchConfig.mixedPort;\n    rawConfig['port'] = realPatchConfig.port;\n    rawConfig['socks-port'] = realPatchConfig.socksPort;\n    rawConfig['redir-port'] = realPatchConfig.redirPort;\n    rawConfig['tproxy-port'] = realPatchConfig.tproxyPort;\n    rawConfig['find-process-mode'] = realPatchConfig.findProcessMode.name;\n    rawConfig['allow-lan'] = realPatchConfig.allowLan;\n    rawConfig['mode'] = realPatchConfig.mode.name;\n    if (rawConfig['tun'] == null) {\n      rawConfig['tun'] = {};\n    }\n    rawConfig['tun']['enable'] = realPatchConfig.tun.enable;\n    rawConfig['tun']['device'] = realPatchConfig.tun.device;\n    rawConfig['tun']['dns-hijack'] = realPatchConfig.tun.dnsHijack;\n    rawConfig['tun']['stack'] = realPatchConfig.tun.stack.name;\n    rawConfig['tun']['route-address'] = realPatchConfig.tun.routeAddress;\n    rawConfig['tun']['route-exclude-address'] = realPatchConfig.tun.routeExcludeAddress;\n    rawConfig['tun']['auto-route'] = true;\n    rawConfig['tun']['auto-detect-interface'] = true;\n    rawConfig['tun']['strict-route'] = realPatchConfig.tun.strictRoute;\n    rawConfig['tun']['endpoint-independent-nat'] =\n        realPatchConfig.tun.endpointIndependentNat;\n    rawConfig['tun']['disable-icmp-forwarding'] =\n        realPatchConfig.tun.disableIcmpForwarding;\n    rawConfig['tun']['mtu'] = realPatchConfig.tun.mtu;\n    rawConfig['geodata-loader'] = realPatchConfig.geodataLoader.name;\n    rawConfig['geodata-mode'] = false;\n    if (rawConfig['sniffer']?['sniff'] != null) {\n      for (final value in (rawConfig['sniffer']?['sniff'] as Map).values) {\n        if (value['ports'] != null && value['ports'] is List) {\n          value['ports'] =\n              value['ports']?.map((item) => item.toString()).toList() ?? [];\n        }\n      }\n    }\n    if (rawConfig['profile'] == null) {\n      rawConfig['profile'] = {};\n    }\n    if (rawConfig['proxy-providers'] != null) {\n      final proxyProviders = rawConfig['proxy-providers'] as Map;\n      for (final key in proxyProviders.keys) {\n        final proxyProvider = proxyProviders[key];\n        if (proxyProvider['type'] != 'http') {\n          continue;\n        }\n        if (proxyProvider['url'] != null) {\n          proxyProvider['path'] = await appPath.getProvidersFilePath(\n            profile.id,\n            'proxies',\n            proxyProvider['url'],\n          );\n        }\n      }\n    }\n\n    if (rawConfig['rule-providers'] != null) {\n      final ruleProviders = rawConfig['rule-providers'] as Map;\n      for (final key in ruleProviders.keys) {\n        final ruleProvider = ruleProviders[key];\n        if (ruleProvider['type'] != 'http') {\n          continue;\n        }\n        if (ruleProvider['url'] != null) {\n          ruleProvider['path'] = await appPath.getProvidersFilePath(\n            profile.id,\n            'rules',\n            ruleProvider['url'],\n          );\n        }\n      }\n    }\n\n    rawConfig['profile']['store-selected'] = true;\n    rawConfig['geox-url'] = realPatchConfig.geoXUrl.toJson();\n    rawConfig['global-ua'] = realPatchConfig.globalUa;\n    if (rawConfig['hosts'] == null) {\n      rawConfig['hosts'] = {};\n    }\n    for (final host in realPatchConfig.hosts.entries) {\n      rawConfig['hosts'][host.key] = host.value.splitByMultipleSeparators;\n    }\n\n    rawConfig['hosts']['dns.msftncsi.com'] = [\n      '131.107.255.255',\n      'fd3e:4f5a:5b81::1',\n    ];\n\n    if (rawConfig['dns'] == null) {\n      rawConfig['dns'] = {};\n    }\n    final isEnableDns = rawConfig['dns']['enable'] == true;\n    final overrideDns = globalState.config.overrideDns;\n    if (overrideDns || !isEnableDns) {\n      final dns = switch (!isEnableDns) {\n        true => realPatchConfig.dns.copyWith(\n          nameserver: [...realPatchConfig.dns.nameserver, 'system://'],\n        ),\n        false => realPatchConfig.dns,\n      };\n      rawConfig['dns'] = dns.toJson();\n      rawConfig['dns']['nameserver-policy'] = {};\n      for (final entry in dns.nameserverPolicy.entries) {\n        rawConfig['dns']['nameserver-policy'][entry.key] =\n            entry.value.splitByMultipleSeparators;\n      }\n    }\n\n    if (system.isAndroid && rawConfig['dns']['listen'] != null) {\n      final listen = rawConfig['dns']['listen'] as String;\n      if (listen.endsWith(':53')) {\n        rawConfig['dns']['listen'] = listen.replaceAll(':53', ':1053');\n      }\n    }\n\n    if (rawConfig['ntp'] == null) {\n      rawConfig['ntp'] = {};\n    }\n    final overrideNtp = globalState.config.overrideNtp;\n    if (overrideNtp) {\n      final ntp = realPatchConfig.ntp;\n      rawConfig['ntp'] = ntp.toJson();\n    }\n    if (rawConfig['sniffer'] == null) {\n      rawConfig['sniffer'] = {};\n    }\n    final overrideSniffer = globalState.config.overrideSniffer;\n    if (overrideSniffer) {\n      final sniffer = realPatchConfig.sniffer;\n      rawConfig['sniffer'] = sniffer.toJson();\n    }\n    final guiTunnels = realPatchConfig.tunnels;\n    if (guiTunnels.isNotEmpty) {\n      final existingTunnels = rawConfig['tunnels'] as List? ?? [];\n      final allTunnels = [\n        ...existingTunnels,\n        ...guiTunnels.map((t) => t.toClashJson()),\n      ];\n      rawConfig['tunnels'] = allTunnels;\n    }\n    if (rawConfig['experimental'] == null) {\n      rawConfig['experimental'] = {};\n    }\n    final overrideExperimental = globalState.config.overrideExperimental;\n    if (overrideExperimental) {\n      final experimental = realPatchConfig.experimental;\n      rawConfig['experimental'] = experimental.toJson();\n    }\n\n    final nodeExcludeFilter = globalState.config.nodeExcludeFilter;\n    final healthCheckTimeout = globalState.config.healthCheckTimeout;\n    if ((nodeExcludeFilter.isNotEmpty || healthCheckTimeout != 5000) &&\n        rawConfig['proxy-groups'] is List) {\n      RegExp? filterRegex;\n      if (nodeExcludeFilter.isNotEmpty) {\n        try {\n          filterRegex = RegExp(nodeExcludeFilter);\n        } catch (_) {}\n      }\n\n      final proxyGroups = rawConfig['proxy-groups'] as List;\n\n      final Set<String> protectedNames = {\n        'DIRECT',\n        'REJECT',\n        'REJECT-DROP',\n        'COMPATIBLE',\n        'PASS',\n      };\n      for (final g in proxyGroups) {\n        if (g is Map && g['name'] is String) {\n          protectedNames.add(g['name'] as String);\n        }\n      }\n\n      for (final group in proxyGroups) {\n        if (group is! Map) continue;\n\n        if (filterRegex != null && group['use'] != null) {\n          final existing = group['exclude-filter'];\n          if (existing is String && existing.isNotEmpty) {\n            group['exclude-filter'] = '$existing|$nodeExcludeFilter';\n          } else {\n            group['exclude-filter'] = nodeExcludeFilter;\n          }\n        }\n\n        if (filterRegex != null && group['proxies'] is List) {\n          final proxiesList = group['proxies'] as List;\n          final filtered = proxiesList.where((item) {\n            if (item is! String || protectedNames.contains(item)) return true;\n            return !filterRegex!.hasMatch(item);\n          }).toList();\n\n          if (filtered.isEmpty &&\n              (group['use'] == null ||\n                  (group['use'] is List && group['use'].isEmpty))) {\n            filtered.add('DIRECT');\n          }\n          group['proxies'] = filtered;\n        }\n\n        if (healthCheckTimeout != 5000) {\n          group['timeout'] ??= healthCheckTimeout;\n        }\n      }\n\n      if (filterRegex != null && rawConfig['proxy-providers'] is Map) {\n        final proxyProviders = rawConfig['proxy-providers'] as Map;\n        for (final provider in proxyProviders.values) {\n          if (provider is! Map) continue;\n          final existing = provider['exclude-filter'];\n          if (existing is String && existing.isNotEmpty) {\n            provider['exclude-filter'] = '$existing|$nodeExcludeFilter';\n          } else {\n            provider['exclude-filter'] = nodeExcludeFilter;\n          }\n        }\n      }\n    }\n\n    if (rawConfig['proxy-groups'] is List) {\n      final proxyGroups = rawConfig['proxy-groups'] as List;\n      for (final group in proxyGroups) {\n        if (group is! Map) continue;\n        final tolerance = group['tolerance'];\n        if (tolerance != null) {\n          if (tolerance is double) {\n            group['tolerance'] = tolerance.toInt();\n          } else if (tolerance is String) {\n            group['tolerance'] = int.tryParse(tolerance) ?? tolerance;\n          }\n        }\n      }\n    }\n\n    var rules = [];\n    if (rawConfig['rules'] != null) {\n      rules = rawConfig['rules'];\n      rawConfig.remove('rules');\n    } else if (rawConfig['rule'] != null) {\n      rules = rawConfig['rule'];\n      rawConfig.remove('rule');\n    }\n\n    final overrideData = profile.overrideData;\n    if (overrideData.enable && config.scriptProps.currentScript == null) {\n      if (overrideData.rule.type == OverrideRuleType.override) {\n        rules = overrideData.runningRule;\n      } else {\n        rules = [...overrideData.runningRule, ...rules];\n      }\n    }\n\n\n\n    if (config.vpnProps.fcmOptimization) {\n      final fcmRules = ['DOMAIN,mtalk.google.com,DIRECT'];\n      rules = [...fcmRules, ...rules];\n    }\n\n    if (config.vpnProps.disableQuic) {\n      final isRussian = config.appSetting.locale?.toLowerCase().startsWith('ru') ?? false;\n      final quicRules = config.vpnProps.excludeChina && !isRussian\n          ? ['AND,((NETWORK,UDP),(DST-PORT,443),(NOT,((OR,((GEOSITE,geolocation-cn),(GEOIP,CN,no-resolve)))))),REJECT']\n          : ['AND,((NETWORK,UDP),(DST-PORT,443)),REJECT'];\n      rules = [...quicRules, ...rules];\n    }\n\n    if (rawConfig['proxy-groups'] == null && originalProxyGroups != null) {\n      rawConfig['proxy-groups'] = originalProxyGroups;\n    }\n\n    rawConfig['rule'] = rules;\n    return rawConfig;\n  }\n\n  Future<Map<String, dynamic>> getProfileConfig(String profileId) async {\n    final configMap = await switch (clashLibHandler != null) {\n      true => clashLibHandler!.getConfig(profileId),\n      false => clashCore.getConfig(profileId),\n    };\n    configMap['rules'] = configMap['rule'];\n    configMap.remove('rule');\n    return configMap;\n  }\n\n  Future<Map<String, dynamic>> handleEvaluate(\n    Map<String, dynamic> config,\n  ) async {\n    return _scriptEvaluateLock.synchronized(() async {\n      final currentScript = globalState.config.scriptProps.currentScript;\n      if (currentScript == null) return config;\n\n      config['proxy-providers'] ??= {};\n      final configJs = json.encode(config);\n      final scriptJs = json.encode(currentScript.content);\n\n      return JavaScriptRuntimeManager.execute((runtime) async {\n        final res = await runtime.evaluateAsync('''\n          (() => {\n            const __bettboxConfig = $configJs;\n            const __bettboxScript = $scriptJs;\n            const __bettboxMain = new Function(\n              __bettboxScript + '\\\\nreturn typeof main === \"function\" ? main : null;',\n            )();\n            if (typeof __bettboxMain !== 'function') {\n              throw new Error('Script must define main(config)');\n            }\n            return __bettboxMain(__bettboxConfig);\n          })()\n        ''');\n        if (res.isError) throw res.stringResult;\n\n        return switch (res.rawResult) {\n              Pointer() => runtime.convertValue<Map<String, dynamic>>(res),\n              _ => Map<String, dynamic>.from(res.rawResult),\n            } ??\n            config;\n      });\n    });\n  }\n}\n\nclass DashboardRefreshManager {\n  Timer? _timer;\n  bool _isRunning = false;\n  int _counter = 0;\n  int _tickToken = 0;\n\n  final tick1s = ValueNotifier<int>(0);\n  final tick2s = ValueNotifier<int>(0);\n  final tick5s = ValueNotifier<int>(0);\n\n  bool get isRunning => _isRunning;\n\n  Future<bool> _isActive() async {\n    final lifecycleState = WidgetsBinding.instance.lifecycleState;\n    if (lifecycleState != null && lifecycleState != AppLifecycleState.resumed) {\n      return false;\n    }\n    if (system.isDesktop) {\n      final visible = await window?.isVisible;\n      if (visible == false) {\n        return false;\n      }\n      final minimized = await window?.isMinimized ?? false;\n      if (minimized) {\n        return false;\n      }\n    }\n    return true;\n  }\n\n  Future<void> _tryTick(int token) async {\n    if (!await _isActive()) {\n      return;\n    }\n    if (token != _tickToken) return;\n    _counter++;\n    tick1s.value++;\n    if (_counter % 2 == 0) {\n      tick2s.value++;\n    }\n    if (_counter % 5 == 0) {\n      tick5s.value++;\n    }\n  }\n\n  void start() {\n    if (_isRunning) return;\n    _isRunning = true;\n    _timer = Timer.periodic(const Duration(seconds: 1), (_) {\n      _tryTick(_tickToken);\n    });\n  }\n\n  void stop() {\n    if (!_isRunning) return;\n    _tickToken++;\n    _timer?.cancel();\n    _timer = null;\n    _isRunning = false;\n  }\n}\n\nfinal dashboardRefreshManager = DashboardRefreshManager();\n\nfinal globalState = GlobalState();\n\nclass DetectionState {\n  static DetectionState? _instance;\n  bool? _preIsStart;\n  int _requestId = 0;\n  CancelToken? _cancelToken;\n  bool _isIpMasked = false;\n  IpInfo? _originalIpInfo;\n  bool _isFirstLaunch = true;\n\n  final state = ValueNotifier<NetworkDetectionState>(\n    const NetworkDetectionState(\n      isLoading: true,\n      ipInfo: null,\n      errorMessage: null,\n    ),\n  );\n\n  DetectionState._internal();\n\n  factory DetectionState() {\n    _instance ??= DetectionState._internal();\n    return _instance!;\n  }\n\n  bool get isIpMasked => _isIpMasked;\n\n  void toggleIpPrivacy() {\n    _isIpMasked = !_isIpMasked;\n    final currentIpInfo = state.value.ipInfo;\n    if (currentIpInfo != null) {\n      if (_isIpMasked) {\n        _originalIpInfo = currentIpInfo;\n        state.value = state.value.copyWith(\n          ipInfo: currentIpInfo.copyWith(ip: '*** *** *** ***'),\n        );\n      } else if (_originalIpInfo != null) {\n        state.value = state.value.copyWith(ipInfo: _originalIpInfo);\n        _originalIpInfo = null;\n      }\n    }\n  }\n\n  void manualRefresh() {\n    _isIpMasked = false;\n    _originalIpInfo = null;\n    state.value = state.value.copyWith(\n      isLoading: true,\n      ipInfo: null,\n      errorMessage: null,\n    );\n    startCheck();\n  }\n\n  Future<void> switchToDomesticIp() async {\n    _isIpMasked = false;\n    _originalIpInfo = null;\n\n    _cancelPreviousRequest();\n    _cancelToken = CancelToken();\n    final requestId = ++_requestId;\n\n    state.value = state.value.copyWith(\n      isLoading: true,\n      ipInfo: null,\n      errorMessage: null,\n    );\n\n    final res = await request.checkIpDomestic(cancelToken: _cancelToken);\n\n    if (requestId != _requestId) return;\n\n    _handleResponse(res);\n  }\n\n  void startCheck({bool immediate = false}) {\n    final appState = globalState.appState;\n    if (!appState.isInit) return;\n\n    final delay = immediate\n        ? Duration.zero\n        : const Duration(milliseconds: 1000);\n\n    debouncer.call(FunctionTag.checkIp, _checkIp, duration: delay);\n  }\n\n  void tryStartCheck() {\n    if (!state.value.isLoading &&\n        state.value.ipInfo == null &&\n        (_preIsStart == null || state.value.errorMessage != null)) {\n      startCheck();\n    }\n  }\n\n  void _cancelPreviousRequest() {\n    _cancelToken?.cancel();\n    _cancelToken = null;\n  }\n\n  void _handleResponse(Result<IpInfo?> res) {\n    if (res.isError) {\n      if (res.message == 'cancelled') {\n        state.value = state.value.copyWith(\n          isLoading: false,\n          ipInfo: null,\n          errorMessage: null,\n        );\n        return;\n      }\n      state.value = state.value.copyWith(\n        isLoading: false,\n        ipInfo: null,\n        errorMessage: appLocalizations.tryManualRefresh,\n      );\n      return;\n    }\n\n    final ipInfo = res.data;\n    state.value = state.value.copyWith(\n      isLoading: false,\n      ipInfo: ipInfo,\n      errorMessage: ipInfo != null ? null : appLocalizations.tryManualRefresh,\n    );\n  }\n\n  Future<void> _checkIp() async {\n    final appState = globalState.appState;\n\n    if (!appState.isInit) return;\n\n    final isStart = appState.runTime != null;\n\n    final isStateChanged = _preIsStart != isStart;\n    _preIsStart = isStart;\n\n    if (!isStart && state.value.ipInfo != null && !state.value.isLoading && !isStateChanged) {\n      return;\n    }\n\n    _cancelPreviousRequest();\n    _cancelToken = CancelToken();\n    final requestId = ++_requestId;\n\n    state.value = state.value.copyWith(\n      isLoading: true,\n      errorMessage: null,\n      ipInfo: isStateChanged ? null : state.value.ipInfo,\n    );\n\n    final timeout = const Duration(seconds: 5);\n\n    final res = isStart\n        ? await request.checkIp(cancelToken: _cancelToken, timeout: timeout)\n        : await request.checkIpDomestic(\n            cancelToken: _cancelToken,\n            timeout: timeout,\n          );\n\n    if (requestId != _requestId) return;\n\n    if (_isFirstLaunch && (res.isError || res.data == null)) {\n      _isFirstLaunch = false;\n      _handleResponse(res);\n\n      Future.delayed(const Duration(seconds: 3), () {\n        if (state.value.ipInfo == null && !state.value.isLoading) {\n          startCheck();\n        }\n      });\n    } else {\n      _isFirstLaunch = false;\n      _handleResponse(res);\n    }\n  }\n}\n\nfinal detectionState = DetectionState();\n"
  },
  {
    "path": "lib/views/about.dart",
    "content": "import 'dart:async';\n\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/list.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\n@immutable\nclass Contributor {\n  final String avatar;\n  final String name;\n\n  const Contributor({required this.avatar, required this.name});\n}\n\nclass AboutView extends StatelessWidget {\n  const AboutView({super.key});\n\n  Future<void> _checkUpdate(BuildContext context) async {\n    final commonScaffoldState = context.commonScaffoldState;\n    if (commonScaffoldState?.mounted != true) return;\n    final data = await globalState.appController.safeRun<Map<String, dynamic>?>(\n      request.checkForUpdate,\n      title: appLocalizations.checkUpdate,\n      needLoading: true,\n    );\n    globalState.appController.checkUpdateResultHandle(\n      data: data,\n      handleError: true,\n    );\n  }\n\n  List<Widget> _buildMoreSection(BuildContext context) {\n    return generateSection(\n      separated: false,\n      title: appLocalizations.more,\n      items: [\n        ListItem(\n          title: Text(appLocalizations.checkUpdate),\n          onTap: () {\n            _checkUpdate(context);\n          },\n        ),\n        ListItem(\n          title: const Text('Github Releases'),\n          onTap: () {\n            globalState.openUrl('https://github.com/appshubcc/Bettbox');\n          },\n          trailing: const Icon(Icons.star),\n        ),\n        ListItem(\n          title: const Text('Telegram Group'),\n          onTap: () {\n            globalState.openUrl('https://t.me/appshub_chat');\n          },\n          trailing: const Icon(Icons.launch),\n        ),\n        ListItem(\n          title: const Text('Telegram Channel'),\n          onTap: () {\n            globalState.openUrl('https://t.me/appshub_channel');\n          },\n          trailing: const Icon(Icons.launch),\n        ),\n        ListItem(\n          title: const Text('FlClash'),\n          onTap: () {\n            globalState.openUrl('https://github.com/chen08209/FlClash');\n          },\n          trailing: const Icon(Icons.launch),\n        ),\n        ListItem(\n          title: const Text('Mihomo'),\n          onTap: () {\n            globalState.openUrl('https://github.com/MetaCubeX/mihomo');\n          },\n          trailing: const Icon(Icons.launch),\n        ),\n      ],\n    );\n  }\n\n  List<Widget> _buildContributorsSection() {\n    const contributors = [\n      Contributor(avatar: 'assets/images/avatars/june2.jpg', name: 'June2'),\n      Contributor(avatar: 'assets/images/avatars/arue.jpg', name: 'Arue'),\n      Contributor(avatar: 'assets/images/avatars/dabaozi.jpg', name: '大包子'),\n      Contributor(avatar: 'assets/images/avatars/xiaolou.jpg', name: '小楼'),\n      Contributor(avatar: 'assets/images/avatars/www.jpg', name: 'Www'),\n      Contributor(avatar: 'assets/images/avatars/AIsouler.jpg', name: 'AIsouler'),\n      Contributor(avatar: 'assets/images/avatars/songchenwen.jpg', name: 'songchenwen'),\n      Contributor(avatar: 'assets/images/avatars/EriDeLee.jpg', name: 'EriDeLee'),\n    ];\n    return generateSection(\n      separated: false,\n      title: appLocalizations.otherContributors,\n      items: [\n        ListItem(\n          title: SingleChildScrollView(\n            scrollDirection: Axis.horizontal,\n            child: Wrap(\n              spacing: 24,\n              children: [\n                for (final contributor in contributors)\n                  Avatar(contributor: contributor),\n              ],\n            ),\n          ),\n        ),\n      ],\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final items = [\n      ListTile(\n        title: Column(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: [\n            Consumer(\n              builder: (_, ref, _) {\n                return _DeveloperModeDetector(\n                  child: Wrap(\n                    spacing: 16,\n                    crossAxisAlignment: WrapCrossAlignment.center,\n                    children: [\n                      Padding(\n                        padding: const EdgeInsets.all(12),\n                        child: Image.asset(\n                          'assets/images/icon.png',\n                          width: 64,\n                          height: 64,\n                        ),\n                      ),\n                      Column(\n                        crossAxisAlignment: CrossAxisAlignment.start,\n                        children: [\n                          Text(\n                            appName,\n                            style: Theme.of(context).textTheme.headlineSmall,\n                          ),\n                          Text(\n                            '${globalState.packageInfo.version}+${globalState.packageInfo.buildNumber}',\n                            style: Theme.of(context).textTheme.labelLarge,\n                          ),\n                        ],\n                      ),\n                    ],\n                  ),\n                  onEnterDeveloperMode: () {\n                    ref\n                        .read(appSettingProvider.notifier)\n                        .updateState(\n                          (state) => state.copyWith(developerMode: true),\n                        );\n                    context.showNotifier(\n                      appLocalizations.developerModeEnableTip,\n                    );\n                  },\n                );\n              },\n            ),\n            const SizedBox(height: 24),\n            Text(\n              appLocalizations.desc,\n              style: Theme.of(context).textTheme.bodySmall,\n            ),\n          ],\n        ),\n      ),\n      const SizedBox(height: 12),\n      ..._buildContributorsSection(),\n      ..._buildMoreSection(context),\n    ];\n    return Padding(\n      padding: kMaterialListPadding.copyWith(top: 16, bottom: 16),\n      child: generateListView(items),\n    );\n  }\n}\n\nclass Avatar extends StatelessWidget {\n  final Contributor contributor;\n\n  const Avatar({super.key, required this.contributor});\n\n  @override\n  Widget build(BuildContext context) {\n    return Column(\n      children: [\n        SizedBox(\n          width: 36,\n          height: 36,\n          child: CircleAvatar(foregroundImage: AssetImage(contributor.avatar)),\n        ),\n        const SizedBox(height: 4),\n        Text(contributor.name, style: context.textTheme.bodySmall),\n      ],\n    );\n  }\n}\n\nclass _DeveloperModeDetector extends StatefulWidget {\n  final Widget child;\n  final VoidCallback onEnterDeveloperMode;\n\n  const _DeveloperModeDetector({\n    required this.child,\n    required this.onEnterDeveloperMode,\n  });\n\n  @override\n  State<_DeveloperModeDetector> createState() => _DeveloperModeDetectorState();\n}\n\nclass _DeveloperModeDetectorState extends State<_DeveloperModeDetector> {\n  int _counter = 0;\n  Timer? _timer;\n\n  void _handleTap() {\n    _counter++;\n    if (_counter >= 5) {\n      widget.onEnterDeveloperMode();\n      _resetCounter();\n    } else {\n      _timer?.cancel();\n      _timer = Timer(Duration(seconds: 1), _resetCounter);\n    }\n  }\n\n  void _resetCounter() {\n    _counter = 0;\n    _timer?.cancel();\n    _timer = null;\n  }\n\n  @override\n  void dispose() {\n    _timer?.cancel();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return GestureDetector(onTap: _handleTap, child: widget.child);\n  }\n}\n"
  },
  {
    "path": "lib/views/access.dart",
    "content": "import 'dart:async';\nimport 'dart:convert';\n\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/plugins/app.dart';\nimport 'package:bett_box/providers/providers.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\n// Force refresh icon flag\nfinal forceRefreshIconProvider = StateProvider<bool>((ref) => false);\n\nclass AccessView extends ConsumerStatefulWidget {\n  const AccessView({super.key});\n\n  @override\n  ConsumerState<AccessView> createState() => _AccessViewState();\n}\n\nclass _AccessViewState extends ConsumerState<AccessView>\n    with WidgetsBindingObserver {\n  List<String> acceptList = [];\n  List<String> rejectList = [];\n  late ScrollController _controller;\n  final Completer<void> _completer = Completer<void>();\n  bool _requestedPackageListPermission = false;\n  bool _notifiedPackageListPermissionDenied = false;\n  bool _isCheckingPermissionOrLoading = false;\n  bool _packageListPermissionDenied = false;\n\n  @override\n  void initState() {\n    super.initState();\n    _updateInitList();\n    _controller = ScrollController();\n    WidgetsBinding.instance.addObserver(this);\n    _checkPermissionAndLoadPackages(interactive: true);\n  }\n\n  @override\n  void didChangeAppLifecycleState(AppLifecycleState state) {\n    if (state != AppLifecycleState.resumed) return;\n    if (!mounted) return;\n    if (ref.read(packagesProvider).isNotEmpty &&\n        !_packageListPermissionDenied) {\n      return;\n    }\n    _checkPermissionAndLoadPackages(interactive: false);\n  }\n\n  void _completeLoadingIfNeeded() {\n    if (_completer.isCompleted) return;\n    _completer.complete();\n  }\n\n  Widget _buildPackageListPermissionDeniedView() {\n    return Center(\n      child: Padding(\n        padding: const EdgeInsets.all(24),\n        child: Column(\n          mainAxisSize: MainAxisSize.min,\n          children: [\n            Icon(\n              Icons.apps_outlined,\n              size: 72,\n              color: Theme.of(context).colorScheme.onSurfaceVariant,\n            ),\n            const SizedBox(height: 16),\n            Text(\n              appLocalizations.packageListPermissionDenied,\n              textAlign: TextAlign.center,\n              style: Theme.of(context).textTheme.titleMedium,\n            ),\n            const SizedBox(height: 8),\n            Text(\n              appLocalizations.packageListPermissionRequired,\n              textAlign: TextAlign.center,\n              style: Theme.of(context).textTheme.bodyMedium?.copyWith(\n                color: Theme.of(context).colorScheme.onSurfaceVariant,\n              ),\n            ),\n            const SizedBox(height: 20),\n            Wrap(\n              spacing: 12,\n              runSpacing: 12,\n              alignment: WrapAlignment.center,\n              children: [\n                FilledButton.tonalIcon(\n                  onPressed: () async {\n                    _requestedPackageListPermission = true;\n                    await app.requestPackageListPermission();\n                  },\n                  icon: const Icon(Icons.settings),\n                  label: Text(appLocalizations.openSettings),\n                ),\n              ],\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n\n  Future<void> _checkPermissionAndLoadPackages({\n    required bool interactive,\n    bool forceReload = false,\n    bool openSettingsFallback = false,\n  }) async {\n    if (_isCheckingPermissionOrLoading) return;\n    _isCheckingPermissionOrLoading = true;\n    try {\n      final hasPermission = await app.hasPackageListPermission();\n      if (!mounted) return;\n\n      if (!hasPermission) {\n        if (!_packageListPermissionDenied) {\n          setState(() {\n            _packageListPermissionDenied = true;\n          });\n        }\n        if (interactive) {\n          if (openSettingsFallback) {\n            _requestedPackageListPermission = true;\n            await app.requestPackageListPermission();\n          } else {\n            final res = await globalState.showMessage(\n              title: appLocalizations.tip,\n              message: TextSpan(\n                text: appLocalizations.packageListPermissionRequired,\n              ),\n              confirmText: appLocalizations.openSettings,\n            );\n            if (!mounted) return;\n            if (res == true) {\n              _requestedPackageListPermission = true;\n              await app.requestPackageListPermission();\n            } else if (!_notifiedPackageListPermissionDenied) {\n              _notifiedPackageListPermissionDenied = true;\n              globalState.showNotifier(\n                appLocalizations.packageListPermissionDenied,\n              );\n            }\n          }\n        } else if (_requestedPackageListPermission &&\n            !_notifiedPackageListPermissionDenied) {\n          _notifiedPackageListPermissionDenied = true;\n          globalState.showNotifier(\n            appLocalizations.packageListPermissionDenied,\n          );\n        }\n        _completeLoadingIfNeeded();\n        return;\n      }\n\n      if (_packageListPermissionDenied) {\n        setState(() {\n          _packageListPermissionDenied = false;\n        });\n      }\n      await globalState.appController.getPackages(forceRefresh: forceReload);\n      if (!mounted) return;\n      if (system.isAndroid && ref.read(packagesProvider).isEmpty) {\n        if (!_packageListPermissionDenied) {\n          setState(() {\n            _packageListPermissionDenied = true;\n          });\n        }\n        if (interactive) {\n          final res = await globalState.showMessage(\n            title: appLocalizations.tip,\n            message: TextSpan(\n              text: appLocalizations.packageListPermissionRequired,\n            ),\n            confirmText: appLocalizations.openSettings,\n          );\n          if (!mounted) return;\n          if (res == true) {\n            _requestedPackageListPermission = true;\n            await app.requestPackageListPermission();\n          }\n        }\n        _completeLoadingIfNeeded();\n        return;\n      }\n      _completeLoadingIfNeeded();\n    } catch (e) {\n      _completeLoadingIfNeeded();\n      if (e is PlatformException && e.code == 'PACKAGE_LIST_PERMISSION') {\n        if (!mounted) return;\n        if (!_packageListPermissionDenied) {\n          setState(() {\n            _packageListPermissionDenied = true;\n          });\n        }\n        if (interactive) {\n          if (openSettingsFallback) {\n            _requestedPackageListPermission = true;\n            await app.requestPackageListPermission();\n            return;\n          }\n          final res = await globalState.showMessage(\n            title: appLocalizations.tip,\n            message: TextSpan(\n              text: appLocalizations.packageListPermissionRequired,\n            ),\n            confirmText: appLocalizations.openSettings,\n          );\n          if (!mounted) return;\n          if (res == true) {\n            _requestedPackageListPermission = true;\n            await app.requestPackageListPermission();\n          } else if (!_notifiedPackageListPermissionDenied) {\n            _notifiedPackageListPermissionDenied = true;\n            globalState.showNotifier(\n              appLocalizations.packageListPermissionDenied,\n            );\n          }\n          return;\n        }\n        if (_requestedPackageListPermission &&\n            !_notifiedPackageListPermissionDenied) {\n          _notifiedPackageListPermissionDenied = true;\n          globalState.showNotifier(\n            appLocalizations.packageListPermissionDenied,\n          );\n          return;\n        }\n      }\n      globalState.showNotifier(e.toString());\n    } finally {\n      _isCheckingPermissionOrLoading = false;\n    }\n  }\n\n  @override\n  void dispose() {\n    WidgetsBinding.instance.removeObserver(this);\n    _controller.dispose();\n    super.dispose();\n  }\n\n  void _updateInitList() {\n    acceptList = globalState.config.vpnProps.accessControl.acceptList;\n    rejectList = globalState.config.vpnProps.accessControl.rejectList;\n  }\n\n  Widget _buildSearchButton() {\n    return IconButton(\n      tooltip: appLocalizations.search,\n      onPressed: () {\n        showSearch(\n          context: context,\n          delegate: AccessControlSearchDelegate(\n            acceptList: acceptList,\n            rejectList: rejectList,\n          ),\n        ).then((_) {\n          if (mounted) {\n            setState(() {\n              _updateInitList();\n            });\n          }\n        });\n      },\n      icon: const Icon(Icons.search),\n    );\n  }\n\n  Widget _buildRefreshButton() {\n    return IconButton(\n      tooltip: appLocalizations.refreshAppList,\n      onPressed: () async {\n        final res = await globalState.showMessage(\n          title: appLocalizations.tip,\n          message: TextSpan(text: appLocalizations.refreshAppListConfirm),\n        );\n        if (res != true) return;\n\n        // Reload app list (auto-checks and requests permissions)\n        await globalState.appController.safeRun(() async {\n          // Clear cache, force reload\n          // Mark as force refresh mode\n          ref.read(forceRefreshIconProvider.notifier).state = true;\n          await _checkPermissionAndLoadPackages(\n            interactive: true,\n            forceReload: true,\n          );\n          // Reset force refresh flag\n          if (mounted) {\n            ref.read(forceRefreshIconProvider.notifier).state = false;\n          }\n        }, needLoading: true);\n\n        if (mounted) {\n          setState(() {\n            _updateInitList();\n          });\n        }\n      },\n      icon: const Icon(Icons.sync),\n    );\n  }\n\n  Widget _buildSelectedAllButton({\n    required bool isSelectedAll,\n    required List<String> allValueList,\n  }) {\n    final tooltip = isSelectedAll\n        ? appLocalizations.cancelSelectAll\n        : appLocalizations.selectAll;\n    return IconButton(\n      tooltip: tooltip,\n      onPressed: () {\n        ref.read(vpnSettingProvider.notifier).updateState((state) {\n          final isAccept =\n              state.accessControl.mode == AccessControlMode.acceptSelected;\n          if (isSelectedAll) {\n            return switch (isAccept) {\n              true => state.copyWith.accessControl(acceptList: []),\n              false => state.copyWith.accessControl(rejectList: []),\n            };\n          } else {\n            return switch (isAccept) {\n              true => state.copyWith.accessControl(acceptList: allValueList),\n              false => state.copyWith.accessControl(rejectList: allValueList),\n            };\n          }\n        });\n      },\n      icon: isSelectedAll\n          ? const Icon(Icons.deselect)\n          : const Icon(Icons.select_all),\n    );\n  }\n\n  Future<void> _intelligentSelected() async {\n    final packageNames = ref.read(\n      packageListSelectorStateProvider.select(\n        (state) => state.list.map((item) => item.packageName),\n      ),\n    );\n    final commonScaffoldState = context.commonScaffoldState;\n    if (commonScaffoldState?.mounted != true) return;\n    final selectedPackageNames =\n        (await globalState.appController.safeRun<List<String>>(\n          needLoading: true,\n          () async {\n            return await app.getChinaPackageNames();\n          },\n        ))?.toSet() ??\n        {};\n    final acceptList = packageNames\n        .where((item) => !selectedPackageNames.contains(item))\n        .toList();\n    final rejectList = packageNames\n        .where((item) => selectedPackageNames.contains(item))\n        .toList();\n    ref\n        .read(vpnSettingProvider.notifier)\n        .updateState(\n          (state) => state.copyWith.accessControl(\n            acceptList: acceptList,\n            rejectList: rejectList,\n          ),\n        );\n  }\n\n  Widget _buildSettingButton() {\n    return IconButton(\n      onPressed: () async {\n        final res = await showSheet<int>(\n          context: context,\n          props: SheetProps(isScrollControlled: true),\n          builder: (_, type) {\n            return AdaptiveSheetScaffold(\n              type: type,\n              body: AccessControlPanel(),\n              title: appLocalizations.proxiesSetting,\n            );\n          },\n        );\n        if (res == 1) {\n          _intelligentSelected();\n        }\n      },\n      icon: const Icon(Icons.tune),\n    );\n  }\n\n  void _handleSelected(List<String> valueList, Package package, bool? value) {\n    if (value == true) {\n      valueList.add(package.packageName);\n    } else {\n      valueList.remove(package.packageName);\n    }\n    ref.read(vpnSettingProvider.notifier).updateState((state) {\n      return switch (state.accessControl.mode ==\n          AccessControlMode.acceptSelected) {\n        true => state.copyWith.accessControl(acceptList: valueList),\n        false => state.copyWith.accessControl(rejectList: valueList),\n      };\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final state = ref.watch(packageListSelectorStateProvider);\n    final accessControl = state.accessControl;\n    final accessControlMode = accessControl.mode;\n    final packages = state.getSortList(\n      accessControlMode == AccessControlMode.acceptSelected\n          ? acceptList\n          : rejectList,\n    );\n    final currentList = accessControl.currentList;\n    final packageNameList = packages.map((e) => e.packageName).toList();\n    final valueList = currentList.intersection(packageNameList);\n    final describe = accessControlMode == AccessControlMode.acceptSelected\n        ? appLocalizations.accessControlAllowDesc\n        : appLocalizations.accessControlNotAllowDesc;\n    return Column(\n      mainAxisSize: MainAxisSize.max,\n      children: [\n        Flexible(\n          flex: 0,\n          child: ListItem.switchItem(\n            title: Text(appLocalizations.appAccessControl),\n            trailing: accessControl.enable ? _buildRefreshButton() : null,\n            delegate: SwitchDelegate(\n              value: accessControl.enable,\n              onChanged: (enable) {\n                ref\n                    .read(vpnSettingProvider.notifier)\n                    .updateState(\n                      (state) => state.copyWith.accessControl(enable: enable),\n                    );\n              },\n            ),\n          ),\n        ),\n        const Padding(\n          padding: EdgeInsets.symmetric(horizontal: 16),\n          child: Divider(height: 12),\n        ),\n        Flexible(\n          child: DisabledMask(\n            status: !accessControl.enable,\n            child: Column(\n              children: [\n                ActivateBox(\n                  active: accessControl.enable,\n                  child: Padding(\n                    padding: const EdgeInsets.only(\n                      top: 4,\n                      bottom: 4,\n                      left: 16,\n                      right: 8,\n                    ),\n                    child: Row(\n                      mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                      mainAxisSize: MainAxisSize.max,\n                      children: [\n                        Expanded(\n                          child: IntrinsicHeight(\n                            child: Column(\n                              mainAxisSize: MainAxisSize.max,\n                              crossAxisAlignment: CrossAxisAlignment.start,\n                              children: [\n                                Expanded(\n                                  child: Row(\n                                    children: [\n                                      Flexible(\n                                        child: Text(\n                                          appLocalizations.selected,\n                                          style: Theme.of(context)\n                                              .textTheme\n                                              .labelLarge\n                                              ?.copyWith(\n                                                color: Theme.of(\n                                                  context,\n                                                ).colorScheme.primary,\n                                              ),\n                                        ),\n                                      ),\n                                      const Flexible(child: SizedBox(width: 8)),\n                                      Flexible(\n                                        child: Text(\n                                          '${valueList.length}',\n                                          style: Theme.of(context)\n                                              .textTheme\n                                              .labelLarge\n                                              ?.copyWith(\n                                                color: Theme.of(\n                                                  context,\n                                                ).colorScheme.primary,\n                                              ),\n                                        ),\n                                      ),\n                                    ],\n                                  ),\n                                ),\n                                Flexible(child: Text(describe)),\n                              ],\n                            ),\n                          ),\n                        ),\n                        Row(\n                          mainAxisSize: MainAxisSize.min,\n                          mainAxisAlignment: MainAxisAlignment.end,\n                          children: [\n                            Flexible(child: _buildSearchButton()),\n                            Flexible(\n                              child: _buildSelectedAllButton(\n                                isSelectedAll:\n                                    valueList.length == packageNameList.length,\n                                allValueList: packageNameList,\n                              ),\n                            ),\n                            Flexible(child: _buildSettingButton()),\n                          ],\n                        ),\n                      ],\n                    ),\n                  ),\n                ),\n                Expanded(\n                  flex: 1,\n                  child: FutureBuilder(\n                    future: _completer.future,\n                    builder: (_, snapshot) {\n                      if (snapshot.connectionState != ConnectionState.done) {\n                        return Center(child: CircularProgressIndicator());\n                      }\n                      if (_packageListPermissionDenied) {\n                        return _buildPackageListPermissionDeniedView();\n                      }\n                      return packages.isEmpty\n                          ? NullStatus(label: appLocalizations.noData)\n                          : CommonScrollBar(\n                              controller: _controller,\n                              child: ListView.builder(\n                                controller: _controller,\n                                itemCount: packages.length,\n                                itemExtent: 72,\n                                itemBuilder: (_, index) {\n                                  final package = packages[index];\n                                  return PackageListItem(\n                                    key: Key(package.packageName),\n                                    package: package,\n                                    value: valueList.contains(\n                                      package.packageName,\n                                    ),\n                                    isActive: accessControl.enable,\n                                    onChanged: (value) {\n                                      _handleSelected(\n                                        valueList,\n                                        package,\n                                        value,\n                                      );\n                                    },\n                                  );\n                                },\n                              ),\n                            );\n                    },\n                  ),\n                ),\n              ],\n            ),\n          ),\n        ),\n      ],\n    );\n  }\n}\n\nclass PackageListItem extends ConsumerWidget {\n  final Package package;\n  final bool value;\n  final bool isActive;\n  final void Function(bool?) onChanged;\n\n  const PackageListItem({\n    super.key,\n    required this.package,\n    required this.value,\n    required this.isActive,\n    required this.onChanged,\n  });\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final forceRefresh = ref.watch(forceRefreshIconProvider);\n\n    return ActivateBox(\n      active: isActive,\n      child: ListItem.checkbox(\n        leading: SizedBox(\n          width: 48,\n          height: 48,\n          child: FutureBuilder<Uint8List?>(\n            future: app.getPackageIcon(\n              package.packageName,\n              forceRefresh: forceRefresh,\n            ),\n            builder: (context, snapshot) {\n              if (snapshot.hasData && snapshot.data != null) {\n                final devicePixelRatio = MediaQuery.of(\n                  context,\n                ).devicePixelRatio;\n                final cacheSize = (48 * devicePixelRatio).ceil();\n\n                return Image.memory(\n                  snapshot.data!,\n                  gaplessPlayback: true,\n                  width: 48,\n                  height: 48,\n                  cacheWidth: cacheSize,\n                  cacheHeight: cacheSize,\n                  errorBuilder: (_, _, _) => _buildDefaultIcon(context),\n                );\n              }\n              return _buildDefaultIcon(context);\n            },\n          ),\n        ),\n        title: Text(\n          package.label,\n          style: const TextStyle(overflow: TextOverflow.ellipsis),\n          maxLines: 1,\n        ),\n        subtitle: Text(\n          package.packageName,\n          style: const TextStyle(overflow: TextOverflow.ellipsis),\n          maxLines: 1,\n        ),\n        delegate: CheckboxDelegate(value: value, onChanged: onChanged),\n      ),\n    );\n  }\n\n  Widget _buildDefaultIcon(BuildContext context) {\n    return Container(\n      decoration: BoxDecoration(\n        color: Theme.of(context).colorScheme.surfaceContainerHighest,\n        borderRadius: BorderRadius.circular(8),\n      ),\n      child: Icon(\n        Icons.apps,\n        size: 24,\n        color: Theme.of(context).colorScheme.onSurfaceVariant,\n      ),\n    );\n  }\n}\n\nclass AccessControlSearchDelegate extends SearchDelegate {\n  List<String> acceptList = [];\n  List<String> rejectList = [];\n\n  AccessControlSearchDelegate({\n    required this.acceptList,\n    required this.rejectList,\n  });\n\n  @override\n  List<Widget>? buildActions(BuildContext context) {\n    return [\n      IconButton(\n        onPressed: () {\n          if (query.isEmpty) {\n            close(context, null);\n            return;\n          }\n          query = '';\n        },\n        icon: const Icon(Icons.clear),\n      ),\n      const SizedBox(width: 8),\n    ];\n  }\n\n  @override\n  Widget? buildLeading(BuildContext context) {\n    return IconButton(\n      onPressed: () {\n        close(context, null);\n      },\n      icon: const Icon(Icons.arrow_back),\n    );\n  }\n\n  void _handleSelected(\n    WidgetRef ref,\n    List<String> valueList,\n    Package package,\n    bool? value,\n  ) {\n    if (value == true) {\n      valueList.add(package.packageName);\n    } else {\n      valueList.remove(package.packageName);\n    }\n    ref.read(vpnSettingProvider.notifier).updateState((state) {\n      return switch (state.accessControl.mode ==\n          AccessControlMode.acceptSelected) {\n        true => state.copyWith.accessControl(acceptList: valueList),\n        false => state.copyWith.accessControl(rejectList: valueList),\n      };\n    });\n  }\n\n  Widget _packageList() {\n    final lowQuery = query.toLowerCase();\n    return Consumer(\n      builder: (context, ref, _) {\n        final vm3 = ref.watch(\n          packageListSelectorStateProvider.select(\n            (state) => VM3(\n              a: state.getSortList(\n                state.accessControl.mode == AccessControlMode.acceptSelected\n                    ? acceptList\n                    : rejectList,\n              ),\n              b: state.accessControl.enable,\n              c: state.accessControl.currentList,\n            ),\n          ),\n        );\n        final packages = vm3.a;\n\n        // Search filter (inside Consumer, auto-responds to query changes)\n        final queryPackages = query.isEmpty\n            ? packages\n            : packages\n                  .where(\n                    (package) =>\n                        package.label.toLowerCase().contains(lowQuery) ||\n                        package.packageName.contains(lowQuery),\n                  )\n                  .toList();\n\n        final isAccessControl = vm3.b;\n        final currentList = vm3.c;\n        final packageNameList = packages.map((e) => e.packageName).toList();\n        final valueList = currentList.intersection(packageNameList);\n\n        return DisabledMask(\n          status: !isAccessControl,\n          child: ListView.builder(\n            itemCount: queryPackages.length,\n            itemExtent: 72,\n            itemBuilder: (_, index) {\n              final package = queryPackages[index];\n              return PackageListItem(\n                key: Key(package.packageName),\n                package: package,\n                value: valueList.contains(package.packageName),\n                isActive: isAccessControl,\n                onChanged: (value) {\n                  _handleSelected(ref, valueList, package, value);\n                },\n              );\n            },\n          ),\n        );\n      },\n    );\n  }\n\n  @override\n  Widget buildResults(BuildContext context) {\n    return buildSuggestions(context);\n  }\n\n  @override\n  Widget buildSuggestions(BuildContext context) {\n    return _packageList();\n  }\n}\n\nclass AccessControlPanel extends ConsumerStatefulWidget {\n  const AccessControlPanel({super.key});\n\n  @override\n  ConsumerState createState() => _AccessControlPanelState();\n}\n\nclass _AccessControlPanelState extends ConsumerState<AccessControlPanel> {\n  IconData _getIconWithAccessControlMode(AccessControlMode mode) {\n    return switch (mode) {\n      AccessControlMode.acceptSelected => Icons.adjust_outlined,\n      AccessControlMode.rejectSelected => Icons.block_outlined,\n    };\n  }\n\n  String _getTextWithAccessControlMode(AccessControlMode mode) {\n    return switch (mode) {\n      AccessControlMode.acceptSelected => appLocalizations.whitelistMode,\n      AccessControlMode.rejectSelected => appLocalizations.blacklistMode,\n    };\n  }\n\n  String _getTextWithAccessSortType(AccessSortType type) {\n    return switch (type) {\n      AccessSortType.none => appLocalizations.defaultText,\n      AccessSortType.name => appLocalizations.name,\n      AccessSortType.time => appLocalizations.time,\n    };\n  }\n\n  IconData _getIconWithProxiesSortType(AccessSortType type) {\n    return switch (type) {\n      AccessSortType.none => Icons.sort,\n      AccessSortType.name => Icons.sort_by_alpha,\n      AccessSortType.time => Icons.timeline,\n    };\n  }\n\n  List<Widget> _buildModeSetting() {\n    return generateSection(\n      title: appLocalizations.mode,\n      items: [\n        SingleChildScrollView(\n          padding: const EdgeInsets.symmetric(horizontal: 16),\n          scrollDirection: Axis.horizontal,\n          child: Consumer(\n            builder: (_, ref, _) {\n              final accessControlMode = ref.watch(\n                vpnSettingProvider.select((state) => state.accessControl.mode),\n              );\n              return Wrap(\n                spacing: 16,\n                children: [\n                  for (final item in AccessControlMode.values)\n                    SettingInfoCard(\n                      Info(\n                        label: _getTextWithAccessControlMode(item),\n                        iconData: _getIconWithAccessControlMode(item),\n                      ),\n                      isSelected: accessControlMode == item,\n                      onPressed: () {\n                        ref\n                            .read(vpnSettingProvider.notifier)\n                            .updateState(\n                              (state) =>\n                                  state.copyWith.accessControl(mode: item),\n                            );\n                      },\n                    ),\n                ],\n              );\n            },\n          ),\n        ),\n      ],\n    );\n  }\n\n  List<Widget> _buildSortSetting() {\n    return generateSection(\n      title: appLocalizations.sort,\n      items: [\n        SingleChildScrollView(\n          padding: const EdgeInsets.symmetric(horizontal: 16),\n          scrollDirection: Axis.horizontal,\n          child: Consumer(\n            builder: (_, ref, _) {\n              final accessSortType = ref.watch(\n                vpnSettingProvider.select((state) => state.accessControl.sort),\n              );\n              return Wrap(\n                spacing: 16,\n                children: [\n                  for (final item in AccessSortType.values)\n                    SettingInfoCard(\n                      Info(\n                        label: _getTextWithAccessSortType(item),\n                        iconData: _getIconWithProxiesSortType(item),\n                      ),\n                      isSelected: accessSortType == item,\n                      onPressed: () {\n                        ref\n                            .read(vpnSettingProvider.notifier)\n                            .updateState(\n                              (state) =>\n                                  state.copyWith.accessControl(sort: item),\n                            );\n                      },\n                    ),\n                ],\n              );\n            },\n          ),\n        ),\n      ],\n    );\n  }\n\n  List<Widget> _buildSourceSetting() {\n    return generateSection(\n      title: appLocalizations.source,\n      items: [\n        SingleChildScrollView(\n          padding: const EdgeInsets.symmetric(horizontal: 16),\n          scrollDirection: Axis.horizontal,\n          child: Consumer(\n            builder: (_, ref, _) {\n              final vm2 = ref.watch(\n                vpnSettingProvider.select(\n                  (state) => VM2(\n                    a: state.accessControl.isFilterSystemApp,\n                    b: state.accessControl.isFilterNonInternetApp,\n                  ),\n                ),\n              );\n              return Wrap(\n                spacing: 16,\n                children: [\n                  SettingTextCard(\n                    appLocalizations.systemApp,\n                    isSelected: vm2.a == false,\n                    onPressed: () {\n                      ref\n                          .read(vpnSettingProvider.notifier)\n                          .updateState(\n                            (state) => state.copyWith.accessControl(\n                              isFilterSystemApp: !vm2.a,\n                            ),\n                          );\n                    },\n                  ),\n                  SettingTextCard(\n                    appLocalizations.noNetworkApp,\n                    isSelected: vm2.b == false,\n                    onPressed: () {\n                      ref\n                          .read(vpnSettingProvider.notifier)\n                          .updateState(\n                            (state) => state.copyWith.accessControl(\n                              isFilterNonInternetApp: !vm2.b,\n                            ),\n                          );\n                    },\n                  ),\n                ],\n              );\n            },\n          ),\n        ),\n      ],\n    );\n  }\n\n  Future<void> _copyToClipboard() async {\n    await globalState.appController.safeRun(() {\n      final data = globalState.config.vpnProps.accessControl.toJson();\n      Clipboard.setData(ClipboardData(text: json.encode(data)));\n    });\n    if (!mounted) return;\n    Navigator.of(context).pop();\n  }\n\n  Future<void> _pasteToClipboard() async {\n    await globalState.appController.safeRun(() async {\n      final data = await Clipboard.getData('text/plain');\n      final text = data?.text;\n      if (text == null) return;\n      ref\n          .read(vpnSettingProvider.notifier)\n          .updateState(\n            (state) => state.copyWith(\n              accessControl: AccessControl.fromJson(json.decode(text)),\n            ),\n          );\n    });\n    if (!mounted) return;\n    Navigator.of(context).pop();\n  }\n\n  List<Widget> _buildActionSetting() {\n    return generateSection(\n      title: appLocalizations.action,\n      items: [\n        Padding(\n          padding: const EdgeInsets.symmetric(horizontal: 16),\n          child: Wrap(\n            runSpacing: 16,\n            spacing: 16,\n            children: [\n              CommonChip(\n                avatar: const Icon(Icons.auto_awesome),\n                label: appLocalizations.intelligentSelected,\n                onPressed: () {\n                  Navigator.of(context).pop(1);\n                },\n              ),\n              CommonChip(\n                avatar: const Icon(Icons.paste),\n                label: appLocalizations.clipboardImport,\n                onPressed: _pasteToClipboard,\n              ),\n              CommonChip(\n                avatar: const Icon(Icons.content_copy),\n                label: appLocalizations.clipboardExport,\n                onPressed: _copyToClipboard,\n              ),\n            ],\n          ),\n        ),\n      ],\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return SingleChildScrollView(\n      child: Padding(\n        padding: const EdgeInsets.only(bottom: 32),\n        child: Column(\n          mainAxisSize: MainAxisSize.min,\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: [\n            ..._buildModeSetting(),\n            ..._buildSortSetting(),\n            ..._buildSourceSetting(),\n            ..._buildActionSetting(),\n          ],\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/application_setting.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass CloseConnectionsItem extends ConsumerWidget {\n  const CloseConnectionsItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final closeConnections = ref.watch(\n      appSettingProvider.select((state) => state.closeConnections),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.autoCloseConnections),\n      subtitle: Text(appLocalizations.autoCloseConnectionsDesc),\n      delegate: SwitchDelegate(\n        value: closeConnections,\n        onChanged: (value) async {\n          ref\n              .read(appSettingProvider.notifier)\n              .updateState((state) => state.copyWith(closeConnections: value));\n        },\n      ),\n    );\n  }\n}\n\nclass UsageItem extends ConsumerWidget {\n  const UsageItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final onlyStatisticsProxy = ref.watch(\n      appSettingProvider.select((state) => state.onlyStatisticsProxy),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.onlyStatisticsProxy),\n      subtitle: Text(appLocalizations.onlyStatisticsProxyDesc),\n      delegate: SwitchDelegate(\n        value: onlyStatisticsProxy,\n        onChanged: (bool value) async {\n          ref\n              .read(appSettingProvider.notifier)\n              .updateState(\n                (state) => state.copyWith(onlyStatisticsProxy: value),\n              );\n        },\n      ),\n    );\n  }\n}\n\nclass AutoLaunchItem extends ConsumerWidget {\n  const AutoLaunchItem({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final autoLaunch = ref.watch(\n      appSettingProvider.select((state) => state.autoLaunch),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.autoLaunch),\n      subtitle: Text(appLocalizations.autoLaunchDesc),\n      delegate: SwitchDelegate(\n        value: autoLaunch,\n        onChanged: (bool value) {\n          ref\n              .read(appSettingProvider.notifier)\n              .updateState((state) => state.copyWith(autoLaunch: value));\n        },\n      ),\n    );\n  }\n}\n\nclass SmartDelayLaunchItem extends ConsumerWidget {\n  const SmartDelayLaunchItem({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final smartDelayLaunch = ref.watch(\n      appSettingProvider.select((state) => state.smartDelayLaunch),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.smartDelayLaunch),\n      subtitle: Text(appLocalizations.smartDelayLaunchDesc),\n      delegate: SwitchDelegate(\n        value: smartDelayLaunch,\n        onChanged: (bool value) {\n          ref\n              .read(appSettingProvider.notifier)\n              .updateState((state) => state.copyWith(smartDelayLaunch: value));\n        },\n      ),\n    );\n  }\n}\n\nclass SilentLaunchItem extends ConsumerWidget {\n  const SilentLaunchItem({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final silentLaunch = ref.watch(\n      appSettingProvider.select((state) => state.silentLaunch),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.silentLaunch),\n      subtitle: Text(appLocalizations.silentLaunchDesc),\n      delegate: SwitchDelegate(\n        value: silentLaunch,\n        onChanged: (bool value) {\n          ref\n              .read(appSettingProvider.notifier)\n              .updateState((state) => state.copyWith(silentLaunch: value));\n        },\n      ),\n    );\n  }\n}\n\nclass AutoRunItem extends ConsumerWidget {\n  const AutoRunItem({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final autoRun = ref.watch(\n      appSettingProvider.select((state) => state.autoRun),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.autoRun),\n      subtitle: Text(appLocalizations.autoRunDesc),\n      delegate: SwitchDelegate(\n        value: autoRun,\n        onChanged: (bool value) {\n          ref\n              .read(appSettingProvider.notifier)\n              .updateState((state) => state.copyWith(autoRun: value));\n        },\n      ),\n    );\n  }\n}\n\nclass HiddenItem extends ConsumerWidget {\n  const HiddenItem({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final hidden = ref.watch(\n      appSettingProvider.select((state) => state.hidden),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.exclude),\n      subtitle: Text(appLocalizations.excludeDesc),\n      delegate: SwitchDelegate(\n        value: hidden,\n        onChanged: (value) {\n          ref\n              .read(appSettingProvider.notifier)\n              .updateState((state) => state.copyWith(hidden: value));\n        },\n      ),\n    );\n  }\n}\n\nclass AnimateTabItem extends ConsumerWidget {\n  const AnimateTabItem({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final isAnimateToPage = ref.watch(\n      appSettingProvider.select((state) => state.isAnimateToPage),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.tabAnimation),\n      subtitle: Text(appLocalizations.tabAnimationDesc),\n      delegate: SwitchDelegate(\n        value: isAnimateToPage,\n        onChanged: (value) {\n          ref\n              .read(appSettingProvider.notifier)\n              .updateState((state) => state.copyWith(isAnimateToPage: value));\n        },\n      ),\n    );\n  }\n}\n\nclass NavBarHapticFeedbackItem extends ConsumerWidget {\n  const NavBarHapticFeedbackItem({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final enableNavBarHapticFeedback = ref.watch(\n      appSettingProvider.select((state) => state.enableNavBarHapticFeedback),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.navBarHapticFeedback),\n      subtitle: Text(appLocalizations.navBarHapticFeedbackDesc),\n      delegate: SwitchDelegate(\n        value: enableNavBarHapticFeedback,\n        onChanged: (value) {\n          ref.read(appSettingProvider.notifier).updateState(\n            (state) => state.copyWith(enableNavBarHapticFeedback: value),\n          );\n        },\n      ),\n    );\n  }\n}\n\nclass HighRefreshRateItem extends ConsumerWidget {\n  const HighRefreshRateItem({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final enableHighRefreshRate = ref.watch(\n      appSettingProvider.select((state) => state.enableHighRefreshRate),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.highRefreshRate),\n      subtitle: Text(appLocalizations.highRefreshRateDesc),\n      delegate: SwitchDelegate(\n        value: enableHighRefreshRate,\n        onChanged: (value) {\n          ref.read(appSettingProvider.notifier).updateState(\n            (state) => state.copyWith(enableHighRefreshRate: value),\n          );\n\n          if (context.mounted) {\n            context.showSnackBar(appLocalizations.restartTip);\n          }\n        },\n      ),\n    );\n  }\n}\n\nclass AutoCheckUpdateItem extends ConsumerWidget {\n  const AutoCheckUpdateItem({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final autoCheckUpdate = ref.watch(\n      appSettingProvider.select((state) => state.autoCheckUpdate),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.autoCheckUpdate),\n      subtitle: Text(appLocalizations.autoCheckUpdateDesc),\n      delegate: SwitchDelegate(\n        value: autoCheckUpdate,\n        onChanged: (bool value) {\n          ref\n              .read(appSettingProvider.notifier)\n              .updateState((state) => state.copyWith(autoCheckUpdate: value));\n        },\n      ),\n    );\n  }\n}\n\nclass ApplicationSettingView extends StatelessWidget {\n  const ApplicationSettingView({super.key});\n\n\n\n  @override\n  Widget build(BuildContext context) {\n    return Consumer(\n      builder: (context, ref, _) {\n        final autoLaunch = ref.watch(\n          appSettingProvider.select((state) => state.autoLaunch),\n        );\n        List<Widget> items = [\n          AutoLaunchItem(),\n          if (system.isDesktop) ...[\n            if (system.isWindows && autoLaunch) SmartDelayLaunchItem(),\n            SilentLaunchItem(),\n          ],\n          AutoRunItem(),\n          if (system.isAndroid) ...[HiddenItem()],\n          AnimateTabItem(),\n          if (system.isAndroid) ...[\n            NavBarHapticFeedbackItem(),\n            HighRefreshRateItem(),\n          ],\n          CloseConnectionsItem(),\n          UsageItem(),\n          AutoCheckUpdateItem(),\n        ];\n        return ListView.separated(\n          itemBuilder: (_, index) {\n            final item = items[index];\n            return item;\n          },\n          separatorBuilder: (_, _) {\n            return const Divider(height: 0);\n          },\n          itemCount: items.length,\n        );\n      },\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/backup_and_recovery.dart",
    "content": "import 'dart:typed_data';\n\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/common/dav_client.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/dialog.dart';\nimport 'package:bett_box/widgets/fade_box.dart';\nimport 'package:bett_box/widgets/input.dart';\nimport 'package:bett_box/widgets/list.dart';\nimport 'package:bett_box/widgets/text.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:intl/intl.dart';\n\nclass BackupAndRecovery extends ConsumerWidget {\n  const BackupAndRecovery({super.key});\n\n  Future<void> _showAddWebDAV(DAV? dav) async {\n    await globalState.showCommonDialog<String>(\n      child: WebDAVFormDialog(dav: dav?.copyWith()),\n    );\n  }\n\n  Future<void> _backupOnWebDAV(DAVClient client) async {\n    final res = await globalState.appController.safeRun<bool>(\n      () async {\n        final backupData = await globalState.appController.backupData();\n        return await client.backup(Uint8List.fromList(backupData));\n      },\n      needLoading: true,\n      title: appLocalizations.backup,\n    );\n    if (res != true) return;\n    globalState.showMessage(\n      title: appLocalizations.backup,\n      message: TextSpan(text: appLocalizations.backupSuccess),\n    );\n  }\n\n  Future<void> _recoveryOnWebDAV(\n    BuildContext context,\n    DAVClient client,\n    RecoveryOption recoveryOption,\n  ) async {\n    final res = await globalState.appController.safeRun<bool>(\n      () async {\n        final data = await client.recovery();\n        await globalState.appController.recoveryData(data, recoveryOption);\n        return true;\n      },\n      needLoading: true,\n      title: appLocalizations.recovery,\n    );\n    if (res != true) return;\n    globalState.showMessage(\n      title: appLocalizations.recovery,\n      message: TextSpan(text: appLocalizations.recoverySuccess),\n    );\n  }\n\n  Future<void> _handleRecoveryOnWebDAV(\n    BuildContext context,\n    DAVClient client,\n  ) async {\n    final recoveryOption = await globalState.showCommonDialog<RecoveryOption>(\n      child: const RecoveryOptionsDialog(),\n    );\n    if (recoveryOption == null || !context.mounted) return;\n    _recoveryOnWebDAV(context, client, recoveryOption);\n  }\n\n  Future<void> _backupOnLocal(BuildContext context) async {\n    final res = await globalState.appController.safeRun<bool>(() async {\n      final backupData = await globalState.appController.backupData();\n      final value = await picker.saveFile(\n        utils.getBackupFileName(),\n        Uint8List.fromList(backupData),\n      );\n      if (value == null) return false;\n      return true;\n    }, title: appLocalizations.backup);\n    if (res != true) return;\n    globalState.showMessage(\n      title: appLocalizations.backup,\n      message: TextSpan(text: appLocalizations.backupSuccess),\n    );\n  }\n\n  Future<void> _recoveryOnLocal(RecoveryOption recoveryOption) async {\n    final file = await picker.pickerFile(withData: false);\n    final path = file?.path;\n    final res = await globalState.appController.safeRun<bool>(\n      () async {\n        if (path != null) {\n          await globalState.appController.recoveryDataFromFile(\n            path,\n            recoveryOption,\n          );\n        } else if (file?.bytes != null) {\n          await globalState.appController.recoveryData(\n            List<int>.from(file!.bytes!),\n            recoveryOption,\n          );\n        } else {\n          return false;\n        }\n        return true;\n      },\n      needLoading: true,\n      title: appLocalizations.recovery,\n    );\n    if (res != true) return;\n    globalState.showMessage(\n      title: appLocalizations.recovery,\n      message: TextSpan(text: appLocalizations.recoverySuccess),\n    );\n  }\n\n  Future<void> _handleRecoveryOnLocal(BuildContext context) async {\n    final recoveryOption = await globalState.showCommonDialog<RecoveryOption>(\n      child: const RecoveryOptionsDialog(),\n    );\n    if (recoveryOption == null || !context.mounted) return;\n    _recoveryOnLocal(recoveryOption);\n  }\n\n  void _handleChange(String? value, WidgetRef ref) {\n    if (value == null) {\n      return;\n    }\n    ref\n        .read(appDAVSettingProvider.notifier)\n        .updateState((state) => state?.copyWith(fileName: value));\n  }\n\n  Future<void> _handleUpdateRecoveryStrategy(WidgetRef ref) async {\n    final recoveryStrategy = ref.read(\n      appSettingProvider.select((state) => state.recoveryStrategy),\n    );\n    final res = await globalState.showCommonDialog(\n      child: OptionsDialog<RecoveryStrategy>(\n        title: appLocalizations.recoveryStrategy,\n        options: RecoveryStrategy.values,\n        textBuilder: (mode) => Intl.message('recoveryStrategy_${mode.name}'),\n        value: recoveryStrategy,\n      ),\n    );\n    if (res == null) {\n      return;\n    }\n    ref\n        .read(appSettingProvider.notifier)\n        .updateState((state) => state.copyWith(recoveryStrategy: res));\n  }\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final dav = ref.watch(appDAVSettingProvider);\n    final client = dav != null ? DAVClient(dav) : null;\n    return ListView(\n      children: [\n        ListHeader(title: appLocalizations.remote),\n        if (dav == null)\n          ListItem(\n            leading: const Icon(Icons.account_box),\n            title: Text(appLocalizations.noInfo),\n            subtitle: Text(appLocalizations.pleaseBindWebDAV),\n            trailing: FilledButton.tonal(\n              onPressed: () {\n                _showAddWebDAV(dav);\n              },\n              child: Text(appLocalizations.bind),\n            ),\n          )\n        else ...[\n          ListItem(\n            leading: const Icon(Icons.account_box),\n            title: TooltipText(\n              text: Text(\n                dav.user,\n                maxLines: 1,\n                overflow: TextOverflow.ellipsis,\n              ),\n            ),\n            subtitle: Padding(\n              padding: const EdgeInsets.symmetric(vertical: 4),\n              child: Row(\n                crossAxisAlignment: CrossAxisAlignment.center,\n                children: [\n                  Text(appLocalizations.connectivity),\n                  FutureBuilder<bool>(\n                    future: client!.pingCompleter.future,\n                    builder: (_, snapshot) {\n                      return Center(\n                        child: FadeThroughBox(\n                          child:\n                              snapshot.connectionState != ConnectionState.done\n                              ? const SizedBox(\n                                  width: 12,\n                                  height: 12,\n                                  child: CircularProgressIndicator(\n                                    strokeWidth: 1,\n                                  ),\n                                )\n                              : Container(\n                                  decoration: BoxDecoration(\n                                    shape: BoxShape.circle,\n                                    color: snapshot.data == true\n                                        ? Colors.green\n                                        : Colors.red,\n                                  ),\n                                  width: 12,\n                                  height: 12,\n                                ),\n                        ),\n                      );\n                    },\n                  ),\n                ],\n              ),\n            ),\n            trailing: FilledButton.tonal(\n              onPressed: () {\n                _showAddWebDAV(dav);\n              },\n              child: Text(appLocalizations.edit),\n            ),\n          ),\n          const SizedBox(height: 4),\n          ListItem.input(\n            title: Text(appLocalizations.file),\n            subtitle: Text(dav.fileName),\n            delegate: InputDelegate(\n              title: appLocalizations.file,\n              value: dav.fileName,\n              resetValue: defaultDavFileName,\n              onChanged: (value) {\n                _handleChange(value, ref);\n              },\n            ),\n          ),\n          ListItem(\n            onTap: () {\n              _backupOnWebDAV(client);\n            },\n            title: Text(appLocalizations.backup),\n            subtitle: Text(appLocalizations.remoteBackupDesc),\n          ),\n          ListItem(\n            onTap: () {\n              _handleRecoveryOnWebDAV(context, client);\n            },\n            title: Text(appLocalizations.recovery),\n            subtitle: Text(appLocalizations.remoteRecoveryDesc),\n          ),\n        ],\n        ListHeader(title: appLocalizations.local),\n        ListItem(\n          onTap: () {\n            _backupOnLocal(context);\n          },\n          title: Text(appLocalizations.backup),\n          subtitle: Text(appLocalizations.localBackupDesc),\n        ),\n        ListItem(\n          onTap: () {\n            _handleRecoveryOnLocal(context);\n          },\n          title: Text(appLocalizations.recovery),\n          subtitle: Text(appLocalizations.localRecoveryDesc),\n        ),\n        ListHeader(title: appLocalizations.options),\n        Consumer(\n          builder: (_, ref, _) {\n            final recoveryStrategy = ref.watch(\n              appSettingProvider.select((state) => state.recoveryStrategy),\n            );\n            return ListItem(\n              onTap: () {\n                _handleUpdateRecoveryStrategy(ref);\n              },\n              title: Text(appLocalizations.recoveryStrategy),\n              trailing: FilledButton(\n                onPressed: () {\n                  _handleUpdateRecoveryStrategy(ref);\n                },\n                child: Text(\n                  Intl.message('recoveryStrategy_${recoveryStrategy.name}'),\n                ),\n              ),\n            );\n          },\n        ),\n      ],\n    );\n  }\n}\n\nclass RecoveryOptionsDialog extends StatefulWidget {\n  const RecoveryOptionsDialog({super.key});\n\n  @override\n  State<RecoveryOptionsDialog> createState() => _RecoveryOptionsDialogState();\n}\n\nclass _RecoveryOptionsDialogState extends State<RecoveryOptionsDialog> {\n  void _handleOnTab(RecoveryOption? value) {\n    if (value == null) return;\n    Navigator.of(context).pop(value);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return CommonDialog(\n      title: appLocalizations.recovery,\n      padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16),\n      child: Wrap(\n        children: [\n          ListItem(\n            onTap: () {\n              _handleOnTab(RecoveryOption.onlyProfiles);\n            },\n            title: Text(appLocalizations.recoveryProfiles),\n          ),\n          ListItem(\n            onTap: () {\n              _handleOnTab(RecoveryOption.all);\n            },\n            title: Text(appLocalizations.recoveryAll),\n          ),\n        ],\n      ),\n    );\n  }\n}\n\nclass WebDAVFormDialog extends ConsumerStatefulWidget {\n  final DAV? dav;\n\n  const WebDAVFormDialog({super.key, this.dav});\n\n  @override\n  ConsumerState<WebDAVFormDialog> createState() => _WebDAVFormDialogState();\n}\n\nclass _WebDAVFormDialogState extends ConsumerState<WebDAVFormDialog> {\n  late TextEditingController uriController;\n  late TextEditingController userController;\n  late TextEditingController passwordController;\n  final _obscureController = ValueNotifier<bool>(true);\n  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();\n\n  @override\n  void initState() {\n    super.initState();\n    uriController = TextEditingController(text: widget.dav?.uri);\n    userController = TextEditingController(text: widget.dav?.user);\n    passwordController = TextEditingController(text: widget.dav?.password);\n  }\n\n  void _submit() {\n    if (!_formKey.currentState!.validate()) return;\n    ref.read(appDAVSettingProvider.notifier).value = DAV(\n      uri: uriController.text,\n      user: userController.text,\n      password: passwordController.text,\n    );\n    Navigator.pop(context);\n  }\n\n  void _delete() {\n    ref.read(appDAVSettingProvider.notifier).value = null;\n    Navigator.pop(context);\n  }\n\n  @override\n  void dispose() {\n    _obscureController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return CommonDialog(\n      title: appLocalizations.webDAVConfiguration,\n      actions: [\n        if (widget.dav != null)\n          TextButton(onPressed: _delete, child: Text(appLocalizations.delete)),\n        TextButton(onPressed: _submit, child: Text(appLocalizations.save)),\n      ],\n      child: Form(\n        key: _formKey,\n        child: Wrap(\n          runSpacing: 16,\n          children: [\n            TextFormField(\n              controller: uriController,\n              maxLines: 5,\n              minLines: 1,\n              decoration: InputDecoration(\n                prefixIcon: const Icon(Icons.link),\n                border: const OutlineInputBorder(),\n                labelText: appLocalizations.address,\n                helperText: appLocalizations.addressHelp,\n              ),\n              validator: (String? value) {\n                if (value == null || value.isEmpty || !value.isUrl) {\n                  return appLocalizations.addressTip;\n                }\n                return null;\n              },\n            ),\n            TextFormField(\n              controller: userController,\n              decoration: InputDecoration(\n                prefixIcon: const Icon(Icons.account_circle),\n                border: const OutlineInputBorder(),\n                labelText: appLocalizations.account,\n              ),\n              validator: (String? value) {\n                if (value == null || value.isEmpty) {\n                  return appLocalizations.emptyTip(appLocalizations.account);\n                }\n                return null;\n              },\n            ),\n            ValueListenableBuilder(\n              valueListenable: _obscureController,\n              builder: (_, obscure, _) {\n                return TextFormField(\n                  controller: passwordController,\n                  obscureText: obscure,\n                  decoration: InputDecoration(\n                    prefixIcon: const Icon(Icons.password),\n                    border: const OutlineInputBorder(),\n                    suffixIcon: IconButton(\n                      icon: Icon(\n                        obscure ? Icons.visibility : Icons.visibility_off,\n                      ),\n                      onPressed: () {\n                        _obscureController.value = !obscure;\n                      },\n                    ),\n                    labelText: appLocalizations.password,\n                  ),\n                  validator: (String? value) {\n                    if (value == null || value.isEmpty) {\n                      return appLocalizations.emptyTip(\n                        appLocalizations.password,\n                      );\n                    }\n                    return null;\n                  },\n                );\n              },\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/config/config.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/models/clash_config.dart';\nimport 'package:bett_box/models/config.dart';\nimport 'package:bett_box/providers/providers.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/views/config/dns.dart';\nimport 'package:bett_box/views/config/general.dart';\nimport 'package:bett_box/views/config/network.dart';\nimport 'package:bett_box/views/config/ntp.dart';\nimport 'package:bett_box/views/config/sniffer.dart';\nimport 'package:bett_box/views/config/tunnel.dart';\nimport 'package:bett_box/views/config/experimental.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass ConfigView extends StatefulWidget {\n  const ConfigView({super.key});\n\n  @override\n  State<ConfigView> createState() => _ConfigViewState();\n}\n\nclass _ConfigViewState extends State<ConfigView> {\n  @override\n  Widget build(BuildContext context) {\n    List<Widget> items = [\n      ListItem.open(\n        title: Text(appLocalizations.general),\n        subtitle: Text(appLocalizations.generalDesc),\n        leading: const Icon(Icons.build),\n        delegate: OpenDelegate(\n          title: appLocalizations.general,\n          widget: generateListView(generalItems),\n          blur: false,\n        ),\n      ),\n      ListItem.open(\n        title: Text(appLocalizations.network),\n        subtitle: Text(appLocalizations.networkDesc),\n        leading: const Icon(Icons.vpn_key),\n        delegate: OpenDelegate(\n          title: appLocalizations.network,\n          blur: false,\n          actions: [\n            Consumer(\n              builder: (_, ref, _) {\n                return IconButton(\n                  onPressed: () async {\n                    final res = await globalState.showMessage(\n                      title: appLocalizations.reset,\n                      message: TextSpan(text: appLocalizations.resetTip),\n                    );\n                    if (res != true) {\n                      return;\n                    }\n                    ref\n                        .read(vpnSettingProvider.notifier)\n                        .updateState(\n                          (state) => defaultVpnProps.copyWith(\n                            accessControl: state.accessControl,\n                          ),\n                        );\n                    ref\n                        .read(patchClashConfigProvider.notifier)\n                        .updateState(\n                          (state) => state.copyWith(tun: defaultTun),\n                        );\n                  },\n                  tooltip: appLocalizations.reset,\n                  icon: const Icon(Icons.replay),\n                );\n              },\n            ),\n          ],\n          widget: const NetworkListView(),\n        ),\n      ),\n      ListItem.open(\n        title: const Text('DNS'),\n        subtitle: Text(appLocalizations.dnsDesc),\n        leading: const Icon(Icons.dns),\n        delegate: OpenDelegate(\n          title: 'DNS',\n          actions: [\n            Consumer(\n              builder: (_, ref, _) {\n                return IconButton(\n                  onPressed: () async {\n                    final res = await globalState.showMessage(\n                      title: appLocalizations.reset,\n                      message: TextSpan(text: appLocalizations.resetTip),\n                    );\n                    if (res != true) {\n                      return;\n                    }\n                    ref\n                        .read(patchClashConfigProvider.notifier)\n                        .updateState(\n                          (state) => state.copyWith(dns: defaultDns),\n                        );\n                  },\n                  tooltip: appLocalizations.reset,\n                  icon: const Icon(Icons.replay),\n                );\n              },\n            ),\n          ],\n          widget: const DnsListView(),\n          blur: false,\n        ),\n      ),\n      ListItem.open(\n        title: const Text('NTP'),\n        subtitle: Text(appLocalizations.ntpDesc),\n        leading: const Icon(Icons.access_time),\n        delegate: OpenDelegate(\n          title: 'NTP',\n          actions: [\n            Consumer(\n              builder: (_, ref, _) {\n                return IconButton(\n                  onPressed: () async {\n                    final res = await globalState.showMessage(\n                      title: appLocalizations.reset,\n                      message: TextSpan(text: appLocalizations.resetTip),\n                    );\n                    if (res != true) {\n                      return;\n                    }\n                    ref\n                        .read(patchClashConfigProvider.notifier)\n                        .updateState(\n                          (state) => state.copyWith(ntp: defaultNtp),\n                        );\n                  },\n                  tooltip: appLocalizations.reset,\n                  icon: const Icon(Icons.replay),\n                );\n              },\n            ),\n          ],\n          widget: const NtpListView(),\n          blur: false,\n        ),\n      ),\n      ListItem.open(\n        title: const Text('Hosts'),\n        subtitle: Text(appLocalizations.hostsDesc),\n        leading: const Icon(Icons.view_list_outlined),\n        delegate: OpenDelegate(\n          blur: false,\n          title: 'Hosts',\n          widget: Consumer(\n            builder: (_, ref, _) {\n              final hosts = ref.watch(\n                patchClashConfigProvider.select((state) => state.hosts),\n              );\n              return MapInputPage(\n                title: 'Hosts',\n                map: hosts,\n                titleBuilder: (item) => Text(item.key),\n                subtitleBuilder: (item) => Text(item.value),\n                onChange: (value) {\n                  ref\n                      .read(patchClashConfigProvider.notifier)\n                      .updateState((state) => state.copyWith(hosts: value));\n                },\n              );\n            },\n          ),\n        ),\n      ),\n      ListItem.open(\n        title: Text(appLocalizations.sniffer),\n        subtitle: Text(appLocalizations.snifferDesc),\n        leading: const Icon(Icons.radar),\n        delegate: OpenDelegate(\n          title: appLocalizations.sniffer,\n          actions: [\n            Consumer(\n              builder: (_, ref, _) {\n                return IconButton(\n                  onPressed: () async {\n                    final res = await globalState.showMessage(\n                      title: appLocalizations.reset,\n                      message: TextSpan(text: appLocalizations.resetTip),\n                    );\n                    if (res != true) {\n                      return;\n                    }\n                    ref\n                        .read(patchClashConfigProvider.notifier)\n                        .updateState(\n                          (state) => state.copyWith(sniffer: defaultSniffer),\n                        );\n                  },\n                  tooltip: appLocalizations.reset,\n                  icon: const Icon(Icons.replay),\n                );\n              },\n            ),\n          ],\n          widget: const SnifferListView(),\n          blur: false,\n        ),\n      ),\n      ListItem.open(\n        title: Text(appLocalizations.tunnel),\n        subtitle: Text(appLocalizations.tunnelDesc),\n        leading: const Icon(Icons.swap_horiz),\n        delegate: OpenDelegate(\n          title: appLocalizations.tunnel,\n          actions: [\n            Consumer(\n              builder: (_, ref, _) {\n                return IconButton(\n                  onPressed: () async {\n                    final res = await globalState.showMessage(\n                      title: appLocalizations.reset,\n                      message: TextSpan(text: appLocalizations.resetTip),\n                    );\n                    if (res != true) {\n                      return;\n                    }\n                    ref\n                        .read(patchClashConfigProvider.notifier)\n                        .updateState(\n                          (state) => state.copyWith(tunnels: defaultTunnel),\n                        );\n                  },\n                  tooltip: appLocalizations.reset,\n                  icon: const Icon(Icons.replay),\n                );\n              },\n            ),\n          ],\n          widget: const TunnelListView(),\n          blur: false,\n        ),\n      ),\n      ListItem.open(\n        title: Text(appLocalizations.experimental),\n        subtitle: Text(appLocalizations.experimentalDesc),\n        leading: const Icon(Icons.science),\n        delegate: OpenDelegate(\n          title: appLocalizations.experimental,\n          actions: [\n            Consumer(\n              builder: (_, ref, _) {\n                return IconButton(\n                  onPressed: () async {\n                    final res = await globalState.showMessage(\n                      title: appLocalizations.reset,\n                      message: TextSpan(text: appLocalizations.resetTip),\n                    );\n                    if (res != true) {\n                      return;\n                    }\n                    ref\n                        .read(patchClashConfigProvider.notifier)\n                        .updateState(\n                          (state) =>\n                              state.copyWith(experimental: defaultExperimental),\n                        );\n                  },\n                  tooltip: appLocalizations.reset,\n                  icon: const Icon(Icons.replay),\n                );\n              },\n            ),\n          ],\n          widget: const ExperimentalListView(),\n          blur: false,\n        ),\n      ),\n    ];\n    return generateListView(items.separated(const Divider(height: 0)).toList());\n  }\n}\n"
  },
  {
    "path": "lib/views/config/dns.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass OverrideItem extends ConsumerWidget {\n  const OverrideItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final override = ref.watch(overrideDnsProvider);\n    return ListItem.switchItem(\n      title: Text(appLocalizations.overrideDns),\n      subtitle: Text(appLocalizations.overrideDnsDesc),\n      delegate: SwitchDelegate(\n        value: override,\n        onChanged: (bool value) async {\n          ref.read(overrideDnsProvider.notifier).value = value;\n        },\n      ),\n    );\n  }\n}\n\nclass StatusItem extends ConsumerWidget {\n  const StatusItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final enable = ref.watch(\n      patchClashConfigProvider.select((state) => state.dns.enable),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.status),\n      subtitle: Text(appLocalizations.statusDesc),\n      delegate: SwitchDelegate(\n        value: enable,\n        onChanged: (bool value) async {\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith.dns(enable: value));\n        },\n      ),\n    );\n  }\n}\n\nclass ListenItem extends ConsumerWidget {\n  const ListenItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final listen = ref.watch(\n      patchClashConfigProvider.select((state) => state.dns.listen),\n    );\n    return ListItem.input(\n      title: Text(appLocalizations.listen),\n      subtitle: Text(listen),\n      delegate: InputDelegate(\n        title: appLocalizations.listen,\n        value: listen,\n        validator: (value) {\n          if (value == null || value.isEmpty) {\n            return appLocalizations.emptyTip(appLocalizations.listen);\n          }\n          return null;\n        },\n        onChanged: (String? value) {\n          if (value == null) {\n            return;\n          }\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith.dns(listen: value));\n        },\n      ),\n    );\n  }\n}\n\nclass PreferH3Item extends ConsumerWidget {\n  const PreferH3Item({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final preferH3 = ref.watch(\n      patchClashConfigProvider.select((state) => state.dns.preferH3),\n    );\n    return ListItem.switchItem(\n      title: const Text('PreferH3'),\n      subtitle: Text(appLocalizations.preferH3Desc),\n      delegate: SwitchDelegate(\n        value: preferH3,\n        onChanged: (bool value) async {\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith.dns(preferH3: value));\n        },\n      ),\n    );\n  }\n}\n\nclass IPv6Item extends ConsumerWidget {\n  const IPv6Item({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final ipv6 = ref.watch(\n      patchClashConfigProvider.select((state) => state.dns.ipv6),\n    );\n    return ListItem.switchItem(\n      title: const Text('IPv6'),\n      delegate: SwitchDelegate(\n        value: ipv6,\n        onChanged: (bool value) async {\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith.dns(ipv6: value));\n        },\n      ),\n    );\n  }\n}\n\nclass RespectRulesItem extends ConsumerWidget {\n  const RespectRulesItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final respectRules = ref.watch(\n      patchClashConfigProvider.select((state) => state.dns.respectRules),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.respectRules),\n      subtitle: Text(appLocalizations.respectRulesDesc),\n      delegate: SwitchDelegate(\n        value: respectRules,\n        onChanged: (bool value) async {\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith.dns(respectRules: value));\n        },\n      ),\n    );\n  }\n}\n\nclass DnsModeItem extends ConsumerWidget {\n  const DnsModeItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final enhancedMode = ref.watch(\n      patchClashConfigProvider.select((state) => state.dns.enhancedMode),\n    );\n    return ListItem<DnsMode>.options(\n      title: Text(appLocalizations.dnsMode),\n      subtitle: Text(enhancedMode.name),\n      delegate: OptionsDelegate(\n        title: appLocalizations.dnsMode,\n        options: DnsMode.values,\n        onChanged: (value) {\n          if (value == null) {\n            return;\n          }\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith.dns(enhancedMode: value));\n        },\n        textBuilder: (dnsMode) => dnsMode.name,\n        value: enhancedMode,\n      ),\n    );\n  }\n}\n\nclass FakeIpRangeItem extends ConsumerWidget {\n  const FakeIpRangeItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final fakeIpRange = ref.watch(\n      patchClashConfigProvider.select((state) => state.dns.fakeIpRange),\n    );\n    return ListItem.input(\n      title: Text(appLocalizations.fakeipRange),\n      subtitle: Text(fakeIpRange),\n      delegate: InputDelegate(\n        title: appLocalizations.fakeipRange,\n        value: fakeIpRange,\n        validator: (value) {\n          if (value == null || value.isEmpty) {\n            return appLocalizations.emptyTip(appLocalizations.fakeipRange);\n          }\n          return null;\n        },\n        onChanged: (String? value) {\n          if (value == null) {\n            return;\n          }\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith.dns(fakeIpRange: value));\n        },\n      ),\n    );\n  }\n}\n\nclass FakeIpRangeV6Item extends ConsumerWidget {\n  const FakeIpRangeV6Item({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final fakeIpRangeV6 = ref.watch(\n      patchClashConfigProvider.select((state) => state.dns.fakeIpRangeV6),\n    );\n    return ListItem.input(\n      title: Text(appLocalizations.fakeipRangeV6),\n      subtitle: Text(fakeIpRangeV6),\n      delegate: InputDelegate(\n        title: appLocalizations.fakeipRangeV6,\n        value: fakeIpRangeV6,\n        validator: (value) {\n          if (value == null || value.isEmpty) {\n            return appLocalizations.emptyTip(appLocalizations.fakeipRangeV6);\n          }\n          return null;\n        },\n        onChanged: (String? value) {\n          if (value == null) {\n            return;\n          }\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith.dns(fakeIpRangeV6: value));\n        },\n      ),\n    );\n  }\n}\n\nclass FakeIpFilterModeItem extends ConsumerWidget {\n  const FakeIpFilterModeItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final fakeIpFilterMode = ref.watch(\n      patchClashConfigProvider.select((state) => state.dns.fakeIpFilterMode),\n    );\n\n    return ListItem<FilterMode>.options(\n      title: Text(appLocalizations.fakeIpFilterMode),\n      delegate: OptionsDelegate(\n        title: appLocalizations.fakeIpFilterMode,\n        // Show only blacklist and whitelist, remove rule mode\n        options: const [FilterMode.blacklist, FilterMode.whitelist],\n        onChanged: (value) {\n          if (value == null) {\n            return;\n          }\n\n          // Switch mode directly, no default value\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState(\n                (state) => state.copyWith.dns(fakeIpFilterMode: value),\n              );\n        },\n        textBuilder: (filterMode) {\n          return switch (filterMode) {\n            FilterMode.blacklist => appLocalizations.blacklist,\n            FilterMode.whitelist => appLocalizations.whitelist,\n            FilterMode.rule => appLocalizations.rule, // Keep for config reading\n          };\n        },\n        value: fakeIpFilterMode,\n      ),\n    );\n  }\n}\n\nclass FakeIpFilterItem extends StatelessWidget {\n  const FakeIpFilterItem({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return ListItem.open(\n      title: Text(appLocalizations.fakeipFilter),\n      delegate: OpenDelegate(\n        blur: false,\n        title: appLocalizations.fakeipFilter,\n        widget: Consumer(\n          builder: (_, ref, _) {\n            final fakeIpFilter = ref.watch(\n              patchClashConfigProvider.select(\n                (state) => state.dns.fakeIpFilter,\n              ),\n            );\n\n            // Use ListInputPage (list form)\n            // Note: rule mode UI no longer supported, all modes use simple list\n            return ListInputPage(\n              title: appLocalizations.fakeipFilter,\n              items: fakeIpFilter,\n              titleBuilder: (item) => Text(item),\n              onChange: (items) {\n                ref\n                    .read(patchClashConfigProvider.notifier)\n                    .updateState(\n                      (state) =>\n                          state.copyWith.dns(fakeIpFilter: List.from(items)),\n                    );\n              },\n            );\n          },\n        ),\n      ),\n    );\n  }\n}\n\nclass FakeIpTtlItem extends ConsumerWidget {\n  const FakeIpTtlItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final fakeIpTtl = ref.watch(\n      patchClashConfigProvider.select((state) => state.dns.fakeIpTtl),\n    );\n    return ListItem.input(\n      title: Text(appLocalizations.fakeipTtl),\n      subtitle: Text(fakeIpTtl.toString()),\n      delegate: InputDelegate(\n        title: appLocalizations.fakeipTtl,\n        value: fakeIpTtl.toString(),\n        validator: (value) {\n          if (value == null || value.isEmpty) {\n            return appLocalizations.emptyTip(appLocalizations.fakeipTtl);\n          }\n          final intValue = int.tryParse(value);\n          if (intValue == null) {\n            return appLocalizations.numberTip(appLocalizations.fakeipTtl);\n          }\n          return null;\n        },\n        onChanged: (String? value) {\n          if (value == null) {\n            return;\n          }\n          final intValue = int.tryParse(value);\n          if (intValue == null) {\n            return;\n          }\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith.dns(fakeIpTtl: intValue));\n        },\n      ),\n    );\n  }\n}\n\nclass DefaultNameserverItem extends StatelessWidget {\n  const DefaultNameserverItem({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return ListItem.open(\n      title: Text(appLocalizations.defaultNameserver),\n      subtitle: Text(appLocalizations.defaultNameserverDesc),\n      delegate: OpenDelegate(\n        blur: false,\n        title: appLocalizations.defaultNameserver,\n        widget: Consumer(\n          builder: (_, ref, _) {\n            final defaultNameserver = ref.watch(\n              patchClashConfigProvider.select(\n                (state) => state.dns.defaultNameserver,\n              ),\n            );\n            return ListInputPage(\n              title: appLocalizations.defaultNameserver,\n              items: defaultNameserver,\n              titleBuilder: (item) => Text(item),\n              onChange: (items) {\n                ref\n                    .read(patchClashConfigProvider.notifier)\n                    .updateState(\n                      (state) => state.copyWith.dns(\n                        defaultNameserver: List.from(items),\n                      ),\n                    );\n              },\n            );\n          },\n        ),\n      ),\n    );\n  }\n}\n\nclass NameserverItem extends StatelessWidget {\n  const NameserverItem({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return ListItem.open(\n      title: Text(appLocalizations.nameserver),\n      subtitle: Text(appLocalizations.nameserverDesc),\n      delegate: OpenDelegate(\n        title: appLocalizations.nameserver,\n        blur: false,\n        widget: Consumer(\n          builder: (_, ref, _) {\n            final nameserver = ref.watch(\n              patchClashConfigProvider.select((state) => state.dns.nameserver),\n            );\n            return ListInputPage(\n              title: appLocalizations.nameserver,\n              items: nameserver,\n              titleBuilder: (item) => Text(item),\n              onChange: (items) {\n                ref\n                    .read(patchClashConfigProvider.notifier)\n                    .updateState(\n                      (state) =>\n                          state.copyWith.dns(nameserver: List.from(items)),\n                    );\n              },\n            );\n          },\n        ),\n      ),\n    );\n  }\n}\n\nclass CacheAlgorithmItem extends ConsumerWidget {\n  const CacheAlgorithmItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final cacheAlgorithm = ref.watch(\n      patchClashConfigProvider.select((state) => state.dns.cacheAlgorithm),\n    );\n    return ListItem<CacheAlgorithm>.options(\n      title: Text(appLocalizations.cacheAlgorithm),\n      subtitle: Text(cacheAlgorithm.name),\n      delegate: OptionsDelegate(\n        title: appLocalizations.cacheAlgorithm,\n        options: CacheAlgorithm.values,\n        onChanged: (value) {\n          if (value == null) {\n            return;\n          }\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState(\n                (state) => state.copyWith.dns(cacheAlgorithm: value),\n              );\n        },\n        textBuilder: (algorithm) => algorithm.name,\n        value: cacheAlgorithm,\n      ),\n    );\n  }\n}\n\nclass UseHostsItem extends ConsumerWidget {\n  const UseHostsItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final useHosts = ref.watch(\n      patchClashConfigProvider.select((state) => state.dns.useHosts),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.useHosts),\n      delegate: SwitchDelegate(\n        value: useHosts,\n        onChanged: (bool value) async {\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith.dns(useHosts: value));\n        },\n      ),\n    );\n  }\n}\n\nclass UseSystemHostsItem extends ConsumerWidget {\n  const UseSystemHostsItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final useSystemHosts = ref.watch(\n      patchClashConfigProvider.select((state) => state.dns.useSystemHosts),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.useSystemHosts),\n      delegate: SwitchDelegate(\n        value: useSystemHosts,\n        onChanged: (bool value) async {\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState(\n                (state) => state.copyWith.dns(useSystemHosts: value),\n              );\n        },\n      ),\n    );\n  }\n}\n\nclass NameserverPolicyItem extends StatelessWidget {\n  const NameserverPolicyItem({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return ListItem.open(\n      title: Text(appLocalizations.nameserverPolicy),\n      subtitle: Text(appLocalizations.nameserverPolicyDesc),\n      delegate: OpenDelegate(\n        blur: false,\n        title: appLocalizations.nameserverPolicy,\n        widget: Consumer(\n          builder: (_, ref, _) {\n            final nameserverPolicy = ref.watch(\n              patchClashConfigProvider.select(\n                (state) => state.dns.nameserverPolicy,\n              ),\n            );\n            return MapInputPage(\n              title: appLocalizations.nameserverPolicy,\n              map: nameserverPolicy,\n              titleBuilder: (item) => Text(item.key),\n              subtitleBuilder: (item) => Text(item.value),\n              onChange: (value) {\n                ref\n                    .read(patchClashConfigProvider.notifier)\n                    .updateState(\n                      (state) => state.copyWith.dns(nameserverPolicy: value),\n                    );\n              },\n            );\n          },\n        ),\n      ),\n    );\n  }\n}\n\nclass ProxyServerNameserverItem extends StatelessWidget {\n  const ProxyServerNameserverItem({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return ListItem.open(\n      title: Text(appLocalizations.proxyNameserver),\n      subtitle: Text(appLocalizations.proxyNameserverDesc),\n      delegate: OpenDelegate(\n        blur: false,\n        title: appLocalizations.proxyNameserver,\n        widget: Consumer(\n          builder: (_, ref, _) {\n            final proxyServerNameserver = ref.watch(\n              patchClashConfigProvider.select(\n                (state) => state.dns.proxyServerNameserver,\n              ),\n            );\n            return ListInputPage(\n              title: appLocalizations.proxyNameserver,\n              items: proxyServerNameserver,\n              titleBuilder: (item) => Text(item),\n              onChange: (items) {\n                ref\n                    .read(patchClashConfigProvider.notifier)\n                    .updateState(\n                      (state) => state.copyWith.dns(\n                        proxyServerNameserver: List.from(items),\n                      ),\n                    );\n              },\n            );\n          },\n        ),\n      ),\n    );\n  }\n}\n\nclass DirectNameserverItem extends StatelessWidget {\n  const DirectNameserverItem({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return ListItem.open(\n      title: Text(appLocalizations.directNameserver),\n      subtitle: Text(appLocalizations.directNameserverDesc),\n      delegate: OpenDelegate(\n        blur: false,\n        title: appLocalizations.directNameserver,\n        widget: Consumer(\n          builder: (_, ref, _) {\n            final directNameserver = ref.watch(\n              patchClashConfigProvider.select(\n                (state) => state.dns.directNameserver,\n              ),\n            );\n            return ListInputPage(\n              title: appLocalizations.directNameserver,\n              items: directNameserver,\n              titleBuilder: (item) => Text(item),\n              onChange: (items) {\n                ref\n                    .read(patchClashConfigProvider.notifier)\n                    .updateState(\n                      (state) => state.copyWith.dns(\n                        directNameserver: List.from(items),\n                      ),\n                    );\n              },\n            );\n          },\n        ),\n      ),\n    );\n  }\n}\n\nclass DirectNameserverFollowPolicyItem extends ConsumerWidget {\n  const DirectNameserverFollowPolicyItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final directNameserver = ref.watch(\n      patchClashConfigProvider.select((state) => state.dns.directNameserver),\n    );\n    final directNameserverFollowPolicy = ref.watch(\n      patchClashConfigProvider.select(\n        (state) => state.dns.directNameserverFollowPolicy,\n      ),\n    );\n\n    // Show only if user set direct nameserver\n    if (directNameserver.isEmpty) {\n      return const SizedBox.shrink();\n    }\n\n    return ListItem.switchItem(\n      title: Text(appLocalizations.directNameserverFollowPolicy),\n      delegate: SwitchDelegate(\n        value: directNameserverFollowPolicy,\n        onChanged: (bool value) async {\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState(\n                (state) =>\n                    state.copyWith.dns(directNameserverFollowPolicy: value),\n              );\n        },\n      ),\n    );\n  }\n}\n\nclass FallbackItem extends StatelessWidget {\n  const FallbackItem({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return ListItem.open(\n      title: Text(appLocalizations.fallback),\n      subtitle: Text(appLocalizations.fallbackDesc),\n      delegate: OpenDelegate(\n        blur: false,\n        title: appLocalizations.fallback,\n        widget: Consumer(\n          builder: (_, ref, _) {\n            final fallback = ref.watch(\n              patchClashConfigProvider.select((state) => state.dns.fallback),\n            );\n            return ListInputPage(\n              title: appLocalizations.fallback,\n              items: fallback,\n              titleBuilder: (item) => Text(item),\n              onChange: (items) {\n                ref\n                    .read(patchClashConfigProvider.notifier)\n                    .updateState(\n                      (state) => state.copyWith.dns(fallback: List.from(items)),\n                    );\n              },\n            );\n          },\n        ),\n      ),\n    );\n  }\n}\n\nclass GeoipItem extends ConsumerWidget {\n  const GeoipItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final geoip = ref.watch(\n      patchClashConfigProvider.select(\n        (state) => state.dns.fallbackFilter.geoip,\n      ),\n    );\n    return ListItem.switchItem(\n      title: const Text('Geoip'),\n      delegate: SwitchDelegate(\n        value: geoip,\n        onChanged: (bool value) async {\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState(\n                (state) => state.copyWith.dns.fallbackFilter(geoip: value),\n              );\n        },\n      ),\n    );\n  }\n}\n\nclass GeoipCodeItem extends ConsumerWidget {\n  const GeoipCodeItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final geoipCode = ref.watch(\n      patchClashConfigProvider.select(\n        (state) => state.dns.fallbackFilter.geoipCode,\n      ),\n    );\n    return ListItem.input(\n      title: Text(appLocalizations.geoipCode),\n      subtitle: Text(geoipCode),\n      delegate: InputDelegate(\n        title: appLocalizations.geoipCode,\n        value: geoipCode,\n        validator: (value) {\n          if (value == null || value.isEmpty) {\n            return appLocalizations.emptyTip(appLocalizations.geoipCode);\n          }\n          return null;\n        },\n        onChanged: (String? value) {\n          if (value == null) {\n            return;\n          }\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState(\n                (state) => state.copyWith.dns.fallbackFilter(geoipCode: value),\n              );\n        },\n      ),\n    );\n  }\n}\n\nclass GeositeItem extends StatelessWidget {\n  const GeositeItem({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return ListItem.open(\n      title: const Text('Geosite'),\n      delegate: OpenDelegate(\n        blur: false,\n        title: 'Geosite',\n        widget: Consumer(\n          builder: (_, ref, _) {\n            final geosite = ref.watch(\n              patchClashConfigProvider.select(\n                (state) => state.dns.fallbackFilter.geosite,\n              ),\n            );\n            return ListInputPage(\n              title: 'Geosite',\n              items: geosite,\n              titleBuilder: (item) => Text(item),\n              onChange: (items) {\n                ref\n                    .read(patchClashConfigProvider.notifier)\n                    .updateState(\n                      (state) => state.copyWith.dns.fallbackFilter(\n                        geosite: List.from(items),\n                      ),\n                    );\n              },\n            );\n          },\n        ),\n      ),\n    );\n  }\n}\n\nclass IpcidrItem extends StatelessWidget {\n  const IpcidrItem({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return ListItem.open(\n      title: Text(appLocalizations.ipcidr),\n      delegate: OpenDelegate(\n        blur: false,\n        title: appLocalizations.ipcidr,\n        widget: Consumer(\n          builder: (_, ref, _) {\n            final ipcidr = ref.watch(\n              patchClashConfigProvider.select(\n                (state) => state.dns.fallbackFilter.ipcidr,\n              ),\n            );\n            return ListInputPage(\n              title: appLocalizations.ipcidr,\n              items: ipcidr,\n              titleBuilder: (item) => Text(item),\n              onChange: (items) {\n                ref\n                    .read(patchClashConfigProvider.notifier)\n                    .updateState(\n                      (state) => state.copyWith.dns.fallbackFilter(\n                        ipcidr: List.from(items),\n                      ),\n                    );\n              },\n            );\n          },\n        ),\n      ),\n    );\n  }\n}\n\nclass DomainItem extends StatelessWidget {\n  const DomainItem({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return ListItem.open(\n      title: Text(appLocalizations.domain),\n      delegate: OpenDelegate(\n        blur: false,\n        title: appLocalizations.domain,\n        widget: Consumer(\n          builder: (_, ref, _) {\n            final domain = ref.watch(\n              patchClashConfigProvider.select(\n                (state) => state.dns.fallbackFilter.domain,\n              ),\n            );\n            return ListInputPage(\n              title: appLocalizations.domain,\n              items: domain,\n              titleBuilder: (item) => Text(item),\n              onChange: (items) {\n                ref\n                    .read(patchClashConfigProvider.notifier)\n                    .updateState(\n                      (state) => state.copyWith.dns.fallbackFilter(\n                        domain: List.from(items),\n                      ),\n                    );\n              },\n            );\n          },\n        ),\n      ),\n    );\n  }\n}\n\nclass DnsOptions extends StatelessWidget {\n  const DnsOptions({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return Column(\n      children: generateSection(\n        title: appLocalizations.options,\n        items: [\n          const StatusItem(),\n          const ListenItem(),\n          const CacheAlgorithmItem(),\n          const UseHostsItem(),\n          const UseSystemHostsItem(),\n          const IPv6Item(),\n          const RespectRulesItem(),\n          const PreferH3Item(),\n          const DnsModeItem(),\n          const FakeIpRangeItem(),\n          const FakeIpRangeV6Item(),\n          const FakeIpFilterModeItem(),\n          const FakeIpFilterItem(),\n          const FakeIpTtlItem(),\n          const DefaultNameserverItem(),\n          const NameserverPolicyItem(),\n          const NameserverItem(),\n          const FallbackItem(),\n          const ProxyServerNameserverItem(),\n          const DirectNameserverItem(),\n          const DirectNameserverFollowPolicyItem(),\n        ],\n      ),\n    );\n  }\n}\n\nclass FallbackFilterOptions extends StatelessWidget {\n  const FallbackFilterOptions({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return Column(\n      children: generateSection(\n        title: appLocalizations.fallbackFilter,\n        items: [\n          const GeoipItem(),\n          const GeoipCodeItem(),\n          const GeositeItem(),\n          const IpcidrItem(),\n          const DomainItem(),\n        ],\n      ),\n    );\n  }\n}\n\nconst dnsItems = <Widget>[\n  OverrideItem(),\n  DnsOptions(),\n  FallbackFilterOptions(),\n];\n\nclass DnsListView extends ConsumerWidget {\n  const DnsListView({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    return generateListView(dnsItems);\n  }\n}\n"
  },
  {
    "path": "lib/views/config/experimental.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass OverrideExperimentalItem extends ConsumerWidget {\n  const OverrideExperimentalItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final override = ref.watch(overrideExperimentalProvider);\n    return ListItem.switchItem(\n      title: Text(appLocalizations.overrideExperimental),\n      subtitle: Text(appLocalizations.overrideExperimentalDesc),\n      delegate: SwitchDelegate(\n        value: override,\n        onChanged: (bool value) async {\n          ref.read(overrideExperimentalProvider.notifier).value = value;\n        },\n      ),\n    );\n  }\n}\n\nclass QuicGoDisableGsoItem extends ConsumerWidget {\n  const QuicGoDisableGsoItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final value = ref.watch(\n      patchClashConfigProvider.select(\n        (state) => state.experimental.quicGoDisableGso,\n      ),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.quicGoDisableGso),\n      delegate: SwitchDelegate(\n        value: value,\n        onChanged: (bool newValue) async {\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState(\n                (state) => state.copyWith(\n                  experimental: state.experimental.copyWith(\n                    quicGoDisableGso: newValue,\n                  ),\n                ),\n              );\n        },\n      ),\n    );\n  }\n}\n\nclass QuicGoDisableEcnItem extends ConsumerWidget {\n  const QuicGoDisableEcnItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final value = ref.watch(\n      patchClashConfigProvider.select(\n        (state) => state.experimental.quicGoDisableEcn,\n      ),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.quicGoDisableEcn),\n      delegate: SwitchDelegate(\n        value: value,\n        onChanged: (bool newValue) async {\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState(\n                (state) => state.copyWith(\n                  experimental: state.experimental.copyWith(\n                    quicGoDisableEcn: newValue,\n                  ),\n                ),\n              );\n        },\n      ),\n    );\n  }\n}\n\nclass DialerIp4pConvertItem extends ConsumerWidget {\n  const DialerIp4pConvertItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final value = ref.watch(\n      patchClashConfigProvider.select(\n        (state) => state.experimental.dialerIp4pConvert,\n      ),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.dialerIp4pConvert),\n      delegate: SwitchDelegate(\n        value: value,\n        onChanged: (bool newValue) async {\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState(\n                (state) => state.copyWith(\n                  experimental: state.experimental.copyWith(\n                    dialerIp4pConvert: newValue,\n                  ),\n                ),\n              );\n        },\n      ),\n    );\n  }\n}\n\nclass ExperimentalOptions extends StatelessWidget {\n  const ExperimentalOptions({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return Column(\n      children: generateSection(\n        title: appLocalizations.options,\n        items: [\n          const QuicGoDisableGsoItem(),\n          const QuicGoDisableEcnItem(),\n          const DialerIp4pConvertItem(),\n        ],\n      ),\n    );\n  }\n}\n\nconst experimentalItems = <Widget>[\n  OverrideExperimentalItem(),\n  ExperimentalOptions(),\n];\n\nclass ExperimentalListView extends ConsumerWidget {\n  const ExperimentalListView({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    return generateListView(experimentalItems);\n  }\n}\n"
  },
  {
    "path": "lib/views/config/general.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/providers/providers.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass LogLevelItem extends ConsumerWidget {\n  const LogLevelItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final logLevel = ref.watch(\n      patchClashConfigProvider.select((state) => state.logLevel),\n    );\n    return ListItem<LogLevel>.options(\n      leading: const Icon(Icons.info_outline),\n      title: Text(appLocalizations.logLevel),\n      subtitle: Text(logLevel.name),\n      delegate: OptionsDelegate<LogLevel>(\n        title: appLocalizations.logLevel,\n        options: LogLevel.values,\n        onChanged: (LogLevel? value) {\n          if (value == null) {\n            return;\n          }\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith(logLevel: value));\n        },\n        textBuilder: (logLevel) => logLevel.name,\n        value: logLevel,\n      ),\n    );\n  }\n}\n\nclass UaItem extends ConsumerStatefulWidget {\n  const UaItem({super.key});\n\n  @override\n  ConsumerState<UaItem> createState() => _UaItemState();\n}\n\nclass _UaItemState extends ConsumerState<UaItem> {\n  String _lastCustomUa = '';\n\n  @override\n  Widget build(BuildContext context) {\n    final globalUa = ref.watch(\n      patchClashConfigProvider.select((state) => state.globalUa),\n    );\n    final isCustom = globalUa != null;\n\n    if (isCustom) {\n      _lastCustomUa = globalUa;\n    }\n\n    return ListItem(\n      leading: const Icon(Icons.computer_outlined),\n      title: const Text('UA'),\n      subtitle: Text(isCustom ? appLocalizations.custom : appLocalizations.defaultText),\n      onTap: () async {\n        final notifier = ref.read(patchClashConfigProvider.notifier);\n        final result = await globalState.showCommonDialog<_UaOption>(\n          child: _UaDialog(isCustom: isCustom),\n        );\n\n        if (result == null) return;\n\n        switch (result.type) {\n          case _UaOptionType.default_:\n            notifier.updateState((state) => state.copyWith(globalUa: null));\n          case _UaOptionType.custom:\n            final customUa = await globalState.showCommonDialog<String>(\n              child: InputDialog(\n                title: appLocalizations.custom,\n                value: _lastCustomUa,\n                hintText: 'Clash.Meta',\n                validator: (value) {\n                  if (value == null || value.trim().isEmpty) {\n                    return appLocalizations.emptyTip('UA');\n                  }\n                  return null;\n                },\n              ),\n            );\n            if (customUa != null && customUa.trim().isNotEmpty) {\n              notifier.updateState((state) => state.copyWith(globalUa: customUa.trim()));\n            }\n        }\n      },\n    );\n  }\n}\n\nenum _UaOptionType { default_, custom }\n\nclass _UaOption {\n  final _UaOptionType type;\n\n  const _UaOption(this.type);\n}\n\nclass _UaDialog extends StatelessWidget {\n  final bool isCustom;\n\n  const _UaDialog({required this.isCustom});\n\n  @override\n  Widget build(BuildContext context) {\n    return CommonDialog(\n      title: 'UA',\n      padding: const EdgeInsets.symmetric(vertical: 8),\n      child: Column(\n        mainAxisSize: MainAxisSize.min,\n        children: [\n          // Default option\n          Material(\n            color: Colors.transparent,\n            child: InkWell(\n              onTap: () {\n                Navigator.of(context, rootNavigator: true).pop(const _UaOption(_UaOptionType.default_));\n              },\n              child: Container(\n                padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),\n                child: Row(\n                  children: [\n                    Icon(\n                      !isCustom ? Icons.check_circle_rounded : Icons.circle_outlined,\n                      size: 21,\n                      color: !isCustom\n                          ? context.colorScheme.primary\n                          : context.colorScheme.onSurfaceVariant.withValues(alpha: 0.6),\n                    ),\n                    const SizedBox(width: 12),\n                    Text(\n                      appLocalizations.defaultText,\n                      style: context.textTheme.bodyMedium,\n                    ),\n                  ],\n                ),\n              ),\n            ),\n          ),\n          // Custom option\n          Material(\n            color: Colors.transparent,\n            child: InkWell(\n              onTap: () {\n                Navigator.of(context, rootNavigator: true).pop(const _UaOption(_UaOptionType.custom));\n              },\n              child: Container(\n                padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),\n                child: Row(\n                  children: [\n                    Icon(\n                      isCustom ? Icons.check_circle_rounded : Icons.circle_outlined,\n                      size: 21,\n                      color: isCustom\n                          ? context.colorScheme.primary\n                          : context.colorScheme.onSurfaceVariant.withValues(alpha: 0.6),\n                    ),\n                    const SizedBox(width: 12),\n                    Text(\n                      appLocalizations.custom,\n                      style: context.textTheme.bodyMedium,\n                    ),\n                  ],\n                ),\n              ),\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n}\n\nclass KeepAliveIntervalItem extends ConsumerWidget {\n  const KeepAliveIntervalItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final keepAliveInterval = ref.watch(\n      patchClashConfigProvider.select((state) => state.keepAliveInterval),\n    );\n    return ListItem.input(\n      leading: const Icon(Icons.timer_outlined),\n      title: Text(appLocalizations.keepAliveIntervalDesc),\n      subtitle: Text('$keepAliveInterval ${appLocalizations.seconds}'),\n      delegate: InputDelegate(\n        title: appLocalizations.keepAliveIntervalDesc,\n        suffixText: appLocalizations.seconds,\n        resetValue: '$defaultKeepAliveInterval',\n        value: '$keepAliveInterval',\n        validator: (String? value) {\n          if (value == null || value.isEmpty) {\n            return appLocalizations.emptyTip(appLocalizations.interval);\n          }\n          final intValue = int.tryParse(value);\n          if (intValue == null) {\n            return appLocalizations.numberTip(appLocalizations.interval);\n          }\n          return null;\n        },\n        onChanged: (String? value) {\n          if (value == null) {\n            return;\n          }\n          final intValue = int.parse(value);\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState(\n                (state) => state.copyWith(keepAliveInterval: intValue),\n              );\n        },\n      ),\n    );\n  }\n}\n\nclass TestUrlItem extends ConsumerWidget {\n  const TestUrlItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final testUrl = ref.watch(\n      appSettingProvider.select((state) => state.testUrl),\n    );\n\n    return ListItem(\n      leading: const Icon(Icons.timeline),\n      title: Text(appLocalizations.testUrl),\n      subtitle: SingleChildScrollView(\n        scrollDirection: Axis.horizontal,\n        child: Text(testUrl),\n      ),\n      onTap: () async {\n        await globalState.showCommonDialog(\n          child: _TestUrlDialog(currentUrl: testUrl),\n        );\n      },\n    );\n  }\n}\n\nclass _TestUrlDialog extends ConsumerWidget {\n  final String currentUrl;\n\n  const _TestUrlDialog({required this.currentUrl});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final overrideTestUrl = ref.watch(overrideTestUrlProvider);\n    final isPresetUrl = presetTestUrls.contains(currentUrl);\n\n    return CommonDialog(\n      title: appLocalizations.testUrl,\n      padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16),\n      child: Column(\n        mainAxisSize: MainAxisSize.min,\n        children: [\n          // Override switch\n          Padding(\n            padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),\n            child: Row(\n              mainAxisAlignment: MainAxisAlignment.spaceBetween,\n              children: [\n                Text(\n                  appLocalizations.overrideTestUrl,\n                  style: context.textTheme.bodyMedium,\n                ),\n                Switch(\n                  value: overrideTestUrl,\n                  onChanged: (bool value) async {\n                    ref.read(overrideTestUrlProvider.notifier).value = value;\n                    globalState.appController.applyProfileDebounce(silence: true);\n                  },\n                ),\n              ],\n            ),\n          ),\n          const SizedBox(height: 8),\n          // URL list\n          ...presetTestUrls.map((url) {\n            final isSelected = currentUrl == url;\n            return Material(\n              color: Colors.transparent,\n              child: InkWell(\n                onTap: () async {\n                  ref\n                      .read(appSettingProvider.notifier)\n                      .updateState((state) => state.copyWith(testUrl: url));\n                  if (ref.read(overrideTestUrlProvider)) {\n                    globalState.appController.applyProfileDebounce(silence: true);\n                  }\n                  if (context.mounted) {\n                    Navigator.of(context, rootNavigator: true).pop();\n                  }\n                },\n                child: Container(\n                  padding: const EdgeInsets.symmetric(\n                    horizontal: 16,\n                    vertical: 12,\n                  ),\n                  child: Row(\n                    children: [\n                      Icon(\n                        isSelected\n                            ? Icons.check_circle_rounded\n                            : Icons.circle_outlined,\n                        size: 21,\n                        color: isSelected\n                            ? context.colorScheme.primary\n                            : context.colorScheme.onSurfaceVariant.withValues(\n                                alpha: 0.6,\n                              ),\n                      ),\n                      const SizedBox(width: 12),\n                      Expanded(\n                        child: isSelected\n                            ? SingleChildScrollView(\n                                scrollDirection: Axis.horizontal,\n                                child: Text(\n                                  url,\n                                  style: context.textTheme.bodyMedium,\n                                ),\n                              )\n                            : Text(\n                                url,\n                                style: context.textTheme.bodyMedium,\n                                overflow: TextOverflow.ellipsis,\n                                maxLines: 1,\n                              ),\n                      ),\n                    ],\n                  ),\n                ),\n              ),\n            );\n          }),\n          // Custom URL option\n          Material(\n            color: Colors.transparent,\n            child: InkWell(\n              onTap: () async {\n                final notifier = ref.read(appSettingProvider.notifier);\n                final overrideTestUrl = ref.read(overrideTestUrlProvider);\n                Navigator.of(context, rootNavigator: true).pop();\n                final customUrl = await globalState.showCommonDialog<String>(\n                  child: InputDialog(\n                    title: appLocalizations.customUrl,\n                    value: isPresetUrl ? '' : currentUrl,\n                    resetValue: defaultTestUrl,\n                    validator: (String? inputValue) {\n                      if (inputValue == null || inputValue.isEmpty) {\n                        return appLocalizations.emptyTip(appLocalizations.testUrl);\n                      }\n                      if (!inputValue.isUrl) {\n                        return appLocalizations.urlTip(appLocalizations.testUrl);\n                      }\n                      return null;\n                    },\n                  ),\n                );\n\n                if (customUrl != null) {\n                  notifier.updateState((state) => state.copyWith(testUrl: customUrl));\n                  if (overrideTestUrl) {\n                    globalState.appController.applyProfileDebounce(silence: true);\n                  }\n                }\n              },\n              child: Container(\n                padding: const EdgeInsets.symmetric(\n                  horizontal: 16,\n                  vertical: 12,\n                ),\n                child: Row(\n                  children: [\n                    Icon(\n                      !isPresetUrl\n                          ? Icons.check_circle_rounded\n                          : Icons.circle_outlined,\n                      size: 21,\n                      color: !isPresetUrl\n                          ? context.colorScheme.primary\n                          : context.colorScheme.onSurfaceVariant.withValues(\n                              alpha: 0.6,\n                            ),\n                    ),\n                    const SizedBox(width: 12),\n                    Expanded(\n                      child: Text(\n                        appLocalizations.customUrl,\n                        style: context.textTheme.bodyMedium,\n                      ),\n                    ),\n                  ],\n                ),\n              ),\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n}\n\nclass PortItem extends ConsumerWidget {\n  const PortItem({super.key});\n\n  Future<void> handleShowPortDialog() async {\n    await globalState.showCommonDialog(child: _PortDialog());\n    // inputDelegate.onChanged(value);\n  }\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final mixedPort = ref.watch(\n      patchClashConfigProvider.select((state) => state.mixedPort),\n    );\n    return ListItem(\n      leading: const Icon(Icons.adjust_outlined),\n      title: Text(appLocalizations.port),\n      subtitle: Text('$mixedPort'),\n      onTap: () {\n        handleShowPortDialog();\n      },\n      // delegate: InputDelegate(\n      //   title: appLocalizations.port,\n      //   value: \"$mixedPort\",\n      //   validator: (String? value) {\n      //     if (value == null || value.isEmpty) {\n      //       return appLocalizations.emptyTip(appLocalizations.proxyPort);\n      //     }\n      //     final mixedPort = int.tryParse(value);\n      //     if (mixedPort == null) {\n      //       return appLocalizations.numberTip(appLocalizations.proxyPort);\n      //     }\n      //     if (mixedPort < 1024 || mixedPort > 49151) {\n      //       return appLocalizations.proxyPortTip;\n      //     }\n      //     return null;\n      //   },\n      //   onChanged: (String? value) {\n      //     if (value == null) {\n      //       return;\n      //     }\n      //     final mixedPort = int.parse(value);\n      //     ref.read(patchClashConfigProvider.notifier).updateState(\n      //           (state) => state.copyWith(\n      //             mixedPort: mixedPort,\n      //           ),\n      //         );\n      //   },\n      //   resetValue: \"$defaultMixedPort\",\n      // ),\n    );\n  }\n}\n\nclass Ipv6Item extends ConsumerWidget {\n  const Ipv6Item({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final ipv6 = ref.watch(\n      patchClashConfigProvider.select((state) => state.ipv6),\n    );\n    return ListItem.switchItem(\n      leading: const Icon(Icons.filter_6_rounded),\n      title: const Text('IPv6'),\n      subtitle: Text(appLocalizations.ipv6Desc),\n      delegate: SwitchDelegate(\n        value: ipv6,\n        onChanged: (bool value) async {\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith(ipv6: value));\n        },\n      ),\n    );\n  }\n}\n\nclass AllowLanItem extends ConsumerWidget {\n  const AllowLanItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final allowLan = ref.watch(\n      patchClashConfigProvider.select((state) => state.allowLan),\n    );\n    return ListItem.switchItem(\n      leading: const Icon(Icons.device_hub),\n      title: Text(appLocalizations.allowLan),\n      subtitle: Text(appLocalizations.allowLanDesc),\n      delegate: SwitchDelegate(\n        value: allowLan,\n        onChanged: (bool value) async {\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith(allowLan: value));\n        },\n      ),\n    );\n  }\n}\n\nclass UnifiedDelayItem extends ConsumerWidget {\n  const UnifiedDelayItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final unifiedDelay = ref.watch(\n      patchClashConfigProvider.select((state) => state.unifiedDelay),\n    );\n\n    return ListItem.switchItem(\n      leading: const Icon(Icons.compress_outlined),\n      title: Text(appLocalizations.unifiedDelay),\n      subtitle: Text(appLocalizations.unifiedDelayDesc),\n      delegate: SwitchDelegate(\n        value: unifiedDelay,\n        onChanged: (bool value) async {\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith(unifiedDelay: value));\n        },\n      ),\n    );\n  }\n}\n\nclass FindProcessItem extends ConsumerWidget {\n  const FindProcessItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final findProcess = ref.watch(\n      patchClashConfigProvider.select(\n        (state) => state.findProcessMode == FindProcessMode.always,\n      ),\n    );\n\n    return ListItem.switchItem(\n      leading: const Icon(Icons.polymer_outlined),\n      title: Text(appLocalizations.findProcessMode),\n      subtitle: Text(appLocalizations.findProcessModeDesc),\n      delegate: SwitchDelegate(\n        value: findProcess,\n        onChanged: (bool value) async {\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState(\n                (state) => state.copyWith(\n                  findProcessMode: value\n                      ? FindProcessMode.always\n                      : FindProcessMode.off,\n                ),\n              );\n        },\n      ),\n    );\n  }\n}\n\nclass TcpConcurrentItem extends ConsumerWidget {\n  const TcpConcurrentItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final tcpConcurrent = ref.watch(\n      patchClashConfigProvider.select((state) => state.tcpConcurrent),\n    );\n    return ListItem.switchItem(\n      leading: const Icon(Icons.double_arrow_outlined),\n      title: Text(appLocalizations.tcpConcurrent),\n      subtitle: Text(appLocalizations.tcpConcurrentDesc),\n      delegate: SwitchDelegate(\n        value: tcpConcurrent,\n        onChanged: (value) async {\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith(tcpConcurrent: value));\n        },\n      ),\n    );\n  }\n}\n\nclass GeodataLoaderItem extends ConsumerWidget {\n  const GeodataLoaderItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final isMemconservative = ref.watch(\n      patchClashConfigProvider.select(\n        (state) => state.geodataLoader == GeodataLoader.memconservative,\n      ),\n    );\n    return ListItem.switchItem(\n      leading: const Icon(Icons.memory),\n      title: Text(appLocalizations.geodataLoader),\n      subtitle: Text(appLocalizations.geodataLoaderDesc),\n      delegate: SwitchDelegate(\n        value: isMemconservative,\n        onChanged: (bool value) async {\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState(\n                (state) => state.copyWith(\n                  geodataLoader: value\n                      ? GeodataLoader.memconservative\n                      : GeodataLoader.standard,\n                ),\n              );\n        },\n      ),\n    );\n  }\n}\n\nclass ExternalControllerItem extends ConsumerWidget {\n  const ExternalControllerItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final hasExternalController = ref.watch(\n      patchClashConfigProvider.select(\n        (state) => state.externalController == ExternalControllerStatus.open,\n      ),\n    );\n    return ListItem.switchItem(\n      leading: const Icon(Icons.api_outlined),\n      title: Text(appLocalizations.externalController),\n      subtitle: Text(appLocalizations.externalControllerDesc),\n      delegate: SwitchDelegate(\n        value: hasExternalController,\n        onChanged: (bool value) async {\n          if (value) {\n            // Auto-generate 8-digit password when enabling external controller\n            final newSecret = utils.generateSecret();\n            ref\n                .read(patchClashConfigProvider.notifier)\n                .updateState(\n                  (state) => state.copyWith(\n                    externalController: ExternalControllerStatus.open,\n                    secret: newSecret,\n                  ),\n                );\n            // Apply config\n            await globalState.appController.applyProfile();\n          } else {\n            // Disable external controller\n            ref\n                .read(patchClashConfigProvider.notifier)\n                .updateState(\n                  (state) => state.copyWith(\n                    externalController: ExternalControllerStatus.close,\n                  ),\n                );\n            // Apply config\n            await globalState.appController.applyProfile();\n          }\n        },\n      ),\n    );\n  }\n}\n\nclass ControlSecretItem extends ConsumerWidget {\n  const ControlSecretItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final hasExternalController = ref.watch(\n      patchClashConfigProvider.select(\n        (state) => state.externalController == ExternalControllerStatus.open,\n      ),\n    );\n\n    // Only show when external controller is enabled\n    if (!hasExternalController) {\n      return const SizedBox.shrink();\n    }\n\n    final secret =\n        ref.watch(patchClashConfigProvider.select((state) => state.secret)) ??\n        '';\n\n    return ListItem(\n      leading: const Icon(Icons.password_outlined),\n      title: Text(appLocalizations.controlSecret),\n      subtitle: Text(\n        secret.isEmpty ? appLocalizations.controlSecretDesc : secret,\n      ),\n      trailing: secret.isNotEmpty\n          ? IconButton(\n              icon: const Icon(Icons.copy),\n              tooltip: appLocalizations.copy,\n              onPressed: () {\n                Clipboard.setData(ClipboardData(text: secret));\n                if (context.mounted) {\n                  context.showSnackBar(appLocalizations.secretCopied);\n                }\n              },\n            )\n          : null,\n      onTap: () async {\n        await globalState.showCommonDialog(\n          child: _SecretDialog(currentSecret: secret),\n        );\n      },\n    );\n  }\n}\n\nclass _SecretDialog extends ConsumerStatefulWidget {\n  final String currentSecret;\n\n  const _SecretDialog({required this.currentSecret});\n\n  @override\n  ConsumerState<_SecretDialog> createState() => _SecretDialogState();\n}\n\nclass _SecretDialogState extends ConsumerState<_SecretDialog> {\n  late TextEditingController _controller;\n  final _formKey = GlobalKey<FormState>();\n\n  @override\n  void initState() {\n    super.initState();\n    _controller = TextEditingController(text: widget.currentSecret);\n  }\n\n  @override\n  void dispose() {\n    _controller.dispose();\n    super.dispose();\n  }\n\n  Future<void> _handleSave() async {\n    if (_formKey.currentState?.validate() == false) return;\n    ref\n        .read(patchClashConfigProvider.notifier)\n        .updateState((state) => state.copyWith(secret: _controller.text));\n    Navigator.of(context).pop();\n    // Apply config after manual password change\n    await globalState.appController.applyProfile();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return CommonDialog(\n      title: appLocalizations.controlSecret,\n      actions: [\n        TextButton(onPressed: _handleSave, child: Text(appLocalizations.save)),\n      ],\n      child: Form(\n        key: _formKey,\n        child: Padding(\n          padding: const EdgeInsets.only(top: 8),\n          child: TextFormField(\n            controller: _controller,\n            decoration: InputDecoration(\n              border: const OutlineInputBorder(),\n              labelText: appLocalizations.controlSecret,\n              hintText: appLocalizations.controlSecretDesc,\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n\nfinal generalItems = <Widget>[\n  LogLevelItem(),\n  UaItem(),\n  if (system.isDesktop) KeepAliveIntervalItem(),\n  TestUrlItem(),\n  PortItem(),\n  Ipv6Item(),\n  AllowLanItem(),\n  UnifiedDelayItem(),\n  FindProcessItem(),\n  TcpConcurrentItem(),\n  GeodataLoaderItem(),\n  ExternalControllerItem(),\n  ControlSecretItem(),\n].separated(const Divider(height: 0)).toList();\n\nclass _PortDialog extends ConsumerStatefulWidget {\n  const _PortDialog();\n\n  @override\n  ConsumerState<_PortDialog> createState() => _PortDialogState();\n}\n\nclass _PortDialogState extends ConsumerState<_PortDialog> {\n  final _formKey = GlobalKey<FormState>();\n  bool _isMore = false;\n\n  late TextEditingController _mixedPortController;\n  late TextEditingController _portController;\n  late TextEditingController _socksPortController;\n  late TextEditingController _redirPortController;\n  late TextEditingController _tProxyPortController;\n\n  @override\n  void initState() {\n    super.initState();\n    final vm5 = ref.read(\n      patchClashConfigProvider.select((state) {\n        return VM5(\n          a: state.mixedPort,\n          b: state.port,\n          c: state.socksPort,\n          d: state.redirPort,\n          e: state.tproxyPort,\n        );\n      }),\n    );\n    _mixedPortController = TextEditingController(text: vm5.a.toString());\n    _portController = TextEditingController(text: vm5.b.toString());\n    _socksPortController = TextEditingController(text: vm5.c.toString());\n    _redirPortController = TextEditingController(text: vm5.d.toString());\n    _tProxyPortController = TextEditingController(text: vm5.e.toString());\n  }\n\n  Future<void> _handleReset() async {\n    final res = await globalState.showMessage(\n      message: TextSpan(text: appLocalizations.resetTip),\n    );\n    if (res != true) {\n      return;\n    }\n    ref\n        .read(patchClashConfigProvider.notifier)\n        .updateState(\n          (state) => state.copyWith(\n            mixedPort: 7890,\n            port: 0,\n            socksPort: 0,\n            redirPort: 0,\n            tproxyPort: 0,\n          ),\n        );\n    if (mounted) {\n      Navigator.of(context).pop();\n    }\n  }\n\n  void _handleUpdate() {\n    if (_formKey.currentState?.validate() == false) return;\n    ref\n        .read(patchClashConfigProvider.notifier)\n        .updateState(\n          (state) => state.copyWith(\n            mixedPort: int.parse(_mixedPortController.text),\n            port: int.parse(_portController.text),\n            socksPort: int.parse(_socksPortController.text),\n            redirPort: int.parse(_redirPortController.text),\n            tproxyPort: int.parse(_tProxyPortController.text),\n          ),\n        );\n    Navigator.of(context).pop();\n  }\n\n  void _handleMore() {\n    setState(() {\n      _isMore = !_isMore;\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return CommonDialog(\n      title: appLocalizations.port,\n      actions: [\n        Row(\n          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n          children: [\n            IconButton.filledTonal(\n              onPressed: _handleMore,\n              icon: CommonExpandIcon(expand: _isMore),\n            ),\n            Row(\n              children: [\n                TextButton(\n                  onPressed: _handleReset,\n                  child: Text(appLocalizations.reset),\n                ),\n                const SizedBox(width: 4),\n                TextButton(\n                  onPressed: _handleUpdate,\n                  child: Text(appLocalizations.submit),\n                ),\n              ],\n            ),\n          ],\n        ),\n      ],\n      child: Form(\n        autovalidateMode: AutovalidateMode.onUserInteraction,\n        key: _formKey,\n        child: Padding(\n          padding: EdgeInsets.only(top: 8),\n          child: AnimatedSize(\n            duration: midDuration,\n            curve: Curves.easeOutQuad,\n            alignment: Alignment.topCenter,\n            child: Column(\n              spacing: 24,\n              children: [\n                TextFormField(\n                  keyboardType: TextInputType.url,\n                  maxLines: 1,\n                  minLines: 1,\n                  controller: _mixedPortController,\n                  onFieldSubmitted: (_) {\n                    _handleUpdate();\n                  },\n                  decoration: InputDecoration(\n                    border: const OutlineInputBorder(),\n                    labelText: appLocalizations.mixedPort,\n                  ),\n                  validator: (value) {\n                    if (value == null || value.isEmpty) {\n                      return appLocalizations.emptyTip(\n                        appLocalizations.mixedPort,\n                      );\n                    }\n                    final port = int.tryParse(value);\n                    if (port == null) {\n                      return appLocalizations.numberTip(\n                        appLocalizations.mixedPort,\n                      );\n                    }\n                    if (port < 1024 || port > 49151) {\n                      return appLocalizations.portTip(\n                        appLocalizations.mixedPort,\n                      );\n                    }\n                    final ports = [\n                      _portController.text,\n                      _socksPortController.text,\n                      _tProxyPortController.text,\n                      _redirPortController.text,\n                    ].map((item) => item.trim());\n                    if (ports.contains(value.trim())) {\n                      return appLocalizations.portConflictTip;\n                    }\n                    return null;\n                  },\n                ),\n                if (_isMore) ...[\n                  TextFormField(\n                    keyboardType: TextInputType.url,\n                    maxLines: 1,\n                    minLines: 1,\n                    controller: _portController,\n                    onFieldSubmitted: (_) {\n                      _handleUpdate();\n                    },\n                    decoration: InputDecoration(\n                      border: const OutlineInputBorder(),\n                      labelText: appLocalizations.port,\n                    ),\n                    validator: (value) {\n                      if (value == null || value.isEmpty) {\n                        return appLocalizations.emptyTip(appLocalizations.port);\n                      }\n                      final port = int.tryParse(value);\n                      if (port == null) {\n                        return appLocalizations.numberTip(\n                          appLocalizations.port,\n                        );\n                      }\n                      if (port == 0) {\n                        return null;\n                      }\n                      if (port < 1024 || port > 49151) {\n                        return appLocalizations.portTip(appLocalizations.port);\n                      }\n                      final ports = [\n                        _mixedPortController.text,\n                        _socksPortController.text,\n                        _tProxyPortController.text,\n                        _redirPortController.text,\n                      ].map((item) => item.trim());\n                      if (ports.contains(value.trim())) {\n                        return appLocalizations.portConflictTip;\n                      }\n                      return null;\n                    },\n                  ),\n                  TextFormField(\n                    keyboardType: TextInputType.url,\n                    maxLines: 1,\n                    minLines: 1,\n                    controller: _socksPortController,\n                    onFieldSubmitted: (_) {\n                      _handleUpdate();\n                    },\n                    decoration: InputDecoration(\n                      border: const OutlineInputBorder(),\n                      labelText: appLocalizations.socksPort,\n                    ),\n                    validator: (value) {\n                      if (value == null || value.isEmpty) {\n                        return appLocalizations.emptyTip(\n                          appLocalizations.socksPort,\n                        );\n                      }\n                      final port = int.tryParse(value);\n                      if (port == null) {\n                        return appLocalizations.numberTip(\n                          appLocalizations.socksPort,\n                        );\n                      }\n                      if (port == 0) {\n                        return null;\n                      }\n                      if (port < 1024 || port > 49151) {\n                        return appLocalizations.portTip(\n                          appLocalizations.socksPort,\n                        );\n                      }\n                      final ports = [\n                        _portController.text,\n                        _mixedPortController.text,\n                        _tProxyPortController.text,\n                        _redirPortController.text,\n                      ].map((item) => item.trim());\n                      if (ports.contains(value.trim())) {\n                        return appLocalizations.portConflictTip;\n                      }\n                      return null;\n                    },\n                  ),\n                  TextFormField(\n                    keyboardType: TextInputType.url,\n                    maxLines: 1,\n                    minLines: 1,\n                    controller: _redirPortController,\n                    onFieldSubmitted: (_) {\n                      _handleUpdate();\n                    },\n                    decoration: InputDecoration(\n                      border: const OutlineInputBorder(),\n                      labelText: appLocalizations.redirPort,\n                    ),\n                    validator: (value) {\n                      if (value == null || value.isEmpty) {\n                        return appLocalizations.emptyTip(\n                          appLocalizations.redirPort,\n                        );\n                      }\n                      final port = int.tryParse(value);\n                      if (port == null) {\n                        return appLocalizations.numberTip(\n                          appLocalizations.redirPort,\n                        );\n                      }\n                      if (port == 0) {\n                        return null;\n                      }\n                      if (port < 1024 || port > 49151) {\n                        return appLocalizations.portTip(\n                          appLocalizations.redirPort,\n                        );\n                      }\n                      final ports = [\n                        _portController.text,\n                        _socksPortController.text,\n                        _tProxyPortController.text,\n                        _mixedPortController.text,\n                      ].map((item) => item.trim());\n                      if (ports.contains(value.trim())) {\n                        return appLocalizations.portConflictTip;\n                      }\n                      return null;\n                    },\n                  ),\n                  TextFormField(\n                    keyboardType: TextInputType.url,\n                    maxLines: 1,\n                    minLines: 1,\n                    controller: _tProxyPortController,\n                    onFieldSubmitted: (_) {\n                      _handleUpdate();\n                    },\n                    decoration: InputDecoration(\n                      border: const OutlineInputBorder(),\n                      labelText: appLocalizations.tproxyPort,\n                    ),\n                    validator: (value) {\n                      if (value == null || value.isEmpty) {\n                        return appLocalizations.emptyTip(\n                          appLocalizations.tproxyPort,\n                        );\n                      }\n                      final port = int.tryParse(value);\n                      if (port == null) {\n                        return appLocalizations.numberTip(\n                          appLocalizations.tproxyPort,\n                        );\n                      }\n                      if (port == 0) {\n                        return null;\n                      }\n                      if (port < 1024 || port > 49151) {\n                        return appLocalizations.portTip(\n                          appLocalizations.tproxyPort,\n                        );\n                      }\n                      final ports = [\n                        _portController.text,\n                        _socksPortController.text,\n                        _mixedPortController.text,\n                        _redirPortController.text,\n                      ].map((item) => item.trim());\n                      if (ports.contains(value.trim())) {\n                        return appLocalizations.portConflictTip;\n                      }\n\n                      return null;\n                    },\n                  ),\n                ],\n              ],\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/config/network.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/providers/providers.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nFuture<void> _handleNetworkConfigChange(WidgetRef ref) async {\n  final bool isVpnOrTunEnabled;\n  if (system.isAndroid) {\n    isVpnOrTunEnabled = ref.read(vpnSettingProvider).enable;\n  } else {\n    isVpnOrTunEnabled = ref.read(patchClashConfigProvider).tun.enable;\n  }\n  \n  final isCoreRunning = ref.read(runTimeProvider) != null;\n\n  if (isVpnOrTunEnabled && isCoreRunning) {\n    final tipMessage = system.isAndroid\n        ? appLocalizations.vpnTip\n        : appLocalizations.restartTip;\n    globalState.showNotifier(\n      tipMessage,\n      actionLabel: appLocalizations.restart,\n      showCountdown: true,\n      onAction: () async {\n        await globalState.appController.restartCore();\n        globalState.showNotifier(appLocalizations.success);\n      },\n    );\n  } else {\n    globalState.appController.updateClashConfig();\n  }\n}\n\nclass VPNItem extends ConsumerWidget {\n  const VPNItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final enable = ref.watch(\n      vpnSettingProvider.select((state) => state.enable),\n    );\n    return ListItem.switchItem(\n      title: const Text('VPN'),\n      subtitle: Text(appLocalizations.vpnEnableDesc),\n      delegate: SwitchDelegate(\n        value: enable,\n        onChanged: (value) async {\n          ref\n              .read(vpnSettingProvider.notifier)\n              .updateState((state) => state.copyWith(enable: value));\n        },\n      ),\n    );\n  }\n}\n\nclass TUNItem extends ConsumerWidget {\n  const TUNItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final enable = ref.watch(\n      patchClashConfigProvider.select((state) => state.tun.enable),\n    );\n\n    // Windows 桌面端：检查系统代理是否开启\n    final systemProxyEnabled = system.isWindows\n        ? ref.watch(networkSettingProvider.select((state) => state.systemProxy))\n        : false;\n\n    return ListItem.switchItem(\n      title: Text(appLocalizations.tun),\n      subtitle: Text(appLocalizations.tunDesc),\n      delegate: SwitchDelegate(\n        value: enable,\n        // Windows 桌面端：系统代理开启时禁用开关（通过 onChanged: null）\n        onChanged: (system.isWindows && systemProxyEnabled)\n            ? null\n            : (value) async {\n                ref\n                    .read(patchClashConfigProvider.notifier)\n                    .updateState((state) => state.copyWith.tun(enable: value));\n              },\n      ),\n    );\n  }\n}\n\nclass AllowBypassItem extends ConsumerWidget {\n  const AllowBypassItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final allowBypass = ref.watch(\n      vpnSettingProvider.select((state) => state.allowBypass),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.allowBypass),\n      subtitle: Text(appLocalizations.allowBypassDesc),\n      delegate: SwitchDelegate(\n        value: allowBypass,\n        onChanged: (bool value) async {\n          ref\n              .read(vpnSettingProvider.notifier)\n              .updateState((state) => state.copyWith(allowBypass: value));\n          await _handleNetworkConfigChange(ref);\n        },\n      ),\n    );\n  }\n}\n\nclass VpnSystemProxyItem extends ConsumerWidget {\n  const VpnSystemProxyItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final systemProxy = ref.watch(\n      vpnSettingProvider.select((state) => state.systemProxy),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.systemProxy),\n      subtitle: Text(appLocalizations.systemProxyDesc),\n      delegate: SwitchDelegate(\n        value: systemProxy,\n        onChanged: (bool value) async {\n          ref\n              .read(vpnSettingProvider.notifier)\n              .updateState((state) => state.copyWith(systemProxy: value));\n        },\n      ),\n    );\n  }\n}\n\nclass SystemProxyItem extends ConsumerWidget {\n  const SystemProxyItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final systemProxy = ref.watch(\n      networkSettingProvider.select((state) => state.systemProxy),\n    );\n\n    // Windows 桌面端：检查 TUN 是否开启\n    final tunEnabled = system.isWindows\n        ? ref.watch(\n            patchClashConfigProvider.select((state) => state.tun.enable),\n          )\n        : false;\n\n    return ListItem.switchItem(\n      title: Text(appLocalizations.systemProxy),\n      subtitle: Text(appLocalizations.systemProxyDesc),\n      delegate: SwitchDelegate(\n        value: systemProxy,\n        // Windows 桌面端：TUN 开启时禁用开关（通过 onChanged: null）\n        onChanged: (system.isWindows && tunEnabled)\n            ? null\n            : (bool value) async {\n                ref\n                    .read(networkSettingProvider.notifier)\n                    .updateState((state) => state.copyWith(systemProxy: value));\n              },\n      ),\n    );\n  }\n}\n\nclass AutoSetSystemDnsItem extends ConsumerWidget {\n  const AutoSetSystemDnsItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final autoSetSystemDns = ref.watch(\n      networkSettingProvider.select((state) => state.autoSetSystemDns),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.autoSetSystemDns),\n      delegate: SwitchDelegate(\n        value: autoSetSystemDns,\n        onChanged: (bool value) async {\n          ref\n              .read(networkSettingProvider.notifier)\n              .updateState((state) => state.copyWith(autoSetSystemDns: value));\n        },\n      ),\n    );\n  }\n}\n\nclass StrictRouteItem extends ConsumerWidget {\n  const StrictRouteItem({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final strictRoute = ref.watch(\n      patchClashConfigProvider.select((state) => state.tun.strictRoute),\n    );\n\n    return ListItem.switchItem(\n      title: Text(appLocalizations.strictRoute),\n      subtitle: Text(appLocalizations.strictRouteDesc),\n      delegate: SwitchDelegate(\n        value: strictRoute,\n        onChanged: (value) async {\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith.tun(strictRoute: value));\n\n          await _handleNetworkConfigChange(ref);\n        },\n      ),\n    );\n  }\n}\n\nclass IcmpForwardingItem extends ConsumerWidget {\n  const IcmpForwardingItem({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    // Note: inverted because disableIcmpForwarding=true means disabled\n    // UI shows \"Enable ICMP forwarding\"\n    final icmpForwarding = ref.watch(\n      patchClashConfigProvider.select(\n        (state) => !state.tun.disableIcmpForwarding,\n      ),\n    );\n\n    return ListItem.switchItem(\n      title: Text(appLocalizations.icmpForwarding),\n      subtitle: Text(appLocalizations.icmpForwardingDesc),\n      delegate: SwitchDelegate(\n        value: icmpForwarding,\n        onChanged: (value) async {\n          // Invert before passing to core\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState(\n                (state) => state.copyWith.tun(disableIcmpForwarding: !value),\n              );\n\n          await _handleNetworkConfigChange(ref);\n        },\n      ),\n    );\n  }\n}\n\n\nclass DnsHijackItem extends ConsumerWidget {\n  const DnsHijackItem({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final dnsHijack = ref.watch(\n      patchClashConfigProvider.select(\n        (state) => state.tun.dnsHijack.isNotEmpty,\n      ),\n    );\n\n    return ListItem.switchItem(\n      title: Text(appLocalizations.dnsHijack),\n      subtitle: Text(appLocalizations.dnsHijackDesc),\n      delegate: SwitchDelegate(\n        value: dnsHijack,\n        onChanged: (value) async {\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState(\n                (state) => state.copyWith.tun(\n                  dnsHijack: value ? ['any:53', 'tcp://any:53'] : [],\n                ),\n              );\n          await _handleNetworkConfigChange(ref);\n        },\n      ),\n    );\n  }\n}\n\nclass EndpointIndependentNatItem extends ConsumerWidget {\n  const EndpointIndependentNatItem({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final endpointIndependentNat = ref.watch(\n      patchClashConfigProvider.select(\n        (state) => state.tun.endpointIndependentNat,\n      ),\n    );\n\n    return ListItem.switchItem(\n      title: Text(appLocalizations.endpointIndependentNat),\n      subtitle: Text(appLocalizations.endpointIndependentNatDesc),\n      delegate: SwitchDelegate(\n        value: endpointIndependentNat,\n        onChanged: (value) async {\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState(\n                (state) => state.copyWith.tun(endpointIndependentNat: value),\n              );\n          await _handleNetworkConfigChange(ref);\n        },\n      ),\n    );\n  }\n}\n\nclass TunStackItem extends ConsumerWidget {\n  const TunStackItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final stack = ref.watch(\n      patchClashConfigProvider.select((state) => state.tun.stack),\n    );\n\n    return ListItem.options(\n      title: Text(appLocalizations.stackMode),\n      subtitle: Text(stack.name),\n      delegate: OptionsDelegate<TunStack>(\n        value: stack,\n        options: TunStack.values,\n        textBuilder: (value) => value.name,\n        onChanged: (value) async {\n          if (value == null) {\n            return;\n          }\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith.tun(stack: value));\n          await _handleNetworkConfigChange(ref);\n        },\n        title: appLocalizations.stackMode,\n      ),\n    );\n  }\n}\n\nclass MtuItem extends ConsumerWidget {\n  const MtuItem({super.key});\n\n  Future<void> _showCustomMtuDialog(\n    BuildContext context,\n    WidgetRef ref,\n    int currentMtu,\n  ) async {\n    String inputValue = '$currentMtu';\n    String? errorText;\n    final controller = TextEditingController(text: inputValue);\n\n    final result = await globalState.showCommonDialog<bool>(\n      child: StatefulBuilder(\n        builder: (context, setState) {\n          return CommonDialog(\n            title: 'MTU',\n            actions: [\n              TextButton(\n                onPressed: () {\n                  Navigator.of(context).pop(false);\n                },\n                child: Text(appLocalizations.cancel),\n              ),\n              TextButton(\n                onPressed: errorText == null && inputValue.isNotEmpty\n                    ? () {\n                        Navigator.of(context).pop(true);\n                      }\n                    : null,\n                child: Text(appLocalizations.confirm),\n              ),\n            ],\n            child: TextField(\n              autofocus: true,\n              keyboardType: TextInputType.number,\n              controller: controller,\n              decoration: InputDecoration(\n                labelText: 'MTU',\n                errorText: errorText,\n                hintText: '1280-65535',\n              ),\n              onChanged: (value) {\n                inputValue = value;\n                // Real-time validation\n                if (value.isEmpty) {\n                  setState(() {\n                    errorText = appLocalizations.emptyTip('MTU');\n                  });\n                } else {\n                  final intValue = int.tryParse(value);\n                  if (intValue == null) {\n                    setState(() {\n                      errorText = appLocalizations.numberTip('MTU');\n                    });\n                  } else if (intValue < 1280 || intValue > 65535) {\n                    setState(() {\n                      errorText = 'MTU Must be 1280-65535';\n                    });\n                  } else {\n                    setState(() {\n                      errorText = null;\n                    });\n                  }\n                }\n              },\n              onSubmitted: (value) {\n                if (errorText == null && value.isNotEmpty) {\n                  Navigator.of(context).pop(true);\n                }\n              },\n            ),\n          );\n        },\n      ),\n    );\n\n    // Clean up controller\n    controller.dispose();\n\n    if (result == true && inputValue.isNotEmpty) {\n      final intValue = int.tryParse(inputValue);\n      if (intValue != null && intValue >= 1280 && intValue <= 65535) {\n        ref\n            .read(patchClashConfigProvider.notifier)\n            .updateState((state) => state.copyWith.tun(mtu: intValue));\n        await _handleNetworkConfigChange(ref);\n      }\n    }\n  }\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final mtu = ref.watch(\n      patchClashConfigProvider.select((state) => state.tun.mtu),\n    );\n\n    // Preset options\n    final presetOptions = [1480, 4064, 9000];\n    final isCustom = !presetOptions.contains(mtu);\n\n    return ListItem.options(\n      title: const Text('MTU'),\n      subtitle: Text(isCustom ? '$mtu (${appLocalizations.custom})' : '$mtu'),\n      delegate: OptionsDelegate<String>(\n        value: isCustom ? 'custom' : '$mtu',\n        options: ['1480', '4064', '9000', 'custom'],\n        textBuilder: (value) {\n          if (value == 'custom') {\n            return '${appLocalizations.custom}...';\n          }\n          return value;\n        },\n        onChanged: (value) async {\n          if (value == null) return;\n\n          // If custom option selected\n          if (value == 'custom') {\n            await _showCustomMtuDialog(context, ref, mtu);\n          } else {\n            // Apply preset value directly\n            final intValue = int.parse(value);\n            ref\n                .read(patchClashConfigProvider.notifier)\n                .updateState((state) => state.copyWith.tun(mtu: intValue));\n            await _handleNetworkConfigChange(ref);\n          }\n        },\n        title: 'MTU',\n      ),\n    );\n  }\n}\n\nclass BypassDomainItem extends StatelessWidget {\n  const BypassDomainItem({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return ListItem.open(\n      title: Text(appLocalizations.bypassDomain),\n      subtitle: Text(appLocalizations.bypassDomainDesc),\n      delegate: OpenDelegate(\n        blur: false,\n        actions: [\n          Consumer(\n            builder: (_, ref, _) {\n              return IconButton(\n                onPressed: () async {\n                  final res = await globalState.showMessage(\n                    title: appLocalizations.reset,\n                    message: TextSpan(text: appLocalizations.resetTip),\n                  );\n                  if (res != true) {\n                    return;\n                  }\n                  ref\n                      .read(networkSettingProvider.notifier)\n                      .updateState(\n                        (state) =>\n                            state.copyWith(bypassDomain: defaultBypassDomain),\n                      );\n                },\n                tooltip: appLocalizations.reset,\n                icon: const Icon(Icons.replay),\n              );\n            },\n          ),\n        ],\n        title: appLocalizations.bypassDomain,\n        widget: Consumer(\n          builder: (_, ref, _) {\n            final bypassDomain = ref.watch(\n              networkSettingProvider.select((state) => state.bypassDomain),\n            );\n            return ListInputPage(\n              title: appLocalizations.bypassDomain,\n              items: bypassDomain,\n              titleBuilder: (item) => Text(item),\n              onChange: (items) {\n                ref\n                    .read(networkSettingProvider.notifier)\n                    .updateState(\n                      (state) => state.copyWith(bypassDomain: List.from(items)),\n                    );\n              },\n            );\n          },\n        ),\n      ),\n    );\n  }\n}\n\nclass BypassPrivateRouteItem extends ConsumerWidget {\n  const BypassPrivateRouteItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final bypassPrivateRoute = ref.watch(\n      networkSettingProvider.select((state) => state.bypassPrivateRoute),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.bypassPrivateRoute),\n      subtitle: Text(appLocalizations.bypassPrivateRouteDesc),\n      delegate: SwitchDelegate(\n        value: bypassPrivateRoute,\n        onChanged: (value) async {\n          ref\n              .read(networkSettingProvider.notifier)\n              .updateState((state) => state.copyWith(bypassPrivateRoute: value));\n          await _handleNetworkConfigChange(ref);\n        },\n      ),\n    );\n  }\n}\n\n\n\nfinal networkItems = [\n  if (system.isAndroid) const VPNItem(),\n  if (system.isAndroid)\n    ...generateSection(\n      title: 'VPN',\n      items: [const AllowBypassItem()],\n    ),\n  if (system.isDesktop)\n    ...generateSection(\n      title: appLocalizations.system,\n      items: [SystemProxyItem(), BypassDomainItem()],\n    ),\n  ...generateSection(\n    title: appLocalizations.options,\n    items: [\n      if (system.isDesktop) const TUNItem(),\n      if (system.isMacOS) const AutoSetSystemDnsItem(),\n      const StrictRouteItem(),\n      const IcmpForwardingItem(),\n      const DnsHijackItem(),\n      const EndpointIndependentNatItem(),\n      const TunStackItem(),\n      const MtuItem(),\n      const BypassPrivateRouteItem(),\n    ],\n  ),\n];\n\nclass NetworkListView extends StatelessWidget {\n  const NetworkListView({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return generateListView(networkItems);\n  }\n}\n"
  },
  {
    "path": "lib/views/config/ntp.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass OverrideNtpItem extends ConsumerWidget {\n  const OverrideNtpItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final override = ref.watch(overrideNtpProvider);\n    return ListItem.switchItem(\n      title: Text(appLocalizations.overrideNtp),\n      subtitle: Text(appLocalizations.overrideNtpDesc),\n      delegate: SwitchDelegate(\n        value: override,\n        onChanged: (bool value) async {\n          ref.read(overrideNtpProvider.notifier).value = value;\n        },\n      ),\n    );\n  }\n}\n\nclass NtpStatusItem extends ConsumerWidget {\n  const NtpStatusItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final enable = ref.watch(\n      patchClashConfigProvider.select((state) => state.ntp.enable),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.ntpStatus),\n      subtitle: Text(appLocalizations.ntpStatusDesc),\n      delegate: SwitchDelegate(\n        value: enable,\n        onChanged: (bool value) async {\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith.ntp(enable: value));\n        },\n      ),\n    );\n  }\n}\n\nclass WriteToSystemItem extends ConsumerWidget {\n  const WriteToSystemItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    // Hide on Android due to lack of root permissions\n    if (system.isAndroid) {\n      return const SizedBox.shrink();\n    }\n\n    final writeToSystem = ref.watch(\n      patchClashConfigProvider.select((state) => state.ntp.writeToSystem),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.writeToSystem),\n      subtitle: Text(appLocalizations.writeToSystemDesc),\n      delegate: SwitchDelegate(\n        value: writeToSystem,\n        onChanged: (bool value) async {\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith.ntp(writeToSystem: value));\n        },\n      ),\n    );\n  }\n}\n\nclass NtpServerItem extends ConsumerWidget {\n  const NtpServerItem({super.key});\n\n  // Custom URL marker\n  static const String _customUrlMarker = '__CUSTOM_NTP_SERVER__';\n\n  // Pre-computed options list to avoid recreating on every build\n  static final List<String> _options = [...presetNtpServers, _customUrlMarker];\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final server = ref.watch(\n      patchClashConfigProvider.select((state) => state.ntp.server),\n    );\n\n    // Check if current server is in preset list\n    final isPresetServer = presetNtpServers.contains(server);\n\n    return ListItem<String>.options(\n      title: Text(appLocalizations.ntpServer),\n      subtitle: Text(server),\n      delegate: OptionsDelegate<String>(\n        title: appLocalizations.ntpServer,\n        options: _options,\n        value: isPresetServer ? server : _customUrlMarker,\n        onChanged: (String? value) async {\n          if (value == null) return;\n\n          if (value == _customUrlMarker) {\n            // Show custom server input dialog\n            final customServer = await globalState.showCommonDialog<String>(\n              child: InputDialog(\n                title: appLocalizations.customUrl,\n                value: isPresetServer ? '' : server,\n                resetValue: defaultNtpServer,\n                validator: (String? inputValue) {\n                  if (inputValue == null || inputValue.isEmpty) {\n                    return appLocalizations.emptyTip(\n                      appLocalizations.ntpServer,\n                    );\n                  }\n                  return null;\n                },\n              ),\n            );\n\n            if (customServer != null) {\n              ref\n                  .read(patchClashConfigProvider.notifier)\n                  .updateState(\n                    (state) => state.copyWith.ntp(server: customServer),\n                  );\n            }\n          } else {\n            // Use preset server\n            ref\n                .read(patchClashConfigProvider.notifier)\n                .updateState((state) => state.copyWith.ntp(server: value));\n          }\n        },\n        textBuilder: (server) {\n          if (server == _customUrlMarker) {\n            return appLocalizations.customUrl;\n          }\n          return server;\n        },\n      ),\n    );\n  }\n}\n\nclass NtpPortItem extends ConsumerWidget {\n  const NtpPortItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final port = ref.watch(\n      patchClashConfigProvider.select((state) => state.ntp.port),\n    );\n    return ListItem.input(\n      title: Text(appLocalizations.ntpPort),\n      subtitle: Text(port.toString()),\n      delegate: InputDelegate(\n        title: appLocalizations.ntpPort,\n        value: port.toString(),\n        validator: (value) {\n          if (value == null || value.isEmpty) {\n            return appLocalizations.emptyTip(appLocalizations.ntpPort);\n          }\n          final intValue = int.tryParse(value);\n          if (intValue == null) {\n            return appLocalizations.numberTip(appLocalizations.ntpPort);\n          }\n          return null;\n        },\n        onChanged: (String? value) {\n          if (value == null) {\n            return;\n          }\n          final intValue = int.tryParse(value);\n          if (intValue == null) {\n            return;\n          }\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith.ntp(port: intValue));\n        },\n      ),\n    );\n  }\n}\n\nclass NtpIntervalItem extends ConsumerWidget {\n  const NtpIntervalItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final interval = ref.watch(\n      patchClashConfigProvider.select((state) => state.ntp.interval),\n    );\n    return ListItem.input(\n      title: Text(appLocalizations.ntpInterval),\n      subtitle: Text(interval.toString()),\n      delegate: InputDelegate(\n        title: appLocalizations.ntpInterval,\n        value: interval.toString(),\n        validator: (value) {\n          if (value == null || value.isEmpty) {\n            return appLocalizations.emptyTip(appLocalizations.ntpInterval);\n          }\n          final intValue = int.tryParse(value);\n          if (intValue == null) {\n            return appLocalizations.numberTip(appLocalizations.ntpInterval);\n          }\n          return null;\n        },\n        onChanged: (String? value) {\n          if (value == null) {\n            return;\n          }\n          final intValue = int.tryParse(value);\n          if (intValue == null) {\n            return;\n          }\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith.ntp(interval: intValue));\n        },\n      ),\n    );\n  }\n}\n\nclass NtpOptions extends StatelessWidget {\n  const NtpOptions({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return Column(\n      children: generateSection(\n        title: appLocalizations.options,\n        items: [\n          const NtpStatusItem(),\n          const WriteToSystemItem(),\n          const NtpServerItem(),\n          const NtpPortItem(),\n          const NtpIntervalItem(),\n        ],\n      ),\n    );\n  }\n}\n\nconst ntpItems = <Widget>[OverrideNtpItem(), NtpOptions()];\n\nclass NtpListView extends ConsumerWidget {\n  const NtpListView({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    return generateListView(ntpItems);\n  }\n}\n"
  },
  {
    "path": "lib/views/config/sniffer.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/models/clash_config.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass OverrideSnifferItem extends ConsumerWidget {\n  const OverrideSnifferItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final override = ref.watch(overrideSnifferProvider);\n    return ListItem.switchItem(\n      title: Text(appLocalizations.overrideSniffer),\n      subtitle: Text(appLocalizations.overrideSnifferDesc),\n      delegate: SwitchDelegate(\n        value: override,\n        onChanged: (bool value) async {\n          ref.read(overrideSnifferProvider.notifier).value = value;\n        },\n      ),\n    );\n  }\n}\n\nclass SnifferStatusItem extends ConsumerWidget {\n  const SnifferStatusItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final enable = ref.watch(\n      patchClashConfigProvider.select((state) => state.sniffer.enable),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.snifferStatus),\n      subtitle: Text(appLocalizations.snifferStatusDesc),\n      delegate: SwitchDelegate(\n        value: enable,\n        onChanged: (bool value) async {\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith.sniffer(enable: value));\n        },\n      ),\n    );\n  }\n}\n\nclass ForceDnsMappingItem extends ConsumerWidget {\n  const ForceDnsMappingItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final forceDnsMapping = ref.watch(\n      patchClashConfigProvider.select((state) => state.sniffer.forceDnsMapping),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.forceDnsMapping),\n      delegate: SwitchDelegate(\n        value: forceDnsMapping,\n        onChanged: (bool value) async {\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState(\n                (state) => state.copyWith.sniffer(forceDnsMapping: value),\n              );\n        },\n      ),\n    );\n  }\n}\n\nclass ParsePureIpItem extends ConsumerWidget {\n  const ParsePureIpItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final parsePureIp = ref.watch(\n      patchClashConfigProvider.select((state) => state.sniffer.parsePureIp),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.parsePureIp),\n      delegate: SwitchDelegate(\n        value: parsePureIp,\n        onChanged: (bool value) async {\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState(\n                (state) => state.copyWith.sniffer(parsePureIp: value),\n              );\n        },\n      ),\n    );\n  }\n}\n\nclass OverrideDestinationItem extends ConsumerWidget {\n  const OverrideDestinationItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final overrideDest = ref.watch(\n      patchClashConfigProvider.select((state) => state.sniffer.overrideDest),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.overrideDestination),\n      delegate: SwitchDelegate(\n        value: overrideDest,\n        onChanged: (bool value) async {\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState(\n                (state) => state.copyWith.sniffer(overrideDest: value),\n              );\n        },\n      ),\n    );\n  }\n}\n\nclass HttpPortSnifferItem extends ConsumerWidget {\n  const HttpPortSnifferItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final httpConfig = ref.watch(\n      patchClashConfigProvider.select((state) => state.sniffer.sniff['HTTP']),\n    );\n    final ports = httpConfig?.ports.join(', ') ?? '';\n\n    return ListItem(\n      title: Text(appLocalizations.httpPortSniffer),\n      subtitle: Text(ports.isEmpty ? '80, 8080-8880' : ports),\n      onTap: () => _showHttpDialog(context, ref, httpConfig),\n    );\n  }\n\n  void _showHttpDialog(\n    BuildContext context,\n    WidgetRef ref,\n    SnifferConfig? httpConfig,\n  ) async {\n    final ports = httpConfig?.ports.join(', ') ?? '';\n    final overrideDest = httpConfig?.overrideDest ?? true; // HTTP 默认为 true\n\n    await globalState.showCommonDialog(\n      child: _SnifferPortDialog(\n        title: appLocalizations.httpPortSniffer,\n        ports: ports,\n        overrideDest: overrideDest,\n        onSave: (newPorts, newOverrideDest) {\n          final portList = newPorts\n              .split(',')\n              .map((e) => e.trim())\n              .where((e) => e.isNotEmpty)\n              .toList();\n          final newSniff = Map<String, SnifferConfig>.from(\n            ref.read(patchClashConfigProvider).sniffer.sniff,\n          );\n          newSniff['HTTP'] = SnifferConfig(\n            ports: portList,\n            overrideDest: newOverrideDest,\n          );\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith.sniffer(sniff: newSniff));\n        },\n      ),\n    );\n  }\n}\n\nclass TlsPortSnifferItem extends ConsumerWidget {\n  const TlsPortSnifferItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final tlsConfig = ref.watch(\n      patchClashConfigProvider.select((state) => state.sniffer.sniff['TLS']),\n    );\n    final ports = tlsConfig?.ports.join(', ') ?? '';\n\n    return ListItem(\n      title: Text(appLocalizations.tlsPortSniffer),\n      subtitle: Text(ports.isEmpty ? '443, 8443' : ports),\n      onTap: () => _showTlsDialog(context, ref, tlsConfig),\n    );\n  }\n\n  void _showTlsDialog(\n    BuildContext context,\n    WidgetRef ref,\n    SnifferConfig? tlsConfig,\n  ) async {\n    final ports = tlsConfig?.ports.join(', ') ?? '';\n    final overrideDest = tlsConfig?.overrideDest ?? false;\n\n    await globalState.showCommonDialog(\n      child: _SnifferPortDialog(\n        title: appLocalizations.tlsPortSniffer,\n        ports: ports,\n        overrideDest: overrideDest,\n        onSave: (newPorts, newOverrideDest) {\n          final portList = newPorts\n              .split(',')\n              .map((e) => e.trim())\n              .where((e) => e.isNotEmpty)\n              .toList();\n          final newSniff = Map<String, SnifferConfig>.from(\n            ref.read(patchClashConfigProvider).sniffer.sniff,\n          );\n          newSniff['TLS'] = SnifferConfig(\n            ports: portList,\n            overrideDest: newOverrideDest,\n          );\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith.sniffer(sniff: newSniff));\n        },\n      ),\n    );\n  }\n}\n\nclass QuicPortSnifferItem extends ConsumerWidget {\n  const QuicPortSnifferItem({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final quicConfig = ref.watch(\n      patchClashConfigProvider.select((state) => state.sniffer.sniff['QUIC']),\n    );\n    final ports = quicConfig?.ports.join(', ') ?? '';\n\n    return ListItem(\n      title: Text(appLocalizations.quicPortSniffer),\n      subtitle: Text(ports.isEmpty ? '443, 8443' : ports),\n      onTap: () => _showQuicDialog(context, ref, quicConfig),\n    );\n  }\n\n  void _showQuicDialog(\n    BuildContext context,\n    WidgetRef ref,\n    SnifferConfig? quicConfig,\n  ) async {\n    final ports = quicConfig?.ports.join(', ') ?? '';\n    final overrideDest = quicConfig?.overrideDest ?? false;\n\n    await globalState.showCommonDialog(\n      child: _SnifferPortDialog(\n        title: appLocalizations.quicPortSniffer,\n        ports: ports,\n        overrideDest: overrideDest,\n        onSave: (newPorts, newOverrideDest) {\n          final portList = newPorts\n              .split(',')\n              .map((e) => e.trim())\n              .where((e) => e.isNotEmpty)\n              .toList();\n          final newSniff = Map<String, SnifferConfig>.from(\n            ref.read(patchClashConfigProvider).sniffer.sniff,\n          );\n          newSniff['QUIC'] = SnifferConfig(\n            ports: portList,\n            overrideDest: newOverrideDest,\n          );\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith.sniffer(sniff: newSniff));\n        },\n      ),\n    );\n  }\n}\n\nclass _SnifferPortDialog extends StatefulWidget {\n  final String title;\n  final String ports;\n  final bool overrideDest;\n  final Function(String ports, bool overrideDest) onSave;\n\n  const _SnifferPortDialog({\n    required this.title,\n    required this.ports,\n    required this.overrideDest,\n    required this.onSave,\n  });\n\n  @override\n  State<_SnifferPortDialog> createState() => _SnifferPortDialogState();\n}\n\nclass _SnifferPortDialogState extends State<_SnifferPortDialog> {\n  late TextEditingController _portsController;\n  late bool _overrideDest;\n\n  @override\n  void initState() {\n    super.initState();\n    _portsController = TextEditingController(text: widget.ports);\n    _overrideDest = widget.overrideDest;\n  }\n\n  @override\n  void dispose() {\n    _portsController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return CommonDialog(\n      title: widget.title,\n      actions: [\n        TextButton(\n          onPressed: () => Navigator.of(context).pop(),\n          child: Text(appLocalizations.cancel),\n        ),\n        TextButton(\n          onPressed: () {\n            widget.onSave(_portsController.text, _overrideDest);\n            Navigator.of(context).pop();\n          },\n          child: Text(appLocalizations.save),\n        ),\n      ],\n      child: SizedBox(\n        width: 300,\n        child: Column(\n          mainAxisSize: MainAxisSize.min,\n          children: [\n            TextField(\n              controller: _portsController,\n              decoration: InputDecoration(\n                border: const OutlineInputBorder(),\n                labelText: appLocalizations.snifferPorts,\n                hintText: '443, 8443',\n              ),\n            ),\n            const SizedBox(height: 16),\n            Row(\n              mainAxisAlignment: MainAxisAlignment.spaceBetween,\n              children: [\n                Text(appLocalizations.overrideDestination),\n                Switch(\n                  value: _overrideDest,\n                  onChanged: (value) {\n                    setState(() {\n                      _overrideDest = value;\n                    });\n                  },\n                ),\n              ],\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n\nclass ForceDomainWidget extends ConsumerWidget {\n  const ForceDomainWidget({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    return ListItem.open(\n      title: Text(appLocalizations.forceDomain),\n      delegate: OpenDelegate(\n        blur: false,\n        title: appLocalizations.forceDomain,\n        widget: Consumer(\n          builder: (_, ref, _) {\n            final forceDomain = ref.watch(\n              patchClashConfigProvider.select(\n                (state) => state.sniffer.forceDomain,\n              ),\n            );\n            return ListInputPage(\n              title: appLocalizations.forceDomain,\n              items: forceDomain,\n              titleBuilder: (item) => Text(item),\n              onChange: (items) {\n                ref\n                    .read(patchClashConfigProvider.notifier)\n                    .updateState(\n                      (state) =>\n                          state.copyWith.sniffer(forceDomain: List.from(items)),\n                    );\n              },\n            );\n          },\n        ),\n      ),\n    );\n  }\n}\n\nclass SkipDomainWidget extends ConsumerWidget {\n  const SkipDomainWidget({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    return ListItem.open(\n      title: Text(appLocalizations.skipDomain),\n      delegate: OpenDelegate(\n        blur: false,\n        title: appLocalizations.skipDomain,\n        widget: Consumer(\n          builder: (_, ref, _) {\n            final skipDomain = ref.watch(\n              patchClashConfigProvider.select(\n                (state) => state.sniffer.skipDomain,\n              ),\n            );\n            return ListInputPage(\n              title: appLocalizations.skipDomain,\n              items: skipDomain,\n              titleBuilder: (item) => Text(item),\n              onChange: (items) {\n                ref\n                    .read(patchClashConfigProvider.notifier)\n                    .updateState(\n                      (state) =>\n                          state.copyWith.sniffer(skipDomain: List.from(items)),\n                    );\n              },\n            );\n          },\n        ),\n      ),\n    );\n  }\n}\n\nclass SkipSrcAddressWidget extends ConsumerWidget {\n  const SkipSrcAddressWidget({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    return ListItem.open(\n      title: Text(appLocalizations.skipSrcAddress),\n      delegate: OpenDelegate(\n        blur: false,\n        title: appLocalizations.skipSrcAddress,\n        widget: Consumer(\n          builder: (_, ref, _) {\n            final skipSrcAddress = ref.watch(\n              patchClashConfigProvider.select(\n                (state) => state.sniffer.skipSrcAddress,\n              ),\n            );\n            return ListInputPage(\n              title: appLocalizations.skipSrcAddress,\n              items: skipSrcAddress,\n              titleBuilder: (item) => Text(item),\n              onChange: (items) {\n                ref\n                    .read(patchClashConfigProvider.notifier)\n                    .updateState(\n                      (state) => state.copyWith.sniffer(\n                        skipSrcAddress: List.from(items),\n                      ),\n                    );\n              },\n            );\n          },\n        ),\n      ),\n    );\n  }\n}\n\nclass SkipDstAddressWidget extends ConsumerWidget {\n  const SkipDstAddressWidget({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    return ListItem.open(\n      title: Text(appLocalizations.skipDstAddress),\n      delegate: OpenDelegate(\n        blur: false,\n        title: appLocalizations.skipDstAddress,\n        widget: Consumer(\n          builder: (_, ref, _) {\n            final skipDstAddress = ref.watch(\n              patchClashConfigProvider.select(\n                (state) => state.sniffer.skipDstAddress,\n              ),\n            );\n            return ListInputPage(\n              title: appLocalizations.skipDstAddress,\n              items: skipDstAddress,\n              titleBuilder: (item) => Text(item),\n              onChange: (items) {\n                ref\n                    .read(patchClashConfigProvider.notifier)\n                    .updateState(\n                      (state) => state.copyWith.sniffer(\n                        skipDstAddress: List.from(items),\n                      ),\n                    );\n              },\n            );\n          },\n        ),\n      ),\n    );\n  }\n}\n\nclass SnifferOptions extends StatelessWidget {\n  const SnifferOptions({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return Column(\n      children: generateSection(\n        title: appLocalizations.options,\n        items: [\n          const SnifferStatusItem(),\n          const ForceDnsMappingItem(),\n          const ParsePureIpItem(),\n          const OverrideDestinationItem(),\n          const HttpPortSnifferItem(),\n          const TlsPortSnifferItem(),\n          const QuicPortSnifferItem(),\n          const ForceDomainWidget(),\n          const SkipDomainWidget(),\n          const SkipSrcAddressWidget(),\n          const SkipDstAddressWidget(),\n        ],\n      ),\n    );\n  }\n}\n\nconst snifferItems = <Widget>[OverrideSnifferItem(), SnifferOptions()];\n\nclass SnifferListView extends ConsumerWidget {\n  const SnifferListView({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    return generateListView(snifferItems);\n  }\n}\n"
  },
  {
    "path": "lib/views/config/tunnel.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/models/clash_config.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass TunnelListWidget extends ConsumerWidget {\n  const TunnelListWidget({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final tunnels = ref.watch(\n      patchClashConfigProvider.select((state) => state.tunnels),\n    );\n\n    if (tunnels.isEmpty) {\n      return NullStatus(label: appLocalizations.noData);\n    }\n\n    return ListView.builder(\n      shrinkWrap: true,\n      physics: const NeverScrollableScrollPhysics(),\n      itemCount: tunnels.length,\n      itemBuilder: (context, index) {\n        final tunnel = tunnels[index];\n        return Padding(\n          padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),\n          child: CommonCard(\n            child: ListItem(\n              title: Text(tunnel.displayValue),\n              onTap: () => _showTunnelDialog(\n                context,\n                ref,\n                tunnels,\n                tunnel: tunnel,\n                index: index,\n              ),\n              trailing: IconButton(\n                icon: const Icon(Icons.delete_outline),\n                onPressed: () => _deleteTunnel(ref, tunnels, index),\n              ),\n            ),\n          ),\n        );\n      },\n    );\n  }\n\n  void _deleteTunnel(WidgetRef ref, List<TunnelEntry> tunnels, int index) {\n    final newTunnels = List<TunnelEntry>.from(tunnels);\n    newTunnels.removeAt(index);\n    ref\n        .read(patchClashConfigProvider.notifier)\n        .updateState((state) => state.copyWith(tunnels: newTunnels));\n  }\n\n  void _showTunnelDialog(\n    BuildContext context,\n    WidgetRef ref,\n    List<TunnelEntry> tunnels, {\n    TunnelEntry? tunnel,\n    int? index,\n  }) {\n    showDialog(\n      context: context,\n      builder: (context) => _TunnelDialog(\n        tunnel: tunnel,\n        onSave: (newTunnel) {\n          final newTunnels = List<TunnelEntry>.from(tunnels);\n          if (index != null) {\n            newTunnels[index] = newTunnel;\n          } else {\n            newTunnels.add(newTunnel);\n          }\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith(tunnels: newTunnels));\n        },\n      ),\n    );\n  }\n}\n\nclass _TunnelDialog extends StatefulWidget {\n  final TunnelEntry? tunnel;\n  final Function(TunnelEntry) onSave;\n\n  const _TunnelDialog({this.tunnel, required this.onSave});\n\n  @override\n  State<_TunnelDialog> createState() => _TunnelDialogState();\n}\n\nclass _TunnelDialogState extends State<_TunnelDialog> {\n  late TextEditingController _networkController;\n  late TextEditingController _addressController;\n  late TextEditingController _targetController;\n  late TextEditingController _proxyController;\n\n  @override\n  void initState() {\n    super.initState();\n    _networkController = TextEditingController(\n      text: widget.tunnel?.network?.join(', ') ?? '',\n    );\n    _addressController = TextEditingController(\n      text: widget.tunnel?.address ?? '',\n    );\n    _targetController = TextEditingController(\n      text: widget.tunnel?.target ?? '',\n    );\n    _proxyController = TextEditingController(\n      text: widget.tunnel?.proxyName ?? '',\n    );\n  }\n\n  @override\n  void dispose() {\n    _networkController.dispose();\n    _addressController.dispose();\n    _targetController.dispose();\n    _proxyController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return AlertDialog(\n      title: Text(\n        widget.tunnel == null\n            ? appLocalizations.addTunnel\n            : appLocalizations.editTunnel,\n      ),\n      content: SingleChildScrollView(\n        child: Column(\n          mainAxisSize: MainAxisSize.min,\n          children: [\n            TextField(\n              controller: _networkController,\n              decoration: InputDecoration(\n                labelText: appLocalizations.tunnelNetwork,\n                hintText: appLocalizations.tunnelNetworkHint,\n              ),\n            ),\n            const SizedBox(height: 16),\n            TextField(\n              controller: _addressController,\n              decoration: InputDecoration(\n                labelText: appLocalizations.tunnelAddress,\n                hintText: appLocalizations.tunnelAddressHint,\n              ),\n            ),\n            const SizedBox(height: 16),\n            TextField(\n              controller: _targetController,\n              decoration: InputDecoration(\n                labelText: appLocalizations.tunnelTarget,\n                hintText: appLocalizations.tunnelTargetHint,\n              ),\n            ),\n            const SizedBox(height: 16),\n            TextField(\n              controller: _proxyController,\n              decoration: InputDecoration(\n                labelText: appLocalizations.tunnelProxy,\n                hintText: appLocalizations.tunnelProxyHint,\n              ),\n            ),\n          ],\n        ),\n      ),\n      actions: [\n        TextButton(\n          onPressed: () => Navigator.of(context).pop(),\n          child: Text(appLocalizations.cancel),\n        ),\n        TextButton(\n          onPressed: () {\n            final network = _networkController.text\n                .split(',')\n                .map((e) => e.trim())\n                .where((e) => e.isNotEmpty)\n                .toList();\n            final address = _addressController.text.trim();\n            final target = _targetController.text.trim();\n            final proxy = _proxyController.text.trim();\n\n            if (address.isEmpty || target.isEmpty) {\n              if (context.mounted) {\n                context.showSnackBar(\n                  appLocalizations.emptyTip(appLocalizations.tunnelAddress),\n                );\n              }\n              return;\n            }\n\n            final newTunnel = TunnelEntry(\n              id: widget.tunnel?.id ?? utils.uuidV4,\n              network: network.isEmpty ? null : network,\n              address: address.isEmpty ? null : address,\n              target: target.isEmpty ? null : target,\n              proxyName: proxy.isEmpty ? null : proxy,\n            );\n\n            widget.onSave(newTunnel);\n            Navigator.of(context).pop();\n          },\n          child: Text(appLocalizations.save),\n        ),\n      ],\n    );\n  }\n}\n\nclass TunnelListView extends ConsumerWidget {\n  const TunnelListView({super.key});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final tunnels = ref.watch(\n      patchClashConfigProvider.select((state) => state.tunnels),\n    );\n\n    return Scaffold(\n      body: ListView(\n        padding: const EdgeInsets.only(top: 16),\n        children: [const TunnelListWidget()],\n      ),\n      floatingActionButton: FloatingActionButton(\n        onPressed: () => _showTunnelDialog(context, ref, tunnels),\n        child: const Icon(Icons.add),\n      ),\n    );\n  }\n\n  void _showTunnelDialog(\n    BuildContext context,\n    WidgetRef ref,\n    List<TunnelEntry> tunnels,\n  ) {\n    showDialog(\n      context: context,\n      builder: (context) => _TunnelDialog(\n        tunnel: null,\n        onSave: (newTunnel) {\n          final newTunnels = List<TunnelEntry>.from(tunnels);\n          newTunnels.add(newTunnel);\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith(tunnels: newTunnels));\n        },\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/connection/connections.dart",
    "content": "import 'dart:async';\n\nimport 'package:bett_box/clash/clash.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/providers/providers.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:window_manager/window_manager.dart';\n\nimport 'item.dart';\n\nclass ConnectionsView extends ConsumerStatefulWidget {\n  final bool respectCurrentPage;\n\n  const ConnectionsView({\n    super.key,\n    this.respectCurrentPage = true,\n  });\n\n  @override\n  ConsumerState<ConnectionsView> createState() => _ConnectionsViewState();\n}\n\nclass _ConnectionsViewState extends ConsumerState<ConnectionsView>\n    with WidgetsBindingObserver, WindowListener {\n  late final ScrollController _scrollController;\n  Timer? _timer;\n  ProviderSubscription? _pageLabelSubscription;\n\n  bool get _isForeground {\n    final lifecycleState = WidgetsBinding.instance.lifecycleState;\n    return lifecycleState == null || lifecycleState == AppLifecycleState.resumed;\n  }\n\n  @override\n  void initState() {\n    super.initState();\n    _scrollController = ScrollController();\n    WidgetsBinding.instance.addObserver(this);\n    globalState.backgroundMode.addListener(_handleBackgroundModeChanged);\n    if (system.isDesktop) {\n      windowManager.addListener(this);\n    }\n    _pageLabelSubscription = ref.listenManual(currentPageLabelProvider, (\n      prev,\n      next,\n    ) {\n      if (prev != next) {\n        unawaited(_syncUpdateTimer());\n      }\n    });\n    WidgetsBinding.instance.addPostFrameCallback((_) {\n      unawaited(_syncUpdateTimer());\n    });\n  }\n\n  @override\n  void dispose() {\n    _pageLabelSubscription?.close();\n    globalState.backgroundMode.removeListener(_handleBackgroundModeChanged);\n    if (system.isDesktop) {\n      windowManager.removeListener(this);\n    }\n    WidgetsBinding.instance.removeObserver(this);\n    _timer?.cancel();\n    _scrollController.dispose();\n    super.dispose();\n  }\n\n  Future<bool> _shouldRunTimer() async {\n    if (!mounted) return false;\n    if (globalState.backgroundMode.value) {\n      return false;\n    }\n    if (widget.respectCurrentPage &&\n        ref.read(currentPageLabelProvider) != PageLabel.connections) {\n      return false;\n    }\n    if (!_isForeground) {\n      return false;\n    }\n    if (system.isDesktop && await window?.isVisible == false) {\n      return false;\n    }\n    return true;\n  }\n\n  Future<void> _syncUpdateTimer() async {\n    final shouldRun = await _shouldRunTimer();\n    if (!mounted) return;\n    if (!shouldRun) {\n      _timer?.cancel();\n      _timer = null;\n      return;\n    }\n    if (_timer != null) {\n      return;\n    }\n    await _updateConnections();\n    if (!mounted || !await _shouldRunTimer()) {\n      return;\n    }\n    _timer?.cancel();\n    _timer = Timer.periodic(const Duration(seconds: 1), (_) {\n      unawaited(_updateConnections());\n    });\n  }\n\n  Future<void> _updateConnections() async {\n    if (!mounted || !await _shouldRunTimer()) {\n      _timer?.cancel();\n      _timer = null;\n      return;\n    }\n    final connections = await clashCore.getConnections();\n    if (!mounted || !await _shouldRunTimer()) {\n      _timer?.cancel();\n      _timer = null;\n      return;\n    }\n    ref.read(connectionsProvider.notifier).state = connections;\n  }\n\n  void _handleBackgroundModeChanged() {\n    unawaited(_syncUpdateTimer());\n  }\n\n  @override\n  void didChangeAppLifecycleState(AppLifecycleState state) {\n    unawaited(_syncUpdateTimer());\n  }\n\n  @override\n  void onWindowMinimize() {\n    unawaited(_syncUpdateTimer());\n  }\n\n  @override\n  void onWindowRestore() {\n    unawaited(_syncUpdateTimer());\n  }\n\n  Future<void> _handleBlockConnection(String id) async {\n    clashCore.closeConnection(id);\n    await _updateConnections();\n  }\n\n  void _handleCloseAll() async {\n    clashCore.closeConnections();\n    await _updateConnections();\n  }\n\n  void _onSearch(String value) {\n    ref.read(connectionsSearchProvider.notifier).state = value;\n  }\n\n  void _onKeywordsUpdate(List<String> keywords) {\n    ref.read(connectionsKeywordsProvider.notifier).state = keywords;\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return CommonScaffold(\n      title: appLocalizations.connections,\n      onKeywordsUpdate: _onKeywordsUpdate,\n      searchState: AppBarSearchState(onSearch: _onSearch),\n      actions: [\n        IconButton(\n          onPressed: _handleCloseAll,\n          icon: const Icon(Icons.delete_sweep_outlined),\n        ),\n      ],\n      body: Consumer(\n        builder: (_, ref, _) {\n          final connections = ref.watch(filteredConnectionsProvider);\n          final hasConnections = connections.isNotEmpty;\n\n          if (!hasConnections) {\n            return NullStatus(\n              label: appLocalizations.nullTip(appLocalizations.connections),\n            );\n          }\n\n          return CommonScrollBar(\n            controller: _scrollController,\n            child: ListView.builder(\n              controller: _scrollController,\n              itemBuilder: (context, index) {\n              if (index.isOdd) {\n                return const Divider(height: 0);\n              }\n              final itemIndex = index ~/ 2;\n              if (itemIndex >= connections.length) {\n                return const SizedBox.shrink();\n              }\n              final trackerInfo = connections[itemIndex];\n              return TrackerInfoItem(\n                key: ValueKey(trackerInfo.id),\n                trackerInfo: trackerInfo,\n                onClickKeyword: (value) {\n                  context.commonScaffoldState?.addKeyword(value);\n                },\n                trailing: IconButton(\n                  padding: EdgeInsets.zero,\n                  visualDensity: VisualDensity.compact,\n                  style: const ButtonStyle(\n                    minimumSize: WidgetStatePropertyAll(Size.zero),\n                  ),\n                  icon: const Icon(Icons.block),\n                  onPressed: () => _handleBlockConnection(trackerInfo.id),\n                ),\n                detailTitle: appLocalizations.details(\n                  appLocalizations.connection,\n                ),\n              );\n            },\n            itemExtentBuilder: (index, _) {\n              if (index.isOdd) {\n                return 0;\n              }\n              return TrackerInfoItem.height;\n            },\n              itemCount: connections.length * 2 - 1,\n            ),\n          );\n        },\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/connection/item.dart",
    "content": "import 'dart:typed_data';\n\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/plugins/app.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nfinal _iconCache = <String, Uint8List?>{};\nfinal _iconCacheKeys = <String>[];\nconst _maxIconCacheSize = 50;\nUint8List? _defaultIconCache;\nFuture<Uint8List?>? _defaultIconFuture;\n\nvoid _addToIconCache(String key, Uint8List? value) {\n  if (_iconCache.containsKey(key)) {\n    _iconCacheKeys.remove(key);\n    _iconCacheKeys.add(key);\n    _iconCache[key] = value;\n    return;\n  }\n\n  while (_iconCacheKeys.length >= _maxIconCacheSize) {\n    final oldestKey = _iconCacheKeys.removeAt(0);\n    _iconCache.remove(oldestKey);\n  }\n\n  _iconCacheKeys.add(key);\n  _iconCache[key] = value;\n}\n\nclass TrackerInfoItem extends ConsumerWidget {\n  final TrackerInfo trackerInfo;\n  final Function(String)? onClickKeyword;\n  final Widget? trailing;\n  final String detailTitle;\n\n  const TrackerInfoItem({\n    super.key,\n    required this.trackerInfo,\n    this.onClickKeyword,\n    this.trailing,\n    required this.detailTitle,\n  });\n\n  static double get subTitleHeight {\n    return globalState.measure.bodySmallHeight + 20;\n  }\n\n  static double get height {\n    final measure = globalState.measure;\n    return measure.bodyMediumHeight +\n        8 +\n        8 +\n        measure.bodyLargeHeight +\n        subTitleHeight +\n        16 * 2;\n  }\n\n  String _getSourceText(TrackerInfo trackerInfo) {\n    final progress = trackerInfo.progressText.isNotEmpty\n        ? '${trackerInfo.progressText} · '\n        : '';\n    final traffic = Traffic(up: trackerInfo.upload, down: trackerInfo.download);\n    return '$progress${traffic.toString()}';\n  }\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final value = ref.watch(\n      patchClashConfigProvider.select(\n        (state) =>\n            state.findProcessMode == FindProcessMode.always && system.isAndroid,\n      ),\n    );\n    final title = Column(\n      crossAxisAlignment: CrossAxisAlignment.start,\n      children: [\n        Row(\n          mainAxisSize: MainAxisSize.max,\n          mainAxisAlignment: MainAxisAlignment.spaceBetween,\n          spacing: 8,\n          children: [\n            Flexible(\n              child: Text(\n                trackerInfo.desc,\n                maxLines: 1,\n                overflow: TextOverflow.ellipsis,\n                style: context.textTheme.bodyLarge,\n              ),\n            ),\n            Text(\n              trackerInfo.start.lastUpdateTimeDesc,\n              style: context.textTheme.bodySmall?.copyWith(\n                color: context.colorScheme.onSurface.opacity60,\n              ),\n            ),\n          ],\n        ),\n        const SizedBox(height: 6),\n        Text(\n          _getSourceText(trackerInfo),\n          maxLines: 1,\n          overflow: TextOverflow.ellipsis,\n          style: context.textTheme.bodyMedium?.copyWith(\n            color: context.colorScheme.onSurfaceVariant,\n          ),\n        ),\n      ],\n    );\n    final subTitle = SizedBox(\n      height: subTitleHeight,\n      child: Row(\n        spacing: 8,\n        mainAxisSize: MainAxisSize.min,\n        mainAxisAlignment: MainAxisAlignment.spaceBetween,\n        children: [\n          Flexible(\n            child: ListView.separated(\n              separatorBuilder: (_, _) => SizedBox(width: 6),\n              padding: EdgeInsets.zero,\n              scrollDirection: Axis.horizontal,\n              itemCount: trackerInfo.chains.length,\n              itemBuilder: (_, index) {\n                final chain = trackerInfo.chains[index];\n                return CommonChip(\n                  label: chain,\n                  labelStyle: context.textTheme.bodySmall?.copyWith(\n                    color: context.colorScheme.onSurfaceVariant,\n                  ),\n                  onPressed: () {\n                    if (onClickKeyword == null) return;\n                    onClickKeyword!(chain);\n                  },\n                );\n              },\n            ),\n          ),\n          ?trailing,\n        ],\n      ),\n    );\n    final icon = value\n        ? _ProcessIcon(\n            process: trackerInfo.metadata.process,\n            onClick: onClickKeyword,\n          )\n        : null;\n    return RepaintBoundary(\n      child: ListItem(\n        padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),\n        onTap: () {\n        showExtend(\n          context,\n          builder: (_, type) {\n            return AdaptiveSheetScaffold(\n              type: type,\n              body: TrackerInfoDetailView(trackerInfo: trackerInfo),\n              title: detailTitle,\n            );\n          },\n        );\n      },\n      title: Column(\n        mainAxisSize: MainAxisSize.min,\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: [\n          Row(\n            mainAxisSize: MainAxisSize.min,\n            mainAxisAlignment: MainAxisAlignment.center,\n            spacing: 12,\n            children: [\n              ?icon,\n              Flexible(child: title),\n            ],\n          ),\n          const SizedBox(height: 8),\n          subTitle,\n        ],\n      ),\n      ),\n    );\n  }\n}\n\nFuture<Uint8List?> _getPackageIcon(String process) async {\n  if (process.isEmpty) {\n    return _getDefaultPackageIcon();\n  }\n  final cachedIcon = _iconCache[process];\n  if (cachedIcon != null) {\n    return cachedIcon;\n  }\n  final icon = await app.getPackageIcon(process);\n  if (icon != null) {\n    _addToIconCache(process, icon);\n    return icon;\n  }\n  return _getDefaultPackageIcon();\n}\n\nFuture<Uint8List?> _getDefaultPackageIcon() {\n  final cachedIcon = _defaultIconCache;\n  if (cachedIcon != null) {\n    return Future.value(cachedIcon);\n  }\n  return _defaultIconFuture ??= app.getPackageIcon('').then((icon) {\n    if (icon != null) {\n      _defaultIconCache = icon;\n    }\n    _defaultIconFuture = null;\n    return icon;\n  });\n}\n\nclass _ProcessIcon extends StatefulWidget {\n  final String process;\n  final Function(String)? onClick;\n\n  const _ProcessIcon({required this.process, this.onClick});\n\n  @override\n  State<_ProcessIcon> createState() => _ProcessIconState();\n}\n\nclass _ProcessIconState extends State<_ProcessIcon> {\n  late Future<Uint8List?> _iconFuture;\n\n  @override\n  void initState() {\n    super.initState();\n    _iconFuture = _getPackageIcon(widget.process);\n  }\n\n  @override\n  void didUpdateWidget(_ProcessIcon oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    if (oldWidget.process != widget.process) {\n      _iconFuture = _getPackageIcon(widget.process);\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final devicePixelRatio = MediaQuery.devicePixelRatioOf(context);\n    final cacheSize = (42 * devicePixelRatio).ceil();\n\n    return RepaintBoundary(\n      child: GestureDetector(\n        onTap: () {\n          if (widget.process.isEmpty) return;\n          widget.onClick?.call(widget.process);\n        },\n        child: Container(\n          margin: const EdgeInsets.only(top: 4),\n          width: 42,\n          height: 42,\n          alignment: Alignment.center,\n          child: FutureBuilder<Uint8List?>(\n            future: _iconFuture,\n            builder: (context, snapshot) {\n              final iconBytes = snapshot.data;\n              if (iconBytes == null) {\n                return const SizedBox(width: 42, height: 42);\n              }\n              return Image(\n                image: ResizeImage(\n                  MemoryImage(iconBytes),\n                  width: cacheSize,\n                  height: cacheSize,\n                  allowUpscaling: false,\n                ),\n                width: 42,\n                height: 42,\n                gaplessPlayback: true,\n              );\n            },\n          ),\n        ),\n      ),\n    );\n  }\n}\n\nclass TrackerInfoDetailView extends StatelessWidget {\n  final TrackerInfo trackerInfo;\n\n  const TrackerInfoDetailView({super.key, required this.trackerInfo});\n\n  String _getRuleText() {\n    final rule = trackerInfo.rule;\n    final rulePayload = trackerInfo.rulePayload;\n    if (rulePayload.isNotEmpty) {\n      return '$rule($rulePayload)';\n    }\n    return rule;\n  }\n\n  String _getProgressText() {\n    final process = trackerInfo.metadata.process;\n    final uid = trackerInfo.metadata.uid;\n    if (uid != 0) {\n      return '$process($uid)';\n    }\n    return process;\n  }\n\n  String _getSourceText() {\n    final sourceIP = trackerInfo.metadata.sourceIP;\n    if (sourceIP.isEmpty) {\n      return '';\n    }\n    final sourcePort = trackerInfo.metadata.sourcePort;\n    if (sourcePort.isNotEmpty) {\n      return '$sourceIP:$sourcePort';\n    }\n    return sourceIP;\n  }\n\n  String _getDestinationText() {\n    final destinationIP = trackerInfo.metadata.destinationIP;\n    if (destinationIP.isEmpty) {\n      return '';\n    }\n    final destinationPort = trackerInfo.metadata.destinationPort;\n    if (destinationPort.isNotEmpty) {\n      return '$destinationIP:$destinationPort';\n    }\n    return destinationIP;\n  }\n\n  Widget _buildChains() {\n    final chains = Wrap(\n      spacing: 8,\n      runSpacing: 8,\n      alignment: WrapAlignment.end,\n      children: [\n        for (final chain in trackerInfo.chains)\n          CommonChip(label: chain, onPressed: () {}),\n      ],\n    );\n    return ListItem(\n      title: Row(\n        mainAxisAlignment: MainAxisAlignment.spaceBetween,\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: [\n          Text(appLocalizations.proxyChains),\n          Flexible(child: chains),\n        ],\n      ),\n    );\n  }\n\n  Widget _buildItem({\n    required String title,\n    required String desc,\n    bool quickCopy = false,\n  }) {\n    return ListItem(\n      title: Row(\n        spacing: 16,\n        mainAxisAlignment: MainAxisAlignment.spaceBetween,\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: [\n          Row(\n            spacing: 4,\n            children: [\n              Text(title),\n              if (quickCopy)\n                Padding(\n                  padding: EdgeInsets.only(top: 4),\n                  child: IconButton(\n                    visualDensity: VisualDensity.compact,\n                    padding: EdgeInsets.zero,\n                    icon: Icon(Icons.content_copy, size: 18),\n                    onPressed: () {},\n                  ),\n                ),\n            ],\n          ),\n          Flexible(child: Text(desc, textAlign: TextAlign.end)),\n        ],\n      ),\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final items = [\n      _buildItem(\n        title: appLocalizations.creationTime,\n        desc: trackerInfo.start.showFull,\n      ),\n      if (_getProgressText().isNotEmpty)\n        _buildItem(title: appLocalizations.progress, desc: _getProgressText()),\n      _buildItem(\n        title: appLocalizations.networkType,\n        desc: trackerInfo.metadata.network,\n      ),\n      _buildItem(title: appLocalizations.rule, desc: _getRuleText()),\n      if (trackerInfo.metadata.host.isNotEmpty)\n        _buildItem(\n          title: appLocalizations.host,\n          desc: trackerInfo.metadata.host,\n        ),\n      if (_getSourceText().isNotEmpty)\n        _buildItem(title: appLocalizations.source, desc: _getSourceText()),\n      if (_getDestinationText().isNotEmpty)\n        _buildItem(\n          title: appLocalizations.destination,\n          desc: _getDestinationText(),\n        ),\n      _buildItem(\n        title: appLocalizations.upload,\n        desc: TrafficValue(value: trackerInfo.upload).show,\n      ),\n      _buildItem(\n        title: appLocalizations.download,\n        desc: TrafficValue(value: trackerInfo.download).show,\n      ),\n      if (trackerInfo.metadata.destinationGeoIP.isNotEmpty)\n        _buildItem(\n          title: appLocalizations.destinationGeoIP,\n          desc: trackerInfo.metadata.destinationGeoIP.join(' '),\n        ),\n      if (trackerInfo.metadata.destinationIPASN.isNotEmpty)\n        _buildItem(\n          title: appLocalizations.destinationIPASN,\n          desc: trackerInfo.metadata.destinationIPASN,\n        ),\n      if (trackerInfo.metadata.dnsMode != null)\n        _buildItem(\n          title: appLocalizations.dnsMode,\n          desc: trackerInfo.metadata.dnsMode!.name,\n        ),\n      if (trackerInfo.metadata.specialProxy.isNotEmpty)\n        _buildItem(\n          title: appLocalizations.specialProxy,\n          desc: trackerInfo.metadata.specialProxy,\n        ),\n      if (trackerInfo.metadata.specialRules.isNotEmpty)\n        _buildItem(\n          title: appLocalizations.specialRules,\n          desc: trackerInfo.metadata.specialRules,\n        ),\n      if (trackerInfo.metadata.remoteDestination.isNotEmpty)\n        _buildItem(\n          title: appLocalizations.remoteDestination,\n          desc: trackerInfo.metadata.remoteDestination,\n        ),\n      _buildChains(),\n    ];\n    return SelectionArea(\n      child: ListView.builder(\n        padding: EdgeInsets.symmetric(vertical: 12),\n        itemCount: items.length,\n        itemBuilder: (_, index) {\n          return items[index];\n        },\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/connection/requests.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/providers/providers.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nimport 'item.dart';\n\nclass RequestsView extends ConsumerStatefulWidget {\n  const RequestsView({super.key});\n\n  @override\n  ConsumerState<RequestsView> createState() => _RequestsViewState();\n}\n\nclass _RequestsViewState extends ConsumerState<RequestsView> {\n  late final ScrollController _scrollController;\n  var _autoScrollToEnd = false;\n\n  @override\n  void initState() {\n    super.initState();\n    final requests = globalState.appState.requests.list;\n    _scrollController = ScrollController(\n      initialScrollOffset: requests.length * TrackerInfoItem.height,\n    );\n  }\n\n  @override\n  void dispose() {\n    _scrollController.dispose();\n    super.dispose();\n  }\n\n  void _onSearch(String value) {\n    ref.read(requestsSearchProvider.notifier).state = value;\n  }\n\n  void _onKeywordsUpdate(List<String> keywords) {\n    ref.read(requestsKeywordsProvider.notifier).state = keywords;\n  }\n\n  void _toggleAutoScroll() {\n    setState(() {\n      _autoScrollToEnd = !_autoScrollToEnd;\n    });\n  }\n\n  void _cancelAutoScroll() {\n    if (_autoScrollToEnd) {\n      setState(() {\n        _autoScrollToEnd = false;\n      });\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final requests = ref.watch(filteredRequestsProvider);\n    final hasRequests = requests.isNotEmpty;\n\n    return CommonScaffold(\n      title: appLocalizations.requests,\n      actions: [\n        IconButton(\n          onPressed: () {\n            ref.read(requestsProvider.notifier).clearRequests();\n          },\n          icon: const Icon(Icons.delete_sweep_outlined),\n        ),\n        IconButton(\n          style: _autoScrollToEnd\n              ? ButtonStyle(\n                  backgroundColor: WidgetStatePropertyAll(\n                    context.colorScheme.secondaryContainer,\n                  ),\n                )\n              : null,\n          onPressed: _toggleAutoScroll,\n          icon: const Icon(Icons.vertical_align_top_outlined),\n        ),\n      ],\n      searchState: AppBarSearchState(onSearch: _onSearch),\n      onKeywordsUpdate: _onKeywordsUpdate,\n      body: !hasRequests\n          ? NullStatus(\n              label: appLocalizations.nullTip(appLocalizations.requests),\n            )\n          : Align(\n              alignment: Alignment.topCenter,\n              child: CommonScrollBar(\n                trackVisibility: false,\n                controller: _scrollController,\n                child: ScrollToEndBox(\n                  controller: _scrollController,\n                  dataSource: requests,\n                  enable: _autoScrollToEnd,\n                  onCancelToEnd: _cancelAutoScroll,\n                  child: LayoutBuilder(\n                    builder: (context, constraints) {\n                      final contentHeight = requests.length * TrackerInfoItem.height;\n                      final listViewHeight = contentHeight < constraints.maxHeight\n                          ? contentHeight\n                          : constraints.maxHeight;\n\n                      return SizedBox(\n                        height: listViewHeight,\n                        child: ListView.builder(\n                          reverse: true,\n                          physics: const NextClampingScrollPhysics(),\n                          controller: _scrollController,\n                          itemBuilder: (_, index) {\n                            if (index.isOdd) {\n                              return const Divider(height: 0);\n                            }\n                            final itemIndex = index ~/ 2;\n                            if (itemIndex >= requests.length) {\n                              return const SizedBox.shrink();\n                            }\n                            final trackerInfo = requests[itemIndex];\n                            return TrackerInfoItem(\n                              key: ValueKey(trackerInfo.id),\n                              trackerInfo: trackerInfo,\n                              onClickKeyword: (value) {\n                                context.commonScaffoldState?.addKeyword(value);\n                              },\n                              detailTitle: appLocalizations.details(\n                                appLocalizations.request,\n                              ),\n                            );\n                          },\n                          itemExtentBuilder: (index, _) {\n                            if (index.isOdd) {\n                              return 0;\n                            }\n                            return TrackerInfoItem.height;\n                          },\n                          itemCount: requests.length * 2 - 1,\n                        ),\n                      );\n                    },\n                  ),\n                ),\n              ),\n            ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/dashboard/dashboard.dart",
    "content": "import 'dart:math';\n\nimport 'package:bett_box/state.dart';\n\nimport 'package:defer_pointer/defer_pointer.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/providers/providers.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nfinal customDashboardTitleProvider =\n    StateNotifierProvider<CustomDashboardTitleNotifier, String?>((ref) {\n      return CustomDashboardTitleNotifier();\n    });\n\nclass CustomDashboardTitleNotifier extends StateNotifier<String?> {\n  CustomDashboardTitleNotifier() : super(null) {\n    _init();\n  }\n\n  Future<void> _init() async {\n    final prefs = await preferences.sharedPreferencesCompleter.future;\n    state = prefs?.getString(customDashboardTitleKey);\n  }\n\n  Future<void> updateTitle(String? title) async {\n    state = title;\n    final prefs = await preferences.sharedPreferencesCompleter.future;\n    if (title == null) {\n      prefs?.remove(customDashboardTitleKey);\n    } else {\n      prefs?.setString(customDashboardTitleKey, title);\n    }\n  }\n}\n\ntypedef _IsEditWidgetBuilder = Widget Function(bool isEdit);\n\nclass DashboardView extends ConsumerStatefulWidget {\n  const DashboardView({super.key});\n\n  @override\n  ConsumerState<DashboardView> createState() => _DashboardViewState();\n}\n\nclass _DashboardViewState extends ConsumerState<DashboardView> {\n  final key = GlobalKey<SuperGridState>();\n  final _isEditNotifier = ValueNotifier<bool>(false);\n  final _addedWidgetsNotifier = ValueNotifier<List<GridItem>>([]);\n\n  @override\n  dispose() {\n    _isEditNotifier.dispose();\n    _addedWidgetsNotifier.dispose();\n    super.dispose();\n  }\n\n  Widget _buildIsEdit(_IsEditWidgetBuilder builder) {\n    return ValueListenableBuilder(\n      valueListenable: _isEditNotifier,\n      builder: (_, isEdit, _) {\n        return builder(isEdit);\n      },\n    );\n  }\n\n  List<Widget> _buildActions() {\n    return [\n      _buildIsEdit((isEdit) {\n        return isEdit\n            ? ValueListenableBuilder(\n                valueListenable: _addedWidgetsNotifier,\n                builder: (_, addedChildren, child) {\n                  if (addedChildren.isEmpty) {\n                    return Container();\n                  }\n                  return child!;\n                },\n                child: IconButton(\n                  onPressed: () {\n                    _showAddWidgetsModal();\n                  },\n                  icon: Icon(Icons.add_circle),\n                ),\n              )\n            : SizedBox();\n      }),\n      Material(\n        type: MaterialType.transparency,\n        shape: const CircleBorder(),\n        clipBehavior: Clip.hardEdge,\n        child: InkWell(\n          onTap: _handleUpdateIsEdit,\n          onLongPress: () {\n            if (!_isEditNotifier.value) {\n              _showEditTitleDialog();\n            }\n          },\n          child: Padding(\n            padding: const EdgeInsets.all(12.0),\n            child: _buildIsEdit((isEdit) {\n              return isEdit ? const Icon(Icons.save) : const Icon(Icons.edit);\n            }),\n          ),\n        ),\n      ),\n    ];\n  }\n\n  void _showAddWidgetsModal() {\n    showSheet(\n      builder: (_, type) {\n        return ValueListenableBuilder(\n          valueListenable: _addedWidgetsNotifier,\n          builder: (_, value, _) {\n            return AdaptiveSheetScaffold(\n              type: type,\n              body: _AddDashboardWidgetModal(\n                items: value,\n                onAdd: (gridItem) {\n                  key.currentState?.handleAdd(gridItem);\n                },\n              ),\n              title: appLocalizations.add,\n            );\n          },\n        );\n      },\n      context: context,\n    );\n  }\n\n  void _showEditTitleDialog() async {\n    final currentTitle = ref.read(customDashboardTitleProvider) ?? '';\n    final title = await globalState.showCommonDialog<String>(\n      child: _DashboardTitleDialog(initialValue: currentTitle),\n    );\n    if (title != null) {\n      ref\n          .read(customDashboardTitleProvider.notifier)\n          .updateTitle(title.isEmpty ? null : title);\n    }\n  }\n\n  void _handleUpdateIsEdit() {\n    if (_isEditNotifier.value == true) {\n      _handleSave();\n    }\n    _isEditNotifier.value = !_isEditNotifier.value;\n  }\n\n  void _handleSave() {\n    final children = key.currentState?.children;\n    if (children == null) {\n      return;\n    }\n    WidgetsBinding.instance.addPostFrameCallback((_) {\n      final dashboardWidgets = children\n          .map((item) => DashboardWidget.getDashboardWidget(item))\n          .toList();\n      ref\n          .read(appSettingProvider.notifier)\n          .updateState(\n            (state) => state.copyWith(dashboardWidgets: dashboardWidgets),\n          );\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final dashboardState = ref.watch(dashboardStateProvider);\n    final columns = max(4 * ((dashboardState.viewWidth / 320).ceil()), 8);\n    final spacing = 16.ap;\n    final children = [\n      ...dashboardState.dashboardWidgets\n          .where(\n            (item) => item.platforms.contains(SupportPlatform.currentPlatform),\n          )\n          .map((item) => item.widget),\n    ];\n    WidgetsBinding.instance.addPostFrameCallback((_) {\n      _addedWidgetsNotifier.value = DashboardWidget.values\n          .where(\n            (item) =>\n                !children.contains(item.widget) &&\n                item.platforms.contains(SupportPlatform.currentPlatform),\n          )\n          .map((item) => item.widget)\n          .toList();\n    });\n    return CommonScaffold(\n      title:\n          ref.watch(customDashboardTitleProvider) ?? appLocalizations.dashboard,\n      actions: _buildActions(),\n      body: Align(\n        alignment: Alignment.topCenter,\n        child: SingleChildScrollView(\n          padding: const EdgeInsets.all(16).copyWith(bottom: 16),\n          child: _buildIsEdit((isEdit) {\n            if (isEdit) {\n              return SystemBackBlock(\n                child: CommonPopScope(\n                  child: Column(\n                    crossAxisAlignment: CrossAxisAlignment.start,\n                    children: [\n                      SuperGrid(\n                        key: key,\n                        crossAxisCount: columns,\n                        crossAxisSpacing: spacing,\n                        mainAxisSpacing: spacing,\n                        children: [\n                          ...dashboardState.dashboardWidgets\n                              .where(\n                                (item) => item.platforms.contains(\n                                  SupportPlatform.currentPlatform,\n                                ),\n                              )\n                              .map((item) => item.widget),\n                        ],\n                        onUpdate: () {\n                          _handleSave();\n                        },\n                      ),\n                    ],\n                  ),\n                  onPop: () {\n                    _handleUpdateIsEdit();\n                    return false;\n                  },\n                ),\n              );\n            } else {\n              return Grid(\n                crossAxisCount: columns,\n                crossAxisSpacing: spacing,\n                mainAxisSpacing: spacing,\n                children: [...children],\n              );\n            }\n          }),\n        ),\n      ),\n    );\n  }\n}\n\nclass _AddDashboardWidgetModal extends StatelessWidget {\n  final List<GridItem> items;\n  final Function(GridItem item) onAdd;\n\n  const _AddDashboardWidgetModal({required this.items, required this.onAdd});\n\n  @override\n  Widget build(BuildContext context) {\n    return DeferredPointerHandler(\n      child: SingleChildScrollView(\n        padding: EdgeInsets.all(16),\n        child: Grid(\n          crossAxisCount: 8,\n          crossAxisSpacing: 16,\n          mainAxisSpacing: 16,\n          children: items\n              .map(\n                (item) => item.wrap(\n                  builder: (child) {\n                    return _AddedContainer(\n                      onAdd: () {\n                        onAdd(item);\n                      },\n                      child: child,\n                    );\n                  },\n                ),\n              )\n              .toList(),\n        ),\n      ),\n    );\n  }\n}\n\nclass _AddedContainer extends StatefulWidget {\n  final Widget child;\n  final VoidCallback onAdd;\n\n  const _AddedContainer({required this.child, required this.onAdd});\n\n  @override\n  State<_AddedContainer> createState() => _AddedContainerState();\n}\n\nclass _AddedContainerState extends State<_AddedContainer> {\n  @override\n  void initState() {\n    super.initState();\n  }\n\n  @override\n  void didUpdateWidget(_AddedContainer oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    if (oldWidget.child != widget.child) {}\n  }\n\n  Future<void> _handleAdd() async {\n    widget.onAdd();\n  }\n\n  @override\n  void dispose() {\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Stack(\n      clipBehavior: Clip.none,\n      children: [\n        ActivateBox(child: widget.child),\n        Positioned(\n          top: -8,\n          right: -8,\n          child: DeferPointer(\n            child: SizedBox(\n              width: 24,\n              height: 24,\n              child: IconButton.filled(\n                iconSize: 20,\n                padding: EdgeInsets.all(2),\n                onPressed: _handleAdd,\n                icon: Icon(Icons.add),\n              ),\n            ),\n          ),\n        ),\n      ],\n    );\n  }\n}\n\nclass _DashboardTitleDialog extends StatefulWidget {\n  final String initialValue;\n\n  const _DashboardTitleDialog({required this.initialValue});\n\n  @override\n  State<_DashboardTitleDialog> createState() => _DashboardTitleDialogState();\n}\n\nclass _DashboardTitleDialogState extends State<_DashboardTitleDialog> {\n  late final TextEditingController _controller;\n  String? _errorText;\n\n  @override\n  void initState() {\n    super.initState();\n    _controller = TextEditingController(text: widget.initialValue);\n  }\n\n  @override\n  void dispose() {\n    _controller.dispose();\n    super.dispose();\n  }\n\n  void _validate(String value) {\n    int len = 0;\n    for (int i = 0; i < value.length; i++) {\n      len += value.codeUnitAt(i) > 127 ? 2 : 1;\n    }\n    setState(() {\n      if (len > 12) {\n        _errorText = 'Too long (Max 6 Chinese or 12 English)';\n      } else {\n        _errorText = null;\n      }\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return CommonDialog(\n      title: '^_^',\n      actions: [\n        TextButton(\n          onPressed: () {\n            Navigator.of(context).pop();\n          },\n          child: Text(appLocalizations.cancel),\n        ),\n        TextButton(\n          onPressed: _errorText == null\n              ? () {\n                  Navigator.of(context).pop(_controller.text);\n                }\n              : null,\n          child: Text(appLocalizations.confirm),\n        ),\n      ],\n      child: Padding(\n        padding: const EdgeInsets.only(top: 8.0),\n        child: TextField(\n          controller: _controller,\n          decoration: InputDecoration(\n            hintText: 'Have fun with Bettbox',\n            errorText: _errorText,\n            border: const OutlineInputBorder(),\n          ),\n          onChanged: _validate,\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/dashboard/widgets/connections_count.dart",
    "content": "import 'dart:async';\n\nimport 'package:bett_box/clash/clash.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/views/connection/connections.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\n\nclass ConnectionsCount extends StatefulWidget {\n  const ConnectionsCount({super.key});\n\n  @override\n  State<ConnectionsCount> createState() => _ConnectionsCountState();\n}\n\nclass _ConnectionsCountState extends State<ConnectionsCount> {\n  int _count = 0;\n  late final VoidCallback _tickListener;\n  Timer? _initTimer;\n  bool _isUpdating = false;\n\n  @override\n  void initState() {\n    super.initState();\n    _tickListener = _updateConnections;\n    dashboardRefreshManager.tick2s.addListener(_tickListener);\n    _initTimer = Timer(const Duration(milliseconds: 1000), _updateConnections);\n  }\n\n  @override\n  void dispose() {\n    _initTimer?.cancel();\n    dashboardRefreshManager.tick2s.removeListener(_tickListener);\n    super.dispose();\n  }\n\n  Future<void> _updateConnections() async {\n    if (!mounted) return;\n    if (_isUpdating) return;\n    _isUpdating = true;\n\n    try {\n      final connections = await clashCore.getConnections();\n      if (mounted) {\n        setState(() {\n          _count = connections.length;\n        });\n      }\n    } catch (e) {\n      // Ignore error, keep current value\n    } finally {\n      _isUpdating = false;\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return SizedBox(\n      height: getWidgetHeight(1),\n      child: CommonCard(\n        info: Info(iconData: Icons.ballot, label: appLocalizations.connection),\n        onPressed: () {\n          showExtend(\n            context,\n            builder: (_, type) {\n              return const ConnectionsView(respectCurrentPage: false);\n            },\n          );\n        },\n        child: Container(\n          padding: baseInfoEdgeInsets.copyWith(top: 0),\n          child: Align(\n            alignment: Alignment.bottomLeft,\n            child: Row(\n              mainAxisAlignment: MainAxisAlignment.start,\n              crossAxisAlignment: CrossAxisAlignment.baseline,\n              textBaseline: TextBaseline.alphabetic,\n              children: [\n                Text(\n                  '$_count',\n                  style: context.textTheme.bodyLarge?.toLight.adjustSize(2),\n                ),\n                const SizedBox(width: 4),\n                Text(\n                  ' Connections',\n                  style: context.textTheme.bodyMedium?.toLight.adjustSize(0),\n                ),\n              ],\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/dashboard/widgets/dns_override.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:bett_box/clash/core.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/views/config/dns.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass DnsOverride extends StatelessWidget {\n  const DnsOverride({super.key});\n\n  Future<void> _handleClearCache(BuildContext context) async {\n    // Show confirmation dialog\n    final result = await globalState.showCommonDialog<bool>(\n      child: CommonDialog(\n        title: appLocalizations.clearCacheTitle,\n        actions: [\n          TextButton(\n            onPressed: () {\n              Navigator.of(context, rootNavigator: true).pop(false);\n            },\n            child: Text(appLocalizations.cancel),\n          ),\n          TextButton(\n            onPressed: () {\n              Navigator.of(context, rootNavigator: true).pop(true);\n            },\n            child: Text(appLocalizations.confirm),\n          ),\n        ],\n        child: Text(appLocalizations.clearCacheDesc),\n      ),\n    );\n\n    // Clear cache after user confirms\n    if (result == true) {\n      await clashCore.flushFakeIP();\n      await clashCore.flushDnsCache();\n      globalState.showNotifier(appLocalizations.success);\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return SizedBox(\n      height: getWidgetHeight(1),\n      child: CommonCard(\n        info: Info(label: 'DNS', iconData: Icons.dns),\n        onPressed: () {\n          // Open DNS settings\n          showExtend(\n            context,\n            builder: (_, type) {\n              return AdaptiveSheetScaffold(\n                type: type,\n                title: 'DNS',\n                body: const DnsListView(),\n              );\n            },\n            props: const ExtendProps(blur: false),\n          );\n        },\n        onLongPress: () async {\n          // Long press: clear DNS cache\n          await _handleClearCache(context);\n        },\n        child: Container(\n          padding: baseInfoEdgeInsets.copyWith(top: 4, bottom: 8, right: 8),\n          child: Row(\n            mainAxisSize: MainAxisSize.max,\n            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n            children: [\n              Flexible(\n                flex: 1,\n                child: TooltipText(\n                  text: Text(\n                    appLocalizations.override,\n                    maxLines: 1,\n                    overflow: TextOverflow.ellipsis,\n                    style: Theme.of(\n                      context,\n                    ).textTheme.titleSmall?.adjustSize(-2).toLight,\n                  ),\n                ),\n              ),\n              Consumer(\n                builder: (_, ref, _) {\n                  final override = ref.watch(overrideDnsProvider);\n                  return Switch(\n                    materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,\n                    value: override,\n                    onChanged: (value) {\n                      ref.read(overrideDnsProvider.notifier).value = value;\n                    },\n                  );\n                },\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/dashboard/widgets/fcm_status.dart",
    "content": "import 'dart:async';\n\nimport 'package:bett_box/clash/clash.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\n\nclass FcmStatusData {\n  final int? minutes;\n  final bool isConnected;\n\n  const FcmStatusData({this.minutes, required this.isConnected});\n}\n\nfinal _fcmStatusNotifier = ValueNotifier<FcmStatusData>(\n  const FcmStatusData(isConnected: false),\n);\n\nclass FcmStatus extends StatefulWidget {\n  const FcmStatus({super.key});\n\n  @override\n  State<FcmStatus> createState() => _FcmStatusState();\n}\n\nclass _FcmStatusState extends State<FcmStatus> {\n  late final VoidCallback _tickListener;\n  Timer? _initTimer;\n  bool _isUpdating = false;\n\n  @override\n  void initState() {\n    super.initState();\n    _tickListener = _updateFcmStatus;\n    dashboardRefreshManager.tick5s.addListener(_tickListener);\n    _initTimer = Timer(const Duration(milliseconds: 1000), _updateFcmStatus);\n  }\n\n  @override\n  void dispose() {\n    _initTimer?.cancel();\n    dashboardRefreshManager.tick5s.removeListener(_tickListener);\n    super.dispose();\n  }\n\n  Future<void> _updateFcmStatus() async {\n    if (!mounted) return;\n    if (_isUpdating) return;\n    _isUpdating = true;\n\n    try {\n      final connections = await clashCore.getConnections();\n\n      final fcmConnections = connections.where((conn) {\n        final host = conn.metadata.host.toLowerCase();\n        final port = conn.metadata.destinationPort;\n        return host.contains('google.com') &&\n            (port == '5228' || port == '5229' || port == '5230');\n      }).toList();\n\n      if (fcmConnections.isEmpty) {\n        _fcmStatusNotifier.value = const FcmStatusData(isConnected: false);\n      } else {\n        final longestConnection = fcmConnections.reduce(\n          (a, b) => a.start.isBefore(b.start) ? a : b,\n        );\n        final duration = DateTime.now().difference(longestConnection.start);\n        final minutes = duration.inMinutes;\n        _fcmStatusNotifier.value = FcmStatusData(\n          minutes: minutes,\n          isConnected: true,\n        );\n      }\n    } catch (e) {\n      // Ignore error, keep current value\n    } finally {\n      _isUpdating = false;\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return RepaintBoundary(\n      child: SizedBox(\n        height: getWidgetHeight(1),\n        child: CommonCard(\n          info: const Info(iconData: Icons.cloud_outlined, label: 'FCM'),\n          onPressed: () async {\n            // Show info dialog\n            await globalState.showCommonDialog<void>(\n              child: CommonDialog(\n                title: 'FCM',\n                actions: [\n                  TextButton(\n                    onPressed: () {\n                      Navigator.of(context, rootNavigator: true).pop();\n                    },\n                    child: Text(appLocalizations.confirm),\n                  ),\n                ],\n                child: Text(appLocalizations.fcmTip),\n              ),\n            );\n          },\n          child: Container(\n            padding: baseInfoEdgeInsets.copyWith(top: 0),\n            child: Align(\n              alignment: Alignment.bottomLeft,\n              child: ValueListenableBuilder<FcmStatusData>(\n                valueListenable: _fcmStatusNotifier,\n                builder: (_, status, _) {\n                  if (status.isConnected && status.minutes != null) {\n                    return Row(\n                      mainAxisAlignment: MainAxisAlignment.start,\n                      crossAxisAlignment: CrossAxisAlignment.baseline,\n                      textBaseline: TextBaseline.alphabetic,\n                      children: [\n                        Text(\n                          '${status.minutes}',\n                          style: context.textTheme.bodyLarge?.toLight\n                              .adjustSize(2),\n                        ),\n                        const SizedBox(width: 4),\n                        Text(\n                          ' Minutes',\n                          style: context.textTheme.bodyMedium?.toLight\n                              .adjustSize(0),\n                        ),\n                      ],\n                    );\n                  } else {\n                    return Text(\n                      appLocalizations.noStatusAvailable,\n                      style: context.textTheme.bodyMedium?.toLight.adjustSize(\n                        0,\n                      ),\n                    );\n                  }\n                },\n              ),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/dashboard/widgets/intranet_ip.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/providers/app.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass IntranetIP extends StatelessWidget {\n  const IntranetIP({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return SizedBox(\n      height: getWidgetHeight(1),\n      child: CommonCard(\n        info: Info(label: appLocalizations.intranetIP, iconData: Icons.devices),\n        onPressed: () {},\n        child: Container(\n          padding: baseInfoEdgeInsets.copyWith(top: 0),\n          child: Column(\n            mainAxisSize: MainAxisSize.max,\n            mainAxisAlignment: MainAxisAlignment.end,\n            children: [\n              SizedBox(\n                height: globalState.measure.bodyMediumHeight + 2,\n                child: Consumer(\n                  builder: (_, ref, _) {\n                    final localIp = ref.watch(localIpProvider);\n                    return FadeThroughBox(\n                      child: localIp != null\n                          ? TooltipText(\n                              text: Text(\n                                localIp.isNotEmpty\n                                    ? localIp\n                                    : appLocalizations.noNetwork,\n                                style: context.textTheme.bodyMedium?.toLight\n                                    .adjustSize(1),\n                                maxLines: 1,\n                                overflow: TextOverflow.ellipsis,\n                              ),\n                            )\n                          : Container(\n                              padding: EdgeInsets.all(2),\n                              child: AspectRatio(\n                                aspectRatio: 1,\n                                child: CircularProgressIndicator(\n                                  strokeWidth: 2,\n                                ),\n                              ),\n                            ),\n                    );\n                  },\n                ),\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/dashboard/widgets/ipv6_switch.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:bett_box/views/config/general.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass Ipv6Switch extends StatelessWidget {\n  const Ipv6Switch({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return SizedBox(\n      height: getWidgetHeight(1),\n      child: CommonCard(\n        info: Info(label: 'IPv6', iconData: Icons.filter_6_rounded),\n        onPressed: () {\n          // Open general settings\n          showExtend(\n            context,\n            builder: (_, type) {\n              return AdaptiveSheetScaffold(\n                type: type,\n                title: appLocalizations.general,\n                body: generateListView(generalItems),\n              );\n            },\n            props: const ExtendProps(blur: false),\n          );\n        },\n        child: Container(\n          padding: baseInfoEdgeInsets.copyWith(top: 4, bottom: 8, right: 8),\n          child: Row(\n            mainAxisSize: MainAxisSize.max,\n            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n            children: [\n              Flexible(\n                flex: 1,\n                child: TooltipText(\n                  text: Text(\n                    appLocalizations.switchLabel,\n                    maxLines: 1,\n                    overflow: TextOverflow.ellipsis,\n                    style: Theme.of(\n                      context,\n                    ).textTheme.titleSmall?.adjustSize(-2).toLight,\n                  ),\n                ),\n              ),\n              Consumer(\n                builder: (_, ref, _) {\n                  final ipv6 = ref.watch(\n                    patchClashConfigProvider.select((state) => state.ipv6),\n                  );\n                  return Switch(\n                    materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,\n                    value: ipv6,\n                    onChanged: (value) {\n                      ref\n                          .read(patchClashConfigProvider.notifier)\n                          .updateState((state) => state.copyWith(ipv6: value));\n                    },\n                  );\n                },\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/dashboard/widgets/memory_info.dart",
    "content": "import 'dart:async';\n\nimport 'package:bett_box/clash/clash.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/models/common.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\n\nclass MemoryInfo extends StatefulWidget {\n  const MemoryInfo({super.key});\n\n  @override\n  State<MemoryInfo> createState() => _MemoryInfoState();\n}\n\nclass _MemoryInfoState extends State<MemoryInfo> {\n  late final VoidCallback _tickListener;\n  // Cache last memory value to avoid showing 0 on rebuild\n  static TrafficValue _lastMemoryValue = TrafficValue(value: 0);\n  TrafficValue _memoryValue = _lastMemoryValue;\n  Timer? _initTimer;\n  bool _isUpdating = false;\n\n  @override\n  void initState() {\n    super.initState();\n    _tickListener = _updateMemory;\n    dashboardRefreshManager.tick2s.addListener(_tickListener);\n\n    // Get immediately on first open, otherwise delay 1000ms\n    if (_lastMemoryValue.value == 0) {\n      _retryUpdateMemory();\n    } else {\n      // Has cached value, delay\n      _initTimer = Timer(const Duration(milliseconds: 1000), _updateMemory);\n    }\n  }\n\n  Future<void> _retryUpdateMemory() async {\n    for (int i = 0; i < 5; i++) {\n      if (!mounted) return;\n      await _updateMemory();\n      if (_memoryValue.value > 0) break;\n      await Future.delayed(const Duration(milliseconds: 500));\n    }\n  }\n\n  @override\n  void dispose() {\n    _initTimer?.cancel();\n    dashboardRefreshManager.tick2s.removeListener(_tickListener);\n    super.dispose();\n  }\n\n  Future<void> _updateMemory() async {\n    if (!mounted) return;\n    if (_isUpdating) return;\n    _isUpdating = true;\n\n    try {\n      final memoryValue = await clashCore.getMemory();\n      // Update only if valid (non-zero)\n      if (memoryValue > 0) {\n        final adjustedValue = memoryValue;\n\n        if (mounted) {\n          setState(() {\n            _memoryValue = TrafficValue(value: adjustedValue);\n            _lastMemoryValue = _memoryValue; // Cache latest valid value\n          });\n        }\n      }\n      // If 0, keep last valid value\n    } catch (e) {\n      // Ignore error, keep current value\n    } finally {\n      _isUpdating = false;\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return SizedBox(\n      height: getWidgetHeight(1),\n      child: CommonCard(\n        info: Info(iconData: Icons.memory, label: appLocalizations.memoryInfo),\n        onLongPress: () async {\n          // Show confirmation dialog\n          final result = await globalState.showCommonDialog<bool>(\n            child: CommonDialog(\n              title: appLocalizations.forceGCTitle,\n              actions: [\n                TextButton(\n                  onPressed: () {\n                    Navigator.of(context, rootNavigator: true).pop(false);\n                  },\n                  child: Text(appLocalizations.cancel),\n                ),\n                TextButton(\n                  onPressed: () {\n                    Navigator.of(context, rootNavigator: true).pop(true);\n                  },\n                  child: Text(appLocalizations.confirm),\n                ),\n              ],\n              child: Text(appLocalizations.forceGCDesc),\n            ),\n          );\n\n          // Execute force GC after user confirms\n          if (result == true) {\n            await clashCore.requestGc();\n            globalState.showNotifier(appLocalizations.success);\n          }\n        },\n        child: Container(\n          padding: baseInfoEdgeInsets.copyWith(top: 0),\n          child: Column(\n            mainAxisSize: MainAxisSize.max,\n            mainAxisAlignment: MainAxisAlignment.end,\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: [\n              SizedBox(\n                height: globalState.measure.bodyMediumHeight + 2,\n                child: Row(\n                  mainAxisAlignment: MainAxisAlignment.start,\n                  children: [\n                    Text(\n                      _memoryValue.showValue,\n                      style: context.textTheme.bodyMedium?.toLight.adjustSize(\n                        1,\n                      ),\n                    ),\n                    SizedBox(width: 8),\n                    Text(\n                      _memoryValue.showUnit,\n                      style: context.textTheme.bodyMedium?.toLight.adjustSize(\n                        1,\n                      ),\n                    ),\n                  ],\n                ),\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}"
  },
  {
    "path": "lib/views/dashboard/widgets/network_detection.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass NetworkDetection extends ConsumerStatefulWidget {\n  const NetworkDetection({super.key});\n\n  @override\n  ConsumerState<NetworkDetection> createState() => _NetworkDetectionState();\n}\n\nclass _NetworkDetectionState extends ConsumerState<NetworkDetection> {\n  String _countryCodeToEmoji(String countryCode) {\n    final String code = countryCode.toUpperCase();\n    if (code.length != 2) {\n      return countryCode;\n    }\n    final int firstLetter = code.codeUnitAt(0) - 0x41 + 0x1F1E6;\n    final int secondLetter = code.codeUnitAt(1) - 0x41 + 0x1F1E6;\n    return String.fromCharCode(firstLetter) + String.fromCharCode(secondLetter);\n  }\n\n  void _showIpClickBehaviorSettings() {\n    globalState.showCommonDialog<IpClickBehavior>(\n      child: CommonDialog(\n        title: appLocalizations.ipClickBehavior,\n        child: Column(\n          mainAxisSize: MainAxisSize.min,\n          children: [\n            ListTile(\n              leading: Icon(Icons.sync),\n              title: Text(appLocalizations.manualRefreshIp),\n              onTap: () {\n                Navigator.of(context, rootNavigator: true).pop();\n                detectionState.manualRefresh();\n              },\n            ),\n            ListTile(\n              leading: Icon(Icons.public),\n              title: Text(appLocalizations.switchToDomesticIp),\n              onTap: () {\n                Navigator.of(context, rootNavigator: true).pop();\n                detectionState.switchToDomesticIp();\n              },\n            ),\n            ListTile(\n              leading: Icon(Icons.security),\n              title: Text(appLocalizations.ipPrivacyProtection),\n              onTap: () {\n                Navigator.of(context, rootNavigator: true).pop();\n                detectionState.toggleIpPrivacy();\n              },\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return SizedBox(\n      height: getWidgetHeight(1),\n      child: ValueListenableBuilder<NetworkDetectionState>(\n        valueListenable: detectionState.state,\n        builder: (_, state, _) {\n          final ipInfo = state.ipInfo;\n          final isLoading = state.isLoading;\n          return CommonCard(\n            onPressed: () {},\n            child: Column(\n              mainAxisAlignment: MainAxisAlignment.spaceBetween,\n              children: [\n                Container(\n                  height: globalState.measure.titleMediumHeight + 16,\n                  padding: baseInfoEdgeInsets.copyWith(bottom: 0),\n                  child: Row(\n                    mainAxisSize: MainAxisSize.max,\n                    children: [\n                      ipInfo != null\n                          ? Text(\n                              _countryCodeToEmoji(ipInfo.countryCode),\n                              style: Theme.of(context)\n                                  .textTheme\n                                  .titleMedium\n                                  ?.toLight\n                                  .copyWith(\n                                    fontFamily: FontFamily.twEmoji.value,\n                                  ),\n                            )\n                          : Icon(\n                              Icons.network_check,\n                              color: Theme.of(\n                                context,\n                              ).colorScheme.onSurfaceVariant,\n                            ),\n                      const SizedBox(width: 8),\n                      Flexible(\n                        flex: 1,\n                        child: TooltipText(\n                          text: Text(\n                            appLocalizations.networkDetection,\n                            maxLines: 1,\n                            overflow: TextOverflow.ellipsis,\n                            style: Theme.of(context).textTheme.titleSmall\n                                ?.copyWith(\n                                  color: context.colorScheme.onSurfaceVariant,\n                                ),\n                          ),\n                        ),\n                      ),\n                      SizedBox(width: 2),\n                      AspectRatio(\n                        aspectRatio: 1,\n                        child: IconButton(\n                          padding: EdgeInsets.zero,\n                          onPressed: _showIpClickBehaviorSettings,\n                          icon: Icon(\n                            size: 16.ap,\n                            Icons.settings_outlined,\n                            color: context.colorScheme.onSurfaceVariant,\n                          ),\n                        ),\n                      ),\n                    ],\n                  ),\n                ),\n                Container(\n                  padding: baseInfoEdgeInsets.copyWith(top: 0),\n                  child: SizedBox(\n                    height: globalState.measure.bodyMediumHeight + 2,\n                    child: FadeThroughBox(\n                      child: ipInfo != null\n                          ? TooltipText(\n                              text: Text(\n                                ipInfo.ip,\n                                style: context.textTheme.bodyMedium?.toLight\n                                    .adjustSize(1),\n                                maxLines: 1,\n                                overflow: TextOverflow.ellipsis,\n                              ),\n                            )\n                          : FadeThroughBox(\n                              child: isLoading == false && ipInfo == null\n                                  ? Text(\n                                      state.errorMessage ?? 'timeout',\n                                      style: context.textTheme.bodyMedium\n                                          ?.copyWith(color: Colors.red)\n                                          .adjustSize(1),\n                                      maxLines: 1,\n                                      overflow: TextOverflow.ellipsis,\n                                    )\n                                  : Container(\n                                      padding: const EdgeInsets.all(2),\n                                      child: const AspectRatio(\n                                        aspectRatio: 1,\n                                        child: CircularProgressIndicator(\n                                          strokeWidth: 2,\n                                        ),\n                                      ),\n                                    ),\n                            ),\n                    ),\n                  ),\n                ),\n              ],\n            ),\n          );\n        },\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/dashboard/widgets/network_speed.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/providers/app.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass NetworkSpeed extends ConsumerWidget {\n  const NetworkSpeed({super.key});\n\n  // Cache as const\n  static const _initPoints = [Point(0, 0), Point(1, 0)];\n\n  static List<Point> _getPoints(List<Traffic> traffics) {\n    if (traffics.isEmpty) return _initPoints;\n\n    // Pre-allocate array capacity\n    final totalLength = traffics.length + _initPoints.length;\n    final result = List<Point>.filled(totalLength, Point(0, 0));\n\n    // Assign init points\n    result[0] = _initPoints[0];\n    result[1] = _initPoints[1];\n\n    // Assign traffic points\n    for (int i = 0; i < traffics.length; i++) {\n      result[i + 2] = Point((i + 2).toDouble(), traffics[i].speed.toDouble());\n    }\n\n    return result;\n  }\n\n  static Traffic _getLastTraffic(List<Traffic> traffics) {\n    if (traffics.isEmpty) return Traffic();\n    return traffics.last;\n  }\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final color = context.colorScheme.onSurfaceVariant.opacity80;\n    final primaryColor = Theme.of(context).colorScheme.primary;\n    return SizedBox(\n      height: getWidgetHeight(2),\n      child: CommonCard(\n        onPressed: () {\n          globalState.openUrl('https://ptclspeed.speedtestcustom.com');\n        },\n        info: Info(\n          label: appLocalizations.networkSpeed,\n          iconData: Icons.speed_sharp,\n        ),\n        child: RepaintBoundary(\n          child: ValueListenableBuilder<int>(\n            valueListenable: dashboardRefreshManager.tick1s,\n            builder: (_, _, _) {\n              final traffics = ref.read(trafficsProvider).list;\n              final points = _getPoints(traffics);\n              final speedText = _getLastTraffic(traffics).toSpeedText();\n              return Stack(\n                children: [\n                  Positioned.fill(\n                    child: Padding(\n                      padding: const EdgeInsets.only(\n                        top: 16,\n                        left: 0,\n                        right: 0,\n                        bottom: 0,\n                      ),\n                      child: RepaintBoundary(\n                        child: LineChart(\n                          gradient: true,\n                          color: primaryColor,\n                          points: points,\n                        ),\n                      ),\n                    ),\n                  ),\n                  Positioned(\n                    top: 0,\n                    right: 0,\n                    child: Transform.translate(\n                      offset: const Offset(-16, -20),\n                      child: Text(\n                        speedText,\n                        style: context.textTheme.bodySmall?.copyWith(\n                          color: color,\n                        ),\n                      ),\n                    ),\n                  ),\n                ],\n              );\n            },\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/dashboard/widgets/network_speed_small.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/providers/app.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass NetworkSpeedSmall extends ConsumerWidget {\n  const NetworkSpeedSmall({super.key});\n\n  // Cache as const\n  static const _initPoints = [Point(0, 0), Point(1, 0)];\n\n  static List<Point> _getPoints(List<Traffic> traffics) {\n    if (traffics.isEmpty) return _initPoints;\n\n    // Pre-allocate array capacity\n    final totalLength = traffics.length + _initPoints.length;\n    final result = List<Point>.filled(totalLength, Point(0, 0));\n\n    // Assign init points\n    result[0] = _initPoints[0];\n    result[1] = _initPoints[1];\n\n    // Assign traffic points\n    for (int i = 0; i < traffics.length; i++) {\n      result[i + 2] = Point((i + 2).toDouble(), traffics[i].speed.toDouble());\n    }\n\n    return result;\n  }\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final primaryColor = Theme.of(context).colorScheme.primary;\n    return RepaintBoundary(\n      child: SizedBox(\n        height: getWidgetHeight(1),\n        child: CommonCard(\n          onPressed: () {\n            globalState.openUrl('https://ptclspeed.speedtestcustom.com');\n          },\n          info: Info(\n            label: appLocalizations.networkSpeed,\n            iconData: Icons.speed_sharp,\n          ),\n          child: ValueListenableBuilder<int>(\n            valueListenable: dashboardRefreshManager.tick1s,\n            builder: (_, _, _) {\n              final traffics = ref.read(trafficsProvider).list;\n              final points = _getPoints(traffics);\n              return Padding(\n                padding: const EdgeInsets.only(\n                  top: 16,\n                  left: 0,\n                  right: 0,\n                  bottom: 0,\n                ),\n                child: LineChart(\n                  gradient: true,\n                  color: primaryColor,\n                  points: points,\n                ),\n              );\n            },\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/dashboard/widgets/ntp_override.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:bett_box/views/config/ntp.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass NtpOverride extends StatelessWidget {\n  const NtpOverride({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return SizedBox(\n      height: getWidgetHeight(1),\n      child: CommonCard(\n        info: Info(label: 'NTP', iconData: Icons.access_time),\n        onPressed: () {\n          // Open NTP settings\n          showExtend(\n            context,\n            builder: (_, type) {\n              return AdaptiveSheetScaffold(\n                type: type,\n                title: 'NTP',\n                body: const NtpListView(),\n              );\n            },\n            props: const ExtendProps(blur: false),\n          );\n        },\n        child: Container(\n          padding: baseInfoEdgeInsets.copyWith(top: 4, bottom: 8, right: 8),\n          child: Row(\n            mainAxisSize: MainAxisSize.max,\n            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n            children: [\n              Flexible(\n                flex: 1,\n                child: TooltipText(\n                  text: Text(\n                    appLocalizations.switchLabel,\n                    maxLines: 1,\n                    overflow: TextOverflow.ellipsis,\n                    style: Theme.of(\n                      context,\n                    ).textTheme.titleSmall?.adjustSize(-2).toLight,\n                  ),\n                ),\n              ),\n              Consumer(\n                builder: (_, ref, _) {\n                  final override = ref.watch(overrideNtpProvider);\n                  return Switch(\n                    materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,\n                    value: override,\n                    onChanged: (value) {\n                      ref.read(overrideNtpProvider.notifier).value = value;\n                    },\n                  );\n                },\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/dashboard/widgets/online_panel.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:url_launcher/url_launcher.dart';\n\nclass OnlinePanel extends ConsumerWidget {\n  const OnlinePanel({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    return RepaintBoundary(\n      child: SizedBox(\n        height: getWidgetHeight(1),\n        child: CommonCard(\n          info: Info(\n            label: appLocalizations.onlinePanel,\n            iconData: Icons.launch,\n          ),\n          onPressed: () async {\n            // Get external controller status and secret\n            final clashConfig = ref.read(patchClashConfigProvider);\n            final externalController = clashConfig.externalController;\n            final secret = clashConfig.secret;\n\n            // Pass secret only if external controller is enabled\n            if (externalController == ExternalControllerStatus.open &&\n                secret != null &&\n                secret.isNotEmpty) {\n              // Build URL with secret in fragment\n              final uri = Uri.parse(\n                'http://127.0.0.1:9090/ui/#/setup?hostname=127.0.0.1&port=9090&secret=$secret',\n              );\n              if (await canLaunchUrl(uri)) {\n                await launchUrl(uri, mode: LaunchMode.externalApplication);\n              }\n            } else {\n              // External controller not enabled or no secret\n              final uri = Uri.parse('http://127.0.0.1:9090/ui/');\n              if (await canLaunchUrl(uri)) {\n                await launchUrl(uri, mode: LaunchMode.externalApplication);\n              }\n            }\n          },\n          child: Container(\n            padding: baseInfoEdgeInsets.copyWith(top: 0),\n            child: Column(\n              mainAxisSize: MainAxisSize.max,\n              mainAxisAlignment: MainAxisAlignment.end,\n              crossAxisAlignment: CrossAxisAlignment.start,\n              children: [\n                SizedBox(\n                  height: globalState.measure.bodyMediumHeight + 2,\n                  child: Row(\n                    mainAxisAlignment: MainAxisAlignment.start,\n                    children: [\n                      Text(\n                        appLocalizations.openDashboard,\n                        style: context.textTheme.bodyMedium?.toLight.adjustSize(\n                          0,\n                        ),\n                        maxLines: 1,\n                        overflow: TextOverflow.ellipsis,\n                      ),\n                    ],\n                  ),\n                ),\n              ],\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/dashboard/widgets/outbound_mode.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:intl/intl.dart';\n\nclass OutboundMode extends StatelessWidget {\n  const OutboundMode({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    final height = getWidgetHeight(2);\n    return SizedBox(\n      height: height,\n      child: Consumer(\n        builder: (_, ref, _) {\n          final mode = ref.watch(\n            patchClashConfigProvider.select((state) => state.mode),\n          );\n          return CommonCard(\n            onPressed: () {},\n            info: Info(\n              label: appLocalizations.outboundMode,\n              iconData: Icons.call_split_sharp,\n            ),\n            child: Padding(\n              padding: const EdgeInsets.only(top: 12, bottom: 16),\n              child: Column(\n                mainAxisSize: MainAxisSize.max,\n                crossAxisAlignment: CrossAxisAlignment.start,\n                mainAxisAlignment: MainAxisAlignment.center,\n                children: [\n                  for (final item in Mode.values)\n                    Flexible(\n                      fit: FlexFit.tight,\n                      child: Material(\n                        color: Colors.transparent,\n                        child: InkWell(\n                          onTap: () {\n                            globalState.appController.changeMode(item);\n                          },\n                          child: Container(\n                            padding: EdgeInsets.symmetric(\n                              horizontal: 16.ap,\n                              vertical: 8.ap,\n                            ),\n                            child: Row(\n                              children: [\n                                Icon(\n                                  item == mode\n                                      ? Icons.check_circle_rounded\n                                      : Icons.circle_outlined,\n                                  size: 21,\n                                  color: item == mode\n                                      ? context.colorScheme.primary\n                                      : context.colorScheme.onSurfaceVariant\n                                            .withValues(alpha: 0.6),\n                                ),\n                                SizedBox(width: 12.ap),\n                                Expanded(\n                                  child: Text(\n                                    Intl.message(item.name),\n                                    style: Theme.of(\n                                      context,\n                                    ).textTheme.bodyMedium?.toSoftBold,\n                                  ),\n                                ),\n                              ],\n                            ),\n                          ),\n                        ),\n                      ),\n                    ),\n                ],\n              ),\n            ),\n          );\n        },\n      ),\n    );\n  }\n}\n\nclass OutboundModeV2 extends StatelessWidget {\n  const OutboundModeV2({super.key});\n\n  Color _getTextColor(BuildContext context, Mode mode) {\n    return switch (mode) {\n      Mode.rule => context.colorScheme.onSecondaryContainer,\n      Mode.global => context.colorScheme.onPrimaryContainer,\n      Mode.direct => context.colorScheme.onTertiaryContainer,\n    };\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final height = getWidgetHeight(0.72);\n    return SizedBox(\n      height: height,\n      child: CommonCard(\n        padding: EdgeInsets.zero,\n        child: Consumer(\n          builder: (_, ref, _) {\n            final mode = ref.watch(\n              patchClashConfigProvider.select((state) => state.mode),\n            );\n            final thumbColor = switch (mode) {\n              Mode.rule => context.colorScheme.secondaryContainer,\n              Mode.global => globalState.theme.darken3PrimaryContainer,\n              Mode.direct => context.colorScheme.tertiaryContainer,\n            };\n            return Container(\n              constraints: BoxConstraints.expand(),\n              child: CommonTabBar<Mode>(\n                children: Map.fromEntries(\n                  Mode.values.map(\n                    (item) => MapEntry(\n                      item,\n                      Container(\n                        clipBehavior: Clip.antiAlias,\n                        alignment: Alignment.center,\n                        decoration: BoxDecoration(),\n                        height: height - 16,\n                        child: Text(\n                          Intl.message(item.name),\n                          style: Theme.of(context).textTheme.titleSmall\n                              ?.adjustSize(1)\n                              .copyWith(\n                                color: item == mode\n                                    ? _getTextColor(context, item)\n                                    : null,\n                              ),\n                        ),\n                      ),\n                    ),\n                  ),\n                ),\n                padding: EdgeInsets.symmetric(horizontal: 8),\n                groupValue: mode,\n                onValueChanged: (value) {\n                  if (value == null) {\n                    return;\n                  }\n                  globalState.appController.changeMode(value);\n                },\n                thumbColor: thumbColor,\n              ),\n            );\n          },\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/dashboard/widgets/providers_info.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/views/proxies/providers.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\n\nclass ProvidersInfo extends StatelessWidget {\n  const ProvidersInfo({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return RepaintBoundary(\n      child: SizedBox(\n        height: getWidgetHeight(1),\n        child: CommonCard(\n          info: const Info(iconData: Icons.poll_outlined, label: 'INFO'),\n          onPressed: () {\n            showExtend(\n              context,\n              builder: (_, type) {\n                return ProvidersView(type: type);\n              },\n            );\n          },\n          child: Container(\n            padding: baseInfoEdgeInsets.copyWith(top: 0),\n            child: Align(\n              alignment: Alignment.bottomLeft,\n              child: Text(\n                'Rule & Providers',\n                style: context.textTheme.bodyMedium?.toLight.adjustSize(0),\n                maxLines: 1,\n                overflow: TextOverflow.ellipsis,\n              ),\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/dashboard/widgets/quick_options.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/views/config/network.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass TUNButton extends StatelessWidget {\n  const TUNButton({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return SizedBox(\n      height: getWidgetHeight(1),\n      child: CommonCard(\n        onPressed: () {\n          showSheet(\n            context: context,\n            builder: (_, type) {\n              return AdaptiveSheetScaffold(\n                type: type,\n                body: generateListView(\n                  generateSection(\n                    items: [\n                      if (system.isDesktop) const TUNItem(),\n                      if (system.isMacOS) const AutoSetSystemDnsItem(),\n                      const StrictRouteItem(),\n                      const IcmpForwardingItem(),\n                      const TunStackItem(),\n                    ],\n                  ),\n                ),\n                title: appLocalizations.tun,\n              );\n            },\n          );\n        },\n        info: Info(\n          label: appLocalizations.tun,\n          iconData: Icons.stacked_line_chart,\n        ),\n        child: Container(\n          padding: baseInfoEdgeInsets.copyWith(top: 4, bottom: 8, right: 8),\n          child: Row(\n            mainAxisSize: MainAxisSize.max,\n            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n            children: [\n              Flexible(\n                flex: 1,\n                child: TooltipText(\n                  text: Text(\n                    appLocalizations.options,\n                    maxLines: 1,\n                    overflow: TextOverflow.ellipsis,\n                    style: Theme.of(\n                      context,\n                    ).textTheme.titleSmall?.adjustSize(-2).toLight,\n                  ),\n                ),\n              ),\n              Consumer(\n                builder: (_, ref, _) {\n                  final enable = ref.watch(\n                    patchClashConfigProvider.select(\n                      (state) => state.tun.enable,\n                    ),\n                  );\n\n                  // Windows 桌面端：检查系统代理是否开启\n                  final systemProxyEnabled = system.isWindows\n                      ? ref.watch(\n                          networkSettingProvider.select(\n                            (state) => state.systemProxy,\n                          ),\n                        )\n                      : false;\n\n                  return Switch(\n                    value: enable,\n                    onChanged: systemProxyEnabled && system.isWindows\n                        ? null // Disable when system proxy is on\n                        : (value) {\n                            // Windows: prompt to close system proxy first\n                            if (system.isWindows && systemProxyEnabled) {\n                              globalState.showNotifier(\n                                appLocalizations.pleaseCloseSystemProxyFirst,\n                              );\n                              return;\n                            }\n\n                            ref\n                                .read(patchClashConfigProvider.notifier)\n                                .updateState(\n                                  (state) => state.copyWith.tun(enable: value),\n                                );\n                          },\n                  );\n                },\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n\nclass SystemProxyButton extends StatelessWidget {\n  const SystemProxyButton({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return SizedBox(\n      height: getWidgetHeight(1),\n      child: CommonCard(\n        onPressed: () {\n          showSheet(\n            context: context,\n            builder: (_, type) {\n              return AdaptiveSheetScaffold(\n                type: type,\n                body: generateListView(\n                  generateSection(\n                    items: [SystemProxyItem(), BypassDomainItem()],\n                  ),\n                ),\n                title: appLocalizations.systemProxy,\n              );\n            },\n          );\n        },\n        info: Info(\n          label: appLocalizations.systemProxy,\n          iconData: Icons.shuffle,\n        ),\n        child: Container(\n          padding: baseInfoEdgeInsets.copyWith(top: 4, bottom: 8, right: 8),\n          child: Row(\n            mainAxisSize: MainAxisSize.max,\n            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n            children: [\n              Flexible(\n                flex: 1,\n                child: TooltipText(\n                  text: Text(\n                    appLocalizations.options,\n                    maxLines: 1,\n                    overflow: TextOverflow.ellipsis,\n                    style: Theme.of(\n                      context,\n                    ).textTheme.titleSmall?.adjustSize(-2).toLight,\n                  ),\n                ),\n              ),\n              Consumer(\n                builder: (_, ref, _) {\n                  final systemProxy = ref.watch(\n                    networkSettingProvider.select((state) => state.systemProxy),\n                  );\n\n                  // Windows 桌面端：检查 TUN 是否开启\n                  final tunEnabled = system.isWindows\n                      ? ref.watch(\n                          patchClashConfigProvider.select(\n                            (state) => state.tun.enable,\n                          ),\n                        )\n                      : false;\n\n                  return Switch(\n                    materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,\n                    value: systemProxy,\n                    onChanged: tunEnabled && system.isWindows\n                        ? null // TUN 开启时禁用开关\n                        : (value) {\n                            // Windows 桌面端：如果 TUN 开启，提示用户先关闭\n                            if (system.isWindows && tunEnabled) {\n                              globalState.showNotifier(\n                                appLocalizations.pleaseCloseTunFirst,\n                              );\n                              return;\n                            }\n\n                            ref\n                                .read(networkSettingProvider.notifier)\n                                .updateState(\n                                  (state) => state.copyWith(systemProxy: value),\n                                );\n                          },\n                  );\n                },\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n\nclass VpnButton extends StatelessWidget {\n  const VpnButton({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return SizedBox(\n      height: getWidgetHeight(1),\n      child: CommonCard(\n        onPressed: () {\n          showSheet(\n            context: context,\n            builder: (_, type) {\n              return AdaptiveSheetScaffold(\n                type: type,\n                body: generateListView(\n                  generateSection(\n                    items: [\n                      const VPNItem(),\n                      const StrictRouteItem(),\n                      const IcmpForwardingItem(),\n                      const TunStackItem(),\n                    ],\n                  ),\n                ),\n                title: 'VPN',\n              );\n            },\n          );\n        },\n        info: Info(label: 'VPN', iconData: Icons.stacked_line_chart),\n        child: Container(\n          padding: baseInfoEdgeInsets.copyWith(top: 4, bottom: 8, right: 8),\n          child: Row(\n            mainAxisSize: MainAxisSize.max,\n            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n            children: [\n              Flexible(\n                flex: 1,\n                child: TooltipText(\n                  text: Text(\n                    appLocalizations.options,\n                    maxLines: 1,\n                    overflow: TextOverflow.ellipsis,\n                    style: Theme.of(\n                      context,\n                    ).textTheme.titleSmall?.adjustSize(-2).toLight,\n                  ),\n                ),\n              ),\n              Consumer(\n                builder: (_, ref, _) {\n                  final enable = ref.watch(\n                    vpnSettingProvider.select((state) => state.enable),\n                  );\n                  return Switch(\n                    value: enable,\n                    onChanged: (value) {\n                      ref\n                          .read(vpnSettingProvider.notifier)\n                          .updateState(\n                            (state) => state.copyWith(enable: value),\n                          );\n                    },\n                  );\n                },\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/dashboard/widgets/sniffer_override.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:bett_box/views/config/sniffer.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass SnifferOverride extends StatelessWidget {\n  const SnifferOverride({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return SizedBox(\n      height: getWidgetHeight(1),\n      child: CommonCard(\n        info: Info(label: 'Sniffer', iconData: Icons.radar),\n        onPressed: () {\n          // Open Sniffer settings\n          showExtend(\n            context,\n            builder: (_, type) {\n              return AdaptiveSheetScaffold(\n                type: type,\n                title: appLocalizations.sniffer,\n                body: const SnifferListView(),\n              );\n            },\n            props: const ExtendProps(blur: false),\n          );\n        },\n        child: Container(\n          padding: baseInfoEdgeInsets.copyWith(top: 4, bottom: 8, right: 8),\n          child: Row(\n            mainAxisSize: MainAxisSize.max,\n            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n            children: [\n              Flexible(\n                flex: 1,\n                child: TooltipText(\n                  text: Text(\n                    appLocalizations.override,\n                    maxLines: 1,\n                    overflow: TextOverflow.ellipsis,\n                    style: Theme.of(\n                      context,\n                    ).textTheme.titleSmall?.adjustSize(-2).toLight,\n                  ),\n                ),\n              ),\n              Consumer(\n                builder: (_, ref, _) {\n                  final override = ref.watch(overrideSnifferProvider);\n                  return Switch(\n                    materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,\n                    value: override,\n                    onChanged: (value) {\n                      ref.read(overrideSnifferProvider.notifier).value = value;\n                    },\n                  );\n                },\n              ),\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/dashboard/widgets/start_button.dart",
    "content": "import 'dart:async';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/providers/providers.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass StartButton extends ConsumerStatefulWidget {\n  const StartButton({super.key});\n\n  @override\n  ConsumerState<StartButton> createState() => _StartButtonState();\n}\n\nclass _StartButtonState extends ConsumerState<StartButton> {\n  static const Duration _disableDuration = Duration(milliseconds: 1000);\n  Timer? _disableTimer;\n  bool _isDisabled = false;\n\n  void _handleStart() {\n    if (_isDisabled) return;\n    _disableTimer?.cancel();\n    setState(() {\n      _isDisabled = true;\n    });\n    _disableTimer = Timer(_disableDuration, () {\n      if (!mounted) return;\n      setState(() {\n        _isDisabled = false;\n      });\n    });\n    final isStart = ref.read(runTimeProvider) != null;\n    final newState = !isStart;\n\n    debouncer.call(FunctionTag.updateStatus, () {\n      globalState.appController.updateStatus(newState);\n    }, duration: commonDuration);\n  }\n\n  Future<void> _handleLongPress() async {\n    final isStart = ref.read(runTimeProvider) != null;\n    if (!isStart) return;\n\n    final result = await globalState.showCommonDialog<bool>(\n      child: CommonDialog(\n        title: appLocalizations.restartCoreTitle,\n        actions: [\n          TextButton(\n            onPressed: () {\n              Navigator.of(context, rootNavigator: true).pop(false);\n            },\n            child: Text(appLocalizations.cancel),\n          ),\n          TextButton(\n            onPressed: () {\n              Navigator.of(context, rootNavigator: true).pop(true);\n            },\n            child: Text(appLocalizations.confirm),\n          ),\n        ],\n        child: Text(appLocalizations.restartCoreDesc),\n      ),\n    );\n\n    if (result == true) {\n      await globalState.appController.restartCore();\n      globalState.showNotifier(appLocalizations.success);\n    }\n  }\n\n  @override\n  void dispose() {\n    _disableTimer?.cancel();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final state = ref.watch(startButtonSelectorStateProvider);\n    final canPress = state.isInit && state.hasProfile && !_isDisabled;\n\n    return ValueListenableBuilder<int>(\n      valueListenable: dashboardRefreshManager.tick1s,\n      builder: (_, _, _) {\n        final runTime = ref.read(runTimeProvider);\n        final isStart = runTime != null;\n        return SizedBox(\n          height: getWidgetHeight(1),\n          child: CommonCard(\n            info: Info(\n              label: isStart\n                  ? appLocalizations.runTime\n                  : appLocalizations.powerSwitch,\n              iconData: Icons.power_settings_new,\n            ),\n            onPressed: canPress ? _handleStart : null,\n            onLongPress: canPress ? _handleLongPress : null,\n            child: Container(\n              padding: baseInfoEdgeInsets.copyWith(top: 0),\n              child: Column(\n                mainAxisSize: MainAxisSize.max,\n                mainAxisAlignment: MainAxisAlignment.end,\n                children: [\n                  SizedBox(\n                    height: globalState.measure.bodyMediumHeight + 2,\n                    child: FadeThroughBox(\n                      child: _buildContent(context, ref, state, isStart, runTime),\n                    ),\n                  ),\n                ],\n              ),\n            ),\n          ),\n        );\n      },\n    );\n  }\n\n  Widget _buildContent(\n    BuildContext context,\n    WidgetRef ref,\n    StartButtonSelectorState state,\n    bool isStart,\n    int? runTime,\n  ) {\n    if (!state.isInit) {\n      return Container(\n        padding: EdgeInsets.all(2),\n        child: AspectRatio(\n          aspectRatio: 1,\n          child: CircularProgressIndicator(strokeWidth: 2),\n        ),\n      );\n    }\n\n    if (!state.hasProfile) {\n      return Text(\n        appLocalizations.checkOrAddProfile,\n        style: context.textTheme.bodyMedium?.toLight.adjustSize(1),\n        maxLines: 1,\n        overflow: TextOverflow.ellipsis,\n      );\n    }\n\n    if (!isStart) {\n      return Row(\n        mainAxisAlignment: MainAxisAlignment.start,\n        children: [\n          Icon(Icons.play_arrow, size: 16, color: context.colorScheme.primary),\n          SizedBox(width: 4),\n          Expanded(\n            child: Text(\n              appLocalizations.serviceReady,\n              style: context.textTheme.bodyMedium?.toLight.adjustSize(1),\n              maxLines: 1,\n              overflow: TextOverflow.ellipsis,\n            ),\n          ),\n        ],\n      );\n    }\n\n    // Started state: show pause icon + run time\n    final timeText = _formatRunTime(runTime);\n    return Row(\n      mainAxisAlignment: MainAxisAlignment.start,\n      children: [\n        Icon(Icons.pause, size: 16, color: context.colorScheme.primary),\n        SizedBox(width: 4),\n        Text('  ', style: context.textTheme.bodyMedium?.toLight.adjustSize(1)),\n        Expanded(\n          child: Text(\n            timeText,\n            style: context.textTheme.bodyMedium?.toLight.adjustSize(1),\n            maxLines: 1,\n            overflow: TextOverflow.ellipsis,\n          ),\n        ),\n      ],\n    );\n  }\n\n  String _formatRunTime(int? timeStamp) {\n    if (timeStamp == null) return '00:00:00';\n\n    final diff = timeStamp / 1000;\n    int inHours = (diff / 3600).floor();\n    int inMinutes = (diff / 60 % 60).floor();\n    int inSeconds = (diff % 60).floor();\n\n    // Limit maximum display to 999:59:59\n    if (inHours > 999) {\n      inHours = 999;\n      inMinutes = 59;\n      inSeconds = 59;\n    }\n\n    // If less than 100 hours, show 2 digits; otherwise 3\n    final hourStr = inHours < 100\n        ? inHours.toString().padLeft(2, '0')\n        : inHours.toString().padLeft(3, '0');\n\n    return '$hourStr:${inMinutes.toString().padLeft(2, '0')}:${inSeconds.toString().padLeft(2, '0')}';\n  }\n}\n"
  },
  {
    "path": "lib/views/dashboard/widgets/traffic_usage.dart",
    "content": "import 'dart:math';\n\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/providers/app.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass TrafficUsage extends StatefulWidget {\n  const TrafficUsage({super.key});\n\n  @override\n  State<TrafficUsage> createState() => _TrafficUsageState();\n}\n\nclass _TrafficUsageState extends State<TrafficUsage> {\n  // cache text measurement results\n  Size? _uploadTextSize;\n  Size? _downloadTextSize;\n\n  @override\n  void didChangeDependencies() {\n    super.didChangeDependencies();\n    // dependency changes when clear cache\n    _uploadTextSize = null;\n    _downloadTextSize = null;\n  }\n\n  Size _getUploadTextSize(BuildContext context) {\n    return _uploadTextSize ??= globalState.measure.computeTextSize(\n      Text(\n        appLocalizations.upload,\n        maxLines: 1,\n        overflow: TextOverflow.ellipsis,\n        style: context.textTheme.bodySmall,\n      ),\n    );\n  }\n\n  Size _getDownloadTextSize(BuildContext context) {\n    return _downloadTextSize ??= globalState.measure.computeTextSize(\n      Text(\n        appLocalizations.download,\n        maxLines: 1,\n        overflow: TextOverflow.ellipsis,\n        style: context.textTheme.bodySmall,\n      ),\n    );\n  }\n\n  Widget _buildTrafficDataItem(\n    BuildContext context,\n    Icon icon,\n    TrafficValue trafficValue,\n  ) {\n    return Row(\n      mainAxisAlignment: MainAxisAlignment.spaceBetween,\n      mainAxisSize: MainAxisSize.max,\n      children: [\n        Flexible(\n          flex: 1,\n          child: Row(\n            mainAxisSize: MainAxisSize.max,\n            mainAxisAlignment: MainAxisAlignment.start,\n            children: [\n              icon,\n              const SizedBox(width: 8),\n              Flexible(\n                flex: 1,\n                child: Text(\n                  trafficValue.showValue,\n                  style: context.textTheme.bodySmall,\n                  maxLines: 1,\n                ),\n              ),\n            ],\n          ),\n        ),\n        Text(\n          trafficValue.showUnit,\n          style: context.textTheme.bodySmall?.toLighter,\n        ),\n      ],\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final primaryColor = globalState.theme.darken3PrimaryContainer;\n    final secondaryColor = globalState.theme.darken2SecondaryContainer;\n    return SizedBox(\n      height: getWidgetHeight(2),\n      child: CommonCard(\n        info: Info(\n          label: appLocalizations.trafficUsage,\n          iconData: Icons.data_saver_off,\n        ),\n        onPressed: () {},\n        child: Consumer(\n          builder: (_, ref, _) {\n            return ValueListenableBuilder<int>(\n              valueListenable: dashboardRefreshManager.tick1s,\n              builder: (_, _, _) {\n                final totalTraffic = ref.read(totalTrafficProvider);\n                final upTotalTrafficValue = totalTraffic.up;\n                final downTotalTrafficValue = totalTraffic.down;\n                return Padding(\n                  padding: baseInfoEdgeInsets.copyWith(top: 0),\n                  child: Column(\n                    mainAxisSize: MainAxisSize.max,\n                    mainAxisAlignment: MainAxisAlignment.end,\n                    crossAxisAlignment: CrossAxisAlignment.start,\n                    children: [\n                      Flexible(\n                        child: Container(\n                          padding: EdgeInsets.symmetric(vertical: 12),\n                          child: Row(\n                            mainAxisSize: MainAxisSize.max,\n                            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                            children: [\n                              AspectRatio(\n                                aspectRatio: 1,\n                                child: DonutChart(\n                                  data: [\n                                    DonutChartData(\n                                      value: upTotalTrafficValue.value.toDouble(),\n                                      color: primaryColor,\n                                    ),\n                                    DonutChartData(\n                                      value: downTotalTrafficValue.value.toDouble(),\n                                      color: secondaryColor,\n                                    ),\n                                  ],\n                                ),\n                              ),\n                              SizedBox(width: 8),\n                              Flexible(\n                                child: LayoutBuilder(\n                                  builder: (_, container) {\n                                    final uploadTextSize = _getUploadTextSize(\n                                      context,\n                                    );\n                                    final downloadTextSize =\n                                        _getDownloadTextSize(context);\n                                    final maxTextWidth = max(\n                                      uploadTextSize.width,\n                                      downloadTextSize.width,\n                                    );\n                                    if (maxTextWidth + 24 > container.maxWidth) {\n                                      return Container();\n                                    }\n                                    return Column(\n                                      crossAxisAlignment: CrossAxisAlignment.start,\n                                      children: [\n                                        Row(\n                                          mainAxisSize: MainAxisSize.min,\n                                          children: [\n                                            Container(\n                                              width: 20,\n                                              height: 8,\n                                              decoration: BoxDecoration(\n                                                color: primaryColor,\n                                                borderRadius:\n                                                    BorderRadius.circular(3),\n                                              ),\n                                            ),\n                                            SizedBox(width: 4),\n                                            Text(\n                                              maxLines: 1,\n                                              appLocalizations.upload,\n                                              overflow: TextOverflow.ellipsis,\n                                              style: context.textTheme.bodySmall,\n                                            ),\n                                          ],\n                                        ),\n                                        SizedBox(height: 4),\n                                        Row(\n                                          mainAxisSize: MainAxisSize.min,\n                                          children: [\n                                            Container(\n                                              width: 20,\n                                              height: 8,\n                                              decoration: BoxDecoration(\n                                                color: secondaryColor,\n                                                borderRadius:\n                                                    BorderRadius.circular(3),\n                                              ),\n                                            ),\n                                            SizedBox(width: 4),\n                                            Text(\n                                              maxLines: 1,\n                                              appLocalizations.download,\n                                              overflow: TextOverflow.ellipsis,\n                                              style: context.textTheme.bodySmall,\n                                            ),\n                                          ],\n                                        ),\n                                      ],\n                                    );\n                                  },\n                                ),\n                              ),\n                            ],\n                          ),\n                        ),\n                      ),\n                      _buildTrafficDataItem(\n                        context,\n                        Icon(Icons.arrow_upward, color: primaryColor, size: 14),\n                        upTotalTrafficValue,\n                      ),\n                      const SizedBox(height: 8),\n                      _buildTrafficDataItem(\n                        context,\n                        Icon(\n                          Icons.arrow_downward,\n                          color: secondaryColor,\n                          size: 14,\n                        ),\n                        downTotalTrafficValue,\n                      ),\n                    ],\n                  ),\n                );\n              },\n            );\n          },\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/dashboard/widgets/wakelock_switch.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/providers/state.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:wakelock_plus/wakelock_plus.dart';\n\nclass WakelockSwitch extends StatelessWidget {\n  const WakelockSwitch({super.key});\n\n  Future<void> _toggleWakelock(BuildContext context) async {\n    try {\n      final enabled = await WakelockPlus.enabled;\n      if (enabled) {\n        await WakelockPlus.disable();\n        globalState.appController.stopWakelockAutoRecovery();\n      } else {\n        await WakelockPlus.enable();\n        globalState.appController.startWakelockAutoRecovery();\n      }\n      globalState.updateWakelockState(!enabled);\n    } catch (e) {\n      commonPrint.log('WakeLock toggle error: $e');\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return RepaintBoundary(\n      child: SizedBox(\n        height: getWidgetHeight(1),\n        child: CommonCard(\n          info: Info(\n            label: appLocalizations.wakelock,\n            iconData: Icons.lightbulb_outline,\n          ),\n          onPressed: () async {\n            // click: show function description dialog\n            await globalState.showCommonDialog<void>(\n              child: CommonDialog(\n                title: appLocalizations.wakelock,\n                actions: [\n                  TextButton(\n                    onPressed: () {\n                      Navigator.of(context, rootNavigator: true).pop();\n                    },\n                    child: Text(appLocalizations.confirm),\n                  ),\n                ],\n                child: Text(appLocalizations.wakelockDescription),\n              ),\n            );\n          },\n          child: Container(\n            padding: baseInfoEdgeInsets.copyWith(top: 4, bottom: 8, right: 8),\n            child: Row(\n              mainAxisSize: MainAxisSize.max,\n              mainAxisAlignment: MainAxisAlignment.spaceBetween,\n              children: [\n                Flexible(\n                  flex: 1,\n                  child: TooltipText(\n                    text: Text(\n                      appLocalizations.switchLabel,\n                      maxLines: 1,\n                      overflow: TextOverflow.ellipsis,\n                      style: Theme.of(\n                        context,\n                      ).textTheme.titleSmall?.adjustSize(-2).toLight,\n                    ),\n                  ),\n                ),\n                Consumer(\n                  builder: (_, ref, _) {\n                    final wakelockEnabled = ref.watch(wakelockStateProvider);\n                    return Switch(\n                      materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,\n                      value: wakelockEnabled,\n                      onChanged: (_) => _toggleWakelock(context),\n                    );\n                  },\n                ),\n              ],\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/dashboard/widgets/widgets.dart",
    "content": "export 'intranet_ip.dart';\nexport 'network_detection.dart';\nexport 'network_speed.dart';\nexport 'network_speed_small.dart';\nexport 'outbound_mode.dart';\nexport 'quick_options.dart';\nexport 'traffic_usage.dart';\nexport 'memory_info.dart';\nexport 'start_button.dart';\nexport 'connections_count.dart';\nexport 'ipv6_switch.dart';\nexport 'dns_override.dart';\nexport 'sniffer_override.dart';\nexport 'ntp_override.dart';\nexport 'providers_info.dart';\nexport 'fcm_status.dart';\nexport 'online_panel.dart';\nexport 'wakelock_switch.dart';\n"
  },
  {
    "path": "lib/views/developer.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/common.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nimport '../providers/app.dart';\n\nclass DeveloperView extends ConsumerWidget {\n  const DeveloperView({super.key});\n\n  Widget _getDeveloperList(BuildContext context, WidgetRef ref) {\n    return generateSectionV2(\n      title: appLocalizations.options,\n      items: [\n        ListItem(\n          title: Text(appLocalizations.messageTest),\n          onTap: () {\n            context.showNotifier(appLocalizations.messageTestTip);\n          },\n        ),\n        ListItem(\n          title: Text(appLocalizations.logsTest),\n          onTap: () {\n            for (int i = 0; i < 1000; i++) {\n              globalState.appController.addLog(\n                Log.app(\n                  '[$i]${utils.generateRandomString(maxLength: 200, minLength: 20)}',\n                ),\n              );\n            }\n          },\n        ),\n        ListItem(\n          title: Text(appLocalizations.clearData),\n          onTap: () async {\n            await globalState.appController.handleClear();\n          },\n        ),\n        ListItem(\n          title: Text('loading'),\n          onTap: () {\n            ref.read(loadingProvider.notifier).value = true;\n          },\n        ),\n      ],\n    );\n  }\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final enable = ref.watch(\n      appSettingProvider.select((state) => state.developerMode),\n    );\n    return SingleChildScrollView(\n      padding: baseInfoEdgeInsets,\n      child: Column(\n        children: [\n          CommonCard(\n            type: CommonCardType.filled,\n            radius: 18,\n            child: ListItem.switchItem(\n              padding: const EdgeInsets.only(left: 16, right: 16),\n              title: Text(appLocalizations.developerMode),\n              delegate: SwitchDelegate(\n                value: enable,\n                onChanged: (value) {\n                  ref\n                      .read(appSettingProvider.notifier)\n                      .updateState(\n                        (state) => state.copyWith(developerMode: value),\n                      );\n                },\n              ),\n            ),\n          ),\n          SizedBox(height: 16),\n          _getDeveloperList(context, ref),\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/hotkey.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/providers/providers.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/card.dart';\nimport 'package:bett_box/widgets/dialog.dart';\nimport 'package:bett_box/widgets/list.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:intl/intl.dart';\n\nextension IntlExt on Intl {\n  static String actionMessage(String messageText) =>\n      Intl.message('action_$messageText');\n}\n\nclass HotKeyView extends StatelessWidget {\n  const HotKeyView({super.key});\n\n  String getSubtitle(HotKeyAction hotKeyAction) {\n    final key = hotKeyAction.key;\n    if (key == null) {\n      return appLocalizations.noHotKey;\n    }\n    final modifierLabels = hotKeyAction.modifiers.map(\n      (item) => item.physicalKeys.first.label,\n    );\n    var text = '';\n    if (modifierLabels.isNotEmpty) {\n      text += \"${modifierLabels.join(\" \")}+\";\n    }\n    text += PhysicalKeyboardKey(key).label;\n    return text;\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return ListView.builder(\n      itemCount: HotAction.values.length,\n      itemBuilder: (_, index) {\n        final hotAction = HotAction.values[index];\n        return Consumer(\n          builder: (_, ref, _) {\n            final hotKeyAction = ref.watch(getHotKeyActionProvider(hotAction));\n            return ListItem(\n              title: Text(IntlExt.actionMessage(hotAction.name)),\n              subtitle: Text(\n                getSubtitle(hotKeyAction),\n                style: context.textTheme.bodyMedium?.copyWith(\n                  color: context.colorScheme.primary,\n                ),\n              ),\n              onTap: () {\n                globalState.showCommonDialog(\n                  child: HotKeyRecorder(hotKeyAction: hotKeyAction),\n                );\n              },\n            );\n          },\n        );\n      },\n    );\n  }\n}\n\nclass HotKeyRecorder extends StatefulWidget {\n  final HotKeyAction hotKeyAction;\n\n  const HotKeyRecorder({super.key, required this.hotKeyAction});\n\n  @override\n  State<HotKeyRecorder> createState() => _HotKeyRecorderState();\n}\n\nclass _HotKeyRecorderState extends State<HotKeyRecorder> {\n  late ValueNotifier<HotKeyAction> hotKeyActionNotifier;\n\n  @override\n  void initState() {\n    super.initState();\n    hotKeyActionNotifier = ValueNotifier<HotKeyAction>(\n      widget.hotKeyAction.copyWith(),\n    );\n    HardwareKeyboard.instance.addHandler(_handleKeyEvent);\n  }\n\n  bool _handleKeyEvent(KeyEvent keyEvent) {\n    if (keyEvent is KeyUpEvent) return false;\n    final keys = HardwareKeyboard.instance.physicalKeysPressed;\n\n    final key = keyEvent.physicalKey;\n\n    final modifiers = KeyboardModifier.values\n        .where(\n          (e) =>\n              e.physicalKeys.any(keys.contains) &&\n              !e.physicalKeys.contains(key),\n        )\n        .toSet();\n    hotKeyActionNotifier.value = hotKeyActionNotifier.value.copyWith(\n      modifiers: modifiers,\n      key: key.usbHidUsage,\n    );\n    return false;\n  }\n\n  @override\n  void dispose() {\n    HardwareKeyboard.instance.removeHandler(_handleKeyEvent);\n    super.dispose();\n  }\n\n  void _handleRemove() {\n    Navigator.of(context).pop();\n    globalState.appController.updateOrAddHotKeyAction(\n      hotKeyActionNotifier.value.copyWith(modifiers: {}, key: null),\n    );\n  }\n\n  void _handleConfirm() {\n    Navigator.of(context).pop();\n    final config = globalState.config;\n    final currentHotkeyAction = hotKeyActionNotifier.value;\n    if (currentHotkeyAction.key == null ||\n        currentHotkeyAction.modifiers.isEmpty) {\n      globalState.showMessage(\n        title: appLocalizations.tip,\n        message: TextSpan(text: appLocalizations.inputCorrectHotkey),\n      );\n      return;\n    }\n    final hotKeyActions = config.hotKeyActions;\n    final index = hotKeyActions.indexWhere(\n      (item) =>\n          item.key == currentHotkeyAction.key &&\n          keyboardModifierListEquality.equals(\n            item.modifiers,\n            currentHotkeyAction.modifiers,\n          ),\n    );\n    if (index != -1) {\n      globalState.showMessage(\n        title: appLocalizations.tip,\n        message: TextSpan(text: appLocalizations.hotkeyConflict),\n      );\n      return;\n    }\n    globalState.appController.updateOrAddHotKeyAction(currentHotkeyAction);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Focus(\n      onKeyEvent: (_, _) {\n        return KeyEventResult.handled;\n      },\n      autofocus: true,\n      child: CommonDialog(\n        title: IntlExt.actionMessage(widget.hotKeyAction.action.name),\n        actions: [\n          TextButton(\n            onPressed: () {\n              _handleRemove();\n            },\n            child: Text(appLocalizations.remove),\n          ),\n          const SizedBox(width: 8),\n          TextButton(\n            onPressed: () {\n              _handleConfirm();\n            },\n            child: Text(appLocalizations.confirm),\n          ),\n        ],\n        child: ValueListenableBuilder(\n          valueListenable: hotKeyActionNotifier,\n          builder: (_, hotKeyAction, _) {\n            final key = hotKeyAction.key;\n            final modifiers = hotKeyAction.modifiers;\n            return SizedBox(\n              width: dialogCommonWidth,\n              child: key != null\n                  ? Wrap(\n                      spacing: 8,\n                      crossAxisAlignment: WrapCrossAlignment.center,\n                      children: [\n                        for (final modifier in modifiers)\n                          KeyboardKeyBox(\n                            keyboardKey: modifier.physicalKeys.first,\n                          ),\n                        if (modifiers.isNotEmpty)\n                          Text('+', style: context.textTheme.titleMedium),\n                        KeyboardKeyBox(keyboardKey: PhysicalKeyboardKey(key)),\n                      ],\n                    )\n                  : Text(\n                      appLocalizations.pressKeyboard,\n                      style: context.textTheme.titleMedium,\n                    ),\n            );\n          },\n        ),\n      ),\n    );\n  }\n}\n\nclass KeyboardKeyBox extends StatelessWidget {\n  final KeyboardKey keyboardKey;\n\n  const KeyboardKeyBox({super.key, required this.keyboardKey});\n\n  @override\n  Widget build(BuildContext context) {\n    return CommonCard(\n      type: CommonCardType.filled,\n      child: Padding(\n        padding: const EdgeInsets.all(12),\n        child: Text(keyboardKey.label, style: const TextStyle(fontSize: 16)),\n      ),\n      onPressed: () {},\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/logs.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/providers/providers.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nimport '../models/models.dart';\nimport '../widgets/widgets.dart';\n\nclass LogsView extends ConsumerStatefulWidget {\n  const LogsView({super.key});\n\n  @override\n  ConsumerState<LogsView> createState() => _LogsViewState();\n}\n\nclass _LogsViewState extends ConsumerState<LogsView> {\n  late final ScrollController _scrollController;\n  var _autoScrollToEnd = false;\n\n  @override\n  void initState() {\n    super.initState();\n    final logs = globalState.appState.logs.list;\n    _scrollController = ScrollController(\n      initialScrollOffset: logs.length * LogItem.height,\n    );\n  }\n\n  @override\n  void dispose() {\n    _scrollController.dispose();\n    super.dispose();\n  }\n\n  void _onSearch(String value) {\n    ref.read(logsSearchProvider.notifier).state = value;\n  }\n\n  void _onKeywordsUpdate(List<String> keywords) {\n    ref.read(logsKeywordsProvider.notifier).state = keywords;\n  }\n\n  void _toggleAutoScroll() {\n    setState(() {\n      _autoScrollToEnd = !_autoScrollToEnd;\n    });\n  }\n\n  void _cancelAutoScroll() {\n    if (_autoScrollToEnd) {\n      setState(() {\n        _autoScrollToEnd = false;\n      });\n    }\n  }\n\n  Future<void> _handleLogLevelSettings() async {\n    final currentLogLevel = ref.read(\n      patchClashConfigProvider.select((state) => state.logLevel),\n    );\n\n    final selectedLogLevel = await globalState.showCommonDialog<LogLevel>(\n      child: OptionsDialog<LogLevel>(\n        title: appLocalizations.logLevel,\n        options: LogLevel.values,\n        value: currentLogLevel,\n        textBuilder: (logLevel) => logLevel.name,\n      ),\n    );\n\n    if (selectedLogLevel != null && selectedLogLevel != currentLogLevel) {\n      ref\n          .read(patchClashConfigProvider.notifier)\n          .updateState((state) => state.copyWith(logLevel: selectedLogLevel));\n      globalState.appController.updateClashConfigDebounce();\n    }\n  }\n\n  Future<void> _handleExport() async {\n    final res = await globalState.appController.safeRun<bool>(\n      () async {\n        return await globalState.appController.exportLogs();\n      },\n      needLoading: true,\n      title: appLocalizations.exportLogs,\n    );\n    if (res != true) return;\n    globalState.showMessage(\n      title: appLocalizations.tip,\n      message: TextSpan(text: appLocalizations.exportSuccess),\n    );\n  }\n\n  void _handleClearLogs() {\n    ref.read(logsProvider.notifier).clearLogs();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final logs = ref.watch(filteredLogsProvider);\n    final hasLogs = logs.isNotEmpty;\n\n    return CommonScaffold(\n      actions: [\n        IconButton(\n          onPressed: _handleLogLevelSettings,\n          icon: const Icon(Icons.settings_outlined),\n          tooltip: appLocalizations.logLevel,\n        ),\n        IconButton(\n          style: _autoScrollToEnd\n              ? ButtonStyle(\n                  backgroundColor: WidgetStatePropertyAll(\n                    context.colorScheme.secondaryContainer,\n                  ),\n                )\n              : null,\n          onPressed: _toggleAutoScroll,\n          icon: const Icon(Icons.vertical_align_top_outlined),\n        ),\n        InkWell(\n          onTap: _handleExport,\n          onLongPress: _handleClearLogs,\n          borderRadius: BorderRadius.circular(20),\n          child: const Padding(\n            padding: EdgeInsets.all(12),\n            child: Icon(Icons.save_as_outlined, size: 24),\n          ),\n        ),\n      ],\n      onKeywordsUpdate: _onKeywordsUpdate,\n      searchState: AppBarSearchState(onSearch: _onSearch),\n      title: appLocalizations.logs,\n      body: !hasLogs\n          ? NullStatus(\n              label: appLocalizations.nullTip(appLocalizations.logs),\n            )\n          : Align(\n              alignment: Alignment.topCenter,\n              child: ScrollToEndBox(\n                onCancelToEnd: _cancelAutoScroll,\n                controller: _scrollController,\n                enable: _autoScrollToEnd,\n                dataSource: logs,\n                child: CommonScrollBar(\n                  controller: _scrollController,\n                  child: LayoutBuilder(\n                    builder: (context, constraints) {\n                      final contentHeight = logs.length * LogItem.height;\n                      final listViewHeight = contentHeight < constraints.maxHeight\n                          ? contentHeight\n                          : constraints.maxHeight;\n\n                      return SizedBox(\n                        height: listViewHeight,\n                        child: ListView.builder(\n                          physics: const NextClampingScrollPhysics(),\n                          reverse: true,\n                          controller: _scrollController,\n                          itemBuilder: (_, index) {\n                            if (index.isOdd) {\n                              return const Divider(height: 0);\n                            }\n                            final itemIndex = index ~/ 2;\n                            if (itemIndex >= logs.length) {\n                              return const SizedBox.shrink();\n                            }\n                            final log = logs[itemIndex];\n                            return LogItem(\n                              key: ValueKey(log.dateTime),\n                              log: log,\n                              onClick: (value) {\n                                context.commonScaffoldState?.addKeyword(value);\n                              },\n                            );\n                          },\n                          itemExtentBuilder: (index, _) {\n                            if (index.isOdd) {\n                              return 0;\n                            }\n                            return LogItem.height;\n                          },\n                          itemCount: logs.length * 2 - 1,\n                        ),\n                      );\n                    },\n                  ),\n                ),\n              ),\n            ),\n    );\n  }\n}\n\nclass LogItem extends StatelessWidget {\n  final Log log;\n  final Function(String)? onClick;\n\n  static double get height {\n    final measure = globalState.measure;\n    return measure.bodyLargeHeight * 2 +\n        8 +\n        24 +\n        globalState.measure.labelMediumHeight +\n        16 +\n        16;\n  }\n\n  const LogItem({super.key, required this.log, this.onClick});\n\n  @override\n  Widget build(BuildContext context) {\n    return RepaintBoundary(\n      child: ListItem(\n        padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),\n        onTap: () {\n        globalState.showCommonDialog(child: LogDetailDialog(log: log));\n      },\n      title: SizedBox(\n        height: globalState.measure.bodyLargeHeight * 2,\n        child: Text(\n          log.payload,\n          maxLines: 2,\n          overflow: TextOverflow.ellipsis,\n          style: context.textTheme.bodyLarge?.copyWith(\n            color: log.logLevel.color,\n          ),\n        ),\n      ),\n      subtitle: Column(\n        children: [\n          const SizedBox(height: 16),\n          Row(\n            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n            children: [\n              CommonChip(\n                onPressed: () {\n                  onClick?.call(log.logLevel.name);\n                },\n                label: log.logLevel.name,\n              ),\n              Text(\n                log.dateTime,\n                style: context.textTheme.bodySmall?.copyWith(\n                  color: context.colorScheme.onSurface.opacity80,\n                ),\n              ),\n            ],\n          ),\n        ],\n      ),\n      ),\n    );\n  }\n}\n\nclass LogDetailDialog extends StatelessWidget {\n  final Log log;\n\n  const LogDetailDialog({super.key, required this.log});\n\n  @override\n  Widget build(BuildContext context) {\n    return CommonDialog(\n      title: appLocalizations.details(appLocalizations.log),\n      actions: [\n        TextButton(\n          onPressed: () {\n            Navigator.of(context).pop(true);\n          },\n          child: Text(appLocalizations.confirm),\n        ),\n      ],\n      child: Column(\n        crossAxisAlignment: CrossAxisAlignment.start,\n        spacing: 6,\n        children: [\n          SelectableText(\n            log.payload,\n            style: context.textTheme.bodyLarge?.copyWith(\n              color: log.logLevel.color,\n            ),\n          ),\n          SelectableText(\n            log.dateTime,\n            style: context.textTheme.bodySmall?.copyWith(\n              color: context.colorScheme.onSurfaceVariant,\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/other_setting.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/common/network_matcher.dart';\nimport 'package:bett_box/plugins/app.dart';\nimport 'package:bett_box/plugins/service.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:bett_box/providers/providers.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass SmartAutoStopItem extends ConsumerWidget {\n  const SmartAutoStopItem({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final smartAutoStop = ref.watch(\n      vpnSettingProvider.select((state) => state.smartAutoStop),\n    );\n    final isQuickResponseEnabled =\n        system.isAndroid &&\n        ref.watch(vpnSettingProvider.select((state) => state.quickResponse));\n\n    return ListItem.switchItem(\n      title: Text(appLocalizations.smartAutoStop),\n      subtitle: Text(appLocalizations.smartAutoStopDesc),\n      delegate: SwitchDelegate(\n        value: smartAutoStop,\n        onChanged: isQuickResponseEnabled\n            ? null\n            : (bool value) async {\n                ref\n                    .read(vpnSettingProvider.notifier)\n                    .updateState(\n                      (state) => state.copyWith(\n                        smartAutoStop: value,\n                        quickResponse: value ? false : state.quickResponse,\n                      ),\n                    );\n\n                if (system.isAndroid) {\n                  if (value) {\n                    await service?.setQuickResponse(false);\n                  } else {\n                    // When turning off smart auto stop, no need to auto-turn-on quickResponse\n                    // User has to explicitly turn it on.\n                  }\n                }\n              },\n      ),\n    );\n  }\n}\n\nclass NetworkMatchItem extends ConsumerWidget {\n  const NetworkMatchItem({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final smartAutoStopNetworks = ref.watch(\n      vpnSettingProvider.select((state) => state.smartAutoStopNetworks),\n    );\n    final isQuickResponseEnabled =\n        system.isAndroid &&\n        ref.watch(vpnSettingProvider.select((state) => state.quickResponse));\n\n    return ListItem.input(\n      title: Text(appLocalizations.networkMatch),\n      subtitle: Text(\n        smartAutoStopNetworks.isEmpty\n            ? appLocalizations.networkMatchHint\n            : smartAutoStopNetworks,\n      ),\n      delegate: InputDelegate(\n        title: appLocalizations.networkMatch,\n        value: smartAutoStopNetworks,\n        onChanged: isQuickResponseEnabled\n            ? null\n            : (String? value) {\n                if (value != null) {\n                  ref\n                      .read(vpnSettingProvider.notifier)\n                      .updateState(\n                        (state) => state.copyWith(smartAutoStopNetworks: value),\n                      );\n                }\n              },\n        validator: (String? value) {\n          if (value == null || value.isEmpty) return null;\n          return NetworkMatcher.getValidationError(\n            value,\n            invalidFormatMsg: appLocalizations.invalidIpFormat,\n            tooManyRulesMsg: appLocalizations.tooManyRules,\n          );\n        },\n      ),\n    );\n  }\n}\n\nclass DozeSuspendItem extends ConsumerWidget {\n  const DozeSuspendItem({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final dozeSuspend = ref.watch(\n      vpnSettingProvider.select((state) => state.dozeSuspend),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.dozeSuspend),\n      subtitle: Text(appLocalizations.dozeSuspendDesc),\n      delegate: SwitchDelegate(\n        value: dozeSuspend,\n        onChanged: (bool value) {\n          ref\n              .read(vpnSettingProvider.notifier)\n              .updateState((state) => state.copyWith(dozeSuspend: value));\n        },\n      ),\n    );\n  }\n}\n\nclass StoreFixItem extends ConsumerWidget {\n  const StoreFixItem({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final storeFix = ref.watch(\n      vpnSettingProvider.select((state) => state.storeFix),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.storeFix),\n      subtitle: Text(appLocalizations.storeFixDesc),\n      delegate: SwitchDelegate(\n        value: storeFix,\n        onChanged: (bool value) async {\n          ref\n              .read(vpnSettingProvider.notifier)\n              .updateState((state) => state.copyWith(storeFix: value));\n\n          // Update hosts mapping\n          final currentHosts = Map<String, String>.from(\n            ref.read(patchClashConfigProvider).hosts,\n          );\n\n          if (value) {\n            // Add the hosts mapping\n            currentHosts['service.googleapis.cn'] = 'service.googleapis.com';\n          } else {\n            // Remove the hosts mapping\n            currentHosts.remove('service.googleapis.cn');\n          }\n\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith(hosts: currentHosts));\n        },\n      ),\n    );\n  }\n}\n\nclass FcmOptimizationItem extends ConsumerWidget {\n  const FcmOptimizationItem({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final fcmOptimization = ref.watch(\n      vpnSettingProvider.select((state) => state.fcmOptimization),\n    );\n\n    return ListItem.switchItem(\n      title: Text(appLocalizations.fcmOptimization),\n      subtitle: Text(appLocalizations.fcmOptimizationDesc),\n      delegate: SwitchDelegate(\n        value: fcmOptimization,\n        onChanged: (bool value) async {\n          // Update FCM optimization state\n          ref\n              .read(vpnSettingProvider.notifier)\n              .updateState(\n                (state) => state.copyWith(\n                  fcmOptimization: value,\n                  // Force disable allowBypass when FCM optimization is on (Android only)\n                  allowBypass: value && system.isAndroid\n                      ? false\n                      : state.allowBypass,\n                ),\n              );\n\n          // Update hosts mapping\n          final currentHosts = Map<String, String>.from(\n            ref.read(patchClashConfigProvider).hosts,\n          );\n\n          if (value) {\n            // Add FCM hosts mapping (comma-separated IPs)\n            currentHosts['mtalk.google.com'] =\n                '142.250.107.188, 108.177.125.188';\n          } else {\n            // Remove FCM hosts mapping\n            currentHosts.remove('mtalk.google.com');\n          }\n\n          ref\n              .read(patchClashConfigProvider.notifier)\n              .updateState((state) => state.copyWith(hosts: currentHosts));\n        },\n      ),\n    );\n  }\n}\n\nclass QuickResponseItem extends ConsumerWidget {\n  const QuickResponseItem({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final quickResponse = ref.watch(\n      vpnSettingProvider.select((state) => state.quickResponse),\n    );\n    final isSmartAutoStopEnabled = ref.watch(\n      vpnSettingProvider.select((state) => state.smartAutoStop),\n    );\n\n    return ListItem.switchItem(\n      title: Text(appLocalizations.quickResponse),\n      subtitle: Text(appLocalizations.quickResponseDesc),\n      delegate: SwitchDelegate(\n        value: quickResponse,\n        onChanged: isSmartAutoStopEnabled\n            ? null\n            : (bool value) async {\n                ref\n                    .read(vpnSettingProvider.notifier)\n                    .updateState(\n                      (state) => state.copyWith(\n                        quickResponse: value,\n                        smartAutoStop: value ? false : state.smartAutoStop,\n                      ),\n                    );\n\n                if (system.isAndroid) {\n                  await service?.setQuickResponse(value);\n                }\n              },\n      ),\n    );\n  }\n}\n\nclass NetworkFixItem extends ConsumerWidget {\n  const NetworkFixItem({super.key});\n\n  Future<void> _applyNetworkFix(bool enable) async {\n    try {\n      // Registry path\n      const regPath =\n          r'HKEY_LOCAL_MACHINE\\SYSTEM\\CurrentControlSet\\Services\\NlaSvc\\Parameters\\Internet';\n\n      if (enable) {\n        // Apply NETFIX config\n        final commands = [\n          // DNS probe config\n          'reg add \"$regPath\" /v ActiveDnsProbeContent /t REG_SZ /d \"131.107.255.255\" /f',\n          'reg add \"$regPath\" /v ActiveDnsProbeContentV6 /t REG_SZ /d \"fd3e:4f5a:5b81::1\" /f',\n          'reg add \"$regPath\" /v ActiveDnsProbeHost /t REG_SZ /d \"dns.msftncsi.com\" /f',\n          'reg add \"$regPath\" /v ActiveDnsProbeHostV6 /t REG_SZ /d \"dns.msftncsi.com\" /f',\n\n          // Web probe config\n          'reg add \"$regPath\" /v ActiveWebProbeContent /t REG_SZ /d \"\" /f',\n          'reg add \"$regPath\" /v ActiveWebProbeContentV6 /t REG_SZ /d \"\" /f',\n          'reg add \"$regPath\" /v ActiveWebProbeHost /t REG_SZ /d \"dns.alidns.com\" /f',\n          'reg add \"$regPath\" /v ActiveWebProbeHostV6 /t REG_SZ /d \"dns.alidns.com\" /f',\n          'reg add \"$regPath\" /v ActiveWebProbePath /t REG_SZ /d \"dns-query\" /f',\n          'reg add \"$regPath\" /v ActiveWebProbePathV6 /t REG_SZ /d \"dns-query\" /f',\n\n          // Other config\n          'reg add \"$regPath\" /v CaptivePortalTimer /t REG_DWORD /d 0x00000000 /f',\n          'reg add \"$regPath\" /v CaptivePortalTimerBackOffIncrementsInSeconds /t REG_DWORD /d 0x00000001 /f',\n          'reg add \"$regPath\" /v CaptivePortalTimerMaxInSeconds /t REG_DWORD /d 0x0000001e /f',\n          'reg add \"$regPath\" /v EnableActiveProbing /t REG_DWORD /d 0x00000001 /f',\n          'reg add \"$regPath\" /v PassivePollPeriod /t REG_DWORD /d 0x0000000f /f',\n          'reg add \"$regPath\" /v StaleThreshold /t REG_DWORD /d 0x0000001e /f',\n          'reg add \"$regPath\" /v WebTimeout /t REG_DWORD /d 0x00000023 /f',\n        ];\n\n        for (final cmd in commands) {\n          windows?.runas(cmd, '', showWindow: false);\n        }\n      } else {\n        // Restore WinNET default config\n        final commands = [\n          // DNS probe config\n          'reg add \"$regPath\" /v ActiveDnsProbeContent /t REG_SZ /d \"131.107.255.255\" /f',\n          'reg add \"$regPath\" /v ActiveDnsProbeContentV6 /t REG_SZ /d \"fd3e:4f5a:5b81::1\" /f',\n          'reg add \"$regPath\" /v ActiveDnsProbeHost /t REG_SZ /d \"dns.msftncsi.com\" /f',\n          'reg add \"$regPath\" /v ActiveDnsProbeHostV6 /t REG_SZ /d \"dns.msftncsi.com\" /f',\n\n          // Web probe config - restore to Microsoft NCSI\n          'reg add \"$regPath\" /v ActiveWebProbeContent /t REG_SZ /d \"Microsoft NCSI\" /f',\n          'reg add \"$regPath\" /v ActiveWebProbeContentV6 /t REG_SZ /d \"Microsoft NCSI\" /f',\n          'reg add \"$regPath\" /v ActiveWebProbeHost /t REG_SZ /d \"www.msftncsi.com\" /f',\n          'reg add \"$regPath\" /v ActiveWebProbeHostV6 /t REG_SZ /d \"ipv6.msftncsi.com\" /f',\n          'reg add \"$regPath\" /v ActiveWebProbePath /t REG_SZ /d \"ncsi.txt\" /f',\n          'reg add \"$regPath\" /v ActiveWebProbePathV6 /t REG_SZ /d \"ncsi.txt\" /f',\n\n          // Other config\n          'reg add \"$regPath\" /v CaptivePortalTimer /t REG_DWORD /d 0x00000000 /f',\n          'reg add \"$regPath\" /v CaptivePortalTimerBackOffIncrementsInSeconds /t REG_DWORD /d 0x00000001 /f',\n          'reg add \"$regPath\" /v CaptivePortalTimerMaxInSeconds /t REG_DWORD /d 0x0000001e /f',\n          'reg add \"$regPath\" /v EnableActiveProbing /t REG_DWORD /d 0x00000001 /f',\n          'reg add \"$regPath\" /v PassivePollPeriod /t REG_DWORD /d 0x0000000f /f',\n          'reg add \"$regPath\" /v StaleThreshold /t REG_DWORD /d 0x0000001e /f',\n          'reg add \"$regPath\" /v WebTimeout /t REG_DWORD /d 0x00000023 /f',\n        ];\n\n        for (final cmd in commands) {\n          windows?.runas(cmd, '', showWindow: false);\n        }\n      }\n    } catch (e) {\n      commonPrint.log('Network fix error: $e');\n      rethrow;\n    }\n  }\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final networkFix = ref.watch(\n      vpnSettingProvider.select((state) => state.networkFix),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.networkFix),\n      subtitle: Text(appLocalizations.networkFixDesc),\n      delegate: SwitchDelegate(\n        value: networkFix,\n        onChanged: (bool value) async {\n          try {\n            await _applyNetworkFix(value);\n\n            ref\n                .read(vpnSettingProvider.notifier)\n                .updateState((state) => state.copyWith(networkFix: value));\n          } catch (e) {\n            // Show error if failed\n            if (context.mounted) {\n              context.showSnackBar('Network fix failed: $e');\n            }\n          }\n        },\n      ),\n    );\n  }\n}\n\nclass BatteryOptimizationItem extends ConsumerWidget {\n  const BatteryOptimizationItem({super.key});\n\n  Future<void> _handleTap(BuildContext context) async {\n    try {\n      // Check if already in whitelist\n      final isIgnoring = await app.isIgnoringBatteryOptimizations();\n\n      if (isIgnoring) {\n        // Already in whitelist\n        if (context.mounted) {\n          context.showSnackBar(appLocalizations.alreadyInWhitelist);\n        }\n      } else {\n        // Request to add to whitelist\n        await app.requestIgnoreBatteryOptimizations();\n      }\n    } catch (e) {\n      commonPrint.log('Battery optimization error: $e');\n    }\n  }\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    return ListItem(\n      title: Text(appLocalizations.batteryOptimization),\n      subtitle: Text(appLocalizations.batteryOptimizationDesc),\n      onTap: () => _handleTap(context),\n    );\n  }\n}\n\nclass DisableQuicItem extends ConsumerWidget {\n  const DisableQuicItem({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final disableQuic = ref.watch(\n      vpnSettingProvider.select((state) => state.disableQuic),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.disableQuic),\n      subtitle: Text(appLocalizations.disableQuicDesc),\n      delegate: SwitchDelegate(\n        value: disableQuic,\n        onChanged: (bool value) async {\n          ref\n              .read(vpnSettingProvider.notifier)\n              .updateState((state) => state.copyWith(disableQuic: value));\n          globalState.appController.setupClashConfigDebounce();\n        },\n      ),\n    );\n  }\n}\n\nclass ExcludeChinaItem extends ConsumerWidget {\n  const ExcludeChinaItem({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final excludeChina = ref.watch(\n      vpnSettingProvider.select((state) => state.excludeChina),\n    );\n    return ListItem.switchItem(\n      title: Text(appLocalizations.excludeChina),\n      subtitle: Text(appLocalizations.excludeChinaDesc),\n      delegate: SwitchDelegate(\n        value: excludeChina,\n        onChanged: (bool value) async {\n          ref\n              .read(vpnSettingProvider.notifier)\n              .updateState((state) => state.copyWith(excludeChina: value));\n          globalState.appController.setupClashConfigDebounce();\n        },\n      ),\n    );\n  }\n}\n\nclass OtherSettingView extends ConsumerWidget {\n  const OtherSettingView({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final smartAutoStop = ref.watch(\n      vpnSettingProvider.select((state) => state.smartAutoStop),\n    );\n    final disableQuic = ref.watch(\n      vpnSettingProvider.select((state) => state.disableQuic),\n    );\n    final locale = ref.watch(appSettingProvider.select((state) => state.locale));\n    final isRussian = locale?.toLowerCase().startsWith('ru') ?? false;\n\n    List<Widget> items = [\n      const SmartAutoStopItem(),\n      if (smartAutoStop) const NetworkMatchItem(),\n      if (system.isAndroid) const DozeSuspendItem(),\n      if (system.isAndroid) const QuickResponseItem(),\n      const FcmOptimizationItem(),\n      const StoreFixItem(),\n      const DisableQuicItem(),\n      if (disableQuic && !isRussian) const ExcludeChinaItem(),\n      if (system.isWindows) const NetworkFixItem(),\n      if (system.isAndroid) const BatteryOptimizationItem(),\n    ];\n\n    if (items.isEmpty) {\n      return const Center(child: Text('No settings available'));\n    }\n\n    return ListView.separated(\n      itemBuilder: (_, index) {\n        final item = items[index];\n        return item;\n      },\n      separatorBuilder: (_, _) {\n        return const Divider(height: 0);\n      },\n      itemCount: items.length,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/profiles/add_profile.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/pages/scan.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\n\nclass AddProfileView extends StatelessWidget {\n  final BuildContext context;\n\n  const AddProfileView({super.key, required this.context});\n\n  Future<void> _handleAddProfileFormFile() async {\n    globalState.appController.addProfileFormFile();\n  }\n\n  Future<void> _handleAddProfileFormURL(String url) async {\n    globalState.appController.addProfileFormURL(url);\n  }\n\n  Future<void> _handleAddProfileFromClipboard() async {\n    try {\n      final clipboardData = await Clipboard.getData(Clipboard.kTextPlain);\n      final text = clipboardData?.text?.trim();\n\n      if (text == null || text.isEmpty) {\n        if (context.mounted) {\n          context.showSnackBar(\n            appLocalizations.emptyTip(appLocalizations.clipboard),\n          );\n        }\n        return;\n      }\n\n      if (!text.isUrl) {\n        if (context.mounted) {\n          context.showSnackBar(\n            appLocalizations.urlTip(appLocalizations.clipboard),\n          );\n        }\n        return;\n      }\n\n      _handleAddProfileFormURL(text);\n    } catch (e) {\n      if (context.mounted) {\n        context.showSnackBar(e.toString());\n      }\n    }\n  }\n\n  Future<void> _toScan() async {\n    if (system.isDesktop) {\n      globalState.appController.addProfileFormQrCode();\n      return;\n    }\n    final url = await BaseNavigator.push(context, const ScanPage());\n    if (url != null) {\n      WidgetsBinding.instance.addPostFrameCallback((_) {\n        _handleAddProfileFormURL(url);\n      });\n    }\n  }\n\n  Future<void> _toAdd() async {\n    final url = await globalState.showCommonDialog<String>(\n      child: InputDialog(\n        autofocus: true,\n        autovalidateMode: AutovalidateMode.onUnfocus,\n        title: appLocalizations.importFromURL,\n        labelText: appLocalizations.url,\n        value: '',\n        validator: (value) {\n          if (value == null || value.isEmpty) {\n            return appLocalizations.emptyTip('').trim();\n          }\n          if (!value.isUrl) {\n            return appLocalizations.urlTip('').trim();\n          }\n          return null;\n        },\n      ),\n    );\n    if (url != null) {\n      _handleAddProfileFormURL(url);\n    }\n  }\n\n  @override\n  Widget build(context) {\n    return ListView(\n      children: [\n        ListItem(\n          leading: const Icon(Icons.qr_code_sharp),\n          title: Text(appLocalizations.qrcode),\n          subtitle: Text(appLocalizations.qrcodeDesc),\n          onTap: _toScan,\n        ),\n        ListItem(\n          leading: const Icon(Icons.content_paste),\n          title: Text(appLocalizations.clipboard),\n          subtitle: Text(appLocalizations.clipboardDesc),\n          onTap: _handleAddProfileFromClipboard,\n        ),\n        ListItem(\n          leading: const Icon(Icons.upload_file_sharp),\n          title: Text(appLocalizations.file),\n          subtitle: Text(appLocalizations.fileDesc),\n          onTap: _handleAddProfileFormFile,\n        ),\n        ListItem(\n          leading: const Icon(Icons.cloud_download_sharp),\n          title: Text(appLocalizations.url),\n          subtitle: Text(appLocalizations.urlDesc),\n          onTap: _toAdd,\n        ),\n      ],\n    );\n  }\n}\n\nclass URLFormDialog extends StatefulWidget {\n  const URLFormDialog({super.key});\n\n  @override\n  State<URLFormDialog> createState() => _URLFormDialogState();\n}\n\nclass _URLFormDialogState extends State<URLFormDialog> {\n  final urlController = TextEditingController();\n\n  Future<void> _handleAddProfileFormURL() async {\n    final url = urlController.value.text;\n    if (url.isEmpty) return;\n    Navigator.of(context).pop<String>(url);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return CommonDialog(\n      title: appLocalizations.importFromURL,\n      actions: [\n        TextButton(\n          onPressed: _handleAddProfileFormURL,\n          child: Text(appLocalizations.submit),\n        ),\n      ],\n      child: SizedBox(\n        width: 300,\n        child: Wrap(\n          runSpacing: 16,\n          children: [\n            TextField(\n              keyboardType: TextInputType.url,\n              minLines: 1,\n              maxLines: 5,\n              onSubmitted: (_) {\n                _handleAddProfileFormURL();\n              },\n              onEditingComplete: _handleAddProfileFormURL,\n              controller: urlController,\n              decoration: InputDecoration(\n                border: const OutlineInputBorder(),\n                labelText: appLocalizations.url,\n              ),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/profiles/edit_profile.dart",
    "content": "import 'dart:convert';\nimport 'dart:io';\nimport 'dart:typed_data';\n\nimport 'package:bett_box/clash/clash.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/pages/editor.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\n\nclass EditProfileView extends StatefulWidget {\n  final Profile profile;\n  final BuildContext context;\n\n  const EditProfileView({\n    super.key,\n    required this.context,\n    required this.profile,\n  });\n\n  @override\n  State<EditProfileView> createState() => _EditProfileViewState();\n}\n\nclass _EditProfileViewState extends State<EditProfileView> {\n  late TextEditingController labelController;\n  late TextEditingController urlController;\n  late TextEditingController autoUpdateDurationController;\n  late bool autoUpdate;\n  String? rawText;\n  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();\n  final fileInfoNotifier = ValueNotifier<FileInfo?>(null);\n  Uint8List? fileData;\n\n  Profile get profile => widget.profile;\n\n  @override\n  void initState() {\n    super.initState();\n    labelController = TextEditingController(text: widget.profile.label);\n    urlController = TextEditingController(text: widget.profile.url);\n    autoUpdate = widget.profile.autoUpdate;\n    autoUpdateDurationController = TextEditingController(\n      text: widget.profile.autoUpdateDuration.inMinutes.toString(),\n    );\n    appPath.getProfilePath(widget.profile.id).then((path) async {\n      fileInfoNotifier.value = await _getFileInfo(path);\n    });\n  }\n\n  Future<void> _handleConfirm() async {\n    if (!_formKey.currentState!.validate()) return;\n    final appController = globalState.appController;\n    Profile profile = this.profile.copyWith(\n      url: urlController.text,\n      label: labelController.text,\n      autoUpdate: autoUpdate,\n      autoUpdateDuration: Duration(\n        minutes: int.parse(autoUpdateDurationController.text),\n      ),\n    );\n    final hasUpdate = widget.profile.url != profile.url;\n    if (fileData != null) {\n      if (profile.type == ProfileType.url && autoUpdate) {\n        final res = await globalState.showMessage(\n          title: appLocalizations.tip,\n          message: TextSpan(text: appLocalizations.profileHasUpdate),\n        );\n        if (res == true) {\n          profile = profile.copyWith(autoUpdate: false);\n        }\n      }\n      try {\n        final updatedProfile = await profile.saveFile(fileData!);\n        appController.setProfileAndAutoApply(updatedProfile);\n      } catch (e) {\n        if (mounted) {\n          await globalState.showMessage(\n            title: appLocalizations.tip,\n            message: TextSpan(text: e.toString()),\n          );\n        }\n        return;\n      }\n    } else if (!hasUpdate) {\n      appController.setProfileAndAutoApply(profile);\n    } else {\n      globalState.appController.safeRun(() async {\n        await Future.delayed(commonDuration);\n        if (hasUpdate) {\n          await appController.updateProfile(profile);\n        }\n      });\n    }\n    if (mounted) {\n      Navigator.of(context).pop();\n    }\n  }\n\n  void _setAutoUpdate(bool value) {\n    if (autoUpdate == value) return;\n    setState(() {\n      autoUpdate = value;\n    });\n  }\n\n  Future<FileInfo?> _getFileInfo(String path) async {\n    final file = File(path);\n    if (!await file.exists()) {\n      return null;\n    }\n    final lastModified = await file.lastModified();\n    final size = await file.length();\n    return FileInfo(size: size, lastModified: lastModified);\n  }\n\n  Future<void> _handleSaveEdit(BuildContext context, String data) async {\n    final message = await globalState.appController.safeRun<String>(() async {\n      final message = await clashCore.validateConfig(data);\n      return message;\n    }, silence: false);\n    if (message?.isNotEmpty == true) {\n      globalState.showMessage(\n        title: appLocalizations.tip,\n        message: TextSpan(text: message),\n      );\n      return;\n    }\n    if (context.mounted) {\n      Navigator.of(context).pop(data);\n    }\n  }\n\n  Future<void> _editProfileFile() async {\n    if (rawText == null) {\n      final profilePath = await appPath.getProfilePath(widget.profile.id);\n      final file = File(profilePath);\n      if (await file.exists()) {\n        rawText = await file.readAsString();\n      }\n    }\n    if (!mounted) return;\n    final title = widget.profile.label ?? widget.profile.id;\n    final editorPage = EditorPage(\n      title: title,\n      content: rawText!,\n      onSave: (context, _, content) {\n        _handleSaveEdit(context, content);\n      },\n      onPop: (context, _, content) async {\n        if (content == rawText) {\n          return true;\n        }\n        final res = await globalState.showMessage(\n          title: title,\n          message: TextSpan(text: appLocalizations.hasCacheChange),\n        );\n        if (res == true && context.mounted) {\n          _handleSaveEdit(context, content);\n        } else {\n          return true;\n        }\n        return false;\n      },\n    );\n    final data = await BaseNavigator.push<String>(context, editorPage);\n    if (data == null) {\n      return;\n    }\n    rawText = data;\n    fileData = Uint8List.fromList(utf8.encode(data));\n    fileInfoNotifier.value = fileInfoNotifier.value?.copyWith(\n      size: fileData?.length ?? 0,\n      lastModified: DateTime.now(),\n    );\n  }\n\n  Future<void> _uploadProfileFile() async {\n    final platformFile = await globalState.appController.safeRun(\n      picker.pickerFile,\n    );\n    if (platformFile?.bytes == null) return;\n    fileData = platformFile?.bytes;\n    fileInfoNotifier.value = fileInfoNotifier.value?.copyWith(\n      size: fileData?.length ?? 0,\n      lastModified: DateTime.now(),\n    );\n  }\n\n  Future<void> _handleBack() async {\n    final res = await globalState.showMessage(\n      title: appLocalizations.tip,\n      message: TextSpan(text: appLocalizations.fileIsUpdate),\n    );\n    if (res == true) {\n      _handleConfirm();\n    } else {\n      if (mounted) {\n        Navigator.of(context).pop();\n      }\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final items = [\n      ListItem(\n        title: TextFormField(\n          textInputAction: TextInputAction.next,\n          controller: labelController,\n          decoration: InputDecoration(\n            border: const OutlineInputBorder(),\n            labelText: appLocalizations.name,\n          ),\n          validator: (String? value) {\n            if (value == null || value.isEmpty) {\n              return appLocalizations.profileNameNullValidationDesc;\n            }\n            return null;\n          },\n        ),\n      ),\n      if (widget.profile.type == ProfileType.url) ...[\n        ListItem(\n          title: TextFormField(\n            textInputAction: TextInputAction.next,\n            keyboardType: TextInputType.url,\n            controller: urlController,\n            maxLines: 5,\n            minLines: 1,\n            decoration: InputDecoration(\n              border: const OutlineInputBorder(),\n              labelText: appLocalizations.url,\n            ),\n            validator: (String? value) {\n              if (value == null || value.isEmpty) {\n                return appLocalizations.profileUrlNullValidationDesc;\n              }\n              if (!value.isUrl) {\n                return appLocalizations.profileUrlInvalidValidationDesc;\n              }\n              return null;\n            },\n          ),\n        ),\n        ListItem.switchItem(\n          title: Text(appLocalizations.autoUpdate),\n          delegate: SwitchDelegate<bool>(\n            value: autoUpdate,\n            onChanged: _setAutoUpdate,\n          ),\n        ),\n        if (autoUpdate)\n          ListItem(\n            title: TextFormField(\n              textInputAction: TextInputAction.next,\n              controller: autoUpdateDurationController,\n              decoration: InputDecoration(\n                border: const OutlineInputBorder(),\n                labelText: appLocalizations.autoUpdateInterval,\n              ),\n              validator: (String? value) {\n                if (value == null || value.isEmpty) {\n                  return appLocalizations\n                      .profileAutoUpdateIntervalNullValidationDesc;\n                }\n                try {\n                  int.parse(value);\n                } catch (_) {\n                  return appLocalizations\n                      .profileAutoUpdateIntervalInvalidValidationDesc;\n                }\n                return null;\n              },\n            ),\n          ),\n      ],\n      ValueListenableBuilder<FileInfo?>(\n        valueListenable: fileInfoNotifier,\n        builder: (_, fileInfo, _) {\n          return FadeThroughBox(\n            child: fileInfo == null\n                ? Container()\n                : ListItem(\n                    title: Text(appLocalizations.profile),\n                    subtitle: Column(\n                      crossAxisAlignment: CrossAxisAlignment.start,\n                      children: [\n                        const SizedBox(height: 4),\n                        Text(fileInfo.desc),\n                        const SizedBox(height: 8),\n                        Wrap(\n                          runSpacing: 6,\n                          spacing: 12,\n                          children: [\n                            CommonChip(\n                              avatar: const Icon(Icons.edit),\n                              label: appLocalizations.edit,\n                              onPressed: _editProfileFile,\n                            ),\n                            CommonChip(\n                              avatar: const Icon(Icons.upload),\n                              label: appLocalizations.upload,\n                              onPressed: _uploadProfileFile,\n                            ),\n                          ],\n                        ),\n                      ],\n                    ),\n                  ),\n          );\n        },\n      ),\n    ];\n    return CommonPopScope(\n      onPop: () {\n        if (fileData == null) {\n          return true;\n        }\n        _handleBack();\n        return false;\n      },\n      child: FloatLayout(\n        floatingWidget: FloatWrapper(\n          child: FloatingActionButton.extended(\n            heroTag: null,\n            onPressed: _handleConfirm,\n            label: Text(appLocalizations.save),\n            icon: const Icon(Icons.save),\n          ),\n        ),\n        child: Form(\n          key: _formKey,\n          child: Padding(\n            padding: const EdgeInsets.symmetric(vertical: 16),\n            child: ListView.separated(\n              padding: kMaterialListPadding.copyWith(bottom: 72),\n              itemBuilder: (_, index) {\n                return items[index];\n              },\n              separatorBuilder: (_, _) {\n                return const SizedBox(height: 24);\n              },\n              itemCount: items.length,\n            ),\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/profiles/override_profile.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/providers/providers.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass OverrideProfileView extends StatefulWidget {\n  final String profileId;\n\n  const OverrideProfileView({super.key, required this.profileId});\n\n  @override\n  State<OverrideProfileView> createState() => _OverrideProfileViewState();\n}\n\nclass _OverrideProfileViewState extends State<OverrideProfileView> {\n  final _controller = ScrollController();\n  double _currentMaxWidth = 0;\n\n  void _initState(WidgetRef ref) {\n    WidgetsBinding.instance.addPostFrameCallback((_) {\n      Future.delayed(Duration(milliseconds: 300), () async {\n        final rawConfig = await globalState.getProfileConfig(widget.profileId);\n        final snippet = ClashConfigSnippet.fromJson(rawConfig);\n        final overrideData = ref.read(\n          getProfileOverrideDataProvider(widget.profileId),\n        );\n        final newOverrideData = overrideData?.rule.type == OverrideRuleType.override &&\n                overrideData?.rule.overrideRules.isEmpty == true\n            ? overrideData?.copyWith(\n                rule: overrideData.rule.copyWith(\n                  overrideRules: snippet.rule,\n                ),\n              )\n            : overrideData;\n        ref\n            .read(profileOverrideStateProvider.notifier)\n            .updateState(\n              (state) =>\n                  state.copyWith(snippet: snippet, overrideData: newOverrideData),\n            );\n      });\n    });\n  }\n\n  void _handleSave(WidgetRef ref, OverrideData overrideData) {\n    ref\n        .read(profilesProvider.notifier)\n        .updateProfile(\n          widget.profileId,\n          (state) => state.copyWith(overrideData: overrideData),\n        );\n    globalState.appController.setupClashConfigDebounce();\n  }\n\n  Future<void> _handleDelete(WidgetRef ref) async {\n    final res = await globalState.showMessage(\n      title: appLocalizations.tip,\n      message: TextSpan(\n        text: appLocalizations.deleteMultipTip(appLocalizations.rule),\n      ),\n    );\n    if (res != true) {\n      return;\n    }\n    final selectedRules = ref.read(\n      profileOverrideStateProvider.select((state) => state.selectedRules),\n    );\n    ref.read(profileOverrideStateProvider.notifier).updateState((state) {\n      final overrideRule = state.overrideData!.rule.updateRules(\n        (rules) =>\n            List.from(rules.where((item) => !selectedRules.contains(item.id))),\n      );\n      return state.copyWith.overrideData!(rule: overrideRule);\n    });\n    ref\n        .read(profileOverrideStateProvider.notifier)\n        .updateState((state) => state.copyWith(selectedRules: {}));\n  }\n\n  Widget _buildContent() {\n    return Consumer(\n      builder: (_, ref, child) {\n        final isInit = ref.watch(\n          profileOverrideStateProvider.select(\n            (state) => state.snippet != null && state.overrideData != null,\n          ),\n        );\n        if (!isInit) {\n          return Center(child: CircularProgressIndicator());\n        }\n        return FadeBox(\n          child: !isInit ? Center(child: CircularProgressIndicator()) : child!,\n        );\n      },\n      child: LayoutBuilder(\n        builder: (_, constraints) {\n          _currentMaxWidth = constraints.maxWidth - 104;\n          return CommonScrollBar(\n            controller: _controller,\n            child: CustomScrollView(\n              controller: _controller,\n              cacheExtent: 500,\n              slivers: [\n                SliverToBoxAdapter(child: SizedBox(height: 8)),\n                SliverToBoxAdapter(\n                  child: Consumer(\n                    builder: (_, ref, child) {\n                      final scriptMode = ref.watch(\n                        scriptStateProvider.select(\n                          (state) => state.realId != null,\n                        ),\n                      );\n                      if (!scriptMode) {\n                        return SizedBox();\n                      }\n                      return child!;\n                    },\n                    child: ListItem(\n                      padding: EdgeInsets.symmetric(\n                        horizontal: 20,\n                        vertical: 0,\n                      ),\n                      title: Row(\n                        spacing: 8,\n                        children: [\n                          Icon(Icons.info),\n                          Text(appLocalizations.overrideInvalidTip),\n                        ],\n                      ),\n                    ),\n                  ),\n                ),\n                SliverToBoxAdapter(child: SizedBox(height: 8)),\n                SliverPadding(\n                  padding: EdgeInsets.symmetric(horizontal: 16),\n                  sliver: SliverToBoxAdapter(child: OverrideSwitch()),\n                ),\n                SliverToBoxAdapter(\n                  child: Padding(\n                    padding: EdgeInsets.only(left: 8, right: 8),\n                    child: RuleTitle(profileId: widget.profileId),\n                  ),\n                ),\n                SliverPadding(\n                  padding: EdgeInsets.symmetric(horizontal: 16.0, vertical: 0),\n                  sliver: RuleContent(maxWidth: _currentMaxWidth),\n                ),\n                SliverToBoxAdapter(child: SizedBox(height: 16)),\n              ],\n            ),\n          );\n        },\n      ),\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return ProviderScope(\n      overrides: [\n        profileOverrideStateProvider.overrideWith(() => ProfileOverrideState()),\n      ],\n      child: Consumer(\n        builder: (_, ref, child) {\n          _initState(ref);\n          return child!;\n        },\n        child: Consumer(\n          builder: (_, ref, _) {\n            final editCount = ref.watch(\n              profileOverrideStateProvider.select(\n                (state) => state.selectedRules.length,\n              ),\n            );\n            final isEdit = editCount != 0;\n            return CommonScaffold(\n              title: appLocalizations.override,\n              body: _buildContent(),\n              actions: [\n                if (!isEdit)\n                  Consumer(\n                    builder: (_, ref, child) {\n                      final overrideData = ref.watch(\n                        getProfileOverrideDataProvider(widget.profileId),\n                      );\n                      final newOverrideData = ref.watch(\n                        profileOverrideStateProvider.select(\n                          (state) => state.overrideData,\n                        ),\n                      );\n                      final equals = overrideData == newOverrideData;\n                      if (equals || newOverrideData == null) {\n                        return SizedBox();\n                      }\n                      return CommonPopScope(\n                        onPop: () async {\n                          if (equals) {\n                            return true;\n                          }\n                          final res = await globalState.showMessage(\n                            message: TextSpan(\n                              text: appLocalizations.saveChanges,\n                            ),\n                            confirmText: appLocalizations.save,\n                          );\n                          if (!context.mounted || res != true) {\n                            return true;\n                          }\n                          _handleSave(ref, newOverrideData);\n                          return true;\n                        },\n                        child: IconButton(\n                          onPressed: () async {\n                            final res = await globalState.showMessage(\n                              message: TextSpan(text: appLocalizations.saveTip),\n                              confirmText: appLocalizations.save,\n                            );\n                            if (res != true) {\n                              return;\n                            }\n                            _handleSave(ref, newOverrideData);\n                          },\n                          icon: Icon(Icons.save),\n                        ),\n                      );\n                    },\n                  ),\n                if (editCount == 1)\n                  IconButton(\n                    onPressed: () {\n                      final rule = ref.read(\n                        profileOverrideStateProvider.select((state) {\n                          return state.overrideData?.rule.rules.firstWhere(\n                            (item) => item.id == state.selectedRules.first,\n                          );\n                        }),\n                      );\n                      if (rule == null) {\n                        return;\n                      }\n                      globalState.appController.handleAddOrUpdate(ref, rule);\n                    },\n                    icon: Icon(Icons.edit),\n                  ),\n                if (editCount > 0)\n                  IconButton(\n                    onPressed: () {\n                      _handleDelete(ref);\n                    },\n                    icon: Icon(Icons.delete),\n                  ),\n              ],\n              editState: AppBarEditState(\n                editCount: editCount,\n                onExit: () {\n                  ref\n                      .read(profileOverrideStateProvider.notifier)\n                      .updateState(\n                        (state) => state.copyWith(selectedRules: {}),\n                      );\n                },\n              ),\n            );\n          },\n        ),\n      ),\n    );\n  }\n}\n\nclass OverrideSwitch extends ConsumerWidget {\n  const OverrideSwitch({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final enable = ref.watch(\n      profileOverrideStateProvider.select(\n        (state) => state.overrideData?.enable,\n      ),\n    );\n    return CommonCard(\n      onPressed: () {},\n      type: CommonCardType.filled,\n      radius: 18,\n      child: ListItem.switchItem(\n        padding: const EdgeInsets.only(left: 16, right: 16),\n        title: Text(appLocalizations.enableOverride),\n        delegate: SwitchDelegate(\n          value: enable ?? false,\n          onChanged: (value) {\n            ref\n                .read(profileOverrideStateProvider.notifier)\n                .updateState(\n                  (state) => state.copyWith.overrideData!(enable: value),\n                );\n          },\n        ),\n      ),\n    );\n  }\n}\n\nclass RuleTitle extends ConsumerWidget {\n  final String profileId;\n\n  const RuleTitle({super.key, required this.profileId});\n\n  void _handleChangeType(WidgetRef ref, isOverrideRule) {\n    ref\n        .read(profileOverrideStateProvider.notifier)\n        .updateState(\n          (state) => state.copyWith.overrideData!.rule(\n            type: isOverrideRule\n                ? OverrideRuleType.added\n                : OverrideRuleType.override,\n          ),\n        );\n  }\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final vm3 = ref.watch(\n      profileOverrideStateProvider.select((state) {\n        final overrideRule = state.overrideData?.rule;\n        return VM3(\n          a: state.selectedRules.isNotEmpty,\n          b: state.selectedRules.containsAll(\n            overrideRule?.rules.map((item) => item.id).toSet() ?? {},\n          ),\n          c: overrideRule?.type == OverrideRuleType.override,\n        );\n      }),\n    );\n    final isEdit = vm3.a;\n    final isSelectAll = vm3.b;\n    final isOverrideRule = vm3.c;\n    return FilledButtonTheme(\n      data: FilledButtonThemeData(\n        style: ButtonStyle(\n          padding: WidgetStatePropertyAll(EdgeInsets.symmetric(horizontal: 8)),\n          visualDensity: VisualDensity.compact,\n        ),\n      ),\n      child: IconButtonTheme(\n        data: IconButtonThemeData(\n          style: ButtonStyle(\n            padding: WidgetStatePropertyAll(EdgeInsets.zero),\n            visualDensity: VisualDensity.compact,\n            iconSize: WidgetStatePropertyAll(20),\n          ),\n        ),\n        child: ListHeader(\n          title: appLocalizations.rule,\n          subTitle: isOverrideRule\n              ? appLocalizations.overrideOriginRules\n              : appLocalizations.addedOriginRules,\n          space: 8,\n          actions: [\n            if (!isEdit)\n              IconButton.filledTonal(\n                icon: Icon(\n                  isOverrideRule ? Icons.edit_document : Icons.note_add,\n                ),\n                onPressed: () {\n                  _handleChangeType(ref, isOverrideRule);\n                },\n              ),\n            !isEdit\n                ? FilledButton.tonal(\n                    onPressed: () {\n                      globalState.appController.handleAddOrUpdate(ref);\n                    },\n                    child: Text(appLocalizations.add),\n                  )\n                : isSelectAll\n                ? FilledButton(\n                    onPressed: () {\n                      ref\n                          .read(profileOverrideStateProvider.notifier)\n                          .updateState(\n                            (state) => state.copyWith(selectedRules: {}),\n                          );\n                    },\n                    child: Text(appLocalizations.selectAll),\n                  )\n                : FilledButton.tonal(\n                    onPressed: () {\n                      ref\n                          .read(profileOverrideStateProvider.notifier)\n                          .updateState(\n                            (state) => state.copyWith(\n                              selectedRules:\n                                  state.overrideData?.rule.rules\n                                      .map((item) => item.id)\n                                      .toSet() ??\n                                  {},\n                            ),\n                          );\n                    },\n                    child: Text(appLocalizations.selectAll),\n                  ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n\nclass RuleContent extends ConsumerWidget {\n  final double maxWidth;\n\n  const RuleContent({super.key, required this.maxWidth});\n\n  Widget _buildItem({\n    required Rule rule,\n    required bool isSelected,\n    required VoidCallback onTab,\n    required BuildContext context,\n  }) {\n    return Material(\n      color: Colors.transparent,\n      child: Container(\n        margin: EdgeInsets.symmetric(vertical: 4),\n        child: CommonCard(\n          padding: EdgeInsets.zero,\n          radius: 18,\n          type: CommonCardType.filled,\n          isSelected: isSelected,\n          // decoration: BoxDecoration(\n          //   color: isSelected\n          //       ? context.colorScheme.secondaryContainer.opacity80\n          //       : context.colorScheme.surfaceContainer,\n          //   borderRadius: BorderRadius.circular(18),\n          // ),\n          onPressed: () {\n            onTab();\n          },\n          child: ListTile(\n            minTileHeight: 0,\n            minVerticalPadding: 0,\n            titleTextStyle: context.textTheme.bodyMedium?.toJetBrainsMono,\n            contentPadding: const EdgeInsets.symmetric(\n              horizontal: 16,\n              vertical: 16,\n            ),\n            trailing: SizedBox(\n              width: 24,\n              height: 24,\n              child: CommonCheckBox(\n                value: isSelected,\n                isCircle: true,\n                onChanged: (_) {\n                  onTab();\n                },\n              ),\n            ),\n            title: Text(rule.value),\n          ),\n        ),\n      ),\n    );\n  }\n\n  void _handleSelect(WidgetRef ref, String ruleId) {\n    ref.read(profileOverrideStateProvider.notifier).updateState((state) {\n      final newSelectedRules = Set<String>.from(state.selectedRules);\n      if (newSelectedRules.contains(ruleId)) {\n        newSelectedRules.remove(ruleId);\n      } else {\n        newSelectedRules.add(ruleId);\n      }\n      return state.copyWith(selectedRules: newSelectedRules);\n    });\n  }\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final vm3 = ref.watch(\n      profileOverrideStateProvider.select((state) {\n        final overrideRule = state.overrideData?.rule;\n        return VM3(\n          a: overrideRule?.rules ?? [],\n          b: overrideRule?.type ?? OverrideRuleType.added,\n          c: state.selectedRules,\n        );\n      }),\n    );\n    final rules = vm3.a;\n    final type = vm3.b;\n    final selectedRules = vm3.c;\n    if (rules.isEmpty) {\n      return SliverToBoxAdapter(\n        child: SizedBox(\n          height: 300,\n          child: Center(\n            child: type == OverrideRuleType.added\n                ? Text(appLocalizations.noData)\n                : FilledButton(\n                    onPressed: () {\n                      final rules = ref.read(\n                        profileOverrideStateProvider.select(\n                          (state) => state.snippet?.rule ?? [],\n                        ),\n                      );\n                      ref\n                          .read(profileOverrideStateProvider.notifier)\n                          .updateState((state) {\n                            return state.copyWith.overrideData!.rule(\n                              overrideRules: rules,\n                            );\n                          });\n                    },\n                    child: Text(appLocalizations.getOriginRules),\n                  ),\n          ),\n        ),\n      );\n    }\n    return CacheItemExtentSliverReorderableList(\n      tag: CacheTag.rules,\n      itemBuilder: (context, index) {\n        final rule = rules[index];\n        return ReorderableDelayedDragStartListener(\n          key: ObjectKey(rule),\n          index: index,\n          child: _buildItem(\n            rule: rule,\n            isSelected: selectedRules.contains(rule.id),\n            onTab: () {\n              _handleSelect(ref, rule.id);\n            },\n            context: context,\n          ),\n        );\n      },\n      proxyDecorator: proxyDecorator,\n      itemCount: rules.length,\n      onReorder: (oldIndex, newIndex) {\n        if (oldIndex < newIndex) {\n          newIndex -= 1;\n        }\n        final newRules = List<Rule>.from(rules);\n        final item = newRules.removeAt(oldIndex);\n        newRules.insert(newIndex, item);\n        ref\n            .read(profileOverrideStateProvider.notifier)\n            .updateState(\n              (state) => state.copyWith.overrideData!(\n                rule: state.overrideData!.rule.updateRules((_) => newRules),\n              ),\n            );\n      },\n      keyBuilder: (int index) {\n        return rules[index].value;\n      },\n      itemExtentBuilder: (index) {\n        final rule = rules[index];\n        return 40 +\n            globalState.measure\n                .computeTextSize(\n                  Text(\n                    rule.value,\n                    style: context.textTheme.bodyMedium?.toJetBrainsMono,\n                  ),\n                  maxWidth: maxWidth,\n                )\n                .height;\n      },\n    );\n  }\n}\n\nclass AddRuleDialog extends StatefulWidget {\n  final ClashConfigSnippet snippet;\n  final Rule? rule;\n\n  const AddRuleDialog({super.key, required this.snippet, this.rule});\n\n  @override\n  State<AddRuleDialog> createState() => _AddRuleDialogState();\n}\n\nclass _AddRuleDialogState extends State<AddRuleDialog> {\n  late RuleAction _ruleAction;\n  final _ruleTargetController = TextEditingController();\n  final _contentController = TextEditingController();\n  final _ruleProviderController = TextEditingController();\n  final _subRuleController = TextEditingController();\n  bool _noResolve = false;\n  bool _src = false;\n  List<DropdownMenuEntry<String>> _targetItems = [];\n  List<DropdownMenuEntry<String>> _ruleProviderItems = [];\n  List<DropdownMenuEntry<String>> _subRuleItems = [];\n  final _formKey = GlobalKey<FormState>();\n\n  @override\n  void initState() {\n    _initState();\n    super.initState();\n  }\n\n  void _initState() {\n    _targetItems = [\n      ...widget.snippet.proxyGroups.map(\n        (item) => DropdownMenuEntry<String>(value: item.name, label: item.name),\n      ),\n      ...RuleTarget.values.map(\n        (item) => DropdownMenuEntry<String>(value: item.name, label: item.name),\n      ),\n    ];\n    _ruleProviderItems = [\n      ...widget.snippet.ruleProvider.map(\n        (item) => DropdownMenuEntry<String>(value: item.name, label: item.name),\n      ),\n    ];\n    _subRuleItems = [\n      ...widget.snippet.subRules.map(\n        (item) => DropdownMenuEntry<String>(value: item.name, label: item.name),\n      ),\n    ];\n    if (widget.rule != null) {\n      final parsedRule = ParsedRule.parseString(widget.rule!.value);\n      _ruleAction = parsedRule.ruleAction;\n      _contentController.text = parsedRule.content ?? '';\n      _ruleTargetController.text = parsedRule.ruleTarget ?? '';\n      _ruleProviderController.text = parsedRule.ruleProvider ?? '';\n      _subRuleController.text = parsedRule.subRule ?? '';\n      _noResolve = parsedRule.noResolve;\n      _src = parsedRule.src;\n      return;\n    }\n    _ruleAction = RuleAction.values.first;\n    if (_targetItems.isNotEmpty) {\n      _ruleTargetController.text = _targetItems.first.value;\n    }\n    if (_ruleProviderItems.isNotEmpty) {\n      _ruleProviderController.text = _ruleProviderItems.first.value;\n    }\n    if (_subRuleItems.isNotEmpty) {\n      _subRuleController.text = _subRuleItems.first.value;\n    }\n  }\n\n  @override\n  void didUpdateWidget(AddRuleDialog oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    if (oldWidget.rule != widget.rule) {\n      _initState();\n    }\n  }\n\n  void _handleSubmit() {\n    final res = _formKey.currentState?.validate();\n    if (res == false) {\n      return;\n    }\n    final parsedRule = ParsedRule(\n      ruleAction: _ruleAction,\n      content: _contentController.text,\n      ruleProvider: _ruleProviderController.text,\n      ruleTarget: _ruleTargetController.text,\n      subRule: _subRuleController.text,\n      noResolve: _noResolve,\n      src: _src,\n    );\n    final rule = widget.rule != null\n        ? widget.rule!.copyWith(value: parsedRule.value)\n        : Rule.value(parsedRule.value);\n    Navigator.of(context).pop(rule);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return CommonDialog(\n      title: appLocalizations.addRule,\n      actions: [\n        TextButton(\n          onPressed: _handleSubmit,\n          child: Text(appLocalizations.confirm),\n        ),\n      ],\n      child: DropdownMenuTheme(\n        data: DropdownMenuThemeData(\n          inputDecorationTheme: InputDecorationTheme(\n            border: OutlineInputBorder(),\n            labelStyle: context.textTheme.bodyLarge?.copyWith(\n              overflow: TextOverflow.ellipsis,\n            ),\n          ),\n        ),\n        child: Form(\n          key: _formKey,\n          child: LayoutBuilder(\n            builder: (_, constraints) {\n              return Column(\n                crossAxisAlignment: CrossAxisAlignment.start,\n                children: [\n                  FilledButton.tonal(\n                    onPressed: () async {\n                      _ruleAction =\n                          await globalState.showCommonDialog<RuleAction>(\n                            child: OptionsDialog<RuleAction>(\n                              title: appLocalizations.ruleName,\n                              options: RuleAction.values,\n                              textBuilder: (item) => item.value,\n                              value: _ruleAction,\n                            ),\n                          ) ??\n                          _ruleAction;\n                      setState(() {});\n                    },\n                    child: Text(_ruleAction.name),\n                  ),\n                  SizedBox(height: 24),\n                  _ruleAction == RuleAction.RULE_SET\n                      ? FormField(\n                          validator: (_) {\n                            if (_ruleProviderController.text.isEmpty) {\n                              return appLocalizations.emptyTip(\n                                appLocalizations.ruleProviders,\n                              );\n                            }\n                            return null;\n                          },\n                          builder: (field) {\n                            return DropdownMenu<String>(\n                              expandedInsets: EdgeInsets.zero,\n                              controller: _ruleProviderController,\n                              label: Text(appLocalizations.ruleProviders),\n                              menuHeight: 250,\n                              errorText: field.errorText,\n                              dropdownMenuEntries: _ruleProviderItems,\n                            );\n                          },\n                        )\n                      : TextFormField(\n                          controller: _contentController,\n                          enabled: _ruleAction != RuleAction.MATCH,\n                          decoration: InputDecoration(\n                            border: const OutlineInputBorder(),\n                            labelText: appLocalizations.content,\n                          ),\n                          validator: (_) {\n                            if (_ruleAction == RuleAction.MATCH) {\n                              return null;\n                            }\n                            if (_contentController.text.isEmpty) {\n                              return appLocalizations.emptyTip(\n                                appLocalizations.content,\n                              );\n                            }\n                            return null;\n                          },\n                        ),\n                  SizedBox(height: 24),\n                  _ruleAction == RuleAction.SUB_RULE\n                      ? FormField(\n                          validator: (_) {\n                            if (_subRuleController.text.isEmpty) {\n                              return appLocalizations.emptyTip(\n                                appLocalizations.subRule,\n                              );\n                            }\n                            return null;\n                          },\n                          builder: (filed) {\n                            return DropdownMenu<String>(\n                              width: 200,\n                              enableFilter: false,\n                              enableSearch: false,\n                              controller: _subRuleController,\n                              label: Text(appLocalizations.subRule),\n                              menuHeight: 250,\n                              dropdownMenuEntries: _subRuleItems,\n                            );\n                          },\n                        )\n                      : FormField<String>(\n                          validator: (_) {\n                            if (_ruleTargetController.text.isEmpty) {\n                              return appLocalizations.emptyTip(\n                                appLocalizations.ruleTarget,\n                              );\n                            }\n                            return null;\n                          },\n                          builder: (filed) {\n                            return DropdownMenu<String>(\n                              controller: _ruleTargetController,\n                              label: Text(appLocalizations.ruleTarget),\n                              width: 200,\n                              menuHeight: 250,\n                              enableFilter: false,\n                              enableSearch: false,\n                              dropdownMenuEntries: _targetItems,\n                              errorText: filed.errorText,\n                            );\n                          },\n                        ),\n                  if (_ruleAction.hasParams) ...[\n                    SizedBox(height: 20),\n                    Wrap(\n                      spacing: 8,\n                      children: [\n                        CommonCard(\n                          radius: 8,\n                          isSelected: _src,\n                          child: Padding(\n                            padding: EdgeInsets.symmetric(\n                              horizontal: 8,\n                              vertical: 8,\n                            ),\n                            child: Text(\n                              appLocalizations.sourceIp,\n                              style: context.textTheme.bodyMedium,\n                            ),\n                          ),\n                          onPressed: () {\n                            setState(() {\n                              _src = !_src;\n                            });\n                          },\n                        ),\n                        CommonCard(\n                          radius: 8,\n                          isSelected: _noResolve,\n                          child: Padding(\n                            padding: EdgeInsets.symmetric(\n                              horizontal: 8,\n                              vertical: 8,\n                            ),\n                            child: Text(\n                              appLocalizations.noResolve,\n                              style: context.textTheme.bodyMedium,\n                            ),\n                          ),\n                          onPressed: () {\n                            setState(() {\n                              _noResolve = !_noResolve;\n                            });\n                          },\n                        ),\n                      ],\n                    ),\n                  ],\n                  SizedBox(height: 20),\n                ],\n              );\n            },\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/profiles/profiles.dart",
    "content": "import 'dart:ui';\n\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/pages/editor.dart';\nimport 'package:bett_box/providers/providers.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/views/profiles/edit_profile.dart';\nimport 'package:bett_box/views/profiles/override_profile.dart';\nimport 'package:bett_box/views/profiles/scripts.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nimport 'add_profile.dart';\n\nclass ProfilesView extends ConsumerStatefulWidget {\n  const ProfilesView({super.key});\n\n  @override\n  ConsumerState<ProfilesView> createState() => _ProfilesViewState();\n}\n\nclass _ProfilesViewState extends ConsumerState<ProfilesView> {\n  Function? applyConfigDebounce;\n\n  void _handleShowAddExtendPage() {\n    showExtend(\n      globalState.navigatorKey.currentState!.context,\n      builder: (_, type) {\n        return AdaptiveSheetScaffold(\n          type: type,\n          body: AddProfileView(\n            context: globalState.navigatorKey.currentState!.context,\n          ),\n          title: '${appLocalizations.add}${appLocalizations.profile}',\n        );\n      },\n    );\n  }\n\n  Future<void> _updateProfiles() async {\n    final profiles = globalState.config.profiles;\n    final messages = [];\n    final updateProfiles = profiles.map<Future>((profile) async {\n      if (profile.type == ProfileType.file) return;\n      globalState.appController.setProfile(profile.copyWith(isUpdating: true));\n      try {\n        await globalState.appController.updateProfile(profile);\n      } catch (e) {\n        messages.add('${profile.label ?? profile.id}: $e \\n');\n        globalState.appController.setProfile(\n          profile.copyWith(isUpdating: false),\n        );\n      }\n    });\n    final titleMedium = context.textTheme.titleMedium;\n    await Future.wait(updateProfiles);\n    if (messages.isNotEmpty) {\n      globalState.showMessage(\n        title: appLocalizations.tip,\n        message: TextSpan(\n          children: [\n            for (final message in messages)\n              TextSpan(text: message, style: titleMedium),\n          ],\n        ),\n      );\n    }\n  }\n\n  List<Widget> _buildActions() {\n    return [\n      IconButton(\n        onPressed: () {\n          _updateProfiles();\n        },\n        icon: const Icon(Icons.sync),\n      ),\n      IconButton(\n        onPressed: () {\n          showExtend(\n            context,\n            builder: (_, type) {\n              return ScriptsView();\n            },\n          );\n        },\n        icon: Consumer(\n          builder: (_, ref, _) {\n            final isScriptMode = ref.watch(\n              scriptStateProvider.select((state) => state.realId != null),\n            );\n            return Icon(\n              Icons.functions,\n              color: isScriptMode ? context.colorScheme.primary : null,\n            );\n          },\n        ),\n      ),\n      IconButton(\n        onPressed: () {\n          final profiles = globalState.config.profiles;\n          showSheet(\n            context: context,\n            builder: (_, type) {\n              return ReorderableProfilesSheet(type: type, profiles: profiles);\n            },\n          );\n        },\n        icon: const Icon(Icons.sort),\n        iconSize: 26,\n      ),\n    ];\n  }\n\n  Widget _buildFAB() {\n    return FloatingActionButton.extended(\n      heroTag: null,\n      onPressed: _handleShowAddExtendPage,\n      icon: const Icon(Icons.add),\n      label: Text(appLocalizations.addProfile),\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    ref.watch(appSettingProvider.select((state) => state.locale));\n    return CommonScaffold(\n      title: appLocalizations.profiles,\n      floatingActionButton: _buildFAB(),\n      actions: _buildActions(),\n      body: Consumer(\n        builder: (_, ref, _) {\n          final profilesSelectorState = ref.watch(\n            profilesSelectorStateProvider,\n          );\n          if (profilesSelectorState.profiles.isEmpty) {\n            return NullStatus(label: appLocalizations.nullProfileDesc);\n          }\n          return Align(\n            alignment: Alignment.topCenter,\n            child: SingleChildScrollView(\n              key: profilesStoreKey,\n              padding: const EdgeInsets.only(\n                left: 16,\n                right: 16,\n                top: 16,\n                bottom: 88,\n              ),\n              child: Grid(\n                mainAxisSpacing: 16,\n                crossAxisSpacing: 16,\n                crossAxisCount: system.isAndroid\n                    ? 1\n                    : profilesSelectorState.profiles.length < profilesSelectorState.columns\n                        ? profilesSelectorState.profiles.length\n                        : profilesSelectorState.columns,\n                children: [\n                  for (\n                    int i = 0;\n                    i < profilesSelectorState.profiles.length;\n                    i++\n                  )\n                    GridItem(\n                      child: ProfileItem(\n                        key: Key(profilesSelectorState.profiles[i].id),\n                        profile: profilesSelectorState.profiles[i],\n                        groupValue: profilesSelectorState.currentProfileId,\n                        onChanged: (profileId) {\n                          ref.read(currentProfileIdProvider.notifier).value =\n                              profileId;\n                        },\n                      ),\n                    ),\n                ],\n              ),\n            ),\n          );\n        },\n      ),\n    );\n  }\n}\n\nclass ProfileItem extends StatelessWidget {\n  final Profile profile;\n  final String? groupValue;\n  final void Function(String? value) onChanged;\n\n  const ProfileItem({\n    super.key,\n    required this.profile,\n    required this.groupValue,\n    required this.onChanged,\n  });\n\n  Future<void> _handleDeleteProfile(BuildContext context) async {\n    final res = await globalState.showMessage(\n      title: appLocalizations.tip,\n      message: TextSpan(\n        text: appLocalizations.deleteTip(appLocalizations.profile),\n      ),\n    );\n    if (res != true) {\n      return;\n    }\n    await globalState.appController.deleteProfile(profile.id);\n  }\n\n  Future<void> _handlePreviewRuntimeConfig(BuildContext context) async {\n    await globalState.appController.safeRun(\n      () async {\n        final patchConfig = globalState.config.patchClashConfig;\n        final runtimeConfig = await globalState.patchRawConfig(\n          patchConfig: patchConfig,\n        );\n        final content = await encodeYamlTask(runtimeConfig);\n        if (!context.mounted) {\n          return;\n        }\n\n        final previewPage = EditorPage(\n          title:\n              '${appLocalizations.runtimeConfig} - ${profile.label ?? profile.id}',\n          content: content,\n          readOnly: true,\n        );\n        BaseNavigator.push<String>(context, previewPage);\n      },\n      needLoading: true,\n      title: appLocalizations.tip,\n    );\n  }\n\n  Future updateProfile() async {\n    final appController = globalState.appController;\n    if (profile.type == ProfileType.file) return;\n    await globalState.appController.safeRun(silence: false, () async {\n      try {\n        appController.setProfile(profile.copyWith(isUpdating: true));\n        await appController.updateProfile(profile);\n      } catch (e) {\n        appController.setProfile(profile.copyWith(isUpdating: false));\n        rethrow;\n      }\n    });\n  }\n\n  void _handleShowEditExtendPage(BuildContext context) {\n    showExtend(\n      context,\n      builder: (_, type) {\n        return AdaptiveSheetScaffold(\n          type: type,\n          body: EditProfileView(profile: profile, context: context),\n          title: '${appLocalizations.edit}${appLocalizations.profile}',\n        );\n      },\n    );\n  }\n\n  List<Widget> _buildUrlProfileInfo(BuildContext context) {\n    final subscriptionInfo = profile.subscriptionInfo;\n    final updateTimeText = profile.lastUpdateDate?.lastUpdateTimeDesc ?? '';\n\n    return [\n      const SizedBox(height: 8),\n      if (subscriptionInfo != null) ...[\n        SubscriptionInfoView(subscriptionInfo: subscriptionInfo),\n        // Traffic / Total · Expiry - Update time\n        Row(\n          children: [\n            Expanded(\n              child: Text(\n                '${_getTrafficText(subscriptionInfo)} · ${_getExpireText(subscriptionInfo)} - $updateTimeText',\n                style: context.textTheme.labelMedium?.toLight,\n                maxLines: 1,\n                overflow: TextOverflow.ellipsis,\n              ),\n            ),\n          ],\n        ),\n      ] else\n        // Show only update time when no subscription info\n        Row(\n          children: [\n            Expanded(\n              child: Text(\n                updateTimeText,\n                style: context.textTheme.labelMedium?.toLight,\n                maxLines: 1,\n                overflow: TextOverflow.ellipsis,\n              ),\n            ),\n          ],\n        ),\n    ];\n  }\n\n  String _getTrafficText(SubscriptionInfo subscriptionInfo) {\n    final use = subscriptionInfo.upload + subscriptionInfo.download;\n    final total = subscriptionInfo.total;\n\n    // Show Unlimited when no traffic info\n    if (use == 0 && total == 0) {\n      return 'Unlimited';\n    }\n\n    // Total is 0 but has usage\n    if (total == 0) {\n      final useShow = TrafficValue(value: use).show;\n      return '$useShow / Unlimited';\n    }\n\n    final useShow = TrafficValue(value: use).show;\n    final totalShow = TrafficValue(value: total).show;\n    return '$useShow / $totalShow';\n  }\n\n  String _getExpireText(SubscriptionInfo subscriptionInfo) {\n    if (subscriptionInfo.expire == 0) {\n      return appLocalizations.infiniteTime;\n    }\n    return DateTime.fromMillisecondsSinceEpoch(\n      subscriptionInfo.expire * 1000,\n    ).show;\n  }\n\n  List<Widget> _buildFileProfileInfo(BuildContext context) {\n    return [\n      const SizedBox(height: 8),\n      Text(\n        profile.lastUpdateDate?.lastUpdateTimeDesc ?? '',\n        style: context.textTheme.labelMedium?.toLight,\n      ),\n    ];\n  }\n\n  // _handleCopyLink(BuildContext context) async {\n  //   await Clipboard.setData(\n  //     ClipboardData(\n  //       text: profile.url,\n  //     ),\n  //   );\n  //   if (context.mounted) {\n  //     context.showNotifier(appLocalizations.copySuccess);\n  //   }\n  // }\n\n  Future<void> _handleExportFile(BuildContext context) async {\n    final res = await globalState.appController.safeRun<bool>(\n      () async {\n        final file = await profile.getFile();\n        final value = await picker.saveFile(\n          profile.label ?? profile.id,\n          await file.readAsBytes(),\n        );\n        if (value == null) return false;\n        return true;\n      },\n      needLoading: true,\n      title: appLocalizations.tip,\n    );\n    if (res == true && context.mounted) {\n      context.showNotifier(appLocalizations.exportSuccess);\n    }\n  }\n\n  void _handlePushGenProfilePage(BuildContext context, String id) {\n    final overrideProfileView = OverrideProfileView(profileId: id);\n    BaseNavigator.push(context, overrideProfileView);\n  }\n\n  List<PopupMenuItemData> _buildMenuItems(BuildContext context) {\n    return [\n      PopupMenuItemData(\n        icon: Icons.edit_outlined,\n        label: appLocalizations.edit,\n        onPressed: () {\n          _handleShowEditExtendPage(context);\n        },\n      ),\n      if (profile.type == ProfileType.url) ...[\n        PopupMenuItemData(\n          icon: Icons.sync_alt_sharp,\n          label: appLocalizations.sync,\n          onPressed: () {\n            updateProfile();\n          },\n        ),\n      ],\n      PopupMenuItemData(\n        icon: Icons.extension_outlined,\n        label: appLocalizations.override,\n        onPressed: () {\n          _handlePushGenProfilePage(context, profile.id);\n        },\n      ),\n      PopupMenuItemData(\n        icon: Icons.file_copy_outlined,\n        label: appLocalizations.exportFile,\n        onPressed: () {\n          _handleExportFile(context);\n        },\n      ),\n      PopupMenuItemData(\n        icon: Icons.delete_outlined,\n        label: appLocalizations.delete,\n        onPressed: () {\n          _handleDeleteProfile(context);\n        },\n      ),\n    ];\n  }\n\n  void _showTVMenu(BuildContext context) {\n    final items = _buildMenuItems(context);\n    showModalBottomSheet(\n      context: context,\n      builder: (context) => SafeArea(\n        child: Column(\n          mainAxisSize: MainAxisSize.min,\n          children: [\n            for (final item in items)\n              ListTile(\n                leading: item.icon != null ? Icon(item.icon) : null,\n                title: Text(item.label),\n                onTap: () {\n                  Navigator.of(context).pop();\n                  item.onPressed!();\n                },\n              ),\n          ],\n        ),\n      ),\n    );\n  }\n\n  Widget _buildNormalLayout(BuildContext context) {\n    final trailingWidget = SizedBox(\n      height: 40,\n      width: 40,\n      child: FadeThroughBox(\n        child: profile.isUpdating\n            ? const Padding(\n                padding: EdgeInsets.all(8),\n                child: CircularProgressIndicator(),\n              )\n            : CommonPopupBox(\n                popup: CommonPopupMenu(items: _buildMenuItems(context)),\n                targetBuilder: (open) {\n                  return IconButton(\n                    onPressed: () {\n                      open();\n                    },\n                    icon: Icon(Icons.more_vert),\n                  );\n                },\n              ),\n      ),\n    );\n    return Stack(\n      children: [\n        ListItem(\n          key: Key(profile.id),\n          horizontalTitleGap: 16,\n          padding: const EdgeInsets.symmetric(horizontal: 16),\n          title: Container(\n            padding: const EdgeInsets.symmetric(vertical: 4),\n            child: Column(\n              crossAxisAlignment: CrossAxisAlignment.stretch,\n              mainAxisAlignment: MainAxisAlignment.center,\n              children: [\n                Padding(\n                  padding: const EdgeInsets.only(right: 52),\n                  child: Text(\n                    profile.label ?? profile.id,\n                    style: context.textTheme.titleMedium,\n                    maxLines: 1,\n                    overflow: TextOverflow.ellipsis,\n                  ),\n                ),\n                Column(\n                  mainAxisSize: MainAxisSize.min,\n                  crossAxisAlignment: CrossAxisAlignment.stretch,\n                  mainAxisAlignment: MainAxisAlignment.center,\n                  children: [\n                    ...switch (profile.type) {\n                      ProfileType.file => _buildFileProfileInfo(context),\n                      ProfileType.url => _buildUrlProfileInfo(context),\n                    },\n                  ],\n                ),\n              ],\n            ),\n          ),\n          tileTitleAlignment: ListTileTitleAlignment.titleHeight,\n        ),\n        Positioned(top: 6, right: 6, child: trailingWidget),\n      ],\n    );\n  }\n\n  Widget _buildTVLayout(BuildContext context) {\n    return Row(\n      children: [\n        Expanded(\n          child: InkWell(\n            onTap: () => onChanged(profile.id),\n            child: ListItem(\n              key: Key(profile.id),\n              horizontalTitleGap: 16,\n              padding: const EdgeInsets.symmetric(horizontal: 16),\n              title: Container(\n                padding: const EdgeInsets.symmetric(vertical: 4),\n                child: Column(\n                  crossAxisAlignment: CrossAxisAlignment.stretch,\n                  mainAxisAlignment: MainAxisAlignment.center,\n                  children: [\n                    Text(\n                      profile.label ?? profile.id,\n                      style: context.textTheme.titleMedium,\n                      maxLines: 1,\n                      overflow: TextOverflow.ellipsis,\n                    ),\n                    Column(\n                      mainAxisSize: MainAxisSize.min,\n                      crossAxisAlignment: CrossAxisAlignment.stretch,\n                      mainAxisAlignment: MainAxisAlignment.center,\n                      children: [\n                        ...switch (profile.type) {\n                          ProfileType.file => _buildFileProfileInfo(context),\n                          ProfileType.url => _buildUrlProfileInfo(context),\n                        },\n                      ],\n                    ),\n                  ],\n                ),\n              ),\n              tileTitleAlignment: ListTileTitleAlignment.titleHeight,\n            ),\n          ),\n        ),\n        IconButton(\n          onPressed: () => _showTVMenu(context),\n          icon: const Icon(Icons.more_vert),\n        ),\n      ],\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final isTV = globalState.isAndroidTV;\n    return CommonCard(\n      isSelected: profile.id == groupValue,\n      onPressed: isTV ? null : () => onChanged(profile.id),\n      onLongPress: isTV ? null : () => _handlePreviewRuntimeConfig(context),\n      child: isTV ? _buildTVLayout(context) : _buildNormalLayout(context),\n    );\n  }\n}\n\nclass ReorderableProfilesSheet extends StatefulWidget {\n  final List<Profile> profiles;\n  final SheetType type;\n\n  const ReorderableProfilesSheet({\n    super.key,\n    required this.profiles,\n    required this.type,\n  });\n\n  @override\n  State<ReorderableProfilesSheet> createState() =>\n      _ReorderableProfilesSheetState();\n}\n\nclass _ReorderableProfilesSheetState extends State<ReorderableProfilesSheet> {\n  late List<Profile> profiles;\n\n  @override\n  void initState() {\n    super.initState();\n    profiles = List.from(widget.profiles);\n  }\n\n  Widget proxyDecorator(Widget child, int index, Animation<double> animation) {\n    final profile = profiles[index];\n    return AnimatedBuilder(\n      animation: animation,\n      builder: (_, Widget? child) {\n        final double animValue = Curves.easeInOut.transform(animation.value);\n        final double scale = lerpDouble(1, 1.02, animValue)!;\n        return Transform.scale(scale: scale, child: child);\n      },\n      child: Container(\n        key: Key(profile.id),\n        padding: const EdgeInsets.symmetric(vertical: 4),\n        child: CommonCard(\n          type: CommonCardType.filled,\n          child: ListTile(\n            contentPadding: const EdgeInsets.only(right: 44, left: 16),\n            title: Text(profile.label ?? profile.id),\n          ),\n        ),\n      ),\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return AdaptiveSheetScaffold(\n      type: widget.type,\n      actions: [\n        IconButton(\n          onPressed: () {\n            Navigator.of(context).pop();\n            globalState.appController.setProfiles(profiles);\n          },\n          icon: Icon(Icons.save),\n        ),\n      ],\n      body: Padding(\n        padding: EdgeInsets.only(bottom: 32, top: 16),\n        child: ReorderableListView.builder(\n          buildDefaultDragHandles: false,\n          padding: const EdgeInsets.symmetric(horizontal: 12),\n          proxyDecorator: proxyDecorator,\n          onReorder: (oldIndex, newIndex) {\n            setState(() {\n              if (oldIndex < newIndex) {\n                newIndex -= 1;\n              }\n              final profile = profiles.removeAt(oldIndex);\n              profiles.insert(newIndex, profile);\n            });\n          },\n          itemBuilder: (_, index) {\n            final profile = profiles[index];\n            return Container(\n              key: Key(profile.id),\n              padding: const EdgeInsets.symmetric(vertical: 4),\n              child: CommonCard(\n                type: CommonCardType.filled,\n                child: ListTile(\n                  contentPadding: const EdgeInsets.only(right: 16, left: 16),\n                  title: Text(profile.label ?? profile.id),\n                  trailing: ReorderableDragStartListener(\n                    index: index,\n                    child: const Icon(Icons.drag_handle),\n                  ),\n                ),\n              ),\n            );\n          },\n          itemCount: profiles.length,\n        ),\n      ),\n      title: appLocalizations.profilesSort,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/profiles/scripts.dart",
    "content": "import 'dart:convert';\n\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/pages/editor.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/card.dart';\nimport 'package:bett_box/widgets/dialog.dart';\nimport 'package:bett_box/widgets/input.dart';\nimport 'package:bett_box/widgets/list.dart';\nimport 'package:bett_box/widgets/null_status.dart';\nimport 'package:bett_box/widgets/popup.dart';\nimport 'package:bett_box/widgets/scaffold.dart';\nimport 'package:bett_box/widgets/scroll.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass ScriptsView extends ConsumerStatefulWidget {\n  const ScriptsView({super.key});\n\n  @override\n  ConsumerState<ScriptsView> createState() => _ScriptsViewState();\n}\n\nclass _ScriptsViewState extends ConsumerState<ScriptsView> {\n  Future<void> _handleDelScript(String label) async {\n    final res = await globalState.showMessage(\n      message: TextSpan(\n        text: appLocalizations.deleteTip(appLocalizations.script),\n      ),\n    );\n    if (res != true) {\n      return;\n    }\n    ref.read(scriptStateProvider.notifier).del(label);\n  }\n\n  Future<void> _handleSyncScript(String id) async {\n    await globalState.appController.safeRun(\n      silence: false,\n      () async {\n        await ref.read(scriptStateProvider.notifier).syncScript(id);\n        globalState.showNotifier(appLocalizations.success);\n      },\n    );\n  }\n\n  Widget _buildContent() {\n    return Consumer(\n      builder: (_, ref, _) {\n        final vm2 = ref.watch(\n          scriptStateProvider.select(\n            (state) => VM2(a: state.currentId, b: state.scripts),\n          ),\n        );\n        final currentId = vm2.a;\n        final scripts = vm2.b;\n        if (scripts.isEmpty) {\n          return NullStatus(\n            label: appLocalizations.nullTip(appLocalizations.script),\n          );\n        }\n        return CommonScrollBar(\n          controller: null,\n          child: ListView.builder(\n            padding: kMaterialListPadding.copyWith(bottom: 16 + 64),\n            itemCount: scripts.length,\n            itemBuilder: (_, index) {\n            final script = scripts[index];\n            return Container(\n              padding: kTabLabelPadding,\n              margin: EdgeInsets.symmetric(vertical: 6),\n              child: CommonCard(\n                type: CommonCardType.filled,\n                radius: 16,\n                child: ListItem.radio(\n                  padding: const EdgeInsets.only(left: 12, right: 12),\n                  title: Text(script.label),\n                  delegate: RadioDelegate(\n                    value: script.id,\n                    groupValue: currentId,\n                    onChanged: (_) {\n                      ref.read(scriptStateProvider.notifier).setId(script.id);\n                    },\n                  ),\n                  trailing: CommonPopupBox(\n                    targetBuilder: (open) {\n                      return IconButton(\n                        onPressed: () {\n                          open();\n                        },\n                        icon: Icon(Icons.more_vert),\n                      );\n                    },\n                    popup: CommonPopupMenu(\n                      items: [\n                        PopupMenuItemData(\n                          icon: Icons.edit,\n                          label: appLocalizations.edit,\n                          onPressed: () {\n                            _handleToEditor(script: script);\n                          },\n                        ),\n                        // URL 导入的脚本才显示同步按鈕\n                        if (script.url != null && script.url!.isNotEmpty)\n                          PopupMenuItemData(\n                            icon: Icons.sync,\n                            label: appLocalizations.sync,\n                            onPressed: () {\n                              _handleSyncScript(script.id);\n                            },\n                          ),\n                        PopupMenuItemData(\n                          icon: Icons.delete,\n                          label: appLocalizations.delete,\n                          onPressed: () {\n                            _handleDelScript(script.label);\n                          },\n                        ),\n                      ],\n                    ),\n                  ),\n                ),\n              ),\n            );\n            },\n          ),\n        );\n      },\n    );\n  }\n\n  Future<void> _handleEditorSave(\n    BuildContext _,\n    String title,\n    String content, {\n    Script? script,\n    String? url,\n  }) async {\n    Script newScript =\n        script?.copyWith(label: title, content: content, url: url) ??\n        Script.create(label: title, content: content, url: url);\n    if (newScript.label.isEmpty) {\n      final res = await globalState.showCommonDialog<String>(\n        child: InputDialog(\n          title: appLocalizations.save,\n          value: '',\n          hintText: appLocalizations.pleaseEnterScriptName,\n          validator: (value) {\n            if (value == null || value.isEmpty) {\n              return appLocalizations.emptyTip(appLocalizations.name);\n            }\n            if (value != script?.label) {\n              final isExits = ref\n                  .read(scriptStateProvider.notifier)\n                  .isExits(value);\n              if (isExits) {\n                return appLocalizations.existsTip(appLocalizations.name);\n              }\n            }\n            return null;\n          },\n        ),\n      );\n      if (res == null || res.isEmpty) {\n        return;\n      }\n      newScript = newScript.copyWith(label: res);\n    }\n    if (newScript.label != script?.label) {\n      final isExits = ref\n          .read(scriptStateProvider.notifier)\n          .isExits(newScript.label);\n      if (isExits) {\n        globalState.showMessage(\n          message: TextSpan(\n            text: appLocalizations.existsTip(appLocalizations.name),\n          ),\n        );\n        return;\n      }\n    }\n    ref.read(scriptStateProvider.notifier).setScript(newScript);\n    if (mounted) {\n      Navigator.of(context).pop();\n    }\n  }\n\n  Future<bool> _handleEditorPop(\n    BuildContext _,\n    String title,\n    String content,\n    String raw, {\n    Script? script,\n  }) async {\n    if (content == raw) {\n      return true;\n    }\n    final res = await globalState.showMessage(\n      message: TextSpan(text: appLocalizations.saveChanges),\n    );\n    if (res == true && mounted) {\n      _handleEditorSave(context, title, content, script: script, url: script?.url);\n    } else {\n      return true;\n    }\n    return false;\n  }\n\n  void _handleToEditor({Script? script, String? initialContent, String? url}) {\n    final title = script?.label ?? '';\n    final raw = script?.content ?? initialContent ?? scriptTemplate;\n    String? importedUrl = url ?? script?.url;\n    BaseNavigator.push(\n      context,\n      EditorPage(\n        titleEditable: true,\n        title: title,\n        supportRemoteDownload: true,\n        onUrlImport: (downloadedUrl) {\n          importedUrl = downloadedUrl;\n        },\n        onSave: (context, title, content) {\n          final scriptToSave = script != null\n              ? script.copyWith(url: importedUrl)\n              : null;\n          _handleEditorSave(context, title, content, script: scriptToSave, url: importedUrl);\n        },\n        onPop: (context, title, content) {\n          return _handleEditorPop(context, title, content, raw, script: script);\n        },\n        languages: const [Language.javaScript],\n        content: raw,\n      ),\n    );\n  }\n\n  Future<void> _handleImport() async {\n    final option = await globalState.showCommonDialog<ImportOption>(\n      child: const _ScriptImportOptionsDialog(),\n    );\n    if (option == null) {\n      return;\n    }\n\n    switch (option) {\n      case ImportOption.code:\n        _handleToEditor();\n      case ImportOption.url:\n        await _handleUrlImport();\n      case ImportOption.file:\n        await _handleFileImport();\n    }\n  }\n\n  Future<void> _handleUrlImport() async {\n    final url = await globalState.showCommonDialog<String>(\n      child: InputDialog(\n        title: appLocalizations.importUrl,\n        value: '',\n        labelText: appLocalizations.url,\n        validator: (value) {\n          if (value == null || value.isEmpty) {\n            return appLocalizations.emptyTip(appLocalizations.value);\n          }\n          if (!value.isUrl) {\n            return appLocalizations.urlTip(appLocalizations.value);\n          }\n          return null;\n        },\n      ),\n    );\n    if (url == null || url.isEmpty) {\n      return;\n    }\n\n    try {\n      final res = await request.getTextResponseForUrl(url);\n      if (mounted) {\n        _handleToEditor(initialContent: res.data, url: url);\n      }\n    } catch (e) {\n      globalState.showMessage(\n        message: TextSpan(text: '${appLocalizations.importFailed}: $e'),\n      );\n    }\n  }\n\n  Future<void> _handleFileImport() async {\n    final file = await picker.pickerFile();\n    if (file == null) {\n      return;\n    }\n    final bytes = file.bytes;\n    if (bytes == null) {\n      return;\n    }\n    final content = utf8.decode(bytes);\n    if (mounted) {\n      _handleToEditor(initialContent: content);\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return CommonScaffold(\n      floatingActionButton: FloatingActionButton(\n        onPressed: () {\n          _handleImport();\n        },\n        child: Icon(Icons.add),\n      ),\n      body: _buildContent(),\n      title: appLocalizations.script,\n    );\n  }\n}\n\nclass _ScriptImportOptionsDialog extends StatelessWidget {\n  const _ScriptImportOptionsDialog();\n\n  @override\n  Widget build(BuildContext context) {\n    return CommonDialog(\n      title: appLocalizations.import,\n      padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16),\n      child: Wrap(\n        children: [\n          ListItem(\n            onTap: () {\n              Navigator.of(context).pop(ImportOption.code);\n            },\n            leading: const Icon(Icons.code),\n            title: Text(appLocalizations.importFromCode),\n          ),\n          ListItem(\n            onTap: () {\n              Navigator.of(context).pop(ImportOption.url);\n            },\n            leading: const Icon(Icons.link),\n            title: Text(appLocalizations.importUrl),\n          ),\n          ListItem(\n            onTap: () {\n              Navigator.of(context).pop(ImportOption.file);\n            },\n            leading: const Icon(Icons.file_open),\n            title: Text(appLocalizations.importFile),\n          ),\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/proxies/advanced_settings.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/providers/providers.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass ProxiesAdvancedSettings extends ConsumerWidget {\n  const ProxiesAdvancedSettings({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    return ListView(\n      padding: const EdgeInsets.only(bottom: 20),\n      children: generateSection(\n        items: [\n          const _NodeExclusionWithInverseItem(),\n          const _ConcurrencyLimitItem(),\n          const _HealthCheckTimeoutItem(),\n          const _DelayAnimationItem(),\n        ],\n      ),\n    );\n  }\n}\n\nclass _NodeExclusionWithInverseItem extends ConsumerWidget {\n  const _NodeExclusionWithInverseItem();\n\n  String? _validateRegex(String? value) {\n    if (value == null || value.trim().isEmpty) return null;\n    try {\n      RegExp(value.trim());\n      return null;\n    } catch (e) {\n      final detail = e is FormatException && e.message.isNotEmpty\n          ? e.message\n          : null;\n      return detail != null\n          ? '${appLocalizations.formatError}: $detail'\n          : appLocalizations.formatError;\n    }\n  }\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    // 通过响应式 provider 监听，对话框确认后立即重建\n    final nodeExcludeFilter = ref.watch(nodeExcludeFilterProvider);\n\n    return ListItem(\n      leading: const Icon(Icons.filter_alt_outlined),\n      title: Text(appLocalizations.nodeExclusion),\n      subtitle: Text(appLocalizations.nodeExclusionDesc),\n      onTap: () async {\n        await globalState.showCommonDialog(\n          child: _NodeExclusionDialog(\n            currentValue: nodeExcludeFilter,\n            validator: _validateRegex,\n          ),\n        );\n      },\n    );\n  }\n}\n\nclass _NodeExclusionDialog extends ConsumerStatefulWidget {\n  final String currentValue;\n  final String? Function(String?)? validator;\n\n  const _NodeExclusionDialog({\n    required this.currentValue,\n    this.validator,\n  });\n\n  @override\n  ConsumerState<_NodeExclusionDialog> createState() => _NodeExclusionDialogState();\n}\n\nclass _NodeExclusionDialogState extends ConsumerState<_NodeExclusionDialog> {\n  late TextEditingController _controller;\n  final _formKey = GlobalKey<FormState>();\n\n  @override\n  void initState() {\n    super.initState();\n    _controller = TextEditingController(text: widget.currentValue);\n  }\n\n  @override\n  void dispose() {\n    _controller.dispose();\n    super.dispose();\n  }\n\n  void _handleSubmit() {\n    if (_formKey.currentState?.validate() == false) return;\n\n    final filter = _controller.text.trim();\n    // 通过 provider notifier 更新，触发父 Widget 响应式重建\n    ref.read(nodeExcludeFilterProvider.notifier).value = filter;\n    globalState.appController.applyProfileDebounce();\n    Navigator.of(context, rootNavigator: true).pop();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return CommonDialog(\n      title: appLocalizations.nodeExclusion,\n      actions: [\n        TextButton(\n          onPressed: _handleSubmit,\n          child: Text(appLocalizations.submit),\n        ),\n      ],\n      child: Form(\n        key: _formKey,\n        child: Column(\n          mainAxisSize: MainAxisSize.min,\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: [\n            TextFormField(\n              controller: _controller,\n              decoration: InputDecoration(\n                border: const OutlineInputBorder(),\n                labelText: appLocalizations.nodeExclusion,\n                hintText: appLocalizations.nodeExclusionPlaceholder,\n              ),\n              validator: widget.validator,\n              onFieldSubmitted: (_) => _handleSubmit(),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n\nclass _ConcurrencyLimitItem extends ConsumerWidget {\n  const _ConcurrencyLimitItem();\n\n  static const _options = [1, 4, 8, 16, 32, 64];\n\n  String _getDisplayText(int value, BuildContext context) {\n    if (value == 64) {\n      return '$value (${appLocalizations.notRecommended})';\n    }\n    return '$value';\n  }\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final concurrencyLimit = ref.watch(\n      proxiesStyleSettingProvider.select((state) => state.concurrencyLimit),\n    );\n\n    return ListItem<int>.options(\n      leading: const Icon(Icons.speed),\n      title: Text(appLocalizations.concurrencyLimit),\n      subtitle: Text(appLocalizations.concurrencyLimitDesc),\n      delegate: OptionsDelegate(\n        title: appLocalizations.concurrencyLimit,\n        options: _options,\n        value: concurrencyLimit,\n        textBuilder: (value) => _getDisplayText(value, context),\n        onChanged: (value) {\n          if (value != null) {\n            ref.read(proxiesStyleSettingProvider.notifier).updateState(\n                  (state) => state.copyWith(concurrencyLimit: value),\n                );\n          }\n        },\n      ),\n    );\n  }\n}\n\nclass _HealthCheckTimeoutItem extends ConsumerWidget {\n  const _HealthCheckTimeoutItem();\n\n  static const _options = [1000, 2000, 3000, 5000, 8000];\n\n  String _getDisplayText(int value) {\n    return '${value ~/ 1000}s';\n  }\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final timeout = ref.watch(healthCheckTimeoutProvider);\n\n    return ListItem<int>.options(\n      leading: const Icon(Icons.timer_outlined),\n      title: Text(appLocalizations.healthCheckTimeout),\n      subtitle: Text(appLocalizations.healthCheckTimeoutDesc),\n      delegate: OptionsDelegate(\n        title: appLocalizations.healthCheckTimeout,\n        options: _options,\n        value: timeout,\n        textBuilder: _getDisplayText,\n        onChanged: (value) {\n          if (value != null) {\n            ref\n                .read(healthCheckTimeoutProvider.notifier)\n                .updateState((_) => value);\n            globalState.appController.applyProfileDebounce();\n          }\n        },\n      ),\n    );\n  }\n}\n\nclass _DelayAnimationItem extends ConsumerWidget {\n  const _DelayAnimationItem();\n\n  String _getTextForDelayAnimation(DelayAnimationType type) {\n    return switch (type) {\n      DelayAnimationType.none => appLocalizations.noAnimation,\n      DelayAnimationType.rotatingCircle => appLocalizations.rotatingCircle,\n      DelayAnimationType.pulse => appLocalizations.pulse,\n      DelayAnimationType.spinningLines => appLocalizations.spinningLines,\n      DelayAnimationType.threeInOut => appLocalizations.threeInOut,\n      DelayAnimationType.threeBounce => appLocalizations.threeBounce,\n      DelayAnimationType.circle => appLocalizations.circle,\n      DelayAnimationType.fadingCircle => appLocalizations.fadingCircle,\n      DelayAnimationType.fadingFour => appLocalizations.fadingFour,\n      DelayAnimationType.wave => appLocalizations.wave,\n      DelayAnimationType.doubleBounce => appLocalizations.doubleBounce,\n    };\n  }\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final delayAnimation = ref.watch(\n      proxiesStyleSettingProvider.select((state) => state.delayAnimation),\n    );\n\n    return ListItem<DelayAnimationType>.options(\n      leading: const Icon(Icons.animation),\n      title: Text(appLocalizations.delayAnimation),\n      subtitle: Text(appLocalizations.delayAnimationDesc),\n      delegate: OptionsDelegate(\n        title: appLocalizations.delayAnimation,\n        options: DelayAnimationType.values,\n        value: delayAnimation,\n        textBuilder: (value) => _getTextForDelayAnimation(value),\n        onChanged: (value) {\n          if (value != null) {\n            ref.read(proxiesStyleSettingProvider.notifier).updateState(\n                  (state) => state.copyWith(delayAnimation: value),\n                );\n          }\n        },\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/proxies/card.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/providers/providers.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/views/proxies/common.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:flutter_spinkit/flutter_spinkit.dart';\n\nclass ProxyCard extends StatelessWidget {\n  final String groupName;\n  final Proxy proxy;\n  final GroupType groupType;\n  final ProxyCardType type;\n  final String? testUrl;\n\n  const ProxyCard({\n    super.key,\n    required this.groupName,\n    required this.testUrl,\n    required this.proxy,\n    required this.groupType,\n    required this.type,\n  });\n\n  Measure get measure => globalState.measure;\n\n  bool get _isNonTestableProxy {\n    final name = proxy.name.toUpperCase();\n    return name == 'REJECT' || name == 'REJECT-DROP' || name == 'PASS';\n  }\n\n  void _handleTestCurrentDelay() {\n    if (_isNonTestableProxy) return;\n    proxyDelayTest(proxy, testUrl);\n  }\n\n  Widget _buildDelayText(BuildContext context) {\n    return SizedBox(\n      height: measure.labelSmallHeight,\n      child: Consumer(\n        builder: (_, ref, _) {\n          final delay = ref.watch(\n            getDelayProvider(proxyName: proxy.name, testUrl: testUrl),\n          );\n          final delayAnimation = ref.watch(\n            proxiesStyleSettingProvider.select((s) => s.delayAnimation),\n          );\n\n          // REJECT, REJECT-DROP, PASS 节点不显示测试按钮\n          if (_isNonTestableProxy) {\n            return const SizedBox(\n              height: 0,\n              width: 0,\n            );\n          }\n\n          if (delay == 0) {\n            return SizedBox(\n              height: measure.labelSmallHeight,\n              width: measure.labelSmallHeight,\n              child: delayAnimation == DelayAnimationType.none\n                  ? const CircularProgressIndicator(strokeWidth: 2)\n                  : _buildDelayAnimation(\n                      delayAnimation,\n                      measure.labelSmallHeight,\n                      context.colorScheme.primary,\n                    ),\n            );\n          }\n\n          if (delay == null) {\n            return SizedBox(\n              height: measure.labelSmallHeight,\n              width: measure.labelSmallHeight,\n              child: IconButton(\n                icon: const Icon(Icons.bolt),\n                iconSize: measure.labelSmallHeight,\n                padding: EdgeInsets.zero,\n                onPressed: _handleTestCurrentDelay,\n              ),\n            );\n          }\n\n          return GestureDetector(\n            onTap: _handleTestCurrentDelay,\n            child: Text(\n              delay > 0 ? '$delay ms' : 'Timeout',\n              style: context.textTheme.labelSmall?.copyWith(\n                overflow: TextOverflow.ellipsis,\n                color: utils.getDelayColor(delay),\n              ),\n            ),\n          );\n        },\n      ),\n    );\n  }\n\n  Widget _buildDelayAnimation(\n    DelayAnimationType animationType,\n    double size,\n    Color color,\n  ) {\n    return switch (animationType) {\n      DelayAnimationType.none => Icon(Icons.bolt, size: size),\n      DelayAnimationType.rotatingCircle =>\n        SpinKitRotatingCircle(color: color, size: size),\n      DelayAnimationType.pulse => SpinKitPulse(color: color, size: size),\n      DelayAnimationType.spinningLines =>\n        SpinKitSpinningLines(color: color, size: size),\n      DelayAnimationType.threeInOut =>\n        SpinKitThreeInOut(color: color, size: size),\n      DelayAnimationType.threeBounce =>\n        SpinKitThreeBounce(color: color, size: size),\n      DelayAnimationType.circle => SpinKitCircle(color: color, size: size),\n      DelayAnimationType.fadingCircle =>\n        SpinKitFadingCircle(color: color, size: size),\n      DelayAnimationType.fadingFour =>\n        SpinKitFadingFour(color: color, size: size),\n      DelayAnimationType.wave => SpinKitWave(color: color, size: size),\n      DelayAnimationType.doubleBounce =>\n        SpinKitDoubleBounce(color: color, size: size),\n    };\n  }\n\n  Widget _buildProxyNameText(BuildContext context) {\n    if (type == ProxyCardType.min) {\n      return SizedBox(\n        height: measure.bodyMediumHeight * 1,\n        child: EmojiText(\n          proxy.name,\n          maxLines: 1,\n          overflow: TextOverflow.ellipsis,\n          style: context.textTheme.bodyMedium,\n        ),\n      );\n    } else {\n      return SizedBox(\n        height: measure.bodyMediumHeight * 2,\n        child: EmojiText(\n          proxy.name,\n          maxLines: 2,\n          overflow: TextOverflow.ellipsis,\n          style: context.textTheme.bodyMedium,\n        ),\n      );\n    }\n  }\n\n  Future<void> _changeProxy(WidgetRef ref) async {\n    final isComputedSelected = groupType.isComputedSelected;\n    final isSelector = groupType == GroupType.Selector;\n    if (isComputedSelected || isSelector) {\n      final currentProxyName = ref.read(getProxyNameProvider(groupName));\n      final nextProxyName = switch (isComputedSelected) {\n        true => currentProxyName == proxy.name ? '' : proxy.name,\n        false => proxy.name,\n      };\n      final appController = globalState.appController;\n      appController.updateCurrentSelectedMap(groupName, nextProxyName);\n      appController.changeProxyDebounce(groupName, nextProxyName);\n      return;\n    }\n    globalState.showNotifier(appLocalizations.notSelectedTip);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final proxyNameText = _buildProxyNameText(context);\n    final delayText = _buildDelayText(context);\n    return RepaintBoundary(\n      child: Stack(\n        children: [\n          Consumer(\n            builder: (_, ref, child) {\n              final selectedProxyName = ref.watch(\n                getSelectedProxyNameProvider(groupName),\n              );\n              return CommonCard(\n                onPressed: () {\n                  _changeProxy(ref);\n                },\n                isSelected: selectedProxyName == proxy.name,\n                child: child!,\n              );\n            },\n            child: Container(\n              alignment: Alignment.centerLeft,\n              padding: const EdgeInsets.symmetric(horizontal: 12),\n              child: Column(\n                mainAxisSize: MainAxisSize.min,\n                crossAxisAlignment: CrossAxisAlignment.start,\n                spacing: 8,\n                children: [\n                  proxyNameText,\n                  if (type == ProxyCardType.expand) ...[\n                    SizedBox(\n                      height: measure.labelSmallHeight * 2 + 4,\n                      child: Column(\n                        mainAxisSize: MainAxisSize.min,\n                        crossAxisAlignment: CrossAxisAlignment.start,\n                        spacing: 4,\n                        children: [\n                          SizedBox(\n                            height: measure.labelSmallHeight,\n                            child: _ProxyDesc(proxy: proxy),\n                          ),\n                          SizedBox(\n                            height: measure.labelSmallHeight,\n                            child: Row(\n                              crossAxisAlignment: CrossAxisAlignment.end,\n                              spacing: 4,\n                              children: [\n                                Expanded(\n                                  child: Align(\n                                    alignment: Alignment.centerLeft,\n                                    child: _ProxyMetaTag(proxy.type),\n                                  ),\n                                ),\n                                delayText,\n                              ],\n                            ),\n                          ),\n                        ],\n                      ),\n                    ),\n                  ] else\n                    SizedBox(\n                      height: measure.bodySmallHeight,\n                      child: Row(\n                        mainAxisAlignment: MainAxisAlignment.spaceBetween,\n                        crossAxisAlignment: CrossAxisAlignment.center,\n                        children: [\n                          Flexible(\n                            flex: 1,\n                            child: TooltipText(\n                              text: Text(\n                                proxy.type,\n                                style: context.textTheme.bodySmall?.copyWith(\n                                  overflow: TextOverflow.ellipsis,\n                                  color: context\n                                      .textTheme\n                                      .bodySmall\n                                      ?.color\n                                      ?.opacity80,\n                                ),\n                              ),\n                            ),\n                          ),\n                          delayText,\n                        ],\n                      ),\n                    ),\n                ],\n              ),\n            ),\n          ),\n          if (groupType.isComputedSelected)\n            Positioned(\n              top: 0,\n              right: 0,\n              child: _ProxyComputedMark(groupName: groupName, proxy: proxy),\n            ),\n        ],\n      ),\n    );\n  }\n}\n\nclass _ProxyDesc extends ConsumerWidget {\n  final Proxy proxy;\n\n  const _ProxyDesc({required this.proxy});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final group = ref.watch(\n      groupsProvider.select((groups) => groups.getGroup(proxy.name)),\n    );\n    if (group == null) return const SizedBox.shrink();\n    final selectedName = ref\n        .watch(getProxyCardStateProvider(proxy.name))\n        .proxyName;\n    if (selectedName.isEmpty) return const SizedBox.shrink();\n    return _ProxyMetaTag(selectedName);\n  }\n}\n\nclass _ProxyMetaTag extends StatelessWidget {\n  final String text;\n\n  const _ProxyMetaTag(this.text);\n\n  @override\n  Widget build(BuildContext context) {\n    final colorScheme = context.colorScheme;\n    return EmojiText(\n      text,\n      maxLines: 1,\n      overflow: TextOverflow.ellipsis,\n      style: context.textTheme.labelSmall?.copyWith(\n        height: 1,\n        color: colorScheme.onSurfaceVariant.opacity80,\n        fontWeight: FontWeight.w400,\n      ),\n    );\n  }\n}\n\nclass _ProxyComputedMark extends ConsumerWidget {\n  final String groupName;\n  final Proxy proxy;\n\n  const _ProxyComputedMark({required this.groupName, required this.proxy});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final proxyName = ref.watch(getProxyNameProvider(groupName));\n    if (proxyName != proxy.name) {\n      return const SizedBox();\n    }\n    return Container(\n      alignment: Alignment.topRight,\n      margin: const EdgeInsets.all(8),\n      child: Container(\n        padding: const EdgeInsets.all(4),\n        decoration: BoxDecoration(\n          shape: BoxShape.circle,\n          color: Theme.of(context).colorScheme.secondaryContainer,\n        ),\n        child: const SelectIcon(),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/proxies/common.dart",
    "content": "import 'package:bett_box/clash/clash.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/state.dart';\n\ndouble get listHeaderHeight {\n  final measure = globalState.measure;\n  return 20 + measure.titleMediumHeight + 4 + measure.bodyMediumHeight;\n}\n\ndouble getItemHeight(ProxyCardType proxyCardType) {\n  final measure = globalState.measure;\n  final baseHeight =\n      16 + measure.bodyMediumHeight * 2 + measure.bodySmallHeight + 8 + 4;\n  return switch (proxyCardType) {\n    ProxyCardType.expand => baseHeight - measure.bodySmallHeight + measure.labelSmallHeight * 2 + 4,\n    ProxyCardType.shrink => baseHeight,\n    ProxyCardType.min => baseHeight - measure.bodyMediumHeight,\n  };\n}\n\nFuture<void> proxyDelayTest(Proxy proxy, [String? testUrl]) async {\n  final appController = globalState.appController;\n  final state = appController.getProxyCardState(proxy.name);\n  final url = appController.getRealTestUrl(state.testUrl.getSafeValue(testUrl ?? ''));\n  if (state.proxyName.isEmpty) {\n    return;\n  }\n  // Set testing state\n  appController.setDelay(Delay(url: url, name: state.proxyName, value: 0));\n  // Get and set delay\n  appController.setDelay(await clashCore.getDelay(url, state.proxyName));\n}\n\nbool _isNonTestableProxy(String proxyName) {\n  final name = proxyName.toUpperCase();\n  return name == 'REJECT' || name == 'REJECT-DROP' || name == 'PASS';\n}\n\nFuture<void> delayTest(List<Proxy> proxies, [String? testUrl]) async {\n  final appController = globalState.appController;\n  final proxyNames = proxies\n      .map((proxy) => proxy.name)\n      .where((name) => !_isNonTestableProxy(name))\n      .toSet()\n      .toList();\n  final concurrencyLimit = globalState.config.proxiesStyle.concurrencyLimit;\n\n  // Create lazy task\n  final delayTasks = proxyNames.map((proxyName) {\n    return () async {\n      final state = appController.getProxyCardState(proxyName);\n      final url = appController.getRealTestUrl(\n        state.testUrl.getSafeValue(testUrl ?? ''),\n      );\n      final name = state.proxyName;\n      if (name.isEmpty) {\n        return;\n      }\n      // Set testing state\n      appController.setDelay(Delay(url: url, name: name, value: 0));\n      // Get and set delay\n      appController.setDelay(await clashCore.getDelay(url, name));\n    };\n  }).toList();\n\n  // Execute tasks in batches\n  final batchedTasks = delayTasks.batch(concurrencyLimit);\n  for (final batchTasks in batchedTasks) {\n    await Future.wait(batchTasks.map((task) => task()));\n  }\n  appController.addSortNum();\n}\n\ndouble getScrollToSelectedOffset({\n  required String groupName,\n  required List<Proxy> proxies,\n}) {\n  final appController = globalState.appController;\n  final columns = appController.getProxiesColumns();\n  final proxyCardType = globalState.config.proxiesStyle.cardType;\n  final selectedProxyName = appController.getSelectedProxyName(groupName);\n  final findSelectedIndex = proxies.indexWhere(\n    (proxy) => proxy.name == selectedProxyName,\n  );\n  final selectedIndex = findSelectedIndex != -1 ? findSelectedIndex : 0;\n  final rows = (selectedIndex / columns).floor();\n  return rows * getItemHeight(proxyCardType) + (rows - 1) * 8;\n}\n"
  },
  {
    "path": "lib/views/proxies/list.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:bett_box/providers/state.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nimport 'card.dart';\nimport 'common.dart';\n\nclass ProxiesListView extends ConsumerWidget {\n  const ProxiesListView({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final state = ref.watch(proxiesListStateProvider);\n\n    if (state.groups.isEmpty) {\n      return NullStatus(\n        label: appLocalizations.nullTip(appLocalizations.proxies),\n      );\n    }\n\n    return _ProxyGroupsList(\n      groups: state.groups,\n      columns: state.columns,\n      cardType: state.proxyCardType,\n      sortType: state.proxiesSortType,\n      currentUnfoldSet: state.currentUnfoldSet,\n    );\n  }\n}\n\nclass _ProxyGroupsList extends StatelessWidget {\n  final List<Group> groups;\n  final int columns;\n  final ProxyCardType cardType;\n  final ProxiesSortType sortType;\n  final Set<String> currentUnfoldSet;\n\n  const _ProxyGroupsList({\n    required this.groups,\n    required this.columns,\n    required this.cardType,\n    required this.sortType,\n    required this.currentUnfoldSet,\n  });\n\n  void _handleToggle(String groupName) {\n    final tempUnfoldSet = Set<String>.from(currentUnfoldSet);\n    if (tempUnfoldSet.contains(groupName)) {\n      tempUnfoldSet.remove(groupName);\n    } else {\n      tempUnfoldSet.add(groupName);\n    }\n    globalState.appController.updateCurrentUnfoldSet(tempUnfoldSet);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return CommonScrollBar(\n      controller: null,\n      thumbVisibility: true,\n      trackVisibility: true,\n      child: CustomScrollView(\n        cacheExtent: 500,\n        slivers: [\n          SliverPadding(\n            padding: const EdgeInsets.all(16),\n            sliver: SliverList.builder(\n              itemCount: groups.length,\n              itemBuilder: (context, index) {\n                final group = groups[index];\n                final isExpand = currentUnfoldSet.contains(group.name);\n                return _GroupSection(\n                  key: ValueKey(group.name),\n                  group: group,\n                  columns: columns,\n                  cardType: cardType,\n                  sortType: sortType,\n                  isExpand: isExpand,\n                  onToggle: () => _handleToggle(group.name),\n                );\n              },\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n}\n\nclass _GroupSection extends StatelessWidget {\n  final Group group;\n  final int columns;\n  final ProxyCardType cardType;\n  final ProxiesSortType sortType;\n  final bool isExpand;\n  final VoidCallback onToggle;\n\n  const _GroupSection({\n    super.key,\n    required this.group,\n    required this.columns,\n    required this.cardType,\n    required this.sortType,\n    required this.isExpand,\n    required this.onToggle,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return RepaintBoundary(\n      child: Padding(\n        padding: const EdgeInsets.only(bottom: 8),\n        child: Column(\n        mainAxisSize: MainAxisSize.min,\n        children: [\n          _GroupHeader(\n            group: group,\n            isExpand: isExpand,\n            onToggle: onToggle,\n            cardType: cardType,\n            columns: columns,\n          ),\n          if (isExpand) ...[\n            const SizedBox(height: 8),\n            _ProxyGrid(\n              group: group,\n              columns: columns,\n              cardType: cardType,\n              sortType: sortType,\n            ),\n          ],\n        ],\n        ),\n      ),\n    );\n  }\n}\n\nclass _GroupHeader extends ConsumerWidget {\n  final Group group;\n  final bool isExpand;\n  final VoidCallback onToggle;\n  final ProxyCardType cardType;\n  final int columns;\n\n  const _GroupHeader({\n    required this.group,\n    required this.isExpand,\n    required this.onToggle,\n    required this.cardType,\n    required this.columns,\n  });\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final iconStyle = ref.watch(\n      proxiesStyleSettingProvider.select((s) => s.iconStyle),\n    );\n    final iconMap = ref.watch(\n      proxiesStyleSettingProvider.select((s) => s.iconMap),\n    );\n    final icon = _getIcon(iconStyle, iconMap);\n    final selectedProxyName = ref.watch(\n      getSelectedProxyNameProvider(group.name),\n    ).getSafeValue('');\n\n    return CommonCard(\n      radius: 16,\n      type: CommonCardType.filled,\n      onPressed: onToggle,\n      child: Padding(\n        padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 12),\n        child: Row(\n          children: [\n            _buildIcon(context, iconStyle, icon),\n            Expanded(\n              child: Column(\n                crossAxisAlignment: CrossAxisAlignment.start,\n                mainAxisSize: MainAxisSize.min,\n                children: [\n                  EmojiText(\n                    group.name,\n                    style: context.textTheme.titleMedium,\n                  ),\n                  const SizedBox(height: 4),\n                  Row(\n                    children: [\n                      Text(\n                        group.type.name,\n                        style: context.textTheme.labelMedium?.toLight,\n                      ),\n                      if (selectedProxyName.isNotEmpty) ...[\n                        const SizedBox(width: 8),\n                        Flexible(\n                          child: EmojiText(\n                            '•  $selectedProxyName',\n                            style: context.textTheme.labelMedium?.toLight,\n                            overflow: TextOverflow.ellipsis,\n                          ),\n                        ),\n                      ],\n                    ],\n                  ),\n                ],\n              ),\n            ),\n            if (isExpand) ...[\n              IconButton(\n                visualDensity: VisualDensity.compact,\n                icon: const Icon(Icons.adjust),\n                onPressed: () => _scrollToSelected(context, ref),\n                tooltip: 'Scroll to selected',\n              ),\n              IconButton(\n                visualDensity: VisualDensity.compact,\n                icon: const Icon(Icons.network_ping),\n                onPressed: () => _delayTest(context),\n                tooltip: 'Delay test',\n              ),\n            ],\n            IconButton.filledTonal(\n              visualDensity: VisualDensity.compact,\n              icon: CommonExpandIcon(expand: isExpand),\n              onPressed: onToggle,\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n\n  String _getIcon(ProxiesIconStyle style, Map<String, String> iconMap) {\n    if (style == ProxiesIconStyle.none) return '';\n    for (final entry in iconMap.entries) {\n      try {\n        if (RegExp(entry.key).hasMatch(group.name)) {\n          return entry.value;\n        }\n      } catch (_) {}\n    }\n    return group.icon;\n  }\n\n  Widget _buildIcon(BuildContext context, ProxiesIconStyle style, String icon) {\n    if (style == ProxiesIconStyle.none) return const SizedBox();\n    const iconSize = 40.0;\n    if (style == ProxiesIconStyle.standard) {\n      return Container(\n        margin: const EdgeInsets.only(right: 16),\n        width: iconSize,\n        height: iconSize,\n        alignment: Alignment.center,\n        padding: const EdgeInsets.all(6),\n        decoration: BoxDecoration(\n          borderRadius: BorderRadius.circular(12),\n          color: context.colorScheme.secondaryContainer,\n        ),\n        clipBehavior: Clip.antiAlias,\n        child: CommonTargetIcon(\n          src: icon,\n          size: iconSize - 12,\n        ),\n      );\n    }\n    return Container(\n      margin: const EdgeInsets.only(right: 16),\n      width: iconSize,\n      height: iconSize,\n      alignment: Alignment.center,\n      child: CommonTargetIcon(\n        src: icon,\n        size: iconSize - 8,\n      ),\n    );\n  }\n\n  Future<void> _delayTest(BuildContext context) async {\n    await delayTest(group.all, group.testUrl);\n  }\n\n  void _scrollToSelected(BuildContext context, WidgetRef ref) {\n    final selectedName = ref.read(getSelectedProxyNameProvider(group.name)).getSafeValue('');\n    if (selectedName.isEmpty) return;\n\n    final scrollable = Scrollable.maybeOf(context);\n    if (scrollable == null) return;\n\n    final proxyIndex = group.all.indexWhere((p) => p.name == selectedName);\n    if (proxyIndex < 0) return;\n\n    final itemHeight = getItemHeight(cardType);\n    final offset = (proxyIndex ~/ columns) * (itemHeight + 8) + 100;\n\n    scrollable.position.animateTo(\n      offset.clamp(0, scrollable.position.maxScrollExtent),\n      duration: const Duration(milliseconds: 300),\n      curve: Curves.easeIn,\n    );\n  }\n}\n\nclass _ProxyGrid extends StatelessWidget {\n  final Group group;\n  final int columns;\n  final ProxyCardType cardType;\n  final ProxiesSortType sortType;\n\n  const _ProxyGrid({\n    required this.group,\n    required this.columns,\n    required this.cardType,\n    required this.sortType,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final sortedProxies = globalState.appController.getSortProxies(\n      proxies: group.all,\n      sortType: sortType,\n      testUrl: group.testUrl,\n    );\n\n    final itemHeight = getItemHeight(cardType);\n\n    return SizedBox(\n      height: ((sortedProxies.length / columns).ceil() * (itemHeight + 8)).toDouble(),\n      child: GridView.builder(\n        physics: const NeverScrollableScrollPhysics(),\n        padding: EdgeInsets.zero,\n        gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(\n          crossAxisCount: columns,\n          mainAxisSpacing: 8,\n          crossAxisSpacing: 8,\n          mainAxisExtent: itemHeight,\n        ),\n        itemCount: sortedProxies.length,\n        itemBuilder: (context, index) {\n          final proxy = sortedProxies[index];\n          return ProxyCard(\n            key: ValueKey('${group.name}.${proxy.name}'),\n            proxy: proxy,\n            groupName: group.name,\n            type: cardType,\n            groupType: group.type,\n            testUrl: group.testUrl,\n          );\n        },\n      ),\n    );\n  }\n}"
  },
  {
    "path": "lib/views/proxies/providers.dart",
    "content": "import 'dart:convert';\nimport 'dart:io';\n\nimport 'package:bett_box/clash/clash.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/models/common.dart';\nimport 'package:bett_box/models/core.dart';\nimport 'package:bett_box/models/profile.dart';\nimport 'package:bett_box/providers/app.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\ntypedef UpdatingMap = Map<String, bool>;\n\nclass ProvidersView extends ConsumerStatefulWidget {\n  final SheetType type;\n\n  const ProvidersView({super.key, required this.type});\n\n  @override\n  ConsumerState<ProvidersView> createState() => _ProvidersViewState();\n}\n\nclass _ProvidersViewState extends ConsumerState<ProvidersView> {\n  @override\n  void initState() {\n    super.initState();\n    WidgetsBinding.instance.addPostFrameCallback((_) {\n      globalState.appController.updateProviders();\n    });\n  }\n\n  Future<void> _updateProviders() async {\n    final providers = ref.read(providersProvider);\n    final providersNotifier = ref.read(providersProvider.notifier);\n    final messages = [];\n    final updateProviders = providers.map<Future>((provider) async {\n      providersNotifier.setProvider(provider.copyWith(isUpdating: true));\n      final message = await clashCore.updateExternalProvider(\n        providerName: provider.name,\n      );\n      if (message.isNotEmpty) {\n        messages.add('${provider.name}: $message \\n');\n      }\n      providersNotifier.setProvider(\n        await clashCore.getExternalProvider(provider.name),\n      );\n    });\n    final titleMedium = context.textTheme.titleMedium;\n    await Future.wait(updateProviders);\n    globalState.appController.updateGroupsDebounce();\n    final hasRuleProvider = providers.any((p) => p.type == 'Rule');\n    if (hasRuleProvider) {\n      globalState.appController.applyProfileDebounce(silence: true);\n    }\n    if (messages.isNotEmpty) {\n      globalState.showMessage(\n        title: appLocalizations.tip,\n        message: TextSpan(\n          children: [\n            for (final message in messages)\n              TextSpan(text: message, style: titleMedium),\n          ],\n        ),\n      );\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final providers = ref.watch(providersProvider);\n    final proxyProviders = providers\n        .where((item) => item.type == 'Proxy')\n        .map((item) => ProviderItem(provider: item));\n    final ruleProviders = providers\n        .where((item) => item.type == 'Rule')\n        .map((item) => ProviderItem(provider: item));\n    final proxySection = generateSection(\n      title: appLocalizations.proxyProviders,\n      items: proxyProviders,\n    );\n    final ruleSection = generateSection(\n      title: appLocalizations.ruleProviders,\n      items: ruleProviders,\n    );\n    return AdaptiveSheetScaffold(\n      actions: [\n        IconButton(\n          onPressed: () {\n            _updateProviders();\n          },\n          icon: const Icon(Icons.sync),\n        ),\n      ],\n      type: widget.type,\n      body: generateListView([...proxySection, ...ruleSection]),\n      title: appLocalizations.providers,\n    );\n  }\n}\n\nclass ProviderItem extends StatelessWidget {\n  final ExternalProvider provider;\n\n  const ProviderItem({super.key, required this.provider});\n\n  Future<void> _handleUpdateProvider() async {\n    final appController = globalState.appController;\n    if (provider.vehicleType != 'HTTP') return;\n    await globalState.appController.safeRun(() async {\n      appController.setProvider(provider.copyWith(isUpdating: true));\n      final message = await clashCore.updateExternalProvider(\n        providerName: provider.name,\n      );\n      if (message.isNotEmpty) throw message;\n    }, silence: false);\n    appController.setProvider(\n      await clashCore.getExternalProvider(provider.name),\n    );\n    globalState.appController.updateGroupsDebounce();\n    if (provider.type == 'Rule') {\n      globalState.appController.applyProfileDebounce(silence: true);\n    }\n  }\n\n  Future<void> _handleSideLoadProvider() async {\n    await globalState.appController.safeRun<void>(() async {\n      final platformFile = await picker.pickerFile();\n      final bytes = platformFile?.bytes;\n      if (bytes == null || provider.path == null) return;\n      final file = await File(provider.path!).create(recursive: true);\n      await file.writeAsBytes(bytes);\n      final providerName = provider.name;\n      var message = await clashCore.sideLoadExternalProvider(\n        providerName: providerName,\n        data: utf8.decode(bytes),\n      );\n      if (message.isNotEmpty) throw message;\n      globalState.appController.setProvider(\n        await clashCore.getExternalProvider(provider.name),\n      );\n      if (message.isNotEmpty) throw message;\n    });\n    globalState.appController.updateGroupsDebounce();\n    if (provider.type == 'Rule') {\n      globalState.appController.applyProfileDebounce(silence: true);\n    }\n  }\n\n  String _buildProviderDesc() {\n    final baseInfo = provider.updateAt.lastUpdateTimeDesc;\n    final trafficInfo = _buildTrafficInfoText(provider.subscriptionInfo);\n    final infoText = trafficInfo == null\n        ? baseInfo\n        : '$trafficInfo  ·  $baseInfo';\n    final count = provider.count;\n    return switch (count == 0) {\n      true => infoText,\n      false => '$infoText  ·  $count${appLocalizations.entries}',\n    };\n  }\n\n  String? _buildTrafficInfoText(SubscriptionInfo? subscriptionInfo) {\n    if (subscriptionInfo == null) {\n      return null;\n    }\n    final use = subscriptionInfo.upload + subscriptionInfo.download;\n    final total = subscriptionInfo.total;\n    if (use == 0 && total == 0) {\n      return null;\n    }\n    if (total == 0) {\n      final useShow = TrafficValue(value: use).show;\n      return '$useShow / Unlimited';\n    }\n    final useShow = TrafficValue(value: use).show;\n    final totalShow = TrafficValue(value: total).show;\n    return '$useShow / $totalShow';\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return ListItem(\n      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),\n      title: Text(provider.name),\n      subtitle: Column(\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: [\n          const SizedBox(height: 4),\n          Text(_buildProviderDesc()),\n          const SizedBox(height: 4),\n          if (provider.subscriptionInfo != null)\n            SubscriptionInfoView(subscriptionInfo: provider.subscriptionInfo),\n          const SizedBox(height: 8),\n          Wrap(\n            runSpacing: 6,\n            spacing: 12,\n            runAlignment: WrapAlignment.center,\n            children: [\n              CommonChip(\n                avatar: const Icon(Icons.upload),\n                label: appLocalizations.upload,\n                onPressed: _handleSideLoadProvider,\n              ),\n              if (provider.vehicleType == 'HTTP')\n                provider.isUpdating\n                    ? SizedBox(\n                        height: 30,\n                        width: 30,\n                        child: const Padding(\n                          padding: EdgeInsets.all(2),\n                          child: CircularProgressIndicator(),\n                        ),\n                      )\n                    : CommonChip(\n                        avatar: const Icon(Icons.sync),\n                        label: appLocalizations.sync,\n                        onPressed: _handleUpdateProvider,\n                      ),\n            ],\n          ),\n          const SizedBox(height: 4),\n        ],\n      ),\n    );\n  }\n}"
  },
  {
    "path": "lib/views/proxies/proxies.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/common.dart';\nimport 'package:bett_box/models/widget.dart';\nimport 'package:bett_box/providers/providers.dart';\nimport 'package:bett_box/views/proxies/list.dart';\nimport 'package:bett_box/views/proxies/providers.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nimport 'advanced_settings.dart';\nimport 'setting.dart';\nimport 'tab.dart';\n\nclass ProxiesView extends ConsumerStatefulWidget {\n  const ProxiesView({super.key});\n\n  @override\n  ConsumerState<ProxiesView> createState() => _ProxiesViewState();\n}\n\nclass _ProxiesViewState extends ConsumerState<ProxiesView> {\n  final GlobalKey<ProxiesTabViewState> _proxiesTabKey = GlobalKey();\n  bool _hasProviders = false;\n  bool _isTab = false;\n\n  List<Widget> _buildActions() {\n    return [\n      if (_isTab)\n        IconButton(\n          onPressed: () {\n            _proxiesTabKey.currentState?.scrollToGroupSelected();\n          },\n          icon: Icon(Icons.adjust, weight: 1),\n        ),\n      CommonPopupBox(\n        targetBuilder: (open) {\n          return IconButton(\n            onPressed: () {\n              open(offset: Offset(0, 20));\n            },\n            icon: Icon(Icons.more_vert),\n          );\n        },\n        popup: CommonPopupMenu(\n          items: [\n            PopupMenuItemData(\n              icon: Icons.tune,\n              label: appLocalizations.settings,\n              onPressed: () {\n                showSheet(\n                  context: context,\n                  props: SheetProps(isScrollControlled: true),\n                  builder: (_, type) {\n                    return AdaptiveSheetScaffold(\n                      type: type,\n                      body: const ProxiesSetting(),\n                      title: appLocalizations.settings,\n                    );\n                  },\n                );\n              },\n            ),\n            if (_hasProviders)\n              PopupMenuItemData(\n                icon: Icons.poll_outlined,\n                label: appLocalizations.providers,\n                onPressed: () {\n                  showExtend(\n                    context,\n                    builder: (_, type) {\n                      return ProvidersView(type: type);\n                    },\n                  );\n                },\n              ),\n            PopupMenuItemData(\n              icon: Icons.settings_suggest,\n              label: appLocalizations.advancedSettings,\n              onPressed: () {\n                showExtend(\n                  context,\n                  builder: (_, type) {\n                    return AdaptiveSheetScaffold(\n                      type: type,\n                      body: const ProxiesAdvancedSettings(),\n                      title: appLocalizations.advancedSettings,\n                    );\n                  },\n                );\n              },\n            ),\n            if (!_isTab)\n              PopupMenuItemData(\n                icon: Icons.style_outlined,\n                label: appLocalizations.iconConfiguration,\n                onPressed: () {\n                  showExtend(\n                    context,\n                    builder: (_, type) {\n                      return AdaptiveSheetScaffold(\n                        type: type,\n                        body: const _IconConfigView(),\n                        title: appLocalizations.iconConfiguration,\n                      );\n                    },\n                  );\n                },\n              ),\n          ],\n        ),\n      ),\n    ];\n  }\n\n  Widget? _buildFAB() {\n    return _isTab\n        ? DelayTestButton(\n            onClick: () async {\n              await _proxiesTabKey.currentState?.delayTestCurrentGroup();\n            },\n          )\n        : null;\n  }\n\n  void _onSearch(String value) {\n    ref.read(queryProvider.notifier).value = value;\n  }\n\n  @override\n  void initState() {\n    super.initState();\n    ref.listenManual(providersProvider.select((state) => state.isNotEmpty), (\n      prev,\n      next,\n    ) {\n      if (prev != next) {\n        setState(() {\n          _hasProviders = next;\n        });\n      }\n    }, fireImmediately: true);\n    ref.listenManual(\n      proxiesStyleSettingProvider.select(\n        (state) => state.type == ProxiesType.tab,\n      ),\n      (prev, next) {\n        if (prev != next) {\n          setState(() {\n            _isTab = next;\n          });\n        }\n      },\n      fireImmediately: true,\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final proxiesType = ref.watch(\n      proxiesStyleSettingProvider.select((state) => state.type),\n    );\n    final hasGroups = ref.watch(\n      groupsProvider.select((state) => state.isNotEmpty),\n    );\n    ref.watch(appSettingProvider.select((state) => state.locale));\n    return CommonScaffold(\n      floatingActionButton: _buildFAB(),\n      actions: _buildActions(),\n      title: appLocalizations.proxies,\n      searchState: AppBarSearchState(onSearch: _onSearch),\n      body: switch (hasGroups) {\n        false => NullStatus(label: appLocalizations.noProxy),\n        true => switch (proxiesType) {\n          ProxiesType.tab => ProxiesTabView(key: _proxiesTabKey),\n          ProxiesType.list => const ProxiesListView(),\n        },\n      },\n    );\n  }\n}\n\nclass _IconConfigView extends ConsumerWidget {\n  const _IconConfigView();\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final iconMap = ref.watch(\n      proxiesStyleSettingProvider.select((state) => state.iconMap),\n    );\n    return MapInputPage(\n      title: appLocalizations.iconConfiguration,\n      map: iconMap,\n      keyLabel: appLocalizations.regExp,\n      valueLabel: appLocalizations.icon,\n      titleBuilder: (item) => Text(item.key),\n      leadingBuilder: (item) => Container(\n        decoration: BoxDecoration(borderRadius: BorderRadius.circular(16)),\n        clipBehavior: Clip.antiAlias,\n        child: CommonTargetIcon(src: item.value, size: 42),\n      ),\n      subtitleBuilder: (item) =>\n          Text(item.value, maxLines: 2, overflow: TextOverflow.ellipsis),\n      onChange: (value) {\n        ref\n            .read(proxiesStyleSettingProvider.notifier)\n            .updateState((state) => state.copyWith(iconMap: value));\n      },\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/proxies/setting.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:intl/intl.dart';\n\nclass ProxiesSetting extends StatelessWidget {\n  const ProxiesSetting({super.key});\n\n  IconData _getIconWithProxiesType(ProxiesType type) {\n    return switch (type) {\n      ProxiesType.tab => Icons.view_carousel,\n      ProxiesType.list => Icons.view_list,\n    };\n  }\n\n  IconData _getIconWithProxiesSortType(ProxiesSortType type) {\n    return switch (type) {\n      ProxiesSortType.none => Icons.sort,\n      ProxiesSortType.delay => Icons.network_ping,\n      ProxiesSortType.name => Icons.sort_by_alpha,\n    };\n  }\n\n  String _getStringProxiesSortType(ProxiesSortType type) {\n    return switch (type) {\n      ProxiesSortType.none => appLocalizations.defaultText,\n      ProxiesSortType.delay => appLocalizations.delay,\n      ProxiesSortType.name => appLocalizations.name,\n    };\n  }\n\n  String getTextForProxiesLayout(ProxiesLayout proxiesLayout) {\n    return switch (proxiesLayout) {\n      ProxiesLayout.tight => appLocalizations.tight,\n      ProxiesLayout.standard => appLocalizations.standard,\n      ProxiesLayout.loose => appLocalizations.loose,\n    };\n  }\n\n  String _getTextWithProxiesIconStyle(ProxiesIconStyle style) {\n    return switch (style) {\n      ProxiesIconStyle.standard => appLocalizations.standard,\n      ProxiesIconStyle.none => appLocalizations.noIcon,\n      ProxiesIconStyle.icon => appLocalizations.onlyIcon,\n    };\n  }\n\n  List<Widget> _buildStyleSetting() {\n    return generateSection(\n      title: appLocalizations.style,\n      items: [\n        SingleChildScrollView(\n          padding: const EdgeInsets.symmetric(horizontal: 16),\n          scrollDirection: Axis.horizontal,\n          child: Consumer(\n            builder: (_, ref, _) {\n              final proxiesType = ref.watch(\n                proxiesStyleSettingProvider.select((state) => state.type),\n              );\n              return Wrap(\n                spacing: 16,\n                children: [\n                  for (final item in ProxiesType.values)\n                    SettingInfoCard(\n                      Info(\n                        label: Intl.message(item.name),\n                        iconData: _getIconWithProxiesType(item),\n                      ),\n                      isSelected: proxiesType == item,\n                      onPressed: () {\n                        ref\n                            .read(proxiesStyleSettingProvider.notifier)\n                            .updateState((state) {\n                              return state.copyWith(type: item);\n                            });\n                      },\n                    ),\n                ],\n              );\n            },\n          ),\n        ),\n      ],\n    );\n  }\n\n  List<Widget> _buildSortSetting() {\n    return generateSection(\n      title: appLocalizations.sort,\n      items: [\n        SingleChildScrollView(\n          padding: const EdgeInsets.symmetric(horizontal: 16),\n          scrollDirection: Axis.horizontal,\n          child: Consumer(\n            builder: (_, ref, _) {\n              final sortType = ref.watch(\n                proxiesStyleSettingProvider.select((state) => state.sortType),\n              );\n              return Wrap(\n                spacing: 16,\n                children: [\n                  for (final item in ProxiesSortType.values)\n                    SettingInfoCard(\n                      Info(\n                        label: _getStringProxiesSortType(item),\n                        iconData: _getIconWithProxiesSortType(item),\n                      ),\n                      isSelected: sortType == item,\n                      onPressed: () {\n                        ref\n                            .read(proxiesStyleSettingProvider.notifier)\n                            .updateState((state) {\n                              return state.copyWith(sortType: item);\n                            });\n                      },\n                    ),\n                ],\n              );\n            },\n          ),\n        ),\n      ],\n    );\n  }\n\n  List<Widget> _buildSizeSetting() {\n    return generateSection(\n      title: appLocalizations.size,\n      items: [\n        SingleChildScrollView(\n          padding: const EdgeInsets.symmetric(horizontal: 16),\n          scrollDirection: Axis.horizontal,\n          child: Consumer(\n            builder: (_, ref, _) {\n              final cardType = ref.watch(\n                proxiesStyleSettingProvider.select((state) => state.cardType),\n              );\n              return Wrap(\n                spacing: 16,\n                children: [\n                  for (final item in ProxyCardType.values)\n                    SettingTextCard(\n                      Intl.message(item.name),\n                      isSelected: item == cardType,\n                      onPressed: () {\n                        ref\n                            .read(proxiesStyleSettingProvider.notifier)\n                            .updateState((state) {\n                              return state.copyWith(cardType: item);\n                            });\n                      },\n                    ),\n                ],\n              );\n            },\n          ),\n        ),\n      ],\n    );\n  }\n\n  List<Widget> _buildLayoutSetting() {\n    return generateSection(\n      title: appLocalizations.layout,\n      items: [\n        SingleChildScrollView(\n          padding: const EdgeInsets.symmetric(horizontal: 16),\n          scrollDirection: Axis.horizontal,\n          child: Consumer(\n            builder: (_, ref, _) {\n              final layout = ref.watch(\n                proxiesStyleSettingProvider.select((state) => state.layout),\n              );\n              return Wrap(\n                spacing: 16,\n                children: [\n                  for (final item in ProxiesLayout.values)\n                    SettingTextCard(\n                      getTextForProxiesLayout(item),\n                      isSelected: item == layout,\n                      onPressed: () {\n                        ref\n                            .watch(proxiesStyleSettingProvider.notifier)\n                            .updateState((state) {\n                              return state.copyWith(layout: item);\n                            });\n                      },\n                    ),\n                ],\n              );\n            },\n          ),\n        ),\n      ],\n    );\n  }\n\n  List<Widget> _buildGroupStyleSetting() {\n    return generateSection(\n      title: appLocalizations.iconStyle,\n      items: [\n        SingleChildScrollView(\n          padding: const EdgeInsets.symmetric(horizontal: 16),\n          scrollDirection: Axis.horizontal,\n          child: Consumer(\n            builder: (_, ref, _) {\n              final iconStyle = ref.watch(\n                proxiesStyleSettingProvider.select((state) => state.iconStyle),\n              );\n              return Wrap(\n                spacing: 16,\n                children: [\n                  for (final item in ProxiesIconStyle.values)\n                    SettingTextCard(\n                      _getTextWithProxiesIconStyle(item),\n                      isSelected: iconStyle == item,\n                      onPressed: () {\n                        ref\n                            .read(proxiesStyleSettingProvider.notifier)\n                            .updateState((state) {\n                              return state.copyWith(iconStyle: item);\n                            });\n                      },\n                    ),\n                ],\n              );\n            },\n          ),\n        ),\n      ],\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return SingleChildScrollView(\n      padding: EdgeInsets.only(bottom: 32),\n      child: Column(\n        mainAxisSize: MainAxisSize.min,\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: [\n          ..._buildStyleSetting(),\n          ..._buildSortSetting(),\n          ..._buildLayoutSetting(),\n          ..._buildSizeSetting(),\n          Consumer(\n            builder: (_, ref, child) {\n              final isList = ref.watch(\n                proxiesStyleSettingProvider.select(\n                  (state) => state.type == ProxiesType.list,\n                ),\n              );\n              if (isList) {\n                return child!;\n              }\n              return Container();\n            },\n            child: Column(\n              mainAxisSize: MainAxisSize.min,\n              crossAxisAlignment: CrossAxisAlignment.start,\n              children: [..._buildGroupStyleSetting()],\n            ),\n          ),\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/proxies/tab.dart",
    "content": "import 'dart:math';\n\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/config.dart';\nimport 'package:bett_box/providers/providers.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nimport '../../models/common.dart';\nimport 'card.dart';\nimport 'common.dart';\n\ntypedef ProxyGroupViewKeyMap =\n    Map<String, GlobalObjectKey<_ProxyGroupViewState>>;\n\nclass ProxiesTabView extends ConsumerStatefulWidget {\n  const ProxiesTabView({super.key});\n\n  static Map<String, PageStorageKey> pageListStoreMap = {};\n\n  @override\n  ConsumerState<ProxiesTabView> createState() => ProxiesTabViewState();\n}\n\nclass ProxiesTabViewState extends ConsumerState<ProxiesTabView>\n    with TickerProviderStateMixin {\n  TabController? _tabController;\n  final _hasMoreButtonNotifier = ValueNotifier<bool>(false);\n  ProxyGroupViewKeyMap _keyMap = {};\n\n  @override\n  void initState() {\n    super.initState();\n    ref.listenManual(proxiesTabControllerStateProvider, (prev, next) {\n      if (prev == next) {\n        return;\n      }\n      if (!stringListEquality.equals(prev?.a, next.a)) {\n        _destroyTabController();\n        final index = next.a.indexWhere((item) => item == next.b);\n        if (mounted) {\n          setState(() {\n            _updateTabController(next.a.length, index);\n          });\n        }\n      }\n    }, fireImmediately: true);\n    // Listen for groups change from empty to non-empty\n    ref.listenManual(groupsProvider, (prev, next) {\n      if (prev?.isEmpty == true && next.isNotEmpty) {\n        // Force rebuild\n        if (mounted) {\n          setState(() {});\n        }\n      }\n    });\n  }\n\n  @override\n  void dispose() {\n    _destroyTabController();\n    super.dispose();\n  }\n\n  void scrollToGroupSelected() {\n    final currentGroupName = globalState.appController.getCurrentGroupName();\n    _keyMap[currentGroupName]?.currentState?.scrollToSelected();\n  }\n\n  Future<void> delayTestCurrentGroup() async {\n    final currentGroupName = globalState.appController.getCurrentGroupName();\n    final currentState = _keyMap[currentGroupName]?.currentState;\n    await delayTest(currentState?.proxies ?? [], currentState?.testUrl);\n  }\n\n  Widget _buildMoreButton() {\n    return Consumer(\n      builder: (_, ref, _) {\n        final isMobileView = ref.watch(isMobileViewProvider);\n        return IconButton(\n          onPressed: _showMoreMenu,\n          icon: isMobileView\n              ? const Icon(Icons.expand_more)\n              : const Icon(Icons.chevron_right),\n        );\n      },\n    );\n  }\n\n  void _showMoreMenu() {\n    showSheet(\n      context: context,\n      props: SheetProps(isScrollControlled: false),\n      builder: (_, type) {\n        return AdaptiveSheetScaffold(\n          type: type,\n          body: SingleChildScrollView(\n            padding: const EdgeInsets.all(16),\n            child: Consumer(\n              builder: (_, ref, _) {\n                final state = ref.watch(proxiesTabControllerStateProvider);\n                final groupNames = state.a;\n                final currentGroupName = state.b;\n                return SizedBox(\n                  width: double.infinity,\n                  child: Wrap(\n                    alignment: WrapAlignment.center,\n                    runSpacing: 8,\n                    spacing: 8,\n                    children: [\n                      for (final groupName in groupNames)\n                        SettingTextCard(\n                          groupName,\n                          onPressed: () {\n                            final index = groupNames.indexWhere(\n                              (item) => item == groupName,\n                            );\n                            if (index == -1) return;\n                            _tabController?.animateTo(index);\n                            globalState.appController.updateCurrentGroupName(\n                              groupName,\n                            );\n                            Navigator.of(context).pop();\n                          },\n                          isSelected: groupName == currentGroupName,\n                        ),\n                    ],\n                  ),\n                );\n              },\n            ),\n          ),\n          title: appLocalizations.proxyGroup,\n        );\n      },\n    );\n  }\n\n  void _tabControllerListener([int? index]) {\n    int? groupIndex = index;\n    if (groupIndex == -1) {\n      return;\n    }\n    final appController = globalState.appController;\n    if (groupIndex == null) {\n      final currentIndex = _tabController?.index;\n      groupIndex = currentIndex;\n    }\n    final currentGroups = appController.getCurrentGroups();\n    if (groupIndex == null || groupIndex > currentGroups.length) {\n      return;\n    }\n    final currentGroup = currentGroups[groupIndex];\n    WidgetsBinding.instance.addPostFrameCallback((_) {\n      globalState.appController.updateCurrentGroupName(currentGroup.name);\n    });\n  }\n\n  void _destroyTabController() {\n    _tabController?.removeListener(_tabControllerListener);\n    _tabController?.dispose();\n    _tabController = null;\n  }\n\n  void _updateTabController(int length, int index) {\n    if (length == 0) {\n      _destroyTabController();\n      return;\n    }\n    final realIndex = index == -1 ? 0 : index;\n    _tabController ??= TabController(\n      length: length,\n      initialIndex: realIndex,\n      vsync: this,\n    );\n    _tabControllerListener(realIndex);\n    _tabController?.addListener(_tabControllerListener);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    ref.watch(themeSettingProvider.select((state) => state.textScale));\n    final state = ref.watch(proxiesTabStateProvider);\n    final groups = state.groups;\n    if (groups.isEmpty) {\n      return NullStatus(\n        label: appLocalizations.nullTip(appLocalizations.proxies),\n      );\n    }\n    // Safety check: ensure controller matches groups count\n    if (_tabController != null && _tabController!.length != groups.length) {\n      _destroyTabController();\n      _updateTabController(groups.length, _tabController?.index ?? 0);\n    }\n    // Also handle case where controller is null but we have groups\n    if (_tabController == null && groups.isNotEmpty) {\n      _updateTabController(groups.length, 0);\n    }\n\n    final ProxyGroupViewKeyMap keyMap = {};\n    final children = groups.map((group) {\n      final key = GlobalObjectKey<_ProxyGroupViewState>(group.name);\n      keyMap[group.name] = key;\n      return KeepScope(\n        keep: true,\n        child: ProxyGroupView(\n          key: key,\n          group: group,\n          columns: state.columns,\n          cardType: state.proxyCardType,\n          sortType: state.proxiesSortType,\n        ),\n      );\n    }).toList();\n    _keyMap = keyMap;\n    return Column(\n      mainAxisAlignment: MainAxisAlignment.start,\n      crossAxisAlignment: CrossAxisAlignment.start,\n      children: [\n        NotificationListener<ScrollMetricsNotification>(\n          onNotification: (scrollNotification) {\n            _hasMoreButtonNotifier.value =\n                scrollNotification.metrics.maxScrollExtent > 0;\n            return false;\n          },\n          child: ValueListenableBuilder(\n            valueListenable: _hasMoreButtonNotifier,\n            builder: (_, value, child) {\n              return Stack(\n                alignment: AlignmentDirectional.centerStart,\n                children: [\n                  TabBar(\n                    controller: _tabController,\n                    padding: EdgeInsets.only(\n                      left: 16,\n                      right: 16 + (value ? 16 : 0),\n                    ),\n                    dividerColor: Colors.transparent,\n                    isScrollable: true,\n                    tabAlignment: TabAlignment.start,\n                    overlayColor: globalState.isAndroidTV\n                        ? null\n                        : const WidgetStatePropertyAll(Colors.transparent),\n                    indicator: globalState.isAndroidTV\n                        ? BoxDecoration(\n                            color: context.colorScheme.primaryContainer,\n                            borderRadius: BorderRadius.circular(8),\n                          )\n                        : null,\n                    indicatorColor: globalState.isAndroidTV\n                        ? Colors.transparent\n                        : context.colorScheme.primary,\n                    labelColor: globalState.isAndroidTV\n                        ? context.colorScheme.onPrimaryContainer\n                        : context.colorScheme.primary,\n                    unselectedLabelColor: context.colorScheme.onSurfaceVariant,\n                    tabs: [\n                      for (final group in groups)\n                        Tab(\n                          child: Padding(\n                            padding: globalState.isAndroidTV\n                                ? const EdgeInsets.symmetric(horizontal: 12, vertical: 6)\n                                : EdgeInsets.zero,\n                            child: EmojiText(group.name),\n                          ),\n                        ),\n                    ],\n                  ),\n                  if (value) Positioned(right: 0, child: child!),\n                ],\n              );\n            },\n            child: Container(\n              decoration: BoxDecoration(\n                gradient: LinearGradient(\n                  begin: Alignment.centerLeft,\n                  end: Alignment.centerRight,\n                  colors: [\n                    context.colorScheme.surface.opacity10,\n                    context.colorScheme.surface,\n                  ],\n                  stops: const [0.0, 0.1],\n                ),\n              ),\n              child: _buildMoreButton(),\n            ),\n          ),\n        ),\n        Expanded(\n          child: TabBarView(controller: _tabController, children: children),\n        ),\n      ],\n    );\n  }\n}\n\nclass ProxyGroupView extends ConsumerStatefulWidget {\n  final Group group;\n  final int columns;\n  final ProxyCardType cardType;\n  final ProxiesSortType sortType;\n\n  const ProxyGroupView({\n    super.key,\n    required this.group,\n    required this.columns,\n    required this.cardType,\n    required this.sortType,\n  });\n\n  @override\n  ConsumerState<ProxyGroupView> createState() => _ProxyGroupViewState();\n}\n\nclass _ProxyGroupViewState extends ConsumerState<ProxyGroupView> {\n  late final ScrollController _controller;\n\n  List<Proxy> proxies = [];\n  String? testUrl;\n\n  @override\n  void initState() {\n    super.initState();\n    _controller = ScrollController();\n  }\n\n  PageStorageKey _getPageStorageKey() {\n    final profile = globalState.config.currentProfile;\n    final key =\n        '${profile?.id}_${ScrollPositionCacheKeys.proxiesTabList.name}_${widget.group.name}';\n    return ProxiesTabView.pageListStoreMap.updateCacheValue(\n      key,\n      () => PageStorageKey(key),\n    );\n  }\n\n  @override\n  void dispose() {\n    _controller.dispose();\n    super.dispose();\n  }\n\n  void scrollToSelected() {\n    if (_controller.position.maxScrollExtent == 0) {\n      return;\n    }\n    _controller.animateTo(\n      min(\n        16 +\n            getScrollToSelectedOffset(\n              groupName: widget.group.name,\n              proxies: proxies,\n            ),\n        _controller.position.maxScrollExtent,\n      ),\n      duration: const Duration(milliseconds: 300),\n      curve: Curves.easeIn,\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final group = widget.group;\n    final proxies = group.all;\n    final sortedProxies = globalState.appController.getSortProxies(\n      proxies: proxies,\n      sortType: widget.sortType,\n      testUrl: group.testUrl,\n    );\n    this.proxies = sortedProxies;\n    testUrl = group.testUrl;\n\n    return Align(\n      alignment: Alignment.topCenter,\n      child: CommonScrollBar(\n        controller: _controller,\n        child: GridView.builder(\n          key: _getPageStorageKey(),\n          controller: _controller,\n          cacheExtent: 500,\n          padding: const EdgeInsets.only(\n            top: 16,\n            left: 16,\n            right: 16,\n            bottom: 96,\n          ),\n          gridDelegate: SliverGridDelegateWithFixedCrossAxisCount(\n            crossAxisCount: widget.columns,\n            mainAxisSpacing: 8,\n            crossAxisSpacing: 8,\n            mainAxisExtent: getItemHeight(widget.cardType),\n          ),\n          itemCount: sortedProxies.length,\n          itemBuilder: (_, index) {\n            final proxy = sortedProxies[index];\n            return ProxyCard(\n              testUrl: group.testUrl,\n              groupType: group.type,\n              type: widget.cardType,\n              proxy: proxy,\n              groupName: group.name,\n            );\n          },\n        ),\n      ),\n    );\n  }\n}\n\nclass DelayTestButton extends ConsumerStatefulWidget {\n  final Future Function() onClick;\n\n  const DelayTestButton({super.key, required this.onClick});\n\n  @override\n  ConsumerState<DelayTestButton> createState() => _DelayTestButtonState();\n}\n\nclass _DelayTestButtonState extends ConsumerState<DelayTestButton>\n    with SingleTickerProviderStateMixin {\n  late AnimationController _controller;\n  late Animation<double> _scale;\n\n  Future<void> _healthcheck() async {\n    if (_controller.isAnimating) {\n      return;\n    }\n    _controller.forward();\n    await widget.onClick();\n    if (mounted) {\n      _controller.reverse();\n    }\n  }\n\n  @override\n  void initState() {\n    super.initState();\n    _controller = AnimationController(\n      vsync: this,\n      duration: const Duration(milliseconds: 200),\n    );\n    _scale = Tween<double>(begin: 1.0, end: 0.0).animate(\n      CurvedAnimation(parent: _controller, curve: const Interval(0, 1)),\n    );\n  }\n\n  @override\n  void dispose() {\n    _controller.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    ref.watch(appSettingProvider.select((state) => state.locale));\n    return AnimatedBuilder(\n      animation: _controller.view,\n      builder: (_, child) {\n        return Transform.scale(scale: _scale.value, child: child);\n      },\n      child: FloatingActionButton.extended(\n        heroTag: null,\n        onPressed: _healthcheck,\n        icon: const Icon(Icons.network_ping),\n        label: Text(appLocalizations.startTest),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/resources.dart",
    "content": "import 'dart:io';\n\nimport 'package:bett_box/clash/clash.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:path/path.dart' hide context;\n\n@immutable\nclass GeoItem {\n  final String label;\n  final String key;\n  final String fileName;\n\n  const GeoItem({\n    required this.label,\n    required this.key,\n    required this.fileName,\n  });\n}\n\nclass ResourcesView extends StatefulWidget {\n  const ResourcesView({super.key});\n\n  @override\n  State<ResourcesView> createState() => _ResourcesViewState();\n}\n\nclass _ResourcesViewState extends State<ResourcesView> {\n  final isUpdatingAll = ValueNotifier<bool>(false);\n\n  static const geoItems = <GeoItem>[\n    GeoItem(label: 'GeoSite', fileName: geoSiteFileName, key: 'geosite'),\n    GeoItem(label: 'MMDB', fileName: mmdbFileName, key: 'mmdb'),\n    GeoItem(label: 'ASN', fileName: asnFileName, key: 'asn'),\n  ];\n\n  Future<void> _handleSyncAll() async {\n    if (isUpdatingAll.value) return;\n\n    isUpdatingAll.value = true;\n    try {\n      await Future.wait(\n        geoItems.map(\n          (geoItem) => clashCore.updateGeoData(\n            UpdateGeoDataParams(\n              geoName: geoItem.fileName,\n              geoType: geoItem.label,\n            ),\n          ),\n        ),\n      );\n      if (mounted) {\n        setState(() {});\n      }\n    } catch (e) {\n      if (mounted) {\n        globalState.showMessage(\n          title: appLocalizations.syncFailed,\n          message: TextSpan(text: e.toString()),\n        );\n      }\n    } finally {\n      isUpdatingAll.value = false;\n    }\n  }\n\n  @override\n  void dispose() {\n    isUpdatingAll.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return CommonScaffold(\n      title: appLocalizations.resources,\n      actions: [\n        ValueListenableBuilder(\n          valueListenable: isUpdatingAll,\n          builder: (_, isUpdating, _) {\n            return IconButton(\n              icon: isUpdating\n                  ? const SizedBox(\n                      width: 24,\n                      height: 24,\n                      child: CircularProgressIndicator(strokeWidth: 2),\n                    )\n                  : const Icon(Icons.sync),\n              onPressed: isUpdating ? null : _handleSyncAll,\n              tooltip: appLocalizations.syncAll,\n            );\n          },\n        ),\n      ],\n      body: ListView.separated(\n        itemBuilder: (_, index) {\n          final geoItem = geoItems[index];\n          return GeoDataListItem(geoItem: geoItem);\n        },\n        separatorBuilder: (BuildContext context, int index) {\n          return const Divider(height: 0);\n        },\n        itemCount: geoItems.length,\n      ),\n    );\n  }\n}\n\nclass GeoDataListItem extends StatefulWidget {\n  final GeoItem geoItem;\n\n  const GeoDataListItem({super.key, required this.geoItem});\n\n  @override\n  State<GeoDataListItem> createState() => _GeoDataListItemState();\n}\n\nclass _GeoDataListItemState extends State<GeoDataListItem> {\n  final isUpdating = ValueNotifier<bool>(false);\n\n  GeoItem get geoItem => widget.geoItem;\n\n  Future<void> _updateUrl(String url, WidgetRef ref) async {\n    final defaultMap = defaultGeoXUrl.toJson();\n    final newUrl = await globalState.showCommonDialog<String>(\n      child: UpdateGeoUrlFormDialog(\n        title: geoItem.label,\n        url: url,\n        defaultValue: defaultMap[geoItem.key],\n      ),\n    );\n    if (newUrl != null && newUrl != url && mounted) {\n      try {\n        if (!newUrl.isUrl) {\n          throw 'Invalid url';\n        }\n        ref.read(patchClashConfigProvider.notifier).updateState((state) {\n          final map = state.geoXUrl.toJson();\n          map[geoItem.key] = newUrl;\n          return state.copyWith(geoXUrl: GeoXUrl.fromJson(map));\n        });\n        await globalState.appController.setupClashConfig();\n      } catch (e) {\n        globalState.showMessage(\n          title: geoItem.label,\n          message: TextSpan(text: e.toString()),\n        );\n      }\n    }\n  }\n\n  Future<FileInfo> _getGeoFileLastModified(String fileName) async {\n    final homePath = await appPath.homeDirPath;\n    final file = File(join(homePath, fileName));\n    final lastModified = await file.lastModified();\n    final size = await file.length();\n    return FileInfo(size: size, lastModified: lastModified);\n  }\n\n  Widget _buildSubtitle() {\n    return Consumer(\n      builder: (_, ref, _) {\n        final url = ref.watch(\n          patchClashConfigProvider.select(\n            (state) => state.geoXUrl.toJson()[geoItem.key],\n          ),\n        );\n        if (url == null) {\n          return SizedBox();\n        }\n        return Column(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: [\n            const SizedBox(height: 6),\n            FutureBuilder<FileInfo>(\n              future: _getGeoFileLastModified(geoItem.fileName),\n              builder: (_, snapshot) {\n                final height = globalState.measure.bodyMediumHeight;\n                return SizedBox(\n                  height: height,\n                  child: snapshot.data == null\n                      ? SizedBox(width: height, height: height)\n                      : Text(\n                          snapshot.data!.desc,\n                          style: context.textTheme.bodyMedium,\n                        ),\n                );\n              },\n            ),\n            const SizedBox(height: 4),\n            Text(url, style: context.textTheme.bodyMedium?.toLight),\n            const SizedBox(height: 12),\n            Wrap(\n              runSpacing: 6,\n              spacing: 12,\n              runAlignment: WrapAlignment.center,\n              children: [\n                CommonChip(\n                  avatar: const Icon(Icons.edit),\n                  label: appLocalizations.edit,\n                  onPressed: () {\n                    _updateUrl(url, ref);\n                  },\n                ),\n                Row(\n                  mainAxisSize: MainAxisSize.min,\n                  children: [\n                    SizedBox(\n                      child: ValueListenableBuilder(\n                        valueListenable: isUpdating,\n                        builder: (_, isUpdating, _) {\n                          return isUpdating\n                              ? SizedBox(\n                                  height: 30,\n                                  width: 30,\n                                  child: const Padding(\n                                    padding: EdgeInsets.all(2),\n                                    child: CircularProgressIndicator(),\n                                  ),\n                                )\n                              : CommonChip(\n                                  avatar: const Icon(Icons.sync),\n                                  label: appLocalizations.sync,\n                                  onPressed: () {\n                                    _handleUpdateGeoDataItem();\n                                  },\n                                );\n                        },\n                      ),\n                    ),\n                  ],\n                ),\n              ],\n            ),\n          ],\n        );\n      },\n    );\n  }\n\n  Future<void> _handleUpdateGeoDataItem() async {\n    await globalState.appController.safeRun<void>(\n      () async {\n        await updateGeoDateItem();\n      },\n      silence: false,\n      needLoading: false,\n    );\n    if (mounted) {\n      setState(() {});\n    }\n  }\n\n  Future<void> updateGeoDateItem() async {\n    isUpdating.value = true;\n    try {\n      final message = await clashCore.updateGeoData(\n        UpdateGeoDataParams(geoName: geoItem.fileName, geoType: geoItem.label),\n      );\n      if (message.isNotEmpty) throw message;\n    } catch (e) {\n      isUpdating.value = false;\n      rethrow;\n    }\n    isUpdating.value = false;\n    return;\n  }\n\n  @override\n  void dispose() {\n    super.dispose();\n    isUpdating.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return ListItem(\n      padding: const EdgeInsets.symmetric(horizontal: 16, vertical: 4),\n      title: Text(geoItem.label),\n      subtitle: _buildSubtitle(),\n    );\n  }\n}\n\nclass UpdateGeoUrlFormDialog extends StatefulWidget {\n  final String title;\n  final String url;\n  final String? defaultValue;\n\n  const UpdateGeoUrlFormDialog({\n    super.key,\n    required this.title,\n    required this.url,\n    this.defaultValue,\n  });\n\n  @override\n  State<UpdateGeoUrlFormDialog> createState() => _UpdateGeoUrlFormDialogState();\n}\n\nclass _UpdateGeoUrlFormDialogState extends State<UpdateGeoUrlFormDialog> {\n  late TextEditingController urlController;\n\n  @override\n  void initState() {\n    super.initState();\n    urlController = TextEditingController(text: widget.url);\n  }\n\n  Future<void> _handleReset() async {\n    if (widget.defaultValue == null) {\n      return;\n    }\n    Navigator.of(context).pop<String>(widget.defaultValue);\n  }\n\n  Future<void> _handleUpdate() async {\n    final url = urlController.value.text;\n    if (url.isEmpty) return;\n    Navigator.of(context).pop<String>(url);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return CommonDialog(\n      title: widget.title,\n      actions: [\n        if (widget.defaultValue != null &&\n            urlController.value.text != widget.defaultValue) ...[\n          TextButton(\n            onPressed: _handleReset,\n            child: Text(appLocalizations.reset),\n          ),\n          const SizedBox(width: 4),\n        ],\n        TextButton(\n          onPressed: _handleUpdate,\n          child: Text(appLocalizations.submit),\n        ),\n      ],\n      child: Wrap(\n        runSpacing: 16,\n        children: [\n          TextField(\n            maxLines: 5,\n            minLines: 1,\n            controller: urlController,\n            decoration: const InputDecoration(border: OutlineInputBorder()),\n          ),\n        ],\n      ),\n    );\n  }\n}"
  },
  {
    "path": "lib/views/theme.dart",
    "content": "// ignore_for_file: deprecated_member_use\n\nimport 'dart:math';\nimport 'dart:ui' as ui;\n\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/selector.dart';\nimport 'package:bett_box/plugins/app.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:bett_box/providers/state.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:intl/intl.dart';\n\nclass ThemeModeItem {\n  final ThemeMode themeMode;\n  final IconData iconData;\n  final String label;\n\n  const ThemeModeItem({\n    required this.themeMode,\n    required this.iconData,\n    required this.label,\n  });\n}\n\nclass FontFamilyItem {\n  final FontFamily fontFamily;\n  final String label;\n\n  const FontFamilyItem({required this.fontFamily, required this.label});\n}\n\nclass ThemeView extends ConsumerWidget {\n  const ThemeView({super.key});\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final brightness = ref.watch(currentBrightnessProvider);\n    final locale = ref.watch(\n      appSettingProvider.select((state) => state.locale),\n    );\n    final shouldShowHarmonyFont = locale?.startsWith('zh') == true || \n        locale?.startsWith('en') == true;\n    \n    final items = [\n      _ThemeModeItem(),\n      _PrimaryColorItem(),\n      if (brightness == Brightness.dark) _PrueBlackItem(),\n      if (shouldShowHarmonyFont) _HarmonyFontItem(),\n      _LightIconItem(),\n      _TextScaleFactorItem(),\n      const SizedBox(height: 64),\n    ];\n    return ListView.separated(\n      itemCount: items.length,\n      itemBuilder: (_, index) {\n        return items[index];\n      },\n      separatorBuilder: (_, _) {\n        return SizedBox(height: 24);\n      },\n    );\n  }\n}\n\nclass ItemCard extends StatelessWidget {\n  final Widget child;\n  final Info info;\n  final List<Widget> actions;\n\n  const ItemCard({\n    super.key,\n    required this.info,\n    required this.child,\n    this.actions = const [],\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return Wrap(\n      runSpacing: 16,\n      children: [\n        InfoHeader(info: info, actions: actions),\n        child,\n      ],\n    );\n  }\n}\n\nclass _ThemeModeItem extends ConsumerWidget {\n  const _ThemeModeItem();\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final themeMode = ref.watch(\n      themeSettingProvider.select((state) => state.themeMode),\n    );\n    List<ThemeModeItem> themeModeItems = [\n      ThemeModeItem(\n        iconData: Icons.auto_mode,\n        label: appLocalizations.auto,\n        themeMode: ThemeMode.system,\n      ),\n      ThemeModeItem(\n        iconData: Icons.light_mode,\n        label: appLocalizations.light,\n        themeMode: ThemeMode.light,\n      ),\n      ThemeModeItem(\n        iconData: Icons.dark_mode,\n        label: appLocalizations.dark,\n        themeMode: ThemeMode.dark,\n      ),\n    ];\n    return ItemCard(\n      info: Info(\n        label: appLocalizations.themeMode,\n        iconData: Icons.brightness_high,\n      ),\n      child: Container(\n        padding: const EdgeInsets.symmetric(horizontal: 16),\n        height: 56,\n        child: ListView.separated(\n          scrollDirection: Axis.horizontal,\n          itemCount: themeModeItems.length,\n          itemBuilder: (_, index) {\n            final themeModeItem = themeModeItems[index];\n            return CommonCard(\n              isSelected: themeModeItem.themeMode == themeMode,\n              onPressed: () {\n                ref\n                    .read(themeSettingProvider.notifier)\n                    .updateState(\n                      (state) =>\n                          state.copyWith(themeMode: themeModeItem.themeMode),\n                    );\n              },\n              child: Padding(\n                padding: const EdgeInsets.symmetric(horizontal: 16),\n                child: Row(\n                  mainAxisSize: MainAxisSize.min,\n                  mainAxisAlignment: MainAxisAlignment.start,\n                  children: [\n                    Flexible(child: Icon(themeModeItem.iconData)),\n                    const SizedBox(width: 8),\n                    Flexible(child: Text(themeModeItem.label)),\n                  ],\n                ),\n              ),\n            );\n          },\n          separatorBuilder: (_, _) {\n            return const SizedBox(width: 16);\n          },\n        ),\n      ),\n    );\n  }\n}\n\nclass _PrimaryColorItem extends ConsumerStatefulWidget {\n  const _PrimaryColorItem();\n\n  @override\n  ConsumerState<_PrimaryColorItem> createState() => _PrimaryColorItemState();\n}\n\nclass _PrimaryColorItemState extends ConsumerState<_PrimaryColorItem> {\n  int? _removablePrimaryColor;\n\n  int _calcColumns(double maxWidth) {\n    return max((maxWidth / 96).ceil(), 3);\n  }\n\n  Future<void> _handleReset() async {\n    final res = await globalState.showMessage(\n      message: TextSpan(text: appLocalizations.resetTip),\n    );\n    if (res != true) {\n      return;\n    }\n    ref.read(themeSettingProvider.notifier).updateState((state) {\n      return state.copyWith(\n        primaryColors: defaultPrimaryColors,\n        primaryColor: defaultPrimaryColor,\n        schemeVariant: DynamicSchemeVariant.content,\n      );\n    });\n  }\n\n  Future<void> _handleDel() async {\n    if (_removablePrimaryColor == null) {\n      return;\n    }\n    final res = await globalState.showMessage(\n      message: TextSpan(\n        text: appLocalizations.deleteTip(appLocalizations.colorSchemes),\n      ),\n    );\n    if (res != true) {\n      return;\n    }\n    ref.read(themeSettingProvider.notifier).updateState((state) {\n      final newPrimaryColors = List<int>.from(state.primaryColors)\n        ..remove(_removablePrimaryColor);\n      int? newPrimaryColor = state.primaryColor;\n      if (state.primaryColor == _removablePrimaryColor) {\n        if (newPrimaryColors.contains(defaultPrimaryColor)) {\n          newPrimaryColor = defaultPrimaryColor;\n        } else {\n          newPrimaryColor = null;\n        }\n      }\n      return state.copyWith(\n        primaryColors: newPrimaryColors,\n        primaryColor: newPrimaryColor,\n      );\n    });\n    setState(() {\n      _removablePrimaryColor = null;\n    });\n  }\n\n  Future<void> _handleAdd() async {\n    final res = await globalState.showCommonDialog<int>(\n      child: _PaletteDialog(),\n    );\n    if (res == null) {\n      return;\n    }\n    final isExists = ref.read(\n      themeSettingProvider.select((state) => state.primaryColors.contains(res)),\n    );\n    if (isExists && mounted) {\n      context.showNotifier(\n        appLocalizations.existsTip(appLocalizations.colorSchemes),\n      );\n      return;\n    }\n    ref.read(themeSettingProvider.notifier).updateState((state) {\n      return state.copyWith(\n        primaryColors: List.from(state.primaryColors)..add(res),\n      );\n    });\n  }\n\n  Future<void> _handleChangeSchemeVariant() async {\n    final schemeVariant = ref.read(\n      themeSettingProvider.select((state) => state.schemeVariant),\n    );\n    final value = await globalState.showCommonDialog<DynamicSchemeVariant>(\n      child: OptionsDialog<DynamicSchemeVariant>(\n        title: appLocalizations.colorSchemes,\n        options: DynamicSchemeVariant.values,\n        textBuilder: (item) => Intl.message('${item.name}Scheme'),\n        value: schemeVariant,\n      ),\n    );\n    if (value == null) {\n      return;\n    }\n    ref.read(themeSettingProvider.notifier).updateState((state) {\n      return state.copyWith(schemeVariant: value);\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final vm4 = ref.watch(\n      themeSettingProvider.select(\n        (state) => VM4(\n          a: state.primaryColor,\n          b: state.primaryColors,\n          c: state.schemeVariant,\n          d:\n              state.primaryColor == defaultPrimaryColor &&\n              intListEquality.equals(state.primaryColors, defaultPrimaryColors),\n        ),\n      ),\n    );\n    final primaryColor = vm4.a;\n    final primaryColors = [null, ...vm4.b];\n    final schemeVariant = vm4.c;\n    final isEquals = vm4.d;\n\n    return CommonPopScope(\n      onPop: () {\n        if (_removablePrimaryColor != null) {\n          setState(() {\n            _removablePrimaryColor = null;\n          });\n          return false;\n        }\n        return true;\n      },\n      child: ItemCard(\n        info: Info(label: appLocalizations.themeColor, iconData: Icons.palette),\n        actions: genActions([\n          if (_removablePrimaryColor == null)\n            FilledButton(\n              style: ButtonStyle(visualDensity: VisualDensity.compact),\n              onPressed: _handleChangeSchemeVariant,\n              child: Text(Intl.message('${schemeVariant.name}Scheme')),\n            ),\n          if (_removablePrimaryColor != null)\n            FilledButton(\n              style: ButtonStyle(visualDensity: VisualDensity.compact),\n              onPressed: () {\n                setState(() {\n                  _removablePrimaryColor = null;\n                });\n              },\n              child: Text(appLocalizations.cancel),\n            ),\n          if (_removablePrimaryColor == null && !isEquals)\n            IconButton.filledTonal(\n              iconSize: 20,\n              padding: EdgeInsets.all(4),\n              visualDensity: VisualDensity.compact,\n              onPressed: _handleReset,\n              icon: Icon(Icons.replay),\n            ),\n        ], space: 8),\n        child: Container(\n          margin: const EdgeInsets.symmetric(horizontal: 16),\n          child: LayoutBuilder(\n            builder: (_, constraints) {\n              final columns = _calcColumns(constraints.maxWidth);\n              final itemWidth =\n                  (constraints.maxWidth - (columns - 1) * 16) / columns;\n              return Wrap(\n                spacing: 16,\n                runSpacing: 16,\n                children: [\n                  for (final color in primaryColors)\n                    Container(\n                      clipBehavior: Clip.none,\n                      width: itemWidth,\n                      height: itemWidth,\n                      child: Stack(\n                        alignment: Alignment.center,\n                        clipBehavior: Clip.none,\n                        children: [\n                          EffectGestureDetector(\n                            child: ColorSchemeBox(\n                              isSelected: color == primaryColor,\n                              primaryColor: color != null ? Color(color) : null,\n                              onPressed: () {\n                                setState(() {\n                                  _removablePrimaryColor = null;\n                                });\n                                ref\n                                    .read(themeSettingProvider.notifier)\n                                    .updateState(\n                                      (state) =>\n                                          state.copyWith(primaryColor: color),\n                                    );\n                              },\n                            ),\n                            onLongPress: () {\n                              setState(() {\n                                _removablePrimaryColor = color;\n                              });\n                            },\n                          ),\n                          if (_removablePrimaryColor != null &&\n                              _removablePrimaryColor == color)\n                            Container(\n                              color: Colors.white.opacity0,\n                              padding: EdgeInsets.all(8),\n                              child: IconButton.filledTonal(\n                                onPressed: _handleDel,\n                                padding: EdgeInsets.all(12),\n                                iconSize: 30,\n                                icon: Icon(\n                                  color: context.colorScheme.primary,\n                                  Icons.delete,\n                                ),\n                              ),\n                            ),\n                        ],\n                      ),\n                    ),\n                  if (_removablePrimaryColor == null)\n                    Container(\n                      width: itemWidth,\n                      height: itemWidth,\n                      padding: EdgeInsets.all(4),\n                      child: IconButton.filledTonal(\n                        onPressed: _handleAdd,\n                        iconSize: 32,\n                        icon: Icon(\n                          color: context.colorScheme.primary,\n                          Icons.add,\n                        ),\n                      ),\n                    ),\n                ],\n              );\n            },\n          ),\n        ),\n      ),\n    );\n  }\n}\n\nclass _PrueBlackItem extends ConsumerWidget {\n  const _PrueBlackItem();\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final prueBlack = ref.watch(\n      themeSettingProvider.select((state) => state.pureBlack),\n    );\n    return ListItem.switchItem(\n      leading: Icon(Icons.contrast),\n      horizontalTitleGap: 12,\n      title: Text(\n        appLocalizations.pureBlackMode,\n        style: Theme.of(context).textTheme.titleSmall?.copyWith(\n          color: context.colorScheme.onSurfaceVariant,\n        ),\n      ),\n      delegate: SwitchDelegate(\n        value: prueBlack,\n        onChanged: (value) {\n          ref\n              .read(themeSettingProvider.notifier)\n              .updateState((state) => state.copyWith(pureBlack: value));\n        },\n      ),\n    );\n  }\n}\n\nclass _HarmonyFontItem extends ConsumerWidget {\n  const _HarmonyFontItem();\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final useHarmonyFont = ref.watch(\n      themeSettingProvider.select((state) => state.useHarmonyFont),\n    );\n    return ListItem.switchItem(\n      leading: Icon(Icons.font_download_outlined),\n      horizontalTitleGap: 12,\n      title: Text(\n        appLocalizations.harmonyFont,\n        style: Theme.of(context).textTheme.titleSmall?.copyWith(\n          color: context.colorScheme.onSurfaceVariant,\n        ),\n      ),\n      subtitle: Text(\n        appLocalizations.harmonyFontDesc,\n        style: Theme.of(context).textTheme.bodySmall?.copyWith(\n          color: context.colorScheme.onSurfaceVariant.withOpacity(0.7),\n        ),\n      ),\n      delegate: SwitchDelegate(\n        value: useHarmonyFont,\n        onChanged: (value) {\n          ref\n              .read(themeSettingProvider.notifier)\n              .updateState((state) => state.copyWith(useHarmonyFont: value));\n        },\n      ),\n    );\n  }\n}\n\nclass _LightIconItem extends ConsumerWidget {\n  const _LightIconItem();\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final useLightIcon = ref.watch(\n      themeSettingProvider.select((state) => state.useLightIcon),\n    );\n    return ListItem.switchItem(\n      leading: Icon(Icons.light_mode_outlined),\n      horizontalTitleGap: 12,\n      title: Text(\n        appLocalizations.lightIcon,\n        style: Theme.of(context).textTheme.titleSmall?.copyWith(\n          color: context.colorScheme.onSurfaceVariant,\n        ),\n      ),\n      subtitle: Text(\n        appLocalizations.lightIconDesc,\n        style: Theme.of(context).textTheme.bodySmall?.copyWith(\n          color: context.colorScheme.onSurfaceVariant.withOpacity(0.7),\n        ),\n      ),\n      delegate: SwitchDelegate(\n        value: useLightIcon,\n        onChanged: (value) async {\n          // Call native method to switch icon\n          await app.setLauncherIcon(value);\n          ref\n              .read(themeSettingProvider.notifier)\n              .updateState((state) => state.copyWith(useLightIcon: value));\n        },\n      ),\n    );\n  }\n}\n\nclass _TextScaleFactorItem extends ConsumerWidget {\n  const _TextScaleFactorItem();\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final textScale = ref.watch(\n      themeSettingProvider.select((state) => state.textScale),\n    );\n    final String process = '${(textScale.scale * 100).round()}%';\n    return Column(\n      crossAxisAlignment: CrossAxisAlignment.start,\n      children: [\n        Padding(\n          padding: EdgeInsets.only(bottom: 8),\n          child: ListItem.switchItem(\n            leading: Icon(Icons.text_fields),\n            horizontalTitleGap: 12,\n            title: Text(\n              appLocalizations.textScale,\n              style: Theme.of(context).textTheme.titleSmall?.copyWith(\n                color: context.colorScheme.onSurfaceVariant,\n              ),\n            ),\n            delegate: SwitchDelegate(\n              value: textScale.enable,\n              onChanged: (value) {\n                ref\n                    .read(themeSettingProvider.notifier)\n                    .updateState(\n                      (state) => state.copyWith.textScale(enable: value),\n                    );\n              },\n            ),\n          ),\n        ),\n        Padding(\n          padding: EdgeInsets.symmetric(horizontal: 16),\n          child: Row(\n            mainAxisAlignment: MainAxisAlignment.spaceBetween,\n            mainAxisSize: MainAxisSize.max,\n            spacing: 32,\n            children: [\n              Expanded(\n                child: DisabledMask(\n                  status: !textScale.enable,\n                  child: ActivateBox(\n                    active: textScale.enable,\n                    child: SliderTheme(\n                      data: _SliderDefaultsM3(context),\n                      child: Slider(\n                        padding: EdgeInsets.zero,\n                        min: minTextScale,\n                        max: maxTextScale,\n                        value: textScale.scale,\n                        onChanged: (value) {\n                          ref\n                              .read(themeSettingProvider.notifier)\n                              .updateState(\n                                (state) =>\n                                    state.copyWith.textScale(scale: value),\n                              );\n                        },\n                      ),\n                    ),\n                  ),\n                ),\n              ),\n              Padding(\n                padding: EdgeInsets.only(right: 4),\n                child: Text(process, style: context.textTheme.titleMedium),\n              ),\n            ],\n          ),\n        ),\n      ],\n    );\n  }\n}\n\nclass _PaletteDialog extends StatefulWidget {\n  const _PaletteDialog();\n\n  @override\n  State<_PaletteDialog> createState() => _PaletteDialogState();\n}\n\nclass _PaletteDialogState extends State<_PaletteDialog> {\n  final _controller = ValueNotifier<ui.Color>(Colors.transparent);\n\n  @override\n  Widget build(BuildContext context) {\n    return CommonDialog(\n      title: appLocalizations.palette,\n      actions: [\n        TextButton(\n          onPressed: () {\n            Navigator.of(context).pop();\n          },\n          child: Text(appLocalizations.cancel),\n        ),\n        TextButton(\n          onPressed: () {\n            Navigator.of(context).pop(_controller.value.toARGB32());\n          },\n          child: Text(appLocalizations.confirm),\n        ),\n      ],\n      child: Column(\n        children: [\n          SizedBox(height: 8),\n          SizedBox(\n            width: 250,\n            height: 250,\n            child: Palette(controller: _controller),\n          ),\n          SizedBox(height: 24),\n          ValueListenableBuilder(\n            valueListenable: _controller,\n            builder: (_, color, _) {\n              return PrimaryColorBox(\n                primaryColor: color,\n                child: FilledButton(\n                  onPressed: () {},\n                  child: Text(_controller.value.hex),\n                ),\n              );\n            },\n          ),\n        ],\n      ),\n    );\n  }\n}\n\nclass _SliderDefaultsM3 extends SliderThemeData {\n  _SliderDefaultsM3(this.context) : super(trackHeight: 16.0);\n\n  final BuildContext context;\n  late final ColorScheme _colors = Theme.of(context).colorScheme;\n\n  @override\n  Color? get activeTrackColor => _colors.primary;\n\n  @override\n  Color? get inactiveTrackColor => _colors.secondaryContainer;\n\n  @override\n  Color? get secondaryActiveTrackColor => _colors.primary.withOpacity(0.54);\n\n  @override\n  Color? get disabledActiveTrackColor => _colors.onSurface.withOpacity(0.38);\n\n  @override\n  Color? get disabledInactiveTrackColor => _colors.onSurface.withOpacity(0.12);\n\n  @override\n  Color? get disabledSecondaryActiveTrackColor =>\n      _colors.onSurface.withOpacity(0.38);\n\n  @override\n  Color? get activeTickMarkColor => _colors.onPrimary.withOpacity(1.0);\n\n  @override\n  Color? get inactiveTickMarkColor =>\n      _colors.onSecondaryContainer.withOpacity(1.0);\n\n  @override\n  Color? get disabledActiveTickMarkColor => _colors.onInverseSurface;\n\n  @override\n  Color? get disabledInactiveTickMarkColor => _colors.onSurface;\n\n  @override\n  Color? get thumbColor => _colors.primary;\n\n  @override\n  Color? get disabledThumbColor => _colors.onSurface.withOpacity(0.38);\n\n  @override\n  Color? get overlayColor =>\n      WidgetStateColor.resolveWith((Set<WidgetState> states) {\n        if (states.contains(WidgetState.dragged)) {\n          return _colors.primary.withOpacity(0.1);\n        }\n        if (states.contains(WidgetState.hovered)) {\n          return _colors.primary.withOpacity(0.08);\n        }\n        if (states.contains(WidgetState.focused)) {\n          return _colors.primary.withOpacity(0.1);\n        }\n\n        return Colors.transparent;\n      });\n\n  @override\n  TextStyle? get valueIndicatorTextStyle => Theme.of(\n    context,\n  ).textTheme.labelLarge!.copyWith(color: _colors.onInverseSurface);\n\n  @override\n  Color? get valueIndicatorColor => _colors.inverseSurface;\n\n  @override\n  SliderComponentShape? get valueIndicatorShape =>\n      const RoundedRectSliderValueIndicatorShape();\n\n  @override\n  SliderComponentShape? get thumbShape => const HandleThumbShape();\n\n  @override\n  SliderTrackShape? get trackShape => const GappedSliderTrackShape();\n\n  @override\n  SliderComponentShape? get overlayShape => const RoundSliderOverlayShape();\n\n  @override\n  SliderTickMarkShape? get tickMarkShape =>\n      const RoundSliderTickMarkShape(tickMarkRadius: 4.0 / 2);\n\n  @override\n  WidgetStateProperty<Size?>? get thumbSize {\n    return WidgetStateProperty.resolveWith((Set<WidgetState> states) {\n      if (states.contains(WidgetState.disabled)) {\n        return const Size(4.0, 44.0);\n      }\n      if (states.contains(WidgetState.hovered)) {\n        return const Size(4.0, 44.0);\n      }\n      if (states.contains(WidgetState.focused)) {\n        return const Size(2.0, 44.0);\n      }\n      if (states.contains(WidgetState.pressed)) {\n        return const Size(2.0, 44.0);\n      }\n      return const Size(4.0, 44.0);\n    });\n  }\n\n  @override\n  double? get trackGap => 6.0;\n}\n"
  },
  {
    "path": "lib/views/tools.dart",
    "content": "import 'dart:io';\n\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/l10n/l10n.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/providers/providers.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/views/about.dart';\nimport 'package:bett_box/views/access.dart';\nimport 'package:bett_box/views/application_setting.dart';\nimport 'package:bett_box/views/config/config.dart';\nimport 'package:bett_box/views/connection/connections.dart';\nimport 'package:bett_box/views/hotkey.dart';\nimport 'package:bett_box/views/other_setting.dart';\nimport 'package:bett_box/widgets/widgets.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\nimport 'package:intl/intl.dart';\nimport 'package:path/path.dart' show dirname, join;\n\nimport 'backup_and_recovery.dart';\nimport 'developer.dart';\nimport 'theme.dart';\n\nclass ToolsView extends ConsumerStatefulWidget {\n  const ToolsView({super.key});\n\n  @override\n  ConsumerState<ToolsView> createState() => _ToolViewState();\n}\n\nclass _ToolViewState extends ConsumerState<ToolsView> {\n  Widget _buildNavigationPage(NavigationItem navigationItem) {\n    if (navigationItem.label == PageLabel.connections) {\n      return const ConnectionsView(respectCurrentPage: false);\n    }\n    return navigationItem.builder(context);\n  }\n\n  Widget _buildNavigationMenuItem(NavigationItem navigationItem) {\n    return ListItem.open(\n      leading: navigationItem.icon,\n      title: Text(Intl.message(navigationItem.label.name)),\n      subtitle: navigationItem.description != null\n          ? Text(Intl.message(navigationItem.description!))\n          : null,\n      delegate: OpenDelegate(\n        title: Intl.message(navigationItem.label.name),\n        widget: _buildNavigationPage(navigationItem),\n        wrap: false,\n      ),\n    );\n  }\n\n  Widget _buildNavigationMenu(List<NavigationItem> navigationItems) {\n    return Column(\n      children: [\n        for (final navigationItem in navigationItems) ...[\n          _buildNavigationMenuItem(navigationItem),\n          navigationItems.last != navigationItem\n              ? const Divider(height: 0)\n              : Container(),\n        ],\n      ],\n    );\n  }\n\n  List<Widget> _getOtherList(bool enableDeveloperMode) {\n    return generateSection(\n      title: appLocalizations.other,\n      items: [\n        _DisclaimerItem(),\n        if (enableDeveloperMode) _DeveloperItem(),\n        _InfoItem(),\n      ],\n    );\n  }\n\n  List<Widget> _getSettingList() {\n    return generateSection(\n      title: appLocalizations.settings,\n      items: [\n        _LocaleItem(),\n        _ThemeItem(),\n        _BackupItem(),\n        if (system.isDesktop) _HotkeyItem(),\n        if (system.isWindows) _LoopbackItem(),\n        if (system.isAndroid) _AccessItem(),\n        _ConfigItem(),\n        _OtherSettingItem(),\n        _SettingItem(),\n      ],\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final vm2 = ref.watch(\n      appSettingProvider.select(\n        (state) => VM2(a: state.locale, b: state.developerMode),\n      ),\n    );\n    final items = [\n      Consumer(\n        builder: (_, ref, _) {\n          final state = ref.watch(moreToolsSelectorStateProvider);\n          if (state.navigationItems.isEmpty) {\n            return Container();\n          }\n          return Column(\n            children: [\n              ListHeader(title: appLocalizations.more),\n              _buildNavigationMenu(state.navigationItems),\n            ],\n          );\n        },\n      ),\n      ..._getSettingList(),\n      ..._getOtherList(vm2.b),\n    ];\n    return CommonScaffold(\n      title: appLocalizations.tools,\n      body: CommonScrollBar(\n        controller: null,\n        child: ListView.builder(\n          key: toolsStoreKey,\n          itemCount: items.length,\n          itemBuilder: (_, index) => items[index],\n          padding: const EdgeInsets.only(bottom: 20),\n        ),\n      ),\n    );\n  }\n}\n\nclass _LocaleItem extends ConsumerWidget {\n  const _LocaleItem();\n\n  static final List<Locale> _localeOptions = \n    AppLocalizations.delegate.supportedLocales;\n\n  String _getLocaleString(Locale locale) {\n    return Intl.message(locale.toString());\n  }\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final locale = ref.watch(\n      appSettingProvider.select((state) => state.locale),\n    );\n    final currentLocale = utils.getLocaleForString(locale) ?? \n      utils.getSystemLocale();\n    return ListItem<Locale>.options(\n      leading: const Icon(Icons.language_outlined),\n      title: Text(appLocalizations.language),\n      subtitle: Text(Intl.message(currentLocale.toString())),\n      delegate: OptionsDelegate(\n        title: appLocalizations.language,\n        options: _localeOptions,\n        onChanged: (Locale? locale) {\n          if (locale == null) return;\n          ref\n              .read(appSettingProvider.notifier)\n              .updateState(\n                (state) => state.copyWith(locale: locale.toString()),\n              );\n        },\n        textBuilder: (locale) => _getLocaleString(locale),\n        value: currentLocale,\n      ),\n    );\n  }\n}\n\nclass _ThemeItem extends StatelessWidget {\n  const _ThemeItem();\n\n  @override\n  Widget build(BuildContext context) {\n    return ListItem.open(\n      leading: const Icon(Icons.style),\n      title: Text(appLocalizations.theme),\n      subtitle: Text(appLocalizations.themeDesc),\n      delegate: OpenDelegate(\n        title: appLocalizations.theme,\n        widget: const ThemeView(),\n      ),\n    );\n  }\n}\n\nclass _BackupItem extends StatelessWidget {\n  const _BackupItem();\n\n  @override\n  Widget build(BuildContext context) {\n    return ListItem.open(\n      leading: const Icon(Icons.cloud_sync),\n      title: Text(appLocalizations.backupAndRecovery),\n      subtitle: Text(appLocalizations.backupAndRecoveryDesc),\n      delegate: OpenDelegate(\n        title: appLocalizations.backupAndRecovery,\n        widget: const BackupAndRecovery(),\n      ),\n    );\n  }\n}\n\nclass _HotkeyItem extends StatelessWidget {\n  const _HotkeyItem();\n\n  @override\n  Widget build(BuildContext context) {\n    return ListItem.open(\n      leading: const Icon(Icons.keyboard),\n      title: Text(appLocalizations.hotkeyManagement),\n      subtitle: Text(appLocalizations.hotkeyManagementDesc),\n      delegate: OpenDelegate(\n        title: appLocalizations.hotkeyManagement,\n        widget: const HotKeyView(),\n      ),\n    );\n  }\n}\n\nclass _LoopbackItem extends StatelessWidget {\n  const _LoopbackItem();\n\n  @override\n  Widget build(BuildContext context) {\n    return ListItem(\n      leading: const Icon(Icons.lock),\n      title: Text(appLocalizations.loopback),\n      subtitle: Text(appLocalizations.loopbackDesc),\n      onTap: () {\n        windows?.runas(\n          '\"${join(dirname(Platform.resolvedExecutable), \"WindowsLoopbackManager.exe\")}\"',\n          '',\n          showWindow: true,\n        );\n      },\n    );\n  }\n}\n\nclass _AccessItem extends StatelessWidget {\n  const _AccessItem();\n\n  @override\n  Widget build(BuildContext context) {\n    return ListItem.open(\n      leading: const Icon(Icons.view_list),\n      title: Text(appLocalizations.accessControl),\n      subtitle: Text(appLocalizations.accessControlDesc),\n      delegate: OpenDelegate(\n        title: appLocalizations.appAccessControl,\n        widget: const AccessView(),\n      ),\n    );\n  }\n}\n\nclass _ConfigItem extends StatelessWidget {\n  const _ConfigItem();\n\n  @override\n  Widget build(BuildContext context) {\n    return ListItem.open(\n      leading: const Icon(Icons.edit),\n      title: Text(appLocalizations.basicConfig),\n      subtitle: Text(appLocalizations.basicConfigDesc),\n      delegate: OpenDelegate(\n        title: appLocalizations.basicConfig,\n        widget: const ConfigView(),\n      ),\n    );\n  }\n}\n\nclass _OtherSettingItem extends StatelessWidget {\n  const _OtherSettingItem();\n\n  @override\n  Widget build(BuildContext context) {\n    return ListItem.open(\n      leading: const Icon(Icons.settings_suggest_outlined),\n      title: Text(appLocalizations.otherSettings),\n      subtitle: Text(appLocalizations.otherSettingsDesc),\n      delegate: OpenDelegate(\n        title: appLocalizations.otherSettings,\n        widget: const OtherSettingView(),\n      ),\n    );\n  }\n}\n\nclass _SettingItem extends StatelessWidget {\n  const _SettingItem();\n\n  @override\n  Widget build(BuildContext context) {\n    return ListItem.open(\n      leading: const Icon(Icons.settings),\n      title: Text(appLocalizations.application),\n      subtitle: Text(appLocalizations.applicationDesc),\n      delegate: OpenDelegate(\n        title: appLocalizations.application,\n        widget: const ApplicationSettingView(),\n      ),\n    );\n  }\n}\n\nclass _DisclaimerItem extends StatelessWidget {\n  const _DisclaimerItem();\n\n  @override\n  Widget build(BuildContext context) {\n    return ListItem(\n      leading: const Icon(Icons.gavel),\n      title: Text(appLocalizations.disclaimer),\n      onTap: () async {\n        final isDisclaimerAccepted = await globalState.appController\n            .showDisclaimer();\n        if (!isDisclaimerAccepted) {\n          globalState.appController.handleExit();\n        }\n      },\n    );\n  }\n}\n\nclass _InfoItem extends StatelessWidget {\n  const _InfoItem();\n\n  @override\n  Widget build(BuildContext context) {\n    return ListItem.open(\n      leading: const Icon(Icons.info),\n      title: Text(appLocalizations.about),\n      delegate: OpenDelegate(\n        title: appLocalizations.about,\n        widget: const AboutView(),\n      ),\n    );\n  }\n}\n\nclass _DeveloperItem extends StatelessWidget {\n  const _DeveloperItem();\n\n  @override\n  Widget build(BuildContext context) {\n    return ListItem.open(\n      leading: const Icon(Icons.developer_board),\n      title: Text(appLocalizations.developerMode),\n      delegate: OpenDelegate(\n        title: appLocalizations.developerMode,\n        widget: const DeveloperView(),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/views/views.dart",
    "content": "export 'proxies/proxies.dart';\nexport 'dashboard/dashboard.dart';\nexport 'tools.dart';\nexport 'profiles/profiles.dart';\nexport 'profiles/scripts.dart';\nexport 'logs.dart';\nexport 'access.dart';\nexport 'config/config.dart';\nexport 'application_setting.dart';\nexport 'about.dart';\nexport 'backup_and_recovery.dart';\nexport 'resources.dart';\nexport 'connection/requests.dart';\nexport 'connection/connections.dart';\nexport 'developer.dart';\n"
  },
  {
    "path": "lib/widgets/activate_box.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass ActivateBox extends StatelessWidget {\n  final Widget child;\n  final bool active;\n\n  const ActivateBox({super.key, required this.child, this.active = false});\n\n  @override\n  Widget build(BuildContext context) {\n    return IgnorePointer(ignoring: !active, child: child);\n  }\n}\n"
  },
  {
    "path": "lib/widgets/animate_grid.dart",
    "content": "import 'package:flutter/material.dart';\n\ntypedef AnimatedGridBuilder<T> = Widget Function(BuildContext, T item);\n\nclass AnimateGrid<T> extends StatelessWidget {\n  final int columns;\n  final double itemHeight;\n  final double gap;\n  final List<T> items;\n  final Key Function(T item) keyBuilder;\n  final AnimatedGridBuilder<T> builder;\n  final Duration duration;\n  final Curve curve;\n\n  const AnimateGrid({\n    super.key,\n    required this.items,\n    required this.itemHeight,\n    required this.keyBuilder,\n    required this.builder,\n    this.gap = 8,\n    this.duration = const Duration(milliseconds: 300),\n    this.curve = Curves.easeOut,\n    this.columns = 2,\n  });\n\n  int _rows(int columns, int count) => (count / columns).ceil();\n\n  Offset _getOffset(int index, int count, double itemWidth, double itemHeight) {\n    final xIndex = index % columns;\n    final yIndex = (index / columns).floor();\n    return Offset(\n      xIndex * itemWidth + xIndex * gap,\n      yIndex * itemHeight + yIndex * gap,\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return LayoutBuilder(\n      builder: (_, constraints) {\n        assert(constraints.hasBoundedHeight == false);\n        final gapWidth = (columns - 1) * gap;\n        final width = constraints.maxWidth;\n        final itemWidth = (width - gapWidth) / columns;\n        final count = items.length;\n        final rows = _rows(columns, count);\n        final gapHeight = (rows - 1) * gap;\n        final height = rows * itemHeight + gapHeight;\n        return SizedBox(\n          width: width,\n          height: height,\n          child: Stack(\n            children: [\n              for (var i = 0; i <= count - 1; i++)\n                Builder(\n                  key: keyBuilder(items[i]),\n                  builder: (context) {\n                    final item = items[i];\n                    final offset = _getOffset(i, count, itemWidth, itemHeight);\n                    return TweenAnimationBuilder(\n                      tween: Tween<Offset>(end: offset),\n                      duration: duration,\n                      curve: curve,\n                      builder: (_, offset, child) {\n                        return Transform.translate(\n                          offset: offset,\n                          child: child,\n                        );\n                      },\n                      child: SizedBox(\n                        height: itemHeight,\n                        width: itemWidth,\n                        child: builder(context, item),\n                      ),\n                    );\n                  },\n                ),\n            ],\n          ),\n        );\n      },\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/bar_chart.dart",
    "content": "import 'dart:math';\nimport 'dart:ui';\n\nimport 'package:bett_box/common/constant.dart';\nimport 'package:flutter/material.dart';\n\n@immutable\nclass BarChartData {\n  final double value;\n  final String label;\n\n  const BarChartData({required this.value, required this.label});\n}\n\nclass BarChart extends StatefulWidget {\n  final List<BarChartData> data;\n  final Duration duration;\n\n  const BarChart({\n    super.key,\n    required this.data,\n    this.duration = commonDuration,\n  });\n\n  @override\n  State<BarChart> createState() => _BarChartState();\n}\n\nclass _BarChartState extends State<BarChart>\n    with SingleTickerProviderStateMixin {\n  late AnimationController _animationController;\n\n  late List<BarChartData> _oldData;\n\n  @override\n  void initState() {\n    super.initState();\n    _oldData = widget.data;\n    _animationController = AnimationController(\n      vsync: this,\n      duration: widget.duration,\n    )..forward(from: 0);\n  }\n\n  @override\n  void didUpdateWidget(BarChart oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    if (oldWidget.data != widget.data) {\n      _oldData = oldWidget.data;\n      _animationController.forward(from: 0);\n    }\n  }\n\n  @override\n  void dispose() {\n    _animationController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return LayoutBuilder(\n      builder: (_, container) {\n        return AnimatedBuilder(\n          animation: _animationController,\n          builder: (context, child) {\n            return CustomPaint(\n              painter: BarChartPainter(\n                _oldData,\n                widget.data,\n                _animationController.value,\n              ),\n              size: Size(container.maxWidth, container.maxHeight),\n            );\n          },\n        );\n      },\n    );\n  }\n}\n\nclass BarChartPainter extends CustomPainter {\n  final List<BarChartData> oldData;\n  final List<BarChartData> newData;\n  final double progress;\n\n  BarChartPainter(this.oldData, this.newData, this.progress);\n\n  Map<String, Rect> getRectMap(List<BarChartData> dataList, Size size) {\n    final spacing = size.width * 0.05;\n    final maxBarWidth = 30;\n    final barWidth =\n        (size.width - spacing * (dataList.length - 1)) / dataList.length;\n    final maxValue = dataList.fold(\n      0.0,\n      (max, item) => max > item.value ? max : item.value,\n    );\n    final rects = <String, Rect>{};\n    for (int i = 0; i < dataList.length; i++) {\n      final data = dataList[i];\n      double barHeight = (data.value / maxValue) * size.height;\n\n      final adjustLeft = barWidth > maxBarWidth\n          ? (barWidth - maxBarWidth) / 2\n          : 0;\n      double left = i * (barWidth + spacing) + adjustLeft;\n      double top = size.height - barHeight;\n      rects[data.label] = Rect.fromLTWH(\n        left,\n        top,\n        min(barWidth, 30),\n        barHeight,\n      );\n    }\n    return rects;\n  }\n\n  @override\n  void paint(Canvas canvas, Size size) {\n    final oldRectMap = getRectMap(oldData, size);\n    final newRectMap = getRectMap(newData, size);\n\n    final paint = Paint()\n      ..color = Colors.blue\n      ..style = PaintingStyle.fill;\n    final newRectEntries = newRectMap.entries.toList();\n    for (int i = 0; i < newRectEntries.length; i++) {\n      final newRectEntry = newRectEntries[i];\n      final newRect = newRectEntry.value;\n      final oldRect =\n          oldRectMap[newRectEntry.key] ??\n          newRect.translate(newRect.left * (progress - 1), 0);\n\n      final interpolatedRect = Rect.fromLTRB(\n        lerpDouble(oldRect.left, newRect.left, progress)!,\n        lerpDouble(oldRect.top, newRect.top, progress)!,\n        lerpDouble(oldRect.right, newRect.right, progress)!,\n        lerpDouble(oldRect.bottom, newRect.bottom, progress)!,\n      );\n\n      canvas.drawRect(interpolatedRect, paint);\n    }\n  }\n\n  @override\n  bool shouldRepaint(BarChartPainter oldDelegate) {\n    return oldDelegate.progress != progress ||\n        oldDelegate.oldData != oldData ||\n        oldDelegate.newData != newData;\n  }\n}\n"
  },
  {
    "path": "lib/widgets/builder.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass ScrollOverBuilder extends StatefulWidget {\n  final Widget Function(bool isOver) builder;\n\n  const ScrollOverBuilder({super.key, required this.builder});\n\n  @override\n  State<ScrollOverBuilder> createState() => _ScrollOverBuilderState();\n}\n\nclass _ScrollOverBuilderState extends State<ScrollOverBuilder> {\n  final isOverNotifier = ValueNotifier<bool>(false);\n\n  @override\n  void dispose() {\n    isOverNotifier.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return NotificationListener<ScrollMetricsNotification>(\n      onNotification: (scrollNotification) {\n        isOverNotifier.value = scrollNotification.metrics.maxScrollExtent > 0;\n        return true;\n      },\n      child: ValueListenableBuilder<bool>(\n        valueListenable: isOverNotifier,\n        builder: (_, isOver, _) {\n          return widget.builder(isOver);\n        },\n      ),\n    );\n  }\n}\n\n// class ProxiesActionsBuilder extends StatelessWidget {\n//   final Widget? child;\n//   final Widget Function(\n//     ProxiesActionsState state,\n//     Widget? child,\n//   ) builder;\n//\n//   const ProxiesActionsBuilder({\n//     super.key,\n//     required this.child,\n//     required this.builder,\n//   });\n//\n//   @override\n//   Widget build(BuildContext context) {\n//     return Selector<AppState, ProxiesActionsState>(\n//       selector: (_, appState) => ProxiesActionsState(\n//         isCurrent: appState.currentLabel == \"proxies\",\n//         hasProvider: appState.providers.isNotEmpty,\n//       ),\n//       builder: (_, state, child) => builder(state, child),\n//       child: child,\n//     );\n//   }\n// }\n\n// class ActiveBuilder extends StatelessWidget {\n//   final String label;\n//   final StateAndChildWidgetBuilder<bool> builder;\n//   final Widget? child;\n//\n//   const ActiveBuilder({\n//     super.key,\n//     required this.label,\n//     required this.builder,\n//     required this.child,\n//   });\n//\n//   @override\n//   Widget build(BuildContext context) {\n//     return Selector<AppState, bool>(\n//       selector: (_, appState) => appState.currentLabel == label,\n//       builder: (_, state, child) {\n//         return builder(\n//           state,\n//           child,\n//         );\n//       },\n//       child: child,\n//     );\n//   }\n// }\n\ntypedef StateWidgetBuilder<T> = Widget Function(T state);\n\ntypedef StateAndChildWidgetBuilder<T> = Widget Function(T state, Widget? child);\n"
  },
  {
    "path": "lib/widgets/card.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/widgets/fade_box.dart';\nimport 'package:flutter/material.dart';\n\nimport 'text.dart';\n\nclass Info {\n  final String label;\n  final IconData? iconData;\n\n  const Info({required this.label, this.iconData});\n}\n\nclass InfoHeader extends StatelessWidget {\n  final Info info;\n  final List<Widget> actions;\n  final EdgeInsetsGeometry? padding;\n\n  const InfoHeader({\n    super.key,\n    required this.info,\n    this.padding,\n    List<Widget>? actions,\n  }) : actions = actions ?? const [];\n\n  @override\n  Widget build(BuildContext context) {\n    return Padding(\n      padding: padding ?? baseInfoEdgeInsets,\n      child: Row(\n        mainAxisSize: MainAxisSize.min,\n        mainAxisAlignment: MainAxisAlignment.spaceBetween,\n        children: [\n          Flexible(\n            flex: 1,\n            child: Row(\n              mainAxisSize: MainAxisSize.max,\n              children: [\n                if (info.iconData != null) ...[\n                  Icon(\n                    info.iconData,\n                    color: Theme.of(context).colorScheme.onSurfaceVariant,\n                  ),\n                  const SizedBox(width: 8),\n                ],\n                Flexible(\n                  flex: 1,\n                  child: TooltipText(\n                    text: EmojiText(\n                      info.label,\n                      maxLines: 1,\n                      overflow: TextOverflow.ellipsis,\n                      style: Theme.of(context).textTheme.titleSmall?.copyWith(\n                        color: context.colorScheme.onSurfaceVariant,\n                      ),\n                    ),\n                  ),\n                ),\n              ],\n            ),\n          ),\n          const SizedBox(width: 8),\n          Row(\n            mainAxisSize: MainAxisSize.min,\n            mainAxisAlignment: MainAxisAlignment.end,\n            children: [...actions],\n          ),\n        ],\n      ),\n    );\n  }\n}\n\nclass CommonCard extends StatelessWidget {\n  const CommonCard({\n    super.key,\n    bool? isSelected,\n    this.type = CommonCardType.plain,\n    this.onPressed,\n    this.onLongPress,\n    this.selectWidget,\n    this.radius = 12,\n    required this.child,\n    this.padding,\n    this.enterAnimated = false,\n    this.info,\n  }) : isSelected = isSelected ?? false;\n\n  final bool enterAnimated;\n  final bool isSelected;\n  final void Function()? onPressed;\n  final void Function()? onLongPress;\n  final Widget? selectWidget;\n  final Widget child;\n  final EdgeInsets? padding;\n  final Info? info;\n  final CommonCardType type;\n  final double radius;\n\n  // final WidgetStateProperty<Color?>? backgroundColor;\n  // final WidgetStateProperty<BorderSide?>? borderSide;\n\n  BorderSide getBorderSide(BuildContext context, Set<WidgetState> states) {\n    final colorScheme = context.colorScheme;\n    if (type == CommonCardType.filled) {\n      return BorderSide.none;\n    }\n    final hoverColor = isSelected\n        ? colorScheme.primary.opacity80\n        : colorScheme.primary.opacity60;\n    if (states.contains(WidgetState.hovered) ||\n        states.contains(WidgetState.focused) ||\n        states.contains(WidgetState.pressed)) {\n      return BorderSide(color: hoverColor);\n    }\n    return BorderSide(\n      color: isSelected\n          ? colorScheme.primary\n          : colorScheme.surfaceContainerHighest,\n    );\n  }\n\n  Color? getBackgroundColor(BuildContext context, Set<WidgetState> states) {\n    final colorScheme = context.colorScheme;\n    if (type == CommonCardType.filled) {\n      if (isSelected) {\n        return colorScheme.secondaryContainer.opacity80;\n      }\n      return colorScheme.surfaceContainer;\n    }\n    if (isSelected) {\n      return colorScheme.secondaryContainer;\n    }\n    return colorScheme.surfaceContainerLow;\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    var childWidget = child;\n\n    if (info != null) {\n      childWidget = Column(\n        mainAxisSize: MainAxisSize.min,\n        children: [\n          InfoHeader(\n            padding: baseInfoEdgeInsets.copyWith(bottom: 0),\n            info: info!,\n          ),\n          Flexible(flex: 1, child: child),\n        ],\n      );\n    }\n\n    if (selectWidget != null && isSelected) {\n      final List<Widget> children = [];\n      children.add(childWidget);\n      children.add(Positioned.fill(child: selectWidget!));\n      childWidget = Stack(children: children);\n    }\n\n    final card = OutlinedButton(\n      onLongPress: onLongPress,\n      clipBehavior: Clip.antiAlias,\n      style: ButtonStyle(\n        padding: const WidgetStatePropertyAll(EdgeInsets.zero),\n        shape: WidgetStatePropertyAll(\n          RoundedRectangleBorder(borderRadius: BorderRadius.circular(radius)),\n        ),\n        iconColor: WidgetStatePropertyAll(context.colorScheme.primary),\n        iconSize: WidgetStateProperty.all(20),\n        backgroundColor: WidgetStateProperty.resolveWith(\n          (states) => getBackgroundColor(context, states),\n        ),\n        side: WidgetStateProperty.resolveWith(\n          (states) => getBorderSide(context, states),\n        ),\n      ),\n      onPressed: onPressed,\n      child: childWidget,\n    );\n\n    return switch (enterAnimated) {\n      true => FadeScaleEnterBox(child: card),\n      false => card,\n    };\n  }\n}\n\nclass SelectIcon extends StatelessWidget {\n  const SelectIcon({super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return Material(\n      color: Theme.of(context).colorScheme.inversePrimary,\n      shape: const CircleBorder(),\n      child: Container(\n        padding: const EdgeInsets.all(4),\n        child: const Icon(Icons.check, size: 16),\n      ),\n    );\n  }\n}\n\nclass SettingsBlock extends StatelessWidget {\n  final String title;\n  final List<Widget> settings;\n\n  const SettingsBlock({super.key, required this.title, required this.settings});\n\n  @override\n  Widget build(BuildContext context) {\n    return Padding(\n      padding: EdgeInsets.all(8),\n      child: Column(\n        children: [\n          InfoHeader(info: Info(label: title)),\n          Card(\n            color: context.colorScheme.surfaceContainer,\n            child: Column(children: settings),\n          ),\n        ],\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/chip.dart",
    "content": "import 'package:bett_box/enum/enum.dart';\nimport 'package:flutter/material.dart';\n\nclass CommonChip extends StatelessWidget {\n  final String label;\n  final VoidCallback? onPressed;\n  final ChipType type;\n  final Widget? avatar;\n  final TextStyle? labelStyle;\n\n  const CommonChip({\n    super.key,\n    required this.label,\n    this.labelStyle,\n    this.onPressed,\n    this.avatar,\n    this.type = ChipType.action,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    if (type == ChipType.delete) {\n      return Chip(\n        avatar: avatar,\n        labelPadding: const EdgeInsets.symmetric(vertical: 0, horizontal: 4),\n        clipBehavior: Clip.antiAlias,\n        materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,\n        onDeleted: onPressed ?? () {},\n        labelStyle: labelStyle,\n        label: Text(label),\n      );\n    }\n    return ActionChip(\n      materialTapTargetSize: MaterialTapTargetSize.shrinkWrap,\n      avatar: avatar,\n      clipBehavior: Clip.antiAlias,\n      labelPadding: const EdgeInsets.symmetric(vertical: 0, horizontal: 4),\n      onPressed: onPressed ?? () {},\n      labelStyle: labelStyle,\n      label: Text(label),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/color_scheme_box.dart",
    "content": "import 'package:bett_box/providers/providers.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nimport 'card.dart';\nimport 'grid.dart';\n\nclass ColorSchemeBox extends StatelessWidget {\n  final Color? primaryColor;\n  final bool? isSelected;\n  final void Function()? onPressed;\n\n  const ColorSchemeBox({\n    super.key,\n    required this.primaryColor,\n    this.onPressed,\n    this.isSelected,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return AspectRatio(\n      aspectRatio: 1,\n      child: PrimaryColorBox(\n        primaryColor: primaryColor,\n        child: Builder(\n          builder: (context) {\n            final colorScheme = Theme.of(context).colorScheme;\n            return Stack(\n              children: [\n                CommonCard(\n                  isSelected: isSelected,\n                  onPressed: onPressed,\n                  selectWidget: Container(\n                    alignment: Alignment.center,\n                    child: const SelectIcon(),\n                  ),\n                  child: Container(\n                    padding: const EdgeInsets.all(8),\n                    child: ClipRRect(\n                      borderRadius: BorderRadius.circular(36),\n                      child: SizedBox(\n                        width: 72,\n                        height: 72,\n                        child: Grid(\n                          crossAxisCount: 2,\n                          children: [\n                            GridItem(\n                              mainAxisCellCount: 2,\n                              child: Container(color: colorScheme.primary),\n                            ),\n                            GridItem(\n                              mainAxisCellCount: 1,\n                              child: Container(color: colorScheme.secondary),\n                            ),\n                            GridItem(\n                              mainAxisCellCount: 1,\n                              child: Container(color: colorScheme.tertiary),\n                            ),\n                          ],\n                        ),\n                      ),\n                    ),\n                  ),\n                ),\n                if (primaryColor == null)\n                  const Positioned(\n                    bottom: 4,\n                    right: 4,\n                    child: Icon(Icons.colorize, size: 20),\n                  ),\n              ],\n            );\n          },\n        ),\n      ),\n    );\n  }\n}\n\nclass PrimaryColorBox extends ConsumerWidget {\n  final Color? primaryColor;\n  final Widget child;\n  final Brightness? brightness;\n  final bool ignoreConfig;\n\n  const PrimaryColorBox({\n    super.key,\n    required this.primaryColor,\n    required this.child,\n    this.brightness,\n    this.ignoreConfig = true,\n  });\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final themeData = Theme.of(context);\n    final colorScheme = ref.watch(\n      genColorSchemeProvider(\n        brightness ?? themeData.brightness,\n        color: primaryColor,\n        ignoreConfig: ignoreConfig,\n      ),\n    );\n    return Theme(\n      data: themeData.copyWith(colorScheme: colorScheme),\n      child: child,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/container.dart",
    "content": "import 'dart:math' as math;\n\nimport 'package:flutter/foundation.dart';\nimport 'package:flutter/material.dart';\n\nclass CommonSafeArea extends StatelessWidget {\n  const CommonSafeArea({\n    super.key,\n    this.left = true,\n    this.top = true,\n    this.right = true,\n    this.bottom = true,\n    this.minimum = EdgeInsets.zero,\n    this.maintainBottomViewPadding = false,\n    required this.child,\n  });\n\n  final bool left;\n\n  final bool top;\n\n  final bool right;\n\n  final bool bottom;\n\n  final EdgeInsets minimum;\n\n  final bool maintainBottomViewPadding;\n\n  final Widget child;\n\n  @override\n  Widget build(BuildContext context) {\n    assert(debugCheckHasMediaQuery(context));\n    EdgeInsets padding = MediaQuery.paddingOf(context);\n    final height = MediaQuery.of(context).size.height;\n    if (maintainBottomViewPadding) {\n      padding = padding.copyWith(\n        bottom: MediaQuery.viewPaddingOf(context).bottom,\n      );\n    }\n    final double realPaddingTop = padding.top > height * 0.5 ? 0 : padding.top;\n    return Padding(\n      padding: EdgeInsets.only(\n        left: math.max(left ? padding.left : 0.0, minimum.left),\n        top: math.max(top ? realPaddingTop : 0.0, minimum.top),\n        right: math.max(right ? padding.right : 0.0, minimum.right),\n        bottom: math.max(bottom ? padding.bottom : 0.0, minimum.bottom),\n      ),\n      child: MediaQuery.removePadding(\n        context: context,\n        removeLeft: left,\n        removeTop: top,\n        removeRight: right,\n        removeBottom: bottom,\n        child: child,\n      ),\n    );\n  }\n\n  @override\n  void debugFillProperties(DiagnosticPropertiesBuilder properties) {\n    super.debugFillProperties(properties);\n    properties.add(\n      FlagProperty('left', value: left, ifTrue: 'avoid left padding'),\n    );\n    properties.add(\n      FlagProperty('top', value: top, ifTrue: 'avoid top padding'),\n    );\n    properties.add(\n      FlagProperty('right', value: right, ifTrue: 'avoid right padding'),\n    );\n    properties.add(\n      FlagProperty('bottom', value: bottom, ifTrue: 'avoid bottom padding'),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/dialog.dart",
    "content": "import 'dart:math';\n\nimport 'package:bett_box/providers/app.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass CommonDialog extends ConsumerWidget {\n  final String title;\n  final Widget? child;\n  final List<Widget>? actions;\n  final EdgeInsets? padding;\n  final bool overrideScroll;\n  final Color? backgroundColor;\n\n  const CommonDialog({\n    super.key,\n    required this.title,\n    this.actions,\n    this.child,\n    this.padding,\n    this.overrideScroll = false,\n    this.backgroundColor,\n  });\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final size = ref.watch(viewSizeProvider);\n    return AlertDialog(\n      title: Text(title),\n      actions: actions,\n      contentPadding: padding,\n      backgroundColor: backgroundColor,\n      content: Container(\n        constraints: BoxConstraints(\n          maxHeight: min(size.height - 40, 500),\n          maxWidth: 300,\n        ),\n        width: size.width - 40,\n        child: !overrideScroll ? SingleChildScrollView(child: child) : child,\n      ),\n    );\n  }\n}\n\nclass CommonModal extends ConsumerWidget {\n  final Widget? child;\n\n  const CommonModal({super.key, this.child});\n\n  @override\n  Widget build(BuildContext context, ref) {\n    final size = ref.watch(viewSizeProvider);\n    return Center(\n      child: Container(\n        width: size.width * 0.85,\n        height: size.height * 0.85,\n        decoration: BoxDecoration(borderRadius: BorderRadius.circular(12)),\n        clipBehavior: Clip.antiAlias,\n        child: child,\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/disabled_mask.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass DisabledMask extends StatefulWidget {\n  final Widget child;\n  final bool status;\n\n  const DisabledMask({super.key, required this.child, this.status = true});\n\n  @override\n  State<DisabledMask> createState() => _DisabledMaskState();\n}\n\nclass _DisabledMaskState extends State<DisabledMask> {\n  GlobalKey childKey = GlobalKey();\n\n  @override\n  Widget build(BuildContext context) {\n    final child = Container(key: childKey, child: widget.child);\n    if (!widget.status) {\n      return child;\n    }\n    return ColorFiltered(\n      colorFilter: const ColorFilter.matrix(<double>[\n        0.2126,\n        0.7152,\n        0.0722,\n        0,\n        30,\n        0.2126,\n        0.7152,\n        0.0722,\n        0,\n        30,\n        0.2126,\n        0.7152,\n        0.0722,\n        0,\n        30,\n        0,\n        0,\n        0,\n        1,\n        0,\n      ]),\n      child: child,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/donut_chart.dart",
    "content": "import 'dart:math';\n\nimport 'package:bett_box/common/common.dart';\nimport 'package:flutter/material.dart';\n\n@immutable\nclass DonutChartData {\n  final double _value;\n  final Color color;\n\n  const DonutChartData({required double value, required this.color})\n    : _value = value + 1;\n\n  double get value => _value;\n\n  @override\n  String toString() {\n    return 'DonutChartData{_value: $_value}';\n  }\n\n  @override\n  bool operator ==(Object other) =>\n      identical(this, other) ||\n      other is DonutChartData &&\n          runtimeType == other.runtimeType &&\n          _value == other._value &&\n          color == other.color;\n\n  @override\n  int get hashCode => _value.hashCode ^ color.hashCode;\n}\n\nclass DonutChart extends StatefulWidget {\n  final List<DonutChartData> data;\n  final Duration duration;\n\n  const DonutChart({\n    super.key,\n    required this.data,\n    this.duration = commonDuration,\n  });\n\n  @override\n  State<DonutChart> createState() => _DonutChartState();\n}\n\nclass _DonutChartState extends State<DonutChart>\n    with SingleTickerProviderStateMixin {\n  late AnimationController _animationController;\n  late List<DonutChartData> _oldData;\n\n  @override\n  void initState() {\n    super.initState();\n    _oldData = widget.data;\n    _animationController = AnimationController(\n      vsync: this,\n      duration: widget.duration,\n    );\n  }\n\n  @override\n  void didUpdateWidget(DonutChart oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    if (oldWidget.data != widget.data) {\n      _oldData = oldWidget.data;\n      _animationController.forward(from: 0);\n    }\n  }\n\n  @override\n  void dispose() {\n    _animationController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return AnimatedBuilder(\n      animation: _animationController,\n      builder: (context, child) {\n        return CustomPaint(\n          painter: DonutChartPainter(\n            _oldData,\n            widget.data,\n            _animationController.value,\n          ),\n        );\n      },\n    );\n  }\n}\n\nclass DonutChartPainter extends CustomPainter {\n  final List<DonutChartData> oldData;\n  final List<DonutChartData> newData;\n  final double progress;\n\n  DonutChartPainter(this.oldData, this.newData, this.progress);\n\n  double _logTransform(double value) {\n    const base = 10.0;\n    const minValue = 0.1;\n    if (value < minValue) return 0;\n    return log(value) / log(base) + 1;\n  }\n\n  double _expTransform(double value) {\n    const base = 10.0;\n    if (value <= 0) return 0;\n    return pow(base, value - 1).toDouble();\n  }\n\n  List<DonutChartData> get interpolatedData {\n    if (oldData.length != newData.length) return newData;\n    final interpolatedData = List.generate(newData.length, (index) {\n      final oldValue = oldData[index].value;\n      final newValue = newData[index].value;\n      final logOldValue = _logTransform(oldValue);\n      final logNewValue = _logTransform(newValue);\n      final interpolatedLogValue =\n          logOldValue + (logNewValue - logOldValue) * progress;\n\n      final interpolatedValue = _expTransform(interpolatedLogValue);\n\n      return DonutChartData(\n        value: interpolatedValue,\n        color: newData[index].color,\n      );\n    });\n\n    return interpolatedData;\n  }\n\n  @override\n  void paint(Canvas canvas, Size size) {\n    final center = Offset(size.width / 2, size.height / 2);\n    final strokeWidth = 10.0.ap;\n    final radius = min(size.width / 2, size.height / 2) - strokeWidth / 2;\n\n    final gapAngle = 2 * asin(strokeWidth * 1 / (2 * radius)) * 1.2;\n\n    final data = interpolatedData;\n    final total = data.fold<double>(0, (sum, item) => sum + item.value);\n\n    if (total <= 0) return;\n\n    final availableAngle = 2 * pi - (data.length * gapAngle);\n    double startAngle = -pi / 2 + gapAngle / 2;\n\n    for (final item in data) {\n      final sweepAngle = availableAngle * (item.value / total);\n\n      if (sweepAngle <= 0) continue;\n\n      final paint = Paint()\n        ..style = PaintingStyle.stroke\n        ..strokeWidth = strokeWidth\n        ..strokeCap = StrokeCap.round\n        ..color = item.color;\n\n      canvas.drawArc(\n        Rect.fromCircle(center: center, radius: radius),\n        startAngle,\n        sweepAngle,\n        false,\n        paint,\n      );\n\n      startAngle += sweepAngle + gapAngle;\n    }\n  }\n\n  @override\n  bool shouldRepaint(DonutChartPainter oldDelegate) {\n    return oldDelegate.progress != progress ||\n        oldDelegate.oldData != oldData ||\n        oldDelegate.newData != newData;\n  }\n}\n"
  },
  {
    "path": "lib/widgets/effect.dart",
    "content": "import 'dart:math';\nimport 'dart:ui';\n\nimport 'package:flutter/material.dart';\n\nclass EffectGestureDetector extends StatefulWidget {\n  final Widget child;\n  final GestureLongPressCallback? onLongPress;\n  final GestureTapCallback? onTap;\n\n  const EffectGestureDetector({\n    super.key,\n    required this.child,\n    this.onLongPress,\n    this.onTap,\n  });\n\n  @override\n  State<EffectGestureDetector> createState() => _EffectGestureDetectorState();\n}\n\nclass _EffectGestureDetectorState extends State<EffectGestureDetector>\n    with SingleTickerProviderStateMixin {\n  late AnimationController _controller;\n  double _scale = 1;\n\n  @override\n  void initState() {\n    super.initState();\n    _controller = AnimationController(vsync: this);\n  }\n\n  @override\n  void dispose() {\n    _controller.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return AnimatedScale(\n      scale: _scale,\n      duration: kThemeAnimationDuration,\n      curve: Curves.easeOut,\n      child: GestureDetector(\n        onLongPress: widget.onLongPress,\n        onLongPressStart: (_) {\n          setState(() {\n            _scale = 0.95;\n          });\n        },\n        onTap: widget.onTap,\n        onLongPressEnd: (_) {\n          setState(() {\n            _scale = 1;\n          });\n        },\n        child: widget.child,\n      ),\n    );\n  }\n}\n\nclass CommonExpandIcon extends StatefulWidget {\n  final bool expand;\n\n  const CommonExpandIcon({super.key, this.expand = false});\n\n  @override\n  State<CommonExpandIcon> createState() => _CommonExpandIconState();\n}\n\nclass _CommonExpandIconState extends State<CommonExpandIcon>\n    with SingleTickerProviderStateMixin {\n  late AnimationController _animationController;\n  late Animation<double> _iconTurns;\n\n  static final Animatable<double> _iconTurnTween = Tween<double>(\n    begin: 0.0,\n    end: 0.5,\n  ).chain(CurveTween(curve: Curves.fastOutSlowIn));\n\n  @override\n  void initState() {\n    super.initState();\n    _animationController = AnimationController(\n      duration: const Duration(milliseconds: 200),\n      vsync: this,\n    );\n    _iconTurns = _animationController.drive(_iconTurnTween);\n    if (widget.expand) {\n      _animationController.value = pi;\n    }\n  }\n\n  @override\n  void didUpdateWidget(covariant CommonExpandIcon oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    if (oldWidget.expand != widget.expand) {\n      if (widget.expand) {\n        _animationController.forward();\n      } else {\n        _animationController.reverse();\n      }\n    }\n  }\n\n  @override\n  void dispose() {\n    _animationController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return AnimatedBuilder(\n      animation: _animationController.view,\n      builder: (_, child) {\n        return RotationTransition(turns: _iconTurns, child: child!);\n      },\n      child: const Icon(Icons.expand_more),\n    );\n  }\n}\n\nWidget proxyDecorator(Widget child, int index, Animation<double> animation) {\n  return AnimatedBuilder(\n    animation: animation,\n    builder: (_, Widget? child) {\n      final double animValue = Curves.easeInOut.transform(animation.value);\n      final double scale = lerpDouble(1, 1.02, animValue)!;\n      return Transform.scale(scale: scale, child: child);\n    },\n    child: child,\n  );\n}\n"
  },
  {
    "path": "lib/widgets/fade_box.dart",
    "content": "import 'package:animations/animations.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:flutter/material.dart';\n\nclass FadeBox extends StatelessWidget {\n  final Widget child;\n  final Alignment? alignment;\n\n  const FadeBox({super.key, required this.child, this.alignment});\n\n  @override\n  Widget build(BuildContext context) {\n    return PageTransitionSwitcher(\n      transitionBuilder: (child, animation, secondaryAnimation) {\n        return Container(\n          alignment: alignment ?? Alignment.centerLeft,\n          child: FadeTransition(opacity: animation, child: child),\n        );\n      },\n      child: child,\n    );\n  }\n}\n\nclass FadeThroughBox extends StatelessWidget {\n  final Widget child;\n  final Alignment? alignment;\n  final EdgeInsets? margin;\n\n  const FadeThroughBox({\n    super.key,\n    required this.child,\n    this.alignment,\n    this.margin,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return PageTransitionSwitcher(\n      transitionBuilder: (child, animation, secondaryAnimation) {\n        return Container(\n          margin: margin,\n          alignment: alignment ?? Alignment.centerLeft,\n          child: FadeThroughTransition(\n            animation: animation,\n            fillColor: Colors.transparent,\n            secondaryAnimation: secondaryAnimation,\n            child: child,\n          ),\n        );\n      },\n      child: child,\n    );\n  }\n}\n\nclass FadeScaleBox extends StatelessWidget {\n  final Widget child;\n\n  const FadeScaleBox({super.key, required this.child});\n\n  @override\n  Widget build(BuildContext context) {\n    return AnimatedSwitcher(\n      transitionBuilder: (child, animation) {\n        return Container(\n          alignment: Alignment.bottomRight,\n          child: FadeScaleTransition(animation: animation, child: child),\n        );\n      },\n      duration: Duration(milliseconds: 300),\n      child: child,\n    );\n  }\n}\n\nclass FadeScaleEnterBox extends StatefulWidget {\n  final Widget child;\n\n  const FadeScaleEnterBox({super.key, required this.child});\n\n  @override\n  State<FadeScaleEnterBox> createState() => _FadeScaleEnterBoxState();\n}\n\nclass _FadeScaleEnterBoxState extends State<FadeScaleEnterBox>\n    with SingleTickerProviderStateMixin {\n  late AnimationController _controller;\n  late Animation<double> _animation;\n\n  @override\n  void initState() {\n    super.initState();\n    _controller = AnimationController(vsync: this, duration: commonDuration);\n    _animation = Tween<double>(\n      begin: 0,\n      end: 1,\n    ).animate(CurvedAnimation(parent: _controller, curve: Curves.easeInOut));\n    _controller.forward();\n  }\n\n  @override\n  void dispose() {\n    _controller.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return AnimatedBuilder(\n      animation: _controller.view,\n      builder: (_, child) {\n        return FadeScaleEnterTransition(animation: _animation, child: child!);\n      },\n      child: widget.child,\n    );\n  }\n}\n\nclass FadeScaleEnterTransition extends StatelessWidget {\n  const FadeScaleEnterTransition({\n    super.key,\n    required this.animation,\n    this.child,\n  });\n\n  final Animation<double> animation;\n  final Widget? child;\n\n  static final Animatable<double> _fadeInTransition = CurveTween(\n    curve: const Interval(0.0, 0.3),\n  );\n  static final Animatable<double> _scaleInTransition = Tween<double>(\n    begin: 0.70,\n    end: 1.00,\n  ).chain(CurveTween(curve: Easing.legacyDecelerate));\n\n  @override\n  Widget build(BuildContext context) {\n    return FadeTransition(\n      opacity: _fadeInTransition.animate(animation),\n      child: ScaleTransition(\n        scale: _scaleInTransition.animate(animation),\n        child: child,\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/float_layout.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass FloatLayout extends StatelessWidget {\n  final Widget floatingWidget;\n\n  final Widget child;\n\n  const FloatLayout({\n    super.key,\n    required this.floatingWidget,\n    required this.child,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return Stack(\n      fit: StackFit.loose,\n      children: [\n        Center(child: child),\n        Positioned(\n          bottom: 0,\n          right: 0,\n          child: Container(child: floatingWidget),\n        ),\n      ],\n    );\n  }\n}\n\nclass FloatWrapper extends StatelessWidget {\n  final Widget child;\n\n  const FloatWrapper({super.key, required this.child});\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      margin: const EdgeInsets.all(kFloatingActionButtonMargin),\n      child: child,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/google_bottom_nav_bar.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter/services.dart';\nimport 'package:google_nav_bar/google_nav_bar.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/models/common.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:intl/intl.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass GoogleBottomNavBar extends ConsumerWidget {\n  final List<NavigationItem> navigationItems;\n  final int selectedIndex;\n  final ValueChanged<int> onTabChange;\n\n  const GoogleBottomNavBar({\n    super.key,\n    required this.navigationItems,\n    required this.selectedIndex,\n    required this.onTabChange,\n  });\n\n  IconData _extractIconData(Widget iconWidget) {\n    if (iconWidget is Icon) {\n      return iconWidget.icon ?? Icons.home;\n    }\n    return Icons.home;\n  }\n\n  @override\n  Widget build(BuildContext context, WidgetRef ref) {\n    final enableHapticFeedback = ref.watch(\n      appSettingProvider.select((state) => state.enableNavBarHapticFeedback),\n    );\n\n    return Container(\n      decoration: BoxDecoration(\n        color: context.colorScheme.surfaceContainer,\n        boxShadow: [\n          BoxShadow(\n            blurRadius: 20,\n            color: Colors.black.withValues(alpha: 0.15),\n          ),\n        ],\n      ),\n      child: SafeArea(\n        child: Padding(\n          padding: const EdgeInsets.symmetric(horizontal: 15.0, vertical: 10),\n          child: GNav(\n            rippleColor: enableHapticFeedback\n                ? context.colorScheme.onSurface.withValues(alpha: 0.15)\n                : Colors.transparent, // Disabling ripple may disable haptic feedback\n            hoverColor: context.colorScheme.onSurface.withValues(alpha: 0.1),\n            haptic: enableHapticFeedback, // Control GNav haptic feedback\n            gap: 8,\n            activeColor: context.colorScheme.onSecondaryContainer,\n            iconSize: 24,\n            padding: const EdgeInsets.symmetric(horizontal: 20, vertical: 12),\n            duration: const Duration(milliseconds: 250),\n            tabBackgroundColor: context.colorScheme.secondaryContainer,\n            color: context.colorScheme.onSurfaceVariant,\n            curve: Curves.easeInOut,\n            tabs: navigationItems\n                .map(\n                  (e) => GButton(\n                    icon: _extractIconData(e.icon),\n                    text: Intl.message(e.label.name),\n                  ),\n                )\n                .toList(),\n            selectedIndex: selectedIndex,\n            onTabChange: (index) {\n              // Trigger vibration only if haptic feedback enabled\n              if (system.isAndroid && enableHapticFeedback) {\n                HapticFeedback.selectionClick(); // Lighter haptic feedback\n              }\n              onTabChange(index);\n            },\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/grid.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/rendering.dart';\nimport 'dart:math' as math;\n\ntypedef WrapBuilder = Widget Function(Widget child);\n\nclass Grid extends MultiChildRenderObjectWidget {\n  final double mainAxisSpacing;\n\n  final double crossAxisSpacing;\n\n  final double? mainAxisExtent;\n\n  final int crossAxisCount;\n\n  final AxisDirection axisDirection;\n\n  final TextDirection textDirection;\n\n  const Grid({\n    super.key,\n    this.mainAxisSpacing = 0,\n    this.crossAxisSpacing = 0,\n    int? crossAxisCount,\n    AxisDirection? axisDirection,\n    TextDirection? textDirection,\n    this.mainAxisExtent,\n    List<Widget>? children,\n  }) : crossAxisCount = crossAxisCount ?? 1,\n       axisDirection = axisDirection ?? AxisDirection.down,\n       textDirection = textDirection ?? TextDirection.ltr,\n       super(children: children ?? const []);\n\n  const Grid.baseGap({\n    Key? key,\n    double mainAxisSpacing = 8.0,\n    double crossAxisSpacing = 8.0,\n    int? crossAxisCount,\n    AxisDirection? axisDirection,\n    TextDirection? textDirection,\n    double? mainAxisExtent,\n    List<Widget>? children,\n  }) : this(\n         key: key,\n         mainAxisSpacing: mainAxisSpacing,\n         crossAxisSpacing: crossAxisSpacing,\n         crossAxisCount: crossAxisCount,\n         axisDirection: axisDirection,\n         textDirection: textDirection,\n         mainAxisExtent: mainAxisExtent,\n         children: children,\n       );\n\n  @override\n  RenderObject createRenderObject(BuildContext context) {\n    return RenderGrid(\n      textDirection: textDirection,\n      crossAxisCount: crossAxisCount,\n      mainAxisSpacing: mainAxisSpacing,\n      crossAxisSpacing: crossAxisSpacing,\n      axisDirection: axisDirection,\n      mainAxisExtent: mainAxisExtent,\n    );\n  }\n\n  @override\n  void updateRenderObject(BuildContext context, RenderGrid renderObject) {\n    renderObject\n      ..mainAxisSpacing = mainAxisSpacing\n      ..mainAxisExtent = mainAxisExtent\n      ..crossAxisSpacing = crossAxisSpacing\n      ..textDirection = textDirection\n      ..axisDirection = axisDirection\n      ..crossAxisCount = crossAxisCount;\n  }\n}\n\nclass RenderGrid extends RenderBox\n    with\n        ContainerRenderObjectMixin<RenderBox, GridParentData>,\n        RenderBoxContainerDefaultsMixin<RenderBox, GridParentData> {\n  RenderGrid({\n    required double mainAxisSpacing,\n    required double crossAxisSpacing,\n    required int crossAxisCount,\n    required AxisDirection axisDirection,\n    required TextDirection textDirection,\n    double? mainAxisExtent,\n  }) : _crossAxisCount = crossAxisCount,\n       _crossAxisSpacing = crossAxisSpacing,\n       _mainAxisSpacing = mainAxisSpacing,\n       _axisDirection = axisDirection,\n       _textDirection = textDirection,\n       _mainAxisExtent = mainAxisExtent;\n\n  int _crossAxisCount;\n\n  int get crossAxisCount => _crossAxisCount;\n\n  set crossAxisCount(int value) {\n    if (_crossAxisCount != value) {\n      _crossAxisCount = value;\n      markNeedsLayout();\n    }\n  }\n\n  double? _mainAxisExtent;\n\n  double? get mainAxisExtent => _mainAxisExtent;\n\n  set mainAxisExtent(double? value) {\n    if (_mainAxisExtent != value) {\n      _mainAxisExtent = value;\n      markNeedsLayout();\n    }\n  }\n\n  double _mainAxisSpacing;\n\n  double get mainAxisSpacing => _mainAxisSpacing;\n\n  set mainAxisSpacing(double value) {\n    if (_mainAxisSpacing != value) {\n      _mainAxisSpacing = value;\n      markNeedsLayout();\n    }\n  }\n\n  double _crossAxisSpacing;\n\n  double get crossAxisSpacing => _crossAxisSpacing;\n\n  set crossAxisSpacing(double value) {\n    if (_crossAxisSpacing != value) {\n      _crossAxisSpacing = value;\n      markNeedsLayout();\n    }\n  }\n\n  AxisDirection _axisDirection;\n\n  AxisDirection get axisDirection => _axisDirection;\n\n  set axisDirection(AxisDirection value) {\n    if (_axisDirection != value) {\n      _axisDirection = value;\n      markNeedsLayout();\n    }\n  }\n\n  TextDirection _textDirection;\n\n  TextDirection get textDirection => _textDirection;\n\n  set textDirection(TextDirection value) {\n    if (_textDirection != value) {\n      _textDirection = value;\n      markNeedsLayout();\n    }\n  }\n\n  Axis get mainAxis => axisDirectionToAxis(_axisDirection);\n\n  @override\n  void setupParentData(RenderObject child) {\n    if (child.parentData is! GridParentData) {\n      child.parentData = GridParentData();\n    }\n  }\n\n  @override\n  void performLayout() {\n    final requestedSize = _computeSize(constraints: constraints);\n    size = constraints.constrain(requestedSize);\n    _hasOverflow = size != requestedSize;\n  }\n\n  @override\n  bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {\n    return defaultHitTestChildren(result, position: position);\n  }\n\n  @override\n  void paint(PaintingContext context, Offset offset) {\n    if (_hasOverflow) {\n      context.pushClipRect(\n        needsCompositing,\n        offset,\n        Offset.zero & size,\n        defaultPaint,\n      );\n    } else {\n      defaultPaint(context, offset);\n    }\n  }\n\n  GridParentData _getParentData(RenderBox child) {\n    return child.parentData as GridParentData;\n  }\n\n  void _layoutChild(\n    RenderBox child,\n    BoxConstraints constraints, {\n    bool parentUsesSize = false,\n  }) {\n    child.layout(constraints, parentUsesSize: parentUsesSize);\n  }\n\n  int _computeCrossAxisCellCount(\n    GridParentData childParentData,\n    int crossAxisCount,\n  ) {\n    return math.min(childParentData.crossAxisCellCount ?? 1, crossAxisCount);\n  }\n\n  Size _computeSize({required BoxConstraints constraints}) {\n    final crossAxisExtent = mainAxis == Axis.vertical\n        ? constraints.maxWidth\n        : constraints.maxHeight;\n    final stride = (crossAxisExtent + crossAxisSpacing) / crossAxisCount;\n    final offsets = List.filled(crossAxisCount, 0.0);\n    RenderBox? child = firstChild;\n    while (child != null) {\n      final childParentData = _getParentData(child);\n      final crossAxisCellCount = _computeCrossAxisCellCount(\n        childParentData,\n        crossAxisCount,\n      );\n      final crossAxisExtent = stride * crossAxisCellCount - crossAxisSpacing;\n      final shouldFitContent = childParentData.mainAxisCellCount == null;\n\n      double mainAxisExtent = 0;\n\n      if (shouldFitContent) {\n        final childConstraints = mainAxis == Axis.vertical\n            ? BoxConstraints.tightFor(width: crossAxisExtent)\n            : BoxConstraints.tightFor(height: crossAxisExtent);\n        _layoutChild(child, childConstraints, parentUsesSize: true);\n        mainAxisExtent = mainAxis == Axis.vertical\n            ? child.size.height\n            : child.size.width;\n      } else {\n        final mainAxisCellCount = childParentData.mainAxisCellCount ?? 1;\n        mainAxisExtent =\n            (this.mainAxisExtent ?? stride) * mainAxisCellCount -\n            mainAxisSpacing;\n        childParentData.realMainAxisExtent = mainAxisExtent;\n        final childSize = mainAxis == Axis.vertical\n            ? Size(crossAxisExtent, mainAxisExtent)\n            : Size(mainAxisExtent, crossAxisExtent);\n        final childConstraints = BoxConstraints.tight(childSize);\n        _layoutChild(child, childConstraints);\n      }\n      final origin = _getOrigin(offsets, crossAxisCellCount);\n      final mainAxisOffset = origin.mainAxisOffset;\n      final crossAxisOffset = origin.crossAxisIndex * stride;\n      final offset = mainAxis == Axis.vertical\n          ? Offset(crossAxisOffset, mainAxisOffset)\n          : Offset(mainAxisOffset, crossAxisOffset);\n      childParentData.offset = offset;\n\n      final nextOffset = mainAxisOffset + mainAxisExtent + mainAxisSpacing;\n\n      for (int i = 0; i < crossAxisCellCount; i++) {\n        offsets[origin.crossAxisIndex + i] = nextOffset;\n      }\n      child = childAfter(child);\n    }\n    final mainAxisExtent = offsets.reduce(math.max) - mainAxisSpacing;\n\n    if (axisDirectionIsReversed(axisDirection)) {\n      child = firstChild;\n      while (child != null) {\n        final childParentData = _getParentData(child);\n        final offset = childParentData.offset;\n        final crossAxisOffset = offset.getCrossAxisOffset(mainAxis);\n        final mainAxisOffset =\n            mainAxisExtent -\n            offset.getMainAxisOffset(mainAxis) -\n            childParentData.realMainAxisExtent!;\n        final newOffset = mainAxis == Axis.vertical\n            ? Offset(crossAxisOffset, mainAxisOffset)\n            : Offset(mainAxisOffset, crossAxisOffset);\n        childParentData.offset = newOffset;\n        child = childAfter(child);\n      }\n    }\n\n    if (mainAxis == Axis.vertical && textDirection == TextDirection.rtl) {\n      child = firstChild;\n      while (child != null) {\n        final childParentData = _getParentData(child);\n        final crossAxisCellCount = crossAxisCount;\n        final crossAxisCellExtent =\n            stride * crossAxisCellCount - crossAxisSpacing;\n        final offset = childParentData.offset;\n        final crossAxisOffset =\n            crossAxisExtent - offset.dx - crossAxisCellExtent;\n        final mainAxisOffset = offset.dy;\n        final newOffset = Offset(crossAxisOffset, mainAxisOffset);\n        childParentData.offset = newOffset;\n        child = childAfter(child);\n      }\n    }\n\n    return mainAxis == Axis.vertical\n        ? Size(crossAxisExtent, mainAxisExtent)\n        : Size(mainAxisExtent, crossAxisExtent);\n  }\n\n  bool _hasOverflow = false;\n}\n\nclass GridParentData extends ContainerBoxParentData<RenderBox> {\n  int? crossAxisCellCount;\n  num? mainAxisCellCount;\n  double? realMainAxisExtent;\n\n  @override\n  String toString() =>\n      'crossAxisCellCount=$crossAxisCellCount; mainAxisCellCount=$mainAxisCellCount;';\n}\n\nclass GridItem extends ParentDataWidget<GridParentData> {\n  final int crossAxisCellCount;\n  final num? mainAxisCellCount;\n  final bool isDeletable;\n\n  const GridItem({\n    super.key,\n    required super.child,\n    this.mainAxisCellCount,\n    this.crossAxisCellCount = 1,\n    this.isDeletable = true,\n  });\n\n  @override\n  void applyParentData(RenderObject renderObject) {\n    final parentData = renderObject.parentData;\n    if (parentData is GridParentData) {\n      bool needsLayout = false;\n      if (parentData.crossAxisCellCount != crossAxisCellCount) {\n        parentData.crossAxisCellCount = crossAxisCellCount;\n        needsLayout = true;\n      }\n\n      if (parentData.mainAxisCellCount != mainAxisCellCount) {\n        parentData.mainAxisCellCount = mainAxisCellCount;\n        needsLayout = true;\n      }\n\n      if (needsLayout) {\n        final targetParent = renderObject.parent;\n        if (targetParent is RenderGrid) {\n          targetParent.markNeedsLayout();\n        }\n      }\n    }\n  }\n\n  @override\n  Type get debugTypicalAncestorWidgetClass => GridItem;\n\n  GridItem wrap({required WrapBuilder builder}) {\n    return GridItem(\n      mainAxisCellCount: mainAxisCellCount,\n      crossAxisCellCount: crossAxisCellCount,\n      isDeletable: isDeletable,\n      child: builder(child),\n    );\n  }\n}\n\nclass _Origin {\n  final int crossAxisIndex;\n  final double mainAxisOffset;\n\n  const _Origin(this.crossAxisIndex, this.mainAxisOffset);\n}\n\n_Origin _getOrigin(List<double> offsets, int crossAxisCount) {\n  final length = offsets.length;\n  _Origin origin = const _Origin(0, double.infinity);\n  for (int i = 0; i < length; i++) {\n    final offset = offsets[i];\n    if (offset.moreOrEqual(origin.mainAxisOffset)) {\n      continue;\n    }\n    int start = 0;\n    int span = 0;\n    for (\n      int j = 0;\n      span < crossAxisCount &&\n          j < length &&\n          length - j >= crossAxisCount - span;\n      j++\n    ) {\n      if (offset.moreOrEqual(offsets[j])) {\n        span++;\n        if (span == crossAxisCount) {\n          origin = _Origin(start, offset);\n        }\n      } else {\n        start = j + 1;\n        span = 0;\n      }\n    }\n  }\n  return origin;\n}\n"
  },
  {
    "path": "lib/widgets/icon.dart",
    "content": "import 'dart:io';\nimport 'dart:ui' as ui;\nimport 'package:bett_box/common/common.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_cache_manager/flutter_cache_manager.dart';\nimport 'package:flutter_svg/svg.dart';\nimport 'package:xml/xml.dart';\nimport 'package:crypto/crypto.dart';\nimport 'dart:convert';\nimport 'package:path/path.dart' as path;\n\nclass CommonTargetIcon extends StatefulWidget {\n  final String src;\n  final double size;\n\n  const CommonTargetIcon({super.key, required this.src, required this.size});\n\n  @override\n  State<CommonTargetIcon> createState() => _CommonTargetIconState();\n}\n\nclass _CommonTargetIconState extends State<CommonTargetIcon> {\n  File? _file;\n  String? _cachedSrc; // Cached src\n  int? _cachedSize; // Cached size\n\n  @override\n  void initState() {\n    super.initState();\n    _init();\n  }\n\n  @override\n  void didUpdateWidget(covariant CommonTargetIcon oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;\n    final cacheSize = (widget.size * devicePixelRatio).ceil();\n\n    // Reinit when src or size changes\n    if (oldWidget.src != widget.src || _cachedSize != cacheSize) {\n      _file = null;\n      _cachedSrc = null;\n      _cachedSize = null;\n      _init();\n    }\n  }\n\n  /// Generate resized cache path\n  Future<String> _getResizedCachePath(String originalPath, int size) async {\n    final hash = md5.convert(utf8.encode('${originalPath}_$size')).toString();\n    final tempDir = await appPath.tempPath;\n    return path.join(tempDir, 'resized_icons', '$hash.png');\n  }\n\n  /// Decode, resize and cache image to disk\n  Future<File?> _resizeAndCacheImage(File originalFile, int targetSize) async {\n    try {\n      final cachePath = await _getResizedCachePath(\n        originalFile.path,\n        targetSize,\n      );\n      final cacheFile = File(cachePath);\n\n      // Return cached file if exists\n      if (await cacheFile.exists()) {\n        return cacheFile;\n      }\n\n      // Read original image\n      final bytes = await originalFile.readAsBytes();\n      final codec = await ui.instantiateImageCodec(\n        bytes,\n        targetWidth: targetSize,\n        targetHeight: targetSize,\n      );\n      final frame = await codec.getNextFrame();\n      final image = frame.image;\n\n      // Convert to PNG bytes\n      final byteData = await image.toByteData(format: ui.ImageByteFormat.png);\n      if (byteData == null) {\n        return null;\n      }\n\n      // Save to disk\n      await cacheFile.parent.create(recursive: true);\n      await cacheFile.writeAsBytes(byteData.buffer.asUint8List());\n\n      return cacheFile;\n    } catch (e) {\n      // Resize failed, return original\n      return originalFile;\n    }\n  }\n\n  /// Validate and sanitize SVG file\n  Future<bool> _validateSvg(File file) async {\n    try {\n      final content = await file.readAsString();\n      final trimmed = content.trim();\n\n      // Check if content starts with valid SVG/XML tags\n      if (!trimmed.startsWith('<svg') &&\n          !trimmed.startsWith('<?xml') &&\n          !trimmed.startsWith('<!DOCTYPE svg')) {\n        commonPrint.log('Invalid SVG: not starting with svg tag');\n        return false;\n      }\n\n      // Check for HTML error pages\n      if (trimmed.contains('<!DOCTYPE html>') ||\n          trimmed.contains('<html>') ||\n          trimmed.contains('<head>') ||\n          trimmed.contains('<body>')) {\n        commonPrint.log('Invalid SVG: HTML content detected');\n        return false;\n      }\n\n      // Validate XML structure\n      try {\n        XmlDocument.parse(content);\n      } catch (e) {\n        commonPrint.log('Invalid SVG: XML parse error - $e');\n        return false;\n      }\n\n      // Fix invalid font-weight values\n      if (content.contains('font-weight:none') ||\n          content.contains('font-weight: none')) {\n        final fixed = content\n            .replaceAll('font-weight:none', 'font-weight:normal')\n            .replaceAll('font-weight: none', 'font-weight: normal');\n        await file.writeAsString(fixed);\n      }\n      return true;\n    } catch (e) {\n      commonPrint.log('SVG validation failed: $e');\n      return false;\n    }\n  }\n\n  Future<void> _init() async {\n    if (widget.src.isEmpty) {\n      return;\n    }\n    if (widget.src.getBase64 != null) {\n      return;\n    }\n\n    final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;\n    final cacheSize = (widget.size * devicePixelRatio).ceil();\n\n    // If cached with same src and size, return directly\n    if (_cachedSrc == widget.src && _cachedSize == cacheSize && _file != null) {\n      return;\n    }\n\n    // Get from cache first, no network check\n    final fileInfo = await DefaultCacheManager().getFileFromCache(widget.src);\n    if (fileInfo != null && mounted && widget.src.isNotEmpty) {\n      // Validate SVG files\n      if (widget.src.isSvg) {\n        final isValid = await _validateSvg(fileInfo.file);\n        if (!isValid) {\n          // Remove invalid cached file\n          await DefaultCacheManager().removeFile(widget.src);\n          if (mounted) {\n            setState(() {\n              _file = null;\n              _cachedSrc = null;\n              _cachedSize = null;\n            });\n          }\n          return;\n        }\n      }\n\n      // Resize non-SVG images\n      File? displayFile = fileInfo.file;\n      if (!widget.src.isSvg) {\n        displayFile = await _resizeAndCacheImage(fileInfo.file, cacheSize);\n      }\n\n      if (mounted) {\n        setState(() {\n          _file = displayFile;\n          _cachedSrc = widget.src; // Mark cached\n          _cachedSize = cacheSize; // Mark cached size\n        });\n      }\n      return;\n    }\n\n    // Download if cache not exists\n    try {\n      final file = await DefaultCacheManager().getSingleFile(widget.src);\n      if (mounted && widget.src.isNotEmpty) {\n        // Validate SVG files\n        if (widget.src.isSvg) {\n          final isValid = await _validateSvg(file);\n          if (!isValid) {\n            // Remove invalid downloaded file\n            await DefaultCacheManager().removeFile(widget.src);\n            if (mounted) {\n              setState(() {\n                _file = null;\n                _cachedSrc = null;\n                _cachedSize = null;\n              });\n            }\n            return;\n          }\n        }\n\n        // Resize non-SVG images\n        File? displayFile = file;\n        if (!widget.src.isSvg) {\n          displayFile = await _resizeAndCacheImage(file, cacheSize);\n        }\n\n        if (mounted) {\n          setState(() {\n            _file = displayFile;\n            _cachedSrc = widget.src; // Mark cached\n            _cachedSize = cacheSize; // Mark cached size\n          });\n        }\n      }\n    } catch (e) {\n      // Handle download error\n    }\n  }\n\n  Widget _defaultIcon() {\n    return Icon(IconsExt.target, size: widget.size);\n  }\n\n  Widget _buildIcon() {\n    if (widget.src.isEmpty) {\n      return _defaultIcon();\n    }\n    final base64 = widget.src.getBase64;\n    final devicePixelRatio = MediaQuery.of(context).devicePixelRatio;\n    final cacheSize = (widget.size * devicePixelRatio).ceil();\n\n    if (base64 != null) {\n      return Image.memory(\n        base64,\n        gaplessPlayback: true,\n        cacheWidth: cacheSize,\n        cacheHeight: cacheSize,\n        errorBuilder: (_, error, _) {\n          return _defaultIcon();\n        },\n      );\n    }\n    if (_file != null) {\n      if (widget.src.isSvg) {\n        return FutureBuilder<bool>(\n          future: _validateSvg(_file!),\n          builder: (context, snapshot) {\n            if (snapshot.connectionState == ConnectionState.waiting) {\n              return _defaultIcon();\n            }\n            if (snapshot.hasError || snapshot.data == false) {\n              commonPrint.log('SVG validation failed in build: ${snapshot.error}');\n              // Remove invalid file and clear state\n              DefaultCacheManager().removeFile(widget.src);\n              _file = null;\n              _cachedSrc = null;\n              _cachedSize = null;\n              return _defaultIcon();\n            }\n            try {\n              return SvgPicture.file(\n                _file!,\n                width: widget.size,\n                height: widget.size,\n                placeholderBuilder: (_) => _defaultIcon(),\n              );\n            } catch (e) {\n              commonPrint.log('Failed to load SVG: $e');\n              DefaultCacheManager().removeFile(widget.src);\n              _file = null;\n              _cachedSrc = null;\n              _cachedSize = null;\n              return _defaultIcon();\n            }\n          },\n        );\n      }\n      return Image.file(\n        _file!,\n        gaplessPlayback: true,\n        errorBuilder: (_, _, _) => _defaultIcon(),\n      );\n    }\n    return _defaultIcon();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return SizedBox(\n      width: widget.size,\n      height: widget.size,\n      child: AnimatedSwitcher(\n        duration: const Duration(milliseconds: 200),\n        child: KeyedSubtree(\n          key: ValueKey<String>('${widget.src}_${_file?.path}'),\n          child: _buildIcon(),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/input.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/models/common.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/dialog.dart';\nimport 'package:bett_box/widgets/null_status.dart';\nimport 'package:flutter/material.dart';\n\nimport 'card.dart';\nimport 'effect.dart';\nimport 'float_layout.dart';\nimport 'list.dart';\n\nclass OptionsDialog<T> extends StatelessWidget {\n  final String title;\n  final List<T> options;\n  final T value;\n  final String Function(T value) textBuilder;\n\n  const OptionsDialog({\n    super.key,\n    required this.title,\n    required this.options,\n    required this.textBuilder,\n    required this.value,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return CommonDialog(\n      title: title,\n      padding: const EdgeInsets.symmetric(horizontal: 8, vertical: 16),\n      child: Column(\n        mainAxisSize: MainAxisSize.min,\n        children: [\n          for (final option in options)\n            Material(\n              color: Colors.transparent,\n              child: InkWell(\n                onTap: () {\n                  Navigator.of(context).pop(option);\n                },\n                child: Container(\n                  padding: const EdgeInsets.symmetric(\n                    horizontal: 16,\n                    vertical: 12,\n                  ),\n                  child: Row(\n                    children: [\n                      Icon(\n                        value == option\n                            ? Icons.check_circle_rounded\n                            : Icons.circle_outlined,\n                        size: 21,\n                        color: value == option\n                            ? context.colorScheme.primary\n                            : context.colorScheme.onSurfaceVariant.withValues(\n                                alpha: 0.6,\n                              ),\n                      ),\n                      const SizedBox(width: 12),\n                      Expanded(\n                        child: value == option\n                            ? SingleChildScrollView(\n                                scrollDirection: Axis.horizontal,\n                                child: Text(\n                                  textBuilder(option),\n                                  style: context.textTheme.bodyMedium,\n                                ),\n                              )\n                            : Text(\n                                textBuilder(option),\n                                style: context.textTheme.bodyMedium,\n                                overflow: TextOverflow.ellipsis,\n                                maxLines: 1,\n                              ),\n                      ),\n                    ],\n                  ),\n                ),\n              ),\n            ),\n        ],\n      ),\n    );\n  }\n}\n\nclass CommonCheckBox extends StatelessWidget {\n  final bool? value;\n  final ValueChanged<bool?>? onChanged;\n  final bool isCircle;\n\n  const CommonCheckBox({\n    required this.value,\n    required this.onChanged,\n    this.isCircle = false,\n    super.key,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return Checkbox(\n      shape: isCircle ? const CircleBorder() : null,\n      value: value,\n      onChanged: onChanged,\n    );\n  }\n}\n\nclass InputDialog extends StatefulWidget {\n  final String title;\n  final String value;\n  final String? suffixText;\n  final String? labelText;\n  final String? resetValue;\n  final String? hintText;\n  final FormFieldValidator<String>? validator;\n  final AutovalidateMode? autovalidateMode;\n  final bool? obscureText;\n  final bool autofocus;\n\n  const InputDialog({\n    super.key,\n    required this.title,\n    required this.value,\n    this.suffixText,\n    this.resetValue,\n    this.hintText,\n    this.validator,\n    this.obscureText,\n    this.labelText,\n    this.autofocus = false,\n    this.autovalidateMode = AutovalidateMode.onUserInteraction,\n  });\n\n  @override\n  State<InputDialog> createState() => _InputDialogState();\n}\n\nclass _InputDialogState extends State<InputDialog> {\n  final _formKey = GlobalKey<FormState>();\n\n  late TextEditingController textController;\n\n  String get value => widget.value;\n\n  String get title => widget.title;\n\n  String? get suffixText => widget.suffixText;\n\n  @override\n  void initState() {\n    super.initState();\n    textController = TextEditingController(text: value);\n  }\n\n  Future<void> _handleUpdate() async {\n    // Trigger validation and check result\n    final isValid = _formKey.currentState?.validate() ?? false;\n    if (!isValid) {\n      // Validation failed, keep dialog open\n      return;\n    }\n    // Validation passed, close dialog with value\n    final text = textController.value.text;\n    Navigator.of(context).pop<String>(text);\n  }\n\n  Future<void> _handleReset() async {\n    if (widget.resetValue == null) {\n      return;\n    }\n    Navigator.of(context).pop<String>(widget.resetValue);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return CommonDialog(\n      title: title,\n      actions: [\n        if (widget.resetValue != null &&\n            textController.value.text != widget.resetValue) ...[\n          TextButton(\n            onPressed: _handleReset,\n            child: Text(appLocalizations.reset),\n          ),\n          const SizedBox(width: 4),\n        ],\n        TextButton(\n          onPressed: _handleUpdate,\n          child: Text(appLocalizations.submit),\n        ),\n      ],\n      child: Form(\n        autovalidateMode: widget.autovalidateMode,\n        key: _formKey,\n        child: Wrap(\n          runSpacing: 16,\n          children: [\n            TextFormField(\n              autofocus: widget.autofocus,\n              obscureText: widget.obscureText ?? false,\n              keyboardType: TextInputType.url,\n              maxLines: widget.obscureText == true ? 1 : 5,\n              minLines: 1,\n              controller: textController,\n              onFieldSubmitted: (_) {\n                _handleUpdate();\n              },\n              decoration: InputDecoration(\n                border: const OutlineInputBorder(),\n                suffixText: suffixText,\n                hintText: widget.hintText,\n                labelText: widget.labelText,\n              ),\n              validator: widget.validator,\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n\nclass ListInputPage extends StatelessWidget {\n  final String title;\n  final List<String> items;\n  final Widget Function(String item) titleBuilder;\n  final Widget Function(String item)? subtitleBuilder;\n  final Widget Function(String item)? leadingBuilder;\n  final String? valueLabel;\n  final Function(List<String> items) onChange;\n\n  const ListInputPage({\n    super.key,\n    required this.title,\n    required this.items,\n    required this.titleBuilder,\n    required this.onChange,\n    this.leadingBuilder,\n    this.valueLabel,\n    this.subtitleBuilder,\n  });\n\n  Future<void> _handleAddOrEdit([String? item]) async {\n    uniqueValidator(String? value) {\n      final index = items.indexWhere((entry) {\n        return entry == value;\n      });\n      final current = item == value;\n      if (index != -1 && !current) {\n        return appLocalizations.existsTip(appLocalizations.value);\n      }\n      return null;\n    }\n\n    final valueField = Field(\n      label: valueLabel ?? appLocalizations.value,\n      value: item ?? '',\n      validator: uniqueValidator,\n    );\n    final value = await globalState.showCommonDialog<String>(\n      child: AddDialog(valueField: valueField, title: title),\n    );\n    if (value == null) return;\n    final index = items.indexWhere((entry) {\n      return entry == item;\n    });\n    final nextItems = List<String>.from(items);\n    if (item != null) {\n      nextItems[index] = value;\n    } else {\n      nextItems.add(value);\n    }\n    onChange(nextItems);\n  }\n\n  void _handleDelete(String? item) {\n    final entries = List<String>.from(items);\n    final index = entries.indexWhere((entry) {\n      return entry == item;\n    });\n    if (index != -1) {\n      entries.removeAt(index);\n    }\n    onChange(entries);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return FloatLayout(\n      floatingWidget: FloatWrapper(\n        child: FloatingActionButton(\n          onPressed: () async {\n            _handleAddOrEdit();\n          },\n          child: const Icon(Icons.add),\n        ),\n      ),\n      child: items.isEmpty\n          ? NullStatus(label: appLocalizations.noData)\n          : ReorderableListView.builder(\n              padding: const EdgeInsets.only(bottom: 16 + 64),\n              buildDefaultDragHandles: false,\n              itemCount: items.length,\n              itemBuilder: (context, index) {\n                final e = items[index];\n                return _InputItem(\n                  key: ValueKey(e),\n                  ReorderableDelayedDragStartListener(\n                    index: index,\n                    child: CommonCard(\n                      child: ListItem(\n                        leading: leadingBuilder != null\n                            ? leadingBuilder!(e)\n                            : null,\n                        title: titleBuilder(e),\n                        subtitle: subtitleBuilder != null\n                            ? subtitleBuilder!(e)\n                            : null,\n                        trailing: IconButton(\n                          icon: const Icon(Icons.delete_outline),\n                          onPressed: () {\n                            _handleDelete(e);\n                          },\n                        ),\n                      ),\n                      onPressed: () {\n                        _handleAddOrEdit(e);\n                      },\n                    ),\n                  ),\n                );\n              },\n              onReorder: (oldIndex, newIndex) {\n                if (oldIndex < newIndex) {\n                  newIndex -= 1;\n                }\n                final nextItems = List<String>.from(items);\n                final item = nextItems.removeAt(oldIndex);\n                nextItems.insert(newIndex, item);\n                onChange(nextItems);\n              },\n            ),\n    );\n  }\n}\n\nclass MapInputPage extends StatelessWidget {\n  final String title;\n  final Map<String, String> map;\n  final Widget Function(MapEntry<String, String> item) titleBuilder;\n  final Widget Function(MapEntry<String, String> item)? subtitleBuilder;\n  final Widget Function(MapEntry<String, String> item)? leadingBuilder;\n  final String? keyLabel;\n  final String? valueLabel;\n  final Function(Map<String, String> items) onChange;\n\n  const MapInputPage({\n    super.key,\n    required this.title,\n    required this.map,\n    required this.titleBuilder,\n    required this.onChange,\n    this.leadingBuilder,\n    this.keyLabel,\n    this.valueLabel,\n    this.subtitleBuilder,\n  });\n\n  List<MapEntry<String, String>> get items =>\n      List<MapEntry<String, String>>.from(map.entries);\n\n  Future<void> _handleAddOrEdit([MapEntry<String, String>? item]) async {\n    uniqueValidator(String? value) {\n      final index = items.indexWhere((entry) {\n        return entry.key == value;\n      });\n      final current = item?.key == value;\n      if (index != -1 && !current) {\n        return appLocalizations.existsTip(appLocalizations.key);\n      }\n      return null;\n    }\n\n    final keyField = Field(\n      label: keyLabel ?? appLocalizations.key,\n      value: item == null ? '' : item.key,\n      validator: uniqueValidator,\n    );\n\n    final valueField = Field(\n      label: valueLabel ?? appLocalizations.value,\n      value: item == null ? '' : item.value,\n    );\n\n    final value = await globalState.showCommonDialog<MapEntry<String, String>>(\n      child: AddDialog(\n        keyField: keyField,\n        valueField: valueField,\n        title: title,\n      ),\n    );\n    if (value == null) return;\n    final index = items.indexWhere((entry) {\n      return entry.key == item?.key;\n    });\n\n    final nextItems = List<MapEntry<String, String>>.from(items);\n    if (item != null) {\n      nextItems[index] = value;\n    } else {\n      nextItems.add(value);\n    }\n    onChange(Map.fromEntries(nextItems));\n  }\n\n  void _handleDelete(MapEntry<String, String> item) {\n    final entries = List<MapEntry<String, String>>.from(items);\n    final index = entries.indexWhere((entry) {\n      return entry.key == item.key && item.value == entry.value;\n    });\n    if (index != -1) {\n      entries.removeAt(index);\n    }\n    onChange(Map.fromEntries(entries));\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return FloatLayout(\n      floatingWidget: FloatWrapper(\n        child: FloatingActionButton(\n          onPressed: () async {\n            _handleAddOrEdit();\n          },\n          child: const Icon(Icons.add),\n        ),\n      ),\n      child: items.isEmpty\n          ? NullStatus(label: appLocalizations.noData)\n          : ReorderableListView.builder(\n              padding: const EdgeInsets.only(bottom: 16 + 64),\n              proxyDecorator: proxyDecorator,\n              buildDefaultDragHandles: false,\n              itemCount: items.length,\n              itemBuilder: (_, index) {\n                final e = items[index];\n                return _InputItem(\n                  key: ValueKey(e.key),\n                  ReorderableDelayedDragStartListener(\n                    index: index,\n                    child: CommonCard(\n                      child: ListItem(\n                        leading: leadingBuilder != null\n                            ? leadingBuilder!(e)\n                            : null,\n                        title: titleBuilder(e),\n                        subtitle: subtitleBuilder != null\n                            ? subtitleBuilder!(e)\n                            : null,\n                        trailing: IconButton(\n                          icon: const Icon(Icons.delete_outline),\n                          onPressed: () {\n                            _handleDelete(e);\n                          },\n                        ),\n                      ),\n                      onPressed: () {\n                        _handleAddOrEdit(e);\n                      },\n                    ),\n                  ),\n                );\n              },\n              onReorder: (oldIndex, newIndex) {\n                if (oldIndex < newIndex) {\n                  newIndex -= 1;\n                }\n                final nextItems = List<MapEntry<String, String>>.from(items);\n                final item = nextItems.removeAt(oldIndex);\n                nextItems.insert(newIndex, item);\n                onChange(Map.fromEntries(nextItems));\n              },\n            ),\n    );\n  }\n}\n\nclass AddDialog extends StatefulWidget {\n  final String title;\n  final Field? keyField;\n  final Field valueField;\n\n  const AddDialog({\n    super.key,\n    required this.title,\n    this.keyField,\n    required this.valueField,\n  });\n\n  @override\n  State<AddDialog> createState() => _AddDialogState();\n}\n\nclass _AddDialogState extends State<AddDialog> {\n  TextEditingController? keyController;\n  late TextEditingController valueController;\n  final GlobalKey<FormState> _formKey = GlobalKey<FormState>();\n\n  Field? get keyField => widget.keyField;\n\n  Field get valueField => widget.valueField;\n\n  @override\n  void initState() {\n    super.initState();\n    if (keyField != null) {\n      keyController = TextEditingController(text: keyField!.value);\n    }\n    valueController = TextEditingController(text: valueField.value);\n  }\n\n  void _submit() {\n    if (!_formKey.currentState!.validate()) return;\n    if (keyField != null) {\n      Navigator.of(context).pop<MapEntry<String, String>>(\n        MapEntry(keyController!.text, valueController.text),\n      );\n    } else {\n      Navigator.of(context).pop<String>(valueController.text);\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return CommonDialog(\n      title: widget.title,\n      actions: [\n        TextButton(onPressed: _submit, child: Text(appLocalizations.confirm)),\n      ],\n      child: Form(\n        autovalidateMode: AutovalidateMode.onUserInteraction,\n        key: _formKey,\n        child: Wrap(\n          runSpacing: 16,\n          children: [\n            if (keyField != null)\n              TextFormField(\n                maxLines: 2,\n                minLines: 1,\n                controller: keyController,\n                decoration: InputDecoration(\n                  border: const OutlineInputBorder(),\n                  labelText: keyField!.label,\n                ),\n                validator: (String? value) {\n                  String? res;\n                  if (keyField!.validator != null) {\n                    res = keyField!.validator!(value);\n                  }\n                  if (res != null) {\n                    return res;\n                  }\n                  if (value == null || value.isEmpty) {\n                    return appLocalizations.emptyTip(appLocalizations.key);\n                  }\n                  return null;\n                },\n              ),\n            TextFormField(\n              maxLines: 3,\n              minLines: 1,\n              controller: valueController,\n              decoration: InputDecoration(\n                border: const OutlineInputBorder(),\n                labelText: valueField.label,\n              ),\n              validator: (String? value) {\n                String? res;\n                if (valueField.validator != null) {\n                  res = valueField.validator!(value);\n                }\n                if (res != null) {\n                  return res;\n                }\n                if (value == null || value.isEmpty) {\n                  return appLocalizations.emptyTip(appLocalizations.value);\n                }\n                return null;\n              },\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n\nclass _InputItem extends StatelessWidget {\n  final Widget child;\n\n  const _InputItem(this.child, {super.key});\n\n  @override\n  Widget build(BuildContext context) {\n    return Material(\n      elevation: 0,\n      key: key,\n      color: Colors.transparent,\n      child: Container(\n        padding: EdgeInsets.symmetric(horizontal: 16),\n        margin: EdgeInsets.symmetric(vertical: 8),\n        child: child,\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/keep_scope.dart",
    "content": "import 'package:flutter/material.dart';\n\nclass KeepScope extends StatefulWidget {\n  final Widget child;\n  final bool keep;\n\n  const KeepScope({super.key, required this.child, this.keep = true});\n\n  @override\n  State<KeepScope> createState() => _KeepContainerState();\n}\n\nclass _KeepContainerState extends State<KeepScope>\n    with AutomaticKeepAliveClientMixin {\n  @override\n  Widget build(BuildContext context) {\n    super.build(context);\n    return widget.child;\n  }\n\n  @override\n  bool get wantKeepAlive => widget.keep;\n}\n"
  },
  {
    "path": "lib/widgets/line_chart.dart",
    "content": "import 'dart:ui';\nimport 'package:bett_box/common/color.dart';\nimport 'package:flutter/material.dart';\n\nclass Point {\n  final double x;\n  final double y;\n\n  const Point(this.x, this.y);\n}\n\nclass LineChart extends StatefulWidget {\n  final List<Point> points;\n  final Color color;\n  final Duration duration;\n  final bool gradient;\n\n  const LineChart({\n    super.key,\n    this.gradient = false,\n    required this.points,\n    required this.color,\n    this.duration = Duration.zero,\n  });\n\n  @override\n  State<LineChart> createState() => _LineChartState();\n}\n\nclass _LineChartState extends State<LineChart>\n    with SingleTickerProviderStateMixin {\n  AnimationController? _controller;\n  List<Point> prevPoints = [];\n  List<Point> points = [];\n  bool get _hasAnimation => widget.duration != Duration.zero;\n\n  @override\n  void initState() {\n    super.initState();\n    points = widget.points;\n    prevPoints = points;\n\n    // Create AnimationController only if needed\n    if (_hasAnimation) {\n      _controller = AnimationController(vsync: this, duration: widget.duration);\n    }\n  }\n\n  @override\n  void didUpdateWidget(LineChart oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    if (widget.points != points) {\n      prevPoints = points;\n      points = widget.points;\n\n      // Trigger animation if needed\n      if (_hasAnimation) {\n        _controller?.forward(from: 0);\n      } else {\n        // Direct repaint without animation\n        setState(() {});\n      }\n    }\n  }\n\n  @override\n  void dispose() {\n    _controller?.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return LayoutBuilder(\n      builder: (_, container) {\n        // Fast path without animation\n        if (!_hasAnimation) {\n          return CustomPaint(\n            painter: LineChartPainter(\n              prevPoints: points,\n              points: points,\n              progress: 1.0,\n              gradient: widget.gradient,\n              color: widget.color,\n              hasAnimation: false,\n            ),\n            child: SizedBox(\n              height: container.maxHeight,\n              width: container.maxWidth,\n            ),\n          );\n        }\n\n        // Use AnimatedBuilder with animation\n        return AnimatedBuilder(\n          animation: _controller!.view,\n          builder: (_, _) {\n            return CustomPaint(\n              painter: LineChartPainter(\n                prevPoints: prevPoints,\n                points: points,\n                progress: _controller!.value,\n                gradient: widget.gradient,\n                color: widget.color,\n                hasAnimation: true,\n              ),\n              child: SizedBox(\n                height: container.maxHeight,\n                width: container.maxWidth,\n              ),\n            );\n          },\n        );\n      },\n    );\n  }\n}\n\nclass LineChartPainter extends CustomPainter {\n  final List<Point> prevPoints;\n  final List<Point> points;\n  final double progress;\n  final Color color;\n  final bool gradient;\n  final bool hasAnimation;\n\n  LineChartPainter({\n    required this.prevPoints,\n    required this.points,\n    required this.progress,\n    required this.color,\n    required this.gradient,\n    this.hasAnimation = true,\n  });\n\n  List<Point> getRenderPoints(List<Point> points) {\n    if (points.isEmpty) return [];\n    double maxX = points[0].x;\n    double minX = points[0].x;\n    double maxY = points[0].y;\n    double minY = points[0].y;\n\n    for (final point in points) {\n      if (point.x > maxX) maxX = point.x;\n      if (point.x < minX) minX = point.x;\n      if (point.y > maxY) maxY = point.y;\n      if (point.y < minY) minY = point.y;\n    }\n\n    return points.map((e) {\n      var x = (e.x - minX) / (maxX - minX);\n      if (x.isNaN) x = 0;\n      var y = (e.y - minY) / (maxY - minY);\n      if (y.isNaN) y = 0;\n      return Point(x, y);\n    }).toList();\n  }\n\n  List<Point> getInterpolatePoints(\n    List<Point> prevPoints,\n    List<Point> points,\n    double t,\n  ) {\n    final renderPrevPoints = getRenderPoints(prevPoints);\n    final renderPoints = getRenderPoints(points);\n\n    return List.generate(renderPoints.length, (i) {\n      if (i > renderPrevPoints.length - 1) {\n        return renderPoints[i];\n      }\n      final x = lerpDouble(renderPrevPoints[i].x, renderPoints[i].x, t)!;\n      final y = lerpDouble(renderPrevPoints[i].y, renderPoints[i].y, t)!;\n      return Point(x, y);\n    });\n  }\n\n  Path getPath(List<Point> points, Size size) {\n    final path = Path()\n      ..moveTo(points[0].x * size.width, (1 - points[0].y) * size.height);\n\n    for (var i = 1; i < points.length - 1; i++) {\n      final nextPoint = points[i + 1];\n      final currentPoint = points[i];\n      final midX = (currentPoint.x + nextPoint.x) / 2;\n      final midY = (currentPoint.y + nextPoint.y) / 2;\n\n      path.quadraticBezierTo(\n        currentPoint.x * size.width,\n        (1 - currentPoint.y) * size.height,\n        midX * size.width,\n        (1 - midY) * size.height,\n      );\n    }\n\n    path.lineTo(points.last.x * size.width, (1 - points.last.y) * size.height);\n    return path;\n  }\n\n  Path getAnimatedPath(Size size) {\n    final interpolatedPoints = getInterpolatePoints(\n      prevPoints,\n      points,\n      progress,\n    );\n    final path = getPath(interpolatedPoints, size);\n\n    // Expensive operation: only with animation\n    final metric = path.computeMetrics().first;\n    final length = metric.length;\n    return metric.extractPath(0, length);\n  }\n\n  @override\n  void paint(Canvas canvas, Size size) {\n    const strokeWidth = 2.0;\n    final chartSize = Size(size.width, size.height * 0.7);\n\n    // Fast path without animation\n    final Path path;\n    if (!hasAnimation || progress >= 1.0) {\n      // Direct render without interpolation\n      final renderPoints = getRenderPoints(points);\n      path = getPath(renderPoints, chartSize);\n    } else {\n      // Animated path\n      path = getAnimatedPath(chartSize);\n    }\n\n    if (gradient) {\n      final fillPath = Path.from(path);\n      fillPath.lineTo(size.width, size.height + strokeWidth * 2);\n      fillPath.lineTo(0, size.height + strokeWidth * 2);\n      fillPath.close();\n\n      final gradient = LinearGradient(\n        begin: Alignment.topCenter,\n        end: Alignment.bottomCenter,\n        colors: [color.opacity38, color.opacity10],\n      );\n\n      final shader = gradient.createShader(\n        Rect.fromLTWH(0, 0, size.width, size.height + strokeWidth * 2),\n      );\n\n      canvas.drawPath(\n        fillPath,\n        Paint()\n          ..shader = shader\n          ..style = PaintingStyle.fill,\n      );\n    }\n\n    canvas.drawPath(\n      path,\n      Paint()\n        ..color = color\n        ..strokeWidth = strokeWidth\n        ..style = PaintingStyle.stroke,\n    );\n  }\n\n  @override\n  bool shouldRepaint(covariant LineChartPainter oldDelegate) {\n    return oldDelegate.progress != progress ||\n        oldDelegate.prevPoints != prevPoints ||\n        oldDelegate.points != points ||\n        oldDelegate.color != color ||\n        oldDelegate.gradient != gradient;\n  }\n}\n"
  },
  {
    "path": "lib/widgets/list.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:bett_box/widgets/open_container.dart';\nimport 'package:flutter/material.dart';\n\nimport 'card.dart';\nimport 'input.dart';\nimport 'scaffold.dart';\nimport 'sheet.dart';\n\nclass Delegate {\n  const Delegate();\n}\n\nclass RadioDelegate<T> extends Delegate {\n  final T value;\n  final T groupValue;\n  final void Function(T?)? onChanged;\n\n  const RadioDelegate({\n    required this.value,\n    required this.groupValue,\n    this.onChanged,\n  });\n}\n\nclass SwitchDelegate<T> extends Delegate {\n  final bool value;\n  final ValueChanged<bool>? onChanged;\n\n  const SwitchDelegate({required this.value, this.onChanged});\n}\n\nclass CheckboxDelegate<T> extends Delegate {\n  final bool value;\n  final ValueChanged<bool?>? onChanged;\n\n  const CheckboxDelegate({this.value = false, this.onChanged});\n}\n\nclass OpenDelegate extends Delegate {\n  final Widget widget;\n  final String title;\n  final double? maxWidth;\n  final List<Widget> actions;\n  final bool blur;\n  final bool wrap;\n  final bool forceFull;\n\n  const OpenDelegate({\n    required this.title,\n    required this.widget,\n    this.maxWidth,\n    this.actions = const [],\n    this.blur = false,\n    this.wrap = true,\n    this.forceFull = true,\n  });\n}\n\nclass NextDelegate extends Delegate {\n  final Widget widget;\n  final String title;\n  final double? maxWidth;\n  final List<Widget> actions;\n  final bool blur;\n  final bool wrap;\n\n  const NextDelegate({\n    required this.title,\n    required this.widget,\n    this.maxWidth,\n    this.actions = const [],\n    this.blur = false,\n    this.wrap = true,\n  });\n}\n\nclass OptionsDelegate<T> extends Delegate {\n  final List<T> options;\n  final String title;\n  final T value;\n  final String Function(T value) textBuilder;\n  final Function(T? value) onChanged;\n\n  const OptionsDelegate({\n    required this.title,\n    required this.options,\n    required this.textBuilder,\n    required this.value,\n    required this.onChanged,\n  });\n}\n\nclass InputDelegate extends Delegate {\n  final String title;\n  final String value;\n  final String? suffixText;\n  final String? hintText;\n  final Function(String? value)? onChanged;\n  final FormFieldValidator<String>? validator;\n\n  final String? resetValue;\n\n  const InputDelegate({\n    required this.title,\n    required this.value,\n    this.suffixText,\n    this.hintText,\n    required this.onChanged,\n    this.resetValue,\n    this.validator,\n  });\n}\n\nclass ListItem<T> extends StatelessWidget {\n  final Widget? leading;\n  final Widget title;\n  final Widget? subtitle;\n  final EdgeInsets padding;\n  final ListTileTitleAlignment tileTitleAlignment;\n  final bool? dense;\n  final Widget? trailing;\n  final Delegate delegate;\n  final double? horizontalTitleGap;\n  final TextStyle? titleTextStyle;\n  final TextStyle? subtitleTextStyle;\n  final void Function()? onTap;\n\n  const ListItem({\n    super.key,\n    required this.title,\n    this.subtitle,\n    this.leading,\n    this.padding = const EdgeInsets.symmetric(horizontal: 16),\n    this.trailing,\n    this.horizontalTitleGap,\n    this.dense,\n    this.onTap,\n    this.titleTextStyle,\n    this.subtitleTextStyle,\n    this.tileTitleAlignment = ListTileTitleAlignment.center,\n  }) : delegate = const Delegate();\n\n  const ListItem.open({\n    super.key,\n    required this.title,\n    this.subtitle,\n    this.leading,\n    this.padding = const EdgeInsets.symmetric(horizontal: 16),\n    this.trailing,\n    required OpenDelegate this.delegate,\n    this.horizontalTitleGap,\n    this.dense,\n    this.titleTextStyle,\n    this.subtitleTextStyle,\n    this.tileTitleAlignment = ListTileTitleAlignment.center,\n  }) : onTap = null;\n\n  const ListItem.next({\n    super.key,\n    required this.title,\n    this.subtitle,\n    this.leading,\n    this.padding = const EdgeInsets.symmetric(horizontal: 16),\n    this.trailing,\n    required NextDelegate this.delegate,\n    this.horizontalTitleGap,\n    this.dense,\n    this.titleTextStyle,\n    this.subtitleTextStyle,\n    this.tileTitleAlignment = ListTileTitleAlignment.center,\n  }) : onTap = null;\n\n  const ListItem.options({\n    super.key,\n    required this.title,\n    this.subtitle,\n    this.leading,\n    this.padding = const EdgeInsets.symmetric(horizontal: 16),\n    this.trailing,\n    required OptionsDelegate<T> this.delegate,\n    this.horizontalTitleGap,\n    this.dense,\n    this.titleTextStyle,\n    this.subtitleTextStyle,\n    this.tileTitleAlignment = ListTileTitleAlignment.center,\n  }) : onTap = null;\n\n  const ListItem.input({\n    super.key,\n    required this.title,\n    this.subtitle,\n    this.leading,\n    this.padding = const EdgeInsets.symmetric(horizontal: 16),\n    this.trailing,\n    required InputDelegate this.delegate,\n    this.horizontalTitleGap,\n    this.dense,\n    this.titleTextStyle,\n    this.subtitleTextStyle,\n    this.tileTitleAlignment = ListTileTitleAlignment.center,\n  }) : onTap = null;\n\n  const ListItem.checkbox({\n    super.key,\n    required this.title,\n    this.subtitle,\n    this.leading,\n    this.padding = const EdgeInsets.only(left: 16, right: 8),\n    required CheckboxDelegate<T> this.delegate,\n    this.horizontalTitleGap,\n    this.dense,\n    this.titleTextStyle,\n    this.subtitleTextStyle,\n    this.tileTitleAlignment = ListTileTitleAlignment.center,\n  }) : trailing = null,\n       onTap = null;\n\n  const ListItem.switchItem({\n    super.key,\n    required this.title,\n    this.subtitle,\n    this.leading,\n    this.trailing,\n    this.padding = const EdgeInsets.only(left: 16, right: 8),\n    required SwitchDelegate<T> this.delegate,\n    this.horizontalTitleGap,\n    this.dense,\n    this.titleTextStyle,\n    this.subtitleTextStyle,\n    this.tileTitleAlignment = ListTileTitleAlignment.center,\n  }) : onTap = null;\n\n  const ListItem.radio({\n    super.key,\n    required this.title,\n    this.subtitle,\n    this.trailing,\n    this.padding = const EdgeInsets.only(left: 12, right: 16),\n    required RadioDelegate<T> this.delegate,\n    this.horizontalTitleGap = 8,\n    this.dense,\n    this.titleTextStyle,\n    this.subtitleTextStyle,\n    this.tileTitleAlignment = ListTileTitleAlignment.center,\n  }) : leading = null,\n       onTap = null;\n\n  Widget _buildListTile({\n    void Function()? onTap,\n    Widget? trailing,\n    Widget? leading,\n    bool enabled = true,\n  }) {\n    return ListTile(\n      key: key,\n      dense: dense,\n      enabled: enabled,\n      titleTextStyle: titleTextStyle,\n      subtitleTextStyle: subtitleTextStyle,\n      leading: leading ?? this.leading,\n      horizontalTitleGap: horizontalTitleGap,\n      title: title,\n      minVerticalPadding: 12,\n      subtitle: subtitle,\n      titleAlignment: tileTitleAlignment,\n      onTap: onTap,\n      trailing: trailing ?? this.trailing,\n      contentPadding: padding,\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    if (delegate is OpenDelegate) {\n      final openDelegate = delegate as OpenDelegate;\n      final child = openDelegate.widget;\n      return OpenContainer(\n        closedBuilder: (_, action) {\n          openAction() {\n            final isMobile = globalState.appState.viewMode == ViewMode.mobile;\n            if (!isMobile || system.isDesktop) {\n              showExtend(\n                context,\n                props: ExtendProps(\n                  blur: openDelegate.blur,\n                  maxWidth: openDelegate.maxWidth,\n                  forceFull: openDelegate.forceFull,\n                ),\n                builder: (_, type) {\n                  return openDelegate.wrap\n                      ? AdaptiveSheetScaffold(\n                          actions: openDelegate.actions,\n                          type: type,\n                          body: child,\n                          title: openDelegate.title,\n                        )\n                      : child;\n                },\n              );\n              return;\n            }\n            action();\n          }\n\n          return _buildListTile(onTap: openAction);\n        },\n        openBuilder: (_, action) {\n          return openDelegate.wrap\n              ? CommonScaffold(\n                  key: Key(openDelegate.title),\n                  title: openDelegate.title,\n                  body: child,\n                  actions: openDelegate.actions,\n                )\n              : child;\n        },\n      );\n    }\n    if (delegate is NextDelegate) {\n      final nextDelegate = delegate as NextDelegate;\n      final child = nextDelegate.widget;\n\n      return _buildListTile(\n        onTap: () {\n          showExtend(\n            context,\n            props: ExtendProps(\n              blur: nextDelegate.blur,\n              maxWidth: nextDelegate.maxWidth,\n            ),\n            builder: (_, type) {\n              return nextDelegate.wrap\n                  ? AdaptiveSheetScaffold(\n                      actions: nextDelegate.actions,\n                      type: type,\n                      body: child,\n                      title: nextDelegate.title,\n                    )\n                  : child;\n            },\n          );\n        },\n      );\n    }\n    if (delegate is OptionsDelegate) {\n      final optionsDelegate = delegate as OptionsDelegate<T>;\n      return _buildListTile(\n        onTap: () async {\n          final value = await globalState.showCommonDialog<T>(\n            child: OptionsDialog<T>(\n              title: optionsDelegate.title,\n              options: optionsDelegate.options,\n              textBuilder: optionsDelegate.textBuilder,\n              value: optionsDelegate.value,\n            ),\n          );\n          optionsDelegate.onChanged(value);\n        },\n      );\n    }\n    if (delegate is InputDelegate) {\n      final inputDelegate = delegate as InputDelegate;\n      final isEnabled = inputDelegate.onChanged != null;\n      return _buildListTile(\n        enabled: isEnabled,\n        onTap: isEnabled\n            ? () async {\n                final value = await globalState.showCommonDialog<String>(\n                  child: InputDialog(\n                    title: inputDelegate.title,\n                    value: inputDelegate.value,\n                    suffixText: inputDelegate.suffixText,\n                    hintText: inputDelegate.hintText,\n                    resetValue: inputDelegate.resetValue,\n                    validator: inputDelegate.validator,\n                  ),\n                );\n                inputDelegate.onChanged!(value);\n              }\n            : null,\n      );\n    }\n    if (delegate is CheckboxDelegate) {\n      final checkboxDelegate = delegate as CheckboxDelegate;\n      return _buildListTile(\n        onTap: () {\n          if (checkboxDelegate.onChanged != null) {\n            checkboxDelegate.onChanged!(!checkboxDelegate.value);\n          }\n        },\n        trailing: Padding(\n          padding: const EdgeInsets.only(right: 8),\n          child: Icon(\n            checkboxDelegate.value\n                ? Icons.check_circle_rounded\n                : Icons.circle_outlined,\n            size: 24,\n            color: checkboxDelegate.value\n                ? context.colorScheme.primary\n                : context.colorScheme.onSurfaceVariant.withValues(alpha: 0.6),\n          ),\n        ),\n      );\n    }\n    if (delegate is SwitchDelegate) {\n      final switchDelegate = delegate as SwitchDelegate;\n      final isEnabled = switchDelegate.onChanged != null;\n      return _buildListTile(\n        enabled: isEnabled,\n        onTap: isEnabled\n            ? () {\n                switchDelegate.onChanged!(!switchDelegate.value);\n              }\n            : null,\n        trailing: Row(\n          mainAxisSize: MainAxisSize.min,\n          children: [\n            ?trailing,\n            Switch(\n              value: switchDelegate.value,\n              onChanged: switchDelegate.onChanged,\n            ),\n          ],\n        ),\n      );\n    }\n    if (delegate is RadioDelegate) {\n      final radioDelegate = delegate as RadioDelegate<T>;\n      final isSelected = radioDelegate.value == radioDelegate.groupValue;\n      return _buildListTile(\n        onTap: () {\n          if (radioDelegate.onChanged != null) {\n            radioDelegate.onChanged!(radioDelegate.value);\n          }\n        },\n        leading: Icon(\n          isSelected ? Icons.check_circle_rounded : Icons.circle_outlined,\n          size: 21,\n          color: isSelected\n              ? context.colorScheme.primary\n              : context.colorScheme.onSurfaceVariant.withValues(alpha: 0.6),\n        ),\n        trailing: trailing,\n      );\n    }\n\n    return _buildListTile(onTap: onTap);\n  }\n}\n\nclass ListHeader extends StatelessWidget {\n  final String title;\n  final String? subTitle;\n  final List<Widget> actions;\n  final EdgeInsets? padding;\n  final double? space;\n\n  const ListHeader({\n    super.key,\n    required this.title,\n    this.subTitle,\n    this.padding,\n    List<Widget>? actions,\n    this.space,\n  }) : actions = actions ?? const [];\n\n  @override\n  Widget build(BuildContext context) {\n    return Container(\n      alignment: Alignment.centerLeft,\n      padding:\n          padding ??\n          const EdgeInsets.only(left: 16, right: 8, top: 24, bottom: 8),\n      child: Row(\n        mainAxisSize: MainAxisSize.max,\n        mainAxisAlignment: MainAxisAlignment.spaceBetween,\n        children: [\n          Expanded(\n            child: Column(\n              crossAxisAlignment: CrossAxisAlignment.start,\n              children: [\n                Text(\n                  title,\n                  style: Theme.of(context).textTheme.labelLarge?.copyWith(\n                    color: Theme.of(\n                      context,\n                    ).colorScheme.onSurfaceVariant.opacity80,\n                    fontWeight: FontWeight.w600,\n                  ),\n                ),\n                if (subTitle != null)\n                  Text(\n                    subTitle!,\n                    style: Theme.of(context).textTheme.bodySmall?.copyWith(\n                      color: Theme.of(context).colorScheme.outline,\n                    ),\n                  ),\n              ],\n            ),\n          ),\n          Row(\n            mainAxisSize: MainAxisSize.min,\n            mainAxisAlignment: MainAxisAlignment.end,\n            children: [...genActions(actions, space: space)],\n          ),\n        ],\n      ),\n    );\n  }\n}\n\nList<Widget> generateSection({\n  String? title,\n  required Iterable<Widget> items,\n  List<Widget>? actions,\n  bool separated = true,\n}) {\n  final genItems = separated\n      ? items.separated(const Divider(height: 0))\n      : items;\n  return [\n    if (items.isNotEmpty && title != null)\n      ListHeader(title: title, actions: actions),\n    ...genItems,\n  ];\n}\n\nWidget generateSectionV2({\n  String? title,\n  required Iterable<Widget> items,\n  List<Widget>? actions,\n  bool separated = true,\n}) {\n  return Column(\n    children: [\n      if (items.isNotEmpty && title != null)\n        ListHeader(title: title, actions: actions),\n      CommonCard(\n        radius: 18,\n        type: CommonCardType.filled,\n        child: Column(children: [...items]),\n      ),\n    ],\n  );\n}\n\nList<Widget> generateInfoSection({\n  required Info info,\n  required Iterable<Widget> items,\n  List<Widget>? actions,\n  bool separated = true,\n}) {\n  final genItems = separated\n      ? items.separated(const Divider(height: 0))\n      : items;\n  return [\n    if (items.isNotEmpty) InfoHeader(info: info, actions: actions),\n    ...genItems,\n  ];\n}\n\nWidget generateListView(List<Widget> items) {\n  return ListView.builder(\n    itemCount: items.length,\n    itemBuilder: (_, index) => items[index],\n    padding: const EdgeInsets.only(bottom: 16),\n  );\n}\n"
  },
  {
    "path": "lib/widgets/notification.dart",
    "content": "import 'package:bett_box/models/config.dart';\nimport 'package:bett_box/providers/config.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nclass TextScaleNotification extends StatelessWidget {\n  final Widget child;\n  final Function(TextScale textScale) onNotification;\n\n  const TextScaleNotification({\n    super.key,\n    required this.child,\n    required this.onNotification,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return Consumer(\n      builder: (_, ref, child) {\n        ref.listen(themeSettingProvider.select((state) => state.textScale), (\n          prev,\n          next,\n        ) {\n          if (prev != next) {\n            onNotification(next);\n          }\n        });\n        return child!;\n      },\n      child: child,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/null_status.dart",
    "content": "import 'package:flutter/material.dart';\n\nimport '../common/common.dart';\n\nclass NullStatus extends StatelessWidget {\n  final String label;\n\n  const NullStatus({super.key, required this.label});\n\n  @override\n  Widget build(BuildContext context) {\n    return Center(\n      child: Text(\n        label,\n        style: Theme.of(context).textTheme.titleMedium?.toBold,\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/open_container.dart",
    "content": "import 'package:flutter/material.dart';\nimport 'package:flutter/scheduler.dart';\n\ntypedef CloseContainerActionCallback<S> = void Function({S? returnValue});\ntypedef OpenContainerBuilder<S> =\n    Widget Function(\n      BuildContext context,\n      CloseContainerActionCallback<S> action,\n    );\ntypedef CloseContainerBuilder =\n    Widget Function(BuildContext context, VoidCallback action);\n\nenum ContainerTransitionType { fade, fadeThrough }\n\ntypedef ClosedCallback<S> = void Function(S data);\n\n@optionalTypeArgs\nclass OpenContainer<T extends Object?> extends StatefulWidget {\n  const OpenContainer({\n    super.key,\n    this.middleColor,\n    this.onClosed,\n    required this.closedBuilder,\n    required this.openBuilder,\n    this.tappable = true,\n    this.transitionDuration = const Duration(milliseconds: 300),\n    this.transitionType = ContainerTransitionType.fade,\n    this.useRootNavigator = false,\n    this.routeSettings,\n    this.clipBehavior = Clip.antiAlias,\n  });\n\n  final Color? middleColor;\n  final ClosedCallback<T?>? onClosed;\n  final CloseContainerBuilder closedBuilder;\n  final OpenContainerBuilder<T> openBuilder;\n  final bool tappable;\n  final Duration transitionDuration;\n  final ContainerTransitionType transitionType;\n  final bool useRootNavigator;\n  final RouteSettings? routeSettings;\n  final Clip clipBehavior;\n\n  @override\n  State<OpenContainer<T?>> createState() => _OpenContainerState<T>();\n}\n\nclass _OpenContainerState<T> extends State<OpenContainer<T?>> {\n  final GlobalKey<_HideableState> _hideableKey = GlobalKey<_HideableState>();\n  final GlobalKey _closedBuilderKey = GlobalKey();\n\n  Future<void> openContainer() async {\n    final Color middleColor =\n        widget.middleColor ?? Theme.of(context).canvasColor;\n    final T? data =\n        await Navigator.of(\n          context,\n          rootNavigator: widget.useRootNavigator,\n        ).push(\n          _OpenContainerRoute<T>(\n            middleColor: middleColor,\n            closedBuilder: widget.closedBuilder,\n            openBuilder: widget.openBuilder,\n            hideableKey: _hideableKey,\n            closedBuilderKey: _closedBuilderKey,\n            transitionDuration: widget.transitionDuration,\n            transitionType: widget.transitionType,\n            useRootNavigator: widget.useRootNavigator,\n            routeSettings: widget.routeSettings,\n          ),\n        );\n    if (widget.onClosed != null) {\n      widget.onClosed!(data);\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return _Hideable(\n      key: _hideableKey,\n      child: GestureDetector(\n        onTap: widget.tappable ? openContainer : null,\n        child: Material(\n          color: Colors.transparent,\n          clipBehavior: widget.clipBehavior,\n          child: Builder(\n            key: _closedBuilderKey,\n            builder: (BuildContext context) {\n              return widget.closedBuilder(context, openContainer);\n            },\n          ),\n        ),\n      ),\n    );\n  }\n}\n\nclass _Hideable extends StatefulWidget {\n  const _Hideable({super.key, required this.child});\n\n  final Widget child;\n\n  @override\n  State<_Hideable> createState() => _HideableState();\n}\n\nclass _HideableState extends State<_Hideable> {\n  Size? get placeholderSize => _placeholderSize;\n  Size? _placeholderSize;\n\n  set placeholderSize(Size? value) {\n    if (_placeholderSize == value) {\n      return;\n    }\n    setState(() {\n      _placeholderSize = value;\n    });\n  }\n\n  bool get isVisible => _visible;\n  bool _visible = true;\n\n  set isVisible(bool value) {\n    if (_visible == value) {\n      return;\n    }\n    setState(() {\n      _visible = value;\n    });\n  }\n\n  bool get isInTree => _placeholderSize == null;\n\n  @override\n  Widget build(BuildContext context) {\n    if (_placeholderSize != null) {\n      return SizedBox.fromSize(size: _placeholderSize);\n    }\n    return Visibility(\n      visible: _visible,\n      maintainSize: true,\n      maintainState: true,\n      maintainAnimation: true,\n      child: widget.child,\n    );\n  }\n}\n\nclass _OpenContainerRoute<T> extends ModalRoute<T> {\n  _OpenContainerRoute({\n    required this.middleColor,\n    required this.closedBuilder,\n    required this.openBuilder,\n    required this.hideableKey,\n    required this.closedBuilderKey,\n    required this.transitionDuration,\n    required this.transitionType,\n    required this.useRootNavigator,\n    required RouteSettings? routeSettings,\n  }) : _closedOpacityTween = _getClosedOpacityTween(transitionType),\n       _openOpacityTween = _getOpenOpacityTween(transitionType),\n       super(settings: routeSettings);\n\n  static _FlippableTweenSequence<Color?> _getColorTween({\n    required ContainerTransitionType transitionType,\n    required Color closedColor,\n    required Color openColor,\n    required Color middleColor,\n  }) {\n    switch (transitionType) {\n      case ContainerTransitionType.fade:\n        return _FlippableTweenSequence<Color?>(<TweenSequenceItem<Color?>>[\n          TweenSequenceItem<Color>(\n            tween: ConstantTween<Color>(closedColor),\n            weight: 1 / 5,\n          ),\n          TweenSequenceItem<Color?>(\n            tween: ColorTween(begin: closedColor, end: openColor),\n            weight: 1 / 5,\n          ),\n          TweenSequenceItem<Color>(\n            tween: ConstantTween<Color>(openColor),\n            weight: 3 / 5,\n          ),\n        ]);\n      case ContainerTransitionType.fadeThrough:\n        return _FlippableTweenSequence<Color?>(<TweenSequenceItem<Color?>>[\n          TweenSequenceItem<Color?>(\n            tween: ColorTween(begin: closedColor, end: middleColor),\n            weight: 1 / 5,\n          ),\n          TweenSequenceItem<Color?>(\n            tween: ColorTween(begin: middleColor, end: openColor),\n            weight: 4 / 5,\n          ),\n        ]);\n    }\n  }\n\n  static _FlippableTweenSequence<double> _getClosedOpacityTween(\n    ContainerTransitionType transitionType,\n  ) {\n    switch (transitionType) {\n      case ContainerTransitionType.fade:\n        return _FlippableTweenSequence<double>(<TweenSequenceItem<double>>[\n          TweenSequenceItem<double>(\n            tween: ConstantTween<double>(1.0),\n            weight: 1,\n          ),\n        ]);\n      case ContainerTransitionType.fadeThrough:\n        return _FlippableTweenSequence<double>(<TweenSequenceItem<double>>[\n          TweenSequenceItem<double>(\n            tween: Tween<double>(begin: 1.0, end: 0.0),\n            weight: 1 / 5,\n          ),\n          TweenSequenceItem<double>(\n            tween: ConstantTween<double>(0.0),\n            weight: 4 / 5,\n          ),\n        ]);\n    }\n  }\n\n  static _FlippableTweenSequence<double> _getOpenOpacityTween(\n    ContainerTransitionType transitionType,\n  ) {\n    switch (transitionType) {\n      case ContainerTransitionType.fade:\n        return _FlippableTweenSequence<double>(<TweenSequenceItem<double>>[\n          TweenSequenceItem<double>(\n            tween: ConstantTween<double>(0.0),\n            weight: 1 / 5,\n          ),\n          TweenSequenceItem<double>(\n            tween: Tween<double>(begin: 0.0, end: 1.0),\n            weight: 1 / 5,\n          ),\n          TweenSequenceItem<double>(\n            tween: ConstantTween<double>(1.0),\n            weight: 3 / 5,\n          ),\n        ]);\n      case ContainerTransitionType.fadeThrough:\n        return _FlippableTweenSequence<double>(<TweenSequenceItem<double>>[\n          TweenSequenceItem<double>(\n            tween: ConstantTween<double>(0.0),\n            weight: 1 / 5,\n          ),\n          TweenSequenceItem<double>(\n            tween: Tween<double>(begin: 0.0, end: 1.0),\n            weight: 4 / 5,\n          ),\n        ]);\n    }\n  }\n\n  final Color middleColor;\n  final CloseContainerBuilder closedBuilder;\n  final OpenContainerBuilder<T> openBuilder;\n  final GlobalKey<_HideableState> hideableKey;\n  final GlobalKey closedBuilderKey;\n\n  @override\n  final Duration transitionDuration;\n  final ContainerTransitionType transitionType;\n\n  final bool useRootNavigator;\n\n  final _FlippableTweenSequence<double> _closedOpacityTween;\n  final _FlippableTweenSequence<double> _openOpacityTween;\n  late _FlippableTweenSequence<Color?> _colorTween;\n  final GlobalKey _openBuilderKey = GlobalKey();\n  final RectTween _rectTween = RectTween();\n\n  AnimationStatus? _lastAnimationStatus;\n  AnimationStatus? _currentAnimationStatus;\n\n  @override\n  TickerFuture didPush() {\n    _takeMeasurements(navigatorContext: hideableKey.currentContext!);\n\n    animation!.addStatusListener((AnimationStatus status) {\n      _lastAnimationStatus = _currentAnimationStatus;\n      _currentAnimationStatus = status;\n      switch (status) {\n        case AnimationStatus.dismissed:\n          _toggleHideable(hide: false);\n          break;\n        case AnimationStatus.completed:\n          _toggleHideable(hide: true);\n          break;\n        case AnimationStatus.forward:\n        case AnimationStatus.reverse:\n          break;\n      }\n    });\n\n    return super.didPush();\n  }\n\n  @override\n  bool didPop(T? result) {\n    _takeMeasurements(\n      navigatorContext: subtreeContext!,\n      delayForSourceRoute: true,\n    );\n    return super.didPop(result);\n  }\n\n  @override\n  void dispose() {\n    if (hideableKey.currentState?.isVisible == false) {\n      SchedulerBinding.instance.addPostFrameCallback(\n        (Duration d) => _toggleHideable(hide: false),\n      );\n    }\n    super.dispose();\n  }\n\n  void _toggleHideable({required bool hide}) {\n    if (hideableKey.currentState != null) {\n      hideableKey.currentState!\n        ..placeholderSize = null\n        ..isVisible = !hide;\n    }\n  }\n\n  void _takeMeasurements({\n    required BuildContext navigatorContext,\n    bool delayForSourceRoute = false,\n  }) {\n    final RenderBox navigator =\n        Navigator.of(\n              navigatorContext,\n              rootNavigator: useRootNavigator,\n            ).context.findRenderObject()!\n            as RenderBox;\n    final Size navSize = _getSize(navigator);\n    _rectTween.end = Offset.zero & navSize;\n\n    void takeMeasurementsInSourceRoute([Duration? _]) {\n      if (!navigator.attached || hideableKey.currentContext == null) {\n        return;\n      }\n      _rectTween.begin = _getRect(hideableKey, navigator);\n      hideableKey.currentState!.placeholderSize = _rectTween.begin!.size;\n    }\n\n    if (delayForSourceRoute) {\n      SchedulerBinding.instance.addPostFrameCallback(\n        takeMeasurementsInSourceRoute,\n      );\n    } else {\n      takeMeasurementsInSourceRoute();\n    }\n  }\n\n  Size _getSize(RenderBox render) {\n    assert(render.hasSize);\n    return render.size;\n  }\n\n  Rect _getRect(GlobalKey key, RenderBox ancestor) {\n    assert(key.currentContext != null);\n    assert(ancestor.hasSize);\n    final RenderBox render =\n        key.currentContext!.findRenderObject()! as RenderBox;\n    assert(render.hasSize);\n    return MatrixUtils.transformRect(\n      render.getTransformTo(ancestor),\n      Offset.zero & render.size,\n    );\n  }\n\n  bool get _transitionWasInterrupted {\n    bool wasInProgress = false;\n    bool isInProgress = false;\n\n    switch (_currentAnimationStatus) {\n      case AnimationStatus.completed:\n      case AnimationStatus.dismissed:\n        isInProgress = false;\n        break;\n      case AnimationStatus.forward:\n      case AnimationStatus.reverse:\n        isInProgress = true;\n        break;\n      case null:\n        break;\n    }\n    switch (_lastAnimationStatus) {\n      case AnimationStatus.completed:\n      case AnimationStatus.dismissed:\n        wasInProgress = false;\n        break;\n      case AnimationStatus.forward:\n      case AnimationStatus.reverse:\n        wasInProgress = true;\n        break;\n      case null:\n        break;\n    }\n    return wasInProgress && isInProgress;\n  }\n\n  void closeContainer({T? returnValue}) {\n    Navigator.of(subtreeContext!).pop(returnValue);\n  }\n\n  @override\n  Widget buildPage(\n    BuildContext context,\n    Animation<double> animation,\n    Animation<double> secondaryAnimation,\n  ) {\n    _colorTween = _getColorTween(\n      transitionType: transitionType,\n      closedColor: Theme.of(context).colorScheme.surface,\n      openColor: Theme.of(context).colorScheme.surface,\n      middleColor: middleColor,\n    );\n    return Align(\n      alignment: Alignment.topLeft,\n      child: AnimatedBuilder(\n        animation: animation,\n        builder: (BuildContext context, Widget? child) {\n          if (animation.isCompleted) {\n            return SizedBox.expand(\n              child: Material(\n                child: Builder(\n                  key: _openBuilderKey,\n                  builder: (BuildContext context) {\n                    return openBuilder(context, closeContainer);\n                  },\n                ),\n              ),\n            );\n          }\n\n          final Animation<double> curvedAnimation = CurvedAnimation(\n            parent: animation,\n            curve: Curves.fastOutSlowIn,\n            reverseCurve: _transitionWasInterrupted\n                ? null\n                : Curves.fastOutSlowIn.flipped,\n          );\n          TweenSequence<Color?>? colorTween;\n          TweenSequence<double>? closedOpacityTween, openOpacityTween;\n          switch (animation.status) {\n            case AnimationStatus.dismissed:\n            case AnimationStatus.forward:\n              closedOpacityTween = _closedOpacityTween;\n              openOpacityTween = _openOpacityTween;\n              colorTween = _colorTween;\n              break;\n            case AnimationStatus.reverse:\n              if (_transitionWasInterrupted) {\n                closedOpacityTween = _closedOpacityTween;\n                openOpacityTween = _openOpacityTween;\n                colorTween = _colorTween;\n                break;\n              }\n              closedOpacityTween = _closedOpacityTween.flipped;\n              openOpacityTween = _openOpacityTween.flipped;\n              colorTween = _colorTween.flipped;\n              break;\n            case AnimationStatus.completed:\n              assert(false); // Unreachable.\n              break;\n          }\n          assert(colorTween != null);\n          assert(closedOpacityTween != null);\n          assert(openOpacityTween != null);\n\n          final Rect rect = _rectTween.evaluate(curvedAnimation)!;\n          return SizedBox.expand(\n            child: Align(\n              alignment: Alignment.topLeft,\n              child: Transform.translate(\n                offset: Offset(rect.left, rect.top),\n                child: SizedBox(\n                  width: rect.width,\n                  height: rect.height,\n                  child: Material(\n                    clipBehavior: Clip.antiAlias,\n                    animationDuration: Duration.zero,\n                    color: colorTween!.evaluate(animation),\n                    child: Stack(\n                      fit: StackFit.passthrough,\n                      children: <Widget>[\n                        // Closed child fading out.\n                        FittedBox(\n                          fit: BoxFit.fitWidth,\n                          alignment: Alignment.topLeft,\n                          child: SizedBox(\n                            width: _rectTween.begin!.width,\n                            height: _rectTween.begin!.height,\n                            child: (hideableKey.currentState?.isInTree ?? false)\n                                ? null\n                                : FadeTransition(\n                                    opacity: closedOpacityTween!.animate(\n                                      animation,\n                                    ),\n                                    child: Builder(\n                                      key: closedBuilderKey,\n                                      builder: (BuildContext context) {\n                                        // Use dummy \"open container\" callback\n                                        // since we are in the process of opening.\n                                        return closedBuilder(context, () {});\n                                      },\n                                    ),\n                                  ),\n                          ),\n                        ),\n\n                        // Open child fading in.\n                        FittedBox(\n                          fit: BoxFit.fitWidth,\n                          alignment: Alignment.topLeft,\n                          child: SizedBox(\n                            width: _rectTween.end!.width,\n                            height: _rectTween.end!.height,\n                            child: FadeTransition(\n                              opacity: openOpacityTween!.animate(animation),\n                              child: Builder(\n                                key: _openBuilderKey,\n                                builder: (BuildContext context) {\n                                  return openBuilder(context, closeContainer);\n                                },\n                              ),\n                            ),\n                          ),\n                        ),\n                      ],\n                    ),\n                  ),\n                ),\n              ),\n            ),\n          );\n        },\n      ),\n    );\n  }\n\n  @override\n  bool get maintainState => true;\n\n  @override\n  Color? get barrierColor => null;\n\n  @override\n  bool get opaque => true;\n\n  @override\n  bool get barrierDismissible => false;\n\n  @override\n  String? get barrierLabel => null;\n}\n\nclass _FlippableTweenSequence<T> extends TweenSequence<T> {\n  _FlippableTweenSequence(this._items) : super(_items);\n\n  final List<TweenSequenceItem<T>> _items;\n  _FlippableTweenSequence<T>? _flipped;\n\n  _FlippableTweenSequence<T>? get flipped {\n    if (_flipped == null) {\n      final List<TweenSequenceItem<T>> newItems = <TweenSequenceItem<T>>[];\n      for (int i = 0; i < _items.length; i++) {\n        newItems.add(\n          TweenSequenceItem<T>(\n            tween: _items[i].tween,\n            weight: _items[_items.length - 1 - i].weight,\n          ),\n        );\n      }\n      _flipped = _FlippableTweenSequence<T>(newItems);\n    }\n    return _flipped;\n  }\n}\n"
  },
  {
    "path": "lib/widgets/palette.dart",
    "content": "import 'dart:math' as math;\n\nimport 'package:flutter/gestures.dart';\nimport 'package:flutter/material.dart';\n\n@immutable\nclass Palette extends StatefulWidget {\n  const Palette({super.key, required this.controller});\n\n  final ValueNotifier<Color> controller;\n\n  @override\n  State<Palette> createState() => _PaletteState();\n}\n\nclass _PaletteState extends State<Palette> {\n  final double _thickness = 20;\n  final double _radius = 4;\n  final double _padding = 8;\n  final GlobalKey renderBoxKey = GlobalKey();\n  bool isSquare = false;\n  bool isTrack = false;\n  late double colorHue;\n  late double colorSaturation;\n  late double colorValue;\n\n  late FocusNode _focusNode;\n\n  Color get value => widget.controller.value;\n\n  HSVColor get color => HSVColor.fromColor(value);\n\n  @override\n  void initState() {\n    super.initState();\n    colorHue = color.hue;\n    colorSaturation = color.saturation;\n    colorValue = color.value;\n    _focusNode = FocusNode();\n  }\n\n  void _handleChange() {\n    widget.controller.value = HSVColor.fromAHSV(\n      color.alpha,\n      colorHue,\n      colorSaturation,\n      colorValue,\n    ).toColor();\n  }\n\n  @override\n  void dispose() {\n    _focusNode.dispose();\n    super.dispose();\n  }\n\n  double trackRadius(Size size) =>\n      math.min(size.width, size.height) / 2 - _thickness;\n\n  static double squareRadius(double radius, double trackSquarePadding) =>\n      (radius - trackSquarePadding) / math.sqrt(2);\n\n  void onStart(Offset offset) {\n    final RenderBox renderBox =\n        renderBoxKey.currentContext!.findRenderObject()! as RenderBox;\n    final size = renderBox.size;\n    final radius = trackRadius(size);\n    final radiusOuter = radius + _thickness;\n    final effectiveSquareRadius = squareRadius(radius, _padding);\n    final startPosition = renderBox.localToGlobal(Offset.zero);\n    final center = Offset(size.width / 2, size.height / 2);\n    final vector = offset - startPosition - center;\n    final vectorLength = _Computer.vectorLength(vector);\n    isSquare =\n        vector.dx.abs() < effectiveSquareRadius &&\n        vector.dy.abs() < effectiveSquareRadius;\n    isTrack = vectorLength >= radius && vectorLength <= radiusOuter;\n    if (isSquare) {\n      colorSaturation = _Computer.vectorToSaturation(\n        vector.dx,\n        effectiveSquareRadius,\n      ).clamp(0.0, 1.0);\n      colorValue = _Computer.vectorToValue(\n        vector.dy,\n        effectiveSquareRadius,\n      ).clamp(0.0, 1.0);\n      _handleChange();\n    } else if (isTrack) {\n      colorHue = _Computer.vectorToHue(vector);\n      _handleChange();\n    } else {\n      isTrack = false;\n      isSquare = false;\n    }\n  }\n\n  void onUpdate(Offset offset) {\n    final RenderBox renderBox =\n        renderBoxKey.currentContext!.findRenderObject()! as RenderBox;\n    final size = renderBox.size;\n    final radius = trackRadius(size);\n    final effectiveSquareRadius = squareRadius(radius, _padding);\n    final startPosition = renderBox.localToGlobal(Offset.zero);\n    final center = Offset(size.width / 2, size.height / 2);\n    final vector = offset - startPosition - center;\n    if (isSquare) {\n      isTrack = false;\n      colorSaturation = _Computer.vectorToSaturation(\n        vector.dx,\n        effectiveSquareRadius,\n      ).clamp(0.0, 1.0);\n      colorValue = _Computer.vectorToValue(\n        vector.dy,\n        effectiveSquareRadius,\n      ).clamp(0.0, 1.0);\n\n      _handleChange();\n    } else if (isTrack) {\n      isSquare = false;\n      colorHue = _Computer.vectorToHue(vector);\n      _handleChange();\n    } else {\n      isTrack = false;\n      isSquare = false;\n    }\n  }\n\n  void onEnd() {\n    isTrack = false;\n    isSquare = false;\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return ValueListenableBuilder(\n      valueListenable: widget.controller,\n      builder: (_, _, _) {\n        return GestureDetector(\n          dragStartBehavior: DragStartBehavior.down,\n          onVerticalDragDown: (DragDownDetails details) =>\n              onStart(details.globalPosition),\n          onVerticalDragUpdate: (DragUpdateDetails details) =>\n              onUpdate(details.globalPosition),\n          onHorizontalDragUpdate: (DragUpdateDetails details) =>\n              onUpdate(details.globalPosition),\n          onVerticalDragEnd: (DragEndDetails details) => onEnd(),\n          onHorizontalDragEnd: (DragEndDetails details) => onEnd(),\n          onTapUp: (TapUpDetails details) => onEnd(),\n          child: SizedBox(\n            key: renderBoxKey,\n            child: Focus(\n              focusNode: _focusNode,\n              child: MouseRegion(\n                cursor: WidgetStateMouseCursor.clickable,\n                child: Stack(\n                  fit: StackFit.expand,\n                  children: <Widget>[\n                    RepaintBoundary(\n                      child: CustomPaint(\n                        painter: _ShadePainter(\n                          colorHue: colorHue,\n                          colorSaturation: colorSaturation,\n                          colorValue: colorValue,\n                          thickness: _thickness,\n                          padding: _padding,\n                          trackBorderRadius: _radius,\n                        ),\n                      ),\n                    ),\n                    CustomPaint(\n                      painter: _ShadeThumbPainter(\n                        colorSaturation: colorSaturation,\n                        colorValue: colorValue,\n                        thickness: _thickness,\n                        padding: _padding,\n                      ),\n                    ),\n                    RepaintBoundary(\n                      child: CustomPaint(\n                        painter: _TrackPainter(\n                          thickness: _thickness,\n                          ticks: 360,\n                        ),\n                      ),\n                    ),\n                    CustomPaint(\n                      painter: _TrackThumbPainter(\n                        colorHue: colorHue,\n                        thickness: _thickness,\n                      ),\n                    ),\n                  ],\n                ),\n              ),\n            ),\n          ),\n        );\n      },\n    );\n  }\n}\n\nclass _ShadePainter extends CustomPainter {\n  const _ShadePainter({\n    required this.colorHue,\n    required this.colorSaturation,\n    required this.colorValue,\n    required this.thickness,\n    required this.padding,\n    required this.trackBorderRadius,\n  }) : super();\n\n  final double colorHue;\n  final double colorSaturation;\n  final double colorValue;\n\n  final double thickness;\n  final double padding;\n  final double trackBorderRadius;\n\n  static double trackRadius(Size size, double trackWidth) =>\n      math.min(size.width, size.height) / 2 - trackWidth / 2;\n\n  static double squareRadius(\n    double radius,\n    double trackWidth,\n    double padding,\n  ) => (radius - trackWidth / 2 - padding) / math.sqrt(2);\n\n  @override\n  void paint(Canvas canvas, Size size) {\n    final Offset center = Offset(size.width / 2, size.height / 2);\n    final double radius = trackRadius(size, thickness);\n    final double effectiveSquareRadius = squareRadius(\n      radius,\n      thickness,\n      padding,\n    );\n\n    final Rect rectBox = Rect.fromLTWH(\n      center.dx - effectiveSquareRadius,\n      center.dy - effectiveSquareRadius,\n      effectiveSquareRadius * 2,\n      effectiveSquareRadius * 2,\n    );\n    final RRect rRect = RRect.fromRectAndRadius(\n      rectBox,\n      Radius.circular(trackBorderRadius),\n    );\n\n    final Shader horizontal = LinearGradient(\n      colors: <Color>[\n        Colors.white,\n        HSVColor.fromAHSV(1, colorHue, 1, 1).toColor(),\n      ],\n    ).createShader(rectBox);\n    canvas.drawRRect(\n      rRect,\n      Paint()\n        ..style = PaintingStyle.fill\n        ..shader = horizontal,\n    );\n\n    final Shader vertical = const LinearGradient(\n      begin: Alignment.topCenter,\n      end: Alignment.bottomCenter,\n      colors: <Color>[Colors.transparent, Colors.black],\n    ).createShader(rectBox);\n    canvas.drawRRect(\n      rRect,\n      Paint()\n        ..style = PaintingStyle.fill\n        ..shader = vertical,\n    );\n  }\n\n  @override\n  bool shouldRepaint(_ShadePainter oldDelegate) {\n    return oldDelegate.thickness != thickness ||\n        oldDelegate.padding != padding ||\n        oldDelegate.trackBorderRadius != trackBorderRadius ||\n        oldDelegate.colorHue != colorHue ||\n        oldDelegate.colorSaturation != colorSaturation ||\n        oldDelegate.colorValue != colorValue;\n  }\n}\n\nclass _TrackPainter extends CustomPainter {\n  const _TrackPainter({this.ticks = 360, required this.thickness}) : super();\n  final int ticks;\n  final double thickness;\n\n  @override\n  void paint(Canvas canvas, Size size) {\n    final Offset center = Offset(size.width / 2, size.height / 2);\n\n    const double rads = (2 * math.pi) / 360;\n    const double step = 1;\n    const double aliasing = 0.5;\n\n    final double shortestRectSide = math.min(size.width, size.height);\n\n    final Rect rectCircle = Rect.fromCenter(\n      center: center,\n      width: shortestRectSide - thickness,\n      height: shortestRectSide - thickness,\n    );\n\n    for (int i = 0; i < ticks; i++) {\n      final double sRad = (i - aliasing) * rads;\n      final double eRad = (i + step) * rads;\n      final Paint segmentPaint = Paint()\n        ..color = HSVColor.fromAHSV(1, i.toDouble(), 1, 1).toColor()\n        ..style = PaintingStyle.stroke\n        ..strokeWidth = thickness;\n      canvas.drawArc(rectCircle, sRad, sRad - eRad, false, segmentPaint);\n    }\n  }\n\n  @override\n  bool shouldRepaint(_TrackPainter oldDelegate) {\n    return oldDelegate.thickness != thickness || oldDelegate.ticks != ticks;\n  }\n}\n\nclass _ShadeThumbPainter extends CustomPainter {\n  const _ShadeThumbPainter({\n    required this.colorSaturation,\n    required this.colorValue,\n    required this.thickness,\n    required this.padding,\n  }) : super();\n\n  final double colorSaturation;\n  final double colorValue;\n  final double thickness;\n  final double padding;\n\n  static double trackRadius(Size size, double thickness) =>\n      math.min(size.width, size.height) / 2 - thickness / 2;\n\n  static double squareRadius(\n    double radius,\n    double thickness,\n    double trackSquarePadding,\n  ) => (radius - thickness / 2 - trackSquarePadding) / math.sqrt(2);\n\n  @override\n  void paint(Canvas canvas, Size size) {\n    final Offset center = Offset(size.width / 2, size.height / 2);\n    final double radius = trackRadius(size, thickness);\n    final double effectiveSquareRadius = squareRadius(\n      radius,\n      thickness,\n      padding,\n    );\n\n    final Paint paintBlack = Paint()\n      ..color = Colors.black\n      ..strokeWidth = 5\n      ..style = PaintingStyle.stroke;\n    final Paint paintWhite = Paint()\n      ..color = Colors.white\n      ..strokeWidth = 3\n      ..style = PaintingStyle.stroke;\n\n    final double paletteX = _Computer.saturationToVector(\n      colorSaturation,\n      effectiveSquareRadius,\n      center.dx,\n    );\n    final double paletteY = _Computer.valueToVector(\n      colorValue,\n      effectiveSquareRadius,\n      center.dy,\n    );\n    final Offset paletteVector = Offset(paletteX, paletteY);\n    canvas.drawCircle(paletteVector, 12, paintBlack);\n    canvas.drawCircle(paletteVector, 12, paintWhite);\n  }\n\n  @override\n  bool shouldRepaint(_ShadeThumbPainter oldDelegate) {\n    return oldDelegate.thickness != thickness ||\n        oldDelegate.colorSaturation != colorSaturation ||\n        oldDelegate.colorValue != colorValue ||\n        oldDelegate.padding != padding;\n  }\n}\n\nclass _TrackThumbPainter extends CustomPainter {\n  const _TrackThumbPainter({required this.colorHue, required this.thickness})\n    : super();\n\n  final double colorHue;\n  final double thickness;\n\n  static double trackRadius(Size size, double thickness) =>\n      math.min(size.width, size.height) / 2 - thickness / 2;\n\n  @override\n  void paint(Canvas canvas, Size size) {\n    final Offset center = Offset(size.width / 2, size.height / 2);\n    final double radius = trackRadius(size, thickness);\n    final Paint paintBlack = Paint()\n      ..color = Colors.black\n      ..strokeWidth = 5\n      ..style = PaintingStyle.stroke;\n    final Paint paintWhite = Paint()\n      ..color = Colors.white\n      ..strokeWidth = 3\n      ..style = PaintingStyle.stroke;\n    final Offset track = _Computer.hueToVector(\n      (colorHue + 360.0) * math.pi / 180.0,\n      radius,\n      center,\n    );\n    canvas.drawCircle(track, thickness / 2 + 4, paintBlack);\n    canvas.drawCircle(track, thickness / 2 + 4, paintWhite);\n  }\n\n  @override\n  bool shouldRepaint(_TrackThumbPainter oldDelegate) {\n    return oldDelegate.thickness != thickness ||\n        oldDelegate.colorHue != colorHue;\n  }\n}\n\nclass _Computer {\n  static double vectorLength(Offset vector) =>\n      math.sqrt(vector.dx * vector.dx + vector.dy * vector.dy);\n\n  static double vectorToHue(Offset vector) =>\n      (((math.atan2(vector.dy, vector.dx)) * 180.0 / math.pi) + 360.0) % 360.0;\n\n  static double vectorToSaturation(double vectorX, double squareRadius) =>\n      vectorX * 0.5 / squareRadius + 0.5;\n\n  static double vectorToValue(double vectorY, double squareRadius) =>\n      0.5 - vectorY * 0.5 / squareRadius;\n\n  static Offset hueToVector(double h, double radius, Offset center) => Offset(\n    math.cos(h) * radius + center.dx,\n    math.sin(h) * radius + center.dy,\n  );\n\n  static double saturationToVector(\n    double s,\n    double squareRadius,\n    double centerX,\n  ) => (s - 0.5) * squareRadius / 0.5 + centerX;\n\n  static double valueToVector(double l, double squareRadius, double centerY) =>\n      (0.5 - l) * squareRadius / 0.5 + centerY;\n}\n"
  },
  {
    "path": "lib/widgets/pop_scope.dart",
    "content": "import 'dart:async';\n\nimport 'package:bett_box/state.dart';\nimport 'package:flutter/widgets.dart';\n\nclass CommonPopScope extends StatelessWidget {\n  final Widget child;\n  final FutureOr<bool> Function()? onPop;\n\n  const CommonPopScope({super.key, required this.child, this.onPop});\n\n  @override\n  Widget build(BuildContext context) {\n    return PopScope(\n      canPop: onPop == null ? true : false,\n      onPopInvokedWithResult: onPop == null\n          ? null\n          : (didPop, _) async {\n              if (didPop) {\n                return;\n              }\n              final res = await onPop!();\n              if (!context.mounted) {\n                return;\n              }\n              if (!res) {\n                return;\n              }\n              Navigator.of(context).pop();\n            },\n      child: child,\n    );\n  }\n}\n\nclass SystemBackBlock extends StatefulWidget {\n  final Widget child;\n\n  const SystemBackBlock({super.key, required this.child});\n\n  @override\n  State<SystemBackBlock> createState() => _SystemBackBlockState();\n}\n\nclass _SystemBackBlockState extends State<SystemBackBlock> {\n  @override\n  void initState() {\n    super.initState();\n    WidgetsBinding.instance.addPostFrameCallback((_) {\n      globalState.appController.backBlock();\n    });\n  }\n\n  @override\n  void dispose() {\n    super.dispose();\n    WidgetsBinding.instance.addPostFrameCallback((_) {\n      globalState.appController.unBackBlock();\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return widget.child;\n  }\n}\n"
  },
  {
    "path": "lib/widgets/popup.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/models/common.dart';\nimport 'package:flutter/material.dart';\n\nclass CommonPopupRoute<T> extends PopupRoute<T> {\n  final WidgetBuilder builder;\n  ValueNotifier<Offset> offsetNotifier;\n\n  CommonPopupRoute({\n    required this.barrierLabel,\n    required this.builder,\n    required this.offsetNotifier,\n  });\n\n  @override\n  String? barrierLabel;\n\n  @override\n  Color? get barrierColor => null;\n\n  @override\n  bool get barrierDismissible => true;\n\n  @override\n  Widget buildPage(\n    BuildContext context,\n    Animation<double> animation,\n    Animation<double> secondaryAnimation,\n  ) {\n    return builder(context);\n  }\n\n  @override\n  Widget buildTransitions(\n    BuildContext context,\n    Animation<double> animation,\n    Animation<double> secondaryAnimation,\n    Widget child,\n  ) {\n    final align = Alignment.topRight;\n    final animationValue = CurvedAnimation(\n      parent: animation,\n      curve: Curves.easeIn,\n    ).value;\n    return SafeArea(\n      child: ValueListenableBuilder(\n        valueListenable: offsetNotifier,\n        builder: (_, value, child) {\n          return Align(\n            alignment: align,\n            child: CustomSingleChildLayout(\n              delegate: OverflowAwareLayoutDelegate(\n                offset: value.translate(48, -8),\n              ),\n              child: child,\n            ),\n          );\n        },\n        child: AnimatedBuilder(\n          animation: animation,\n          builder: (_, Widget? child) {\n            return Opacity(\n              opacity: 0.1 + 0.9 * animationValue,\n              child: Transform.scale(\n                alignment: align,\n                scale: 0.7 + 0.3 * animationValue,\n                child: Transform.translate(\n                  offset: Offset(0, -10) * (1 - animationValue),\n                  child: child!,\n                ),\n              ),\n            );\n          },\n          child: builder(context),\n        ),\n      ),\n    );\n  }\n\n  @override\n  Duration get transitionDuration => const Duration(milliseconds: 150);\n}\n\nclass PopupController extends ValueNotifier<bool> {\n  PopupController() : super(false);\n\n  void open() {\n    value = true;\n  }\n\n  void close() {\n    value = false;\n  }\n}\n\ntypedef PopupOpen = Function({Offset offset});\n\nclass CommonPopupBox extends StatefulWidget {\n  final Widget Function(PopupOpen open) targetBuilder;\n  final Widget popup;\n\n  const CommonPopupBox({\n    super.key,\n    required this.targetBuilder,\n    required this.popup,\n  });\n\n  @override\n  State<CommonPopupBox> createState() => _CommonPopupBoxState();\n}\n\nclass _CommonPopupBoxState extends State<CommonPopupBox> {\n  bool _isOpen = false;\n  final _targetOffsetValueNotifier = ValueNotifier<Offset>(Offset.zero);\n  Offset _offset = Offset.zero;\n\n  void _open({Offset offset = Offset.zero}) {\n    _offset = offset;\n    _updateOffset();\n    _isOpen = true;\n    Navigator.of(context)\n        .push(\n          CommonPopupRoute(\n            barrierLabel: utils.id,\n            builder: (BuildContext context) {\n              return widget.popup;\n            },\n            offsetNotifier: _targetOffsetValueNotifier,\n          ),\n        )\n        .then((_) {\n          _isOpen = false;\n        });\n  }\n\n  void _updateOffset() {\n    final renderBox = context.findRenderObject() as RenderBox?;\n    if (renderBox == null) {\n      return;\n    }\n    final viewPadding = MediaQuery.of(context).viewPadding;\n    _targetOffsetValueNotifier.value = renderBox\n        .localToGlobal(\n          Offset.zero.translate(viewPadding.right, viewPadding.top),\n        )\n        .translate(_offset.dx, _offset.dy);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return LayoutBuilder(\n      builder: (_, _) {\n        WidgetsBinding.instance.addPostFrameCallback((_) {\n          if (_isOpen) {\n            _updateOffset();\n          }\n        });\n        return widget.targetBuilder(_open);\n      },\n    );\n  }\n}\n\nclass OverflowAwareLayoutDelegate extends SingleChildLayoutDelegate {\n  final Offset offset;\n\n  OverflowAwareLayoutDelegate({required this.offset});\n\n  @override\n  Size getSize(BoxConstraints constraints) {\n    return Size(constraints.maxWidth, constraints.maxHeight);\n  }\n\n  @override\n  Offset getPositionForChild(Size size, Size childSize) {\n    final safeOffset = Offset(16, 16);\n    double x = (offset.dx - childSize.width).clamp(\n      0,\n      size.width - safeOffset.dx - childSize.width,\n    );\n    double y = (offset.dy).clamp(\n      0,\n      size.height - safeOffset.dy - childSize.height,\n    );\n    return Offset(x, y);\n  }\n\n  @override\n  bool shouldRelayout(covariant OverflowAwareLayoutDelegate oldDelegate) {\n    return oldDelegate.offset != offset;\n  }\n}\n\nclass CommonPopupMenu extends StatelessWidget {\n  final List<PopupMenuItemData> items;\n  final double minWidth;\n  final double minItemVerticalPadding;\n  final double fontSize;\n\n  const CommonPopupMenu({\n    super.key,\n    required this.items,\n    this.minWidth = 200,\n    this.minItemVerticalPadding = 16,\n    this.fontSize = 15,\n  });\n\n  Widget _popupMenuItem(\n    BuildContext context, {\n    required PopupMenuItemData item,\n    required int index,\n  }) {\n    final onPressed = item.onPressed;\n    final disabled = onPressed == null;\n    final color = disabled\n        ? context.colorScheme.onSurface.opacity30\n        : context.colorScheme.onSurface;\n    return InkWell(\n      onTap: onPressed != null\n          ? () {\n              Navigator.of(context).pop();\n              onPressed();\n            }\n          : null,\n      child: Container(\n        constraints: BoxConstraints(minWidth: minWidth),\n        padding: EdgeInsets.only(\n          left: 16,\n          right: 64,\n          top: minItemVerticalPadding,\n          bottom: minItemVerticalPadding,\n        ),\n        child: Row(\n          mainAxisSize: MainAxisSize.max,\n          children: [\n            if (item.icon != null) ...[\n              Icon(item.icon, size: fontSize + 4, color: color),\n              SizedBox(width: 16),\n            ],\n            Flexible(\n              child: Text(\n                item.label,\n                style: context.textTheme.bodyMedium?.copyWith(\n                  color: color,\n                  fontSize: fontSize,\n                ),\n              ),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return IntrinsicHeight(\n      child: IntrinsicWidth(\n        child: Card(\n          elevation: 12,\n          color: context.colorScheme.surfaceContainer,\n          clipBehavior: Clip.antiAlias,\n          shape: RoundedRectangleBorder(\n            borderRadius: BorderRadius.circular(12),\n          ),\n          child: Column(\n            mainAxisSize: MainAxisSize.min,\n            crossAxisAlignment: CrossAxisAlignment.start,\n            children: [\n              for (final item in items.asMap().entries) ...[\n                _popupMenuItem(context, item: item.value, index: item.key),\n                if (item.value != items.last) Divider(height: 0),\n              ],\n            ],\n          ),\n        ),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/scaffold.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/providers/app.dart';\nimport 'package:bett_box/widgets/fade_box.dart';\nimport 'package:bett_box/widgets/pop_scope.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter_riverpod/flutter_riverpod.dart';\n\nimport 'chip.dart';\n\ntypedef OnKeywordsUpdateCallback = void Function(List<String> keywords);\n\ntypedef AppBarSearchStateBuilder =\n    AppBarSearchState? Function(AppBarSearchState? state);\n\nclass CommonScaffold extends StatefulWidget {\n  final AppBar? appBar;\n  final Widget body;\n  final Color? backgroundColor;\n  final String? title;\n  final Widget? leading;\n  final List<Widget>? actions;\n  final bool? centerTitle;\n  final Widget? floatingActionButton;\n  final AppBarEditState? editState;\n  final AppBarSearchState? searchState;\n  final OnKeywordsUpdateCallback? onKeywordsUpdate;\n\n  const CommonScaffold({\n    super.key,\n    this.appBar,\n    required this.body,\n    this.backgroundColor,\n    this.leading,\n    this.title,\n    this.actions,\n    this.centerTitle,\n    this.editState,\n    this.searchState,\n    this.floatingActionButton,\n    this.onKeywordsUpdate,\n  });\n\n  @override\n  State<CommonScaffold> createState() => CommonScaffoldState();\n}\n\nclass CommonScaffoldState extends State<CommonScaffold> {\n  late final ValueNotifier<AppBarState> _appBarState;\n  final ValueNotifier<Widget?> _floatingActionButton = ValueNotifier(null);\n  final ValueNotifier<List<String>> _keywordsNotifier = ValueNotifier([]);\n  final _textController = TextEditingController();\n\n  bool get _isSearch {\n    return _appBarState.value.searchState?.query != null;\n  }\n\n  bool get _isEdit {\n    final editState = _appBarState.value.editState;\n    if (editState == null) {\n      return false;\n    }\n    return editState.editCount > 0;\n  }\n\n  @override\n  void initState() {\n    super.initState();\n    _appBarState = ValueNotifier(\n      AppBarState(editState: widget.editState, searchState: widget.searchState),\n    );\n  }\n\n  Future<void> _updateSearchState(AppBarSearchStateBuilder builder) async {\n    _appBarState.value = _appBarState.value.copyWith(\n      searchState: builder(_appBarState.value.searchState),\n    );\n  }\n\n  set floatingActionButton(Widget? floatingActionButton) {\n    if (_floatingActionButton.value != floatingActionButton) {\n      _floatingActionButton.value = floatingActionButton;\n    }\n  }\n\n  Widget _buildSearchingAppBarTheme(Widget child) {\n    final ThemeData theme = Theme.of(context);\n    final ColorScheme colorScheme = theme.colorScheme;\n    return Theme(\n      data: theme.copyWith(\n        appBarTheme: theme.appBarTheme.copyWith(\n          backgroundColor: colorScheme.brightness == Brightness.dark\n              ? Colors.grey[900]\n              : Colors.white,\n          iconTheme: theme.primaryIconTheme.copyWith(color: Colors.grey),\n          titleTextStyle: theme.textTheme.titleLarge,\n          toolbarTextStyle: theme.textTheme.bodyMedium,\n        ),\n        inputDecorationTheme: InputDecorationTheme(\n          hintStyle: theme.inputDecorationTheme.hintStyle,\n          border: InputBorder.none,\n        ),\n      ),\n      child: child,\n    );\n  }\n\n  @override\n  void didUpdateWidget(CommonScaffold oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    if (oldWidget.editState != widget.editState) {\n      _appBarState.value = _appBarState.value.copyWith(\n        editState: widget.editState,\n      );\n    }\n    if (oldWidget.searchState != widget.searchState) {\n      _appBarState.value = _appBarState.value.copyWith(\n        searchState: widget.searchState,\n      );\n    }\n  }\n\n  void _handleClearInput() {\n    _textController.text = '';\n    if (_appBarState.value.searchState != null) {\n      _appBarState.value.searchState!.onSearch('');\n    }\n  }\n\n  void _handleClear() {\n    if (_textController.text.isNotEmpty) {\n      _handleClearInput();\n      return;\n    }\n    _updateSearchState((state) => state?.copyWith(query: null));\n  }\n\n  void _handleExitSearching() {\n    _handleClearInput();\n    _updateSearchState((state) => state?.copyWith(query: null));\n  }\n\n  @override\n  void dispose() {\n    _appBarState.dispose();\n    _textController.dispose();\n    _floatingActionButton.dispose();\n    super.dispose();\n  }\n\n  void addKeyword(String keyword) {\n    final isContains = _keywordsNotifier.value.contains(keyword);\n    if (isContains) return;\n    final keywords = List<String>.from(_keywordsNotifier.value)..add(keyword);\n    _keywordsNotifier.value = keywords;\n  }\n\n  void _deleteKeyword(String keyword) {\n    final isContains = _keywordsNotifier.value.contains(keyword);\n    if (!isContains) return;\n    final keywords = List<String>.from(_keywordsNotifier.value)\n      ..remove(keyword);\n    _keywordsNotifier.value = keywords;\n  }\n\n  Widget? _buildLeading() {\n    if (_isEdit) {\n      return IconButton(\n        onPressed: _appBarState.value.editState?.onExit,\n        icon: Icon(Icons.close),\n      );\n    }\n    if (_isSearch) {\n      return IconButton(\n        onPressed: _handleExitSearching,\n        icon: Icon(Icons.arrow_back),\n      );\n    }\n    return widget.leading;\n  }\n\n  Widget _buildTitle(AppBarSearchState? startState) {\n    return _isSearch\n        ? TextField(\n            autofocus: true,\n            controller: _textController,\n            style: context.textTheme.titleLarge,\n            onChanged: (value) {\n              if (startState != null) {\n                startState.onSearch(value);\n              }\n            },\n            decoration: InputDecoration(hintText: appLocalizations.search),\n          )\n        : Text(\n            !_isEdit\n                ? widget.title!\n                : appLocalizations.selectedCountTitle(\n                    '${_appBarState.value.editState?.editCount ?? 0}',\n                  ),\n          );\n  }\n\n  List<Widget> _buildActions(bool hasSearch, List<Widget> actions) {\n    if (_isSearch) {\n      return genActions([\n        IconButton(onPressed: _handleClear, icon: Icon(Icons.close)),\n      ]);\n    }\n    return genActions([\n      if (hasSearch)\n        IconButton(\n          onPressed: () {\n            _updateSearchState((state) => state?.copyWith(query: ''));\n          },\n          icon: Icon(Icons.search),\n        ),\n      ...actions,\n    ]);\n  }\n\n  Widget _buildAppBarWrap(Widget child) {\n    final appBar = _isSearch ? _buildSearchingAppBarTheme(child) : child;\n    if (_isEdit || _isSearch) {\n      return SystemBackBlock(\n        child: CommonPopScope(\n          onPop: () {\n            if (_isEdit || _isSearch) {\n              _handleExitSearching();\n              _appBarState.value.editState?.onExit();\n              return false;\n            }\n            return true;\n          },\n          child: appBar,\n        ),\n      );\n    }\n    return appBar;\n  }\n\n  Widget _buildLoading() {\n    return Consumer(\n      builder: (_, ref, _) {\n        final loading = ref.watch(loadingProvider);\n        final isMobileView = ref.watch(isMobileViewProvider);\n        return loading && isMobileView\n            ? const LinearProgressIndicator()\n            : Container();\n      },\n    );\n  }\n\n  PreferredSizeWidget _buildAppBar() {\n    return PreferredSize(\n      preferredSize: const Size.fromHeight(kToolbarHeight),\n      child: Stack(\n        alignment: Alignment.bottomCenter,\n        children: [\n          widget.appBar ??\n              ValueListenableBuilder<AppBarState>(\n                valueListenable: _appBarState,\n                builder: (_, state, _) {\n                  return _buildAppBarWrap(\n                    AppBar(\n                      centerTitle: widget.centerTitle ?? false,\n                      leading: _buildLeading(),\n                      title: _buildTitle(state.searchState),\n                      actions: _buildActions(\n                        state.searchState != null,\n                        state.actions.isNotEmpty\n                            ? state.actions\n                            : widget.actions ?? [],\n                      ),\n                    ),\n                  );\n                },\n              ),\n          _buildLoading(),\n        ],\n      ),\n    );\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    assert(widget.appBar != null || widget.title != null);\n    final body = SafeArea(\n      child: Column(\n        crossAxisAlignment: CrossAxisAlignment.start,\n        children: [\n          ValueListenableBuilder(\n            valueListenable: _keywordsNotifier,\n            builder: (_, keywords, _) {\n              if (widget.onKeywordsUpdate != null) {\n                WidgetsBinding.instance.addPostFrameCallback((_) {\n                  widget.onKeywordsUpdate!(keywords);\n                });\n              }\n              if (keywords.isEmpty) {\n                return SizedBox();\n              }\n              return Padding(\n                padding: const EdgeInsets.symmetric(\n                  horizontal: 16,\n                  vertical: 16,\n                ),\n                child: Wrap(\n                  runSpacing: 8,\n                  spacing: 8,\n                  children: [\n                    for (final keyword in keywords)\n                      CommonChip(\n                        label: keyword,\n                        type: ChipType.delete,\n                        onPressed: () {\n                          _deleteKeyword(keyword);\n                        },\n                      ),\n                  ],\n                ),\n              );\n            },\n          ),\n          Expanded(child: widget.body),\n        ],\n      ),\n    );\n    return Scaffold(\n      appBar: _buildAppBar(),\n      body: body,\n      resizeToAvoidBottomInset: true,\n      backgroundColor: widget.backgroundColor,\n      floatingActionButton:\n          widget.floatingActionButton ??\n          ValueListenableBuilder<Widget?>(\n            valueListenable: _floatingActionButton,\n            builder: (_, value, _) {\n              return IntrinsicWidth(\n                child: IntrinsicHeight(\n                  child: FadeScaleBox(child: value ?? SizedBox()),\n                ),\n              );\n            },\n          ),\n    );\n  }\n}\n\nList<Widget> genActions(List<Widget> actions, {double? space}) {\n  return <Widget>[\n    ...actions.separated(SizedBox(width: space ?? 4)),\n    SizedBox(width: 8),\n  ];\n}\n"
  },
  {
    "path": "lib/widgets/scroll.dart",
    "content": "import 'dart:async';\n\nimport 'package:collection/collection.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:flutter/material.dart';\n\nclass CommonScrollBar extends StatelessWidget {\n  final ScrollController? controller;\n  final Widget child;\n  final bool trackVisibility;\n  final bool thumbVisibility;\n\n  const CommonScrollBar({\n    super.key,\n    required this.child,\n    required this.controller,\n    this.trackVisibility = false,\n    this.thumbVisibility = false,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    final hasController = controller != null;\n    return Scrollbar(\n      controller: controller,\n      thumbVisibility: hasController ? thumbVisibility : false,\n      trackVisibility: hasController ? trackVisibility : false,\n      thickness: 8,\n      radius: const Radius.circular(8),\n      interactive: hasController,\n      child: child,\n    );\n  }\n}\n\nclass ScrollToEndBox<T> extends StatefulWidget {\n  final ScrollController controller;\n  final List<T> dataSource;\n  final Widget child;\n  final bool enable;\n  final VoidCallback? onCancelToEnd;\n\n  const ScrollToEndBox({\n    super.key,\n    required this.child,\n    required this.controller,\n    required this.dataSource,\n    this.onCancelToEnd,\n    this.enable = true,\n  });\n\n  @override\n  State<ScrollToEndBox<T>> createState() => _ScrollToEndBoxState<T>();\n}\n\nclass _ScrollToEndBoxState<T> extends State<ScrollToEndBox<T>> {\n  final _equals = ListEquality<T>();\n  var _isFastToEnd = false;\n\n  Future<void> _handleTryToEnd() async {\n    final completer = Completer<void>();\n    WidgetsBinding.instance.addPostFrameCallback((_) async {\n      if (mounted && widget.controller.hasClients) {\n        final pos = widget.controller.position;\n        if (pos.pixels != pos.maxScrollExtent) {\n          await widget.controller.animateTo(\n            pos.maxScrollExtent,\n            duration: kThemeAnimationDuration,\n            curve: Curves.easeOut,\n          );\n        }\n      }\n      completer.complete();\n    });\n    return completer.future;\n  }\n\n  @override\n  void didUpdateWidget(ScrollToEndBox<T> oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    if (widget.enable && !oldWidget.enable) {\n      _isFastToEnd = true;\n      _handleTryToEnd().then((_) => _isFastToEnd = false);\n      return;\n    }\n    if (widget.enable && !_equals.equals(oldWidget.dataSource, widget.dataSource)) {\n      _handleTryToEnd();\n    }\n  }\n\n  @override\n  Widget build(BuildContext context) => NotificationListener<UserScrollNotification>(\n    onNotification: (n) {\n      if (!_isFastToEnd && widget.onCancelToEnd != null) {\n        widget.onCancelToEnd!();\n      }\n      return false;\n    },\n    child: widget.child,\n  );\n}\n\nclass CacheItemExtentListView extends StatefulWidget {\n  final NullableIndexedWidgetBuilder itemBuilder;\n  final int itemCount;\n  final String Function(int index) keyBuilder;\n  final double Function(int index) itemExtentBuilder;\n  final ScrollPhysics? physics;\n  final bool shrinkWrap;\n  final bool reverse;\n  final ScrollController controller;\n  final CacheTag tag;\n\n  const CacheItemExtentListView({\n    super.key,\n    this.physics,\n    this.reverse = false,\n    this.shrinkWrap = false,\n    required this.itemBuilder,\n    required this.controller,\n    required this.keyBuilder,\n    required this.itemCount,\n    required this.itemExtentBuilder,\n    required this.tag,\n  });\n\n  @override\n  State<CacheItemExtentListView> createState() => _CacheItemExtentListViewState();\n}\n\nclass _CacheItemExtentListViewState extends State<CacheItemExtentListView> {\n  @override\n  void initState() {\n    super.initState();\n    _updateCache();\n  }\n\n  void _updateCache() {\n    globalState.computeHeightMapCache[widget.tag]?.updateMaxLength(widget.itemCount);\n    globalState.computeHeightMapCache[widget.tag] ??= FixedMap(widget.itemCount);\n  }\n\n  @override\n  Widget build(BuildContext context) => ListView.builder(\n    itemBuilder: widget.itemBuilder,\n    itemCount: widget.itemCount,\n    physics: widget.physics,\n    reverse: widget.reverse,\n    shrinkWrap: widget.shrinkWrap,\n    controller: widget.controller,\n    itemExtentBuilder: (index, _) {\n      _updateCache();\n      return globalState.computeHeightMapCache[widget.tag]?.updateCacheValue(\n        widget.keyBuilder(index),\n        () => widget.itemExtentBuilder(index),\n      );\n    },\n  );\n}\n\nclass CacheItemExtentSliverReorderableList extends StatefulWidget {\n  final IndexedWidgetBuilder itemBuilder;\n  final int itemCount;\n  final String Function(int index) keyBuilder;\n  final double Function(int index) itemExtentBuilder;\n  final ReorderCallback onReorder;\n  final ReorderItemProxyDecorator? proxyDecorator;\n  final CacheTag tag;\n\n  const CacheItemExtentSliverReorderableList({\n    super.key,\n    required this.itemBuilder,\n    required this.keyBuilder,\n    required this.itemCount,\n    required this.itemExtentBuilder,\n    required this.onReorder,\n    this.proxyDecorator,\n    required this.tag,\n  });\n\n  @override\n  State<CacheItemExtentSliverReorderableList> createState() =>\n      _CacheItemExtentSliverReorderableListState();\n}\n\nclass _CacheItemExtentSliverReorderableListState\n    extends State<CacheItemExtentSliverReorderableList> {\n  @override\n  void initState() {\n    super.initState();\n    _updateCache();\n  }\n\n  void _updateCache() {\n    globalState.computeHeightMapCache[widget.tag]?.updateMaxLength(widget.itemCount);\n    globalState.computeHeightMapCache[widget.tag] ??= FixedMap(widget.itemCount);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    _updateCache();\n    return SliverReorderableList(\n      itemBuilder: widget.itemBuilder,\n      itemCount: widget.itemCount,\n      itemExtentBuilder: (index, _) => globalState.computeHeightMapCache[widget.tag]?.updateCacheValue(\n        widget.keyBuilder(index),\n        () => widget.itemExtentBuilder(index),\n      ),\n      onReorder: widget.onReorder,\n      proxyDecorator: widget.proxyDecorator,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/setting.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:flutter/material.dart';\n\nimport 'card.dart';\nimport 'text.dart';\n\nclass SettingInfoCard extends StatelessWidget {\n  final Info info;\n  final bool? isSelected;\n  final VoidCallback onPressed;\n\n  const SettingInfoCard(\n    this.info, {\n    super.key,\n    this.isSelected,\n    required this.onPressed,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return CommonCard(\n      isSelected: isSelected,\n      onPressed: onPressed,\n      child: Padding(\n        padding: const EdgeInsets.all(12),\n        child: Row(\n          mainAxisSize: MainAxisSize.min,\n          mainAxisAlignment: MainAxisAlignment.start,\n          children: [\n            Flexible(child: Icon(info.iconData)),\n            const SizedBox(width: 8),\n            Flexible(\n              child: Text(info.label, style: context.textTheme.bodyMedium),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n\nclass SettingTextCard extends StatelessWidget {\n  final String text;\n  final bool? isSelected;\n  final VoidCallback onPressed;\n\n  const SettingTextCard(\n    this.text, {\n    super.key,\n    this.isSelected,\n    required this.onPressed,\n  });\n\n  @override\n  Widget build(BuildContext context) {\n    return CommonCard(\n      onPressed: onPressed,\n      isSelected: isSelected,\n      child: Padding(\n        padding: const EdgeInsets.all(12),\n        child: EmojiText(text, style: context.textTheme.bodyMedium),\n      ),\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/sheet.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:bett_box/state.dart';\nimport 'package:flutter/material.dart';\n\nimport 'scaffold.dart';\nimport 'side_sheet.dart';\n\n@immutable\nclass SheetProps {\n  final double? maxWidth;\n  final double? maxHeight;\n  final bool isScrollControlled;\n  final bool useSafeArea;\n  final bool blur;\n\n  const SheetProps({\n    this.maxWidth,\n    this.maxHeight,\n    this.useSafeArea = true,\n    this.isScrollControlled = false,\n    this.blur = false,\n  });\n}\n\n@immutable\nclass ExtendProps {\n  final double? maxWidth;\n  final bool useSafeArea;\n  final bool blur;\n  final bool forceFull;\n\n  const ExtendProps({\n    this.maxWidth,\n    this.useSafeArea = true,\n    this.blur = false,\n    this.forceFull = false,\n  });\n}\n\nenum SheetType { page, bottomSheet, sideSheet }\n\ntypedef SheetBuilder = Widget Function(BuildContext context, SheetType type);\n\nFuture<T?> showSheet<T>({\n  required BuildContext context,\n  required SheetBuilder builder,\n  SheetProps props = const SheetProps(),\n}) {\n  final isMobile = globalState.appState.viewMode == ViewMode.mobile;\n  return switch (isMobile) {\n    true => showModalBottomSheet<T>(\n      context: context,\n      isScrollControlled: props.isScrollControlled,\n      builder: (_) {\n        return SafeArea(child: builder(context, SheetType.bottomSheet));\n      },\n      showDragHandle: false,\n      useSafeArea: props.useSafeArea,\n    ),\n    false => showModalSideSheet<T>(\n      useSafeArea: props.useSafeArea,\n      isScrollControlled: props.isScrollControlled,\n      context: context,\n      constraints: BoxConstraints(maxWidth: props.maxWidth ?? 360),\n      filter: props.blur ? commonFilter : null,\n      builder: (_) {\n        return builder(context, SheetType.sideSheet);\n      },\n    ),\n  };\n}\n\nFuture<T?> showExtend<T>(\n  BuildContext context, {\n  required SheetBuilder builder,\n  ExtendProps props = const ExtendProps(),\n}) {\n  final isMobile = globalState.appState.viewMode == ViewMode.mobile;\n  return switch (isMobile || props.forceFull) {\n    true => BaseNavigator.push(context, builder(context, SheetType.page)),\n    false => showModalSideSheet<T>(\n      useSafeArea: props.useSafeArea,\n      context: context,\n      constraints: BoxConstraints(maxWidth: props.maxWidth ?? 360),\n      filter: props.blur ? commonFilter : null,\n      builder: (context) {\n        return builder(context, SheetType.sideSheet);\n      },\n    ),\n  };\n}\n\nclass AdaptiveSheetScaffold extends StatefulWidget {\n  final SheetType type;\n  final Widget body;\n  final String title;\n  final List<Widget> actions;\n\n  const AdaptiveSheetScaffold({\n    super.key,\n    required this.type,\n    required this.body,\n    required this.title,\n    this.actions = const [],\n  });\n\n  @override\n  State<AdaptiveSheetScaffold> createState() => _AdaptiveSheetScaffoldState();\n}\n\nclass _AdaptiveSheetScaffoldState extends State<AdaptiveSheetScaffold> {\n  @override\n  Widget build(BuildContext context) {\n    final backgroundColor = context.colorScheme.surface;\n    final bottomSheet = widget.type == SheetType.bottomSheet;\n    final sideSheet = widget.type == SheetType.sideSheet;\n    final appBar = AppBar(\n      forceMaterialTransparency: bottomSheet ? true : false,\n      automaticallyImplyLeading: bottomSheet\n          ? false\n          : widget.actions.isEmpty && sideSheet\n          ? false\n          : true,\n      centerTitle: bottomSheet,\n      backgroundColor: backgroundColor,\n      title: Text(widget.title),\n      actions: genActions([\n        if (widget.actions.isEmpty && sideSheet) CloseButton(),\n        ...widget.actions,\n      ]),\n    );\n    if (bottomSheet) {\n      final handleSize = Size(32, 4);\n      return Container(\n        clipBehavior: Clip.hardEdge,\n        decoration: BoxDecoration(\n          color: backgroundColor,\n          borderRadius: BorderRadius.vertical(top: Radius.circular(28.0)),\n        ),\n        child: Column(\n          mainAxisSize: MainAxisSize.min,\n          children: [\n            Padding(\n              padding: EdgeInsets.only(top: 16),\n              child: Container(\n                alignment: Alignment.center,\n                height: handleSize.height,\n                width: handleSize.width,\n                decoration: BoxDecoration(\n                  borderRadius: BorderRadius.circular(handleSize.height / 2),\n                  color: context.colorScheme.onSurfaceVariant,\n                ),\n              ),\n            ),\n            appBar,\n            Flexible(flex: 1, child: widget.body),\n          ],\n        ),\n      );\n    }\n    return CommonScaffold(\n      appBar: appBar,\n      backgroundColor: backgroundColor,\n      body: widget.body,\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/side_sheet.dart",
    "content": "import 'dart:ui';\n\nimport 'package:bett_box/common/color.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/rendering.dart';\n\nconst Duration _bottomSheetEnterDuration = Duration(milliseconds: 300);\nconst Duration _bottomSheetExitDuration = Duration(milliseconds: 200);\nconst Curve _modalBottomSheetCurve = Easing.standardDecelerate;\nconst double _defaultScrollControlDisabledMaxHeightRatio = 9.0 / 16.0;\n\nclass SideSheet extends StatefulWidget {\n  const SideSheet({\n    super.key,\n    this.animationController,\n    this.enableDrag = true,\n    this.showDragHandle,\n    this.dragHandleColor,\n    this.dragHandleSize,\n    this.onDragStart,\n    this.onDragEnd,\n    this.backgroundColor,\n    this.shadowColor,\n    this.elevation,\n    this.shape,\n    this.clipBehavior,\n    this.constraints,\n    required this.onClosing,\n    required this.builder,\n  }) : assert(elevation == null || elevation >= 0.0);\n\n  final AnimationController? animationController;\n\n  final VoidCallback onClosing;\n\n  final WidgetBuilder builder;\n\n  final bool enableDrag;\n\n  final bool? showDragHandle;\n\n  final Color? dragHandleColor;\n\n  final Size? dragHandleSize;\n\n  final BottomSheetDragStartHandler? onDragStart;\n\n  final BottomSheetDragEndHandler? onDragEnd;\n\n  final Color? backgroundColor;\n\n  final Color? shadowColor;\n\n  final double? elevation;\n\n  final ShapeBorder? shape;\n\n  final Clip? clipBehavior;\n\n  final BoxConstraints? constraints;\n\n  @override\n  State<SideSheet> createState() => _SideSheetState();\n\n  static AnimationController createAnimationController(TickerProvider vsync) {\n    return AnimationController(\n      duration: _bottomSheetEnterDuration,\n      reverseDuration: _bottomSheetExitDuration,\n      debugLabel: 'SideSheet',\n      vsync: vsync,\n    );\n  }\n}\n\nclass _SideSheetState extends State<SideSheet> {\n  final GlobalKey _childKey = GlobalKey(debugLabel: 'SideSheet child');\n\n  @override\n  Widget build(BuildContext context) {\n    final colorScheme = Theme.of(context).colorScheme;\n    final Color color = widget.backgroundColor ?? colorScheme.surface;\n    final Color surfaceTintColor = colorScheme.surfaceTint;\n    final Color shadowColor = widget.shadowColor ?? Colors.transparent;\n    final double elevation = widget.elevation ?? 0;\n    final ShapeBorder shape =\n        widget.shape ??\n        RoundedRectangleBorder(borderRadius: BorderRadius.circular(0));\n\n    final BoxConstraints constraints =\n        widget.constraints ??\n        const BoxConstraints(maxWidth: 320, minWidth: 320);\n\n    final Clip clipBehavior = widget.clipBehavior ?? Clip.none;\n\n    Widget sideSheet = Material(\n      key: _childKey,\n      color: color,\n      elevation: elevation,\n      surfaceTintColor: surfaceTintColor,\n      shadowColor: shadowColor,\n      shape: shape,\n      clipBehavior: clipBehavior,\n      child: widget.builder(context),\n    );\n\n    return ConstrainedBox(constraints: constraints, child: sideSheet);\n  }\n}\n\ntypedef _SizeChangeCallback<Size> = void Function(Size);\n\nclass _SideSheetLayoutWithSizeListener extends SingleChildRenderObjectWidget {\n  const _SideSheetLayoutWithSizeListener({\n    required this.onChildSizeChanged,\n    required this.animationValue,\n    required this.isScrollControlled,\n    required this.scrollControlDisabledMaxHeightRatio,\n    super.child,\n  });\n\n  final _SizeChangeCallback<Size> onChildSizeChanged;\n  final double animationValue;\n  final bool isScrollControlled;\n  final double scrollControlDisabledMaxHeightRatio;\n\n  @override\n  _RenderSideSheetLayoutWithSizeListener createRenderObject(\n    BuildContext context,\n  ) {\n    return _RenderSideSheetLayoutWithSizeListener(\n      onChildSizeChanged: onChildSizeChanged,\n      animationValue: animationValue,\n      isScrollControlled: isScrollControlled,\n      scrollControlDisabledMaxHeightRatio: scrollControlDisabledMaxHeightRatio,\n    );\n  }\n\n  @override\n  void updateRenderObject(\n    BuildContext context,\n    _RenderSideSheetLayoutWithSizeListener renderObject,\n  ) {\n    renderObject.onChildSizeChanged = onChildSizeChanged;\n    renderObject.animationValue = animationValue;\n    renderObject.isScrollControlled = isScrollControlled;\n    renderObject.scrollControlDisabledMaxHeightRatio =\n        scrollControlDisabledMaxHeightRatio;\n  }\n}\n\nclass _RenderSideSheetLayoutWithSizeListener extends RenderShiftedBox {\n  _RenderSideSheetLayoutWithSizeListener({\n    RenderBox? child,\n    required _SizeChangeCallback<Size> onChildSizeChanged,\n    required double animationValue,\n    required bool isScrollControlled,\n    required double scrollControlDisabledMaxHeightRatio,\n  }) : _onChildSizeChanged = onChildSizeChanged,\n       _animationValue = animationValue,\n       _isScrollControlled = isScrollControlled,\n       _scrollControlDisabledMaxHeightRatio =\n           scrollControlDisabledMaxHeightRatio,\n       super(child);\n\n  Size _lastSize = Size.zero;\n\n  _SizeChangeCallback<Size> get onChildSizeChanged => _onChildSizeChanged;\n  _SizeChangeCallback<Size> _onChildSizeChanged;\n\n  set onChildSizeChanged(_SizeChangeCallback<Size> newCallback) {\n    if (_onChildSizeChanged == newCallback) {\n      return;\n    }\n\n    _onChildSizeChanged = newCallback;\n    markNeedsLayout();\n  }\n\n  double get animationValue => _animationValue;\n  double _animationValue;\n\n  set animationValue(double newValue) {\n    if (_animationValue == newValue) {\n      return;\n    }\n\n    _animationValue = newValue;\n    markNeedsLayout();\n  }\n\n  bool get isScrollControlled => _isScrollControlled;\n  bool _isScrollControlled;\n\n  set isScrollControlled(bool newValue) {\n    if (_isScrollControlled == newValue) {\n      return;\n    }\n\n    _isScrollControlled = newValue;\n    markNeedsLayout();\n  }\n\n  double get scrollControlDisabledMaxHeightRatio =>\n      _scrollControlDisabledMaxHeightRatio;\n  double _scrollControlDisabledMaxHeightRatio;\n\n  set scrollControlDisabledMaxHeightRatio(double newValue) {\n    if (_scrollControlDisabledMaxHeightRatio == newValue) {\n      return;\n    }\n\n    _scrollControlDisabledMaxHeightRatio = newValue;\n    markNeedsLayout();\n  }\n\n  Size _getSize(BoxConstraints constraints) {\n    return constraints.constrain(constraints.biggest);\n  }\n\n  @override\n  double computeMinIntrinsicWidth(double height) {\n    final double width = _getSize(\n      BoxConstraints.tightForFinite(height: height),\n    ).width;\n    if (width.isFinite) {\n      return width;\n    }\n    return 0.0;\n  }\n\n  @override\n  double computeMaxIntrinsicWidth(double height) {\n    final double width = _getSize(\n      BoxConstraints.tightForFinite(height: height),\n    ).width;\n    if (width.isFinite) {\n      return width;\n    }\n    return 0.0;\n  }\n\n  @override\n  double computeMinIntrinsicHeight(double width) {\n    final double height = _getSize(\n      BoxConstraints.tightForFinite(width: width),\n    ).height;\n    if (height.isFinite) {\n      return height;\n    }\n    return 0.0;\n  }\n\n  @override\n  double computeMaxIntrinsicHeight(double width) {\n    final double height = _getSize(\n      BoxConstraints.tightForFinite(width: width),\n    ).height;\n    if (height.isFinite) {\n      return height;\n    }\n    return 0.0;\n  }\n\n  @override\n  Size computeDryLayout(BoxConstraints constraints) {\n    return _getSize(constraints);\n  }\n\n  BoxConstraints _getConstraintsForChild(BoxConstraints constraints) {\n    return BoxConstraints(maxHeight: constraints.maxHeight);\n  }\n\n  Offset _getPositionForChild(Size size, Size childSize) {\n    return Offset(size.width - childSize.width * animationValue, 0.0);\n  }\n\n  @override\n  void performLayout() {\n    size = _getSize(constraints);\n    if (child != null) {\n      final BoxConstraints childConstraints = _getConstraintsForChild(\n        constraints,\n      );\n      assert(childConstraints.debugAssertIsValid(isAppliedConstraint: true));\n      child!.layout(\n        childConstraints,\n        parentUsesSize: !childConstraints.isTight,\n      );\n      final BoxParentData childParentData = child!.parentData! as BoxParentData;\n      childParentData.offset = _getPositionForChild(\n        size,\n        childConstraints.isTight ? childConstraints.smallest : child!.size,\n      );\n      final Size childSize = childConstraints.isTight\n          ? childConstraints.smallest\n          : child!.size;\n\n      if (_lastSize != childSize) {\n        _lastSize = childSize;\n        _onChildSizeChanged.call(_lastSize);\n      }\n    }\n  }\n}\n\nclass _ModalSideSheet<T> extends StatefulWidget {\n  const _ModalSideSheet({\n    super.key,\n    required this.route,\n    this.backgroundColor,\n    this.elevation,\n    this.shape,\n    this.clipBehavior,\n    this.constraints,\n    this.isScrollControlled = false,\n    this.scrollControlDisabledMaxHeightRatio =\n        _defaultScrollControlDisabledMaxHeightRatio,\n    this.enableDrag = true,\n    this.showDragHandle = false,\n  });\n\n  final ModalSideSheetRoute<T> route;\n  final bool isScrollControlled;\n  final double scrollControlDisabledMaxHeightRatio;\n  final Color? backgroundColor;\n  final double? elevation;\n  final ShapeBorder? shape;\n  final Clip? clipBehavior;\n  final BoxConstraints? constraints;\n  final bool enableDrag;\n  final bool showDragHandle;\n\n  @override\n  _ModalSideSheetState<T> createState() => _ModalSideSheetState<T>();\n}\n\nclass _ModalSideSheetState<T> extends State<_ModalSideSheet<T>> {\n  ParametricCurve<double> animationCurve = _modalBottomSheetCurve;\n\n  String _getRouteLabel(MaterialLocalizations localizations) {\n    switch (Theme.of(context).platform) {\n      case TargetPlatform.iOS:\n      case TargetPlatform.macOS:\n        return '';\n      case TargetPlatform.android:\n      case TargetPlatform.fuchsia:\n      case TargetPlatform.linux:\n      case TargetPlatform.windows:\n        return localizations.dialogLabel;\n    }\n  }\n\n  EdgeInsets _getNewClipDetails(Size topLayerSize) {\n    return EdgeInsets.fromLTRB(0, 0, 0, topLayerSize.height);\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    assert(debugCheckHasMediaQuery(context));\n    assert(debugCheckHasMaterialLocalizations(context));\n    final MaterialLocalizations localizations = MaterialLocalizations.of(\n      context,\n    );\n    final String routeLabel = _getRouteLabel(localizations);\n\n    return AnimatedBuilder(\n      animation: widget.route.animation!,\n      child: SideSheet(\n        animationController: widget.route._animationController,\n        onClosing: () {\n          if (widget.route.isCurrent) {\n            Navigator.pop(context);\n          }\n        },\n        builder: widget.route.builder,\n        backgroundColor: widget.backgroundColor,\n        elevation: widget.elevation,\n        shape: widget.shape,\n        clipBehavior: widget.clipBehavior,\n        constraints: widget.constraints,\n        enableDrag: widget.enableDrag,\n        showDragHandle: widget.showDragHandle,\n      ),\n      builder: (BuildContext context, Widget? child) {\n        final double animationValue = animationCurve.transform(\n          widget.route.animation!.value,\n        );\n        return Semantics(\n          scopesRoute: true,\n          namesRoute: true,\n          label: routeLabel,\n          explicitChildNodes: true,\n          child: ClipRect(\n            child: _SideSheetLayoutWithSizeListener(\n              onChildSizeChanged: (Size size) {\n                widget.route._didChangeBarrierSemanticsClip(\n                  _getNewClipDetails(size),\n                );\n              },\n              animationValue: animationValue,\n              isScrollControlled: widget.isScrollControlled,\n              scrollControlDisabledMaxHeightRatio:\n                  widget.scrollControlDisabledMaxHeightRatio,\n              child: child,\n            ),\n          ),\n        );\n      },\n    );\n  }\n}\n\nclass ModalSideSheetRoute<T> extends PopupRoute<T> {\n  ModalSideSheetRoute({\n    required this.builder,\n    this.capturedThemes,\n    this.barrierLabel,\n    this.barrierOnTapHint,\n    this.backgroundColor,\n    this.elevation,\n    this.shape,\n    this.clipBehavior,\n    this.constraints,\n    this.modalBarrierColor,\n    this.isDismissible = true,\n    this.isScrollControlled = false,\n    this.scrollControlDisabledMaxHeightRatio =\n        _defaultScrollControlDisabledMaxHeightRatio,\n    super.settings,\n    this.transitionAnimationController,\n    this.anchorPoint,\n    this.useSafeArea = false,\n    super.filter,\n  });\n\n  final WidgetBuilder builder;\n\n  final CapturedThemes? capturedThemes;\n\n  final bool isScrollControlled;\n\n  final double scrollControlDisabledMaxHeightRatio;\n\n  final Color? backgroundColor;\n\n  final double? elevation;\n\n  final ShapeBorder? shape;\n\n  final Clip? clipBehavior;\n\n  final BoxConstraints? constraints;\n\n  final Color? modalBarrierColor;\n\n  final bool isDismissible;\n\n  final AnimationController? transitionAnimationController;\n\n  final Offset? anchorPoint;\n\n  final bool useSafeArea;\n\n  final String? barrierOnTapHint;\n\n  final ValueNotifier<EdgeInsets> _clipDetailsNotifier =\n      ValueNotifier<EdgeInsets>(EdgeInsets.zero);\n\n  @override\n  void dispose() {\n    _clipDetailsNotifier.dispose();\n    super.dispose();\n  }\n\n  bool _didChangeBarrierSemanticsClip(EdgeInsets newClipDetails) {\n    if (_clipDetailsNotifier.value == newClipDetails) {\n      return false;\n    }\n    _clipDetailsNotifier.value = newClipDetails;\n    return true;\n  }\n\n  @override\n  Duration get transitionDuration => _bottomSheetEnterDuration;\n\n  @override\n  Duration get reverseTransitionDuration => _bottomSheetExitDuration;\n\n  @override\n  bool get barrierDismissible => isDismissible;\n\n  @override\n  final String? barrierLabel;\n\n  @override\n  Color get barrierColor => modalBarrierColor ?? Colors.black54;\n\n  AnimationController? _animationController;\n\n  @override\n  AnimationController createAnimationController() {\n    assert(_animationController == null);\n    if (transitionAnimationController != null) {\n      _animationController = transitionAnimationController;\n      willDisposeAnimationController = false;\n    } else {\n      _animationController = SideSheet.createAnimationController(navigator!);\n    }\n    return _animationController!;\n  }\n\n  @override\n  Widget buildPage(\n    BuildContext context,\n    Animation<double> animation,\n    Animation<double> secondaryAnimation,\n  ) {\n    final Widget content = DisplayFeatureSubScreen(\n      anchorPoint: anchorPoint,\n      child: Builder(\n        builder: (BuildContext context) {\n          final colorScheme = Theme.of(context).colorScheme;\n          return _ModalSideSheet<T>(\n            route: this,\n            backgroundColor: backgroundColor ?? colorScheme.surface,\n            elevation: elevation ?? 0,\n            shape: shape,\n            clipBehavior: clipBehavior,\n            constraints: constraints,\n            isScrollControlled: isScrollControlled,\n            scrollControlDisabledMaxHeightRatio:\n                scrollControlDisabledMaxHeightRatio,\n          );\n        },\n      ),\n    );\n\n    final Widget sideSheet = content;\n\n    return capturedThemes?.wrap(sideSheet) ?? sideSheet;\n  }\n\n  @override\n  Widget buildModalBarrier() {\n    if (barrierColor.a != 0 && !offstage) {\n      assert(barrierColor != barrierColor.opacity0);\n      final Animation<Color?> color = animation!.drive(\n        ColorTween(\n          begin: barrierColor.opacity0,\n          end: barrierColor,\n        ).chain(CurveTween(curve: barrierCurve)),\n      );\n      return AnimatedModalBarrier(\n        color: color,\n        dismissible: barrierDismissible,\n        semanticsLabel: barrierLabel,\n        barrierSemanticsDismissible: semanticsDismissible,\n        clipDetailsNotifier: _clipDetailsNotifier,\n        semanticsOnTapHint: barrierOnTapHint,\n      );\n    } else {\n      return ModalBarrier(\n        dismissible: barrierDismissible,\n        semanticsLabel: barrierLabel,\n        barrierSemanticsDismissible: semanticsDismissible,\n        clipDetailsNotifier: _clipDetailsNotifier,\n        semanticsOnTapHint: barrierOnTapHint,\n      );\n    }\n  }\n}\n\nFuture<T?> showModalSideSheet<T>({\n  required BuildContext context,\n  required WidgetBuilder builder,\n  Color? backgroundColor,\n  String? barrierLabel,\n  double? elevation,\n  ShapeBorder? shape,\n  Clip? clipBehavior,\n  BoxConstraints? constraints,\n  Color? barrierColor,\n  bool isScrollControlled = false,\n  double scrollControlDisabledMaxHeightRatio =\n      _defaultScrollControlDisabledMaxHeightRatio,\n  bool useRootNavigator = false,\n  bool isDismissible = true,\n  bool useSafeArea = false,\n  RouteSettings? routeSettings,\n  AnimationController? transitionAnimationController,\n  Offset? anchorPoint,\n  ImageFilter? filter,\n}) {\n  assert(debugCheckHasMediaQuery(context));\n  assert(debugCheckHasMaterialLocalizations(context));\n\n  final NavigatorState navigator = Navigator.of(\n    context,\n    rootNavigator: useRootNavigator,\n  );\n  final MaterialLocalizations localizations = MaterialLocalizations.of(context);\n  return navigator.push(\n    ModalSideSheetRoute<T>(\n      builder: builder,\n      filter: filter,\n      capturedThemes: InheritedTheme.capture(\n        from: context,\n        to: navigator.context,\n      ),\n      isScrollControlled: isScrollControlled,\n      scrollControlDisabledMaxHeightRatio: scrollControlDisabledMaxHeightRatio,\n      barrierLabel: barrierLabel ?? localizations.scrimLabel,\n      barrierOnTapHint: localizations.scrimOnTapHint(\n        localizations.bottomSheetLabel,\n      ),\n      backgroundColor: backgroundColor,\n      elevation: elevation,\n      shape: shape,\n      clipBehavior: clipBehavior,\n      constraints: constraints,\n      isDismissible: isDismissible,\n      modalBarrierColor:\n          barrierColor ?? Theme.of(context).bottomSheetTheme.modalBarrierColor,\n      settings: routeSettings,\n      transitionAnimationController: transitionAnimationController,\n      anchorPoint: anchorPoint,\n      useSafeArea: useSafeArea,\n    ),\n  );\n}\n\n// class ModalAppBar extends StatelessWidget {\n//   final String title;\n//\n//   const ModalAppBar({\n//     super.key,\n//     required this.title,\n//   });\n//\n//   @override\n//   Widget build(BuildContext context) {\n//     return AppBar(\n//       automaticallyImplyLeading: false,\n//       title: Text(title),\n//       centerTitle: false,\n//       actions: const [\n//         SizedBox(\n//           height: kToolbarHeight,\n//           width: kToolbarHeight,\n//           child: CloseButton(),\n//         )\n//       ],\n//     );\n//   }\n// }\n"
  },
  {
    "path": "lib/widgets/subscription_info_view.dart",
    "content": "import 'package:bett_box/common/common.dart';\nimport 'package:bett_box/models/models.dart';\nimport 'package:flutter/material.dart';\n\nclass SubscriptionInfoView extends StatelessWidget {\n  final SubscriptionInfo? subscriptionInfo;\n\n  const SubscriptionInfoView({super.key, this.subscriptionInfo});\n\n  @override\n  Widget build(BuildContext context) {\n    if (subscriptionInfo == null) {\n      return Container();\n    }\n\n    final use = subscriptionInfo!.upload + subscriptionInfo!.download;\n    final total = subscriptionInfo!.total;\n\n    // No traffic info\n    if (use == 0 && total == 0) {\n      return Container();\n    }\n\n    // Show progress bar\n    final progress = total > 0 ? use / total : 0.0;\n\n    return Column(\n      crossAxisAlignment: CrossAxisAlignment.start,\n      children: [\n        LinearProgressIndicator(\n          minHeight: 6,\n          value: progress,\n          backgroundColor: context.colorScheme.primary.opacity15,\n        ),\n        const SizedBox(height: 8),\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/super_grid.dart",
    "content": "import 'dart:async';\nimport 'dart:math';\n\nimport 'package:defer_pointer/defer_pointer.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:bett_box/widgets/activate_box.dart';\nimport 'package:bett_box/widgets/card.dart';\nimport 'package:bett_box/widgets/grid.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/physics.dart';\n\ntypedef VoidCallback = void Function();\n\nclass SuperGrid extends StatefulWidget {\n  final List<GridItem> children;\n  final double mainAxisSpacing;\n  final double crossAxisSpacing;\n  final int crossAxisCount;\n  final VoidCallback? onUpdate;\n\n  const SuperGrid({\n    super.key,\n    required this.children,\n    this.crossAxisCount = 1,\n    this.mainAxisSpacing = 0,\n    this.crossAxisSpacing = 0,\n    this.onUpdate,\n  });\n\n  @override\n  State<SuperGrid> createState() => SuperGridState();\n}\n\nclass SuperGridState extends State<SuperGrid> with TickerProviderStateMixin {\n  final ValueNotifier<List<GridItem>> _childrenNotifier = ValueNotifier([]);\n  List<GridItem> children = [];\n\n  int get length => _childrenNotifier.value.length;\n  List<int> _tempIndexList = [];\n  List<BuildContext?> _itemContexts = [];\n  Size _containerSize = Size.zero;\n  int _targetIndex = -1;\n  Offset _targetOffset = Offset.zero;\n  List<Size> _sizes = [];\n  List<Offset> _offsets = [];\n  Offset _parentOffset = Offset.zero;\n  EdgeDraggingAutoScroller? _edgeDraggingAutoScroller;\n  Map<int, Tween<Offset>> _transformTweenMap = {};\n\n  final ValueNotifier<bool> _animating = ValueNotifier(false);\n\n  final _dragWidgetSizeNotifier = ValueNotifier(Size.zero);\n\n  final _dragIndexNotifier = ValueNotifier(-1);\n\n  late AnimationController _transformController;\n\n  Completer? _transformCompleter;\n\n  Map<int, Animation<Offset>> _transformAnimationMap = {};\n\n  late AnimationController _fakeDragWidgetController;\n  Animation<Offset>? _fakeDragWidgetAnimation;\n\n  late AnimationController _shakeController;\n  late Animation<double> _shakeAnimation;\n  Rect _dragRect = Rect.zero;\n  Scrollable? _scrollable;\n\n  int get crossCount => widget.crossAxisCount;\n\n  void _onChildrenChange() {\n    _tempIndexList = List.generate(length, (index) => index);\n    _itemContexts = List.filled(length, null);\n  }\n\n  void _preTransformState() {\n    _sizes = _itemContexts.map((item) => item!.size!).toList();\n    _parentOffset = (context.findRenderObject() as RenderBox).localToGlobal(\n      Offset.zero,\n    );\n    _offsets = _itemContexts\n        .map(\n          (item) =>\n              (item!.findRenderObject() as RenderBox).localToGlobal(\n                Offset.zero,\n              ) -\n              _parentOffset,\n        )\n        .toList();\n    _containerSize = context.size!;\n  }\n\n  void _initState() {\n    _transformController.value = 0;\n    _sizes = List.generate(length, (index) => Size.zero);\n    _offsets = [];\n    _transformTweenMap.clear();\n    _transformAnimationMap.clear();\n    _containerSize = Size.zero;\n    _dragIndexNotifier.value = -1;\n    _dragWidgetSizeNotifier.value = Size.zero;\n    _targetOffset = Offset.zero;\n    _parentOffset = Offset.zero;\n    _dragRect = Rect.zero;\n    _targetIndex = -1;\n  }\n\n  @override\n  void initState() {\n    super.initState();\n    _childrenNotifier.addListener(() {\n      children = _childrenNotifier.value;\n      if (widget.onUpdate != null) {\n        widget.onUpdate!();\n      }\n    });\n\n    _childrenNotifier.value = widget.children;\n\n    _fakeDragWidgetController = AnimationController.unbounded(\n      vsync: this,\n      duration: commonDuration,\n    );\n\n    _shakeController = AnimationController(\n      vsync: this,\n      duration: Duration(milliseconds: 120),\n    );\n\n    _shakeAnimation = Tween<double>(begin: -0.012, end: 0.012).animate(\n      CurvedAnimation(parent: _shakeController, curve: Curves.easeInOut),\n    );\n\n    _transformController = AnimationController(\n      vsync: this,\n      duration: commonDuration,\n    );\n\n    _initState();\n  }\n\n  void handleAdd(GridItem gridItem) {\n    _childrenNotifier.value = List.from(_childrenNotifier.value)..add(gridItem);\n  }\n\n  @override\n  void didChangeDependencies() {\n    super.didChangeDependencies();\n\n    final scrollable = context.findAncestorWidgetOfExactType<Scrollable>();\n    if (scrollable == null) {\n      return;\n    }\n    if (_scrollable != scrollable) {\n      _edgeDraggingAutoScroller = EdgeDraggingAutoScroller(\n        Scrollable.of(context),\n        onScrollViewScrolled: () {\n          _edgeDraggingAutoScroller?.startAutoScrollIfNecessary(_dragRect);\n        },\n        velocityScalar: 40,\n      );\n    }\n  }\n\n  Future _transform() async {\n    List<Offset> layoutOffsets = [Offset(_containerSize.width, 0)];\n    final List<Offset> nextOffsets = [];\n\n    for (final index in _tempIndexList) {\n      final size = _sizes[index];\n      final offset = _getNextOffset(layoutOffsets, size);\n      final layoutOffset = Offset(\n        min(\n          offset.dx + size.width + widget.crossAxisSpacing,\n          _containerSize.width,\n        ),\n        min(\n          offset.dy + size.height + widget.mainAxisSpacing,\n          _containerSize.height,\n        ),\n      );\n      final startLayoutOffsetX = offset.dx;\n      final endLayoutOffsetX = layoutOffset.dx;\n      nextOffsets.add(offset);\n\n      final startIndex = layoutOffsets.indexWhere(\n        (i) => i.dx >= startLayoutOffsetX,\n      );\n      final endIndex = layoutOffsets.indexWhere(\n        (i) => i.dx >= endLayoutOffsetX,\n      );\n      final endOffset = layoutOffsets[endIndex];\n\n      if (startIndex != endIndex) {\n        final startOffset = layoutOffsets[startIndex];\n        if (startOffset.dx != startLayoutOffsetX) {\n          layoutOffsets[startIndex] = Offset(\n            startLayoutOffsetX,\n            startOffset.dy,\n          );\n        }\n      }\n      if (endOffset.dx == endLayoutOffsetX) {\n        layoutOffsets[endIndex] = layoutOffset;\n      } else {\n        layoutOffsets.insert(endIndex, layoutOffset);\n      }\n      layoutOffsets.removeRange(min(startIndex + 1, endIndex), endIndex);\n    }\n\n    final Map<int, Tween<Offset>> transformTweenMap = {};\n\n    for (final index in _tempIndexList) {\n      final nextIndex = _tempIndexList.indexWhere((i) => i == index);\n      transformTweenMap[index] = Tween(\n        begin: _transformTweenMap[index]?.begin ?? Offset.zero,\n        end: nextOffsets[nextIndex] - _offsets[index],\n      );\n    }\n\n    _transformTweenMap = transformTweenMap;\n\n    _transformAnimationMap = transformTweenMap.map((key, value) {\n      final preAnimationValue = _transformAnimationMap[key]?.value;\n      return MapEntry(\n        key,\n        Tween(\n          begin: preAnimationValue ?? Offset.zero,\n          end: value.end,\n        ).animate(_transformController),\n      );\n    });\n\n    if (_targetIndex != -1) {\n      _targetOffset = nextOffsets[_targetIndex];\n    }\n    return _transformController.forward(from: 0);\n  }\n\n  void _handleDragStarted(int index) {\n    _initState();\n    _preTransformState();\n    _dragIndexNotifier.value = index;\n    _dragWidgetSizeNotifier.value = _sizes[index];\n    _targetIndex = index;\n    _targetOffset = _offsets[index];\n    _dragRect = Rect.fromLTWH(\n      _targetOffset.dx + _parentOffset.dx,\n      _targetOffset.dy + _parentOffset.dy,\n      _sizes[index].width,\n      _sizes[index].height,\n    );\n  }\n\n  Future<void> _handleDragEnd(DraggableDetails details) async {\n    final children = List<GridItem>.from(_childrenNotifier.value);\n    children.insert(_targetIndex, children.removeAt(_dragIndexNotifier.value));\n    this.children = children;\n    debouncer.cancel(FunctionTag.handleWill);\n    if (_targetIndex == -1) {\n      return;\n    }\n    const spring = SpringDescription(mass: 1, stiffness: 100, damping: 10);\n    final simulation = SpringSimulation(spring, 0, 1, 0);\n    _fakeDragWidgetAnimation = Tween(\n      begin: details.offset - _parentOffset,\n      end: _targetOffset,\n    ).animate(_fakeDragWidgetController);\n    _animating.value = true;\n\n    _transformCompleter = Completer();\n    final animateWith = _fakeDragWidgetController.animateWith(simulation);\n    _transformCompleter?.complete(animateWith);\n    await animateWith;\n    _animating.value = false;\n    _fakeDragWidgetAnimation = null;\n    _transformTweenMap.clear();\n    _transformAnimationMap.clear();\n    _childrenNotifier.value = children;\n    _initState();\n  }\n\n  void _handleDragUpdate(DragUpdateDetails details) {\n    _dragRect = _dragRect.translate(0, details.delta.dy);\n    _edgeDraggingAutoScroller?.startAutoScrollIfNecessary(_dragRect);\n  }\n\n  Future<void> _handleWill(int index) async {\n    final dragIndex = _dragIndexNotifier.value;\n    if (dragIndex < 0 || dragIndex > _offsets.length - 1) {\n      return;\n    }\n    final targetIndex = _tempIndexList.indexWhere((i) => i == index);\n    if (_targetIndex == targetIndex) {\n      return;\n    }\n    _tempIndexList = List.generate(length, (i) {\n      if (i == targetIndex) return _dragIndexNotifier.value;\n      if (_targetIndex > targetIndex && i > targetIndex && i <= _targetIndex) {\n        return _tempIndexList[i - 1];\n      } else if (_targetIndex < targetIndex &&\n          i >= _targetIndex &&\n          i < targetIndex) {\n        return _tempIndexList[i + 1];\n      }\n      return _tempIndexList[i];\n    }).toList();\n\n    _targetIndex = targetIndex;\n\n    await _transform();\n  }\n\n  Future<void> _handleDelete(int index) async {\n    _preTransformState();\n    final indexWhere = _tempIndexList.indexWhere((i) => i == index);\n    _tempIndexList.removeAt(indexWhere);\n    await _transform();\n    final children = List<GridItem>.from(_childrenNotifier.value);\n    children.removeAt(index);\n    _childrenNotifier.value = children;\n    _initState();\n  }\n\n  Widget _buildTransform(Widget rawChild, int index) {\n    return ValueListenableBuilder(\n      valueListenable: _animating,\n      builder: (_, animating, child) {\n        if (animating && _dragIndexNotifier.value == index) {\n          return _buildSizeBox(Container(), index);\n        }\n        return child!;\n      },\n      child: AnimatedBuilder(\n        builder: (_, child) {\n          return Transform.translate(\n            offset: _transformAnimationMap[index]?.value ?? Offset.zero,\n            child: child,\n          );\n        },\n        animation: _transformController.view,\n        child: rawChild,\n      ),\n    );\n  }\n\n  Offset _getNextOffset(List<Offset> offsets, Size size) {\n    final length = offsets.length;\n    Offset nextOffset = Offset(0, double.infinity);\n    for (int i = 0; i < length; i++) {\n      final offset = offsets[i];\n      if (offset.dy.moreOrEqual(nextOffset.dy)) {\n        continue;\n      }\n      double offsetX = 0;\n      double span = 0;\n      for (\n        int j = 0;\n        span < size.width &&\n            j < length &&\n            _containerSize.width.moreOrEqual(offsetX + size.width);\n        j++\n      ) {\n        final tempOffset = offsets[j];\n        if (offset.dy.moreOrEqual(tempOffset.dy)) {\n          span = tempOffset.dx - offsetX;\n          if (span.moreOrEqual(size.width)) {\n            nextOffset = Offset(offsetX, offset.dy);\n          }\n        } else {\n          offsetX = tempOffset.dx;\n          span = 0;\n        }\n      }\n    }\n    return nextOffset;\n  }\n\n  Widget _buildSizeBox(Widget child, int index) {\n    return ValueListenableBuilder(\n      valueListenable: _dragWidgetSizeNotifier,\n      builder: (_, size, child) {\n        return SizedBox.fromSize(size: size, child: child!);\n      },\n      child: child,\n    );\n  }\n\n  Widget _buildInactivate(Widget child) {\n    return ValueListenableBuilder(\n      valueListenable: _animating,\n      builder: (_, animating, child) {\n        if (animating) {\n          return ActivateBox(child: child!);\n        } else {\n          return child!;\n        }\n      },\n      child: child,\n    );\n  }\n\n  Widget _buildShake(Widget child) {\n    final random = 0.7 + Random().nextDouble() * 0.3;\n    _shakeController.stop();\n    _shakeController.repeat(reverse: true);\n    return AnimatedBuilder(\n      animation: _shakeAnimation,\n      builder: (_, child) {\n        return Transform.rotate(\n          angle: _shakeAnimation.value * random,\n          child: child!,\n        );\n      },\n      child: child,\n    );\n  }\n\n  Widget _buildDraggable({\n    required Widget childWhenDragging,\n    required Widget feedback,\n    required Widget item,\n    required int index,\n    bool isDeletable = true,\n  }) {\n    final target = DragTarget<int>(\n      builder: (_, _, _) {\n        return AbsorbPointer(child: item);\n      },\n      onWillAcceptWithDetails: (_) {\n        debouncer.call(FunctionTag.handleWill, _handleWill, args: [index]);\n        return false;\n      },\n    );\n    final shakeTarget = ValueListenableBuilder(\n      valueListenable: _animating,\n      builder: (_, animating, child) {\n        if (animating) {\n          return target;\n        } else {\n          return child!;\n        }\n      },\n      child: ValueListenableBuilder(\n        valueListenable: _dragIndexNotifier,\n        builder: (_, dragIndex, child) {\n          if (dragIndex == index) {\n            return child!;\n          }\n          final content = isDeletable\n              ? _DeletableContainer(\n                  onDelete: () {\n                    _handleDelete(index);\n                  },\n                  child: child!,\n                )\n              : child!;\n          return _buildShake(content);\n        },\n        child: target,\n      ),\n    );\n    final draggableChild = system.isDesktop\n        ? Draggable(\n            childWhenDragging: childWhenDragging,\n            data: index,\n            feedback: feedback,\n            onDragStarted: () {\n              _handleDragStarted(index);\n            },\n            onDragUpdate: (details) {\n              _handleDragUpdate(details);\n            },\n            onDragEnd: (details) {\n              _handleDragEnd(details);\n            },\n            child: shakeTarget,\n          )\n        : LongPressDraggable(\n            childWhenDragging: childWhenDragging,\n            data: index,\n            feedback: feedback,\n            onDragStarted: () {\n              _handleDragStarted(index);\n            },\n            onDragUpdate: (details) {\n              _handleDragUpdate(details);\n            },\n            onDragEnd: (details) {\n              _handleDragEnd(details);\n            },\n            child: shakeTarget,\n          );\n    return draggableChild;\n  }\n\n  Widget _builderItem(int index) {\n    final girdItem = _childrenNotifier.value[index];\n    final child = girdItem.child;\n    return GridItem(\n      mainAxisCellCount: girdItem.mainAxisCellCount,\n      crossAxisCellCount: girdItem.crossAxisCellCount,\n      isDeletable: girdItem.isDeletable,\n      child: Builder(\n        builder: (context) {\n          _itemContexts[index] = context;\n          final childWhenDragging = ActivateBox(\n            child: Opacity(\n              opacity: 0.6,\n              child: _buildSizeBox(CommonCard(child: child), index),\n            ),\n          );\n          final feedback = ActivateBox(\n            child: _buildSizeBox(\n              CommonCard(child: Material(elevation: 6, child: child)),\n              index,\n            ),\n          );\n          return _buildTransform(\n            _buildDraggable(\n              childWhenDragging: childWhenDragging,\n              feedback: feedback,\n              item: child,\n              index: index,\n              isDeletable: girdItem.isDeletable,\n            ),\n            index,\n          );\n        },\n      ),\n    );\n  }\n\n  Widget _buildFakeTransformWidget() {\n    return ValueListenableBuilder<bool>(\n      valueListenable: _animating,\n      builder: (_, animating, _) {\n        final index = _dragIndexNotifier.value;\n        if (!animating || _fakeDragWidgetAnimation == null || index == -1) {\n          return Container();\n        }\n        return _buildSizeBox(\n          AnimatedBuilder(\n            animation: _fakeDragWidgetAnimation!,\n            builder: (_, child) {\n              return Transform.translate(\n                offset: _fakeDragWidgetAnimation!.value,\n                child: child!,\n              );\n            },\n            child: ActivateBox(child: _childrenNotifier.value[index].child),\n          ),\n          index,\n        );\n      },\n    );\n  }\n\n  @override\n  void dispose() {\n    _scrollable = null;\n    _fakeDragWidgetController.dispose();\n    _shakeController.dispose();\n    _transformController.dispose();\n    _dragIndexNotifier.dispose();\n    _animating.dispose();\n    _childrenNotifier.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return DeferredPointerHandler(\n      child: Stack(\n        children: [\n          _buildInactivate(\n            ValueListenableBuilder(\n              valueListenable: _childrenNotifier,\n              builder: (_, children, _) {\n                _onChildrenChange();\n                return Grid(\n                  axisDirection: AxisDirection.down,\n                  crossAxisCount: crossCount,\n                  crossAxisSpacing: widget.crossAxisSpacing,\n                  mainAxisSpacing: widget.mainAxisSpacing,\n                  children: [\n                    for (int i = 0; i < children.length; i++) _builderItem(i),\n                  ],\n                );\n              },\n            ),\n          ),\n          _buildFakeTransformWidget(),\n        ],\n      ),\n    );\n  }\n}\n\nclass _DeletableContainer extends StatefulWidget {\n  final Widget child;\n  final VoidCallback onDelete;\n\n  const _DeletableContainer({required this.child, required this.onDelete});\n\n  @override\n  State<_DeletableContainer> createState() => _DeletableContainerState();\n}\n\nclass _DeletableContainerState extends State<_DeletableContainer>\n    with SingleTickerProviderStateMixin {\n  late AnimationController _controller;\n  late Animation<double> _scaleAnimation;\n  late Animation<double> _fadeAnimation;\n  bool _deleteButtonVisible = true;\n\n  @override\n  void initState() {\n    super.initState();\n    _controller = AnimationController(vsync: this, duration: commonDuration);\n    _scaleAnimation = Tween(\n      begin: 1.0,\n      end: 0.4,\n    ).animate(CurvedAnimation(parent: _controller, curve: Curves.easeIn));\n    _fadeAnimation = Tween(\n      begin: 1.0,\n      end: 0.0,\n    ).animate(CurvedAnimation(parent: _controller, curve: Curves.easeIn));\n  }\n\n  @override\n  void didUpdateWidget(_DeletableContainer oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    if (oldWidget.child != widget.child) {\n      setState(() {\n        _controller.value = 0;\n        _deleteButtonVisible = true;\n      });\n    }\n  }\n\n  Future<void> _handleDel() async {\n    setState(() {\n      _deleteButtonVisible = false;\n    });\n    await _controller.forward(from: 0);\n    widget.onDelete();\n  }\n\n  @override\n  void dispose() {\n    _controller.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return Stack(\n      clipBehavior: Clip.none,\n      children: [\n        AnimatedBuilder(\n          animation: _controller.view,\n          builder: (_, child) {\n            return Transform.scale(\n              scale: _scaleAnimation.value,\n              child: Opacity(opacity: _fadeAnimation.value, child: child!),\n            );\n          },\n          child: widget.child,\n        ),\n        if (_deleteButtonVisible)\n          Positioned(\n            top: -8,\n            right: -8,\n            child: DeferPointer(\n              child: SizedBox(\n                width: 24,\n                height: 24,\n                child: IconButton.filled(\n                  iconSize: 20,\n                  padding: EdgeInsets.all(2),\n                  onPressed: _handleDel,\n                  icon: Icon(Icons.close),\n                ),\n              ),\n            ),\n          ),\n      ],\n    );\n  }\n}\n"
  },
  {
    "path": "lib/widgets/tab.dart",
    "content": "import 'dart:math' as math;\nimport 'dart:math';\n\nimport 'package:collection/collection.dart';\nimport 'package:flutter/foundation.dart';\nimport 'package:flutter/gestures.dart';\nimport 'package:flutter/material.dart';\nimport 'package:flutter/physics.dart';\nimport 'package:flutter/rendering.dart';\n\nconst EdgeInsetsGeometry _kHorizontalItemPadding = EdgeInsets.symmetric(\n  vertical: 2,\n  horizontal: 3,\n);\n\nconst Radius _kCornerRadius = Radius.circular(9);\n\nconst Radius _kThumbRadius = Radius.circular(8);\n\nconst EdgeInsets _kThumbInsets = EdgeInsets.symmetric(horizontal: 1);\n\nconst double _kMinSegmentedControlHeight = 28.0;\n\nconst EdgeInsets _kSeparatorInset = EdgeInsets.symmetric(vertical: 5);\n\nconst double _kSeparatorWidth = 1;\n\nconst double _kMinThumbScale = 0.95;\n\nconst double _kSegmentMinPadding = 10;\n\nconst double _kTouchYDistanceThreshold = 50.0 * 50.0;\n\nconst double _kContentPressedMinOpacity = 0.2;\n\nconst double _kFontSize = 13.0;\n\nconst FontWeight _kFontWeight = FontWeight.w500;\n\nconst FontWeight _kHighlightedFontWeight = FontWeight.w600;\n\nconst Color _kDisabledContentColor = Color.fromARGB(115, 122, 122, 122);\n\nfinal SpringSimulation _kThumbSpringAnimationSimulation = SpringSimulation(\n  const SpringDescription(mass: 1, stiffness: 503.551, damping: 44.8799),\n  0,\n  1,\n  0,\n);\n\nconst Duration _kSpringAnimationDuration = Duration(milliseconds: 412);\n\nconst Duration _kOpacityAnimationDuration = Duration(milliseconds: 470);\n\nconst Duration _kHighlightAnimationDuration = Duration(milliseconds: 200);\n\nclass CommonTabBar<T extends Object> extends StatefulWidget {\n  CommonTabBar({\n    super.key,\n    required this.children,\n    required this.onValueChanged,\n    this.disabledChildren = const <Never>{},\n    this.groupValue,\n    required this.thumbColor,\n    this.padding = _kHorizontalItemPadding,\n    this.backgroundColor,\n    this.proportionalWidth = false,\n  }) : assert(children.length >= 2),\n       assert(\n         groupValue == null || children.keys.contains(groupValue),\n         'The groupValue must be either null or one of the keys in the children map.',\n       );\n  final Map<T, Widget> children;\n  final Set<T> disabledChildren;\n  final T? groupValue;\n  final ValueChanged<T?> onValueChanged;\n  final Color? backgroundColor;\n  final Color thumbColor;\n  final bool proportionalWidth;\n  final EdgeInsetsGeometry padding;\n\n  @override\n  State<CommonTabBar<T>> createState() => _CommonTabBarState<T>();\n}\n\nclass _CommonTabBarState<T extends Object> extends State<CommonTabBar<T>>\n    with TickerProviderStateMixin<CommonTabBar<T>> {\n  late final AnimationController thumbController = AnimationController(\n    duration: _kSpringAnimationDuration,\n    value: 0,\n    vsync: this,\n  );\n  Animatable<Rect?>? thumbAnimatable;\n\n  late final AnimationController thumbScaleController = AnimationController(\n    duration: _kSpringAnimationDuration,\n    value: 0,\n    vsync: this,\n  );\n  late Animation<double> thumbScaleAnimation = thumbScaleController.drive(\n    Tween<double>(begin: 1, end: _kMinThumbScale),\n  );\n\n  final TapGestureRecognizer tap = TapGestureRecognizer();\n  final HorizontalDragGestureRecognizer drag =\n      HorizontalDragGestureRecognizer();\n  final LongPressGestureRecognizer longPress = LongPressGestureRecognizer();\n  final GlobalKey segmentedControlRenderWidgetKey = GlobalKey();\n\n  @override\n  void initState() {\n    super.initState();\n    final GestureArenaTeam team = GestureArenaTeam();\n    longPress.team = team;\n    drag.team = team;\n    team.captain = drag;\n\n    drag\n      ..onDown = onDown\n      ..onUpdate = onUpdate\n      ..onEnd = onEnd\n      ..onCancel = onCancel;\n\n    tap.onTapUp = onTapUp;\n    longPress.onLongPress = () {};\n\n    highlighted = widget.groupValue;\n  }\n\n  @override\n  void didUpdateWidget(CommonTabBar<T> oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    if (!isThumbDragging && highlighted != widget.groupValue) {\n      thumbController.animateWith(_kThumbSpringAnimationSimulation);\n      thumbAnimatable = null;\n      highlighted = widget.groupValue;\n    }\n  }\n\n  @override\n  void dispose() {\n    thumbScaleController.dispose();\n    thumbController.dispose();\n\n    drag.dispose();\n    tap.dispose();\n    longPress.dispose();\n\n    super.dispose();\n  }\n\n  bool? _startedOnSelectedSegment;\n  bool _startedOnDisabledSegment = false;\n\n  bool get isThumbDragging =>\n      (_startedOnSelectedSegment ?? false) && !_startedOnDisabledSegment;\n\n  T segmentForXPosition(double dx) {\n    final BuildContext currentContext =\n        segmentedControlRenderWidgetKey.currentContext!;\n    final _RenderSegmentedControl<T> renderBox =\n        currentContext.findRenderObject()! as _RenderSegmentedControl<T>;\n\n    final int numOfChildren = widget.children.length;\n    assert(renderBox.hasSize);\n    assert(numOfChildren >= 2);\n\n    int segmentIndex = renderBox.getClosestSegmentIndex(dx);\n\n    switch (Directionality.of(context)) {\n      case TextDirection.ltr:\n        break;\n      case TextDirection.rtl:\n        segmentIndex = numOfChildren - 1 - segmentIndex;\n    }\n    return widget.children.keys.elementAt(segmentIndex);\n  }\n\n  bool _hasDraggedTooFar(DragUpdateDetails details) {\n    final RenderBox renderBox = context.findRenderObject()! as RenderBox;\n    assert(renderBox.hasSize);\n    final Size size = renderBox.size;\n    final Offset offCenter =\n        details.localPosition - Offset(size.width / 2, size.height / 2);\n    final double l2 =\n        math.pow(math.max(0.0, offCenter.dx.abs() - size.width / 2), 2) +\n                math.pow(math.max(0.0, offCenter.dy.abs() - size.height / 2), 2)\n            as double;\n    return l2 > _kTouchYDistanceThreshold;\n  }\n\n  void _playThumbScaleAnimation({required bool isExpanding}) {\n    thumbScaleAnimation = thumbScaleController.drive(\n      Tween<double>(\n        begin: thumbScaleAnimation.value,\n        end: isExpanding ? 1 : _kMinThumbScale,\n      ),\n    );\n    thumbScaleController.animateWith(_kThumbSpringAnimationSimulation);\n  }\n\n  void onHighlightChangedByGesture(T newValue) {\n    if (highlighted == newValue) {\n      return;\n    }\n\n    setState(() {\n      highlighted = newValue;\n    });\n    thumbController.animateWith(_kThumbSpringAnimationSimulation);\n    thumbAnimatable = null;\n  }\n\n  void onPressedChangedByGesture(T? newValue) {\n    if (pressed != newValue) {\n      setState(() {\n        pressed = newValue;\n      });\n    }\n  }\n\n  void onTapUp(TapUpDetails details) {\n    if (isThumbDragging) {\n      return;\n    }\n    final T segment = segmentForXPosition(details.localPosition.dx);\n    onPressedChangedByGesture(null);\n    if (segment != widget.groupValue &&\n        !widget.disabledChildren.contains(segment)) {\n      widget.onValueChanged(segment);\n    }\n  }\n\n  void onDown(DragDownDetails details) {\n    final T touchDownSegment = segmentForXPosition(details.localPosition.dx);\n    _startedOnSelectedSegment = touchDownSegment == highlighted;\n    _startedOnDisabledSegment = widget.disabledChildren.contains(\n      touchDownSegment,\n    );\n    if (widget.disabledChildren.contains(touchDownSegment)) {\n      return;\n    }\n    onPressedChangedByGesture(touchDownSegment);\n\n    if (isThumbDragging) {\n      _playThumbScaleAnimation(isExpanding: false);\n    }\n  }\n\n  void onUpdate(DragUpdateDetails details) {\n    if (_startedOnDisabledSegment) {\n      return;\n    }\n    final T touchDownSegment = segmentForXPosition(details.localPosition.dx);\n    if (widget.disabledChildren.contains(touchDownSegment)) {\n      return;\n    }\n    if (isThumbDragging) {\n      onPressedChangedByGesture(touchDownSegment);\n      onHighlightChangedByGesture(touchDownSegment);\n    } else {\n      final T? segment = _hasDraggedTooFar(details)\n          ? null\n          : segmentForXPosition(details.localPosition.dx);\n      onPressedChangedByGesture(segment);\n    }\n  }\n\n  void onEnd(DragEndDetails details) {\n    final T? pressed = this.pressed;\n    if (isThumbDragging) {\n      _playThumbScaleAnimation(isExpanding: true);\n      if (highlighted != widget.groupValue) {\n        widget.onValueChanged(highlighted);\n      }\n    } else if (pressed != null) {\n      onHighlightChangedByGesture(pressed);\n      assert(pressed == highlighted);\n      if (highlighted != widget.groupValue) {\n        widget.onValueChanged(highlighted);\n      }\n    }\n\n    onPressedChangedByGesture(null);\n    _startedOnSelectedSegment = null;\n  }\n\n  void onCancel() {\n    if (isThumbDragging) {\n      _playThumbScaleAnimation(isExpanding: true);\n    }\n    onPressedChangedByGesture(null);\n    _startedOnSelectedSegment = null;\n  }\n\n  T? highlighted;\n\n  T? pressed;\n\n  @override\n  Widget build(BuildContext context) {\n    assert(widget.children.length >= 2);\n    List<Widget> children = <Widget>[];\n    bool isPreviousSegmentHighlighted = false;\n\n    int index = 0;\n    int? highlightedIndex;\n    for (final MapEntry<T, Widget> entry in widget.children.entries) {\n      final bool isHighlighted = highlighted == entry.key;\n      if (isHighlighted) {\n        highlightedIndex = index;\n      }\n\n      if (index != 0) {\n        children.add(\n          _SegmentSeparator(\n            key: ValueKey<int>(index),\n            highlighted: isPreviousSegmentHighlighted || isHighlighted,\n          ),\n        );\n      }\n\n      final TextDirection textDirection = Directionality.of(context);\n      final _SegmentLocation segmentLocation = switch (textDirection) {\n        TextDirection.ltr when index == 0 => _SegmentLocation.leftmost,\n        TextDirection.ltr when index == widget.children.length - 1 =>\n          _SegmentLocation.rightmost,\n        TextDirection.rtl when index == widget.children.length - 1 =>\n          _SegmentLocation.leftmost,\n        TextDirection.rtl when index == 0 => _SegmentLocation.rightmost,\n        TextDirection.ltr || TextDirection.rtl => _SegmentLocation.inbetween,\n      };\n      children.add(\n        Semantics(\n          button: true,\n          onTap: () {\n            if (widget.disabledChildren.contains(entry.key)) {\n              return;\n            }\n            widget.onValueChanged(entry.key);\n          },\n          inMutuallyExclusiveGroup: true,\n          selected: widget.groupValue == entry.key,\n          child: MouseRegion(\n            cursor: kIsWeb ? SystemMouseCursors.click : MouseCursor.defer,\n            child: _Segment<T>(\n              key: ValueKey<T>(entry.key),\n              highlighted: isHighlighted,\n              pressed: pressed == entry.key,\n              isDragging: isThumbDragging,\n              enabled: !widget.disabledChildren.contains(entry.key),\n              segmentLocation: segmentLocation,\n              child: entry.value,\n            ),\n          ),\n        ),\n      );\n\n      index += 1;\n      isPreviousSegmentHighlighted = isHighlighted;\n    }\n\n    assert((highlightedIndex == null) == (highlighted == null));\n\n    switch (Directionality.of(context)) {\n      case TextDirection.ltr:\n        break;\n      case TextDirection.rtl:\n        children = children.reversed.toList(growable: false);\n        if (highlightedIndex != null) {\n          highlightedIndex = index - 1 - highlightedIndex;\n        }\n    }\n\n    return UnconstrainedBox(\n      constrainedAxis: Axis.horizontal,\n      child: Container(\n        clipBehavior: Clip.antiAlias,\n        padding: widget.padding.resolve(Directionality.of(context)),\n        decoration: BoxDecoration(\n          borderRadius: const BorderRadius.all(_kCornerRadius),\n          color: widget.backgroundColor,\n        ),\n        child: AnimatedBuilder(\n          animation: thumbScaleAnimation,\n          builder: (BuildContext context, Widget? child) {\n            return _CommonTabBarRenderWidget<T>(\n              proportionalWidth: widget.proportionalWidth,\n              key: segmentedControlRenderWidgetKey,\n              highlightedIndex: highlightedIndex,\n              thumbColor: widget.thumbColor,\n              thumbScale: thumbScaleAnimation.value,\n              state: this,\n              children: children,\n            );\n          },\n        ),\n      ),\n    );\n  }\n}\n\nclass _Segment<T> extends StatefulWidget {\n  const _Segment({\n    required ValueKey<T> key,\n    required this.child,\n    required this.pressed,\n    required this.highlighted,\n    required this.isDragging,\n    required this.enabled,\n    required this.segmentLocation,\n  }) : super(key: key);\n\n  final Widget child;\n\n  final bool pressed;\n  final bool highlighted;\n  final bool enabled;\n  final _SegmentLocation segmentLocation;\n  final bool isDragging;\n\n  bool get shouldFadeoutContent => pressed && !highlighted && enabled;\n\n  bool get shouldScaleContent =>\n      pressed && highlighted && isDragging && enabled;\n\n  @override\n  _SegmentState<T> createState() => _SegmentState<T>();\n}\n\nclass _SegmentState<T> extends State<_Segment<T>>\n    with TickerProviderStateMixin<_Segment<T>> {\n  late final AnimationController highlightPressScaleController;\n  late Animation<double> highlightPressScaleAnimation;\n\n  @override\n  void initState() {\n    super.initState();\n    highlightPressScaleController = AnimationController(\n      duration: _kOpacityAnimationDuration,\n      value: widget.shouldScaleContent ? 1 : 0,\n      vsync: this,\n    );\n\n    highlightPressScaleAnimation = highlightPressScaleController.drive(\n      Tween<double>(begin: 1.0, end: _kMinThumbScale),\n    );\n  }\n\n  @override\n  void didUpdateWidget(_Segment<T> oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    assert(oldWidget.key == widget.key);\n\n    if (oldWidget.shouldScaleContent != widget.shouldScaleContent) {\n      highlightPressScaleAnimation = highlightPressScaleController.drive(\n        Tween<double>(\n          begin: highlightPressScaleAnimation.value,\n          end: widget.shouldScaleContent ? _kMinThumbScale : 1.0,\n        ),\n      );\n      highlightPressScaleController.animateWith(\n        _kThumbSpringAnimationSimulation,\n      );\n    }\n  }\n\n  @override\n  void dispose() {\n    highlightPressScaleController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final Alignment scaleAlignment = switch (widget.segmentLocation) {\n      _SegmentLocation.leftmost => Alignment.centerLeft,\n      _SegmentLocation.rightmost => Alignment.centerRight,\n      _SegmentLocation.inbetween => Alignment.center,\n    };\n\n    return MetaData(\n      behavior: HitTestBehavior.opaque,\n      child: IndexedStack(\n        alignment: Alignment.center,\n        children: <Widget>[\n          AnimatedOpacity(\n            opacity: widget.shouldFadeoutContent\n                ? _kContentPressedMinOpacity\n                : 1,\n            duration: _kOpacityAnimationDuration,\n            curve: Curves.ease,\n            child: AnimatedDefaultTextStyle(\n              style: DefaultTextStyle.of(context).style.merge(\n                TextStyle(\n                  fontWeight: widget.highlighted\n                      ? _kHighlightedFontWeight\n                      : _kFontWeight,\n                  fontSize: _kFontSize,\n                  color: widget.enabled ? null : _kDisabledContentColor,\n                ),\n              ),\n              duration: _kHighlightAnimationDuration,\n              curve: Curves.ease,\n              child: ScaleTransition(\n                alignment: scaleAlignment,\n                scale: highlightPressScaleAnimation,\n                child: widget.child,\n              ),\n            ),\n          ),\n          DefaultTextStyle.merge(\n            style: const TextStyle(\n              fontWeight: _kHighlightedFontWeight,\n              fontSize: _kFontSize,\n            ),\n            child: widget.child,\n          ),\n        ],\n      ),\n    );\n  }\n}\n\nclass _SegmentSeparator extends StatefulWidget {\n  const _SegmentSeparator({\n    required ValueKey<int> key,\n    required this.highlighted,\n  }) : super(key: key);\n\n  final bool highlighted;\n\n  @override\n  _SegmentSeparatorState createState() => _SegmentSeparatorState();\n}\n\nclass _SegmentSeparatorState extends State<_SegmentSeparator>\n    with TickerProviderStateMixin<_SegmentSeparator> {\n  late final AnimationController separatorOpacityController;\n\n  @override\n  void initState() {\n    super.initState();\n\n    separatorOpacityController = AnimationController(\n      duration: _kSpringAnimationDuration,\n      value: widget.highlighted ? 0 : 1,\n      vsync: this,\n    );\n  }\n\n  @override\n  void didUpdateWidget(_SegmentSeparator oldWidget) {\n    super.didUpdateWidget(oldWidget);\n    assert(oldWidget.key == widget.key);\n\n    if (oldWidget.highlighted != widget.highlighted) {\n      separatorOpacityController.animateTo(\n        widget.highlighted ? 0 : 1,\n        duration: _kSpringAnimationDuration,\n        curve: Curves.ease,\n      );\n    }\n  }\n\n  @override\n  void dispose() {\n    separatorOpacityController.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return AnimatedBuilder(\n      animation: separatorOpacityController,\n      child: const SizedBox(width: _kSeparatorWidth),\n      builder: (BuildContext context, Widget? child) {\n        return Padding(\n          padding: _kSeparatorInset,\n          child: DecoratedBox(\n            decoration: BoxDecoration(color: Colors.transparent),\n            child: child,\n          ),\n        );\n      },\n    );\n  }\n}\n\nclass _CommonTabBarRenderWidget<T extends Object>\n    extends MultiChildRenderObjectWidget {\n  const _CommonTabBarRenderWidget({\n    super.key,\n    super.children,\n    required this.highlightedIndex,\n    required this.thumbColor,\n    required this.thumbScale,\n    required this.state,\n    required this.proportionalWidth,\n  });\n\n  final int? highlightedIndex;\n  final Color thumbColor;\n  final double thumbScale;\n  final bool proportionalWidth;\n  final _CommonTabBarState<T> state;\n\n  @override\n  RenderObject createRenderObject(BuildContext context) {\n    return _RenderSegmentedControl<T>(\n      highlightedIndex: highlightedIndex,\n      thumbColor: thumbColor,\n      thumbScale: thumbScale,\n      proportionalWidth: proportionalWidth,\n      state: state,\n    );\n  }\n\n  @override\n  void updateRenderObject(\n    BuildContext context,\n    _RenderSegmentedControl<T> renderObject,\n  ) {\n    assert(renderObject.state == state);\n    renderObject\n      ..thumbColor = thumbColor\n      ..thumbScale = thumbScale\n      ..highlightedIndex = highlightedIndex\n      ..proportionalWidth = proportionalWidth;\n  }\n}\n\nclass _SegmentedControlContainerBoxParentData\n    extends ContainerBoxParentData<RenderBox> {}\n\nenum _SegmentLocation { leftmost, rightmost, inbetween }\n\nclass _RenderSegmentedControl<T extends Object> extends RenderBox\n    with\n        ContainerRenderObjectMixin<\n          RenderBox,\n          ContainerBoxParentData<RenderBox>\n        >,\n        RenderBoxContainerDefaultsMixin<\n          RenderBox,\n          ContainerBoxParentData<RenderBox>\n        > {\n  _RenderSegmentedControl({\n    required int? highlightedIndex,\n    required Color thumbColor,\n    required double thumbScale,\n    required bool proportionalWidth,\n    required this.state,\n  }) : _highlightedIndex = highlightedIndex,\n       _thumbColor = thumbColor,\n       _thumbScale = thumbScale,\n       _proportionalWidth = proportionalWidth;\n\n  final _CommonTabBarState<T> state;\n\n  Rect? currentThumbRect;\n\n  @override\n  void attach(PipelineOwner owner) {\n    super.attach(owner);\n    state.thumbController.addListener(markNeedsPaint);\n  }\n\n  @override\n  void detach() {\n    state.thumbController.removeListener(markNeedsPaint);\n    super.detach();\n  }\n\n  double get thumbScale => _thumbScale;\n  double _thumbScale;\n\n  set thumbScale(double value) {\n    if (_thumbScale == value) {\n      return;\n    }\n\n    _thumbScale = value;\n    if (state.highlighted != null) {\n      markNeedsPaint();\n    }\n  }\n\n  int? get highlightedIndex => _highlightedIndex;\n  int? _highlightedIndex;\n\n  set highlightedIndex(int? value) {\n    if (_highlightedIndex == value) {\n      return;\n    }\n\n    _highlightedIndex = value;\n    markNeedsPaint();\n  }\n\n  Color get thumbColor => _thumbColor;\n  Color _thumbColor;\n\n  set thumbColor(Color value) {\n    if (_thumbColor == value) {\n      return;\n    }\n    _thumbColor = value;\n    markNeedsPaint();\n  }\n\n  bool get proportionalWidth => _proportionalWidth;\n  bool _proportionalWidth;\n\n  set proportionalWidth(bool value) {\n    if (_proportionalWidth == value) {\n      return;\n    }\n    _proportionalWidth = value;\n    markNeedsLayout();\n  }\n\n  @override\n  void handleEvent(PointerEvent event, BoxHitTestEntry entry) {\n    assert(debugHandleEvent(event, entry));\n    if (event is PointerDownEvent && !state.isThumbDragging) {\n      state.tap.addPointer(event);\n      state.longPress.addPointer(event);\n      state.drag.addPointer(event);\n    }\n  }\n\n  double get separatorWidth => _kSeparatorInset.horizontal + _kSeparatorWidth;\n\n  double get totalSeparatorWidth => separatorWidth * (childCount ~/ 2);\n\n  int getClosestSegmentIndex(double dx) {\n    int index = 0;\n    RenderBox? child = firstChild;\n    while (child != null) {\n      final _SegmentedControlContainerBoxParentData childParentData =\n          child.parentData! as _SegmentedControlContainerBoxParentData;\n      final double clampX = clampDouble(\n        dx,\n        childParentData.offset.dx,\n        child.size.width + childParentData.offset.dx,\n      );\n\n      if (dx <= clampX) {\n        break;\n      }\n\n      index++;\n      child = nonSeparatorChildAfter(child);\n    }\n\n    final int segmentCount = childCount ~/ 2 + 1;\n    return min(index, segmentCount - 1);\n  }\n\n  RenderBox? nonSeparatorChildAfter(RenderBox child) {\n    final RenderBox? nextChild = childAfter(child);\n    return nextChild == null ? null : childAfter(nextChild);\n  }\n\n  @override\n  double computeMinIntrinsicWidth(double height) {\n    final int childCount = this.childCount ~/ 2 + 1;\n    RenderBox? child = firstChild;\n    double maxMinChildWidth = 0;\n    while (child != null) {\n      final double childWidth = child.getMinIntrinsicWidth(height);\n      maxMinChildWidth = math.max(maxMinChildWidth, childWidth);\n      child = nonSeparatorChildAfter(child);\n    }\n    return (maxMinChildWidth + 2 * _kSegmentMinPadding) * childCount +\n        totalSeparatorWidth;\n  }\n\n  @override\n  double computeMaxIntrinsicWidth(double height) {\n    final int childCount = this.childCount ~/ 2 + 1;\n    RenderBox? child = firstChild;\n    double maxMaxChildWidth = 0;\n    while (child != null) {\n      final double childWidth = child.getMaxIntrinsicWidth(height);\n      maxMaxChildWidth = math.max(maxMaxChildWidth, childWidth);\n      child = nonSeparatorChildAfter(child);\n    }\n    return (maxMaxChildWidth + 2 * _kSegmentMinPadding) * childCount +\n        totalSeparatorWidth;\n  }\n\n  @override\n  double computeMinIntrinsicHeight(double width) {\n    RenderBox? child = firstChild;\n    double maxMinChildHeight = _kMinSegmentedControlHeight;\n    while (child != null) {\n      final double childHeight = child.getMinIntrinsicHeight(width);\n      maxMinChildHeight = math.max(maxMinChildHeight, childHeight);\n      child = nonSeparatorChildAfter(child);\n    }\n    return maxMinChildHeight;\n  }\n\n  @override\n  double computeMaxIntrinsicHeight(double width) {\n    RenderBox? child = firstChild;\n    double maxMaxChildHeight = _kMinSegmentedControlHeight;\n    while (child != null) {\n      final double childHeight = child.getMaxIntrinsicHeight(width);\n      maxMaxChildHeight = math.max(maxMaxChildHeight, childHeight);\n      child = nonSeparatorChildAfter(child);\n    }\n    return maxMaxChildHeight;\n  }\n\n  @override\n  double? computeDistanceToActualBaseline(TextBaseline baseline) {\n    return defaultComputeDistanceToHighestActualBaseline(baseline);\n  }\n\n  @override\n  void setupParentData(RenderBox child) {\n    if (child.parentData is! _SegmentedControlContainerBoxParentData) {\n      child.parentData = _SegmentedControlContainerBoxParentData();\n    }\n  }\n\n  double _getMaxChildHeight(BoxConstraints constraints, double childWidth) {\n    double maxHeight = _kMinSegmentedControlHeight;\n    RenderBox? child = firstChild;\n    while (child != null) {\n      final double boxHeight = child.getMaxIntrinsicHeight(childWidth);\n      maxHeight = math.max(maxHeight, boxHeight);\n      child = nonSeparatorChildAfter(child);\n    }\n    return maxHeight;\n  }\n\n  double _getMaxChildWidth(BoxConstraints constraints) {\n    final int childCount = this.childCount ~/ 2 + 1;\n    double childWidth =\n        (constraints.minWidth - totalSeparatorWidth) / childCount;\n    RenderBox? child = firstChild;\n    while (child != null) {\n      childWidth = math.max(\n        childWidth,\n        child.getMaxIntrinsicWidth(double.infinity) + 2 * _kSegmentMinPadding,\n      );\n      child = nonSeparatorChildAfter(child);\n    }\n    return math.min(\n      childWidth,\n      (constraints.maxWidth - totalSeparatorWidth) / childCount,\n    );\n  }\n\n  List<double> _getChildWidths(BoxConstraints constraints) {\n    if (!proportionalWidth) {\n      final double maxChildWidth = _getMaxChildWidth(constraints);\n      final int segmentCount = childCount ~/ 2 + 1;\n      return List<double>.filled(segmentCount, maxChildWidth);\n    }\n\n    final List<double> segmentWidths = <double>[];\n    RenderBox? child = firstChild;\n    while (child != null) {\n      final double childWidth =\n          child.getMaxIntrinsicWidth(double.infinity) + 2 * _kSegmentMinPadding;\n      child = nonSeparatorChildAfter(child);\n      segmentWidths.add(childWidth);\n    }\n\n    final double totalWidth = segmentWidths.sum;\n    final double allowedMaxWidth = constraints.maxWidth - totalSeparatorWidth;\n    final double allowedMinWidth = constraints.minWidth - totalSeparatorWidth;\n\n    final double scale =\n        clampDouble(totalWidth, allowedMinWidth, allowedMaxWidth) / totalWidth;\n    if (scale != 1) {\n      for (int i = 0; i < segmentWidths.length; i++) {\n        segmentWidths[i] = segmentWidths[i] * scale;\n      }\n    }\n    return segmentWidths;\n  }\n\n  Size _computeOverallSize(BoxConstraints constraints) {\n    final double maxChildHeight = _getMaxChildHeight(\n      constraints,\n      constraints.maxWidth,\n    );\n    return constraints.constrain(\n      Size(\n        _getChildWidths(constraints).sum + totalSeparatorWidth,\n        maxChildHeight,\n      ),\n    );\n  }\n\n  @override\n  double? computeDryBaseline(\n    covariant BoxConstraints constraints,\n    TextBaseline baseline,\n  ) {\n    final List<double> segmentWidths = _getChildWidths(constraints);\n    final double childHeight = _getMaxChildHeight(\n      constraints,\n      constraints.maxWidth,\n    );\n\n    int index = 0;\n    BaselineOffset baselineOffset = BaselineOffset.noBaseline;\n    RenderBox? child = firstChild;\n    while (child != null) {\n      final BoxConstraints childConstraints = BoxConstraints.tight(\n        Size(segmentWidths[index], childHeight),\n      );\n      baselineOffset = baselineOffset.minOf(\n        BaselineOffset(child.getDryBaseline(childConstraints, baseline)),\n      );\n\n      child = nonSeparatorChildAfter(child);\n      index++;\n    }\n\n    return baselineOffset.offset;\n  }\n\n  @override\n  Size computeDryLayout(BoxConstraints constraints) {\n    return _computeOverallSize(constraints);\n  }\n\n  @override\n  void performLayout() {\n    final BoxConstraints constraints = this.constraints;\n    final List<double> segmentWidths = _getChildWidths(constraints);\n\n    final double childHeight = _getMaxChildHeight(constraints, double.infinity);\n    final BoxConstraints separatorConstraints = BoxConstraints(\n      minHeight: childHeight,\n      maxHeight: childHeight,\n    );\n    RenderBox? child = firstChild;\n    int index = 0;\n    double start = 0;\n    while (child != null) {\n      final BoxConstraints childConstraints = BoxConstraints.tight(\n        Size(segmentWidths[index ~/ 2], childHeight),\n      );\n      child.layout(\n        index.isEven ? childConstraints : separatorConstraints,\n        parentUsesSize: true,\n      );\n      final _SegmentedControlContainerBoxParentData childParentData =\n          child.parentData! as _SegmentedControlContainerBoxParentData;\n      final Offset childOffset = Offset(start, 0);\n      childParentData.offset = childOffset;\n      start += child.size.width;\n      assert(\n        index.isEven ||\n            child.size.width == _kSeparatorWidth + _kSeparatorInset.horizontal,\n        '${child.size.width} != ${_kSeparatorWidth + _kSeparatorInset.horizontal}',\n      );\n      child = childAfter(child);\n      index += 1;\n    }\n    size = _computeOverallSize(constraints);\n  }\n\n  Rect? moveThumbRectInBound(Rect? thumbRect, List<RenderBox> children) {\n    assert(hasSize);\n    assert(children.length >= 2);\n    if (thumbRect == null) {\n      return null;\n    }\n\n    final Offset firstChildOffset =\n        (children.first.parentData! as _SegmentedControlContainerBoxParentData)\n            .offset;\n    final double leftMost = firstChildOffset.dx;\n    final double rightMost =\n        (children.last.parentData! as _SegmentedControlContainerBoxParentData)\n            .offset\n            .dx +\n        children.last.size.width;\n    assert(rightMost > leftMost);\n    return Rect.fromLTRB(\n      math.max(thumbRect.left, leftMost - _kThumbInsets.left),\n      firstChildOffset.dy - _kThumbInsets.top,\n      math.min(thumbRect.right, rightMost + _kThumbInsets.right),\n      firstChildOffset.dy + children.first.size.height + _kThumbInsets.bottom,\n    );\n  }\n\n  @override\n  void paint(PaintingContext context, Offset offset) {\n    final List<RenderBox> children = getChildrenAsList();\n    for (int index = 1; index < childCount; index += 2) {\n      _paintSeparator(context, offset, children[index]);\n    }\n\n    final int? highlightedChildIndex = highlightedIndex;\n    if (highlightedChildIndex != null) {\n      final RenderBox selectedChild = children[highlightedChildIndex * 2];\n\n      final _SegmentedControlContainerBoxParentData childParentData =\n          selectedChild.parentData! as _SegmentedControlContainerBoxParentData;\n      final Rect newThumbRect = _kThumbInsets.inflateRect(\n        childParentData.offset & selectedChild.size,\n      );\n      if (state.thumbController.isAnimating) {\n        final Animatable<Rect?>? thumbTween = state.thumbAnimatable;\n        if (thumbTween == null) {\n          final Rect startingRect =\n              moveThumbRectInBound(currentThumbRect, children) ?? newThumbRect;\n          state.thumbAnimatable = RectTween(\n            begin: startingRect,\n            end: newThumbRect,\n          );\n        } else if (newThumbRect != thumbTween.transform(1)) {\n          final Rect startingRect =\n              moveThumbRectInBound(currentThumbRect, children) ?? newThumbRect;\n          state.thumbAnimatable = RectTween(\n            begin: startingRect,\n            end: newThumbRect,\n          ).chain(CurveTween(curve: Interval(state.thumbController.value, 1)));\n        }\n      } else {\n        state.thumbAnimatable = null;\n      }\n\n      final Rect unscaledThumbRect =\n          state.thumbAnimatable?.evaluate(state.thumbController) ??\n          newThumbRect;\n      currentThumbRect = unscaledThumbRect;\n\n      final _SegmentLocation childLocation;\n      if (highlightedChildIndex == 0) {\n        childLocation = _SegmentLocation.leftmost;\n      } else if (highlightedChildIndex == children.length ~/ 2) {\n        childLocation = _SegmentLocation.rightmost;\n      } else {\n        childLocation = _SegmentLocation.inbetween;\n      }\n      final double delta = switch (childLocation) {\n        _SegmentLocation.leftmost =>\n          unscaledThumbRect.width - unscaledThumbRect.width * thumbScale,\n        _SegmentLocation.rightmost =>\n          unscaledThumbRect.width * thumbScale - unscaledThumbRect.width,\n        _SegmentLocation.inbetween => 0,\n      };\n      final Rect thumbRect = Rect.fromCenter(\n        center: unscaledThumbRect.center - Offset(delta / 2, 0),\n        width: unscaledThumbRect.width * thumbScale,\n        height: unscaledThumbRect.height * thumbScale,\n      );\n\n      _paintThumb(context, offset, thumbRect);\n    } else {\n      currentThumbRect = null;\n    }\n\n    for (int index = 0; index < children.length; index += 2) {\n      _paintChild(context, offset, children[index]);\n    }\n  }\n\n  final Paint separatorPaint = Paint();\n\n  void _paintSeparator(\n    PaintingContext context,\n    Offset offset,\n    RenderBox child,\n  ) {\n    final _SegmentedControlContainerBoxParentData childParentData =\n        child.parentData! as _SegmentedControlContainerBoxParentData;\n    context.paintChild(child, offset + childParentData.offset);\n  }\n\n  void _paintChild(PaintingContext context, Offset offset, RenderBox child) {\n    final _SegmentedControlContainerBoxParentData childParentData =\n        child.parentData! as _SegmentedControlContainerBoxParentData;\n    context.paintChild(child, childParentData.offset + offset);\n  }\n\n  void _paintThumb(PaintingContext context, Offset offset, Rect thumbRect) {\n    // const List<BoxShadow> thumbShadow = <BoxShadow>[\n    //   BoxShadow(color: Color(0x1F000000), offset: Offset(0, 3), blurRadius: 8),\n    //   BoxShadow(color: Color(0x0A000000), offset: Offset(0, 3), blurRadius: 1),\n    // ];\n\n    final RRect thumbRRect = RRect.fromRectAndRadius(\n      thumbRect.shift(offset),\n      _kThumbRadius,\n    );\n\n    context.canvas.drawRRect(\n      thumbRRect.inflate(0.5),\n      Paint()..color = const Color(0x0A000000),\n    );\n\n    context.canvas.drawRRect(thumbRRect, Paint()..color = thumbColor);\n  }\n\n  @override\n  bool hitTestChildren(BoxHitTestResult result, {required Offset position}) {\n    RenderBox? child = lastChild;\n    while (child != null) {\n      final _SegmentedControlContainerBoxParentData childParentData =\n          child.parentData! as _SegmentedControlContainerBoxParentData;\n      if ((childParentData.offset & child.size).contains(position)) {\n        return result.addWithPaintOffset(\n          offset: childParentData.offset,\n          position: position,\n          hitTest: (BoxHitTestResult result, Offset localOffset) {\n            assert(localOffset == position - childParentData.offset);\n            return child!.hitTest(result, position: localOffset);\n          },\n        );\n      }\n      child = childParentData.previousSibling;\n    }\n    return false;\n  }\n}\n"
  },
  {
    "path": "lib/widgets/text.dart",
    "content": "import 'package:emoji_regex/emoji_regex.dart';\nimport 'package:bett_box/common/common.dart';\nimport 'package:bett_box/enum/enum.dart';\nimport 'package:flutter/material.dart';\n\nimport '../state.dart';\n\nclass TooltipText extends StatelessWidget {\n  final Widget text;\n\n  const TooltipText({super.key, required this.text});\n\n  @override\n  Widget build(BuildContext context) {\n    return LayoutBuilder(\n      builder: (context, container) {\n        final maxWidth = container.maxWidth;\n        Text? textWidget;\n        String? message;\n        if (text is Text) {\n          textWidget = text as Text;\n          message = textWidget.data;\n        } else if (text is EmojiText) {\n          final emojiText = text as EmojiText;\n          textWidget = Text(\n            emojiText.text,\n            style: emojiText.style,\n            maxLines: emojiText.maxLines,\n            overflow: emojiText.overflow,\n          );\n          message = emojiText.text;\n        }\n        if (textWidget != null) {\n          final size = globalState.measure.computeTextSize(textWidget);\n          if (maxWidth < size.width) {\n            return Tooltip(\n              triggerMode: TooltipTriggerMode.longPress,\n              preferBelow: false,\n              message: message,\n              child: text,\n            );\n          }\n        }\n        return text;\n      },\n    );\n  }\n}\n\nclass EmojiText extends StatelessWidget {\n  final String text;\n  final TextStyle? style;\n  final int? maxLines;\n  final TextOverflow? overflow;\n\n  const EmojiText(\n    this.text, {\n    super.key,\n    this.maxLines,\n    this.overflow,\n    this.style,\n  });\n\n  List<TextSpan> _buildTextSpans(String emojis, TextStyle defaultStyle) {\n    final List<TextSpan> spans = [];\n    final matches = emojiRegex().allMatches(text);\n    final effectiveStyle = style ?? defaultStyle;\n\n    int lastMatchEnd = 0;\n    for (final match in matches) {\n      if (match.start > lastMatchEnd) {\n        spans.add(\n          TextSpan(\n            text: text.substring(lastMatchEnd, match.start),\n            style: effectiveStyle,\n          ),\n        );\n      }\n      spans.add(\n        TextSpan(\n          text: match.group(0),\n          style: effectiveStyle.merge(\n            TextStyle(\n              fontFamily: system.isDesktop && !system.isMacOS \n                  ? FontFamily.twEmoji.value \n                  : null,\n            ),\n          ),\n        ),\n      );\n      lastMatchEnd = match.end;\n    }\n    if (lastMatchEnd < text.length) {\n      spans.add(\n        TextSpan(text: text.substring(lastMatchEnd), style: effectiveStyle),\n      );\n    }\n\n    return spans;\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    final defaultStyle = DefaultTextStyle.of(context).style;\n    return RichText(\n      textScaler: MediaQuery.of(context).textScaler,\n      maxLines: maxLines,\n      overflow: overflow ?? TextOverflow.clip,\n      text: TextSpan(children: _buildTextSpans(text, defaultStyle)),\n    );\n  }\n}"
  },
  {
    "path": "lib/widgets/view.dart",
    "content": "import 'package:flutter/cupertino.dart';\n\nclass CommonView extends StatefulWidget {\n  final List<Widget> actions;\n\n  const CommonView({super.key, required this.actions});\n\n  @override\n  State<CommonView> createState() => _CommonViewState();\n}\n\nclass _CommonViewState extends State<CommonView> {\n  @override\n  Widget build(BuildContext context) {\n    return const Placeholder();\n  }\n}\n"
  },
  {
    "path": "lib/widgets/wave.dart",
    "content": "import 'dart:math';\n\nimport 'package:flutter/material.dart';\n\nclass WaveView extends StatefulWidget {\n  final double waveAmplitude;\n  final double waveFrequency;\n  final Color waveColor;\n  final Duration duration;\n\n  const WaveView({\n    super.key,\n    this.waveAmplitude = 50.0,\n    this.waveFrequency = 1.5,\n    required this.waveColor,\n    this.duration = const Duration(seconds: 2),\n  });\n\n  @override\n  State<WaveView> createState() => _WaveViewState();\n}\n\nclass _WaveViewState extends State<WaveView>\n    with SingleTickerProviderStateMixin {\n  late AnimationController _controller;\n\n  @override\n  void initState() {\n    super.initState();\n    _controller = AnimationController(vsync: this, duration: widget.duration)\n      ..repeat();\n  }\n\n  @override\n  void dispose() {\n    _controller.dispose();\n    super.dispose();\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    return LayoutBuilder(\n      builder: (_, constraints) {\n        return RepaintBoundary(\n          child: AnimatedBuilder(\n            animation: _controller,\n            builder: (context, child) {\n              return CustomPaint(\n                painter: WavePainter(\n                  animationValue: _controller.value,\n                  waveAmplitude: widget.waveAmplitude,\n                  waveFrequency: widget.waveFrequency,\n                  waveColor: widget.waveColor,\n                ),\n                size: Size(constraints.maxWidth, constraints.maxHeight),\n              );\n            },\n          ),\n        );\n      },\n    );\n  }\n}\n\nclass WavePainter extends CustomPainter {\n  final double animationValue;\n  final double waveAmplitude;\n  final double waveFrequency;\n  final Color waveColor;\n\n  late Paint _paint;\n  final Path _path = Path();\n  Color _lastColor;\n\n  WavePainter({\n    required this.animationValue,\n    required this.waveAmplitude,\n    required this.waveFrequency,\n    required this.waveColor,\n  }) : _lastColor = waveColor {\n    _paint = Paint()\n      ..color = waveColor\n      ..style = PaintingStyle.fill;\n  }\n\n  @override\n  void paint(Canvas canvas, Size size) {\n    if (waveColor != _lastColor) {\n      _paint = Paint()\n        ..color = waveColor\n        ..style = PaintingStyle.fill;\n      _lastColor = waveColor;\n    }\n\n    _path.reset();\n\n    final baseHeight = size.height / 3;\n    final phase = animationValue * 2 * pi;\n    final widthFactor = 2 * pi * waveFrequency / size.width;\n\n    _path.moveTo(0, baseHeight);\n\n    for (double x = 0; x <= size.width; x += size.width / 20) {\n      final y = waveAmplitude * sin(x * widthFactor + phase);\n      _path.lineTo(x, baseHeight + y);\n    }\n\n    _path.lineTo(\n      size.width,\n      baseHeight + waveAmplitude * sin(size.width * widthFactor + phase),\n    );\n\n    _path.lineTo(size.width, size.height);\n    _path.lineTo(0, size.height);\n    _path.close();\n\n    canvas.drawPath(_path, _paint);\n  }\n\n  @override\n  bool shouldRepaint(covariant WavePainter oldDelegate) {\n    return oldDelegate.animationValue != animationValue ||\n        oldDelegate.waveAmplitude != waveAmplitude ||\n        oldDelegate.waveFrequency != waveFrequency ||\n        oldDelegate.waveColor != waveColor;\n  }\n}\n"
  },
  {
    "path": "lib/widgets/widgets.dart",
    "content": "export 'animate_grid.dart';\nexport 'google_bottom_nav_bar.dart';\nexport 'pop_scope.dart';\nexport 'builder.dart';\nexport 'card.dart';\nexport 'chip.dart';\nexport 'color_scheme_box.dart';\nexport 'disabled_mask.dart';\nexport 'fade_box.dart';\nexport 'float_layout.dart';\nexport 'grid.dart';\nexport 'icon.dart';\nexport 'input.dart';\nexport 'keep_scope.dart';\nexport 'line_chart.dart';\nexport 'list.dart';\nexport 'null_status.dart';\nexport 'open_container.dart';\nexport 'popup.dart';\nexport 'scaffold.dart';\nexport 'setting.dart';\nexport 'sheet.dart';\nexport 'side_sheet.dart';\nexport 'subscription_info_view.dart';\nexport 'text.dart';\nexport 'super_grid.dart';\nexport 'donut_chart.dart';\nexport 'activate_box.dart';\nexport 'wave.dart';\nexport 'scroll.dart';\nexport 'dialog.dart';\nexport 'effect.dart';\nexport 'palette.dart';\nexport 'tab.dart';\nexport 'container.dart';\nexport 'notification.dart';\n"
  },
  {
    "path": "linux/.gitignore",
    "content": "flutter/ephemeral\n"
  },
  {
    "path": "linux/CMakeLists.txt",
    "content": "# Project-level configuration.\ncmake_minimum_required(VERSION 3.10)\nproject(runner LANGUAGES CXX)\n\nset(SENTRY_BACKEND none CACHE STRING \"Sentry backend\" FORCE)\n\n# The name of the executable created for the application. Change this to change\n# the on-disk name of your application.\nset(BINARY_NAME \"Bettbox\")\n# The unique GTK application identifier for this application. See:\n# https://wiki.gnome.org/HowDoI/ChooseApplicationID\nset(APPLICATION_ID \"com.appshub.bettbox\")\n\nadd_definitions(-DFLUTTER_DISABLE_IMPELLER=1)\n\n# Explicitly opt in to modern CMake behaviors to avoid warnings with recent\n# versions of CMake.\ncmake_policy(SET CMP0063 NEW)\n\n# Load bundled libraries from the lib/ directory relative to the binary.\nset(CMAKE_INSTALL_RPATH \"$ORIGIN/lib\")\n\n# Root filesystem for cross-building.\nif(FLUTTER_TARGET_PLATFORM_SYSROOT)\n  set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})\n  set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})\n  set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)\n  set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)\n  set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)\n  set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)\nendif()\n\n# Define build configuration options.\nif(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)\n  set(CMAKE_BUILD_TYPE \"Debug\" CACHE\n    STRING \"Flutter build mode\" FORCE)\n  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS\n    \"Debug\" \"Profile\" \"Release\")\nendif()\n\n# Compilation settings that should be applied to most targets.\n#\n# Be cautious about adding new options here, as plugins use this function by\n# default. In most cases, you should add new options to specific targets instead\n# of modifying this function.\nfunction(APPLY_STANDARD_SETTINGS TARGET)\n  target_compile_features(${TARGET} PUBLIC cxx_std_14)\n  target_compile_options(${TARGET} PRIVATE -Wall -Werror)\n  target_compile_options(${TARGET} PRIVATE \"$<$<NOT:$<CONFIG:Debug>>:-O3>\")\n  target_compile_definitions(${TARGET} PRIVATE \"$<$<NOT:$<CONFIG:Debug>>:NDEBUG>\")\nendfunction()\n\n# Flutter library and tool build rules.\nset(FLUTTER_MANAGED_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/flutter\")\nadd_subdirectory(${FLUTTER_MANAGED_DIR})\n\n# System-level dependencies.\nfind_package(PkgConfig REQUIRED)\npkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)\n\n# Find X11 for thread safety\nfind_package(X11 REQUIRED)\n\nadd_definitions(-DAPPLICATION_ID=\"${APPLICATION_ID}\")\n\n# Define the application target. To change its name, change BINARY_NAME above,\n# not the value here, or `flutter run` will no longer work.\n#\n# Any new source files that you add to the application should be added here.\nadd_executable(${BINARY_NAME}\n  \"main.cc\"\n  \"my_application.cc\"\n  \"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc\"\n)\n\n# Apply the standard set of build settings. This can be removed for applications\n# that need different build settings.\napply_standard_settings(${BINARY_NAME})\n\n# Add dependency libraries. Add any application-specific dependencies here.\ntarget_link_libraries(${BINARY_NAME} PRIVATE flutter)\ntarget_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)\ntarget_link_libraries(${BINARY_NAME} PRIVATE ${X11_LIBRARIES})\n\n# Run the Flutter tool portions of the build. This must not be removed.\nadd_dependencies(${BINARY_NAME} flutter_assemble)\n\n# Only the install-generated bundle's copy of the executable will launch\n# correctly, since the resources must in the right relative locations. To avoid\n# people trying to run the unbundled copy, put it in a subdirectory instead of\n# the default top-level location.\nset_target_properties(${BINARY_NAME}\n  PROPERTIES\n  RUNTIME_OUTPUT_DIRECTORY \"${CMAKE_BINARY_DIR}/intermediates_do_not_run\"\n)\n\n\n# Generated plugin build rules, which manage building the plugins and adding\n# them to the application.\ninclude(flutter/generated_plugins.cmake)\n\n\n# === Installation ===\n# By default, \"installing\" just makes a relocatable bundle in the build\n# directory.\nset(BUILD_BUNDLE_DIR \"${PROJECT_BINARY_DIR}/bundle\")\nif(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)\n  set(CMAKE_INSTALL_PREFIX \"${BUILD_BUNDLE_DIR}\" CACHE PATH \"...\" FORCE)\nendif()\n\n# Start with a clean build bundle directory every time.\ninstall(CODE \"\n  file(REMOVE_RECURSE \\\"${BUILD_BUNDLE_DIR}/\\\")\n  \" COMPONENT Runtime)\n\nset(INSTALL_BUNDLE_DATA_DIR \"${CMAKE_INSTALL_PREFIX}/data\")\nset(INSTALL_BUNDLE_LIB_DIR \"${CMAKE_INSTALL_PREFIX}/lib\")\n\ninstall(TARGETS ${BINARY_NAME} RUNTIME DESTINATION \"${CMAKE_INSTALL_PREFIX}\"\n  COMPONENT Runtime)\n\ninstall(FILES \"${FLUTTER_ICU_DATA_FILE}\" DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\"\n  COMPONENT Runtime)\n\ninstall(FILES \"${FLUTTER_LIBRARY}\" DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n  COMPONENT Runtime)\n\n# libclash.so\nset(CLASH_DIR \"../libclash/linux\")\ninstall(PROGRAMS \"${CLASH_DIR}/BettboxCore\" DESTINATION \"${BUILD_BUNDLE_DIR}\"\n  COMPONENT Runtime)\n\nforeach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})\n  install(FILES \"${bundled_library}\"\n    DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n    COMPONENT Runtime)\nendforeach(bundled_library)\n\nset(FLUTTER_JS_LIB \"${CMAKE_CURRENT_SOURCE_DIR}/flutter/ephemeral/.plugin_symlinks/flutter_js/linux/shared/libquickjs_c_bridge_plugin.so\")\nif(EXISTS \"${FLUTTER_JS_LIB}\")\n  install(FILES \"${FLUTTER_JS_LIB}\" DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\" COMPONENT Runtime)\nendif()\n\n# Fully re-copy the assets directory on each build to avoid having stale files\n# from a previous install.\nset(FLUTTER_ASSET_DIR_NAME \"flutter_assets\")\ninstall(CODE \"\n  file(REMOVE_RECURSE \\\"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\\\")\n  \" COMPONENT Runtime)\ninstall(DIRECTORY \"${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}\"\n  DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\" COMPONENT Runtime)\n\n# Install the AOT library on non-Debug builds only.\nif(NOT CMAKE_BUILD_TYPE MATCHES \"Debug\")\n  install(FILES \"${AOT_LIBRARY}\" DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n    COMPONENT Runtime)\nendif()"
  },
  {
    "path": "linux/flutter/CMakeLists.txt",
    "content": "# This file controls Flutter-level build steps. It should not be edited.\ncmake_minimum_required(VERSION 3.10)\n\nset(EPHEMERAL_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/ephemeral\")\n\n# Configuration provided via flutter tool.\ninclude(${EPHEMERAL_DIR}/generated_config.cmake)\n\n# TODO: Move the rest of this into files in ephemeral. See\n# https://github.com/flutter/flutter/issues/57146.\n\n# Serves the same purpose as list(TRANSFORM ... PREPEND ...),\n# which isn't available in 3.10.\nfunction(list_prepend LIST_NAME PREFIX)\n    set(NEW_LIST \"\")\n    foreach(element ${${LIST_NAME}})\n        list(APPEND NEW_LIST \"${PREFIX}${element}\")\n    endforeach(element)\n    set(${LIST_NAME} \"${NEW_LIST}\" PARENT_SCOPE)\nendfunction()\n\n# === Flutter Library ===\n# System-level dependencies.\nfind_package(PkgConfig REQUIRED)\npkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)\npkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)\npkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)\n\nset(FLUTTER_LIBRARY \"${EPHEMERAL_DIR}/libflutter_linux_gtk.so\")\n\n# Published to parent scope for install step.\nset(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)\nset(FLUTTER_ICU_DATA_FILE \"${EPHEMERAL_DIR}/icudtl.dat\" PARENT_SCOPE)\nset(PROJECT_BUILD_DIR \"${PROJECT_DIR}/build/\" PARENT_SCOPE)\nset(AOT_LIBRARY \"${PROJECT_DIR}/build/lib/libapp.so\" PARENT_SCOPE)\n\nlist(APPEND FLUTTER_LIBRARY_HEADERS\n  \"fl_basic_message_channel.h\"\n  \"fl_binary_codec.h\"\n  \"fl_binary_messenger.h\"\n  \"fl_dart_project.h\"\n  \"fl_engine.h\"\n  \"fl_json_message_codec.h\"\n  \"fl_json_method_codec.h\"\n  \"fl_message_codec.h\"\n  \"fl_method_call.h\"\n  \"fl_method_channel.h\"\n  \"fl_method_codec.h\"\n  \"fl_method_response.h\"\n  \"fl_plugin_registrar.h\"\n  \"fl_plugin_registry.h\"\n  \"fl_standard_message_codec.h\"\n  \"fl_standard_method_codec.h\"\n  \"fl_string_codec.h\"\n  \"fl_value.h\"\n  \"fl_view.h\"\n  \"flutter_linux.h\"\n)\nlist_prepend(FLUTTER_LIBRARY_HEADERS \"${EPHEMERAL_DIR}/flutter_linux/\")\nadd_library(flutter INTERFACE)\ntarget_include_directories(flutter INTERFACE\n  \"${EPHEMERAL_DIR}\"\n)\ntarget_link_libraries(flutter INTERFACE \"${FLUTTER_LIBRARY}\")\ntarget_link_libraries(flutter INTERFACE\n  PkgConfig::GTK\n  PkgConfig::GLIB\n  PkgConfig::GIO\n)\nadd_dependencies(flutter flutter_assemble)\n\n# === Flutter tool backend ===\n# _phony_ is a non-existent file to force this command to run every time,\n# since currently there's no way to get a full input/output list from the\n# flutter tool.\nadd_custom_command(\n  OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}\n    ${CMAKE_CURRENT_BINARY_DIR}/_phony_\n  COMMAND ${CMAKE_COMMAND} -E env\n    ${FLUTTER_TOOL_ENVIRONMENT}\n    \"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh\"\n      ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}\n  VERBATIM\n)\nadd_custom_target(flutter_assemble DEPENDS\n  \"${FLUTTER_LIBRARY}\"\n  ${FLUTTER_LIBRARY_HEADERS}\n)\n"
  },
  {
    "path": "linux/flutter/generated_plugin_registrant.cc",
    "content": "//\n//  Generated file. Do not edit.\n//\n\n// clang-format off\n\n#include \"generated_plugin_registrant.h\"\n\n#include <dynamic_color/dynamic_color_plugin.h>\n#include <file_selector_linux/file_selector_plugin.h>\n#include <flutter_acrylic/flutter_acrylic_plugin.h>\n#include <flutter_js/flutter_js_plugin.h>\n#include <gtk/gtk_plugin.h>\n#include <hotkey_manager_linux/hotkey_manager_linux_plugin.h>\n#include <screen_retriever_linux/screen_retriever_linux_plugin.h>\n#include <sentry_flutter/sentry_flutter_plugin.h>\n#include <tray_manager/tray_manager_plugin.h>\n#include <url_launcher_linux/url_launcher_plugin.h>\n#include <window_manager/window_manager_plugin.h>\n\nvoid fl_register_plugins(FlPluginRegistry* registry) {\n  g_autoptr(FlPluginRegistrar) dynamic_color_registrar =\n      fl_plugin_registry_get_registrar_for_plugin(registry, \"DynamicColorPlugin\");\n  dynamic_color_plugin_register_with_registrar(dynamic_color_registrar);\n  g_autoptr(FlPluginRegistrar) file_selector_linux_registrar =\n      fl_plugin_registry_get_registrar_for_plugin(registry, \"FileSelectorPlugin\");\n  file_selector_plugin_register_with_registrar(file_selector_linux_registrar);\n  g_autoptr(FlPluginRegistrar) flutter_acrylic_registrar =\n      fl_plugin_registry_get_registrar_for_plugin(registry, \"FlutterAcrylicPlugin\");\n  flutter_acrylic_plugin_register_with_registrar(flutter_acrylic_registrar);\n  g_autoptr(FlPluginRegistrar) flutter_js_registrar =\n      fl_plugin_registry_get_registrar_for_plugin(registry, \"FlutterJsPlugin\");\n  flutter_js_plugin_register_with_registrar(flutter_js_registrar);\n  g_autoptr(FlPluginRegistrar) gtk_registrar =\n      fl_plugin_registry_get_registrar_for_plugin(registry, \"GtkPlugin\");\n  gtk_plugin_register_with_registrar(gtk_registrar);\n  g_autoptr(FlPluginRegistrar) hotkey_manager_linux_registrar =\n      fl_plugin_registry_get_registrar_for_plugin(registry, \"HotkeyManagerLinuxPlugin\");\n  hotkey_manager_linux_plugin_register_with_registrar(hotkey_manager_linux_registrar);\n  g_autoptr(FlPluginRegistrar) screen_retriever_linux_registrar =\n      fl_plugin_registry_get_registrar_for_plugin(registry, \"ScreenRetrieverLinuxPlugin\");\n  screen_retriever_linux_plugin_register_with_registrar(screen_retriever_linux_registrar);\n  g_autoptr(FlPluginRegistrar) sentry_flutter_registrar =\n      fl_plugin_registry_get_registrar_for_plugin(registry, \"SentryFlutterPlugin\");\n  sentry_flutter_plugin_register_with_registrar(sentry_flutter_registrar);\n  g_autoptr(FlPluginRegistrar) tray_manager_registrar =\n      fl_plugin_registry_get_registrar_for_plugin(registry, \"TrayManagerPlugin\");\n  tray_manager_plugin_register_with_registrar(tray_manager_registrar);\n  g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar =\n      fl_plugin_registry_get_registrar_for_plugin(registry, \"UrlLauncherPlugin\");\n  url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar);\n  g_autoptr(FlPluginRegistrar) window_manager_registrar =\n      fl_plugin_registry_get_registrar_for_plugin(registry, \"WindowManagerPlugin\");\n  window_manager_plugin_register_with_registrar(window_manager_registrar);\n}\n"
  },
  {
    "path": "linux/flutter/generated_plugin_registrant.h",
    "content": "//\n//  Generated file. Do not edit.\n//\n\n// clang-format off\n\n#ifndef GENERATED_PLUGIN_REGISTRANT_\n#define GENERATED_PLUGIN_REGISTRANT_\n\n#include <flutter_linux/flutter_linux.h>\n\n// Registers Flutter plugins.\nvoid fl_register_plugins(FlPluginRegistry* registry);\n\n#endif  // GENERATED_PLUGIN_REGISTRANT_\n"
  },
  {
    "path": "linux/flutter/generated_plugins.cmake",
    "content": "#\n# Generated file, do not edit.\n#\n\nlist(APPEND FLUTTER_PLUGIN_LIST\n  dynamic_color\n  file_selector_linux\n  flutter_acrylic\n  flutter_js\n  gtk\n  hotkey_manager_linux\n  screen_retriever_linux\n  sentry_flutter\n  tray_manager\n  url_launcher_linux\n  window_manager\n)\n\nlist(APPEND FLUTTER_FFI_PLUGIN_LIST\n  jni\n)\n\nset(PLUGIN_BUNDLED_LIBRARIES)\n\nforeach(plugin ${FLUTTER_PLUGIN_LIST})\n  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})\n  target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})\nendforeach(plugin)\n\nforeach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})\n  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})\nendforeach(ffi_plugin)\n"
  },
  {
    "path": "linux/main.cc",
    "content": "#include \"my_application.h\"\n#include <X11/Xlib.h>\n\nint main(int argc, char** argv) {\n  XInitThreads();\n\n  g_autoptr(MyApplication) app = my_application_new();\n  return g_application_run(G_APPLICATION(app), argc, argv);\n}\n"
  },
  {
    "path": "linux/my_application.cc",
    "content": "#include \"my_application.h\"\n\n#include <flutter_linux/flutter_linux.h>\n#ifdef GDK_WINDOWING_X11\n#include <gdk/gdkx.h>\n#endif\n\n#include \"flutter/generated_plugin_registrant.h\"\n\n// App method channel related\nstatic FlMethodChannel* app_channel = nullptr;\nstatic GtkWindow* main_window = nullptr;\nstatic gboolean use_light_icon = FALSE;\n\n// Forward declarations\nstatic void setup_app_method_channel(FlView* view);\nstatic gboolean set_window_icon(gboolean use_light);\nstatic void save_icon_preference(gboolean use_light);\nstatic gboolean load_icon_preference();\n\nstruct _MyApplication {\n  GtkApplication parent_instance;\n  char** dart_entrypoint_arguments;\n};\n\nG_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)\n\n// Implements GApplication::activate.\nstatic void my_application_activate(GApplication* application) {\n  MyApplication* self = MY_APPLICATION(application);\n  GtkWindow* window =\n      GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));\n\n  // Use a header bar when running in GNOME as this is the common style used\n  // by applications and is the setup most users will be using (e.g. Ubuntu\n  // desktop).\n  // If running on X and not using GNOME then just use a traditional title bar\n  // in case the window manager does more exotic layout, e.g. tiling.\n  // If running on Wayland assume the header bar will work (may need changing\n  // if future cases occur).\n  gboolean use_header_bar = TRUE;\n#ifdef GDK_WINDOWING_X11\n  GdkScreen* screen = gtk_window_get_screen(window);\n  if (GDK_IS_X11_SCREEN(screen)) {\n    const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);\n    if (g_strcmp0(wm_name, \"GNOME Shell\") != 0) {\n      use_header_bar = FALSE;\n    }\n  }\n#endif\n  if (use_header_bar) {\n    GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());\n    gtk_widget_show(GTK_WIDGET(header_bar));\n    gtk_header_bar_set_title(header_bar, \"Bettbox\");\n    gtk_header_bar_set_show_close_button(header_bar, TRUE);\n    gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));\n  } else {\n    gtk_window_set_title(window, \"Bettbox\");\n  }\n\n  gtk_window_set_default_size(window, 1280, 720);\n  gtk_widget_realize(GTK_WIDGET(window));\n  \n  // Save window reference\n  main_window = window;\n\n  g_autoptr(FlDartProject) project = fl_dart_project_new();\n  fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);\n\n  FlView* view = fl_view_new(project);\n  gtk_widget_show(GTK_WIDGET(view));\n  gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));\n\n  fl_register_plugins(FL_PLUGIN_REGISTRY(view));\n  \n  // Setup app method channel\n  setup_app_method_channel(view);\n  \n  // Load and apply saved icon preference\n  use_light_icon = load_icon_preference();\n  if (use_light_icon) {\n    set_window_icon(TRUE);\n  }\n\n  gtk_widget_grab_focus(GTK_WIDGET(view));\n}\n\n// Implements GApplication::local_command_line.\nstatic gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) {\n  MyApplication* self = MY_APPLICATION(application);\n  // Strip out the first argument as it is the binary name.\n  self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);\n\n  g_autoptr(GError) error = nullptr;\n  if (!g_application_register(application, nullptr, &error)) {\n     g_warning(\"Failed to register: %s\", error->message);\n     *exit_status = 1;\n     return TRUE;\n  }\n\n  g_application_activate(application);\n  *exit_status = 0;\n\n  return TRUE;\n}\n\n// Implements GApplication::startup.\nstatic void my_application_startup(GApplication* application) {\n  //MyApplication* self = MY_APPLICATION(object);\n\n  // Perform any actions required at application startup.\n\n  G_APPLICATION_CLASS(my_application_parent_class)->startup(application);\n}\n\n// Implements GApplication::shutdown.\nstatic void my_application_shutdown(GApplication* application) {\n  //MyApplication* self = MY_APPLICATION(object);\n\n  // Perform any actions required at application shutdown.\n\n  G_APPLICATION_CLASS(my_application_parent_class)->shutdown(application);\n}\n\n// Implements GObject::dispose.\nstatic void my_application_dispose(GObject* object) {\n  MyApplication* self = MY_APPLICATION(object);\n  g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);\n  G_OBJECT_CLASS(my_application_parent_class)->dispose(object);\n}\n\nstatic void my_application_class_init(MyApplicationClass* klass) {\n  G_APPLICATION_CLASS(klass)->activate = my_application_activate;\n  G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;\n  G_APPLICATION_CLASS(klass)->startup = my_application_startup;\n  G_APPLICATION_CLASS(klass)->shutdown = my_application_shutdown;\n  G_OBJECT_CLASS(klass)->dispose = my_application_dispose;\n}\n\nstatic void my_application_init(MyApplication* self) {}\n\nMyApplication* my_application_new() {\n  // Set the program name to the application ID, which helps various systems\n  // like GTK and desktop environments map this running application to its\n  // corresponding .desktop file. This ensures better integration by allowing\n  // the application to be recognized beyond its binary name.\n  g_set_prgname(APPLICATION_ID);\n\n  return MY_APPLICATION(g_object_new(my_application_get_type(),\n                                     \"application-id\", APPLICATION_ID,\n                                     nullptr));\n}\n\n// App method channel implementation\n\nstatic void app_method_call_handler(FlMethodChannel* channel,\n                                    FlMethodCall* method_call,\n                                    gpointer user_data) {\n  const gchar* method = fl_method_call_get_name(method_call);\n  \n  if (strcmp(method, \"setLauncherIcon\") == 0) {\n    FlValue* args = fl_method_call_get_args(method_call);\n    if (fl_value_get_type(args) == FL_VALUE_TYPE_MAP) {\n      FlValue* use_light_value = fl_value_lookup_string(args, \"useLightIcon\");\n      if (use_light_value != nullptr && fl_value_get_type(use_light_value) == FL_VALUE_TYPE_BOOL) {\n        gboolean use_light = fl_value_get_bool(use_light_value);\n        gboolean success = set_window_icon(use_light);\n        \n        g_autoptr(FlValue) result = fl_value_new_bool(success);\n        fl_method_call_respond_success(method_call, result, nullptr);\n        return;\n      }\n    }\n    \n    fl_method_call_respond_error(method_call, \"INVALID_ARGUMENT\",\n                                 \"Missing useLightIcon argument\", nullptr, nullptr);\n  } else {\n    fl_method_call_respond_not_implemented(method_call, nullptr);\n  }\n}\n\nstatic void setup_app_method_channel(FlView* view) {\n  FlEngine* engine = fl_view_get_engine(view);\n  FlBinaryMessenger* messenger = fl_engine_get_binary_messenger(engine);\n  \n  g_autoptr(FlStandardMethodCodec) codec = fl_standard_method_codec_new();\n  app_channel = fl_method_channel_new(messenger, \"app\", FL_METHOD_CODEC(codec));\n  \n  fl_method_channel_set_method_call_handler(app_channel, app_method_call_handler,\n                                           nullptr, nullptr);\n}\n\nstatic gboolean set_window_icon(gboolean use_light) {\n  if (main_window == nullptr) {\n    return FALSE;\n  }\n  \n  // Icon file path\n  const gchar* icon_name = use_light ? \"icon_light.png\" : \"icon.png\";\n  gchar* icon_path = g_strdup_printf(\"data/flutter_assets/assets/images/%s\", icon_name);\n  \n  // Load icon\n  GError* error = nullptr;\n  GdkPixbuf* pixbuf = gdk_pixbuf_new_from_file(icon_path, &error);\n  g_free(icon_path);\n  \n  if (error != nullptr) {\n    g_warning(\"Failed to load icon: %s\", error->message);\n    g_error_free(error);\n    return FALSE;\n  }\n  \n  if (pixbuf == nullptr) {\n    return FALSE;\n  }\n  \n  // Set window icon\n  gtk_window_set_icon(main_window, pixbuf);\n  g_object_unref(pixbuf);\n  \n  // Save preference\n  use_light_icon = use_light;\n  save_icon_preference(use_light);\n  \n  return TRUE;\n}\n\nstatic void save_icon_preference(gboolean use_light) {\n  // Save to config file\n  const gchar* config_dir = g_get_user_config_dir();\n  gchar* app_config_dir = g_build_filename(config_dir, \"bettbox\", nullptr);\n  \n  // Create config directory\n  g_mkdir_with_parents(app_config_dir, 0755);\n  \n  gchar* config_file = g_build_filename(app_config_dir, \"icon_preference\", nullptr);\n  \n  // Write config\n  const gchar* value = use_light ? \"1\" : \"0\";\n  GError* error = nullptr;\n  g_file_set_contents(config_file, value, -1, &error);\n  \n  if (error != nullptr) {\n    g_warning(\"Failed to save icon preference: %s\", error->message);\n    g_error_free(error);\n  }\n  \n  g_free(config_file);\n  g_free(app_config_dir);\n}\n\nstatic gboolean load_icon_preference() {\n  const gchar* config_dir = g_get_user_config_dir();\n  gchar* config_file = g_build_filename(config_dir, \"bettbox\", \"icon_preference\", nullptr);\n  \n  gchar* contents = nullptr;\n  GError* error = nullptr;\n  gboolean result = FALSE;\n  \n  if (g_file_get_contents(config_file, &contents, nullptr, &error)) {\n    result = (g_strcmp0(contents, \"1\") == 0);\n    g_free(contents);\n  } else if (error != nullptr) {\n    // File not found or read failed, use default\n    g_error_free(error);\n  }\n  \n  g_free(config_file);\n  return result;\n}\n"
  },
  {
    "path": "linux/my_application.h",
    "content": "#ifndef FLUTTER_MY_APPLICATION_H_\n#define FLUTTER_MY_APPLICATION_H_\n\n#include <gtk/gtk.h>\n\nG_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,\n                     GtkApplication)\n\n/**\n * my_application_new:\n *\n * Creates a new Flutter-based application.\n *\n * Returns: a new #MyApplication.\n */\nMyApplication* my_application_new();\n\n#endif  // FLUTTER_MY_APPLICATION_H_\n"
  },
  {
    "path": "linux/packaging/appimage/make_config.yaml",
    "content": "display_name: Bettbox\n\nicon: ./assets/images/icon.png\n\nkeywords:\n  - Bettbox\n  - Clash\n  - ClashMeta\n  - Proxy\n\ngeneric_name: Bettbox\n\ncategories:\n  - Network\n\nstartup_notify: true\n\ninclude: []"
  },
  {
    "path": "linux/packaging/deb/make_config.yaml",
    "content": "display_name: Bettbox\npackage_name: Bettbox\nmaintainer:\n  name: appshub\n  email: appshubcc@gmail.com\n\npriority: optional\nsection: x11\ninstalled_size: 6604\nessential: false\nicon: ./assets/images/icon.png\n\ndependencies:\n  - libayatana-appindicator3-dev\n  - libkeybinder-3.0-dev\n\nkeywords:\n  - Bettbox\n  - Clash\n  - ClashMeta\n  - Mihomo\n  - Proxy\n\ngeneric_name: Bettbox\n\ncategories:\n  - Network\n\nstartup_notify: true"
  },
  {
    "path": "linux/packaging/rpm/make_config.yaml",
    "content": "display_name: Bettbox\n\npackager: appshub\npackagerEmail: appshubcc@gmail.com\nlicense: Other\n\npriority: optional\nsection: x11\ninstalled_size: 6604\nessential: false\nicon: ./assets/images/icon.png\n\nkeywords:\n  - Bettbox\n  - Clash\n  - ClashMeta\n  - Proxy\n\ngeneric_name: Bettbox\n\ngroup: Applications/Internet\n\nstartup_notify: true"
  },
  {
    "path": "macos/.gitignore",
    "content": "# Flutter-related\n**/Flutter/ephemeral/\n**/Pods/\n\n# Xcode-related\n**/dgph\n**/xcuserdata/\n"
  },
  {
    "path": "macos/Flutter/Flutter-Debug.xcconfig",
    "content": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"\n#include \"ephemeral/Flutter-Generated.xcconfig\"\n"
  },
  {
    "path": "macos/Flutter/Flutter-Release.xcconfig",
    "content": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"\n#include \"ephemeral/Flutter-Generated.xcconfig\"\n"
  },
  {
    "path": "macos/Flutter/GeneratedPluginRegistrant.swift",
    "content": "//\n//  Generated file. Do not edit.\n//\n\nimport FlutterMacOS\nimport Foundation\n\nimport app_links\nimport connectivity_plus\nimport device_info_plus\nimport dynamic_color\nimport file_picker\nimport file_selector_macos\nimport flutter_js\nimport hotkey_manager_macos\nimport macos_window_utils\nimport mobile_scanner\nimport package_info_plus\nimport path_provider_foundation\nimport screen_retriever_macos\nimport sentry_flutter\nimport shared_preferences_foundation\nimport sqflite_darwin\nimport tray_manager\nimport url_launcher_macos\nimport wakelock_plus\nimport window_ext\nimport window_manager\n\nfunc RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {\n  AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: \"AppLinksMacosPlugin\"))\n  ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: \"ConnectivityPlusPlugin\"))\n  DeviceInfoPlusMacosPlugin.register(with: registry.registrar(forPlugin: \"DeviceInfoPlusMacosPlugin\"))\n  DynamicColorPlugin.register(with: registry.registrar(forPlugin: \"DynamicColorPlugin\"))\n  FilePickerPlugin.register(with: registry.registrar(forPlugin: \"FilePickerPlugin\"))\n  FileSelectorPlugin.register(with: registry.registrar(forPlugin: \"FileSelectorPlugin\"))\n  FlutterJsPlugin.register(with: registry.registrar(forPlugin: \"FlutterJsPlugin\"))\n  HotkeyManagerMacosPlugin.register(with: registry.registrar(forPlugin: \"HotkeyManagerMacosPlugin\"))\n  MacOSWindowUtilsPlugin.register(with: registry.registrar(forPlugin: \"MacOSWindowUtilsPlugin\"))\n  MobileScannerPlugin.register(with: registry.registrar(forPlugin: \"MobileScannerPlugin\"))\n  FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: \"FPPPackageInfoPlusPlugin\"))\n  PathProviderPlugin.register(with: registry.registrar(forPlugin: \"PathProviderPlugin\"))\n  ScreenRetrieverMacosPlugin.register(with: registry.registrar(forPlugin: \"ScreenRetrieverMacosPlugin\"))\n  SentryFlutterPlugin.register(with: registry.registrar(forPlugin: \"SentryFlutterPlugin\"))\n  SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: \"SharedPreferencesPlugin\"))\n  SqflitePlugin.register(with: registry.registrar(forPlugin: \"SqflitePlugin\"))\n  TrayManagerPlugin.register(with: registry.registrar(forPlugin: \"TrayManagerPlugin\"))\n  UrlLauncherPlugin.register(with: registry.registrar(forPlugin: \"UrlLauncherPlugin\"))\n  WakelockPlusMacosPlugin.register(with: registry.registrar(forPlugin: \"WakelockPlusMacosPlugin\"))\n  WindowExtPlugin.register(with: registry.registrar(forPlugin: \"WindowExtPlugin\"))\n  WindowManagerPlugin.register(with: registry.registrar(forPlugin: \"WindowManagerPlugin\"))\n}\n"
  },
  {
    "path": "macos/Podfile",
    "content": "platform :osx, '10.15'\n\n# CocoaPods analytics sends network stats synchronously affecting flutter build latency.\nENV['COCOAPODS_DISABLE_STATS'] = 'true'\n\nproject 'Runner', {\n  'Debug' => :debug,\n  'Profile' => :release,\n  'Release' => :release,\n}\n\ndef flutter_root\n  generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)\n  unless File.exist?(generated_xcode_build_settings_path)\n    raise \"#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \\\"flutter pub get\\\" is executed first\"\n  end\n\n  File.foreach(generated_xcode_build_settings_path) do |line|\n    matches = line.match(/FLUTTER_ROOT\\=(.*)/)\n    return matches[1].strip if matches\n  end\n  raise \"FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \\\"flutter pub get\\\"\"\nend\n\nrequire File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)\n\nflutter_macos_podfile_setup\n\ntarget 'Runner' do\n  use_frameworks!\n  use_modular_headers!\n\n  flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))\n  target 'RunnerTests' do\n    inherit! :search_paths\n  end\nend\n\npost_install do |installer|\n  installer.pods_project.targets.each do |target|\n    flutter_additional_macos_build_settings(target)\n  end\nend\n"
  },
  {
    "path": "macos/Runner/AppDelegate.swift",
    "content": "import Cocoa\nimport FlutterMacOS\nimport window_ext\n\n@main\nclass AppDelegate: FlutterAppDelegate {\n    \n    override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {\n        return false\n    }\n    \n    override func applicationShouldTerminate(_ sender: NSApplication) -> NSApplication.TerminateReply {\n        WindowExtPlugin.instance?.handleShouldTerminate()\n        return .terminateCancel\n    }\n\n    override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool {\n      return true\n    }\n    \n    override func applicationShouldHandleReopen(_ sender: NSApplication, hasVisibleWindows flag: Bool) -> Bool {\n        if !flag {\n            for window in NSApp.windows {\n                if !window.isVisible {\n                    window.setIsVisible(true)\n                }\n                window.makeKeyAndOrderFront(self)\n                NSApp.activate(ignoringOtherApps: true)\n            }\n        }\n        return true\n    }\n}\n"
  },
  {
    "path": "macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n    \"images\": [\n        {\n            \"size\": \"16x16\",\n            \"idiom\": \"mac\",\n            \"filename\": \"app_icon_16.png\",\n            \"scale\": \"1x\"\n        },\n        {\n            \"size\": \"16x16\",\n            \"idiom\": \"mac\",\n            \"filename\": \"app_icon_32.png\",\n            \"scale\": \"2x\"\n        },\n        {\n            \"size\": \"32x32\",\n            \"idiom\": \"mac\",\n            \"filename\": \"app_icon_32.png\",\n            \"scale\": \"1x\"\n        },\n        {\n            \"size\": \"32x32\",\n            \"idiom\": \"mac\",\n            \"filename\": \"app_icon_64.png\",\n            \"scale\": \"2x\"\n        },\n        {\n            \"size\": \"128x128\",\n            \"idiom\": \"mac\",\n            \"filename\": \"app_icon_128.png\",\n            \"scale\": \"1x\"\n        },\n        {\n            \"size\": \"128x128\",\n            \"idiom\": \"mac\",\n            \"filename\": \"app_icon_256.png\",\n            \"scale\": \"2x\"\n        },\n        {\n            \"size\": \"256x256\",\n            \"idiom\": \"mac\",\n            \"filename\": \"app_icon_256.png\",\n            \"scale\": \"1x\"\n        },\n        {\n            \"size\": \"256x256\",\n            \"idiom\": \"mac\",\n            \"filename\": \"app_icon_512.png\",\n            \"scale\": \"2x\"\n        },\n        {\n            \"size\": \"512x512\",\n            \"idiom\": \"mac\",\n            \"filename\": \"app_icon_512.png\",\n            \"scale\": \"1x\"\n        },\n        {\n            \"size\": \"512x512\",\n            \"idiom\": \"mac\",\n            \"filename\": \"app_icon_1024.png\",\n            \"scale\": \"2x\"\n        }\n    ],\n    \"info\": {\n        \"version\": 1,\n        \"author\": \"xcode\"\n    }\n}"
  },
  {
    "path": "macos/Runner/Assets.xcassets/Contents.json",
    "content": "{\n  \"info\" : {\n    \"author\" : \"xcode\",\n    \"version\" : 1\n  }\n}\n"
  },
  {
    "path": "macos/Runner/Base.lproj/MainMenu.xib",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion=\"21701\" targetRuntime=\"MacOSX.Cocoa\" propertyAccessControl=\"none\" useAutolayout=\"YES\" customObjectInstantitationMethod=\"direct\">\n    <dependencies>\n        <deployment identifier=\"macosx\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.CocoaPlugin\" version=\"21701\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <objects>\n        <customObject id=\"-2\" userLabel=\"File's Owner\" customClass=\"NSApplication\">\n            <connections>\n                <outlet property=\"delegate\" destination=\"Voe-Tx-rLC\" id=\"GzC-gU-4Uq\"/>\n            </connections>\n        </customObject>\n        <customObject id=\"-1\" userLabel=\"First Responder\" customClass=\"FirstResponder\"/>\n        <customObject id=\"-3\" userLabel=\"Application\" customClass=\"NSObject\"/>\n        <customObject id=\"Voe-Tx-rLC\" customClass=\"AppDelegate\" customModule=\"Bettbox\" customModuleProvider=\"target\">\n            <connections>\n                <outlet property=\"applicationMenu\" destination=\"uQy-DD-JDr\" id=\"XBo-yE-nKs\"/>\n                <outlet property=\"mainFlutterWindow\" destination=\"QvC-M9-y7g\" id=\"gIp-Ho-8D9\"/>\n            </connections>\n        </customObject>\n        <customObject id=\"YLy-65-1bz\" customClass=\"NSFontManager\"/>\n        <menu title=\"Main Menu\" systemMenu=\"main\" id=\"AYu-sK-qS6\">\n            <items>\n                <menuItem title=\"APP_NAME\" id=\"1Xt-HY-uBw\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"APP_NAME\" systemMenu=\"apple\" id=\"uQy-DD-JDr\">\n                        <items>\n                            <menuItem title=\"About APP_NAME\" id=\"5kV-Vb-QxS\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"orderFrontStandardAboutPanel:\" target=\"-1\" id=\"Exp-CZ-Vem\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"VOq-y0-SEH\"/>\n                            <menuItem title=\"Preferences…\" keyEquivalent=\",\" id=\"BOF-NM-1cW\"/>\n                            <menuItem isSeparatorItem=\"YES\" id=\"wFC-TO-SCJ\"/>\n                            <menuItem title=\"Services\" id=\"NMo-om-nkz\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Services\" systemMenu=\"services\" id=\"hz9-B4-Xy5\"/>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"4je-JR-u6R\"/>\n                            <menuItem title=\"Hide APP_NAME\" keyEquivalent=\"h\" id=\"Olw-nP-bQN\">\n                                <connections>\n                                    <action selector=\"hide:\" target=\"-1\" id=\"PnN-Uc-m68\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Hide Others\" keyEquivalent=\"h\" id=\"Vdr-fp-XzO\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"hideOtherApplications:\" target=\"-1\" id=\"VT4-aY-XCT\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Show All\" id=\"Kd2-mp-pUS\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"unhideAllApplications:\" target=\"-1\" id=\"Dhg-Le-xox\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"kCx-OE-vgT\"/>\n                            <menuItem title=\"Quit APP_NAME\" keyEquivalent=\"q\" id=\"4sb-4s-VLi\">\n                                <connections>\n                                    <action selector=\"terminate:\" target=\"-1\" id=\"Te7-pn-YzF\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"Edit\" id=\"5QF-Oa-p0T\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"Edit\" id=\"W48-6f-4Dl\">\n                        <items>\n                            <menuItem title=\"Undo\" keyEquivalent=\"z\" id=\"dRJ-4n-Yzg\">\n                                <connections>\n                                    <action selector=\"undo:\" target=\"-1\" id=\"M6e-cu-g7V\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Redo\" keyEquivalent=\"Z\" id=\"6dh-zS-Vam\">\n                                <connections>\n                                    <action selector=\"redo:\" target=\"-1\" id=\"oIA-Rs-6OD\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"WRV-NI-Exz\"/>\n                            <menuItem title=\"Cut\" keyEquivalent=\"x\" id=\"uRl-iY-unG\">\n                                <connections>\n                                    <action selector=\"cut:\" target=\"-1\" id=\"YJe-68-I9s\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Copy\" keyEquivalent=\"c\" id=\"x3v-GG-iWU\">\n                                <connections>\n                                    <action selector=\"copy:\" target=\"-1\" id=\"G1f-GL-Joy\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Paste\" keyEquivalent=\"v\" id=\"gVA-U4-sdL\">\n                                <connections>\n                                    <action selector=\"paste:\" target=\"-1\" id=\"UvS-8e-Qdg\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Paste and Match Style\" keyEquivalent=\"V\" id=\"WeT-3V-zwk\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"pasteAsPlainText:\" target=\"-1\" id=\"cEh-KX-wJQ\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Delete\" id=\"pa3-QI-u2k\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"delete:\" target=\"-1\" id=\"0Mk-Ml-PaM\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Select All\" keyEquivalent=\"a\" id=\"Ruw-6m-B2m\">\n                                <connections>\n                                    <action selector=\"selectAll:\" target=\"-1\" id=\"VNm-Mi-diN\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"uyl-h8-XO2\"/>\n                            <menuItem title=\"Find\" id=\"4EN-yA-p0u\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Find\" id=\"1b7-l0-nxx\">\n                                    <items>\n                                        <menuItem title=\"Find…\" tag=\"1\" keyEquivalent=\"f\" id=\"Xz5-n4-O0W\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"cD7-Qs-BN4\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Find and Replace…\" tag=\"12\" keyEquivalent=\"f\" id=\"YEy-JH-Tfz\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"WD3-Gg-5AJ\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Find Next\" tag=\"2\" keyEquivalent=\"g\" id=\"q09-fT-Sye\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"NDo-RZ-v9R\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Find Previous\" tag=\"3\" keyEquivalent=\"G\" id=\"OwM-mh-QMV\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"HOh-sY-3ay\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Use Selection for Find\" tag=\"7\" keyEquivalent=\"e\" id=\"buJ-ug-pKt\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"U76-nv-p5D\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Jump to Selection\" keyEquivalent=\"j\" id=\"S0p-oC-mLd\">\n                                            <connections>\n                                                <action selector=\"centerSelectionInVisibleArea:\" target=\"-1\" id=\"IOG-6D-g5B\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Spelling and Grammar\" id=\"Dv1-io-Yv7\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Spelling\" id=\"3IN-sU-3Bg\">\n                                    <items>\n                                        <menuItem title=\"Show Spelling and Grammar\" keyEquivalent=\":\" id=\"HFo-cy-zxI\">\n                                            <connections>\n                                                <action selector=\"showGuessPanel:\" target=\"-1\" id=\"vFj-Ks-hy3\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Check Document Now\" keyEquivalent=\";\" id=\"hz2-CU-CR7\">\n                                            <connections>\n                                                <action selector=\"checkSpelling:\" target=\"-1\" id=\"fz7-VC-reM\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"bNw-od-mp5\"/>\n                                        <menuItem title=\"Check Spelling While Typing\" id=\"rbD-Rh-wIN\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleContinuousSpellChecking:\" target=\"-1\" id=\"7w6-Qz-0kB\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Check Grammar With Spelling\" id=\"mK6-2p-4JG\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleGrammarChecking:\" target=\"-1\" id=\"muD-Qn-j4w\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Correct Spelling Automatically\" id=\"78Y-hA-62v\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticSpellingCorrection:\" target=\"-1\" id=\"2lM-Qi-WAP\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Substitutions\" id=\"9ic-FL-obx\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Substitutions\" id=\"FeM-D8-WVr\">\n                                    <items>\n                                        <menuItem title=\"Show Substitutions\" id=\"z6F-FW-3nz\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"orderFrontSubstitutionsPanel:\" target=\"-1\" id=\"oku-mr-iSq\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"gPx-C9-uUO\"/>\n                                        <menuItem title=\"Smart Copy/Paste\" id=\"9yt-4B-nSM\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleSmartInsertDelete:\" target=\"-1\" id=\"3IJ-Se-DZD\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smart Quotes\" id=\"hQb-2v-fYv\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticQuoteSubstitution:\" target=\"-1\" id=\"ptq-xd-QOA\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smart Dashes\" id=\"rgM-f4-ycn\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticDashSubstitution:\" target=\"-1\" id=\"oCt-pO-9gS\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smart Links\" id=\"cwL-P1-jid\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticLinkDetection:\" target=\"-1\" id=\"Gip-E3-Fov\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Data Detectors\" id=\"tRr-pd-1PS\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticDataDetection:\" target=\"-1\" id=\"R1I-Nq-Kbl\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Text Replacement\" id=\"HFQ-gK-NFA\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticTextReplacement:\" target=\"-1\" id=\"DvP-Fe-Py6\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Transformations\" id=\"2oI-Rn-ZJC\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Transformations\" id=\"c8a-y6-VQd\">\n                                    <items>\n                                        <menuItem title=\"Make Upper Case\" id=\"vmV-6d-7jI\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"uppercaseWord:\" target=\"-1\" id=\"sPh-Tk-edu\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Make Lower Case\" id=\"d9M-CD-aMd\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"lowercaseWord:\" target=\"-1\" id=\"iUZ-b5-hil\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Capitalize\" id=\"UEZ-Bs-lqG\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"capitalizeWord:\" target=\"-1\" id=\"26H-TL-nsh\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Speech\" id=\"xrE-MZ-jX0\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Speech\" id=\"3rS-ZA-NoH\">\n                                    <items>\n                                        <menuItem title=\"Start Speaking\" id=\"Ynk-f8-cLZ\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"startSpeaking:\" target=\"-1\" id=\"654-Ng-kyl\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Stop Speaking\" id=\"Oyz-dy-DGm\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"stopSpeaking:\" target=\"-1\" id=\"dX8-6p-jy9\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"View\" id=\"H8h-7b-M4v\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"View\" id=\"HyV-fh-RgO\">\n                        <items>\n                            <menuItem title=\"Enter Full Screen\" keyEquivalent=\"f\" id=\"4J7-dP-txa\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" control=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"toggleFullScreen:\" target=\"-1\" id=\"dU3-MA-1Rq\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"Window\" id=\"aUF-d1-5bR\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"Window\" systemMenu=\"window\" id=\"Td7-aD-5lo\">\n                        <items>\n                            <menuItem title=\"Minimize\" keyEquivalent=\"m\" id=\"OY7-WF-poV\">\n                                <connections>\n                                    <action selector=\"performMiniaturize:\" target=\"-1\" id=\"VwT-WD-YPe\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Zoom\" id=\"R4o-n2-Eq4\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"performZoom:\" target=\"-1\" id=\"DIl-cC-cCs\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"eu3-7i-yIM\"/>\n                            <menuItem title=\"Bring All to Front\" id=\"LE2-aR-0XJ\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"arrangeInFront:\" target=\"-1\" id=\"DRN-fu-gQh\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"Help\" id=\"EPT-qC-fAb\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"Help\" systemMenu=\"help\" id=\"rJ0-wn-3NY\"/>\n                </menuItem>\n            </items>\n            <point key=\"canvasLocation\" x=\"142\" y=\"-258\"/>\n        </menu>\n        <window title=\"APP_NAME\" allowsToolTipsWhenApplicationIsInactive=\"NO\" autorecalculatesKeyViewLoop=\"NO\" releasedWhenClosed=\"NO\" animationBehavior=\"default\" id=\"QvC-M9-y7g\" customClass=\"MainFlutterWindow\" customModule=\"Bettbox\" customModuleProvider=\"target\">\n            <windowStyleMask key=\"styleMask\" titled=\"YES\" closable=\"YES\" miniaturizable=\"YES\" resizable=\"YES\"/>\n            <rect key=\"contentRect\" x=\"335\" y=\"390\" width=\"800\" height=\"600\"/>\n            <rect key=\"screenRect\" x=\"0.0\" y=\"0.0\" width=\"1280\" height=\"775\"/>\n            <view key=\"contentView\" wantsLayer=\"YES\" id=\"EiT-Mj-1SZ\">\n                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"800\" height=\"600\"/>\n                <autoresizingMask key=\"autoresizingMask\"/>\n            </view>\n        </window>\n    </objects>\n</document>\n"
  },
  {
    "path": "macos/Runner/Configs/AppInfo.xcconfig",
    "content": "// Application-level settings for the Runner target.\n//\n// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the\n// future. If not, the values below would default to using the project name when this becomes a\n// 'flutter create' template.\n\n// The application's name. By default this is also the title of the Flutter window.\nPRODUCT_NAME = Bettbox\n\n// The application's bundle identifier\nPRODUCT_BUNDLE_IDENTIFIER = com.appshub.bettbox\n\n// The copyright displayed in application information\nPRODUCT_COPYRIGHT = Copyright © 2026 Appshub All rights reserved.\n"
  },
  {
    "path": "macos/Runner/Configs/Debug.xcconfig",
    "content": "#include \"../../Flutter/Flutter-Debug.xcconfig\"\n#include \"Warnings.xcconfig\"\n"
  },
  {
    "path": "macos/Runner/Configs/Release.xcconfig",
    "content": "#include \"../../Flutter/Flutter-Release.xcconfig\"\n#include \"Warnings.xcconfig\"\n"
  },
  {
    "path": "macos/Runner/Configs/Warnings.xcconfig",
    "content": "WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings\nGCC_WARN_UNDECLARED_SELECTOR = YES\nCLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES\nCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE\nCLANG_WARN__DUPLICATE_METHOD_MATCH = YES\nCLANG_WARN_PRAGMA_PACK = YES\nCLANG_WARN_STRICT_PROTOTYPES = YES\nCLANG_WARN_COMMA = YES\nGCC_WARN_STRICT_SELECTOR_MATCH = YES\nCLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES\nCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES\nGCC_WARN_SHADOW = YES\nCLANG_WARN_UNREACHABLE_CODE = YES\n"
  },
  {
    "path": "macos/Runner/DebugProfile.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.cs.allow-jit</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "macos/Runner/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIconFile</key>\n\t<string></string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(FLUTTER_BUILD_NAME)</string>\n\t<key>CFBundleURLTypes</key>\n\t<array>\n\t\t<dict>\n\t\t\t<key>CFBundleTypeRole</key>\n\t\t\t<string>Editor</string>\n\t\t\t<key>CFBundleURLName</key>\n\t\t\t<string></string>\n\t\t\t<key>CFBundleURLSchemes</key>\n\t\t\t<array>\n\t\t\t\t<string>clash</string>\n\t\t\t\t<string>clashmeta</string>\n\t\t\t\t<string>bettbox</string>\n\t\t\t</array>\n\t\t</dict>\n\t</array>\n\t<key>CFBundleVersion</key>\n\t<string>$(FLUTTER_BUILD_NUMBER)</string>\n\t<key>LSMinimumSystemVersion</key>\n\t<string>$(MACOSX_DEPLOYMENT_TARGET)</string>\n\t<key>NSHumanReadableCopyright</key>\n\t<string>$(PRODUCT_COPYRIGHT)</string>\n\t<key>NSMainNibFile</key>\n\t<string>MainMenu</string>\n\t<key>NSPrincipalClass</key>\n\t<string>NSApplication</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "macos/Runner/MainFlutterWindow.swift",
    "content": "import Cocoa\nimport FlutterMacOS\nimport window_manager\nimport LaunchAtLogin\n\nclass MainFlutterWindow: NSWindow {\n    private var appMethodChannel: FlutterMethodChannel?\n    \n    override func awakeFromNib() {\n        let flutterViewController = FlutterViewController()\n        let windowFrame = self.frame\n        self.contentViewController = flutterViewController\n        self.setFrame(windowFrame, display: true)\n        \n        FlutterMethodChannel(\n            name: \"launch_at_startup\", binaryMessenger: flutterViewController.engine.binaryMessenger\n        )\n        .setMethodCallHandler { (_ call: FlutterMethodCall, result: @escaping FlutterResult) in\n            switch call.method {\n            case \"launchAtStartupIsEnabled\":\n                result(LaunchAtLogin.isEnabled)\n            case \"launchAtStartupSetEnabled\":\n                if let arguments = call.arguments as? [String: Any] {\n                    LaunchAtLogin.isEnabled = arguments[\"setEnabledValue\"] as! Bool\n                }\n                result(nil)\n            default:\n                result(FlutterMethodNotImplemented)\n            }\n        }\n        \n        // Setup app method channel\n        setupAppMethodChannel(flutterViewController: flutterViewController)\n        \n        RegisterGeneratedPlugins(registry: flutterViewController)\n        \n        // Load and apply saved icon preference\n        if loadIconPreference() {\n            setDockIcon(useLightIcon: true)\n        }\n        \n        super.awakeFromNib()\n    }\n    \n    override public func order(_ place: NSWindow.OrderingMode, relativeTo otherWin: Int) {\n        super.order(place, relativeTo: otherWin)\n        hiddenWindowAtLaunch()\n    }\n    \n    // MARK: - App Method Channel\n    \n    private func setupAppMethodChannel(flutterViewController: FlutterViewController) {\n        appMethodChannel = FlutterMethodChannel(\n            name: \"app\",\n            binaryMessenger: flutterViewController.engine.binaryMessenger\n        )\n        \n        appMethodChannel?.setMethodCallHandler { [weak self] (call: FlutterMethodCall, result: @escaping FlutterResult) in\n            guard let self = self else {\n                result(FlutterError(code: \"UNAVAILABLE\", message: \"Window unavailable\", details: nil))\n                return\n            }\n            \n            switch call.method {\n            case \"setLauncherIcon\":\n                if let arguments = call.arguments as? [String: Any],\n                   let useLightIcon = arguments[\"useLightIcon\"] as? Bool {\n                    let success = self.setDockIcon(useLightIcon: useLightIcon)\n                    result(success)\n                } else {\n                    result(FlutterError(code: \"INVALID_ARGUMENT\", message: \"Missing useLightIcon argument\", details: nil))\n                }\n            default:\n                result(FlutterMethodNotImplemented)\n            }\n        }\n    }\n    \n    // MARK: - Icon Management\n    \n    private func setDockIcon(useLightIcon: Bool) -> Bool {\n        let iconName = useLightIcon ? \"icon_light\" : \"icon\"\n        \n        // Load icon from app bundle\n        guard let iconPath = Bundle.main.path(forResource: iconName, ofType: \"png\", inDirectory: \"data/flutter_assets/assets/images\"),\n              let image = NSImage(contentsOfFile: iconPath) else {\n            // Fallback to default app icon if load fails\n            if let appIcon = NSImage(named: \"AppIcon\") {\n                NSApp.applicationIconImage = appIcon\n            }\n            return false\n        }\n        \n        // Set Dock icon\n        NSApp.applicationIconImage = image\n        \n        // Save preference\n        saveIconPreference(useLightIcon: useLightIcon)\n        \n        return true\n    }\n    \n    private func saveIconPreference(useLightIcon: Bool) {\n        UserDefaults.standard.set(useLightIcon, forKey: \"UseLightIcon\")\n        UserDefaults.standard.synchronize()\n    }\n    \n    private func loadIconPreference() -> Bool {\n        return UserDefaults.standard.bool(forKey: \"UseLightIcon\")\n    }\n}\n"
  },
  {
    "path": "macos/Runner/Release.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n</dict>\n</plist>\n"
  },
  {
    "path": "macos/Runner.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXAggregateTarget section */\n\t\t33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {\n\t\t\tisa = PBXAggregateTarget;\n\t\t\tbuildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget \"Flutter Assemble\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t33CC111E2044C6BF0003C045 /* ShellScript */,\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = \"Flutter Assemble\";\n\t\t\tproductName = FLX;\n\t\t};\n/* End PBXAggregateTarget section */\n\n/* Begin PBXBuildFile section */\n\t\t331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };\n\t\t335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };\n\t\t33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };\n\t\t33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };\n\t\t33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };\n\t\t33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };\n\t\t5377B2253E1C5AB4D9D56A31 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 72CBDF47BB69EDEFE644C48D /* Pods_RunnerTests.framework */; };\n\t\t7AC6855B2B8AF836004C123B /* (null) in Bundle Framework */ = {isa = PBXBuildFile; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };\n\t\tCDD319C761C7664F6008596B /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 4121E8CCDC7DC35194714CDE /* Pods_Runner.framework */; };\n\t\tF50091052CF74B7700D43AEA /* BettboxCore in CopyFiles */ = {isa = PBXBuildFile; fileRef = F50091042CF74B7700D43AEA /* BettboxCore */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };\n\t\tF5AA39AF2DA1D9FB00F5C816 /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = F5AA39AE2DA1D9FB00F5C816 /* LaunchAtLogin */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXContainerItemProxy section */\n\t\t331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 33CC10E52044A3C60003C045 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 33CC10EC2044A3C60003C045;\n\t\t\tremoteInfo = Runner;\n\t\t};\n\t\t33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 33CC10E52044A3C60003C045 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 33CC111A2044C6BA0003C045;\n\t\t\tremoteInfo = FLX;\n\t\t};\n/* End PBXContainerItemProxy section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\t33CC110E2044A8840003C045 /* Bundle Framework */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 10;\n\t\t\tfiles = (\n\t\t\t\t7AC6855B2B8AF836004C123B /* (null) in Bundle Framework */,\n\t\t\t);\n\t\t\tname = \"Bundle Framework\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF50091032CF74B6400D43AEA /* CopyFiles */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 6;\n\t\t\tfiles = (\n\t\t\t\tF50091052CF74B7700D43AEA /* BettboxCore in CopyFiles */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF5FAC0AA2CEDC4DA000CF079 /* CopyFiles */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 12;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 6;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\tF5FAC0AE2CEDC891000CF079 /* CopyFiles */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 6;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\t331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = \"<group>\"; };\n\t\t333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = \"<group>\"; };\n\t\t335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = \"<group>\"; };\n\t\t33CC10ED2044A3C60003C045 /* Bettbox.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Bettbox.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = \"<group>\"; };\n\t\t33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = \"<group>\"; };\n\t\t33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = \"<group>\"; };\n\t\t33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = \"Flutter-Debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = \"Flutter-Release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = \"Flutter-Generated.xcconfig\"; path = \"ephemeral/Flutter-Generated.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = \"<group>\"; };\n\t\t33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = \"<group>\"; };\n\t\t33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = \"<group>\"; };\n\t\t391D53BFAED9DD921DE2681C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.release.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t4121E8CCDC7DC35194714CDE /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t72CBDF47BB69EDEFE644C48D /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t779829C96DE7998FCC810C37 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.profile.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t7AF070893C29500AB9129D89 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.debug.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = \"<group>\"; };\n\t\t7D929F2AFD80E155D78F3718 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-RunnerTests.debug.xcconfig\"; path = \"Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t8F1D6D6423063FA738863205 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-RunnerTests.release.xcconfig\"; path = \"Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = \"<group>\"; };\n\t\tCA9CA9C2D0B5E93A91F45924 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-RunnerTests.profile.xcconfig\"; path = \"Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig\"; sourceTree = \"<group>\"; };\n\t\tF50091042CF74B7700D43AEA /* BettboxCore */ = {isa = PBXFileReference; lastKnownFileType = \"compiled.mach-o.executable\"; name = BettboxCore; path = ../libclash/macos/BettboxCore; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t331C80D2294CF70F00263BE5 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t5377B2253E1C5AB4D9D56A31 /* Pods_RunnerTests.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t33CC10EA2044A3C60003C045 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\tF5AA39AF2DA1D9FB00F5C816 /* LaunchAtLogin in Frameworks */,\n\t\t\t\tCDD319C761C7664F6008596B /* Pods_Runner.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t331C80D6294CF71000263BE5 /* RunnerTests */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t331C80D7294CF71000263BE5 /* RunnerTests.swift */,\n\t\t\t);\n\t\t\tpath = RunnerTests;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33BA886A226E78AF003329D5 /* Configs */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33E5194F232828860026EE4D /* AppInfo.xcconfig */,\n\t\t\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */,\n\t\t\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */,\n\t\t\t\t333000ED22D3DE5D00554162 /* Warnings.xcconfig */,\n\t\t\t);\n\t\t\tpath = Configs;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CC10E42044A3C60003C045 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tF50091042CF74B7700D43AEA /* BettboxCore */,\n\t\t\t\t33FAB671232836740065AC1E /* Runner */,\n\t\t\t\t33CEB47122A05771004F2AC0 /* Flutter */,\n\t\t\t\t331C80D6294CF71000263BE5 /* RunnerTests */,\n\t\t\t\t33CC10EE2044A3C60003C045 /* Products */,\n\t\t\t\tD73912EC22F37F3D000D13A0 /* Frameworks */,\n\t\t\t\t89B479066870C6FCC9EB240B /* Pods */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CC10EE2044A3C60003C045 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10ED2044A3C60003C045 /* Bettbox.app */,\n\t\t\t\t331C80D5294CF71000263BE5 /* RunnerTests.xctest */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CC11242044D66E0003C045 /* Resources */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10F22044A3C60003C045 /* Assets.xcassets */,\n\t\t\t\t33CC10F42044A3C60003C045 /* MainMenu.xib */,\n\t\t\t\t33CC10F72044A3C60003C045 /* Info.plist */,\n\t\t\t);\n\t\t\tname = Resources;\n\t\t\tpath = ..;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CEB47122A05771004F2AC0 /* Flutter */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,\n\t\t\t\t33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,\n\t\t\t\t33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,\n\t\t\t\t33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,\n\t\t\t);\n\t\t\tpath = Flutter;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33FAB671232836740065AC1E /* Runner */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10F02044A3C60003C045 /* AppDelegate.swift */,\n\t\t\t\t33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,\n\t\t\t\t33E51913231747F40026EE4D /* DebugProfile.entitlements */,\n\t\t\t\t33E51914231749380026EE4D /* Release.entitlements */,\n\t\t\t\t33CC11242044D66E0003C045 /* Resources */,\n\t\t\t\t33BA886A226E78AF003329D5 /* Configs */,\n\t\t\t);\n\t\t\tpath = Runner;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t89B479066870C6FCC9EB240B /* Pods */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t7AF070893C29500AB9129D89 /* Pods-Runner.debug.xcconfig */,\n\t\t\t\t391D53BFAED9DD921DE2681C /* Pods-Runner.release.xcconfig */,\n\t\t\t\t779829C96DE7998FCC810C37 /* Pods-Runner.profile.xcconfig */,\n\t\t\t\t7D929F2AFD80E155D78F3718 /* Pods-RunnerTests.debug.xcconfig */,\n\t\t\t\t8F1D6D6423063FA738863205 /* Pods-RunnerTests.release.xcconfig */,\n\t\t\t\tCA9CA9C2D0B5E93A91F45924 /* Pods-RunnerTests.profile.xcconfig */,\n\t\t\t);\n\t\t\tpath = Pods;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD73912EC22F37F3D000D13A0 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t4121E8CCDC7DC35194714CDE /* Pods_Runner.framework */,\n\t\t\t\t72CBDF47BB69EDEFE644C48D /* Pods_RunnerTests.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t331C80D4294CF70F00263BE5 /* RunnerTests */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget \"RunnerTests\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tF99797C7DFD1C67140B8C749 /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t331C80D1294CF70F00263BE5 /* Sources */,\n\t\t\t\t331C80D2294CF70F00263BE5 /* Frameworks */,\n\t\t\t\t331C80D3294CF70F00263BE5 /* Resources */,\n\t\t\t\tF5FAC0AA2CEDC4DA000CF079 /* CopyFiles */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t331C80DA294CF71000263BE5 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = RunnerTests;\n\t\t\tproductName = RunnerTests;\n\t\t\tproductReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */;\n\t\t\tproductType = \"com.apple.product-type.bundle.unit-test\";\n\t\t};\n\t\t33CC10EC2044A3C60003C045 /* Runner */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget \"Runner\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tE94087E39E2BA74B2566D9D6 /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t33CC10E92044A3C60003C045 /* Sources */,\n\t\t\t\t33CC10EA2044A3C60003C045 /* Frameworks */,\n\t\t\t\t33CC10EB2044A3C60003C045 /* Resources */,\n\t\t\t\t33CC110E2044A8840003C045 /* Bundle Framework */,\n\t\t\t\t3399D490228B24CF009A79C7 /* ShellScript */,\n\t\t\t\t1522C6AC211009D2A7DFAD40 /* [CP] Embed Pods Frameworks */,\n\t\t\t\tF5FAC0AE2CEDC891000CF079 /* CopyFiles */,\n\t\t\t\tF50091032CF74B6400D43AEA /* CopyFiles */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t33CC11202044C79F0003C045 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = Runner;\n\t\t\tproductName = Runner;\n\t\t\tproductReference = 33CC10ED2044A3C60003C045 /* Bettbox.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t33CC10E52044A3C60003C045 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastSwiftUpdateCheck = 0920;\n\t\t\t\tLastUpgradeCheck = 1510;\n\t\t\t\tORGANIZATIONNAME = \"\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t331C80D4294CF70F00263BE5 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 14.0;\n\t\t\t\t\t\tTestTargetID = 33CC10EC2044A3C60003C045;\n\t\t\t\t\t};\n\t\t\t\t\t33CC10EC2044A3C60003C045 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.2;\n\t\t\t\t\t\tLastSwiftMigration = 1100;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t\tSystemCapabilities = {\n\t\t\t\t\t\t\tcom.apple.Sandbox = {\n\t\t\t\t\t\t\t\tenabled = 1;\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t};\n\t\t\t\t\t};\n\t\t\t\t\t33CC111A2044C6BA0003C045 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.2;\n\t\t\t\t\t\tProvisioningStyle = Manual;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject \"Runner\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 33CC10E42044A3C60003C045;\n\t\t\tpackageReferences = (\n\t\t\t\tF5AA39AD2DA1D9FB00F5C816 /* XCRemoteSwiftPackageReference \"LaunchAtLogin\" */,\n\t\t\t);\n\t\t\tproductRefGroup = 33CC10EE2044A3C60003C045 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t33CC10EC2044A3C60003C045 /* Runner */,\n\t\t\t\t331C80D4294CF70F00263BE5 /* RunnerTests */,\n\t\t\t\t33CC111A2044C6BA0003C045 /* Flutter Assemble */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t331C80D3294CF70F00263BE5 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t33CC10EB2044A3C60003C045 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,\n\t\t\t\t33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t1522C6AC211009D2A7DFAD40 /* [CP] Embed Pods Frameworks */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist\",\n\t\t\t);\n\t\t\tname = \"[CP] Embed Pods Frameworks\";\n\t\t\toutputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"\\\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\t3399D490228B24CF009A79C7 /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\talwaysOutOfDate = 1;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"echo \\\"$PRODUCT_NAME.app\\\" > \\\"$PROJECT_DIR\\\"/Flutter/ephemeral/.app_filename && \\\"$FLUTTER_ROOT\\\"/packages/flutter_tools/bin/macos_assemble.sh embed\\n\";\n\t\t};\n\t\t33CC111E2044C6BF0003C045 /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t\tFlutter/ephemeral/FlutterInputs.xcfilelist,\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\tFlutter/ephemeral/tripwire,\n\t\t\t);\n\t\t\toutputFileListPaths = (\n\t\t\t\tFlutter/ephemeral/FlutterOutputs.xcfilelist,\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"\\\"$FLUTTER_ROOT\\\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire\";\n\t\t};\n\t\tE94087E39E2BA74B2566D9D6 /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\tF99797C7DFD1C67140B8C749 /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t331C80D1294CF70F00263BE5 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t33CC10E92044A3C60003C045 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,\n\t\t\t\t33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,\n\t\t\t\t335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXTargetDependency section */\n\t\t331C80DA294CF71000263BE5 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 33CC10EC2044A3C60003C045 /* Runner */;\n\t\t\ttargetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */;\n\t\t};\n\t\t33CC11202044C79F0003C045 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;\n\t\t\ttargetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;\n\t\t};\n/* End PBXTargetDependency section */\n\n/* Begin PBXVariantGroup section */\n\t\t33CC10F42044A3C60003C045 /* MainMenu.xib */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10F52044A3C60003C045 /* Base */,\n\t\t\t);\n\t\t\tname = MainMenu.xib;\n\t\t\tpath = Runner;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t331C80DB294CF71000263BE5 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7D929F2AFD80E155D78F3718 /* Pods-RunnerTests.debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.appshub.bettbox.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/Bettbox.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Bettbox\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t331C80DC294CF71000263BE5 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 8F1D6D6423063FA738863205 /* Pods-RunnerTests.release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.appshub.bettbox.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/Bettbox.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Bettbox\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t331C80DD294CF71000263BE5 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = CA9CA9C2D0B5E93A91F45924 /* Pods-RunnerTests.profile.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.appshub.bettbox.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/Bettbox.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Bettbox\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t338D0CE9231458BD00FA5F75 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.15;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t338D0CEA231458BD00FA5F75 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tLIBRARY_SEARCH_PATHS = \"\";\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.15;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.appshub.bettbox;\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t338D0CEB231458BD00FA5F75 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t33CC10F92044A3C60003C045 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.15;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t33CC10FA2044A3C60003C045 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.15;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t33CC10FC2044A3C60003C045 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tLIBRARY_SEARCH_PATHS = \"\";\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.15;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.appshub.bettbox.debug;\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t33CC10FD2044A3C60003C045 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tLIBRARY_SEARCH_PATHS = \"\";\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.15;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.appshub.bettbox;\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t33CC111C2044C6BA0003C045 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t33CC111D2044C6BA0003C045 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget \"RunnerTests\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t331C80DB294CF71000263BE5 /* Debug */,\n\t\t\t\t331C80DC294CF71000263BE5 /* Release */,\n\t\t\t\t331C80DD294CF71000263BE5 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t33CC10E82044A3C60003C045 /* Build configuration list for PBXProject \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t33CC10F92044A3C60003C045 /* Debug */,\n\t\t\t\t33CC10FA2044A3C60003C045 /* Release */,\n\t\t\t\t338D0CE9231458BD00FA5F75 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t33CC10FC2044A3C60003C045 /* Debug */,\n\t\t\t\t33CC10FD2044A3C60003C045 /* Release */,\n\t\t\t\t338D0CEA231458BD00FA5F75 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget \"Flutter Assemble\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t33CC111C2044C6BA0003C045 /* Debug */,\n\t\t\t\t33CC111D2044C6BA0003C045 /* Release */,\n\t\t\t\t338D0CEB231458BD00FA5F75 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\n/* Begin XCRemoteSwiftPackageReference section */\n\t\tF5AA39AD2DA1D9FB00F5C816 /* XCRemoteSwiftPackageReference \"LaunchAtLogin\" */ = {\n\t\t\tisa = XCRemoteSwiftPackageReference;\n\t\t\trepositoryURL = \"https://github.com/sindresorhus/LaunchAtLogin\";\n\t\t\trequirement = {\n\t\t\t\tbranch = main;\n\t\t\t\tkind = branch;\n\t\t\t};\n\t\t};\n/* End XCRemoteSwiftPackageReference section */\n\n/* Begin XCSwiftPackageProductDependency section */\n\t\tF5AA39AE2DA1D9FB00F5C816 /* LaunchAtLogin */ = {\n\t\t\tisa = XCSwiftPackageProductDependency;\n\t\t\tpackage = F5AA39AD2DA1D9FB00F5C816 /* XCRemoteSwiftPackageReference \"LaunchAtLogin\" */;\n\t\t\tproductName = LaunchAtLogin;\n\t\t};\n/* End XCSwiftPackageProductDependency section */\n\t};\n\trootObject = 33CC10E52044A3C60003C045 /* Project object */;\n}\n"
  },
  {
    "path": "macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1510\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n               BuildableName = \"Bettbox.app\"\n               BlueprintName = \"Runner\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"Bettbox.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n      <Testables>\n         <TestableReference\n            skipped = \"NO\"\n            parallelizable = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"331C80D4294CF70F00263BE5\"\n               BuildableName = \"RunnerTests.xctest\"\n               BlueprintName = \"RunnerTests\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </TestableReference>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      enableGPUValidationMode = \"1\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"Bettbox.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Profile\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"Bettbox.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "macos/Runner.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:Runner.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:Pods/Pods.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "macos/RunnerTests/RunnerTests.swift",
    "content": "import FlutterMacOS\nimport Cocoa\nimport XCTest\n\nclass RunnerTests: XCTestCase {\n\n  func testExample() {\n    // If you add code to the Runner application, consider adding tests here.\n    // See https://developer.apple.com/documentation/xctest for more information about using XCTest.\n  }\n\n}\n"
  },
  {
    "path": "macos/packaging/dmg/make_config.yaml",
    "content": "title: Bettbox\ncontents:\n  - x: 448\n    y: 344\n    type: link\n    path: \"/Applications\"\n  - x: 192\n    y: 344\n    type: file\n    path: Bettbox.app\n"
  },
  {
    "path": "plugins/flutter_distributor/.all-contributorsrc",
    "content": "{\n  \"projectName\": \"flutter_distributor\",\n  \"projectOwner\": \"leanflutter\",\n  \"repoType\": \"github\",\n  \"repoHost\": \"https://github.com\",\n  \"files\": [\n    \"README.md\",\n    \"README-ZH.md\"\n  ],\n  \"imageSize\": 100,\n  \"commit\": false,\n  \"commitConvention\": \"none\",\n  \"contributors\": [\n    {\n      \"login\": \"lijy91\",\n      \"name\": \"LiJianying\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/3889523?v=4\",\n      \"profile\": \"https://github.com/lijy91\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"BytesZero\",\n      \"name\": \"Zero\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/8764899?v=4\",\n      \"profile\": \"https://juejin.cn/user/764915820276439\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"KRTirtho\",\n      \"name\": \"Kingkor Roy Tirtho\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/61944859?v=4\",\n      \"profile\": \"https://github.com/KRTirtho\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"laiiihz\",\n      \"name\": \"LAIIIHZ\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/35956195?v=4\",\n      \"profile\": \"https://github.com/laiiihz\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"ueki-tomohiro\",\n      \"name\": \"Tomohiro Ueki\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/27331430?v=4\",\n      \"profile\": \"https://github.com/ueki-tomohiro\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"cybrox\",\n      \"name\": \"Sven Gehring\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/2383736?v=4\",\n      \"profile\": \"https://cybrox.eu/\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"GargantuaX\",\n      \"name\": \"GargantuaX\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/14013111?v=4\",\n      \"profile\": \"https://github.com/GargantuaX\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"hiperioncn\",\n      \"name\": \"Hiperion\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/6045710?v=4\",\n      \"profile\": \"https://github.com/hiperioncn\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"GroovinChip\",\n      \"name\": \"Reuben Turner\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/4250470?v=4\",\n      \"profile\": \"https://github.com/GroovinChip\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"animator\",\n      \"name\": \"Ankit Mahato\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/615622?v=4\",\n      \"profile\": \"http://animator.github.io\",\n      \"contributions\": [\n        \"doc\"\n      ]\n    },\n    {\n      \"login\": \"Tienisto\",\n      \"name\": \"Tien Do Nam\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/38380847?v=4\",\n      \"profile\": \"http://tienisto.com\",\n      \"contributions\": [\n        \"code\"\n      ]\n    },\n    {\n      \"login\": \"zacksleo\",\n      \"name\": \"zacks\",\n      \"avatar_url\": \"https://avatars.githubusercontent.com/u/3369169?v=4\",\n      \"profile\": \"https://zacksleo.top/\",\n      \"contributions\": [\n        \"code\"\n      ]\n    }\n  ],\n  \"contributorsPerLine\": 7,\n  \"linkToUsage\": true,\n  \"commitType\": \"docs\"\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/.github/FUNDING.yml",
    "content": "liberapay: lijy91\ngithub: lijy91\n"
  },
  {
    "path": "plugins/flutter_distributor/.github/workflows/hello_world_build.yml",
    "content": "name: \"HelloWorld: Build\"\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    types: [opened, reopened]\n\njobs:\n  build-aab:\n    name: Build AAB\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - uses: subosito/flutter-action@v2\n        with:\n          channel: stable\n          flutter-version: 3.24.1\n      - uses: bluefireteam/melos-action@v3\n      - run: |\n          dart ../../packages/fastforge/bin/main.dart package \\\n            --platform=android \\\n            --targets aab\n        working-directory: examples/hello_world\n\n  build-apk:\n    name: Build APK\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - uses: subosito/flutter-action@v2\n        with:\n          channel: stable\n          flutter-version: 3.24.1\n      - uses: bluefireteam/melos-action@v3\n      - run: |\n          dart ../../packages/fastforge/bin/main.dart package \\\n            --platform=android \\\n            --targets apk\n        working-directory: examples/hello_world\n\n  build-appimage:\n    name: Build AppImage\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - uses: subosito/flutter-action@v2\n        with:\n          channel: stable\n          flutter-version: 3.24.1\n      - run: |\n          sudo apt-get update -y\n          sudo apt-get install -y ninja-build libgtk-3-dev libfuse2\n      - uses: bluefireteam/melos-action@v3\n      - run: |\n          wget -O appimagetool \"https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage\"\n          chmod +x appimagetool\n          mv appimagetool /usr/local/bin/\n      - run: |\n          dart ../../packages/fastforge/bin/main.dart package \\\n            --platform=linux \\\n            --targets appimage\n        working-directory: examples/hello_world\n\n  build-deb:\n    name: Build DEB\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v2\n      - uses: subosito/flutter-action@v2\n        with:\n          channel: stable\n          flutter-version: 3.24.1\n      - run: |\n          sudo apt-get update -y\n          sudo apt-get install -y ninja-build libgtk-3-dev\n      - uses: bluefireteam/melos-action@v3\n      - run: |\n          dart ../../packages/fastforge/bin/main.dart package \\\n            --platform=linux \\\n            --targets deb\n        working-directory: examples/hello_world\n"
  },
  {
    "path": "plugins/flutter_distributor/.github/workflows/lint.yml",
    "content": "name: lint\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\njobs:\n  analyze:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - uses: subosito/flutter-action@v2\n        with:\n          flutter-version: \"3.24.1\"\n          channel: \"stable\"\n      - uses: bluefireteam/melos-action@v2\n      - run: melos run analyze\n\n  format:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - uses: subosito/flutter-action@v2\n        with:\n          flutter-version: \"3.24.1\"\n          channel: \"stable\"\n          cache: true\n      - uses: bluefireteam/melos-action@v2\n      - run: melos run format-check\n\n  dependency_validator:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - uses: subosito/flutter-action@v2\n        with:\n          flutter-version: \"3.24.1\"\n          channel: \"stable\"\n          cache: true\n      - uses: bluefireteam/melos-action@v2\n      - run: melos run dependency_validator\n"
  },
  {
    "path": "plugins/flutter_distributor/.github/workflows/test.yml",
    "content": "name: test\n\non:\n  push:\n    branches: [main]\n  pull_request:\n    branches: [main]\n\njobs:\n  test:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - uses: subosito/flutter-action@v2\n        with:\n          flutter-version: \"3.24.1\"\n          channel: \"stable\"\n          cache: true\n      - uses: bluefireteam/melos-action@v2\n      - run: melos run test --no-select\n"
  },
  {
    "path": "plugins/flutter_distributor/.gitignore",
    "content": ".dart_tool/\n\n# IntelliJ related\n*.iml\n.idea/\n\npubspec_overrides.yaml\npubspec.lock\n"
  },
  {
    "path": "plugins/flutter_distributor/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021-present LiJianying <lijy91@foxmail.com>\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": "plugins/flutter_distributor/analysis_options.yaml",
    "content": "include: package:lints/recommended.yaml\n\nlinter:\n  rules:\n    # - always_declare_return_types\n    # - always_put_control_body_on_new_line\n    # - always_put_required_named_parameters_first\n    # - always_specify_types\n    - always_use_package_imports\n    - annotate_overrides\n    # - annotate_redeclares\n    # - avoid_annotating_with_dynamic\n    # - avoid_bool_literals_in_conditional_expressions\n    # - avoid_catches_without_on_clauses\n    # - avoid_catching_errors\n    # - avoid_classes_with_only_static_members\n    # - avoid_double_and_int_checks\n    # - avoid_dynamic_calls\n    - avoid_empty_else\n    # - avoid_equals_and_hash_code_on_mutable_classes\n    # - avoid_escaping_inner_quotes\n    # - avoid_field_initializers_in_const_classes\n    - avoid_final_parameters\n    - avoid_function_literals_in_foreach_calls\n    # - avoid_implementing_value_types\n    - avoid_init_to_null\n    # - avoid_js_rounded_ints\n    # - avoid_multiple_declarations_per_line\n    - avoid_null_checks_in_equality_operators\n    # - avoid_positional_boolean_parameters\n    # - avoid_print\n    # - avoid_private_typedef_functions\n    # - avoid_redundant_argument_values\n    - avoid_relative_lib_imports\n    - avoid_renaming_method_parameters\n    - avoid_return_types_on_setters\n    - avoid_returning_null_for_void\n    # - avoid_returning_this\n    # - avoid_setters_without_getters\n    - avoid_shadowing_type_parameters\n    - avoid_single_cascade_in_expression_statements\n    # - avoid_slow_async_io\n    # - avoid_type_to_string\n    - avoid_types_as_parameter_names\n    # - avoid_types_on_closure_parameters\n    - avoid_unnecessary_containers\n    # - avoid_unused_constructor_parameters\n    - avoid_void_async\n    - avoid_web_libraries_in_flutter\n    - await_only_futures\n    - camel_case_extensions\n    - camel_case_types\n    # - cancel_subscriptions\n    # - cascade_invocations\n    # - cast_nullable_to_non_nullable\n    # - close_sinks\n    # - collection_methods_unrelated_type\n    - combinators_ordering\n    # - comment_references\n    # - conditional_uri_does_not_exist\n    # - constant_identifier_names\n    - control_flow_in_finally\n    - curly_braces_in_flow_control_structures\n    # - dangling_library_doc_comments\n    - depend_on_referenced_packages\n    # - deprecated_consistency\n    # - deprecated_member_use_from_same_package\n    # - diagnostic_describe_all_properties\n    - directives_ordering\n    # - discarded_futures\n    # - do_not_use_environment\n    - empty_catches\n    - empty_constructor_bodies\n    - empty_statements\n    - eol_at_end_of_file\n    - exhaustive_cases\n    - file_names\n    - flutter_style_todos\n    - hash_and_equals\n    - implementation_imports\n    # - implicit_call_tearoffs\n    # - implicit_reopen\n    # - invalid_case_patterns\n    # - join_return_with_assignment\n    # - leading_newlines_in_multiline_strings\n    # - library_annotations\n    - library_names\n    - library_prefixes\n    - library_private_types_in_public_api\n    # - lines_longer_than_80_chars\n    # - literal_only_boolean_expressions\n    # - matching_super_parameters\n    # - missing_code_block_language_in_doc_comment\n    # - missing_whitespace_between_adjacent_strings\n    # - no_adjacent_strings_in_list\n    # - no_default_cases\n    - no_duplicate_case_values\n    - no_leading_underscores_for_library_prefixes\n    - no_leading_underscores_for_local_identifiers\n    # - no_literal_bool_comparisons\n    - no_logic_in_create_state\n    # - no_runtimeType_toString\n    # - no_self_assignments\n    # - no_wildcard_variable_uses\n    - non_constant_identifier_names\n    # - noop_primitive_operations\n    - null_check_on_nullable_type_parameter\n    - null_closures\n    # - omit_local_variable_types\n    # - one_member_abstracts\n    # - only_throw_errors\n    - overridden_fields\n    # - package_api_docs\n    - package_names\n    - package_prefixed_library_names\n    # - parameter_assignments\n    - prefer_adjacent_string_concatenation\n    # - prefer_asserts_in_initializer_lists\n    # - prefer_asserts_with_message\n    - prefer_collection_literals\n    - prefer_conditional_assignment\n    - prefer_const_constructors\n    - prefer_const_constructors_in_immutables\n    - prefer_const_declarations\n    - prefer_const_literals_to_create_immutables\n    # - prefer_constructors_over_static_methods\n    - prefer_contains\n    # - prefer_double_quotes\n    # - prefer_expression_function_bodies\n    - prefer_final_fields\n    # - prefer_final_in_for_each\n    # - prefer_final_locals\n    # - prefer_final_parameters\n    - prefer_for_elements_to_map_fromIterable\n    # - prefer_foreach\n    - prefer_function_declarations_over_variables\n    - prefer_generic_function_type_aliases\n    # - prefer_if_elements_to_conditional_expressions\n    - prefer_if_null_operators\n    - prefer_initializing_formals\n    - prefer_inlined_adds\n    # - prefer_int_literals\n    - prefer_interpolation_to_compose_strings\n    - prefer_is_empty\n    - prefer_is_not_empty\n    - prefer_is_not_operator\n    - prefer_iterable_whereType\n    # - prefer_mixin\n    - prefer_null_aware_method_calls\n    - prefer_null_aware_operators\n    # - prefer_relative_imports\n    - prefer_single_quotes\n    - prefer_spread_collections\n    - prefer_typing_uninitialized_variables\n    - prefer_void_to_null\n    - provide_deprecation_message\n    # - public_member_api_docs\n    - recursive_getters\n    - require_trailing_commas\n    # - secure_pubspec_urls\n    - sized_box_for_whitespace\n    # - sized_box_shrink_expand\n    - slash_for_doc_comments\n    - sort_child_properties_last\n    - sort_constructors_first\n    - sort_pub_dependencies\n    - sort_unnamed_constructors_first\n    # - test_types_in_equals\n    # - throw_in_finally\n    # - tighten_type_of_initializing_formals\n    # - type_annotate_public_apis\n    - type_init_formals\n    # - type_literal_in_constant_pattern\n    # - unawaited_futures\n    # - unintended_html_in_doc_comment\n    # - unnecessary_await_in_return\n    - unnecessary_brace_in_string_interps\n    # - unnecessary_breaks\n    - unnecessary_const\n    - unnecessary_constructor_name\n    # - unnecessary_final\n    - unnecessary_getters_setters\n    # - unnecessary_lambdas\n    - unnecessary_late\n    # - unnecessary_library_directive\n    # - unnecessary_library_name\n    - unnecessary_new\n    - unnecessary_null_aware_assignments\n    # - unnecessary_null_aware_operator_on_extension_on_nullable\n    # - unnecessary_null_checks\n    - unnecessary_null_in_if_null_operators\n    - unnecessary_nullable_for_final_variable_declarations\n    - unnecessary_overrides\n    # - unnecessary_parenthesis\n    # - unnecessary_raw_strings\n    # - unnecessary_statements\n    - unnecessary_string_escapes\n    - unnecessary_string_interpolations\n    - unnecessary_this\n    # - unnecessary_to_list_in_spreads\n    # - unreachable_from_main\n    - unrelated_type_equality_checks\n    # - unsafe_html\n    - use_build_context_synchronously\n    - use_colored_box\n    - use_decorated_box\n    # - use_enums\n    - use_full_hex_values_for_flutter_colors\n    - use_function_type_syntax_for_parameters\n    # - use_if_null_to_convert_nulls_to_bools\n    # - use_is_even_rather_than_modulo\n    - use_key_in_widget_constructors\n    # - use_late_for_private_fields_and_variables\n    # - use_named_constants\n    # - use_raw_strings\n    - use_rethrow_when_possible\n    # - use_setters_to_change_properties\n    # - use_string_buffers\n    # - use_string_in_part_of_directives\n    # - use_super_parameters\n    # - use_test_throws_matchers\n    # - use_to_and_as_if_applicable\n    - valid_regexps\n    # - void_checks\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/.gitignore",
    "content": "# Miscellaneous\n*.class\n*.log\n*.pyc\n*.swp\n.DS_Store\n.atom/\n.buildlog/\n.history\n.svn/\nmigrate_working_dir/\n\n# IntelliJ related\n*.iml\n*.ipr\n*.iws\n.idea/\n\n# The .vscode folder contains launch configuration and tasks you configure in\n# VS Code which you may wish to be included in version control, so this line\n# is commented out by default.\n#.vscode/\n\n# Flutter/Dart/Pub related\n**/doc/api/\n**/ios/Flutter/.last_build_id\n.dart_tool/\n.flutter-plugins\n.flutter-plugins-dependencies\n.packages\n.pub-cache/\n.pub/\n/build/\n\n# Symbolication related\napp.*.symbols\n\n# Obfuscation related\napp.*.map.json\n\n# Android Studio will place build artifacts here\n/android/app/debug\n/android/app/profile\n/android/app/release\n\ndist/"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/.metadata",
    "content": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrades etc.\n#\n# This file should be version controlled.\n\nversion:\n  revision: c07f7888888435fd9df505aa2efc38d3cf65681b\n  channel: stable\n\nproject_type: app\n\n# Tracks metadata for the flutter migrate command\nmigration:\n  platforms:\n    - platform: root\n      create_revision: c07f7888888435fd9df505aa2efc38d3cf65681b\n      base_revision: c07f7888888435fd9df505aa2efc38d3cf65681b\n    - platform: linux\n      create_revision: c07f7888888435fd9df505aa2efc38d3cf65681b\n      base_revision: c07f7888888435fd9df505aa2efc38d3cf65681b\n\n  # User provided section\n\n  # List of Local paths (relative to this file) that should be\n  # ignored by the migrate tool.\n  #\n  # Files that are not part of the templates will be ignored by default.\n  unmanaged_files:\n    - 'lib/main.dart'\n    - 'ios/Runner.xcodeproj/project.pbxproj'\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/analysis_options.yaml",
    "content": "# This file configures the analyzer, which statically analyzes Dart code to\n# check for errors, warnings, and lints.\n#\n# The issues identified by the analyzer are surfaced in the UI of Dart-enabled\n# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be\n# invoked from the command line by running `flutter analyze`.\n\n# The following line activates a set of recommended lints for Flutter apps,\n# packages, and plugins designed to encourage good coding practices.\ninclude: package:flutter_lints/flutter.yaml\n\nlinter:\n  # The lint rules applied to this project can be customized in the\n  # section below to disable rules from the `package:flutter_lints/flutter.yaml`\n  # included above or to enable additional rules. A list of all available lints\n  # and their documentation is published at\n  # https://dart-lang.github.io/linter/lints/index.html.\n  #\n  # Instead of disabling a lint rule for the entire project in the\n  # section below, it can also be suppressed for a single line of code\n  # or a specific dart file by using the `// ignore: name_of_lint` and\n  # `// ignore_for_file: name_of_lint` syntax on the line or in the file\n  # producing the lint.\n  rules:\n    # avoid_print: false  # Uncomment to disable the `avoid_print` rule\n    # prefer_single_quotes: true  # Uncomment to enable the `prefer_single_quotes` rule\n\n# Additional information about this file can be found at\n# https://dart.dev/guides/language/analysis-options\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/distribute_options.yaml",
    "content": "output: dist/\nreleases:\n  - name: dev\n    jobs:\n      # - name: linux-appimage\n      #   package:\n      #     platform: linux\n      #     target: appimage\n      - name: linux-deb\n        package:\n          platform: linux\n          target: deb\n      - name: linux-zip\n        package:\n          platform: linux\n          target: zip\n      - name: macos-dmg\n        package:\n          platform: macos\n          target: dmg\n      - name: macos-zip\n        package:\n          platform: macos\n          target: zip\n      - name: windows-exe\n        package:\n          platform: windows\n          target: exe\n      - name: windows-msix\n        package:\n          platform: windows\n          target: msix\n      - name: windows-zip\n        package:\n          platform: windows\n          target: zip\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/lib/main.dart",
    "content": "import 'package:flutter/material.dart';\n\nvoid main() {\n  runApp(const MyApp());\n}\n\nclass MyApp extends StatelessWidget {\n  const MyApp({Key? key}) : super(key: key);\n\n  // This widget is the root of your application.\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      title: 'Flutter Demo',\n      theme: ThemeData(\n        // This is the theme of your application.\n        //\n        // TRY THIS: Try running your application with \"flutter run\". You'll see\n        // the application has a blue toolbar. Then, without quitting the app,\n        // try changing the seedColor in the colorScheme below to Colors.green\n        // and then invoke \"hot reload\" (save your changes or press the \"hot\n        // reload\" button in a Flutter-supported IDE, or press \"r\" if you used\n        // the command line to start the app).\n        //\n        // Notice that the counter didn't reset back to zero; the application\n        // state is not lost during the reload. To reset the state, use hot\n        // restart instead.\n        //\n        // This works for code too, not just values: Most code changes can be\n        // tested with just a hot reload.\n        colorScheme: ColorScheme.fromSeed(seedColor: Colors.deepPurple),\n        useMaterial3: true,\n      ),\n      home: const MyHomePage(title: 'Flutter Demo Home Page'),\n    );\n  }\n}\n\nclass MyHomePage extends StatefulWidget {\n  const MyHomePage({Key? key, required this.title}) : super(key: key);\n\n  // This widget is the home page of your application. It is stateful, meaning\n  // that it has a State object (defined below) that contains fields that affect\n  // how it looks.\n\n  // This class is the configuration for the state. It holds the values (in this\n  // case the title) provided by the parent (in this case the App widget) and\n  // used by the build method of the State. Fields in a Widget subclass are\n  // always marked \"final\".\n\n  final String title;\n\n  @override\n  State<MyHomePage> createState() => _MyHomePageState();\n}\n\nclass _MyHomePageState extends State<MyHomePage> {\n  int _counter = 0;\n\n  void _incrementCounter() {\n    setState(() {\n      // This call to setState tells the Flutter framework that something has\n      // changed in this State, which causes it to rerun the build method below\n      // so that the display can reflect the updated values. If we changed\n      // _counter without calling setState(), then the build method would not be\n      // called again, and so nothing would appear to happen.\n      _counter++;\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    // This method is rerun every time setState is called, for instance as done\n    // by the _incrementCounter method above.\n    //\n    // The Flutter framework has been optimized to make rerunning build methods\n    // fast, so that you can just rebuild anything that needs updating rather\n    // than having to individually change instances of widgets.\n    return Scaffold(\n      appBar: AppBar(\n        // TRY THIS: Try changing the color here to a specific color (to\n        // Colors.amber, perhaps?) and trigger a hot reload to see the AppBar\n        // change color while the other colors stay the same.\n        backgroundColor: Theme.of(context).colorScheme.inversePrimary,\n        // Here we take the value from the MyHomePage object that was created by\n        // the App.build method, and use it to set our appbar title.\n        title: Text(widget.title),\n      ),\n      body: Center(\n        // Center is a layout widget. It takes a single child and positions it\n        // in the middle of the parent.\n        child: Column(\n          // Column is also a layout widget. It takes a list of children and\n          // arranges them vertically. By default, it sizes itself to fit its\n          // children horizontally, and tries to be as tall as its parent.\n          //\n          // Column has various properties to control how it sizes itself and\n          // how it positions its children. Here we use mainAxisAlignment to\n          // center the children vertically; the main axis here is the vertical\n          // axis because Columns are vertical (the cross axis would be\n          // horizontal).\n          //\n          // TRY THIS: Invoke \"debug painting\" (choose the \"Toggle Debug Paint\"\n          // action in the IDE, or press \"p\" in the console), to see the\n          // wireframe for each widget.\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: <Widget>[\n            const Text('You have pushed the button this many times:'),\n            Text(\n              '$_counter',\n              style: Theme.of(context).textTheme.headlineMedium,\n            ),\n          ],\n        ),\n      ),\n      floatingActionButton: FloatingActionButton(\n        onPressed: _incrementCounter,\n        tooltip: 'Increment',\n        child: const Icon(Icons.add),\n      ), // This trailing comma makes auto-formatting nicer for build methods.\n    );\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/linux/.gitignore",
    "content": "flutter/ephemeral\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/linux/CMakeLists.txt",
    "content": "# Project-level configuration.\ncmake_minimum_required(VERSION 3.10)\nproject(runner LANGUAGES CXX)\n\n# The name of the executable created for the application. Change this to change\n# the on-disk name of your application.\nset(BINARY_NAME \"custom-binary-name\")\n# The unique GTK application identifier for this application. See:\n# https://wiki.gnome.org/HowDoI/ChooseApplicationID\nset(APPLICATION_ID \"com.example.custom_binary_name\")\n\n# Explicitly opt in to modern CMake behaviors to avoid warnings with recent\n# versions of CMake.\ncmake_policy(SET CMP0063 NEW)\n\n# Load bundled libraries from the lib/ directory relative to the binary.\nset(CMAKE_INSTALL_RPATH \"$ORIGIN/lib\")\n\n# Root filesystem for cross-building.\nif(FLUTTER_TARGET_PLATFORM_SYSROOT)\n  set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})\n  set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})\n  set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)\n  set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)\n  set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)\n  set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)\nendif()\n\n# Define build configuration options.\nif(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)\n  set(CMAKE_BUILD_TYPE \"Debug\" CACHE\n    STRING \"Flutter build mode\" FORCE)\n  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS\n    \"Debug\" \"Profile\" \"Release\")\nendif()\n\n# Compilation settings that should be applied to most targets.\n#\n# Be cautious about adding new options here, as plugins use this function by\n# default. In most cases, you should add new options to specific targets instead\n# of modifying this function.\nfunction(APPLY_STANDARD_SETTINGS TARGET)\n  target_compile_features(${TARGET} PUBLIC cxx_std_14)\n  target_compile_options(${TARGET} PRIVATE -Wall -Werror)\n  target_compile_options(${TARGET} PRIVATE \"$<$<NOT:$<CONFIG:Debug>>:-O3>\")\n  target_compile_definitions(${TARGET} PRIVATE \"$<$<NOT:$<CONFIG:Debug>>:NDEBUG>\")\nendfunction()\n\n# Flutter library and tool build rules.\nset(FLUTTER_MANAGED_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/flutter\")\nadd_subdirectory(${FLUTTER_MANAGED_DIR})\n\n# System-level dependencies.\nfind_package(PkgConfig REQUIRED)\npkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)\n\nadd_definitions(-DAPPLICATION_ID=\"${APPLICATION_ID}\")\n\n# Define the application target. To change its name, change BINARY_NAME above,\n# not the value here, or `flutter run` will no longer work.\n#\n# Any new source files that you add to the application should be added here.\nadd_executable(${BINARY_NAME}\n  \"main.cc\"\n  \"my_application.cc\"\n  \"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc\"\n)\n\n# Apply the standard set of build settings. This can be removed for applications\n# that need different build settings.\napply_standard_settings(${BINARY_NAME})\n\n# Add dependency libraries. Add any application-specific dependencies here.\ntarget_link_libraries(${BINARY_NAME} PRIVATE flutter)\ntarget_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)\n\n# Run the Flutter tool portions of the build. This must not be removed.\nadd_dependencies(${BINARY_NAME} flutter_assemble)\n\n# Only the install-generated bundle's copy of the executable will launch\n# correctly, since the resources must in the right relative locations. To avoid\n# people trying to run the unbundled copy, put it in a subdirectory instead of\n# the default top-level location.\nset_target_properties(${BINARY_NAME}\n  PROPERTIES\n  RUNTIME_OUTPUT_DIRECTORY \"${CMAKE_BINARY_DIR}/intermediates_do_not_run\"\n)\n\n# Generated plugin build rules, which manage building the plugins and adding\n# them to the application.\ninclude(flutter/generated_plugins.cmake)\n\n\n# === Installation ===\n# By default, \"installing\" just makes a relocatable bundle in the build\n# directory.\nset(BUILD_BUNDLE_DIR \"${PROJECT_BINARY_DIR}/bundle\")\nif(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)\n  set(CMAKE_INSTALL_PREFIX \"${BUILD_BUNDLE_DIR}\" CACHE PATH \"...\" FORCE)\nendif()\n\n# Start with a clean build bundle directory every time.\ninstall(CODE \"\n  file(REMOVE_RECURSE \\\"${BUILD_BUNDLE_DIR}/\\\")\n  \" COMPONENT Runtime)\n\nset(INSTALL_BUNDLE_DATA_DIR \"${CMAKE_INSTALL_PREFIX}/data\")\nset(INSTALL_BUNDLE_LIB_DIR \"${CMAKE_INSTALL_PREFIX}/lib\")\n\ninstall(TARGETS ${BINARY_NAME} RUNTIME DESTINATION \"${CMAKE_INSTALL_PREFIX}\"\n  COMPONENT Runtime)\n\ninstall(FILES \"${FLUTTER_ICU_DATA_FILE}\" DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\"\n  COMPONENT Runtime)\n\ninstall(FILES \"${FLUTTER_LIBRARY}\" DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n  COMPONENT Runtime)\n\nforeach(bundled_library ${PLUGIN_BUNDLED_LIBRARIES})\n  install(FILES \"${bundled_library}\"\n    DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n    COMPONENT Runtime)\nendforeach(bundled_library)\n\n# Fully re-copy the assets directory on each build to avoid having stale files\n# from a previous install.\nset(FLUTTER_ASSET_DIR_NAME \"flutter_assets\")\ninstall(CODE \"\n  file(REMOVE_RECURSE \\\"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\\\")\n  \" COMPONENT Runtime)\ninstall(DIRECTORY \"${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}\"\n  DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\" COMPONENT Runtime)\n\n# Install the AOT library on non-Debug builds only.\nif(NOT CMAKE_BUILD_TYPE MATCHES \"Debug\")\n  install(FILES \"${AOT_LIBRARY}\" DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n    COMPONENT Runtime)\nendif()\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/linux/flutter/CMakeLists.txt",
    "content": "# This file controls Flutter-level build steps. It should not be edited.\ncmake_minimum_required(VERSION 3.10)\n\nset(EPHEMERAL_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/ephemeral\")\n\n# Configuration provided via flutter tool.\ninclude(${EPHEMERAL_DIR}/generated_config.cmake)\n\n# TODO: Move the rest of this into files in ephemeral. See\n# https://github.com/flutter/flutter/issues/57146.\n\n# Serves the same purpose as list(TRANSFORM ... PREPEND ...),\n# which isn't available in 3.10.\nfunction(list_prepend LIST_NAME PREFIX)\n    set(NEW_LIST \"\")\n    foreach(element ${${LIST_NAME}})\n        list(APPEND NEW_LIST \"${PREFIX}${element}\")\n    endforeach(element)\n    set(${LIST_NAME} \"${NEW_LIST}\" PARENT_SCOPE)\nendfunction()\n\n# === Flutter Library ===\n# System-level dependencies.\nfind_package(PkgConfig REQUIRED)\npkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)\npkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)\npkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)\n\nset(FLUTTER_LIBRARY \"${EPHEMERAL_DIR}/libflutter_linux_gtk.so\")\n\n# Published to parent scope for install step.\nset(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)\nset(FLUTTER_ICU_DATA_FILE \"${EPHEMERAL_DIR}/icudtl.dat\" PARENT_SCOPE)\nset(PROJECT_BUILD_DIR \"${PROJECT_DIR}/build/\" PARENT_SCOPE)\nset(AOT_LIBRARY \"${PROJECT_DIR}/build/lib/libapp.so\" PARENT_SCOPE)\n\nlist(APPEND FLUTTER_LIBRARY_HEADERS\n  \"fl_basic_message_channel.h\"\n  \"fl_binary_codec.h\"\n  \"fl_binary_messenger.h\"\n  \"fl_dart_project.h\"\n  \"fl_engine.h\"\n  \"fl_json_message_codec.h\"\n  \"fl_json_method_codec.h\"\n  \"fl_message_codec.h\"\n  \"fl_method_call.h\"\n  \"fl_method_channel.h\"\n  \"fl_method_codec.h\"\n  \"fl_method_response.h\"\n  \"fl_plugin_registrar.h\"\n  \"fl_plugin_registry.h\"\n  \"fl_standard_message_codec.h\"\n  \"fl_standard_method_codec.h\"\n  \"fl_string_codec.h\"\n  \"fl_value.h\"\n  \"fl_view.h\"\n  \"flutter_linux.h\"\n)\nlist_prepend(FLUTTER_LIBRARY_HEADERS \"${EPHEMERAL_DIR}/flutter_linux/\")\nadd_library(flutter INTERFACE)\ntarget_include_directories(flutter INTERFACE\n  \"${EPHEMERAL_DIR}\"\n)\ntarget_link_libraries(flutter INTERFACE \"${FLUTTER_LIBRARY}\")\ntarget_link_libraries(flutter INTERFACE\n  PkgConfig::GTK\n  PkgConfig::GLIB\n  PkgConfig::GIO\n)\nadd_dependencies(flutter flutter_assemble)\n\n# === Flutter tool backend ===\n# _phony_ is a non-existent file to force this command to run every time,\n# since currently there's no way to get a full input/output list from the\n# flutter tool.\nadd_custom_command(\n  OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}\n    ${CMAKE_CURRENT_BINARY_DIR}/_phony_\n  COMMAND ${CMAKE_COMMAND} -E env\n    ${FLUTTER_TOOL_ENVIRONMENT}\n    \"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh\"\n      ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}\n  VERBATIM\n)\nadd_custom_target(flutter_assemble DEPENDS\n  \"${FLUTTER_LIBRARY}\"\n  ${FLUTTER_LIBRARY_HEADERS}\n)\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/linux/flutter/generated_plugin_registrant.cc",
    "content": "//\n//  Generated file. Do not edit.\n//\n\n// clang-format off\n\n#include \"generated_plugin_registrant.h\"\n\n\nvoid fl_register_plugins(FlPluginRegistry* registry) {\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/linux/flutter/generated_plugin_registrant.h",
    "content": "//\n//  Generated file. Do not edit.\n//\n\n// clang-format off\n\n#ifndef GENERATED_PLUGIN_REGISTRANT_\n#define GENERATED_PLUGIN_REGISTRANT_\n\n#include <flutter_linux/flutter_linux.h>\n\n// Registers Flutter plugins.\nvoid fl_register_plugins(FlPluginRegistry* registry);\n\n#endif  // GENERATED_PLUGIN_REGISTRANT_\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/linux/flutter/generated_plugins.cmake",
    "content": "#\n# Generated file, do not edit.\n#\n\nlist(APPEND FLUTTER_PLUGIN_LIST\n)\n\nlist(APPEND FLUTTER_FFI_PLUGIN_LIST\n)\n\nset(PLUGIN_BUNDLED_LIBRARIES)\n\nforeach(plugin ${FLUTTER_PLUGIN_LIST})\n  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})\n  target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})\nendforeach(plugin)\n\nforeach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})\n  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})\nendforeach(ffi_plugin)\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/linux/main.cc",
    "content": "#include \"my_application.h\"\n\nint main(int argc, char** argv) {\n  g_autoptr(MyApplication) app = my_application_new();\n  return g_application_run(G_APPLICATION(app), argc, argv);\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/linux/my_application.cc",
    "content": "#include \"my_application.h\"\n\n#include <flutter_linux/flutter_linux.h>\n#ifdef GDK_WINDOWING_X11\n#include <gdk/gdkx.h>\n#endif\n\n#include \"flutter/generated_plugin_registrant.h\"\n\nstruct _MyApplication {\n  GtkApplication parent_instance;\n  char** dart_entrypoint_arguments;\n};\n\nG_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)\n\n// Implements GApplication::activate.\nstatic void my_application_activate(GApplication* application) {\n  MyApplication* self = MY_APPLICATION(application);\n  GtkWindow* window =\n      GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));\n\n  // Use a header bar when running in GNOME as this is the common style used\n  // by applications and is the setup most users will be using (e.g. Ubuntu\n  // desktop).\n  // If running on X and not using GNOME then just use a traditional title bar\n  // in case the window manager does more exotic layout, e.g. tiling.\n  // If running on Wayland assume the header bar will work (may need changing\n  // if future cases occur).\n  gboolean use_header_bar = TRUE;\n#ifdef GDK_WINDOWING_X11\n  GdkScreen* screen = gtk_window_get_screen(window);\n  if (GDK_IS_X11_SCREEN(screen)) {\n    const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);\n    if (g_strcmp0(wm_name, \"GNOME Shell\") != 0) {\n      use_header_bar = FALSE;\n    }\n  }\n#endif\n  if (use_header_bar) {\n    GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());\n    gtk_widget_show(GTK_WIDGET(header_bar));\n    gtk_header_bar_set_title(header_bar, \"custom_binary_name\");\n    gtk_header_bar_set_show_close_button(header_bar, TRUE);\n    gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));\n  } else {\n    gtk_window_set_title(window, \"custom_binary_name\");\n  }\n\n  gtk_window_set_default_size(window, 1280, 720);\n  gtk_widget_show(GTK_WIDGET(window));\n\n  g_autoptr(FlDartProject) project = fl_dart_project_new();\n  fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);\n\n  FlView* view = fl_view_new(project);\n  gtk_widget_show(GTK_WIDGET(view));\n  gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));\n\n  fl_register_plugins(FL_PLUGIN_REGISTRY(view));\n\n  gtk_widget_grab_focus(GTK_WIDGET(view));\n}\n\n// Implements GApplication::local_command_line.\nstatic gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) {\n  MyApplication* self = MY_APPLICATION(application);\n  // Strip out the first argument as it is the binary name.\n  self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);\n\n  g_autoptr(GError) error = nullptr;\n  if (!g_application_register(application, nullptr, &error)) {\n     g_warning(\"Failed to register: %s\", error->message);\n     *exit_status = 1;\n     return TRUE;\n  }\n\n  g_application_activate(application);\n  *exit_status = 0;\n\n  return TRUE;\n}\n\n// Implements GObject::dispose.\nstatic void my_application_dispose(GObject* object) {\n  MyApplication* self = MY_APPLICATION(object);\n  g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);\n  G_OBJECT_CLASS(my_application_parent_class)->dispose(object);\n}\n\nstatic void my_application_class_init(MyApplicationClass* klass) {\n  G_APPLICATION_CLASS(klass)->activate = my_application_activate;\n  G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;\n  G_OBJECT_CLASS(klass)->dispose = my_application_dispose;\n}\n\nstatic void my_application_init(MyApplication* self) {}\n\nMyApplication* my_application_new() {\n  return MY_APPLICATION(g_object_new(my_application_get_type(),\n                                     \"application-id\", APPLICATION_ID,\n                                     \"flags\", G_APPLICATION_NON_UNIQUE,\n                                     nullptr));\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/linux/my_application.h",
    "content": "#ifndef FLUTTER_MY_APPLICATION_H_\n#define FLUTTER_MY_APPLICATION_H_\n\n#include <gtk/gtk.h>\n\nG_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,\n                     GtkApplication)\n\n/**\n * my_application_new:\n *\n * Creates a new Flutter-based application.\n *\n * Returns: a new #MyApplication.\n */\nMyApplication* my_application_new();\n\n#endif  // FLUTTER_MY_APPLICATION_H_\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/linux/packaging/appimage/make_config.yaml",
    "content": "display_name: Custom Binary Name\n\nicon: assets/logo.png\n\nkeywords:\n  - Hello\n  - World\n  - Test\n  - Application\n\ngeneric_name: Cool Application\n\nactions:\n  - name: Say Hi\n    label: say-hi\n    arguments:\n      - --say\n      - hi\n  - name: Say Bye\n    label: say-bye\n    arguments:\n      - --say\n      - bye\n\ncategories:\n  - Music\n\nstartup_notify: true\n\n# You can specify the shared libraries that you want to bundle with your app\n#\n# flutter_distributor automatically detects the shared libraries that your app\n# depends on, but you can also specify them manually here.\n# \n# The following example shows how to bundle the libcurl library with your app.\n#\n# include:\n#   - libcurl.so.4\ninclude: []"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/linux/packaging/deb/make_config.yaml",
    "content": "# the name used to display in the OS. Specifically desktop\n# entry name\ndisplay_name: Custom Binary Name\n\n# package name for debian/apt repository\n# the name should be all lowercase with -+.\npackage_name: custom-binary-name\n\nmaintainer:\n  name: LiJianying\n  email: lijy91@foxmail.com\n\nco_authors:\n  - name: Kingkor Roy Tirtho\n    email: krtirtho@gmail.com\n\n# enum options -> required, important, standard, optional, extra\n# refer: https://www.debian.org/doc/debian-policy/ch-archive.html#s-priorities\npriority: optional\n\n# enum options: admin, cli-mono, comm, database, debug, devel, doc, editors, education, electronics, embedded, fonts, games, gnome, gnu-r, gnustep, graphics, hamradio, haskell, httpd, interpreters, introspection, java, javascript, kde, kernel, libdevel, libs, lisp, localization, mail, math, metapackages, misc, net, news, ocaml, oldlibs, otherosfs, perl, php, python, ruby, rust, science, shells, sound, tasks, tex, text, utils, vcs, video, web, x11, xfce, zope\n# refer: https://www.debian.org/doc/debian-policy/ch-archive.html#s-subsections\nsection: x11\n\n# the size of binary in kilobyte\ninstalled_size: 6604\n\n# direct dependencies required by the application\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\n# dependencies:\n#   - libkeybinder-3.0-0 (>= 0.3.2)\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\n# build_dependencies_indep:\n#   - texinfo\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\n# build_dependencies:\n#   - kernel-headers-2.2.10 [!hurd-i386]\n#   - gnumach-dev [hurd-i386]\n#   - libluajit5.1-dev [i386 amd64 kfreebsd-i386 armel armhf powerpc mips]\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\n# recommended_dependencies:\n#   - neofetch\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\n# suggested_dependencies:\n#   - libkeybinder-3.0-0 (>= 0.3.2)\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\n# enhances:\n#   - spotube\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\n# pre_dependencies:\n#   - libc6\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html#packages-which-break-other-packages-breaks\n# breaks:\n#   - libspotify (<< 3.0.0)\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html#conflicting-binary-packages-conflicts\n# conflicts:\n#   - spotify\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html#virtual-packages-provides\n# provides:\n#   - libx11\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html#overwriting-files-and-replacing-packages-replaces\n# replaces:\n#   - spotify\n\nessential: false\n\npostinstall_scripts:\n  - echo `Installed my awesome app`\npostuninstall_scripts:\n  - echo `Surprised Pickachu face`\n\n# application icon path relative to project url\nicon: assets/logo.png\n\nkeywords:\n  - Hello\n  - World\n  - Test\n  - Application\n\n# a name to categorize the app into a section of application\ngeneric_name: Music Application\n\n# supported mime types that can be opened using this application\n# supported_mime_type:\n#   - audio/mpeg\n\n# shown when right clicked the desktop entry icons\n# actions:\n#   - Gallery\n#   - Create\n\n# the categories the application belong to\n# refer: https://specifications.freedesktop.org/menu-spec/latest/\ncategories:\n  - Music\n  - Media\n\n# let OS know if the application can be run on start_up. If it's false\n# the application will deny to the OS if it was added as a start_up\n# application\nstartup_notify: true\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/macos/.gitignore",
    "content": "# Flutter-related\n**/Flutter/ephemeral/\n**/Pods/\n\n# Xcode-related\n**/dgph\n**/xcuserdata/\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/macos/Flutter/Flutter-Debug.xcconfig",
    "content": "#include \"ephemeral/Flutter-Generated.xcconfig\"\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/macos/Flutter/Flutter-Release.xcconfig",
    "content": "#include \"ephemeral/Flutter-Generated.xcconfig\"\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/macos/Flutter/GeneratedPluginRegistrant.swift",
    "content": "//\n//  Generated file. Do not edit.\n//\n\nimport FlutterMacOS\nimport Foundation\n\n\nfunc RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/macos/Runner/AppDelegate.swift",
    "content": "import Cocoa\nimport FlutterMacOS\n\n@NSApplicationMain\nclass AppDelegate: FlutterAppDelegate {\n  override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {\n    return true\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"size\" : \"16x16\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_16.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"16x16\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_32.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"32x32\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_32.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"32x32\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_64.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"128x128\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_128.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"128x128\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_256.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"256x256\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_256.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"256x256\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_512.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"512x512\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_512.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"512x512\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_1024.png\",\n      \"scale\" : \"2x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/macos/Runner/Base.lproj/MainMenu.xib",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion=\"14490.70\" targetRuntime=\"MacOSX.Cocoa\" propertyAccessControl=\"none\" useAutolayout=\"YES\" customObjectInstantitationMethod=\"direct\">\n    <dependencies>\n        <deployment identifier=\"macosx\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.CocoaPlugin\" version=\"14490.70\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <objects>\n        <customObject id=\"-2\" userLabel=\"File's Owner\" customClass=\"NSApplication\">\n            <connections>\n                <outlet property=\"delegate\" destination=\"Voe-Tx-rLC\" id=\"GzC-gU-4Uq\"/>\n            </connections>\n        </customObject>\n        <customObject id=\"-1\" userLabel=\"First Responder\" customClass=\"FirstResponder\"/>\n        <customObject id=\"-3\" userLabel=\"Application\" customClass=\"NSObject\"/>\n        <customObject id=\"Voe-Tx-rLC\" customClass=\"AppDelegate\" customModule=\"Runner\" customModuleProvider=\"target\">\n            <connections>\n                <outlet property=\"applicationMenu\" destination=\"uQy-DD-JDr\" id=\"XBo-yE-nKs\"/>\n                <outlet property=\"mainFlutterWindow\" destination=\"QvC-M9-y7g\" id=\"gIp-Ho-8D9\"/>\n            </connections>\n        </customObject>\n        <customObject id=\"YLy-65-1bz\" customClass=\"NSFontManager\"/>\n        <menu title=\"Main Menu\" systemMenu=\"main\" id=\"AYu-sK-qS6\">\n            <items>\n                <menuItem title=\"APP_NAME\" id=\"1Xt-HY-uBw\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"APP_NAME\" systemMenu=\"apple\" id=\"uQy-DD-JDr\">\n                        <items>\n                            <menuItem title=\"About APP_NAME\" id=\"5kV-Vb-QxS\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"orderFrontStandardAboutPanel:\" target=\"-1\" id=\"Exp-CZ-Vem\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"VOq-y0-SEH\"/>\n                            <menuItem title=\"Preferences…\" keyEquivalent=\",\" id=\"BOF-NM-1cW\"/>\n                            <menuItem isSeparatorItem=\"YES\" id=\"wFC-TO-SCJ\"/>\n                            <menuItem title=\"Services\" id=\"NMo-om-nkz\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Services\" systemMenu=\"services\" id=\"hz9-B4-Xy5\"/>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"4je-JR-u6R\"/>\n                            <menuItem title=\"Hide APP_NAME\" keyEquivalent=\"h\" id=\"Olw-nP-bQN\">\n                                <connections>\n                                    <action selector=\"hide:\" target=\"-1\" id=\"PnN-Uc-m68\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Hide Others\" keyEquivalent=\"h\" id=\"Vdr-fp-XzO\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"hideOtherApplications:\" target=\"-1\" id=\"VT4-aY-XCT\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Show All\" id=\"Kd2-mp-pUS\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"unhideAllApplications:\" target=\"-1\" id=\"Dhg-Le-xox\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"kCx-OE-vgT\"/>\n                            <menuItem title=\"Quit APP_NAME\" keyEquivalent=\"q\" id=\"4sb-4s-VLi\">\n                                <connections>\n                                    <action selector=\"terminate:\" target=\"-1\" id=\"Te7-pn-YzF\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"Edit\" id=\"5QF-Oa-p0T\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"Edit\" id=\"W48-6f-4Dl\">\n                        <items>\n                            <menuItem title=\"Undo\" keyEquivalent=\"z\" id=\"dRJ-4n-Yzg\">\n                                <connections>\n                                    <action selector=\"undo:\" target=\"-1\" id=\"M6e-cu-g7V\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Redo\" keyEquivalent=\"Z\" id=\"6dh-zS-Vam\">\n                                <connections>\n                                    <action selector=\"redo:\" target=\"-1\" id=\"oIA-Rs-6OD\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"WRV-NI-Exz\"/>\n                            <menuItem title=\"Cut\" keyEquivalent=\"x\" id=\"uRl-iY-unG\">\n                                <connections>\n                                    <action selector=\"cut:\" target=\"-1\" id=\"YJe-68-I9s\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Copy\" keyEquivalent=\"c\" id=\"x3v-GG-iWU\">\n                                <connections>\n                                    <action selector=\"copy:\" target=\"-1\" id=\"G1f-GL-Joy\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Paste\" keyEquivalent=\"v\" id=\"gVA-U4-sdL\">\n                                <connections>\n                                    <action selector=\"paste:\" target=\"-1\" id=\"UvS-8e-Qdg\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Paste and Match Style\" keyEquivalent=\"V\" id=\"WeT-3V-zwk\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"pasteAsPlainText:\" target=\"-1\" id=\"cEh-KX-wJQ\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Delete\" id=\"pa3-QI-u2k\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"delete:\" target=\"-1\" id=\"0Mk-Ml-PaM\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Select All\" keyEquivalent=\"a\" id=\"Ruw-6m-B2m\">\n                                <connections>\n                                    <action selector=\"selectAll:\" target=\"-1\" id=\"VNm-Mi-diN\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"uyl-h8-XO2\"/>\n                            <menuItem title=\"Find\" id=\"4EN-yA-p0u\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Find\" id=\"1b7-l0-nxx\">\n                                    <items>\n                                        <menuItem title=\"Find…\" tag=\"1\" keyEquivalent=\"f\" id=\"Xz5-n4-O0W\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"cD7-Qs-BN4\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Find and Replace…\" tag=\"12\" keyEquivalent=\"f\" id=\"YEy-JH-Tfz\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"WD3-Gg-5AJ\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Find Next\" tag=\"2\" keyEquivalent=\"g\" id=\"q09-fT-Sye\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"NDo-RZ-v9R\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Find Previous\" tag=\"3\" keyEquivalent=\"G\" id=\"OwM-mh-QMV\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"HOh-sY-3ay\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Use Selection for Find\" tag=\"7\" keyEquivalent=\"e\" id=\"buJ-ug-pKt\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"U76-nv-p5D\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Jump to Selection\" keyEquivalent=\"j\" id=\"S0p-oC-mLd\">\n                                            <connections>\n                                                <action selector=\"centerSelectionInVisibleArea:\" target=\"-1\" id=\"IOG-6D-g5B\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Spelling and Grammar\" id=\"Dv1-io-Yv7\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Spelling\" id=\"3IN-sU-3Bg\">\n                                    <items>\n                                        <menuItem title=\"Show Spelling and Grammar\" keyEquivalent=\":\" id=\"HFo-cy-zxI\">\n                                            <connections>\n                                                <action selector=\"showGuessPanel:\" target=\"-1\" id=\"vFj-Ks-hy3\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Check Document Now\" keyEquivalent=\";\" id=\"hz2-CU-CR7\">\n                                            <connections>\n                                                <action selector=\"checkSpelling:\" target=\"-1\" id=\"fz7-VC-reM\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"bNw-od-mp5\"/>\n                                        <menuItem title=\"Check Spelling While Typing\" id=\"rbD-Rh-wIN\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleContinuousSpellChecking:\" target=\"-1\" id=\"7w6-Qz-0kB\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Check Grammar With Spelling\" id=\"mK6-2p-4JG\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleGrammarChecking:\" target=\"-1\" id=\"muD-Qn-j4w\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Correct Spelling Automatically\" id=\"78Y-hA-62v\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticSpellingCorrection:\" target=\"-1\" id=\"2lM-Qi-WAP\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Substitutions\" id=\"9ic-FL-obx\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Substitutions\" id=\"FeM-D8-WVr\">\n                                    <items>\n                                        <menuItem title=\"Show Substitutions\" id=\"z6F-FW-3nz\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"orderFrontSubstitutionsPanel:\" target=\"-1\" id=\"oku-mr-iSq\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"gPx-C9-uUO\"/>\n                                        <menuItem title=\"Smart Copy/Paste\" id=\"9yt-4B-nSM\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleSmartInsertDelete:\" target=\"-1\" id=\"3IJ-Se-DZD\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smart Quotes\" id=\"hQb-2v-fYv\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticQuoteSubstitution:\" target=\"-1\" id=\"ptq-xd-QOA\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smart Dashes\" id=\"rgM-f4-ycn\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticDashSubstitution:\" target=\"-1\" id=\"oCt-pO-9gS\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smart Links\" id=\"cwL-P1-jid\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticLinkDetection:\" target=\"-1\" id=\"Gip-E3-Fov\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Data Detectors\" id=\"tRr-pd-1PS\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticDataDetection:\" target=\"-1\" id=\"R1I-Nq-Kbl\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Text Replacement\" id=\"HFQ-gK-NFA\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticTextReplacement:\" target=\"-1\" id=\"DvP-Fe-Py6\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Transformations\" id=\"2oI-Rn-ZJC\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Transformations\" id=\"c8a-y6-VQd\">\n                                    <items>\n                                        <menuItem title=\"Make Upper Case\" id=\"vmV-6d-7jI\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"uppercaseWord:\" target=\"-1\" id=\"sPh-Tk-edu\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Make Lower Case\" id=\"d9M-CD-aMd\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"lowercaseWord:\" target=\"-1\" id=\"iUZ-b5-hil\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Capitalize\" id=\"UEZ-Bs-lqG\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"capitalizeWord:\" target=\"-1\" id=\"26H-TL-nsh\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Speech\" id=\"xrE-MZ-jX0\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Speech\" id=\"3rS-ZA-NoH\">\n                                    <items>\n                                        <menuItem title=\"Start Speaking\" id=\"Ynk-f8-cLZ\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"startSpeaking:\" target=\"-1\" id=\"654-Ng-kyl\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Stop Speaking\" id=\"Oyz-dy-DGm\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"stopSpeaking:\" target=\"-1\" id=\"dX8-6p-jy9\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"View\" id=\"H8h-7b-M4v\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"View\" id=\"HyV-fh-RgO\">\n                        <items>\n                            <menuItem title=\"Enter Full Screen\" keyEquivalent=\"f\" id=\"4J7-dP-txa\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" control=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"toggleFullScreen:\" target=\"-1\" id=\"dU3-MA-1Rq\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"Window\" id=\"aUF-d1-5bR\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"Window\" systemMenu=\"window\" id=\"Td7-aD-5lo\">\n                        <items>\n                            <menuItem title=\"Minimize\" keyEquivalent=\"m\" id=\"OY7-WF-poV\">\n                                <connections>\n                                    <action selector=\"performMiniaturize:\" target=\"-1\" id=\"VwT-WD-YPe\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Zoom\" id=\"R4o-n2-Eq4\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"performZoom:\" target=\"-1\" id=\"DIl-cC-cCs\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"eu3-7i-yIM\"/>\n                            <menuItem title=\"Bring All to Front\" id=\"LE2-aR-0XJ\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"arrangeInFront:\" target=\"-1\" id=\"DRN-fu-gQh\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"Help\" id=\"EPT-qC-fAb\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"Help\" systemMenu=\"help\" id=\"rJ0-wn-3NY\"/>\n                </menuItem>\n            </items>\n            <point key=\"canvasLocation\" x=\"142\" y=\"-258\"/>\n        </menu>\n        <window title=\"APP_NAME\" allowsToolTipsWhenApplicationIsInactive=\"NO\" autorecalculatesKeyViewLoop=\"NO\" releasedWhenClosed=\"NO\" animationBehavior=\"default\" id=\"QvC-M9-y7g\" customClass=\"MainFlutterWindow\" customModule=\"Runner\" customModuleProvider=\"target\">\n            <windowStyleMask key=\"styleMask\" titled=\"YES\" closable=\"YES\" miniaturizable=\"YES\" resizable=\"YES\"/>\n            <rect key=\"contentRect\" x=\"335\" y=\"390\" width=\"800\" height=\"600\"/>\n            <rect key=\"screenRect\" x=\"0.0\" y=\"0.0\" width=\"2560\" height=\"1577\"/>\n            <view key=\"contentView\" wantsLayer=\"YES\" id=\"EiT-Mj-1SZ\">\n                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"800\" height=\"600\"/>\n                <autoresizingMask key=\"autoresizingMask\"/>\n            </view>\n        </window>\n    </objects>\n</document>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/macos/Runner/Configs/AppInfo.xcconfig",
    "content": "// Application-level settings for the Runner target.\n//\n// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the\n// future. If not, the values below would default to using the project name when this becomes a\n// 'flutter create' template.\n\n// The application's name. By default this is also the title of the Flutter window.\nPRODUCT_NAME = custom_binary_name\n\n// The application's bundle identifier\nPRODUCT_BUNDLE_IDENTIFIER = com.example.customBinaryName\n\n// The copyright displayed in application information\nPRODUCT_COPYRIGHT = Copyright © 2026 com.example. All rights reserved.\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/macos/Runner/Configs/Debug.xcconfig",
    "content": "#include \"../../Flutter/Flutter-Debug.xcconfig\"\n#include \"Warnings.xcconfig\"\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/macos/Runner/Configs/Release.xcconfig",
    "content": "#include \"../../Flutter/Flutter-Release.xcconfig\"\n#include \"Warnings.xcconfig\"\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/macos/Runner/Configs/Warnings.xcconfig",
    "content": "WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings\nGCC_WARN_UNDECLARED_SELECTOR = YES\nCLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES\nCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE\nCLANG_WARN__DUPLICATE_METHOD_MATCH = YES\nCLANG_WARN_PRAGMA_PACK = YES\nCLANG_WARN_STRICT_PROTOTYPES = YES\nCLANG_WARN_COMMA = YES\nGCC_WARN_STRICT_SELECTOR_MATCH = YES\nCLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES\nCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES\nGCC_WARN_SHADOW = YES\nCLANG_WARN_UNREACHABLE_CODE = YES\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/macos/Runner/DebugProfile.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.app-sandbox</key>\n\t<true/>\n\t<key>com.apple.security.cs.allow-jit</key>\n\t<true/>\n\t<key>com.apple.security.network.server</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/macos/Runner/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIconFile</key>\n\t<string></string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(FLUTTER_BUILD_NAME)</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(FLUTTER_BUILD_NUMBER)</string>\n\t<key>LSMinimumSystemVersion</key>\n\t<string>$(MACOSX_DEPLOYMENT_TARGET)</string>\n\t<key>NSHumanReadableCopyright</key>\n\t<string>$(PRODUCT_COPYRIGHT)</string>\n\t<key>NSMainNibFile</key>\n\t<string>MainMenu</string>\n\t<key>NSPrincipalClass</key>\n\t<string>NSApplication</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/macos/Runner/MainFlutterWindow.swift",
    "content": "import Cocoa\nimport FlutterMacOS\n\nclass MainFlutterWindow: NSWindow {\n  override func awakeFromNib() {\n    let flutterViewController = FlutterViewController()\n    let windowFrame = self.frame\n    self.contentViewController = flutterViewController\n    self.setFrame(windowFrame, display: true)\n\n    RegisterGeneratedPlugins(registry: flutterViewController)\n\n    super.awakeFromNib()\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/macos/Runner/Release.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.app-sandbox</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/macos/Runner.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXAggregateTarget section */\n\t\t33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {\n\t\t\tisa = PBXAggregateTarget;\n\t\t\tbuildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget \"Flutter Assemble\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t33CC111E2044C6BF0003C045 /* ShellScript */,\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = \"Flutter Assemble\";\n\t\t\tproductName = FLX;\n\t\t};\n/* End PBXAggregateTarget section */\n\n/* Begin PBXBuildFile section */\n\t\t331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };\n\t\t335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };\n\t\t33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };\n\t\t33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };\n\t\t33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };\n\t\t33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXContainerItemProxy section */\n\t\t331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 33CC10E52044A3C60003C045 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 33CC10EC2044A3C60003C045;\n\t\t\tremoteInfo = Runner;\n\t\t};\n\t\t33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 33CC10E52044A3C60003C045 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 33CC111A2044C6BA0003C045;\n\t\t\tremoteInfo = FLX;\n\t\t};\n/* End PBXContainerItemProxy section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\t33CC110E2044A8840003C045 /* Bundle Framework */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 10;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tname = \"Bundle Framework\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\t331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = \"<group>\"; };\n\t\t333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = \"<group>\"; };\n\t\t335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = \"<group>\"; };\n\t\t33CC10ED2044A3C60003C045 /* custom_binary_name.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = \"custom_binary_name.app\"; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = \"<group>\"; };\n\t\t33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = \"<group>\"; };\n\t\t33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = \"<group>\"; };\n\t\t33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = \"Flutter-Debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = \"Flutter-Release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = \"Flutter-Generated.xcconfig\"; path = \"ephemeral/Flutter-Generated.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = \"<group>\"; };\n\t\t33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = \"<group>\"; };\n\t\t33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = \"<group>\"; };\n\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = \"<group>\"; };\n\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t331C80D2294CF70F00263BE5 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t33CC10EA2044A3C60003C045 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t331C80D6294CF71000263BE5 /* RunnerTests */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t331C80D7294CF71000263BE5 /* RunnerTests.swift */,\n\t\t\t);\n\t\t\tpath = RunnerTests;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33BA886A226E78AF003329D5 /* Configs */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33E5194F232828860026EE4D /* AppInfo.xcconfig */,\n\t\t\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */,\n\t\t\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */,\n\t\t\t\t333000ED22D3DE5D00554162 /* Warnings.xcconfig */,\n\t\t\t);\n\t\t\tpath = Configs;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CC10E42044A3C60003C045 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33FAB671232836740065AC1E /* Runner */,\n\t\t\t\t33CEB47122A05771004F2AC0 /* Flutter */,\n\t\t\t\t331C80D6294CF71000263BE5 /* RunnerTests */,\n\t\t\t\t33CC10EE2044A3C60003C045 /* Products */,\n\t\t\t\tD73912EC22F37F3D000D13A0 /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CC10EE2044A3C60003C045 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10ED2044A3C60003C045 /* custom_binary_name.app */,\n\t\t\t\t331C80D5294CF71000263BE5 /* RunnerTests.xctest */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CC11242044D66E0003C045 /* Resources */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10F22044A3C60003C045 /* Assets.xcassets */,\n\t\t\t\t33CC10F42044A3C60003C045 /* MainMenu.xib */,\n\t\t\t\t33CC10F72044A3C60003C045 /* Info.plist */,\n\t\t\t);\n\t\t\tname = Resources;\n\t\t\tpath = ..;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CEB47122A05771004F2AC0 /* Flutter */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,\n\t\t\t\t33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,\n\t\t\t\t33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,\n\t\t\t\t33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,\n\t\t\t);\n\t\t\tpath = Flutter;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33FAB671232836740065AC1E /* Runner */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10F02044A3C60003C045 /* AppDelegate.swift */,\n\t\t\t\t33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,\n\t\t\t\t33E51913231747F40026EE4D /* DebugProfile.entitlements */,\n\t\t\t\t33E51914231749380026EE4D /* Release.entitlements */,\n\t\t\t\t33CC11242044D66E0003C045 /* Resources */,\n\t\t\t\t33BA886A226E78AF003329D5 /* Configs */,\n\t\t\t);\n\t\t\tpath = Runner;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD73912EC22F37F3D000D13A0 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t331C80D4294CF70F00263BE5 /* RunnerTests */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget \"RunnerTests\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t331C80D1294CF70F00263BE5 /* Sources */,\n\t\t\t\t331C80D2294CF70F00263BE5 /* Frameworks */,\n\t\t\t\t331C80D3294CF70F00263BE5 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t331C80DA294CF71000263BE5 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = RunnerTests;\n\t\t\tproductName = RunnerTests;\n\t\t\tproductReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */;\n\t\t\tproductType = \"com.apple.product-type.bundle.unit-test\";\n\t\t};\n\t\t33CC10EC2044A3C60003C045 /* Runner */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget \"Runner\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t33CC10E92044A3C60003C045 /* Sources */,\n\t\t\t\t33CC10EA2044A3C60003C045 /* Frameworks */,\n\t\t\t\t33CC10EB2044A3C60003C045 /* Resources */,\n\t\t\t\t33CC110E2044A8840003C045 /* Bundle Framework */,\n\t\t\t\t3399D490228B24CF009A79C7 /* ShellScript */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t33CC11202044C79F0003C045 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = Runner;\n\t\t\tproductName = Runner;\n\t\t\tproductReference = 33CC10ED2044A3C60003C045 /* custom_binary_name.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t33CC10E52044A3C60003C045 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastSwiftUpdateCheck = 0920;\n\t\t\t\tLastUpgradeCheck = 1300;\n\t\t\t\tORGANIZATIONNAME = \"\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t331C80D4294CF70F00263BE5 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 14.0;\n\t\t\t\t\t\tTestTargetID = 33CC10EC2044A3C60003C045;\n\t\t\t\t\t};\n\t\t\t\t\t33CC10EC2044A3C60003C045 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.2;\n\t\t\t\t\t\tLastSwiftMigration = 1100;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t\tSystemCapabilities = {\n\t\t\t\t\t\t\tcom.apple.Sandbox = {\n\t\t\t\t\t\t\t\tenabled = 1;\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t};\n\t\t\t\t\t};\n\t\t\t\t\t33CC111A2044C6BA0003C045 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.2;\n\t\t\t\t\t\tProvisioningStyle = Manual;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject \"Runner\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 33CC10E42044A3C60003C045;\n\t\t\tproductRefGroup = 33CC10EE2044A3C60003C045 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t33CC10EC2044A3C60003C045 /* Runner */,\n\t\t\t\t331C80D4294CF70F00263BE5 /* RunnerTests */,\n\t\t\t\t33CC111A2044C6BA0003C045 /* Flutter Assemble */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t331C80D3294CF70F00263BE5 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t33CC10EB2044A3C60003C045 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,\n\t\t\t\t33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t3399D490228B24CF009A79C7 /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\talwaysOutOfDate = 1;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"echo \\\"$PRODUCT_NAME.app\\\" > \\\"$PROJECT_DIR\\\"/Flutter/ephemeral/.app_filename && \\\"$FLUTTER_ROOT\\\"/packages/flutter_tools/bin/macos_assemble.sh embed\\n\";\n\t\t};\n\t\t33CC111E2044C6BF0003C045 /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t\tFlutter/ephemeral/FlutterInputs.xcfilelist,\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\tFlutter/ephemeral/tripwire,\n\t\t\t);\n\t\t\toutputFileListPaths = (\n\t\t\t\tFlutter/ephemeral/FlutterOutputs.xcfilelist,\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"\\\"$FLUTTER_ROOT\\\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire\";\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t331C80D1294CF70F00263BE5 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t33CC10E92044A3C60003C045 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,\n\t\t\t\t33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,\n\t\t\t\t335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXTargetDependency section */\n\t\t331C80DA294CF71000263BE5 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 33CC10EC2044A3C60003C045 /* Runner */;\n\t\t\ttargetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */;\n\t\t};\n\t\t33CC11202044C79F0003C045 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;\n\t\t\ttargetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;\n\t\t};\n/* End PBXTargetDependency section */\n\n/* Begin PBXVariantGroup section */\n\t\t33CC10F42044A3C60003C045 /* MainMenu.xib */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10F52044A3C60003C045 /* Base */,\n\t\t\t);\n\t\t\tname = MainMenu.xib;\n\t\t\tpath = Runner;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t331C80DB294CF71000263BE5 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.example.customBinaryName.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/custom_binary_name.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/custom_binary_name\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t331C80DC294CF71000263BE5 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.example.customBinaryName.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/custom_binary_name.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/custom_binary_name\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t331C80DD294CF71000263BE5 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = com.example.customBinaryName.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/custom_binary_name.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/custom_binary_name\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t338D0CE9231458BD00FA5F75 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.14;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t338D0CEA231458BD00FA5F75 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t338D0CEB231458BD00FA5F75 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t33CC10F92044A3C60003C045 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.14;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t33CC10FA2044A3C60003C045 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.14;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t33CC10FC2044A3C60003C045 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t33CC10FD2044A3C60003C045 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t33CC111C2044C6BA0003C045 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t33CC111D2044C6BA0003C045 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget \"RunnerTests\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t331C80DB294CF71000263BE5 /* Debug */,\n\t\t\t\t331C80DC294CF71000263BE5 /* Release */,\n\t\t\t\t331C80DD294CF71000263BE5 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t33CC10E82044A3C60003C045 /* Build configuration list for PBXProject \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t33CC10F92044A3C60003C045 /* Debug */,\n\t\t\t\t33CC10FA2044A3C60003C045 /* Release */,\n\t\t\t\t338D0CE9231458BD00FA5F75 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t33CC10FC2044A3C60003C045 /* Debug */,\n\t\t\t\t33CC10FD2044A3C60003C045 /* Release */,\n\t\t\t\t338D0CEA231458BD00FA5F75 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget \"Flutter Assemble\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t33CC111C2044C6BA0003C045 /* Debug */,\n\t\t\t\t33CC111D2044C6BA0003C045 /* Release */,\n\t\t\t\t338D0CEB231458BD00FA5F75 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 33CC10E52044A3C60003C045 /* Project object */;\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1300\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n               BuildableName = \"custom_binary_name.app\"\n               BlueprintName = \"Runner\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"custom_binary_name.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n      <Testables>\n         <TestableReference\n            skipped = \"NO\"\n            parallelizable = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"331C80D4294CF70F00263BE5\"\n               BuildableName = \"RunnerTests.xctest\"\n               BlueprintName = \"RunnerTests\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </TestableReference>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"custom_binary_name.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Profile\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"custom_binary_name.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/macos/Runner.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:Runner.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/macos/RunnerTests/RunnerTests.swift",
    "content": "import FlutterMacOS\nimport Cocoa\nimport XCTest\n\nclass RunnerTests: XCTestCase {\n\n  func testExample() {\n    // If you add code to the Runner application, consider adding tests here.\n    // See https://developer.apple.com/documentation/xctest for more information about using XCTest.\n  }\n\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/macos/packaging/dmg/make_config.yaml",
    "content": "title: custom_binary_name\ncontents:\n  - x: 448\n    y: 344\n    type: link\n    path: \"/Applications\"\n  - x: 192\n    y: 344\n    type: file\n    path: custom_binary_name.app\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/pubspec.yaml",
    "content": "name: custom_binary_name\ndescription: A new Flutter project.\n# The following line prevents the package from being accidentally published to\n# pub.dev using `flutter pub publish`. This is preferred for private packages.\npublish_to: 'none' # Remove this line if you wish to publish to pub.dev\n\n# The following defines the version and build number for your application.\n# A version number is three numbers separated by dots, like 1.2.43\n# followed by an optional build number separated by a +.\n# Both the version and the builder number may be overridden in flutter\n# build by specifying --build-name and --build-number, respectively.\n# In Android, build-name is used as versionName while build-number used as versionCode.\n# Read more about Android versioning at https://developer.android.com/studio/publish/versioning\n# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.\n# Read more about iOS versioning at\n# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html\n# In Windows, build-name is used as the major, minor, and patch parts\n# of the product and file versions while build-number is used as the build suffix.\nversion: 1.0.0+1\n\nenvironment:\n  sdk: '>=2.16.0 <4.0.0'\n\n# Dependencies specify other packages that your package needs in order to work.\n# To automatically upgrade your package dependencies to the latest versions\n# consider running `flutter pub upgrade --major-versions`. Alternatively,\n# dependencies can be manually updated by changing the version numbers below to\n# the latest version available on pub.dev. To see which dependencies have newer\n# versions available, run `flutter pub outdated`.\ndependencies:\n  flutter:\n    sdk: flutter\n\n\n  # The following adds the Cupertino Icons font to your application.\n  # Use with the CupertinoIcons class for iOS style icons.\n  cupertino_icons: ^1.0.2\n\ndev_dependencies:\n  flutter_test:\n    sdk: flutter\n\n  # The \"flutter_lints\" package below contains a set of recommended lints to\n  # encourage good coding practices. The lint set provided by the package is\n  # activated in the `analysis_options.yaml` file located at the root of your\n  # package. See that file for information about deactivating specific lint\n  # rules and activating additional ones.\n  flutter_lints: ^2.0.0\n\n# For information on the generic Dart part of this file, see the\n# following page: https://dart.dev/tools/pub/pubspec\n\n# The following section is specific to Flutter packages.\nflutter:\n\n  # The following line ensures that the Material Icons font is\n  # included with your application, so that you can use the icons in\n  # the material Icons class.\n  uses-material-design: true\n\n  # To add assets to your application, add an assets section, like this:\n  # assets:\n  #   - images/a_dot_burr.jpeg\n  #   - images/a_dot_ham.jpeg\n\n  # An image asset can refer to one or more resolution-specific \"variants\", see\n  # https://flutter.dev/assets-and-images/#resolution-aware\n\n  # For details regarding adding assets from package dependencies, see\n  # https://flutter.dev/assets-and-images/#from-packages\n\n  # To add custom fonts to your application, add a fonts section here,\n  # in this \"flutter\" section. Each entry in this list should have a\n  # \"family\" key with the font family name, and a \"fonts\" key with a\n  # list giving the asset and other descriptors for the font. For\n  # example:\n  # fonts:\n  #   - family: Schyler\n  #     fonts:\n  #       - asset: fonts/Schyler-Regular.ttf\n  #       - asset: fonts/Schyler-Italic.ttf\n  #         style: italic\n  #   - family: Trajan Pro\n  #     fonts:\n  #       - asset: fonts/TrajanPro.ttf\n  #       - asset: fonts/TrajanPro_Bold.ttf\n  #         weight: 700\n  #\n  # For details regarding fonts from package dependencies,\n  # see https://flutter.dev/custom-fonts/#from-packages\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/release.sh",
    "content": "#!/bin/bash\n\ndart ../../packages/flutter_distributor/bin/main.dart release --name $1 --skip-clean --no-version-check"
  },
  {
    "path": "plugins/flutter_distributor/examples/custom_binary_name/test/widget_test.dart",
    "content": "// This is a basic Flutter widget test.\n//\n// To perform an interaction with a widget in your test, use the WidgetTester\n// utility in the flutter_test package. For example, you can send tap and scroll\n// gestures. You can also use WidgetTester to find child widgets in the widget\n// tree, read text, and verify that the values of widget properties are correct.\n\nimport 'package:flutter/material.dart';\nimport 'package:flutter_test/flutter_test.dart';\n\nimport 'package:custom_binary_name/main.dart';\n\nvoid main() {\n  testWidgets('Counter increments smoke test', (WidgetTester tester) async {\n    // Build our app and trigger a frame.\n    await tester.pumpWidget(const MyApp());\n\n    // Verify that our counter starts at 0.\n    expect(find.text('0'), findsOneWidget);\n    expect(find.text('1'), findsNothing);\n\n    // Tap the '+' icon and trigger a frame.\n    await tester.tap(find.byIcon(Icons.add));\n    await tester.pump();\n\n    // Verify that our counter has incremented.\n    expect(find.text('0'), findsNothing);\n    expect(find.text('1'), findsOneWidget);\n  });\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/.gitignore",
    "content": "# Miscellaneous\n*.class\n*.log\n*.pyc\n*.swp\n.DS_Store\n.atom/\n.buildlog/\n.history\n.svn/\nmigrate_working_dir/\n\n# IntelliJ related\n*.iml\n*.ipr\n*.iws\n.idea/\n\n# The .vscode folder contains launch configuration and tasks you configure in\n# VS Code which you may wish to be included in version control, so this line\n# is commented out by default.\n#.vscode/\n\n# Flutter/Dart/Pub related\n**/doc/api/\n**/ios/Flutter/.last_build_id\n.dart_tool/\n.flutter-plugins\n.flutter-plugins-dependencies\n.packages\n.pub-cache/\n.pub/\n/build/\n\n# Symbolication related\napp.*.symbols\n\n# Obfuscation related\napp.*.map.json\n\n# Android Studio will place build artifacts here\n/android/app/debug\n/android/app/profile\n/android/app/release\n\n/dist/"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/.metadata",
    "content": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrades etc.\n#\n# This file should be version controlled.\n\nversion:\n  revision: 9944297138845a94256f1cf37beb88ff9a8e811a\n  channel: stable\n\nproject_type: app\n\n# Tracks metadata for the flutter migrate command\nmigration:\n  platforms:\n    - platform: root\n      create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a\n      base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a\n    - platform: android\n      create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a\n      base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a\n    - platform: ios\n      create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a\n      base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a\n    - platform: linux\n      create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a\n      base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a\n    - platform: macos\n      create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a\n      base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a\n    - platform: web\n      create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a\n      base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a\n    - platform: windows\n      create_revision: 9944297138845a94256f1cf37beb88ff9a8e811a\n      base_revision: 9944297138845a94256f1cf37beb88ff9a8e811a\n\n  # User provided section\n\n  # List of Local paths (relative to this file) that should be\n  # ignored by the migrate tool.\n  #\n  # Files that are not part of the templates will be ignored by default.\n  unmanaged_files:\n    - 'lib/main.dart'\n    - 'ios/Runner.xcodeproj/project.pbxproj'\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/analysis_options.yaml",
    "content": "# This file configures the analyzer, which statically analyzes Dart code to\n# check for errors, warnings, and lints.\n#\n# The issues identified by the analyzer are surfaced in the UI of Dart-enabled\n# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be\n# invoked from the command line by running `flutter analyze`.\n\n# The following line activates a set of recommended lints for Flutter apps,\n# packages, and plugins designed to encourage good coding practices.\ninclude: package:flutter_lints/flutter.yaml\n\nlinter:\n  # The lint rules applied to this project can be customized in the\n  # section below to disable rules from the `package:flutter_lints/flutter.yaml`\n  # included above or to enable additional rules. A list of all available lints\n  # and their documentation is published at\n  # https://dart-lang.github.io/linter/lints/index.html.\n  #\n  # Instead of disabling a lint rule for the entire project in the\n  # section below, it can also be suppressed for a single line of code\n  # or a specific dart file by using the `// ignore: name_of_lint` and\n  # `// ignore_for_file: name_of_lint` syntax on the line or in the file\n  # producing the lint.\n  rules:\n    # avoid_print: false  # Uncomment to disable the `avoid_print` rule\n    # prefer_single_quotes: true  # Uncomment to enable the `prefer_single_quotes` rule\n\n# Additional information about this file can be found at\n# https://dart.dev/guides/language/analysis-options\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/android/.gitignore",
    "content": "gradle-wrapper.jar\n/.gradle\n/captures/\n/gradlew\n/gradlew.bat\n/local.properties\nGeneratedPluginRegistrant.java\n\n# Remember to never publicly share your keystore.\n# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app\nkey.properties\n**/*.keystore\n**/*.jks\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/android/app/build.gradle",
    "content": "def localProperties = new Properties()\ndef localPropertiesFile = rootProject.file('local.properties')\nif (localPropertiesFile.exists()) {\n    localPropertiesFile.withReader('UTF-8') { reader ->\n        localProperties.load(reader)\n    }\n}\n\ndef flutterRoot = localProperties.getProperty('flutter.sdk')\nif (flutterRoot == null) {\n    throw new GradleException(\"Flutter SDK not found. Define location with flutter.sdk in the local.properties file.\")\n}\n\ndef flutterVersionCode = localProperties.getProperty('flutter.versionCode')\nif (flutterVersionCode == null) {\n    flutterVersionCode = '1'\n}\n\ndef flutterVersionName = localProperties.getProperty('flutter.versionName')\nif (flutterVersionName == null) {\n    flutterVersionName = '1.0'\n}\n\napply plugin: 'com.android.application'\napply plugin: 'kotlin-android'\napply from: \"$flutterRoot/packages/flutter_tools/gradle/flutter.gradle\"\n\nandroid {\n    compileSdkVersion flutter.compileSdkVersion\n    ndkVersion flutter.ndkVersion\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n\n    kotlinOptions {\n        jvmTarget = '1.8'\n    }\n\n    sourceSets {\n        main.java.srcDirs += 'src/main/kotlin'\n    }\n\n    defaultConfig {\n        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).\n        applicationId \"org.leanflutter.examples.hello_world\"\n        // You can update the following values to match your application needs.\n        // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.\n        minSdkVersion flutter.minSdkVersion\n        targetSdkVersion flutter.targetSdkVersion\n        versionCode flutterVersionCode.toInteger()\n        versionName flutterVersionName\n    }\n\n    buildTypes {\n        release {\n            // TODO: Add your own signing config for the release build.\n            // Signing with the debug keys for now, so `flutter run --release` works.\n            signingConfig signingConfigs.debug\n        }\n    }\n}\n\nflutter {\n    source '../..'\n}\n\ndependencies {\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version\"\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/android/app/src/debug/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"org.leanflutter.examples.hello_world\">\n    <!-- Flutter needs it to communicate with the running application\n         to allow setting breakpoints, to provide hot reload, etc.\n    -->\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n</manifest>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/android/app/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"org.leanflutter.examples.hello_world\">\n   <application\n        android:label=\"hello_world\"\n        android:name=\"${applicationName}\"\n        android:icon=\"@mipmap/ic_launcher\">\n        <activity\n            android:name=\".MainActivity\"\n            android:exported=\"true\"\n            android:launchMode=\"singleTop\"\n            android:theme=\"@style/LaunchTheme\"\n            android:configChanges=\"orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode\"\n            android:hardwareAccelerated=\"true\"\n            android:windowSoftInputMode=\"adjustResize\">\n            <!-- Specifies an Android theme to apply to this Activity as soon as\n                 the Android process has started. This theme is visible to the user\n                 while the Flutter UI initializes. After that, this theme continues\n                 to determine the Window background behind the Flutter UI. -->\n            <meta-data\n              android:name=\"io.flutter.embedding.android.NormalTheme\"\n              android:resource=\"@style/NormalTheme\"\n              />\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\"/>\n                <category android:name=\"android.intent.category.LAUNCHER\"/>\n            </intent-filter>\n        </activity>\n        <!-- Don't delete the meta-data below.\n             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->\n        <meta-data\n            android:name=\"flutterEmbedding\"\n            android:value=\"2\" />\n    </application>\n</manifest>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/android/app/src/main/kotlin/org/leanflutter/examples/hello_world/MainActivity.kt",
    "content": "package org.leanflutter.examples.hello_world\n\nimport io.flutter.embedding.android.FlutterActivity\n\nclass MainActivity: FlutterActivity() {\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/android/app/src/main/res/drawable/launch_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Modify this file to customize your launch splash screen -->\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@android:color/white\" />\n\n    <!-- You can insert your own image assets here -->\n    <!-- <item>\n        <bitmap\n            android:gravity=\"center\"\n            android:src=\"@mipmap/launch_image\" />\n    </item> -->\n</layer-list>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/android/app/src/main/res/drawable-v21/launch_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Modify this file to customize your launch splash screen -->\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"?android:colorBackground\" />\n\n    <!-- You can insert your own image assets here -->\n    <!-- <item>\n        <bitmap\n            android:gravity=\"center\"\n            android:src=\"@mipmap/launch_image\" />\n    </item> -->\n</layer-list>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/android/app/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->\n    <style name=\"LaunchTheme\" parent=\"@android:style/Theme.Light.NoTitleBar\">\n        <!-- Show a splash screen on the activity. Automatically removed when\n             Flutter draws its first frame -->\n        <item name=\"android:windowBackground\">@drawable/launch_background</item>\n    </style>\n    <!-- Theme applied to the Android Window as soon as the process has started.\n         This theme determines the color of the Android Window while your\n         Flutter UI initializes, as well as behind your Flutter UI while its\n         running.\n\n         This Theme is only used starting with V2 of Flutter's Android embedding. -->\n    <style name=\"NormalTheme\" parent=\"@android:style/Theme.Light.NoTitleBar\">\n        <item name=\"android:windowBackground\">?android:colorBackground</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/android/app/src/main/res/values-night/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->\n    <style name=\"LaunchTheme\" parent=\"@android:style/Theme.Black.NoTitleBar\">\n        <!-- Show a splash screen on the activity. Automatically removed when\n             Flutter draws its first frame -->\n        <item name=\"android:windowBackground\">@drawable/launch_background</item>\n    </style>\n    <!-- Theme applied to the Android Window as soon as the process has started.\n         This theme determines the color of the Android Window while your\n         Flutter UI initializes, as well as behind your Flutter UI while its\n         running.\n\n         This Theme is only used starting with V2 of Flutter's Android embedding. -->\n    <style name=\"NormalTheme\" parent=\"@android:style/Theme.Black.NoTitleBar\">\n        <item name=\"android:windowBackground\">?android:colorBackground</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/android/app/src/profile/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    package=\"org.leanflutter.examples.hello_world\">\n    <!-- Flutter needs it to communicate with the running application\n         to allow setting breakpoints, to provide hot reload, etc.\n    -->\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n</manifest>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/android/build.gradle",
    "content": "buildscript {\n    ext.kotlin_version = '1.7.10'\n    repositories {\n        google()\n        mavenCentral()\n    }\n\n    dependencies {\n        classpath 'com.android.tools.build:gradle:7.2.0'\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\n\nrootProject.buildDir = '../build'\nsubprojects {\n    project.buildDir = \"${rootProject.buildDir}/${project.name}\"\n}\nsubprojects {\n    project.evaluationDependsOn(':app')\n}\n\ntasks.register(\"clean\", Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-7.5-all.zip\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/android/gradle.properties",
    "content": "org.gradle.jvmargs=-Xmx1536M\nandroid.useAndroidX=true\nandroid.enableJetifier=true\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/android/settings.gradle",
    "content": "include ':app'\n\ndef localPropertiesFile = new File(rootProject.projectDir, \"local.properties\")\ndef properties = new Properties()\n\nassert localPropertiesFile.exists()\nlocalPropertiesFile.withReader(\"UTF-8\") { reader -> properties.load(reader) }\n\ndef flutterSdkPath = properties.getProperty(\"flutter.sdk\")\nassert flutterSdkPath != null, \"flutter.sdk not set in local.properties\"\napply from: \"$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle\"\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/distribute_options.yaml",
    "content": "output: dist/\nvariables:\n  FLUTTER_ROOT: ~/fvm/versions/3.16.5\n  # PGYER_API_KEY: your api key\nreleases:\n  - name: dev-profile\n    jobs:\n      - name: android-aab\n        package:\n          platform: android\n          target: aab\n          build_args:\n            profile: true\n            target-platform: android-arm,android-arm64\n      - name: android-apk\n        package:\n          platform: android\n          target: apk\n          build_args:\n            profile: true\n            target-platform: android-arm,android-arm64\n      - name: ios-ipa\n        package:\n          platform: ios\n          target: ipa\n          build_args:\n            profile: true\n            export-method: ad-hoc\n      - name: linux-appimage\n        package:\n          platform: linux\n          target: appimage\n          build_args:\n            profile: true\n      - name: linux-deb\n        package:\n          platform: linux\n          target: deb\n          build_args:\n            profile: true\n      - name: linux-zip\n        package:\n          platform: linux\n          target: zip\n          build_args:\n            profile: true\n      - name: macos-dmg\n        package:\n          platform: macos\n          target: dmg\n          build_args:\n            profile: true\n      - name: macos-zip\n        package:\n          platform: macos\n          target: zip\n          build_args:\n            profile: true\n      - name: windows-exe\n        package:\n          platform: windows\n          target: exe\n          build_args:\n            profile: true\n      # msix does not support profile mode yet\n      # - name: windows-msix\n      #   package:\n      #     platform: windows\n      #     target: msix\n      #     build_args:\n      #       profile: true\n      - name: windows-zip\n        package:\n          platform: windows\n          target: zip\n          build_args:\n            profile: true\n  - name: dev-release\n    jobs:\n      - name: android-aab\n        variables:\n          FLUTTER_ROOT: ~/fvm/versions/3.10.3\n        package:\n          platform: android\n          target: aab\n          build_args:\n            target-platform: android-arm,android-arm64\n      - name: android-apk\n        package:\n          platform: android\n          target: apk\n          build_args:\n            target-platform: android-arm,android-arm64\n      - name: ios-ipa\n        package:\n          platform: ios\n          target: ipa\n          build_args:\n            export-method: ad-hoc\n      - name: linux-appimage\n        package:\n          platform: linux\n          target: appimage\n      - name: linux-deb\n        package:\n          platform: linux\n          target: deb\n      - name: linux-zip\n        package:\n          platform: linux\n          target: zip\n      - name: macos-dmg\n        package:\n          platform: macos\n          target: dmg\n          build_args:\n            dart-define-from-file: env.json\n      - name: macos-pkg\n        package:\n          platform: macos\n          target: pkg\n      - name: macos-zip\n        package:\n          platform: macos\n          target: zip\n      - name: windows-exe\n        package:\n          platform: windows\n          target: exe\n      - name: windows-msix\n        package:\n          platform: windows\n          target: msix\n      - name: windows-zip\n        package:\n          platform: windows\n          target: zip\n      - name: web-direct\n        package:\n          platform: web\n          target: direct\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/env.json",
    "content": "{\n  \"APP_ENV\": \"dev\"\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ios/.gitignore",
    "content": "**/dgph\n*.mode1v3\n*.mode2v3\n*.moved-aside\n*.pbxuser\n*.perspectivev3\n**/*sync/\n.sconsign.dblite\n.tags*\n**/.vagrant/\n**/DerivedData/\nIcon?\n**/Pods/\n**/.symlinks/\nprofile\nxcuserdata\n**/.generated/\nFlutter/App.framework\nFlutter/Flutter.framework\nFlutter/Flutter.podspec\nFlutter/Generated.xcconfig\nFlutter/ephemeral/\nFlutter/app.flx\nFlutter/app.zip\nFlutter/flutter_assets/\nFlutter/flutter_export_environment.sh\nServiceDefinitions.json\nRunner/GeneratedPluginRegistrant.*\n\n# Exceptions to above rules.\n!default.mode1v3\n!default.mode2v3\n!default.pbxuser\n!default.perspectivev3\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ios/ExportOptions.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>compileBitcode</key>\n\t<true/>\n\t<key>method</key>\n\t<string>ad-hoc</string>\n\t<key>signingStyle</key>\n\t<string>automatic</string>\n\t<key>stripSwiftSymbols</key>\n\t<true/>\n\t<key>teamID</key>\n\t<string>G83H824X6L</string>\n\t<key>thinning</key>\n\t<string>&lt;none&gt;</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ios/Flutter/AppFrameworkInfo.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n  <key>CFBundleDevelopmentRegion</key>\n  <string>en</string>\n  <key>CFBundleExecutable</key>\n  <string>App</string>\n  <key>CFBundleIdentifier</key>\n  <string>io.flutter.flutter.app</string>\n  <key>CFBundleInfoDictionaryVersion</key>\n  <string>6.0</string>\n  <key>CFBundleName</key>\n  <string>App</string>\n  <key>CFBundlePackageType</key>\n  <string>FMWK</string>\n  <key>CFBundleShortVersionString</key>\n  <string>1.0</string>\n  <key>CFBundleSignature</key>\n  <string>????</string>\n  <key>CFBundleVersion</key>\n  <string>1.0</string>\n  <key>MinimumOSVersion</key>\n  <string>11.0</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ios/Flutter/Debug.xcconfig",
    "content": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"\n#include \"Generated.xcconfig\"\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ios/Flutter/Release.xcconfig",
    "content": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"\n#include \"Generated.xcconfig\"\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ios/Podfile",
    "content": "# Uncomment this line to define a global platform for your project\n# platform :ios, '11.0'\n\n# CocoaPods analytics sends network stats synchronously affecting flutter build latency.\nENV['COCOAPODS_DISABLE_STATS'] = 'true'\n\nproject 'Runner', {\n  'Debug' => :debug,\n  'Profile' => :release,\n  'Release' => :release,\n}\n\ndef flutter_root\n  generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)\n  unless File.exist?(generated_xcode_build_settings_path)\n    raise \"#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first\"\n  end\n\n  File.foreach(generated_xcode_build_settings_path) do |line|\n    matches = line.match(/FLUTTER_ROOT\\=(.*)/)\n    return matches[1].strip if matches\n  end\n  raise \"FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get\"\nend\n\nrequire File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)\n\nflutter_ios_podfile_setup\n\ntarget 'Runner' do\n  use_frameworks!\n  use_modular_headers!\n\n  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))\nend\n\npost_install do |installer|\n  installer.pods_project.targets.each do |target|\n    flutter_additional_ios_build_settings(target)\n  end\nend\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ios/Runner/AppDelegate.swift",
    "content": "import UIKit\nimport Flutter\n\n@UIApplicationMain\n@objc class AppDelegate: FlutterAppDelegate {\n  override func application(\n    _ application: UIApplication,\n    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?\n  ) -> Bool {\n    GeneratedPluginRegistrant.register(with: self)\n    return super.application(application, didFinishLaunchingWithOptions: launchOptions)\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-20x20@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-20x20@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-29x29@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-29x29@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-29x29@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-40x40@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-40x40@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"60x60\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-60x60@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"60x60\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-60x60@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-20x20@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-20x20@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-29x29@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-29x29@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-40x40@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-40x40@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"76x76\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-76x76@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"76x76\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-76x76@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"83.5x83.5\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-83.5x83.5@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"1024x1024\",\n      \"idiom\" : \"ios-marketing\",\n      \"filename\" : \"Icon-App-1024x1024@1x.png\",\n      \"scale\" : \"1x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"LaunchImage.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"LaunchImage@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"LaunchImage@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ios/Runner/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"12121\" systemVersion=\"16G29\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"12089\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <layoutGuides>\n                        <viewControllerLayoutGuide type=\"top\" id=\"Ydg-fD-yQy\"/>\n                        <viewControllerLayoutGuide type=\"bottom\" id=\"xbc-2k-c8Z\"/>\n                    </layoutGuides>\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <imageView opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" image=\"LaunchImage\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"YRO-k0-Ey4\">\n                            </imageView>\n                        </subviews>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <constraints>\n                            <constraint firstItem=\"YRO-k0-Ey4\" firstAttribute=\"centerX\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"centerX\" id=\"1a2-6s-vTC\"/>\n                            <constraint firstItem=\"YRO-k0-Ey4\" firstAttribute=\"centerY\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"centerY\" id=\"4X2-HB-R7a\"/>\n                        </constraints>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <image name=\"LaunchImage\" width=\"168\" height=\"185\"/>\n    </resources>\n</document>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ios/Runner/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"10117\" systemVersion=\"15F34\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" initialViewController=\"BYZ-38-t0r\">\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"10085\"/>\n    </dependencies>\n    <scenes>\n        <!--Flutter View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"FlutterViewController\" sceneMemberID=\"viewController\">\n                    <layoutGuides>\n                        <viewControllerLayoutGuide type=\"top\" id=\"y3c-jy-aDJ\"/>\n                        <viewControllerLayoutGuide type=\"bottom\" id=\"wfy-db-euE\"/>\n                    </layoutGuides>\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"8bC-Xf-vdC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"600\" height=\"600\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"calibratedWhite\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ios/Runner/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>hello_world</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(FLUTTER_BUILD_NAME)</string>\n\t<key>CFBundleSignature</key>\n\t<string>????</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(FLUTTER_BUILD_NUMBER)</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UIViewControllerBasedStatusBarAppearance</key>\n\t<false/>\n\t<key>CADisableMinimumFrameDurationOnPhone</key>\n\t<true/>\n\t<key>UIApplicationSupportsIndirectInputEvents</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ios/Runner/Runner-Bridging-Header.h",
    "content": "#import \"GeneratedPluginRegistrant.h\"\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ios/Runner.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };\n\t\t3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };\n\t\t74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; };\n\t\t7AFFAC4C87DB09D7B5D8EF35 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EDB96FD4A96DA3C64710A751 /* Pods_Runner.framework */; };\n\t\t97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };\n\t\t97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };\n\t\t97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\t9705A1C41CF9048500538489 /* Embed Frameworks */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 10;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tname = \"Embed Frameworks\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\t0722C86F52094B5AFA9D74C3 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.release.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = \"<group>\"; };\n\t\t1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = \"<group>\"; };\n\t\t3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = \"<group>\"; };\n\t\t74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = \"Runner-Bridging-Header.h\"; sourceTree = \"<group>\"; };\n\t\t74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = \"<group>\"; };\n\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = \"<group>\"; };\n\t\t9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = \"<group>\"; };\n\t\t97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\t97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\t97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tD723D2BB8E0CB3CA5CD7CEE7 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.debug.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\tEDB96FD4A96DA3C64710A751 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tF420089D2E1AFAF26E3F1A48 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.profile.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig\"; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t97C146EB1CF9000F007C117D /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t7AFFAC4C87DB09D7B5D8EF35 /* Pods_Runner.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t03FD8A5FB3D39D2B3686A708 /* Pods */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tD723D2BB8E0CB3CA5CD7CEE7 /* Pods-Runner.debug.xcconfig */,\n\t\t\t\t0722C86F52094B5AFA9D74C3 /* Pods-Runner.release.xcconfig */,\n\t\t\t\tF420089D2E1AFAF26E3F1A48 /* Pods-Runner.profile.xcconfig */,\n\t\t\t);\n\t\t\tpath = Pods;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t9740EEB11CF90186004384FC /* Flutter */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,\n\t\t\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */,\n\t\t\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */,\n\t\t\t\t9740EEB31CF90195004384FC /* Generated.xcconfig */,\n\t\t\t);\n\t\t\tname = Flutter;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146E51CF9000F007C117D = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9740EEB11CF90186004384FC /* Flutter */,\n\t\t\t\t97C146F01CF9000F007C117D /* Runner */,\n\t\t\t\t97C146EF1CF9000F007C117D /* Products */,\n\t\t\t\t03FD8A5FB3D39D2B3686A708 /* Pods */,\n\t\t\t\tEC1423DB39D644697C41810D /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146EF1CF9000F007C117D /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t97C146EE1CF9000F007C117D /* Runner.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146F01CF9000F007C117D /* Runner */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t97C146FA1CF9000F007C117D /* Main.storyboard */,\n\t\t\t\t97C146FD1CF9000F007C117D /* Assets.xcassets */,\n\t\t\t\t97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,\n\t\t\t\t97C147021CF9000F007C117D /* Info.plist */,\n\t\t\t\t1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,\n\t\t\t\t1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,\n\t\t\t\t74858FAE1ED2DC5600515810 /* AppDelegate.swift */,\n\t\t\t\t74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */,\n\t\t\t);\n\t\t\tpath = Runner;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tEC1423DB39D644697C41810D /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tEDB96FD4A96DA3C64710A751 /* Pods_Runner.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t97C146ED1CF9000F007C117D /* Runner */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget \"Runner\" */;\n\t\t\tbuildPhases = (\n\t\t\t\tB5A13BEDCE69F7CBF88029BB /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t9740EEB61CF901F6004384FC /* Run Script */,\n\t\t\t\t97C146EA1CF9000F007C117D /* Sources */,\n\t\t\t\t97C146EB1CF9000F007C117D /* Frameworks */,\n\t\t\t\t97C146EC1CF9000F007C117D /* Resources */,\n\t\t\t\t9705A1C41CF9048500538489 /* Embed Frameworks */,\n\t\t\t\t3B06AD1E1E4923F5004D2608 /* Thin Binary */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = Runner;\n\t\t\tproductName = Runner;\n\t\t\tproductReference = 97C146EE1CF9000F007C117D /* Runner.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t97C146E61CF9000F007C117D /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastUpgradeCheck = 1430;\n\t\t\t\tORGANIZATIONNAME = \"\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t97C146ED1CF9000F007C117D = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 7.3.1;\n\t\t\t\t\t\tLastSwiftMigration = 1100;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject \"Runner\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 97C146E51CF9000F007C117D;\n\t\t\tproductRefGroup = 97C146EF1CF9000F007C117D /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t97C146ED1CF9000F007C117D /* Runner */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t97C146EC1CF9000F007C117D /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,\n\t\t\t\t3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,\n\t\t\t\t97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,\n\t\t\t\t97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\talwaysOutOfDate = 1;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${TARGET_BUILD_DIR}/${INFOPLIST_PATH}\",\n\t\t\t);\n\t\t\tname = \"Thin Binary\";\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"/bin/sh \\\"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\\\" embed_and_thin\";\n\t\t};\n\t\t9740EEB61CF901F6004384FC /* Run Script */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\talwaysOutOfDate = 1;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Run Script\";\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"/bin/sh \\\"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\\\" build\";\n\t\t};\n\t\tB5A13BEDCE69F7CBF88029BB /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t97C146EA1CF9000F007C117D /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */,\n\t\t\t\t1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXVariantGroup section */\n\t\t97C146FA1CF9000F007C117D /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t97C146FB1CF9000F007C117D /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t97C147001CF9000F007C117D /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t249021D3217E4FDB00AE95B9 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 11.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSUPPORTED_PLATFORMS = iphoneos;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t249021D4217E4FDB00AE95B9 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tDEVELOPMENT_TEAM = G83H824X6L;\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"org.leanflutter.examples.hello-world\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"Runner/Runner-Bridging-Header.h\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t97C147031CF9000F007C117D /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 11.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t97C147041CF9000F007C117D /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 11.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSUPPORTED_PLATFORMS = iphoneos;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t97C147061CF9000F007C117D /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tDEVELOPMENT_TEAM = G83H824X6L;\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"org.leanflutter.examples.hello-world\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"Runner/Runner-Bridging-Header.h\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t97C147071CF9000F007C117D /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tDEVELOPMENT_TEAM = G83H824X6L;\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = \"org.leanflutter.examples.hello-world\";\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_OBJC_BRIDGING_HEADER = \"Runner/Runner-Bridging-Header.h\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t97C146E91CF9000F007C117D /* Build configuration list for PBXProject \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t97C147031CF9000F007C117D /* Debug */,\n\t\t\t\t97C147041CF9000F007C117D /* Release */,\n\t\t\t\t249021D3217E4FDB00AE95B9 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t97C147061CF9000F007C117D /* Debug */,\n\t\t\t\t97C147071CF9000F007C117D /* Release */,\n\t\t\t\t249021D4217E4FDB00AE95B9 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 97C146E61CF9000F007C117D /* Project object */;\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>PreviewsEnabled</key>\n\t<false/>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1430\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n               BuildableName = \"Runner.app\"\n               BlueprintName = \"Runner\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <Testables>\n      </Testables>\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n      <AdditionalOptions>\n      </AdditionalOptions>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n      <AdditionalOptions>\n      </AdditionalOptions>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Profile\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ios/Runner.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:Runner.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:Pods/Pods.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>PreviewsEnabled</key>\n\t<false/>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/lib/main.dart",
    "content": "import 'package:flutter/material.dart';\n\nvoid main() {\n  runApp(const MyApp());\n}\n\nclass MyApp extends StatelessWidget {\n  const MyApp({Key? key}) : super(key: key);\n\n  // This widget is the root of your application.\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      title: 'Flutter Demo',\n      theme: ThemeData(\n        // This is the theme of your application.\n        //\n        // Try running your application with \"flutter run\". You'll see the\n        // application has a blue toolbar. Then, without quitting the app, try\n        // changing the primarySwatch below to Colors.green and then invoke\n        // \"hot reload\" (press \"r\" in the console where you ran \"flutter run\",\n        // or simply save your changes to \"hot reload\" in a Flutter IDE).\n        // Notice that the counter didn't reset back to zero; the application\n        // is not restarted.\n        primarySwatch: Colors.blue,\n      ),\n      home: const MyHomePage(title: 'Flutter Demo Home Page'),\n    );\n  }\n}\n\nclass MyHomePage extends StatefulWidget {\n  const MyHomePage({Key? key, required this.title}) : super(key: key);\n\n  // This widget is the home page of your application. It is stateful, meaning\n  // that it has a State object (defined below) that contains fields that affect\n  // how it looks.\n\n  // This class is the configuration for the state. It holds the values (in this\n  // case the title) provided by the parent (in this case the App widget) and\n  // used by the build method of the State. Fields in a Widget subclass are\n  // always marked \"final\".\n\n  final String title;\n\n  @override\n  State<MyHomePage> createState() => _MyHomePageState();\n}\n\nclass _MyHomePageState extends State<MyHomePage> {\n  int _counter = 0;\n\n  String get _appEnv {\n    return const String.fromEnvironment('APP_ENV');\n  }\n\n  String get _buildName {\n    return const String.fromEnvironment('FLUTTER_BUILD_NAME');\n  }\n\n  String get _buildNumber {\n    return const String.fromEnvironment('FLUTTER_BUILD_NUMBER');\n  }\n\n  void _incrementCounter() {\n    setState(() {\n      // This call to setState tells the Flutter framework that something has\n      // changed in this State, which causes it to rerun the build method below\n      // so that the display can reflect the updated values. If we changed\n      // _counter without calling setState(), then the build method would not be\n      // called again, and so nothing would appear to happen.\n      _counter++;\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    // This method is rerun every time setState is called, for instance as done\n    // by the _incrementCounter method above.\n    //\n    // The Flutter framework has been optimized to make rerunning build methods\n    // fast, so that you can just rebuild anything that needs updating rather\n    // than having to individually change instances of widgets.\n    return Scaffold(\n      appBar: AppBar(\n        // Here we take the value from the MyHomePage object that was created by\n        // the App.build method, and use it to set our appbar title.\n        title: Text(widget.title),\n      ),\n      body: Center(\n        // Center is a layout widget. It takes a single child and positions it\n        // in the middle of the parent.\n        child: Column(\n          // Column is also a layout widget. It takes a list of children and\n          // arranges them vertically. By default, it sizes itself to fit its\n          // children horizontally, and tries to be as tall as its parent.\n          //\n          // Invoke \"debug painting\" (press \"p\" in the console, choose the\n          // \"Toggle Debug Paint\" action from the Flutter Inspector in Android\n          // Studio, or the \"Toggle Debug Paint\" command in Visual Studio Code)\n          // to see the wireframe for each widget.\n          //\n          // Column has various properties to control how it sizes itself and\n          // how it positions its children. Here we use mainAxisAlignment to\n          // center the children vertically; the main axis here is the vertical\n          // axis because Columns are vertical (the cross axis would be\n          // horizontal).\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: <Widget>[\n            const Text(\n              'You have pushed the button this many times:',\n            ),\n            Text(\n              '$_counter',\n              style: Theme.of(context).textTheme.headlineMedium,\n            ),\n            Text(\n              'APP_ENV: $_appEnv',\n              style: Theme.of(context).textTheme.bodyMedium,\n            ),\n            Text(\n              'FLUTTER_BUILD_NAME: $_buildName',\n              style: Theme.of(context).textTheme.bodyMedium,\n            ),\n            Text(\n              'FLUTTER_BUILD_NUMBER: $_buildNumber',\n              style: Theme.of(context).textTheme.bodyMedium,\n            ),\n          ],\n        ),\n      ),\n      floatingActionButton: FloatingActionButton(\n        onPressed: _incrementCounter,\n        tooltip: 'Increment',\n        child: const Icon(Icons.add),\n      ), // This trailing comma makes auto-formatting nicer for build methods.\n    );\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/linux/.gitignore",
    "content": "flutter/ephemeral\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/linux/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.10)\nproject(runner LANGUAGES CXX)\n\nset(BINARY_NAME \"hello_world\")\nset(APPLICATION_ID \"com.example.hello_world\")\n\ncmake_policy(SET CMP0063 NEW)\n\nset(CMAKE_INSTALL_RPATH \"$ORIGIN/lib\")\n\n# Root filesystem for cross-building.\nif(FLUTTER_TARGET_PLATFORM_SYSROOT)\n  set(CMAKE_SYSROOT ${FLUTTER_TARGET_PLATFORM_SYSROOT})\n  set(CMAKE_FIND_ROOT_PATH ${CMAKE_SYSROOT})\n  set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)\n  set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)\n  set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)\n  set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)\nendif()\n\n# Configure build options.\nif(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)\n  set(CMAKE_BUILD_TYPE \"Debug\" CACHE\n    STRING \"Flutter build mode\" FORCE)\n  set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS\n    \"Debug\" \"Profile\" \"Release\")\nendif()\n\n# Compilation settings that should be applied to most targets.\nfunction(APPLY_STANDARD_SETTINGS TARGET)\n  target_compile_features(${TARGET} PUBLIC cxx_std_14)\n  target_compile_options(${TARGET} PRIVATE -Wall -Werror)\n  target_compile_options(${TARGET} PRIVATE \"$<$<NOT:$<CONFIG:Debug>>:-O3>\")\n  target_compile_definitions(${TARGET} PRIVATE \"$<$<NOT:$<CONFIG:Debug>>:NDEBUG>\")\nendfunction()\n\nset(FLUTTER_MANAGED_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/flutter\")\n\n# Flutter library and tool build rules.\nadd_subdirectory(${FLUTTER_MANAGED_DIR})\n\n# System-level dependencies.\nfind_package(PkgConfig REQUIRED)\npkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)\n\nadd_definitions(-DAPPLICATION_ID=\"${APPLICATION_ID}\")\n\n# Application build\nadd_executable(${BINARY_NAME}\n  \"main.cc\"\n  \"my_application.cc\"\n  \"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc\"\n)\napply_standard_settings(${BINARY_NAME})\ntarget_link_libraries(${BINARY_NAME} PRIVATE flutter)\ntarget_link_libraries(${BINARY_NAME} PRIVATE PkgConfig::GTK)\nadd_dependencies(${BINARY_NAME} flutter_assemble)\n# Only the install-generated bundle's copy of the executable will launch\n# correctly, since the resources must in the right relative locations. To avoid\n# people trying to run the unbundled copy, put it in a subdirectory instead of\n# the default top-level location.\nset_target_properties(${BINARY_NAME}\n  PROPERTIES\n  RUNTIME_OUTPUT_DIRECTORY \"${CMAKE_BINARY_DIR}/intermediates_do_not_run\"\n)\n\n# Generated plugin build rules, which manage building the plugins and adding\n# them to the application.\ninclude(flutter/generated_plugins.cmake)\n\n\n# === Installation ===\n# By default, \"installing\" just makes a relocatable bundle in the build\n# directory.\nset(BUILD_BUNDLE_DIR \"${PROJECT_BINARY_DIR}/bundle\")\nif(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)\n  set(CMAKE_INSTALL_PREFIX \"${BUILD_BUNDLE_DIR}\" CACHE PATH \"...\" FORCE)\nendif()\n\n# Start with a clean build bundle directory every time.\ninstall(CODE \"\n  file(REMOVE_RECURSE \\\"${BUILD_BUNDLE_DIR}/\\\")\n  \" COMPONENT Runtime)\n\nset(INSTALL_BUNDLE_DATA_DIR \"${CMAKE_INSTALL_PREFIX}/data\")\nset(INSTALL_BUNDLE_LIB_DIR \"${CMAKE_INSTALL_PREFIX}/lib\")\n\ninstall(TARGETS ${BINARY_NAME} RUNTIME DESTINATION \"${CMAKE_INSTALL_PREFIX}\"\n  COMPONENT Runtime)\n\ninstall(FILES \"${FLUTTER_ICU_DATA_FILE}\" DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\"\n  COMPONENT Runtime)\n\ninstall(FILES \"${FLUTTER_LIBRARY}\" DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n  COMPONENT Runtime)\n\nif(PLUGIN_BUNDLED_LIBRARIES)\n  install(FILES \"${PLUGIN_BUNDLED_LIBRARIES}\"\n    DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n    COMPONENT Runtime)\nendif()\n\n# Fully re-copy the assets directory on each build to avoid having stale files\n# from a previous install.\nset(FLUTTER_ASSET_DIR_NAME \"flutter_assets\")\ninstall(CODE \"\n  file(REMOVE_RECURSE \\\"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\\\")\n  \" COMPONENT Runtime)\ninstall(DIRECTORY \"${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}\"\n  DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\" COMPONENT Runtime)\n\n# Install the AOT library on non-Debug builds only.\nif(NOT CMAKE_BUILD_TYPE MATCHES \"Debug\")\n  install(FILES \"${AOT_LIBRARY}\" DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n    COMPONENT Runtime)\nendif()\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/linux/flutter/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.10)\n\nset(EPHEMERAL_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/ephemeral\")\n\n# Configuration provided via flutter tool.\ninclude(${EPHEMERAL_DIR}/generated_config.cmake)\n\n# TODO: Move the rest of this into files in ephemeral. See\n# https://github.com/flutter/flutter/issues/57146.\n\n# Serves the same purpose as list(TRANSFORM ... PREPEND ...),\n# which isn't available in 3.10.\nfunction(list_prepend LIST_NAME PREFIX)\n    set(NEW_LIST \"\")\n    foreach(element ${${LIST_NAME}})\n        list(APPEND NEW_LIST \"${PREFIX}${element}\")\n    endforeach(element)\n    set(${LIST_NAME} \"${NEW_LIST}\" PARENT_SCOPE)\nendfunction()\n\n# === Flutter Library ===\n# System-level dependencies.\nfind_package(PkgConfig REQUIRED)\npkg_check_modules(GTK REQUIRED IMPORTED_TARGET gtk+-3.0)\npkg_check_modules(GLIB REQUIRED IMPORTED_TARGET glib-2.0)\npkg_check_modules(GIO REQUIRED IMPORTED_TARGET gio-2.0)\n\nset(FLUTTER_LIBRARY \"${EPHEMERAL_DIR}/libflutter_linux_gtk.so\")\n\n# Published to parent scope for install step.\nset(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)\nset(FLUTTER_ICU_DATA_FILE \"${EPHEMERAL_DIR}/icudtl.dat\" PARENT_SCOPE)\nset(PROJECT_BUILD_DIR \"${PROJECT_DIR}/build/\" PARENT_SCOPE)\nset(AOT_LIBRARY \"${PROJECT_DIR}/build/lib/libapp.so\" PARENT_SCOPE)\n\nlist(APPEND FLUTTER_LIBRARY_HEADERS\n  \"fl_basic_message_channel.h\"\n  \"fl_binary_codec.h\"\n  \"fl_binary_messenger.h\"\n  \"fl_dart_project.h\"\n  \"fl_engine.h\"\n  \"fl_json_message_codec.h\"\n  \"fl_json_method_codec.h\"\n  \"fl_message_codec.h\"\n  \"fl_method_call.h\"\n  \"fl_method_channel.h\"\n  \"fl_method_codec.h\"\n  \"fl_method_response.h\"\n  \"fl_plugin_registrar.h\"\n  \"fl_plugin_registry.h\"\n  \"fl_standard_message_codec.h\"\n  \"fl_standard_method_codec.h\"\n  \"fl_string_codec.h\"\n  \"fl_value.h\"\n  \"fl_view.h\"\n  \"flutter_linux.h\"\n)\nlist_prepend(FLUTTER_LIBRARY_HEADERS \"${EPHEMERAL_DIR}/flutter_linux/\")\nadd_library(flutter INTERFACE)\ntarget_include_directories(flutter INTERFACE\n  \"${EPHEMERAL_DIR}\"\n)\ntarget_link_libraries(flutter INTERFACE \"${FLUTTER_LIBRARY}\")\ntarget_link_libraries(flutter INTERFACE\n  PkgConfig::GTK\n  PkgConfig::GLIB\n  PkgConfig::GIO\n)\nadd_dependencies(flutter flutter_assemble)\n\n# === Flutter tool backend ===\n# _phony_ is a non-existent file to force this command to run every time,\n# since currently there's no way to get a full input/output list from the\n# flutter tool.\nadd_custom_command(\n  OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}\n    ${CMAKE_CURRENT_BINARY_DIR}/_phony_\n  COMMAND ${CMAKE_COMMAND} -E env\n    ${FLUTTER_TOOL_ENVIRONMENT}\n    \"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.sh\"\n      ${FLUTTER_TARGET_PLATFORM} ${CMAKE_BUILD_TYPE}\n  VERBATIM\n)\nadd_custom_target(flutter_assemble DEPENDS\n  \"${FLUTTER_LIBRARY}\"\n  ${FLUTTER_LIBRARY_HEADERS}\n)\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/linux/flutter/generated_plugin_registrant.cc",
    "content": "//\n//  Generated file. Do not edit.\n//\n\n// clang-format off\n\n#include \"generated_plugin_registrant.h\"\n\n\nvoid fl_register_plugins(FlPluginRegistry* registry) {\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/linux/flutter/generated_plugin_registrant.h",
    "content": "//\n//  Generated file. Do not edit.\n//\n\n// clang-format off\n\n#ifndef GENERATED_PLUGIN_REGISTRANT_\n#define GENERATED_PLUGIN_REGISTRANT_\n\n#include <flutter_linux/flutter_linux.h>\n\n// Registers Flutter plugins.\nvoid fl_register_plugins(FlPluginRegistry* registry);\n\n#endif  // GENERATED_PLUGIN_REGISTRANT_\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/linux/flutter/generated_plugins.cmake",
    "content": "#\n# Generated file, do not edit.\n#\n\nlist(APPEND FLUTTER_PLUGIN_LIST\n)\n\nlist(APPEND FLUTTER_FFI_PLUGIN_LIST\n)\n\nset(PLUGIN_BUNDLED_LIBRARIES)\n\nforeach(plugin ${FLUTTER_PLUGIN_LIST})\n  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/linux plugins/${plugin})\n  target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})\nendforeach(plugin)\n\nforeach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})\n  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/linux plugins/${ffi_plugin})\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})\nendforeach(ffi_plugin)\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/linux/main.cc",
    "content": "#include \"my_application.h\"\n\nint main(int argc, char** argv) {\n  g_autoptr(MyApplication) app = my_application_new();\n  return g_application_run(G_APPLICATION(app), argc, argv);\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/linux/my_application.cc",
    "content": "#include \"my_application.h\"\n\n#include <flutter_linux/flutter_linux.h>\n#ifdef GDK_WINDOWING_X11\n#include <gdk/gdkx.h>\n#endif\n\n#include \"flutter/generated_plugin_registrant.h\"\n\nstruct _MyApplication {\n  GtkApplication parent_instance;\n  char** dart_entrypoint_arguments;\n};\n\nG_DEFINE_TYPE(MyApplication, my_application, GTK_TYPE_APPLICATION)\n\n// Implements GApplication::activate.\nstatic void my_application_activate(GApplication* application) {\n  MyApplication* self = MY_APPLICATION(application);\n  GtkWindow* window =\n      GTK_WINDOW(gtk_application_window_new(GTK_APPLICATION(application)));\n\n  // Use a header bar when running in GNOME as this is the common style used\n  // by applications and is the setup most users will be using (e.g. Ubuntu\n  // desktop).\n  // If running on X and not using GNOME then just use a traditional title bar\n  // in case the window manager does more exotic layout, e.g. tiling.\n  // If running on Wayland assume the header bar will work (may need changing\n  // if future cases occur).\n  gboolean use_header_bar = TRUE;\n#ifdef GDK_WINDOWING_X11\n  GdkScreen* screen = gtk_window_get_screen(window);\n  if (GDK_IS_X11_SCREEN(screen)) {\n    const gchar* wm_name = gdk_x11_screen_get_window_manager_name(screen);\n    if (g_strcmp0(wm_name, \"GNOME Shell\") != 0) {\n      use_header_bar = FALSE;\n    }\n  }\n#endif\n  if (use_header_bar) {\n    GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());\n    gtk_widget_show(GTK_WIDGET(header_bar));\n    gtk_header_bar_set_title(header_bar, \"hello_world\");\n    gtk_header_bar_set_show_close_button(header_bar, TRUE);\n    gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));\n  } else {\n    gtk_window_set_title(window, \"hello_world\");\n  }\n\n  gtk_window_set_default_size(window, 1280, 720);\n  gtk_widget_show(GTK_WIDGET(window));\n\n  g_autoptr(FlDartProject) project = fl_dart_project_new();\n  fl_dart_project_set_dart_entrypoint_arguments(project, self->dart_entrypoint_arguments);\n\n  FlView* view = fl_view_new(project);\n  gtk_widget_show(GTK_WIDGET(view));\n  gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(view));\n\n  fl_register_plugins(FL_PLUGIN_REGISTRY(view));\n\n  gtk_widget_grab_focus(GTK_WIDGET(view));\n}\n\n// Implements GApplication::local_command_line.\nstatic gboolean my_application_local_command_line(GApplication* application, gchar*** arguments, int* exit_status) {\n  MyApplication* self = MY_APPLICATION(application);\n  // Strip out the first argument as it is the binary name.\n  self->dart_entrypoint_arguments = g_strdupv(*arguments + 1);\n\n  g_autoptr(GError) error = nullptr;\n  if (!g_application_register(application, nullptr, &error)) {\n     g_warning(\"Failed to register: %s\", error->message);\n     *exit_status = 1;\n     return TRUE;\n  }\n\n  g_application_activate(application);\n  *exit_status = 0;\n\n  return TRUE;\n}\n\n// Implements GObject::dispose.\nstatic void my_application_dispose(GObject* object) {\n  MyApplication* self = MY_APPLICATION(object);\n  g_clear_pointer(&self->dart_entrypoint_arguments, g_strfreev);\n  G_OBJECT_CLASS(my_application_parent_class)->dispose(object);\n}\n\nstatic void my_application_class_init(MyApplicationClass* klass) {\n  G_APPLICATION_CLASS(klass)->activate = my_application_activate;\n  G_APPLICATION_CLASS(klass)->local_command_line = my_application_local_command_line;\n  G_OBJECT_CLASS(klass)->dispose = my_application_dispose;\n}\n\nstatic void my_application_init(MyApplication* self) {}\n\nMyApplication* my_application_new() {\n  return MY_APPLICATION(g_object_new(my_application_get_type(),\n                                     \"application-id\", APPLICATION_ID,\n                                     \"flags\", G_APPLICATION_NON_UNIQUE,\n                                     nullptr));\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/linux/my_application.h",
    "content": "#ifndef FLUTTER_MY_APPLICATION_H_\n#define FLUTTER_MY_APPLICATION_H_\n\n#include <gtk/gtk.h>\n\nG_DECLARE_FINAL_TYPE(MyApplication, my_application, MY, APPLICATION,\n                     GtkApplication)\n\n/**\n * my_application_new:\n *\n * Creates a new Flutter-based application.\n *\n * Returns: a new #MyApplication.\n */\nMyApplication* my_application_new();\n\n#endif  // FLUTTER_MY_APPLICATION_H_\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/linux/packaging/appimage/make_config.yaml",
    "content": "display_name: Hello World\n\nicon: assets/logo.png\n\nkeywords:\n  - Hello\n  - World\n  - Test\n  - Application\n\ngeneric_name: Cool Application\n\nactions:\n  - name: Say Hi\n    label: say-hi\n    arguments:\n      - --say\n      - hi\n  - name: Say Bye\n    label: say-bye\n    arguments:\n      - --say\n      - bye\n\ncategories:\n  - Music\n\nstartup_notify: true\n\n# You can specify the shared libraries that you want to bundle with your app\n#\n# flutter_distributor automatically detects the shared libraries that your app\n# depends on, but you can also specify them manually here.\n# \n# The following example shows how to bundle the libcurl library with your app.\n#\n# include:\n#   - libcurl.so.4\ninclude: []"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/linux/packaging/deb/make_config.yaml",
    "content": "# the name used to display in the OS. Specifically desktop\n# entry name\ndisplay_name: Hello World\n\n# package name for debian/apt repository\n# the name should be all lowercase with -+.\npackage_name: hello-world\n\nmaintainer:\n  name: LiJianying\n  email: lijy91@foxmail.com\n\nco_authors:\n  - name: Kingkor Roy Tirtho\n    email: krtirtho@gmail.com\n\n# enum options -> required, important, standard, optional, extra\n# refer: https://www.debian.org/doc/debian-policy/ch-archive.html#s-priorities\npriority: optional\n\n# enum options: admin, cli-mono, comm, database, debug, devel, doc, editors, education, electronics, embedded, fonts, games, gnome, gnu-r, gnustep, graphics, hamradio, haskell, httpd, interpreters, introspection, java, javascript, kde, kernel, libdevel, libs, lisp, localization, mail, math, metapackages, misc, net, news, ocaml, oldlibs, otherosfs, perl, php, python, ruby, rust, science, shells, sound, tasks, tex, text, utils, vcs, video, web, x11, xfce, zope\n# refer: https://www.debian.org/doc/debian-policy/ch-archive.html#s-subsections\nsection: x11\n\n# the size of binary in kilobyte\ninstalled_size: 6604\n\n# direct dependencies required by the application\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\n# dependencies:\n#   - libkeybinder-3.0-0 (>= 0.3.2)\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\n# build_dependencies_indep:\n#   - texinfo\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\n# build_dependencies:\n#   - kernel-headers-2.2.10 [!hurd-i386]\n#   - gnumach-dev [hurd-i386]\n#   - libluajit5.1-dev [i386 amd64 kfreebsd-i386 armel armhf powerpc mips]\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\n# recommended_dependencies:\n#   - neofetch\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\n# suggested_dependencies:\n#   - libkeybinder-3.0-0 (>= 0.3.2)\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\n# enhances:\n#   - spotube\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\n# pre_dependencies:\n#   - libc6\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html#packages-which-break-other-packages-breaks\n# breaks:\n#   - libspotify (<< 3.0.0)\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html#conflicting-binary-packages-conflicts\n# conflicts:\n#   - spotify\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html#virtual-packages-provides\n# provides:\n#   - libx11\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html#overwriting-files-and-replacing-packages-replaces\n# replaces:\n#   - spotify\n\nessential: false\n\npostinstall_scripts:\n  - echo `Installed my awesome app`\npostuninstall_scripts:\n  - echo `Surprised Pickachu face`\n\n# application icon path relative to project url\nicon: assets/logo.png\n\nkeywords:\n  - Hello\n  - World\n  - Test\n  - Application\n\n# a name to categorize the app into a section of application\ngeneric_name: Music Application\n\n# supported mime types that can be opened using this application\n# supported_mime_type:\n#   - audio/mpeg\n\n# shown when right clicked the desktop entry icons\n# actions:\n#   - Gallery\n#   - Create\n\n# the categories the application belong to\n# refer: https://specifications.freedesktop.org/menu-spec/latest/\ncategories:\n  - Music\n  - Media\n\n# let OS know if the application can be run on start_up. If it's false\n# the application will deny to the OS if it was added as a start_up\n# application\nstartup_notify: true\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/linux/packaging/helloworld.appdata.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<component type=\"desktop-application\">\n    <id>hello_world</id>\n    <metadata_license>CC0-1.0</metadata_license>\n    <project_license>MIT</project_license>\n    <name>Hello World</name>\n    <summary>Sample project that is a demo for packaging</summary>\n    <description>\n        <p>The application can be used as a demo for the packaging of flutter application to various platform</p>\n    </description>\n    <launchable type=\"desktop-id\">hello_world.desktop</launchable>\n    <url type=\"homepage\">https://github.com/fastforgedev/fastforge/</url>\n    <screenshots>\n        <screenshot type=\"default\">\n            <image>https://github.com/fastforgedev/fastforge/assets/41370460/bf942f1d-d26b-441d-b5b2-4ca210b5ea92</image>\n        </screenshot>\n    </screenshots>\n    <releases>\n        <release version=\"1.0.0\" date=\"2024-06-16\"/>\n    </releases>\n    <provides>\n        <id>hello_world.desktop</id>\n    </provides>\n    <content_rating type=\"oars-1.0\" />\n    <developer id=\"io.github.lijy91\">\n        <name>lijy91</name>\n    </developer>\n    <update_contact>lijy91@foxmail.com</update_contact>\n</component>"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/linux/packaging/pacman/make_config.yaml",
    "content": "# the name used to display in the OS. Specifically desktop\n# entry name\ndisplay_name: Hello World\n\n# package name for debian/apt repository\n# the name should be all lowercase with -+.\npackage_name: hello-world\n\nmaintainer:\n  name: LiJianying\n  email: lijy91@foxmail.com\n\n# the size of binary in kilobyte\ninstalled_size: 6604\n\n# direct dependencies required by the application\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\n# dependencies:\n#   - libkeybinder-3.0-0 (>= 0.3.2)\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\n# build_dependencies_indep:\n#   - texinfo\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\n# build_dependencies:\n#   - kernel-headers-2.2.10 [!hurd-i386]\n#   - gnumach-dev [hurd-i386]\n#   - libluajit5.1-dev [i386 amd64 kfreebsd-i386 armel armhf powerpc mips]\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\n# recommended_dependencies:\n#   - neofetch\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\n# suggested_dependencies:\n#   - libkeybinder-3.0-0 (>= 0.3.2)\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\n# enhances:\n#   - spotube\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\n# pre_dependencies:\n#   - libc6\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html#packages-which-break-other-packages-breaks\n# breaks:\n#   - libspotify (<< 3.0.0)\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html#conflicting-binary-packages-conflicts\n# conflicts:\n#   - spotify\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html#virtual-packages-provides\n# provides:\n#   - libx11\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html#overwriting-files-and-replacing-packages-replaces\n# replaces:\n#   - spotify\n\npostinstall_scripts:\n  - echo `Installed my awesome app`\npostuninstall_scripts:\n  - echo `Surprised Pickachu face`\n\n# application icon path relative to project url\nicon: assets/logo.png\n\nkeywords:\n  - Hello\n  - World\n  - Test\n  - Application\n\n# a name to categorize the app into a section of application\ngeneric_name: Music Application\n\n# supported mime types that can be opened using this application\n# supported_mime_type:\n#   - audio/mpeg\n\nmetainfo: linux/packaging/helloworld.appdata.xml\n\n# shown when right clicked the desktop entry icons\n# actions:\n#   - Gallery\n#   - Create\n\n# the categories the application belong to\n# refer: https://specifications.freedesktop.org/menu-spec/latest/\ncategories:\n  - Music\n  - Media\n\n# let OS know if the application can be run on start_up. If it's false\n# the application will deny to the OS if it was added as a start_up\n# application\nstartup_notify: true\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/linux/packaging/rpm/make_config.yaml",
    "content": "icon: assets/logo.png\nsummary: A really cool application\ngroup: Application/Emulator\nvendor: Kingkor Roy Tirtho\npackager: Kingkor Roy Tirtho\npackagerEmail: krtirtho@gmail.com\nlicense: GPLv3\nurl: https://github.com/leanflutter/flutter_distributor\n\ndisplay_name: Hello World\n\nkeywords:\n  - Hello\n  - World\n  - Test\n  - Application\n\ngeneric_name: Cool Application\n\ncategories:\n  - Cool\n  - Awesome\n\nstartup_notify: true\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/macos/.gitignore",
    "content": "# Flutter-related\n**/Flutter/ephemeral/\n**/Pods/\n\n# Xcode-related\n**/dgph\n**/xcuserdata/\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/macos/Flutter/Flutter-Debug.xcconfig",
    "content": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"\n#include \"ephemeral/Flutter-Generated.xcconfig\"\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/macos/Flutter/Flutter-Release.xcconfig",
    "content": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"\n#include \"ephemeral/Flutter-Generated.xcconfig\"\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/macos/Flutter/GeneratedPluginRegistrant.swift",
    "content": "//\n//  Generated file. Do not edit.\n//\n\nimport FlutterMacOS\nimport Foundation\n\n\nfunc RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/macos/Podfile",
    "content": "platform :osx, '10.14'\n\n# CocoaPods analytics sends network stats synchronously affecting flutter build latency.\nENV['COCOAPODS_DISABLE_STATS'] = 'true'\n\nproject 'Runner', {\n  'Debug' => :debug,\n  'Profile' => :release,\n  'Release' => :release,\n}\n\ndef flutter_root\n  generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)\n  unless File.exist?(generated_xcode_build_settings_path)\n    raise \"#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \\\"flutter pub get\\\" is executed first\"\n  end\n\n  File.foreach(generated_xcode_build_settings_path) do |line|\n    matches = line.match(/FLUTTER_ROOT\\=(.*)/)\n    return matches[1].strip if matches\n  end\n  raise \"FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \\\"flutter pub get\\\"\"\nend\n\nrequire File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)\n\nflutter_macos_podfile_setup\n\ntarget 'Runner' do\n  use_frameworks!\n  use_modular_headers!\n\n  flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))\nend\n\npost_install do |installer|\n  installer.pods_project.targets.each do |target|\n    flutter_additional_macos_build_settings(target)\n  end\nend\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/macos/Runner/AppDelegate.swift",
    "content": "import Cocoa\nimport FlutterMacOS\n\n@NSApplicationMain\nclass AppDelegate: FlutterAppDelegate {\n  override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {\n    return true\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"size\" : \"16x16\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_16.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"16x16\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_32.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"32x32\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_32.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"32x32\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_64.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"128x128\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_128.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"128x128\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_256.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"256x256\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_256.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"256x256\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_512.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"512x512\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_512.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"512x512\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_1024.png\",\n      \"scale\" : \"2x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/macos/Runner/Base.lproj/MainMenu.xib",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion=\"14490.70\" targetRuntime=\"MacOSX.Cocoa\" propertyAccessControl=\"none\" useAutolayout=\"YES\" customObjectInstantitationMethod=\"direct\">\n    <dependencies>\n        <deployment identifier=\"macosx\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.CocoaPlugin\" version=\"14490.70\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <objects>\n        <customObject id=\"-2\" userLabel=\"File's Owner\" customClass=\"NSApplication\">\n            <connections>\n                <outlet property=\"delegate\" destination=\"Voe-Tx-rLC\" id=\"GzC-gU-4Uq\"/>\n            </connections>\n        </customObject>\n        <customObject id=\"-1\" userLabel=\"First Responder\" customClass=\"FirstResponder\"/>\n        <customObject id=\"-3\" userLabel=\"Application\" customClass=\"NSObject\"/>\n        <customObject id=\"Voe-Tx-rLC\" customClass=\"AppDelegate\" customModule=\"Runner\" customModuleProvider=\"target\">\n            <connections>\n                <outlet property=\"applicationMenu\" destination=\"uQy-DD-JDr\" id=\"XBo-yE-nKs\"/>\n                <outlet property=\"mainFlutterWindow\" destination=\"QvC-M9-y7g\" id=\"gIp-Ho-8D9\"/>\n            </connections>\n        </customObject>\n        <customObject id=\"YLy-65-1bz\" customClass=\"NSFontManager\"/>\n        <menu title=\"Main Menu\" systemMenu=\"main\" id=\"AYu-sK-qS6\">\n            <items>\n                <menuItem title=\"APP_NAME\" id=\"1Xt-HY-uBw\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"APP_NAME\" systemMenu=\"apple\" id=\"uQy-DD-JDr\">\n                        <items>\n                            <menuItem title=\"About APP_NAME\" id=\"5kV-Vb-QxS\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"orderFrontStandardAboutPanel:\" target=\"-1\" id=\"Exp-CZ-Vem\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"VOq-y0-SEH\"/>\n                            <menuItem title=\"Preferences…\" keyEquivalent=\",\" id=\"BOF-NM-1cW\"/>\n                            <menuItem isSeparatorItem=\"YES\" id=\"wFC-TO-SCJ\"/>\n                            <menuItem title=\"Services\" id=\"NMo-om-nkz\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Services\" systemMenu=\"services\" id=\"hz9-B4-Xy5\"/>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"4je-JR-u6R\"/>\n                            <menuItem title=\"Hide APP_NAME\" keyEquivalent=\"h\" id=\"Olw-nP-bQN\">\n                                <connections>\n                                    <action selector=\"hide:\" target=\"-1\" id=\"PnN-Uc-m68\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Hide Others\" keyEquivalent=\"h\" id=\"Vdr-fp-XzO\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"hideOtherApplications:\" target=\"-1\" id=\"VT4-aY-XCT\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Show All\" id=\"Kd2-mp-pUS\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"unhideAllApplications:\" target=\"-1\" id=\"Dhg-Le-xox\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"kCx-OE-vgT\"/>\n                            <menuItem title=\"Quit APP_NAME\" keyEquivalent=\"q\" id=\"4sb-4s-VLi\">\n                                <connections>\n                                    <action selector=\"terminate:\" target=\"-1\" id=\"Te7-pn-YzF\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"Edit\" id=\"5QF-Oa-p0T\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"Edit\" id=\"W48-6f-4Dl\">\n                        <items>\n                            <menuItem title=\"Undo\" keyEquivalent=\"z\" id=\"dRJ-4n-Yzg\">\n                                <connections>\n                                    <action selector=\"undo:\" target=\"-1\" id=\"M6e-cu-g7V\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Redo\" keyEquivalent=\"Z\" id=\"6dh-zS-Vam\">\n                                <connections>\n                                    <action selector=\"redo:\" target=\"-1\" id=\"oIA-Rs-6OD\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"WRV-NI-Exz\"/>\n                            <menuItem title=\"Cut\" keyEquivalent=\"x\" id=\"uRl-iY-unG\">\n                                <connections>\n                                    <action selector=\"cut:\" target=\"-1\" id=\"YJe-68-I9s\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Copy\" keyEquivalent=\"c\" id=\"x3v-GG-iWU\">\n                                <connections>\n                                    <action selector=\"copy:\" target=\"-1\" id=\"G1f-GL-Joy\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Paste\" keyEquivalent=\"v\" id=\"gVA-U4-sdL\">\n                                <connections>\n                                    <action selector=\"paste:\" target=\"-1\" id=\"UvS-8e-Qdg\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Paste and Match Style\" keyEquivalent=\"V\" id=\"WeT-3V-zwk\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"pasteAsPlainText:\" target=\"-1\" id=\"cEh-KX-wJQ\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Delete\" id=\"pa3-QI-u2k\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"delete:\" target=\"-1\" id=\"0Mk-Ml-PaM\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Select All\" keyEquivalent=\"a\" id=\"Ruw-6m-B2m\">\n                                <connections>\n                                    <action selector=\"selectAll:\" target=\"-1\" id=\"VNm-Mi-diN\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"uyl-h8-XO2\"/>\n                            <menuItem title=\"Find\" id=\"4EN-yA-p0u\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Find\" id=\"1b7-l0-nxx\">\n                                    <items>\n                                        <menuItem title=\"Find…\" tag=\"1\" keyEquivalent=\"f\" id=\"Xz5-n4-O0W\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"cD7-Qs-BN4\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Find and Replace…\" tag=\"12\" keyEquivalent=\"f\" id=\"YEy-JH-Tfz\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"WD3-Gg-5AJ\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Find Next\" tag=\"2\" keyEquivalent=\"g\" id=\"q09-fT-Sye\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"NDo-RZ-v9R\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Find Previous\" tag=\"3\" keyEquivalent=\"G\" id=\"OwM-mh-QMV\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"HOh-sY-3ay\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Use Selection for Find\" tag=\"7\" keyEquivalent=\"e\" id=\"buJ-ug-pKt\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"U76-nv-p5D\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Jump to Selection\" keyEquivalent=\"j\" id=\"S0p-oC-mLd\">\n                                            <connections>\n                                                <action selector=\"centerSelectionInVisibleArea:\" target=\"-1\" id=\"IOG-6D-g5B\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Spelling and Grammar\" id=\"Dv1-io-Yv7\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Spelling\" id=\"3IN-sU-3Bg\">\n                                    <items>\n                                        <menuItem title=\"Show Spelling and Grammar\" keyEquivalent=\":\" id=\"HFo-cy-zxI\">\n                                            <connections>\n                                                <action selector=\"showGuessPanel:\" target=\"-1\" id=\"vFj-Ks-hy3\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Check Document Now\" keyEquivalent=\";\" id=\"hz2-CU-CR7\">\n                                            <connections>\n                                                <action selector=\"checkSpelling:\" target=\"-1\" id=\"fz7-VC-reM\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"bNw-od-mp5\"/>\n                                        <menuItem title=\"Check Spelling While Typing\" id=\"rbD-Rh-wIN\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleContinuousSpellChecking:\" target=\"-1\" id=\"7w6-Qz-0kB\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Check Grammar With Spelling\" id=\"mK6-2p-4JG\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleGrammarChecking:\" target=\"-1\" id=\"muD-Qn-j4w\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Correct Spelling Automatically\" id=\"78Y-hA-62v\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticSpellingCorrection:\" target=\"-1\" id=\"2lM-Qi-WAP\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Substitutions\" id=\"9ic-FL-obx\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Substitutions\" id=\"FeM-D8-WVr\">\n                                    <items>\n                                        <menuItem title=\"Show Substitutions\" id=\"z6F-FW-3nz\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"orderFrontSubstitutionsPanel:\" target=\"-1\" id=\"oku-mr-iSq\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"gPx-C9-uUO\"/>\n                                        <menuItem title=\"Smart Copy/Paste\" id=\"9yt-4B-nSM\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleSmartInsertDelete:\" target=\"-1\" id=\"3IJ-Se-DZD\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smart Quotes\" id=\"hQb-2v-fYv\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticQuoteSubstitution:\" target=\"-1\" id=\"ptq-xd-QOA\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smart Dashes\" id=\"rgM-f4-ycn\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticDashSubstitution:\" target=\"-1\" id=\"oCt-pO-9gS\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smart Links\" id=\"cwL-P1-jid\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticLinkDetection:\" target=\"-1\" id=\"Gip-E3-Fov\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Data Detectors\" id=\"tRr-pd-1PS\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticDataDetection:\" target=\"-1\" id=\"R1I-Nq-Kbl\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Text Replacement\" id=\"HFQ-gK-NFA\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticTextReplacement:\" target=\"-1\" id=\"DvP-Fe-Py6\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Transformations\" id=\"2oI-Rn-ZJC\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Transformations\" id=\"c8a-y6-VQd\">\n                                    <items>\n                                        <menuItem title=\"Make Upper Case\" id=\"vmV-6d-7jI\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"uppercaseWord:\" target=\"-1\" id=\"sPh-Tk-edu\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Make Lower Case\" id=\"d9M-CD-aMd\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"lowercaseWord:\" target=\"-1\" id=\"iUZ-b5-hil\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Capitalize\" id=\"UEZ-Bs-lqG\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"capitalizeWord:\" target=\"-1\" id=\"26H-TL-nsh\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Speech\" id=\"xrE-MZ-jX0\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Speech\" id=\"3rS-ZA-NoH\">\n                                    <items>\n                                        <menuItem title=\"Start Speaking\" id=\"Ynk-f8-cLZ\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"startSpeaking:\" target=\"-1\" id=\"654-Ng-kyl\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Stop Speaking\" id=\"Oyz-dy-DGm\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"stopSpeaking:\" target=\"-1\" id=\"dX8-6p-jy9\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"View\" id=\"H8h-7b-M4v\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"View\" id=\"HyV-fh-RgO\">\n                        <items>\n                            <menuItem title=\"Enter Full Screen\" keyEquivalent=\"f\" id=\"4J7-dP-txa\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" control=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"toggleFullScreen:\" target=\"-1\" id=\"dU3-MA-1Rq\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"Window\" id=\"aUF-d1-5bR\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"Window\" systemMenu=\"window\" id=\"Td7-aD-5lo\">\n                        <items>\n                            <menuItem title=\"Minimize\" keyEquivalent=\"m\" id=\"OY7-WF-poV\">\n                                <connections>\n                                    <action selector=\"performMiniaturize:\" target=\"-1\" id=\"VwT-WD-YPe\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Zoom\" id=\"R4o-n2-Eq4\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"performZoom:\" target=\"-1\" id=\"DIl-cC-cCs\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"eu3-7i-yIM\"/>\n                            <menuItem title=\"Bring All to Front\" id=\"LE2-aR-0XJ\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"arrangeInFront:\" target=\"-1\" id=\"DRN-fu-gQh\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n            </items>\n            <point key=\"canvasLocation\" x=\"142\" y=\"-258\"/>\n        </menu>\n        <window title=\"APP_NAME\" allowsToolTipsWhenApplicationIsInactive=\"NO\" autorecalculatesKeyViewLoop=\"NO\" releasedWhenClosed=\"NO\" animationBehavior=\"default\" id=\"QvC-M9-y7g\" customClass=\"MainFlutterWindow\" customModule=\"Runner\" customModuleProvider=\"target\">\n            <windowStyleMask key=\"styleMask\" titled=\"YES\" closable=\"YES\" miniaturizable=\"YES\" resizable=\"YES\"/>\n            <rect key=\"contentRect\" x=\"335\" y=\"390\" width=\"800\" height=\"600\"/>\n            <rect key=\"screenRect\" x=\"0.0\" y=\"0.0\" width=\"2560\" height=\"1577\"/>\n            <view key=\"contentView\" wantsLayer=\"YES\" id=\"EiT-Mj-1SZ\">\n                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"800\" height=\"600\"/>\n                <autoresizingMask key=\"autoresizingMask\"/>\n            </view>\n        </window>\n    </objects>\n</document>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/macos/Runner/Configs/AppInfo.xcconfig",
    "content": "// Application-level settings for the Runner target.\n//\n// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the\n// future. If not, the values below would default to using the project name when this becomes a\n// 'flutter create' template.\n\n// The application's name. By default this is also the title of the Flutter window.\nPRODUCT_NAME = hello_world\n\n// The application's bundle identifier\nPRODUCT_BUNDLE_IDENTIFIER = com.example.helloWorld\n\n// The copyright displayed in application information\nPRODUCT_COPYRIGHT = Copyright © 2021 com.example. All rights reserved.\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/macos/Runner/Configs/Debug.xcconfig",
    "content": "#include \"../../Flutter/Flutter-Debug.xcconfig\"\n#include \"Warnings.xcconfig\"\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/macos/Runner/Configs/Release.xcconfig",
    "content": "#include \"../../Flutter/Flutter-Release.xcconfig\"\n#include \"Warnings.xcconfig\"\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/macos/Runner/Configs/Warnings.xcconfig",
    "content": "WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings\nGCC_WARN_UNDECLARED_SELECTOR = YES\nCLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES\nCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE\nCLANG_WARN__DUPLICATE_METHOD_MATCH = YES\nCLANG_WARN_PRAGMA_PACK = YES\nCLANG_WARN_STRICT_PROTOTYPES = YES\nCLANG_WARN_COMMA = YES\nGCC_WARN_STRICT_SELECTOR_MATCH = YES\nCLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES\nCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES\nGCC_WARN_SHADOW = YES\nCLANG_WARN_UNREACHABLE_CODE = YES\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/macos/Runner/DebugProfile.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.app-sandbox</key>\n\t<true/>\n\t<key>com.apple.security.cs.allow-jit</key>\n\t<true/>\n\t<key>com.apple.security.network.server</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/macos/Runner/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIconFile</key>\n\t<string></string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(FLUTTER_BUILD_NAME)</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(FLUTTER_BUILD_NUMBER)</string>\n\t<key>LSMinimumSystemVersion</key>\n\t<string>$(MACOSX_DEPLOYMENT_TARGET)</string>\n\t<key>NSHumanReadableCopyright</key>\n\t<string>$(PRODUCT_COPYRIGHT)</string>\n\t<key>NSMainNibFile</key>\n\t<string>MainMenu</string>\n\t<key>NSPrincipalClass</key>\n\t<string>NSApplication</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/macos/Runner/MainFlutterWindow.swift",
    "content": "import Cocoa\nimport FlutterMacOS\n\nclass MainFlutterWindow: NSWindow {\n  override func awakeFromNib() {\n    let flutterViewController = FlutterViewController.init()\n    let windowFrame = self.frame\n    self.contentViewController = flutterViewController\n    self.setFrame(windowFrame, display: true)\n\n    RegisterGeneratedPlugins(registry: flutterViewController)\n\n    super.awakeFromNib()\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/macos/Runner/Release.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.app-sandbox</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/macos/Runner.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXAggregateTarget section */\n\t\t33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {\n\t\t\tisa = PBXAggregateTarget;\n\t\t\tbuildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget \"Flutter Assemble\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t33CC111E2044C6BF0003C045 /* ShellScript */,\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = \"Flutter Assemble\";\n\t\t\tproductName = FLX;\n\t\t};\n/* End PBXAggregateTarget section */\n\n/* Begin PBXBuildFile section */\n\t\t335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };\n\t\t33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };\n\t\t33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };\n\t\t33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };\n\t\t33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };\n\t\t889CAFAFFE80A223E7446BF7 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 136F3972794957D3C00B7C5E /* Pods_Runner.framework */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXContainerItemProxy section */\n\t\t33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 33CC10E52044A3C60003C045 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 33CC111A2044C6BA0003C045;\n\t\t\tremoteInfo = FLX;\n\t\t};\n/* End PBXContainerItemProxy section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\t33CC110E2044A8840003C045 /* Bundle Framework */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 10;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tname = \"Bundle Framework\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\t136F3972794957D3C00B7C5E /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t1A9261D37735A3703FA295FD /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.debug.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t30178CB5FF4FACA7B49BB1EC /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.profile.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = \"<group>\"; };\n\t\t335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = \"<group>\"; };\n\t\t33CC10ED2044A3C60003C045 /* hello_world.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = hello_world.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = \"<group>\"; };\n\t\t33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = \"<group>\"; };\n\t\t33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = \"<group>\"; };\n\t\t33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = \"Flutter-Debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = \"Flutter-Release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = \"Flutter-Generated.xcconfig\"; path = \"ephemeral/Flutter-Generated.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = \"<group>\"; };\n\t\t33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = \"<group>\"; };\n\t\t33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = \"<group>\"; };\n\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = \"<group>\"; };\n\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = \"<group>\"; };\n\t\t9F660002B436ABA26771AB03 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.release.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t33CC10EA2044A3C60003C045 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t889CAFAFFE80A223E7446BF7 /* Pods_Runner.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t33BA886A226E78AF003329D5 /* Configs */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33E5194F232828860026EE4D /* AppInfo.xcconfig */,\n\t\t\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */,\n\t\t\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */,\n\t\t\t\t333000ED22D3DE5D00554162 /* Warnings.xcconfig */,\n\t\t\t);\n\t\t\tpath = Configs;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CC10E42044A3C60003C045 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33FAB671232836740065AC1E /* Runner */,\n\t\t\t\t33CEB47122A05771004F2AC0 /* Flutter */,\n\t\t\t\t33CC10EE2044A3C60003C045 /* Products */,\n\t\t\t\tD73912EC22F37F3D000D13A0 /* Frameworks */,\n\t\t\t\tB65631292BE5E4A4BD35F95B /* Pods */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CC10EE2044A3C60003C045 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10ED2044A3C60003C045 /* hello_world.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CC11242044D66E0003C045 /* Resources */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10F22044A3C60003C045 /* Assets.xcassets */,\n\t\t\t\t33CC10F42044A3C60003C045 /* MainMenu.xib */,\n\t\t\t\t33CC10F72044A3C60003C045 /* Info.plist */,\n\t\t\t);\n\t\t\tname = Resources;\n\t\t\tpath = ..;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CEB47122A05771004F2AC0 /* Flutter */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,\n\t\t\t\t33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,\n\t\t\t\t33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,\n\t\t\t\t33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,\n\t\t\t);\n\t\t\tpath = Flutter;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33FAB671232836740065AC1E /* Runner */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10F02044A3C60003C045 /* AppDelegate.swift */,\n\t\t\t\t33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,\n\t\t\t\t33E51913231747F40026EE4D /* DebugProfile.entitlements */,\n\t\t\t\t33E51914231749380026EE4D /* Release.entitlements */,\n\t\t\t\t33CC11242044D66E0003C045 /* Resources */,\n\t\t\t\t33BA886A226E78AF003329D5 /* Configs */,\n\t\t\t);\n\t\t\tpath = Runner;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tB65631292BE5E4A4BD35F95B /* Pods */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t1A9261D37735A3703FA295FD /* Pods-Runner.debug.xcconfig */,\n\t\t\t\t9F660002B436ABA26771AB03 /* Pods-Runner.release.xcconfig */,\n\t\t\t\t30178CB5FF4FACA7B49BB1EC /* Pods-Runner.profile.xcconfig */,\n\t\t\t);\n\t\t\tname = Pods;\n\t\t\tpath = Pods;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD73912EC22F37F3D000D13A0 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t136F3972794957D3C00B7C5E /* Pods_Runner.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t33CC10EC2044A3C60003C045 /* Runner */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget \"Runner\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t477BEA2BAE18AAE3DE0E451C /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t33CC10E92044A3C60003C045 /* Sources */,\n\t\t\t\t33CC10EA2044A3C60003C045 /* Frameworks */,\n\t\t\t\t33CC10EB2044A3C60003C045 /* Resources */,\n\t\t\t\t33CC110E2044A8840003C045 /* Bundle Framework */,\n\t\t\t\t3399D490228B24CF009A79C7 /* ShellScript */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t33CC11202044C79F0003C045 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = Runner;\n\t\t\tproductName = Runner;\n\t\t\tproductReference = 33CC10ED2044A3C60003C045 /* hello_world.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t33CC10E52044A3C60003C045 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastSwiftUpdateCheck = 0920;\n\t\t\t\tLastUpgradeCheck = 1430;\n\t\t\t\tORGANIZATIONNAME = \"\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t33CC10EC2044A3C60003C045 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.2;\n\t\t\t\t\t\tLastSwiftMigration = 1100;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t\tSystemCapabilities = {\n\t\t\t\t\t\t\tcom.apple.Sandbox = {\n\t\t\t\t\t\t\t\tenabled = 1;\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t};\n\t\t\t\t\t};\n\t\t\t\t\t33CC111A2044C6BA0003C045 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.2;\n\t\t\t\t\t\tProvisioningStyle = Manual;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject \"Runner\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 33CC10E42044A3C60003C045;\n\t\t\tproductRefGroup = 33CC10EE2044A3C60003C045 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t33CC10EC2044A3C60003C045 /* Runner */,\n\t\t\t\t33CC111A2044C6BA0003C045 /* Flutter Assemble */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t33CC10EB2044A3C60003C045 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,\n\t\t\t\t33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t3399D490228B24CF009A79C7 /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\talwaysOutOfDate = 1;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"echo \\\"$PRODUCT_NAME.app\\\" > \\\"$PROJECT_DIR\\\"/Flutter/ephemeral/.app_filename && \\\"$FLUTTER_ROOT\\\"/packages/flutter_tools/bin/macos_assemble.sh embed\\n\";\n\t\t};\n\t\t33CC111E2044C6BF0003C045 /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t\tFlutter/ephemeral/FlutterInputs.xcfilelist,\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\tFlutter/ephemeral/tripwire,\n\t\t\t);\n\t\t\toutputFileListPaths = (\n\t\t\t\tFlutter/ephemeral/FlutterOutputs.xcfilelist,\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"\\\"$FLUTTER_ROOT\\\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire\";\n\t\t};\n\t\t477BEA2BAE18AAE3DE0E451C /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t33CC10E92044A3C60003C045 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,\n\t\t\t\t33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,\n\t\t\t\t335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXTargetDependency section */\n\t\t33CC11202044C79F0003C045 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;\n\t\t\ttargetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;\n\t\t};\n/* End PBXTargetDependency section */\n\n/* Begin PBXVariantGroup section */\n\t\t33CC10F42044A3C60003C045 /* MainMenu.xib */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10F52044A3C60003C045 /* Base */,\n\t\t\t);\n\t\t\tname = MainMenu.xib;\n\t\t\tpath = Runner;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t338D0CE9231458BD00FA5F75 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.14;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t338D0CEA231458BD00FA5F75 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t338D0CEB231458BD00FA5F75 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t33CC10F92044A3C60003C045 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.14;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t33CC10FA2044A3C60003C045 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.14;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t33CC10FC2044A3C60003C045 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t33CC10FD2044A3C60003C045 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t33CC111C2044C6BA0003C045 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t33CC111D2044C6BA0003C045 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t33CC10E82044A3C60003C045 /* Build configuration list for PBXProject \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t33CC10F92044A3C60003C045 /* Debug */,\n\t\t\t\t33CC10FA2044A3C60003C045 /* Release */,\n\t\t\t\t338D0CE9231458BD00FA5F75 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t33CC10FC2044A3C60003C045 /* Debug */,\n\t\t\t\t33CC10FD2044A3C60003C045 /* Release */,\n\t\t\t\t338D0CEA231458BD00FA5F75 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget \"Flutter Assemble\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t33CC111C2044C6BA0003C045 /* Debug */,\n\t\t\t\t33CC111D2044C6BA0003C045 /* Release */,\n\t\t\t\t338D0CEB231458BD00FA5F75 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 33CC10E52044A3C60003C045 /* Project object */;\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1430\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n               BuildableName = \"hello_world.app\"\n               BlueprintName = \"Runner\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"hello_world.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n      <AdditionalOptions>\n      </AdditionalOptions>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"hello_world.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n      <AdditionalOptions>\n      </AdditionalOptions>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Profile\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"hello_world.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/macos/Runner.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:Runner.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:Pods/Pods.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/macos/packaging/dmg/make_config.yaml",
    "content": "title: hello_world\ncontents:\n  - x: 448\n    y: 344\n    type: link\n    path: \"/Applications\"\n  - x: 192\n    y: 344\n    type: file\n    path: hello_world.app\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/macos/packaging/pkg/make_config.yaml",
    "content": "install-path: /Applications/\nsign-identity: G83H824X6L\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/.gitignore",
    "content": "/node_modules\n/oh_modules\n/local.properties\n/.idea\n**/build\n/.hvigor\n.cxx\n/.clangd\n/.clang-format\n/.clang-tidy\n**/.test\n*.har\n**/BuildProfile.ets\n**/oh-package-lock.json5\n\n**/src/main/resources/rawfile/flutter_assets/\n**/libs/arm64-v8a/libapp.so\n**/libs/arm64-v8a/libflutter.so\n**/libs/arm64-v8a/libvmservice_snapshot.so\nhar/flutter.har\noh-package-lock.json5\ndta"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/AppScope/app.json5",
    "content": "{\n  \"app\": {\n    \"bundleName\": \"org.leanflutter.examples.hello_world\",\n    \"vendor\": \"example\",\n    \"versionCode\": 1000000,\n    \"versionName\": \"1.0.0\",\n    \"icon\": \"$media:app_icon\",\n    \"label\": \"$string:app_name\"\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/AppScope/resources/base/element/string.json",
    "content": "{\n  \"string\": [\n    {\n      \"name\": \"app_name\",\n      \"value\": \"hello_world\"\n    }\n  ]\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/build-profile.json5",
    "content": "\n{\n  \"app\": {\n    \"signingConfigs\": [],\n    \"products\": [\n      {\n        \"name\": \"default\",\n        \"signingConfig\": \"default\",\n        \"compatibleSdkVersion\": \"5.0.0(12)\",\n        \"runtimeOS\": \"HarmonyOS\",\n      }\n    ]\n  },\n  \"modules\": [\n    {\n      \"name\": \"entry\",\n      \"srcPath\": \"./entry\",\n      \"targets\": [\n        {\n          \"name\": \"default\",\n          \"applyToProducts\": [\n            \"default\"\n          ]\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/entry/.gitignore",
    "content": "\n/node_modules\n/oh_modules\n/.preview\n/build\n/.cxx\n/.test"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/entry/build-profile.json5",
    "content": "\n{\n  \"apiType\": 'stageMode',\n  \"buildOption\": {\n  },\n  \"targets\": [\n    {\n      \"name\": \"default\",\n      \"runtimeOS\": \"HarmonyOS\"\n    },\n    {\n      \"name\": \"ohosTest\",\n    }\n  ]\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/entry/hvigorfile.ts",
    "content": "\n// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently.\nexport { hapTasks } from '@ohos/hvigor-ohos-plugin';\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/entry/oh-package.json5",
    "content": "{\n  \"name\": \"entry\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Please describe the basic information.\",\n  \"main\": \"\",\n  \"author\": \"\",\n  \"license\": \"\",\n  \"dependencies\": {}\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/entry/src/main/ets/entryability/EntryAbility.ets",
    "content": "\nimport { FlutterAbility, FlutterEngine } from '@ohos/flutter_ohos';\nimport { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant';\n\nexport default class EntryAbility extends FlutterAbility {\n  configureFlutterEngine(flutterEngine: FlutterEngine) {\n    super.configureFlutterEngine(flutterEngine)\n    GeneratedPluginRegistrant.registerWith(flutterEngine)\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/entry/src/main/ets/pages/Index.ets",
    "content": "\nimport common from '@ohos.app.ability.common';\nimport { FlutterPage } from '@ohos/flutter_ohos'\n\nlet storage = LocalStorage.getShared()\nconst EVENT_BACK_PRESS = 'EVENT_BACK_PRESS'\n\n@Entry(storage)\n@Component\nstruct Index {\n  private context = getContext(this) as common.UIAbilityContext\n  @LocalStorageLink('viewId') viewId: string = \"\";\n\n  build() {\n    Column() {\n      FlutterPage({ viewId: this.viewId })\n    }\n  }\n\n  onBackPress(): boolean {\n    this.context.eventHub.emit(EVENT_BACK_PRESS)\n    return true\n  }\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/entry/src/main/ets/plugins/GeneratedPluginRegistrant.ets",
    "content": "import { FlutterEngine, Log } from '@ohos/flutter_ohos';\n\n/**\n * Generated file. Do not edit.\n * This file is generated by the Flutter tool based on the\n * plugins that support the Ohos platform.\n */\n\nconst TAG = \"GeneratedPluginRegistrant\";\n\nexport class GeneratedPluginRegistrant {\n\n  static registerWith(flutterEngine: FlutterEngine) {\n    try {\n    } catch (e) {\n      Log.e(\n        TAG,\n        \"Tried to register plugins with FlutterEngine (\"\n          + flutterEngine\n          + \") failed.\");\n      Log.e(TAG, \"Received exception while registering\", e);\n    }\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/entry/src/main/module.json5",
    "content": "\n{\n  \"module\": {\n    \"name\": \"entry\",\n    \"type\": \"entry\",\n    \"description\": \"$string:module_desc\",\n    \"mainElement\": \"EntryAbility\",\n    \"deviceTypes\": [\n      \"phone\"\n    ],\n    \"deliveryWithInstall\": true,\n    \"installationFree\": false,\n    \"pages\": \"$profile:main_pages\",\n    \"abilities\": [\n      {\n        \"name\": \"EntryAbility\",\n        \"srcEntry\": \"./ets/entryability/EntryAbility.ets\",\n        \"description\": \"$string:EntryAbility_desc\",\n        \"icon\": \"$media:icon\",\n        \"label\": \"$string:EntryAbility_label\",\n        \"startWindowIcon\": \"$media:icon\",\n        \"startWindowBackground\": \"$color:start_window_background\",\n        \"exported\": true,\n        \"skills\": [\n          {\n            \"entities\": [\n              \"entity.system.home\"\n            ],\n            \"actions\": [\n              \"action.system.home\"\n            ]\n          }\n        ]\n      }\n    ],\n    \"requestPermissions\": [\n      {\"name\" :  \"ohos.permission.INTERNET\"},\n    ]\n  }\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/entry/src/main/resources/base/element/color.json",
    "content": "{\n  \"color\": [\n    {\n      \"name\": \"start_window_background\",\n      \"value\": \"#FFFFFF\"\n    }\n  ]\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/entry/src/main/resources/base/element/string.json",
    "content": "{\n  \"string\": [\n    {\n      \"name\": \"module_desc\",\n      \"value\": \"module description\"\n    },\n    {\n      \"name\": \"EntryAbility_desc\",\n      \"value\": \"description\"\n    },\n    {\n      \"name\": \"EntryAbility_label\",\n      \"value\": \"hello_world\"\n    }\n  ]\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/entry/src/main/resources/base/profile/buildinfo.json5",
    "content": "{\n  \"string\": [\n    {\n      \"name\": \"enable_impeller\",\n      \"value\": \"true\"\n    }\n  ]\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/entry/src/main/resources/base/profile/main_pages.json",
    "content": "{\n  \"src\": [\n    \"pages/Index\"\n  ]\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/entry/src/main/resources/en_US/element/string.json",
    "content": "{\n  \"string\": [\n    {\n      \"name\": \"module_desc\",\n      \"value\": \"module description\"\n    },\n    {\n      \"name\": \"EntryAbility_desc\",\n      \"value\": \"description\"\n    },\n    {\n      \"name\": \"EntryAbility_label\",\n      \"value\": \"hello_world\"\n    }\n  ]\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/entry/src/main/resources/zh_CN/element/string.json",
    "content": "{\n  \"string\": [\n    {\n      \"name\": \"module_desc\",\n      \"value\": \"模块描述\"\n    },\n    {\n      \"name\": \"EntryAbility_desc\",\n      \"value\": \"description\"\n    },\n    {\n      \"name\": \"EntryAbility_label\",\n      \"value\": \"hello_world\"\n    }\n  ]\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/entry/src/ohosTest/ets/test/Ability.test.ets",
    "content": "import hilog from '@ohos.hilog';\nimport { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'\n\nexport default function abilityTest() {\n  describe('ActsAbilityTest', function () {\n    // Defines a test suite. Two parameters are supported: test suite name and test suite function.\n    beforeAll(function () {\n      // Presets an action, which is performed only once before all test cases of the test suite start.\n      // This API supports only one parameter: preset action function.\n    })\n    beforeEach(function () {\n      // Presets an action, which is performed before each unit test case starts.\n      // The number of execution times is the same as the number of test cases defined by **it**.\n      // This API supports only one parameter: preset action function.\n    })\n    afterEach(function () {\n      // Presets a clear action, which is performed after each unit test case ends.\n      // The number of execution times is the same as the number of test cases defined by **it**.\n      // This API supports only one parameter: clear action function.\n    })\n    afterAll(function () {\n      // Presets a clear action, which is performed after all test cases of the test suite end.\n      // This API supports only one parameter: clear action function.\n    })\n    it('assertContain',0, function () {\n      // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.\n      hilog.info(0x0000, 'testTag', '%{public}s', 'it begin');\n      let a = 'abc'\n      let b = 'b'\n      // Defines a variety of assertion methods, which are used to declare expected boolean conditions.\n      expect(a).assertContain(b)\n      expect(a).assertEqual(a)\n    })\n  })\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/entry/src/ohosTest/ets/test/List.test.ets",
    "content": "import abilityTest from './Ability.test'\n\nexport default function testsuite() {\n  abilityTest()\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets",
    "content": "import UIAbility from '@ohos.app.ability.UIAbility';\nimport AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';\nimport hilog from '@ohos.hilog';\nimport { Hypium } from '@ohos/hypium';\nimport testsuite from '../test/List.test';\nimport window from '@ohos.window';\n\nexport default class TestAbility extends UIAbility {\n    onCreate(want, launchParam) {\n        hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate');\n        hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? '');\n        hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:'+ JSON.stringify(launchParam) ?? '');\n        var abilityDelegator: any\n        abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()\n        var abilityDelegatorArguments: any\n        abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()\n        hilog.info(0x0000, 'testTag', '%{public}s', 'start run testcase!!!');\n        Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite)\n    }\n\n    onDestroy() {\n        hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onDestroy');\n    }\n\n    onWindowStageCreate(windowStage: window.WindowStage) {\n        hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageCreate');\n        windowStage.loadContent('testability/pages/Index', (err, data) => {\n            if (err.code) {\n                hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');\n                return;\n            }\n            hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s',\n                JSON.stringify(data) ?? '');\n        });\n    }\n\n    onWindowStageDestroy() {\n        hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy');\n    }\n\n    onForeground() {\n        hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground');\n    }\n\n    onBackground() {\n        hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground');\n    }\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets",
    "content": "\n\nimport hilog from '@ohos.hilog';\n\n@Entry\n@Component\nstruct Index {\n  aboutToAppear() {\n    hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility index aboutToAppear');\n  }\n @State message: string = 'Hello World'\n   build() {\n         Row() {\n           Column() {\n             Text(this.message)\n               .fontSize(50)\n               .fontWeight(FontWeight.Bold)\n             Button() {\n               Text('next page')\n                 .fontSize(20)\n                 .fontWeight(FontWeight.Bold)\n             }.type(ButtonType.Capsule)\n             .margin({\n               top: 20\n             })\n             .backgroundColor('#0D9FFB')\n             .width('35%')\n             .height('5%')\n             .onClick(()=>{\n             })\n           }\n             .width('100%')\n         }\n             .height('100%')\n   }\n }"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts",
    "content": "\nimport hilog from '@ohos.hilog';\nimport TestRunner from '@ohos.application.testRunner';\nimport AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';\n\nvar abilityDelegator = undefined\nvar abilityDelegatorArguments = undefined\n\nasync function onAbilityCreateCallback() {\n    hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback');\n}\n\nasync function addAbilityMonitorCallback(err: any) {\n    hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? '');\n}\n\nexport default class OpenHarmonyTestRunner implements TestRunner {\n    constructor() {\n    }\n\n    onPrepare() {\n        hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare ');\n    }\n\n    async onRun() {\n        hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun run');\n        abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()\n        abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()\n        var testAbilityName = abilityDelegatorArguments.bundleName + '.TestAbility'\n        let lMonitor = {\n            abilityName: testAbilityName,\n            onAbilityCreate: onAbilityCreateCallback,\n        };\n        abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback)\n        var cmd = 'aa start -d 0 -a TestAbility' + ' -b ' + abilityDelegatorArguments.bundleName\n        var debug = abilityDelegatorArguments.parameters['-D']\n        if (debug == 'true')\n        {\n            cmd += ' -D'\n        }\n        hilog.info(0x0000, 'testTag', 'cmd : %{public}s', cmd);\n        abilityDelegator.executeShellCommand(cmd,\n            (err: any, d: any) => {\n                hilog.info(0x0000, 'testTag', 'executeShellCommand : err : %{public}s', JSON.stringify(err) ?? '');\n                hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.stdResult ?? '');\n                hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.exitCode ?? '');\n            })\n        hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end');\n    }\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/entry/src/ohosTest/module.json5",
    "content": "\n{\n  \"module\": {\n    \"name\": \"entry_test\",\n    \"type\": \"feature\",\n    \"description\": \"$string:module_test_desc\",\n    \"mainElement\": \"TestAbility\",\n    \"deviceTypes\": [\n      \"phone\"\n    ],\n    \"deliveryWithInstall\": true,\n    \"installationFree\": false,\n    \"pages\": \"$profile:test_pages\",\n    \"abilities\": [\n      {\n        \"name\": \"TestAbility\",\n        \"srcEntry\": \"./ets/testability/TestAbility.ets\",\n        \"description\": \"$string:TestAbility_desc\",\n        \"icon\": \"$media:icon\",\n        \"label\": \"$string:TestAbility_label\",\n        \"exported\": true,\n        \"startWindowIcon\": \"$media:icon\",\n        \"startWindowBackground\": \"$color:start_window_background\",\n        \"skills\": [\n          {\n            \"actions\": [\n              \"action.system.home\"\n            ],\n            \"entities\": [\n              \"entity.system.home\"\n            ]\n          }\n        ]\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/entry/src/ohosTest/resources/base/element/color.json",
    "content": "{\n  \"color\": [\n    {\n      \"name\": \"start_window_background\",\n      \"value\": \"#FFFFFF\"\n    }\n  ]\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/entry/src/ohosTest/resources/base/element/string.json",
    "content": "{\n  \"string\": [\n    {\n      \"name\": \"module_test_desc\",\n      \"value\": \"test ability description\"\n    },\n    {\n      \"name\": \"TestAbility_desc\",\n      \"value\": \"the test ability\"\n    },\n    {\n      \"name\": \"TestAbility_label\",\n      \"value\": \"test label\"\n    }\n  ]\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json",
    "content": "{\n  \"src\": [\n    \"testability/pages/Index\"\n  ]\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/hvigor/hvigor-config.json5",
    "content": "\n{ \n  \"modelVersion\": \"5.0.0\",\n  \"dependencies\": {\n  }\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/hvigorfile.ts",
    "content": "\nimport { appTasks } from '@ohos/hvigor-ohos-plugin';\n\nexport default {\n    system: appTasks,  /* Built-in plugin of Hvigor. It cannot be modified. */\n    plugins:[]         /* Custom plugin to extend the functionality of Hvigor. */\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/ohos/oh-package.json5",
    "content": "\n\n{\n  \"modelVersion\": \"5.0.0\",\n  \"name\": \"hello_world\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Please describe the basic information.\",\n  \"main\": \"\",\n  \"author\": \"\",\n  \"license\": \"\",\n  \"dependencies\": {\n    \"@ohos/flutter_ohos\": \"file:./har/flutter.har\"\n  },\n  \"devDependencies\": {\n    \"@ohos/hypium\": \"1.0.6\"\n  },\n  \"overrides\": {\n    \"@ohos/flutter_ohos\": \"file:./har/flutter.har\"\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/pubspec.yaml",
    "content": "name: hello_world\ndescription: A new Flutter project.\n# The following line prevents the package from being accidentally published to\n# pub.dev using `flutter pub publish`. This is preferred for private packages.\npublish_to: 'none' # Remove this line if you wish to publish to pub.dev\n\n# The following defines the version and build number for your application.\n# A version number is three numbers separated by dots, like 1.2.43\n# followed by an optional build number separated by a +.\n# Both the version and the builder number may be overridden in flutter\n# build by specifying --build-name and --build-number, respectively.\n# In Android, build-name is used as versionName while build-number used as versionCode.\n# Read more about Android versioning at https://developer.android.com/studio/publish/versioning\n# In iOS, build-name is used as CFBundleShortVersionString while build-number is used as CFBundleVersion.\n# Read more about iOS versioning at\n# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html\n# In Windows, build-name is used as the major, minor, and patch parts\n# of the product and file versions while build-number is used as the build suffix.\nversion: 1.0.0+1\n\nenvironment:\n  sdk: \">=2.16.0 <4.0.0\"\n\n# Dependencies specify other packages that your package needs in order to work.\n# To automatically upgrade your package dependencies to the latest versions\n# consider running `flutter pub upgrade --major-versions`. Alternatively,\n# dependencies can be manually updated by changing the version numbers below to\n# the latest version available on pub.dev. To see which dependencies have newer\n# versions available, run `flutter pub outdated`.\ndependencies:\n  flutter:\n    sdk: flutter\n\n\n  # The following adds the Cupertino Icons font to your application.\n  # Use with the CupertinoIcons class for iOS style icons.\n  cupertino_icons: ^1.0.2\n\ndev_dependencies:\n  flutter_test:\n    sdk: flutter\n\n  # The \"flutter_lints\" package below contains a set of recommended lints to\n  # encourage good coding practices. The lint set provided by the package is\n  # activated in the `analysis_options.yaml` file located at the root of your\n  # package. See that file for information about deactivating specific lint\n  # rules and activating additional ones.\n  flutter_lints: ^2.0.0\n\n# For information on the generic Dart part of this file, see the\n# following page: https://dart.dev/tools/pub/pubspec\n\n# The following section is specific to Flutter packages.\nflutter:\n\n  # The following line ensures that the Material Icons font is\n  # included with your application, so that you can use the icons in\n  # the material Icons class.\n  uses-material-design: true\n\n  # To add assets to your application, add an assets section, like this:\n  # assets:\n  #   - images/a_dot_burr.jpeg\n  #   - images/a_dot_ham.jpeg\n\n  # An image asset can refer to one or more resolution-specific \"variants\", see\n  # https://flutter.dev/assets-and-images/#resolution-aware\n\n  # For details regarding adding assets from package dependencies, see\n  # https://flutter.dev/assets-and-images/#from-packages\n\n  # To add custom fonts to your application, add a fonts section here,\n  # in this \"flutter\" section. Each entry in this list should have a\n  # \"family\" key with the font family name, and a \"fonts\" key with a\n  # list giving the asset and other descriptors for the font. For\n  # example:\n  # fonts:\n  #   - family: Schyler\n  #     fonts:\n  #       - asset: fonts/Schyler-Regular.ttf\n  #       - asset: fonts/Schyler-Italic.ttf\n  #         style: italic\n  #   - family: Trajan Pro\n  #     fonts:\n  #       - asset: fonts/TrajanPro.ttf\n  #       - asset: fonts/TrajanPro_Bold.ttf\n  #         weight: 700\n  #\n  # For details regarding fonts from package dependencies,\n  # see https://flutter.dev/custom-fonts/#from-packages\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/release.sh",
    "content": "#!/bin/bash\n\ndart ../../packages/flutter_distributor/bin/main.dart release --name $1 --skip-clean --no-version-check"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/test/widget_test.dart",
    "content": "// This is a basic Flutter widget test.\n//\n// To perform an interaction with a widget in your test, use the WidgetTester\n// utility that Flutter provides. For example, you can send tap and scroll\n// gestures. You can also use WidgetTester to find child widgets in the widget\n// tree, read text, and verify that the values of widget properties are correct.\n\nimport 'package:flutter/material.dart';\nimport 'package:flutter_test/flutter_test.dart';\n\nimport 'package:hello_world/main.dart';\n\nvoid main() {\n  testWidgets('Counter increments smoke test', (WidgetTester tester) async {\n    // Build our app and trigger a frame.\n    await tester.pumpWidget(const MyApp());\n\n    // Verify that our counter starts at 0.\n    expect(find.text('0'), findsOneWidget);\n    expect(find.text('1'), findsNothing);\n\n    // Tap the '+' icon and trigger a frame.\n    await tester.tap(find.byIcon(Icons.add));\n    await tester.pump();\n\n    // Verify that our counter has incremented.\n    expect(find.text('0'), findsNothing);\n    expect(find.text('1'), findsOneWidget);\n  });\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/web/index.html",
    "content": "<!DOCTYPE html>\n<html>\n<head>\n  <!--\n    If you are serving your web app in a path other than the root, change the\n    href value below to reflect the base path you are serving from.\n\n    The path provided below has to start and end with a slash \"/\" in order for\n    it to work correctly.\n\n    For more details:\n    * https://developer.mozilla.org/en-US/docs/Web/HTML/Element/base\n\n    This is a placeholder for base href that will be replaced by the value of\n    the `--base-href` argument provided to `flutter build`.\n  -->\n  <base href=\"$FLUTTER_BASE_HREF\">\n\n  <meta charset=\"UTF-8\">\n  <meta content=\"IE=Edge\" http-equiv=\"X-UA-Compatible\">\n  <meta name=\"description\" content=\"A new Flutter project.\">\n\n  <!-- iOS meta tags & icons -->\n  <meta name=\"apple-mobile-web-app-capable\" content=\"yes\">\n  <meta name=\"apple-mobile-web-app-status-bar-style\" content=\"black\">\n  <meta name=\"apple-mobile-web-app-title\" content=\"hello_world\">\n  <link rel=\"apple-touch-icon\" href=\"icons/Icon-192.png\">\n\n  <title>hello_world</title>\n  <link rel=\"manifest\" href=\"manifest.json\">\n</head>\n<body>\n  <!-- This script installs service_worker.js to provide PWA functionality to\n       application. For more information, see:\n       https://developers.google.com/web/fundamentals/primers/service-workers -->\n  <script>\n    var serviceWorkerVersion = null;\n    var scriptLoaded = false;\n    function loadMainDartJs() {\n      if (scriptLoaded) {\n        return;\n      }\n      scriptLoaded = true;\n      var scriptTag = document.createElement('script');\n      scriptTag.src = 'main.dart.js';\n      scriptTag.type = 'application/javascript';\n      document.body.append(scriptTag);\n    }\n\n    if ('serviceWorker' in navigator) {\n      // Service workers are supported. Use them.\n      window.addEventListener('load', function () {\n        // Wait for registration to finish before dropping the <script> tag.\n        // Otherwise, the browser will load the script multiple times,\n        // potentially different versions.\n        var serviceWorkerUrl = 'flutter_service_worker.js?v=' + serviceWorkerVersion;\n        navigator.serviceWorker.register(serviceWorkerUrl)\n          .then((reg) => {\n            function waitForActivation(serviceWorker) {\n              serviceWorker.addEventListener('statechange', () => {\n                if (serviceWorker.state == 'activated') {\n                  console.log('Installed new service worker.');\n                  loadMainDartJs();\n                }\n              });\n            }\n            if (!reg.active && (reg.installing || reg.waiting)) {\n              // No active web worker and we have installed or are installing\n              // one for the first time. Simply wait for it to activate.\n              waitForActivation(reg.installing || reg.waiting);\n            } else if (!reg.active.scriptURL.endsWith(serviceWorkerVersion)) {\n              // When the app updates the serviceWorkerVersion changes, so we\n              // need to ask the service worker to update.\n              console.log('New service worker available.');\n              reg.update();\n              waitForActivation(reg.installing);\n            } else {\n              // Existing service worker is still good.\n              console.log('Loading app from service worker.');\n              loadMainDartJs();\n            }\n          });\n\n        // If service worker doesn't succeed in a reasonable amount of time,\n        // fallback to plaint <script> tag.\n        setTimeout(() => {\n          if (!scriptLoaded) {\n            console.warn(\n              'Failed to load app from service worker. Falling back to plain <script> tag.',\n            );\n            loadMainDartJs();\n          }\n        }, 4000);\n      });\n    } else {\n      // Service workers not supported. Just drop the <script> tag.\n      loadMainDartJs();\n    }\n  </script>\n</body>\n</html>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/web/manifest.json",
    "content": "{\n    \"name\": \"hello_world\",\n    \"short_name\": \"hello_world\",\n    \"start_url\": \".\",\n    \"display\": \"standalone\",\n    \"background_color\": \"#0175C2\",\n    \"theme_color\": \"#0175C2\",\n    \"description\": \"A new Flutter project.\",\n    \"orientation\": \"portrait-primary\",\n    \"prefer_related_applications\": false,\n    \"icons\": [\n        {\n            \"src\": \"icons/Icon-192.png\",\n            \"sizes\": \"192x192\",\n            \"type\": \"image/png\"\n        },\n        {\n            \"src\": \"icons/Icon-512.png\",\n            \"sizes\": \"512x512\",\n            \"type\": \"image/png\"\n        },\n        {\n            \"src\": \"icons/Icon-maskable-192.png\",\n            \"sizes\": \"192x192\",\n            \"type\": \"image/png\",\n            \"purpose\": \"maskable\"\n        },\n        {\n            \"src\": \"icons/Icon-maskable-512.png\",\n            \"sizes\": \"512x512\",\n            \"type\": \"image/png\",\n            \"purpose\": \"maskable\"\n        }\n    ]\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/windows/.gitignore",
    "content": "flutter/ephemeral/\n\n# Visual Studio user-specific files.\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# Visual Studio build-related files.\nx64/\nx86/\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!*.[Cc]ache/\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/windows/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.15)\nproject(hello_world LANGUAGES CXX)\n\nset(BINARY_NAME \"hello_world\")\n\ncmake_policy(SET CMP0063 NEW)\n\nset(CMAKE_INSTALL_RPATH \"$ORIGIN/lib\")\n\n# Configure build options.\nget_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)\nif(IS_MULTICONFIG)\n  set(CMAKE_CONFIGURATION_TYPES \"Debug;Profile;Release\"\n    CACHE STRING \"\" FORCE)\nelse()\n  if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)\n    set(CMAKE_BUILD_TYPE \"Debug\" CACHE\n      STRING \"Flutter build mode\" FORCE)\n    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS\n      \"Debug\" \"Profile\" \"Release\")\n  endif()\nendif()\n\nset(CMAKE_EXE_LINKER_FLAGS_PROFILE \"${CMAKE_EXE_LINKER_FLAGS_RELEASE}\")\nset(CMAKE_SHARED_LINKER_FLAGS_PROFILE \"${CMAKE_SHARED_LINKER_FLAGS_RELEASE}\")\nset(CMAKE_C_FLAGS_PROFILE \"${CMAKE_C_FLAGS_RELEASE}\")\nset(CMAKE_CXX_FLAGS_PROFILE \"${CMAKE_CXX_FLAGS_RELEASE}\")\n\n# Use Unicode for all projects.\nadd_definitions(-DUNICODE -D_UNICODE)\n\n# Compilation settings that should be applied to most targets.\nfunction(APPLY_STANDARD_SETTINGS TARGET)\n  target_compile_features(${TARGET} PUBLIC cxx_std_17)\n  target_compile_options(${TARGET} PRIVATE /W4 /WX /wd\"4100\")\n  target_compile_options(${TARGET} PRIVATE /EHsc)\n  target_compile_definitions(${TARGET} PRIVATE \"_HAS_EXCEPTIONS=0\")\n  target_compile_definitions(${TARGET} PRIVATE \"$<$<CONFIG:Debug>:_DEBUG>\")\nendfunction()\n\nset(FLUTTER_MANAGED_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/flutter\")\n\n# Flutter library and tool build rules.\nadd_subdirectory(${FLUTTER_MANAGED_DIR})\n\n# Application build\nadd_subdirectory(\"runner\")\n\n# Generated plugin build rules, which manage building the plugins and adding\n# them to the application.\ninclude(flutter/generated_plugins.cmake)\n\n\n# === Installation ===\n# Support files are copied into place next to the executable, so that it can\n# run in place. This is done instead of making a separate bundle (as on Linux)\n# so that building and running from within Visual Studio will work.\nset(BUILD_BUNDLE_DIR \"$<TARGET_FILE_DIR:${BINARY_NAME}>\")\n# Make the \"install\" step default, as it's required to run.\nset(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)\nif(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)\n  set(CMAKE_INSTALL_PREFIX \"${BUILD_BUNDLE_DIR}\" CACHE PATH \"...\" FORCE)\nendif()\n\nset(INSTALL_BUNDLE_DATA_DIR \"${CMAKE_INSTALL_PREFIX}/data\")\nset(INSTALL_BUNDLE_LIB_DIR \"${CMAKE_INSTALL_PREFIX}\")\n\ninstall(TARGETS ${BINARY_NAME} RUNTIME DESTINATION \"${CMAKE_INSTALL_PREFIX}\"\n  COMPONENT Runtime)\n\ninstall(FILES \"${FLUTTER_ICU_DATA_FILE}\" DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\"\n  COMPONENT Runtime)\n\ninstall(FILES \"${FLUTTER_LIBRARY}\" DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n  COMPONENT Runtime)\n\nif(PLUGIN_BUNDLED_LIBRARIES)\n  install(FILES \"${PLUGIN_BUNDLED_LIBRARIES}\"\n    DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n    COMPONENT Runtime)\nendif()\n\n# Fully re-copy the assets directory on each build to avoid having stale files\n# from a previous install.\nset(FLUTTER_ASSET_DIR_NAME \"flutter_assets\")\ninstall(CODE \"\n  file(REMOVE_RECURSE \\\"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\\\")\n  \" COMPONENT Runtime)\ninstall(DIRECTORY \"${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}\"\n  DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\" COMPONENT Runtime)\n\n# Install the AOT library on non-Debug builds only.\ninstall(FILES \"${AOT_LIBRARY}\" DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\"\n  CONFIGURATIONS Profile;Release\n  COMPONENT Runtime)\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/windows/flutter/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.15)\n\nset(EPHEMERAL_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/ephemeral\")\n\n# Configuration provided via flutter tool.\ninclude(${EPHEMERAL_DIR}/generated_config.cmake)\n\n# TODO: Move the rest of this into files in ephemeral. See\n# https://github.com/flutter/flutter/issues/57146.\nset(WRAPPER_ROOT \"${EPHEMERAL_DIR}/cpp_client_wrapper\")\n\n# Set fallback configurations for older versions of the flutter tool.\nif (NOT DEFINED FLUTTER_TARGET_PLATFORM)\n  set(FLUTTER_TARGET_PLATFORM \"windows-x64\")\nendif()\n\n# === Flutter Library ===\nset(FLUTTER_LIBRARY \"${EPHEMERAL_DIR}/flutter_windows.dll\")\n\n# Published to parent scope for install step.\nset(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)\nset(FLUTTER_ICU_DATA_FILE \"${EPHEMERAL_DIR}/icudtl.dat\" PARENT_SCOPE)\nset(PROJECT_BUILD_DIR \"${PROJECT_DIR}/build/\" PARENT_SCOPE)\nset(AOT_LIBRARY \"${PROJECT_DIR}/build/windows/app.so\" PARENT_SCOPE)\n\nlist(APPEND FLUTTER_LIBRARY_HEADERS\n  \"flutter_export.h\"\n  \"flutter_windows.h\"\n  \"flutter_messenger.h\"\n  \"flutter_plugin_registrar.h\"\n  \"flutter_texture_registrar.h\"\n)\nlist(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND \"${EPHEMERAL_DIR}/\")\nadd_library(flutter INTERFACE)\ntarget_include_directories(flutter INTERFACE\n  \"${EPHEMERAL_DIR}\"\n)\ntarget_link_libraries(flutter INTERFACE \"${FLUTTER_LIBRARY}.lib\")\nadd_dependencies(flutter flutter_assemble)\n\n# === Wrapper ===\nlist(APPEND CPP_WRAPPER_SOURCES_CORE\n  \"core_implementations.cc\"\n  \"standard_codec.cc\"\n)\nlist(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND \"${WRAPPER_ROOT}/\")\nlist(APPEND CPP_WRAPPER_SOURCES_PLUGIN\n  \"plugin_registrar.cc\"\n)\nlist(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND \"${WRAPPER_ROOT}/\")\nlist(APPEND CPP_WRAPPER_SOURCES_APP\n  \"flutter_engine.cc\"\n  \"flutter_view_controller.cc\"\n)\nlist(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND \"${WRAPPER_ROOT}/\")\n\n# Wrapper sources needed for a plugin.\nadd_library(flutter_wrapper_plugin STATIC\n  ${CPP_WRAPPER_SOURCES_CORE}\n  ${CPP_WRAPPER_SOURCES_PLUGIN}\n)\napply_standard_settings(flutter_wrapper_plugin)\nset_target_properties(flutter_wrapper_plugin PROPERTIES\n  POSITION_INDEPENDENT_CODE ON)\nset_target_properties(flutter_wrapper_plugin PROPERTIES\n  CXX_VISIBILITY_PRESET hidden)\ntarget_link_libraries(flutter_wrapper_plugin PUBLIC flutter)\ntarget_include_directories(flutter_wrapper_plugin PUBLIC\n  \"${WRAPPER_ROOT}/include\"\n)\nadd_dependencies(flutter_wrapper_plugin flutter_assemble)\n\n# Wrapper sources needed for the runner.\nadd_library(flutter_wrapper_app STATIC\n  ${CPP_WRAPPER_SOURCES_CORE}\n  ${CPP_WRAPPER_SOURCES_APP}\n)\napply_standard_settings(flutter_wrapper_app)\ntarget_link_libraries(flutter_wrapper_app PUBLIC flutter)\ntarget_include_directories(flutter_wrapper_app PUBLIC\n  \"${WRAPPER_ROOT}/include\"\n)\nadd_dependencies(flutter_wrapper_app flutter_assemble)\n\n# === Flutter tool backend ===\n# _phony_ is a non-existent file to force this command to run every time,\n# since currently there's no way to get a full input/output list from the\n# flutter tool.\nset(PHONY_OUTPUT \"${CMAKE_CURRENT_BINARY_DIR}/_phony_\")\nset_source_files_properties(\"${PHONY_OUTPUT}\" PROPERTIES SYMBOLIC TRUE)\nadd_custom_command(\n  OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}\n    ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}\n    ${CPP_WRAPPER_SOURCES_APP}\n    ${PHONY_OUTPUT}\n  COMMAND ${CMAKE_COMMAND} -E env\n    ${FLUTTER_TOOL_ENVIRONMENT}\n    \"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat\"\n      ${FLUTTER_TARGET_PLATFORM} $<CONFIG>\n  VERBATIM\n)\nadd_custom_target(flutter_assemble DEPENDS\n  \"${FLUTTER_LIBRARY}\"\n  ${FLUTTER_LIBRARY_HEADERS}\n  ${CPP_WRAPPER_SOURCES_CORE}\n  ${CPP_WRAPPER_SOURCES_PLUGIN}\n  ${CPP_WRAPPER_SOURCES_APP}\n)\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/windows/flutter/generated_plugin_registrant.cc",
    "content": "//\n//  Generated file. Do not edit.\n//\n\n// clang-format off\n\n#include \"generated_plugin_registrant.h\"\n\n\nvoid RegisterPlugins(flutter::PluginRegistry* registry) {\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/windows/flutter/generated_plugin_registrant.h",
    "content": "//\n//  Generated file. Do not edit.\n//\n\n// clang-format off\n\n#ifndef GENERATED_PLUGIN_REGISTRANT_\n#define GENERATED_PLUGIN_REGISTRANT_\n\n#include <flutter/plugin_registry.h>\n\n// Registers Flutter plugins.\nvoid RegisterPlugins(flutter::PluginRegistry* registry);\n\n#endif  // GENERATED_PLUGIN_REGISTRANT_\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/windows/flutter/generated_plugins.cmake",
    "content": "#\n# Generated file, do not edit.\n#\n\nlist(APPEND FLUTTER_PLUGIN_LIST\n)\n\nlist(APPEND FLUTTER_FFI_PLUGIN_LIST\n)\n\nset(PLUGIN_BUNDLED_LIBRARIES)\n\nforeach(plugin ${FLUTTER_PLUGIN_LIST})\n  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})\n  target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})\nendforeach(plugin)\n\nforeach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})\n  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})\nendforeach(ffi_plugin)\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/windows/packaging/exe/custom-inno-setup-script.iss",
    "content": "[Setup]\nAppId={{APP_ID}}\nAppVersion={{APP_VERSION}}\nAppName={{DISPLAY_NAME}}\nAppPublisher={{PUBLISHER_NAME}}\nAppPublisherURL={{PUBLISHER_URL}}\nAppSupportURL={{PUBLISHER_URL}}\nAppUpdatesURL={{PUBLISHER_URL}}\nDefaultDirName={{INSTALL_DIR_NAME}}\nDisableProgramGroupPage=yes\nOutputDir=.\nOutputBaseFilename={{OUTPUT_BASE_FILENAME}}\nCompression=lzma\nSolidCompression=yes\nWizardStyle=modern\n\n[Languages]\n{% for locale in LOCALES %}\n{% if locale == 'en' %}Name: \"english\"; MessagesFile: \"compiler:Default.isl\"{% endif %}\n{% if locale == 'zh' %}Name: \"chinesesimplified\"; MessagesFile: \"compiler:Languages\\\\ChineseSimplified.isl\"{% endif %}\n{% if locale == 'ja' %}Name: \"japanese\"; MessagesFile: \"compiler:Languages\\\\Japanese.isl\"{% endif %}\n{% endfor %}\n\n[Tasks]\nName: \"desktopicon\"; Description: \"{cm:CreateDesktopIcon}\"; GroupDescription: \"{cm:AdditionalIcons}\"; Flags: {% if CREATE_DESKTOP_ICON != true %}unchecked{% else %}checkedonce{% endif %}\nName: \"launchAtStartup\"; Description: \"{cm:AutoStartProgram,{{DISPLAY_NAME}}}\"; GroupDescription: \"{cm:AdditionalIcons}\"; Flags: {% if LAUNCH_AT_STARTUP != true %}unchecked{% else %}checkedonce{% endif %}\n[Files]\nSource: \"{{SOURCE_DIR}}\\\\*\"; DestDir: \"{app}\"; Flags: ignoreversion recursesubdirs createallsubdirs\n; NOTE: Don't use \"Flags: ignoreversion\" on any shared system files\n\n[Icons]\nName: \"{autoprograms}\\\\{{APP_NAME}}\"; Filename: \"{app}\\\\{{EXECUTABLE_NAME}}\"\nName: \"{autodesktop}\\\\{{APP_NAME}}\"; Filename: \"{app}\\\\{{EXECUTABLE_NAME}}\"; Tasks: desktopicon\nName: \"{userstartup}\\\\{{APP_NAME}}\"; Filename: \"{app}\\\\{{EXECUTABLE_NAME}}\"; WorkingDir: \"{app}\"; Tasks: launchAtStartup\n[Run]\nFilename: \"{app}\\\\{{EXECUTABLE_NAME}}\"; Description: \"{cm:LaunchProgram,{{DISPLAY_NAME}}}\"; Flags: nowait postinstall skipifsilent\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/windows/packaging/exe/make_config.yaml",
    "content": "# script_template: \"custom-inno-setup-script.iss\"\napp_id: 15F38A1C-4C4B-4477-99C3-1205BC28C4CC\npublisher_name: LeanFlutter\npublisher_url: https://github.com/leanflutter/flutter_distributor\ndisplay_name: Hello 世界\ncreate_desktop_icon: true\nlaunch_at_startup: true\n# install_dir_name: \"D:\\\\HELLO-WORLD\"\nlocales:\n  - zh\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/windows/packaging/msix/make_config.yaml",
    "content": "display_name: HelloWorld\nmsix_version: 1.0.0.0\n# logo_path: C:\\path\\to\\logo.png"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/windows/runner/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.15)\nproject(runner LANGUAGES CXX)\n\nadd_executable(${BINARY_NAME} WIN32\n  \"flutter_window.cpp\"\n  \"main.cpp\"\n  \"utils.cpp\"\n  \"win32_window.cpp\"\n  \"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc\"\n  \"Runner.rc\"\n  \"runner.exe.manifest\"\n)\napply_standard_settings(${BINARY_NAME})\ntarget_compile_definitions(${BINARY_NAME} PRIVATE \"NOMINMAX\")\ntarget_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)\ntarget_include_directories(${BINARY_NAME} PRIVATE \"${CMAKE_SOURCE_DIR}\")\nadd_dependencies(${BINARY_NAME} flutter_assemble)\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/windows/runner/Runner.rc",
    "content": "// Microsoft Visual C++ generated resource script.\n//\n#pragma code_page(65001)\n#include \"resource.h\"\n\n#define APSTUDIO_READONLY_SYMBOLS\n/////////////////////////////////////////////////////////////////////////////\n//\n// Generated from the TEXTINCLUDE 2 resource.\n//\n#include \"winres.h\"\n\n/////////////////////////////////////////////////////////////////////////////\n#undef APSTUDIO_READONLY_SYMBOLS\n\n/////////////////////////////////////////////////////////////////////////////\n// English (United States) resources\n\n#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\nLANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US\n\n#ifdef APSTUDIO_INVOKED\n/////////////////////////////////////////////////////////////////////////////\n//\n// TEXTINCLUDE\n//\n\n1 TEXTINCLUDE\nBEGIN\n    \"resource.h\\0\"\nEND\n\n2 TEXTINCLUDE\nBEGIN\n    \"#include \"\"winres.h\"\"\\r\\n\"\n    \"\\0\"\nEND\n\n3 TEXTINCLUDE\nBEGIN\n    \"\\r\\n\"\n    \"\\0\"\nEND\n\n#endif    // APSTUDIO_INVOKED\n\n\n/////////////////////////////////////////////////////////////////////////////\n//\n// Icon\n//\n\n// Icon with lowest ID value placed first to ensure application icon\n// remains consistent on all systems.\nIDI_APP_ICON            ICON                    \"resources\\\\app_icon.ico\"\n\n\n/////////////////////////////////////////////////////////////////////////////\n//\n// Version\n//\n\n#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD)\n#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD\n#else\n#define VERSION_AS_NUMBER 1,0,0,0\n#endif\n\n#if defined(FLUTTER_VERSION)\n#define VERSION_AS_STRING FLUTTER_VERSION\n#else\n#define VERSION_AS_STRING \"1.0.0\"\n#endif\n\nVS_VERSION_INFO VERSIONINFO\n FILEVERSION VERSION_AS_NUMBER\n PRODUCTVERSION VERSION_AS_NUMBER\n FILEFLAGSMASK VS_FFI_FILEFLAGSMASK\n#ifdef _DEBUG\n FILEFLAGS VS_FF_DEBUG\n#else\n FILEFLAGS 0x0L\n#endif\n FILEOS VOS__WINDOWS32\n FILETYPE VFT_APP\n FILESUBTYPE 0x0L\nBEGIN\n    BLOCK \"StringFileInfo\"\n    BEGIN\n        BLOCK \"040904e4\"\n        BEGIN\n            VALUE \"CompanyName\", \"com.example\" \"\\0\"\n            VALUE \"FileDescription\", \"A new Flutter project.\" \"\\0\"\n            VALUE \"FileVersion\", VERSION_AS_STRING \"\\0\"\n            VALUE \"InternalName\", \"hello_world\" \"\\0\"\n            VALUE \"LegalCopyright\", \"Copyright (C) 2021 com.example. All rights reserved.\" \"\\0\"\n            VALUE \"OriginalFilename\", \"hello_world.exe\" \"\\0\"\n            VALUE \"ProductName\", \"hello_world\" \"\\0\"\n            VALUE \"ProductVersion\", VERSION_AS_STRING \"\\0\"\n        END\n    END\n    BLOCK \"VarFileInfo\"\n    BEGIN\n        VALUE \"Translation\", 0x409, 1252\n    END\nEND\n\n#endif    // English (United States) resources\n/////////////////////////////////////////////////////////////////////////////\n\n\n\n#ifndef APSTUDIO_INVOKED\n/////////////////////////////////////////////////////////////////////////////\n//\n// Generated from the TEXTINCLUDE 3 resource.\n//\n\n\n/////////////////////////////////////////////////////////////////////////////\n#endif    // not APSTUDIO_INVOKED\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/windows/runner/flutter_window.cpp",
    "content": "#include \"flutter_window.h\"\n\n#include <optional>\n\n#include \"flutter/generated_plugin_registrant.h\"\n\nFlutterWindow::FlutterWindow(const flutter::DartProject& project)\n    : project_(project) {}\n\nFlutterWindow::~FlutterWindow() {}\n\nbool FlutterWindow::OnCreate() {\n  if (!Win32Window::OnCreate()) {\n    return false;\n  }\n\n  RECT frame = GetClientArea();\n\n  // The size here must match the window dimensions to avoid unnecessary surface\n  // creation / destruction in the startup path.\n  flutter_controller_ = std::make_unique<flutter::FlutterViewController>(\n      frame.right - frame.left, frame.bottom - frame.top, project_);\n  // Ensure that basic setup of the controller was successful.\n  if (!flutter_controller_->engine() || !flutter_controller_->view()) {\n    return false;\n  }\n  RegisterPlugins(flutter_controller_->engine());\n  SetChildContent(flutter_controller_->view()->GetNativeWindow());\n  return true;\n}\n\nvoid FlutterWindow::OnDestroy() {\n  if (flutter_controller_) {\n    flutter_controller_ = nullptr;\n  }\n\n  Win32Window::OnDestroy();\n}\n\nLRESULT\nFlutterWindow::MessageHandler(HWND hwnd, UINT const message,\n                              WPARAM const wparam,\n                              LPARAM const lparam) noexcept {\n  // Give Flutter, including plugins, an opportunity to handle window messages.\n  if (flutter_controller_) {\n    std::optional<LRESULT> result =\n        flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,\n                                                      lparam);\n    if (result) {\n      return *result;\n    }\n  }\n\n  switch (message) {\n    case WM_FONTCHANGE:\n      flutter_controller_->engine()->ReloadSystemFonts();\n      break;\n  }\n\n  return Win32Window::MessageHandler(hwnd, message, wparam, lparam);\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/windows/runner/flutter_window.h",
    "content": "#ifndef RUNNER_FLUTTER_WINDOW_H_\n#define RUNNER_FLUTTER_WINDOW_H_\n\n#include <flutter/dart_project.h>\n#include <flutter/flutter_view_controller.h>\n\n#include <memory>\n\n#include \"win32_window.h\"\n\n// A window that does nothing but host a Flutter view.\nclass FlutterWindow : public Win32Window {\n public:\n  // Creates a new FlutterWindow hosting a Flutter view running |project|.\n  explicit FlutterWindow(const flutter::DartProject& project);\n  virtual ~FlutterWindow();\n\n protected:\n  // Win32Window:\n  bool OnCreate() override;\n  void OnDestroy() override;\n  LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,\n                         LPARAM const lparam) noexcept override;\n\n private:\n  // The project to run.\n  flutter::DartProject project_;\n\n  // The Flutter instance hosted by this window.\n  std::unique_ptr<flutter::FlutterViewController> flutter_controller_;\n};\n\n#endif  // RUNNER_FLUTTER_WINDOW_H_\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/windows/runner/main.cpp",
    "content": "#include <flutter/dart_project.h>\n#include <flutter/flutter_view_controller.h>\n#include <windows.h>\n\n#include \"flutter_window.h\"\n#include \"utils.h\"\n\nint APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,\n                      _In_ wchar_t *command_line, _In_ int show_command) {\n  // Attach to console when present (e.g., 'flutter run') or create a\n  // new console when running with a debugger.\n  if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {\n    CreateAndAttachConsole();\n  }\n\n  // Initialize COM, so that it is available for use in the library and/or\n  // plugins.\n  ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);\n\n  flutter::DartProject project(L\"data\");\n\n  std::vector<std::string> command_line_arguments =\n      GetCommandLineArguments();\n\n  project.set_dart_entrypoint_arguments(std::move(command_line_arguments));\n\n  FlutterWindow window(project);\n  Win32Window::Point origin(10, 10);\n  Win32Window::Size size(1280, 720);\n  if (!window.CreateAndShow(L\"hello_world\", origin, size)) {\n    return EXIT_FAILURE;\n  }\n  window.SetQuitOnClose(true);\n\n  ::MSG msg;\n  while (::GetMessage(&msg, nullptr, 0, 0)) {\n    ::TranslateMessage(&msg);\n    ::DispatchMessage(&msg);\n  }\n\n  ::CoUninitialize();\n  return EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/windows/runner/resource.h",
    "content": "//{{NO_DEPENDENCIES}}\n// Microsoft Visual C++ generated include file.\n// Used by Runner.rc\n//\n#define IDI_APP_ICON                    101\n\n// Next default values for new objects\n//\n#ifdef APSTUDIO_INVOKED\n#ifndef APSTUDIO_READONLY_SYMBOLS\n#define _APS_NEXT_RESOURCE_VALUE        102\n#define _APS_NEXT_COMMAND_VALUE         40001\n#define _APS_NEXT_CONTROL_VALUE         1001\n#define _APS_NEXT_SYMED_VALUE           101\n#endif\n#endif\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/windows/runner/runner.exe.manifest",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\n  <application xmlns=\"urn:schemas-microsoft-com:asm.v3\">\n    <windowsSettings>\n      <dpiAwareness xmlns=\"http://schemas.microsoft.com/SMI/2016/WindowsSettings\">PerMonitorV2</dpiAwareness>\n    </windowsSettings>\n  </application>\n  <compatibility xmlns=\"urn:schemas-microsoft-com:compatibility.v1\">\n    <application>\n      <!-- Windows 10 -->\n      <supportedOS Id=\"{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}\"/>\n      <!-- Windows 8.1 -->\n      <supportedOS Id=\"{1f676c76-80e1-4239-95bb-83d0f6d0da78}\"/>\n      <!-- Windows 8 -->\n      <supportedOS Id=\"{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}\"/>\n      <!-- Windows 7 -->\n      <supportedOS Id=\"{35138b9a-5d96-4fbd-8e2d-a2440225f93a}\"/>\n    </application>\n  </compatibility>\n</assembly>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/windows/runner/utils.cpp",
    "content": "#include \"utils.h\"\n\n#include <flutter_windows.h>\n#include <io.h>\n#include <stdio.h>\n#include <windows.h>\n\n#include <iostream>\n\nvoid CreateAndAttachConsole() {\n  if (::AllocConsole()) {\n    FILE *unused;\n    if (freopen_s(&unused, \"CONOUT$\", \"w\", stdout)) {\n      _dup2(_fileno(stdout), 1);\n    }\n    if (freopen_s(&unused, \"CONOUT$\", \"w\", stderr)) {\n      _dup2(_fileno(stdout), 2);\n    }\n    std::ios::sync_with_stdio();\n    FlutterDesktopResyncOutputStreams();\n  }\n}\n\nstd::vector<std::string> GetCommandLineArguments() {\n  // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.\n  int argc;\n  wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);\n  if (argv == nullptr) {\n    return std::vector<std::string>();\n  }\n\n  std::vector<std::string> command_line_arguments;\n\n  // Skip the first argument as it's the binary name.\n  for (int i = 1; i < argc; i++) {\n    command_line_arguments.push_back(Utf8FromUtf16(argv[i]));\n  }\n\n  ::LocalFree(argv);\n\n  return command_line_arguments;\n}\n\nstd::string Utf8FromUtf16(const wchar_t* utf16_string) {\n  if (utf16_string == nullptr) {\n    return std::string();\n  }\n  int target_length = ::WideCharToMultiByte(\n      CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,\n      -1, nullptr, 0, nullptr, nullptr);\n  if (target_length == 0) {\n    return std::string();\n  }\n  std::string utf8_string;\n  utf8_string.resize(target_length);\n  int converted_length = ::WideCharToMultiByte(\n      CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,\n      -1, utf8_string.data(),\n      target_length, nullptr, nullptr);\n  if (converted_length == 0) {\n    return std::string();\n  }\n  return utf8_string;\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/windows/runner/utils.h",
    "content": "#ifndef RUNNER_UTILS_H_\n#define RUNNER_UTILS_H_\n\n#include <string>\n#include <vector>\n\n// Creates a console for the process, and redirects stdout and stderr to\n// it for both the runner and the Flutter library.\nvoid CreateAndAttachConsole();\n\n// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string\n// encoded in UTF-8. Returns an empty std::string on failure.\nstd::string Utf8FromUtf16(const wchar_t* utf16_string);\n\n// Gets the command line arguments passed in as a std::vector<std::string>,\n// encoded in UTF-8. Returns an empty std::vector<std::string> on failure.\nstd::vector<std::string> GetCommandLineArguments();\n\n#endif  // RUNNER_UTILS_H_\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/windows/runner/win32_window.cpp",
    "content": "#include \"win32_window.h\"\n\n#include <flutter_windows.h>\n\n#include \"resource.h\"\n\nnamespace {\n\nconstexpr const wchar_t kWindowClassName[] = L\"FLUTTER_RUNNER_WIN32_WINDOW\";\n\n// The number of Win32Window objects that currently exist.\nstatic int g_active_window_count = 0;\n\nusing EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);\n\n// Scale helper to convert logical scaler values to physical using passed in\n// scale factor\nint Scale(int source, double scale_factor) {\n  return static_cast<int>(source * scale_factor);\n}\n\n// Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.\n// This API is only needed for PerMonitor V1 awareness mode.\nvoid EnableFullDpiSupportIfAvailable(HWND hwnd) {\n  HMODULE user32_module = LoadLibraryA(\"User32.dll\");\n  if (!user32_module) {\n    return;\n  }\n  auto enable_non_client_dpi_scaling =\n      reinterpret_cast<EnableNonClientDpiScaling*>(\n          GetProcAddress(user32_module, \"EnableNonClientDpiScaling\"));\n  if (enable_non_client_dpi_scaling != nullptr) {\n    enable_non_client_dpi_scaling(hwnd);\n    FreeLibrary(user32_module);\n  }\n}\n\n}  // namespace\n\n// Manages the Win32Window's window class registration.\nclass WindowClassRegistrar {\n public:\n  ~WindowClassRegistrar() = default;\n\n  // Returns the singleton registar instance.\n  static WindowClassRegistrar* GetInstance() {\n    if (!instance_) {\n      instance_ = new WindowClassRegistrar();\n    }\n    return instance_;\n  }\n\n  // Returns the name of the window class, registering the class if it hasn't\n  // previously been registered.\n  const wchar_t* GetWindowClass();\n\n  // Unregisters the window class. Should only be called if there are no\n  // instances of the window.\n  void UnregisterWindowClass();\n\n private:\n  WindowClassRegistrar() = default;\n\n  static WindowClassRegistrar* instance_;\n\n  bool class_registered_ = false;\n};\n\nWindowClassRegistrar* WindowClassRegistrar::instance_ = nullptr;\n\nconst wchar_t* WindowClassRegistrar::GetWindowClass() {\n  if (!class_registered_) {\n    WNDCLASS window_class{};\n    window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);\n    window_class.lpszClassName = kWindowClassName;\n    window_class.style = CS_HREDRAW | CS_VREDRAW;\n    window_class.cbClsExtra = 0;\n    window_class.cbWndExtra = 0;\n    window_class.hInstance = GetModuleHandle(nullptr);\n    window_class.hIcon =\n        LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));\n    window_class.hbrBackground = 0;\n    window_class.lpszMenuName = nullptr;\n    window_class.lpfnWndProc = Win32Window::WndProc;\n    RegisterClass(&window_class);\n    class_registered_ = true;\n  }\n  return kWindowClassName;\n}\n\nvoid WindowClassRegistrar::UnregisterWindowClass() {\n  UnregisterClass(kWindowClassName, nullptr);\n  class_registered_ = false;\n}\n\nWin32Window::Win32Window() {\n  ++g_active_window_count;\n}\n\nWin32Window::~Win32Window() {\n  --g_active_window_count;\n  Destroy();\n}\n\nbool Win32Window::CreateAndShow(const std::wstring& title,\n                                const Point& origin,\n                                const Size& size) {\n  Destroy();\n\n  const wchar_t* window_class =\n      WindowClassRegistrar::GetInstance()->GetWindowClass();\n\n  const POINT target_point = {static_cast<LONG>(origin.x),\n                              static_cast<LONG>(origin.y)};\n  HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);\n  UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);\n  double scale_factor = dpi / 96.0;\n\n  HWND window = CreateWindow(\n      window_class, title.c_str(), WS_OVERLAPPEDWINDOW | WS_VISIBLE,\n      Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),\n      Scale(size.width, scale_factor), Scale(size.height, scale_factor),\n      nullptr, nullptr, GetModuleHandle(nullptr), this);\n\n  if (!window) {\n    return false;\n  }\n\n  return OnCreate();\n}\n\n// static\nLRESULT CALLBACK Win32Window::WndProc(HWND const window,\n                                      UINT const message,\n                                      WPARAM const wparam,\n                                      LPARAM const lparam) noexcept {\n  if (message == WM_NCCREATE) {\n    auto window_struct = reinterpret_cast<CREATESTRUCT*>(lparam);\n    SetWindowLongPtr(window, GWLP_USERDATA,\n                     reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));\n\n    auto that = static_cast<Win32Window*>(window_struct->lpCreateParams);\n    EnableFullDpiSupportIfAvailable(window);\n    that->window_handle_ = window;\n  } else if (Win32Window* that = GetThisFromHandle(window)) {\n    return that->MessageHandler(window, message, wparam, lparam);\n  }\n\n  return DefWindowProc(window, message, wparam, lparam);\n}\n\nLRESULT\nWin32Window::MessageHandler(HWND hwnd,\n                            UINT const message,\n                            WPARAM const wparam,\n                            LPARAM const lparam) noexcept {\n  switch (message) {\n    case WM_DESTROY:\n      window_handle_ = nullptr;\n      Destroy();\n      if (quit_on_close_) {\n        PostQuitMessage(0);\n      }\n      return 0;\n\n    case WM_DPICHANGED: {\n      auto newRectSize = reinterpret_cast<RECT*>(lparam);\n      LONG newWidth = newRectSize->right - newRectSize->left;\n      LONG newHeight = newRectSize->bottom - newRectSize->top;\n\n      SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,\n                   newHeight, SWP_NOZORDER | SWP_NOACTIVATE);\n\n      return 0;\n    }\n    case WM_SIZE: {\n      RECT rect = GetClientArea();\n      if (child_content_ != nullptr) {\n        // Size and position the child window.\n        MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,\n                   rect.bottom - rect.top, TRUE);\n      }\n      return 0;\n    }\n\n    case WM_ACTIVATE:\n      if (child_content_ != nullptr) {\n        SetFocus(child_content_);\n      }\n      return 0;\n  }\n\n  return DefWindowProc(window_handle_, message, wparam, lparam);\n}\n\nvoid Win32Window::Destroy() {\n  OnDestroy();\n\n  if (window_handle_) {\n    DestroyWindow(window_handle_);\n    window_handle_ = nullptr;\n  }\n  if (g_active_window_count == 0) {\n    WindowClassRegistrar::GetInstance()->UnregisterWindowClass();\n  }\n}\n\nWin32Window* Win32Window::GetThisFromHandle(HWND const window) noexcept {\n  return reinterpret_cast<Win32Window*>(\n      GetWindowLongPtr(window, GWLP_USERDATA));\n}\n\nvoid Win32Window::SetChildContent(HWND content) {\n  child_content_ = content;\n  SetParent(content, window_handle_);\n  RECT frame = GetClientArea();\n\n  MoveWindow(content, frame.left, frame.top, frame.right - frame.left,\n             frame.bottom - frame.top, true);\n\n  SetFocus(child_content_);\n}\n\nRECT Win32Window::GetClientArea() {\n  RECT frame;\n  GetClientRect(window_handle_, &frame);\n  return frame;\n}\n\nHWND Win32Window::GetHandle() {\n  return window_handle_;\n}\n\nvoid Win32Window::SetQuitOnClose(bool quit_on_close) {\n  quit_on_close_ = quit_on_close;\n}\n\nbool Win32Window::OnCreate() {\n  // No-op; provided for subclasses.\n  return true;\n}\n\nvoid Win32Window::OnDestroy() {\n  // No-op; provided for subclasses.\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/hello_world/windows/runner/win32_window.h",
    "content": "#ifndef RUNNER_WIN32_WINDOW_H_\n#define RUNNER_WIN32_WINDOW_H_\n\n#include <windows.h>\n\n#include <functional>\n#include <memory>\n#include <string>\n\n// A class abstraction for a high DPI-aware Win32 Window. Intended to be\n// inherited from by classes that wish to specialize with custom\n// rendering and input handling\nclass Win32Window {\n public:\n  struct Point {\n    unsigned int x;\n    unsigned int y;\n    Point(unsigned int x, unsigned int y) : x(x), y(y) {}\n  };\n\n  struct Size {\n    unsigned int width;\n    unsigned int height;\n    Size(unsigned int width, unsigned int height)\n        : width(width), height(height) {}\n  };\n\n  Win32Window();\n  virtual ~Win32Window();\n\n  // Creates and shows a win32 window with |title| and position and size using\n  // |origin| and |size|. New windows are created on the default monitor. Window\n  // sizes are specified to the OS in physical pixels, hence to ensure a\n  // consistent size to will treat the width height passed in to this function\n  // as logical pixels and scale to appropriate for the default monitor. Returns\n  // true if the window was created successfully.\n  bool CreateAndShow(const std::wstring& title,\n                     const Point& origin,\n                     const Size& size);\n\n  // Release OS resources associated with window.\n  void Destroy();\n\n  // Inserts |content| into the window tree.\n  void SetChildContent(HWND content);\n\n  // Returns the backing Window handle to enable clients to set icon and other\n  // window properties. Returns nullptr if the window has been destroyed.\n  HWND GetHandle();\n\n  // If true, closing this window will quit the application.\n  void SetQuitOnClose(bool quit_on_close);\n\n  // Return a RECT representing the bounds of the current client area.\n  RECT GetClientArea();\n\n protected:\n  // Processes and route salient window messages for mouse handling,\n  // size change and DPI. Delegates handling of these to member overloads that\n  // inheriting classes can handle.\n  virtual LRESULT MessageHandler(HWND window,\n                                 UINT const message,\n                                 WPARAM const wparam,\n                                 LPARAM const lparam) noexcept;\n\n  // Called when CreateAndShow is called, allowing subclass window-related\n  // setup. Subclasses should return false if setup fails.\n  virtual bool OnCreate();\n\n  // Called when Destroy is called.\n  virtual void OnDestroy();\n\n private:\n  friend class WindowClassRegistrar;\n\n  // OS callback called by message pump. Handles the WM_NCCREATE message which\n  // is passed when the non-client area is being created and enables automatic\n  // non-client DPI scaling so that the non-client area automatically\n  // responsponds to changes in DPI. All other messages are handled by\n  // MessageHandler.\n  static LRESULT CALLBACK WndProc(HWND const window,\n                                  UINT const message,\n                                  WPARAM const wparam,\n                                  LPARAM const lparam) noexcept;\n\n  // Retrieves a class instance pointer for |window|\n  static Win32Window* GetThisFromHandle(HWND const window) noexcept;\n\n  bool quit_on_close_ = false;\n\n  // window handle for top level window.\n  HWND window_handle_ = nullptr;\n\n  // window handle for hosted content.\n  HWND child_content_ = nullptr;\n};\n\n#endif  // RUNNER_WIN32_WINDOW_H_\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/.gitignore",
    "content": "# Miscellaneous\n*.class\n*.log\n*.pyc\n*.swp\n.DS_Store\n.atom/\n.buildlog/\n.history\n.svn/\n\n# IntelliJ related\n*.iml\n*.ipr\n*.iws\n.idea/\n\n# The .vscode folder contains launch configuration and tasks you configure in\n# VS Code which you may wish to be included in version control, so this line\n# is commented out by default.\n#.vscode/\n\n# Flutter/Dart/Pub related\n**/doc/api/\n**/ios/Flutter/.last_build_id\n.dart_tool/\n.flutter-plugins\n.flutter-plugins-dependencies\n.packages\n.pub-cache/\n.pub/\n/build/\n\n# Web related\nlib/generated_plugin_registrant.dart\n\n# Symbolication related\napp.*.symbols\n\n# Obfuscation related\napp.*.map.json\n\n# Android Studio will place build artifacts here\n/android/app/debug\n/android/app/profile\n/android/app/release\n\n# flutter_distributor related\ndist/\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/.metadata",
    "content": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrades etc.\n#\n# This file should be version controlled.\n\nversion:\n  revision: 9cd3d0d9ff05768afa249e036acc66e8abe93bff\n  channel: stable\n\nproject_type: app\n\n# Tracks metadata for the flutter migrate command\nmigration:\n  platforms:\n    - platform: root\n      create_revision: 9cd3d0d9ff05768afa249e036acc66e8abe93bff\n      base_revision: 9cd3d0d9ff05768afa249e036acc66e8abe93bff\n    - platform: android\n      create_revision: 9cd3d0d9ff05768afa249e036acc66e8abe93bff\n      base_revision: 9cd3d0d9ff05768afa249e036acc66e8abe93bff\n\n  # User provided section\n\n  # List of Local paths (relative to this file) that should be\n  # ignored by the migrate tool.\n  #\n  # Files that are not part of the templates will be ignored by default.\n  unmanaged_files:\n    - 'lib/main.dart'\n    - 'ios/Runner.xcodeproj/project.pbxproj'\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/analysis_options.yaml",
    "content": "# This file configures the analyzer, which statically analyzes Dart code to\n# check for errors, warnings, and lints.\n#\n# The issues identified by the analyzer are surfaced in the UI of Dart-enabled\n# IDEs (https://dart.dev/tools#ides-and-editors). The analyzer can also be\n# invoked from the command line by running `flutter analyze`.\n\n# The following line activates a set of recommended lints for Flutter apps,\n# packages, and plugins designed to encourage good coding practices.\ninclude: package:flutter_lints/flutter.yaml\n\nlinter:\n  # The lint rules applied to this project can be customized in the\n  # section below to disable rules from the `package:flutter_lints/flutter.yaml`\n  # included above or to enable additional rules. A list of all available lints\n  # and their documentation is published at\n  # https://dart-lang.github.io/linter/lints/index.html.\n  #\n  # Instead of disabling a lint rule for the entire project in the\n  # section below, it can also be suppressed for a single line of code\n  # or a specific dart file by using the `// ignore: name_of_lint` and\n  # `// ignore_for_file: name_of_lint` syntax on the line or in the file\n  # producing the lint.\n  rules:\n    # avoid_print: false  # Uncomment to disable the `avoid_print` rule\n    # prefer_single_quotes: true  # Uncomment to enable the `prefer_single_quotes` rule\n\n# Additional information about this file can be found at\n# https://dart.dev/guides/language/analysis-options\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/android/.gitignore",
    "content": "gradle-wrapper.jar\n/.gradle\n/captures/\n/gradlew\n/gradlew.bat\n/local.properties\nGeneratedPluginRegistrant.java\n\n# Remember to never publicly share your keystore.\n# See https://flutter.dev/docs/deployment/android#reference-the-keystore-from-the-app\nkey.properties\n**/*.keystore\n**/*.jks\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/android/app/build.gradle",
    "content": "def localProperties = new Properties()\ndef localPropertiesFile = rootProject.file('local.properties')\nif (localPropertiesFile.exists()) {\n    localPropertiesFile.withReader('UTF-8') { reader ->\n        localProperties.load(reader)\n    }\n}\n\ndef flutterRoot = localProperties.getProperty('flutter.sdk')\nif (flutterRoot == null) {\n    throw new GradleException(\"Flutter SDK not found. Define location with flutter.sdk in the local.properties file.\")\n}\n\ndef flutterVersionCode = localProperties.getProperty('flutter.versionCode')\nif (flutterVersionCode == null) {\n    flutterVersionCode = '1'\n}\n\ndef flutterVersionName = localProperties.getProperty('flutter.versionName')\nif (flutterVersionName == null) {\n    flutterVersionName = '1.0'\n}\n\napply plugin: 'com.android.application'\napply plugin: 'kotlin-android'\napply from: \"$flutterRoot/packages/flutter_tools/gradle/flutter.gradle\"\n\nandroid {\n    namespace \"org.leanflutter.examples.multiple_flavors\"\n    compileSdkVersion flutter.compileSdkVersion\n    ndkVersion flutter.ndkVersion\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_1_8\n        targetCompatibility JavaVersion.VERSION_1_8\n    }\n\n    defaultConfig {\n        // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).\n        applicationId \"org.leanflutter.examples.multiple_flavors\"\n        // You can update the following values to match your application needs.\n        // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-gradle-build-configuration.\n        minSdkVersion 19\n        targetSdkVersion flutter.targetSdkVersion\n        versionCode flutterVersionCode.toInteger()\n        versionName flutterVersionName\n    }\n    \n    flavorDimensions \"mode\"\n\n    productFlavors {\n        dev {\n            dimension \"mode\"\n        }\n        prod {\n            dimension \"mode\"\n        }\n        prod_64 {\n            dimension \"mode\"\n            ndk {\n                abiFilters \"arm64-v8a\"\n            }\n        }\n    }\n\n    buildTypes {\n        release {\n            productFlavors.dev.signingConfig signingConfigs.debug\n            productFlavors.prod.signingConfig signingConfigs.debug\n            productFlavors.prod_64.signingConfig signingConfigs.debug\n        }\n    }\n}\n\nflutter {\n    source '../..'\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/android/app/src/debug/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <!-- The INTERNET permission is required for development. Specifically,\n         the Flutter tool needs it to communicate with the running application\n         to allow setting breakpoints, to provide hot reload, etc.\n    -->\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n</manifest>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/android/app/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <application\n        android:label=\"multiple_flavors\"\n        android:name=\"${applicationName}\"\n        android:icon=\"@mipmap/ic_launcher\">\n        <activity\n            android:name=\".MainActivity\"\n            android:exported=\"true\"\n            android:launchMode=\"singleTop\"\n            android:theme=\"@style/LaunchTheme\"\n            android:configChanges=\"orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode\"\n            android:hardwareAccelerated=\"true\"\n            android:windowSoftInputMode=\"adjustResize\">\n            <!-- Specifies an Android theme to apply to this Activity as soon as\n                 the Android process has started. This theme is visible to the user\n                 while the Flutter UI initializes. After that, this theme continues\n                 to determine the Window background behind the Flutter UI. -->\n            <meta-data\n              android:name=\"io.flutter.embedding.android.NormalTheme\"\n              android:resource=\"@style/NormalTheme\"\n              />\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\"/>\n                <category android:name=\"android.intent.category.LAUNCHER\"/>\n            </intent-filter>\n        </activity>\n        <!-- Don't delete the meta-data below.\n             This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->\n        <meta-data\n            android:name=\"flutterEmbedding\"\n            android:value=\"2\" />\n    </application>\n</manifest>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/android/app/src/main/java/org/leanflutter/examples/multiple_flavors/MainActivity.java",
    "content": "package org.leanflutter.examples.multiple_flavors;\n\nimport io.flutter.embedding.android.FlutterActivity;\n\npublic class MainActivity extends FlutterActivity {\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/android/app/src/main/res/drawable/launch_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Modify this file to customize your launch splash screen -->\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"@android:color/white\" />\n\n    <!-- You can insert your own image assets here -->\n    <!-- <item>\n        <bitmap\n            android:gravity=\"center\"\n            android:src=\"@mipmap/launch_image\" />\n    </item> -->\n</layer-list>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/android/app/src/main/res/drawable-v21/launch_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!-- Modify this file to customize your launch splash screen -->\n<layer-list xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <item android:drawable=\"?android:colorBackground\" />\n\n    <!-- You can insert your own image assets here -->\n    <!-- <item>\n        <bitmap\n            android:gravity=\"center\"\n            android:src=\"@mipmap/launch_image\" />\n    </item> -->\n</layer-list>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/android/app/src/main/res/values/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off -->\n    <style name=\"LaunchTheme\" parent=\"@android:style/Theme.Light.NoTitleBar\">\n        <!-- Show a splash screen on the activity. Automatically removed when\n             the Flutter engine draws its first frame -->\n        <item name=\"android:windowBackground\">@drawable/launch_background</item>\n    </style>\n    <!-- Theme applied to the Android Window as soon as the process has started.\n         This theme determines the color of the Android Window while your\n         Flutter UI initializes, as well as behind your Flutter UI while its\n         running.\n\n         This Theme is only used starting with V2 of Flutter's Android embedding. -->\n    <style name=\"NormalTheme\" parent=\"@android:style/Theme.Light.NoTitleBar\">\n        <item name=\"android:windowBackground\">?android:colorBackground</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/android/app/src/main/res/values-night/styles.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on -->\n    <style name=\"LaunchTheme\" parent=\"@android:style/Theme.Black.NoTitleBar\">\n        <!-- Show a splash screen on the activity. Automatically removed when\n             the Flutter engine draws its first frame -->\n        <item name=\"android:windowBackground\">@drawable/launch_background</item>\n    </style>\n    <!-- Theme applied to the Android Window as soon as the process has started.\n         This theme determines the color of the Android Window while your\n         Flutter UI initializes, as well as behind your Flutter UI while its\n         running.\n\n         This Theme is only used starting with V2 of Flutter's Android embedding. -->\n    <style name=\"NormalTheme\" parent=\"@android:style/Theme.Black.NoTitleBar\">\n        <item name=\"android:windowBackground\">?android:colorBackground</item>\n    </style>\n</resources>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/android/app/src/profile/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <!-- The INTERNET permission is required for development. Specifically,\n         the Flutter tool needs it to communicate with the running application\n         to allow setting breakpoints, to provide hot reload, etc.\n    -->\n    <uses-permission android:name=\"android.permission.INTERNET\"/>\n</manifest>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/android/build.gradle",
    "content": "buildscript {\n    ext.kotlin_version = '1.7.10'\n    repositories {\n        google()\n        mavenCentral()\n    }\n\n    dependencies {\n        classpath 'com.android.tools.build:gradle:7.3.0'\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n    }\n}\n\nrootProject.buildDir = '../build'\nsubprojects {\n    project.buildDir = \"${rootProject.buildDir}/${project.name}\"\n}\nsubprojects {\n    project.evaluationDependsOn(':app')\n}\n\ntasks.register(\"clean\", Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/android/gradle/wrapper/gradle-wrapper.properties",
    "content": "distributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-7.5-all.zip\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/android/gradle.properties",
    "content": "org.gradle.jvmargs=-Xmx1536M\nandroid.useAndroidX=true\nandroid.enableJetifier=true\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/android/settings.gradle",
    "content": "include ':app'\n\ndef localPropertiesFile = new File(rootProject.projectDir, \"local.properties\")\ndef properties = new Properties()\n\nassert localPropertiesFile.exists()\nlocalPropertiesFile.withReader(\"UTF-8\") { reader -> properties.load(reader) }\n\ndef flutterSdkPath = properties.getProperty(\"flutter.sdk\")\nassert flutterSdkPath != null, \"flutter.sdk not set in local.properties\"\napply from: \"$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle\"\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/distribute_options.yaml",
    "content": "output: dist/\n# See https://mustache.github.io/mustache.5.html for string template format.\nartifact_name: \"{{name}}{{#channel}}-{{channel}}{{/channel}}-{{build_name}}({{build_number}}){{#is_profile}}-{{build_mode}}{{/is_profile}}-{{platform}}.{{ext}}\"\nreleases:\n  - name: release-android\n    jobs:\n      - name: release-android-dev\n        package:\n          platform: android\n          target: apk\n          channel: dev2\n          build_args:\n            profile: true\n            flavor: dev\n            dart-define:\n              APP_ENV: dev\n      - name: release-android-prod\n        package:\n          platform: android\n          target: apk\n          channel: prod2\n          build_args:\n            profile: true\n            flavor: prod\n            dart-define:\n              APP_ENV: prod\n      - name: release-android-prod64\n        package:\n          platform: android\n          target: apk\n          channel: prod2_64\n          build_args:\n            profile: true\n            flavor: prod_64\n            target-platform: android-arm64\n            dart-define:\n              APP_ENV: prod\n      - name: release-android-dev-aab\n        package:\n          platform: android\n          target: aab\n          build_args:\n            profile: true\n            flavor: dev\n            dart-define:\n              APP_ENV: dev\n      - name: release-android-prod-aab\n        package:\n          platform: android\n          target: aab\n          build_args:\n            profile: true\n            flavor: prod\n            dart-define:\n              APP_ENV: prod\n      - name: release-android-prod64-aab\n        package:\n          platform: android\n          target: aab\n          build_args:\n            profile: true\n            flavor: prod_64\n            target-platform: android-arm64\n            dart-define:\n              APP_ENV: prod\n  - name: release-ios\n    jobs:\n      - name: release-ios-dev-ipa\n        package:\n          platform: ios\n          target: ipa\n          channel: dev2\n          build_args:\n            export-options-plist: ios/dev_ExportOptions.plist\n            flavor: dev\n            dart-define:\n              APP_ENV: dev\n      - name: release-ios-prod-ipa\n        package:\n          platform: ios\n          target: ipa\n          channel: prod2\n          build_args:\n            export-options-plist: ios/prod_ExportOptions.plist\n            flavor: prod\n            dart-define:\n              APP_ENV: prod\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ios/.gitignore",
    "content": "**/dgph\n*.mode1v3\n*.mode2v3\n*.moved-aside\n*.pbxuser\n*.perspectivev3\n**/*sync/\n.sconsign.dblite\n.tags*\n**/.vagrant/\n**/DerivedData/\nIcon?\n**/Pods/\n**/.symlinks/\nprofile\nxcuserdata\n**/.generated/\nFlutter/App.framework\nFlutter/Flutter.framework\nFlutter/Flutter.podspec\nFlutter/Generated.xcconfig\nFlutter/ephemeral/\nFlutter/app.flx\nFlutter/app.zip\nFlutter/flutter_assets/\nFlutter/flutter_export_environment.sh\nServiceDefinitions.json\nRunner/GeneratedPluginRegistrant.*\n\n# Exceptions to above rules.\n!default.mode1v3\n!default.mode2v3\n!default.pbxuser\n!default.perspectivev3\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ios/Flutter/AppFrameworkInfo.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n  <key>CFBundleDevelopmentRegion</key>\n  <string>en</string>\n  <key>CFBundleExecutable</key>\n  <string>App</string>\n  <key>CFBundleIdentifier</key>\n  <string>io.flutter.flutter.app</string>\n  <key>CFBundleInfoDictionaryVersion</key>\n  <string>6.0</string>\n  <key>CFBundleName</key>\n  <string>App</string>\n  <key>CFBundlePackageType</key>\n  <string>FMWK</string>\n  <key>CFBundleShortVersionString</key>\n  <string>1.0</string>\n  <key>CFBundleSignature</key>\n  <string>????</string>\n  <key>CFBundleVersion</key>\n  <string>1.0</string>\n  <key>MinimumOSVersion</key>\n  <string>11.0</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ios/Flutter/Debug.xcconfig",
    "content": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"\n#include \"Generated.xcconfig\"\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ios/Flutter/Release.xcconfig",
    "content": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"\n#include \"Generated.xcconfig\"\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ios/Podfile",
    "content": "# Uncomment this line to define a global platform for your project\nplatform :ios, '11.0'\n\n# CocoaPods analytics sends network stats synchronously affecting flutter build latency.\nENV['COCOAPODS_DISABLE_STATS'] = 'true'\n\nproject 'Runner', {\n  'Debug' => :debug,\n  'Profile' => :release,\n  'Release' => :release,\n  'Release-dev' => :release,\n  'Release-prod' => :release,\n}\n\ndef flutter_root\n  generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'Generated.xcconfig'), __FILE__)\n  unless File.exist?(generated_xcode_build_settings_path)\n    raise \"#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure flutter pub get is executed first\"\n  end\n\n  File.foreach(generated_xcode_build_settings_path) do |line|\n    matches = line.match(/FLUTTER_ROOT\\=(.*)/)\n    return matches[1].strip if matches\n  end\n  raise \"FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Generated.xcconfig, then run flutter pub get\"\nend\n\nrequire File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)\n\nflutter_ios_podfile_setup\n\ntarget 'Runner' do\n  flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__))\nend\n\npost_install do |installer|\n  installer.pods_project.targets.each do |target|\n    flutter_additional_ios_build_settings(target)\n  end\nend\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ios/Runner/AppDelegate.h",
    "content": "#import <Flutter/Flutter.h>\n#import <UIKit/UIKit.h>\n\n@interface AppDelegate : FlutterAppDelegate\n\n@end\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ios/Runner/AppDelegate.m",
    "content": "#import \"AppDelegate.h\"\n#import \"GeneratedPluginRegistrant.h\"\n\n@implementation AppDelegate\n\n- (BOOL)application:(UIApplication *)application\n    didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {\n  [GeneratedPluginRegistrant registerWithRegistry:self];\n  // Override point for customization after application launch.\n  return [super application:application didFinishLaunchingWithOptions:launchOptions];\n}\n\n@end\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ios/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-20x20@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-20x20@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-29x29@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-29x29@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-29x29@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-40x40@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-40x40@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"60x60\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-60x60@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"60x60\",\n      \"idiom\" : \"iphone\",\n      \"filename\" : \"Icon-App-60x60@3x.png\",\n      \"scale\" : \"3x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-20x20@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"20x20\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-20x20@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-29x29@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"29x29\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-29x29@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-40x40@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"40x40\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-40x40@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"76x76\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-76x76@1x.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"76x76\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-76x76@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"83.5x83.5\",\n      \"idiom\" : \"ipad\",\n      \"filename\" : \"Icon-App-83.5x83.5@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"1024x1024\",\n      \"idiom\" : \"ios-marketing\",\n      \"filename\" : \"Icon-App-1024x1024@1x.png\",\n      \"scale\" : \"1x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ios/Runner/Assets.xcassets/LaunchImage.imageset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"LaunchImage.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"LaunchImage@2x.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"idiom\" : \"universal\",\n      \"filename\" : \"LaunchImage@3x.png\",\n      \"scale\" : \"3x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ios/Runner/Base.lproj/LaunchScreen.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"12121\" systemVersion=\"16G29\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" launchScreen=\"YES\" colorMatched=\"YES\" initialViewController=\"01J-lp-oVM\">\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"12089\"/>\n    </dependencies>\n    <scenes>\n        <!--View Controller-->\n        <scene sceneID=\"EHf-IW-A2E\">\n            <objects>\n                <viewController id=\"01J-lp-oVM\" sceneMemberID=\"viewController\">\n                    <layoutGuides>\n                        <viewControllerLayoutGuide type=\"top\" id=\"Ydg-fD-yQy\"/>\n                        <viewControllerLayoutGuide type=\"bottom\" id=\"xbc-2k-c8Z\"/>\n                    </layoutGuides>\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"Ze5-6b-2t3\">\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <subviews>\n                            <imageView opaque=\"NO\" clipsSubviews=\"YES\" multipleTouchEnabled=\"YES\" contentMode=\"center\" image=\"LaunchImage\" translatesAutoresizingMaskIntoConstraints=\"NO\" id=\"YRO-k0-Ey4\">\n                            </imageView>\n                        </subviews>\n                        <color key=\"backgroundColor\" red=\"1\" green=\"1\" blue=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"sRGB\"/>\n                        <constraints>\n                            <constraint firstItem=\"YRO-k0-Ey4\" firstAttribute=\"centerX\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"centerX\" id=\"1a2-6s-vTC\"/>\n                            <constraint firstItem=\"YRO-k0-Ey4\" firstAttribute=\"centerY\" secondItem=\"Ze5-6b-2t3\" secondAttribute=\"centerY\" id=\"4X2-HB-R7a\"/>\n                        </constraints>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"iYj-Kq-Ea1\" userLabel=\"First Responder\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n            <point key=\"canvasLocation\" x=\"53\" y=\"375\"/>\n        </scene>\n    </scenes>\n    <resources>\n        <image name=\"LaunchImage\" width=\"168\" height=\"185\"/>\n    </resources>\n</document>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ios/Runner/Base.lproj/Main.storyboard",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n<document type=\"com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB\" version=\"3.0\" toolsVersion=\"10117\" systemVersion=\"15F34\" targetRuntime=\"iOS.CocoaTouch\" propertyAccessControl=\"none\" useAutolayout=\"YES\" useTraitCollections=\"YES\" initialViewController=\"BYZ-38-t0r\">\n    <dependencies>\n        <deployment identifier=\"iOS\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.IBCocoaTouchPlugin\" version=\"10085\"/>\n    </dependencies>\n    <scenes>\n        <!--Flutter View Controller-->\n        <scene sceneID=\"tne-QT-ifu\">\n            <objects>\n                <viewController id=\"BYZ-38-t0r\" customClass=\"FlutterViewController\" sceneMemberID=\"viewController\">\n                    <layoutGuides>\n                        <viewControllerLayoutGuide type=\"top\" id=\"y3c-jy-aDJ\"/>\n                        <viewControllerLayoutGuide type=\"bottom\" id=\"wfy-db-euE\"/>\n                    </layoutGuides>\n                    <view key=\"view\" contentMode=\"scaleToFill\" id=\"8bC-Xf-vdC\">\n                        <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"600\" height=\"600\"/>\n                        <autoresizingMask key=\"autoresizingMask\" widthSizable=\"YES\" heightSizable=\"YES\"/>\n                        <color key=\"backgroundColor\" white=\"1\" alpha=\"1\" colorSpace=\"custom\" customColorSpace=\"calibratedWhite\"/>\n                    </view>\n                </viewController>\n                <placeholder placeholderIdentifier=\"IBFirstResponder\" id=\"dkx-z0-nzr\" sceneMemberID=\"firstResponder\"/>\n            </objects>\n        </scene>\n    </scenes>\n</document>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ios/Runner/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleDisplayName</key>\n\t<string>Multiple Flavors</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(BUNDLE_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(FLUTTER_BUILD_NAME)</string>\n\t<key>CFBundleSignature</key>\n\t<string>????</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(FLUTTER_BUILD_NUMBER)</string>\n\t<key>LSRequiresIPhoneOS</key>\n\t<true/>\n\t<key>UILaunchStoryboardName</key>\n\t<string>LaunchScreen</string>\n\t<key>UIMainStoryboardFile</key>\n\t<string>Main</string>\n\t<key>UISupportedInterfaceOrientations</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UISupportedInterfaceOrientations~ipad</key>\n\t<array>\n\t\t<string>UIInterfaceOrientationPortrait</string>\n\t\t<string>UIInterfaceOrientationPortraitUpsideDown</string>\n\t\t<string>UIInterfaceOrientationLandscapeLeft</string>\n\t\t<string>UIInterfaceOrientationLandscapeRight</string>\n\t</array>\n\t<key>UIViewControllerBasedStatusBarAppearance</key>\n\t<false/>\n\t<key>CADisableMinimumFrameDurationOnPhone</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ios/Runner/main.m",
    "content": "#import <Flutter/Flutter.h>\n#import <UIKit/UIKit.h>\n#import \"AppDelegate.h\"\n\nint main(int argc, char* argv[]) {\n  @autoreleasepool {\n    return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ios/Runner.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 50;\n\tobjects = {\n\n/* Begin PBXBuildFile section */\n\t\t1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; };\n\t\t1CAEDD5155F2EB163DB44156 /* libPods-Runner.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E9ECD0077873D16742718AF3 /* libPods-Runner.a */; };\n\t\t3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; };\n\t\t978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */ = {isa = PBXBuildFile; fileRef = 7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */; };\n\t\t97C146F31CF9000F007C117D /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 97C146F21CF9000F007C117D /* main.m */; };\n\t\t97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; };\n\t\t97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; };\n\t\t97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\t9705A1C41CF9048500538489 /* Embed Frameworks */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 10;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tname = \"Embed Frameworks\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\t0C88C752B959FEAD3300AB0E /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.release.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = \"<group>\"; };\n\t\t1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = \"<group>\"; };\n\t\t23EA6A7DCC1D9B995BBF4360 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.profile.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t2CA062782B731FA3340C7B07 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.debug.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t30E41D6617A08ACBFA85E15F /* Pods-Runner.release-prod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.release-prod.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.release-prod.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = \"<group>\"; };\n\t\t6A9AA014DF44CAC71B1DBFDE /* Pods-Runner.release-dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.release-dev.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.release-dev.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = \"<group>\"; };\n\t\t7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = \"<group>\"; };\n\t\t7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = AppDelegate.m; sourceTree = \"<group>\"; };\n\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = \"<group>\"; };\n\t\t9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = \"<group>\"; };\n\t\t97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t97C146F21CF9000F007C117D /* main.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = main.m; sourceTree = \"<group>\"; };\n\t\t97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = \"<group>\"; };\n\t\t97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = \"<group>\"; };\n\t\t97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = \"<group>\"; };\n\t\tE9ECD0077873D16742718AF3 /* libPods-Runner.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = \"libPods-Runner.a\"; sourceTree = BUILT_PRODUCTS_DIR; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t97C146EB1CF9000F007C117D /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t1CAEDD5155F2EB163DB44156 /* libPods-Runner.a in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t9740EEB11CF90186004384FC /* Flutter */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */,\n\t\t\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */,\n\t\t\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */,\n\t\t\t\t9740EEB31CF90195004384FC /* Generated.xcconfig */,\n\t\t\t);\n\t\t\tname = Flutter;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146E51CF9000F007C117D = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t9740EEB11CF90186004384FC /* Flutter */,\n\t\t\t\t97C146F01CF9000F007C117D /* Runner */,\n\t\t\t\t97C146EF1CF9000F007C117D /* Products */,\n\t\t\t\tA2C8F672AF56CBDA2D1C04AD /* Pods */,\n\t\t\t\tB011BCA6C13E4EB47B215E83 /* Frameworks */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146EF1CF9000F007C117D /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t97C146EE1CF9000F007C117D /* Runner.app */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146F01CF9000F007C117D /* Runner */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t7AFFD8ED1D35381100E5BB4D /* AppDelegate.h */,\n\t\t\t\t7AFFD8EE1D35381100E5BB4D /* AppDelegate.m */,\n\t\t\t\t97C146FA1CF9000F007C117D /* Main.storyboard */,\n\t\t\t\t97C146FD1CF9000F007C117D /* Assets.xcassets */,\n\t\t\t\t97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */,\n\t\t\t\t97C147021CF9000F007C117D /* Info.plist */,\n\t\t\t\t97C146F11CF9000F007C117D /* Supporting Files */,\n\t\t\t\t1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */,\n\t\t\t\t1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */,\n\t\t\t);\n\t\t\tpath = Runner;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146F11CF9000F007C117D /* Supporting Files */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t97C146F21CF9000F007C117D /* main.m */,\n\t\t\t);\n\t\t\tname = \"Supporting Files\";\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tA2C8F672AF56CBDA2D1C04AD /* Pods */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t2CA062782B731FA3340C7B07 /* Pods-Runner.debug.xcconfig */,\n\t\t\t\t0C88C752B959FEAD3300AB0E /* Pods-Runner.release.xcconfig */,\n\t\t\t\t23EA6A7DCC1D9B995BBF4360 /* Pods-Runner.profile.xcconfig */,\n\t\t\t\t30E41D6617A08ACBFA85E15F /* Pods-Runner.release-prod.xcconfig */,\n\t\t\t\t6A9AA014DF44CAC71B1DBFDE /* Pods-Runner.release-dev.xcconfig */,\n\t\t\t);\n\t\t\tpath = Pods;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tB011BCA6C13E4EB47B215E83 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tE9ECD0077873D16742718AF3 /* libPods-Runner.a */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t97C146ED1CF9000F007C117D /* Runner */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget \"Runner\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t795BBEB30B39DB26BF559A5B /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t9740EEB61CF901F6004384FC /* Run Script */,\n\t\t\t\t97C146EA1CF9000F007C117D /* Sources */,\n\t\t\t\t97C146EB1CF9000F007C117D /* Frameworks */,\n\t\t\t\t97C146EC1CF9000F007C117D /* Resources */,\n\t\t\t\t9705A1C41CF9048500538489 /* Embed Frameworks */,\n\t\t\t\t3B06AD1E1E4923F5004D2608 /* Thin Binary */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = Runner;\n\t\t\tproductName = Runner;\n\t\t\tproductReference = 97C146EE1CF9000F007C117D /* Runner.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t97C146E61CF9000F007C117D /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tLastUpgradeCheck = 1300;\n\t\t\t\tORGANIZATIONNAME = \"\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t97C146ED1CF9000F007C117D = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 7.3.1;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 97C146E91CF9000F007C117D /* Build configuration list for PBXProject \"Runner\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 97C146E51CF9000F007C117D;\n\t\t\tproductRefGroup = 97C146EF1CF9000F007C117D /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t97C146ED1CF9000F007C117D /* Runner */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t97C146EC1CF9000F007C117D /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */,\n\t\t\t\t3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */,\n\t\t\t\t97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */,\n\t\t\t\t97C146FC1CF9000F007C117D /* Main.storyboard in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t3B06AD1E1E4923F5004D2608 /* Thin Binary */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Thin Binary\";\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"/bin/sh \\\"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\\\" embed_and_thin\";\n\t\t};\n\t\t795BBEB30B39DB26BF559A5B /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\t9740EEB61CF901F6004384FC /* Run Script */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\tname = \"Run Script\";\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"/bin/sh \\\"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\\\" build\";\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t97C146EA1CF9000F007C117D /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t978B8F6F1D3862AE00F588F7 /* AppDelegate.m in Sources */,\n\t\t\t\t97C146F31CF9000F007C117D /* main.m in Sources */,\n\t\t\t\t1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXVariantGroup section */\n\t\t97C146FA1CF9000F007C117D /* Main.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t97C146FB1CF9000F007C117D /* Base */,\n\t\t\t);\n\t\t\tname = Main.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t97C147001CF9000F007C117D /* Base */,\n\t\t\t);\n\t\t\tname = LaunchScreen.storyboard;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t249021D3217E4FDB00AE95B9 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 11.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSUPPORTED_PLATFORMS = iphoneos;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t249021D4217E4FDB00AE95B9 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tDEVELOPMENT_TEAM = G83H824X6L;\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.leanflutter.examples.multipleFlavors;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t97C147031CF9000F007C117D /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 11.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t97C147041CF9000F007C117D /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 11.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSUPPORTED_PLATFORMS = iphoneos;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t97C147061CF9000F007C117D /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tDEVELOPMENT_TEAM = G83H824X6L;\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.leanflutter.examples.multipleFlavors;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t97C147071CF9000F007C117D /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tDEVELOPMENT_TEAM = G83H824X6L;\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.leanflutter.examples.multipleFlavors;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tF55ED2D2280AD5CC003CBF8D /* Release-prod */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 11.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSUPPORTED_PLATFORMS = iphoneos;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = \"Release-prod\";\n\t\t};\n\t\tF55ED2D3280AD5CC003CBF8D /* Release-prod */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tDEVELOPMENT_TEAM = G83H824X6L;\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.leanflutter.examples.multipleFlavors;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = \"Release-prod\";\n\t\t};\n\t\tF55ED2D4280AD5D5003CBF8D /* Release-dev */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++0x\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_COMMA = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_STRICT_PROTOTYPES = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCLANG_WARN_UNREACHABLE_CODE = YES;\n\t\t\t\tCLANG_WARN__DUPLICATE_METHOD_MATCH = YES;\n\t\t\t\t\"CODE_SIGN_IDENTITY[sdk=iphoneos*]\" = \"iPhone Developer\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu99;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNDECLARED_SELECTOR = YES;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tIPHONEOS_DEPLOYMENT_TARGET = 11.0;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = iphoneos;\n\t\t\t\tSUPPORTED_PLATFORMS = iphoneos;\n\t\t\t\tTARGETED_DEVICE_FAMILY = \"1,2\";\n\t\t\t\tVALIDATE_PRODUCT = YES;\n\t\t\t};\n\t\t\tname = \"Release-dev\";\n\t\t};\n\t\tF55ED2D5280AD5D5003CBF8D /* Release-dev */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCURRENT_PROJECT_VERSION = \"$(FLUTTER_BUILD_NUMBER)\";\n\t\t\t\tDEVELOPMENT_TEAM = G83H824X6L;\n\t\t\t\tENABLE_BITCODE = NO;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/Frameworks\",\n\t\t\t\t);\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.leanflutter.examples.multipleFlavors;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tVERSIONING_SYSTEM = \"apple-generic\";\n\t\t\t};\n\t\t\tname = \"Release-dev\";\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t97C146E91CF9000F007C117D /* Build configuration list for PBXProject \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t97C147031CF9000F007C117D /* Debug */,\n\t\t\t\t97C147041CF9000F007C117D /* Release */,\n\t\t\t\tF55ED2D4280AD5D5003CBF8D /* Release-dev */,\n\t\t\t\tF55ED2D2280AD5CC003CBF8D /* Release-prod */,\n\t\t\t\t249021D3217E4FDB00AE95B9 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t97C147061CF9000F007C117D /* Debug */,\n\t\t\t\t97C147071CF9000F007C117D /* Release */,\n\t\t\t\tF55ED2D5280AD5D5003CBF8D /* Release-dev */,\n\t\t\t\tF55ED2D3280AD5CC003CBF8D /* Release-prod */,\n\t\t\t\t249021D4217E4FDB00AE95B9 /* Profile */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 97C146E61CF9000F007C117D /* Project object */;\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ios/Runner.xcodeproj/project.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"self:\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ios/Runner.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>PreviewsEnabled</key>\n\t<false/>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ios/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1300\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n               BuildableName = \"Runner.app\"\n               BlueprintName = \"Runner\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Profile\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ios/Runner.xcodeproj/xcshareddata/xcschemes/dev.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1300\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n               BuildableName = \"Runner.app\"\n               BlueprintName = \"Runner\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Profile\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release-dev\"\n      customArchiveName = \"multiple_flavors_dev\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ios/Runner.xcodeproj/xcshareddata/xcschemes/prod.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1300\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n               BuildableName = \"Runner.app\"\n               BlueprintName = \"Runner\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n      <Testables>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Profile\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"97C146ED1CF9000F007C117D\"\n            BuildableName = \"Runner.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"prod_Release\"\n      customArchiveName = \"multiple_flavors_prod\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ios/Runner.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:Runner.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:Pods/Pods.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>PreviewsEnabled</key>\n\t<false/>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ios/dev_ExportOptions.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>compileBitcode</key>\n\t<true/>\n\t<key>method</key>\n\t<string>ad-hoc</string>\n\t<key>signingStyle</key>\n\t<string>automatic</string>\n\t<key>stripSwiftSymbols</key>\n\t<true/>\n\t<key>teamID</key>\n\t<string>G83H824X6L</string>\n\t<key>thinning</key>\n\t<string>&lt;none&gt;</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ios/prod_ExportOptions.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>compileBitcode</key>\n\t<true/>\n\t<key>method</key>\n\t<string>ad-hoc</string>\n\t<key>signingStyle</key>\n\t<string>automatic</string>\n\t<key>stripSwiftSymbols</key>\n\t<true/>\n\t<key>teamID</key>\n\t<string>G83H824X6L</string>\n\t<key>thinning</key>\n\t<string>&lt;none&gt;</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/lib/main.dart",
    "content": "import 'package:flutter/material.dart';\n\nvoid main() {\n  runApp(const MyApp());\n}\n\nclass MyApp extends StatelessWidget {\n  const MyApp({Key? key}) : super(key: key);\n\n  // This widget is the root of your application.\n  @override\n  Widget build(BuildContext context) {\n    return MaterialApp(\n      title: 'Flutter Demo',\n      theme: ThemeData(\n        // This is the theme of your application.\n        //\n        // Try running your application with \"flutter run\". You'll see the\n        // application has a blue toolbar. Then, without quitting the app, try\n        // changing the primarySwatch below to Colors.green and then invoke\n        // \"hot reload\" (press \"r\" in the console where you ran \"flutter run\",\n        // or simply save your changes to \"hot reload\" in a Flutter IDE).\n        // Notice that the counter didn't reset back to zero; the application\n        // is not restarted.\n        primarySwatch: Colors.blue,\n      ),\n      home: const MyHomePage(title: 'Flutter Demo Home Page'),\n    );\n  }\n}\n\nclass MyHomePage extends StatefulWidget {\n  const MyHomePage({Key? key, required this.title}) : super(key: key);\n\n  // This widget is the home page of your application. It is stateful, meaning\n  // that it has a State object (defined below) that contains fields that affect\n  // how it looks.\n\n  // This class is the configuration for the state. It holds the values (in this\n  // case the title) provided by the parent (in this case the App widget) and\n  // used by the build method of the State. Fields in a Widget subclass are\n  // always marked \"final\".\n\n  final String title;\n\n  @override\n  State<MyHomePage> createState() => _MyHomePageState();\n}\n\nclass _MyHomePageState extends State<MyHomePage> {\n  int _counter = 0;\n\n  void _incrementCounter() {\n    setState(() {\n      // This call to setState tells the Flutter framework that something has\n      // changed in this State, which causes it to rerun the build method below\n      // so that the display can reflect the updated values. If we changed\n      // _counter without calling setState(), then the build method would not be\n      // called again, and so nothing would appear to happen.\n      _counter++;\n    });\n  }\n\n  @override\n  Widget build(BuildContext context) {\n    // This method is rerun every time setState is called, for instance as done\n    // by the _incrementCounter method above.\n    //\n    // The Flutter framework has been optimized to make rerunning build methods\n    // fast, so that you can just rebuild anything that needs updating rather\n    // than having to individually change instances of widgets.\n    return Scaffold(\n      appBar: AppBar(\n        // Here we take the value from the MyHomePage object that was created by\n        // the App.build method, and use it to set our appbar title.\n        title: Text(widget.title),\n      ),\n      body: Center(\n        // Center is a layout widget. It takes a single child and positions it\n        // in the middle of the parent.\n        child: Column(\n          // Column is also a layout widget. It takes a list of children and\n          // arranges them vertically. By default, it sizes itself to fit its\n          // children horizontally, and tries to be as tall as its parent.\n          //\n          // Invoke \"debug painting\" (press \"p\" in the console, choose the\n          // \"Toggle Debug Paint\" action from the Flutter Inspector in Android\n          // Studio, or the \"Toggle Debug Paint\" command in Visual Studio Code)\n          // to see the wireframe for each widget.\n          //\n          // Column has various properties to control how it sizes itself and\n          // how it positions its children. Here we use mainAxisAlignment to\n          // center the children vertically; the main axis here is the vertical\n          // axis because Columns are vertical (the cross axis would be\n          // horizontal).\n          mainAxisAlignment: MainAxisAlignment.center,\n          children: <Widget>[\n            const Text(\n              'You have pushed the button this many times:',\n            ),\n            Text(\n              '$_counter',\n              style: Theme.of(context).textTheme.headlineMedium,\n            ),\n          ],\n        ),\n      ),\n      floatingActionButton: FloatingActionButton(\n        onPressed: _incrementCounter,\n        tooltip: 'Increment',\n        child: const Icon(Icons.add),\n      ), // This trailing comma makes auto-formatting nicer for build methods.\n    );\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/macos/.gitignore",
    "content": "# Flutter-related\n**/Flutter/ephemeral/\n**/Pods/\n\n# Xcode-related\n**/dgph\n**/xcuserdata/\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/macos/Flutter/Flutter-Debug.xcconfig",
    "content": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"\n#include \"ephemeral/Flutter-Generated.xcconfig\"\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/macos/Flutter/Flutter-Release.xcconfig",
    "content": "#include? \"Pods/Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"\n#include \"ephemeral/Flutter-Generated.xcconfig\"\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/macos/Flutter/GeneratedPluginRegistrant.swift",
    "content": "//\n//  Generated file. Do not edit.\n//\n\nimport FlutterMacOS\nimport Foundation\n\nimport package_info_plus\n\nfunc RegisterGeneratedPlugins(registry: FlutterPluginRegistry) {\n  FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: \"FPPPackageInfoPlusPlugin\"))\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/macos/Podfile",
    "content": "platform :osx, '10.14'\n\n# CocoaPods analytics sends network stats synchronously affecting flutter build latency.\nENV['COCOAPODS_DISABLE_STATS'] = 'true'\n\nproject 'Runner', {\n  'Debug' => :debug,\n  'Profile' => :release,\n  'Release' => :release,\n}\n\ndef flutter_root\n  generated_xcode_build_settings_path = File.expand_path(File.join('..', 'Flutter', 'ephemeral', 'Flutter-Generated.xcconfig'), __FILE__)\n  unless File.exist?(generated_xcode_build_settings_path)\n    raise \"#{generated_xcode_build_settings_path} must exist. If you're running pod install manually, make sure \\\"flutter pub get\\\" is executed first\"\n  end\n\n  File.foreach(generated_xcode_build_settings_path) do |line|\n    matches = line.match(/FLUTTER_ROOT\\=(.*)/)\n    return matches[1].strip if matches\n  end\n  raise \"FLUTTER_ROOT not found in #{generated_xcode_build_settings_path}. Try deleting Flutter-Generated.xcconfig, then run \\\"flutter pub get\\\"\"\nend\n\nrequire File.expand_path(File.join('packages', 'flutter_tools', 'bin', 'podhelper'), flutter_root)\n\nflutter_macos_podfile_setup\n\ntarget 'Runner' do\n  use_frameworks!\n  use_modular_headers!\n\n  flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__))\n  target 'RunnerTests' do\n    inherit! :search_paths\n  end\nend\n\npost_install do |installer|\n  installer.pods_project.targets.each do |target|\n    flutter_additional_macos_build_settings(target)\n  end\nend\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/macos/Runner/AppDelegate.swift",
    "content": "import Cocoa\nimport FlutterMacOS\n\n@NSApplicationMain\nclass AppDelegate: FlutterAppDelegate {\n  override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool {\n    return true\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/macos/Runner/Assets.xcassets/AppIcon.appiconset/Contents.json",
    "content": "{\n  \"images\" : [\n    {\n      \"size\" : \"16x16\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_16.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"16x16\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_32.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"32x32\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_32.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"32x32\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_64.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"128x128\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_128.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"128x128\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_256.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"256x256\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_256.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"256x256\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_512.png\",\n      \"scale\" : \"2x\"\n    },\n    {\n      \"size\" : \"512x512\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_512.png\",\n      \"scale\" : \"1x\"\n    },\n    {\n      \"size\" : \"512x512\",\n      \"idiom\" : \"mac\",\n      \"filename\" : \"app_icon_1024.png\",\n      \"scale\" : \"2x\"\n    }\n  ],\n  \"info\" : {\n    \"version\" : 1,\n    \"author\" : \"xcode\"\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/macos/Runner/Base.lproj/MainMenu.xib",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<document type=\"com.apple.InterfaceBuilder3.Cocoa.XIB\" version=\"3.0\" toolsVersion=\"14490.70\" targetRuntime=\"MacOSX.Cocoa\" propertyAccessControl=\"none\" useAutolayout=\"YES\" customObjectInstantitationMethod=\"direct\">\n    <dependencies>\n        <deployment identifier=\"macosx\"/>\n        <plugIn identifier=\"com.apple.InterfaceBuilder.CocoaPlugin\" version=\"14490.70\"/>\n        <capability name=\"documents saved in the Xcode 8 format\" minToolsVersion=\"8.0\"/>\n    </dependencies>\n    <objects>\n        <customObject id=\"-2\" userLabel=\"File's Owner\" customClass=\"NSApplication\">\n            <connections>\n                <outlet property=\"delegate\" destination=\"Voe-Tx-rLC\" id=\"GzC-gU-4Uq\"/>\n            </connections>\n        </customObject>\n        <customObject id=\"-1\" userLabel=\"First Responder\" customClass=\"FirstResponder\"/>\n        <customObject id=\"-3\" userLabel=\"Application\" customClass=\"NSObject\"/>\n        <customObject id=\"Voe-Tx-rLC\" customClass=\"AppDelegate\" customModule=\"Runner\" customModuleProvider=\"target\">\n            <connections>\n                <outlet property=\"applicationMenu\" destination=\"uQy-DD-JDr\" id=\"XBo-yE-nKs\"/>\n                <outlet property=\"mainFlutterWindow\" destination=\"QvC-M9-y7g\" id=\"gIp-Ho-8D9\"/>\n            </connections>\n        </customObject>\n        <customObject id=\"YLy-65-1bz\" customClass=\"NSFontManager\"/>\n        <menu title=\"Main Menu\" systemMenu=\"main\" id=\"AYu-sK-qS6\">\n            <items>\n                <menuItem title=\"APP_NAME\" id=\"1Xt-HY-uBw\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"APP_NAME\" systemMenu=\"apple\" id=\"uQy-DD-JDr\">\n                        <items>\n                            <menuItem title=\"About APP_NAME\" id=\"5kV-Vb-QxS\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"orderFrontStandardAboutPanel:\" target=\"-1\" id=\"Exp-CZ-Vem\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"VOq-y0-SEH\"/>\n                            <menuItem title=\"Preferences…\" keyEquivalent=\",\" id=\"BOF-NM-1cW\"/>\n                            <menuItem isSeparatorItem=\"YES\" id=\"wFC-TO-SCJ\"/>\n                            <menuItem title=\"Services\" id=\"NMo-om-nkz\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Services\" systemMenu=\"services\" id=\"hz9-B4-Xy5\"/>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"4je-JR-u6R\"/>\n                            <menuItem title=\"Hide APP_NAME\" keyEquivalent=\"h\" id=\"Olw-nP-bQN\">\n                                <connections>\n                                    <action selector=\"hide:\" target=\"-1\" id=\"PnN-Uc-m68\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Hide Others\" keyEquivalent=\"h\" id=\"Vdr-fp-XzO\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"hideOtherApplications:\" target=\"-1\" id=\"VT4-aY-XCT\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Show All\" id=\"Kd2-mp-pUS\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"unhideAllApplications:\" target=\"-1\" id=\"Dhg-Le-xox\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"kCx-OE-vgT\"/>\n                            <menuItem title=\"Quit APP_NAME\" keyEquivalent=\"q\" id=\"4sb-4s-VLi\">\n                                <connections>\n                                    <action selector=\"terminate:\" target=\"-1\" id=\"Te7-pn-YzF\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"Edit\" id=\"5QF-Oa-p0T\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"Edit\" id=\"W48-6f-4Dl\">\n                        <items>\n                            <menuItem title=\"Undo\" keyEquivalent=\"z\" id=\"dRJ-4n-Yzg\">\n                                <connections>\n                                    <action selector=\"undo:\" target=\"-1\" id=\"M6e-cu-g7V\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Redo\" keyEquivalent=\"Z\" id=\"6dh-zS-Vam\">\n                                <connections>\n                                    <action selector=\"redo:\" target=\"-1\" id=\"oIA-Rs-6OD\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"WRV-NI-Exz\"/>\n                            <menuItem title=\"Cut\" keyEquivalent=\"x\" id=\"uRl-iY-unG\">\n                                <connections>\n                                    <action selector=\"cut:\" target=\"-1\" id=\"YJe-68-I9s\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Copy\" keyEquivalent=\"c\" id=\"x3v-GG-iWU\">\n                                <connections>\n                                    <action selector=\"copy:\" target=\"-1\" id=\"G1f-GL-Joy\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Paste\" keyEquivalent=\"v\" id=\"gVA-U4-sdL\">\n                                <connections>\n                                    <action selector=\"paste:\" target=\"-1\" id=\"UvS-8e-Qdg\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Paste and Match Style\" keyEquivalent=\"V\" id=\"WeT-3V-zwk\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"pasteAsPlainText:\" target=\"-1\" id=\"cEh-KX-wJQ\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Delete\" id=\"pa3-QI-u2k\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"delete:\" target=\"-1\" id=\"0Mk-Ml-PaM\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Select All\" keyEquivalent=\"a\" id=\"Ruw-6m-B2m\">\n                                <connections>\n                                    <action selector=\"selectAll:\" target=\"-1\" id=\"VNm-Mi-diN\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"uyl-h8-XO2\"/>\n                            <menuItem title=\"Find\" id=\"4EN-yA-p0u\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Find\" id=\"1b7-l0-nxx\">\n                                    <items>\n                                        <menuItem title=\"Find…\" tag=\"1\" keyEquivalent=\"f\" id=\"Xz5-n4-O0W\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"cD7-Qs-BN4\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Find and Replace…\" tag=\"12\" keyEquivalent=\"f\" id=\"YEy-JH-Tfz\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\" option=\"YES\" command=\"YES\"/>\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"WD3-Gg-5AJ\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Find Next\" tag=\"2\" keyEquivalent=\"g\" id=\"q09-fT-Sye\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"NDo-RZ-v9R\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Find Previous\" tag=\"3\" keyEquivalent=\"G\" id=\"OwM-mh-QMV\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"HOh-sY-3ay\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Use Selection for Find\" tag=\"7\" keyEquivalent=\"e\" id=\"buJ-ug-pKt\">\n                                            <connections>\n                                                <action selector=\"performFindPanelAction:\" target=\"-1\" id=\"U76-nv-p5D\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Jump to Selection\" keyEquivalent=\"j\" id=\"S0p-oC-mLd\">\n                                            <connections>\n                                                <action selector=\"centerSelectionInVisibleArea:\" target=\"-1\" id=\"IOG-6D-g5B\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Spelling and Grammar\" id=\"Dv1-io-Yv7\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Spelling\" id=\"3IN-sU-3Bg\">\n                                    <items>\n                                        <menuItem title=\"Show Spelling and Grammar\" keyEquivalent=\":\" id=\"HFo-cy-zxI\">\n                                            <connections>\n                                                <action selector=\"showGuessPanel:\" target=\"-1\" id=\"vFj-Ks-hy3\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Check Document Now\" keyEquivalent=\";\" id=\"hz2-CU-CR7\">\n                                            <connections>\n                                                <action selector=\"checkSpelling:\" target=\"-1\" id=\"fz7-VC-reM\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"bNw-od-mp5\"/>\n                                        <menuItem title=\"Check Spelling While Typing\" id=\"rbD-Rh-wIN\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleContinuousSpellChecking:\" target=\"-1\" id=\"7w6-Qz-0kB\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Check Grammar With Spelling\" id=\"mK6-2p-4JG\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleGrammarChecking:\" target=\"-1\" id=\"muD-Qn-j4w\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Correct Spelling Automatically\" id=\"78Y-hA-62v\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticSpellingCorrection:\" target=\"-1\" id=\"2lM-Qi-WAP\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Substitutions\" id=\"9ic-FL-obx\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Substitutions\" id=\"FeM-D8-WVr\">\n                                    <items>\n                                        <menuItem title=\"Show Substitutions\" id=\"z6F-FW-3nz\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"orderFrontSubstitutionsPanel:\" target=\"-1\" id=\"oku-mr-iSq\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem isSeparatorItem=\"YES\" id=\"gPx-C9-uUO\"/>\n                                        <menuItem title=\"Smart Copy/Paste\" id=\"9yt-4B-nSM\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleSmartInsertDelete:\" target=\"-1\" id=\"3IJ-Se-DZD\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smart Quotes\" id=\"hQb-2v-fYv\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticQuoteSubstitution:\" target=\"-1\" id=\"ptq-xd-QOA\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smart Dashes\" id=\"rgM-f4-ycn\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticDashSubstitution:\" target=\"-1\" id=\"oCt-pO-9gS\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Smart Links\" id=\"cwL-P1-jid\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticLinkDetection:\" target=\"-1\" id=\"Gip-E3-Fov\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Data Detectors\" id=\"tRr-pd-1PS\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticDataDetection:\" target=\"-1\" id=\"R1I-Nq-Kbl\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Text Replacement\" id=\"HFQ-gK-NFA\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"toggleAutomaticTextReplacement:\" target=\"-1\" id=\"DvP-Fe-Py6\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Transformations\" id=\"2oI-Rn-ZJC\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Transformations\" id=\"c8a-y6-VQd\">\n                                    <items>\n                                        <menuItem title=\"Make Upper Case\" id=\"vmV-6d-7jI\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"uppercaseWord:\" target=\"-1\" id=\"sPh-Tk-edu\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Make Lower Case\" id=\"d9M-CD-aMd\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"lowercaseWord:\" target=\"-1\" id=\"iUZ-b5-hil\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Capitalize\" id=\"UEZ-Bs-lqG\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"capitalizeWord:\" target=\"-1\" id=\"26H-TL-nsh\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                            <menuItem title=\"Speech\" id=\"xrE-MZ-jX0\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <menu key=\"submenu\" title=\"Speech\" id=\"3rS-ZA-NoH\">\n                                    <items>\n                                        <menuItem title=\"Start Speaking\" id=\"Ynk-f8-cLZ\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"startSpeaking:\" target=\"-1\" id=\"654-Ng-kyl\"/>\n                                            </connections>\n                                        </menuItem>\n                                        <menuItem title=\"Stop Speaking\" id=\"Oyz-dy-DGm\">\n                                            <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                            <connections>\n                                                <action selector=\"stopSpeaking:\" target=\"-1\" id=\"dX8-6p-jy9\"/>\n                                            </connections>\n                                        </menuItem>\n                                    </items>\n                                </menu>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"View\" id=\"H8h-7b-M4v\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"View\" id=\"HyV-fh-RgO\">\n                        <items>\n                            <menuItem title=\"Enter Full Screen\" keyEquivalent=\"f\" id=\"4J7-dP-txa\">\n                                <modifierMask key=\"keyEquivalentModifierMask\" control=\"YES\" command=\"YES\"/>\n                                <connections>\n                                    <action selector=\"toggleFullScreen:\" target=\"-1\" id=\"dU3-MA-1Rq\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"Window\" id=\"aUF-d1-5bR\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"Window\" systemMenu=\"window\" id=\"Td7-aD-5lo\">\n                        <items>\n                            <menuItem title=\"Minimize\" keyEquivalent=\"m\" id=\"OY7-WF-poV\">\n                                <connections>\n                                    <action selector=\"performMiniaturize:\" target=\"-1\" id=\"VwT-WD-YPe\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem title=\"Zoom\" id=\"R4o-n2-Eq4\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"performZoom:\" target=\"-1\" id=\"DIl-cC-cCs\"/>\n                                </connections>\n                            </menuItem>\n                            <menuItem isSeparatorItem=\"YES\" id=\"eu3-7i-yIM\"/>\n                            <menuItem title=\"Bring All to Front\" id=\"LE2-aR-0XJ\">\n                                <modifierMask key=\"keyEquivalentModifierMask\"/>\n                                <connections>\n                                    <action selector=\"arrangeInFront:\" target=\"-1\" id=\"DRN-fu-gQh\"/>\n                                </connections>\n                            </menuItem>\n                        </items>\n                    </menu>\n                </menuItem>\n                <menuItem title=\"Help\" id=\"EPT-qC-fAb\">\n                    <modifierMask key=\"keyEquivalentModifierMask\"/>\n                    <menu key=\"submenu\" title=\"Help\" systemMenu=\"help\" id=\"rJ0-wn-3NY\"/>\n                </menuItem>\n            </items>\n            <point key=\"canvasLocation\" x=\"142\" y=\"-258\"/>\n        </menu>\n        <window title=\"APP_NAME\" allowsToolTipsWhenApplicationIsInactive=\"NO\" autorecalculatesKeyViewLoop=\"NO\" releasedWhenClosed=\"NO\" animationBehavior=\"default\" id=\"QvC-M9-y7g\" customClass=\"MainFlutterWindow\" customModule=\"Runner\" customModuleProvider=\"target\">\n            <windowStyleMask key=\"styleMask\" titled=\"YES\" closable=\"YES\" miniaturizable=\"YES\" resizable=\"YES\"/>\n            <rect key=\"contentRect\" x=\"335\" y=\"390\" width=\"800\" height=\"600\"/>\n            <rect key=\"screenRect\" x=\"0.0\" y=\"0.0\" width=\"2560\" height=\"1577\"/>\n            <view key=\"contentView\" wantsLayer=\"YES\" id=\"EiT-Mj-1SZ\">\n                <rect key=\"frame\" x=\"0.0\" y=\"0.0\" width=\"800\" height=\"600\"/>\n                <autoresizingMask key=\"autoresizingMask\"/>\n            </view>\n        </window>\n    </objects>\n</document>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/macos/Runner/Configs/AppInfo.xcconfig",
    "content": "// Application-level settings for the Runner target.\n//\n// This may be replaced with something auto-generated from metadata (e.g., pubspec.yaml) in the\n// future. If not, the values below would default to using the project name when this becomes a\n// 'flutter create' template.\n\n// The application's name. By default this is also the title of the Flutter window.\nPRODUCT_NAME = multiple_flavors\n\n// The application's bundle identifier\nPRODUCT_BUNDLE_IDENTIFIER = org.leanflutter.examples.multipleFlavors\n\n// The copyright displayed in application information\nPRODUCT_COPYRIGHT = Copyright © 2024 org.leanflutter.examples. All rights reserved.\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/macos/Runner/Configs/Debug.xcconfig",
    "content": "#include \"../../Flutter/Flutter-Debug.xcconfig\"\n#include \"Warnings.xcconfig\"\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/macos/Runner/Configs/Release.xcconfig",
    "content": "#include \"../../Flutter/Flutter-Release.xcconfig\"\n#include \"Warnings.xcconfig\"\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/macos/Runner/Configs/Warnings.xcconfig",
    "content": "WARNING_CFLAGS = -Wall -Wconditional-uninitialized -Wnullable-to-nonnull-conversion -Wmissing-method-return-type -Woverlength-strings\nGCC_WARN_UNDECLARED_SELECTOR = YES\nCLANG_UNDEFINED_BEHAVIOR_SANITIZER_NULLABILITY = YES\nCLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE\nCLANG_WARN__DUPLICATE_METHOD_MATCH = YES\nCLANG_WARN_PRAGMA_PACK = YES\nCLANG_WARN_STRICT_PROTOTYPES = YES\nCLANG_WARN_COMMA = YES\nGCC_WARN_STRICT_SELECTOR_MATCH = YES\nCLANG_WARN_OBJC_REPEATED_USE_OF_WEAK = YES\nCLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES\nGCC_WARN_SHADOW = YES\nCLANG_WARN_UNREACHABLE_CODE = YES\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/macos/Runner/DebugProfile.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.app-sandbox</key>\n\t<true/>\n\t<key>com.apple.security.cs.allow-jit</key>\n\t<true/>\n\t<key>com.apple.security.network.server</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/macos/Runner/Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>CFBundleDevelopmentRegion</key>\n\t<string>$(DEVELOPMENT_LANGUAGE)</string>\n\t<key>CFBundleExecutable</key>\n\t<string>$(EXECUTABLE_NAME)</string>\n\t<key>CFBundleIconFile</key>\n\t<string></string>\n\t<key>CFBundleIdentifier</key>\n\t<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>\n\t<key>CFBundleInfoDictionaryVersion</key>\n\t<string>6.0</string>\n\t<key>CFBundleName</key>\n\t<string>$(PRODUCT_NAME)</string>\n\t<key>CFBundlePackageType</key>\n\t<string>APPL</string>\n\t<key>CFBundleShortVersionString</key>\n\t<string>$(FLUTTER_BUILD_NAME)</string>\n\t<key>CFBundleVersion</key>\n\t<string>$(FLUTTER_BUILD_NUMBER)</string>\n\t<key>LSMinimumSystemVersion</key>\n\t<string>$(MACOSX_DEPLOYMENT_TARGET)</string>\n\t<key>NSHumanReadableCopyright</key>\n\t<string>$(PRODUCT_COPYRIGHT)</string>\n\t<key>NSMainNibFile</key>\n\t<string>MainMenu</string>\n\t<key>NSPrincipalClass</key>\n\t<string>NSApplication</string>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/macos/Runner/MainFlutterWindow.swift",
    "content": "import Cocoa\nimport FlutterMacOS\n\nclass MainFlutterWindow: NSWindow {\n  override func awakeFromNib() {\n    let flutterViewController = FlutterViewController()\n    let windowFrame = self.frame\n    self.contentViewController = flutterViewController\n    self.setFrame(windowFrame, display: true)\n\n    RegisterGeneratedPlugins(registry: flutterViewController)\n\n    super.awakeFromNib()\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/macos/Runner/Release.entitlements",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>com.apple.security.app-sandbox</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/macos/Runner.xcodeproj/project.pbxproj",
    "content": "// !$*UTF8*$!\n{\n\tarchiveVersion = 1;\n\tclasses = {\n\t};\n\tobjectVersion = 54;\n\tobjects = {\n\n/* Begin PBXAggregateTarget section */\n\t\t33CC111A2044C6BA0003C045 /* Flutter Assemble */ = {\n\t\t\tisa = PBXAggregateTarget;\n\t\t\tbuildConfigurationList = 33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget \"Flutter Assemble\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t33CC111E2044C6BF0003C045 /* ShellScript */,\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t);\n\t\t\tname = \"Flutter Assemble\";\n\t\t\tproductName = FLX;\n\t\t};\n/* End PBXAggregateTarget section */\n\n/* Begin PBXBuildFile section */\n\t\t0640139504EBB825519BC570 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BA4893A83552EBAABCA498D9 /* Pods_Runner.framework */; };\n\t\t0F3046C7A8405A4885456CCA /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 771AFDF2BAA0ACD25543902A /* Pods_RunnerTests.framework */; };\n\t\t331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; };\n\t\t335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; };\n\t\t33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; };\n\t\t33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; };\n\t\t33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; };\n\t\t33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; };\n/* End PBXBuildFile section */\n\n/* Begin PBXContainerItemProxy section */\n\t\t331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 33CC10E52044A3C60003C045 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 33CC10EC2044A3C60003C045;\n\t\t\tremoteInfo = Runner;\n\t\t};\n\t\t33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = {\n\t\t\tisa = PBXContainerItemProxy;\n\t\t\tcontainerPortal = 33CC10E52044A3C60003C045 /* Project object */;\n\t\t\tproxyType = 1;\n\t\t\tremoteGlobalIDString = 33CC111A2044C6BA0003C045;\n\t\t\tremoteInfo = FLX;\n\t\t};\n/* End PBXContainerItemProxy section */\n\n/* Begin PBXCopyFilesBuildPhase section */\n\t\t33CC110E2044A8840003C045 /* Bundle Framework */ = {\n\t\t\tisa = PBXCopyFilesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tdstPath = \"\";\n\t\t\tdstSubfolderSpec = 10;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tname = \"Bundle Framework\";\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXCopyFilesBuildPhase section */\n\n/* Begin PBXFileReference section */\n\t\t18CE1DE3B79E8BA6C8C5F5B4 /* Pods-Runner.profile-prod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.profile-prod.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.profile-prod.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t1C2D8297C5B081A26E4A5EF7 /* Pods-Runner.release-dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.release-dev.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.release-dev.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t21F0E5EDEEFBAE48AC66E470 /* Pods-RunnerTests.profile-prod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-RunnerTests.profile-prod.xcconfig\"; path = \"Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile-prod.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t2A747F6A39C21082F9411486 /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-RunnerTests.debug.xcconfig\"; path = \"Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = \"<group>\"; };\n\t\t333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = \"<group>\"; };\n\t\t335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = \"<group>\"; };\n\t\t33CC10ED2044A3C60003C045 /* multiple_flavors.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = multiple_flavors.app; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = \"<group>\"; };\n\t\t33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = \"<group>\"; };\n\t\t33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = \"<group>\"; };\n\t\t33CC10F72044A3C60003C045 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; name = Info.plist; path = Runner/Info.plist; sourceTree = \"<group>\"; };\n\t\t33CC11122044BFA00003C045 /* MainFlutterWindow.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainFlutterWindow.swift; sourceTree = \"<group>\"; };\n\t\t33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = \"Flutter-Debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = \"Flutter-Release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = \"Flutter-Generated.xcconfig\"; path = \"ephemeral/Flutter-Generated.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = \"<group>\"; };\n\t\t33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = \"<group>\"; };\n\t\t33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = \"<group>\"; };\n\t\t550DAD9A3B9C07FBAC407740 /* Pods-Runner.profile-dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.profile-dev.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.profile-dev.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t701F0FAB6E430954A43A0CF7 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-RunnerTests.profile.xcconfig\"; path = \"Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t72779351C7D178E838823752 /* Pods-RunnerTests.profile-dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-RunnerTests.profile-dev.xcconfig\"; path = \"Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile-dev.xcconfig\"; sourceTree = \"<group>\"; };\n\t\t771AFDF2BAA0ACD25543902A /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = \"<group>\"; };\n\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = \"<group>\"; };\n\t\tAF05764EFDB89356A66BE764 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.debug.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig\"; sourceTree = \"<group>\"; };\n\t\tB58363B85E9ED6606E885147 /* Pods-RunnerTests.release-prod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-RunnerTests.release-prod.xcconfig\"; path = \"Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release-prod.xcconfig\"; sourceTree = \"<group>\"; };\n\t\tB8C78CD30F864BD0B242D321 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.profile.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig\"; sourceTree = \"<group>\"; };\n\t\tBA4893A83552EBAABCA498D9 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; };\n\t\tCC3CA63941F965225A33A488 /* Pods-RunnerTests.release-dev.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-RunnerTests.release-dev.xcconfig\"; path = \"Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release-dev.xcconfig\"; sourceTree = \"<group>\"; };\n\t\tD45A6BB3F7EAAA796E36F97B /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-RunnerTests.release.xcconfig\"; path = \"Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig\"; sourceTree = \"<group>\"; };\n\t\tEE99E17B771005D809DE73AC /* Pods-Runner.release-prod.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.release-prod.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.release-prod.xcconfig\"; sourceTree = \"<group>\"; };\n\t\tF8B6CAD2DDD4DD37C70F7904 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = \"Pods-Runner.release.xcconfig\"; path = \"Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig\"; sourceTree = \"<group>\"; };\n/* End PBXFileReference section */\n\n/* Begin PBXFrameworksBuildPhase section */\n\t\t331C80D2294CF70F00263BE5 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t0F3046C7A8405A4885456CCA /* Pods_RunnerTests.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t33CC10EA2044A3C60003C045 /* Frameworks */ = {\n\t\t\tisa = PBXFrameworksBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t0640139504EBB825519BC570 /* Pods_Runner.framework in Frameworks */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXFrameworksBuildPhase section */\n\n/* Begin PBXGroup section */\n\t\t331C80D6294CF71000263BE5 /* RunnerTests */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t331C80D7294CF71000263BE5 /* RunnerTests.swift */,\n\t\t\t);\n\t\t\tpath = RunnerTests;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33BA886A226E78AF003329D5 /* Configs */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33E5194F232828860026EE4D /* AppInfo.xcconfig */,\n\t\t\t\t9740EEB21CF90195004384FC /* Debug.xcconfig */,\n\t\t\t\t7AFA3C8E1D35360C0083082E /* Release.xcconfig */,\n\t\t\t\t333000ED22D3DE5D00554162 /* Warnings.xcconfig */,\n\t\t\t);\n\t\t\tpath = Configs;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CC10E42044A3C60003C045 = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33FAB671232836740065AC1E /* Runner */,\n\t\t\t\t33CEB47122A05771004F2AC0 /* Flutter */,\n\t\t\t\t331C80D6294CF71000263BE5 /* RunnerTests */,\n\t\t\t\t33CC10EE2044A3C60003C045 /* Products */,\n\t\t\t\tD73912EC22F37F3D000D13A0 /* Frameworks */,\n\t\t\t\t54901D1B7AF2B05E9EA35CB1 /* Pods */,\n\t\t\t);\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CC10EE2044A3C60003C045 /* Products */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10ED2044A3C60003C045 /* multiple_flavors.app */,\n\t\t\t\t331C80D5294CF71000263BE5 /* RunnerTests.xctest */,\n\t\t\t);\n\t\t\tname = Products;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CC11242044D66E0003C045 /* Resources */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10F22044A3C60003C045 /* Assets.xcassets */,\n\t\t\t\t33CC10F42044A3C60003C045 /* MainMenu.xib */,\n\t\t\t\t33CC10F72044A3C60003C045 /* Info.plist */,\n\t\t\t);\n\t\t\tname = Resources;\n\t\t\tpath = ..;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33CEB47122A05771004F2AC0 /* Flutter */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */,\n\t\t\t\t33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */,\n\t\t\t\t33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */,\n\t\t\t\t33CEB47722A0578A004F2AC0 /* Flutter-Generated.xcconfig */,\n\t\t\t);\n\t\t\tpath = Flutter;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t33FAB671232836740065AC1E /* Runner */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10F02044A3C60003C045 /* AppDelegate.swift */,\n\t\t\t\t33CC11122044BFA00003C045 /* MainFlutterWindow.swift */,\n\t\t\t\t33E51913231747F40026EE4D /* DebugProfile.entitlements */,\n\t\t\t\t33E51914231749380026EE4D /* Release.entitlements */,\n\t\t\t\t33CC11242044D66E0003C045 /* Resources */,\n\t\t\t\t33BA886A226E78AF003329D5 /* Configs */,\n\t\t\t);\n\t\t\tpath = Runner;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\t54901D1B7AF2B05E9EA35CB1 /* Pods */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tAF05764EFDB89356A66BE764 /* Pods-Runner.debug.xcconfig */,\n\t\t\t\tF8B6CAD2DDD4DD37C70F7904 /* Pods-Runner.release.xcconfig */,\n\t\t\t\tB8C78CD30F864BD0B242D321 /* Pods-Runner.profile.xcconfig */,\n\t\t\t\t2A747F6A39C21082F9411486 /* Pods-RunnerTests.debug.xcconfig */,\n\t\t\t\tD45A6BB3F7EAAA796E36F97B /* Pods-RunnerTests.release.xcconfig */,\n\t\t\t\t701F0FAB6E430954A43A0CF7 /* Pods-RunnerTests.profile.xcconfig */,\n\t\t\t\tEE99E17B771005D809DE73AC /* Pods-Runner.release-prod.xcconfig */,\n\t\t\t\tB58363B85E9ED6606E885147 /* Pods-RunnerTests.release-prod.xcconfig */,\n\t\t\t\t1C2D8297C5B081A26E4A5EF7 /* Pods-Runner.release-dev.xcconfig */,\n\t\t\t\tCC3CA63941F965225A33A488 /* Pods-RunnerTests.release-dev.xcconfig */,\n\t\t\t\t550DAD9A3B9C07FBAC407740 /* Pods-Runner.profile-dev.xcconfig */,\n\t\t\t\t18CE1DE3B79E8BA6C8C5F5B4 /* Pods-Runner.profile-prod.xcconfig */,\n\t\t\t\t72779351C7D178E838823752 /* Pods-RunnerTests.profile-dev.xcconfig */,\n\t\t\t\t21F0E5EDEEFBAE48AC66E470 /* Pods-RunnerTests.profile-prod.xcconfig */,\n\t\t\t);\n\t\t\tpath = Pods;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n\t\tD73912EC22F37F3D000D13A0 /* Frameworks */ = {\n\t\t\tisa = PBXGroup;\n\t\t\tchildren = (\n\t\t\t\tBA4893A83552EBAABCA498D9 /* Pods_Runner.framework */,\n\t\t\t\t771AFDF2BAA0ACD25543902A /* Pods_RunnerTests.framework */,\n\t\t\t);\n\t\t\tname = Frameworks;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXGroup section */\n\n/* Begin PBXNativeTarget section */\n\t\t331C80D4294CF70F00263BE5 /* RunnerTests */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget \"RunnerTests\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t4239D5188C53CC88E92CEEE6 /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t331C80D1294CF70F00263BE5 /* Sources */,\n\t\t\t\t331C80D2294CF70F00263BE5 /* Frameworks */,\n\t\t\t\t331C80D3294CF70F00263BE5 /* Resources */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t331C80DA294CF71000263BE5 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = RunnerTests;\n\t\t\tproductName = RunnerTests;\n\t\t\tproductReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */;\n\t\t\tproductType = \"com.apple.product-type.bundle.unit-test\";\n\t\t};\n\t\t33CC10EC2044A3C60003C045 /* Runner */ = {\n\t\t\tisa = PBXNativeTarget;\n\t\t\tbuildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget \"Runner\" */;\n\t\t\tbuildPhases = (\n\t\t\t\t28BB9CAB224434EC9D1D6B6E /* [CP] Check Pods Manifest.lock */,\n\t\t\t\t33CC10E92044A3C60003C045 /* Sources */,\n\t\t\t\t33CC10EA2044A3C60003C045 /* Frameworks */,\n\t\t\t\t33CC10EB2044A3C60003C045 /* Resources */,\n\t\t\t\t33CC110E2044A8840003C045 /* Bundle Framework */,\n\t\t\t\t3399D490228B24CF009A79C7 /* ShellScript */,\n\t\t\t\tD4A182524C116952FF1A809D /* [CP] Embed Pods Frameworks */,\n\t\t\t);\n\t\t\tbuildRules = (\n\t\t\t);\n\t\t\tdependencies = (\n\t\t\t\t33CC11202044C79F0003C045 /* PBXTargetDependency */,\n\t\t\t);\n\t\t\tname = Runner;\n\t\t\tproductName = Runner;\n\t\t\tproductReference = 33CC10ED2044A3C60003C045 /* multiple_flavors.app */;\n\t\t\tproductType = \"com.apple.product-type.application\";\n\t\t};\n/* End PBXNativeTarget section */\n\n/* Begin PBXProject section */\n\t\t33CC10E52044A3C60003C045 /* Project object */ = {\n\t\t\tisa = PBXProject;\n\t\t\tattributes = {\n\t\t\t\tBuildIndependentTargetsInParallel = YES;\n\t\t\t\tLastSwiftUpdateCheck = 0920;\n\t\t\t\tLastUpgradeCheck = 1510;\n\t\t\t\tORGANIZATIONNAME = \"\";\n\t\t\t\tTargetAttributes = {\n\t\t\t\t\t331C80D4294CF70F00263BE5 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 14.0;\n\t\t\t\t\t\tTestTargetID = 33CC10EC2044A3C60003C045;\n\t\t\t\t\t};\n\t\t\t\t\t33CC10EC2044A3C60003C045 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.2;\n\t\t\t\t\t\tLastSwiftMigration = 1100;\n\t\t\t\t\t\tProvisioningStyle = Automatic;\n\t\t\t\t\t\tSystemCapabilities = {\n\t\t\t\t\t\t\tcom.apple.Sandbox = {\n\t\t\t\t\t\t\t\tenabled = 1;\n\t\t\t\t\t\t\t};\n\t\t\t\t\t\t};\n\t\t\t\t\t};\n\t\t\t\t\t33CC111A2044C6BA0003C045 = {\n\t\t\t\t\t\tCreatedOnToolsVersion = 9.2;\n\t\t\t\t\t\tProvisioningStyle = Manual;\n\t\t\t\t\t};\n\t\t\t\t};\n\t\t\t};\n\t\t\tbuildConfigurationList = 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject \"Runner\" */;\n\t\t\tcompatibilityVersion = \"Xcode 9.3\";\n\t\t\tdevelopmentRegion = en;\n\t\t\thasScannedForEncodings = 0;\n\t\t\tknownRegions = (\n\t\t\t\ten,\n\t\t\t\tBase,\n\t\t\t);\n\t\t\tmainGroup = 33CC10E42044A3C60003C045;\n\t\t\tproductRefGroup = 33CC10EE2044A3C60003C045 /* Products */;\n\t\t\tprojectDirPath = \"\";\n\t\t\tprojectRoot = \"\";\n\t\t\ttargets = (\n\t\t\t\t33CC10EC2044A3C60003C045 /* Runner */,\n\t\t\t\t331C80D4294CF70F00263BE5 /* RunnerTests */,\n\t\t\t\t33CC111A2044C6BA0003C045 /* Flutter Assemble */,\n\t\t\t);\n\t\t};\n/* End PBXProject section */\n\n/* Begin PBXResourcesBuildPhase section */\n\t\t331C80D3294CF70F00263BE5 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t33CC10EB2044A3C60003C045 /* Resources */ = {\n\t\t\tisa = PBXResourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */,\n\t\t\t\t33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXResourcesBuildPhase section */\n\n/* Begin PBXShellScriptBuildPhase section */\n\t\t28BB9CAB224434EC9D1D6B6E /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\t3399D490228B24CF009A79C7 /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\talwaysOutOfDate = 1;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t);\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"echo \\\"$PRODUCT_NAME.app\\\" > \\\"$PROJECT_DIR\\\"/Flutter/ephemeral/.app_filename && \\\"$FLUTTER_ROOT\\\"/packages/flutter_tools/bin/macos_assemble.sh embed\\n\";\n\t\t};\n\t\t33CC111E2044C6BF0003C045 /* ShellScript */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t\tFlutter/ephemeral/FlutterInputs.xcfilelist,\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\tFlutter/ephemeral/tripwire,\n\t\t\t);\n\t\t\toutputFileListPaths = (\n\t\t\t\tFlutter/ephemeral/FlutterOutputs.xcfilelist,\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"\\\"$FLUTTER_ROOT\\\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire\";\n\t\t};\n\t\t4239D5188C53CC88E92CEEE6 /* [CP] Check Pods Manifest.lock */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t);\n\t\t\tinputPaths = (\n\t\t\t\t\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\",\n\t\t\t\t\"${PODS_ROOT}/Manifest.lock\",\n\t\t\t);\n\t\t\tname = \"[CP] Check Pods Manifest.lock\";\n\t\t\toutputFileListPaths = (\n\t\t\t);\n\t\t\toutputPaths = (\n\t\t\t\t\"$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"diff \\\"${PODS_PODFILE_DIR_PATH}/Podfile.lock\\\" \\\"${PODS_ROOT}/Manifest.lock\\\" > /dev/null\\nif [ $? != 0 ] ; then\\n    # print error to STDERR\\n    echo \\\"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\\\" >&2\\n    exit 1\\nfi\\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\\necho \\\"SUCCESS\\\" > \\\"${SCRIPT_OUTPUT_FILE_0}\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n\t\tD4A182524C116952FF1A809D /* [CP] Embed Pods Frameworks */ = {\n\t\t\tisa = PBXShellScriptBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t);\n\t\t\tinputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist\",\n\t\t\t);\n\t\t\tname = \"[CP] Embed Pods Frameworks\";\n\t\t\toutputFileListPaths = (\n\t\t\t\t\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist\",\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t\tshellPath = /bin/sh;\n\t\t\tshellScript = \"\\\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\\\"\\n\";\n\t\t\tshowEnvVarsInLog = 0;\n\t\t};\n/* End PBXShellScriptBuildPhase section */\n\n/* Begin PBXSourcesBuildPhase section */\n\t\t331C80D1294CF70F00263BE5 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n\t\t33CC10E92044A3C60003C045 /* Sources */ = {\n\t\t\tisa = PBXSourcesBuildPhase;\n\t\t\tbuildActionMask = 2147483647;\n\t\t\tfiles = (\n\t\t\t\t33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */,\n\t\t\t\t33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */,\n\t\t\t\t335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */,\n\t\t\t);\n\t\t\trunOnlyForDeploymentPostprocessing = 0;\n\t\t};\n/* End PBXSourcesBuildPhase section */\n\n/* Begin PBXTargetDependency section */\n\t\t331C80DA294CF71000263BE5 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 33CC10EC2044A3C60003C045 /* Runner */;\n\t\t\ttargetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */;\n\t\t};\n\t\t33CC11202044C79F0003C045 /* PBXTargetDependency */ = {\n\t\t\tisa = PBXTargetDependency;\n\t\t\ttarget = 33CC111A2044C6BA0003C045 /* Flutter Assemble */;\n\t\t\ttargetProxy = 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */;\n\t\t};\n/* End PBXTargetDependency section */\n\n/* Begin PBXVariantGroup section */\n\t\t33CC10F42044A3C60003C045 /* MainMenu.xib */ = {\n\t\t\tisa = PBXVariantGroup;\n\t\t\tchildren = (\n\t\t\t\t33CC10F52044A3C60003C045 /* Base */,\n\t\t\t);\n\t\t\tname = MainMenu.xib;\n\t\t\tpath = Runner;\n\t\t\tsourceTree = \"<group>\";\n\t\t};\n/* End PBXVariantGroup section */\n\n/* Begin XCBuildConfiguration section */\n\t\t331C80DB294CF71000263BE5 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 2A747F6A39C21082F9411486 /* Pods-RunnerTests.debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.leanflutter.examples.multipleFlavors.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/multiple_flavors.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/multiple_flavors\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t331C80DC294CF71000263BE5 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = D45A6BB3F7EAAA796E36F97B /* Pods-RunnerTests.release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.leanflutter.examples.multipleFlavors.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/multiple_flavors.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/multiple_flavors\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t331C80DD294CF71000263BE5 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 701F0FAB6E430954A43A0CF7 /* Pods-RunnerTests.profile.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.leanflutter.examples.multipleFlavors.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/multiple_flavors.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/multiple_flavors\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t338D0CE9231458BD00FA5F75 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = NO;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.14;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t338D0CEA231458BD00FA5F75 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t338D0CEB231458BD00FA5F75 /* Profile */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Profile;\n\t\t};\n\t\t33CC10F92044A3C60003C045 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = dwarf;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_TESTABILITY = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = NO;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_DYNAMIC_NO_PIC = NO;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_OPTIMIZATION_LEVEL = 0;\n\t\t\t\tGCC_PREPROCESSOR_DEFINITIONS = (\n\t\t\t\t\t\"DEBUG=1\",\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t);\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.14;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = YES;\n\t\t\t\tONLY_ACTIVE_ARCH = YES;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t33CC10FA2044A3C60003C045 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = NO;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.14;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t33CC10FC2044A3C60003C045 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-Onone\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t33CC10FD2044A3C60003C045 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\t33CC111C2044C6BA0003C045 /* Debug */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Debug;\n\t\t};\n\t\t33CC111D2044C6BA0003C045 /* Release */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = Release;\n\t\t};\n\t\tF5C5D0032C1DC6380057B2C4 /* Release-prod */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = NO;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.14;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t};\n\t\t\tname = \"Release-prod\";\n\t\t};\n\t\tF5C5D0042C1DC6380057B2C4 /* Release-prod */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = \"Release-prod\";\n\t\t};\n\t\tF5C5D0052C1DC6380057B2C4 /* Release-prod */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = B58363B85E9ED6606E885147 /* Pods-RunnerTests.release-prod.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.leanflutter.examples.multipleFlavors.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/multiple_flavors.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/multiple_flavors\";\n\t\t\t};\n\t\t\tname = \"Release-prod\";\n\t\t};\n\t\tF5C5D0062C1DC6380057B2C4 /* Release-prod */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = \"Release-prod\";\n\t\t};\n\t\tF5C5D0072C1DC64F0057B2C4 /* Release-dev */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = NO;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.14;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t};\n\t\t\tname = \"Release-dev\";\n\t\t};\n\t\tF5C5D0082C1DC64F0057B2C4 /* Release-dev */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/Release.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = \"Release-dev\";\n\t\t};\n\t\tF5C5D0092C1DC64F0057B2C4 /* Release-dev */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = CC3CA63941F965225A33A488 /* Pods-RunnerTests.release-dev.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.leanflutter.examples.multipleFlavors.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/multiple_flavors.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/multiple_flavors\";\n\t\t\t};\n\t\t\tname = \"Release-dev\";\n\t\t};\n\t\tF5C5D00A2C1DC64F0057B2C4 /* Release-dev */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = \"Release-dev\";\n\t\t};\n\t\tF5C5D00F2C1DC7CF0057B2C4 /* Profile-prod */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = NO;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.14;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t};\n\t\t\tname = \"Profile-prod\";\n\t\t};\n\t\tF5C5D0102C1DC7CF0057B2C4 /* Profile-prod */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = \"Profile-prod\";\n\t\t};\n\t\tF5C5D0112C1DC7CF0057B2C4 /* Profile-prod */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 21F0E5EDEEFBAE48AC66E470 /* Pods-RunnerTests.profile-prod.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.leanflutter.examples.multipleFlavors.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/multiple_flavors.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/multiple_flavors\";\n\t\t\t};\n\t\t\tname = \"Profile-prod\";\n\t\t};\n\t\tF5C5D0122C1DC7CF0057B2C4 /* Profile-prod */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = \"Profile-prod\";\n\t\t};\n\t\tF5C5D0132C1DC7DA0057B2C4 /* Profile-dev */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tALWAYS_SEARCH_USER_PATHS = NO;\n\t\t\t\tASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;\n\t\t\t\tCLANG_ANALYZER_NONNULL = YES;\n\t\t\t\tCLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;\n\t\t\t\tCLANG_CXX_LANGUAGE_STANDARD = \"gnu++14\";\n\t\t\t\tCLANG_CXX_LIBRARY = \"libc++\";\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCLANG_ENABLE_OBJC_ARC = YES;\n\t\t\t\tCLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;\n\t\t\t\tCLANG_WARN_BOOL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_CONSTANT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;\n\t\t\t\tCLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;\n\t\t\t\tCLANG_WARN_DOCUMENTATION_COMMENTS = YES;\n\t\t\t\tCLANG_WARN_EMPTY_BODY = YES;\n\t\t\t\tCLANG_WARN_ENUM_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_INFINITE_RECURSION = YES;\n\t\t\t\tCLANG_WARN_INT_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_LITERAL_CONVERSION = YES;\n\t\t\t\tCLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;\n\t\t\t\tCLANG_WARN_RANGE_LOOP_ANALYSIS = YES;\n\t\t\t\tCLANG_WARN_SUSPICIOUS_MOVE = YES;\n\t\t\t\tCODE_SIGN_IDENTITY = \"-\";\n\t\t\t\tCOPY_PHASE_STRIP = NO;\n\t\t\t\tDEAD_CODE_STRIPPING = YES;\n\t\t\t\tDEBUG_INFORMATION_FORMAT = \"dwarf-with-dsym\";\n\t\t\t\tENABLE_NS_ASSERTIONS = NO;\n\t\t\t\tENABLE_STRICT_OBJC_MSGSEND = YES;\n\t\t\t\tENABLE_USER_SCRIPT_SANDBOXING = NO;\n\t\t\t\tGCC_C_LANGUAGE_STANDARD = gnu11;\n\t\t\t\tGCC_NO_COMMON_BLOCKS = YES;\n\t\t\t\tGCC_WARN_64_TO_32_BIT_CONVERSION = YES;\n\t\t\t\tGCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;\n\t\t\t\tGCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;\n\t\t\t\tGCC_WARN_UNUSED_FUNCTION = YES;\n\t\t\t\tGCC_WARN_UNUSED_VARIABLE = YES;\n\t\t\t\tMACOSX_DEPLOYMENT_TARGET = 10.14;\n\t\t\t\tMTL_ENABLE_DEBUG_INFO = NO;\n\t\t\t\tSDKROOT = macosx;\n\t\t\t\tSWIFT_COMPILATION_MODE = wholemodule;\n\t\t\t\tSWIFT_OPTIMIZATION_LEVEL = \"-O\";\n\t\t\t};\n\t\t\tname = \"Profile-dev\";\n\t\t};\n\t\tF5C5D0142C1DC7DA0057B2C4 /* Profile-dev */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 33E5194F232828860026EE4D /* AppInfo.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;\n\t\t\t\tCLANG_ENABLE_MODULES = YES;\n\t\t\t\tCODE_SIGN_ENTITLEMENTS = Runner/DebugProfile.entitlements;\n\t\t\t\tCODE_SIGN_STYLE = Automatic;\n\t\t\t\tCOMBINE_HIDPI_IMAGES = YES;\n\t\t\t\tINFOPLIST_FILE = Runner/Info.plist;\n\t\t\t\tLD_RUNPATH_SEARCH_PATHS = (\n\t\t\t\t\t\"$(inherited)\",\n\t\t\t\t\t\"@executable_path/../Frameworks\",\n\t\t\t\t);\n\t\t\t\tPROVISIONING_PROFILE_SPECIFIER = \"\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t};\n\t\t\tname = \"Profile-dev\";\n\t\t};\n\t\tF5C5D0152C1DC7DA0057B2C4 /* Profile-dev */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbaseConfigurationReference = 72779351C7D178E838823752 /* Pods-RunnerTests.profile-dev.xcconfig */;\n\t\t\tbuildSettings = {\n\t\t\t\tBUNDLE_LOADER = \"$(TEST_HOST)\";\n\t\t\t\tCURRENT_PROJECT_VERSION = 1;\n\t\t\t\tGENERATE_INFOPLIST_FILE = YES;\n\t\t\t\tMARKETING_VERSION = 1.0;\n\t\t\t\tPRODUCT_BUNDLE_IDENTIFIER = org.leanflutter.examples.multipleFlavors.RunnerTests;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t\tSWIFT_VERSION = 5.0;\n\t\t\t\tTEST_HOST = \"$(BUILT_PRODUCTS_DIR)/multiple_flavors.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/multiple_flavors\";\n\t\t\t};\n\t\t\tname = \"Profile-dev\";\n\t\t};\n\t\tF5C5D0162C1DC7DA0057B2C4 /* Profile-dev */ = {\n\t\t\tisa = XCBuildConfiguration;\n\t\t\tbuildSettings = {\n\t\t\t\tCODE_SIGN_STYLE = Manual;\n\t\t\t\tPRODUCT_NAME = \"$(TARGET_NAME)\";\n\t\t\t};\n\t\t\tname = \"Profile-dev\";\n\t\t};\n/* End XCBuildConfiguration section */\n\n/* Begin XCConfigurationList section */\n\t\t331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget \"RunnerTests\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t331C80DB294CF71000263BE5 /* Debug */,\n\t\t\t\t331C80DC294CF71000263BE5 /* Release */,\n\t\t\t\tF5C5D0092C1DC64F0057B2C4 /* Release-dev */,\n\t\t\t\tF5C5D0052C1DC6380057B2C4 /* Release-prod */,\n\t\t\t\t331C80DD294CF71000263BE5 /* Profile */,\n\t\t\t\tF5C5D0152C1DC7DA0057B2C4 /* Profile-dev */,\n\t\t\t\tF5C5D0112C1DC7CF0057B2C4 /* Profile-prod */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t33CC10E82044A3C60003C045 /* Build configuration list for PBXProject \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t33CC10F92044A3C60003C045 /* Debug */,\n\t\t\t\t33CC10FA2044A3C60003C045 /* Release */,\n\t\t\t\tF5C5D0072C1DC64F0057B2C4 /* Release-dev */,\n\t\t\t\tF5C5D0032C1DC6380057B2C4 /* Release-prod */,\n\t\t\t\t338D0CE9231458BD00FA5F75 /* Profile */,\n\t\t\t\tF5C5D0132C1DC7DA0057B2C4 /* Profile-dev */,\n\t\t\t\tF5C5D00F2C1DC7CF0057B2C4 /* Profile-prod */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget \"Runner\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t33CC10FC2044A3C60003C045 /* Debug */,\n\t\t\t\t33CC10FD2044A3C60003C045 /* Release */,\n\t\t\t\tF5C5D0082C1DC64F0057B2C4 /* Release-dev */,\n\t\t\t\tF5C5D0042C1DC6380057B2C4 /* Release-prod */,\n\t\t\t\t338D0CEA231458BD00FA5F75 /* Profile */,\n\t\t\t\tF5C5D0142C1DC7DA0057B2C4 /* Profile-dev */,\n\t\t\t\tF5C5D0102C1DC7CF0057B2C4 /* Profile-prod */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n\t\t33CC111B2044C6BA0003C045 /* Build configuration list for PBXAggregateTarget \"Flutter Assemble\" */ = {\n\t\t\tisa = XCConfigurationList;\n\t\t\tbuildConfigurations = (\n\t\t\t\t33CC111C2044C6BA0003C045 /* Debug */,\n\t\t\t\t33CC111D2044C6BA0003C045 /* Release */,\n\t\t\t\tF5C5D00A2C1DC64F0057B2C4 /* Release-dev */,\n\t\t\t\tF5C5D0062C1DC6380057B2C4 /* Release-prod */,\n\t\t\t\t338D0CEB231458BD00FA5F75 /* Profile */,\n\t\t\t\tF5C5D0162C1DC7DA0057B2C4 /* Profile-dev */,\n\t\t\t\tF5C5D0122C1DC7CF0057B2C4 /* Profile-prod */,\n\t\t\t);\n\t\t\tdefaultConfigurationIsVisible = 0;\n\t\t\tdefaultConfigurationName = Release;\n\t\t};\n/* End XCConfigurationList section */\n\t};\n\trootObject = 33CC10E52044A3C60003C045 /* Project object */;\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1510\"\n   version = \"1.3\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n               BuildableName = \"multiple_flavors.app\"\n               BlueprintName = \"Runner\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\">\n      <MacroExpansion>\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"multiple_flavors.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </MacroExpansion>\n      <Testables>\n         <TestableReference\n            skipped = \"NO\"\n            parallelizable = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"331C80D4294CF70F00263BE5\"\n               BuildableName = \"RunnerTests.xctest\"\n               BlueprintName = \"RunnerTests\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </TestableReference>\n      </Testables>\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"multiple_flavors.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Profile\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"multiple_flavors.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/macos/Runner.xcodeproj/xcshareddata/xcschemes/dev.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1540\"\n   version = \"1.7\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\"\n      buildArchitectures = \"Automatic\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n               BuildableName = \"multiple_flavors.app\"\n               BlueprintName = \"Runner\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      shouldAutocreateTestPlan = \"YES\">\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"multiple_flavors.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"multiple_flavors.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/macos/Runner.xcodeproj/xcshareddata/xcschemes/prod.xcscheme",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Scheme\n   LastUpgradeVersion = \"1540\"\n   version = \"1.7\">\n   <BuildAction\n      parallelizeBuildables = \"YES\"\n      buildImplicitDependencies = \"YES\"\n      buildArchitectures = \"Automatic\">\n      <BuildActionEntries>\n         <BuildActionEntry\n            buildForTesting = \"YES\"\n            buildForRunning = \"YES\"\n            buildForProfiling = \"YES\"\n            buildForArchiving = \"YES\"\n            buildForAnalyzing = \"YES\">\n            <BuildableReference\n               BuildableIdentifier = \"primary\"\n               BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n               BuildableName = \"multiple_flavors.app\"\n               BlueprintName = \"Runner\"\n               ReferencedContainer = \"container:Runner.xcodeproj\">\n            </BuildableReference>\n         </BuildActionEntry>\n      </BuildActionEntries>\n   </BuildAction>\n   <TestAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      shouldAutocreateTestPlan = \"YES\">\n   </TestAction>\n   <LaunchAction\n      buildConfiguration = \"Debug\"\n      selectedDebuggerIdentifier = \"Xcode.DebuggerFoundation.Debugger.LLDB\"\n      selectedLauncherIdentifier = \"Xcode.DebuggerFoundation.Launcher.LLDB\"\n      launchStyle = \"0\"\n      useCustomWorkingDirectory = \"NO\"\n      ignoresPersistentStateOnLaunch = \"NO\"\n      debugDocumentVersioning = \"YES\"\n      debugServiceExtension = \"internal\"\n      allowLocationSimulation = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"multiple_flavors.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </LaunchAction>\n   <ProfileAction\n      buildConfiguration = \"Release\"\n      shouldUseLaunchSchemeArgsEnv = \"YES\"\n      savedToolIdentifier = \"\"\n      useCustomWorkingDirectory = \"NO\"\n      debugDocumentVersioning = \"YES\">\n      <BuildableProductRunnable\n         runnableDebuggingMode = \"0\">\n         <BuildableReference\n            BuildableIdentifier = \"primary\"\n            BlueprintIdentifier = \"33CC10EC2044A3C60003C045\"\n            BuildableName = \"multiple_flavors.app\"\n            BlueprintName = \"Runner\"\n            ReferencedContainer = \"container:Runner.xcodeproj\">\n         </BuildableReference>\n      </BuildableProductRunnable>\n   </ProfileAction>\n   <AnalyzeAction\n      buildConfiguration = \"Debug\">\n   </AnalyzeAction>\n   <ArchiveAction\n      buildConfiguration = \"Release\"\n      revealArchiveInOrganizer = \"YES\">\n   </ArchiveAction>\n</Scheme>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/macos/Runner.xcworkspace/contents.xcworkspacedata",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Workspace\n   version = \"1.0\">\n   <FileRef\n      location = \"group:Runner.xcodeproj\">\n   </FileRef>\n   <FileRef\n      location = \"group:Pods/Pods.xcodeproj\">\n   </FileRef>\n</Workspace>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/macos/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n<dict>\n\t<key>IDEDidComputeMac32BitWarning</key>\n\t<true/>\n</dict>\n</plist>\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/macos/RunnerTests/RunnerTests.swift",
    "content": "import Cocoa\nimport FlutterMacOS\nimport XCTest\n\nclass RunnerTests: XCTestCase {\n\n  func testExample() {\n    // If you add code to the Runner application, consider adding tests here.\n    // See https://developer.apple.com/documentation/xctest for more information about using XCTest.\n  }\n\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/macos/packaging/dmg/make_config.yaml",
    "content": "title: multiple_flavors\ncontents:\n  - x: 448\n    y: 344\n    type: link\n    path: \"/Applications\"\n  - x: 192\n    y: 344\n    type: file\n    path: multiple_flavors.app\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/.gitignore",
    "content": "/node_modules\n/oh_modules\n/local.properties\n/.idea\n**/build\n/.hvigor\n.cxx\n/.clangd\n/.clang-format\n/.clang-tidy\n**/.test\n*.har\n**/BuildProfile.ets\n**/oh-package-lock.json5\n\n**/src/main/resources/rawfile/flutter_assets/\n**/libs/arm64-v8a/libapp.so\n**/libs/arm64-v8a/libflutter.so\n**/libs/arm64-v8a/libvmservice_snapshot.so\nhar/flutter.har\noh-package-lock.json5\ndta"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/AppScope/app.json5",
    "content": "{\n  \"app\": {\n    \"bundleName\": \"org.leanflutter.examples.multiple_flavors\",\n    \"vendor\": \"example\",\n    \"versionCode\": 1000000,\n    \"versionName\": \"1.0.0\",\n    \"icon\": \"$media:app_icon\",\n    \"label\": \"$string:app_name\"\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/AppScope/resources/base/element/string.json",
    "content": "{\n  \"string\": [\n    {\n      \"name\": \"app_name\",\n      \"value\": \"multiple_flavors\"\n    }\n  ]\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/build-profile.json5",
    "content": "\n{\n  \"app\": {\n    \"signingConfigs\": [],\n    \"products\": [\n      {\n        \"name\": \"default\",\n        \"signingConfig\": \"default\", // replace with your own signing config in production\n        \"compatibleSdkVersion\": \"5.0.0(12)\",\n        \"runtimeOS\": \"HarmonyOS\",\n      },\n      {\n        \"name\": \"prod\",\n        \"signingConfig\": \"default\", // replace with your own signing config in production\n        \"compatibleSdkVersion\": \"5.0.0(12)\",\n        \"runtimeOS\": \"HarmonyOS\",\n        \"buildOption\": {\n          \"debuggable\": false\n        }\n      },\n    ]\n  },\n  \"modules\": [\n    {\n      \"name\": \"entry\",\n      \"srcPath\": \"./entry\",\n      \"targets\": [\n        {\n          \"name\": \"default\",\n          \"applyToProducts\": [\n            \"default\",\n          ]\n        },\n        {\n          \"name\": \"prod\", // target name defined in build-profile.json5 in entry module\n          \"applyToProducts\": [\n            \"prod\", // apply to 'prod' product defined above\n          ]\n        }\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/entry/.gitignore",
    "content": "\n/node_modules\n/oh_modules\n/.preview\n/build\n/.cxx\n/.test"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/entry/build-profile.json5",
    "content": "\n{\n  \"apiType\": 'stageMode',\n  \"buildOption\": {\n  },\n  \"targets\": [\n    {\n      \"name\": \"default\",\n      \"runtimeOS\": \"HarmonyOS\"\n    },\n    {\n      \"name\": \"prod\",\n      \"runtimeOS\": \"HarmonyOS\"\n    },\n  ]\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/entry/hvigorfile.ts",
    "content": "\n// Script for compiling build behavior. It is built in the build plug-in and cannot be modified currently.\nexport { hapTasks } from '@ohos/hvigor-ohos-plugin';\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/entry/oh-package.json5",
    "content": "{\n  \"name\": \"entry\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Please describe the basic information.\",\n  \"main\": \"\",\n  \"author\": \"\",\n  \"license\": \"\",\n  \"dependencies\": {}\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/entry/src/main/ets/entryability/EntryAbility.ets",
    "content": "\nimport { FlutterAbility, FlutterEngine } from '@ohos/flutter_ohos';\nimport { GeneratedPluginRegistrant } from '../plugins/GeneratedPluginRegistrant';\n\nexport default class EntryAbility extends FlutterAbility {\n  configureFlutterEngine(flutterEngine: FlutterEngine) {\n    super.configureFlutterEngine(flutterEngine)\n    GeneratedPluginRegistrant.registerWith(flutterEngine)\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/entry/src/main/ets/pages/Index.ets",
    "content": "\nimport common from '@ohos.app.ability.common';\nimport { FlutterPage } from '@ohos/flutter_ohos'\n\nlet storage = LocalStorage.getShared()\nconst EVENT_BACK_PRESS = 'EVENT_BACK_PRESS'\n\n@Entry(storage)\n@Component\nstruct Index {\n  private context = getContext(this) as common.UIAbilityContext\n  @LocalStorageLink('viewId') viewId: string = \"\";\n\n  build() {\n    Column() {\n      FlutterPage({ viewId: this.viewId })\n    }\n  }\n\n  onBackPress(): boolean {\n    this.context.eventHub.emit(EVENT_BACK_PRESS)\n    return true\n  }\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/entry/src/main/ets/plugins/GeneratedPluginRegistrant.ets",
    "content": "import { FlutterEngine, Log } from '@ohos/flutter_ohos';\n\n/**\n * Generated file. Do not edit.\n * This file is generated by the Flutter tool based on the\n * plugins that support the Ohos platform.\n */\n\nconst TAG = \"GeneratedPluginRegistrant\";\n\nexport class GeneratedPluginRegistrant {\n\n  static registerWith(flutterEngine: FlutterEngine) {\n    try {\n    } catch (e) {\n      Log.e(\n        TAG,\n        \"Tried to register plugins with FlutterEngine (\"\n          + flutterEngine\n          + \") failed.\");\n      Log.e(TAG, \"Received exception while registering\", e);\n    }\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/entry/src/main/module.json5",
    "content": "\n{\n  \"module\": {\n    \"name\": \"entry\",\n    \"type\": \"entry\",\n    \"description\": \"$string:module_desc\",\n    \"mainElement\": \"EntryAbility\",\n    \"deviceTypes\": [\n      \"phone\"\n    ],\n    \"deliveryWithInstall\": true,\n    \"installationFree\": false,\n    \"pages\": \"$profile:main_pages\",\n    \"abilities\": [\n      {\n        \"name\": \"EntryAbility\",\n        \"srcEntry\": \"./ets/entryability/EntryAbility.ets\",\n        \"description\": \"$string:EntryAbility_desc\",\n        \"icon\": \"$media:icon\",\n        \"label\": \"$string:EntryAbility_label\",\n        \"startWindowIcon\": \"$media:icon\",\n        \"startWindowBackground\": \"$color:start_window_background\",\n        \"exported\": true,\n        \"skills\": [\n          {\n            \"entities\": [\n              \"entity.system.home\"\n            ],\n            \"actions\": [\n              \"action.system.home\"\n            ]\n          }\n        ]\n      }\n    ],\n    \"requestPermissions\": [\n      {\"name\" :  \"ohos.permission.INTERNET\"},\n    ]\n  }\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/entry/src/main/resources/base/element/color.json",
    "content": "{\n  \"color\": [\n    {\n      \"name\": \"start_window_background\",\n      \"value\": \"#FFFFFF\"\n    }\n  ]\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/entry/src/main/resources/base/element/string.json",
    "content": "{\n  \"string\": [\n    {\n      \"name\": \"module_desc\",\n      \"value\": \"module description\"\n    },\n    {\n      \"name\": \"EntryAbility_desc\",\n      \"value\": \"description\"\n    },\n    {\n      \"name\": \"EntryAbility_label\",\n      \"value\": \"multiple_flavors\"\n    }\n  ]\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/entry/src/main/resources/base/profile/buildinfo.json5",
    "content": "{\n  \"string\": [\n    {\n      \"name\": \"enable_impeller\",\n      \"value\": \"true\"\n    }\n  ]\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/entry/src/main/resources/base/profile/main_pages.json",
    "content": "{\n  \"src\": [\n    \"pages/Index\"\n  ]\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/entry/src/main/resources/en_US/element/string.json",
    "content": "{\n  \"string\": [\n    {\n      \"name\": \"module_desc\",\n      \"value\": \"module description\"\n    },\n    {\n      \"name\": \"EntryAbility_desc\",\n      \"value\": \"description\"\n    },\n    {\n      \"name\": \"EntryAbility_label\",\n      \"value\": \"multiple_flavors\"\n    }\n  ]\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/entry/src/main/resources/zh_CN/element/string.json",
    "content": "{\n  \"string\": [\n    {\n      \"name\": \"module_desc\",\n      \"value\": \"模块描述\"\n    },\n    {\n      \"name\": \"EntryAbility_desc\",\n      \"value\": \"description\"\n    },\n    {\n      \"name\": \"EntryAbility_label\",\n      \"value\": \"multiple_flavors\"\n    }\n  ]\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/entry/src/ohosTest/ets/test/Ability.test.ets",
    "content": "import hilog from '@ohos.hilog';\nimport { describe, beforeAll, beforeEach, afterEach, afterAll, it, expect } from '@ohos/hypium'\n\nexport default function abilityTest() {\n  describe('ActsAbilityTest', function () {\n    // Defines a test suite. Two parameters are supported: test suite name and test suite function.\n    beforeAll(function () {\n      // Presets an action, which is performed only once before all test cases of the test suite start.\n      // This API supports only one parameter: preset action function.\n    })\n    beforeEach(function () {\n      // Presets an action, which is performed before each unit test case starts.\n      // The number of execution times is the same as the number of test cases defined by **it**.\n      // This API supports only one parameter: preset action function.\n    })\n    afterEach(function () {\n      // Presets a clear action, which is performed after each unit test case ends.\n      // The number of execution times is the same as the number of test cases defined by **it**.\n      // This API supports only one parameter: clear action function.\n    })\n    afterAll(function () {\n      // Presets a clear action, which is performed after all test cases of the test suite end.\n      // This API supports only one parameter: clear action function.\n    })\n    it('assertContain',0, function () {\n      // Defines a test case. This API supports three parameters: test case name, filter parameter, and test case function.\n      hilog.info(0x0000, 'testTag', '%{public}s', 'it begin');\n      let a = 'abc'\n      let b = 'b'\n      // Defines a variety of assertion methods, which are used to declare expected boolean conditions.\n      expect(a).assertContain(b)\n      expect(a).assertEqual(a)\n    })\n  })\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/entry/src/ohosTest/ets/test/List.test.ets",
    "content": "import abilityTest from './Ability.test'\n\nexport default function testsuite() {\n  abilityTest()\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/entry/src/ohosTest/ets/testability/TestAbility.ets",
    "content": "import UIAbility from '@ohos.app.ability.UIAbility';\nimport AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';\nimport hilog from '@ohos.hilog';\nimport { Hypium } from '@ohos/hypium';\nimport testsuite from '../test/List.test';\nimport window from '@ohos.window';\n\nexport default class TestAbility extends UIAbility {\n    onCreate(want, launchParam) {\n        hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onCreate');\n        hilog.info(0x0000, 'testTag', '%{public}s', 'want param:' + JSON.stringify(want) ?? '');\n        hilog.info(0x0000, 'testTag', '%{public}s', 'launchParam:'+ JSON.stringify(launchParam) ?? '');\n        var abilityDelegator: any\n        abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()\n        var abilityDelegatorArguments: any\n        abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()\n        hilog.info(0x0000, 'testTag', '%{public}s', 'start run testcase!!!');\n        Hypium.hypiumTest(abilityDelegator, abilityDelegatorArguments, testsuite)\n    }\n\n    onDestroy() {\n        hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onDestroy');\n    }\n\n    onWindowStageCreate(windowStage: window.WindowStage) {\n        hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageCreate');\n        windowStage.loadContent('testability/pages/Index', (err, data) => {\n            if (err.code) {\n                hilog.error(0x0000, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err) ?? '');\n                return;\n            }\n            hilog.info(0x0000, 'testTag', 'Succeeded in loading the content. Data: %{public}s',\n                JSON.stringify(data) ?? '');\n        });\n    }\n\n    onWindowStageDestroy() {\n        hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onWindowStageDestroy');\n    }\n\n    onForeground() {\n        hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onForeground');\n    }\n\n    onBackground() {\n        hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility onBackground');\n    }\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/entry/src/ohosTest/ets/testability/pages/Index.ets",
    "content": "\n\nimport hilog from '@ohos.hilog';\n\n@Entry\n@Component\nstruct Index {\n  aboutToAppear() {\n    hilog.info(0x0000, 'testTag', '%{public}s', 'TestAbility index aboutToAppear');\n  }\n @State message: string = 'Hello World'\n   build() {\n         Row() {\n           Column() {\n             Text(this.message)\n               .fontSize(50)\n               .fontWeight(FontWeight.Bold)\n             Button() {\n               Text('next page')\n                 .fontSize(20)\n                 .fontWeight(FontWeight.Bold)\n             }.type(ButtonType.Capsule)\n             .margin({\n               top: 20\n             })\n             .backgroundColor('#0D9FFB')\n             .width('35%')\n             .height('5%')\n             .onClick(()=>{\n             })\n           }\n             .width('100%')\n         }\n             .height('100%')\n   }\n }"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/entry/src/ohosTest/ets/testrunner/OpenHarmonyTestRunner.ts",
    "content": "\nimport hilog from '@ohos.hilog';\nimport TestRunner from '@ohos.application.testRunner';\nimport AbilityDelegatorRegistry from '@ohos.app.ability.abilityDelegatorRegistry';\n\nvar abilityDelegator = undefined\nvar abilityDelegatorArguments = undefined\n\nasync function onAbilityCreateCallback() {\n    hilog.info(0x0000, 'testTag', '%{public}s', 'onAbilityCreateCallback');\n}\n\nasync function addAbilityMonitorCallback(err: any) {\n    hilog.info(0x0000, 'testTag', 'addAbilityMonitorCallback : %{public}s', JSON.stringify(err) ?? '');\n}\n\nexport default class OpenHarmonyTestRunner implements TestRunner {\n    constructor() {\n    }\n\n    onPrepare() {\n        hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner OnPrepare ');\n    }\n\n    async onRun() {\n        hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun run');\n        abilityDelegatorArguments = AbilityDelegatorRegistry.getArguments()\n        abilityDelegator = AbilityDelegatorRegistry.getAbilityDelegator()\n        var testAbilityName = abilityDelegatorArguments.bundleName + '.TestAbility'\n        let lMonitor = {\n            abilityName: testAbilityName,\n            onAbilityCreate: onAbilityCreateCallback,\n        };\n        abilityDelegator.addAbilityMonitor(lMonitor, addAbilityMonitorCallback)\n        var cmd = 'aa start -d 0 -a TestAbility' + ' -b ' + abilityDelegatorArguments.bundleName\n        var debug = abilityDelegatorArguments.parameters['-D']\n        if (debug == 'true')\n        {\n            cmd += ' -D'\n        }\n        hilog.info(0x0000, 'testTag', 'cmd : %{public}s', cmd);\n        abilityDelegator.executeShellCommand(cmd,\n            (err: any, d: any) => {\n                hilog.info(0x0000, 'testTag', 'executeShellCommand : err : %{public}s', JSON.stringify(err) ?? '');\n                hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.stdResult ?? '');\n                hilog.info(0x0000, 'testTag', 'executeShellCommand : data : %{public}s', d.exitCode ?? '');\n            })\n        hilog.info(0x0000, 'testTag', '%{public}s', 'OpenHarmonyTestRunner onRun end');\n    }\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/entry/src/ohosTest/module.json5",
    "content": "\n{\n  \"module\": {\n    \"name\": \"entry_test\",\n    \"type\": \"feature\",\n    \"description\": \"$string:module_test_desc\",\n    \"mainElement\": \"TestAbility\",\n    \"deviceTypes\": [\n      \"phone\"\n    ],\n    \"deliveryWithInstall\": true,\n    \"installationFree\": false,\n    \"pages\": \"$profile:test_pages\",\n    \"abilities\": [\n      {\n        \"name\": \"TestAbility\",\n        \"srcEntry\": \"./ets/testability/TestAbility.ets\",\n        \"description\": \"$string:TestAbility_desc\",\n        \"icon\": \"$media:icon\",\n        \"label\": \"$string:TestAbility_label\",\n        \"exported\": true,\n        \"startWindowIcon\": \"$media:icon\",\n        \"startWindowBackground\": \"$color:start_window_background\",\n        \"skills\": [\n          {\n            \"actions\": [\n              \"action.system.home\"\n            ],\n            \"entities\": [\n              \"entity.system.home\"\n            ]\n          }\n        ]\n      }\n    ]\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/entry/src/ohosTest/resources/base/element/color.json",
    "content": "{\n  \"color\": [\n    {\n      \"name\": \"start_window_background\",\n      \"value\": \"#FFFFFF\"\n    }\n  ]\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/entry/src/ohosTest/resources/base/element/string.json",
    "content": "{\n  \"string\": [\n    {\n      \"name\": \"module_test_desc\",\n      \"value\": \"test ability description\"\n    },\n    {\n      \"name\": \"TestAbility_desc\",\n      \"value\": \"the test ability\"\n    },\n    {\n      \"name\": \"TestAbility_label\",\n      \"value\": \"test label\"\n    }\n  ]\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/entry/src/ohosTest/resources/base/profile/test_pages.json",
    "content": "{\n  \"src\": [\n    \"testability/pages/Index\"\n  ]\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/hvigor/hvigor-config.json5",
    "content": "\n{ \n  \"modelVersion\": \"5.0.0\",\n  \"dependencies\": {\n  }\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/hvigorfile.ts",
    "content": "\nimport { appTasks } from '@ohos/hvigor-ohos-plugin';\n\nexport default {\n    system: appTasks,  /* Built-in plugin of Hvigor. It cannot be modified. */\n    plugins:[]         /* Custom plugin to extend the functionality of Hvigor. */\n}"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/ohos/oh-package.json5",
    "content": "\n\n{\n  \"modelVersion\": \"5.0.0\",\n  \"name\": \"multiple_flavors\",\n  \"version\": \"1.0.0\",\n  \"description\": \"Please describe the basic information.\",\n  \"main\": \"\",\n  \"author\": \"\",\n  \"license\": \"\",\n  \"dependencies\": {\n    \"@ohos/flutter_ohos\": \"file:./har/flutter.har\"\n  },\n  \"devDependencies\": {\n    \"@ohos/hypium\": \"1.0.6\"\n  },\n  \"overrides\": {\n    \"@ohos/flutter_ohos\": \"file:./har/flutter.har\"\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/pubspec.yaml",
    "content": "name: multiple_flavors\ndescription: A new Flutter project.\n\n# The following line prevents the package from being accidentally published to\n# pub.dev using `flutter pub publish`. This is preferred for private packages.\npublish_to: 'none' # Remove this line if you wish to publish to pub.dev\n\n# The following defines the version and build number for your application.\n# A version number is three numbers separated by dots, like 1.2.43\n# followed by an optional build number separated by a +.\n# Both the version and the builder number may be overridden in flutter\n# build by specifying --build-name and --build-number, respectively.\n# In Android, build-name is used as versionName while build-number used as versionCode.\n# Read more about Android versioning at https://developer.android.com/studio/publish/versioning\n# In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion.\n# Read more about iOS versioning at\n# https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html\nversion: 1.0.0+1\n\nenvironment:\n  sdk: \">=2.16.0 <4.0.0\"\n\n# Dependencies specify other packages that your package needs in order to work.\n# To automatically upgrade your package dependencies to the latest versions\n# consider running `flutter pub upgrade --major-versions`. Alternatively,\n# dependencies can be manually updated by changing the version numbers below to\n# the latest version available on pub.dev. To see which dependencies have newer\n# versions available, run `flutter pub outdated`.\ndependencies:\n  flutter:\n    sdk: flutter\n\n  package_info_plus: ^4.0.1\n\n  # The following adds the Cupertino Icons font to your application.\n  # Use with the CupertinoIcons class for iOS style icons.\n  cupertino_icons: ^1.0.2\n\ndev_dependencies:\n  flutter_test:\n    sdk: flutter\n\n  # The \"flutter_lints\" package below contains a set of recommended lints to\n  # encourage good coding practices. The lint set provided by the package is\n  # activated in the `analysis_options.yaml` file located at the root of your\n  # package. See that file for information about deactivating specific lint\n  # rules and activating additional ones.\n  flutter_lints: ^1.0.0\n\n  flutter_flavorizr: ^2.1.3\n\n# For information on the generic Dart part of this file, see the\n# following page: https://dart.dev/tools/pub/pubspec\n\n# The following section is specific to Flutter.\nflutter:\n\n  # The following line ensures that the Material Icons font is\n  # included with your application, so that you can use the icons in\n  # the material Icons class.\n  uses-material-design: true\n\n  # To add assets to your application, add an assets section, like this:\n  # assets:\n  #   - images/a_dot_burr.jpeg\n  #   - images/a_dot_ham.jpeg\n\n  # An image asset can refer to one or more resolution-specific \"variants\", see\n  # https://flutter.dev/assets-and-images/#resolution-aware.\n\n  # For details regarding adding assets from package dependencies, see\n  # https://flutter.dev/assets-and-images/#from-packages\n\n  # To add custom fonts to your application, add a fonts section here,\n  # in this \"flutter\" section. Each entry in this list should have a\n  # \"family\" key with the font family name, and a \"fonts\" key with a\n  # list giving the asset and other descriptors for the font. For\n  # example:\n  # fonts:\n  #   - family: Schyler\n  #     fonts:\n  #       - asset: fonts/Schyler-Regular.ttf\n  #       - asset: fonts/Schyler-Italic.ttf\n  #         style: italic\n  #   - family: Trajan Pro\n  #     fonts:\n  #       - asset: fonts/TrajanPro.ttf\n  #       - asset: fonts/TrajanPro_Bold.ttf\n  #         weight: 700\n  #\n  # For details regarding fonts from package dependencies,\n  # see https://flutter.dev/custom-fonts/#from-packages\n"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/release.sh",
    "content": "#!/bin/bash\n\ndart ../../packages/flutter_distributor/bin/main.dart release --name $1 --skip-clean"
  },
  {
    "path": "plugins/flutter_distributor/examples/multiple_flavors/test/widget_test.dart",
    "content": "// This is a basic Flutter widget test.\n//\n// To perform an interaction with a widget in your test, use the WidgetTester\n// utility that Flutter provides. For example, you can send tap and scroll\n// gestures. You can also use WidgetTester to find child widgets in the widget\n// tree, read text, and verify that the values of widget properties are correct.\n\nimport 'package:flutter/material.dart';\nimport 'package:flutter_test/flutter_test.dart';\n\nimport 'package:multiple_flavors/main.dart';\n\nvoid main() {\n  testWidgets('Counter increments smoke test', (WidgetTester tester) async {\n    // Build our app and trigger a frame.\n    await tester.pumpWidget(const MyApp());\n\n    // Verify that our counter starts at 0.\n    expect(find.text('0'), findsOneWidget);\n    expect(find.text('1'), findsNothing);\n\n    // Tap the '+' icon and trigger a frame.\n    await tester.tap(find.byIcon(Icons.add));\n    await tester.pump();\n\n    // Verify that our counter has incremented.\n    expect(find.text('0'), findsNothing);\n    expect(find.text('1'), findsOneWidget);\n  });\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/melos.yaml",
    "content": "name: flutter_distributor\nrepository: https://github.com/leanflutter/flutter_distributor\n\npackages:\n  - apps/**\n  - examples/**\n  - packages/**\n\ncommand:\n  bootstrap:\n    # Uses the pubspec_overrides.yaml instead of having Melos modifying the lock file.\n    usePubspecOverrides: true\n\nscripts:\n  analyze:\n    exec: flutter analyze --fatal-infos\n    description: Run `flutter analyze` for all packages.\n\n  test:\n    exec: flutter test\n    description: Run `flutter test` for a specific package.\n    packageFilters:\n      dirExists:\n        - test\n\n  format:\n    exec: dart format . --fix\n    description: Run `dart format` for all packages.\n\n  format-check:\n    exec: dart format . --fix --set-exit-if-changed\n    description: Run `dart format` checks for all packages.\n\n  fix:\n    exec: dart fix . --apply\n    description: Run `dart fix` for all packages.\n\n  dependency_validator:\n    exec: flutter pub run dependency_validator\n    packageFilters:\n      dependsOn:\n        - dependency_validator\n\n  activate:\n    run: melos exec --scope=\"flutter_distributor\" -- dart pub global activate -s path .\n\n  build_apk:\n    run: melos exec --scope=\"hello_world\" -- flutter_distributor release --name dev --jobs android-apk --skip-clean\n\n  build_ipa:\n    run: melos exec --scope=\"hello_world\" -- flutter_distributor release --name dev --jobs ios-ipa --skip-clean\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/fastforge/.gitignore",
    "content": ".dart_tool/\n.packages\nbuild/\npubspec.lock  # Except for application packages\n.flutter-plugins\n.flutter-plugins-dependencies\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/fastforge/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021-present LiJianying <lijy91@foxmail.com>\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": "plugins/flutter_distributor/packages/fastforge/analysis_options.yaml",
    "content": "include: package:mostly_reasonable_lints/analysis_options.yaml\n\nlinter:\n  rules:\n    avoid_print: false\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/fastforge/bin/main.dart",
    "content": "import 'package:unified_distributor/unified_distributor.dart';\n\nFuture<void> main(List<String> args) async {\n  final cli = UnifiedDistributorCommandLineInterface(\n    'fastforge',\n    'Package and publish your apps with ease.',\n    packageName: 'fastforge',\n    displayName: 'Fastforge',\n  );\n  return await cli.run(args);\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/fastforge/lib/fastforge.dart",
    "content": "library fastforge;\n\nimport 'package:unified_distributor/unified_distributor.dart';\n\n/// The main class for the Fastforge package.\n///\n/// This class extends the [UnifiedDistributor] class and provides a\n/// default implementation for the [UnifiedDistributor] class.\nclass Fastforge extends UnifiedDistributor {\n  /// Creates a new instance of the Fastforge class.\n  Fastforge() : super('fastforge', 'Fastforge');\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/fastforge/pubspec.yaml",
    "content": "name: fastforge\ndescription: A powerful and efficient tool for packaging and publishing your applications with ease.\nversion: 0.6.0\nhomepage: https://fastforge.dev\nrepository: https://github.com/fastforgedev/fastforge/tree/main/packages/fastforge\nissue_tracker: https://github.com/fastforgedev/fastforge/issues\n\nplatforms:\n  linux:\n  macos:\n  windows:\n\nenvironment:\n  sdk: \">=2.16.0 <4.0.0\"\n\ndependencies:\n  unified_distributor: ^0.2.0\n\ndev_dependencies:\n  dependency_validator: ^3.0.0\n  mostly_reasonable_lints: ^0.1.2\n  test: ^1.23.1\n\nexecutables:\n  fastforge: main\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/.gitignore",
    "content": ".dart_tool/\n.packages\nbuild/\npubspec.lock  # Except for application packages"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021-present LiJianying <lijy91@foxmail.com>\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": "plugins/flutter_distributor/packages/flutter_app_builder/analysis_options.yaml",
    "content": "include: package:mostly_reasonable_lints/analysis_options.yaml\n\nlinter:\n  rules:\n    avoid_print: false\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/lib/flutter_app_builder.dart",
    "content": "library flutter_app_builder;\n\nexport 'src/build_config.dart';\nexport 'src/build_result.dart';\nexport 'src/builders/app_builder.dart';\nexport 'src/builders/builders.dart';\nexport 'src/flutter_app_builder.dart';\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/lib/src/build_config.dart",
    "content": "enum BuildMode { profile, release }\n\nclass BuildConfig {\n  BuildConfig({\n    this.arguments = const {},\n  });\n\n  final Map<String, dynamic> arguments;\n\n  BuildMode get mode {\n    return arguments.containsKey('profile')\n        ? BuildMode.profile\n        : BuildMode.release;\n  }\n\n  String? get flavor {\n    return arguments['flavor'];\n  }\n\n  Map<String, dynamic> toJson() {\n    return {\n      'mode': mode.name,\n      'flavor': flavor,\n      'arguments': arguments,\n    }..removeWhere((key, value) => value == null);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/lib/src/build_error.dart",
    "content": "class BuildError extends Error {\n  BuildError([this.message]);\n\n  final String? message;\n\n  @override\n  String toString() {\n    var message = this.message;\n    return (message != null) ? 'BuildError: $message' : 'BuildError';\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/lib/src/build_result.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_builder/src/build_config.dart';\n\nabstract class BuildResult {\n  BuildResult(\n    this.config, {\n    this.duration,\n    this.outputFiles = const [],\n  });\n\n  final BuildConfig config;\n  Duration? duration;\n  Directory get outputDirectory;\n  List<File> outputFiles;\n\n  Map<String, dynamic> toJson() {\n    return {\n      'config': config.toJson(),\n      'outputDirectory': outputDirectory.path,\n      'duration': duration?.inMilliseconds,\n      'outputFiles': outputFiles.map((e) => e.path).toList(),\n    }..removeWhere((key, value) => value == null);\n  }\n}\n\nabstract class BuildResultResolver {\n  BuildResult resolve(BuildConfig config);\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/lib/src/builders/android/app_builder_android.dart",
    "content": "import 'package:flutter_app_builder/src/build_result.dart';\nimport 'package:flutter_app_builder/src/builders/android/build_android_result.dart';\nimport 'package:flutter_app_builder/src/builders/app_builder.dart';\n\nclass AppBuilderAndroid extends AppBuilder {\n  AppBuilderAndroid(this.target);\n\n  factory AppBuilderAndroid.apk() {\n    return AppBuilderAndroid('apk');\n  }\n\n  factory AppBuilderAndroid.aab() {\n    return AppBuilderAndroid('aab');\n  }\n\n  @override\n  String get platform => 'android';\n\n  @override\n  bool get isSupportedOnCurrentPlatform => true;\n\n  @override\n  BuildResultResolver get resultResolver => BuildAndroidResultResolver(target);\n\n  @override\n  String get buildSubcommand => target == 'aab' ? 'appbundle' : 'apk';\n\n  final String target;\n\n  @override\n  bool match(String platform, [String? target]) {\n    return this.platform == platform && this.target == target;\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/lib/src/builders/android/build_android_result.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_builder/src/build_config.dart';\nimport 'package:flutter_app_builder/src/build_result.dart';\nimport 'package:glob/glob.dart';\nimport 'package:glob/list_local_fs.dart';\nimport 'package:recase/recase.dart';\n\nclass BuildAndroidResultResolver extends BuildResultResolver {\n  BuildAndroidResultResolver(\n    this.target,\n  ) : _actualResultResolver = target == 'aab'\n            ? _BuildAndroidAabResultResolver()\n            : _BuildAndroidApkResultResolver();\n\n  factory BuildAndroidResultResolver.apk() {\n    return BuildAndroidResultResolver('apk');\n  }\n\n  factory BuildAndroidResultResolver.aab() {\n    return BuildAndroidResultResolver('aab');\n  }\n\n  final String target;\n\n  late BuildResultResolver _actualResultResolver;\n\n  @override\n  BuildResult resolve(BuildConfig config) {\n    return _actualResultResolver.resolve(config);\n  }\n}\n\nclass BuildAndroidResult extends BuildResult {\n  BuildAndroidResult(this.target, BuildConfig config)\n      : _actualResult = target == 'aab'\n            ? _BuildAndroidAabResult(config)\n            : _BuildAndroidApkResult(config),\n        super(config);\n\n  factory BuildAndroidResult.apk(BuildConfig config) {\n    return BuildAndroidResult('apk', config);\n  }\n\n  factory BuildAndroidResult.aab(BuildConfig config) {\n    return BuildAndroidResult(\n      'aab',\n      config,\n    );\n  }\n  final String target;\n\n  late BuildResult _actualResult;\n\n  @override\n  Directory get outputDirectory => _actualResult.outputDirectory;\n}\n\nclass _BuildAndroidAabResultResolver extends BuildResultResolver {\n  @override\n  BuildResult resolve(BuildConfig config) {\n    final r = _BuildAndroidAabResult(config);\n    final String pattern = [\n      '${r.outputDirectory.path}/**',\n      config.flavor != null ? '-${config.flavor}' : '',\n      '-${config.mode.name}.aab',\n    ].join();\n    r.outputFiles = Glob(pattern).listSync().map((e) => File(e.path)).toList();\n    return r;\n  }\n}\n\nclass _BuildAndroidAabResult extends BuildResult {\n  _BuildAndroidAabResult(BuildConfig config) : super(config);\n\n  @override\n  Directory get outputDirectory {\n    String buildMode = config.mode.name;\n    String path = 'build/app/outputs/bundle/$buildMode';\n    if (config.flavor != null) {\n      buildMode = ReCase(buildMode).sentenceCase;\n      path = 'build/app/outputs/bundle/${config.flavor}$buildMode';\n    }\n    return Directory(path);\n  }\n}\n\nclass _BuildAndroidApkResultResolver extends BuildResultResolver {\n  @override\n  BuildResult resolve(BuildConfig config, {Duration? duration}) {\n    final r = _BuildAndroidApkResult(config)..duration = duration;\n    final String pattern = [\n      '${r.outputDirectory.path}/**',\n      config.flavor != null ? '-${config.flavor}' : '',\n      '-${config.mode.name}.apk',\n    ].join();\n    r.outputFiles = Glob(pattern).listSync().map((e) => File(e.path)).toList();\n    return r;\n  }\n}\n\nclass _BuildAndroidApkResult extends BuildResult {\n  _BuildAndroidApkResult(BuildConfig config) : super(config);\n\n  @override\n  Directory get outputDirectory {\n    return Directory('build/app/outputs/flutter-apk');\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/lib/src/builders/app_builder.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_builder/src/build_config.dart';\nimport 'package:flutter_app_builder/src/build_error.dart';\nimport 'package:flutter_app_builder/src/build_result.dart';\nimport 'package:flutter_app_builder/src/commands/flutter.dart';\nimport 'package:pub_semver/pub_semver.dart';\nimport 'package:pubspec_parse/pubspec_parse.dart';\n\nabstract class AppBuilder {\n  String get platform => throw UnimplementedError();\n  bool get isSupportedOnCurrentPlatform => throw UnimplementedError();\n  BuildResultResolver get resultResolver;\n  String get buildSubcommand => platform;\n\n  String get appName => pubspec.name;\n  Version get appVersion => pubspec.version!;\n  String get appBuildName => appVersion.toString().split('+').first;\n  String get appBuildNumber => appVersion.toString().split('+').last;\n\n  Pubspec? _pubspec;\n\n  Pubspec get pubspec {\n    if (_pubspec == null) {\n      final yamlString = File('pubspec.yaml').readAsStringSync();\n      _pubspec = Pubspec.parse(yamlString);\n    }\n    return _pubspec!;\n  }\n\n  bool match(String platform, [String? target]) {\n    return this.platform == platform;\n  }\n\n  Future<BuildResult> build({\n    required Map<String, dynamic> arguments,\n    Map<String, String>? environment,\n  }) async {\n    final time = Stopwatch()..start();\n\n    BuildConfig config = BuildConfig(arguments: arguments);\n    List<String> buildArguments = [];\n    for (String key in config.arguments.keys) {\n      dynamic value = config.arguments[key];\n      if (value == null || value is bool) {\n        buildArguments.add('--$key');\n      } else if (value is Map) {\n        for (String subKey in value.keys) {\n          if(key == \"dart-define\"){\n            buildArguments.add('--$key=$subKey=${value[subKey]}');\n          }else{\n            buildArguments.addAll(['--$key', '$subKey=${value[subKey]}']);\n          }\n        }\n      } else {\n        buildArguments.addAll(['--$key', value]);\n      }\n    }\n\n    buildArguments.addAll([\n      '--dart-define',\n      'FLUTTER_BUILD_NAME=$appBuildName',\n      '--dart-define',\n      'FLUTTER_BUILD_NUMBER=$appBuildNumber',\n    ]);\n\n    ProcessResult processResult = await flutter.withEnv(environment).build(\n      [buildSubcommand, ...buildArguments],\n    );\n\n    if (processResult.exitCode != 0) {\n      throw BuildError('${processResult.stderr}');\n    }\n\n    return resultResolver.resolve(config)..duration = time.elapsed;\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/lib/src/builders/builders.dart",
    "content": "export 'android/app_builder_android.dart';\nexport 'android/build_android_result.dart';\nexport 'app_builder.dart';\nexport 'ios/app_builder_ios.dart';\nexport 'ios/build_ios_result.dart';\nexport 'linux/app_builder_linux.dart';\nexport 'linux/build_linux_result.dart';\nexport 'macos/app_builder_macos.dart';\nexport 'macos/build_macos_result.dart';\nexport 'web/app_builder_web.dart';\nexport 'web/build_web_result.dart';\nexport 'windows/app_builder_windows.dart';\nexport 'windows/build_windows_result.dart';\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/lib/src/builders/ios/app_builder_ios.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_builder/src/build_error.dart';\nimport 'package:flutter_app_builder/src/build_result.dart';\nimport 'package:flutter_app_builder/src/builders/app_builder.dart';\nimport 'package:flutter_app_builder/src/builders/ios/build_ios_result.dart';\n\nclass AppBuilderIos extends AppBuilder {\n  @override\n  String get platform => 'ios';\n\n  @override\n  bool get isSupportedOnCurrentPlatform => Platform.isMacOS;\n\n  @override\n  BuildResultResolver get resultResolver => BuildIosResultResolver();\n\n  @override\n  String get buildSubcommand => 'ipa';\n\n  @override\n  Future<BuildResult> build({\n    required Map<String, dynamic> arguments,\n    Map<String, String>? environment,\n  }) {\n    if (!arguments.containsKey('export-options-plist') &&\n        !arguments.containsKey('export-method')) {\n      throw BuildError(\n        'Missing `export-options-plist` or `export-method` build argument.',\n      );\n    }\n    return super.build(\n      arguments: arguments,\n      environment: environment,\n    );\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/lib/src/builders/ios/build_ios_result.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_builder/src/build_config.dart';\nimport 'package:flutter_app_builder/src/build_result.dart';\nimport 'package:glob/glob.dart';\nimport 'package:glob/list_local_fs.dart';\n\nclass BuildIosResultResolver extends BuildResultResolver {\n  @override\n  BuildResult resolve(BuildConfig config, {Duration? duration}) {\n    final r = BuildIosResult(config);\n    final String pattern = '${r.outputDirectory.path}/**.ipa';\n    List<FileSystemEntity> entities = Glob(pattern).listSync();\n    List<File> pkgFiles = (entities.map((e) => File(e.path)).toList())\n      ..sort((a, b) => b.lastModifiedSync().compareTo(a.lastModifiedSync()));\n    r.outputFiles = [pkgFiles.first];\n    return r;\n  }\n}\n\nclass BuildIosResult extends BuildResult {\n  BuildIosResult(BuildConfig config) : super(config);\n\n  @override\n  Directory get outputDirectory {\n    String path = 'build/ios/ipa';\n    return Directory(path);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/lib/src/builders/linux/app_builder_linux.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_builder/src/build_result.dart';\nimport 'package:flutter_app_builder/src/builders/app_builder.dart';\nimport 'package:flutter_app_builder/src/builders/linux/build_linux_result.dart';\n\nclass AppBuilderLinux extends AppBuilder {\n  @override\n  String get platform => 'linux';\n\n  @override\n  bool get isSupportedOnCurrentPlatform => Platform.isLinux;\n\n  @override\n  BuildResultResolver get resultResolver => BuildLinuxResultResolver();\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/lib/src/builders/linux/build_linux_result.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_builder/src/build_config.dart';\nimport 'package:flutter_app_builder/src/build_result.dart';\n\nclass BuildLinuxResultResolver extends BuildResultResolver {\n  @override\n  BuildResult resolve(BuildConfig config, {Duration? duration}) {\n    return BuildLinuxResult(config)..duration = duration;\n  }\n}\n\nclass BuildLinuxResult extends BuildResult {\n  BuildLinuxResult(BuildConfig config) : super(config);\n\n  String? _arch;\n\n  String get arch {\n    if (_arch == null) {\n      ProcessResult r = Process.runSync('uname', ['-m']);\n      if ('${r.stdout}'.trim() == 'aarch64') {\n        _arch = 'arm64';\n      } else {\n        _arch = 'x64';\n      }\n    }\n    return _arch!;\n  }\n\n  set arch(String value) {\n    _arch = value;\n  }\n\n  @override\n  Directory get outputDirectory {\n    String buildMode = config.mode.name;\n    String path = 'build/linux/$arch/$buildMode/bundle';\n    return Directory(path);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/lib/src/builders/macos/app_builder_macos.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_builder/src/build_result.dart';\nimport 'package:flutter_app_builder/src/builders/app_builder.dart';\nimport 'package:flutter_app_builder/src/builders/macos/build_macos_result.dart';\n\nclass AppBuilderMacOs extends AppBuilder {\n  @override\n  String get platform => 'macos';\n\n  @override\n  bool get isSupportedOnCurrentPlatform => Platform.isMacOS;\n\n  @override\n  BuildResultResolver get resultResolver => BuildMacOsResultResolver();\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/lib/src/builders/macos/build_macos_result.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_builder/src/build_config.dart';\nimport 'package:flutter_app_builder/src/build_result.dart';\nimport 'package:glob/glob.dart';\nimport 'package:glob/list_local_fs.dart';\nimport 'package:recase/recase.dart';\n\nclass BuildMacOsResultResolver extends BuildResultResolver {\n  @override\n  BuildResult resolve(BuildConfig config) {\n    final r = BuildMacOsResult(config);\n    final String pattern = '${r.outputDirectory.path}/*.app';\n    r.outputFiles = Glob(pattern).listSync().map((e) => File(e.path)).toList();\n    return r;\n  }\n}\n\nclass BuildMacOsResult extends BuildResult {\n  BuildMacOsResult(BuildConfig config) : super(config);\n\n  @override\n  Directory get outputDirectory {\n    String buildMode = ReCase(config.mode.name).sentenceCase;\n    String path = 'build/macos/Build/Products/$buildMode';\n    return Directory(path);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/lib/src/builders/ohos/app_builder_ohos.dart",
    "content": "import 'package:flutter_app_builder/src/build_result.dart';\nimport 'package:flutter_app_builder/src/builders/app_builder.dart';\nimport 'package:flutter_app_builder/src/builders/ohos/build_ohos_result.dart';\n\nclass AppBuilderOhos extends AppBuilder {\n  AppBuilderOhos(this.target);\n\n  factory AppBuilderOhos.hap() {\n    return AppBuilderOhos('hap');\n  }\n\n  factory AppBuilderOhos.app() {\n    return AppBuilderOhos('app');\n  }\n\n  @override\n  String get platform => 'ohos';\n\n  @override\n  bool get isSupportedOnCurrentPlatform => true;\n\n  @override\n  BuildResultResolver get resultResolver => BuildOhosResultResolver(target);\n\n  @override\n  String get buildSubcommand => target == 'hap' ? 'hap' : 'app';\n\n  final String target;\n\n  @override\n  bool match(String platform, [String? target]) {\n    return this.platform == platform && this.target == target;\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/lib/src/builders/ohos/build_ohos_result.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_builder/src/build_config.dart';\nimport 'package:flutter_app_builder/src/build_result.dart';\nimport 'package:glob/glob.dart';\nimport 'package:glob/list_local_fs.dart';\n\nclass BuildOhosResultResolver extends BuildResultResolver {\n  BuildOhosResultResolver(\n    this.target,\n  ) : _actualResultResolver = target == 'hap'\n            ? _BuildOhosHapResultResolver()\n            : _BuildOhosAppResultResolver();\n\n  factory BuildOhosResultResolver.hap() {\n    return BuildOhosResultResolver('hap');\n  }\n\n  factory BuildOhosResultResolver.app() {\n    return BuildOhosResultResolver('app');\n  }\n\n  final String target;\n\n  late BuildResultResolver _actualResultResolver;\n\n  @override\n  BuildResult resolve(BuildConfig config) {\n    return _actualResultResolver.resolve(config);\n  }\n}\n\nclass BuildOhosResult extends BuildResult {\n  BuildOhosResult(this.target, BuildConfig config)\n      : _actualResult = target == 'hap'\n            ? _BuildOhosHapResult(config)\n            : _BuildOhosAppResult(config),\n        super(config);\n\n  factory BuildOhosResult.hap(BuildConfig config) {\n    return BuildOhosResult('hap', config);\n  }\n\n  factory BuildOhosResult.app(BuildConfig config) {\n    return BuildOhosResult('app', config);\n  }\n\n  final String target;\n\n  late BuildResult _actualResult;\n\n  @override\n  Directory get outputDirectory => _actualResult.outputDirectory;\n}\n\nclass _BuildOhosHapResultResolver extends BuildResultResolver {\n  @override\n  BuildResult resolve(BuildConfig config) {\n    final r = _BuildOhosHapResult(config);\n    final String pattern = [\n      '${r.outputDirectory.path}/**',\n      '-${config.flavor ?? 'default'}',\n      '-signed.hap',\n    ].join();\n    r.outputFiles = Glob(pattern).listSync().map((e) => File(e.path)).toList();\n    return r;\n  }\n}\n\nclass _BuildOhosHapResult extends BuildResult {\n  _BuildOhosHapResult(BuildConfig config) : super(config);\n\n  @override\n  Directory get outputDirectory {\n    String flavor = config.flavor ?? 'default';\n    String path = 'ohos/entry/build/$flavor/outputs/$flavor';\n    return Directory(path);\n  }\n}\n\nclass _BuildOhosAppResultResolver extends BuildResultResolver {\n  @override\n  BuildResult resolve(BuildConfig config) {\n    final r = _BuildOhosAppResult(config);\n    final String pattern = [\n      '${r.outputDirectory.path}/**',\n      '-${config.flavor ?? 'default'}',\n      '-signed.app',\n    ].join();\n    r.outputFiles = Glob(pattern).listSync().map((e) => File(e.path)).toList();\n    return r;\n  }\n}\n\nclass _BuildOhosAppResult extends BuildResult {\n  _BuildOhosAppResult(BuildConfig config) : super(config);\n\n  @override\n  Directory get outputDirectory {\n    String flavor = config.flavor ?? 'default';\n    String path = 'ohos/build/outputs/$flavor';\n    return Directory(path);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/lib/src/builders/web/app_builder_web.dart",
    "content": "import 'package:flutter_app_builder/src/build_result.dart';\nimport 'package:flutter_app_builder/src/builders/app_builder.dart';\nimport 'package:flutter_app_builder/src/builders/web/build_web_result.dart';\n\nclass AppBuilderWeb extends AppBuilder {\n  @override\n  String get platform => 'web';\n\n  @override\n  bool get isSupportedOnCurrentPlatform => true;\n\n  @override\n  BuildResultResolver get resultResolver => BuildWebResultResolver();\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/lib/src/builders/web/build_web_result.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_builder/src/build_config.dart';\nimport 'package:flutter_app_builder/src/build_result.dart';\n\nclass BuildWebResultResolver extends BuildResultResolver {\n  @override\n  BuildResult resolve(BuildConfig config, {Duration? duration}) {\n    return BuildWebResult(config)..duration = duration;\n  }\n}\n\nclass BuildWebResult extends BuildResult {\n  BuildWebResult(BuildConfig config) : super(config);\n\n  @override\n  Directory get outputDirectory {\n    String path = 'build/web';\n    return Directory(path);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/lib/src/builders/windows/app_builder_windows.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_builder/src/build_result.dart';\nimport 'package:flutter_app_builder/src/builders/app_builder.dart';\nimport 'package:flutter_app_builder/src/builders/windows/build_windows_result.dart';\n\nclass AppBuilderWindows extends AppBuilder {\n  @override\n  String get platform => 'windows';\n\n  @override\n  bool get isSupportedOnCurrentPlatform => Platform.isWindows;\n\n  @override\n  BuildResultResolver get resultResolver => BuildWindowsResultResolver();\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/lib/src/builders/windows/build_windows_result.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_builder/src/build_config.dart';\nimport 'package:flutter_app_builder/src/build_result.dart';\nimport 'package:flutter_app_builder/src/commands/flutter.dart';\nimport 'package:pub_semver/pub_semver.dart';\nimport 'package:recase/recase.dart';\n\nbool currentVersionIsGreaterOrEqual(Version version, String versionString) {\n  return version.compareTo(Version.parse(versionString)) >= 0;\n}\n\nclass BuildWindowsResultResolver extends BuildResultResolver {\n  @override\n  BuildResult resolve(BuildConfig config, {Duration? duration}) {\n    return BuildWindowsResult(config)..duration = duration;\n  }\n}\n\nclass BuildWindowsResult extends BuildResult {\n  BuildWindowsResult(BuildConfig config) : super(config);\n\n  FlutterVersion? _flutterVersion;\n\n  FlutterVersion get flutterVersion {\n    _flutterVersion ??= flutter.version;\n    return _flutterVersion!;\n  }\n\n  set flutterVersion(FlutterVersion value) {\n    _flutterVersion = value;\n  }\n\n  String? _arch;\n\n  String get arch {\n    if (_arch == null) {\n      final processorArchitecture =\n          Platform.environment['PROCESSOR_ARCHITECTURE'];\n      if (processorArchitecture?.toUpperCase() == 'ARM64') {\n        _arch = 'arm64';\n      } else {\n        _arch = 'x64';\n      }\n    }\n    return _arch!;\n  }\n\n  set arch(String value) {\n    _arch = value;\n  }\n\n  @override\n  Directory get outputDirectory {\n    String buildMode = ReCase(config.mode.name).sentenceCase;\n    String path = 'build/windows/$arch/runner/$buildMode';\n    if (!flutterVersion.isGreaterOrEqual('3.15.0')) {\n      path = 'build/windows/runner/$buildMode';\n    }\n    return Directory(path);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/lib/src/commands/flutter.dart",
    "content": "import 'dart:convert';\nimport 'dart:io';\n\nimport 'package:path/path.dart' as p;\nimport 'package:pub_semver/pub_semver.dart';\nimport 'package:shell_executor/shell_executor.dart';\n\nclass FlutterVersion {\n  const FlutterVersion({\n    this.frameworkVersion,\n    this.channel,\n    this.repositoryUrl,\n    this.frameworkRevision,\n    this.frameworkCommitDate,\n    this.engineRevision,\n    this.dartSdkVersion,\n    this.devToolsVersion,\n    this.flutterVersion,\n  });\n\n  factory FlutterVersion.fromJson(Map<String, dynamic> json) {\n    return FlutterVersion(\n      frameworkVersion: json['frameworkVersion'] as String?,\n      channel: json['channel'] as String?,\n      repositoryUrl: json['repositoryUrl'] as String?,\n      frameworkRevision: json['frameworkRevision'] as String?,\n      frameworkCommitDate: json['frameworkCommitDate'] as String,\n      engineRevision: json['engineRevision'] as String?,\n      dartSdkVersion: json['dartSdkVersion'] as String?,\n      devToolsVersion: json['devToolsVersion'] as String?,\n      flutterVersion: json['flutterVersion'] as String? ??\n          (json['frameworkVersion'] as String?),\n    );\n  }\n\n  final String? frameworkVersion;\n  final String? channel;\n  final String? repositoryUrl;\n  final String? frameworkRevision;\n  final String? frameworkCommitDate;\n  final String? engineRevision;\n  final String? dartSdkVersion;\n  final String? devToolsVersion;\n  final String? flutterVersion;\n\n  bool isGreaterOrEqual(String versionString) {\n    // just keep the first part of the version string\n    final String currentVersionString = flutterVersion!.split('-').first;\n    final Version currentVersion = Version.parse(currentVersionString);\n    return currentVersion.compareTo(Version.parse(versionString)) >= 0;\n  }\n}\n\nclass _Flutter extends Command {\n  @override\n  String get executable {\n    String flutterRoot = environment?['FLUTTER_ROOT'] ?? '';\n    if (flutterRoot.isNotEmpty) {\n      flutterRoot = pathExpansion(flutterRoot, environment ?? {});\n      if (!Directory(flutterRoot).existsSync()) {\n        throw CommandError(\n          this,\n          'FLUTTER_ROOT environment variable is set to a path that does not exist: $flutterRoot',\n        );\n      }\n      return p.join(flutterRoot, 'bin', 'flutter');\n    }\n    return 'flutter';\n  }\n\n  Map<String, String>? environment;\n\n  _Flutter withEnv(Map<String, String>? environment) {\n    this.environment = environment;\n    return this;\n  }\n\n  FlutterVersion get version {\n    final result = execSync(\n      ['--version', '--machine'],\n      environment: environment,\n      runInShell: true,\n    );\n    final String jsonString = '${result.stdout}';\n    return FlutterVersion.fromJson(\n      Map<String, dynamic>.from(\n        json.decode(jsonString) as Map,\n      ),\n    );\n  }\n\n  Future<void> clean() {\n    return exec(\n      ['clean'],\n      environment: environment,\n    );\n  }\n\n  Future<ProcessResult> build(List<String> arguments) {\n    return exec(\n      ['build', ...arguments],\n      environment: environment,\n    );\n  }\n\n  @override\n  Future<void> install() {\n    throw UnimplementedError();\n  }\n}\n\nfinal flutter = _Flutter();\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/lib/src/flutter_app_builder.dart",
    "content": "import 'package:flutter_app_builder/src/build_result.dart';\nimport 'package:flutter_app_builder/src/builders/builders.dart';\nimport 'package:flutter_app_builder/src/commands/flutter.dart';\n\nclass FlutterAppBuilder {\n  final List<AppBuilder> _builders = [\n    AppBuilderAndroid.aab(),\n    AppBuilderAndroid.apk(),\n    AppBuilderIos(),\n    AppBuilderLinux(),\n    AppBuilderMacOs(),\n    AppBuilderWeb(),\n    AppBuilderWindows(),\n  ];\n\n  Future<void> clean({\n    Map<String, String>? environment,\n  }) async {\n    await flutter.withEnv(environment).clean();\n  }\n\n  Future<BuildResult> build(\n    String platform, {\n    String? target,\n    required Map<String, dynamic> arguments,\n    Map<String, String>? environment,\n  }) {\n    final builder = _builders.firstWhere((e) => e.match(platform, target));\n    if (!builder.isSupportedOnCurrentPlatform) {\n      throw UnsupportedError(\n        '${builder.runtimeType} is not supported on the current platform',\n      );\n    }\n    return builder.build(\n      arguments: arguments,\n      environment: environment,\n    );\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/pubspec.yaml",
    "content": "name: flutter_app_builder\ndescription: Build your Flutter app via Dart.\nversion: 0.4.2\nhomepage: https://distributor.leanflutter.dev\nrepository: https://github.com/leanflutter/flutter_distributor/tree/main/packages/flutter_app_builder\n\nenvironment:\n  sdk: \">=2.16.0 <4.0.0\"\n\ndependencies:\n  glob: ^2.1.1\n  path: ^1.8.1\n  pub_semver: ^2.1.0\n  pubspec_parse: ^1.1.0\n  recase: ^4.1.0\n  shell_executor: ^0.1.5\n\ndev_dependencies:\n  dependency_validator: ^3.0.0\n  test: ^1.23.1\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/test/src/build_config_test.dart",
    "content": "import 'package:flutter_app_builder/src/build_config.dart';\nimport 'package:test/test.dart';\n\nvoid main() {\n  group('build config', () {\n    test('mode', () {\n      final profileConfig = BuildConfig(\n        arguments: {'profile': true},\n      );\n      expect(profileConfig.mode, BuildMode.profile);\n      final releaseCconfig = BuildConfig();\n      expect(releaseCconfig.mode, BuildMode.release);\n    });\n    test('flavor', () {\n      final config = BuildConfig(\n        arguments: {'flavor': 'dev'},\n      );\n      expect(config.flavor, 'dev');\n    });\n  });\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/test/src/builders/android/build_android_result_test.dart",
    "content": "import 'package:flutter_app_builder/src/build_config.dart';\nimport 'package:flutter_app_builder/src/builders/android/build_android_result.dart';\nimport 'package:test/test.dart';\n\nvoid main() {\n  group('android aab result', () {\n    test('profile mode', () {\n      final r = BuildAndroidResult.aab(\n        BuildConfig(\n          arguments: {'profile': true},\n        ),\n      );\n      expect(r.outputDirectory.path, 'build/app/outputs/bundle/profile');\n    });\n    test('profile mode + flavor', () {\n      final r = BuildAndroidResult.aab(\n        BuildConfig(\n          arguments: {'profile': true, 'flavor': 'dev'},\n        ),\n      );\n      expect(r.outputDirectory.path, 'build/app/outputs/bundle/devProfile');\n    });\n    test('release mode', () {\n      final r = BuildAndroidResult.aab(\n        BuildConfig(),\n      );\n      expect(r.outputDirectory.path, 'build/app/outputs/bundle/release');\n    });\n    test('release mode + flavor', () {\n      final r = BuildAndroidResult.aab(\n        BuildConfig(\n          arguments: {'flavor': 'dev'},\n        ),\n      );\n      expect(r.outputDirectory.path, 'build/app/outputs/bundle/devRelease');\n    });\n  });\n  group('android apk result', () {\n    test('profile mode', () {\n      final r = BuildAndroidResult.apk(\n        BuildConfig(\n          arguments: {'profile': true},\n        ),\n      );\n      expect(r.outputDirectory.path, 'build/app/outputs/flutter-apk');\n    });\n    test('profile mode + flavor', () {\n      final r = BuildAndroidResult.apk(\n        BuildConfig(\n          arguments: {'profile': true, 'flavor': 'dev'},\n        ),\n      );\n      expect(r.outputDirectory.path, 'build/app/outputs/flutter-apk');\n    });\n    test('release mode', () {\n      final r = BuildAndroidResult.apk(\n        BuildConfig(),\n      );\n      expect(r.outputDirectory.path, 'build/app/outputs/flutter-apk');\n    });\n\n    test('release mode + flavor', () {\n      final r = BuildAndroidResult.apk(\n        BuildConfig(\n          arguments: {'flavor': 'dev'},\n        ),\n      );\n      String dirPath = r.outputDirectory.path;\n      expect(dirPath, 'build/app/outputs/flutter-apk');\n    });\n  });\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/test/src/builders/ios/build_ios_result_test.dart",
    "content": "import 'package:flutter_app_builder/src/build_config.dart';\nimport 'package:flutter_app_builder/src/builders/ios/build_ios_result.dart';\nimport 'package:test/test.dart';\n\nvoid main() {\n  group('ios result', () {\n    test('profile mode', () {\n      final r = BuildIosResult(\n        BuildConfig(\n          arguments: {'profile': true},\n        ),\n      );\n      expect(r.outputDirectory.path, 'build/ios/ipa');\n    });\n    test('release mode', () {\n      final r = BuildIosResult(\n        BuildConfig(),\n      );\n      expect(r.outputDirectory.path, 'build/ios/ipa');\n    });\n  });\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/test/src/builders/linux/build_linux_result_test.dart",
    "content": "import 'package:flutter_app_builder/src/build_config.dart';\nimport 'package:flutter_app_builder/src/builders/linux/build_linux_result.dart';\nimport 'package:test/test.dart';\n\nvoid main() {\n  group('linux result', () {\n    test('profile mode', () {\n      final r = BuildLinuxResult(\n        BuildConfig(\n          arguments: {'profile': true},\n        ),\n      );\n      r.arch = 'x64';\n      expect(r.outputDirectory.path, 'build/linux/x64/profile/bundle');\n      r.arch = 'arm64';\n      expect(r.outputDirectory.path, 'build/linux/arm64/profile/bundle');\n    });\n    test('release mode', () {\n      final r = BuildLinuxResult(\n        BuildConfig(),\n      );\n      r.arch = 'x64';\n      expect(r.outputDirectory.path, 'build/linux/x64/release/bundle');\n      r.arch = 'arm64';\n      expect(r.outputDirectory.path, 'build/linux/arm64/release/bundle');\n    });\n  });\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/test/src/builders/macos/build_ios_result_test.dart",
    "content": "import 'package:flutter_app_builder/src/build_config.dart';\nimport 'package:flutter_app_builder/src/builders/macos/build_macos_result.dart';\nimport 'package:test/test.dart';\n\nvoid main() {\n  group('macos result', () {\n    test('profile mode', () {\n      final r = BuildMacOsResult(\n        BuildConfig(\n          arguments: {'profile': true},\n        ),\n      );\n      expect(r.outputDirectory.path, 'build/macos/Build/Products/Profile');\n    });\n    test('release mode', () {\n      final r = BuildMacOsResult(\n        BuildConfig(),\n      );\n      expect(r.outputDirectory.path, 'build/macos/Build/Products/Release');\n    });\n  });\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/test/src/builders/web/build_web_result_test.dart",
    "content": "import 'package:flutter_app_builder/src/build_config.dart';\nimport 'package:flutter_app_builder/src/builders/web/build_web_result.dart';\nimport 'package:test/test.dart';\n\nvoid main() {\n  group('web result', () {\n    test('profile mode', () {\n      final r = BuildWebResult(\n        BuildConfig(\n          arguments: {'profile': true},\n        ),\n      );\n      expect(r.outputDirectory.path, 'build/web');\n    });\n    test('release mode', () {\n      final r = BuildWebResult(\n        BuildConfig(),\n      );\n      expect(r.outputDirectory.path, 'build/web');\n    });\n  });\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/test/src/builders/windows/build_windows_result_test.dart",
    "content": "import 'package:flutter_app_builder/src/build_config.dart';\nimport 'package:flutter_app_builder/src/builders/windows/build_windows_result.dart';\nimport 'package:flutter_app_builder/src/commands/flutter.dart';\nimport 'package:test/test.dart';\n\nvoid main() {\n  group('windows result', () {\n    test('profile mode', () {\n      final r = BuildWindowsResult(\n        BuildConfig(\n          arguments: {'profile': true},\n        ),\n      );\n      r.flutterVersion = const FlutterVersion(flutterVersion: '3.16.0');\n      expect(r.outputDirectory.path, 'build/windows/x64/runner/Profile');\n    });\n    test('profile mode (less 3.15.0)', () {\n      final r = BuildWindowsResult(\n        BuildConfig(\n          arguments: {'profile': true},\n        ),\n      );\n      r.flutterVersion = const FlutterVersion(flutterVersion: '3.10.0');\n      expect(r.outputDirectory.path, 'build/windows/runner/Profile');\n    });\n    test('release mode', () {\n      final r = BuildWindowsResult(\n        BuildConfig(),\n      );\n      r.flutterVersion = const FlutterVersion(flutterVersion: '3.16.0');\n      expect(r.outputDirectory.path, 'build/windows/x64/runner/Release');\n    });\n    test('release mode (less 3.15.0)', () {\n      final r = BuildWindowsResult(\n        BuildConfig(),\n      );\n      r.flutterVersion = const FlutterVersion(flutterVersion: '3.10.0');\n      expect(r.outputDirectory.path, 'build/windows/runner/Release');\n    });\n  });\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_builder/test/src/commands/flutter_test.dart",
    "content": "import 'package:flutter_app_builder/src/commands/flutter.dart';\nimport 'package:test/test.dart';\n\nvoid main() {\n  group('FlutterVersion', () {\n    test('isGreaterOrEqual#1', () {\n      const v3100 = FlutterVersion(\n        flutterVersion: '3.10.0',\n      );\n      expect(v3100.isGreaterOrEqual('3.3.10'), true);\n      expect(v3100.isGreaterOrEqual('3.10.0'), true);\n      expect(v3100.isGreaterOrEqual('3.10.1'), false);\n    });\n    test('isGreaterOrEqual#2', () {\n      const v3150 = FlutterVersion(\n        flutterVersion: '3.15.0-15.2.pre',\n      );\n      expect(v3150.isGreaterOrEqual('3.3.10'), true);\n      expect(v3150.isGreaterOrEqual('3.15.0'), true);\n      expect(v3150.isGreaterOrEqual('3.16.1'), false);\n    });\n  });\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/.gitignore",
    "content": ".dart_tool/\n.packages\nbuild/\npubspec.lock  # Except for application packages\n.flutter-plugins\n.flutter-plugins-dependencies\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021-present LiJianying <lijy91@foxmail.com>\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": "plugins/flutter_distributor/packages/flutter_app_packager/analysis_options.yaml",
    "content": "include: package:mostly_reasonable_lints/analysis_options.yaml\n\nlinter:\n  rules:\n    avoid_print: false\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/flutter_app_packager.dart",
    "content": "library flutter_app_packager;\n\nexport 'src/api/app_package_maker.dart';\nexport 'src/api/make_config.dart';\nexport 'src/api/make_error.dart';\nexport 'src/api/make_result.dart';\nexport 'src/flutter_app_packager.dart';\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/api/app_package_maker.dart",
    "content": "import 'dart:convert';\nimport 'dart:io';\n\nimport 'package:flutter_app_packager/src/api/make_config.dart';\nimport 'package:flutter_app_packager/src/api/make_result.dart';\nimport 'package:shell_executor/shell_executor.dart';\nimport 'package:yaml/yaml.dart';\n\nexport 'make_config.dart';\nexport 'make_error.dart';\nexport 'make_result.dart';\n\nMap<String, dynamic> loadMakeConfigYaml(String path) {\n  final yamlDoc = loadYaml(File(path).readAsStringSync());\n  return json.decode(json.encode(yamlDoc));\n}\n\nabstract class AppPackageMaker {\n  List<Command> get requirements => [];\n\n  String get name => throw UnimplementedError();\n  String get platform => throw UnimplementedError();\n  bool get isSupportedOnCurrentPlatform => true;\n  String get packageFormat => throw UnimplementedError();\n\n  MakeConfigLoader get configLoader {\n    return DefaultMakeConfigLoader()\n      ..platform = platform\n      ..packageFormat = packageFormat;\n  }\n\n  MakeResultResolver get resultResolver => DefaultMakeResultResolver();\n\n  bool match(String platform, [String? target]) {\n    return this.platform == platform && name == target;\n  }\n\n  Future<MakeResult> make(MakeConfig config) {\n    throw UnimplementedError();\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/api/distribute_options_base.dart",
    "content": "class DistributeOptionsBase {\n\n  DistributeOptionsBase({\n    this.appName,\n  });\n\n  factory DistributeOptionsBase.fromJson(Map<String, dynamic> json) {\n    return DistributeOptionsBase(\n      appName: json['app_name']\n    );\n  }\n\n  final String? appName;\n}"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/api/make_config.dart",
    "content": "import 'dart:convert';\nimport 'dart:io';\n\nimport 'package:flutter_app_packager/src/api/make_error.dart';\nimport 'package:flutter_app_packager/src/api/distribute_options_base.dart';\nimport 'package:mustache_template/mustache.dart';\nimport 'package:pub_semver/pub_semver.dart';\nimport 'package:pubspec_parse/pubspec_parse.dart';\nimport 'package:yaml/yaml.dart';\n\nconst _kArtifactName =\n    '{{name}}-{{build_name}}-{{platform}}{{#description}}-{{description}}{{/description}}{{#is_installer}}-setup{{/is_installer}}{{#ext}}.{{ext}}{{/ext}}';\n\nclass MakeConfig {\n  late bool isInstaller = false;\n  late String buildMode;\n  late Directory buildOutputDirectory;\n  late List<File> buildOutputFiles;\n  late String platform;\n  String? flavor;\n  String? arch;\n  String? channel;\n  String? description;\n\n  /// https://mustache.github.io/mustache.5.html\n  String? artifactName;\n  late String packageFormat;\n  late Directory outputDirectory;\n\n  String get appName => distributeOptionsBase.appName ?? pubspec.name;\n\n  String get appBinaryName => distributeOptionsBase.appName ?? pubspec.name;\n\n  Version get appVersion => pubspec.version!;\n\n  String get appBuildName => appVersion.toString().split('+').first;\n\n  String get appBuildNumber => appVersion.toString().split('+').last;\n\n  Pubspec? _pubspec;\n  DistributeOptionsBase? _distributeOptionsBase;\n  Directory? _packagingDirectory;\n\n  MakeConfig copyWith(MakeConfig makeConfig) {\n    buildMode = makeConfig.buildMode;\n    buildOutputDirectory = makeConfig.buildOutputDirectory;\n    buildOutputFiles = makeConfig.buildOutputFiles;\n    platform = makeConfig.platform;\n    description = makeConfig.description;\n    arch = makeConfig.arch;\n    flavor = makeConfig.flavor;\n    channel = makeConfig.channel;\n    artifactName = makeConfig.artifactName;\n    packageFormat = makeConfig.packageFormat;\n    outputDirectory = makeConfig.outputDirectory;\n    return this;\n  }\n\n  File get outputFile {\n    if (packageFormat.isEmpty) {\n      throw MakeError('Direct output is not a file');\n    }\n    return File(outputArtifactPath);\n  }\n\n  String get outputArtifactPath {\n    String useArtifactName = _kArtifactName;\n    if (artifactName != null) useArtifactName = artifactName!;\n\n    Map<String, dynamic> variables = {\n      'is_installer': isInstaller,\n      'is_profile': buildMode == 'profile',\n      'name': appName,\n      'version': appVersion.toString(),\n      'build_name': appBuildName,\n      'build_number': appBuildNumber,\n      'build_mode': buildMode,\n      'platform': platform,\n      'flavor': flavor,\n      'channel': channel,\n      'description': description,\n      'ext': packageFormat.isEmpty ? null : packageFormat,\n    };\n\n    String filename = Template(useArtifactName).renderString(variables);\n\n    Directory versionOutputDirectory = Directory(outputDirectory.path);\n\n    if (!versionOutputDirectory.existsSync()) {\n      versionOutputDirectory.createSync(recursive: true);\n    }\n\n    return '${versionOutputDirectory.path}/$filename';\n  }\n\n  List<FileSystemEntity> get outputArtifacts {\n    List<FileSystemEntity> artifacts = [];\n    if (packageFormat.isEmpty) {\n      artifacts.add(Directory(outputArtifactPath));\n    } else {\n      artifacts.add(File(outputArtifactPath));\n    }\n    return artifacts;\n  }\n\n  Directory get packagingDirectory {\n    if (_packagingDirectory == null) {\n      _packagingDirectory = Directory(\n        outputArtifactPath.replaceAll('.$packageFormat', '_$packageFormat'),\n      );\n      if (_packagingDirectory!.existsSync()) {\n        _packagingDirectory!.deleteSync(recursive: true);\n      }\n      _packagingDirectory!.createSync(recursive: true);\n    }\n    return _packagingDirectory!;\n  }\n\n  Pubspec get pubspec {\n    if (_pubspec == null) {\n      final yamlString = File('pubspec.yaml').readAsStringSync();\n      _pubspec = Pubspec.parse(yamlString);\n    }\n    return _pubspec!;\n  }\n\n  DistributeOptionsBase get distributeOptionsBase {\n    if (_distributeOptionsBase == null) {\n      File file = File('distribute_options.yaml');\n      if (file.existsSync()) {\n        final yamlString = File('distribute_options.yaml').readAsStringSync();\n        final yamlDoc = loadYaml(yamlString);\n        _distributeOptionsBase = DistributeOptionsBase.fromJson(\n          json.decode(json.encode(yamlDoc)),\n        );\n      } else {\n        _distributeOptionsBase = DistributeOptionsBase();\n      }\n    }\n    return _distributeOptionsBase!;\n  }\n\n  Map<String, dynamic> toJson() {\n    return {\n      'isInstaller': isInstaller,\n      'buildMode': buildMode,\n      'buildOutputDirectory': buildOutputDirectory.path,\n      'buildOutputFiles': buildOutputFiles.map((e) => e.path).toList(),\n      'platform': platform,\n      'arch': arch,\n      'description': description,\n      'flavor': flavor,\n      'channel': channel,\n      'artifactName': artifactName,\n      'packageFormat': packageFormat,\n      'outputDirectory': outputDirectory.path,\n      'appName': appName,\n      'appVersion': appVersion.toString(),\n      'appBuildName': appBuildName,\n      'appBuildNumber': appBuildNumber,\n    }..removeWhere((key, value) => value == null);\n  }\n}\n\nabstract class MakeConfigLoader {\n  late String platform;\n  late String packageFormat;\n\n  MakeConfig load(\n    Map<String, dynamic>? arguments,\n    Directory outputDirectory, {\n    required Directory buildOutputDirectory,\n    required List<File> buildOutputFiles,\n  });\n}\n\nclass DefaultMakeConfigLoader extends MakeConfigLoader {\n  @override\n  MakeConfig load(\n    Map<String, dynamic>? arguments,\n    Directory outputDirectory, {\n    required Directory buildOutputDirectory,\n    required List<File> buildOutputFiles,\n  }) {\n    return MakeConfig()\n      ..platform = platform\n      ..arch = arguments?['arch']\n      ..buildMode = arguments?['build_mode']\n      ..buildOutputDirectory = buildOutputDirectory\n      ..buildOutputFiles = buildOutputFiles\n      ..flavor = arguments?['flavor']\n      ..description = arguments?['description']\n      ..channel = arguments?['channel']\n      ..artifactName = arguments?['artifact_name']\n      ..packageFormat = packageFormat\n      ..outputDirectory = outputDirectory;\n  }\n}\n\nclass MakeLinuxPackageConfig extends MakeConfig {\n  String? _appBinaryName;\n\n  @override\n  String get appBinaryName {\n    if (_appBinaryName == null) {\n      final cMakeListsFile = File('linux/CMakeLists.txt');\n      final RegExp regex = RegExp(r'(?<=set\\(BINARY_NAME\\s\")[^\"]+(?=\"\\))');\n      final Match? match = regex.firstMatch(cMakeListsFile.readAsStringSync());\n\n      if (match != null) {\n        final String? binaryName = match.group(0);\n        _appBinaryName = binaryName;\n      } else {\n        _appBinaryName = appName;\n      }\n    }\n    return _appBinaryName!;\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/api/make_error.dart",
    "content": "class MakeError extends Error {\n  MakeError([this.message]);\n\n  final String? message;\n\n  @override\n  String toString() {\n    var message = this.message;\n    return (message != null) ? 'MakeError: $message' : 'MakeError';\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/api/make_result.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_packager/src/api/make_config.dart';\nimport 'package:flutter_app_packager/src/api/make_error.dart';\n\nclass MakeResult {\n  MakeResult(\n    this.config, {\n    this.duration,\n  }) : artifacts = config.outputArtifacts;\n\n  final MakeConfig config;\n  final List<FileSystemEntity> artifacts;\n  final Duration? duration;\n\n  Map<String, dynamic> toJson() {\n    return {\n      'config': config.toJson(),\n      'artifacts': artifacts\n          .map(\n            (e) => {\n              'type': e is File ? 'file' : 'directory',\n              'path': e.path,\n            },\n          )\n          .toList(),\n      'duration': duration,\n    }..removeWhere((key, value) => value == null);\n  }\n}\n\nabstract class MakeResultResolver {\n  MakeResult resolve(MakeConfig config);\n}\n\nclass DefaultMakeResultResolver extends MakeResultResolver {\n  @override\n  MakeResult resolve(MakeConfig config) {\n    MakeResult makeResult = MakeResult(config);\n    return makeResult;\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/flutter_app_packager.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_packager/src/api/app_package_maker.dart';\nimport 'package:flutter_app_packager/src/makers/makers.dart';\n\nclass FlutterAppPackager {\n  final List<AppPackageMaker> _makers = [\n    AppPackageMakerAab(),\n    AppPackageMakerApk(),\n    AppPackageMakerAppImage(),\n    AppPackageMakerDeb(),\n    AppPackageMakerDirect('linux'),\n    AppPackageMakerDirect('windows'),\n    AppPackageMakerDirect('web'),\n    AppPackageMakerDmg(),\n    AppPackageMakerExe(),\n    AppPackageMakerIpa(),\n    AppPackageMakerMsix(),\n    AppPackageMakerPkg(),\n    AppPackageMakerRPM(),\n    AppPackageMakerZip('linux'),\n    AppPackageMakerZip('macos'),\n    AppPackageMakerZip('windows'),\n    AppPackageMakerZip('web'),\n  ];\n\n  Future<MakeResult> package(\n    String platform,\n    String target,\n    Map<String, dynamic>? arguments,\n    Directory outputDirectory, {\n    required Directory buildOutputDirectory,\n    required List<File> buildOutputFiles,\n  }) {\n    final maker = _makers.firstWhere((e) => e.match(platform, target));\n    final config = maker.configLoader.load(\n      arguments,\n      outputDirectory,\n      buildOutputDirectory: buildOutputDirectory,\n      buildOutputFiles: buildOutputFiles,\n    );\n    return maker.make(config);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/aab/app_package_maker_aab.dart",
    "content": "import 'package:flutter_app_packager/src/api/app_package_maker.dart';\n\nclass AppPackageMakerAab extends AppPackageMaker {\n  @override\n  String get name => 'aab';\n  @override\n  String get platform => 'android';\n  @override\n  String get packageFormat => 'aab';\n\n  @override\n  Future<MakeResult> make(MakeConfig config) {\n    config.buildOutputFiles.first.copySync(config.outputFile.path);\n    return Future.value(resultResolver.resolve(config));\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/apk/app_package_maker_apk.dart",
    "content": "import 'package:flutter_app_packager/src/api/app_package_maker.dart';\n\nclass AppPackageMakerApk extends AppPackageMaker {\n  @override\n  String get name => 'apk';\n\n  @override\n  String get platform => 'android';\n\n  @override\n  String get packageFormat => 'apk';\n\n  @override\n  Future<MakeResult> make(MakeConfig config) {\n    for (final file in config.buildOutputFiles) {\n      final splits = file.uri.pathSegments.last.split('-');\n      final outputPath = config.outputFile.path;\n      if (splits.length > 2) {\n        final sublist = splits.sublist(1, splits.length - 1);\n        final lastDotIndex = outputPath.lastIndexOf('.');\n        final firstPart = outputPath.substring(0, lastDotIndex);\n        final lastPart = outputPath.substring(lastDotIndex + 1);\n        final output = '$firstPart-${sublist.join('-')}.${lastPart}';\n        file.copySync(output);\n      } else {\n        file.copySync(outputPath);\n      }\n    }\n    return Future.value(resultResolver.resolve(config));\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/app/app_package_maker_app.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_packager/src/api/app_package_maker.dart';\n\nclass AppPackageMakerApp extends AppPackageMaker {\n  @override\n  String get name => 'app';\n  @override\n  String get platform => 'ohos';\n  @override\n  String get packageFormat => 'app';\n\n  @override\n  Future<MakeResult> make(MakeConfig config) {\n    File pkgFile = config.buildOutputFiles.first;\n    pkgFile.copySync(config.outputFile.path);\n    return Future.value(resultResolver.resolve(config));\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/appimage/app_package_maker_appimage.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_packager/src/api/app_package_maker.dart';\nimport 'package:flutter_app_packager/src/makers/appimage/make_appimage_config.dart';\nimport 'package:path/path.dart' as path;\nimport 'package:shell_executor/shell_executor.dart';\n\nclass AppPackageMakerAppImage extends AppPackageMaker {\n  @override\n  String get name => 'appimage';\n  @override\n  String get platform => 'linux';\n  @override\n  bool get isSupportedOnCurrentPlatform => Platform.isLinux;\n  @override\n  String get packageFormat => 'appimage';\n\n  @override\n  MakeConfigLoader get configLoader {\n    return MakeAppImageConfigLoader()\n      ..platform = platform\n      ..packageFormat = packageFormat;\n  }\n\n  Future<Set<String>> _getSharedDependencies(String so) {\n    return $('ldd', ['-d', so])\n        .then((value) {\n          if (value.exitCode != 0) {\n            throw MakeError(value.stderr as String);\n          }\n          return value.stdout as String;\n        })\n        .then((lines) {\n          final soDeps =\n              lines\n                  .split('\\n')\n                  .where(\n                    (line) =>\n                        line.contains('=>') && line.trim().startsWith('lib'),\n                  )\n                  /// converts this:\n                  ///  libkeybinder-3.0.so.0 => /lib64/libkeybinder-3.0.so.0 (0x00007f6513811000)\n                  /// to this:\n                  ///  /lib64/libkeybinder-3.0.so.0\n                  .map(\n                    (line) =>\n                        line.split(' => ')[1].trim().split(' ').first.trim(),\n                  )\n                  .toList()\n                ..sort();\n\n          return soDeps.toSet();\n        });\n  }\n\n  @override\n  Future<MakeResult> make(MakeConfig config) {\n    return _make(\n      config.buildOutputDirectory,\n      outputDirectory: config.outputDirectory,\n      makeConfig: config as MakeAppImageConfig,\n    );\n  }\n\n  Future<MakeResult> _make(\n    Directory appDirectory, {\n    required Directory outputDirectory,\n    required MakeAppImageConfig makeConfig,\n  }) async {\n    try {\n      await $('cp', [\n        '-r',\n        appDirectory.path,\n        path.join(\n          makeConfig.packagingDirectory.path,\n          '${makeConfig.appName}.AppDir',\n        ),\n      ]).then((value) {\n        if (value.exitCode != 0) {\n          throw MakeError(value.stderr as String);\n        }\n      });\n\n      final desktopFile = File(\n        path.join(\n          makeConfig.packagingDirectory.path,\n          '${makeConfig.appName}.AppDir',\n          '${makeConfig.appName}.desktop',\n        ),\n      )..createSync(recursive: true);\n\n      await desktopFile.writeAsString(makeConfig.desktopFileContent);\n\n      final appRunFile = File(\n        path.join(\n          makeConfig.packagingDirectory.path,\n          '${makeConfig.appName}.AppDir',\n          'AppRun',\n        ),\n      )..createSync(recursive: true);\n\n      await appRunFile.writeAsString(makeConfig.appRunContent);\n\n      await $('chmod', ['+x', appRunFile.path]).then((value) {\n        if (value.exitCode != 0) {\n          throw MakeError(value.stderr as String);\n        }\n      });\n\n      final iconFile = File(makeConfig.icon);\n      if (!iconFile.existsSync()) {\n        throw MakeError(\"icon ${makeConfig.icon} path doesn't exist\");\n      }\n\n      await iconFile.copy(\n        path.join(\n          makeConfig.packagingDirectory.path,\n          '${makeConfig.appName}.AppDir',\n          '${makeConfig.appName}${path.extension(makeConfig.icon)}',\n        ),\n      );\n\n      final icon256x256 = path.join(\n        makeConfig.packagingDirectory.path,\n        '${makeConfig.appName}.AppDir/usr/share/icons/hicolor/256x256/apps',\n      );\n      final icon128x128 = path.join(\n        makeConfig.packagingDirectory.path,\n        '${makeConfig.appName}.AppDir/usr/share/icons/hicolor/128x128/apps',\n      );\n\n      await $('mkdir', ['-p', icon128x128, icon256x256]).then((value) {\n        if (value.exitCode != 0) {\n          throw MakeError(value.stderr as String);\n        }\n      });\n\n      await iconFile.copy(\n        path.join(\n          icon128x128,\n          '${makeConfig.appName}${path.extension(makeConfig.icon)}',\n        ),\n      );\n\n      await iconFile.copy(\n        path.join(\n          icon256x256,\n          '${makeConfig.appName}${path.extension(makeConfig.icon)}',\n        ),\n      );\n\n      final defaultSharedObjects = [\n        'libapp.so',\n        'libflutter_linux_gtk.so',\n        'libgtk-3.so.0',\n      ];\n\n      final appSOLibs =\n          Directory(\n            path.join(\n              makeConfig.packagingDirectory.path,\n              '${makeConfig.appName}.AppDir/lib',\n            ),\n          ).listSync().where(\n            (e) => !defaultSharedObjects.contains(path.basename(e.path)),\n          );\n\n      await $('mkdir', [\n        '-p',\n        path.join(\n          makeConfig.packagingDirectory.path,\n          '${makeConfig.appName}.AppDir/usr/lib',\n        ),\n      ]).then((value) {\n        if (value.exitCode != 0) {\n          throw MakeError(value.stderr as String);\n        }\n      });\n\n      final libFlutterGtkDeps = await _getSharedDependencies(\n        path.join(\n          makeConfig.packagingDirectory.path,\n          '${makeConfig.appName}.AppDir/lib/libflutter_linux_gtk.so',\n        ),\n      );\n\n      final allReferencedSharedLibs = <String>{};\n      for (final so in appSOLibs) {\n        final referencedSharedLibs = await _getSharedDependencies(so.path).then(\n          (d) =>\n              d.difference(libFlutterGtkDeps)\n                ..removeWhere((lib) => lib.contains('libflutter_linux_gtk.so')),\n        );\n        allReferencedSharedLibs.addAll(referencedSharedLibs);\n      }\n\n      if (allReferencedSharedLibs.isNotEmpty) {\n        await $('cp', [\n          ...allReferencedSharedLibs,\n          path.join(\n            makeConfig.packagingDirectory.path,\n            '${makeConfig.appName}.AppDir/usr/lib',\n          ),\n        ]).then((value) {\n          if (value.exitCode != 0) {\n            throw MakeError(value.stderr as String);\n          }\n        });\n      }\n\n      await Future.wait(\n        makeConfig.include.map((so) async {\n          final file = await $('locate', [so])\n              .then((value) {\n                if (value.exitCode != 0) {\n                  throw MakeError(value.stderr as String);\n                }\n                return value.stdout as String;\n              })\n              .then((out) {\n                final paths = out\n                    .split('\\n')\n                    .where((p) => p.isNotEmpty && !p.contains('/Trash'))\n                    .toList();\n                if (paths.isEmpty) {\n                  throw MakeError(\"Can't find specified shared object $so\");\n                }\n                return File(paths.first.trim());\n              });\n\n          await file.copy(\n            path.join(\n              makeConfig.packagingDirectory.path,\n              '${makeConfig.appName}.AppDir/usr/lib/',\n              path.basename(file.path),\n            ),\n          );\n        }),\n      );\n\n      await $(\n        'appimagetool',\n        [\n          path.join(\n            makeConfig.packagingDirectory.path,\n            '${makeConfig.appName}.AppDir',\n          ),\n          makeConfig.outputFile.path.replaceAll('.appimage', '.AppImage'),\n        ],\n        environment: {'ARCH': 'x86_64'},\n      ).then((value) {\n        if (value.exitCode != 0) {\n          throw MakeError(value.stderr as String);\n        }\n      });\n\n      makeConfig.packagingDirectory.deleteSync(recursive: true);\n      return MakeResult(makeConfig);\n    } catch (e) {\n      if (e is MakeError) rethrow;\n      throw MakeError(e.toString());\n    }\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/appimage/make_appimage_config.dart",
    "content": "// ignore_for_file: flutter_style_todo,todo\n\nimport 'dart:io';\n\nimport 'package:flutter_app_packager/src/api/app_package_maker.dart';\n\nclass AppImageAction {\n  AppImageAction({\n    required this.label,\n    required this.name,\n    required this.arguments,\n  });\n  factory AppImageAction.fromJson(Map<String, dynamic> map) {\n    return AppImageAction(\n      label: map['label'] as String,\n      name: map['name'] as String,\n      arguments: (map['arguments'] as List<dynamic>).cast<String>(),\n    );\n  }\n  String label;\n  String name;\n  List<String> arguments;\n\n  Map<String, dynamic> toJson() {\n    return {\n      'label': label,\n      'name': name,\n      'arguments': arguments,\n    };\n  }\n}\n\nclass MakeAppImageConfig extends MakeConfig {\n  MakeAppImageConfig({\n    required this.displayName,\n    required this.icon,\n    this.keywords = const [],\n    this.categories = const [],\n    this.actions = const [],\n    this.include = const [],\n    this.startupNotify = true,\n    this.genericName = 'A Flutter Application',\n  });\n  factory MakeAppImageConfig.fromJson(Map<String, dynamic> map) {\n    return MakeAppImageConfig(\n      displayName: map['display_name'] as String,\n      icon: map['icon'] as String,\n      include: (map['include'] as List<dynamic>? ?? []).cast<String>(),\n      keywords: (map['keywords'] as List<dynamic>? ?? []).cast<String>(),\n      categories: (map['categories'] as List<dynamic>? ?? []).cast<String>(),\n      startupNotify: map['startup_notify'] as bool? ?? false,\n      genericName: map['generic_name'] as String? ?? 'A Flutter Application',\n      actions: (map['actions'] as List? ?? [])\n          .map(\n            (e) => AppImageAction.fromJson(\n              (Map.castFrom<dynamic, dynamic, String, dynamic>(e)),\n            ),\n          )\n          .toList(),\n    );\n  }\n\n  final String icon;\n  final List<String> keywords;\n  final List<String> categories;\n  final List<AppImageAction> actions;\n  final bool startupNotify;\n  final String genericName;\n  final String displayName;\n  final List<String> include;\n\n  String get desktopFileContent {\n    final fields = {\n      'Name': displayName,\n      'GenericName': genericName,\n      'Exec': 'LD_LIBRARY_PATH=usr/lib $appName %u',\n      'Icon': appName,\n      'Type': 'Application',\n      'StartupNotify': startupNotify ? 'true' : 'false',\n      if (categories.isNotEmpty) 'Categories': categories.join(';'),\n      if (keywords.isNotEmpty) 'Keywords': keywords.join(';'),\n      if (this.actions.isNotEmpty)\n        'Actions': this.actions.map((e) => e.label).join(';'),\n    }.entries.map((e) => '${e.key}=${e.value}').join('\\n');\n\n    final actions = this.actions.map((action) {\n      final fields = {\n        'Name': action.name,\n        'Exec':\n            'LD_LIBRARY_PATH=usr/lib $appName ${action.arguments.join(' ')} %u',\n      };\n      return '[Desktop Action ${action.label}]\\n${fields.entries.map((e) => '${e.key}=${e.value}').join('\\n')}';\n    }).join('\\n\\n');\n\n    return '[Desktop Entry]\\n$fields\\n\\n$actions';\n  }\n\n  String get appRunContent {\n    return '''\n#!/bin/bash\n\ncd \"\\$(dirname \"\\$0\")\"\nexport LD_LIBRARY_PATH=usr/lib\nexec ./$appName\n''';\n  }\n}\n\nclass MakeAppImageConfigLoader extends DefaultMakeConfigLoader {\n  @override\n  MakeConfig load(\n    Map<String, dynamic>? arguments,\n    Directory outputDirectory, {\n    required Directory buildOutputDirectory,\n    required List<File> buildOutputFiles,\n  }) {\n    final baseMakeConfig = super.load(\n      arguments,\n      outputDirectory,\n      buildOutputDirectory: buildOutputDirectory,\n      buildOutputFiles: buildOutputFiles,\n    );\n    final map = loadMakeConfigYaml(\n      '$platform/packaging/$packageFormat/make_config.yaml',\n    );\n    return MakeAppImageConfig.fromJson(map).copyWith(baseMakeConfig);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/deb/app_package_maker_deb.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_packager/src/api/app_package_maker.dart';\nimport 'package:flutter_app_packager/src/makers/deb/make_deb_config.dart';\nimport 'package:path/path.dart' as path;\nimport 'package:shell_executor/shell_executor.dart';\n\nclass AppPackageMakerDeb extends AppPackageMaker {\n  @override\n  String get name => 'deb';\n  @override\n  String get platform => 'linux';\n  @override\n  bool get isSupportedOnCurrentPlatform => Platform.isLinux;\n  @override\n  String get packageFormat => 'deb';\n\n  @override\n  MakeConfigLoader get configLoader {\n    return MakeDebConfigLoader()\n      ..platform = platform\n      ..packageFormat = packageFormat;\n  }\n\n  @override\n  Future<MakeResult> make(MakeConfig config) {\n    return _make(\n      config.buildOutputDirectory,\n      outputDirectory: config.outputDirectory,\n      makeConfig: config as MakeDebConfig,\n    );\n  }\n\n  Future<MakeResult> _make(\n    Directory appDirectory, {\n    required Directory outputDirectory,\n    required MakeDebConfig makeConfig,\n  }) async {\n    final files = makeConfig.toFilesString();\n\n    Directory packagingDirectory = makeConfig.packagingDirectory;\n\n    /// Need to create following directories\n    /// /DEBIAN\n    /// /usr/share/$appBinaryName\n    /// /usr/share/applications\n    /// /usr/share/icons/hicolor/128x128/apps\n    /// /usr/share/icons/hicolor/256x256/apps\n\n    final debianDir = path.join(packagingDirectory.path, 'DEBIAN');\n    final applicationsDir =\n        path.join(packagingDirectory.path, 'usr/share/applications');\n    final icon128Dir = path.join(\n      packagingDirectory.path,\n      'usr/share/icons/hicolor/128x128/apps',\n    );\n    final icon256Dir = path.join(\n      packagingDirectory.path,\n      'usr/share/icons/hicolor/256x256/apps',\n    );\n    final mkdirProcessResult = await $('mkdir', [\n      '-p',\n      debianDir,\n      path.join(packagingDirectory.path, 'usr/share', makeConfig.appBinaryName),\n      applicationsDir,\n      if (makeConfig.icon != null) ...[icon128Dir, icon256Dir],\n    ]);\n\n    if (mkdirProcessResult.exitCode != 0) throw MakeError();\n\n    if (makeConfig.icon != null) {\n      final iconFile = File(makeConfig.icon!);\n      if (!iconFile.existsSync()) {\n        throw MakeError(\"provided icon ${makeConfig.icon} path wasn't found\");\n      }\n\n      await iconFile.copy(\n        path.join(\n          icon128Dir,\n          makeConfig.appBinaryName + path.extension(makeConfig.icon!),\n        ),\n      );\n      await iconFile.copy(\n        path.join(\n          icon256Dir,\n          makeConfig.appBinaryName + path.extension(makeConfig.icon!),\n        ),\n      );\n    }\n\n    // create & write the files got from makeConfig\n    final controlFile = File(path.join(debianDir, 'control'));\n    final postinstFile = File(path.join(debianDir, 'postinst'));\n    final postrmFile = File(path.join(debianDir, 'postrm'));\n    final desktopEntryFile =\n        File(path.join(applicationsDir, '${makeConfig.appBinaryName}.desktop'));\n\n    if (!controlFile.existsSync()) controlFile.createSync();\n    if (!postinstFile.existsSync()) postinstFile.createSync();\n    if (!postrmFile.existsSync()) postrmFile.createSync();\n    if (!desktopEntryFile.existsSync()) desktopEntryFile.createSync();\n\n    await controlFile.writeAsString(files['CONTROL']!);\n    await desktopEntryFile.writeAsString(files['DESKTOP']!);\n    await postinstFile.writeAsString(files['postinst']!);\n    await postrmFile.writeAsString(files['postrm']!);\n\n    // give execution permission to shell scripts\n    await $('chmod', ['+x', postinstFile.path, postrmFile.path]);\n\n    // copy the application binary to /usr/share/$appBinaryName\n    await $('cp', [\n      '-fr',\n      '${appDirectory.path}/.',\n      '${packagingDirectory.path}/usr/share/${makeConfig.appBinaryName}/',\n    ]);\n\n    ProcessResult processResult = await $('dpkg-deb', [\n      '--build',\n      '--root-owner-group',\n      packagingDirectory.path,\n      makeConfig.outputFile.path,\n    ]);\n\n    if (processResult.exitCode != 0) {\n      throw MakeError();\n    }\n\n    packagingDirectory.deleteSync(recursive: true);\n    return MakeResult(makeConfig);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/deb/make_deb_config.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_packager/src/api/app_package_maker.dart';\n\n// format of make_config for deb\n/*\n# the name used to display in the OS. Specifically desktop\n# entry name\ndisplay_name: Hola Amigos\n\n# package name for debian/apt repository\n# the name should be all lowercase with -+.\npackage_name: hola-amigos\n\nmaintainer:\n  name: Gamer Boy 69\n  email: rickastley@gmail.lol\n\nco_authors:\n  - name: Mir Jafar\n    email: contributor@gmail.com\n\n# enum options -> required, important, standard, optional, extra\n# refer: https://www.debian.org/doc/debian-policy/ch-archive.html#s-priorities\npriority: optional\n\n# enum options: admin, cli-mono, comm, database, debug, devel, doc, editors, education, electronics, embedded, fonts, games, gnome, gnu-r, gnustep, graphics, hamradio, haskell, httpd, interpreters, introspection, java, javascript, kde, kernel, libdevel, libs, lisp, localization, mail, math, metapackages, misc, net, news, ocaml, oldlibs, otherosfs, perl, php, python, ruby, rust, science, shells, sound, tasks, tex, text, utils, vcs, video, web, x11, xfce, zope\n# refer: https://www.debian.org/doc/debian-policy/ch-archive.html#s-subsections\nsection: x11\n\n# the size of binary in kilobyte\ninstalled_size: 24400\n\n# direct dependencies required by the application\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\ndependencies:\n  - libkeybinder-3.0-0 (>= 0.3.2)\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\nbuild_dependencies_indep:\n  - texinfo\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\nbuild_dependencies:\n  - kernel-headers-2.2.10 [!hurd-i386]\n  - gnumach-dev [hurd-i386]\n  - libluajit5.1-dev [i386 amd64 kfreebsd-i386 armel armhf powerpc mips]\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\nrecommended_dependencies:\n  - neofetch\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\nsuggested_dependencies:\n  - libkeybinder-3.0-0 (>= 0.3.2)\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\nenhances:\n  - spotube\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html\npre_dependencies:\n  - libc6\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html#packages-which-break-other-packages-breaks\nbreaks:\n  - libspotify (<< 3.0.0)\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html#conflicting-binary-packages-conflicts\nconflicts:\n  - spotify\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html#virtual-packages-provides\nprovides:\n  - libx11\n\n# refer: https://www.debian.org/doc/debian-policy/ch-relationships.html#overwriting-files-and-replacing-packages-replaces\nreplaces:\n  - spotify\n\nessential: false\n\npostinstall_scripts:\n  - echo `Installed my awesome app`\npostuninstall_scripts:\n  - echo `Surprised Pickachu face`\n\n\n# application icon path relative to project url\nicon: assets/logo.png\n\nkeywords:\n  - Hello\n  - World\n  - Test\n  - Application\n\n# a name to categorize the app into a section of application\ngeneric_name: Hobby Application\n\n# supported mime types that can be opened using this application\nsupported_mime_type:\n  - audio/mpeg\n\n# shown when right clicked the desktop entry icons\nactions:\n  - Gallery\n  - Create\n\n# the categories the application belong to\n# refer: https://specifications.freedesktop.org/menu-spec/latest/\ncategories:\n  - Music\n  - Media\n\n# let OS know if the application can be run on start_up. If it's false \n# the application will deny to the OS if it was added as a start_up \n# application\nstartup_notify: true  \n*/\n\nclass MakeDebConfig extends MakeLinuxPackageConfig {\n  MakeDebConfig({\n    required this.displayName,\n    required this.packageName,\n    required this.installedSize,\n    required this.maintainer,\n    this.startupNotify = true,\n    this.essential = false,\n    List<String>? postinstallScripts,\n    List<String>? postuninstallScripts,\n    this.priority = 'optional',\n    this.section = 'x11',\n    this.actions,\n    this.breaks,\n    this.buildDependencies,\n    this.buildDependenciesIndep,\n    this.suggestedDependencies,\n    this.categories,\n    this.coAuthors,\n    this.dependencies,\n    this.enhances,\n    this.genericName,\n    this.icon,\n    this.keywords,\n    this.preDependencies,\n    this.provides,\n    this.recommendedDependencies,\n    this.replaces,\n    this.conflicts,\n    this.supportedMimeType,\n  })  : _postinstallScripts = postinstallScripts ?? [],\n        _postuninstallScripts = postuninstallScripts ?? [];\n\n  factory MakeDebConfig.fromJson(Map<String, dynamic> map) {\n    return MakeDebConfig(\n      displayName: map['display_name'],\n      packageName: map['package_name'],\n      maintainer:\n          \"${map['maintainer']['name']} <${map['maintainer']['email']}>\",\n      coAuthors: (map['co_authors'] as List?)\n          ?.map((e) => \"${e['name']} <${e['email']}>\")\n          .toList(),\n      priority: map['priority'],\n      section: map['section'],\n      dependencies: map['dependencies'] != null\n          ? List.castFrom<dynamic, String>(map['dependencies'])\n          : null,\n      buildDependenciesIndep: map['build_dependencies_indep'] != null\n          ? List.castFrom<dynamic, String>(map['build_dependencies_indep'])\n          : null,\n      buildDependencies: map['build_dependencies'] != null\n          ? List.castFrom<dynamic, String>(map['build_dependencies'])\n          : null,\n      recommendedDependencies: map['recommended_dependencies'] != null\n          ? List.castFrom<dynamic, String>(map['recommended_dependencies'])\n          : null,\n      suggestedDependencies: map['suggested_dependencies'] != null\n          ? List.castFrom<dynamic, String>(map['suggested_dependencies'])\n          : null,\n      enhances: map['enhances'] != null\n          ? List.castFrom<dynamic, String>(map['enhances'])\n          : null,\n      preDependencies: map['pre_dependencies'] != null\n          ? List.castFrom<dynamic, String>(map['pre_dependencies'])\n          : null,\n      breaks: map['breaks'] != null\n          ? List.castFrom<dynamic, String>(map['breaks'])\n          : null,\n      conflicts: map['conflicts'] != null\n          ? List.castFrom<dynamic, String>(map['conflicts'])\n          : null,\n      provides: map['provides'] != null\n          ? List.castFrom<dynamic, String>(map['provides'])\n          : null,\n      replaces: map['replaces'] != null\n          ? List.castFrom<dynamic, String>(map['replaces'])\n          : null,\n      postinstallScripts: map['postinstall_scripts'] != null\n          ? List.castFrom<dynamic, String>(map['postinstall_scripts'])\n          : null,\n      postuninstallScripts: map['postuninstall_scripts'] != null\n          ? List.castFrom<dynamic, String>(map['postuninstall_scripts'])\n          : null,\n      keywords: map['keywords'] != null\n          ? List.castFrom<dynamic, String>(map['keywords'])\n          : null,\n      supportedMimeType: map['supported_mime_type'] != null\n          ? List.castFrom<dynamic, String>(map['supported_mime_type'])\n          : null,\n      actions: map['actions'] != null\n          ? List.castFrom<dynamic, String>(map['actions'])\n          : null,\n      categories: map['categories'] != null\n          ? List.castFrom<dynamic, String>(map['categories'])\n          : null,\n      essential: map['essential'],\n      genericName: map['generic_name'],\n      startupNotify: map['startup_notify'],\n      installedSize: map['installed_size'],\n      icon: map['icon'],\n    );\n  }\n\n  String displayName;\n  String packageName;\n  String maintainer;\n  String priority;\n  String section;\n  int installedSize;\n  bool? essential;\n  String? icon;\n  String? genericName;\n  bool? startupNotify;\n  List<String>? coAuthors;\n  List<String>? dependencies;\n  List<String>? buildDependenciesIndep;\n  List<String>? buildDependencies;\n  List<String>? recommendedDependencies;\n  List<String>? suggestedDependencies;\n  List<String>? enhances;\n  List<String>? preDependencies;\n  List<String>? breaks;\n  List<String>? conflicts;\n  List<String>? provides;\n  List<String>? replaces;\n  List<String> _postinstallScripts;\n  List<String> _postuninstallScripts;\n  List<String>? keywords;\n  List<String>? supportedMimeType;\n  List<String>? actions;\n  List<String>? categories;\n\n  List<String> get postinstallScripts => [\n        'ln -s /usr/share/$appBinaryName/$appBinaryName /usr/bin/$appBinaryName',\n        'chmod +x /usr/bin/$appBinaryName',\n        ..._postinstallScripts,\n      ];\n\n  List<String> get postuninstallScripts => [\n        'rm /usr/bin/$appBinaryName',\n        ..._postuninstallScripts,\n      ];\n\n  @override\n  Map<String, dynamic> toJson() {\n    return {\n      'CONTROL': {\n        'Maintainer': maintainer,\n        'Package': packageName,\n        'Version': appVersion.toString(),\n        'Section': section,\n        'Priority': priority,\n        'Architecture': _getArchitecture(),\n        'Essential':\n            essential != null ? (essential == true ? 'yes' : 'no') : null,\n        'Installed-Size': installedSize,\n        'Description': pubspec.description,\n        'Homepage': pubspec.homepage,\n        'Depends': dependencies?.join(', '),\n        'Build-Depends-Indep': buildDependenciesIndep?.join(', '),\n        'Build-Depends': buildDependencies?.join(', '),\n        'Pre-Depends': preDependencies?.join(', '),\n        'Recommends': recommendedDependencies?.join(', '),\n        'Suggests': suggestedDependencies?.join(', '),\n        'Enhances': enhances?.join(', '),\n        'Breaks': breaks?.join(', '),\n        'Conflicts': conflicts?.join(', '),\n        'Provides': provides?.join(', '),\n        'Replaces': replaces?.join(', '),\n        'Uploaders': coAuthors?.join(', '),\n      }..removeWhere((key, value) => value == null),\n      'DESKTOP': {\n        'Type': 'Application',\n        'Version': appVersion.toString(),\n        'Name': displayName,\n        'GenericName': genericName,\n        'Icon': appBinaryName,\n        'Exec': '$appBinaryName %U',\n        'Actions': actions != null && actions!.isNotEmpty\n            ? '${actions!.join(';')};'\n            : null,\n        'MimeType': supportedMimeType != null && supportedMimeType!.isNotEmpty\n            ? '${supportedMimeType!.join(';')};'\n            : null,\n        'Categories': categories != null && categories!.isNotEmpty\n            ? '${categories!.join(';')};'\n            : null,\n        'Keywords': keywords != null && keywords!.isNotEmpty\n            ? '${keywords!.join(';')};'\n            : null,\n        'StartupNotify': startupNotify,\n      }..removeWhere((key, value) => value == null),\n    };\n  }\n\n  Map<String, String> toFilesString() {\n    final json = toJson();\n    final controlFile =\n        '${(json['CONTROL'] as Map<String, dynamic>).entries.map(\n              (e) => '${e.key}: ${e.value}',\n            ).join('\\n')}\\n';\n\n    final desktopFile = [\n      '[Desktop Entry]',\n      ...(json['DESKTOP'] as Map<String, dynamic>).entries.map(\n            (e) => '${e.key}=${e.value}',\n          ),\n    ].join('\\n');\n    final map = {\n      'CONTROL': controlFile,\n      'DESKTOP': desktopFile,\n      'postinst': postinstallScripts.isNotEmpty\n          ? [\n              '#!/usr/bin/env sh',\n              ...postinstallScripts,\n              'exit 0',\n            ].join('\\n')\n          : null,\n      'postrm': postuninstallScripts.isNotEmpty\n          ? [\n              '#!/usr/bin/env sh',\n              ...postuninstallScripts,\n              'exit 0',\n            ].join('\\n')\n          : null,\n    }..removeWhere((key, value) => value == null);\n    return Map.castFrom<String, String?, String, String>(map);\n  }\n}\n\nclass MakeDebConfigLoader extends DefaultMakeConfigLoader {\n  @override\n  MakeConfig load(\n    Map<String, dynamic>? arguments,\n    Directory outputDirectory, {\n    required Directory buildOutputDirectory,\n    required List<File> buildOutputFiles,\n  }) {\n    final baseMakeConfig = super.load(\n      arguments,\n      outputDirectory,\n      buildOutputDirectory: buildOutputDirectory,\n      buildOutputFiles: buildOutputFiles,\n    );\n    final map = loadMakeConfigYaml(\n      '$platform/packaging/$packageFormat/make_config.yaml',\n    );\n    return MakeDebConfig.fromJson(map).copyWith(baseMakeConfig);\n  }\n}\n\nString _getArchitecture() {\n  final result = Process.runSync('uname', ['-m']);\n  if ('${result.stdout}'.trim() == 'aarch64') {\n    return 'arm64';\n  } else {\n    return 'amd64';\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/direct/app_package_maker_direct.dart",
    "content": "import 'package:flutter_app_packager/src/api/app_package_maker.dart';\nimport 'package:io/io.dart';\n\nclass AppPackageMakerDirect extends AppPackageMaker {\n  AppPackageMakerDirect(String platform) {\n    _platform = platform;\n  }\n\n  late String _platform;\n\n  @override\n  String get name => 'direct';\n\n  @override\n  String get platform => _platform;\n\n  @override\n  String get packageFormat => '';\n\n  @override\n  Future<MakeResult> make(MakeConfig config) {\n    copyPathSync(config.buildOutputDirectory.path, config.outputArtifactPath);\n    return Future.value(resultResolver.resolve(config));\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/dmg/app_package_maker_dmg.dart",
    "content": "import 'dart:convert';\nimport 'dart:io';\n\nimport 'package:flutter_app_packager/src/api/app_package_maker.dart';\nimport 'package:flutter_app_packager/src/makers/dmg/commands/appdmg.dart';\nimport 'package:flutter_app_packager/src/makers/dmg/make_dmg_config.dart';\nimport 'package:shell_executor/shell_executor.dart';\n\nclass AppPackageMakerDmg extends AppPackageMaker {\n  @override\n  List<Command> get requirements => [appdmg];\n\n  @override\n  String get name => 'dmg';\n\n  @override\n  String get platform => 'macos';\n\n  @override\n  bool get isSupportedOnCurrentPlatform => Platform.isMacOS;\n\n  @override\n  String get packageFormat => 'dmg';\n\n  @override\n  MakeConfigLoader get configLoader {\n    return MakeDmgConfigLoader()\n      ..platform = platform\n      ..packageFormat = packageFormat;\n  }\n\n  @override\n  Future<MakeResult> make(MakeConfig config) async {\n    Directory packagingDirectory = config.packagingDirectory;\n\n    File appFile = config.buildOutputDirectory\n        .listSync()\n        .where((e) => e.path.endsWith('.app'))\n        .map((e) => File(e.path))\n        .first;\n\n    try {\n      await $('cp', ['-RH', appFile.path, packagingDirectory.path]);\n\n      await $('cp', ['-RH', 'macos/packaging/dmg/.', packagingDirectory.path]);\n\n      File makeDmgConfigJsonFile = File(\n        '${packagingDirectory.path}/make_config.json',\n      );\n      makeDmgConfigJsonFile.writeAsStringSync(json.encode(config.toJson()));\n      final file = File(config.outputFile.path);\n      if (file.existsSync()) {\n        file.deleteSync();\n      }\n      ProcessResult processResult = await appdmg.exec([\n        makeDmgConfigJsonFile.path,\n        config.outputFile.path,\n      ]);\n\n      if (processResult.exitCode != 0) {\n        throw MakeError();\n      }\n    } catch (error) {\n      rethrow;\n    } finally {\n      packagingDirectory.deleteSync(recursive: true);\n    }\n\n    return resultResolver.resolve(config);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/dmg/commands/appdmg.dart",
    "content": "import 'package:shell_executor/shell_executor.dart';\n\nclass _AppDmg extends Command {\n  @override\n  String get executable => 'appdmg';\n\n  @override\n  Future<void> install() async {\n    await $('pnpm', 'install -g appdmg'.split(' '));\n  }\n}\n\nfinal appdmg = _AppDmg();\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/dmg/make_dmg_config.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_packager/src/api/app_package_maker.dart';\n\nclass DmgWindowPosition {\n  DmgWindowPosition({\n    required this.x,\n    required this.y,\n  });\n\n  factory DmgWindowPosition.fromJson(Map<String, dynamic> json) {\n    return DmgWindowPosition(\n      x: json['x'],\n      y: json['y'],\n    );\n  }\n\n  final num x;\n  final num y;\n\n  Map<String, dynamic> toJson() {\n    return {\n      'x': x,\n      'y': y,\n    };\n  }\n}\n\nclass DmgWindowSize {\n  DmgWindowSize({\n    required this.width,\n    required this.height,\n  });\n\n  factory DmgWindowSize.fromJson(Map<String, dynamic> json) {\n    return DmgWindowSize(\n      width: json['width'],\n      height: json['height'],\n    );\n  }\n  final num width;\n  final num height;\n\n  Map<String, dynamic> toJson() {\n    return {\n      'width': width,\n      'height': height,\n    };\n  }\n}\n\nclass DmgWindow {\n  DmgWindow({\n    this.position,\n    this.size,\n  });\n\n  factory DmgWindow.fromJson(Map<String, dynamic> json) {\n    return DmgWindow(\n      position: json['position'] != null\n          ? DmgWindowPosition.fromJson(json['position'])\n          : null,\n      size: json['size'] != null ? DmgWindowSize.fromJson(json['size']) : null,\n    );\n  }\n  final DmgWindowPosition? position;\n  final DmgWindowSize? size;\n\n  Map<String, dynamic> toJson() {\n    return {\n      'position': position?.toJson(),\n      'size': size?.toJson(),\n    }..removeWhere((key, value) => value == null);\n  }\n}\n\nclass DmgCodeSign {\n  DmgCodeSign({\n    required this.signingIdentity,\n    this.identifier,\n  });\n\n  factory DmgCodeSign.fromJson(Map<String, dynamic> json) {\n    return DmgCodeSign(\n      signingIdentity: json['signing-identity'],\n      identifier: json['identifier'],\n    );\n  }\n  final String signingIdentity;\n  final String? identifier;\n\n  Map<String, dynamic> toJson() {\n    return {\n      'signing-identity': signingIdentity,\n      'identifier': identifier,\n    }..removeWhere((key, value) => value == null);\n  }\n}\n\nclass DmgContent {\n  DmgContent({\n    required this.x,\n    required this.y,\n    required this.type,\n    required this.path,\n    this.name,\n  });\n\n  factory DmgContent.fromJson(Map<String, dynamic> json) {\n    return DmgContent(\n      x: json['x'],\n      y: json['y'],\n      type: json['type'],\n      path: json['path'],\n      name: json['name'],\n    );\n  }\n  final num x;\n  final num y;\n  final String type;\n  final String path;\n  final String? name;\n\n  Map<String, dynamic> toJson() {\n    return {\n      'x': x,\n      'y': y,\n      'type': type,\n      'path': path,\n      'name': name,\n    }..removeWhere((key, value) => value == null);\n  }\n}\n\nclass MakeDmgConfig extends MakeConfig {\n  MakeDmgConfig({\n    required this.title,\n    this.icon,\n    this.background,\n    this.backgroundColor,\n    this.iconSize,\n    this.format,\n    required this.contents,\n    this.codeSign,\n    this.window,\n  });\n\n  factory MakeDmgConfig.fromJson(Map<String, dynamic> json) {\n    List<DmgContent> contents = (json['contents'] as List)\n        .map((item) => DmgContent.fromJson(item))\n        .toList();\n\n    return MakeDmgConfig(\n      title: json['title'],\n      icon: json['icon'],\n      background: json['background'],\n      backgroundColor: json['background-color'],\n      iconSize: json['icon-size'],\n      format: json['format'],\n      contents: contents,\n      codeSign: json['code-sign'] != null\n          ? DmgCodeSign.fromJson(json['code-sign'])\n          : null,\n      window:\n          json['window'] != null ? DmgWindow.fromJson(json['window']) : null,\n    );\n  }\n  final String title;\n  final String? icon;\n  final String? background;\n  final String? backgroundColor;\n  final int? iconSize;\n  final String? format;\n  final List<DmgContent> contents;\n  final DmgCodeSign? codeSign;\n  final DmgWindow? window;\n\n  @override\n  Map<String, dynamic> toJson() {\n    return {\n      'title': title,\n      'icon': icon,\n      'background': background,\n      'background-color': backgroundColor,\n      'icon-size': iconSize,\n      'format': format,\n      'contents': contents.map((e) => e.toJson()).toList(),\n      'code-sign': codeSign?.toJson(),\n      'window': window?.toJson(),\n    }..removeWhere((key, value) => value == null);\n  }\n}\n\nclass MakeDmgConfigLoader extends DefaultMakeConfigLoader {\n  @override\n  MakeConfig load(\n    Map<String, dynamic>? arguments,\n    Directory outputDirectory, {\n    required Directory buildOutputDirectory,\n    required List<File> buildOutputFiles,\n  }) {\n    final baseMakeConfig = super.load(\n      arguments,\n      outputDirectory,\n      buildOutputDirectory: buildOutputDirectory,\n      buildOutputFiles: buildOutputFiles,\n    );\n    final map = loadMakeConfigYaml(\n      '$platform/packaging/$packageFormat/make_config.yaml',\n    );\n    return MakeDmgConfig.fromJson(map).copyWith(baseMakeConfig);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/exe/app_package_maker_exe.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_packager/src/api/app_package_maker.dart';\nimport 'package:flutter_app_packager/src/makers/exe/inno_setup/inno_setup_compiler.dart';\nimport 'package:flutter_app_packager/src/makers/exe/inno_setup/inno_setup_script.dart';\nimport 'package:flutter_app_packager/src/makers/exe/make_exe_config.dart';\nimport 'package:io/io.dart';\n\nclass AppPackageMakerExe extends AppPackageMaker {\n  @override\n  String get name => 'exe';\n  @override\n  String get platform => 'windows';\n  @override\n  bool get isSupportedOnCurrentPlatform => Platform.isWindows;\n  @override\n  String get packageFormat => 'exe';\n\n  @override\n  MakeConfigLoader get configLoader {\n    return MakeExeConfigLoader()\n      ..platform = platform\n      ..packageFormat = packageFormat;\n  }\n\n  @override\n  Future<MakeResult> make(MakeConfig config) {\n    return _make(\n      config.buildOutputDirectory,\n      outputDirectory: config.outputDirectory,\n      makeConfig: config as MakeExeConfig,\n    );\n  }\n\n  Future<MakeResult> _make(\n    Directory appDirectory, {\n    required Directory outputDirectory,\n    required MakeExeConfig makeConfig,\n  }) async {\n    Directory packagingDirectory = makeConfig.packagingDirectory;\n    copyPathSync(appDirectory.path, packagingDirectory.path);\n\n    InnoSetupScript script = InnoSetupScript.fromMakeConfig(makeConfig);\n    InnoSetupCompiler compiler = InnoSetupCompiler();\n\n    bool compiled = await compiler.compile(script);\n\n    if (!compiled) {\n      throw MakeError();\n    }\n\n    packagingDirectory.deleteSync(recursive: true);\n\n    return MakeResult(makeConfig);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/exe/inno_setup/inno_setup_compiler.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_packager/src/makers/exe/inno_setup/inno_setup_script.dart';\nimport 'package:path/path.dart' as p;\nimport 'package:shell_executor/shell_executor.dart';\n\nclass InnoSetupCompiler {\n  Future<bool> compile(InnoSetupScript script) async {\n    Directory innoSetupDirectory =\n        Directory('C:\\\\Program Files (x86)\\\\Inno Setup 6');\n\n    if (!innoSetupDirectory.existsSync()) {\n      throw Exception('`Inno Setup 6` was not installed.');\n    }\n\n    File file = await script.createFile();\n\n    ProcessResult processResult = await $(\n      p.join(innoSetupDirectory.path, 'ISCC.exe'),\n      [file.path],\n    );\n\n    if (processResult.exitCode != 0) {\n      return false;\n    }\n\n    file.deleteSync(recursive: true);\n    return true;\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/exe/inno_setup/inno_setup_script.dart",
    "content": "import 'dart:convert';\nimport 'dart:io';\n\nimport 'package:flutter_app_packager/src/makers/exe/make_exe_config.dart';\nimport 'package:liquid_engine/liquid_engine.dart';\nimport 'package:path/path.dart' as path;\n\nconst String _template = \"\"\"\n[Setup]\nAppId={{APP_ID}}\nAppVersion={{APP_VERSION}}\nAppName={{DISPLAY_NAME}}\nAppPublisher={{PUBLISHER_NAME}}\nAppPublisherURL={{PUBLISHER_URL}}\nAppSupportURL={{PUBLISHER_URL}}\nAppUpdatesURL={{PUBLISHER_URL}}\nDefaultDirName={{INSTALL_DIR_NAME}}\nDisableProgramGroupPage=yes\nOutputDir=.\nOutputBaseFilename={{OUTPUT_BASE_FILENAME}}\nCompression=lzma\nSolidCompression=yes\nSetupIconFile={{SETUP_ICON_FILE}}\nWizardStyle=modern\nPrivilegesRequired={{PRIVILEGES_REQUIRED}}\nArchitecturesAllowed={{ARCH}}\nArchitecturesInstallIn64BitMode={{ARCH}}\n\n[Languages]\n{% for locale in LOCALES %}\n{% if locale.lang == 'en' %}Name: \"english\"; MessagesFile: \"compiler:Default.isl\"{% endif %}\n{% if locale.lang == 'hy' %}Name: \"armenian\"; MessagesFile: \"compiler:Languages\\\\Armenian.isl\"{% endif %}\n{% if locale.lang == 'bg' %}Name: \"bulgarian\"; MessagesFile: \"compiler:Languages\\\\Bulgarian.isl\"{% endif %}\n{% if locale.lang == 'ca' %}Name: \"catalan\"; MessagesFile: \"compiler:Languages\\\\Catalan.isl\"{% endif %}\n{% if locale.lang == 'zh' %}\nName: \"chineseSimplified\"; MessagesFile: {% if locale.file %}{{ locale.file }}{% else %}\"compiler:Languages\\\\ChineseSimplified.isl\"{% endif %}\n{% endif %}\n{% if locale.lang == 'co' %}Name: \"corsican\"; MessagesFile: \"compiler:Languages\\\\Corsican.isl\"{% endif %}\n{% if locale.lang == 'cs' %}Name: \"czech\"; MessagesFile: \"compiler:Languages\\\\Czech.isl\"{% endif %}\n{% if locale.lang == 'da' %}Name: \"danish\"; MessagesFile: \"compiler:Languages\\\\Danish.isl\"{% endif %}\n{% if locale.lang == 'nl' %}Name: \"dutch\"; MessagesFile: \"compiler:Languages\\\\Dutch.isl\"{% endif %}\n{% if locale.lang == 'fi' %}Name: \"finnish\"; MessagesFile: \"compiler:Languages\\\\Finnish.isl\"{% endif %}\n{% if locale.lang == 'fr' %}Name: \"french\"; MessagesFile: \"compiler:Languages\\\\French.isl\"{% endif %}\n{% if locale.lang == 'de' %}Name: \"german\"; MessagesFile: \"compiler:Languages\\\\German.isl\"{% endif %}\n{% if locale.lang == 'he' %}Name: \"hebrew\"; MessagesFile: \"compiler:Languages\\\\Hebrew.isl\"{% endif %}\n{% if locale.lang == 'is' %}Name: \"icelandic\"; MessagesFile: \"compiler:Languages\\\\Icelandic.isl\"{% endif %}\n{% if locale.lang == 'it' %}Name: \"italian\"; MessagesFile: \"compiler:Languages\\\\Italian.isl\"{% endif %}\n{% if locale.lang == 'ja' %}Name: \"japanese\"; MessagesFile: \"compiler:Languages\\\\Japanese.isl\"{% endif %}\n{% if locale.lang == 'no' %}Name: \"norwegian\"; MessagesFile: \"compiler:Languages\\\\Norwegian.isl\"{% endif %}\n{% if locale.lang == 'pl' %}Name: \"polish\"; MessagesFile: \"compiler:Languages\\\\Polish.isl\"{% endif %}\n{% if locale.lang == 'pt' %}Name: \"portuguese\"; MessagesFile: \"compiler:Languages\\\\Portuguese.isl\"{% endif %}\n{% if locale.lang == 'ru' %}Name: \"russian\"; MessagesFile: \"compiler:Languages\\\\Russian.isl\"{% endif %}\n{% if locale.lang == 'sk' %}Name: \"slovak\"; MessagesFile: \"compiler:Languages\\\\Slovak.isl\"{% endif %}\n{% if locale.lang == 'sl' %}Name: \"slovenian\"; MessagesFile: \"compiler:Languages\\\\Slovenian.isl\"{% endif %}\n{% if locale.lang == 'es' %}Name: \"spanish\"; MessagesFile: \"compiler:Languages\\\\Spanish.isl\"{% endif %}\n{% if locale.lang == 'tr' %}Name: \"turkish\"; MessagesFile: \"compiler:Languages\\\\Turkish.isl\"{% endif %}\n{% if locale.lang == 'uk' %}Name: \"ukrainian\"; MessagesFile: \"compiler:Languages\\\\Ukrainian.isl\"{% endif %}\n{% endfor %}\n\n[Tasks]\nName: \"desktopicon\"; Description: \"{cm:CreateDesktopIcon}\"; GroupDescription: \"{cm:AdditionalIcons}\"; Flags: {% if CREATE_DESKTOP_ICON != true %}unchecked{% else %}checkedonce{% endif %}\nName: \"launchAtStartup\"; Description: \"{cm:AutoStartProgram,{{DISPLAY_NAME}}}\"; GroupDescription: \"{cm:AdditionalIcons}\"; Flags: {% if LAUNCH_AT_STARTUP != true %}unchecked{% else %}checkedonce{% endif %}\n[Files]\nSource: \"{{SOURCE_DIR}}\\\\*\"; DestDir: \"{app}\"; Flags: ignoreversion recursesubdirs createallsubdirs\n; NOTE: Don't use \"Flags: ignoreversion\" on any shared system files\n\n[Icons]\nName: \"{autoprograms}\\\\{{DISPLAY_NAME}}\"; Filename: \"{app}\\\\{{EXECUTABLE_NAME}}\"\nName: \"{autodesktop}\\\\{{DISPLAY_NAME}}\"; Filename: \"{app}\\\\{{EXECUTABLE_NAME}}\"; Tasks: desktopicon\nName: \"{userstartup}\\\\{{DISPLAY_NAME}}\"; Filename: \"{app}\\\\{{EXECUTABLE_NAME}}\"; WorkingDir: \"{app}\"; Tasks: launchAtStartup\n[Run]\nFilename: \"{app}\\\\{{EXECUTABLE_NAME}}\"; Description: \"{cm:LaunchProgram,{{DISPLAY_NAME}}}\"; Flags: {% if PRIVILEGES_REQUIRED == 'admin' %}runascurrentuser{% endif %} nowait postinstall skipifsilent\n\"\"\";\n\nclass InnoSetupScript {\n  InnoSetupScript({\n    required this.makeConfig,\n  });\n\n  factory InnoSetupScript.fromMakeConfig(MakeExeConfig makeConfig) {\n    return InnoSetupScript(\n      makeConfig: makeConfig,\n    );\n  }\n\n  final MakeExeConfig makeConfig;\n\n  Future<File> createFile() async {\n    Map<String, dynamic> variables = {\n      'APP_ID': makeConfig.appId,\n      'APP_NAME': makeConfig.appName,\n      'APP_VERSION': makeConfig.appVersion.toString(),\n      'EXECUTABLE_NAME':\n          makeConfig.executableName ?? makeConfig.defaultExecutableName,\n      'DISPLAY_NAME': makeConfig.displayName,\n      'PUBLISHER_NAME': makeConfig.publisherName,\n      'ARCH': makeConfig.arch ?? 'x64',\n      'PUBLISHER_URL': makeConfig.publisherUrl,\n      'CREATE_DESKTOP_ICON': makeConfig.createDesktopIcon,\n      'LAUNCH_AT_STARTUP': makeConfig.launchAtStartup,\n      'INSTALL_DIR_NAME':\n          makeConfig.installDirName ?? makeConfig.defaultInstallDirName,\n      'SOURCE_DIR': makeConfig.sourceDir,\n      'OUTPUT_BASE_FILENAME': makeConfig.outputBaseFileName,\n      'LOCALES': makeConfig.locales,\n      'SETUP_ICON_FILE': makeConfig.setupIconFile ?? '',\n      'PRIVILEGES_REQUIRED': makeConfig.privilegesRequired ?? 'none',\n    }..removeWhere((key, value) => value == null);\n\n    Context context = Context.create();\n    context.variables = variables;\n\n    String scriptTemplate = _template;\n    if (makeConfig.scriptTemplate != null) {\n      File scriptTemplateFile = File(\n        path.join(\n          'windows/packaging/exe/',\n          makeConfig.scriptTemplate!,\n        ),\n      );\n      scriptTemplate = scriptTemplateFile.readAsStringSync();\n    }\n\n    Template template = Template.parse(\n      context,\n      Source.fromString(scriptTemplate),\n    );\n\n    String content = '\\uFEFF${await template.render(context)}';\n    File file = File('${makeConfig.packagingDirectory.path}.iss');\n\n    file.writeAsBytesSync(utf8.encode(content));\n    return file;\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/exe/make_exe_config.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_packager/src/api/app_package_maker.dart';\nimport 'package:path/path.dart' as p;\n\nclass MakeExeConfig extends MakeConfig {\n  MakeExeConfig({\n    this.scriptTemplate,\n    required this.appId,\n    this.executableName,\n    this.displayName,\n    this.publisherName,\n    this.publisherUrl,\n    this.createDesktopIcon,\n    this.launchAtStartup,\n    this.installDirName,\n    this.setupIconFile,\n    this.privilegesRequired,\n    this.locales,\n  });\n\n  factory MakeExeConfig.fromJson(Map<String, dynamic> json) {\n    List<Map<String, dynamic>>? locales = json['locales'] != null\n        ? List<Map<String, dynamic>>.from(json['locales'])\n        : null;\n    if (locales == null || locales.isEmpty) {\n      locales = [\n        {'lang': 'en'}\n      ];\n    }\n\n    MakeExeConfig makeExeConfig = MakeExeConfig(\n      scriptTemplate: json['script_template'],\n      appId: json['app_id'] ?? json['appId'],\n      executableName: json['executable_name'],\n      displayName: json['display_name'],\n      publisherName: json['publisher_name'] ?? json['appPublisher'],\n      publisherUrl: json['publisher_url'] ?? json['appPublisherUrl'],\n      createDesktopIcon: json['create_desktop_icon'],\n      launchAtStartup: json['launch_at_startup'],\n      installDirName: json['install_dir_name'],\n      setupIconFile: json['setup_icon_file'],\n      privilegesRequired: json['privileges_required'],\n      locales: locales,\n    );\n    return makeExeConfig;\n  }\n\n  String? scriptTemplate;\n  final String appId;\n  String? executableName;\n  String? displayName;\n  String? publisherName;\n  String? publisherUrl;\n  bool? createDesktopIcon;\n  bool? launchAtStartup;\n  String? installDirName;\n  String? setupIconFile;\n  String? privilegesRequired;\n  List<Map<String, dynamic>>? locales;\n\n  String get defaultExecutableName {\n    File executableFile = packagingDirectory\n        .listSync()\n        .where((e) => e.path.endsWith('.exe'))\n        .map((e) => File(e.path))\n        .first;\n    return p.basename(executableFile.path);\n  }\n\n  String get defaultInstallDirName => '{autopf64}\\\\$appName';\n\n  String get sourceDir => p.basename(packagingDirectory.path);\n\n  String get outputBaseFileName =>\n      p.basename(outputFile.path).replaceAll('.exe', '');\n\n  @override\n  Map<String, dynamic> toJson() {\n    return {\n      'script_template': scriptTemplate,\n      'app_id': appId,\n      'arch': arch,\n      'app_name': appName,\n      'app_version': appVersion.toString(),\n      'executable_name': executableName,\n      'display_name': displayName,\n      'publisher_name': publisherName,\n      'publisher_url': publisherUrl,\n      'create_desktop_icon': createDesktopIcon,\n      'launch_at_startup': launchAtStartup,\n      'install_dir_name': installDirName,\n      'setup_icon_file': setupIconFile,\n      'privileges_required': privilegesRequired,\n      'locales': locales,\n    }..removeWhere((key, value) => value == null);\n  }\n}\n\nclass MakeExeConfigLoader extends DefaultMakeConfigLoader {\n  @override\n  MakeConfig load(\n    Map<String, dynamic>? arguments,\n    Directory outputDirectory, {\n    required Directory buildOutputDirectory,\n    required List<File> buildOutputFiles,\n  }) {\n    final baseMakeConfig = super.load(\n      arguments,\n      outputDirectory,\n      buildOutputDirectory: buildOutputDirectory,\n      buildOutputFiles: buildOutputFiles,\n    );\n    final map = loadMakeConfigYaml(\n      '$platform/packaging/$packageFormat/make_config.yaml',\n    );\n    return MakeExeConfig.fromJson(map).copyWith(baseMakeConfig)\n      ..isInstaller = true;\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/hap/app_package_maker_hap.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_packager/src/api/app_package_maker.dart';\n\nclass AppPackageMakerHap extends AppPackageMaker {\n  @override\n  String get name => 'hap';\n  @override\n  String get platform => 'ohos';\n  @override\n  String get packageFormat => 'hap';\n\n  @override\n  Future<MakeResult> make(MakeConfig config) {\n    File pkgFile = config.buildOutputFiles.first;\n    pkgFile.copySync(config.outputFile.path);\n    return Future.value(resultResolver.resolve(config));\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/ipa/app_package_maker_ipa.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_packager/src/api/app_package_maker.dart';\n\nclass AppPackageMakerIpa extends AppPackageMaker {\n  @override\n  String get name => 'ipa';\n  @override\n  String get platform => 'ios';\n  @override\n  String get packageFormat => 'ipa';\n\n  @override\n  Future<MakeResult> make(MakeConfig config) {\n    File pkgFile = config.buildOutputFiles.first;\n    pkgFile.copySync(config.outputFile.path);\n    return Future.value(resultResolver.resolve(config));\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/makers.dart",
    "content": "export 'aab/app_package_maker_aab.dart';\nexport 'apk/app_package_maker_apk.dart';\nexport 'appimage/app_package_maker_appimage.dart';\nexport 'deb/app_package_maker_deb.dart';\nexport 'direct/app_package_maker_direct.dart';\nexport 'dmg/app_package_maker_dmg.dart';\nexport 'exe/app_package_maker_exe.dart';\nexport 'ipa/app_package_maker_ipa.dart';\nexport 'msix/app_package_maker_msix.dart';\nexport 'pkg/app_package_maker_pkg.dart';\nexport 'rpm/app_package_maker_rpm.dart';\nexport 'zip/app_package_maker_zip.dart';\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/msix/app_package_maker_msix.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_packager/src/api/app_package_maker.dart';\nimport 'package:flutter_app_packager/src/makers/msix/make_msix_config.dart';\nimport 'package:msix/msix.dart';\nimport 'package:path/path.dart' as p;\n\nclass AppPackageMakerMsix extends AppPackageMaker {\n  @override\n  String get name => 'msix';\n  @override\n  String get platform => 'windows';\n  @override\n  bool get isSupportedOnCurrentPlatform => Platform.isWindows;\n  @override\n  String get packageFormat => 'msix';\n\n  @override\n  MakeConfigLoader get configLoader {\n    return MakeMsixConfigLoader()\n      ..platform = platform\n      ..packageFormat = packageFormat;\n  }\n\n  @override\n  Future<MakeResult> make(MakeConfig config) {\n    return _make(\n      config.buildOutputDirectory,\n      outputDirectory: config.outputDirectory,\n      makeConfig: config as MakeMsixConfig,\n    );\n  }\n\n  Future<MakeResult> _make(\n    Directory appDirectory, {\n    required Directory outputDirectory,\n    required MakeMsixConfig makeConfig,\n  }) async {\n    makeConfig.output_path = makeConfig.outputFile.parent.path;\n    makeConfig.output_name =\n        p.basenameWithoutExtension(makeConfig.outputFile.path);\n    makeConfig.build_windows = 'false';\n\n    Map<String, dynamic> makeConfigJson = makeConfig.toJson();\n    List<String> arguments = [];\n    for (String key in makeConfigJson.keys) {\n      dynamic value = makeConfigJson[key];\n      String newKey = key.replaceAll('_', '-');\n      if (newKey == 'msix-version') newKey = 'version';\n\n      if (value is Map) {\n        for (String subKey in value.keys) {\n          arguments.addAll(['--$newKey', '$subKey=${value[subKey]}']);\n        }\n      } else {\n        arguments.addAll(['--$newKey', value]);\n      }\n    }\n    await Msix(arguments).create();\n    return MakeResult(makeConfig);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/msix/make_msix_config.dart",
    "content": "// ignore_for_file: non_constant_identifier_names\n\nimport 'dart:io';\n\nimport 'package:flutter_app_packager/src/api/app_package_maker.dart';\n\nclass MakeMsixConfig extends MakeConfig {\n  MakeMsixConfig({\n    this.display_name,\n    this.publisher_display_name,\n    this.identity_name,\n    this.msix_version,\n    this.logo_path,\n    this.trim_logo,\n    this.capabilities,\n    this.languages,\n    this.file_extension,\n    this.protocol_activation,\n    this.add_execution_alias,\n    this.enable_at_startup,\n    this.store,\n    this.debug,\n    this.output_path,\n    this.output_name,\n    this.architecture,\n    this.build_windows,\n    this.certificate_path,\n    this.certificate_password,\n    this.publisher,\n    this.signtool_options,\n    this.sign_msix,\n    this.install_certificate,\n  });\n\n  factory MakeMsixConfig.fromJson(Map<String, dynamic> json) {\n    return MakeMsixConfig(\n      display_name: json['display_name'],\n      publisher_display_name: json['publisher_display_name'],\n      identity_name: json['identity_name'],\n      msix_version: json['msix_version'],\n      logo_path: json['logo_path'],\n      trim_logo: json['trim_logo'],\n      capabilities: json['capabilities'],\n      languages: json['languages'],\n      file_extension: json['file_extension'],\n      protocol_activation: json['protocol_activation'],\n      add_execution_alias: json['add_execution_alias'],\n      enable_at_startup: json['enable_at_startup'],\n      store: json['store'],\n      debug: json['debug'],\n      output_path: json['output_path'],\n      output_name: json['output_name'],\n      architecture: json['architecture'],\n      build_windows: json['build_windows'],\n      certificate_path: json['certificate_path'],\n      certificate_password: json['certificate_password'],\n      publisher: json['publisher'],\n      signtool_options: json['signtool_options'],\n      sign_msix: json['sign_msix'],\n      install_certificate: json['install_certificate'],\n    );\n  }\n  // MSIX configuration\n\n  /// A friendly app name that can be displayed to users.\n  String? display_name;\n\n  /// A friendly name for the publisher that can be displayed to users.\n  String? publisher_display_name;\n\n  /// Defines the unique identifier for the app.\n  String? identity_name;\n\n  /// The version number of the package, in `a.b.c.d` format.\n  String? msix_version;\n\n  /// Path to an [image file] for use as the app icon (size recommended at least 400x400px).\n  String? logo_path;\n\n  /// If `false`, don't trim the logo image, default is `true`.\n  String? trim_logo;\n\n  /// List of the [capabilities][windows capabilities] the app requires.\n  String? capabilities;\n\n  /// Declares the language resources contained in the package.\n  String? languages;\n\n  /// File extensions that the app may be registered to open.\n  String? file_extension;\n\n  /// [Protocol activation] that will open the app.\n  String? protocol_activation;\n\n  /// Add an alias to active the app, use the `pubspec.yaml` `name:` value, so if your app calls 'Flutter_App', user can activate the app using `flutterapp` command.\n  String? add_execution_alias;\n\n  /// App start at startup or user log-in.\n  String? enable_at_startup;\n\n  /// Generate a MSIX file for publishing to the Microsoft Store.                                                                                         |\n  String? store;\n\n  // Build configuration\n\n  /// Create MSIX from the **debug** or **release** build files (`\\build\\windows\\runner\\<Debug/Release>`), **release** is the default.                                 | `true`                                                                                          |\n  String? debug;\n\n  /// The directory where the output MSIX file should be stored.                                                                                            | `C:\\src\\some\\folder`                                                                             |\n  String? output_path;\n\n  /// The filename that should be given to the created MSIX file.                                                                                           | `flutterApp_dev`                                                                                     |\n  String? output_name;\n\n  /// Describes the architecture of the code in the package, `x64` or `x86`, `x64` is default.                                                                                               | `x64`                                                                                           |\n  String? architecture;\n\n  /// If `false`, don't run the build command `flutter build windows`, default is `true`.                                                                   | `true`                                                                                          |\n  String? build_windows;\n\n  /// Path to the certificate content to place in the store.                                                                                                | `C:\\certs\\signcert.pfx`                                                                         |\n  String? certificate_path;\n\n  /// Password for the certificate.                                                                                                                         | `1234`                                                                                          |\n  String? certificate_password;\n\n  /// The `Subject` value in the certificate.                                                                                                               | `CN=BF212345-5644-46DF-8668-014043C1B138` or `CN=Contoso Software, O=Contoso Corporation, C=US` |\n  String? publisher;\n\n  /// Options to be provided to the `signtool` for app signing (see below.)                                                                                 | `/v /fd SHA256 /f C:/Users/me/Desktop/my.cer`                                                   |\n  String? signtool_options;\n\n  /// If `false`, don't sign the msix file, default is `true`.                                                                                         | `true`                                                                                          |\n  String? sign_msix;\n\n  /// If `false`, don't try to install the certificate, default is `true`.                                                                                         | `true`                                                                                          |\n  String? install_certificate;\n\n  @override\n  Map<String, dynamic> toJson() {\n    return {\n      'display_name': display_name,\n      'publisher_display_name': publisher_display_name,\n      'identity_name': identity_name,\n      'msix_version': msix_version,\n      'logo_path': logo_path,\n      'trim_logo': trim_logo,\n      'capabilities': capabilities,\n      'languages': languages,\n      'file_extension': file_extension,\n      'protocol_activation': protocol_activation,\n      'add_execution_alias': add_execution_alias,\n      'enable_at_startup': enable_at_startup,\n      'store': store,\n      'debug': debug,\n      'output_path': output_path,\n      'output_name': output_name,\n      'architecture': architecture,\n      'build_windows': build_windows,\n      'certificate_path': certificate_path,\n      'certificate_password': certificate_password,\n      'publisher': publisher,\n      'signtool_options': signtool_options,\n      'sign_msix': sign_msix,\n      'install_certificate': install_certificate,\n    }..removeWhere((key, value) => value == null);\n  }\n}\n\nclass MakeMsixConfigLoader extends DefaultMakeConfigLoader {\n  @override\n  MakeConfig load(\n    Map<String, dynamic>? arguments,\n    Directory outputDirectory, {\n    required Directory buildOutputDirectory,\n    required List<File> buildOutputFiles,\n  }) {\n    final baseMakeConfig = super.load(\n      arguments,\n      outputDirectory,\n      buildOutputDirectory: buildOutputDirectory,\n      buildOutputFiles: buildOutputFiles,\n    );\n    final map = loadMakeConfigYaml(\n      '$platform/packaging/$packageFormat/make_config.yaml',\n    );\n    return MakeMsixConfig.fromJson(map).copyWith(baseMakeConfig);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/pacman/app_package_maker_pacman.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_packager/src/api/app_package_maker.dart';\nimport 'package:flutter_app_packager/src/makers/pacman/make_pacman_config.dart';\nimport 'package:path/path.dart' as path;\nimport 'package:shell_executor/shell_executor.dart';\n\nclass AppPackageMakerPacman extends AppPackageMaker {\n  @override\n  String get name => 'pacman';\n  @override\n  String get platform => 'linux';\n  @override\n  bool get isSupportedOnCurrentPlatform => Platform.isLinux;\n  @override\n  String get packageFormat => 'pacman';\n\n  @override\n  MakeConfigLoader get configLoader {\n    return MakePacmanConfigLoader()\n      ..platform = platform\n      ..packageFormat = packageFormat;\n  }\n\n  @override\n  Future<MakeResult> make(MakeConfig config) {\n    return _make(\n      config.buildOutputDirectory,\n      outputDirectory: config.outputDirectory,\n      makeConfig: config as MakePacmanConfig,\n    );\n  }\n\n  Future<MakeResult> _make(\n    Directory appDirectory, {\n    required Directory outputDirectory,\n    required MakePacmanConfig makeConfig,\n  }) async {\n    final files = makeConfig.toFilesString();\n\n    Directory packagingDirectory = makeConfig.packagingDirectory;\n\n    /// Need to create following directories\n    /// /usr/share/$appBinaryName\n    /// /usr/share/applications\n    /// /usr/share/icons/hicolor/128x128/apps\n    /// /usr/share/icons/hicolor/256x256/apps\n\n    final applicationsDir =\n        path.join(packagingDirectory.path, 'usr/share/applications');\n    final icon128Dir = path.join(\n      packagingDirectory.path,\n      'usr/share/icons/hicolor/128x128/apps',\n    );\n    final icon256Dir = path.join(\n      packagingDirectory.path,\n      'usr/share/icons/hicolor/256x256/apps',\n    );\n    final metainfoDir =\n        path.join(packagingDirectory.path, 'usr/share/metainfo');\n    final mkdirProcessResult = await $('mkdir', [\n      '-p',\n      path.join(packagingDirectory.path, 'usr/share', makeConfig.appBinaryName),\n      applicationsDir,\n      if (makeConfig.metainfo != null) metainfoDir,\n      if (makeConfig.icon != null) ...[icon128Dir, icon256Dir],\n    ]);\n\n    if (mkdirProcessResult.exitCode != 0) throw MakeError();\n\n    if (makeConfig.icon != null) {\n      final iconFile = File(makeConfig.icon!);\n      if (!iconFile.existsSync()) {\n        throw MakeError(\"provided icon ${makeConfig.icon} path wasn't found\");\n      }\n\n      await iconFile.copy(\n        path.join(\n          icon128Dir,\n          makeConfig.appBinaryName + path.extension(makeConfig.icon!),\n        ),\n      );\n      await iconFile.copy(\n        path.join(\n          icon256Dir,\n          makeConfig.appBinaryName + path.extension(makeConfig.icon!),\n        ),\n      );\n    }\n    if (makeConfig.metainfo != null) {\n      final metainfoPath =\n          path.join(Directory.current.path, makeConfig.metainfo!);\n      final metainfoFile = File(metainfoPath);\n      if (!metainfoFile.existsSync()) {\n        throw MakeError(\"Metainfo $metainfoPath path wasn't found\");\n      }\n      await metainfoFile.copy(\n        path.join(\n          metainfoDir,\n          makeConfig.appBinaryName + path.extension(makeConfig.metainfo!, 2),\n        ),\n      );\n    }\n\n    // create & write the files got from makeConfig\n    final installFile = File(path.join(packagingDirectory.path, '.INSTALL'));\n    final pkgInfoFile = File(path.join(packagingDirectory.path, '.PKGINFO'));\n    final desktopEntryFile =\n        File(path.join(applicationsDir, '${makeConfig.appBinaryName}.desktop'));\n\n    if (!installFile.existsSync()) installFile.createSync();\n    if (!pkgInfoFile.existsSync()) pkgInfoFile.createSync();\n    if (!desktopEntryFile.existsSync()) desktopEntryFile.createSync();\n\n    await installFile.writeAsString(files['INSTALL']!);\n    await pkgInfoFile.writeAsString(files['PKGINFO']!);\n    await desktopEntryFile.writeAsString(files['DESKTOP']!);\n\n    // copy the application binary to /usr/share/$appBinaryName\n    await $('cp', [\n      '-fr',\n      '${appDirectory.path}/.',\n      '${packagingDirectory.path}/usr/share/${makeConfig.appBinaryName}/',\n    ]);\n\n    // MTREE Metadata using bsdtar and fakeroot\n    ProcessResult mtreeResult = await $(\n      'bsdtar',\n      [\n        '-czf',\n        '.MTREE',\n        '--format=mtree',\n        '--options=!all,use-set,type,uid,gid,mode,time,size,md5,sha256,link',\n        '.PKGINFO',\n        '.INSTALL',\n        'usr',\n      ],\n      environment: {\n        'LANG': 'C',\n      },\n      workingDirectory: packagingDirectory.path,\n    );\n    if (mtreeResult.exitCode != 0) {\n      throw MakeError(mtreeResult.stderr);\n    }\n\n    // create the pacman package using fakeroot and bsdtar\n    // fakeroot -- env LANG=C bsdtar -cf - .MTREE .PKGINFO * | xz -c -z - > $pkgname-$pkgver-$pkgrel-$arch.tar.xz\n\n    ProcessResult archiveResult = await $(\n      'bsdtar',\n      [\n        '-cf',\n        'temptar',\n        '.MTREE',\n        '.INSTALL',\n        '.PKGINFO',\n        'usr',\n      ],\n      environment: {\n        'LANG': 'C',\n      },\n      workingDirectory: packagingDirectory.path,\n    );\n    if (archiveResult.exitCode != 0) {\n      throw MakeError(archiveResult.stderr);\n    }\n\n    ProcessResult processResult = await $(\n      'xz',\n      [\n        '-z',\n        'temptar',\n      ],\n      workingDirectory: packagingDirectory.path,\n    );\n\n    if (processResult.exitCode != 0) {\n      throw MakeError(processResult.stderr);\n    }\n\n    // copy file from temptar.xz to the makeConfig.outputFile.path\n    final copyResult = await $(\n      'mv',\n      [\n        '${packagingDirectory.path}/temptar.xz',\n        makeConfig.outputFile.path,\n      ],\n    );\n    if (copyResult.exitCode != 0) {\n      throw MakeError(copyResult.stderr);\n    }\n\n    packagingDirectory.deleteSync(recursive: true);\n    return MakeResult(makeConfig);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/pacman/make_pacman_config.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_packager/src/api/app_package_maker.dart';\n\n// Ported from https://gist.github.com/Earnestly/bebad057f40a662b5cc3\n// format of make_config for pacman\n/*\n# the name used to display in the OS. Specifically desktop\n# entry name\ndisplay_name: Hola Amigos\n\n# package name for arch repository\n# the name should be all lowercase with -+.\npackage_name: hola-amigos\n\nlicenses:\n  - MIT\n\nmaintainer:\n  name: Gamer Boy 69\n  email: rickastley@gmail.lol\n\n# the size of binary in kilobyte\ninstalled_size: 24400\n\n# direct dependencies required by the application\n# refer: https://man.archlinux.org/man/PKGBUILD.5#OPTIONS_AND_DIRECTIVES\ndependencies:\n  - mysupercooldep\n\n# optional dependencies not so much required by the application\n# refer: https://man.archlinux.org/man/PKGBUILD.5#OPTIONS_AND_DIRECTIVES\noptional_dependencies:\n  - iamalwaysoptional\n\n# refer: https://man.archlinux.org/man/PKGBUILD.5#OPTIONS_AND_DIRECTIVES\nprovides:\n  - whatsup\n\n# refer: https://man.archlinux.org/man/PKGBUILD.5#OPTIONS_AND_DIRECTIVES\noptions:\n  - zipman\n\n# refer: https://man.archlinux.org/man/PKGBUILD.5#OPTIONS_AND_DIRECTIVES\nconflicts:\n  - libwhatsup\n\n# refer: https://man.archlinux.org/man/PKGBUILD.5#OPTIONS_AND_DIRECTIVES\nreplaces:\n  - yourdep\n\n# refer: https://man.archlinux.org/man/PKGBUILD.5#OPTIONS_AND_DIRECTIVES\nprovides:\n  - libx11\n\npostinstall_scripts:\n  - echo `Installed my awesome app`\npostupgrade_scripts:\n  - echo `Supercharger my awesome app`\npostuninstall_scripts:\n  - echo `Surprised Pickachu face`\n\n\n# application icon path relative to project url\nicon: assets/logo.png\n\nkeywords:\n  - Hello\n  - World\n  - Test\n  - Application\n\n# a name to categorize the app into a section of application\ngeneric_name: Hobby Application\n\n# supported mime types that can be opened using this application\nsupported_mime_type:\n  - audio/mpeg\n\n# shown when right clicked the desktop entry icons\nactions:\n  - Gallery\n  - Create\n\n# the categories the application belong to\n# refer: https://specifications.freedesktop.org/menu-spec/latest/\ncategories:\n  - Music\n  - Media\n\n# let OS know if the application can be run on start_up. If it's false \n# the application will deny to the OS if it was added as a start_up \n# application\nstartup_notify: true  \n*/\n\nclass MakePacmanConfig extends MakeLinuxPackageConfig {\n  MakePacmanConfig({\n    required this.displayName,\n    required this.packageName,\n    this.installedSize,\n    required this.maintainer,\n    this.packageRelease = 1,\n    List<String>? postinstallScripts,\n    List<String>? postupgradeScripts,\n    List<String>? postuninstallScripts,\n    this.actions,\n    this.categories,\n    this.dependencies,\n    this.genericName,\n    this.optDependencies,\n    this.options,\n    this.startupNotify = false,\n    this.groups = const ['default'],\n    this.licenses = const ['unknown'],\n    this.icon,\n    this.metainfo,\n    this.keywords,\n    this.provides,\n    this.conflicts,\n    this.replaces,\n    this.supportedMimeType,\n  })  : _postinstallScripts = postinstallScripts ?? [],\n        _postupgradeScripts = postupgradeScripts ?? [],\n        _postremoveScripts = postuninstallScripts ?? [];\n\n  factory MakePacmanConfig.fromJson(Map<String, dynamic> map) {\n    return MakePacmanConfig(\n      displayName: map['display_name'],\n      packageName: map['package_name'], //\n      packageRelease: int.tryParse(map['package_release'] ?? '1') ?? 1,\n      maintainer:\n          \"${map['maintainer']['name']} <${map['maintainer']['email']}>\",\n      dependencies: map['dependencies'] != null\n          ? List.castFrom<dynamic, String>(map['dependencies'])\n          : null,\n      conflicts: map['conflicts'] != null\n          ? List.castFrom<dynamic, String>(map['conflicts'])\n          : null,\n      replaces: map['replaces'] != null\n          ? List.castFrom<dynamic, String>(map['replaces'])\n          : null,\n      options: map['options'] != null\n          ? List.castFrom<dynamic, String>(map['options'])\n          : null,\n      optDependencies: map['optional_dependencies'] != null\n          ? List.castFrom<dynamic, String>(map['optional_dependencies'])\n          : null,\n      licenses: map['licenses'] != null\n          ? List.castFrom<dynamic, String>(map['licenses'])\n          : ['unknown'],\n      groups: map['groups'] != null\n          ? List.castFrom<dynamic, String>(map['groups'])\n          : ['default'],\n      provides: map['provides'] != null\n          ? List.castFrom<dynamic, String>(map['provides'])\n          : null,\n      postinstallScripts: map['postinstall_scripts'] != null\n          ? List.castFrom<dynamic, String>(map['postinstall_scripts'])\n          : null,\n      postuninstallScripts: map['postuninstall_scripts'] != null\n          ? List.castFrom<dynamic, String>(map['postuninstall_scripts'])\n          : null,\n      postupgradeScripts: map['postupgrade_scripts'] != null\n          ? List.castFrom<dynamic, String>(map['postupgrade_scripts'])\n          : null,\n      keywords: map['keywords'] != null\n          ? List.castFrom<dynamic, String>(map['keywords'])\n          : null,\n      supportedMimeType: map['supported_mime_type'] != null\n          ? List.castFrom<dynamic, String>(map['supported_mime_type'])\n          : null,\n      actions: map['actions'] != null\n          ? List.castFrom<dynamic, String>(map['actions'])\n          : null,\n      categories: map['categories'] != null\n          ? List.castFrom<dynamic, String>(map['categories'])\n          : null,\n      startupNotify: map['startup_notify'],\n      genericName: map['generic_name'],\n      installedSize: map['installed_size'],\n      icon: map['icon'],\n      metainfo: map['metainfo'],\n    );\n  }\n\n  String displayName;\n  String packageName;\n  String maintainer;\n  int packageRelease;\n  int? installedSize;\n  List<String> licenses;\n  List<String> groups;\n  String? icon;\n  String? metainfo;\n  String? genericName;\n  bool startupNotify;\n  List<String>? options;\n  List<String>? dependencies;\n  List<String>? optDependencies;\n  List<String>? conflicts;\n  List<String>? replaces;\n  List<String>? provides;\n  List<String> _postinstallScripts;\n  List<String> _postupgradeScripts;\n  List<String> _postremoveScripts;\n  List<String>? keywords;\n  List<String>? supportedMimeType;\n  List<String>? actions;\n  List<String>? categories;\n\n  List<String> get postinstallScripts => [\n        'ln -s /usr/share/$appBinaryName/$appBinaryName /usr/bin/$appBinaryName',\n        'chmod +x /usr/bin/$appBinaryName',\n        ..._postinstallScripts,\n      ];\n\n  List<String> get postuninstallScripts => [\n        'rm /usr/bin/$appBinaryName',\n        ..._postremoveScripts,\n      ];\n\n  List<String> get postupgradeScripts => _postupgradeScripts;\n\n  @override\n  Map<String, dynamic> toJson() {\n    return {\n      'PKGINFO': {\n        'pkgname': packageName,\n        'pkgver': appVersion.toString(),\n        'pkgdesc': pubspec.description,\n        'packager': maintainer,\n        'size': installedSize,\n        'license': '(${licenses.join(', ')})',\n        'groups': '(${groups.join(', ')})',\n        'arch': '(${_getArchitecture()})',\n        'url': pubspec.homepage,\n        'options': options != null ? \"(${options!.join(', ')})\" : null,\n        'depends':\n            dependencies != null ? \"(${dependencies!.join(', ')})\" : null,\n        'optdepends':\n            optDependencies != null ? \"(${optDependencies!.join(', ')})\" : null,\n        'conflicts': conflicts != null ? \"(${conflicts!.join(', ')})\" : null,\n        'replaces': replaces != null ? \"(${replaces!.join(', ')})\" : null,\n        'provides': provides != null ? \"(${provides!.join(', ')})\" : null,\n      }..removeWhere((key, value) => value == null),\n      'DESKTOP': {\n        'Type': 'Application',\n        'Version': appVersion.toString(),\n        'Name': displayName,\n        'GenericName': genericName,\n        'Icon': appBinaryName,\n        'Exec': '$appBinaryName %U',\n        'Actions': actions != null && actions!.isNotEmpty\n            ? '${actions!.join(';')};'\n            : null,\n        'MimeType': supportedMimeType != null && supportedMimeType!.isNotEmpty\n            ? '${supportedMimeType!.join(';')};'\n            : null,\n        'Categories': categories != null && categories!.isNotEmpty\n            ? '${categories!.join(';')};'\n            : null,\n        'Keywords': keywords != null && keywords!.isNotEmpty\n            ? '${keywords!.join(';')};'\n            : null,\n        'StartupNotify': startupNotify,\n      }..removeWhere((key, value) => value == null),\n    };\n  }\n\n  Map<String, String> toFilesString() {\n    final json = toJson();\n    final pkginfoFile =\n        '${(json['PKGINFO'] as Map<String, dynamic>).entries.map(\n              (e) => '${e.key}=${e.value}',\n            ).join('\\n')}\\n';\n    final installFileMap = {\n      'post_install': postinstallScripts.join('\\n\\t'),\n      'post_upgrade':\n          postupgradeScripts.isNotEmpty ? postupgradeScripts.join('\\n') : null,\n      'post_remove': postuninstallScripts.join('\\n'),\n    }..removeWhere((key, value) => value == null);\n\n    final installFile = installFileMap.entries\n        .map(\n          (e) => '${e.key}() {\\n\\t${e.value}\\n}',\n        )\n        .join('\\n');\n\n    final desktopFile = [\n      '[Desktop Entry]',\n      ...(json['DESKTOP'] as Map<String, dynamic>).entries.map(\n            (e) => '${e.key}=${e.value}',\n          ),\n    ].join('\\n');\n    final map = {\n      'PKGINFO': pkginfoFile,\n      'DESKTOP': desktopFile,\n      'INSTALL': installFile,\n    };\n    return map;\n  }\n}\n\nclass MakePacmanConfigLoader extends DefaultMakeConfigLoader {\n  @override\n  MakeConfig load(\n    Map<String, dynamic>? arguments,\n    Directory outputDirectory, {\n    required Directory buildOutputDirectory,\n    required List<File> buildOutputFiles,\n  }) {\n    final baseMakeConfig = super.load(\n      arguments,\n      outputDirectory,\n      buildOutputDirectory: buildOutputDirectory,\n      buildOutputFiles: buildOutputFiles,\n    );\n    final map = loadMakeConfigYaml(\n      '$platform/packaging/$packageFormat/make_config.yaml',\n    );\n    return MakePacmanConfig.fromJson(map).copyWith(baseMakeConfig);\n  }\n}\n\nString _getArchitecture() {\n  final result = Process.runSync('uname', ['-m']);\n  if ('${result.stdout}'.trim() == 'aarch64') {\n    return 'aarch64';\n  } else {\n    return 'x86_64';\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/pkg/app_package_maker_pkg.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_packager/src/api/app_package_maker.dart';\nimport 'package:flutter_app_packager/src/makers/pkg/make_pkg_config.dart';\nimport 'package:shell_executor/shell_executor.dart';\n\nclass AppPackageMakerPkg extends AppPackageMaker {\n  @override\n  String get name => 'pkg';\n  @override\n  String get platform => 'macos';\n  @override\n  String get packageFormat => 'pkg';\n\n  @override\n  MakeConfigLoader get configLoader {\n    return MakePkgConfigLoader()\n      ..platform = platform\n      ..packageFormat = packageFormat;\n  }\n\n  @override\n  Future<MakeResult> make(MakeConfig config) async {\n    MakePkgConfig makeConfig = config as MakePkgConfig;\n    File appFile = config.buildOutputFiles.first;\n\n    File outputFile = config.outputFile;\n    File unsignedPkgFile = File(\n      outputFile.path.replaceFirst(\n        '.$packageFormat',\n        '-unsigned.$packageFormat',\n      ),\n    );\n\n    await $('xcrun', [\n      'productbuild',\n      '--root',\n      appFile.path,\n      makeConfig.installPath ?? '/Applications/',\n      unsignedPkgFile.path,\n    ]);\n    if (makeConfig.signIdentity != null) {\n      await $('xcrun', [\n        'productsign',\n        '--sign',\n        makeConfig.signIdentity!,\n        unsignedPkgFile.path,\n        outputFile.path,\n      ]);\n      unsignedPkgFile.deleteSync();\n    } else {\n      unsignedPkgFile.renameSync(outputFile.path);\n    }\n    return Future.value(resultResolver.resolve(config));\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/pkg/make_pkg_config.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_packager/src/api/app_package_maker.dart';\n\nclass MakePkgConfig extends MakeConfig {\n  MakePkgConfig({\n    this.installPath,\n    this.signIdentity,\n  });\n\n  factory MakePkgConfig.fromJson(Map<String, dynamic> json) {\n    return MakePkgConfig(\n      installPath: json['install-path'],\n      signIdentity: json['sign-identity'],\n    );\n  }\n  final String? installPath;\n  final String? signIdentity;\n\n  @override\n  Map<String, dynamic> toJson() {\n    return {\n      'install-path': installPath,\n      'sign-identity': signIdentity,\n    }..removeWhere((key, value) => value == null);\n  }\n}\n\nclass MakePkgConfigLoader extends DefaultMakeConfigLoader {\n  @override\n  MakeConfig load(\n    Map<String, dynamic>? arguments,\n    Directory outputDirectory, {\n    required Directory buildOutputDirectory,\n    required List<File> buildOutputFiles,\n  }) {\n    final baseMakeConfig = super.load(\n      arguments,\n      outputDirectory,\n      buildOutputDirectory: buildOutputDirectory,\n      buildOutputFiles: buildOutputFiles,\n    );\n    final map = loadMakeConfigYaml(\n      '$platform/packaging/$packageFormat/make_config.yaml',\n    );\n    return MakePkgConfig.fromJson(map).copyWith(baseMakeConfig);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/rpm/app_package_maker_rpm.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_packager/src/api/app_package_maker.dart';\nimport 'package:flutter_app_packager/src/makers/rpm/make_rpm_config.dart';\nimport 'package:flutter_app_packager/src/makers/rpm/rpmbuild.dart';\nimport 'package:path/path.dart' as path;\nimport 'package:shell_executor/shell_executor.dart';\n\nclass AppPackageMakerRPM extends AppPackageMaker {\n  @override\n  List<Command> get requirements => [rpmbuild];\n\n  @override\n  String get name => 'rpm';\n  @override\n  String get platform => 'linux';\n  @override\n  String get packageFormat => 'rpm';\n\n  @override\n  bool get isSupportedOnCurrentPlatform => Platform.isLinux;\n\n  @override\n  MakeConfigLoader get configLoader {\n    return MakeRpmConfigLoader()\n      ..platform = platform\n      ..packageFormat = packageFormat;\n  }\n\n  @override\n  Future<MakeResult> make(MakeConfig config) {\n    return _make(\n      config.buildOutputDirectory,\n      outputDirectory: config.outputDirectory,\n      makeConfig: config as MakeRPMConfig,\n    );\n  }\n\n  Future<MakeResult> _make(\n    Directory appDirectory, {\n    required Directory outputDirectory,\n    required MakeRPMConfig makeConfig,\n  }) async {\n    final files = makeConfig.toFilesString();\n\n    Directory packagingDirectory = makeConfig.packagingDirectory;\n\n    // making rpm setup directory\n\n    final rpmbuild = [\n      'BUILD',\n      'BUILDROOT',\n      'RPMS',\n      'SOURCES',\n      'SPECS',\n      'SRPMS',\n    ];\n\n    final rpmbuildDirPath =\n        path.join(packagingDirectory.absolute.path, 'rpmbuild');\n\n    for (final dir in rpmbuild) {\n      final dirPath = path.join(rpmbuildDirPath, dir);\n      final dirFile = Directory(dirPath);\n      if (!dirFile.existsSync()) {\n        dirFile.createSync(recursive: true);\n      }\n    }\n\n    // making rpmbuild/BUILD/[appName] directory\n    final buildPath = path.join(rpmbuildDirPath, 'BUILD');\n    final buildRoot = path.join(buildPath, makeConfig.appName);\n    final specsPath = path.join(rpmbuildDirPath, 'SPECS');\n    final rpmPath =\n        path.join(rpmbuildDirPath, 'RPMS', makeConfig.buildArch ?? 'x86_64');\n    final buildWivesDirFile = Directory(buildRoot);\n    if (!buildWivesDirFile.existsSync()) {\n      buildWivesDirFile.createSync(recursive: true);\n    }\n\n    /// copying app files to rpmbuild/BUILD/[appName] directory\n    final bundleFiles = appDirectory.listSync();\n    for (final file in bundleFiles) {\n      await $(\n        'cp',\n        [\n          '-r',\n          file.path,\n          buildRoot,\n        ],\n      );\n    }\n\n    // fix lib_*_plugin.so rpath\n    //\n    // more details: https://github.com/flutter/flutter/issues/65400\n    final libFiles = Directory(path.join(buildRoot, 'lib')).listSync();\n    for (final file in libFiles) {\n      if (file is! File) continue;\n      if (!file.path.endsWith('.so')) continue;\n      // check if points to /home dir\n      final processResult = await $(\n        'patchelf',\n        [\n          '--print-rpath',\n          file.path,\n        ],\n      );\n      if (processResult.stdout.toString().contains('/home')) {\n        await $(\n          'patchelf',\n          [\n            '--set-rpath',\n            '\\$ORIGIN',\n            file.path,\n          ],\n        );\n      }\n    }\n\n    final iconFile = makeConfig.icon != null\n        ? File(path.join(Directory.current.path, makeConfig.icon!))\n        : null;\n\n    iconFile?.copy(\n      path.join(\n        buildPath,\n        makeConfig.appName + path.extension(iconFile.path),\n      ),\n    );\n\n    if (makeConfig.icon != null) {\n      final iconFile = File(makeConfig.icon!);\n      if (!iconFile.existsSync()) {\n        throw MakeError(\"provided icon ${makeConfig.icon} path wasn't found\");\n      }\n    }\n\n    // create & write the files got from makeConfig\n    final specFile = File(path.join(specsPath, '${makeConfig.appName}.spec'));\n    final desktopEntryFile =\n        File(path.join(buildPath, '${makeConfig.appName}.desktop'));\n\n    if (!specFile.existsSync()) specFile.createSync();\n    if (!desktopEntryFile.existsSync()) desktopEntryFile.createSync();\n\n    await specFile.writeAsString(files['SPEC']!);\n    await desktopEntryFile.writeAsString(files['DESKTOP']!);\n\n    // make the rpm\n    final processResult = await $(\n      'rpmbuild',\n      [\n        '--define',\n        '_topdir $rpmbuildDirPath',\n        '-bb',\n        specFile.path,\n      ],\n      environment: {'QA_RPATHS': (0x0001 | 0x0010).toString()},\n    );\n\n    if (processResult.exitCode != 0) {\n      throw MakeError();\n    }\n\n    final rpms = Directory(rpmPath).listSync();\n    for (var rpm in rpms) {\n      if (rpm is! File) continue;\n      await $(\n        'cp',\n        [\n          rpm.path,\n          makeConfig.outputFile.path,\n        ],\n      );\n      break;\n    }\n    packagingDirectory.deleteSync(recursive: true);\n    return MakeResult(makeConfig);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/rpm/make_rpm_config.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_packager/src/api/app_package_maker.dart';\n\nclass MakeRPMConfig extends MakeConfig {\n  MakeRPMConfig({\n    // Desktop file\n    required this.displayName,\n    this.startupNotify = true,\n    this.actions,\n    this.categories,\n    this.genericName,\n    this.icon,\n    this.keywords,\n    this.supportedMimeType,\n\n    // Spec file\n    this.summary,\n    this.group,\n    this.vendor,\n    this.packager,\n    this.packagerEmail,\n    this.license,\n    this.url,\n    this.buildArch,\n    this.requires,\n    this.buildRequires,\n    this.description,\n    this.prep,\n    this.build,\n    this.install,\n    this.postun,\n    this.files,\n    this.defattr,\n    this.attr,\n    this.changelog,\n  });\n\n  factory MakeRPMConfig.fromJson(Map<String, dynamic> json) {\n    return MakeRPMConfig(\n      displayName: json['display_name'] as String,\n      icon: json['icon'] as String?,\n      genericName: json['generic_name'] as String?,\n      startupNotify: json['startup_notify'] as bool?,\n      keywords: (json['keywords'] as List<dynamic>?)?.cast<String>(),\n      supportedMimeType:\n          (json['supported_mime_type'] as List<dynamic>?)?.cast<String>(),\n      actions: (json['actions'] as List<dynamic>?)?.cast<String>(),\n      categories: (json['categories'] as List<dynamic>?)?.cast<String>(),\n      summary: json['summary'] as String?,\n      group: json['group'] as String?,\n      vendor: json['vendor'] as String?,\n      packager: json['packager'] as String?,\n      packagerEmail: json['packagerEmail'] as String?,\n      license: json['license'] as String?,\n      url: json['url'] as String?,\n      buildArch: json['build_arch'] as String?,\n      requires: (json['requires'] as List<dynamic>?)?.cast<String>(),\n      buildRequires: (json['build_requires'] as List<dynamic>?)?.cast<String>(),\n      description: json['description'] as String?,\n      prep: json['prep'] as String?,\n      build: json['build'] as String?,\n      install: json['install'] as String?,\n      postun: json['postun'] as String?,\n      files: json['files'] as String?,\n      defattr: json['defattr'] as String?,\n      attr: json['attr'] as String?,\n      changelog: json['changelog'] as String?,\n    );\n  }\n\n  String displayName;\n  String? icon;\n  String? genericName;\n  bool? startupNotify;\n  List<String>? keywords;\n  List<String>? supportedMimeType;\n  List<String>? actions;\n  List<String>? categories;\n\n  //RPM preamble Spec file fields\n  String? summary;\n  String? group;\n  String? vendor;\n  String? packager;\n  String? packagerEmail;\n  String? license;\n  String? url;\n  String? buildArch;\n  List<String>? requires;\n  List<String>? buildRequires;\n  //RPM postamble Spec file fields\n  String? description;\n  String? prep;\n  String? build;\n  String? install;\n  String? postun;\n  String? files;\n  String? defattr;\n  String? attr;\n  String? changelog;\n\n  @override\n  Map<String, dynamic> toJson() {\n    return {\n      'SPEC': {\n        'preamble': {\n          'Name': appName,\n          'Version': appVersion.toString(),\n          'Release':\n              \"${appVersion.build.isNotEmpty ? appVersion.build.first : \"1\"}%{?dist}\",\n          'Summary': summary ?? pubspec.description,\n          'Group': group,\n          'Vendor': vendor,\n          'Packager':\n              packagerEmail != null ? '$packager <$packagerEmail>' : packager,\n          'License': license,\n          'URL': url,\n          'Requires': requires?.join(', '),\n          'BuildRequires': buildRequires?.join(', '),\n          'BuildArch': buildArch ?? 'x86_64',\n        }..removeWhere((key, value) => value == null),\n        'body': {\n          '%description': description ?? pubspec.description,\n          '%install': [\n            'mkdir -p %{buildroot}%{_bindir}',\n            'mkdir -p %{buildroot}%{_datadir}/%{name}',\n            'mkdir -p %{buildroot}%{_datadir}/applications',\n            'mkdir -p %{buildroot}%{_datadir}/pixmaps',\n            'cp -r %{name}/* %{buildroot}%{_datadir}/%{name}',\n            'ln -s %{_datadir}/%{name}/%{name} %{buildroot}%{_bindir}/%{name}',\n            'cp -r %{name}.desktop %{buildroot}%{_datadir}/applications',\n            'cp -r %{name}.png %{buildroot}%{_datadir}/pixmaps',\n            'update-mime-database %{_datadir}/mime &> /dev/null || :',\n          ].join('\\n'),\n          '%postun': ['update-mime-database %{_datadir}/mime &> /dev/null || :']\n              .join('\\n'),\n          '%files': [\n            '%{_bindir}/%{name}',\n            '%{_datadir}/%{name}',\n            '%{_datadir}/applications/%{name}.desktop',\n          ].join('\\n'),\n        }..removeWhere((key, value) => value == null),\n        'inline-body': {\n          '%defattr': '(-,root,root)',\n          '%attr': '(4755, root, root) %{_datadir}/pixmaps/%{name}.png',\n        },\n      },\n      'DESKTOP': {\n        'Type': 'Application',\n        'Version': appVersion.toString(),\n        'Name': displayName,\n        'GenericName': genericName,\n        'Icon': appName,\n        'Exec': '$appName %U',\n        'Actions': actions != null && actions!.isNotEmpty\n            ? '${actions!.join(';')};'\n            : null,\n        'MimeType': supportedMimeType != null && supportedMimeType!.isNotEmpty\n            ? '${supportedMimeType!.join(';')};'\n            : null,\n        'Categories': categories != null && categories!.isNotEmpty\n            ? '${categories!.join(';')};'\n            : null,\n        'Keywords': keywords != null && keywords!.isNotEmpty\n            ? '${keywords!.join(';')};'\n            : null,\n        'StartupNotify': startupNotify,\n      }..removeWhere((key, value) => value == null),\n    };\n  }\n\n  Map<String, String> toFilesString() {\n    final json = toJson();\n\n    final preamble = (json['SPEC']['preamble'] as Map)\n        .entries\n        .map((e) => '${e.key}: ${e.value}')\n        .join('\\n');\n    final body = (json['SPEC']['body'] as Map).entries.map(\n      (e) {\n        return '${e.key}\\n${e.value}\\n';\n      },\n    ).join('\\n');\n    final inlineBody = (json['SPEC']['inline-body'] as Map).entries.map(\n      (e) {\n        return '${e.key}${e.value}\\n';\n      },\n    ).join('\\n');\n\n    final desktopFile = [\n      '[Desktop Entry]',\n      ...(json['DESKTOP'] as Map<String, dynamic>).entries.map(\n            (e) => '${e.key}=${e.value}',\n          ),\n    ].join('\\n');\n    final map = {\n      'DESKTOP': desktopFile,\n      'SPEC': '$preamble\\n\\n$body\\n\\n$inlineBody',\n    };\n    return Map.castFrom<String, String?, String, String>(map);\n  }\n}\n\nclass MakeRpmConfigLoader extends DefaultMakeConfigLoader {\n  @override\n  MakeConfig load(\n    Map<String, dynamic>? arguments,\n    Directory outputDirectory, {\n    required Directory buildOutputDirectory,\n    required List<File> buildOutputFiles,\n  }) {\n    final baseMakeConfig = super.load(\n      arguments,\n      outputDirectory,\n      buildOutputDirectory: buildOutputDirectory,\n      buildOutputFiles: buildOutputFiles,\n    );\n    final map = loadMakeConfigYaml(\n      '$platform/packaging/$packageFormat/make_config.yaml',\n    );\n    return MakeRPMConfig.fromJson(map).copyWith(baseMakeConfig);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/rpm/rpmbuild.dart",
    "content": "import 'package:shell_executor/shell_executor.dart';\n\nclass _RpmBuild extends Command {\n  @override\n  String get executable => 'rpmbuild';\n\n  @override\n  Future<void> install() {\n    return Future.value();\n  }\n}\n\nfinal rpmbuild = _RpmBuild();\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/lib/src/makers/zip/app_package_maker_zip.dart",
    "content": "import 'dart:io';\n\nimport 'package:archive/archive_io.dart';\nimport 'package:flutter_app_packager/src/api/app_package_maker.dart';\nimport 'package:shell_executor/shell_executor.dart';\n\nclass AppPackageMakerZip extends AppPackageMaker {\n  AppPackageMakerZip(String platform) {\n    _platform = platform;\n  }\n\n  late String _platform;\n\n  @override\n  String get name => 'zip';\n  @override\n  String get platform => _platform;\n  @override\n  String get packageFormat => 'zip';\n\n  @override\n  Future<MakeResult> make(MakeConfig config) async {\n    Directory appDirectory = config.buildOutputDirectory;\n    Directory packagingDirectory = appDirectory;\n\n    if (platform == 'macos') {\n      // 由于使用 archive 在压缩时会导致 app 损坏，所以这里使用 7z 压缩。\n      packagingDirectory = config.packagingDirectory;\n      File appFile = appDirectory\n          .listSync()\n          .where((e) => e.path.endsWith('.app'))\n          .map((e) => File(e.path))\n          .first;\n      await $('cp', ['-RH', appFile.path, packagingDirectory.path]);\n      await $(\n        '7z',\n        ['a', config.outputFile.path, './${packagingDirectory.path}/*.app'],\n      );\n      packagingDirectory.deleteSync(recursive: true);\n    } else {\n      final zipFileEncoder = ZipFileEncoder();\n      zipFileEncoder.zipDirectory(\n        packagingDirectory,\n        filename: config.outputFile.path,\n        followLinks: true,\n      );\n    }\n    return resultResolver.resolve(config);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/pubspec.yaml",
    "content": "name: flutter_app_packager\ndescription: Package your Flutter app into OS-specific bundles (.dmg, .exe, etc.) via Dart or the command line.\nversion: 0.4.2\nhomepage: https://github.com/leanflutter/flutter_distributor\n\nenvironment:\n  sdk: \">=2.16.0 <4.0.0\"\n\ndependencies:\n  archive: ^3.4.10\n  io: ^1.0.3\n  liquid_engine: ^0.2.2\n  msix: ^3.16.6\n  mustache_template: ^2.0.0\n  path: ^1.8.1\n  pub_semver: ^2.1.0\n  pubspec_parse: ^1.1.0\n  shell_executor: ^0.1.5\n  yaml: ^3.1.0\n\ndev_dependencies:\n  dependency_validator: ^3.0.0\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_packager/test/src/api/make_config_test.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_packager/src/api/make_config.dart';\nimport 'package:pub_semver/pub_semver.dart';\nimport 'package:pubspec_parse/pubspec_parse.dart';\nimport 'package:test/test.dart';\n\nvoid main() {\n  group('MakeConfig', () {\n    test('#1', () {\n      final makeConfig = MakeConfig()\n        ..buildMode = 'release'\n        ..buildOutputDirectory = Directory('build')\n        ..buildOutputFiles = []\n        ..platform = 'android'\n        ..packageFormat = 'apk'\n        ..outputDirectory = Directory('dist/')\n        ..pubspec = Pubspec(\n          'test_app',\n          version: Version.parse('1.0.0'),\n        );\n      expect(\n        makeConfig.outputArtifactPath,\n        'dist/1.0.0/test_app-1.0.0-android.apk',\n      );\n    });\n    test('#2', () {\n      final makeConfig = MakeConfig()\n        ..buildMode = 'release'\n        ..buildOutputDirectory = Directory('build')\n        ..buildOutputFiles = []\n        ..platform = 'android'\n        ..packageFormat = 'apk'\n        ..outputDirectory = Directory('dist/')\n        ..pubspec = Pubspec(\n          'test_app',\n          version: Version.parse('1.0.0+1'),\n        );\n      expect(\n        makeConfig.outputArtifactPath,\n        'dist/1.0.0+1/test_app-1.0.0+1-android.apk',\n      );\n    });\n  });\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_publisher/.gitignore",
    "content": ".dart_tool/\n.packages\nbuild/\npubspec.lock  # Except for application packages"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_publisher/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021-present LiJianying <lijy91@foxmail.com>\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": "plugins/flutter_distributor/packages/flutter_app_publisher/analysis_options.yaml",
    "content": "include: package:mostly_reasonable_lints/analysis_options.yaml\n\nlinter:\n  rules:\n    avoid_print: false\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_publisher/lib/flutter_app_publisher.dart",
    "content": "library flutter_app_publisher;\n\nexport 'src/api/app_package_publisher.dart';\nexport 'src/flutter_app_publisher.dart';\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_publisher/lib/src/api/app_package_publisher.dart",
    "content": "import 'dart:io';\n\nimport 'package:pubspec_parse/pubspec_parse.dart';\nimport 'package:shell_executor/shell_executor.dart';\n\ntypedef PublishProgressCallback = void Function(int sent, int total);\n\nabstract class AppPackagePublisher {\n  List<Command> get requirements => [];\n\n  String get name => throw UnimplementedError();\n  List<String> get supportedPlatforms => throw UnimplementedError();\n\n  Future<PublishResult> publish(\n    FileSystemEntity fileSystemEntity, {\n    Map<String, String>? environment,\n    Map<String, dynamic>? publishArguments,\n    PublishProgressCallback? onPublishProgress,\n  });\n}\n\nclass PublishConfig {\n  Pubspec? _pubspec;\n\n  Pubspec get pubspec {\n    if (_pubspec == null) {\n      final yamlString = File('pubspec.yaml').readAsStringSync();\n      _pubspec = Pubspec.parse(yamlString);\n    }\n    return _pubspec!;\n  }\n}\n\nclass PublishResult {\n  PublishResult({\n    this.url,\n  });\n\n  final String? url;\n}\n\nclass PublishError extends Error {\n  PublishError([this.message]);\n  final String? message;\n\n  @override\n  String toString() {\n    var message = this.message;\n    return (message != null) ? 'PublishError: $message' : 'PublishError';\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_publisher/lib/src/flutter_app_publisher.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_publisher/src/api/app_package_publisher.dart';\nimport 'package:flutter_app_publisher/src/publishers/publishers.dart';\n\nclass FlutterAppPublisher {\n  final List<AppPackagePublisher> _publishers = [\n    AppPackagePublisherAppCenter(),\n    AppPackagePublisherAppStore(),\n    AppPackagePublisherFir(),\n    AppPackagePublisherFirebase(),\n    AppPackagePublisherFirebaseHosting(),\n    AppPackagePublisherGithub(),\n    AppPackagePublisherPgyer(),\n    AppPackagePublisherPlayStore(),\n    AppPackagePublisherQiniu(),\n    AppPackagePublisherVercel(),\n  ];\n\n  Future<PublishResult> publish(\n    FileSystemEntity fileSystemEntity, {\n    required String target,\n    Map<String, String>? environment,\n    Map<String, dynamic>? publishArguments,\n    PublishProgressCallback? onPublishProgress,\n  }) async {\n    AppPackagePublisher publisher = _publishers.firstWhere(\n      (e) => e.name == target,\n    );\n    return await publisher.publish(\n      fileSystemEntity,\n      environment: environment,\n      publishArguments: publishArguments,\n      onPublishProgress: onPublishProgress,\n    );\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_publisher/lib/src/publishers/appcenter/app_package_publisher_appcenter.dart",
    "content": "import 'dart:io';\nimport 'dart:typed_data';\n\nimport 'package:dio/dio.dart';\nimport 'package:flutter_app_publisher/src/api/app_package_publisher.dart';\nimport 'package:flutter_app_publisher/src/publishers/appcenter/publish_appcenter_config.dart';\nimport 'package:shell_executor/shell_executor.dart';\n\nconst _kUploadDomain = 'https://file.appcenter.ms/upload';\n\nclass AppPackagePublisherAppCenter extends AppPackagePublisher {\n  final Dio _dio = Dio();\n\n  @override\n  String get name => 'appcenter';\n\n  @override\n  List<String> get supportedPlatforms => [\n        'android',\n        'ios',\n        'linux',\n        'macos',\n        'windows',\n        'web',\n      ];\n\n  @override\n  Future<PublishResult> publish(\n    FileSystemEntity fileSystemEntity, {\n    Map<String, String>? environment,\n    Map<String, dynamic>? publishArguments,\n    PublishProgressCallback? onPublishProgress,\n  }) async {\n    File file = fileSystemEntity as File;\n    PublishAppCenterConfig publishConfig = PublishAppCenterConfig.parse(\n      environment,\n      publishArguments,\n    );\n    _dio.options = BaseOptions(\n      baseUrl: 'https://api.appcenter.ms/v0.1',\n      headers: {\n        'X-API-Token': publishConfig.apiToken,\n      },\n    );\n    // _dio.interceptors.add(LogInterceptor(\n    //   requestBody: true,\n    //   responseBody: true,\n    // ));\n\n    try {\n      // Creating release (1/7)\n      Map<String, dynamic> release = await _createRelease(\n        ownerName: publishConfig.ownerName,\n        appName: publishConfig.appName,\n      );\n      String releasesId = release['id'];\n      String packageAssetId = release['package_asset_id'];\n      String urlEncodedToken = release['url_encoded_token'];\n\n      // Creating metadata (2/7)\n      String fileName = file.path.split('/').last;\n      String contentType = 'application/octet-stream';\n      if (fileName.endsWith('.apk')) {\n        contentType = 'application/vnd.android.package-archive';\n      }\n      Map<String, dynamic> metadata = await _createMetadata(\n        fileName: fileName,\n        fileSize: file.lengthSync(),\n        packageAssetId: packageAssetId,\n        urlEncodedToken: urlEncodedToken,\n        contentType: contentType,\n      );\n\n      int chunkSize = metadata['chunk_size'];\n\n      // Uploading chunked binary (3/7)\n      await _uploadingChunkedBinary(\n        file: file,\n        packageAssetId: packageAssetId,\n        urlEncodedToken: urlEncodedToken,\n        contentType: contentType,\n        chunkSize: chunkSize,\n        onPublishProgress: onPublishProgress,\n      );\n\n      // Finalising upload (4/7)\n      await _finalisingUpload(\n        packageAssetId: packageAssetId,\n        urlEncodedToken: urlEncodedToken,\n      );\n\n      // Commit release (5/7)\n      await _commitRelease(\n        ownerName: publishConfig.ownerName,\n        appName: publishConfig.appName,\n        releasesId: releasesId,\n      );\n\n      // Polling for release id (6/7)\n      int releaseDistinctId = await _pollingForReleaseDistinctId(\n        ownerName: publishConfig.ownerName,\n        appName: publishConfig.appName,\n        releasesId: releasesId,\n      );\n\n      // Applying destination to release (7/7)\n      await _applyingDestinationToRelease(\n        ownerName: publishConfig.ownerName,\n        appName: publishConfig.appName,\n        releaseDistinctId: releaseDistinctId,\n        distributionGroup: publishConfig.distributionGroup!,\n      );\n    } catch (error) {\n      rethrow;\n    }\n\n    return PublishResult(\n      url:\n          'https://install.appcenter.ms/users/${publishConfig.ownerName}/apps/${publishConfig.appName}/distribution_groups/${publishConfig.distributionGroup}',\n    );\n  }\n\n  Future<Map<String, dynamic>> _createRelease({\n    required String ownerName,\n    required String appName,\n  }) async {\n    final response = await _dio.post(\n      '/apps/$ownerName/$appName/uploads/releases',\n    );\n\n    return Map<String, dynamic>.from(response.data);\n  }\n\n  Future<Map<String, dynamic>> _createMetadata({\n    required String fileName,\n    required int fileSize,\n    required String packageAssetId,\n    required String urlEncodedToken,\n    required String contentType,\n  }) async {\n    final response = await _dio.post(\n      '$_kUploadDomain/set_metadata/$packageAssetId?file_name=$fileName&file_size=$fileSize&token=$urlEncodedToken&content_type=$contentType',\n    );\n    return Map<String, dynamic>.from(response.data);\n  }\n\n  Future<Map<String, dynamic>> _uploadingChunkedBinary({\n    required File file,\n    required String packageAssetId,\n    required String urlEncodedToken,\n    required String contentType,\n    required int chunkSize,\n    PublishProgressCallback? onPublishProgress,\n  }) async {\n    String chunkingPath = '${file.path}_chunking/';\n    await $('rm', ['-rf', chunkingPath]);\n    await $('mkdir', [chunkingPath]);\n    await $(\n      'split',\n      ['-b', '$chunkSize', file.path, chunkingPath],\n    );\n\n    Directory chunkingDir = Directory(chunkingPath);\n    List<FileSystemEntity> entityList = chunkingDir.listSync();\n    entityList.sort((a, b) => a.path.compareTo(b.path));\n\n    for (var i = 0; i < entityList.length; i++) {\n      FileSystemEntity entity = entityList[i];\n      Uint8List fileData = File(entity.path).readAsBytesSync();\n      int contentLength = fileData.length;\n      await _dio.post(\n        '$_kUploadDomain/upload_chunk/$packageAssetId?token=$urlEncodedToken&block_number=${i + 1}',\n        data: Stream.fromIterable(fileData.map((e) => [e])),\n        options: Options(\n          headers: {\n            Headers.contentLengthHeader: contentLength,\n            Headers.contentTypeHeader: contentType,\n          },\n        ),\n        onSendProgress: (sent, total) {\n          if (onPublishProgress != null) {\n            onPublishProgress((i * chunkSize) + sent, file.lengthSync());\n          }\n        },\n      );\n    }\n    await $('rm', ['-rf', chunkingPath]);\n    return Map<String, dynamic>.from({});\n  }\n\n  Future<Map<String, dynamic>> _finalisingUpload({\n    required String packageAssetId,\n    required String urlEncodedToken,\n  }) async {\n    final response = await _dio.post(\n      '$_kUploadDomain/finished/$packageAssetId?token=$urlEncodedToken',\n    );\n    return Map<String, dynamic>.from(response.data);\n  }\n\n  Future<Map<String, dynamic>> _commitRelease({\n    required String ownerName,\n    required String appName,\n    required String releasesId,\n  }) async {\n    final response = await _dio.patch(\n      '/apps/$ownerName/$appName/uploads/releases/$releasesId',\n      data: {\n        'upload_status': 'uploadFinished',\n        'id': releasesId,\n      },\n    );\n\n    return Map<String, dynamic>.from(response.data);\n  }\n\n  Future<int> _pollingForReleaseDistinctId({\n    required String ownerName,\n    required String appName,\n    required String releasesId,\n  }) async {\n    int? releaseDistinctId;\n    int counter = 0;\n    int maxPollAttempts = 15;\n\n    while (releaseDistinctId == null && counter < maxPollAttempts) {\n      try {\n        final response = await _dio.get(\n          '/apps/$ownerName/$appName/uploads/releases/$releasesId',\n        );\n        releaseDistinctId = response.data['release_distinct_id'];\n      } catch (error) {\n        // skip\n      }\n      counter = counter + 1;\n      await Future.delayed(const Duration(seconds: 3));\n    }\n\n    if (releaseDistinctId == null) {\n      throw PublishError('Failed to find release from appcenter');\n    }\n    return releaseDistinctId;\n  }\n\n  Future<Map<String, dynamic>> _applyingDestinationToRelease({\n    required String ownerName,\n    required String appName,\n    required int releaseDistinctId,\n    required String distributionGroup,\n  }) async {\n    final response = await _dio.patch(\n      '/apps/$ownerName/$appName/releases/$releaseDistinctId',\n      data: {\n        'destinations': [\n          {'name': distributionGroup},\n        ],\n      },\n    );\n\n    return Map<String, dynamic>.from(response.data);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_publisher/lib/src/publishers/appcenter/publish_appcenter_config.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_publisher/src/api/app_package_publisher.dart';\n\nconst kEnvAppCenterApiToken = 'APPCENTER_API_TOKEN';\n\nclass PublishAppCenterConfig extends PublishConfig {\n  PublishAppCenterConfig({\n    required this.apiToken,\n    required this.ownerName,\n    required this.appName,\n    this.distributionGroup,\n  });\n\n  factory PublishAppCenterConfig.parse(\n    Map<String, String>? environment,\n    Map<String, dynamic>? publishArguments,\n  ) {\n    String? apiToken =\n        (environment ?? Platform.environment)[kEnvAppCenterApiToken];\n    if ((apiToken ?? '').isEmpty) {\n      throw PublishError(\n        'Missing `$kEnvAppCenterApiToken` environment variable.',\n      );\n    }\n    String? ownerName = publishArguments?['owner-name'];\n    if ((ownerName ?? '').isEmpty) {\n      throw PublishError('Missing `owner-name` arg');\n    }\n\n    String? appName = publishArguments?['app-name'];\n    if ((appName ?? '').isEmpty) {\n      throw PublishError('Missing `app-name` arg');\n    }\n\n    PublishAppCenterConfig publishConfig = PublishAppCenterConfig(\n      apiToken: apiToken!,\n      ownerName: ownerName!,\n      appName: appName!,\n      distributionGroup:\n          publishArguments?['distribution-group'] ?? 'Collaborators',\n    );\n    return publishConfig;\n  }\n\n  final String apiToken;\n  String ownerName;\n  String appName;\n  String? distributionGroup;\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_publisher/lib/src/publishers/appstore/app_package_publisher_appstore.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_publisher/src/api/app_package_publisher.dart';\nimport 'package:flutter_app_publisher/src/publishers/appstore/publish_appstore_config.dart';\nimport 'package:shell_executor/shell_executor.dart';\n\n/// AppStore doc [https://help.apple.com/asc/appsaltool/]\nclass AppPackagePublisherAppStore extends AppPackagePublisher {\n  @override\n  String get name => 'appstore';\n\n  @override\n  List<String> get supportedPlatforms => ['ios', 'macos'];\n\n  @override\n  Future<PublishResult> publish(\n    FileSystemEntity fileSystemEntity, {\n    Map<String, String>? environment,\n    Map<String, dynamic>? publishArguments,\n    PublishProgressCallback? onPublishProgress,\n  }) async {\n    File file = fileSystemEntity as File;\n    // Get type\n    String type = file.path.endsWith('.ipa') ? 'ios' : 'osx';\n    // Get config\n    PublishAppStoreConfig publishConfig =\n        PublishAppStoreConfig.parse(environment, publishArguments);\n    // Publish to AppStore\n    ProcessResult processResult = await $(\n      'xcrun',\n      [\n        'altool',\n        '--upload-app',\n        '--file',\n        file.path,\n        '--type',\n        type,\n        // cmd list\n        ...publishConfig.toAppStoreCliDistributeArgs(),\n      ],\n    );\n\n    if (processResult.exitCode == 0) {\n      return PublishResult(\n        url: 'https://appstoreconnect.apple.com/apps',\n      );\n    } else {\n      throw PublishError(\n        '${processResult.exitCode} - Upload of appstore failed',\n      );\n    }\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_publisher/lib/src/publishers/appstore/publish_appstore_config.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_publisher/src/api/app_package_publisher.dart';\n\nconst kEnvAppStoreUsername = 'APPSTORE_USERNAME';\nconst kEnvAppStorePassword = 'APPSTORE_PASSWORD';\nconst kEnvAppStoreApiKey = 'APPSTORE_APIKEY';\nconst kEnvAppStoreApiIssuer = 'APPSTORE_APIISSUER';\n\nclass PublishAppStoreConfig extends PublishConfig {\n  PublishAppStoreConfig({\n    this.username,\n    this.password,\n    this.apiKey,\n    this.apiIssuer,\n  });\n\n  factory PublishAppStoreConfig.parse(\n    Map<String, String>? environment,\n    Map<String, dynamic>? publishArguments,\n  ) {\n    // Get authorization info\n    String? username =\n        (environment ?? Platform.environment)[kEnvAppStoreUsername];\n    String? password =\n        (environment ?? Platform.environment)[kEnvAppStorePassword];\n    String? apiKey = (environment ?? Platform.environment)[kEnvAppStoreApiKey];\n    String? apiIssuer =\n        (environment ?? Platform.environment)[kEnvAppStoreApiIssuer];\n    // Check username & password & apiKey & apiIssuer\n    if ('$username$password$apiKey$apiIssuer'.replaceAll('null', '').isEmpty) {\n      throw PublishError(\n        'Missing `$kEnvAppStoreUsername` & `$kEnvAppStorePassword` | `$kEnvAppStoreApiKey` & `$kEnvAppStoreApiIssuer` environment variable. See:https://help.apple.com/asc/appsaltool/#/apdATD1E53-D1E1A1303-D1E53A1126',\n      );\n    }\n    // Check username & password\n    if (((username ?? '').isNotEmpty && (password ?? '').isEmpty) ||\n        ((username ?? '').isEmpty && (password ?? '').isNotEmpty)) {\n      throw PublishError(\n        'Missing `$kEnvAppStoreUsername` & `$kEnvAppStorePassword` environment variable. See:https://help.apple.com/asc/appsaltool/#/apdATD1E53-D1E1A1303-D1E53A1126',\n      );\n    } else {\n      // Check apiKey & apiIssuer\n      if (((apiKey ?? '').isNotEmpty && (apiIssuer ?? '').isEmpty) ||\n          ((apiKey ?? '').isEmpty && (apiIssuer ?? '').isNotEmpty)) {\n        throw PublishError(\n          'Missing `$kEnvAppStoreApiKey` & `$kEnvAppStoreApiIssuer` environment variable. See:https://help.apple.com/asc/appsaltool/#/apdATD1E53-D1E1A1303-D1E53A1126',\n        );\n      }\n    }\n\n    return PublishAppStoreConfig(\n      username: username,\n      password: password,\n      apiKey: apiKey,\n      apiIssuer: apiIssuer,\n    );\n  }\n\n  final String? username;\n  final String? password;\n  final String? apiKey;\n  final String? apiIssuer;\n\n  List<String> toAppStoreCliDistributeArgs() {\n    Map<String, String?> cmdData = {\n      '--username': username,\n      '--password': password,\n      '--apiKey': apiKey,\n      '--apiIssuer': apiIssuer,\n    };\n    // clean null value cmd\n    cmdData.removeWhere((key, value) => value?.isEmpty ?? true);\n    // format cmd\n    List<String> cmdList = [];\n    cmdData.forEach((key, value) {\n      cmdList.addAll([key, value!]);\n    });\n    return cmdList;\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_publisher/lib/src/publishers/fir/app_package_publisher_fir.dart",
    "content": "import 'dart:io';\n\nimport 'package:dio/dio.dart';\nimport 'package:flutter_app_publisher/src/api/app_package_publisher.dart';\nimport 'package:flutter_app_publisher/src/publishers/fir/publish_fir_config.dart';\nimport 'package:parse_app_package/parse_app_package.dart';\n\nconst kEnvFirApiToken = 'FIR_API_TOKEN';\n\nclass AppPackagePublisherFir extends AppPackagePublisher {\n  @override\n  String get name => 'fir';\n\n  @override\n  List<String> get supportedPlatforms => ['android', 'ios'];\n\n  final Dio _dio = Dio(\n    BaseOptions(baseUrl: 'http://api.bq04.com'),\n  );\n\n  Future<String> _uploadAppBinary(\n    File file,\n    AppPackage appPackage, {\n    required String key,\n    required String token,\n    required String uploadUrl,\n    PublishProgressCallback? onPublishProgress,\n  }) async {\n    FormData formData = FormData.fromMap({\n      'key': key,\n      'token': token,\n      'file': await MultipartFile.fromFile(file.path),\n      'x:name': appPackage.name,\n      'x:version': appPackage.version,\n      'x:build': appPackage.buildNumber,\n    });\n\n    final response = await _dio.post(\n      uploadUrl,\n      data: formData,\n      onSendProgress: (int sent, int total) {\n        if (onPublishProgress != null) {\n          onPublishProgress(sent, total);\n        }\n      },\n    );\n    return response.data['release_id'];\n  }\n\n  @override\n  Future<PublishResult> publish(\n    FileSystemEntity fileSystemEntity, {\n    Map<String, String>? environment,\n    Map<String, dynamic>? publishArguments,\n    PublishProgressCallback? onPublishProgress,\n  }) async {\n    File file = fileSystemEntity as File;\n    String? apiToken = (environment ?? Platform.environment)[kEnvFirApiToken];\n    if ((apiToken ?? '').isEmpty) {\n      throw PublishError('Missing `$kEnvFirApiToken` environment variable.');\n    }\n\n    PublishFirConfig publishConfig = PublishFirConfig(\n      apiToken: apiToken!,\n    );\n\n    try {\n      AppPackage appPackage = await parseAppPackage(file);\n\n      final response = await _dio.post(\n        '/apps',\n        data: {\n          'type': appPackage.platform,\n          'bundle_id': appPackage.identifier,\n          'api_token': publishConfig.apiToken,\n        },\n      );\n\n      Map<String, dynamic> data = response.data;\n      Map<String, dynamic> cert = data['cert'];\n\n      String releaseId = await _uploadAppBinary(\n        file,\n        appPackage,\n        key: cert['binary']['key'],\n        token: cert['binary']['token'],\n        uploadUrl: cert['binary']['upload_url'],\n        onPublishProgress: onPublishProgress,\n      );\n\n      Uri downloadUri = Uri(\n        scheme: data['download_domain_https_ready'] ? 'https' : 'http',\n        host: data['download_domain'],\n        path: '/${data['short']}',\n        queryParameters: {'release_id': releaseId},\n      );\n\n      return PublishResult(\n        url: downloadUri.toString(),\n      );\n    } on DioException catch (error) {\n      String? message;\n      if (error.response?.data != null) {\n        int? code = error.response?.data['code'];\n        message = error.response?.data['errors']['exception'][0];\n        message = '$code - $message';\n      }\n      throw PublishError(message);\n    } catch (error) {\n      rethrow;\n    }\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_publisher/lib/src/publishers/fir/publish_fir_config.dart",
    "content": "import 'package:flutter_app_publisher/src/api/app_package_publisher.dart';\n\nclass PublishFirConfig extends PublishConfig {\n  PublishFirConfig({\n    required this.apiToken,\n  });\n\n  final String apiToken;\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_publisher/lib/src/publishers/firebase/app_package_publisher_firebase.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_publisher/src/api/app_package_publisher.dart';\nimport 'package:flutter_app_publisher/src/publishers/firebase/publish_firebase_config.dart';\nimport 'package:shell_executor/shell_executor.dart';\n\n/// Firebase doc\n/// iOS: [https://firebase.google.com/docs/app-distribution/ios/distribute-cli]\n/// Android: [https://firebase.google.com/docs/app-distribution/android/distribute-cli]\nclass AppPackagePublisherFirebase extends AppPackagePublisher {\n  @override\n  String get name => 'firebase';\n\n  @override\n  List<String> get supportedPlatforms => ['android', 'ios'];\n\n  @override\n  Future<PublishResult> publish(\n    FileSystemEntity fileSystemEntity, {\n    Map<String, String>? environment,\n    Map<String, dynamic>? publishArguments,\n    PublishProgressCallback? onPublishProgress,\n  }) async {\n    File file = fileSystemEntity as File;\n    PublishFirebaseConfig publishConfig =\n        PublishFirebaseConfig.parse(environment, publishArguments);\n\n    // Publish to Firebase\n    ProcessResult processResult = await $(\n      'firebase',\n      [\n        'appdistribution:distribute',\n        file.path,\n        // cmd list\n        ...publishConfig.toFirebaseCliDistributeArgs(),\n      ],\n    );\n    if (processResult.exitCode == 0) {\n      return PublishResult(\n        url: 'https://console.firebase.google.com/project/_/appdistribution',\n      );\n    } else {\n      throw PublishError(\n        '${processResult.exitCode} - Upload of firebase failed',\n      );\n    }\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_publisher/lib/src/publishers/firebase/publish_firebase_config.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_publisher/src/api/app_package_publisher.dart';\n\nconst kEnvFirebaseToken = 'FIREBASE_TOKEN';\n\nclass PublishFirebaseConfig extends PublishConfig {\n  PublishFirebaseConfig({\n    required this.app,\n    this.token,\n    this.releaseNotes,\n    this.releaseNotesFile,\n    this.testers,\n    this.testersFile,\n    this.groups,\n    this.groupsFile,\n  });\n\n  factory PublishFirebaseConfig.parse(\n    Map<String, String>? environment,\n    Map<String, dynamic>? publishArguments,\n  ) {\n    // Get token\n    String? token = (environment ?? Platform.environment)[kEnvFirebaseToken];\n    if ((token ?? '').isEmpty) {\n      throw PublishError(\n        'Missing `$kEnvFirebaseToken` environment variable. See:https://firebase.google.com/docs/cli?authuser=0#cli-ci-systems',\n      );\n    }\n    // Get app\n    String? app = publishArguments?['app'];\n    if ((app ?? '').isEmpty) {\n      throw PublishError(\n        'Missing app args. See:https://console.firebase.google.com/project/_/settings/general/?authuser=0',\n      );\n    }\n    return PublishFirebaseConfig(\n      app: app!,\n      token: token!,\n      releaseNotes: publishArguments?['release-notes'],\n      releaseNotesFile: publishArguments?['release-notes-file'],\n      testers: publishArguments?['testers'],\n      testersFile: publishArguments?['testers-file'],\n      groups: publishArguments?['groups'],\n      groupsFile: publishArguments?['groups-file'],\n    );\n  }\n\n  final String app;\n  final String? token;\n  final String? releaseNotes;\n  final String? releaseNotesFile;\n  final String? testers;\n  final String? testersFile;\n  final String? groups;\n  final String? groupsFile;\n\n  List<String> toFirebaseCliDistributeArgs() {\n    Map<String, String?> cmdData = {\n      '--app': app,\n      '--token': token,\n      '--release-notes': releaseNotes,\n      '--release-notes-file': releaseNotesFile,\n      '--testers': testers,\n      '--testers-file': testersFile,\n      '--groups': groups,\n      '--groups-file': groupsFile,\n    };\n    // clean null value cmd\n    cmdData.removeWhere((key, value) => value?.isEmpty ?? true);\n    // format cmd\n    List<String> cmdList = [];\n    cmdData.forEach((key, value) {\n      cmdList.addAll([key, value!]);\n    });\n    return cmdList;\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_publisher/lib/src/publishers/firebase_hosting/app_package_publisher_firebase_hosting.dart",
    "content": "import 'dart:convert';\nimport 'dart:io';\n\nimport 'package:flutter_app_publisher/src/api/app_package_publisher.dart';\nimport 'package:flutter_app_publisher/src/publishers/firebase_hosting/publish_firebase_hosting_config.dart';\nimport 'package:shell_executor/shell_executor.dart';\n\nclass AppPackagePublisherFirebaseHosting extends AppPackagePublisher {\n  @override\n  String get name => 'firebase-hosting';\n\n  @override\n  List<String> get supportedPlatforms => ['web'];\n\n  @override\n  Future<PublishResult> publish(\n    FileSystemEntity fileSystemEntity, {\n    Map<String, String>? environment,\n    Map<String, dynamic>? publishArguments,\n    PublishProgressCallback? onPublishProgress,\n  }) async {\n    Directory directory = fileSystemEntity as Directory;\n\n    PublishFirebaseHostingConfig publishConfig =\n        PublishFirebaseHostingConfig.parse(\n      environment,\n      publishArguments,\n    );\n\n    try {\n      File firebaseRcFile = File('${directory.path}/.firebaserc');\n      firebaseRcFile.createSync(recursive: true);\n      firebaseRcFile.writeAsStringSync(\n        json.encode({\n          'projects': {'default': publishConfig.projectId},\n        }),\n      );\n      File firebaseJsonFile = File('${directory.path}/firebase.json');\n      firebaseJsonFile.createSync(recursive: true);\n      firebaseJsonFile.writeAsStringSync(\n        json.encode({\n          'hosting': {\n            'public': '.',\n            'ignore': ['firebase.json'],\n          },\n        }),\n      );\n      ProcessResult r = await $(\n        'firebase',\n        ['deploy'],\n        workingDirectory: directory.path,\n      );\n\n      String log = r.stdout.toString();\n      RegExpMatch? match =\n          RegExp(r'(?<=Hosting URL: )\\bhttps?:\\/\\/\\S+\\b').firstMatch(log);\n\n      return PublishResult(\n        url: match != null ? match.group(0)! : '',\n      );\n    } catch (error) {\n      rethrow;\n    }\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_publisher/lib/src/publishers/firebase_hosting/publish_firebase_hosting_config.dart",
    "content": "import 'package:flutter_app_publisher/src/api/app_package_publisher.dart';\n\nclass PublishFirebaseHostingConfig extends PublishConfig {\n  PublishFirebaseHostingConfig({\n    required this.projectId,\n  });\n\n  factory PublishFirebaseHostingConfig.parse(\n    Map<String, String>? environment,\n    Map<String, dynamic>? publishArguments,\n  ) {\n    String? projectId = publishArguments?['project-id'];\n    if ((projectId ?? '').isEmpty) {\n      throw PublishError('Missing `project-id` config.');\n    }\n\n    PublishFirebaseHostingConfig publishConfig = PublishFirebaseHostingConfig(\n      projectId: projectId!,\n    );\n    return publishConfig;\n  }\n\n  String projectId;\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_publisher/lib/src/publishers/github/app_package_publisher_github.dart",
    "content": "import 'dart:io';\nimport 'dart:typed_data';\n\nimport 'package:dio/dio.dart';\nimport 'package:flutter_app_publisher/src/api/app_package_publisher.dart';\nimport 'package:flutter_app_publisher/src/publishers/github/publish_github_config.dart';\n\nclass AppPackagePublisherGithub extends AppPackagePublisher {\n  final Dio _dio = Dio();\n\n  @override\n  String get name => 'github';\n\n  @override\n  List<String> get supportedPlatforms => [\n        'android',\n        'ios',\n        'linux',\n        'macos',\n        'windows',\n        'web',\n      ];\n\n  @override\n  Future<PublishResult> publish(\n    FileSystemEntity fileSystemEntity, {\n    Map<String, String>? environment,\n    Map<String, dynamic>? publishArguments,\n    PublishProgressCallback? onPublishProgress,\n  }) async {\n    File file = fileSystemEntity as File;\n    PublishGithubConfig publishConfig = PublishGithubConfig.parse(\n      environment,\n      publishArguments,\n    );\n    // Set auth\n    _dio.options = BaseOptions(\n      headers: {\n        'Authorization': 'token ${publishConfig.token}',\n      },\n    );\n\n    // Get uploadUrl\n    String? uploadUrl;\n    if (publishConfig.releaseTitle?.isEmpty ?? true) {\n      uploadUrl = await _getUploadurlByLatestRelease(publishConfig);\n    } else {\n      uploadUrl = await _getUploadurlByReleaseName(publishConfig);\n      if (uploadUrl?.isEmpty ?? true) {\n        uploadUrl = await _createRelease(publishConfig);\n      }\n    }\n    if (uploadUrl?.isEmpty ?? true) {\n      throw PublishError('Upload url isEmpty');\n    }\n    // Upload file\n    String browserDownloadUrl =\n        await _uploadReleaseAsset(file, uploadUrl!, onPublishProgress);\n    return PublishResult(\n      url: browserDownloadUrl,\n    );\n  }\n\n  /// Get uploadUrl by releaseName\n  Future<String?> _getUploadurlByReleaseName(\n    PublishGithubConfig publishConfig,\n  ) async {\n    Response resp = await _dio.get(\n      'https://api.github.com/repos/${publishConfig.repoOwner}/${publishConfig.repoName}/releases',\n    );\n    List relist = (resp.data as List?) ?? [];\n    var release = relist.firstWhere(\n      (item) => item['name'] == publishConfig.releaseTitle,\n      orElse: () => {},\n    );\n    return release?['upload_url'];\n  }\n\n  /// Create release\n  Future<String?> _createRelease(PublishGithubConfig publishConfig) async {\n    Response resp = await _dio.post(\n      'https://api.github.com/repos/${publishConfig.repoOwner}/${publishConfig.repoName}/releases',\n      data: {\n        'tag_name': publishConfig.releaseTitle,\n        'name': publishConfig.releaseTitle,\n        'draft': true,\n      },\n    );\n    return resp.data?['upload_url'];\n  }\n\n  /// Get uploadUrl by latest release\n  Future<String?> _getUploadurlByLatestRelease(\n    PublishGithubConfig publishConfig,\n  ) async {\n    Response resp = await _dio.get(\n      'https://api.github.com/repos/${publishConfig.repoOwner}/${publishConfig.repoName}/releases/latest',\n    );\n    return resp.data?['upload_url'];\n  }\n\n  /// Upload Release Asset\n  Future<String> _uploadReleaseAsset(\n    File file,\n    String uploadUrl,\n    PublishProgressCallback? onPublishProgress,\n  ) async {\n    // Fromat uploadUrl\n    uploadUrl = uploadUrl.split('{').first;\n    String fileName = file.path.split('/').last;\n    Uint8List fileData = await file.readAsBytes();\n    String url = '$uploadUrl?name=${Uri.encodeComponent(fileName)}';\n    // dio upload\n    _dio.options.contentType = 'application/octet-stream';\n    _dio.options.headers\n        .putIfAbsent(Headers.contentLengthHeader, () => fileData.length);\n    String? browserDownloadUrl;\n    try {\n      Response resp = await _dio.post(\n        url,\n        data: Stream.fromIterable(fileData.map((e) => [e])),\n        onSendProgress: (int sent, int total) {\n          if (onPublishProgress != null) {\n            onPublishProgress(sent, total);\n          }\n        },\n      );\n      browserDownloadUrl = resp.data?['browser_download_url'];\n    } catch (e) {\n      throw PublishError(e.toString());\n    }\n    // Check release asset\n    if (browserDownloadUrl?.isEmpty ?? true) {\n      throw PublishError('Release asset exist [$fileName]');\n    }\n    return browserDownloadUrl!;\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_publisher/lib/src/publishers/github/publish_github_config.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_publisher/src/api/app_package_publisher.dart';\n\nconst kEnvGithubToken = 'GITHUB_TOKEN';\n\nclass PublishGithubConfig extends PublishConfig {\n  PublishGithubConfig({\n    required this.token,\n    required this.repoOwner,\n    required this.repoName,\n    this.releaseTitle,\n  });\n\n  factory PublishGithubConfig.parse(\n    Map<String, String>? environment,\n    Map<String, dynamic>? publishArguments,\n  ) {\n    String? token = (environment ?? Platform.environment)[kEnvGithubToken];\n    if ((token ?? '').isEmpty) {\n      throw PublishError('Missing `$kEnvGithubToken` environment variable.');\n    }\n    String? owner = publishArguments?['repo-owner'];\n    if ((owner ?? '').isEmpty) {\n      throw PublishError('<repo-owner> is null');\n    }\n\n    String? name = publishArguments?['repo-name'];\n    if ((name ?? '').isEmpty) {\n      throw PublishError('<repo-name> is null');\n    }\n\n    PublishGithubConfig publishConfig = PublishGithubConfig(\n      token: token!,\n      repoOwner: owner!,\n      repoName: name!,\n      releaseTitle: publishArguments?['release-title'],\n    );\n\n    String appVersion =\n        publishConfig.pubspec.version.toString().split('+').first;\n    String appBuildNumber =\n        publishConfig.pubspec.version.toString().split('+').last;\n\n    if ((publishConfig.releaseTitle ?? '').trim().isEmpty) {\n      publishConfig.releaseTitle = 'v$appVersion';\n    } else {\n      publishConfig.releaseTitle = publishConfig.releaseTitle\n          ?.replaceAll('{appVersion}', appVersion)\n          .replaceAll('{appBuildNumber}', appBuildNumber);\n    }\n\n    return publishConfig;\n  }\n\n  // Personal access tokens\n  final String token;\n  // Repository Owner\n  String repoOwner;\n  // Repository Name\n  String repoName;\n  // Release title\n  String? releaseTitle;\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_publisher/lib/src/publishers/pgyer/app_package_publisher_pgyer.dart",
    "content": "import 'dart:io';\n\nimport 'package:dio/dio.dart';\nimport 'package:flutter_app_publisher/src/api/app_package_publisher.dart';\n\nconst kEnvPgyerApiKey = 'PGYER_API_KEY';\n\n/// pgyer doc [https://www.pgyer.com/doc/view/api#uploadApp]\nclass AppPackagePublisherPgyer extends AppPackagePublisher {\n  @override\n  String get name => 'pgyer';\n\n  // dio 网络请求实例\n  final Dio _dio = Dio();\n  // 轮询尝试次数\n  int tryCount = 0;\n  // 最大尝试轮询次数\n  final maxTryCount = 10;\n\n  @override\n  Future<PublishResult> publish(\n    FileSystemEntity fileSystemEntity, {\n    Map<String, String>? environment,\n    Map<String, dynamic>? publishArguments,\n    PublishProgressCallback? onPublishProgress,\n  }) async {\n    File file = fileSystemEntity as File;\n    String? apiKey = (environment ?? Platform.environment)[kEnvPgyerApiKey];\n    if ((apiKey ?? '').isEmpty) {\n      throw PublishError('Missing `$kEnvPgyerApiKey` environment variable.');\n    }\n\n    var tokenInfo = await getCOSToken(apiKey!, file.path);\n    String uploadKey = await uploadApp(tokenInfo, file, onPublishProgress);\n    if (uploadKey.isEmpty) {\n      throw PublishError('UploadApp error');\n    }\n    // 重试次数设置为 0\n    tryCount = 0;\n    var buildResult = await getBuildInfo(apiKey, uploadKey);\n    String buildKey = buildResult.data!['data']['buildKey'];\n    return PublishResult(\n      url: 'http://www.pgyer.com/$buildKey',\n    );\n  }\n\n  /// 获取上传 Token 信息\n  /// [apiKey] apiKey\n  /// [filePath] 文件路径\n  Future<Response> getCOSToken(String apiKey, String filePath) async {\n    FormData formData = FormData.fromMap({\n      '_api_key': apiKey,\n      'buildType': filePath.split('.').last,\n    });\n    try {\n      Response response = await _dio.post(\n        'https://www.pgyer.com/apiv2/app/getCOSToken',\n        data: formData,\n      );\n      if (response.data['code'] != 0) {\n        throw PublishError('getCOSToken error: ${response.data}');\n      }\n      return response;\n    } catch (e) {\n      throw PublishError(e.toString());\n    }\n  }\n\n  /// 上传应用\n  /// [tokenInfo] token信息\n  /// [file] 文件\n  /// [onPublishProgress] 进度回调\n  Future<String> uploadApp(\n    Response tokenInfo,\n    File file,\n    PublishProgressCallback? onPublishProgress,\n  ) async {\n    var tokenData = tokenInfo.data['data'];\n    String endpoint = tokenData['endpoint'];\n    String key = tokenData['key'];\n    var params = tokenData['params'];\n    FormData formData = FormData.fromMap({\n      'key': key,\n      'signature': params['signature'],\n      'x-cos-security-token': params['x-cos-security-token'],\n      'x-cos-meta-file-name': file.path.split('/').last,\n      'file': await MultipartFile.fromFile(file.path),\n    });\n\n    try {\n      Response response = await _dio.post(\n        endpoint,\n        data: formData,\n        onSendProgress: (int sent, int total) {\n          if (onPublishProgress != null) {\n            onPublishProgress(sent, total);\n          }\n        },\n      );\n      if (response.statusCode == 204) {\n        // 上传成功，准备轮询结果\n        return key;\n      }\n    } catch (e) {\n      throw PublishError(e.toString());\n    }\n    return '';\n  }\n\n  /// 获取应用发布构建信息\n  /// [apiKey] apiKey\n  /// [uploadKey] uploadKey\n  Future<Response> getBuildInfo(String apiKey, String uploadKey) async {\n    if (tryCount > maxTryCount) {\n      throw PublishError('getBuildInfo error :Too many retries');\n    }\n    await Future.delayed(const Duration(seconds: 3));\n    try {\n      Response response = await _dio.get(\n        'https://www.pgyer.com/apiv2/app/buildInfo',\n        queryParameters: {\n          '_api_key': apiKey,\n          'buildKey': uploadKey,\n        },\n      );\n      int code = response.data['code'];\n      if (code == 1247) {\n        tryCount++;\n        print('应用发布信息获取中，请稍等 $tryCount');\n        return await getBuildInfo(apiKey, uploadKey);\n      } else if (code != 0) {\n        throw PublishError('getBuildInfo error: ${response.data}');\n      }\n      return response;\n    } catch (e) {\n      throw PublishError(e.toString());\n    }\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_publisher/lib/src/publishers/pgyer/publish_pgyer_config.dart",
    "content": "import 'package:flutter_app_publisher/src/api/app_package_publisher.dart';\n\nclass PublishPgyerConfig extends PublishConfig {\n  PublishPgyerConfig({\n    required this.apiKey,\n  });\n\n  final String apiKey;\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_publisher/lib/src/publishers/playstore/app_package_publisher_playstore.dart",
    "content": "import 'dart:convert';\nimport 'dart:io';\n\nimport 'package:flutter_app_publisher/src/api/app_package_publisher.dart';\nimport 'package:flutter_app_publisher/src/publishers/playstore/publish_playstore_config.dart';\nimport 'package:googleapis/androidpublisher/v3.dart';\nimport 'package:googleapis_auth/auth_io.dart';\n\nclass AppPackagePublisherPlayStore extends AppPackagePublisher {\n  @override\n  String get name => 'playstore';\n\n  @override\n  List<String> get supportedPlatforms => ['android'];\n\n  @override\n  Future<PublishResult> publish(\n    FileSystemEntity fileSystemEntity, {\n    Map<String, String>? environment,\n    Map<String, dynamic>? publishArguments,\n    PublishProgressCallback? onPublishProgress,\n  }) async {\n    File file = fileSystemEntity as File;\n    PublishPlayStoreConfig publishConfig = PublishPlayStoreConfig.parse(\n      environment,\n      publishArguments,\n    );\n\n    String jsonString = File(publishConfig.credentialsFile).readAsStringSync();\n    ServiceAccountCredentials serviceAccountCredentials =\n        ServiceAccountCredentials.fromJson(json.decode(jsonString));\n\n    final client = await clientViaServiceAccount(\n      serviceAccountCredentials,\n      [\n        AndroidPublisherApi.androidpublisherScope,\n      ],\n    );\n\n    final AndroidPublisherApi publisherApi = AndroidPublisherApi(client);\n\n    AppEdit appEdit = await publisherApi.edits.insert(\n      AppEdit(),\n      publishConfig.packageName,\n    );\n    Media uploadMedia = Media(file.openRead(), file.lengthSync());\n\n    await publisherApi.edits.bundles.upload(\n      publishConfig.packageName,\n      appEdit.id!,\n      uploadMedia: uploadMedia,\n    );\n\n    await publisherApi.edits.commit(\n      publishConfig.packageName,\n      appEdit.id!,\n    );\n\n    return PublishResult(\n      url: '',\n    );\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_publisher/lib/src/publishers/playstore/publish_playstore_config.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_publisher/src/api/app_package_publisher.dart';\n\nconst kEnvPlayStoreCredentialsFile = 'PLAYSTORE_CREDENTIALS';\n\nclass PublishPlayStoreConfig extends PublishConfig {\n  PublishPlayStoreConfig({\n    required this.credentialsFile,\n    required this.packageName,\n  });\n\n  factory PublishPlayStoreConfig.parse(\n    Map<String, String>? environment,\n    Map<String, dynamic>? publishArguments,\n  ) {\n    String? credentialsFile =\n        (environment ?? Platform.environment)[kEnvPlayStoreCredentialsFile];\n\n    if ((credentialsFile ?? '').isEmpty) {\n      throw PublishError(\n        'Missing `$kEnvPlayStoreCredentialsFile` environment variable.',\n      );\n    }\n    PublishPlayStoreConfig publishConfig = PublishPlayStoreConfig(\n      credentialsFile: credentialsFile!,\n      packageName: publishArguments?['package-name'],\n    );\n    return publishConfig;\n  }\n  final String credentialsFile;\n  final String packageName;\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_publisher/lib/src/publishers/publishers.dart",
    "content": "export 'appcenter/app_package_publisher_appcenter.dart';\nexport 'appstore/app_package_publisher_appstore.dart';\nexport 'fir/app_package_publisher_fir.dart';\nexport 'firebase/app_package_publisher_firebase.dart';\nexport 'firebase_hosting/app_package_publisher_firebase_hosting.dart';\nexport 'github/app_package_publisher_github.dart';\nexport 'pgyer/app_package_publisher_pgyer.dart';\nexport 'playstore/app_package_publisher_playstore.dart';\nexport 'qiniu/app_package_publisher_qiniu.dart';\nexport 'vercel/app_package_publisher_vercel.dart';\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_publisher/lib/src/publishers/qiniu/app_package_publisher_qiniu.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_publisher/src/api/app_package_publisher.dart';\nimport 'package:flutter_app_publisher/src/publishers/qiniu/publish_qiniu_config.dart';\n// import 'package:parse_app_package/parse_app_package.dart';\nimport 'package:qiniu_sdk_base/qiniu_sdk_base.dart';\n\nclass AppPackagePublisherQiniu extends AppPackagePublisher {\n  @override\n  String get name => 'qiniu';\n\n  @override\n  List<String> get supportedPlatforms => [\n        'android',\n        'ios',\n        'linux',\n        'macos',\n        'windows',\n      ];\n\n  @override\n  Future<PublishResult> publish(\n    FileSystemEntity fileSystemEntity, {\n    Map<String, String>? environment,\n    Map<String, dynamic>? publishArguments,\n    PublishProgressCallback? onPublishProgress,\n  }) async {\n    File file = fileSystemEntity as File;\n    PublishQiniuConfig publishConfig = PublishQiniuConfig.parse(\n      environment,\n      publishArguments,\n    );\n\n    try {\n      Auth auth = Auth(\n        accessKey: publishConfig.accessKey,\n        secretKey: publishConfig.secretKey,\n      );\n\n      String saveKey =\n          '${publishConfig.savekeyPrefix}${file.path.split('/').last}';\n\n      String uploadToken = auth.generateUploadToken(\n        putPolicy: PutPolicy(\n          scope: publishConfig.bucket,\n          deadline: (DateTime.now().millisecondsSinceEpoch ~/ 1000) + 3600,\n          saveKey: saveKey,\n        ),\n      );\n\n      Storage storage = Storage();\n      PutController putController = PutController();\n\n      int sent = 0;\n      int total = file.lengthSync();\n\n      putController.addSendProgressListener((double percent) {\n        if (onPublishProgress != null) {\n          sent = (total * percent).toInt();\n          onPublishProgress(sent, total);\n        }\n      });\n\n      if (onPublishProgress != null) {\n        onPublishProgress(sent, total);\n      }\n      PutResponse putResponse = await storage.putFile(\n        file,\n        uploadToken,\n        options: PutOptions(\n          controller: putController,\n        ),\n      );\n      return PublishResult(\n        url:\n            '${publishConfig.bucketDomain ?? '<bucketDomain>'}/${putResponse.key}',\n      );\n    } on StorageError catch (error) {\n      throw PublishError('${error.code} - ${error.message}');\n    } catch (error) {\n      rethrow;\n    }\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_publisher/lib/src/publishers/qiniu/publish_qiniu_config.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_app_publisher/src/api/app_package_publisher.dart';\n\nconst kEnvQiniuAccessKey = 'QINIU_ACCESS_KEY';\nconst kEnvQiniuSecretKey = 'QINIU_SECRET_KEY';\n\nclass PublishQiniuConfig extends PublishConfig {\n  PublishQiniuConfig({\n    required this.accessKey,\n    required this.secretKey,\n    required this.bucket,\n    this.bucketDomain,\n    this.savekeyPrefix,\n  });\n\n  factory PublishQiniuConfig.parse(\n    Map<String, String>? environment,\n    Map<String, dynamic>? publishArguments,\n  ) {\n    String? accessKey =\n        (environment ?? Platform.environment)[kEnvQiniuAccessKey];\n    String? secretKey =\n        (environment ?? Platform.environment)[kEnvQiniuSecretKey];\n    if ((accessKey ?? '').isEmpty) {\n      throw PublishError('Missing `$kEnvQiniuAccessKey` environment variable.');\n    }\n    if ((secretKey ?? '').isEmpty) {\n      throw PublishError('Missing `$kEnvQiniuSecretKey` environment variable.');\n    }\n    return PublishQiniuConfig(\n      accessKey: accessKey!,\n      secretKey: secretKey!,\n      bucket: publishArguments?['bucket'],\n      bucketDomain: publishArguments?['bucket-domain'],\n      savekeyPrefix: publishArguments?['savekey-prefix'] ?? '',\n    );\n  }\n\n  final String accessKey;\n  final String secretKey;\n  final String bucket;\n  String? bucketDomain;\n  String? savekeyPrefix;\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_publisher/lib/src/publishers/vercel/app_package_publisher_vercel.dart",
    "content": "import 'dart:convert';\nimport 'dart:io';\n\nimport 'package:flutter_app_publisher/src/api/app_package_publisher.dart';\nimport 'package:flutter_app_publisher/src/publishers/vercel/publish_vercel_config.dart';\nimport 'package:shell_executor/shell_executor.dart';\n\nclass AppPackagePublisherVercel extends AppPackagePublisher {\n  @override\n  String get name => 'vercel';\n\n  @override\n  List<String> get supportedPlatforms => ['web'];\n\n  @override\n  Future<PublishResult> publish(\n    FileSystemEntity fileSystemEntity, {\n    Map<String, String>? environment,\n    Map<String, dynamic>? publishArguments,\n    PublishProgressCallback? onPublishProgress,\n  }) async {\n    Directory directory = fileSystemEntity as Directory;\n\n    PublishVercelConfig publishConfig = PublishVercelConfig.parse(\n      environment,\n      publishArguments,\n    );\n\n    try {\n      File file = File('${directory.path}/.vercel/project.json');\n      file.createSync(recursive: true);\n      file.writeAsStringSync(\n        json.encode({\n          'orgId': publishConfig.orgId,\n          'projectId': publishConfig.projectId,\n        }),\n      );\n      ProcessResult r = await $(\n        'vercel',\n        ['--prod'],\n        workingDirectory: directory.path,\n      );\n\n      String log = r.stderr.toString();\n      RegExpMatch? match =\n          RegExp(r'(?<=Production: )\\bhttps?:\\/\\/\\S+\\b').firstMatch(log);\n\n      return PublishResult(\n        url: match != null ? match.group(0)! : '',\n      );\n    } catch (error) {\n      rethrow;\n    }\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_publisher/lib/src/publishers/vercel/publish_vercel_config.dart",
    "content": "import 'package:flutter_app_publisher/src/api/app_package_publisher.dart';\n\nclass PublishVercelConfig extends PublishConfig {\n  PublishVercelConfig({\n    required this.orgId,\n    required this.projectId,\n  });\n\n  factory PublishVercelConfig.parse(\n    Map<String, String>? environment,\n    Map<String, dynamic>? publishArguments,\n  ) {\n    String? orgId = publishArguments?['org-id'];\n    if ((orgId ?? '').isEmpty) {\n      throw PublishError('Missing `org-id` config.');\n    }\n\n    String? projectId = publishArguments?['project-id'];\n    if ((projectId ?? '').isEmpty) {\n      throw PublishError('Missing `project-id` config.');\n    }\n\n    PublishVercelConfig publishConfig = PublishVercelConfig(\n      orgId: orgId!,\n      projectId: projectId!,\n    );\n    return publishConfig;\n  }\n\n  String orgId;\n  String projectId;\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_app_publisher/pubspec.yaml",
    "content": "name: flutter_app_publisher\ndescription: Flutter app publisher\nversion: 0.4.2\nhomepage: https://distributor.leanflutter.dev\nrepository: https://github.com/leanflutter/flutter_distributor/tree/main/packages/flutter_app_publisher\n\nenvironment:\n  sdk: \">=2.16.0 <4.0.0\"\n\ndependencies:\n  dio: ^5.3.4\n  googleapis: ^9.1.0\n  googleapis_auth: ^1.3.1\n  parse_app_package:\n    path: ../parse_app_package\n  pubspec_parse: ^1.1.0\n  qiniu_sdk_base: ^0.5.0\n  shell_executor: ^0.1.5\n\ndev_dependencies:\n  dependency_validator: ^3.0.0\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_distributor/.gitignore",
    "content": ".dart_tool/\n.packages\nbuild/\npubspec.lock  # Except for application packages\n.flutter-plugins\n.flutter-plugins-dependencies\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_distributor/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021-present LiJianying <lijy91@foxmail.com>\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": "plugins/flutter_distributor/packages/flutter_distributor/analysis_options.yaml",
    "content": "include: package:mostly_reasonable_lints/analysis_options.yaml\n\nlinter:\n  rules:\n    avoid_print: false\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_distributor/bin/command_package.dart",
    "content": "import 'dart:io';\n\nimport 'package:args/command_runner.dart';\nimport 'package:flutter_distributor/flutter_distributor.dart';\nimport 'package:flutter_distributor/src/extensions/extensions.dart';\n\n/// Package an application bundle for a specific platform and target\n///\n/// This command wrapper defines, parses and transforms all passed arguments,\n/// so that they may be passed to `flutter_distributor`. The distributor will\n/// then build an application bundle using `flutter_app_packager`.\nclass CommandPackage extends Command {\n  CommandPackage(this.distributor) {\n    argParser.addOption(\n      'platform',\n      valueHelp: [\n        'android',\n        'ios',\n        'linux',\n        'macos',\n        'windows',\n        'web',\n      ].join(','),\n      help: 'The platform to package the application for',\n    );\n\n    argParser.addOption(\n      'targets',\n      aliases: ['target'],\n      valueHelp: [\n        'apk',\n        'aab',\n        'appimage',\n        'deb',\n        'dmg',\n        'exe',\n        'ipa',\n        'msix',\n        'pkg',\n        'rpm',\n        'zip',\n      ].join(','),\n      help: 'Comma separated list of bundle types to build.',\n    );\n\n    argParser.addOption('channel', valueHelp: '');\n    argParser.addOption('artifact-name', valueHelp: '');\n    argParser.addOption(\n      'description',\n      valueHelp: '',\n    );\n    argParser.addFlag(\n      'skip-clean',\n      help: 'Whether or not to skip \\'flutter clean\\' before packaging.',\n    );\n\n    argParser.addOption(\n      'flutter-build-args',\n      valueHelp: 'verbose,obfuscate',\n      help: 'Arguments to pass directly to flutter build',\n    );\n\n    argParser.addOption(\n      'build-target',\n      valueHelp: 'path',\n      help: 'The --target argument passed to \\'flutter build\\'',\n    );\n\n    argParser.addOption(\n      'build-flavor',\n      valueHelp: '',\n      help: 'The --flavor argument passed to \\'flutter build\\'',\n    );\n\n    argParser.addOption(\n      'build-target-platform',\n      valueHelp: '',\n      help: 'The --target-platform argument passed to \\'flutter build\\'',\n    );\n\n    argParser.addOption(\n      'build-export-options-plist',\n      valueHelp: '',\n      help: 'The --export-options-plist argument passed \\'flutter build\\'',\n    );\n\n    argParser.addMultiOption(\n      'build-dart-define',\n      valueHelp: 'foo=bar',\n      help: [\n        'The --dart-define argument(s) passed to \\'flutter build\\'',\n        'You may add multiple \\'--build-dart-define key=value\\' pairs',\n      ].join('\\n'),\n    );\n  }\n\n  final FlutterDistributor distributor;\n\n  @override\n  String get name => 'package';\n\n  @override\n  String get description => [\n        'Package the current Flutter application',\n        '',\n        'Options named --build-* are passed to \\'flutter build\\' as is',\n        'Please consult the \\'flutter build\\' CLI help for more informations.',\n      ].join('\\n');\n\n  @override\n  Future run() async {\n    final String? platform = argResults?['platform'];\n    final List<String> targets = '${argResults?['targets'] ?? ''}'\n        .split(',')\n        .where((e) => e.isNotEmpty)\n        .toList();\n    final String? channel = argResults?['channel'];\n    final String? artifactName = argResults?['artifact-name'];\n    final String? flutterBuildArgs = argResults?['flutter-build-args'];\n    final bool isSkipClean = argResults?.wasParsed('skip-clean') ?? false;\n    final Map<String, dynamic> buildArguments =\n        _generateBuildArgs(flutterBuildArgs);\n\n    // At least `platform` and one `targets` is required for flutter build\n    if (platform == null) {\n      print('\\nThe \\'platform\\' options is mandatory!'.red(bold: true));\n      exit(1);\n    }\n\n    if (targets.isEmpty) {\n      print('\\nAt least one \\'target\\' must be specified!'.red(bold: true));\n      exit(1);\n    }\n\n    return distributor.package(\n      platform,\n      targets,\n      channel: channel,\n      artifactName: artifactName,\n      cleanBeforeBuild: !isSkipClean,\n      buildArguments: buildArguments,\n      description: argResults!['description'],\n    );\n  }\n\n  Map<String, dynamic> _generateBuildArgs(String? flutterBuildArgs) {\n    Map<String, dynamic> buildArguments = {};\n\n    if (argResults?.options == null) return buildArguments;\n\n    for (var option in argResults!.options) {\n      if (!option.startsWith('build-')) continue;\n      dynamic value = argResults?[option];\n\n      if (value is List) {\n        // ignore: prefer_for_elements_to_map_fromiterable\n        value = Map.fromIterable(\n          value,\n          key: (e) => e.split('=')[0],\n          value: (e) => e.split('=')[1],\n        );\n      }\n\n      buildArguments.putIfAbsent(\n        option.replaceAll('build-', ''),\n        () => value,\n      );\n    }\n\n    for (var arg in flutterBuildArgs?.split(',') ?? <String>[]) {\n      if (arg.split('=').length == 2) {\n        buildArguments.putIfAbsent(\n          arg.split('=').first,\n          () => arg.split('=').last,\n        );\n      } else if (arg.split('=').length == 1) {\n        buildArguments.putIfAbsent(\n          arg.split('=')[0],\n          () => true,\n        );\n      } else {\n        buildArguments.putIfAbsent(arg, () => true);\n      }\n    }\n\n    return buildArguments;\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_distributor/bin/command_publish.dart",
    "content": "import 'dart:io';\n\nimport 'package:args/command_runner.dart';\nimport 'package:flutter_distributor/flutter_distributor.dart';\nimport 'package:flutter_distributor/src/extensions/extensions.dart';\n\n/// Publish an application to a third party provider\n///\n/// This command wrapper defines, parses and transforms all passed arguments,\n/// so that they may be passed to `flutter_distributor`. The distributor will\n/// then publish an application bundle using `flutter_app_publisher`.\nclass CommandPublish extends Command {\n  CommandPublish(this.distributor) {\n    argParser.addOption(\n      'path',\n      valueHelp: '',\n      help: 'The path to the application bundle to publish.',\n    );\n\n    argParser.addOption(\n      'targets',\n      aliases: ['target'],\n      valueHelp: [\n        'appcenter',\n        'appstore',\n        'fir',\n        'firebase',\n        'github',\n        'playstore',\n        'pgyer',\n        'qiniu',\n        'vercel',\n      ].join(','),\n      help: 'The target provider(s) to publish to.',\n    );\n\n    // AppCenter\n    argParser.addSeparator('appcenter');\n\n    argParser.addOption(\n      'appcenter-owner-name',\n      valueHelp: '',\n      help: 'The owner name for appcenter.',\n    );\n\n    argParser.addOption(\n      'appcenter-app-name',\n      valueHelp: '',\n      help: 'The app name for appcenter.',\n    );\n\n    argParser.addOption(\n      'appcenter-distribution-group',\n      valueHelp: '',\n      help: 'The distribution group for appcenter.',\n    );\n\n    // Firebase\n    argParser.addSeparator('firebase');\n\n    argParser.addOption(\n      'firebase-app',\n      valueHelp: '',\n      help: [\n        'The unique ID of the application on Firebase.',\n        'This is NOT your bundle identifier',\n      ].join('\\n'),\n    );\n\n    argParser.addOption(\n      'firebase-release-notes',\n      valueHelp: '',\n      help: 'The release notes for the published application.',\n    );\n\n    argParser.addOption(\n      'firebase-release-notes-file',\n      valueHelp: '',\n      help: [\n        'The path of a file containing the release notes',\n        'This is a more extensive alternative to firebase-release-notes',\n      ].join('\\n'),\n    );\n\n    argParser.addOption(\n      'firebase-testers',\n      valueHelp: '',\n      help:\n          'The testers that will be notified about the published application.',\n    );\n\n    argParser.addOption(\n      'firebase-testers-file',\n      valueHelp: '',\n      help: [\n        'The path of a file containing testers that will be notified',\n        'This is a more extensive alternative to firebase-testers',\n      ].join('\\n'),\n    );\n\n    argParser.addOption(\n      'firebase-groups',\n      valueHelp: '',\n      help: 'The groups that will be notified about the published application.',\n    );\n\n    argParser.addOption(\n      'firebase-groups-file',\n      valueHelp: '',\n      help: [\n        'The path of a file containing groups that will be notified',\n        'This is a more extensive alternative to firebase-groups',\n      ].join('\\n'),\n    );\n\n    // Firebase Hosting\n    argParser.addSeparator('firebase-hosting');\n    argParser.addOption('firebase-hosting-project-id', valueHelp: '');\n\n    // Github\n    argParser.addSeparator('github');\n\n    argParser.addOption(\n      'github-repo-owner',\n      valueHelp: '',\n      help: 'The name of the target GitHub repository wner (namespace)',\n    );\n\n    argParser.addOption(\n      'github-repo-name',\n      valueHelp: '',\n      help: 'The name of the target GitHub repository',\n    );\n\n    argParser.addOption(\n      'github-release-title',\n      valueHelp: '',\n      help: 'The title of the new release on GitHub',\n    );\n\n    // PlayStore\n    argParser.addSeparator('playstore');\n    argParser.addOption('playstore-package-name', valueHelp: '');\n\n    // Qiniu\n    argParser.addSeparator('qiniu');\n    argParser.addOption('qiniu-bucket', valueHelp: '');\n    argParser.addOption('qiniu-bucket-domain', valueHelp: '');\n    argParser.addOption('qiniu-savekey-prefix', valueHelp: '');\n\n    // Vercel\n    argParser.addSeparator('vercel');\n    argParser.addOption('vercel-org-id', valueHelp: '');\n    argParser.addOption('vercel-project-id', valueHelp: '');\n  }\n\n  final FlutterDistributor distributor;\n\n  @override\n  String get name => 'publish';\n\n  @override\n  String get description => 'Publish the current Flutter application';\n\n  @override\n  Future run() async {\n    String? path = argResults?['path'];\n    List<String> targets = '${argResults?['targets'] ?? ''}'\n        .split(',')\n        .where((t) => t.isNotEmpty)\n        .toList();\n\n    // At least `path` and one `targets` is required for flutter build\n    if (path == null) {\n      print('\\nThe \\'path\\' options is mandatory!'.red(bold: true));\n      exit(1);\n    }\n\n    print(targets);\n    if (targets.isEmpty) {\n      print('\\nAt least one \\'target\\' must be specified!'.red(bold: true));\n      exit(1);\n    }\n\n    // Required parameters for firebase\n    if (targets.contains('firebase')) {\n      if (argResults?['firebase-app'] == null) {\n        print('\\nFirebase app identifier is required for target \\'firebase\\'');\n        exit(1);\n      }\n    }\n\n    Map<String, String?> publishArguments = {\n      'appcenter-owner-name': argResults?['appcenter-owner-name'],\n      'appcenter-app-name': argResults?['appcenter-app-name'],\n      'appcenter-distribution-group':\n          argResults?['appcenter-distribution-group'],\n      'firebase-app': argResults?['firebase-app'],\n      'firebase-release-notes': argResults?['firebase-release-notes'],\n      'firebase-release-notes-file': argResults?['firebase-release-notes-file'],\n      'firebase-testers': argResults?['firebase-testers'],\n      'firebase-testers-file': argResults?['firebase-testers-file'],\n      'firebase-groups': argResults?['firebase-groups'],\n      'firebase-groups-file': argResults?['firebase-groups-file'],\n      'firebase-hosting-project-id': argResults?['firebase-hosting-project-id'],\n      'github-repo-owner': argResults?['github-repo-owner'],\n      'github-repo-name': argResults?['github-repo-name'],\n      'github-release-title': argResults?['github-release-title'],\n      'playstore-package-name': argResults?['playstore-package-name'],\n      'qiniu-bucket': argResults?['qiniu-bucket'],\n      'qiniu-bucket-domain': argResults?['qiniu-bucket-domain'],\n      'qiniu-savekey-prefix': argResults?['qiniu-savekey-prefix'],\n      'vercel-org-id': argResults?['vercel-org-id'],\n      'vercel-project-id': argResults?['vercel-project-id'],\n    }..removeWhere((key, value) => value == null);\n\n    final fileSystemEntity =\n        await FileSystemEntity.type(path) == FileSystemEntityType.directory\n            ? Directory(path)\n            : File(path);\n\n    return distributor.publish(\n      fileSystemEntity,\n      targets,\n      publishArguments: publishArguments,\n    );\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_distributor/bin/command_release.dart",
    "content": "import 'dart:io';\n\nimport 'package:args/command_runner.dart';\nimport 'package:flutter_distributor/flutter_distributor.dart';\nimport 'package:flutter_distributor/src/extensions/extensions.dart';\n\n/// Release (package and publish) an application based on the config\n///\n/// This command wrapper defines, parses and transforms all passed arguments,\n/// so that they may be passed to `flutter_distributor`. The distributor will\n/// then use the `distribute_options.yaml` file in the Flutter project root\n/// to run a release with one or multiple release jobs.\n///\n/// Each release job will package and optionally also publish the application\n/// based on the configuration on `distribute_options.yaml`.\nclass CommandRelease extends Command {\n  CommandRelease(this.distributor) {\n    argParser.addOption(\n      'name',\n      valueHelp: '',\n      help: 'The name of the release to run.',\n    );\n\n    argParser.addOption(\n      'jobs',\n      valueHelp: '',\n      help: 'Comma-separated list of jobs to run for the specified release.',\n    );\n\n    argParser.addOption(\n      'skip-jobs',\n      valueHelp: '',\n      help: 'Comma-separated list of jobs to skip for the specified release.',\n    );\n\n    argParser.addFlag(\n      'skip-clean',\n      help: 'Whether or not to skip \\'flutter clean\\' before packaging.',\n    );\n  }\n\n  final FlutterDistributor distributor;\n\n  @override\n  String get name => 'release';\n\n  @override\n  String get description => 'Release the current Flutter application';\n\n  @override\n  Future run() async {\n    String? name = argResults?['name'] ?? '';\n    List<String> jobNameList = (argResults?['jobs'] ?? '')\n        .split(',')\n        .where((String e) => e.isNotEmpty)\n        .toList();\n    List<String> skipJobNameList = (argResults?['skip-jobs'] ?? '')\n        .split(',')\n        .where((String e) => e.isNotEmpty)\n        .toList();\n    bool isSkipClean = argResults?.wasParsed('skip-clean') ?? false;\n\n    // At least `name` must be passed to select a release\n    if (name == null) {\n      print('\\nThe \\'name\\' options is mandatory!'.red(bold: true));\n      exit(1);\n    }\n\n    return distributor.release(\n      name,\n      jobNameList: jobNameList,\n      skipJobNameList: skipJobNameList,\n      cleanBeforeBuild: !isSkipClean,\n    );\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_distributor/bin/command_upgrade.dart",
    "content": "import 'package:args/command_runner.dart';\nimport 'package:flutter_distributor/flutter_distributor.dart';\n\n/// Upgrade flutter_distributor to the latest version\nclass CommandUpgrade extends Command {\n  CommandUpgrade(this.distributor);\n\n  final FlutterDistributor distributor;\n\n  @override\n  String get name => 'upgrade';\n\n  @override\n  String get description => 'Upgrade your copy of Flutter Distributor.';\n\n  @override\n  Future run() async {\n    await distributor.upgrade();\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_distributor/bin/main.dart",
    "content": "import 'package:args/args.dart';\nimport 'package:args/command_runner.dart';\nimport 'package:flutter_distributor/flutter_distributor.dart';\nimport 'package:flutter_distributor/src/utils/logger.dart';\n\nimport 'command_package.dart';\nimport 'command_publish.dart';\nimport 'command_release.dart';\nimport 'command_upgrade.dart';\n\nFuture<void> main(List<String> args) async {\n  FlutterDistributor distributor = FlutterDistributor();\n\n  final runner = CommandRunner('flutter_distributor', '');\n  runner.argParser\n    ..addFlag(\n      'version',\n      help: 'Reports the version of this tool.',\n      negatable: false,\n    )\n    ..addFlag(\n      'version-check',\n      help: 'Check for updates when this command runs.',\n      defaultsTo: true,\n      negatable: true,\n    );\n\n  runner.addCommand(CommandPackage(distributor));\n  runner.addCommand(CommandPublish(distributor));\n  runner.addCommand(CommandRelease(distributor));\n  runner.addCommand(CommandUpgrade(distributor));\n\n  ArgResults argResults = runner.parse(args);\n  if (argResults.wasParsed('version')) {\n    String? currentVersion = await distributor.getCurrentVersion();\n    if (currentVersion != null) {\n      logger.info(currentVersion);\n      return;\n    }\n  }\n\n  // if (argResults['version-check']) {\n  //   // Check version of flutter_distributor on every run\n  //   await distributor.checkVersion();\n  // }\n\n  return runner.runCommand(argResults);\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_distributor/lib/flutter_distributor.dart",
    "content": "library flutter_distributor;\n\nexport 'src/distribute_options.dart';\nexport 'src/flutter_distributor.dart';\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_distributor/lib/src/distribute_options.dart",
    "content": "import 'dart:io';\n\nimport 'package:flutter_distributor/src/release.dart';\n\nclass DistributeOptions {\n  DistributeOptions({\n    required this.output,\n    this.variables,\n    this.artifactName,\n    required this.releases,\n  });\n\n  factory DistributeOptions.fromJson(Map<String, dynamic> json) {\n    Map<String, String> variables = {};\n    if (json.containsKey('variables') && json['variables'] != null) {\n      variables = Map<String, String>.from(json['variables']);\n      // 兼容老版本\n    } else if (json.containsKey('env') && json['env'] != null) {\n      variables = Map<String, String>.from(json['env']);\n    }\n    List<Release> releases = ((json['releases'] ?? []) as List)\n        .map((item) => Release.fromJson(item))\n        .toList();\n    return DistributeOptions(\n      output: json['output'],\n      variables: variables,\n      artifactName: json['artifact_name'],\n      releases: releases,\n    );\n  }\n\n  final String output;\n  final Map<String, String>? variables;\n  final String? artifactName;\n  final List<Release> releases;\n\n  Directory get outputDirectory => Directory(output);\n\n  Map<String, dynamic> toJson() {\n    return {\n      'output': output,\n      'variables': variables,\n      'artifact_name': artifactName,\n      'releases': releases.map((e) => e.toJson()).toList(),\n    }..removeWhere((key, value) => value == null);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_distributor/lib/src/extensions/extensions.dart",
    "content": "export 'string.dart';\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_distributor/lib/src/extensions/string.dart",
    "content": "import 'package:ansicolor/ansicolor.dart';\n\nAnsiPen _ansiPen = AnsiPen();\n\nconst ansiResetForeground = '${ansiEscape}39m';\n\nextension StringExt on String {\n  String _applyColor(int color, {bool bg = false, bool bold = false}) {\n    String appliedColor = (_ansiPen..xterm(color, bg: bg))(this);\n    if (bold) {\n      return '${ansiEscape}1m$appliedColor$ansiDefault';\n    }\n    return appliedColor;\n  }\n\n  String black({bool bg = false, bool bold = false}) {\n    return _applyColor(0, bg: bg, bold: bold);\n  }\n\n  String red({bool bg = false, bool bold = false}) {\n    return _applyColor(1, bg: bg, bold: bold);\n  }\n\n  String green({bool bg = false, bool bold = false}) {\n    return _applyColor(2, bg: bg, bold: bold);\n  }\n\n  String yellow({bool bg = false, bool bold = false}) {\n    return _applyColor(3, bg: bg, bold: bold);\n  }\n\n  String blue({bool bg = false, bool bold = false}) {\n    return _applyColor(4, bg: bg, bold: bold);\n  }\n\n  String magenta({bool bg = false, bool bold = false}) {\n    return _applyColor(5, bg: bg, bold: bold);\n  }\n\n  String cyan({bool bg = false, bool bold = false}) {\n    return _applyColor(6, bg: bg, bold: bold);\n  }\n\n  String white({bool bg = false, bool bold = false}) {\n    return _applyColor(7, bg: bg, bold: bold);\n  }\n\n  String brightBlack({bool bg = false, bool bold = false}) {\n    return _applyColor(8, bg: bg, bold: bold);\n  }\n\n  String brightRed({bool bg = false, bool bold = false}) {\n    return _applyColor(9, bg: bg, bold: bold);\n  }\n\n  String brightGreen({bool bg = false, bool bold = false}) {\n    return _applyColor(10, bg: bg, bold: bold);\n  }\n\n  String brightYellow({bool bg = false, bool bold = false}) {\n    return _applyColor(11, bg: bg, bold: bold);\n  }\n\n  String brightBlue({bool bg = false, bool bold = false}) {\n    return _applyColor(12, bg: bg, bold: bold);\n  }\n\n  String brightMagenta({bool bg = false, bool bold = false}) {\n    return _applyColor(13, bg: bg, bold: bold);\n  }\n\n  String brightCyan({bool bg = false, bool bold = false}) {\n    return _applyColor(14, bg: bg, bold: bold);\n  }\n\n  String brightWhite({bool bg = false, bool bold = false}) {\n    return _applyColor(15, bg: bg, bold: bold);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_distributor/lib/src/flutter_distributor.dart",
    "content": "import 'dart:convert';\nimport 'dart:io';\n\nimport 'package:flutter_app_builder/flutter_app_builder.dart';\nimport 'package:flutter_app_packager/flutter_app_packager.dart';\nimport 'package:flutter_app_publisher/flutter_app_publisher.dart';\nimport 'package:flutter_distributor/src/distribute_options.dart';\nimport 'package:flutter_distributor/src/extensions/extensions.dart';\nimport 'package:flutter_distributor/src/release.dart';\nimport 'package:flutter_distributor/src/release_job.dart';\nimport 'package:flutter_distributor/src/utils/utils.dart';\nimport 'package:path/path.dart' as p;\nimport 'package:pubspec_parse/pubspec_parse.dart';\nimport 'package:shell_executor/shell_executor.dart';\nimport 'package:shell_uikit/shell_uikit.dart';\nimport 'package:yaml/yaml.dart';\n\nclass FlutterDistributor {\n  FlutterDistributor() {\n    ShellExecutor.global = DefaultShellExecutor();\n  }\n\n  final FlutterAppBuilder _builder = FlutterAppBuilder();\n  final FlutterAppPackager _packager = FlutterAppPackager();\n  final FlutterAppPublisher _publisher = FlutterAppPublisher();\n\n  Pubspec? _pubspec;\n\n  Pubspec get pubspec {\n    if (_pubspec == null) {\n      final yamlString = File('pubspec.yaml').readAsStringSync();\n      _pubspec = Pubspec.parse(yamlString);\n    }\n    return _pubspec!;\n  }\n\n  final Map<String, String> _globalVariables = {};\n\n  Map<String, String> get globalVariables {\n    if (_globalVariables.keys.isEmpty) {\n      for (String key in Platform.environment.keys) {\n        String? value = Platform.environment[key];\n        if ((value ?? '').isNotEmpty) {\n          _globalVariables[key] = value!;\n        }\n      }\n      List<String> keys = distributeOptions.variables?.keys.toList() ?? [];\n      for (String key in keys) {\n        String? value = distributeOptions.variables?[key];\n        if ((value ?? '').isNotEmpty) {\n          _globalVariables[key] = value!;\n        }\n      }\n    }\n    return _globalVariables;\n  }\n\n  DistributeOptions? _distributeOptions;\n\n  DistributeOptions get distributeOptions {\n    if (_distributeOptions == null) {\n      File file = File('distribute_options.yaml');\n      if (file.existsSync()) {\n        final yamlString = File('distribute_options.yaml').readAsStringSync();\n        final yamlDoc = loadYaml(yamlString);\n        _distributeOptions = DistributeOptions.fromJson(\n          json.decode(json.encode(yamlDoc)),\n        );\n      } else {\n        _distributeOptions = DistributeOptions(\n          output: 'dist/',\n          releases: [],\n        );\n      }\n    }\n    return _distributeOptions!;\n  }\n\n  Future<String?> _getCurrentVersion() async {\n    try {\n      var scriptFile = Platform.script.toFilePath();\n      var pathToPubSpecYaml = p.join(p.dirname(scriptFile), '../pubspec.yaml');\n      var pathToPubSpecLock = p.join(p.dirname(scriptFile), '../pubspec.lock');\n\n      var pubSpecYamlFile = File(pathToPubSpecYaml);\n\n      var pubSpecLockFile = File(pathToPubSpecLock);\n\n      if (pubSpecLockFile.existsSync()) {\n        var yamlDoc = loadYaml(await pubSpecLockFile.readAsString());\n        if (yamlDoc['packages']['flutter_distributor'] == null) {\n          var yamlDoc = loadYaml(await pubSpecYamlFile.readAsString());\n          return yamlDoc['version'];\n        }\n\n        return yamlDoc['packages']['flutter_distributor']['version'];\n      }\n    } catch (_) {}\n    return null;\n  }\n\n  Future<void> checkVersion() async {\n    String? currentVersion = await _getCurrentVersion();\n    String? latestVersion =\n        await PubDevApi.getLatestVersionFromPackage('flutter_distributor');\n\n    if (currentVersion != null &&\n        latestVersion != null &&\n        currentVersion.compareTo(latestVersion) < 0) {\n      String msg = [\n        '╔════════════════════════════════════════════════════════════════════════════╗',\n        '║ A new version of Flutter Distributor is available!                         ║',\n        '║                                                                            ║',\n        '║ To update to the latest version, run \"flutter_distributor upgrade\".        ║',\n        '╚════════════════════════════════════════════════════════════════════════════╝',\n        '',\n      ].join('\\n');\n      print(msg);\n    }\n    return Future.value();\n  }\n\n  Future<String?> getCurrentVersion() async {\n    return await _getCurrentVersion();\n  }\n\n  Future<List<MakeResult>> package(\n    String platform,\n    List<String> targets, {\n    String? channel,\n    String? artifactName,\n    String? description,\n    required bool cleanBeforeBuild,\n    required Map<String, dynamic> buildArguments,\n    Map<String, String>? variables,\n  }) async {\n    List<MakeResult> makeResultList = [];\n\n    try {\n      Directory outputDirectory = distributeOptions.outputDirectory;\n      if (!outputDirectory.existsSync()) {\n        outputDirectory.createSync(recursive: true);\n      }\n\n      if (cleanBeforeBuild) {\n        await _builder.clean();\n      }\n\n      bool isBuildOnlyOnce = platform != 'android';\n      BuildResult? buildResult;\n\n      for (String target in targets) {\n        logger.info('Packaging ${pubspec.name} ${pubspec.version} as $target:');\n        if (!isBuildOnlyOnce || (isBuildOnlyOnce && buildResult == null)) {\n          try {\n            buildResult = await _builder.build(\n              platform,\n              target: target,\n              arguments: buildArguments,\n              environment: variables ?? globalVariables,\n            );\n            print(\n              const JsonEncoder.withIndent('  ').convert(buildResult.toJson()),\n            );\n            logger.info(\n              'Successfully built ${buildResult.outputDirectory} in ${buildResult.duration!.inSeconds}s'\n                  .brightGreen(),\n            );\n          } on UnsupportedError catch (error) {\n            logger.warning('Warning: ${error.message}'.yellow());\n            continue;\n          } catch (error) {\n            rethrow;\n          }\n        }\n\n        if (buildResult != null) {\n          String buildMode =\n              buildArguments.containsKey('profile') ? 'profile' : 'release';\n          Map<String, dynamic>? arguments = {\n            'build_mode': buildMode,\n            'flavor': buildArguments['flavor'],\n            'channel': channel,\n            'artifact_name': artifactName,\n            'description': description,\n            if (Platform.isWindows)\n              'arch': (buildResult as BuildWindowsResult).arch,\n          };\n          MakeResult makeResult = await _packager.package(\n            platform,\n            target,\n            arguments,\n            outputDirectory,\n            buildOutputDirectory: buildResult.outputDirectory,\n            buildOutputFiles: buildResult.outputFiles,\n          );\n          print(\n            const JsonEncoder.withIndent('  ').convert(makeResult.toJson()),\n          );\n          FileSystemEntity artifact = makeResult.artifacts.first;\n          logger.info(\n            'Successfully packaged ${artifact.path}'.brightGreen(),\n          );\n          makeResultList.add(makeResult);\n        }\n      }\n    } catch (error) {\n      logger.severe(error.toString().red());\n      if (error is Error) {\n        logger.severe(error.stackTrace.toString().red());\n      }\n      rethrow;\n    }\n\n    return makeResultList;\n  }\n\n  Future<List<PublishResult>> publish(\n    FileSystemEntity fileSystemEntity,\n    List<String> targets, {\n    Map<String, dynamic>? publishArguments,\n    Map<String, String>? variables,\n  }) async {\n    List<PublishResult> publishResultList = [];\n    try {\n      for (String target in targets) {\n        ProgressBar progressBar = ProgressBar(\n          format: 'Publishing to $target: {bar} {value}/{total} {percentage}%',\n        );\n\n        Map<String, dynamic>? newPublishArguments = {};\n\n        if (publishArguments != null) {\n          for (var key in publishArguments.keys) {\n            if (!key.startsWith('$target-')) continue;\n            dynamic value = publishArguments[key];\n\n            if (value is List) {\n              // ignore: prefer_for_elements_to_map_fromiterable\n              value = Map.fromIterable(\n                value,\n                key: (e) => e.split('=')[0],\n                value: (e) => e.split('=')[1],\n              );\n            }\n\n            newPublishArguments.putIfAbsent(\n              key.replaceAll('$target-', ''),\n              () => value,\n            );\n          }\n        }\n\n        if (newPublishArguments.keys.isEmpty) {\n          newPublishArguments = publishArguments;\n        }\n\n        PublishResult publishResult = await _publisher.publish(\n          fileSystemEntity,\n          target: target,\n          environment: variables ?? globalVariables,\n          publishArguments: newPublishArguments,\n          onPublishProgress: (sent, total) {\n            if (!progressBar.isActive) {\n              progressBar.start(total, sent);\n            } else {\n              progressBar.update(sent);\n            }\n          },\n        );\n        if (progressBar.isActive) progressBar.stop();\n        logger.info(\n          'Successfully published ${publishResult.url}'.brightGreen(),\n        );\n        publishResultList.add(publishResult);\n      }\n    } on Error catch (error) {\n      logger.severe(error.toString().brightRed());\n      logger.severe(error.stackTrace.toString().brightRed());\n    }\n    return publishResultList;\n  }\n\n  Future<void> release(\n    String name, {\n    required List<String> jobNameList,\n    required List<String> skipJobNameList,\n    required bool cleanBeforeBuild,\n  }) async {\n    final time = Stopwatch()..start();\n\n    try {\n      Directory outputDirectory = distributeOptions.outputDirectory;\n      if (!outputDirectory.existsSync()) {\n        outputDirectory.createSync(recursive: true);\n      }\n\n      List<Release> releases = [];\n\n      if (name.isNotEmpty) {\n        releases =\n            distributeOptions.releases.where((e) => e.name == name).toList();\n      }\n\n      if (releases.isEmpty) {\n        throw Exception('Missing/incomplete `distribute_options.yaml` file.');\n      }\n\n      for (Release release in releases) {\n        List<ReleaseJob> filteredJobs = release.jobs.where((e) {\n          if (jobNameList.isNotEmpty) {\n            return jobNameList.contains(e.name);\n          }\n          if (skipJobNameList.isNotEmpty) {\n            return !skipJobNameList.contains(e.name);\n          }\n          return true;\n        }).toList();\n        if (filteredJobs.isEmpty) {\n          throw Exception('No available jobs found in ${release.name}.');\n        }\n\n        bool needCleanBeforeBuild = cleanBeforeBuild;\n\n        for (ReleaseJob job in filteredJobs) {\n          logger.info('');\n          logger.info(\n            '${'===>'.blue()} ${'Releasing'.white(bold: true)} $name:${job.name.green(bold: true)}',\n          );\n\n          Map<String, String> variables = {}\n            ..addAll(globalVariables)\n            ..addAll(release.variables ?? {})\n            ..addAll(job.variables ?? {});\n\n          List<MakeResult> makeResultList = await package(\n            job.package.platform,\n            [job.package.target],\n            channel: job.package.channel,\n            artifactName: distributeOptions.artifactName,\n            cleanBeforeBuild: needCleanBeforeBuild,\n            buildArguments: job.package.buildArgs ?? {},\n            variables: variables,\n          );\n          // Clean only once\n          needCleanBeforeBuild = false;\n\n          if (job.publish != null || job.publishTo != null) {\n            String? publishTarget = job.publishTo ?? job.publish?.target;\n            MakeResult makeResult = makeResultList.first;\n            FileSystemEntity artifact = makeResult.artifacts.first;\n            await publish(\n              artifact,\n              [publishTarget!],\n              publishArguments: job.publish?.args,\n              variables: variables,\n            );\n          }\n        }\n      }\n\n      time.stop();\n      logger.info('');\n      logger.info(\n        'RELEASE SUCCESSFUL in ${time.elapsed.inSeconds}s'.green(bold: true),\n      );\n    } catch (error, stacktrace) {\n      time.stop();\n      logger.info('');\n      logger.severe(\n        [\n          'RELEASE FAILED in ${time.elapsed.inSeconds}s'.red(bold: true),\n          error.toString().red(),\n          stacktrace,\n        ].join('\\n'),\n      );\n    }\n    return Future.value();\n  }\n\n  Future<void> upgrade() async {\n    await $(\n      'dart',\n      ['pub', 'global', 'activate', 'flutter_distributor'],\n    );\n    return Future.value();\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_distributor/lib/src/release.dart",
    "content": "import 'package:flutter_distributor/src/release_job.dart';\n\nclass Release {\n  Release({\n    this.variables,\n    required this.name,\n    required this.jobs,\n  });\n\n  factory Release.fromJson(Map<String, dynamic> json) {\n    Map<String, String> variables = {};\n    if (json.containsKey('variables') && json['variables'] != null) {\n      variables = Map<String, String>.from(json['variables']);\n    }\n    List<ReleaseJob> jobs = (json['jobs'] as List)\n        .map((item) => ReleaseJob.fromJson(item))\n        .toList();\n    return Release(\n      variables: variables,\n      name: json['name'],\n      jobs: jobs,\n    );\n  }\n\n  final Map<String, String>? variables;\n  final String name;\n  final List<ReleaseJob> jobs;\n\n  Map<String, dynamic> toJson() {\n    return {\n      'variables': variables,\n      'name': name,\n      'jobs': jobs.map((e) => e.toJson()).toList(),\n    }..removeWhere((key, value) => value == null);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_distributor/lib/src/release_job.dart",
    "content": "class ReleaseJobPackage {\n  ReleaseJobPackage({\n    required this.platform,\n    required this.target,\n    this.channel,\n    this.buildArgs,\n  });\n\n  factory ReleaseJobPackage.fromJson(Map<String, dynamic> json) {\n    return ReleaseJobPackage(\n      platform: json['platform'],\n      target: json['target'],\n      channel: json['channel'],\n      buildArgs: json['build_args'],\n    );\n  }\n\n  final String platform;\n  final String target;\n  final String? channel;\n  final Map<String, dynamic>? buildArgs;\n\n  Map<String, dynamic> toJson() {\n    return {\n      'platform': platform,\n      'target': target,\n      'channel': channel,\n      'build_args': buildArgs,\n    }..removeWhere((key, value) => value == null);\n  }\n}\n\nclass ReleaseJobPublish {\n  ReleaseJobPublish({\n    required this.target,\n    this.args,\n  });\n\n  factory ReleaseJobPublish.fromJson(Map<String, dynamic> json) {\n    return ReleaseJobPublish(\n      target: json['target'],\n      args: json['args'],\n    );\n  }\n  final String target;\n  final Map<String, dynamic>? args;\n\n  Map<String, dynamic> toJson() {\n    return {\n      'target': target,\n      'args': args,\n    }..removeWhere((key, value) => value == null);\n  }\n}\n\nclass ReleaseJob {\n  ReleaseJob({\n    this.variables,\n    required this.name,\n    required this.package,\n    this.publish,\n    this.publishTo,\n  });\n\n  factory ReleaseJob.fromJson(Map<String, dynamic> json) {\n    Map<String, String> variables = {};\n    if (json.containsKey('variables') && json['variables'] != null) {\n      variables = Map<String, String>.from(json['variables']);\n    }\n    return ReleaseJob(\n      variables: variables,\n      name: json['name'],\n      package: ReleaseJobPackage.fromJson(json['package']),\n      publish: json['publish'] != null\n          ? ReleaseJobPublish.fromJson(json['publish'])\n          : null,\n      publishTo: json['publish_to'],\n    );\n  }\n\n  final Map<String, String>? variables;\n  final String name;\n  final ReleaseJobPackage package;\n  final ReleaseJobPublish? publish;\n  final String? publishTo;\n\n  Map<String, dynamic> toJson() {\n    return {\n      'variables': variables,\n      'name': name,\n      'package': package.toJson(),\n      'publish': publish?.toJson(),\n      'publish_to': publishTo,\n    }..removeWhere((key, value) => value == null);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_distributor/lib/src/utils/default_shell_executor.dart",
    "content": "import 'dart:convert';\nimport 'dart:io';\n\nimport 'package:charset/charset.dart';\nimport 'package:flutter_distributor/src/extensions/string.dart';\nimport 'package:flutter_distributor/src/utils/logger.dart';\nimport 'package:shell_executor/shell_executor.dart';\n\n/// Convert bytes to string (UTF-8 or detected charset)\nString convertToString(List<int> bytes) {\n  if (Platform.isWindows) {\n    final charset = Charset.detect(bytes);\n    if (charset != null) {\n      return charset.decode(bytes);\n    }\n  }\n  return utf8.decode(bytes, allowMalformed: true);\n}\n\nclass DefaultShellExecutor extends ShellExecutor {\n  @override\n  Future<ProcessResult> exec(\n    String executable,\n    List<String> arguments, {\n    String? workingDirectory,\n    Map<String, String>? environment,\n  }) async {\n    final Process process = await Process.start(\n      executable,\n      arguments,\n      workingDirectory: workingDirectory,\n      environment: environment,\n      runInShell: true,\n    );\n\n    logger.info('\\$ $executable ${arguments.join(' ')}'.brightBlack());\n\n    String? stdoutStr;\n    String? stderrStr;\n\n    process.stdout.listen((data) {\n      String msg = convertToString(data);\n      stdoutStr = '${stdoutStr ?? ''}$msg';\n      stdout.write(msg.brightBlack());\n    });\n    process.stderr.listen((data) {\n      String msg = convertToString(data);\n      stderrStr = '${stderrStr ?? ''}$msg';\n      stderr.write(msg.brightRed());\n    });\n    int exitCode = await process.exitCode;\n    return ProcessResult(process.pid, exitCode, stdoutStr, stderrStr);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_distributor/lib/src/utils/logger.dart",
    "content": "import 'package:logging/logging.dart';\n\nLogger logger = Logger('flutter_distributor')\n  ..onRecord.listen((record) {\n    print(record.message);\n  });\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_distributor/lib/src/utils/pub_dev_api.dart",
    "content": "import 'dart:io';\n\nimport 'package:dio/dio.dart';\n\nclass PubDevApi {\n  static Future<String?> getLatestVersionFromPackage(String package) async {\n    String pubHostedUrl = Platform.environment['PUB_HOSTED_URL'] ?? '';\n    final pubSite = pubHostedUrl.isNotEmpty\n        ? '$pubHostedUrl/api/packages/$package'\n        : 'https://pub.dev/api/packages/$package';\n    final uri = Uri.parse(pubSite);\n    try {\n      final response = await Dio().get(uri.toString());\n      return response.data['latest']['version'] as String?;\n    } catch (error) {\n      rethrow;\n    }\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_distributor/lib/src/utils/utils.dart",
    "content": "export 'default_shell_executor.dart';\nexport 'logger.dart';\nexport 'pub_dev_api.dart';\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_distributor/pubspec.yaml",
    "content": "name: flutter_distributor\ndescription: A complete tool for packaging and publishing your Flutter apps.\nversion: 0.3.7\nhomepage: https://distributor.leanflutter.org\nrepository: https://github.com/leanflutter/flutter_distributor/tree/main/packages/flutter_distributor\nissue_tracker: https://github.com/leanflutter/flutter_distributor/issues\n\nplatforms:\n  linux:\n  macos:\n  windows:\n\nenvironment:\n  sdk: \">=2.16.0 <4.0.0\"\n\ndependencies:\n  ansicolor: ^2.0.1\n  args: ^2.2.0\n  charset: ^2.0.1\n  dio: ^5.3.4\n  flutter_app_builder:\n    path: ../flutter_app_builder\n  flutter_app_packager:\n    path: ../flutter_app_packager\n  flutter_app_publisher:\n    path: ../flutter_app_publisher\n  logging: ^1.0.2\n  path: ^1.8.1\n  pubspec_parse: ^1.1.0\n  shell_executor: ^0.1.5\n  shell_uikit: ^0.1.1\n  yaml: ^3.1.0\n\ndev_dependencies:\n  dependency_validator: ^3.0.0\n  test: ^1.23.1\n\nexecutables:\n  flutter_distributor: main\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/flutter_distributor/test/src/extensions/string_test.dart",
    "content": "import 'package:flutter_distributor/src/extensions/string.dart';\nimport 'package:test/test.dart';\n\nvoid main() {\n  // print('black        '.black());\n  // print('red          '.red());\n  // print('green        '.green());\n  // print('yellow       '.yellow());\n  // print('blue         '.blue());\n  // print('magenta      '.magenta());\n  // print('cyan         '.cyan());\n  // print('white        '.white());\n  // print('brightBlack  '.brightBlack());\n  // print('brightRed    '.brightRed());\n  // print('brightGreen  '.brightGreen());\n  // print('brightYellow '.brightYellow());\n  // print('brightBlue   '.brightBlue());\n  // print('brightMagenta'.brightMagenta());\n  // print('BrightCyan   '.brightCyan());\n  // print('brightWhite  '.brightWhite());\n\n  // print('black        '.black(bold: true));\n  // print('red          '.red(bold: true));\n  // print('green        '.green(bold: true));\n  // print('yellow       '.yellow(bold: true));\n  // print('blue         '.blue(bold: true));\n  // print('magenta      '.magenta(bold: true));\n  // print('cyan         '.cyan(bold: true));\n  // print('white        '.white(bold: true));\n  // print('brightBlack  '.brightBlack(bold: true));\n  // print('brightRed    '.brightRed(bold: true));\n  // print('brightGreen  '.brightGreen(bold: true));\n  // print('brightYellow '.brightYellow(bold: true));\n  // print('brightBlue   '.brightBlue(bold: true));\n  // print('brightMagenta'.brightMagenta(bold: true));\n  // print('BrightCyan   '.brightCyan(bold: true));\n  // print('brightWhite  '.brightWhite(bold: true));\n\n  // print('black        '.black(bg: true));\n  // print('red          '.red(bg: true));\n  // print('green        '.green(bg: true));\n  // print('yellow       '.yellow(bg: true));\n  // print('blue         '.blue(bg: true));\n  // print('magenta      '.magenta(bg: true));\n  // print('cyan         '.cyan(bg: true));\n  // print('white        '.white(bg: true));\n  // print('brightBlack  '.brightBlack(bg: true));\n  // print('brightRed    '.brightRed(bg: true));\n  // print('brightGreen  '.brightGreen(bg: true));\n  // print('brightYellow '.brightYellow(bg: true));\n  // print('brightBlue   '.brightBlue(bg: true));\n  // print('brightMagenta'.brightMagenta(bg: true));\n  // print('BrightCyan   '.brightCyan(bg: true));\n  // print('brightWhite  '.brightWhite(bg: true));\n\n  // print('black        '.black(bold: true, bg: true));\n  // print('red          '.red(bold: true, bg: true));\n  // print('green        '.green(bold: true, bg: true));\n  // print('yellow       '.yellow(bold: true, bg: true));\n  // print('blue         '.blue(bold: true, bg: true));\n  // print('magenta      '.magenta(bold: true, bg: true));\n  // print('cyan         '.cyan(bold: true, bg: true));\n  // print('white        '.white(bold: true, bg: true));\n  // print('brightBlack  '.brightBlack(bold: true, bg: true));\n  // print('brightRed    '.brightRed(bold: true, bg: true));\n  // print('brightGreen  '.brightGreen(bold: true, bg: true));\n  // print('brightYellow '.brightYellow(bold: true, bg: true));\n  // print('brightBlue   '.brightBlue(bold: true, bg: true));\n  // print('brightMagenta'.brightMagenta(bold: true, bg: true));\n  // print('BrightCyan   '.brightCyan(bold: true, bg: true));\n  // print('brightWhite  '.brightWhite(bold: true, bg: true));\n\n  group('StringExt', () {\n    test('black', () {\n      expect('test'.red(), 'test');\n    });\n  });\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/parse_app_package/.gitignore",
    "content": ".dart_tool/\n.packages\nbuild/\npubspec.lock  # Except for application packages"
  },
  {
    "path": "plugins/flutter_distributor/packages/parse_app_package/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021-present LiJianying <lijy91@foxmail.com>\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": "plugins/flutter_distributor/packages/parse_app_package/bin/main.dart",
    "content": "import 'dart:convert';\nimport 'dart:io';\n\nimport 'package:args/args.dart';\nimport 'package:parse_app_package/parse_app_package.dart';\n\nJsonEncoder _encoder = const JsonEncoder.withIndent('  ');\n\nFuture<void> main(List<String> args) async {\n  ArgParser argParser = ArgParser();\n\n  ArgResults argResults = argParser.parse(args);\n\n  String path = argResults.arguments.first;\n  AppPackage appPackage = await parseAppPackage(File(path));\n\n  print(_encoder.convert(appPackage.toJson()));\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/parse_app_package/lib/parse_app_package.dart",
    "content": "library app_package_maker;\n\nexport 'src/api/app_package_parser.dart';\nexport 'src/parse_app_package.dart';\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/parse_app_package/lib/src/api/app_package_parser.dart",
    "content": "import 'dart:io';\n\nabstract class AppPackageParser {\n  String get name => throw UnimplementedError();\n\n  bool get isSupportedOnCurrentPlatform => true;\n\n  Future<AppPackage> parse(File file);\n}\n\nclass AppPackage {\n  AppPackage({\n    required this.platform,\n    required this.identifier,\n    required this.name,\n    required this.version,\n    required this.buildNumber,\n  });\n\n  final String platform;\n  final String identifier;\n  final String name;\n  final String version;\n  final int buildNumber;\n\n  Map<String, dynamic> toJson() {\n    return {\n      'platform': platform,\n      'identifier': identifier,\n      'name': name,\n      'version': version,\n      'buildNumber': buildNumber,\n    }..removeWhere((key, value) => value == null);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/parse_app_package/lib/src/parse_app_package.dart",
    "content": "import 'dart:io';\n\nimport 'package:parse_app_package/src/api/app_package_parser.dart';\nimport 'package:parse_app_package/src/parsers/parsers.dart';\n\nfinal List<AppPackageParser> _parsers = [\n  AppPackageParserApk(),\n  AppPackageParserIpa(),\n];\n\nFuture<AppPackage> parseAppPackage(File file) {\n  AppPackageParser parser = _parsers.firstWhere(\n    (e) => file.path.endsWith('.${e.name}'),\n  );\n  return parser.parse(file);\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/parse_app_package/lib/src/parsers/apk/app_package_parser_apk.dart",
    "content": "import 'dart:io';\n\nimport 'package:parse_app_package/src/api/app_package_parser.dart';\nimport 'package:shell_executor/shell_executor.dart';\n\nclass AppPackageParserApk extends AppPackageParser {\n  @override\n  String get name => 'apk';\n\n  @override\n  Future<AppPackage> parse(File file) async {\n    String? androidHome = Platform.environment['ANDROID_HOME'];\n    if ((androidHome ?? '').isEmpty) {\n      throw Exception('Missing `ANDROID_HOME` environment variable.');\n    }\n\n    String buildToolsDir = Directory('$androidHome/build-tools')\n        .listSync()\n        .firstWhere((element) => !element.path.contains('.DS_Store'))\n        .path;\n\n    ProcessResult processResult = await $(\n      '$buildToolsDir/aapt',\n      ['d', '--values', 'badging', file.path],\n    );\n\n    String resultString = processResult.stdout;\n\n    RegExpMatch? regExpMatch1 =\n        RegExp(r\"name='([^']+)'\").firstMatch(resultString);\n    RegExpMatch? regExpMatch2 =\n        RegExp(r\"application-label:'([^']+)'\").firstMatch(resultString);\n    RegExpMatch? regExpMatch3 =\n        RegExp(r\"versionName='([^']+)\").firstMatch(resultString);\n    RegExpMatch? regExpMatch4 =\n        RegExp(r\"versionCode='(\\d+)'\").firstMatch(resultString);\n\n    AppPackage appPackage = AppPackage(\n      platform: 'android',\n      identifier: regExpMatch1!.group(1)!,\n      name: regExpMatch2!.group(1)!,\n      version: regExpMatch3!.group(1)!,\n      buildNumber: int.parse(regExpMatch4!.group(1)!),\n    );\n    return appPackage;\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/parse_app_package/lib/src/parsers/ipa/app_package_parser_ipa.dart",
    "content": "import 'dart:io';\n\nimport 'package:archive/archive.dart';\nimport 'package:archive/archive_io.dart';\nimport 'package:parse_app_package/src/api/app_package_parser.dart';\nimport 'package:plist_parser/plist_parser.dart';\n\nclass AppPackageParserIpa extends AppPackageParser {\n  @override\n  String get name => 'ipa';\n\n  @override\n  Future<AppPackage> parse(File file) async {\n    final ipaBytes = file.readAsBytesSync();\n    final archive = ZipDecoder().decodeBytes(ipaBytes);\n    Map<dynamic, dynamic>? result;\n    for (final item in archive) {\n      if (item.isFile && item.name.endsWith('.app/Info.plist')) {\n        final data = item.content;\n        result = PlistParser().parseBytes(data);\n        break;\n      }\n    }\n    if (result == null) throw Exception('Can\\'t parse .ipa file.');\n    return AppPackage(\n      platform: 'ios',\n      identifier: result['CFBundleIdentifier'],\n      name: result['CFBundleDisplayName'] ?? result['CFBundleName'],\n      version: result['CFBundleShortVersionString'],\n      buildNumber: int.parse(result['CFBundleVersion']),\n    );\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/parse_app_package/lib/src/parsers/parsers.dart",
    "content": "export 'apk/app_package_parser_apk.dart';\nexport 'ipa/app_package_parser_ipa.dart';\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/parse_app_package/pubspec.yaml",
    "content": "name: parse_app_package\ndescription: Parse app package\nversion: 0.4.0\nhomepage: https://distributor.leanflutter.dev\nrepository: https://github.com/leanflutter/flutter_distributor/tree/main/packages/parse_app_package\n\nenvironment:\n  sdk: \">=2.16.0 <4.0.0\"\n\ndependencies:\n  archive: ^3.4.10\n  args: ^2.2.0\n  plist_parser: ^0.0.11\n  shell_executor: ^0.1.5\n\nexecutables:\n  parse_app_package: main\n\ndev_dependencies:\n  dependency_validator: ^3.0.0\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/shell_executor/.gitignore",
    "content": ".dart_tool/\n.packages\nbuild/\npubspec.lock  # Except for application packages"
  },
  {
    "path": "plugins/flutter_distributor/packages/shell_executor/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021-present LiJianying <lijy91@foxmail.com>\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": "plugins/flutter_distributor/packages/shell_executor/lib/shell_executor.dart",
    "content": "library shell_executor;\n\nexport 'src/command.dart';\nexport 'src/command_error.dart';\nexport 'src/shell_executor.dart';\nexport 'src/utils/path_expansion.dart';\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/shell_executor/lib/src/command.dart",
    "content": "import 'dart:io';\n\nimport 'package:shell_executor/src/shell_executor.dart';\n\nabstract class Command {\n  String get executable => throw UnimplementedError();\n\n  Future<void> install();\n\n  Future<ProcessResult> exec(\n    List<String> arguments, {\n    String? workingDirectory,\n    Map<String, String>? environment,\n  }) {\n    return ShellExecutor.global.exec(\n      executable,\n      arguments,\n      workingDirectory: workingDirectory,\n      environment: environment,\n    );\n  }\n\n  ProcessResult execSync(\n    List<String> arguments, {\n    String? workingDirectory,\n    Map<String, String>? environment,\n    bool runInShell = false,\n  }) {\n    return ShellExecutor.global.execSync(\n      executable,\n      arguments,\n      workingDirectory: workingDirectory,\n      environment: environment,\n      runInShell: runInShell,\n    );\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/shell_executor/lib/src/command_error.dart",
    "content": "import 'package:shell_executor/src/command.dart';\n\nclass CommandError extends Error {\n  CommandError(this.command, [this.message]);\n\n  final Command command;\n  final String? message;\n\n  @override\n  String toString() {\n    return (message != null) ? 'CommandError: $message' : 'CommandError';\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/shell_executor/lib/src/shell_executor.dart",
    "content": "import 'dart:convert';\nimport 'dart:io';\n\nFuture<ProcessResult> $(\n  String executable,\n  List<String> arguments, {\n  String? workingDirectory,\n  Map<String, String>? environment,\n}) {\n  return ShellExecutor.global.exec(\n    executable,\n    arguments,\n    workingDirectory: workingDirectory,\n    environment: environment,\n  );\n}\n\nclass ShellExecutor {\n  static ShellExecutor global = ShellExecutor();\n\n  Future<ProcessResult> exec(\n    String executable,\n    List<String> arguments, {\n    String? workingDirectory,\n    Map<String, String>? environment,\n  }) async {\n    final Process process = await Process.start(\n      executable,\n      arguments,\n      workingDirectory: workingDirectory,\n      environment: environment,\n    );\n\n    String? stdoutStr;\n    String? stderrStr;\n\n    process.stdout.listen((event) {\n      String msg = utf8.decoder.convert(event);\n      stdoutStr = '${stdoutStr ?? ''}$msg';\n      stdout.write(msg);\n    });\n    process.stderr.listen((event) {\n      String msg = utf8.decoder.convert(event);\n      stderrStr = '${stderrStr ?? ''}$msg';\n      stderr.write(msg);\n    });\n    int exitCode = await process.exitCode;\n    return ProcessResult(process.pid, exitCode, stdoutStr, stderrStr);\n  }\n\n  ProcessResult execSync(\n    String executable,\n    List<String> arguments, {\n    String? workingDirectory,\n    Map<String, String>? environment,\n    bool runInShell = false,\n  }) {\n    final ProcessResult processResult = Process.runSync(\n      executable,\n      arguments,\n      workingDirectory: workingDirectory,\n      environment: environment,\n      runInShell: runInShell,\n    );\n    return processResult;\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/shell_executor/lib/src/utils/path_expansion.dart",
    "content": "String pathExpansion(\n  String path, [\n  Map<String, String> environment = const {},\n]) {\n  if (path.startsWith('~/')) {\n    final home = environment['HOME'] ?? environment['USERPROFILE'];\n    path = '$home${path.substring(1)}';\n  }\n\n  final matches = [\n    ...RegExp(r'\\$(\\w+)').allMatches(path),\n    ...RegExp(r'\\$\\{(\\w+)\\}').allMatches(path),\n  ];\n  for (final match in matches) {\n    final envName = match.group(1);\n    final envValue = environment[envName];\n    path = path.replaceFirst(match.group(0)!, envValue ?? '');\n  }\n  return path;\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/shell_executor/pubspec.yaml",
    "content": "name: shell_executor\ndescription: A simple shell commands executor.\nversion: 0.1.6\nhomepage: https://distributor.leanflutter.dev\nrepository: https://github.com/leanflutter/flutter_distributor/tree/main/packages/shell_executor\n\nenvironment:\n  sdk: \">=2.16.0 <4.0.0\"\n\ndev_dependencies:\n  dependency_validator: ^3.0.0\n  test: ^1.23.1\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/shell_executor/test/src/utils/path_expansion_test.dart",
    "content": "import 'package:shell_executor/src/utils/path_expansion.dart';\nimport 'package:test/test.dart';\n\nvoid main() {\n  Map<String, String> environment = {\n    'HOME': '/home/root',\n  };\n  group('pathExpansion', () {\n    test('~/Documents', () {\n      final path = pathExpansion('~/Documents', environment);\n      expect(path, '/home/root/Documents');\n    });\n    test('\\$HOME/Documents', () {\n      final r = pathExpansion('\\$HOME/Documents', environment);\n      expect(r, '/home/root/Documents');\n    });\n    test('\\${HOME}/Documents', () {\n      final r = pathExpansion('\\${HOME}/Documents', environment);\n      expect(r, '/home/root/Documents');\n    });\n  });\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/shell_uikit/.gitignore",
    "content": ".dart_tool/\n.packages\nbuild/\npubspec.lock  # Except for application packages"
  },
  {
    "path": "plugins/flutter_distributor/packages/shell_uikit/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021-present LiJianying <lijy91@foxmail.com>\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": "plugins/flutter_distributor/packages/shell_uikit/example/progress_bar_example.dart",
    "content": "import 'dart:async';\nimport 'dart:io';\nimport 'dart:math' as math;\n\nimport 'package:shell_uikit/shell_uikit.dart';\n\n/// This example demonstrates various ways to use the ProgressBar component.\nvoid main() async {\n  // Clear the screen\n  if (Platform.isWindows) {\n    stdout.write('\\x1B[2J\\x1B[0f');\n  } else {\n    stdout.write('\\x1B[2J\\x1B[H');\n  }\n\n  stdout.writeln('ProgressBar Examples\\n');\n\n  // Run examples one by one\n  await basicExample();\n  await customFormatExample();\n  await customAppearanceExample();\n  await incrementExample();\n  await simulateDownloadExample();\n  await multipleProgressBarsExample();\n\n  stdout.writeln('\\nAll examples completed!');\n}\n\n/// Basic usage of ProgressBar\nFuture<void> basicExample() async {\n  stdout.writeln('1. Basic Progress Bar:');\n\n  // Create a simple progress bar\n  final progressBar = ProgressBar(\n    format: '[{bar}] {percentage}% ({value}/{total})',\n  );\n\n  // Start the progress bar with a total of 100\n  progressBar.start(100);\n\n  // Update the progress bar in a loop\n  for (int i = 0; i <= 100; i++) {\n    await Future.delayed(Duration(milliseconds: 20));\n    progressBar.update(i);\n  }\n\n  // Progress bar will automatically stop when it reaches 100%\n  await Future.delayed(Duration(milliseconds: 500));\n  stdout.writeln('\\n');\n}\n\n/// Example with custom format including duration and ETA\nFuture<void> customFormatExample() async {\n  stdout.writeln('2. Custom Format with Duration and ETA:');\n\n  // Create a progress bar with custom format\n  final progressBar = ProgressBar(\n    format: '[{bar}] {percentage}% | Elapsed: {duration}s | ETA: {eta}s',\n  );\n\n  // Start the progress bar with a total of 50\n  progressBar.start(50);\n\n  // Update the progress bar with varying speed to demonstrate ETA changes\n  for (int i = 0; i <= 50; i++) {\n    // Simulate varying processing speed\n    final delay = 30 + (math.sin(i / 5) * 20).round();\n    await Future.delayed(Duration(milliseconds: delay));\n    progressBar.update(i);\n  }\n\n  await Future.delayed(Duration(milliseconds: 500));\n  stdout.writeln('\\n');\n}\n\n/// Example with custom appearance\nFuture<void> customAppearanceExample() async {\n  stdout.writeln('3. Custom Appearance:');\n\n  // Create a progress bar with custom characters and size\n  final progressBar = ProgressBar(\n    format: '[{bar}] {percentage}%',\n    barCompleteChar: '█', // Full block\n    barIncompleteChar: '░', // Light shade\n    barSize: 30, // Shorter bar\n  );\n\n  progressBar.start(100);\n\n  for (int i = 0; i <= 100; i++) {\n    await Future.delayed(Duration(milliseconds: 15));\n    progressBar.update(i);\n  }\n\n  await Future.delayed(Duration(milliseconds: 500));\n  stdout.writeln('\\n');\n}\n\n/// Example using increment method\nFuture<void> incrementExample() async {\n  stdout.writeln('4. Using Increment Method:');\n\n  final progressBar = ProgressBar(\n    format: '[{bar}] {percentage}% | {value}/{total} steps completed',\n  );\n\n  // Start with a total of 20 steps\n  progressBar.start(20);\n\n  // Increment by different amounts\n  for (int i = 0; i < 5; i++) {\n    await Future.delayed(Duration(milliseconds: 300));\n    progressBar.increment(); // Increment by 1 (default)\n  }\n\n  for (int i = 0; i < 3; i++) {\n    await Future.delayed(Duration(milliseconds: 500));\n    progressBar.increment(2); // Increment by 2\n  }\n\n  await Future.delayed(Duration(milliseconds: 700));\n  progressBar.increment(5); // Increment by 5\n\n  await Future.delayed(Duration(milliseconds: 1000));\n  progressBar.update(20); // Complete\n\n  await Future.delayed(Duration(milliseconds: 500));\n  stdout.writeln('\\n');\n}\n\n/// Example simulating a file download with changing total\nFuture<void> simulateDownloadExample() async {\n  stdout.writeln('5. Simulating Download (with changing total):');\n\n  final progressBar = ProgressBar(\n    format: '[{bar}] {percentage}% | {value}/{total} KB | {duration}s elapsed',\n  );\n\n  // Start with an initial estimate\n  progressBar.start(1000);\n\n  int value = 0;\n\n  // Simulate download with varying speed and changing total size\n  for (int i = 0; i < 20; i++) {\n    await Future.delayed(Duration(milliseconds: 200));\n\n    // Simulate downloaded chunk\n    final chunk = 50 + (math.Random().nextInt(50));\n    value += chunk;\n    progressBar.update(value);\n\n    // Occasionally update the total size (simulating better estimates as download progresses)\n    if (i == 5) {\n      progressBar.setTotal(1200);\n      stdout.writeln('\\n   (Download size updated to 1200 KB)');\n    } else if (i == 12) {\n      progressBar.setTotal(1500);\n      stdout.writeln('\\n   (Download size updated to 1500 KB)');\n    }\n  }\n\n  // Complete the download\n  progressBar.update(progressBar.total);\n\n  await Future.delayed(Duration(milliseconds: 500));\n  stdout.writeln('\\n');\n}\n\n/// Example showing multiple progress bars\nFuture<void> multipleProgressBarsExample() async {\n  stdout.writeln('6. Multiple Progress Bars:');\n  stdout.writeln('   (Three tasks running at different speeds)');\n\n  // Create three progress bars with different formats\n  final task1 = ProgressBar(\n    format: 'Task 1: [{bar}] {percentage}%',\n    barCompleteChar: '=',\n    barIncompleteChar: ' ',\n  );\n\n  final task2 = ProgressBar(\n    format: 'Task 2: [{bar}] {percentage}%',\n    barCompleteChar: '#',\n    barIncompleteChar: '-',\n  );\n\n  final task3 = ProgressBar(\n    format: 'Task 3: [{bar}] {percentage}%',\n    barCompleteChar: '■',\n    barIncompleteChar: '□',\n  );\n\n  // Start all tasks\n  task1.start(100);\n  stdout.writeln();\n  task2.start(100);\n  stdout.writeln();\n  task3.start(100);\n\n  // Update tasks at different speeds\n  for (int i = 0; i <= 100; i++) {\n    await Future.delayed(Duration(milliseconds: 30));\n\n    if (i <= 100) task1.update(i);\n    if (i <= 100 && i % 2 == 0) task2.update(i ~/ 2);\n    if (i <= 100 && i % 3 == 0) task3.update(i ~/ 3);\n  }\n\n  // Wait for task2 to complete\n  for (int i = 51; i <= 100; i++) {\n    await Future.delayed(Duration(milliseconds: 60));\n    task2.update(i);\n  }\n\n  // Wait for task3 to complete\n  for (int i = 34; i <= 100; i++) {\n    await Future.delayed(Duration(milliseconds: 90));\n    task3.update(i);\n  }\n\n  await Future.delayed(Duration(milliseconds: 500));\n  stdout.writeln('\\n');\n\n  // Make sure to dispose all progress bars\n  task1.dispose();\n  task2.dispose();\n  task3.dispose();\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/shell_uikit/example/spinner_example.dart",
    "content": "import 'dart:async';\nimport 'package:shell_uikit/shell_uikit.dart';\n\n/// This example demonstrates how to use various features of the Spinner component\nvoid main() async {\n  print('Spinner Component Examples\\n');\n\n  // Basic usage example\n  await basicExample();\n\n  // Show all animation types\n  await allSpinnerTypesExample();\n\n  // Demonstrate text updating functionality\n  await updateTextExample();\n\n  // Show different status messages\n  await statusMessagesExample();\n\n  print('\\nAll examples completed!');\n}\n\n/// Demonstrates basic usage of Spinner\nFuture<void> basicExample() async {\n  print('1. Basic Usage Example:');\n\n  final spinner = Spinner(text: 'Loading...');\n  spinner.start();\n\n  // Simulate some time-consuming operation\n  await Future.delayed(const Duration(seconds: 2));\n\n  spinner.success('Loading completed!');\n  await Future.delayed(const Duration(milliseconds: 500));\n}\n\n/// Shows all available Spinner animation types\nFuture<void> allSpinnerTypesExample() async {\n  print('\\n2. All Animation Types Example:');\n\n  final spinnerTypes = SpinnerType.values;\n\n  for (final type in spinnerTypes) {\n    final spinner = Spinner(\n      text: '${type.name} type animation',\n      spinnerType: type,\n    );\n\n    spinner.start();\n    await Future.delayed(const Duration(seconds: 2));\n    spinner.stop();\n\n    print('  - Displayed ${type.name} type');\n    await Future.delayed(const Duration(milliseconds: 300));\n  }\n}\n\n/// Demonstrates how to update text while Spinner is running\nFuture<void> updateTextExample() async {\n  print('\\n3. Text Update Example:');\n\n  final spinner = Spinner(text: 'Initial text');\n  spinner.start();\n\n  await Future.delayed(const Duration(seconds: 1));\n  spinner.updateText('Updated text 1');\n\n  await Future.delayed(const Duration(seconds: 1));\n  spinner.updateText('Updated text 2');\n\n  await Future.delayed(const Duration(seconds: 1));\n  spinner.updateText('Updated text 3');\n\n  await Future.delayed(const Duration(seconds: 1));\n  spinner.success('Text update example completed');\n  await Future.delayed(const Duration(milliseconds: 500));\n}\n\n/// Shows different status messages (success, error, info, warning)\nFuture<void> statusMessagesExample() async {\n  print('\\n4. Status Messages Example:');\n\n  // Success message\n  var spinner = Spinner(text: 'Processing success message');\n  spinner.start();\n  await Future.delayed(const Duration(seconds: 1));\n  spinner.success('Operation completed successfully');\n  await Future.delayed(const Duration(milliseconds: 500));\n\n  // Error message\n  spinner = Spinner(text: 'Processing error message');\n  spinner.start();\n  await Future.delayed(const Duration(seconds: 1));\n  spinner.error('Operation failed');\n  await Future.delayed(const Duration(milliseconds: 500));\n\n  // Info message\n  spinner = Spinner(text: 'Processing info message');\n  spinner.start();\n  await Future.delayed(const Duration(seconds: 1));\n  spinner.info('This is an information message');\n  await Future.delayed(const Duration(milliseconds: 500));\n\n  // Warning message\n  spinner = Spinner(text: 'Processing warning message');\n  spinner.start();\n  await Future.delayed(const Duration(seconds: 1));\n  spinner.warn('This is a warning message');\n  await Future.delayed(const Duration(milliseconds: 500));\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/shell_uikit/lib/shell_uikit.dart",
    "content": "library shell_uikit;\n\nexport 'src/progress_bar.dart';\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/shell_uikit/lib/src/progress_bar.dart",
    "content": "import 'dart:async';\nimport 'dart:io';\n\nclass ProgressBar {\n  ProgressBar({\n    required this.format,\n    this.barCompleteChar = '\\u2588',\n    this.barIncompleteChar = '\\u2591',\n  });\n\n  final String format;\n  final String barCompleteChar;\n  final String barIncompleteChar;\n  late int barSize = 40;\n\n  Timer? timer;\n\n  // the current bar value\n  int value = 0;\n\n  // the end value of the bar\n  int total = 100;\n\n  // start time (used for eta calculation)\n  DateTime? startTime;\n\n  // stop time (used for duration calculation)\n  DateTime? stopTime;\n\n  // progress bar active ?\n  bool isActive = false;\n\n  /// Starts the progress bar and set the total and initial value\n  void start(int totalValue, [int? startValue]) {\n    // set initial values\n    value = startValue ?? 0;\n    total = (totalValue >= 0) ? totalValue : 100;\n\n    // store start time for duration+eta calculation\n    startTime = DateTime.now();\n\n    // reset stop time for 're-start' scenario (used for duration calculation)\n    stopTime = null;\n\n    // set flag\n    isActive = true;\n\n    timer = Timer.periodic(const Duration(milliseconds: 10), (_) {\n      render();\n      if (!isActive && timer?.isActive == true) {\n        timer?.cancel();\n        timer = null;\n        stdout.writeln();\n      }\n    });\n  }\n\n  void update(int currentValue) {\n    if (value == currentValue) return;\n    value = currentValue;\n\n    if (currentValue >= total) {\n      stop();\n    }\n  }\n\n  /// Gets the total progress value.\n  int getTotal() {\n    return total;\n  }\n\n  /// Sets the total progress value while progressbar is active.\n  void setTotal(totalValue) {\n    if (totalValue >= 0) {\n      total = totalValue;\n    }\n  }\n\n  /// Stops the progress bar and go to next line\n  void stop() {\n    // set flag\n    isActive = false;\n\n    // store stop timestamp to get total duration\n    stopTime = DateTime.now();\n  }\n\n  void render() {\n    // calculate the bar complete size\n    int barCompleteSize = ((value / total) * barSize).toInt();\n\n    String bar =\n        '${barCompleteChar * barCompleteSize}${barIncompleteChar * (barSize - barCompleteSize)}';\n    String percentage = (value * 100 / total).toStringAsFixed(1);\n\n    stdout.write('\\r');\n    stdout.write(\n      format\n          .replaceAll('{bar}', bar)\n          .replaceAll('{percentage}', percentage)\n          .replaceAll('{value}', '$value')\n          .replaceAll('{total}', '$total'),\n    );\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/shell_uikit/lib/src/spinner.dart",
    "content": "import 'dart:async';\nimport 'dart:io';\n\n/// A command-line spinner component that shows an animation\n/// to indicate a loading or processing state.\nclass Spinner {\n  /// Creates a new spinner with the specified configuration.\n  ///\n  /// [text] is the message displayed next to the spinner.\n  /// [spinnerType] defines the animation style (default is 'dots').\n  Spinner({\n    String text = 'Loading',\n    this.spinnerType = SpinnerType.dots,\n    this.interval = const Duration(milliseconds: 80),\n  }) : _text = text;\n\n  /// The text displayed next to the spinner.\n  String _text;\n\n  /// Gets the current spinner text.\n  String get text => _text;\n\n  /// The type of spinner animation to display.\n  final SpinnerType spinnerType;\n\n  /// The interval between animation frames.\n  final Duration interval;\n\n  /// Timer that controls the animation.\n  Timer? _timer;\n\n  /// Current frame index in the animation sequence.\n  int _frameIndex = 0;\n\n  /// Whether the spinner is currently running.\n  bool get isRunning => _timer != null;\n\n  /// Starts the spinner animation.\n  void start() {\n    if (_timer != null) return;\n\n    // Record the start time\n    _frameIndex = 0;\n\n    // Clear the current line\n    stdout.write('\\r');\n\n    // Start the animation timer\n    _timer = Timer.periodic(interval, (_) {\n      _draw();\n    });\n  }\n\n  /// Stops the spinner animation.\n  void stop() {\n    _timer?.cancel();\n    _timer = null;\n\n    // Clear the spinner line\n    _clearLine();\n  }\n\n  /// Stops the spinner and shows a success message.\n  void success([String? message]) {\n    stop();\n    if (message != null) {\n      stdout.write('\\r✓ $message\\n');\n    }\n  }\n\n  /// Stops the spinner and shows an error message.\n  void error([String? message]) {\n    stop();\n    if (message != null) {\n      stdout.write('\\r✗ $message\\n');\n    }\n  }\n\n  /// Stops the spinner and shows an info message.\n  void info([String? message]) {\n    stop();\n    if (message != null) {\n      stdout.write('\\rℹ $message\\n');\n    }\n  }\n\n  /// Stops the spinner and shows a warning message.\n  void warn([String? message]) {\n    stop();\n    if (message != null) {\n      stdout.write('\\r⚠ $message\\n');\n    }\n  }\n\n  /// Updates the spinner text while it's running.\n  void updateText(String newText) {\n    if (_text != newText) {\n      _clearLine();\n      _text = newText;\n      if (isRunning) {\n        _draw();\n      }\n    }\n  }\n\n  /// Draws the current frame of the spinner animation.\n  void _draw() {\n    final frames = _getFrames();\n    final frame = frames[_frameIndex % frames.length];\n\n    // Clear the current line and write the new frame\n    stdout.write('\\r$frame $_text');\n\n    // Move to the next frame\n    _frameIndex++;\n  }\n\n  /// Clears the current line in the terminal.\n  void _clearLine() {\n    stdout.write('\\r${' ' * (_text.length + 10)}\\r');\n  }\n\n  /// Gets the animation frames based on the selected spinner type.\n  List<String> _getFrames() {\n    switch (spinnerType) {\n      case SpinnerType.dots:\n        return ['⠋', '⠙', '⠹', '⠸', '⠼', '⠴', '⠦', '⠧', '⠇', '⠏'];\n      case SpinnerType.line:\n        return ['-', '\\\\', '|', '/'];\n      case SpinnerType.growVertical:\n        return ['▁', '▃', '▄', '▅', '▆', '▇', '█', '▇', '▆', '▅', '▄', '▃'];\n      case SpinnerType.growHorizontal:\n        return ['▏', '▎', '▍', '▌', '▋', '▊', '▉', '█'];\n      case SpinnerType.circle:\n        return ['◜', '◠', '◝', '◞', '◡', '◟'];\n      case SpinnerType.dots2:\n        return ['.  ', '.. ', '...', ' ..', '  .', '   '];\n      case SpinnerType.bounce:\n        return ['⠁', '⠂', '⠄', '⠂'];\n      case SpinnerType.arrows:\n        return ['←', '↖', '↑', '↗', '→', '↘', '↓', '↙'];\n    }\n  }\n}\n\n/// Defines different spinner animation styles.\nenum SpinnerType {\n  /// Braille dots animation (default)\n  dots,\n\n  /// Simple line animation\n  line,\n\n  /// Growing vertical bar\n  growVertical,\n\n  /// Growing horizontal bar\n  growHorizontal,\n\n  /// Circle animation\n  circle,\n\n  /// Simple dots animation\n  dots2,\n\n  /// Bouncing animation\n  bounce,\n\n  /// Rotating arrows\n  arrows,\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/shell_uikit/pubspec.yaml",
    "content": "name: shell_uikit\ndescription: A simple shell ui kit.\nversion: 0.1.1\nhomepage: https://distributor.leanflutter.dev\nrepository: https://github.com/leanflutter/flutter_distributor/tree/main/packages/shell_uikit\n\nenvironment:\n  sdk: \">=2.16.0 <4.0.0\"\n\ndev_dependencies:\n  dependency_validator: ^3.0.0\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/unified_distributor/.gitignore",
    "content": ".dart_tool/\n.packages\nbuild/\npubspec.lock  # Except for application packages\n.flutter-plugins\n.flutter-plugins-dependencies\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/unified_distributor/LICENSE",
    "content": "MIT License\n\nCopyright (c) 2021-present LiJianying <lijy91@foxmail.com>\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": "plugins/flutter_distributor/packages/unified_distributor/analysis_options.yaml",
    "content": "include: package:mostly_reasonable_lints/analysis_options.yaml\n\nlinter:\n  rules:\n    avoid_print: false\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/unified_distributor/lib/src/check_version_result.dart",
    "content": "class CheckVersionResult {\n  CheckVersionResult({\n    this.latestVersion,\n    this.currentVersion,\n  });\n  final String? latestVersion;\n  final String? currentVersion;\n\n  /// Whether a new version is available.\n  bool get isNewVersionAvailable =>\n      currentVersion != null &&\n      latestVersion != null &&\n      currentVersion!.compareTo(latestVersion!) < 0;\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/unified_distributor/lib/src/cli/cli.dart",
    "content": "import 'package:args/args.dart';\nimport 'package:args/command_runner.dart';\nimport 'package:shell_uikit/shell_uikit.dart';\nimport 'package:unified_distributor/src/cli/command_package.dart';\nimport 'package:unified_distributor/src/cli/command_publish.dart';\nimport 'package:unified_distributor/src/cli/command_release.dart';\nimport 'package:unified_distributor/src/cli/command_upgrade.dart';\nimport 'package:unified_distributor/unified_distributor.dart';\n\nclass UnifiedDistributorCommandLineInterface {\n  UnifiedDistributorCommandLineInterface(\n    String executableName,\n    String description, {\n    String? packageName,\n    String? displayName,\n  }) {\n    _distributor = UnifiedDistributor(\n      packageName ?? executableName,\n      displayName ?? executableName,\n    );\n\n    if (packageName != 'fastforge') {\n      String note = [\n        '╔════════════════════════════════════════════════════════════════════════════╗',\n        '║ Important Notice: flutter_distributor has been renamed to fastforge.       ║',\n        '║ You can continue to use flutter_distributor, but we recommend migrating to ║',\n        '║ fastforge for the latest features and updates.                             ║',\n        '║                                                                            ║',\n        '║ Please visit https://fastforge.dev for more information.                   ║',\n        '╚════════════════════════════════════════════════════════════════════════════╝',\n      ].join('\\n').yellow(bold: true);\n      description = '$note\\n\\n$description';\n    }\n\n    _runner = CommandRunner(executableName, description);\n    _runner.addCommand(CommandPackage(_distributor));\n    _runner.addCommand(CommandPublish(_distributor));\n    _runner.addCommand(CommandRelease(_distributor));\n    _runner.addCommand(CommandUpgrade(_distributor));\n    _runner.argParser\n      ..addFlag(\n        'version',\n        help: 'Reports the version of this tool.',\n        negatable: false,\n      )\n      ..addFlag(\n        'version-check',\n        help: 'Check for updates when this command runs.',\n        defaultsTo: true,\n        negatable: true,\n      );\n  }\n\n  late final UnifiedDistributor _distributor;\n  late final CommandRunner _runner;\n\n  String get displayName => _distributor.displayName;\n  String get packageName => _distributor.packageName;\n\n  Future<void> run(List<String> args) async {\n    ArgResults argResults = _runner.parse(args);\n    if (argResults.wasParsed('version')) {\n      String? currentVersion = await _distributor.getCurrentVersion();\n      if (currentVersion != null) {\n        logger.info(currentVersion);\n        return;\n      }\n    }\n\n    if (argResults['version-check']) {\n      Spinner spinner = Spinner(text: 'Checking for updates...');\n      spinner.start();\n      // Check if a newer version of the tool is available\n      final result = await _distributor.checkVersion();\n      spinner.stop();\n      if (result.isNewVersionAvailable) {\n        String msg = [\n          '🚀 New version of $displayName available! '\n                  .brightYellow(bold: true) +\n              '${result.currentVersion}'.brightRed() +\n              ' → '.brightYellow() +\n              '${result.latestVersion}'.brightGreen(bold: true),\n          'Update with: '.brightYellow() +\n              '\"$packageName upgrade\"'.cyan(bold: true),\n        ].join('\\n');\n        logger.info(msg);\n      } else {\n        String msg = [\n          '🎉 You are using the latest version '.brightBlack() +\n              '(${result.currentVersion})'.brightBlack(bold: true),\n        ].join('\\n');\n        logger.info(msg);\n      }\n      logger.info('');\n    }\n    return _runner.runCommand(argResults);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/unified_distributor/lib/src/cli/command_package.dart",
    "content": "import 'dart:io';\n\nimport 'package:args/command_runner.dart';\nimport 'package:unified_distributor/src/extensions/string.dart';\nimport 'package:unified_distributor/src/unified_distributor.dart';\n\n/// Package an application bundle for a specific platform and target\n///\n/// This command wrapper defines, parses and transforms all passed arguments,\n/// so that they may be passed to `unified_distributor`. The distributor will\n/// then build an application bundle using `flutter_app_packager`.\nclass CommandPackage extends Command {\n  CommandPackage(this.distributor) {\n    argParser.addOption(\n      'platform',\n      valueHelp: [\n        'android',\n        'ios',\n        'linux',\n        'macos',\n        'ohos',\n        'windows',\n        'web',\n      ].join(','),\n      help: 'The platform to package the application for',\n    );\n\n    argParser.addOption(\n      'targets',\n      aliases: ['target'],\n      valueHelp: [\n        'apk',\n        'aab',\n        'app',\n        'appimage',\n        'deb',\n        'dmg',\n        'exe',\n        'hap',\n        'ipa',\n        'msix',\n        'pkg',\n        'rpm',\n        'zip',\n      ].join(','),\n      help: 'Comma separated list of bundle types to build.',\n    );\n\n    argParser.addOption('channel', valueHelp: '');\n    argParser.addOption('artifact-name', valueHelp: '');\n\n    argParser.addFlag(\n      'skip-clean',\n      help: 'Whether or not to skip \\'flutter clean\\' before packaging.',\n    );\n\n    argParser.addOption(\n      'flutter-build-args',\n      valueHelp: 'verbose,obfuscate',\n      help: 'Arguments to pass directly to flutter build',\n    );\n\n    argParser.addOption(\n      'build-target',\n      valueHelp: 'path',\n      help: 'The --target argument passed to \\'flutter build\\'',\n    );\n\n    argParser.addOption(\n      'build-flavor',\n      valueHelp: '',\n      help: 'The --flavor argument passed to \\'flutter build\\'',\n    );\n\n    argParser.addOption(\n      'build-target-platform',\n      valueHelp: '',\n      help: 'The --target-platform argument passed to \\'flutter build\\'',\n    );\n\n    argParser.addOption(\n      'build-export-options-plist',\n      valueHelp: '',\n      help: 'The --export-options-plist argument passed \\'flutter build\\'',\n    );\n\n    argParser.addMultiOption(\n      'build-dart-define',\n      valueHelp: 'foo=bar',\n      help: [\n        'The --dart-define argument(s) passed to \\'flutter build\\'',\n        'You may add multiple \\'--build-dart-define key=value\\' pairs',\n      ].join('\\n'),\n    );\n  }\n\n  final UnifiedDistributor distributor;\n\n  @override\n  String get name => 'package';\n\n  @override\n  String get description => [\n        'Package the current Flutter application for distribution',\n        '',\n        'Options prefixed with --build- are passed directly to \\'flutter build\\'',\n        'For more details on build options, refer to the \\'flutter build\\' documentation.',\n      ].join('\\n');\n\n  @override\n  Future run() async {\n    final String? platform = argResults?['platform'];\n    final List<String> targets = '${argResults?['targets'] ?? ''}'\n        .split(',')\n        .where((e) => e.isNotEmpty)\n        .toList();\n    final String? channel = argResults?['channel'];\n    final String? artifactName = argResults?['artifact-name'];\n    final String? flutterBuildArgs = argResults?['flutter-build-args'];\n    final bool isSkipClean = argResults?.wasParsed('skip-clean') ?? false;\n    final Map<String, dynamic> buildArguments =\n        _generateBuildArgs(flutterBuildArgs);\n\n    // At least `platform` and one `targets` is required for flutter build\n    if (platform == null) {\n      print('\\nThe \\'platform\\' options is mandatory!'.red(bold: true));\n      exit(1);\n    }\n\n    if (targets.isEmpty) {\n      print('\\nAt least one \\'target\\' must be specified!'.red(bold: true));\n      exit(1);\n    }\n\n    return distributor.package(\n      platform,\n      targets,\n      channel: channel,\n      artifactName: artifactName,\n      cleanBeforeBuild: !isSkipClean,\n      buildArguments: buildArguments,\n    );\n  }\n\n  Map<String, dynamic> _generateBuildArgs(String? flutterBuildArgs) {\n    Map<String, dynamic> buildArguments = {};\n\n    if (argResults?.options == null) return buildArguments;\n\n    for (var option in argResults!.options) {\n      if (!option.startsWith('build-')) continue;\n      dynamic value = argResults?[option];\n\n      if (value is List) {\n        // ignore: prefer_for_elements_to_map_fromiterable\n        value = Map.fromIterable(\n          value,\n          key: (e) => e.split('=')[0],\n          value: (e) => e.split('=')[1],\n        );\n      }\n\n      buildArguments.putIfAbsent(\n        option.replaceAll('build-', ''),\n        () => value,\n      );\n    }\n\n    for (var arg in flutterBuildArgs?.split(',') ?? <String>[]) {\n      if (arg.split('=').length == 2) {\n        buildArguments.putIfAbsent(\n          arg.split('=').first,\n          () => arg.split('=').last,\n        );\n      } else if (arg.split('=').length == 1) {\n        buildArguments.putIfAbsent(\n          arg.split('=')[0],\n          () => true,\n        );\n      } else {\n        buildArguments.putIfAbsent(arg, () => true);\n      }\n    }\n\n    return buildArguments;\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/unified_distributor/lib/src/cli/command_publish.dart",
    "content": "import 'dart:io';\n\nimport 'package:args/command_runner.dart';\nimport 'package:unified_distributor/src/extensions/string.dart';\nimport 'package:unified_distributor/src/unified_distributor.dart';\n\n/// Publish an application to a third party provider\n///\n/// This command wrapper defines, parses and transforms all passed arguments,\n/// so that they may be passed to `unified_distributor`. The distributor will\n/// then publish an application bundle using `flutter_app_publisher`.\nclass CommandPublish extends Command {\n  CommandPublish(this.distributor) {\n    argParser.addOption(\n      'path',\n      valueHelp: '',\n      help: 'The path to the application bundle to publish.',\n    );\n\n    argParser.addOption(\n      'targets',\n      aliases: ['target'],\n      valueHelp: [\n        'appcenter',\n        'appstore',\n        'fir',\n        'firebase',\n        'github',\n        'playstore',\n        'pgyer',\n        'qiniu',\n        'vercel',\n      ].join(','),\n      help: 'The target provider(s) to publish to.',\n    );\n\n    // AppCenter\n    argParser.addSeparator('appcenter');\n\n    argParser.addOption(\n      'appcenter-owner-name',\n      valueHelp: '',\n      help: 'The owner name for appcenter.',\n    );\n\n    argParser.addOption(\n      'appcenter-app-name',\n      valueHelp: '',\n      help: 'The app name for appcenter.',\n    );\n\n    argParser.addOption(\n      'appcenter-distribution-group',\n      valueHelp: '',\n      help: 'The distribution group for appcenter.',\n    );\n\n    // Firebase\n    argParser.addSeparator('firebase');\n\n    argParser.addOption(\n      'firebase-app',\n      valueHelp: '',\n      help: [\n        'The unique ID of the application on Firebase.',\n        'This is NOT your bundle identifier',\n      ].join('\\n'),\n    );\n\n    argParser.addOption(\n      'firebase-release-notes',\n      valueHelp: '',\n      help: 'The release notes for the published application.',\n    );\n\n    argParser.addOption(\n      'firebase-release-notes-file',\n      valueHelp: '',\n      help: [\n        'The path of a file containing the release notes',\n        'This is a more extensive alternative to firebase-release-notes',\n      ].join('\\n'),\n    );\n\n    argParser.addOption(\n      'firebase-testers',\n      valueHelp: '',\n      help:\n          'The testers that will be notified about the published application.',\n    );\n\n    argParser.addOption(\n      'firebase-testers-file',\n      valueHelp: '',\n      help: [\n        'The path of a file containing testers that will be notified',\n        'This is a more extensive alternative to firebase-testers',\n      ].join('\\n'),\n    );\n\n    argParser.addOption(\n      'firebase-groups',\n      valueHelp: '',\n      help: 'The groups that will be notified about the published application.',\n    );\n\n    argParser.addOption(\n      'firebase-groups-file',\n      valueHelp: '',\n      help: [\n        'The path of a file containing groups that will be notified',\n        'This is a more extensive alternative to firebase-groups',\n      ].join('\\n'),\n    );\n\n    // Firebase Hosting\n    argParser.addSeparator('firebase-hosting');\n    argParser.addOption('firebase-hosting-project-id', valueHelp: '');\n\n    // Github\n    argParser.addSeparator('github');\n\n    argParser.addOption(\n      'github-repo-owner',\n      valueHelp: '',\n      help: 'The name of the target GitHub repository wner (namespace)',\n    );\n\n    argParser.addOption(\n      'github-repo-name',\n      valueHelp: '',\n      help: 'The name of the target GitHub repository',\n    );\n\n    argParser.addOption(\n      'github-release-title',\n      valueHelp: '',\n      help: 'The title of the new release on GitHub',\n    );\n\n    // PlayStore\n    argParser.addSeparator('playstore');\n    argParser.addOption('playstore-package-name', valueHelp: '');\n    argParser.addOption('playstore-track', valueHelp: '');\n\n    // Qiniu\n    argParser.addSeparator('qiniu');\n    argParser.addOption('qiniu-bucket', valueHelp: '');\n    argParser.addOption('qiniu-bucket-domain', valueHelp: '');\n    argParser.addOption('qiniu-savekey-prefix', valueHelp: '');\n\n    // Vercel\n    argParser.addSeparator('vercel');\n    argParser.addOption('vercel-org-id', valueHelp: '');\n    argParser.addOption('vercel-project-id', valueHelp: '');\n  }\n\n  final UnifiedDistributor distributor;\n\n  @override\n  String get name => 'publish';\n\n  @override\n  String get description => [\n        'Publish the built Flutter application artifacts to distribution platforms',\n        '',\n        'This command uploads your application bundle to specified target providers',\n        'Use --targets to specify one or more distribution platforms',\n      ].join('\\n');\n\n  @override\n  Future run() async {\n    String? path = argResults?['path'];\n    List<String> targets = '${argResults?['targets'] ?? ''}'\n        .split(',')\n        .where((t) => t.isNotEmpty)\n        .toList();\n\n    // At least `path` and one `targets` is required for flutter build\n    if (path == null) {\n      print('\\nThe \\'path\\' options is mandatory!'.red(bold: true));\n      exit(1);\n    }\n\n    print(targets);\n    if (targets.isEmpty) {\n      print('\\nAt least one \\'target\\' must be specified!'.red(bold: true));\n      exit(1);\n    }\n\n    // Required parameters for firebase\n    if (targets.contains('firebase')) {\n      if (argResults?['firebase-app'] == null) {\n        print('\\nFirebase app identifier is required for target \\'firebase\\'');\n        exit(1);\n      }\n    }\n\n    Map<String, String?> publishArguments = {\n      'appcenter-owner-name': argResults?['appcenter-owner-name'],\n      'appcenter-app-name': argResults?['appcenter-app-name'],\n      'appcenter-distribution-group':\n          argResults?['appcenter-distribution-group'],\n      'firebase-app': argResults?['firebase-app'],\n      'firebase-release-notes': argResults?['firebase-release-notes'],\n      'firebase-release-notes-file': argResults?['firebase-release-notes-file'],\n      'firebase-testers': argResults?['firebase-testers'],\n      'firebase-testers-file': argResults?['firebase-testers-file'],\n      'firebase-groups': argResults?['firebase-groups'],\n      'firebase-groups-file': argResults?['firebase-groups-file'],\n      'firebase-hosting-project-id': argResults?['firebase-hosting-project-id'],\n      'github-repo-owner': argResults?['github-repo-owner'],\n      'github-repo-name': argResults?['github-repo-name'],\n      'github-release-title': argResults?['github-release-title'],\n      'playstore-package-name': argResults?['playstore-package-name'],\n      'playstore-track': argResults?['playstore-track'],\n      'qiniu-bucket': argResults?['qiniu-bucket'],\n      'qiniu-bucket-domain': argResults?['qiniu-bucket-domain'],\n      'qiniu-savekey-prefix': argResults?['qiniu-savekey-prefix'],\n      'vercel-org-id': argResults?['vercel-org-id'],\n      'vercel-project-id': argResults?['vercel-project-id'],\n    }..removeWhere((key, value) => value == null);\n\n    final fileSystemEntity =\n        await FileSystemEntity.type(path) == FileSystemEntityType.directory\n            ? Directory(path)\n            : File(path);\n\n    return distributor.publish(\n      fileSystemEntity,\n      targets,\n      publishArguments: publishArguments,\n    );\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/unified_distributor/lib/src/cli/command_release.dart",
    "content": "import 'dart:io';\n\nimport 'package:args/command_runner.dart';\nimport 'package:unified_distributor/src/extensions/string.dart';\nimport 'package:unified_distributor/src/unified_distributor.dart';\n\n/// Release (package and publish) an application based on the config\n///\n/// This command wrapper defines, parses and transforms all passed arguments,\n/// so that they may be passed to `unified_distributor`. The distributor will\n/// then use the `distribute_options.yaml` file in the Flutter project root\n/// to run a release with one or multiple release jobs.\n///\n/// Each release job will package and optionally also publish the application\n/// based on the configuration on `distribute_options.yaml`.\nclass CommandRelease extends Command {\n  CommandRelease(this.distributor) {\n    argParser.addOption(\n      'name',\n      valueHelp: '',\n      help: 'The name of the release to run.',\n    );\n\n    argParser.addOption(\n      'jobs',\n      valueHelp: '',\n      help: 'Comma-separated list of jobs to run for the specified release.',\n    );\n\n    argParser.addOption(\n      'skip-jobs',\n      valueHelp: '',\n      help: 'Comma-separated list of jobs to skip for the specified release.',\n    );\n\n    argParser.addFlag(\n      'skip-clean',\n      help: 'Whether or not to skip \\'flutter clean\\' before packaging.',\n    );\n  }\n\n  final UnifiedDistributor distributor;\n\n  @override\n  String get name => 'release';\n\n  @override\n  String get description => 'Release the current Flutter application';\n\n  @override\n  Future run() async {\n    String? name = argResults?['name'] ?? '';\n    List<String> jobNameList = (argResults?['jobs'] ?? '')\n        .split(',')\n        .where((String e) => e.isNotEmpty)\n        .toList();\n    List<String> skipJobNameList = (argResults?['skip-jobs'] ?? '')\n        .split(',')\n        .where((String e) => e.isNotEmpty)\n        .toList();\n    bool isSkipClean = argResults?.wasParsed('skip-clean') ?? false;\n\n    // At least `name` must be passed to select a release\n    if (name == null) {\n      print('\\nThe \\'name\\' options is mandatory!'.red(bold: true));\n      exit(1);\n    }\n\n    return distributor.release(\n      name,\n      jobNameList: jobNameList,\n      skipJobNameList: skipJobNameList,\n      cleanBeforeBuild: !isSkipClean,\n    );\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/unified_distributor/lib/src/cli/command_upgrade.dart",
    "content": "import 'package:args/command_runner.dart';\nimport 'package:unified_distributor/src/unified_distributor.dart';\n\n/// Upgrade unified_distributor to the latest version\nclass CommandUpgrade extends Command {\n  CommandUpgrade(this.distributor);\n\n  final UnifiedDistributor distributor;\n\n  @override\n  String get name => 'upgrade';\n\n  @override\n  String get description =>\n      'Update ${distributor.displayName} to the latest version.';\n\n  @override\n  Future run() async {\n    await distributor.upgrade();\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/unified_distributor/lib/src/distribute_options.dart",
    "content": "import 'dart:io';\n\nimport 'package:unified_distributor/src/release.dart';\n\nclass DistributeOptions {\n  DistributeOptions({\n    required this.output,\n    this.variables,\n    this.artifactName,\n    required this.releases,\n  });\n\n  factory DistributeOptions.fromJson(Map<String, dynamic> json) {\n    Map<String, String> variables = {};\n    if (json.containsKey('variables') && json['variables'] != null) {\n      variables = Map<String, String>.from(json['variables']);\n      // 兼容老版本\n    } else if (json.containsKey('env') && json['env'] != null) {\n      variables = Map<String, String>.from(json['env']);\n    }\n    List<Release> releases = ((json['releases'] ?? []) as List)\n        .map((item) => Release.fromJson(item))\n        .toList();\n    return DistributeOptions(\n      output: json['output'],\n      variables: variables,\n      artifactName: json['artifact_name'],\n      releases: releases,\n    );\n  }\n\n  final String output;\n  final Map<String, String>? variables;\n  final String? artifactName;\n  final List<Release> releases;\n\n  Directory get outputDirectory => Directory(output);\n\n  Map<String, dynamic> toJson() {\n    return {\n      'output': output,\n      'variables': variables,\n      'artifact_name': artifactName,\n      'releases': releases.map((e) => e.toJson()).toList(),\n    }..removeWhere((key, value) => value == null);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/unified_distributor/lib/src/extensions/string.dart",
    "content": "import 'package:ansicolor/ansicolor.dart';\n\nAnsiPen _ansiPen = AnsiPen();\n\nconst ansiResetForeground = '${ansiEscape}39m';\n\nextension StringExt on String {\n  String _applyColor(int color, {bool bg = false, bool bold = false}) {\n    String appliedColor = (_ansiPen..xterm(color, bg: bg))(this);\n    if (bold) {\n      return '${ansiEscape}1m$appliedColor$ansiDefault';\n    }\n    return appliedColor;\n  }\n\n  String black({bool bg = false, bool bold = false}) {\n    return _applyColor(0, bg: bg, bold: bold);\n  }\n\n  String red({bool bg = false, bool bold = false}) {\n    return _applyColor(1, bg: bg, bold: bold);\n  }\n\n  String green({bool bg = false, bool bold = false}) {\n    return _applyColor(2, bg: bg, bold: bold);\n  }\n\n  String yellow({bool bg = false, bool bold = false}) {\n    return _applyColor(3, bg: bg, bold: bold);\n  }\n\n  String blue({bool bg = false, bool bold = false}) {\n    return _applyColor(4, bg: bg, bold: bold);\n  }\n\n  String magenta({bool bg = false, bool bold = false}) {\n    return _applyColor(5, bg: bg, bold: bold);\n  }\n\n  String cyan({bool bg = false, bool bold = false}) {\n    return _applyColor(6, bg: bg, bold: bold);\n  }\n\n  String white({bool bg = false, bool bold = false}) {\n    return _applyColor(7, bg: bg, bold: bold);\n  }\n\n  String brightBlack({bool bg = false, bool bold = false}) {\n    return _applyColor(8, bg: bg, bold: bold);\n  }\n\n  String brightRed({bool bg = false, bool bold = false}) {\n    return _applyColor(9, bg: bg, bold: bold);\n  }\n\n  String brightGreen({bool bg = false, bool bold = false}) {\n    return _applyColor(10, bg: bg, bold: bold);\n  }\n\n  String brightYellow({bool bg = false, bool bold = false}) {\n    return _applyColor(11, bg: bg, bold: bold);\n  }\n\n  String brightBlue({bool bg = false, bool bold = false}) {\n    return _applyColor(12, bg: bg, bold: bold);\n  }\n\n  String brightMagenta({bool bg = false, bool bold = false}) {\n    return _applyColor(13, bg: bg, bold: bold);\n  }\n\n  String brightCyan({bool bg = false, bool bold = false}) {\n    return _applyColor(14, bg: bg, bold: bold);\n  }\n\n  String brightWhite({bool bg = false, bool bold = false}) {\n    return _applyColor(15, bg: bg, bold: bold);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/unified_distributor/lib/src/release.dart",
    "content": "import 'package:unified_distributor/src/release_job.dart';\n\nclass Release {\n  Release({\n    this.variables,\n    required this.name,\n    required this.jobs,\n  });\n\n  factory Release.fromJson(Map<String, dynamic> json) {\n    Map<String, String> variables = {};\n    if (json.containsKey('variables') && json['variables'] != null) {\n      variables = Map<String, String>.from(json['variables']);\n    }\n    List<ReleaseJob> jobs = (json['jobs'] as List? ?? [])\n        .map((item) => ReleaseJob.fromJson(item))\n        .toList();\n    return Release(\n      variables: variables,\n      name: json['name'],\n      jobs: jobs,\n    );\n  }\n\n  final Map<String, String>? variables;\n  final String name;\n  final List<ReleaseJob> jobs;\n\n  Map<String, dynamic> toJson() {\n    return {\n      'variables': variables,\n      'name': name,\n      'jobs': jobs.map((e) => e.toJson()).toList(),\n    }..removeWhere((key, value) => value == null);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/unified_distributor/lib/src/release_job.dart",
    "content": "class ReleaseJobPackage {\n  ReleaseJobPackage({\n    required this.platform,\n    required this.target,\n    this.channel,\n    this.buildArgs,\n  });\n\n  factory ReleaseJobPackage.fromJson(Map<String, dynamic> json) {\n    return ReleaseJobPackage(\n      platform: json['platform'],\n      target: json['target'],\n      channel: json['channel'],\n      buildArgs: json['build_args'],\n    );\n  }\n\n  final String platform;\n  final String target;\n  final String? channel;\n  final Map<String, dynamic>? buildArgs;\n\n  Map<String, dynamic> toJson() {\n    return {\n      'platform': platform,\n      'target': target,\n      'channel': channel,\n      'build_args': buildArgs,\n    }..removeWhere((key, value) => value == null);\n  }\n}\n\nclass ReleaseJobPublish {\n  ReleaseJobPublish({\n    required this.target,\n    this.args,\n  });\n\n  factory ReleaseJobPublish.fromJson(Map<String, dynamic> json) {\n    return ReleaseJobPublish(\n      target: json['target'],\n      args: json['args'],\n    );\n  }\n  final String target;\n  final Map<String, dynamic>? args;\n\n  Map<String, dynamic> toJson() {\n    return {\n      'target': target,\n      'args': args,\n    }..removeWhere((key, value) => value == null);\n  }\n}\n\nclass ReleaseJob {\n  ReleaseJob({\n    this.variables,\n    required this.name,\n    required this.package,\n    this.publish,\n    this.publishTo,\n  });\n\n  factory ReleaseJob.fromJson(Map<String, dynamic> json) {\n    Map<String, String> variables = {};\n    if (json.containsKey('variables') && json['variables'] != null) {\n      variables = Map<String, String>.from(json['variables']);\n    }\n    return ReleaseJob(\n      variables: variables,\n      name: json['name'],\n      package: ReleaseJobPackage.fromJson(json['package']),\n      publish: json['publish'] != null\n          ? ReleaseJobPublish.fromJson(json['publish'])\n          : null,\n      publishTo: json['publish_to'],\n    );\n  }\n\n  final Map<String, String>? variables;\n  final String name;\n  final ReleaseJobPackage package;\n  final ReleaseJobPublish? publish;\n  final String? publishTo;\n\n  Map<String, dynamic> toJson() {\n    return {\n      'variables': variables,\n      'name': name,\n      'package': package.toJson(),\n      'publish': publish?.toJson(),\n      'publish_to': publishTo,\n    }..removeWhere((key, value) => value == null);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/unified_distributor/lib/src/unified_distributor.dart",
    "content": "import 'dart:convert';\nimport 'dart:io';\n\nimport 'package:flutter_app_builder/flutter_app_builder.dart';\nimport 'package:flutter_app_packager/flutter_app_packager.dart';\nimport 'package:flutter_app_publisher/flutter_app_publisher.dart';\nimport 'package:path/path.dart' as p;\nimport 'package:pubspec_parse/pubspec_parse.dart';\nimport 'package:shell_executor/shell_executor.dart';\nimport 'package:shell_uikit/shell_uikit.dart';\nimport 'package:unified_distributor/src/check_version_result.dart';\nimport 'package:unified_distributor/src/distribute_options.dart';\nimport 'package:unified_distributor/src/extensions/string.dart';\nimport 'package:unified_distributor/src/release.dart';\nimport 'package:unified_distributor/src/release_job.dart';\nimport 'package:unified_distributor/src/utils/default_shell_executor.dart';\nimport 'package:unified_distributor/src/utils/logger.dart';\nimport 'package:unified_distributor/src/utils/pub_dev_api.dart';\nimport 'package:yaml/yaml.dart';\n\n/// A class that provides a unified interface for distributing applications\n/// across different platforms.\nclass UnifiedDistributor {\n  /// Creates a new instance of the [UnifiedDistributor] class.\n  ///\n  /// The [packageName] parameter is the name of the package to be distributed.\n  /// The [displayName] parameter is the display name of the package.\n  UnifiedDistributor(\n    this.packageName,\n    this.displayName,\n  ) {\n    ShellExecutor.global = DefaultShellExecutor();\n  }\n\n  /// The name of the package\n  final String packageName;\n\n  /// The display name of the package\n  final String displayName;\n\n  final FlutterAppBuilder _builder = FlutterAppBuilder();\n  final FlutterAppPackager _packager = FlutterAppPackager();\n  final FlutterAppPublisher _publisher = FlutterAppPublisher();\n\n  Pubspec? _pubspec;\n  Pubspec get pubspec {\n    if (_pubspec == null) {\n      final yamlString = File('pubspec.yaml').readAsStringSync();\n      _pubspec = Pubspec.parse(yamlString);\n    }\n    return _pubspec!;\n  }\n\n  final Map<String, String> _globalVariables = {};\n  Map<String, String> get globalVariables {\n    if (_globalVariables.keys.isEmpty) {\n      for (String key in Platform.environment.keys) {\n        String? value = Platform.environment[key];\n        if ((value ?? '').isNotEmpty) {\n          _globalVariables[key] = value!;\n        }\n      }\n      List<String> keys = distributeOptions.variables?.keys.toList() ?? [];\n      for (String key in keys) {\n        String? value = distributeOptions.variables?[key];\n        if ((value ?? '').isNotEmpty) {\n          _globalVariables[key] = value!;\n        }\n      }\n    }\n    return _globalVariables;\n  }\n\n  DistributeOptions? _distributeOptions;\n\n  /// Get the distribute options\n  DistributeOptions get distributeOptions {\n    if (_distributeOptions == null) {\n      File file = File('distribute_options.yaml');\n      if (file.existsSync()) {\n        final yamlString = File('distribute_options.yaml').readAsStringSync();\n        final yamlDoc = loadYaml(yamlString);\n        _distributeOptions = DistributeOptions.fromJson(\n          json.decode(json.encode(yamlDoc)),\n        );\n      } else {\n        _distributeOptions = DistributeOptions(\n          output: 'dist/',\n          releases: [],\n        );\n      }\n    }\n    return _distributeOptions!;\n  }\n\n  Future<String?> _getCurrentVersion() async {\n    try {\n      var scriptFile = Platform.script.toFilePath();\n      var pathToPubSpecYaml = p.join(p.dirname(scriptFile), '../pubspec.yaml');\n      var pathToPubSpecLock = p.join(p.dirname(scriptFile), '../pubspec.lock');\n\n      var pubSpecYamlFile = File(pathToPubSpecYaml);\n\n      var pubSpecLockFile = File(pathToPubSpecLock);\n\n      if (pubSpecLockFile.existsSync()) {\n        var yamlDoc = loadYaml(await pubSpecLockFile.readAsString());\n        if (yamlDoc['packages'][packageName] == null) {\n          var yamlDoc = loadYaml(await pubSpecYamlFile.readAsString());\n          return yamlDoc['version'];\n        }\n\n        return yamlDoc['packages'][packageName]['version'];\n      }\n    } catch (_) {}\n    return null;\n  }\n\n  /// Check the version of the package\n  ///\n  /// This method checks the version of the package against the latest version\n  /// available on pub.dev.\n  Future<CheckVersionResult> checkVersion() async {\n    String? currentVersion = await _getCurrentVersion();\n    String? latestVersion =\n        await PubDevApi.getLatestVersionFromPackage(packageName);\n    return CheckVersionResult(\n      currentVersion: currentVersion,\n      latestVersion: latestVersion,\n    );\n  }\n\n  /// Get the current version of the package\n  Future<String?> getCurrentVersion() async {\n    return await _getCurrentVersion();\n  }\n\n  /// Package an application for a specific platform and target\n  Future<List<MakeResult>> package(\n    String platform,\n    List<String> targets, {\n    String? channel,\n    String? artifactName,\n    required bool cleanBeforeBuild,\n    required Map<String, dynamic> buildArguments,\n    Map<String, String>? variables,\n  }) async {\n    List<MakeResult> makeResultList = [];\n\n    try {\n      Directory outputDirectory = distributeOptions.outputDirectory;\n      if (!outputDirectory.existsSync()) {\n        outputDirectory.createSync(recursive: true);\n      }\n\n      if (cleanBeforeBuild) {\n        await _builder.clean();\n      }\n\n      bool isBuildOnlyOnce = platform != 'android';\n      BuildResult? buildResult;\n\n      for (String target in targets) {\n        logger.info('Packaging ${pubspec.name} ${pubspec.version} as $target:');\n        if (!isBuildOnlyOnce || (isBuildOnlyOnce && buildResult == null)) {\n          try {\n            buildResult = await _builder.build(\n              platform,\n              target: target,\n              arguments: buildArguments,\n              environment: variables ?? globalVariables,\n            );\n            print(\n              const JsonEncoder.withIndent('  ').convert(buildResult.toJson()),\n            );\n            logger.info(\n              'Successfully built ${buildResult.outputDirectory} in ${buildResult.duration!.inSeconds}s'\n                  .brightGreen(),\n            );\n          } on UnsupportedError catch (error) {\n            logger.warning('Warning: ${error.message}'.yellow());\n            continue;\n          } catch (error) {\n            rethrow;\n          }\n        }\n\n        if (buildResult != null) {\n          String buildMode =\n              buildArguments.containsKey('profile') ? 'profile' : 'release';\n          Map<String, dynamic>? arguments = {\n            'build_mode': buildMode,\n            'flavor': buildArguments['flavor'],\n            'channel': channel,\n            'artifact_name': artifactName,\n          };\n          MakeResult makeResult = await _packager.package(\n            platform,\n            target,\n            arguments,\n            outputDirectory,\n            buildOutputDirectory: buildResult.outputDirectory,\n            buildOutputFiles: buildResult.outputFiles,\n          );\n          print(\n            const JsonEncoder.withIndent('  ').convert(makeResult.toJson()),\n          );\n          FileSystemEntity artifact = makeResult.artifacts.first;\n          logger.info(\n            'Successfully packaged ${artifact.path}'.brightGreen(),\n          );\n          makeResultList.add(makeResult);\n        }\n      }\n    } catch (error) {\n      logger.severe(error.toString().red());\n      if (error is Error) {\n        logger.severe(error.stackTrace.toString().red());\n      }\n      rethrow;\n    }\n\n    return makeResultList;\n  }\n\n  /// Publish an application to a third party provider\n  ///\n  /// This method publishes an application to a third party provider.\n  Future<List<PublishResult>> publish(\n    FileSystemEntity fileSystemEntity,\n    List<String> targets, {\n    Map<String, dynamic>? publishArguments,\n    Map<String, String>? variables,\n  }) async {\n    List<PublishResult> publishResultList = [];\n    try {\n      for (String target in targets) {\n        ProgressBar progressBar = ProgressBar(\n          format: 'Publishing to $target: {bar} {value}/{total} {percentage}%',\n        );\n\n        Map<String, dynamic>? newPublishArguments = {};\n\n        if (publishArguments != null) {\n          for (var key in publishArguments.keys) {\n            if (!key.startsWith('$target-')) continue;\n            dynamic value = publishArguments[key];\n\n            if (value is List) {\n              // ignore: prefer_for_elements_to_map_fromiterable\n              value = Map.fromIterable(\n                value,\n                key: (e) => e.split('=')[0],\n                value: (e) => e.split('=')[1],\n              );\n            }\n\n            newPublishArguments.putIfAbsent(\n              key.replaceAll('$target-', ''),\n              () => value,\n            );\n          }\n        }\n\n        if (newPublishArguments.keys.isEmpty) {\n          newPublishArguments = publishArguments;\n        }\n\n        PublishResult publishResult = await _publisher.publish(\n          fileSystemEntity,\n          target: target,\n          environment: variables ?? globalVariables,\n          publishArguments: newPublishArguments,\n          onPublishProgress: (sent, total) {\n            if (!progressBar.isActive) {\n              progressBar.start(total, sent);\n            } else {\n              progressBar.update(sent);\n            }\n          },\n        );\n        if (progressBar.isActive) progressBar.stop();\n        logger.info(\n          'Successfully published ${publishResult.url}'.brightGreen(),\n        );\n        publishResultList.add(publishResult);\n      }\n    } on Error catch (error) {\n      logger.severe(error.toString().brightRed());\n      logger.severe(error.stackTrace.toString().brightRed());\n      rethrow;\n    }\n    return publishResultList;\n  }\n\n  /// Release an application to a third party provider\n  ///\n  /// This method releases an application to a third party provider.\n  ///\n  /// The [name] parameter is the name of the release to be released.\n  /// The [jobNameList] parameter is the list of job names to be released.\n  /// The [skipJobNameList] parameter is the list of job names to be skipped.\n  /// The [cleanBeforeBuild] parameter is a boolean that indicates whether to clean the build directory before building.\n  Future<void> release(\n    String name, {\n    required List<String> jobNameList,\n    required List<String> skipJobNameList,\n    required bool cleanBeforeBuild,\n  }) async {\n    final time = Stopwatch()..start();\n\n    try {\n      Directory outputDirectory = distributeOptions.outputDirectory;\n      if (!outputDirectory.existsSync()) {\n        outputDirectory.createSync(recursive: true);\n      }\n\n      List<Release> releases = distributeOptions.releases;\n\n      if (name.isNotEmpty) {\n        releases =\n            distributeOptions.releases.where((e) => e.name == name).toList();\n      }\n\n      if (releases.isEmpty) {\n        throw Exception('Missing/incomplete `distribute_options.yaml` file.');\n      }\n\n      for (Release release in releases) {\n        List<ReleaseJob> filteredJobs = release.jobs.where((e) {\n          if (jobNameList.isNotEmpty) {\n            return jobNameList.contains(e.name);\n          }\n          if (skipJobNameList.isNotEmpty) {\n            return !skipJobNameList.contains(e.name);\n          }\n          return true;\n        }).toList();\n        if (filteredJobs.isEmpty) {\n          throw Exception('No available jobs found in ${release.name}.');\n        }\n\n        bool needCleanBeforeBuild = cleanBeforeBuild;\n\n        for (ReleaseJob job in filteredJobs) {\n          logger.info('');\n          logger.info(\n            '${'===>'.blue()} ${'Releasing'.white(bold: true)} $name:${job.name.green(bold: true)}',\n          );\n\n          Map<String, String> variables = {}\n            ..addAll(globalVariables)\n            ..addAll(release.variables ?? {})\n            ..addAll(job.variables ?? {});\n\n          List<MakeResult> makeResultList = await package(\n            job.package.platform,\n            [job.package.target],\n            channel: job.package.channel,\n            artifactName: distributeOptions.artifactName,\n            cleanBeforeBuild: needCleanBeforeBuild,\n            buildArguments: job.package.buildArgs ?? {},\n            variables: variables,\n          );\n          // Clean only once\n          needCleanBeforeBuild = false;\n\n          if (job.publish != null || job.publishTo != null) {\n            String? publishTarget = job.publishTo ?? job.publish?.target;\n            MakeResult makeResult = makeResultList.first;\n            FileSystemEntity artifact = makeResult.artifacts.first;\n            await publish(\n              artifact,\n              [publishTarget!],\n              publishArguments: job.publish?.args,\n              variables: variables,\n            );\n          }\n        }\n      }\n\n      time.stop();\n      logger.info('');\n      logger.info(\n        'RELEASE SUCCESSFUL in ${time.elapsed.inSeconds}s'.green(bold: true),\n      );\n    } catch (error, stacktrace) {\n      time.stop();\n      logger.info('');\n      logger.severe(\n        [\n          'RELEASE FAILED in ${time.elapsed.inSeconds}s'.red(bold: true),\n          error.toString().red(),\n          stacktrace,\n        ].join('\\n'),\n      );\n      rethrow;\n    }\n    return Future.value();\n  }\n\n  /// Upgrade the package to the latest version\n  Future<void> upgrade() async {\n    await $(\n      'dart',\n      ['pub', 'global', 'activate', packageName],\n    );\n    return Future.value();\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/unified_distributor/lib/src/utils/default_shell_executor.dart",
    "content": "import 'dart:convert';\nimport 'dart:io';\n\nimport 'package:charset/charset.dart';\nimport 'package:shell_executor/shell_executor.dart';\nimport 'package:unified_distributor/src/extensions/string.dart';\nimport 'package:unified_distributor/src/utils/logger.dart';\n\n/// Convert bytes to string (UTF-8 or detected charset)\nString convertToString(List<int> bytes) {\n  if (Platform.isWindows) {\n    final charset = Charset.detect(bytes);\n    if (charset != null) {\n      return charset.decode(bytes);\n    }\n  }\n  return utf8.decode(bytes, allowMalformed: true);\n}\n\nclass DefaultShellExecutor extends ShellExecutor {\n  @override\n  Future<ProcessResult> exec(\n    String executable,\n    List<String> arguments, {\n    String? workingDirectory,\n    Map<String, String>? environment,\n  }) async {\n    final Process process = await Process.start(\n      executable,\n      arguments,\n      workingDirectory: workingDirectory,\n      environment: environment,\n      runInShell: true,\n    );\n\n    logger.info('\\$ $executable ${arguments.join(' ')}'.brightBlack());\n\n    String? stdoutStr;\n    String? stderrStr;\n\n    process.stdout.listen((data) {\n      String msg = convertToString(data);\n      stdoutStr = '${stdoutStr ?? ''}$msg';\n      stdout.write(msg.brightBlack());\n    });\n    process.stderr.listen((data) {\n      String msg = convertToString(data);\n      stderrStr = '${stderrStr ?? ''}$msg';\n      stderr.write(msg.brightRed());\n    });\n    int exitCode = await process.exitCode;\n    return ProcessResult(process.pid, exitCode, stdoutStr, stderrStr);\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/unified_distributor/lib/src/utils/logger.dart",
    "content": "import 'package:logging/logging.dart';\n\nLogger logger = Logger('unified_distributor')\n  ..onRecord.listen((record) {\n    print(record.message);\n  });\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/unified_distributor/lib/src/utils/pub_dev_api.dart",
    "content": "import 'dart:convert';\nimport 'dart:io';\n\nimport 'package:dio/dio.dart';\n\nclass PubDevApi {\n  static Future<String?> getLatestVersionFromPackage(String package) async {\n    String pubHostedUrl = Platform.environment['PUB_HOSTED_URL'] ?? '';\n    final pubSite = pubHostedUrl.isNotEmpty\n        ? '$pubHostedUrl/api/packages/$package'\n        : 'https://pub.dev/api/packages/$package';\n    final uri = Uri.parse(pubSite);\n    try {\n      final response = await Dio().get(uri.toString());\n      Map<String, dynamic> data = {};\n      if (response.data is String) {\n        data = json.decode(response.data);\n      } else {\n        data = response.data;\n      }\n      if (data['latest'] == null) return null;\n      return data['latest']['version'] as String?;\n    } catch (error) {\n      rethrow;\n    }\n  }\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/unified_distributor/lib/unified_distributor.dart",
    "content": "library unified_distributor;\n\nexport 'src/check_version_result.dart';\nexport 'src/cli/cli.dart';\nexport 'src/distribute_options.dart';\nexport 'src/extensions/string.dart';\nexport 'src/unified_distributor.dart';\nexport 'src/utils/default_shell_executor.dart';\nexport 'src/utils/logger.dart';\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/unified_distributor/pubspec.yaml",
    "content": "name: unified_distributor\ndescription: A powerful and efficient tool for packaging and publishing your applications with ease.\nversion: 0.2.0\nhomepage: https://fastforge.dev\nrepository: https://github.com/fastforgedev/fastforge/tree/main/packages/unified_distributor\nissue_tracker: https://github.com/fastforgedev/fastforge/issues\n\nplatforms:\n  linux:\n  macos:\n  windows:\n\nenvironment:\n  sdk: \">=2.16.0 <4.0.0\"\n\ndependencies:\n  ansicolor: ^2.0.1\n  args: ^2.2.0\n  charset: ^2.0.1\n  dio: ^5.3.4\n  flutter_app_builder: ^0.6.0\n  flutter_app_packager: ^0.6.0\n  flutter_app_publisher: ^0.6.0\n  logging: ^1.0.2\n  path: ^1.8.1\n  pubspec_parse: ^1.1.0\n  shell_executor: ^0.3.0\n  shell_uikit: ^0.3.0\n  yaml: ^3.1.0\n\ndev_dependencies:\n  dependency_validator: ^3.0.0\n  mostly_reasonable_lints: ^0.1.2\n  test: ^1.23.1\n"
  },
  {
    "path": "plugins/flutter_distributor/packages/unified_distributor/test/src/extensions/string_test.dart",
    "content": "import 'package:test/test.dart';\nimport 'package:unified_distributor/src/extensions/string.dart';\n\nvoid main() {\n  // print('black        '.black());\n  // print('red          '.red());\n  // print('green        '.green());\n  // print('yellow       '.yellow());\n  // print('blue         '.blue());\n  // print('magenta      '.magenta());\n  // print('cyan         '.cyan());\n  // print('white        '.white());\n  // print('brightBlack  '.brightBlack());\n  // print('brightRed    '.brightRed());\n  // print('brightGreen  '.brightGreen());\n  // print('brightYellow '.brightYellow());\n  // print('brightBlue   '.brightBlue());\n  // print('brightMagenta'.brightMagenta());\n  // print('BrightCyan   '.brightCyan());\n  // print('brightWhite  '.brightWhite());\n\n  // print('black        '.black(bold: true));\n  // print('red          '.red(bold: true));\n  // print('green        '.green(bold: true));\n  // print('yellow       '.yellow(bold: true));\n  // print('blue         '.blue(bold: true));\n  // print('magenta      '.magenta(bold: true));\n  // print('cyan         '.cyan(bold: true));\n  // print('white        '.white(bold: true));\n  // print('brightBlack  '.brightBlack(bold: true));\n  // print('brightRed    '.brightRed(bold: true));\n  // print('brightGreen  '.brightGreen(bold: true));\n  // print('brightYellow '.brightYellow(bold: true));\n  // print('brightBlue   '.brightBlue(bold: true));\n  // print('brightMagenta'.brightMagenta(bold: true));\n  // print('BrightCyan   '.brightCyan(bold: true));\n  // print('brightWhite  '.brightWhite(bold: true));\n\n  // print('black        '.black(bg: true));\n  // print('red          '.red(bg: true));\n  // print('green        '.green(bg: true));\n  // print('yellow       '.yellow(bg: true));\n  // print('blue         '.blue(bg: true));\n  // print('magenta      '.magenta(bg: true));\n  // print('cyan         '.cyan(bg: true));\n  // print('white        '.white(bg: true));\n  // print('brightBlack  '.brightBlack(bg: true));\n  // print('brightRed    '.brightRed(bg: true));\n  // print('brightGreen  '.brightGreen(bg: true));\n  // print('brightYellow '.brightYellow(bg: true));\n  // print('brightBlue   '.brightBlue(bg: true));\n  // print('brightMagenta'.brightMagenta(bg: true));\n  // print('BrightCyan   '.brightCyan(bg: true));\n  // print('brightWhite  '.brightWhite(bg: true));\n\n  // print('black        '.black(bold: true, bg: true));\n  // print('red          '.red(bold: true, bg: true));\n  // print('green        '.green(bold: true, bg: true));\n  // print('yellow       '.yellow(bold: true, bg: true));\n  // print('blue         '.blue(bold: true, bg: true));\n  // print('magenta      '.magenta(bold: true, bg: true));\n  // print('cyan         '.cyan(bold: true, bg: true));\n  // print('white        '.white(bold: true, bg: true));\n  // print('brightBlack  '.brightBlack(bold: true, bg: true));\n  // print('brightRed    '.brightRed(bold: true, bg: true));\n  // print('brightGreen  '.brightGreen(bold: true, bg: true));\n  // print('brightYellow '.brightYellow(bold: true, bg: true));\n  // print('brightBlue   '.brightBlue(bold: true, bg: true));\n  // print('brightMagenta'.brightMagenta(bold: true, bg: true));\n  // print('BrightCyan   '.brightCyan(bold: true, bg: true));\n  // print('brightWhite  '.brightWhite(bold: true, bg: true));\n\n  group('StringExt', () {\n    test('black', () {\n      expect('test'.red(), 'test');\n    });\n  });\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/pubspec.yaml",
    "content": "name: flutter_distributor_workspace\npublish_to: \"none\"\n\nenvironment:\n  sdk: \">=2.16.0 <4.0.0\"\n\ndev_dependencies:\n  flutter_lints: ^2.0.0\n  melos: ^3.1.0\n"
  },
  {
    "path": "plugins/flutter_distributor/website/.gitignore",
    "content": "# build output\ndist/\n# generated types\n.astro/\n\n# dependencies\nnode_modules/\n\n# logs\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\n\n\n# environment variables\n.env\n.env.production\n\n# macOS-specific files\n.DS_Store\n.vercel\n"
  },
  {
    "path": "plugins/flutter_distributor/website/astro.config.mjs",
    "content": "import { defineConfig } from \"astro/config\";\nimport starlight from \"@astrojs/starlight\";\nimport tailwind from \"@astrojs/tailwind\";\nimport partytown from \"@astrojs/partytown\";\n\nconst googleAnalyticsId = \"G-EC75P3JKR5\";\n\n// https://astro.build/config\nexport default defineConfig({\n  integrations: [\n    starlight({\n      title: \"Flutter Distributor\",\n      logo: {\n        src: \"./src/assets/logo.png\",\n      },\n      editLink: {\n        baseUrl:\n          \"https://github.com/leanflutter/flutter_distributor/tree/main/website/\",\n      },\n      defaultLocale: \"root\",\n      locales: {\n        root: {\n          label: \"English\",\n          lang: \"en\",\n        },\n        \"zh-hans\": {\n          label: \"简体中文\",\n          lang: \"zh-hans\",\n        },\n      },\n      social: {\n        github: \"https://github.com/leanflutter/flutter_distributor\",\n        discord: \"https://discord.com/invite/zPa6EZ2jqb\",\n      },\n      sidebar: [\n        {\n          label: \"Guides\",\n          translations: { \"zh-hans\": \"指南\" },\n          items: [\n            {\n              label: \"Getting started\",\n              link: \"/getting-started/\",\n              translations: { \"zh-hans\": \"开始\" },\n            },\n            {\n              label: \"Distribute Options\",\n              link: \"/distribute-options/\",\n              translations: { \"zh-hans\": \"分发选项\" },\n            },\n            { label: \"CLI\", link: \"/cli/\" },\n          ],\n        },\n        {\n          label: \"Makers\",\n          translations: { \"zh-hans\": \"制作器\" },\n          items: [\n            { label: \"aab\", link: \"/makers/aab/\" },\n            { label: \"apk\", link: \"/makers/apk/\" },\n            { label: \"appimage\", link: \"/makers/appimage/\" },\n            { label: \"deb\", link: \"/makers/deb/\" },\n            { label: \"dmg\", link: \"/makers/dmg/\" },\n            { label: \"exe\", link: \"/makers/exe/\" },\n            { label: \"ipa\", link: \"/makers/ipa/\" },\n            { label: \"msix\", link: \"/makers/msix/\" },\n            { label: \"pkg\", link: \"/makers/pkg/\" },\n            { label: \"rpm\", link: \"/makers/rpm/\" },\n            { label: \"zip\", link: \"/makers/zip/\" },\n          ],\n        },\n        {\n          label: \"Publishers\",\n          translations: { \"zh-hans\": \"发布器\" },\n          items: [\n            { label: \"appcenter\", link: \"/publishers/appcenter/\" },\n            { label: \"appstore\", link: \"/publishers/appstore/\" },\n            { label: \"fir\", link: \"/publishers/fir/\" },\n            {\n              label: \"firebase-hosting\",\n              link: \"/publishers/firebase-hosting/\",\n            },\n            { label: \"firebase\", link: \"/publishers/firebase/\" },\n            { label: \"github\", link: \"/publishers/github/\" },\n            { label: \"pgyer\", link: \"/publishers/pgyer/\" },\n            { label: \"playstore\", link: \"/publishers/playstore/\" },\n            { label: \"qiniu\", link: \"/publishers/qiniu/\" },\n            { label: \"vercel\", link: \"/publishers/vercel/\" },\n          ],\n        },\n        {\n          label: \"Tools\",\n          translations: { \"zh-hans\": \"工具\" },\n          items: [\n            { label: \"Parse App Package\", link: \"/tools/parse-app-package/\" },\n          ],\n        },\n      ],\n      components: {\n        TableOfContents: \"./src/components/starlight/TableOfContents.astro\",\n      },\n      head: [\n        {\n          tag: \"script\",\n          attrs: {\n            type: \"text/partytown\",\n            src: `https://www.googletagmanager.com/gtag/js?id=${googleAnalyticsId}`,\n            async: true,\n          },\n        },\n        {\n          tag: \"script\",\n          attrs: {\n            type: \"text/partytown\",\n          },\n          content: `\n              window.dataLayer = window.dataLayer || [];\n              function gtag(){dataLayer.push(arguments);}\n              gtag('js', new Date());\n              gtag('config', '${googleAnalyticsId}');\n            `,\n        },\n        {\n          tag: \"script\",\n          attrs: {\n            src: `https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-6049036475236211`,\n            async: true,\n            crossorigin: \"anonymous\",\n          },\n        },\n      ],\n    }),\n    tailwind({\n      // Disable the default base styles:\n      applyBaseStyles: false,\n    }),\n    partytown({\n      // Adds dataLayer.push as a forwarding-event.\n      config: {\n        forward: [\"dataLayer.push\"],\n      },\n    }),\n  ],\n});\n"
  },
  {
    "path": "plugins/flutter_distributor/website/package.json",
    "content": "{\n  \"name\": \"website\",\n  \"type\": \"module\",\n  \"version\": \"0.0.1\",\n  \"scripts\": {\n    \"dev\": \"astro dev\",\n    \"start\": \"astro dev\",\n    \"build\": \"astro check && astro build\",\n    \"preview\": \"astro preview\",\n    \"astro\": \"astro\"\n  },\n  \"dependencies\": {\n    \"@astrojs/check\": \"^0.3.2\",\n    \"@astrojs/partytown\": \"^2.0.4\",\n    \"@astrojs/starlight\": \"^0.15.0\",\n    \"@astrojs/starlight-tailwind\": \"^2.0.1\",\n    \"@astrojs/tailwind\": \"^5.1.0\",\n    \"astro\": \"^4.5.12\",\n    \"sharp\": \">=0.32.6\",\n    \"tailwindcss\": \"^3.4.1\",\n    \"typescript\": \"^5.3.3\"\n  }\n}"
  },
  {
    "path": "plugins/flutter_distributor/website/src/components/starlight/TableOfContents.astro",
    "content": "---\nimport type { Props } from \"@astrojs/starlight/props\";\nimport AstrolightTableOfContents from \"@astrojs/starlight/components/TableOfContents.astro\";\n---\n\n<AstrolightTableOfContents {...Astro.props} />\n<div style={{ marginTop: \"20px\" }}>\n  <ins\n    class=\"adsbygoogle\"\n    style=\"display:inline-block;width:268px;height:268px\"\n    data-ad-client=\"ca-pub-6049036475236211\"\n    data-ad-slot=\"3475386518\"></ins>\n  <script>\n    (window.adsbygoogle = window.adsbygoogle || []).push({});\n  </script>\n</div>\n"
  },
  {
    "path": "plugins/flutter_distributor/website/src/content/config.ts",
    "content": "import { defineCollection } from 'astro:content';\nimport { docsSchema, i18nSchema } from '@astrojs/starlight/schema';\n\nexport const collections = {\n\tdocs: defineCollection({ schema: docsSchema() }),\n\ti18n: defineCollection({ type: 'data', schema: i18nSchema() }),\n};\n"
  },
  {
    "path": "plugins/flutter_distributor/website/src/content/docs/index.mdx",
    "content": "---\ntitle: Flutter Distributor\ndescription: An all-in-one Flutter application packaging and distribution tool, providing you with a one-stop solution to meet various distribution needs.\ntemplate: splash\nhero:\n  tagline: An all-in-one Flutter application packaging and distribution tool, providing you with a one-stop solution to meet various distribution needs.\n  actions:\n    - text: Getting Started\n      link: /getting-started/\n      icon: right-arrow\n      variant: primary\n    - text: GitHub\n      link: https://github.com/leanflutter/flutter_distributor\n      icon: external\n---\n\nimport { Card, CardGrid } from \"@astrojs/starlight/components\";\n\n<CardGrid stagger>\n  <Card title=\"Mainstream Package Format Support\" icon=\"puzzle\">\n    Build platform-specific distributable files such as APK, IPA, and desktop\n    installation packages.\n  </Card>\n  <Card title=\"Mainstream Distribution Platform Support\" icon=\"rocket\">\n    Support major app stores and internal testing distribution platforms such as\n    `Google Play Store` and `Apple App Store`, simplifying the publishing\n    process.\n  </Card>\n  <Card title=\"Custom Configuration\" icon=\"setting\">\n    Flexibly customize the packaging and publishing process through simple yet\n    powerful configuration options.\n  </Card>\n  <Card title=\"Continuous Updates\" icon=\"pencil\">\n    Actively maintained to adapt to the latest Flutter framework and platform\n    requirements at any time.\n  </Card>\n</CardGrid>\n"
  },
  {
    "path": "plugins/flutter_distributor/website/src/content/docs/zh-hans/index.mdx",
    "content": "---\ntitle: Flutter Distributor\ndescription: 一款全能的 Flutter 应用打包和发布工具，为您提供一站式解决方案，满足各种分发需求。\ntemplate: splash\nhero:\n  tagline: 一款全能的 Flutter 应用打包和发布工具，为您提供一站式解决方案，满足各种分发需求。\n  actions:\n    - text: 开始使用\n      link: /zh-hans/getting-started/\n      icon: right-arrow\n      variant: primary\n    - text: GitHub\n      link: https://github.com/leanflutter/flutter_distributor\n      icon: external\n---\n\nimport { Card, CardGrid } from \"@astrojs/starlight/components\";\n\n<CardGrid stagger>\n  <Card title=\"主流包格式支持\" icon=\"puzzle\">\n    构建 APK、IPA、桌面端安装包文件等特定于平台的可分发文件。\n  </Card>\n  <Card title=\"主流分发平台支持\" icon=\"rocket\">\n    支持 `Google Play Store`、`Apple App Store`\n    等主流应用商店及内测分发平台，简化发布流程。\n  </Card>\n  <Card title=\"自定义配置\" icon=\"setting\">\n    通过简单而强大的配置选项，灵活地定制打包和发布的过程。\n  </Card>\n  <Card title=\"持续更新\" icon=\"pencil\">\n    活跃维护，随时适应最新的 Flutter 框架和平台要求。\n  </Card>\n</CardGrid>\n"
  },
  {
    "path": "plugins/flutter_distributor/website/src/content/i18n/en.json",
    "content": "{}\n"
  },
  {
    "path": "plugins/flutter_distributor/website/src/content/i18n/zh-cn.json",
    "content": "{}\n"
  },
  {
    "path": "plugins/flutter_distributor/website/src/env.d.ts",
    "content": "/// <reference path=\"../.astro/types.d.ts\" />\n/// <reference types=\"astro/client\" />\n\ndeclare let adsbygoogle: any;\n\ninterface Window {\n  adsbygoogle: any;\n}\n"
  },
  {
    "path": "plugins/flutter_distributor/website/src/tailwind.css",
    "content": "@tailwind base;\n@tailwind components;\n@tailwind utilities;\n"
  },
  {
    "path": "plugins/flutter_distributor/website/tailwind.config.js",
    "content": "import starlightPlugin from \"@astrojs/starlight-tailwind\";\n\n/** @type {import('tailwindcss').Config} */\nexport default {\n  content: [\"./src/**/*.{astro,html,js,jsx,md,mdx,svelte,ts,tsx,vue}\"],\n  theme: {\n    extend: {},\n  },\n  plugins: [starlightPlugin()],\n};\n"
  },
  {
    "path": "plugins/flutter_distributor/website/tsconfig.json",
    "content": "{\n  \"extends\": \"astro/tsconfigs/strict\"\n}"
  },
  {
    "path": "plugins/proxy/.gitignore",
    "content": "# Miscellaneous\n*.class\n*.log\n*.pyc\n*.swp\n.DS_Store\n.atom/\n.buildlog/\n.history\n.svn/\nmigrate_working_dir/\n\n# IntelliJ related\n*.iml\n*.ipr\n*.iws\n.idea/\n\n# The .vscode folder contains launch configuration and tasks you configure in\n# VS Code which you may wish to be included in version control, so this line\n# is commented out by default.\n#.vscode/\n\n# Flutter/Dart/Pub related\n# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.\n/pubspec.lock\n**/doc/api/\n.dart_tool/\n.packages\nbuild/\n"
  },
  {
    "path": "plugins/proxy/.metadata",
    "content": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrades etc.\n#\n# This file should be version controlled and should not be manually edited.\n\nversion:\n  revision: \"e1e47221e86272429674bec4f1bd36acc4fc7b77\"\n  channel: \"stable\"\n\nproject_type: plugin\n\n# Tracks metadata for the flutter migrate command\nmigration:\n  platforms:\n    - platform: root\n      create_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77\n      base_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77\n    - platform: android\n      create_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77\n      base_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77\n    - platform: windows\n      create_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77\n      base_revision: e1e47221e86272429674bec4f1bd36acc4fc7b77\n\n  # User provided section\n\n  # List of Local paths (relative to this file) that should be\n  # ignored by the migrate tool.\n  #\n  # Files that are not part of the templates will be ignored by default.\n  unmanaged_files:\n    - 'lib/main.dart'\n    - 'ios/Runner.xcodeproj/project.pbxproj'\n"
  },
  {
    "path": "plugins/proxy/LICENSE",
    "content": "TODO: Add your license here.\n"
  },
  {
    "path": "plugins/proxy/analysis_options.yaml",
    "content": "include: package:flutter_lints/flutter.yaml\n\n# Additional information about this file can be found at\n# https://dart.dev/guides/language/analysis-options\n"
  },
  {
    "path": "plugins/proxy/lib/proxy.dart",
    "content": "import 'dart:io';\n\nimport \"package:path/path.dart\";\n\nimport 'proxy_platform_interface.dart';\n\nenum ProxyTypes { http, https, socks }\n\nclass Proxy extends ProxyPlatform {\n  static String url = \"127.0.0.1\";\n\n  @override\n  Future<bool?> startProxy(\n    int port, [\n    List<String> bypassDomain = const [],\n  ]) async {\n    return switch (Platform.operatingSystem) {\n      \"macos\" => await _startProxyWithMacos(port, bypassDomain),\n      \"linux\" => await _startProxyWithLinux(port, bypassDomain),\n      \"windows\" => await ProxyPlatform.instance.startProxy(port, bypassDomain),\n      String() => false,\n    };\n  }\n\n  @override\n  Future<bool?> stopProxy() async {\n    return switch (Platform.operatingSystem) {\n      \"macos\" => await _stopProxyWithMacos(),\n      \"linux\" => await _stopProxyWithLinux(),\n      \"windows\" => await ProxyPlatform.instance.stopProxy(),\n      String() => false,\n    };\n  }\n\n  Future<bool> _startProxyWithLinux(int port, List<String> bypassDomain) async {\n    try {\n      final homeDir = Platform.environment['HOME']!;\n      final configDir = join(homeDir, \".config\");\n      final cmdList = List<List<String>>.empty(growable: true);\n      final desktop = Platform.environment['XDG_CURRENT_DESKTOP'];\n      final isKDE = desktop == \"KDE\";\n      if (isKDE) {\n        cmdList.add(\n          [\n            \"kwriteconfig5\",\n            \"--file\",\n            \"$configDir/kioslaverc\",\n            \"--group\",\n            \"Proxy Settings\",\n            \"--key\",\n            \"ProxyType\",\n            \"1\"\n          ],\n        );\n        cmdList.add(\n          [\n            \"kwriteconfig5\",\n            \"--file\",\n            \"$configDir/kioslaverc\",\n            \"--group\",\n            \"Proxy Settings\",\n            \"--key\",\n            \"NoProxyFor\",\n            bypassDomain.join(\",\")\n          ],\n        );\n      } else {\n        cmdList.add(\n          [\"gsettings\", \"set\", \"org.gnome.system.proxy\", \"mode\", \"manual\"],\n        );\n        final ignoreHosts = \"\\\"['${bypassDomain.join(\"', '\")}']\\\"\";\n        cmdList.add(\n          [\n            \"gsettings\",\n            \"set\",\n            \"org.gnome.system.proxy\",\n            \"ignore-hosts\",\n            ignoreHosts\n          ],\n        );\n      }\n      for (final type in ProxyTypes.values) {\n        if (!isKDE) {\n          cmdList.add(\n            [\n              \"gsettings\",\n              \"set\",\n              \"org.gnome.system.proxy.${type.name}\",\n              \"host\",\n              url\n            ],\n          );\n          cmdList.add(\n            [\n              \"gsettings\",\n              \"set\",\n              \"org.gnome.system.proxy.${type.name}\",\n              \"port\",\n              \"$port\"\n            ],\n          );\n          cmdList.add(\n            [\n              \"gsettings\",\n              \"set\",\n              \"org.gnome.system.proxy.${type.name}\",\n              \"port\",\n              \"$port\"\n            ],\n          );\n          cmdList.add(\n            [\n              \"gsettings\",\n              \"set\",\n              \"org.gnome.system.proxy.${type.name}\",\n              \"port\",\n              \"$port\"\n            ],\n          );\n        }\n        if (isKDE) {\n          cmdList.add(\n            [\n              \"kwriteconfig5\",\n              \"--file\",\n              \"$configDir/kioslaverc\",\n              \"--group\",\n              \"Proxy Settings\",\n              \"--key\",\n              \"${type.name}Proxy\",\n              \"${type.name}://$url:$port\"\n            ],\n          );\n        }\n      }\n      for (final cmd in cmdList) {\n        await Process.run(cmd[0], cmd.sublist(1), runInShell: true);\n      }\n      return true;\n    } catch (_) {\n      return false;\n    }\n  }\n\n  Future<bool> _stopProxyWithLinux() async {\n    try {\n      final homeDir = Platform.environment['HOME']!;\n      final configDir = join(homeDir, \".config/\");\n      final cmdList = List<List<String>>.empty(growable: true);\n      final desktop = Platform.environment['XDG_CURRENT_DESKTOP'];\n      final isKDE = desktop == \"KDE\";\n      if (isKDE) {\n        cmdList.add(\n          [\n            \"kwriteconfig5\",\n            \"--file\",\n            \"$configDir/kioslaverc\",\n            \"--group\",\n            \"Proxy Settings\",\n            \"--key\",\n            \"ProxyType\",\n            \"0\"\n          ],\n        );\n      } else {\n        cmdList.add(\n          [\"gsettings\", \"set\", \"org.gnome.system.proxy\", \"mode\", \"none\"],\n        );\n      }\n      for (final cmd in cmdList) {\n        await Process.run(cmd[0], cmd.sublist(1));\n      }\n      return true;\n    } catch (_) {\n      return false;\n    }\n  }\n\n  Future<bool> _startProxyWithMacos(int port, List<String> bypassDomain) async {\n    try {\n      final devices = await _getNetworkDeviceListWithMacos();\n      for (final dev in devices) {\n        await Future.wait([\n          Process.run(\n            \"/usr/sbin/networksetup\",\n            [\"-setwebproxystate\", dev, \"on\"],\n          ),\n          Process.run(\n            \"/usr/sbin/networksetup\",\n            [\"-setwebproxy\", dev, url, \"$port\"],\n          ),\n          Process.run(\n            \"/usr/sbin/networksetup\",\n            [\"-setsecurewebproxystate\", dev, \"on\"],\n          ),\n          Process.run(\n            \"/usr/sbin/networksetup\",\n            [\"-setsecurewebproxy\", dev, url, \"$port\"],\n          ),\n          Process.run(\n            \"/usr/sbin/networksetup\",\n            [\"-setsocksfirewallproxystate\", dev, \"on\"],\n          ),\n          Process.run(\n            \"/usr/sbin/networksetup\",\n            [\"-setsocksfirewallproxy\", dev, url, \"$port\"],\n          ),\n          Process.run(\n            \"/usr/sbin/networksetup\",\n            [\n              \"-setproxybypassdomains\",\n              dev,\n              bypassDomain.join(\",\"),\n            ],\n          ),\n        ]);\n      }\n      return true;\n    } catch (e) {\n      return false;\n    }\n  }\n\n  Future<bool> _stopProxyWithMacos() async {\n    try {\n      final devices = await _getNetworkDeviceListWithMacos();\n      for (final dev in devices) {\n        await Future.wait([\n          Process.run(\n            \"/usr/sbin/networksetup\",\n            [\"-setautoproxystate\", dev, \"off\"],\n          ),\n          Process.run(\n            \"/usr/sbin/networksetup\",\n            [\"-setwebproxystate\", dev, \"off\"],\n          ),\n          Process.run(\n            \"/usr/sbin/networksetup\",\n            [\"-setsecurewebproxystate\", dev, \"off\"],\n          ),\n          Process.run(\n            \"/usr/sbin/networksetup\",\n            [\"-setsocksfirewallproxystate\", dev, \"off\"],\n          ),\n          Process.run(\n            \"/usr/sbin/networksetup\",\n            [\"-setproxybypassdomains\", dev, \"\"],\n          ),\n        ]);\n      }\n      return true;\n    } catch (e) {\n      return false;\n    }\n  }\n\n  Future<List<String>> _getNetworkDeviceListWithMacos() async {\n    final res = await Process.run(\n        \"/usr/sbin/networksetup\", [\"-listallnetworkservices\"]);\n    final lines = res.stdout.toString().split(\"\\n\");\n    lines.removeWhere((element) => element.contains(\"*\"));\n    return lines;\n  }\n}\n"
  },
  {
    "path": "plugins/proxy/lib/proxy_method_channel.dart",
    "content": "import 'package:flutter/foundation.dart';\nimport 'package:flutter/services.dart';\n\nimport 'proxy_platform_interface.dart';\n\n/// An implementation of [ProxyPlatform] that uses method channels.\nclass MethodChannelProxy extends ProxyPlatform {\n  /// The method channel used to interact with the native platform.\n  @visibleForTesting\n  final methodChannel = const MethodChannel('proxy');\n\n  MethodChannelProxy();\n\n  @override\n  Future<bool?> startProxy(int port, List<String> bypassDomain) async {\n    return await methodChannel.invokeMethod<bool>(\"StartProxy\", {\n      'port': port,\n      'bypassDomain': bypassDomain,\n    });\n  }\n\n  @override\n  Future<bool?> stopProxy() async {\n    return await methodChannel.invokeMethod<bool>(\"StopProxy\");\n  }\n}\n"
  },
  {
    "path": "plugins/proxy/lib/proxy_platform_interface.dart",
    "content": "import 'package:plugin_platform_interface/plugin_platform_interface.dart';\n\nimport 'proxy_method_channel.dart';\n\nabstract class ProxyPlatform extends PlatformInterface {\n  /// Constructs a ProxyPlatform.\n  ProxyPlatform() : super(token: _token);\n\n  static final Object _token = Object();\n\n  static ProxyPlatform _instance = MethodChannelProxy();\n\n  /// The default instance of [ProxyPlatform] to use.\n  ///\n  /// Defaults to [MethodChannelProxy].\n  static ProxyPlatform get instance => _instance;\n\n  static set instance(ProxyPlatform instance) {\n    PlatformInterface.verifyToken(instance, _token);\n    _instance = instance;\n  }\n\n  Future<bool?> startProxy(int port, List<String> bypassDomain) {\n    throw UnimplementedError('startProxy() has not been implemented.');\n  }\n\n  Future<bool?> stopProxy() {\n    throw UnimplementedError('stopProxy() has not been implemented.');\n  }\n}\n"
  },
  {
    "path": "plugins/proxy/pubspec.yaml",
    "content": "name: proxy\ndescription: A new Flutter plugin project.\nversion: 0.0.1\nhomepage:\n\nenvironment:\n  sdk: '>=3.1.0 <4.0.0'\n  flutter: '>=3.3.0'\n\ndependencies:\n  flutter:\n    sdk: flutter\n  plugin_platform_interface: ^2.0.2\n  path: ^1.8.3\n\ndev_dependencies:\n  flutter_test:\n    sdk: flutter\n  flutter_lints: ^2.0.0\n\n# For information on the generic Dart part of this file, see the\n# following page: https://dart.dev/tools/pub/pubspec\n\n# The following section is specific to Flutter packages.\nflutter:\n  # This section identifies this Flutter project as a plugin project.\n  # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.)\n  # which should be registered in the plugin registry. This is required for\n  # using method channels.\n  # The Android 'package' specifies package in which the registered class is.\n  # This is required for using method channels on Android.\n  # The 'ffiPlugin' specifies that native code should be built and bundled.\n  # This is required for using `dart:ffi`.\n  # All these are used by the tooling to maintain consistency when\n  # adding or updating assets for this project.\n  plugin:\n    platforms:\n      windows:\n        pluginClass: ProxyPluginCApi\n\n  # To add assets to your plugin package, add an assets section, like this:\n  # assets:\n  #   - images/a_dot_burr.jpeg\n  #   - images/a_dot_ham.jpeg\n  #\n  # For details regarding assets in packages, see\n  # https://flutter.dev/assets-and-images/#from-packages\n  #\n  # An image asset can refer to one or more resolution-specific \"variants\", see\n  # https://flutter.dev/assets-and-images/#resolution-aware\n\n  # To add custom fonts to your plugin package, add a fonts section here,\n  # in this \"flutter\" section. Each entry in this list should have a\n  # \"family\" key with the font family name, and a \"fonts\" key with a\n  # list giving the asset and other descriptors for the font. For\n  # example:\n  # fonts:\n  #   - family: Schyler\n  #     fonts:\n  #       - asset: fonts/Schyler-Regular.ttf\n  #       - asset: fonts/Schyler-Italic.ttf\n  #         style: italic\n  #   - family: Trajan Pro\n  #     fonts:\n  #       - asset: fonts/TrajanPro.ttf\n  #       - asset: fonts/TrajanPro_Bold.ttf\n  #         weight: 700\n  #\n  # For details regarding fonts in packages, see\n  # https://flutter.dev/custom-fonts/#from-packages\n"
  },
  {
    "path": "plugins/proxy/windows/.gitignore",
    "content": "flutter/\n\n# Visual Studio user-specific files.\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# Visual Studio build-related files.\nx64/\nx86/\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!*.[Cc]ache/\n"
  },
  {
    "path": "plugins/proxy/windows/CMakeLists.txt",
    "content": "# The Flutter tooling requires that developers have a version of Visual Studio\n# installed that includes CMake 3.14 or later. You should not increase this\n# version, as doing so will cause the plugin to fail to compile for some\n# customers of the plugin.\ncmake_minimum_required(VERSION 3.14)\n\n# Project-level configuration.\nset(PROJECT_NAME \"proxy\")\nproject(${PROJECT_NAME} LANGUAGES CXX)\n\n# Explicitly opt in to modern CMake behaviors to avoid warnings with recent\n# versions of CMake.\ncmake_policy(VERSION 3.14...3.25)\n\n# This value is used when generating builds using this plugin, so it must\n# not be changed\nset(PLUGIN_NAME \"proxy_plugin\")\n\n# Any new source files that you add to the plugin should be added here.\nlist(APPEND PLUGIN_SOURCES\n  \"proxy_plugin.cpp\"\n  \"proxy_plugin.h\"\n)\n\n# Define the plugin library target. Its name must not be changed (see comment\n# on PLUGIN_NAME above).\nadd_library(${PLUGIN_NAME} SHARED\n  \"include/proxy/proxy_plugin_c_api.h\"\n  \"proxy_plugin_c_api.cpp\"\n  ${PLUGIN_SOURCES}\n)\n\n# Apply a standard set of build settings that are configured in the\n# application-level CMakeLists.txt. This can be removed for plugins that want\n# full control over build settings.\napply_standard_settings(${PLUGIN_NAME})\n\n# Symbols are hidden by default to reduce the chance of accidental conflicts\n# between plugins. This should not be removed; any symbols that should be\n# exported should be explicitly exported with the FLUTTER_PLUGIN_EXPORT macro.\nset_target_properties(${PLUGIN_NAME} PROPERTIES\n  CXX_VISIBILITY_PRESET hidden)\ntarget_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)\n\n# Source include directories and library dependencies. Add any plugin-specific\n# dependencies here.\ntarget_include_directories(${PLUGIN_NAME} INTERFACE\n  \"${CMAKE_CURRENT_SOURCE_DIR}/include\")\ntarget_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin)\n\n# List of absolute paths to libraries that should be bundled with the plugin.\n# This list could contain prebuilt libraries, or libraries created by an\n# external build triggered from this build file.\nset(proxy_bundled_libraries\n  \"\"\n  PARENT_SCOPE\n)\n\n# === Tests ===\n# These unit tests can be run from a terminal after building the example, or\n# from Visual Studio after opening the generated solution file.\n\n# Only enable test builds when building the example (which sets this variable)\n# so that plugin clients aren't building the tests.\nif (${include_${PROJECT_NAME}_tests})\nset(TEST_RUNNER \"${PROJECT_NAME}_test\")\nenable_testing()\n\n# Add the Google Test dependency.\ninclude(FetchContent)\nFetchContent_Declare(\n  googletest\n  URL https://github.com/google/googletest/archive/release-1.11.0.zip\n)\n# Prevent overriding the parent project's compiler/linker settings\nset(gtest_force_shared_crt ON CACHE BOOL \"\" FORCE)\n# Disable install commands for gtest so it doesn't end up in the bundle.\nset(INSTALL_GTEST OFF CACHE BOOL \"Disable installation of googletest\" FORCE)\nFetchContent_MakeAvailable(googletest)\n\n# The plugin's C API is not very useful for unit testing, so build the sources\n# directly into the test binary rather than using the DLL.\nadd_executable(${TEST_RUNNER}\n  test/proxy_plugin_test.cpp\n  ${PLUGIN_SOURCES}\n)\napply_standard_settings(${TEST_RUNNER})\ntarget_include_directories(${TEST_RUNNER} PRIVATE \"${CMAKE_CURRENT_SOURCE_DIR}\")\ntarget_link_libraries(${TEST_RUNNER} PRIVATE flutter_wrapper_plugin)\ntarget_link_libraries(${TEST_RUNNER} PRIVATE gtest_main gmock)\n# flutter_wrapper_plugin has link dependencies on the Flutter DLL.\nadd_custom_command(TARGET ${TEST_RUNNER} POST_BUILD\n  COMMAND ${CMAKE_COMMAND} -E copy_if_different\n  \"${FLUTTER_LIBRARY}\" $<TARGET_FILE_DIR:${TEST_RUNNER}>\n)\n\n# Enable automatic test discovery.\ninclude(GoogleTest)\ngtest_discover_tests(${TEST_RUNNER})\nendif()\n"
  },
  {
    "path": "plugins/proxy/windows/include/proxy/proxy_plugin_c_api.h",
    "content": "#ifndef FLUTTER_PLUGIN_PROXY_PLUGIN_C_API_H_\n#define FLUTTER_PLUGIN_PROXY_PLUGIN_C_API_H_\n\n#include <flutter_plugin_registrar.h>\n\n#ifdef FLUTTER_PLUGIN_IMPL\n#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport)\n#else\n#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport)\n#endif\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif\n\nFLUTTER_PLUGIN_EXPORT void ProxyPluginCApiRegisterWithRegistrar(\n    FlutterDesktopPluginRegistrarRef registrar);\n\n#if defined(__cplusplus)\n}  // extern \"C\"\n#endif\n\n#endif  // FLUTTER_PLUGIN_PROXY_PLUGIN_C_API_H_\n"
  },
  {
    "path": "plugins/proxy/windows/proxy_plugin.cpp",
    "content": "#include \"proxy_plugin.h\"\n\n// This must be included before many other Windows headers.\n#include <windows.h>\n\n#include <WinInet.h>\n#include <Ras.h>\n#include <RasError.h>\n#include <vector>\n#include <iostream>\n\n#pragma comment(lib, \"wininet\")\n#pragma comment(lib, \"Rasapi32\")\n\n// For getPlatformVersion; remove unless needed for your plugin implementation.\n#include <VersionHelpers.h>\n\n#include <flutter/method_channel.h>\n#include <flutter/plugin_registrar_windows.h>\n#include <flutter/standard_method_codec.h>\n\n#include <memory>\n#include <sstream>\n\nvoid startProxy(const int port, const flutter::EncodableList& bypassDomain)\n{\n  INTERNET_PER_CONN_OPTION_LIST list;\n  DWORD dwBufSize = sizeof(list);\n  list.dwSize = sizeof(list);\n  list.pszConnection = nullptr;\n\n  auto url = \"127.0.0.1:\" + std::to_string(port);\n  auto wUrl = std::wstring(url.begin(), url.end());\n  auto fullAddr = new WCHAR[url.length() + 1];\n  wcscpy_s(fullAddr, url.length() + 1, wUrl.c_str());\n\n  std::wstring wBypassList;\n\n  for (const auto& domain : bypassDomain) {\n    if (!wBypassList.empty()) {\n       wBypassList += L\";\";\n    }\n    wBypassList += std::wstring(std::get<std::string>(domain).begin(), std::get<std::string>(domain).end());\n  }\n\n  auto bypassAddr = new WCHAR[wBypassList.length() + 1];\n  wcscpy_s(bypassAddr, wBypassList.length() + 1, wBypassList.c_str());\n\n  list.dwOptionCount = 3;\n  list.pOptions = new INTERNET_PER_CONN_OPTION[3];\n\n  if (!list.pOptions)\n  {\n    return;\n  }\n\n  list.pOptions[0].dwOption = INTERNET_PER_CONN_FLAGS;\n  list.pOptions[0].Value.dwValue = PROXY_TYPE_DIRECT | PROXY_TYPE_PROXY;\n\n  list.pOptions[1].dwOption = INTERNET_PER_CONN_PROXY_SERVER;\n  list.pOptions[1].Value.pszValue = fullAddr;\n\n  list.pOptions[2].dwOption = INTERNET_PER_CONN_PROXY_BYPASS;\n  list.pOptions[2].Value.pszValue = bypassAddr;\n\n  InternetSetOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &list, dwBufSize);\n\n  RASENTRYNAME entry;\n  entry.dwSize = sizeof(entry);\n  std::vector<RASENTRYNAME> entries;\n  DWORD size = sizeof(entry), count;\n  LPRASENTRYNAME entryAddr = &entry;\n  auto ret = RasEnumEntries(nullptr, nullptr, entryAddr, &size, &count);\n  if (ret == ERROR_BUFFER_TOO_SMALL)\n  {\n    entries.resize(count);\n    entries[0].dwSize = sizeof(RASENTRYNAME);\n    entryAddr = entries.data();\n    ret = RasEnumEntries(nullptr, nullptr, entryAddr, &size, &count);\n  }\n  if (ret != ERROR_SUCCESS)\n  {\n    return;\n  }\n  for (DWORD i = 0; i < count; i++)\n  {\n    list.pszConnection = entryAddr[i].szEntryName;\n    InternetSetOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &list, dwBufSize);\n  }\n\n  delete[] fullAddr;\n  delete[] bypassAddr;\n  delete[] list.pOptions;\n\n  InternetSetOption(nullptr, INTERNET_OPTION_SETTINGS_CHANGED, nullptr, 0);\n  InternetSetOption(nullptr, INTERNET_OPTION_REFRESH, nullptr, 0);\n}\n\nvoid stopProxy()\n{\n  INTERNET_PER_CONN_OPTION_LIST list;\n  DWORD dwBufSize = sizeof(list);\n\n  list.dwSize = sizeof(list);\n  list.pszConnection = nullptr;\n  list.dwOptionCount = 1;\n  list.pOptions = new INTERNET_PER_CONN_OPTION[1];\n  if (nullptr == list.pOptions)\n  {\n    return;\n  }\n  list.pOptions[0].dwOption = INTERNET_PER_CONN_FLAGS;\n  list.pOptions[0].Value.dwValue = PROXY_TYPE_DIRECT;\n\n  InternetSetOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &list, dwBufSize);\n\n  RASENTRYNAME entry;\n  entry.dwSize = sizeof(entry);\n  std::vector<RASENTRYNAME> entries;\n  DWORD size = sizeof(entry), count;\n  LPRASENTRYNAME entryAddr = &entry;\n  auto ret = RasEnumEntries(nullptr, nullptr, entryAddr, &size, &count);\n  if (ret == ERROR_BUFFER_TOO_SMALL)\n  {\n    entries.resize(count);\n    entries[0].dwSize = sizeof(RASENTRYNAME);\n    entryAddr = entries.data();\n    ret = RasEnumEntries(nullptr, nullptr, entryAddr, &size, &count);\n  }\n  if (ret != ERROR_SUCCESS)\n  {\n    return;\n  }\n  for (DWORD i = 0; i < count; i++)\n  {\n    list.pszConnection = entryAddr[i].szEntryName;\n    InternetSetOption(nullptr, INTERNET_OPTION_PER_CONNECTION_OPTION, &list, dwBufSize);\n  }\n  delete[] list.pOptions;\n  InternetSetOption(nullptr, INTERNET_OPTION_SETTINGS_CHANGED, nullptr, 0);\n  InternetSetOption(nullptr, INTERNET_OPTION_REFRESH, nullptr, 0);\n}\n\nnamespace proxy\n{\n\n  // static\n  void ProxyPlugin::RegisterWithRegistrar(\n      flutter::PluginRegistrarWindows *registrar)\n  {\n    auto channel =\n        std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(\n            registrar->messenger(), \"proxy\",\n            &flutter::StandardMethodCodec::GetInstance());\n\n    auto plugin = std::make_unique<ProxyPlugin>();\n\n    channel->SetMethodCallHandler(\n        [plugin_pointer = plugin.get()](const auto &call, auto result)\n        {\n          plugin_pointer->HandleMethodCall(call, std::move(result));\n        });\n\n    registrar->AddPlugin(std::move(plugin));\n  }\n\n  ProxyPlugin::ProxyPlugin() {}\n\n  ProxyPlugin::~ProxyPlugin() {}\n\n  void ProxyPlugin::HandleMethodCall(\n      const flutter::MethodCall<flutter::EncodableValue> &method_call,\n      std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result)\n  {\n    if (method_call.method_name().compare(\"StopProxy\") == 0)\n    {\n      stopProxy();\n      result->Success(true);\n    }\n    else if (method_call.method_name().compare(\"StartProxy\") == 0)\n    {\n      auto *arguments = std::get_if<flutter::EncodableMap>(method_call.arguments());\n      auto port = std::get<int>(arguments->at(flutter::EncodableValue(\"port\")));\n      auto bypassDomain = std::get<flutter::EncodableList>(arguments->at(flutter::EncodableValue(\"bypassDomain\")));\n      startProxy(port, bypassDomain);\n      result->Success(true);\n    }\n    else\n    {\n      result->NotImplemented();\n    }\n  }\n} // namespace proxy\n"
  },
  {
    "path": "plugins/proxy/windows/proxy_plugin.h",
    "content": "#ifndef FLUTTER_PLUGIN_PROXY_PLUGIN_H_\n#define FLUTTER_PLUGIN_PROXY_PLUGIN_H_\n\n#include <flutter/method_channel.h>\n#include <flutter/plugin_registrar_windows.h>\n\n#include <memory>\n\nnamespace proxy {\n\nclass ProxyPlugin : public flutter::Plugin {\n public:\n  static void RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar);\n\n  ProxyPlugin();\n\n  virtual ~ProxyPlugin();\n\n  // Disallow copy and assign.\n  ProxyPlugin(const ProxyPlugin&) = delete;\n  ProxyPlugin& operator=(const ProxyPlugin&) = delete;\n\n  // Called when a method is called on this plugin's channel from Dart.\n  void HandleMethodCall(\n      const flutter::MethodCall<flutter::EncodableValue> &method_call,\n      std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);\n};\n\n}  // namespace proxy\n\n#endif  // FLUTTER_PLUGIN_PROXY_PLUGIN_H_\n"
  },
  {
    "path": "plugins/proxy/windows/proxy_plugin_c_api.cpp",
    "content": "#include \"include/proxy/proxy_plugin_c_api.h\"\n\n#include <flutter/plugin_registrar_windows.h>\n\n#include \"proxy_plugin.h\"\n\nvoid ProxyPluginCApiRegisterWithRegistrar(\n    FlutterDesktopPluginRegistrarRef registrar) {\n  proxy::ProxyPlugin::RegisterWithRegistrar(\n      flutter::PluginRegistrarManager::GetInstance()\n          ->GetRegistrar<flutter::PluginRegistrarWindows>(registrar));\n}\n"
  },
  {
    "path": "plugins/proxy/windows/test/proxy_plugin_test.cpp",
    "content": "#include <flutter/method_call.h>\n#include <flutter/method_result_functions.h>\n#include <flutter/standard_method_codec.h>\n#include <gtest/gtest.h>\n#include <windows.h>\n\n#include <memory>\n#include <string>\n#include <variant>\n\n#include \"proxy_plugin.h\"\n\nnamespace proxy {\nnamespace test {\n\nnamespace {\n\nusing flutter::EncodableMap;\nusing flutter::EncodableValue;\nusing flutter::MethodCall;\nusing flutter::MethodResultFunctions;\n\n}  // namespace\n\nTEST(ProxyPlugin, GetPlatformVersion) {\n  ProxyPlugin plugin;\n  // Save the reply value from the success callback.\n  std::string result_string;\n  plugin.HandleMethodCall(\n      MethodCall(\"getPlatformVersion\", std::make_unique<EncodableValue>()),\n      std::make_unique<MethodResultFunctions<>>(\n          [&result_string](const EncodableValue* result) {\n            result_string = std::get<std::string>(*result);\n          },\n          nullptr, nullptr));\n\n  // Since the exact string varies by host, just ensure that it's a string\n  // with the expected format.\n  EXPECT_TRUE(result_string.rfind(\"Windows \", 0) == 0);\n}\n\n}  // namespace test\n}  // namespace proxy\n"
  },
  {
    "path": "plugins/window_ext/.gitignore",
    "content": "# Miscellaneous\n*.class\n*.log\n*.pyc\n*.swp\n.DS_Store\n.atom/\n.buildlog/\n.history\n.svn/\nmigrate_working_dir/\n\n# IntelliJ related\n*.iml\n*.ipr\n*.iws\n.idea/\n\n# The .vscode folder contains launch configuration and tasks you configure in\n# VS Code which you may wish to be included in version control, so this line\n# is commented out by default.\n#.vscode/\n\n# Flutter/Dart/Pub related\n# Libraries should not include pubspec.lock, per https://dart.dev/guides/libraries/private-files#pubspeclock.\n/pubspec.lock\n**/doc/api/\n.dart_tool/\nbuild/\n"
  },
  {
    "path": "plugins/window_ext/.metadata",
    "content": "# This file tracks properties of this Flutter project.\n# Used by Flutter tool to assess capabilities and perform upgrades etc.\n#\n# This file should be version controlled and should not be manually edited.\n\nversion:\n  revision: \"603104015dd692ea3403755b55d07813d5cf8965\"\n  channel: \"stable\"\n\nproject_type: plugin\n\n# Tracks metadata for the flutter migrate command\nmigration:\n  platforms:\n    - platform: root\n      create_revision: 603104015dd692ea3403755b55d07813d5cf8965\n      base_revision: 603104015dd692ea3403755b55d07813d5cf8965\n    - platform: macos\n      create_revision: 603104015dd692ea3403755b55d07813d5cf8965\n      base_revision: 603104015dd692ea3403755b55d07813d5cf8965\n    - platform: windows\n      create_revision: 603104015dd692ea3403755b55d07813d5cf8965\n      base_revision: 603104015dd692ea3403755b55d07813d5cf8965\n\n  # User provided section\n\n  # List of Local paths (relative to this file) that should be\n  # ignored by the migrate tool.\n  #\n  # Files that are not part of the templates will be ignored by default.\n  unmanaged_files:\n    - 'lib/main.dart'\n    - 'ios/Runner.xcodeproj/project.pbxproj'\n"
  },
  {
    "path": "plugins/window_ext/LICENSE",
    "content": "TODO: Add your license here.\n"
  },
  {
    "path": "plugins/window_ext/analysis_options.yaml",
    "content": "include: package:flutter_lints/flutter.yaml\n\n# Additional information about this file can be found at\n# https://dart.dev/guides/language/analysis-options\n"
  },
  {
    "path": "plugins/window_ext/lib/window_ext.dart",
    "content": "export 'window_ext_listener.dart';\nexport 'window_ext_manager.dart';"
  },
  {
    "path": "plugins/window_ext/lib/window_ext_listener.dart",
    "content": "abstract mixin class WindowExtListener {\n  void onTaskbarCreated() {}\n\n  void onShouldTerminate() {}\n}\n"
  },
  {
    "path": "plugins/window_ext/lib/window_ext_manager.dart",
    "content": "import 'package:flutter/foundation.dart';\nimport 'package:flutter/services.dart';\n\nimport 'window_ext_listener.dart';\n\nclass WindowExtManager {\n  WindowExtManager._() {\n    _channel.setMethodCallHandler(_methodCallHandler);\n  }\n\n  static final WindowExtManager instance = WindowExtManager._();\n\n  final MethodChannel _channel = const MethodChannel('window_ext');\n\n  final ObserverList<WindowExtListener> _listeners =\n      ObserverList<WindowExtListener>();\n\n  Future<void> _methodCallHandler(MethodCall call) async {\n    for (final WindowExtListener listener in _listeners) {\n      switch (call.method) {\n        case \"taskbarCreated\":\n          listener.onTaskbarCreated();\n          break;\n        case \"shouldTerminate\":\n          listener.onShouldTerminate();\n          break;\n      }\n    }\n  }\n\n  bool get hasListeners {\n    return _listeners.isNotEmpty;\n  }\n\n  void addListener(WindowExtListener listener) {\n    _listeners.add(listener);\n  }\n\n  void removeListener(WindowExtListener listener) {\n    _listeners.remove(listener);\n  }\n}\n\nfinal windowExtManager = WindowExtManager.instance;\n"
  },
  {
    "path": "plugins/window_ext/macos/Classes/WindowExtPlugin.swift",
    "content": "import Cocoa\nimport FlutterMacOS\n\npublic class WindowExtPlugin: NSObject, FlutterPlugin {\n    public static var instance:WindowExtPlugin?\n    \n    public static func register(with registrar: FlutterPluginRegistrar) {\n        let channel = FlutterMethodChannel(name: \"window_ext\", binaryMessenger: registrar.messenger)\n        instance = WindowExtPlugin(registrar, channel)\n        registrar.addMethodCallDelegate(instance!, channel: channel)\n    }\n    \n    private var registrar: FlutterPluginRegistrar!\n    private var channel: FlutterMethodChannel!\n    \n    public init(_ registrar: FlutterPluginRegistrar, _ channel: FlutterMethodChannel) {\n        super.init()\n        self.registrar = registrar\n        self.channel = channel\n    }\n    \n    public func handleShouldTerminate(){\n        channel.invokeMethod(\"shouldTerminate\", arguments: nil)\n    }\n}\n"
  },
  {
    "path": "plugins/window_ext/macos/window_ext.podspec",
    "content": "#\n# To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html.\n# Run `pod lib lint window_ext.podspec` to validate before publishing.\n#\nPod::Spec.new do |s|\n  s.name             = 'window_ext'\n  s.version          = '0.0.1'\n  s.summary          = 'A new Flutter plugin project.'\n  s.description      = <<-DESC\nA new Flutter plugin project.\n                       DESC\n  s.homepage         = 'http://example.com'\n  s.license          = { :file => '../LICENSE' }\n  s.author           = { 'Your Company' => 'email@example.com' }\n\n  s.source           = { :path => '.' }\n  s.source_files = 'Classes/**/*'\n  s.dependency 'FlutterMacOS'\n\n  s.platform = :osx, '10.11'\n  s.pod_target_xcconfig = { 'DEFINES_MODULE' => 'YES' }\n  s.swift_version = '5.0'\nend\n"
  },
  {
    "path": "plugins/window_ext/pubspec.yaml",
    "content": "name: window_ext\ndescription: \"A new Flutter plugin project.\"\nversion: 0.0.1\nhomepage:\n\nenvironment:\n  sdk: '>=3.4.4 <4.0.0'\n  flutter: '>=3.3.0'\n\ndependencies:\n  flutter:\n    sdk: flutter\n  plugin_platform_interface: ^2.0.2\n\ndev_dependencies:\n  flutter_test:\n    sdk: flutter\n  flutter_lints: ^3.0.0\n\n# For information on the generic Dart part of this file, see the\n# following page: https://dart.dev/tools/pub/pubspec\n\n# The following section is specific to Flutter packages.\nflutter:\n  # This section identifies this Flutter project as a plugin project.\n  # The 'pluginClass' specifies the class (in Java, Kotlin, Swift, Objective-C, etc.)\n  # which should be registered in the plugin registry. This is required for\n  # using method channels.\n  # The Android 'package' specifies package in which the registered class is.\n  # This is required for using method channels on Android.\n  # The 'ffiPlugin' specifies that native code should be built and bundled.\n  # This is required for using `dart:ffi`.\n  # All these are used by the tooling to maintain consistency when\n  # adding or updating assets for this project.\n  plugin:\n    platforms:\n      windows:\n        pluginClass: WindowExtPluginCApi\n      macos:\n        pluginClass: WindowExtPlugin\n\n  # To add assets to your plugin package, add an assets section, like this:\n  # assets:\n  #   - images/a_dot_burr.jpeg\n  #   - images/a_dot_ham.jpeg\n  #\n  # For details regarding assets in packages, see\n  # https://flutter.dev/assets-and-images/#from-packages\n  #\n  # An image asset can refer to one or more resolution-specific \"variants\", see\n  # https://flutter.dev/assets-and-images/#resolution-aware\n\n  # To add custom fonts to your plugin package, add a fonts section here,\n  # in this \"flutter\" section. Each entry in this list should have a\n  # \"family\" key with the font family name, and a \"fonts\" key with a\n  # list giving the asset and other descriptors for the font. For\n  # example:\n  # fonts:\n  #   - family: Schyler\n  #     fonts:\n  #       - asset: fonts/Schyler-Regular.ttf\n  #       - asset: fonts/Schyler-Italic.ttf\n  #         style: italic\n  #   - family: Trajan Pro\n  #     fonts:\n  #       - asset: fonts/TrajanPro.ttf\n  #       - asset: fonts/TrajanPro_Bold.ttf\n  #         weight: 700\n  #\n  # For details regarding fonts in packages, see\n  # https://flutter.dev/custom-fonts/#from-packages\n"
  },
  {
    "path": "plugins/window_ext/windows/.gitignore",
    "content": "flutter/\n\n# Visual Studio user-specific files.\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# Visual Studio build-related files.\nx64/\nx86/\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!*.[Cc]ache/\n"
  },
  {
    "path": "plugins/window_ext/windows/CMakeLists.txt",
    "content": "# The Flutter tooling requires that developers have a version of Visual Studio\n# installed that includes CMake 3.14 or later. You should not increase this\n# version, as doing so will cause the plugin to fail to compile for some\n# customers of the plugin.\ncmake_minimum_required(VERSION 3.14)\n\n# Project-level configuration.\nset(PROJECT_NAME \"window_ext\")\nproject(${PROJECT_NAME} LANGUAGES CXX)\n\n# Explicitly opt in to modern CMake behaviors to avoid warnings with recent\n# versions of CMake.\ncmake_policy(VERSION 3.14...3.25)\n\n# This value is used when generating builds using this plugin, so it must\n# not be changed\nset(PLUGIN_NAME \"window_ext_plugin\")\n\n# Any new source files that you add to the plugin should be added here.\nlist(APPEND PLUGIN_SOURCES\n  \"window_ext_plugin.cpp\"\n  \"window_ext_plugin.h\"\n)\n\n# Define the plugin library target. Its name must not be changed (see comment\n# on PLUGIN_NAME above).\nadd_library(${PLUGIN_NAME} SHARED\n  \"include/window_ext/window_ext_plugin_c_api.h\"\n  \"window_ext_plugin_c_api.cpp\"\n  ${PLUGIN_SOURCES}\n)\n\n# Apply a standard set of build settings that are configured in the\n# application-level CMakeLists.txt. This can be removed for plugins that want\n# full control over build settings.\napply_standard_settings(${PLUGIN_NAME})\n\n# Symbols are hidden by default to reduce the chance of accidental conflicts\n# between plugins. This should not be removed; any symbols that should be\n# exported should be explicitly exported with the FLUTTER_PLUGIN_EXPORT macro.\nset_target_properties(${PLUGIN_NAME} PROPERTIES\n  CXX_VISIBILITY_PRESET hidden)\ntarget_compile_definitions(${PLUGIN_NAME} PRIVATE FLUTTER_PLUGIN_IMPL)\n\n# Source include directories and library dependencies. Add any plugin-specific\n# dependencies here.\ntarget_include_directories(${PLUGIN_NAME} INTERFACE\n  \"${CMAKE_CURRENT_SOURCE_DIR}/include\")\ntarget_link_libraries(${PLUGIN_NAME} PRIVATE flutter flutter_wrapper_plugin)\n\n# List of absolute paths to libraries that should be bundled with the plugin.\n# This list could contain prebuilt libraries, or libraries created by an\n# external build triggered from this build file.\nset(window_ext_bundled_libraries\n  \"\"\n  PARENT_SCOPE\n)\n\n# === Tests ===\n# These unit tests can be run from a terminal after building the example, or\n# from Visual Studio after opening the generated solution file.\n\n# Only enable test builds when building the example (which sets this variable)\n# so that plugin clients aren't building the tests.\nif (${include_${PROJECT_NAME}_tests})\nset(TEST_RUNNER \"${PROJECT_NAME}_test\")\nenable_testing()\n\n# Add the Google Test dependency.\ninclude(FetchContent)\nFetchContent_Declare(\n  googletest\n  URL https://github.com/google/googletest/archive/release-1.11.0.zip\n)\n# Prevent overriding the parent project's compiler/linker settings\nset(gtest_force_shared_crt ON CACHE BOOL \"\" FORCE)\n# Disable install commands for gtest so it doesn't end up in the bundle.\nset(INSTALL_GTEST OFF CACHE BOOL \"Disable installation of googletest\" FORCE)\nFetchContent_MakeAvailable(googletest)\n\n# The plugin's C API is not very useful for unit testing, so build the sources\n# directly into the test binary rather than using the DLL.\nadd_executable(${TEST_RUNNER}\n  test/window_ext_plugin_test.cpp\n  ${PLUGIN_SOURCES}\n)\napply_standard_settings(${TEST_RUNNER})\ntarget_include_directories(${TEST_RUNNER} PRIVATE \"${CMAKE_CURRENT_SOURCE_DIR}\")\ntarget_link_libraries(${TEST_RUNNER} PRIVATE flutter_wrapper_plugin)\ntarget_link_libraries(${TEST_RUNNER} PRIVATE gtest_main gmock)\n# flutter_wrapper_plugin has link dependencies on the Flutter DLL.\nadd_custom_command(TARGET ${TEST_RUNNER} POST_BUILD\n  COMMAND ${CMAKE_COMMAND} -E copy_if_different\n  \"${FLUTTER_LIBRARY}\" $<TARGET_FILE_DIR:${TEST_RUNNER}>\n)\n\n# Enable automatic test discovery.\ninclude(GoogleTest)\ngtest_discover_tests(${TEST_RUNNER})\nendif()\n"
  },
  {
    "path": "plugins/window_ext/windows/include/window_ext/window_ext_plugin_c_api.h",
    "content": "#ifndef FLUTTER_PLUGIN_WINDOW_EXT_PLUGIN_C_API_H_\n#define FLUTTER_PLUGIN_WINDOW_EXT_PLUGIN_C_API_H_\n\n#include <flutter_plugin_registrar.h>\n\n#ifdef FLUTTER_PLUGIN_IMPL\n#define FLUTTER_PLUGIN_EXPORT __declspec(dllexport)\n#else\n#define FLUTTER_PLUGIN_EXPORT __declspec(dllimport)\n#endif\n\n#if defined(__cplusplus)\nextern \"C\" {\n#endif\n\nFLUTTER_PLUGIN_EXPORT void WindowExtPluginCApiRegisterWithRegistrar(\n    FlutterDesktopPluginRegistrarRef registrar);\n\n#if defined(__cplusplus)\n}  // extern \"C\"\n#endif\n\n#endif  // FLUTTER_PLUGIN_WINDOW_EXT_PLUGIN_C_API_H_\n"
  },
  {
    "path": "plugins/window_ext/windows/test/window_ext_plugin_test.cpp",
    "content": "#include <flutter/method_call.h>\n#include <flutter/method_result_functions.h>\n#include <flutter/standard_method_codec.h>\n#include <gtest/gtest.h>\n#include <windows.h>\n\n#include <memory>\n#include <string>\n#include <variant>\n\n#include \"window_ext_plugin.h\"\n\nnamespace window_ext {\nnamespace test {\n\nnamespace {\n\nusing flutter::EncodableMap;\nusing flutter::EncodableValue;\nusing flutter::MethodCall;\nusing flutter::MethodResultFunctions;\n\n}  // namespace\n\nTEST(WindowExtPlugin, GetPlatformVersion) {\n  WindowExtPlugin plugin;\n  // Save the reply value from the success callback.\n  std::string result_string;\n  plugin.HandleMethodCall(\n      MethodCall(\"getPlatformVersion\", std::make_unique<EncodableValue>()),\n      std::make_unique<MethodResultFunctions<>>(\n          [&result_string](const EncodableValue* result) {\n            result_string = std::get<std::string>(*result);\n          },\n          nullptr, nullptr));\n\n  // Since the exact string varies by host, just ensure that it's a string\n  // with the expected format.\n  EXPECT_TRUE(result_string.rfind(\"Windows \", 0) == 0);\n}\n\n}  // namespace test\n}  // namespace window_ext\n"
  },
  {
    "path": "plugins/window_ext/windows/window_ext_plugin.cpp",
    "content": "#include \"window_ext_plugin.h\"\n\n// This must be included before many other Windows headers.\n#include <windows.h>\n\n// For getPlatformVersion; remove unless needed for your plugin implementation.\n#include <VersionHelpers.h>\n\n#include <flutter/method_channel.h>\n#include <flutter/plugin_registrar_windows.h>\n#include <flutter/standard_method_codec.h>\n\n#include <memory>\n#include <sstream>\n\nnamespace window_ext {\n\n\nstd::unique_ptr<\n    flutter::MethodChannel<flutter::EncodableValue>,\n    std::default_delete<flutter::MethodChannel<flutter::EncodableValue>>>\n    channel = nullptr;\n\n\n// static\nvoid WindowExtPlugin::RegisterWithRegistrar(\n    flutter::PluginRegistrarWindows *registrar) {\n  channel =\n      std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(\n          registrar->messenger(), \"window_ext\",\n          &flutter::StandardMethodCodec::GetInstance());\n\n  auto plugin = std::make_unique<WindowExtPlugin>(registrar);\n\n  channel->SetMethodCallHandler(\n      [plugin_pointer = plugin.get()](const auto &call, auto result) {\n        plugin_pointer->HandleMethodCall(call, std::move(result));\n      });\n\n  registrar->AddPlugin(std::move(plugin));\n}\n\nWindowExtPlugin::WindowExtPlugin(flutter::PluginRegistrarWindows* registrar)\n    : registrar(registrar) {\n  WM_TASKBARCREATED = RegisterWindowMessage(TEXT(\"TaskbarCreated\"));\n  window_proc_id = registrar->RegisterTopLevelWindowProcDelegate(\n      [this](HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) {\n        return HandleWindowProc(hwnd, message, wparam, lparam);\n      });\n}\n\nWindowExtPlugin::~WindowExtPlugin() {\n  registrar->UnregisterTopLevelWindowProcDelegate(window_proc_id);\n}\n\nstd::optional<LRESULT> WindowExtPlugin::HandleWindowProc(HWND hWnd,\n                                                           UINT message,\n                                                           WPARAM wParam,\n                                                           LPARAM lParam) {\n  std::optional<LRESULT> result;\n  if(message == WM_TASKBARCREATED){\n    channel -> InvokeMethod(\"taskbarCreated\", std::make_unique<flutter::EncodableValue>());\n  }\n  return result;\n}\n\n\n\nvoid WindowExtPlugin::HandleMethodCall(\n    const flutter::MethodCall<flutter::EncodableValue> &method_call,\n    std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result) {\n  if (method_call.method_name().compare(\"getPlatformVersion\") == 0) {\n    std::ostringstream version_stream;\n    version_stream << \"Windows \";\n    if (IsWindows10OrGreater()) {\n      version_stream << \"10+\";\n    } else if (IsWindows8OrGreater()) {\n      version_stream << \"8\";\n    } else if (IsWindows7OrGreater()) {\n      version_stream << \"7\";\n    }\n    result->Success(flutter::EncodableValue(version_stream.str()));\n  } else {\n    result->NotImplemented();\n  }\n}\n\n}  // namespace window_ext\n"
  },
  {
    "path": "plugins/window_ext/windows/window_ext_plugin.h",
    "content": "#ifndef FLUTTER_PLUGIN_WINDOW_EXT_PLUGIN_H_\n#define FLUTTER_PLUGIN_WINDOW_EXT_PLUGIN_H_\n\n#include <flutter/method_channel.h>\n#include <flutter/plugin_registrar_windows.h>\n\n#include <memory>\n\nnamespace window_ext {\n\nclass WindowExtPlugin : public flutter::Plugin {\n public:\n  static void RegisterWithRegistrar(flutter::PluginRegistrarWindows *registrar);\n\n\n  WindowExtPlugin(flutter::PluginRegistrarWindows *registrar);\n  virtual ~WindowExtPlugin();\n\n  // Disallow copy and assign.\n  WindowExtPlugin(const WindowExtPlugin&) = delete;\n  WindowExtPlugin& operator=(const WindowExtPlugin&) = delete;\n\n  // Called when a method is called on this plugin's channel from Dart.\n  void HandleMethodCall(\n      const flutter::MethodCall<flutter::EncodableValue> &method_call,\n      std::unique_ptr<flutter::MethodResult<flutter::EncodableValue>> result);\n\n  std::optional<LRESULT> HandleWindowProc(\n              HWND hwnd,\n              UINT message,\n              WPARAM wparam,\n              LPARAM lparam);\n\n  int window_proc_id = -1;\n  UINT WM_TASKBARCREATED = 0;\n  flutter::PluginRegistrarWindows *registrar;\n};\n\n}  // namespace window_ext\n\n#endif  // FLUTTER_PLUGIN_WINDOW_EXT_PLUGIN_H_\n"
  },
  {
    "path": "plugins/window_ext/windows/window_ext_plugin_c_api.cpp",
    "content": "#include \"include/window_ext/window_ext_plugin_c_api.h\"\n\n#include <flutter/plugin_registrar_windows.h>\n\n#include \"window_ext_plugin.h\"\n\nvoid WindowExtPluginCApiRegisterWithRegistrar(\n    FlutterDesktopPluginRegistrarRef registrar) {\n  window_ext::WindowExtPlugin::RegisterWithRegistrar(\n      flutter::PluginRegistrarManager::GetInstance()\n          ->GetRegistrar<flutter::PluginRegistrarWindows>(registrar));\n}\n"
  },
  {
    "path": "pubspec.yaml",
    "content": "name: bett_box\ndescription: A multi-platform proxy client based on Mihomo, simple and easy to use, open-source and ad-free.\npublish_to: 'none'\nversion: 1.17.3+2026050901\nenvironment:\n  sdk: '>=3.9.0 <4.0.0'\n\ndependencies:\n  flutter:\n    sdk: flutter\n  flutter_localizations:\n    sdk: flutter\n  intl: ^0.20.0\n  path_provider: ^2.1.4\n  path: ^1.9.0\n  shared_preferences: ^2.5.5\n  window_manager: ^0.5.1\n  dynamic_color: ^1.8.1\n  vclibs: ^0.1.3\n  proxy:\n    path: plugins/proxy\n  window_ext:\n    path: plugins/window_ext\n  launch_at_startup: ^0.5.1\n  json_annotation: ^4.9.0\n  file_picker: ^8.3.7\n  mobile_scanner: ^7.2.0\n  app_links: ^6.3.3\n  win32_registry: ^2.0.0\n  tray_manager: ^0.5.2\n  collection: ^1.18.0\n  animations: ^2.1.2\n  package_info_plus: ^9.0.1\n  url_launcher: ^6.3.2\n  freezed_annotation: ^3.1.0\n  image_picker: ^1.2.1\n  webdav_client: ^1.2.2\n  dio: ^5.9.2\n  win32: ^5.15.0\n  ffi: ^2.2.0\n  re_editor: ^0.8.0\n  re_highlight: ^0.0.3\n  archive: ^4.0.7\n  lpinyin: ^2.0.3\n  emoji_regex: ^0.0.5\n  hotkey_manager: ^0.2.3\n  uni_platform: ^0.1.3\n  device_info_plus: ^12.4.0\n  sqflite: ^2.3.0\n  sqflite_common_ffi: ^2.3.7\n  yaml: ^3.1.2\n  connectivity_plus: 7.0.0\n  screen_retriever: ^0.2.0\n  defer_pointer: ^0.0.2\n  flutter_riverpod: ^2.6.1\n  riverpod_annotation: ^2.6.1\n  riverpod: ^2.6.1\n  material_color_utilities: ^0.11.1\n  flutter_js: ^0.8.7\n  flutter_svg: ^2.2.4\n  xml: ^6.5.0\n  flutter_cache_manager: ^3.4.1\n  crypto: ^3.0.7\n  flutter_acrylic: ^1.1.4\n  google_nav_bar: ^5.0.7\n  wakelock_plus: ^1.4.0\n  flutter_spinkit: ^5.2.2\n  synchronized: ^3.4.0\n  flutter_displaymode: ^0.7.0\ndev_dependencies:\n  flutter_test:\n    sdk: flutter\n  flutter_lints: ^6.0.0\n  ffigen: ^20.1.1\n  json_serializable: ^6.9.5\n  build_runner: ^2.4.15\n  args: ^2.4.2\n  freezed: ^3.1.0\n  riverpod_generator: ^2.6.4\n  custom_lint: ^0.7.6\n  intl_utils: ^2.8.11\n\nflutter:\n  generate: true\n  uses-material-design: true\n  assets:\n    - assets/data/\n    - assets/fonts/\n    - assets/images/\n    - assets/images/avatars/\n  fonts:\n    - family: JetBrainsMono\n      fonts:\n        - asset: assets/fonts/JetBrainsMono-Regular.ttf\n    - family: Twemoji\n      fonts:\n        - asset: assets/fonts/Twemoji.Mozilla.ttf\n    - family: Icons\n      fonts:\n        - asset: assets/fonts/Icons.ttf\n    - family: HarmonyOS_Sans\n      fonts:\n        - asset: assets/fonts/HarmonyOS_Sans_SC_Regular.ttf\nffigen:\n  name: \"ClashFFI\"\n  output: 'lib/clash/generated/clash_ffi.dart'\n  headers:\n    entry-points:\n      - 'libclash/android/arm64-v8a/libclash.h'\nflutter_intl:\n  enabled: true\n  class_name: AppLocalizations\n  arb_dir: arb\n  output_dir: lib/l10n"
  },
  {
    "path": "services/helper/Cargo.toml",
    "content": "[package]\nname = \"helper\"\nversion = \"0.1.0\"\nedition = \"2021\"\n\n[[bin]]\nname = \"helper\"\npath = \"src/main.rs\"\n\n[dependencies]\nwindows-service = { version = \"0.7.0\", optional = true }\ntokio = { version = \"1\", features = [\"full\"] }\nanyhow = \"1.0.93\"\nwarp = \"0.3.7\"\nserde = { version = \"1.0.215\", features = [\"derive\"] }\nserde_json = \"1.0\"\nonce_cell = \"1.20.2\"\nsha2 = \"0.10.8\"\nfs2 = \"0.4\"\nhmac = \"0.12\"\nhex = \"0.4\"\nbytes = \"1\"\n\n\n[profile.release]\npanic = \"abort\"\ncodegen-units = 1\nlto = true\nopt-level = \"s\"\n"
  },
  {
    "path": "services/helper/build.rs",
    "content": "fn main() {\n    let version = std::env::var(\"TOKEN\").unwrap_or_default();\n    println!(\"cargo:rustc-env=TOKEN={}\", version);\n    println!(\"cargo:rerun-if-env-changed=TOKEN\");\n}\n"
  },
  {
    "path": "services/helper/src/main.rs",
    "content": "#[cfg(not(all(feature = \"windows-service\", target_os = \"windows\")))]\nuse tokio::runtime::Runtime;\n#[cfg(not(all(feature = \"windows-service\", target_os = \"windows\")))]\nuse crate::service::hub::run_service;\n\nmod service;\n\n#[cfg(all(feature = \"windows-service\", target_os = \"windows\"))]\npub fn main() -> windows_service::Result<()> {\n    service::windows::main()\n}\n\n#[cfg(not(all(feature = \"windows-service\", target_os = \"windows\")))]\nfn main() {\n    if let Ok(rt) = Runtime::new() {\n        rt.block_on(async {\n            let _ = run_service().await;\n        });\n    }\n}\n"
  },
  {
    "path": "services/helper/src/service/hub.rs",
    "content": "use once_cell::sync::Lazy;\nuse serde::{Deserialize, Serialize};\nuse sha2::{Digest, Sha256};\nuse std::collections::VecDeque;\nuse std::fs::Metadata;\nuse std::fs::{File, OpenOptions};\nuse std::io::{BufRead, BufReader, Error, Read};\nuse std::process::{Command, Stdio};\nuse std::sync::{Arc, Mutex};\nuse std::{io, thread};\nuse warp::{Filter, Reply};\nuse hmac::{Hmac, Mac};\nuse std::time::{SystemTime, UNIX_EPOCH};\nuse bytes::Bytes;\n\n#[allow(unused_imports)]\nuse fs2::FileExt;\n\nconst LISTEN_PORT: u16 = 45678;\nconst TIME_WINDOW_SECS: u64 = 5;\nconst MAX_LOG_ENTRIES: usize = 100;\nconst HASH_BUFFER_SIZE: usize = 65536;\n\ntype HmacSha256 = Hmac<Sha256>;\n\n#[derive(Debug, Deserialize, Serialize, Clone)]\npub struct StartParams {\n    pub path: String,\n    pub arg: String,\n    pub home_dir: Option<String>,\n}\n\n#[derive(Debug, Clone)]\nstruct CachedFileHash {\n    path: String,\n    size: u64,\n    modified_nanos: u128,\n    hash: String,\n}\n\nfn metadata_signature(metadata: &Metadata) -> Result<(u64, u128), Error> {\n    let modified_nanos = metadata\n        .modified()?\n        .duration_since(UNIX_EPOCH)\n        .unwrap_or_default()\n        .as_nanos();\n    Ok((metadata.len(), modified_nanos))\n}\n\nfn sha256_file_with_lock(file: &File, path: &str) -> Result<String, Error> {\n    let metadata = file.metadata()?;\n    let (size, modified_nanos) = metadata_signature(&metadata)?;\n\n    if let Ok(cache_guard) = FILE_HASH_CACHE.lock() {\n        if let Some(cache) = cache_guard.as_ref() {\n            if cache.path == path\n                && cache.size == size\n                && cache.modified_nanos == modified_nanos\n            {\n                return Ok(cache.hash.clone());\n            }\n        }\n    }\n\n    let mut hasher = Sha256::new();\n    let mut buffer = [0; HASH_BUFFER_SIZE];\n    let mut reader = BufReader::new(file);\n\n    loop {\n        let bytes_read = reader.read(&mut buffer)?;\n        if bytes_read == 0 {\n            break;\n        }\n        hasher.update(&buffer[..bytes_read]);\n    }\n\n    let hash = format!(\"{:x}\", hasher.finalize());\n    if let Ok(mut cache_guard) = FILE_HASH_CACHE.lock() {\n        *cache_guard = Some(CachedFileHash {\n            path: path.to_string(),\n            size,\n            modified_nanos,\n            hash: hash.clone(),\n        });\n    }\n    Ok(hash)\n}\n\nstatic LOGS: Lazy<Arc<Mutex<VecDeque<String>>>> =\n    Lazy::new(|| Arc::new(Mutex::new(VecDeque::with_capacity(MAX_LOG_ENTRIES))));\nstatic PROCESS: Lazy<Arc<Mutex<Option<std::process::Child>>>> =\n    Lazy::new(|| Arc::new(Mutex::new(None)));\nstatic AUTH_KEY: Lazy<Arc<Mutex<Option<Vec<u8>>>>> =\n    Lazy::new(|| Arc::new(Mutex::new(None)));\nstatic FILE_HASH_CACHE: Lazy<Arc<Mutex<Option<CachedFileHash>>>> =\n    Lazy::new(|| Arc::new(Mutex::new(None)));\n\nfn init_auth_key() {\n    if let Ok(key_hex) = std::env::var(\"HELPER_AUTH_KEY\") {\n        if let Ok(key) = hex::decode(&key_hex) {\n            if let Ok(mut auth_key) = AUTH_KEY.lock() {\n                *auth_key = Some(key);\n                log_message(\"Auth key initialized\".to_string());\n            }\n        }\n    }\n}\n\nfn verify_request(timestamp: u64, signature: &str, body: &str) -> bool {\n    let key = match AUTH_KEY.lock() {\n        Ok(guard) => match guard.as_ref() {\n            Some(k) => k.clone(),\n            None => {\n                log_message(\"Auth key not initialized, skipping verification\".to_string());\n                return true; // Backward compatible: allow if no key\n            }\n        },\n        Err(_) => return false,\n    };\n    \n    let now = SystemTime::now()\n        .duration_since(UNIX_EPOCH)\n        .unwrap()\n        .as_secs();\n    \n    if now.abs_diff(timestamp) > TIME_WINDOW_SECS {\n        log_message(format!(\"Request timestamp out of window: {} vs {}\", timestamp, now));\n        return false;\n    }\n    \n    let message = format!(\"{}:{}\", timestamp, body);\n    let mut mac = match HmacSha256::new_from_slice(&key) {\n        Ok(m) => m,\n        Err(_) => return false,\n    };\n    mac.update(message.as_bytes());\n    let expected_signature = hex::encode(mac.finalize().into_bytes());\n    \n    signature == expected_signature\n}\n\nfn start(start_params: StartParams) -> String {\n    // Open file and add shared lock\n    let file = match OpenOptions::new()\n        .read(true)\n        .open(&start_params.path) \n    {\n        Ok(f) => f,\n        Err(e) => {\n            let msg = format!(\"Failed to open file: {}\", e);\n            log_message(msg.clone());\n            return msg;\n        }\n    };\n    \n    // Lock (prevent other processes from modifying)\n    if let Err(e) = file.lock_shared() {\n        let msg = format!(\"Failed to lock file: {}\", e);\n        log_message(msg.clone());\n        return msg;\n    }\n    \n    // Calculate SHA256 while holding lock\n    let sha256 = match sha256_file_with_lock(&file, &start_params.path) {\n        Ok(hash) => hash,\n        Err(e) => {\n            let _ = file.unlock();\n            let msg = format!(\"Failed to calculate SHA256: {}\", e);\n            log_message(msg.clone());\n            return msg;\n        }\n    };\n    \n    // Verify hash\n    if sha256 != env!(\"TOKEN\") {\n        let _ = file.unlock();\n        let msg = format!(\"The SHA256 hash of the program requesting execution is: {}. The helper program only allows execution of applications with the SHA256 hash: {}.\", sha256, env!(\"TOKEN\"));\n        log_message(msg.clone());\n        return msg;\n    }\n    \n\n    let _ = file.unlock();\n    drop(file);\n    \n    // Start process after lock is released\n    stop();\n    let mut process = PROCESS.lock().unwrap();\n    \n    let mut command = Command::new(&start_params.path);\n    command.stderr(Stdio::piped()).arg(&start_params.arg);\n    \n    if let Some(home_dir) = &start_params.home_dir {\n        command.env(\"SAFE_PATHS\", home_dir);\n    }\n    \n    let result = match command.spawn() {\n        Ok(child) => {\n            *process = Some(child);\n            \n            // Start log collection thread\n            if let Some(ref mut child) = *process {\n                if let Some(stderr) = child.stderr.take() {\n                    let reader = io::BufReader::new(stderr);\n                    thread::spawn(move || {\n                        for line in reader.lines() {\n                            match line {\n                                Ok(output) => {\n                                    log_message(output);\n                                }\n                                Err(_) => {\n                                    break;\n                                }\n                            }\n                        }\n                    });\n                }\n            }\n            \"\".to_string()\n        }\n        Err(e) => {\n            log_message(e.to_string());\n            e.to_string()\n        }\n    };\n    \n    result\n}\n\nfn stop() -> String {\n    let mut process = PROCESS.lock().unwrap();\n    if let Some(mut child) = process.take() {\n        let _ = child.kill();\n        let _ = child.wait();\n    }\n    *process = None;\n    \"\".to_string()\n}\n\nfn log_message(message: String) {\n    let mut log_buffer = LOGS.lock().unwrap();\n    if log_buffer.len() == MAX_LOG_ENTRIES {\n        log_buffer.pop_front();\n    }\n    log_buffer.push_back(message);\n}\n\n\nfn check_authentication(timestamp: Option<u64>, signature: Option<String>, body: &[u8]) -> bool {\n    let auth_enabled = AUTH_KEY.lock().unwrap().is_some();\n    if !auth_enabled {\n        return true;\n    }\n\n    if let (Some(ts), Some(sig)) = (timestamp, signature) {\n        let body_str = String::from_utf8_lossy(body);\n        if verify_request(ts, &sig, &body_str) {\n            return true;\n        }\n    }\n    log_message(\"Authentication failed\".to_string());\n    false\n}\n\npub async fn run_service() -> anyhow::Result<()> {\n    init_auth_key();\n    \n    let api_ping = warp::get().and(warp::path(\"ping\")).map(|| env!(\"TOKEN\"));\n\n    let api_start = warp::post()\n        .and(warp::path(\"start\"))\n        .and(warp::header::optional::<u64>(\"X-Timestamp\"))\n        .and(warp::header::optional::<String>(\"X-Signature\"))\n        .and(warp::body::bytes())\n        .and_then(|timestamp: Option<u64>, signature: Option<String>, body_bytes: Bytes| async move {\n            if !check_authentication(timestamp, signature, &body_bytes) {\n                return Ok::<_, warp::Rejection>(warp::reply::with_status(\n                    \"Unauthorized\".to_string(),\n                    warp::http::StatusCode::UNAUTHORIZED\n                ).into_response());\n            }\n\n            let start_params: StartParams = match serde_json::from_slice(&body_bytes) {\n                Ok(p) => p,\n                Err(_) => return Ok(warp::reply::with_status(\n                    \"Invalid JSON body\".to_string(),\n                    warp::http::StatusCode::BAD_REQUEST\n                ).into_response()),\n            };\n\n            let result = tokio::task::spawn_blocking(move || {\n                start(start_params)\n            }).await.unwrap_or_else(|e| e.to_string());\n\n            Ok(warp::reply::with_status(result, warp::http::StatusCode::OK).into_response())\n        });\n\n    let api_stop = warp::post()\n        .and(warp::path(\"stop\"))\n        .and(warp::header::optional::<u64>(\"X-Timestamp\"))\n        .and(warp::header::optional::<String>(\"X-Signature\"))\n        .map(|timestamp: Option<u64>, signature: Option<String>| {\n            if !check_authentication(timestamp, signature, b\"\") {\n                return warp::reply::with_status(\"Unauthorized\", warp::http::StatusCode::UNAUTHORIZED).into_response();\n            }\n            warp::reply::with_status(stop(), warp::http::StatusCode::OK).into_response()\n        });\n\n    let api_logs = warp::get()\n        .and(warp::path(\"logs\"))\n        .and(warp::header::optional::<u64>(\"X-Timestamp\"))\n        .and(warp::header::optional::<String>(\"X-Signature\"))\n        .map(|timestamp: Option<u64>, signature: Option<String>| {\n            if !check_authentication(timestamp, signature, b\"\") {\n                return warp::reply::with_status(\"Unauthorized\".to_string(), warp::http::StatusCode::UNAUTHORIZED).into_response();\n            }\n            \n            let log_str = LOGS.lock().unwrap()\n                .iter()\n                .cloned()\n                .collect::<Vec<String>>()\n                .join(\"\\n\");\n                \n            warp::reply::with_header(log_str, \"Content-Type\", \"text/plain\").into_response()\n        });\n\n    let routes = api_ping\n        .or(api_start)\n        .or(api_stop)\n        .or(api_logs);\n\n    warp::serve(routes)\n        .run(([127, 0, 0, 1], LISTEN_PORT))\n        .await;\n\n    Ok(())\n}\n"
  },
  {
    "path": "services/helper/src/service/mod.rs",
    "content": "pub mod hub;\n#[cfg(all(feature = \"windows-service\", target_os = \"windows\"))]\npub mod windows;\n\n\n\n\n\n"
  },
  {
    "path": "services/helper/src/service/windows.rs",
    "content": "use crate::service::hub::run_service;\n\nuse std::ffi::OsString;\nuse std::time::Duration;\n\nuse tokio::runtime::Runtime;\n\nuse windows_service::{\n    define_windows_service,\n    service::{\n        ServiceControl, ServiceControlAccept, ServiceExitCode, ServiceState, ServiceStatus,\n        ServiceType,\n    },\n    service_control_handler::{self, ServiceControlHandlerResult},\n    service_dispatcher, Result,\n};\n\nconst SERVICE_NAME: &str = \"BettboxHelperService\";\n\nconst SERVICE_TYPE: ServiceType = ServiceType::OWN_PROCESS;\n\npub fn main() -> Result<()> {\n    start_service()\n}\n\npub fn start_service() -> Result<()> {\n    service_dispatcher::start(SERVICE_NAME, service_entry)\n}\n\ndefine_windows_service!(service_entry, service_main);\n\npub fn service_main(_arguments: Vec<OsString>) {\n    if let Ok(rt) = Runtime::new() {\n        rt.block_on(async {\n            if let Err(e) = run_windows_service().await {\n                let log_path = std::env::temp_dir().join(\"bettbox_helper_error.log\");\n                let ts = std::time::SystemTime::now()\n                    .duration_since(std::time::UNIX_EPOCH)\n                    .map(|d| d.as_secs())\n                    .unwrap_or(0);\n                let msg = format!(\"[{}] service error: {}\\n\", ts, e);\n                let _ = std::fs::OpenOptions::new()\n                    .create(true).append(true)\n                    .open(log_path)\n                    .map(|mut f| { use std::io::Write; let _ = f.write_all(msg.as_bytes()); });\n            }\n        });\n    }\n}\n\nasync fn run_windows_service() -> anyhow::Result<()> {\n    let status_handle = service_control_handler::register(\n        SERVICE_NAME,\n        move |event| -> ServiceControlHandlerResult {\n            match event {\n                ServiceControl::Interrogate => ServiceControlHandlerResult::NoError,\n                ServiceControl::Stop => std::process::exit(0),\n                _ => ServiceControlHandlerResult::NotImplemented,\n            }\n        },\n    )?;\n\n    status_handle.set_service_status(ServiceStatus {\n        service_type: SERVICE_TYPE,\n        current_state: ServiceState::StartPending,\n        controls_accepted: ServiceControlAccept::empty(),\n        exit_code: ServiceExitCode::Win32(0),\n        checkpoint: 0,\n        wait_hint: Duration::from_secs(5),\n        process_id: None,\n    })?;\n\n    status_handle.set_service_status(ServiceStatus {\n        service_type: SERVICE_TYPE,\n        current_state: ServiceState::Running,\n        controls_accepted: ServiceControlAccept::STOP,\n        exit_code: ServiceExitCode::Win32(0),\n        checkpoint: 0,\n        wait_hint: Duration::default(),\n        process_id: None,\n    })?;\n\n    let result = run_service().await;\n\n    let exit_code = if result.is_ok() {\n        ServiceExitCode::Win32(0)\n    } else {\n        ServiceExitCode::ServiceSpecific(1)\n    };\n    let _ = status_handle.set_service_status(ServiceStatus {\n        service_type: SERVICE_TYPE,\n        current_state: ServiceState::Stopped,\n        controls_accepted: ServiceControlAccept::empty(),\n        exit_code,\n        checkpoint: 0,\n        wait_hint: Duration::default(),\n        process_id: None,\n    });\n\n    result\n}\n"
  },
  {
    "path": "setup.dart",
    "content": "// ignore_for_file: avoid_print\n\nimport 'dart:convert';\nimport 'dart:io';\n\nimport 'package:args/command_runner.dart';\nimport 'package:crypto/crypto.dart';\nimport 'package:path/path.dart';\n\nenum Target { windows, linux, android, macos }\n\nextension TargetExt on Target {\n  String get os {\n    if (this == Target.macos) {\n      return 'darwin';\n    }\n    return name;\n  }\n\n  bool get same {\n    if (this == Target.android) {\n      return true;\n    }\n    if (Platform.isWindows && this == Target.windows) {\n      return true;\n    }\n    if (Platform.isLinux && this == Target.linux) {\n      return true;\n    }\n    if (Platform.isMacOS && this == Target.macos) {\n      return true;\n    }\n    return false;\n  }\n\n  String get dynamicLibExtensionName {\n    final String extensionName;\n    switch (this) {\n      case Target.android || Target.linux:\n        extensionName = '.so';\n        break;\n      case Target.windows:\n        extensionName = '.dll';\n        break;\n      case Target.macos:\n        extensionName = '.dylib';\n        break;\n    }\n    return extensionName;\n  }\n\n  String get executableExtensionName {\n    final String extensionName;\n    switch (this) {\n      case Target.windows:\n        extensionName = '.exe';\n        break;\n      default:\n        extensionName = '';\n        break;\n    }\n    return extensionName;\n  }\n}\n\nenum Mode { core, lib }\n\nenum Arch { amd64, arm64, arm }\n\nclass BuildItem {\n  Target target;\n  Arch? arch;\n  String? archName;\n\n  BuildItem({required this.target, this.arch, this.archName});\n\n  @override\n  String toString() {\n    return 'BuildLibItem{target: $target, arch: $arch, archName: $archName}';\n  }\n}\n\nclass Build {\n  static List<BuildItem> get buildItems => [\n    BuildItem(target: Target.macos, arch: Arch.arm64),\n    BuildItem(target: Target.macos, arch: Arch.amd64),\n    BuildItem(target: Target.linux, arch: Arch.arm64),\n    BuildItem(target: Target.linux, arch: Arch.amd64),\n    BuildItem(target: Target.windows, arch: Arch.amd64),\n    BuildItem(target: Target.windows, arch: Arch.arm64),\n    BuildItem(target: Target.android, arch: Arch.arm, archName: 'armeabi-v7a'),\n    BuildItem(target: Target.android, arch: Arch.arm64, archName: 'arm64-v8a'),\n    BuildItem(target: Target.android, arch: Arch.amd64, archName: 'x86_64'),\n  ];\n\n  static String get appName => 'Bettbox';\n\n  static String get coreName => 'BettboxCore';\n\n  static String get libName => 'libclash';\n\n  static String get outDir => join(current, libName);\n\n  static String get _coreDir => join(current, 'core');\n\n  static String get _servicesDir => join(current, 'services', 'helper');\n\n  static String get distPath => join(current, 'dist');\n\n  static String _getCc(BuildItem buildItem) {\n    final environment = Platform.environment;\n    if (buildItem.target == Target.android) {\n      final ndk = environment['ANDROID_NDK'];\n      assert(ndk != null);\n      final prebuiltDir = Directory(\n        join(ndk!, 'toolchains', 'llvm', 'prebuilt'),\n      );\n      final prebuiltDirList = prebuiltDir.listSync();\n      final map = {\n        'armeabi-v7a': 'armv7a-linux-androideabi21-clang',\n        'arm64-v8a': 'aarch64-linux-android21-clang',\n        'x86': 'i686-linux-android21-clang',\n        'x86_64': 'x86_64-linux-android21-clang',\n      };\n      return join(prebuiltDirList.first.path, 'bin', map[buildItem.archName]);\n    }\n    return 'gcc';\n  }\n\n  static String getTags(BuildItem buildItem) {\n    final baseTags = 'with_gvisor,no_fake_tcp';\n    if (buildItem.target == Target.android &&\n        buildItem.archName == 'armeabi-v7a') {\n      return '$baseTags,with_low_memory';\n    }\n    return baseTags;\n  }\n\n  static Future<void> exec(\n    List<String> executable, {\n    String? name,\n    Map<String, String>? environment,\n    String? workingDirectory,\n    bool runInShell = true,\n  }) async {\n    if (name != null) print('run $name');\n    final process = await Process.start(\n      executable[0],\n      executable.sublist(1),\n      environment: environment,\n      workingDirectory: workingDirectory,\n      runInShell: runInShell,\n    );\n    process.stdout.listen((data) {\n      print(utf8.decode(data));\n    });\n    process.stderr.listen((data) {\n      print(utf8.decode(data));\n    });\n    final exitCode = await process.exitCode;\n    if (exitCode != 0 && name != null) throw '$name error';\n  }\n\n  static Future<String> calcSha256(String filePath) async {\n    final file = File(filePath);\n    if (!await file.exists()) {\n      throw 'File not exists';\n    }\n    final stream = file.openRead();\n    return sha256.convert(await stream.reduce((a, b) => a + b)).toString();\n  }\n\n  static Future<List<String>> buildCore({\n    required Mode mode,\n    required Target target,\n    Arch? arch,\n    bool compatible = false,\n  }) async {\n    final isLib = mode == Mode.lib;\n\n    final items = buildItems.where((element) {\n      return element.target == target &&\n          (arch == null ? true : element.arch == arch);\n    }).toList();\n\n    final List<String> corePaths = [];\n\n    for (final item in items) {\n      final outFileDir = join(outDir, item.target.name, item.archName);\n\n      final file = File(outFileDir);\n      if (file.existsSync()) {\n        file.deleteSync(recursive: true);\n      }\n\n      final fileName = isLib\n          ? '$libName${item.target.dynamicLibExtensionName}'\n          : '$coreName${item.target.executableExtensionName}';\n      final outPath = join(outFileDir, fileName);\n      corePaths.add(outPath);\n\n      final Map<String, String> env = {};\n      env['GOOS'] = item.target.os;\n      if (item.arch != null) {\n        env['GOARCH'] = item.arch!.name;\n      }\n      if (item.arch == Arch.amd64 &&\n          (item.target == Target.windows ||\n              item.target == Target.linux ||\n              item.target == Target.macos)) {\n        env['GOAMD64'] = compatible ? 'v1' : 'v3';\n      }\n      if (isLib) {\n        env['CGO_ENABLED'] = '1';\n        env['CC'] = _getCc(item);\n        env['CFLAGS'] = '-O3 -Werror';\n      } else {\n        env['CGO_ENABLED'] = '0';\n      }\n\n      final buildTags = getTags(item);\n\n      await exec(\n        ['go', 'mod', 'tidy'],\n        name: 'go mod tidy',\n        environment: env,\n        workingDirectory: _coreDir,\n      );\n\n      final execLines = [\n        'go',\n        'build',\n        '-trimpath',\n        '-ldflags=-w -s${item.target == Target.android && (item.arch == Arch.arm64 || item.arch == Arch.amd64) ? ' -extldflags \"-Wl,-z,max-page-size=16384\"' : ''}',\n        '-tags=$buildTags',\n        if (isLib) '-buildmode=c-shared',\n        '-o',\n        outPath,\n      ];\n      await exec(\n        execLines,\n        name: 'build core',\n        environment: env,\n        workingDirectory: _coreDir,\n      );\n    }\n\n    return corePaths;\n  }\n\n  static Future<void> buildHelper(Target target, String token) async {\n    await exec(\n      ['cargo', 'build', '--release', '--features', 'windows-service'],\n      environment: {'TOKEN': token},\n      name: 'build helper',\n      workingDirectory: _servicesDir,\n    );\n    final outPath = join(\n      _servicesDir,\n      'target',\n      'release',\n      'helper${target.executableExtensionName}',\n    );\n    final targetPath = join(\n      outDir,\n      target.name,\n      'BettboxHelperService${target.executableExtensionName}',\n    );\n    await File(outPath).copy(targetPath);\n  }\n\n  static List<String> getExecutable(String command) {\n    return command.split(' ');\n  }\n\n  static Future<void> getDistributor() async {\n    final distributorDir = join(\n      current,\n      'plugins',\n      'flutter_distributor',\n      'packages',\n      'flutter_distributor',\n    );\n\n    await exec(\n      name: 'clean distributor',\n      Build.getExecutable('flutter clean'),\n      workingDirectory: distributorDir,\n    );\n    await exec(\n      name: 'upgrade distributor',\n      Build.getExecutable('flutter pub upgrade'),\n      workingDirectory: distributorDir,\n    );\n    await exec(\n      name: 'get distributor',\n      Build.getExecutable('dart pub global activate -s path $distributorDir'),\n    );\n  }\n\n  static void copyFile(String sourceFilePath, String destinationFilePath) {\n    final sourceFile = File(sourceFilePath);\n    if (!sourceFile.existsSync()) {\n      throw 'SourceFilePath not exists';\n    }\n    final destinationFile = File(destinationFilePath);\n    final destinationDirectory = destinationFile.parent;\n    if (!destinationDirectory.existsSync()) {\n      destinationDirectory.createSync(recursive: true);\n    }\n    try {\n      sourceFile.copySync(destinationFilePath);\n      print('File copied successfully!');\n    } catch (e) {\n      print('Failed to copy file: $e');\n    }\n  }\n}\n\nclass BuildCommand extends Command {\n  Target target;\n\n  BuildCommand({required this.target}) {\n    if (target == Target.android || target == Target.linux) {\n      argParser.addOption(\n        'arch',\n        valueHelp: arches.map((e) => e.name).join(','),\n        help: 'The $name build desc',\n      );\n    } else {\n      argParser.addOption('arch', help: 'The $name build archName');\n    }\n    argParser.addOption(\n      'out',\n      valueHelp: [if (target.same) 'app', 'core'].join(','),\n      help: 'The $name build arch',\n    );\n    argParser.addOption(\n      'env',\n      valueHelp: ['pre', 'stable'].join(','),\n      help: 'The $name build env',\n    );\n    argParser.addFlag(\n      'compatible',\n      help: 'Build with GOAMD64=v2 for broader compatibility on amd64',\n    );\n  }\n\n  @override\n  String get description => 'build $name application';\n\n  @override\n  String get name => target.name;\n\n  List<Arch> get arches => Build.buildItems\n      .where((element) => element.target == target && element.arch != null)\n      .map((e) => e.arch!)\n      .toList();\n\n  Future<void> _getLinuxDependencies(Arch arch) async {\n    await Build.exec(Build.getExecutable('sudo apt update -y'));\n    await Build.exec(\n      Build.getExecutable('sudo apt install -y ninja-build libgtk-3-dev'),\n    );\n    await Build.exec(\n      Build.getExecutable('sudo apt install -y libayatana-appindicator3-dev'),\n    );\n    await Build.exec(\n      Build.getExecutable('sudo apt-get install -y libkeybinder-3.0-dev'),\n    );\n    await Build.exec(Build.getExecutable('sudo apt install -y locate'));\n    if (arch == Arch.amd64) {\n      await Build.exec(\n        Build.getExecutable('sudo apt install -y rpm patchelf libfuse2'),\n      );\n\n      final downloadName = arch == Arch.amd64 ? 'x86_64' : 'aarch64';\n      await Build.exec(\n        Build.getExecutable(\n          'wget -O appimagetool https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-$downloadName.AppImage',\n        ),\n      );\n      await Build.exec(Build.getExecutable('chmod +x appimagetool'));\n      await Build.exec(\n        Build.getExecutable('sudo mv appimagetool /usr/local/bin/'),\n      );\n    }\n  }\n\n  Future<void> _getMacosDependencies() async {\n    await Build.exec(Build.getExecutable('npm install -g appdmg'));\n  }\n\n  Future<void> _setMacOSCompatibleBuild(bool enable) async {\n    final infoPlistPath = 'macos/Runner/Info.plist';\n    final file = File(infoPlistPath);\n    \n    if (!await file.exists()) {\n      print('Warning: Info.plist not found at $infoPlistPath');\n      return;\n    }\n    \n    var content = await file.readAsString();\n    \n    // Check if FLTDisableImpeller key exists\n    if (content.contains('<key>FLTDisableImpeller</key>')) {\n      // Update existing key\n      if (enable) {\n        content = content.replaceAll(\n          RegExp(r'<key>FLTDisableImpeller</key>\\s*<(?:true|false)/>'),\n          '<key>FLTDisableImpeller</key>\\n\\t<true/>',\n        );\n      } else {\n        content = content.replaceAll(\n          RegExp(r'<key>FLTDisableImpeller</key>\\s*<(?:true|false)/>'),\n          '<key>FLTDisableImpeller</key>\\n\\t<false/>',\n        );\n      }\n    } else {\n      // Add new key before </dict>\n      final impellerEntry = enable\n          ? '\\t<key>FLTDisableImpeller</key>\\n\\t<true/>\\n'\n          : '\\t<key>FLTDisableImpeller</key>\\n\\t<false/>\\n';\n      content = content.replaceFirst(\n        '</dict>\\n</plist>',\n        '$impellerEntry</dict>\\n</plist>',\n      );\n    }\n    \n    await file.writeAsString(content);\n    print('macOS ${enable ? \"Compatible\" : \"Standard\"} build: FLTDisableImpeller set to $enable');\n  }\n\n  Future<void> _buildDistributor({\n    required Target target,\n    required String targets,\n    String args = '',\n    required String env,\n  }) async {\n    final sentryDsn = Platform.environment['SENTRY_DSN'] ?? '';\n    final sentryArg = sentryDsn.isNotEmpty\n        ? ' --build-dart-define=SENTRY_DSN=$sentryDsn'\n        : '';\n\n    await Build.getDistributor();\n    await Build.exec(\n      name: name,\n      Build.getExecutable(\n        'flutter_distributor package --skip-clean --platform ${target.name} --targets $targets --flutter-build-args=verbose$args$sentryArg --build-dart-define=APP_ENV=$env',\n      ),\n    );\n  }\n\n  Future<String?> get systemArch async {\n    if (Platform.isWindows) {\n      return Platform.environment['PROCESSOR_ARCHITECTURE'];\n    } else if (Platform.isLinux || Platform.isMacOS) {\n      final result = await Process.run('uname', ['-m']);\n      return result.stdout.toString().trim();\n    }\n    return null;\n  }\n\n  @override\n  Future<void> run() async {\n    final mode = target == Target.android ? Mode.lib : Mode.core;\n    final String out = argResults?['out'] ?? (target.same ? 'app' : 'core');\n    final archName = argResults?['arch'];\n    final env = argResults?['env'] ?? 'pre';\n    final currentArches = arches\n        .where((element) => element.name == archName)\n        .toList();\n    final arch = currentArches.isEmpty ? null : currentArches.first;\n\n    if (arch == null && target != Target.android) {\n      throw 'Invalid arch parameter';\n    }\n\n    final bool compatible = argResults?['compatible'] ?? false;\n\n    final corePaths = await Build.buildCore(\n      target: target,\n      arch: arch,\n      mode: mode,\n      compatible: compatible,\n    );\n\n    if (out != 'app') {\n      return;\n    }\n\n    final String desc = compatible ? '$archName-compatible' : (archName ?? '');\n\n    switch (target) {\n      case Target.windows:\n        final token = target != Target.android\n            ? await Build.calcSha256(corePaths.first)\n            : null;\n        Build.buildHelper(target, token!);\n        _buildDistributor(\n          target: target,\n          targets: 'exe',\n          args: ' --description $desc --build-dart-define=CORE_SHA256=$token',\n          env: env,\n        );\n        return;\n      case Target.linux:\n        final targetMap = {Arch.arm64: 'linux-arm64', Arch.amd64: 'linux-x64'};\n        final targets = [\n          'deb',\n          if (arch == Arch.amd64) 'appimage',\n          if (arch == Arch.amd64) 'rpm',\n        ].join(',');\n        final defaultTarget = targetMap[arch];\n        await _getLinuxDependencies(arch!);\n        _buildDistributor(\n          target: target,\n          targets: targets,\n          args: ' --description $desc --build-target-platform $defaultTarget',\n          env: env,\n        );\n        return;\n      case Target.android:\n        final targetMap = {\n          Arch.arm: 'android-arm',\n          Arch.arm64: 'android-arm64',\n          Arch.amd64: 'android-x64',\n        };\n        final defaultArches = [Arch.arm, Arch.arm64, Arch.amd64];\n        final defaultTargets = defaultArches\n            .where((element) => arch == null ? true : element == arch)\n            .map((e) => targetMap[e])\n            .toList();\n\n        final buildArgs = archName == 'universal'\n            ? ' --build-target-platform ${defaultTargets.join(\",\")} --description universal'\n            : ',split-per-abi --build-target-platform ${defaultTargets.join(\",\")}';\n\n        _buildDistributor(\n          target: target,\n          targets: 'apk',\n          args: buildArgs,\n          env: env,\n        );\n        return;\n      case Target.macos:\n        await _getMacosDependencies();\n        // For compatible build, disable Impeller and use Skia renderer\n        if (compatible) {\n          await _setMacOSCompatibleBuild(true);\n        } else {\n          await _setMacOSCompatibleBuild(false);\n        }\n        _buildDistributor(\n          target: target,\n          targets: 'dmg',\n          args: ' --description $desc',\n          env: env,\n        );\n        return;\n    }\n  }\n}\n\nFuture<void> main(Iterable<String> args) async {\n  final runner = CommandRunner('setup', 'build Application');\n  runner.addCommand(BuildCommand(target: Target.android));\n  runner.addCommand(BuildCommand(target: Target.linux));\n  runner.addCommand(BuildCommand(target: Target.windows));\n  runner.addCommand(BuildCommand(target: Target.macos));\n  runner.run(args);\n}\n"
  },
  {
    "path": "windows/.gitignore",
    "content": "flutter/ephemeral/\n\n# Visual Studio user-specific files.\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# Visual Studio build-related files.\nx64/\nx86/\n\n# Visual Studio cache files\n# files ending in .cache can be ignored\n*.[Cc]ache\n# but keep track of directories ending in .cache\n!*.[Cc]ache/\n\nbuild/\n\nout/\n.idea/\n.vs/\n.vscode/\n"
  },
  {
    "path": "windows/CMakeLists.txt",
    "content": "# Project-level configuration.\ncmake_minimum_required(VERSION 3.14)\nproject(Bettbox LANGUAGES CXX)\n\n# The name of the executable created for the application. Change this to change\n# the on-disk name of your application.\nset(BINARY_NAME \"Bettbox\")\n\nadd_definitions(-DFLUTTER_DISABLE_IMPELLER=1)\n\n# Explicitly opt in to modern CMake behaviors to avoid warnings with recent\n# versions of CMake.\ncmake_policy(SET CMP0063 NEW)\n\n# Define build configuration option.\nget_property(IS_MULTICONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)\nif(IS_MULTICONFIG)\n  set(CMAKE_CONFIGURATION_TYPES \"Debug;Profile;Release\"\n    CACHE STRING \"\" FORCE)\nelse()\n  if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)\n    set(CMAKE_BUILD_TYPE \"Debug\" CACHE\n      STRING \"Flutter build mode\" FORCE)\n    set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS\n      \"Debug\" \"Profile\" \"Release\")\n  endif()\nendif()\n# Define settings for the Profile build mode.\nset(CMAKE_EXE_LINKER_FLAGS_PROFILE \"${CMAKE_EXE_LINKER_FLAGS_RELEASE}\")\nset(CMAKE_SHARED_LINKER_FLAGS_PROFILE \"${CMAKE_SHARED_LINKER_FLAGS_RELEASE}\")\nset(CMAKE_C_FLAGS_PROFILE \"${CMAKE_C_FLAGS_RELEASE}\")\nset(CMAKE_CXX_FLAGS_PROFILE \"${CMAKE_CXX_FLAGS_RELEASE}\")\n\n# Use Unicode for all projects.\nadd_definitions(-DUNICODE -D_UNICODE)\n\n# Compilation settings that should be applied to most targets.\n#\n# Be cautious about adding new options here, as plugins use this function by\n# default. In most cases, you should add new options to specific targets instead\n# of modifying this function.\nfunction(APPLY_STANDARD_SETTINGS TARGET)\n  target_compile_features(${TARGET} PUBLIC cxx_std_17)\n  target_compile_options(${TARGET} PRIVATE /W4 /WX /wd\"4100\")\n  target_compile_options(${TARGET} PRIVATE /EHsc)\n  target_compile_definitions(${TARGET} PRIVATE \"_HAS_EXCEPTIONS=0\")\n  target_compile_definitions(${TARGET} PRIVATE \"$<$<CONFIG:Debug>:_DEBUG>\")\nendfunction()\n\n# Flutter library and tool build rules.\nset(FLUTTER_MANAGED_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/flutter\")\nadd_subdirectory(${FLUTTER_MANAGED_DIR})\n\n# Application build; see runner/CMakeLists.txt.\nadd_subdirectory(\"runner\")\n\n\n# Generated plugin build rules, which manage building the plugins and adding\n# them to the application.\ninclude(flutter/generated_plugins.cmake)\n\n\n# === Installation ===\n# Support files are copied into place next to the executable, so that it can\n# run in place. This is done instead of making a separate bundle (as on Linux)\n# so that building and running from within Visual Studio will work.\nset(BUILD_BUNDLE_DIR \"$<TARGET_FILE_DIR:${BINARY_NAME}>\")\n# Make the \"install\" step default, as it's required to run.\nset(CMAKE_VS_INCLUDE_INSTALL_TO_DEFAULT_BUILD 1)\nif(CMAKE_INSTALL_PREFIX_INITIALIZED_TO_DEFAULT)\n  set(CMAKE_INSTALL_PREFIX \"${BUILD_BUNDLE_DIR}\" CACHE PATH \"...\" FORCE)\nendif()\n\nset(INSTALL_BUNDLE_DATA_DIR \"${CMAKE_INSTALL_PREFIX}/data\")\nset(INSTALL_BUNDLE_LIB_DIR \"${CMAKE_INSTALL_PREFIX}\")\n\ninstall(TARGETS ${BINARY_NAME} RUNTIME DESTINATION \"${CMAKE_INSTALL_PREFIX}\"\n  COMPONENT Runtime)\n\ninstall(FILES \"${FLUTTER_ICU_DATA_FILE}\" DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\"\n  COMPONENT Runtime)\n\ninstall(FILES \"${FLUTTER_LIBRARY}\" DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n  COMPONENT Runtime)\n\n# libclash.so\nset(CLASH_DIR \"../libclash/windows\")\n\n# if(CMAKE_SYSTEM_PROCESSOR STREQUAL \"ARM64\" OR CMAKE_SYSTEM_PROCESSOR MATCHES \"aarch64\")\n# elseif(CMAKE_SYSTEM_PROCESSOR STREQUAL \"ARM\" OR CMAKE_SYSTEM_PROCESSOR MATCHES \"armv[0-9]+\")\n# elseif(CMAKE_SYSTEM_PROCESSOR MATCHES \"x86\")\n#   set(CLASH_DIR \"../libclash/windows/x86\")\n# endif()\n\ninstall(PROGRAMS \"${CLASH_DIR}/BettboxCore.exe\" DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n  COMPONENT Runtime)\n\ninstall(PROGRAMS \"${CLASH_DIR}/BettboxHelperService.exe\" DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n  COMPONENT Runtime)\n\ninstall(FILES \"WindowsLoopbackManager.exe\" DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n  COMPONENT Runtime)\n\nif(PLUGIN_BUNDLED_LIBRARIES)\n  install(FILES \"${PLUGIN_BUNDLED_LIBRARIES}\"\n    DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n    COMPONENT Runtime)\nendif()\n\n# Fully re-copy the assets directory on each build to avoid having stale files\n# from a previous install.\nset(FLUTTER_ASSET_DIR_NAME \"flutter_assets\")\ninstall(CODE \"\n  file(REMOVE_RECURSE \\\"${INSTALL_BUNDLE_DATA_DIR}/${FLUTTER_ASSET_DIR_NAME}\\\")\n  \" COMPONENT Runtime)\ninstall(DIRECTORY \"${PROJECT_BUILD_DIR}/${FLUTTER_ASSET_DIR_NAME}\"\n  DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\" COMPONENT Runtime)\n\n# Install the AOT library on non-Debug builds only.\ninstall(FILES \"${AOT_LIBRARY}\" DESTINATION \"${INSTALL_BUNDLE_DATA_DIR}\"\n  CONFIGURATIONS Profile;Release\n  COMPONENT Runtime)\n\nset(BETTBOX_FLUTTER_JS_ARM64_BRIDGE \"${CMAKE_CURRENT_SOURCE_DIR}/overrides/flutter_js/arm64/quickjs_c_bridge.dll\")\nfile(GLOB BETTBOX_FLUTTER_JS_ARM64_COMPANION_DLLS\n  \"${CMAKE_CURRENT_SOURCE_DIR}/overrides/flutter_js/arm64/*.dll\")\nlist(REMOVE_ITEM BETTBOX_FLUTTER_JS_ARM64_COMPANION_DLLS \"${BETTBOX_FLUTTER_JS_ARM64_BRIDGE}\")\nset(BETTBOX_IS_WINDOWS_ARM64 FALSE)\nif(DEFINED FLUTTER_TARGET_PLATFORM AND FLUTTER_TARGET_PLATFORM STREQUAL \"windows-arm64\")\n  set(BETTBOX_IS_WINDOWS_ARM64 TRUE)\nelseif(CMAKE_GENERATOR_PLATFORM STREQUAL \"ARM64\")\n  set(BETTBOX_IS_WINDOWS_ARM64 TRUE)\nelseif(CMAKE_SYSTEM_PROCESSOR MATCHES \"^(ARM64|arm64|aarch64)$\")\n  set(BETTBOX_IS_WINDOWS_ARM64 TRUE)\nendif()\n\nif(BETTBOX_IS_WINDOWS_ARM64)\n  if(NOT EXISTS \"${BETTBOX_FLUTTER_JS_ARM64_BRIDGE}\")\n    message(FATAL_ERROR \"Missing ARM64 quickjs_c_bridge.dll: ${BETTBOX_FLUTTER_JS_ARM64_BRIDGE}\")\n  endif()\n\n  install(CODE \"file(REMOVE_RECURSE \\\"${INSTALL_BUNDLE_LIB_DIR}/quickjs_c_bridge.dll\\\")\"\n    COMPONENT Runtime)\n  install(FILES \"${BETTBOX_FLUTTER_JS_ARM64_BRIDGE}\"\n    DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n    COMPONENT Runtime)\n\n  if(BETTBOX_FLUTTER_JS_ARM64_COMPANION_DLLS)\n    install(FILES ${BETTBOX_FLUTTER_JS_ARM64_COMPANION_DLLS}\n      DESTINATION \"${INSTALL_BUNDLE_LIB_DIR}\"\n      COMPONENT Runtime)\n  endif()\nendif()\n"
  },
  {
    "path": "windows/flutter/CMakeLists.txt",
    "content": "# This file controls Flutter-level build steps. It should not be edited.\ncmake_minimum_required(VERSION 3.14)\n\nset(EPHEMERAL_DIR \"${CMAKE_CURRENT_SOURCE_DIR}/ephemeral\")\n\n# Configuration provided via flutter tool.\ninclude(${EPHEMERAL_DIR}/generated_config.cmake)\n\n# TODO: Move the rest of this into files in ephemeral. See\n# https://github.com/flutter/flutter/issues/57146.\nset(WRAPPER_ROOT \"${EPHEMERAL_DIR}/cpp_client_wrapper\")\n\n# Set fallback configurations for older versions of the flutter tool.\nif (NOT DEFINED FLUTTER_TARGET_PLATFORM)\n  set(FLUTTER_TARGET_PLATFORM \"windows-x64\")\nendif()\n\n# === Flutter Library ===\nset(FLUTTER_LIBRARY \"${EPHEMERAL_DIR}/flutter_windows.dll\")\n\n# Published to parent scope for install step.\nset(FLUTTER_LIBRARY ${FLUTTER_LIBRARY} PARENT_SCOPE)\nset(FLUTTER_ICU_DATA_FILE \"${EPHEMERAL_DIR}/icudtl.dat\" PARENT_SCOPE)\nset(PROJECT_BUILD_DIR \"${PROJECT_DIR}/build/\" PARENT_SCOPE)\nset(AOT_LIBRARY \"${PROJECT_DIR}/build/windows/app.so\" PARENT_SCOPE)\n\nlist(APPEND FLUTTER_LIBRARY_HEADERS\n  \"flutter_export.h\"\n  \"flutter_windows.h\"\n  \"flutter_messenger.h\"\n  \"flutter_plugin_registrar.h\"\n  \"flutter_texture_registrar.h\"\n)\nlist(TRANSFORM FLUTTER_LIBRARY_HEADERS PREPEND \"${EPHEMERAL_DIR}/\")\nadd_library(flutter INTERFACE)\ntarget_include_directories(flutter INTERFACE\n  \"${EPHEMERAL_DIR}\"\n)\ntarget_link_libraries(flutter INTERFACE \"${FLUTTER_LIBRARY}.lib\")\nadd_dependencies(flutter flutter_assemble)\n\n# === Wrapper ===\nlist(APPEND CPP_WRAPPER_SOURCES_CORE\n  \"core_implementations.cc\"\n  \"standard_codec.cc\"\n)\nlist(TRANSFORM CPP_WRAPPER_SOURCES_CORE PREPEND \"${WRAPPER_ROOT}/\")\nlist(APPEND CPP_WRAPPER_SOURCES_PLUGIN\n  \"plugin_registrar.cc\"\n)\nlist(TRANSFORM CPP_WRAPPER_SOURCES_PLUGIN PREPEND \"${WRAPPER_ROOT}/\")\nlist(APPEND CPP_WRAPPER_SOURCES_APP\n  \"flutter_engine.cc\"\n  \"flutter_view_controller.cc\"\n)\nlist(TRANSFORM CPP_WRAPPER_SOURCES_APP PREPEND \"${WRAPPER_ROOT}/\")\n\n# Wrapper sources needed for a plugin.\nadd_library(flutter_wrapper_plugin STATIC\n  ${CPP_WRAPPER_SOURCES_CORE}\n  ${CPP_WRAPPER_SOURCES_PLUGIN}\n)\napply_standard_settings(flutter_wrapper_plugin)\nset_target_properties(flutter_wrapper_plugin PROPERTIES\n  POSITION_INDEPENDENT_CODE ON)\nset_target_properties(flutter_wrapper_plugin PROPERTIES\n  CXX_VISIBILITY_PRESET hidden)\ntarget_link_libraries(flutter_wrapper_plugin PUBLIC flutter)\ntarget_include_directories(flutter_wrapper_plugin PUBLIC\n  \"${WRAPPER_ROOT}/include\"\n)\nadd_dependencies(flutter_wrapper_plugin flutter_assemble)\n\n# Wrapper sources needed for the runner.\nadd_library(flutter_wrapper_app STATIC\n  ${CPP_WRAPPER_SOURCES_CORE}\n  ${CPP_WRAPPER_SOURCES_APP}\n)\napply_standard_settings(flutter_wrapper_app)\ntarget_link_libraries(flutter_wrapper_app PUBLIC flutter)\ntarget_include_directories(flutter_wrapper_app PUBLIC\n  \"${WRAPPER_ROOT}/include\"\n)\nadd_dependencies(flutter_wrapper_app flutter_assemble)\n\n# === Flutter tool backend ===\n# _phony_ is a non-existent file to force this command to run every time,\n# since currently there's no way to get a full input/output list from the\n# flutter tool.\nset(PHONY_OUTPUT \"${CMAKE_CURRENT_BINARY_DIR}/_phony_\")\nset_source_files_properties(\"${PHONY_OUTPUT}\" PROPERTIES SYMBOLIC TRUE)\nadd_custom_command(\n  OUTPUT ${FLUTTER_LIBRARY} ${FLUTTER_LIBRARY_HEADERS}\n    ${CPP_WRAPPER_SOURCES_CORE} ${CPP_WRAPPER_SOURCES_PLUGIN}\n    ${CPP_WRAPPER_SOURCES_APP}\n    ${PHONY_OUTPUT}\n  COMMAND ${CMAKE_COMMAND} -E env\n    ${FLUTTER_TOOL_ENVIRONMENT}\n    \"${FLUTTER_ROOT}/packages/flutter_tools/bin/tool_backend.bat\"\n      ${FLUTTER_TARGET_PLATFORM} $<CONFIG>\n  VERBATIM\n)\nadd_custom_target(flutter_assemble DEPENDS\n  \"${FLUTTER_LIBRARY}\"\n  ${FLUTTER_LIBRARY_HEADERS}\n  ${CPP_WRAPPER_SOURCES_CORE}\n  ${CPP_WRAPPER_SOURCES_PLUGIN}\n  ${CPP_WRAPPER_SOURCES_APP}\n)\n"
  },
  {
    "path": "windows/flutter/generated_plugin_registrant.cc",
    "content": "//\n//  Generated file. Do not edit.\n//\n\n// clang-format off\n\n#include \"generated_plugin_registrant.h\"\n\n#include <app_links/app_links_plugin_c_api.h>\n#include <connectivity_plus/connectivity_plus_windows_plugin.h>\n#include <dynamic_color/dynamic_color_plugin_c_api.h>\n#include <file_selector_windows/file_selector_windows.h>\n#include <flutter_acrylic/flutter_acrylic_plugin.h>\n#include <flutter_js/flutter_js_plugin.h>\n#include <hotkey_manager_windows/hotkey_manager_windows_plugin_c_api.h>\n#include <proxy/proxy_plugin_c_api.h>\n#include <screen_retriever_windows/screen_retriever_windows_plugin_c_api.h>\n#include <sentry_flutter/sentry_flutter_plugin.h>\n#include <tray_manager/tray_manager_plugin.h>\n#include <url_launcher_windows/url_launcher_windows.h>\n#include <vclibs/vclibs_plugin_c_api.h>\n#include <window_ext/window_ext_plugin_c_api.h>\n#include <window_manager/window_manager_plugin.h>\n\nvoid RegisterPlugins(flutter::PluginRegistry* registry) {\n  AppLinksPluginCApiRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"AppLinksPluginCApi\"));\n  ConnectivityPlusWindowsPluginRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"ConnectivityPlusWindowsPlugin\"));\n  DynamicColorPluginCApiRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"DynamicColorPluginCApi\"));\n  FileSelectorWindowsRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"FileSelectorWindows\"));\n  FlutterAcrylicPluginRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"FlutterAcrylicPlugin\"));\n  FlutterJsPluginRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"FlutterJsPlugin\"));\n  HotkeyManagerWindowsPluginCApiRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"HotkeyManagerWindowsPluginCApi\"));\n  ProxyPluginCApiRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"ProxyPluginCApi\"));\n  ScreenRetrieverWindowsPluginCApiRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"ScreenRetrieverWindowsPluginCApi\"));\n  SentryFlutterPluginRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"SentryFlutterPlugin\"));\n  TrayManagerPluginRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"TrayManagerPlugin\"));\n  UrlLauncherWindowsRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"UrlLauncherWindows\"));\n  VclibsPluginCApiRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"VclibsPluginCApi\"));\n  WindowExtPluginCApiRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"WindowExtPluginCApi\"));\n  WindowManagerPluginRegisterWithRegistrar(\n      registry->GetRegistrarForPlugin(\"WindowManagerPlugin\"));\n}\n"
  },
  {
    "path": "windows/flutter/generated_plugin_registrant.h",
    "content": "//\n//  Generated file. Do not edit.\n//\n\n// clang-format off\n\n#ifndef GENERATED_PLUGIN_REGISTRANT_\n#define GENERATED_PLUGIN_REGISTRANT_\n\n#include <flutter/plugin_registry.h>\n\n// Registers Flutter plugins.\nvoid RegisterPlugins(flutter::PluginRegistry* registry);\n\n#endif  // GENERATED_PLUGIN_REGISTRANT_\n"
  },
  {
    "path": "windows/flutter/generated_plugins.cmake",
    "content": "#\n# Generated file, do not edit.\n#\n\nlist(APPEND FLUTTER_PLUGIN_LIST\n  app_links\n  connectivity_plus\n  dynamic_color\n  file_selector_windows\n  flutter_acrylic\n  flutter_js\n  hotkey_manager_windows\n  proxy\n  screen_retriever_windows\n  sentry_flutter\n  tray_manager\n  url_launcher_windows\n  vclibs\n  window_ext\n  window_manager\n)\n\nlist(APPEND FLUTTER_FFI_PLUGIN_LIST\n  jni\n)\n\nset(PLUGIN_BUNDLED_LIBRARIES)\n\nforeach(plugin ${FLUTTER_PLUGIN_LIST})\n  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${plugin}/windows plugins/${plugin})\n  target_link_libraries(${BINARY_NAME} PRIVATE ${plugin}_plugin)\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES $<TARGET_FILE:${plugin}_plugin>)\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${plugin}_bundled_libraries})\nendforeach(plugin)\n\nforeach(ffi_plugin ${FLUTTER_FFI_PLUGIN_LIST})\n  add_subdirectory(flutter/ephemeral/.plugin_symlinks/${ffi_plugin}/windows plugins/${ffi_plugin})\n  list(APPEND PLUGIN_BUNDLED_LIBRARIES ${${ffi_plugin}_bundled_libraries})\nendforeach(ffi_plugin)\n"
  },
  {
    "path": "windows/packaging/exe/ChineseSimplified.isl",
    "content": "﻿; *** Inno Setup version 6.1.0+ Chinese Simplified messages ***\n;\n; To download user-contributed translations of this file, go to:\n;   https://jrsoftware.org/files/istrans/\n;\n; Note: When translating this text, do not add periods (.) to the end of\n; messages that didn't have them already, because on those messages Inno\n; Setup adds the periods automatically (appending a period would result in\n; two periods being displayed).\n;\n; Maintained by Zhenghan Yang\n; Email: 847320916@QQ.com\n; Translation based on network resource\n; The latest Translation is on https://github.com/kira-96/Inno-Setup-Chinese-Simplified-Translation\n;\n\n[LangOptions]\n; The following three entries are very important. Be sure to read and \n; understand the '[LangOptions] section' topic in the help file.\nLanguageName=简体中文\n; If Language Name display incorrect, uncomment next line\n; LanguageName=<7B80><4F53><4E2D><6587>\n; About LanguageID, to reference link:\n; https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-lcid/a9eac961-e77d-41a6-90a5-ce1a8b0cdb9c\nLanguageID=$0804\nLanguageCodePage=936\n; If the language you are translating to requires special font faces or\n; sizes, uncomment any of the following entries and change them accordingly.\nDialogFontName=Microsoft YaHei UI\n;DialogFontSize=8\nWelcomeFontName=Microsoft YaHei UI\n;WelcomeFontSize=12\nTitleFontName=Microsoft YaHei UI\n;TitleFontSize=29\n;CopyrightFontName=Arial\n;CopyrightFontSize=8\n\n[Messages]\n\n; *** 应用程序标题\nSetupAppTitle=安装\nSetupWindowTitle=安装 - %1\nUninstallAppTitle=卸载\nUninstallAppFullTitle=%1 卸载\n\n; *** Misc. common\nInformationTitle=信息\nConfirmTitle=确认\nErrorTitle=错误\n\n; *** SetupLdr messages\nSetupLdrStartupMessage=现在将安装 %1。您想要继续吗？\nLdrCannotCreateTemp=不能创建临时文件。安装中断。\nLdrCannotExecTemp=不能执行临时目录中的文件。安装中断。\nHelpTextNote=\n\n; *** 启动错误消息\nLastErrorMessage=%1.%n%n错误 %2: %3\nSetupFileMissing=安装目录中的文件 %1 丢失。请修正这个问题或者获取程序的新副本。\nSetupFileCorrupt=安装文件已损坏。请获取程序的新副本。\nSetupFileCorruptOrWrongVer=安装文件已损坏，或是与这个安装程序的版本不兼容。请修正这个问题或获取新的程序副本。\nInvalidParameter=无效的命令行参数：%n%n%1\nSetupAlreadyRunning=安装程序正在运行。\nWindowsVersionNotSupported=这个程序不支持当前计算机运行的 Windows 版本。\nWindowsServicePackRequired=这个程序需要 %1 服务包 %2 或更高。\nNotOnThisPlatform=这个程序将不能运行于 %1。\nOnlyOnThisPlatform=这个程序必须运行于 %1。\nOnlyOnTheseArchitectures=这个程序只能在为下列处理器架构的 Windows 版本中进行安装：%n%n%1\nWinVersionTooLowError=这个程序需要 %1 版本 %2 或更高。\nWinVersionTooHighError=这个程序不能安装于 %1 版本 %2 或更高。\nAdminPrivilegesRequired=在安装这个程序时您必须以管理员身份登录。\nPowerUserPrivilegesRequired=在安装这个程序时您必须以管理员身份或有权限的用户组身份登录。\nSetupAppRunningError=安装程序发现 %1 当前正在运行。%n%n请先关闭所有运行的窗口，然后点击“确定”继续，或按“取消”退出。\nUninstallAppRunningError=卸载程序发现 %1 当前正在运行。%n%n请先关闭所有运行的窗口，然后点击“确定”继续，或按“取消”退出。\n\n; *** 启动问题\nPrivilegesRequiredOverrideTitle=选择安装程序模式\nPrivilegesRequiredOverrideInstruction=选择安装模式\nPrivilegesRequiredOverrideText1=%1 可以为所有用户安装(需要管理员权限)，或仅为您安装。\nPrivilegesRequiredOverrideText2=%1 只能为您安装，或为所有用户安装(需要管理员权限)。\nPrivilegesRequiredOverrideAllUsers=为所有用户安装(&A)\nPrivilegesRequiredOverrideAllUsersRecommended=为所有用户安装(&A) (建议选项)\nPrivilegesRequiredOverrideCurrentUser=仅为我安装(&M)\nPrivilegesRequiredOverrideCurrentUserRecommended=仅为我安装(&M) (建议选项)\n\n; *** 其它错误\nErrorCreatingDir=安装程序不能创建目录“%1”。\nErrorTooManyFilesInDir=不能在目录“%1”中创建文件，因为里面的文件太多\n\n; *** 安装程序公共消息\nExitSetupTitle=退出安装程序\nExitSetupMessage=安装程序尚未完成安装。如果您现在退出，程序将不能安装。%n%n您可以以后再运行安装程序完成安装。%n%n现在退出安装程序吗？\nAboutSetupMenuItem=关于安装程序(&A)...\nAboutSetupTitle=关于安装程序\nAboutSetupMessage=%1 版本 %2%n%3%n%n%1 主页：%n%4\nAboutSetupNote=\nTranslatorNote=Translated by Zhenghan Yang.\n\n; *** 按钮\nButtonBack=< 上一步(&B)\nButtonNext=下一步(&N) >\nButtonInstall=安装(&I)\nButtonOK=确定\nButtonCancel=取消\nButtonYes=是(&Y)\nButtonYesToAll=全是(&A)\nButtonNo=否(&N)\nButtonNoToAll=全否(&O)\nButtonFinish=完成(&F)\nButtonBrowse=浏览(&B)...\nButtonWizardBrowse=浏览(&R)...\nButtonNewFolder=新建文件夹(&M)\n\n; *** “选择语言”对话框消息\nSelectLanguageTitle=选择安装语言\nSelectLanguageLabel=选择安装时要使用的语言。\n\n; *** 公共向导文字\nClickNext=点击“下一步”继续，或点击“取消”退出安装程序。\nBeveledLabel=\nBrowseDialogTitle=浏览文件夹\nBrowseDialogLabel=在下列列表中选择一个文件夹，然后点击“确定”。\nNewFolderName=新建文件夹\n\n; *** “欢迎”向导页\nWelcomeLabel1=欢迎使用 [name] 安装向导\nWelcomeLabel2=现在将安装 [name/ver] 到您的电脑中。%n%n推荐您在继续安装前关闭所有其它应用程序。\n\n; *** “密码”向导页\nWizardPassword=密码\nPasswordLabel1=这个安装程序有密码保护。\nPasswordLabel3=请输入密码，然后点击“下一步”继续。密码区分大小写。\nPasswordEditLabel=密码(&P)：\nIncorrectPassword=您所输入的密码不正确，请重试。\n\n; *** “许可协议”向导页\nWizardLicense=许可协议\nLicenseLabel=继续安装前请阅读下列重要信息。\nLicenseLabel3=请仔细阅读下列许可协议。您在继续安装前必须同意这些协议条款。\nLicenseAccepted=我同意此协议(&A)\nLicenseNotAccepted=我拒绝此协议(&D)\n\n; *** “信息”向导页\nWizardInfoBefore=信息\nInfoBeforeLabel=请在继续安装前阅读下列重要信息。\nInfoBeforeClickLabel=如果您想继续安装，点击“下一步”。\nWizardInfoAfter=信息\nInfoAfterLabel=请在继续安装前阅读下列重要信息。\nInfoAfterClickLabel=如果您想继续安装，点击“下一步”。\n\n; *** “用户信息”向导页\nWizardUserInfo=用户信息\nUserInfoDesc=请输入您的信息。\nUserInfoName=用户名(&U)：\nUserInfoOrg=组织(&O)：\nUserInfoSerial=序列号(&S)：\nUserInfoNameRequired=您必须输入用户名。\n\n; *** “选择目标目录”向导页\nWizardSelectDir=选择目标位置\nSelectDirDesc=您想将 [name] 安装在哪里？\nSelectDirLabel3=安装程序将安装 [name] 到下列文件夹中。\nSelectDirBrowseLabel=点击“下一步”继续。如果您想选择其它文件夹，点击“浏览”。\nDiskSpaceGBLabel=至少需要有 [gb] GB 的可用磁盘空间。\nDiskSpaceMBLabel=至少需要有 [mb] MB 的可用磁盘空间。\nCannotInstallToNetworkDrive=安装程序无法安装到一个网络驱动器。\nCannotInstallToUNCPath=安装程序无法安装到一个UNC路径。\nInvalidPath=您必须输入一个带驱动器卷标的完整路径，例如：%n%nC:\\APP%n%n或下列形式的UNC路径：%n%n\\\\server\\share\nInvalidDrive=您选定的驱动器或 UNC 共享不存在或不能访问。请选选择其它位置。\nDiskSpaceWarningTitle=没有足够的磁盘空间\nDiskSpaceWarning=安装程序至少需要 %1 KB 的可用空间才能安装，但选定驱动器只有 %2 KB 的可用空间。%n%n您一定要继续吗？\nDirNameTooLong=文件夹名称或路径太长。\nInvalidDirName=文件夹名称无效。\nBadDirName32=文件夹名称不能包含下列任何字符：%n%n%1\nDirExistsTitle=文件夹已存在\nDirExists=文件夹：%n%n%1%n%n已经存在。您一定要安装到这个文件夹中吗？\nDirDoesntExistTitle=文件夹不存在\nDirDoesntExist=文件夹：%n%n%1%n%n不存在。您想要创建此文件夹吗？\n\n; *** “选择组件”向导页\nWizardSelectComponents=选择组件\nSelectComponentsDesc=您想安装哪些程序的组件？\nSelectComponentsLabel2=选择您想要安装的组件；清除您不想安装的组件。然后点击“下一步”继续。\nFullInstallation=完全安装\n; if possible don't translate 'Compact' as 'Minimal' (I mean 'Minimal' in your language)\nCompactInstallation=简洁安装\nCustomInstallation=自定义安装\nNoUninstallWarningTitle=组件已存在\nNoUninstallWarning=安装程序检测到下列组件已在您的电脑中安装：%n%n%1%n%n取消选定这些组件将不能卸载它们。%n%n您一定要继续吗？\nComponentSize1=%1 KB\nComponentSize2=%1 MB\nComponentsDiskSpaceGBLabel=当前选择的组件至少需要 [gb] GB 的磁盘空间。\nComponentsDiskSpaceMBLabel=当前选择的组件至少需要 [mb] MB 的磁盘空间。\n\n; *** “选择附加任务”向导页\nWizardSelectTasks=选择附加任务\nSelectTasksDesc=您想要安装程序执行哪些附加任务？\nSelectTasksLabel2=选择您想要安装程序在安装 [name] 时执行的附加任务，然后点击“下一步”。\n\n; *** “选择开始菜单文件夹”向导页\nWizardSelectProgramGroup=选择开始菜单文件夹\nSelectStartMenuFolderDesc=安装程序应该在哪里放置程序的快捷方式？\nSelectStartMenuFolderLabel3=安装程序现在将在下列开始菜单文件夹中创建程序的快捷方式。\nSelectStartMenuFolderBrowseLabel=点击“下一步”继续。如果您想选择其它文件夹，点击“浏览”。\nMustEnterGroupName=您必须输入一个文件夹名。\nGroupNameTooLong=文件夹名或路径太长。\nInvalidGroupName=文件夹名无效。\nBadGroupName=文件夹名不能包含下列任何字符：%n%n%1\nNoProgramGroupCheck2=不创建开始菜单文件夹(&D)\n\n; *** “准备安装”向导页\nWizardReady=准备安装\nReadyLabel1=安装程序现在准备开始安装 [name] 到您的电脑中。\nReadyLabel2a=点击“安装”继续此安装程序。如果您想要回顾或修改设置，请点击“上一步”。\nReadyLabel2b=点击“安装”继续此安装程序？\nReadyMemoUserInfo=用户信息：\nReadyMemoDir=目标位置：\nReadyMemoType=安装类型：\nReadyMemoComponents=选定组件：\nReadyMemoGroup=开始菜单文件夹：\nReadyMemoTasks=附加任务：\n\n; *** TDownloadWizardPage wizard page and DownloadTemporaryFile\nDownloadingLabel=正在下载附加文件...\nButtonStopDownload=停止下载(&S)\nStopDownload=您确定要停止下载吗？\nErrorDownloadAborted=下载已中止\nErrorDownloadFailed=下载失败：%1 %2\nErrorDownloadSizeFailed=获取下载大小失败：%1 %2\nErrorFileHash1=校验文件哈希失败：%1\nErrorFileHash2=无效的文件哈希：预期为 %1，实际为 %2\nErrorProgress=无效的进度：%1，总共%2\nErrorFileSize=文件大小错误：预期为 %1，实际为 %2\n\n; *** “正在准备安装”向导页\nWizardPreparing=正在准备安装\nPreparingDesc=安装程序正在准备安装 [name] 到您的电脑中。\nPreviousInstallNotCompleted=先前程序的安装/卸载未完成。您需要重新启动您的电脑才能完成安装。%n%n在重新启动电脑后，再运行安装完成 [name] 的安装。\nCannotContinue=安装程序不能继续。请点击“取消”退出。\nApplicationsFound=下列应用程序正在使用的文件需要更新设置。它是建议您允许安装程序自动关闭这些应用程序。\nApplicationsFound2=下列应用程序正在使用的文件需要更新设置。它是建议您允许安装程序自动关闭这些应用程序。安装完成后，安装程序将尝试重新启动应用程序。\nCloseApplications=自动关闭该应用程序(&A)\nDontCloseApplications=不要关闭该应用程序(&D)\nErrorCloseApplications=安装程序无法自动关闭所有应用程序。在继续之前，我们建议您关闭所有使用需要更新的安装程序文件。\nPrepareToInstallNeedsRestart=安装程序必须重新启动计算机。重新启动计算机后，请再次运行安装程序以完成 [name] 的安装。%n%n是否立即重新启动？\n\n; *** “正在安装”向导页\nWizardInstalling=正在安装\nInstallingLabel=安装程序正在安装 [name] 到您的电脑中，请稍等。\n\n; *** “安装完成”向导页\nFinishedHeadingLabel=[name] 安装完成\nFinishedLabelNoIcons=安装程序已在您的电脑中安装了 [name]。\nFinishedLabel=安装程序已在您的电脑中安装了 [name]。此应用程序可以通过选择安装的快捷方式运行。\nClickFinish=点击“完成”退出安装程序。\nFinishedRestartLabel=要完成 [name] 的安装，安装程序必须重新启动您的电脑。您想要立即重新启动吗？\nFinishedRestartMessage=要完成 [name] 的安装，安装程序必须重新启动您的电脑。%n%n您想要立即重新启动吗？\nShowReadmeCheck=是，我想查阅自述文件\nYesRadio=是，立即重新启动电脑(&Y)\nNoRadio=否，稍后重新启动电脑(&N)\n; used for example as 'Run MyProg.exe'\nRunEntryExec=运行 %1\n; used for example as 'View Readme.txt'\nRunEntryShellExec=查阅 %1\n\n; *** “安装程序需要下一张磁盘”提示\nChangeDiskTitle=安装程序需要下一张磁盘\nSelectDiskLabel2=请插入磁盘 %1 并点击“确定”。%n%n如果这个磁盘中的文件可以在下列文件夹之外的文件夹中找到，请输入正确的路径或点击“浏览”。\nPathLabel=路径(&P)：\nFileNotInDir2=文件“%1”不能在“%2”定位。请插入正确的磁盘或选择其它文件夹。\nSelectDirectoryLabel=请指定下一张磁盘的位置。\n\n; *** 安装状态消息\nSetupAborted=安装程序未完成安装。%n%n请修正这个问题并重新运行安装程序。\nAbortRetryIgnoreSelectAction=选择操作\nAbortRetryIgnoreRetry=重试(&T)\nAbortRetryIgnoreIgnore=忽略错误并继续(&I)\nAbortRetryIgnoreCancel=关闭安装程序\n\n; *** 安装状态消息\nStatusClosingApplications=正在关闭应用程序...\nStatusCreateDirs=正在创建目录...\nStatusExtractFiles=正在解压缩文件...\nStatusCreateIcons=正在创建快捷方式...\nStatusCreateIniEntries=正在创建 INI 条目...\nStatusCreateRegistryEntries=正在创建注册表条目...\nStatusRegisterFiles=正在注册文件...\nStatusSavingUninstall=正在保存卸载信息...\nStatusRunProgram=正在完成安装...\nStatusRestartingApplications=正在重启应用程序...\nStatusRollback=正在撤销更改...\n\n; *** 其它错误\nErrorInternal2=内部错误：%1\nErrorFunctionFailedNoCode=%1 失败\nErrorFunctionFailed=%1 失败；错误代码 %2\nErrorFunctionFailedWithMessage=%1 失败；错误代码 %2.%n%3\nErrorExecutingProgram=不能执行文件：%n%1\n\n; *** 注册表错误\nErrorRegOpenKey=打开注册表项时出错：%n%1\\%2\nErrorRegCreateKey=创建注册表项时出错：%n%1\\%2\nErrorRegWriteKey=写入注册表项时出错：%n%1\\%2\n\n; *** INI 错误\nErrorIniEntry=在文件“%1”中创建INI条目时出错。\n\n; *** 文件复制错误\nFileAbortRetryIgnoreSkipNotRecommended=跳过这个文件(&S) (不推荐)\nFileAbortRetryIgnoreIgnoreNotRecommended=忽略错误并继续(&I) (不推荐)\nSourceIsCorrupted=源文件已损坏\nSourceDoesntExist=源文件“%1”不存在\nExistingFileReadOnly2=无法替换现有文件，因为它是只读的。\nExistingFileReadOnlyRetry=移除只读属性并重试(&R)\nExistingFileReadOnlyKeepExisting=保留现有文件(&K)\nErrorReadingExistingDest=尝试读取现有文件时出错：\nFileExistsSelectAction=选择操作\nFileExists2=文件已经存在。\nFileExistsOverwriteExisting=覆盖已经存在的文件(&O)\nFileExistsKeepExisting=保留现有的文件(&K)\nFileExistsOverwriteOrKeepAll=为所有的冲突文件执行此操作(&D)\nExistingFileNewerSelectAction=选择操作\nExistingFileNewer2=现有的文件比安装程序将要安装的文件更新。\nExistingFileNewerOverwriteExisting=覆盖已经存在的文件(&O)\nExistingFileNewerKeepExisting=保留现有的文件(&K) (推荐)\nExistingFileNewerOverwriteOrKeepAll=为所有的冲突文件执行此操作(&D)\nErrorChangingAttr=尝试改变下列现有的文件的属性时出错：\nErrorCreatingTemp=尝试在目标目录创建文件时出错：\nErrorReadingSource=尝试读取下列源文件时出错：\nErrorCopying=尝试复制下列文件时出错：\nErrorReplacingExistingFile=尝试替换现有的文件时出错：\nErrorRestartReplace=重新启动替换失败：\nErrorRenamingTemp=尝试重新命名以下目标目录中的一个文件时出错：\nErrorRegisterServer=无法注册 DLL/OCX：%1\nErrorRegSvr32Failed=RegSvr32 失败；退出代码 %1\nErrorRegisterTypeLib=无法注册类型库：%1\n\n; *** 卸载显示名字标记\n; used for example as 'My Program (32-bit)'\nUninstallDisplayNameMark=%1 (%2)\n; used for example as 'My Program (32-bit, All users)'\nUninstallDisplayNameMarks=%1 (%2, %3)\nUninstallDisplayNameMark32Bit=32位\nUninstallDisplayNameMark64Bit=64位\nUninstallDisplayNameMarkAllUsers=所有用户\nUninstallDisplayNameMarkCurrentUser=当前用户\n\n; *** 安装后错误\nErrorOpeningReadme=尝试打开自述文件时出错。\nErrorRestartingComputer=安装程序不能重新启动电脑，请手动重启。\n\n; *** 卸载消息\nUninstallNotFound=文件“%1”不存在。无法卸载。\nUninstallOpenError=文件“%1”不能打开。无法卸载。\nUninstallUnsupportedVer=此版本的卸载程序无法识别卸载日志文件“%1”的格式。无法卸载\nUninstallUnknownEntry=在卸载日志中遇到一个未知的条目 (%1)\nConfirmUninstall=您确认想要完全删除 %1 及它的所有组件吗？\nUninstallOnlyOnWin64=这个安装程序只能在64位Windows中进行卸载。\nOnlyAdminCanUninstall=这个安装的程序需要有管理员权限的用户才能卸载。\nUninstallStatusLabel=正在从您的电脑中删除 %1，请稍等。\nUninstalledAll=%1 已顺利地从您的电脑中删除。\nUninstalledMost=%1 卸载完成。%n%n有一些内容无法被删除。您可以手动删除它们。\nUninstalledAndNeedsRestart=要完成 %1 的卸载，您的电脑必须重新启动。%n%n您想立即重新启动电脑吗？\nUninstallDataCorrupted=文件“%1”已损坏，无法卸载\n\n; *** 卸载状态消息\nConfirmDeleteSharedFileTitle=删除共享文件吗？\nConfirmDeleteSharedFile2=系统中包含的下列共享文件已经不再被其它程序使用。您想要卸载程序删除这些共享文件吗？%n%n如果这些文件被删除，但还有程序正在使用这些文件，这些程序可能不能正确执行。如果您不能确定，选择“否”。把这些文件保留在系统中以免引起问题。\nSharedFileNameLabel=文件名：\nSharedFileLocationLabel=位置：\nWizardUninstalling=卸载状态\nStatusUninstalling=正在卸载 %1...\n\n; *** Shutdown block reasons\nShutdownBlockReasonInstallingApp=正在安装 %1。\nShutdownBlockReasonUninstallingApp=正在卸载 %1。\n\n; The custom messages below aren't used by Setup itself, but if you make\n; use of them in your scripts, you'll want to translate them.\n\n[CustomMessages]\n\nNameAndVersion=%1 版本 %2\nAdditionalIcons=附加快捷方式：\nCreateDesktopIcon=创建桌面快捷方式(&D)\nCreateQuickLaunchIcon=创建快速运行栏快捷方式(&Q)\nProgramOnTheWeb=%1 网站\nUninstallProgram=卸载 %1\nLaunchProgram=运行 %1\nAssocFileExtension=将 %2 文件扩展名与 %1 建立关联(&A)\nAssocingFileExtension=正在将 %2 文件扩展名与 %1 建立关联...\nAutoStartProgramGroupDescription=启动组：\nAutoStartProgram=自动启动 %1\nAddonHostProgramNotFound=%1无法找到您所选择的文件夹。%n%n您想要继续吗？\n"
  },
  {
    "path": "windows/packaging/exe/inno_setup.iss",
    "content": "[Setup]\nAppId={{APP_ID}}\nAppVersion={{APP_VERSION}}\nAppName={{DISPLAY_NAME}}\nAppPublisher={{PUBLISHER_NAME}}\nAppPublisherURL={{PUBLISHER_URL}}\nAppSupportURL={{PUBLISHER_URL}}\nAppUpdatesURL={{PUBLISHER_URL}}\nDefaultDirName={{INSTALL_DIR_NAME}}\nDisableProgramGroupPage=yes\nOutputDir=.\nOutputBaseFilename={{OUTPUT_BASE_FILENAME}}\nCompression=lzma\nSolidCompression=yes\nSetupIconFile={{SETUP_ICON_FILE}}\nUninstallDisplayIcon={app}\\{{EXECUTABLE_NAME}}\nWizardStyle=modern\nPrivilegesRequired={{PRIVILEGES_REQUIRED}}\nArchitecturesAllowed={{ARCH}}\nArchitecturesInstallIn64BitMode={{ARCH}}\nCloseApplications=yes\nCloseApplicationsFilter={{EXECUTABLE_NAME}},BettboxCore.exe,BettboxHelperService.exe\nSetupLogging=yes\n\n[Code]\nvar\n  ShouldCleanUserData: Boolean;\n\nfunction IsProcessRunning(ProcessName: String): Boolean;\nvar\n  ResultCode: Integer;\nbegin\n  Exec('cmd.exe', '/c tasklist /fi \"imagename eq ' + ProcessName + '\" 2>nul | find /i \"' + ProcessName + '\" >nul', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);\n  Result := (ResultCode = 0);\nend;\n\nprocedure ForceKillProcesses;\nvar\n  ResultCode: Integer;\n  Processes: TArrayOfString;\n  i: Integer;\n  WaitCount: Integer;\nbegin\n  if IsProcessRunning('BettboxHelperService.exe') then\n  begin\n    Exec('sc', 'stop BettboxHelperService', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);\n\n    WaitCount := 0;\n    while (WaitCount < 5) and IsProcessRunning('BettboxHelperService.exe') do\n    begin\n      Sleep(400);\n      WaitCount := WaitCount + 1;\n    end;\n\n    if IsProcessRunning('BettboxHelperService.exe') then\n      Exec('taskkill', '/f /im BettboxHelperService.exe', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);\n  end;\n\n  Processes := ['Bettbox.exe', 'BettboxCore.exe'];\n\n  for i := 0 to GetArrayLength(Processes)-1 do\n  begin\n    if IsProcessRunning(Processes[i]) then\n      Exec('taskkill', '/f /im ' + Processes[i], '', SW_HIDE, ewWaitUntilTerminated, ResultCode);\n  end;\nend;\n\nprocedure CleanWintunDevices;\nvar\n  ResultCode: Integer;\n  PowerShellScript: String;\n  TempScriptPath: String;\nbegin\n  PowerShellScript := \n    '$ErrorActionPreference = ''SilentlyContinue'';' + #13#10 +\n    'Write-Host \"Cleaning old Wintun/Bettbox/LiClash network adapters...\";' + #13#10 +\n    '$adapters = Get-NetAdapter | Where-Object {' + #13#10 +\n    '$_.InterfaceDescription -like \"*Bettbox*\" -or' + #13#10 +\n    '  $_.Name -like \"*Bettbox*\"' + #13#10 +\n    '};' + #13#10 +\n    'if ($adapters) {' + #13#10 +\n    '  foreach ($adapter in $adapters) {' + #13#10 +\n    '    Write-Host \"Removing adapter: $($adapter.Name) - $($adapter.InterfaceDescription)\";' + #13#10 +\n    '    try {' + #13#10 +\n    '      $adapter | Disable-NetAdapter -Confirm:$false -ErrorAction Stop;' + #13#10 +\n    '      Start-Sleep -Milliseconds 500;' + #13#10 +\n    '      $adapter | Remove-NetAdapter -Confirm:$false -ErrorAction Stop;' + #13#10 +\n    '      Write-Host \"Successfully removed: $($adapter.Name)\";' + #13#10 +\n    '    } catch {' + #13#10 +\n    '      Write-Host \"Failed to remove $($adapter.Name): $_\";' + #13#10 +\n    '    }' + #13#10 +\n    '  }' + #13#10 +\n    '} else {' + #13#10 +\n    '  Write-Host \"No old adapters found.\";' + #13#10 +\n    '}' + #13#10 +\n    'Write-Host \"Cleanup completed.\";';\n  \n  TempScriptPath := ExpandConstant('{tmp}\\clean_wintun.ps1');\n  SaveStringToFile(TempScriptPath, PowerShellScript, False);\n\n  Exec('powershell.exe', \n    '-NoProfile -ExecutionPolicy Bypass -File \"' + TempScriptPath + '\"', \n    '', SW_HIDE, ewWaitUntilTerminated, ResultCode);\n  \n  DeleteFile(TempScriptPath);\nend;\n\nprocedure RegisterHelperService;\nvar\n  ResultCode: Integer;\n  HelperPath: String;\n  ServiceName: String;\nbegin\n  ServiceName := 'BettboxHelperService';\n  HelperPath := ExpandConstant('{app}\\BettboxHelperService.exe');\n  \n  Exec('sc', 'stop ' + ServiceName, '', SW_HIDE, ewWaitUntilTerminated, ResultCode);\n  Exec('sc', 'delete ' + ServiceName, '', SW_HIDE, ewWaitUntilTerminated, ResultCode);\n  \n  Exec('sc', 'create ' + ServiceName + ' binPath= \"' + HelperPath + '\" start= auto', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);\n  \n  Exec('sc', 'start ' + ServiceName, '', SW_HIDE, ewWaitUntilTerminated, ResultCode);\nend;\n\nprocedure UnregisterHelperService;\nvar\n  ResultCode: Integer;\n  ServiceName: String;\nbegin\n  ServiceName := 'BettboxHelperService';\n  \n  Exec('sc', 'stop ' + ServiceName, '', SW_HIDE, ewWaitUntilTerminated, ResultCode);\n  Exec('sc', 'delete ' + ServiceName, '', SW_HIDE, ewWaitUntilTerminated, ResultCode);\nend;\n\nprocedure UnregisterTaskScheduler;\nvar\n  ResultCode: Integer;\n  TaskNames: TArrayOfString;\n  i: Integer;\nbegin\n  TaskNames := ['Bettbox'];\n  \n  for i := 0 to GetArrayLength(TaskNames)-1 do\n  begin\n    Exec('schtasks', '/Delete /TN ' + TaskNames[i] + ' /F', '', SW_HIDE, ewWaitUntilTerminated, ResultCode);\n  end;\nend;\n\nprocedure CleanRegistry;\nvar\n  RegistryKeys: TArrayOfString;\n  i: Integer;\nbegin\n  SetArrayLength(RegistryKeys, 2);\n  RegistryKeys[0] := 'Software\\com.appshub.bettbox';\n  RegistryKeys[1] := 'Software\\com.appshub\\Bettbox';\n  \n  for i := 0 to GetArrayLength(RegistryKeys)-1 do\n  begin\n    RegDeleteKeyIncludingSubkeys(HKCU, RegistryKeys[i]);\n  end;\nend;\n\nprocedure CleanUserData;\nvar\n  UserDataPaths: TArrayOfString;\n  i: Integer;\n  AppDataPath: String;\nbegin\n  AppDataPath := ExpandConstant('{userappdata}');\n  \n  SetArrayLength(UserDataPaths, 2);\n  UserDataPaths[0] := AppDataPath + '\\com.appshub.bettbox';\n  UserDataPaths[1] := AppDataPath + '\\com.appshub\\Bettbox';\n  \n  for i := 0 to GetArrayLength(UserDataPaths)-1 do\n  begin\n    if DirExists(UserDataPaths[i]) then\n    begin\n      DelTree(UserDataPaths[i], True, True, True);\n    end;\n  end;\n  \n  if DirExists(AppDataPath + '\\com.appshub') then\n  begin\n    RemoveDir(AppDataPath + '\\com.appshub');\n  end;\nend;\n\nfunction InitializeSetup(): Boolean;\nbegin\n  Result := True;\nend;\n\nfunction InitializeUninstall(): Boolean;\nvar\n  Response: Integer;\nbegin\n  Response := MsgBox(CustomMessage('RemoveUserDataPrompt'), mbConfirmation, MB_YESNOCANCEL);\n  \n  if Response = IDCANCEL then\n  begin\n    Result := False;\n  end\n  else\n  begin\n    ShouldCleanUserData := (Response = IDYES);\n    Result := True;\n  end;\nend;\n\nprocedure CurStepChanged(CurStep: TSetupStep);\nbegin\n  if CurStep = ssInstall then\n  begin\n    { Let Inno Setup try CloseApplications first; force-kill any leftovers before files are copied. }\n    ForceKillProcesses;\n  end;\n\n  if CurStep = ssPostInstall then\n  begin\n    RegisterHelperService;\n  end;\nend;\n\nprocedure CurUninstallStepChanged(CurUninstallStep: TUninstallStep);\nbegin\n  if CurUninstallStep = usUninstall then\n  begin\n    ForceKillProcesses;\n    CleanWintunDevices;\n  end;\n  \n  if CurUninstallStep = usPostUninstall then\n  begin\n    UnregisterHelperService;\n    \n    UnregisterTaskScheduler;\n    \n    if ShouldCleanUserData then\n    begin\n      CleanUserData;\n      CleanRegistry;\n    end;\n  end;\nend;\n\n[Languages]\nName: \"english\"; MessagesFile: \"compiler:Default.isl\"\n{% if LOCALES %}\n{% for locale in LOCALES %}\n{% if locale.lang == 'zh' %}\nName: \"chineseSimplified\"; MessagesFile: {% if locale.file %}{{ locale.file }}{% else %}\"compiler:Languages\\\\ChineseSimplified.isl\"{% endif %}\n{% endif %}\n{% endfor %}\n{% endif %}\n\n[CustomMessages]\nenglish.RemoveUserDataPrompt=Do you want to remove all user data?%n%nThis will remove:%n• Configuration files%n• Profiles and subscriptions%n• Settings and preferences%n• Registry entries%n%nThis action cannot be undone.\n{% if LOCALES %}\n{% for locale in LOCALES %}\n{% if locale.lang == 'zh' %}\nchineseSimplified.RemoveUserDataPrompt=是否要删除所有用户数据？%n%n这将删除：%n• 配置文件%n• 代理配置与订阅%n• 设置和偏好%n• 注册表记录%n%n此操作无法撤销。\n{% endif %}\n{% endfor %}\n{% endif %}\n\n[Tasks]\nName: \"desktopicon\"; Description: \"{cm:CreateDesktopIcon}\"; GroupDescription: \"{cm:AdditionalIcons}\"; Flags: {% if CREATE_DESKTOP_ICON != true %}unchecked{% else %}checkedonce{% endif %}\n[Files]\nSource: \"{{SOURCE_DIR}}\\\\*\"; DestDir: \"{app}\"; Flags: ignoreversion recursesubdirs createallsubdirs\n; NOTE: Don't use \"Flags: ignoreversion\" on any shared system files\n\n[Icons]\nName: \"{autoprograms}\\\\{{DISPLAY_NAME}}\"; Filename: \"{app}\\\\{{EXECUTABLE_NAME}}\"\nName: \"{autodesktop}\\\\{{DISPLAY_NAME}}\"; Filename: \"{app}\\\\{{EXECUTABLE_NAME}}\"; Tasks: desktopicon\n[Run]\nFilename: \"{app}\\\\{{EXECUTABLE_NAME}}\"; Description: \"{cm:LaunchProgram,{{DISPLAY_NAME}}}\"; Flags: {% if PRIVILEGES_REQUIRED == 'admin' %}runascurrentuser{% endif %} nowait postinstall skipifsilent"
  },
  {
    "path": "windows/packaging/exe/make_config.yaml",
    "content": "script_template: inno_setup.iss\napp_id: 728B3532-C74B-4870-9068-BE70FE1LITES\napp_name: Bettbox\npublisher: appshub.cc\npublisher_url: https://github.com/appshubcc/Bettbox\ndisplay_name: Bettbox\nexecutable_name: Bettbox.exe\noutput_base_file_name: Bettbox.exe\nsetup_icon_file: ..\\windows\\runner\\resources\\app_icon.ico\nlocales:\n  - lang: zh\n    file: ..\\windows\\packaging\\exe\\ChineseSimplified.isl\n  - lang: en\nprivileges_required: admin"
  },
  {
    "path": "windows/runner/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.14)\nproject(runner LANGUAGES CXX)\n\n# Define the application target. To change its name, change BINARY_NAME in the\n# top-level CMakeLists.txt, not the value here, or `flutter run` will no longer\n# work.\n#\n# Any new source files that you add to the application should be added here.\n\n\nadd_executable(${BINARY_NAME} WIN32\n  \"flutter_window.cpp\"\n  \"main.cpp\"\n  \"utils.cpp\"\n  \"win32_window.cpp\"\n  \"${FLUTTER_MANAGED_DIR}/generated_plugin_registrant.cc\"\n  \"Runner.rc\"\n  \"runner.exe.manifest\"\n)\n\n# Apply the standard set of build settings. This can be removed for applications\n# that need different build settings.\napply_standard_settings(${BINARY_NAME})\n# Add preprocessor definitions for the build version.\ntarget_compile_definitions(${BINARY_NAME} PRIVATE \"FLUTTER_VERSION=\\\"${FLUTTER_VERSION}\\\"\")\ntarget_compile_definitions(${BINARY_NAME} PRIVATE \"FLUTTER_VERSION_MAJOR=${FLUTTER_VERSION_MAJOR}\")\ntarget_compile_definitions(${BINARY_NAME} PRIVATE \"FLUTTER_VERSION_MINOR=${FLUTTER_VERSION_MINOR}\")\ntarget_compile_definitions(${BINARY_NAME} PRIVATE \"FLUTTER_VERSION_PATCH=${FLUTTER_VERSION_PATCH}\")\ntarget_compile_definitions(${BINARY_NAME} PRIVATE \"FLUTTER_VERSION_BUILD=${FLUTTER_VERSION_BUILD}\")\n\n# Disable Windows macros that collide with C++ standard library functions.\ntarget_compile_definitions(${BINARY_NAME} PRIVATE \"NOMINMAX\")\n\n# Add dependency libraries and include directories. Add any application-specific\n# dependencies here.\ntarget_link_libraries(${BINARY_NAME} PRIVATE flutter flutter_wrapper_app)\ntarget_link_libraries(${BINARY_NAME} PRIVATE \"dwmapi.lib\")\ntarget_include_directories(${BINARY_NAME} PRIVATE \"${CMAKE_SOURCE_DIR}\")\n\n# Run the Flutter tool portions of the build. This must not be removed.\nadd_dependencies(${BINARY_NAME} flutter_assemble)\n"
  },
  {
    "path": "windows/runner/Runner.rc",
    "content": "// Microsoft Visual C++ generated resource script.\n//\n#pragma code_page(65001)\n#include \"resource.h\"\n\n#define APSTUDIO_READONLY_SYMBOLS\n/////////////////////////////////////////////////////////////////////////////\n//\n// Generated from the TEXTINCLUDE 2 resource.\n//\n#include \"winres.h\"\n\n/////////////////////////////////////////////////////////////////////////////\n#undef APSTUDIO_READONLY_SYMBOLS\n\n/////////////////////////////////////////////////////////////////////////////\n// English (United States) resources\n\n#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\nLANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US\n\n#ifdef APSTUDIO_INVOKED\n/////////////////////////////////////////////////////////////////////////////\n//\n// TEXTINCLUDE\n//\n\n1 TEXTINCLUDE\nBEGIN\n    \"resource.h\\0\"\nEND\n\n2 TEXTINCLUDE\nBEGIN\n    \"#include \"\"winres.h\"\"\\r\\n\"\n    \"\\0\"\nEND\n\n3 TEXTINCLUDE\nBEGIN\n    \"\\r\\n\"\n    \"\\0\"\nEND\n\n#endif    // APSTUDIO_INVOKED\n\n\n/////////////////////////////////////////////////////////////////////////////\n//\n// Icon\n//\n\n// Icon with lowest ID value placed first to ensure application icon\n// remains consistent on all systems.\nIDI_APP_ICON            ICON                    \"resources\\\\app_icon.ico\"\n\n\n/////////////////////////////////////////////////////////////////////////////\n//\n// Version\n//\n\n#if defined(FLUTTER_VERSION_MAJOR) && defined(FLUTTER_VERSION_MINOR) && defined(FLUTTER_VERSION_PATCH) && defined(FLUTTER_VERSION_BUILD)\n#define VERSION_AS_NUMBER FLUTTER_VERSION_MAJOR,FLUTTER_VERSION_MINOR,FLUTTER_VERSION_PATCH,FLUTTER_VERSION_BUILD\n#else\n#define VERSION_AS_NUMBER 1,0,0,0\n#endif\n\n#if defined(FLUTTER_VERSION)\n#define VERSION_AS_STRING FLUTTER_VERSION\n#else\n#define VERSION_AS_STRING \"1.0.0\"\n#endif\n\nVS_VERSION_INFO VERSIONINFO\n FILEVERSION VERSION_AS_NUMBER\n PRODUCTVERSION VERSION_AS_NUMBER\n FILEFLAGSMASK VS_FFI_FILEFLAGSMASK\n#ifdef _DEBUG\n FILEFLAGS VS_FF_DEBUG\n#else\n FILEFLAGS 0x0L\n#endif\n FILEOS VOS__WINDOWS32\n FILETYPE VFT_APP\n FILESUBTYPE 0x0L\nBEGIN\n    BLOCK \"StringFileInfo\"\n    BEGIN\n        BLOCK \"040904e4\"\n        BEGIN\n            VALUE \"CompanyName\", \"com.appshub\" \"\\0\"\n            VALUE \"FileDescription\", \"Bettbox\" \"\\0\"\n            VALUE \"FileVersion\", VERSION_AS_STRING \"\\0\"\n            VALUE \"InternalName\", \"bettbox\" \"\\0\"\n            VALUE \"LegalCopyright\", \"Copyright (C) 2025 com.appshub. All rights reserved.\" \"\\0\"\n            VALUE \"OriginalFilename\", \"Bettbox.exe\" \"\\0\"\n            VALUE \"ProductName\", \"Bettbox\" \"\\0\"\n            VALUE \"ProductVersion\", VERSION_AS_STRING \"\\0\"\n        END\n    END\n    BLOCK \"VarFileInfo\"\n    BEGIN\n        VALUE \"Translation\", 0x409, 1252\n    END\nEND\n\n#endif    // English (United States) resources\n/////////////////////////////////////////////////////////////////////////////\n\n\n\n#ifndef APSTUDIO_INVOKED\n/////////////////////////////////////////////////////////////////////////////\n//\n// Generated from the TEXTINCLUDE 3 resource.\n//\n\n\n/////////////////////////////////////////////////////////////////////////////\n#endif    // not APSTUDIO_INVOKED\n"
  },
  {
    "path": "windows/runner/flutter_window.cpp",
    "content": "#include \"flutter_window.h\"\n\n#include <optional>\n#include <flutter/method_channel.h>\n#include <flutter/standard_method_codec.h>\n#include <shobjidl.h>  // For ITaskbarList3\n\n#include \"flutter/generated_plugin_registrant.h\"\n#include \"resource.h\"\n\nFlutterWindow::FlutterWindow(const flutter::DartProject& project)\n    : project_(project) {}\n\nFlutterWindow::~FlutterWindow() {}\n\nbool FlutterWindow::OnCreate() {\n  if (!Win32Window::OnCreate()) {\n    return false;\n  }\n\n  RECT frame = GetClientArea();\n\n  // The size here must match the window dimensions to avoid unnecessary surface\n  // creation / destruction in the startup path.\n  flutter_controller_ = std::make_unique<flutter::FlutterViewController>(\n      frame.right - frame.left, frame.bottom - frame.top, project_);\n  // Ensure that basic setup of the controller was successful.\n  if (!flutter_controller_->engine() || !flutter_controller_->view()) {\n    return false;\n  }\n  RegisterPlugins(flutter_controller_->engine());\n  \n  // Register app method channel\n  SetupAppMethodChannel();\n  \n  // Load and apply saved icon preference\n  bool use_light_icon = LoadIconPreference();\n  SetWindowIcon(use_light_icon);\n  \n  SetChildContent(flutter_controller_->view()->GetNativeWindow());\n\n  flutter_controller_->engine()->SetNextFrameCallback([&]() {\n\n  });\n\n  // Flutter can complete the first frame before the \"show window\" callback is\n  // registered. The following call ensures a frame is pending to ensure the\n  // window is shown. It is a no-op if the first frame hasn't completed yet.\n  flutter_controller_->ForceRedraw();\n\n  return true;\n}\n\nvoid FlutterWindow::OnDestroy() {\n  if (flutter_controller_) {\n    flutter_controller_ = nullptr;\n  }\n\n  Win32Window::OnDestroy();\n}\n\nLRESULT\nFlutterWindow::MessageHandler(HWND hwnd, UINT const message,\n                              WPARAM const wparam,\n                              LPARAM const lparam) noexcept {\n  // Give Flutter, including plugins, an opportunity to handle window messages.\n  if (flutter_controller_) {\n    std::optional<LRESULT> result =\n        flutter_controller_->HandleTopLevelWindowProc(hwnd, message, wparam,\n                                                      lparam);\n    if (result) {\n      return *result;\n    }\n  }\n\n  switch (message) {\n    case WM_FONTCHANGE:\n      flutter_controller_->engine()->ReloadSystemFonts();\n      break;\n  }\n\n  return Win32Window::MessageHandler(hwnd, message, wparam, lparam);\n}\n\nvoid FlutterWindow::SetupAppMethodChannel() {\n  auto channel = std::make_unique<flutter::MethodChannel<flutter::EncodableValue>>(\n      flutter_controller_->engine()->messenger(), \"app\",\n      &flutter::StandardMethodCodec::GetInstance());\n\n  channel->SetMethodCallHandler(\n      [this](const auto& call, auto result) {\n        if (call.method_name() == \"setLauncherIcon\") {\n          const auto* arguments = std::get_if<flutter::EncodableMap>(call.arguments());\n          if (arguments) {\n            auto use_light_icon_it = arguments->find(flutter::EncodableValue(\"useLightIcon\"));\n            if (use_light_icon_it != arguments->end()) {\n              bool use_light_icon = std::get<bool>(use_light_icon_it->second);\n              bool success = SetWindowIcon(use_light_icon);\n              result->Success(flutter::EncodableValue(success));\n              return;\n            }\n          }\n          result->Error(\"INVALID_ARGUMENT\", \"Missing useLightIcon argument\");\n        } else {\n          result->NotImplemented();\n        }\n      });\n}\n\nbool FlutterWindow::SetWindowIcon(bool use_light_icon) {\n  HWND hwnd = GetHandle();\n  if (!hwnd) {\n    return false;\n  }\n\n  std::wstring icon_name = use_light_icon ? L\"icon_light.ico\" : L\"icon.ico\";\n  wchar_t exe_path_buf[MAX_PATH] = {0};\n  DWORD exe_path_len = GetModuleFileNameW(NULL, exe_path_buf, MAX_PATH);\n  std::wstring exe_path = exe_path_len > 0 ? std::wstring(exe_path_buf) : L\"\";\n  std::wstring base_dir = L\".\";\n  size_t last_slash = exe_path.find_last_of(L\"\\\\/\");\n  if (!exe_path.empty() && last_slash != std::wstring::npos) {\n    base_dir = exe_path.substr(0, last_slash);\n  }\n  std::wstring icon_path =\n      base_dir + L\"\\\\data\\\\flutter_assets\\\\assets\\\\images\\\\\" + icon_name;\n\n  // Load icon file\n  HICON hIcon = (HICON)LoadImageW(\n      NULL,\n      icon_path.c_str(),\n      IMAGE_ICON,\n      0,\n      0,\n      LR_LOADFROMFILE | LR_DEFAULTSIZE | LR_SHARED\n  );\n\n  if (!hIcon) {\n    // Fallback to app resource if load fails\n    hIcon = LoadIcon(GetModuleHandle(NULL), MAKEINTRESOURCE(IDI_APP_ICON));\n    if (!hIcon) {\n      return false;\n    }\n  }\n\n  // Set window icon (title bar)\n  SendMessage(hwnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);\n  SendMessage(hwnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);\n  SetClassLongPtr(hwnd, GCLP_HICON, (LONG_PTR)hIcon);\n  SetClassLongPtr(hwnd, GCLP_HICONSM, (LONG_PTR)hIcon);\n\n  // Update taskbar icon\n  // Method: Refresh via ITaskbarList3 interface\n  ITaskbarList3* pTaskbarList = nullptr;\n  HRESULT hr = CoCreateInstance(\n      CLSID_TaskbarList,\n      NULL,\n      CLSCTX_INPROC_SERVER,\n      IID_ITaskbarList3,\n      (void**)&pTaskbarList\n  );\n\n  if (SUCCEEDED(hr) && pTaskbarList) {\n    pTaskbarList->HrInit();\n    \n    // Refresh taskbar button to update icon\n    pTaskbarList->AddTab(hwnd);\n    pTaskbarList->DeleteTab(hwnd);\n    pTaskbarList->AddTab(hwnd);\n    \n    pTaskbarList->Release();\n  }\n\n  RedrawWindow(hwnd, NULL, NULL,\n               RDW_INVALIDATE | RDW_FRAME | RDW_UPDATENOW | RDW_ALLCHILDREN);\n\n  // Save preference to registry\n  SaveIconPreference(use_light_icon);\n\n  return true;\n}\n\nvoid FlutterWindow::SaveIconPreference(bool use_light_icon) {\n  HKEY hKey;\n  LONG result = RegCreateKeyExW(\n      HKEY_CURRENT_USER,\n      L\"Software\\\\Bettbox\",\n      0,\n      NULL,\n      REG_OPTION_NON_VOLATILE,\n      KEY_WRITE,\n      NULL,\n      &hKey,\n      NULL\n  );\n\n  if (result == ERROR_SUCCESS) {\n    DWORD value = use_light_icon ? 1 : 0;\n    RegSetValueExW(hKey, L\"UseLightIcon\", 0, REG_DWORD, (BYTE*)&value, sizeof(DWORD));\n    RegCloseKey(hKey);\n  }\n}\n\nbool FlutterWindow::LoadIconPreference() {\n  HKEY hKey;\n  LONG result = RegOpenKeyExW(\n      HKEY_CURRENT_USER,\n      L\"Software\\\\Bettbox\",\n      0,\n      KEY_READ,\n      &hKey\n  );\n\n  if (result == ERROR_SUCCESS) {\n    DWORD value = 0;\n    DWORD size = sizeof(DWORD);\n    result = RegQueryValueExW(hKey, L\"UseLightIcon\", NULL, NULL, (BYTE*)&value, &size);\n    RegCloseKey(hKey);\n    \n    if (result == ERROR_SUCCESS) {\n      return value != 0;\n    }\n  }\n\n  return false;\n}\n"
  },
  {
    "path": "windows/runner/flutter_window.h",
    "content": "#ifndef RUNNER_FLUTTER_WINDOW_H_\n#define RUNNER_FLUTTER_WINDOW_H_\n\n#include <flutter/dart_project.h>\n#include <flutter/flutter_view_controller.h>\n\n#include <memory>\n\n#include \"win32_window.h\"\n\n// A window that does nothing but host a Flutter view.\nclass FlutterWindow : public Win32Window {\n public:\n  // Creates a new FlutterWindow hosting a Flutter view running |project|.\n  explicit FlutterWindow(const flutter::DartProject& project);\n  virtual ~FlutterWindow();\n\n protected:\n  // Win32Window:\n  bool OnCreate() override;\n  void OnDestroy() override;\n  LRESULT MessageHandler(HWND window, UINT const message, WPARAM const wparam,\n                         LPARAM const lparam) noexcept override;\n\n private:\n  // The project to run.\n  flutter::DartProject project_;\n\n  // The Flutter instance hosted by this window.\n  std::unique_ptr<flutter::FlutterViewController> flutter_controller_;\n  \n  // Setup app method channel\n  void SetupAppMethodChannel();\n  \n  // Set window icon\n  bool SetWindowIcon(bool use_light_icon);\n  \n  // Save icon preference\n  void SaveIconPreference(bool use_light_icon);\n  \n  // Load icon preference\n  bool LoadIconPreference();\n};\n\n#endif  // RUNNER_FLUTTER_WINDOW_H_\n"
  },
  {
    "path": "windows/runner/main.cpp",
    "content": "#include <flutter/dart_project.h>\n#include <flutter/flutter_view_controller.h>\n#include <windows.h>\n\n#include \"flutter_window.h\"\n#include \"utils.h\"\n\nint APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,\n                      _In_ wchar_t *command_line, _In_ int show_command) {\n  // Attach to console when present (e.g., 'flutter run') or create a\n  // new console when running with a debugger.\n  if (!::AttachConsole(ATTACH_PARENT_PROCESS) && ::IsDebuggerPresent()) {\n    CreateAndAttachConsole();\n  }\n\n  // Initialize COM, so that it is available for use in the library and/or\n  // plugins.\n  ::CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED);\n\n  flutter::DartProject project(L\"data\");\n\n  std::vector<std::string> command_line_arguments =\n      GetCommandLineArguments();\n\n  project.set_dart_entrypoint_arguments(std::move(command_line_arguments));\n\n  FlutterWindow window(project);\n  Win32Window::Point origin(10, 10);\n  Win32Window::Size size(1280, 720);\n  if (!window.Create(L\"Bettbox\", origin, size)) {\n    return EXIT_FAILURE;\n  }\n  window.SetQuitOnClose(true);\n\n  ::MSG msg;\n  while (::GetMessage(&msg, nullptr, 0, 0)) {\n    ::TranslateMessage(&msg);\n    ::DispatchMessage(&msg);\n  }\n\n  ::CoUninitialize();\n  return EXIT_SUCCESS;\n}\n"
  },
  {
    "path": "windows/runner/resource.h",
    "content": "//{{NO_DEPENDENCIES}}\n// Microsoft Visual C++ generated include file.\n// Used by Runner.rc\n//\n#define IDI_APP_ICON                    101\n\n// Next default values for new objects\n//\n#ifdef APSTUDIO_INVOKED\n#ifndef APSTUDIO_READONLY_SYMBOLS\n#define _APS_NEXT_RESOURCE_VALUE        102\n#define _APS_NEXT_COMMAND_VALUE         40001\n#define _APS_NEXT_CONTROL_VALUE         1001\n#define _APS_NEXT_SYMED_VALUE           101\n#endif\n#endif\n"
  },
  {
    "path": "windows/runner/runner.exe.manifest",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>\n<assembly xmlns=\"urn:schemas-microsoft-com:asm.v1\" manifestVersion=\"1.0\">\n  <application xmlns=\"urn:schemas-microsoft-com:asm.v3\">\n    <windowsSettings>\n      <dpiAwareness xmlns=\"http://schemas.microsoft.com/SMI/2016/WindowsSettings\">PerMonitorV2</dpiAwareness>\n    </windowsSettings>\n  </application>\n  <compatibility xmlns=\"urn:schemas-microsoft-com:compatibility.v1\">\n    <application>\n      <!-- Windows 10 and Windows 11 -->\n      <supportedOS Id=\"{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}\"/>\n      <!-- Windows 8.1 -->\n      <supportedOS Id=\"{1f676c76-80e1-4239-95bb-83d0f6d0da78}\"/>\n      <!-- Windows 8 -->\n      <supportedOS Id=\"{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}\"/>\n      <!-- Windows 7 -->\n      <supportedOS Id=\"{35138b9a-5d96-4fbd-8e2d-a2440225f93a}\"/>\n    </application>\n  </compatibility>\n</assembly>\n"
  },
  {
    "path": "windows/runner/utils.cpp",
    "content": "#include \"utils.h\"\n\n#include <flutter_windows.h>\n#include <io.h>\n#include <stdio.h>\n#include <windows.h>\n\n#include <iostream>\n\nvoid CreateAndAttachConsole() {\n  if (::AllocConsole()) {\n    FILE *unused;\n    if (freopen_s(&unused, \"CONOUT$\", \"w\", stdout)) {\n      _dup2(_fileno(stdout), 1);\n    }\n    if (freopen_s(&unused, \"CONOUT$\", \"w\", stderr)) {\n      _dup2(_fileno(stdout), 2);\n    }\n    std::ios::sync_with_stdio();\n    FlutterDesktopResyncOutputStreams();\n  }\n}\n\nstd::vector<std::string> GetCommandLineArguments() {\n  // Convert the UTF-16 command line arguments to UTF-8 for the Engine to use.\n  int argc;\n  wchar_t** argv = ::CommandLineToArgvW(::GetCommandLineW(), &argc);\n  if (argv == nullptr) {\n    return std::vector<std::string>();\n  }\n\n  std::vector<std::string> command_line_arguments;\n\n  // Skip the first argument as it's the binary name.\n  for (int i = 1; i < argc; i++) {\n    command_line_arguments.push_back(Utf8FromUtf16(argv[i]));\n  }\n\n  ::LocalFree(argv);\n\n  return command_line_arguments;\n}\n\nstd::string Utf8FromUtf16(const wchar_t* utf16_string) {\n  if (utf16_string == nullptr) {\n    return std::string();\n  }\n  int target_length = ::WideCharToMultiByte(\n      CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,\n      -1, nullptr, 0, nullptr, nullptr)\n    -1; // remove the trailing null character\n  int input_length = (int)wcslen(utf16_string);\n  std::string utf8_string;\n  if (target_length <= 0 || target_length > utf8_string.max_size()) {\n    return utf8_string;\n  }\n  utf8_string.resize(target_length);\n  int converted_length = ::WideCharToMultiByte(\n      CP_UTF8, WC_ERR_INVALID_CHARS, utf16_string,\n      input_length, utf8_string.data(), target_length, nullptr, nullptr);\n  if (converted_length == 0) {\n    return std::string();\n  }\n  return utf8_string;\n}\n"
  },
  {
    "path": "windows/runner/utils.h",
    "content": "#ifndef RUNNER_UTILS_H_\n#define RUNNER_UTILS_H_\n\n#include <string>\n#include <vector>\n\n// Creates a console for the process, and redirects stdout and stderr to\n// it for both the runner and the Flutter library.\nvoid CreateAndAttachConsole();\n\n// Takes a null-terminated wchar_t* encoded in UTF-16 and returns a std::string\n// encoded in UTF-8. Returns an empty std::string on failure.\nstd::string Utf8FromUtf16(const wchar_t* utf16_string);\n\n// Gets the command line arguments passed in as a std::vector<std::string>,\n// encoded in UTF-8. Returns an empty std::vector<std::string> on failure.\nstd::vector<std::string> GetCommandLineArguments();\n\n#endif  // RUNNER_UTILS_H_\n"
  },
  {
    "path": "windows/runner/win32_window.cpp",
    "content": "#include \"win32_window.h\"\n#include \"app_links/app_links_plugin_c_api.h\"\n\n#include <dwmapi.h>\n#include <flutter_windows.h>\n\n#include \"resource.h\"\n\nnamespace\n{\n\n/// Window attribute that enables dark mode window decorations.\n///\n/// Redefined in case the developer's machine has a Windows SDK older than\n/// version 10.0.22000.0.\n/// See: https://docs.microsoft.com/windows/win32/api/dwmapi/ne-dwmapi-dwmwindowattribute\n#ifndef DWMWA_USE_IMMERSIVE_DARK_MODE\n#define DWMWA_USE_IMMERSIVE_DARK_MODE 20\n#endif\n\n  constexpr const wchar_t kWindowClassName[] = L\"FLUTTER_RUNNER_WIN32_WINDOW\";\n\n  /// Registry key for app theme preference.\n  ///\n  /// A value of 0 indicates apps should use dark mode. A non-zero or missing\n  /// value indicates apps should use light mode.\n  constexpr const wchar_t kGetPreferredBrightnessRegKey[] =\n      L\"Software\\\\Microsoft\\\\Windows\\\\CurrentVersion\\\\Themes\\\\Personalize\";\n  constexpr const wchar_t kGetPreferredBrightnessRegValue[] = L\"AppsUseLightTheme\";\n\n  // The number of Win32Window objects that currently exist.\n  static int g_active_window_count = 0;\n\n  using EnableNonClientDpiScaling = BOOL __stdcall(HWND hwnd);\n\n  // Scale helper to convert logical scaler values to physical using passed in\n  // scale factor\n  int Scale(int source, double scale_factor)\n  {\n    return static_cast<int>(source * scale_factor);\n  }\n\n  // Dynamically loads the |EnableNonClientDpiScaling| from the User32 module.\n  // This API is only needed for PerMonitor V1 awareness mode.\n  void EnableFullDpiSupportIfAvailable(HWND hwnd)\n  {\n    HMODULE user32_module = LoadLibraryA(\"User32.dll\");\n    if (!user32_module)\n    {\n      return;\n    }\n    auto enable_non_client_dpi_scaling =\n        reinterpret_cast<EnableNonClientDpiScaling *>(\n            GetProcAddress(user32_module, \"EnableNonClientDpiScaling\"));\n    if (enable_non_client_dpi_scaling != nullptr)\n    {\n      enable_non_client_dpi_scaling(hwnd);\n    }\n    FreeLibrary(user32_module);\n  }\n\n} // namespace\n\n// Manages the Win32Window's window class registration.\nclass WindowClassRegistrar\n{\npublic:\n  ~WindowClassRegistrar() = default;\n\n  // Returns the singleton registrar instance.\n  static WindowClassRegistrar *GetInstance()\n  {\n    if (!instance_)\n    {\n      instance_ = new WindowClassRegistrar();\n    }\n    return instance_;\n  }\n\n  // Returns the name of the window class, registering the class if it hasn't\n  // previously been registered.\n  const wchar_t *GetWindowClass();\n\n  // Unregisters the window class. Should only be called if there are no\n  // instances of the window.\n  void UnregisterWindowClass();\n\nprivate:\n  WindowClassRegistrar() = default;\n\n  static WindowClassRegistrar *instance_;\n\n  bool class_registered_ = false;\n};\n\nWindowClassRegistrar *WindowClassRegistrar::instance_ = nullptr;\n\nconst wchar_t *WindowClassRegistrar::GetWindowClass()\n{\n  if (!class_registered_)\n  {\n    WNDCLASS window_class{};\n    window_class.hCursor = LoadCursor(nullptr, IDC_ARROW);\n    window_class.lpszClassName = kWindowClassName;\n    window_class.style = CS_HREDRAW | CS_VREDRAW;\n    window_class.cbClsExtra = 0;\n    window_class.cbWndExtra = 0;\n    window_class.hInstance = GetModuleHandle(nullptr);\n    window_class.hIcon =\n        LoadIcon(window_class.hInstance, MAKEINTRESOURCE(IDI_APP_ICON));\n    window_class.hbrBackground = 0;\n    window_class.lpszMenuName = nullptr;\n    window_class.lpfnWndProc = Win32Window::WndProc;\n    RegisterClass(&window_class);\n    class_registered_ = true;\n  }\n  return kWindowClassName;\n}\n\nvoid WindowClassRegistrar::UnregisterWindowClass()\n{\n  UnregisterClass(kWindowClassName, nullptr);\n  class_registered_ = false;\n}\n\nWin32Window::Win32Window()\n{\n  ++g_active_window_count;\n}\n\nWin32Window::~Win32Window()\n{\n  --g_active_window_count;\n  Destroy();\n}\n\nbool Win32Window::Create(const std::wstring &title,\n                         const Point &origin,\n                         const Size &size)\n{\n\n  if (SendAppLinkToInstance(title))\n  {\n    return false;\n  }\n  Destroy();\n\n  const wchar_t *window_class =\n      WindowClassRegistrar::GetInstance()->GetWindowClass();\n\n  const POINT target_point = {static_cast<LONG>(origin.x),\n                              static_cast<LONG>(origin.y)};\n  HMONITOR monitor = MonitorFromPoint(target_point, MONITOR_DEFAULTTONEAREST);\n  UINT dpi = FlutterDesktopGetDpiForMonitor(monitor);\n  double scale_factor = dpi / 96.0;\n\n  HWND window = CreateWindow(\n      window_class, title.c_str(), WS_OVERLAPPEDWINDOW,\n      Scale(origin.x, scale_factor), Scale(origin.y, scale_factor),\n      Scale(size.width, scale_factor), Scale(size.height, scale_factor),\n      nullptr, nullptr, GetModuleHandle(nullptr), this);\n\n  if (!window)\n  {\n    return false;\n  }\n\n  UpdateTheme(window);\n\n  return OnCreate();\n}\n\nbool Win32Window::Show()\n{\n  return ShowWindow(window_handle_, SW_SHOWNORMAL);\n}\n\nbool Win32Window::SendAppLinkToInstance(const std::wstring &title)\n{\n  // Find our exact window\n  HWND hwnd = ::FindWindow(kWindowClassName, title.c_str());\n\n  if (hwnd)\n  {\n    // Dispatch new link to current window\n    SendAppLink(hwnd);\n\n    // (Optional) Restore our window to front in same state\n    WINDOWPLACEMENT place = {sizeof(WINDOWPLACEMENT)};\n    GetWindowPlacement(hwnd, &place);\n\n    switch (place.showCmd)\n    {\n    case SW_SHOWMAXIMIZED:\n      ShowWindow(hwnd, SW_SHOWMAXIMIZED);\n      break;\n    case SW_SHOWMINIMIZED:\n      ShowWindow(hwnd, SW_RESTORE);\n      break;\n    default:\n      ShowWindow(hwnd, SW_NORMAL);\n      break;\n    }\n\n    SetWindowPos(0, HWND_TOP, 0, 0, 0, 0, SWP_SHOWWINDOW | SWP_NOSIZE | SWP_NOMOVE);\n    SetForegroundWindow(hwnd);\n\n    // Window has been found, don't create another one.\n    return true;\n  }\n\n  return false;\n}\n\n// static\nLRESULT CALLBACK Win32Window::WndProc(HWND const window,\n                                      UINT const message,\n                                      WPARAM const wparam,\n                                      LPARAM const lparam) noexcept\n{\n  if (message == WM_NCCREATE)\n  {\n    auto window_struct = reinterpret_cast<CREATESTRUCT *>(lparam);\n    SetWindowLongPtr(window, GWLP_USERDATA,\n                     reinterpret_cast<LONG_PTR>(window_struct->lpCreateParams));\n\n    auto that = static_cast<Win32Window *>(window_struct->lpCreateParams);\n    EnableFullDpiSupportIfAvailable(window);\n    that->window_handle_ = window;\n  }\n  else if (Win32Window *that = GetThisFromHandle(window))\n  {\n    return that->MessageHandler(window, message, wparam, lparam);\n  }\n\n  return DefWindowProc(window, message, wparam, lparam);\n}\n\nLRESULT\nWin32Window::MessageHandler(HWND hwnd,\n                            UINT const message,\n                            WPARAM const wparam,\n                            LPARAM const lparam) noexcept\n{\n  switch (message)\n  {\n  case WM_DESTROY:\n    window_handle_ = nullptr;\n    Destroy();\n    if (quit_on_close_)\n    {\n      PostQuitMessage(0);\n    }\n    return 0;\n\n  case WM_DPICHANGED:\n  {\n    auto newRectSize = reinterpret_cast<RECT *>(lparam);\n    LONG newWidth = newRectSize->right - newRectSize->left;\n    LONG newHeight = newRectSize->bottom - newRectSize->top;\n\n    SetWindowPos(hwnd, nullptr, newRectSize->left, newRectSize->top, newWidth,\n                 newHeight, SWP_NOZORDER | SWP_NOACTIVATE);\n\n    return 0;\n  }\n  case WM_SIZE:\n  {\n    RECT rect = GetClientArea();\n    if (child_content_ != nullptr)\n    {\n      // Size and position the child window.\n      MoveWindow(child_content_, rect.left, rect.top, rect.right - rect.left,\n                 rect.bottom - rect.top, TRUE);\n    }\n    return 0;\n  }\n\n  case WM_ACTIVATE:\n    if (child_content_ != nullptr)\n    {\n      SetFocus(child_content_);\n    }\n    return 0;\n\n  case WM_DWMCOLORIZATIONCOLORCHANGED:\n    UpdateTheme(hwnd);\n    return 0;\n  }\n\n  return DefWindowProc(window_handle_, message, wparam, lparam);\n}\n\nvoid Win32Window::Destroy()\n{\n  OnDestroy();\n\n  if (window_handle_)\n  {\n    DestroyWindow(window_handle_);\n    window_handle_ = nullptr;\n  }\n  if (g_active_window_count == 0)\n  {\n    WindowClassRegistrar::GetInstance()->UnregisterWindowClass();\n  }\n}\n\nWin32Window *Win32Window::GetThisFromHandle(HWND const window) noexcept\n{\n  return reinterpret_cast<Win32Window *>(\n      GetWindowLongPtr(window, GWLP_USERDATA));\n}\n\nvoid Win32Window::SetChildContent(HWND content)\n{\n  child_content_ = content;\n  SetParent(content, window_handle_);\n  RECT frame = GetClientArea();\n\n  MoveWindow(content, frame.left, frame.top, frame.right - frame.left,\n             frame.bottom - frame.top, true);\n\n  SetFocus(child_content_);\n}\n\nRECT Win32Window::GetClientArea()\n{\n  RECT frame;\n  GetClientRect(window_handle_, &frame);\n  return frame;\n}\n\nHWND Win32Window::GetHandle()\n{\n  return window_handle_;\n}\n\nvoid Win32Window::SetQuitOnClose(bool quit_on_close)\n{\n  quit_on_close_ = quit_on_close;\n}\n\nbool Win32Window::OnCreate()\n{\n  // No-op; provided for subclasses.\n  return true;\n}\n\nvoid Win32Window::OnDestroy()\n{\n  // No-op; provided for subclasses.\n}\n\nvoid Win32Window::UpdateTheme(HWND const window)\n{\n  DWORD light_mode;\n  DWORD light_mode_size = sizeof(light_mode);\n  LSTATUS result = RegGetValue(HKEY_CURRENT_USER, kGetPreferredBrightnessRegKey,\n                               kGetPreferredBrightnessRegValue,\n                               RRF_RT_REG_DWORD, nullptr, &light_mode,\n                               &light_mode_size);\n\n  if (result == ERROR_SUCCESS)\n  {\n    BOOL enable_dark_mode = light_mode == 0;\n    DwmSetWindowAttribute(window, DWMWA_USE_IMMERSIVE_DARK_MODE,\n                          &enable_dark_mode, sizeof(enable_dark_mode));\n  }\n}\n"
  },
  {
    "path": "windows/runner/win32_window.h",
    "content": "#ifndef RUNNER_WIN32_WINDOW_H_\n#define RUNNER_WIN32_WINDOW_H_\n\n#include <windows.h>\n\n#include <functional>\n#include <memory>\n#include <string>\n\n// A class abstraction for a high DPI-aware Win32 Window. Intended to be\n// inherited from by classes that wish to specialize with custom\n// rendering and input handling\nclass Win32Window\n{\npublic:\n  struct Point\n  {\n    unsigned int x;\n    unsigned int y;\n    Point(unsigned int x, unsigned int y) : x(x), y(y) {}\n  };\n\n  struct Size\n  {\n    unsigned int width;\n    unsigned int height;\n    Size(unsigned int width, unsigned int height)\n        : width(width), height(height) {}\n  };\n\n  Win32Window();\n  virtual ~Win32Window();\n\n  // Creates a win32 window with |title| that is positioned and sized using\n  // |origin| and |size|. New windows are created on the default monitor. Window\n  // sizes are specified to the OS in physical pixels, hence to ensure a\n  // consistent size this function will scale the inputted width and height as\n  // as appropriate for the default monitor. The window is invisible until\n  // |Show| is called. Returns true if the window was created successfully.\n  bool Create(const std::wstring &title, const Point &origin, const Size &size);\n\n  // Show the current window. Returns true if the window was successfully shown.\n  bool Show();\n\n  // Release OS resources associated with window.\n  void Destroy();\n\n  // Inserts |content| into the window tree.\n  void SetChildContent(HWND content);\n\n  // Returns the backing Window handle to enable clients to set icon and other\n  // window properties. Returns nullptr if the window has been destroyed.\n  HWND GetHandle();\n\n  // If true, closing this window will quit the application.\n  void SetQuitOnClose(bool quit_on_close);\n\n  // Return a RECT representing the bounds of the current client area.\n  RECT GetClientArea();\n\nprotected:\n  // Processes and route salient window messages for mouse handling,\n  // size change and DPI. Delegates handling of these to member overloads that\n  // inheriting classes can handle.\n  virtual LRESULT MessageHandler(HWND window,\n                                 UINT const message,\n                                 WPARAM const wparam,\n                                 LPARAM const lparam) noexcept;\n\n  // Called when CreateAndShow is called, allowing subclass window-related\n  // setup. Subclasses should return false if setup fails.\n  virtual bool OnCreate();\n\n  // Called when Destroy is called.\n  virtual void OnDestroy();\n\nprivate:\n  friend class WindowClassRegistrar;\n\n  bool SendAppLinkToInstance(const std::wstring &title);\n\n  // OS callback called by message pump. Handles the WM_NCCREATE message which\n  // is passed when the non-client area is being created and enables automatic\n  // non-client DPI scaling so that the non-client area automatically\n  // responds to changes in DPI. All other messages are handled by\n  // MessageHandler.\n  static LRESULT CALLBACK WndProc(HWND const window,\n                                  UINT const message,\n                                  WPARAM const wparam,\n                                  LPARAM const lparam) noexcept;\n\n  // Retrieves a class instance pointer for |window|\n  static Win32Window *GetThisFromHandle(HWND const window) noexcept;\n\n  // Update the window frame's theme to match the system theme.\n  static void UpdateTheme(HWND const window);\n\n  bool quit_on_close_ = false;\n\n  // window handle for top level window.\n  HWND window_handle_ = nullptr;\n\n  // window handle for hosted content.\n  HWND child_content_ = nullptr;\n};\n\n#endif // RUNNER_WIN32_WINDOW_H_\n"
  }
]