[
  {
    "path": ".gitattributes",
    "content": "# Auto detect text files and perform LF normalization\n*    text=auto\n*.sh text      eol=lf\n"
  },
  {
    "path": ".github/workflows/build.yml",
    "content": "# This workflow will build a .NET project\n# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net\n\nname: Build\n\non:\n  push:\n    branches: [ \"stable\" ]\n  pull_request:\n    types:\n      - opened\n      - edited\n      - reopened\n      - synchronize\n  workflow_call:\n  workflow_dispatch:\n\nenv:\n  WORKING_DIRECTORY: \"Stardrop\"\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v4\n    - name: Setup .NET\n      uses: actions/setup-dotnet@v4\n      with:\n        dotnet-version: 8.0.x\n    - name: Restore dependencies\n      run: dotnet restore \"${{ env.WORKING_DIRECTORY }}\"\n    - name: Build\n      run: dotnet build \"${{ env.WORKING_DIRECTORY }}\" --no-restore\n"
  },
  {
    "path": ".github/workflows/nightly-build.yml",
    "content": "name: Package Nightly Build\n\non:\n  schedule:\n    - cron: '0 10 * * *'\n  workflow_dispatch:\n\npermissions:\n  contents: write\n\njobs:\n    package-and-release:\n      uses: ./.github/workflows/package-and-release.yml\n      with:\n        tag: nightly\n        is-prerelease: 'true'\n        generate-release-notes: false\n"
  },
  {
    "path": ".github/workflows/package-and-release.yml",
    "content": "name: Package and Release\n\non:\n  workflow_call:\n    inputs:\n      tag:\n        required: false\n        type: string\n        default: ''\n      is-prerelease:\n        required: true\n        type: string\n      generate-release-notes:\n        required: true\n        type: boolean\n        default: true\n  workflow_dispatch:\n    inputs:\n      tag:\n        required: true\n        type: string\n      is-prerelease:\n        required: true\n        type: choice\n        options:\n        - 'true'\n        - 'false'\n      generate-release-notes:\n        required: true\n        type: boolean\n        default: true\n\nenv:\n  TAG: ${{ inputs.tag == '' && github.ref_name || inputs.tag }}\n  \npermissions:\n  contents: write\n\n# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued.\n# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete.\nconcurrency:\n  group: \"releasing\"\n  cancel-in-progress: false\n\nrun-name: \"[${{ inputs.is-prerelease == 'true' && 'Pre-release' || 'Release' }}] Package and Release (${{ inputs.tag == '' && github.ref_name || inputs.tag }})\"\n\njobs:\n  verify-build:\n    uses: ./.github/workflows/build.yml\n  package:  \n    needs: [ verify-build ]\n    uses: ./.github/workflows/package.yml\n    with:\n        branch-name: '' # Leaving blank to use default branch\n  release:\n    runs-on: ubuntu-latest\n    needs: [ verify-build, package ]\n\n    steps:\n    - uses: actions/checkout@v4\n    - uses: actions/download-artifact@v4    \n      with:\n        merge-multiple: true\n    - name: Release\n      uses: softprops/action-gh-release@v2\n      with:\n        tag_name: ${{ env.TAG }}\n        prerelease: ${{ inputs.is-prerelease }}\n        name: \"Stardrop ${{ env.TAG }}\"\n        append_body: false\n        body: |\n          ![Downloads](https://img.shields.io/github/downloads/Floogen/Stardrop/${{ env.TAG }}/Stardrop-linux-x64.zip?label=Linux+Downloads) ![Downloads](https://img.shields.io/github/downloads/Floogen/Stardrop/${{ env.TAG }}/Stardrop-osx-x64.zip?label=macOS+Downloads) ![Downloads](https://img.shields.io/github/downloads/Floogen/Stardrop/${{ env.TAG }}/Stardrop-win-x64.zip?label=Windows+Downloads)\n          \n          Documentation can be [found here](https://floogen.gitbook.io/stardrop/).\n          \n          **User Note:**\n          Download the respective ZIP file relating to the host's operating system (Stardrop-win-x64 if using a Window x64 OS and so on).\n                \n          <br/>\n        generate_release_notes: ${{ inputs.generate-release-notes }}\n        make_latest: true\n        fail_on_unmatched_files: true\n        files: |\n          Stardrop-win-x64.zip\n          Stardrop-linux-x64.zip\n          Stardrop-osx-x64.zip\n"
  },
  {
    "path": ".github/workflows/package.yml",
    "content": "name: Package\n\non:\n  pull_request:\n    types:\n      - labeled\n      - unlabeled\n      - opened\n      - edited\n      - reopened\n      - synchronize\n  workflow_call:\n    inputs:\n      branch-name:\n        required: true\n        type: string\n  workflow_dispatch:\n    inputs:\n      branch-name:\n        required: true\n        type: string\n\nenv:\n  OUTPUT_PATH_WINDOWS: published/windows\n  OUTPUT_PATH_LINUX: published/linux\n  OUTPUT_PATH_MAC: published/mac  \n  CONFIGURATION: Release\n  WORKING_DIRECTORY: \"Stardrop\"\n  BRANCH: ${{ github.event_name != 'pull_request' && inputs.branch-name || github.event.pull_request.head.ref }}\n\nrun-name: \"[${{ github.event_name == 'pull_request' && github.event.pull_request.head.ref || 'Default Branch' }}] Packaging\"\n\njobs:  \n  package-macos:  \n    if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'generate packages')\n    runs-on: macos-latest\n\n    steps:\n    - uses: actions/checkout@v4\n      with:\n          ref: ${{ env.BRANCH }}\n          repository: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name || github.repository }}\n    - run: mkdir -p path/to/release-artifacts\n    - name: Publish (MacOS - Creating)\n      run:  |\n          mkdir -p \"${{ env.OUTPUT_PATH_MAC }}/Stardrop.app/Contents/MacOS\"\n          mkdir -p \"${{ env.OUTPUT_PATH_MAC }}/Stardrop.app/Contents/Resources\"\n          dotnet publish \"${{ env.WORKING_DIRECTORY }}\" --output \"${{ env.OUTPUT_PATH_MAC }}/Stardrop.app/Contents/MacOS\" --configuration \"${{ env.CONFIGURATION }}\" --runtime \"osx-x64\" --framework \"net8.0\" --self-contained\n    - name: Publish (MacOS - Packaging)\n      working-directory: \"${{ env.OUTPUT_PATH_MAC }}/Stardrop.app\"\n      run:  |\n          cp ${GITHUB_WORKSPACE}/Stardrop/Assets/Info.plist \"Contents/Info.plist\"\n          cp ${GITHUB_WORKSPACE}/Stardrop/Assets/Stardrop.icns \"Contents/Resources/Stardrop.icns\"\n\n          chmod +x \"Contents/MacOS/Stardrop\"\n          (cd ../ && codesign --force --deep -s - Stardrop.app)\n          \n          (cd ../ && zip -r ${GITHUB_WORKSPACE}/Stardrop-osx-x64.zip \"Stardrop.app\")\n    - name: Upload artifacts\n      uses: actions/upload-artifact@v4\n      with:\n        name: release-artifacts-macos\n        path: |\n          Stardrop-osx-x64.zip\n  package-linux:  \n    if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'generate packages')\n    runs-on: ubuntu-latest\n    \n    steps:\n    - uses: actions/checkout@v4\n      with:\n          ref: ${{ env.BRANCH }}\n          repository: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name || github.repository }}\n    - run: mkdir -p path/to/release-artifacts\n    - name: Publish (Linux - Creating)\n      run:  |\n          dotnet publish \"${{ env.WORKING_DIRECTORY }}\" --output \"${{ env.OUTPUT_PATH_LINUX }}/Stardrop\" --configuration \"${{ env.CONFIGURATION }}\" --runtime \"linux-x64\" --framework \"net8.0\" --self-contained\n    - name: Publish (Linux - Packaging)\n      working-directory: \"${{ env.OUTPUT_PATH_LINUX }}/Stardrop\"\n      run:  |\n          mv Stardrop Internal\n          mv Stardrop.pdb Internal.pdb\n\n          cp ${GITHUB_WORKSPACE}/Stardrop/Assets/Stardrop.sh Stardrop.sh\n\n          chmod 755 ../Stardrop\n          chmod +x Stardrop.sh\n          chmod 644 Internal\n          chmod 644 Internal.pdb\n          \n          (cd ../ && zip -r ${GITHUB_WORKSPACE}/Stardrop-linux-x64.zip \"Stardrop\")\n    - name: Upload artifacts\n      uses: actions/upload-artifact@v4\n      with:\n        name: release-artifacts-linux\n        path: |\n          Stardrop-linux-x64.zip\n  package-windows:\n    if: github.event_name != 'pull_request' || contains(github.event.pull_request.labels.*.name, 'generate packages')\n    runs-on: ubuntu-latest\n    \n    steps:\n    - uses: actions/checkout@v4\n      with:\n          ref: ${{ env.BRANCH }}\n          repository: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name || github.repository }}\n    - run: mkdir -p path/to/release-artifacts\n    - name: Publish (Windows - Creating & Packaging)\n      run:  |\n          dotnet publish \"${{ env.WORKING_DIRECTORY }}\" --output \"${{ env.OUTPUT_PATH_WINDOWS }}/Stardrop\" --configuration \"${{ env.CONFIGURATION }}\" --runtime \"win-x64\" --framework \"net8.0\" --self-contained\n          (cd \"${{ env.OUTPUT_PATH_WINDOWS }}\" && zip -r ${GITHUB_WORKSPACE}/Stardrop-win-x64.zip \"Stardrop\")\n    - name: Upload artifacts\n      uses: actions/upload-artifact@v4\n      with:\n        name: release-artifacts-windows\n        path: |\n          Stardrop-win-x64.zip\n"
  },
  {
    "path": ".github/workflows/pr-target-action.yml",
    "content": "name: PR Target Check\n\non:\n  pull_request:\n    types:\n      - opened\n      - edited\n      - reopened\n      - synchronize\n  \njobs:\n  is-using-correct-target:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Check branches\n        run: |\n          if [ ${{ github.base_ref }} == \"stable\" ] && [ ${{ github.head_ref }} != \"development\" ]; then\n            echo \"Merge requests to the stable branch are only allowed from the development branch.\"\n            exit 1\n          fi\n"
  },
  {
    "path": ".github/workflows/version-build.yml",
    "content": "name: Package Version Release\n\non:\n  push:\n    tags:\n      - \"v*.*.*\"\n\npermissions:\n  contents: write\n\njobs:\n    package-and-release:\n      uses: ./.github/workflows/package-and-release.yml\n      with:\n        tag: ${{ github.ref_name }}\n        is-prerelease: ${{ contains(github.ref_name, '-beta') && 'true' || 'false' }}\n        generate-release-notes: true\n"
  },
  {
    "path": ".gitignore",
    "content": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore\n\n# User-specific files\n*.rsuser\n*.suo\n*.user\n*.userosscache\n*.sln.docstates\n\n# User-specific files (MonoDevelop/Xamarin Studio)\n*.userprefs\n\n# Build results\n[Dd]ebug/\n[Dd]ebugPublic/\n[Rr]elease/\n[Rr]eleases/\nx64/\nx86/\nbld/\n[Bb]in/\n[Oo]bj/\n[Ll]og/\n\n# Visual Studio 2015/2017 cache/options directory\n.vs/\n# Uncomment if you have tasks that create the project's static files in wwwroot\n#wwwroot/\n\n# Visual Studio 2017 auto generated files\nGenerated\\ Files/\n\n# MSTest test Results\n[Tt]est[Rr]esult*/\n[Bb]uild[Ll]og.*\n\n# NUNIT\n*.VisualState.xml\nTestResult.xml\n\n# Build Results of an ATL Project\n[Dd]ebugPS/\n[Rr]eleasePS/\ndlldata.c\n\n# Benchmark Results\nBenchmarkDotNet.Artifacts/\n\n# .NET Core\nproject.lock.json\nproject.fragment.lock.json\nartifacts/\n\n# StyleCop\nStyleCopReport.xml\n\n# Files built by Visual Studio\n*_i.c\n*_p.c\n*_h.h\n*.ilk\n*.meta\n*.obj\n*.iobj\n*.pch\n*.pdb\n*.ipdb\n*.pgc\n*.pgd\n*.rsp\n*.sbr\n*.tlb\n*.tli\n*.tlh\n*.tmp\n*.tmp_proj\n*_wpftmp.csproj\n*.log\n*.vspscc\n*.vssscc\n.builds\n*.pidb\n*.svclog\n*.scc\n\n# Chutzpah Test files\n_Chutzpah*\n\n# Visual C++ cache files\nipch/\n*.aps\n*.ncb\n*.opendb\n*.opensdf\n*.sdf\n*.cachefile\n*.VC.db\n*.VC.VC.opendb\n\n# Visual Studio profiler\n*.psess\n*.vsp\n*.vspx\n*.sap\n\n# Visual Studio Trace Files\n*.e2e\n\n# TFS 2012 Local Workspace\n$tf/\n\n# Guidance Automation Toolkit\n*.gpState\n\n# ReSharper is a .NET coding add-in\n_ReSharper*/\n*.[Rr]e[Ss]harper\n*.DotSettings.user\n\n# JustCode is a .NET coding add-in\n.JustCode\n\n# TeamCity is a build add-in\n_TeamCity*\n\n# DotCover is a Code Coverage Tool\n*.dotCover\n\n# AxoCover is a Code Coverage Tool\n.axoCover/*\n!.axoCover/settings.json\n\n# Visual Studio code coverage results\n*.coverage\n*.coveragexml\n\n# NCrunch\n_NCrunch_*\n.*crunch*.local.xml\nnCrunchTemp_*\n\n# MightyMoose\n*.mm.*\nAutoTest.Net/\n\n# Web workbench (sass)\n.sass-cache/\n\n# Installshield output folder\n[Ee]xpress/\n\n# DocProject is a documentation generator add-in\nDocProject/buildhelp/\nDocProject/Help/*.HxT\nDocProject/Help/*.HxC\nDocProject/Help/*.hhc\nDocProject/Help/*.hhk\nDocProject/Help/*.hhp\nDocProject/Help/Html2\nDocProject/Help/html\n\n# Click-Once directory\npublish/\n\n# Publish Web Output\n*.[Pp]ublish.xml\n*.azurePubxml\n# Note: Comment the next line if you want to checkin your web deploy settings,\n# but database connection strings (with potential passwords) will be unencrypted\n#*.pubxml\n*.publishproj\n\n# Microsoft Azure Web App publish settings. Comment the next line if you want to\n# checkin your Azure Web App publish settings, but sensitive information contained\n# in these scripts will be unencrypted\nPublishScripts/\n\n# NuGet Packages\n*.nupkg\n# The packages folder can be ignored because of Package Restore\n**/[Pp]ackages/*\n# except build/, which is used as an MSBuild target.\n!**/[Pp]ackages/build/\n# Uncomment if necessary however generally it will be regenerated when needed\n#!**/[Pp]ackages/repositories.config\n# NuGet v3's project.json files produces more ignorable files\n*.nuget.props\n*.nuget.targets\n\n# Microsoft Azure Build Output\ncsx/\n*.build.csdef\n\n# Microsoft Azure Emulator\necf/\nrcf/\n\n# Windows Store app package directories and files\nAppPackages/\nBundleArtifacts/\nPackage.StoreAssociation.xml\n_pkginfo.txt\n*.appx\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\n# Others\nClientBin/\n~$*\n*~\n*.dbmdl\n*.dbproj.schemaview\n*.jfm\n*.pfx\n*.publishsettings\norleans.codegen.cs\n\n# Including strong name files can present a security risk\n# (https://github.com/github/gitignore/pull/2483#issue-259490424)\n#*.snk\n\n# Since there are multiple workflows, uncomment next line to ignore bower_components\n# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)\n#bower_components/\n\n# RIA/Silverlight projects\nGenerated_Code/\n\n# Backup & report files from converting an old project file\n# to a newer Visual Studio version. Backup files are not needed,\n# because we have git ;-)\n_UpgradeReport_Files/\nBackup*/\nUpgradeLog*.XML\nUpgradeLog*.htm\nServiceFabricBackup/\n*.rptproj.bak\n\n# SQL Server files\n*.mdf\n*.ldf\n*.ndf\n\n# Business Intelligence projects\n*.rdl.data\n*.bim.layout\n*.bim_*.settings\n*.rptproj.rsuser\n\n# Microsoft Fakes\nFakesAssemblies/\n\n# GhostDoc plugin setting file\n*.GhostDoc.xml\n\n# Node.js Tools for Visual Studio\n.ntvs_analysis.dat\nnode_modules/\n\n# Visual Studio 6 build log\n*.plg\n\n# Visual Studio 6 workspace options file\n*.opt\n\n# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)\n*.vbw\n\n# Visual Studio LightSwitch build output\n**/*.HTMLClient/GeneratedArtifacts\n**/*.DesktopClient/GeneratedArtifacts\n**/*.DesktopClient/ModelManifest.xml\n**/*.Server/GeneratedArtifacts\n**/*.Server/ModelManifest.xml\n_Pvt_Extensions\n\n# Paket dependency manager\n.paket/paket.exe\npaket-files/\n\n# FAKE - F# Make\n.fake/\n\n# JetBrains Rider\n.idea/\n*.sln.iml\n\n# CodeRush personal settings\n.cr/personal\n\n# Python Tools for Visual Studio (PTVS)\n__pycache__/\n*.pyc\n\n# Cake - Uncomment if you are using it\n# tools/**\n# !tools/packages.config\n\n# Tabs Studio\n*.tss\n\n# Telerik's JustMock configuration file\n*.jmconfig\n\n# BizTalk build output\n*.btp.cs\n*.btm.cs\n*.odx.cs\n*.xsd.cs\n\n# OpenCover UI analysis results\nOpenCover/\n\n# Azure Stream Analytics local run output\nASALocalRun/\n\n# MSBuild Binary and Structured Log\n*.binlog\n\n# NVidia Nsight GPU debugger configuration file\n*.nvuser\n\n# MFractors (Xamarin productivity tool) working folder\n.mfractor/\n\n# Local History for Visual Studio\n.localhistory/\n\n"
  },
  {
    "path": "CODE_OF_CONDUCT.md",
    "content": "# Contributor Covenant Code of Conduct\n\n## Our Pledge\n\nWe as members, contributors, and leaders pledge to make participation in our\ncommunity a harassment-free experience for everyone, regardless of age, body\nsize, visible or invisible disability, ethnicity, sex characteristics, gender\nidentity and expression, level of experience, education, socio-economic status,\nnationality, personal appearance, race, religion, or sexual identity\nand orientation.\n\nWe pledge to act and interact in ways that contribute to an open, welcoming,\ndiverse, inclusive, and healthy community.\n\n## Our Standards\n\nExamples of behavior that contributes to a positive environment for our\ncommunity include:\n\n* Demonstrating empathy and kindness toward other people\n* Being respectful of differing opinions, viewpoints, and experiences\n* Giving and gracefully accepting constructive feedback\n* Accepting responsibility and apologizing to those affected by our mistakes,\n  and learning from the experience\n* Focusing on what is best not just for us as individuals, but for the\n  overall community\n\nExamples of unacceptable behavior include:\n\n* The use of sexualized language or imagery, and sexual attention or\n  advances of any kind\n* Trolling, insulting or derogatory comments, and personal or political attacks\n* Public or private harassment\n* Publishing others' private information, such as a physical or email\n  address, without their explicit permission\n* Other conduct which could reasonably be considered inappropriate in a\n  professional setting\n\n## Enforcement Responsibilities\n\nCommunity leaders are responsible for clarifying and enforcing our standards of\nacceptable behavior and will take appropriate and fair corrective action in\nresponse to any behavior that they deem inappropriate, threatening, offensive,\nor harmful.\n\nCommunity leaders have the right and responsibility to remove, edit, or reject\ncomments, commits, code, wiki edits, issues, and other contributions that are\nnot aligned to this Code of Conduct, and will communicate reasons for moderation\ndecisions when appropriate.\n\n## Scope\n\nThis Code of Conduct applies within all community spaces, and also applies when\nan individual is officially representing the community in public spaces.\nExamples of representing our community include using an official e-mail address,\nposting via an official social media account, or acting as an appointed\nrepresentative at an online or offline event.\n\n## Enforcement\n\nInstances of abusive, harassing, or otherwise unacceptable behavior may be\nreported to the community leaders responsible for enforcement at\n.\nAll complaints will be reviewed and investigated promptly and fairly.\n\nAll community leaders are obligated to respect the privacy and security of the\nreporter of any incident.\n\n## Enforcement Guidelines\n\nCommunity leaders will follow these Community Impact Guidelines in determining\nthe consequences for any action they deem in violation of this Code of Conduct:\n\n### 1. Correction\n\n**Community Impact**: Use of inappropriate language or other behavior deemed\nunprofessional or unwelcome in the community.\n\n**Consequence**: A private, written warning from community leaders, providing\nclarity around the nature of the violation and an explanation of why the\nbehavior was inappropriate. A public apology may be requested.\n\n### 2. Warning\n\n**Community Impact**: A violation through a single incident or series\nof actions.\n\n**Consequence**: A warning with consequences for continued behavior. No\ninteraction with the people involved, including unsolicited interaction with\nthose enforcing the Code of Conduct, for a specified period of time. This\nincludes avoiding interactions in community spaces as well as external channels\nlike social media. Violating these terms may lead to a temporary or\npermanent ban.\n\n### 3. Temporary Ban\n\n**Community Impact**: A serious violation of community standards, including\nsustained inappropriate behavior.\n\n**Consequence**: A temporary ban from any sort of interaction or public\ncommunication with the community for a specified period of time. No public or\nprivate interaction with the people involved, including unsolicited interaction\nwith those enforcing the Code of Conduct, is allowed during this period.\nViolating these terms may lead to a permanent ban.\n\n### 4. Permanent Ban\n\n**Community Impact**: Demonstrating a pattern of violation of community\nstandards, including sustained inappropriate behavior,  harassment of an\nindividual, or aggression toward or disparagement of classes of individuals.\n\n**Consequence**: A permanent ban from any sort of public interaction within\nthe community.\n\n## Attribution\n\nThis Code of Conduct is adapted from the [Contributor Covenant][homepage],\nversion 2.0, available at\nhttps://www.contributor-covenant.org/version/2/0/code_of_conduct.html.\n\nCommunity Impact Guidelines were inspired by [Mozilla's code of conduct\nenforcement ladder](https://github.com/mozilla/diversity).\n\n[homepage]: https://www.contributor-covenant.org\n\nFor answers to common questions about this code of conduct, see the FAQ at\nhttps://www.contributor-covenant.org/faq. Translations are available at\nhttps://www.contributor-covenant.org/translations.\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "If reporting an issue with Stardrop, please ensure that a copy of Stardrop's log is attached.\n\nThe log file can be found via Stardrop's menu under `View` > `Log File`.\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": "README.md",
    "content": "# Stardrop\n \nStardrop is an open-source, cross-platform mod manager for the game [Stardew Valley](https://www.stardewvalley.net/). It is built using the Avalonia UI framework.\n\nStadrop utilizes [SMAPI (Stardew Modding API)](https://smapi.io/) to simplify the management and update checking for all applicable Stardew Valley mods.\n\nProfiles are also supported, allowing users to have multiple mod groups for specific gameplay or multiplayer sessions.\n\n# Getting Started\n\nSee the [GitBook pages](https://floogen.gitbook.io/stardrop/) for detailed documentation on how to install, update and use Stardrop.\n\n## Downloading Stardrop\n\nSee the [release page](https://github.com/Floogen/Stardrop/releases/latest) for the latest builds.\n\n# Credits\n## Translations\nStardrop has been generously translated into several languages by the following users:\n\n* **Chinese** - guanyintu, PIut02, Z2549, CuteCat233\n* **French** - xynerorias\n* **German** - Schn1ek3\n* **Hungarian** - martin66789\n* **Italian** - S-zombie\n* **Japanese** - OishiiUnichan\n* **Korean** - buriburishoebill\n* **Polish** - Naciux\n* **Portuguese** - aracnus\n* **Russian** - Rongarah\n* **Spanish** - Evexyron, Gaelhaine\n* **Thai** - ellipszist\n* **Turkish** - KediDili\n* **Ukrainian** - burunduk, ChulkyBow, DanielleTlumach\n\n# Gallery\n\n![](https://imgur.com/WdjwfnG.gif)\n\n![](https://imgur.com/kalsOjS.gif)\n"
  },
  {
    "path": "Stardrop/App.axaml",
    "content": "<Application xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:local=\"using:Stardrop\"\n             x:Class=\"Stardrop.App\"\n             Name=\"Stardrop\">\n\t<Application.DataTemplates>\n\t\t<local:ViewLocator/>\n\t</Application.DataTemplates>\n\n\t<Application.Styles>\n\t\t<FluentTheme Mode=\"Light\" />\n\t\t<StyleInclude Source=\"avares://Avalonia.Controls.DataGrid/Themes/Fluent.xaml\"/>\n\t</Application.Styles>\n</Application>\n"
  },
  {
    "path": "Stardrop/App.axaml.cs",
    "content": "using Avalonia;\nusing Avalonia.Controls.ApplicationLifetimes;\nusing Avalonia.Markup.Xaml;\nusing Avalonia.Styling;\nusing Stardrop.Models.Nexus.Web;\nusing Stardrop.Utilities;\nusing Stardrop.Utilities.External;\nusing Stardrop.Views;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\n\nnamespace Stardrop\n{\n    public class App : Application\n    {\n        public override void Initialize()\n        {\n            AvaloniaXamlLoader.Load(this);\n\n            // Verify that the helper is instantiated, if it isn't then this code is likely reached by Avalonia's previewer and bypassed Main\n            if (Program.helper is null)\n            {\n                Program.helper = new Helper();\n            }\n\n            // Load in translations\n            Program.translation.LoadTranslations();\n\n            // Handle adding the themes\n            Dictionary<string, IStyle> themes = new Dictionary<string, IStyle>();\n            foreach (string fileFullName in Directory.EnumerateFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, \"Themes\"), \"*.xaml\", SearchOption.AllDirectories))\n            {\n                try\n                {\n                    var themeName = Path.GetFileNameWithoutExtension(fileFullName);\n                    themes[themeName] = AvaloniaRuntimeXamlLoader.Parse<Styles>(File.ReadAllText(fileFullName));\n                    Program.helper.Log($\"Loaded theme {Path.GetFileNameWithoutExtension(fileFullName)}\", Helper.Status.Debug);\n                }\n                catch (Exception ex)\n                {\n                    Program.helper.Log($\"Unable to load theme on {Path.GetFileNameWithoutExtension(fileFullName)}: {ex}\", Helper.Status.Warning);\n                }\n            }\n\n            Current.Styles.Insert(0, !themes.ContainsKey(Program.settings.Theme) ? themes.Values.First() : themes[Program.settings.Theme]);\n        }\n\n        private async void OnUrlsOpen(object? sender, UrlOpenedEventArgs e, MainWindow mainWindow)\n        {\n            foreach (string? url in e.Urls.Where(u => String.IsNullOrEmpty(u) is false))\n            {\n                await mainWindow.ProcessNXMLink(new NXM() { Link = url, Timestamp = DateTime.Now });\n            }\n        }\n\n        public override void OnFrameworkInitializationCompleted()\n        {\n            if (ApplicationLifetime is IClassicDesktopStyleApplicationLifetime desktop)\n            {\n                var mainWindow = new MainWindow();\n                desktop.MainWindow = mainWindow;\n\n                // Register events\n                this.UrlsOpened += (sender, e) => OnUrlsOpen(sender, e, mainWindow);\n            }\n\n            base.OnFrameworkInitializationCompleted();\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Assets/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\t<dict>\n\t\t<key>CFBundleInfoDictionaryVersion</key>\n\t\t<string>6.0</string>\n\t\t<key>CFBundlePackageType</key>\n\t\t<string>APPL</string>\n\t\t<key>CFBundleIconFile</key>\n\t\t<string>Stardrop</string>\n\t\t<key>CFBundleSignature</key>\n\t\t<string>com.Floogen.Stardrop</string>\n\t\t<key>CFBundleName</key>\n\t\t<string>Stardrop</string>\n\t\t<key>CFBundleExecutable</key>\n\t\t<string>Stardrop</string>\n\t\t<key>CFBundleIdentifier</key>\n\t\t<string>stardrop</string>\n\t\t<key>CFBundleVersion</key>\n\t\t<string>1.0.0</string>\n\t\t<key>CFBundleShortVersionString</key>\n\t\t<string>1.0</string>\n\t\t<key>CFBundleURLTypes</key>\n\t\t<array>\n\t\t\t<dict>\n\t\t\t\t<key>CFBundleURLName</key>\n\t\t\t\t<string>NXM</string>\n\t\t\t\t<key>CFBundleURLSchemes</key>\n\t\t\t\t<array>\n\t\t\t\t\t<string>nxm</string>\n\t\t\t\t</array>\n\t\t\t</dict>\n\t\t</array>\n\t\t<key>NSHighResolutionCapable</key>\n\t\t<string>true</string>\n\t</dict>\n</plist>"
  },
  {
    "path": "Stardrop/Assets/Stardrop.sh",
    "content": "#!/usr/bin/env bash\nchmod u+x ./Internal\n./Internal\n"
  },
  {
    "path": "Stardrop/Converters/EnumConverter.cs",
    "content": "﻿using Avalonia.Data.Converters;\nusing System;\nusing System.Globalization;\n\nnamespace Stardrop.Converters\n{\n    public class EnumConverter : IValueConverter\n    {\n        public object Convert(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            return Enum.GetName((value.GetType()), value);\n        }\n\n        public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)\n        {\n            throw new NotImplementedException();\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Converters/EnumEqualsConverter.cs",
    "content": "﻿using Avalonia.Data.Converters;\nusing System;\nusing System.Globalization;\n\nnamespace Stardrop.Converters\n{\n    public class EnumEqualsConverter : IValueConverter\n    {\n        public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)\n        {\n            if (value?.GetType()?.IsEnum is not true)\n            {\n                throw new ArgumentOutOfRangeException(nameof(value), \"Value must be a non-null enum.\");\n            }\n            if (parameter?.GetType()?.IsEnum is not true)\n            {\n                throw new ArgumentOutOfRangeException(nameof(parameter), \"Parameter must be a non-null enum.\");\n            }\n\n            return Enum.Equals(value, parameter);\n        }\n\n        public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)\n        {\n            throw new NotImplementedException();\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Config.cs",
    "content": "﻿using System;\n\nnamespace Stardrop.Models\n{\n    public class Config\n    {\n        public string UniqueId { get; set; }\n        public string FilePath { get; set; }\n        public DateTime LastWriteTimeUtc { get; set; }\n        public string Data { get; set; }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Data/ClientData.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace Stardrop.Models.Data\n{\n    public class ClientData\n    {\n        public List<ModInstallData> ModInstallData { get; set; }\n        public Dictionary<string, bool> ColumnActiveStates { get; set; } = new Dictionary<string, bool>();\n        public LastSessionData LastSessionData { get; set; }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Data/Enums/Choice.cs",
    "content": "﻿namespace Stardrop.Models.Data.Enums\n{\n    public enum Choice\n    {\n        First,\n        Second,\n        Third\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Data/Enums/DisplayFilter.cs",
    "content": "﻿namespace Stardrop.Models.Data.Enums\n{\n    public enum DisplayFilter\n    {\n        None,\n        ShowEnabled,\n        ShowDisabled,\n        RequireConfig\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Data/Enums/EndorsementResponse.cs",
    "content": "﻿namespace Stardrop.Models.Data.Enums\n{\n    public enum InstallState\n    {\n        Unknown,\n        Downloading,\n        Installing\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Data/Enums/InstallState.cs",
    "content": "﻿namespace Stardrop.Models.Data.Enums\n{\n    public enum EndorsementResponse\n    {\n        Unknown,\n        IsOwnMod,\n        TooSoonAfterDownload,\n        NotDownloadedMod,\n        Abstained,\n        Endorsed\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Data/Enums/ModGrouping.cs",
    "content": "﻿using System.ComponentModel;\n\nnamespace Stardrop.Models.Data.Enums\n{\n    public enum ModGrouping\n    {\n        None,\n        Folder,\n        [Description(\"Content Pack\")]\n        ContentPack,\n        [Description(\"Folder (Condensed)\")]\n        FolderCondensed\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Data/Enums/NexusServers.cs",
    "content": "﻿using System.ComponentModel;\n\nnamespace Stardrop.Models.Data.Enums\n{\n    public enum NexusServers\n    {\n        [Description(\"Nexus CDN\")]\n        NexusCDN,\n        Chicago,\n        Paris,\n        Amsterdam,\n        Prague,\n        [Description(\"Los Angeles\")]\n        LosAngeles,\n        Miami,\n        Singapore\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Data/LastSessionData.cs",
    "content": "﻿using Avalonia;\nusing System;\n\nnamespace Stardrop.Models.Data\n{\n    public class LastSessionData\n    {\n        public double Height { get; set; } = 800;\n        public double Width { get; set; } = 1430;\n        public int PositionX { get; set; }\n        public int PositionY { get; set; }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Data/ModDownloadEvents.cs",
    "content": "﻿using System;\nusing System.Threading;\n\nnamespace Stardrop.Models.Data\n{\n    internal record ModDownloadStartedEventArgs(Uri Uri, string Name, long? Size, CancellationTokenSource DownloadCancellationSource);    \n    internal record ModDownloadProgressEventArgs(Uri Uri, long TotalBytes);\n    internal record ModDownloadCompletedEventArgs(Uri Uri);\n    internal record ModDownloadFailedEventArgs(Uri Uri);\n}\n"
  },
  {
    "path": "Stardrop/Models/Data/ModInstallData.cs",
    "content": "﻿using System;\n\nnamespace Stardrop.Models.Data\n{\n    public class ModInstallData\n    {\n        public string UniqueId { get; set; }\n        public DateTime InstallTimestamp { get; set; }\n        public DateTime? LastUpdateTimestamp { get; set; }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Data/ModKeyInfo.cs",
    "content": "﻿namespace Stardrop.Models.Data\n{\n    public class ModKeyInfo\n    {\n        public string UniqueId { get; set; }\n        public string Name { get; set; }\n        public string PageUrl { get; set; }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Data/ModUpdateInfo.cs",
    "content": "﻿using static Stardrop.Models.SMAPI.Web.ModEntryMetadata;\n\nnamespace Stardrop.Models.Data\n{\n    public class ModUpdateInfo\n    {\n        public string UniqueId { get; set; }\n        public string SuggestedVersion { get; set; }\n        public WikiCompatibilityStatus Status { get; set; }\n        public string Link { get; set; }\n\n\n        public ModUpdateInfo()\n        {\n\n        }\n\n        public ModUpdateInfo(string uniqueId, string recommendedVersion, WikiCompatibilityStatus status, string link)\n        {\n            UniqueId = uniqueId;\n            SuggestedVersion = recommendedVersion;\n            Status = status;\n            Link = link;\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Data/PairedKeys.cs",
    "content": "﻿namespace Stardrop.Models.Data\n{\n    public class PairedKeys\n    {\n        public byte[] Lock { get; set; }\n        public byte[] Vector { get; set; }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Data/UpdateCache.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\n\nnamespace Stardrop.Models.Data\n{\n    public class UpdateCache\n    {\n        public DateTime LastRuntime { get; set; }\n        public List<ModUpdateInfo> Mods { get; set; }\n\n        public UpdateCache(DateTime lastRuntime)\n        {\n            LastRuntime = lastRuntime;\n            Mods = new List<ModUpdateInfo>();\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Mod.cs",
    "content": "﻿using Avalonia.Media.Imaging;\nusing Avalonia.Platform;\nusing Avalonia.Shared.PlatformSupport;\nusing Semver;\nusing Stardrop.Models.Data.Enums;\nusing Stardrop.Models.SMAPI;\nusing Stardrop.Utilities;\nusing System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.IO;\nusing System.Linq;\nusing System.Runtime.CompilerServices;\nusing System.Text.RegularExpressions;\nusing static Stardrop.Models.SMAPI.Web.ModEntryMetadata;\n\nnamespace Stardrop.Models\n{\n    public class Mod : INotifyPropertyChanged\n    {\n        internal readonly FileInfo ModFileInfo;\n        internal readonly Manifest Manifest;\n\n        public string UniqueId { get; set; }\n        public SemVersion Version { get; set; }\n        public string ParsedVersion { get { return Version.ToString(); } }\n        public string SuggestedVersion { get; set; }\n        public string Name { get; set; }\n        public string Path { get { return _path; } set { _path = value; RootPath = GetRootPath(value); } } // Whole mod path inside installed mods path for grouping mod components in the same mod\n        private string _path { get; set; }\n        public string RootPath { get; private set; } // Root mod path inside installed mods path for grouping mod components in the same mod\n        public string ManifestFilePath { get { return ModFileInfo.FullName; } }\n        public string Description { get; set; }\n        public string Summary { get { return $\"Author: {Author}\\nVersion: {ParsedVersion}\\nHas Config: {HasConfig}\\n\\n{Description}\"; } }\n        public string Author { get; set; }\n        public DateTime? InstallTimestamp { get; set; }\n        public DateTime? LastUpdateTimestamp { get; set; }\n        public Config? _config { get; set; }\n        public Config? Config { get { return _config; } set { _config = value; NotifyPropertyChanged(\"Config\"); NotifyPropertyChanged(\"HasConfig\"); } }\n        public bool HasConfig { get { return Config is not null; } }\n        public string FrameworkID { get; set; } = string.Empty;\n        private List<ManifestDependency> _requirements { get; set; }\n        public List<ManifestDependency> Requirements { get { return _requirements; } set { _requirements = value; NotifyPropertyChanged(\"Requirements\"); NotifyPropertyChanged(\"MissingRequirements\"); NotifyPropertyChanged(\"HardRequirements\"); } }\n        public List<ManifestDependency> MissingRequirements { get { return _requirements is null ? null : _requirements.Where(r => !String.IsNullOrEmpty(r.Name) && r.IsMissing && r.IsRequired).ToList(); } }\n        public List<ManifestDependency> HardRequirements { get { return _requirements is null ? null : _requirements.Where(r => !String.IsNullOrEmpty(r.Name) && !r.IsMissing && r.IsRequired).ToList(); } }\n        private string _updateUri { get; set; }\n        public string UpdateUri { get { return _updateUri; } set { _updateUri = value; NotifyPropertyChanged(\"UpdateUri\"); } }\n        private string _modPageUri { get; set; }\n        public string ModPageUri { get { return _modPageUri; } set { _modPageUri = value; NotifyPropertyChanged(\"ModPageUri\"); } }\n        public int? NexusModId { get { return GetNexusId(); } }\n        private string? _nexusModThumbnailPath { get; set; }\n        public string? NexusModThumbnailPath { get { return _nexusModThumbnailPath; } set { _nexusModThumbnailPath = value; NexusModThumbnailFile = TryLoadThumbnail(value); NotifyPropertyChanged(\"NexusModThumbnailFile\"); } }\n        public Bitmap? NexusModThumbnailFile { get; set; }\n        private bool _isEnabled { get; set; }\n        public bool IsEnabled\n        {\n            get { return _isEnabled; }\n            set\n            {\n                _isEnabled = value;\n                NotifyPropertyChanged(\"IsEnabled\");\n                NotifyPropertyChanged(\"ChangeStateText\");\n                NotifyPropertyChanged(\"ChangeWholeModGroupStateText\");\n            }\n        }\n        private bool _isHidden { get; set; }\n        public bool IsHidden { get { return _isHidden; } set { _isHidden = value; NotifyPropertyChanged(\"IsHidden\"); } }\n        private bool _isEndorsement { get; set; }\n        public bool IsEndorsed { get { return _isEndorsement; } set { _isEndorsement = value; NotifyPropertyChanged(\"IsEndorsed\"); } }\n        public string ChangeStateText { get { return IsEnabled ? Program.translation.Get(\"internal.disable\") : Program.translation.Get(\"internal.enable\"); } }\n        public string ChangeWholeModGroupStateText { get { return IsEnabled ? Program.translation.Get(\"internal.disable_whole_mod\") : Program.translation.Get(\"internal.enable_whole_mod\"); } }\n        private WikiCompatibilityStatus _status { get; set; }\n        public WikiCompatibilityStatus Status { get { return _status; } set { _status = value; NotifyPropertyChanged(\"Status\"); NotifyPropertyChanged(\"ParsedStatus\"); NotifyPropertyChanged(\"InstallStatus\"); } }\n        public string ParsedStatus\n        {\n            get\n            {\n                if (!String.IsNullOrEmpty(SuggestedVersion) && IsModOutdated(SuggestedVersion))\n                {\n                    if (_status == WikiCompatibilityStatus.Unofficial)\n                    {\n                        return String.Format(Program.translation.Get(\"ui.main_window.hyperlinks.unofficial_update_available\"), SuggestedVersion);\n                    }\n                    return String.Format(Program.translation.Get(\"ui.main_window.hyperlinks.update_available\"), SuggestedVersion);\n                }\n                else if (_status == WikiCompatibilityStatus.Broken)\n                {\n                    return Program.translation.Get(\"ui.main_window.hyperlinks.broken_compatibility_issue\");\n                }\n\n                return String.Empty;\n            }\n        }\n        private InstallState _installState { get; set; }\n        public InstallState InstallState { get { return _installState; } set { _installState = value; NotifyPropertyChanged(\"InstallState\"); NotifyPropertyChanged(\"InstallStatus\"); } }\n        public string InstallStatus\n        {\n            get\n            {\n                if (!String.IsNullOrEmpty(SuggestedVersion) && IsModOutdated(SuggestedVersion))\n                {\n                    var nexusModId = GetNexusId();\n                    if (_status == WikiCompatibilityStatus.Unofficial || nexusModId is null)\n                    {\n                        return String.Empty;\n                    }\n                    else if (InstallState == InstallState.Unknown)\n                    {\n                        return Program.translation.Get(\"ui.main_window.hyperlinks.install_update\");\n                    }\n\n                    return InstallState == InstallState.Downloading ? Program.translation.Get(\"ui.main_window.hyperlinks.downloading\") : Program.translation.Get(\"ui.main_window.hyperlinks.installing\");\n                }\n\n                return String.Empty;\n            }\n        }\n\n        public event PropertyChangedEventHandler? PropertyChanged;\n        public Mod(Manifest manifest, FileInfo modFileInfo, string uniqueId, string version, string? name = null, string? description = null, string? author = null)\n        {\n            Manifest = manifest;\n            ModFileInfo = modFileInfo;\n            UniqueId = uniqueId;\n            Version = SemVersion.TryParse(version, SemVersionStyles.Any, out var parsedVersion) ? parsedVersion : SemVersion.ParsedFrom(0, 0, 0, \"bad-version\");\n            Name = String.IsNullOrEmpty(name) ? uniqueId : name;\n            Path = ComputeModPath(modFileInfo);\n            Description = String.IsNullOrEmpty(description) ? String.Empty : description;\n            Author = String.IsNullOrEmpty(author) ? Program.translation.Get(\"internal.unknown\") : author;\n            Requirements = new List<ManifestDependency>();\n        }\n\n        /// <summary>\n        /// Compute relative path to a mod from the installed mods path or default Stardew Valley mods path.\n        /// </summary>\n        private string ComputeModPath(FileInfo modFileInfo)\n        {\n            // Set whole mod path for grouping with other mods from the same mod.\n            var commonNameInstalledFolder = Program.settings.ModInstallPath;\n            var commonNameModsFolder = Program.settings.ModFolderPath;\n            string modNamePath;\n            if (System.IO.Path.EndsInDirectorySeparator(commonNameInstalledFolder))\n            {\n                commonNameInstalledFolder += System.IO.Path.DirectorySeparatorChar;\n            }\n\n            if (modFileInfo.DirectoryName.Contains(commonNameModsFolder))\n            {\n                // Mod inside default Stardew Valley mods folder.\n                modNamePath = modFileInfo.DirectoryName.Substring(commonNameModsFolder.Length + 1);\n            }\n            else\n            {\n                throw new Exception($\"Invalid mod folder path: {modFileInfo.DirectoryName}\");\n            }\n\n            // TODO: Add program config option to switch between both approaches? And to disable grouping entirely?\n            // For top-level folder grouping.\n            // Producing group \"automation\" as a single group for both \"automation/Automate\" and\n            //  \"automation/Producer Framework Mod\".\n            // var foundIndex = modNamePath.IndexOf(System.IO.Path.DirectorySeparatorChar);\n            // For subfolders-specific grouping.\n            // Producing groups \"automation/Automate\" (with mods `[CP] Automate/manifest.json`, `[JA] Automate/manifest.json`)\n            //  and \"automation/Producer Framework Mod\" (with mods `[CP] PFM` and `[JA] PFM`) folders as separate groups.\n            var foundIndex = modNamePath.LastIndexOf(System.IO.Path.DirectorySeparatorChar);\n\n            var nameLength = foundIndex == -1 ? modNamePath.Length : foundIndex;\n            var finalPath = modNamePath.Substring(0, nameLength);\n            return String.IsNullOrEmpty(finalPath) ? Program.translation.Get(\"internal.unknown\") : finalPath;\n        }\n\n        private string GetRootPath(string path)\n        {\n            var foundIndex = path.IndexOf(System.IO.Path.DirectorySeparatorChar);\n            if (foundIndex == -1)\n            {\n                return path;\n            }\n\n            return path.Substring(0, foundIndex);\n        }\n\n        public bool IsModOutdated(string version)\n        {\n            if (String.IsNullOrEmpty(version) || !HasValidVersion())\n            {\n                return false;\n            }\n\n            return SemVersion.Parse(version, SemVersionStyles.Any).CompareSortOrderTo(Version) > 0;\n        }\n\n        public bool HasValidVersion()\n        {\n            if (Version.Prerelease.Equals(\"bad-version\", StringComparison.OrdinalIgnoreCase))\n            {\n                return false;\n            }\n\n            return true;\n        }\n\n        public bool HasUpdateKeys()\n        {\n            if (Manifest is not null && Manifest.UpdateKeys is not null && !Manifest.UpdateKeys.Any(k => String.IsNullOrEmpty(k)))\n            {\n                return true;\n            }\n\n            return false;\n        }\n\n        public int? GetNexusId()\n        {\n            if (HasUpdateKeys() is false)\n            {\n                return null;\n            }\n\n            foreach (string key in Manifest.UpdateKeys)\n            {\n                string cleanedKey = String.Concat(key.Where(c => !Char.IsWhiteSpace(c)));\n                var match = Regex.Match(key, @\"Nexus:[^0-9-]*(?<modId>-?\\d+)(?<flag>\\@.*)?.*\");\n                if (match.Success)\n                {\n                    if (Int32.TryParse(match.Groups[\"modId\"].ToString(), out int modId) && modId > 0)\n                    {\n                        return modId;\n                    }\n                }\n            }\n\n            return null;\n        }\n\n        public string? GetNexusFlag()\n        {\n            if (HasUpdateKeys() is false)\n            {\n                return null;\n            }\n\n            foreach (string key in Manifest.UpdateKeys)\n            {\n                string cleanedKey = String.Concat(key.Where(c => !Char.IsWhiteSpace(c)));\n                var match = Regex.Match(key, @\"Nexus:[^0-9-]*(?<modId>-?\\d+)(?<flag>\\@.*)?.*\");\n                if (match.Success)\n                {\n                    if (match.Groups.ContainsKey(\"flag\"))\n                    {\n                        return match.Groups[\"flag\"].ToString();\n                    }\n                }\n            }\n\n            return null;\n        }\n        \n        private Bitmap? TryLoadThumbnail(string? filePath)\n        {\n            if (string.IsNullOrEmpty(filePath))\n            {\n                return null;\n            }\n\n            try\n            {\n                var thumbnail = new Bitmap(filePath);\n                return thumbnail;\n            }\n            catch (Exception ex)\n            {\n                Program.helper.Log($\"Failed to load thumbnail for mod {UniqueId} using following path {filePath}\");\n                return null;\n            }\n        }\n\n        internal void NotifyPropertyChanged([CallerMemberName] String propertyName = \"\")\n        {\n            if (PropertyChanged != null)\n            {\n                PropertyChanged(this, new PropertyChangedEventArgs(propertyName));\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Nexus/NexusUser.cs",
    "content": "﻿namespace Stardrop.Models.Nexus\n{\n    public class NexusUser\n    {\n        public string Username { get; set; }\n        public bool IsPremium { get; set; }\n\n        public byte[] Key { get; set; }\n\n        public NexusUser()\n        {\n\n        }\n\n        public NexusUser(string username, byte[] key)\n        {\n            Username = username;\n            Key = key;\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Nexus/Web/DownloadLink.cs",
    "content": "﻿using System.Text.Json.Serialization;\n\nnamespace Stardrop.Models.Nexus.Web\n{\n    public class DownloadLink\n    {\n        [JsonPropertyName(\"name\")]\n        public string? Name { get; set; }\n\n        [JsonPropertyName(\"short_name\")]\n        public string? ShortName { get; set; }\n\n        [JsonPropertyName(\"URI\")]\n        public string? Uri { get; set; }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Nexus/Web/Endorsement.cs",
    "content": "﻿using System.Text.Json.Serialization;\n\nnamespace Stardrop.Models.Nexus.Web\n{\n    public class EndorsementResult\n    {\n        [JsonPropertyName(\"message\")]\n        public string? Message { get; set; }\n\n        [JsonPropertyName(\"status\")]\n        public string? Status { get; set; }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Nexus/Web/EndorsementResult.cs",
    "content": "﻿using System.Text.Json.Serialization;\n\nnamespace Stardrop.Models.Nexus.Web\n{\n    public class Endorsement\n    {\n        [JsonPropertyName(\"mod_id\")]\n        public int Id { get; set; }\n\n        [JsonPropertyName(\"domain_name\")]\n        public string? DomainName { get; set; }\n\n        [JsonPropertyName(\"status\")]\n        public string? Status { get; set; }\n\n        public bool IsEndorsed()\n        {\n            if (Status?.ToUpper() == \"ENDORSED\")\n            {\n                return true;\n            }\n\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Nexus/Web/ModDetails.cs",
    "content": "﻿using System.Text.Json.Serialization;\n\nnamespace Stardrop.Models.Nexus.Web\n{\n    public class ModDetails\n    {\n        [JsonPropertyName(\"name\")]\n        public string? Name { get; set; }\n\n        [JsonPropertyName(\"picture_url\")]\n        public string? ThumbnailUrl { get; set; }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Nexus/Web/ModFile.cs",
    "content": "﻿using System.Text.Json.Serialization;\n\nnamespace Stardrop.Models.Nexus.Web\n{\n    public class ModFile\n    {\n        [JsonPropertyName(\"file_id\")]\n        public int Id { get; set; }\n\n        [JsonPropertyName(\"file_name\")]\n        public string? Name { get; set; }\n\n        [JsonPropertyName(\"description\")]\n        public string? Description { get; set; }\n\n        [JsonPropertyName(\"version\")]\n        public string? Version { get; set; }\n\n        [JsonPropertyName(\"category_name\")]\n        public string? Category { get; set; }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Nexus/Web/ModFiles.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Text.Json.Serialization;\n\nnamespace Stardrop.Models.Nexus.Web\n{\n    public class ModFiles\n    {\n        [JsonPropertyName(\"files\")]\n        public List<ModFile> Files { get; set; }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Nexus/Web/NXM.cs",
    "content": "﻿using System;\n\nnamespace Stardrop.Models.Nexus.Web\n{\n    public class NXM\n    {\n        public string? Link { get; set; }\n        public DateTime Timestamp { get; set; }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Nexus/Web/NexusConnectionResult.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Stardrop.Models.Nexus.Web\n{\n    public class NexusConnectionResult\n    {\n        public string? Error { get; set; }\n        public string? Message { get; set; }\n\n        public string? ApiKey { get; set; }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Nexus/Web/Validate.cs",
    "content": "﻿using System.Text.Json.Serialization;\n\nnamespace Stardrop.Models.Nexus.Web\n{\n    public class Validate\n    {\n        [JsonPropertyName(\"name\")]\n        public string Name { get; set; }\n\n        [JsonPropertyName(\"is_premium\")]\n        public bool IsPremium { get; set; }\n\n        [JsonPropertyName(\"profile_url\")]\n        public string ProfileUrl { get; set; }\n\n        [JsonPropertyName(\"message\")]\n        public string Message { get; set; }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Nexus/Web/WebsocketResponse.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Text.Json.Serialization;\nusing System.Threading.Tasks;\n\nnamespace Stardrop.Models.Nexus.Web\n{\n    public class WebsocketResponse\n    {\n        [JsonPropertyName(\"success\")]\n        public bool Success { get; set; }\n\n        [JsonPropertyName(\"data\")]\n        public WebsocketResponseData? Data { get; set; }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Nexus/Web/WebsocketResponseData.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Text.Json.Serialization;\nusing System.Threading.Tasks;\n\nnamespace Stardrop.Models.Nexus.Web\n{\n    public class WebsocketResponseData\n    {\n        [JsonPropertyName(\"connection_token\")]\n        public string? ConnectionToken { get; set; }\n\n        [JsonPropertyName(\"api_key\")]\n        public string? ApiKey { get; set; }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Profile.cs",
    "content": "﻿using System.Collections.Generic;\nusing System.Text.Json;\n\nnamespace Stardrop.Models\n{\n    public class Profile\n    {\n        public string Name { get; set; }\n        public bool IsProtected { get; set; }\n        public List<string> EnabledModIds { get; set; }\n        public Dictionary<string, JsonDocument> PreservedModConfigs { get; set; }\n\n        public Profile()\n        {\n            Name = \"Unknown\";\n            IsProtected = false;\n            EnabledModIds = new List<string>();\n            PreservedModConfigs = new Dictionary<string, JsonDocument>();\n        }\n\n        public Profile(string name, bool isProtected = false, List<string>? enabledMods = null, Dictionary<string, JsonDocument>? preservedModConfigs = null)\n        {\n            Name = name;\n            IsProtected = isProtected;\n            EnabledModIds = enabledMods is null ? new List<string>() : enabledMods;\n            PreservedModConfigs = preservedModConfigs is null ? new Dictionary<string, JsonDocument>() : preservedModConfigs;\n        }\n\n        public Profile ShallowCopy()\n        {\n            return (Profile)this.MemberwiseClone();\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/SMAPI/Converters/BooleanConverter.cs",
    "content": "﻿using System;\nusing System.Text.Json;\nusing System.Text.Json.Serialization;\n\nnamespace Stardrop.Models.SMAPI.Converters\n{\n    internal class BooleanConverter : JsonConverter<bool>\n    {\n        public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)\n        {\n            if (reader.TokenType == JsonTokenType.True || reader.TokenType == JsonTokenType.False)\n            {\n                return reader.GetBoolean();\n            }\n\n            string value = reader.GetString();\n            if (Boolean.TryParse(value, out bool result))\n            {\n                return result;\n            }\n\n            return false;\n        }\n\n        public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOptions options)\n        {\n            throw new InvalidOperationException(\"This converter should not be used to write, it is read only.\");\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/SMAPI/Converters/BooleanConverterAssumeTrue.cs",
    "content": "﻿using System;\nusing System.Text.Json;\nusing System.Text.Json.Serialization;\n\nnamespace Stardrop.Models.SMAPI.Converters\n{\n    internal class BooleanConverterAssumeTrue : JsonConverter<bool>\n    {\n        public override bool Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)\n        {\n            if (reader.TokenType == JsonTokenType.True || reader.TokenType == JsonTokenType.False)\n            {\n                return reader.GetBoolean();\n            }\n\n            string value = reader.GetString();\n            if (Boolean.TryParse(value, out bool result))\n            {\n                return result;\n            }\n\n            return true;\n        }\n\n        public override void Write(Utf8JsonWriter writer, bool value, JsonSerializerOptions options)\n        {\n            throw new InvalidOperationException(\"This converter should not be used to write, it is read only.\");\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/SMAPI/Converters/ModKeyConverter.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.Text.Json;\nusing System.Text.Json.Serialization;\n\nnamespace Stardrop.Models.SMAPI.Converters\n{\n    internal class ModKeyConverter : JsonConverter<string[]>\n    {\n        public override string[] Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)\n        {\n            if (reader.TokenType != JsonTokenType.StartArray)\n            {\n                throw new JsonException();\n            }\n\n            var modKeys = new List<string>();\n            while (reader.Read())\n            {\n                if (reader.TokenType == JsonTokenType.EndArray)\n                {\n                    return modKeys.ToArray();\n                }\n\n                if (reader.TokenType == JsonTokenType.Number)\n                {\n                    modKeys.Add($\"Nexus: {reader.GetInt32()}\");\n                }\n                else\n                {\n                    modKeys.Add(reader.GetString());\n                }\n            }\n\n            // Should not reach here, due to reader.TokenType == JsonTokenType.EndArray\n            throw new JsonException();\n        }\n\n        public override void Write(Utf8JsonWriter writer, string[] value, JsonSerializerOptions options)\n        {\n            throw new InvalidOperationException(\"This converter should not be used to write, it is read only.\");\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/SMAPI/GameDetails.cs",
    "content": "﻿using Semver;\nusing System;\n\nnamespace Stardrop.Models.SMAPI\n{\n    public class GameDetails\n    {\n        public enum OS\n        {\n            Unknown,\n            Linux,\n            Mac,\n            Windows\n        }\n\n        /// <summary>Stardew Valley's game version.</summary>\n        public string GameVersion { get; set; }\n\n        /// <summary>SMAPI's version.</summary>\n        public string SmapiVersion { get; set; }\n\n        /// <summary>The operating system.</summary>\n        public OS System { get; set; }\n\n        public GameDetails()\n        {\n\n        }\n\n        public GameDetails(string gameVersion, string smapiVersion, string system)\n        {\n            GameVersion = gameVersion;\n            if (GameVersion.Contains(' '))\n            {\n                GameVersion = GameVersion.Split(' ')[0];\n            }\n            SmapiVersion = smapiVersion;\n\n            if (system.Contains(\"macOS\", StringComparison.OrdinalIgnoreCase))\n            {\n                System = OS.Mac;\n            }\n            else if (system.Contains(\"Windows\", StringComparison.OrdinalIgnoreCase))\n            {\n                System = OS.Windows;\n            }\n            else\n            {\n                System = OS.Linux;\n            }\n        }\n\n        public bool HasSMAPIUpdated(string version)\n        {\n            if (String.IsNullOrEmpty(version))\n            {\n                return false;\n            }\n\n            return HasSMAPIUpdated(SemVersion.Parse(version));\n        }\n\n        public bool HasSMAPIUpdated(SemVersion version)\n        {\n            if (version is null)\n            {\n                return false;\n            }\n\n            return version != SemVersion.Parse(SmapiVersion);\n        }\n\n        public bool HasBadGameVersion()\n        {\n            if (GameVersion.Contains(' '))\n            {\n                return true;\n            }\n\n            return false;\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/SMAPI/Manifest.cs",
    "content": "﻿using Stardrop.Models.SMAPI.Converters;\nusing System.Text.Json.Serialization;\n\nnamespace Stardrop.Models.SMAPI\n{\n    public class Manifest\n    {\n        // Based on SMAPI's Manfiest.cs: https://github.com/Pathoschild/SMAPI/blob/c10685b03574e967c1bf48aafc814f60196812ec/src/SMAPI.Toolkit/Serialization/Models/Manifest.cs\n\n        /// <summary>The mod name.</summary>\n        public string Name { get; set; }\n\n        /// <summary>A brief description of the mod.</summary>\n        public string Description { get; set; }\n\n        /// <summary>The namespaced mod IDs to query for updates (like <c>Nexus:541</c>).</summary>\n        [JsonConverter(typeof(ModKeyConverter))]\n        public string[] UpdateKeys { get; set; }\n\n        /// <summary>The mod author's name.</summary>\n        public string Author { get; set; }\n\n        /// <summary>The mod version.</summary>\n        public string Version { get; set; }\n\n        /// <summary>The unique mod ID.</summary>\n        public string UniqueID { get; set; }\n\n        /// <summary>The mod which will read this as a content pack. Mutually exclusive with <see cref=\"Manifest.EntryDll\"/>.</summary>\n        //[JsonConverter(typeof(ManifestContentPackForConverter))]\n        public ManifestContentPackFor ContentPackFor { get; set; }\n\n        /// <summary>The other mods that must be loaded before this mod.</summary>\n        //[JsonConverter(typeof(ManifestDependencyArrayConverter))]\n        public ManifestDependency[] Dependencies { get; set; }\n\n        /// <summary>Custom property for Stardrop.</summary>\n        public bool DeleteOldVersion { get; set; }\n\n        /// <summary>Custom property for Stardrop.</summary>\n        public string? UpdateCautionMessage { get; set; }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/SMAPI/ManifestContentPackFor.cs",
    "content": "﻿namespace Stardrop.Models.SMAPI\n{\n    public class ManifestContentPackFor\n    {\n        // Based on SMAPI's IManifestContentPackFor.cs: https://github.com/Pathoschild/SMAPI/blob/develop/src/SMAPI.Toolkit.CoreInterfaces/IManifestContentPackFor.cs\n\n        /// <summary>The unique ID of the mod which can read this content pack.</summary>\n        public string UniqueID { get; set; }\n\n        /// <summary>The minimum required version (if any).</summary>\n        public string MinimumVersion { get; set; }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/SMAPI/ManifestDependency.cs",
    "content": "﻿using Stardrop.Models.SMAPI.Converters;\nusing System.ComponentModel;\nusing System.Text.Json.Serialization;\n\nnamespace Stardrop.Models.SMAPI\n{\n    public class ManifestDependency : INotifyPropertyChanged\n    {\n        // Based on SMAPI's IManifestDependency.cs: https://github.com/Pathoschild/SMAPI/blob/develop/src/SMAPI.Toolkit.CoreInterfaces/IManifestDependency.cs\n\n        /// <summary>The unique mod ID to require.</summary>\n        public string UniqueID { get; set; }\n\n        /// <summary>The minimum required version (if any).</summary>\n        public string MinimumVersion { get; set; }\n\n        /// <summary>Whether the dependency must be installed to use the mod.</summary>\n        [JsonConverter(typeof(BooleanConverterAssumeTrue))]\n        public bool IsRequired { get; set; }\n\n        // <summary>Custom properties for Stardrop.</summary>\n        private string _name { get; set; }\n        public string Name { get { return _name; } set { _name = value; NotifyPropertyChanged(\"Name\"); NotifyPropertyChanged(\"GenericLink\"); } }\n        public bool IsMissing { get; set; }\n        public string GenericLink { get { return $\"https://smapi.io/mods#{Name.Replace(\" \", \"_\")}\"; } }\n\n        public event PropertyChangedEventHandler? PropertyChanged;\n        public ManifestDependency(string uniqueId, string minimumVersion, bool isRequired = true)\n        {\n            UniqueID = uniqueId;\n            MinimumVersion = minimumVersion;\n            IsRequired = isRequired;\n        }\n\n        private void NotifyPropertyChanged(string propertyName)\n        {\n            var handler = PropertyChanged;\n            if (handler != null)\n                handler(this, new PropertyChangedEventArgs(propertyName));\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/SMAPI/Web/ModEntry.cs",
    "content": "﻿namespace Stardrop.Models.SMAPI.Web\n{\n    public class ModEntry\n    {\n        // Based on SMAPI's ModEntryModel.cs: https://github.com/Pathoschild/SMAPI/blob/develop/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryModel.cs\n\n        /// <summary>The mod's unique ID.</summary>\n        public string Id { get; set; }\n\n        /// <summary>The update version recommended by the web API based on its version update and mapping rules.</summary>\n        public ModEntryVersion SuggestedUpdate { get; set; }\n\n        /// <summary>Optional extended data which isn't needed for update checks.</summary>\n        public ModEntryMetadata Metadata { get; set; }\n\n        /// <summary>The errors that occurred while fetching update data.</summary>\n        public string[] Errors { get; set; } = new string[0];\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/SMAPI/Web/ModEntryMetadata.cs",
    "content": "﻿using System.Text.Json.Serialization;\n\nnamespace Stardrop.Models.SMAPI.Web\n{\n    public class ModEntryMetadata\n    {\n        // Based on SMAPI's WikiCompatibilityStatus.cs: https://github.com/Pathoschild/SMAPI/blob/develop/src/SMAPI.Toolkit/Framework/Clients/Wiki/WikiCompatibilityStatus.cs\n        /// <summary>The compatibility status for a mod.</summary>\n        public enum WikiCompatibilityStatus\n        {\n            /// <summary>The status is unknown.</summary>\n            Unknown,\n\n            /// <summary>The mod is compatible.</summary>\n            Ok,\n\n            /// <summary>The mod is compatible if you use an optional official download.</summary>\n            Optional,\n\n            /// <summary>The mod is compatible if you use an unofficial update.</summary>\n            Unofficial,\n\n            /// <summary>The mod isn't compatible, but the player can fix it or there's a good alternative.</summary>\n            Workaround,\n\n            /// <summary>The mod isn't compatible.</summary>\n            Broken,\n\n            /// <summary>The mod is no longer maintained by the author, and an unofficial update or continuation is unlikely.</summary>\n            Abandoned,\n\n            /// <summary>The mod is no longer needed and should be removed.</summary>\n            Obsolete\n        }\n\n        // Based on SMAPI's ModExtendedMetadataModel.cs: https://github.com/Pathoschild/SMAPI/blob/develop/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModExtendedMetadataModel.cs\n        /// <summary>The mod's display name.</summary>\n        public string Name { get; set; }\n\n        /// <summary>The main version.</summary>\n        public ModEntryVersion Main { get; set; }\n\n        /// <summary>The latest unofficial version, if newer than <see cref=\"Main\"/> and <see cref=\"Optional\"/>.</summary>\n        public ModEntryVersion Unofficial { get; set; }\n        public string CustomUrl { get; set; }\n\n        [JsonConverter(typeof(JsonStringEnumConverter))]\n        public WikiCompatibilityStatus CompatibilityStatus { get; set; }\n\n        /// <summary>The human-readable summary of the compatibility status or workaround, without HTML formatting.</summary>\n        public string CompatibilitySummary { get; set; }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/SMAPI/Web/ModEntryVersion.cs",
    "content": "﻿namespace Stardrop.Models.SMAPI.Web\n{\n    public class ModEntryVersion\n    {\n        // Based on SMAPI's ModEntryVersionModel.cs: https://github.com/Pathoschild/SMAPI/blob/develop/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModEntryVersionModel.cs\n\n        /*********\n        ** Accessors\n        *********/\n        /// <summary>The version number.</summary>\n        public string Version { get; set; }\n\n        /// <summary>The mod page URL.</summary>\n        public string Url { get; set; }\n\n\n        /*********\n        ** Public methods\n        *********/\n        /// <summary>Construct an instance.</summary>\n        public ModEntryVersion() { }\n\n        /// <summary>Construct an instance.</summary>\n        /// <param name=\"version\">The version number.</param>\n        /// <param name=\"url\">The mod page URL.</param>\n        public ModEntryVersion(string version, string url)\n        {\n            this.Version = version;\n            this.Url = url;\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/SMAPI/Web/ModSearchData.cs",
    "content": "﻿using System.Collections.Generic;\n\nnamespace Stardrop.Models.SMAPI.Web\n{\n    class ModSearchData\n    {\n        // Based on SMAPI's ModSearchModel.cs: https://github.com/Pathoschild/SMAPI/blob/develop/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchModel.cs\n\n        /*********\n        ** Accessors\n        *********/\n        /// <summary>The mods for which to find data.</summary>\n        public ModSearchEntry[] Mods { get; set; }\n\n        /// <summary>Whether to include extended metadata for each mod.</summary>\n        public bool IncludeExtendedMetadata { get; set; }\n\n        /// <summary>The SMAPI version installed by the player. This is used for version mapping in some cases.</summary>\n        public string ApiVersion { get; set; }\n\n        /// <summary>The Stardew Valley version installed by the player.</summary>\n        public string GameVersion { get; set; }\n\n        /// <summary>The OS on which the player plays.</summary>\n        public string Platform { get; set; }\n\n\n        /*********\n        ** Public methods\n        *********/\n        /// <summary>Construct an empty instance.</summary>\n        public ModSearchData()\n        {\n            // needed for JSON deserializing\n        }\n\n        /// <summary>Construct an instance.</summary>\n        /// <param name=\"mods\">The mods to search.</param>\n        /// <param name=\"apiVersion\">The SMAPI version installed by the player. If this is null, the API won't provide a recommended update.</param>\n        /// <param name=\"gameVersion\">The Stardew Valley version installed by the player.</param>\n        /// <param name=\"platform\">The OS on which the player plays.</param>\n        /// <param name=\"includeExtendedMetadata\">Whether to include extended metadata for each mod.</param>\n        public ModSearchData(List<ModSearchEntry> mods, string apiVersion, string gameVersion, string platform, bool includeExtendedMetadata)\n        {\n            this.Mods = mods.ToArray();\n            this.ApiVersion = apiVersion.ToString();\n            this.GameVersion = gameVersion.ToString();\n            this.Platform = platform;\n            this.IncludeExtendedMetadata = includeExtendedMetadata;\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/SMAPI/Web/ModSearchEntry.cs",
    "content": "﻿using Semver;\n\nnamespace Stardrop.Models.SMAPI.Web\n{\n    public class ModSearchEntry\n    {\n        // Based on SMAPI's ModSearchEntryModel.cs: https://github.com/Pathoschild/SMAPI/blob/develop/src/SMAPI.Toolkit/Framework/Clients/WebApi/ModSearchEntryModel.cs\n\n        /*********\n        ** Accessors\n        *********/\n        /// <summary>The unique mod ID.</summary>\n        public string Id { get; set; }\n\n        /// <summary>The namespaced mod update keys (if available).</summary>\n        public string[] UpdateKeys { get; set; }\n\n        /// <summary>The mod version installed by the local player. This is used for version mapping in some cases.</summary>\n        public string InstalledVersion { get; set; }\n\n\n        /*********\n        ** Public methods\n        *********/\n        /// <summary>Construct an empty instance.</summary>\n        public ModSearchEntry()\n        {\n            // needed for JSON deserializing\n        }\n\n        /// <summary>Construct an instance.</summary>\n        /// <param name=\"id\">The unique mod ID.</param>\n        /// <param name=\"installedVersion\">The version installed by the local player. This is used for version mapping in some cases.</param>\n        /// <param name=\"updateKeys\">The namespaced mod update keys (if available).</param>\n        /// <param name=\"isBroken\">Whether the installed version is broken or could not be loaded.</param>\n        public ModSearchEntry(string id, SemVersion installedVersion, string[] updateKeys, bool isBroken = false)\n        {\n            this.Id = id;\n            this.InstalledVersion = installedVersion.ToString();\n            this.UpdateKeys = updateKeys ?? new string[0];\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Settings.cs",
    "content": "﻿using Stardrop.Models.Data.Enums;\nusing Stardrop.Models.Nexus;\nusing Stardrop.Models.SMAPI;\n\nnamespace Stardrop.Models\n{\n    public class Settings\n    {\n        public string Theme { get; set; } = \"Stardrop\";\n        public string Language { get; set; }\n        public ModGrouping ModGroupingMethod { get; set; } = ModGrouping.None;\n        public string Version { get; set; }\n        public string LastSelectedProfileName { get; set; }\n        public string SMAPIFolderPath { get; set; }\n        public string ModFolderPath { get; set; }\n        public string ModInstallPath { get; set; }\n        public bool IgnoreHiddenFolders { get; set; } = true;\n        public bool EnableProfileSpecificModConfigs { get; set; }\n        public bool ShouldWriteToModConfigs { get; set; }\n        public bool EnableModsOnAdd { get; set; }\n        /// <summary>\n        /// Whether to always ask before deleting a previous version of a mod when updating the mod.\n        /// </summary>\n        public bool AlwaysAskToDelete { get; set; } = true;\n        public bool ShouldAutomaticallySaveProfileChanges { get; set; } = true;\n        public bool ShowModThumbnails { get; set; }\n        public NexusServers PreferredNexusServer { get; set; } = NexusServers.NexusCDN;\n        public bool IsAskingBeforeAcceptingNXM { get; set; } = true;\n        public GameDetails GameDetails { get; set; }\n        public NexusUser NexusDetails { get; set; }\n\n        public Settings ShallowCopy()\n        {\n            return (Settings)this.MemberwiseClone();\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Models/Theme.cs",
    "content": "﻿using Avalonia.Styling;\nusing System;\nusing System.Collections.Generic;\nusing System.Linq;\nusing System.Text;\nusing System.Threading.Tasks;\n\nnamespace Stardrop.Models\n{\n    public class Theme\n    {\n        public required string Name { get; set; }\n        public string? Author { get; set; }\n        public bool IsEnabled { get; set; }\n\n        public IStyle? Style { get; set; }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Program.cs",
    "content": "using Avalonia;\nusing Avalonia.ReactiveUI;\nusing Avalonia.Shared.PlatformSupport;\nusing CommandLine;\nusing Projektanker.Icons.Avalonia;\nusing Projektanker.Icons.Avalonia.MaterialDesign;\nusing Semver;\nusing Stardrop.Models;\nusing Stardrop.Models.Nexus;\nusing Stardrop.Models.Nexus.Web;\nusing Stardrop.Utilities;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.Globalization;\nusing System.IO;\nusing System.Linq;\nusing System.Reflection;\nusing System.Runtime.InteropServices;\nusing System.Text.Json;\nusing System.Text.RegularExpressions;\nusing System.Threading;\n\nnamespace Stardrop\n{\n    class Program\n    {\n        internal static Helper helper;\n        internal static Settings settings = new Settings();\n        internal static Translation translation = new Translation();\n        internal static AssetLoader assetLoader = new AssetLoader();\n\n        internal static bool onBootStartSMAPI = false;\n        internal static string? nxmLink = null;\n        internal static readonly string defaultProfileName = \"Default\";\n        internal static readonly string executablePath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, \"Stardrop.exe\");\n        internal static readonly Regex gameDetailsPattern = new Regex(@\"SMAPI (?<smapiVersion>.+) with Stardew Valley (?<gameVersion>.+) on (?<system>.+)\");\n\n        public static string ApplicationVersion { get { return $\"{_applicationVersion.WithoutMetadata()}\"; } }\n        private static readonly SemVersion _applicationVersion = SemVersion.Parse(typeof(Program).Assembly.GetCustomAttribute<AssemblyInformationalVersionAttribute>().InformationalVersion, SemVersionStyles.Any);\n\n        public class Options\n        {\n            [Option(\"start-smapi\", Required = false, HelpText = \"Automatically starts SMAPI based on the last selected mod profile.\")]\n            public bool StartSmapi { get; set; }\n            [Option(\"nxm\", Required = false, HelpText = \"Downloads the given NXM file from Nexus Mods.\")]\n            public string? NXMLink { get; set; }\n        }\n\n        // Initialization code. Don't use any Avalonia, third-party APIs or any\n        // SynchronizationContext-reliant code before AppMain is called: things aren't initialized\n        // yet and stuff might break.\n        [STAThread]\n        public static void Main(string[] args)\n        {\n            // Enforce the directory's path\n            Directory.SetCurrentDirectory(Path.GetDirectoryName(Process.GetCurrentProcess().MainModule.FileName));\n\n            // Establish file and folders paths\n            Pathing.SetHomePath(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData));\n\n            // Verify if another instance is already running\n            if (Process.GetProcessesByName(Process.GetCurrentProcess().ProcessName).Count() > 1 && RuntimeInformation.IsOSPlatform(OSPlatform.OSX) is false)\n            {\n                helper = new Helper($\"nxm\", \".txt\", Pathing.GetLogFolderPath());\n\n                HandleSecondaryInstance(args);\n                return;\n            }\n\n            // Set up our logger\n            helper = new Helper(\"log\", \".txt\", Pathing.GetLogFolderPath());\n\n            try\n            {\n                var operatingSystem = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? \"Windows\" : RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? \"Unix\" : RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? \"macOS\" : \"Unknown\";\n                helper.Log($\"{Environment.NewLine}-- Startup Data --{Environment.NewLine}Time: {DateTime.Now}{Environment.NewLine}OS: [{operatingSystem}] {RuntimeInformation.OSDescription}{Environment.NewLine}Settings Directory: {Pathing.defaultHomePath}{Environment.NewLine}Active Directory: {Directory.GetCurrentDirectory()}{Environment.NewLine}Version: v{ApplicationVersion}{Environment.NewLine}----------------------{Environment.NewLine}\");\n                helper.Log($\"Started with the following arguments: {String.Join('|', args)}\");\n\n                // Set the argument values\n                Parser.Default.ParseArguments<Options>(args).WithParsed<Options>(o =>\n                {\n                    onBootStartSMAPI = o.StartSmapi;\n                    nxmLink = o.NXMLink;\n                });\n\n                // Verify the folder paths are created\n                Directory.CreateDirectory(Pathing.GetCacheFolderPath());\n                Directory.CreateDirectory(Pathing.GetLogFolderPath());\n                Directory.CreateDirectory(Pathing.GetProfilesFolderPath());\n                Directory.CreateDirectory(Pathing.GetSelectedModsFolderPath());\n                Directory.CreateDirectory(Pathing.GetNexusPath());\n                Directory.CreateDirectory(Pathing.GetThumbnailsPath());\n                Directory.CreateDirectory(Pathing.GetSmapiUpgradeFolderPath());\n\n                // Verify the settings folder path is created\n                if (File.Exists(Pathing.GetSettingsPath()))\n                {\n                    try\n                    {\n                        settings = JsonSerializer.Deserialize<Settings>(File.ReadAllText(Pathing.GetSettingsPath()), new JsonSerializerOptions { AllowTrailingCommas = true });\n                    }\n                    catch (JsonException ex)\n                    {\n                        settings = new Settings();\n                        helper.Log($\"Reset the settings.json file as it was unreadable: {ex}\", Helper.Status.Alert);\n                    }\n                }\n\n                // Set the default paths\n                if (!String.IsNullOrEmpty(settings.ModFolderPath))\n                {\n                    Pathing.SetSmapiPath(settings.SMAPIFolderPath);\n                    Pathing.SetModPath(settings.ModFolderPath);\n                }\n                else\n                {\n                    Pathing.SetSmapiPath(settings.SMAPIFolderPath, true);\n                    settings.ModFolderPath = Pathing.defaultModPath;\n                }\n\n                // Set the default mod install path (for mods that are installed by Stardrop)\n                if (!String.IsNullOrEmpty(Pathing.defaultModPath) && String.IsNullOrEmpty(settings.ModInstallPath))\n                {\n                    settings.ModInstallPath = Path.Combine(Pathing.defaultModPath, \"Stardrop Installed Mods\");\n                }\n\n                // Set the default Nexus Mods information\n                if (settings.NexusDetails is null)\n                {\n                    settings.NexusDetails = new NexusUser();\n                }\n\n                // Delete any files underneath the Nexus folder\n                var nexusDirectory = new DirectoryInfo(Pathing.GetNexusPath());\n                foreach (FileInfo file in nexusDirectory.GetFiles())\n                {\n                    file.Delete();\n                }\n                foreach (DirectoryInfo dir in nexusDirectory.GetDirectories())\n                {\n                    dir.Delete(true);\n                }\n\n                // Load the translations\n                if (String.IsNullOrEmpty(settings.Language))\n                {\n                    settings.Language = translation.GetLanguageFromAbbreviation(CultureInfo.CurrentCulture.TwoLetterISOLanguageName);\n                }\n                translation.LoadTranslations(translation.GetLanguage(settings.Language));\n\n                BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);\n            }\n            catch (Exception ex)\n            {\n                helper.Log(ex, Helper.Status.Alert);\n            }\n        }\n\n        private static void HandleSecondaryInstance(string[] args)\n        {\n            Parser.Default.ParseArguments<Options>(args).WithParsed<Options>(o =>\n            {\n                nxmLink = o.NXMLink;\n            });\n\n            // Verify NXM link is valid\n            if (String.IsNullOrEmpty(nxmLink))\n            {\n                Program.helper.Log(\"Given empty NXM link\");\n                return;\n            }\n\n            // Write to bridge file\n            int attempts = 0;\n\n            Program.helper.Log(\"STARTING\");\n            while (true)\n            {\n                try\n                {\n                    using (FileStream stream = new FileStream(Pathing.GetLinksCachePath(), FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None))\n                    {\n                        List<NXM> links;\n                        try\n                        {\n                            links = JsonSerializer.DeserializeAsync<List<NXM>>(stream, new JsonSerializerOptions { AllowTrailingCommas = true }).Result;\n\n                            if (links is null)\n                            {\n                                links = new List<NXM>();\n                            }\n                        }\n                        catch (JsonException ex)\n                        {\n                            links = new List<NXM>();\n                        }\n\n                        try\n                        {\n                            links.Add(new NXM() { Link = nxmLink, Timestamp = DateTime.Now });\n                            Program.helper.Log(links.Count());\n\n                            stream.SetLength(0);\n\n                            JsonSerializer.SerializeAsync(stream, links, new JsonSerializerOptions() { WriteIndented = true });\n\n                            break;\n                        }\n                        catch (Exception ex)\n                        {\n                            Program.helper.Log(ex);\n                        }\n                    }\n                }\n                catch (Exception ex)\n                {\n                    Program.helper.Log(ex);\n                }\n\n                if (attempts >= 3)\n                {\n                    return;\n                }\n                else\n                {\n                    attempts += 1;\n                    Thread.Sleep(500);\n                }\n            }\n\n            Program.helper.Log(\"DONE\");\n        }\n\n        // Avalonia configuration, don't remove; also used by visual designer.\n        public static AppBuilder BuildAvaloniaApp()\n        {\n            return AppBuilder.Configure<App>()\n                .UseReactiveUI()\n                .UsePlatformDetect()\n                .LogToTrace()\n                .WithIcons(container => container\n                .Register<MaterialDesignIconProvider>());\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Properties/PublishProfiles/FolderProfile - Linux.pubxml",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\nhttps://go.microsoft.com/fwlink/?LinkID=208121. \n-->\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <PropertyGroup>\n    <Configuration>Release</Configuration>\n    <Platform>Any CPU</Platform>\n    <PublishDir>publish\\linux</PublishDir>\n    <PublishProtocol>FileSystem</PublishProtocol>\n    <TargetFramework>net7.0</TargetFramework>\n    <RuntimeIdentifier>linux-x64</RuntimeIdentifier>\n    <SelfContained>true</SelfContained>\n    <PublishSingleFile>true</PublishSingleFile>\n  </PropertyGroup>\n</Project>"
  },
  {
    "path": "Stardrop/Properties/PublishProfiles/FolderProfile - MacOS.pubxml",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\nhttps://go.microsoft.com/fwlink/?LinkID=208121. \n-->\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <PropertyGroup>\n    <Configuration>Release</Configuration>\n    <Platform>Any CPU</Platform>\n    <PublishDir>publish\\mac</PublishDir>\n    <PublishProtocol>FileSystem</PublishProtocol>\n    <TargetFramework>net7.0</TargetFramework>\n    <RuntimeIdentifier>osx-x64</RuntimeIdentifier>\n    <SelfContained>true</SelfContained>\n    <PublishSingleFile>true</PublishSingleFile>\n  </PropertyGroup>\n</Project>"
  },
  {
    "path": "Stardrop/Properties/PublishProfiles/FolderProfile - Windows.pubxml",
    "content": "﻿<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<!--\nhttps://go.microsoft.com/fwlink/?LinkID=208121. \n-->\n<Project ToolsVersion=\"4.0\" xmlns=\"http://schemas.microsoft.com/developer/msbuild/2003\">\n  <PropertyGroup>\n    <Configuration>Release</Configuration>\n    <Platform>Any CPU</Platform>\n    <PublishDir>publish\\windows</PublishDir>\n    <PublishProtocol>FileSystem</PublishProtocol>\n    <TargetFramework>net7.0</TargetFramework>\n    <RuntimeIdentifier>win-x64</RuntimeIdentifier>\n    <SelfContained>true</SelfContained>\n    <PublishSingleFile>true</PublishSingleFile>\n    <PublishReadyToRun>false</PublishReadyToRun>\n  </PropertyGroup>\n</Project>"
  },
  {
    "path": "Stardrop/Stardrop.csproj",
    "content": "﻿<Project Sdk=\"Microsoft.NET.Sdk\">\n\t<PropertyGroup>\n\t\t<OutputType>WinExe</OutputType>\n\t\t<TargetFramework>net8.0</TargetFramework>\n\t\t<Nullable>enable</Nullable>\n\t\t<Version>1.8.4-beta.2</Version>\n\t\t<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>\n\t\t<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>\n\t\t<ApplicationIcon>Assets\\icon.ico</ApplicationIcon>\n\t</PropertyGroup>\n\t<PropertyGroup>\n\t\t<PublishSingleFile>true</PublishSingleFile>\n\t\t<IncludeNativeLibrariesForSelfExtract>true</IncludeNativeLibrariesForSelfExtract>\n\t</PropertyGroup>\n\t<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Debug|AnyCPU'\">\n\t\t<OutputPath></OutputPath>\n\t\t<PlatformTarget>x64</PlatformTarget>\n\t</PropertyGroup>\n\t<PropertyGroup Condition=\"'$(Configuration)|$(Platform)'=='Release|AnyCPU'\">\n\t\t<PlatformTarget>x64</PlatformTarget>\n\t</PropertyGroup>\n\t<ItemGroup>\n\t\t<AvaloniaResource Include=\"Assets\\**\" />\n\t\t<Content Include=\"Themes\\**\" CopyToOutputDirectory=\"PreserveNewest\" />\n\t\t<Content Include=\"i18n\\*\" CopyToOutputDirectory=\"PreserveNewest\" />\n\t\t<None Remove=\".gitignore\" />\n\t\t<None Remove=\"Assets\\icon.ico\" />\n\t\t<None Remove=\"Assets\\Info.plist\" />\n\t\t<None Remove=\"Assets\\smapi.png\" />\n\t\t<None Remove=\"Assets\\Stardrop.icns\" />\n\t\t<None Remove=\"Assets\\Stardrop.sh\" />\n\t\t<None Remove=\"Themes\\Dark.xaml\" />\n\t\t<None Remove=\"Themes\\Light.xaml\" />\n\t\t<None Remove=\"Themes\\Solarized-Lite.xaml\" />\n\t</ItemGroup>\n\t<ItemGroup>\n\t  <Content Include=\"Themes\\Contributors\\hotcereal\\CASH MONEY.xaml\" />\n\t  <Content Include=\"Themes\\Contributors\\hotcereal\\Cerene Dark.xaml\" />\n\t  <Content Include=\"Themes\\Contributors\\hotcereal\\Cerene Light.xaml\" />\n\t  <Content Include=\"Themes\\Contributors\\hotcereal\\Dark Cherry Chocolate.xaml\" />\n\t  <Content Include=\"Themes\\Contributors\\hotcereal\\Fairy.xaml\" />\n\t  <Content Include=\"Themes\\Contributors\\hotcereal\\Forest.xaml\" />\n\t  <Content Include=\"Themes\\Contributors\\hotcereal\\Light %28Pink%29.xaml\" />\n\t  <Content Include=\"Themes\\Contributors\\hotcereal\\Light Cherry Chocolate.xaml\" />\n\t</ItemGroup>\n\t<ItemGroup>\n\t\t<PackageReference Include=\"Avalonia\" Version=\"0.10.22\" />\n\t\t<PackageReference Include=\"Avalonia.Controls.DataGrid\" Version=\"0.10.22\" />\n\t\t<PackageReference Include=\"Avalonia.Desktop\" Version=\"0.10.22\" />\n\t\t<!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->\n\t\t<PackageReference Condition=\"'$(Configuration)' == 'Debug'\" Include=\"Avalonia.Diagnostics\" Version=\"0.10.22\" />\n\t\t<PackageReference Include=\"Avalonia.Markup.Xaml.Loader\" Version=\"0.10.22\" />\n\t\t<PackageReference Include=\"Avalonia.ReactiveUI\" Version=\"0.10.22\" />\n\t\t<PackageReference Include=\"CommandLineParser\" Version=\"2.9.1\" />\n\t\t<PackageReference Include=\"Json.More.Net\" Version=\"2.0.1\" />\n\t\t<PackageReference Include=\"Microsoft.Win32.Registry\" Version=\"5.0.0\" />\n\t\t<PackageReference Include=\"Projektanker.Icons.Avalonia\" Version=\"5.13.0\" />\n\t\t<PackageReference Include=\"Projektanker.Icons.Avalonia.MaterialDesign\" Version=\"5.13.0\" />\n\t\t<PackageReference Include=\"semver\" Version=\"2.3.0\" />\n\t\t<PackageReference Include=\"SharpCompress\" Version=\"0.32.1\" />\n\t</ItemGroup>\n\t<ItemGroup>\n\t\t<Compile Update=\"Views\\FlexibleOptionWindow.axaml.cs\">\n\t\t\t<DependentUpon>FlexibleOptionWindow.axaml</DependentUpon>\n\t\t</Compile>\n\t\t<Compile Update=\"Views\\NexusInfo.axaml.cs\">\n\t\t\t<DependentUpon>NexusInfo.axaml</DependentUpon>\n\t\t</Compile>\n\t\t<Compile Update=\"Views\\ProfileNaming.axaml.cs\">\n\t\t\t<DependentUpon>ProfileNaming.axaml</DependentUpon>\n\t\t</Compile>\n\t\t<Compile Update=\"Views\\ProfileEditor.axaml.cs\">\n\t\t\t<DependentUpon>ProfileEditor.axaml</DependentUpon>\n\t\t</Compile>\n\t\t<Compile Update=\"Views\\WarningWindow.axaml.cs\">\n\t\t\t<DependentUpon>WarningWindow.axaml</DependentUpon>\n\t\t</Compile>\n        <Compile Update=\"Views\\DownloadPanel.axaml.cs\">\n            <DependentUpon>DownloadPanel.axaml</DependentUpon>\n        </Compile>\n\t</ItemGroup>\n\t<ItemGroup>\n\t\t<Content Update=\"Themes\\Light.xaml\">\n\t\t\t<SubType>Designer</SubType>\n\t\t\t<Generator>MSBuild:Compile</Generator>\n\t\t</Content>\n\t\t<Content Update=\"Themes\\Solarized-Lite.xaml\">\n\t\t\t<SubType>Designer</SubType>\n\t\t\t<Generator>MSBuild:Compile</Generator>\n\t\t</Content>\n\t\t<Content Update=\"Themes\\Dark.xaml\">\n\t\t\t<SubType>Designer</SubType>\n\t\t\t<Generator>MSBuild:Compile</Generator>\n\t\t</Content>\n\t\t<Content Update=\"Themes\\Stardrop.xaml\">\n\t\t\t<Generator>MSBuild:Compile</Generator>\n\t\t</Content>\n\t</ItemGroup>\n</Project>\n"
  },
  {
    "path": "Stardrop/Stardrop.sln",
    "content": "﻿\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 16\nVisualStudioVersion = 16.0.31729.503\nMinimumVisualStudioVersion = 10.0.40219.1\nProject(\"{9A19103F-16F7-4668-BE54-9A1E7A4F7556}\") = \"Stardrop\", \"Stardrop.csproj\", \"{68543B63-0EB4-43E8-9B7B-7AFA64097CAF}\"\nEndProject\nGlobal\n\tGlobalSection(SolutionConfigurationPlatforms) = preSolution\n\t\tDebug|Any CPU = Debug|Any CPU\n\t\tRelease|Any CPU = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(ProjectConfigurationPlatforms) = postSolution\n\t\t{68543B63-0EB4-43E8-9B7B-7AFA64097CAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU\n\t\t{68543B63-0EB4-43E8-9B7B-7AFA64097CAF}.Debug|Any CPU.Build.0 = Debug|Any CPU\n\t\t{68543B63-0EB4-43E8-9B7B-7AFA64097CAF}.Release|Any CPU.ActiveCfg = Release|Any CPU\n\t\t{68543B63-0EB4-43E8-9B7B-7AFA64097CAF}.Release|Any CPU.Build.0 = Release|Any CPU\n\tEndGlobalSection\n\tGlobalSection(SolutionProperties) = preSolution\n\t\tHideSolutionNode = FALSE\n\tEndGlobalSection\n\tGlobalSection(ExtensibilityGlobals) = postSolution\n\t\tSolutionGuid = {86B7436F-BA81-4F86-B816-DB4AD0E2C918}\n\tEndGlobalSection\nEndGlobal\n"
  },
  {
    "path": "Stardrop/Themes/Contributors/hotcereal/CASH MONEY.xaml",
    "content": "﻿<Styles\n    xmlns=\"https://github.com/avaloniaui\"\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n\n    <Styles.Resources>\n\n        <!-- Base Colors -->\n        <Color x:Key=\"GreenBg\">#4FA57E</Color>\n        <Color x:Key=\"GreenRow\">#6AB892</Color>\n        <Color x:Key=\"GreenRowAlt\">#74C29B</Color>\n        <Color x:Key=\"GreenText\">#0F241C</Color>\n\n        <!-- Accent / UI Colors -->\n        <Color x:Key=\"SelectedGreen\">#C6E0D3</Color>\n        <Color x:Key=\"MintHover\">#8AD3AF</Color>\n        <Color x:Key=\"StemBorder\">#3E7F62</Color>\n        <Color x:Key=\"DeepGreen\">#2E5F4B</Color>\n\n        <Color x:Key=\"SystemAccentColor\">#6AB892</Color>\n\n        <!-- Avalonia Brushes -->\n        <SolidColorBrush x:Key=\"ThemeBackgroundBrush\"\n                         Color=\"{DynamicResource GreenBg}\" />\n        <SolidColorBrush x:Key=\"ThemeForegroundBrush\"\n                         Color=\"{DynamicResource GreenText}\" />\n        <SolidColorBrush x:Key=\"ThemeControlMidBrush\"\n                         Color=\"{DynamicResource GreenRowAlt}\" />\n        <SolidColorBrush x:Key=\"ThemeControlHighBrush\"\n                         Color=\"{DynamicResource GreenText}\"\n                         Opacity=\"0.65\" />\n        <SolidColorBrush x:Key=\"ThemeForegroundLowBrush\"\n                         Color=\"{DynamicResource GreenText}\"\n                         Opacity=\"0.6\" />\n        <SolidColorBrush x:Key=\"HighlightBrush\"\n                         Color=\"{DynamicResource StemBorder}\" />\n        <SolidColorBrush x:Key=\"HighlightForegroundBrush\"\n                         Color=\"{DynamicResource GreenText}\" />\n\n    </Styles.Resources>\n\n    <!-- DataGrid -->\n    <Style Selector=\"DataGrid\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource GreenBg}\" />\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource StemBorder}\" />\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource GreenText}\" />\n    </Style>\n\n    <Style Selector=\"DataGridRow\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource GreenRow}\" />\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource GreenText}\" />\n    </Style>\n\n    <Style Selector=\"DataGridRow:nth-child(even)\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource GreenRowAlt}\" />\n    </Style>\n\n    <Style Selector=\"DataGridRow:selected\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource SelectedGreen}\" />\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource GreenText}\" />\n    </Style>\n\n    <Style Selector=\"DataGridRow:pointerover\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource MintHover}\" />\n    </Style>\n\n</Styles>"
  },
  {
    "path": "Stardrop/Themes/Contributors/hotcereal/Cerene Dark.xaml",
    "content": "﻿<Styles\n    xmlns=\"https://github.com/avaloniaui\"\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n    xmlns:system=\"clr-namespace:System;assembly=System.Runtime\">\n\n    <Style>\n        <Style.Resources>\n\n            <!-- Base Colors -->\n            <Color x:Key=\"NightBlack\">#2B313C</Color>        <!-- Main background -->\n            <Color x:Key=\"OffBlack\">#343B48</Color>          <!-- Panels / rows -->\n            <Color x:Key=\"DarkestGray\">#252A34</Color>       <!-- Deep background -->\n            <Color x:Key=\"DarkGray\">#3A4252</Color>          <!-- Headers -->\n            <Color x:Key=\"OffGray\">#40485A</Color>           <!-- Alt rows -->\n            <Color x:Key=\"PerfectGray\">#5E677A</Color>       <!-- Subtle borders -->\n            <Color x:Key=\"White\">#E6E9EF</Color>             <!-- Primary text -->\n\n            <!-- Accent / UI Colors -->\n            <Color x:Key=\"NeonMint\">#8FD4A1</Color>          <!-- Enabled checkmarks -->\n            <Color x:Key=\"CalmBlue\">#6FA8DC</Color>          <!-- Highlights -->\n            <Color x:Key=\"NeonRed\">#D96C6C</Color>           <!-- Errors -->\n            <Color x:Key=\"NeonYellow\">#D7D18B</Color>        <!-- Warnings -->\n            <Color x:Key=\"GrayBlue\">#6FA3B8</Color>          <!-- Links / focus -->\n            <Color x:Key=\"DarkPurple\">#3B3F5C</Color>        <!-- Rare accents -->\n\n            <!-- Avalonia Brushes -->\n            <SolidColorBrush x:Key=\"ThemeBackgroundBrush\" Color=\"{DynamicResource NightBlack}\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundBrush\" Color=\"{DynamicResource White}\" />\n            <SolidColorBrush x:Key=\"HighlightBrush\" Color=\"{DynamicResource OffBlack}\" />\n            <SolidColorBrush x:Key=\"HighlightForegroundBrush\" Color=\"{DynamicResource CalmBlue}\" />\n\n            <SolidColorBrush x:Key=\"ThemeControlMidBrush\" Color=\"{DynamicResource PerfectGray}\" />\n            <SolidColorBrush x:Key=\"ThemeControlMidHighBrush\" Color=\"{DynamicResource White}\" Opacity=\"0.25\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundHighBrush\" Color=\"{DynamicResource White}\" />\n            <SolidColorBrush x:Key=\"ThemeControlHighBrush\" Color=\"{DynamicResource White}\" Opacity=\"0.65\" />\n            <SolidColorBrush x:Key=\"ThemeControlVeryHighBrush\" Color=\"{DynamicResource White}\" Opacity=\"0.75\" />\n            <SolidColorBrush x:Key=\"ThemeAccentBrush\" Color=\"{DynamicResource White}\" Opacity=\"0.2\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundLowBrush\" Color=\"{DynamicResource White}\" Opacity=\"0.35\" />\n\n            <!-- DataGrid -->\n            <SolidColorBrush x:Key=\"DataGridHeaderBrush\" Color=\"{DynamicResource DarkGray}\" />\n            <SolidColorBrush x:Key=\"DataGridRowBackground\" Color=\"{DynamicResource OffBlack}\" />\n            <SolidColorBrush x:Key=\"AlternativeDataGridRowBackground\" Color=\"{DynamicResource OffGray}\" />\n            <SolidColorBrush x:Key=\"DataGridRowForeground\" Color=\"{DynamicResource White}\" />\n\n            <!-- ComboBox -->\n            <SolidColorBrush x:Key=\"ComboBoxItemBackgroundBrush\" Color=\"{DynamicResource DarkGray}\" />\n            <SolidColorBrush x:Key=\"ComboBoxItemSelectedBackgroundBrush\" Color=\"{DynamicResource GrayBlue}\" />\n\n            <!-- Status -->\n            <SolidColorBrush x:Key=\"UpdateAvailableBrush\" Color=\"{DynamicResource NeonMint}\" />\n            <SolidColorBrush x:Key=\"UpdateBrokenBrush\" Color=\"{DynamicResource NeonRed}\" />\n            <SolidColorBrush x:Key=\"UpdateUnofficialBrush\" Color=\"{DynamicResource NeonYellow}\" />\n\n        </Style.Resources>\n    </Style>\n\n</Styles>"
  },
  {
    "path": "Stardrop/Themes/Contributors/hotcereal/Cerene Light.xaml",
    "content": "﻿<Styles\n    xmlns=\"https://github.com/avaloniaui\"\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\">\n\n    <Styles.Resources>\n\n        <!-- Base Colors -->\n        <Color x:Key=\"IceBg\">#F6FAFF</Color>\n        <Color x:Key=\"IceRow\">#EEF3FF</Color>\n        <Color x:Key=\"IceRowAlt\">#E6EDFF</Color>\n        <Color x:Key=\"IceText\">#1E2433</Color>\n\n        <!-- Accent / UI Colors -->\n        <Color x:Key=\"RRed\">#FF5F5F</Color>\n        <Color x:Key=\"ROrange\">#FF9F43</Color>\n        <Color x:Key=\"RYellow\">#FFD93D</Color>\n        <Color x:Key=\"RGreen\">#4EDEA3</Color>\n        <Color x:Key=\"RCyan\">#3FD9FF</Color>\n        <Color x:Key=\"RBlue\">#5C7CFA</Color>\n        <Color x:Key=\"RPurple\">#B197FC</Color>\n\n        <!-- Avalonia Brushes -->\n        <SolidColorBrush x:Key=\"ThemeBackgroundBrush\" Color=\"{DynamicResource IceBg}\" />\n        <SolidColorBrush x:Key=\"ThemeForegroundBrush\" Color=\"{DynamicResource IceText}\" />\n        <SolidColorBrush x:Key=\"ThemeControlMidBrush\" Color=\"{DynamicResource IceRowAlt}\" />\n        <SolidColorBrush x:Key=\"ThemeControlHighBrush\" Color=\"{DynamicResource IceText}\" Opacity=\"0.65\" />\n        <SolidColorBrush x:Key=\"ThemeForegroundLowBrush\" Color=\"{DynamicResource IceText}\" Opacity=\"0.6\" />\n        \n        <!-- Need to look into why missing a SolidColorBrush causes Stardrop to not display them in styles -->\n        <SolidColorBrush x:Key=\"HighlightBrush\" Color=\"{DynamicResource LightGray}\" />\n    </Styles.Resources>\n\n    <!-- DataGrid -->\n    <Style Selector=\"DataGrid\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource IceBg}\" />\n        <Setter Property=\"BorderBrush\" Value=\"{DynamicResource RCyan}\" />\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource IceText}\" />\n    </Style>\n\n    <Style Selector=\"DataGridRow\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource IceRow}\" />\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource IceText}\" />\n    </Style>\n\n    <Style Selector=\"DataGridRow:nth-child(even)\">\n        <Setter Property=\"Background\" Value=\"{DynamicResource IceRowAlt}\" />\n    </Style>\n\n    <Style Selector=\"DataGridRow:selected\">\n        <Setter Property=\"Background\">\n            <LinearGradientBrush StartPoint=\"0,0\" EndPoint=\"1,0\">\n                <GradientStop Color=\"{DynamicResource RRed}\" Offset=\"0.0\" />\n                <GradientStop Color=\"{DynamicResource ROrange}\" Offset=\"0.16\" />\n                <GradientStop Color=\"{DynamicResource RYellow}\" Offset=\"0.33\" />\n                <GradientStop Color=\"{DynamicResource RGreen}\" Offset=\"0.5\" />\n                <GradientStop Color=\"{DynamicResource RCyan}\" Offset=\"0.66\" />\n                <GradientStop Color=\"{DynamicResource RBlue}\" Offset=\"0.83\" />\n                <GradientStop Color=\"{DynamicResource RPurple}\" Offset=\"1.0\" />\n            </LinearGradientBrush>\n        </Setter>\n        <Setter Property=\"Foreground\" Value=\"{DynamicResource IceText}\" />\n    </Style>\n\n    <Style Selector=\"DataGridRow:pointerover\">\n        <Setter Property=\"Background\">\n            <LinearGradientBrush StartPoint=\"0,0\" EndPoint=\"1,0\" Opacity=\"0.4\">\n                <GradientStop Color=\"{DynamicResource RCyan}\" Offset=\"0.0\" />\n                <GradientStop Color=\"{DynamicResource RBlue}\" Offset=\"1.0\" />\n            </LinearGradientBrush>\n        </Setter>\n    </Style>\n\n</Styles>"
  },
  {
    "path": "Stardrop/Themes/Contributors/hotcereal/Dark Cherry Chocolate.xaml",
    "content": "﻿<Styles\n    xmlns=\"https://github.com/avaloniaui\"\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n    xmlns:system=\"clr-namespace:System;assembly=System.Runtime\">\n\n    <Style>\n        <Style.Resources>\n\n            <!-- Base Colors -->\n            <Color x:Key=\"NightBlack\">#2E2326</Color>\n            <Color x:Key=\"OffBlack\">#3A2B30</Color>\n            <Color x:Key=\"DarkestGray\">#24191D</Color>\n            <Color x:Key=\"DarkGray\">#46323A</Color>\n            <Color x:Key=\"OffGray\">#4A353D</Color>\n            <Color x:Key=\"PerfectGray\">#6E5860</Color>\n            <Color x:Key=\"White\">#EFE6EA</Color>\n\n            <!-- Accent / UI Colors -->\n            <Color x:Key=\"NeonMint\">#CFA3B4</Color>\n            <Color x:Key=\"CalmBlue\">#B57A8C</Color>\n            <Color x:Key=\"NeonRed\">#C96C7D</Color>\n            <Color x:Key=\"NeonYellow\">#D6B3A2</Color>\n            <Color x:Key=\"GrayBlue\">#C08FA0</Color>\n            <Color x:Key=\"DarkPurple\">#3B2A30</Color>\n\n            <!-- Avalonia Brushes -->\n            <SolidColorBrush x:Key=\"ThemeBackgroundBrush\"\n                             Color=\"{DynamicResource NightBlack}\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundBrush\"\n                             Color=\"{DynamicResource White}\" />\n\n            <SolidColorBrush x:Key=\"HighlightBrush\"\n                             Color=\"{DynamicResource OffBlack}\" />\n            <SolidColorBrush x:Key=\"HighlightForegroundBrush\"\n                             Color=\"{DynamicResource CalmBlue}\" />\n\n            <SolidColorBrush x:Key=\"ThemeControlMidBrush\"\n                             Color=\"{DynamicResource PerfectGray}\" />\n            <SolidColorBrush x:Key=\"ThemeControlMidHighBrush\"\n                             Color=\"{DynamicResource White}\"\n                             Opacity=\"0.25\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundHighBrush\"\n                             Color=\"{DynamicResource White}\" />\n            <SolidColorBrush x:Key=\"ThemeControlHighBrush\"\n                             Color=\"{DynamicResource White}\"\n                             Opacity=\"0.65\" />\n            <SolidColorBrush x:Key=\"ThemeControlVeryHighBrush\"\n                             Color=\"{DynamicResource White}\"\n                             Opacity=\"0.75\" />\n            <SolidColorBrush x:Key=\"ThemeAccentBrush\"\n                             Color=\"{DynamicResource White}\"\n                             Opacity=\"0.2\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundLowBrush\"\n                             Color=\"{DynamicResource White}\"\n                             Opacity=\"0.35\" />\n\n            <!-- DataGrid -->\n            <SolidColorBrush x:Key=\"DataGridHeaderBrush\"\n                             Color=\"{DynamicResource DarkGray}\" />\n            <SolidColorBrush x:Key=\"DataGridRowBackground\"\n                             Color=\"{DynamicResource OffBlack}\" />\n            <SolidColorBrush x:Key=\"AlternativeDataGridRowBackground\"\n                             Color=\"{DynamicResource OffGray}\" />\n            <SolidColorBrush x:Key=\"DataGridRowForeground\"\n                             Color=\"{DynamicResource White}\" />\n\n            <!-- ComboBox -->\n            <SolidColorBrush x:Key=\"ComboBoxItemBackgroundBrush\"\n                             Color=\"{DynamicResource DarkGray}\" />\n            <SolidColorBrush x:Key=\"ComboBoxItemSelectedBackgroundBrush\"\n                             Color=\"{DynamicResource GrayBlue}\" />\n\n            <!-- Status -->\n            <SolidColorBrush x:Key=\"UpdateAvailableBrush\"\n                             Color=\"{DynamicResource NeonMint}\" />\n            <SolidColorBrush x:Key=\"UpdateBrokenBrush\"\n                             Color=\"{DynamicResource NeonRed}\" />\n            <SolidColorBrush x:Key=\"UpdateUnofficialBrush\"\n                             Color=\"{DynamicResource NeonYellow}\" />\n\n        </Style.Resources>\n    </Style>\n\n</Styles>"
  },
  {
    "path": "Stardrop/Themes/Contributors/hotcereal/Fairy.xaml",
    "content": "﻿<Styles\n    xmlns=\"https://github.com/avaloniaui\"\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n    xmlns:system=\"clr-namespace:System;assembly=System.Runtime\">\n\n    <Style>\n        <Style.Resources>\n\n            <!-- Base Colors -->\n            <Color x:Key=\"NightBlack\">#F6ECEF</Color>\n            <Color x:Key=\"OffBlack\">#EFE3E8</Color>\n            <Color x:Key=\"DarkestGray\">#E5D7DD</Color>\n            <Color x:Key=\"DarkGray\">#DDD0D6</Color>\n            <Color x:Key=\"OffGray\">#F8EEF2</Color>\n            <Color x:Key=\"PerfectGray\">#C8B8BF</Color>\n            <Color x:Key=\"White\">#3A2A31</Color>\n\n            <!-- Accent / UI Colors -->\n            <Color x:Key=\"NeonMint\">#E8B8C9</Color>\n            <Color x:Key=\"CalmBlue\">#F0B4CB</Color>\n            <Color x:Key=\"NeonRed\">#D98AA4</Color>\n            <Color x:Key=\"NeonYellow\">#EAD3C6</Color>\n            <Color x:Key=\"GrayBlue\">#F3C9D9</Color>\n            <Color x:Key=\"DarkPurple\">#8A6675</Color>\n\n            <!-- Avalonia Brushes -->\n            <SolidColorBrush x:Key=\"ThemeBackgroundBrush\"\n                             Color=\"{DynamicResource NightBlack}\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundBrush\"\n                             Color=\"{DynamicResource White}\" />\n\n            <SolidColorBrush x:Key=\"HighlightBrush\"\n                             Color=\"{DynamicResource OffBlack}\" />\n            <SolidColorBrush x:Key=\"HighlightForegroundBrush\"\n                             Color=\"{DynamicResource CalmBlue}\" />\n\n            <SolidColorBrush x:Key=\"ThemeControlMidBrush\"\n                             Color=\"{DynamicResource PerfectGray}\" />\n            <SolidColorBrush x:Key=\"ThemeControlMidHighBrush\"\n                             Color=\"{DynamicResource White}\"\n                             Opacity=\"0.18\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundHighBrush\"\n                             Color=\"{DynamicResource White}\" />\n            <SolidColorBrush x:Key=\"ThemeControlHighBrush\"\n                             Color=\"{DynamicResource White}\"\n                             Opacity=\"0.4\" />\n            <SolidColorBrush x:Key=\"ThemeControlVeryHighBrush\"\n                             Color=\"{DynamicResource White}\"\n                             Opacity=\"0.6\" />\n            <SolidColorBrush x:Key=\"ThemeAccentBrush\"\n                             Color=\"{DynamicResource White}\"\n                             Opacity=\"0.12\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundLowBrush\"\n                             Color=\"{DynamicResource White}\"\n                             Opacity=\"0.65\" />\n\n            <!-- DataGrid -->\n            <SolidColorBrush x:Key=\"DataGridHeaderBrush\"\n                             Color=\"{DynamicResource DarkGray}\" />\n            <SolidColorBrush x:Key=\"DataGridRowBackground\"\n                             Color=\"{DynamicResource OffBlack}\" />\n            <SolidColorBrush x:Key=\"AlternativeDataGridRowBackground\"\n                             Color=\"{DynamicResource OffGray}\" />\n            <SolidColorBrush x:Key=\"DataGridRowForeground\"\n                             Color=\"{DynamicResource White}\" />\n\n            <!-- ComboBox -->\n            <SolidColorBrush x:Key=\"ComboBoxItemBackgroundBrush\"\n                             Color=\"{DynamicResource DarkGray}\" />\n            <SolidColorBrush x:Key=\"ComboBoxItemSelectedBackgroundBrush\"\n                             Color=\"{DynamicResource GrayBlue}\" />\n\n            <!-- Status -->\n            <SolidColorBrush x:Key=\"UpdateAvailableBrush\"\n                             Color=\"{DynamicResource NeonMint}\" />\n            <SolidColorBrush x:Key=\"UpdateBrokenBrush\"\n                             Color=\"{DynamicResource NeonRed}\" />\n            <SolidColorBrush x:Key=\"UpdateUnofficialBrush\"\n                             Color=\"{DynamicResource NeonYellow}\" />\n\n        </Style.Resources>\n    </Style>\n\n</Styles>"
  },
  {
    "path": "Stardrop/Themes/Contributors/hotcereal/Forest.xaml",
    "content": "﻿<Styles\n    xmlns=\"https://github.com/avaloniaui\"\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n    xmlns:system=\"clr-namespace:System;assembly=System.Runtime\">\n\n    <Style>\n        <Style.Resources>\n\n            <!-- Base Colors -->\n            <Color x:Key=\"NightBlack\">#2B2118</Color>\n            <Color x:Key=\"OffBlack\">#33271D</Color>\n            <Color x:Key=\"DarkestGray\">#241B14</Color>\n            <Color x:Key=\"DarkGray\">#3B2E23</Color>\n            <Color x:Key=\"OffGray\">#413327</Color>\n            <Color x:Key=\"PerfectGray\">#6A5B4C</Color>\n            <Color x:Key=\"White\">#E6E2DD</Color>\n\n            <!-- Accent / UI Colors -->\n            <Color x:Key=\"NeonMint\">#8FBFA3</Color>\n            <Color x:Key=\"CalmBlue\">#6F9E86</Color>\n            <Color x:Key=\"NeonRed\">#C97A6A</Color>\n            <Color x:Key=\"NeonYellow\">#CBBF87</Color>\n            <Color x:Key=\"GrayBlue\">#7FAF95</Color>\n            <Color x:Key=\"DarkPurple\">#3E352C</Color>\n\n            <!-- Avalonia Brushes -->\n            <SolidColorBrush x:Key=\"ThemeBackgroundBrush\"\n                             Color=\"{DynamicResource NightBlack}\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundBrush\"\n                             Color=\"{DynamicResource White}\" />\n\n            <SolidColorBrush x:Key=\"HighlightBrush\"\n                             Color=\"{DynamicResource OffBlack}\" />\n            <SolidColorBrush x:Key=\"HighlightForegroundBrush\"\n                             Color=\"{DynamicResource CalmBlue}\" />\n\n            <SolidColorBrush x:Key=\"ThemeControlMidBrush\"\n                             Color=\"{DynamicResource PerfectGray}\" />\n            <SolidColorBrush x:Key=\"ThemeControlMidHighBrush\"\n                             Color=\"{DynamicResource White}\" Opacity=\"0.25\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundHighBrush\"\n                             Color=\"{DynamicResource White}\" />\n            <SolidColorBrush x:Key=\"ThemeControlHighBrush\"\n                             Color=\"{DynamicResource White}\" Opacity=\"0.65\" />\n            <SolidColorBrush x:Key=\"ThemeControlVeryHighBrush\"\n                             Color=\"{DynamicResource White}\" Opacity=\"0.75\" />\n            <SolidColorBrush x:Key=\"ThemeAccentBrush\"\n                             Color=\"{DynamicResource White}\" Opacity=\"0.2\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundLowBrush\"\n                             Color=\"{DynamicResource White}\" Opacity=\"0.35\" />\n\n            <!-- DataGrid -->\n            <SolidColorBrush x:Key=\"DataGridHeaderBrush\"\n                             Color=\"{DynamicResource DarkGray}\" />\n            <SolidColorBrush x:Key=\"DataGridRowBackground\"\n                             Color=\"{DynamicResource OffBlack}\" />\n            <SolidColorBrush x:Key=\"AlternativeDataGridRowBackground\"\n                             Color=\"{DynamicResource OffGray}\" />\n            <SolidColorBrush x:Key=\"DataGridRowForeground\"\n                             Color=\"{DynamicResource White}\" />\n\n            <!-- ComboBox -->\n            <SolidColorBrush x:Key=\"ComboBoxItemBackgroundBrush\"\n                             Color=\"{DynamicResource DarkGray}\" />\n            <SolidColorBrush x:Key=\"ComboBoxItemSelectedBackgroundBrush\"\n                             Color=\"{DynamicResource GrayBlue}\" />\n\n            <!-- Status -->\n            <SolidColorBrush x:Key=\"UpdateAvailableBrush\"\n                             Color=\"{DynamicResource NeonMint}\" />\n            <SolidColorBrush x:Key=\"UpdateBrokenBrush\"\n                             Color=\"{DynamicResource NeonRed}\" />\n            <SolidColorBrush x:Key=\"UpdateUnofficialBrush\"\n                             Color=\"{DynamicResource NeonYellow}\" />\n\n        </Style.Resources>\n    </Style>\n\n</Styles>"
  },
  {
    "path": "Stardrop/Themes/Contributors/hotcereal/Light (Pink).xaml",
    "content": "﻿<Styles\n    xmlns=\"https://github.com/avaloniaui\"\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n    xmlns:system=\"clr-namespace:System;assembly=System.Runtime\">\n\n    <Style>\n        <Style.Resources>\n\n            <!-- Base Colors -->\n            <Color x:Key=\"NightBlack\">#FAF3F6</Color>\n            <Color x:Key=\"OffBlack\">#F2E6EB</Color>\n            <Color x:Key=\"DarkestGray\">#E8D8DE</Color>\n            <Color x:Key=\"DarkGray\">#DEC9D2</Color>\n            <Color x:Key=\"OffGray\">#F8EFF3</Color>\n            <Color x:Key=\"PerfectGray\">#C9B5BE</Color>\n            <Color x:Key=\"White\">#3B2A31</Color>\n\n            <!-- Accent / UI Colors -->\n            <Color x:Key=\"NeonMint\">#E9B9C9</Color>\n            <Color x:Key=\"CalmBlue\">#F1B6CD</Color>\n            <Color x:Key=\"NeonRed\">#DB8FA6</Color>\n            <Color x:Key=\"NeonYellow\">#ECD6C9</Color>\n            <Color x:Key=\"GrayBlue\">#F4CEDC</Color>\n            <Color x:Key=\"DarkPurple\">#8E6A78</Color>\n\n            <!-- Avalonia Brushes -->\n            <SolidColorBrush x:Key=\"ThemeBackgroundBrush\"\n                             Color=\"{DynamicResource NightBlack}\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundBrush\"\n                             Color=\"{DynamicResource White}\" />\n\n            <SolidColorBrush x:Key=\"HighlightBrush\"\n                             Color=\"{DynamicResource OffBlack}\" />\n            <SolidColorBrush x:Key=\"HighlightForegroundBrush\"\n                             Color=\"{DynamicResource CalmBlue}\" />\n\n            <SolidColorBrush x:Key=\"ThemeControlMidBrush\"\n                             Color=\"{DynamicResource PerfectGray}\" />\n            <SolidColorBrush x:Key=\"ThemeControlMidHighBrush\"\n                             Color=\"{DynamicResource White}\"\n                             Opacity=\"0.18\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundHighBrush\"\n                             Color=\"{DynamicResource White}\" />\n            <SolidColorBrush x:Key=\"ThemeControlHighBrush\"\n                             Color=\"{DynamicResource White}\"\n                             Opacity=\"0.4\" />\n            <SolidColorBrush x:Key=\"ThemeControlVeryHighBrush\"\n                             Color=\"{DynamicResource White}\"\n                             Opacity=\"0.6\" />\n            <SolidColorBrush x:Key=\"ThemeAccentBrush\"\n                             Color=\"{DynamicResource White}\"\n                             Opacity=\"0.12\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundLowBrush\"\n                             Color=\"{DynamicResource White}\"\n                             Opacity=\"0.65\" />\n\n            <!-- DataGrid -->\n            <SolidColorBrush x:Key=\"DataGridHeaderBrush\"\n                             Color=\"{DynamicResource DarkGray}\" />\n            <SolidColorBrush x:Key=\"DataGridRowBackground\"\n                             Color=\"{DynamicResource OffBlack}\" />\n            <SolidColorBrush x:Key=\"AlternativeDataGridRowBackground\"\n                             Color=\"{DynamicResource OffGray}\" />\n            <SolidColorBrush x:Key=\"DataGridRowForeground\"\n                             Color=\"{DynamicResource White}\" />\n\n            <!-- ComboBox -->\n            <SolidColorBrush x:Key=\"ComboBoxItemBackgroundBrush\"\n                             Color=\"{DynamicResource DarkGray}\" />\n            <SolidColorBrush x:Key=\"ComboBoxItemSelectedBackgroundBrush\"\n                             Color=\"{DynamicResource GrayBlue}\" />\n\n            <!-- Status -->\n            <SolidColorBrush x:Key=\"UpdateAvailableBrush\"\n                             Color=\"{DynamicResource NeonMint}\" />\n            <SolidColorBrush x:Key=\"UpdateBrokenBrush\"\n                             Color=\"{DynamicResource NeonRed}\" />\n            <SolidColorBrush x:Key=\"UpdateUnofficialBrush\"\n                             Color=\"{DynamicResource NeonYellow}\" />\n\n        </Style.Resources>\n    </Style>\n\n</Styles>"
  },
  {
    "path": "Stardrop/Themes/Contributors/hotcereal/Light Cherry Chocolate.xaml",
    "content": "﻿<Styles\n    xmlns=\"https://github.com/avaloniaui\"\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n    xmlns:system=\"clr-namespace:System;assembly=System.Runtime\">\n\n    <Style>\n        <Style.Resources>\n\n        <!-- Base Colors -->\n            <Color x:Key=\"NightBlack\">#3A2F34</Color>        \n            <Color x:Key=\"OffBlack\">#4A3A41</Color>          \n            <Color x:Key=\"DarkestGray\">#2F2428</Color>       \n            <Color x:Key=\"DarkGray\">#5A454D</Color>          \n            <Color x:Key=\"OffGray\">#5F4850</Color>           \n            <Color x:Key=\"PerfectGray\">#8A717B</Color>       \n            <Color x:Key=\"White\">#F6EDF1</Color>             \n\n \n            <!-- Accent / UI Colors -->\n            <Color x:Key=\"NeonMint\">#D8A8BC</Color>\n            <Color x:Key=\"CalmBlue\">#E2A3BE</Color>          \n            <Color x:Key=\"NeonRed\">#E08AA2</Color>\n            <Color x:Key=\"NeonYellow\">#E8C6B8</Color>\n            <Color x:Key=\"GrayBlue\">#E6B4C8</Color>\n            <Color x:Key=\"DarkPurple\">#4A343C</Color>\n\n            <!-- Avalonia brushes -->\n            <SolidColorBrush x:Key=\"ThemeBackgroundBrush\"\n                             Color=\"{DynamicResource NightBlack}\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundBrush\"\n                             Color=\"{DynamicResource White}\" />\n\n            <!-- Selection behavior preserved -->\n            <SolidColorBrush x:Key=\"HighlightBrush\"\n                             Color=\"{DynamicResource OffBlack}\" />\n            <SolidColorBrush x:Key=\"HighlightForegroundBrush\"\n                             Color=\"{DynamicResource CalmBlue}\" />\n\n            <SolidColorBrush x:Key=\"ThemeControlMidBrush\"\n                             Color=\"{DynamicResource PerfectGray}\" />\n            <SolidColorBrush x:Key=\"ThemeControlMidHighBrush\"\n                             Color=\"{DynamicResource White}\"\n                             Opacity=\"0.25\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundHighBrush\"\n                             Color=\"{DynamicResource White}\" />\n            <SolidColorBrush x:Key=\"ThemeControlHighBrush\"\n                             Color=\"{DynamicResource White}\"\n                             Opacity=\"0.65\" />\n            <SolidColorBrush x:Key=\"ThemeControlVeryHighBrush\"\n                             Color=\"{DynamicResource White}\"\n                             Opacity=\"0.75\" />\n            <SolidColorBrush x:Key=\"ThemeAccentBrush\"\n                             Color=\"{DynamicResource White}\"\n                             Opacity=\"0.2\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundLowBrush\"\n                             Color=\"{DynamicResource White}\"\n                             Opacity=\"0.35\" />\n\n            <!-- DataGrid -->\n            <SolidColorBrush x:Key=\"DataGridHeaderBrush\"\n                             Color=\"{DynamicResource DarkGray}\" />\n            <SolidColorBrush x:Key=\"DataGridRowBackground\"\n                             Color=\"{DynamicResource OffBlack}\" />\n            <SolidColorBrush x:Key=\"AlternativeDataGridRowBackground\"\n                             Color=\"{DynamicResource OffGray}\" />\n            <SolidColorBrush x:Key=\"DataGridRowForeground\"\n                             Color=\"{DynamicResource White}\" />\n\n            <!-- ComboBox -->\n            <SolidColorBrush x:Key=\"ComboBoxItemBackgroundBrush\"\n                             Color=\"{DynamicResource DarkGray}\" />\n            <SolidColorBrush x:Key=\"ComboBoxItemSelectedBackgroundBrush\"\n                             Color=\"{DynamicResource GrayBlue}\" />\n\n            <!-- Status -->\n            <SolidColorBrush x:Key=\"UpdateAvailableBrush\"\n                             Color=\"{DynamicResource NeonMint}\" />\n            <SolidColorBrush x:Key=\"UpdateBrokenBrush\"\n                             Color=\"{DynamicResource NeonRed}\" />\n            <SolidColorBrush x:Key=\"UpdateUnofficialBrush\"\n                             Color=\"{DynamicResource NeonYellow}\" />\n\n        </Style.Resources>\n    </Style>\n\n</Styles>"
  },
  {
    "path": "Stardrop/Themes/Dark.xaml",
    "content": "﻿<Styles\n    xmlns=\"https://github.com/avaloniaui\"\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n    xmlns:system=\"clr-namespace:System;assembly=System.Runtime\">\n    <Style>\n        <Style.Resources>\n\n            <!-- Colors -->\n            <Color x:Key=\"NeonOrange\">#ff9f2a</Color>\n            <Color x:Key=\"OffBlack\">#424242</Color>\n            <Color x:Key=\"NightBlack\">#1b1b1b</Color>\n            <Color x:Key=\"NeonMint\">#1cff96</Color>\n            <Color x:Key=\"NeonRed\">#f74040</Color>\n            <Color x:Key=\"NeonYellow\">#fdfd2e</Color>\n            <Color x:Key=\"DarkPurple\">#2a0a84</Color>\n            <Color x:Key=\"DarkGray\">#2E2E2E</Color>\n            <Color x:Key=\"GrayBlue\">#3abcbc</Color>\n            <Color x:Key=\"PerfectGray\">#6B6B6B</Color>\n            <Color x:Key=\"OffGray\">#525252</Color>\n            <Color x:Key=\"DarkestGray\">#2a2824</Color>\n            <Color x:Key=\"White\">#ffffff</Color>\n\n\n            <!-- Avalonia brushes -->\n            <SolidColorBrush x:Key=\"ThemeBackgroundBrush\" Color=\"{DynamicResource NightBlack}\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundBrush\" Color=\"{DynamicResource NeonMint}\"/>\n            <SolidColorBrush x:Key=\"HighlightBrush\" Color=\"{DynamicResource OffBlack}\" />\n            <SolidColorBrush x:Key=\"HighlightForegroundBrush\" Color=\"{DynamicResource NeonOrange}\"/>\n            <SolidColorBrush x:Key=\"ThemeAccentBrush4\" Color=\"{DynamicResource OrangeColor}\" />\n            <SolidColorBrush x:Key=\"ThemeAccentBrush3\" Color=\"{DynamicResource BrightOrangeColor}\" />\n            <SolidColorBrush x:Key=\"ThemeControlMidBrush\" Color=\"{DynamicResource PerfectGray}\" />\n            <SolidColorBrush x:Key=\"ThemeControlMidHighBrush\" Color=\"{DynamicResource White}\"\n                   Opacity=\"0.3\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundHighBrush\" Color=\"{DynamicResource White}\" />\n            <SolidColorBrush x:Key=\"ThemeControlHighBrush\" Color=\"{DynamicResource White}\"\n                   Opacity=\"0.7\" />\n            <SolidColorBrush x:Key=\"ThemeControlVeryHighBrush\" Color=\"{DynamicResource White}\"\n                   Opacity=\"0.7\" />\n            <SolidColorBrush x:Key=\"ThemeBorderMidBrush\" Color=\"{DynamicResource BlackColor}\"\n                   Opacity=\"0.4\" />\n            <SolidColorBrush x:Key=\"ThemeAccentBrush\" Color=\"{DynamicResource White}\"\n                   Opacity=\"0.3\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundLowBrush\" Color=\"{DynamicResource White}\"\n                   Opacity=\"0.3\" />\n            <SolidColorBrush x:Key=\"ErrorBrush\" Color=\"{DynamicResource ErrorColor}\" />\n\n            <!-- Custom brushes -->\n            <SolidColorBrush x:Key=\"DataGridHeaderBrush\" Color=\"{DynamicResource DarkGray}\" />\n            <SolidColorBrush x:Key=\"DataGridRowBackground\" Color=\"{DynamicResource PerfectGray}\" />\n            <SolidColorBrush x:Key=\"DataGridRowForeground\" Color=\"{DynamicResource White}\" />\n            <SolidColorBrush x:Key=\"AlternativeDataGridRowBackground\" Color=\"{DynamicResource OffGray}\" />\n            <SolidColorBrush x:Key=\"ComboBoxItemBackgroundBrush\" Color=\"{DynamicResource DarkGray}\" />\n            <SolidColorBrush x:Key=\"ComboBoxItemSelectedBackgroundBrush\" Color=\"{DynamicResource NeonMint}\" />\n            <SolidColorBrush x:Key=\"UpdateAvailableBrush\" Color=\"{DynamicResource NeonMint}\" />\n            <SolidColorBrush x:Key=\"UpdateBrokenBrush\" Color=\"{DynamicResource NeonRed}\" />\n            <SolidColorBrush x:Key=\"UpdateUnofficialBrush\" Color=\"{DynamicResource NeonYellow}\" />\n\n        </Style.Resources>\n    </Style>\n\n</Styles>"
  },
  {
    "path": "Stardrop/Themes/Light.xaml",
    "content": "﻿<Styles\n    xmlns=\"https://github.com/avaloniaui\"\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n    xmlns:system=\"clr-namespace:System;assembly=System.Runtime\">\n\n    <Style>\n        <Style.Resources>\n\n            <!-- Colors -->\n            <Color x:Key=\"LightGray\">#a69b97</Color>\n            <Color x:Key=\"NeonOrange\">#ff9f2a</Color>\n            <Color x:Key=\"DarkSeaBlue\">#031332</Color>\n            <Color x:Key=\"NeonMint\">#389538</Color>\n            <Color x:Key=\"NeonRed\">#ff0000</Color>\n            <Color x:Key=\"NeonYellow\">#ffc107</Color>\n            <Color x:Key=\"DarkPurple\">#2a0a84</Color>\n            <Color x:Key=\"DarkGray\">#bdb9b7</Color>\n            <Color x:Key=\"GrayBlue\">#3abcbc</Color>\n            <Color x:Key=\"PerfectGray\">#e0e0e0</Color>\n            <Color x:Key=\"OffGray\">#c2c2c2</Color>\n            <Color x:Key=\"MiddleGray\">#8c8c8c</Color>\n            <Color x:Key=\"DarkestGray\">#2a2824</Color>\n            <Color x:Key=\"White\">#efebe9</Color>\n\n\n            <!-- Avalonia brushes -->\n            <SolidColorBrush x:Key=\"ThemeBackgroundBrush\" Color=\"{DynamicResource PerfectGray}\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundBrush\" Color=\"{DynamicResource DarkestGray}\"/>\n            <SolidColorBrush x:Key=\"HighlightBrush\" Color=\"{DynamicResource LightGray}\" />\n            <SolidColorBrush x:Key=\"HighlightForegroundBrush\" Color=\"{DynamicResource NeonOrange}\"/>\n            <SolidColorBrush x:Key=\"ThemeAccentBrush4\" Color=\"{DynamicResource OrangeColor}\" />\n            <SolidColorBrush x:Key=\"ThemeAccentBrush3\" Color=\"{DynamicResource BrightOrangeColor}\" />\n            <SolidColorBrush x:Key=\"ThemeControlMidBrush\" Color=\"{DynamicResource DarkGray}\" />\n            <SolidColorBrush x:Key=\"ThemeControlMidHighBrush\" Color=\"{DynamicResource White}\"\n                   Opacity=\"0.3\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundHighBrush\" Color=\"{DynamicResource DarkestGray}\" />\n            <SolidColorBrush x:Key=\"ThemeControlHighBrush\" Color=\"{DynamicResource White}\"\n                   Opacity=\"0.7\" />\n            <SolidColorBrush x:Key=\"ThemeControlVeryHighBrush\" Color=\"{DynamicResource White}\"\n                   Opacity=\"0.7\" />\n            <SolidColorBrush x:Key=\"ThemeBorderMidBrush\" Color=\"{DynamicResource BlackColor}\"\n                   Opacity=\"0.4\" />\n            <SolidColorBrush x:Key=\"ThemeAccentBrush\" Color=\"{DynamicResource White}\"\n                   Opacity=\"0.3\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundLowBrush\" Color=\"{DynamicResource White}\"\n                   Opacity=\"0.3\" />\n            <SolidColorBrush x:Key=\"ErrorBrush\" Color=\"{DynamicResource ErrorColor}\" />\n\n            <!-- Custom brushes -->\n            <SolidColorBrush x:Key=\"DataGridHeaderBrush\" Color=\"{DynamicResource DarkGray}\" />\n            <SolidColorBrush x:Key=\"DataGridRowBackground\" Color=\"{DynamicResource PerfectGray}\" />\n            <SolidColorBrush x:Key=\"DataGridRowForeground\" Color=\"{DynamicResource DarkestGray}\" />\n            <SolidColorBrush x:Key=\"AlternativeDataGridRowBackground\" Color=\"{DynamicResource White}\" />\n            <SolidColorBrush x:Key=\"ComboBoxItemBackgroundBrush\" Color=\"{DynamicResource DarkGray}\" />\n            <SolidColorBrush x:Key=\"ComboBoxItemSelectedBackgroundBrush\" Color=\"{DynamicResource White}\" />\n            <SolidColorBrush x:Key=\"UpdateAvailableBrush\" Color=\"{DynamicResource NeonMint}\" />\n            <SolidColorBrush x:Key=\"UpdateBrokenBrush\" Color=\"{DynamicResource NeonRed}\" />\n            <SolidColorBrush x:Key=\"UpdateUnofficialBrush\" Color=\"{DynamicResource NeonYellow}\" />\n\n        </Style.Resources>\n    </Style>\n\n</Styles>"
  },
  {
    "path": "Stardrop/Themes/Solarized-Lite.xaml",
    "content": "﻿<Styles\n    xmlns=\"https://github.com/avaloniaui\"\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n    xmlns:system=\"clr-namespace:System;assembly=System.Runtime\">\n    <Style>\n        <Style.Resources>\n\n            <!-- Colors -->\n            <Color x:Key=\"NeonOrange\">#ff9f2a</Color>\n            <Color x:Key=\"DarkSeaBlue\">#031332</Color>\n            <Color x:Key=\"NeonMint\">#1cff96</Color>\n            <Color x:Key=\"NeonRed\">#f74040</Color>\n            <Color x:Key=\"NeonYellow\">#fdfd2e</Color>\n            <Color x:Key=\"DarkPurple\">#2a0a84</Color>\n            <Color x:Key=\"DarkGray\">#2E2E2E</Color>\n            <Color x:Key=\"GrayBlue\">#3abcbc</Color>\n            <Color x:Key=\"PerfectGray\">#6B6B6B</Color>\n            <Color x:Key=\"OffGray\">#525252</Color>\n            <Color x:Key=\"DarkestGray\">#2a2824</Color>\n            <Color x:Key=\"White\">#ffffff</Color>\n\n\n            <!-- Avalonia brushes -->\n            <SolidColorBrush x:Key=\"ThemeBackgroundBrush\" Color=\"{DynamicResource OffGray}\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundBrush\" Color=\"{DynamicResource NeonMint}\"/>\n            <SolidColorBrush x:Key=\"HighlightBrush\" Color=\"{DynamicResource NeonOrange}\" />\n            <SolidColorBrush x:Key=\"HighlightForegroundBrush\" Color=\"{DynamicResource NeonOrange}\"/>\n            <SolidColorBrush x:Key=\"ThemeAccentBrush4\" Color=\"{DynamicResource OrangeColor}\" />\n            <SolidColorBrush x:Key=\"ThemeAccentBrush3\" Color=\"{DynamicResource BrightOrangeColor}\" />\n            <SolidColorBrush x:Key=\"ThemeControlMidBrush\" Color=\"{DynamicResource PerfectGray}\" />\n            <SolidColorBrush x:Key=\"ThemeControlMidHighBrush\" Color=\"{DynamicResource White}\"\n                   Opacity=\"0.3\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundHighBrush\" Color=\"{DynamicResource White}\" />\n            <SolidColorBrush x:Key=\"ThemeControlHighBrush\" Color=\"{DynamicResource White}\"\n                   Opacity=\"0.7\" />\n            <SolidColorBrush x:Key=\"ThemeControlVeryHighBrush\" Color=\"{DynamicResource White}\"\n                   Opacity=\"0.7\" />\n            <SolidColorBrush x:Key=\"ThemeBorderMidBrush\" Color=\"{DynamicResource BlackColor}\"\n                   Opacity=\"0.4\" />\n            <SolidColorBrush x:Key=\"ThemeAccentBrush\" Color=\"{DynamicResource White}\"\n                   Opacity=\"0.3\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundLowBrush\" Color=\"{DynamicResource White}\"\n                   Opacity=\"0.3\" />\n            <SolidColorBrush x:Key=\"ErrorBrush\" Color=\"{DynamicResource ErrorColor}\" />\n\n            <!-- Custom brushes -->\n            <SolidColorBrush x:Key=\"DataGridHeaderBrush\" Color=\"{DynamicResource DarkGray}\" />\n            <SolidColorBrush x:Key=\"DataGridRowBackground\" Color=\"{DynamicResource PerfectGray}\" />\n            <SolidColorBrush x:Key=\"DataGridRowForeground\" Color=\"{DynamicResource White}\" />\n            <SolidColorBrush x:Key=\"AlternativeDataGridRowBackground\" Color=\"{DynamicResource OffGray}\" />\n            <SolidColorBrush x:Key=\"ComboBoxItemBackgroundBrush\" Color=\"{DynamicResource DarkGray}\" />\n            <SolidColorBrush x:Key=\"ComboBoxItemSelectedBackgroundBrush\" Color=\"{DynamicResource NeonMint}\" />\n            <SolidColorBrush x:Key=\"UpdateAvailableBrush\" Color=\"{DynamicResource NeonMint}\" />\n            <SolidColorBrush x:Key=\"UpdateBrokenBrush\" Color=\"{DynamicResource NeonRed}\" />\n            <SolidColorBrush x:Key=\"UpdateUnofficialBrush\" Color=\"{DynamicResource NeonYellow}\" />\n\n        </Style.Resources>\n    </Style>\n\n</Styles>"
  },
  {
    "path": "Stardrop/Themes/Stardrop.xaml",
    "content": "﻿<Styles\n    xmlns=\"https://github.com/avaloniaui\"\n    xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n    xmlns:system=\"clr-namespace:System;assembly=System.Runtime\">\n    <Style>\n        <Style.Resources>\n\n            <!-- Colors -->\n            <Color x:Key=\"NeonOrange\">#ff9f2a</Color>\n            <Color x:Key=\"DarkSeaBlue\">#031332</Color>\n            <Color x:Key=\"NeonMint\">#1cff96</Color>\n            <Color x:Key=\"NeonRed\">#f74040</Color>\n            <Color x:Key=\"NeonYellow\">#fdfd2e</Color>\n            <Color x:Key=\"DarkPurple\">#2a0a84</Color>\n            <Color x:Key=\"DarkGray\">#2E2E2E</Color>\n            <Color x:Key=\"GrayBlue\">#3abcbc</Color>\n            <Color x:Key=\"PerfectGray\">#6B6B6B</Color>\n            <Color x:Key=\"OffGray\">#525252</Color>\n            <Color x:Key=\"DarkestGray\">#2a2824</Color>\n            <Color x:Key=\"White\">#ffffff</Color>\n\n\n            <!-- Avalonia brushes -->\n            <SolidColorBrush x:Key=\"ThemeBackgroundBrush\" Color=\"{DynamicResource DarkSeaBlue}\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundBrush\" Color=\"{DynamicResource NeonMint}\"/>\n            <SolidColorBrush x:Key=\"HighlightBrush\" Color=\"{DynamicResource NeonOrange}\" />\n            <SolidColorBrush x:Key=\"HighlightForegroundBrush\" Color=\"{DynamicResource NeonOrange}\"/>\n            <SolidColorBrush x:Key=\"ThemeAccentBrush4\" Color=\"{DynamicResource OrangeColor}\" />\n            <SolidColorBrush x:Key=\"ThemeAccentBrush3\" Color=\"{DynamicResource BrightOrangeColor}\" />\n            <SolidColorBrush x:Key=\"ThemeControlMidBrush\" Color=\"{DynamicResource GrayBlue}\" />\n            <SolidColorBrush x:Key=\"ThemeControlMidHighBrush\" Color=\"{DynamicResource White}\"\n                   Opacity=\"0.3\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundHighBrush\" Color=\"{DynamicResource White}\" />\n            <SolidColorBrush x:Key=\"ThemeControlHighBrush\" Color=\"{DynamicResource White}\"\n                   Opacity=\"0.7\" />\n            <SolidColorBrush x:Key=\"ThemeControlVeryHighBrush\" Color=\"{DynamicResource White}\"\n                   Opacity=\"0.7\" />\n            <SolidColorBrush x:Key=\"ThemeBorderMidBrush\" Color=\"{DynamicResource BlackColor}\"\n                   Opacity=\"0.4\" />\n            <SolidColorBrush x:Key=\"ThemeAccentBrush\" Color=\"{DynamicResource White}\"\n                   Opacity=\"0.3\" />\n            <SolidColorBrush x:Key=\"ThemeForegroundLowBrush\" Color=\"{DynamicResource White}\"\n                   Opacity=\"0.3\" />\n            <SolidColorBrush x:Key=\"ErrorBrush\" Color=\"{DynamicResource ErrorColor}\" />\n\n            <!-- Custom brushes -->\n            <SolidColorBrush x:Key=\"DataGridHeaderBrush\" Color=\"{DynamicResource DarkGray}\" />\n            <SolidColorBrush x:Key=\"DataGridRowBackground\" Color=\"{DynamicResource PerfectGray}\" />\n            <SolidColorBrush x:Key=\"DataGridRowForeground\" Color=\"{DynamicResource White}\" />\n            <SolidColorBrush x:Key=\"AlternativeDataGridRowBackground\" Color=\"{DynamicResource OffGray}\" />\n            <SolidColorBrush x:Key=\"ComboBoxItemBackgroundBrush\" Color=\"{DynamicResource DarkGray}\" />\n            <SolidColorBrush x:Key=\"ComboBoxItemSelectedBackgroundBrush\" Color=\"{DynamicResource GrayBlue}\" />\n            <SolidColorBrush x:Key=\"UpdateAvailableBrush\" Color=\"{DynamicResource NeonMint}\" />\n            <SolidColorBrush x:Key=\"UpdateBrokenBrush\" Color=\"{DynamicResource NeonRed}\" />\n            <SolidColorBrush x:Key=\"UpdateUnofficialBrush\" Color=\"{DynamicResource NeonYellow}\" />\n\n        </Style.Resources>\n    </Style>\n\n</Styles>"
  },
  {
    "path": "Stardrop/Utilities/Extension/TranslateExtension.cs",
    "content": "﻿using Avalonia.Data;\nusing Avalonia.Markup.Xaml;\nusing Avalonia.Markup.Xaml.MarkupExtensions;\nusing System;\n\n\nnamespace Stardrop.Utilities.Extension\n{\n    public class TranslateExtension : MarkupExtension\n    {\n        public TranslateExtension(string key)\n        {\n            this.Key = key;\n        }\n\n        public string Key { get; set; }\n\n        public string Context { get; set; }\n\n        public override object ProvideValue(IServiceProvider serviceProvider)\n        {\n            var keyToUse = Key;\n            if (!string.IsNullOrWhiteSpace(Context))\n            {\n                keyToUse = $\"{Context}/{Key}\";\n            }\n\n            var binding = new ReflectionBindingExtension($\"[{keyToUse}]\")\n            {\n                Mode = BindingMode.OneWay,\n                Source = Program.translation,\n            };\n\n            return binding.ProvideValue(serviceProvider);\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Utilities/External/GitHub.cs",
    "content": "﻿using SharpCompress.Archives;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Net.Http;\nusing System.Runtime.InteropServices;\nusing System.Text.Json;\nusing System.Threading.Tasks;\n\nnamespace Stardrop.Utilities.External\n{\n    static class GitHub\n    {\n        public async static Task<KeyValuePair<string, string>?> GetLatestSMAPIRelease()\n        {\n            KeyValuePair<string, string>? versionToUri = null;\n\n            // Create a throwaway client\n            HttpClient client = new HttpClient();\n            client.DefaultRequestHeaders.Add(\"User-Agent\", \"Stardrop - SDV Mod Manager\");\n\n            try\n            {\n                var response = await client.GetAsync(\"https://api.github.com/repos/Pathoschild/SMAPI/releases/latest\");\n\n                if (response.Content is not null)\n                {\n                    JsonDocument parsedContent = await JsonDocument.ParseAsync(await response.Content.ReadAsStreamAsync());\n                    string tagName = parsedContent.RootElement.GetProperty(\"tag_name\").ToString();\n                    string downloadUri = parsedContent.RootElement.GetProperty(\"html_url\").ToString();\n                    downloadUri = String.Concat(downloadUri, \"/\", $\"SMAPI-{tagName}-installer.zip\").Replace(\"releases/tag/\", \"releases/download/\");\n\n                    versionToUri = new KeyValuePair<string, string>(tagName, downloadUri);\n                }\n            }\n            catch (Exception ex)\n            {\n                Program.helper.Log($\"Failed to get latest the version of SMAPI: {ex}\", Helper.Status.Alert);\n            }\n            client.Dispose();\n\n            return versionToUri;\n        }\n\n        public async static Task<string> DownloadLatestSMAPIRelease(string uri)\n        {\n            // Create a throwaway client\n            HttpClient client = new HttpClient();\n            client.DefaultRequestHeaders.Add(\"User-Agent\", \"Stardrop - SDV Mod Manager\");\n\n            string downloadedArchivePath = String.Empty;\n            try\n            {\n                var response = await client.GetAsync(uri);\n                using (var archive = ArchiveFactory.Open(await response.Content.ReadAsStreamAsync()))\n                {\n                    downloadedArchivePath = Path.Combine(Pathing.GetSmapiUpgradeFolderPath(), Path.GetDirectoryName(archive.Entries.First().Key));\n                    foreach (var entry in archive.Entries)\n                    {\n                        entry.WriteToDirectory(Pathing.GetSmapiUpgradeFolderPath(), new SharpCompress.Common.ExtractionOptions() { ExtractFullPath = true, Overwrite = true });\n                    }\n                }\n            }\n            catch (Exception ex)\n            {\n                Program.helper.Log($\"Failed to download latest the version of SMAPI: {ex}\", Helper.Status.Alert);\n            }\n            client.Dispose();\n\n            return downloadedArchivePath;\n        }\n\n        public async static Task<KeyValuePair<string, string>?> GetLatestStardropRelease()\n        {\n            KeyValuePair<string, string>? versionToUri = null;\n\n            // Create a throwaway client\n            HttpClient client = new HttpClient();\n            client.DefaultRequestHeaders.Add(\"User-Agent\", \"Stardrop - SDV Mod Manager\");\n\n            try\n            {\n                var response = await client.GetAsync(\"https://api.github.com/repos/Floogen/Stardrop/releases/latest\");\n\n                if (response.Content is not null)\n                {\n                    JsonDocument parsedContent = await JsonDocument.ParseAsync(await response.Content.ReadAsStreamAsync());\n                    string tagName = parsedContent.RootElement.GetProperty(\"tag_name\").ToString();\n                    string downloadUri = parsedContent.RootElement.GetProperty(\"html_url\").ToString();\n                    if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))\n                    {\n                        downloadUri = String.Concat(downloadUri, \"/\", \"Stardrop-osx-x64.zip\");\n                    }\n                    else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))\n                    {\n                        downloadUri = String.Concat(downloadUri, \"/\", \"Stardrop-linux-x64.zip\");\n                    }\n                    else\n                    {\n                        downloadUri = String.Concat(downloadUri, \"/\", \"Stardrop-win-x64.zip\");\n                    }\n                    downloadUri = downloadUri.Replace(\"releases/tag/\", \"releases/download/\");\n\n                    versionToUri = new KeyValuePair<string, string>(tagName, downloadUri);\n                }\n            }\n            catch (Exception ex)\n            {\n                Program.helper.Log($\"Failed to get latest the version of Stardrop: {ex}\", Helper.Status.Alert);\n            }\n            client.Dispose();\n\n            return versionToUri;\n        }\n\n        public async static Task<string> DownloadLatestStardropRelease(string uri)\n        {\n            // Create a throwaway client\n            HttpClient client = new HttpClient();\n            client.DefaultRequestHeaders.Add(\"User-Agent\", \"Stardrop - SDV Mod Manager\");\n\n            string downloadedArchivePath = String.Empty;\n            try\n            {\n                var response = await client.GetAsync(uri);\n                using (var archive = ArchiveFactory.Open(await response.Content.ReadAsStreamAsync()))\n                {\n                    foreach (var entry in archive.Entries)\n                    {\n                        entry.WriteToDirectory(Directory.GetCurrentDirectory(), new SharpCompress.Common.ExtractionOptions() { ExtractFullPath = true, Overwrite = true });\n                    }\n                }\n\n                var extractFolderName = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? \"Stardrop.app\" : \"Stardrop\";\n                var adjustedExtractFolderName = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? \"~Stardrop.app\" : \"~Stardrop\";\n                if (Directory.Exists(extractFolderName))\n                {\n                    if (Directory.Exists(adjustedExtractFolderName))\n                    {\n                        Directory.Delete(adjustedExtractFolderName, true);\n                    }\n                    Directory.Move(extractFolderName, adjustedExtractFolderName);\n                }\n                downloadedArchivePath = Path.Combine(Directory.GetCurrentDirectory(), adjustedExtractFolderName);\n            }\n            catch (Exception ex)\n            {\n                Program.helper.Log($\"Failed to download latest the version of Stardrop: {ex}\", Helper.Status.Alert);\n            }\n            client.Dispose();\n\n            return downloadedArchivePath;\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Utilities/External/NexusClient.cs",
    "content": "﻿using Semver;\nusing Stardrop.Models.Data;\nusing Stardrop.Models.Data.Enums;\nusing Stardrop.Models.Nexus;\nusing Stardrop.Models.Nexus.Web;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Net.Http;\nusing System.Net.Http.Headers;\nusing System.Text;\nusing System.Text.Json;\nusing System.Text.RegularExpressions;\nusing System.Threading;\nusing System.Threading.Tasks;\n\nnamespace Stardrop.Utilities.External\n{\n    public static class Nexus\n    {\n        private static readonly Uri _baseUrl = new Uri(\"https://api.nexusmods.com/v1/\");\n\n        public static NexusClient? Client { get; private set; }\n\n        public delegate void NexusClientChangedHandler(NexusClient? oldClient, NexusClient? newClient);\n        public static event NexusClientChangedHandler? ClientChanged = null;\n\n        /// <summary>\n        /// If the user has entered their Nexus API key in a previous session, this will attempt to retreive\n        /// it.\n        /// </summary>\n        /// <returns>The key, if it exists. Null otherwise.</returns>\n        public static string? GetCachedKey()\n        {\n            if (Program.settings.NexusDetails?.Key is null || File.Exists(Pathing.GetNotionCachePath()) is false)\n            {\n                return null;\n            }\n\n            var pairedKeys = JsonSerializer.Deserialize<PairedKeys>(File.ReadAllText(Pathing.GetNotionCachePath()), new JsonSerializerOptions { AllowTrailingCommas = true });\n            if (pairedKeys?.Vector is null || pairedKeys?.Lock is null)\n            {\n                return null;\n            }\n\n            try\n            {\n                return SimpleObscure.Decrypt(Program.settings.NexusDetails.Key, pairedKeys.Lock, pairedKeys.Vector);\n            }\n            catch (Exception ex)\n            {\n                Program.helper.Log($\"Failed to parse API key when requested: {ex}\");\n            }\n\n            return null;\n        }\n\n        /// <summary>\n        /// Creates a new HttpClient configured with a Nexus API key, and validates it against the Nexus API.\n        /// If successfully validated, sets <see cref=\"Client\"/>, as well as returning a reference to it.\n        /// If called when a <see cref=\"Client\"/> is already set, the Client will be replaced.<br/>\n        /// On success, fires <see cref=\"ClientChanged\"/>, with the previous client (if any), and the new client.\n        /// </summary>\n        /// <param name=\"apiKey\">The API key from Nexus mods that will be included in the 'apiKey' header when making calls.</param>\n        /// <returns>The created client, if successful. Null otherwise.</returns>\n        public static async Task<NexusClient?> CreateClient(string apiKey)\n        {\n            HttpClient client = new HttpClient();\n            client.BaseAddress = _baseUrl;\n            client.DefaultRequestHeaders.Add(\"apiKey\", apiKey);\n            client.DefaultRequestHeaders.Add(\"Application-Name\", \"Stardrop\");\n            client.DefaultRequestHeaders.Add(\"Application-Version\", Program.ApplicationVersion);\n            client.DefaultRequestHeaders.Add(\"User-Agent\", $\"Stardrop/{Program.ApplicationVersion} {Environment.OSVersion}\");\n\n            var nexusClient = new NexusClient(client);\n\n            bool isKeyValid = await nexusClient.ValidateKey();\n            if (isKeyValid is false)\n            {\n                return null;\n            }\n\n            ClientChanged?.Invoke(oldClient: Client, newClient: nexusClient);\n            Client = nexusClient;\n            return Client;\n        }\n\n        /// <summary>\n        /// Nulls out the <see cref=\"Client\"/>, and fires a <see cref=\"ClientChanged\"/> event\n        /// to give consumers a chance to clean up their event handlers.\n        /// </summary>\n        public static void ClearClient()\n        {\n            ClientChanged?.Invoke(oldClient: Client, newClient: null);\n            Client = null;\n        }\n    }\n\n    public class NexusClient\n    {\n        private const string _nxmPattern = @\"nxm:\\/\\/(?<domain>stardewvalley)\\/mods\\/(?<mod>[0-9]+)\\/files\\/(?<file>[0-9]+)\\?key=(?<key>.*)&expires=(?<expiry>[0-9]+)&user_id=(?<user>[0-9]+)\";\n\n        private readonly HttpClient _client;\n        private NexusUser _settings = null!;\n\n        internal int DailyRequestsLimit { get; private set; }\n        internal int DailyRequestsRemaining { get; private set; }\n        internal event EventHandler? DailyRequestLimitsChanged = null;\n        internal event EventHandler<ModDownloadStartedEventArgs>? DownloadStarted = null;\n        internal event EventHandler<ModDownloadProgressEventArgs>? DownloadProgressChanged = null;\n        internal event EventHandler<ModDownloadCompletedEventArgs>? DownloadCompleted = null;\n        internal event EventHandler<ModDownloadFailedEventArgs>? DownloadFailed = null;\n\n        public NexusClient(HttpClient client)\n        {\n            _client = client;\n        }\n\n        /// <summary>\n        /// Calls the /validate endpoint on Nexus Mods' API using the API key stored in this client upon creation.\n        /// If the key is successfully validated, its information is cached in <see cref=\"Program.settings.NexusDetails\"/>.<br/>\n        /// Returns <see langword=\"false\"/> if validation fails.\n        /// </summary>\n        public async Task<bool> ValidateKey()\n        {\n            try\n            {\n                var response = await _client.GetAsync(\"users/validate\");\n                if (!response.IsSuccessStatusCode || response.Content == null)\n                {\n                    Program.helper.Log($\"Call to Nexus Mods failed. HTTP status code: {response.StatusCode}, {response.ReasonPhrase}\");\n                    if (response.Content == null)\n                    {\n                        Program.helper.Log($\"No response from Nexus Mods!\");\n                    }\n                    else\n                    {\n                        Program.helper.Log($\"Response from Nexus Mods:\\n{await response.Content.ReadAsStringAsync()}\");\n                    }\n                    return false;\n                }\n\n\n                string content = await response.Content.ReadAsStringAsync();\n                Validate validationModel = JsonSerializer.Deserialize<Validate>(content, new JsonSerializerOptions { PropertyNameCaseInsensitive = true })!;\n\n                if (validationModel is null || String.IsNullOrEmpty(validationModel.Message) is false)\n                {\n                    Program.helper.Log($\"Unable to validate given API key for Nexus Mods\");\n                    Program.helper.Log($\"Response from Nexus Mods:\\n{content}\");\n\n                    return false;\n                }\n\n                if (Program.settings.NexusDetails is null)\n                {\n                    return false;\n                }\n\n                Program.settings.NexusDetails.Username = validationModel.Name;\n                Program.settings.NexusDetails.IsPremium = validationModel.IsPremium;\n                _settings = Program.settings.NexusDetails;\n\n                UpdateRequestCounts(response.Headers);\n\n                return true;\n            }\n            catch (Exception ex)\n            {\n                Program.helper.Log($\"Failed to validate user's API key for Nexus Mods: {ex}\", Helper.Status.Alert);\n                return false;\n            }\n        }\n\n        public async Task<ModDetails?> GetModDetailsViaNXM(NXM nxmData)\n        {\n            if (nxmData.Link is null)\n            {\n                return null;\n            }\n\n            var match = Regex.Match(Regex.Unescape(nxmData.Link), _nxmPattern);\n            if (match.Success is false || match.Groups[\"domain\"].ToString().ToLower() != \"stardewvalley\" || Int32.TryParse(match.Groups[\"mod\"].ToString(), out int modId) is false)\n            {\n                return null;\n            }\n\n            try\n            {\n                var response = await _client.GetAsync($\"games/stardewvalley/mods/{modId}.json\");\n                if (response.StatusCode == System.Net.HttpStatusCode.OK && response.Content is not null)\n                {\n                    string content = await response.Content.ReadAsStringAsync();\n                    ModDetails modDetails = JsonSerializer.Deserialize<ModDetails>(content, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });\n\n                    if (modDetails is null)\n                    {\n                        Program.helper.Log($\"Unable to get mod details for the mod {modId} on Nexus Mods\");\n                        Program.helper.Log($\"Response from Nexus Mods:\\n{content}\");\n\n                        return null;\n                    }\n\n                    UpdateRequestCounts(response.Headers);\n\n                    return modDetails;\n                }\n                else\n                {\n                    if (response.StatusCode != System.Net.HttpStatusCode.OK)\n                    {\n                        Program.helper.Log($\"Bad status given from Nexus Mods: {response.StatusCode}\");\n                        if (response.Content is not null)\n                        {\n                            Program.helper.Log($\"Response from Nexus Mods:\\n{await response.Content.ReadAsStringAsync()}\");\n                        }\n                    }\n                    else if (response.Content is null)\n                    {\n                        Program.helper.Log($\"No response from Nexus Mods!\");\n                    }\n                }\n            }\n            catch (Exception ex)\n            {\n                Program.helper.Log($\"Unable to get mod details for the mod {modId} on Nexus Mods: {ex}\", Helper.Status.Alert);\n            }\n\n            return null;\n        }\n\n        public async Task<ModFile?> GetFileByVersion(int modId, string version, string? modFlag = null)\n        {\n            if (SemVersion.TryParse(version.Replace(\"v\", String.Empty), SemVersionStyles.Any, out var targetVersion) is false)\n            {\n                Program.helper.Log($\"Unable to parse given target version {version}\");\n                return null;\n            }\n\n            Program.helper.Log($\"Requesting version {version} of mod {modId}{(String.IsNullOrEmpty(modFlag) is false ? $\" with flag {modFlag}\" : String.Empty)}\");\n\n            try\n            {\n                var response = await _client.GetAsync($\"games/stardewvalley/mods/{modId}/files.json\");\n                if (response.StatusCode == System.Net.HttpStatusCode.OK && response.Content is not null)\n                {\n                    string content = await response.Content.ReadAsStringAsync();\n                    ModFiles modFiles = JsonSerializer.Deserialize<ModFiles>(content, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });\n\n                    if (modFiles is null || modFiles.Files is null || modFiles.Files.Count == 0)\n                    {\n                        Program.helper.Log($\"Unable to get the mod file for Nexus Mods\");\n                        Program.helper.Log($\"Response from Nexus Mods:\\n{content}\");\n                    }\n                    else\n                    {\n                        ModFile? selectedFile = null;\n                        foreach (var file in modFiles.Files.Where(x => String.IsNullOrEmpty(x.Version) is false && SemVersion.TryParse(x.Version.Replace(\"v\", String.Empty), SemVersionStyles.Any, out var modVersion) && modVersion == targetVersion))\n                        {\n                            if (String.IsNullOrEmpty(modFlag) is false && ((String.IsNullOrEmpty(file.Name) is false && file.Name.Contains(modFlag, StringComparison.OrdinalIgnoreCase)) || (String.IsNullOrEmpty(file.Description) is false && file.Description.Contains(modFlag, StringComparison.OrdinalIgnoreCase))))\n                            {\n                                selectedFile = file;\n                            }\n                            else if (String.IsNullOrEmpty(modFlag) is true && String.IsNullOrEmpty(file.Category) is false && file.Category.Equals(\"MAIN\", StringComparison.OrdinalIgnoreCase))\n                            {\n                                selectedFile = file;\n                            }\n                        }\n\n                        if (selectedFile is null)\n                        {\n                            Program.helper.Log($\"Unable to get a matching file for the mod {modId} with version {version}{(String.IsNullOrEmpty(modFlag) is false ? $\" and with the flag {modFlag}\" : String.Empty)} via Nexus Mods: \\n{String.Join(\"\\n\", modFiles.Files.Select(m => $\"{m.Name} | {m.Version}\"))}\");\n                        }\n\n                        UpdateRequestCounts(response.Headers);\n\n                        return selectedFile;\n                    }\n                }\n                else\n                {\n                    if (response.StatusCode != System.Net.HttpStatusCode.OK)\n                    {\n                        Program.helper.Log($\"Bad status given from Nexus Mods: {response.StatusCode}\");\n                        if (response.Content is not null)\n                        {\n                            Program.helper.Log($\"Response from Nexus Mods:\\n{await response.Content.ReadAsStringAsync()}\");\n                        }\n                    }\n                    else if (response.Content is null)\n                    {\n                        Program.helper.Log($\"No response from Nexus Mods!\");\n                    }\n                }\n            }\n            catch (Exception ex)\n            {\n                Program.helper.Log($\"Failed to get the mod file for Nexus Mods: {ex}\", Helper.Status.Alert);\n            }\n\n            return null;\n        }\n\n        public async Task<string?> GetFileDownloadLink(NXM nxmData, string? serverName = null)\n        {\n            if (nxmData.Link is null)\n            {\n                return null;\n            }\n\n            var match = Regex.Match(Regex.Unescape(nxmData.Link), _nxmPattern);\n            if (match.Success is false || match.Groups[\"domain\"].ToString().ToLower() != \"stardewvalley\" || Int32.TryParse(match.Groups[\"mod\"].ToString(), out int modId) is false || Int32.TryParse(match.Groups[\"file\"].ToString(), out int fileId) is false)\n            {\n                return null;\n            }\n\n            return await GetFileDownloadLink(modId, fileId, match.Groups[\"key\"].ToString(), match.Groups[\"expiry\"].ToString(), serverName);\n        }\n\n        public async Task<string?> GetFileDownloadLink(int modId, int fileId, string? nxmKey = null, string? nxmExpiry = null, string? serverName = null)\n        {\n            if (String.IsNullOrEmpty(serverName) || _settings.IsPremium is false)\n            {\n                serverName = \"Nexus CDN\";\n            }\n\n            try\n            {\n                string url = $\"games/stardewvalley/mods/{modId}/files/{fileId}/download_link.json\";\n                if (String.IsNullOrEmpty(nxmKey) is false && String.IsNullOrEmpty(nxmExpiry) is false)\n                {\n                    url = $\"{url}?key={nxmKey}&expires={nxmExpiry}\";\n                }\n                var response = await _client.GetAsync(url);\n\n                if (response.StatusCode == System.Net.HttpStatusCode.OK && response.Content is not null)\n                {\n                    string content = await response.Content.ReadAsStringAsync();\n                    List<DownloadLink> downloadLinks = JsonSerializer.Deserialize<List<DownloadLink>>(content, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });\n\n                    if (downloadLinks is null || downloadLinks.Count == 0)\n                    {\n                        Program.helper.Log($\"Unable to get the download link for Nexus Mods\");\n                        Program.helper.Log($\"Response from Nexus Mods:\\n{content}\");\n                    }\n                    else\n                    {\n                        UpdateRequestCounts(response.Headers);\n\n                        var selectedFile = downloadLinks.FirstOrDefault(x => x.ShortName?.ToLower() == serverName.ToLower());\n                        if (selectedFile is not null)\n                        {\n                            Program.helper.Log($\"Requested download link from Nexus Mods using their {serverName} server\");\n                            return selectedFile.Uri;\n                        }\n                    }\n                }\n                else\n                {\n                    if (response.StatusCode != System.Net.HttpStatusCode.OK)\n                    {\n                        Program.helper.Log($\"Bad status given from Nexus Mods: {response.StatusCode}\");\n                        if (response.Content is not null)\n                        {\n                            Program.helper.Log($\"Response from Nexus Mods:\\n{await response.Content.ReadAsStringAsync()}\");\n                        }\n                    }\n                    else if (response.Content is null)\n                    {\n                        Program.helper.Log($\"No response from Nexus Mods!\");\n                    }\n                }\n            }\n            catch (Exception ex)\n            {\n                Program.helper.Log($\"Failed to get the download link for Nexus Mods: {ex}\", Helper.Status.Alert);\n            }\n\n            return null;\n        }\n\n        public async Task<NexusDownloadResult> DownloadFileAndGetPath(string uri, string fileName)\n        {\n            var requestUri = new Uri(uri);\n            var downloadCancellationSource = new CancellationTokenSource();\n            var requestMessage = new HttpRequestMessage(HttpMethod.Get, requestUri);\n            try\n            {\n\n                var response = await _client.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead, downloadCancellationSource.Token);\n                if (response.IsSuccessStatusCode is false)\n                {\n                    Program.helper.Log($\"Failed to download mod file for Nexus Mods: HTTP {response.StatusCode}, {response.ReasonPhrase}\", Helper.Status.Alert);\n                    return new(DownloadResultKind.Failed, null);\n                }\n\n                using var fileStream = new FileStream(Path.Combine(Pathing.GetNexusPath(), fileName), FileMode.CreateNew);\n                using var downloadStream = await response.Content.ReadAsStreamAsync();\n\n                long? contentLength = response.Content.Headers.ContentLength;\n                DownloadStarted?.Invoke(this, new ModDownloadStartedEventArgs(requestUri, fileName, contentLength, downloadCancellationSource));\n                    \n                var buffer = new byte[81920];\n                long totalBytesRead = 0;\n                int bytesRead;\n                while ((bytesRead = await downloadStream.ReadAsync(buffer, 0, buffer.Length, downloadCancellationSource.Token)) != 0)\n                {\n                    await fileStream.WriteAsync(buffer, 0, bytesRead, downloadCancellationSource.Token);\n                    totalBytesRead += bytesRead;\n                    DownloadProgressChanged?.Invoke(this, new ModDownloadProgressEventArgs(requestUri, totalBytesRead));\n                }                \n\n                DownloadCompleted?.Invoke(this, new ModDownloadCompletedEventArgs(requestUri));\n                return new(DownloadResultKind.Success, Path.Combine(Pathing.GetNexusPath(), fileName));\n            }\n            catch (Exception ex)\n            {\n                // Delete partially downloaded file, if any.\n                File.Delete(Path.Combine(Pathing.GetNexusPath(), fileName));\n                if (ex is TaskCanceledException)\n                {\n                    Program.helper.Log($\"The user canceled the download from Nexus from URL {uri}\", Helper.Status.Info);\n                    return new(DownloadResultKind.UserCanceled, null);\n                }\n                else\n                {\n                    Program.helper.Log($\"Failed to download mod file for Nexus Mods: {ex}\", Helper.Status.Alert);\n                    DownloadFailed?.Invoke(this, new ModDownloadFailedEventArgs(requestUri));\n                    return new(DownloadResultKind.Failed, null);\n                }\n            }\n        }\n\n        public async Task<List<Endorsement>> GetEndorsements()\n        {\n            try\n            {\n                var response = await _client.GetAsync($\"user/endorsements\");\n                if (response.StatusCode == System.Net.HttpStatusCode.OK && response.Content is not null)\n                {\n                    string content = await response.Content.ReadAsStringAsync();\n                    List<Endorsement> endorsements = JsonSerializer.Deserialize<List<Endorsement>>(content, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });\n\n                    if (endorsements is null)\n                    {\n                        Program.helper.Log($\"Unable to get endorsements for Nexus Mods\");\n                        Program.helper.Log($\"Response from Nexus Mods:\\n{content}\");\n                    }\n                    else\n                    {\n                        endorsements = endorsements.Where(e => e.DomainName?.ToLower() == \"stardewvalley\").ToList();\n\n                        UpdateRequestCounts(response.Headers);\n\n                        return endorsements;\n                    }\n                }\n                else\n                {\n                    if (response.StatusCode != System.Net.HttpStatusCode.OK)\n                    {\n                        Program.helper.Log($\"Bad status given from Nexus Mods: {response.StatusCode}\");\n                        if (response.Content is not null)\n                        {\n                            Program.helper.Log($\"Response from Nexus Mods:\\n{await response.Content.ReadAsStringAsync()}\");\n                        }\n                    }\n                    else if (response.Content is null)\n                    {\n                        Program.helper.Log($\"No response from Nexus Mods!\");\n                    }\n                }\n            }\n            catch (Exception ex)\n            {\n                Program.helper.Log($\"Failed to get endorsements for Nexus Mods: {ex}\", Helper.Status.Alert);\n            }\n\n            return new List<Endorsement>();\n        }\n\n        public async Task<EndorsementResponse> SetModEndorsement(int modId, bool isEndorsed)\n        {\n            try\n            {\n                var requestPackage = new StringContent(\"{\\\"Version\\\":\\\"1.0.0\\\"}\", Encoding.UTF8, \"application/json\");\n                var response = await _client.PostAsync($\"games/stardewvalley/mods/{modId}/{(isEndorsed is true ? \"endorse.json\" : \"abstain.json\")}\", requestPackage);\n                if (response.Content is not null)\n                {\n                    string content = await response.Content.ReadAsStringAsync();\n                    EndorsementResult endorsementResult = JsonSerializer.Deserialize<EndorsementResult>(content, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });\n\n                    if (endorsementResult is null)\n                    {\n                        Program.helper.Log($\"Unable to set endorsement for Nexus Mods\");\n                        Program.helper.Log($\"Response from Nexus Mods:\\n{content}\");\n\n                        return EndorsementResponse.Unknown;\n                    }\n\n                    UpdateRequestCounts(response.Headers);\n\n                    switch (endorsementResult.Status?.ToUpper())\n                    {\n                        case \"ENDORSED\":\n                            return EndorsementResponse.Endorsed;\n                        case \"ABSTAINED\":\n                            return EndorsementResponse.Abstained;\n                        case \"ERROR\":\n                            var parsedMessage = endorsementResult.Message?.ToUpper();\n                            if (parsedMessage == \"IS_OWN_MOD\")\n                            {\n                                return EndorsementResponse.IsOwnMod;\n                            }\n                            else if (parsedMessage == \"TOO_SOON_AFTER_DOWNLOAD\")\n                            {\n                                return EndorsementResponse.TooSoonAfterDownload;\n                            }\n                            else if (parsedMessage == \"NOT_DOWNLOADED_MOD\")\n                            {\n                                return EndorsementResponse.NotDownloadedMod;\n                            }\n                            Program.helper.Log(parsedMessage);\n                            break;\n                        default:\n                            Program.helper.Log($\"Unhandled status for endorsement: {endorsementResult.Status} | {endorsementResult.Message}\");\n                            break;\n                    }\n                }\n                else\n                {\n                    Program.helper.Log($\"No response from Nexus Mods! Status code: {response.StatusCode}\");\n                }\n            }\n            catch (Exception ex)\n            {\n                Program.helper.Log($\"Failed to set endorsement for Nexus Mods: {ex}\", Helper.Status.Alert);\n            }\n\n            return EndorsementResponse.Unknown;\n        }\n\n        public async Task<string?> DownloadThumbnail(int modId)\n        {\n            try\n            {\n                var response = await _client.GetAsync($\"games/stardewvalley/mods/{modId}.json\");\n                if (response.StatusCode == System.Net.HttpStatusCode.OK && response.Content is not null)\n                {\n                    string content = await response.Content.ReadAsStringAsync();\n                    ModDetails modDetails = JsonSerializer.Deserialize<ModDetails>(content, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });\n\n                    if (modDetails is null)\n                    {\n                        Program.helper.Log($\"Unable to get mod thumbnail for the mod {modId} on Nexus Mods\");\n                        Program.helper.Log($\"Response from Nexus Mods:\\n{content}\");\n\n                        return null;\n                    }\n\n                    UpdateRequestCounts(response.Headers);\n\n                    if (string.IsNullOrEmpty(modDetails.ThumbnailUrl))\n                    {\n                        Program.helper.Log($\"The mod {modId} does not have a valid thumbnail image available on Nexus Mods\");\n                        Program.helper.Log($\"Response from Nexus Mods:\\n{content}\");\n                        return null;\n                    }\n\n                    // Download the thumbnail\n                    var thumbnailPath = Path.Combine(Pathing.GetThumbnailsPath(), $\"{modId}{Path.GetExtension(modDetails.ThumbnailUrl)}\");\n                    using var fileStream = new FileStream(thumbnailPath, FileMode.Create, FileAccess.Write, FileShare.None, bufferSize: 8192, useAsync: true);\n                    using var downloadStream = await _client.GetStreamAsync(modDetails.ThumbnailUrl);\n\n                    await downloadStream.CopyToAsync(fileStream);\n                    await fileStream.FlushAsync();\n\n                    return thumbnailPath;\n                }\n                else\n                {\n                    if (response.StatusCode != System.Net.HttpStatusCode.OK)\n                    {\n                        Program.helper.Log($\"Bad status given from Nexus Mods: {response.StatusCode}\");\n                        if (response.Content is not null)\n                        {\n                            Program.helper.Log($\"Response from Nexus Mods:\\n{await response.Content.ReadAsStringAsync()}\");\n                        }\n                    }\n                    else if (response.Content is null)\n                    {\n                        Program.helper.Log($\"No response from Nexus Mods!\");\n                    }\n                }\n            }\n            catch (Exception ex)\n            {\n                Program.helper.Log($\"Unable to get mod thumbnail for the mod {modId} on Nexus Mods: {ex}\", Helper.Status.Alert);\n            }\n\n            return null;\n        }\n\n        private void UpdateRequestCounts(HttpResponseHeaders headers)\n        {\n            if (headers.TryGetValues(\"x-rl-daily-limit\", out var limitValues) && Int32.TryParse(limitValues.First(), out int dailyLimit))\n            {\n                DailyRequestsLimit = dailyLimit;\n            }\n\n            if (headers.TryGetValues(\"x-rl-daily-remaining\", out var remainingValues) && Int32.TryParse(remainingValues.First(), out int dailyRemaining))\n            {\n                DailyRequestsRemaining = dailyRemaining;\n            }\n\n            DailyRequestLimitsChanged?.Invoke(this, EventArgs.Empty);\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Utilities/External/NexusDownloadResult.cs",
    "content": "﻿namespace Stardrop.Utilities.External\n{\n    public enum DownloadResultKind\n    {\n        Failed,\n        UserCanceled,\n        Success\n    }\n\n    public record struct NexusDownloadResult(DownloadResultKind ResultKind, string? DownloadedModFilePath);\n}\n"
  },
  {
    "path": "Stardrop/Utilities/External/SMAPI.cs",
    "content": "﻿using Semver;\nusing Stardrop.Models;\nusing Stardrop.Models.SMAPI;\nusing Stardrop.Models.SMAPI.Web;\nusing System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Linq;\nusing System.Net.Http;\nusing System.Reflection;\nusing System.Runtime.InteropServices;\nusing System.Text;\nusing System.Text.Json;\nusing System.Threading.Tasks;\n\nnamespace Stardrop.Utilities.External\n{\n    static class SMAPI\n    {\n        internal static bool IsRunning = false;\n        internal static Process Process;\n\n        public static ProcessStartInfo GetPrepareProcess(bool hideConsole)\n        {\n            var smapiInfo = new FileInfo(Pathing.GetSmapiPath());\n\n            var fileName = smapiInfo.FullName;\n            var arguments = string.Empty;\n            var parsedModPath = string.Empty;\n            if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux) is true)\n            {\n                fileName = \"/usr/bin/env\";\n                arguments = $\"bash -c \\\"SMAPI_MODS_PATH='{Pathing.GetSelectedModsFolderPath()}' '{Pathing.GetSmapiPath().Replace(\"StardewModdingAPI.dll\", \"StardewValley\")}'\\\"\";\n                parsedModPath = $\"'{Pathing.GetSelectedModsFolderPath()}'\";\n            }\n            else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) is true)\n            {\n                fileName = \"/usr/bin/open\";\n                arguments = $\"-a \\\"Terminal\\\" \\\"{Pathing.GetSmapiPath().Replace(\"StardewModdingAPI.dll\", \"StardewModdingAPI\")}\\\" --args --mods-path \\\"{Pathing.GetSelectedModsFolderPath()}\\\"\";\n                parsedModPath = $\"{Pathing.GetSelectedModsFolderPath()}\";\n\n                /* Alternative route (using AppleScript) of activating Terminal + SMAPI\n                fileName = \"/usr/bin/env\";\n                arguments = $@\"osascript -e \"\"tell application \\\"\"Terminal\\\"\"\n                set smapi_path to \\\"\"{Pathing.GetSmapiPath().Replace(\"StardewModdingAPI.dll\", \"StardewModdingAPI\")}\\\"\"\n                set mods_path to \\\"\"{Pathing.GetSelectedModsFolderPath()}\\\"\"\n                activate\n                do script \\\"\"\\\"\" & quoted form of the POSIX path of smapi_path & \\\"\" --mods-path \\\"\" & quoted form of the POSIX path of mods_path\n                end tell\"\"\";\n                */\n            }\n            else\n            {\n                parsedModPath = Pathing.GetSelectedModsFolderPath();\n            }\n\n            Program.helper.Log($\"Starting SMAPI with the following arguments: {arguments}\");\n            var processInfo = new ProcessStartInfo\n            {\n                FileName = fileName,\n                Arguments = arguments,\n                WorkingDirectory = smapiInfo.DirectoryName,\n                RedirectStandardOutput = false,\n                RedirectStandardError = false,\n                CreateNoWindow = hideConsole,\n                UseShellExecute = false\n            };\n\n            // Set SMAPI_MODS_PATH EnvironmentVariable if required\n            if (string.IsNullOrEmpty(parsedModPath) is false)\n            {\n                Program.helper.Log($\"Setting SMAPI_MODS_PATH to: {parsedModPath}\");\n                processInfo.EnvironmentVariables[\"SMAPI_MODS_PATH\"] = parsedModPath;\n\n                Program.helper.Log($\"Process SMAPI_MODS_PATH: {processInfo.EnvironmentVariables[\"SMAPI_MODS_PATH\"]}\");\n                Program.helper.Log($\"System SMAPI_MODS_PATH: {Environment.GetEnvironmentVariable(\"SMAPI_MODS_PATH\")}\");\n            }\n\n            return processInfo;\n        }\n\n        public static string GetProcessName()\n        {\n            if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))\n            {\n                return \"StardewModdingA\";\n            }\n\n            return \"StardewModdingAPI\";\n        }\n\n        public async static Task<List<ModEntry>> GetModUpdateData(GameDetails gameDetails, List<Mod> mods)\n        {\n            List<ModSearchEntry> searchEntries = new List<ModSearchEntry>();\n            foreach (var mod in mods.Where(m => m.HasValidVersion() && m.HasUpdateKeys()))\n            {\n                searchEntries.Add(new ModSearchEntry(mod.UniqueId, mod.Version, mod.Manifest.UpdateKeys));\n            }\n            foreach (var requirementKey in mods.SelectMany(m => m.Requirements))\n            {\n                if (!searchEntries.Any(e => e.Id.Equals(requirementKey.UniqueID, StringComparison.OrdinalIgnoreCase)))\n                {\n                    searchEntries.Add(new ModSearchEntry() { Id = requirementKey.UniqueID });\n                }\n            }\n\n            // Create the body to be sent via the POST request\n            ModSearchData searchData = new ModSearchData(searchEntries, gameDetails.SmapiVersion, gameDetails.GameVersion, gameDetails.System.ToString(), true);\n\n            // Create a throwaway client\n            HttpClient client = new HttpClient();\n            client.DefaultRequestHeaders.Add(\"Application-Name\", \"Stardrop\");\n            client.DefaultRequestHeaders.Add(\"Application-Version\", Program.ApplicationVersion);\n            client.DefaultRequestHeaders.Add(\"User-Agent\", $\"Stardrop/{Program.ApplicationVersion} {Environment.OSVersion}\");\n\n            var parsedRequest = JsonSerializer.Serialize(searchData, new JsonSerializerOptions() { WriteIndented = true, IgnoreNullValues = true });\n            var requestPackage = new StringContent(parsedRequest, Encoding.UTF8, \"application/json\");\n            var response = await client.PostAsync(\"https://smapi.io/api/v3.0/mods\", requestPackage);\n\n            List<ModEntry> modUpdateData = new List<ModEntry>();\n            if (response.StatusCode == System.Net.HttpStatusCode.OK && response.Content is not null)\n            {\n                // In the name of the Nine Divines, why is JsonSerializer.Deserialize case sensitive by default???\n                string content = await response.Content.ReadAsStringAsync();\n                modUpdateData = JsonSerializer.Deserialize<List<ModEntry>>(content, new JsonSerializerOptions { PropertyNameCaseInsensitive = true });\n\n                if (modUpdateData is null || modUpdateData.Count == 0)\n                {\n                    Program.helper.Log($\"Mod update data was not parsable from smapi.io\");\n                    Program.helper.Log($\"Response from smapi.io:\\n{content}\");\n                    Program.helper.Log($\"Our request to smapi.io:\\n{parsedRequest}\");\n                }\n            }\n            else\n            {\n                if (response.StatusCode != System.Net.HttpStatusCode.OK)\n                {\n                    Program.helper.Log($\"Bad status given from smapi.io: {response.StatusCode}\");\n                    if (response.Content is not null)\n                    {\n                        Program.helper.Log($\"Response from smapi.io:\\n{await response.Content.ReadAsStringAsync()}\");\n                    }\n                }\n                else if (response.Content is null)\n                {\n                    Program.helper.Log($\"No response from smapi.io!\");\n                }\n                else\n                {\n                    Program.helper.Log($\"Error getting mod update data from smapi.io!\");\n                }\n\n                Program.helper.Log($\"Our request to smapi.io:\\n{parsedRequest}\");\n            }\n\n            client.Dispose();\n\n            return modUpdateData;\n        }\n\n        internal static SemVersion? GetVersion()\n        {\n            AssemblyName smapiAssembly = AssemblyName.GetAssemblyName(Path.Combine(Pathing.defaultGamePath, \"StardewModdingAPI.dll\"));\n\n            if (smapiAssembly is null || smapiAssembly.Version is null)\n            {\n                return null;\n            }\n\n            return SemVersion.Parse($\"{smapiAssembly.Version.Major}.{smapiAssembly.Version.Minor}.{smapiAssembly.Version.Build}\", SemVersionStyles.Any);\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Utilities/Helper.cs",
    "content": "﻿using System;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Runtime.CompilerServices;\n\nnamespace Stardrop.Utilities\n{\n    internal class Helper\n    {\n        // Log file related\n        private string basePath = AppDomain.CurrentDomain.BaseDirectory;\n        private string logFileName = \"log\";\n        private string logFileExtension = \".txt\";\n\n        // Listener and stat related\n        private TraceListener listener;\n        private int totalDebug = 0, totalAlert = 0, totalWarning = 0, totalInfo = 0;\n\n        // Used for identifying different statuses when logging\n        public enum Status { Debug, Alert, Warning, Info };\n\n        public Helper(string fileName = \"log\", string fileExtension = \".txt\", string path = null)\n        {\n            // Set the log file name and extension\n            logFileName = fileName;\n            logFileExtension = fileExtension;\n            basePath = String.IsNullOrEmpty(path) ? basePath : path;\n\n            // Delete any previous log file\n            if (File.Exists(GetLogPath()))\n            {\n                File.Delete(GetLogPath());\n            }\n\n            // Create and enable the listener\n            listener = new DelimitedListTraceListener(GetLogPath());\n            Trace.Listeners.Add(listener);\n\n            // This makes the Debug.WriteLine() calls always write to the text file\n            // Rather than waiting for a Debug.Flush() call\n            Trace.AutoFlush = true;\n        }\n\n        public string GetLogPath()\n        {\n            return Path.Combine(basePath, String.Concat(logFileName, logFileExtension));\n        }\n\n        public void DisableTracing()\n        {\n            // If listener exists and still is active, remove it\n            if (!(listener is null) && Trace.Listeners.Contains(listener))\n            {\n                Trace.Listeners.Remove(listener);\n                listener.Close();\n            }\n        }\n\n        public bool IsActive()\n        {\n            return listener != null;\n        }\n\n        // Handles the Debug.WriteLine calls\n        // It will grab the calling method and line as well via CompilerServices \n        public void Log(string message, Status status = Status.Debug, [CallerMemberName] string caller = \"\", [CallerLineNumber] int line = 0, [CallerFilePath] string path = \"\")\n        {\n            // Tracking status info\n            TrackStatus(status);\n\n            string fileName = Path.GetFileName(path).Split('.')[0];\n            Trace.WriteLine(string.Format(\"[{0}][{1}][{2}.{3}: Line {4}] {5}\", DateTime.Now.ToString(), status.ToString(), fileName, caller.ToString(), line, message));\n        }\n\n        public void Log(object messageObj, Status status = Status.Debug, [CallerMemberName] string caller = \"\", [CallerLineNumber] int line = 0, [CallerFilePath] string path = \"\")\n        {\n            try\n            {\n                Log(messageObj.ToString(), status, caller, line, path);\n            }\n            catch\n            {\n                Log(String.Format($\"Unable to parse {messageObj} to string!\"), Status.Warning);\n            }\n        }\n\n        #region Status related tracking\n        public bool HasAlert()\n        {\n            return totalAlert > 0 ? true : false;\n        }\n\n        public bool HasWarning()\n        {\n            return totalWarning > 0 ? true : false;\n        }\n\n        public bool HasInfo()\n        {\n            return totalInfo > 0 ? true : false;\n        }\n\n        public bool HasDebug()\n        {\n            return totalDebug > 0 ? true : false;\n        }\n        #endregion\n\n        // Handles tracking the count of different Status counts\n        private void TrackStatus(Status status)\n        {\n            switch (status)\n            {\n                case Status.Debug:\n                    totalDebug += 1;\n                    break;\n                case Status.Alert:\n                    totalAlert += 1;\n                    break;\n                case Status.Warning:\n                    totalWarning += 1;\n                    break;\n                case Status.Info:\n                    totalInfo += 1;\n                    break;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Utilities/Internal/EnumParser.cs",
    "content": "﻿using System;\nusing System.ComponentModel;\nusing System.Reflection;\n\nnamespace Stardrop.Utilities.Internal\n{\n    internal static class EnumParser\n    {\n        // Shamelessly copied from: https://stackoverflow.com/questions/1415140/can-my-enums-have-friendly-names\n        public static string? GetDescription(this Enum? value)\n        {\n            if (value is null)\n            {\n                return null;\n            }\n\n            Type type = value.GetType();\n            string? name = Enum.GetName(type, value);\n            if (name is not null)\n            {\n                FieldInfo? field = type.GetField(name);\n                if (field is not null)\n                {\n                    DescriptionAttribute? attr = Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) as DescriptionAttribute;\n\n                    if (attr is not null)\n                    {\n                        return attr.Description;\n                    }\n                }\n            }\n\n            return value.ToString();\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Utilities/Internal/ManifestParser.cs",
    "content": "﻿using SharpCompress.Archives;\nusing Stardrop.Models.SMAPI;\nusing System;\nusing System.IO;\nusing System.Text.Json;\nusing System.Threading.Tasks;\n\nnamespace Stardrop.Utilities.Internal\n{\n    internal static class ManifestParser\n    {\n        public static async Task<Manifest?> GetDataAsync(IArchiveEntry manifestFile)\n        {\n            using (Stream stream = manifestFile.OpenEntryStream())\n            {\n                using (var reader = new StreamReader(stream))\n                {\n                    return GetData(await reader.ReadToEndAsync());\n                }\n            }\n        }\n\n        public static Manifest? GetData(string manifestText)\n        {\n            try\n            {\n                return JsonSerializer.Deserialize<Manifest>(manifestText, new JsonSerializerOptions() { AllowTrailingCommas = true, ReadCommentHandling = JsonCommentHandling.Skip, PropertyNameCaseInsensitive = true });\n            }\n            catch (JsonException)\n            {\n                // Attempt to parse out illegal JSON characters, as System.Text.Json does not have any native handling (unlike Newtonsoft.Json)\n                manifestText = manifestText.Replace(\"\\r\", String.Empty).Replace(\"\\n\", String.Empty);\n                return JsonSerializer.Deserialize<Manifest>(manifestText, new JsonSerializerOptions() { AllowTrailingCommas = true, ReadCommentHandling = JsonCommentHandling.Skip, PropertyNameCaseInsensitive = true });\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Utilities/JsonTools.cs",
    "content": "﻿using System;\nusing System.Buffers;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Text;\nusing System.Text.Json;\n\nnamespace Stardrop.Utilities\n{\n    internal class JsonTools\n    {\n        // https://stackoverflow.com/questions/58378409/jsondocument-get-json-string\n        public static string ParseDocumentToString(JsonDocument jdoc)\n        {\n            using (var stream = new MemoryStream())\n            {\n                Utf8JsonWriter writer = new Utf8JsonWriter(stream, new JsonWriterOptions { Indented = true });\n                jdoc.WriteTo(writer);\n                writer.Flush();\n\n                return Encoding.UTF8.GetString(stream.ToArray());\n            }\n        }\n\n        // https://stackoverflow.com/questions/58694837/system-text-json-merge-two-objects\n        public static string Merge(string originalJson, string newContent, bool includeMissingProperties)\n        {\n            var outputBuffer = new ArrayBufferWriter<byte>();\n\n            using (JsonDocument jDoc1 = JsonDocument.Parse(originalJson))\n            using (JsonDocument jDoc2 = JsonDocument.Parse(newContent))\n            using (var jsonWriter = new Utf8JsonWriter(outputBuffer, new JsonWriterOptions { Indented = true }))\n            {\n                JsonElement root1 = jDoc1.RootElement;\n                JsonElement root2 = jDoc2.RootElement;\n\n                if (root1.ValueKind != JsonValueKind.Array && root1.ValueKind != JsonValueKind.Object)\n                {\n                    throw new InvalidOperationException($\"The original JSON document to merge new content into must be a container type. Instead it is {root1.ValueKind}.\");\n                }\n\n                if (root1.ValueKind != root2.ValueKind)\n                {\n                    return originalJson;\n                }\n\n                if (root1.ValueKind == JsonValueKind.Array)\n                {\n                    MergeArrays(jsonWriter, root1, root2);\n                }\n                else\n                {\n                    MergeObjects(jsonWriter, root1, root2, includeMissingProperties);\n                }\n            }\n\n            return Encoding.UTF8.GetString(outputBuffer.WrittenSpan);\n        }\n\n        private static void MergeObjects(Utf8JsonWriter jsonWriter, JsonElement root1, JsonElement root2, bool includeMissingProperties)\n        {\n            Debug.Assert(root1.ValueKind == JsonValueKind.Object);\n            Debug.Assert(root2.ValueKind == JsonValueKind.Object);\n\n            jsonWriter.WriteStartObject();\n\n            // Write all the properties of the first document.\n            // If a property exists in both documents, either:\n            // * Merge them, if the value kinds match (e.g. both are objects or arrays),\n            // * Completely override the value of the first with the one from the second, if the value kind mismatches (e.g. one is object, while the other is an array or string),\n            // * Or favor the value of the first (regardless of what it may be), if the second one is null (i.e. don't override the first).\n            foreach (JsonProperty property in root1.EnumerateObject())\n            {\n                string propertyName = property.Name;\n\n                JsonValueKind newValueKind;\n\n                if (root2.TryGetProperty(propertyName, out JsonElement newValue) && (newValueKind = newValue.ValueKind) != JsonValueKind.Null)\n                {\n                    jsonWriter.WritePropertyName(propertyName);\n\n                    JsonElement originalValue = property.Value;\n                    JsonValueKind originalValueKind = originalValue.ValueKind;\n\n                    if (newValueKind == JsonValueKind.Object && originalValueKind == JsonValueKind.Object)\n                    {\n                        MergeObjects(jsonWriter, originalValue, newValue, includeMissingProperties); // Recursive call\n                    }\n                    else if (newValueKind == JsonValueKind.Array && originalValueKind == JsonValueKind.Array)\n                    {\n                        MergeArrays(jsonWriter, originalValue, newValue);\n                    }\n                    else\n                    {\n                        newValue.WriteTo(jsonWriter);\n                    }\n                }\n                else\n                {\n                    property.WriteTo(jsonWriter);\n                }\n            }\n\n            // Write all the properties of the second document that are unique to it\n            if (includeMissingProperties is true)\n            {\n                foreach (JsonProperty property in root2.EnumerateObject())\n                {\n                    if (!root1.TryGetProperty(property.Name, out _))\n                    {\n                        property.WriteTo(jsonWriter);\n                    }\n                }\n            }\n\n            jsonWriter.WriteEndObject();\n        }\n\n        private static void MergeArrays(Utf8JsonWriter jsonWriter, JsonElement root1, JsonElement root2)\n        {\n            Debug.Assert(root1.ValueKind == JsonValueKind.Array);\n            Debug.Assert(root2.ValueKind == JsonValueKind.Array);\n\n            jsonWriter.WriteStartArray();\n\n            // Write all the elements from the original JSON arrays\n            foreach (JsonElement element in root2.EnumerateArray())\n            {\n                element.WriteTo(jsonWriter);\n            }\n\n            jsonWriter.WriteEndArray();\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Utilities/NXMProtocol.cs",
    "content": "﻿using Microsoft.Win32;\nusing System;\nusing System.Runtime.InteropServices;\n\nnamespace Stardrop.Utilities\n{\n    internal static class NXMProtocol\n    {\n        public static bool Register(string applicationPath)\n        {\n            try\n            {\n                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) is false)\n                {\n                    Program.helper.Log($\"Attempted to modify registery keys for NXM protocol on a non-Windows system!\");\n                    return false;\n                }\n\n                var keyTest = Registry.CurrentUser.OpenSubKey(\"Software\", true).OpenSubKey(\"Classes\", true);\n                RegistryKey key = keyTest.CreateSubKey(\"nxm\");\n                key.SetValue(\"URL Protocol\", \"nxm\");\n                key.CreateSubKey(@\"shell\\open\\command\").SetValue(\"\", \"\\\"\" + applicationPath + \"\\\" --nxm \\\"%1\\\"\");\n            }\n            catch (Exception ex)\n            {\n                Program.helper.Log($\"Failed to associate Stardrop with the NXM protocol: {ex}\");\n                return false;\n            }\n\n            return true;\n        }\n\n        public static bool Validate(string applicationPath)\n        {\n            try\n            {\n                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) is false)\n                {\n                    Program.helper.Log($\"Attempted to modify registery keys for NXM protocol on a non-Windows system!\");\n                    return false;\n                }\n\n                var baseKeyTest = Registry.CurrentUser.OpenSubKey(\"Software\", true).OpenSubKey(\"Classes\", true).OpenSubKey(\"nxm\", true);\n                if (baseKeyTest is null || baseKeyTest.GetValue(\"URL Protocol\").ToString() != \"nxm\")\n                {\n                    return false;\n                }\n\n                var actualKeyTest = Registry.CurrentUser.OpenSubKey(\"Software\", true).OpenSubKey(\"Classes\", true).OpenSubKey(@\"nxm\\shell\\open\\command\", true);\n                if (actualKeyTest.GetValue(String.Empty).ToString() != \"\\\"\" + applicationPath + \"\\\" --nxm \\\"%1\\\"\")\n                {\n                    return false;\n                }\n            }\n            catch (Exception ex)\n            {\n                return false;\n            }\n\n            return true;\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Utilities/NexusWebsocket.cs",
    "content": "﻿using Stardrop.Models.Nexus.Web;\nusing Stardrop.ViewModels;\nusing System;\nusing System.Diagnostics;\nusing System.Net.WebSockets;\nusing System.Text;\nusing System.Text.Json;\nusing System.Threading;\nusing System.Threading.Tasks;\nusing System.Timers;\n\nnamespace Stardrop.Utilities\n{\n    internal class NexusWebsocket\n    {\n        private readonly Uri ssoWebsocketURI = new(\"wss://sso.nexusmods.com\");\n        private readonly string connectionUUID = Guid.NewGuid().ToString();\n        private readonly string connectionSlug = \"stardrop\";\n\n        internal readonly string ssoUrl;\n\n        private ClientWebSocket? _socket;\n        private System.Timers.Timer? _pingTimer;\n        private bool _hasResolved;\n\n        public NexusWebsocket()\n        {\n            this.ssoUrl = $\"https://www.nexusmods.com/sso?id={connectionUUID}&application={connectionSlug}\";\n        }\n\n        public async Task<NexusConnectionResult> ConnectAsync(CancellationToken cancellationToken = default)\n        {\n            var result = new NexusConnectionResult();\n            _socket = new ClientWebSocket();\n\n            try\n            {\n                await _socket.ConnectAsync(ssoWebsocketURI, cancellationToken);\n\n                var initialData = new\n                {\n                    id = connectionUUID,\n                    token = (string?)null,\n                    protocol = 2\n                };\n                string json = JsonSerializer.Serialize(initialData);\n                var bytes = Encoding.UTF8.GetBytes(json);\n                await _socket.SendAsync(\n                    new ArraySegment<byte>(bytes), WebSocketMessageType.Text, true, cancellationToken\n                );\n\n                // ping every 30 seconds as requested by docs\n                _pingTimer = new System.Timers.Timer(30000);\n                _pingTimer.Elapsed += async (_, __) =>\n                {\n                    if (_socket?.State == WebSocketState.Open)\n                    {\n                        try\n                        {\n                            await _socket.SendAsync(\n                                new ArraySegment<byte>(Array.Empty<byte>()), WebSocketMessageType.Text, true, CancellationToken.None\n                            );\n                        }\n                        catch\n                        {\n                            _pingTimer?.Stop();\n                        }\n                    }\n                    else\n                    {\n                        _pingTimer?.Stop();\n                    }\n                };\n                _pingTimer.AutoReset = true;\n                _pingTimer.Start();\n\n                // Receive data\n                var buffer = new byte[4096];\n                while (_socket.State == WebSocketState.Open && !cancellationToken.IsCancellationRequested)\n                {\n                    var recv = await _socket.ReceiveAsync(\n                        new ArraySegment<byte>(buffer), cancellationToken\n                    );\n                    if (recv.MessageType == WebSocketMessageType.Close) break;\n\n                    var msg = Encoding.UTF8.GetString(buffer, 0, recv.Count);\n                    Program.helper.Log($\"[Nexus SSO] Received data: {msg}\", Helper.Status.Debug);\n\n                    var response = JsonSerializer.Deserialize<WebsocketResponse>(msg);\n                    if (response != null && response.Success && response.Data != null)\n                    {\n                        // ignore ConnectionToken\n                        if (response.Data.ConnectionToken != null && response.Data.ApiKey == null)\n                        {\n                            continue;\n                        }\n\n                        result.Message = \"Successfully obtained API key\";\n                        result.ApiKey = response.Data.ApiKey;\n                        _hasResolved = true;\n                        await _socket.CloseAsync(\n                            WebSocketCloseStatus.NormalClosure, \"got key\", CancellationToken.None\n                        );\n                        break;\n                    }\n                    else\n                    {\n                        result.Error = \"Received invalid message\";\n                        _hasResolved = true;\n                        await _socket.CloseAsync(\n                            WebSocketCloseStatus.NormalClosure,\n                            \"invalid\",\n                            CancellationToken.None\n                        );\n                        break;\n                    }\n                }\n            }\n            catch (Exception ex)\n            {\n                Program.helper.Log($\"[Nexus SSO] Exception: {ex}\", Helper.Status.Debug);\n                if (!_hasResolved)\n                {\n                    result.Error = ex.Message;\n                    _hasResolved = true;\n                }\n            }\n            finally\n            {\n                _pingTimer?.Stop();\n                if (_socket?.State == WebSocketState.Open)\n                {\n                    await _socket.CloseAsync(\n                        WebSocketCloseStatus.NormalClosure, \"shutdown\", CancellationToken.None\n                    );\n                }\n                _socket?.Dispose();\n            }\n\n            return result;\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Utilities/Pathing.cs",
    "content": "﻿using System;\nusing System.IO;\nusing System.Runtime.InteropServices;\n\nnamespace Stardrop.Utilities\n{\n    public static class Pathing\n    {\n        internal static string defaultGamePath;\n        internal static string defaultModPath;\n        internal static string defaultHomePath;\n\n        internal static void SetHomePath(string homePath)\n        {\n            defaultHomePath = Path.Combine(homePath, \"Stardrop\", \"Data\");\n        }\n\n        internal static void SetSmapiPath(string smapiPath, bool useDefaultModPath = false)\n        {\n            if (smapiPath is not null)\n            {\n                defaultGamePath = smapiPath;\n\n                if (useDefaultModPath)\n                {\n                    defaultModPath = Path.Combine(smapiPath, \"Mods\");\n                }\n            }\n        }\n\n        internal static void SetModPath(string modPath)\n        {\n            if (modPath is not null)\n            {\n                defaultModPath = modPath;\n            }\n        }\n\n        internal static string GetLogFolderPath()\n        {\n            return Path.Combine(defaultHomePath, \"Logs\");\n        }\n\n        internal static string GetSettingsPath()\n        {\n            return Path.Combine(defaultHomePath, \"Settings.json\");\n        }\n\n        public static string GetProfilesFolderPath()\n        {\n            return Path.Combine(defaultHomePath, \"Profiles\");\n        }\n\n        public static string GetSelectedModsFolderPath()\n        {\n            return Path.Combine(defaultHomePath, \"Selected Mods\");\n        }\n\n        public static string GetSmapiPath()\n        {\n            return Path.Combine(defaultGamePath, RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? \"StardewModdingAPI.exe\" : \"StardewModdingAPI.dll\");\n        }\n\n        internal static string GetSmapiLogFolderPath()\n        {\n            if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))\n            {\n                return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), \".config\", \"StardewValley\", \"ErrorLogs\");\n            }\n\n            return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), \"StardewValley\", \"ErrorLogs\");\n        }\n\n        public static string GetCacheFolderPath()\n        {\n            return Path.Combine(defaultHomePath, \"Cache\");\n        }\n\n        public static string GetVersionCachePath()\n        {\n            return Path.Combine(GetCacheFolderPath(), \"Versions.json\");\n        }\n\n        internal static string GetKeyCachePath()\n        {\n            return Path.Combine(GetCacheFolderPath(), \"Keys.json\");\n        }\n\n        internal static string GetDataCachePath()\n        {\n            return Path.Combine(GetCacheFolderPath(), \"Data.json\");\n        }\n\n        public static string GetNotionCachePath()\n        {\n            return Path.Combine(GetCacheFolderPath(), \"Notion.json\");\n        }\n\n        public static string GetLinksCachePath()\n        {\n            return Path.Combine(GetCacheFolderPath(), \"Links.json\");\n        }\n\n        public static string GetNexusPath()\n        {\n            return Path.Combine(defaultHomePath, \"Nexus\");\n        }\n\n        public static string GetThumbnailsPath()\n        {\n            return Path.Combine(defaultHomePath, \"Thumbnails\", \"Nexus\");\n        }\n\n        public static string GetSmapiUpgradeFolderPath()\n        {\n            return Path.Combine(defaultHomePath, \"SMAPI\");\n        }\n    }\n}"
  },
  {
    "path": "Stardrop/Utilities/SimpleObscure.cs",
    "content": "﻿using System.IO;\nusing System.Security.Cryptography;\n\nnamespace Stardrop.Utilities\n{\n    internal class SimpleObscure\n    {\n        internal byte[] Key { get; set; }\n        internal byte[] Vector { get; set; }\n\n        public SimpleObscure()\n        {\n            using (Aes aes = Aes.Create())\n            {\n                Key = aes.Key;\n                Vector = aes.IV;\n            }\n        }\n\n        internal static byte[] Encrypt(string plainText, byte[] Key, byte[] IV)\n        {\n            byte[] encrypted;\n\n            using (Aes aes = Aes.Create())\n            {\n                aes.Key = Key;\n                aes.IV = IV;\n\n                // Create an encryptor to perform the stream transform.\n                ICryptoTransform encryptor = aes.CreateEncryptor(aes.Key, aes.IV);\n\n                // Create the streams used for encryption.\n                using (MemoryStream msEncrypt = new MemoryStream())\n                {\n                    using (CryptoStream csEncrypt = new CryptoStream(msEncrypt, encryptor, CryptoStreamMode.Write))\n                    {\n                        using (StreamWriter swEncrypt = new StreamWriter(csEncrypt))\n                        {\n                            swEncrypt.Write(plainText);\n                        }\n                        encrypted = msEncrypt.ToArray();\n                    }\n                }\n            }\n\n            return encrypted;\n        }\n\n        internal static string Decrypt(byte[] cipherText, byte[] Key, byte[] IV)\n        {\n            string plaintext = null;\n\n            using (Aes aes = Aes.Create())\n            {\n                aes.Key = Key;\n                aes.IV = IV;\n\n                // Create a decryptor to perform the stream transform.\n                ICryptoTransform decryptor = aes.CreateDecryptor(aes.Key, aes.IV);\n\n                // Create the streams used for decryption.\n                using (MemoryStream msDecrypt = new MemoryStream(cipherText))\n                {\n                    using (CryptoStream csDecrypt = new CryptoStream(msDecrypt, decryptor, CryptoStreamMode.Read))\n                    {\n                        using (StreamReader srDecrypt = new StreamReader(csDecrypt))\n                        {\n                            plaintext = srDecrypt.ReadToEnd();\n                        }\n                    }\n                }\n            }\n\n            return plaintext;\n        }\n    }\n}"
  },
  {
    "path": "Stardrop/Utilities/Translation.cs",
    "content": "﻿using System;\nusing System.Collections.Generic;\nusing System.ComponentModel;\nusing System.IO;\nusing System.Linq;\nusing System.Text.Json;\n\nnamespace Stardrop.Utilities\n{\n    internal class Translation : INotifyPropertyChanged\n    {\n        public enum Language\n        {\n            English,\n            Chinese,\n            French,\n            German,\n            Hungarian,\n            Italian,\n            Japanese,\n            Korean,\n            Portuguese,\n            Russian,\n            Spanish,\n            Thai,\n            Turkish,\n            Ukrainian\n        }\n\n        public enum LanguageAbbreviation\n        {\n            @default,\n            zh,\n            fr,\n            de,\n            hu,\n            it,\n            ja,\n            ko,\n            pt,\n            ru,\n            es,\n            th,\n            tr,\n            uk\n        }\n        public Dictionary<Language, LanguageAbbreviation> LanguageNameToAbbreviations = new();\n        public Dictionary<LanguageAbbreviation, Language> AbbreviationsToLanguageName = new();\n\n        private Language _selectedLanguage = Language.English;\n        private Dictionary<LanguageAbbreviation, Dictionary<string, string>> _languageTranslations = new();\n        private const string IndexerName = \"Item\";\n        private const string IndexerArrayName = \"Item[]\";\n\n\n        public Translation()\n        {\n            int index = 0;\n            foreach (Language language in Enum.GetValues(typeof(Language)))\n            {\n                LanguageNameToAbbreviations[language] = (LanguageAbbreviation)index;\n                AbbreviationsToLanguageName[(LanguageAbbreviation)index] = language;\n\n                index++;\n            }\n        }\n\n        public string GetLanguageFromAbbreviation(string abbreviation)\n        {\n            if (Enum.TryParse(typeof(LanguageAbbreviation), abbreviation, out var languageAbbreviation))\n            {\n                if (AbbreviationsToLanguageName.ContainsKey((LanguageAbbreviation)languageAbbreviation))\n                {\n                    return AbbreviationsToLanguageName[(LanguageAbbreviation)languageAbbreviation].ToString();\n                }\n            }\n\n            return Language.English.ToString();\n        }\n\n        public Language GetLanguage(string language)\n        {\n            if (Enum.TryParse(typeof(Language), language, out var parsedLanguage))\n            {\n                return (Language)parsedLanguage;\n            }\n\n            return Language.English;\n        }\n\n        public void SetLanguage(string language)\n        {\n            if (Enum.TryParse(typeof(Language), language, out var parsedLanguage))\n            {\n                SetLanguage((Language)parsedLanguage);\n            }\n        }\n\n        public void SetLanguage(Language language)\n        {\n            _selectedLanguage = language;\n\n            Invalidate();\n        }\n\n        public void LoadTranslations()\n        {\n            // Load the languages\n            foreach (string fileFullName in Directory.EnumerateFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, \"i18n\"), \"*.json\"))\n            {\n                try\n                {\n                    var fileName = Path.GetFileNameWithoutExtension(fileFullName);\n                    if (Enum.TryParse(typeof(LanguageAbbreviation), fileName, out var language))\n                    {\n                        _languageTranslations[(LanguageAbbreviation)language] = JsonSerializer.Deserialize<Dictionary<string, string>>(File.ReadAllText(fileFullName), new JsonSerializerOptions { AllowTrailingCommas = true, ReadCommentHandling = JsonCommentHandling.Skip, PropertyNameCaseInsensitive = true });\n                        Program.helper.Log($\"Loaded language {Path.GetFileNameWithoutExtension(fileFullName)}\", Helper.Status.Debug);\n                    }\n                }\n                catch (Exception ex)\n                {\n                    Program.helper.Log($\"Unable to load translation at {Path.GetFileNameWithoutExtension(fileFullName)}: {ex}\", Helper.Status.Warning);\n                }\n            }\n        }\n\n        public void LoadTranslations(Language language)\n        {\n            // Set the language\n            SetLanguage(language);\n\n            LoadTranslations();\n        }\n\n        public List<Language> GetAvailableTranslations()\n        {\n            List<Language> availableLanguages = new();\n            foreach (var abbreviation in _languageTranslations.Keys.Where(l => AbbreviationsToLanguageName.ContainsKey(l)))\n            {\n                availableLanguages.Add(AbbreviationsToLanguageName[abbreviation]);\n            }\n\n            return availableLanguages;\n        }\n\n        public string Get(string key)\n        {\n            var languageAbbreviation = LanguageNameToAbbreviations[_selectedLanguage];\n            if (_languageTranslations.ContainsKey(languageAbbreviation) && _languageTranslations[languageAbbreviation].ContainsKey(key))\n            {\n                return _languageTranslations[languageAbbreviation][key];\n            }\n            else if (_languageTranslations.ContainsKey(LanguageAbbreviation.@default) && _languageTranslations[LanguageAbbreviation.@default].ContainsKey(key))\n            {\n                return _languageTranslations[LanguageAbbreviation.@default][key];\n            }\n\n            return $\"(No translation provided for key {key})\";\n        }\n\n        public string this[string key]\n        {\n            get\n            {\n                return Get(key);\n            }\n        }\n\n        public event PropertyChangedEventHandler PropertyChanged;\n        public void Invalidate()\n        {\n            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(IndexerName));\n            PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(IndexerArrayName));\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/ViewLocator.cs",
    "content": "using Avalonia.Controls;\nusing Avalonia.Controls.Templates;\nusing Stardrop.ViewModels;\nusing System;\n\nnamespace Stardrop\n{\n    public class ViewLocator : IDataTemplate\n    {\n        public IControl Build(object data)\n        {\n            var name = data.GetType().FullName!.Replace(\"ViewModel\", \"View\");\n            var type = Type.GetType(name);\n\n            if (type != null)\n            {\n                return (Control)Activator.CreateInstance(type)!;\n            }\n            else\n            {\n                return new TextBlock { Text = \"Not Found: \" + name };\n            }\n        }\n\n        public bool Match(object data)\n        {\n            return data is ViewModelBase;\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/ViewModels/DownloadPanelViewModel.cs",
    "content": "﻿using Avalonia.Controls;\nusing DynamicData;\nusing DynamicData.Aggregation;\nusing DynamicData.Alias;\nusing DynamicData.Binding;\nusing ReactiveUI;\nusing Stardrop.Models.Data;\nusing Stardrop.Utilities.External;\nusing System;\nusing System.Collections.ObjectModel;\nusing System.Linq;\n\nnamespace Stardrop.ViewModels\n{\n    public class DownloadPanelViewModel : ViewModelBase\n    {\n        private ObservableCollection<ModDownloadViewModel> _downloads = new();\n        public ObservableCollection<ModDownloadViewModel> Downloads { get => _downloads; set => this.RaiseAndSetIfChanged(ref _downloads, value); }        \n\n        public IObservable<int> InProgressDownloads { get; init; }\n\n        public DownloadPanelViewModel(NexusClient? nexusClient)\n        {\n            Nexus.ClientChanged += NexusClientChanged;\n            if (nexusClient is not null)\n            {\n                RegisterEventHandlers(nexusClient);\n            }\n\n            // Count failed and canceled downloads toward this value, because those still need to be \n            // handled in some way by the user\n            InProgressDownloads = Downloads\n                .ToObservableChangeSet(t => t.ModUri)\n                .AutoRefresh(x => x.DownloadStatus, scheduler: RxApp.MainThreadScheduler)\n                .Filter(x => x.DownloadStatus != ModDownloadStatus.Successful)\n                .Count();\n        }\n\n        private void NexusClientChanged(NexusClient? oldClient, NexusClient? newClient)\n        {\n            if (oldClient is not null)\n            {\n                // Cancel all downloads and clear the dictionary, so we don't have zombie downloads from an old client lingering\n                foreach (var download in Downloads)\n                {\n                    // Trigger the cancel command, and ignore any return values (as it has none)\n                    download.CancelCommand.Execute().Subscribe();\n                }\n                ClearEventHandlers(oldClient);\n                Downloads.Clear();\n            }\n            if (newClient is not null)\n            {\n                RegisterEventHandlers(newClient);\n            }\n        }\n\n        private void RegisterEventHandlers(NexusClient nexusClient)\n        {\n            nexusClient.DownloadStarted += DownloadStarted;\n            nexusClient.DownloadProgressChanged += DownloadProgressChanged;\n            nexusClient.DownloadCompleted += DownloadCompleted;\n            nexusClient.DownloadFailed += DownloadFailed;\n        }\n\n        private void ClearEventHandlers(NexusClient nexusClient)\n        {\n            nexusClient.DownloadStarted -= DownloadStarted;\n            nexusClient.DownloadProgressChanged -= DownloadProgressChanged;\n            nexusClient.DownloadCompleted -= DownloadCompleted;\n            nexusClient.DownloadFailed -= DownloadFailed;\n        }\n\n        private void DownloadStarted(object? sender, ModDownloadStartedEventArgs e)\n        {\n            var existingDownload = Downloads.FirstOrDefault(x => x.ModUri == e.Uri);\n            if (existingDownload is not null)\n            {\n                // If the user is trying to download the same file twice, it's *probably* because they\n                // want to retry a failed download.\n                // But just in case, check to see if the existing download is still in-progress. If it is, do nothing.\n                // We don't want to stop a user's 95% download because they accidentally hit the \"download again please\" button!\n                if (existingDownload.DownloadStatus == ModDownloadStatus.NotStarted\n                    || existingDownload.DownloadStatus == ModDownloadStatus.InProgress)\n                {\n                    return;\n                }\n\n                // If it does exist, and isn't in a progress state, they're probably trying to redownload a failed download.\n                // Since we use the URI as our unique ID, we shouldn't have two items with the same URI in the list,\n                // so clear out the old one.\n                Downloads.Remove(existingDownload);\n            }\n\n            var downloadVM = new ModDownloadViewModel(e.Uri, e.Name, e.Size, e.DownloadCancellationSource);\n            downloadVM.RemovalRequested += DownloadRemovalRequested;\n            Downloads.Add(downloadVM);\n        }\n\n        private void DownloadProgressChanged(object? sender, ModDownloadProgressEventArgs e)\n        {\n            var download = Downloads.SingleOrDefault(x => x.ModUri == e.Uri);\n            if (download is not null)\n            {\n                download.DownloadStatus = ModDownloadStatus.InProgress;\n                download.DownloadedBytes = e.TotalBytes;\n            }\n        }\n\n        private void DownloadCompleted(object? sender, ModDownloadCompletedEventArgs e)\n        {\n            var download = Downloads.SingleOrDefault(x => x.ModUri == e.Uri);\n            if (download is not null)\n            {\n                download.DownloadStatus = ModDownloadStatus.Successful;\n            }\n        }\n\n        private void DownloadFailed(object? sender, ModDownloadFailedEventArgs e)\n        {\n            var download = Downloads.SingleOrDefault(x => x.ModUri == e.Uri);\n            if (download is not null)\n            {\n                download.DownloadStatus = ModDownloadStatus.Failed;\n            }\n        }\n\n        private void DownloadRemovalRequested(object? sender, EventArgs _)\n        {\n            if (sender is not ModDownloadViewModel downloadVM)\n            {\n                return;\n            }\n\n            downloadVM.RemovalRequested -= DownloadRemovalRequested;\n            Downloads.Remove(downloadVM);\n        }\n\n        // Designer-only constructor\n        public DownloadPanelViewModel()\n        {\n            if (!Design.IsDesignMode)\n            {\n                throw new Exception(\"This constructor should only be called in design mode.\");\n            }\n\n            var inProgressDownload = new ModDownloadViewModel(\n                new Uri(\"https://www.fakeurl.com/testMod\"),\n                \"Fake Test Mod Download\",\n                1024 * 1024,\n                new()\n            );\n            inProgressDownload.DownloadStatus = ModDownloadStatus.InProgress;\n            inProgressDownload.DownloadedBytes = inProgressDownload.SizeBytes!.Value / 2;\n            Downloads.Add(inProgressDownload);\n\n            var succeededDownload = new ModDownloadViewModel(\n                new Uri(\"https://www.fakeSuccess.com\"),\n                \"Fake Succeeded Download\",\n                1234,\n                new()\n            );\n            succeededDownload.DownloadStatus = ModDownloadStatus.Successful;\n            succeededDownload.DownloadedBytes = 1234;\n            Downloads.Add(succeededDownload);\n\n            var failedDownload = new ModDownloadViewModel(\n                new Uri(\"https://www.differentFakeUrl.com\"),\n                \"Failed Fake Download\",\n                1024 * 1024 * 1024,\n                new()\n            );\n            failedDownload.DownloadedBytes = failedDownload.SizeBytes!.Value / 3;\n            failedDownload.DownloadStatus = ModDownloadStatus.Failed;\n            Downloads.Add(failedDownload);\n\n            var cancelledDownload = new ModDownloadViewModel(\n                new Uri(\"https://www.cancelledFake.com\"),\n                \"Cancelled Fake Download\",\n                1024 * 1024 * 5,\n                new()\n            );\n            cancelledDownload.DownloadedBytes = cancelledDownload.SizeBytes!.Value / 4;\n            cancelledDownload.DownloadStatus = ModDownloadStatus.Canceled;\n            Downloads.Add(cancelledDownload);\n\n            var indeterminateInProgressDownload = new ModDownloadViewModel(\n                new Uri(\"https://www.inProgressMystery.com\"),\n                \"In Progress Download of Unknown Size\",\n                null,\n                new()\n            );\n            indeterminateInProgressDownload.DownloadedBytes = 1024 * 1024 * 2;\n            indeterminateInProgressDownload.DownloadStatus = ModDownloadStatus.InProgress;\n            Downloads.Add(indeterminateInProgressDownload);\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/ViewModels/FlexibleOptionWindowViewModel.cs",
    "content": "using ReactiveUI;\n\nnamespace Stardrop.ViewModels\n{\n    public class FlexibleOptionWindowViewModel : ViewModelBase\n    {\n        private string _messageText;\n        public string MessageText { get { return _messageText; } set { this.RaiseAndSetIfChanged(ref _messageText, value); } }\n        private string _firstButtonText;\n        public string FirstButtonText { get { return _firstButtonText; } set { this.RaiseAndSetIfChanged(ref _firstButtonText, value); } }\n        private string _secondButtonText;\n        public string SecondButtonText { get { return _secondButtonText; } set { this.RaiseAndSetIfChanged(ref _secondButtonText, value); } }\n        private string _thirdButtonText;\n        public string ThirdButtonText { get { return _thirdButtonText; } set { this.RaiseAndSetIfChanged(ref _thirdButtonText, value); } }\n\n        private bool _isFirstButtonVisible;\n        public bool IsFirstButtonVisible { get { return _isFirstButtonVisible; } set { this.RaiseAndSetIfChanged(ref _isFirstButtonVisible, value); } }\n        private bool _isSecondButtonVisible;\n        public bool IsSecondButtonVisible { get { return _isSecondButtonVisible; } set { this.RaiseAndSetIfChanged(ref _isSecondButtonVisible, value); } }\n        private bool _isThirdButtonVisible;\n        public bool IsThirdButtonVisible { get { return _isThirdButtonVisible; } set { this.RaiseAndSetIfChanged(ref _isThirdButtonVisible, value); } }\n    }\n}\n"
  },
  {
    "path": "Stardrop/ViewModels/MainWindowViewModel.cs",
    "content": "using Avalonia.Collections;\nusing Avalonia.Controls;\nusing DynamicData;\nusing Json.More;\nusing ReactiveUI;\nusing Stardrop.Models;\nusing Stardrop.Models.Data;\nusing Stardrop.Models.Data.Enums;\nusing Stardrop.Models.SMAPI;\nusing Stardrop.Utilities;\nusing Stardrop.Utilities.External;\nusing Stardrop.Utilities.Internal;\nusing System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.ComponentModel;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text.Json;\n\nnamespace Stardrop.ViewModels\n{\n    public class MainWindowViewModel : ViewModelBase\n    {\n        private string ChromeHint { get; set; } = \"NoChrome\";\n        private bool HasSystemDecorations { get; set; } = true;\n        private bool ShowTitle { get; set; } = true;\n        private bool ShowMainMenu { get; set; } = true;\n        private bool ShowWindowMenu { get; set; } = true;\n\n        private DataGridPathGroupDescription _modPathGrouping = new DataGridPathGroupDescription(nameof(Mod.Path));\n        private DataGridPathGroupDescription _rootPathGrouping = new DataGridPathGroupDescription(nameof(Mod.RootPath));\n        private DataGridPathGroupDescription _frameworkGrouping = new DataGridPathGroupDescription(nameof(Mod.FrameworkID));\n\n        private string _dragOverColor = \"#ff9f2a\";\n        public string DragOverColor { get { return _dragOverColor; } set { this.RaiseAndSetIfChanged(ref _dragOverColor, value); } }\n        private bool _isLocked;\n        public bool IsLocked { get { return _isLocked; } set { this.RaiseAndSetIfChanged(ref _isLocked, value); } }\n        private bool _isCheckingForUpdates;\n        public bool IsCheckingForUpdates { get { return _isCheckingForUpdates; } set { this.RaiseAndSetIfChanged(ref _isCheckingForUpdates, value); } }\n        public ObservableCollection<Mod> Mods { get; set; }\n        private int _enabledModCount;\n        public int EnabledModCount { get { return _enabledModCount; } set { this.RaiseAndSetIfChanged(ref _enabledModCount, value); } }\n        private int _actualModCount;\n        public int ActualModCount { get { return _actualModCount; } set { this.RaiseAndSetIfChanged(ref _actualModCount, value); } }\n        public DataGridCollectionView DataView { get; set; }\n\n        private DisplayFilter _disabledModFilter;\n        public DisplayFilter DisabledModFilter { get { return _disabledModFilter; } set { _disabledModFilter = value; UpdateFilter(); } }\n\n        private bool _showUpdatableMods;\n        public bool ShowUpdatableMods { get { return _showUpdatableMods; } set { _showUpdatableMods = value; UpdateFilter(); } }\n        private bool _showEndorsements;\n        public bool ShowEndorsements { get { return _showEndorsements; } set { this.RaiseAndSetIfChanged(ref _showEndorsements, value); } }\n        private bool _showInstalls;\n        public bool ShowInstalls { get { return _showInstalls; } set { this.RaiseAndSetIfChanged(ref _showInstalls, value); } }\n        private string _filterText;\n        public string FilterText { get { return _filterText; } set { _filterText = value; UpdateFilter(); } }\n        private List<string> _columnFilter;\n        public List<string> ColumnFilter { get { return _columnFilter; } set { _columnFilter = value; UpdateFilter(); } }\n        private string _updateStatusText = Program.translation.Get(\"ui.main_window.button.update_status.generic\");\n        public string UpdateStatusText { get { return _updateStatusText; } set { this.RaiseAndSetIfChanged(ref _updateStatusText, value); } }\n        private string _downloadsButtonText;\n        public string DownloadsButtonText { get => _downloadsButtonText; set => this.RaiseAndSetIfChanged(ref _downloadsButtonText, value); }\n        private int _modsWithCachedUpdates;\n        public int ModsWithCachedUpdates { get { return _modsWithCachedUpdates; } set { this.RaiseAndSetIfChanged(ref _modsWithCachedUpdates, value); } }\n        public string Version { get; set; }\n\n        private string _nexusStatus = String.Concat(\"Nexus Mods: \", Program.translation.Get(\"internal.disconnected\"));\n        public string NexusStatus { get { return _nexusStatus; } set { this.RaiseAndSetIfChanged(ref _nexusStatus, String.Concat(\"Nexus Mods: \", value)); } }\n\n        private string _nexusLimits;\n        public string NexusLimits { get { return _nexusLimits; } set { this.RaiseAndSetIfChanged(ref _nexusLimits, value); } }\n        private string _smapiVersion;\n        public string SmapiVersion { get { return String.IsNullOrEmpty(_smapiVersion) ? Program.translation.Get(\"ui.main_window.labels.unknown_SMAPI\") : $\"v{_smapiVersion}\"; } set { this.RaiseAndSetIfChanged(ref _smapiVersion, value); } }\n\n        public bool ShowSaveProfileChanges { get { return _showSaveProfileChanges; } set { this.RaiseAndSetIfChanged(ref _showSaveProfileChanges, value); } }\n        private bool _showSaveProfileChanges;\n        public bool AreModGroupsEnabled { get { return _areModGroupsEnabled; } set { this.RaiseAndSetIfChanged(ref _areModGroupsEnabled, value); } }\n        private bool _areModGroupsEnabled = Program.settings.ModGroupingMethod != ModGrouping.None;\n        public bool ShowModThumbnails { get { return _showModThumbnails; } set { this.RaiseAndSetIfChanged(ref _showModThumbnails, value); } }\n        private bool _showModThumbnails = Program.settings.ShowModThumbnails;\n        public string ModGroupsStateButtonText { get { return _modGroupsStateButtonText; } set { this.RaiseAndSetIfChanged(ref _modGroupsStateButtonText, value); } }\n        private string _modGroupsStateButtonText = Program.settings.ModGroupingMethod != ModGrouping.None ? Program.translation.Get(\"ui.main_window.buttons.mod_groups_state.collapse\") : Program.translation.Get(\"ui.main_window.buttons.mod_groups_state.expand\");\n\n        public MainWindowViewModel(string modsFilePath, string version)\n        {\n            DiscoverMods(modsFilePath);\n            Version = $\"v{version}\";\n            SmapiVersion = Program.settings.GameDetails?.SmapiVersion;\n\n            // Create data view\n            DataView = new DataGridCollectionView(Mods, isDataSorted: false, isDataInGroupOrder: false);\n            DataView.SortDescriptions.CollectionChanged += DataViewSortDescription_CollectionChanged;\n\n            UpdateFilter();\n\n            DataView.SortDescriptions.Add(DataGridSortDescription.FromPath(nameof(Mod.Name), ListSortDirection.Ascending));\n\n            // Do OS specific setup\n            if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))\n            {\n                ChromeHint = \"Default\";\n                ShowMainMenu = false;\n                ShowWindowMenu = false;\n            }\n            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))\n            {\n                ChromeHint = \"Default\";\n                ShowWindowMenu = false;\n                ShowTitle = false;\n            }\n        }\n\n        public void OpenBrowser(string url)\n        {\n            if (String.IsNullOrEmpty(url))\n            {\n                return;\n            }\n\n            try\n            {\n                using Process process = Process.Start(new ProcessStartInfo\n                {\n                    FileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? url :\n                        RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? \"open\" : \"xdg-open\",\n                    Arguments = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? \"\" : $\"\\\"{url}\\\"\",\n                    CreateNoWindow = true,\n                    UseShellExecute = RuntimeInformation.IsOSPlatform(OSPlatform.Windows)\n                });\n            }\n            catch (Exception ex)\n            {\n                Program.helper.Log($\"Failed to utilize OpenBrowser with the url ({url}): {ex}\");\n            }\n        }\n\n        public void ChangeColumnVisibility(MenuItem column)\n        {\n            if (column is null)\n            {\n                return;\n            }\n\n            var modGrid = column.FindControl<DataGrid>(\"modGrid\");\n            if (modGrid is null)\n            {\n                return;\n            }\n\n            if (column.Classes.Contains(\"ColumnInactive\"))\n            {\n                SetColumnVisibility(column, modGrid, true);\n            }\n            else\n            {\n                SetColumnVisibility(column, modGrid, false);\n            }\n        }\n\n        public void SetColumnVisibility(MenuItem column, DataGrid modGrid, bool isActive)\n        {\n            // Get the local data\n            ClientData localDataCache = new ClientData();\n            if (File.Exists(Pathing.GetDataCachePath()))\n            {\n                localDataCache = JsonSerializer.Deserialize<ClientData>(File.ReadAllText(Pathing.GetDataCachePath()), new JsonSerializerOptions { AllowTrailingCommas = true });\n            }\n\n            if (isActive)\n            {\n                if (modGrid.Columns.Any(c => c.Header is TextBlock textBlock && textBlock.Text == (string)column.Header))\n                {\n                    column.Classes.Remove(\"ColumnInactive\");\n                    column.Classes.Add(\"ColumnActive\");\n\n                    modGrid.Columns.First(c => c.Header is TextBlock textBlock && textBlock.Text == (string)column.Header).IsVisible = true;\n                    localDataCache.ColumnActiveStates[(string)column.Header] = true;\n                }\n            }\n            else\n            {\n                if (modGrid.Columns.Any(c => c.Header is TextBlock textBlock && textBlock.Text == (string)column.Header))\n                {\n                    column.Classes.Remove(\"ColumnActive\");\n                    column.Classes.Add(\"ColumnInactive\");\n\n                    modGrid.Columns.First(c => c.Header is TextBlock textBlock && textBlock.Text == (string)column.Header).IsVisible = false;\n                    localDataCache.ColumnActiveStates[(string)column.Header] = false;\n                }\n            }\n\n            // Cache the local data\n            File.WriteAllText(Pathing.GetDataCachePath(), JsonSerializer.Serialize(localDataCache, new JsonSerializerOptions() { WriteIndented = true }));\n        }\n\n        public bool ParentFolderContainsPeriod(string oldestAncestorPath, DirectoryInfo? directoryInfo)\n        {\n            if (directoryInfo is null)\n            {\n                return false;\n            }\n            else if (directoryInfo.Name[0] == '.')\n            {\n                return true;\n            }\n\n            var ancestorFolder = directoryInfo.Parent;\n            while (ancestorFolder is not null && !ancestorFolder.FullName.Equals(oldestAncestorPath, StringComparison.OrdinalIgnoreCase))\n            {\n                if (ancestorFolder.Name[0] == '.')\n                {\n                    return true;\n                }\n\n                ancestorFolder = ancestorFolder.Parent;\n            }\n\n            return false;\n        }\n\n        public List<FileInfo> GetManifestFiles(DirectoryInfo modDirectory)\n        {\n            List<FileInfo> manifests = new List<FileInfo>();\n            foreach (var directory in modDirectory.EnumerateDirectories())\n            {\n                try\n                {\n                    var localManifest = directory.EnumerateFiles().FirstOrDefault(file => file.Name.Equals(\"manifest.json\", StringComparison.OrdinalIgnoreCase));\n                    if (localManifest is null)\n                    {\n                        manifests.AddRange(GetManifestFiles(directory));\n                    }\n                    else\n                    {\n                        manifests.Add(localManifest);\n                    }\n                }\n                catch (Exception ex)\n                {\n                    Program.helper.Log($\"There was an error when attempting to get the manifest.json within the directory ({(directory is null ? String.Empty : directory.FullName)}): {ex}\", Helper.Status.Alert);\n                }\n            }\n\n            return manifests;\n        }\n\n        public void DiscoverMods(string modsFilePath)\n        {\n            if (Mods is null)\n            {\n                Mods = new ObservableCollection<Mod>();\n            }\n            Mods.Clear();\n\n            if (modsFilePath is null || !Directory.Exists(modsFilePath))\n            {\n                return;\n            }\n\n            // Get cached key data\n            List<ModKeyInfo> modKeysCache = new List<ModKeyInfo>();\n            if (File.Exists(Pathing.GetKeyCachePath()))\n            {\n                try\n                {\n                    modKeysCache = JsonSerializer.Deserialize<List<ModKeyInfo>>(File.ReadAllText(Pathing.GetKeyCachePath()), new JsonSerializerOptions { AllowTrailingCommas = true });\n                }\n                catch (Exception ex)\n                {\n                    Program.helper.Log($\"Failed to parse cached mod keys: {ex}\", Helper.Status.Alert);\n                }\n            }\n\n            // Get the local data\n            ClientData localDataCache = new ClientData();\n            if (File.Exists(Pathing.GetDataCachePath()))\n            {\n                try\n                {\n                    localDataCache = JsonSerializer.Deserialize<ClientData>(File.ReadAllText(Pathing.GetDataCachePath()), new JsonSerializerOptions { AllowTrailingCommas = true });\n                }\n                catch (Exception ex)\n                {\n                    Program.helper.Log($\"Failed to parse client data: {ex}\", Helper.Status.Alert);\n                }\n            }\n\n            foreach (var fileInfo in GetManifestFiles(new DirectoryInfo(modsFilePath)))\n            {\n                if (fileInfo.DirectoryName is null || (Program.settings.IgnoreHiddenFolders && ParentFolderContainsPeriod(modsFilePath, fileInfo.Directory)))\n                {\n                    continue;\n                }\n\n                try\n                {\n                    var manifest = ManifestParser.GetData(File.ReadAllText(fileInfo.FullName));\n                    if (manifest is null || String.IsNullOrEmpty(manifest.UniqueID))\n                    {\n                        Program.helper.Log($\"The manifest.json was empty or not deserializable from {fileInfo.DirectoryName}\", Helper.Status.Alert);\n                        continue;\n                    }\n\n                    var mod = new Mod(manifest, fileInfo, manifest.UniqueID, manifest.Version, manifest.Name, manifest.Description, manifest.Author);\n                    if (manifest.ContentPackFor is not null && modKeysCache is not null)\n                    {\n                        var dependencyKey = modKeysCache.FirstOrDefault(m => m.UniqueId.Equals(manifest.ContentPackFor.UniqueID, StringComparison.OrdinalIgnoreCase));\n                        mod.FrameworkID = manifest.ContentPackFor.UniqueID;\n                        mod.Requirements.Add(new ManifestDependency(manifest.ContentPackFor.UniqueID, manifest.ContentPackFor.MinimumVersion, true) { Name = dependencyKey is null ? manifest.ContentPackFor.UniqueID : dependencyKey.Name });\n                    }\n                    if (manifest.Dependencies is not null && modKeysCache is not null)\n                    {\n                        foreach (var dependency in manifest.Dependencies)\n                        {\n                            if (mod.Requirements.Any(r => r.UniqueID.Equals(dependency.UniqueID, StringComparison.OrdinalIgnoreCase)))\n                            {\n                                continue;\n                            }\n\n                            var dependencyKey = modKeysCache.FirstOrDefault(m => m.UniqueId.Equals(dependency.UniqueID, StringComparison.OrdinalIgnoreCase));\n                            mod.Requirements.Add(new ManifestDependency(dependency.UniqueID, dependency.MinimumVersion, dependency.IsRequired) { Name = dependencyKey is null ? dependency.UniqueID : dependencyKey.Name });\n                        }\n                    }\n                    if (modKeysCache is not null && modKeysCache.Any(m => m.UniqueId.Equals(mod.UniqueId, StringComparison.OrdinalIgnoreCase)))\n                    {\n                        mod.ModPageUri = modKeysCache.First(m => m.UniqueId.Equals(mod.UniqueId, StringComparison.OrdinalIgnoreCase)).PageUrl;\n                    }\n\n                    if (localDataCache is not null && localDataCache.ModInstallData is not null && localDataCache.ModInstallData.Any(m => m.UniqueId.Equals(mod.UniqueId, StringComparison.OrdinalIgnoreCase)))\n                    {\n                        mod.InstallTimestamp = localDataCache.ModInstallData.First(m => m.UniqueId.Equals(mod.UniqueId, StringComparison.OrdinalIgnoreCase)).InstallTimestamp;\n                        mod.LastUpdateTimestamp = localDataCache.ModInstallData.First(m => m.UniqueId.Equals(mod.UniqueId, StringComparison.OrdinalIgnoreCase)).LastUpdateTimestamp;\n                    }\n\n                    // Check if any config file exists\n                    var configPath = Path.Combine(fileInfo.DirectoryName, \"config.json\");\n                    if (File.Exists(configPath) && new FileInfo(configPath) is FileInfo configInfo && configInfo is not null)\n                    {\n                        mod.Config = new Config() { UniqueId = mod.UniqueId, FilePath = configInfo.FullName, LastWriteTimeUtc = configInfo.LastWriteTimeUtc, Data = File.ReadAllText(configInfo.FullName) };\n                    }\n\n                    // Add or update the mod\n                    if (!Mods.Any(m => m.UniqueId.Equals(manifest.UniqueID, StringComparison.OrdinalIgnoreCase)))\n                    {\n                        Mods.Add(mod);\n                    }\n                    else if (Mods.FirstOrDefault(m => m.UniqueId.Equals(manifest.UniqueID, StringComparison.OrdinalIgnoreCase) && m.Version.CompareSortOrderTo(mod.Version) < 0) is Mod oldMod && oldMod is not null)\n                    {\n                        // Replace old mod with newer one\n                        int oldModIndex = Mods.IndexOf(Mods.First(m => m.UniqueId.Equals(manifest.UniqueID, StringComparison.OrdinalIgnoreCase) && m.Version.CompareSortOrderTo(mod.Version) < 0));\n                        Mods[oldModIndex] = mod;\n                    }\n                }\n                catch (Exception ex)\n                {\n                    Program.helper.Log($\"Unable to load the manifest.json from {fileInfo.DirectoryName}: {ex}\", Helper.Status.Alert);\n                }\n            }\n\n            if (Program.settings.ShowModThumbnails)\n            {\n                UpdateThumbnails();\n            }\n\n            // Update the local data\n            var modInstallData = new List<ModInstallData>();\n            foreach (var mod in Mods.Where(m => m is not null))\n            {\n                if (mod.InstallTimestamp is null)\n                {\n                    mod.InstallTimestamp = DateTime.Now;\n                }\n\n                modInstallData.Add(new ModInstallData() { UniqueId = mod.UniqueId, InstallTimestamp = mod.InstallTimestamp.Value, LastUpdateTimestamp = mod.LastUpdateTimestamp });\n            }\n            localDataCache.ModInstallData = modInstallData;\n\n            // Cache the local data\n            File.WriteAllText(Pathing.GetDataCachePath(), JsonSerializer.Serialize(localDataCache, new JsonSerializerOptions() { WriteIndented = true }));\n\n            EvaluateRequirements();\n            DiscoverConfigs(modsFilePath, useArchive: true);\n            HideRequiredMods();\n\n            ActualModCount = Mods.Count(m => !m.IsHidden);\n        }\n\n        public void HideRequiredMods()\n        {\n            var requiredModIds = new List<string> { \"SMAPI.ConsoleCommands\", \"SMAPI.ErrorHandler\", \"SMAPI.SaveBackup\" };\n            foreach (var mod in Mods.Where(m => requiredModIds.Any(id => id.Equals(m.UniqueId, StringComparison.OrdinalIgnoreCase))))\n            {\n                mod.IsHidden = true;\n                mod.IsEnabled = true;\n            }\n\n            // Update the EnabledModCount\n            EnabledModCount = Mods.Where(m => m.IsEnabled && !m.IsHidden).Count();\n\n            // Update data grid grouping\n            UpdateDataGridGrouping();\n        }\n\n        public void EvaluateRequirements()\n        {\n            // Get cached key data\n            List<ModKeyInfo> modKeysCache = new List<ModKeyInfo>();\n            if (File.Exists(Pathing.GetKeyCachePath()))\n            {\n                modKeysCache = JsonSerializer.Deserialize<List<ModKeyInfo>>(File.ReadAllText(Pathing.GetKeyCachePath()), new JsonSerializerOptions { AllowTrailingCommas = true });\n            }\n\n            // Flag any missing requirements\n            foreach (var mod in Mods)\n            {\n                try\n                {\n                    foreach (var requirement in mod.Requirements.Where(r => r.IsRequired))\n                    {\n                        if (!Mods.Any(m => m.UniqueId.Equals(requirement.UniqueID, StringComparison.OrdinalIgnoreCase)) || Mods.First(m => m.UniqueId.Equals(requirement.UniqueID, StringComparison.OrdinalIgnoreCase)) is Mod matchedMod && matchedMod.IsModOutdated(requirement.MinimumVersion))\n                        {\n                            requirement.IsMissing = true;\n\n                            if (modKeysCache is not null)\n                            {\n                                var dependencyKey = modKeysCache.FirstOrDefault(m => m.UniqueId.Equals(requirement.UniqueID, StringComparison.OrdinalIgnoreCase));\n                                requirement.Name = dependencyKey is null ? requirement.UniqueID : dependencyKey.Name;\n                            }\n                        }\n                    }\n\n                    mod.NotifyPropertyChanged(\"Requirements\");\n                    mod.NotifyPropertyChanged(\"MissingRequirements\");\n                }\n                catch (Exception ex)\n                {\n                    Program.helper.Log($\"Failed to check requirements for {mod.Name} due to the following error: {ex}\");\n                }\n            }\n        }\n\n        public List<FileInfo> GetConfigFiles(DirectoryInfo modDirectory)\n        {\n            List<FileInfo> configs = new List<FileInfo>();\n            foreach (var directory in modDirectory.EnumerateDirectories())\n            {\n                var localConfigs = directory.EnumerateFiles(\"config.json\");\n                if (localConfigs.Count() == 0)\n                {\n                    configs.AddRange(GetConfigFiles(directory));\n                    continue;\n                }\n\n                var localConfig = localConfigs.First();\n                if (localConfig.Directory is not null && localConfig.Directory.EnumerateFiles(\"manifest.json\", SearchOption.TopDirectoryOnly).Count() == 1)\n                {\n                    configs.Add(localConfig);\n                }\n            }\n\n            return configs;\n        }\n\n        public void DiscoverConfigs(string modsFilePath, bool useArchive = false)\n        {\n            if (modsFilePath is null || !Directory.Exists(modsFilePath))\n            {\n                return;\n            }\n\n            foreach (var fileInfo in GetConfigFiles(new DirectoryInfo(modsFilePath)))\n            {\n                if (fileInfo.DirectoryName is null || (Program.settings.IgnoreHiddenFolders && ParentFolderContainsPeriod(modsFilePath, fileInfo.Directory)))\n                {\n                    continue;\n                }\n\n                var mod = Mods.FirstOrDefault(m => m.ModFileInfo is not null && m.ModFileInfo.DirectoryName == fileInfo.DirectoryName);\n                if (mod is null)\n                {\n                    continue;\n                }\n                else if (useArchive && mod.Config is not null)\n                {\n                    if (fileInfo.LastWriteTimeUtc <= mod.Config.LastWriteTimeUtc)\n                    {\n                        continue;\n                    }\n\n                    mod.Config.Data = File.ReadAllText(fileInfo.FullName);\n                    mod.Config.LastWriteTimeUtc = fileInfo.LastWriteTimeUtc;\n                }\n                else\n                {\n                    mod.Config = new Config() { UniqueId = mod.UniqueId, FilePath = fileInfo.FullName, LastWriteTimeUtc = fileInfo.LastWriteTimeUtc, Data = File.ReadAllText(fileInfo.FullName) };\n                }\n            }\n        }\n\n        internal List<Config> GetPendingConfigUpdates(Profile profile, bool excludeMissingConfigs = false, bool useArchiveAsBase = false)\n        {\n            // Merge any existing preserved configs\n            List<Config> pendingConfigUpdates = new List<Config>();\n            foreach (var modId in profile.EnabledModIds.Select(id => id.ToLower()))\n            {\n                var mod = Mods.FirstOrDefault(m => m.UniqueId.Equals(modId, StringComparison.OrdinalIgnoreCase));\n                if (mod is null || mod.ModFileInfo is null)\n                {\n                    continue;\n                }\n\n                try\n                {\n                    if (profile.PreservedModConfigs.ContainsKey(modId))\n                    {\n                        // Write the archived config, if the current one doesn't exist\n                        if (mod.Config is null)\n                        {\n                            if (excludeMissingConfigs || String.IsNullOrEmpty(mod.ModFileInfo.DirectoryName))\n                            {\n                                continue;\n                            }\n\n                            mod.Config = new Config() { UniqueId = modId, FilePath = Path.Combine(mod.ModFileInfo.DirectoryName, \"config.json\"), Data = JsonTools.ParseDocumentToString(profile.PreservedModConfigs[modId]) };\n                            pendingConfigUpdates.Add(mod.Config);\n                        }\n                        else\n                        {\n                            // Merge the config\n                            var currentJson = mod.Config.Data;\n                            var archivedJson = JsonTools.ParseDocumentToString(profile.PreservedModConfigs[modId]);\n                            if (JsonDocumentEqualityComparer.Instance.Equals(JsonDocument.Parse(mod.Config.Data), profile.PreservedModConfigs[modId]) is false)\n                            {\n                                // JsonTools.Merge will preserve the originalJson values, but will add new properties from archivedJson\n                                string mergedJson = String.Empty;\n                                if (useArchiveAsBase is false)\n                                {\n                                    mergedJson = JsonTools.Merge(archivedJson, currentJson, false); ;\n                                }\n                                else\n                                {\n                                    mergedJson = JsonTools.Merge(currentJson, archivedJson, false);\n                                }\n\n                                // Apply the changes to the config file\n                                //Program.helper.Log($\"The mod {modId} does not have its current configuration preserved\\nCurrent:\\n{currentJson}\\nArchived:\\n{archivedJson}\", Helper.Status.Warning);\n                                pendingConfigUpdates.Add(new Config() { UniqueId = modId, FilePath = mod.Config.FilePath, Data = mergedJson });\n                            }\n                        }\n                    }\n                    else if (mod.Config is not null)\n                    {\n                        pendingConfigUpdates.Add(new Config() { UniqueId = modId, FilePath = mod.Config.FilePath, Data = mod.Config.Data });\n                    }\n                }\n                catch (Exception ex)\n                {\n                    Program.helper.Log($\"Failed to process config.json for mod {modId}: {ex}\", Helper.Status.Warning);\n                }\n            }\n\n            return pendingConfigUpdates;\n        }\n\n        internal async void UpdateEndorsements()\n        {\n            if (Nexus.Client is null)\n            {\n                return;\n            }\n\n            var endorsements = await Nexus.Client.GetEndorsements();\n            foreach (var mod in Mods.Where(m => m.HasUpdateKeys() && endorsements.Any(e => e.Id == m.NexusModId)))\n            {\n                mod.IsEndorsed = endorsements.First(e => e.Id == mod.NexusModId).IsEndorsed();\n            }\n        }\n\n        internal async void UpdateThumbnails()\n        {\n            // Get all existing thumbnails\n            IEnumerable<FileInfo> nexusModThumbnails = new List<FileInfo>();\n            var thumbnailDirectory = new DirectoryInfo(Pathing.GetThumbnailsPath());\n            if (thumbnailDirectory.Exists)\n            {\n                nexusModThumbnails = thumbnailDirectory.EnumerateFiles();\n            }\n\n            var modsWithWebpages = Mods.Where(m => m.NexusModId is not null).ToList();\n            foreach (var mod in modsWithWebpages)\n            {\n                var thumbnail = nexusModThumbnails.FirstOrDefault(t => mod.NexusModId is not null && Path.GetFileNameWithoutExtension(t.Name).Equals(mod.NexusModId.ToString(), StringComparison.OrdinalIgnoreCase));\n                if (thumbnail is not null)\n                {\n                    mod.NexusModThumbnailPath = thumbnail.FullName;\n                }\n                else if (Nexus.Client is not null && mod.NexusModThumbnailPath is null)\n                {\n                    mod.NexusModThumbnailPath = await Nexus.Client.DownloadThumbnail((int)mod.NexusModId);\n                }\n            }\n        }\n\n        internal void ReadModConfigs(Profile profile)\n        {\n            ReadModConfigs(profile, GetPendingConfigUpdates(profile));\n        }\n\n        internal void ReadModConfigs(Profile profile, List<Config> pendingConfigUpdates)\n        {\n            foreach (var configInfo in pendingConfigUpdates)\n            {\n                try\n                {\n                    profile.PreservedModConfigs[configInfo.UniqueId] = JsonDocument.Parse(configInfo.Data);\n                }\n                catch (Exception ex)\n                {\n                    Program.helper.Log($\"Failed to read config for the mod {configInfo.UniqueId} due to the following error:\\n{ex}\");\n                }\n            }\n        }\n\n        internal bool WriteModConfigs(Profile profile)\n        {\n            return WriteModConfigs(profile, GetPendingConfigUpdates(profile, useArchiveAsBase: true));\n        }\n\n        internal bool WriteModConfigs(Profile profile, List<Config> pendingConfigUpdates)\n        {\n            if (pendingConfigUpdates.Count == 0)\n            {\n                return false;\n            }\n\n            // Merge any existing preserved configs\n            foreach (var configInfo in pendingConfigUpdates.Where(c => profile.PreservedModConfigs.ContainsKey(c.UniqueId.ToLower())))\n            {\n                try\n                {\n                    var fileInfo = new FileInfo(configInfo.FilePath);\n                    if (!Directory.Exists(fileInfo.DirectoryName))\n                    {\n                        continue;\n                    }\n\n                    // Apply the changes to the config file\n                    File.WriteAllText(configInfo.FilePath, configInfo.Data);\n                }\n                catch (Exception ex)\n                {\n                    Program.helper.Log($\"Failed to write config for the mod {configInfo.UniqueId} due to the following error:\\n{ex}\");\n                }\n            }\n\n            return true;\n        }\n\n        public void EnableModsByProfile(Profile profile)\n        {\n            foreach (var mod in Mods)\n            {\n                mod.IsEnabled = false;\n                if (profile.EnabledModIds.Any(id => id.Equals(mod.UniqueId, StringComparison.OrdinalIgnoreCase)))\n                {\n                    mod.IsEnabled = true;\n                }\n            }\n            HideRequiredMods();\n\n            // Update the EnabledModCount\n            EnabledModCount = Mods.Where(m => m.IsEnabled && !m.IsHidden).Count();\n        }\n\n        public void ForceModState(Profile profile, List<Mod> mods, bool modEnableState = false)\n        {\n            foreach (var mod in Mods)\n            {\n                if (mods.Any(m => m.UniqueId.Equals(mod.UniqueId, StringComparison.OrdinalIgnoreCase)) is false)\n                {\n                    continue;\n                }\n                mod.IsEnabled = modEnableState;\n            }\n\n            // Update the EnabledModCount\n            EnabledModCount = Mods.Where(m => m.IsEnabled && !m.IsHidden).Count();\n        }\n\n        internal void UpdateDataGridGrouping()\n        {\n            if (DataView is not null)\n            {\n                DataGridPathGroupDescription? currentGroupingMethod = null;\n                switch (Program.settings.ModGroupingMethod)\n                {\n                    case ModGrouping.Folder:\n                        currentGroupingMethod = _modPathGrouping;\n                        break;\n                    case ModGrouping.FolderCondensed:\n                        currentGroupingMethod = _rootPathGrouping;\n                        break;\n                    case ModGrouping.ContentPack:\n                        currentGroupingMethod = _frameworkGrouping;\n                        break;\n                }\n\n                foreach (var grouping in DataView.GroupDescriptions.ToList())\n                {\n                    if (grouping == currentGroupingMethod)\n                    {\n                        continue;\n                    }\n\n                    DataView.GroupDescriptions.Remove(grouping);\n                }\n\n                if (currentGroupingMethod is not null && DataView.GroupDescriptions.Contains(currentGroupingMethod) is false)\n                {\n                    DataView.GroupDescriptions.Add(currentGroupingMethod);\n                }\n\n                HandleModGroupingSorting();\n            }\n        }\n\n        internal void UpdateFilter()\n        {\n            if (DataView is not null)\n            {\n                UpdateDataGridGrouping();\n\n                DataView.Filter = null;\n                DataView.Filter = ModFilter;\n            }\n        }\n\n        private bool ModFilter(object item)\n        {\n            var mod = item as Mod;\n            if (mod is null)\n            {\n                return false;\n            }\n\n            if (mod.IsHidden)\n            {\n                return false;\n            }\n\n            if (_disabledModFilter == DisplayFilter.ShowEnabled && !mod.IsEnabled)\n            {\n                return false;\n            }\n            else if (_disabledModFilter == DisplayFilter.ShowDisabled && mod.IsEnabled)\n            {\n                return false;\n            }\n            else if (_disabledModFilter == DisplayFilter.RequireConfig && !mod.HasConfig)\n            {\n                return false;\n            }\n\n            if (_showUpdatableMods && String.IsNullOrEmpty(mod.ParsedStatus))\n            {\n                return false;\n            }\n\n            if (String.IsNullOrEmpty(_filterText) || _columnFilter is null || !_columnFilter.Any())\n            {\n                return true;\n            }\n\n            if (!String.IsNullOrEmpty(_filterText) && _columnFilter.Any())\n            {\n                var filterTextNoWhitespace = _filterText.Replace(\" \", String.Empty);\n                if (_columnFilter.Contains(Program.translation.Get(\"ui.main_window.combobox.mod_name\")) && mod.Name.Replace(\" \", String.Empty).Contains(filterTextNoWhitespace, StringComparison.OrdinalIgnoreCase))\n                {\n                    return true;\n                }\n                if (_columnFilter.Contains(Program.translation.Get(\"ui.main_window.combobox.group\")))\n                {\n                    ModGrouping modGroupingMethod = Program.settings.ModGroupingMethod;\n                    switch (Program.settings.ModGroupingMethod)\n                    {\n                        case ModGrouping.Folder:\n                            if (mod.Path.Replace(\" \", String.Empty).Contains(filterTextNoWhitespace, StringComparison.OrdinalIgnoreCase) is true)\n                            {\n                                return true;\n                            }\n                            break;\n                        case ModGrouping.FolderCondensed:\n                            if (mod.RootPath.Replace(\" \", String.Empty).Contains(filterTextNoWhitespace, StringComparison.OrdinalIgnoreCase) is true)\n                            {\n                                return true;\n                            }\n                            break;\n                        case ModGrouping.ContentPack:\n                            if (mod.FrameworkID is not null && mod.FrameworkID.Replace(\" \", String.Empty).Contains(filterTextNoWhitespace, StringComparison.OrdinalIgnoreCase) is true)\n                            {\n                                return true;\n                            }\n                            break;\n                    }\n                }\n                if (_columnFilter.Contains(Program.translation.Get(\"ui.main_window.combobox.top_level_group\")))\n                {\n                    ModGrouping modGroupingMethod = Program.settings.ModGroupingMethod;\n                    switch (Program.settings.ModGroupingMethod)\n                    {\n                        case ModGrouping.Folder:\n                        case ModGrouping.FolderCondensed:\n                            if (mod.RootPath is not null && mod.RootPath.Replace(\" \", String.Empty).Contains(filterTextNoWhitespace, StringComparison.OrdinalIgnoreCase) is true)\n                            {\n                                return true;\n                            }\n                            break;\n                        case ModGrouping.ContentPack:\n                            if (mod.FrameworkID is not null && mod.FrameworkID.Replace(\" \", String.Empty).Contains(filterTextNoWhitespace, StringComparison.OrdinalIgnoreCase) is true)\n                            {\n                                return true;\n                            }\n                            break;\n                    }\n                }\n                if (_columnFilter.Contains(Program.translation.Get(\"ui.main_window.combobox.author\")) && mod.Author.Replace(\" \", String.Empty).Contains(filterTextNoWhitespace, StringComparison.OrdinalIgnoreCase))\n                {\n                    return true;\n                }\n                if (_columnFilter.Contains(Program.translation.Get(\"ui.main_window.combobox.requirements\")) && ((mod.HardRequirements is not null && mod.HardRequirements.Any(r => r.Name is null || r.Name.Replace(\" \", String.Empty).Contains(filterTextNoWhitespace, StringComparison.OrdinalIgnoreCase))) || (mod.MissingRequirements is not null && mod.MissingRequirements.Any(r => r.Name is null || r.Name.Replace(\" \", String.Empty).Contains(filterTextNoWhitespace, StringComparison.OrdinalIgnoreCase)))))\n                {\n                    return true;\n                }\n            }\n\n            return false;\n        }\n\n        private void DataViewSortDescription_CollectionChanged(object? sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)\n        {\n            HandleModGroupingSorting();\n        }\n\n        private void HandleModGroupingSorting()\n        {\n            switch (Program.settings.ModGroupingMethod)\n            {\n                case ModGrouping.None:\n                    break;\n                case ModGrouping.Folder:\n                    if (DataView.SortDescriptions.Any(d => d.PropertyPath == nameof(Mod.Path)) is false)\n                    {\n                        DataView.SortDescriptions.Add(DataGridSortDescription.FromPath(nameof(Mod.Path), ListSortDirection.Ascending));\n                    }\n                    break;\n                case ModGrouping.FolderCondensed:\n                    if (DataView.SortDescriptions.Any(d => d.PropertyPath == nameof(Mod.RootPath)) is false)\n                    {\n                        DataView.SortDescriptions.Add(DataGridSortDescription.FromPath(nameof(Mod.RootPath), ListSortDirection.Ascending));\n                    }\n                    break;\n                case ModGrouping.ContentPack:\n                    if (DataView.SortDescriptions.Any(d => d.PropertyPath == nameof(Mod.FrameworkID)) is false)\n                    {\n                        DataView.SortDescriptions.Add(DataGridSortDescription.FromPath(nameof(Mod.FrameworkID), ListSortDirection.Ascending));\n                    }\n                    break;\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/ViewModels/MessageWindowViewModel.cs",
    "content": "using ReactiveUI;\n\nnamespace Stardrop.ViewModels\n{\n    public class MessageWindowViewModel : ViewModelBase\n    {\n        private string _messageText;\n        public string MessageText { get { return _messageText; } set { this.RaiseAndSetIfChanged(ref _messageText, value); } }\n        private string _positiveButtonText;\n        public string PositiveButtonText { get { return _positiveButtonText; } set { this.RaiseAndSetIfChanged(ref _positiveButtonText, value); } }\n        private string _negativeButtonText;\n        public string NegativeButtonText { get { return _negativeButtonText; } set { this.RaiseAndSetIfChanged(ref _negativeButtonText, value); } }\n    }\n}\n"
  },
  {
    "path": "Stardrop/ViewModels/ModDownloadViewModel.cs",
    "content": "﻿using ReactiveUI;\nusing System;\nusing System.Linq;\nusing System.Reactive;\nusing System.Reactive.Linq;\nusing System.Threading;\n\nnamespace Stardrop.ViewModels\n{\n    public enum ModDownloadStatus\n    {\n        NotStarted,\n        InProgress,\n        Successful,\n        Canceled,\n        Failed\n    }\n\n    public class ModDownloadViewModel : ViewModelBase\n    {\n        private readonly DateTimeOffset _startTime;\n        private readonly CancellationTokenSource _downloadCancellationSource;\n\n        // Communicates up to the parent panel that the user wants to remove this download from the list.\n        public event EventHandler? RemovalRequested = null!;\n\n        // --Set-once properties--\n        public Uri ModUri { get; init; }\n\n        // --Bindable properties--        \n\n        private string _name;\n        public string Name { get => _name; set => this.RaiseAndSetIfChanged(ref _name, value); }\n\n        private long? _sizeBytes;\n        public long? SizeBytes { get => _sizeBytes; set => this.RaiseAndSetIfChanged(ref _sizeBytes, value); }\n\n        private long _downloadedBytes;\n        public long DownloadedBytes { get => _downloadedBytes; set => this.RaiseAndSetIfChanged(ref _downloadedBytes, value); }\n\n        private ModDownloadStatus _downloadStatus = ModDownloadStatus.NotStarted;\n        public ModDownloadStatus DownloadStatus { get => _downloadStatus; set => this.RaiseAndSetIfChanged(ref _downloadStatus, value); }\n\n        // --Composite or dependent properties--\n\n        private readonly ObservableAsPropertyHelper<double> _completion = null!;\n        public double Completion => _completion.Value;\n\n        private readonly ObservableAsPropertyHelper<bool> _isSizeUnknown = null!;\n        public bool IsSizeUnknown => _isSizeUnknown.Value;\n\n        private readonly ObservableAsPropertyHelper<string> _downloadSpeedLabel = null!;\n        public string DownloadSpeedLabel => _downloadSpeedLabel.Value;\n\n        private readonly ObservableAsPropertyHelper<string> _downloadProgressLabel = null!;\n        public string DownloadProgressLabel => _downloadProgressLabel.Value;\n\n        // --Commands--\n\n        public ReactiveCommand<Unit, Unit> CancelCommand { get; }\n        public ReactiveCommand<Unit, Unit> RemoveCommand { get; }\n\n        public ModDownloadViewModel(Uri modUri, string name, long? sizeInBytes, CancellationTokenSource downloadCancellationSource)\n        {\n            _startTime = DateTimeOffset.UtcNow;\n\n            ModUri = modUri;\n            _name = name;\n            _sizeBytes = sizeInBytes;\n            _downloadedBytes = 0;\n            _downloadCancellationSource = downloadCancellationSource;\n\n            CancelCommand = ReactiveCommand.Create(Cancel);\n            RemoveCommand = ReactiveCommand.Create(Remove);\n\n            // SizeBytes null-ness to IsSizeUnknown converison\n            this.WhenAnyValue(x => x.SizeBytes)\n                .Select(x => x.HasValue is false)\n                .ToProperty(this, x => x.IsSizeUnknown, out _isSizeUnknown);\n\n            // DownloadedBytes to DownloadSpeedLabel conversion\n            this.WhenAnyValue(x => x.DownloadedBytes)\n                .Sample(TimeSpan.FromMilliseconds(500), RxApp.MainThreadScheduler)\n                .Select(bytes =>\n                {\n                    double elapsedSeconds = (DateTimeOffset.UtcNow - _startTime).TotalSeconds;\n                    double bytesPerSecond = bytes / elapsedSeconds;\n                    if (bytesPerSecond > 1024 * 1024)  // MB \n                    {\n                        return $\"{(bytesPerSecond / (1024 * 1024)):N2} {Program.translation.Get(\"internal.measurements.megabytes_per_second\")}\";\n                    }\n                    else if (bytesPerSecond > 1024) // KB\n                    {\n                        return $\"{(bytesPerSecond / 1024):N2} {Program.translation.Get(\"internal.measurements.kilobytes_per_second\")}\";\n                    }\n                    else // Bytes\n                    {\n                        return $\"{bytesPerSecond:N0} {Program.translation.Get(\"internal.measurements.bytes_per_second\")}\";\n                    }\n                }).ToProperty(this, x => x.DownloadSpeedLabel, out _downloadSpeedLabel);\n\n            // DownloadedBytes and SizeBytes to DownloadProgressLabel conversion\n            this.WhenAnyValue(x => x.DownloadedBytes, x => x.SizeBytes)\n                .Sample(TimeSpan.FromMilliseconds(500), RxApp.MainThreadScheduler)\n                .Select(((long Bytes, long? Total) x) =>\n                {\n                    string bytesString = ToHumanReadable(x.Bytes);\n                    if (x.Total is null)\n                    {\n                        return $\"{bytesString} / ??? {Program.translation.Get(\"internal.measurements.megabytes_size\")}\";\n                    }\n                    else\n                    {\n                        string totalString = ToHumanReadable(x.Total!.Value);\n                        return $\"{bytesString} / {totalString}\";\n                    }\n\n                    static string ToHumanReadable(long bytes)\n                    {\n                        if (bytes > 1024 * 1024) // MB\n                        {\n                            return $\"{(bytes / (1024.0 * 1024.0)):N2} {Program.translation.Get(\"internal.measurements.megabytes_size\")}\";\n                        }\n                        else if (bytes > 1024) // KB\n                        {\n                            return $\"{(bytes / 1024.0):N2} {Program.translation.Get(\"internal.measurements.kilobytes_size\")}\";\n                        }\n                        else\n                        {\n                            return $\"{bytes:N0} {Program.translation.Get(\"internal.measurements.bytes_size\")}\";\n                        }\n                    }\n                }).ToProperty(this, x => x.DownloadProgressLabel, out _downloadProgressLabel);\n\n            if (SizeBytes.HasValue)\n            {\n                // DownloadedBytes to Completion conversion\n                this.WhenAnyValue(x => x.DownloadedBytes)\n                    .Sample(TimeSpan.FromMilliseconds(500), RxApp.MainThreadScheduler)\n                    .Select(x => (DownloadedBytes / (double)SizeBytes) * 100)\n                    .ToProperty(this, x => x.Completion, out _completion);                \n            }\n        }\n\n        private void Cancel()\n        {\n            _downloadCancellationSource.Cancel();\n            DownloadStatus = ModDownloadStatus.Canceled;\n        }\n\n        private void Remove()\n        {\n            RemovalRequested?.Invoke(this, EventArgs.Empty);\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/ViewModels/ProfileEditorViewModel.cs",
    "content": "using Stardrop.Models;\nusing System;\nusing System.Collections.Generic;\nusing System.Collections.ObjectModel;\nusing System.IO;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text.Json;\n\nnamespace Stardrop.ViewModels\n{\n    public class ProfileEditorViewModel : ViewModelBase\n    {\n        public ObservableCollection<Profile> Profiles { get; set; }\n        public List<Profile> OldProfiles { get; set; }\n        public string ToolTip_Save { get; set; }\n        public string ToolTip_Cancel { get; set; }\n\n        private readonly string _profileFilePath;\n\n        public ProfileEditorViewModel(string profilesFilePath)\n        {\n            OldProfiles = new List<Profile>();\n            Profiles = new ObservableCollection<Profile>();\n\n            _profileFilePath = profilesFilePath;\n            DirectoryInfo profileDirectory = new DirectoryInfo(_profileFilePath);\n            foreach (var fileInfo in profileDirectory.GetFiles(\"*.json\", SearchOption.AllDirectories))\n            {\n                if (fileInfo.DirectoryName is null)\n                {\n                    continue;\n                }\n\n                try\n                {\n                    var profile = JsonSerializer.Deserialize<Profile>(File.ReadAllText(fileInfo.FullName), new JsonSerializerOptions { AllowTrailingCommas = true });\n                    if (profile is null)\n                    {\n                        Program.helper.Log($\"The profile file {fileInfo.Name} was empty or not deserializable from {fileInfo.DirectoryName}\", Utilities.Helper.Status.Alert);\n                        continue;\n                    }\n\n                    Profiles.Add(profile);\n                }\n                catch (Exception ex)\n                {\n                    Program.helper.Log($\"Unable to load the profile file {fileInfo.Name} from {fileInfo.DirectoryName}: {ex}\", Utilities.Helper.Status.Alert);\n                }\n            }\n\n            if (!Profiles.Any(p => p.Name == Program.defaultProfileName))\n            {\n                var defaultProfile = new Profile(Program.defaultProfileName) { IsProtected = true };\n                Profiles.Insert(0, defaultProfile);\n                CreateProfile(defaultProfile);\n            }\n            else if (Profiles.IndexOf(Profiles.First(p => p.Name == Program.defaultProfileName)) != 0)\n            {\n                // Move the default profile to the top\n                Profiles.Move(Profiles.IndexOf(Profiles.First(p => p.Name == Program.defaultProfileName)), 0);\n            }\n\n            OldProfiles = Profiles.ToList();\n\n            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) || RuntimeInformation.IsOSPlatform(OSPlatform.OSX))\n            {\n                ToolTip_Save = Program.translation.Get(\"ui.settings_window.tooltips.save_changes\");\n                ToolTip_Cancel = Program.translation.Get(\"ui.settings_window.tooltips.cancel_changes\");\n            }\n            else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))\n            {\n                // TEMPORARY FIX: Due to bug with Avalonia on Linux platforms, tooltips currently cause crashes when they disappear\n                // To work around this, tooltips are purposely not displayed\n            }\n        }\n\n        internal void CreateProfile(Profile profile, bool force = false)\n        {\n            string fileFullName = Path.Combine(_profileFilePath, profile.Name + \".json\");\n            if (File.Exists(fileFullName) && !force)\n            {\n                Program.helper.Log($\"Attempted to create an already existing profile file ({profile.Name}) at the path {fileFullName}\", Utilities.Helper.Status.Warning);\n                return;\n            }\n\n            File.WriteAllText(fileFullName, JsonSerializer.Serialize(profile, new JsonSerializerOptions() { WriteIndented = true }));\n        }\n\n        internal void DeleteProfile(Profile profile)\n        {\n            string fileFullName = Path.Combine(_profileFilePath, profile.Name + \".json\");\n            if (!File.Exists(fileFullName))\n            {\n                Program.helper.Log($\"Attempted to delete a non-existent profile file ({profile.Name}) at the path {fileFullName}\", Utilities.Helper.Status.Warning);\n                return;\n            }\n\n            File.Delete(fileFullName);\n        }\n\n        internal void UpdateProfile(Profile profile, List<string> enabledModIds)\n        {\n            int profileIndex = Profiles.IndexOf(profile);\n            if (profileIndex == -1)\n            {\n                return;\n            }\n\n            Profiles[profileIndex].EnabledModIds = enabledModIds;\n            CreateProfile(profile, true);\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/ViewModels/SettingsWindowViewModel.cs",
    "content": "using Stardrop.Models;\nusing Stardrop.Utilities;\nusing System;\nusing System.Collections.Generic;\nusing System.Runtime.InteropServices;\n\nnamespace Stardrop.ViewModels\n{\n    public class SettingsWindowViewModel : ViewModelBase\n    {\n        // Setting bindings\n        public string SMAPIPath { get { return Program.settings.SMAPIFolderPath; } set { Program.settings.SMAPIFolderPath = value; Pathing.SetSmapiPath(Program.settings.SMAPIFolderPath, String.IsNullOrEmpty(Program.settings.ModFolderPath)); } }\n        public string ModFolderPath { get { return Program.settings.ModFolderPath; } set { Program.settings.ModFolderPath = value; Pathing.SetModPath(Program.settings.ModFolderPath); } }\n        public string ModInstallPath { get { return Program.settings.ModInstallPath; } set { Program.settings.ModInstallPath = value; } }\n        public bool IgnoreHiddenFolders { get { return Program.settings.IgnoreHiddenFolders; } set { Program.settings.IgnoreHiddenFolders = value; } }\n        public bool IsAskingBeforeAcceptingNXM { get { return Program.settings.IsAskingBeforeAcceptingNXM; } set { Program.settings.IsAskingBeforeAcceptingNXM = value; } }\n        public bool EnableProfileSpecificModConfigs { get { return Program.settings.EnableProfileSpecificModConfigs; } set { Program.settings.EnableProfileSpecificModConfigs = value; } }\n        public bool EnableModsOnAdd { get { return Program.settings.EnableModsOnAdd; } set { Program.settings.EnableModsOnAdd = value; } }\n        public bool AlwaysAskToDelete { get { return Program.settings.AlwaysAskToDelete; } set { Program.settings.AlwaysAskToDelete = value; } }\n        public bool ShouldAutomaticallySaveProfileChanges { get { return Program.settings.ShouldAutomaticallySaveProfileChanges; } set { Program.settings.ShouldAutomaticallySaveProfileChanges = value; } }\n        public bool ShowModThumbnails { get { return Program.settings.ShowModThumbnails; } set { Program.settings.ShowModThumbnails = value; } }\n        public List<Theme> Themes { get; set; } = new List<Theme>();\n\n        // Tooltips\n        public string ToolTip_SMAPI { get; set; }\n        public string ToolTip_ModFolder { get; set; }\n        public string ToolTip_ModInstall { get; set; }\n        public string ToolTip_Theme { get; set; }\n        public string ToolTip_Language { get; set; }\n        public string ToolTip_Grouping { get; set; }\n        public string ToolTip_IgnoreHiddenFolders { get; set; }\n        public string ToolTip_PreferredServer { get; set; }\n        public string ToolTip_NXMAssociation { get; set; }\n        public string ToolTip_AlwaysAskNXMFiles { get; set; }\n        public string ToolTip_EnableProfileSpecificModConfigs { get; set; }\n        public string ToolTip_EnableModsOnAdd { get; set; }\n        public string ToolTip_ShouldAutomaticallySaveProfileChanges { get; set; }\n        public string ToolTip_ShowModThumbnails { get; set; }\n        public string ToolTip_Save { get; set; }\n        public string ToolTip_Cancel { get; set; }\n\n        // Other UI controls\n        public bool ShowMainMenu { get; set; }\n        public bool ShowNXMAssociationButton { get; set; }\n        public bool ShowNexusServers { get; set; }\n\n        public SettingsWindowViewModel()\n        {\n            ToolTip_SMAPI = Program.translation.Get(\"ui.settings_window.tooltips.smapi\");\n            ToolTip_ModFolder = Program.translation.Get(\"ui.settings_window.tooltips.mod_folder_path\");\n            ToolTip_ModInstall = Program.translation.Get(\"ui.settings_window.tooltips.mod_install_path\");\n            ToolTip_Theme = Program.translation.Get(\"ui.settings_window.tooltips.theme\");\n            ToolTip_Language = Program.translation.Get(\"ui.settings_window.tooltips.language\");\n            ToolTip_Grouping = Program.translation.Get(\"ui.settings_window.tooltips.grouping\");\n            ToolTip_IgnoreHiddenFolders = Program.translation.Get(\"ui.settings_window.tooltips.ignore_hidden_folders\");\n            ToolTip_PreferredServer = Program.translation.Get(\"ui.settings_window.tooltips.preferred_server\");\n            ToolTip_NXMAssociation = Program.translation.Get(\"ui.settings_window.tooltips.nxm_file_association\");\n            ToolTip_AlwaysAskNXMFiles = Program.translation.Get(\"ui.settings_window.tooltips.always_ask_nxm_files\");\n            ToolTip_EnableProfileSpecificModConfigs = Program.translation.Get(\"ui.settings_window.tooltips.enable_profile_specific_configs\");\n            ToolTip_EnableModsOnAdd = Program.translation.Get(\"ui.settings_window.tooltips.enable_mods_on_add\");\n            ToolTip_ShouldAutomaticallySaveProfileChanges = Program.translation.Get(\"ui.settings_window.tooltips.automatically_save_profile_changes\");\n            ToolTip_ShowModThumbnails = Program.translation.Get(\"ui.settings_window.tooltips.show_mod_thumbnails\");\n            ToolTip_Save = Program.translation.Get(\"ui.settings_window.tooltips.save_changes\");\n            ToolTip_Cancel = Program.translation.Get(\"ui.settings_window.tooltips.cancel_changes\");\n\n            ShowMainMenu = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);\n            ShowNXMAssociationButton = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);\n            ShowNexusServers = Program.settings.NexusDetails is not null && Program.settings.NexusDetails.IsPremium;\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/ViewModels/ViewModelBase.cs",
    "content": "using ReactiveUI;\n\nnamespace Stardrop.ViewModels\n{\n    public class ViewModelBase : ReactiveObject\n    {\n    }\n}\n"
  },
  {
    "path": "Stardrop/ViewModels/WarningWindowViewModel.cs",
    "content": "using ReactiveUI;\n\nnamespace Stardrop.ViewModels\n{\n    public class WarningWindowViewModel : ViewModelBase\n    {\n        private string _warningText;\n        public string WarningText { get { return _warningText; } set { this.RaiseAndSetIfChanged(ref _warningText, value); } }\n        private string _buttonText;\n        public string ButtonText { get { return _buttonText; } set { this.RaiseAndSetIfChanged(ref _buttonText, value); } }\n        private bool _isButtonVisible;\n        public bool IsButtonVisible { get { return _isButtonVisible; } set { this.RaiseAndSetIfChanged(ref _isButtonVisible, value); } }\n        private bool _isProgressBarVisible;\n        public bool IsProgressBarVisible { get { return _isProgressBarVisible; } set { this.RaiseAndSetIfChanged(ref _isProgressBarVisible, value); } }\n        private double _progressBarValue;\n        public double ProgressBarValue { get { return _progressBarValue; } set { this.RaiseAndSetIfChanged(ref _progressBarValue, value); } }\n\n        public WarningWindowViewModel()\n        {\n\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Views/DownloadPanel.axaml",
    "content": "<UserControl xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n             xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n             xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n             mc:Ignorable=\"d\"\n             xmlns:sys=\"using:System\"\n             xmlns:scg=\"using:System.Collections.Generic\"\n             xmlns:i=\"clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia\"\n             xmlns:vm=\"using:Stardrop.ViewModels\"\n             xmlns:conv=\"using:Stardrop.Converters\"\n             xmlns:i18n=\"using:Stardrop.Utilities.Extension\"\n             Width=\"400\"\n             Height=\"300\"\n             x:Class=\"Stardrop.Views.DownloadPanel\"\n             x:DataType=\"vm:DownloadPanelViewModel\">\n\n    <Design.DataContext>\n        <vm:DownloadPanelViewModel/>\n    </Design.DataContext>\n\n    <UserControl.Resources>\n        <conv:EnumEqualsConverter x:Key=\"EnumEqualsConverter\"/>\n    </UserControl.Resources>\n\n    <UserControl.Styles>\n        <Style Selector=\"TextBlock.foreground\">\n            <Setter Property=\"Foreground\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n        </Style>\n        <Style Selector=\"TextBlock.header\">\n            <Setter Property=\"Foreground\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n            <Setter Property=\"FontWeight\" Value=\"Bold\"/>\n        </Style>\n        <Style Selector=\"Button\">\n            <Setter Property=\"Foreground\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n            <Setter Property=\"Background\" Value=\"Transparent\"/>\n            <Setter Property=\"BorderBrush\" Value=\"{DynamicResource HighlightBrush}\"/>\n        </Style>\n        <Style Selector=\"Button:pointerover /template/ ContentPresenter\">\n            <Setter Property=\"Background\" Value=\"LightGray\"/>\n        </Style>\n        <Style Selector=\"Button:pressed /template/ ContentPresenter\">\n            <Setter Property=\"Background\" Value=\"White\"/>\n        </Style>\n    </UserControl.Styles>\n    \n    <ScrollViewer Padding=\"12 0 24 0\">\n        <Grid>\n            <TextBlock x:Name=\"NoDownloadsHeader\"\n                       Classes=\"header\"\n                       Margin=\"0 12 0 12\"\n                       HorizontalAlignment=\"Center\"\n                       TextAlignment=\"Center\"\n                       VerticalAlignment=\"Center\"\n                       Text=\"{i18n:Translate ui.downloads_panel.no_downloads_label}\"\n                       IsVisible=\"{Binding !Downloads.Count}\"/>\n            <ItemsRepeater x:Name=\"DownloadItems\" \n                           Items=\"{Binding Downloads}\"\n                           Margin=\"0 12 0 12\"\n                           IsVisible=\"{Binding !!Downloads.Count}\">\n                <ItemsRepeater.Layout>\n                    <StackLayout Spacing=\"24\"/>\n                </ItemsRepeater.Layout>                \n                <ItemsRepeater.ItemTemplate>\n                    <DataTemplate DataType=\"vm:ModDownloadViewModel\">\n                        <!--Each of the Classes.className variables is a boolean. The converter checks the current DownloadStatus and sets\n                        the appropriate Classes.className to 'True', which then causes the style to kick in and fill in the UI with one of the \n                        styles defined below. This simulates a Visual State Manager like WPF or UWP uses.-->\n                        <ContentControl Classes.notStarted=\"{Binding DownloadStatus, Converter={StaticResource EnumEqualsConverter}, \n                                            ConverterParameter={x:Static vm:ModDownloadStatus.NotStarted}}\"\n                                        Classes.inProgress=\"{Binding DownloadStatus, Converter={StaticResource EnumEqualsConverter}, \n                                            ConverterParameter={x:Static vm:ModDownloadStatus.InProgress}}\"\n                                        Classes.successful=\"{Binding DownloadStatus, Converter={StaticResource EnumEqualsConverter}, \n                                            ConverterParameter={x:Static vm:ModDownloadStatus.Successful}}\"\n                                        Classes.canceled=\"{Binding DownloadStatus, Converter={StaticResource EnumEqualsConverter}, \n                                            ConverterParameter={x:Static vm:ModDownloadStatus.Canceled}}\"\n                                        Classes.failed=\"{Binding DownloadStatus, Converter={StaticResource EnumEqualsConverter}, \n                                            ConverterParameter={x:Static vm:ModDownloadStatus.Failed}}\"/>\n                    </DataTemplate>\n                </ItemsRepeater.ItemTemplate>\n                <ItemsRepeater.Styles>\n                    <!--UI for not-yet-started download-->\n                    <Style Selector=\"ContentControl.notStarted\">\n                        <Setter Property=\"Content\" Value=\"{x:Null}\"/>\n                    </Style>\n                    <!--UI for in-progress download-->\n                    <Style Selector=\"ContentControl.inProgress\" x:DataType=\"vm:ModDownloadViewModel\">\n                        <Setter Property=\"Content\">\n                            <Template>\n                                <Grid RowDefinitions=\"Auto, Auto, Auto\">\n\n                                    <TextBlock Grid.Row=\"0\" Classes=\"foreground\" Text=\"{Binding Name}\"/>\n\n                                    <Grid Grid.Row=\"1\" ColumnDefinitions=\"8*, 2*, Auto\">\n\n                                        <ProgressBar x:Name=\"ProgressBar\"\n                                                     Grid.Column=\"0\"\n                                                     Minimum=\"0\" Maximum=\"100\"\n                                                     IsIndeterminate=\"{Binding IsSizeUnknown}\"\n                                                     Value=\"{Binding Completion}\"/>\n                                        <TextBlock x:Name=\"PercentageText\"\n                                                   Grid.Row=\"1\" Grid.Column=\"1\"\n                                                   Classes=\"foreground\"\n                                                   HorizontalAlignment=\"Center\"\n                                                   VerticalAlignment=\"Center\"\n                                                   TextAlignment=\"Right\"\n                                                   Text=\"{Binding Completion, StringFormat='{}{0:0.##}%'}\"/>\n                                        <Button x:Name=\"CancelButton\"\n                                                Grid.Row=\"1\" Grid.Column=\"2\"\n                                                i:Attached.Icon=\"mdi-cancel\"\n                                                Command=\"{Binding CancelCommand}\"\n                                                ToolTip.Tip=\"{i18n:Translate ui.downloads_panel.tooltips.cancel_button}\"/>\n                                    </Grid>\n\n                                    <TextBlock x:Name=\"SpeedText\"\n                                               Grid.Row=\"2\"\n                                               Classes=\"foreground\"\n                                               Margin=\"0 6 0 0\"\n                                               Text=\"{Binding DownloadSpeedLabel}\"/>\n                                    <TextBlock x:Name=\"DownloadProgressText\"\n                                               Grid.Row=\"2\"\n                                               Classes=\"foreground\"\n                                               Margin=\"0 6 0 0\"\n                                               HorizontalAlignment=\"Right\"\n                                               TextAlignment=\"Right\"\n                                               Text=\"{Binding DownloadProgressLabel}\"/>\n                                </Grid>\n                            </Template>\n                        </Setter>\n                    </Style>\n                    <!--UI for successful download-->\n                    <Style Selector=\"ContentControl.successful\" x:DataType=\"vm:DownloadPanelViewModel\">\n                        <Setter Property=\"Content\">\n                            <Template>\n                                <Grid RowDefinitions=\"Auto, Auto, Auto\">\n\n                                    <TextBlock Grid.Row=\"0\" Classes=\"foreground\" Text=\"{Binding Name}\"/>\n\n                                    <Grid Grid.Row=\"1\" ColumnDefinitions=\"8*, 2*, Auto\">\n\n                                        <ProgressBar x:Name=\"ProgressBar\"\n                                                     Grid.Column=\"0\"\n                                                     Minimum=\"0\" Maximum=\"100\"\n                                                     IsIndeterminate=\"{Binding IsSizeUnknown}\"\n                                                     Value=\"{Binding Completion}\"/>\n                                        <TextBlock x:Name=\"PercentageText\"\n                                                   Grid.Row=\"1\" Grid.Column=\"1\"\n                                                   Classes=\"foreground\"\n                                                   HorizontalAlignment=\"Center\"\n                                                   VerticalAlignment=\"Center\"\n                                                   TextAlignment=\"Right\"\n                                                   Text=\"{Binding Completion, StringFormat='{}{0:0.##}%'}\"/>\n                                        <Button x:Name=\"RemoveButton\"\n                                                Grid.Row=\"1\" Grid.Column=\"2\"\n                                                i:Attached.Icon=\"mdi-close\"\n                                                Command=\"{Binding RemoveCommand}\"\n                                                ToolTip.Tip=\"{i18n:Translate ui.downloads_panel.tooltips.remove_button}\"/>\n                                    </Grid>\n\n                                    <TextBlock x:Name=\"DownloadProgressText\"\n                                               Grid.Row=\"2\"\n                                               Classes=\"foreground\"\n                                               Margin=\"0 6 0 0\"\n                                               HorizontalAlignment=\"Right\"\n                                               TextAlignment=\"Right\"\n                                               Text=\"{Binding DownloadProgressLabel}\"/>\n                                    <TextBlock x:Name=\"StatusText\"\n                                               Grid.Row=\"2\"\n                                               Classes=\"foreground\"\n                                               Margin=\"0 6 0 0\"\n                                               Text=\"{i18n:Translate ui_downloads_panel.download_success}\"/>\n\n                                </Grid>\n                            </Template>\n                        </Setter>\n                    </Style>\n                    <!--UI for a canceled download-->\n                    <Style Selector=\"ContentControl.canceled\" x:DataType=\"vm:DownloadPanelViewModel\">\n                        <Setter Property=\"Content\">\n                            <Template>\n                                <Grid RowDefinitions=\"Auto, Auto, Auto\">\n\n                                    <TextBlock Grid.Row=\"0\" Classes=\"foreground\" Text=\"{Binding Name}\"/>\n\n                                    <Grid Grid.Row=\"1\" ColumnDefinitions=\"8*, 2*, Auto\">\n\n                                        <Border x:Name=\"UnsuccessfulProgressBar\"\n                                                Grid.Column=\"0\"\n                                                CornerRadius=\"4\"\n                                                Height=\"4\"\n                                                Background=\"Red\"/>\n                                        <TextBlock x:Name=\"PercentageText\"\n                                                   Grid.Row=\"1\" Grid.Column=\"1\"\n                                                   Classes=\"foreground\"\n                                                   HorizontalAlignment=\"Center\"\n                                                   VerticalAlignment=\"Center\"\n                                                   TextAlignment=\"Right\"\n                                                   Text=\"--\"/>\n                                        <Button x:Name=\"RemoveButton\"\n                                                Grid.Row=\"1\" Grid.Column=\"2\"\n                                                i:Attached.Icon=\"mdi-close\"\n                                                Command=\"{Binding RemoveCommand}\"\n                                                ToolTip.Tip=\"{i18n:Translate ui.downloads_panel.tooltips.remove_button}\"/>\n                                    </Grid>\n\n                                    <TextBlock x:Name=\"DownloadProgressText\"\n                                               Grid.Row=\"2\"\n                                               Classes=\"foreground\"\n                                               Margin=\"0 6 0 0\"\n                                               HorizontalAlignment=\"Right\"\n                                               TextAlignment=\"Right\"\n                                               Text=\"{Binding DownloadProgressLabel}\"/>\n                                    <TextBlock x:Name=\"StatusText\"\n                                               Grid.Row=\"2\"\n                                               Classes=\"foreground\"\n                                               Margin=\"0 6 0 0\"\n                                               Text=\"{i18n:Translate ui.downloads_panel.download_canceled}\"/>\n\n                                </Grid>\n                            </Template>\n                        </Setter>\n                    </Style>\n                    <!--UI for a failed download-->\n                    <Style Selector=\"ContentControl.failed\" x:DataType=\"vm:DownloadPanelViewModel\">\n                        <Setter Property=\"Content\">\n                            <Template>\n                                <Grid RowDefinitions=\"Auto, Auto, Auto\">\n\n                                    <TextBlock Grid.Row=\"0\" Classes=\"foreground\" Text=\"{Binding Name}\"/>\n\n                                    <Grid Grid.Row=\"1\" ColumnDefinitions=\"8*, 2*, Auto\">\n\n                                        <Border x:Name=\"UnsuccessfulProgressBar\"\n                                                Grid.Column=\"0\"\n                                                CornerRadius=\"4\"\n                                                Height=\"4\"\n                                                Background=\"Red\"/>\n                                        <TextBlock x:Name=\"PercentageText\"\n                                                   Grid.Row=\"1\" Grid.Column=\"1\"\n                                                   Classes=\"foreground\"\n                                                   HorizontalAlignment=\"Center\"\n                                                   VerticalAlignment=\"Center\"\n                                                   TextAlignment=\"Right\"\n                                                   Text=\"--\"/>\n                                        <Button x:Name=\"RemoveButton\"\n                                                Grid.Row=\"1\" Grid.Column=\"2\"\n                                                i:Attached.Icon=\"mdi-close\"\n                                                Command=\"{Binding RemoveCommand}\"\n                                                ToolTip.Tip=\"{i18n:Translate ui.downloads_panel.tooltips.remove_button}\"/>\n                                    </Grid>\n\n                                    <TextBlock x:Name=\"DownloadProgressText\"\n                                               Grid.Row=\"2\"\n                                               Classes=\"foreground\"\n                                               Margin=\"0 6 0 0\"\n                                               HorizontalAlignment=\"Right\"\n                                               TextAlignment=\"Right\"\n                                               Text=\"{Binding DownloadProgressLabel}\"/>\n                                    <TextBlock x:Name=\"StatusText\"\n                                               Grid.Row=\"2\"\n                                               Classes=\"foreground\"\n                                               Margin=\"0 6 0 0\"\n                                               Text=\"{i18n:Translate ui.downloads_panel.download_failed}\"/>\n\n                                </Grid>\n                            </Template>\n                        </Setter>\n                    </Style>\n                </ItemsRepeater.Styles>\n            </ItemsRepeater>\n        </Grid>\n    </ScrollViewer>    \n    \n</UserControl>\n"
  },
  {
    "path": "Stardrop/Views/DownloadPanel.axaml.cs",
    "content": "using Avalonia.Controls;\nusing Avalonia.Markup.Xaml;\nusing Avalonia.Media;\nusing Stardrop.Utilities.External;\nusing Stardrop.ViewModels;\n\nnamespace Stardrop.Views;\n\npublic partial class DownloadPanel : UserControl\n{\n    private DownloadPanelViewModel _viewModel = null!;\n\n    public DownloadPanel()\n    {\n        AvaloniaXamlLoader.Load(this);\n        if (Design.IsDesignMode)\n        {\n            // Normally, the background gets handled by the hosting flyout's FlyoutPresenterClasses\n            // Since design mode doesn't have a flyout to host this, we do it manually\n            Background = new SolidColorBrush(new Color(0xFF, 0x03, 0x13, 0x32));\n            return;\n        }\n\n        _viewModel = new DownloadPanelViewModel(Nexus.Client);\n        DataContext = _viewModel;\n    }\n}"
  },
  {
    "path": "Stardrop/Views/FlexibleOptionWindow.axaml",
    "content": "<Window xmlns=\"https://github.com/avaloniaui\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:vm=\"using:Stardrop.ViewModels\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:i=\"clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia\"\n        mc:Ignorable=\"d\" d:DesignWidth=\"300\" d:DesignHeight=\"150\"\n        x:Class=\"Stardrop.Views.FlexibleOptionWindow\"\n        Title=\"Message\"\n        MinWidth=\"300\"\n        MinHeight=\"150\"\n        Width=\"300\"\n        Height=\"150\"\n        Background=\"{DynamicResource ThemeBackgroundBrush}\"\n        HasSystemDecorations=\"false\"\n        Icon=\"/Assets/icon.ico\">\n\n\t<Window.Styles>\n\t\t<Style Selector=\"Button:pointerover /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button:pressed /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource HighlightBrush}\"/>\n\t\t</Style>\n\t</Window.Styles>\n\n\t<Border BorderBrush=\"{DynamicResource HighlightBrush}\" BorderThickness=\"1\">\n\t\t<Grid>\n\t\t\t<Grid.RowDefinitions>\n\t\t\t\t<RowDefinition Height=\"Auto\" />\n\t\t\t\t<RowDefinition Height=\"Auto\" />\n\t\t\t\t<RowDefinition Height=\"25\" />\n\t\t\t</Grid.RowDefinitions>\n\n\t\t\t<Grid.ColumnDefinitions>\n\t\t\t\t<ColumnDefinition Width=\"*\" />\n\t\t\t\t<ColumnDefinition Width=\"*\" />\n\t\t\t\t<ColumnDefinition Width=\"*\" />\n\t\t\t</Grid.ColumnDefinitions>\n\n\t\t\t<TextBlock Grid.Row=\"0\" Grid.ColumnSpan=\"3\" Text=\"{Binding MessageText}\" TextWrapping=\"Wrap\" TextAlignment=\"Center\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" HorizontalAlignment=\"Center\" Margin=\"25 35 25 20\"/>\n\t\t\t<Button Grid.Row=\"1\"  Grid.Column=\"0\" IsVisible=\"{Binding IsFirstButtonVisible}\" Content=\"{Binding FirstButtonText}\" Click=\"Button_Click\" HorizontalAlignment=\"Left\" BorderBrush=\"{DynamicResource HighlightBrush}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Background=\"Transparent\" Margin=\"25 0 0 0\"/>\n\t\t\t<Button Grid.Row=\"1\"  Grid.Column=\"1\" IsVisible=\"{Binding IsSecondButtonVisible}\" IsCancel=\"true\" Content=\"{Binding SecondButtonText}\" Click=\"Button_Click\" HorizontalAlignment=\"Center\" BorderBrush=\"{DynamicResource HighlightBrush}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Background=\"Transparent\"  Margin=\"0 0 0 0\"/>\n\t\t\t<Button Grid.Row=\"1\"  Grid.Column=\"2\" IsVisible=\"{Binding IsThirdButtonVisible}\" IsCancel=\"true\" Content=\"{Binding ThirdButtonText}\" Click=\"Button_Click\" HorizontalAlignment=\"Right\" BorderBrush=\"{DynamicResource HighlightBrush}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Background=\"Transparent\"  Margin=\"0 0 25 0\"/>\n\t\t</Grid>\n\t</Border>\n</Window>\n"
  },
  {
    "path": "Stardrop/Views/FlexibleOptionWindow.axaml.cs",
    "content": "using Avalonia;\nusing Avalonia.Controls;\nusing Avalonia.Markup.Xaml;\nusing Stardrop.Models.Data.Enums;\nusing Stardrop.ViewModels;\nusing System;\n\nnamespace Stardrop.Views\n{\n    public partial class FlexibleOptionWindow : Window\n    {\n        private readonly FlexibleOptionWindowViewModel _viewModel;\n\n        public FlexibleOptionWindow()\n        {\n            InitializeComponent();\n\n            // Set the main window view\n            _viewModel = new FlexibleOptionWindowViewModel();\n            DataContext = _viewModel;\n\n#if DEBUG\n            this.AttachDevTools();\n#endif\n        }\n\n        public FlexibleOptionWindow(string messageText, string? firstButtonText = null, string? secondButtonText = null, string? thirdButtonText = null) : this()\n        {\n            _viewModel.MessageText = messageText;\n\n            if (String.IsNullOrEmpty(firstButtonText) is false)\n            {\n                _viewModel.FirstButtonText = firstButtonText;\n                _viewModel.IsFirstButtonVisible = true;\n            }\n            if (String.IsNullOrEmpty(secondButtonText) is false)\n            {\n                _viewModel.SecondButtonText = secondButtonText;\n                _viewModel.IsSecondButtonVisible = true;\n            }\n            if (String.IsNullOrEmpty(thirdButtonText) is false)\n            {\n                _viewModel.ThirdButtonText = thirdButtonText;\n                _viewModel.IsThirdButtonVisible = true;\n            }\n\n            this.WindowStartupLocation = WindowStartupLocation.CenterOwner;\n            this.SizeToContent = SizeToContent.Height;\n        }\n\n        private void Button_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            Button? button = sender as Button;\n            if (button is null)\n            {\n                return;\n            }\n\n            if (button.Content.Equals(_viewModel.FirstButtonText))\n            {\n                this.Close(Choice.First);\n            }\n            else if (button.Content.Equals(_viewModel.SecondButtonText))\n            {\n                this.Close(Choice.Second);\n            }\n            else if (button.Content.Equals(_viewModel.ThirdButtonText))\n            {\n                this.Close(Choice.Third);\n            }\n        }\n\n        private void InitializeComponent()\n        {\n            AvaloniaXamlLoader.Load(this);\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Views/MainWindow.axaml",
    "content": "﻿<Window xmlns=\"https://github.com/avaloniaui\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:vm=\"using:Stardrop.ViewModels\"\n        xmlns:view=\"using:Stardrop.Views\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:sys=\"clr-namespace:System;assembly=netstandard\"\n        xmlns:i=\"clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia\"\n        xmlns:conv=\"clr-namespace:Stardrop.Converters;assembly=Stardrop\"\n\t\txmlns:i18n=\"clr-namespace:Stardrop.Utilities.Extension\"\n        mc:Ignorable=\"d\" d:DesignWidth=\"800\" d:DesignHeight=\"450\"\n        x:Class=\"Stardrop.Views.MainWindow\"\n        Title=\"Stardrop\"\n        MinWidth=\"400\"\n        MinHeight=\"400\"\n        Width=\"1430\"\n        Height=\"800\"\n        Background=\"{DynamicResource ThemeBackgroundBrush}\"\n        HasSystemDecorations=\"{Binding HasSystemDecorations}\"\n        ExtendClientAreaToDecorationsHint=\"true\"\n        ExtendClientAreaChromeHints=\"{Binding ChromeHint}\"\n        ExtendClientAreaTitleBarHeightHint=\"-1\"\n        CanResize=\"True\"\n        Opened=\"MainWindow_Opened\"\n        Closing=\"MainWindow_Closing\"\n        WindowStartupLocation=\"CenterScreen\"\n        Icon=\"/Assets/icon.ico\">\n\n\t<Window.Resources>\n\t\t<conv:EnumConverter x:Key=\"EnumToStringConverter\"/>\n\t</Window.Resources>\n\n\t<Window.Styles>\n\t\t<Style Selector=\"FlyoutPresenter.FilterBoxFlyout\">\n\t\t\t<Setter Property=\"Background\" Value=\"Transparent\"/>\n\t\t\t<Setter Property=\"BorderThickness\" Value=\"0\"/>\n\t\t</Style>\n        <Style Selector=\"FlyoutPresenter.DownloadBoxFlyout\">\n            <Setter Property=\"Background\" Value=\"{DynamicResource ThemeBackgroundBrush}\"/>\n            <Setter Property=\"BorderBrush\" Value=\"{DynamicResource HighlightBrush}\"/>\n            <Setter Property=\"Cursor\" Value=\"Arrow\"/>\n            <Setter Property=\"Padding\" Value=\"0\"/>\n        </Style>\n\t\t<Style Selector=\"MenuItem.ColumnActive\">\n\t\t\t<Setter Property=\"i:MenuItem.Icon\" Value=\"mdi-check-outline\" />\n\t\t</Style>\n\t\t<Style Selector=\"MenuItem.ColumnInactive\">\n\t\t\t<Setter Property=\"i:MenuItem.Icon\" Value=\"\" />\n\t\t</Style>\n\t\t<Style Selector=\"MenuItem.Bar Separator\">\n\t\t\t<Setter Property=\"Border.Background\" Value=\"{DynamicResource HighlightForegroundBrush}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"MenuItem.Bar\">\n\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"MenuItem.Bar:selected /template/ ContentPresenter#PART_HeaderPresenter\">\n\t\t\t<Setter Property=\"TextBlock.Foreground\" Value=\"{DynamicResource HighlightForegroundBrush}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"MenuItem.Bar:selected /template/ Border#PART_LayoutRoot\">\n\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource ThemeBackgroundBrush}\" />\n\t\t</Style>\n\t\t<Style Selector=\"Menu > MenuItem\">\n\t\t\t<Setter Property=\"Template\">\n\t\t\t\t<ControlTemplate>\n\t\t\t\t\t<Border Name=\"PART_LayoutRoot\"\n\t\t\t\t\t\t\tBackground=\"{TemplateBinding Background}\"\n\t\t\t\t\t\t\tBorderBrush=\"{TemplateBinding BorderBrush}\"\n\t\t\t\t\t\t\tBorderThickness=\"{TemplateBinding BorderThickness}\"\n\t\t\t\t\t\t\tCornerRadius=\"{TemplateBinding CornerRadius}\">\n\t\t\t\t\t\t<Panel>\n\t\t\t\t\t\t\t<ContentPresenter Name=\"PART_HeaderPresenter\"\n\t\t\t\t\t\t\t\t\t\t\t  Content=\"{TemplateBinding Header}\"\n\t\t\t\t\t\t\t\t\t\t\t  VerticalAlignment=\"Center\"\n\t\t\t\t\t\t\t\t\t\t\t  HorizontalAlignment=\"Stretch\"\n\t\t\t\t\t\t\t\t\t\t\t  Margin=\"{TemplateBinding Padding}\">\n\t\t\t\t\t\t\t\t<ContentPresenter.DataTemplates>\n\t\t\t\t\t\t\t\t\t<DataTemplate DataType=\"sys:String\">\n\t\t\t\t\t\t\t\t\t\t<AccessText Text=\"{Binding}\" />\n\t\t\t\t\t\t\t\t\t</DataTemplate>\n\t\t\t\t\t\t\t\t</ContentPresenter.DataTemplates>\n\t\t\t\t\t\t\t</ContentPresenter>\n\t\t\t\t\t\t\t<Popup Name=\"PART_Popup\"\n\t\t\t\t\t\t\t\t   WindowManagerAddShadowHint=\"False\"\n\t\t\t\t\t\t\t\t   MinWidth=\"{Binding Bounds.Width, RelativeSource={RelativeSource TemplatedParent}}\"\n\t\t\t\t\t\t\t\t   IsLightDismissEnabled=\"True\"\n\t\t\t\t\t\t\t\t   IsOpen=\"{TemplateBinding IsSubMenuOpen, Mode=TwoWay}\"\n\t\t\t\t\t\t\t\t   OverlayInputPassThroughElement=\"{Binding $parent[Menu]}\">\n\t\t\t\t\t\t\t\t<Border Background=\"{DynamicResource ThemeBackgroundBrush}\"\n\t\t\t\t\t\t\t\t\t\tBorderBrush=\"{DynamicResource MenuFlyoutPresenterBorderBrush}\"\n\t\t\t\t\t\t\t\t\t\tBorderThickness=\"{DynamicResource MenuFlyoutPresenterBorderThemeThickness}\"\n\t\t\t\t\t\t\t\t\t\tPadding=\"{DynamicResource MenuFlyoutPresenterThemePadding}\"\n\t\t\t\t\t\t\t\t\t\tMaxWidth=\"{DynamicResource FlyoutThemeMaxWidth}\"\n\t\t\t\t\t\t\t\t\t\tMinHeight=\"{DynamicResource MenuFlyoutThemeMinHeight}\"\n\t\t\t\t\t\t\t\t\t\tHorizontalAlignment=\"Stretch\"\n\t\t\t\t\t\t\t\t\t\tCornerRadius=\"{DynamicResource OverlayCornerRadius}\">\n\t\t\t\t\t\t\t\t\t<ScrollViewer Classes=\"menuscroller\">\n\t\t\t\t\t\t\t\t\t\t<ItemsPresenter Name=\"PART_ItemsPresenter\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tItems=\"{TemplateBinding Items}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tItemsPanel=\"{TemplateBinding ItemsPanel}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tItemTemplate=\"{TemplateBinding ItemTemplate}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tMargin=\"{DynamicResource MenuFlyoutScrollerMargin}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tGrid.IsSharedSizeScope=\"True\" />\n\t\t\t\t\t\t\t\t\t</ScrollViewer>\n\t\t\t\t\t\t\t\t</Border>\n\t\t\t\t\t\t\t</Popup>\n\t\t\t\t\t\t</Panel>\n\t\t\t\t\t</Border>\n\t\t\t\t</ControlTemplate>\n\t\t\t</Setter>\n\t\t</Style>\n\t\t<Style Selector=\"Button:pointerover /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"LightGray\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button:pressed /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"White\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button[IsEnabled=false] /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Opacity\" Value=\"0.5\"/>\n\t\t\t<Setter Property=\"Background\" Value=\"Transparent\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button.hyperlink\">\n\t\t\t<Setter Property=\"Background\" Value=\"Transparent\" />\n\t\t\t<Setter Property=\"BorderThickness\" Value=\"0\" />\n\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource ThemeForegroundBrush}\" />\n\t\t\t<Setter Property=\"HorizontalAlignment\" Value=\"Center\" />\n\t\t\t<Setter Property=\"Cursor\" Value=\"Hand\" />\n\t\t</Style>\n\t\t<Style Selector=\"Button.status\">\n\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource UpdateAvailableBrush}\" />\n\t\t</Style>\n\t\t<Style Selector=\"Button.status[Tag=Broken]\">\n\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource UpdateBrokenBrush}\" />\n\t\t</Style>\n\t\t<Style Selector=\"Button.status[Tag=Unofficial]\">\n\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource UpdateUnofficialBrush}\" />\n\t\t</Style>\n\t\t<Style Selector=\"Button.abstained\">\n\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource HighlightBrush}\" />\n\t\t\t<Setter Property=\"i:Attached.Icon\" Value=\"mdi-thumb-up-outline\" />\n\t\t</Style>\n\t\t<Style Selector=\"Button.endorsed\">\n\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource HighlightBrush}\" />\n\t\t\t<Setter Property=\"i:Attached.Icon\" Value=\"mdi-thumb-up\" />\n\t\t</Style>\n\t\t<Style Selector=\"Button.hyperlink:pointerover /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"Transparent\" />\n\t\t\t<Setter Property=\"TextBlock.Foreground\" Value=\"White\" />\n\t\t</Style>\n\t\t<Style Selector=\"Button.hyperlink:pressed /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"Transparent\" />\n\t\t\t<Setter Property=\"TextBlock.Foreground\" Value=\"{DynamicResource HighlightForegroundBrush}\" />\n\t\t</Style>\n\t\t<Style Selector=\"Button#exitButton:pointerover /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"Red\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button#exitButton:pressed /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"Pink\"/>\n\t\t</Style>\n\t\t<Style Selector=\"ComboBoxItem\">\n\t\t\t<Setter Property=\"FontWeight\" Value=\"Bold\"/>\n\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource ComboBoxItemBackgroundBrush}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"ComboBox\">\n\t\t\t<Setter Property=\"Background\" Value=\"Transparent\" />\n\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource ThemeBorderMidBrush}\" />\n\t\t\t<Setter Property=\"BorderThickness\" Value=\"{DynamicResource ThemeBorderThickness}\" />\n\t\t\t<Setter Property=\"HorizontalContentAlignment\" Value=\"Stretch\" />\n\t\t\t<Setter Property=\"VerticalContentAlignment\" Value=\"Center\" />\n\t\t\t<Setter Property=\"Padding\" Value=\"4\" />\n\t\t\t<Setter Property=\"MinHeight\" Value=\"20\" />\n\t\t\t<Setter Property=\"PlaceholderForeground\" Value=\"{DynamicResource ThemeForegroundBrush}\" />\n\t\t\t<Setter Property=\"ScrollViewer.HorizontalScrollBarVisibility\" Value=\"Disabled\" />\n\t\t\t<Setter Property=\"Template\">\n\t\t\t\t<ControlTemplate>\n\t\t\t\t\t<Border Name=\"border\"\n\t\t\t\t\t\t\tBackground=\"{TemplateBinding Background}\"\n\t\t\t\t\t\t\tBorderBrush=\"{TemplateBinding BorderBrush}\"\n\t\t\t\t\t\t\tBorderThickness=\"{TemplateBinding BorderThickness}\"\n\t\t\t\t\t\t\tCornerRadius=\"{TemplateBinding CornerRadius}\">\n\t\t\t\t\t\t<Grid ColumnDefinitions=\"*,Auto\">\n\t\t\t\t\t\t\t<TextBlock Name=\"PlaceholderTextBlock\"\n\t\t\t\t\t\t\t\t\t   Grid.Column=\"0\"\n\t\t\t\t\t\t\t\t\t   HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\"\n\t\t\t\t\t\t\t\t\t   VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\"\n\t\t\t\t\t\t\t\t\t   Margin=\"{TemplateBinding Padding}\"\n\t\t\t\t\t\t\t\t\t   Text=\"{TemplateBinding PlaceholderText}\"\n\t\t\t\t\t\t\t\t\t   Foreground=\"{TemplateBinding PlaceholderForeground}\"\n\t\t\t\t\t\t\t\t\t   IsVisible=\"{TemplateBinding SelectionBoxItem, Converter={x:Static ObjectConverters.IsNull}}\" />\n\t\t\t\t\t\t\t<ContentControl Content=\"{TemplateBinding SelectionBoxItem}\"\n\t\t\t\t\t\t\t\t\t\t\tContentTemplate=\"{TemplateBinding ItemTemplate}\"\n\t\t\t\t\t\t\t\t\t\t\tMargin=\"{TemplateBinding Padding}\"\n\t\t\t\t\t\t\t\t\t\t\tVerticalContentAlignment=\"{TemplateBinding VerticalContentAlignment}\"\n\t\t\t\t\t\t\t\t\t\t\tHorizontalContentAlignment=\"{TemplateBinding HorizontalContentAlignment}\" />\n\t\t\t\t\t\t\t<ToggleButton Name=\"toggle\"\n\t\t\t\t\t\t\t\t\t\t  BorderThickness=\"0\"\n\t\t\t\t\t\t\t\t\t\t  Background=\"Transparent\"\n\t\t\t\t\t\t\t\t\t\t  ClickMode=\"Press\"\n\t\t\t\t\t\t\t\t\t\t  Focusable=\"False\"\n\t\t\t\t\t\t\t\t\t\t  IsChecked=\"{TemplateBinding IsDropDownOpen, Mode=TwoWay}\"\n\t\t\t\t\t\t\t\t\t\t  Grid.Column=\"1\">\n\t\t\t\t\t\t\t\t<Path Fill=\"{DynamicResource ThemeForegroundBrush}\"\n\t\t\t\t\t\t\t\t\t  Width=\"8\"\n\t\t\t\t\t\t\t\t\t  Height=\"4\"\n\t\t\t\t\t\t\t\t\t  Stretch=\"Uniform\"\n\t\t\t\t\t\t\t\t\t  HorizontalAlignment=\"Center\"\n\t\t\t\t\t\t\t\t\t  VerticalAlignment=\"Center\"\n\t\t\t\t\t\t\t\t\t  Data=\"F1 M 301.14,-189.041L 311.57,-189.041L 306.355,-182.942L 301.14,-189.041 Z\" />\n\t\t\t\t\t\t\t</ToggleButton>\n\t\t\t\t\t\t\t<Popup Name=\"PART_Popup\"\n\t\t\t\t\t\t\t\t   IsOpen=\"{TemplateBinding IsDropDownOpen, Mode=TwoWay}\"\n\t\t\t\t\t\t\t\t   MinWidth=\"{Binding Bounds.Width, RelativeSource={RelativeSource TemplatedParent}}\"\n\t\t\t\t\t\t\t\t   MaxHeight=\"{TemplateBinding MaxDropDownHeight}\"\n\t\t\t\t\t\t\t\t   PlacementTarget=\"{TemplateBinding}\"\n\t\t\t\t\t\t\t\t   IsLightDismissEnabled=\"True\">\n\t\t\t\t\t\t\t\t<Border Background=\"{DynamicResource ThemeBackgroundBrush}\"\n\t\t\t\t\t\t\t\t\t\tBorderBrush=\"{DynamicResource ThemeBorderMidBrush}\"\n\t\t\t\t\t\t\t\t\t\tBorderThickness=\"1\">\n\t\t\t\t\t\t\t\t\t<ScrollViewer HorizontalScrollBarVisibility=\"{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}\"\n\t\t\t\t\t\t\t\t\t\t\t\t  VerticalScrollBarVisibility=\"{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}\">\n\t\t\t\t\t\t\t\t\t\t<ItemsPresenter Name=\"PART_ItemsPresenter\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tItems=\"{TemplateBinding Items}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tItemsPanel=\"{TemplateBinding ItemsPanel}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tItemTemplate=\"{TemplateBinding ItemTemplate}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tVirtualizationMode=\"{TemplateBinding VirtualizationMode}\" />\n\t\t\t\t\t\t\t\t\t</ScrollViewer>\n\t\t\t\t\t\t\t\t</Border>\n\t\t\t\t\t\t\t</Popup>\n\t\t\t\t\t\t</Grid>\n\t\t\t\t\t</Border>\n\t\t\t\t</ControlTemplate>\n\t\t\t</Setter>\n\t\t</Style>\n\t\t<Style Selector=\"ComboBoxItem:pointerover /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource HighlightBrush}\" />\n\t\t</Style>\n\t\t<Style Selector=\"ComboBoxItem:selected /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource ComboBoxItemSelectedBackgroundBrush}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"ComboBox /template/ Path#DropDownGlyph\">\n\t\t\t<Setter Property=\"Fill\" Value=\"{DynamicResource HighlightForegroundBrush}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"CheckBox:checked /template/ Border#NormalRectangle\">\n\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource ThemeBackgroundBrush}\" />\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource ThemeBackgroundBrush}\" />\n\t\t</Style>\n\t\t<Style Selector=\"CheckBox:checked /template/ Path#CheckGlyph\">\n\t\t\t<Setter Property=\"Fill\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"CheckBox.OptionClickbox:unchecked /template/ Border#NormalRectangle\">\n\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource HighlightForegroundBrush}\" />\n\t\t</Style>\n\t\t<Style Selector=\"CheckBox.OptionClickbox:checked /template/ Border#NormalRectangle\">\n\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource HighlightForegroundBrush}\" />\n\t\t</Style>\n\t\t<Style Selector=\"CheckBox.OptionClickbox:pointerover /template/ ContentPresenter#ContentPresenter\">\n\t\t\t<Setter Property=\"TextBlock.Foreground\" Value=\"{DynamicResource HighlightForegroundBrush}\" />\n\t\t</Style>\n\t\t<Style Selector=\"DataGridCell.centered\">\n\t\t\t<Setter Property=\"TextBlock.TextAlignment\" Value=\"Center\"/>\n\t\t\t<Setter Property=\"TextBlock.HorizontalAlignment\" Value=\"Center\"/>\n\t\t\t<Setter Property=\"TextBlock.VerticalAlignment\" Value=\"Center\"/>\n\t\t</Style>\n\t\t<Style Selector=\"DataGridRowGroupHeader\">\n\t\t\t<Setter Property=\"FontWeight\" Value=\"Bold\"/>\n\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource DataGridHeaderBrush}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"DataGridColumnHeader\">\n\t\t\t<Setter Property=\"FontWeight\" Value=\"Bold\"/>\n\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource DataGridHeaderBrush}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"DataGridColumnHeader[Content=Enabled]\">\n\t\t\t<Setter Property=\"Margin\" Value=\"-13 0 0 0\"/>\n\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource DataGridHeaderBrush}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"DataGridRow:selected /template/ Rectangle#BackgroundRectangle\">\n\t\t\t<Setter Property=\"Fill\" Value=\"{DynamicResource HighlightForegroundBrush}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"ListBox#FilterBox\">\n\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource ThemeBorderMidBrush}\" />\n\t\t\t<Setter Property=\"BorderThickness\" Value=\"1\" />\n\t\t</Style>\n\t\t<Style Selector=\"ListBoxItem\">\n\t\t\t<Setter Property=\"HorizontalContentAlignment\" Value=\"Center\" />\n\t\t</Style>\n\t\t<Style Selector=\"ListBoxItem:pointerover /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"Transparent\"/>\n\t\t\t<Setter Property=\"HorizontalContentAlignment\" Value=\"Center\" />\n\t\t</Style>\n\t\t<Style Selector=\"ListBoxItem:selected /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"Transparent\"/>\n\t\t</Style>\n\t\t<Style Selector=\"ListBoxItem.FilterItemBox:not(:selected) /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"TextBlock.Foreground\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t\t<Setter Property=\"TextBlock.Opacity\" Value=\"0.25\"/>\n\t\t</Style>\n\t\t<Style Selector=\"ListBoxItem.FilterItemBox:selected /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"TextBlock.Foreground\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t\t<Setter Property=\"TextBlock.Opacity\" Value=\"1\"/>\n\t\t</Style>\n\t\t<Style Selector=\"TextBox:focus\">\n\t\t\t<Setter Property=\"Foreground\" Value=\"White\"/>\n\t\t</Style>\n\t\t<Style Selector=\"TextBox:focus /template/ Border\">\n\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource ThemeControlMidBrush}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"TextBox:not(:focus)\">\n\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource ThemeBackgroundBrush}\"/>\n\t\t</Style>\n\t</Window.Styles>\n\n\t<NativeMenu.Menu>\n\t\t<NativeMenu>\n\t\t\t<NativeMenuItem Header=\"{i18n:Translate ui.main_window.menu_headers.file}\">\n\t\t\t\t<NativeMenu>\n\t\t\t\t\t<NativeMenuItem Header=\"{i18n:Translate ui.main_window.menu_items.add_mods}\" Click=\"AddMod_Click\" />\n\t\t\t\t\t<NativeMenuItemSeparator/>\n\t\t\t\t\t<NativeMenuItem Header=\"{i18n:Translate ui.main_window.menu_items.start_smapi}\" Click=\"Smapi_Click\" />\n\t\t\t\t\t<NativeMenuItemSeparator/>\n\t\t\t\t\t<NativeMenuItem Header=\"{i18n:Translate ui.main_window.menu_items.exit}\" Click=\"Exit_Click\" />\n\t\t\t\t</NativeMenu>\n\t\t\t</NativeMenuItem>\n\t\t\t<NativeMenuItem Header=\"{i18n:Translate ui.main_window.menu_headers.view}\">\n\t\t\t\t<NativeMenu>\n\t\t\t\t\t<NativeMenuItem Header=\"{i18n:Translate ui.main_window.menu_items.settings}\" Click=\"Settings_Click\" />\n\t\t\t\t\t<NativeMenuItemSeparator/>\n\t\t\t\t\t<NativeMenuItem Header=\"{i18n:Translate ui.main_window.menu_items.stardrop_log_file}\" Click=\"LogFile_Click\" />\n\t\t\t\t\t<NativeMenuItem Header=\"{i18n:Translate ui.main_window.menu_items.smapi_log_file}\" Click=\"SmapiLogFile_Click\" />\n\t\t\t\t</NativeMenu>\n\t\t\t</NativeMenuItem>\n\t\t\t<NativeMenuItem Header=\"{i18n:Translate ui.main_window.menu_headers.tools}\">\n\t\t\t\t<NativeMenu>\n\t\t\t\t\t<NativeMenuItem Header=\"{i18n:Translate ui.main_window.menu_items.refresh_mod_list}\" Click=\"ModListRefresh_Click\" />\n\t\t\t\t\t<NativeMenuItem Header=\"{i18n:Translate ui.main_window.menu_items.check_for_mod_updates}\" Click=\"ModUpdateCheck_Click\" />\n\t\t\t\t\t<NativeMenuItemSeparator/>\n\t\t\t\t\t<NativeMenuItem Header=\"{i18n:Translate ui.main_window.menu_items.enable_all_mods}\" Click=\"EnableAllMods_Click\" />\n\t\t\t\t\t<NativeMenuItem Header=\"{i18n:Translate ui.main_window.menu_items.disable_all_mods}\" Click=\"DisableAllMods_Click\" />\n\t\t\t\t\t<NativeMenuItemSeparator/>\n\t\t\t\t\t<NativeMenuItem Header=\"{i18n:Translate ui.main_window.menu_items.check_for_stardrop_update}\" Click=\"StardropUpdate_Click\" />\n\t\t\t\t\t<NativeMenuItem Header=\"{i18n:Translate ui.main_window.menu_items.stardrop_repository}\" Command=\"{Binding OpenBrowser}\" CommandParameter=\"https://github.com/Floogen/Stardrop\"/>\n\t\t\t\t\t<NativeMenuItemSeparator/>\n\t\t\t\t\t<NativeMenuItem Header=\"{i18n:Translate ui.main_window.menu_items.check_for_SMAPI_update}\" Click=\"SMAPIUpdate_Click\" />\n\t\t\t\t</NativeMenu>\n\t\t\t</NativeMenuItem>\n\t\t\t<NativeMenuItem Header=\"{i18n:Translate ui.main_window.menu_headers.nexus}\">\n\t\t\t\t<NativeMenu>\n\t\t\t\t\t<NativeMenuItem Header=\"{i18n:Translate ui.main_window.menu_items.nexus_connection}\" Click=\"NexusConnection_Click\" />\n\t\t\t\t\t<NativeMenuItem Header=\"{i18n:Translate ui.main_window.menu_items.nexus_install_all}\" Click=\"NexusModBulkInstall_Click\" />\n\t\t\t\t\t<NativeMenuItem Header=\"{i18n:Translate ui.main_window.menu_items.nexus_install_enabled}\" Click=\"NexusModBulkInstallEnabledOnly_Click\" />\n\t\t\t\t</NativeMenu>\n\t\t\t</NativeMenuItem>\n\t\t</NativeMenu>\n\t</NativeMenu.Menu>\n\n\t<Grid>\n\t\t<Grid.RowDefinitions>\n\t\t\t<RowDefinition Height=\"Auto\" />\n\t\t\t<RowDefinition Height=\"10\" />\n\t\t\t<RowDefinition Height=\"Auto\" />\n\t\t\t<RowDefinition Height=\"*\" />\n\t\t\t<RowDefinition Height=\"Auto\" />\n\t\t\t<RowDefinition Height=\"Auto\" />\n\t\t</Grid.RowDefinitions>\n\n\t\t<Grid.ColumnDefinitions>\n\t\t\t<ColumnDefinition Width=\"2*\" />\n\t\t\t<ColumnDefinition Width=\"Auto\" />\n\t\t</Grid.ColumnDefinitions>\n\n\t\t<Border Name=\"menuBorder\" Grid.Row=\"0\" BorderBrush=\"{DynamicResource HighlightBrush}\" BorderThickness=\"0 0 0 2\" Grid.ColumnSpan=\"2\">\n\t\t\t<Menu Name=\"mainMenu\" IsVisible=\"{Binding ShowMainMenu}\" HorizontalAlignment=\"Left\" KeyboardNavigation.TabNavigation=\"None\">\n\t\t\t\t<Image Name=\"menuIcon\" Source=\"/Assets/icon.ico\" Stretch=\"None\"/>\n\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_headers.file}\" Margin=\"-10 0 0 0\" Classes=\"Bar\">\n\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.add_mods}\" Click=\"AddMod_Click\" Classes=\"Bar\"/>\n\t\t\t\t\t<Separator/>\n\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.start_smapi}\" Click=\"Smapi_Click\" Classes=\"Bar\"/>\n\t\t\t\t\t<Separator/>\n\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.exit}\" Click=\"Exit_Click\" Classes=\"Bar\"/>\n\t\t\t\t</MenuItem>\n\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_headers.view}\" Margin=\"-10 0 0 0\" Classes=\"Bar\">\n\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.settings}\" Click=\"Settings_Click\" Classes=\"Bar\"/>\n\t\t\t\t\t<Separator/>\n\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.stardrop_log_file}\" Click=\"LogFile_Click\" Classes=\"Bar\"/>\n\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.smapi_log_file}\" Click=\"SmapiLogFile_Click\" Classes=\"Bar\"/>\n\t\t\t\t</MenuItem>\n\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_headers.tools}\" Margin=\"-10 0 0 0\" Classes=\"Bar\">\n\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.refresh_mod_list}\" Click=\"ModListRefresh_Click\" Classes=\"Bar\"/>\n\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.check_for_mod_updates}\" Click=\"ModUpdateCheck_Click\" Classes=\"Bar\"/>\n\t\t\t\t\t<Separator/>\n\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.enable_all_mods}\" Click=\"EnableAllMods_Click\" Classes=\"Bar\"/>\n\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.disable_all_mods}\" Click=\"DisableAllMods_Click\" Classes=\"Bar\"/>\n\t\t\t\t\t<Separator/>\n\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.check_for_stardrop_update}\" Click=\"StardropUpdate_Click\" Classes=\"Bar\"/>\n\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.stardrop_repository}\" Classes=\"Bar\" Command=\"{Binding OpenBrowser}\" CommandParameter=\"https://github.com/Floogen/Stardrop\"/>\n\t\t\t\t\t<Separator/>\n\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.check_for_SMAPI_update}\" Click=\"SMAPIUpdate_Click\" Classes=\"Bar\"/>\n\t\t\t\t</MenuItem>\n\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_headers.nexus}\" Margin=\"-10 0 0 0\" Classes=\"Bar\">\n\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.nexus_connection}\" Click=\"NexusConnection_Click\" Classes=\"Bar\"/>\n\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.nexus_install_all}\" Click=\"NexusModBulkInstall_Click\" Classes=\"Bar\" />\n\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.nexus_install_enabled}\" Click=\"NexusModBulkInstallEnabledOnly_Click\" Classes=\"Bar\" />\n\t\t\t\t</MenuItem>\n\t\t\t</Menu>\n\t\t</Border>\n\t\t<TextBlock Text=\"☆ Stardrop ☆\" IsVisible=\"{Binding ShowTitle}\" IsHitTestVisible=\"False\" Grid.Row=\"0\" Grid.ColumnSpan=\"2\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Center\" FontSize=\"20\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" />\n\t\t<Menu Name=\"windowMenu\" IsVisible=\"{Binding ShowWindowMenu}\" HorizontalAlignment=\"Right\" KeyboardNavigation.TabNavigation=\"None\" Grid.Column=\"1\">\n\t\t\t<Button Name=\"minimizeButton\" i:Attached.Icon=\"mdi-window-minimize\" Margin=\"0 0 -10 0\" Foreground=\"{DynamicResource ThemeForegroundBrush}\"  Background=\"Transparent\"/>\n\t\t\t<Button Name=\"maximizeButton\" i:Attached.Icon=\"mdi-window-maximize\" Margin=\"0 0 -10 0\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Background=\"Transparent\"/>\n\t\t\t<Button Name=\"exitButton\" i:Attached.Icon=\"mdi-close\" Margin=\"0 0 -10 0\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Background=\"Transparent\"/>\n\t\t</Menu>\n\t\t<!-- ToolBar -->\n\t\t<Separator />\n\n\t\t<DockPanel Name=\"profileBar\" Grid.Row=\"2\" Grid.Column=\"0\">\n\t\t\t<TextBlock Text=\"{i18n:Translate ui.main_window.labels.profile}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Padding=\"7\" Focusable=\"False\" VerticalAlignment=\"Center\" />\n\t\t\t<ComboBox x:Name=\"profileComboBox\" Background=\"{DynamicResource ThemeBackgroundBrush}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" VerticalAlignment=\"Center\"  FontWeight=\"Bold\" BorderBrush=\"{DynamicResource HighlightBrush}\" SelectedIndex=\"0\" Width=\"300\">\n\t\t\t\t<ComboBox.ItemTemplate>\n\t\t\t\t\t<DataTemplate>\n\t\t\t\t\t\t<StackPanel>\n\t\t\t\t\t\t\t<TextBlock Text=\"{Binding Path=Name}\"/>\n\t\t\t\t\t\t</StackPanel>\n\t\t\t\t\t</DataTemplate>\n\t\t\t\t</ComboBox.ItemTemplate>\n\t\t\t</ComboBox>\n\t\t\t<Button Name=\"editProfilesButton\" i:Attached.Icon=\"mdi-playlist-edit\" VerticalAlignment=\"Center\" Margin=\"5 0 0 0\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Background=\"Transparent\" BorderBrush=\"{DynamicResource HighlightBrush}\"/>\n\t\t\t<Button Name=\"saveConfigsToProfile\" Content=\"{i18n:Translate ui.main_window.buttons.save_configs}\" VerticalAlignment=\"Center\" Margin=\"5 0 0 0\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Background=\"Transparent\" BorderBrush=\"{DynamicResource HighlightBrush}\"/>\n\t\t\t<Button Name=\"saveProfileChanges\" IsVisible=\"{Binding ShowSaveProfileChanges}\" Content=\"{i18n:Translate ui.main_window.buttons.save_profile}\" VerticalAlignment=\"Center\" Margin=\"5 0 0 0\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Background=\"Transparent\" BorderBrush=\"{DynamicResource HighlightBrush}\"/>\n\t\t\t<Grid>\n\t\t\t\t<Grid.ColumnDefinitions>\n\t\t\t\t\t<ColumnDefinition Width=\"Auto\" />\n\t\t\t\t\t<ColumnDefinition Width=\"Auto\" />\n\t\t\t\t\t<ColumnDefinition Width=\"Auto\" />\n\t\t\t\t\t<ColumnDefinition Width=\"Auto\" />\n\t\t\t\t</Grid.ColumnDefinitions>\n\t\t\t\t<StackPanel Grid.Column=\"0\" VerticalAlignment=\"Center\" Orientation=\"Horizontal\" Margin=\"10 0 0 2\">\n\t\t\t\t\t<TextBlock Text=\"{i18n:Translate ui.main_window.labels.enabled_mods}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t\t\t\t<TextBlock Text=\"{Binding EnabledModCount}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t\t\t\t<TextBlock Text=\" | \" Foreground=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t\t\t\t<TextBlock Text=\"{i18n:Translate ui.main_window.labels.total_mods}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t\t\t\t<TextBlock Text=\"{Binding ActualModCount}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t\t\t\t<TextBlock Text=\" | \" Foreground=\"{DynamicResource ThemeForegroundBrush}\" VerticalAlignment=\"Center\" Margin=\"0 0 0 2\"/>\n\t\t\t\t\t<TextBlock Text=\"{i18n:Translate ui.main_window.labels.updatable_mods}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t\t\t\t<TextBlock Text=\"{Binding ModsWithCachedUpdates}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t\t\t</StackPanel>\n\t\t\t</Grid>\n\t\t</DockPanel>\n\t\t<DockPanel Name=\"toolBar\" Grid.Row=\"2\"  Grid.Column=\"1\">\n\t\t\t<TextBlock Text=\"{Binding SmapiVersion}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" VerticalAlignment=\"Bottom\" HorizontalAlignment=\"Right\" Margin=\"30 0 0 0\" />\n\t\t\t<Button Name=\"smapiButton\" IsEnabled=\"{Binding !IsLocked}\" Background=\"Transparent\" HorizontalAlignment=\"Right\" Width=\"60\" Height=\"40\" VerticalAlignment=\"Center\"  Margin=\"0 0 10 0\">\n\t\t\t\t<Panel>\n\t\t\t\t\t<Image Source=\"/Assets/smapi.png\" Stretch=\"Uniform\" />\n\t\t\t\t</Panel>\n\t\t\t</Button>\n\t\t</DockPanel>\n\n\t\t<Border Name=\"dragOverBorder\" Margin=\"10\" DragDrop.AllowDrop=\"True\" Grid.Row=\"3\" Grid.ColumnSpan=\"2\" BorderBrush=\"{DynamicResource HighlightBrush}\" BorderThickness=\"2\">\n\t\t\t<DataGrid Name=\"modGrid\" Foreground=\"{DynamicResource DataGridRowForeground}\" Background=\"{DynamicResource DataGridRowBackground}\" AlternatingRowBackground=\"{DynamicResource AlternativeDataGridRowBackground}\" CanUserSortColumns=\"True\" CanUserResizeColumns=\"True\" ScrollViewer.HorizontalScrollBarVisibility=\"Disabled\">\n\t\t\t\t<DataGrid.Resources>\n\t\t\t\t\t<ContextMenu x:Key=\"GridColumnContextMenu\" Name=\"gridColumnContextMenu\" DataContext=\"{Binding $parent[DataGrid].DataContext}\">\n\t\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.context.columns.show_hide}\" />\n\t\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_headers.requirements}\"  Classes=\"ColumnInactive\" Command=\"{Binding ChangeColumnVisibility}\" CommandParameter=\"{Binding $self}\" />\n\t\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_headers.author}\" Classes=\"ColumnInactive\" Command=\"{Binding ChangeColumnVisibility}\" CommandParameter=\"{Binding $self}\" />\n\t\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_headers.install_timestamp}\"  Classes=\"ColumnInactive\" Command=\"{Binding ChangeColumnVisibility}\" CommandParameter=\"{Binding $self}\" />\n\t\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_headers.last_updated}\"  Classes=\"ColumnActive\" Command=\"{Binding ChangeColumnVisibility}\" CommandParameter=\"{Binding $self}\" />\n\t\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_headers.version}\"  Classes=\"ColumnActive\" Command=\"{Binding ChangeColumnVisibility}\" CommandParameter=\"{Binding $self}\" />\n\t\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_headers.configuration}\"  Classes=\"ColumnInactive\" Command=\"{Binding ChangeColumnVisibility}\" CommandParameter=\"{Binding $self}\" />\n\t\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.context.cancel}\" />\n\t\t\t\t\t</ContextMenu>\n\t\t\t\t\t<ContextMenu x:Key=\"GridRowContextMenu\">\n\t\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.context.open}\">\n\t\t\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.context.open_containing_folder}\" Click=\"ModGridMenuRow_OpenFolderPath\" />\n\t\t\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.context.open_mod_page}\" Click=\"ModGridMenuRow_OpenModPage\" IsVisible=\"{Binding ModPageUri, Converter={x:Static StringConverters.IsNotNullOrEmpty}}\" />\n\t\t\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.context.open_manifest}\" Command=\"{Binding $parent[DataGrid].DataContext.OpenBrowser}\" CommandParameter=\"{Binding ManifestFilePath}\" />\n\t\t\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.context.open_config}\" Command=\"{Binding $parent[DataGrid].DataContext.OpenBrowser}\" CommandParameter=\"{Binding Config.FilePath}\" IsVisible=\"{Binding HasConfig}\" />\n\t\t\t\t\t\t</MenuItem>\n\t\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.context.filters}\">\n\t\t\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.context.show_whole_mod_group}\" Click=\"ModGridMenuRow_ShowWholeModGroup\" />\n\t\t\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.context.show_authors_mods}\" Click=\"ModGridMenuRow_ShowAuthorsMods\" />\n\t\t\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.context.clear_filter}\" Click=\"ModGridMenuRow_ClearFilter\" />\n\t\t\t\t\t\t</MenuItem>\n\t\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.context.options}\">\n\t\t\t\t\t\t\t<MenuItem Header=\"{Binding ChangeStateText}\" Click=\"ModGridMenuRow_ChangeState\" IsVisible=\"{Binding !$parent[DataGrid].DataContext.AreModGroupsEnabled}\" />\n\t\t\t\t\t\t\t<MenuItem Header=\"{Binding ChangeStateText}\" IsVisible=\"{Binding $parent[DataGrid].DataContext.AreModGroupsEnabled}\">\n\t\t\t\t\t\t\t\t<MenuItem Header=\"{Binding ChangeStateText}\" Click=\"ModGridMenuRow_ChangeState\" />\n\t\t\t\t\t\t\t\t<MenuItem Header=\"{Binding ChangeWholeModGroupStateText}\" Click=\"ModGridMenuRow_ChangeWholeModGroupState\" />\n\t\t\t\t\t\t\t</MenuItem>\n\t\t\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.context.delete}\" Click=\"ModGridMenuRow_Delete\" />\n\t\t\t\t\t\t</MenuItem>\n\t\t\t\t\t\t<MenuItem Header=\"{i18n:Translate ui.main_window.menu_items.context.cancel}\" />\n\t\t\t\t\t</ContextMenu>\n\t\t\t\t</DataGrid.Resources>\n\t\t\t\t<DataGrid.Styles>\n\t\t\t\t\t<Style Selector=\"DataGridColumnHeader\">\n\t\t\t\t\t\t<Setter Property=\"ContextMenu\" Value=\"{StaticResource GridColumnContextMenu}\" />\n\t\t\t\t\t</Style>\n\t\t\t\t\t<Style Selector=\"DataGridRow\">\n\t\t\t\t\t\t<Setter Property=\"ContextMenu\" Value=\"{StaticResource GridRowContextMenu}\" />\n\t\t\t\t\t\t<Setter Property=\"ToolTip.Tip\" Value=\"{Binding Summary}\" />\n\t\t\t\t\t</Style>\n\t\t\t\t</DataGrid.Styles>\n\t\t\t\t<DataGrid.Columns>\n\t\t\t\t\t<DataGridTemplateColumn IsVisible=\"{Binding ShowModThumbnails}\" Header=\"\" Width=\"150\" CanUserResize=\"False\" CanUserSort=\"False\">\n\t\t\t\t\t\t<DataGridTemplateColumn.CellTemplate>\n\t\t\t\t\t\t\t<DataTemplate>\n\t\t\t\t\t\t\t\t<VirtualizingStackPanel>\n\t\t\t\t\t\t\t\t\t<Image Height=\"125\" Width=\"125\" Source=\"{Binding NexusModThumbnailFile}\" Stretch=\"Uniform\" />\n\t\t\t\t\t\t\t\t</VirtualizingStackPanel>\n\t\t\t\t\t\t\t</DataTemplate>\n\t\t\t\t\t\t</DataGridTemplateColumn.CellTemplate>\n\t\t\t\t\t</DataGridTemplateColumn>\n\t\t\t\t\t<DataGridTemplateColumn Header=\"{i18n:Translate ui.main_window.menu_headers.enabled}\" Width=\"25\" CanUserResize=\"False\" CanUserSort=\"True\" SortMemberPath=\"IsEnabled\">\n\t\t\t\t\t\t<DataGridTemplateColumn.CellTemplate>\n\t\t\t\t\t\t\t<DataTemplate>\n\t\t\t\t\t\t\t\t<CheckBox Content=\"{Binding UniqueId}\" IsChecked=\"{Binding IsEnabled}\" Click=\"EnabledBox_Clicked\" Margin=\"5 0 0 0\"/>\n\t\t\t\t\t\t\t</DataTemplate>\n\t\t\t\t\t\t</DataGridTemplateColumn.CellTemplate>\n\t\t\t\t\t</DataGridTemplateColumn>\n\t\t\t\t\t<DataGridTemplateColumn SortMemberPath=\"Name\">\n\t\t\t\t\t\t<DataGridTemplateColumn.Header>\n\t\t\t\t\t\t\t<TextBlock Text=\"{i18n:Translate ui.main_window.menu_headers.mod_name}\" TextAlignment=\"Center\" />\n\t\t\t\t\t\t</DataGridTemplateColumn.Header>\n\t\t\t\t\t\t<DataGridTemplateColumn.CellTemplate>\n\t\t\t\t\t\t\t<DataTemplate>\n\t\t\t\t\t\t\t\t<TextBlock Text=\"{Binding Name}\" TextAlignment=\"Left\" VerticalAlignment=\"Center\" Margin=\"7 0 0 0\" />\n\t\t\t\t\t\t\t</DataTemplate>\n\t\t\t\t\t\t</DataGridTemplateColumn.CellTemplate>\n\t\t\t\t\t</DataGridTemplateColumn>\n\t\t\t\t\t<DataGridTemplateColumn CellStyleClasses=\"centered\" IsVisible=\"{Binding ShowEndorsements}\">\n\t\t\t\t\t\t<DataGridTemplateColumn.Header>\n\t\t\t\t\t\t\t<TextBlock Text=\"{i18n:Translate ui.main_window.menu_headers.endorsement}\" TextAlignment=\"Center\" />\n\t\t\t\t\t\t</DataGridTemplateColumn.Header>\n\t\t\t\t\t\t<DataGridTemplateColumn.CellTemplate>\n\t\t\t\t\t\t\t<DataTemplate>\n\t\t\t\t\t\t\t\t<Button Name=\"endorsementButton\" Tag=\"{Binding UniqueId}\" Classes=\"hyperlink status\" Classes.endorsed=\"{Binding IsEndorsed}\" Classes.abstained=\"{Binding !IsEndorsed}\" Click=\"EndorsementButton_Click\" IsVisible=\"{Binding NexusModId, Converter={x:Static StringConverters.IsNotNullOrEmpty}}\"/>\n\t\t\t\t\t\t\t</DataTemplate>\n\t\t\t\t\t\t</DataGridTemplateColumn.CellTemplate>\n\t\t\t\t\t</DataGridTemplateColumn>\n\t\t\t\t\t<DataGridTextColumn Binding=\"{Binding UniqueId}\" IsVisible=\"False\">\n\t\t\t\t\t\t<DataGridTextColumn.Header>\n\t\t\t\t\t\t\t<TextBlock Text=\"{i18n:Translate ui.main_window.menu_headers.unique_id}\" TextAlignment=\"Center\" />\n\t\t\t\t\t\t</DataGridTextColumn.Header>\n\t\t\t\t\t</DataGridTextColumn>\n\t\t\t\t\t<DataGridTextColumn Binding=\"{Binding Author}\" CellStyleClasses=\"centered\" IsVisible=\"False\">\n\t\t\t\t\t\t<DataGridTextColumn.Header>\n\t\t\t\t\t\t\t<TextBlock Text=\"{i18n:Translate ui.main_window.menu_headers.author}\" TextAlignment=\"Center\" />\n\t\t\t\t\t\t</DataGridTextColumn.Header>\n\t\t\t\t\t</DataGridTextColumn>\n\t\t\t\t\t<DataGridTextColumn Binding=\"{Binding InstallTimestamp}\" IsVisible=\"False\">\n\t\t\t\t\t\t<DataGridTextColumn.Header>\n\t\t\t\t\t\t\t<TextBlock Text=\"{i18n:Translate ui.main_window.menu_headers.install_timestamp}\" TextAlignment=\"Center\" />\n\t\t\t\t\t\t</DataGridTextColumn.Header>\n\t\t\t\t\t</DataGridTextColumn>\n\t\t\t\t\t<DataGridTextColumn Binding=\"{Binding LastUpdateTimestamp}\" IsVisible=\"True\">\n\t\t\t\t\t\t<DataGridTextColumn.Header>\n\t\t\t\t\t\t\t<TextBlock Text=\"{i18n:Translate ui.main_window.menu_headers.last_updated}\" TextAlignment=\"Center\" />\n\t\t\t\t\t\t</DataGridTextColumn.Header>\n\t\t\t\t\t</DataGridTextColumn>\n\t\t\t\t\t<DataGridTemplateColumn IsVisible=\"False\" SortMemberPath=\"HardRequirements.Count\" CellStyleClasses=\"centered\">\n\t\t\t\t\t\t<DataGridTemplateColumn.Header>\n\t\t\t\t\t\t\t<TextBlock Text=\"{i18n:Translate ui.main_window.menu_headers.requirements}\" TextAlignment=\"Center\" />\n\t\t\t\t\t\t</DataGridTemplateColumn.Header>\n\t\t\t\t\t\t<DataGridTemplateColumn.CellTemplate>\n\t\t\t\t\t\t\t<DataTemplate>\n\t\t\t\t\t\t\t\t<ListBox Items=\"{Binding HardRequirements}\" Background=\"Transparent\">\n\t\t\t\t\t\t\t\t\t<ListBox.ItemTemplate>\n\t\t\t\t\t\t\t\t\t\t<DataTemplate>\n\t\t\t\t\t\t\t\t\t\t\t<TextBlock Text=\"{Binding Path=Name}\" Foreground=\"{DynamicResource DataGridRowForeground}\" />\n\t\t\t\t\t\t\t\t\t\t</DataTemplate>\n\t\t\t\t\t\t\t\t\t</ListBox.ItemTemplate>\n\t\t\t\t\t\t\t\t</ListBox>\n\t\t\t\t\t\t\t</DataTemplate>\n\t\t\t\t\t\t</DataGridTemplateColumn.CellTemplate>\n\t\t\t\t\t</DataGridTemplateColumn>\n\t\t\t\t\t<DataGridTemplateColumn SortMemberPath=\"MissingRequirements.Count\" CellStyleClasses=\"centered\">\n\t\t\t\t\t\t<DataGridTemplateColumn.Header>\n\t\t\t\t\t\t\t<TextBlock Text=\"{i18n:Translate ui.main_window.menu_headers.missing_requirements}\" TextAlignment=\"Center\" />\n\t\t\t\t\t\t</DataGridTemplateColumn.Header>\n\t\t\t\t\t\t<DataGridTemplateColumn.CellTemplate>\n\t\t\t\t\t\t\t<DataTemplate>\n\t\t\t\t\t\t\t\t<ListBox Items=\"{Binding MissingRequirements}\" Background=\"Transparent\">\n\t\t\t\t\t\t\t\t\t<ListBox.ItemTemplate>\n\t\t\t\t\t\t\t\t\t\t<DataTemplate>\n\t\t\t\t\t\t\t\t\t\t\t<Button Content=\"{Binding Path=Name}\" Classes=\"hyperlink\" Command=\"{Binding $parent[DataGrid].DataContext.OpenBrowser}\" CommandParameter=\"{Binding GenericLink}\" Foreground=\"{DynamicResource HighlightForegroundBrush}\"/>\n\t\t\t\t\t\t\t\t\t\t</DataTemplate>\n\t\t\t\t\t\t\t\t\t</ListBox.ItemTemplate>\n\t\t\t\t\t\t\t\t</ListBox>\n\t\t\t\t\t\t\t</DataTemplate>\n\t\t\t\t\t\t</DataGridTemplateColumn.CellTemplate>\n\t\t\t\t\t</DataGridTemplateColumn>\n\t\t\t\t\t<DataGridTextColumn Binding=\"{Binding ParsedVersion}\" CellStyleClasses=\"centered\" IsVisible=\"True\">\n\t\t\t\t\t\t<DataGridTextColumn.Header>\n\t\t\t\t\t\t\t<TextBlock Text=\"{i18n:Translate ui.main_window.menu_headers.version}\" TextAlignment=\"Center\" />\n\t\t\t\t\t\t</DataGridTextColumn.Header>\n\t\t\t\t\t</DataGridTextColumn>\n\t\t\t\t\t<DataGridTemplateColumn SortMemberPath=\"HasConfig\" CellStyleClasses=\"centered\" IsVisible=\"False\" >\n\t\t\t\t\t\t<DataGridTemplateColumn.Header>\n\t\t\t\t\t\t\t<TextBlock Text=\"{i18n:Translate ui.main_window.menu_headers.configuration}\" TextAlignment=\"Center\" />\n\t\t\t\t\t\t</DataGridTemplateColumn.Header>\n\t\t\t\t\t\t<DataGridTemplateColumn.CellTemplate>\n\t\t\t\t\t\t\t<DataTemplate>\n\t\t\t\t\t\t\t\t<Button Content=\"{i18n:Translate ui.main_window.hyperlinks.click_to_open}\" Classes=\"hyperlink\" IsVisible=\"{Binding HasConfig}\" Command=\"{Binding $parent[DataGrid].DataContext.OpenBrowser}\" CommandParameter=\"{Binding Config.FilePath}\" Foreground=\"{DynamicResource HighlightForegroundBrush}\"/>\n\t\t\t\t\t\t\t</DataTemplate>\n\t\t\t\t\t\t</DataGridTemplateColumn.CellTemplate>\n\t\t\t\t\t</DataGridTemplateColumn>\n\t\t\t\t\t<DataGridTemplateColumn SortMemberPath=\"ParsedStatus\" CellStyleClasses=\"centered\" >\n\t\t\t\t\t\t<DataGridTemplateColumn.Header>\n\t\t\t\t\t\t\t<TextBlock Text=\"{i18n:Translate ui.main_window.menu_headers.status}\" TextAlignment=\"Center\" />\n\t\t\t\t\t\t</DataGridTemplateColumn.Header>\n\t\t\t\t\t\t<DataGridTemplateColumn.CellTemplate>\n\t\t\t\t\t\t\t<DataTemplate>\n\t\t\t\t\t\t\t\t<Button Name=\"statusButton\" Tag=\"{Binding Status, Converter={StaticResource EnumToStringConverter}}\" Classes=\"hyperlink status\" Content=\"{Binding ParsedStatus}\" Command=\"{Binding $parent[DataGrid].DataContext.OpenBrowser}\" CommandParameter=\"{Binding UpdateUri}\"/>\n\t\t\t\t\t\t\t</DataTemplate>\n\t\t\t\t\t\t</DataGridTemplateColumn.CellTemplate>\n\t\t\t\t\t</DataGridTemplateColumn>\n\t\t\t\t\t<DataGridTemplateColumn SortMemberPath=\"InstallStatus\" CellStyleClasses=\"centered\" IsVisible=\"{Binding ShowInstalls}\" >\n\t\t\t\t\t\t<DataGridTemplateColumn.Header>\n\t\t\t\t\t\t\t<TextBlock Text=\"{i18n:Translate ui.main_window.menu_headers.nexus_install}\" TextAlignment=\"Center\"/>\n\t\t\t\t\t\t</DataGridTemplateColumn.Header>\n\t\t\t\t\t\t<DataGridTemplateColumn.CellTemplate>\n\t\t\t\t\t\t\t<DataTemplate>\n\t\t\t\t\t\t\t\t<Button Name=\"installButton\" Classes=\"hyperlink status\" Content=\"{Binding InstallStatus}\" Click=\"InstallButton_Click\" Tag=\"{Binding UniqueId}\" Foreground=\"{DynamicResource HighlightForegroundBrush}\" />\n\t\t\t\t\t\t\t</DataTemplate>\n\t\t\t\t\t\t</DataGridTemplateColumn.CellTemplate>\n\t\t\t\t\t</DataGridTemplateColumn>\n\t\t\t\t</DataGrid.Columns>\n\t\t\t</DataGrid>\n\t\t</Border>\n\t\t<DockPanel Name=\"filterBar\" Grid.Row=\"4\" Grid.Column=\"0\" Margin=\"7 0 0 5\">\n\t\t\t<TextBox Name=\"searchBox\" Watermark=\"{i18n:Translate ui.main_window.labels.filter}\" Height=\"10\" Width=\"250\" BorderBrush=\"{DynamicResource HighlightBrush}\" HorizontalAlignment=\"Left\"/>\n\t\t\t<Button Name=\"searchFilterColumnButton\" Content=\"{i18n:Translate ui.main_window.buttons.no_search_filters}\" VerticalAlignment=\"Center\" Margin=\"5 0 0 0\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Background=\"Transparent\" BorderBrush=\"{DynamicResource HighlightBrush}\">\n\t\t\t\t<Button.Flyout>\n\t\t\t\t\t<Flyout FlyoutPresenterClasses=\"FilterBoxFlyout\">\n\t\t\t\t\t\t<ListBox x:Name=\"searchFilterColumnBox\" Name=\"FilterBox\" SelectionMode=\"Multiple,Toggle\" SelectedIndex=\"0\" Margin=\"5 2 0 0\" Background=\"{DynamicResource ThemeBackgroundBrush}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" FontWeight=\"Bold\" BorderBrush=\"{DynamicResource HighlightBrush}\" Width=\"200\">\n\t\t\t\t\t\t\t<ListBoxItem Content=\"{i18n:Translate ui.main_window.combobox.author}\" Classes=\"FilterItemBox\" />\n\t\t\t\t\t\t\t<ListBoxItem Content=\"{i18n:Translate ui.main_window.combobox.mod_name}\" Classes=\"FilterItemBox\" />\n\t\t\t\t\t\t\t<ListBoxItem Content=\"{i18n:Translate ui.main_window.combobox.group}\" Classes=\"FilterItemBox\" />\n\t\t\t\t\t\t\t<ListBoxItem Content=\"{i18n:Translate ui.main_window.combobox.top_level_group}\" Classes=\"FilterItemBox\" />\n\t\t\t\t\t\t\t<ListBoxItem Content=\"{i18n:Translate ui.main_window.combobox.requirements}\" Classes=\"FilterItemBox\" />\n\t\t\t\t\t\t</ListBox>\n\t\t\t\t\t</Flyout>\n\t\t    </Button.Flyout>\n\t\t\t</Button>\n\t\t\t<StackPanel IsVisible=\"{Binding AreModGroupsEnabled}\" Orientation=\"Horizontal\" Margin=\"10 0 10 0\">\n\t\t\t\t<Separator Width=\"1\" Height=\"15\" Background=\"{DynamicResource HighlightBrush}\" />\n\t\t\t</StackPanel>\n\t\t\t<Button Name=\"modGroupStateButton\" IsVisible=\"{Binding AreModGroupsEnabled}\" Content=\"{Binding ModGroupsStateButtonText}\" VerticalAlignment=\"Center\" Margin=\"0 0 0 0\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Background=\"Transparent\" BorderBrush=\"{DynamicResource HighlightBrush}\"/>\n\t\t\t<StackPanel Orientation=\"Horizontal\" Margin=\"10 0 10 0\">\n\t\t\t\t<Separator Width=\"1\" Height=\"15\" Background=\"{DynamicResource HighlightBrush}\" />\n\t\t\t</StackPanel>\n\t\t\t<ComboBox x:Name=\"disabledModFilterColumnBox\" Margin=\"0 2 0 0\" Background=\"{DynamicResource ThemeBackgroundBrush}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" FontWeight=\"Bold\" BorderBrush=\"{DynamicResource HighlightBrush}\" SelectedIndex=\"0\" Width=\"210\">\n\t\t\t\t<ComboBoxItem Content=\"{i18n:Translate ui.main_window.combobox.show_all_mods}\" />\n\t\t\t\t<ComboBoxItem Content=\"{i18n:Translate ui.main_window.combobox.show_mods_with_configs}\" />\n\t\t\t\t<ComboBoxItem Content=\"{i18n:Translate ui.main_window.combobox.show_disabled_mods}\" />\n\t\t\t\t<ComboBoxItem Content=\"{i18n:Translate ui.main_window.combobox.show_enabled_mods}\" />\n\t\t\t</ComboBox>\n\t\t\t<CheckBox Name=\"showUpdatableMods\" Content=\"{i18n:Translate ui.main_window.buttons.show_updatable_mods}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Margin=\"7 0 0 0\" Classes=\"OptionClickbox\"/>\n\t\t</DockPanel>\n\t\t<Border Grid.Row=\"5\" Grid.ColumnSpan=\"2\" BorderBrush=\"{DynamicResource HighlightBrush}\" BorderThickness=\"0 2 0 0\">\n\t\t\t<Grid>\n                <Button Name=\"statusBarButton\" Content=\"{Binding UpdateStatusText}\" Grid.Column=\"0\" Classes=\"hyperlink\" Click=\"ModUpdateCheck_Click\" HorizontalAlignment=\"Left\"/>                \n                <Button Name=\"downloadButton\" Content=\"{Binding DownloadsButtonText}\" Grid.Column=\"0\" Classes=\"hyperlink\" HorizontalAlignment=\"Center\">\n                    <Button.Flyout>\n                        <Flyout FlyoutPresenterClasses=\"DownloadBoxFlyout\">\n\t\t\t\t\t\t\t<view:DownloadPanel x:Name=\"DownloadPanel\" />\n                        </Flyout>\n                    </Button.Flyout>\n                </Button>\n                <Grid ColumnDefinitions=\"Auto, Auto, Auto, Auto\" HorizontalAlignment=\"Right\">\n                    <Button Name=\"nexusModsButton\" Content=\"{Binding NexusStatus}\" Grid.Column=\"0\" Classes=\"hyperlink\" HorizontalAlignment=\"Right\" Padding=\"8,0,8,0\" VerticalAlignment=\"Center\"/>\n                    <TextBlock Text=\"{Binding NexusLimits}\" Grid.Column=\"1\" HorizontalAlignment=\"Right\" VerticalAlignment=\"Center\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" IsVisible=\"{Binding ShowEndorsements}\"/>\n                    <TextBlock Text=\" | \" Grid.Column=\"2\" HorizontalAlignment=\"Right\" VerticalAlignment=\"Center\" Foreground=\"{DynamicResource ThemeForegroundBrush}\"/>\n                    <Button Content=\"{Binding Version}\" Grid.Column=\"3\" Classes=\"hyperlink\" Command=\"{Binding OpenBrowser}\" CommandParameter=\"https://www.nexusmods.com/stardewvalley/mods/10455\" HorizontalAlignment=\"Right\" Padding=\"8,0,8,0\" VerticalAlignment=\"Center\"/>\n                </Grid>\n            </Grid>\n\t\t</Border>\n\t</Grid>\n</Window>\n"
  },
  {
    "path": "Stardrop/Views/MainWindow.axaml.cs",
    "content": "using Avalonia;\nusing Avalonia.Collections;\nusing Avalonia.Controls;\nusing Avalonia.Input;\nusing Avalonia.Markup.Xaml;\nusing Avalonia.Threading;\nusing DynamicData;\nusing DynamicData.Binding;\nusing Semver;\nusing SharpCompress.Archives;\nusing SharpCompress.Common;\nusing Stardrop.Models;\nusing Stardrop.Models.Data;\nusing Stardrop.Models.Data.Enums;\nusing Stardrop.Models.Nexus.Web;\nusing Stardrop.Models.SMAPI;\nusing Stardrop.Models.SMAPI.Web;\nusing Stardrop.Utilities;\nusing Stardrop.Utilities.External;\nusing Stardrop.Utilities.Internal;\nusing Stardrop.ViewModels;\nusing System;\nusing System.Collections.Generic;\nusing System.Data;\nusing System.Diagnostics;\nusing System.IO;\nusing System.Linq;\nusing System.Reactive.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text.Json;\nusing System.Text.RegularExpressions;\nusing System.Threading.Tasks;\nusing static Stardrop.Models.SMAPI.Web.ModEntryMetadata;\n\nnamespace Stardrop.Views\n{\n    public partial class MainWindow : Window\n    {\n        private readonly MainWindowViewModel _viewModel;\n        private readonly ProfileEditorViewModel _editorView;\n        private DispatcherTimer _searchBoxTimer;\n        private DispatcherTimer _smapiProcessTimer;\n        private DispatcherTimer _lockSentinel;\n        private DispatcherTimer _nxmSentinel;\n\n        // Tracking related\n        private bool _shiftPressed;\n        private bool _ctrlPressed;\n\n        private string _lockReason;\n\n        // Session related\n        private LastSessionData _lastSessionDate;\n\n        public MainWindow()\n        {\n            InitializeComponent();\n\n            // Set the main window view\n            _viewModel = new MainWindowViewModel(Pathing.defaultModPath, Program.ApplicationVersion);\n            DataContext = _viewModel;\n\n            // Set the path according to the environmental variable SMAPI_MODS_PATH\n            // SMAPI_MODS_PATH is set via the profile dropdown on the UI\n            var modGrid = this.FindControl<DataGrid>(\"modGrid\");\n            modGrid.IsReadOnly = true;\n            modGrid.LoadingRow += (sender, e) => { e.Row.Header = e.Row.GetIndex() + 1; };\n            modGrid.Items = _viewModel.DataView;\n            modGrid.LoadingRowGroup += ModGrid_LoadingRowGroup;\n\n            AddHandler(DragDrop.DropEvent, Drop);\n            AddHandler(DragDrop.DragOverEvent, (sender, e) =>\n            {\n                _viewModel.DragOverColor = \"#1cff96\";\n            });\n            AddHandler(DragDrop.DragLeaveEvent, (sender, e) =>\n            {\n                _viewModel.DragOverColor = \"#ff9f2a\";\n            });\n\n            // Get the local data\n            ClientData localDataCache = new ClientData();\n            if (File.Exists(Pathing.GetDataCachePath()))\n            {\n                try\n                {\n                    localDataCache = JsonSerializer.Deserialize<ClientData>(File.ReadAllText(Pathing.GetDataCachePath()), new JsonSerializerOptions { AllowTrailingCommas = true });\n                }\n                catch\n                {\n                    localDataCache = new ClientData();\n                }\n            }\n\n            // Set the application's position and size\n            if (localDataCache.LastSessionData is not null)\n            {\n                _lastSessionDate = localDataCache.LastSessionData;\n            }\n\n            // Sets the grid's column visibility, based on previous session\n            if (localDataCache.ColumnActiveStates is not null)\n            {\n                var gridColumnContextMenu = this.FindControl<ContextMenu>(\"gridColumnContextMenu\");\n                foreach (MenuItem column in gridColumnContextMenu.Items)\n                {\n                    string columnName = (string)column.Header;\n                    if (localDataCache.ColumnActiveStates.ContainsKey(columnName))\n                    {\n                        _viewModel.SetColumnVisibility(column, modGrid, localDataCache.ColumnActiveStates[columnName]);\n                    }\n                }\n            }\n\n            // Handle the mainMenu bar for drag and related events\n            var menuBorder = this.FindControl<Border>(\"menuBorder\");\n            menuBorder.PointerPressed += MainBar_PointerPressed;\n            menuBorder.DoubleTapped += MainBar_DoubleTapped;\n\n            // HEADER: \"Value cannot be null. (Parameter 'path1')\" error clears removing the below chunk\n\n            // Set profile list\n            _editorView = new ProfileEditorViewModel(Pathing.GetProfilesFolderPath());\n            var profileComboBox = this.FindControl<ComboBox>(\"profileComboBox\");\n            profileComboBox.Items = _editorView.Profiles;\n            profileComboBox.SelectedIndex = 0;\n            if (_editorView.Profiles.FirstOrDefault(p => p.Name == Program.settings.LastSelectedProfileName) is Profile oldProfile && oldProfile is not null)\n            {\n                profileComboBox.SelectedItem = oldProfile;\n            }\n            profileComboBox.SelectionChanged += ProfileComboBox_SelectionChanged;\n\n            // Update selected mods\n            var profile = profileComboBox.SelectedItem as Profile;\n            _viewModel.EnableModsByProfile(profile);\n\n            // Check if we have any cached updates for mods\n            if (_viewModel.IsCheckingForUpdates is false)\n            {\n                _viewModel.UpdateStatusText = Program.translation.Get(\"ui.main_window.button.update_status.updating\");\n                CheckForModUpdates(_viewModel.Mods.ToList(), useCache: true);\n            }\n            else\n            {\n                CheckForModUpdates(_viewModel.Mods.ToList(), probe: true);\n            }\n\n            // Start sentinel for watching NXM files\n            if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) is false)\n            {\n                _nxmSentinel = new DispatcherTimer();\n                _nxmSentinel.Interval = new TimeSpan(TimeSpan.TicksPerMillisecond * 1000);\n                _nxmSentinel.Tick += _nxmSentinelTimer_Tick;\n                _nxmSentinel.Start();\n            }\n\n            // Start sentinel for watching for lock state\n            _lockSentinel = new DispatcherTimer();\n            _lockSentinel.Interval = new TimeSpan(TimeSpan.TicksPerMillisecond * 100);\n            _lockSentinel.Tick += _lockSentinelTimer_Tick;\n            _lockSentinel.Start();\n\n            // FOOTER: \"Value cannot be null. (Parameter 'path1')\" error clears removing the above chunk\n\n            // Handle buttons\n            this.FindControl<Button>(\"minimizeButton\").Click += delegate { this.WindowState = WindowState.Minimized; };\n            this.FindControl<Button>(\"maximizeButton\").Click += delegate { AdjustWindowState(); };\n            this.FindControl<Button>(\"exitButton\").Click += Exit_Click;\n            this.FindControl<Button>(\"editProfilesButton\").Click += EditProfilesButton_Click;\n            this.FindControl<Button>(\"saveConfigsToProfile\").Click += SaveConfigButton_Click;\n            this.FindControl<Button>(\"saveProfileChanges\").Click += SaveProfileChanges_Click;\n            this.FindControl<Button>(\"smapiButton\").Click += Smapi_Click;\n            this.FindControl<CheckBox>(\"showUpdatableMods\").Click += ShowUpdatableModsButton_Click;\n            this.FindControl<Button>(\"nexusModsButton\").Click += NexusModsButton_Click;\n            this.FindControl<Button>(\"modGroupStateButton\").Click += ModGroupStateButton;\n\n            // Handle filtering via textbox\n            this.FindControl<TextBox>(\"searchBox\").AddHandler(KeyUpEvent, SearchBox_KeyUp);\n\n            // Handle filtering by searchFilterColumnBox\n            var searchFilterColumnBox = this.FindControl<ListBox>(\"searchFilterColumnBox\");\n            searchFilterColumnBox.SelectionChanged += FilterListBox_SelectionChanged;\n            searchFilterColumnBox.SelectedItem = searchFilterColumnBox.Items.Cast<ListBoxItem>().First(c => c.Content.ToString() == Program.translation.Get(\"ui.main_window.combobox.mod_name\"));\n\n            var disabledModFilterColumnBox = this.FindControl<ComboBox>(\"disabledModFilterColumnBox\");\n            disabledModFilterColumnBox.SelectedIndex = 0;\n            disabledModFilterColumnBox.SelectionChanged += DisabledModComboBox_SelectionChanged;\n\n            // Have to register this even here, as MacOS doesn't detect it via axaml during build\n            this.PropertyChanged += MainWindow_PropertyChanged;\n\n            // Hook into key related events\n            this.KeyDown += MainWindow_KeyDown;\n            this.KeyUp += MainWindow_KeyUp;\n\n            // Check if SMAPI should be started immediately via --start-smapi\n            if (Program.onBootStartSMAPI)\n            {\n                StartSMAPI();\n            }\n\n            Program.helper.Log($\"Initialization complete!\");\n\n#if DEBUG\n            this.AttachDevTools();\n#endif\n        }\n\n        private void ModGrid_LoadingRowGroup(object? sender, DataGridRowGroupHeaderEventArgs e)\n        {\n            _viewModel.ModGroupsStateButtonText = Program.translation.Get(\"ui.main_window.buttons.mod_groups_state.collapse\");\n        }\n\n        private void MainWindow_KeyDown(object? sender, KeyEventArgs e)\n        {\n            if (e.Key == Key.LeftShift || e.Key == Key.RightShift)\n            {\n                _shiftPressed = true;\n            }\n            else if (e.Key == Key.LeftCtrl || e.Key == Key.RightCtrl)\n            {\n                _ctrlPressed = true;\n            }\n            else\n            {\n                var searchBox = this.FindControl<TextBox>(\"searchBox\");\n                searchBox.Focus();\n                SearchBox_KeyUp(sender, e);\n            }\n        }\n\n        private void MainWindow_KeyUp(object? sender, KeyEventArgs e)\n        {\n            if (e.Key == Key.LeftShift || e.Key == Key.RightShift)\n            {\n                _shiftPressed = false;\n            }\n            else if (e.Key == Key.LeftCtrl || e.Key == Key.RightCtrl)\n            {\n                _ctrlPressed = false;\n            }\n        }\n\n        private async void MainWindow_PropertyChanged(object? sender, AvaloniaPropertyChangedEventArgs e)\n        {\n            if (this.IsVisible is false)\n            {\n                return;\n            }\n\n            if (e.Property == WindowStateProperty && (WindowState)e.OldValue == WindowState.Minimized && SMAPI.IsRunning)\n            {\n                var warningWindow = new WarningWindow(Program.translation.Get(\"ui.warning.stardrop_locked\"), Program.translation.Get(\"internal.unlock\"), true);\n                warningWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner;\n                await warningWindow.ShowDialog(this);\n            }\n        }\n\n        private void MainWindow_Closing(object? sender, System.ComponentModel.CancelEventArgs e)\n        {\n            Program.settings.LastSelectedProfileName = GetCurrentProfile().Name;\n\n            // Write the settings cache\n            File.WriteAllText(Pathing.GetSettingsPath(), JsonSerializer.Serialize(Program.settings, new JsonSerializerOptions() { WriteIndented = true }));\n\n            // Cache the local data with latest values\n            if (File.Exists(Pathing.GetDataCachePath()))\n            {\n                ClientData localDataCache = JsonSerializer.Deserialize<ClientData>(File.ReadAllText(Pathing.GetDataCachePath()), new JsonSerializerOptions { AllowTrailingCommas = true });\n\n                if (localDataCache is not null)\n                {\n                    localDataCache.LastSessionData = new LastSessionData()\n                    {\n                        Height = this.Height,\n                        Width = this.Width,\n                        PositionX = this.Position.X,\n                        PositionY = this.Position.Y\n                    };\n                }\n\n                File.WriteAllText(Pathing.GetDataCachePath(), JsonSerializer.Serialize(localDataCache, new JsonSerializerOptions() { WriteIndented = true }));\n            }\n        }\n\n        private async void MainWindow_Opened(object? sender, EventArgs e)\n        {\n            if (_lastSessionDate is not null)\n            {\n                try\n                {\n                    Program.helper.Log($\"Setting window size according to settings: {_lastSessionDate.Width}x{_lastSessionDate.Height} ({_lastSessionDate.PositionX}, {_lastSessionDate.PositionY})\");\n\n                    this.Width = _lastSessionDate.Width;\n                    this.Height = _lastSessionDate.Height;\n\n                    var screen = this.Screens.ScreenFromBounds(new PixelRect(_lastSessionDate.PositionX, _lastSessionDate.PositionY, (int)_lastSessionDate.Width, (int)_lastSessionDate.Height));\n                    if (screen is not null)\n                    {\n                        this.Position = new PixelPoint(_lastSessionDate.PositionX, _lastSessionDate.PositionY);\n                        this.WindowStartupLocation = WindowStartupLocation.Manual;\n                    }\n                }\n                catch (Exception ex)\n                {\n                    Program.helper.Log($\"Failed to restore window settings!\");\n                }\n            }\n\n            // Check for Stardrop updates\n            await HandleStardropUpdateCheck();\n\n            // Check if Stardrop should display the changelog (if it has updated since last boot)\n            if (String.IsNullOrEmpty(Program.settings.Version))\n            {\n                Program.settings.Version = _viewModel.Version.Replace(\"v\", String.Empty);\n            }\n            else if (SemVersion.TryParse(Program.settings.Version, SemVersionStyles.Any, out var cachedVersion) && SemVersion.TryParse(_viewModel.Version.Replace(\"v\", String.Empty), SemVersionStyles.Any, out var currentVersion) && cachedVersion.CompareSortOrderTo(currentVersion) < 0)\n            {\n                // Display message with link to release notes\n                var requestWindow = new MessageWindow(Program.translation.Get(\"ui.message.stardrop_update_complete\"));\n                if (await requestWindow.ShowDialog<bool>(this))\n                {\n                    _viewModel.OpenBrowser(\"https://github.com/Floogen/Stardrop/releases/latest\");\n                }\n\n                Program.settings.Version = _viewModel.Version.Replace(\"v\", String.Empty);\n            }\n\n            if (Pathing.defaultGamePath is null || File.Exists(Pathing.GetSmapiPath()) is false)\n            {\n                await DisplayInvalidSMAPIWarning();\n            }\n            else\n            {\n                await HandleSMAPIUpdateCheck(false);\n            }\n\n\n            // Register a handler to watch whenever the Nexus client changes, so setpu and teardown get handled automatically\n            Nexus.ClientChanged += NexusClientChanged;\n\n            // Set up the Nexus Mods connection, and attempt to register for the NXM URI protocol\n            await CheckForNexusConnection();\n\n            if (String.IsNullOrEmpty(Program.nxmLink) is false)\n            {\n                await ProcessNXMLink(new NXM() { Link = Program.nxmLink, Timestamp = DateTime.Now });\n                Program.nxmLink = null;\n            }\n\n            // Set up handler for listening to download count\n            SetupDownloadCountListener();\n        }\n\n        private async Task CreateWarningWindow(string warningText, string buttonText)\n        {\n            var warningWindow = new WarningWindow(warningText, buttonText);\n            await warningWindow.ShowDialog(this);\n        }\n\n        private async void Drop(object sender, DragEventArgs e)\n        {\n            if (String.IsNullOrWhiteSpace(Pathing.defaultModPath) || Directory.Exists(Pathing.defaultModPath) is false)\n            {\n                await DisplayInvalidSMAPIWarning();\n                return;\n            }\n\n            if (!e.Data.Contains(DataFormats.FileNames))\n            {\n                return;\n            }\n\n            var addedMods = await AddMods(e.Data.GetFileNames()?.ToArray());\n\n            // TODO: Add optional setting to disable checking for updates when a new mod is installed?\n            await CheckForModUpdates(addedMods, useCache: true, skipCacheCheck: true);\n            await GetCachedModUpdates(_viewModel.Mods.ToList(), skipCacheCheck: true);\n\n            _viewModel.EvaluateRequirements();\n            _viewModel.UpdateEndorsements();\n            _viewModel.UpdateFilter();\n\n            _viewModel.DragOverColor = \"#ff9f2a\";\n        }\n\n        private void _smapiProcessTimer_Tick(object? sender, EventArgs e)\n        {\n            if (SMAPI.Process is null)\n            {\n                SMAPI.Process = Process.GetProcessesByName(SMAPI.GetProcessName()).FirstOrDefault();\n            }\n            else if (SMAPI.Process.HasExited || Process.GetProcessesByName(SMAPI.GetProcessName()).FirstOrDefault() is null)\n            {\n                Program.helper.Log(\"SMAPI has exited, restoring Stardrop\", Helper.Status.Debug);\n\n                SMAPI.Process = null;\n                SMAPI.IsRunning = false;\n\n                _viewModel.IsLocked = false;\n                _smapiProcessTimer.IsEnabled = false;\n\n                this.WindowState = WindowState.Normal;\n            }\n        }\n\n        private async void _nxmSentinelTimer_Tick(object? sender, EventArgs e)\n        {\n            if (File.Exists(Pathing.GetLinksCachePath()) is false)\n            {\n                return;\n            }\n\n            try\n            {\n                var nxmLinks = new List<NXM>();\n\n                // Gather the NXM links, then clear the file\n                using (FileStream stream = new FileStream(Pathing.GetLinksCachePath(), FileMode.Open, FileAccess.ReadWrite, FileShare.None))\n                {\n                    foreach (var nxmLink in await JsonSerializer.DeserializeAsync<List<NXM>>(stream, new JsonSerializerOptions { AllowTrailingCommas = true }))\n                    {\n                        nxmLinks.Add(nxmLink);\n                    }\n\n                    // Clear the stream and empty out the file\n                    stream.SetLength(0);\n\n                    await JsonSerializer.SerializeAsync(stream, new List<NXM>(), new JsonSerializerOptions() { WriteIndented = true });\n                }\n\n                // Process each link\n                foreach (var nxmLink in nxmLinks)\n                {\n                    if (await ProcessNXMLink(nxmLink) is false)\n                    {\n                        break;\n                    }\n                }\n            }\n            catch (IOException ex)\n            {\n                Program.helper.Log($\"Unable to access the Links.json file\");\n                return;\n            }\n            catch (Exception ex)\n            {\n                Program.helper.Log($\"Failed to process the Links.json file: {ex}\");\n\n                try\n                {\n                    if (File.Exists(Pathing.GetLinksCachePath()))\n                    {\n                        File.Delete(Pathing.GetLinksCachePath());\n                    }\n                }\n                catch (IOException ioEx)\n                {\n                    Program.helper.Log($\"Failed to delete the Links.json file: {ioEx}\");\n                }\n            }\n        }\n\n        private async void _lockSentinelTimer_Tick(object? sender, EventArgs e)\n        {\n            if (this.OwnedWindows.Any(w => w is WarningWindow) is false && _viewModel.IsLocked && String.IsNullOrEmpty(_lockReason) is false)\n            {\n                Program.helper.Log($\"Detected lock state request ({_lockReason}): Locking main window!\");\n\n                var warningWindow = new WarningWindow(_lockReason, _viewModel);\n                warningWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner;\n                await warningWindow.ShowDialog(this);\n            }\n        }\n\n        private void ModGridMenuRow_ChangeState(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            var modGrid = this.FindControl<DataGrid>(\"modGrid\");\n            if (modGrid is null)\n            {\n                return;\n            }\n            var selectedMod = (sender as MenuItem).DataContext as Mod;\n            if (selectedMod is null)\n            {\n                return;\n            }\n            // Add the selected mod into the selection list if shift or ctrl is held, otherwise clear the current\n            //  selection.\n            if (!modGrid.SelectedItems.Contains(selectedMod))\n            {\n                if (!(_ctrlPressed || _shiftPressed))\n                {\n                    modGrid.SelectedItems.Clear();\n                }\n                modGrid.SelectedItems.Add(selectedMod);\n            }\n\n            EnableDisableSelectedMods(modGrid, selectedMod);\n        }\n\n        /// <summary>\n        /// Enable/Disable all mods for the mod group(s) of the selected mod(s).\n        /// </summary>\n        ///\n        /// <param name=\"sender\">\n        /// The currently selected mod of the whole mod group whose mods to enable/disable.\n        /// </param>\n        private void ModGridMenuRow_ChangeWholeModGroupState(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            var modGrid = this.FindControl<DataGrid>(\"modGrid\");\n            if (modGrid is null)\n            {\n                return;\n            }\n            var selectedMod = (sender as MenuItem)?.DataContext as Mod;\n            if (selectedMod is null)\n            {\n                return;\n            }\n\n            // Select all mods of the mod group(s) whose mod 'selectedMod' is currently selected or whose mods have \n            //  been already selected previously.\n            var originallySelectedModPaths = new List<string>();\n            foreach (Mod mod in modGrid.SelectedItems)\n            {\n                originallySelectedModPaths.Add(mod.Path);\n            }\n            if (originallySelectedModPaths.Count is 0)\n            {\n                originallySelectedModPaths.Add(selectedMod.Path);\n            }\n            foreach (Mod mod in modGrid.Items)\n            {\n                if (originallySelectedModPaths.Contains(mod.Path))\n                {\n                    modGrid.SelectedItems.Add(mod);\n                }\n            }\n\n            EnableDisableSelectedMods(modGrid, selectedMod);\n        }\n\n        /// <summary>\n        /// Enable/Disable selected mods.\n        /// </summary>\n        ///\n        /// <param name=\"selectedMod\">The currently selected which the enabling/disabling is performed on.</param>\n        private void EnableDisableSelectedMods(DataGrid? modGrid, Mod? selectedMod)\n        {\n            if (selectedMod is null || modGrid is null)\n            {\n                return;\n            }\n            // Enable / disable all selected mods based on the currently selected mod.\n            selectedMod.IsEnabled = !selectedMod.IsEnabled;\n            foreach (Mod mod in modGrid.SelectedItems)\n            {\n                mod.IsEnabled = selectedMod.IsEnabled;\n                if (selectedMod.IsEnabled)\n                {\n                    EnableRequirements(mod);\n                }\n                else\n                {\n                    DisableRequirements(mod);\n                }\n            }\n\n            if (Program.settings.ShouldAutomaticallySaveProfileChanges)\n            {\n                UpdateProfile(GetCurrentProfile());\n            }\n            else\n            {\n                _viewModel.ShowSaveProfileChanges = true;\n            }\n        }\n\n        private void ModGridMenuRow_OpenFolderPath(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            var selectedMod = (sender as MenuItem).DataContext as Mod;\n            if (selectedMod is null)\n            {\n                return;\n            }\n\n            OpenNativeExplorer(selectedMod.ModFileInfo.DirectoryName);\n        }\n\n        private void ModGridMenuRow_OpenModPage(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            var selectedMod = (sender as MenuItem).DataContext as Mod;\n            if (selectedMod is null)\n            {\n                return;\n            }\n\n            _viewModel.OpenBrowser(selectedMod.ModPageUri);\n        }\n\n        private void ModGridMenuRow_ShowWholeModGroup(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            var selectedMod = (sender as MenuItem)?.DataContext as Mod;\n            if (selectedMod is null)\n            {\n                return;\n            }\n\n            var searchFilterColumnBox = this.FindControl<ListBox>(\"searchFilterColumnBox\");\n            searchFilterColumnBox.SelectedItem = searchFilterColumnBox.Items.Cast<ListBoxItem>().First(c => c.Content.ToString() == Program.translation.Get(\"ui.main_window.combobox.group\"));\n\n            var filterText = string.Empty;\n            switch (Program.settings.ModGroupingMethod)\n            {\n                case ModGrouping.None:\n                    break;\n                case ModGrouping.Folder:\n                    filterText = selectedMod.Path;\n                    break;\n                case ModGrouping.FolderCondensed:\n                    filterText = selectedMod.RootPath;\n                    break;\n                case ModGrouping.ContentPack:\n                    filterText = selectedMod.FrameworkID;\n                    break;\n            }\n\n            this.FindControl<TextBox>(\"searchBox\").Text = filterText;\n            _viewModel.FilterText = filterText;\n        }\n\n        private void ModGridMenuRow_ShowAuthorsMods(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            var selectedMod = (sender as MenuItem)?.DataContext as Mod;\n            if (selectedMod is null)\n            {\n                return;\n            }\n\n            var searchFilterColumnBox = this.FindControl<ListBox>(\"searchFilterColumnBox\");\n            searchFilterColumnBox.SelectedItem = searchFilterColumnBox.Items.Cast<ListBoxItem>().First(c => c.Content.ToString() == Program.translation.Get(\"ui.main_window.combobox.author\"));\n\n            this.FindControl<TextBox>(\"searchBox\").Text = selectedMod.Author;\n            _viewModel.FilterText = selectedMod.Author;\n        }\n\n        private void ModGridMenuRow_ClearFilter(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            var selectedMod = (sender as MenuItem)?.DataContext as Mod;\n            if (selectedMod is null)\n            {\n                return;\n            }\n\n            this.FindControl<TextBox>(\"searchBox\").Text = String.Empty;\n            _viewModel.FilterText = String.Empty;\n        }\n\n        private async void ModGridMenuRow_Delete(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            var modGrid = this.FindControl<DataGrid>(\"modGrid\");\n            if (modGrid is null)\n            {\n                return;\n            }\n\n            var selectedMod = (sender as MenuItem).DataContext as Mod;\n            if (selectedMod is not null)\n            {\n                // Add the selected mod into the selection list if shift or ctrl is held, otherwise clear the current selection\n                if (!modGrid.SelectedItems.Contains(selectedMod))\n                {\n                    if (!(_ctrlPressed || _shiftPressed))\n                    {\n                        modGrid.SelectedItems.Clear();\n                    }\n                    modGrid.SelectedItems.Add(selectedMod);\n                }\n\n                // Delete all selected mods, though ask before each instance\n                bool hasDeletedAMod = false;\n                foreach (Mod mod in modGrid.SelectedItems)\n                {\n                    var requestWindow = new MessageWindow(String.Format(Program.translation.Get(\"ui.message.confirm_mod_deletion\"), mod.Name));\n                    if (await requestWindow.ShowDialog<bool>(this))\n                    {\n                        // Delete old vesrion\n                        if (TryDeleteMod(mod) is false)\n                        {\n                            await CreateWarningWindow(String.Format(Program.translation.Get(\"ui.warning.failed_to_delete\"), mod.Name), Program.translation.Get(\"internal.ok\"));\n                        }\n                        else\n                        {\n                            hasDeletedAMod = true;\n                        }\n                    }\n                }\n\n                if (hasDeletedAMod)\n                {\n                    // Update the current profile\n                    UpdateProfile(GetCurrentProfile());\n\n                    // Refresh mod list\n                    _viewModel.DiscoverMods(Pathing.defaultModPath);\n\n                    // Refresh enabled mods\n                    _viewModel.EnableModsByProfile(GetCurrentProfile());\n\n                    // Refresh the update data\n                    await CheckForModUpdates(_viewModel.Mods.ToList(), probe: true);\n                }\n            }\n        }\n\n        private void SearchBox_KeyUp(object? sender, KeyEventArgs e)\n        {\n            if (_searchBoxTimer is null)\n            {\n                _searchBoxTimer = new DispatcherTimer();\n                _searchBoxTimer.Interval = new TimeSpan(TimeSpan.TicksPerMillisecond / 2);\n                _searchBoxTimer.Tick += SearchBoxTimer_Tick;\n                _searchBoxTimer.Start();\n            }\n        }\n\n        private void SearchBoxTimer_Tick(object? sender, EventArgs e)\n        {\n            var filterText = this.FindControl<TextBox>(\"searchBox\").Text;\n            if (_viewModel.FilterText == filterText)\n            {\n                return;\n            }\n            _viewModel.FilterText = filterText;\n\n            // Ensure the ColumnFilter is set\n            if (_viewModel.ColumnFilter is null || _viewModel.ColumnFilter.Any() is false)\n            {\n                var searchFilterColumnBox = this.FindControl<ListBox>(\"searchFilterColumnBox\");\n                _viewModel.ColumnFilter = searchFilterColumnBox.SelectedItems.Cast<ListBoxItem>().Select(i => i.Content.ToString()).ToList();\n            }\n        }\n\n        private void FilterListBox_SelectionChanged(object? sender, SelectionChangedEventArgs e)\n        {\n            var searchFilterColumnBox = (e.Source as ListBox);\n            _viewModel.ColumnFilter = searchFilterColumnBox.SelectedItems.Cast<ListBoxItem>().Select(i => i.Content.ToString()).ToList();\n\n            int selectedItemCount = searchFilterColumnBox.SelectedItems.Count;\n            this.FindControl<Button>(\"searchFilterColumnButton\").Content = selectedItemCount > 0 ? String.Format(Program.translation.Get(\"ui.main_window.buttons.active_search_filters\"), selectedItemCount) : Program.translation.Get(\"ui.main_window.buttons.no_search_filters\");\n        }\n\n        private void DisabledModComboBox_SelectionChanged(object? sender, SelectionChangedEventArgs e)\n        {\n            var disabledModFilterColumnBox = (e.Source as ComboBox);\n            var filterText = (disabledModFilterColumnBox.SelectedItem as ComboBoxItem).Content.ToString();\n\n            if (filterText == Program.translation.Get(\"ui.main_window.combobox.show_all_mods\"))\n            {\n                _viewModel.DisabledModFilter = Models.Data.Enums.DisplayFilter.None;\n            }\n            else if (filterText == Program.translation.Get(\"ui.main_window.combobox.show_mods_with_configs\"))\n            {\n                _viewModel.DisabledModFilter = Models.Data.Enums.DisplayFilter.RequireConfig;\n            }\n            else if (filterText == Program.translation.Get(\"ui.main_window.combobox.show_enabled_mods\"))\n            {\n                _viewModel.DisabledModFilter = Models.Data.Enums.DisplayFilter.ShowEnabled;\n            }\n            else if (filterText == Program.translation.Get(\"ui.main_window.combobox.show_disabled_mods\"))\n            {\n                _viewModel.DisabledModFilter = Models.Data.Enums.DisplayFilter.ShowDisabled;\n            }\n        }\n\n        private void ShowUpdatableModsButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            var showUpdatableModsCheckBox = e.Source as CheckBox;\n            _viewModel.ShowUpdatableMods = (bool)showUpdatableModsCheckBox.IsChecked;\n        }\n\n        private async void ProfileComboBox_SelectionChanged(object? sender, SelectionChangedEventArgs e)\n        {\n            var profile = (e.Source as ComboBox).SelectedItem as Profile;\n            if (profile is null)\n            {\n                return;\n            }\n\n            // Verify if any unsaved config changes need to be saved\n            if (Program.settings.EnableProfileSpecificModConfigs && e.RemovedItems.Count > 0 && e.RemovedItems[0] is Profile oldProfile && oldProfile is not null)\n            {\n                _viewModel.DiscoverConfigs(Pathing.defaultModPath, useArchive: true);\n                var pendingConfigUpdates = _viewModel.GetPendingConfigUpdates(oldProfile, excludeMissingConfigs: true);\n                foreach (var pendingConfigUpdate in pendingConfigUpdates)\n                {\n                    Program.helper.Log($\"The mod {pendingConfigUpdate.UniqueId} for profile \\\"{oldProfile.Name}\\\" does not have its current configuration preserved ->\\nModified configuration:\\n{pendingConfigUpdate.Data}\", Helper.Status.Warning);\n                }\n\n                if (pendingConfigUpdates.Count > 0 && await new MessageWindow(String.Format(Program.translation.Get(\"ui.message.unsaved_config_changes\"), oldProfile.Name)).ShowDialog<bool>(this))\n                {\n                    _viewModel.ReadModConfigs(oldProfile, pendingConfigUpdates);\n                    UpdateProfile(oldProfile);\n                }\n            }\n\n            // Enable the mods for the selected profile\n            _viewModel.EnableModsByProfile(profile);\n\n            // Update the EnabledModCount\n            _viewModel.EnabledModCount = _viewModel.Mods.Where(m => m.IsEnabled && !m.IsHidden).Count();\n\n            Program.settings.ShouldWriteToModConfigs = true;\n        }\n\n        private async void EndorsementButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            var button = e.Source as Button;\n            var modGrid = this.FindControl<DataGrid>(\"modGrid\");\n            if (button is null || modGrid is null)\n            {\n                return;\n            }\n\n            if (Nexus.Client is null)\n            {\n                return;\n            }\n\n            // Get the mod based on the checkbox's content (which contains the UniqueId)\n            var clickedMod = _viewModel.Mods.FirstOrDefault(m => m.UniqueId.Equals(button.Tag));\n            if (clickedMod is null || clickedMod.NexusModId is null)\n            {\n                return;\n            }\n            int modId = (int)clickedMod.NexusModId;\n\n            bool targetState = !clickedMod.IsEndorsed;\n            var result = await Nexus.Client.SetModEndorsement(modId, targetState);\n            if (result == EndorsementResponse.Endorsed || result == EndorsementResponse.Abstained)\n            {\n                _viewModel.Mods.First(m => m.NexusModId == modId).IsEndorsed = targetState;\n                Program.helper.Log($\"Set endorsement state ({targetState}) for mod id: {modId}\");\n            }\n            else if (result == EndorsementResponse.IsOwnMod)\n            {\n                await CreateWarningWindow(String.Format(Program.translation.Get(\"ui.warning.unable_to_endorse\"), Program.translation.Get(\"ui.warning.mod_owned\")), Program.translation.Get(\"internal.ok\"));\n            }\n            else if (result == EndorsementResponse.TooSoonAfterDownload)\n            {\n                await CreateWarningWindow(String.Format(Program.translation.Get(\"ui.warning.unable_to_endorse\"), Program.translation.Get(\"ui.warning.too_soon_after_download\")), Program.translation.Get(\"internal.ok\"));\n            }\n            else if (result == EndorsementResponse.NotDownloadedMod)\n            {\n                await CreateWarningWindow(String.Format(Program.translation.Get(\"ui.warning.unable_to_endorse\"), Program.translation.Get(\"ui.warning.not_downloaded\")), Program.translation.Get(\"internal.ok\"));\n            }\n            else\n            {\n                await CreateWarningWindow(String.Format(Program.translation.Get(\"ui.warning.unable_to_endorse\"), Program.translation.Get(\"internal.unknown\")), Program.translation.Get(\"internal.ok\"));\n            }\n        }\n\n        private async void InstallButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            var button = e.Source as Button;\n            var modGrid = this.FindControl<DataGrid>(\"modGrid\");\n            if (button is null || modGrid is null)\n            {\n                return;\n            }\n\n            // Get the mod based on the checkbox's content (which contains the UniqueId)\n            var clickedMod = _viewModel.Mods.FirstOrDefault(m => m.UniqueId.Equals(button.Tag));\n            if (clickedMod is null)\n            {\n                return;\n            }\n\n            // Install the mod\n            var downloadedFilePath = await InstallModViaNexus(clickedMod);\n            if (String.IsNullOrEmpty(downloadedFilePath))\n            {\n                return;\n            }\n\n            var addedMods = await AddMods(new string[] { downloadedFilePath });\n            await CheckForModUpdates(addedMods, useCache: true, skipCacheCheck: true);\n            await GetCachedModUpdates(_viewModel.Mods.ToList(), skipCacheCheck: true);\n\n            // Delete the downloaded archived mod\n            if (File.Exists(downloadedFilePath))\n            {\n                File.Delete(downloadedFilePath);\n            }\n\n            _viewModel.EvaluateRequirements();\n            _viewModel.UpdateEndorsements();\n            _viewModel.UpdateFilter();\n        }\n\n        private void EnabledBox_Clicked(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            var checkBox = e.Source as CheckBox;\n            var modGrid = this.FindControl<DataGrid>(\"modGrid\");\n            if (checkBox is null || modGrid is null)\n            {\n                return;\n            }\n\n            // Get the mod based on the checkbox's content (which contains the UniqueId)\n            var clickedMod = _viewModel.Mods.FirstOrDefault(m => m.UniqueId.Equals(checkBox.Content));\n            if (clickedMod is not null)\n            {\n                // Add the selected mod into the selection list if shift or ctrl is held, otherwise clear the current selection\n                if (!modGrid.SelectedItems.Contains(clickedMod))\n                {\n                    if (!(_ctrlPressed || _shiftPressed))\n                    {\n                        modGrid.SelectedItems.Clear();\n                    }\n                    modGrid.SelectedItems.Add(clickedMod);\n                }\n\n                // Enable / disable all selected mods based on the clicked mod\n                foreach (Mod mod in modGrid.SelectedItems)\n                {\n                    mod.IsEnabled = clickedMod.IsEnabled;\n\n                    if (clickedMod.IsEnabled)\n                    {\n                        // Enable any existing requirements\n                        EnableRequirements(mod);\n                    }\n                    else\n                    {\n                        // Disable any mods that require it requirements\n                        DisableRequirements(mod);\n                    }\n                }\n            }\n\n            if (Program.settings.ShouldAutomaticallySaveProfileChanges)\n            {\n                UpdateProfile(GetCurrentProfile());\n            }\n            else\n            {\n                _viewModel.ShowSaveProfileChanges = true;\n            }\n        }\n\n        private async void EditProfilesButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            var profileComboBox = this.FindControl<ComboBox>(\"profileComboBox\");\n            var oldProfile = profileComboBox.SelectedItem as Profile;\n\n            var editorWindow = new ProfileEditor(_editorView);\n            editorWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner;\n            await editorWindow.ShowDialog(this);\n\n            // Restore the previously selected profile\n            if (_editorView.Profiles.Any(p => p.Name == oldProfile.Name))\n            {\n                profileComboBox.SelectedItem = oldProfile;\n            }\n            else\n            {\n                profileComboBox.SelectedIndex = 0;\n            }\n        }\n\n        private void SaveConfigButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            var profileComboBox = this.FindControl<ComboBox>(\"profileComboBox\");\n            var profile = profileComboBox.SelectedItem as Profile;\n\n            if (profile is not null)\n            {\n                _viewModel.DiscoverConfigs(Pathing.defaultModPath, useArchive: true);\n                _viewModel.ReadModConfigs(profile, _viewModel.GetPendingConfigUpdates(profile));\n                UpdateProfile(profile);\n\n                if (!Program.settings.EnableProfileSpecificModConfigs)\n                {\n                    CreateWarningWindow(String.Format(Program.translation.Get(\"ui.warning.mod_config_saved_but_not_enabled\"), profile.Name), Program.translation.Get(\"internal.ok\"));\n                }\n\n                Program.settings.ShouldWriteToModConfigs = true;\n            }\n        }\n\n        private void ModGroupStateButton(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            bool areModGroupsExpanded = _viewModel.ModGroupsStateButtonText == Program.translation.Get(\"ui.main_window.buttons.mod_groups_state.collapse\");\n\n            var modGrid = this.FindControl<DataGrid>(\"modGrid\");\n            foreach (DataGridCollectionViewGroup group in _viewModel.DataView.Groups.Where(g => g is DataGridCollectionViewGroup))\n            {\n                if (group is null)\n                {\n                    continue;\n                }\n\n                try\n                {\n                    if (areModGroupsExpanded)\n                    {\n                        modGrid.CollapseRowGroup(group, true);\n                    }\n                    else\n                    {\n                        modGrid.ExpandRowGroup(group, true);\n                    }\n                }\n                catch (Exception ex)\n                {\n                    Program.helper.Log($\"Failed to change group collapse / expand state: {ex}\");\n                }\n            }\n\n            _viewModel.ModGroupsStateButtonText = !areModGroupsExpanded ? Program.translation.Get(\"ui.main_window.buttons.mod_groups_state.collapse\") : Program.translation.Get(\"ui.main_window.buttons.mod_groups_state.expand\");\n        }\n\n        private async void NexusModsButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            await HandleNexusConnection();\n        }\n\n        // Menu related click events\n        private async void SaveProfileChanges_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            await SaveChanges();\n        }\n\n        private async void SaveProfileChanges_Click(object? sender, EventArgs e)\n        {\n            await SaveChanges();\n        }\n\n        private async void Smapi_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            await StartSMAPI();\n        }\n\n        private async void Smapi_Click(object? sender, EventArgs e)\n        {\n            await StartSMAPI();\n        }\n\n        private async void AddMod_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            await HandleModAdd();\n        }\n\n        private async void AddMod_Click(object? sender, EventArgs e)\n        {\n            await HandleModAdd();\n        }\n\n        private async void Settings_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            await DisplaySettingsWindow();\n        }\n\n        private async void Settings_Click(object? sender, EventArgs e)\n        {\n            await DisplaySettingsWindow();\n        }\n\n        private void LogFile_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            OpenNativeExplorer(Pathing.GetLogFolderPath());\n        }\n\n        private void LogFile_Click(object? sender, EventArgs e)\n        {\n            OpenNativeExplorer(Pathing.GetLogFolderPath());\n        }\n\n        private void SmapiLogFile_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            OpenNativeExplorer(Pathing.GetSmapiLogFolderPath());\n        }\n\n        private void SmapiLogFile_Click(object? sender, EventArgs e)\n        {\n            OpenNativeExplorer(Pathing.GetSmapiLogFolderPath());\n        }\n\n        private async void ModUpdateCheck_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            await HandleModUpdateCheck();\n        }\n\n        private async void ModUpdateCheck_Click(object? sender, EventArgs e)\n        {\n            await HandleModUpdateCheck();\n        }\n\n        private async void StardropUpdate_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            await HandleStardropUpdateCheck(true);\n        }\n\n        private async void StardropUpdate_Click(object? sender, EventArgs e)\n        {\n            await HandleStardropUpdateCheck(true);\n        }\n\n        private async void SMAPIUpdate_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            await HandleSMAPIUpdateCheck(true);\n        }\n\n        private async void SMAPIUpdate_Click(object? sender, EventArgs e)\n        {\n            await HandleSMAPIUpdateCheck(true);\n        }\n\n        private async void ModListRefresh_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            await HandleModListRefresh();\n        }\n\n        private async void ModListRefresh_Click(object? sender, EventArgs e)\n        {\n            await HandleModListRefresh();\n        }\n\n        private async void NexusModBulkInstall_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            await HandleBulkModInstall();\n        }\n\n        private async void NexusModBulkInstall_Click(object? sender, EventArgs e)\n        {\n            await HandleBulkModInstall();\n        }\n\n        private async void NexusModBulkInstallEnabledOnly_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            await HandleBulkModInstall(enabledModsOnly: true);\n        }\n\n        private async void NexusModBulkInstallEnabledOnly_Click(object? sender, EventArgs e)\n        {\n            await HandleBulkModInstall(enabledModsOnly: true);\n        }\n\n        private async void NexusConnection_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            await HandleNexusConnection();\n        }\n\n        private async void NexusConnection_Click(object? sender, EventArgs e)\n        {\n            await HandleNexusConnection();\n        }\n\n        private async void EnableAllMods_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            await HandleBulkModStateChange(true);\n        }\n\n        private async void EnableAllMods_Click(object? sender, EventArgs e)\n        {\n            await HandleBulkModStateChange(true);\n        }\n\n        private async void DisableAllMods_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            await HandleBulkModStateChange(false);\n        }\n\n        private async void DisableAllMods_Click(object? sender, EventArgs e)\n        {\n            await HandleBulkModStateChange(false);\n        }\n\n        private void Exit_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            this.Close();\n        }\n\n        private void Exit_Click(object? sender, EventArgs e)\n        {\n            this.Close();\n        }\n\n        private void MainBar_DoubleTapped(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            var menu = this.FindControl<Menu>(\"mainMenu\");\n            if (!menu.IsPointerOver && !e.Handled)\n            {\n                AdjustWindowState();\n            }\n        }\n\n        private void MainBar_PointerPressed(object? sender, Avalonia.Input.PointerPressedEventArgs e)\n        {\n            var menu = this.FindControl<Menu>(\"mainMenu\");\n            if (e.Pointer.IsPrimary && !menu.IsOpen && !e.Handled)\n            {\n                this.BeginMoveDrag(e);\n            }\n        }\n\n        // End of events\n        private async Task SaveChanges()\n        {\n            UpdateProfile(GetCurrentProfile());\n\n            _viewModel.ShowSaveProfileChanges = false;\n        }\n\n        private async Task StartSMAPI()\n        {\n            await SaveChanges();\n\n            Program.helper.Log($\"Starting SMAPI at path: {Program.settings.SMAPIFolderPath}\", Helper.Status.Debug);\n            if (await ValidateSMAPIPath() is false)\n            {\n                return;\n            }\n\n            // Set the environment variable for the mod path\n            var enabledModsPath = Pathing.GetSelectedModsFolderPath();\n            Environment.SetEnvironmentVariable(\"SMAPI_MODS_PATH\", enabledModsPath);\n\n            // Get the currently selected profile\n            var profile = this.FindControl<ComboBox>(\"profileComboBox\").SelectedItem as Profile;\n            if (profile is null)\n            {\n                await CreateWarningWindow(Program.translation.Get(\"ui.warning.unable_to_determine_profile\"), Program.translation.Get(\"internal.ok\"));\n                Program.helper.Log($\"Unable to determine selected profile, SMAPI will not be started!\", Helper.Status.Alert);\n                return;\n            }\n\n            // Update the enabled mod folder linkage\n            UpdateEnabledModsFolder(profile, enabledModsPath);\n\n            // Update the profile's configurations\n            if (Program.settings.EnableProfileSpecificModConfigs && Program.settings.ShouldWriteToModConfigs)\n            {\n                // Set the config files\n                _viewModel.WriteModConfigs(profile);\n\n                Program.settings.ShouldWriteToModConfigs = false;\n\n                // Write the settings cache\n                File.WriteAllText(Pathing.GetSettingsPath(), JsonSerializer.Serialize(Program.settings, new JsonSerializerOptions() { WriteIndented = true }));\n            }\n\n            using (Process smapi = Process.Start(SMAPI.GetPrepareProcess(false)))\n            {\n                SMAPI.IsRunning = true;\n                _viewModel.IsLocked = true;\n\n                _smapiProcessTimer = new DispatcherTimer();\n                _smapiProcessTimer.Interval = new TimeSpan(TimeSpan.TicksPerMillisecond * 500);\n                _smapiProcessTimer.Tick += _smapiProcessTimer_Tick;\n                _smapiProcessTimer.Start();\n\n                this.WindowState = WindowState.Minimized;\n            }\n        }\n\n        private async Task HandleModAdd()\n        {\n            if (Pathing.defaultModPath is null || !Directory.Exists(Pathing.defaultModPath))\n            {\n                await DisplayInvalidSMAPIWarning();\n                return;\n            }\n\n            OpenFileDialog dialog = new OpenFileDialog();\n            dialog.Filters.Add(new FileDialogFilter() { Name = \"Mod Archive (*.zip, *.7z, *.rar)\", Extensions = { \"zip\", \"7z\", \"rar\" } });\n            dialog.AllowMultiple = true;\n\n            var addedMods = await AddMods(await dialog.ShowAsync(this));\n\n            await CheckForModUpdates(addedMods, useCache: true, skipCacheCheck: true);\n            await GetCachedModUpdates(_viewModel.Mods.ToList(), skipCacheCheck: true);\n\n            _viewModel.EvaluateRequirements();\n            _viewModel.UpdateEndorsements();\n            _viewModel.UpdateFilter();\n        }\n\n        private async Task DisplaySettingsWindow()\n        {\n            Program.helper.Log($\"Opening settings window\");\n\n            var editorWindow = new SettingsWindow(this.Height);\n            editorWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner;\n            if (await editorWindow.ShowDialog<bool>(this))\n            {\n                await HandleModListRefresh();\n\n                _viewModel.ShowSaveProfileChanges = !Program.settings.ShouldAutomaticallySaveProfileChanges;\n                _viewModel.AreModGroupsEnabled = Program.settings.ModGroupingMethod != ModGrouping.None;\n                _viewModel.ShowModThumbnails = Program.settings.ShowModThumbnails;\n            }\n        }\n\n        private async Task HandleStardropUpdateCheck(bool manualCheck = false)\n        {\n            SemVersion? latestVersion = null;\n            bool updateAvailable = false;\n\n            // Check if current version is the latest\n            var versionToUri = await GitHub.GetLatestStardropRelease();\n            if (versionToUri is not null && SemVersion.TryParse(versionToUri?.Key.Replace(\"v\", String.Empty), out latestVersion) && SemVersion.TryParse(_viewModel.Version.Replace(\"v\", String.Empty), out var currentVersion) && latestVersion.CompareSortOrderTo(currentVersion) > 0)\n            {\n                updateAvailable = true;\n            }\n            else if (versionToUri is null)\n            {\n                await CreateWarningWindow(String.Format(Program.translation.Get(\"ui.warning.stardrop_unable_to_find_latest\"), _viewModel.Version), Program.translation.Get(\"internal.ok\"));\n                return;\n            }\n\n            // If an update is available, notify the user otherwise let them know Stardrop is up-to-date\n            if (updateAvailable)\n            {\n                var requestWindow = new MessageWindow(String.Format(Program.translation.Get(\"ui.message.stardrop_update_available\"), latestVersion));\n                if (await requestWindow.ShowDialog<bool>(this))\n                {\n                    SetLockState(true, Program.translation.Get(\"ui.warning.stardrop_downloading\"));\n                    var extractedLatestReleasePath = await GitHub.DownloadLatestStardropRelease(versionToUri?.Value);\n                    if (String.IsNullOrEmpty(extractedLatestReleasePath))\n                    {\n                        await CreateWarningWindow(String.Format(Program.translation.Get(\"ui.warning.stardrop_unable_to_download_latest\"), _viewModel.Version), Program.translation.Get(\"internal.ok\"));\n                        SetLockState(false);\n                        return;\n                    }\n                    SetLockState(false);\n\n                    await CreateWarningWindow(String.Format(Program.translation.Get(\"ui.warning.stardrop_update_downloaded\"), _viewModel.Version), Program.translation.Get(\"internal.ok\"));\n                    if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))\n                    {\n                        // Prepare the process\n                        string[] arguments = new string[] { \"timeout 1\", $\"move \\\"{Path.Combine(extractedLatestReleasePath, \"*\")}\\\" .\", $\"robocopy \\\"{Path.Combine(extractedLatestReleasePath, \"Themes\")}\\\" Themes /E /MOVE\", $\"move \\\"{Path.Combine(extractedLatestReleasePath, \"i18n\", \"*\")}\\\" .\\\\i18n\", $\"rmdir /s /q \\\"{extractedLatestReleasePath}\\\"\", $\"\\\"{Path.Combine(Directory.GetCurrentDirectory(), \"Stardrop.exe\")}\\\"\" };\n                        var processInfo = new ProcessStartInfo\n                        {\n                            FileName = \"cmd\",\n                            Arguments = $\"/C {string.Join(\" & \", arguments)}\",\n                            CreateNoWindow = true,\n                            UseShellExecute = false\n                        };\n\n                        try\n                        {\n                            Program.helper.Log($\"Starting update process from {_viewModel.Version} to {versionToUri?.Key}\");\n                            Process.Start(processInfo);\n                            this.Close();\n                        }\n                        catch (Exception ex)\n                        {\n                            Program.helper.Log($\"Process failed to update Stardrop using {processInfo.FileName} with arguments: {processInfo.Arguments}\");\n                            Program.helper.Log($\"Exception for failed update process: {ex}\");\n                        }\n                    }\n                    else\n                    {\n                        try\n                        {\n                            // Move the top level files\n                            var adjustedExtractedLatestReleasePath = RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? extractedLatestReleasePath : Path.Combine(extractedLatestReleasePath, \"Contents\", \"MacOS\");\n                            foreach (var file in new DirectoryInfo(adjustedExtractedLatestReleasePath).GetFiles())\n                            {\n                                file.MoveTo(Path.Combine(Directory.GetCurrentDirectory(), file.Name), true);\n                            }\n\n                            // Move the child folders / files\n                            foreach (var folder in new DirectoryInfo(adjustedExtractedLatestReleasePath).GetDirectories())\n                            {\n                                foreach (var file in folder.GetFiles())\n                                {\n                                    file.MoveTo(Path.Combine(Directory.GetCurrentDirectory(), folder.Name, file.Name), true);\n                                }\n                            }\n\n                            // Apply specific update logic for MacOS\n                            var macInfoPath = Path.Combine(extractedLatestReleasePath, \"Contents\", \"Info.plist\");\n                            if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX) && File.Exists(macInfoPath))\n                            {\n                                File.Copy(macInfoPath, Path.Combine(Directory.GetCurrentDirectory(), \"..\", \"Info.plist\"), true);\n                            }\n\n                            // Delete the update package\n                            Directory.Delete(extractedLatestReleasePath, true);\n\n                            // Restart the application\n                            string scriptPath = $\"'{Path.Combine(Directory.GetCurrentDirectory(), RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? \"Stardrop.sh\" : \"Stardrop\")}'\";\n                            string[] arguments = new string[] { $\"chmod +x {scriptPath}\", $\"{scriptPath}\" };\n                            var processInfo = new ProcessStartInfo\n                            {\n                                FileName = \"/usr/bin/env\",\n                                Arguments = $\"bash -c \\\"{string.Join(\" ; \", arguments)}\\\"\",\n                                CreateNoWindow = true,\n                                UseShellExecute = false\n                            };\n\n                            using (var process = Process.Start(processInfo))\n                            {\n                                this.Close();\n                            }\n                        }\n                        catch (Exception ex)\n                        {\n                            Program.helper.Log($\"Process failed to update Stardrop\");\n                            Program.helper.Log($\"Exception for failed update process: {ex}\");\n                        }\n                    }\n                }\n            }\n            else if (manualCheck)\n            {\n                await CreateWarningWindow(String.Format(Program.translation.Get(\"ui.warning.stardrop_up_to_date\"), _viewModel.Version), Program.translation.Get(\"internal.ok\"));\n            }\n        }\n\n        private async Task HandleSMAPIUpdateCheck(bool manualCheck = false)\n        {\n            // Handle failure gracefully with a warning.\n            if (await ValidateSMAPIPath() is false)\n            {\n                return;\n            }\n\n            // Check for SMAPI updates\n            var currentSmapiVersion = SMAPI.GetVersion();\n            if (currentSmapiVersion is not null && Program.settings.GameDetails is not null)\n            {\n                Program.settings.GameDetails.SmapiVersion = currentSmapiVersion.ToString();\n                _viewModel.SmapiVersion = Program.settings.GameDetails.SmapiVersion;\n            }\n\n            KeyValuePair<string, string>? latestSmapiToUri = await GitHub.GetLatestSMAPIRelease();\n            if (latestSmapiToUri is not null && SemVersion.TryParse(latestSmapiToUri?.Key, SemVersionStyles.Any, out var latestVersion) && currentSmapiVersion is not null && latestVersion.CompareSortOrderTo(currentSmapiVersion) > 0)\n            {\n                var confirmationWindow = new MessageWindow(String.Format(Program.translation.Get(\"ui.message.SMAPI_update_available\"), latestVersion));\n                if (await confirmationWindow.ShowDialog<bool>(this) is false)\n                {\n                    Program.helper.Log(\"Player opted to not install the latest version of SMAPI\");\n                    return;\n                }\n\n                SetLockState(true, Program.translation.Get(\"ui.warning.SMAPI_downloading\"));\n                var extractedLatestReleasePath = await GitHub.DownloadLatestSMAPIRelease(latestSmapiToUri?.Value);\n                if (String.IsNullOrEmpty(extractedLatestReleasePath))\n                {\n                    await CreateWarningWindow(String.Format(Program.translation.Get(\"ui.warning.SMAPI_unable_to_download_latest\"), _viewModel.Version), Program.translation.Get(\"internal.ok\"));\n                    SetLockState(false);\n                    return;\n                }\n\n                // Get the install.dat archive\n                var subFolderName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? \"windows\" : RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? \"macOS\" : \"linux\";\n                var filePath = Path.Combine(extractedLatestReleasePath, \"internal\", subFolderName, \"install.dat\");\n\n                // Verify it exists\n                if (File.Exists(filePath) is false)\n                {\n                    await CreateWarningWindow(String.Format(Program.translation.Get(\"ui.warning.SMAPI_unable_to_download_latest\"), _viewModel.Version), Program.translation.Get(\"internal.ok\"));\n                    SetLockState(false);\n                    return;\n                }\n\n                // Attempt to install\n                SetLockState(true, Program.translation.Get(\"ui.warning.SMAPI_installing\"));\n                try\n                {\n                    // Extract the items from internal/OS_TYPE/install.dat to Pathing.defaultGamePath\n                    ArchiveFactory.WriteToDirectory(filePath, Pathing.defaultGamePath, new ExtractionOptions() { Overwrite = true, ExtractFullPath = true });\n\n                    // Create a copy of Stardew Valley.deps.json with the name of StardewModdingAPI.deps.json\n                    if (File.Exists(Path.Combine(Pathing.defaultGamePath, \"Stardew Valley.deps.json\")))\n                    {\n                        File.Copy(Path.Combine(Pathing.defaultGamePath, \"Stardew Valley.deps.json\"), Path.Combine(Pathing.defaultGamePath, \"StardewModdingAPI.deps.json\"), true);\n                    }\n\n                    // For Linux / macOS, follow the steps defined here: https://github.com/Pathoschild/SMAPI/blob/c1342bd4cd6b75b24d11275bdd73ebf893f916ea/src/SMAPI.Installer/InteractiveInstaller.cs#L398\n                    if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) is false && File.Exists(Path.Combine(Pathing.defaultGamePath, \"StardewModdingAPI\")))\n                    {\n                        File.Move(Path.Combine(Pathing.defaultGamePath, \"unix-launcher.sh\"), Path.Combine(Pathing.defaultGamePath, \"StardewValley\"), true);\n\n                        foreach (string path in new[] { Path.Combine(Pathing.defaultGamePath, \"StardewValley\"), Path.Combine(Pathing.defaultGamePath, \"StardewModdingAPI\") })\n                        {\n                            new Process\n                            {\n                                StartInfo = new ProcessStartInfo\n                                {\n                                    FileName = \"chmod\",\n                                    Arguments = $\"755 \\\"{path}\\\"\",\n                                    CreateNoWindow = true\n                                }\n                            }.Start();\n                        }\n                    }\n                }\n                catch (Exception ex)\n                {\n                    Program.helper.Log($\"Failed to install SMAPI's update due to the following error: {ex}\");\n\n                    SetLockState(false);\n                    await CreateWarningWindow(String.Format(Program.translation.Get(\"ui.warning.SMAPI_unable_to_install_latest\"), _viewModel.Version), Program.translation.Get(\"internal.ok\"));\n\n                    OpenNativeExplorer(extractedLatestReleasePath);\n                    return;\n                }\n\n                // Update the setting version\n                Program.settings.GameDetails.SmapiVersion = latestVersion.ToString();\n                _viewModel.SmapiVersion = Program.settings.GameDetails.SmapiVersion;\n\n                // Delete any files underneath the SMAPI upgrade folder\n                var upgradeDirectory = new DirectoryInfo(Pathing.GetSmapiUpgradeFolderPath());\n                foreach (FileInfo file in upgradeDirectory.GetFiles())\n                {\n                    file.Delete();\n                }\n                foreach (DirectoryInfo dir in upgradeDirectory.GetDirectories())\n                {\n                    dir.Delete(true);\n                }\n                SetLockState(false);\n\n                // Display message with link to release notes\n                var requestWindow = new MessageWindow(Program.translation.Get(\"ui.message.SMAPI_update_complete\"));\n                if (await requestWindow.ShowDialog<bool>(this))\n                {\n                    _viewModel.OpenBrowser($\"https://smapi.io/release/{latestSmapiToUri?.Key.Replace(\".\", String.Empty)}\");\n                }\n            }\n            else if (manualCheck is true)\n            {\n                await CreateWarningWindow(String.Format(Program.translation.Get(\"ui.warning.SMAPI_up_to_date\"), _viewModel.SmapiVersion), Program.translation.Get(\"internal.ok\"));\n            }\n        }\n\n        private async Task HandleModUpdateCheck()\n        {\n            if (Pathing.defaultModPath is null)\n            {\n                await DisplayInvalidSMAPIWarning();\n                return;\n            }\n\n            if (_viewModel.IsCheckingForUpdates is false)\n            {\n                await CheckForModUpdates(_viewModel.Mods.ToList());\n            }\n        }\n\n        private async Task HandleBulkModStateChange(bool enableState)\n        {\n            var requestWindow = new MessageWindow(enableState ? Program.translation.Get(\"ui.message.confirm_bulk_change_mod_states_enable\") : Program.translation.Get(\"ui.message.confirm_bulk_change_mod_states_disable\"));\n            if (await requestWindow.ShowDialog<bool>(this))\n            {\n                foreach (var mod in _viewModel.Mods.Where(m => m.IsEnabled != enableState))\n                {\n                    mod.IsEnabled = enableState;\n                }\n\n                if (Program.settings.ShouldAutomaticallySaveProfileChanges)\n                {\n                    UpdateProfile(GetCurrentProfile());\n                }\n                else\n                {\n                    _viewModel.ShowSaveProfileChanges = true;\n                }\n            }\n        }\n\n        private async Task HandleBulkModInstall(bool enabledModsOnly = false)\n        {\n            if (Nexus.Client is null)\n            {\n                return;\n            }\n\n            if (Program.settings.NexusDetails is null || Program.settings.NexusDetails.IsPremium is false)\n            {\n                await CreateWarningWindow(Program.translation.Get(\"ui.warning.download_without_premium\"), Program.translation.Get(\"internal.ok\"));\n                return;\n            }\n            else if (_viewModel.Mods.Where(m => String.IsNullOrEmpty(m.InstallStatus) is false).Count() == 0)\n            {\n                await CreateWarningWindow(Program.translation.Get(\"ui.warning.no_downloads_available\"), Program.translation.Get(\"internal.ok\"));\n                return;\n            }\n\n            List<string> updateFilePaths = new List<string>();\n            foreach (var mod in _viewModel.Mods.Where(m => String.IsNullOrEmpty(m.InstallStatus) is false))\n            {\n                if (enabledModsOnly is true && mod.IsEnabled is false)\n                {\n                    Program.helper.Log($\"Skipping mod update install for {mod.Name}: Mod was not enabled when using the \\\"Install Updates (Enabled Mods Only)\\\" option\");\n                    continue;\n                }\n\n                var downloadFilePath = await InstallModViaNexus(mod);\n\n                if (String.IsNullOrEmpty(downloadFilePath))\n                {\n                    continue;\n                }\n                updateFilePaths.Add(downloadFilePath);\n            }\n\n            var addedMods = await AddMods(updateFilePaths.ToArray());\n            await CheckForModUpdates(addedMods, useCache: true, skipCacheCheck: true);\n            await GetCachedModUpdates(_viewModel.Mods.ToList(), skipCacheCheck: true);\n\n            // Delete the downloaded archived mods\n            foreach (var filePath in updateFilePaths.Where(p => File.Exists(p)))\n            {\n                File.Delete(filePath);\n            }\n\n            _viewModel.EvaluateRequirements();\n            _viewModel.UpdateEndorsements();\n            _viewModel.UpdateFilter();\n        }\n\n        private async Task HandleNexusConnection()\n        {\n            // If the user is logged out\n            if (Nexus.Client is null)\n            {\n                // Check if they have a cached key\n                string? apiKey = Nexus.GetCachedKey();\n                if (apiKey is null)\n                {\n                    // If not, display the login window\n                    var loginWindow = new NexusLogin(_viewModel);\n                    loginWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner;\n                    apiKey = await loginWindow.ShowDialog<string>(this);\n\n                    if (String.IsNullOrEmpty(apiKey))\n                    {\n                        return;\n                    }\n                }\n\n                await SetupNexusConnection(apiKey);\n                if (Nexus.Client is null)\n                {\n                    // Failed to create, warn the user\n                    await CreateWarningWindow(Program.translation.Get(\"ui.warning.unable_to_validate_nexus_key\"), Program.translation.Get(\"internal.ok\"));\n                    return;\n                }\n\n                // Store the validated key\n                var obscurer = new SimpleObscure();\n                Program.settings.NexusDetails.Key = SimpleObscure.Encrypt(apiKey, obscurer.Key, obscurer.Vector);\n\n                // Cache the required data\n                File.WriteAllText(Pathing.GetNotionCachePath(), JsonSerializer.Serialize(new PairedKeys { Lock = obscurer.Key, Vector = obscurer.Vector }, new JsonSerializerOptions() { WriteIndented = true }));\n\n                // Set the status\n                _viewModel.NexusStatus = Program.translation.Get(\"internal.connected\");\n\n                // Update any required NexusClient Mods related components\n                await CheckForNexusConnection();\n\n                return;\n            }\n\n            // If the user is logged in\n            // Display information window\n            var detailsWindow = new NexusInfo(Program.settings.NexusDetails);\n            detailsWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner;\n            if (await detailsWindow.ShowDialog<bool>(this) is true)\n            {\n                Nexus.ClearClient();\n            }\n        }\n\n        private async Task HandleModListRefresh()\n        {\n            // Refresh mod list\n            _viewModel.DiscoverMods(Pathing.defaultModPath);\n\n            // Refresh enabled mods\n            _viewModel.EnableModsByProfile(GetCurrentProfile());\n\n            // Refresh cached mods\n            await GetCachedModUpdates(_viewModel.Mods.ToList(), skipCacheCheck: true);\n\n            // Evaluate mod requirements\n            _viewModel.EvaluateRequirements();\n\n            // Check for Nexus Mods connection perform related tasks\n            await CheckForNexusConnection();\n\n            // Hide the required mods\n            _viewModel.HideRequiredMods();\n        }\n\n        internal async Task<bool> ProcessNXMLink(NXM nxmLink)\n        {\n\n            if (Nexus.Client is null)\n            {\n                await CreateWarningWindow(Program.translation.Get(\"ui.message.require_nexus_login\"), Program.translation.Get(\"internal.ok\"));\n                return false;\n            }\n\n\n            if (await ValidateSMAPIPath() is false)\n            {\n                return false;\n            }\n\n            Program.helper.Log($\"Processing NXM link: {nxmLink.Link}\");\n            var processedDownloadLink = await Nexus.Client.GetFileDownloadLink(nxmLink, EnumParser.GetDescription(Program.settings.PreferredNexusServer));\n            Program.helper.Log($\"Processed link: {processedDownloadLink}\");\n\n            if (String.IsNullOrEmpty(processedDownloadLink))\n            {\n                await CreateWarningWindow(String.Format(Program.translation.Get(\"ui.warning.failed_to_get_download_link\"), nxmLink.Link), Program.translation.Get(\"internal.ok\"));\n                return false;\n            }\n\n            // Get the mod details\n            var modDetails = await Nexus.Client.GetModDetailsViaNXM(nxmLink);\n            if (modDetails is null || String.IsNullOrEmpty(modDetails.Name))\n            {\n                await CreateWarningWindow(String.Format(Program.translation.Get(\"ui.warning.failed_to_get_mod_details\"), nxmLink.Link), Program.translation.Get(\"internal.ok\"));\n                return false;\n            }\n\n            var requestWindow = new MessageWindow(String.Format(Program.translation.Get(\"ui.message.confirm_nxm_install\"), modDetails.Name));\n            if (Program.settings.IsAskingBeforeAcceptingNXM is false || await requestWindow.ShowDialog<bool>(this))\n            {\n                var downloadResult = await Nexus.Client.DownloadFileAndGetPath(processedDownloadLink, modDetails.Name);\n                if (downloadResult.ResultKind is DownloadResultKind.Failed)\n                {\n                    await CreateWarningWindow(String.Format(Program.translation.Get(\"ui.warning.failed_nexus_install\"), modDetails.Name), Program.translation.Get(\"internal.ok\"));\n                    return false;\n                }\n                if (downloadResult.ResultKind is DownloadResultKind.UserCanceled)\n                {\n                    // No need for a warning, this is something the user chose intentionally\n                    return false;\n                }\n                string downloadedFilePath = downloadResult.DownloadedModFilePath!;\n\n                var addedMods = await AddMods(new string[] { downloadedFilePath });\n                await CheckForModUpdates(addedMods, useCache: true, skipCacheCheck: true);\n                await GetCachedModUpdates(_viewModel.Mods.ToList(), skipCacheCheck: true);\n\n                // Delete the downloaded archived mod\n                if (File.Exists(downloadedFilePath))\n                {\n                    File.Delete(downloadedFilePath);\n                }\n\n                _viewModel.EvaluateRequirements();\n                _viewModel.UpdateEndorsements();\n                _viewModel.UpdateFilter();\n\n                // Let the user know that the mod was installed via NXM\n                await CreateWarningWindow(String.Format(Program.translation.Get(\"ui.message.succeeded_nexus_install\"), modDetails.Name), Program.translation.Get(\"internal.ok\"));\n            }\n\n            return true;\n        }\n\n        private void SetLockState(bool isWindowLocked, string? lockReason = null)\n        {\n            _viewModel.IsLocked = isWindowLocked;\n            _lockReason = lockReason is null ? String.Empty : lockReason;\n\n            foreach (var ownedWindow in this.OwnedWindows.ToList())\n            {\n                ownedWindow.Close();\n            }\n        }\n\n        private void UpdateLockWindow(string? lockReason = null, int? progress = null, int? maxProgress = null)\n        {\n            Program.helper.Log($\"Attempting to update lock window with the following parameters: {lockReason} {progress} {maxProgress}\");\n            if (_viewModel.IsLocked is false)\n            {\n                return;\n            }\n\n            WarningWindow? lockWindow = OwnedWindows.FirstOrDefault(w => w is WarningWindow) as WarningWindow;\n            if (lockWindow is null)\n            {\n                return;\n            }\n\n            Program.helper.Log($\"Successfully updated lock window!\");\n            lockWindow.UpdateProgress(lockReason, progress, maxProgress);\n        }\n\n        private async Task<UpdateCache?> GetCachedModUpdates(List<Mod> mods, bool skipCacheCheck = false)\n        {\n            int modsToUpdate = 0;\n            UpdateCache? oldUpdateCache = null;\n\n            if (File.Exists(Pathing.GetVersionCachePath()))\n            {\n                oldUpdateCache = JsonSerializer.Deserialize<UpdateCache>(File.ReadAllText(Pathing.GetVersionCachePath()), new JsonSerializerOptions { AllowTrailingCommas = true });\n                if (oldUpdateCache is not null && (skipCacheCheck || _viewModel.IsCheckingForUpdates is false))\n                {\n                    foreach (var modItem in mods)\n                    {\n                        var modUpdateInfo = oldUpdateCache.Mods.FirstOrDefault(m => m.UniqueId.Equals(modItem.UniqueId));\n                        if (modUpdateInfo is null)\n                        {\n                            continue;\n                        }\n\n                        if (modItem.IsModOutdated(modUpdateInfo.SuggestedVersion))\n                        {\n                            modItem.UpdateUri = modUpdateInfo.Link;\n                            modItem.SuggestedVersion = modUpdateInfo.SuggestedVersion;\n                            modItem.Status = modUpdateInfo.Status;\n\n                            modsToUpdate++;\n                        }\n                        if (modUpdateInfo.Status != WikiCompatibilityStatus.Unknown && modUpdateInfo.Status != WikiCompatibilityStatus.Ok)\n                        {\n                            modItem.UpdateUri = modUpdateInfo.Link;\n                            modItem.SuggestedVersion = modUpdateInfo.SuggestedVersion;\n                            modItem.Status = modUpdateInfo.Status;\n                        }\n                    }\n                }\n            }\n\n            // Update the status to let the user know the update is finished\n            _viewModel.ModsWithCachedUpdates = modsToUpdate;\n            _viewModel.UpdateStatusText = Program.translation.Get(\"ui.main_window.button.update_status.generic\");\n\n            return oldUpdateCache;\n        }\n\n        private async Task CheckForModUpdates(List<Mod> mods, bool useCache = false, bool probe = false, bool skipCacheCheck = false)\n        {\n            try\n            {\n                // Only check if Stardrop isn't currently checking for updates\n                UpdateCache? oldUpdateCache = await GetCachedModUpdates(mods, skipCacheCheck);\n\n                // Check if this was just a probe\n                if (probe || _viewModel.IsCheckingForUpdates is true)\n                {\n                    return;\n                }\n                Program.helper.Log($\"Attempting to check for mod updates {(useCache ? \"via cache\" : \"via smapi.io\")}\");\n                _viewModel.IsCheckingForUpdates = true;\n\n                // Close the menu, as it will remain open until the process is complete\n                var mainMenu = this.FindControl<Menu>(\"mainMenu\");\n                if (mainMenu.IsOpen)\n                {\n                    mainMenu.Close();\n                }\n\n                // Update the status to let the user know the update is polling\n                _viewModel.UpdateStatusText = Program.translation.Get(\"ui.main_window.button.update_status.updating\");\n\n                // Set the environment variable for the mod path\n                var enabledModsPath = Path.Combine(Pathing.GetSelectedModsFolderPath());\n                Environment.SetEnvironmentVariable(\"SMAPI_MODS_PATH\", enabledModsPath);\n\n                if (Program.settings.GameDetails is null || String.IsNullOrEmpty(Program.settings.GameDetails.SmapiVersion) || Program.settings.GameDetails.HasBadGameVersion() || Program.settings.GameDetails.HasSMAPIUpdated(SMAPI.GetVersion()))\n                {\n                    var smapiLogPath = Path.Combine(Pathing.GetSmapiLogFolderPath(), \"SMAPI-latest.txt\");\n                    Program.helper.Log($\"Checking for SMAPI-latest.txt at path: {Pathing.GetSmapiLogFolderPath()}\");\n                    if (File.Exists(smapiLogPath))\n                    {\n                        // Parse SMAPI's log\n                        Program.helper.Log($\"Grabbing game details (SMAPI / SDV versions) from SMAPI's log file.\");\n\n                        using (var fileStream = new FileStream(smapiLogPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))\n                        using (var reader = new StreamReader(fileStream))\n                        {\n                            while (reader.Peek() >= 0)\n                            {\n                                var line = reader.ReadLine();\n                                if (Program.gameDetailsPattern.IsMatch(line))\n                                {\n                                    var match = Program.gameDetailsPattern.Match(line);\n                                    Program.settings.GameDetails = new GameDetails(match.Groups[\"gameVersion\"].ToString(), match.Groups[\"smapiVersion\"].ToString(), match.Groups[\"system\"].ToString());\n                                }\n                            }\n                        }\n                    }\n                    else\n                    {\n                        await CreateWarningWindow(String.Format(Program.translation.Get(\"ui.warning.unable_to_locate_log\"), _viewModel.Version), Program.translation.Get(\"internal.ok\"));\n                        Program.helper.Log($\"Unable to locate SMAPI-latest.txt\", Helper.Status.Alert);\n\n                        _viewModel.IsCheckingForUpdates = false;\n                        return;\n                    }\n                }\n\n                if (Program.settings.GameDetails is null || String.IsNullOrEmpty(Program.settings.GameDetails.SmapiVersion))\n                {\n                    await CreateWarningWindow(String.Format(Program.translation.Get(\"ui.warning.unable_to_read_log\"), _viewModel.Version), Program.translation.Get(\"internal.ok\"));\n                    Program.helper.Log($\"SMAPI started but Stardrop was unable to read SMAPI-latest.txt. Mods will not be checked for updates.\", Helper.Status.Alert);\n\n                    _viewModel.IsCheckingForUpdates = false;\n                    return;\n                }\n                else\n                {\n                    _viewModel.SmapiVersion = Program.settings.GameDetails.SmapiVersion;\n                }\n\n                // Fetch the mods to see if there are updates available\n                if (useCache && oldUpdateCache is not null)\n                {\n                    oldUpdateCache.LastRuntime = DateTime.Now;\n                }\n\n                int modsToUpdate = 0;\n                var updateCache = useCache && oldUpdateCache is not null ? oldUpdateCache : new UpdateCache(DateTime.Now);\n                var modUpdateData = await SMAPI.GetModUpdateData(Program.settings.GameDetails, mods);\n                foreach (var modItem in mods)\n                {\n                    var updateLink = String.Empty;\n                    var modPageLink = String.Empty;\n                    var recommendedVersion = String.Empty;\n                    var status = WikiCompatibilityStatus.Unknown;\n\n                    // Prep the data to be checked\n                    var suggestedUpdateData = modUpdateData.Where(m => modItem.UniqueId.Equals(m.Id, StringComparison.OrdinalIgnoreCase) && m.SuggestedUpdate is not null).Select(m => m.SuggestedUpdate).FirstOrDefault();\n                    var metaData = modUpdateData.Where(m => modItem.UniqueId.Equals(m.Id, StringComparison.OrdinalIgnoreCase) && m.Metadata is not null).Select(m => m.Metadata).FirstOrDefault();\n                    if (suggestedUpdateData is not null)\n                    {\n                        updateLink = suggestedUpdateData.Url;\n                        if (metaData is not null && metaData.CompatibilityStatus != WikiCompatibilityStatus.Ok)\n                        {\n                            status = metaData.CompatibilityStatus;\n                        }\n                        recommendedVersion = suggestedUpdateData.Version;\n\n                        modsToUpdate++;\n                    }\n                    else if (metaData is not null && metaData.CompatibilityStatus != WikiCompatibilityStatus.Unknown && metaData.CompatibilityStatus != ModEntryMetadata.WikiCompatibilityStatus.Ok)\n                    {\n                        status = metaData.CompatibilityStatus;\n                        if (metaData.CompatibilityStatus == WikiCompatibilityStatus.Unofficial && metaData.Unofficial is not null && modItem.IsModOutdated(metaData.Unofficial.Version))\n                        {\n                            updateLink = metaData.Unofficial.Url;\n                            recommendedVersion = metaData.Unofficial.Version;\n\n                            modsToUpdate++;\n                        }\n                        else if (metaData.Main is not null)\n                        {\n                            updateLink = metaData.Main.Url;\n                            recommendedVersion = metaData.Main.Version;\n                        }\n                    }\n\n                    // Check for smapi.io's suggested webpage\n                    if (metaData is not null)\n                    {\n                        modPageLink = metaData.CustomUrl;\n                        if (String.IsNullOrEmpty(modPageLink) && metaData.Main is not null)\n                        {\n                            modPageLink = metaData.Main.Url;\n                        }\n                    }\n\n                    modItem.UpdateUri = updateLink;\n                    modItem.ModPageUri = modPageLink;\n                    modItem.SuggestedVersion = recommendedVersion;\n                    modItem.Status = status;\n\n                    if (!String.IsNullOrEmpty(modItem.ParsedStatus))\n                    {\n                        Program.helper.Log($\"Update available for {modItem.UniqueId} (v{modItem.SuggestedVersion}): {modItem.UpdateUri}\");\n                        if (updateCache.Mods.FirstOrDefault(m => m.UniqueId.Equals(modItem.UniqueId)) is ModUpdateInfo modInfo && modInfo is not null)\n                        {\n                            modInfo.SuggestedVersion = recommendedVersion;\n                            modInfo.Status = status;\n                        }\n                        else\n                        {\n                            updateCache.Mods.Add(new ModUpdateInfo(modItem.UniqueId, recommendedVersion, status, modItem.UpdateUri));\n                        }\n                    }\n                }\n\n                // Cache the update data\n                if (!Directory.Exists(Pathing.GetCacheFolderPath()))\n                {\n                    Directory.CreateDirectory(Pathing.GetCacheFolderPath());\n                }\n                File.WriteAllText(Pathing.GetVersionCachePath(), JsonSerializer.Serialize(updateCache, new JsonSerializerOptions() { WriteIndented = true }));\n\n                // Get cached key data\n                List<ModKeyInfo> modKeysCache = new List<ModKeyInfo>();\n                if (File.Exists(Pathing.GetKeyCachePath()))\n                {\n                    modKeysCache = JsonSerializer.Deserialize<List<ModKeyInfo>>(File.ReadAllText(Pathing.GetKeyCachePath()), new JsonSerializerOptions { AllowTrailingCommas = true });\n                }\n\n                // Update the cached key data\n                foreach (var modEntry in modUpdateData.Where(m => m.Metadata is not null))\n                {\n                    var modUrl = String.IsNullOrEmpty(modEntry.Metadata.CustomUrl) && modEntry.Metadata.Main is not null ? modEntry.Metadata.Main.Url : modEntry.Metadata.CustomUrl;\n                    if (modKeysCache.FirstOrDefault(m => m.UniqueId.Equals(modEntry.Id)) is ModKeyInfo keyInfo && keyInfo is not null)\n                    {\n                        keyInfo.Name = modEntry.Metadata.Name;\n                        keyInfo.PageUrl = modUrl;\n                    }\n                    else\n                    {\n                        modKeysCache.Add(new ModKeyInfo() { Name = modEntry.Metadata.Name, UniqueId = modEntry.Id, PageUrl = modUrl });\n                    }\n                }\n\n                // Cache the key data\n                File.WriteAllText(Pathing.GetKeyCachePath(), JsonSerializer.Serialize(modKeysCache, new JsonSerializerOptions() { WriteIndented = true }));\n\n                // Re-evaluate all mod requirements (to check for cached names)\n                _viewModel.EvaluateRequirements();\n\n                // Update the status to let the user know the update is finished\n                _viewModel.ModsWithCachedUpdates = modsToUpdate;\n                _viewModel.UpdateStatusText = Program.translation.Get(\"ui.main_window.button.update_status.generic\");\n\n                Program.helper.Log($\"Mod update check {(useCache ? \"via cache\" : \"via smapi.io\")} completed without error\");\n            }\n            catch (Exception ex)\n            {\n                Program.helper.Log($\"Failed to get mod updates via smapi.io: {ex}\", Helper.Status.Alert);\n                _viewModel.UpdateStatusText = Program.translation.Get(\"ui.main_window.button.update_status.failed\");\n            }\n\n            _viewModel.IsCheckingForUpdates = false;\n        }\n\n        private async Task CheckForNexusConnection()\n        {\n            // Create the client, and open access to Nexus if we haven't already done it\n            await SetupNexusConnection(Nexus.GetCachedKey());\n\n            Program.helper.Log($\"Attempting to check for Nexus Mods connection (Has valid client: {Nexus.Client is not null})\");\n\n            if (Nexus.Client is null)\n            {\n                return;\n            }\n\n            // Validate the user's cached key to ensure it's still valid\n            bool isKeyValid = await Nexus.Client.ValidateKey();\n\n            if (isKeyValid is true)\n            {\n                _viewModel.NexusStatus = Program.translation.Get(\"internal.connected\");\n                _viewModel.NexusLimits = $\"(Remaining Daily Requests: {Nexus.Client.DailyRequestsRemaining}) \";\n\n                // Gather any endorsements\n                _viewModel.UpdateEndorsements();\n\n                // Show endorsements\n                _viewModel.ShowEndorsements = true;\n\n                // Show thumbnails\n                if (Program.settings.ShowModThumbnails)\n                {\n                    _viewModel.UpdateThumbnails();\n                }\n\n                // Show Nexus mod download column, if user is premium\n                _viewModel.ShowInstalls = Program.settings.NexusDetails.IsPremium;\n            }\n            else\n            {\n                Program.helper.Log($\"Nexus Mods connection failed.\");\n\n                Program.settings.NexusDetails = new Models.Nexus.NexusUser();\n                Nexus.ClearClient();\n            }\n        }\n\n        private async Task SetupNexusConnection(string? apiKey)\n        {\n            if (apiKey is null)\n            {\n                return;\n            }\n\n            if (Nexus.Client is not null)\n            {\n                return;\n            }\n\n            // Create a global Nexus client. Further setup gets taken care of in NexusClientChanged.\n            await Nexus.CreateClient(apiKey);\n        }\n\n        private async void NexusClientChanged(NexusClient? oldClient, NexusClient? newClient)\n        {\n            // Tear down old client stuff, if an old client is being discarded\n            if (oldClient is not null)\n            {\n                oldClient.DailyRequestLimitsChanged -= NexusDailyLimitsChanged;\n                _viewModel.NexusStatus = Program.translation.Get(\"internal.disconnected\");\n                _viewModel.ShowEndorsements = false;\n                _viewModel.ShowInstalls = false;\n            }\n\n            if (newClient is not null)\n            {\n                newClient.DailyRequestLimitsChanged += NexusDailyLimitsChanged;\n\n                // Verify NXM protocol usage\n                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows) && NXMProtocol.Validate(Program.executablePath) is false)\n                {\n                    var requestWindow = new MessageWindow(Program.translation.Get(\"ui.message.confirm_nxm_association\"));\n                    if (await requestWindow.ShowDialog<bool>(this))\n                    {\n                        if (NXMProtocol.Register(Program.executablePath) is false)\n                        {\n                            await new WarningWindow(Program.translation.Get(\"ui.warning.failed_to_set_association\"), Program.translation.Get(\"internal.ok\")).ShowDialog(this);\n                        }\n                    }\n                }\n            }\n        }\n\n        private void NexusDailyLimitsChanged(object? sender, EventArgs e)\n        {\n            NexusClient? client = sender as NexusClient;\n            if (client is not null)\n            {\n                _viewModel.NexusStatus = Program.translation.Get(\"internal.connected\");\n                _viewModel.NexusLimits = $\"(Remaining Daily Requests: {client.DailyRequestsRemaining}) \";\n            }\n        }\n\n        private void AdjustWindowState()\n        {\n            this.WindowState = this.WindowState == WindowState.Normal ? WindowState.Maximized : WindowState.Normal;\n        }\n\n        /// <summary>\n        /// Enable all existing requirements for <paramref name=\"mod\" />.\n        /// </summary>\n        /// <param name=\"mod\">The mod whose requirements to enable.</param>\n        private void EnableRequirements(Mod mod)\n        {\n            foreach (var requirement in mod.Requirements.Where(r => r.IsRequired))\n            {\n                var requiredMod = _viewModel.Mods.FirstOrDefault(m => m.UniqueId.Equals(requirement.UniqueID, StringComparison.OrdinalIgnoreCase));\n                if (requiredMod is not null)\n                {\n                    requiredMod.IsEnabled = true;\n\n                    // Enable the requirement's requirements\n                    EnableRequirements(requiredMod);\n                }\n            }\n        }\n\n        /// <summary>\n        /// Disable all mods that require the mod <paramref name=\"mod\" />.\n        /// </summary>\n        /// <param name=\"mod\">The mod to look for in requirements.</param>\n        private void DisableRequirements(Mod mod)\n        {\n            foreach (var childMod in _viewModel.Mods.Where(m => m.Requirements.Any(r => r.IsRequired && r.UniqueID.Equals(mod.UniqueId, StringComparison.OrdinalIgnoreCase))))\n            {\n                if (childMod is not null)\n                {\n                    childMod.IsEnabled = false;\n\n                    // Disable the requirement's requirements\n                    DisableRequirements(childMod);\n                }\n            }\n        }\n\n        private Profile GetCurrentProfile()\n        {\n            return this.FindControl<ComboBox>(\"profileComboBox\").SelectedItem as Profile;\n        }\n\n        private void UpdateProfile(Profile profile)\n        {\n            // Hide the required mods\n            _viewModel.HideRequiredMods();\n\n            // Update the profile's enabled mods\n            _editorView.UpdateProfile(profile, _viewModel.Mods.Where(m => m.IsEnabled).Select(m => m.UniqueId).ToList());\n\n            // Update the EnabledModCount\n            _viewModel.EnabledModCount = _viewModel.Mods.Where(m => m.IsEnabled && !m.IsHidden).Count();\n        }\n\n        private async Task<string?> InstallModViaNexus(Mod mod)\n        {\n            if (mod is null || mod.InstallState != InstallState.Unknown)\n            {\n                return null;\n            }\n\n            var modId = mod.GetNexusId();\n            if (modId is null || Nexus.Client is null)\n            {\n                await CreateWarningWindow(String.Format(Program.translation.Get(\"ui.warning.unable_nexus_install\"), mod.Name), Program.translation.Get(\"internal.ok\"));\n                return null;\n            }\n            mod.InstallState = InstallState.Downloading;\n\n            var modFile = await Nexus.Client.GetFileByVersion(modId.Value, mod.SuggestedVersion, modFlag: mod.GetNexusFlag());\n            if (modFile is null)\n            {\n                await CreateWarningWindow(String.Format(Program.translation.Get(\"ui.warning.failed_nexus_install\"), mod.Name), Program.translation.Get(\"internal.ok\"));\n                mod.InstallState = InstallState.Unknown;\n                return null;\n            }\n\n            var modDownloadLink = await Nexus.Client.GetFileDownloadLink(modId.Value, modFile.Id, serverName: EnumParser.GetDescription(Program.settings.PreferredNexusServer));\n            if (modDownloadLink is null)\n            {\n                await CreateWarningWindow(String.Format(Program.translation.Get(\"ui.warning.failed_nexus_install\"), mod.Name), Program.translation.Get(\"internal.ok\"));\n                mod.InstallState = InstallState.Unknown;\n                return null;\n            }\n\n            var downloadResult = await Nexus.Client.DownloadFileAndGetPath(modDownloadLink, modFile.Name);\n            if (downloadResult.ResultKind is DownloadResultKind.UserCanceled)\n            {\n                mod.InstallState = InstallState.Unknown;\n                // No warning, as the user triggered this intentionally\n                return null;\n            }\n            if (downloadResult.ResultKind is DownloadResultKind.Failed)\n            {\n                await CreateWarningWindow(String.Format(Program.translation.Get(\"ui.warning.failed_nexus_install\"), mod.Name), Program.translation.Get(\"internal.ok\"));\n                mod.InstallState = InstallState.Unknown;\n                return null;\n            }\n\n            mod.InstallState = InstallState.Installing;\n\n            return downloadResult.DownloadedModFilePath;\n        }\n\n        public bool TryDeleteMod(Mod mod, int retries = 3)\n        {\n            try\n            {\n                DeleteMod(mod);\n            }\n            catch (Exception ex)\n            {\n                if (retries > 0)\n                {\n                    return TryDeleteMod(mod, retries - 1);\n                }\n\n                Program.helper.Log($\"Failed to delete the mod {mod.Name}: {ex}\", Utilities.Helper.Status.Warning);\n                return false;\n            }\n\n            return true;\n        }\n\n        private void DeleteMod(Mod mod)\n        {\n            Program.helper.Log($\"Attempting to delete the mod {mod.Name}\");\n\n            var targetDirectory = new DirectoryInfo(mod.ModFileInfo.DirectoryName);\n            if (targetDirectory is not null && targetDirectory.Exists)\n            {\n                targetDirectory.Delete(true);\n            }\n        }\n\n        private async Task<List<Mod>> AddMods(string[]? filePaths)\n        {\n            Guid request = new Guid();\n\n            // Wait until current lock is finished before doing further installs\n            Program.helper.Log($\"Add mods request received ({request}): Pending\");\n            while (_viewModel.IsLocked)\n            {\n                await Task.Delay(500);\n            }\n            Program.helper.Log($\"Add mods request received ({request}): Accepted\");\n\n            await HandleModListRefresh();\n\n            var addedMods = new List<Mod>();\n            if (filePaths is null)\n            {\n                return addedMods;\n            }\n\n            // Lock the window\n            int totalMods = filePaths.Length;\n            SetLockState(true, String.Format(Program.translation.Get(\"ui.warning.install_mod_attempt_count\"), totalMods));\n\n            // Get the local data\n            ClientData localDataCache = new ClientData();\n            if (File.Exists(Pathing.GetDataCachePath()))\n            {\n                localDataCache = JsonSerializer.Deserialize<ClientData>(File.ReadAllText(Pathing.GetDataCachePath()), new JsonSerializerOptions { AllowTrailingCommas = true });\n            }\n\n            // Export zip to the default mods folder\n            int currentModIndex = 1;\n            List<string> warnings = new List<string>();\n            foreach (string fileFullName in filePaths)\n            {\n                try\n                {\n                    // Extract the archive data\n                    using (var archive = ArchiveFactory.Open(fileFullName))\n                    {\n                        Dictionary<string, Manifest?> pathToManifests = new Dictionary<string, Manifest?>();\n                        foreach (var manifest in archive.Entries.Where(e => Path.GetFileName(e.Key).Equals(\"manifest.json\", StringComparison.OrdinalIgnoreCase)))\n                        {\n                            Program.helper.Log(manifest.Key);\n                            pathToManifests[manifest.Key] = await ManifestParser.GetDataAsync(manifest);\n                        }\n\n                        // Warn and skip the install logic if the given archive has no manifest.json\n                        if (pathToManifests.Count == 0)\n                        {\n                            warnings.Add(String.Format(Program.translation.Get(\"ui.warning.no_manifest\"), fileFullName));\n                            continue;\n                        }\n\n                        // If this is a mod update and if the Manifest.UpdateCautionMessage has a value, display message (and confirm if user wants to continue with mod update)\n                        bool shouldProceedWithUpdate = true;\n                        foreach (var manifest in pathToManifests.Values.Where(m => m is not null && HasModInstalled(m.UniqueID) is true && string.IsNullOrEmpty(m.UpdateCautionMessage) is false))\n                        {\n                            var requestWindow = new MessageWindow(String.Format(Program.translation.Get(\"ui.message.confirm_mod_update_caution\"), manifest!.Name, manifest!.UpdateCautionMessage)) { Topmost = true };\n                            if (await requestWindow.ShowDialog<bool>(this) is false)\n                            {\n                                Program.helper.Log($\"User elected to skip mod update due to given Manifest.UpdateCautionMessage message for mod {manifest!.UniqueID}:{manifest!.UpdateCautionMessage}\");\n                                shouldProceedWithUpdate = false;\n                                break;\n                            }\n                        }\n\n                        // Skip updating if user elected to skip any of the bundled mods due to Manifest.UpdateCautionMessage\n                        if (shouldProceedWithUpdate is false)\n                        {\n                            continue;\n                        }\n\n                        int currentManifestIndex = 1;\n                        bool alwaysAskToDelete = Program.settings.AlwaysAskToDelete;\n                        foreach (var manifestPath in pathToManifests.Keys)\n                        {\n                            var manifest = pathToManifests[manifestPath];\n\n                            // If the archive doesn't have a manifest, warn the user\n                            bool isUpdate = false;\n                            if (manifest is not null)\n                            {\n                                var installPath = Program.settings.ModInstallPath;\n                                if (_viewModel.Mods.FirstOrDefault(m => m.UniqueId.Equals(manifest.UniqueID, StringComparison.OrdinalIgnoreCase)) is Mod mod && mod is not null && mod.ModFileInfo.Directory is not null)\n                                {\n                                    if (manifest.DeleteOldVersion is false && alwaysAskToDelete is true)\n                                    {\n                                        string warningMessage = Program.translation.Get(\"ui.message.confirm_mod_update_method_no_config\");\n                                        if (mod.HasConfig)\n                                        {\n                                            warningMessage = Program.translation.Get(\"ui.message.confirm_mod_update_method\");\n                                            if (Program.settings.EnableProfileSpecificModConfigs)\n                                            {\n                                                warningMessage = Program.translation.Get(\"ui.message.confirm_mod_update_method_preserved\");\n                                            }\n                                        }\n\n                                        var requestWindow = new FlexibleOptionWindow(String.Format(warningMessage, manifest.Name), Program.translation.Get(\"internal.yes\"), Program.translation.Get(\"internal.yes_all\"), Program.translation.Get(\"internal.no\"))\n                                        {\n                                            Topmost = true\n                                        };\n                                        Choice response = await requestWindow.ShowDialog<Choice>(this);\n                                        if (response == Choice.First || response == Choice.Second)\n                                        {\n                                            if (response == Choice.Second)\n                                            {\n                                                alwaysAskToDelete = false;\n                                            }\n\n                                            // Delete old version\n                                            UpdateLockWindow(String.Format(Program.translation.Get(\"ui.warning.mod_deleting\"), manifest.Name), currentManifestIndex, pathToManifests.Keys.Count);\n                                            if (TryDeleteMod(mod) is false)\n                                            {\n                                                warnings.Add(String.Format(Program.translation.Get(\"ui.warning.failed_to_delete_during_update\"), mod.Name));\n                                            }\n                                        }\n                                    }\n                                    else\n                                    {\n                                        // Delete old version\n                                        UpdateLockWindow(String.Format(Program.translation.Get(\"ui.warning.mod_deleting\"), manifest.Name), currentManifestIndex, pathToManifests.Keys.Count);\n                                        if (TryDeleteMod(mod) is false)\n                                        {\n                                            warnings.Add(String.Format(Program.translation.Get(\"ui.warning.failed_to_delete_during_update\"), mod.Name));\n                                        }\n                                    }\n\n                                    isUpdate = true;\n                                    installPath = mod.ModFileInfo.Directory.FullName;\n\n                                    // Set the LastUpdateTimestamp\n                                    if (localDataCache.ModInstallData is not null && localDataCache.ModInstallData.Any(m => m.UniqueId.Equals(manifest.UniqueID, StringComparison.OrdinalIgnoreCase)))\n                                    {\n                                        var updatedTimestamp = DateTime.Now;\n                                        mod.LastUpdateTimestamp = updatedTimestamp;\n                                        localDataCache.ModInstallData.First(m => m.UniqueId.Equals(manifest.UniqueID, StringComparison.OrdinalIgnoreCase)).LastUpdateTimestamp = updatedTimestamp;\n                                    }\n                                }\n                                else if (String.IsNullOrEmpty(manifestPath.Replace(\"manifest.json\", String.Empty, StringComparison.OrdinalIgnoreCase)))\n                                {\n                                    installPath = Path.Combine(installPath, manifest.UniqueID);\n                                }\n\n                                // Create the base directory, if needed\n                                if (Directory.Exists(installPath) is false)\n                                {\n                                    Directory.CreateDirectory(installPath);\n                                }\n\n                                // Set lock state to let user know we are installing the mod\n                                string individualProgressText = String.Format(isUpdate ? Program.translation.Get(\"ui.warning.mod_updating\") : Program.translation.Get(\"ui.warning.mod_installing\"), manifest.Name);\n                                string modProgressText = String.Format(\"[{0} / {1}] Mods\", currentModIndex, totalMods);\n                                string manifestProgressText = String.Format(\"[{0} / {1}] Manifests\", currentManifestIndex, pathToManifests.Keys.Count);\n                                UpdateLockWindow(String.Concat(modProgressText, \"\\n\", manifestProgressText, \"\\n\\n\", individualProgressText), currentManifestIndex, pathToManifests.Keys.Count);\n\n                                Program.helper.Log($\"Install path for mod {manifest.UniqueID}:{installPath}\");\n                                var manifestFolderPath = manifestPath.Replace(\"manifest.json\", String.Empty, StringComparison.OrdinalIgnoreCase);\n                                foreach (var entry in archive.Entries.Where(e => e.Key.StartsWith(manifestFolderPath)))\n                                {\n                                    if (entry.Key.Contains(\"__MACOSX\", StringComparison.OrdinalIgnoreCase) || entry.Key.Contains(\".DS_Store\", StringComparison.OrdinalIgnoreCase))\n                                    {\n                                        continue;\n                                    }\n                                    var outputPath = Path.Combine(installPath, manifestFolderPath, String.IsNullOrEmpty(manifestFolderPath) ? entry.Key : Path.GetRelativePath(manifestFolderPath, entry.Key));\n\n                                    if (String.IsNullOrEmpty(manifestFolderPath) is false)\n                                    {\n                                        var installDirectory = new DirectoryInfo(installPath);\n                                        var manifestDirectory = new DirectoryInfo(manifestFolderPath);\n                                        if (installDirectory.Exists && (installDirectory.Name.Equals(manifestDirectory.Name, StringComparison.OrdinalIgnoreCase) || installDirectory.Name.Equals(manifest.UniqueID)))\n                                        {\n                                            outputPath = Path.Combine(installPath, String.IsNullOrEmpty(manifestFolderPath) ? entry.Key : Path.GetRelativePath(manifestFolderPath, entry.Key));\n\n                                            Program.helper.Log(outputPath);\n                                        }\n                                    }\n                                    outputPath = Regex.Replace(outputPath, @\"\\s+\\/\", \"/\");\n\n                                    // Create the default location if it doesn't existe\n                                    var outputFolder = Path.GetDirectoryName(outputPath);\n                                    if (String.IsNullOrEmpty(outputFolder))\n                                    {\n                                        continue;\n                                    }\n                                    else if (Directory.Exists(outputFolder) is false)\n                                    {\n                                        Directory.CreateDirectory(outputFolder);\n                                    }\n\n                                    if (entry.IsDirectory is false)\n                                    {\n                                        Program.helper.Log($\"Writing mod file to {outputPath}\");\n                                        await Task.Run(() => entry.WriteToFile(outputPath, new ExtractionOptions() { ExtractFullPath = false, Overwrite = true }));\n                                    }\n                                }\n\n                                addedMods.Add(new Mod(manifest, new FileInfo(Path.Join(installPath, manifestFolderPath)), manifest.UniqueID, manifest.Version, manifest.Name, manifest.Description, manifest.Author));\n                            }\n                            else\n                            {\n                                warnings.Add(String.Format(Program.translation.Get(\"ui.warning.no_manifest\"), fileFullName));\n                            }\n\n                            currentManifestIndex += 1;\n                        }\n                    }\n                }\n                catch (Exception ex)\n                {\n                    Program.helper.Log($\"Failed to unzip the file {fileFullName} due to the following error: {ex}\", Utilities.Helper.Status.Warning);\n                    warnings.Add(String.Format(Program.translation.Get(\"ui.warning.unable_to_load_mod\"), fileFullName));\n                }\n\n                currentModIndex += 1;\n            }\n\n            // Display warnings\n            SetLockState(false);\n            foreach (string warningMessage in warnings)\n            {\n                await CreateWarningWindow(warningMessage, Program.translation.Get(\"internal.ok\"));\n            }\n\n            // Cache the local data\n            File.WriteAllText(Pathing.GetDataCachePath(), JsonSerializer.Serialize(localDataCache, new JsonSerializerOptions() { WriteIndented = true }));\n\n            // Refresh mod list\n            _viewModel.DiscoverMods(Pathing.defaultModPath);\n\n            // Refresh enabled mods\n            _viewModel.EnableModsByProfile(GetCurrentProfile());\n\n            // Handle automatically enabling the added mods, if the setting is enabled\n            if (Program.settings.EnableModsOnAdd is true)\n            {\n                _viewModel.ForceModState(GetCurrentProfile(), addedMods, modEnableState: true);\n\n                foreach (var mod in addedMods)\n                {\n                    EnableRequirements(mod);\n                }\n            }\n\n            // Update the current profile\n            UpdateProfile(GetCurrentProfile());\n\n            Program.helper.Log($\"Add mods request received ({request}): Processed\");\n            return addedMods;\n        }\n\n        private bool HasModInstalled(string uniqueID)\n        {\n            return _viewModel.Mods.Any(m => m.UniqueId.Equals(uniqueID, StringComparison.OrdinalIgnoreCase));\n        }\n\n        private void CreateDirectoryJunctions(List<string> arguments)\n        {\n            // Prepare the process\n            var processInfo = new ProcessStartInfo\n            {\n                FileName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? \"cmd\" : \"/usr/bin/env\",\n                Arguments = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? $\"/C {string.Join(\" & \", arguments)}\" : $\"bash -c \\\"{string.Join(\" ; \", arguments)}\\\"\",\n                RedirectStandardOutput = true,\n                RedirectStandardError = true,\n                CreateNoWindow = true,\n                UseShellExecute = false\n            };\n\n            try\n            {\n                Program.helper.Log($\"Starting process to link folders via terminal using {processInfo.FileName} and an argument length of {processInfo.Arguments.Length}\");\n\n                using (var process = Process.Start(processInfo))\n                {\n                    // Synchronously read the standard output / error of the spawned process.\n                    var standardOutput = process.StandardOutput.ReadToEnd();\n                    var errorOutput = process.StandardError.ReadToEnd();\n\n                    Program.helper.Log($\"Standard Output: {(String.IsNullOrWhiteSpace(standardOutput) ? \"Empty\" : String.Concat(Environment.NewLine, standardOutput))}\");\n                    Program.helper.Log($\"Error Output: {(String.IsNullOrWhiteSpace(errorOutput) ? \"Empty\" : String.Concat(Environment.NewLine, errorOutput))}\");\n\n                    if (!String.IsNullOrWhiteSpace(errorOutput))\n                    {\n                        Program.helper.Log($\"Printing full argument chain due to error output being detected: {Environment.NewLine}{processInfo.Arguments}\");\n                    }\n\n                    process.WaitForExit();\n                }\n\n                Program.helper.Log($\"Link process completed\");\n            }\n            catch (Exception ex)\n            {\n                Program.helper.Log($\"Process failed for creating mod folder links using {processInfo.FileName} with arguments: {processInfo.Arguments}\");\n                Program.helper.Log($\"Exception for failed mod folder link creation: {ex}\");\n            }\n        }\n\n        private void UpdateEnabledModsFolder(Profile profile, string enabledModsPath)\n        {\n            // Clear any previous linked mods\n            foreach (var linkedModFolder in new DirectoryInfo(enabledModsPath).GetDirectories())\n            {\n                linkedModFolder.Delete(true);\n            }\n\n            string spacing = String.Concat(Environment.NewLine, \"\\t\");\n            Program.helper.Log($\"Creating links for the following enabled mods from profile {profile.Name}:{spacing}{String.Join(spacing, profile.EnabledModIds)}\");\n\n            // Link the enabled mods via a chained command\n            List<string> arguments = new List<string>();\n            foreach (string modId in _viewModel.Mods.Where(m => m.IsEnabled).Select(m => m.UniqueId))\n            {\n                var mod = _viewModel.Mods.FirstOrDefault(m => m.UniqueId == modId);\n                if (mod is null)\n                {\n                    continue;\n                }\n\n                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))\n                {\n                    var longPathPrefix = @\"\\\\?\\\";\n\n                    var linkPath = Path.Combine(enabledModsPath, mod.ModFileInfo.Directory.Name);\n                    if (linkPath.Length >= 260)\n                    {\n                        linkPath = longPathPrefix + linkPath;\n                    }\n\n                    var modDirectoryName = mod.ModFileInfo.DirectoryName;\n                    if (Path.Combine(enabledModsPath, mod.ModFileInfo.Directory.Name).Length >= 260)\n                    {\n                        modDirectoryName = longPathPrefix + modDirectoryName;\n                    }\n\n                    arguments.Add($\"mklink /J \\\"{linkPath}\\\" \\\"{modDirectoryName}\\\"\");\n                }\n                else\n                {\n                    var edq = \"\\\\\\\"\"; // Escaped double quotes, to prevent issues with paths that contain single quotes\n                    arguments.Add($\"ln -sf {edq}{mod.ModFileInfo.DirectoryName}{edq} {edq}{Path.Combine(enabledModsPath, mod.ModFileInfo.Directory.Name)}{edq}\");\n                }\n            }\n\n            // Attempt to create the directory junction\n            try\n            {\n                int maxArgumentLength = 8000;\n                if (arguments.Sum(a => a.Length) + (arguments.Count * 3) >= maxArgumentLength)\n                {\n                    int argumentIndex = 0;\n                    var segmentedArguments = new List<string>();\n                    while (arguments.ElementAtOrDefault(argumentIndex) is not null)\n                    {\n                        if (arguments[argumentIndex].Length + segmentedArguments.Sum(a => a.Length) + (segmentedArguments.Count * 3) >= maxArgumentLength)\n                        {\n                            // Create the process and clear segmentedArguments\n                            CreateDirectoryJunctions(segmentedArguments);\n                            segmentedArguments.Clear();\n                        }\n                        segmentedArguments.Add(arguments[argumentIndex]);\n                        argumentIndex++;\n\n                        // Check if the next index is null, if so then push the changes\n                        if (arguments.ElementAtOrDefault(argumentIndex) is null && segmentedArguments.Count > 0)\n                        {\n                            CreateDirectoryJunctions(segmentedArguments);\n                        }\n                    }\n                }\n                else\n                {\n                    CreateDirectoryJunctions(arguments);\n                }\n            }\n            catch (Exception ex)\n            {\n                Program.helper.Log($\"Failed to link all mod folders: {Environment.NewLine}{ex}\");\n            }\n\n            Program.helper.Log($\"Finished creating all linked mod folders\");\n        }\n\n        private void OpenNativeExplorer(string folderPath)\n        {\n            try\n            {\n                if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))\n                {\n                    Process.Start(\"explorer\", folderPath.Replace(\"&\", \"^&\"));\n                }\n                else\n                {\n                    var processInfo = new ProcessStartInfo\n                    {\n                        FileName = folderPath,\n                        CreateNoWindow = false,\n                        UseShellExecute = true\n                    };\n\n                    var process = Process.Start(processInfo);\n                }\n            }\n            catch (Exception ex)\n            {\n                Program.helper.Log($\"Unable to open the folder path ({folderPath}) due to the following exception: {ex}\", Helper.Status.Alert);\n            }\n        }\n\n        private async Task<bool> ValidateSMAPIPath()\n        {\n            if (Program.settings.SMAPIFolderPath is not null && File.Exists(Pathing.GetSmapiPath()))\n            {\n                return true;\n            }\n\n            await DisplayInvalidSMAPIWarning();\n\n            Program.helper.Log(\n                Program.settings.SMAPIFolderPath is null\n                    ? \"No path given for StardewModdingAPI.\"\n                    : $\"Bad path given for StardewModdingAPI: {Pathing.GetSmapiPath()}\", Helper.Status.Warning);\n\n            return false;\n        }\n\n        private async Task DisplayInvalidSMAPIWarning()\n        {\n            await CreateWarningWindow(Program.translation.Get(\"ui.warning.unable_to_locate_smapi\"), Program.translation.Get(\"internal.ok\"));\n\n            await DisplaySettingsWindow();\n        }\n\n        private void SetupDownloadCountListener()\n        {\n            var downloadPanel = this.FindControl<DownloadPanel>(\"DownloadPanel\");\n            if (downloadPanel is null)\n            {\n                return;\n            }\n\n            if (downloadPanel.DataContext is not DownloadPanelViewModel panelVM)\n            {\n                return;\n            }\n\n            // Change listener and intial value setter\n            // Both of these need to have a .StartWith(), because a) CombineLatest() will never emit anything until both\n            // sources have emitted *something*, and b) this also ensures that we do intial value setting before \n            // Downloads count or selected language otherwise changes.\n            Observable.CombineLatest(\n                first: panelVM.InProgressDownloads.StartWith(0),\n                second: Program.translation.WhenAnyPropertyChanged().StartWith(Program.translation),\n                resultSelector: (int count, Translation? translation) => (count, translation!)\n            ).Subscribe(((int downloadCount, Translation translation) x) =>\n            {\n                _viewModel.DownloadsButtonText = String.Format(x.translation.Get(\"ui.main_window.buttons.downloads.label\"), x.downloadCount);\n            });\n        }\n\n        private void InitializeComponent()\n        {\n            AvaloniaXamlLoader.Load(this);\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Views/MessageWindow.axaml",
    "content": "<Window xmlns=\"https://github.com/avaloniaui\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:vm=\"using:Stardrop.ViewModels\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:i=\"clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia\"\n        mc:Ignorable=\"d\" d:DesignWidth=\"300\" d:DesignHeight=\"150\"\n        x:Class=\"Stardrop.Views.MessageWindow\"\n        Title=\"Message\"\n        MinWidth=\"300\"\n        MinHeight=\"150\"\n        Width=\"300\"\n        Height=\"150\"\n        Background=\"{DynamicResource ThemeBackgroundBrush}\"\n        HasSystemDecorations=\"false\"\n        Icon=\"/Assets/icon.ico\">\n\n\t<Window.Styles>\n\t\t<Style Selector=\"Button:pointerover /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button:pressed /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource HighlightBrush}\"/>\n\t\t</Style>\n\t</Window.Styles>\n\n\t<Border BorderBrush=\"{DynamicResource HighlightBrush}\" BorderThickness=\"1\">\n\t\t<Grid>\n\t\t\t<Grid.RowDefinitions>\n\t\t\t\t<RowDefinition Height=\"Auto\" />\n\t\t\t\t<RowDefinition Height=\"Auto\" />\n\t\t\t\t<RowDefinition Height=\"25\" />\n\t\t\t</Grid.RowDefinitions>\n\n\t\t\t<Grid.ColumnDefinitions>\n\t\t\t\t<ColumnDefinition Width=\"25\" />\n\t\t\t\t<ColumnDefinition Width=\"*\" />\n\t\t\t\t<ColumnDefinition Width=\"25\" />\n\t\t\t</Grid.ColumnDefinitions>\n\n\t\t\t<TextBlock Grid.Row=\"0\" Grid.Column=\"1\" Text=\"{Binding MessageText}\" TextWrapping=\"Wrap\" TextAlignment=\"Center\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" HorizontalAlignment=\"Center\" Margin=\"0 35 0 20\"/>\n\t\t\t<Button Grid.Row=\"1\"  Grid.Column=\"1\" Name=\"positiveButton\" Content=\"{Binding PositiveButtonText}\" Click=\"PositiveButton_Click\" HorizontalAlignment=\"Left\" BorderBrush=\"{DynamicResource HighlightBrush}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Background=\"Transparent\" Margin=\"25 0 0 0\"/>\n\t\t\t<Button Grid.Row=\"1\"  Grid.Column=\"1\" Name=\"negativeButton\" IsCancel=\"true\" Content=\"{Binding NegativeButtonText}\" Click=\"NegativeButton_Click\" HorizontalAlignment=\"Right\" BorderBrush=\"{DynamicResource HighlightBrush}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Background=\"Transparent\"  Margin=\"0 0 25 0\"/>\n\t\t</Grid>\n\t</Border>\n</Window>\n"
  },
  {
    "path": "Stardrop/Views/MessageWindow.axaml.cs",
    "content": "using Avalonia;\nusing Avalonia.Controls;\nusing Avalonia.Markup.Xaml;\nusing Stardrop.ViewModels;\nusing System;\n\nnamespace Stardrop.Views\n{\n    public partial class MessageWindow : Window\n    {\n        private readonly MessageWindowViewModel _viewModel;\n\n        public MessageWindow()\n        {\n            InitializeComponent();\n\n            // Set the main window view\n            _viewModel = new MessageWindowViewModel();\n            DataContext = _viewModel;\n\n#if DEBUG\n            this.AttachDevTools();\n#endif\n        }\n\n        public MessageWindow(string messageText, string? positiveButtonText = null, string? negativeButtonText = null) : this()\n        {\n            Program.helper.Log($\"Created a message window with the following text: [{positiveButtonText} | {negativeButtonText}] {messageText}\");\n\n            _viewModel.MessageText = messageText;\n            _viewModel.PositiveButtonText = String.IsNullOrEmpty(positiveButtonText) ? Program.translation.Get(\"internal.yes\") : positiveButtonText;\n            _viewModel.NegativeButtonText = String.IsNullOrEmpty(negativeButtonText) ? Program.translation.Get(\"internal.no\") : negativeButtonText;\n\n            this.WindowStartupLocation = WindowStartupLocation.CenterOwner;\n            this.SizeToContent = SizeToContent.Height;\n        }\n\n        private void PositiveButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            this.Close(true);\n        }\n\n        private void NegativeButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            this.Close(false);\n        }\n\n        private void InitializeComponent()\n        {\n            AvaloniaXamlLoader.Load(this);\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Views/NexusInfo.axaml",
    "content": "<Window xmlns=\"https://github.com/avaloniaui\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:vm=\"using:Stardrop.ViewModels\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:i=\"clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia\"\n\t\txmlns:i18n=\"clr-namespace:Stardrop.Utilities.Extension\"\n        mc:Ignorable=\"d\" d:DesignWidth=\"300\" d:DesignHeight=\"150\"\n        x:Class=\"Stardrop.Views.NexusInfo\"\n        Title=\"{i18n:Translate ui.window.nexus_user.name}\"\n        MinWidth=\"350\"\n        MinHeight=\"235\"\n        Width=\"350\"\n        Height=\"235\"\n        Background=\"{DynamicResource ThemeBackgroundBrush}\"\n\t\tExtendClientAreaToDecorationsHint=\"true\"\n        ExtendClientAreaChromeHints=\"NoChrome\"\n        ExtendClientAreaTitleBarHeightHint=\"-1\"\n\t\tCanResize=\"False\"\n        Icon=\"/Assets/icon.ico\">\n\n\t<Window.Styles>\n\t\t<Style Selector=\"Button:pointerover /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"LightGray\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button:pressed /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"White\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button#exitButton:pointerover /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"Red\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button#exitButton:pressed /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"Pink\"/>\n\t\t</Style>\n\t\t<Style Selector=\"TextBox:focus\">\n\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource AlternativeDataGridRowBackground}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"TextBox:focus /template/ Border\">\n\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource HighlightBrush}\"/>\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource AlternativeDataGridRowBackground}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"TextBox:not(:focus)\">\n\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource DataGridRowBackground}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button#exitButton:pointerover /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"Red\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button#exitButton:pressed /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"Pink\"/>\n\t\t</Style>\n\t</Window.Styles>\n\n\t<Grid>\n\t\t<Grid.RowDefinitions>\n\t\t\t<RowDefinition Height=\"Auto\" />\n\t\t\t<RowDefinition Height=\"Auto\" />\n\t\t\t<RowDefinition Height=\"Auto\" />\n\t\t\t<RowDefinition Height=\"Auto\" />\n\t\t</Grid.RowDefinitions>\n\n\t\t<Grid.ColumnDefinitions>\n\t\t\t<ColumnDefinition Width=\"*\" />\n\t\t</Grid.ColumnDefinitions>\n\n\t\t<Border Grid.Row=\"0\" BorderBrush=\"{DynamicResource HighlightBrush}\" BorderThickness=\"0 0 0 2\" Grid.ColumnSpan=\"2\">\n\t\t\t<Menu Name=\"menuBar\" KeyboardNavigation.TabNavigation=\"None\">\n\t\t\t\t<Image Source=\"/Assets/icon.ico\" Stretch=\"None\"/>\n\t\t\t\t<TextBlock Text=\"{i18n:Translate ui.window.nexus_user.name}\" Margin=\"-10 0 0 0\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Left\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" />\n\t\t\t</Menu>\n\t\t</Border>\n\t\t<Menu Name=\"windowMenu\" IsVisible=\"{Binding ShowMainMenu}\" HorizontalAlignment=\"Right\" KeyboardNavigation.TabNavigation=\"None\" Grid.Column=\"1\">\n\t\t\t<Button Name=\"exitButton\" i:Attached.Icon=\"mdi-close\" Margin=\"0 0 -10 0\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Background=\"Transparent\"/>\n\t\t</Menu>\n\n\t\t<StackPanel Grid.Row=\"1\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\" Orientation=\"Vertical\" Margin=\"0 15 0 0\">\n\t\t\t<TextBlock Text=\"{i18n:Translate ui.nexus_login.labels.nexus_details}\" FontWeight=\"Bold\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Margin=\"0 0 0 0\"/>\n\t\t\t<TextBlock Name=\"nexusUserName\" Text=\"{i18n:Translate ui.nexus_login.labels.username}\" TextWrapping=\"Wrap\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Margin=\"0 25 0 0\"/>\n\t\t\t<TextBlock Name=\"nexusUserPremium\" Text=\"{i18n:Translate ui.nexus_login.labels.is_premium}\" TextWrapping=\"Wrap\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Margin=\"0 25 0 0\"/>\n\t\t</StackPanel>\n\n\t\t<StackPanel Grid.Row=\"2\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\" Orientation=\"Vertical\" Margin=\"0 35 0 0\">\n\t\t\t<Button Name=\"disconnectNexusButton\" Content=\"{i18n:Translate ui.nexus_login.buttons.disconnect}\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\"  Margin=\"0 0 0 0\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Background=\"Transparent\" BorderBrush=\"{DynamicResource HighlightBrush}\"/>\n\t\t</StackPanel>\n\t</Grid>\n</Window>\n"
  },
  {
    "path": "Stardrop/Views/NexusInfo.axaml.cs",
    "content": "using Avalonia;\nusing Avalonia.Controls;\nusing Avalonia.Markup.Xaml;\nusing Stardrop.Models.Data;\nusing Stardrop.Models.Nexus;\nusing Stardrop.Utilities;\nusing System;\nusing System.IO;\nusing System.Text.Json;\n\nnamespace Stardrop.Views\n{\n    public partial class NexusInfo : Window\n    {\n        public NexusInfo()\n        {\n            InitializeComponent();\n#if DEBUG\n            this.AttachDevTools();\n#endif\n        }\n\n\n        public NexusInfo(NexusUser nexusUser) : this()\n        {\n            // Handle buttons\n            this.FindControl<Button>(\"exitButton\").Click += delegate { this.Close(false); };\n            this.FindControl<Button>(\"disconnectNexusButton\").Click += DisconnectNexus_Click;\n\n            this.FindControl<TextBlock>(\"nexusUserName\").Text = String.Format(Program.translation.Get(\"ui.nexus_login.labels.username\"), nexusUser.Username);\n            this.FindControl<TextBlock>(\"nexusUserPremium\").Text = String.Format(Program.translation.Get(\"ui.nexus_login.labels.is_premium\"), nexusUser.IsPremium);\n        }\n\n        private void DisconnectNexus_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            Program.settings.NexusDetails = new NexusUser();\n            File.WriteAllText(Pathing.GetNotionCachePath(), JsonSerializer.Serialize(new PairedKeys(), new JsonSerializerOptions() { WriteIndented = true }));\n\n            this.Close(true);\n        }\n\n        private void InitializeComponent()\n        {\n            AvaloniaXamlLoader.Load(this);\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Views/NexusLogin.axaml",
    "content": "<Window xmlns=\"https://github.com/avaloniaui\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:vm=\"using:Stardrop.ViewModels\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:i=\"clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia\"\n\t\txmlns:i18n=\"clr-namespace:Stardrop.Utilities.Extension\"\n        mc:Ignorable=\"d\" d:DesignWidth=\"300\" d:DesignHeight=\"150\"\n        x:Class=\"Stardrop.Views.NexusLogin\"\n        Title=\"{i18n:Translate ui.window.nexus_login_actual.name}\"\n        MinWidth=\"500\"\n        MinHeight=\"400\"\n        Width=\"500\"\n        Height=\"400\"\n        Background=\"{DynamicResource ThemeBackgroundBrush}\"\n\t\tExtendClientAreaToDecorationsHint=\"true\"\n        ExtendClientAreaChromeHints=\"NoChrome\"\n        ExtendClientAreaTitleBarHeightHint=\"-1\"\n        Icon=\"/Assets/icon.ico\">\n\n\t<Window.Styles>\n\t\t<Style Selector=\"Button:pointerover /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"LightGray\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button:pressed /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"White\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button#exitButton:pointerover /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"Red\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button#exitButton:pressed /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"Pink\"/>\n\t\t</Style>\n\t\t<Style Selector=\"TextBox:focus\">\n\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource AlternativeDataGridRowBackground}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"TextBox:focus /template/ Border\">\n\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource HighlightBrush}\"/>\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource AlternativeDataGridRowBackground}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"TextBox:not(:focus)\">\n\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource DataGridRowBackground}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button#exitButton:pointerover /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"Red\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button#exitButton:pressed /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"Pink\"/>\n\t\t</Style>\n\t</Window.Styles>\n\n\t<Grid>\n\t\t<Grid.RowDefinitions>\n\t\t\t<RowDefinition Height=\"Auto\" />\n\t\t\t<RowDefinition Height=\"Auto\" />\n\t\t\t<RowDefinition Height=\"Auto\" />\n\t\t\t<RowDefinition Height=\"Auto\" />\n\t\t\t<RowDefinition Height=\"*\" />\n\t\t\t<RowDefinition Height=\"Auto\" />\n\t\t</Grid.RowDefinitions>\n\n\t\t<Grid.ColumnDefinitions>\n\t\t\t<ColumnDefinition Width=\"*\" />\n\t\t</Grid.ColumnDefinitions>\n\n\t\t<Border Grid.Row=\"0\" BorderBrush=\"{DynamicResource HighlightBrush}\" BorderThickness=\"0 0 0 2\" Grid.ColumnSpan=\"2\">\n\t\t\t<Menu Name=\"menuBar\" KeyboardNavigation.TabNavigation=\"None\">\n\t\t\t\t<Image Source=\"/Assets/icon.ico\" Stretch=\"None\"/>\n\t\t\t\t<TextBlock Text=\"{i18n:Translate ui.window.nexus_login_actual.name}\" Margin=\"-10 0 0 0\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Left\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" />\n\t\t\t</Menu>\n\t\t</Border>\n\t\t<Menu Name=\"windowMenu\" IsVisible=\"{Binding ShowMainMenu}\" HorizontalAlignment=\"Right\" KeyboardNavigation.TabNavigation=\"None\" Grid.Column=\"1\">\n\t\t\t<Button Name=\"exitButton\" i:Attached.Icon=\"mdi-close\" Margin=\"0 0 -10 0\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Background=\"Transparent\"/>\n\t\t</Menu>\n\n\t\t<StackPanel Grid.Row=\"1\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\" Orientation=\"Vertical\">\n\t\t\t<TextBlock Text=\"{i18n:Translate ui.nexus_login.labels.get_key_actual}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Margin=\"0 15 0 10\"/>\n\t\t\t<Button Name=\"goToNexusButton\" Content=\"{i18n:Translate ui.nexus_login.buttons.nexus}\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\"  Margin=\"0 0 0 0\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Background=\"Transparent\" BorderBrush=\"{DynamicResource HighlightBrush}\"/>\n\t\t</StackPanel>\n\n\t\t<StackPanel Grid.Row=\"2\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\" Orientation=\"Vertical\" Margin=\"0 15 0 0\">\n\t\t\t<Border Margin=\"0 10 0 0\" BorderBrush=\"{DynamicResource HighlightBrush}\" BorderThickness=\"2\">\n\t\t\t\t<TextBox Name=\"apiBox\" TextWrapping=\"Wrap\" Height=\"50\" Width=\"450\" Background=\"{DynamicResource DataGridRowBackground}\" Foreground=\"{DynamicResource ThemeForegroundHighBrush}\" />\n\t\t\t</Border>\n\t\t</StackPanel>\n\t\t\n\t\t<StackPanel Grid.Row=\"3\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\" Orientation=\"Vertical\" Margin=\"0 15 0 0\">\n\t\t\t<TextBlock Text=\"{i18n:Translate ui.nexus_login.labels.note}\" FontWeight=\"Bold\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Margin=\"0 0 0 0\"/>\n\t\t\t<TextBlock Text=\"{i18n:Translate ui.nexus_login.labels.share_warning_actual}\" Width=\"400\" TextWrapping=\"Wrap\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Margin=\"0 15 0 0\"/>\n\t\t\t<TextBlock Text=\"{i18n:Translate ui.nexus_login.labels.revoke_actual}\" Width=\"400\" TextWrapping=\"Wrap\" HorizontalAlignment=\"Center\" VerticalAlignment=\"Center\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Margin=\"0 25 0 0\"/>\n\t\t</StackPanel>\n\n\n\t\t<DockPanel Grid.Row=\"5\" Margin=\"0 0 0 10\">\n\t\t\t<Button Name=\"applyButton\" i:Attached.Icon=\"mdi-check\" Margin=\"25 0 0 0\" HorizontalAlignment=\"Left\" BorderBrush=\"{DynamicResource HighlightBrush}\" Foreground=\"Green\" Background=\"Transparent\"/>\n\t\t\t<Button Name=\"cancelButton\" IsCancel=\"True\" i:Attached.Icon=\"mdi-cancel\" Margin=\"0 0 25 0\" HorizontalAlignment=\"Right\" BorderBrush=\"{DynamicResource HighlightBrush}\" Foreground=\"Red\" Background=\"Transparent\"/>\n\t\t</DockPanel>\n\t</Grid>\n</Window>\n"
  },
  {
    "path": "Stardrop/Views/NexusLogin.axaml.cs",
    "content": "using Avalonia;\nusing Avalonia.Controls;\nusing Avalonia.Input;\nusing Avalonia.Markup.Xaml;\nusing DynamicData.Binding;\nusing Stardrop.Utilities;\nusing Stardrop.ViewModels;\nusing System;\nusing System.Net.WebSockets;\n\nnamespace Stardrop.Views\n{\n    public partial class NexusLogin : Window\n    {\n        private NexusWebsocket? _nexusWebsocket;\n        public NexusLogin()\n        {\n            InitializeComponent();\n            _nexusWebsocket = new NexusWebsocket();\n\n#if DEBUG\n            this.AttachDevTools();\n#endif\n        }\n\n        public NexusLogin(MainWindowViewModel viewModel) : this()\n        {\n            // Handle buttons\n            this.FindControl<Button>(\"cancelButton\").Click += delegate { this.Close(null); };\n            this.FindControl<Button>(\"exitButton\").Click += delegate { this.Close(null); };\n            this.FindControl<Button>(\"goToNexusButton\").Click += delegate {\n                viewModel.OpenBrowser(_nexusWebsocket.ssoUrl);\n                HandleNexusFlow();\n            };\n\n            var applyButton = this.FindControl<Button>(\"applyButton\");\n            applyButton.Click += ApplyButton_Click;\n            applyButton.IsEnabled = false;\n\n            var apiKeyBox = this.FindControl<TextBox>(\"apiBox\");\n            apiKeyBox.WhenValueChanged(textbox => textbox.Text).Subscribe(text =>\n            {\n                applyButton.IsEnabled = string.IsNullOrEmpty(text) is false;\n            });\n            apiKeyBox.KeyDown += KeyBox_KeyDown;\n        }\n\n        private async void HandleNexusFlow()\n        {\n            var result = await _nexusWebsocket.ConnectAsync();\n\n            if (result.Error is not null)\n            {\n                Program.helper.Log($\"Error getting API key: {result.Error}\", Helper.Status.Warning);\n            }\n            else\n            {\n                var apiKeyBox = this.FindControl<TextBox>(\"apiBox\");\n                apiKeyBox.Text = result.ApiKey ?? string.Empty;\n\n                var applyButton = this.FindControl<Button>(\"applyButton\");\n                applyButton.IsEnabled = true;\n            }\n        }\n\n        private void ApplyChanges()\n        {\n            var apiKeyBox = this.FindControl<TextBox>(\"apiBox\");\n\n            this.Close(apiKeyBox.Text);\n        }\n\n        private void KeyBox_KeyDown(object? sender, KeyEventArgs e)\n        {\n            var apiKeyBox = sender as TextBox;\n            if (e.Key == Key.Enter && string.IsNullOrEmpty(apiKeyBox.Text) is false)\n            {\n                ApplyChanges();\n            }\n        }\n\n        private void ApplyButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            ApplyChanges();\n        }\n\n        private void InitializeComponent()\n        {\n            AvaloniaXamlLoader.Load(this);\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Views/ProfileEditor.axaml",
    "content": "<Window xmlns=\"https://github.com/avaloniaui\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:vm=\"using:Stardrop.ViewModels\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:i=\"clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia\"\n\t\txmlns:i18n=\"clr-namespace:Stardrop.Utilities.Extension\"\n        mc:Ignorable=\"d\" d:DesignWidth=\"500\" d:DesignHeight=\"400\"\n        x:Class=\"Stardrop.Views.ProfileEditor\"\n        Title=\"{i18n:Translate ui.window.profiles.name}\"\n        MinWidth=\"400\"\n        MinHeight=\"300\"\n        Width=\"500\"\n        Height=\"400\"\n        Background=\"{DynamicResource ThemeBackgroundBrush}\"\n        HasSystemDecorations=\"true\"\n        ExtendClientAreaToDecorationsHint=\"true\"\n        ExtendClientAreaChromeHints=\"NoChrome\"\n        ExtendClientAreaTitleBarHeightHint=\"-1\"\n        CanResize=\"True\"\n        Icon=\"/Assets/icon.ico\">\n\n\t<Window.Styles>\n\t\t<Style Selector=\"Button:pointerover /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"LightGray\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button:pressed /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"White\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button[IsEnabled=False] /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"Gray\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button#exitButton:pointerover /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"Red\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button#exitButton:pressed /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"Pink\"/>\n\t\t</Style>\n\t\t<Style Selector=\"ListBoxItem:pointerover /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource AlternativeDataGridRowBackground}\"/>\n\t\t\t<Setter Property=\"TextBlock.Foreground\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"ListBoxItem:selected /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource DataGridHeaderBrush}\"/>\n\t\t\t<Setter Property=\"TextBlock.Foreground\" Value=\"{DynamicResource HighlightForegroundBrush}\"/>\n\t\t</Style>\n\t</Window.Styles>\n\n\t<Grid>\n\t\t<Grid.RowDefinitions>\n\t\t\t<RowDefinition Height=\"Auto\" />\n\t\t\t<RowDefinition Height=\"*\" />\n\t\t\t<RowDefinition Height=\"50\" />\n\t\t</Grid.RowDefinitions>\n\n\t\t<Grid.ColumnDefinitions>\n\t\t\t<ColumnDefinition Width=\"*\" />\n\t\t\t<ColumnDefinition Width=\"50\" />\n\t\t</Grid.ColumnDefinitions>\n\t\t<Border Grid.Row=\"0\" BorderBrush=\"{DynamicResource HighlightBrush}\" BorderThickness=\"0 0 0 2\" Grid.ColumnSpan=\"2\">\n\t\t\t<Menu Name=\"menuBar\" KeyboardNavigation.TabNavigation=\"None\">\n\t\t\t\t<Image Source=\"/Assets/icon.ico\" Stretch=\"None\"/>\n\t\t\t\t<TextBlock Text=\"Profiles\" Margin=\"-10 0 0 0\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Left\"  Foreground=\"{DynamicResource ThemeForegroundBrush}\" />\n\t\t\t</Menu>\n\t\t</Border>\n\t\t<Menu Name=\"windowMenu\" HorizontalAlignment=\"Right\" KeyboardNavigation.TabNavigation=\"None\" Grid.Column=\"1\">\n\t\t\t<Button Name=\"exitButton\" i:Attached.Icon=\"mdi-close\" Margin=\"0 0 -10 0\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Background=\"Transparent\"/>\n\t\t</Menu>\n\n\t\t<Border Grid.Row=\"1\" Margin=\"20\" BorderBrush=\"{DynamicResource HighlightBrush}\" BorderThickness=\"2\">\n\t\t\t<ListBox Name=\"profileList\" Background=\"{DynamicResource DataGridRowBackground}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\">\n\t\t\t\t<ListBox.ItemTemplate>\n\t\t\t\t\t<DataTemplate>\n\t\t\t\t\t\t<StackPanel>\n\t\t\t\t\t\t\t<TextBlock Text=\"{Binding Path=Name}\"/>\n\t\t\t\t\t\t</StackPanel>\n\t\t\t\t\t</DataTemplate>\n\t\t\t\t</ListBox.ItemTemplate>\n\t\t\t</ListBox>\n\t\t</Border>\n\n\t\t<StackPanel Grid.Row=\"1\" Grid.Column=\"1\" Margin=\"0 45 0 0\" Width=\"50\">\n\t\t\t<Button Name=\"addButton\" Margin=\"0 15 0 15\" i:Attached.Icon=\"mdi-plus\" BorderBrush=\"{DynamicResource HighlightBrush}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Background=\"Transparent\"/>\n\t\t\t<Button Name=\"deleteButton\" IsEnabled=\"False\" Margin=\"0 15 0 15\" i:Attached.Icon=\"mdi-minus\" BorderBrush=\"{DynamicResource HighlightBrush}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Background=\"Transparent\"/>\n\t\t\t<Button Name=\"renameButton\" IsEnabled=\"False\" Margin=\"0 15 0 15\" i:Attached.Icon=\"mdi-pencil\" BorderBrush=\"{DynamicResource HighlightBrush}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Background=\"Transparent\"/>\n\t\t\t<Button Name=\"copyButton\" Margin=\"0 15 0 15\" i:Attached.Icon=\"mdi-content-copy\" BorderBrush=\"{DynamicResource HighlightBrush}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Background=\"Transparent\"/>\n\t\t</StackPanel>\n\n\t\t<DockPanel Grid.Row=\"2\" Grid.Column=\"1\" Margin=\"0 0 0 10\">\n\t\t\t<Button Name=\"applyButton\" ToolTip.Tip=\"{Binding ToolTip_Save}\" i:Attached.Icon=\"mdi-check\" Margin=\"-40 0 15 0\" BorderBrush=\"{DynamicResource HighlightBrush}\" Foreground=\"Green\" Background=\"Transparent\"/>\n\t\t\t<Button Name=\"cancelButton\" IsCancel=\"True\" ToolTip.Tip=\"{Binding ToolTip_Cancel}\" i:Attached.Icon=\"mdi-cancel\" BorderBrush=\"{DynamicResource HighlightBrush}\" Foreground=\"Red\" Background=\"Transparent\"/>\n\t\t</DockPanel>\n\t</Grid>\n</Window>\n"
  },
  {
    "path": "Stardrop/Views/ProfileEditor.axaml.cs",
    "content": "using Avalonia;\nusing Avalonia.Controls;\nusing Avalonia.Markup.Xaml;\nusing Stardrop.Models;\nusing Stardrop.ViewModels;\nusing System;\nusing System.Linq;\n\nnamespace Stardrop.Views\n{\n    public partial class ProfileEditor : Window\n    {\n        private readonly ProfileEditorViewModel _viewModel;\n\n        public ProfileEditor()\n        {\n            InitializeComponent();\n\n#if DEBUG\n            this.AttachDevTools();\n#endif\n        }\n\n        public ProfileEditor(ProfileEditorViewModel viewModel) : this()\n        {\n            _viewModel = viewModel;\n\n            // Load the profiles\n            var profileListBox = this.FindControl<ListBox>(\"profileList\");\n            profileListBox.Items = _viewModel.Profiles;\n            profileListBox.SelectedIndex = 0;\n            profileListBox.SelectionChanged += ProfileListBox_SelectionChanged;\n\n            // Handle the mainMenu bar for drag and related events\n            var menuBar = this.FindControl<Menu>(\"menuBar\");\n            menuBar.PointerPressed += MainBar_PointerPressed;\n            menuBar.DoubleTapped += MainBar_DoubleTapped;\n\n            // Handle buttons\n            this.FindControl<Button>(\"exitButton\").Click += delegate { this.Close(); };\n            this.FindControl<Button>(\"cancelButton\").Click += delegate { this.Close(); };\n            this.FindControl<Button>(\"applyButton\").Click += ApplyButton_Click;\n            this.FindControl<Button>(\"addButton\").Click += AddButton_Click;\n            this.FindControl<Button>(\"deleteButton\").Click += DeleteButton_Click;\n            this.FindControl<Button>(\"renameButton\").Click += RenameButton_Click;\n            this.FindControl<Button>(\"copyButton\").Click += CopyButton_Click;\n        }\n\n        private void ProfileListBox_SelectionChanged(object? sender, SelectionChangedEventArgs e)\n        {\n            var profile = this.FindControl<ListBox>(\"profileList\").SelectedItem as Profile;\n            if (profile is not null)\n            {\n                this.FindControl<Button>(\"deleteButton\").IsEnabled = !profile.IsProtected;\n                this.FindControl<Button>(\"renameButton\").IsEnabled = !profile.IsProtected;\n            }\n        }\n\n        private void CopyButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            var selectedProfile = this.FindControl<ListBox>(\"profileList\").SelectedItem as Profile;\n\n            int copyIndex = 1;\n            var fileNameCopied = selectedProfile.Name + $\" - Copy ({copyIndex})\";\n            while (_viewModel.Profiles.Any(p => p.Name == fileNameCopied))\n            {\n                copyIndex += 1;\n                fileNameCopied = selectedProfile.Name + $\" - Copy ({copyIndex})\";\n            }\n\n            var copiedProfile = selectedProfile.ShallowCopy();\n            copiedProfile.Name = fileNameCopied;\n            copiedProfile.IsProtected = false;\n\n            _viewModel.Profiles.Add(copiedProfile);\n        }\n\n        private void RenameButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            var profileListBox = this.FindControl<ListBox>(\"profileList\");\n            var naming = new ProfileNaming(_viewModel, profileListBox.SelectedItem as Profile);\n            naming.WindowStartupLocation = WindowStartupLocation.CenterOwner;\n            naming.ShowDialog(this);\n        }\n\n        private void DeleteButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            var profile = this.FindControl<ListBox>(\"profileList\").SelectedItem as Profile;\n            _viewModel.Profiles.Remove(profile);\n        }\n\n        private async void AddButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            var namingWindow = new ProfileNaming(_viewModel);\n            namingWindow.WindowStartupLocation = WindowStartupLocation.CenterOwner;\n\n            var profile = await namingWindow.ShowDialog<Profile>(this);\n            if (profile is not null && _viewModel.OldProfiles.Any(p => p.Name == profile.Name))\n            {\n                await new WarningWindow(String.Format(Program.translation.Get(\"ui.warning.unable_to_add_profile\"), profile.Name), Program.translation.Get(\"internal.ok\")).ShowDialog(this);\n            }\n        }\n\n        private void ApplyButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            // Save any changes made\n            var oldProfileList = _viewModel.OldProfiles;\n            var currentProfileList = _viewModel.Profiles;\n\n            // Remove any deleted profiles\n            foreach (var profile in oldProfileList.Where(old => !currentProfileList.Any(p => p.Name == old.Name)))\n            {\n                Program.helper.Log($\"Deleting profile {profile.Name}\");\n                _viewModel.DeleteProfile(profile);\n            }\n\n            // Add any created profiles\n            foreach (var profile in currentProfileList.Where(current => !oldProfileList.Any(p => p.Name == current.Name)))\n            {\n                Program.helper.Log($\"Adding profile {profile.Name}\");\n                _viewModel.CreateProfile(profile);\n            }\n\n            _viewModel.OldProfiles = currentProfileList.ToList();\n            this.Close();\n        }\n\n        private void MainBar_DoubleTapped(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            if (!e.Handled)\n            {\n                this.WindowState = this.WindowState == WindowState.Normal ? WindowState.Maximized : WindowState.Normal;\n            }\n        }\n\n        private void MainBar_PointerPressed(object? sender, Avalonia.Input.PointerPressedEventArgs e)\n        {\n            if (e.Pointer.IsPrimary && !e.Handled)\n            {\n                this.BeginMoveDrag(e);\n            }\n        }\n\n        private void InitializeComponent()\n        {\n            AvaloniaXamlLoader.Load(this);\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Views/ProfileNaming.axaml",
    "content": "<Window xmlns=\"https://github.com/avaloniaui\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:vm=\"using:Stardrop.ViewModels\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:i=\"clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia\"\n\t\txmlns:i18n=\"clr-namespace:Stardrop.Utilities.Extension\"\n        mc:Ignorable=\"d\" d:DesignWidth=\"300\" d:DesignHeight=\"150\"\n        x:Class=\"Stardrop.Views.ProfileNaming\"\n        Title=\"{i18n:Translate ui.window.profile_naming.name}\"\n        MinWidth=\"300\"\n        MinHeight=\"150\"\n        Width=\"300\"\n        Height=\"150\"\n        Background=\"{DynamicResource ThemeBackgroundBrush}\"\n        Icon=\"/Assets/icon.ico\">\n\n\t<Window.Styles>\n\t\t<Style Selector=\"Button:pointerover /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"LightGray\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button:pressed /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"White\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button#exitButton:pointerover /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"Red\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button#exitButton:pressed /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"Pink\"/>\n\t\t</Style>\n\t\t<Style Selector=\"TextBox:focus\">\n\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource AlternativeDataGridRowBackground}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"TextBox:focus /template/ Border\">\n\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource HighlightBrush}\"/>\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource AlternativeDataGridRowBackground}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"TextBox:not(:focus)\">\n\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource DataGridRowBackground}\"/>\n\t\t</Style>\n\t</Window.Styles>\n\n\t<Grid>\n\t\t<Grid.RowDefinitions>\n\t\t\t<RowDefinition Height=\"Auto\" />\n\t\t\t<RowDefinition Height=\"*\" />\n\t\t</Grid.RowDefinitions>\n\n\t\t<Grid.ColumnDefinitions>\n\t\t\t<ColumnDefinition Width=\"*\" />\n\t\t</Grid.ColumnDefinitions>\n\n\t\t<Border Grid.Row=\"0\" Margin=\"35 50 35 0\" BorderBrush=\"{DynamicResource HighlightBrush}\" BorderThickness=\"2\">\n\t\t\t<TextBox Name=\"profileNameBox\" Background=\"{DynamicResource DataGridRowBackground}\" Foreground=\"{DynamicResource ThemeForegroundHighBrush}\" />\n\t\t</Border>\n\n\n\t\t<DockPanel Grid.Row=\"1\" Margin=\"0 0 0 10\">\n\t\t\t<Button Name=\"applyButton\" i:Attached.Icon=\"mdi-check\" Margin=\"25 0 0 0\" HorizontalAlignment=\"Left\" BorderBrush=\"{DynamicResource HighlightBrush}\" Foreground=\"Green\" Background=\"Transparent\"/>\n\t\t\t<Button Name=\"cancelButton\" IsCancel=\"True\" i:Attached.Icon=\"mdi-cancel\" Margin=\"0 0 25 0\" HorizontalAlignment=\"Right\" BorderBrush=\"{DynamicResource HighlightBrush}\" Foreground=\"Red\" Background=\"Transparent\"/>\n\t\t</DockPanel>\n\t</Grid>\n</Window>\n"
  },
  {
    "path": "Stardrop/Views/ProfileNaming.axaml.cs",
    "content": "using Avalonia;\nusing Avalonia.Controls;\nusing Avalonia.Input;\nusing Avalonia.Markup.Xaml;\nusing Stardrop.Models;\nusing Stardrop.ViewModels;\nusing System;\nusing System.Linq;\n\nnamespace Stardrop.Views\n{\n    public partial class ProfileNaming : Window\n    {\n        private readonly ProfileEditorViewModel _profileEditor;\n        private Profile? _renameTarget;\n\n        public ProfileNaming()\n        {\n            InitializeComponent();\n#if DEBUG\n            this.AttachDevTools();\n#endif\n        }\n\n        public ProfileNaming(ProfileEditorViewModel parentView, Profile? renameTarget = null) : this()\n        {\n            _profileEditor = parentView;\n            _renameTarget = renameTarget;\n\n            // Handle buttons\n            this.FindControl<Button>(\"cancelButton\").Click += delegate { this.Close(null); };\n            this.FindControl<Button>(\"applyButton\").Click += ApplyButton_Click;\n\n            // Give focus to textbox\n            var profileNameBox = this.FindControl<TextBox>(\"profileNameBox\");\n            profileNameBox.AttachedToVisualTree += (s, e) => profileNameBox.Focus();\n            profileNameBox.KeyDown += ProfileNameBox_KeyDown;\n        }\n\n        private void ApplyChanges()\n        {\n            // Save any changes made\n            var profileNameBox = this.FindControl<TextBox>(\"profileNameBox\");\n\n            Profile addedProfile = null;\n            if (!String.IsNullOrEmpty(profileNameBox.Text))\n            {\n                if (_renameTarget is not null)\n                {\n                    _profileEditor.Profiles.Remove(_renameTarget);\n                }\n\n                addedProfile = new Profile(profileNameBox.Text, false, _renameTarget is null ? null : _renameTarget.EnabledModIds);\n                if (!_profileEditor.Profiles.Any(p => p.Name == addedProfile.Name))\n                {\n                    _profileEditor.Profiles.Add(addedProfile);\n                }\n            }\n\n            this.Close(addedProfile);\n        }\n\n        private void ProfileNameBox_KeyDown(object? sender, KeyEventArgs e)\n        {\n            if (e.Key == Key.Enter)\n            {\n                ApplyChanges();\n            }\n        }\n\n        private void ApplyButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            ApplyChanges();\n        }\n\n        private void MainMenu_DoubleTapped(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            if (!e.Handled)\n            {\n                this.WindowState = this.WindowState == WindowState.Normal ? WindowState.Maximized : WindowState.Normal;\n            }\n        }\n\n        private void MainMenu_PointerPressed(object? sender, Avalonia.Input.PointerPressedEventArgs e)\n        {\n            if (e.Pointer.IsPrimary && !e.Handled)\n            {\n                this.BeginMoveDrag(e);\n            }\n        }\n\n        private void InitializeComponent()\n        {\n            AvaloniaXamlLoader.Load(this);\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Views/SettingsWindow.axaml",
    "content": "<Window xmlns=\"https://github.com/avaloniaui\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:vm=\"using:Stardrop.ViewModels\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:i=\"clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia\"\n\t\txmlns:i18n=\"clr-namespace:Stardrop.Utilities.Extension\"\n        mc:Ignorable=\"d\" d:DesignWidth=\"430\" d:DesignHeight=\"650\"\n        x:Class=\"Stardrop.Views.SettingsWindow\"\n        Title=\"{i18n:Translate ui.window.settings.name}\"\n        MinWidth=\"430\"\n        MinHeight=\"430\"\n        Width=\"430\"\n        Background=\"{DynamicResource ThemeBackgroundBrush}\"\n        HasSystemDecorations=\"true\"\n        ExtendClientAreaToDecorationsHint=\"true\"\n        ExtendClientAreaChromeHints=\"NoChrome\"\n        ExtendClientAreaTitleBarHeightHint=\"-1\"\n        CanResize=\"True\"\n        Icon=\"/Assets/icon.ico\">\n\n\t<Window.Styles>\n\t\t<Style Selector=\"Button:pointerover /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"LightGray\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button:pressed /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"White\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button[IsEnabled=False] /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"Gray\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button#exitButton:pointerover /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"Red\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button#exitButton:pressed /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"Pink\"/>\n\t\t</Style>\n\t\t<Style Selector=\"CheckBox:checked /template/ Border#NormalRectangle\">\n\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource ThemeBackgroundBrush}\" />\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource ThemeBackgroundBrush}\" />\n\t\t</Style>\n\t\t<Style Selector=\"CheckBox:checked /template/ Path#CheckGlyph\">\n\t\t\t<Setter Property=\"Fill\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"CheckBox:unchecked /template/ Border#NormalRectangle\">\n\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource HighlightForegroundBrush}\" />\n\t\t</Style>\n\t\t<Style Selector=\"CheckBox:checked /template/ Border#NormalRectangle\">\n\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource HighlightForegroundBrush}\" />\n\t\t</Style>\n\t\t<Style Selector=\"CheckBox:pointerover /template/ ContentPresenter#ContentPresenter\">\n\t\t\t<Setter Property=\"TextBlock.Foreground\" Value=\"{DynamicResource HighlightForegroundBrush}\" />\n\t\t</Style>\n\t\t<Style Selector=\"ComboBoxItem\">\n\t\t\t<Setter Property=\"FontWeight\" Value=\"Bold\"/>\n\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource ComboBoxItemBackgroundBrush}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"ComboBox\">\n\t\t\t<Setter Property=\"Background\" Value=\"Transparent\" />\n\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource ThemeBorderMidBrush}\" />\n\t\t\t<Setter Property=\"BorderThickness\" Value=\"{DynamicResource ThemeBorderThickness}\" />\n\t\t\t<Setter Property=\"HorizontalContentAlignment\" Value=\"Stretch\" />\n\t\t\t<Setter Property=\"VerticalContentAlignment\" Value=\"Center\" />\n\t\t\t<Setter Property=\"Padding\" Value=\"4\" />\n\t\t\t<Setter Property=\"MinHeight\" Value=\"20\" />\n\t\t\t<Setter Property=\"PlaceholderForeground\" Value=\"{DynamicResource ThemeForegroundBrush}\" />\n\t\t\t<Setter Property=\"ScrollViewer.HorizontalScrollBarVisibility\" Value=\"Disabled\" />\n\t\t\t<Setter Property=\"Template\">\n\t\t\t\t<ControlTemplate>\n\t\t\t\t\t<Border Name=\"border\"\n\t\t\t\t\t\t\tBackground=\"{TemplateBinding Background}\"\n\t\t\t\t\t\t\tBorderBrush=\"{TemplateBinding BorderBrush}\"\n\t\t\t\t\t\t\tBorderThickness=\"{TemplateBinding BorderThickness}\"\n\t\t\t\t\t\t\tCornerRadius=\"{TemplateBinding CornerRadius}\">\n\t\t\t\t\t\t<Grid ColumnDefinitions=\"*,Auto\">\n\t\t\t\t\t\t\t<TextBlock Name=\"PlaceholderTextBlock\"\n\t\t\t\t\t\t\t\t\t   Grid.Column=\"0\"\n\t\t\t\t\t\t\t\t\t   HorizontalAlignment=\"{TemplateBinding HorizontalContentAlignment}\"\n\t\t\t\t\t\t\t\t\t   VerticalAlignment=\"{TemplateBinding VerticalContentAlignment}\"\n\t\t\t\t\t\t\t\t\t   Margin=\"{TemplateBinding Padding}\"\n\t\t\t\t\t\t\t\t\t   Text=\"{TemplateBinding PlaceholderText}\"\n\t\t\t\t\t\t\t\t\t   Foreground=\"{TemplateBinding PlaceholderForeground}\"\n\t\t\t\t\t\t\t\t\t   IsVisible=\"{TemplateBinding SelectionBoxItem, Converter={x:Static ObjectConverters.IsNull}}\" />\n\t\t\t\t\t\t\t<ContentControl Content=\"{TemplateBinding SelectionBoxItem}\"\n\t\t\t\t\t\t\t\t\t\t\tContentTemplate=\"{TemplateBinding ItemTemplate}\"\n\t\t\t\t\t\t\t\t\t\t\tMargin=\"{TemplateBinding Padding}\"\n\t\t\t\t\t\t\t\t\t\t\tVerticalContentAlignment=\"{TemplateBinding VerticalContentAlignment}\"\n\t\t\t\t\t\t\t\t\t\t\tHorizontalContentAlignment=\"{TemplateBinding HorizontalContentAlignment}\" />\n\t\t\t\t\t\t\t<ToggleButton Name=\"toggle\"\n\t\t\t\t\t\t\t\t\t\t  BorderThickness=\"0\"\n\t\t\t\t\t\t\t\t\t\t  Background=\"Transparent\"\n\t\t\t\t\t\t\t\t\t\t  ClickMode=\"Press\"\n\t\t\t\t\t\t\t\t\t\t  Focusable=\"False\"\n\t\t\t\t\t\t\t\t\t\t  IsChecked=\"{TemplateBinding IsDropDownOpen, Mode=TwoWay}\"\n\t\t\t\t\t\t\t\t\t\t  Grid.Column=\"1\">\n\t\t\t\t\t\t\t\t<Path Fill=\"{DynamicResource ThemeForegroundBrush}\"\n\t\t\t\t\t\t\t\t\t  Width=\"8\"\n\t\t\t\t\t\t\t\t\t  Height=\"4\"\n\t\t\t\t\t\t\t\t\t  Stretch=\"Uniform\"\n\t\t\t\t\t\t\t\t\t  HorizontalAlignment=\"Center\"\n\t\t\t\t\t\t\t\t\t  VerticalAlignment=\"Center\"\n\t\t\t\t\t\t\t\t\t  Data=\"F1 M 301.14,-189.041L 311.57,-189.041L 306.355,-182.942L 301.14,-189.041 Z\" />\n\t\t\t\t\t\t\t</ToggleButton>\n\t\t\t\t\t\t\t<Popup Name=\"PART_Popup\"\n\t\t\t\t\t\t\t\t   IsOpen=\"{TemplateBinding IsDropDownOpen, Mode=TwoWay}\"\n\t\t\t\t\t\t\t\t   MinWidth=\"{Binding Bounds.Width, RelativeSource={RelativeSource TemplatedParent}}\"\n\t\t\t\t\t\t\t\t   MaxHeight=\"{TemplateBinding MaxDropDownHeight}\"\n\t\t\t\t\t\t\t\t   PlacementTarget=\"{TemplateBinding}\"\n\t\t\t\t\t\t\t\t   IsLightDismissEnabled=\"True\">\n\t\t\t\t\t\t\t\t<Border Background=\"{DynamicResource ThemeBackgroundBrush}\"\n\t\t\t\t\t\t\t\t\t\tBorderBrush=\"{DynamicResource ThemeBorderMidBrush}\"\n\t\t\t\t\t\t\t\t\t\tBorderThickness=\"1\">\n\t\t\t\t\t\t\t\t\t<ScrollViewer HorizontalScrollBarVisibility=\"{TemplateBinding ScrollViewer.HorizontalScrollBarVisibility}\"\n\t\t\t\t\t\t\t\t\t\t\t\t  VerticalScrollBarVisibility=\"{TemplateBinding ScrollViewer.VerticalScrollBarVisibility}\">\n\t\t\t\t\t\t\t\t\t\t<ItemsPresenter Name=\"PART_ItemsPresenter\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tItems=\"{TemplateBinding Items}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tItemsPanel=\"{TemplateBinding ItemsPanel}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tItemTemplate=\"{TemplateBinding ItemTemplate}\"\n\t\t\t\t\t\t\t\t\t\t\t\t\t\tVirtualizationMode=\"{TemplateBinding VirtualizationMode}\" />\n\t\t\t\t\t\t\t\t\t</ScrollViewer>\n\t\t\t\t\t\t\t\t</Border>\n\t\t\t\t\t\t\t</Popup>\n\t\t\t\t\t\t</Grid>\n\t\t\t\t\t</Border>\n\t\t\t\t</ControlTemplate>\n\t\t\t</Setter>\n\t\t</Style>\n\t\t<Style Selector=\"ComboBoxItem:pointerover /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource HighlightBrush}\" />\n\t\t</Style>\n\t\t<Style Selector=\"ComboBoxItem:selected /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource ComboBoxItemSelectedBackgroundBrush}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"ComboBox /template/ Path#DropDownGlyph\">\n\t\t\t<Setter Property=\"Fill\" Value=\"{DynamicResource HighlightForegroundBrush}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"TextBox:pointerover /template/ Border\">\n\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource ThemeBackgroundBrush}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"TextBox:focus\">\n\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"TextBox:focus /template/ Border\">\n\t\t\t<Setter Property=\"BorderBrush\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource ThemeBackgroundBrush}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"TextBox:not(:focus)\">\n\t\t\t<Setter Property=\"Foreground\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource ThemeBackgroundBrush}\"/>\n\t\t</Style>\n\t</Window.Styles>\n\n\n\t<Grid>\n\t\t<Grid.RowDefinitions>\n\t\t\t<RowDefinition Height=\"Auto\" />\n\t\t\t<RowDefinition Height=\"*\" />\n\t\t\t<RowDefinition Height=\"Auto\" />\n\t\t\t<RowDefinition Height=\"Auto\" />\n\t\t</Grid.RowDefinitions>\n\n\t\t<Grid.ColumnDefinitions>\n\t\t\t<ColumnDefinition Width=\"*\" />\n\t\t\t<ColumnDefinition Width=\"Auto\" />\n\t\t</Grid.ColumnDefinitions>\n        \n\t\t<Border Grid.Row=\"0\" BorderBrush=\"{DynamicResource HighlightBrush}\" BorderThickness=\"0 0 0 2\" Grid.ColumnSpan=\"2\">\n\t\t\t<Menu Name=\"menuBar\" KeyboardNavigation.TabNavigation=\"None\">\n\t\t\t\t<Image Source=\"/Assets/icon.ico\" Stretch=\"None\"/>\n\t\t\t\t<TextBlock Text=\"{i18n:Translate ui.window.settings.name}\" Margin=\"-10 0 0 0\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Left\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" />\n\t\t\t</Menu>\n\t\t</Border>\n\t\t<Menu Name=\"windowMenu\" Grid.Row=\"0\" IsVisible=\"{Binding ShowMainMenu}\" HorizontalAlignment=\"Right\" KeyboardNavigation.TabNavigation=\"None\" Grid.Column=\"1\">\n\t\t\t<Button Name=\"exitButton\" i:Attached.Icon=\"mdi-close\" Margin=\"0 0 -10 0\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Background=\"Transparent\"/>\n\t\t</Menu>\n\n\t\t<ScrollViewer AllowAutoHide=\"True\" Grid.Row=\"1\" Grid.ColumnSpan=\"2\">\n\t\t\t<StackPanel>\n\t\t\t\t<StackPanel Margin=\"10 10 0 0\">\n\t\t\t\t\t<TextBlock Text=\"{i18n:Translate ui.settings_window.labels.smapi_path}\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Left\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" />\n\t\t\t\t\t<TextBox Name=\"smapiFolderPathBox\" Text=\"{Binding SMAPIPath}\" ToolTip.Tip=\"{Binding ToolTip_SMAPI}\" Margin=\"10 10 0 0\" Height=\"10\" Width=\"350\" BorderBrush=\"{DynamicResource HighlightBrush}\" HorizontalAlignment=\"Left\"/>\n\t\t\t\t\t<Button Name=\"smapiFolderButton\" Margin=\"360 -32 0 0\" i:Attached.Icon=\"mdi-folder\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Background=\"Transparent\" HorizontalAlignment=\"Left\" />\n\n\t\t\t\t\t<TextBlock Text=\"{i18n:Translate ui.settings_window.labels.mod_path}\" Margin=\"0 10 0 0\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Left\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" />\n\t\t\t\t\t<TextBox Name=\"modFolderPathBox\" Text=\"{Binding ModFolderPath}\" ToolTip.Tip=\"{Binding ToolTip_ModFolder}\" Margin=\"10 10 0 0\" Height=\"10\" Width=\"350\" BorderBrush=\"{DynamicResource HighlightBrush}\" HorizontalAlignment=\"Left\"/>\n\t\t\t\t\t<Button Name=\"modFolderButton\" Margin=\"360 -32 0 0\" i:Attached.Icon=\"mdi-folder\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Background=\"Transparent\" HorizontalAlignment=\"Left\" />\n\n\t\t\t\t\t<TextBlock Text=\"{i18n:Translate ui.settings_window.labels.mod_install}\" Margin=\"0 10 0 0\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Left\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" />\n\t\t\t\t\t<TextBox Name=\"modInstallPathBox\" Text=\"{Binding ModInstallPath}\" ToolTip.Tip=\"{Binding ToolTip_ModInstall}\" Margin=\"10 10 0 0\" Height=\"10\" Width=\"350\" BorderBrush=\"{DynamicResource HighlightBrush}\" HorizontalAlignment=\"Left\"/>\n\t\t\t\t\t<Button Name=\"modInstallButton\" Margin=\"360 -32 0 0\" i:Attached.Icon=\"mdi-folder\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Background=\"Transparent\" HorizontalAlignment=\"Left\" />\n\t\t\t\t</StackPanel>\n\n\t\t\t\t<StackPanel Margin=\"10 10 0 0\">\n\t\t\t\t\t<TextBlock Text=\"{i18n:Translate ui.settings_window.labels.themes}\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Left\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" />\n\t\t\t\t\t<ComboBox Name=\"themeComboBox\" ToolTip.Tip=\"{Binding ToolTip_Theme}\" Margin=\"10 10 0 10\" Width=\"175\" Background=\"{DynamicResource ThemeBackgroundBrush}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" BorderBrush=\"{DynamicResource HighlightBrush}\" HorizontalAlignment=\"Left\">\n\t\t\t\t\t\t<ComboBox.ItemTemplate>\n\t\t\t\t\t\t\t<DataTemplate>\n\t\t\t\t\t\t\t\t<StackPanel Orientation=\"Horizontal\" IsEnabled=\"{Binding IsEnabled}\">\n\t\t\t\t\t\t\t\t\t<TextBlock Text=\"{Binding Name}\" FontWeight=\"Bold\"/>\n\t\t\t\t\t\t\t\t\t<TextBlock Text=\"{Binding Author}\" Margin=\"5 0 0 0\" Foreground=\"Gray\"/>\n\t\t\t\t\t\t\t\t</StackPanel>\n\t\t\t\t\t\t\t</DataTemplate>\n\t\t\t\t\t\t</ComboBox.ItemTemplate>\n\t\t\t\t\t</ComboBox>\n\t\t\t\t\t<TextBlock Text=\"{i18n:Translate ui.settings_window.labels.languages}\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Left\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" />\n\t\t\t\t\t<ComboBox Name=\"languageComboBox\" ToolTip.Tip=\"{Binding ToolTip_Language}\" Margin=\"10 10 0 10\" Width=\"175\" Background=\"{DynamicResource ThemeBackgroundBrush}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" BorderBrush=\"{DynamicResource HighlightBrush}\" HorizontalAlignment=\"Left\"/>\n\t\t\t\t\t<TextBlock Text=\"{i18n:Translate ui.settings_window.labels.grouping}\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Left\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" />\n\t\t\t\t\t<ComboBox Name=\"groupingComboBox\" ToolTip.Tip=\"{Binding ToolTip_Grouping}\" Margin=\"10 10 0 0\" Width=\"175\" Background=\"{DynamicResource ThemeBackgroundBrush}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" BorderBrush=\"{DynamicResource HighlightBrush}\" HorizontalAlignment=\"Left\"/>\n\t\t\t\t</StackPanel>\n\n\t\t\t\t<StackPanel Margin=\"10 25 0 0\">\n\t\t\t\t\t<TextBlock Text=\"{i18n:Translate ui.settings_window.labels.nexus}\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Left\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" />\n\t\t\t\t\t<StackPanel Orientation=\"Horizontal\" IsVisible=\"{Binding ShowNexusServers}\">\n\t\t\t\t\t\t<TextBlock Text=\"{i18n:Translate ui.settings_window.labels.preferred_server}\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Left\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" />\n\t\t\t\t\t\t<ComboBox Name=\"preferredServerBox\" ToolTip.Tip=\"{Binding ToolTip_PreferredServer}\" Margin=\"10 10 0 10\" Width=\"150\" Background=\"{DynamicResource ThemeBackgroundBrush}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" BorderBrush=\"{DynamicResource HighlightBrush}\" HorizontalAlignment=\"Left\"/>\n\t\t\t\t\t</StackPanel>\n\t\t\t\t\t<Button Name=\"registerNXMButton\" IsVisible=\"{Binding ShowNXMAssociationButton}\" ToolTip.Tip=\"{Binding ToolTip_NXMAssociation}\" Content=\"{i18n:Translate ui.settings_window.buttons.register_nxm_association}\" Margin=\"0 5 0 10\" BorderBrush=\"{DynamicResource HighlightBrush}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Background=\"Transparent\" HorizontalAlignment=\"Left\"/>\n\t\t\t\t\t<CheckBox Name=\"askBeforeNXMInstallCheckbox\" IsChecked=\"{Binding IsAskingBeforeAcceptingNXM}\" Content=\"{i18n:Translate ui.settings_window.buttons.always_ask_for_NXM_installs}\" ToolTip.Tip=\"{Binding ToolTip_AlwaysAskNXMFiles}\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Left\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" />\n\t\t\t\t</StackPanel>\n\n\t\t\t\t<StackPanel Margin=\"10 25 0 0\">\n\t\t\t\t\t<TextBlock Text=\"{i18n:Translate ui.settings_window.labels.miscellaneous}\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Left\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" />\n\t\t\t\t\t<CheckBox Name=\"ignoreHiddenFoldersCheckbox\" IsChecked=\"{Binding IgnoreHiddenFolders}\" Content=\"{i18n:Translate ui.settings_window.buttons.ignore_hidden_folders}\" ToolTip.Tip=\"{Binding ToolTip_IgnoreHiddenFolders}\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Left\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" />\n\t\t\t\t\t<CheckBox Name=\"enableProfileSpecificModConfigsCheckbox\" IsChecked=\"{Binding EnableProfileSpecificModConfigs}\" Content=\"{i18n:Translate ui.settings_window.buttons.enable_profile_specific_mod_configs}\" ToolTip.Tip=\"{Binding ToolTip_EnableProfileSpecificModConfigs}\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Left\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" />\n\t\t\t\t\t<CheckBox Name=\"enableModsOnAdd\" IsChecked=\"{Binding EnableModsOnAdd}\" Content=\"{i18n:Translate ui.settings_window.buttons.enable_mods_on_add}\" ToolTip.Tip=\"{Binding ToolTip_EnableModsOnAdd}\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Left\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" />\n\t\t\t\t\t<CheckBox Name=\"alwaysAskToDelete\" IsChecked=\"{Binding AlwaysAskToDelete}\" Content=\"{i18n:Translate ui.settings_window.buttons.always_ask_to_delete}\" ToolTip.Tip=\"{Binding ToolTip_AlwaysAskToDelete}\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Left\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" />\n\t\t\t\t\t<CheckBox Name=\"automaticallySaveProfileChanges\" IsChecked=\"{Binding ShouldAutomaticallySaveProfileChanges}\" Content=\"{i18n:Translate ui.settings_window.buttons.automatically_save_profile_changes}\" ToolTip.Tip=\"{Binding ToolTip_ShouldAutomaticallySaveProfileChanges}\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Left\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" />\n\t\t\t\t\t<CheckBox Name=\"showModThumbnails\" IsChecked=\"{Binding ShowModThumbnails}\" Content=\"{i18n:Translate ui.settings_window.buttons.show_mod_thumbnails}\" ToolTip.Tip=\"{Binding ToolTip_ShowModThumbnails}\" VerticalAlignment=\"Center\" HorizontalAlignment=\"Left\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" />\n\t\t\t\t</StackPanel>\n\t\t\t</StackPanel>\n\t\t</ScrollViewer>\n\n\t\t<Border Grid.Row=\"2\" Grid.ColumnSpan=\"2\" BorderBrush=\"{DynamicResource HighlightBrush}\" BorderThickness=\"0 0 0 2\" Height=\"4\"/>\n        <Grid Grid.Row=\"3\" Grid.ColumnSpan=\"2\" RowDefinitions=\"Auto, Auto\" ColumnDefinitions=\"Auto, Auto\" HorizontalAlignment=\"Right\" Margin=\"0 15 0 15\">\n            <Button Name=\"applyButton\" Grid.Row=\"1\" Grid.Column=\"0\" ToolTip.Tip=\"{Binding ToolTip_Save}\" i:Attached.Icon=\"mdi-check\" Margin=\"0 0 15 0\" BorderBrush=\"{DynamicResource HighlightBrush}\" Foreground=\"Green\" Background=\"Transparent\" HorizontalAlignment=\"Right\"/>\n\t\t\t<Button Name=\"cancelButton\" Grid.Row=\"1\" Grid.Column=\"1\" IsCancel=\"True\" ToolTip.Tip=\"{Binding ToolTip_Cancel}\" i:Attached.Icon=\"mdi-cancel\" Margin=\"0 0 15 0\" BorderBrush=\"{DynamicResource HighlightBrush}\" Foreground=\"Red\" Background=\"Transparent\" HorizontalAlignment=\"Right\"/>\n        </Grid>\n\t</Grid>\n</Window>\n"
  },
  {
    "path": "Stardrop/Views/SettingsWindow.axaml.cs",
    "content": "using Avalonia;\nusing Avalonia.Controls;\nusing Avalonia.Interactivity;\nusing Avalonia.Markup.Xaml;\nusing Avalonia.Styling;\nusing SharpCompress.Common;\nusing Stardrop.Models;\nusing Stardrop.Models.Data.Enums;\nusing Stardrop.Utilities;\nusing Stardrop.Utilities.Internal;\nusing Stardrop.ViewModels;\nusing System;\nusing System.Collections.Generic;\nusing System.IO;\nusing System.Linq;\nusing System.Runtime.InteropServices;\nusing System.Text.Json;\n\nnamespace Stardrop.Views\n{\n    public partial class SettingsWindow : Window\n    {\n        private Settings _oldSettings;\n        private SettingsWindowViewModel _viewModel;\n\n        public SettingsWindow()\n        {\n            InitializeComponent();\n\n            // Set the datacontext\n            _viewModel = new SettingsWindowViewModel();\n            DataContext = _viewModel;\n\n            // Handle buttons\n            this.FindControl<Button>(\"exitButton\").Click += Exit_Click;\n            this.FindControl<Button>(\"cancelButton\").Click += Exit_Click;\n            this.FindControl<Button>(\"smapiFolderButton\").Click += SmapiFolderButton_Click;\n            this.FindControl<Button>(\"modFolderButton\").Click += ModFolderButton_Click;\n            this.FindControl<Button>(\"modInstallButton\").Click += ModInstallButton_Click;\n            this.FindControl<Button>(\"registerNXMButton\").Click += RegisterNXMButton_Click;\n            this.FindControl<Button>(\"applyButton\").Click += ApplyButton_Click;\n\n            // Push the focus for the textboxes to the end of their strings\n            var smapiTextBox = this.FindControl<TextBox>(\"smapiFolderPathBox\");\n            var modFolderTextBox = this.FindControl<TextBox>(\"modFolderPathBox\");\n            var modInstallTextBox = this.FindControl<TextBox>(\"modInstallPathBox\");\n            SetTextboxTextFocusToEnd(smapiTextBox, smapiTextBox.Text);\n            SetTextboxTextFocusToEnd(modFolderTextBox, modFolderTextBox.Text);\n            SetTextboxTextFocusToEnd(modInstallTextBox, modInstallTextBox.Text);\n\n            // Handle adding the themes\n            string? lastContributorName = null;\n            foreach (string fileFullName in Directory.EnumerateFiles(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, \"Themes\"), \"*.xaml\", SearchOption.AllDirectories))\n            {\n                try\n                {\n                    var contributorName = new DirectoryInfo(Path.GetDirectoryName(fileFullName)).Name;\n                    if (contributorName is not null && contributorName.Equals(\"Themes\", StringComparison.OrdinalIgnoreCase))\n                    {\n                        contributorName = null;\n                    }\n\n                    if (lastContributorName != contributorName)\n                    {\n                        // Add separator\n                        _viewModel.Themes.Add(new Theme()\n                        {\n                            Name = \"------------\",\n                            IsEnabled = false\n                        });\n                    }\n                    lastContributorName = contributorName;\n\n                    var themeName = Path.GetFileNameWithoutExtension(fileFullName);\n                    var style = AvaloniaRuntimeXamlLoader.Parse<Styles>(File.ReadAllText(fileFullName));\n\n                    _viewModel.Themes.Add(new Theme()\n                    {\n                        Author = contributorName is not null ? $\"by {contributorName}\" : \"\",\n                        Name = themeName,\n                        Style = style,\n                        IsEnabled = true\n                    });\n\n                    Program.helper.Log($\"Loaded theme {Path.GetFileNameWithoutExtension(fileFullName)}\", Helper.Status.Debug);\n                }\n                catch (Exception ex)\n                {\n                    Program.helper.Log($\"Unable to load theme on {Path.GetFileNameWithoutExtension(fileFullName)}: {ex}\", Helper.Status.Warning);\n                }\n            }\n\n            var themeComboBox = this.FindControl<ComboBox>(\"themeComboBox\");\n            themeComboBox.Items = _viewModel.Themes;\n            var currentTheme = _viewModel.Themes.FirstOrDefault(t => t.Name.Equals(Program.settings.Theme, StringComparison.OrdinalIgnoreCase));\n            if (currentTheme is not null)\n            {\n                themeComboBox.SelectedItem = currentTheme;\n            }\n            themeComboBox.SelectionChanged += (sender, e) =>\n            {\n                Theme? theme = themeComboBox.SelectedItem as Theme;\n                if (theme is not null && theme.Style is not null)\n                {\n                    Application.Current.Styles[0] = theme.Style;\n                    Program.settings.Theme = theme.Name;\n                }\n            };\n\n            // Handle Nexus Mods preferred server\n            var descriptionToServerEnum = new Dictionary<string, NexusServers>();\n            foreach (NexusServers serverName in Enum.GetValues(typeof(NexusServers)))\n            {\n                if (EnumParser.GetDescription(serverName) is not null)\n                {\n                    descriptionToServerEnum[EnumParser.GetDescription(serverName)] = serverName;\n                }\n            }\n\n            var preferredComboBox = this.FindControl<ComboBox>(\"preferredServerBox\");\n            preferredComboBox.Items = descriptionToServerEnum.Keys;\n            preferredComboBox.SelectedItem = EnumParser.GetDescription(Program.settings.PreferredNexusServer);\n            preferredComboBox.SelectionChanged += (sender, e) =>\n            {\n                Program.settings.PreferredNexusServer = descriptionToServerEnum[preferredComboBox.SelectedItem.ToString()];\n            };\n\n            // Handle adding the languages\n            var languageComboBox = this.FindControl<ComboBox>(\"languageComboBox\");\n            languageComboBox.Items = Program.translation.GetAvailableTranslations();\n            languageComboBox.SelectedItem = String.IsNullOrEmpty(Program.settings.Language) ? Program.translation.GetAvailableTranslations().First() : Program.translation.GetLanguage(Program.settings.Language);\n            languageComboBox.SelectionChanged += (sender, e) =>\n            {\n                var language = languageComboBox.SelectedItem.ToString();\n                Program.translation.SetLanguage(language);\n                Program.settings.Language = language;\n            };\n\n            // Handle adding the mod grouping methods\n            var descriptionToModGroupingEnum = new Dictionary<string, ModGrouping>()\n            {\n                { \"None\", ModGrouping.None }\n            };\n\n            foreach (ModGrouping modGrouping in Enum.GetValues(typeof(ModGrouping)).Cast<ModGrouping>().OrderBy(g => EnumParser.GetDescription(g)))\n            {\n                if (modGrouping != ModGrouping.None && EnumParser.GetDescription(modGrouping) is not null)\n                {\n                    descriptionToModGroupingEnum[EnumParser.GetDescription(modGrouping)] = modGrouping;\n                }\n            }\n\n            var groupingComboBox = this.FindControl<ComboBox>(\"groupingComboBox\");\n            groupingComboBox.Items = descriptionToModGroupingEnum.Keys;\n            groupingComboBox.SelectedItem = EnumParser.GetDescription(Program.settings.ModGroupingMethod);\n            groupingComboBox.SelectionChanged += (sender, e) =>\n            {\n                Program.settings.ModGroupingMethod = descriptionToModGroupingEnum[groupingComboBox.SelectedItem.ToString()];\n            };\n\n            this.FontFamily = new Avalonia.Media.FontFamily(\"Segoe UI Symbol\");\n\n            // Cache the old settings\n            _oldSettings = Program.settings.ShallowCopy();\n\n#if DEBUG\n            this.AttachDevTools();\n#endif\n        }\n\n        public SettingsWindow(double parentWindowHeight) : this()\n        {\n            // Adjust the height of the this window to be slightly smaller than the parent\n            this.Height = parentWindowHeight - (parentWindowHeight / 4);\n        }\n\n        private async void RegisterNXMButton_Click(object? sender, RoutedEventArgs e)\n        {\n            if (NXMProtocol.Validate(Program.executablePath) is false)\n            {\n                var requestWindow = new MessageWindow(Program.translation.Get(\"ui.message.confirm_nxm_association\"));\n                if (await requestWindow.ShowDialog<bool>(this))\n                {\n                    if (NXMProtocol.Register(Program.executablePath) is false)\n                    {\n                        await new WarningWindow(Program.translation.Get(\"ui.warning.failed_to_set_association\"), Program.translation.Get(\"internal.ok\")).ShowDialog(this);\n                    }\n                }\n            }\n            else\n            {\n                await new WarningWindow(Program.translation.Get(\"ui.warning.already_associated\"), Program.translation.Get(\"internal.ok\")).ShowDialog(this);\n            }\n        }\n\n        private void Exit_Click(object? sender, RoutedEventArgs e)\n        {\n            var oldTheme = _viewModel.Themes.FirstOrDefault(t => t.Name.Equals(_oldSettings.Theme));\n            if (oldTheme is not null && oldTheme.Style is not null)\n            {\n                Application.Current.Styles[0] = oldTheme.Style;\n            }\n\n            Program.settings = _oldSettings;\n            Program.translation.SetLanguage(String.IsNullOrEmpty(Program.settings.Language) ? Program.translation.GetAvailableTranslations().First() : Program.translation.GetLanguage(Program.settings.Language));\n\n            this.Close(false);\n        }\n\n        private async void SmapiFolderButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            OpenFileDialog dialog = new OpenFileDialog();\n            if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))\n            {\n                dialog.Filters.Add(new FileDialogFilter() { Name = \"StardewModdingAPI.exe\", Extensions = { \"exe\" } });\n            }\n            else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))\n            {\n                dialog.Filters.Add(new FileDialogFilter() { Name = \"StardewModdingAPI.dll\" });\n            }\n            else\n            {\n                dialog.Filters.Add(new FileDialogFilter() { Name = \"StardewModdingAPI.dll\", Extensions = { \"*\" } });\n            }\n            dialog.AllowMultiple = false;\n\n            var filePaths = await dialog.ShowAsync(this);\n            if (filePaths is not null && filePaths.Count() > 0)\n            {\n                this.SetSMAPIPath(filePaths.First());\n            }\n        }\n\n        private async void ModFolderButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            OpenFolderDialog dialog = new OpenFolderDialog()\n            {\n                Title = \"Select the mod folder\"\n            };\n\n            if (!String.IsNullOrEmpty(Program.settings.ModFolderPath))\n            {\n                dialog.Directory = Program.settings.ModFolderPath;\n            }\n\n            var folderPath = await dialog.ShowAsync(this);\n            if (!String.IsNullOrEmpty(folderPath))\n            {\n                var modFolderPathBox = this.FindControl<TextBox>(\"modFolderPathBox\");\n                SetTextboxTextFocusToEnd(modFolderPathBox, folderPath);\n\n                var modInstallPathBox = this.FindControl<TextBox>(\"modInstallPathBox\");\n                if (String.IsNullOrEmpty(modInstallPathBox.Text) || !Directory.Exists(modInstallPathBox.Text) || !modInstallPathBox.Text.Contains(modFolderPathBox.Text, StringComparison.OrdinalIgnoreCase))\n                {\n                    modInstallPathBox.Text = Path.Combine(modFolderPathBox.Text, \"Stardrop Installed Mods\");\n                    SetTextboxTextFocusToEnd(modInstallPathBox, _oldSettings.ModInstallPath);\n                    return;\n                }\n            }\n        }\n\n        private async void ModInstallButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            OpenFolderDialog dialog = new OpenFolderDialog()\n            {\n                Title = \"Select the output folder for mods installed via Stardrop\"\n            };\n\n            if (!String.IsNullOrEmpty(Program.settings.ModInstallPath))\n            {\n                dialog.Directory = Program.settings.ModInstallPath;\n            }\n\n            var folderPath = await dialog.ShowAsync(this);\n            if (!String.IsNullOrEmpty(folderPath))\n            {\n                SetTextboxTextFocusToEnd(this.FindControl<TextBox>(\"modInstallPathBox\"), folderPath);\n            }\n        }\n\n        private void ApplyButton_Click(object? sender, RoutedEventArgs e)\n        {\n            var smapiFolderPathBox = this.FindControl<TextBox>(\"smapiFolderPathBox\");\n            var smapiPath = String.IsNullOrEmpty(smapiFolderPathBox.Text) || smapiFolderPathBox.Text.Contains(GetTargetSmapiName(), StringComparison.OrdinalIgnoreCase) ? smapiFolderPathBox.Text : Path.Combine(smapiFolderPathBox.Text, GetTargetSmapiName());\n            if (!SetSMAPIPath(smapiPath))\n            {\n                SetTextboxTextFocusToEnd(smapiFolderPathBox, _oldSettings.SMAPIFolderPath);\n                return;\n            }\n\n            var modFolderPathBox = this.FindControl<TextBox>(\"modFolderPathBox\");\n            if (String.IsNullOrEmpty(modFolderPathBox.Text) || !Directory.Exists(modFolderPathBox.Text))\n            {\n                new WarningWindow(Program.translation.Get(\"ui.warning.given_mod_folder_does_not_exist\"), Program.translation.Get(\"internal.ok\")).ShowDialog(this);\n                SetTextboxTextFocusToEnd(modFolderPathBox, _oldSettings.ModFolderPath);\n                return;\n            }\n\n            var modInstallPathBox = this.FindControl<TextBox>(\"modInstallPathBox\");\n            if (String.IsNullOrEmpty(modInstallPathBox.Text) || !Directory.Exists(modInstallPathBox.Text))\n            {\n                if (Directory.Exists(_oldSettings.ModInstallPath) is false)\n                {\n                    _oldSettings.ModInstallPath = Path.Combine(modFolderPathBox.Text, \"Stardrop Installed Mods\");\n                    Directory.CreateDirectory(_oldSettings.ModInstallPath);\n\n                    new WarningWindow(String.Format(Program.translation.Get(\"ui.warning.given_install_folder_not_exist_default\"), modFolderPathBox.Text), Program.translation.Get(\"internal.ok\")).ShowDialog(this);\n                    SetTextboxTextFocusToEnd(modInstallPathBox, _oldSettings.ModInstallPath);\n                    return;\n                }\n                else\n                {\n                    new WarningWindow(String.Format(Program.translation.Get(\"ui.warning.given_install_folder_not_exist\"), modFolderPathBox.Text), Program.translation.Get(\"internal.ok\")).ShowDialog(this);\n                    SetTextboxTextFocusToEnd(modInstallPathBox, _oldSettings.ModInstallPath);\n                    return;\n                }\n            }\n            else if (!modInstallPathBox.Text.Contains(modFolderPathBox.Text, StringComparison.OrdinalIgnoreCase))\n            {\n                new WarningWindow(String.Format(Program.translation.Get(\"ui.warning.given_install_folder_not_under_mod_folder\"), modFolderPathBox.Text), Program.translation.Get(\"internal.ok\")).ShowDialog(this);\n                SetTextboxTextFocusToEnd(modInstallPathBox, _oldSettings.ModInstallPath);\n                return;\n            }\n\n            // Write the settings cache\n            File.WriteAllText(Pathing.GetSettingsPath(), JsonSerializer.Serialize(Program.settings, new JsonSerializerOptions() { WriteIndented = true }));\n\n            this.Close(true);\n        }\n\n        private bool SetSMAPIPath(string filePath)\n        {\n            if (String.IsNullOrEmpty(filePath))\n            {\n                new WarningWindow(String.Format(Program.translation.Get(\"ui.warning.given_invalid_smapi_executable\"), GetTargetSmapiName()), Program.translation.Get(\"internal.ok\")).ShowDialog(this);\n                return false;\n            }\n\n            var smapiFileInfo = new FileInfo(filePath);\n            if (!smapiFileInfo.Exists || !smapiFileInfo.Name.Equals(GetTargetSmapiName(), StringComparison.OrdinalIgnoreCase))\n            {\n                new WarningWindow(String.Format(Program.translation.Get(\"ui.warning.given_invalid_smapi_executable\"), GetTargetSmapiName()), Program.translation.Get(\"internal.ok\")).ShowDialog(this);\n                return false;\n            }\n\n            var modFolderPathBox = this.FindControl<TextBox>(\"modFolderPathBox\");\n            var modInstallPathBox = this.FindControl<TextBox>(\"modInstallPathBox\");\n\n            SetTextboxTextFocusToEnd(this.FindControl<TextBox>(\"smapiFolderPathBox\"), smapiFileInfo.DirectoryName);\n            if (String.IsNullOrEmpty(Program.settings.ModFolderPath) || !Directory.Exists(modFolderPathBox.Text))\n            {\n                SetTextboxTextFocusToEnd(this.FindControl<TextBox>(\"modFolderPathBox\"), Path.Combine(smapiFileInfo.DirectoryName, \"Mods\"));\n            }\n\n            if (String.IsNullOrEmpty(Program.settings.ModInstallPath) || !Directory.Exists(modInstallPathBox.Text))\n            {\n                SetTextboxTextFocusToEnd(this.FindControl<TextBox>(\"modInstallPathBox\"), Path.Combine(smapiFileInfo.DirectoryName, \"Mods\", \"Stardrop Installed Mods\"));\n            }\n\n            return true;\n        }\n\n        private string GetTargetSmapiName()\n        {\n            var targetSmapiName = \"StardewModdingAPI.exe\";\n            if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))\n            {\n                targetSmapiName = \"StardewModdingAPI.dll\";\n            }\n\n            return targetSmapiName;\n        }\n\n        private void SetTextboxTextFocusToEnd(TextBox textBox, string text)\n        {\n            if (String.IsNullOrEmpty(text))\n            {\n                return;\n            }\n\n            textBox.Text = text;\n            textBox.CaretIndex = text.Length - 1;\n        }\n\n        private void InitializeComponent()\n        {\n            AvaloniaXamlLoader.Load(this);\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/Views/WarningWindow.axaml",
    "content": "<Window xmlns=\"https://github.com/avaloniaui\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xmlns:vm=\"using:Stardrop.ViewModels\"\n        xmlns:d=\"http://schemas.microsoft.com/expression/blend/2008\"\n        xmlns:mc=\"http://schemas.openxmlformats.org/markup-compatibility/2006\"\n        xmlns:i=\"clr-namespace:Projektanker.Icons.Avalonia;assembly=Projektanker.Icons.Avalonia\"\n        mc:Ignorable=\"d\" d:DesignWidth=\"300\" d:DesignHeight=\"150\"\n        x:Class=\"Stardrop.Views.WarningWindow\"\n        Title=\"Warning\"\n        MinWidth=\"300\"\n        MinHeight=\"150\"\n        Width=\"300\"\n        Height=\"150\"\n        Background=\"{DynamicResource ThemeBackgroundBrush}\"\n        HasSystemDecorations=\"false\"\n        Icon=\"/Assets/icon.ico\">\n\n\t<Window.Styles>\n\t\t<Style Selector=\"Button:pointerover /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource ThemeForegroundBrush}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"Button:pressed /template/ ContentPresenter\">\n\t\t\t<Setter Property=\"Background\" Value=\"{DynamicResource HighlightBrush}\"/>\n\t\t</Style>\n\t\t<Style Selector=\"ProgressBar /template/ TextBlock\">\n\t\t\t<Setter Property=\"FontWeight\" Value=\"ExtraBold\"/>\n\t\t</Style>\n\t</Window.Styles>\n\t\n\t<Window.Resources>\n\t\t<StaticResource x:Key=\"SystemControlForegroundBaseHighBrush\" ResourceKey=\"ThemeForegroundHighBrush\"/>\n\t</Window.Resources>\n\t\n\t<Border BorderBrush=\"{DynamicResource HighlightBrush}\" BorderThickness=\"1\">\n\t\t<Grid>\n\t\t\t<Grid.RowDefinitions>\n\t\t\t\t<RowDefinition Height=\"Auto\" />\n\t\t\t\t<RowDefinition Height=\"Auto\" />\n\t\t\t\t<RowDefinition Height=\"25\" />\n\t\t\t</Grid.RowDefinitions>\n\n\t\t\t<Grid.ColumnDefinitions>\n\t\t\t\t<ColumnDefinition Width=\"10\" />\n\t\t\t\t<ColumnDefinition Width=\"*\" />\n\t\t\t\t<ColumnDefinition Width=\"10\" />\n\t\t\t</Grid.ColumnDefinitions>\n\n\t\t\t<StackPanel Grid.Column=\"1\">\n\t\t\t\t<TextBlock Grid.Row=\"0\" Text=\"{Binding WarningText}\" TextWrapping=\"Wrap\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" HorizontalAlignment=\"Center\" TextAlignment=\"Center\"  Margin=\"0 35 0 20\"/>\n\t\t\t\t<ProgressBar Grid.Row=\"1\" IsVisible=\"{Binding IsProgressBarVisible}\" Margin=\"0 10\" Height=\"20\"\n\t\t\t\t\t\t\t  Minimum=\"0\" Value=\"{Binding ProgressBarValue}\"\n\t\t\t\t\t\t\t  Foreground=\"{DynamicResource HighlightBrush}\"\n\t\t\t\t\t\t\t  ShowProgressText=\"True\"/>\n\t\t\t\t<Button Grid.Row=\"2\" IsVisible=\"{Binding IsButtonVisible}\" Name=\"unlockButton\" IsCancel=\"True\" Content=\"{Binding ButtonText}\" Click=\"UnlockButton_Click\" HorizontalAlignment=\"Center\" BorderBrush=\"{DynamicResource HighlightBrush}\" Foreground=\"{DynamicResource ThemeForegroundBrush}\" Background=\"Transparent\"/>\n\t\t\t</StackPanel>\n\t\t</Grid>\n\t</Border>\n</Window>\n"
  },
  {
    "path": "Stardrop/Views/WarningWindow.axaml.cs",
    "content": "using Avalonia;\nusing Avalonia.Controls;\nusing Avalonia.Markup.Xaml;\nusing Stardrop.Utilities.External;\nusing Stardrop.ViewModels;\nusing System;\nusing System.Threading.Tasks;\n\nnamespace Stardrop.Views\n{\n    public partial class WarningWindow : Window\n    {\n        private readonly MainWindowViewModel _mainWindowModel;\n        private readonly WarningWindowViewModel _viewModel;\n        private bool _closeOnExitSMAPI;\n        private bool _closeOnParentUnlock;\n\n        public WarningWindow()\n        {\n            InitializeComponent();\n\n            // Set the datacontext\n            _viewModel = new WarningWindowViewModel();\n            DataContext = _viewModel;\n\n            this.WindowStartupLocation = WindowStartupLocation.CenterOwner;\n            this.SizeToContent = SizeToContent.Height;\n\n#if DEBUG\n            this.AttachDevTools();\n#endif\n        }\n\n        public WarningWindow(string warningText, string buttonText) : this()\n        {\n            Program.helper.Log($\"Created a warning window with the following text: [{buttonText}] {warningText}\");\n\n            _viewModel.WarningText = warningText;\n            _viewModel.ButtonText = buttonText;\n            _viewModel.IsButtonVisible = true;\n        }\n\n        public WarningWindow(string warningText, string buttonText, bool closeOnExitSMAPI) : this(warningText, buttonText)\n        {\n            _closeOnExitSMAPI = closeOnExitSMAPI;\n        }\n\n        public WarningWindow(string warningText, MainWindowViewModel model, bool closeOnParentUnlock = true) : this(warningText, String.Empty)\n        {\n            _mainWindowModel = model;\n            _closeOnParentUnlock = closeOnParentUnlock;\n            _viewModel.IsButtonVisible = false;\n            _viewModel.IsProgressBarVisible = false;\n        }\n\n        public void UpdateProgress(string? text = null, int? progress = null, int? maxProgress = null)\n        {\n            if (text is not null)\n            {\n                _viewModel.WarningText = text;\n            }\n\n            if (maxProgress is null || maxProgress == 0)\n            {\n                maxProgress = progress is null ? 1 : progress.Value;\n            }\n\n            _viewModel.IsProgressBarVisible = progress is not null && progress.Value >= 0;\n            _viewModel.ProgressBarValue = progress is null ? 0 : (progress.Value / (double)maxProgress.Value) * 100;\n        }\n\n        public override void Show()\n        {\n            base.Show();\n\n            if (_closeOnExitSMAPI)\n            {\n                WaitForProcessToClose();\n            }\n\n            if (_closeOnParentUnlock)\n            {\n                WaitForParentToUnlock();\n            }\n        }\n\n        private async Task WaitForProcessToClose()\n        {\n            while (SMAPI.IsRunning)\n            {\n                await Task.Delay(500);\n            }\n            this.Close();\n        }\n\n        private async Task WaitForParentToUnlock()\n        {\n            while (_mainWindowModel.IsLocked)\n            {\n                await Task.Delay(500);\n            }\n            this.Close();\n        }\n\n        private void UnlockButton_Click(object? sender, Avalonia.Interactivity.RoutedEventArgs e)\n        {\n            this.Close();\n        }\n\n        private void InitializeComponent()\n        {\n            AvaloniaXamlLoader.Load(this);\n        }\n    }\n}\n"
  },
  {
    "path": "Stardrop/i18n/de.json",
    "content": "{\n  // Main Window - Menu Items\n  \"ui.main_window.menu_items.add_mod\": \"Mod hinzufügen\",\n  \"ui.main_window.menu_items.start_smapi\": \"SMAPI starten\",\n  \"ui.main_window.menu_items.exit\": \"Beenden\",\n  \"ui.main_window.menu_items.settings\": \"Einstellungen\",\n  \"ui.main_window.menu_items.stardrop_log_file\": \"Stardrop Logdatei\",\n  \"ui.main_window.menu_items.smapi_log_file\": \"SMAPI Logdatei\",\n  \"ui.main_window.menu_items.refresh_mod_list\": \"Mod Liste Aktualisieren\",\n  \"ui.main_window.menu_items.check_for_mod_updates\": \"Nach Mod Updates suchen\",\n  \"ui.main_window.menu_items.enable_all_mods\": \"Alle Mods einschalten\",\n  \"ui.main_window.menu_items.disable_all_mods\": \"Alle Mods ausschalten\",\n  \"ui.main_window.menu_items.check_for_stardrop_update\": \"Nach Stardrop Update suchen\",\n  \"ui.main_window.menu_items.stardrop_repository\": \"Stardrop Quelle - GitHub\",\n\n  // Main Window - Menu Items - Context\n  \"ui.main_window.menu_items.context.show_requirements\": \"Voraussetzungen anzeigen\",\n  \"ui.main_window.menu_items.context.hide_requirements\": \"Voraussetzungen ausblenden\",\n  \"ui.main_window.menu_items.context.cancel\": \"Abbrechen\",\n  \"ui.main_window.menu_items.context.open_containing_folder\": \"Modordner öffnen\",\n  \"ui.main_window.menu_items.context.open_mod_page\": \"Modseite öffnen\",\n  \"ui.main_window.menu_items.context.delete\": \"Löschen\",\n\n  // Main Window - Menu Headers\n  \"ui.main_window.menu_headers.file\": \"Datei\",\n  \"ui.main_window.menu_headers.view\": \"Ansicht\",\n  \"ui.main_window.menu_headers.tools\": \"Extras\",\n\n  \"ui.main_window.menu_headers.enabled\": \"Aktiviert\",\n  \"ui.main_window.menu_headers.mod_name\": \"Mod Name\",\n  \"ui.main_window.menu_headers.unique_id\": \"Einmalige ID\",\n  \"ui.main_window.menu_headers.author\": \"Author\",\n  \"ui.main_window.menu_headers.requirements\": \"Voraussetzungen\",\n  \"ui.main_window.menu_headers.missing_requirements\": \"Fehlende Voraussetzungen\",\n  \"ui.main_window.menu_headers.version\": \"Version\",\n  \"ui.main_window.menu_headers.configuration\": \"Konfiguration\",\n  \"ui.main_window.menu_headers.status\": \"Status\",\n\n  // Main Window - Labels\n  \"ui.main_window.labels.profile\": \"Profil\",\n  \"ui.main_window.labels.enabled_mods\": \"Aktivierte Mods: \",\n  \"ui.main_window.labels.total_mods\": \"Gesamte Mods: \",\n  \"ui.main_window.labels.filter\": \"Filter\",\n\n  // Main Window - Hyperlinks\n  \"ui.main_window.hyperlinks.click_to_open\": \"Konfig öffnen\",\n  \"ui.main_window.hyperlinks.update_available\": \"Update verfügbar ({0})\",\n  \"ui.main_window.hyperlinks.unofficial_update_available\": \"Inoffizielles Update verfügbar ({0})\",\n  \"ui.main_window.hyperlinks.broken_compatibility_issue\": \"[Fehlerhaft] Kompatibilitätsproblem\",\n\n  // Main Window - Buttons\n  \"ui.main_window.buttons.save_configs\": \"Konfig speichern\",\n  \"ui.main_window.buttons.hide_disabled_mods\": \"Aktivierte Mods anzeigen\",\n  \"ui.main_window.buttons.show_updatable_mods\": \"Aktualisierbare Mods anzeigen\",\n\n  // Main Window - Buttons - Update Status\n  \"ui.main_window.button.update_status.generic\": \"Mods bereit zum Update: Zum Updaten klicken\",\n  \"ui.main_window.button.update_status.updating\": \"Suche Update...\",\n  \"ui.main_window.button.update_status.list_available_updates\": \"Verfügbare Updates: {0}\",\n  \"ui.main_window.button.update_status.failed\": \"Mod Update Check fehlgeschlagen\",\n\n  // Main Window - ComboBox\n  \"ui.main_window.combobox.show_all_mods\": \"Alle Mods anzeigen\",\n  \"ui.main_window.combobox.hide_enabled_mods\": \"Deaktivierte Mods anzeigen\",\n  \"ui.main_window.combobox.mod_name\": \"Mod Name\",\n  \"ui.main_window.combobox.author\": \"Author\",\n  \"ui.main_window.combobox.requirements\": \"Voraussetzungen\",\n\n  // Settings Window - Labels\n  \"ui.settings_window.labels.smapi_path\": \"SMAPI Pfad\",\n  \"ui.settings_window.labels.mod_path\": \"Mod Ordnerpfad\",\n  \"ui.settings_window.labels.mod_install\": \"Ordnerpfad für neu installierte Mods\",\n  \"ui.settings_window.labels.themes\": \"Themes\",\n  \"ui.settings_window.labels.languages\": \"Sprache\",\n  \"ui.settings_window.labels.miscellaneous\": \"Sonstiges\",\n\n  // Settings Window - Buttons\n  \"ui.settings_window.buttons.ignore_hidden_folders\": \"Versteckte Ordner ignorieren\",\n  \"ui.settings_window.buttons.enable_profile_specific_mod_configs\": \"Profilbezogene Mod Konfig aktivieren\",\n\n  // Settings Window - Tooltips\n  \"ui.settings_window.tooltips.smapi\": \"Der Dateipfad von StardewModdingAPI\",\n  \"ui.settings_window.tooltips.mod_folder_path\": \"Der Ordnerpfad in dem sich die Mods befinden\",\n  \"ui.settings_window.tooltips.mod_install_path\": \"Der Ordnerpfad, in dem Stardrop neu installierte Mods platziert. Dieser muss sich in dem Ordner, der in \\\"Mod Ordnerpfad\\\" angegeben ist befinden.\",\n  \"ui.settings_window.tooltips.theme\": \"Das aktuelle Thema von Stardrop\",\n  \"ui.settings_window.tooltips.language\": \"Die aktuelle Sprache von Stardrop\",\n  \"ui.settings_window.tooltips.ignore_hidden_folders\": \"Wenn diese Option aktiviert ist, ignoriert Stardrop alle Mods, deren übergeordneter Ordner mit \\\".\\\" beginnt.\",\n  \"ui.settings_window.tooltips.enable_profile_specific_configs\": \"Wenn diese Option aktiviert ist, speichert Stardrop die config.json von Mods und stellt sie wieder her, wenn Profile gewechselt werden.\",\n  \"ui.settings_window.tooltips.save_changes\": \"Änderungen Speichern\",\n  \"ui.settings_window.tooltips.cancel_changes\": \"Abbrechen\",\n\n  // Warnings\n  \"ui.warning.unable_to_add_profile\": \"{0} kann nicht hinzugefügt werden, es existiert bereits ein Profil unter diesem Namen!\",\n  \"ui.warning.given_mod_folder_does_not_exist\": \"Der angegebene Ordnerpfad existiert nicht.\\n\\nZum vorherigen Pfad zurückkehren.\",\n  \"ui.warning.given_install_folder_not_exist\": \"Der angegebene Ordnerpfad in den Stardrop Mods installieren soll existiert nicht.\\n\\nZum vorherigen Pfad zurückkehren.\",\n  \"ui.warning.given_install_folder_not_under_mod_folder\": \"Der angegebene Ordnerpfad in den Stardrop Mods installieren soll existiert nicht unter {0}\\n\\nZum vorherigen Pfad zurückkehren.\",\n  \"ui.warning.given_invalid_smapi_executable\": \"Die angegebene Datei ist nicht {0}\\n\\nZum vorherigen Pfad zurückkehren.\",\n  \"ui.warning.stardrop_locked\": \"Stardrop ist gesperrt, solange die SMAPI läuft. Alle vorgenommenen Änderungen werden erst nach dem Schließen von SMAPI übernommen.\",\n\n  \"ui.warning.unable_to_locate_smapi\": \"StardewModdingAPI.exe kann nicht gefunden werden.\\n\\nBitte geben Sie den richtigen Dateipfad unter\\nAnsicht > Einstellungen an.\",\n  \"ui.warning.mod_config_saved_but_not_enabled\": \"Die Einstellungen der Mod Konfig wurden in Profil {0} gespeichert, aber die profilbezogene Mod Konfig sind nicht aktiviert.\\n\\n Geh zu Ansicht > Einstellungen und aktiviere \\\"Profilbezogene Mod Konfig aktivieren\\\" für Stardrop, um Änderungen an der Mod-Konfig automatisch zu speichern und anzuwenden.\",\n  \"ui.warning.unable_to_determine_profile\": \"Ausgewähltes Profil kann nicht ermittelt werden.\\n\\nSMAPI wird nicht gestartet.\",\n  \"ui.warning.stardrop_up_to_date\": \"Stardrop ist auf dem neuesten Stand.\\n\\n{0} ist die letzte Version.\",\n  \"ui.warning.update_cooldown_minutes\": \"Updates können nur einmal alle 5 Minuten angefordert werden.\\n\\nBitte versuche es in {0} Minute(n) erneut.\",\n  \"ui.warning.update_cooldown_seconds\": \"Updates können nur einmal alle 5 Minuten angefordert werden.\\n\\nBitte versuche es in {0} Sekunde(n) erneut.\",\n  \"ui.warning.unable_to_locate_log\": \"Es kann keine SMAPI-latest.txt gefunden werden! SMAPI muss mindestens einmal erfolgreich ausgeführt werden, damit Stardrop die Spieldetails erkennen kann.\",\n  \"ui.warning.unable_to_read_log\": \"Die SMAPI-Logdatei konnte nicht gelesen werden, um die Spielversion zu ermitteln.\\n\\nMods werden nicht auf Updates geprüft.\",\n  \"ui.warning.no_manifest\": \"Keine manifest.json in \\\"{0}\\\" gefunden.\",\n  \"ui.warning.unable_to_load_mod\": \"Die Datei unter \\\"{0}\\\" konnte nicht geladen werden.\\n\\n Weitere Informationen findest du in der Logdatei.\",\n\n  // Messages\n  \"ui.message.confirm_mod_deletion\": \"Bist du sicher, dass du {0} löschen möchtest? Dies kann nicht rückgängig gemacht werden.\",\n  \"ui.message.unsaved_config_changes\": \"Nicht gespeicherte Änderungen der Mod Konfig für das Profil {0}.\\n\\nWollen Sie die Änderungen speichern?\",\n  \"ui.message.stardrop_update_available\": \"Für Stardrop ist ein Update (v{0}) verfügbar. Möchtest du es jetzt herunterladen?\",\n  \"ui.message.confirm_bulk_change_mod_states_enable\": \"Alle Mods aktivieren?\\n\\nHinweis: Deine aktuelle Auswahl wird nicht gespeichert.\",\n  \"ui.message.confirm_bulk_change_mod_states_disable\": \"Alle Mods deaktivieren?\\n\\nHinweis: Deine aktuelle Auswahl wird nicht gespeichert.\",\n  \"ui.message.confirm_mod_update_method\": \"Es wurde eine frühere Version von {0} erkannt. Möchtest du die vorherige Installation löschen?\\n\\nHinweis: Das Löschen früherer Versionen wird normalerweise empfohlen, allerdings gehen dabei alle Konfig Dateien verloren.\",\n\n  // Window Names\n  \"ui.window.settings.name\": \"Einstellungen\",\n  \"ui.window.profiles.name\": \"Profile\",\n  \"ui.window.profile_naming.name\": \"Profil Name\",\n\n  // Internals\n  \"internal.unknown\": \"Unbekannt\",\n  \"internal.enable\": \"Aktivieren\",\n  \"internal.disable\": \"Deaktivieren\",\n  \"internal.yes\": \"Ja\",\n  \"internal.no\": \"Nein\",\n  \"internal.ok\": \"OK\",\n  \"internal.unlock\": \"Entsperren\"\n}\n"
  },
  {
    "path": "Stardrop/i18n/default.json",
    "content": "{\n  // Main Window - Menu Items\n  \"ui.main_window.menu_items.add_mod\": \"Add Mod\",\n  \"ui.main_window.menu_items.add_mods\": \"Add Mods\",\n  \"ui.main_window.menu_items.start_smapi\": \"Start SMAPI\",\n  \"ui.main_window.menu_items.exit\": \"Exit\",\n  \"ui.main_window.menu_items.settings\": \"Settings\",\n  \"ui.main_window.menu_items.stardrop_log_file\": \"Stardrop Log File\",\n  \"ui.main_window.menu_items.smapi_log_file\": \"SMAPI Log File\",\n  \"ui.main_window.menu_items.refresh_mod_list\": \"Refresh Mod List\",\n  \"ui.main_window.menu_items.check_for_mod_updates\": \"Check for Mod Updates\",\n  \"ui.main_window.menu_items.enable_all_mods\": \"Enable All Mods\",\n  \"ui.main_window.menu_items.disable_all_mods\": \"Disable All Mods\",\n  \"ui.main_window.menu_items.check_for_stardrop_update\": \"Check for Stardrop Update\",\n  \"ui.main_window.menu_items.stardrop_repository\": \"Stardrop Repository\",\n  \"ui.main_window.menu_items.nexus_install_all\": \"Install All Updates\",\n  \"ui.main_window.menu_items.nexus_install_enabled\": \"Install Updates for Enabled Mods\",\n  \"ui.main_window.menu_items.nexus_connection\": \"API Connection\",\n  \"ui.main_window.menu_items.check_for_SMAPI_update\": \"Check for SMAPI Update\",\n\n  // Main Window - Menu Items - Context\n  \"ui.main_window.menu_items.context.columns.show_hide\": \"Show / Hide Columns\",\n  \"ui.main_window.menu_items.context.hide_requirements\": \"Hide Requirements\",\n  \"ui.main_window.menu_items.context.cancel\": \"Cancel\",\n  \"ui.main_window.menu_items.context.open_containing_folder\": \"Open Containing Folder\",\n  \"ui.main_window.menu_items.context.open_mod_page\": \"Open Mod Page\",\n  \"ui.main_window.menu_items.context.open_config\": \"Open Config File\",\n  \"ui.main_window.menu_items.context.open_manifest\": \"Open Manifest File\",\n  \"ui.main_window.menu_items.context.show_whole_mod_group\": \"Show Whole Mod Group\",\n  \"ui.main_window.menu_items.context.show_authors_mods\": \"Show Author's Mods\",\n  \"ui.main_window.menu_items.context.open\": \"Open...\",\n  \"ui.main_window.menu_items.context.filters\": \"Filters\",\n  \"ui.main_window.menu_items.context.options\": \"Options\",\n  \"ui.main_window.menu_items.context.clear_filter\": \"Clear\",\n  \"ui.main_window.menu_items.context.delete\": \"Delete\",\n\n  // Main Window - Menu Headers\n  \"ui.main_window.menu_headers.file\": \"File\",\n  \"ui.main_window.menu_headers.view\": \"View\",\n  \"ui.main_window.menu_headers.tools\": \"Tools\",\n  \"ui.main_window.menu_headers.nexus\": \"Nexus Mods\",\n\n  \"ui.main_window.menu_headers.enabled\": \"Enabled\",\n  \"ui.main_window.menu_headers.mod_name\": \"Mod Name\",\n  \"ui.main_window.menu_headers.unique_id\": \"Unique ID\",\n  \"ui.main_window.menu_headers.author\": \"Author\",\n  \"ui.main_window.menu_headers.requirements\": \"Requirements\",\n  \"ui.main_window.menu_headers.missing_requirements\": \"Missing Requirements\",\n  \"ui.main_window.menu_headers.version\": \"Version\",\n  \"ui.main_window.menu_headers.configuration\": \"Configuration\",\n  \"ui.main_window.menu_headers.status\": \"Status\",\n  \"ui.main_window.menu_headers.endorsement\": \"Endorsement\",\n  \"ui.main_window.menu_headers.nexus_install\": \"Nexus Mods Updates\",\n  \"ui.main_window.menu_headers.install_timestamp\": \"Install Timestamp\",\n  \"ui.main_window.menu_headers.last_updated\": \"Last Updated\",\n\n  // Main Window - Labels\n  \"ui.main_window.labels.profile\": \"Profile\",\n  \"ui.main_window.labels.enabled_mods\": \"Enabled Mods: \",\n  \"ui.main_window.labels.total_mods\": \"Total Mods: \",\n  \"ui.main_window.labels.updatable_mods\": \"Mods Ready to Update: \",\n  \"ui.main_window.labels.filter\": \"Filter\",\n  \"ui.main_window.labels.unknown_SMAPI\": \"Unknown SMAPI version\",\n\n  // Main Window - Hyperlinks\n  \"ui.main_window.hyperlinks.click_to_open\": \"Click to open\",\n  \"ui.main_window.hyperlinks.update_available\": \"Update Available ({0})\",\n  \"ui.main_window.hyperlinks.unofficial_update_available\": \"Unofficial Update Available ({0})\",\n  \"ui.main_window.hyperlinks.broken_compatibility_issue\": \"[Broken] Compatibility Issue\",\n  \"ui.main_window.hyperlinks.install_update\": \"Install Update\",\n  \"ui.main_window.hyperlinks.downloading\": \"Downloading...\",\n  \"ui.main_window.hyperlinks.installing\": \"Installing...\",\n\n  // Main Window - Buttons\n  \"ui.main_window.buttons.save_configs\": \"Save Configs\",\n  \"ui.main_window.buttons.save_profile\": \"Save Profile\",\n  \"ui.main_window.buttons.show_updatable_mods\": \"Show Updatable Mods\",\n  \"ui.main_window.buttons.active_search_filters\": \"{0} Filter(s) Active\",\n  \"ui.main_window.buttons.no_search_filters\": \"No Filters Active\",\n  \"ui.main_window.buttons.mod_groups_state.expand\": \"Expand Mod Groups\",\n  \"ui.main_window.buttons.mod_groups_state.collapse\": \"Collapse Mod Groups\",\n\n  // Main Window - Buttons - Update Status\n  \"ui.main_window.button.update_status.generic\": \"Click To Check For Mod Updates\",\n  \"ui.main_window.button.update_status.updating\": \"Updating...\",\n  \"ui.main_window.button.update_status.list_available_updates\": \"Mods Ready to Update: {0}\",\n  \"ui.main_window.button.update_status.failed\": \"Mod Update Check Failed\",\n\n  // Main Window - Buttons - Downloads\n  \"ui.main_window.buttons.downloads.label\": \"Downloads ({0})\",\n\n  // Main Window - ComboBox\n  \"ui.main_window.combobox.show_all_mods\": \"Show All Mods\",\n  \"ui.main_window.combobox.show_mods_with_configs\": \"Show Configurable Mods\",\n  \"ui.main_window.combobox.show_disabled_mods\": \"Show Disabled Mods\",\n  \"ui.main_window.combobox.show_enabled_mods\": \"Show Enabled Mods\",\n\n  // Main Window - ComboBox (ListBox)\n  \"ui.main_window.combobox.mod_name\": \"Mod Name\",\n  \"ui.main_window.combobox.author\": \"Author\",\n  \"ui.main_window.combobox.requirements\": \"Requirements\",\n  \"ui.main_window.combobox.group\": \"Mod Group\",\n  \"ui.main_window.combobox.top_level_group\": \"Root Folder\",\n\n  // Settings Window - Labels\n  \"ui.settings_window.labels.smapi_path\": \"SMAPI Path\",\n  \"ui.settings_window.labels.mod_path\": \"Mod Folder Path\",\n  \"ui.settings_window.labels.mod_install\": \"Stardrop Installed Mods Path\",\n  \"ui.settings_window.labels.themes\": \"Themes\",\n  \"ui.settings_window.labels.languages\": \"Languages\",\n  \"ui.settings_window.labels.grouping\": \"Mod Grouping\",\n  \"ui.settings_window.labels.miscellaneous\": \"Miscellaneous\",\n  \"ui.settings_window.labels.nexus\": \"Nexus Mods\",\n  \"ui.settings_window.labels.preferred_server\": \"Preferred Server:\",\n\n  // Settings Window - Buttons\n  \"ui.settings_window.buttons.ignore_hidden_folders\": \"Ignore Hidden Folders\",\n  \"ui.settings_window.buttons.enable_profile_specific_mod_configs\": \"Enable Profile Specific Mod Configs\",\n  \"ui.settings_window.buttons.enable_mods_on_add\": \"Automatically Enable Mods On Install\",\n  \"ui.settings_window.buttons.always_ask_to_delete\": \"Always Ask To Delete Mod Files On Update\",\n  \"ui.settings_window.buttons.automatically_save_profile_changes\": \"Automatically Save Profile Changes\",\n  \"ui.settings_window.buttons.show_mod_thumbnails\": \"Show Mod Thumbnails\",\n  \"ui.settings_window.buttons.always_ask_for_NXM_installs\": \"Always Ask Before Installing NXM Files\",\n  \"ui.settings_window.buttons.register_nxm_association\": \"Register NXM Association\",\n\n  // Settings Window - Tooltips\n  \"ui.settings_window.tooltips.smapi\": \"The file path of StardewModdingAPI\",\n  \"ui.settings_window.tooltips.mod_folder_path\": \"The folder path of the mod folder\",\n  \"ui.settings_window.tooltips.mod_install_path\": \"The folder path which Stardrop will place newly installed mods. Must be under the folder given in \\\"Mod Folder Path\\\"\",\n  \"ui.settings_window.tooltips.theme\": \"The current theme of Stardrop\",\n  \"ui.settings_window.tooltips.language\": \"The current language of Stardrop\",\n  \"ui.settings_window.tooltips.grouping\": \"The current method for grouping mods.\",\n  \"ui.settings_window.tooltips.ignore_hidden_folders\": \"If checked, Stardrop will ignore any mods which have a parent folder that start with \\\".\\\"\",\n  \"ui.settings_window.tooltips.nxm_file_association\": \"Registers NXM protocol to be associated with Stardrop, allowing for direct installs from www.nexusmods.com\",\n  \"ui.settings_window.tooltips.always_ask_nxm_files\": \"If checked, Stardrop will always ask before installing a mod from www.nexusmods.com via NXM\",\n  \"ui.settings_window.tooltips.enable_profile_specific_configs\": \"If checked, Stardrop will save and restore config.json files from mods when swapping profiles\",\n  \"ui.settings_window.tooltips.enable_mods_on_add\": \"If checked, Stardrop will automatically enable newly added or updated mods\",\n  \"ui.settings_window.tooltips.always_ask_to_delete\": \"If checked, Stardrop will always ask whether to delete mod files when updating the mod\",\n  \"ui.settings_window.tooltips.automatically_save_profile_changes\": \"If checked, Stardrop will automatically save changes made to the profile\",\n  \"ui.settings_window.tooltips.show_mod_thumbnails\": \"If checked, displays the mod's thumbnail from Nexus Mods (where applicable).\\n\\nRequires a Stardrop API key from Nexus Mods.'\",\n  \"ui.settings_window.tooltips.preferred_server\": \"Sets your preferred server to use when downloading from Nexus Mods\",\n  \"ui.settings_window.tooltips.save_changes\": \"Save Changes\",\n  \"ui.settings_window.tooltips.cancel_changes\": \"Cancel\",\n\n  // Nexus Login Window - Buttons\n  \"ui.nexus_login.buttons.nexus\": \"Go to Nexus Mods\",\n  \"ui.nexus_login.buttons.disconnect\": \"Disconnect From Nexus\",\n\n  // Nexus Login Window - Labels\n  \"ui.nexus_login.labels.note\": \"Please Note\",\n  \"ui.nexus_login.labels.get_key\": \"Get your Personal API key from Nexus Mods\",\n  \"ui.nexus_login.labels.get_key_actual\": \"Get your Stardrop API key from Nexus Mods\",\n  \"ui.nexus_login.labels.paste\": \"Then paste the key below\",\n  \"ui.nexus_login.labels.share_warning\": \"Don't share your personal API key with others, doing so may result in actions against your Nexus account.\",\n  \"ui.nexus_login.labels.share_warning_actual\": \"Don't share your API key with others, doing so may result in actions against your Nexus account.\",\n  \"ui.nexus_login.labels.revoke\": \"You may reset / revoke your key at any time by going to My Nexus Account > API > Personal API Key.\",\n  \"ui.nexus_login.labels.revoke_actual\": \"You may reset / revoke your Stardrop API key at any time by going to My Nexus Account > API > Stardrop.\",\n  \"ui.nexus_login.labels.nexus_details\": \"Nexus Mods User Details\",\n  \"ui.nexus_login.labels.nexus_disconnect\": \"Click the button below to have Stardrop forget your personal key and disconnect from Nexus Mods.\",\n  \"ui.nexus_login.labels.nexus_disconnect_actual\": \"Click the button below to have Stardrop forget your Stardrop API key and disconnect from Nexus Mods.\",\n  \"ui.nexus_login.labels.username\": \"Username: {0}\",\n  \"ui.nexus_login.labels.is_premium\": \"Is Premium Member: {0}\",\n\n  // Downloads Panel - Labels\n  \"ui.downloads_panel.download_canceled\": \"Canceled\",\n  \"ui.downloads_panel.download_failed\": \"Failed\",\n  \"ui_downloads_panel.download_success\": \"Complete!\",\n  \"ui.downloads_panel.no_downloads_label\": \"No downloads in progress\",\n\n  // Downloads Panel - Tooltips\n  \"ui.downloads_panel.tooltips.cancel_button\": \"Cancel\",\n  \"ui.downloads_panel.tooltips.remove_button\": \"Remove\", // Tooltip for the button that clears out a completed or failed download\n\n  // Warnings\n  \"ui.warning.unable_to_add_profile\": \"Unable to add {0}, a profile already exists under that name!\",\n  \"ui.warning.given_mod_folder_does_not_exist\": \"The given mod folder path doesn't exist.\\n\\nReverting to previous path.\",\n  \"ui.warning.given_install_folder_not_exist\": \"The given Stardrop installed mods folder path doesn't exist.\\n\\nReverting to previous path.\",\n  \"ui.warning.given_install_folder_not_exist_default\": \"The given Stardrop installed mods folder path doesn't exist.\\n\\nReverting to default path.\",\n  \"ui.warning.given_install_folder_not_under_mod_folder\": \"The given Stardrop installed mods folder path doesn't exist under {0}\\n\\nReverting to previous path.\",\n  \"ui.warning.given_invalid_smapi_executable\": \"The given file isn't {0}\\n\\nReverting to previous path.\",\n  \"ui.warning.stardrop_locked\": \"Stardrop is locked while the SMAPI is running. Any changes made will not reflect until SMAPI is closed.\",\n  \"ui.warning.stardrop_downloading\": \"Downloading the latest version of Stardrop.\",\n  \"ui.warning.SMAPI_downloading\": \"Downloading the latest version of SMAPI from GitHub.\",\n  \"ui.warning.SMAPI_installing\": \"Updating SMAPI to the latest version.\",\n  \"ui.warning.SMAPI_up_to_date\": \"SMAPI is up-to-date.\\n\\n{0} is the latest version.\",\n  \"ui.warning.install_mod_attempt_count\": \"Attempting to install {0} mod(s)\",\n  \"ui.warning.mod_installing\": \"Installing {0}\",\n  \"ui.warning.mod_updating\": \"Updating {0}\",\n  \"ui.warning.mod_deleting\": \"Deleting old instance of {0}\",\n  \"ui.warning.failed_to_delete\": \"Failed to delete {0}.\\n\\nMod files may be locked, see log for details.\",\n  \"ui.warning.failed_to_delete_during_update\": \"Failed to delete the old instance of {0}.\\n\\nThe mod may install incorrectly.\",\n  \"ui.warning.unable_to_locate_smapi\": \"Unable to locate StardewModdingAPI.exe\\n\\nPlease set the correct file path under\\nView > Settings\",\n  \"ui.warning.mod_config_saved_but_not_enabled\": \"Saved mod config settings to profile {0}, however profile specific mod configs are not enabled.\\n\\nGo to View > Settings and check \\\"Enable Profile Specific Mod Configs\\\" for Stardrop to automatically save and apply mod config changes.\",\n  \"ui.warning.unable_to_determine_profile\": \"Unable to determine selected profile.\\n\\nSMAPI will not be started.\",\n  \"ui.warning.stardrop_up_to_date\": \"Stardrop is up-to-date.\\n\\n{0} is the latest version.\",\n  \"ui.warning.stardrop_unable_to_download_latest\": \"Unable to download the latest release for Stardrop.\\n\\nPlease download the latest version manually.\",\n  \"ui.warning.stardrop_unable_to_find_latest\": \"Unable to check Stardrop's GitHub repository for an update.\\n\\nPlease check GitHub for a possible update or try again later.\",\n  \"ui.warning.stardrop_update_downloaded\": \"Downloaded the latest version successfully.\\n\\nStardrop will now restart.\",\n  \"ui.warning.SMAPI_unable_to_download_latest\": \"Unable to download the latest release of SMAPI.\\n\\nPlease download the latest version manually.\",\n  \"ui.warning.SMAPI_unable_to_install_latest\": \"Unable to install the latest release of SMAPI.\\n\\nPlease download the latest version manually.\",\n  \"ui.warning.update_cooldown_minutes\": \"Updates can only be requested once every 5 minutes.\\n\\nPlease try again in {0} minute(s).\",\n  \"ui.warning.update_cooldown_seconds\": \"Updates can only be requested once every 5 minutes.\\n\\nPlease try again in {0} second(s).\",\n  \"ui.warning.unable_to_locate_log\": \"Unable to locate SMAPI-latest.txt! SMAPI is required to run successfully at least once for Stardrop to detect game details.\",\n  \"ui.warning.unable_to_read_log\": \"Unable to read SMAPI's log file to grab game version.\\n\\nMods will not be checked for updates.\",\n  \"ui.warning.no_manifest\": \"No manifest.json found in \\\"{0}\\\"\",\n  \"ui.warning.unable_to_load_mod\": \"Unable to load the file located at \\\"{0}\\\".\\n\\nSee log file for more information.\",\n  \"ui.warning.unable_to_validate_nexus_key\": \"Unable to validate Nexus Mod API key.\\n\\nPlease verify the key is correct and try again.\",\n  \"ui.warning.download_without_premium\": \"You must have a premium Nexus Mods membership to download via Stardrop.\",\n  \"ui.warning.no_downloads_available\": \"There are currently no mods with updates available from Nexus Mods.\",\n  \"ui.warning.failed_nexus_install\": \"Failed to install the mod {0} due a connection issue with Nexus Mods.\\n\\nPlease try again.\",\n  \"ui.warning.unable_nexus_install\": \"Unable to install the mod {0}.\",\n  \"ui.warning.unable_to_endorse\": \"Unable to set the endorsement state:\\n\\n{0}\",\n  \"ui.warning.mod_owned\": \"You are the owner of this mod.\",\n  \"ui.warning.too_soon_after_download\": \"You must wait 15 minutes after downloading this mod.\\n\\nAttempting to endorse will reset this timer.\",\n  \"ui.warning.not_downloaded\": \"You must download this mod from Nexus Mods in order to endorse it.\",\n  \"ui.warning.failed_to_get_mod_details\": \"Failed to get the mod details from the NXM link: {0}\",\n  \"ui.warning.failed_to_get_download_link\": \"Failed to get the download url from Nexus Mods via the NXM link: {0}\",\n  \"ui.warning.failed_to_set_association\": \"Failed to register the NXM protocol with Stardrop.\\n\\nSee log for details.\",\n  \"ui.warning.already_associated\": \"Stardrop is already associated with the NXM protocol.\",\n\n  // Messages\n  \"ui.message.confirm_mod_deletion\": \"Are you sure you'd like to delete {0}? This cannot be undone.\",\n  \"ui.message.unsaved_config_changes\": \"Unsaved mod config changes detected for the profile {0}.\\n\\nWould you like to save the changes?\",\n  \"ui.message.stardrop_update_available\": \"An update (v{0}) is available for Stardrop.\\n\\nWould you like to download it now?\",\n  \"ui.message.SMAPI_update_available\": \"An update (v{0}) is available for SMAPI.\\n\\nWould you like to download it now?\",\n  \"ui.message.stardrop_update_complete\": \"Stardrop updated successfully.\\n\\nWould you like to see the release notes?\",\n  \"ui.message.SMAPI_update_complete\": \"SMAPI updated successfully.\\n\\nWould you like to see the release notes?\",\n  \"ui.message.confirm_bulk_change_mod_states_enable\": \"Enable all mods?\\n\\nNote: This cannot be undone.\",\n  \"ui.message.confirm_bulk_change_mod_states_disable\": \"Disable all mods?\\n\\nNote: This cannot be undone.\",\n  \"ui.message.confirm_mod_update_method\": \"A previous version of {0} has been detected. Would you like to clear the previous install?\\n\\nNote: Clearing previous versions is usually recommended, however any config files will be lost.\",\n  \"ui.message.confirm_mod_update_method_no_config\": \"A previous version of {0} has been detected. Would you like to clear the previous install?\\n\\nNote: Clearing previous versions is usually recommended.\",\n  \"ui.message.confirm_mod_update_method_preserved\": \"A previous version of {0} has been detected. Would you like to clear the previous install?\\n\\nNote: Clearing previous versions is usually recommended and your config files will be restored.\",\n  \"ui.message.confirm_nxm_association\": \"Would you like to associate the NXM protocol with Stardrop?\\n\\nThis will allow Stardrop to automatically install mods downloaded from Nexus Mods.\",\n  \"ui.message.confirm_nxm_install\": \"Would you like to install the following mod:\\n\\n{0}\\n\\nYou can disable this confirmation in the settings menu.\",\n  \"ui.message.require_nexus_login\": \"This action requires being signed into the Nexus Mods API.\",\n  \"ui.message.succeeded_nexus_install\": \"Successfully installed the following mod via Nexus Mods:\\n\\n{0}\",\n  \"ui.message.confirm_mod_update_caution\": \"{0}\\nUpdate Caution Message:\\n\\n{1}\\n\\nProceed with the mod update?\",\n\n  // Window Names\n  \"ui.window.settings.name\": \"Settings\",\n  \"ui.window.profiles.name\": \"Profiles\",\n  \"ui.window.profile_naming.name\": \"Profile Name\",\n  \"ui.window.nexus_login.name\": \"Nexus Mods Personal API Key\",\n  \"ui.window.nexus_login_actual.name\": \"Nexus Mods API Key\",\n  \"ui.window.nexus_user.name\": \"Nexus Mods User Details\",\n\n  // Internals\n  \"internal.connected\": \"Connected\",\n  \"internal.disconnected\": \"Disconnected\",\n  \"internal.unknown\": \"Unknown\",\n  \"internal.enable\": \"Enable\",\n  \"internal.disable\": \"Disable\",\n  \"internal.enable_whole_mod\": \"Enable Whole Mod Group\",\n  \"internal.disable_whole_mod\": \"Disable Whole Mod Group\",\n  \"internal.yes\": \"Yes\",\n  \"internal.yes_all\": \"Yes to All\",\n  \"internal.no\": \"No\",\n  \"internal.no_all\": \"No to All\",\n  \"internal.ok\": \"OK\",\n  \"internal.unlock\": \"Unlock\",\n\n  // Internals - Measurements\n  \"internal.measurements.bytes_size\": \"B\",\n  \"internal.measurements.bytes_per_second\": \"B/s\",\n  \"internal.measurements.kilobytes_size\": \"KB\",\n  \"internal.measurements.kilobytes_per_second\": \"KB/s\",\n  \"internal.measurements.megabytes_size\": \"MB\",\n  \"internal.measurements.megabytes_per_second\": \"MB/s\"\n}\n"
  },
  {
    "path": "Stardrop/i18n/es.json",
    "content": "{\n  // Main Window - Menu Items\n  \"ui.main_window.menu_items.add_mod\": \"Agregar Mod.\",\n  \"ui.main_window.menu_items.start_smapi\": \"Iniciar SMAPI\",\n  \"ui.main_window.menu_items.exit\": \"Salir\",\n  \"ui.main_window.menu_items.settings\": \"Configuración\",\n  \"ui.main_window.menu_items.stardrop_log_file\": \"Archivo De Registro De Stardrop\",\n  \"ui.main_window.menu_items.smapi_log_file\": \"Archivo De Registro De SMAPI\",\n  \"ui.main_window.menu_items.refresh_mod_list\": \"Actualizar Lista De Mods\",\n  \"ui.main_window.menu_items.check_for_mod_updates\": \"Comprobar Actualizaciones De Mods\",\n  \"ui.main_window.menu_items.enable_all_mods\": \"Activar Todos Los Mods\",\n  \"ui.main_window.menu_items.disable_all_mods\": \"Desactivar Todos Los Mods\",\n  \"ui.main_window.menu_items.check_for_stardrop_update\": \"Comprobar Actualización De Stardrop\",\n  \"ui.main_window.menu_items.stardrop_repository\": \"Repositorio De Stardrop\",\n   \"ui.main_window.menu_items.nexus_install_all\": \"Instalar Todas Las Actualizaciones\",\n  \"ui.main_window.menu_items.nexus_connection\": \"Conexion API\",\n  \"ui.main_window.menu_items.check_for_SMAPI_update\": \"Comprobar Actualizacion De SMAPI\",\n\n  // Main Window - Menu Items - Context\n  \"ui.main_window.menu_items.context.show_requirements\": \"Mostrar Requisitos\",\n  \"ui.main_window.menu_items.context.hide_requirements\": \"Ocultar Requisitos\",\n  \"ui.main_window.menu_items.context.cancel\": \"Cancelar\",\n  \"ui.main_window.menu_items.context.open_containing_folder\": \"Abrir La Carpeta Que Contiene\",\n  \"ui.main_window.menu_items.context.open_mod_page\": \"Abrir La Página Del Mod\",\n  \"ui.main_window.menu_items.context.delete\": \"Eliminar\",\n\n  // Main Window - Menu Headers\n  \"ui.main_window.menu_headers.file\": \"Archivo\",\n  \"ui.main_window.menu_headers.view\": \"Vista\",\n  \"ui.main_window.menu_headers.tools\": \"Herramientas\",\n  \"ui.main_window.menu_headers.nexus\": \"Nexus Mods\",\n\n  \"ui.main_window.menu_headers.enabled\": \"Activado\",\n  \"ui.main_window.menu_headers.mod_name\": \"Nombre Del Mod\",\n  \"ui.main_window.menu_headers.unique_id\": \"ID único\",\n  \"ui.main_window.menu_headers.author\": \"Autor\",\n  \"ui.main_window.menu_headers.requirements\": \"Requisitos\",\n  \"ui.main_window.menu_headers.missing_requirements\": \"Falta De Requisitos\",\n  \"ui.main_window.menu_headers.version\": \"Versión\",\n  \"ui.main_window.menu_headers.configuration\": \"Configuración\",\n  \"ui.main_window.menu_headers.status\": \"Estado\",\n  \"ui.main_window.menu_headers.endorsement\": \"Aprobación\",\n  \"ui.main_window.menu_headers.nexus_install\": \"Actualización de Nexus Mods\",\n\n  // Main Window - Labels\n  \"ui.main_window.labels.profile\": \"Perfil\",\n  \"ui.main_window.labels.enabled_mods\": \"Mods habilitados: \",\n  \"ui.main_window.labels.total_mods\": \"Total de Mods: \",\n  \"ui.main_window.labels.updatable_mods\": \"Mods listos para actualizar: \",\n  \"ui.main_window.labels.filter\": \"Filtro\",\n  \"ui.main_window.labels.unknown_SMAPI\": \"Versión de SMAPI desconocida\",\n\n  // Main Window - Hyperlinks\n  \"ui.main_window.hyperlinks.click_to_open\": \"Click para abrir\",\n  \"ui.main_window.hyperlinks.update_available\": \"Actualización disponible ({0})\",\n  \"ui.main_window.hyperlinks.unofficial_update_available\": \"Actualización no oficial disponible ({0})\",\n  \"ui.main_window.hyperlinks.broken_compatibility_issue\": \"Problema de compatibilidad [roto]\",\n  \"ui.main_window.hyperlinks.install_update\": \"Instalar Actualización\",\n  \"ui.main_window.hyperlinks.downloading\": \"Descargando...\",\n  \"ui.main_window.hyperlinks.installing\": \"Instalando...\",\n\n  // Main Window - Buttons\n  \"ui.main_window.buttons.save_configs\": \"Guardar Configuraciones\",\n  \"ui.main_window.buttons.hide_disabled_mods\": \"Ocultar Mods Desactivados\",\n  \"ui.main_window.buttons.show_updatable_mods\": \"Mostrar Mods Actualizables\",\n\n  // Main Window - Buttons - Update Status\n  \"ui.main_window.button.update_status.generic\": \"Mods Listos Para Actualizar: Haga Clic Para Actualizar\",\n  \"ui.main_window.button.update_status.updating\": \"Actualizando...\",\n  \"ui.main_window.button.update_status.list_available_updates\": \"Mods Listos Para Actualizar: {0}\",\n  \"ui.main_window.button.update_status.failed\": \"Fallo Comprobando Actualizaciones Del Mod.\",\n\n  // Main Window - ComboBox\n  \"ui.main_window.combobox.show_all_mods\": \"Mostrar Todos Los Mods\",\n  \"ui.main_window.combobox.hide_enabled_mods\": \"Ocultar Mods Activados\",\n  \"ui.main_window.combobox.mod_name\": \"Nombre Del mod\",\n  \"ui.main_window.combobox.author\": \"Autor\",\n  \"ui.main_window.combobox.requirements\": \"Requisitos\",\n\n  // Settings Window - Labels\n  \"ui.settings_window.labels.smapi_path\": \"Ruta De Acceso De SMAPI\",\n  \"ui.settings_window.labels.mod_path\": \"Ruta De Carpeta De Mods\",\n  \"ui.settings_window.labels.mod_install\": \"Ruta De Mods Instalados En Stardrop\",\n  \"ui.settings_window.labels.themes\": \"Temas\",\n  \"ui.settings_window.labels.languages\": \"Idiomas\",\n  \"ui.settings_window.labels.miscellaneous\": \"Miscelánea\",\n  \"ui.settings_window.labels.nexus\": \"Nexus Mods\",\n  \"ui.settings_window.labels.preferred_server\": \"Servidor Preferido:\",\n\n  // Settings Window - Buttons\n  \"ui.settings_window.buttons.ignore_hidden_folders\": \"Ignorar Carpetas Ocultas\",\n  \"ui.settings_window.buttons.enable_profile_specific_mod_configs\": \"Habilitar Configuraciones De Mods Específicas Para Cada Perfil\",\n  \"ui.settings_window.buttons.always_ask_for_NXM_installs\": \"Pregunte Siempre Antes De Instalar Archivos NXM\",\n  \"ui.settings_window.buttons.register_nxm_association\": \"Registrar la Asociación NXM\",\n\n  // Settings Window - Tooltips\n  \"ui.settings_window.tooltips.smapi\": \"La ruta del archivo de StardewModdingAPI\",\n  \"ui.settings_window.tooltips.mod_folder_path\": \"La ruta de la carpeta de mods\",\n  \"ui.settings_window.tooltips.mod_install_path\": \"La ruta de la carpeta en la que Stardrop colocará los mods recién instalados. Debe estar bajo la carpeta indicada en \\\"Mod Folder Path\\\".\",\n  \"ui.settings_window.tooltips.theme\": \"El tema actual de Stardrop\",\n  \"ui.settings_window.tooltips.language\": \"El idioma actual de Stardrop\",\n  \"ui.settings_window.tooltips.ignore_hidden_folders\": \"Si está marcada, Stardrop ignorará cualquier mods que tenga una carpeta padre que empiece por \\\".\\\"\",\n  \"ui.settings_window.tooltips.nxm_file_association\": \"Registra el protocolo NXM para asociarlo con Stardrop, lo que permite realizar instalaciones directas desde www.nexusmods.com.\",\n  \"ui.settings_window.tooltips.always_ask_nxm_files\": \"Si está marcada, Stardrop siempre preguntará antes de instalar un mod desde www.nexusmods.com a través de NXM.\",\n  \"ui.settings_window.tooltips.enable_profile_specific_configs\": \"Si está marcada, Stardrop guardará y restaurará los archivos config.json de los mods cuando se intercambien los perfiles\",\n   \"ui.settings_window.tooltips.preferred_server\": \"Establece tu servidor preferido para usar cuando descargues desde Nexus Mods.\",\n  \"ui.settings_window.tooltips.save_changes\": \"Guardar Cambios\",\n  \"ui.settings_window.tooltips.cancel_changes\": \"Cancelar\",\n  \n  // Nexus Login Window - Buttons\n  \"ui.nexus_login.buttons.nexus\": \"Ir A Nexus Mods\",\n  \"ui.nexus_login.buttons.disconnect\": \"Desconectar Nexus\",\n  \n  // Nexus Login Window - Labels\n  \"ui.nexus_login.labels.note\": \"Tenga en cuenta\",\n  \"ui.nexus_login.labels.get_key\": \"Obtenga su clave API personal de Nexus Mods\",\n  \"ui.nexus_login.labels.get_key_actual\": \"Consigue tu clave API de Stardrop en Nexus Mods\",\n  \"ui.nexus_login.labels.paste\": \"A continuación, pegue la clave\",\n  \"ui.nexus_login.labels.share_warning\": \"No comparta su clave personal de API con otras personas, ya que esto puede dar lugar a acciones contra su cuenta Nexus.\",\n  \"ui.nexus_login.labels.share_warning_actual\": \"No comparta su clave API con otras personas, ya que esto puede dar lugar a acciones contra su cuenta Nexus.\",\n  \"ui.nexus_login.labels.revoke\": \"Puede restablecer/revocar su clave en cualquier momento yendo a Mi cuenta Nexus > API > Clave personal de API.\",\n  \"ui.nexus_login.labels.revoke_actual\": \"Puede restablecer/revocar su clave API de Stardrop en cualquier momento yendo a Mi Cuenta Nexus > API > Stardrop.\",\n  \"ui.nexus_login.labels.nexus_details\": \"Detalles del usuario de Nexus Mods\",\n  \"ui.nexus_login.labels.nexus_disconnect\": \"Haz clic en el botón de abajo para que Stardrop olvide tu clave personal y se desconecte de Nexus Mods.\",\n  \"ui.nexus_login.labels.nexus_disconnect_actual\": \"Haz clic en el botón de abajo para que Stardrop olvide tu clave API de Stardrop y se desconecte de Nexus Mods.\",\n  \"ui.nexus_login.labels.username\": \"Nombre de usuario: {0}\",\n  \"ui.nexus_login.labels.is_premium\": \"Usuario premium: {0}\",\n\n  // Warnings\n  \"ui.warning.unable_to_add_profile\": \"No se puede añadir {0}, ya existe un perfil con ese nombre!\",\n  \"ui.warning.given_mod_folder_does_not_exist\": \"La ruta de la carpeta del mod dada no existe.\\n\\nRevirtiendo a la ruta anterior.\",\n  \"ui.warning.given_install_folder_not_exist\": \"La ruta de la carpeta de mods instalados en Stardrop no existe.\\n\\nRevirtiendo a la ruta anterior.\",\n  \"ui.warning.given_install_folder_not_exist_default\": \"La ruta de la carpeta de mods instalada por Stardrop no existe.\\n\\nRevirtiendo a la ruta anterior.\",\n  \"ui.warning.given_install_folder_not_under_mod_folder\": \"La ruta de la carpeta de mods instalada por Stardrop no existe en {0}\\n\\nRevirtiendo a la ruta anterior.\",\n  \"ui.warning.given_invalid_smapi_executable\": \"El archivo dado no es {0}\\n\\nRevirtiendo a la ruta anterior.\",\n  \"ui.warning.stardrop_locked\": \"Stardrop está bloqueado mientras SMAPI está en funcionamiento. Cualquier cambio realizado no se reflejará hasta que se cierre SMAPI.\",\n  \"ui.warning.stardrop_downloading\": \"Descargando la última versión de Stardrop.\",\n  \"ui.warning.SMAPI_downloading\": \"Descargando la última versión de SMAPI desde GitHub.\",\n  \"ui.warning.SMAPI_installing\": \"Actualizando SMAPI a la última versión.\",\n  \"ui.warning.mod_installing\": \"Instalando {0}\",\n  \"ui.warning.mod_updating\": \"Actualizando {0}\",\n  \n  \"ui.warning.unable_to_locate_smapi\": \"No se puede localizar StardewModdingAPI.exe\\n\\nPor favor, establezca la ruta correcta del archivo en\\nVista > Ajustes\",\n  \"ui.warning.mod_config_saved_but_not_enabled\": \"Guardado los ajustes de configuración del mod en el perfil {0}, sin embargo, las configuraciones de mods específicas del perfil no están habilitadas.\\n\\nVe a Vista > Ajustes y marca \\\"Enable Profile Specific Mod Configs\\\" para que Stardrop guarde y aplique automáticamente los cambios de configuración del mod.\",\n  \"ui.warning.unable_to_determine_profile\": \"No se ha podido determinar el perfil seleccionado.\\n\\nSMAPI no se iniciará.\",\n  \"ui.warning.stardrop_up_to_date\": \"Stardrop está actualizado.\\n\\n{0} es la última versión.\",\n  \"ui.warning.stardrop_unable_to_download_latest\": \"No se puede descargar la última versión de Stardrop.\\n\\nPor favor, descargue la última versión manualmente.\",\n  \"ui.warning.stardrop_unable_to_find_latest\": \"No se puede comprobar la actualización del repositorio GitHub de Stardrop.\\n\\nPor favor, comprueba en GitHub una posible actualización o inténtalo más tarde.\",\n  \"ui.warning.stardrop_update_downloaded\": \"He descargado la última versión con éxito.\\n\\nStardrop se reiniciará.\",\n  \"ui.warning.SMAPI_unable_to_download_latest\": \"No se puede descargar la última versión de SMAPI.\\n\\nPor favor, descargue la última versión manualmente.\",\n  \"ui.warning.SMAPI_unable_to_install_latest\": \"No se puede instalar la última versión de SMAPI.\\n\\nPor favor, descargue la última versión manualmente.\",\n  \"ui.warning.update_cooldown_minutes\": \"Las actualizaciones sólo pueden solicitarse una vez cada 5 minutos.\\n\\nPor favor, inténtelo de nuevo en {0} minuto(s).\",\n  \"ui.warning.update_cooldown_seconds\": \"Las actualizaciones sólo pueden solicitarse una vez cada 5 minutos.\\n\\nPor favor, inténtelo de nuevo en {0} segundo(s).\",\n  \"ui.warning.unable_to_locate_log\": \"No se ha podido localizar el archivo SMAPI-latest.txt! Es necesario que SMAPI se ejecute con éxito al menos una vez para que Stardrop detecte los detalles del juego.\",\n  \"ui.warning.unable_to_read_log\": \"No se puede leer el archivo de registro de SMAPI para obtener la versión del juego.\\n\\nNo se comprobarán actualizaciones para los mods\",\n  \"ui.warning.no_manifest\": \"No se ha encontrado manifest.json en \\\"{0}\\\"\",\n  \"ui.warning.unable_to_load_mod\": \"No se puede cargar el archivo ubicado en \\\"{0}\\\".\\n\\nConsulte el archivo de registro para obtener más información.\",\n  \"ui.warning.unable_to_validate_nexus_key\": \"No se ha podido validar la clave API de Nexus Mod.\\n\\nPor favor, verifique que la clave es correcta y vuelva a intentarlo.\",\n  \"ui.warning.download_without_premium\": \"Debes tener una membresía premium de Nexus Mods para descargar a través de Stardrop.\",\n  \"ui.warning.no_downloads_available\": \"Actualmente no hay mods con actualizaciones disponibles en Nexus Mods.\",\n  \"ui.warning.failed_nexus_install\": \"No se ha podido instalar el mod {0} debido a un problema de conexión con Nexus Mods.\\n\\nPor favor, inténtalo de nuevo.\",\n  \"ui.warning.unable_nexus_install\": \"No se puede instalar el mod {0}.\",\n  \"ui.warning.unable_to_endorse\": \"No se ha podido establecer el estado de la aprobación:\\n\\n{0}\",\n  \"ui.warning.mod_owned\": \"Usted es el propietario de este mod.\",\n  \"ui.warning.too_soon_after_download\": \"Debes esperar 15 minutos después de descargar este mod.\\n\\nAl intentar aprobar se reiniciará este temporizador.\",\n  \"ui.warning.not_downloaded\": \"Debes descargar este mod desde Nexus Mods para poder aprobarlo.\",\n  \"ui.warning.failed_to_get_mod_details\": \"No se han podido obtener los detalles del mod desde el enlace NXM: {0}\",\n  \"ui.warning.failed_to_get_download_link\": \"No se ha podido obtener la url de descarga de Nexus Mods a través del enlace NXM: {0}\",\n  \"ui.warning.failed_to_set_association\": \"No se ha podido registrar el protocolo NXM con Stardrop.\\n\\nConsulte el registro para obtener más detalles.\",\n  \"ui.warning.already_associated\": \"Stardrop ya está asociado al protocolo NXM.\",\n  \n  // Messages\n  \"ui.message.confirm_mod_deletion\": \"¿Está seguro de que desea eliminar {0}? Esto no se puede deshacer.\",\n  \"ui.message.unsaved_config_changes\": \"Se han detectado cambios de configuración de mods no guardados para el perfil {0}.\\n\\n¿Desea guardar los cambios?\",\n  \"ui.message.stardrop_update_available\": \"Hay una actualización (v{0}) disponible para Stardrop.\\n\\n¿Desea descargarlo ahora?\",\n  \"ui.message.SMAPI_update_available\": \"Hay una actualización (v{0}) disponible para SMAPI.\\n\\n¿Quiere descargarlo ahora?\",\n  \"ui.message.stardrop_update_complete\": \"Stardrop actualizado con éxito.\\n\\n¿Quiere ver las notas de la versión?\",\n  \"ui.message.SMAPI_update_complete\": \"SMAPI actualizado con éxito.\\n\\n¿Quiere ver las notas de la versión?\",\n  \"ui.message.confirm_bulk_change_mod_states_enable\": \"¿Activar todos los mods?\\n\\nNota: Esto no se puede deshacer.\",\n  \"ui.message.confirm_bulk_change_mod_states_disable\": \"¿Desactivar todos los mods?\\n\\nNota: Esto no se puede deshacer.\",\n  \"ui.message.confirm_mod_update_method\": \"Se ha detectado una versión anterior de {0}. ¿Desea borrar la instalación anterior?\\n\\nNota: Normalmente se recomienda borrar las versiones anteriores, aunque se perderán los archivos de configuración.\",\n  \"ui.message.confirm_mod_update_method_no_config\": \"Se ha detectado una versión anterior de {0}. Desea borrar la instalación anterior?\\n\\nNota: Normalmente se recomienda borrar las versiones anteriores.\",\n  \"ui.message.confirm_mod_update_method_preserved\": \"Se ha detectado una versión anterior de {0}. Desea borrar la instalación anterior?\\n\\nNota: Normalmente se recomienda borrar las versiones anteriores y sus archivos de configuración se restaurarán.\",\n  \"ui.message.confirm_nxm_association\": \"¿Quiere asociar el protocolo NXM con Stardrop?\\n\\nEsto permitirá a Stardrop instalar automáticamente los mods descargados de Nexus Mods.\",\n  \"ui.message.confirm_nxm_install\": \"Quiere instalar el siguiente mod:\\n\\n{0}\\n\\nPuede desactivar esta confirmación en el menú de configuración.\",\n  \"ui.message.require_nexus_login\": \"Esta acción requiere estar registrado en la API de Nexus Mods.\",\n  \"ui.message.succeeded_nexus_install\": \"Instalado con éxito el siguiente mod a través de Nexus Mods:\\n\\n{0}\",\n  \n  // Window Names\n  \"ui.window.settings.name\": \"Ajustes\",\n  \"ui.window.profiles.name\": \"Perfiles\",\n  \"ui.window.profile_naming.name\": \"Nombre De Perfil\",\n  \"ui.window.nexus_login.name\": \"Clave API personal de Nexus Mods\",\n  \"ui.window.nexus_login_actual.name\": \"Clave API de Nexus Mods\",\n  \"ui.window.nexus_user.name\": \"Detalles del usuario de Nexus Mods\",\n\n  // Internals\n  \"internal.connected\": \"Conectado\",\n  \"internal.disconnected\": \"Desconectado\",\n  \"internal.unknown\": \"Desconocido\",\n  \"internal.enable\": \"Activar\",\n  \"internal.disable\": \"Desactivar\",\n  \"internal.yes\": \"Si\",\n  \"internal.yes_all\": \"Yes a Todo\",\n  \"internal.no\": \"No\",\n  \"internal.no_all\": \"No a Todo\",\n  \"internal.ok\": \"OK\",\n  \"internal.unlock\": \"Desbloquear\"\n}\n"
  },
  {
    "path": "Stardrop/i18n/fr.json",
    "content": "{\n    // Main Window - Menu Items\n    \"ui.main_window.menu_items.add_mod\": \"Ajouter Un Mod\",\n    \"ui.main_window.menu_items.start_smapi\": \"Lancer SMAPI\",\n    \"ui.main_window.menu_items.exit\": \"Quitter\",\n    \"ui.main_window.menu_items.settings\": \"Paramètres\",\n    \"ui.main_window.menu_items.stardrop_log_file\": \"Log Stardrop\",\n    \"ui.main_window.menu_items.smapi_log_file\": \"Log SMAPI\",\n    \"ui.main_window.menu_items.refresh_mod_list\": \"Rafraîchir La Liste\",\n    \"ui.main_window.menu_items.check_for_mod_updates\": \"Rechercher Des MAJ\",\n    \"ui.main_window.menu_items.enable_all_mods\": \"Tout Activer\",\n    \"ui.main_window.menu_items.disable_all_mods\": \" Tout Désactiver\",\n    \"ui.main_window.menu_items.check_for_stardrop_update\": \"Rechercher Une MAJ De Stardrop\",\n    \"ui.main_window.menu_items.stardrop_repository\": \"Repository GitHub De Stardrop\",\n  \n    // Main Window - Menu Items - Context\n    \"ui.main_window.menu_items.context.show_requirements\": \"Afficher Les Dépendances\",\n    \"ui.main_window.menu_items.context.hide_requirements\": \"Cacher Les Dépendances\",\n    \"ui.main_window.menu_items.context.cancel\": \"Annuler\",\n    \"ui.main_window.menu_items.context.open_containing_folder\": \"Ouvrir Le Dossier\",\n    \"ui.main_window.menu_items.context.open_mod_page\": \"Aller Sur La Page Du Mod\",\n    \"ui.main_window.menu_items.context.delete\": \"Supprimer\",\n  \n    // Main Window - Menu Headers\n    \"ui.main_window.menu_headers.file\": \"Fichier\",\n    \"ui.main_window.menu_headers.view\": \"Affichage\",\n    \"ui.main_window.menu_headers.tools\": \"Outils\",\n  \n    \"ui.main_window.menu_headers.enabled\": \"Activé\",\n    \"ui.main_window.menu_headers.mod_name\": \"Nom Du Mod\",\n    \"ui.main_window.menu_headers.unique_id\": \"ID Unique\",\n    \"ui.main_window.menu_headers.author\": \"Auteur\",\n    \"ui.main_window.menu_headers.requirements\": \"Dépendances\",\n    \"ui.main_window.menu_headers.missing_requirements\": \"Dépendances Manquantes\",\n    \"ui.main_window.menu_headers.version\": \"Version\",\n    \"ui.main_window.menu_headers.configuration\": \"Configuration\",\n    \"ui.main_window.menu_headers.status\": \"État\",\n  \n    // Main Window - Labels\n    \"ui.main_window.labels.profile\": \"Profil\",\n    \"ui.main_window.labels.enabled_mods\": \"Mods Activés: \",\n    \"ui.main_window.labels.total_mods\": \"Mods Totaux: \",\n    \"ui.main_window.labels.filter\": \"Filtres\",\n  \n    // Main Window - Hyperlinks\n    \"ui.main_window.hyperlinks.click_to_open\": \"Cliquer Pour Ouvrir\",\n    \"ui.main_window.hyperlinks.update_available\": \"MAJ Disponible ({0})\",\n    \"ui.main_window.hyperlinks.unofficial_update_available\": \"MAJ Non-Officielle Disponible ({0})\",\n    \"ui.main_window.hyperlinks.broken_compatibility_issue\": \"[Broken] Erreur De Compatibilité\",\n  \n    // Main Window - Buttons\n    \"ui.main_window.buttons.save_configs\": \"Sauvegarder Les Configurations\",\n    \"ui.main_window.buttons.hide_disabled_mods\": \"Cacher Les Mods Désactivés\",\n    \"ui.main_window.buttons.show_updatable_mods\": \"Afficher Les Mods Prêts À Mettre À Jour\",\n  \n    // Main Window - Buttons - Update Status\n    \"ui.main_window.button.update_status.generic\": \"Mods Prêts À Mettre À Jour : Cliquer Pour Rafraîchir\",\n    \"ui.main_window.button.update_status.updating\": \"Mises À Jour En Cours ...\",\n    \"ui.main_window.button.update_status.list_available_updates\": \"Mods Prêt À Mettre À Jour: {0}\",\n    \"ui.main_window.button.update_status.failed\": \"Recherche De MAJ Échouée\",\n  \n    // Main Window - ComboBox\n    \"ui.main_window.combobox.show_all_mods\": \"Afficher Tous Les Mods\",\n    \"ui.main_window.combobox.hide_enabled_mods\": \"Cacher Les Mods Activés\",\n    \"ui.main_window.combobox.mod_name\": \"Nom Du Mod\",\n    \"ui.main_window.combobox.author\": \"Auteur\",\n    \"ui.main_window.combobox.requirements\": \"Dépendances\",\n  \n    // Settings Window - Labels\n    \"ui.settings_window.labels.smapi_path\": \"Chemin de SMAPI\",\n    \"ui.settings_window.labels.mod_path\": \"Chemin du mod\",\n    \"ui.settings_window.labels.mod_install\": \"Chemin des mods installés par Stardrop\",\n    \"ui.settings_window.labels.themes\": \"Thèmes\",\n    \"ui.settings_window.labels.languages\": \"Langues\",\n    \"ui.settings_window.labels.miscellaneous\": \"Divers\",\n  \n    // Settings Window - Buttons\n    \"ui.settings_window.buttons.ignore_hidden_folders\": \"Ignorer Les Dossiers Cachés\",\n    \"ui.settings_window.buttons.enable_profile_specific_mod_configs\": \"Activer La Configuration Par Profil\",\n  \n    // Settings Window - Tooltips\n    \"ui.settings_window.tooltips.smapi\": \"Le chemin d'accès de StardewModdingAPI\",\n    \"ui.settings_window.tooltips.mod_folder_path\": \"Le chemin d'accès du dossier du mod\",\n    \"ui.settings_window.tooltips.mod_install_path\": \"Le chemin d'accès dans lequel Stardrop va installer les mods. Doit être en dessous du chemin donné dans  \\\"Chemin du mod\\\".\",\n    \"ui.settings_window.tooltips.theme\": \"Le thème actuel de Stardrop\",\n    \"ui.settings_window.tooltips.language\": \"La langue actuelle de Stardrop\",\n    \"ui.settings_window.tooltips.ignore_hidden_folders\": \"Si coché, Stardrop ignorera tout les mods dont le dossier parent commence par \\\".\\\"\",\n    \"ui.settings_window.tooltips.enable_profile_specific_configs\": \"Si coché, Stardrop sauvegardera et restaurera les fichiers config.json des mods lors d'un changement de profil\",\n    \"ui.settings_window.tooltips.save_changes\": \"Sauvegarder les changements\",\n    \"ui.settings_window.tooltips.cancel_changes\": \"Annuler\",\n  \n    // Warnings\n    \"ui.warning.unable_to_add_profile\": \"Impossible d'ajouter {0}, un profil avec le même nom existe déjà!\",\n    \"ui.warning.given_mod_folder_does_not_exist\": \"Le chemin d'accès donné pour le dossier du mod n'existe pas.\\n\\nRetour à un chemin antèrieur.\",\n    \"ui.warning.given_install_folder_not_exist\": \"Le chemin d'accès donné pour les mods installés par Stardrop n'existe pas.\\n\\nRetour à un chemin antèrieur.\",\n    \"ui.warning.given_install_folder_not_under_mod_folder\": \"Le chemin d'accès donné pour les mods installés par Stardrop n'existe pas sous {0}\\n\\nRetour à un chemin antèrieur.\",\n    \"ui.warning.given_invalid_smapi_executable\": \"Le fichier n'est pas {0}\\n\\nRetour à un chemin antèrieur.\",\n    \"ui.warning.stardrop_locked\": \"Stardrop est bloqué lorsque SMAPI est lancé. Tout changement ne prendra effet que lorsque SMAPI sera fermé.\",\n  \n    \"ui.warning.unable_to_locate_smapi\": \"Impossible de trouver StardewModdingAPI.exe\\n\\nChoisissez le chemin correct dans\\nAffichage > Paramètres\",\n    \"ui.warning.mod_config_saved_but_not_enabled\": \"Les configurations du profil {0} on été sauvegardées, mais les configurations par profils ne sont pas activées.\\n\\nAller dans Affichage > Paramètres et cocher \\\"Activer La Configuration Par Profil\\\" pour que Stardrop sauvegarde et applique automatiquement les changements.\",\n    \"ui.warning.unable_to_determine_profile\": \"Impossible de determiner le bon profil.\\n\\nSMAPI ne se lancera pas.\",\n    \"ui.warning.stardrop_up_to_date\": \"Stardrop est à jour.\\n\\n{0} est la dernière version.\",\n    \"ui.warning.stardrop_unable_to_download_latest\": \"Impossible de télécharger la dernière version de Stardrop.\\n\\nVeuillez la télécharger manuellement.\",\n    \"ui.warning.stardrop_update_downloaded\": \"Le téléchargement de la dernière version est terminé.\\n\\nStardrop va maintenant redémarrer.\",\n    \"ui.warning.update_cooldown_minutes\": \"La recherche de MAJ ne peut se faire que toutes les 5 minutes.\\n\\nRéessayer dans {0} minute(s).\",\n    \"ui.warning.update_cooldown_seconds\": \"La recherche de MAJ ne peut se faire que toutes les 5 minutes.\\n\\nRéessayer dans {0} seconde(s).\",\n    \"ui.warning.unable_to_locate_log\": \"Impossible de trouver SMAPI-latest.txt! Vous devez lancer SMAPI au moins une fois pour que Stardrop trouve les détails du jeu.\",\n    \"ui.warning.unable_to_read_log\": \"Impossible de lire le log de SMAPI pour trouver le numéro de version.\\n\\nLes mods de rechercheront pas de mise à jours.\",\n    \"ui.warning.no_manifest\": \"Pas de manifest.json trouvé dans \\\"{0}\\\"\",\n    \"ui.warning.unable_to_load_mod\": \"Impossible de charge le fichier à \\\"{0}\\\".\\n\\nConsulter le log pour plus d'informations.\",\n  \n    // Messages\n    \"ui.message.confirm_mod_deletion\": \"Etes-vous sûr de supprimer {0}? Cette action est irréversible.\",\n    \"ui.message.unsaved_config_changes\": \"Des changements pour la configuration {0} on été détectés.\\n\\nVoulez-vous sauvegarder vos changements?\",\n    \"ui.message.stardrop_update_available\": \"Une nouvelle version (v{0}) est disponible pour Stardrop.\\n\\nVoulez-vous la télécharger maintenant?\",\n    \"ui.message.confirm_bulk_change_mod_states_enable\": \"Activer tous les mods?\\n\\nNote: Cette action est irréversible.\",\n    \"ui.message.confirm_bulk_change_mod_states_disable\": \"Désactiver tout les mods?\\n\\nNote: Cette action est irréversible.\",\n    \"ui.message.confirm_mod_update_method\": \"Une version antèrieure de {0} a été détecté. Voulez-vous supprimer l'ancienne version?\\n\\nNote: Supprimer les versions antèrieures est souvent recommandé. Les configurations seront en revanche perdues..\",\n  \n    // Window Names\n    \"ui.window.settings.name\": \"Paramètres\",\n    \"ui.window.profiles.name\": \"Profils\",\n    \"ui.window.profile_naming.name\": \"Non Du Profil\",\n  \n    // Internals\n    \"internal.unknown\": \"Inconnu\",\n    \"internal.enable\": \"Activer\",\n    \"internal.disable\": \"Désactiver\",\n    \"internal.yes\": \"Oui\",\n    \"internal.no\": \"Non\",\n    \"internal.ok\": \"OK\",\n    \"internal.unlock\": \"Débloquer\"\n  }\n"
  },
  {
    "path": "Stardrop/i18n/hu.json",
    "content": "{\n  // Főablak - Menüpontok\n  \"ui.main_window.menu_items.add_mod\": \"Mod hozzáadása\",\n  \"ui.main_window.menu_items.add_mods\": \"Modok hozzáadása\",\n  \"ui.main_window.menu_items.start_smapi\": \"SMAPI indítása\",\n  \"ui.main_window.menu_items.exit\": \"Kilépés\",\n  \"ui.main_window.menu_items.settings\": \"Beállítások\",\n  \"ui.main_window.menu_items.stardrop_log_file\": \"Stardrop naplófájl\",\n  \"ui.main_window.menu_items.smapi_log_file\": \"SMAPI naplófájl\",\n  \"ui.main_window.menu_items.refresh_mod_list\": \"Mod lista frissítése\",\n  \"ui.main_window.menu_items.check_for_mod_updates\": \"Mod frissítések ellenőrzése\",\n  \"ui.main_window.menu_items.enable_all_mods\": \"Összes mod engedélyezése\",\n  \"ui.main_window.menu_items.disable_all_mods\": \"Összes mod letiltása\",\n  \"ui.main_window.menu_items.check_for_stardrop_update\": \"Stardrop frissítés ellenőrzése\",\n  \"ui.main_window.menu_items.stardrop_repository\": \"Stardrop adattár\",\n  \"ui.main_window.menu_items.nexus_install_all\": \"Minden frissítés telepítése\",\n  \"ui.main_window.menu_items.nexus_connection\": \"API kapcsolat\",\n  \"ui.main_window.menu_items.check_for_SMAPI_update\": \"SMAPI frissítés ellenőrzése\",\n\n  // Főablak - Menüelemek - Kontextus\n  \"ui.main_window.menu_items.context.show_requirements\": \"Követelmények megjelenítése\",\n  \"ui.main_window.menu_items.context.hide_requirements\": \"Követelmények elrejtése\",\n  \"ui.main_window.menu_items.context.cancel\": \"Mégsem\",\n  \"ui.main_window.menu_items.context.open_containing_folder\": \"Tartalmazó mappa megnyitása\",\n  \"ui.main_window.menu_items.context.open_mod_page\": \"Mod oldal megnyitása\",\n  \"ui.main_window.menu_items.context.open_config\": \"Konfigurációs fájl megnyitása\",\n  \"ui.main_window.menu_items.context.show_whole_mod_group\": \"Teljes mod csoport megjelenítése\",\n  \"ui.main_window.menu_items.context.show_authors_mods\": \"A szerző modjainak megjelenítése\",\n  \"ui.main_window.menu_items.context.open\": \"Megnyitás...\",\n  \"ui.main_window.menu_items.context.filters\": \"Szűrők\",\n  \"ui.main_window.menu_items.context.options\": \"Beállítások\",\n  \"ui.main_window.menu_items.context.clear_filter\": \"Tisztítás\",\n  \"ui.main_window.menu_items.context.delete\": \"Törlés\",\n\n  // Főablak - Menüfejlécek\n  \"ui.main_window.menu_headers.file\": \"Fájl\",\n  \"ui.main_window.menu_headers.view\": \"Megtekintés\",\n  \"ui.main_window.menu_headers.tools\": \"Eszközök\",\n  \"ui.main_window.menu_headers.nexus\": \"Nexus modok\",\n\n  \"ui.main_window.menu_headers.enabled\": \"Engedélyezve\",\n  \"ui.main_window.menu_headers.mod_name\": \"Mod név\",\n  \"ui.main_window.menu_headers.unique_id\": \"Egyedi azonosító (ID)\",\n  \"ui.main_window.menu_headers.author\": \"Szerző\",\n  \"ui.main_window.menu_headers.requirements\": \"Követelmények\",\n  \"ui.main_window.menu_headers.missing_requirements\": \"Hiányzó követelmények\",\n  \"ui.main_window.menu_headers.version\": \"Verzió\",\n  \"ui.main_window.menu_headers.configuration\": \"Konfiguráció\",\n  \"ui.main_window.menu_headers.status\": \"Állapot\",\n  \"ui.main_window.menu_headers.endorsement\": \"Jóváhagyás\",\n  \"ui.main_window.menu_headers.nexus_install\": \"Nexus mod frissítések\",\n  \"ui.main_window.menu_headers.install_timestamp\": \"Telepítés időbélyege\",\n  \"ui.main_window.menu_headers.last_updated\": \"Utolsó frissítés\",\n\n  // Főablak - Címkék\n  \"ui.main_window.labels.profile\": \"Profil\",\n  \"ui.main_window.labels.enabled_mods\": \"Engedélyezett modok: \",\n  \"ui.main_window.labels.total_mods\": \"Összes mod: \",\n  \"ui.main_window.labels.updatable_mods\": \"Frissíthető modok: \",\n  \"ui.main_window.labels.filter\": \"Szűrő\",\n  \"ui.main_window.labels.unknown_SMAPI\": \"Ismeretlen SMAPI verzió\",\n\n  // Főablak - Hiperhivatkozások\n  \"ui.main_window.hyperlinks.click_to_open\": \"Kattints a megnyitáshoz\",\n  \"ui.main_window.hyperlinks.update_available\": \"({0}) elérhető frissítés\",\n  \"ui.main_window.hyperlinks.unofficial_update_available\": \"({0}) elérhető nem hivatalos frissítés\",\n  \"ui.main_window.hyperlinks.broken_compatibility_issue\": \"[Hiba] Kompatibilitás probléma\",\n  \"ui.main_window.hyperlinks.install_update\": \"Frissítés telepítés\",\n  \"ui.main_window.hyperlinks.downloading\": \"Letöltés...\",\n  \"ui.main_window.hyperlinks.installing\": \"Telepítés...\",\n\n  // Főablak - Gombok\n  \"ui.main_window.buttons.save_configs\": \"Konfiguráció mentés\",\n  \"ui.main_window.buttons.save_profile\": \"Profil mentés\",\n  \"ui.main_window.buttons.show_updatable_mods\": \"Frissíthető modok megjelenítése\",\n  \"ui.main_window.buttons.active_search_filters\": \"{0} aktív szűrő\",\n  \"ui.main_window.buttons.no_search_filters\": \"Nincs aktív szűrő\",\n\n  // Főablak - Gombok - Állapotfrissítés\n  \"ui.main_window.button.update_status.generic\": \"Mod frissítések ellenőrzése\",\n  \"ui.main_window.button.update_status.updating\": \"Frissítés...\",\n  \"ui.main_window.button.update_status.list_available_updates\": \"Frissítendő modok: {0}\",\n  \"ui.main_window.button.update_status.failed\": \"Sikertelen mod frissítés ellenőrzés\",\n\n  // Főablak - Gombok - Letöltések\n  \"ui.main_window.buttons.downloads.label\": \"Letöltés(ek) ({0})\",\n\n  // Főablak - Kombinációs mező\n  \"ui.main_window.combobox.show_all_mods\": \"Összes mod megjelenítése\",\n  \"ui.main_window.combobox.show_mods_with_configs\": \"Konfigurálható modok megjelenítése\",\n  \"ui.main_window.combobox.show_disabled_mods\": \"Letiltott modok megjelenítése\",\n  \"ui.main_window.combobox.show_enabled_mods\": \"Engedélyezett modok megjelenítése\",\n\n  // Főablak - kombinációs doboz (Lista doboz)\n  \"ui.main_window.combobox.mod_name\": \"Mod név\",\n  \"ui.main_window.combobox.author\": \"Szerző\",\n  \"ui.main_window.combobox.requirements\": \"Követelmények\",\n  \"ui.main_window.combobox.group\": \"Mod csoport\",\n\n  // Beállítások ablak - Címkék\n  \"ui.settings_window.labels.smapi_path\": \"SMAPI elérési útvonal\",\n  \"ui.settings_window.labels.mod_path\": \"Mod mappa elérési útvonal\",\n  \"ui.settings_window.labels.mod_install\": \"Stardrop által telepített mod útvonal\",\n  \"ui.settings_window.labels.themes\": \"Témák\",\n  \"ui.settings_window.labels.languages\": \"Nyelvek\",\n  \"ui.settings_window.labels.grouping\": \"Mod csoportosítás\",\n  \"ui.settings_window.labels.miscellaneous\": \"Egyéb\",\n  \"ui.settings_window.labels.nexus\": \"Nexus modok\",\n  \"ui.settings_window.labels.preferred_server\": \"Előnyben részesített szerver:\",\n\n  // Beállítások ablak - Gombok\n  \"ui.settings_window.buttons.ignore_hidden_folders\": \"Rejtett mappák figyelmen kívül hagyása\",\n  \"ui.settings_window.buttons.enable_profile_specific_mod_configs\": \"Profil specifikus mod konfigurációk engedélyezése\",\n  \"ui.settings_window.buttons.enable_mods_on_add\": \"Modok automatikus engedélyezése telepítéskor\",\n  \"ui.settings_window.buttons.always_ask_to_delete\": \"Mod fájlok törlése frissítéskor\",\n  \"ui.settings_window.buttons.automatically_save_profile_changes\": \"Profilváltozások automatikus mentése\",\n  \"ui.settings_window.buttons.always_ask_for_NXM_installs\": \"Mindig kérdezzen rá az NXM fájlok telepítése előtt\",\n  \"ui.settings_window.buttons.register_nxm_association\": \"NXM Egyesület regisztrálása\",\n\n  // Beállítások ablaka - Eszköztippek\n  \"ui.settings_window.tooltips.smapi\": \"A StardewModdingAPI fájl elérési útvonala.\",\n  \"ui.settings_window.tooltips.mod_folder_path\": \"A mod mappa elérési útvonala.\",\n  \"ui.settings_window.tooltips.mod_install_path\": \"A mod mappa elérési útvonala, ahová a Stardrop az újonnan telepített modokat helyezi. A következő mappában megadott mappa alatt kell lennie \\\"Mod mappa elérési útvonal\\\"\",\n  \"ui.settings_window.tooltips.theme\": \"A Stardrop jelenlegi témája.\",\n  \"ui.settings_window.tooltips.language\": \"A Stardrop jelenlegi nyelve.\",\n  \"ui.settings_window.tooltips.grouping\": \"A modok csoportosításának jelenlegi módszere.\",\n  \"ui.settings_window.tooltips.ignore_hidden_folders\": \"Ha be van jelölve, a Stardrop figyelmen kívül hagy minden olyan modot, melynek mappája a következővel kezdődik \\\".\\\"\",\n  \"ui.settings_window.tooltips.nxm_file_association\": \"Regisztráld az NXM protokollt a Stardrophoz társítva, lehetővé téve a közvetlen telepítést a www.nexusmods.com weboldalról.\",\n  \"ui.settings_window.tooltips.always_ask_nxm_files\": \"Ha be van jelölve, a Stardrop mindig megkérdezi, mielőtt egy modot telepít a www.nexusmods.com oldalról az NXM-en keresztül.\",\n  \"ui.settings_window.tooltips.enable_profile_specific_configs\": \"Ha be van jelölve, profilcsere esetén a Stardrop elmenti és visszaállítja a modok config.json fájljait.\",\n  \"ui.settings_window.tooltips.enable_mods_on_add\": \"Ha be van jelölve, a Stardrop automatikusan engedélyezi az újonnan hozzáadott vagy frissített modokat.\",\n  \"ui.settings_window.tooltips.always_ask_to_delete\": \"Ha be van jelölve, a Stardrop mindig megkérdezi, hogy törölje-e a mod fájlokat mod frissítésekor.\",\n  \"ui.settings_window.tooltips.automatically_save_profile_changes\": \"Ha be van jelölve, a Stardrop automatikusan elmenti a profilon végzett változtatásokat.\",\n  \"ui.settings_window.tooltips.preferred_server\": \"Beállítja a Nexus modsról történő letöltéskor használni kívánt kiszolgálót.\",\n  \"ui.settings_window.tooltips.save_changes\": \"Változások mentése\",\n  \"ui.settings_window.tooltips.cancel_changes\": \"Mégsem\",\n\n  // Nexus bejelentkezési ablak - Gombok\n  \"ui.nexus_login.buttons.nexus\": \"Tovább a Nexus mod oldalra\",\n  \"ui.nexus_login.buttons.disconnect\": \"Lekapcsolódás a Nexusról\",\n\n  // Nexus bejelentkezési ablak - Címkék\n  \"ui.nexus_login.labels.note\": \"Megjegyzés\",\n  \"ui.nexus_login.labels.get_key\": \"Szerezd meg a személyes API kulcsodat a Nexus modstól.\",\n  \"ui.nexus_login.labels.get_key_actual\": \"Szerezd meg a Stardrop API kulcsot a Nexus modstól.\",\n  \"ui.nexus_login.labels.paste\": \"Majd illeszd be az alábbi kulcsot.\",\n  \"ui.nexus_login.labels.share_warning\": \"Ne oszd meg a személyes API kulcsodat másokkal, mert az a Nexus fiókoddal szembeni intézkedéseket eredményezhet.\",\n  \"ui.nexus_login.labels.share_warning_actual\": \"Kulcsodat bármikor visszaállíthatod/visszavonhatod az Én Nexus fiókom > API > Személyes API kulcs menüpontban.\",\n  \"ui.nexus_login.labels.revoke\": \"Visszaállíthatod/visszavonhtod kulcsodat bármikor az Én Nexus fiókom > API > Személyes API kulcs menüpontban.\",\n  \"ui.nexus_login.labels.revoke_actual\": \"A Stardrop API kulcsodat bármikor visszaállíthatod/visszavonhatod az Én Nexus fiókom > API > Stardrop menüpontban.\",\n  \"ui.nexus_login.labels.nexus_details\": \"Nexus mods felhasználói adatok\",\n  \"ui.nexus_login.labels.nexus_disconnect\": \"Kattints az alábbi gombra, hogy a Stardrop elfelejtse a személyes kulcsodat és lekapcsolódjon a Nexus modsról.\",\n  \"ui.nexus_login.labels.nexus_disconnect_actual\": \"Kattints az alábbi gombra, hogy a Stardrop elfelejtse a Stardrop API kulcsodat és lekapcsolódjon a Nexus modsról.\",\n  \"ui.nexus_login.labels.username\": \"Felhasználónév: {0}\",\n  \"ui.nexus_login.labels.is_premium\": \"Prémium tagság: {0}\",\n\n  // Letöltések panel - Feliratok\n  \"ui.downloads_panel.download_canceled\": \"Megszakítva\",\n  \"ui.downloads_panel.download_failed\": \"Sikertelen\",\n  \"ui_downloads_panel.download_success\": \"Kész!\",\n  \"ui.downloads_panel.no_downloads_label\": \"Nincs folyamatban lévő letöltés\",\n\n  // Letöltések panel - Eszköztipp\n  \"ui.downloads_panel.tooltips.cancel_button\": \"Mégsem\",\n  \"ui.downloads_panel.tooltips.remove_button\": \"Eltávolítva\", // Eszköztipp a befejezett vagy sikertelen letöltés törlő gombhoz\n\n  // Figyelmeztetések\n  \"ui.warning.unable_to_add_profile\": \"Nem lehet hozzáadni a(z) {0}, ezen a néven már létezik egy profil!\",\n  \"ui.warning.given_mod_folder_does_not_exist\": \"A megadott mod mappa elérési útvonala nem létezik.\\n\\nElőző útvonal visszaállítása.\",\n  \"ui.warning.given_install_folder_not_exist\": \"A megadott Stardrop telepített mod mappa elérési útvonal nem létezik.\\n\\nElőző útvonal visszaállítása.\",\n  \"ui.warning.given_install_folder_not_exist_default\": \"A megadott Stardrop telepített mod mappa elérési útvonal nem létezik.\\n\\nAlapértelmezett útvonal visszaállítása.\",\n  \"ui.warning.given_install_folder_not_under_mod_folder\": \"A megadott Stardrop telepített mod mappa elérési útvonal nem létezik a(z) {0} alatt.\\n\\nElőző útvonal visszaállítása.\",\n  \"ui.warning.given_invalid_smapi_executable\": \"A {0} nem a megadott fájl.\\n\\nElőző útvonal visszaállítása.\",\n  \"ui.warning.stardrop_locked\": \"A Stardrop zárolva van, amíg az SMAPI fut. A végrehajtott módosítások a SMAPI  bezárásáig nem jelennek meg.\",\n  \"ui.warning.stardrop_downloading\": \"A Stardrop legújabb verziójának letöltése.\",\n  \"ui.warning.SMAPI_downloading\": \"A SMAPI legújabb verziójának letöltése a GitHubról.\",\n  \"ui.warning.SMAPI_installing\": \"A SMAPI frissítése a legújabb verzióra.\",\n  \"ui.warning.SMAPI_up_to_date\": \"A SMAPI naprakész.\\n\\n{0} a legfrissebb verzió.\",\n  \"ui.warning.install_mod_attempt_count\": \"{0} mod telepítése\",\n  \"ui.warning.mod_installing\": \"A(z) {0} telepítése.\",\n  \"ui.warning.mod_updating\": \"A(z) {0} frissítése.\",\n  \"ui.warning.mod_deleting\": \"A(z) {0} régi példányának törlése\",\n  \"ui.warning.failed_to_delete\": \"A(z) {0} törlése nem sikerült.\\n\\nA mod fájlai zárolva lehetnek, a részletekért lásd a naplót.\",\n  \"ui.warning.failed_to_delete_during_update\": \"Nem sikerült törölni a(z) {0} régi példányát.\\n\\nElőfordulhat, hogy a mod hibásan települ.\",\n  \"ui.warning.unable_to_locate_smapi\": \"Nem található a StardewModdingAPI.exe fájl.\\n\\nKérjük, állítsd be a fájl helyes elérési útvonalát a Nézet -> Beállítások menüpont alatt.\",\n  \"ui.warning.mod_config_saved_but_not_enabled\": \"A mod config beállítások elmentve a(z) {0} profilba, azonban a profilon a specifikus mod konfigurációk nincsenek engedélyezve.\\n\\nVálaszd a Nézet -> Beállítások menüpontot, és ellenőrizd a \\\"Profil specifikus mod konfigurációk engedélyezése\\\" menüpontot hogy a Stardrop automatikusan mentse és alkalmazza a mod konfigurációs módosításokat.\",\n  \"ui.warning.unable_to_determine_profile\": \"Nem sikerült meghatározni a kiválasztott profilt.\\n\\nA SMAPI nem fog elindulni.\",\n  \"ui.warning.stardrop_up_to_date\": \"A Stardrop naprakész.\\n\\nA {0} a legfrissebb verzió.\",\n  \"ui.warning.stardrop_unable_to_download_latest\": \"Nem lehet letölteni a Stardrop legújabb verzióját.\\n\\nKérjük, töltsd le manuálisan a legújabb verziót.\",\n  \"ui.warning.stardrop_unable_to_find_latest\": \"Nem lehet ellenőrizni a Stardrop frissítését a GitHub tárolójban.\\n\\nKérjük, ellenőrizd a GitHubot egy esetleges frissítésért, vagy próbálkozz újra később.\",\n  \"ui.warning.stardrop_update_downloaded\": \"A legújabb verzió letöltése sikeres volt.\\n\\nA Stardrop most újraindul.\",\n  \"ui.warning.SMAPI_unable_to_download_latest\": \"Nem lehet letölteni a SMAPI legújabb kiadását.\\n\\nKérjük, töltsd le manuálisan a legújabb verziót.\",\n  \"ui.warning.SMAPI_unable_to_install_latest\": \"Nem sikerült telepíteni a SMAPI legújabb verzióját.\\n\\nKérjük, töltsd le manuálisan a legújabb verziót.\",\n  \"ui.warning.update_cooldown_minutes\": \"Frissítéseket csak 5 percenként lehet kérni.\\n\\nKérjük, próbáld meg újra {0} perc múlva.\",\n  \"ui.warning.update_cooldown_seconds\": \"Frissítéseket csak 5 percenként lehet kérni.\\n\\nKérjük, próbáld meg újra {0} másodperc múlva.\",\n  \"ui.warning.unable_to_locate_log\": \"Nem sikerült megtalálni a SMAPI-latest.txt fájlt! A SMAPI-nak legalább egyszer sikeresen kell futnia ahhoz, hogy a Stardrop felismerje a játék részleteit.\",\n  \"ui.warning.unable_to_read_log\": \"Nem sikerült beolvasni a SMAPI naplófájt a játék verziójának lekérdezéséhez.\\n\\nA mod frissítések ellenőrzése nem lehetséges.\",\n  \"ui.warning.no_manifest\": \"Nem található manifest.json fájl a \\\"{0}\\\" modban.\",\n  \"ui.warning.unable_to_load_mod\": \"Nem sikerült betölteni a következő \\\"{0}\\\" címen található fájlt.\\n\\nTovábbi információkért lásd a naplófájlt.\",\n  \"ui.warning.unable_to_validate_nexus_key\": \"Nem sikerült érvényesíteni a Nexus mod API kulcsot.\\n\\nKérjük, ellenőrizd, hogy a kulcs helyes-e, és próbáld meg újra.\",\n  \"ui.warning.download_without_premium\": \"A Stardropon keresztül történő letöltéshez prémium Nexus mods tagsággal kell rendelkezned.\",\n  \"ui.warning.no_downloads_available\": \"Jelenleg nem állnak rendelkezésre frissítéseket tartalmazó modok a Nexus mods oldalon.\",\n  \"ui.warning.failed_nexus_install\": \"Nem sikerült telepíteni a(z) {0} modot a Nexus modssal való kapcsolódási probléma miatt.\\n\\nKéjük, próbáld meg újra.\",\n  \"ui.warning.unable_nexus_install\": \"Nem sikerült telepíteni a(z) {0} modot.\",\n  \"ui.warning.unable_to_endorse\": \"Nem sikerült beállítani a jóváhagyási állapotot:\\n\\n{0}\",\n  \"ui.warning.mod_owned\": \"Te vagy a mod tulajdonosa.\",\n  \"ui.warning.too_soon_after_download\": \"A mod letöltése után 15 percet kell várnod.\\n\\nA jóváhagyás megkísérlése visszaállítja ezt az időzítőt.\",\n  \"ui.warning.not_downloaded\": \"Ezt a modot le kell töltened a Nexus mods oldaláról, hogy támogatni tudd.\",\n  \"ui.warning.failed_to_get_mod_details\": \"Nem sikerült lekérni a(z) {0} mod adatait az NXM linkről.\",\n  \"ui.warning.failed_to_get_download_link\": \"Nem sikerült megkapni a letöltési url-t a Nexus modstól az NXM linken keresztül: {0}\",\n  \"ui.warning.failed_to_set_association\": \"Nem sikerült regisztrálni az NXM protokollt a Stardropban.\\n\\nRészletekért lásd a naplót.\",\n  \"ui.warning.already_associated\": \"A Stardrop már kapcsolódik az NXM protokollhoz.\",\n\n  // Üzenetek\n  \"ui.message.confirm_mod_deletion\": \"Biztos, hogy törölni szeretnéd a(z) {0}t? A folyamat nem vonható vissza.\",\n  \"ui.message.unsaved_config_changes\": \"A(z) {0} profilhoz nem mentett mod konfiguráció változtatások észlelve.\\n\\nSzeretnéd elmenteni a változtatásokat?\",\n  \"ui.message.stardrop_update_available\": \"Elérhető a Stardrop (v{0}) frissítése.\\n\\nSzeretnéd most letölteni?\",\n  \"ui.message.SMAPI_update_available\": \"Elérhető a SMAPI (v{0}) frissítése.\\n\\nSzeretnéd most letölteni?\",\n  \"ui.message.stardrop_update_complete\": \"A Stardrop sikeresen frissült.\\n\\nMeg szeretnéd tekinteni a kiadási megjegyzéseket?\",\n  \"ui.message.SMAPI_update_complete\": \"A SMAPI sikeresen frissült.\\n\\nMeg szeretnéd tekinteni a kiadási megjegyzéseket?\",\n  \"ui.message.confirm_bulk_change_mod_states_enable\": \"Az összes modot engedélyezed?\\n\\nMegjegyzés: Ezt a folyamatot nem lehet visszavonni.\",\n  \"ui.message.confirm_bulk_change_mod_states_disable\": \"Az összes modot letiltod?\\n\\nMegjegyzés: Ezt a folyamatot nem lehet visszavonni.\",\n  \"ui.message.confirm_mod_update_method\": \"A(z) {0} korábbi verziója észlelve. Szeretnéd törölni a korábbi telepítést?\\n\\nMegjegyzés: A korábbi verziók törlése általában ajánlott, azonban a konfigurációs fájlok elvesznek.\",\n  \"ui.message.confirm_mod_update_method_no_config\": \"A(z) {0} korábbi verziója észlelve. Szeretnéd törölni az előző telepítést?\\n\\nMegjegyzés: A korábbi verziók törlése általában ajánlott.\",\n  \"ui.message.confirm_mod_update_method_preserved\": \"A(z) {0} korábbi verziója észlelve. Szeretnéd törölni az előző telepítést?\\n\\nMegjegyzés: A korábbi verziók törlése általában ajánlott, és a konfigurációs fájlok visszaállnak.\",\n  \"ui.message.confirm_nxm_association\": \"Szeretnéd az NXM protokollt a Stardrophoz társítani?\\n\\nEz lehetővé teszi a Stardrop számára, hogy automatikusan letöltse és telepítse a Nexus modsról a modokat.\",\n  \"ui.message.confirm_nxm_install\": \"Szeretnéd telepíteni a következő modot:\\n\\n{0}\\n\\nEzt a megerősítést a beállítások menüben letilthatod.\",\n  \"ui.message.require_nexus_login\": \"Ehhez a művelethez be kell jelentkezned a Nexus mods API-val.\",\n  \"ui.message.succeeded_nexus_install\": \"A Nexus mods segítségével sikeresen telepítve a következő mod:\\n\\n{0}\",\n\n  // Ablak nevek\n  \"ui.window.settings.name\": \"Beállítások\",\n  \"ui.window.profiles.name\": \"Profilok\",\n  \"ui.window.profile_naming.name\": \"Profil név\",\n  \"ui.window.nexus_login.name\": \"Nexus mods személyes API kulcs\",\n  \"ui.window.nexus_login_actual.name\": \"Nexus mods API kulcs\",\n  \"ui.window.nexus_user.name\": \"Nexus mods felhasználói adatok\",\n\n  // Belső részek\n  \"internal.connected\": \"Kapcsolódva\",\n  \"internal.disconnected\": \"Lekapcsolódva\",\n  \"internal.unknown\": \"Ismeretlen\",\n  \"internal.enable\": \"Engedélyezés\",\n  \"internal.disable\": \"Letiltás\",\n  \"internal.enable_whole_mod\": \"Teljes mod csoport engedélyezése\",\n  \"internal.disable_whole_mod\": \"Teljes mod csoport letiltása\",\n  \"internal.yes\": \"Igen\",\n  \"internal.yes_all\": \"Igen mindenre\",\n  \"internal.no\": \"Nem\",\n  \"internal.no_all\": \"Nem mindenre\",\n  \"internal.ok\": \"OKÉ\",\n  \"internal.unlock\": \"Feloldás\",\n\n  // Belső részek - Mérések\n  \"internal.measurements.bytes_size\": \"B\",\n  \"internal.measurements.bytes_per_second\": \"B/s\",\n  \"internal.measurements.kilobytes_size\": \"KB\",\n  \"internal.measurements.kilobytes_per_second\": \"KB/s\",\n  \"internal.measurements.megabytes_size\": \"MB\",\n  \"internal.measurements.megabytes_per_second\": \"MB/s\"\n}\n"
  },
  {
    "path": "Stardrop/i18n/it.json",
    "content": "{\n  // Main Window - Menu Items\n  \"ui.main_window.menu_items.add_mod\": \"Aggiungi Mod\",\n  \"ui.main_window.menu_items.add_mods\": \"Aggiungi Mod\",\n  \"ui.main_window.menu_items.start_smapi\": \"Avvia SMAPI\",\n  \"ui.main_window.menu_items.exit\": \"Esci\",\n  \"ui.main_window.menu_items.settings\": \"Impostazioni\",\n  \"ui.main_window.menu_items.stardrop_log_file\": \"File Log di Stardrop\",\n  \"ui.main_window.menu_items.smapi_log_file\": \"File Log di SMAPI\",\n  \"ui.main_window.menu_items.refresh_mod_list\": \"Aggiorna l'Elenco Mod\",\n  \"ui.main_window.menu_items.check_for_mod_updates\": \"Controlla Aggiornamenti Mod\",\n  \"ui.main_window.menu_items.enable_all_mods\": \"Abilita Tutte le Mod\",\n  \"ui.main_window.menu_items.disable_all_mods\": \"Dsabilita Tutte le Mod\",\n  \"ui.main_window.menu_items.check_for_stardrop_update\": \"Controlla Aggiornamenti Stardrop\",\n  \"ui.main_window.menu_items.stardrop_repository\": \"Archivio di Stardrop\",\n  \"ui.main_window.menu_items.nexus_install_all\": \"Installa Tutti gli Aggiornamenti\",\n  \"ui.main_window.menu_items.nexus_install_enabled\": \"Installa gli Aggiornamenti per le Mod Abilitate\",\n  \"ui.main_window.menu_items.nexus_connection\": \"Connessione API\",\n  \"ui.main_window.menu_items.check_for_SMAPI_update\": \"Controlla Aggiornamenti SMAPI\",\n\n  // Main Window - Menu Items - Context\n  \"ui.main_window.menu_items.context.columns.show_hide\": \"Mostra / Nascondi Colonne\",\n  \"ui.main_window.menu_items.context.hide_requirements\": \"Nascondi Requisiti\",\n  \"ui.main_window.menu_items.context.cancel\": \"Cancella\",\n  \"ui.main_window.menu_items.context.open_containing_folder\": \"Apri Cartella\",\n  \"ui.main_window.menu_items.context.open_mod_page\": \"Apri Pagina Mod\",\n  \"ui.main_window.menu_items.context.open_config\": \"Apri File Config\",\n  \"ui.main_window.menu_items.context.open_manifest\": \"Apri File Manifest\",\n  \"ui.main_window.menu_items.context.show_whole_mod_group\": \"Mostra l'Intero Gruppo Mod\",\n  \"ui.main_window.menu_items.context.show_authors_mods\": \"Mostra Autore Mod\",\n  \"ui.main_window.menu_items.context.open\": \"Apri...\",\n  \"ui.main_window.menu_items.context.filters\": \"Filtri\",\n  \"ui.main_window.menu_items.context.options\": \"Opzioni\",\n  \"ui.main_window.menu_items.context.clear_filter\": \"Pulisci\",\n  \"ui.main_window.menu_items.context.delete\": \"Cancella\",\n\n  // Main Window - Menu Headers\n  \"ui.main_window.menu_headers.file\": \"File\",\n  \"ui.main_window.menu_headers.view\": \"Vista\",\n  \"ui.main_window.menu_headers.tools\": \"Strumenti\",\n  \"ui.main_window.menu_headers.nexus\": \"Nexus Mods\",\n\n  \"ui.main_window.menu_headers.enabled\": \"Abilita\",\n  \"ui.main_window.menu_headers.mod_name\": \"Nome Mod\",\n  \"ui.main_window.menu_headers.unique_id\": \"ID Univoco\",\n  \"ui.main_window.menu_headers.author\": \"Autore\",\n  \"ui.main_window.menu_headers.requirements\": \"Requisiti\",\n  \"ui.main_window.menu_headers.missing_requirements\": \"Requisiti Mancanti\",\n  \"ui.main_window.menu_headers.version\": \"Versione\",\n  \"ui.main_window.menu_headers.configuration\": \"Configurazione\",\n  \"ui.main_window.menu_headers.status\": \"Stato\",\n  \"ui.main_window.menu_headers.endorsement\": \"Approvazione\",\n  \"ui.main_window.menu_headers.nexus_install\": \"Aggiornamenti Nexus Mods\",\n  \"ui.main_window.menu_headers.install_timestamp\": \"Data Installazione\",\n  \"ui.main_window.menu_headers.last_updated\": \"Ultimo Aggiornamento\",\n\n  // Main Window - Labels\n  \"ui.main_window.labels.profile\": \"Profilo\",\n  \"ui.main_window.labels.enabled_mods\": \"Mod Abilitate: \",\n  \"ui.main_window.labels.total_mods\": \"Mod Totali: \",\n  \"ui.main_window.labels.updatable_mods\": \"Mod da Aggiornare: \",\n  \"ui.main_window.labels.filter\": \"Filtri\",\n  \"ui.main_window.labels.unknown_SMAPI\": \"Versione SMAPI sconosiuta\",\n\n  // Main Window - Hyperlinks\n  \"ui.main_window.hyperlinks.click_to_open\": \"Clicca per aprire\",\n  \"ui.main_window.hyperlinks.update_available\": \"Aggiornamento Disponibile ({0})\",\n  \"ui.main_window.hyperlinks.unofficial_update_available\": \"Aggiornamento non Ufficiale Disponibile ({0})\",\n  \"ui.main_window.hyperlinks.broken_compatibility_issue\": \"[Broken] Problemi di Compatibilità\",\n  \"ui.main_window.hyperlinks.install_update\": \"Installa Aggiornamento\",\n  \"ui.main_window.hyperlinks.downloading\": \"Scaricando...\",\n  \"ui.main_window.hyperlinks.installing\": \"Installando...\",\n\n  // Main Window - Buttons\n  \"ui.main_window.buttons.save_configs\": \"Salva Configurazione\",\n  \"ui.main_window.buttons.save_profile\": \"Salva Profilo\",\n  \"ui.main_window.buttons.show_updatable_mods\": \"Mostra Mod Con Aggiornamenti Disponibili\",\n  \"ui.main_window.buttons.active_search_filters\": \"{0} Filtri Attivi\",\n  \"ui.main_window.buttons.no_search_filters\": \"Nessun Filtro Attivo\",\n  \"ui.main_window.buttons.mod_groups_state.expand\": \"Espandi Gruppi Mod\",\n  \"ui.main_window.buttons.mod_groups_state.collapse\": \"Collassa Gruppi Mod\",\n\n  // Main Window - Buttons - Update Status\n  \"ui.main_window.button.update_status.generic\": \"Clicca per Controllare Aggiornamenti Mod\",\n  \"ui.main_window.button.update_status.updating\": \"In Aggiornamento...\",\n  \"ui.main_window.button.update_status.list_available_updates\": \"Mod da Aggiornare: {0}\",\n  \"ui.main_window.button.update_status.failed\": \"Controllo Aggiornamento Mod Fallito\",\n\n  // Main Window - Buttons - Downloads\n  \"ui.main_window.buttons.downloads.label\": \"Downloads ({0})\",\n\n  // Main Window - ComboBox\n  \"ui.main_window.combobox.show_all_mods\": \"Mostra Tutte le Mod\",\n  \"ui.main_window.combobox.show_mods_with_configs\": \"Mostra Mod Configurabili\",\n  \"ui.main_window.combobox.show_disabled_mods\": \"Mostra Mod Disabilitate\",\n  \"ui.main_window.combobox.show_enabled_mods\": \"Mostra Mod Abilitate\",\n\n  // Main Window - ComboBox (ListBox)\n  \"ui.main_window.combobox.mod_name\": \"Nome Mod\",\n  \"ui.main_window.combobox.author\": \"Autore\",\n  \"ui.main_window.combobox.requirements\": \"Requisiti\",\n  \"ui.main_window.combobox.group\": \"Gruppo Mod\",\n  \"ui.main_window.combobox.top_level_group\": \"Cartella Principale\",\n\n  // Settings Window - Labels\n  \"ui.settings_window.labels.smapi_path\": \"Percorso SMAPI\",\n  \"ui.settings_window.labels.mod_path\": \"Percorso Cartella Mod\",\n  \"ui.settings_window.labels.mod_install\": \"Percorso Mod Installate da Stardrop\",\n  \"ui.settings_window.labels.themes\": \"Temi\",\n  \"ui.settings_window.labels.languages\": \"Lingua\",\n  \"ui.settings_window.labels.grouping\": \"Raggruppamento Mod\",\n  \"ui.settings_window.labels.miscellaneous\": \"Varie\",\n  \"ui.settings_window.labels.nexus\": \"Nexus Mods\",\n  \"ui.settings_window.labels.preferred_server\": \"Server Preferito:\",\n\n  // Settings Window - Buttons\n  \"ui.settings_window.buttons.ignore_hidden_folders\": \"Ignora Cartelle Nascoste\",\n  \"ui.settings_window.buttons.enable_profile_specific_mod_configs\": \"Abilita Configurazioni Mod Uniche per Profilo\",\n  \"ui.settings_window.buttons.enable_mods_on_add\": \"Abilita Automaticamente le Mod Quando Vengono Installate\",\n  \"ui.settings_window.buttons.always_ask_to_delete\": \"Chiedi Sempre se Eliminare i File Quando Si Aggiorna una Mod\",\n  \"ui.settings_window.buttons.automatically_save_profile_changes\": \"Salva Automaticamente i Cambiamenti dei Profili\",\n  \"ui.settings_window.buttons.show_mod_thumbnails\": \"Mostra le Miniature delle Mod\",\n  \"ui.settings_window.buttons.always_ask_for_NXM_installs\": \"Chiedi Sempre Prima di Installare File NXM\",\n  \"ui.settings_window.buttons.register_nxm_association\": \"Registra Associazione NXM\",\n\n  // Settings Window - Tooltips\n  \"ui.settings_window.tooltips.smapi\": \"Il percorso del file StardewModdingAPI\",\n  \"ui.settings_window.tooltips.mod_folder_path\": \"Il percorso della cartella Mod\",\n  \"ui.settings_window.tooltips.mod_install_path\": \"Il percorso della cartella in cui Stardrop metterà le mod installate. Deve essere contenuta nella cartella indicata in \\\"Percorso Cartella Mod\\\".\",\n  \"ui.settings_window.tooltips.theme\": \"Il tema corrente di Stardrop\",\n  \"ui.settings_window.tooltips.language\": \"La lingua corrente di Stardrop\",\n  \"ui.settings_window.tooltips.grouping\": \"Il modo corrente di raggruppamento delle mod.\",\n  \"ui.settings_window.tooltips.ignore_hidden_folders\": \"Se selezionato, Stardrop ignorerà ogni mod contenuta in cartelle che iniziano con \\\".\\\"\",\n  \"ui.settings_window.tooltips.nxm_file_association\": \"Registra il protocollo NXM da associare a Stardrop, consentendo installazioni dirette da www.nexusmods.com\",\n  \"ui.settings_window.tooltips.always_ask_nxm_files\": \"Se selezionato, Stardrop chiederà sempre prima di installare una mod da www.nexusmods.com tramite NXM\",\n  \"ui.settings_window.tooltips.enable_profile_specific_configs\": \"Se selezionato, Stardrop salverà e ripristinerà i file config.json di ogni mod quando si cambia profilo\",\n  \"ui.settings_window.tooltips.enable_mods_on_add\": \"Se selezionato, Stardrop abiliterà automaticamente le mod appena aggiunte o aggiornate\",\n  \"ui.settings_window.tooltips.always_ask_to_delete\": \"Se selezionato, Stardrop chiederà sempre se eliminare i file durante l'aggiornamento delle mod\",\n  \"ui.settings_window.tooltips.automatically_save_profile_changes\": \"Se selezionato, Stardrop salverà automaticamente le modifiche apportate al profilo\",\n  \"ui.settings_window.tooltips.show_mod_thumbnails\": \"Se selezionato, mostra la miniatura della mod da Nexus Mods (ove applicabile).\\n\\nRichiede una chiave API Stardrop da Nexus Mods.\",\n  \"ui.settings_window.tooltips.preferred_server\": \"Imposta il server preferito da utilizzare durante il download da Nexus Mods\",\n  \"ui.settings_window.tooltips.save_changes\": \"Salva i Cambiamenti\",\n  \"ui.settings_window.tooltips.cancel_changes\": \"Cancella\",\n\n  // Nexus Login Window - Buttons\n  \"ui.nexus_login.buttons.nexus\": \"Vai a Nexus Mods\",\n  \"ui.nexus_login.buttons.disconnect\": \"Disconnetti Da Nexus\",\n\n  // Nexus Login Window - Labels\n  \"ui.nexus_login.labels.note\": \"Prendere Nota\",\n  \"ui.nexus_login.labels.get_key\": \"Ottieni la tua chiave API personale da Nexus Mods\",\n  \"ui.nexus_login.labels.get_key_actual\": \"Ottieni la tua chiave API di Stardrop da Nexus Mods\",\n  \"ui.nexus_login.labels.paste\": \"Quindi incolla la chiave qui sotto\",\n  \"ui.nexus_login.labels.share_warning\": \"Non condividere la tua chiave API personale con altri, potresti incorrere in azioni legali nei confronti del tuo account Nexus.\",\n  \"ui.nexus_login.labels.share_warning_actual\": \"Non condividere la tua chiave API con altri, altrimenti potresti incorrere in azioni contro il tuo account Nexus.\",\n  \"ui.nexus_login.labels.revoke\": \"Puoi reimpostare/revocare la tua chiave in qualsiasi momento su Nexus Mods andando su Settings > API Keys > Personal API Key.\",\n  \"ui.nexus_login.labels.revoke_actual\": \"Puoi reimpostare/revocare la tua chiave in qualsiasi momento su Nexus Mods andando su Settings > API Keys > Stardrop.\",\n  \"ui.nexus_login.labels.nexus_details\": \"Dettagli Utente Nexus Mods\",\n  \"ui.nexus_login.labels.nexus_disconnect\": \"Clicca il pulsante sotto per far dimenticare a Stardrop la tua chiave personale e disconnetterti da Nexus Mods.\",\n  \"ui.nexus_login.labels.nexus_disconnect_actual\": \"Clicca il pulsante sotto per far dimenticare a Stardrop la tua chiave API di Stardrop e disconnetterti da Nexus Mods.\",\n  \"ui.nexus_login.labels.username\": \"Nome Utente: {0}\",\n  \"ui.nexus_login.labels.is_premium\": \"È un Utente Premium: {0}\",\n\n  // Downloads Panel - Labels\n  \"ui.downloads_panel.download_canceled\": \"Cancellato\",\n  \"ui.downloads_panel.download_failed\": \"Fallito\",\n  \"ui_downloads_panel.download_success\": \"Completato!\",\n  \"ui.downloads_panel.no_downloads_label\": \"Nessun download in corso\",\n\n  // Downloads Panel - Tooltips\n  \"ui.downloads_panel.tooltips.cancel_button\": \"Cancella\",\n  \"ui.downloads_panel.tooltips.remove_button\": \"Elimina\", // Tooltip for the button that clears out a completed or failed download\n\n  // Warnings\n  \"ui.warning.unable_to_add_profile\": \"Impossibile aggiungere {0}, esiste già un profilo con lo stesso nome!\",\n  \"ui.warning.given_mod_folder_does_not_exist\": \"Il percorso mod specificato non esiste.\\n\\nRipristino del percorso precedente.\",\n  \"ui.warning.given_install_folder_not_exist\": \"Il percorso specificato per le mod installate da Stardrop non esiste.\\n\\nRipristino del percorso precedente.\",\n  \"ui.warning.given_install_folder_not_exist_default\": \"Il percorso specificato per le mod installate da Stardrop non esiste.\\n\\nRipristino del percorso predfinito.\",\n  \"ui.warning.given_install_folder_not_under_mod_folder\": \"Il percorso specificato per le mod installate da Stardrop non esiste in {0}\\n\\nRipristino del percorso precedente.\",\n  \"ui.warning.given_invalid_smapi_executable\": \"Il file specificato non è {0}\\n\\nRipristino del percorso precedente.\",\n  \"ui.warning.stardrop_locked\": \"Stardrop è bloccato mentre SMAPI è avviato. Qualsiasi cambiamento non avrà effetto finché SMAPI non viene chiuso.\",\n  \"ui.warning.stardrop_downloading\": \"Ultima versione di Stardrop in download.\",\n  \"ui.warning.SMAPI_downloading\": \"Unltima versione di SMAPI in donwload da GitHub.\",\n  \"ui.warning.SMAPI_installing\": \"SMAPI si sta aggiornando alll'ultima versione.\",\n  \"ui.warning.SMAPI_up_to_date\": \"SMAPI è aggiornato.\\n\\n{0} è l'ultima versione.\",\n  \"ui.warning.install_mod_attempt_count\": \"Tentativo di installare {0} mod\",\n  \"ui.warning.mod_installing\": \"Installando {0}\",\n  \"ui.warning.mod_updating\": \"Aggiornando {0}\",\n  \"ui.warning.mod_deleting\": \"Cancellando la vecchia versione di {0}\",\n  \"ui.warning.failed_to_delete\": \"Impossibile eliminare {0}.\\n\\nI file della mod potrebbero essere bloccati, vedere il file log per dettagli.\",\n  \"ui.warning.failed_to_delete_during_update\": \"Impossibile eliminare la vecchia versione di {0}.\\n\\nLa mod potrebbe non essere installata correttamente.\",\n  \"ui.warning.unable_to_locate_smapi\": \"Impossibile trovare StardewModdingAPI.exe\\n\\nPer favore specifica il percorso corretto in\\nVista > Impostazioni\",\n  \"ui.warning.mod_config_saved_but_not_enabled\": \"Le impostazioni di configurazione della mod sono state salvate per il profilo {0}, tuttavia la configurazione specifica per profilo non è abilitata.\\n\\nVai in Vista > Impostazioni e seleziona \\\"Abilita Configurazioni Mod Uniche per Profilo\\\" per permettere a Stardrop di salvare e applicare automaticamente i cambiamenti alle configurazioni.\",\n  \"ui.warning.unable_to_determine_profile\": \"Impossibile determinare il profilo selezionato.\\n\\nSMAPI non verrà avviato.\",\n  \"ui.warning.stardrop_up_to_date\": \"Stardrop è aggiornato.\\n\\n{0} è l'ultima versione.\",\n  \"ui.warning.stardrop_unable_to_download_latest\": \"Impossibile scaricare l'ultima versione di Stardrop.\\n\\nScarica l'ultima versione manualmente.\",\n  \"ui.warning.stardrop_unable_to_find_latest\": \"Impossibile controllare la Repository Github di Stardrop per gli aggiornamenti.\\n\\nControlla GitHub per un possibile aggiornamento o riprova più tardi.\",\n  \"ui.warning.stardrop_update_downloaded\": \"Downloaded dell'ultima versione riuscito.\\n\\nStardrop verrà riavviato.\",\n  \"ui.warning.SMAPI_unable_to_download_latest\": \"Impossibile scaricare l'ultima versione di SMAPI.\\n\\nScarica l'ultima versione manualmente.\",\n  \"ui.warning.SMAPI_unable_to_install_latest\": \"Impossibile installare l'ultima versione di SMAPI.\\n\\nScarica l'ultima versione manualmente.\",\n  \"ui.warning.update_cooldown_minutes\": \"Puoi controllare gli aggiornamenti una volta ogni 5 minuti.\\n\\nRiprova tra {0} minuto(i).\",\n  \"ui.warning.update_cooldown_seconds\": \"Puoi controllare gli aggiornamenti una volta ogni 5 minuti.\\n\\nRiprova tra {0} secondo(i).\",\n  \"ui.warning.unable_to_locate_log\": \"Impossibile trovare il file SMAPI-latest.txt! SMAPI deve essere avviato con successo almeno una volta per permettere a Stardrop di rilevare i dettagli del gioco.\",\n  \"ui.warning.unable_to_read_log\": \"Impossibile leggere il file log di SMAPI e controllare la versione di gioco.\\n\\nNon è possibile controllare gli aggiornamenti delle mod.\",\n  \"ui.warning.no_manifest\": \"Nessuno file manifest.json trovato in \\\"{0}\\\"\",\n  \"ui.warning.unable_to_load_mod\": \"Impossibile caricare il file in \\\"{0}\\\".\\n\\nVedere il file log per maggiori informazioni.\",\n  \"ui.warning.unable_to_validate_nexus_key\": \"Impossibile convalidare l'API key di Nexus Mods.\\n\\nVerifica che l'API key sia corretta e riprova.\",\n  \"ui.warning.download_without_premium\": \"Devi avere un account premium di Nexus Mods per scaricare direttamente da Stardrop.\",\n  \"ui.warning.no_downloads_available\": \"Al momento non ci sono mod con aggiornamenti disponibili da Nexus Mods.\",\n  \"ui.warning.failed_nexus_install\": \"Impossibile installare la mod {0} a causa di un problema di connessione con Nexus Mods.\\n\\nRiprova.\",\n  \"ui.warning.unable_nexus_install\": \"Impossibile installare la mod {0}.\",\n  \"ui.warning.unable_to_endorse\": \"Impossibile impostare lo stato di approvazione:\\n\\n{0}\",\n  \"ui.warning.mod_owned\": \"Sei il proprietario di questa mod.\",\n  \"ui.warning.too_soon_after_download\": \"Devi aspettare almeno 15 minuti dopo aver scaricato questa mod.\\n\\nTentare di approvarla farà ricominciare il timer.\",\n  \"ui.warning.not_downloaded\": \"Devi scaricare questa mod da Nexus Mods per poterla approvare.\",\n  \"ui.warning.failed_to_get_mod_details\": \"Impossibile recuperare i dettagli della mod dal link NXM: {0}\",\n  \"ui.warning.failed_to_get_download_link\": \"Impossibile recuperare l'url del download da Nexus Mods tramite il link NXM: {0}\",\n  \"ui.warning.failed_to_set_association\": \"Impossibile registrare il protocollo NXM con Stardrop.\\n\\nVedi il file log per dettagli.\",\n  \"ui.warning.already_associated\": \"Stardrop è già associato al protocollo NXM.\",\n\n  // Messages\n  \"ui.message.confirm_mod_deletion\": \"Sei sicuro di voler eliminare {0}? Questa azione non può essere annullata.\",\n  \"ui.message.unsaved_config_changes\": \"Rilevati cambiamenti non salvati per le configurazioni delle mod per il profilo {0}.\\n\\nVuoi salvare i cambiamenti?\",\n  \"ui.message.stardrop_update_available\": \"Un aggiornamento (v{0}) è disponibile per Stardrop.\\n\\nVuoi scaricarlo ora?\",\n  \"ui.message.SMAPI_update_available\": \"Un aggiornamento (v{0}) è disponibile per SMAPI.\\n\\nVuoi scaricarlo ora?\",\n  \"ui.message.stardrop_update_complete\": \"Stardrop è stato aggiornato con successo.\\n\\nVuoi vedere le note di rilascio?\",\n  \"ui.message.SMAPI_update_complete\": \"SMAPI è stato aggiornato con successo.\\n\\nVuoi vedere le note di rilascio?\",\n  \"ui.message.confirm_bulk_change_mod_states_enable\": \"Abilitare tutte le mod?\\n\\nNota: Questa azione non può essere annullata.\",\n  \"ui.message.confirm_bulk_change_mod_states_disable\": \"Disabilitare tutte le mod?\\n\\nNota: Questa azione non può essere annullata.\",\n  \"ui.message.confirm_mod_update_method\": \"È stata rilevata una versione esistente di {0}. Vuoi ripulare i file della precedente installazione?\\n\\nNote: Pulire i file della precedente installazione è di solito raccomandato, tuttavia i file congif verranno persi.\",\n  \"ui.message.confirm_mod_update_method_no_config\": \"È stata rilevata una versione esistente di {0}. Vuoi ripulare i file della precedente installazione?\\n\\nNote: Pulire i file della precedente installazione è di solito raccomandato.\",\n  \"ui.message.confirm_mod_update_method_preserved\": \"È stata rilevata una versione esistente di {0}. Vuoi ripulare i file della precedente installazione?\\n\\nNote: Pulire i file della precedente installazione è di solito raccomandato e i tuoi file config verranno ripristinati.\",\n  \"ui.message.confirm_nxm_association\": \"Vuoi associare il protocollo NXM a Stardrop?\\n\\nQuesto permetterà a Stardrop di installare automaticamente le mod scaricate da Nexus Mods.\",\n  \"ui.message.confirm_nxm_install\": \"Vuoi installare la seguente mod:\\n\\n{0}\\n\\nPuoi disattivare questa conferma in impostazioni.\",\n  \"ui.message.require_nexus_login\": \"Questa azione richiede l'accesso all'API di Nexus Mods.\",\n  \"ui.message.succeeded_nexus_install\": \"La seguente mod è stata installata correttamente tramite Nexus Mods:\\n\\n{0}\",\n  \"ui.message.confirm_mod_update_caution\": \"{0}\\nMessaggio di Attenzione Aggiornamento:\\n\\n{1}\\n\\nProcedere con l'aggiornamento della mod?\",\n\n  // Window Names\n  \"ui.window.settings.name\": \"Impostazioni\",\n  \"ui.window.profiles.name\": \"Profili\",\n  \"ui.window.profile_naming.name\": \"Nome Profilo\",\n  \"ui.window.nexus_login.name\": \"Chiave API Personale di Nexus Mods\",\n  \"ui.window.nexus_login_actual.name\": \"Chiave API di Nexus Mods\",\n  \"ui.window.nexus_user.name\": \"Dettagli Utente Nexus Mods\",\n\n  // Internals\n  \"internal.connected\": \"Connesso\",\n  \"internal.disconnected\": \"Disconnesso\",\n  \"internal.unknown\": \"Sconosciuto\",\n  \"internal.enable\": \"Abilitato\",\n  \"internal.disable\": \"Disabilitato\",\n  \"internal.enable_whole_mod\": \"Abilita Intero Gruppo Mod\",\n  \"internal.disable_whole_mod\": \"Disabilita Intero Gruppo Mod\",\n  \"internal.yes\": \"Si\",\n  \"internal.yes_all\": \"Si a Tutto\",\n  \"internal.no\": \"No\",\n  \"internal.no_all\": \"No a Tutto\",\n  \"internal.ok\": \"OK\",\n  \"internal.unlock\": \"Sblocca\",\n\n  // Internals - Measurements\n  \"internal.measurements.bytes_size\": \"B\",\n  \"internal.measurements.bytes_per_second\": \"B/s\",\n  \"internal.measurements.kilobytes_size\": \"KB\",\n  \"internal.measurements.kilobytes_per_second\": \"KB/s\",\n  \"internal.measurements.megabytes_size\": \"MB\",\n  \"internal.measurements.megabytes_per_second\": \"MB/s\"\n}\n"
  },
  {
    "path": "Stardrop/i18n/ja.json",
    "content": "{\n  // Main Window - Menu Items\n  \"ui.main_window.menu_items.add_mod\": \"Modを追加\",\n  \"ui.main_window.menu_items.add_mods\": \"Modを追加\",\n  \"ui.main_window.menu_items.start_smapi\": \"SMAPIを起動\",\n  \"ui.main_window.menu_items.exit\": \"終了\",\n  \"ui.main_window.menu_items.settings\": \"設定\",\n  \"ui.main_window.menu_items.stardrop_log_file\": \"Stardropログファイル\",\n  \"ui.main_window.menu_items.smapi_log_file\": \"SMAPIログファイル\",\n  \"ui.main_window.menu_items.refresh_mod_list\": \"Mod一覧を更新\",\n  \"ui.main_window.menu_items.check_for_mod_updates\": \"Mod更新を確認\",\n  \"ui.main_window.menu_items.enable_all_mods\": \"すべてのModを有効化\",\n  \"ui.main_window.menu_items.disable_all_mods\": \"すべてのModを無効化\",\n  \"ui.main_window.menu_items.check_for_stardrop_update\": \"Stardrop更新を確認\",\n  \"ui.main_window.menu_items.stardrop_repository\": \"Stardropリポジトリ\",\n  \"ui.main_window.menu_items.nexus_install_all\": \"更新をすべてインストール\",\n  \"ui.main_window.menu_items.nexus_connection\": \"API接続\",\n  \"ui.main_window.menu_items.check_for_SMAPI_update\": \"SMAPI更新を確認\",\n\n  // Main Window - Menu Items - Context\n  \"ui.main_window.menu_items.context.columns.show_hide\": \"列の表示／非表示\",\n  \"ui.main_window.menu_items.context.hide_requirements\": \"必要要件を非表示\",\n  \"ui.main_window.menu_items.context.cancel\": \"キャンセル\",\n  \"ui.main_window.menu_items.context.open_containing_folder\": \"フォルダを開く\",\n  \"ui.main_window.menu_items.context.open_mod_page\": \"Modページを開く\",\n  \"ui.main_window.menu_items.context.open_config\": \"Configファイルを開く\",\n  \"ui.main_window.menu_items.context.open_manifest\": \"manifestファイルを開く\",\n  \"ui.main_window.menu_items.context.show_whole_mod_group\": \"Modグループ全体を表示\",\n  \"ui.main_window.menu_items.context.show_authors_mods\": \"作者のModを表示\",\n  \"ui.main_window.menu_items.context.open\": \"開く...\",\n  \"ui.main_window.menu_items.context.filters\": \"フィルター\",\n  \"ui.main_window.menu_items.context.options\": \"オプション\",\n  \"ui.main_window.menu_items.context.clear_filter\": \"クリア\",\n  \"ui.main_window.menu_items.context.delete\": \"削除\",\n\n  // Main Window - Menu Headers\n  \"ui.main_window.menu_headers.file\": \"ファイル\",\n  \"ui.main_window.menu_headers.view\": \"表示\",\n  \"ui.main_window.menu_headers.tools\": \"ツール\",\n  \"ui.main_window.menu_headers.nexus\": \"Nexus Mods\",\n\n  \"ui.main_window.menu_headers.enabled\": \"有効\",\n  \"ui.main_window.menu_headers.mod_name\": \"Mod名\",\n  \"ui.main_window.menu_headers.unique_id\": \"ユニークID\",\n  \"ui.main_window.menu_headers.author\": \"作者\",\n  \"ui.main_window.menu_headers.requirements\": \"必要要件\",\n  \"ui.main_window.menu_headers.missing_requirements\": \"不足している必要要件\",\n  \"ui.main_window.menu_headers.version\": \"バージョン\",\n  \"ui.main_window.menu_headers.configuration\": \"設定\",\n  \"ui.main_window.menu_headers.status\": \"状態\",\n  \"ui.main_window.menu_headers.endorsement\": \"Endorse（推奨）\",\n  \"ui.main_window.menu_headers.nexus_install\": \"Nexus Mods更新\",\n  \"ui.main_window.menu_headers.install_timestamp\": \"インストール日時\",\n  \"ui.main_window.menu_headers.last_updated\": \"最終更新\",\n\n  // Main Window - Labels\n  \"ui.main_window.labels.profile\": \"プロファイル\",\n  \"ui.main_window.labels.enabled_mods\": \"有効なMod: \",\n  \"ui.main_window.labels.total_mods\": \"Mod総数: \",\n  \"ui.main_window.labels.updatable_mods\": \"更新可能なMod: \",\n  \"ui.main_window.labels.filter\": \"フィルター\",\n  \"ui.main_window.labels.unknown_SMAPI\": \"不明なSMAPIバージョン\",\n\n  // Main Window - Hyperlinks\n  \"ui.main_window.hyperlinks.click_to_open\": \"クリックして開く\",\n  \"ui.main_window.hyperlinks.update_available\": \"更新あり（{0}）\",\n  \"ui.main_window.hyperlinks.unofficial_update_available\": \"非公式アップデートあり（{0}）\",\n  \"ui.main_window.hyperlinks.broken_compatibility_issue\": \"【破損】互換性の問題\",\n  \"ui.main_window.hyperlinks.install_update\": \"更新をインストール\",\n  \"ui.main_window.hyperlinks.downloading\": \"ダウンロード中...\",\n  \"ui.main_window.hyperlinks.installing\": \"インストール中...\",\n\n  // Main Window - Buttons\n  \"ui.main_window.buttons.save_configs\": \"設定を保存\",\n  \"ui.main_window.buttons.save_profile\": \"プロファイルを保存\",\n  \"ui.main_window.buttons.show_updatable_mods\": \"更新可能なModを表示\",\n  \"ui.main_window.buttons.active_search_filters\": \"フィルター有効: {0}件\",\n  \"ui.main_window.buttons.no_search_filters\": \"フィルターなし\",\n\n  // Main Window - Buttons - Update Status\n  \"ui.main_window.button.update_status.generic\": \"クリックしてMod更新を確認\",\n  \"ui.main_window.button.update_status.updating\": \"更新中...\",\n  \"ui.main_window.button.update_status.list_available_updates\": \"更新可能なMod: {0}\",\n  \"ui.main_window.button.update_status.failed\": \"Mod更新チェックに失敗\",\n\n  // Main Window - Buttons - Downloads\n  \"ui.main_window.buttons.downloads.label\": \"ダウンロード（{0}）\",\n\n  // Main Window - ComboBox\n  \"ui.main_window.combobox.show_all_mods\": \"すべてのModを表示\",\n  \"ui.main_window.combobox.show_mods_with_configs\": \"設定可能なModを表示\",\n  \"ui.main_window.combobox.show_disabled_mods\": \"無効なModを表示\",\n  \"ui.main_window.combobox.show_enabled_mods\": \"有効なModを表示\",\n\n  // Main Window - ComboBox (ListBox)\n  \"ui.main_window.combobox.mod_name\": \"Mod名\",\n  \"ui.main_window.combobox.author\": \"作者\",\n  \"ui.main_window.combobox.requirements\": \"必要要件\",\n  \"ui.main_window.combobox.group\": \"Modグループ\",\n\n  // Settings Window - Labels\n  \"ui.settings_window.labels.smapi_path\": \"SMAPIのパス\",\n  \"ui.settings_window.labels.mod_path\": \"Modフォルダのパス\",\n  \"ui.settings_window.labels.mod_install\": \"Stardrop導入Modの保存先\",\n  \"ui.settings_window.labels.themes\": \"テーマ\",\n  \"ui.settings_window.labels.languages\": \"言語\",\n  \"ui.settings_window.labels.grouping\": \"Modグループ化\",\n  \"ui.settings_window.labels.miscellaneous\": \"その他\",\n  \"ui.settings_window.labels.nexus\": \"Nexus Mods\",\n  \"ui.settings_window.labels.preferred_server\": \"優先サーバー:\",\n\n  // Settings Window - Buttons\n  \"ui.settings_window.buttons.ignore_hidden_folders\": \"隠しフォルダを無視\",\n  \"ui.settings_window.buttons.enable_profile_specific_mod_configs\": \"プロファイル別Mod設定を有効化\",\n  \"ui.settings_window.buttons.enable_mods_on_add\": \"インストール時に自動でModを有効化\",\n  \"ui.settings_window.buttons.always_ask_to_delete\": \"更新時のファイル削除を毎回確認\",\n  \"ui.settings_window.buttons.automatically_save_profile_changes\": \"プロファイル変更を自動保存\",\n  \"ui.settings_window.buttons.always_ask_for_NXM_installs\": \"NXMインストール前に毎回確認\",\n  \"ui.settings_window.buttons.register_nxm_association\": \"NXM関連付けを登録\",\n\n  // Settings Window - Tooltips\n  \"ui.settings_window.tooltips.smapi\": \"StardewModdingAPI のファイルパス\",\n  \"ui.settings_window.tooltips.mod_folder_path\": \"Modフォルダのパス\",\n  \"ui.settings_window.tooltips.mod_install_path\": \"Stardropが新規インストールしたModを配置するフォルダのパス。「Modフォルダのパス」で指定したフォルダ配下である必要があります\",\n  \"ui.settings_window.tooltips.theme\": \"Stardropの現在のテーマ\",\n  \"ui.settings_window.tooltips.language\": \"Stardropの現在の言語\",\n  \"ui.settings_window.tooltips.grouping\": \"Modのグループ化方法（現在の設定）\",\n  \"ui.settings_window.tooltips.ignore_hidden_folders\": \"チェックすると、親フォルダ名が \\\".\\\" で始まるModは無視されます\",\n  \"ui.settings_window.tooltips.nxm_file_association\": \"NXMプロトコルをStardropに関連付け、www.nexusmods.com から直接インストールできるようにします\",\n  \"ui.settings_window.tooltips.always_ask_nxm_files\": \"チェックすると、NXM経由（www.nexusmods.com）でModをインストールする前に毎回確認します\",\n  \"ui.settings_window.tooltips.enable_profile_specific_configs\": \"チェックすると、プロファイル切り替え時に各Modの config.json を保存／復元します\",\n  \"ui.settings_window.tooltips.enable_mods_on_add\": \"チェックすると、新規追加または更新したModを自動で有効化します\",\n  \"ui.settings_window.tooltips.always_ask_to_delete\": \"チェックすると、Mod更新時にファイルを削除するか毎回確認します\",\n  \"ui.settings_window.tooltips.automatically_save_profile_changes\": \"チェックすると、プロファイルへの変更を自動で保存します\",\n  \"ui.settings_window.tooltips.preferred_server\": \"Nexus Modsからダウンロードする際に使う優先サーバーを設定します\",\n  \"ui.settings_window.tooltips.save_changes\": \"変更を保存\",\n  \"ui.settings_window.tooltips.cancel_changes\": \"変更をキャンセル\",\n\n  // Nexus Login Window - Buttons\n  \"ui.nexus_login.buttons.nexus\": \"Nexus Modsへ移動\",\n  \"ui.nexus_login.buttons.disconnect\": \"Nexusとの接続を解除\",\n\n  // Nexus Login Window - Labels\n  \"ui.nexus_login.labels.note\": \"注意\",\n  \"ui.nexus_login.labels.get_key\": \"Nexus Modsで個人用APIキーを取得してください\",\n  \"ui.nexus_login.labels.get_key_actual\": \"Nexus ModsでStardrop APIキーを取得してください\",\n  \"ui.nexus_login.labels.paste\": \"次に、下にキーを貼り付けてください\",\n  \"ui.nexus_login.labels.share_warning\": \"個人用APIキーを他人と共有しないでください。Nexusアカウントに対する措置が取られる可能性があります。\",\n  \"ui.nexus_login.labels.share_warning_actual\": \"APIキーを他人と共有しないでください。Nexusアカウントに対する措置が取られる可能性があります。\",\n  \"ui.nexus_login.labels.revoke\": \"キーはいつでも再発行／無効化できます（My Nexus Account > API > Personal API Key）。\",\n  \"ui.nexus_login.labels.revoke_actual\": \"キーはいつでも再発行／無効化できます（My Nexus Account > API > Stardrop）。\",\n  \"ui.nexus_login.labels.nexus_details\": \"Nexus Modsユーザー情報\",\n  \"ui.nexus_login.labels.nexus_disconnect\": \"下のボタンを押すと、Stardropが個人用キーを忘れてNexus Modsとの接続を解除します。\",\n  \"ui.nexus_login.labels.nexus_disconnect_actual\": \"下のボタンを押すと、StardropがStardrop APIキーを忘れてNexus Modsとの接続を解除します。\",\n  \"ui.nexus_login.labels.username\": \"ユーザー名: {0}\",\n  \"ui.nexus_login.labels.is_premium\": \"プレミアム会員: {0}\",\n\n  // Downloads Panel - Labels\n  \"ui.downloads_panel.download_canceled\": \"キャンセル済み\",\n  \"ui.downloads_panel.download_failed\": \"失敗\",\n  \"ui_downloads_panel.download_success\": \"完了！\",\n  \"ui.downloads_panel.no_downloads_label\": \"進行中のダウンロードはありません\",\n\n  // Downloads Panel - Tooltips\n  \"ui.downloads_panel.tooltips.cancel_button\": \"キャンセル\",\n  \"ui.downloads_panel.tooltips.remove_button\": \"削除\", // 完了または失敗したダウンロードを一覧から消すボタンのツールチップ\n\n  // Warnings\n  \"ui.warning.unable_to_add_profile\": \"{0} を追加できません。同名のプロファイルが既に存在します！\",\n  \"ui.warning.given_mod_folder_does_not_exist\": \"指定したModフォルダのパスが存在しません。\\n\\n前のパスに戻します。\",\n  \"ui.warning.given_install_folder_not_exist\": \"指定したStardrop導入Modフォルダのパスが存在しません。\\n\\n前のパスに戻します。\",\n  \"ui.warning.given_install_folder_not_exist_default\": \"指定したStardrop導入Modフォルダのパスが存在しません。\\n\\n既定のパスに戻します。\",\n  \"ui.warning.given_install_folder_not_under_mod_folder\": \"指定したStardrop導入Modフォルダのパスが {0} の配下にありません。\\n\\n前のパスに戻します。\",\n  \"ui.warning.given_invalid_smapi_executable\": \"指定したファイルは {0} ではありません。\\n\\n前のパスに戻します。\",\n  \"ui.warning.stardrop_locked\": \"SMAPIの実行中はStardropがロックされます。変更はSMAPIを終了するまで反映されません。\",\n  \"ui.warning.stardrop_downloading\": \"Stardropの最新バージョンをダウンロードしています。\",\n  \"ui.warning.SMAPI_downloading\": \"GitHubからSMAPIの最新バージョンをダウンロードしています。\",\n  \"ui.warning.SMAPI_installing\": \"SMAPIを最新バージョンへ更新しています。\",\n  \"ui.warning.SMAPI_up_to_date\": \"SMAPIは最新です。\\n\\n最新バージョンは {0} です。\",\n  \"ui.warning.install_mod_attempt_count\": \"{0} 個のModをインストールしようとしています\",\n  \"ui.warning.mod_installing\": \"{0} をインストールしています\",\n  \"ui.warning.mod_updating\": \"{0} を更新しています\",\n  \"ui.warning.mod_deleting\": \"{0} の古いインスタンスを削除しています\",\n  \"ui.warning.failed_to_delete\": \"{0} の削除に失敗しました。\\n\\nModファイルがロックされている可能性があります。詳細はログを確認してください。\",\n  \"ui.warning.failed_to_delete_during_update\": \"{0} の古いインスタンスを削除できませんでした。\\n\\nModが正しくインストールされない可能性があります。\",\n  \"ui.warning.unable_to_locate_smapi\": \"StardewModdingAPI.exe が見つかりません。\\n\\n正しいファイルパスを\\n表示 > 設定 で指定してください\",\n  \"ui.warning.mod_config_saved_but_not_enabled\": \"プロファイル {0} にMod設定を保存しましたが、プロファイル別Mod設定が有効になっていません。\\n\\n表示 > 設定 で「プロファイル別Mod設定を有効化」にチェックを入れると、Stardropが自動で保存・適用します。\",\n  \"ui.warning.unable_to_determine_profile\": \"選択中のプロファイルを判定できません。\\n\\nSMAPIは起動されません。\",\n  \"ui.warning.stardrop_up_to_date\": \"Stardropは最新です。\\n\\n最新バージョンは {0} です。\",\n  \"ui.warning.stardrop_unable_to_download_latest\": \"Stardropの最新リリースをダウンロードできません。\\n\\n手動で最新バージョンをダウンロードしてください。\",\n  \"ui.warning.stardrop_unable_to_find_latest\": \"StardropのGitHubリポジトリで更新確認ができません。\\n\\nGitHubで更新があるか確認するか、時間をおいて再試行してください。\",\n  \"ui.warning.stardrop_update_downloaded\": \"最新バージョンのダウンロードに成功しました。\\n\\nStardropを再起動します。\",\n  \"ui.warning.SMAPI_unable_to_download_latest\": \"SMAPIの最新リリースをダウンロードできません。\\n\\n手動で最新バージョンをダウンロードしてください。\",\n  \"ui.warning.SMAPI_unable_to_install_latest\": \"SMAPIの最新リリースをインストールできません。\\n\\n手動で最新バージョンをダウンロードしてください。\",\n  \"ui.warning.update_cooldown_minutes\": \"更新のリクエストは5分に1回のみ可能です。\\n\\n{0} 分後に再試行してください。\",\n  \"ui.warning.update_cooldown_seconds\": \"更新のリクエストは5分に1回のみ可能です。\\n\\n{0} 秒後に再試行してください。\",\n  \"ui.warning.unable_to_locate_log\": \"SMAPI-latest.txt が見つかりません。ゲーム情報を検出するには、SMAPIで正常に1回以上起動する必要があります。\",\n  \"ui.warning.unable_to_read_log\": \"ゲームバージョン取得のためにSMAPIのログファイルを読めませんでした。\\n\\nMod更新チェックは行われません。\",\n  \"ui.warning.no_manifest\": \"\\\"{0}\\\" に manifest.json が見つかりません\",\n  \"ui.warning.unable_to_load_mod\": \"\\\"{0}\\\" にあるファイルを読み込めません。\\n\\n詳細はログファイルを確認してください。\",\n  \"ui.warning.unable_to_validate_nexus_key\": \"Nexus Mod APIキーを検証できません。\\n\\nキーが正しいか確認して再試行してください。\",\n  \"ui.warning.download_without_premium\": \"Stardrop経由でダウンロードするにはNexus Modsのプレミアム会員が必要です。\",\n  \"ui.warning.no_downloads_available\": \"Nexus Modsに更新があるModは現在ありません。\",\n  \"ui.warning.failed_nexus_install\": \"Nexus Modsとの接続問題により、Mod {0} のインストールに失敗しました。\\n\\n再試行してください。\",\n  \"ui.warning.unable_nexus_install\": \"Mod {0} をインストールできません。\",\n  \"ui.warning.unable_to_endorse\": \"Endorse状態を設定できません:\\n\\n{0}\",\n  \"ui.warning.mod_owned\": \"あなたはこのModの所有者です。\",\n  \"ui.warning.too_soon_after_download\": \"このModをダウンロードしてから15分待つ必要があります。\\n\\nEndorseを試みると、このタイマーがリセットされます。\",\n  \"ui.warning.not_downloaded\": \"Endorseするには、このModをNexus Modsからダウンロードする必要があります。\",\n  \"ui.warning.failed_to_get_mod_details\": \"NXMリンクからMod詳細を取得できませんでした: {0}\",\n  \"ui.warning.failed_to_get_download_link\": \"NXMリンク経由でNexus ModsからダウンロードURLを取得できませんでした: {0}\",\n  \"ui.warning.failed_to_set_association\": \"NXMプロトコルをStardropに登録できませんでした。\\n\\n詳細はログを確認してください。\",\n  \"ui.warning.already_associated\": \"Stardropは既にNXMプロトコルに関連付けられています。\",\n\n  // Messages\n  \"ui.message.confirm_mod_deletion\": \"{0} を削除しますか？元に戻せません。\",\n  \"ui.message.unsaved_config_changes\": \"プロファイル {0} に未保存のMod設定変更があります。\\n\\n変更を保存しますか？\",\n  \"ui.message.stardrop_update_available\": \"Stardropの更新（v{0}）があります。\\n\\n今すぐダウンロードしますか？\",\n  \"ui.message.SMAPI_update_available\": \"SMAPIの更新（v{0}）があります。\\n\\n今すぐダウンロードしますか？\",\n  \"ui.message.stardrop_update_complete\": \"Stardropの更新が完了しました。\\n\\nリリースノートを表示しますか？\",\n  \"ui.message.SMAPI_update_complete\": \"SMAPIの更新が完了しました。\\n\\nリリースノートを表示しますか？\",\n  \"ui.message.confirm_bulk_change_mod_states_enable\": \"すべてのModを有効化しますか？\\n\\n注意: 元に戻せません。\",\n  \"ui.message.confirm_bulk_change_mod_states_disable\": \"すべてのModを無効化しますか？\\n\\n注意: 元に戻せません。\",\n  \"ui.message.confirm_mod_update_method\": \"{0} の旧バージョンを検出しました。以前のインストールを消去しますか？\\n\\n注意: 以前のバージョンを消去するのが推奨ですが、設定ファイルは失われます。\",\n  \"ui.message.confirm_mod_update_method_no_config\": \"{0} の旧バージョンを検出しました。以前のインストールを消去しますか？\\n\\n注意: 以前のバージョンを消去するのが推奨です。\",\n  \"ui.message.confirm_mod_update_method_preserved\": \"{0} の旧バージョンを検出しました。以前のインストールを消去しますか？\\n\\n注意: 以前のバージョンを消去するのが推奨で、設定ファイルは復元されます。\",\n  \"ui.message.confirm_nxm_association\": \"NXMプロトコルをStardropに関連付けますか？\\n\\nこれにより、Nexus ModsからダウンロードしたModをStardropが自動でインストールできるようになります。\",\n  \"ui.message.confirm_nxm_install\": \"次のModをインストールしますか？\\n\\n{0}\\n\\nこの確認は設定メニューで無効化できます。\",\n  \"ui.message.require_nexus_login\": \"この操作にはNexus Mods APIへのサインインが必要です。\",\n  \"ui.message.succeeded_nexus_install\": \"Nexus Mods経由で次のModのインストールに成功しました:\\n\\n{0}\",\n\n  // Window Names\n  \"ui.window.settings.name\": \"設定\",\n  \"ui.window.profiles.name\": \"プロファイル\",\n  \"ui.window.profile_naming.name\": \"プロファイル名\",\n  \"ui.window.nexus_login.name\": \"Nexus Mods 個人用APIキー\",\n  \"ui.window.nexus_login_actual.name\": \"Nexus Mods APIキー\",\n  \"ui.window.nexus_user.name\": \"Nexus Mods ユーザー情報\",\n\n  // Internals\n  \"internal.connected\": \"接続済み\",\n  \"internal.disconnected\": \"未接続\",\n  \"internal.unknown\": \"不明\",\n  \"internal.enable\": \"有効化\",\n  \"internal.disable\": \"無効化\",\n  \"internal.enable_whole_mod\": \"Modグループ全体を有効化\",\n  \"internal.disable_whole_mod\": \"Modグループ全体を無効化\",\n  \"internal.yes\": \"はい\",\n  \"internal.yes_all\": \"すべてはい\",\n  \"internal.no\": \"いいえ\",\n  \"internal.no_all\": \"すべていいえ\",\n  \"internal.ok\": \"OK\",\n  \"internal.unlock\": \"ロック解除\",\n\n  // Internals - Measurements\n  \"internal.measurements.bytes_size\": \"B\",\n  \"internal.measurements.bytes_per_second\": \"B/秒\",\n  \"internal.measurements.kilobytes_size\": \"KB\",\n  \"internal.measurements.kilobytes_per_second\": \"KB/秒\",\n  \"internal.measurements.megabytes_size\": \"MB\",\n  \"internal.measurements.megabytes_per_second\": \"MB/秒\"\n}\n"
  },
  {
    "path": "Stardrop/i18n/ko.json",
    "content": "{\r\n  // Main Window - Menu Items\r\n  \"ui.main_window.menu_items.add_mod\": \"모드 추가\",\r\n  \"ui.main_window.menu_items.add_mods\": \"모드 추가\",\r\n  \"ui.main_window.menu_items.start_smapi\": \"SMAPI 시작하기\",\r\n  \"ui.main_window.menu_items.exit\": \"종료\",\r\n  \"ui.main_window.menu_items.settings\": \"설정\",\r\n  \"ui.main_window.menu_items.stardrop_log_file\": \"Stardrop 로그 파일\",\r\n  \"ui.main_window.menu_items.smapi_log_file\": \"SMAPI 로그 파일\",\r\n  \"ui.main_window.menu_items.refresh_mod_list\": \"모드 목록 새로고침\",\r\n  \"ui.main_window.menu_items.check_for_mod_updates\": \"모드 업데이트 확인\",\r\n  \"ui.main_window.menu_items.enable_all_mods\": \"모드 전부 켜기\",\r\n  \"ui.main_window.menu_items.disable_all_mods\": \"모드 전부 끄기\",\r\n  \"ui.main_window.menu_items.check_for_stardrop_update\": \"Stardrop 업데이트 확인\",\r\n  \"ui.main_window.menu_items.stardrop_repository\": \"Stardrop 리포지토리\",\r\n  \"ui.main_window.menu_items.nexus_install_all\": \"업데이트 모두 설치\",\r\n  \"ui.main_window.menu_items.nexus_connection\": \"API 연결\",\r\n  \"ui.main_window.menu_items.check_for_SMAPI_update\": \"SMAPI 업데이트 확인\",\r\n\r\n  // Main Window - Menu Items - Context\r\n  \"ui.main_window.menu_items.context.columns.show_hide\": \"열 보기 / 숨기기\",\r\n  \"ui.main_window.menu_items.context.hide_requirements\": \"선행 모드 숨기기\",\r\n  \"ui.main_window.menu_items.context.cancel\": \"취소\",\r\n  \"ui.main_window.menu_items.context.open_containing_folder\": \"폴더 열기\",\r\n  \"ui.main_window.menu_items.context.open_mod_page\": \"모드 페이지 열기\",\r\n  \"ui.main_window.menu_items.context.open_config\": \"설정 파일 열기\",\r\n  \"ui.main_window.menu_items.context.show_whole_mod_group\": \"모드 그룹 보기\",\r\n  \"ui.main_window.menu_items.context.show_authors_mods\": \"제작자 모드 보기\",\r\n  \"ui.main_window.menu_items.context.open\": \"열기...\",\r\n  \"ui.main_window.menu_items.context.filters\": \"필터\",\r\n  \"ui.main_window.menu_items.context.options\": \"옵션\",\r\n  \"ui.main_window.menu_items.context.clear_filter\": \"초기화\",\r\n  \"ui.main_window.menu_items.context.delete\": \"삭제\",\r\n\r\n  // Main Window - Menu Headers\r\n  \"ui.main_window.menu_headers.file\": \"파일\",\r\n  \"ui.main_window.menu_headers.view\": \"보기\",\r\n  \"ui.main_window.menu_headers.tools\": \"도구\",\r\n  \"ui.main_window.menu_headers.nexus\": \"Nexus Mods\",\r\n\r\n  \"ui.main_window.menu_headers.enabled\": \"켜짐\",\r\n  \"ui.main_window.menu_headers.mod_name\": \"모드 이름\",\r\n  \"ui.main_window.menu_headers.unique_id\": \"고유 ID\",\r\n  \"ui.main_window.menu_headers.author\": \"제작자\",\r\n  \"ui.main_window.menu_headers.requirements\": \"선행 모드\",\r\n  \"ui.main_window.menu_headers.missing_requirements\": \"빠진 선행 모드\",\r\n  \"ui.main_window.menu_headers.version\": \"버전\",\r\n  \"ui.main_window.menu_headers.configuration\": \"설정\",\r\n  \"ui.main_window.menu_headers.status\": \"상태\",\r\n  \"ui.main_window.menu_headers.endorsement\": \"좋아요\",\r\n  \"ui.main_window.menu_headers.nexus_install\": \"Nexus Mods 업데이트\",\r\n  \"ui.main_window.menu_headers.install_timestamp\": \"설치 날짜\",\r\n  \"ui.main_window.menu_headers.last_updated\": \"업데이트 날짜\",\r\n\r\n  // Main Window - Labels\r\n  \"ui.main_window.labels.profile\": \"프로필\",\r\n  \"ui.main_window.labels.enabled_mods\": \"켜진 모드: \",\r\n  \"ui.main_window.labels.total_mods\": \"전체 모드: \",\r\n  \"ui.main_window.labels.updatable_mods\": \"업데이트 있는 모드: \",\r\n  \"ui.main_window.labels.filter\": \"필터\",\r\n  \"ui.main_window.labels.unknown_SMAPI\": \"확인할 수 없는 SMAPI 버전\",\r\n\r\n  // Main Window - Hyperlinks\r\n  \"ui.main_window.hyperlinks.click_to_open\": \"열기\",\r\n  \"ui.main_window.hyperlinks.update_available\": \"업데이트 있음 ({0})\",\r\n  \"ui.main_window.hyperlinks.unofficial_update_available\": \"비공식 업데이트 있음 ({0})\",\r\n  \"ui.main_window.hyperlinks.broken_compatibility_issue\": \"[작동 멈춤] 호환성 문제 발생\",\r\n  \"ui.main_window.hyperlinks.install_update\": \"업데이트 설치\",\r\n  \"ui.main_window.hyperlinks.downloading\": \"다운로드 중...\",\r\n  \"ui.main_window.hyperlinks.installing\": \"설치 중...\",\r\n\r\n  // Main Window - Buttons\r\n  \"ui.main_window.buttons.save_configs\": \"설정 저장\",\r\n  \"ui.main_window.buttons.save_profile\": \"프로필 저장\",\r\n  \"ui.main_window.buttons.show_updatable_mods\": \"업데이트 있는 모드 보기\",\r\n  \"ui.main_window.buttons.active_search_filters\": \"필터 {0}개 켜짐\",\r\n  \"ui.main_window.buttons.no_search_filters\": \"필터 꺼짐\",\r\n\r\n  // Main Window - Buttons - Update Status\r\n  \"ui.main_window.button.update_status.generic\": \"모드 업데이트 확인\",\r\n  \"ui.main_window.button.update_status.updating\": \"업데이트 중...\",\r\n  \"ui.main_window.button.update_status.list_available_updates\": \"업데이트 있는 모드: {0}\",\r\n  \"ui.main_window.button.update_status.failed\": \"모드 업데이트 확인 실패\",\r\n\r\n  // Main Window - Buttons - Downloads\r\n  \"ui.main_window.buttons.downloads.label\": \"다운로드 ({0})\",\r\n\r\n  // Main Window - ComboBox\r\n  \"ui.main_window.combobox.show_all_mods\": \"모드 전부 보기\",\r\n  \"ui.main_window.combobox.show_mods_with_configs\": \"설정 가능한 모드 보기\",\r\n  \"ui.main_window.combobox.show_disabled_mods\": \"꺼진 모드 보기\",\r\n  \"ui.main_window.combobox.show_enabled_mods\": \"켜진 모드 보기\",\r\n\r\n  // Main Window - ComboBox (ListBox)\r\n  \"ui.main_window.combobox.mod_name\": \"모드 이름\",\r\n  \"ui.main_window.combobox.author\": \"제작자\",\r\n  \"ui.main_window.combobox.requirements\": \"선행 모드\",\r\n  \"ui.main_window.combobox.group\": \"모드 그룹\",\r\n\r\n  // Settings Window - Labels\r\n  \"ui.settings_window.labels.smapi_path\": \"SMAPI 경로\",\r\n  \"ui.settings_window.labels.mod_path\": \"모드 폴더 경로\",\r\n  \"ui.settings_window.labels.mod_install\": \"Stardrop 모드 설치 경로\",\r\n  \"ui.settings_window.labels.themes\": \"테마\",\r\n  \"ui.settings_window.labels.languages\": \"언어\",\r\n  \"ui.settings_window.labels.grouping\": \"모드 그룹 방식\",\r\n  \"ui.settings_window.labels.miscellaneous\": \"기타\",\r\n  \"ui.settings_window.labels.nexus\": \"Nexus Mods\",\r\n  \"ui.settings_window.labels.preferred_server\": \"선호하는 서버:\",\r\n\r\n  // Settings Window - Buttons\r\n  \"ui.settings_window.buttons.ignore_hidden_folders\": \"숨겨진 폴더 무시\",\r\n  \"ui.settings_window.buttons.enable_profile_specific_mod_configs\": \"프로필별 모드 설정\",\r\n  \"ui.settings_window.buttons.enable_mods_on_add\": \"설치한 모드 자동으로 켜기\",\r\n  \"ui.settings_window.buttons.always_ask_to_delete\": \"업데이트 시 모드 파일 삭제 알림 표시\",\r\n  \"ui.settings_window.buttons.automatically_save_profile_changes\": \"프로필 변경 사항 자동으로 저장\",\r\n  \"ui.settings_window.buttons.always_ask_for_NXM_installs\": \"Nexus 모드 매니저 설치 알림 표시\",\r\n  \"ui.settings_window.buttons.register_nxm_association\": \"Nexus 연동하기\",\r\n\r\n  // Settings Window - Tooltips\r\n  \"ui.settings_window.tooltips.smapi\": \"StardewModdingAPI.exe 경로. 파일은 SMAPI 설치 후, 일반적으로 스타듀 밸리 로컬 폴더에 있습니다.\",\r\n  \"ui.settings_window.tooltips.mod_folder_path\": \"스타듀 밸리 모드를 모아둔 폴더 경로\",\r\n  \"ui.settings_window.tooltips.mod_install_path\": \"Stardrop으로 설치한 모드를 저장하는 경로. 위에서 설정한 '모드 폴더 경로' 혹은 하위 경로여야 합니다.\",\r\n  \"ui.settings_window.tooltips.theme\": \"Stardrop 테마\",\r\n  \"ui.settings_window.tooltips.language\": \"Stardrop 언어\",\r\n  \"ui.settings_window.tooltips.grouping\": \"모드 그룹을 형성하는 방식\",\r\n  \"ui.settings_window.tooltips.ignore_hidden_folders\": \"상위 폴더의 이름이 마침표(.)로 시작하는 모드를 모두 무시합니다.\",\r\n  \"ui.settings_window.tooltips.nxm_file_association\": \"Nexus에 연동하기 위해, NXM 프로토콜을 등록합니다. www.nexusmods.com에서 모드를 바로 다운로드할 수 있게 됩니다.\",\r\n  \"ui.settings_window.tooltips.always_ask_nxm_files\": \"www.nexusmods.com에서 모드 매니저 다운로드를 이용하면, Stardrop에서 알림을 표시합니다.\",\r\n  \"ui.settings_window.tooltips.enable_profile_specific_configs\": \"프로필별로 config.json 모드 설정 파일을 다르게 저장하고 불러옵니다.\",\r\n  \"ui.settings_window.tooltips.enable_mods_on_add\": \"새로 추가하거나 업데이트한 모드를 자동으로 켭니다.\",\r\n  \"ui.settings_window.tooltips.always_ask_to_delete\": \"모드 업데이트 시, 모드 파일을 삭제할지 알림을 표시합니다.\",\r\n  \"ui.settings_window.tooltips.automatically_save_profile_changes\": \"프로필 변경 사항이 있으면 자동으로 저장합니다.\",\r\n  \"ui.settings_window.tooltips.preferred_server\": \"Nexus Mods에서 다운로드 시, 선호하는 서버를 지정합니다.\",\r\n  \"ui.settings_window.tooltips.save_changes\": \"저장\",\r\n  \"ui.settings_window.tooltips.cancel_changes\": \"취소\",\r\n\r\n  // Nexus Login Window - Buttons\r\n  \"ui.nexus_login.buttons.nexus\": \"Nexus Mods 접속\",\r\n  \"ui.nexus_login.buttons.disconnect\": \"Nexus 연동 끊기\",\r\n\r\n  // Nexus Login Window - Labels\r\n  \"ui.nexus_login.labels.note\": \"참고 부탁드립니다.\",\r\n  \"ui.nexus_login.labels.get_key\": \"Nexus Mods 사이트에서 계정 > Site preferences > API KEYS를 누르고,\\nPersonal API Key를 찾아 복사합니다.\",\r\n  \"ui.nexus_login.labels.get_key_actual\": \"Nexus Mods 사이트에서 계정 > Site preferences > API KEYS를 누르고,\\nStardrop API Key를 찾아 복사합니다.\",\r\n  \"ui.nexus_login.labels.paste\": \"API 키를 하단에 붙여넣으시길 바랍니다.\",\r\n  \"ui.nexus_login.labels.share_warning\": \"개인 API 키를 타인에게 공유하지 마십시오. Nexus 계정 보안에 문제가 발생할 수 있습니다.\",\r\n  \"ui.nexus_login.labels.share_warning_actual\": \"Stardrop API 키를 타인에게 공유하지 마십시오. Nexus 계정 보안에 문제가 발생할 수 있습니다.\",\r\n  \"ui.nexus_login.labels.revoke\": \"개인 키를 리셋 / 폐기하려면 언제든 Nexus Mods 사이트에서 계정 > Site preferences > API KEYS > Personal API Key를 통해 확인하시면 됩니다.\",\r\n  \"ui.nexus_login.labels.revoke_actual\": \"Stardrop 키를 리셋 / 폐기하려면 언제든 Nexus Mods 사이트에서 계정 > Site preferences > API KEYS > Stardrop을 통해 확인하시면 됩니다.\",\r\n  \"ui.nexus_login.labels.nexus_details\": \"Nexus Mods 사용자 정보\",\r\n  \"ui.nexus_login.labels.nexus_disconnect\": \"하단 버튼을 누르면, 개인 키 정보를 폐기하고 Nexus Mods와 연동을 해제합니다.\",\r\n  \"ui.nexus_login.labels.nexus_disconnect_actual\": \"하단 버튼을 누르면, Stardrop 키 정보를 폐기하고 Nexus Mods와 연동을 해제합니다.\",\r\n  \"ui.nexus_login.labels.username\": \"사용자 이름: {0}\",\r\n  \"ui.nexus_login.labels.is_premium\": \"프리미엄 멤버십: {0}\",\r\n\r\n  // Downloads Panel - Labels\r\n  \"ui.downloads_panel.download_canceled\": \"취소됨\",\r\n  \"ui.downloads_panel.download_failed\": \"실패\",\r\n  \"ui_downloads_panel.download_success\": \"완료!\",\r\n  \"ui.downloads_panel.no_downloads_label\":  \"진행 중인 다운로드 없음\",\r\n\r\n  // Downloads Panel - Tooltips\r\n  \"ui.downloads_panel.tooltips.cancel_button\": \"취소\",  \r\n  \"ui.downloads_panel.tooltips.remove_button\": \"목록에서 지우기\", // Tooltip for the button that clears out a completed or failed download\r\n\r\n  // Warnings\r\n  \"ui.warning.unable_to_add_profile\": \"{0} 프로필을 추가하는 데 실패했습니다. 동일한 이름의 프로필이 이미 존재합니다!\",\r\n  \"ui.warning.given_mod_folder_does_not_exist\": \"모드 폴더 경로가 존재하지 않습니다.\\n\\n이전 경로로 재설정합니다.\",\r\n  \"ui.warning.given_install_folder_not_exist\": \"Stardrop으로 설치한 모드를 저장할 폴더 경로가 존재하지 않습니다.\\n\\n이전 경로로 재설정합니다.\",\r\n  \"ui.warning.given_install_folder_not_exist_default\": \"Stardrop으로 설치한 모드를 저장할 폴더 경로가 존재하지 않습니다.\\n\\n이전 경로로 재설정합니다.\",\r\n  \"ui.warning.given_install_folder_not_under_mod_folder\": \"Stardrop으로 설치한 모드를 저장할 폴더 경로가 \\\"{0}\\\" 내 존재하지 않습니다.\\n\\n이전 경로로 재설정합니다.\",\r\n  \"ui.warning.given_invalid_smapi_executable\": \"{0} 파일이 아닙니다.\\n\\n이전 경로로 재설정합니다.\",\r\n  \"ui.warning.stardrop_locked\": \"SMAPI 실행 중에 Stardrop을 잠급니다. SMAPI를 닫을 때까지 변경 사항이 반영되지 않습니다.\",\r\n  \"ui.warning.stardrop_downloading\": \"Stardrop 최신 버전 다운로드 중\",\r\n  \"ui.warning.SMAPI_downloading\": \"GitHub에서 SMAPI 최신 버전 다운로드 중\",\r\n  \"ui.warning.SMAPI_installing\": \"SMAPI 최신 버전 업데이트 중\",\r\n  \"ui.warning.SMAPI_up_to_date\": \"SMAPI가 최신 버전입니다.\\n\\n최신 버전: {0}\",\r\n  \"ui.warning.install_mod_attempt_count\": \"모드 {0}개 설치 시도 중\",\r\n  \"ui.warning.mod_installing\": \"{0} 모드 설치 중\",\r\n  \"ui.warning.mod_updating\": \"{0} 모드 업데이트 중\",\r\n  \"ui.warning.mod_deleting\": \"{0} 모드의 이전 인스턴스 삭제 중\",\r\n  \"ui.warning.failed_to_delete\": \"{0} 모드를 삭제하는 데 실패했습니다.\\n\\n모드 파일이 잠겨있을 수도 있습니다. 자세한 내용은 로그 파일을 참조하십시오.\",\r\n  \"ui.warning.failed_to_delete_during_update\": \"{0} 모드의 이전 인스턴스를 삭제하는 데 실패했습니다.\\n\\n모드가 잘못 설치될 수 있습니다.\",\r\n  \"ui.warning.unable_to_locate_smapi\": \"StardewModdingAPI.exe를 찾을 수 없습니다.\\n\\n보기 > 설정 메뉴에서 파일 경로를 다시 설정하십시오.\",\r\n  \"ui.warning.mod_config_saved_but_not_enabled\": \"모드 설정을 {0} 프로필에 저장했으나, 프로필별로 모드 설정이 다르게 적용되지 않습니다.\\n\\n보기 > 설정 메뉴에서 '프로필별 모드 설정' 항목을 체크하십시오.\",\r\n  \"ui.warning.unable_to_determine_profile\": \"선택한 프로필을 찾을 수 없습니다.\\n\\nSMAPI를 실행할 수 없습니다.\",\r\n  \"ui.warning.stardrop_up_to_date\": \"Stardrop이 최신 버전입니다.\\n\\n최신 버전: {0}\",\r\n  \"ui.warning.stardrop_unable_to_download_latest\": \"Stardrop의 최신 버전을 다운로드할 수 없습니다.\\n\\n최신 버전을 수동으로 다운로드하십시오.\",\r\n  \"ui.warning.stardrop_unable_to_find_latest\": \"Stardrop의 GitHub 리포지토리를 확인할 수 없어, 업데이트를 진행할 수 없습니다.\\n\\nGitHub에서 업데이트를 확인하거나 나중에 다시 시도해주십시오.\",\r\n  \"ui.warning.stardrop_update_downloaded\": \"최신 버전을 다운로드하는 데 성공했습니다.\\n\\nStardrop을 재시작합니다.\",\r\n  \"ui.warning.SMAPI_unable_to_download_latest\": \"SMAPI의 최신 버전을 다운로드할 수 없습니다.\\n\\n최신 버전을 수동으로 다운로드하십시오.\",\r\n  \"ui.warning.SMAPI_unable_to_install_latest\": \"SMAPI의 최신 버전을 설치할 수 없습니다.\\n\\n최신 버전을 수동으로 다운로드하십시오.\",\r\n  \"ui.warning.update_cooldown_minutes\": \"업데이트는 5분에 한 번씩만 요청할 수 있습니다.\\n\\n{0}분 뒤에 다시 시도해주십시오.\",\r\n  \"ui.warning.update_cooldown_seconds\": \"업데이트는 5분에 한 번씩만 요청할 수 있습니다.\\n\\n{0}초 뒤에 다시 시도해주십시오.\",\r\n  \"ui.warning.unable_to_locate_log\": \"SMAPI-latest.txt를 찾을 수 없습니다! SMAPI를 한 번이라도 정상적으로 실행해야, Stardrop에서 게임 정보를 읽을 수 있습니다.\",\r\n  \"ui.warning.unable_to_read_log\": \"SMAPI 로그 파일을 읽을 수 없어, 게임 버전을 확인할 수 없습니다.\\n\\n모드 업데이트 확인을 중단합니다.\",\r\n  \"ui.warning.no_manifest\": \"\\\"{0}\\\"에서 manifest.json을 찾을 수 없습니다.\",\r\n  \"ui.warning.unable_to_load_mod\": \"\\\"{0}\\\"에 있는 파일을 불러올 수 없습니다.\\n\\n자세한 내용은 로그 파일을 참조하십시오.\",\r\n  \"ui.warning.unable_to_validate_nexus_key\": \"Nexus Mod API 키를 인증할 수 없습니다.\\n\\n키가 맞는지 확인하고 다시 시도하십시오.\",\r\n  \"ui.warning.download_without_premium\": \"Nexus Mods 프리미엄 멤버십이 있어야, Stardrop으로 일괄 업데이트 설치가 가능합니다.\",\r\n  \"ui.warning.no_downloads_available\": \"Nexus Mods에 업데이트할 수 있는 모드가 없습니다.\",\r\n  \"ui.warning.failed_nexus_install\": \"Nexus Mods 연결 문제로 {0} 모드를 설치하는 데 실패했습니다.\\n\\n다시 시도하십시오.\",\r\n  \"ui.warning.unable_nexus_install\": \"{0} 모드를 설치할 수 없습니다.\",\r\n  \"ui.warning.unable_to_endorse\": \"좋아요 설정을 할 수 없습니다.\\n\\n사유: {0}\",\r\n  \"ui.warning.mod_owned\": \"본인의 모드입니다.\",\r\n  \"ui.warning.too_soon_after_download\": \"모드를 다운로드하고 15분이 지나야 합니다.\\n\\n시간이 지나기 전에 좋아요를 누를 경우, 15분을 다시 기다려야 합니다.\",\r\n  \"ui.warning.not_downloaded\": \"Nexus Mods에서 다운로드해야 합니다.\",\r\n  \"ui.warning.failed_to_get_mod_details\": \"NXM 링크에서 모드 정보를 읽을 수 없습니다. {0}\",\r\n  \"ui.warning.failed_to_get_download_link\": \"NXM 링크에서 모드 URL을 읽을 수 없습니다. {0}\",\r\n  \"ui.warning.failed_to_set_association\": \"Stardrop에서 NXM 프로토콜을 등록할 수 없습니다.\\n\\n자세한 내용은 로그 파일을 참조하십시오.\",\r\n  \"ui.warning.already_associated\": \"이미 Stardrop이 NXM 프로토콜로 연동되어 있습니다.\",\r\n\r\n  // Messages\r\n  \"ui.message.confirm_mod_deletion\": \"정말 {0} 모드를 삭제하시겠습니까? 되돌릴 수 없습니다.\",\r\n  \"ui.message.unsaved_config_changes\": \"{0} 프로필에 모드 설정 변경 사항을 저장하지 않았습니다.\\n\\n저장하시겠습니까?\",\r\n  \"ui.message.stardrop_update_available\": \"Stardrop 업데이트가 있습니다. (v{0})\\n\\n지금 다운로드하시겠습니까?\",\r\n  \"ui.message.SMAPI_update_available\": \"SMAPI 업데이트가 있습니다. (v{0})\\n\\n지금 다운로드하시겠습니까?\",\r\n  \"ui.message.stardrop_update_complete\": \"Stardrop을 성공적으로 업데이트했습니다.\\n\\n릴리스 노트를 읽으시겠습니까?\",\r\n  \"ui.message.SMAPI_update_complete\": \"SMAPI를 성공적으로 업데이트했습니다.\\n\\n릴리스 노트를 읽으시겠습니까?\",\r\n  \"ui.message.confirm_bulk_change_mod_states_enable\": \"모드를 모두 켜시겠습니까?\\n\\n주의: 되돌릴 수 없습니다.\",\r\n  \"ui.message.confirm_bulk_change_mod_states_disable\": \"모드를 모두 끄시겠습니까?\\n\\n주의: 되돌릴 수 없습니다.\",\r\n  \"ui.message.confirm_mod_update_method\": \"{0} 모드의 이전 버전이 있습니다. 이전 설치 파일을 삭제하시겠습니까?\\n\\n주의: 이전 버전 삭제를 권장합니다. 모드 설정 파일도 삭제되므로 유의하십시오.\",\r\n  \"ui.message.confirm_mod_update_method_no_config\": \"{0} 모드의 이전 버전이 있습니다. 이전 설치 파일을 삭제하시겠습니까?\\n\\n주의: 이전 버전 삭제를 권장합니다.\",\r\n  \"ui.message.confirm_mod_update_method_preserved\": \"{0} 모드의 이전 버전이 있습니다. 이전 설치 파일을 삭제하시겠습니까?\\n\\n주의: 이전 버전 삭제를 권장합니다. 모드 설정 파일은 복원됩니다.\",\r\n  \"ui.message.confirm_nxm_association\": \"Stardrop에서 NXM 프로토콜을 연동하시겠습니까?\\n\\nNexus Mods에서 다운로드한 모드를 자동으로 설치할 수 있습니다.\",\r\n  \"ui.message.confirm_nxm_install\": \"다음 모드를 설치하시겠습니까?\\n\\n{0}\\n\\n설정 메뉴에서 알림을 끌 수 있습니다.\",\r\n  \"ui.message.require_nexus_login\": \"Nexus Mods API 인증을 받아야 합니다.\",\r\n  \"ui.message.succeeded_nexus_install\": \"Nexus Mods에서 다음 모드를 성공적으로 설치했습니다.\\n\\n{0}\",\r\n\r\n  // Window Names\r\n  \"ui.window.settings.name\": \"설정\",\r\n  \"ui.window.profiles.name\": \"프로필\",\r\n  \"ui.window.profile_naming.name\": \"프로필 이름\",\r\n  \"ui.window.nexus_login.name\": \"Nexus Mods 개인 API 키\",\r\n  \"ui.window.nexus_login_actual.name\": \"Nexus Mods API 키\",\r\n  \"ui.window.nexus_user.name\": \"Nexus Mods 사용자 정보\",\r\n\r\n  // Internals\r\n  \"internal.connected\": \"연결됨\",\r\n  \"internal.disconnected\": \"연결 끊김\",\r\n  \"internal.unknown\": \"알 수 없음\",\r\n  \"internal.enable\": \"켜기\",\r\n  \"internal.disable\": \"끄기\",\r\n  \"internal.enable_whole_mod\": \"모드 그룹 전부 켜기\",\r\n  \"internal.disable_whole_mod\": \"모드 그룹 전부 끄기\",\r\n  \"internal.yes\": \"예\",\r\n  \"internal.yes_all\": \"모두 예\",\r\n  \"internal.no\": \"아니요\",\r\n  \"internal.no_all\": \"모두 아니요\",\r\n  \"internal.ok\": \"OK\",\r\n  \"internal.unlock\": \"잠금 해제\",\r\n\r\n  // Internals - Measurements\r\n  \"internal.measurements.bytes_size\": \"B\",\r\n  \"internal.measurements.bytes_per_second\": \"B/s\",\r\n  \"internal.measurements.kilobytes_size\": \"KB\",\r\n  \"internal.measurements.kilobytes_per_second\": \"KB/s\",\r\n  \"internal.measurements.megabytes_size\": \"MB\",\r\n  \"internal.measurements.megabytes_per_second\": \"MB/s\"\r\n}\r\n"
  },
  {
    "path": "Stardrop/i18n/pl.json",
    "content": "{\n  // Main Window - Menu Items\n  \"ui.main_window.menu_items.add_mod\": \"Dodaj mod\",\n  \"ui.main_window.menu_items.add_mods\": \"Dodaj mody\",\n  \"ui.main_window.menu_items.start_smapi\": \"Uruchom SMAPI\",\n  \"ui.main_window.menu_items.exit\": \"Wyjście\",\n  \"ui.main_window.menu_items.settings\": \"Ustawienia\",\n  \"ui.main_window.menu_items.stardrop_log_file\": \"Plik logów Stardrop\",\n  \"ui.main_window.menu_items.smapi_log_file\": \"Plik logów SMAPI\",\n  \"ui.main_window.menu_items.refresh_mod_list\": \"Odśwież listę modów\",\n  \"ui.main_window.menu_items.check_for_mod_updates\": \"Sprawdź aktualizacje modów\",\n  \"ui.main_window.menu_items.enable_all_mods\": \"Włącz wszystkie mody\",\n  \"ui.main_window.menu_items.disable_all_mods\": \"Wyłącz wszystkie mody\",\n  \"ui.main_window.menu_items.check_for_stardrop_update\": \"Sprawdź aktualizację Stardrop\",\n  \"ui.main_window.menu_items.stardrop_repository\": \"Repozytorium Stardrop\",\n  \"ui.main_window.menu_items.nexus_install_all\": \"Zainstaluj wszystkie aktualizacje\",\n  \"ui.main_window.menu_items.nexus_connection\": \"Połączenie z API\",\n  \"ui.main_window.menu_items.check_for_SMAPI_update\": \"Sprawdź aktualizację SMAPI\",\n\n  // Main Window - Menu Items - Context\n  \"ui.main_window.menu_items.context.columns.show_hide\": \"Pokaż / ukryj kolumny\",\n  \"ui.main_window.menu_items.context.hide_requirements\": \"Ukryj wymagania\",\n  \"ui.main_window.menu_items.context.cancel\": \"Anuluj\",\n  \"ui.main_window.menu_items.context.open_containing_folder\": \"Otwórz folder nadrzędny\",\n  \"ui.main_window.menu_items.context.open_mod_page\": \"Otwórz stronę moda\",\n  \"ui.main_window.menu_items.context.open_config\": \"Otwórz plik konfiguracyjny\",\n  \"ui.main_window.menu_items.context.open_manifest\": \"Otwórz plik manifest\",\n  \"ui.main_window.menu_items.context.show_whole_mod_group\": \"Pokaż całą grupę modów\",\n  \"ui.main_window.menu_items.context.show_authors_mods\": \"Pokaż mody autora\",\n  \"ui.main_window.menu_items.context.open\": \"Otwórz...\",\n  \"ui.main_window.menu_items.context.filters\": \"Filtry\",\n  \"ui.main_window.menu_items.context.options\": \"Opcje\",\n  \"ui.main_window.menu_items.context.clear_filter\": \"Wyczyść\",\n  \"ui.main_window.menu_items.context.delete\": \"Usuń\",\n\n  // Main Window - Menu Headers\n  \"ui.main_window.menu_headers.file\": \"Plik\",\n  \"ui.main_window.menu_headers.view\": \"Widok\",\n  \"ui.main_window.menu_headers.tools\": \"Narzędzia\",\n  \"ui.main_window.menu_headers.nexus\": \"Nexus Mods\",\n\n  \"ui.main_window.menu_headers.enabled\": \"Aktywny\",\n  \"ui.main_window.menu_headers.mod_name\": \"Nazwa moda\",\n  \"ui.main_window.menu_headers.unique_id\": \"Unique ID\",\n  \"ui.main_window.menu_headers.author\": \"Autor\",\n  \"ui.main_window.menu_headers.requirements\": \"Wymagania\",\n  \"ui.main_window.menu_headers.missing_requirements\": \"Brakujące wymagania\",\n  \"ui.main_window.menu_headers.version\": \"Wersja\",\n  \"ui.main_window.menu_headers.configuration\": \"Konfiguracja\",\n  \"ui.main_window.menu_headers.status\": \"Status\",\n  \"ui.main_window.menu_headers.endorsement\": \"Polecenia\",\n  \"ui.main_window.menu_headers.nexus_install\": \"Aktualizacje z Nexus Mods\",\n  \"ui.main_window.menu_headers.install_timestamp\": \"Data instalacji\",\n  \"ui.main_window.menu_headers.last_updated\": \"Ostatnia aktualizacja\",\n\n  // Main Window - Labels\n  \"ui.main_window.labels.profile\": \"Profil\",\n  \"ui.main_window.labels.enabled_mods\": \"Włączone mody: \",\n  \"ui.main_window.labels.total_mods\": \"Łącznie modów: \",\n  \"ui.main_window.labels.updatable_mods\": \"Mody gotowe do aktualizacji: \",\n  \"ui.main_window.labels.filter\": \"Filtr\",\n  \"ui.main_window.labels.unknown_SMAPI\": \"Nieznana wersja SMAPI\",\n\n  // Main Window - Hyperlinks\n  \"ui.main_window.hyperlinks.click_to_open\": \"Kliknij, aby otworzyć\"\n  \"ui.main_window.hyperlinks.update_available\": \"Dostępna aktualizacja ({0})\",\n  \"ui.main_window.hyperlinks.unofficial_update_available\": \"Dostępna nieoficjalna aktualizacja ({0})\",\n  \"ui.main_window.hyperlinks.broken_compatibility_issue\": \"[Broken] Niekompatybilny\",\n  \"ui.main_window.hyperlinks.install_update\": \"Zainstaluj aktualizację\",\n  \"ui.main_window.hyperlinks.downloading\": \"Pobieranie...\",\n  \"ui.main_window.hyperlinks.installing\": \"Instalowanie...\",\n\n  // Main Window - Buttons\n  \"ui.main_window.buttons.save_configs\": \"Zapisz konfiguracje\",\n  \"ui.main_window.buttons.save_profile\": \"Zapisz profil\",\n  \"ui.main_window.buttons.show_updatable_mods\": \"Pokaż mody do aktualizacji\",\n  \"ui.main_window.buttons.active_search_filters\": \"Aktywne filtry: {0} \",\n  \"ui.main_window.buttons.no_search_filters\": \"Brak aktywnych filtrów\",\n\n  // Main Window - Buttons - Update Status\n  \"ui.main_window.button.update_status.generic\": \"Kliknij, aby sprawdzić aktualizacje modów\",\n  \"ui.main_window.button.update_status.updating\": \"Aktualizowanie...\",\n  \"ui.main_window.button.update_status.list_available_updates\": \"Mody gotowe do aktualizacji: {0}\",\n  \"ui.main_window.button.update_status.failed\": \"Nie udało się sprawdzić aktualizacji modów\",\n\n  // Main Window - Buttons - Downloads\n  \"ui.main_window.buttons.downloads.label\": \"Pobieranie ({0})\",\n\n  // Main Window - ComboBox\n  \"ui.main_window.combobox.show_all_mods\": \"Pokaż wszystkie mody\",\n  \"ui.main_window.combobox.show_mods_with_configs\": \"Pokaż mody z konfiguracją\",\n  \"ui.main_window.combobox.show_disabled_mods\": \"Pokaż wyłączone mody\",\n  \"ui.main_window.combobox.show_enabled_mods\": \"Pokaż włączone mody\",\n\n  // Main Window - ComboBox (ListBox)\n  \"ui.main_window.combobox.mod_name\": \"Nazwa moda\",\n  \"ui.main_window.combobox.author\": \"Autor\",\n  \"ui.main_window.combobox.requirements\": \"Wymagania\",\n  \"ui.main_window.combobox.group\": \"Grupa modów\",\n\n  // Settings Window - Labels\n  \"ui.settings_window.labels.smapi_path\": \"Lokalizacja SMAPI\",\n  \"ui.settings_window.labels.mod_path\": \"Lokalizacja folderu \\\"Mods\\\"\",\n  \"ui.settings_window.labels.mod_install\": \"Lokalizacja modów Stardrop\",\n  \"ui.settings_window.labels.themes\": \"Motywy\",\n  \"ui.settings_window.labels.languages\": \"Język\",\n  \"ui.settings_window.labels.grouping\": \"Grupowanie modów\",\n  \"ui.settings_window.labels.miscellaneous\": \"Różne\",\n  \"ui.settings_window.labels.nexus\": \"Nexus Mods\",\n  \"ui.settings_window.labels.preferred_server\": \"Preferowany serwer: \",\n\n  // Settings Window - Buttons\n  \"ui.settings_window.buttons.ignore_hidden_folders\": \"Ignoruj ukryte foldery\",\n  \"ui.settings_window.buttons.enable_profile_specific_mod_configs\": \"Włącz oddzielne konfiguracje modów dla profili\",\n  \"ui.settings_window.buttons.enable_mods_on_add\": \"Automatycznie włączaj mody po instalacji\",\n  \"ui.settings_window.buttons.always_ask_to_delete\": \"Zawsze pytaj o usuwanie plików moda przy aktualizacji\",\n  \"ui.settings_window.buttons.automatically_save_profile_changes\": \"Automatycznie zapisuj zmiany profilu\",\n  \"ui.settings_window.buttons.always_ask_for_NXM_installs\": \"Zawsze pytaj przed instalacją plików NXM\",\n  \"ui.settings_window.buttons.register_nxm_association\": \"Zarejestruj powiązanie NXM\",\n\n  // Settings Window - Tooltips\n  \"ui.settings_window.tooltips.smapi\": \"Lokalizacja pliku StardewModdingAPI (SMAPI) \",\n  \"ui.settings_window.tooltips.mod_folder_path\": \"Lokalizacja folderu z modami\",\n  \"ui.settings_window.tooltips.mod_install_path\": \"Folder, w którym Stardrop umieszcza nowo zainstalowane mody. Musi znajdować się w folderze z modami\",\n  \"ui.settings_window.tooltips.theme\": \"Aktualnie używany motyw Stardrop\",\n  \"ui.settings_window.tooltips.language\": \"Aktualnie używany język Stardrop\",\n  \"ui.settings_window.tooltips.grouping\": \"Aktualny sposób grupowania modów\",\n  \"ui.settings_window.tooltips.ignore_hidden_folders\": \"Jeśli włączone, Stardrop będzie ignorował mody znajdujące się w folderach zaczynających się od \\\".\\\"\",\n  \"ui.settings_window.tooltips.nxm_file_association\": \"Powiązuje protokół NXM ze Stardrop, umożliwiając bezpośrednią instalację modów z www.nexusmods.com\",\n  \"ui.settings_window.tooltips.always_ask_nxm_files\": \"Jeśli włączone, Stardrop zawsze zapyta przed instalacją moda z Nexus Mods przez NXM\",\n  \"ui.settings_window.tooltips.enable_profile_specific_configs\": \"Jeśli włączone, Stardrop zapisuje i przywraca pliki config.json modów przy przełączaniu profili\",\n  \"ui.settings_window.tooltips.enable_mods_on_add\": \"Jeśli włączone, Stardrop automatycznie włącza nowo dodane lub zaktualizowane mody\",\n  \"ui.settings_window.tooltips.always_ask_to_delete\": \"Jeśli włączone, Stardrop zawsze zapyta o usunięcie plików moda podczas aktualizacj\",\n  \"ui.settings_window.tooltips.automatically_save_profile_changes\": \"Jeśli włączone, Stardrop automatycznie zapisuje zmiany w profilu\",\n  \"ui.settings_window.tooltips.preferred_server\": \"Ustawia preferowany serwer używany do pobierania z Nexus Mods\",\n  \"ui.settings_window.tooltips.save_changes\": \"Zapisz zmiany\",\n  \"ui.settings_window.tooltips.cancel_changes\": \"Anuluj\",\n\n  // Nexus Login Window - Buttons\n  \"ui.nexus_login.buttons.nexus\": \"Przejdź do Nexus Mods\",\n  \"ui.nexus_login.buttons.disconnect\": \"Odłącz od Nexus Mods\",\n\n  // Nexus Login Window - Labels\n  \"ui.nexus_login.labels.note\": \"Uwaga\",\n  \"ui.nexus_login.labels.get_key\": \"Pobierz swój osobisty klucz API z Nexus Mods\",\n  \"ui.nexus_login.labels.get_key_actual\": \"Pobierz klucz API Stardrop z Nexus Mods\",\n  \"ui.nexus_login.labels.paste\": \"Następnie wklej klucz poniżej\",\n  \"ui.nexus_login.labels.share_warning\": \"Nie udostępniaj swojego osobistego klucza API innym osobom. Może to skutkować działaniami wobec Twojego konta Nexus.\",\n  \"ui.nexus_login.labels.share_warning_actual\": \"Nie udostępniaj swojego klucza API innym osobom. Może to skutkować działaniami wobec Twojego konta Nexus.\",\n  \"ui.nexus_login.labels.revoke\": \"Możesz w dowolnym momencie zresetować lub cofnąć swój klucz, przechodząc do: My Nexus Account (Moje konto Nexus)  > API > Personal API Key\",\n  \"ui.nexus_login.labels.revoke_actual\": \"Możesz w dowolnym momencie zresetować lub cofnąć swój klucz API Stardrop, przechodząc do: My Nexus Account (Moje konto Nexus)  > API > Stardrop.\",\n  \"ui.nexus_login.labels.nexus_details\": \"Dane użytkownika Nexus Mods\",\n  \"ui.nexus_login.labels.nexus_disconnect\": \"Kliknij przycisk poniżej, aby Stardrop zapomniał Twój osobisty klucz i odłączył się od Nexus Mods.\",\n  \"ui.nexus_login.labels.nexus_disconnect_actual\": \"Kliknij przycisk poniżej, aby Stardrop zapomniał klucz API Stardrop i odłączył się od Nexus Mods.\",\n  \"ui.nexus_login.labels.username\": \"Nazwa użytkownika: {0} \",\n  \"ui.nexus_login.labels.is_premium\": \"Konto Premium: {0} \",\n\n  // Downloads Panel - Labels\n  \"ui.downloads_panel.download_canceled\": \"Anulowane\",\n  \"ui.downloads_panel.download_failed\": \"Nieudane\",\n  \"ui_downloads_panel.download_success\": \"Zakończone!\",\n  \"ui.downloads_panel.no_downloads_label\":  \"Brak aktywnych pobrań\",\n\n  // Downloads Panel - Tooltips\n  \"ui.downloads_panel.tooltips.cancel_button\": \"Anuluj\",  \n  \"ui.downloads_panel.tooltips.remove_button\": \"Usuń\", // Tooltip for the button that clears out a completed or failed download\n\n  // Warnings\n  \"ui.warning.unable_to_add_profile\": \"Nie można dodać profilu {0} , profil o tej nazwie już istnieje!\",\n  \"ui.warning.given_mod_folder_does_not_exist\": \"Podana lokalizacja folderu modów nie istnieje.\\n\\nPrzywracanie poprzedniej lokalizacji.\",\n  \"ui.warning.given_install_folder_not_exist\": \"Podana lokalizacja folderu zainstalowanych modów Stardrop nie istnieje.\\n\\nPrzywracanie poprzedniej lokalizacji.\",\n  \"ui.warning.given_install_folder_not_exist_default\": \"Podana lokalizacja folderu zainstalowanych modów Stardrop nie istnieje.\\n\\nPrzywracanie domyślnej lokalizacji.\",\n  \"ui.warning.given_install_folder_not_under_mod_folder\": \"Podana lokalizacja folderu zainstalowanych modów Stardrop nie znajduje się w {0} .\\n\\nPrzywracanie poprzedniej lokalizacji.\",\n  \"ui.warning.given_invalid_smapi_executable\": \"Podany plik nie jest {0} .\\n\\nPrzywracanie poprzedniej lokalizacji.\",\n  \"ui.warning.stardrop_locked\": \"Stardrop jest zablokowany podczas działania SMAPI. Wszelkie wprowadzone zmiany zostaną zastosowane dopiero po zamknięciu SMAPI.\",\n  \"ui.warning.stardrop_downloading\": \"Pobieranie najnowszej wersji Stardrop.\",\n  \"ui.warning.SMAPI_downloading\": \"Pobieranie najnowszej wersji SMAPI z GitHub.\",\n  \"ui.warning.SMAPI_installing\": \"Aktualizowanie SMAPI do najnowszej wersji.\",\n  \"ui.warning.SMAPI_up_to_date\": \"SMAPI jest aktualne.\\n\\n{0} to najnowsza wersja.\",\n  \"ui.warning.install_mod_attempt_count\": \"Próba instalacji modów: {0} \",\n  \"ui.warning.mod_installing\": \"Instalowanie {0}\",\n  \"ui.warning.mod_updating\": \"Aktualizowanie {0}\",\n  \"ui.warning.mod_deleting\": \"Usuwanie starej wersji {0} \",\n  \"ui.warning.failed_to_delete\": \"Nie udało się usunąć {0} .\\n\\nPliki moda mogą być zablokowane. Sprawdź log, aby uzyskać szczegóły.\",\n  \"ui.warning.failed_to_delete_during_update\": \"Nie udało się usunąć starej wersji {0}.\\n\\nMod może zostać zainstalowany niepoprawnie.\",\n  \"ui.warning.unable_to_locate_smapi\": \"Nie można odnaleźć pliku StardewModdingAPI.exe.\\n\\nUstaw poprawną lokalizację w:\\nWidok > Ustawienia\",\n  \"ui.warning.mod_config_saved_but_not_enabled\": \"Zapisano ustawienia konfiguracji modów do profilu {0}, jednak konfiguracje modów zależne od profilu nie są włączone.\\n\\nPrzejdź do Widok > Ustawienia i zaznacz \\\"Włącz oddzielne konfiguracje modów dla profili\\\" aby Stardrop automatycznie zapisywał i stosował zmiany konfiguracji modów.\",\n  \"ui.warning.unable_to_determine_profile\": \"Nie można określić wybranego profilu.\\n\\nSMAPI nie zostanie uruchomione.\",\n  \"ui.warning.stardrop_up_to_date\": \"Stardrop jest aktualny.\\n\\n{0} to najnowsza wersja.\",\n  \"ui.warning.stardrop_unable_to_download_latest\": \"Nie można pobrać najnowszej wersji Stardrop.\\n\\nPobierz najnowszą wersję ręcznie.\",\n  \"ui.warning.stardrop_unable_to_find_latest\": \"Nie można sprawdzić repozytorium Stardrop na GitHubie pod kątem aktualizacji.\\n\\nSprawdź GitHub lub spróbuj ponownie później\",\n  \"ui.warning.stardrop_update_downloaded\": \"Najnowsza wersja została pobrana pomyślnie.\\n\\nStardrop zostanie teraz uruchomiony ponownie.\",\n  \"ui.warning.SMAPI_unable_to_download_latest\": \"Nie można pobrać najnowszej wersji SMAPI.\\n\\nPobierz najnowszą wersję ręcznie.\",\n  \"ui.warning.SMAPI_unable_to_install_latest\": \"Nie można zainstalować najnowszej wersji SMAPI.\\n\\nPobierz najnowszą wersję ręcznie.\",\n  \"ui.warning.update_cooldown_minutes\": \"Aktualizacje można sprawdzać tylko raz na 5 minut.\\n\\nSpróbuj ponownie za {0} minut.\",\n  \"ui.warning.update_cooldown_seconds\": \"Aktualizacje można sprawdzać tylko raz na 5 minut.\\n\\nSpróbuj ponownie za {0} sekund.\",\n  \"ui.warning.unable_to_locate_log\": \"Nie można odnaleźć pliku SMAPI-latest.txt! SMAPI musi zostać uruchomione przynajmniej raz, aby Stardrop mógł wykryć szczegóły gry.\",\n  \"ui.warning.unable_to_read_log\": \"Nie można odczytać pliku logów SMAPI w celu pobrania wersji gry.\\n\\nMody nie zostaną sprawdzone pod kątem aktualizacji.\",\n  \"ui.warning.no_manifest\": \"Nie znaleziono pliku manifest.json w \\\"{0}\\\"\",\n  \"ui.warning.unable_to_load_mod\": \"Nie można załadować pliku znajdującego się w \\\"{0}\\\".\\n\\nSprawdź plik logów, aby uzyskać więcej informacji.\",\n  \"ui.warning.unable_to_validate_nexus_key\": \"Nie można zweryfikować klucza API Nexus Mods.\\n\\nSprawdź, czy klucz jest poprawny i spróbuj ponownie.\",\n  \"ui.warning.download_without_premium\": \"Do pobierania modów przez Stardrop wymagane jest konto Premium Nexus Mods.\",\n  \"ui.warning.no_downloads_available\": \"Obecnie nie ma modów z dostępnymi aktualizacjami na Nexus Mods.\",\n  \"ui.warning.failed_nexus_install\": \"Nie udało się zainstalować moda {0} z powodu problemu z połączeniem z Nexus Mods.\\n\\nSpróbuj ponownie.\",\n  \"ui.warning.unable_nexus_install\": \"Nie można zainstalować moda {0}.\",\n  \"ui.warning.unable_to_endorse\": \"Nie można ustawić stanu polecenia:\\n\\n{0}\",\n  \"ui.warning.mod_owned\": \"Jesteś właścicielem tego moda.\",\n  \"ui.warning.too_soon_after_download\": \"Musisz odczekać 15 minut od pobrania tego moda.\\n\\nPróba polecenia zresetuje ten licznik.\",\n  \"ui.warning.not_downloaded\": \"Aby polecić ten mod, musisz pobrać go z Nexus Mods.\",\n  \"ui.warning.failed_to_get_mod_details\": \"Nie udało się pobrać szczegółów moda z linku NXM: {0}\",\n  \"ui.warning.failed_to_get_download_link\": \"Nie udało się pobrać linku do pobierania z Nexus Mods przez link NXM: {0}\",\n  \"ui.warning.failed_to_set_association\": \"Nie udało się zarejestrować protokołu NXM dla Stardrop.\\n\\nSprawdź log, aby uzyskać szczegóły.\",\n  \"ui.warning.already_associated\": \"Stardrop jest już powiązany z protokołem NXM.\",\n\n  // Messages\n  \"ui.message.confirm_mod_deletion\": \"Czy na pewno chcesz usunąć {0}? Tej operacji nie można cofnąć.\",\n  \"ui.message.unsaved_config_changes\": \"Wykryto niezapisane zmiany konfiguracji modów dla profilu {0}.\\n\\nCzy chcesz zapisać zmiany?\",\n  \"ui.message.stardrop_update_available\": \"Dostępna jest aktualizacja Stardrop (v{0}).\\n\\nCzy chcesz pobrać ją teraz?\",\n  \"ui.message.SMAPI_update_available\": \"Dostępna jest aktualizacja SMAPI (v{0}).\\n\\nCzy chcesz pobrać ją teraz?\",\n  \"ui.message.stardrop_update_complete\": \"Stardrop został pomyślnie zaktualizowany.\\n\\nCzy chcesz zobaczyć listę zmian?\",\n  \"ui.message.SMAPI_update_complete\": \"SMAPI zostało pomyślnie zaktualizowane.\\n\\nCzy chcesz zobaczyć listę zmian?\",\n  \"ui.message.confirm_bulk_change_mod_states_enable\": \"Włączyć wszystkie mody?\\n\\nUwaga: tej operacji nie można cofnąć.\",\n  \"ui.message.confirm_bulk_change_mod_states_disable\": \"Wyłączyć wszystkie mody?\\n\\nUwaga: tej operacji nie można cofnąć.\",\n  \"ui.message.confirm_mod_update_method\": \"Wykryto poprzednią wersję {0}. Czy chcesz usunąć poprzednią instalację?\\n\\nUwaga: usunięcie poprzednich wersji jest zazwyczaj zalecane, jednak pliki konfiguracyjne zostaną utracone.\",\n  \"ui.message.confirm_mod_update_method_no_config\": \"Wykryto poprzednią wersję {0}. Czy chcesz usunąć poprzednią instalację?\\n\\nUwaga: usunięcie poprzednich wersji jest zazwyczaj zalecane.\",\n  \"ui.message.confirm_mod_update_method_preserved\": \"Wykryto poprzednią wersję {0}. Czy chcesz usunąć poprzednią instalację?\\n\\nUwaga: usunięcie poprzednich wersji jest zazwyczaj zalecane, a pliki konfiguracyjne zostaną przywrócone.\",\n  \"ui.message.confirm_nxm_association\": \"Czy chcesz powiązać protokół NXM ze Stardrop?\\n\\nPozwoli to Stardrop automatycznie instalować mody pobierane z Nexus Mods.\",\n  \"ui.message.confirm_nxm_install\": \"Czy chcesz zainstalować następujący mod:\\n\\n{0}\\n\\nMożesz wyłączyć to potwierdzenie w ustawieniach.\",\n  \"ui.message.require_nexus_login\": \"Ta akcja wymaga zalogowania się do API Nexus Mods.\",\n  \"ui.message.succeeded_nexus_install\": \"Pomyślnie zainstalowano następujący mod przez Nexus Mods:\\n\\n{0}\",\n\n  // Window Names\n  \"ui.window.settings.name\": \"Ustawienia\",\n  \"ui.window.profiles.name\": \"Profile\",\n  \"ui.window.profile_naming.name\": \"Nazwa profilu\",\n  \"ui.window.nexus_login.name\": \"Osobisty klucz API Nexus Mods\",\n  \"ui.window.nexus_login_actual.name\": \"Klucz API Nexus Mods\",\n  \"ui.window.nexus_user.name\": \"Dane użytkownika Nexus Mods\",\n\n  // Internals\n  \"internal.connected\": \"Połączono\",\n  \"internal.disconnected\": \"Rozłączono\",\n  \"internal.unknown\": \"Nieznane\",\n  \"internal.enable\": \"Włącz\",\n  \"internal.disable\": \"Wyłącz\",\n  \"internal.enable_whole_mod\": \"Włącz całą grupę modów\",\n  \"internal.disable_whole_mod\": \"Wyłącz całą grupę modów\",\n  \"internal.yes\": \"Tak\",\n  \"internal.yes_all\": \"Tak dla wszystkich\",\n  \"internal.no\": \"Nie\",\n  \"internal.no_all\": \"Nie dla wszystkich\",\n  \"internal.ok\": \"OK\",\n  \"internal.unlock\": \"Odblokuj\",\n\n  // Internals - Measurements\n  \"internal.measurements.bytes_size\": \"B\",\n  \"internal.measurements.bytes_per_second\": \"B/s\",\n  \"internal.measurements.kilobytes_size\": \"KB\",\n  \"internal.measurements.kilobytes_per_second\": \"KB/s\",\n  \"internal.measurements.megabytes_size\": \"MB\",\n  \"internal.measurements.megabytes_per_second\": \"MB/s\"\n}\n"
  },
  {
    "path": "Stardrop/i18n/pt.json",
    "content": "{\n  // Main Window - Menu Items\n  \"ui.main_window.menu_items.add_mod\": \"Adicionar mod\",\n  \"ui.main_window.menu_items.add_mods\": \"Adcionar mods\",\n  \"ui.main_window.menu_items.start_smapi\": \"Iniciar SMAPI\",\n  \"ui.main_window.menu_items.exit\": \"Sair\",\n  \"ui.main_window.menu_items.settings\": \"Configurações\",\n  \"ui.main_window.menu_items.stardrop_log_file\": \"Arquivo de log do Stardrop\",\n  \"ui.main_window.menu_items.smapi_log_file\": \"Arquivo de log do SMAPI\",\n  \"ui.main_window.menu_items.refresh_mod_list\": \"Atualizar a lista de mods\",\n  \"ui.main_window.menu_items.check_for_mod_updates\": \"Verificar atualizações dos mods\",\n  \"ui.main_window.menu_items.enable_all_mods\": \"Habilitar todos os mods\",\n  \"ui.main_window.menu_items.disable_all_mods\": \"Desabilitar todos os mods\",\n  \"ui.main_window.menu_items.check_for_stardrop_update\": \"Verificar atualizações do Stardrop\",\n  \"ui.main_window.menu_items.stardrop_repository\": \"Repositório do Stardrop\",\n  \"ui.main_window.menu_items.nexus_install_all\": \"Instalar todas as atualizações\",\n  \"ui.main_window.menu_items.nexus_connection\": \"Conexão com a API\",\n  \"ui.main_window.menu_items.check_for_SMAPI_update\": \"Verificar atualizações do SMAPI\",\n\n  // Main Window - Menu Items - Context\n  \"ui.main_window.menu_items.context.columns.show_hide\": \"Exibir/ocultar colunas\",\n  \"ui.main_window.menu_items.context.hide_requirements\": \"Ocultar requisitos\",\n  \"ui.main_window.menu_items.context.cancel\": \"Cancelar\",\n  \"ui.main_window.menu_items.context.open_containing_folder\": \"Abrir pasta do mod\",\n  \"ui.main_window.menu_items.context.open_mod_page\": \"Abrir página do mod\",\n  \"ui.main_window.menu_items.context.open_config\": \"Abrir arquivo de ocnfiguração\",\n  \"ui.main_window.menu_items.context.show_whole_mod_group\": \"Exibir o grupo inteiro do mod\",\n  \"ui.main_window.menu_items.context.show_authors_mods\": \"Exibir os mods do autor\",\n  \"ui.main_window.menu_items.context.open\": \"Abrir...\",\n  \"ui.main_window.menu_items.context.filters\": \"Filtros\",\n  \"ui.main_window.menu_items.context.options\": \"Opções\",\n  \"ui.main_window.menu_items.context.clear_filter\": \"Limpar\",\n  \"ui.main_window.menu_items.context.delete\": \"Excluir\",\n\n  // Main Window - Menu Headers\n  \"ui.main_window.menu_headers.file\": \"Arquivo\",\n  \"ui.main_window.menu_headers.view\": \"Ver\",\n  \"ui.main_window.menu_headers.tools\": \"Ferramentas\",\n  \"ui.main_window.menu_headers.nexus\": \"Nexus Mods\",\n\n  \"ui.main_window.menu_headers.enabled\": \"Habilitado\",\n  \"ui.main_window.menu_headers.mod_name\": \"Nome do mod\",\n  \"ui.main_window.menu_headers.unique_id\": \"ID único\",\n  \"ui.main_window.menu_headers.author\": \"Autor\",\n  \"ui.main_window.menu_headers.requirements\": \"Requisitos\",\n  \"ui.main_window.menu_headers.missing_requirements\": \"Requisitos faltantes\",\n  \"ui.main_window.menu_headers.version\": \"Versão\",\n  \"ui.main_window.menu_headers.configuration\": \"Configuração\",\n  \"ui.main_window.menu_headers.status\": \"Status\",\n  \"ui.main_window.menu_headers.endorsement\": \"Endosso\",\n  \"ui.main_window.menu_headers.nexus_install\": \"Atualizações de mods do Nexus\",\n  \"ui.main_window.menu_headers.install_timestamp\": \"Horário da instalação\",\n  \"ui.main_window.menu_headers.last_updated\": \"Última atualização\",\n\n  // Main Window - Labels\n  \"ui.main_window.labels.profile\": \"Perfil\",\n  \"ui.main_window.labels.enabled_mods\": \"Mods habilitados: \",\n  \"ui.main_window.labels.total_mods\": \"Total de mods: \",\n  \"ui.main_window.labels.updatable_mods\": \"Mods atualizáveis: \",\n  \"ui.main_window.labels.filter\": \"Filtrar\",\n  \"ui.main_window.labels.unknown_SMAPI\": \"Versão desconhecida do SMAPI\",\n\n  // Main Window - Hyperlinks\n  \"ui.main_window.hyperlinks.click_to_open\": \"Clique para abrir\",\n  \"ui.main_window.hyperlinks.update_available\": \"Atualização disponível ({0})\",\n  \"ui.main_window.hyperlinks.unofficial_update_available\": \"Atualização não oficial disponível ({0})\",\n  \"ui.main_window.hyperlinks.broken_compatibility_issue\": \"[Defeituoso] Problema de compatibilidade\",\n  \"ui.main_window.hyperlinks.install_update\": \"Instalar atualização\",\n  \"ui.main_window.hyperlinks.downloading\": \"Baixando...\",\n  \"ui.main_window.hyperlinks.installing\": \"Instalando...\",\n\n  // Main Window - Buttons\n  \"ui.main_window.buttons.save_configs\": \"Salvar configurações\",\n  \"ui.main_window.buttons.save_profile\": \"Salvar perfil\",\n  \"ui.main_window.buttons.show_updatable_mods\": \"Exibir mods atualizáveis\",\n  \"ui.main_window.buttons.active_search_filters\": \"{0} filtro(s) ativo(s)\",\n  \"ui.main_window.buttons.no_search_filters\": \"Nenhum filtro ativo\",\n\n  // Main Window - Buttons - Update Status\n  \"ui.main_window.button.update_status.generic\": \"Clique para procurar por atualizações de mods\",\n  \"ui.main_window.button.update_status.updating\": \"Atualizando...\",\n  \"ui.main_window.button.update_status.list_available_updates\": \"Mods prontos para atualizar: {0}\",\n  \"ui.main_window.button.update_status.failed\": \"Falha na verificação de atualizações\",\n\n  // Main Window - ComboBox\n  \"ui.main_window.combobox.show_all_mods\": \"Exibir todos os mods\",\n  \"ui.main_window.combobox.show_mods_with_configs\": \"Exibir mods configuráveis\",\n  \"ui.main_window.combobox.show_disabled_mods\": \"Exibir mods desabilitados\",\n  \"ui.main_window.combobox.show_enabled_mods\": \"Exibir mods habilitados\",\n  \n  // Main Window - ComboBox (ListBox)\n  \"ui.main_window.combobox.mod_name\": \"Nome do mod\",\n  \"ui.main_window.combobox.author\": \"Autor\",\n  \"ui.main_window.combobox.requirements\": \"Requisitos\",\n  \"ui.main_window.combobox.group\": \"Grupo do mod\",\n\n  // Settings Window - Labels\n  \"ui.settings_window.labels.smapi_path\": \"Caminho do SMAPI\",\n  \"ui.settings_window.labels.mod_path\": \"Caminho da pasta dos mods\",\n  \"ui.settings_window.labels.mod_install\": \"Caminho dos mods instalados pelo Stardrop\",\n  \"ui.settings_window.labels.themes\": \"Temas\",\n  \"ui.settings_window.labels.languages\": \"Idiomas\",\n  \"ui.settings_window.labels.grouping\": \"Agrupamento de mods\",\n  \"ui.settings_window.labels.miscellaneous\": \"Diversos\",\n  \"ui.settings_window.labels.nexus\": \"Mods do Nexus\",\n  \"ui.settings_window.labels.preferred_server\": \"Servidor preferencial:\",\n\n  // Settings Window - Buttons\n  \"ui.settings_window.buttons.ignore_hidden_folders\": \"Ignorar pastas ocultas\",\n  \"ui.settings_window.buttons.enable_profile_specific_mod_configs\": \"Habilitar configurações de mods específicas por perfil\",\n  \"ui.settings_window.buttons.enable_mods_on_add\": \"Habilitar os mods automaticamente ao instalar\",\n  \"ui.settings_window.buttons.always_ask_to_delete\": \"Sempre perguntar quando for excluir arquivos de mod na atualização\",\n  \"ui.settings_window.buttons.automatically_save_profile_changes\": \"Salvar alterações do perfil automaticamente\",\n  \"ui.settings_window.buttons.always_ask_for_NXM_installs\": \"Sempre perguntar antes de instalar arquivos NXM\",\n  \"ui.settings_window.buttons.register_nxm_association\": \"Registrar associação NXM\",\n\n  // Settings Window - Tooltips\n  \"ui.settings_window.tooltips.smapi\": \"O caminho para o arquivo do StardewModdingAPI\",\n  \"ui.settings_window.tooltips.mod_folder_path\": \"O caminho para a pasta de mods\",\n  \"ui.settings_window.tooltips.mod_install_path\": \"O caminho da pasta na qual o Stardrop colocará os novos mods instalados. Deve estar dentro da pasta definida em \\\"Caminho da pasta dos mods\\\".\",\n  \"ui.settings_window.tooltips.theme\": \"O tema atual do Stardrop\",\n  \"ui.settings_window.tooltips.language\": \"O idioma atual do Stardrop\",\n  \"ui.settings_window.tooltips.grouping\": \"O método atual para agrupar mods\",\n  \"ui.settings_window.tooltips.ignore_hidden_folders\": \"Se estiver marcado, o Stardrop irá ignorar qualquer mod cuja pasta raiz comece com \\\".\\\"\",\n  \"ui.settings_window.tooltips.nxm_file_association\": \"Registra a associação do protocolo NXM com o Stardrop, permitindo instalações diretas a partir de www.nexusmods.com\",\n  \"ui.settings_window.tooltips.always_ask_nxm_files\": \"Se estiver marcado, o Stardrop sempre perguntará antes de instalar um mod de www.nexusmods.com via NXM\",\n  \"ui.settings_window.tooltips.enable_profile_specific_configs\": \"Se estiver marcado, o Stardrop irá salvar e restaurar arquivos config.json dos mods, ao alterar o perfil\",\n  \"ui.settings_window.tooltips.enable_mods_on_add\": \"Se estiver marcado, o Stardrop habilitará, automaticamente, mods novos ou atualizados\",\n  \"ui.settings_window.tooltips.always_ask_to_delete\": \"Se esiver marcado, o Stardrop sempre perguntará de deve excluir arquivos do mod ao atualizar o mod\",\n  \"ui.settings_window.tooltips.automatically_save_profile_changes\": \"Se estiver marcado, o Stardrop salvará automaticamente as alterações feitas no perfil\",\n  \"ui.settings_window.tooltips.preferred_server\": \"Define o seu servidor preferencial usado para baixar mods do Nexus\",\n  \"ui.settings_window.tooltips.save_changes\": \"Salvar alterações\",\n  \"ui.settings_window.tooltips.cancel_changes\": \"Cancelar\",\n\n  // Nexus Login Window - Buttons\n  \"ui.nexus_login.buttons.nexus\": \"Ir para Nexus Mods\",\n  \"ui.nexus_login.buttons.disconnect\": \"Desconectar do Nexus\",\n\n  // Nexus Login Window - Labels\n  \"ui.nexus_login.labels.note\": \"Por favor, preste atenção\",\n  \"ui.nexus_login.labels.get_key\": \"Obtenha sua chave de API pessoal do Nexus Mods\",\n  \"ui.nexus_login.labels.get_key_actual\": \"Obtenha sua chave de API do Stardrop do Nexus Mods\",\n  \"ui.nexus_login.labels.paste\": \"Depois, cole-a abaixo\",\n  \"ui.nexus_login.labels.share_warning\": \"Não compartilhe sua chave de API pessoal com ninguém. Se fizer isso, você pode sofrer sanções em sua conta do Nexus.\",\n  \"ui.nexus_login.labels.share_warning_actual\": \"Não compartilhe sua chave de API com ninguém. Se fizer isso, você pode sofrer sanções em sua conta do Nexus.\",\n  \"ui.nexus_login.labels.revoke\": \"Você pode redefinir/revogar a sua chave a qualquer momento, indo até My Nexus Account > API > Personal API Key.\",\n  \"ui.nexus_login.labels.revoke_actual\": \"Você pode redefinir/revogar a sua chave de API do Stardrop a qualquer momento, indo até My Nexus Account > API > Stardrop\",\n  \"ui.nexus_login.labels.nexus_details\": \"Detalhes de usuário do Nexus Mods\",\n  \"ui.nexus_login.labels.nexus_disconnect\": \"Clique no botão abaixo para fazer o Stardrop esquecer sua chave pessoal e desconectar do Nexus Mods.\",\n  \"ui.nexus_login.labels.nexus_disconnect_actual\": \"Clique no botão abaixo para fazer o Stardrop esquecer sua chave API do Stardrop e desconectar do Nexus Mods.\",\n  \"ui.nexus_login.labels.username\": \"Nome de usuário: {0}\",\n  \"ui.nexus_login.labels.is_premium\": \"É membro premium: {0}\",\n\n  // Warnings\n  \"ui.warning.unable_to_add_profile\": \"Não foi possível adicionar {0}, já existe um perfil com esse nome.\",\n  \"ui.warning.given_mod_folder_does_not_exist\": \"O caminho fornecido para pasta do mod não existe.\\n\\nRevertendo para o caminho anterior.\",\n  \"ui.warning.given_install_folder_not_exist\": \"O caminho fornecido para pasta de mods instalados pelo Stardrop não existe.\\n\\nRevertendo para o caminho anterior.\",\n  \"ui.warning.given_install_folder_not_exist_default\": \"O caminho fornecido para pasta de mods instalados pelo Stardrop não existe.\\n\\nRevertendo para o caminho padrão.\",\n  \"ui.warning.given_install_folder_not_under_mod_folder\": \"Não existe o caminho da pasta de mods instalados pelo Stardrop, dentro de {0}.\\n\\nRevertendo para o caminho anterior.\",\n  \"ui.warning.given_invalid_smapi_executable\": \"O arquivo fornecido não é {0}\\n\\nRevertendo para o caminho anterior.\",\n  \"ui.warning.stardrop_locked\": \"O Stardrop fica bloqueado enquanto o SMAPI estiver em execução. Qualquer alteração só será ativada quando o SMAPI for fechado.\",\n  \"ui.warning.stardrop_downloading\": \"Baixando a última versão do Stardrop.\",\n  \"ui.warning.SMAPI_downloading\": \"Baixando a última versão do SMAPI no GitHub.\",\n  \"ui.warning.SMAPI_installing\": \"Atualizando o SMAPI para a última versão.\",\n  \"ui.warning.SMAPI_up_to_date\": \"O SMAPI está atualizado.\\n\\n{0} é a última versão.\",\n  \"ui.warning.install_mod_attempt_count\": \"Tentando instalar {0} mod(s)\",\n  \"ui.warning.mod_installing\": \"Instalando {0}\",\n  \"ui.warning.mod_updating\": \"Atualizando {0}\",\n  \"ui.warning.mod_deleting\": \"Excluindo instâncias antigas de {0}\",\n  \"ui.warning.failed_to_delete\": \"Não foi possível excluir {0}.\\n\\nOs arquivos do mod podem estar travados, veja o log para mais detalhes.\",\n  \"ui.warning.failed_to_delete_during_update\": \"Não foi possível excluir a instância antiga de {0}.\\n\\nO mod pode ser instalado incorretamente.\",\n\n  \"ui.warning.unable_to_locate_smapi\": \"Não foi possível localizar o StardewModdingAPI.exe\\n\\nPor favor, defina o caminho correto do arquivo em\\nVer > Configurações\",\n  \"ui.warning.mod_config_saved_but_not_enabled\": \"As configurações do mod foram salvas no perfil {0}, entretanto, a opção de configurações específicas de mods por perfil não está habilitada.\\n\\nVá até Ver > Configurações e marque a opção \\\"Habilitar configurações de mods específicas por perfil\\\" para que o Stardrop salve e aplique as alterações nas configurações dos mods automaticamente.\",\n  \"ui.warning.unable_to_determine_profile\": \"Não foi possível determinar o perfil selecionado.\\n\\nO SMAPI não será iniciado.\",\n  \"ui.warning.stardrop_up_to_date\": \"O Stardrop está atualizado.\\n\\n{0} é a última versão.\",\n  \"ui.warning.stardrop_unable_to_download_latest\": \"Não foi possível obter a última versão do Stardrop.\\n\\nPor favor, faça o download da última versão manualmente.\",\n  \"ui.warning.stardrop_unable_to_find_latest\": \"Não foi possível procurar por atualizações no GitHub do Stardrop.\\n\\nPor favor, verifique o GitHub para ver se existem atualizações ou tente novamente depois.\",\n  \"ui.warning.stardrop_update_downloaded\": \"A última versão foi obtida com sucesso.\\n\\nO Stardrop irá reiniciar agora.\",\n  \"ui.warning.SMAPI_unable_to_download_latest\": \"Não foi possível baixar a última versão do SMAPI.\\n\\nPor favor, baixe a última versão manualmente.\",\n  \"ui.warning.SMAPI_unable_to_install_latest\": \"Não foi possível instalar a última versão do SMAPI.\\n\\nPor favor, baixe a última versão manualmente.\",\n  \"ui.warning.update_cooldown_minutes\": \"Atualizações só podem ser solicitadas a cada 5 minutos.\\n\\nPor favor, tente novamente em {0} minuto(s).\",\n  \"ui.warning.update_cooldown_seconds\": \"Atualizações só podem ser solicitadas a cada 5 minutos.\\n\\nPor favor, tente novamente em {0} segundo(s).\",\n  \"ui.warning.unable_to_locate_log\": \"Não foi possível localizar o SMAPI-latest.txt! É necessário que o SMAPI seja executado corretamente ao menos uma vez para que o Stardrop detecte os detalhes do jogo.\",\n  \"ui.warning.unable_to_read_log\": \"Não foi possível ler o arquivo de log do SMAPI, para obter a versão do jogo.\\n\\nNão será possível fazer a verificação de atualizações dos mods.\",\n  \"ui.warning.no_manifest\": \"O manifest.json não foi encontrado em \\\"{0}\\\"\",\n  \"ui.warning.unable_to_load_mod\": \"Não foi possível abrir o arquivo localizado em \\\"{0}\\\".\\n\\nVeja o arquivo de log para mais informações.\",\n  \"ui.warning.unable_to_validate_nexus_key\": \"Não foi possível validar a chave API do Nexus Mods.\\n\\nPor favor, verifique se a chave está correta e tente novamente.\",\n  \"ui.warning.download_without_premium\": \"Você tem que ter uma filiação premium no Nexus Mods para baixar via Stardrop.\",\n  \"ui.warning.no_downloads_available\": \"Atualmente não existem mods com atualizações disponíveis no Nexus Mods.\",\n  \"ui.warning.failed_nexus_install\": \"Não foi possível instalar o mod {0}, devido a um problema de conexão com o Nexus Mods.\\n\\nPor favor, tente novamente.\",\n  \"ui.warning.unable_nexus_install\": \"Não foi possível instalar o mod {0}.\",\n  \"ui.warning.unable_to_endorse\": \"Não foi possível definir o estado do endosso:\\n\\n{0}\",\n  \"ui.warning.mod_owned\": \"Você é o proprietário deste mod.\",\n  \"ui.warning.too_soon_after_download\": \"Você deve esperar 15 minutos após baixar o mod.\\n\\nCaso tente endossar, o temporizador será reiniciado.\",\n  \"ui.warning.not_downloaded\": \"Você deve baixar este mod do Nexus Mods para poder endossá-lo.\",\n  \"ui.warning.failed_to_get_mod_details\": \"Não foi possível obter os detalhes do mod no link do NXM: {0}\",\n  \"ui.warning.failed_to_get_download_link\": \"Não foi possível obter a URL de download do Nexus Mods, via link do NXM: {0}\",\n  \"ui.warning.failed_to_set_association\": \"Não foi possível registrar o protocolo NXM com o Stardrop.\\n\\nVeja o log pra mais detalhes.\",\n  \"ui.warning.already_associated\": \"O Stardrop já está associado ao protocolo NXM.\",\n\n  // Messages\n  \"ui.message.confirm_mod_deletion\": \"Tem certeza que você deseja excluir {0}? Isso não poderá ser desfeito.\",\n  \"ui.message.unsaved_config_changes\": \"Foram detectadas alterações não salvas nas configurações de mods do perfil {0}.\\n\\nDeseja salvar essas alterações?\",\n  \"ui.message.stardrop_update_available\": \"Existe uma atualização (v{0}) disponível para o Stardrop.\\n\\nDeseja baixá-la agora?\",\n  \"ui.message.SMAPI_update_available\": \"Existe uma atualização (v{0}) disponível para o SMAPI.\\n\\nDeseja baixá-la agora?\",\n  \"ui.message.stardrop_update_complete\": \"O Stardrop foi atualizado com sucesso.\\n\\nDeseja ver as notas de lançamento?\",\n  \"ui.message.SMAPI_update_complete\": \"O SMAPI foi atualizado com sucesso.\\n\\nDeseja ver as notas de lançamento?\",\n  \"ui.message.confirm_bulk_change_mod_states_enable\": \"Habilitar todos os mods?\\n\\nNota: Isso não pode ser desfeito.\",\n  \"ui.message.confirm_bulk_change_mod_states_disable\": \"Desabilitar todos os mods?\\n\\nNota: Isso não pode ser desfeito.\",\n  \"ui.message.confirm_mod_update_method\": \"Foi detectada uma versão anterior de {0}. Deseja limpar a instalação anterior?\\n\\nNota: Apesar de ser geralmente recomendável limpar a versão anterior, ao fazer isso, os arquivos de configuração desse mod serão perdidos.\",\n  \"ui.message.confirm_mod_update_method_no_config\": \"Foi detectada uma versão anterior de {0}. Deseja limpar a instalação anterior?\\n\\nNota: Geralmente é recomendável limpar a versão anterior.\",\n  \"ui.message.confirm_mod_update_method_preserved\": \"Foi detectada uma versão anterior de {0}. Deseja limpar a instalação anterior?\\n\\nNota: Geralmente é recomendável limpar a versão anterior e seus arquivos de configuração serão restaurados.\",\n  \"ui.message.confirm_nxm_association\": \"Deseja associar o protocolo NXM ao Stardrop?\\n\\nIsso permitirá ao Stardrop instalar automaticamente mods baixados do Nexus Mods.\",\n  \"ui.message.confirm_nxm_install\": \"Deseja instalar o seguinte módulo?\\n\\n{0}\\n\\nVocê pode desabilitar essa confirmação no menu de configurações.\",\n  \"ui.message.require_nexus_login\": \"Essa ação requer que você esteja conectado à API do Nexus Mods.\",\n  \"ui.message.succeeded_nexus_install\": \"O mod seguinte foi instalado com sucesso via Nexus Mods:\\n\\n{0}\",\n\n  // Window Names\n  \"ui.window.settings.name\": \"Configurações\",\n  \"ui.window.profiles.name\": \"Perfis\",\n  \"ui.window.profile_naming.name\": \"Nome do perfil\",\n  \"ui.window.nexus_login.name\": \"Chave API pessoal do Nexus Mods\",\n  \"ui.window.nexus_login_actual.name\": \"Chave API do Nexus Mods\",\n  \"ui.window.nexus_user.name\": \"Detalhes do suuário no Nexus Mods\",\n\n  // Internals\n  \"internal.connected\": \"Conectado\",\n  \"internal.disconnected\": \"Desconectado\",\n  \"internal.unknown\": \"Desconhecido\",\n  \"internal.enable\": \"Habilitado\",\n  \"internal.disable\": \"Desabilitado\",\n  \"internal.enable_whole_mod\": \"Habilitar o grupo inteiro do mod\",\n  \"internal.disable_whole_mod\": \"Desabilitar o grupo inteiro do mod\",\n  \"internal.yes\": \"Sim\",\n  \"internal.yes_all\": \"Sim para tudo\",\n  \"internal.no\": \"Não\",\n  \"internal.no_all\": \"Não para tudo\",\n  \"internal.ok\": \"OK\",\n  \"internal.unlock\": \"Desbloquear\"\n}\n"
  },
  {
    "path": "Stardrop/i18n/ru.json",
    "content": "{\n  // Main Window - Menu Items\n  \"ui.main_window.menu_items.add_mod\": \"Добавить мод\",\n  \"ui.main_window.menu_items.start_smapi\": \"Запустить SMAPI\",\n  \"ui.main_window.menu_items.exit\": \"Выход\",\n  \"ui.main_window.menu_items.settings\": \"Настройки\",\n  \"ui.main_window.menu_items.stardrop_log_file\": \"Файл журнала Stardrop\",\n  \"ui.main_window.menu_items.smapi_log_file\": \"Файл журнала SMAPI\",\n  \"ui.main_window.menu_items.refresh_mod_list\": \"Обновить список модов\",\n  \"ui.main_window.menu_items.check_for_mod_updates\": \"Проверить обновления модов\",\n  \"ui.main_window.menu_items.enable_all_mods\": \"Включить все моды\",\n  \"ui.main_window.menu_items.disable_all_mods\": \"Отключить все моды\",\n  \"ui.main_window.menu_items.check_for_stardrop_update\": \"Проверить обновления Stardrop\",\n  \"ui.main_window.menu_items.stardrop_repository\": \"Репозиторий Stardrop\",\n  \"ui.main_window.menu_items.nexus_install_all\": \"Установить все обновления\",\n  \"ui.main_window.menu_items.nexus_connection\": \"Соединение с API\",\n  \"ui.main_window.menu_items.check_for_SMAPI_update\": \"Проверить обновления SMAPI\",\n\n  // Main Window - Menu Items - Context\n  \"ui.main_window.menu_items.context.show_requirements\": \"Показать требования\",\n  \"ui.main_window.menu_items.context.hide_requirements\": \"Скрыть требования\",\n  \"ui.main_window.menu_items.context.cancel\": \"Отмена\",\n  \"ui.main_window.menu_items.context.open_containing_folder\": \"Открыть папку\",\n  \"ui.main_window.menu_items.context.open_mod_page\": \"Открыть страницу мода\",\n  \"ui.main_window.menu_items.context.delete\": \"Удалить\",\n\n  // Main Window - Menu Headers\n  \"ui.main_window.menu_headers.file\": \"Файл\",\n  \"ui.main_window.menu_headers.view\": \"Вид\",\n  \"ui.main_window.menu_headers.tools\": \"Инструменты\",\n  \"ui.main_window.menu_headers.nexus\": \"Моды Nexus\",\n\n  \"ui.main_window.menu_headers.enabled\": \"Включено\",\n  \"ui.main_window.menu_headers.mod_name\": \"Название мода\",\n  \"ui.main_window.menu_headers.unique_id\": \"Уникальный ID\",\n  \"ui.main_window.menu_headers.author\": \"Автор\",\n  \"ui.main_window.menu_headers.requirements\": \"Требования\",\n  \"ui.main_window.menu_headers.missing_requirements\": \"Отсутствующие требования\",\n  \"ui.main_window.menu_headers.version\": \"Версия\",\n  \"ui.main_window.menu_headers.configuration\": \"Конфигурация\",\n  \"ui.main_window.menu_headers.status\": \"Статус\",\n  \"ui.main_window.menu_headers.endorsement\": \"Оценка\",\n  \"ui.main_window.menu_headers.nexus_install\": \"Обновления модов с Nexus\",\n\n  // Main Window - Labels\n  \"ui.main_window.labels.profile\": \"Профиль\",\n  \"ui.main_window.labels.enabled_mods\": \"Включенные моды: \",\n  \"ui.main_window.labels.total_mods\": \"Всего модов: \",\n  \"ui.main_window.labels.updatable_mods\": \"Моды готовые к обновлению: \",\n  \"ui.main_window.labels.filter\": \"Фильтр\",\n  \"ui.main_window.labels.unknown_SMAPI\": \"Неизвестная версия SMAPI\",\n\n  // Main Window - Hyperlinks\n  \"ui.main_window.hyperlinks.click_to_open\": \"Нажмите, чтобы открыть\",\n  \"ui.main_window.hyperlinks.update_available\": \"Доступно обновление ({0})\",\n  \"ui.main_window.hyperlinks.unofficial_update_available\": \"Доступно неофициальное обновление ({0})\",\n  \"ui.main_window.hyperlinks.broken_compatibility_issue\": \"[Не работает] Проблема совместимости\",\n  \"ui.main_window.hyperlinks.install_update\": \"Установить обновление\",\n  \"ui.main_window.hyperlinks.downloading\": \"Загрузка...\",\n  \"ui.main_window.hyperlinks.installing\": \"Установка...\",\n\n  // Main Window - Buttons\n  \"ui.main_window.buttons.save_configs\": \"Сохранить конфигурации\",\n  \"ui.main_window.buttons.hide_disabled_mods\": \"Скрыть отключенные моды\",\n  \"ui.main_window.buttons.show_updatable_mods\": \"Показать обновляемые моды\",\n\n  // Main Window - Buttons - Update Status\n  \"ui.main_window.button.update_status.generic\": \"Нажмите, чтобы проверить обновления модов\",\n  \"ui.main_window.button.update_status.updating\": \"Обновление...\",\n  \"ui.main_window.button.update_status.list_available_updates\": \"Моды готовы к обновлению: {0}\",\n  \"ui.main_window.button.update_status.failed\": \"Не удалось проверить обновления модов\",\n\n  // Main Window - ComboBox\n  \"ui.main_window.combobox.show_all_mods\": \"Показать все моды\",\n  \"ui.main_window.combobox.hide_enabled_mods\": \"Скрыть включенные моды\",\n  \"ui.main_window.combobox.mod_name\": \"Название мода\",\n  \"ui.main_window.combobox.author\": \"Автор\",\n  \"ui.main_window.combobox.requirements\": \"Требования\",\n\n  // Settings Window - Labels\n  \"ui.settings_window.labels.smapi_path\": \"Путь к SMAPI\",\n  \"ui.settings_window.labels.mod_path\": \"Путь к папке с модами\",\n  \"ui.settings_window.labels.mod_install\": \"Путь к установленным модам Stardrop\",\n  \"ui.settings_window.labels.themes\": \"Темы\",\n  \"ui.settings_window.labels.languages\": \"Языки\",\n  \"ui.settings_window.labels.miscellaneous\": \"Разное\",\n  \"ui.settings_window.labels.nexus\": \"Nexus Mods\",\n  \"ui.settings_window.labels.preferred_server\": \"Предпочитаемый сервер:\",\n\n  // Settings Window - Buttons\n  \"ui.settings_window.buttons.ignore_hidden_folders\": \"Игнорировать скрытые папки\",\n  \"ui.settings_window.buttons.enable_profile_specific_mod_configs\": \"Включить конфигурации модов для конкретного профиля\",\n  \"ui.settings_window.buttons.enable_mods_on_add\": \"Автоматически включать моды после установки\",\n  \"ui.settings_window.buttons.always_ask_for_NXM_installs\": \"Всегда спрашивать подтверждение перед установкой файлов NXM\",\n  \"ui.settings_window.buttons.register_nxm_association\": \"Зарегистрировать ассоциацию NXM\",\n\n  // Settings Window - Tooltips\n  \"ui.settings_window.tooltips.smapi\": \"Путь к файлу StardewModdingAPI\",\n  \"ui.settings_window.tooltips.mod_folder_path\": \"Путь к папке с модами\",\n  \"ui.settings_window.tooltips.mod_install_path\": \"Путь к папке, в которую Stardrop будет устанавливать новые моды. Должен быть в папке, указанной в \\\"Путь к папке с модами\\\"\",\n  \"ui.settings_window.tooltips.theme\": \"Текущая тема Stardrop\",\n  \"ui.settings_window.tooltips.language\": \"Текущий язык Stardrop\",\n  \"ui.settings_window.tooltips.ignore_hidden_folders\": \"Если отмечено, то Stardrop будет игнорировать любые моды, у которых родительская папка начинается с \\\".\\\"\",\n  \"ui.settings_window.tooltips.nxm_file_association\": \"Регистрирует протокол NXM для ассоциации с Stardrop, позволяя осуществлять прямую установку модов с www.nexusmods.com\",\n  \"ui.settings_window.tooltips.always_ask_nxm_files\": \"Если отмечено, то Stardrop будет всегда спрашивать подтверждение перед установкой мода с www.nexusmods.com через NXM\",\n  \"ui.settings_window.tooltips.enable_profile_specific_configs\": \"Если отмечено, то Stardrop будет сохранять и восстанавливать файлы config.json для модов при смене профилей\",\n  \"ui.settings_window.tooltips.enable_mods_on_add\": \"Если отмечено, то Stardrop будет автоматически включать вновь добавленные или обновленные моды\",\n  \"ui.settings_window.tooltips.preferred_server\": \"Устанавливает ваш предпочтительный сервер для загрузки с Nexus Mods\",\n  \"ui.settings_window.tooltips.save_changes\": \"Сохранить изменения\",\n  \"ui.settings_window.tooltips.cancel_changes\": \"Отменить\",\n\n  // Nexus Login Window - Buttons\n  \"ui.nexus_login.buttons.nexus\": \"Перейти на Nexus Mods\",\n  \"ui.nexus_login.buttons.disconnect\": \"Отключить от Nexus\",\n\n  // Nexus Login Window - Labels\n  \"ui.nexus_login.labels.note\": \"Пожалуйста, обратите внимание\",\n  \"ui.nexus_login.labels.get_key\": \"Получите свой личный API-ключ от Nexus Mods\",\n  \"ui.nexus_login.labels.get_key_actual\": \"Получите свой API-ключ Stardrop от Nexus Mods\",\n  \"ui.nexus_login.labels.paste\": \"Затем вставьте ключ ниже\",\n  \"ui.nexus_login.labels.share_warning\": \"Не передавайте свой личный API-ключ другим, это может привести к действиям против вашей учетной записи Nexus.\",\n  \"ui.nexus_login.labels.share_warning_actual\": \"Не передавайте свой API-ключ другим, это может привести к действиям против вашей учетной записи Nexus.\",\n  \"ui.nexus_login.labels.revoke\": \"Вы можете сбросить / отозвать свой ключ в любое время, перейдя в Мой аккаунт Nexus> API> Личный API-ключ.\",\n  \"ui.nexus_login.labels.revoke_actual\": \"Вы можете сбросить / отозвать свой API-ключ Stardrop в любое время, перейдя в Мой аккаунт Nexus> API> Stardrop.\",\n  \"ui.nexus_login.labels.nexus_details\": \"Данные пользователя Nexus Mods\",\n  \"ui.nexus_login.labels.nexus_disconnect\": \"Нажмите кнопку ниже, чтобы Stardrop забыл ваш личный ключ и отключился от Nexus Mods.\",\n  \"ui.nexus_login.labels.nexus_disconnect_actual\": \"Нажмите кнопку ниже, чтобы Stardrop забыл ваш API-ключ Stardrop и отключился от Nexus Mods.\",\n  \"ui.nexus_login.labels.username\": \"Имя пользователя: {0}\",\n  \"ui.nexus_login.labels.is_premium\": \"Является ли пользователь премиум-пользователем: {0}\",\n\n  // Warnings\n  \"ui.warning.unable_to_add_profile\": \"Не удалось добавить {0}, профиль уже существует под этим именем!\",\n  \"ui.warning.given_mod_folder_does_not_exist\": \"Указанный путь к папке мода не существует.\\n\\nВозвращение к предыдущему пути.\",\n  \"ui.warning.given_install_folder_not_exist\": \"Указанный путь к установленной папке модов Stardrop не существует.\\n\\nВозвращение к предыдущему пути.\",\n  \"ui.warning.given_install_folder_not_exist_default\": \"Указанный путь к установленной папке модов Stardrop не существует.\\n\\nВозвращение к пути по умолчанию.\",\n  \"ui.warning.given_install_folder_not_under_mod_folder\": \"Указанная установленная папка модов Stardrop не существует в {0}\\n\\nВозвращение к предыдущему пути.\",\n  \"ui.warning.given_invalid_smapi_executable\": \"Указанный файл не является {0}\\n\\nВозвращение к предыдущему пути.\",\n  \"ui.warning.stardrop_locked\": \"Stardrop заблокирован, пока работает SMAPI. Любые сделанные изменения не будут отображаться, пока SMAPI не будет закрыт.\",\n  \"ui.warning.stardrop_downloading\": \"Загрузка последней версии Stardrop.\",\n  \"ui.warning.SMAPI_downloading\": \"Загрузка последней версии SMAPI с GitHub.\",\n  \"ui.warning.SMAPI_installing\": \"Обновление SMAPI до последней версии.\",\n  \"ui.warning.SMAPI_up_to_date\": \"SMAPI находится в актуальном состоянии.\\n\\n{0} - последняя версия.\",\n  \"ui.warning.mod_installing\": \"Установка {0}\",\n  \"ui.warning.mod_updating\": \"Обновление {0}\",\n\n  \"ui.warning.unable_to_locate_smapi\": \"Не удается найти StardewModdingAPI.exe\\n\\nУстановите правильный путь к файлу в меню\\nПросмотр > Настройки.\",\n  \"ui.warning.mod_config_saved_but_not_enabled\": \"Настройки конфигурации мода сохранены для профиля {0}, однако настройки конфигурации модов для конкретного профиля не включены.\\n\\nПерейдите в меню Просмотр > Настройки и установите флажок \\\"Включить настройки конфигурации мода для конкретного профиля\\\", чтобы Stardrop автоматически сохранял и применял изменения в конфигурации мода.\",\n  \"ui.warning.unable_to_determine_profile\": \"Не удалось определить выбранный профиль.\\n\\nSMAPI не будет запущен.\",\n  \"ui.warning.stardrop_up_to_date\": \"Stardrop находится в актуальном состоянии.\\n\\n{0} - последняя версия.\",\n  \"ui.warning.stardrop_unable_to_download_latest\": \"Не удалось загрузить последнюю версию Stardrop.\\n\\nПожалуйста, загрузите последнюю версию вручную.\",\n  \"ui.warning.stardrop_unable_to_find_latest\": \"Не удалось проверить репозиторий GitHub для обновления Stardrop.\\n\\nПожалуйста, проверьте GitHub на возможное обновление или повторите попытку позже.\",\n  \"ui.warning.stardrop_update_downloaded\": \"Последняя версия успешно загружена.\\n\\nStardrop будет перезапущен.\",\n  \"ui.warning.SMAPI_unable_to_download_latest\": \"Не удалось загрузить последнюю версию SMAPI.\\n\\nПожалуйста, загрузите последнюю версию вручную.\",\n  \"ui.warning.SMAPI_unable_to_install_latest\": \"Не удалось установить последнюю версию SMAPI.\\n\\nПожалуйста, загрузите последнюю версию вручную.\",\n  \"ui.warning.update_cooldown_minutes\": \"Обновления можно запрашивать только раз в 5 минут.\\n\\nПожалуйста, повторите попытку через {0} минут(ы).\",\n  \"ui.warning.update_cooldown_seconds\": \"Обновления можно запрашивать только раз в 5 минут.\\n\\nПожалуйста, повторите попытку через {0} секунд(ы).\",\n  \"ui.warning.unable_to_locate_log\": \"Не удалось найти файл SMAPI-latest.txt! SMAPI должен успешно запуститься хотя бы один раз, чтобы Stardrop мог обнаружить детали игры.\",\n  \"ui.warning.unable_to_read_log\": \"Не удалось прочитать файл журнала SMAPI для получения версии игры.\\n\\nМодификации не будут проверяться на наличие обновлений.\",\n  \"ui.warning.no_manifest\": \"В \\\"{0}\\\" не найден manifest.json\",\n  \"ui.warning.unable_to_load_mod\": \"Не удалось загрузить файл, расположенный в \\\"{0}\\\".\\n\\nСмотрите файл журнала для получения дополнительной информации.\",\n  \"ui.warning.unable_to_validate_nexus_key\": \"Не удалось проверить ключ API Nexus Mod.\\n\\nПожалуйста, убедитесь, что ключ правильный, и повторите попытку.\",\n  \"ui.warning.download_without_premium\": \"Чтобы загрузить через Stardrop, вам необходимо иметь премиум-аккаунт на Nexus Mods.\",\n  \"ui.warning.no_downloads_available\": \"В настоящее время нет модификаций с обновлениями, доступными на Nexus Mods.\",\n  \"ui.warning.failed_nexus_install\": \"Не удалось установить модификацию {0} из-за проблемы со связью с Nexus Mods.\\n\\nПожалуйста, повторите попытку.\",\n  \"ui.warning.unable_nexus_install\": \"Не удалось установить мод {0}.\",\n  \"ui.warning.unable_to_endorse\": \"Не удалось установить статус одобрения:\\n\\n{0}\",\n  \"ui.warning.mod_owned\": \"Вы являетесь владельцем этого мода.\",\n  \"ui.warning.too_soon_after_download\": \"Вы должны подождать 15 минут после загрузки этого мода.\\n\\nПопытка одобрения сбросит этот таймер.\",\n  \"ui.warning.not_downloaded\": \"Вы должны загрузить этот мод с Nexus Mods, чтобы его одобрить.\",\n  \"ui.warning.failed_to_get_mod_details\": \"Не удалось получить данные мода из ссылки NXM: {0}\",\n  \"ui.warning.failed_to_get_download_link\": \"Не удалось получить ссылку для загрузки с Nexus Mods через ссылку NXM: {0}\",\n  \"ui.warning.failed_to_set_association\": \"Не удалось зарегистрировать протокол NXM в Stardrop.\\n\\nСмотрите журнал для получения подробной информации.\",\n  \"ui.warning.already_associated\": \"Stardrop уже связан с протоколом NXM.\",\n\n  // Messages\n  \"ui.message.confirm_mod_deletion\": \"Вы уверены, что хотите удалить {0}? Это действие нельзя будет отменить.\",\n  \"ui.message.unsaved_config_changes\": \"Для профиля {0} обнаружены несохраненные изменения конфигурации модов.\\n\\nХотели бы вы сохранить изменения?\",\n  \"ui.message.stardrop_update_available\": \"Доступно обновление (v{0}) для Stardrop.\\n\\nХотели бы вы загрузить его сейчас?\",\n  \"ui.message.SMAPI_update_available\": \"Доступно обновление (v{0}) для SMAPI.\\n\\nХотели бы вы загрузить его сейчас?\",\n  \"ui.message.stardrop_update_complete\": \"Stardrop успешно обновлен.\\n\\nХотели бы вы просмотреть примечания для этого обновления?\",\n  \"ui.message.SMAPI_update_complete\": \"SMAPI успешно обновлен.\\n\\nХотели бы вы просмотреть примечания для этого обновления?\",\n  \"ui.message.confirm_bulk_change_mod_states_enable\": \"Включить все моды?\\n\\nПримечание: Это действие нельзя будет отменить.\",\n  \"ui.message.confirm_bulk_change_mod_states_disable\": \"Отключить все моды?\\n\\nПримечание: Это действие нельзя будет отменить.\",\n  \"ui.message.confirm_mod_update_method\": \"Обнаружена предыдущая версия {0}. Хотели бы вы удалить предыдущую установку?\\n\\nПримечание: Удаление предыдущих версий как правило рекомендуется, но это приведет к потере файлов конфигурации.\",\n  \"ui.message.confirm_mod_update_method_no_config\": \"Обнаружена предыдущая версия {0}. Хотели бы вы удалить предыдущую установку?\\n\\nПримечание: Удаление предыдущих версий как правило рекомендуется.\",\n  \"ui.message.confirm_mod_update_method_preserved\": \"Обнаружена предыдущая версия {0}. Хотели бы вы удалить предыдущую установку?\\n\\nПримечание: Удаление предыдущих версий как правило рекомендуется, и ваши файлы конфигурации будут восстановлены.\",\n  \"ui.message.confirm_nxm_association\": \"Хотели бы вы связать протокол NXM с Stardrop?\\n\\nЭто позволит Stardrop автоматически устанавливать моды, загруженные с Nexus Mods.\",\n  \"ui.message.confirm_nxm_install\": \"Хотели бы вы установить следующий мод:\\n\\n{0}\\n\\nВы можете отключить это подтверждение в меню настроек.\",\n  \"ui.message.require_nexus_login\": \"Для этого действия необходимо войти в систему Nexus Mods API.\",\n  \"ui.message.succeeded_nexus_install\": \"Успешно установлен следующий мод через Nexus Mods:\\n\\n{0}\",\n\n  // Window Names\n  \"ui.window.settings.name\": \"Настройки\",\n  \"ui.window.profiles.name\": \"Профили\",\n  \"ui.window.profile_naming.name\": \"Название профиля\",\n  \"ui.window.nexus_login.name\": \"Личный ключ API Nexus Mods\",\n  \"ui.window.nexus_login_actual.name\": \"Ключ API Nexus Mods\",\n  \"ui.window.nexus_user.name\": \"Данные пользователя Nexus Mods\",\n\n  // Internals\n  \"internal.connected\": \"Подключено\",\n  \"internal.disconnected\": \"Отключено\",\n  \"internal.unknown\": \"Неизвестно\",\n  \"internal.enable\": \"Включить\",\n  \"internal.disable\": \"Выключить\",\n  \"internal.yes\": \"Да\",\n  \"internal.yes_all\": \"Да для всех\",\n  \"internal.no\": \"Нет\",\n  \"internal.no_all\": \"Нет для всех\",\n  \"internal.ok\": \"OK\",\n  \"internal.unlock\": \"Разблокировать\"\n}\n"
  },
  {
    "path": "Stardrop/i18n/th.json",
    "content": "{\n  // Main Window - Menu Items\n  \"ui.main_window.menu_items.add_mod\": \"เพิ่มม็อด\",\n  \"ui.main_window.menu_items.add_mods\": \"เพิ่มม็อด\",\n  \"ui.main_window.menu_items.start_smapi\": \"เริ่ม SMAPI\",\n  \"ui.main_window.menu_items.exit\": \"ปิด\",\n  \"ui.main_window.menu_items.settings\": \"ตั้งค่า\",\n  \"ui.main_window.menu_items.stardrop_log_file\": \"ไฟล์บันทึก Stardrop\",\n  \"ui.main_window.menu_items.smapi_log_file\": \"ไฟล์บันทึก SMAPI\",\n  \"ui.main_window.menu_items.refresh_mod_list\": \"รีเฟรชรายการม็อด\",\n  \"ui.main_window.menu_items.check_for_mod_updates\": \"ตรวจสอบการอัปเดตม็อด\",\n  \"ui.main_window.menu_items.enable_all_mods\": \"เปิดใช้งานม็อดทั้งหมด\",\n  \"ui.main_window.menu_items.disable_all_mods\": \"ปิดการใช้งานม็อดทั้งหมด\",\n  \"ui.main_window.menu_items.check_for_stardrop_update\": \"ตรวจสอบการอัปเดต Stardrop\",\n  \"ui.main_window.menu_items.stardrop_repository\": \"พื้นที่เก็บข้อมูล Stardrop\",\n  \"ui.main_window.menu_items.nexus_install_all\": \"ติดตั้งการอัปเดตทั้งหมด\",\n  \"ui.main_window.menu_items.nexus_connection\": \"การเชื่อมต่อ API\",\n  \"ui.main_window.menu_items.check_for_SMAPI_update\": \"ตรวจสอบการอัปเดต SMAPI\",\n\n  // Main Window - Menu Items - Context\n  \"ui.main_window.menu_items.context.columns.show_hide\": \"แสดง / ซ่อนคอลัมน์\",\n  \"ui.main_window.menu_items.context.hide_requirements\": \"ซ่อนความต้องการของม็อด\",\n  \"ui.main_window.menu_items.context.cancel\": \"ยกเลิก\",\n  \"ui.main_window.menu_items.context.open_containing_folder\": \"เปิดโฟลเดอร์\",\n  \"ui.main_window.menu_items.context.open_mod_page\": \"เปิดหน้าเพจม็อด\",\n  \"ui.main_window.menu_items.context.open_config\": \"เปิดไฟล์กำหนดค่า\",\n  \"ui.main_window.menu_items.context.show_whole_mod_group\": \"แสดงกลุ่มม็อดทั้งหมด\",\n  \"ui.main_window.menu_items.context.show_authors_mods\": \"แสดงม็อดของผู้เขียน\",\n  \"ui.main_window.menu_items.context.open\": \"เปิด...\",\n  \"ui.main_window.menu_items.context.filters\": \"กรอง\",\n  \"ui.main_window.menu_items.context.options\": \"ตัวเลือก\",\n  \"ui.main_window.menu_items.context.clear_filter\": \"ล้างข้อมูล\",\n  \"ui.main_window.menu_items.context.delete\": \"ลบ\",\n\n  // Main Window - Menu Headers\n  \"ui.main_window.menu_headers.file\": \"ไฟล์\",\n  \"ui.main_window.menu_headers.view\": \"มุมมอง\",\n  \"ui.main_window.menu_headers.tools\": \"เครื่องมือ\",\n  \"ui.main_window.menu_headers.nexus\": \"Nexus Mods\",\n\n  \"ui.main_window.menu_headers.enabled\": \"เปิดใช้งาน\",\n  \"ui.main_window.menu_headers.mod_name\": \"ชื่อม็อด\",\n  \"ui.main_window.menu_headers.unique_id\": \"ID เฉพาะ\",\n  \"ui.main_window.menu_headers.author\": \"ผู้เขียน\",\n  \"ui.main_window.menu_headers.requirements\": \"ความต้องการ\",\n  \"ui.main_window.menu_headers.missing_requirements\": \"ม็อดที่ไม่ได้ติดตั้ง\",\n  \"ui.main_window.menu_headers.version\": \"เวอร์ชัน\",\n  \"ui.main_window.menu_headers.configuration\": \"การกำหนดค่า\",\n  \"ui.main_window.menu_headers.status\": \"สถานะ\",\n  \"ui.main_window.menu_headers.endorsement\": \"การรับรอง\",\n  \"ui.main_window.menu_headers.nexus_install\": \"การอัปเดต Nexus Mods\",\n  \"ui.main_window.menu_headers.install_timestamp\": \"Install Timestamp\",\n  \"ui.main_window.menu_headers.last_updated\": \"อัปเดตล่าสุด\",\n\n  // Main Window - Labels\n  \"ui.main_window.labels.profile\": \"โปรไฟล์\",\n  \"ui.main_window.labels.enabled_mods\": \"ม็อดที่เปิดใช้งาน: \",\n  \"ui.main_window.labels.total_mods\": \"ม็อดทั้งหมด: \",\n  \"ui.main_window.labels.updatable_mods\": \"ม็อดที่พร้อมสำหรับการอัปเดต: \",\n  \"ui.main_window.labels.filter\": \"กรอง\",\n  \"ui.main_window.labels.unknown_SMAPI\": \"เวอร์ชัน SMAPI ที่ไม่รู้จัก\",\n\n  // Main Window - Hyperlinks\n  \"ui.main_window.hyperlinks.click_to_open\": \"คลิกเพื่อเปิด\",\n  \"ui.main_window.hyperlinks.update_available\": \"มีการอัปเดต ({0})\",\n  \"ui.main_window.hyperlinks.unofficial_update_available\": \"มีการอัปเดตอย่างไม่เป็นทางการ ({0})\",\n  \"ui.main_window.hyperlinks.broken_compatibility_issue\": \"[ใช้งานไม่ได้] ปัญหาความเข้ากันได้\",\n  \"ui.main_window.hyperlinks.install_update\": \"ติดตั้งอัปเดต\",\n  \"ui.main_window.hyperlinks.downloading\": \"กำลังดาวน์โหลด...\",\n  \"ui.main_window.hyperlinks.installing\": \"กำลังติดตั้ง...\",\n\n  // Main Window - Buttons\n  \"ui.main_window.buttons.save_configs\": \"บันทึกการกำหนดค่า\",\n  \"ui.main_window.buttons.save_profile\": \"บันทึกโปรไฟล์\",\n  \"ui.main_window.buttons.show_updatable_mods\": \"แสดงม็อดที่อัปเดตได้\",\n  \"ui.main_window.buttons.active_search_filters\": \"{0} ตัวกรองมีการใช้งาน\",\n  \"ui.main_window.buttons.no_search_filters\": \"ไม่มีตัวกรองที่ใช้งานอยู่\",\n\n  // Main Window - Buttons - Update Status\n  \"ui.main_window.button.update_status.generic\": \"ม็อดพร้อมที่จะอัปเดต: คลิกเพื่อรีเฟรช\",\n  \"ui.main_window.button.update_status.updating\": \"กำลังอัปเดต...\",\n  \"ui.main_window.button.update_status.list_available_updates\": \"ม็อดพร้อมที่จะอัปเดต: {0}\",\n  \"ui.main_window.button.update_status.failed\": \"ตรวจสอบการอัปเดตม็อดล้มเหลว\",\n\n  // Main Window - Buttons - Downloads\n  \"ui.main_window.buttons.downloads.label\": \"ดาวน์โหลด ({0})\",\n\n  // Main Window - ComboBox\n  \"ui.main_window.combobox.show_all_mods\": \"แสดงม็อดทั้งหมด\",\n  \"ui.main_window.combobox.show_mods_with_configs\": \"แสดงม็อดที่กำหนดค่าได้\",\n  \"ui.main_window.combobox.show_disabled_mods\": \"แสดงม็อดที่ปิดใช้งาน\",\n  \"ui.main_window.combobox.show_enabled_mods\": \"แสดงม็อดที่เปิดใช้งาน\",\n\n  // Main Window - ComboBox (ListBox)\n  \"ui.main_window.combobox.mod_name\": \"ชื่อม็อด\",\n  \"ui.main_window.combobox.author\": \"ผู้เขียน\",\n  \"ui.main_window.combobox.requirements\": \"ความต้องการ\",\n  \"ui.main_window.combobox.group\": \"กลุ่มม็อด\",\n\n  // Settings Window - Labels\n  \"ui.settings_window.labels.smapi_path\": \"เส้นทางของ SMAPI\",\n  \"ui.settings_window.labels.mod_path\": \"เส้นทางของโฟลเดอร์ม็อด\",\n  \"ui.settings_window.labels.mod_install\": \"เส้นทางม็อดที่ถูกติดตั้งผ่าน Stardrop\",\n  \"ui.settings_window.labels.themes\": \"ธีม\",\n  \"ui.settings_window.labels.languages\": \"ภาษา\",\n  \"ui.settings_window.labels.grouping\": \"การจัดกลุ่มม็อด\",\n  \"ui.settings_window.labels.miscellaneous\": \"เบ็ดเตล็ด\",\n  \"ui.settings_window.labels.nexus\": \"Nexus Mods\",\n  \"ui.settings_window.labels.preferred_server\": \"เซิร์ฟเวอร์ที่ต้องการ:\",\n\n  // Settings Window - Buttons\n  \"ui.settings_window.buttons.ignore_hidden_folders\": \"ละเว้นโฟลเดอร์ที่ซ่อนอยู่\",\n  \"ui.settings_window.buttons.enable_profile_specific_mod_configs\": \"เปิดใช้งานการกำหนดค่าม็อดเฉพาะโปรไฟล์\",\n  \"ui.settings_window.buttons.enable_mods_on_add\": \"เปิดใช้งานม็อดโดยอัตโนมัติเมื่อติดตั้ง\",\n  \"ui.settings_window.buttons.always_ask_to_delete\": \"ขอให้ลบไฟล์ม็อดในการอัปเดตเสมอ\",\n  \"ui.settings_window.buttons.automatically_save_profile_changes\": \"บันทึกการเปลี่ยนแปลงโปรไฟล์โดยอัตโนมัติ\",\n  \"ui.settings_window.buttons.always_ask_for_NXM_installs\": \"ถามทุกครั้งก่อนติดตั้งไฟล์ NXM\",\n  \"ui.settings_window.buttons.register_nxm_association\": \"ลงทะเบียนการเชื่อมโยง NXM\",\n\n  // Settings Window - Tooltips\n  \"ui.settings_window.tooltips.smapi\": \"เส้นทางของไฟล์ StardewModdingAPI\",\n  \"ui.settings_window.tooltips.mod_folder_path\": \"เส้นทางของโฟลเดอร์ม็อด\",\n  \"ui.settings_window.tooltips.mod_install_path\": \"เส้นทางโฟลเดอร์ที่ Stardrop จะวางม็อดที่เพิ่งติดตั้งใหม่ ต้องอยู่ภายใต้โฟลเดอร์ที่ระบุใน \\\"เส้นทางของโฟลเดอร์ม็อด\\\"\",\n  \"ui.settings_window.tooltips.theme\": \"ธีมปัจจุบันของ Stardrop\",\n  \"ui.settings_window.tooltips.language\": \"ภาษาปัจจุบันของ Stardrop\",\n  \"ui.settings_window.tooltips.grouping\": \"วิธีการปัจจุบันสำหรับการจัดกลุ่มม็อด\",\n  \"ui.settings_window.tooltips.ignore_hidden_folders\": \"หากเลือกไว้ Stardrop จะไม่สนใจม็อดใดๆ ที่มีโฟลเดอร์หลักที่ขึ้นต้นด้วย \\\".\\\"\",\n  \"ui.settings_window.tooltips.nxm_file_association\": \"ลงทะเบียนโปรโตคอล NXM เพื่อเชื่อมโยงกับ Stardrop ช่วยให้สามารถติดตั้งได้โดยตรงจาก www.nexusmods.com\",\n  \"ui.settings_window.tooltips.always_ask_nxm_files\": \"หากเลือกตัวเลือกนี้ Stardrop จะถามก่อนติดตั้งม็อดจาก www.nexusmods.com ผ่าน NXM เสมอ\",\n  \"ui.settings_window.tooltips.enable_profile_specific_configs\": \"หากเลือกไว้ Stardrop จะบันทึกและกู้คืนไฟล์ config.json จากม็อดเมื่อสลับโปรไฟล์\",\n  \"ui.settings_window.tooltips.enable_mods_on_add\": \"หากเลือกตัวเลือกนี้ Stardrop จะเปิดใช้งานม็อดที่เพิ่มหรืออัปเดตใหม่โดยอัตโนมัติ\",\n  \"ui.settings_window.tooltips.always_ask_to_delete\": \"หากเลือกตัวเลือกนี้ Stardrop จะถามเสมอว่าจะลบไฟล์ม็อดหรือไม่ เมื่อทำการอัปเดตม็อด\",\n  \"ui.settings_window.tooltips.automatically_save_profile_changes\": \"หากเลือกตัวเลือกนี้ Stardrop จะบันทึกการเปลี่ยนแปลงที่ทำกับโปรไฟล์โดยอัตโนมัติ\",\n  \"ui.settings_window.tooltips.preferred_server\": \"ตั้งค่าเซิร์ฟเวอร์ที่คุณต้องการใช้เมื่อดาวน์โหลดจาก Nexus Mods\",\n  \"ui.settings_window.tooltips.save_changes\": \"บันทึกการเปลี่ยนแปลง\",\n  \"ui.settings_window.tooltips.cancel_changes\": \"ยกเลิก\",\n\n  // Nexus Login Window - Buttons\n  \"ui.nexus_login.buttons.nexus\": \"ไปที่ Nexus Mods\",\n  \"ui.nexus_login.buttons.disconnect\": \"ตัดการเชื่อมต่อจาก Nexus\",\n\n  // Nexus Login Window - Labels\n  \"ui.nexus_login.labels.note\": \"โปรดทราบ\",\n  \"ui.nexus_login.labels.get_key\": \"รับคีย์ API ส่วนบุคคลของคุณจาก Nexus Mods\",\n  \"ui.nexus_login.labels.get_key_actual\": \"รับคีย์ Stardrop API ของคุณจาก Nexus Mods\",\n  \"ui.nexus_login.labels.paste\": \"จากนั้นวางคีย์ด้านล่าง\",\n  \"ui.nexus_login.labels.share_warning\": \"อย่าแชร์คีย์ API ส่วนตัวของคุณกับผู้อื่น การทำเช่นนี้อาจส่งผลให้มีการดำเนินการกับบัญชี Nexus ของคุณ\",\n  \"ui.nexus_login.labels.share_warning_actual\": \"อย่าแชร์คีย์ API ของคุณกับผู้อื่น การทำเช่นนี้อาจส่งผลให้มีการดำเนินการกับบัญชี Nexus ของคุณ\",\n  \"ui.nexus_login.labels.revoke\": \"คุณสามารถรีเซ็ต / เพิกถอนคีย์ของคุณได้ตลอดเวลา โดยไปที่ My Nexus Account > API > Personal API Key\",\n  \"ui.nexus_login.labels.revoke_actual\": \"คุณสามารถรีเซ็ต / เพิกถอนคีย์ Stardrop API ของคุณได้ตลอดเวลา โดยไปที่ My Nexus Account > API > Stardrop\",\n  \"ui.nexus_login.labels.nexus_details\": \"รายละเอียดผู้ใช้ Nexus Mods\",\n  \"ui.nexus_login.labels.nexus_disconnect\": \"คลิกปุ่มด้านล่างเพื่อให้ Stardrop ลืมคีย์ส่วนตัวของคุณ และยกเลิกการเชื่อมต่อกับ Nexus Mods\",\n  \"ui.nexus_login.labels.nexus_disconnect_actual\": \"คลิกปุ่มด้านล่างเพื่อให้ Stardrop ลืมคีย์ Stardrop API ของคุณ และยกเลิกการเชื่อมต่อกับ Nexus Mods\",\n  \"ui.nexus_login.labels.username\": \"ชื่อผู้ใช้: {0}\",\n  \"ui.nexus_login.labels.is_premium\": \"เป็นสมาชิกพรีเมี่ยม: {0}\",\n\n  // Downloads Panel - Labels\n  \"ui.downloads_panel.download_canceled\": \"ยกเลิกแล้ว\",\n  \"ui.downloads_panel.download_failed\": \"ล้มเหลว\",\n  \"ui_downloads_panel.download_success\": \"เสร็จสมบูรณ์!\",\n  \"ui.downloads_panel.no_downloads_label\":  \"ไม่มีการดาวน์โหลดอยู่ระหว่างดำเนินการ\",\n\n  // Downloads Panel - Tooltips\n  \"ui.downloads_panel.tooltips.cancel_button\": \"ยกเลิก\",  \n  \"ui.downloads_panel.tooltips.remove_button\": \"ลบจากประวัติ\", // Tooltip for the button that clears out a completed or failed download\n\n  // Warnings\n  \"ui.warning.unable_to_add_profile\": \"ไม่สามารถเพิ่ม {0} โปรไฟล์มีอยู่แล้วภายใต้ชื่อนั้น!\",\n  \"ui.warning.given_mod_folder_does_not_exist\": \"ไม่มีเส้นทางของโฟลเดอร์ม็อดที่ระบุ\\n\\nกำลังย้อนกลับไปยังเส้นทางก่อนหน้า\",\n  \"ui.warning.given_install_folder_not_exist\": \"ไม่มีเส้นทางโฟลเดอร์ม็อดที่ถูกติดตั้งผ่าน Stardrop ที่ระบุ\\n\\nกำลังเปลี่ยนกลับเป็นเส้นทางก่อนหน้า\",\n  \"ui.warning.given_install_folder_not_exist_default\": \"ไม่มีเส้นทางโฟลเดอร์ม็อดที่ถูกติดตั้งผ่าน Stardrop ที่ระบุ\\n\\nกำลังเปลี่ยนกลับเป็นเส้นทางเริ่มต้น\",\n  \"ui.warning.given_install_folder_not_under_mod_folder\": \"เส้นทางโฟลเดอร์ม็อดที่ติดตั้งผ่าน Stardrop ที่ระบุ ไม่มีอยู่ใน {0}\\n\\nกำลังเปลี่ยนกลับเป็นเส้นทางก่อนหน้า\",\n  \"ui.warning.given_invalid_smapi_executable\": \"ไฟล์ที่ระบุไม่ใช่ {0}\\n\\nกำลังย้อนกลับไปยังเส้นทางก่อนหน้า\",\n  \"ui.warning.stardrop_locked\": \"Stardrop ถูกล็อกในขณะที่ SMAPI กำลังทำงาน การเปลี่ยนแปลงใดๆ ที่ทำขึ้นจะไม่มีผลจนกว่า SMAPI จะปิดลง\",\n  \"ui.warning.stardrop_downloading\": \"กำลังดาวน์โหลด Stardrop เวอร์ชันล่าสุด\",\n  \"ui.warning.SMAPI_downloading\": \"กำลังดาวน์โหลด SMAPI เวอร์ชันล่าสุดจาก GitHub\",\n  \"ui.warning.SMAPI_installing\": \"กำลังอัปเดต SMAPI เป็นเวอร์ชันล่าสุด\",\n  \"ui.warning.SMAPI_up_to_date\": \"SMAPI เป็นเวอร์ชันล่าสุดแล้ว\\n\\n{0} เป็นเวอร์ชันล่าสุด\",\n  \"ui.warning.install_mod_attempt_count\": \"กำลังพยายามติดตั้ง {0} ม็อด\",\n  \"ui.warning.mod_installing\": \"กำลังติดตั้ง {0}\",\n  \"ui.warning.mod_updating\": \"กำลังอัปเดต {0}\",\n  \"ui.warning.mod_deleting\": \"กำลังลบอินสแตนซ์เก่าของ {0}\",\n  \"ui.warning.failed_to_delete\": \"ลบ {0} ไม่สำเร็จ\\n\\nไฟล์ม็อดอาจถูกล็อก โปรดดูรายละเอียดในบันทึก\",\n  \"ui.warning.failed_to_delete_during_update\": \"ไม่สามารถลบอินสแตนซ์เก่าของ {0} ได้\\n\\nม็อดอาจติดตั้งไม่ถูกต้อง\",\n  \"ui.warning.unable_to_locate_smapi\": \"ไม่พบ StardewModdingAPI.exe\\n\\nโปรดตั้งค่าเส้นทางไฟล์ที่ถูกต้องใน\\nมุมมอง > ตั้งค่า\",\n  \"ui.warning.mod_config_saved_but_not_enabled\": \"บันทึกการตั้งค่าการกำหนดค่าม็อดไปที่โปรไฟล์ {0} อย่างไรก็ตาม การกำหนดค่าม็อดเฉพาะโปรไฟล์ไม่ได้เปิดใช้งาน\\n\\nไปที่ มุมมอง > ตั้งค่า และตรวจสอบ \\\"เปิดใช้งานการกำหนดค่าม็อดเฉพาะโปรไฟล์\\\" เพื่อให้ Stardrop บันทึกและใช้การเปลี่ยนแปลงการกำหนดค่าม็อดโดยอัตโนมัติ\",\n  \"ui.warning.unable_to_determine_profile\": \"ไม่สามารถระบุโปรไฟล์ที่เลือกได้\\n\\nSMAPI จะไม่เริ่มทำงาน\",\n  \"ui.warning.stardrop_up_to_date\": \"Stardrop เป็นข้อมูลล่าสุด\\n\\n{0} เป็นเวอร์ชันล่าสุด\",\n  \"ui.warning.stardrop_unable_to_download_latest\": \"ไม่สามารถดาวน์โหลดรุ่นล่าสุดสำหรับ Stardrop ได้\\n\\nโปรดดาวน์โหลดเวอร์ชันล่าสุดด้วยตนเอง\",\n  \"ui.warning.stardrop_unable_to_find_latest\": \"ไม่สามารถตรวจสอบพื้นที่เก็บข้อมูล GitHub ของ Stardrop เพื่อดูการอัปเดตได้\\n\\nโปรดตรวจสอบ GitHub เพื่อดูการอัปเดตที่เป็นไปได้ หรือลองอีกครั้งในภายหลัง\",\n  \"ui.warning.stardrop_update_downloaded\": \"ดาวน์โหลดเวอร์ชันล่าสุดเรียบร้อยแล้ว\\n\\nStardrop จะรีสตาร์ท\",\n  \"ui.warning.SMAPI_unable_to_download_latest\": \"ไม่สามารถดาวน์โหลด SMAPI รุ่นล่าสุดได้\\n\\nโปรดดาวน์โหลดเวอร์ชันล่าสุดด้วยตนเอง\",\n  \"ui.warning.SMAPI_unable_to_install_latest\": \"ไม่สามารถติดตั้ง SMAPI รุ่นล่าสุดได้\\n\\nโปรดดาวน์โหลดเวอร์ชันล่าสุดด้วยตนเอง\",\n  \"ui.warning.update_cooldown_minutes\": \"สามารถขออัปเดตได้ทุกๆ 5 นาทีเท่านั้น\\n\\nโปรดลองอีกครั้งใน {0} นาที\",\n  \"ui.warning.update_cooldown_seconds\": \"สามารถขออัปเดตได้ทุกๆ 5 นาทีเท่านั้น\\n\\nโปรดลองอีกครั้งใน {0} วินาที\",\n  \"ui.warning.unable_to_locate_log\": \"ไม่พบ SMAPI-latest.txt! จะต้องเปิดใช้งาน SMAPI อย่างน้อย 1 ครั้ง เพื่อให้ Stardrop ตรวจพบรายละเอียดของเกม\",\n  \"ui.warning.unable_to_read_log\": \"ไม่สามารถอ่านไฟล์บันทึกของ SMAPI เพื่อหาเวอร์ชันเกม\\n\\nม็อดจะไม่ถูกตรวจสอบการอัปเดต\",\n  \"ui.warning.no_manifest\": \"ไม่พบ manifest.json ใน \\\"{0}\\\"\",\n  \"ui.warning.unable_to_load_mod\": \"ไม่สามารถโหลดไฟล์ที่อยู่ใน \\\"{0}\\\"\\n\\nดูไฟล์บันทึกสำหรับข้อมูลเพิ่มเติม\",\n  \"ui.warning.unable_to_validate_nexus_key\": \"ไม่สามารถตรวจสอบคีย์ Nexus Mod API ได้\\n\\nโปรดตรวจสอบความถูกต้องของคีย์แล้วลองอีกครั้ง\",\n  \"ui.warning.download_without_premium\": \"คุณต้องเป็นสมาชิก Nexus Mods ระดับพรีเมียมจึงจะดาวน์โหลดผ่าน Stardrop ได้\",\n  \"ui.warning.no_downloads_available\": \"ขณะนี้ไม่มีม็อดที่มีการอัปเดตจาก Nexus Mods\",\n  \"ui.warning.failed_nexus_install\": \"ไม่สามารถติดตั้งม็อด {0} เนื่องจากปัญหาการเชื่อมต่อกับ Nexus Mods\\n\\nโปรดลองอีกครั้ง\",\n  \"ui.warning.unable_nexus_install\": \"ไม่สามารถติดตั้งม็อด {0}\",\n  \"ui.warning.unable_to_endorse\": \"ไม่สามารถตั้งค่าสถานะการรับรอง:\\n\\n{0}\",\n  \"ui.warning.mod_owned\": \"คุณเป็นเจ้าของม็อดนี้\",\n  \"ui.warning.too_soon_after_download\": \"คุณต้องรอ 15 นาทีหลังจากดาวน์โหลดม็อดนี้\\n\\nการพยายามรับรองจะรีเซ็ตตัวจับเวลานี้\",\n  \"ui.warning.not_downloaded\": \"คุณต้องดาวน์โหลดม็อดนี้จาก Nexus Mods เพื่อรับรอง\",\n  \"ui.warning.failed_to_get_mod_details\": \"ไม่สามารถรับรายละเอียดม็อดจากลิงก์ NXM: {0}\",\n  \"ui.warning.failed_to_get_download_link\": \"ไม่สามารถรับ URL ดาวน์โหลดจาก Nexus Mods ผ่านลิงก์ NXM: {0}\",\n  \"ui.warning.failed_to_set_association\": \"ลงทะเบียนโปรโตคอล NXM กับ Stardrop ไม่สำเร็จ\\n\\nดูรายละเอียดในบันทึก\",\n  \"ui.warning.already_associated\": \"Stardrop เชื่อมโยงกับโปรโตคอล NXM แล้ว\",\n\n  // Messages\n  \"ui.message.confirm_mod_deletion\": \"คุณแน่ใจหรือว่าต้องการลบ {0}? การดำเนินการนี้ไม่สามารถยกเลิกได้\",\n  \"ui.message.unsaved_config_changes\": \"ตรวจพบการเปลี่ยนแปลงการกำหนดค่าม็อด ที่ไม่ได้บันทึกสำหรับโปรไฟล์ {0}\\n\\nคุณต้องการบันทึกการเปลี่ยนแปลงหรือไม่?\",\n  \"ui.message.stardrop_update_available\": \"มีการอัปเดต (v{0}) สำหรับ Stardrop\\n\\nคุณต้องการดาวน์โหลดตอนนี้?\",\n  \"ui.message.SMAPI_update_available\": \"มีการอัปเดต (v{0}) สำหรับ SMAPI แล้ว\\n\\nคุณต้องการดาวน์โหลดทันทีหรือไม่?\",\n  \"ui.message.stardrop_update_complete\": \"อัปเดต Stardrop สำเร็จแล้ว\\n\\nคุณต้องการดูบันทึกประจำรุ่นหรือไม่?\",\n  \"ui.message.SMAPI_update_complete\": \"อัปเดต SMAPI สำเร็จแล้ว\\n\\nคุณต้องการดูบันทึกประจำรุ่นหรือไม่?\",\n  \"ui.message.confirm_bulk_change_mod_states_enable\": \"เปิดใช้งานม็อดทั้งหมด?\\n\\nหมายเหตุ: การดำเนินการนี้ไม่สามารถยกเลิกได้\",\n  \"ui.message.confirm_bulk_change_mod_states_disable\": \"ปิดการใช้งานม็อดทั้งหมด?\\n\\nหมายเหตุ: การดำเนินการนี้ไม่สามารถยกเลิกได้\",\n  \"ui.message.confirm_mod_update_method\": \"ตรวจพบ {0} เวอร์ชันก่อนหน้าแล้ว คุณต้องการล้างการติดตั้งก่อนหน้านี้หรือไม่?\\n\\nหมายเหตุ: โดยปกติแล้ว แนะนำให้ล้างเวอร์ชันก่อนหน้า อย่างไรก็ตาม ไฟล์ปรับแต่งใดๆ จะสูญหายไป\",\n  \"ui.message.confirm_mod_update_method_no_config\": \"ตรวจพบเวอร์ชันก่อนหน้าของ {0} คุณต้องการล้างการติดตั้งก่อนหน้าหรือไม่?\\n\\nหมายเหตุ: โดยปกติแล้ว แนะนำให้ล้างเวอร์ชันก่อนหน้า\",\n  \"ui.message.confirm_mod_update_method_preserved\": \"ตรวจพบเวอร์ชันก่อนหน้าของ {0} คุณต้องการล้างการติดตั้งก่อนหน้าหรือไม่?\\n\\nหมายเหตุ: โดยทั่วไปแนะนำให้ล้างเวอร์ชันก่อนหน้า และไฟล์การกำหนดค่าของคุณจะถูกกู้คืน\",\n  \"ui.message.confirm_nxm_association\": \"คุณต้องการเชื่อมโยงโปรโตคอล NXM กับ Stardrop หรือไม่?\\n\\nซึ่งจะทำให้ Stardrop ติดตั้ง Mods ที่ดาวน์โหลดจาก Nexus Mods ได้โดยอัตโนมัติ\",\n  \"ui.message.confirm_nxm_install\": \"คุณต้องการติดตั้งม็อดต่อไปนี้:\\n\\n{0}\\n\\nคุณสามารถปิดการใช้งานการยืนยันนี้ได้ในเมนูการตั้งค่า\",\n  \"ui.message.require_nexus_login\": \"การดำเนินการนี้จำเป็นต้องลงชื่อเข้าใช้ Nexus Mods API\",\n  \"ui.message.succeeded_nexus_install\": \"ติดตั้งม็อดต่อไปนี้ผ่าน Nexus Mods สำเร็จแล้ว:\\n\\n{0}\",\n\n  // Window Names\n  \"ui.window.settings.name\": \"การตั้งค่า\",\n  \"ui.window.profiles.name\": \"โปรไฟล์\",\n  \"ui.window.profile_naming.name\": \"ชื่อโปรไฟล์\",\n  \"ui.window.nexus_login.name\": \"คีย์ API ส่วนบุคคลของ Nexus Mods\",\n  \"ui.window.nexus_login_actual.name\": \"คีย์ Nexus Mods API\",\n  \"ui.window.nexus_user.name\": \"รายละเอียดผู้ใช้ Nexus Mods\",\n\n  // Internals\n  \"internal.connected\": \"เชื่อมต่อแล้ว\",\n  \"internal.disconnected\": \"ตัดการเชื่อมต่อแล้ว\",\n  \"internal.unknown\": \"ไม่ทราบ\",\n  \"internal.enable\": \"เปิดใช้งาน\",\n  \"internal.disable\": \"ปิดการใช้งาน\",\n  \"internal.enable_whole_mod\": \"เปิดใช้งานกลุ่มม็อดทั้งหมด\",\n  \"internal.disable_whole_mod\": \"ปิดการใช้งานกลุ่มม็อดทั้งหมด\",\n  \"internal.yes\": \"ใช่\",\n  \"internal.yes_all\": \"ใช่ทั้งหมด\",\n  \"internal.no\": \"ไม่\",\n  \"internal.no_all\": \"ไม่ใช่ทั้งหมด\",\n  \"internal.ok\": \"ตกลง\",\n  \"internal.unlock\": \"ปลดล็อก\",\n\n  // Internals - Measurements\n  \"internal.measurements.bytes_size\": \"B\",\n  \"internal.measurements.bytes_per_second\": \"B/s\",\n  \"internal.measurements.kilobytes_size\": \"KB\",\n  \"internal.measurements.kilobytes_per_second\": \"KB/s\",\n  \"internal.measurements.megabytes_size\": \"MB\",\n  \"internal.measurements.megabytes_per_second\": \"MB/s\"\n}\n"
  },
  {
    "path": "Stardrop/i18n/tr.json",
    "content": "{\n\t// Main Window - Menu Items\n\t\"ui.main_window.menu_items.add_mod\": \"Mod Ekle\",\n\t\"ui.main_window.menu_items.start_smapi\": \"SMAPI'yi aç\",\n\t\"ui.main_window.menu_items.exit\": \"Çıkış\",\n\t\"ui.main_window.menu_items.settings\": \"Ayarlar\",\n\t\"ui.main_window.menu_items.stardrop_log_file\": \"Stardrop Log Dosyası\",\n\t\"ui.main_window.menu_items.smapi_log_file\": \"SMAPI Log Dosyası\",\n\t\"ui.main_window.menu_items.refresh_mod_list\": \"Mod Listesini Tazele\",\n\t\"ui.main_window.menu_items.check_for_mod_updates\": \"Mod Güncellemelerini Kontrol Et\",\n\t\"ui.main_window.menu_items.enable_all_mods\": \"Tüm Modları Etkinleştir\",\n\t\"ui.main_window.menu_items.disable_all_mods\": \"Tüm Modları Devre Dışı Bırak\",\n\t\"ui.main_window.menu_items.check_for_stardrop_update\": \"Stardrop Güncellemesini Kontrol Et\",\n\t\"ui.main_window.menu_items.stardrop_repository\": \"Stardrop GitHub Deposu\",\n\n\t// Main Window - Menu Items - Context\n\t\"ui.main_window.menu_items.context.show_requirements\": \"Gereksinimleri Gizle\",\n\t\"ui.main_window.menu_items.context.hide_requirements\": \"Gereksinimleri Göster\",\n\t\"ui.main_window.menu_items.context.cancel\": \"Vazgeç\",\n\t\"ui.main_window.menu_items.context.open_containing_folder\": \"İçeren Klasörü Aç\",\n\t\"ui.main_window.menu_items.context.delete\": \"Sil\",\n\n\t// Main Window - Menu Headers\n\t\"ui.main_window.menu_headers.file\": \"Dosya\",\n\t\"ui.main_window.menu_headers.view\": \"Görünüm\",\n\t\"ui.main_window.menu_headers.tools\": \"Araçlar\",\n\n\t\"ui.main_window.menu_headers.enabled\": \"Etkin\",\n\t\"ui.main_window.menu_headers.mod_name\": \"Mod İsmi\",\n\t\"ui.main_window.menu_headers.unique_id\": \"Özel ID\",\n\t\"ui.main_window.menu_headers.author\": \"Yapımcı\",\n\t\"ui.main_window.menu_headers.requirements\": \"Gereksinimler\",\n\t\"ui.main_window.menu_headers.missing_requirements\": \"Eksik Gereksinimler\",\n\t\"ui.main_window.menu_headers.version\": \"Sürüm\",\n\t\"ui.main_window.menu_headers.configuration\": \"Yapılandırma\",\n\t\"ui.main_window.menu_headers.status\": \"Durum\",\n\n\t// Main Window - Labels\n\t\"ui.main_window.labels.profile\": \"Profil\",\n\t\"ui.main_window.labels.enabled_mods\": \"Etkin Modlar: \",\n\t\"ui.main_window.labels.total_mods\": \"Toplam Mod: \",\n\t\"ui.main_window.labels.filter\": \"Filtre\",\n\n\t// Main Window - Hyperlinks\n\t\"ui.main_window.hyperlinks.click_to_open\": \"Açmak için tıkla\",\n\t\"ui.main_window.hyperlinks.update_available\": \"Güncelleme Mevcut ({0})\",\n\t\"ui.main_window.hyperlinks.unofficial_update_available\": \"Resmi Olmayan Güncelleme Mevcut ({0})\",\n\t\"ui.main_window.hyperlinks.broken_compatibility_issue\": \"[Bozuk] Uyumluluk Sorunu\",\n\n\t// Main Window - Buttons\n\t\"ui.main_window.buttons.save_configs\": \"Yapılandırmaları Kaydet\",\n\t\"ui.main_window.buttons.hide_disabled_mods\": \"Devre Dışı Modları Gizle\",\n\t\"ui.main_window.buttons.show_updatable_mods\": \"Güncellenebilir Modları Göster\",\n\n\t// Main Window - Buttons - Update Status\n\t\"ui.main_window.button.update_status.generic\": \"Güncellenmeye Hazır Modlar: Tazelemek için Tıkla\",\n\t\"ui.main_window.button.update_status.updating\": \"Güncelleniyor...\",\n\t\"ui.main_window.button.update_status.list_available_updates\": \"Güncellenmeye Hazır Modlar: {0}\",\n\t\"ui.main_window.button.update_status.failed\": \"Mod Güncelleme Kontrolü Başarısız\",\n\n\t// Main Window - ComboBox\n\t\"ui.main_window.combobox.show_all_mods\": \"Tüm Modları Göster\",\n\t\"ui.main_window.combobox.hide_enabled_mods\": \"Etkin Modları Gizle\",\n\t\"ui.main_window.combobox.mod_name\": \"Mod İsmi\",\n\t\"ui.main_window.combobox.author\": \"Yapımcı\",\n\t\"ui.main_window.combobox.requirements\": \"Gereksinimler\",\n\n\t// Settings Window - Labels\n\t\"ui.settings_window.labels.smapi_path\": \"SMAPI Dosya Yolu\",\n\t\"ui.settings_window.labels.mod_path\": \"Mod Klasör Yolu\",\n\t\"ui.settings_window.labels.themes\": \"Temalar\",\n\t\"ui.settings_window.labels.languages\": \"Diller\",\n\t\"ui.settings_window.labels.miscellaneous\": \"Diğer\",\n\n\t// Settings Window - Buttons\n\t\"ui.settings_window.buttons.ignore_hidden_folders\": \"Gizli Dosyaları Görmezden Gel\",\n\t\"ui.settings_window.buttons.enable_profile_specific_mod_configs\": \"Profile Özel Mod Yapılandırmalarını Etkinleştir\",\n\n\t// Settings Window - Tooltips\n\t\"ui.settings_window.tooltips.smapi\": \"StardewModdingAPI'nin dosya yolu\",\n\t\"ui.settings_window.tooltips.mod_folder_path\": \"Mod klasörüne giden dosya yolu\",\n\t\"ui.settings_window.tooltips.theme\": \"Stardrop'un mevcut teması\",\n\t\"ui.settings_window.tooltips.language\": \"Stardrop'un mevcut dili\",\n\t\"ui.settings_window.tooltips.ignore_hidden_folders\": \"Eğer işaretlenirse, Stardrop \\\".\\\" ile başlayan üst klasörleri olan modları görmezden gelecek.\",\n\t\"ui.settings_window.tooltips.enable_profile_specific_configs\": \"Eğer işaretlenirse, Stardrop profiller değiştirildiğinde modların config.json dosyalarını kaydedecek ve yenileyecek.\",\n\t\"ui.settings_window.tooltips.save_changes\": \"Değşiklikleri Kaydet\",\n\t\"ui.settings_window.tooltips.cancel_changes\": \"Vazgeç\",\n\n\t// Warnings\n\t\"ui.warning.unable_to_add_profile\": \"{0} eklenemiyor, bu isim altında bir profil zaten var!\",\n\t\"ui.warning.given_mod_folder_does_not_exist\": \"Verilen mod klasör yolu mevcut değil.\\n\\nEski dosya yoluna dönülüyor.\",\n\t\"ui.warning.given_invalid_smapi_executable\": \"Verilen dosya {0} değil.\\n\\nEski dosya yoluna dönülüyor.\",\n\t\"ui.warning.stardrop_locked\": \"Stardrop, SMAPI çalışırken kilitlidir. SMAPI kapatılana kadar yapılan hiçbir değişiklik etkisini göstermeyecek.\",\n\n\t\"ui.warning.unable_to_locate_smapi\": \"StardewModdingAPI.exe konumu bulunamıyor\\n\\nLütfen şuranın altında doğru dosya yolunu ayarlayın\\nGörünüm > Ayarlar\",\n\t\"ui.warning.mod_config_saved_but_not_enabled\": \"Profil {0} için mod yapılandırma ayarları kaydedildi, yine de profile özel mod yapılandırma ayarları devre dışı.\\n\\nGörüntü > Ayarlar kısmına gidin ve Stardrop'un mod yapılandırma seçeneklerini otomatik olarak kaydetmesi ve uygulaması için \\\"Profile Özel Mod Yapılandırmalarını Etkinleştir\\\" seçeneğini işaretleyin.\",\n\t\"ui.warning.unable_to_determine_profile\": \"Seçilen profil ayarlanamıyor.\\n\\nSMAPI başlatılmayacak.\",\n\t\"ui.warning.stardrop_up_to_date\": \"Stardrop güncel.\\n\\nSon sürüm {0}.\",\n\t\"ui.warning.update_cooldown_minutes\": \"Güncellemeler sadece 5 dakikada bir kez istenebilir.\\n\\nLütfen {0} dakika içerisinde tekrar deneyin.\",\n\t\"ui.warning.update_cooldown_seconds\": \"Güncellemeler sadece 5 dakikada bir kez istenebilir.\\n\\nLütfen {0} saniye içerisinde tekrar deneyin.\",\n\t\"ui.warning.unable_to_locate_log\": \"SMAPI-latest.txt dosyası bulunamıyor! Stardrop'un oyunla ilgili detayları tespit edebilmesi için SMAPI en az bir kez başarılı bir şekilde çalıştırılmak zorunda.\",\n\t\"ui.warning.unable_to_read_log\": \"Oyun versiyonunu görmek için SMAPI'nin log dosyası okunamıyor.\\n\\nMod güncellemeleri kontrol edilmeyecek.\",\n\t\"ui.warning.no_manifest\": \"\\\"{0}\\\" konumunda manifest.json bulunamadı.\",\n\t\"ui.warning.unable_to_load_mod\": \"\\\"{0}\\\" konumunda bulunan dosya yüklenemedi.\\n\\nDaha fazla bilgi için log dosyasına bakın.\",\n\n\t// Messages\n\t\"ui.message.confirm_mod_deletion\": \"{0} modunu silmek istediğinden emin misin? Bu işlem geri alınamaz.\",\n\t\"ui.message.unsaved_config_changes\": \"{0} profili için kaydedilmemiş mod yapılandırma değişiklikleri tespit edildi.\\n\\nDeğişiklikleri kaydetmek ister misin?\",\n\t\"ui.message.stardrop_update_available\": \"Stardrop için (v{0}) güncelleme mevcut.\\n\\nŞimdi indirmek ister misin?\",\n\t\"ui.message.confirm_bulk_change_mod_states\": \"Tüm modlar {0}?\\n\\nNot: Bu işlem geri alınamaz.\",\n\t\"ui.message.confirm_mod_update_method\": \"{0} modunun eski bir sürümü tespit edildi. Eski sürümü temizlemek ister misin?\\n\\nNot: Eski sürümleri temizlemek genellikle önerilir, ancak her türlü yapılandırma dosyası kaybolacaktır.\",\n\n\t// Window Names\n\t\"ui.window.settings.name\": \"Ayarlar\",\n\t\"ui.window.profiles.name\": \"Profiller\",\n\t\"ui.window.profile_naming.name\": \"Profil İsmi\",\n\n\t// Internals\n\t\"internal.unknown\": \"Bilinmiyor\",\n\t\"internal.enable\": \"Etkinleştir\",\n\t\"internal.disable\": \"Devre Dışı Bırak\",\n\t\"internal.yes\": \"Evet\",\n\t\"internal.no\": \"Hayır\",\n\t\"internal.ok\": \"Tamam\",\n\t\"internal.unlock\": \"Kilidi Aç\"\n}\n"
  },
  {
    "path": "Stardrop/i18n/uk.json",
    "content": "{\r\n  // Main Window - Menu Items\r\n  \"ui.main_window.menu_items.add_mod\": \"Додати модифікацію\",\r\n  \"ui.main_window.menu_items.add_mods\": \"Додати модифікації\",\r\n  \"ui.main_window.menu_items.start_smapi\": \"Запустити SMAPI\",\r\n  \"ui.main_window.menu_items.exit\": \"Вийти\",\r\n  \"ui.main_window.menu_items.settings\": \"Налаштування\",\r\n  \"ui.main_window.menu_items.stardrop_log_file\": \"Файл журналу Stardrop\",\r\n  \"ui.main_window.menu_items.smapi_log_file\": \"Файл журналу SMAPI\",\r\n  \"ui.main_window.menu_items.refresh_mod_list\": \"Оновити перелік модифікацій\",\r\n  \"ui.main_window.menu_items.check_for_mod_updates\": \"Перевірити наявність оновлень для модифікацій\",\r\n  \"ui.main_window.menu_items.enable_all_mods\": \"Увімкнути всі модифікації\",\r\n  \"ui.main_window.menu_items.disable_all_mods\": \"Вимкнути всі модифікації\",\r\n  \"ui.main_window.menu_items.check_for_stardrop_update\": \"Перевірити наявність оновлень для Stardrop\",\r\n  \"ui.main_window.menu_items.stardrop_repository\": \"Репозиторій Stardrop\",\r\n  \"ui.main_window.menu_items.nexus_install_all\": \"Завантажити всі оновлення\",\r\n  \"ui.main_window.menu_items.nexus_connection\": \"Підключення через API\",\r\n  \"ui.main_window.menu_items.check_for_SMAPI_update\": \"Перевірити SMAPI на наявність оновлення\",\r\n\r\n  // Main Window - Menu Items - Context\r\n  \"ui.main_window.menu_items.context.columns.show_hide\": \"Показати/Приховати стовпчики\",\r\n  \"ui.main_window.menu_items.context.hide_requirements\": \"Приховувати вимоги\",\r\n  \"ui.main_window.menu_items.context.cancel\": \"Скасувати\",\r\n  \"ui.main_window.menu_items.context.open_containing_folder\": \"Відкрити теку з вмістом\",\r\n  \"ui.main_window.menu_items.context.open_mod_page\": \"Відкрити сторінку модифікації\",\r\n  \"ui.main_window.menu_items.context.open_config\": \"Відкрити файл конфігурації\",\r\n  \"ui.main_window.menu_items.context.show_whole_mod_group\": \"Показати всю групу модифікацій\",\r\n  \"ui.main_window.menu_items.context.show_authors_mods\": \"Показати всі модифікації автора\",\r\n  \"ui.main_window.menu_items.context.open\": \"Відкрити...\",\r\n  \"ui.main_window.menu_items.context.filters\": \"Фільтри\",\r\n  \"ui.main_window.menu_items.context.options\": \"Параметри\",\r\n  \"ui.main_window.menu_items.context.clear_filter\": \"Очистити\",\r\n  \"ui.main_window.menu_items.context.delete\": \"Вилучити\",\r\n\r\n  // Main Window - Menu Headers\r\n  \"ui.main_window.menu_headers.file\": \"Файл\",\r\n  \"ui.main_window.menu_headers.view\": \"Перегляд\",\r\n  \"ui.main_window.menu_headers.tools\": \"Інструменти\",\r\n  \"ui.main_window.menu_headers.nexus\": \"Nexusmods\",\r\n\r\n  \"ui.main_window.menu_headers.enabled\": \"Увімкнено\",\r\n  \"ui.main_window.menu_headers.mod_name\": \"Назва модифікації\",\r\n  \"ui.main_window.menu_headers.unique_id\": \"Унікальний ID\",\r\n  \"ui.main_window.menu_headers.author\": \"Автор\",\r\n  \"ui.main_window.menu_headers.requirements\": \"Вимоги\",\r\n  \"ui.main_window.menu_headers.missing_requirements\": \"Вимоги відсутні\",\r\n  \"ui.main_window.menu_headers.version\": \"Версія\",\r\n  \"ui.main_window.menu_headers.configuration\": \"Конфігурація\",\r\n  \"ui.main_window.menu_headers.status\": \"Статус\",\r\n  \"ui.main_window.menu_headers.endorsement\": \"Схвалення спільноти\",\r\n  \"ui.main_window.menu_headers.nexus_install\": \"Оновлення із сайту Nexusmods\",\r\n  \"ui.main_window.menu_headers.install_timestamp\": \"Часова мітка встановлення\",\r\n  \"ui.main_window.menu_headers.last_updated\": \"Востаннє оновлено\",\r\n\r\n  // Main Window - Labels\r\n  \"ui.main_window.labels.profile\": \"Профіль\",\r\n  \"ui.main_window.labels.enabled_mods\": \"Увімкнуто модифікацій: \",\r\n  \"ui.main_window.labels.total_mods\": \"Загалом модифікацій: \",\r\n  \"ui.main_window.labels.updatable_mods\": \"Готових до оновлення: \",\r\n  \"ui.main_window.labels.filter\": \"Фільтр\",\r\n  \"ui.main_window.labels.unknown_SMAPI\": \"Невідома версія SMAPI\",\r\n\r\n  // Main Window - Hyperlinks\r\n  \"ui.main_window.hyperlinks.click_to_open\": \"Відкрити\",\r\n  \"ui.main_window.hyperlinks.update_available\": \"Доступне оновлення ({0})\",\r\n  \"ui.main_window.hyperlinks.unofficial_update_available\": \"Доступне неофіційне оновлення ({0})\",\r\n  \"ui.main_window.hyperlinks.broken_compatibility_issue\": \"[Broken] Проблема сумісності\",\r\n  \"ui.main_window.hyperlinks.install_update\": \"Оновити\",\r\n  \"ui.main_window.hyperlinks.downloading\": \"Завантаження...\",\r\n  \"ui.main_window.hyperlinks.installing\": \"Встановлення...\",\r\n\r\n  // Main Window - Buttons\r\n  \"ui.main_window.buttons.save_configs\": \"Зберегти конфігурацію\",\r\n  \"ui.main_window.buttons.save_profile\": \"Зберегти профіль\",\r\n  \"ui.main_window.buttons.show_updatable_mods\": \"Показати готові до оновлення модифікації\",\r\n  \"ui.main_window.buttons.active_search_filters\": \"Фільтрів активно: {0}\",\r\n  \"ui.main_window.buttons.no_search_filters\": \"Фільтр не застосовано\",\r\n\r\n  // Main Window - Buttons - Update Status\r\n  \"ui.main_window.button.update_status.generic\": \"Натисніть для перевірки модифікацій на наявність оновлень\",\r\n  \"ui.main_window.button.update_status.updating\": \"Оновлення...\",\r\n  \"ui.main_window.button.update_status.list_available_updates\": \"Модифікацій готових до оновлення: {0}\",\r\n  \"ui.main_window.button.update_status.failed\": \"Не вдалося перевірити на наявність оновлень\",\r\n\r\n  // Main Window - Buttons - Downloads\r\n  \"ui.main_window.buttons.downloads.label\": \"Завантаження ({0})\",\r\n\r\n  // Main Window - ComboBox\r\n  \"ui.main_window.combobox.show_all_mods\": \"Показати всі\",\r\n  \"ui.main_window.combobox.show_mods_with_configs\": \"Показати модифікації з конфігурацією\",\r\n  \"ui.main_window.combobox.show_disabled_mods\": \"Показати вимкнуті модифікації\",\r\n  \"ui.main_window.combobox.show_enabled_mods\": \"Показати увімкнуті модифікації\",\r\n\r\n  // Main Window - ComboBox (ListBox)\r\n  \"ui.main_window.combobox.mod_name\": \"Назва\",\r\n  \"ui.main_window.combobox.author\": \"Автор\",\r\n  \"ui.main_window.combobox.requirements\": \"Вимоги\",\r\n  \"ui.main_window.combobox.group\": \"Група модифікацій\",\r\n\r\n  // Settings Window - Labels\r\n  \"ui.settings_window.labels.smapi_path\": \"Шлях до SMAPI\",\r\n  \"ui.settings_window.labels.mod_path\": \"Шлях до теки з модифікаціями\",\r\n  \"ui.settings_window.labels.mod_install\": \"Шлях до встановлених Stardrop модифікацій\",\r\n  \"ui.settings_window.labels.themes\": \"Теми\",\r\n  \"ui.settings_window.labels.languages\": \"Мова\",\r\n  \"ui.settings_window.labels.grouping\": \"Групування модифікацій\",\r\n  \"ui.settings_window.labels.miscellaneous\": \"Інше\",\r\n  \"ui.settings_window.labels.nexus\": \"Nexusmods\",\r\n  \"ui.settings_window.labels.preferred_server\": \"Бажаний сервер:\",\r\n\r\n  // Settings Window - Buttons\r\n  \"ui.settings_window.buttons.ignore_hidden_folders\": \"Ігнорувати приховані теки\",\r\n  \"ui.settings_window.buttons.enable_profile_specific_mod_configs\": \"Увімкнути прив’язку конфігурації модифікацій\\nдо профілю\",\r\n  \"ui.settings_window.buttons.enable_mods_on_add\": \"Автоматично вмикати моди після встановлення\",\r\n  \"ui.settings_window.buttons.always_ask_to_delete\": \"Завжди питати про видалення файлів\\nмодифікації при оновленні\",\r\n  \"ui.settings_window.buttons.automatically_save_profile_changes\": \"Автоматично зберігати зміни профілю\",\r\n  \"ui.settings_window.buttons.always_ask_for_NXM_installs\": \"Завжди питати перед встановленням файлів із\\nNexusmods\",\r\n  \"ui.settings_window.buttons.register_nxm_association\": \"Пов’язати з NXM\",\r\n\r\n  // Settings Window - Tooltips\r\n  \"ui.settings_window.tooltips.smapi\": \"Шлях до файлу StardewModdingAPI\",\r\n  \"ui.settings_window.tooltips.mod_folder_path\": \"Шлях до теки з модифікаціями\",\r\n  \"ui.settings_window.tooltips.mod_install_path\": \"Шлях до теки, в яку Stardrop буде розміщувати щойно встановлені моди. Має бути в теці, зазначеній у «Шлях до теки з модифікаціями».\",\r\n  \"ui.settings_window.tooltips.theme\": \"Поточна тема Stardrop\",\r\n  \"ui.settings_window.tooltips.language\": \"Поточна мова Stardrop\",\r\n  \"ui.settings_window.tooltips.grouping\": \"Поточний метод для групування модифікацій.\",\r\n  \"ui.settings_window.tooltips.ignore_hidden_folders\": \"Якщо позначено, Stardrop ігноруватиме кореневі теки з модифікаціями, які починаються на «.»\",\r\n  \"ui.settings_window.tooltips.nxm_file_association\": \"Реєструє протокол NXM, який буде пов’язаний зі Stardrop, що дозволяє здійснювати пряме встановлення з www.nexusmods.com\",\r\n  \"ui.settings_window.tooltips.always_ask_nxm_files\": \"Якщо позначено, Stardrop завжди запитуватиме вас перед встановленням моду з www.nexusmods.com через NXM.\",\r\n  \"ui.settings_window.tooltips.enable_profile_specific_configs\": \"Якщо позначено, Stardrop зберігатиме та відновлюватиме config.json файли з модифікацій при зміні профілю.\",\r\n  \"ui.settings_window.tooltips.enable_mods_on_add\": \"Якщо позначено, Stardrop автоматично вмикатиме щойно встановлені або оновлені модифікації.\",\r\n  \"ui.settings_window.tooltips.always_ask_to_delete\": \"Якщо позначено, Stardrop завжди запитуватиме вас чи треба видалити файли модифікації перед її оновленням.\",\r\n  \"ui.settings_window.tooltips.automatically_save_profile_changes\": \"Якщо позначено, Stardrop буде автоматично зберігати всі зміни що внесені в профіль.\",\r\n  \"ui.settings_window.tooltips.preferred_server\": \"Встановлює ваш бажаний сервер для використання під час завантаження з Nexusmods.\",\r\n  \"ui.settings_window.tooltips.save_changes\": \"Зберегти зміни\",\r\n  \"ui.settings_window.tooltips.cancel_changes\": \"Скасувати\",\r\n\r\n  // Nexus Login Window - Buttons\r\n  \"ui.nexus_login.buttons.nexus\": \"Перейти на Nexusmods\",\r\n  \"ui.nexus_login.buttons.disconnect\": \"Від’єднатися від Nexusmods\",\r\n\r\n  // Nexus Login Window - Labels\r\n  \"ui.nexus_login.labels.note\": \"Зауваження\",\r\n  \"ui.nexus_login.labels.get_key\": \"Отримати свій особистий ключ API від Nexusmods\",\r\n  \"ui.nexus_login.labels.get_key_actual\": \"Отримати API ключ Stardrop від Nexusmods\",\r\n  \"ui.nexus_login.labels.paste\": \"Потім вставте ключ нижче\",\r\n  \"ui.nexus_login.labels.share_warning\": \"Не передавайте свій особистий ключ API іншим, це може призвести до зловмисних дій щодо вашої обліковки Nexusmods.\",\r\n  \"ui.nexus_login.labels.share_warning_actual\": \"Не передавайте свій ключ API іншим, це може призвести до зловмисних дій щодо вашої обліковки Nexusmods.\",\r\n  \"ui.nexus_login.labels.revoke\": \"Ви можете скинути / відкликати свій ключ у будь-який час, перейшовши в My Nexus Account > API > Personal API Key.\",\r\n  \"ui.nexus_login.labels.revoke_actual\": \"Ви можете будь-коли скинути або відкликати свій API ключ\\nStardrop, перейшовши в My Nexus Account > API > Stardrop.\",\r\n  \"ui.nexus_login.labels.nexus_details\": \"Інформація про користувача Nexusmods\",\r\n  \"ui.nexus_login.labels.nexus_disconnect\": \"Натисніть на кнопку нижче, щоб Stardrop видалив зі своєї системи ваш особистий ключ та від’єднався від Nexusmods.\",\r\n  \"ui.nexus_login.labels.nexus_disconnect_actual\": \"Натисніть на кнопку нижче, щоб Stardrop видалив зі своєї системи ваш особистий API ключ Stardrop та від’єднався від Nexusmods.\",\r\n  \"ui.nexus_login.labels.username\": \"Ім’я користувача: {0}\",\r\n  \"ui.nexus_login.labels.is_premium\": \"Користувач преміуму: {0}\",\r\n\r\n  // Downloads Panel - Labels\r\n  \"ui.downloads_panel.download_canceled\": \"Скасовано\",\r\n  \"ui.downloads_panel.download_failed\": \"Помилка\",\r\n  \"ui_downloads_panel.download_success\": \"Завершено!\",\r\n  \"ui.downloads_panel.no_downloads_label\":  \"Актуальних завантажень немає.\",\r\n\r\n  // Downloads Panel - Tooltips\r\n  \"ui.downloads_panel.tooltips.cancel_button\": \"Скасувати\",  \r\n  \"ui.downloads_panel.tooltips.remove_button\": \"Прибрати\", // Tooltip for the button that clears out a completed or failed download\r\n\r\n  // Warnings\r\n  \"ui.warning.unable_to_add_profile\": \"Не вдалося додати «{0}», профіль з такою назвою вже існує!\",\r\n  \"ui.warning.given_mod_folder_does_not_exist\": \"Обраної теки з модифікаціями не існує.\\n\\nПовертаємо попередній шлях.\",\r\n  \"ui.warning.given_install_folder_not_exist\": \"Вказаний шлях до встановлених Stardrop модифікацій не існує.\\n\\nПовертаємо попередній шлях.\",\r\n  \"ui.warning.given_install_folder_not_exist_default\": \"Вказаний шлях до теки встановлених Stardrop модифікацій не існує.\\n\\nПовертаємо на типовий шлях.\",\r\n  \"ui.warning.given_install_folder_not_under_mod_folder\": \"Вказаний шлях до встановлених Stardrop модифікацій не існує в {0}\\n\\nПовертаємо попередній шлях.\",\r\n  \"ui.warning.given_invalid_smapi_executable\": \"Вказаний файл не є {0}\\n\\nПовертаємо попередній шлях.\",\r\n  \"ui.warning.stardrop_locked\": \"Stardrop заблоковано, коли SMAPI запущений. Жодні зміни не набудуть чинності доки не закриєте SMAPI.\",\r\n  \"ui.warning.stardrop_downloading\": \"Завантаження останньої версії Stardrop.\",\r\n  \"ui.warning.SMAPI_downloading\": \"Завантаження останньої версії SMAPI з GitHub.\",\r\n  \"ui.warning.SMAPI_installing\": \"Оновлення SMAPI до останньої версії.\",\r\n  \"ui.warning.SMAPI_up_to_date\": \"SMAPI не потребує оновлення.\\n\\n{0} є останньою версією.\",\r\n  \"ui.warning.install_mod_attempt_count\": \"Пробуємо встановити {0} мод(ів)\",\r\n  \"ui.warning.mod_installing\": \"Встановлення модифікації {0}\",\r\n  \"ui.warning.mod_updating\": \"Оновлення модифікації {0}\",\r\n  \"ui.warning.mod_deleting\": \"Видалення старого зразку модифікації {0}\",\r\n  \"ui.warning.failed_to_delete\": \"Не вдалося видалити модифікацію {0}.\\n\\nЇї файли можуть бути заблоковані, дивіться подробиці в журналі.\",\r\n  \"ui.warning.failed_to_delete_during_update\": \"Не вдалося видалити старий зразок модифікації {0}.\\n\\nМодифікація може встановитися неправильно.\",\r\n  \"ui.warning.unable_to_locate_smapi\": \"Не вдалося виявити розташування StardewModdingAPI.exe\\n\\nБудь ласка, встановіть правильний шлях в меню:\\nПерегляд > Налаштування\",\r\n  \"ui.warning.mod_config_saved_but_not_enabled\": \"Налаштування конфігурації моду збережено в профілі «{0}», однак специфічні конфігурації модифікації в профілі не ввімкнено.\\n\\nПерейдіть до «Перегляд» > «Налаштування» та оберіть «Увімкнути прив’язку конфігурації модифікацій до профілю», щоб Stardrop автоматично зберігав і застосовував зміни конфігурації моду.\",\r\n  \"ui.warning.unable_to_determine_profile\": \"Не вдалося визначити обраний профіль.\\n\\nSMAPI не буде запущено.\",\r\n  \"ui.warning.stardrop_up_to_date\": \"Stardrop не потребує оновлення.\\n\\n{0} є останньою версією.\",\r\n  \"ui.warning.stardrop_unable_to_download_latest\": \"Не вдається завантажити останню версію Stardrop.\\n\\nБудь ласка, завантажте останню версію вручну.\",\r\n  \"ui.warning.stardrop_unable_to_find_latest\": \"Не вдалося перевірити GitHub репозиторій Stardrop’у на оновлення.\\n\\nБудь ласка, перевірте GitHub на можливе оновлення або спробуйте пізніше.\",\r\n  \"ui.warning.stardrop_update_downloaded\": \"Успішно завантажено останню версію.\\n\\nЗараз Stardrop перезапуститься.\",\r\n  \"ui.warning.SMAPI_unable_to_download_latest\": \"Не вдалося завантажити останню версію SMAPI.\\n\\nБудь ласка, завантажте останню версію власноруч.\",\r\n  \"ui.warning.SMAPI_unable_to_install_latest\": \"Не вдалося встановити останню версію SMAPI.\\n\\nБудь ласка, завантажте останню версію власноруч.\",\r\n  \"ui.warning.update_cooldown_minutes\": \"Запит на оновлення можна робити не частіше 5 хвилин.\\n\\nБудь ласка, спробуйте ще раз за {0} хв.\",\r\n  \"ui.warning.update_cooldown_seconds\": \"Запит на оновлення можна робити не частіше 5 хвилин.\\n\\nБудь ласка, спробуйте ще раз за {0} с.\",\r\n  \"ui.warning.unable_to_locate_log\": \"Не вдалося виявити SMAPI-latest.txt! SMAPI необхідний для визначення додаткової інформації про гру.\",\r\n  \"ui.warning.unable_to_read_log\": \"Не вдалося прочитати файл звіту SMAPI, аби отримати версію гри.\\n\\nМодифікації не будуть перевірятись на наявність оновлень.\",\r\n  \"ui.warning.no_manifest\": \"Файл manifest.json не знайдено в {0}\",\r\n  \"ui.warning.unable_to_load_mod\": \"Не вдалося завантажити файл з {0}.\\n\\nПодробиці дивіться у файлі журналу.\",\r\n  \"ui.warning.unable_to_validate_nexus_key\": \"Не вдається перевірити API ключ Nexusmods.\\n\\nБудь ласка, переконайтеся, що ключ правильний, і повторіть спробу.\",\r\n  \"ui.warning.download_without_premium\": \"Щоб завантажити через Stardrop, ви повинні мати преміум-членство на Nexusmods.\",\r\n  \"ui.warning.no_downloads_available\": \"Наразі немає модифікацій з оновленнями, доступними на Nexusmods.\",\r\n  \"ui.warning.failed_nexus_install\": \"Не вдалося встановити модифікацію {0} через проблему з’єднання з Nexusmods.\\n\\nБудь ласка, спробуйте ще раз.\",\r\n  \"ui.warning.unable_nexus_install\": \"Не вдається встановити модифікацію {0}.\",\r\n  \"ui.warning.unable_to_endorse\": \"Неможливо встановити стан схвалень від спільноти:\\n\\n{0}\",\r\n  \"ui.warning.mod_owned\": \"Ви є власником цього моду.\",\r\n  \"ui.warning.too_soon_after_download\": \"Ви повинні почекати 15 хвилин після завантаження цього моду.\\n\\nСпроба вподобати скине цей таймер.\",\r\n  \"ui.warning.not_downloaded\": \"Ви повинні завантажити цю модифікацію з Nexusmods, щоб її вподобати.\",\r\n  \"ui.warning.failed_to_get_mod_details\": \"Не вдалося отримати деталі моду за посиланням NXM: {0}\",\r\n  \"ui.warning.failed_to_get_download_link\": \"Не вдалося отримати URL-адресу для завантаження з Nexusmods за посиланням NXM: {0}\",\r\n  \"ui.warning.failed_to_set_association\": \"Не вдалося зареєструвати протокол NXM у Stardrop.\\n\\nПодробиці дивіться в журналі.\",\r\n  \"ui.warning.already_associated\": \"Stardrop вже пов’язано з протоколом NXM.\",\r\n\r\n  // Messages\r\n  \"ui.message.confirm_mod_deletion\": \"Хочете видалити {0}? Ця дія безповоротна.\",\r\n  \"ui.message.unsaved_config_changes\": \"Виявлено не збережені зміни конфігурації модифікацій для профілю {0}.\\n\\nХочете зберегти зміни?\",\r\n  \"ui.message.stardrop_update_available\": \"Доступне оновлення Stardrop (в.{0}).\\n\\nЧи хотіли б ви завантажити його зараз?\",\r\n  \"ui.message.SMAPI_update_available\": \"Доступне оновлення для SMAPI (в.{0}).\\n\\nЧи хотіли б ви завантажити його зараз?\",\r\n  \"ui.message.stardrop_update_complete\": \"Stardrop успішно оновився.\\n\\nЧи хотіли б ви дізнатися про зміни?\",\r\n  \"ui.message.SMAPI_update_complete\": \"SMAPI успішно оновився.\\n\\nЧи хотіли б ви дізнатися про зміни?\",\r\n  \"ui.message.confirm_bulk_change_mod_states_enable\": \"Увімкнути всі модифікації?\\n\\nПримітка: Ця дія безповоротна.\",\r\n  \"ui.message.confirm_bulk_change_mod_states_disable\": \"Вимкнути всі модифікації?\\n\\nПримітка: Ця дія безповоротна.\",\r\n  \"ui.message.confirm_mod_update_method\": \"Виявлено попередню версію {0}. Чи хотіли б ви очистити попереднє встановлення?\\n\\nПримітка: Зазвичай рекомендується очищати попередню версію, але тоді файли конфігурацій буде втрачено.\",\r\n  \"ui.message.confirm_mod_update_method_no_config\": \"Виявлено попередню версію {0}. Чи хотіли б ви очистити попереднє встановлення?\\n\\nПримітка: Зазвичай рекомендується очистити попередні версії.\",\r\n  \"ui.message.confirm_mod_update_method_preserved\": \"Виявлено попередню версію {0}. Чи хотіли б ви очистити попереднє встановлення?\\n\\nПримітка: Зазвичай рекомендується очистити попередні версії, ваші файли сурисування будуть відновлені.\",\r\n  \"ui.message.confirm_nxm_association\": \"Чи хотіли б ви пов’язати протокол NXM зі Stardrop?\\n\\nЦе дозволить Stardrop автоматично встановлювати моди, завантажені з Nexusmods.\",\r\n  \"ui.message.confirm_nxm_install\": \"Чи хотіли б ви встановити модифікацію:\\n\\n{0}?\\n\\nВи можете вимкнути це підтвердження в меню налаштувань.\",\r\n  \"ui.message.require_nexus_login\": \"Для цієї дії потрібно ввійти в Nexusmods API.\",\r\n  \"ui.message.succeeded_nexus_install\": \"Успішно встановлено модифікацію через Nexusmods:\\n\\n{0}\",\r\n\r\n  // Window Names\r\n  \"ui.window.settings.name\": \"Налаштування\",\r\n  \"ui.window.profiles.name\": \"Профілі\",\r\n  \"ui.window.profile_naming.name\": \"Назва профілю\",\r\n  \"ui.window.nexus_login.name\": \"Персональний API ключ Nexusmods\",\r\n  \"ui.window.nexus_login_actual.name\": \"API ключ Nexusmods\",\r\n  \"ui.window.nexus_user.name\": \"Інформація про користувача Nexusmods\",\r\n\r\n  // Internals\r\n  \"internal.connected\": \"З’єднано\",\r\n  \"internal.disconnected\": \"Роз’єднано\",\r\n  \"internal.unknown\": \"Невідомо\",\r\n  \"internal.enable\": \"Увімкнено\",\r\n  \"internal.disable\": \"Вимкнено\",\r\n  \"internal.enable_whole_mod\": \"Увімкнути всю групу моду\",\r\n  \"internal.disable_whole_mod\": \"Вимкнути всю групу моду\",\r\n  \"internal.yes\": \"Так\",\r\n  \"internal.yes_all\": \"Погодити все\",\r\n  \"internal.no\": \"Ні\",\r\n  \"internal.no_all\": \"Не погоджувати нічого\",\r\n  \"internal.ok\": \"Гаразд\",\r\n  \"internal.unlock\": \"Розблокувати\",\r\n\r\n  // Internals - Measurements\r\n  \"internal.measurements.bytes_size\": \"Б\",\r\n  \"internal.measurements.bytes_per_second\": \"Б/с\",\r\n  \"internal.measurements.kilobytes_size\": \"КБ\",\r\n  \"internal.measurements.kilobytes_per_second\": \"КБ/с\",\r\n  \"internal.measurements.megabytes_size\": \"МБ\",\r\n  \"internal.measurements.megabytes_per_second\": \"МБ/с\"\r\n}\r\n"
  },
  {
    "path": "Stardrop/i18n/zh.json",
    "content": "{\r\n  // Main Window - Menu Items\r\n  \"ui.main_window.menu_items.add_mod\": \"添加模组\",\r\n  \"ui.main_window.menu_items.add_mods\": \"添加模组\",\r\n  \"ui.main_window.menu_items.start_smapi\": \"启动 SMAPI\",\r\n  \"ui.main_window.menu_items.exit\": \"退出\",\r\n  \"ui.main_window.menu_items.settings\": \"设置\",\r\n  \"ui.main_window.menu_items.stardrop_log_file\": \"Stardrop 日志文件\",\r\n  \"ui.main_window.menu_items.smapi_log_file\": \"SMAPI 日志文件\",\r\n  \"ui.main_window.menu_items.refresh_mod_list\": \"刷新模组列表\",\r\n  \"ui.main_window.menu_items.check_for_mod_updates\": \"检查模组更新\",\r\n  \"ui.main_window.menu_items.enable_all_mods\": \"启用所有模组\",\r\n  \"ui.main_window.menu_items.disable_all_mods\": \"禁用所有模组\",\r\n  \"ui.main_window.menu_items.check_for_stardrop_update\": \"检查 Stardrop 更新\",\r\n  \"ui.main_window.menu_items.stardrop_repository\": \"Stardrop 仓库\",\r\n  \"ui.main_window.menu_items.nexus_install_all\": \"安装所有更新\",\r\n  \"ui.main_window.menu_items.nexus_connection\": \"使用 API 密钥连接到 Nexus Mods\",\r\n  \"ui.main_window.menu_items.check_for_SMAPI_update\": \"检查 SMAPI 更新\",\r\n\r\n  // Main Window - Menu Items - Context\r\n  \"ui.main_window.menu_items.context.columns.show_hide\": \"显示/隐藏列\",\r\n  \"ui.main_window.menu_items.context.hide_requirements\": \"隐藏依赖\",\r\n  \"ui.main_window.menu_items.context.cancel\": \"取消\",\r\n  \"ui.main_window.menu_items.context.open_containing_folder\": \"打开所在文件夹\",\r\n  \"ui.main_window.menu_items.context.open_mod_page\": \"打开模组页面\",\r\n  \"ui.main_window.menu_items.context.open_config\": \"打开设置文件\",\r\n  \"ui.main_window.menu_items.context.open_manifest\": \"打开清单文件\",\r\n  \"ui.main_window.menu_items.context.show_whole_mod_group\": \"显示该分类下所有模组（当分类为Content Pack时可能会有问题）\",\r\n  \"ui.main_window.menu_items.context.show_authors_mods\": \"显示该作者的模组\",\r\n  \"ui.main_window.menu_items.context.open\": \"打开...\",\r\n  \"ui.main_window.menu_items.context.filters\": \"过滤\",\r\n  \"ui.main_window.menu_items.context.options\": \"选项\",\r\n  \"ui.main_window.menu_items.context.clear_filter\": \"清除\",\r\n  \"ui.main_window.menu_items.context.delete\": \"删除\",\r\n\r\n  // Main Window - Menu Headers\r\n  \"ui.main_window.menu_headers.file\": \"文件\",\r\n  \"ui.main_window.menu_headers.view\": \"系统\",\r\n  \"ui.main_window.menu_headers.tools\": \"工具\",\r\n  \"ui.main_window.menu_headers.nexus\": \"Nexus Mods\",\r\n\r\n  \"ui.main_window.menu_headers.enabled\": \"启用\",\r\n  \"ui.main_window.menu_headers.mod_name\": \"模组名称\",\r\n  \"ui.main_window.menu_headers.unique_id\": \"专属ID\",\r\n  \"ui.main_window.menu_headers.author\": \"作者\",\r\n  \"ui.main_window.menu_headers.requirements\": \"依赖\",\r\n  \"ui.main_window.menu_headers.missing_requirements\": \"缺少的依赖\",\r\n  \"ui.main_window.menu_headers.version\": \"版本\",\r\n  \"ui.main_window.menu_headers.configuration\": \"设置\",\r\n  \"ui.main_window.menu_headers.status\": \"状态\",\r\n  \"ui.main_window.menu_headers.endorsement\": \"支持\",\r\n  \"ui.main_window.menu_headers.nexus_install\": \"Nexus 模组更新\",\r\n  \"ui.main_window.menu_headers.install_timestamp\": \"安装时间\",\r\n  \"ui.main_window.menu_headers.last_updated\": \"上次更新时间\",\r\n\r\n  // Main Window - Labels\r\n  \"ui.main_window.labels.profile\": \"个性化设置\",\r\n  \"ui.main_window.labels.enabled_mods\": \"已启用模组：\",\r\n  \"ui.main_window.labels.total_mods\": \"总模组数：\",\r\n  \"ui.main_window.labels.updatable_mods\": \"准备更新的模组: \",\r\n  \"ui.main_window.labels.filter\": \"搜索\",\r\n  \"ui.main_window.labels.unknown_SMAPI\": \"未知的 SMAPI 版本\",\r\n\r\n  // Main Window - Hyperlinks\r\n  \"ui.main_window.hyperlinks.click_to_open\": \"点击打开\",\r\n  \"ui.main_window.hyperlinks.update_available\": \"可用更新（{0}）\",\r\n  \"ui.main_window.hyperlinks.unofficial_update_available\": \"非官方可用更新（{0}）\",\r\n  \"ui.main_window.hyperlinks.broken_compatibility_issue\": \"[已损坏] 兼容性问题\",\r\n  \"ui.main_window.hyperlinks.install_update\": \"安装更新\",\r\n  \"ui.main_window.hyperlinks.downloading\": \"正在下载...\",\r\n  \"ui.main_window.hyperlinks.installing\": \"正在安装...\",\r\n\r\n  // Main Window - Buttons\r\n  \"ui.main_window.buttons.save_configs\": \"保存系统设置\",\r\n  \"ui.main_window.buttons.save_profile\": \"保存个性化设置\",\r\n  \"ui.main_window.buttons.show_updatable_mods\": \"显示可更新模组\",\r\n  \"ui.main_window.buttons.active_search_filters\": \"已启用搜索范围: {0}个\",\r\n  \"ui.main_window.buttons.no_search_filters\": \"未选择搜索范围\",\r\n\r\n  // Main Window - Buttons - Update Status\r\n  \"ui.main_window.button.update_status.generic\": \"可更新模组：点击刷新\",\r\n  \"ui.main_window.button.update_status.updating\": \"正在更新...\",\r\n  \"ui.main_window.button.update_status.list_available_updates\": \"可更新模组：{0}\",\r\n  \"ui.main_window.button.update_status.failed\": \"模组更新检查失败\",\r\n\r\n  // Main Window - Buttons - Downloads\r\n  \"ui.main_window.buttons.downloads.label\": \"下载 ({0})\",\r\n\r\n  // Main Window - ComboBox\r\n  \"ui.main_window.combobox.show_all_mods\": \"显示所有模组\",\r\n  \"ui.main_window.combobox.show_mods_with_configs\": \"显示可设置模组\",\r\n  \"ui.main_window.combobox.show_disabled_mods\": \"显示禁用模组\",\r\n  \"ui.main_window.combobox.show_enabled_mods\": \"显示启用模组\",\r\n\r\n    // Main Window - ComboBox (ListBox)\r\n  \"ui.main_window.combobox.mod_name\": \"模组名称\",\r\n  \"ui.main_window.combobox.author\": \"作者\",\r\n  \"ui.main_window.combobox.requirements\": \"依赖\",\r\n  \"ui.main_window.combobox.group\": \"分类规则\",\r\n\r\n  // Settings Window - Labels\r\n  \"ui.settings_window.labels.smapi_path\": \"SMAPI 路径\",\r\n  \"ui.settings_window.labels.mod_path\": \"模组文件夹路径\",\r\n  \"ui.settings_window.labels.mod_install\": \"Stardrop 已安装模组路径\",\r\n  \"ui.settings_window.labels.themes\": \"主题\",\r\n  \"ui.settings_window.labels.languages\": \"语言\",\r\n  \"ui.settings_window.labels.grouping\": \"模组分类规则\",\r\n  \"ui.settings_window.labels.miscellaneous\": \"杂项\",\r\n  \"ui.settings_window.labels.nexus\": \"Nexus Mods\",\r\n  \"ui.settings_window.labels.preferred_server\": \"首选服务器: \",\r\n\r\n  // Settings Window - Buttons\r\n  \"ui.settings_window.buttons.ignore_hidden_folders\": \"忽略隐藏文件夹\",\r\n  \"ui.settings_window.buttons.enable_profile_specific_mod_configs\": \"对不同个性化设置启用不同的模组设置\",\r\n  \"ui.settings_window.buttons.enable_mods_on_add\": \"添加后自动启用模组\",\r\n  \"ui.settings_window.buttons.always_ask_to_delete\": \"更新时总是询问是否替换原模组文件\",\r\n  \"ui.settings_window.buttons.automatically_save_profile_changes\": \"自动保存个性化设置\",\r\n  \"ui.settings_window.buttons.always_ask_for_NXM_installs\": \"从 NXM 安装前总是询问\",\r\n  \"ui.settings_window.buttons.register_nxm_association\": \"关联 NXM\",\r\n\r\n  // Settings Window - Tooltips\r\n  \"ui.settings_window.tooltips.smapi\": \"StardewModdingAPI 文件路径\",\r\n  \"ui.settings_window.tooltips.mod_folder_path\": \"模组文件夹路径\",\r\n  \"ui.settings_window.tooltips.mod_install_path\": \"Stardrop 存放新安装模组的文件夹路径必须位于“模组文件夹路径”给定的文件夹下\",\r\n  \"ui.settings_window.tooltips.theme\": \"Stardrop 当前使用的主题\",\r\n  \"ui.settings_window.tooltips.language\": \"Stardrop 当前使用的语言\",\r\n  \"ui.settings_window.tooltips.grouping\": \"当前对模组分类的规则\",\r\n  \"ui.settings_window.tooltips.ignore_hidden_folders\": \"勾选后，Stardrop 会忽略所有以“.”开头的文件夹\",\r\n  \"ui.settings_window.tooltips.nxm_file_association\": \"关联 NXM，允许从 www.nexusmods.com 直接更新模组\",\r\n  \"ui.settings_window.tooltips.always_ask_nxm_files\": \"勾选后，Stardrop 将始终询问是否从 www.nexusmods.com 更新模组\",\r\n  \"ui.settings_window.tooltips.enable_profile_specific_configs\": \"勾选后，Stardrop 会在切换个性化设置时保存并切换 config.json 文件\",\r\n  \"ui.settings_window.tooltips.enable_mods_on_add\": \"勾选后，Stardrop 将自动启用新添加或更新的模组\",\r\n  \"ui.settings_window.tooltips.always_ask_to_delete\": \"勾选后，Stardrop 在更新模组时始终询问是否替换原模组文件\",\r\n  \"ui.settings_window.tooltips.automatically_save_profile_changes\": \"勾选后，Stardrop 将自动保存对个性化设置所做的更改\",\r\n  \"ui.settings_window.tooltips.preferred_server\": \"设置您在从 Nexus Mods 下载时首选使用的服务器\",\r\n  \"ui.settings_window.tooltips.save_changes\": \"保存更改\",\r\n  \"ui.settings_window.tooltips.cancel_changes\": \"取消\",\r\n  \r\n  // Nexus Login Window - Buttons\r\n  \"ui.nexus_login.buttons.nexus\": \"前往 Nexus Mods\",\r\n  \"ui.nexus_login.buttons.disconnect\": \"断开与 Nexus 的连接\",\r\n\r\n  // Nexus Login Window - Labels\r\n  \"ui.nexus_login.labels.note\": \"请注意\",\r\n  \"ui.nexus_login.labels.get_key\": \"从 Nexus Mods 获取您的个人 API 密钥\",\r\n  \"ui.nexus_login.labels.get_key_actual\": \"从 Nexus Mods 获取您的 Stardrop API 密钥\",\r\n  \"ui.nexus_login.labels.paste\": \"然后将密钥粘贴在下方\",\r\n  \"ui.nexus_login.labels.share_warning\": \"请不要与他人分享您的个人 API 密钥，这样做可能会导致您的 Nexus 账户被封禁\",\r\n  \"ui.nexus_login.labels.share_warning_actual\": \"请不要与他人分享您的个人 API 密钥，这样做可能会导致您的 Nexus 账户被封禁\",\r\n  \"ui.nexus_login.labels.revoke\": \"您可以随时通过访问 https://www.nexusmods.com/settings/api-keys 来重置/撤销您的密钥\",\r\n  \"ui.nexus_login.labels.revoke_actual\": \"您可以随时通过访问 https://www.nexusmods.com/settings/api-keys 来重置/撤销您的 Stardrop 密钥\",\r\n  \"ui.nexus_login.labels.nexus_details\": \"Nexus Mods 用户信息\",\r\n  \"ui.nexus_login.labels.nexus_disconnect\": \"点击下方按钮，让 Stardrop 忘记您的个人密钥并断开与 Nexus Mods 的连接\",\r\n  \"ui.nexus_login.labels.nexus_disconnect_actual\": \"点击下方按钮，让 Stardrop 忘记您的 Stardrop API 密钥并断开与 Nexus Mods 连接\",\r\n  \"ui.nexus_login.labels.username\": \"用户名: {0}\",\r\n  \"ui.nexus_login.labels.is_premium\": \"是否为高级会员: {0}\",\r\n\r\n  // Downloads Panel - Labels\r\n  \"ui.downloads_panel.download_canceled\": \"取消\",\r\n  \"ui.downloads_panel.download_failed\": \"失败\",\r\n  \"ui_downloads_panel.download_success\": \"完成!\",\r\n  \"ui.downloads_panel.no_downloads_label\":  \"没有正在进行的下载\",\r\n\r\n  // Downloads Panel - Tooltips\r\n  \"ui.downloads_panel.tooltips.cancel_button\": \"取消\",  \r\n  \"ui.downloads_panel.tooltips.remove_button\": \"移除\", // Tooltip for the button that clears out a completed or failed download\r\n\r\n  // Warnings\r\n  \"ui.warning.unable_to_add_profile\": \"无法添加{0}，已经存在同名个性化配置！\",\r\n  \"ui.warning.given_mod_folder_does_not_exist\": \"给定模组文件夹路径不存在\\n\\n恢复到之前的路径\",\r\n  \"ui.warning.given_install_folder_not_exist\": \"给定 Stardrop 已安装模组文件夹路径不存在\\n\\n恢复到之前的路径\",\r\n  \"ui.warning.given_install_folder_not_exist_default\": \"给定 Stardrop 已安装模组文件夹路径不存在\\n\\n恢复到之前的路径\",\r\n  \"ui.warning.given_install_folder_not_under_mod_folder\": \"给定 Stardrop 已安装模组文件夹路径不存在于{0}\\n\\n恢复到之前的路径\",\r\n  \"ui.warning.given_invalid_smapi_executable\": \"给定文件不是{0}\\n\\n恢复到之前的路径\",\r\n  \"ui.warning.stardrop_locked\": \"当 SMAPI 运行时，Stardrop 会处于锁定状态在 SMAPI 关闭之前，所有更改都无法生效\",\r\n  \"ui.warning.stardrop_downloading\": \"正在下载最新版本的 Stardrop\",\r\n  \"ui.warning.SMAPI_downloading\": \"正在从 GitHub 下载最新版本的 SMAPI\",\r\n  \"ui.warning.SMAPI_installing\": \"正在更新 SMAPI 到最新版本\",\r\n  \"ui.warning.SMAPI_up_to_date\": \"SMAPI 已经是最新版本\\n\\n最新版本号为: {0}\",\r\n  \"ui.warning.install_mod_attempt_count\": \"正在尝试安装{0}个模组\",\r\n  \"ui.warning.mod_installing\": \"正在安装: {0}\",\r\n  \"ui.warning.mod_updating\": \"正在更新: {0}\",\r\n  \"ui.warning.mod_deleting\": \"正在替换: {0}\",\r\n  \"ui.warning.failed_to_delete\": \"{0}删除失败\\n\\n模组文件可能被锁定，更多内容请查看日志\",\r\n  \"ui.warning.failed_to_delete_during_update\": \"{0}替换失败\\n\\n模组可能未正确安装\",\r\n  \"ui.warning.unable_to_locate_smapi\": \"无法定位 StardewModdingAPI.exe\\n\\n请在设置中更改为正确的文件路径\",\r\n  \"ui.warning.mod_config_saved_but_not_enabled\": \"已将模组设置设置保存到个性化设置{0}，但未启用“对不同个性化设置启用不同的模组设置”\\n\\n可在设置中勾选“对不同个性化设置启用不同的模组设置”，以便 Stardrop 能自动保存和应用模组设置的更改\",\r\n  \"ui.warning.unable_to_determine_profile\": \"无法启用选定的个性化设置\\n\\nSMAPI 将不会启动\",\r\n  \"ui.warning.stardrop_up_to_date\": \"Stardrop 已是最新版本\\n\\n最新版本号为: {0}\",\r\n  \"ui.warning.stardrop_unable_to_download_latest\": \"无法下载 Stardrop 的最新版本\\n\\n请手动下载最新版本\",\r\n  \"ui.warning.stardrop_unable_to_find_latest\": \"无法检查 Stardrop 的 GitHub 仓库是否有更新\\n\\n请手动检查 GitHub 是否有可用更新，或稍后再试\",\r\n  \"ui.warning.stardrop_update_downloaded\": \"成功下载了最新版本\\n\\nStardrop 现在将重新启动\",\r\n  \"ui.warning.SMAPI_unable_to_download_latest\": \"无法下载 SMAPI 的最新版本\\n\\n请手动下载最新版本\",\r\n  \"ui.warning.SMAPI_unable_to_install_latest\": \"无法安装 SMAPI 的最新版本\\n\\n请手动下载最新版本\",\r\n  \"ui.warning.update_cooldown_minutes\": \"每5分钟只能请求一次更新\\n\\n请在{0}分钟后再次尝试\",\r\n  \"ui.warning.update_cooldown_seconds\": \"每5分钟只能请求一次更新\\n\\n请在{0}秒钟后再次尝试\",\r\n  \"ui.warning.unable_to_locate_log\": \"无法定位 SMAPI-latest.txt！SMAPI 需要至少成功运行一次，以便 Stardrop 检测游戏详情\",\r\n  \"ui.warning.unable_to_read_log\": \"无法通过读取 SMAPI 的日志文件来获取游戏版本\\n\\n将不会检查模组更新\",\r\n  \"ui.warning.no_manifest\": \"在{0}中无法找到 manifest.json\",\r\n  \"ui.warning.unable_to_load_mod\": \"无法加载位于{0}的文件\\n\\n更多内容请查看日志\",\r\n  \"ui.warning.unable_to_validate_nexus_key\": \"无法验证 Nexus Mod API 密钥\\n\\n请确认密钥是否正确，并重试\",\r\n  \"ui.warning.download_without_premium\": \"您必须拥有 Nexus Mods 的高级会员资格才能通过 Stardrop 进行下载\",\r\n  \"ui.warning.no_downloads_available\": \"目前在 Nexus Mods 上没有可更新的模组\",\r\n  \"ui.warning.failed_nexus_install\": \"由于与 Nexus Mods 的连接问题，{0}安装失败\\n\\n请重试\",\r\n  \"ui.warning.unable_nexus_install\": \"无法安装模组: {0}\",\r\n  \"ui.warning.unable_to_endorse\": \"无法设置签名状态: \\n\\n{0}\",\r\n  \"ui.warning.mod_owned\": \"您是这个模组的所有者\",\r\n  \"ui.warning.too_soon_after_download\": \"在下载这个模组后，您必须等待15分钟\\n\\n尝试签名将重置计时器\",\r\n  \"ui.warning.not_downloaded\": \"您必须从 Nexus Mods 下载这个模组才能进行签名\",\r\n  \"ui.warning.failed_to_get_mod_details\": \"无法从 NXM 获取该模组详细信息: {0}\",\r\n  \"ui.warning.failed_to_get_download_link\": \"无法从 Nexus Mods 获取下载链接: {0}\",\r\n  \"ui.warning.failed_to_set_association\": \"Stardrop 与NXM 关联失败\\n\\n更多内容请查看日志\",\r\n  \"ui.warning.already_associated\": \"Stardrop 已经与 NXM 协议关联\",\r\n\r\n  // Messages\r\n  \"ui.message.confirm_mod_deletion\": \"你确定要删除{0}？此操作无法撤销！\",\r\n  \"ui.message.unsaved_config_changes\": \"检测到个性化设置{0}的模组设置更改未保存\\n\\n是否要保存更改？\",\r\n  \"ui.message.stardrop_update_available\": \"Stardrop 有可用更新（v{0}）\\n\\n是否要现在下载？\",\r\n  \"ui.message.SMAPI_update_available\": \"SMAPI 有可用更新（v{0}）\\n\\n是否要现在下载？\",\r\n  \"ui.message.stardrop_update_complete\": \"Stardrop 已成功更新\\n\\n是否要查看发布说明？\",\r\n  \"ui.message.SMAPI_update_complete\": \"SMAPI 已成功更新\\n\\n是否要查看发布说明？\",\r\n  \"ui.message.confirm_bulk_change_mod_states_enable\": \"启用所有模组？\\n\\n注意：此操作无法撤销\",\r\n  \"ui.message.confirm_bulk_change_mod_states_disable\": \"禁用所有模组？\\n\\n注意：此操作无法撤销\",\r\n  \"ui.message.confirm_mod_update_method\": \"检测到您已安装{0}，是否要覆盖？\\n\\n注：建议覆盖，但模组设置将初始化\",\r\n  \"ui.message.confirm_mod_update_method_no_config\": \"检测到您已安装{0}，是否要覆盖？\\n\\n注：建议覆盖\",\r\n  \"ui.message.confirm_mod_update_method_preserved\": \"检测到您已安装{0}，是否要覆盖？\\n\\n注：建议覆盖，模组设置将保留\",\r\n  \"ui.message.confirm_nxm_association\": \"您是否希望关联 NXM ？\\n\\n这将允许 Stardrop 自动从 Nexus Mods 下载模组并安装\",\r\n  \"ui.message.confirm_nxm_install\": \"您是否希望安装以下模组：\\n\\n{0}\\n\\n您可以在设置菜单中禁用此确认\",\r\n  \"ui.message.require_nexus_login\": \"此操作需要填写 Nexus Mods API\",\r\n  \"ui.message.succeeded_nexus_install\": \"成功通过 Nexus Mods 安装了以下模组：\\n\\n{0}\",\r\n\r\n  // Window Names\r\n  \"ui.window.settings.name\": \"设置\",\r\n  \"ui.window.profiles.name\": \"个性化设置\",\r\n  \"ui.window.profile_naming.name\": \"个性化设置名称\",\r\n  \"ui.window.nexus_login.name\": \"Nexus Mods 个人 API 密钥\",\r\n  \"ui.window.nexus_login_actual.name\": \"Nexus Mods API 密钥\",\r\n  \"ui.window.nexus_user.name\": \"Nexus Mods 用户信息\",\r\n\r\n  // Internals\r\n  \"internal.connected\": \"已连接\",\r\n  \"internal.disconnected\": \"未连接\",\r\n  \"internal.unknown\": \"未知\",\r\n  \"internal.enable\": \"启用\",\r\n  \"internal.disable\": \"禁用\",\r\n  \"internal.enable_whole_mod\": \"启用该分类下所有模组\",\r\n  \"internal.disable_whole_mod\": \"禁用该分类下所有模组\",\r\n  \"internal.yes\": \"是\",\r\n  \"internal.yes_all\": \"全是\",\r\n  \"internal.no\": \"否\",\r\n  \"internal.no_all\": \"全否\",\r\n  \"internal.ok\": \"确定\",\r\n  \"internal.unlock\": \"解除锁定\",\r\n\r\n  // Internals - Measurements\r\n  \"internal.measurements.bytes_size\": \"B\",\r\n  \"internal.measurements.bytes_per_second\": \"B/s\",\r\n  \"internal.measurements.kilobytes_size\": \"KB\",\r\n  \"internal.measurements.kilobytes_per_second\": \"KB/s\",\r\n  \"internal.measurements.megabytes_size\": \"MB\",\r\n  \"internal.measurements.megabytes_per_second\": \"MB/s\"\r\n}\r\n\r\n"
  }
]